aboutsummaryrefslogtreecommitdiffstats
path: root/packages
diff options
context:
space:
mode:
Diffstat (limited to 'packages')
-rw-r--r--packages/0x.js/package.json184
-rw-r--r--packages/0x.js/src/0x.ts608
-rw-r--r--packages/0x.js/src/artifacts.ts14
-rw-r--r--packages/0x.js/src/artifacts/DummyToken.json40
-rw-r--r--packages/0x.js/src/artifacts/EtherToken.json570
-rw-r--r--packages/0x.js/src/artifacts/Exchange.json1216
-rw-r--r--packages/0x.js/src/artifacts/Token.json340
-rw-r--r--packages/0x.js/src/artifacts/TokenRegistry.json1090
-rw-r--r--packages/0x.js/src/artifacts/TokenTransferProxy.json370
-rw-r--r--packages/0x.js/src/artifacts/ZRX.json36
-rw-r--r--packages/0x.js/src/contract_wrappers/contract_wrapper.ts378
-rw-r--r--packages/0x.js/src/contract_wrappers/ether_token_wrapper.ts318
-rw-r--r--packages/0x.js/src/contract_wrappers/exchange_wrapper.ts1718
-rw-r--r--packages/0x.js/src/contract_wrappers/token_registry_wrapper.ts224
-rw-r--r--packages/0x.js/src/contract_wrappers/token_transfer_proxy_wrapper.ts110
-rw-r--r--packages/0x.js/src/contract_wrappers/token_wrapper.ts720
-rw-r--r--packages/0x.js/src/globals.d.ts50
-rw-r--r--packages/0x.js/src/globalsAugment.d.ts34
-rw-r--r--packages/0x.js/src/index.ts88
-rw-r--r--packages/0x.js/src/order_watcher/event_watcher.ts148
-rw-r--r--packages/0x.js/src/order_watcher/expiration_watcher.ts118
-rw-r--r--packages/0x.js/src/order_watcher/order_state_watcher.ts670
-rw-r--r--packages/0x.js/src/order_watcher/remaining_fillable_calculator.ts180
-rw-r--r--packages/0x.js/src/schemas/zero_ex_config_schema.ts50
-rw-r--r--packages/0x.js/src/stores/balance_proxy_allowance_lazy_store.ts150
-rw-r--r--packages/0x.js/src/stores/order_filled_cancelled_lazy_store.ts100
-rw-r--r--packages/0x.js/src/types.ts390
-rw-r--r--packages/0x.js/src/utils/abi_decoder.ts118
-rw-r--r--packages/0x.js/src/utils/assert.ts42
-rw-r--r--packages/0x.js/src/utils/constants.ts16
-rw-r--r--packages/0x.js/src/utils/decorators.ts114
-rw-r--r--packages/0x.js/src/utils/exchange_transfer_simulator.ts182
-rw-r--r--packages/0x.js/src/utils/filter_utils.ts150
-rw-r--r--packages/0x.js/src/utils/order_state_utils.ts254
-rw-r--r--packages/0x.js/src/utils/order_validation_utils.ts404
-rw-r--r--packages/0x.js/src/utils/signature_utils.ts80
-rw-r--r--packages/0x.js/src/utils/utils.ts116
-rw-r--r--packages/0x.js/test/0x.js_test.ts538
-rw-r--r--packages/0x.js/test/artifacts_test.ts80
-rw-r--r--packages/0x.js/test/assert_test.ts60
-rw-r--r--packages/0x.js/test/ether_token_wrapper_test.ts668
-rw-r--r--packages/0x.js/test/event_watcher_test.ts212
-rw-r--r--packages/0x.js/test/exchange_transfer_simulator_test.ts198
-rw-r--r--packages/0x.js/test/exchange_wrapper_test.ts2234
-rw-r--r--packages/0x.js/test/expiration_watcher_test.ts266
-rw-r--r--packages/0x.js/test/order_state_watcher_test.ts1022
-rw-r--r--packages/0x.js/test/order_validation_test.ts900
-rw-r--r--packages/0x.js/test/remaining_fillable_calculator_test.ts436
-rw-r--r--packages/0x.js/test/subscription_test.ts130
-rw-r--r--packages/0x.js/test/token_registry_wrapper_test.ts212
-rw-r--r--packages/0x.js/test/token_transfer_proxy_wrapper_test.ts46
-rw-r--r--packages/0x.js/test/token_wrapper_test.ts994
-rw-r--r--packages/0x.js/test/utils/chai_setup.ts12
-rw-r--r--packages/0x.js/test/utils/constants.ts18
-rw-r--r--packages/0x.js/test/utils/fill_scenarios.ts374
-rw-r--r--packages/0x.js/test/utils/order_factory.ts74
-rw-r--r--packages/0x.js/test/utils/report_callback_errors.ts102
-rw-r--r--packages/0x.js/test/utils/subproviders/empty_wallet_subprovider.ts34
-rw-r--r--packages/0x.js/test/utils/subproviders/fake_gas_estimate_subprovider.ts42
-rw-r--r--packages/0x.js/test/utils/token_utils.ts48
-rw-r--r--packages/0x.js/test/utils/web3_factory.ts40
-rw-r--r--packages/0x.js/tsconfig.json28
-rw-r--r--packages/0x.js/tslint.json2
-rw-r--r--packages/abi-gen/package.json92
-rw-r--r--packages/abi-gen/src/globals.d.ts2
-rw-r--r--packages/abi-gen/src/index.ts130
-rw-r--r--packages/abi-gen/src/types.ts20
-rw-r--r--packages/abi-gen/src/utils.ts128
-rw-r--r--packages/abi-gen/tsconfig.json10
-rw-r--r--packages/abi-gen/tslint.json2
-rw-r--r--packages/assert/package.json86
-rw-r--r--packages/assert/src/index.ts166
-rw-r--r--packages/assert/test/assert_test.ts478
-rw-r--r--packages/assert/tsconfig.json10
-rw-r--r--packages/assert/tslint.json2
-rw-r--r--packages/chai-as-promised-typescript-typings/index.d.ts466
-rw-r--r--packages/chai-as-promised-typescript-typings/package.json38
-rw-r--r--packages/chai-as-promised-typescript-typings/tslint.json2
-rw-r--r--packages/chai-typescript-typings/index.d.ts2462
-rw-r--r--packages/chai-typescript-typings/package.json28
-rw-r--r--packages/chai-typescript-typings/tslint.json2
-rw-r--r--packages/connect/package.json124
-rw-r--r--packages/connect/src/globals.d.ts4
-rw-r--r--packages/connect/src/http_client.ts268
-rw-r--r--packages/connect/src/index.ts30
-rw-r--r--packages/connect/src/schemas/relayer_fees_request_schema.ts12
-rw-r--r--packages/connect/src/schemas/relayer_orderbook_request_schema.ts12
-rw-r--r--packages/connect/src/schemas/relayer_orders_request_schema.ts28
-rw-r--r--packages/connect/src/schemas/relayer_token_pairs_request_schema.ts12
-rw-r--r--packages/connect/src/schemas/schemas.ts6
-rw-r--r--packages/connect/src/types.ts192
-rw-r--r--packages/connect/src/utils/orderbook_channel_message_parser.ts54
-rw-r--r--packages/connect/src/utils/relayer_response_json_parsers.ts54
-rw-r--r--packages/connect/src/utils/type_converters.ts50
-rw-r--r--packages/connect/src/ws_orderbook_channel.ts238
-rw-r--r--packages/connect/test/fixtures/standard_relayer_api/fees.json6
-rw-r--r--packages/connect/test/fixtures/standard_relayer_api/fees.ts6
-rw-r--r--packages/connect/test/fixtures/standard_relayer_api/order/0xabc67323774bdbd24d94f977fa9ac94a50f016026fd13f42990861238897721f.json34
-rw-r--r--packages/connect/test/fixtures/standard_relayer_api/order/0xabc67323774bdbd24d94f977fa9ac94a50f016026fd13f42990861238897721f.ts34
-rw-r--r--packages/connect/test/fixtures/standard_relayer_api/orderbook.json84
-rw-r--r--packages/connect/test/fixtures/standard_relayer_api/orderbook.ts84
-rw-r--r--packages/connect/test/fixtures/standard_relayer_api/orders.json38
-rw-r--r--packages/connect/test/fixtures/standard_relayer_api/orders.ts38
-rw-r--r--packages/connect/test/fixtures/standard_relayer_api/token_pairs.json28
-rw-r--r--packages/connect/test/fixtures/standard_relayer_api/token_pairs.ts28
-rw-r--r--packages/connect/test/http_client_test.ts248
-rw-r--r--packages/connect/test/orderbook_channel_message_parsers_test.ts94
-rw-r--r--packages/connect/test/ws_orderbook_channel_test.ts96
-rw-r--r--packages/connect/tsconfig.json20
-rw-r--r--packages/connect/tslint.json2
-rw-r--r--packages/contracts/globals.d.ts24
-rw-r--r--packages/contracts/globalsAugment.d.ts26
-rw-r--r--packages/contracts/migrations/1_initial_migration.ts2
-rw-r--r--packages/contracts/migrations/2_deploy_independent_contracts.ts60
-rw-r--r--packages/contracts/migrations/3_register_tokens.ts166
-rw-r--r--packages/contracts/migrations/4_configure_proxy.ts30
-rw-r--r--packages/contracts/migrations/5_transfer_ownership.ts26
-rw-r--r--packages/contracts/migrations/config/multisig_sample.ts10
-rw-r--r--packages/contracts/migrations/config/token_info.ts188
-rw-r--r--packages/contracts/package.json144
-rw-r--r--packages/contracts/test/ts/ether_token.ts206
-rw-r--r--packages/contracts/test/ts/exchange/core.ts1610
-rw-r--r--packages/contracts/test/ts/exchange/helpers.ts298
-rw-r--r--packages/contracts/test/ts/exchange/wrapper.ts656
-rw-r--r--packages/contracts/test/ts/multi_sig_with_time_lock.ts178
-rw-r--r--packages/contracts/test/ts/multi_sig_with_time_lock_except_remove_auth_addr.ts264
-rw-r--r--packages/contracts/test/ts/token_registry.ts432
-rw-r--r--packages/contracts/test/ts/token_transfer_proxy/auth.ts150
-rw-r--r--packages/contracts/test/ts/token_transfer_proxy/transfer_from.ts102
-rw-r--r--packages/contracts/test/ts/unlimited_allowance_token.ts218
-rw-r--r--packages/contracts/test/ts/unlimited_allowance_token_v2.ts246
-rw-r--r--packages/contracts/test/ts/utils/chai_setup.ts12
-rw-r--r--packages/contracts/test/ts/zrx_token.ts314
-rw-r--r--packages/contracts/tsconfig.json46
-rw-r--r--packages/contracts/tslint.json2
-rw-r--r--packages/contracts/util/artifacts.ts52
-rw-r--r--packages/contracts/util/balances.ts40
-rw-r--r--packages/contracts/util/constants.ts14
-rw-r--r--packages/contracts/util/crypto.ts46
-rw-r--r--packages/contracts/util/exchange_wrapper.ts358
-rw-r--r--packages/contracts/util/formatters.ts206
-rw-r--r--packages/contracts/util/multi_sig_wrapper.ts60
-rw-r--r--packages/contracts/util/order.ts188
-rw-r--r--packages/contracts/util/order_factory.ts40
-rw-r--r--packages/contracts/util/token_registry_wrapper.ts104
-rw-r--r--packages/contracts/util/types.ts150
-rw-r--r--packages/deployer/package.json82
-rw-r--r--packages/deployer/src/cli.ts218
-rw-r--r--packages/deployer/src/commands.ts24
-rw-r--r--packages/deployer/src/compiler.ts450
-rw-r--r--packages/deployer/src/deployer.ts326
-rw-r--r--packages/deployer/src/globals.d.ts6
-rw-r--r--packages/deployer/src/migrations/config/multisig_sample.ts10
-rw-r--r--packages/deployer/src/migrations/config/token_info.ts70
-rw-r--r--packages/deployer/src/migrations/migrate.ts154
-rw-r--r--packages/deployer/src/solc/bin_paths.ts20
-rw-r--r--packages/deployer/src/types.ts24
-rw-r--r--packages/deployer/src/utils/constants.ts2
-rw-r--r--packages/deployer/src/utils/contract.ts144
-rw-r--r--packages/deployer/src/utils/encoder.ts24
-rw-r--r--packages/deployer/src/utils/fs_wrapper.ts12
-rw-r--r--packages/deployer/src/utils/types.ts110
-rw-r--r--packages/deployer/src/utils/utils.ts22
-rw-r--r--packages/deployer/test/deploy_test.ts122
-rw-r--r--packages/deployer/test/fixtures/exchange_bin.ts4
-rw-r--r--packages/deployer/test/util/constants.ts14
-rw-r--r--packages/deployer/tsconfig.json28
-rw-r--r--packages/dev-utils/package.json70
-rw-r--r--packages/dev-utils/src/blockchain_lifecycle.ts38
-rw-r--r--packages/dev-utils/src/rpc.ts110
-rw-r--r--packages/dev-utils/tsconfig.json18
-rw-r--r--packages/dev-utils/tslint.json2
-rw-r--r--packages/json-schemas/package.json86
-rw-r--r--packages/json-schemas/schemas/basic_type_schemas.ts12
-rw-r--r--packages/json-schemas/schemas/block_range_schema.ts30
-rw-r--r--packages/json-schemas/schemas/ec_signature_schema.ts30
-rw-r--r--packages/json-schemas/schemas/index_filter_values_schema.ts10
-rw-r--r--packages/json-schemas/schemas/order_cancel_schema.ts20
-rw-r--r--packages/json-schemas/schemas/order_fill_or_kill_requests_schema.ts20
-rw-r--r--packages/json-schemas/schemas/order_fill_requests_schema.ts20
-rw-r--r--packages/json-schemas/schemas/order_hash_schema.ts6
-rw-r--r--packages/json-schemas/schemas/order_schemas.ts76
-rw-r--r--packages/json-schemas/schemas/relayer_api_error_response_schema.ts38
-rw-r--r--packages/json-schemas/schemas/relayer_api_fees_payload_schema.ts44
-rw-r--r--packages/json-schemas/schemas/relayer_api_fees_response_schema.ts16
-rw-r--r--packages/json-schemas/schemas/relayer_api_orberbook_channel_subscribe_schema.ts36
-rw-r--r--packages/json-schemas/schemas/relayer_api_orderbook_channel_snapshot_schema.ts32
-rw-r--r--packages/json-schemas/schemas/relayer_api_orderbook_channel_update_response_schema.ts18
-rw-r--r--packages/json-schemas/schemas/relayer_api_orderbook_response_schema.ts14
-rw-r--r--packages/json-schemas/schemas/relayer_api_token_pairs_response_schema.ts38
-rw-r--r--packages/json-schemas/schemas/signed_orders_schema.ts6
-rw-r--r--packages/json-schemas/schemas/token_schema.ts18
-rw-r--r--packages/json-schemas/schemas/tx_data_schema.ts56
-rw-r--r--packages/json-schemas/src/schema_validator.ts44
-rw-r--r--packages/json-schemas/src/schemas.ts68
-rw-r--r--packages/json-schemas/test/schema_test.ts1798
-rw-r--r--packages/json-schemas/tsconfig.json10
-rw-r--r--packages/json-schemas/tslint.json2
-rw-r--r--packages/monorepo-scripts/package.json64
-rw-r--r--packages/monorepo-scripts/src/deps_versions.ts54
-rw-r--r--packages/monorepo-scripts/tsconfig.json10
-rw-r--r--packages/monorepo-scripts/tslint.json2
-rw-r--r--packages/subproviders/package.json106
-rw-r--r--packages/subproviders/src/globals.d.ts150
-rw-r--r--packages/subproviders/src/index.ts18
-rw-r--r--packages/subproviders/src/subproviders/injected_web3.ts68
-rw-r--r--packages/subproviders/src/subproviders/ledger.ts544
-rw-r--r--packages/subproviders/src/subproviders/redundant_rpc.ts84
-rw-r--r--packages/subproviders/src/subproviders/subprovider.ts58
-rw-r--r--packages/subproviders/src/types.ts102
-rw-r--r--packages/subproviders/test/chai_setup.ts10
-rw-r--r--packages/subproviders/test/integration/ledger_subprovider_test.ts322
-rw-r--r--packages/subproviders/test/unit/ledger_subprovider_test.ts452
-rw-r--r--packages/subproviders/test/unit/redundant_rpc_subprovider_test.ts78
-rw-r--r--packages/subproviders/test/utils/report_callback_errors.ts20
-rw-r--r--packages/subproviders/tsconfig.json26
-rw-r--r--packages/subproviders/tslint.json2
-rw-r--r--packages/testnet-faucets/README.md40
-rw-r--r--packages/testnet-faucets/package.json82
-rw-r--r--packages/testnet-faucets/src/ts/configs.ts10
-rw-r--r--packages/testnet-faucets/src/ts/error_reporter.ts58
-rw-r--r--packages/testnet-faucets/src/ts/ether_request_queue.ts30
-rw-r--r--packages/testnet-faucets/src/ts/global.d.ts28
-rw-r--r--packages/testnet-faucets/src/ts/handler.ts166
-rw-r--r--packages/testnet-faucets/src/ts/id_management.ts26
-rw-r--r--packages/testnet-faucets/src/ts/request_queue.ts80
-rw-r--r--packages/testnet-faucets/src/ts/rpc_urls.ts8
-rw-r--r--packages/testnet-faucets/src/ts/server.ts8
-rw-r--r--packages/testnet-faucets/src/ts/utils.ts10
-rw-r--r--packages/testnet-faucets/src/ts/zrx_request_queue.ts52
-rw-r--r--packages/testnet-faucets/tsconfig.json10
-rw-r--r--packages/testnet-faucets/tslint.json2
-rw-r--r--packages/tslint-config/README.md2
-rw-r--r--packages/tslint-config/package.json70
-rw-r--r--packages/tslint-config/rules/asyncSuffixRule.ts6
-rw-r--r--packages/tslint-config/rules/underscorePrivatesRule.ts72
-rw-r--r--packages/tslint-config/rules/walkers/async_suffix.ts40
-rw-r--r--packages/tslint-config/tsconfig.json10
-rw-r--r--packages/tslint-config/tslint.json198
-rw-r--r--packages/types/package.json58
-rw-r--r--packages/types/src/index.ts32
-rw-r--r--packages/types/tsconfig.json10
-rw-r--r--packages/types/tslint.json2
-rw-r--r--packages/utils/package.json64
-rw-r--r--packages/utils/src/address_utils.ts56
-rw-r--r--packages/utils/src/class_utils.ts28
-rw-r--r--packages/utils/src/configured_bignumber.ts2
-rw-r--r--packages/utils/src/interval_utils.ts66
-rw-r--r--packages/utils/src/promisify.ts18
-rw-r--r--packages/utils/tsconfig.json10
-rw-r--r--packages/utils/tslint.json2
-rw-r--r--packages/web3-typescript-typings/index.d.ts840
-rw-r--r--packages/web3-typescript-typings/package.json56
-rw-r--r--packages/web3-typescript-typings/tslint.json2
-rw-r--r--packages/web3-wrapper/package.json68
-rw-r--r--packages/web3-wrapper/src/index.ts326
-rw-r--r--packages/web3-wrapper/tsconfig.json10
-rw-r--r--packages/web3-wrapper/tslint.json2
-rw-r--r--packages/website/contracts/Mintable.json376
-rw-r--r--packages/website/md/docs/0xjs/async.md14
-rw-r--r--packages/website/package.json214
-rw-r--r--packages/website/ts/blockchain.ts1504
-rw-r--r--packages/website/ts/components/dialogs/blockchain_err_dialog.tsx282
-rw-r--r--packages/website/ts/components/dialogs/eth_weth_conversion_dialog.tsx290
-rw-r--r--packages/website/ts/components/dialogs/ledger_config_dialog.tsx460
-rw-r--r--packages/website/ts/components/dialogs/portal_disclaimer_dialog.tsx52
-rw-r--r--packages/website/ts/components/dialogs/send_dialog.tsx200
-rw-r--r--packages/website/ts/components/dialogs/track_token_confirmation_dialog.tsx160
-rw-r--r--packages/website/ts/components/dialogs/u2f_not_supported_dialog.tsx70
-rw-r--r--packages/website/ts/components/dialogs/wrapped_eth_section_notice_dialog.tsx46
-rw-r--r--packages/website/ts/components/eth_weth_conversion_button.tsx208
-rw-r--r--packages/website/ts/components/eth_wrappers.tsx674
-rw-r--r--packages/website/ts/components/fill_order.tsx1270
-rw-r--r--packages/website/ts/components/fill_order_json.tsx124
-rw-r--r--packages/website/ts/components/fill_warning_dialog.tsx70
-rw-r--r--packages/website/ts/components/flash_messages/token_send_completed.tsx38
-rw-r--r--packages/website/ts/components/flash_messages/transaction_submitted.tsx30
-rw-r--r--packages/website/ts/components/footer.tsx362
-rw-r--r--packages/website/ts/components/generate_order/asset_picker.tsx498
-rw-r--r--packages/website/ts/components/generate_order/generate_order_form.tsx620
-rw-r--r--packages/website/ts/components/generate_order/new_token_form.tsx418
-rw-r--r--packages/website/ts/components/inputs/address_input.tsx112
-rw-r--r--packages/website/ts/components/inputs/allowance_toggle.tsx142
-rw-r--r--packages/website/ts/components/inputs/balance_bounded_input.tsx264
-rw-r--r--packages/website/ts/components/inputs/eth_amount_input.tsx68
-rw-r--r--packages/website/ts/components/inputs/expiration_input.tsx168
-rw-r--r--packages/website/ts/components/inputs/hash_input.tsx86
-rw-r--r--packages/website/ts/components/inputs/identicon_address_input.tsx76
-rw-r--r--packages/website/ts/components/inputs/token_amount_input.tsx108
-rw-r--r--packages/website/ts/components/inputs/token_input.tsx164
-rw-r--r--packages/website/ts/components/order_json.tsx322
-rw-r--r--packages/website/ts/components/portal.tsx630
-rw-r--r--packages/website/ts/components/portal_menu.tsx122
-rw-r--r--packages/website/ts/components/send_button.tsx136
-rw-r--r--packages/website/ts/components/token_balances.tsx1066
-rw-r--r--packages/website/ts/components/top_bar.tsx634
-rw-r--r--packages/website/ts/components/top_bar_menu_item.tsx74
-rw-r--r--packages/website/ts/components/track_token_confirmation.tsx94
-rw-r--r--packages/website/ts/components/trade_history/trade_history.tsx190
-rw-r--r--packages/website/ts/components/trade_history/trade_history_item.tsx282
-rw-r--r--packages/website/ts/components/ui/alert.tsx30
-rw-r--r--packages/website/ts/components/ui/badge.tsx84
-rw-r--r--packages/website/ts/components/ui/copy_icon.tsx126
-rw-r--r--packages/website/ts/components/ui/drop_down_menu_item.tsx170
-rw-r--r--packages/website/ts/components/ui/ethereum_address.tsx38
-rw-r--r--packages/website/ts/components/ui/etherscan_icon.tsx52
-rw-r--r--packages/website/ts/components/ui/fake_text_field.tsx44
-rw-r--r--packages/website/ts/components/ui/flash_message.tsx52
-rw-r--r--packages/website/ts/components/ui/help_tooltip.tsx28
-rw-r--r--packages/website/ts/components/ui/identicon.tsx72
-rw-r--r--packages/website/ts/components/ui/input_label.tsx28
-rw-r--r--packages/website/ts/components/ui/lifecycle_raised_button.tsx166
-rw-r--r--packages/website/ts/components/ui/loading.tsx52
-rw-r--r--packages/website/ts/components/ui/menu_item.tsx78
-rw-r--r--packages/website/ts/components/ui/party.tsx240
-rw-r--r--packages/website/ts/components/ui/required_label.tsx14
-rw-r--r--packages/website/ts/components/ui/simple_loading.tsx18
-rw-r--r--packages/website/ts/components/ui/swap_icon.tsx60
-rw-r--r--packages/website/ts/components/ui/token_icon.tsx30
-rw-r--r--packages/website/ts/components/visual_order.tsx120
-rw-r--r--packages/website/ts/containers/connect_documentation.tsx116
-rw-r--r--packages/website/ts/containers/generate_order_form.tsx64
-rw-r--r--packages/website/ts/containers/portal.tsx112
-rw-r--r--packages/website/ts/containers/smart_contracts_documentation.tsx58
-rw-r--r--packages/website/ts/containers/zero_ex_js_documentation.tsx258
-rw-r--r--packages/website/ts/globals.d.ts170
-rw-r--r--packages/website/ts/index.tsx60
-rw-r--r--packages/website/ts/lazy_component.tsx82
-rw-r--r--packages/website/ts/local_storage/local_storage.ts62
-rw-r--r--packages/website/ts/local_storage/tracked_token_storage.ts114
-rw-r--r--packages/website/ts/local_storage/trade_history_storage.tsx160
-rw-r--r--packages/website/ts/pages/about/about.tsx416
-rw-r--r--packages/website/ts/pages/about/profile.tsx116
-rw-r--r--packages/website/ts/pages/documentation/comment.tsx16
-rw-r--r--packages/website/ts/pages/documentation/custom_enum.tsx34
-rw-r--r--packages/website/ts/pages/documentation/docs_info.ts198
-rw-r--r--packages/website/ts/pages/documentation/documentation.tsx648
-rw-r--r--packages/website/ts/pages/documentation/enum.tsx26
-rw-r--r--packages/website/ts/pages/documentation/event_definition.tsx132
-rw-r--r--packages/website/ts/pages/documentation/interface.tsx98
-rw-r--r--packages/website/ts/pages/documentation/method_block.tsx238
-rw-r--r--packages/website/ts/pages/documentation/method_signature.tsx148
-rw-r--r--packages/website/ts/pages/documentation/source_link.tsx38
-rw-r--r--packages/website/ts/pages/documentation/type.tsx354
-rw-r--r--packages/website/ts/pages/documentation/type_definition.tsx194
-rw-r--r--packages/website/ts/pages/faq/faq.tsx846
-rw-r--r--packages/website/ts/pages/faq/question.tsx76
-rw-r--r--packages/website/ts/pages/landing/landing.tsx1420
-rw-r--r--packages/website/ts/pages/not_found.tsx52
-rw-r--r--packages/website/ts/pages/shared/anchor_title.tsx142
-rw-r--r--packages/website/ts/pages/shared/markdown_code_block.tsx28
-rw-r--r--packages/website/ts/pages/shared/markdown_section.tsx112
-rw-r--r--packages/website/ts/pages/shared/nested_sidebar_menu.tsx268
-rw-r--r--packages/website/ts/pages/shared/section_header.tsx72
-rw-r--r--packages/website/ts/pages/shared/version_drop_down.tsx50
-rw-r--r--packages/website/ts/pages/wiki/wiki.tsx344
-rw-r--r--packages/website/ts/redux/dispatcher.ts452
-rw-r--r--packages/website/ts/redux/reducer.ts738
-rw-r--r--packages/website/ts/schemas/order_schema.ts26
-rw-r--r--packages/website/ts/schemas/order_taker_schema.ts18
-rw-r--r--packages/website/ts/schemas/signature_data_schema.ts18
-rw-r--r--packages/website/ts/schemas/token_schema.ts18
-rw-r--r--packages/website/ts/schemas/validator.ts22
-rw-r--r--packages/website/ts/types.ts782
-rw-r--r--packages/website/ts/utils/colors.ts78
-rw-r--r--packages/website/ts/utils/configs.ts230
-rw-r--r--packages/website/ts/utils/constants.ts164
-rw-r--r--packages/website/ts/utils/doc_utils.ts84
-rw-r--r--packages/website/ts/utils/doxity_utils.ts316
-rw-r--r--packages/website/ts/utils/error_reporter.ts74
-rw-r--r--packages/website/ts/utils/mui_theme.ts60
-rw-r--r--packages/website/ts/utils/typedoc_utils.ts678
-rw-r--r--packages/website/ts/utils/utils.ts522
-rw-r--r--packages/website/ts/web3_wrapper.ts288
-rw-r--r--packages/website/tsconfig.json30
-rw-r--r--packages/website/tslint.json14
376 files changed, 34256 insertions, 34256 deletions
diff --git a/packages/0x.js/package.json b/packages/0x.js/package.json
index 67d6452b5..465a2a753 100644
--- a/packages/0x.js/package.json
+++ b/packages/0x.js/package.json
@@ -1,94 +1,94 @@
{
- "name": "0x.js",
- "version": "0.30.2",
- "description": "A javascript library for interacting with the 0x protocol",
- "keywords": ["0x.js", "0xproject", "ethereum", "tokens", "exchange"],
- "main": "lib/src/index.js",
- "types": "lib/src/index.d.ts",
- "scripts": {
- "prebuild": "run-s clean generate_contract_wrappers",
- "build": "run-p build:umd:prod build:commonjs; exit 0;",
- "docs:json": "typedoc --excludePrivate --excludeExternals --target ES5 --json $JSON_FILE_PATH $PROJECT_DIR",
- "upload_docs_json":
- "aws s3 cp generated_docs/index.json $S3_URL --profile 0xproject --grants read=uri=http://acs.amazonaws.com/groups/global/AllUsers --content-type application/json",
- "generate_contract_wrappers":
- "node ../abi-gen/lib/index.js --abiGlob 'src/artifacts/@(Exchange|Token|TokenTransferProxy|EtherToken|TokenRegistry|DummyToken).json' --templates contract_templates --output src/contract_wrappers/generated",
- "lint": "tslint --project . 'src/**/*.ts' 'test/**/*.ts'",
- "test:circleci": "run-s test:coverage report_test_coverage",
- "test": "run-s clean test:commonjs",
- "test:coverage": "nyc npm run test --all",
- "report_test_coverage": "nyc report --reporter=text-lcov | coveralls",
- "update_contracts":
- "for i in ${npm_package_config_artifacts}; do copyfiles -u 4 ../contracts/build/contracts/$i.json ../0x.js/src/artifacts; done;",
- "clean": "shx rm -rf _bundles lib test_temp",
- "build:umd:prod": "NODE_ENV=production webpack",
- "build:commonjs": "tsc && copyfiles -u 2 './src/artifacts/**/*.json' ./lib/src/artifacts;",
- "test:commonjs": "run-s build:commonjs run_mocha",
- "run_mocha": "mocha lib/test/**/*_test.js --timeout 10000 --bail --exit"
- },
- "config": {
- "artifacts": "TokenTransferProxy Exchange TokenRegistry Token EtherToken"
- },
- "repository": {
- "type": "git",
- "url": "https://github.com/0xProject/0x.js"
- },
- "license": "Apache-2.0",
- "engines": {
- "node": ">=6.0.0"
- },
- "devDependencies": {
- "@0xproject/abi-gen": "^0.1.4",
- "@0xproject/dev-utils": "^0.0.7",
- "@0xproject/tslint-config": "^0.4.4",
- "@types/bintrees": "^1.0.2",
- "@types/jsonschema": "^1.1.1",
- "@types/lodash": "^4.14.86",
- "@types/mocha": "^2.2.42",
- "@types/node": "^8.0.53",
- "@types/sinon": "^2.2.2",
- "@types/uuid": "^3.4.2",
- "awesome-typescript-loader": "^3.1.3",
- "chai": "^4.0.1",
- "chai-as-promised": "^7.1.0",
- "chai-as-promised-typescript-typings": "^0.0.6",
- "chai-bignumber": "^2.0.1",
- "chai-typescript-typings": "^0.0.2",
- "copyfiles": "^1.2.0",
- "coveralls": "^3.0.0",
- "dirty-chai": "^2.0.1",
- "json-loader": "^0.5.4",
- "mocha": "^4.0.1",
- "npm-run-all": "^4.1.2",
- "nyc": "^11.0.1",
- "opn-cli": "^3.1.0",
- "request": "^2.81.0",
- "request-promise-native": "^1.0.4",
- "shx": "^0.2.2",
- "sinon": "^4.0.0",
- "source-map-support": "^0.5.0",
- "truffle-hdwallet-provider": "^0.0.3",
- "tslint": "5.8.0",
- "typedoc": "~0.8.0",
- "typescript": "~2.6.1",
- "web3-provider-engine": "^13.0.1",
- "web3-typescript-typings": "^0.9.6",
- "webpack": "^3.1.0"
- },
- "dependencies": {
- "@0xproject/assert": "^0.0.13",
- "@0xproject/json-schemas": "^0.7.5",
- "@0xproject/types": "^0.1.6",
- "@0xproject/utils": "^0.2.2",
- "@0xproject/web3-wrapper": "^0.1.7",
- "bintrees": "^1.0.2",
- "bn.js": "^4.11.8",
- "ethereumjs-abi": "^0.6.4",
- "ethereumjs-blockstream": "^2.0.6",
- "ethereumjs-util": "^5.1.1",
- "js-sha3": "^0.6.1",
- "lodash": "^4.17.4",
- "uuid": "^3.1.0",
- "web3": "^0.20.0"
- }
+ "name": "0x.js",
+ "version": "0.30.2",
+ "description": "A javascript library for interacting with the 0x protocol",
+ "keywords": ["0x.js", "0xproject", "ethereum", "tokens", "exchange"],
+ "main": "lib/src/index.js",
+ "types": "lib/src/index.d.ts",
+ "scripts": {
+ "prebuild": "run-s clean generate_contract_wrappers",
+ "build": "run-p build:umd:prod build:commonjs; exit 0;",
+ "docs:json": "typedoc --excludePrivate --excludeExternals --target ES5 --json $JSON_FILE_PATH $PROJECT_DIR",
+ "upload_docs_json":
+ "aws s3 cp generated_docs/index.json $S3_URL --profile 0xproject --grants read=uri=http://acs.amazonaws.com/groups/global/AllUsers --content-type application/json",
+ "generate_contract_wrappers":
+ "node ../abi-gen/lib/index.js --abiGlob 'src/artifacts/@(Exchange|Token|TokenTransferProxy|EtherToken|TokenRegistry|DummyToken).json' --templates contract_templates --output src/contract_wrappers/generated",
+ "lint": "tslint --project . 'src/**/*.ts' 'test/**/*.ts'",
+ "test:circleci": "run-s test:coverage report_test_coverage",
+ "test": "run-s clean test:commonjs",
+ "test:coverage": "nyc npm run test --all",
+ "report_test_coverage": "nyc report --reporter=text-lcov | coveralls",
+ "update_contracts":
+ "for i in ${npm_package_config_artifacts}; do copyfiles -u 4 ../contracts/build/contracts/$i.json ../0x.js/src/artifacts; done;",
+ "clean": "shx rm -rf _bundles lib test_temp",
+ "build:umd:prod": "NODE_ENV=production webpack",
+ "build:commonjs": "tsc && copyfiles -u 2 './src/artifacts/**/*.json' ./lib/src/artifacts;",
+ "test:commonjs": "run-s build:commonjs run_mocha",
+ "run_mocha": "mocha lib/test/**/*_test.js --timeout 10000 --bail --exit"
+ },
+ "config": {
+ "artifacts": "TokenTransferProxy Exchange TokenRegistry Token EtherToken"
+ },
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/0xProject/0x.js"
+ },
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=6.0.0"
+ },
+ "devDependencies": {
+ "@0xproject/abi-gen": "^0.1.4",
+ "@0xproject/dev-utils": "^0.0.7",
+ "@0xproject/tslint-config": "^0.4.4",
+ "@types/bintrees": "^1.0.2",
+ "@types/jsonschema": "^1.1.1",
+ "@types/lodash": "^4.14.86",
+ "@types/mocha": "^2.2.42",
+ "@types/node": "^8.0.53",
+ "@types/sinon": "^2.2.2",
+ "@types/uuid": "^3.4.2",
+ "awesome-typescript-loader": "^3.1.3",
+ "chai": "^4.0.1",
+ "chai-as-promised": "^7.1.0",
+ "chai-as-promised-typescript-typings": "^0.0.6",
+ "chai-bignumber": "^2.0.1",
+ "chai-typescript-typings": "^0.0.2",
+ "copyfiles": "^1.2.0",
+ "coveralls": "^3.0.0",
+ "dirty-chai": "^2.0.1",
+ "json-loader": "^0.5.4",
+ "mocha": "^4.0.1",
+ "npm-run-all": "^4.1.2",
+ "nyc": "^11.0.1",
+ "opn-cli": "^3.1.0",
+ "request": "^2.81.0",
+ "request-promise-native": "^1.0.4",
+ "shx": "^0.2.2",
+ "sinon": "^4.0.0",
+ "source-map-support": "^0.5.0",
+ "truffle-hdwallet-provider": "^0.0.3",
+ "tslint": "5.8.0",
+ "typedoc": "~0.8.0",
+ "typescript": "~2.6.1",
+ "web3-provider-engine": "^13.0.1",
+ "web3-typescript-typings": "^0.9.6",
+ "webpack": "^3.1.0"
+ },
+ "dependencies": {
+ "@0xproject/assert": "^0.0.13",
+ "@0xproject/json-schemas": "^0.7.5",
+ "@0xproject/types": "^0.1.6",
+ "@0xproject/utils": "^0.2.2",
+ "@0xproject/web3-wrapper": "^0.1.7",
+ "bintrees": "^1.0.2",
+ "bn.js": "^4.11.8",
+ "ethereumjs-abi": "^0.6.4",
+ "ethereumjs-blockstream": "^2.0.6",
+ "ethereumjs-util": "^5.1.1",
+ "js-sha3": "^0.6.1",
+ "lodash": "^4.17.4",
+ "uuid": "^3.1.0",
+ "web3": "^0.20.0"
+ }
}
diff --git a/packages/0x.js/src/0x.ts b/packages/0x.js/src/0x.ts
index dec26e3eb..f8a484c5d 100644
--- a/packages/0x.js/src/0x.ts
+++ b/packages/0x.js/src/0x.ts
@@ -13,13 +13,13 @@ import { TokenWrapper } from './contract_wrappers/token_wrapper';
import { OrderStateWatcher } from './order_watcher/order_state_watcher';
import { zeroExConfigSchema } from './schemas/zero_ex_config_schema';
import {
- ECSignature,
- Order,
- SignedOrder,
- TransactionReceiptWithDecodedLogs,
- Web3Provider,
- ZeroExConfig,
- ZeroExError,
+ ECSignature,
+ Order,
+ SignedOrder,
+ TransactionReceiptWithDecodedLogs,
+ Web3Provider,
+ ZeroExConfig,
+ ZeroExError,
} from './types';
import { AbiDecoder } from './utils/abi_decoder';
import { assert } from './utils/assert';
@@ -33,318 +33,318 @@ import { utils } from './utils/utils';
* and all calls to the library should be made through a ZeroEx instance.
*/
export class ZeroEx {
- /**
- * When creating an order without a specified taker or feeRecipient you must supply the Solidity
- * address null type (as opposed to Javascripts `null`, `undefined` or empty string). We expose
- * this constant for your convenience.
- */
- public static NULL_ADDRESS = constants.NULL_ADDRESS;
+ /**
+ * When creating an order without a specified taker or feeRecipient you must supply the Solidity
+ * address null type (as opposed to Javascripts `null`, `undefined` or empty string). We expose
+ * this constant for your convenience.
+ */
+ public static NULL_ADDRESS = constants.NULL_ADDRESS;
- /**
- * An instance of the ExchangeWrapper class containing methods for interacting with the 0x Exchange smart contract.
- */
- public exchange: ExchangeWrapper;
- /**
- * An instance of the TokenRegistryWrapper class containing methods for interacting with the 0x
- * TokenRegistry smart contract.
- */
- public tokenRegistry: TokenRegistryWrapper;
- /**
- * An instance of the TokenWrapper class containing methods for interacting with any ERC20 token smart contract.
- */
- public token: TokenWrapper;
- /**
- * An instance of the EtherTokenWrapper class containing methods for interacting with the
- * wrapped ETH ERC20 token smart contract.
- */
- public etherToken: EtherTokenWrapper;
- /**
- * An instance of the TokenTransferProxyWrapper class containing methods for interacting with the
- * tokenTransferProxy smart contract.
- */
- public proxy: TokenTransferProxyWrapper;
- /**
- * An instance of the OrderStateWatcher class containing methods for watching a set of orders for relevant
- * blockchain state changes.
- */
- public orderStateWatcher: OrderStateWatcher;
- private _web3Wrapper: Web3Wrapper;
- private _abiDecoder: AbiDecoder;
- /**
- * Verifies that the elliptic curve signature `signature` was generated
- * by signing `data` with the private key corresponding to the `signerAddress` address.
- * @param data The hex encoded data signed by the supplied signature.
- * @param signature An object containing the elliptic curve signature parameters.
- * @param signerAddress The hex encoded address that signed the data, producing the supplied signature.
- * @return Whether the signature is valid for the supplied signerAddress and data.
- */
- public static isValidSignature(data: string, signature: ECSignature, signerAddress: string): boolean {
- assert.isHexString('data', data);
- assert.doesConformToSchema('signature', signature, schemas.ecSignatureSchema);
- assert.isETHAddressHex('signerAddress', signerAddress);
+ /**
+ * An instance of the ExchangeWrapper class containing methods for interacting with the 0x Exchange smart contract.
+ */
+ public exchange: ExchangeWrapper;
+ /**
+ * An instance of the TokenRegistryWrapper class containing methods for interacting with the 0x
+ * TokenRegistry smart contract.
+ */
+ public tokenRegistry: TokenRegistryWrapper;
+ /**
+ * An instance of the TokenWrapper class containing methods for interacting with any ERC20 token smart contract.
+ */
+ public token: TokenWrapper;
+ /**
+ * An instance of the EtherTokenWrapper class containing methods for interacting with the
+ * wrapped ETH ERC20 token smart contract.
+ */
+ public etherToken: EtherTokenWrapper;
+ /**
+ * An instance of the TokenTransferProxyWrapper class containing methods for interacting with the
+ * tokenTransferProxy smart contract.
+ */
+ public proxy: TokenTransferProxyWrapper;
+ /**
+ * An instance of the OrderStateWatcher class containing methods for watching a set of orders for relevant
+ * blockchain state changes.
+ */
+ public orderStateWatcher: OrderStateWatcher;
+ private _web3Wrapper: Web3Wrapper;
+ private _abiDecoder: AbiDecoder;
+ /**
+ * Verifies that the elliptic curve signature `signature` was generated
+ * by signing `data` with the private key corresponding to the `signerAddress` address.
+ * @param data The hex encoded data signed by the supplied signature.
+ * @param signature An object containing the elliptic curve signature parameters.
+ * @param signerAddress The hex encoded address that signed the data, producing the supplied signature.
+ * @return Whether the signature is valid for the supplied signerAddress and data.
+ */
+ public static isValidSignature(data: string, signature: ECSignature, signerAddress: string): boolean {
+ assert.isHexString('data', data);
+ assert.doesConformToSchema('signature', signature, schemas.ecSignatureSchema);
+ assert.isETHAddressHex('signerAddress', signerAddress);
- const isValidSignature = signatureUtils.isValidSignature(data, signature, signerAddress);
- return isValidSignature;
- }
- /**
- * Generates a pseudo-random 256-bit salt.
- * The salt can be included in an 0x order, ensuring that the order generates a unique orderHash
- * and will not collide with other outstanding orders that are identical in all other parameters.
- * @return A pseudo-random 256-bit number that can be used as a salt.
- */
- public static generatePseudoRandomSalt(): BigNumber {
- // BigNumber.random returns a pseudo-random number between 0 & 1 with a passed in number of decimal places.
- // Source: https://mikemcl.github.io/bignumber.js/#random
- const randomNumber = BigNumber.random(constants.MAX_DIGITS_IN_UNSIGNED_256_INT);
- const factor = new BigNumber(10).pow(constants.MAX_DIGITS_IN_UNSIGNED_256_INT - 1);
- const salt = randomNumber.times(factor).round();
- return salt;
- }
- /**
- * Checks if the supplied hex encoded order hash is valid.
- * Note: Valid means it has the expected format, not that an order with the orderHash exists.
- * Use this method when processing orderHashes submitted as user input.
- * @param orderHash Hex encoded orderHash.
- * @return Whether the supplied orderHash has the expected format.
- */
- public static isValidOrderHash(orderHash: string): boolean {
- // Since this method can be called to check if any arbitrary string conforms to an orderHash's
- // format, we only assert that we were indeed passed a string.
- assert.isString('orderHash', orderHash);
- const schemaValidator = new SchemaValidator();
- const isValidOrderHash = schemaValidator.validate(orderHash, schemas.orderHashSchema).valid;
- return isValidOrderHash;
- }
- /**
- * A unit amount is defined as the amount of a token above the specified decimal places (integer part).
- * E.g: If a currency has 18 decimal places, 1e18 or one quintillion of the currency is equivalent
- * to 1 unit.
- * @param amount The amount in baseUnits that you would like converted to units.
- * @param decimals The number of decimal places the unit amount has.
- * @return The amount in units.
- */
- public static toUnitAmount(amount: BigNumber, decimals: number): BigNumber {
- assert.isValidBaseUnitAmount('amount', amount);
- assert.isNumber('decimals', decimals);
+ const isValidSignature = signatureUtils.isValidSignature(data, signature, signerAddress);
+ return isValidSignature;
+ }
+ /**
+ * Generates a pseudo-random 256-bit salt.
+ * The salt can be included in an 0x order, ensuring that the order generates a unique orderHash
+ * and will not collide with other outstanding orders that are identical in all other parameters.
+ * @return A pseudo-random 256-bit number that can be used as a salt.
+ */
+ public static generatePseudoRandomSalt(): BigNumber {
+ // BigNumber.random returns a pseudo-random number between 0 & 1 with a passed in number of decimal places.
+ // Source: https://mikemcl.github.io/bignumber.js/#random
+ const randomNumber = BigNumber.random(constants.MAX_DIGITS_IN_UNSIGNED_256_INT);
+ const factor = new BigNumber(10).pow(constants.MAX_DIGITS_IN_UNSIGNED_256_INT - 1);
+ const salt = randomNumber.times(factor).round();
+ return salt;
+ }
+ /**
+ * Checks if the supplied hex encoded order hash is valid.
+ * Note: Valid means it has the expected format, not that an order with the orderHash exists.
+ * Use this method when processing orderHashes submitted as user input.
+ * @param orderHash Hex encoded orderHash.
+ * @return Whether the supplied orderHash has the expected format.
+ */
+ public static isValidOrderHash(orderHash: string): boolean {
+ // Since this method can be called to check if any arbitrary string conforms to an orderHash's
+ // format, we only assert that we were indeed passed a string.
+ assert.isString('orderHash', orderHash);
+ const schemaValidator = new SchemaValidator();
+ const isValidOrderHash = schemaValidator.validate(orderHash, schemas.orderHashSchema).valid;
+ return isValidOrderHash;
+ }
+ /**
+ * A unit amount is defined as the amount of a token above the specified decimal places (integer part).
+ * E.g: If a currency has 18 decimal places, 1e18 or one quintillion of the currency is equivalent
+ * to 1 unit.
+ * @param amount The amount in baseUnits that you would like converted to units.
+ * @param decimals The number of decimal places the unit amount has.
+ * @return The amount in units.
+ */
+ public static toUnitAmount(amount: BigNumber, decimals: number): BigNumber {
+ assert.isValidBaseUnitAmount('amount', amount);
+ assert.isNumber('decimals', decimals);
- const aUnit = new BigNumber(10).pow(decimals);
- const unit = amount.div(aUnit);
- return unit;
- }
- /**
- * A baseUnit is defined as the smallest denomination of a token. An amount expressed in baseUnits
- * is the amount expressed in the smallest denomination.
- * E.g: 1 unit of a token with 18 decimal places is expressed in baseUnits as 1000000000000000000
- * @param amount The amount of units that you would like converted to baseUnits.
- * @param decimals The number of decimal places the unit amount has.
- * @return The amount in baseUnits.
- */
- public static toBaseUnitAmount(amount: BigNumber, decimals: number): BigNumber {
- assert.isBigNumber('amount', amount);
- assert.isNumber('decimals', decimals);
+ const aUnit = new BigNumber(10).pow(decimals);
+ const unit = amount.div(aUnit);
+ return unit;
+ }
+ /**
+ * A baseUnit is defined as the smallest denomination of a token. An amount expressed in baseUnits
+ * is the amount expressed in the smallest denomination.
+ * E.g: 1 unit of a token with 18 decimal places is expressed in baseUnits as 1000000000000000000
+ * @param amount The amount of units that you would like converted to baseUnits.
+ * @param decimals The number of decimal places the unit amount has.
+ * @return The amount in baseUnits.
+ */
+ public static toBaseUnitAmount(amount: BigNumber, decimals: number): BigNumber {
+ assert.isBigNumber('amount', amount);
+ assert.isNumber('decimals', decimals);
- const unit = new BigNumber(10).pow(decimals);
- const baseUnitAmount = amount.times(unit);
- const hasDecimals = baseUnitAmount.decimalPlaces() !== 0;
- if (hasDecimals) {
- throw new Error(`Invalid unit amount: ${amount.toString()} - Too many decimal places`);
- }
- return baseUnitAmount;
- }
- /**
- * Computes the orderHash for a supplied order.
- * @param order An object that conforms to the Order or SignedOrder interface definitions.
- * @return The resulting orderHash from hashing the supplied order.
- */
- @decorators.syncZeroExErrorHandler
- public static getOrderHashHex(order: Order | SignedOrder): string {
- assert.doesConformToSchema('order', order, schemas.orderSchema);
- const orderHashHex = utils.getOrderHashHex(order);
- return orderHashHex;
- }
- /**
- * Instantiates a new ZeroEx instance that provides the public interface to the 0x.js library.
- * @param provider The Web3.js Provider instance you would like the 0x.js library to use for interacting with
- * the Ethereum network.
- * @param config The configuration object. Look up the type for the description.
- * @return An instance of the 0x.js ZeroEx class.
- */
- constructor(provider: Web3Provider, config: ZeroExConfig) {
- assert.isWeb3Provider('provider', provider);
- assert.doesConformToSchema('config', config, zeroExConfigSchema);
- const artifactJSONs = _.values(artifacts);
- const abiArrays = _.map(artifactJSONs, artifact => artifact.abi);
- this._abiDecoder = new AbiDecoder(abiArrays);
- const defaults = {
- gasPrice: config.gasPrice,
- };
- this._web3Wrapper = new Web3Wrapper(provider, defaults);
- this.proxy = new TokenTransferProxyWrapper(
- this._web3Wrapper,
- config.networkId,
- config.tokenTransferProxyContractAddress,
- );
- this.token = new TokenWrapper(this._web3Wrapper, config.networkId, this._abiDecoder, this.proxy);
- this.exchange = new ExchangeWrapper(
- this._web3Wrapper,
- config.networkId,
- this._abiDecoder,
- this.token,
- config.exchangeContractAddress,
- config.zrxContractAddress,
- );
- this.tokenRegistry = new TokenRegistryWrapper(
- this._web3Wrapper,
- config.networkId,
- config.tokenRegistryContractAddress,
- );
- this.etherToken = new EtherTokenWrapper(this._web3Wrapper, config.networkId, this._abiDecoder, this.token);
- this.orderStateWatcher = new OrderStateWatcher(
- this._web3Wrapper,
- this._abiDecoder,
- this.token,
- this.exchange,
- config.orderWatcherConfig,
- );
- }
- /**
- * Sets a new web3 provider for 0x.js. Updating the provider will stop all
- * subscriptions so you will need to re-subscribe to all events relevant to your app after this call.
- * @param provider The Web3Provider you would like the 0x.js library to use from now on.
- * @param networkId The id of the network your provider is connected to
- */
- public setProvider(provider: Web3Provider, networkId: number): void {
- this._web3Wrapper.setProvider(provider);
- (this.exchange as any)._invalidateContractInstances();
- (this.exchange as any)._setNetworkId(networkId);
- (this.tokenRegistry as any)._invalidateContractInstance();
- (this.tokenRegistry as any)._setNetworkId(networkId);
- (this.token as any)._invalidateContractInstances();
- (this.token as any)._setNetworkId(networkId);
- (this.proxy as any)._invalidateContractInstance();
- (this.proxy as any)._setNetworkId(networkId);
- (this.etherToken as any)._invalidateContractInstance();
- (this.etherToken as any)._setNetworkId(networkId);
- }
- /**
- * Get user Ethereum addresses available through the supplied web3 provider available for sending transactions.
- * @return An array of available user Ethereum addresses.
- */
- public async getAvailableAddressesAsync(): Promise<string[]> {
- const availableAddresses = await this._web3Wrapper.getAvailableAddressesAsync();
- return availableAddresses;
- }
- /**
- * Signs an orderHash and returns it's elliptic curve signature.
- * This method currently supports TestRPC, Geth and Parity above and below V1.6.6
- * @param orderHash Hex encoded orderHash to sign.
- * @param signerAddress The hex encoded Ethereum address you wish to sign it with. This address
- * must be available via the Web3.Provider supplied to 0x.js.
- * @param shouldAddPersonalMessagePrefix Some signers add the personal message prefix `\x19Ethereum Signed Message`
- * themselves (e.g Parity Signer, Ledger, TestRPC) and others expect it to already be done by the client
- * (e.g Metamask). Depending on which signer this request is going to, decide on whether to add the prefix
- * before sending the request.
- * @return An object containing the Elliptic curve signature parameters generated by signing the orderHash.
- */
- public async signOrderHashAsync(
- orderHash: string,
- signerAddress: string,
- shouldAddPersonalMessagePrefix: boolean,
- ): Promise<ECSignature> {
- assert.isHexString('orderHash', orderHash);
- await assert.isSenderAddressAsync('signerAddress', signerAddress, this._web3Wrapper);
+ const unit = new BigNumber(10).pow(decimals);
+ const baseUnitAmount = amount.times(unit);
+ const hasDecimals = baseUnitAmount.decimalPlaces() !== 0;
+ if (hasDecimals) {
+ throw new Error(`Invalid unit amount: ${amount.toString()} - Too many decimal places`);
+ }
+ return baseUnitAmount;
+ }
+ /**
+ * Computes the orderHash for a supplied order.
+ * @param order An object that conforms to the Order or SignedOrder interface definitions.
+ * @return The resulting orderHash from hashing the supplied order.
+ */
+ @decorators.syncZeroExErrorHandler
+ public static getOrderHashHex(order: Order | SignedOrder): string {
+ assert.doesConformToSchema('order', order, schemas.orderSchema);
+ const orderHashHex = utils.getOrderHashHex(order);
+ return orderHashHex;
+ }
+ /**
+ * Instantiates a new ZeroEx instance that provides the public interface to the 0x.js library.
+ * @param provider The Web3.js Provider instance you would like the 0x.js library to use for interacting with
+ * the Ethereum network.
+ * @param config The configuration object. Look up the type for the description.
+ * @return An instance of the 0x.js ZeroEx class.
+ */
+ constructor(provider: Web3Provider, config: ZeroExConfig) {
+ assert.isWeb3Provider('provider', provider);
+ assert.doesConformToSchema('config', config, zeroExConfigSchema);
+ const artifactJSONs = _.values(artifacts);
+ const abiArrays = _.map(artifactJSONs, artifact => artifact.abi);
+ this._abiDecoder = new AbiDecoder(abiArrays);
+ const defaults = {
+ gasPrice: config.gasPrice,
+ };
+ this._web3Wrapper = new Web3Wrapper(provider, defaults);
+ this.proxy = new TokenTransferProxyWrapper(
+ this._web3Wrapper,
+ config.networkId,
+ config.tokenTransferProxyContractAddress,
+ );
+ this.token = new TokenWrapper(this._web3Wrapper, config.networkId, this._abiDecoder, this.proxy);
+ this.exchange = new ExchangeWrapper(
+ this._web3Wrapper,
+ config.networkId,
+ this._abiDecoder,
+ this.token,
+ config.exchangeContractAddress,
+ config.zrxContractAddress,
+ );
+ this.tokenRegistry = new TokenRegistryWrapper(
+ this._web3Wrapper,
+ config.networkId,
+ config.tokenRegistryContractAddress,
+ );
+ this.etherToken = new EtherTokenWrapper(this._web3Wrapper, config.networkId, this._abiDecoder, this.token);
+ this.orderStateWatcher = new OrderStateWatcher(
+ this._web3Wrapper,
+ this._abiDecoder,
+ this.token,
+ this.exchange,
+ config.orderWatcherConfig,
+ );
+ }
+ /**
+ * Sets a new web3 provider for 0x.js. Updating the provider will stop all
+ * subscriptions so you will need to re-subscribe to all events relevant to your app after this call.
+ * @param provider The Web3Provider you would like the 0x.js library to use from now on.
+ * @param networkId The id of the network your provider is connected to
+ */
+ public setProvider(provider: Web3Provider, networkId: number): void {
+ this._web3Wrapper.setProvider(provider);
+ (this.exchange as any)._invalidateContractInstances();
+ (this.exchange as any)._setNetworkId(networkId);
+ (this.tokenRegistry as any)._invalidateContractInstance();
+ (this.tokenRegistry as any)._setNetworkId(networkId);
+ (this.token as any)._invalidateContractInstances();
+ (this.token as any)._setNetworkId(networkId);
+ (this.proxy as any)._invalidateContractInstance();
+ (this.proxy as any)._setNetworkId(networkId);
+ (this.etherToken as any)._invalidateContractInstance();
+ (this.etherToken as any)._setNetworkId(networkId);
+ }
+ /**
+ * Get user Ethereum addresses available through the supplied web3 provider available for sending transactions.
+ * @return An array of available user Ethereum addresses.
+ */
+ public async getAvailableAddressesAsync(): Promise<string[]> {
+ const availableAddresses = await this._web3Wrapper.getAvailableAddressesAsync();
+ return availableAddresses;
+ }
+ /**
+ * Signs an orderHash and returns it's elliptic curve signature.
+ * This method currently supports TestRPC, Geth and Parity above and below V1.6.6
+ * @param orderHash Hex encoded orderHash to sign.
+ * @param signerAddress The hex encoded Ethereum address you wish to sign it with. This address
+ * must be available via the Web3.Provider supplied to 0x.js.
+ * @param shouldAddPersonalMessagePrefix Some signers add the personal message prefix `\x19Ethereum Signed Message`
+ * themselves (e.g Parity Signer, Ledger, TestRPC) and others expect it to already be done by the client
+ * (e.g Metamask). Depending on which signer this request is going to, decide on whether to add the prefix
+ * before sending the request.
+ * @return An object containing the Elliptic curve signature parameters generated by signing the orderHash.
+ */
+ public async signOrderHashAsync(
+ orderHash: string,
+ signerAddress: string,
+ shouldAddPersonalMessagePrefix: boolean,
+ ): Promise<ECSignature> {
+ assert.isHexString('orderHash', orderHash);
+ await assert.isSenderAddressAsync('signerAddress', signerAddress, this._web3Wrapper);
- let msgHashHex = orderHash;
- if (shouldAddPersonalMessagePrefix) {
- const orderHashBuff = ethUtil.toBuffer(orderHash);
- const msgHashBuff = ethUtil.hashPersonalMessage(orderHashBuff);
- msgHashHex = ethUtil.bufferToHex(msgHashBuff);
- }
+ let msgHashHex = orderHash;
+ if (shouldAddPersonalMessagePrefix) {
+ const orderHashBuff = ethUtil.toBuffer(orderHash);
+ const msgHashBuff = ethUtil.hashPersonalMessage(orderHashBuff);
+ msgHashHex = ethUtil.bufferToHex(msgHashBuff);
+ }
- const signature = await this._web3Wrapper.signTransactionAsync(signerAddress, msgHashHex);
+ const signature = await this._web3Wrapper.signTransactionAsync(signerAddress, msgHashHex);
- // HACK: There is no consensus on whether the signatureHex string should be formatted as
- // v + r + s OR r + s + v, and different clients (even different versions of the same client)
- // return the signature params in different orders. In order to support all client implementations,
- // we parse the signature in both ways, and evaluate if either one is a valid signature.
- const validVParamValues = [27, 28];
- const ecSignatureVRS = signatureUtils.parseSignatureHexAsVRS(signature);
- if (_.includes(validVParamValues, ecSignatureVRS.v)) {
- const isValidVRSSignature = ZeroEx.isValidSignature(orderHash, ecSignatureVRS, signerAddress);
- if (isValidVRSSignature) {
- return ecSignatureVRS;
- }
- }
+ // HACK: There is no consensus on whether the signatureHex string should be formatted as
+ // v + r + s OR r + s + v, and different clients (even different versions of the same client)
+ // return the signature params in different orders. In order to support all client implementations,
+ // we parse the signature in both ways, and evaluate if either one is a valid signature.
+ const validVParamValues = [27, 28];
+ const ecSignatureVRS = signatureUtils.parseSignatureHexAsVRS(signature);
+ if (_.includes(validVParamValues, ecSignatureVRS.v)) {
+ const isValidVRSSignature = ZeroEx.isValidSignature(orderHash, ecSignatureVRS, signerAddress);
+ if (isValidVRSSignature) {
+ return ecSignatureVRS;
+ }
+ }
- const ecSignatureRSV = signatureUtils.parseSignatureHexAsRSV(signature);
- if (_.includes(validVParamValues, ecSignatureRSV.v)) {
- const isValidRSVSignature = ZeroEx.isValidSignature(orderHash, ecSignatureRSV, signerAddress);
- if (isValidRSVSignature) {
- return ecSignatureRSV;
- }
- }
+ const ecSignatureRSV = signatureUtils.parseSignatureHexAsRSV(signature);
+ if (_.includes(validVParamValues, ecSignatureRSV.v)) {
+ const isValidRSVSignature = ZeroEx.isValidSignature(orderHash, ecSignatureRSV, signerAddress);
+ if (isValidRSVSignature) {
+ return ecSignatureRSV;
+ }
+ }
- throw new Error(ZeroExError.InvalidSignature);
- }
- /**
- * Waits for a transaction to be mined and returns the transaction receipt.
- * @param txHash Transaction hash
- * @param pollingIntervalMs How often (in ms) should we check if the transaction is mined.
- * @param timeoutMs How long (in ms) to poll for transaction mined until aborting.
- * @return Transaction receipt with decoded log args.
- */
- public async awaitTransactionMinedAsync(
- txHash: string,
- pollingIntervalMs = 1000,
- timeoutMs?: number,
- ): Promise<TransactionReceiptWithDecodedLogs> {
- let timeoutExceeded = false;
- if (timeoutMs) {
- setTimeout(() => (timeoutExceeded = true), timeoutMs);
- }
+ throw new Error(ZeroExError.InvalidSignature);
+ }
+ /**
+ * Waits for a transaction to be mined and returns the transaction receipt.
+ * @param txHash Transaction hash
+ * @param pollingIntervalMs How often (in ms) should we check if the transaction is mined.
+ * @param timeoutMs How long (in ms) to poll for transaction mined until aborting.
+ * @return Transaction receipt with decoded log args.
+ */
+ public async awaitTransactionMinedAsync(
+ txHash: string,
+ pollingIntervalMs = 1000,
+ timeoutMs?: number,
+ ): Promise<TransactionReceiptWithDecodedLogs> {
+ let timeoutExceeded = false;
+ if (timeoutMs) {
+ setTimeout(() => (timeoutExceeded = true), timeoutMs);
+ }
- const txReceiptPromise = new Promise(
- (resolve: (receipt: TransactionReceiptWithDecodedLogs) => void, reject) => {
- const intervalId = intervalUtils.setAsyncExcludingInterval(
- async () => {
- if (timeoutExceeded) {
- intervalUtils.clearAsyncExcludingInterval(intervalId);
- return reject(ZeroExError.TransactionMiningTimeout);
- }
+ const txReceiptPromise = new Promise(
+ (resolve: (receipt: TransactionReceiptWithDecodedLogs) => void, reject) => {
+ const intervalId = intervalUtils.setAsyncExcludingInterval(
+ async () => {
+ if (timeoutExceeded) {
+ intervalUtils.clearAsyncExcludingInterval(intervalId);
+ return reject(ZeroExError.TransactionMiningTimeout);
+ }
- const transactionReceipt = await this._web3Wrapper.getTransactionReceiptAsync(txHash);
- if (!_.isNull(transactionReceipt)) {
- intervalUtils.clearAsyncExcludingInterval(intervalId);
- const logsWithDecodedArgs = _.map(
- transactionReceipt.logs,
- this._abiDecoder.tryToDecodeLogOrNoop.bind(this._abiDecoder),
- );
- const transactionReceiptWithDecodedLogArgs: TransactionReceiptWithDecodedLogs = {
- ...transactionReceipt,
- logs: logsWithDecodedArgs,
- };
- resolve(transactionReceiptWithDecodedLogArgs);
- }
- },
- pollingIntervalMs,
- (err: Error) => {
- intervalUtils.clearAsyncExcludingInterval(intervalId);
- reject(err);
- },
- );
- },
- );
+ const transactionReceipt = await this._web3Wrapper.getTransactionReceiptAsync(txHash);
+ if (!_.isNull(transactionReceipt)) {
+ intervalUtils.clearAsyncExcludingInterval(intervalId);
+ const logsWithDecodedArgs = _.map(
+ transactionReceipt.logs,
+ this._abiDecoder.tryToDecodeLogOrNoop.bind(this._abiDecoder),
+ );
+ const transactionReceiptWithDecodedLogArgs: TransactionReceiptWithDecodedLogs = {
+ ...transactionReceipt,
+ logs: logsWithDecodedArgs,
+ };
+ resolve(transactionReceiptWithDecodedLogArgs);
+ }
+ },
+ pollingIntervalMs,
+ (err: Error) => {
+ intervalUtils.clearAsyncExcludingInterval(intervalId);
+ reject(err);
+ },
+ );
+ },
+ );
- return txReceiptPromise;
- }
- /*
+ return txReceiptPromise;
+ }
+ /*
* HACK: `TokenWrapper` needs a token transfer proxy address. `TokenTransferProxy` address is fetched from
* an `ExchangeWrapper`. `ExchangeWrapper` needs `TokenWrapper` to validate orders, creating a dependency cycle.
* In order to break this - we create this function here and pass it as a parameter to the `TokenWrapper`
* and `ProxyWrapper`.
*/
- private async _getTokenTransferProxyAddressAsync(): Promise<string> {
- const tokenTransferProxyAddress = await (this.exchange as any)._getTokenTransferProxyAddressAsync();
- return tokenTransferProxyAddress;
- }
+ private async _getTokenTransferProxyAddressAsync(): Promise<string> {
+ const tokenTransferProxyAddress = await (this.exchange as any)._getTokenTransferProxyAddressAsync();
+ return tokenTransferProxyAddress;
+ }
}
diff --git a/packages/0x.js/src/artifacts.ts b/packages/0x.js/src/artifacts.ts
index 7d4bd6757..cbacd7d56 100644
--- a/packages/0x.js/src/artifacts.ts
+++ b/packages/0x.js/src/artifacts.ts
@@ -8,11 +8,11 @@ import * as ZRXArtifact from './artifacts/ZRX.json';
import { Artifact } from './types';
export const artifacts = {
- ZRXArtifact: (ZRXArtifact as any) as Artifact,
- DummyTokenArtifact: (DummyTokenArtifact as any) as Artifact,
- TokenArtifact: (TokenArtifact as any) as Artifact,
- ExchangeArtifact: (ExchangeArtifact as any) as Artifact,
- EtherTokenArtifact: (EtherTokenArtifact as any) as Artifact,
- TokenRegistryArtifact: (TokenRegistryArtifact as any) as Artifact,
- TokenTransferProxyArtifact: (TokenTransferProxyArtifact as any) as Artifact,
+ ZRXArtifact: (ZRXArtifact as any) as Artifact,
+ DummyTokenArtifact: (DummyTokenArtifact as any) as Artifact,
+ TokenArtifact: (TokenArtifact as any) as Artifact,
+ ExchangeArtifact: (ExchangeArtifact as any) as Artifact,
+ EtherTokenArtifact: (EtherTokenArtifact as any) as Artifact,
+ TokenRegistryArtifact: (TokenRegistryArtifact as any) as Artifact,
+ TokenTransferProxyArtifact: (TokenTransferProxyArtifact as any) as Artifact,
};
diff --git a/packages/0x.js/src/artifacts/DummyToken.json b/packages/0x.js/src/artifacts/DummyToken.json
index f96d63c21..f64a8cd3d 100644
--- a/packages/0x.js/src/artifacts/DummyToken.json
+++ b/packages/0x.js/src/artifacts/DummyToken.json
@@ -1,22 +1,22 @@
{
- "contract_name": "DummyToken",
- "abi": [
- {
- "constant": false,
- "inputs": [
- {
- "name": "_target",
- "type": "address"
- },
- {
- "name": "_value",
- "type": "uint256"
- }
- ],
- "name": "setBalance",
- "outputs": [],
- "payable": false,
- "type": "function"
- }
- ]
+ "contract_name": "DummyToken",
+ "abi": [
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "name": "_target",
+ "type": "address"
+ },
+ {
+ "name": "_value",
+ "type": "uint256"
+ }
+ ],
+ "name": "setBalance",
+ "outputs": [],
+ "payable": false,
+ "type": "function"
+ }
+ ]
}
diff --git a/packages/0x.js/src/artifacts/EtherToken.json b/packages/0x.js/src/artifacts/EtherToken.json
index ca5f9c035..26cca57cd 100644
--- a/packages/0x.js/src/artifacts/EtherToken.json
+++ b/packages/0x.js/src/artifacts/EtherToken.json
@@ -1,287 +1,287 @@
{
- "contract_name": "EtherToken",
- "abi": [
- {
- "constant": true,
- "inputs": [],
- "name": "name",
- "outputs": [
- {
- "name": "",
- "type": "string"
- }
- ],
- "payable": false,
- "type": "function"
- },
- {
- "constant": false,
- "inputs": [
- {
- "name": "_spender",
- "type": "address"
- },
- {
- "name": "_value",
- "type": "uint256"
- }
- ],
- "name": "approve",
- "outputs": [
- {
- "name": "",
- "type": "bool"
- }
- ],
- "payable": false,
- "type": "function"
- },
- {
- "constant": true,
- "inputs": [],
- "name": "totalSupply",
- "outputs": [
- {
- "name": "",
- "type": "uint256"
- }
- ],
- "payable": false,
- "type": "function"
- },
- {
- "constant": false,
- "inputs": [
- {
- "name": "_from",
- "type": "address"
- },
- {
- "name": "_to",
- "type": "address"
- },
- {
- "name": "_value",
- "type": "uint256"
- }
- ],
- "name": "transferFrom",
- "outputs": [
- {
- "name": "",
- "type": "bool"
- }
- ],
- "payable": false,
- "type": "function"
- },
- {
- "constant": false,
- "inputs": [
- {
- "name": "amount",
- "type": "uint256"
- }
- ],
- "name": "withdraw",
- "outputs": [],
- "payable": false,
- "type": "function"
- },
- {
- "constant": true,
- "inputs": [],
- "name": "decimals",
- "outputs": [
- {
- "name": "",
- "type": "uint8"
- }
- ],
- "payable": false,
- "type": "function"
- },
- {
- "constant": true,
- "inputs": [
- {
- "name": "_owner",
- "type": "address"
- }
- ],
- "name": "balanceOf",
- "outputs": [
- {
- "name": "",
- "type": "uint256"
- }
- ],
- "payable": false,
- "type": "function"
- },
- {
- "constant": true,
- "inputs": [],
- "name": "symbol",
- "outputs": [
- {
- "name": "",
- "type": "string"
- }
- ],
- "payable": false,
- "type": "function"
- },
- {
- "constant": false,
- "inputs": [
- {
- "name": "_to",
- "type": "address"
- },
- {
- "name": "_value",
- "type": "uint256"
- }
- ],
- "name": "transfer",
- "outputs": [
- {
- "name": "",
- "type": "bool"
- }
- ],
- "payable": false,
- "type": "function"
- },
- {
- "constant": false,
- "inputs": [],
- "name": "deposit",
- "outputs": [],
- "payable": true,
- "type": "function"
- },
- {
- "constant": true,
- "inputs": [
- {
- "name": "_owner",
- "type": "address"
- },
- {
- "name": "_spender",
- "type": "address"
- }
- ],
- "name": "allowance",
- "outputs": [
- {
- "name": "",
- "type": "uint256"
- }
- ],
- "payable": false,
- "type": "function"
- },
- {
- "payable": true,
- "type": "fallback"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "name": "_from",
- "type": "address"
- },
- {
- "indexed": true,
- "name": "_to",
- "type": "address"
- },
- {
- "indexed": false,
- "name": "_value",
- "type": "uint256"
- }
- ],
- "name": "Transfer",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "name": "_owner",
- "type": "address"
- },
- {
- "indexed": true,
- "name": "_spender",
- "type": "address"
- },
- {
- "indexed": false,
- "name": "_value",
- "type": "uint256"
- }
- ],
- "name": "Approval",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "name": "_owner",
- "type": "address"
- },
- {
- "indexed": false,
- "name": "_value",
- "type": "uint256"
- }
- ],
- "name": "Deposit",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "name": "_owner",
- "type": "address"
- },
- {
- "indexed": false,
- "name": "_value",
- "type": "uint256"
- }
- ],
- "name": "Withdrawal",
- "type": "event"
- }
- ],
- "networks": {
- "1": {
- "address": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2"
- },
- "3": {
- "address": "0xc00fd9820cd2898cc4c054b7bf142de637ad129a"
- },
- "4": {
- "address": "0xc778417e063141139fce010982780140aa0cd5ab"
- },
- "42": {
- "address": "0x653e49e301e508a13237c0ddc98ae7d4cd2667a1"
- },
- "50": {
- "address": "0x871dd7c2b4b25e1aa18728e9d5f2af4c4e431f5c"
- }
- }
+ "contract_name": "EtherToken",
+ "abi": [
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "name",
+ "outputs": [
+ {
+ "name": "",
+ "type": "string"
+ }
+ ],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "name": "_spender",
+ "type": "address"
+ },
+ {
+ "name": "_value",
+ "type": "uint256"
+ }
+ ],
+ "name": "approve",
+ "outputs": [
+ {
+ "name": "",
+ "type": "bool"
+ }
+ ],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "totalSupply",
+ "outputs": [
+ {
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "name": "_from",
+ "type": "address"
+ },
+ {
+ "name": "_to",
+ "type": "address"
+ },
+ {
+ "name": "_value",
+ "type": "uint256"
+ }
+ ],
+ "name": "transferFrom",
+ "outputs": [
+ {
+ "name": "",
+ "type": "bool"
+ }
+ ],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "name": "amount",
+ "type": "uint256"
+ }
+ ],
+ "name": "withdraw",
+ "outputs": [],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "decimals",
+ "outputs": [
+ {
+ "name": "",
+ "type": "uint8"
+ }
+ ],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "name": "_owner",
+ "type": "address"
+ }
+ ],
+ "name": "balanceOf",
+ "outputs": [
+ {
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "symbol",
+ "outputs": [
+ {
+ "name": "",
+ "type": "string"
+ }
+ ],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "name": "_to",
+ "type": "address"
+ },
+ {
+ "name": "_value",
+ "type": "uint256"
+ }
+ ],
+ "name": "transfer",
+ "outputs": [
+ {
+ "name": "",
+ "type": "bool"
+ }
+ ],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [],
+ "name": "deposit",
+ "outputs": [],
+ "payable": true,
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "name": "_owner",
+ "type": "address"
+ },
+ {
+ "name": "_spender",
+ "type": "address"
+ }
+ ],
+ "name": "allowance",
+ "outputs": [
+ {
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "payable": true,
+ "type": "fallback"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "name": "_from",
+ "type": "address"
+ },
+ {
+ "indexed": true,
+ "name": "_to",
+ "type": "address"
+ },
+ {
+ "indexed": false,
+ "name": "_value",
+ "type": "uint256"
+ }
+ ],
+ "name": "Transfer",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "name": "_owner",
+ "type": "address"
+ },
+ {
+ "indexed": true,
+ "name": "_spender",
+ "type": "address"
+ },
+ {
+ "indexed": false,
+ "name": "_value",
+ "type": "uint256"
+ }
+ ],
+ "name": "Approval",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "name": "_owner",
+ "type": "address"
+ },
+ {
+ "indexed": false,
+ "name": "_value",
+ "type": "uint256"
+ }
+ ],
+ "name": "Deposit",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "name": "_owner",
+ "type": "address"
+ },
+ {
+ "indexed": false,
+ "name": "_value",
+ "type": "uint256"
+ }
+ ],
+ "name": "Withdrawal",
+ "type": "event"
+ }
+ ],
+ "networks": {
+ "1": {
+ "address": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2"
+ },
+ "3": {
+ "address": "0xc00fd9820cd2898cc4c054b7bf142de637ad129a"
+ },
+ "4": {
+ "address": "0xc778417e063141139fce010982780140aa0cd5ab"
+ },
+ "42": {
+ "address": "0x653e49e301e508a13237c0ddc98ae7d4cd2667a1"
+ },
+ "50": {
+ "address": "0x871dd7c2b4b25e1aa18728e9d5f2af4c4e431f5c"
+ }
+ }
}
diff --git a/packages/0x.js/src/artifacts/Exchange.json b/packages/0x.js/src/artifacts/Exchange.json
index 44bb41d4d..af8db7360 100644
--- a/packages/0x.js/src/artifacts/Exchange.json
+++ b/packages/0x.js/src/artifacts/Exchange.json
@@ -1,610 +1,610 @@
{
- "contract_name": "Exchange",
- "abi": [
- {
- "constant": true,
- "inputs": [
- {
- "name": "numerator",
- "type": "uint256"
- },
- {
- "name": "denominator",
- "type": "uint256"
- },
- {
- "name": "target",
- "type": "uint256"
- }
- ],
- "name": "isRoundingError",
- "outputs": [
- {
- "name": "",
- "type": "bool"
- }
- ],
- "payable": false,
- "type": "function"
- },
- {
- "constant": true,
- "inputs": [
- {
- "name": "",
- "type": "bytes32"
- }
- ],
- "name": "filled",
- "outputs": [
- {
- "name": "",
- "type": "uint256"
- }
- ],
- "payable": false,
- "type": "function"
- },
- {
- "constant": true,
- "inputs": [
- {
- "name": "",
- "type": "bytes32"
- }
- ],
- "name": "cancelled",
- "outputs": [
- {
- "name": "",
- "type": "uint256"
- }
- ],
- "payable": false,
- "type": "function"
- },
- {
- "constant": false,
- "inputs": [
- {
- "name": "orderAddresses",
- "type": "address[5][]"
- },
- {
- "name": "orderValues",
- "type": "uint256[6][]"
- },
- {
- "name": "fillTakerTokenAmount",
- "type": "uint256"
- },
- {
- "name": "shouldThrowOnInsufficientBalanceOrAllowance",
- "type": "bool"
- },
- {
- "name": "v",
- "type": "uint8[]"
- },
- {
- "name": "r",
- "type": "bytes32[]"
- },
- {
- "name": "s",
- "type": "bytes32[]"
- }
- ],
- "name": "fillOrdersUpTo",
- "outputs": [
- {
- "name": "",
- "type": "uint256"
- }
- ],
- "payable": false,
- "type": "function"
- },
- {
- "constant": false,
- "inputs": [
- {
- "name": "orderAddresses",
- "type": "address[5]"
- },
- {
- "name": "orderValues",
- "type": "uint256[6]"
- },
- {
- "name": "cancelTakerTokenAmount",
- "type": "uint256"
- }
- ],
- "name": "cancelOrder",
- "outputs": [
- {
- "name": "",
- "type": "uint256"
- }
- ],
- "payable": false,
- "type": "function"
- },
- {
- "constant": true,
- "inputs": [],
- "name": "ZRX_TOKEN_CONTRACT",
- "outputs": [
- {
- "name": "",
- "type": "address"
- }
- ],
- "payable": false,
- "type": "function"
- },
- {
- "constant": false,
- "inputs": [
- {
- "name": "orderAddresses",
- "type": "address[5][]"
- },
- {
- "name": "orderValues",
- "type": "uint256[6][]"
- },
- {
- "name": "fillTakerTokenAmounts",
- "type": "uint256[]"
- },
- {
- "name": "v",
- "type": "uint8[]"
- },
- {
- "name": "r",
- "type": "bytes32[]"
- },
- {
- "name": "s",
- "type": "bytes32[]"
- }
- ],
- "name": "batchFillOrKillOrders",
- "outputs": [],
- "payable": false,
- "type": "function"
- },
- {
- "constant": false,
- "inputs": [
- {
- "name": "orderAddresses",
- "type": "address[5]"
- },
- {
- "name": "orderValues",
- "type": "uint256[6]"
- },
- {
- "name": "fillTakerTokenAmount",
- "type": "uint256"
- },
- {
- "name": "v",
- "type": "uint8"
- },
- {
- "name": "r",
- "type": "bytes32"
- },
- {
- "name": "s",
- "type": "bytes32"
- }
- ],
- "name": "fillOrKillOrder",
- "outputs": [],
- "payable": false,
- "type": "function"
- },
- {
- "constant": true,
- "inputs": [
- {
- "name": "orderHash",
- "type": "bytes32"
- }
- ],
- "name": "getUnavailableTakerTokenAmount",
- "outputs": [
- {
- "name": "",
- "type": "uint256"
- }
- ],
- "payable": false,
- "type": "function"
- },
- {
- "constant": true,
- "inputs": [
- {
- "name": "signer",
- "type": "address"
- },
- {
- "name": "hash",
- "type": "bytes32"
- },
- {
- "name": "v",
- "type": "uint8"
- },
- {
- "name": "r",
- "type": "bytes32"
- },
- {
- "name": "s",
- "type": "bytes32"
- }
- ],
- "name": "isValidSignature",
- "outputs": [
- {
- "name": "",
- "type": "bool"
- }
- ],
- "payable": false,
- "type": "function"
- },
- {
- "constant": true,
- "inputs": [
- {
- "name": "numerator",
- "type": "uint256"
- },
- {
- "name": "denominator",
- "type": "uint256"
- },
- {
- "name": "target",
- "type": "uint256"
- }
- ],
- "name": "getPartialAmount",
- "outputs": [
- {
- "name": "",
- "type": "uint256"
- }
- ],
- "payable": false,
- "type": "function"
- },
- {
- "constant": true,
- "inputs": [],
- "name": "TOKEN_TRANSFER_PROXY_CONTRACT",
- "outputs": [
- {
- "name": "",
- "type": "address"
- }
- ],
- "payable": false,
- "type": "function"
- },
- {
- "constant": false,
- "inputs": [
- {
- "name": "orderAddresses",
- "type": "address[5][]"
- },
- {
- "name": "orderValues",
- "type": "uint256[6][]"
- },
- {
- "name": "fillTakerTokenAmounts",
- "type": "uint256[]"
- },
- {
- "name": "shouldThrowOnInsufficientBalanceOrAllowance",
- "type": "bool"
- },
- {
- "name": "v",
- "type": "uint8[]"
- },
- {
- "name": "r",
- "type": "bytes32[]"
- },
- {
- "name": "s",
- "type": "bytes32[]"
- }
- ],
- "name": "batchFillOrders",
- "outputs": [],
- "payable": false,
- "type": "function"
- },
- {
- "constant": false,
- "inputs": [
- {
- "name": "orderAddresses",
- "type": "address[5][]"
- },
- {
- "name": "orderValues",
- "type": "uint256[6][]"
- },
- {
- "name": "cancelTakerTokenAmounts",
- "type": "uint256[]"
- }
- ],
- "name": "batchCancelOrders",
- "outputs": [],
- "payable": false,
- "type": "function"
- },
- {
- "constant": false,
- "inputs": [
- {
- "name": "orderAddresses",
- "type": "address[5]"
- },
- {
- "name": "orderValues",
- "type": "uint256[6]"
- },
- {
- "name": "fillTakerTokenAmount",
- "type": "uint256"
- },
- {
- "name": "shouldThrowOnInsufficientBalanceOrAllowance",
- "type": "bool"
- },
- {
- "name": "v",
- "type": "uint8"
- },
- {
- "name": "r",
- "type": "bytes32"
- },
- {
- "name": "s",
- "type": "bytes32"
- }
- ],
- "name": "fillOrder",
- "outputs": [
- {
- "name": "filledTakerTokenAmount",
- "type": "uint256"
- }
- ],
- "payable": false,
- "type": "function"
- },
- {
- "constant": true,
- "inputs": [
- {
- "name": "orderAddresses",
- "type": "address[5]"
- },
- {
- "name": "orderValues",
- "type": "uint256[6]"
- }
- ],
- "name": "getOrderHash",
- "outputs": [
- {
- "name": "",
- "type": "bytes32"
- }
- ],
- "payable": false,
- "type": "function"
- },
- {
- "constant": true,
- "inputs": [],
- "name": "EXTERNAL_QUERY_GAS_LIMIT",
- "outputs": [
- {
- "name": "",
- "type": "uint16"
- }
- ],
- "payable": false,
- "type": "function"
- },
- {
- "constant": true,
- "inputs": [],
- "name": "VERSION",
- "outputs": [
- {
- "name": "",
- "type": "string"
- }
- ],
- "payable": false,
- "type": "function"
- },
- {
- "inputs": [
- {
- "name": "_zrxToken",
- "type": "address"
- },
- {
- "name": "_tokenTransferProxy",
- "type": "address"
- }
- ],
- "payable": false,
- "type": "constructor"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "name": "maker",
- "type": "address"
- },
- {
- "indexed": false,
- "name": "taker",
- "type": "address"
- },
- {
- "indexed": true,
- "name": "feeRecipient",
- "type": "address"
- },
- {
- "indexed": false,
- "name": "makerToken",
- "type": "address"
- },
- {
- "indexed": false,
- "name": "takerToken",
- "type": "address"
- },
- {
- "indexed": false,
- "name": "filledMakerTokenAmount",
- "type": "uint256"
- },
- {
- "indexed": false,
- "name": "filledTakerTokenAmount",
- "type": "uint256"
- },
- {
- "indexed": false,
- "name": "paidMakerFee",
- "type": "uint256"
- },
- {
- "indexed": false,
- "name": "paidTakerFee",
- "type": "uint256"
- },
- {
- "indexed": true,
- "name": "tokens",
- "type": "bytes32"
- },
- {
- "indexed": false,
- "name": "orderHash",
- "type": "bytes32"
- }
- ],
- "name": "LogFill",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "name": "maker",
- "type": "address"
- },
- {
- "indexed": true,
- "name": "feeRecipient",
- "type": "address"
- },
- {
- "indexed": false,
- "name": "makerToken",
- "type": "address"
- },
- {
- "indexed": false,
- "name": "takerToken",
- "type": "address"
- },
- {
- "indexed": false,
- "name": "cancelledMakerTokenAmount",
- "type": "uint256"
- },
- {
- "indexed": false,
- "name": "cancelledTakerTokenAmount",
- "type": "uint256"
- },
- {
- "indexed": true,
- "name": "tokens",
- "type": "bytes32"
- },
- {
- "indexed": false,
- "name": "orderHash",
- "type": "bytes32"
- }
- ],
- "name": "LogCancel",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "name": "errorId",
- "type": "uint8"
- },
- {
- "indexed": true,
- "name": "orderHash",
- "type": "bytes32"
- }
- ],
- "name": "LogError",
- "type": "event"
- }
- ],
- "networks": {
- "1": {
- "address": "0x12459c951127e0c374ff9105dda097662a027093"
- },
- "3": {
- "address": "0x479cc461fecd078f766ecc58533d6f69580cf3ac"
- },
- "4": {
- "address": "0x1d16ef40fac01cec8adac2ac49427b9384192c05"
- },
- "42": {
- "address": "0x90fe2af704b34e0224bf2299c838e04d4dcf1364"
- },
- "50": {
- "address": "0x48bacb9266a570d521063ef5dd96e61686dbe788"
- }
- }
+ "contract_name": "Exchange",
+ "abi": [
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "name": "numerator",
+ "type": "uint256"
+ },
+ {
+ "name": "denominator",
+ "type": "uint256"
+ },
+ {
+ "name": "target",
+ "type": "uint256"
+ }
+ ],
+ "name": "isRoundingError",
+ "outputs": [
+ {
+ "name": "",
+ "type": "bool"
+ }
+ ],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "name": "",
+ "type": "bytes32"
+ }
+ ],
+ "name": "filled",
+ "outputs": [
+ {
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "name": "",
+ "type": "bytes32"
+ }
+ ],
+ "name": "cancelled",
+ "outputs": [
+ {
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "name": "orderAddresses",
+ "type": "address[5][]"
+ },
+ {
+ "name": "orderValues",
+ "type": "uint256[6][]"
+ },
+ {
+ "name": "fillTakerTokenAmount",
+ "type": "uint256"
+ },
+ {
+ "name": "shouldThrowOnInsufficientBalanceOrAllowance",
+ "type": "bool"
+ },
+ {
+ "name": "v",
+ "type": "uint8[]"
+ },
+ {
+ "name": "r",
+ "type": "bytes32[]"
+ },
+ {
+ "name": "s",
+ "type": "bytes32[]"
+ }
+ ],
+ "name": "fillOrdersUpTo",
+ "outputs": [
+ {
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "name": "orderAddresses",
+ "type": "address[5]"
+ },
+ {
+ "name": "orderValues",
+ "type": "uint256[6]"
+ },
+ {
+ "name": "cancelTakerTokenAmount",
+ "type": "uint256"
+ }
+ ],
+ "name": "cancelOrder",
+ "outputs": [
+ {
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "ZRX_TOKEN_CONTRACT",
+ "outputs": [
+ {
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "name": "orderAddresses",
+ "type": "address[5][]"
+ },
+ {
+ "name": "orderValues",
+ "type": "uint256[6][]"
+ },
+ {
+ "name": "fillTakerTokenAmounts",
+ "type": "uint256[]"
+ },
+ {
+ "name": "v",
+ "type": "uint8[]"
+ },
+ {
+ "name": "r",
+ "type": "bytes32[]"
+ },
+ {
+ "name": "s",
+ "type": "bytes32[]"
+ }
+ ],
+ "name": "batchFillOrKillOrders",
+ "outputs": [],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "name": "orderAddresses",
+ "type": "address[5]"
+ },
+ {
+ "name": "orderValues",
+ "type": "uint256[6]"
+ },
+ {
+ "name": "fillTakerTokenAmount",
+ "type": "uint256"
+ },
+ {
+ "name": "v",
+ "type": "uint8"
+ },
+ {
+ "name": "r",
+ "type": "bytes32"
+ },
+ {
+ "name": "s",
+ "type": "bytes32"
+ }
+ ],
+ "name": "fillOrKillOrder",
+ "outputs": [],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "name": "orderHash",
+ "type": "bytes32"
+ }
+ ],
+ "name": "getUnavailableTakerTokenAmount",
+ "outputs": [
+ {
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "name": "signer",
+ "type": "address"
+ },
+ {
+ "name": "hash",
+ "type": "bytes32"
+ },
+ {
+ "name": "v",
+ "type": "uint8"
+ },
+ {
+ "name": "r",
+ "type": "bytes32"
+ },
+ {
+ "name": "s",
+ "type": "bytes32"
+ }
+ ],
+ "name": "isValidSignature",
+ "outputs": [
+ {
+ "name": "",
+ "type": "bool"
+ }
+ ],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "name": "numerator",
+ "type": "uint256"
+ },
+ {
+ "name": "denominator",
+ "type": "uint256"
+ },
+ {
+ "name": "target",
+ "type": "uint256"
+ }
+ ],
+ "name": "getPartialAmount",
+ "outputs": [
+ {
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "TOKEN_TRANSFER_PROXY_CONTRACT",
+ "outputs": [
+ {
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "name": "orderAddresses",
+ "type": "address[5][]"
+ },
+ {
+ "name": "orderValues",
+ "type": "uint256[6][]"
+ },
+ {
+ "name": "fillTakerTokenAmounts",
+ "type": "uint256[]"
+ },
+ {
+ "name": "shouldThrowOnInsufficientBalanceOrAllowance",
+ "type": "bool"
+ },
+ {
+ "name": "v",
+ "type": "uint8[]"
+ },
+ {
+ "name": "r",
+ "type": "bytes32[]"
+ },
+ {
+ "name": "s",
+ "type": "bytes32[]"
+ }
+ ],
+ "name": "batchFillOrders",
+ "outputs": [],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "name": "orderAddresses",
+ "type": "address[5][]"
+ },
+ {
+ "name": "orderValues",
+ "type": "uint256[6][]"
+ },
+ {
+ "name": "cancelTakerTokenAmounts",
+ "type": "uint256[]"
+ }
+ ],
+ "name": "batchCancelOrders",
+ "outputs": [],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "name": "orderAddresses",
+ "type": "address[5]"
+ },
+ {
+ "name": "orderValues",
+ "type": "uint256[6]"
+ },
+ {
+ "name": "fillTakerTokenAmount",
+ "type": "uint256"
+ },
+ {
+ "name": "shouldThrowOnInsufficientBalanceOrAllowance",
+ "type": "bool"
+ },
+ {
+ "name": "v",
+ "type": "uint8"
+ },
+ {
+ "name": "r",
+ "type": "bytes32"
+ },
+ {
+ "name": "s",
+ "type": "bytes32"
+ }
+ ],
+ "name": "fillOrder",
+ "outputs": [
+ {
+ "name": "filledTakerTokenAmount",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "name": "orderAddresses",
+ "type": "address[5]"
+ },
+ {
+ "name": "orderValues",
+ "type": "uint256[6]"
+ }
+ ],
+ "name": "getOrderHash",
+ "outputs": [
+ {
+ "name": "",
+ "type": "bytes32"
+ }
+ ],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "EXTERNAL_QUERY_GAS_LIMIT",
+ "outputs": [
+ {
+ "name": "",
+ "type": "uint16"
+ }
+ ],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "VERSION",
+ "outputs": [
+ {
+ "name": "",
+ "type": "string"
+ }
+ ],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "name": "_zrxToken",
+ "type": "address"
+ },
+ {
+ "name": "_tokenTransferProxy",
+ "type": "address"
+ }
+ ],
+ "payable": false,
+ "type": "constructor"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "name": "maker",
+ "type": "address"
+ },
+ {
+ "indexed": false,
+ "name": "taker",
+ "type": "address"
+ },
+ {
+ "indexed": true,
+ "name": "feeRecipient",
+ "type": "address"
+ },
+ {
+ "indexed": false,
+ "name": "makerToken",
+ "type": "address"
+ },
+ {
+ "indexed": false,
+ "name": "takerToken",
+ "type": "address"
+ },
+ {
+ "indexed": false,
+ "name": "filledMakerTokenAmount",
+ "type": "uint256"
+ },
+ {
+ "indexed": false,
+ "name": "filledTakerTokenAmount",
+ "type": "uint256"
+ },
+ {
+ "indexed": false,
+ "name": "paidMakerFee",
+ "type": "uint256"
+ },
+ {
+ "indexed": false,
+ "name": "paidTakerFee",
+ "type": "uint256"
+ },
+ {
+ "indexed": true,
+ "name": "tokens",
+ "type": "bytes32"
+ },
+ {
+ "indexed": false,
+ "name": "orderHash",
+ "type": "bytes32"
+ }
+ ],
+ "name": "LogFill",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "name": "maker",
+ "type": "address"
+ },
+ {
+ "indexed": true,
+ "name": "feeRecipient",
+ "type": "address"
+ },
+ {
+ "indexed": false,
+ "name": "makerToken",
+ "type": "address"
+ },
+ {
+ "indexed": false,
+ "name": "takerToken",
+ "type": "address"
+ },
+ {
+ "indexed": false,
+ "name": "cancelledMakerTokenAmount",
+ "type": "uint256"
+ },
+ {
+ "indexed": false,
+ "name": "cancelledTakerTokenAmount",
+ "type": "uint256"
+ },
+ {
+ "indexed": true,
+ "name": "tokens",
+ "type": "bytes32"
+ },
+ {
+ "indexed": false,
+ "name": "orderHash",
+ "type": "bytes32"
+ }
+ ],
+ "name": "LogCancel",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "name": "errorId",
+ "type": "uint8"
+ },
+ {
+ "indexed": true,
+ "name": "orderHash",
+ "type": "bytes32"
+ }
+ ],
+ "name": "LogError",
+ "type": "event"
+ }
+ ],
+ "networks": {
+ "1": {
+ "address": "0x12459c951127e0c374ff9105dda097662a027093"
+ },
+ "3": {
+ "address": "0x479cc461fecd078f766ecc58533d6f69580cf3ac"
+ },
+ "4": {
+ "address": "0x1d16ef40fac01cec8adac2ac49427b9384192c05"
+ },
+ "42": {
+ "address": "0x90fe2af704b34e0224bf2299c838e04d4dcf1364"
+ },
+ "50": {
+ "address": "0x48bacb9266a570d521063ef5dd96e61686dbe788"
+ }
+ }
}
diff --git a/packages/0x.js/src/artifacts/Token.json b/packages/0x.js/src/artifacts/Token.json
index bd4208275..3b5a86ae0 100644
--- a/packages/0x.js/src/artifacts/Token.json
+++ b/packages/0x.js/src/artifacts/Token.json
@@ -1,172 +1,172 @@
{
- "contract_name": "Token",
- "abi": [
- {
- "constant": false,
- "inputs": [
- {
- "name": "_spender",
- "type": "address"
- },
- {
- "name": "_value",
- "type": "uint256"
- }
- ],
- "name": "approve",
- "outputs": [
- {
- "name": "success",
- "type": "bool"
- }
- ],
- "payable": false,
- "type": "function"
- },
- {
- "constant": true,
- "inputs": [],
- "name": "totalSupply",
- "outputs": [
- {
- "name": "supply",
- "type": "uint256"
- }
- ],
- "payable": false,
- "type": "function"
- },
- {
- "constant": false,
- "inputs": [
- {
- "name": "_from",
- "type": "address"
- },
- {
- "name": "_to",
- "type": "address"
- },
- {
- "name": "_value",
- "type": "uint256"
- }
- ],
- "name": "transferFrom",
- "outputs": [
- {
- "name": "success",
- "type": "bool"
- }
- ],
- "payable": false,
- "type": "function"
- },
- {
- "constant": true,
- "inputs": [
- {
- "name": "_owner",
- "type": "address"
- }
- ],
- "name": "balanceOf",
- "outputs": [
- {
- "name": "balance",
- "type": "uint256"
- }
- ],
- "payable": false,
- "type": "function"
- },
- {
- "constant": false,
- "inputs": [
- {
- "name": "_to",
- "type": "address"
- },
- {
- "name": "_value",
- "type": "uint256"
- }
- ],
- "name": "transfer",
- "outputs": [
- {
- "name": "success",
- "type": "bool"
- }
- ],
- "payable": false,
- "type": "function"
- },
- {
- "constant": true,
- "inputs": [
- {
- "name": "_owner",
- "type": "address"
- },
- {
- "name": "_spender",
- "type": "address"
- }
- ],
- "name": "allowance",
- "outputs": [
- {
- "name": "remaining",
- "type": "uint256"
- }
- ],
- "payable": false,
- "type": "function"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "name": "_from",
- "type": "address"
- },
- {
- "indexed": true,
- "name": "_to",
- "type": "address"
- },
- {
- "indexed": false,
- "name": "_value",
- "type": "uint256"
- }
- ],
- "name": "Transfer",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "name": "_owner",
- "type": "address"
- },
- {
- "indexed": true,
- "name": "_spender",
- "type": "address"
- },
- {
- "indexed": false,
- "name": "_value",
- "type": "uint256"
- }
- ],
- "name": "Approval",
- "type": "event"
- }
- ]
+ "contract_name": "Token",
+ "abi": [
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "name": "_spender",
+ "type": "address"
+ },
+ {
+ "name": "_value",
+ "type": "uint256"
+ }
+ ],
+ "name": "approve",
+ "outputs": [
+ {
+ "name": "success",
+ "type": "bool"
+ }
+ ],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "totalSupply",
+ "outputs": [
+ {
+ "name": "supply",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "name": "_from",
+ "type": "address"
+ },
+ {
+ "name": "_to",
+ "type": "address"
+ },
+ {
+ "name": "_value",
+ "type": "uint256"
+ }
+ ],
+ "name": "transferFrom",
+ "outputs": [
+ {
+ "name": "success",
+ "type": "bool"
+ }
+ ],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "name": "_owner",
+ "type": "address"
+ }
+ ],
+ "name": "balanceOf",
+ "outputs": [
+ {
+ "name": "balance",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "name": "_to",
+ "type": "address"
+ },
+ {
+ "name": "_value",
+ "type": "uint256"
+ }
+ ],
+ "name": "transfer",
+ "outputs": [
+ {
+ "name": "success",
+ "type": "bool"
+ }
+ ],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "name": "_owner",
+ "type": "address"
+ },
+ {
+ "name": "_spender",
+ "type": "address"
+ }
+ ],
+ "name": "allowance",
+ "outputs": [
+ {
+ "name": "remaining",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "name": "_from",
+ "type": "address"
+ },
+ {
+ "indexed": true,
+ "name": "_to",
+ "type": "address"
+ },
+ {
+ "indexed": false,
+ "name": "_value",
+ "type": "uint256"
+ }
+ ],
+ "name": "Transfer",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "name": "_owner",
+ "type": "address"
+ },
+ {
+ "indexed": true,
+ "name": "_spender",
+ "type": "address"
+ },
+ {
+ "indexed": false,
+ "name": "_value",
+ "type": "uint256"
+ }
+ ],
+ "name": "Approval",
+ "type": "event"
+ }
+ ]
}
diff --git a/packages/0x.js/src/artifacts/TokenRegistry.json b/packages/0x.js/src/artifacts/TokenRegistry.json
index 490ea0c34..0f583628c 100644
--- a/packages/0x.js/src/artifacts/TokenRegistry.json
+++ b/packages/0x.js/src/artifacts/TokenRegistry.json
@@ -1,547 +1,547 @@
{
- "contract_name": "TokenRegistry",
- "abi": [
- {
- "constant": false,
- "inputs": [
- {
- "name": "_token",
- "type": "address"
- },
- {
- "name": "_index",
- "type": "uint256"
- }
- ],
- "name": "removeToken",
- "outputs": [],
- "payable": false,
- "type": "function"
- },
- {
- "constant": true,
- "inputs": [
- {
- "name": "_name",
- "type": "string"
- }
- ],
- "name": "getTokenAddressByName",
- "outputs": [
- {
- "name": "",
- "type": "address"
- }
- ],
- "payable": false,
- "type": "function"
- },
- {
- "constant": true,
- "inputs": [
- {
- "name": "_symbol",
- "type": "string"
- }
- ],
- "name": "getTokenAddressBySymbol",
- "outputs": [
- {
- "name": "",
- "type": "address"
- }
- ],
- "payable": false,
- "type": "function"
- },
- {
- "constant": false,
- "inputs": [
- {
- "name": "_token",
- "type": "address"
- },
- {
- "name": "_swarmHash",
- "type": "bytes"
- }
- ],
- "name": "setTokenSwarmHash",
- "outputs": [],
- "payable": false,
- "type": "function"
- },
- {
- "constant": true,
- "inputs": [
- {
- "name": "_token",
- "type": "address"
- }
- ],
- "name": "getTokenMetaData",
- "outputs": [
- {
- "name": "",
- "type": "address"
- },
- {
- "name": "",
- "type": "string"
- },
- {
- "name": "",
- "type": "string"
- },
- {
- "name": "",
- "type": "uint8"
- },
- {
- "name": "",
- "type": "bytes"
- },
- {
- "name": "",
- "type": "bytes"
- }
- ],
- "payable": false,
- "type": "function"
- },
- {
- "constant": true,
- "inputs": [],
- "name": "owner",
- "outputs": [
- {
- "name": "",
- "type": "address"
- }
- ],
- "payable": false,
- "type": "function"
- },
- {
- "constant": false,
- "inputs": [
- {
- "name": "_token",
- "type": "address"
- },
- {
- "name": "_name",
- "type": "string"
- },
- {
- "name": "_symbol",
- "type": "string"
- },
- {
- "name": "_decimals",
- "type": "uint8"
- },
- {
- "name": "_ipfsHash",
- "type": "bytes"
- },
- {
- "name": "_swarmHash",
- "type": "bytes"
- }
- ],
- "name": "addToken",
- "outputs": [],
- "payable": false,
- "type": "function"
- },
- {
- "constant": false,
- "inputs": [
- {
- "name": "_token",
- "type": "address"
- },
- {
- "name": "_name",
- "type": "string"
- }
- ],
- "name": "setTokenName",
- "outputs": [],
- "payable": false,
- "type": "function"
- },
- {
- "constant": true,
- "inputs": [
- {
- "name": "",
- "type": "address"
- }
- ],
- "name": "tokens",
- "outputs": [
- {
- "name": "token",
- "type": "address"
- },
- {
- "name": "name",
- "type": "string"
- },
- {
- "name": "symbol",
- "type": "string"
- },
- {
- "name": "decimals",
- "type": "uint8"
- },
- {
- "name": "ipfsHash",
- "type": "bytes"
- },
- {
- "name": "swarmHash",
- "type": "bytes"
- }
- ],
- "payable": false,
- "type": "function"
- },
- {
- "constant": true,
- "inputs": [
- {
- "name": "",
- "type": "uint256"
- }
- ],
- "name": "tokenAddresses",
- "outputs": [
- {
- "name": "",
- "type": "address"
- }
- ],
- "payable": false,
- "type": "function"
- },
- {
- "constant": true,
- "inputs": [
- {
- "name": "_name",
- "type": "string"
- }
- ],
- "name": "getTokenByName",
- "outputs": [
- {
- "name": "",
- "type": "address"
- },
- {
- "name": "",
- "type": "string"
- },
- {
- "name": "",
- "type": "string"
- },
- {
- "name": "",
- "type": "uint8"
- },
- {
- "name": "",
- "type": "bytes"
- },
- {
- "name": "",
- "type": "bytes"
- }
- ],
- "payable": false,
- "type": "function"
- },
- {
- "constant": true,
- "inputs": [],
- "name": "getTokenAddresses",
- "outputs": [
- {
- "name": "",
- "type": "address[]"
- }
- ],
- "payable": false,
- "type": "function"
- },
- {
- "constant": false,
- "inputs": [
- {
- "name": "_token",
- "type": "address"
- },
- {
- "name": "_ipfsHash",
- "type": "bytes"
- }
- ],
- "name": "setTokenIpfsHash",
- "outputs": [],
- "payable": false,
- "type": "function"
- },
- {
- "constant": true,
- "inputs": [
- {
- "name": "_symbol",
- "type": "string"
- }
- ],
- "name": "getTokenBySymbol",
- "outputs": [
- {
- "name": "",
- "type": "address"
- },
- {
- "name": "",
- "type": "string"
- },
- {
- "name": "",
- "type": "string"
- },
- {
- "name": "",
- "type": "uint8"
- },
- {
- "name": "",
- "type": "bytes"
- },
- {
- "name": "",
- "type": "bytes"
- }
- ],
- "payable": false,
- "type": "function"
- },
- {
- "constant": false,
- "inputs": [
- {
- "name": "_token",
- "type": "address"
- },
- {
- "name": "_symbol",
- "type": "string"
- }
- ],
- "name": "setTokenSymbol",
- "outputs": [],
- "payable": false,
- "type": "function"
- },
- {
- "constant": false,
- "inputs": [
- {
- "name": "newOwner",
- "type": "address"
- }
- ],
- "name": "transferOwnership",
- "outputs": [],
- "payable": false,
- "type": "function"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "name": "token",
- "type": "address"
- },
- {
- "indexed": false,
- "name": "name",
- "type": "string"
- },
- {
- "indexed": false,
- "name": "symbol",
- "type": "string"
- },
- {
- "indexed": false,
- "name": "decimals",
- "type": "uint8"
- },
- {
- "indexed": false,
- "name": "ipfsHash",
- "type": "bytes"
- },
- {
- "indexed": false,
- "name": "swarmHash",
- "type": "bytes"
- }
- ],
- "name": "LogAddToken",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "name": "token",
- "type": "address"
- },
- {
- "indexed": false,
- "name": "name",
- "type": "string"
- },
- {
- "indexed": false,
- "name": "symbol",
- "type": "string"
- },
- {
- "indexed": false,
- "name": "decimals",
- "type": "uint8"
- },
- {
- "indexed": false,
- "name": "ipfsHash",
- "type": "bytes"
- },
- {
- "indexed": false,
- "name": "swarmHash",
- "type": "bytes"
- }
- ],
- "name": "LogRemoveToken",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "name": "token",
- "type": "address"
- },
- {
- "indexed": false,
- "name": "oldName",
- "type": "string"
- },
- {
- "indexed": false,
- "name": "newName",
- "type": "string"
- }
- ],
- "name": "LogTokenNameChange",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "name": "token",
- "type": "address"
- },
- {
- "indexed": false,
- "name": "oldSymbol",
- "type": "string"
- },
- {
- "indexed": false,
- "name": "newSymbol",
- "type": "string"
- }
- ],
- "name": "LogTokenSymbolChange",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "name": "token",
- "type": "address"
- },
- {
- "indexed": false,
- "name": "oldIpfsHash",
- "type": "bytes"
- },
- {
- "indexed": false,
- "name": "newIpfsHash",
- "type": "bytes"
- }
- ],
- "name": "LogTokenIpfsHashChange",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "name": "token",
- "type": "address"
- },
- {
- "indexed": false,
- "name": "oldSwarmHash",
- "type": "bytes"
- },
- {
- "indexed": false,
- "name": "newSwarmHash",
- "type": "bytes"
- }
- ],
- "name": "LogTokenSwarmHashChange",
- "type": "event"
- }
- ],
- "networks": {
- "1": {
- "address": "0x926a74c5c36adf004c87399e65f75628b0f98d2c"
- },
- "3": {
- "address": "0x6b1a50f0bb5a7995444bd3877b22dc89c62843ed"
- },
- "4": {
- "address": "0x4e9aad8184de8833365fea970cd9149372fdf1e6"
- },
- "42": {
- "address": "0xf18e504561f4347bea557f3d4558f559dddbae7f"
- },
- "50": {
- "address": "0x0b1ba0af832d7c05fd64161e0db78e85978e8082"
- }
- }
+ "contract_name": "TokenRegistry",
+ "abi": [
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "name": "_token",
+ "type": "address"
+ },
+ {
+ "name": "_index",
+ "type": "uint256"
+ }
+ ],
+ "name": "removeToken",
+ "outputs": [],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "name": "_name",
+ "type": "string"
+ }
+ ],
+ "name": "getTokenAddressByName",
+ "outputs": [
+ {
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "name": "_symbol",
+ "type": "string"
+ }
+ ],
+ "name": "getTokenAddressBySymbol",
+ "outputs": [
+ {
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "name": "_token",
+ "type": "address"
+ },
+ {
+ "name": "_swarmHash",
+ "type": "bytes"
+ }
+ ],
+ "name": "setTokenSwarmHash",
+ "outputs": [],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "name": "_token",
+ "type": "address"
+ }
+ ],
+ "name": "getTokenMetaData",
+ "outputs": [
+ {
+ "name": "",
+ "type": "address"
+ },
+ {
+ "name": "",
+ "type": "string"
+ },
+ {
+ "name": "",
+ "type": "string"
+ },
+ {
+ "name": "",
+ "type": "uint8"
+ },
+ {
+ "name": "",
+ "type": "bytes"
+ },
+ {
+ "name": "",
+ "type": "bytes"
+ }
+ ],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "owner",
+ "outputs": [
+ {
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "name": "_token",
+ "type": "address"
+ },
+ {
+ "name": "_name",
+ "type": "string"
+ },
+ {
+ "name": "_symbol",
+ "type": "string"
+ },
+ {
+ "name": "_decimals",
+ "type": "uint8"
+ },
+ {
+ "name": "_ipfsHash",
+ "type": "bytes"
+ },
+ {
+ "name": "_swarmHash",
+ "type": "bytes"
+ }
+ ],
+ "name": "addToken",
+ "outputs": [],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "name": "_token",
+ "type": "address"
+ },
+ {
+ "name": "_name",
+ "type": "string"
+ }
+ ],
+ "name": "setTokenName",
+ "outputs": [],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "name": "tokens",
+ "outputs": [
+ {
+ "name": "token",
+ "type": "address"
+ },
+ {
+ "name": "name",
+ "type": "string"
+ },
+ {
+ "name": "symbol",
+ "type": "string"
+ },
+ {
+ "name": "decimals",
+ "type": "uint8"
+ },
+ {
+ "name": "ipfsHash",
+ "type": "bytes"
+ },
+ {
+ "name": "swarmHash",
+ "type": "bytes"
+ }
+ ],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "name": "tokenAddresses",
+ "outputs": [
+ {
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "name": "_name",
+ "type": "string"
+ }
+ ],
+ "name": "getTokenByName",
+ "outputs": [
+ {
+ "name": "",
+ "type": "address"
+ },
+ {
+ "name": "",
+ "type": "string"
+ },
+ {
+ "name": "",
+ "type": "string"
+ },
+ {
+ "name": "",
+ "type": "uint8"
+ },
+ {
+ "name": "",
+ "type": "bytes"
+ },
+ {
+ "name": "",
+ "type": "bytes"
+ }
+ ],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "getTokenAddresses",
+ "outputs": [
+ {
+ "name": "",
+ "type": "address[]"
+ }
+ ],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "name": "_token",
+ "type": "address"
+ },
+ {
+ "name": "_ipfsHash",
+ "type": "bytes"
+ }
+ ],
+ "name": "setTokenIpfsHash",
+ "outputs": [],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "name": "_symbol",
+ "type": "string"
+ }
+ ],
+ "name": "getTokenBySymbol",
+ "outputs": [
+ {
+ "name": "",
+ "type": "address"
+ },
+ {
+ "name": "",
+ "type": "string"
+ },
+ {
+ "name": "",
+ "type": "string"
+ },
+ {
+ "name": "",
+ "type": "uint8"
+ },
+ {
+ "name": "",
+ "type": "bytes"
+ },
+ {
+ "name": "",
+ "type": "bytes"
+ }
+ ],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "name": "_token",
+ "type": "address"
+ },
+ {
+ "name": "_symbol",
+ "type": "string"
+ }
+ ],
+ "name": "setTokenSymbol",
+ "outputs": [],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "name": "newOwner",
+ "type": "address"
+ }
+ ],
+ "name": "transferOwnership",
+ "outputs": [],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "name": "token",
+ "type": "address"
+ },
+ {
+ "indexed": false,
+ "name": "name",
+ "type": "string"
+ },
+ {
+ "indexed": false,
+ "name": "symbol",
+ "type": "string"
+ },
+ {
+ "indexed": false,
+ "name": "decimals",
+ "type": "uint8"
+ },
+ {
+ "indexed": false,
+ "name": "ipfsHash",
+ "type": "bytes"
+ },
+ {
+ "indexed": false,
+ "name": "swarmHash",
+ "type": "bytes"
+ }
+ ],
+ "name": "LogAddToken",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "name": "token",
+ "type": "address"
+ },
+ {
+ "indexed": false,
+ "name": "name",
+ "type": "string"
+ },
+ {
+ "indexed": false,
+ "name": "symbol",
+ "type": "string"
+ },
+ {
+ "indexed": false,
+ "name": "decimals",
+ "type": "uint8"
+ },
+ {
+ "indexed": false,
+ "name": "ipfsHash",
+ "type": "bytes"
+ },
+ {
+ "indexed": false,
+ "name": "swarmHash",
+ "type": "bytes"
+ }
+ ],
+ "name": "LogRemoveToken",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "name": "token",
+ "type": "address"
+ },
+ {
+ "indexed": false,
+ "name": "oldName",
+ "type": "string"
+ },
+ {
+ "indexed": false,
+ "name": "newName",
+ "type": "string"
+ }
+ ],
+ "name": "LogTokenNameChange",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "name": "token",
+ "type": "address"
+ },
+ {
+ "indexed": false,
+ "name": "oldSymbol",
+ "type": "string"
+ },
+ {
+ "indexed": false,
+ "name": "newSymbol",
+ "type": "string"
+ }
+ ],
+ "name": "LogTokenSymbolChange",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "name": "token",
+ "type": "address"
+ },
+ {
+ "indexed": false,
+ "name": "oldIpfsHash",
+ "type": "bytes"
+ },
+ {
+ "indexed": false,
+ "name": "newIpfsHash",
+ "type": "bytes"
+ }
+ ],
+ "name": "LogTokenIpfsHashChange",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "name": "token",
+ "type": "address"
+ },
+ {
+ "indexed": false,
+ "name": "oldSwarmHash",
+ "type": "bytes"
+ },
+ {
+ "indexed": false,
+ "name": "newSwarmHash",
+ "type": "bytes"
+ }
+ ],
+ "name": "LogTokenSwarmHashChange",
+ "type": "event"
+ }
+ ],
+ "networks": {
+ "1": {
+ "address": "0x926a74c5c36adf004c87399e65f75628b0f98d2c"
+ },
+ "3": {
+ "address": "0x6b1a50f0bb5a7995444bd3877b22dc89c62843ed"
+ },
+ "4": {
+ "address": "0x4e9aad8184de8833365fea970cd9149372fdf1e6"
+ },
+ "42": {
+ "address": "0xf18e504561f4347bea557f3d4558f559dddbae7f"
+ },
+ "50": {
+ "address": "0x0b1ba0af832d7c05fd64161e0db78e85978e8082"
+ }
+ }
}
diff --git a/packages/0x.js/src/artifacts/TokenTransferProxy.json b/packages/0x.js/src/artifacts/TokenTransferProxy.json
index 5e45e4903..8cf551ddb 100644
--- a/packages/0x.js/src/artifacts/TokenTransferProxy.json
+++ b/packages/0x.js/src/artifacts/TokenTransferProxy.json
@@ -1,187 +1,187 @@
{
- "contract_name": "TokenTransferProxy",
- "abi": [
- {
- "constant": false,
- "inputs": [
- {
- "name": "token",
- "type": "address"
- },
- {
- "name": "from",
- "type": "address"
- },
- {
- "name": "to",
- "type": "address"
- },
- {
- "name": "value",
- "type": "uint256"
- }
- ],
- "name": "transferFrom",
- "outputs": [
- {
- "name": "",
- "type": "bool"
- }
- ],
- "payable": false,
- "type": "function"
- },
- {
- "constant": false,
- "inputs": [
- {
- "name": "target",
- "type": "address"
- }
- ],
- "name": "addAuthorizedAddress",
- "outputs": [],
- "payable": false,
- "type": "function"
- },
- {
- "constant": true,
- "inputs": [
- {
- "name": "",
- "type": "uint256"
- }
- ],
- "name": "authorities",
- "outputs": [
- {
- "name": "",
- "type": "address"
- }
- ],
- "payable": false,
- "type": "function"
- },
- {
- "constant": false,
- "inputs": [
- {
- "name": "target",
- "type": "address"
- }
- ],
- "name": "removeAuthorizedAddress",
- "outputs": [],
- "payable": false,
- "type": "function"
- },
- {
- "constant": true,
- "inputs": [],
- "name": "owner",
- "outputs": [
- {
- "name": "",
- "type": "address"
- }
- ],
- "payable": false,
- "type": "function"
- },
- {
- "constant": true,
- "inputs": [
- {
- "name": "",
- "type": "address"
- }
- ],
- "name": "authorized",
- "outputs": [
- {
- "name": "",
- "type": "bool"
- }
- ],
- "payable": false,
- "type": "function"
- },
- {
- "constant": true,
- "inputs": [],
- "name": "getAuthorizedAddresses",
- "outputs": [
- {
- "name": "",
- "type": "address[]"
- }
- ],
- "payable": false,
- "type": "function"
- },
- {
- "constant": false,
- "inputs": [
- {
- "name": "newOwner",
- "type": "address"
- }
- ],
- "name": "transferOwnership",
- "outputs": [],
- "payable": false,
- "type": "function"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "name": "target",
- "type": "address"
- },
- {
- "indexed": true,
- "name": "caller",
- "type": "address"
- }
- ],
- "name": "LogAuthorizedAddressAdded",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "name": "target",
- "type": "address"
- },
- {
- "indexed": true,
- "name": "caller",
- "type": "address"
- }
- ],
- "name": "LogAuthorizedAddressRemoved",
- "type": "event"
- }
- ],
- "networks": {
- "1": {
- "address": "0x8da0d80f5007ef1e431dd2127178d224e32c2ef4"
- },
- "3": {
- "address": "0x4e9aad8184de8833365fea970cd9149372fdf1e6"
- },
- "4": {
- "address": "0xa8e9fa8f91e5ae138c74648c9c304f1c75003a8d"
- },
- "42": {
- "address": "0x087eed4bc1ee3de49befbd66c662b434b15d49d4"
- },
- "50": {
- "address": "0x1dc4c1cefef38a777b15aa20260a54e584b16c48"
- }
- }
+ "contract_name": "TokenTransferProxy",
+ "abi": [
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "name": "token",
+ "type": "address"
+ },
+ {
+ "name": "from",
+ "type": "address"
+ },
+ {
+ "name": "to",
+ "type": "address"
+ },
+ {
+ "name": "value",
+ "type": "uint256"
+ }
+ ],
+ "name": "transferFrom",
+ "outputs": [
+ {
+ "name": "",
+ "type": "bool"
+ }
+ ],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "name": "target",
+ "type": "address"
+ }
+ ],
+ "name": "addAuthorizedAddress",
+ "outputs": [],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "name": "authorities",
+ "outputs": [
+ {
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "name": "target",
+ "type": "address"
+ }
+ ],
+ "name": "removeAuthorizedAddress",
+ "outputs": [],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "owner",
+ "outputs": [
+ {
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "name": "authorized",
+ "outputs": [
+ {
+ "name": "",
+ "type": "bool"
+ }
+ ],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "getAuthorizedAddresses",
+ "outputs": [
+ {
+ "name": "",
+ "type": "address[]"
+ }
+ ],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "name": "newOwner",
+ "type": "address"
+ }
+ ],
+ "name": "transferOwnership",
+ "outputs": [],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "name": "target",
+ "type": "address"
+ },
+ {
+ "indexed": true,
+ "name": "caller",
+ "type": "address"
+ }
+ ],
+ "name": "LogAuthorizedAddressAdded",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "name": "target",
+ "type": "address"
+ },
+ {
+ "indexed": true,
+ "name": "caller",
+ "type": "address"
+ }
+ ],
+ "name": "LogAuthorizedAddressRemoved",
+ "type": "event"
+ }
+ ],
+ "networks": {
+ "1": {
+ "address": "0x8da0d80f5007ef1e431dd2127178d224e32c2ef4"
+ },
+ "3": {
+ "address": "0x4e9aad8184de8833365fea970cd9149372fdf1e6"
+ },
+ "4": {
+ "address": "0xa8e9fa8f91e5ae138c74648c9c304f1c75003a8d"
+ },
+ "42": {
+ "address": "0x087eed4bc1ee3de49befbd66c662b434b15d49d4"
+ },
+ "50": {
+ "address": "0x1dc4c1cefef38a777b15aa20260a54e584b16c48"
+ }
+ }
}
diff --git a/packages/0x.js/src/artifacts/ZRX.json b/packages/0x.js/src/artifacts/ZRX.json
index 44f478ab4..e40b8f268 100644
--- a/packages/0x.js/src/artifacts/ZRX.json
+++ b/packages/0x.js/src/artifacts/ZRX.json
@@ -1,20 +1,20 @@
{
- "contract_name": "ZRX",
- "networks": {
- "1": {
- "address": "0xe41d2489571d322189246dafa5ebde1f4699f498"
- },
- "3": {
- "address": "0xa8e9fa8f91e5ae138c74648c9c304f1c75003a8d"
- },
- "4": {
- "address": "0x00f58d6d585f84b2d7267940cede30ce2fe6eae8"
- },
- "42": {
- "address": "0x6ff6c0ff1d68b964901f986d4c9fa3ac68346570"
- },
- "50": {
- "address": "0x1d7022f5b17d2f8b695918fb48fa1089c9f85401"
- }
- }
+ "contract_name": "ZRX",
+ "networks": {
+ "1": {
+ "address": "0xe41d2489571d322189246dafa5ebde1f4699f498"
+ },
+ "3": {
+ "address": "0xa8e9fa8f91e5ae138c74648c9c304f1c75003a8d"
+ },
+ "4": {
+ "address": "0x00f58d6d585f84b2d7267940cede30ce2fe6eae8"
+ },
+ "42": {
+ "address": "0x6ff6c0ff1d68b964901f986d4c9fa3ac68346570"
+ },
+ "50": {
+ "address": "0x1d7022f5b17d2f8b695918fb48fa1089c9f85401"
+ }
+ }
}
diff --git a/packages/0x.js/src/contract_wrappers/contract_wrapper.ts b/packages/0x.js/src/contract_wrappers/contract_wrapper.ts
index d1b753f45..27551c01d 100644
--- a/packages/0x.js/src/contract_wrappers/contract_wrapper.ts
+++ b/packages/0x.js/src/contract_wrappers/contract_wrapper.ts
@@ -5,203 +5,203 @@ import * as _ from 'lodash';
import * as Web3 from 'web3';
import {
- Artifact,
- BlockParamLiteral,
- BlockRange,
- ContractEventArgs,
- ContractEvents,
- EventCallback,
- IndexedFilterValues,
- InternalZeroExError,
- LogWithDecodedArgs,
- RawLog,
- ZeroExError,
+ Artifact,
+ BlockParamLiteral,
+ BlockRange,
+ ContractEventArgs,
+ ContractEvents,
+ EventCallback,
+ IndexedFilterValues,
+ InternalZeroExError,
+ LogWithDecodedArgs,
+ RawLog,
+ ZeroExError,
} from '../types';
import { AbiDecoder } from '../utils/abi_decoder';
import { constants } from '../utils/constants';
import { filterUtils } from '../utils/filter_utils';
const CONTRACT_NAME_TO_NOT_FOUND_ERROR: {
- [contractName: string]: ZeroExError;
+ [contractName: string]: ZeroExError;
} = {
- ZRX: ZeroExError.ZRXContractDoesNotExist,
- EtherToken: ZeroExError.EtherTokenContractDoesNotExist,
- Token: ZeroExError.TokenContractDoesNotExist,
- TokenRegistry: ZeroExError.TokenRegistryContractDoesNotExist,
- TokenTransferProxy: ZeroExError.TokenTransferProxyContractDoesNotExist,
- Exchange: ZeroExError.ExchangeContractDoesNotExist,
+ ZRX: ZeroExError.ZRXContractDoesNotExist,
+ EtherToken: ZeroExError.EtherTokenContractDoesNotExist,
+ Token: ZeroExError.TokenContractDoesNotExist,
+ TokenRegistry: ZeroExError.TokenRegistryContractDoesNotExist,
+ TokenTransferProxy: ZeroExError.TokenTransferProxyContractDoesNotExist,
+ Exchange: ZeroExError.ExchangeContractDoesNotExist,
};
export class ContractWrapper {
- protected _web3Wrapper: Web3Wrapper;
- private _networkId: number;
- private _abiDecoder?: AbiDecoder;
- private _blockAndLogStreamerIfExists: BlockAndLogStreamer | undefined;
- private _blockAndLogStreamInterval: NodeJS.Timer;
- private _filters: { [filterToken: string]: Web3.FilterObject };
- private _filterCallbacks: {
- [filterToken: string]: EventCallback<ContractEventArgs>;
- };
- private _onLogAddedSubscriptionToken: string | undefined;
- private _onLogRemovedSubscriptionToken: string | undefined;
- constructor(web3Wrapper: Web3Wrapper, networkId: number, abiDecoder?: AbiDecoder) {
- this._web3Wrapper = web3Wrapper;
- this._networkId = networkId;
- this._abiDecoder = abiDecoder;
- this._filters = {};
- this._filterCallbacks = {};
- this._blockAndLogStreamerIfExists = undefined;
- this._onLogAddedSubscriptionToken = undefined;
- this._onLogRemovedSubscriptionToken = undefined;
- }
- protected unsubscribeAll(): void {
- const filterTokens = _.keys(this._filterCallbacks);
- _.each(filterTokens, filterToken => {
- this._unsubscribe(filterToken);
- });
- }
- protected _unsubscribe(filterToken: string, err?: Error): void {
- if (_.isUndefined(this._filters[filterToken])) {
- throw new Error(ZeroExError.SubscriptionNotFound);
- }
- if (!_.isUndefined(err)) {
- const callback = this._filterCallbacks[filterToken];
- callback(err, undefined);
- }
- delete this._filters[filterToken];
- delete this._filterCallbacks[filterToken];
- if (_.isEmpty(this._filters)) {
- this._stopBlockAndLogStream();
- }
- }
- protected _subscribe<ArgsType extends ContractEventArgs>(
- address: string,
- eventName: ContractEvents,
- indexFilterValues: IndexedFilterValues,
- abi: Web3.ContractAbi,
- callback: EventCallback<ArgsType>,
- ): string {
- const filter = filterUtils.getFilter(address, eventName, indexFilterValues, abi);
- if (_.isUndefined(this._blockAndLogStreamerIfExists)) {
- this._startBlockAndLogStream();
- }
- const filterToken = filterUtils.generateUUID();
- this._filters[filterToken] = filter;
- this._filterCallbacks[filterToken] = callback as EventCallback<ContractEventArgs>;
- return filterToken;
- }
- protected async _getLogsAsync<ArgsType extends ContractEventArgs>(
- address: string,
- eventName: ContractEvents,
- blockRange: BlockRange,
- indexFilterValues: IndexedFilterValues,
- abi: Web3.ContractAbi,
- ): Promise<Array<LogWithDecodedArgs<ArgsType>>> {
- const filter = filterUtils.getFilter(address, eventName, indexFilterValues, abi, blockRange);
- const logs = await this._web3Wrapper.getLogsAsync(filter);
- const logsWithDecodedArguments = _.map(logs, this._tryToDecodeLogOrNoop.bind(this));
- return logsWithDecodedArguments;
- }
- protected _tryToDecodeLogOrNoop<ArgsType extends ContractEventArgs>(
- log: Web3.LogEntry,
- ): LogWithDecodedArgs<ArgsType> | RawLog {
- if (_.isUndefined(this._abiDecoder)) {
- throw new Error(InternalZeroExError.NoAbiDecoder);
- }
- const logWithDecodedArgs = this._abiDecoder.tryToDecodeLogOrNoop(log);
- return logWithDecodedArgs;
- }
- protected async _instantiateContractIfExistsAsync(
- artifact: Artifact,
- addressIfExists?: string,
- ): Promise<Web3.ContractInstance> {
- let contractAddress: string;
- if (_.isUndefined(addressIfExists)) {
- if (_.isUndefined(artifact.networks[this._networkId])) {
- throw new Error(ZeroExError.ContractNotDeployedOnNetwork);
- }
- contractAddress = artifact.networks[this._networkId].address.toLowerCase();
- } else {
- contractAddress = addressIfExists;
- }
- const doesContractExist = await this._web3Wrapper.doesContractExistAtAddressAsync(contractAddress);
- if (!doesContractExist) {
- throw new Error(CONTRACT_NAME_TO_NOT_FOUND_ERROR[artifact.contract_name]);
- }
- const contractInstance = this._web3Wrapper.getContractInstance(artifact.abi, contractAddress);
- return contractInstance;
- }
- protected _getContractAddress(artifact: Artifact, addressIfExists?: string): string {
- if (_.isUndefined(addressIfExists)) {
- const contractAddress = artifact.networks[this._networkId].address;
- if (_.isUndefined(contractAddress)) {
- throw new Error(ZeroExError.ExchangeContractDoesNotExist);
- }
- return contractAddress;
- } else {
- return addressIfExists;
- }
- }
- private _onLogStateChanged<ArgsType extends ContractEventArgs>(isRemoved: boolean, log: Web3.LogEntry): void {
- _.forEach(this._filters, (filter: Web3.FilterObject, filterToken: string) => {
- if (filterUtils.matchesFilter(log, filter)) {
- const decodedLog = this._tryToDecodeLogOrNoop(log) as LogWithDecodedArgs<ArgsType>;
- const logEvent = {
- log: decodedLog,
- isRemoved,
- };
- this._filterCallbacks[filterToken](null, logEvent);
- }
- });
- }
- private _startBlockAndLogStream(): void {
- if (!_.isUndefined(this._blockAndLogStreamerIfExists)) {
- throw new Error(ZeroExError.SubscriptionAlreadyPresent);
- }
- this._blockAndLogStreamerIfExists = new BlockAndLogStreamer(
- this._web3Wrapper.getBlockAsync.bind(this._web3Wrapper),
- this._web3Wrapper.getLogsAsync.bind(this._web3Wrapper),
- );
- const catchAllLogFilter = {};
- this._blockAndLogStreamerIfExists.addLogFilter(catchAllLogFilter);
- this._blockAndLogStreamInterval = intervalUtils.setAsyncExcludingInterval(
- this._reconcileBlockAsync.bind(this),
- constants.DEFAULT_BLOCK_POLLING_INTERVAL,
- this._onReconcileBlockError.bind(this),
- );
- let isRemoved = false;
- this._onLogAddedSubscriptionToken = this._blockAndLogStreamerIfExists.subscribeToOnLogAdded(
- this._onLogStateChanged.bind(this, isRemoved),
- );
- isRemoved = true;
- this._onLogRemovedSubscriptionToken = this._blockAndLogStreamerIfExists.subscribeToOnLogRemoved(
- this._onLogStateChanged.bind(this, isRemoved),
- );
- }
- private _onReconcileBlockError(err: Error): void {
- const filterTokens = _.keys(this._filterCallbacks);
- _.each(filterTokens, filterToken => {
- this._unsubscribe(filterToken, err);
- });
- }
- private _setNetworkId(networkId: number): void {
- this._networkId = networkId;
- }
- private _stopBlockAndLogStream(): void {
- if (_.isUndefined(this._blockAndLogStreamerIfExists)) {
- throw new Error(ZeroExError.SubscriptionNotFound);
- }
- this._blockAndLogStreamerIfExists.unsubscribeFromOnLogAdded(this._onLogAddedSubscriptionToken as string);
- this._blockAndLogStreamerIfExists.unsubscribeFromOnLogRemoved(this._onLogRemovedSubscriptionToken as string);
- intervalUtils.clearAsyncExcludingInterval(this._blockAndLogStreamInterval);
- delete this._blockAndLogStreamerIfExists;
- }
- private async _reconcileBlockAsync(): Promise<void> {
- const latestBlock = await this._web3Wrapper.getBlockAsync(BlockParamLiteral.Latest);
- // We need to coerce to Block type cause Web3.Block includes types for mempool blocks
- if (!_.isUndefined(this._blockAndLogStreamerIfExists)) {
- // If we clear the interval while fetching the block - this._blockAndLogStreamer will be undefined
- await this._blockAndLogStreamerIfExists.reconcileNewBlock((latestBlock as any) as Block);
- }
- }
+ protected _web3Wrapper: Web3Wrapper;
+ private _networkId: number;
+ private _abiDecoder?: AbiDecoder;
+ private _blockAndLogStreamerIfExists: BlockAndLogStreamer | undefined;
+ private _blockAndLogStreamInterval: NodeJS.Timer;
+ private _filters: { [filterToken: string]: Web3.FilterObject };
+ private _filterCallbacks: {
+ [filterToken: string]: EventCallback<ContractEventArgs>;
+ };
+ private _onLogAddedSubscriptionToken: string | undefined;
+ private _onLogRemovedSubscriptionToken: string | undefined;
+ constructor(web3Wrapper: Web3Wrapper, networkId: number, abiDecoder?: AbiDecoder) {
+ this._web3Wrapper = web3Wrapper;
+ this._networkId = networkId;
+ this._abiDecoder = abiDecoder;
+ this._filters = {};
+ this._filterCallbacks = {};
+ this._blockAndLogStreamerIfExists = undefined;
+ this._onLogAddedSubscriptionToken = undefined;
+ this._onLogRemovedSubscriptionToken = undefined;
+ }
+ protected unsubscribeAll(): void {
+ const filterTokens = _.keys(this._filterCallbacks);
+ _.each(filterTokens, filterToken => {
+ this._unsubscribe(filterToken);
+ });
+ }
+ protected _unsubscribe(filterToken: string, err?: Error): void {
+ if (_.isUndefined(this._filters[filterToken])) {
+ throw new Error(ZeroExError.SubscriptionNotFound);
+ }
+ if (!_.isUndefined(err)) {
+ const callback = this._filterCallbacks[filterToken];
+ callback(err, undefined);
+ }
+ delete this._filters[filterToken];
+ delete this._filterCallbacks[filterToken];
+ if (_.isEmpty(this._filters)) {
+ this._stopBlockAndLogStream();
+ }
+ }
+ protected _subscribe<ArgsType extends ContractEventArgs>(
+ address: string,
+ eventName: ContractEvents,
+ indexFilterValues: IndexedFilterValues,
+ abi: Web3.ContractAbi,
+ callback: EventCallback<ArgsType>,
+ ): string {
+ const filter = filterUtils.getFilter(address, eventName, indexFilterValues, abi);
+ if (_.isUndefined(this._blockAndLogStreamerIfExists)) {
+ this._startBlockAndLogStream();
+ }
+ const filterToken = filterUtils.generateUUID();
+ this._filters[filterToken] = filter;
+ this._filterCallbacks[filterToken] = callback as EventCallback<ContractEventArgs>;
+ return filterToken;
+ }
+ protected async _getLogsAsync<ArgsType extends ContractEventArgs>(
+ address: string,
+ eventName: ContractEvents,
+ blockRange: BlockRange,
+ indexFilterValues: IndexedFilterValues,
+ abi: Web3.ContractAbi,
+ ): Promise<Array<LogWithDecodedArgs<ArgsType>>> {
+ const filter = filterUtils.getFilter(address, eventName, indexFilterValues, abi, blockRange);
+ const logs = await this._web3Wrapper.getLogsAsync(filter);
+ const logsWithDecodedArguments = _.map(logs, this._tryToDecodeLogOrNoop.bind(this));
+ return logsWithDecodedArguments;
+ }
+ protected _tryToDecodeLogOrNoop<ArgsType extends ContractEventArgs>(
+ log: Web3.LogEntry,
+ ): LogWithDecodedArgs<ArgsType> | RawLog {
+ if (_.isUndefined(this._abiDecoder)) {
+ throw new Error(InternalZeroExError.NoAbiDecoder);
+ }
+ const logWithDecodedArgs = this._abiDecoder.tryToDecodeLogOrNoop(log);
+ return logWithDecodedArgs;
+ }
+ protected async _instantiateContractIfExistsAsync(
+ artifact: Artifact,
+ addressIfExists?: string,
+ ): Promise<Web3.ContractInstance> {
+ let contractAddress: string;
+ if (_.isUndefined(addressIfExists)) {
+ if (_.isUndefined(artifact.networks[this._networkId])) {
+ throw new Error(ZeroExError.ContractNotDeployedOnNetwork);
+ }
+ contractAddress = artifact.networks[this._networkId].address.toLowerCase();
+ } else {
+ contractAddress = addressIfExists;
+ }
+ const doesContractExist = await this._web3Wrapper.doesContractExistAtAddressAsync(contractAddress);
+ if (!doesContractExist) {
+ throw new Error(CONTRACT_NAME_TO_NOT_FOUND_ERROR[artifact.contract_name]);
+ }
+ const contractInstance = this._web3Wrapper.getContractInstance(artifact.abi, contractAddress);
+ return contractInstance;
+ }
+ protected _getContractAddress(artifact: Artifact, addressIfExists?: string): string {
+ if (_.isUndefined(addressIfExists)) {
+ const contractAddress = artifact.networks[this._networkId].address;
+ if (_.isUndefined(contractAddress)) {
+ throw new Error(ZeroExError.ExchangeContractDoesNotExist);
+ }
+ return contractAddress;
+ } else {
+ return addressIfExists;
+ }
+ }
+ private _onLogStateChanged<ArgsType extends ContractEventArgs>(isRemoved: boolean, log: Web3.LogEntry): void {
+ _.forEach(this._filters, (filter: Web3.FilterObject, filterToken: string) => {
+ if (filterUtils.matchesFilter(log, filter)) {
+ const decodedLog = this._tryToDecodeLogOrNoop(log) as LogWithDecodedArgs<ArgsType>;
+ const logEvent = {
+ log: decodedLog,
+ isRemoved,
+ };
+ this._filterCallbacks[filterToken](null, logEvent);
+ }
+ });
+ }
+ private _startBlockAndLogStream(): void {
+ if (!_.isUndefined(this._blockAndLogStreamerIfExists)) {
+ throw new Error(ZeroExError.SubscriptionAlreadyPresent);
+ }
+ this._blockAndLogStreamerIfExists = new BlockAndLogStreamer(
+ this._web3Wrapper.getBlockAsync.bind(this._web3Wrapper),
+ this._web3Wrapper.getLogsAsync.bind(this._web3Wrapper),
+ );
+ const catchAllLogFilter = {};
+ this._blockAndLogStreamerIfExists.addLogFilter(catchAllLogFilter);
+ this._blockAndLogStreamInterval = intervalUtils.setAsyncExcludingInterval(
+ this._reconcileBlockAsync.bind(this),
+ constants.DEFAULT_BLOCK_POLLING_INTERVAL,
+ this._onReconcileBlockError.bind(this),
+ );
+ let isRemoved = false;
+ this._onLogAddedSubscriptionToken = this._blockAndLogStreamerIfExists.subscribeToOnLogAdded(
+ this._onLogStateChanged.bind(this, isRemoved),
+ );
+ isRemoved = true;
+ this._onLogRemovedSubscriptionToken = this._blockAndLogStreamerIfExists.subscribeToOnLogRemoved(
+ this._onLogStateChanged.bind(this, isRemoved),
+ );
+ }
+ private _onReconcileBlockError(err: Error): void {
+ const filterTokens = _.keys(this._filterCallbacks);
+ _.each(filterTokens, filterToken => {
+ this._unsubscribe(filterToken, err);
+ });
+ }
+ private _setNetworkId(networkId: number): void {
+ this._networkId = networkId;
+ }
+ private _stopBlockAndLogStream(): void {
+ if (_.isUndefined(this._blockAndLogStreamerIfExists)) {
+ throw new Error(ZeroExError.SubscriptionNotFound);
+ }
+ this._blockAndLogStreamerIfExists.unsubscribeFromOnLogAdded(this._onLogAddedSubscriptionToken as string);
+ this._blockAndLogStreamerIfExists.unsubscribeFromOnLogRemoved(this._onLogRemovedSubscriptionToken as string);
+ intervalUtils.clearAsyncExcludingInterval(this._blockAndLogStreamInterval);
+ delete this._blockAndLogStreamerIfExists;
+ }
+ private async _reconcileBlockAsync(): Promise<void> {
+ const latestBlock = await this._web3Wrapper.getBlockAsync(BlockParamLiteral.Latest);
+ // We need to coerce to Block type cause Web3.Block includes types for mempool blocks
+ if (!_.isUndefined(this._blockAndLogStreamerIfExists)) {
+ // If we clear the interval while fetching the block - this._blockAndLogStreamer will be undefined
+ await this._blockAndLogStreamerIfExists.reconcileNewBlock((latestBlock as any) as Block);
+ }
+ }
}
diff --git a/packages/0x.js/src/contract_wrappers/ether_token_wrapper.ts b/packages/0x.js/src/contract_wrappers/ether_token_wrapper.ts
index d412971be..b03571636 100644
--- a/packages/0x.js/src/contract_wrappers/ether_token_wrapper.ts
+++ b/packages/0x.js/src/contract_wrappers/ether_token_wrapper.ts
@@ -5,14 +5,14 @@ import * as _ from 'lodash';
import { artifacts } from '../artifacts';
import {
- BlockRange,
- EtherTokenContractEventArgs,
- EtherTokenEvents,
- EventCallback,
- IndexedFilterValues,
- LogWithDecodedArgs,
- TransactionOpts,
- ZeroExError,
+ BlockRange,
+ EtherTokenContractEventArgs,
+ EtherTokenEvents,
+ EventCallback,
+ IndexedFilterValues,
+ LogWithDecodedArgs,
+ TransactionOpts,
+ ZeroExError,
} from '../types';
import { AbiDecoder } from '../utils/abi_decoder';
import { assert } from '../utils/assert';
@@ -26,159 +26,159 @@ import { TokenWrapper } from './token_wrapper';
* The caller can convert ETH into the equivalent number of wrapped ETH ERC20 tokens and back.
*/
export class EtherTokenWrapper extends ContractWrapper {
- private _etherTokenContractsByAddress: {
- [address: string]: EtherTokenContract;
- } = {};
- private _tokenWrapper: TokenWrapper;
- constructor(web3Wrapper: Web3Wrapper, networkId: number, abiDecoder: AbiDecoder, tokenWrapper: TokenWrapper) {
- super(web3Wrapper, networkId, abiDecoder);
- this._tokenWrapper = tokenWrapper;
- }
- /**
- * Deposit ETH into the Wrapped ETH smart contract and issues the equivalent number of wrapped ETH tokens
- * to the depositor address. These wrapped ETH tokens can be used in 0x trades and are redeemable for 1-to-1
- * for ETH.
- * @param etherTokenAddress EtherToken address you wish to deposit into.
- * @param amountInWei Amount of ETH in Wei the caller wishes to deposit.
- * @param depositor The hex encoded user Ethereum address that would like to make the deposit.
- * @param txOpts Transaction parameters.
- * @return Transaction hash.
- */
- public async depositAsync(
- etherTokenAddress: string,
- amountInWei: BigNumber,
- depositor: string,
- txOpts: TransactionOpts = {},
- ): Promise<string> {
- assert.isValidBaseUnitAmount('amountInWei', amountInWei);
- await assert.isSenderAddressAsync('depositor', depositor, this._web3Wrapper);
+ private _etherTokenContractsByAddress: {
+ [address: string]: EtherTokenContract;
+ } = {};
+ private _tokenWrapper: TokenWrapper;
+ constructor(web3Wrapper: Web3Wrapper, networkId: number, abiDecoder: AbiDecoder, tokenWrapper: TokenWrapper) {
+ super(web3Wrapper, networkId, abiDecoder);
+ this._tokenWrapper = tokenWrapper;
+ }
+ /**
+ * Deposit ETH into the Wrapped ETH smart contract and issues the equivalent number of wrapped ETH tokens
+ * to the depositor address. These wrapped ETH tokens can be used in 0x trades and are redeemable for 1-to-1
+ * for ETH.
+ * @param etherTokenAddress EtherToken address you wish to deposit into.
+ * @param amountInWei Amount of ETH in Wei the caller wishes to deposit.
+ * @param depositor The hex encoded user Ethereum address that would like to make the deposit.
+ * @param txOpts Transaction parameters.
+ * @return Transaction hash.
+ */
+ public async depositAsync(
+ etherTokenAddress: string,
+ amountInWei: BigNumber,
+ depositor: string,
+ txOpts: TransactionOpts = {},
+ ): Promise<string> {
+ assert.isValidBaseUnitAmount('amountInWei', amountInWei);
+ await assert.isSenderAddressAsync('depositor', depositor, this._web3Wrapper);
- const ethBalanceInWei = await this._web3Wrapper.getBalanceInWeiAsync(depositor);
- assert.assert(ethBalanceInWei.gte(amountInWei), ZeroExError.InsufficientEthBalanceForDeposit);
+ const ethBalanceInWei = await this._web3Wrapper.getBalanceInWeiAsync(depositor);
+ assert.assert(ethBalanceInWei.gte(amountInWei), ZeroExError.InsufficientEthBalanceForDeposit);
- const wethContract = await this._getEtherTokenContractAsync(etherTokenAddress);
- const txHash = await wethContract.deposit.sendTransactionAsync({
- from: depositor,
- value: amountInWei,
- gas: txOpts.gasLimit,
- gasPrice: txOpts.gasPrice,
- });
- return txHash;
- }
- /**
- * Withdraw ETH to the withdrawer's address from the wrapped ETH smart contract in exchange for the
- * equivalent number of wrapped ETH tokens.
- * @param etherTokenAddress EtherToken address you wish to withdraw from.
- * @param amountInWei Amount of ETH in Wei the caller wishes to withdraw.
- * @param withdrawer The hex encoded user Ethereum address that would like to make the withdrawl.
- * @param txOpts Transaction parameters.
- * @return Transaction hash.
- */
- public async withdrawAsync(
- etherTokenAddress: string,
- amountInWei: BigNumber,
- withdrawer: string,
- txOpts: TransactionOpts = {},
- ): Promise<string> {
- assert.isValidBaseUnitAmount('amountInWei', amountInWei);
- await assert.isSenderAddressAsync('withdrawer', withdrawer, this._web3Wrapper);
+ const wethContract = await this._getEtherTokenContractAsync(etherTokenAddress);
+ const txHash = await wethContract.deposit.sendTransactionAsync({
+ from: depositor,
+ value: amountInWei,
+ gas: txOpts.gasLimit,
+ gasPrice: txOpts.gasPrice,
+ });
+ return txHash;
+ }
+ /**
+ * Withdraw ETH to the withdrawer's address from the wrapped ETH smart contract in exchange for the
+ * equivalent number of wrapped ETH tokens.
+ * @param etherTokenAddress EtherToken address you wish to withdraw from.
+ * @param amountInWei Amount of ETH in Wei the caller wishes to withdraw.
+ * @param withdrawer The hex encoded user Ethereum address that would like to make the withdrawl.
+ * @param txOpts Transaction parameters.
+ * @return Transaction hash.
+ */
+ public async withdrawAsync(
+ etherTokenAddress: string,
+ amountInWei: BigNumber,
+ withdrawer: string,
+ txOpts: TransactionOpts = {},
+ ): Promise<string> {
+ assert.isValidBaseUnitAmount('amountInWei', amountInWei);
+ await assert.isSenderAddressAsync('withdrawer', withdrawer, this._web3Wrapper);
- const WETHBalanceInBaseUnits = await this._tokenWrapper.getBalanceAsync(etherTokenAddress, withdrawer);
- assert.assert(WETHBalanceInBaseUnits.gte(amountInWei), ZeroExError.InsufficientWEthBalanceForWithdrawal);
+ const WETHBalanceInBaseUnits = await this._tokenWrapper.getBalanceAsync(etherTokenAddress, withdrawer);
+ assert.assert(WETHBalanceInBaseUnits.gte(amountInWei), ZeroExError.InsufficientWEthBalanceForWithdrawal);
- const wethContract = await this._getEtherTokenContractAsync(etherTokenAddress);
- const txHash = await wethContract.withdraw.sendTransactionAsync(amountInWei, {
- from: withdrawer,
- gas: txOpts.gasLimit,
- gasPrice: txOpts.gasPrice,
- });
- return txHash;
- }
- /**
- * Gets historical logs without creating a subscription
- * @param etherTokenAddress An address of the ether token that emmited the logs.
- * @param eventName The ether token contract event you would like to subscribe to.
- * @param blockRange Block range to get logs from.
- * @param indexFilterValues An object where the keys are indexed args returned by the event and
- * the value is the value you are interested in. E.g `{_owner: aUserAddressHex}`
- * @return Array of logs that match the parameters
- */
- public async getLogsAsync<ArgsType extends EtherTokenContractEventArgs>(
- etherTokenAddress: string,
- eventName: EtherTokenEvents,
- blockRange: BlockRange,
- indexFilterValues: IndexedFilterValues,
- ): Promise<Array<LogWithDecodedArgs<ArgsType>>> {
- assert.isETHAddressHex('etherTokenAddress', etherTokenAddress);
- assert.doesBelongToStringEnum('eventName', eventName, EtherTokenEvents);
- assert.doesConformToSchema('blockRange', blockRange, schemas.blockRangeSchema);
- assert.doesConformToSchema('indexFilterValues', indexFilterValues, schemas.indexFilterValuesSchema);
- const logs = await this._getLogsAsync<ArgsType>(
- etherTokenAddress,
- eventName,
- blockRange,
- indexFilterValues,
- artifacts.EtherTokenArtifact.abi,
- );
- return logs;
- }
- /**
- * Subscribe to an event type emitted by the Token contract.
- * @param etherTokenAddress The hex encoded address where the ether token is deployed.
- * @param eventName The ether token contract event you would like to subscribe to.
- * @param indexFilterValues An object where the keys are indexed args returned by the event and
- * the value is the value you are interested in. E.g `{_owner: aUserAddressHex}`
- * @param callback Callback that gets called when a log is added/removed
- * @return Subscription token used later to unsubscribe
- */
- public subscribe<ArgsType extends EtherTokenContractEventArgs>(
- etherTokenAddress: string,
- eventName: EtherTokenEvents,
- indexFilterValues: IndexedFilterValues,
- callback: EventCallback<ArgsType>,
- ): string {
- assert.isETHAddressHex('etherTokenAddress', etherTokenAddress);
- assert.doesBelongToStringEnum('eventName', eventName, EtherTokenEvents);
- assert.doesConformToSchema('indexFilterValues', indexFilterValues, schemas.indexFilterValuesSchema);
- assert.isFunction('callback', callback);
- const subscriptionToken = this._subscribe<ArgsType>(
- etherTokenAddress,
- eventName,
- indexFilterValues,
- artifacts.EtherTokenArtifact.abi,
- callback,
- );
- return subscriptionToken;
- }
- /**
- * Cancel a subscription
- * @param subscriptionToken Subscription token returned by `subscribe()`
- */
- public unsubscribe(subscriptionToken: string): void {
- this._unsubscribe(subscriptionToken);
- }
- /**
- * Cancels all existing subscriptions
- */
- public unsubscribeAll(): void {
- super.unsubscribeAll();
- }
- private _invalidateContractInstance(): void {
- this.unsubscribeAll();
- this._etherTokenContractsByAddress = {};
- }
- private async _getEtherTokenContractAsync(etherTokenAddress: string): Promise<EtherTokenContract> {
- let etherTokenContract = this._etherTokenContractsByAddress[etherTokenAddress];
- if (!_.isUndefined(etherTokenContract)) {
- return etherTokenContract;
- }
- const web3ContractInstance = await this._instantiateContractIfExistsAsync(
- artifacts.EtherTokenArtifact,
- etherTokenAddress,
- );
- const contractInstance = new EtherTokenContract(web3ContractInstance, this._web3Wrapper.getContractDefaults());
- etherTokenContract = contractInstance;
- this._etherTokenContractsByAddress[etherTokenAddress] = etherTokenContract;
- return etherTokenContract;
- }
+ const wethContract = await this._getEtherTokenContractAsync(etherTokenAddress);
+ const txHash = await wethContract.withdraw.sendTransactionAsync(amountInWei, {
+ from: withdrawer,
+ gas: txOpts.gasLimit,
+ gasPrice: txOpts.gasPrice,
+ });
+ return txHash;
+ }
+ /**
+ * Gets historical logs without creating a subscription
+ * @param etherTokenAddress An address of the ether token that emmited the logs.
+ * @param eventName The ether token contract event you would like to subscribe to.
+ * @param blockRange Block range to get logs from.
+ * @param indexFilterValues An object where the keys are indexed args returned by the event and
+ * the value is the value you are interested in. E.g `{_owner: aUserAddressHex}`
+ * @return Array of logs that match the parameters
+ */
+ public async getLogsAsync<ArgsType extends EtherTokenContractEventArgs>(
+ etherTokenAddress: string,
+ eventName: EtherTokenEvents,
+ blockRange: BlockRange,
+ indexFilterValues: IndexedFilterValues,
+ ): Promise<Array<LogWithDecodedArgs<ArgsType>>> {
+ assert.isETHAddressHex('etherTokenAddress', etherTokenAddress);
+ assert.doesBelongToStringEnum('eventName', eventName, EtherTokenEvents);
+ assert.doesConformToSchema('blockRange', blockRange, schemas.blockRangeSchema);
+ assert.doesConformToSchema('indexFilterValues', indexFilterValues, schemas.indexFilterValuesSchema);
+ const logs = await this._getLogsAsync<ArgsType>(
+ etherTokenAddress,
+ eventName,
+ blockRange,
+ indexFilterValues,
+ artifacts.EtherTokenArtifact.abi,
+ );
+ return logs;
+ }
+ /**
+ * Subscribe to an event type emitted by the Token contract.
+ * @param etherTokenAddress The hex encoded address where the ether token is deployed.
+ * @param eventName The ether token contract event you would like to subscribe to.
+ * @param indexFilterValues An object where the keys are indexed args returned by the event and
+ * the value is the value you are interested in. E.g `{_owner: aUserAddressHex}`
+ * @param callback Callback that gets called when a log is added/removed
+ * @return Subscription token used later to unsubscribe
+ */
+ public subscribe<ArgsType extends EtherTokenContractEventArgs>(
+ etherTokenAddress: string,
+ eventName: EtherTokenEvents,
+ indexFilterValues: IndexedFilterValues,
+ callback: EventCallback<ArgsType>,
+ ): string {
+ assert.isETHAddressHex('etherTokenAddress', etherTokenAddress);
+ assert.doesBelongToStringEnum('eventName', eventName, EtherTokenEvents);
+ assert.doesConformToSchema('indexFilterValues', indexFilterValues, schemas.indexFilterValuesSchema);
+ assert.isFunction('callback', callback);
+ const subscriptionToken = this._subscribe<ArgsType>(
+ etherTokenAddress,
+ eventName,
+ indexFilterValues,
+ artifacts.EtherTokenArtifact.abi,
+ callback,
+ );
+ return subscriptionToken;
+ }
+ /**
+ * Cancel a subscription
+ * @param subscriptionToken Subscription token returned by `subscribe()`
+ */
+ public unsubscribe(subscriptionToken: string): void {
+ this._unsubscribe(subscriptionToken);
+ }
+ /**
+ * Cancels all existing subscriptions
+ */
+ public unsubscribeAll(): void {
+ super.unsubscribeAll();
+ }
+ private _invalidateContractInstance(): void {
+ this.unsubscribeAll();
+ this._etherTokenContractsByAddress = {};
+ }
+ private async _getEtherTokenContractAsync(etherTokenAddress: string): Promise<EtherTokenContract> {
+ let etherTokenContract = this._etherTokenContractsByAddress[etherTokenAddress];
+ if (!_.isUndefined(etherTokenContract)) {
+ return etherTokenContract;
+ }
+ const web3ContractInstance = await this._instantiateContractIfExistsAsync(
+ artifacts.EtherTokenArtifact,
+ etherTokenAddress,
+ );
+ const contractInstance = new EtherTokenContract(web3ContractInstance, this._web3Wrapper.getContractDefaults());
+ etherTokenContract = contractInstance;
+ this._etherTokenContractsByAddress[etherTokenAddress] = etherTokenContract;
+ return etherTokenContract;
+ }
}
diff --git a/packages/0x.js/src/contract_wrappers/exchange_wrapper.ts b/packages/0x.js/src/contract_wrappers/exchange_wrapper.ts
index 668893d45..2b6117729 100644
--- a/packages/0x.js/src/contract_wrappers/exchange_wrapper.ts
+++ b/packages/0x.js/src/contract_wrappers/exchange_wrapper.ts
@@ -6,27 +6,27 @@ import * as Web3 from 'web3';
import { artifacts } from '../artifacts';
import {
- BlockParamLiteral,
- BlockRange,
- DecodedLogArgs,
- ECSignature,
- EventCallback,
- ExchangeContractErrCodes,
- ExchangeContractErrs,
- ExchangeContractEventArgs,
- ExchangeEvents,
- IndexedFilterValues,
- LogErrorContractEventArgs,
- LogWithDecodedArgs,
- MethodOpts,
- Order,
- OrderAddresses,
- OrderCancellationRequest,
- OrderFillRequest,
- OrderTransactionOpts,
- OrderValues,
- SignedOrder,
- ValidateOrderFillableOpts,
+ BlockParamLiteral,
+ BlockRange,
+ DecodedLogArgs,
+ ECSignature,
+ EventCallback,
+ ExchangeContractErrCodes,
+ ExchangeContractErrs,
+ ExchangeContractEventArgs,
+ ExchangeEvents,
+ IndexedFilterValues,
+ LogErrorContractEventArgs,
+ LogWithDecodedArgs,
+ MethodOpts,
+ Order,
+ OrderAddresses,
+ OrderCancellationRequest,
+ OrderFillRequest,
+ OrderTransactionOpts,
+ OrderValues,
+ SignedOrder,
+ ValidateOrderFillableOpts,
} from '../types';
import { AbiDecoder } from '../utils/abi_decoder';
import { assert } from '../utils/assert';
@@ -42,7 +42,7 @@ import { TokenWrapper } from './token_wrapper';
const SHOULD_VALIDATE_BY_DEFAULT = true;
interface ExchangeContractErrCodesToMsgs {
- [exchangeContractErrCodes: number]: string;
+ [exchangeContractErrCodes: number]: string;
}
/**
@@ -50,864 +50,864 @@ interface ExchangeContractErrCodesToMsgs {
* events of the 0x Exchange smart contract.
*/
export class ExchangeWrapper extends ContractWrapper {
- private _exchangeContractIfExists?: ExchangeContract;
- private _orderValidationUtils: OrderValidationUtils;
- private _tokenWrapper: TokenWrapper;
- private _exchangeContractErrCodesToMsg: ExchangeContractErrCodesToMsgs = {
- [ExchangeContractErrCodes.ERROR_FILL_EXPIRED]: ExchangeContractErrs.OrderFillExpired,
- [ExchangeContractErrCodes.ERROR_CANCEL_EXPIRED]: ExchangeContractErrs.OrderFillExpired,
- [ExchangeContractErrCodes.ERROR_FILL_NO_VALUE]: ExchangeContractErrs.OrderRemainingFillAmountZero,
- [ExchangeContractErrCodes.ERROR_CANCEL_NO_VALUE]: ExchangeContractErrs.OrderRemainingFillAmountZero,
- [ExchangeContractErrCodes.ERROR_FILL_TRUNCATION]: ExchangeContractErrs.OrderFillRoundingError,
- [ExchangeContractErrCodes.ERROR_FILL_BALANCE_ALLOWANCE]: ExchangeContractErrs.FillBalanceAllowanceError,
- };
- private _contractAddressIfExists?: string;
- private _zrxContractAddressIfExists?: string;
- private static _getOrderAddressesAndValues(order: Order): [OrderAddresses, OrderValues] {
- const orderAddresses: OrderAddresses = [
- order.maker,
- order.taker,
- order.makerTokenAddress,
- order.takerTokenAddress,
- order.feeRecipient,
- ];
- const orderValues: OrderValues = [
- order.makerTokenAmount,
- order.takerTokenAmount,
- order.makerFee,
- order.takerFee,
- order.expirationUnixTimestampSec,
- order.salt,
- ];
- return [orderAddresses, orderValues];
- }
- constructor(
- web3Wrapper: Web3Wrapper,
- networkId: number,
- abiDecoder: AbiDecoder,
- tokenWrapper: TokenWrapper,
- contractAddressIfExists?: string,
- zrxContractAddressIfExists?: string,
- ) {
- super(web3Wrapper, networkId, abiDecoder);
- this._tokenWrapper = tokenWrapper;
- this._orderValidationUtils = new OrderValidationUtils(this);
- this._contractAddressIfExists = contractAddressIfExists;
- this._zrxContractAddressIfExists = zrxContractAddressIfExists;
- }
- /**
- * Returns the unavailable takerAmount of an order. Unavailable amount is defined as the total
- * amount that has been filled or cancelled. The remaining takerAmount can be calculated by
- * subtracting the unavailable amount from the total order takerAmount.
- * @param orderHash The hex encoded orderHash for which you would like to retrieve the
- * unavailable takerAmount.
- * @param methodOpts Optional arguments this method accepts.
- * @return The amount of the order (in taker tokens) that has either been filled or cancelled.
- */
- public async getUnavailableTakerAmountAsync(orderHash: string, methodOpts?: MethodOpts): Promise<BigNumber> {
- assert.doesConformToSchema('orderHash', orderHash, schemas.orderHashSchema);
+ private _exchangeContractIfExists?: ExchangeContract;
+ private _orderValidationUtils: OrderValidationUtils;
+ private _tokenWrapper: TokenWrapper;
+ private _exchangeContractErrCodesToMsg: ExchangeContractErrCodesToMsgs = {
+ [ExchangeContractErrCodes.ERROR_FILL_EXPIRED]: ExchangeContractErrs.OrderFillExpired,
+ [ExchangeContractErrCodes.ERROR_CANCEL_EXPIRED]: ExchangeContractErrs.OrderFillExpired,
+ [ExchangeContractErrCodes.ERROR_FILL_NO_VALUE]: ExchangeContractErrs.OrderRemainingFillAmountZero,
+ [ExchangeContractErrCodes.ERROR_CANCEL_NO_VALUE]: ExchangeContractErrs.OrderRemainingFillAmountZero,
+ [ExchangeContractErrCodes.ERROR_FILL_TRUNCATION]: ExchangeContractErrs.OrderFillRoundingError,
+ [ExchangeContractErrCodes.ERROR_FILL_BALANCE_ALLOWANCE]: ExchangeContractErrs.FillBalanceAllowanceError,
+ };
+ private _contractAddressIfExists?: string;
+ private _zrxContractAddressIfExists?: string;
+ private static _getOrderAddressesAndValues(order: Order): [OrderAddresses, OrderValues] {
+ const orderAddresses: OrderAddresses = [
+ order.maker,
+ order.taker,
+ order.makerTokenAddress,
+ order.takerTokenAddress,
+ order.feeRecipient,
+ ];
+ const orderValues: OrderValues = [
+ order.makerTokenAmount,
+ order.takerTokenAmount,
+ order.makerFee,
+ order.takerFee,
+ order.expirationUnixTimestampSec,
+ order.salt,
+ ];
+ return [orderAddresses, orderValues];
+ }
+ constructor(
+ web3Wrapper: Web3Wrapper,
+ networkId: number,
+ abiDecoder: AbiDecoder,
+ tokenWrapper: TokenWrapper,
+ contractAddressIfExists?: string,
+ zrxContractAddressIfExists?: string,
+ ) {
+ super(web3Wrapper, networkId, abiDecoder);
+ this._tokenWrapper = tokenWrapper;
+ this._orderValidationUtils = new OrderValidationUtils(this);
+ this._contractAddressIfExists = contractAddressIfExists;
+ this._zrxContractAddressIfExists = zrxContractAddressIfExists;
+ }
+ /**
+ * Returns the unavailable takerAmount of an order. Unavailable amount is defined as the total
+ * amount that has been filled or cancelled. The remaining takerAmount can be calculated by
+ * subtracting the unavailable amount from the total order takerAmount.
+ * @param orderHash The hex encoded orderHash for which you would like to retrieve the
+ * unavailable takerAmount.
+ * @param methodOpts Optional arguments this method accepts.
+ * @return The amount of the order (in taker tokens) that has either been filled or cancelled.
+ */
+ public async getUnavailableTakerAmountAsync(orderHash: string, methodOpts?: MethodOpts): Promise<BigNumber> {
+ assert.doesConformToSchema('orderHash', orderHash, schemas.orderHashSchema);
- const exchangeContract = await this._getExchangeContractAsync();
- const defaultBlock = _.isUndefined(methodOpts) ? undefined : methodOpts.defaultBlock;
- let unavailableTakerTokenAmount = await exchangeContract.getUnavailableTakerTokenAmount.callAsync(
- orderHash,
- defaultBlock,
- );
- // Wrap BigNumbers returned from web3 with our own (later) version of BigNumber
- unavailableTakerTokenAmount = new BigNumber(unavailableTakerTokenAmount);
- return unavailableTakerTokenAmount;
- }
- /**
- * Retrieve the takerAmount of an order that has already been filled.
- * @param orderHash The hex encoded orderHash for which you would like to retrieve the filled takerAmount.
- * @param methodOpts Optional arguments this method accepts.
- * @return The amount of the order (in taker tokens) that has already been filled.
- */
- public async getFilledTakerAmountAsync(orderHash: string, methodOpts?: MethodOpts): Promise<BigNumber> {
- assert.doesConformToSchema('orderHash', orderHash, schemas.orderHashSchema);
+ const exchangeContract = await this._getExchangeContractAsync();
+ const defaultBlock = _.isUndefined(methodOpts) ? undefined : methodOpts.defaultBlock;
+ let unavailableTakerTokenAmount = await exchangeContract.getUnavailableTakerTokenAmount.callAsync(
+ orderHash,
+ defaultBlock,
+ );
+ // Wrap BigNumbers returned from web3 with our own (later) version of BigNumber
+ unavailableTakerTokenAmount = new BigNumber(unavailableTakerTokenAmount);
+ return unavailableTakerTokenAmount;
+ }
+ /**
+ * Retrieve the takerAmount of an order that has already been filled.
+ * @param orderHash The hex encoded orderHash for which you would like to retrieve the filled takerAmount.
+ * @param methodOpts Optional arguments this method accepts.
+ * @return The amount of the order (in taker tokens) that has already been filled.
+ */
+ public async getFilledTakerAmountAsync(orderHash: string, methodOpts?: MethodOpts): Promise<BigNumber> {
+ assert.doesConformToSchema('orderHash', orderHash, schemas.orderHashSchema);
- const exchangeContract = await this._getExchangeContractAsync();
- const defaultBlock = _.isUndefined(methodOpts) ? undefined : methodOpts.defaultBlock;
- let fillAmountInBaseUnits = await exchangeContract.filled.callAsync(orderHash, defaultBlock);
- // Wrap BigNumbers returned from web3 with our own (later) version of BigNumber
- fillAmountInBaseUnits = new BigNumber(fillAmountInBaseUnits);
- return fillAmountInBaseUnits;
- }
- /**
- * Retrieve the takerAmount of an order that has been cancelled.
- * @param orderHash The hex encoded orderHash for which you would like to retrieve the
- * cancelled takerAmount.
- * @param methodOpts Optional arguments this method accepts.
- * @return The amount of the order (in taker tokens) that has been cancelled.
- */
- public async getCancelledTakerAmountAsync(orderHash: string, methodOpts?: MethodOpts): Promise<BigNumber> {
- assert.doesConformToSchema('orderHash', orderHash, schemas.orderHashSchema);
+ const exchangeContract = await this._getExchangeContractAsync();
+ const defaultBlock = _.isUndefined(methodOpts) ? undefined : methodOpts.defaultBlock;
+ let fillAmountInBaseUnits = await exchangeContract.filled.callAsync(orderHash, defaultBlock);
+ // Wrap BigNumbers returned from web3 with our own (later) version of BigNumber
+ fillAmountInBaseUnits = new BigNumber(fillAmountInBaseUnits);
+ return fillAmountInBaseUnits;
+ }
+ /**
+ * Retrieve the takerAmount of an order that has been cancelled.
+ * @param orderHash The hex encoded orderHash for which you would like to retrieve the
+ * cancelled takerAmount.
+ * @param methodOpts Optional arguments this method accepts.
+ * @return The amount of the order (in taker tokens) that has been cancelled.
+ */
+ public async getCancelledTakerAmountAsync(orderHash: string, methodOpts?: MethodOpts): Promise<BigNumber> {
+ assert.doesConformToSchema('orderHash', orderHash, schemas.orderHashSchema);
- const exchangeContract = await this._getExchangeContractAsync();
- const defaultBlock = _.isUndefined(methodOpts) ? undefined : methodOpts.defaultBlock;
- let cancelledAmountInBaseUnits = await exchangeContract.cancelled.callAsync(orderHash, defaultBlock);
- // Wrap BigNumbers returned from web3 with our own (later) version of BigNumber
- cancelledAmountInBaseUnits = new BigNumber(cancelledAmountInBaseUnits);
- return cancelledAmountInBaseUnits;
- }
- /**
- * Fills a signed order with an amount denominated in baseUnits of the taker token.
- * Since the order in which transactions are included in the next block is indeterminate, race-conditions
- * could arise where a users balance or allowance changes before the fillOrder executes. Because of this,
- * we allow you to specify `shouldThrowOnInsufficientBalanceOrAllowance`.
- * If false, the smart contract will not throw if the parties
- * do not have sufficient balances/allowances, preserving gas costs. Setting it to true forgoes this check
- * and causes the smart contract to throw (using all the gas supplied) instead.
- * @param signedOrder An object that conforms to the SignedOrder interface.
- * @param fillTakerTokenAmount The amount of the order (in taker tokens baseUnits) that
- * you wish to fill.
- * @param shouldThrowOnInsufficientBalanceOrAllowance Whether or not you wish for the contract call to throw
- * if upon execution the tokens cannot be transferred.
- * @param takerAddress The user Ethereum address who would like to fill this order.
- * Must be available via the supplied Web3.Provider
- * passed to 0x.js.
- * @param orderTransactionOpts Optional arguments this method accepts.
- * @return Transaction hash.
- */
- @decorators.asyncZeroExErrorHandler
- public async fillOrderAsync(
- signedOrder: SignedOrder,
- fillTakerTokenAmount: BigNumber,
- shouldThrowOnInsufficientBalanceOrAllowance: boolean,
- takerAddress: string,
- orderTransactionOpts: OrderTransactionOpts = {},
- ): Promise<string> {
- assert.doesConformToSchema('signedOrder', signedOrder, schemas.signedOrderSchema);
- assert.isValidBaseUnitAmount('fillTakerTokenAmount', fillTakerTokenAmount);
- assert.isBoolean('shouldThrowOnInsufficientBalanceOrAllowance', shouldThrowOnInsufficientBalanceOrAllowance);
- await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper);
+ const exchangeContract = await this._getExchangeContractAsync();
+ const defaultBlock = _.isUndefined(methodOpts) ? undefined : methodOpts.defaultBlock;
+ let cancelledAmountInBaseUnits = await exchangeContract.cancelled.callAsync(orderHash, defaultBlock);
+ // Wrap BigNumbers returned from web3 with our own (later) version of BigNumber
+ cancelledAmountInBaseUnits = new BigNumber(cancelledAmountInBaseUnits);
+ return cancelledAmountInBaseUnits;
+ }
+ /**
+ * Fills a signed order with an amount denominated in baseUnits of the taker token.
+ * Since the order in which transactions are included in the next block is indeterminate, race-conditions
+ * could arise where a users balance or allowance changes before the fillOrder executes. Because of this,
+ * we allow you to specify `shouldThrowOnInsufficientBalanceOrAllowance`.
+ * If false, the smart contract will not throw if the parties
+ * do not have sufficient balances/allowances, preserving gas costs. Setting it to true forgoes this check
+ * and causes the smart contract to throw (using all the gas supplied) instead.
+ * @param signedOrder An object that conforms to the SignedOrder interface.
+ * @param fillTakerTokenAmount The amount of the order (in taker tokens baseUnits) that
+ * you wish to fill.
+ * @param shouldThrowOnInsufficientBalanceOrAllowance Whether or not you wish for the contract call to throw
+ * if upon execution the tokens cannot be transferred.
+ * @param takerAddress The user Ethereum address who would like to fill this order.
+ * Must be available via the supplied Web3.Provider
+ * passed to 0x.js.
+ * @param orderTransactionOpts Optional arguments this method accepts.
+ * @return Transaction hash.
+ */
+ @decorators.asyncZeroExErrorHandler
+ public async fillOrderAsync(
+ signedOrder: SignedOrder,
+ fillTakerTokenAmount: BigNumber,
+ shouldThrowOnInsufficientBalanceOrAllowance: boolean,
+ takerAddress: string,
+ orderTransactionOpts: OrderTransactionOpts = {},
+ ): Promise<string> {
+ assert.doesConformToSchema('signedOrder', signedOrder, schemas.signedOrderSchema);
+ assert.isValidBaseUnitAmount('fillTakerTokenAmount', fillTakerTokenAmount);
+ assert.isBoolean('shouldThrowOnInsufficientBalanceOrAllowance', shouldThrowOnInsufficientBalanceOrAllowance);
+ await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper);
- const exchangeInstance = await this._getExchangeContractAsync();
- const shouldValidate = _.isUndefined(orderTransactionOpts.shouldValidate)
- ? SHOULD_VALIDATE_BY_DEFAULT
- : orderTransactionOpts.shouldValidate;
- if (shouldValidate) {
- const zrxTokenAddress = this.getZRXTokenAddress();
- const exchangeTradeEmulator = new ExchangeTransferSimulator(this._tokenWrapper, BlockParamLiteral.Latest);
- await this._orderValidationUtils.validateFillOrderThrowIfInvalidAsync(
- exchangeTradeEmulator,
- signedOrder,
- fillTakerTokenAmount,
- takerAddress,
- zrxTokenAddress,
- );
- }
+ const exchangeInstance = await this._getExchangeContractAsync();
+ const shouldValidate = _.isUndefined(orderTransactionOpts.shouldValidate)
+ ? SHOULD_VALIDATE_BY_DEFAULT
+ : orderTransactionOpts.shouldValidate;
+ if (shouldValidate) {
+ const zrxTokenAddress = this.getZRXTokenAddress();
+ const exchangeTradeEmulator = new ExchangeTransferSimulator(this._tokenWrapper, BlockParamLiteral.Latest);
+ await this._orderValidationUtils.validateFillOrderThrowIfInvalidAsync(
+ exchangeTradeEmulator,
+ signedOrder,
+ fillTakerTokenAmount,
+ takerAddress,
+ zrxTokenAddress,
+ );
+ }
- const [orderAddresses, orderValues] = ExchangeWrapper._getOrderAddressesAndValues(signedOrder);
+ const [orderAddresses, orderValues] = ExchangeWrapper._getOrderAddressesAndValues(signedOrder);
- const txHash: string = await exchangeInstance.fillOrder.sendTransactionAsync(
- orderAddresses,
- orderValues,
- fillTakerTokenAmount,
- shouldThrowOnInsufficientBalanceOrAllowance,
- signedOrder.ecSignature.v,
- signedOrder.ecSignature.r,
- signedOrder.ecSignature.s,
- {
- from: takerAddress,
- gas: orderTransactionOpts.gasLimit,
- gasPrice: orderTransactionOpts.gasPrice,
- },
- );
- return txHash;
- }
- /**
- * Sequentially and atomically fills signedOrders up to the specified takerTokenFillAmount.
- * If the fill amount is reached - it succeeds and does not fill the rest of the orders.
- * If fill amount is not reached - it fills as much of the fill amount as possible and succeeds.
- * @param signedOrders The array of signedOrders that you would like to fill until
- * takerTokenFillAmount is reached.
- * @param fillTakerTokenAmount The total amount of the takerTokens you would like to fill.
- * @param shouldThrowOnInsufficientBalanceOrAllowance Whether or not you wish for the contract call to throw if
- * upon execution any of the tokens cannot be transferred.
- * If set to false, the call will continue to fill subsequent
- * signedOrders even when some cannot be filled.
- * @param takerAddress The user Ethereum address who would like to fill these
- * orders. Must be available via the supplied Web3.Provider
- * passed to 0x.js.
- * @param orderTransactionOpts Optional arguments this method accepts.
- * @return Transaction hash.
- */
- @decorators.asyncZeroExErrorHandler
- public async fillOrdersUpToAsync(
- signedOrders: SignedOrder[],
- fillTakerTokenAmount: BigNumber,
- shouldThrowOnInsufficientBalanceOrAllowance: boolean,
- takerAddress: string,
- orderTransactionOpts: OrderTransactionOpts = {},
- ): Promise<string> {
- assert.doesConformToSchema('signedOrders', signedOrders, schemas.signedOrdersSchema);
- const takerTokenAddresses = _.map(signedOrders, signedOrder => signedOrder.takerTokenAddress);
- assert.hasAtMostOneUniqueValue(
- takerTokenAddresses,
- ExchangeContractErrs.MultipleTakerTokensInFillUpToDisallowed,
- );
- const exchangeContractAddresses = _.map(signedOrders, signedOrder => signedOrder.exchangeContractAddress);
- assert.hasAtMostOneUniqueValue(
- exchangeContractAddresses,
- ExchangeContractErrs.BatchOrdersMustHaveSameExchangeAddress,
- );
- assert.isValidBaseUnitAmount('fillTakerTokenAmount', fillTakerTokenAmount);
- assert.isBoolean('shouldThrowOnInsufficientBalanceOrAllowance', shouldThrowOnInsufficientBalanceOrAllowance);
- await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper);
+ const txHash: string = await exchangeInstance.fillOrder.sendTransactionAsync(
+ orderAddresses,
+ orderValues,
+ fillTakerTokenAmount,
+ shouldThrowOnInsufficientBalanceOrAllowance,
+ signedOrder.ecSignature.v,
+ signedOrder.ecSignature.r,
+ signedOrder.ecSignature.s,
+ {
+ from: takerAddress,
+ gas: orderTransactionOpts.gasLimit,
+ gasPrice: orderTransactionOpts.gasPrice,
+ },
+ );
+ return txHash;
+ }
+ /**
+ * Sequentially and atomically fills signedOrders up to the specified takerTokenFillAmount.
+ * If the fill amount is reached - it succeeds and does not fill the rest of the orders.
+ * If fill amount is not reached - it fills as much of the fill amount as possible and succeeds.
+ * @param signedOrders The array of signedOrders that you would like to fill until
+ * takerTokenFillAmount is reached.
+ * @param fillTakerTokenAmount The total amount of the takerTokens you would like to fill.
+ * @param shouldThrowOnInsufficientBalanceOrAllowance Whether or not you wish for the contract call to throw if
+ * upon execution any of the tokens cannot be transferred.
+ * If set to false, the call will continue to fill subsequent
+ * signedOrders even when some cannot be filled.
+ * @param takerAddress The user Ethereum address who would like to fill these
+ * orders. Must be available via the supplied Web3.Provider
+ * passed to 0x.js.
+ * @param orderTransactionOpts Optional arguments this method accepts.
+ * @return Transaction hash.
+ */
+ @decorators.asyncZeroExErrorHandler
+ public async fillOrdersUpToAsync(
+ signedOrders: SignedOrder[],
+ fillTakerTokenAmount: BigNumber,
+ shouldThrowOnInsufficientBalanceOrAllowance: boolean,
+ takerAddress: string,
+ orderTransactionOpts: OrderTransactionOpts = {},
+ ): Promise<string> {
+ assert.doesConformToSchema('signedOrders', signedOrders, schemas.signedOrdersSchema);
+ const takerTokenAddresses = _.map(signedOrders, signedOrder => signedOrder.takerTokenAddress);
+ assert.hasAtMostOneUniqueValue(
+ takerTokenAddresses,
+ ExchangeContractErrs.MultipleTakerTokensInFillUpToDisallowed,
+ );
+ const exchangeContractAddresses = _.map(signedOrders, signedOrder => signedOrder.exchangeContractAddress);
+ assert.hasAtMostOneUniqueValue(
+ exchangeContractAddresses,
+ ExchangeContractErrs.BatchOrdersMustHaveSameExchangeAddress,
+ );
+ assert.isValidBaseUnitAmount('fillTakerTokenAmount', fillTakerTokenAmount);
+ assert.isBoolean('shouldThrowOnInsufficientBalanceOrAllowance', shouldThrowOnInsufficientBalanceOrAllowance);
+ await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper);
- const shouldValidate = _.isUndefined(orderTransactionOpts.shouldValidate)
- ? SHOULD_VALIDATE_BY_DEFAULT
- : orderTransactionOpts.shouldValidate;
- if (shouldValidate) {
- let filledTakerTokenAmount = new BigNumber(0);
- const zrxTokenAddress = this.getZRXTokenAddress();
- const exchangeTradeEmulator = new ExchangeTransferSimulator(this._tokenWrapper, BlockParamLiteral.Latest);
- for (const signedOrder of signedOrders) {
- const singleFilledTakerTokenAmount = await this._orderValidationUtils.validateFillOrderThrowIfInvalidAsync(
- exchangeTradeEmulator,
- signedOrder,
- fillTakerTokenAmount.minus(filledTakerTokenAmount),
- takerAddress,
- zrxTokenAddress,
- );
- filledTakerTokenAmount = filledTakerTokenAmount.plus(singleFilledTakerTokenAmount);
- }
- }
+ const shouldValidate = _.isUndefined(orderTransactionOpts.shouldValidate)
+ ? SHOULD_VALIDATE_BY_DEFAULT
+ : orderTransactionOpts.shouldValidate;
+ if (shouldValidate) {
+ let filledTakerTokenAmount = new BigNumber(0);
+ const zrxTokenAddress = this.getZRXTokenAddress();
+ const exchangeTradeEmulator = new ExchangeTransferSimulator(this._tokenWrapper, BlockParamLiteral.Latest);
+ for (const signedOrder of signedOrders) {
+ const singleFilledTakerTokenAmount = await this._orderValidationUtils.validateFillOrderThrowIfInvalidAsync(
+ exchangeTradeEmulator,
+ signedOrder,
+ fillTakerTokenAmount.minus(filledTakerTokenAmount),
+ takerAddress,
+ zrxTokenAddress,
+ );
+ filledTakerTokenAmount = filledTakerTokenAmount.plus(singleFilledTakerTokenAmount);
+ }
+ }
- if (_.isEmpty(signedOrders)) {
- throw new Error(ExchangeContractErrs.BatchOrdersMustHaveAtLeastOneItem);
- }
+ if (_.isEmpty(signedOrders)) {
+ throw new Error(ExchangeContractErrs.BatchOrdersMustHaveAtLeastOneItem);
+ }
- const orderAddressesValuesAndSignatureArray = _.map(signedOrders, signedOrder => {
- return [
- ...ExchangeWrapper._getOrderAddressesAndValues(signedOrder),
- signedOrder.ecSignature.v,
- signedOrder.ecSignature.r,
- signedOrder.ecSignature.s,
- ];
- });
- // We use _.unzip<any> because _.unzip doesn't type check if values have different types :'(
- const [orderAddressesArray, orderValuesArray, vArray, rArray, sArray] = _.unzip<any>(
- orderAddressesValuesAndSignatureArray,
- );
+ const orderAddressesValuesAndSignatureArray = _.map(signedOrders, signedOrder => {
+ return [
+ ...ExchangeWrapper._getOrderAddressesAndValues(signedOrder),
+ signedOrder.ecSignature.v,
+ signedOrder.ecSignature.r,
+ signedOrder.ecSignature.s,
+ ];
+ });
+ // We use _.unzip<any> because _.unzip doesn't type check if values have different types :'(
+ const [orderAddressesArray, orderValuesArray, vArray, rArray, sArray] = _.unzip<any>(
+ orderAddressesValuesAndSignatureArray,
+ );
- const exchangeInstance = await this._getExchangeContractAsync();
- const txHash = await exchangeInstance.fillOrdersUpTo.sendTransactionAsync(
- orderAddressesArray,
- orderValuesArray,
- fillTakerTokenAmount,
- shouldThrowOnInsufficientBalanceOrAllowance,
- vArray,
- rArray,
- sArray,
- {
- from: takerAddress,
- gas: orderTransactionOpts.gasLimit,
- gasPrice: orderTransactionOpts.gasPrice,
- },
- );
- return txHash;
- }
- /**
- * Batch version of fillOrderAsync.
- * Executes multiple fills atomically in a single transaction.
- * If shouldThrowOnInsufficientBalanceOrAllowance is set to false, it will continue filling subsequent orders even
- * when earlier ones fail.
- * When shouldThrowOnInsufficientBalanceOrAllowance is set to true, if any fill fails, the entire batch fails.
- * @param orderFillRequests An array of objects that conform to the
- * OrderFillRequest interface.
- * @param shouldThrowOnInsufficientBalanceOrAllowance Whether or not you wish for the contract call to throw
- * if upon execution any of the tokens cannot be
- * transferred. If set to false, the call will continue to
- * fill subsequent signedOrders even when some
- * cannot be filled.
- * @param takerAddress The user Ethereum address who would like to fill
- * these orders. Must be available via the supplied
- * Web3.Provider passed to 0x.js.
- * @param orderTransactionOpts Optional arguments this method accepts.
- * @return Transaction hash.
- */
- @decorators.asyncZeroExErrorHandler
- public async batchFillOrdersAsync(
- orderFillRequests: OrderFillRequest[],
- shouldThrowOnInsufficientBalanceOrAllowance: boolean,
- takerAddress: string,
- orderTransactionOpts: OrderTransactionOpts = {},
- ): Promise<string> {
- assert.doesConformToSchema('orderFillRequests', orderFillRequests, schemas.orderFillRequestsSchema);
- const exchangeContractAddresses = _.map(
- orderFillRequests,
- orderFillRequest => orderFillRequest.signedOrder.exchangeContractAddress,
- );
- assert.hasAtMostOneUniqueValue(
- exchangeContractAddresses,
- ExchangeContractErrs.BatchOrdersMustHaveSameExchangeAddress,
- );
- assert.isBoolean('shouldThrowOnInsufficientBalanceOrAllowance', shouldThrowOnInsufficientBalanceOrAllowance);
- await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper);
- const shouldValidate = _.isUndefined(orderTransactionOpts.shouldValidate)
- ? SHOULD_VALIDATE_BY_DEFAULT
- : orderTransactionOpts.shouldValidate;
- if (shouldValidate) {
- const zrxTokenAddress = this.getZRXTokenAddress();
- const exchangeTradeEmulator = new ExchangeTransferSimulator(this._tokenWrapper, BlockParamLiteral.Latest);
- for (const orderFillRequest of orderFillRequests) {
- await this._orderValidationUtils.validateFillOrderThrowIfInvalidAsync(
- exchangeTradeEmulator,
- orderFillRequest.signedOrder,
- orderFillRequest.takerTokenFillAmount,
- takerAddress,
- zrxTokenAddress,
- );
- }
- }
- if (_.isEmpty(orderFillRequests)) {
- throw new Error(ExchangeContractErrs.BatchOrdersMustHaveAtLeastOneItem);
- }
+ const exchangeInstance = await this._getExchangeContractAsync();
+ const txHash = await exchangeInstance.fillOrdersUpTo.sendTransactionAsync(
+ orderAddressesArray,
+ orderValuesArray,
+ fillTakerTokenAmount,
+ shouldThrowOnInsufficientBalanceOrAllowance,
+ vArray,
+ rArray,
+ sArray,
+ {
+ from: takerAddress,
+ gas: orderTransactionOpts.gasLimit,
+ gasPrice: orderTransactionOpts.gasPrice,
+ },
+ );
+ return txHash;
+ }
+ /**
+ * Batch version of fillOrderAsync.
+ * Executes multiple fills atomically in a single transaction.
+ * If shouldThrowOnInsufficientBalanceOrAllowance is set to false, it will continue filling subsequent orders even
+ * when earlier ones fail.
+ * When shouldThrowOnInsufficientBalanceOrAllowance is set to true, if any fill fails, the entire batch fails.
+ * @param orderFillRequests An array of objects that conform to the
+ * OrderFillRequest interface.
+ * @param shouldThrowOnInsufficientBalanceOrAllowance Whether or not you wish for the contract call to throw
+ * if upon execution any of the tokens cannot be
+ * transferred. If set to false, the call will continue to
+ * fill subsequent signedOrders even when some
+ * cannot be filled.
+ * @param takerAddress The user Ethereum address who would like to fill
+ * these orders. Must be available via the supplied
+ * Web3.Provider passed to 0x.js.
+ * @param orderTransactionOpts Optional arguments this method accepts.
+ * @return Transaction hash.
+ */
+ @decorators.asyncZeroExErrorHandler
+ public async batchFillOrdersAsync(
+ orderFillRequests: OrderFillRequest[],
+ shouldThrowOnInsufficientBalanceOrAllowance: boolean,
+ takerAddress: string,
+ orderTransactionOpts: OrderTransactionOpts = {},
+ ): Promise<string> {
+ assert.doesConformToSchema('orderFillRequests', orderFillRequests, schemas.orderFillRequestsSchema);
+ const exchangeContractAddresses = _.map(
+ orderFillRequests,
+ orderFillRequest => orderFillRequest.signedOrder.exchangeContractAddress,
+ );
+ assert.hasAtMostOneUniqueValue(
+ exchangeContractAddresses,
+ ExchangeContractErrs.BatchOrdersMustHaveSameExchangeAddress,
+ );
+ assert.isBoolean('shouldThrowOnInsufficientBalanceOrAllowance', shouldThrowOnInsufficientBalanceOrAllowance);
+ await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper);
+ const shouldValidate = _.isUndefined(orderTransactionOpts.shouldValidate)
+ ? SHOULD_VALIDATE_BY_DEFAULT
+ : orderTransactionOpts.shouldValidate;
+ if (shouldValidate) {
+ const zrxTokenAddress = this.getZRXTokenAddress();
+ const exchangeTradeEmulator = new ExchangeTransferSimulator(this._tokenWrapper, BlockParamLiteral.Latest);
+ for (const orderFillRequest of orderFillRequests) {
+ await this._orderValidationUtils.validateFillOrderThrowIfInvalidAsync(
+ exchangeTradeEmulator,
+ orderFillRequest.signedOrder,
+ orderFillRequest.takerTokenFillAmount,
+ takerAddress,
+ zrxTokenAddress,
+ );
+ }
+ }
+ if (_.isEmpty(orderFillRequests)) {
+ throw new Error(ExchangeContractErrs.BatchOrdersMustHaveAtLeastOneItem);
+ }
- const orderAddressesValuesAmountsAndSignatureArray = _.map(orderFillRequests, orderFillRequest => {
- return [
- ...ExchangeWrapper._getOrderAddressesAndValues(orderFillRequest.signedOrder),
- orderFillRequest.takerTokenFillAmount,
- orderFillRequest.signedOrder.ecSignature.v,
- orderFillRequest.signedOrder.ecSignature.r,
- orderFillRequest.signedOrder.ecSignature.s,
- ];
- });
- // We use _.unzip<any> because _.unzip doesn't type check if values have different types :'(
- const [orderAddressesArray, orderValuesArray, fillTakerTokenAmounts, vArray, rArray, sArray] = _.unzip<any>(
- orderAddressesValuesAmountsAndSignatureArray,
- );
+ const orderAddressesValuesAmountsAndSignatureArray = _.map(orderFillRequests, orderFillRequest => {
+ return [
+ ...ExchangeWrapper._getOrderAddressesAndValues(orderFillRequest.signedOrder),
+ orderFillRequest.takerTokenFillAmount,
+ orderFillRequest.signedOrder.ecSignature.v,
+ orderFillRequest.signedOrder.ecSignature.r,
+ orderFillRequest.signedOrder.ecSignature.s,
+ ];
+ });
+ // We use _.unzip<any> because _.unzip doesn't type check if values have different types :'(
+ const [orderAddressesArray, orderValuesArray, fillTakerTokenAmounts, vArray, rArray, sArray] = _.unzip<any>(
+ orderAddressesValuesAmountsAndSignatureArray,
+ );
- const exchangeInstance = await this._getExchangeContractAsync();
- const txHash = await exchangeInstance.batchFillOrders.sendTransactionAsync(
- orderAddressesArray,
- orderValuesArray,
- fillTakerTokenAmounts,
- shouldThrowOnInsufficientBalanceOrAllowance,
- vArray,
- rArray,
- sArray,
- {
- from: takerAddress,
- gas: orderTransactionOpts.gasLimit,
- gasPrice: orderTransactionOpts.gasPrice,
- },
- );
- return txHash;
- }
- /**
- * Attempts to fill a specific amount of an order. If the entire amount specified cannot be filled,
- * the fill order is abandoned.
- * @param signedOrder An object that conforms to the SignedOrder interface. The
- * signedOrder you wish to fill.
- * @param fillTakerTokenAmount The total amount of the takerTokens you would like to fill.
- * @param takerAddress The user Ethereum address who would like to fill this order.
- * Must be available via the supplied Web3.Provider passed to 0x.js.
- * @param orderTransactionOpts Optional arguments this method accepts.
- * @return Transaction hash.
- */
- @decorators.asyncZeroExErrorHandler
- public async fillOrKillOrderAsync(
- signedOrder: SignedOrder,
- fillTakerTokenAmount: BigNumber,
- takerAddress: string,
- orderTransactionOpts: OrderTransactionOpts = {},
- ): Promise<string> {
- assert.doesConformToSchema('signedOrder', signedOrder, schemas.signedOrderSchema);
- assert.isValidBaseUnitAmount('fillTakerTokenAmount', fillTakerTokenAmount);
- await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper);
+ const exchangeInstance = await this._getExchangeContractAsync();
+ const txHash = await exchangeInstance.batchFillOrders.sendTransactionAsync(
+ orderAddressesArray,
+ orderValuesArray,
+ fillTakerTokenAmounts,
+ shouldThrowOnInsufficientBalanceOrAllowance,
+ vArray,
+ rArray,
+ sArray,
+ {
+ from: takerAddress,
+ gas: orderTransactionOpts.gasLimit,
+ gasPrice: orderTransactionOpts.gasPrice,
+ },
+ );
+ return txHash;
+ }
+ /**
+ * Attempts to fill a specific amount of an order. If the entire amount specified cannot be filled,
+ * the fill order is abandoned.
+ * @param signedOrder An object that conforms to the SignedOrder interface. The
+ * signedOrder you wish to fill.
+ * @param fillTakerTokenAmount The total amount of the takerTokens you would like to fill.
+ * @param takerAddress The user Ethereum address who would like to fill this order.
+ * Must be available via the supplied Web3.Provider passed to 0x.js.
+ * @param orderTransactionOpts Optional arguments this method accepts.
+ * @return Transaction hash.
+ */
+ @decorators.asyncZeroExErrorHandler
+ public async fillOrKillOrderAsync(
+ signedOrder: SignedOrder,
+ fillTakerTokenAmount: BigNumber,
+ takerAddress: string,
+ orderTransactionOpts: OrderTransactionOpts = {},
+ ): Promise<string> {
+ assert.doesConformToSchema('signedOrder', signedOrder, schemas.signedOrderSchema);
+ assert.isValidBaseUnitAmount('fillTakerTokenAmount', fillTakerTokenAmount);
+ await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper);
- const exchangeInstance = await this._getExchangeContractAsync();
+ const exchangeInstance = await this._getExchangeContractAsync();
- const shouldValidate = _.isUndefined(orderTransactionOpts.shouldValidate)
- ? SHOULD_VALIDATE_BY_DEFAULT
- : orderTransactionOpts.shouldValidate;
- if (shouldValidate) {
- const zrxTokenAddress = this.getZRXTokenAddress();
- const exchangeTradeEmulator = new ExchangeTransferSimulator(this._tokenWrapper, BlockParamLiteral.Latest);
- await this._orderValidationUtils.validateFillOrKillOrderThrowIfInvalidAsync(
- exchangeTradeEmulator,
- signedOrder,
- fillTakerTokenAmount,
- takerAddress,
- zrxTokenAddress,
- );
- }
+ const shouldValidate = _.isUndefined(orderTransactionOpts.shouldValidate)
+ ? SHOULD_VALIDATE_BY_DEFAULT
+ : orderTransactionOpts.shouldValidate;
+ if (shouldValidate) {
+ const zrxTokenAddress = this.getZRXTokenAddress();
+ const exchangeTradeEmulator = new ExchangeTransferSimulator(this._tokenWrapper, BlockParamLiteral.Latest);
+ await this._orderValidationUtils.validateFillOrKillOrderThrowIfInvalidAsync(
+ exchangeTradeEmulator,
+ signedOrder,
+ fillTakerTokenAmount,
+ takerAddress,
+ zrxTokenAddress,
+ );
+ }
- const [orderAddresses, orderValues] = ExchangeWrapper._getOrderAddressesAndValues(signedOrder);
- const txHash = await exchangeInstance.fillOrKillOrder.sendTransactionAsync(
- orderAddresses,
- orderValues,
- fillTakerTokenAmount,
- signedOrder.ecSignature.v,
- signedOrder.ecSignature.r,
- signedOrder.ecSignature.s,
- {
- from: takerAddress,
- gas: orderTransactionOpts.gasLimit,
- gasPrice: orderTransactionOpts.gasPrice,
- },
- );
- return txHash;
- }
- /**
- * Batch version of fillOrKill. Allows a taker to specify a batch of orders that will either be atomically
- * filled (each to the specified fillAmount) or aborted.
- * @param orderFillRequests An array of objects that conform to the OrderFillRequest interface.
- * @param takerAddress The user Ethereum address who would like to fill there orders.
- * Must be available via the supplied Web3.Provider passed to 0x.js.
- * @param orderTransactionOpts Optional arguments this method accepts.
- * @return Transaction hash.
- */
- @decorators.asyncZeroExErrorHandler
- public async batchFillOrKillAsync(
- orderFillRequests: OrderFillRequest[],
- takerAddress: string,
- orderTransactionOpts: OrderTransactionOpts = {},
- ): Promise<string> {
- assert.doesConformToSchema('orderFillRequests', orderFillRequests, schemas.orderFillRequestsSchema);
- const exchangeContractAddresses = _.map(
- orderFillRequests,
- orderFillRequest => orderFillRequest.signedOrder.exchangeContractAddress,
- );
- assert.hasAtMostOneUniqueValue(
- exchangeContractAddresses,
- ExchangeContractErrs.BatchOrdersMustHaveSameExchangeAddress,
- );
- await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper);
- if (_.isEmpty(orderFillRequests)) {
- throw new Error(ExchangeContractErrs.BatchOrdersMustHaveAtLeastOneItem);
- }
- const exchangeInstance = await this._getExchangeContractAsync();
+ const [orderAddresses, orderValues] = ExchangeWrapper._getOrderAddressesAndValues(signedOrder);
+ const txHash = await exchangeInstance.fillOrKillOrder.sendTransactionAsync(
+ orderAddresses,
+ orderValues,
+ fillTakerTokenAmount,
+ signedOrder.ecSignature.v,
+ signedOrder.ecSignature.r,
+ signedOrder.ecSignature.s,
+ {
+ from: takerAddress,
+ gas: orderTransactionOpts.gasLimit,
+ gasPrice: orderTransactionOpts.gasPrice,
+ },
+ );
+ return txHash;
+ }
+ /**
+ * Batch version of fillOrKill. Allows a taker to specify a batch of orders that will either be atomically
+ * filled (each to the specified fillAmount) or aborted.
+ * @param orderFillRequests An array of objects that conform to the OrderFillRequest interface.
+ * @param takerAddress The user Ethereum address who would like to fill there orders.
+ * Must be available via the supplied Web3.Provider passed to 0x.js.
+ * @param orderTransactionOpts Optional arguments this method accepts.
+ * @return Transaction hash.
+ */
+ @decorators.asyncZeroExErrorHandler
+ public async batchFillOrKillAsync(
+ orderFillRequests: OrderFillRequest[],
+ takerAddress: string,
+ orderTransactionOpts: OrderTransactionOpts = {},
+ ): Promise<string> {
+ assert.doesConformToSchema('orderFillRequests', orderFillRequests, schemas.orderFillRequestsSchema);
+ const exchangeContractAddresses = _.map(
+ orderFillRequests,
+ orderFillRequest => orderFillRequest.signedOrder.exchangeContractAddress,
+ );
+ assert.hasAtMostOneUniqueValue(
+ exchangeContractAddresses,
+ ExchangeContractErrs.BatchOrdersMustHaveSameExchangeAddress,
+ );
+ await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper);
+ if (_.isEmpty(orderFillRequests)) {
+ throw new Error(ExchangeContractErrs.BatchOrdersMustHaveAtLeastOneItem);
+ }
+ const exchangeInstance = await this._getExchangeContractAsync();
- const shouldValidate = _.isUndefined(orderTransactionOpts.shouldValidate)
- ? SHOULD_VALIDATE_BY_DEFAULT
- : orderTransactionOpts.shouldValidate;
- if (shouldValidate) {
- const zrxTokenAddress = this.getZRXTokenAddress();
- const exchangeTradeEmulator = new ExchangeTransferSimulator(this._tokenWrapper, BlockParamLiteral.Latest);
- for (const orderFillRequest of orderFillRequests) {
- await this._orderValidationUtils.validateFillOrKillOrderThrowIfInvalidAsync(
- exchangeTradeEmulator,
- orderFillRequest.signedOrder,
- orderFillRequest.takerTokenFillAmount,
- takerAddress,
- zrxTokenAddress,
- );
- }
- }
+ const shouldValidate = _.isUndefined(orderTransactionOpts.shouldValidate)
+ ? SHOULD_VALIDATE_BY_DEFAULT
+ : orderTransactionOpts.shouldValidate;
+ if (shouldValidate) {
+ const zrxTokenAddress = this.getZRXTokenAddress();
+ const exchangeTradeEmulator = new ExchangeTransferSimulator(this._tokenWrapper, BlockParamLiteral.Latest);
+ for (const orderFillRequest of orderFillRequests) {
+ await this._orderValidationUtils.validateFillOrKillOrderThrowIfInvalidAsync(
+ exchangeTradeEmulator,
+ orderFillRequest.signedOrder,
+ orderFillRequest.takerTokenFillAmount,
+ takerAddress,
+ zrxTokenAddress,
+ );
+ }
+ }
- const orderAddressesValuesAndTakerTokenFillAmounts = _.map(orderFillRequests, request => {
- return [
- ...ExchangeWrapper._getOrderAddressesAndValues(request.signedOrder),
- request.takerTokenFillAmount,
- request.signedOrder.ecSignature.v,
- request.signedOrder.ecSignature.r,
- request.signedOrder.ecSignature.s,
- ];
- });
+ const orderAddressesValuesAndTakerTokenFillAmounts = _.map(orderFillRequests, request => {
+ return [
+ ...ExchangeWrapper._getOrderAddressesAndValues(request.signedOrder),
+ request.takerTokenFillAmount,
+ request.signedOrder.ecSignature.v,
+ request.signedOrder.ecSignature.r,
+ request.signedOrder.ecSignature.s,
+ ];
+ });
- // We use _.unzip<any> because _.unzip doesn't type check if values have different types :'(
- const [orderAddresses, orderValues, fillTakerTokenAmounts, vParams, rParams, sParams] = _.unzip<any>(
- orderAddressesValuesAndTakerTokenFillAmounts,
- );
- const txHash = await exchangeInstance.batchFillOrKillOrders.sendTransactionAsync(
- orderAddresses,
- orderValues,
- fillTakerTokenAmounts,
- vParams,
- rParams,
- sParams,
- {
- from: takerAddress,
- gas: orderTransactionOpts.gasLimit,
- gasPrice: orderTransactionOpts.gasPrice,
- },
- );
- return txHash;
- }
- /**
- * Cancel a given fill amount of an order. Cancellations are cumulative.
- * @param order An object that conforms to the Order or SignedOrder interface.
- * The order you would like to cancel.
- * @param cancelTakerTokenAmount The amount (specified in taker tokens) that you would like to cancel.
- * @param transactionOpts Optional arguments this method accepts.
- * @return Transaction hash.
- */
- @decorators.asyncZeroExErrorHandler
- public async cancelOrderAsync(
- order: Order | SignedOrder,
- cancelTakerTokenAmount: BigNumber,
- orderTransactionOpts: OrderTransactionOpts = {},
- ): Promise<string> {
- assert.doesConformToSchema('order', order, schemas.orderSchema);
- assert.isValidBaseUnitAmount('takerTokenCancelAmount', cancelTakerTokenAmount);
- await assert.isSenderAddressAsync('order.maker', order.maker, this._web3Wrapper);
+ // We use _.unzip<any> because _.unzip doesn't type check if values have different types :'(
+ const [orderAddresses, orderValues, fillTakerTokenAmounts, vParams, rParams, sParams] = _.unzip<any>(
+ orderAddressesValuesAndTakerTokenFillAmounts,
+ );
+ const txHash = await exchangeInstance.batchFillOrKillOrders.sendTransactionAsync(
+ orderAddresses,
+ orderValues,
+ fillTakerTokenAmounts,
+ vParams,
+ rParams,
+ sParams,
+ {
+ from: takerAddress,
+ gas: orderTransactionOpts.gasLimit,
+ gasPrice: orderTransactionOpts.gasPrice,
+ },
+ );
+ return txHash;
+ }
+ /**
+ * Cancel a given fill amount of an order. Cancellations are cumulative.
+ * @param order An object that conforms to the Order or SignedOrder interface.
+ * The order you would like to cancel.
+ * @param cancelTakerTokenAmount The amount (specified in taker tokens) that you would like to cancel.
+ * @param transactionOpts Optional arguments this method accepts.
+ * @return Transaction hash.
+ */
+ @decorators.asyncZeroExErrorHandler
+ public async cancelOrderAsync(
+ order: Order | SignedOrder,
+ cancelTakerTokenAmount: BigNumber,
+ orderTransactionOpts: OrderTransactionOpts = {},
+ ): Promise<string> {
+ assert.doesConformToSchema('order', order, schemas.orderSchema);
+ assert.isValidBaseUnitAmount('takerTokenCancelAmount', cancelTakerTokenAmount);
+ await assert.isSenderAddressAsync('order.maker', order.maker, this._web3Wrapper);
- const exchangeInstance = await this._getExchangeContractAsync();
+ const exchangeInstance = await this._getExchangeContractAsync();
- const shouldValidate = _.isUndefined(orderTransactionOpts.shouldValidate)
- ? SHOULD_VALIDATE_BY_DEFAULT
- : orderTransactionOpts.shouldValidate;
- if (shouldValidate) {
- const orderHash = utils.getOrderHashHex(order);
- const unavailableTakerTokenAmount = await this.getUnavailableTakerAmountAsync(orderHash);
- OrderValidationUtils.validateCancelOrderThrowIfInvalid(
- order,
- cancelTakerTokenAmount,
- unavailableTakerTokenAmount,
- );
- }
+ const shouldValidate = _.isUndefined(orderTransactionOpts.shouldValidate)
+ ? SHOULD_VALIDATE_BY_DEFAULT
+ : orderTransactionOpts.shouldValidate;
+ if (shouldValidate) {
+ const orderHash = utils.getOrderHashHex(order);
+ const unavailableTakerTokenAmount = await this.getUnavailableTakerAmountAsync(orderHash);
+ OrderValidationUtils.validateCancelOrderThrowIfInvalid(
+ order,
+ cancelTakerTokenAmount,
+ unavailableTakerTokenAmount,
+ );
+ }
- const [orderAddresses, orderValues] = ExchangeWrapper._getOrderAddressesAndValues(order);
- const txHash = await exchangeInstance.cancelOrder.sendTransactionAsync(
- orderAddresses,
- orderValues,
- cancelTakerTokenAmount,
- {
- from: order.maker,
- gas: orderTransactionOpts.gasLimit,
- gasPrice: orderTransactionOpts.gasPrice,
- },
- );
- return txHash;
- }
- /**
- * Batch version of cancelOrderAsync. Atomically cancels multiple orders in a single transaction.
- * All orders must be from the same maker.
- * @param orderCancellationRequests An array of objects that conform to the OrderCancellationRequest
- * interface.
- * @param transactionOpts Optional arguments this method accepts.
- * @return Transaction hash.
- */
- @decorators.asyncZeroExErrorHandler
- public async batchCancelOrdersAsync(
- orderCancellationRequests: OrderCancellationRequest[],
- orderTransactionOpts: OrderTransactionOpts = {},
- ): Promise<string> {
- assert.doesConformToSchema(
- 'orderCancellationRequests',
- orderCancellationRequests,
- schemas.orderCancellationRequestsSchema,
- );
- const exchangeContractAddresses = _.map(
- orderCancellationRequests,
- orderCancellationRequest => orderCancellationRequest.order.exchangeContractAddress,
- );
- assert.hasAtMostOneUniqueValue(
- exchangeContractAddresses,
- ExchangeContractErrs.BatchOrdersMustHaveSameExchangeAddress,
- );
- const makers = _.map(orderCancellationRequests, cancellationRequest => cancellationRequest.order.maker);
- assert.hasAtMostOneUniqueValue(makers, ExchangeContractErrs.MultipleMakersInSingleCancelBatchDisallowed);
- const maker = makers[0];
- await assert.isSenderAddressAsync('maker', maker, this._web3Wrapper);
- const shouldValidate = _.isUndefined(orderTransactionOpts.shouldValidate)
- ? SHOULD_VALIDATE_BY_DEFAULT
- : orderTransactionOpts.shouldValidate;
- if (shouldValidate) {
- for (const orderCancellationRequest of orderCancellationRequests) {
- const orderHash = utils.getOrderHashHex(orderCancellationRequest.order);
- const unavailableTakerTokenAmount = await this.getUnavailableTakerAmountAsync(orderHash);
- OrderValidationUtils.validateCancelOrderThrowIfInvalid(
- orderCancellationRequest.order,
- orderCancellationRequest.takerTokenCancelAmount,
- unavailableTakerTokenAmount,
- );
- }
- }
- if (_.isEmpty(orderCancellationRequests)) {
- throw new Error(ExchangeContractErrs.BatchOrdersMustHaveAtLeastOneItem);
- }
- const exchangeInstance = await this._getExchangeContractAsync();
- const orderAddressesValuesAndTakerTokenCancelAmounts = _.map(orderCancellationRequests, cancellationRequest => {
- return [
- ...ExchangeWrapper._getOrderAddressesAndValues(cancellationRequest.order),
- cancellationRequest.takerTokenCancelAmount,
- ];
- });
- // We use _.unzip<any> because _.unzip doesn't type check if values have different types :'(
- const [orderAddresses, orderValues, cancelTakerTokenAmounts] = _.unzip<any>(
- orderAddressesValuesAndTakerTokenCancelAmounts,
- );
- const txHash = await exchangeInstance.batchCancelOrders.sendTransactionAsync(
- orderAddresses,
- orderValues,
- cancelTakerTokenAmounts,
- {
- from: maker,
- gas: orderTransactionOpts.gasLimit,
- gasPrice: orderTransactionOpts.gasPrice,
- },
- );
- return txHash;
- }
- /**
- * Subscribe to an event type emitted by the Exchange contract.
- * @param eventName The exchange contract event you would like to subscribe to.
- * @param indexFilterValues An object where the keys are indexed args returned by the event and
- * the value is the value you are interested in. E.g `{maker: aUserAddressHex}`
- * @param callback Callback that gets called when a log is added/removed
- * @return Subscription token used later to unsubscribe
- */
- public subscribe<ArgsType extends ExchangeContractEventArgs>(
- eventName: ExchangeEvents,
- indexFilterValues: IndexedFilterValues,
- callback: EventCallback<ArgsType>,
- ): string {
- assert.doesBelongToStringEnum('eventName', eventName, ExchangeEvents);
- assert.doesConformToSchema('indexFilterValues', indexFilterValues, schemas.indexFilterValuesSchema);
- assert.isFunction('callback', callback);
- const exchangeContractAddress = this.getContractAddress();
- const subscriptionToken = this._subscribe<ArgsType>(
- exchangeContractAddress,
- eventName,
- indexFilterValues,
- artifacts.ExchangeArtifact.abi,
- callback,
- );
- return subscriptionToken;
- }
- /**
- * Cancel a subscription
- * @param subscriptionToken Subscription token returned by `subscribe()`
- */
- public unsubscribe(subscriptionToken: string): void {
- this._unsubscribe(subscriptionToken);
- }
- /**
- * Cancels all existing subscriptions
- */
- public unsubscribeAll(): void {
- super.unsubscribeAll();
- }
- /**
- * Gets historical logs without creating a subscription
- * @param eventName The exchange contract event you would like to subscribe to.
- * @param blockRange Block range to get logs from.
- * @param indexFilterValues An object where the keys are indexed args returned by the event and
- * the value is the value you are interested in. E.g `{_from: aUserAddressHex}`
- * @return Array of logs that match the parameters
- */
- public async getLogsAsync<ArgsType extends ExchangeContractEventArgs>(
- eventName: ExchangeEvents,
- blockRange: BlockRange,
- indexFilterValues: IndexedFilterValues,
- ): Promise<Array<LogWithDecodedArgs<ArgsType>>> {
- assert.doesBelongToStringEnum('eventName', eventName, ExchangeEvents);
- assert.doesConformToSchema('blockRange', blockRange, schemas.blockRangeSchema);
- assert.doesConformToSchema('indexFilterValues', indexFilterValues, schemas.indexFilterValuesSchema);
- const exchangeContractAddress = this.getContractAddress();
- const logs = await this._getLogsAsync<ArgsType>(
- exchangeContractAddress,
- eventName,
- blockRange,
- indexFilterValues,
- artifacts.ExchangeArtifact.abi,
- );
- return logs;
- }
- /**
- * Retrieves the Ethereum address of the Exchange contract deployed on the network
- * that the user-passed web3 provider is connected to.
- * @returns The Ethereum address of the Exchange contract being used.
- */
- public getContractAddress(): string {
- const contractAddress = this._getContractAddress(artifacts.ExchangeArtifact, this._contractAddressIfExists);
- return contractAddress;
- }
- /**
- * Checks if order is still fillable and throws an error otherwise. Useful for orderbook
- * pruning where you want to remove stale orders without knowing who the taker will be.
- * @param signedOrder An object that conforms to the SignedOrder interface. The
- * signedOrder you wish to validate.
- * @param opts An object that conforms to the ValidateOrderFillableOpts
- * interface. Allows specifying a specific fillTakerTokenAmount
- * to validate for.
- */
- public async validateOrderFillableOrThrowAsync(
- signedOrder: SignedOrder,
- opts?: ValidateOrderFillableOpts,
- ): Promise<void> {
- assert.doesConformToSchema('signedOrder', signedOrder, schemas.signedOrderSchema);
- const zrxTokenAddress = this.getZRXTokenAddress();
- const expectedFillTakerTokenAmount = !_.isUndefined(opts) ? opts.expectedFillTakerTokenAmount : undefined;
- const exchangeTradeEmulator = new ExchangeTransferSimulator(this._tokenWrapper, BlockParamLiteral.Latest);
- await this._orderValidationUtils.validateOrderFillableOrThrowAsync(
- exchangeTradeEmulator,
- signedOrder,
- zrxTokenAddress,
- expectedFillTakerTokenAmount,
- );
- }
- /**
- * Checks if order fill will succeed and throws an error otherwise.
- * @param signedOrder An object that conforms to the SignedOrder interface. The
- * signedOrder you wish to fill.
- * @param fillTakerTokenAmount The total amount of the takerTokens you would like to fill.
- * @param takerAddress The user Ethereum address who would like to fill this order.
- * Must be available via the supplied Web3.Provider passed to 0x.js.
- */
- public async validateFillOrderThrowIfInvalidAsync(
- signedOrder: SignedOrder,
- fillTakerTokenAmount: BigNumber,
- takerAddress: string,
- ): Promise<void> {
- assert.doesConformToSchema('signedOrder', signedOrder, schemas.signedOrderSchema);
- assert.isValidBaseUnitAmount('fillTakerTokenAmount', fillTakerTokenAmount);
- await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper);
- const zrxTokenAddress = this.getZRXTokenAddress();
- const exchangeTradeEmulator = new ExchangeTransferSimulator(this._tokenWrapper, BlockParamLiteral.Latest);
- await this._orderValidationUtils.validateFillOrderThrowIfInvalidAsync(
- exchangeTradeEmulator,
- signedOrder,
- fillTakerTokenAmount,
- takerAddress,
- zrxTokenAddress,
- );
- }
- /**
- * Checks if cancelling a given order will succeed and throws an informative error if it won't.
- * @param order An object that conforms to the Order or SignedOrder interface.
- * The order you would like to cancel.
- * @param cancelTakerTokenAmount The amount (specified in taker tokens) that you would like to cancel.
- */
- public async validateCancelOrderThrowIfInvalidAsync(
- order: Order,
- cancelTakerTokenAmount: BigNumber,
- ): Promise<void> {
- assert.doesConformToSchema('order', order, schemas.orderSchema);
- assert.isValidBaseUnitAmount('cancelTakerTokenAmount', cancelTakerTokenAmount);
- const orderHash = utils.getOrderHashHex(order);
- const unavailableTakerTokenAmount = await this.getUnavailableTakerAmountAsync(orderHash);
- OrderValidationUtils.validateCancelOrderThrowIfInvalid(
- order,
- cancelTakerTokenAmount,
- unavailableTakerTokenAmount,
- );
- }
- /**
- * Checks if calling fillOrKill on a given order will succeed and throws an informative error if it won't.
- * @param signedOrder An object that conforms to the SignedOrder interface. The
- * signedOrder you wish to fill.
- * @param fillTakerTokenAmount The total amount of the takerTokens you would like to fill.
- * @param takerAddress The user Ethereum address who would like to fill this order.
- * Must be available via the supplied Web3.Provider passed to 0x.js.
- */
- public async validateFillOrKillOrderThrowIfInvalidAsync(
- signedOrder: SignedOrder,
- fillTakerTokenAmount: BigNumber,
- takerAddress: string,
- ): Promise<void> {
- assert.doesConformToSchema('signedOrder', signedOrder, schemas.signedOrderSchema);
- assert.isValidBaseUnitAmount('fillTakerTokenAmount', fillTakerTokenAmount);
- await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper);
- const zrxTokenAddress = this.getZRXTokenAddress();
- const exchangeTradeEmulator = new ExchangeTransferSimulator(this._tokenWrapper, BlockParamLiteral.Latest);
- await this._orderValidationUtils.validateFillOrKillOrderThrowIfInvalidAsync(
- exchangeTradeEmulator,
- signedOrder,
- fillTakerTokenAmount,
- takerAddress,
- zrxTokenAddress,
- );
- }
- /**
- * Checks if rounding error will be > 0.1% when computing makerTokenAmount by doing:
- * `(fillTakerTokenAmount * makerTokenAmount) / takerTokenAmount`.
- * 0x Protocol does not accept any trades that result in large rounding errors. This means that tokens with few or
- * no decimals can only be filled in quantities and ratios that avoid large rounding errors.
- * @param fillTakerTokenAmount The amount of the order (in taker tokens baseUnits) that you wish to fill.
- * @param takerTokenAmount The order size on the taker side
- * @param makerTokenAmount The order size on the maker side
- */
- public async isRoundingErrorAsync(
- fillTakerTokenAmount: BigNumber,
- takerTokenAmount: BigNumber,
- makerTokenAmount: BigNumber,
- ): Promise<boolean> {
- assert.isValidBaseUnitAmount('fillTakerTokenAmount', fillTakerTokenAmount);
- assert.isValidBaseUnitAmount('takerTokenAmount', takerTokenAmount);
- assert.isValidBaseUnitAmount('makerTokenAmount', makerTokenAmount);
- const exchangeInstance = await this._getExchangeContractAsync();
- const isRoundingError = await exchangeInstance.isRoundingError.callAsync(
- fillTakerTokenAmount,
- takerTokenAmount,
- makerTokenAmount,
- );
- return isRoundingError;
- }
- /**
- * Checks if logs contain LogError, which is emmited by Exchange contract on transaction failure.
- * @param logs Transaction logs as returned by `zeroEx.awaitTransactionMinedAsync`
- */
- public throwLogErrorsAsErrors(logs: Array<LogWithDecodedArgs<DecodedLogArgs> | Web3.LogEntry>): void {
- const errLog = _.find(logs, {
- event: ExchangeEvents.LogError,
- }) as LogWithDecodedArgs<LogErrorContractEventArgs> | undefined;
- if (!_.isUndefined(errLog)) {
- const logArgs = errLog.args;
- const errCode = logArgs.errorId.toNumber();
- const errMessage = this._exchangeContractErrCodesToMsg[errCode];
- throw new Error(errMessage);
- }
- }
- /**
- * Returns the ZRX token address used by the exchange contract.
- * @return Address of ZRX token
- */
- public getZRXTokenAddress(): string {
- const contractAddress = this._getContractAddress(artifacts.ZRXArtifact, this._zrxContractAddressIfExists);
- return contractAddress;
- }
- private _invalidateContractInstances(): void {
- this.unsubscribeAll();
- delete this._exchangeContractIfExists;
- }
- private async _isValidSignatureUsingContractCallAsync(
- dataHex: string,
- ecSignature: ECSignature,
- signerAddressHex: string,
- ): Promise<boolean> {
- assert.isHexString('dataHex', dataHex);
- assert.doesConformToSchema('ecSignature', ecSignature, schemas.ecSignatureSchema);
- assert.isETHAddressHex('signerAddressHex', signerAddressHex);
+ const [orderAddresses, orderValues] = ExchangeWrapper._getOrderAddressesAndValues(order);
+ const txHash = await exchangeInstance.cancelOrder.sendTransactionAsync(
+ orderAddresses,
+ orderValues,
+ cancelTakerTokenAmount,
+ {
+ from: order.maker,
+ gas: orderTransactionOpts.gasLimit,
+ gasPrice: orderTransactionOpts.gasPrice,
+ },
+ );
+ return txHash;
+ }
+ /**
+ * Batch version of cancelOrderAsync. Atomically cancels multiple orders in a single transaction.
+ * All orders must be from the same maker.
+ * @param orderCancellationRequests An array of objects that conform to the OrderCancellationRequest
+ * interface.
+ * @param transactionOpts Optional arguments this method accepts.
+ * @return Transaction hash.
+ */
+ @decorators.asyncZeroExErrorHandler
+ public async batchCancelOrdersAsync(
+ orderCancellationRequests: OrderCancellationRequest[],
+ orderTransactionOpts: OrderTransactionOpts = {},
+ ): Promise<string> {
+ assert.doesConformToSchema(
+ 'orderCancellationRequests',
+ orderCancellationRequests,
+ schemas.orderCancellationRequestsSchema,
+ );
+ const exchangeContractAddresses = _.map(
+ orderCancellationRequests,
+ orderCancellationRequest => orderCancellationRequest.order.exchangeContractAddress,
+ );
+ assert.hasAtMostOneUniqueValue(
+ exchangeContractAddresses,
+ ExchangeContractErrs.BatchOrdersMustHaveSameExchangeAddress,
+ );
+ const makers = _.map(orderCancellationRequests, cancellationRequest => cancellationRequest.order.maker);
+ assert.hasAtMostOneUniqueValue(makers, ExchangeContractErrs.MultipleMakersInSingleCancelBatchDisallowed);
+ const maker = makers[0];
+ await assert.isSenderAddressAsync('maker', maker, this._web3Wrapper);
+ const shouldValidate = _.isUndefined(orderTransactionOpts.shouldValidate)
+ ? SHOULD_VALIDATE_BY_DEFAULT
+ : orderTransactionOpts.shouldValidate;
+ if (shouldValidate) {
+ for (const orderCancellationRequest of orderCancellationRequests) {
+ const orderHash = utils.getOrderHashHex(orderCancellationRequest.order);
+ const unavailableTakerTokenAmount = await this.getUnavailableTakerAmountAsync(orderHash);
+ OrderValidationUtils.validateCancelOrderThrowIfInvalid(
+ orderCancellationRequest.order,
+ orderCancellationRequest.takerTokenCancelAmount,
+ unavailableTakerTokenAmount,
+ );
+ }
+ }
+ if (_.isEmpty(orderCancellationRequests)) {
+ throw new Error(ExchangeContractErrs.BatchOrdersMustHaveAtLeastOneItem);
+ }
+ const exchangeInstance = await this._getExchangeContractAsync();
+ const orderAddressesValuesAndTakerTokenCancelAmounts = _.map(orderCancellationRequests, cancellationRequest => {
+ return [
+ ...ExchangeWrapper._getOrderAddressesAndValues(cancellationRequest.order),
+ cancellationRequest.takerTokenCancelAmount,
+ ];
+ });
+ // We use _.unzip<any> because _.unzip doesn't type check if values have different types :'(
+ const [orderAddresses, orderValues, cancelTakerTokenAmounts] = _.unzip<any>(
+ orderAddressesValuesAndTakerTokenCancelAmounts,
+ );
+ const txHash = await exchangeInstance.batchCancelOrders.sendTransactionAsync(
+ orderAddresses,
+ orderValues,
+ cancelTakerTokenAmounts,
+ {
+ from: maker,
+ gas: orderTransactionOpts.gasLimit,
+ gasPrice: orderTransactionOpts.gasPrice,
+ },
+ );
+ return txHash;
+ }
+ /**
+ * Subscribe to an event type emitted by the Exchange contract.
+ * @param eventName The exchange contract event you would like to subscribe to.
+ * @param indexFilterValues An object where the keys are indexed args returned by the event and
+ * the value is the value you are interested in. E.g `{maker: aUserAddressHex}`
+ * @param callback Callback that gets called when a log is added/removed
+ * @return Subscription token used later to unsubscribe
+ */
+ public subscribe<ArgsType extends ExchangeContractEventArgs>(
+ eventName: ExchangeEvents,
+ indexFilterValues: IndexedFilterValues,
+ callback: EventCallback<ArgsType>,
+ ): string {
+ assert.doesBelongToStringEnum('eventName', eventName, ExchangeEvents);
+ assert.doesConformToSchema('indexFilterValues', indexFilterValues, schemas.indexFilterValuesSchema);
+ assert.isFunction('callback', callback);
+ const exchangeContractAddress = this.getContractAddress();
+ const subscriptionToken = this._subscribe<ArgsType>(
+ exchangeContractAddress,
+ eventName,
+ indexFilterValues,
+ artifacts.ExchangeArtifact.abi,
+ callback,
+ );
+ return subscriptionToken;
+ }
+ /**
+ * Cancel a subscription
+ * @param subscriptionToken Subscription token returned by `subscribe()`
+ */
+ public unsubscribe(subscriptionToken: string): void {
+ this._unsubscribe(subscriptionToken);
+ }
+ /**
+ * Cancels all existing subscriptions
+ */
+ public unsubscribeAll(): void {
+ super.unsubscribeAll();
+ }
+ /**
+ * Gets historical logs without creating a subscription
+ * @param eventName The exchange contract event you would like to subscribe to.
+ * @param blockRange Block range to get logs from.
+ * @param indexFilterValues An object where the keys are indexed args returned by the event and
+ * the value is the value you are interested in. E.g `{_from: aUserAddressHex}`
+ * @return Array of logs that match the parameters
+ */
+ public async getLogsAsync<ArgsType extends ExchangeContractEventArgs>(
+ eventName: ExchangeEvents,
+ blockRange: BlockRange,
+ indexFilterValues: IndexedFilterValues,
+ ): Promise<Array<LogWithDecodedArgs<ArgsType>>> {
+ assert.doesBelongToStringEnum('eventName', eventName, ExchangeEvents);
+ assert.doesConformToSchema('blockRange', blockRange, schemas.blockRangeSchema);
+ assert.doesConformToSchema('indexFilterValues', indexFilterValues, schemas.indexFilterValuesSchema);
+ const exchangeContractAddress = this.getContractAddress();
+ const logs = await this._getLogsAsync<ArgsType>(
+ exchangeContractAddress,
+ eventName,
+ blockRange,
+ indexFilterValues,
+ artifacts.ExchangeArtifact.abi,
+ );
+ return logs;
+ }
+ /**
+ * Retrieves the Ethereum address of the Exchange contract deployed on the network
+ * that the user-passed web3 provider is connected to.
+ * @returns The Ethereum address of the Exchange contract being used.
+ */
+ public getContractAddress(): string {
+ const contractAddress = this._getContractAddress(artifacts.ExchangeArtifact, this._contractAddressIfExists);
+ return contractAddress;
+ }
+ /**
+ * Checks if order is still fillable and throws an error otherwise. Useful for orderbook
+ * pruning where you want to remove stale orders without knowing who the taker will be.
+ * @param signedOrder An object that conforms to the SignedOrder interface. The
+ * signedOrder you wish to validate.
+ * @param opts An object that conforms to the ValidateOrderFillableOpts
+ * interface. Allows specifying a specific fillTakerTokenAmount
+ * to validate for.
+ */
+ public async validateOrderFillableOrThrowAsync(
+ signedOrder: SignedOrder,
+ opts?: ValidateOrderFillableOpts,
+ ): Promise<void> {
+ assert.doesConformToSchema('signedOrder', signedOrder, schemas.signedOrderSchema);
+ const zrxTokenAddress = this.getZRXTokenAddress();
+ const expectedFillTakerTokenAmount = !_.isUndefined(opts) ? opts.expectedFillTakerTokenAmount : undefined;
+ const exchangeTradeEmulator = new ExchangeTransferSimulator(this._tokenWrapper, BlockParamLiteral.Latest);
+ await this._orderValidationUtils.validateOrderFillableOrThrowAsync(
+ exchangeTradeEmulator,
+ signedOrder,
+ zrxTokenAddress,
+ expectedFillTakerTokenAmount,
+ );
+ }
+ /**
+ * Checks if order fill will succeed and throws an error otherwise.
+ * @param signedOrder An object that conforms to the SignedOrder interface. The
+ * signedOrder you wish to fill.
+ * @param fillTakerTokenAmount The total amount of the takerTokens you would like to fill.
+ * @param takerAddress The user Ethereum address who would like to fill this order.
+ * Must be available via the supplied Web3.Provider passed to 0x.js.
+ */
+ public async validateFillOrderThrowIfInvalidAsync(
+ signedOrder: SignedOrder,
+ fillTakerTokenAmount: BigNumber,
+ takerAddress: string,
+ ): Promise<void> {
+ assert.doesConformToSchema('signedOrder', signedOrder, schemas.signedOrderSchema);
+ assert.isValidBaseUnitAmount('fillTakerTokenAmount', fillTakerTokenAmount);
+ await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper);
+ const zrxTokenAddress = this.getZRXTokenAddress();
+ const exchangeTradeEmulator = new ExchangeTransferSimulator(this._tokenWrapper, BlockParamLiteral.Latest);
+ await this._orderValidationUtils.validateFillOrderThrowIfInvalidAsync(
+ exchangeTradeEmulator,
+ signedOrder,
+ fillTakerTokenAmount,
+ takerAddress,
+ zrxTokenAddress,
+ );
+ }
+ /**
+ * Checks if cancelling a given order will succeed and throws an informative error if it won't.
+ * @param order An object that conforms to the Order or SignedOrder interface.
+ * The order you would like to cancel.
+ * @param cancelTakerTokenAmount The amount (specified in taker tokens) that you would like to cancel.
+ */
+ public async validateCancelOrderThrowIfInvalidAsync(
+ order: Order,
+ cancelTakerTokenAmount: BigNumber,
+ ): Promise<void> {
+ assert.doesConformToSchema('order', order, schemas.orderSchema);
+ assert.isValidBaseUnitAmount('cancelTakerTokenAmount', cancelTakerTokenAmount);
+ const orderHash = utils.getOrderHashHex(order);
+ const unavailableTakerTokenAmount = await this.getUnavailableTakerAmountAsync(orderHash);
+ OrderValidationUtils.validateCancelOrderThrowIfInvalid(
+ order,
+ cancelTakerTokenAmount,
+ unavailableTakerTokenAmount,
+ );
+ }
+ /**
+ * Checks if calling fillOrKill on a given order will succeed and throws an informative error if it won't.
+ * @param signedOrder An object that conforms to the SignedOrder interface. The
+ * signedOrder you wish to fill.
+ * @param fillTakerTokenAmount The total amount of the takerTokens you would like to fill.
+ * @param takerAddress The user Ethereum address who would like to fill this order.
+ * Must be available via the supplied Web3.Provider passed to 0x.js.
+ */
+ public async validateFillOrKillOrderThrowIfInvalidAsync(
+ signedOrder: SignedOrder,
+ fillTakerTokenAmount: BigNumber,
+ takerAddress: string,
+ ): Promise<void> {
+ assert.doesConformToSchema('signedOrder', signedOrder, schemas.signedOrderSchema);
+ assert.isValidBaseUnitAmount('fillTakerTokenAmount', fillTakerTokenAmount);
+ await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper);
+ const zrxTokenAddress = this.getZRXTokenAddress();
+ const exchangeTradeEmulator = new ExchangeTransferSimulator(this._tokenWrapper, BlockParamLiteral.Latest);
+ await this._orderValidationUtils.validateFillOrKillOrderThrowIfInvalidAsync(
+ exchangeTradeEmulator,
+ signedOrder,
+ fillTakerTokenAmount,
+ takerAddress,
+ zrxTokenAddress,
+ );
+ }
+ /**
+ * Checks if rounding error will be > 0.1% when computing makerTokenAmount by doing:
+ * `(fillTakerTokenAmount * makerTokenAmount) / takerTokenAmount`.
+ * 0x Protocol does not accept any trades that result in large rounding errors. This means that tokens with few or
+ * no decimals can only be filled in quantities and ratios that avoid large rounding errors.
+ * @param fillTakerTokenAmount The amount of the order (in taker tokens baseUnits) that you wish to fill.
+ * @param takerTokenAmount The order size on the taker side
+ * @param makerTokenAmount The order size on the maker side
+ */
+ public async isRoundingErrorAsync(
+ fillTakerTokenAmount: BigNumber,
+ takerTokenAmount: BigNumber,
+ makerTokenAmount: BigNumber,
+ ): Promise<boolean> {
+ assert.isValidBaseUnitAmount('fillTakerTokenAmount', fillTakerTokenAmount);
+ assert.isValidBaseUnitAmount('takerTokenAmount', takerTokenAmount);
+ assert.isValidBaseUnitAmount('makerTokenAmount', makerTokenAmount);
+ const exchangeInstance = await this._getExchangeContractAsync();
+ const isRoundingError = await exchangeInstance.isRoundingError.callAsync(
+ fillTakerTokenAmount,
+ takerTokenAmount,
+ makerTokenAmount,
+ );
+ return isRoundingError;
+ }
+ /**
+ * Checks if logs contain LogError, which is emmited by Exchange contract on transaction failure.
+ * @param logs Transaction logs as returned by `zeroEx.awaitTransactionMinedAsync`
+ */
+ public throwLogErrorsAsErrors(logs: Array<LogWithDecodedArgs<DecodedLogArgs> | Web3.LogEntry>): void {
+ const errLog = _.find(logs, {
+ event: ExchangeEvents.LogError,
+ }) as LogWithDecodedArgs<LogErrorContractEventArgs> | undefined;
+ if (!_.isUndefined(errLog)) {
+ const logArgs = errLog.args;
+ const errCode = logArgs.errorId.toNumber();
+ const errMessage = this._exchangeContractErrCodesToMsg[errCode];
+ throw new Error(errMessage);
+ }
+ }
+ /**
+ * Returns the ZRX token address used by the exchange contract.
+ * @return Address of ZRX token
+ */
+ public getZRXTokenAddress(): string {
+ const contractAddress = this._getContractAddress(artifacts.ZRXArtifact, this._zrxContractAddressIfExists);
+ return contractAddress;
+ }
+ private _invalidateContractInstances(): void {
+ this.unsubscribeAll();
+ delete this._exchangeContractIfExists;
+ }
+ private async _isValidSignatureUsingContractCallAsync(
+ dataHex: string,
+ ecSignature: ECSignature,
+ signerAddressHex: string,
+ ): Promise<boolean> {
+ assert.isHexString('dataHex', dataHex);
+ assert.doesConformToSchema('ecSignature', ecSignature, schemas.ecSignatureSchema);
+ assert.isETHAddressHex('signerAddressHex', signerAddressHex);
- const exchangeInstance = await this._getExchangeContractAsync();
+ const exchangeInstance = await this._getExchangeContractAsync();
- const isValidSignature = await exchangeInstance.isValidSignature.callAsync(
- signerAddressHex,
- dataHex,
- ecSignature.v,
- ecSignature.r,
- ecSignature.s,
- );
- return isValidSignature;
- }
- private async _getOrderHashHexUsingContractCallAsync(order: Order | SignedOrder): Promise<string> {
- const exchangeInstance = await this._getExchangeContractAsync();
- const [orderAddresses, orderValues] = ExchangeWrapper._getOrderAddressesAndValues(order);
- const orderHashHex = await exchangeInstance.getOrderHash.callAsync(orderAddresses, orderValues);
- return orderHashHex;
- }
- private async _getExchangeContractAsync(): Promise<ExchangeContract> {
- if (!_.isUndefined(this._exchangeContractIfExists)) {
- return this._exchangeContractIfExists;
- }
- const web3ContractInstance = await this._instantiateContractIfExistsAsync(
- artifacts.ExchangeArtifact,
- this._contractAddressIfExists,
- );
- const contractInstance = new ExchangeContract(web3ContractInstance, this._web3Wrapper.getContractDefaults());
- this._exchangeContractIfExists = contractInstance;
- return this._exchangeContractIfExists;
- }
- private async _getTokenTransferProxyAddressAsync(): Promise<string> {
- const exchangeInstance = await this._getExchangeContractAsync();
- const tokenTransferProxyAddress = await exchangeInstance.TOKEN_TRANSFER_PROXY_CONTRACT.callAsync();
- const tokenTransferProxyAddressLowerCase = tokenTransferProxyAddress.toLowerCase();
- return tokenTransferProxyAddressLowerCase;
- }
+ const isValidSignature = await exchangeInstance.isValidSignature.callAsync(
+ signerAddressHex,
+ dataHex,
+ ecSignature.v,
+ ecSignature.r,
+ ecSignature.s,
+ );
+ return isValidSignature;
+ }
+ private async _getOrderHashHexUsingContractCallAsync(order: Order | SignedOrder): Promise<string> {
+ const exchangeInstance = await this._getExchangeContractAsync();
+ const [orderAddresses, orderValues] = ExchangeWrapper._getOrderAddressesAndValues(order);
+ const orderHashHex = await exchangeInstance.getOrderHash.callAsync(orderAddresses, orderValues);
+ return orderHashHex;
+ }
+ private async _getExchangeContractAsync(): Promise<ExchangeContract> {
+ if (!_.isUndefined(this._exchangeContractIfExists)) {
+ return this._exchangeContractIfExists;
+ }
+ const web3ContractInstance = await this._instantiateContractIfExistsAsync(
+ artifacts.ExchangeArtifact,
+ this._contractAddressIfExists,
+ );
+ const contractInstance = new ExchangeContract(web3ContractInstance, this._web3Wrapper.getContractDefaults());
+ this._exchangeContractIfExists = contractInstance;
+ return this._exchangeContractIfExists;
+ }
+ private async _getTokenTransferProxyAddressAsync(): Promise<string> {
+ const exchangeInstance = await this._getExchangeContractAsync();
+ const tokenTransferProxyAddress = await exchangeInstance.TOKEN_TRANSFER_PROXY_CONTRACT.callAsync();
+ const tokenTransferProxyAddressLowerCase = tokenTransferProxyAddress.toLowerCase();
+ return tokenTransferProxyAddressLowerCase;
+ }
} // tslint:disable:max-file-line-count
diff --git a/packages/0x.js/src/contract_wrappers/token_registry_wrapper.ts b/packages/0x.js/src/contract_wrappers/token_registry_wrapper.ts
index bce48a590..f54aaf0f8 100644
--- a/packages/0x.js/src/contract_wrappers/token_registry_wrapper.ts
+++ b/packages/0x.js/src/contract_wrappers/token_registry_wrapper.ts
@@ -13,117 +13,117 @@ import { TokenRegistryContract } from './generated/token_registry';
* This class includes all the functionality related to interacting with the 0x Token Registry smart contract.
*/
export class TokenRegistryWrapper extends ContractWrapper {
- private _tokenRegistryContractIfExists?: TokenRegistryContract;
- private _contractAddressIfExists?: string;
- private static _createTokenFromMetadata(metadata: TokenMetadata): Token | undefined {
- if (metadata[0] === constants.NULL_ADDRESS) {
- return undefined;
- }
- const token = {
- address: metadata[0],
- name: metadata[1],
- symbol: metadata[2],
- decimals: metadata[3].toNumber(),
- };
- return token;
- }
- constructor(web3Wrapper: Web3Wrapper, networkId: number, contractAddressIfExists?: string) {
- super(web3Wrapper, networkId);
- this._contractAddressIfExists = contractAddressIfExists;
- }
- /**
- * Retrieves all the tokens currently listed in the Token Registry smart contract
- * @return An array of objects that conform to the Token interface.
- */
- public async getTokensAsync(): Promise<Token[]> {
- const addresses = await this.getTokenAddressesAsync();
- const tokenPromises: Array<Promise<Token | undefined>> = _.map(addresses, async (address: string) =>
- this.getTokenIfExistsAsync(address),
- );
- const tokens = await Promise.all(tokenPromises);
- return tokens as Token[];
- }
- /**
- * Retrieves all the addresses of the tokens currently listed in the Token Registry smart contract
- * @return An array of token addresses.
- */
- public async getTokenAddressesAsync(): Promise<string[]> {
- const tokenRegistryContract = await this._getTokenRegistryContractAsync();
- const addresses = await tokenRegistryContract.getTokenAddresses.callAsync();
- return addresses;
- }
- /**
- * Retrieves a token by address currently listed in the Token Registry smart contract
- * @return An object that conforms to the Token interface or undefined if token not found.
- */
- public async getTokenIfExistsAsync(address: string): Promise<Token | undefined> {
- assert.isETHAddressHex('address', address);
+ private _tokenRegistryContractIfExists?: TokenRegistryContract;
+ private _contractAddressIfExists?: string;
+ private static _createTokenFromMetadata(metadata: TokenMetadata): Token | undefined {
+ if (metadata[0] === constants.NULL_ADDRESS) {
+ return undefined;
+ }
+ const token = {
+ address: metadata[0],
+ name: metadata[1],
+ symbol: metadata[2],
+ decimals: metadata[3].toNumber(),
+ };
+ return token;
+ }
+ constructor(web3Wrapper: Web3Wrapper, networkId: number, contractAddressIfExists?: string) {
+ super(web3Wrapper, networkId);
+ this._contractAddressIfExists = contractAddressIfExists;
+ }
+ /**
+ * Retrieves all the tokens currently listed in the Token Registry smart contract
+ * @return An array of objects that conform to the Token interface.
+ */
+ public async getTokensAsync(): Promise<Token[]> {
+ const addresses = await this.getTokenAddressesAsync();
+ const tokenPromises: Array<Promise<Token | undefined>> = _.map(addresses, async (address: string) =>
+ this.getTokenIfExistsAsync(address),
+ );
+ const tokens = await Promise.all(tokenPromises);
+ return tokens as Token[];
+ }
+ /**
+ * Retrieves all the addresses of the tokens currently listed in the Token Registry smart contract
+ * @return An array of token addresses.
+ */
+ public async getTokenAddressesAsync(): Promise<string[]> {
+ const tokenRegistryContract = await this._getTokenRegistryContractAsync();
+ const addresses = await tokenRegistryContract.getTokenAddresses.callAsync();
+ return addresses;
+ }
+ /**
+ * Retrieves a token by address currently listed in the Token Registry smart contract
+ * @return An object that conforms to the Token interface or undefined if token not found.
+ */
+ public async getTokenIfExistsAsync(address: string): Promise<Token | undefined> {
+ assert.isETHAddressHex('address', address);
- const tokenRegistryContract = await this._getTokenRegistryContractAsync();
- const metadata = await tokenRegistryContract.getTokenMetaData.callAsync(address);
- const token = TokenRegistryWrapper._createTokenFromMetadata(metadata);
- return token;
- }
- public async getTokenAddressBySymbolIfExistsAsync(symbol: string): Promise<string | undefined> {
- assert.isString('symbol', symbol);
- const tokenRegistryContract = await this._getTokenRegistryContractAsync();
- const addressIfExists = await tokenRegistryContract.getTokenAddressBySymbol.callAsync(symbol);
- if (addressIfExists === constants.NULL_ADDRESS) {
- return undefined;
- }
- return addressIfExists;
- }
- public async getTokenAddressByNameIfExistsAsync(name: string): Promise<string | undefined> {
- assert.isString('name', name);
- const tokenRegistryContract = await this._getTokenRegistryContractAsync();
- const addressIfExists = await tokenRegistryContract.getTokenAddressByName.callAsync(name);
- if (addressIfExists === constants.NULL_ADDRESS) {
- return undefined;
- }
- return addressIfExists;
- }
- public async getTokenBySymbolIfExistsAsync(symbol: string): Promise<Token | undefined> {
- assert.isString('symbol', symbol);
- const tokenRegistryContract = await this._getTokenRegistryContractAsync();
- const metadata = await tokenRegistryContract.getTokenBySymbol.callAsync(symbol);
- const token = TokenRegistryWrapper._createTokenFromMetadata(metadata);
- return token;
- }
- public async getTokenByNameIfExistsAsync(name: string): Promise<Token | undefined> {
- assert.isString('name', name);
- const tokenRegistryContract = await this._getTokenRegistryContractAsync();
- const metadata = await tokenRegistryContract.getTokenByName.callAsync(name);
- const token = TokenRegistryWrapper._createTokenFromMetadata(metadata);
- return token;
- }
- /**
- * Retrieves the Ethereum address of the TokenRegistry contract deployed on the network
- * that the user-passed web3 provider is connected to.
- * @returns The Ethereum address of the TokenRegistry contract being used.
- */
- public getContractAddress(): string {
- const contractAddress = this._getContractAddress(
- artifacts.TokenRegistryArtifact,
- this._contractAddressIfExists,
- );
- return contractAddress;
- }
- private _invalidateContractInstance(): void {
- delete this._tokenRegistryContractIfExists;
- }
- private async _getTokenRegistryContractAsync(): Promise<TokenRegistryContract> {
- if (!_.isUndefined(this._tokenRegistryContractIfExists)) {
- return this._tokenRegistryContractIfExists;
- }
- const web3ContractInstance = await this._instantiateContractIfExistsAsync(
- artifacts.TokenRegistryArtifact,
- this._contractAddressIfExists,
- );
- const contractInstance = new TokenRegistryContract(
- web3ContractInstance,
- this._web3Wrapper.getContractDefaults(),
- );
- this._tokenRegistryContractIfExists = contractInstance;
- return this._tokenRegistryContractIfExists;
- }
+ const tokenRegistryContract = await this._getTokenRegistryContractAsync();
+ const metadata = await tokenRegistryContract.getTokenMetaData.callAsync(address);
+ const token = TokenRegistryWrapper._createTokenFromMetadata(metadata);
+ return token;
+ }
+ public async getTokenAddressBySymbolIfExistsAsync(symbol: string): Promise<string | undefined> {
+ assert.isString('symbol', symbol);
+ const tokenRegistryContract = await this._getTokenRegistryContractAsync();
+ const addressIfExists = await tokenRegistryContract.getTokenAddressBySymbol.callAsync(symbol);
+ if (addressIfExists === constants.NULL_ADDRESS) {
+ return undefined;
+ }
+ return addressIfExists;
+ }
+ public async getTokenAddressByNameIfExistsAsync(name: string): Promise<string | undefined> {
+ assert.isString('name', name);
+ const tokenRegistryContract = await this._getTokenRegistryContractAsync();
+ const addressIfExists = await tokenRegistryContract.getTokenAddressByName.callAsync(name);
+ if (addressIfExists === constants.NULL_ADDRESS) {
+ return undefined;
+ }
+ return addressIfExists;
+ }
+ public async getTokenBySymbolIfExistsAsync(symbol: string): Promise<Token | undefined> {
+ assert.isString('symbol', symbol);
+ const tokenRegistryContract = await this._getTokenRegistryContractAsync();
+ const metadata = await tokenRegistryContract.getTokenBySymbol.callAsync(symbol);
+ const token = TokenRegistryWrapper._createTokenFromMetadata(metadata);
+ return token;
+ }
+ public async getTokenByNameIfExistsAsync(name: string): Promise<Token | undefined> {
+ assert.isString('name', name);
+ const tokenRegistryContract = await this._getTokenRegistryContractAsync();
+ const metadata = await tokenRegistryContract.getTokenByName.callAsync(name);
+ const token = TokenRegistryWrapper._createTokenFromMetadata(metadata);
+ return token;
+ }
+ /**
+ * Retrieves the Ethereum address of the TokenRegistry contract deployed on the network
+ * that the user-passed web3 provider is connected to.
+ * @returns The Ethereum address of the TokenRegistry contract being used.
+ */
+ public getContractAddress(): string {
+ const contractAddress = this._getContractAddress(
+ artifacts.TokenRegistryArtifact,
+ this._contractAddressIfExists,
+ );
+ return contractAddress;
+ }
+ private _invalidateContractInstance(): void {
+ delete this._tokenRegistryContractIfExists;
+ }
+ private async _getTokenRegistryContractAsync(): Promise<TokenRegistryContract> {
+ if (!_.isUndefined(this._tokenRegistryContractIfExists)) {
+ return this._tokenRegistryContractIfExists;
+ }
+ const web3ContractInstance = await this._instantiateContractIfExistsAsync(
+ artifacts.TokenRegistryArtifact,
+ this._contractAddressIfExists,
+ );
+ const contractInstance = new TokenRegistryContract(
+ web3ContractInstance,
+ this._web3Wrapper.getContractDefaults(),
+ );
+ this._tokenRegistryContractIfExists = contractInstance;
+ return this._tokenRegistryContractIfExists;
+ }
}
diff --git a/packages/0x.js/src/contract_wrappers/token_transfer_proxy_wrapper.ts b/packages/0x.js/src/contract_wrappers/token_transfer_proxy_wrapper.ts
index 635588fed..f5d9d108a 100644
--- a/packages/0x.js/src/contract_wrappers/token_transfer_proxy_wrapper.ts
+++ b/packages/0x.js/src/contract_wrappers/token_transfer_proxy_wrapper.ts
@@ -10,59 +10,59 @@ import { TokenTransferProxyContract } from './generated/token_transfer_proxy';
* This class includes the functionality related to interacting with the TokenTransferProxy contract.
*/
export class TokenTransferProxyWrapper extends ContractWrapper {
- private _tokenTransferProxyContractIfExists?: TokenTransferProxyContract;
- private _contractAddressIfExists?: string;
- constructor(web3Wrapper: Web3Wrapper, networkId: number, contractAddressIfExists?: string) {
- super(web3Wrapper, networkId);
- this._contractAddressIfExists = contractAddressIfExists;
- }
- /**
- * Check if the Exchange contract address is authorized by the TokenTransferProxy contract.
- * @param exchangeContractAddress The hex encoded address of the Exchange contract to call.
- * @return Whether the exchangeContractAddress is authorized.
- */
- public async isAuthorizedAsync(exchangeContractAddress: string): Promise<boolean> {
- const tokenTransferProxyContractInstance = await this._getTokenTransferProxyContractAsync();
- const isAuthorized = await tokenTransferProxyContractInstance.authorized.callAsync(exchangeContractAddress);
- return isAuthorized;
- }
- /**
- * Get the list of all Exchange contract addresses authorized by the TokenTransferProxy contract.
- * @return The list of authorized addresses.
- */
- public async getAuthorizedAddressesAsync(): Promise<string[]> {
- const tokenTransferProxyContractInstance = await this._getTokenTransferProxyContractAsync();
- const authorizedAddresses = await tokenTransferProxyContractInstance.getAuthorizedAddresses.callAsync();
- return authorizedAddresses;
- }
- /**
- * Retrieves the Ethereum address of the TokenTransferProxy contract deployed on the network
- * that the user-passed web3 provider is connected to.
- * @returns The Ethereum address of the TokenTransferProxy contract being used.
- */
- public getContractAddress(): string {
- const contractAddress = this._getContractAddress(
- artifacts.TokenTransferProxyArtifact,
- this._contractAddressIfExists,
- );
- return contractAddress;
- }
- private _invalidateContractInstance(): void {
- delete this._tokenTransferProxyContractIfExists;
- }
- private async _getTokenTransferProxyContractAsync(): Promise<TokenTransferProxyContract> {
- if (!_.isUndefined(this._tokenTransferProxyContractIfExists)) {
- return this._tokenTransferProxyContractIfExists;
- }
- const web3ContractInstance = await this._instantiateContractIfExistsAsync(
- artifacts.TokenTransferProxyArtifact,
- this._contractAddressIfExists,
- );
- const contractInstance = new TokenTransferProxyContract(
- web3ContractInstance,
- this._web3Wrapper.getContractDefaults(),
- );
- this._tokenTransferProxyContractIfExists = contractInstance;
- return this._tokenTransferProxyContractIfExists;
- }
+ private _tokenTransferProxyContractIfExists?: TokenTransferProxyContract;
+ private _contractAddressIfExists?: string;
+ constructor(web3Wrapper: Web3Wrapper, networkId: number, contractAddressIfExists?: string) {
+ super(web3Wrapper, networkId);
+ this._contractAddressIfExists = contractAddressIfExists;
+ }
+ /**
+ * Check if the Exchange contract address is authorized by the TokenTransferProxy contract.
+ * @param exchangeContractAddress The hex encoded address of the Exchange contract to call.
+ * @return Whether the exchangeContractAddress is authorized.
+ */
+ public async isAuthorizedAsync(exchangeContractAddress: string): Promise<boolean> {
+ const tokenTransferProxyContractInstance = await this._getTokenTransferProxyContractAsync();
+ const isAuthorized = await tokenTransferProxyContractInstance.authorized.callAsync(exchangeContractAddress);
+ return isAuthorized;
+ }
+ /**
+ * Get the list of all Exchange contract addresses authorized by the TokenTransferProxy contract.
+ * @return The list of authorized addresses.
+ */
+ public async getAuthorizedAddressesAsync(): Promise<string[]> {
+ const tokenTransferProxyContractInstance = await this._getTokenTransferProxyContractAsync();
+ const authorizedAddresses = await tokenTransferProxyContractInstance.getAuthorizedAddresses.callAsync();
+ return authorizedAddresses;
+ }
+ /**
+ * Retrieves the Ethereum address of the TokenTransferProxy contract deployed on the network
+ * that the user-passed web3 provider is connected to.
+ * @returns The Ethereum address of the TokenTransferProxy contract being used.
+ */
+ public getContractAddress(): string {
+ const contractAddress = this._getContractAddress(
+ artifacts.TokenTransferProxyArtifact,
+ this._contractAddressIfExists,
+ );
+ return contractAddress;
+ }
+ private _invalidateContractInstance(): void {
+ delete this._tokenTransferProxyContractIfExists;
+ }
+ private async _getTokenTransferProxyContractAsync(): Promise<TokenTransferProxyContract> {
+ if (!_.isUndefined(this._tokenTransferProxyContractIfExists)) {
+ return this._tokenTransferProxyContractIfExists;
+ }
+ const web3ContractInstance = await this._instantiateContractIfExistsAsync(
+ artifacts.TokenTransferProxyArtifact,
+ this._contractAddressIfExists,
+ );
+ const contractInstance = new TokenTransferProxyContract(
+ web3ContractInstance,
+ this._web3Wrapper.getContractDefaults(),
+ );
+ this._tokenTransferProxyContractIfExists = contractInstance;
+ return this._tokenTransferProxyContractIfExists;
+ }
}
diff --git a/packages/0x.js/src/contract_wrappers/token_wrapper.ts b/packages/0x.js/src/contract_wrappers/token_wrapper.ts
index 366cabd9e..7943f4a60 100644
--- a/packages/0x.js/src/contract_wrappers/token_wrapper.ts
+++ b/packages/0x.js/src/contract_wrappers/token_wrapper.ts
@@ -5,15 +5,15 @@ import * as _ from 'lodash';
import { artifacts } from '../artifacts';
import {
- BlockRange,
- EventCallback,
- IndexedFilterValues,
- LogWithDecodedArgs,
- MethodOpts,
- TokenContractEventArgs,
- TokenEvents,
- TransactionOpts,
- ZeroExError,
+ BlockRange,
+ EventCallback,
+ IndexedFilterValues,
+ LogWithDecodedArgs,
+ MethodOpts,
+ TokenContractEventArgs,
+ TokenEvents,
+ TransactionOpts,
+ ZeroExError,
} from '../types';
import { AbiDecoder } from '../utils/abi_decoder';
import { assert } from '../utils/assert';
@@ -29,367 +29,367 @@ import { TokenTransferProxyWrapper } from './token_transfer_proxy_wrapper';
* to the 0x Proxy smart contract.
*/
export class TokenWrapper extends ContractWrapper {
- public UNLIMITED_ALLOWANCE_IN_BASE_UNITS = constants.UNLIMITED_ALLOWANCE_IN_BASE_UNITS;
- private _tokenContractsByAddress: { [address: string]: TokenContract };
- private _tokenTransferProxyWrapper: TokenTransferProxyWrapper;
- constructor(
- web3Wrapper: Web3Wrapper,
- networkId: number,
- abiDecoder: AbiDecoder,
- tokenTransferProxyWrapper: TokenTransferProxyWrapper,
- ) {
- super(web3Wrapper, networkId, abiDecoder);
- this._tokenContractsByAddress = {};
- this._tokenTransferProxyWrapper = tokenTransferProxyWrapper;
- }
- /**
- * Retrieves an owner's ERC20 token balance.
- * @param tokenAddress The hex encoded contract Ethereum address where the ERC20 token is deployed.
- * @param ownerAddress The hex encoded user Ethereum address whose balance you would like to check.
- * @param methodOpts Optional arguments this method accepts.
- * @return The owner's ERC20 token balance in base units.
- */
- public async getBalanceAsync(
- tokenAddress: string,
- ownerAddress: string,
- methodOpts?: MethodOpts,
- ): Promise<BigNumber> {
- assert.isETHAddressHex('ownerAddress', ownerAddress);
- assert.isETHAddressHex('tokenAddress', tokenAddress);
+ public UNLIMITED_ALLOWANCE_IN_BASE_UNITS = constants.UNLIMITED_ALLOWANCE_IN_BASE_UNITS;
+ private _tokenContractsByAddress: { [address: string]: TokenContract };
+ private _tokenTransferProxyWrapper: TokenTransferProxyWrapper;
+ constructor(
+ web3Wrapper: Web3Wrapper,
+ networkId: number,
+ abiDecoder: AbiDecoder,
+ tokenTransferProxyWrapper: TokenTransferProxyWrapper,
+ ) {
+ super(web3Wrapper, networkId, abiDecoder);
+ this._tokenContractsByAddress = {};
+ this._tokenTransferProxyWrapper = tokenTransferProxyWrapper;
+ }
+ /**
+ * Retrieves an owner's ERC20 token balance.
+ * @param tokenAddress The hex encoded contract Ethereum address where the ERC20 token is deployed.
+ * @param ownerAddress The hex encoded user Ethereum address whose balance you would like to check.
+ * @param methodOpts Optional arguments this method accepts.
+ * @return The owner's ERC20 token balance in base units.
+ */
+ public async getBalanceAsync(
+ tokenAddress: string,
+ ownerAddress: string,
+ methodOpts?: MethodOpts,
+ ): Promise<BigNumber> {
+ assert.isETHAddressHex('ownerAddress', ownerAddress);
+ assert.isETHAddressHex('tokenAddress', tokenAddress);
- const tokenContract = await this._getTokenContractAsync(tokenAddress);
- const defaultBlock = _.isUndefined(methodOpts) ? undefined : methodOpts.defaultBlock;
- let balance = await tokenContract.balanceOf.callAsync(ownerAddress, defaultBlock);
- // Wrap BigNumbers returned from web3 with our own (later) version of BigNumber
- balance = new BigNumber(balance);
- return balance;
- }
- /**
- * Sets the spender's allowance to a specified number of baseUnits on behalf of the owner address.
- * Equivalent to the ERC20 spec method `approve`.
- * @param tokenAddress The hex encoded contract Ethereum address where the ERC20 token is deployed.
- * @param ownerAddress The hex encoded user Ethereum address who would like to set an allowance
- * for spenderAddress.
- * @param spenderAddress The hex encoded user Ethereum address who will be able to spend the set allowance.
- * @param amountInBaseUnits The allowance amount you would like to set.
- * @param txOpts Transaction parameters.
- * @return Transaction hash.
- */
- public async setAllowanceAsync(
- tokenAddress: string,
- ownerAddress: string,
- spenderAddress: string,
- amountInBaseUnits: BigNumber,
- txOpts: TransactionOpts = {},
- ): Promise<string> {
- await assert.isSenderAddressAsync('ownerAddress', ownerAddress, this._web3Wrapper);
- assert.isETHAddressHex('spenderAddress', spenderAddress);
- assert.isETHAddressHex('tokenAddress', tokenAddress);
- assert.isValidBaseUnitAmount('amountInBaseUnits', amountInBaseUnits);
+ const tokenContract = await this._getTokenContractAsync(tokenAddress);
+ const defaultBlock = _.isUndefined(methodOpts) ? undefined : methodOpts.defaultBlock;
+ let balance = await tokenContract.balanceOf.callAsync(ownerAddress, defaultBlock);
+ // Wrap BigNumbers returned from web3 with our own (later) version of BigNumber
+ balance = new BigNumber(balance);
+ return balance;
+ }
+ /**
+ * Sets the spender's allowance to a specified number of baseUnits on behalf of the owner address.
+ * Equivalent to the ERC20 spec method `approve`.
+ * @param tokenAddress The hex encoded contract Ethereum address where the ERC20 token is deployed.
+ * @param ownerAddress The hex encoded user Ethereum address who would like to set an allowance
+ * for spenderAddress.
+ * @param spenderAddress The hex encoded user Ethereum address who will be able to spend the set allowance.
+ * @param amountInBaseUnits The allowance amount you would like to set.
+ * @param txOpts Transaction parameters.
+ * @return Transaction hash.
+ */
+ public async setAllowanceAsync(
+ tokenAddress: string,
+ ownerAddress: string,
+ spenderAddress: string,
+ amountInBaseUnits: BigNumber,
+ txOpts: TransactionOpts = {},
+ ): Promise<string> {
+ await assert.isSenderAddressAsync('ownerAddress', ownerAddress, this._web3Wrapper);
+ assert.isETHAddressHex('spenderAddress', spenderAddress);
+ assert.isETHAddressHex('tokenAddress', tokenAddress);
+ assert.isValidBaseUnitAmount('amountInBaseUnits', amountInBaseUnits);
- const tokenContract = await this._getTokenContractAsync(tokenAddress);
- const txHash = await tokenContract.approve.sendTransactionAsync(spenderAddress, amountInBaseUnits, {
- from: ownerAddress,
- gas: txOpts.gasLimit,
- gasPrice: txOpts.gasPrice,
- });
- return txHash;
- }
- /**
- * Sets the spender's allowance to an unlimited number of baseUnits on behalf of the owner address.
- * Equivalent to the ERC20 spec method `approve`.
- * Setting an unlimited allowance will lower the gas cost for filling orders involving tokens that forego updating
- * allowances set to the max amount (e.g ZRX, WETH)
- * @param tokenAddress The hex encoded contract Ethereum address where the ERC20 token is deployed.
- * @param ownerAddress The hex encoded user Ethereum address who would like to set an allowance
- * for spenderAddress.
- * @param spenderAddress The hex encoded user Ethereum address who will be able to spend the set allowance.
- * @param txOpts Transaction parameters.
- * @return Transaction hash.
- */
- public async setUnlimitedAllowanceAsync(
- tokenAddress: string,
- ownerAddress: string,
- spenderAddress: string,
- txOpts: TransactionOpts = {},
- ): Promise<string> {
- const txHash = await this.setAllowanceAsync(
- tokenAddress,
- ownerAddress,
- spenderAddress,
- this.UNLIMITED_ALLOWANCE_IN_BASE_UNITS,
- txOpts,
- );
- return txHash;
- }
- /**
- * Retrieves the owners allowance in baseUnits set to the spender's address.
- * @param tokenAddress The hex encoded contract Ethereum address where the ERC20 token is deployed.
- * @param ownerAddress The hex encoded user Ethereum address whose allowance to spenderAddress
- * you would like to retrieve.
- * @param spenderAddress The hex encoded user Ethereum address who can spend the allowance you are fetching.
- * @param methodOpts Optional arguments this method accepts.
- */
- public async getAllowanceAsync(
- tokenAddress: string,
- ownerAddress: string,
- spenderAddress: string,
- methodOpts?: MethodOpts,
- ): Promise<BigNumber> {
- assert.isETHAddressHex('ownerAddress', ownerAddress);
- assert.isETHAddressHex('tokenAddress', tokenAddress);
+ const tokenContract = await this._getTokenContractAsync(tokenAddress);
+ const txHash = await tokenContract.approve.sendTransactionAsync(spenderAddress, amountInBaseUnits, {
+ from: ownerAddress,
+ gas: txOpts.gasLimit,
+ gasPrice: txOpts.gasPrice,
+ });
+ return txHash;
+ }
+ /**
+ * Sets the spender's allowance to an unlimited number of baseUnits on behalf of the owner address.
+ * Equivalent to the ERC20 spec method `approve`.
+ * Setting an unlimited allowance will lower the gas cost for filling orders involving tokens that forego updating
+ * allowances set to the max amount (e.g ZRX, WETH)
+ * @param tokenAddress The hex encoded contract Ethereum address where the ERC20 token is deployed.
+ * @param ownerAddress The hex encoded user Ethereum address who would like to set an allowance
+ * for spenderAddress.
+ * @param spenderAddress The hex encoded user Ethereum address who will be able to spend the set allowance.
+ * @param txOpts Transaction parameters.
+ * @return Transaction hash.
+ */
+ public async setUnlimitedAllowanceAsync(
+ tokenAddress: string,
+ ownerAddress: string,
+ spenderAddress: string,
+ txOpts: TransactionOpts = {},
+ ): Promise<string> {
+ const txHash = await this.setAllowanceAsync(
+ tokenAddress,
+ ownerAddress,
+ spenderAddress,
+ this.UNLIMITED_ALLOWANCE_IN_BASE_UNITS,
+ txOpts,
+ );
+ return txHash;
+ }
+ /**
+ * Retrieves the owners allowance in baseUnits set to the spender's address.
+ * @param tokenAddress The hex encoded contract Ethereum address where the ERC20 token is deployed.
+ * @param ownerAddress The hex encoded user Ethereum address whose allowance to spenderAddress
+ * you would like to retrieve.
+ * @param spenderAddress The hex encoded user Ethereum address who can spend the allowance you are fetching.
+ * @param methodOpts Optional arguments this method accepts.
+ */
+ public async getAllowanceAsync(
+ tokenAddress: string,
+ ownerAddress: string,
+ spenderAddress: string,
+ methodOpts?: MethodOpts,
+ ): Promise<BigNumber> {
+ assert.isETHAddressHex('ownerAddress', ownerAddress);
+ assert.isETHAddressHex('tokenAddress', tokenAddress);
- const tokenContract = await this._getTokenContractAsync(tokenAddress);
- const defaultBlock = _.isUndefined(methodOpts) ? undefined : methodOpts.defaultBlock;
- let allowanceInBaseUnits = await tokenContract.allowance.callAsync(ownerAddress, spenderAddress, defaultBlock);
- // Wrap BigNumbers returned from web3 with our own (later) version of BigNumber
- allowanceInBaseUnits = new BigNumber(allowanceInBaseUnits);
- return allowanceInBaseUnits;
- }
- /**
- * Retrieves the owner's allowance in baseUnits set to the 0x proxy contract.
- * @param tokenAddress The hex encoded contract Ethereum address where the ERC20 token is deployed.
- * @param ownerAddress The hex encoded user Ethereum address whose proxy contract allowance we are retrieving.
- * @param methodOpts Optional arguments this method accepts.
- */
- public async getProxyAllowanceAsync(
- tokenAddress: string,
- ownerAddress: string,
- methodOpts?: MethodOpts,
- ): Promise<BigNumber> {
- assert.isETHAddressHex('ownerAddress', ownerAddress);
- assert.isETHAddressHex('tokenAddress', tokenAddress);
+ const tokenContract = await this._getTokenContractAsync(tokenAddress);
+ const defaultBlock = _.isUndefined(methodOpts) ? undefined : methodOpts.defaultBlock;
+ let allowanceInBaseUnits = await tokenContract.allowance.callAsync(ownerAddress, spenderAddress, defaultBlock);
+ // Wrap BigNumbers returned from web3 with our own (later) version of BigNumber
+ allowanceInBaseUnits = new BigNumber(allowanceInBaseUnits);
+ return allowanceInBaseUnits;
+ }
+ /**
+ * Retrieves the owner's allowance in baseUnits set to the 0x proxy contract.
+ * @param tokenAddress The hex encoded contract Ethereum address where the ERC20 token is deployed.
+ * @param ownerAddress The hex encoded user Ethereum address whose proxy contract allowance we are retrieving.
+ * @param methodOpts Optional arguments this method accepts.
+ */
+ public async getProxyAllowanceAsync(
+ tokenAddress: string,
+ ownerAddress: string,
+ methodOpts?: MethodOpts,
+ ): Promise<BigNumber> {
+ assert.isETHAddressHex('ownerAddress', ownerAddress);
+ assert.isETHAddressHex('tokenAddress', tokenAddress);
- const proxyAddress = this._tokenTransferProxyWrapper.getContractAddress();
- const allowanceInBaseUnits = await this.getAllowanceAsync(tokenAddress, ownerAddress, proxyAddress, methodOpts);
- return allowanceInBaseUnits;
- }
- /**
- * Sets the 0x proxy contract's allowance to a specified number of a tokens' baseUnits on behalf
- * of an owner address.
- * @param tokenAddress The hex encoded contract Ethereum address where the ERC20 token is deployed.
- * @param ownerAddress The hex encoded user Ethereum address who is setting an allowance
- * for the Proxy contract.
- * @param amountInBaseUnits The allowance amount specified in baseUnits.
- * @param txOpts Transaction parameters.
- * @return Transaction hash.
- */
- public async setProxyAllowanceAsync(
- tokenAddress: string,
- ownerAddress: string,
- amountInBaseUnits: BigNumber,
- txOpts: TransactionOpts = {},
- ): Promise<string> {
- assert.isETHAddressHex('ownerAddress', ownerAddress);
- assert.isETHAddressHex('tokenAddress', tokenAddress);
- assert.isValidBaseUnitAmount('amountInBaseUnits', amountInBaseUnits);
+ const proxyAddress = this._tokenTransferProxyWrapper.getContractAddress();
+ const allowanceInBaseUnits = await this.getAllowanceAsync(tokenAddress, ownerAddress, proxyAddress, methodOpts);
+ return allowanceInBaseUnits;
+ }
+ /**
+ * Sets the 0x proxy contract's allowance to a specified number of a tokens' baseUnits on behalf
+ * of an owner address.
+ * @param tokenAddress The hex encoded contract Ethereum address where the ERC20 token is deployed.
+ * @param ownerAddress The hex encoded user Ethereum address who is setting an allowance
+ * for the Proxy contract.
+ * @param amountInBaseUnits The allowance amount specified in baseUnits.
+ * @param txOpts Transaction parameters.
+ * @return Transaction hash.
+ */
+ public async setProxyAllowanceAsync(
+ tokenAddress: string,
+ ownerAddress: string,
+ amountInBaseUnits: BigNumber,
+ txOpts: TransactionOpts = {},
+ ): Promise<string> {
+ assert.isETHAddressHex('ownerAddress', ownerAddress);
+ assert.isETHAddressHex('tokenAddress', tokenAddress);
+ assert.isValidBaseUnitAmount('amountInBaseUnits', amountInBaseUnits);
- const proxyAddress = this._tokenTransferProxyWrapper.getContractAddress();
- const txHash = await this.setAllowanceAsync(
- tokenAddress,
- ownerAddress,
- proxyAddress,
- amountInBaseUnits,
- txOpts,
- );
- return txHash;
- }
- /**
- * Sets the 0x proxy contract's allowance to a unlimited number of a tokens' baseUnits on behalf
- * of an owner address.
- * Setting an unlimited allowance will lower the gas cost for filling orders involving tokens that forego updating
- * allowances set to the max amount (e.g ZRX, WETH)
- * @param tokenAddress The hex encoded contract Ethereum address where the ERC20 token is deployed.
- * @param ownerAddress The hex encoded user Ethereum address who is setting an allowance
- * for the Proxy contract.
- * @param txOpts Transaction parameters.
- * @return Transaction hash.
- */
- public async setUnlimitedProxyAllowanceAsync(
- tokenAddress: string,
- ownerAddress: string,
- txOpts: TransactionOpts = {},
- ): Promise<string> {
- const txHash = await this.setProxyAllowanceAsync(
- tokenAddress,
- ownerAddress,
- this.UNLIMITED_ALLOWANCE_IN_BASE_UNITS,
- txOpts,
- );
- return txHash;
- }
- /**
- * Transfers `amountInBaseUnits` ERC20 tokens from `fromAddress` to `toAddress`.
- * @param tokenAddress The hex encoded contract Ethereum address where the ERC20 token is deployed.
- * @param fromAddress The hex encoded user Ethereum address that will send the funds.
- * @param toAddress The hex encoded user Ethereum address that will receive the funds.
- * @param amountInBaseUnits The amount (specified in baseUnits) of the token to transfer.
- * @param txOpts Transaction parameters.
- * @return Transaction hash.
- */
- public async transferAsync(
- tokenAddress: string,
- fromAddress: string,
- toAddress: string,
- amountInBaseUnits: BigNumber,
- txOpts: TransactionOpts = {},
- ): Promise<string> {
- assert.isETHAddressHex('tokenAddress', tokenAddress);
- await assert.isSenderAddressAsync('fromAddress', fromAddress, this._web3Wrapper);
- assert.isETHAddressHex('toAddress', toAddress);
- assert.isValidBaseUnitAmount('amountInBaseUnits', amountInBaseUnits);
+ const proxyAddress = this._tokenTransferProxyWrapper.getContractAddress();
+ const txHash = await this.setAllowanceAsync(
+ tokenAddress,
+ ownerAddress,
+ proxyAddress,
+ amountInBaseUnits,
+ txOpts,
+ );
+ return txHash;
+ }
+ /**
+ * Sets the 0x proxy contract's allowance to a unlimited number of a tokens' baseUnits on behalf
+ * of an owner address.
+ * Setting an unlimited allowance will lower the gas cost for filling orders involving tokens that forego updating
+ * allowances set to the max amount (e.g ZRX, WETH)
+ * @param tokenAddress The hex encoded contract Ethereum address where the ERC20 token is deployed.
+ * @param ownerAddress The hex encoded user Ethereum address who is setting an allowance
+ * for the Proxy contract.
+ * @param txOpts Transaction parameters.
+ * @return Transaction hash.
+ */
+ public async setUnlimitedProxyAllowanceAsync(
+ tokenAddress: string,
+ ownerAddress: string,
+ txOpts: TransactionOpts = {},
+ ): Promise<string> {
+ const txHash = await this.setProxyAllowanceAsync(
+ tokenAddress,
+ ownerAddress,
+ this.UNLIMITED_ALLOWANCE_IN_BASE_UNITS,
+ txOpts,
+ );
+ return txHash;
+ }
+ /**
+ * Transfers `amountInBaseUnits` ERC20 tokens from `fromAddress` to `toAddress`.
+ * @param tokenAddress The hex encoded contract Ethereum address where the ERC20 token is deployed.
+ * @param fromAddress The hex encoded user Ethereum address that will send the funds.
+ * @param toAddress The hex encoded user Ethereum address that will receive the funds.
+ * @param amountInBaseUnits The amount (specified in baseUnits) of the token to transfer.
+ * @param txOpts Transaction parameters.
+ * @return Transaction hash.
+ */
+ public async transferAsync(
+ tokenAddress: string,
+ fromAddress: string,
+ toAddress: string,
+ amountInBaseUnits: BigNumber,
+ txOpts: TransactionOpts = {},
+ ): Promise<string> {
+ assert.isETHAddressHex('tokenAddress', tokenAddress);
+ await assert.isSenderAddressAsync('fromAddress', fromAddress, this._web3Wrapper);
+ assert.isETHAddressHex('toAddress', toAddress);
+ assert.isValidBaseUnitAmount('amountInBaseUnits', amountInBaseUnits);
- const tokenContract = await this._getTokenContractAsync(tokenAddress);
+ const tokenContract = await this._getTokenContractAsync(tokenAddress);
- const fromAddressBalance = await this.getBalanceAsync(tokenAddress, fromAddress);
- if (fromAddressBalance.lessThan(amountInBaseUnits)) {
- throw new Error(ZeroExError.InsufficientBalanceForTransfer);
- }
+ const fromAddressBalance = await this.getBalanceAsync(tokenAddress, fromAddress);
+ if (fromAddressBalance.lessThan(amountInBaseUnits)) {
+ throw new Error(ZeroExError.InsufficientBalanceForTransfer);
+ }
- const txHash = await tokenContract.transfer.sendTransactionAsync(toAddress, amountInBaseUnits, {
- from: fromAddress,
- gas: txOpts.gasLimit,
- gasPrice: txOpts.gasPrice,
- });
- return txHash;
- }
- /**
- * Transfers `amountInBaseUnits` ERC20 tokens from `fromAddress` to `toAddress`.
- * Requires the fromAddress to have sufficient funds and to have approved an allowance of
- * `amountInBaseUnits` to `senderAddress`.
- * @param tokenAddress The hex encoded contract Ethereum address where the ERC20 token is deployed.
- * @param fromAddress The hex encoded user Ethereum address whose funds are being sent.
- * @param toAddress The hex encoded user Ethereum address that will receive the funds.
- * @param senderAddress The hex encoded user Ethereum address whose initiates the fund transfer. The
- * `fromAddress` must have set an allowance to the `senderAddress`
- * before this call.
- * @param amountInBaseUnits The amount (specified in baseUnits) of the token to transfer.
- * @param txOpts Transaction parameters.
- * @return Transaction hash.
- */
- public async transferFromAsync(
- tokenAddress: string,
- fromAddress: string,
- toAddress: string,
- senderAddress: string,
- amountInBaseUnits: BigNumber,
- txOpts: TransactionOpts = {},
- ): Promise<string> {
- assert.isETHAddressHex('tokenAddress', tokenAddress);
- assert.isETHAddressHex('fromAddress', fromAddress);
- assert.isETHAddressHex('toAddress', toAddress);
- await assert.isSenderAddressAsync('senderAddress', senderAddress, this._web3Wrapper);
- assert.isValidBaseUnitAmount('amountInBaseUnits', amountInBaseUnits);
+ const txHash = await tokenContract.transfer.sendTransactionAsync(toAddress, amountInBaseUnits, {
+ from: fromAddress,
+ gas: txOpts.gasLimit,
+ gasPrice: txOpts.gasPrice,
+ });
+ return txHash;
+ }
+ /**
+ * Transfers `amountInBaseUnits` ERC20 tokens from `fromAddress` to `toAddress`.
+ * Requires the fromAddress to have sufficient funds and to have approved an allowance of
+ * `amountInBaseUnits` to `senderAddress`.
+ * @param tokenAddress The hex encoded contract Ethereum address where the ERC20 token is deployed.
+ * @param fromAddress The hex encoded user Ethereum address whose funds are being sent.
+ * @param toAddress The hex encoded user Ethereum address that will receive the funds.
+ * @param senderAddress The hex encoded user Ethereum address whose initiates the fund transfer. The
+ * `fromAddress` must have set an allowance to the `senderAddress`
+ * before this call.
+ * @param amountInBaseUnits The amount (specified in baseUnits) of the token to transfer.
+ * @param txOpts Transaction parameters.
+ * @return Transaction hash.
+ */
+ public async transferFromAsync(
+ tokenAddress: string,
+ fromAddress: string,
+ toAddress: string,
+ senderAddress: string,
+ amountInBaseUnits: BigNumber,
+ txOpts: TransactionOpts = {},
+ ): Promise<string> {
+ assert.isETHAddressHex('tokenAddress', tokenAddress);
+ assert.isETHAddressHex('fromAddress', fromAddress);
+ assert.isETHAddressHex('toAddress', toAddress);
+ await assert.isSenderAddressAsync('senderAddress', senderAddress, this._web3Wrapper);
+ assert.isValidBaseUnitAmount('amountInBaseUnits', amountInBaseUnits);
- const tokenContract = await this._getTokenContractAsync(tokenAddress);
+ const tokenContract = await this._getTokenContractAsync(tokenAddress);
- const fromAddressAllowance = await this.getAllowanceAsync(tokenAddress, fromAddress, senderAddress);
- if (fromAddressAllowance.lessThan(amountInBaseUnits)) {
- throw new Error(ZeroExError.InsufficientAllowanceForTransfer);
- }
+ const fromAddressAllowance = await this.getAllowanceAsync(tokenAddress, fromAddress, senderAddress);
+ if (fromAddressAllowance.lessThan(amountInBaseUnits)) {
+ throw new Error(ZeroExError.InsufficientAllowanceForTransfer);
+ }
- const fromAddressBalance = await this.getBalanceAsync(tokenAddress, fromAddress);
- if (fromAddressBalance.lessThan(amountInBaseUnits)) {
- throw new Error(ZeroExError.InsufficientBalanceForTransfer);
- }
+ const fromAddressBalance = await this.getBalanceAsync(tokenAddress, fromAddress);
+ if (fromAddressBalance.lessThan(amountInBaseUnits)) {
+ throw new Error(ZeroExError.InsufficientBalanceForTransfer);
+ }
- const txHash = await tokenContract.transferFrom.sendTransactionAsync(
- fromAddress,
- toAddress,
- amountInBaseUnits,
- {
- from: senderAddress,
- gas: txOpts.gasLimit,
- gasPrice: txOpts.gasPrice,
- },
- );
- return txHash;
- }
- /**
- * Subscribe to an event type emitted by the Token contract.
- * @param tokenAddress The hex encoded address where the ERC20 token is deployed.
- * @param eventName The token contract event you would like to subscribe to.
- * @param indexFilterValues An object where the keys are indexed args returned by the event and
- * the value is the value you are interested in. E.g `{maker: aUserAddressHex}`
- * @param callback Callback that gets called when a log is added/removed
- * @return Subscription token used later to unsubscribe
- */
- public subscribe<ArgsType extends TokenContractEventArgs>(
- tokenAddress: string,
- eventName: TokenEvents,
- indexFilterValues: IndexedFilterValues,
- callback: EventCallback<ArgsType>,
- ): string {
- assert.isETHAddressHex('tokenAddress', tokenAddress);
- assert.doesBelongToStringEnum('eventName', eventName, TokenEvents);
- assert.doesConformToSchema('indexFilterValues', indexFilterValues, schemas.indexFilterValuesSchema);
- assert.isFunction('callback', callback);
- const subscriptionToken = this._subscribe<ArgsType>(
- tokenAddress,
- eventName,
- indexFilterValues,
- artifacts.TokenArtifact.abi,
- callback,
- );
- return subscriptionToken;
- }
- /**
- * Cancel a subscription
- * @param subscriptionToken Subscription token returned by `subscribe()`
- */
- public unsubscribe(subscriptionToken: string): void {
- this._unsubscribe(subscriptionToken);
- }
- /**
- * Cancels all existing subscriptions
- */
- public unsubscribeAll(): void {
- super.unsubscribeAll();
- }
- /**
- * Gets historical logs without creating a subscription
- * @param tokenAddress An address of the token that emmited the logs.
- * @param eventName The token contract event you would like to subscribe to.
- * @param blockRange Block range to get logs from.
- * @param indexFilterValues An object where the keys are indexed args returned by the event and
- * the value is the value you are interested in. E.g `{_from: aUserAddressHex}`
- * @return Array of logs that match the parameters
- */
- public async getLogsAsync<ArgsType extends TokenContractEventArgs>(
- tokenAddress: string,
- eventName: TokenEvents,
- blockRange: BlockRange,
- indexFilterValues: IndexedFilterValues,
- ): Promise<Array<LogWithDecodedArgs<ArgsType>>> {
- assert.isETHAddressHex('tokenAddress', tokenAddress);
- assert.doesBelongToStringEnum('eventName', eventName, TokenEvents);
- assert.doesConformToSchema('blockRange', blockRange, schemas.blockRangeSchema);
- assert.doesConformToSchema('indexFilterValues', indexFilterValues, schemas.indexFilterValuesSchema);
- const logs = await this._getLogsAsync<ArgsType>(
- tokenAddress,
- eventName,
- blockRange,
- indexFilterValues,
- artifacts.TokenArtifact.abi,
- );
- return logs;
- }
- private _invalidateContractInstances(): void {
- this.unsubscribeAll();
- this._tokenContractsByAddress = {};
- }
- private async _getTokenContractAsync(tokenAddress: string): Promise<TokenContract> {
- let tokenContract = this._tokenContractsByAddress[tokenAddress];
- if (!_.isUndefined(tokenContract)) {
- return tokenContract;
- }
- const web3ContractInstance = await this._instantiateContractIfExistsAsync(
- artifacts.TokenArtifact,
- tokenAddress,
- );
- const contractInstance = new TokenContract(web3ContractInstance, this._web3Wrapper.getContractDefaults());
- tokenContract = contractInstance;
- this._tokenContractsByAddress[tokenAddress] = tokenContract;
- return tokenContract;
- }
+ const txHash = await tokenContract.transferFrom.sendTransactionAsync(
+ fromAddress,
+ toAddress,
+ amountInBaseUnits,
+ {
+ from: senderAddress,
+ gas: txOpts.gasLimit,
+ gasPrice: txOpts.gasPrice,
+ },
+ );
+ return txHash;
+ }
+ /**
+ * Subscribe to an event type emitted by the Token contract.
+ * @param tokenAddress The hex encoded address where the ERC20 token is deployed.
+ * @param eventName The token contract event you would like to subscribe to.
+ * @param indexFilterValues An object where the keys are indexed args returned by the event and
+ * the value is the value you are interested in. E.g `{maker: aUserAddressHex}`
+ * @param callback Callback that gets called when a log is added/removed
+ * @return Subscription token used later to unsubscribe
+ */
+ public subscribe<ArgsType extends TokenContractEventArgs>(
+ tokenAddress: string,
+ eventName: TokenEvents,
+ indexFilterValues: IndexedFilterValues,
+ callback: EventCallback<ArgsType>,
+ ): string {
+ assert.isETHAddressHex('tokenAddress', tokenAddress);
+ assert.doesBelongToStringEnum('eventName', eventName, TokenEvents);
+ assert.doesConformToSchema('indexFilterValues', indexFilterValues, schemas.indexFilterValuesSchema);
+ assert.isFunction('callback', callback);
+ const subscriptionToken = this._subscribe<ArgsType>(
+ tokenAddress,
+ eventName,
+ indexFilterValues,
+ artifacts.TokenArtifact.abi,
+ callback,
+ );
+ return subscriptionToken;
+ }
+ /**
+ * Cancel a subscription
+ * @param subscriptionToken Subscription token returned by `subscribe()`
+ */
+ public unsubscribe(subscriptionToken: string): void {
+ this._unsubscribe(subscriptionToken);
+ }
+ /**
+ * Cancels all existing subscriptions
+ */
+ public unsubscribeAll(): void {
+ super.unsubscribeAll();
+ }
+ /**
+ * Gets historical logs without creating a subscription
+ * @param tokenAddress An address of the token that emmited the logs.
+ * @param eventName The token contract event you would like to subscribe to.
+ * @param blockRange Block range to get logs from.
+ * @param indexFilterValues An object where the keys are indexed args returned by the event and
+ * the value is the value you are interested in. E.g `{_from: aUserAddressHex}`
+ * @return Array of logs that match the parameters
+ */
+ public async getLogsAsync<ArgsType extends TokenContractEventArgs>(
+ tokenAddress: string,
+ eventName: TokenEvents,
+ blockRange: BlockRange,
+ indexFilterValues: IndexedFilterValues,
+ ): Promise<Array<LogWithDecodedArgs<ArgsType>>> {
+ assert.isETHAddressHex('tokenAddress', tokenAddress);
+ assert.doesBelongToStringEnum('eventName', eventName, TokenEvents);
+ assert.doesConformToSchema('blockRange', blockRange, schemas.blockRangeSchema);
+ assert.doesConformToSchema('indexFilterValues', indexFilterValues, schemas.indexFilterValuesSchema);
+ const logs = await this._getLogsAsync<ArgsType>(
+ tokenAddress,
+ eventName,
+ blockRange,
+ indexFilterValues,
+ artifacts.TokenArtifact.abi,
+ );
+ return logs;
+ }
+ private _invalidateContractInstances(): void {
+ this.unsubscribeAll();
+ this._tokenContractsByAddress = {};
+ }
+ private async _getTokenContractAsync(tokenAddress: string): Promise<TokenContract> {
+ let tokenContract = this._tokenContractsByAddress[tokenAddress];
+ if (!_.isUndefined(tokenContract)) {
+ return tokenContract;
+ }
+ const web3ContractInstance = await this._instantiateContractIfExistsAsync(
+ artifacts.TokenArtifact,
+ tokenAddress,
+ );
+ const contractInstance = new TokenContract(web3ContractInstance, this._web3Wrapper.getContractDefaults());
+ tokenContract = contractInstance;
+ this._tokenContractsByAddress[tokenAddress] = tokenContract;
+ return tokenContract;
+ }
}
diff --git a/packages/0x.js/src/globals.d.ts b/packages/0x.js/src/globals.d.ts
index 9a53949b4..4f4932b6e 100644
--- a/packages/0x.js/src/globals.d.ts
+++ b/packages/0x.js/src/globals.d.ts
@@ -10,50 +10,50 @@ declare module 'web3-provider-engine/subproviders/rpc';
// disallow `namespace`, we disable tslint for the following.
/* tslint:disable */
declare namespace Chai {
- interface Assertion {
- bignumber: Assertion;
- // HACK: In order to comply with chai-as-promised we make eventually a `PromisedAssertion` not an `Assertion`
- eventually: PromisedAssertion;
- }
+ interface Assertion {
+ bignumber: Assertion;
+ // HACK: In order to comply with chai-as-promised we make eventually a `PromisedAssertion` not an `Assertion`
+ eventually: PromisedAssertion;
+ }
}
/* tslint:enable */
declare module '*.json' {
- const json: any;
- /* tslint:disable */
- export default json;
- /* tslint:enable */
+ const json: any;
+ /* tslint:disable */
+ export default json;
+ /* tslint:enable */
}
declare module 'ethereumjs-abi' {
- const soliditySHA3: (argTypes: string[], args: any[]) => Buffer;
+ const soliditySHA3: (argTypes: string[], args: any[]) => Buffer;
}
// truffle-hdwallet-provider declarations
declare module 'truffle-hdwallet-provider' {
- import * as Web3 from 'web3';
- class HDWalletProvider implements Web3.Provider {
- constructor(mnemonic: string, rpcUrl: string);
- public sendAsync(
- payload: Web3.JSONRPCRequestPayload,
- callback: (err: Error, result: Web3.JSONRPCResponsePayload) => void,
- ): void;
- }
- export = HDWalletProvider;
+ import * as Web3 from 'web3';
+ class HDWalletProvider implements Web3.Provider {
+ constructor(mnemonic: string, rpcUrl: string);
+ public sendAsync(
+ payload: Web3.JSONRPCRequestPayload,
+ callback: (err: Error, result: Web3.JSONRPCResponsePayload) => void,
+ ): void;
+ }
+ export = HDWalletProvider;
}
// abi-decoder declarations
interface DecodedLogArg {}
interface DecodedLog {
- name: string;
- events: DecodedLogArg[];
+ name: string;
+ events: DecodedLogArg[];
}
declare module 'abi-decoder' {
- import * as Web3 from 'web3';
- const addABI: (abi: Web3.AbiDefinition) => void;
- const decodeLogs: (logs: Web3.LogEntry[]) => DecodedLog[];
+ import * as Web3 from 'web3';
+ const addABI: (abi: Web3.AbiDefinition) => void;
+ const decodeLogs: (logs: Web3.LogEntry[]) => DecodedLog[];
}
declare module 'web3/lib/solidity/coder' {
- const decodeParams: (types: string[], data: string) => any[];
+ const decodeParams: (types: string[], data: string) => any[];
}
diff --git a/packages/0x.js/src/globalsAugment.d.ts b/packages/0x.js/src/globalsAugment.d.ts
index c2f200ab8..3e2f2216b 100644
--- a/packages/0x.js/src/globalsAugment.d.ts
+++ b/packages/0x.js/src/globalsAugment.d.ts
@@ -3,21 +3,21 @@ import { BigNumber } from '@0xproject/utils';
// HACK: This module overrides the Chai namespace so that we can use BigNumber types inside.
// Source: https://github.com/Microsoft/TypeScript/issues/7352#issuecomment-191547232
declare global {
- // HACK: In order to merge the bignumber declaration added by chai-bignumber to the chai Assertion
- // interface we must use `namespace` as the Chai definitelyTyped definition does. Since we otherwise
- // disallow `namespace`, we disable tslint for the following.
- /* tslint:disable */
- namespace Chai {
- interface NumberComparer {
- (value: number | BigNumber, message?: string): Assertion;
- }
- interface NumericComparison {
- greaterThan: NumberComparer;
- }
- }
- /* tslint:enable */
- interface DecodedLogArg {
- name: string;
- value: string | BigNumber;
- }
+ // HACK: In order to merge the bignumber declaration added by chai-bignumber to the chai Assertion
+ // interface we must use `namespace` as the Chai definitelyTyped definition does. Since we otherwise
+ // disallow `namespace`, we disable tslint for the following.
+ /* tslint:disable */
+ namespace Chai {
+ interface NumberComparer {
+ (value: number | BigNumber, message?: string): Assertion;
+ }
+ interface NumericComparison {
+ greaterThan: NumberComparer;
+ }
+ }
+ /* tslint:enable */
+ interface DecodedLogArg {
+ name: string;
+ value: string | BigNumber;
+ }
}
diff --git a/packages/0x.js/src/index.ts b/packages/0x.js/src/index.ts
index ad5ef8534..599c3a2b0 100644
--- a/packages/0x.js/src/index.ts
+++ b/packages/0x.js/src/index.ts
@@ -1,50 +1,50 @@
export { ZeroEx } from './0x';
export {
- Order,
- BlockParamLiteral,
- SignedOrder,
- ECSignature,
- ZeroExError,
- EventCallback,
- ExchangeContractErrs,
- ContractEvent,
- Token,
- ExchangeEvents,
- TokenEvents,
- IndexedFilterValues,
- BlockRange,
- BlockParam,
- OrderCancellationRequest,
- OrderFillRequest,
- LogErrorContractEventArgs,
- LogCancelContractEventArgs,
- LogFillContractEventArgs,
- ExchangeContractEventArgs,
- TransferContractEventArgs,
- ApprovalContractEventArgs,
- TokenContractEventArgs,
- EtherTokenContractEventArgs,
- WithdrawalContractEventArgs,
- DepositContractEventArgs,
- ContractEventArgs,
- ContractEventArg,
- Web3Provider,
- ZeroExConfig,
- EtherTokenEvents,
- TransactionReceiptWithDecodedLogs,
- LogWithDecodedArgs,
- MethodOpts,
- OrderTransactionOpts,
- TransactionOpts,
- FilterObject,
- LogEvent,
- DecodedLogEvent,
- EventWatcherCallback,
- OnOrderStateChangeCallback,
- OrderStateValid,
- OrderStateInvalid,
- OrderState,
+ Order,
+ BlockParamLiteral,
+ SignedOrder,
+ ECSignature,
+ ZeroExError,
+ EventCallback,
+ ExchangeContractErrs,
+ ContractEvent,
+ Token,
+ ExchangeEvents,
+ TokenEvents,
+ IndexedFilterValues,
+ BlockRange,
+ BlockParam,
+ OrderCancellationRequest,
+ OrderFillRequest,
+ LogErrorContractEventArgs,
+ LogCancelContractEventArgs,
+ LogFillContractEventArgs,
+ ExchangeContractEventArgs,
+ TransferContractEventArgs,
+ ApprovalContractEventArgs,
+ TokenContractEventArgs,
+ EtherTokenContractEventArgs,
+ WithdrawalContractEventArgs,
+ DepositContractEventArgs,
+ ContractEventArgs,
+ ContractEventArg,
+ Web3Provider,
+ ZeroExConfig,
+ EtherTokenEvents,
+ TransactionReceiptWithDecodedLogs,
+ LogWithDecodedArgs,
+ MethodOpts,
+ OrderTransactionOpts,
+ TransactionOpts,
+ FilterObject,
+ LogEvent,
+ DecodedLogEvent,
+ EventWatcherCallback,
+ OnOrderStateChangeCallback,
+ OrderStateValid,
+ OrderStateInvalid,
+ OrderState,
} from './types';
export { TransactionReceipt } from '@0xproject/types';
diff --git a/packages/0x.js/src/order_watcher/event_watcher.ts b/packages/0x.js/src/order_watcher/event_watcher.ts
index 3e3cd978d..5d05bfb60 100644
--- a/packages/0x.js/src/order_watcher/event_watcher.ts
+++ b/packages/0x.js/src/order_watcher/event_watcher.ts
@@ -9,8 +9,8 @@ import { assert } from '../utils/assert';
const DEFAULT_EVENT_POLLING_INTERVAL_MS = 200;
enum LogEventState {
- Removed,
- Added,
+ Removed,
+ Added,
}
/*
@@ -18,76 +18,76 @@ enum LogEventState {
* depth.
*/
export class EventWatcher {
- private _web3Wrapper: Web3Wrapper;
- private _pollingIntervalMs: number;
- private _intervalIdIfExists?: NodeJS.Timer;
- private _lastEvents: Web3.LogEntry[] = [];
- constructor(web3Wrapper: Web3Wrapper, pollingIntervalIfExistsMs: undefined | number) {
- this._web3Wrapper = web3Wrapper;
- this._pollingIntervalMs = _.isUndefined(pollingIntervalIfExistsMs)
- ? DEFAULT_EVENT_POLLING_INTERVAL_MS
- : pollingIntervalIfExistsMs;
- }
- public subscribe(callback: EventWatcherCallback): void {
- assert.isFunction('callback', callback);
- if (!_.isUndefined(this._intervalIdIfExists)) {
- throw new Error(ZeroExError.SubscriptionAlreadyPresent);
- }
- this._intervalIdIfExists = intervalUtils.setAsyncExcludingInterval(
- this._pollForBlockchainEventsAsync.bind(this, callback),
- this._pollingIntervalMs,
- (err: Error) => {
- this.unsubscribe();
- callback(err);
- },
- );
- }
- public unsubscribe(): void {
- this._lastEvents = [];
- if (!_.isUndefined(this._intervalIdIfExists)) {
- intervalUtils.clearAsyncExcludingInterval(this._intervalIdIfExists);
- delete this._intervalIdIfExists;
- }
- }
- private async _pollForBlockchainEventsAsync(callback: EventWatcherCallback): Promise<void> {
- const pendingEvents = await this._getEventsAsync();
- if (_.isUndefined(pendingEvents)) {
- // HACK: This should never happen, but happens frequently on CI due to a ganache bug
- return;
- }
- if (pendingEvents.length === 0) {
- // HACK: Sometimes when node rebuilds the pending block we get back the empty result.
- // We don't want to emit a lot of removal events and bring them back after a couple of miliseconds,
- // that's why we just ignore those cases.
- return;
- }
- const removedEvents = _.differenceBy(this._lastEvents, pendingEvents, JSON.stringify);
- const newEvents = _.differenceBy(pendingEvents, this._lastEvents, JSON.stringify);
- await this._emitDifferencesAsync(removedEvents, LogEventState.Removed, callback);
- await this._emitDifferencesAsync(newEvents, LogEventState.Added, callback);
- this._lastEvents = pendingEvents;
- }
- private async _getEventsAsync(): Promise<Web3.LogEntry[]> {
- const eventFilter = {
- fromBlock: BlockParamLiteral.Pending,
- toBlock: BlockParamLiteral.Pending,
- };
- const events = await this._web3Wrapper.getLogsAsync(eventFilter);
- return events;
- }
- private async _emitDifferencesAsync(
- logs: Web3.LogEntry[],
- logEventState: LogEventState,
- callback: EventWatcherCallback,
- ): Promise<void> {
- for (const log of logs) {
- const logEvent = {
- removed: logEventState === LogEventState.Removed,
- ...log,
- };
- if (!_.isUndefined(this._intervalIdIfExists)) {
- callback(null, logEvent);
- }
- }
- }
+ private _web3Wrapper: Web3Wrapper;
+ private _pollingIntervalMs: number;
+ private _intervalIdIfExists?: NodeJS.Timer;
+ private _lastEvents: Web3.LogEntry[] = [];
+ constructor(web3Wrapper: Web3Wrapper, pollingIntervalIfExistsMs: undefined | number) {
+ this._web3Wrapper = web3Wrapper;
+ this._pollingIntervalMs = _.isUndefined(pollingIntervalIfExistsMs)
+ ? DEFAULT_EVENT_POLLING_INTERVAL_MS
+ : pollingIntervalIfExistsMs;
+ }
+ public subscribe(callback: EventWatcherCallback): void {
+ assert.isFunction('callback', callback);
+ if (!_.isUndefined(this._intervalIdIfExists)) {
+ throw new Error(ZeroExError.SubscriptionAlreadyPresent);
+ }
+ this._intervalIdIfExists = intervalUtils.setAsyncExcludingInterval(
+ this._pollForBlockchainEventsAsync.bind(this, callback),
+ this._pollingIntervalMs,
+ (err: Error) => {
+ this.unsubscribe();
+ callback(err);
+ },
+ );
+ }
+ public unsubscribe(): void {
+ this._lastEvents = [];
+ if (!_.isUndefined(this._intervalIdIfExists)) {
+ intervalUtils.clearAsyncExcludingInterval(this._intervalIdIfExists);
+ delete this._intervalIdIfExists;
+ }
+ }
+ private async _pollForBlockchainEventsAsync(callback: EventWatcherCallback): Promise<void> {
+ const pendingEvents = await this._getEventsAsync();
+ if (_.isUndefined(pendingEvents)) {
+ // HACK: This should never happen, but happens frequently on CI due to a ganache bug
+ return;
+ }
+ if (pendingEvents.length === 0) {
+ // HACK: Sometimes when node rebuilds the pending block we get back the empty result.
+ // We don't want to emit a lot of removal events and bring them back after a couple of miliseconds,
+ // that's why we just ignore those cases.
+ return;
+ }
+ const removedEvents = _.differenceBy(this._lastEvents, pendingEvents, JSON.stringify);
+ const newEvents = _.differenceBy(pendingEvents, this._lastEvents, JSON.stringify);
+ await this._emitDifferencesAsync(removedEvents, LogEventState.Removed, callback);
+ await this._emitDifferencesAsync(newEvents, LogEventState.Added, callback);
+ this._lastEvents = pendingEvents;
+ }
+ private async _getEventsAsync(): Promise<Web3.LogEntry[]> {
+ const eventFilter = {
+ fromBlock: BlockParamLiteral.Pending,
+ toBlock: BlockParamLiteral.Pending,
+ };
+ const events = await this._web3Wrapper.getLogsAsync(eventFilter);
+ return events;
+ }
+ private async _emitDifferencesAsync(
+ logs: Web3.LogEntry[],
+ logEventState: LogEventState,
+ callback: EventWatcherCallback,
+ ): Promise<void> {
+ for (const log of logs) {
+ const logEvent = {
+ removed: logEventState === LogEventState.Removed,
+ ...log,
+ };
+ if (!_.isUndefined(this._intervalIdIfExists)) {
+ callback(null, logEvent);
+ }
+ }
+ }
}
diff --git a/packages/0x.js/src/order_watcher/expiration_watcher.ts b/packages/0x.js/src/order_watcher/expiration_watcher.ts
index a08de94c0..00b62162d 100644
--- a/packages/0x.js/src/order_watcher/expiration_watcher.ts
+++ b/packages/0x.js/src/order_watcher/expiration_watcher.ts
@@ -13,63 +13,63 @@ const DEFAULT_ORDER_EXPIRATION_CHECKING_INTERVAL_MS = 50;
* It stores them in a min heap by expiration time and checks for expired ones every `orderExpirationCheckingIntervalMs`
*/
export class ExpirationWatcher {
- private _orderHashByExpirationRBTree: RBTree<string>;
- private _expiration: { [orderHash: string]: BigNumber } = {};
- private _orderExpirationCheckingIntervalMs: number;
- private _expirationMarginMs: number;
- private _orderExpirationCheckingIntervalIdIfExists?: NodeJS.Timer;
- constructor(expirationMarginIfExistsMs?: number, orderExpirationCheckingIntervalIfExistsMs?: number) {
- this._expirationMarginMs = expirationMarginIfExistsMs || DEFAULT_EXPIRATION_MARGIN_MS;
- this._orderExpirationCheckingIntervalMs =
- expirationMarginIfExistsMs || DEFAULT_ORDER_EXPIRATION_CHECKING_INTERVAL_MS;
- const scoreFunction = (orderHash: string) => this._expiration[orderHash].toNumber();
- const comparator = (lhs: string, rhs: string) => scoreFunction(lhs) - scoreFunction(rhs);
- this._orderHashByExpirationRBTree = new RBTree(comparator);
- }
- public subscribe(callback: (orderHash: string) => void): void {
- if (!_.isUndefined(this._orderExpirationCheckingIntervalIdIfExists)) {
- throw new Error(ZeroExError.SubscriptionAlreadyPresent);
- }
- this._orderExpirationCheckingIntervalIdIfExists = intervalUtils.setInterval(
- this._pruneExpiredOrders.bind(this, callback),
- this._orderExpirationCheckingIntervalMs,
- _.noop, // _pruneExpiredOrders never throws
- );
- }
- public unsubscribe(): void {
- if (_.isUndefined(this._orderExpirationCheckingIntervalIdIfExists)) {
- throw new Error(ZeroExError.SubscriptionNotFound);
- }
- intervalUtils.clearInterval(this._orderExpirationCheckingIntervalIdIfExists);
- delete this._orderExpirationCheckingIntervalIdIfExists;
- }
- public addOrder(orderHash: string, expirationUnixTimestampMs: BigNumber): void {
- this._expiration[orderHash] = expirationUnixTimestampMs;
- this._orderHashByExpirationRBTree.insert(orderHash);
- }
- public removeOrder(orderHash: string): void {
- this._orderHashByExpirationRBTree.remove(orderHash);
- delete this._expiration[orderHash];
- }
- private _pruneExpiredOrders(callback: (orderHash: string) => void): void {
- const currentUnixTimestampMs = utils.getCurrentUnixTimestampMs();
- while (true) {
- const hasTrakedOrders = this._orderHashByExpirationRBTree.size === 0;
- if (hasTrakedOrders) {
- break;
- }
- const nextOrderHashToExpire = this._orderHashByExpirationRBTree.min();
- const hasNoExpiredOrders = this._expiration[nextOrderHashToExpire].greaterThan(
- currentUnixTimestampMs.plus(this._expirationMarginMs),
- );
- const isSubscriptionActive = _.isUndefined(this._orderExpirationCheckingIntervalIdIfExists);
- if (hasNoExpiredOrders || isSubscriptionActive) {
- break;
- }
- const orderHash = this._orderHashByExpirationRBTree.min();
- this._orderHashByExpirationRBTree.remove(orderHash);
- delete this._expiration[orderHash];
- callback(orderHash);
- }
- }
+ private _orderHashByExpirationRBTree: RBTree<string>;
+ private _expiration: { [orderHash: string]: BigNumber } = {};
+ private _orderExpirationCheckingIntervalMs: number;
+ private _expirationMarginMs: number;
+ private _orderExpirationCheckingIntervalIdIfExists?: NodeJS.Timer;
+ constructor(expirationMarginIfExistsMs?: number, orderExpirationCheckingIntervalIfExistsMs?: number) {
+ this._expirationMarginMs = expirationMarginIfExistsMs || DEFAULT_EXPIRATION_MARGIN_MS;
+ this._orderExpirationCheckingIntervalMs =
+ expirationMarginIfExistsMs || DEFAULT_ORDER_EXPIRATION_CHECKING_INTERVAL_MS;
+ const scoreFunction = (orderHash: string) => this._expiration[orderHash].toNumber();
+ const comparator = (lhs: string, rhs: string) => scoreFunction(lhs) - scoreFunction(rhs);
+ this._orderHashByExpirationRBTree = new RBTree(comparator);
+ }
+ public subscribe(callback: (orderHash: string) => void): void {
+ if (!_.isUndefined(this._orderExpirationCheckingIntervalIdIfExists)) {
+ throw new Error(ZeroExError.SubscriptionAlreadyPresent);
+ }
+ this._orderExpirationCheckingIntervalIdIfExists = intervalUtils.setInterval(
+ this._pruneExpiredOrders.bind(this, callback),
+ this._orderExpirationCheckingIntervalMs,
+ _.noop, // _pruneExpiredOrders never throws
+ );
+ }
+ public unsubscribe(): void {
+ if (_.isUndefined(this._orderExpirationCheckingIntervalIdIfExists)) {
+ throw new Error(ZeroExError.SubscriptionNotFound);
+ }
+ intervalUtils.clearInterval(this._orderExpirationCheckingIntervalIdIfExists);
+ delete this._orderExpirationCheckingIntervalIdIfExists;
+ }
+ public addOrder(orderHash: string, expirationUnixTimestampMs: BigNumber): void {
+ this._expiration[orderHash] = expirationUnixTimestampMs;
+ this._orderHashByExpirationRBTree.insert(orderHash);
+ }
+ public removeOrder(orderHash: string): void {
+ this._orderHashByExpirationRBTree.remove(orderHash);
+ delete this._expiration[orderHash];
+ }
+ private _pruneExpiredOrders(callback: (orderHash: string) => void): void {
+ const currentUnixTimestampMs = utils.getCurrentUnixTimestampMs();
+ while (true) {
+ const hasTrakedOrders = this._orderHashByExpirationRBTree.size === 0;
+ if (hasTrakedOrders) {
+ break;
+ }
+ const nextOrderHashToExpire = this._orderHashByExpirationRBTree.min();
+ const hasNoExpiredOrders = this._expiration[nextOrderHashToExpire].greaterThan(
+ currentUnixTimestampMs.plus(this._expirationMarginMs),
+ );
+ const isSubscriptionActive = _.isUndefined(this._orderExpirationCheckingIntervalIdIfExists);
+ if (hasNoExpiredOrders || isSubscriptionActive) {
+ break;
+ }
+ const orderHash = this._orderHashByExpirationRBTree.min();
+ this._orderHashByExpirationRBTree.remove(orderHash);
+ delete this._expiration[orderHash];
+ callback(orderHash);
+ }
+ }
}
diff --git a/packages/0x.js/src/order_watcher/order_state_watcher.ts b/packages/0x.js/src/order_watcher/order_state_watcher.ts
index 2c5da6b57..12ac60960 100644
--- a/packages/0x.js/src/order_watcher/order_state_watcher.ts
+++ b/packages/0x.js/src/order_watcher/order_state_watcher.ts
@@ -9,25 +9,25 @@ import { TokenWrapper } from '../contract_wrappers/token_wrapper';
import { BalanceAndProxyAllowanceLazyStore } from '../stores/balance_proxy_allowance_lazy_store';
import { OrderFilledCancelledLazyStore } from '../stores/order_filled_cancelled_lazy_store';
import {
- ApprovalContractEventArgs,
- BlockParamLiteral,
- ContractEventArgs,
- DepositContractEventArgs,
- EtherTokenEvents,
- ExchangeContractErrs,
- ExchangeEvents,
- LogCancelContractEventArgs,
- LogEvent,
- LogFillContractEventArgs,
- LogWithDecodedArgs,
- OnOrderStateChangeCallback,
- OrderState,
- OrderStateWatcherConfig,
- SignedOrder,
- TokenEvents,
- TransferContractEventArgs,
- WithdrawalContractEventArgs,
- ZeroExError,
+ ApprovalContractEventArgs,
+ BlockParamLiteral,
+ ContractEventArgs,
+ DepositContractEventArgs,
+ EtherTokenEvents,
+ ExchangeContractErrs,
+ ExchangeEvents,
+ LogCancelContractEventArgs,
+ LogEvent,
+ LogFillContractEventArgs,
+ LogWithDecodedArgs,
+ OnOrderStateChangeCallback,
+ OrderState,
+ OrderStateWatcherConfig,
+ SignedOrder,
+ TokenEvents,
+ TransferContractEventArgs,
+ WithdrawalContractEventArgs,
+ ZeroExError,
} from '../types';
import { AbiDecoder } from '../utils/abi_decoder';
import { assert } from '../utils/assert';
@@ -38,17 +38,17 @@ import { EventWatcher } from './event_watcher';
import { ExpirationWatcher } from './expiration_watcher';
interface DependentOrderHashes {
- [makerAddress: string]: {
- [makerToken: string]: Set<string>;
- };
+ [makerAddress: string]: {
+ [makerToken: string]: Set<string>;
+ };
}
interface OrderByOrderHash {
- [orderHash: string]: SignedOrder;
+ [orderHash: string]: SignedOrder;
}
interface OrderStateByOrderHash {
- [orderHash: string]: OrderState;
+ [orderHash: string]: OrderState;
}
const DEFAULT_CLEANUP_JOB_INTERVAL_MS = 1000 * 60 * 60; // 1h
@@ -60,319 +60,319 @@ const DEFAULT_CLEANUP_JOB_INTERVAL_MS = 1000 * 60 * 60; // 1h
* the order should be deemed invalid.
*/
export class OrderStateWatcher {
- private _orderStateByOrderHashCache: OrderStateByOrderHash = {};
- private _orderByOrderHash: OrderByOrderHash = {};
- private _dependentOrderHashes: DependentOrderHashes = {};
- private _callbackIfExists?: OnOrderStateChangeCallback;
- private _eventWatcher: EventWatcher;
- private _web3Wrapper: Web3Wrapper;
- private _abiDecoder: AbiDecoder;
- private _expirationWatcher: ExpirationWatcher;
- private _orderStateUtils: OrderStateUtils;
- private _orderFilledCancelledLazyStore: OrderFilledCancelledLazyStore;
- private _balanceAndProxyAllowanceLazyStore: BalanceAndProxyAllowanceLazyStore;
- private _cleanupJobInterval: number;
- private _cleanupJobIntervalIdIfExists?: NodeJS.Timer;
- constructor(
- web3Wrapper: Web3Wrapper,
- abiDecoder: AbiDecoder,
- token: TokenWrapper,
- exchange: ExchangeWrapper,
- config?: OrderStateWatcherConfig,
- ) {
- this._abiDecoder = abiDecoder;
- this._web3Wrapper = web3Wrapper;
- const pollingIntervalIfExistsMs = _.isUndefined(config) ? undefined : config.eventPollingIntervalMs;
- this._eventWatcher = new EventWatcher(web3Wrapper, pollingIntervalIfExistsMs);
- this._balanceAndProxyAllowanceLazyStore = new BalanceAndProxyAllowanceLazyStore(
- token,
- BlockParamLiteral.Pending,
- );
- this._orderFilledCancelledLazyStore = new OrderFilledCancelledLazyStore(exchange);
- this._orderStateUtils = new OrderStateUtils(
- this._balanceAndProxyAllowanceLazyStore,
- this._orderFilledCancelledLazyStore,
- );
- const orderExpirationCheckingIntervalMsIfExists = _.isUndefined(config)
- ? undefined
- : config.orderExpirationCheckingIntervalMs;
- const expirationMarginIfExistsMs = _.isUndefined(config) ? undefined : config.expirationMarginMs;
- this._expirationWatcher = new ExpirationWatcher(
- expirationMarginIfExistsMs,
- orderExpirationCheckingIntervalMsIfExists,
- );
- this._cleanupJobInterval =
- _.isUndefined(config) || _.isUndefined(config.cleanupJobIntervalMs)
- ? DEFAULT_CLEANUP_JOB_INTERVAL_MS
- : config.cleanupJobIntervalMs;
- }
- /**
- * Add an order to the orderStateWatcher. Before the order is added, it's
- * signature is verified.
- * @param signedOrder The order you wish to start watching.
- */
- public addOrder(signedOrder: SignedOrder): void {
- assert.doesConformToSchema('signedOrder', signedOrder, schemas.signedOrderSchema);
- const orderHash = ZeroEx.getOrderHashHex(signedOrder);
- assert.isValidSignature(orderHash, signedOrder.ecSignature, signedOrder.maker);
- this._orderByOrderHash[orderHash] = signedOrder;
- this._addToDependentOrderHashes(signedOrder, orderHash);
- const expirationUnixTimestampMs = signedOrder.expirationUnixTimestampSec.times(1000);
- this._expirationWatcher.addOrder(orderHash, expirationUnixTimestampMs);
- }
- /**
- * Removes an order from the orderStateWatcher
- * @param orderHash The orderHash of the order you wish to stop watching.
- */
- public removeOrder(orderHash: string): void {
- assert.doesConformToSchema('orderHash', orderHash, schemas.orderHashSchema);
- const signedOrder = this._orderByOrderHash[orderHash];
- if (_.isUndefined(signedOrder)) {
- return; // noop
- }
- delete this._orderByOrderHash[orderHash];
- delete this._orderStateByOrderHashCache[orderHash];
- const exchange = (this._orderFilledCancelledLazyStore as any)._exchange as ExchangeWrapper;
- const zrxTokenAddress = exchange.getZRXTokenAddress();
- this._removeFromDependentOrderHashes(signedOrder.maker, zrxTokenAddress, orderHash);
- this._removeFromDependentOrderHashes(signedOrder.maker, signedOrder.makerTokenAddress, orderHash);
- this._expirationWatcher.removeOrder(orderHash);
- }
- /**
- * Starts an orderStateWatcher subscription. The callback will be called every time a watched order's
- * backing blockchain state has changed. This is a call-to-action for the caller to re-validate the order.
- * @param callback Receives the orderHash of the order that should be re-validated, together
- * with all the order-relevant blockchain state needed to re-validate the order.
- */
- public subscribe(callback: OnOrderStateChangeCallback): void {
- assert.isFunction('callback', callback);
- if (!_.isUndefined(this._callbackIfExists)) {
- throw new Error(ZeroExError.SubscriptionAlreadyPresent);
- }
- this._callbackIfExists = callback;
- this._eventWatcher.subscribe(this._onEventWatcherCallbackAsync.bind(this));
- this._expirationWatcher.subscribe(this._onOrderExpired.bind(this));
- this._cleanupJobIntervalIdIfExists = intervalUtils.setAsyncExcludingInterval(
- this._cleanupAsync.bind(this),
- this._cleanupJobInterval,
- (err: Error) => {
- this.unsubscribe();
- callback(err);
- },
- );
- }
- /**
- * Ends an orderStateWatcher subscription.
- */
- public unsubscribe(): void {
- if (_.isUndefined(this._callbackIfExists) || _.isUndefined(this._cleanupJobIntervalIdIfExists)) {
- throw new Error(ZeroExError.SubscriptionNotFound);
- }
- this._balanceAndProxyAllowanceLazyStore.deleteAll();
- this._orderFilledCancelledLazyStore.deleteAll();
- delete this._callbackIfExists;
- this._eventWatcher.unsubscribe();
- this._expirationWatcher.unsubscribe();
- intervalUtils.clearAsyncExcludingInterval(this._cleanupJobIntervalIdIfExists);
- }
- private async _cleanupAsync(): Promise<void> {
- for (const orderHash of _.keys(this._orderByOrderHash)) {
- this._cleanupOrderRelatedState(orderHash);
- await this._emitRevalidateOrdersAsync([orderHash]);
- }
- }
- private _cleanupOrderRelatedState(orderHash: string): void {
- const signedOrder = this._orderByOrderHash[orderHash];
+ private _orderStateByOrderHashCache: OrderStateByOrderHash = {};
+ private _orderByOrderHash: OrderByOrderHash = {};
+ private _dependentOrderHashes: DependentOrderHashes = {};
+ private _callbackIfExists?: OnOrderStateChangeCallback;
+ private _eventWatcher: EventWatcher;
+ private _web3Wrapper: Web3Wrapper;
+ private _abiDecoder: AbiDecoder;
+ private _expirationWatcher: ExpirationWatcher;
+ private _orderStateUtils: OrderStateUtils;
+ private _orderFilledCancelledLazyStore: OrderFilledCancelledLazyStore;
+ private _balanceAndProxyAllowanceLazyStore: BalanceAndProxyAllowanceLazyStore;
+ private _cleanupJobInterval: number;
+ private _cleanupJobIntervalIdIfExists?: NodeJS.Timer;
+ constructor(
+ web3Wrapper: Web3Wrapper,
+ abiDecoder: AbiDecoder,
+ token: TokenWrapper,
+ exchange: ExchangeWrapper,
+ config?: OrderStateWatcherConfig,
+ ) {
+ this._abiDecoder = abiDecoder;
+ this._web3Wrapper = web3Wrapper;
+ const pollingIntervalIfExistsMs = _.isUndefined(config) ? undefined : config.eventPollingIntervalMs;
+ this._eventWatcher = new EventWatcher(web3Wrapper, pollingIntervalIfExistsMs);
+ this._balanceAndProxyAllowanceLazyStore = new BalanceAndProxyAllowanceLazyStore(
+ token,
+ BlockParamLiteral.Pending,
+ );
+ this._orderFilledCancelledLazyStore = new OrderFilledCancelledLazyStore(exchange);
+ this._orderStateUtils = new OrderStateUtils(
+ this._balanceAndProxyAllowanceLazyStore,
+ this._orderFilledCancelledLazyStore,
+ );
+ const orderExpirationCheckingIntervalMsIfExists = _.isUndefined(config)
+ ? undefined
+ : config.orderExpirationCheckingIntervalMs;
+ const expirationMarginIfExistsMs = _.isUndefined(config) ? undefined : config.expirationMarginMs;
+ this._expirationWatcher = new ExpirationWatcher(
+ expirationMarginIfExistsMs,
+ orderExpirationCheckingIntervalMsIfExists,
+ );
+ this._cleanupJobInterval =
+ _.isUndefined(config) || _.isUndefined(config.cleanupJobIntervalMs)
+ ? DEFAULT_CLEANUP_JOB_INTERVAL_MS
+ : config.cleanupJobIntervalMs;
+ }
+ /**
+ * Add an order to the orderStateWatcher. Before the order is added, it's
+ * signature is verified.
+ * @param signedOrder The order you wish to start watching.
+ */
+ public addOrder(signedOrder: SignedOrder): void {
+ assert.doesConformToSchema('signedOrder', signedOrder, schemas.signedOrderSchema);
+ const orderHash = ZeroEx.getOrderHashHex(signedOrder);
+ assert.isValidSignature(orderHash, signedOrder.ecSignature, signedOrder.maker);
+ this._orderByOrderHash[orderHash] = signedOrder;
+ this._addToDependentOrderHashes(signedOrder, orderHash);
+ const expirationUnixTimestampMs = signedOrder.expirationUnixTimestampSec.times(1000);
+ this._expirationWatcher.addOrder(orderHash, expirationUnixTimestampMs);
+ }
+ /**
+ * Removes an order from the orderStateWatcher
+ * @param orderHash The orderHash of the order you wish to stop watching.
+ */
+ public removeOrder(orderHash: string): void {
+ assert.doesConformToSchema('orderHash', orderHash, schemas.orderHashSchema);
+ const signedOrder = this._orderByOrderHash[orderHash];
+ if (_.isUndefined(signedOrder)) {
+ return; // noop
+ }
+ delete this._orderByOrderHash[orderHash];
+ delete this._orderStateByOrderHashCache[orderHash];
+ const exchange = (this._orderFilledCancelledLazyStore as any)._exchange as ExchangeWrapper;
+ const zrxTokenAddress = exchange.getZRXTokenAddress();
+ this._removeFromDependentOrderHashes(signedOrder.maker, zrxTokenAddress, orderHash);
+ this._removeFromDependentOrderHashes(signedOrder.maker, signedOrder.makerTokenAddress, orderHash);
+ this._expirationWatcher.removeOrder(orderHash);
+ }
+ /**
+ * Starts an orderStateWatcher subscription. The callback will be called every time a watched order's
+ * backing blockchain state has changed. This is a call-to-action for the caller to re-validate the order.
+ * @param callback Receives the orderHash of the order that should be re-validated, together
+ * with all the order-relevant blockchain state needed to re-validate the order.
+ */
+ public subscribe(callback: OnOrderStateChangeCallback): void {
+ assert.isFunction('callback', callback);
+ if (!_.isUndefined(this._callbackIfExists)) {
+ throw new Error(ZeroExError.SubscriptionAlreadyPresent);
+ }
+ this._callbackIfExists = callback;
+ this._eventWatcher.subscribe(this._onEventWatcherCallbackAsync.bind(this));
+ this._expirationWatcher.subscribe(this._onOrderExpired.bind(this));
+ this._cleanupJobIntervalIdIfExists = intervalUtils.setAsyncExcludingInterval(
+ this._cleanupAsync.bind(this),
+ this._cleanupJobInterval,
+ (err: Error) => {
+ this.unsubscribe();
+ callback(err);
+ },
+ );
+ }
+ /**
+ * Ends an orderStateWatcher subscription.
+ */
+ public unsubscribe(): void {
+ if (_.isUndefined(this._callbackIfExists) || _.isUndefined(this._cleanupJobIntervalIdIfExists)) {
+ throw new Error(ZeroExError.SubscriptionNotFound);
+ }
+ this._balanceAndProxyAllowanceLazyStore.deleteAll();
+ this._orderFilledCancelledLazyStore.deleteAll();
+ delete this._callbackIfExists;
+ this._eventWatcher.unsubscribe();
+ this._expirationWatcher.unsubscribe();
+ intervalUtils.clearAsyncExcludingInterval(this._cleanupJobIntervalIdIfExists);
+ }
+ private async _cleanupAsync(): Promise<void> {
+ for (const orderHash of _.keys(this._orderByOrderHash)) {
+ this._cleanupOrderRelatedState(orderHash);
+ await this._emitRevalidateOrdersAsync([orderHash]);
+ }
+ }
+ private _cleanupOrderRelatedState(orderHash: string): void {
+ const signedOrder = this._orderByOrderHash[orderHash];
- this._orderFilledCancelledLazyStore.deleteFilledTakerAmount(orderHash);
- this._orderFilledCancelledLazyStore.deleteCancelledTakerAmount(orderHash);
+ this._orderFilledCancelledLazyStore.deleteFilledTakerAmount(orderHash);
+ this._orderFilledCancelledLazyStore.deleteCancelledTakerAmount(orderHash);
- this._balanceAndProxyAllowanceLazyStore.deleteBalance(signedOrder.makerTokenAddress, signedOrder.maker);
- this._balanceAndProxyAllowanceLazyStore.deleteProxyAllowance(signedOrder.makerTokenAddress, signedOrder.maker);
- this._balanceAndProxyAllowanceLazyStore.deleteBalance(signedOrder.takerTokenAddress, signedOrder.taker);
- this._balanceAndProxyAllowanceLazyStore.deleteProxyAllowance(signedOrder.takerTokenAddress, signedOrder.taker);
+ this._balanceAndProxyAllowanceLazyStore.deleteBalance(signedOrder.makerTokenAddress, signedOrder.maker);
+ this._balanceAndProxyAllowanceLazyStore.deleteProxyAllowance(signedOrder.makerTokenAddress, signedOrder.maker);
+ this._balanceAndProxyAllowanceLazyStore.deleteBalance(signedOrder.takerTokenAddress, signedOrder.taker);
+ this._balanceAndProxyAllowanceLazyStore.deleteProxyAllowance(signedOrder.takerTokenAddress, signedOrder.taker);
- const zrxTokenAddress = this._getZRXTokenAddress();
- if (!signedOrder.makerFee.isZero()) {
- this._balanceAndProxyAllowanceLazyStore.deleteBalance(zrxTokenAddress, signedOrder.maker);
- this._balanceAndProxyAllowanceLazyStore.deleteProxyAllowance(zrxTokenAddress, signedOrder.maker);
- }
- if (!signedOrder.takerFee.isZero()) {
- this._balanceAndProxyAllowanceLazyStore.deleteBalance(zrxTokenAddress, signedOrder.taker);
- this._balanceAndProxyAllowanceLazyStore.deleteProxyAllowance(zrxTokenAddress, signedOrder.taker);
- }
- }
- private _onOrderExpired(orderHash: string): void {
- const orderState: OrderState = {
- isValid: false,
- orderHash,
- error: ExchangeContractErrs.OrderFillExpired,
- };
- if (!_.isUndefined(this._orderByOrderHash[orderHash])) {
- this.removeOrder(orderHash);
- if (!_.isUndefined(this._callbackIfExists)) {
- this._callbackIfExists(null, orderState);
- }
- }
- }
- private async _onEventWatcherCallbackAsync(err: Error | null, logIfExists?: LogEvent): Promise<void> {
- if (!_.isNull(err)) {
- if (!_.isUndefined(this._callbackIfExists)) {
- this._callbackIfExists(err);
- this.unsubscribe();
- }
- return;
- }
- const log = logIfExists as LogEvent; // At this moment we are sure that no error occured and log is defined.
- const maybeDecodedLog = this._abiDecoder.tryToDecodeLogOrNoop(log);
- const isLogDecoded = !_.isUndefined((maybeDecodedLog as LogWithDecodedArgs<any>).event);
- if (!isLogDecoded) {
- return; // noop
- }
- const decodedLog = maybeDecodedLog as LogWithDecodedArgs<ContractEventArgs>;
- let makerToken: string;
- let makerAddress: string;
- switch (decodedLog.event) {
- case TokenEvents.Approval: {
- // Invalidate cache
- const args = decodedLog.args as ApprovalContractEventArgs;
- this._balanceAndProxyAllowanceLazyStore.deleteProxyAllowance(decodedLog.address, args._owner);
- // Revalidate orders
- makerToken = decodedLog.address;
- makerAddress = args._owner;
- if (
- !_.isUndefined(this._dependentOrderHashes[makerAddress]) &&
- !_.isUndefined(this._dependentOrderHashes[makerAddress][makerToken])
- ) {
- const orderHashes = Array.from(this._dependentOrderHashes[makerAddress][makerToken]);
- await this._emitRevalidateOrdersAsync(orderHashes);
- }
- break;
- }
- case TokenEvents.Transfer: {
- // Invalidate cache
- const args = decodedLog.args as TransferContractEventArgs;
- this._balanceAndProxyAllowanceLazyStore.deleteBalance(decodedLog.address, args._from);
- this._balanceAndProxyAllowanceLazyStore.deleteBalance(decodedLog.address, args._to);
- // Revalidate orders
- makerToken = decodedLog.address;
- makerAddress = args._from;
- if (
- !_.isUndefined(this._dependentOrderHashes[makerAddress]) &&
- !_.isUndefined(this._dependentOrderHashes[makerAddress][makerToken])
- ) {
- const orderHashes = Array.from(this._dependentOrderHashes[makerAddress][makerToken]);
- await this._emitRevalidateOrdersAsync(orderHashes);
- }
- break;
- }
- case EtherTokenEvents.Deposit: {
- // Invalidate cache
- const args = decodedLog.args as DepositContractEventArgs;
- this._balanceAndProxyAllowanceLazyStore.deleteBalance(decodedLog.address, args._owner);
- // Revalidate orders
- makerToken = decodedLog.address;
- makerAddress = args._owner;
- if (
- !_.isUndefined(this._dependentOrderHashes[makerAddress]) &&
- !_.isUndefined(this._dependentOrderHashes[makerAddress][makerToken])
- ) {
- const orderHashes = Array.from(this._dependentOrderHashes[makerAddress][makerToken]);
- await this._emitRevalidateOrdersAsync(orderHashes);
- }
- break;
- }
- case EtherTokenEvents.Withdrawal: {
- // Invalidate cache
- const args = decodedLog.args as WithdrawalContractEventArgs;
- this._balanceAndProxyAllowanceLazyStore.deleteBalance(decodedLog.address, args._owner);
- // Revalidate orders
- makerToken = decodedLog.address;
- makerAddress = args._owner;
- if (
- !_.isUndefined(this._dependentOrderHashes[makerAddress]) &&
- !_.isUndefined(this._dependentOrderHashes[makerAddress][makerToken])
- ) {
- const orderHashes = Array.from(this._dependentOrderHashes[makerAddress][makerToken]);
- await this._emitRevalidateOrdersAsync(orderHashes);
- }
- break;
- }
- case ExchangeEvents.LogFill: {
- // Invalidate cache
- const args = decodedLog.args as LogFillContractEventArgs;
- this._orderFilledCancelledLazyStore.deleteFilledTakerAmount(args.orderHash);
- // Revalidate orders
- const orderHash = args.orderHash;
- const isOrderWatched = !_.isUndefined(this._orderByOrderHash[orderHash]);
- if (isOrderWatched) {
- await this._emitRevalidateOrdersAsync([orderHash]);
- }
- break;
- }
- case ExchangeEvents.LogCancel: {
- // Invalidate cache
- const args = decodedLog.args as LogCancelContractEventArgs;
- this._orderFilledCancelledLazyStore.deleteCancelledTakerAmount(args.orderHash);
- // Revalidate orders
- const orderHash = args.orderHash;
- const isOrderWatched = !_.isUndefined(this._orderByOrderHash[orderHash]);
- if (isOrderWatched) {
- await this._emitRevalidateOrdersAsync([orderHash]);
- }
- break;
- }
- case ExchangeEvents.LogError:
- return; // noop
+ const zrxTokenAddress = this._getZRXTokenAddress();
+ if (!signedOrder.makerFee.isZero()) {
+ this._balanceAndProxyAllowanceLazyStore.deleteBalance(zrxTokenAddress, signedOrder.maker);
+ this._balanceAndProxyAllowanceLazyStore.deleteProxyAllowance(zrxTokenAddress, signedOrder.maker);
+ }
+ if (!signedOrder.takerFee.isZero()) {
+ this._balanceAndProxyAllowanceLazyStore.deleteBalance(zrxTokenAddress, signedOrder.taker);
+ this._balanceAndProxyAllowanceLazyStore.deleteProxyAllowance(zrxTokenAddress, signedOrder.taker);
+ }
+ }
+ private _onOrderExpired(orderHash: string): void {
+ const orderState: OrderState = {
+ isValid: false,
+ orderHash,
+ error: ExchangeContractErrs.OrderFillExpired,
+ };
+ if (!_.isUndefined(this._orderByOrderHash[orderHash])) {
+ this.removeOrder(orderHash);
+ if (!_.isUndefined(this._callbackIfExists)) {
+ this._callbackIfExists(null, orderState);
+ }
+ }
+ }
+ private async _onEventWatcherCallbackAsync(err: Error | null, logIfExists?: LogEvent): Promise<void> {
+ if (!_.isNull(err)) {
+ if (!_.isUndefined(this._callbackIfExists)) {
+ this._callbackIfExists(err);
+ this.unsubscribe();
+ }
+ return;
+ }
+ const log = logIfExists as LogEvent; // At this moment we are sure that no error occured and log is defined.
+ const maybeDecodedLog = this._abiDecoder.tryToDecodeLogOrNoop(log);
+ const isLogDecoded = !_.isUndefined((maybeDecodedLog as LogWithDecodedArgs<any>).event);
+ if (!isLogDecoded) {
+ return; // noop
+ }
+ const decodedLog = maybeDecodedLog as LogWithDecodedArgs<ContractEventArgs>;
+ let makerToken: string;
+ let makerAddress: string;
+ switch (decodedLog.event) {
+ case TokenEvents.Approval: {
+ // Invalidate cache
+ const args = decodedLog.args as ApprovalContractEventArgs;
+ this._balanceAndProxyAllowanceLazyStore.deleteProxyAllowance(decodedLog.address, args._owner);
+ // Revalidate orders
+ makerToken = decodedLog.address;
+ makerAddress = args._owner;
+ if (
+ !_.isUndefined(this._dependentOrderHashes[makerAddress]) &&
+ !_.isUndefined(this._dependentOrderHashes[makerAddress][makerToken])
+ ) {
+ const orderHashes = Array.from(this._dependentOrderHashes[makerAddress][makerToken]);
+ await this._emitRevalidateOrdersAsync(orderHashes);
+ }
+ break;
+ }
+ case TokenEvents.Transfer: {
+ // Invalidate cache
+ const args = decodedLog.args as TransferContractEventArgs;
+ this._balanceAndProxyAllowanceLazyStore.deleteBalance(decodedLog.address, args._from);
+ this._balanceAndProxyAllowanceLazyStore.deleteBalance(decodedLog.address, args._to);
+ // Revalidate orders
+ makerToken = decodedLog.address;
+ makerAddress = args._from;
+ if (
+ !_.isUndefined(this._dependentOrderHashes[makerAddress]) &&
+ !_.isUndefined(this._dependentOrderHashes[makerAddress][makerToken])
+ ) {
+ const orderHashes = Array.from(this._dependentOrderHashes[makerAddress][makerToken]);
+ await this._emitRevalidateOrdersAsync(orderHashes);
+ }
+ break;
+ }
+ case EtherTokenEvents.Deposit: {
+ // Invalidate cache
+ const args = decodedLog.args as DepositContractEventArgs;
+ this._balanceAndProxyAllowanceLazyStore.deleteBalance(decodedLog.address, args._owner);
+ // Revalidate orders
+ makerToken = decodedLog.address;
+ makerAddress = args._owner;
+ if (
+ !_.isUndefined(this._dependentOrderHashes[makerAddress]) &&
+ !_.isUndefined(this._dependentOrderHashes[makerAddress][makerToken])
+ ) {
+ const orderHashes = Array.from(this._dependentOrderHashes[makerAddress][makerToken]);
+ await this._emitRevalidateOrdersAsync(orderHashes);
+ }
+ break;
+ }
+ case EtherTokenEvents.Withdrawal: {
+ // Invalidate cache
+ const args = decodedLog.args as WithdrawalContractEventArgs;
+ this._balanceAndProxyAllowanceLazyStore.deleteBalance(decodedLog.address, args._owner);
+ // Revalidate orders
+ makerToken = decodedLog.address;
+ makerAddress = args._owner;
+ if (
+ !_.isUndefined(this._dependentOrderHashes[makerAddress]) &&
+ !_.isUndefined(this._dependentOrderHashes[makerAddress][makerToken])
+ ) {
+ const orderHashes = Array.from(this._dependentOrderHashes[makerAddress][makerToken]);
+ await this._emitRevalidateOrdersAsync(orderHashes);
+ }
+ break;
+ }
+ case ExchangeEvents.LogFill: {
+ // Invalidate cache
+ const args = decodedLog.args as LogFillContractEventArgs;
+ this._orderFilledCancelledLazyStore.deleteFilledTakerAmount(args.orderHash);
+ // Revalidate orders
+ const orderHash = args.orderHash;
+ const isOrderWatched = !_.isUndefined(this._orderByOrderHash[orderHash]);
+ if (isOrderWatched) {
+ await this._emitRevalidateOrdersAsync([orderHash]);
+ }
+ break;
+ }
+ case ExchangeEvents.LogCancel: {
+ // Invalidate cache
+ const args = decodedLog.args as LogCancelContractEventArgs;
+ this._orderFilledCancelledLazyStore.deleteCancelledTakerAmount(args.orderHash);
+ // Revalidate orders
+ const orderHash = args.orderHash;
+ const isOrderWatched = !_.isUndefined(this._orderByOrderHash[orderHash]);
+ if (isOrderWatched) {
+ await this._emitRevalidateOrdersAsync([orderHash]);
+ }
+ break;
+ }
+ case ExchangeEvents.LogError:
+ return; // noop
- default:
- throw utils.spawnSwitchErr('decodedLog.event', decodedLog.event);
- }
- }
- private async _emitRevalidateOrdersAsync(orderHashes: string[]): Promise<void> {
- for (const orderHash of orderHashes) {
- const signedOrder = this._orderByOrderHash[orderHash];
- // Most of these calls will never reach the network because the data is fetched from stores
- // and only updated when cache is invalidated
- const orderState = await this._orderStateUtils.getOrderStateAsync(signedOrder);
- if (_.isUndefined(this._callbackIfExists)) {
- break; // Unsubscribe was called
- }
- if (_.isEqual(orderState, this._orderStateByOrderHashCache[orderHash])) {
- // Actual order state didn't change
- continue;
- } else {
- this._orderStateByOrderHashCache[orderHash] = orderState;
- }
- this._callbackIfExists(null, orderState);
- }
- }
- private _addToDependentOrderHashes(signedOrder: SignedOrder, orderHash: string): void {
- if (_.isUndefined(this._dependentOrderHashes[signedOrder.maker])) {
- this._dependentOrderHashes[signedOrder.maker] = {};
- }
- if (_.isUndefined(this._dependentOrderHashes[signedOrder.maker][signedOrder.makerTokenAddress])) {
- this._dependentOrderHashes[signedOrder.maker][signedOrder.makerTokenAddress] = new Set();
- }
- this._dependentOrderHashes[signedOrder.maker][signedOrder.makerTokenAddress].add(orderHash);
- const zrxTokenAddress = this._getZRXTokenAddress();
- if (_.isUndefined(this._dependentOrderHashes[signedOrder.maker][zrxTokenAddress])) {
- this._dependentOrderHashes[signedOrder.maker][zrxTokenAddress] = new Set();
- }
- this._dependentOrderHashes[signedOrder.maker][zrxTokenAddress].add(orderHash);
- }
- private _removeFromDependentOrderHashes(makerAddress: string, tokenAddress: string, orderHash: string) {
- this._dependentOrderHashes[makerAddress][tokenAddress].delete(orderHash);
- if (this._dependentOrderHashes[makerAddress][tokenAddress].size === 0) {
- delete this._dependentOrderHashes[makerAddress][tokenAddress];
- }
- if (_.isEmpty(this._dependentOrderHashes[makerAddress])) {
- delete this._dependentOrderHashes[makerAddress];
- }
- }
- private _getZRXTokenAddress(): string {
- const exchange = (this._orderFilledCancelledLazyStore as any)._exchange as ExchangeWrapper;
- const zrxTokenAddress = exchange.getZRXTokenAddress();
- return zrxTokenAddress;
- }
+ default:
+ throw utils.spawnSwitchErr('decodedLog.event', decodedLog.event);
+ }
+ }
+ private async _emitRevalidateOrdersAsync(orderHashes: string[]): Promise<void> {
+ for (const orderHash of orderHashes) {
+ const signedOrder = this._orderByOrderHash[orderHash];
+ // Most of these calls will never reach the network because the data is fetched from stores
+ // and only updated when cache is invalidated
+ const orderState = await this._orderStateUtils.getOrderStateAsync(signedOrder);
+ if (_.isUndefined(this._callbackIfExists)) {
+ break; // Unsubscribe was called
+ }
+ if (_.isEqual(orderState, this._orderStateByOrderHashCache[orderHash])) {
+ // Actual order state didn't change
+ continue;
+ } else {
+ this._orderStateByOrderHashCache[orderHash] = orderState;
+ }
+ this._callbackIfExists(null, orderState);
+ }
+ }
+ private _addToDependentOrderHashes(signedOrder: SignedOrder, orderHash: string): void {
+ if (_.isUndefined(this._dependentOrderHashes[signedOrder.maker])) {
+ this._dependentOrderHashes[signedOrder.maker] = {};
+ }
+ if (_.isUndefined(this._dependentOrderHashes[signedOrder.maker][signedOrder.makerTokenAddress])) {
+ this._dependentOrderHashes[signedOrder.maker][signedOrder.makerTokenAddress] = new Set();
+ }
+ this._dependentOrderHashes[signedOrder.maker][signedOrder.makerTokenAddress].add(orderHash);
+ const zrxTokenAddress = this._getZRXTokenAddress();
+ if (_.isUndefined(this._dependentOrderHashes[signedOrder.maker][zrxTokenAddress])) {
+ this._dependentOrderHashes[signedOrder.maker][zrxTokenAddress] = new Set();
+ }
+ this._dependentOrderHashes[signedOrder.maker][zrxTokenAddress].add(orderHash);
+ }
+ private _removeFromDependentOrderHashes(makerAddress: string, tokenAddress: string, orderHash: string) {
+ this._dependentOrderHashes[makerAddress][tokenAddress].delete(orderHash);
+ if (this._dependentOrderHashes[makerAddress][tokenAddress].size === 0) {
+ delete this._dependentOrderHashes[makerAddress][tokenAddress];
+ }
+ if (_.isEmpty(this._dependentOrderHashes[makerAddress])) {
+ delete this._dependentOrderHashes[makerAddress];
+ }
+ }
+ private _getZRXTokenAddress(): string {
+ const exchange = (this._orderFilledCancelledLazyStore as any)._exchange as ExchangeWrapper;
+ const zrxTokenAddress = exchange.getZRXTokenAddress();
+ return zrxTokenAddress;
+ }
}
diff --git a/packages/0x.js/src/order_watcher/remaining_fillable_calculator.ts b/packages/0x.js/src/order_watcher/remaining_fillable_calculator.ts
index e010092af..20b09d606 100644
--- a/packages/0x.js/src/order_watcher/remaining_fillable_calculator.ts
+++ b/packages/0x.js/src/order_watcher/remaining_fillable_calculator.ts
@@ -3,94 +3,94 @@ import { BigNumber } from '@0xproject/utils';
import { SignedOrder } from '../types';
export class RemainingFillableCalculator {
- private _signedOrder: SignedOrder;
- private _isMakerTokenZRX: boolean;
- // Transferrable Amount is the minimum of Approval and Balance
- private _transferrableMakerTokenAmount: BigNumber;
- private _transferrableMakerFeeTokenAmount: BigNumber;
- private _remainingMakerTokenAmount: BigNumber;
- private _remainingMakerFeeAmount: BigNumber;
- constructor(
- signedOrder: SignedOrder,
- isMakerTokenZRX: boolean,
- transferrableMakerTokenAmount: BigNumber,
- transferrableMakerFeeTokenAmount: BigNumber,
- remainingMakerTokenAmount: BigNumber,
- ) {
- this._signedOrder = signedOrder;
- this._isMakerTokenZRX = isMakerTokenZRX;
- this._transferrableMakerTokenAmount = transferrableMakerTokenAmount;
- this._transferrableMakerFeeTokenAmount = transferrableMakerFeeTokenAmount;
- this._remainingMakerTokenAmount = remainingMakerTokenAmount;
- this._remainingMakerFeeAmount = remainingMakerTokenAmount
- .times(signedOrder.makerFee)
- .dividedToIntegerBy(signedOrder.makerTokenAmount);
- }
- public computeRemainingMakerFillable(): BigNumber {
- if (this._hasSufficientFundsForFeeAndTransferAmount()) {
- return this._remainingMakerTokenAmount;
- }
- if (this._signedOrder.makerFee.isZero()) {
- return BigNumber.min(this._remainingMakerTokenAmount, this._transferrableMakerTokenAmount);
- }
- return this._calculatePartiallyFillableMakerTokenAmount();
- }
- public computeRemainingTakerFillable(): BigNumber {
- return this.computeRemainingMakerFillable()
- .times(this._signedOrder.takerTokenAmount)
- .dividedToIntegerBy(this._signedOrder.makerTokenAmount);
- }
- private _hasSufficientFundsForFeeAndTransferAmount(): boolean {
- if (this._isMakerTokenZRX) {
- const totalZRXTransferAmountRequired = this._remainingMakerTokenAmount.plus(this._remainingMakerFeeAmount);
- const hasSufficientFunds = this._transferrableMakerTokenAmount.greaterThanOrEqualTo(
- totalZRXTransferAmountRequired,
- );
- return hasSufficientFunds;
- } else {
- const hasSufficientFundsForTransferAmount = this._transferrableMakerTokenAmount.greaterThanOrEqualTo(
- this._remainingMakerTokenAmount,
- );
- const hasSufficientFundsForFeeAmount = this._transferrableMakerFeeTokenAmount.greaterThanOrEqualTo(
- this._remainingMakerFeeAmount,
- );
- const hasSufficientFunds = hasSufficientFundsForTransferAmount && hasSufficientFundsForFeeAmount;
- return hasSufficientFunds;
- }
- }
- private _calculatePartiallyFillableMakerTokenAmount(): BigNumber {
- // Given an order for 200 wei for 2 ZRXwei fee, find 100 wei for 1 ZRXwei. Order ratio is then 100:1
- const orderToFeeRatio = this._signedOrder.makerTokenAmount.dividedBy(this._signedOrder.makerFee);
- // The number of times the maker can fill the order, if each fill only required the transfer of a single
- // baseUnit of fee tokens.
- // Given 2 ZRXwei, the maximum amount of times Maker can fill this order, in terms of fees, is 2
- const fillableTimesInFeeTokenBaseUnits = BigNumber.min(
- this._transferrableMakerFeeTokenAmount,
- this._remainingMakerFeeAmount,
- );
- // The number of times the Maker can fill the order, given the Maker Token Balance
- // Assuming a balance of 150 wei, and an orderToFeeRatio of 100:1, maker can fill this order 1 time.
- let fillableTimesInMakerTokenUnits = this._transferrableMakerTokenAmount.dividedBy(orderToFeeRatio);
- if (this._isMakerTokenZRX) {
- // If ZRX is the maker token, the Fee and the Maker amount need to be removed from the same pool;
- // 200 ZRXwei for 2ZRXwei fee can only be filled once (need 202 ZRXwei)
- const totalZRXTokenPooled = this._transferrableMakerTokenAmount;
- // The purchasing power here is less as the tokens are taken from the same Pool
- // For every one number of fills, we have to take an extra ZRX out of the pool
- fillableTimesInMakerTokenUnits = totalZRXTokenPooled.dividedBy(orderToFeeRatio.plus(new BigNumber(1)));
- }
- // When Ratio is not fully divisible there can be remainders which cannot be represented, so they are floored.
- // This can result in a RoundingError being thrown by the Exchange Contract.
- const partiallyFillableMakerTokenAmount = fillableTimesInMakerTokenUnits
- .times(this._signedOrder.makerTokenAmount)
- .dividedToIntegerBy(this._signedOrder.makerFee);
- const partiallyFillableFeeTokenAmount = fillableTimesInFeeTokenBaseUnits
- .times(this._signedOrder.makerTokenAmount)
- .dividedToIntegerBy(this._signedOrder.makerFee);
- const partiallyFillableAmount = BigNumber.min(
- partiallyFillableMakerTokenAmount,
- partiallyFillableFeeTokenAmount,
- );
- return partiallyFillableAmount;
- }
+ private _signedOrder: SignedOrder;
+ private _isMakerTokenZRX: boolean;
+ // Transferrable Amount is the minimum of Approval and Balance
+ private _transferrableMakerTokenAmount: BigNumber;
+ private _transferrableMakerFeeTokenAmount: BigNumber;
+ private _remainingMakerTokenAmount: BigNumber;
+ private _remainingMakerFeeAmount: BigNumber;
+ constructor(
+ signedOrder: SignedOrder,
+ isMakerTokenZRX: boolean,
+ transferrableMakerTokenAmount: BigNumber,
+ transferrableMakerFeeTokenAmount: BigNumber,
+ remainingMakerTokenAmount: BigNumber,
+ ) {
+ this._signedOrder = signedOrder;
+ this._isMakerTokenZRX = isMakerTokenZRX;
+ this._transferrableMakerTokenAmount = transferrableMakerTokenAmount;
+ this._transferrableMakerFeeTokenAmount = transferrableMakerFeeTokenAmount;
+ this._remainingMakerTokenAmount = remainingMakerTokenAmount;
+ this._remainingMakerFeeAmount = remainingMakerTokenAmount
+ .times(signedOrder.makerFee)
+ .dividedToIntegerBy(signedOrder.makerTokenAmount);
+ }
+ public computeRemainingMakerFillable(): BigNumber {
+ if (this._hasSufficientFundsForFeeAndTransferAmount()) {
+ return this._remainingMakerTokenAmount;
+ }
+ if (this._signedOrder.makerFee.isZero()) {
+ return BigNumber.min(this._remainingMakerTokenAmount, this._transferrableMakerTokenAmount);
+ }
+ return this._calculatePartiallyFillableMakerTokenAmount();
+ }
+ public computeRemainingTakerFillable(): BigNumber {
+ return this.computeRemainingMakerFillable()
+ .times(this._signedOrder.takerTokenAmount)
+ .dividedToIntegerBy(this._signedOrder.makerTokenAmount);
+ }
+ private _hasSufficientFundsForFeeAndTransferAmount(): boolean {
+ if (this._isMakerTokenZRX) {
+ const totalZRXTransferAmountRequired = this._remainingMakerTokenAmount.plus(this._remainingMakerFeeAmount);
+ const hasSufficientFunds = this._transferrableMakerTokenAmount.greaterThanOrEqualTo(
+ totalZRXTransferAmountRequired,
+ );
+ return hasSufficientFunds;
+ } else {
+ const hasSufficientFundsForTransferAmount = this._transferrableMakerTokenAmount.greaterThanOrEqualTo(
+ this._remainingMakerTokenAmount,
+ );
+ const hasSufficientFundsForFeeAmount = this._transferrableMakerFeeTokenAmount.greaterThanOrEqualTo(
+ this._remainingMakerFeeAmount,
+ );
+ const hasSufficientFunds = hasSufficientFundsForTransferAmount && hasSufficientFundsForFeeAmount;
+ return hasSufficientFunds;
+ }
+ }
+ private _calculatePartiallyFillableMakerTokenAmount(): BigNumber {
+ // Given an order for 200 wei for 2 ZRXwei fee, find 100 wei for 1 ZRXwei. Order ratio is then 100:1
+ const orderToFeeRatio = this._signedOrder.makerTokenAmount.dividedBy(this._signedOrder.makerFee);
+ // The number of times the maker can fill the order, if each fill only required the transfer of a single
+ // baseUnit of fee tokens.
+ // Given 2 ZRXwei, the maximum amount of times Maker can fill this order, in terms of fees, is 2
+ const fillableTimesInFeeTokenBaseUnits = BigNumber.min(
+ this._transferrableMakerFeeTokenAmount,
+ this._remainingMakerFeeAmount,
+ );
+ // The number of times the Maker can fill the order, given the Maker Token Balance
+ // Assuming a balance of 150 wei, and an orderToFeeRatio of 100:1, maker can fill this order 1 time.
+ let fillableTimesInMakerTokenUnits = this._transferrableMakerTokenAmount.dividedBy(orderToFeeRatio);
+ if (this._isMakerTokenZRX) {
+ // If ZRX is the maker token, the Fee and the Maker amount need to be removed from the same pool;
+ // 200 ZRXwei for 2ZRXwei fee can only be filled once (need 202 ZRXwei)
+ const totalZRXTokenPooled = this._transferrableMakerTokenAmount;
+ // The purchasing power here is less as the tokens are taken from the same Pool
+ // For every one number of fills, we have to take an extra ZRX out of the pool
+ fillableTimesInMakerTokenUnits = totalZRXTokenPooled.dividedBy(orderToFeeRatio.plus(new BigNumber(1)));
+ }
+ // When Ratio is not fully divisible there can be remainders which cannot be represented, so they are floored.
+ // This can result in a RoundingError being thrown by the Exchange Contract.
+ const partiallyFillableMakerTokenAmount = fillableTimesInMakerTokenUnits
+ .times(this._signedOrder.makerTokenAmount)
+ .dividedToIntegerBy(this._signedOrder.makerFee);
+ const partiallyFillableFeeTokenAmount = fillableTimesInFeeTokenBaseUnits
+ .times(this._signedOrder.makerTokenAmount)
+ .dividedToIntegerBy(this._signedOrder.makerFee);
+ const partiallyFillableAmount = BigNumber.min(
+ partiallyFillableMakerTokenAmount,
+ partiallyFillableFeeTokenAmount,
+ );
+ return partiallyFillableAmount;
+ }
}
diff --git a/packages/0x.js/src/schemas/zero_ex_config_schema.ts b/packages/0x.js/src/schemas/zero_ex_config_schema.ts
index 657b24c58..546b1c2d0 100644
--- a/packages/0x.js/src/schemas/zero_ex_config_schema.ts
+++ b/packages/0x.js/src/schemas/zero_ex_config_schema.ts
@@ -1,27 +1,27 @@
export const zeroExConfigSchema = {
- id: '/ZeroExConfig',
- properties: {
- networkId: {
- type: 'number',
- minimum: 0,
- },
- gasPrice: { $ref: '/Number' },
- exchangeContractAddress: { $ref: '/Address' },
- tokenRegistryContractAddress: { $ref: '/Address' },
- orderWatcherConfig: {
- type: 'object',
- properties: {
- pollingIntervalMs: {
- type: 'number',
- minimum: 0,
- },
- numConfirmations: {
- type: 'number',
- minimum: 0,
- },
- },
- },
- },
- type: 'object',
- required: ['networkId'],
+ id: '/ZeroExConfig',
+ properties: {
+ networkId: {
+ type: 'number',
+ minimum: 0,
+ },
+ gasPrice: { $ref: '/Number' },
+ exchangeContractAddress: { $ref: '/Address' },
+ tokenRegistryContractAddress: { $ref: '/Address' },
+ orderWatcherConfig: {
+ type: 'object',
+ properties: {
+ pollingIntervalMs: {
+ type: 'number',
+ minimum: 0,
+ },
+ numConfirmations: {
+ type: 'number',
+ minimum: 0,
+ },
+ },
+ },
+ },
+ type: 'object',
+ required: ['networkId'],
};
diff --git a/packages/0x.js/src/stores/balance_proxy_allowance_lazy_store.ts b/packages/0x.js/src/stores/balance_proxy_allowance_lazy_store.ts
index 379afa4bc..33feea105 100644
--- a/packages/0x.js/src/stores/balance_proxy_allowance_lazy_store.ts
+++ b/packages/0x.js/src/stores/balance_proxy_allowance_lazy_store.ts
@@ -8,79 +8,79 @@ import { BlockParamLiteral } from '../types';
* Copy on read store for balances/proxyAllowances of tokens/accounts
*/
export class BalanceAndProxyAllowanceLazyStore {
- private _token: TokenWrapper;
- private _defaultBlock: BlockParamLiteral;
- private _balance: {
- [tokenAddress: string]: {
- [userAddress: string]: BigNumber;
- };
- };
- private _proxyAllowance: {
- [tokenAddress: string]: {
- [userAddress: string]: BigNumber;
- };
- };
- constructor(token: TokenWrapper, defaultBlock: BlockParamLiteral) {
- this._token = token;
- this._defaultBlock = defaultBlock;
- this._balance = {};
- this._proxyAllowance = {};
- }
- public async getBalanceAsync(tokenAddress: string, userAddress: string): Promise<BigNumber> {
- if (_.isUndefined(this._balance[tokenAddress]) || _.isUndefined(this._balance[tokenAddress][userAddress])) {
- const methodOpts = {
- defaultBlock: this._defaultBlock,
- };
- const balance = await this._token.getBalanceAsync(tokenAddress, userAddress, methodOpts);
- this.setBalance(tokenAddress, userAddress, balance);
- }
- const cachedBalance = this._balance[tokenAddress][userAddress];
- return cachedBalance;
- }
- public setBalance(tokenAddress: string, userAddress: string, balance: BigNumber): void {
- if (_.isUndefined(this._balance[tokenAddress])) {
- this._balance[tokenAddress] = {};
- }
- this._balance[tokenAddress][userAddress] = balance;
- }
- public deleteBalance(tokenAddress: string, userAddress: string): void {
- if (!_.isUndefined(this._balance[tokenAddress])) {
- delete this._balance[tokenAddress][userAddress];
- if (_.isEmpty(this._balance[tokenAddress])) {
- delete this._balance[tokenAddress];
- }
- }
- }
- public async getProxyAllowanceAsync(tokenAddress: string, userAddress: string): Promise<BigNumber> {
- if (
- _.isUndefined(this._proxyAllowance[tokenAddress]) ||
- _.isUndefined(this._proxyAllowance[tokenAddress][userAddress])
- ) {
- const methodOpts = {
- defaultBlock: this._defaultBlock,
- };
- const proxyAllowance = await this._token.getProxyAllowanceAsync(tokenAddress, userAddress, methodOpts);
- this.setProxyAllowance(tokenAddress, userAddress, proxyAllowance);
- }
- const cachedProxyAllowance = this._proxyAllowance[tokenAddress][userAddress];
- return cachedProxyAllowance;
- }
- public setProxyAllowance(tokenAddress: string, userAddress: string, proxyAllowance: BigNumber): void {
- if (_.isUndefined(this._proxyAllowance[tokenAddress])) {
- this._proxyAllowance[tokenAddress] = {};
- }
- this._proxyAllowance[tokenAddress][userAddress] = proxyAllowance;
- }
- public deleteProxyAllowance(tokenAddress: string, userAddress: string): void {
- if (!_.isUndefined(this._proxyAllowance[tokenAddress])) {
- delete this._proxyAllowance[tokenAddress][userAddress];
- if (_.isEmpty(this._proxyAllowance[tokenAddress])) {
- delete this._proxyAllowance[tokenAddress];
- }
- }
- }
- public deleteAll(): void {
- this._balance = {};
- this._proxyAllowance = {};
- }
+ private _token: TokenWrapper;
+ private _defaultBlock: BlockParamLiteral;
+ private _balance: {
+ [tokenAddress: string]: {
+ [userAddress: string]: BigNumber;
+ };
+ };
+ private _proxyAllowance: {
+ [tokenAddress: string]: {
+ [userAddress: string]: BigNumber;
+ };
+ };
+ constructor(token: TokenWrapper, defaultBlock: BlockParamLiteral) {
+ this._token = token;
+ this._defaultBlock = defaultBlock;
+ this._balance = {};
+ this._proxyAllowance = {};
+ }
+ public async getBalanceAsync(tokenAddress: string, userAddress: string): Promise<BigNumber> {
+ if (_.isUndefined(this._balance[tokenAddress]) || _.isUndefined(this._balance[tokenAddress][userAddress])) {
+ const methodOpts = {
+ defaultBlock: this._defaultBlock,
+ };
+ const balance = await this._token.getBalanceAsync(tokenAddress, userAddress, methodOpts);
+ this.setBalance(tokenAddress, userAddress, balance);
+ }
+ const cachedBalance = this._balance[tokenAddress][userAddress];
+ return cachedBalance;
+ }
+ public setBalance(tokenAddress: string, userAddress: string, balance: BigNumber): void {
+ if (_.isUndefined(this._balance[tokenAddress])) {
+ this._balance[tokenAddress] = {};
+ }
+ this._balance[tokenAddress][userAddress] = balance;
+ }
+ public deleteBalance(tokenAddress: string, userAddress: string): void {
+ if (!_.isUndefined(this._balance[tokenAddress])) {
+ delete this._balance[tokenAddress][userAddress];
+ if (_.isEmpty(this._balance[tokenAddress])) {
+ delete this._balance[tokenAddress];
+ }
+ }
+ }
+ public async getProxyAllowanceAsync(tokenAddress: string, userAddress: string): Promise<BigNumber> {
+ if (
+ _.isUndefined(this._proxyAllowance[tokenAddress]) ||
+ _.isUndefined(this._proxyAllowance[tokenAddress][userAddress])
+ ) {
+ const methodOpts = {
+ defaultBlock: this._defaultBlock,
+ };
+ const proxyAllowance = await this._token.getProxyAllowanceAsync(tokenAddress, userAddress, methodOpts);
+ this.setProxyAllowance(tokenAddress, userAddress, proxyAllowance);
+ }
+ const cachedProxyAllowance = this._proxyAllowance[tokenAddress][userAddress];
+ return cachedProxyAllowance;
+ }
+ public setProxyAllowance(tokenAddress: string, userAddress: string, proxyAllowance: BigNumber): void {
+ if (_.isUndefined(this._proxyAllowance[tokenAddress])) {
+ this._proxyAllowance[tokenAddress] = {};
+ }
+ this._proxyAllowance[tokenAddress][userAddress] = proxyAllowance;
+ }
+ public deleteProxyAllowance(tokenAddress: string, userAddress: string): void {
+ if (!_.isUndefined(this._proxyAllowance[tokenAddress])) {
+ delete this._proxyAllowance[tokenAddress][userAddress];
+ if (_.isEmpty(this._proxyAllowance[tokenAddress])) {
+ delete this._proxyAllowance[tokenAddress];
+ }
+ }
+ }
+ public deleteAll(): void {
+ this._balance = {};
+ this._proxyAllowance = {};
+ }
}
diff --git a/packages/0x.js/src/stores/order_filled_cancelled_lazy_store.ts b/packages/0x.js/src/stores/order_filled_cancelled_lazy_store.ts
index a99db0702..e22364c09 100644
--- a/packages/0x.js/src/stores/order_filled_cancelled_lazy_store.ts
+++ b/packages/0x.js/src/stores/order_filled_cancelled_lazy_store.ts
@@ -8,54 +8,54 @@ import { BlockParamLiteral } from '../types';
* Copy on read store for filled/cancelled taker amounts
*/
export class OrderFilledCancelledLazyStore {
- private _exchange: ExchangeWrapper;
- private _filledTakerAmount: {
- [orderHash: string]: BigNumber;
- };
- private _cancelledTakerAmount: {
- [orderHash: string]: BigNumber;
- };
- constructor(exchange: ExchangeWrapper) {
- this._exchange = exchange;
- this._filledTakerAmount = {};
- this._cancelledTakerAmount = {};
- }
- public async getFilledTakerAmountAsync(orderHash: string): Promise<BigNumber> {
- if (_.isUndefined(this._filledTakerAmount[orderHash])) {
- const methodOpts = {
- defaultBlock: BlockParamLiteral.Pending,
- };
- const filledTakerAmount = await this._exchange.getFilledTakerAmountAsync(orderHash, methodOpts);
- this.setFilledTakerAmount(orderHash, filledTakerAmount);
- }
- const cachedFilled = this._filledTakerAmount[orderHash];
- return cachedFilled;
- }
- public setFilledTakerAmount(orderHash: string, filledTakerAmount: BigNumber): void {
- this._filledTakerAmount[orderHash] = filledTakerAmount;
- }
- public deleteFilledTakerAmount(orderHash: string): void {
- delete this._filledTakerAmount[orderHash];
- }
- public async getCancelledTakerAmountAsync(orderHash: string): Promise<BigNumber> {
- if (_.isUndefined(this._cancelledTakerAmount[orderHash])) {
- const methodOpts = {
- defaultBlock: BlockParamLiteral.Pending,
- };
- const cancelledTakerAmount = await this._exchange.getCancelledTakerAmountAsync(orderHash, methodOpts);
- this.setCancelledTakerAmount(orderHash, cancelledTakerAmount);
- }
- const cachedCancelled = this._cancelledTakerAmount[orderHash];
- return cachedCancelled;
- }
- public setCancelledTakerAmount(orderHash: string, cancelledTakerAmount: BigNumber): void {
- this._cancelledTakerAmount[orderHash] = cancelledTakerAmount;
- }
- public deleteCancelledTakerAmount(orderHash: string): void {
- delete this._cancelledTakerAmount[orderHash];
- }
- public deleteAll(): void {
- this._filledTakerAmount = {};
- this._cancelledTakerAmount = {};
- }
+ private _exchange: ExchangeWrapper;
+ private _filledTakerAmount: {
+ [orderHash: string]: BigNumber;
+ };
+ private _cancelledTakerAmount: {
+ [orderHash: string]: BigNumber;
+ };
+ constructor(exchange: ExchangeWrapper) {
+ this._exchange = exchange;
+ this._filledTakerAmount = {};
+ this._cancelledTakerAmount = {};
+ }
+ public async getFilledTakerAmountAsync(orderHash: string): Promise<BigNumber> {
+ if (_.isUndefined(this._filledTakerAmount[orderHash])) {
+ const methodOpts = {
+ defaultBlock: BlockParamLiteral.Pending,
+ };
+ const filledTakerAmount = await this._exchange.getFilledTakerAmountAsync(orderHash, methodOpts);
+ this.setFilledTakerAmount(orderHash, filledTakerAmount);
+ }
+ const cachedFilled = this._filledTakerAmount[orderHash];
+ return cachedFilled;
+ }
+ public setFilledTakerAmount(orderHash: string, filledTakerAmount: BigNumber): void {
+ this._filledTakerAmount[orderHash] = filledTakerAmount;
+ }
+ public deleteFilledTakerAmount(orderHash: string): void {
+ delete this._filledTakerAmount[orderHash];
+ }
+ public async getCancelledTakerAmountAsync(orderHash: string): Promise<BigNumber> {
+ if (_.isUndefined(this._cancelledTakerAmount[orderHash])) {
+ const methodOpts = {
+ defaultBlock: BlockParamLiteral.Pending,
+ };
+ const cancelledTakerAmount = await this._exchange.getCancelledTakerAmountAsync(orderHash, methodOpts);
+ this.setCancelledTakerAmount(orderHash, cancelledTakerAmount);
+ }
+ const cachedCancelled = this._cancelledTakerAmount[orderHash];
+ return cachedCancelled;
+ }
+ public setCancelledTakerAmount(orderHash: string, cancelledTakerAmount: BigNumber): void {
+ this._cancelledTakerAmount[orderHash] = cancelledTakerAmount;
+ }
+ public deleteCancelledTakerAmount(orderHash: string): void {
+ delete this._cancelledTakerAmount[orderHash];
+ }
+ public deleteAll(): void {
+ this._filledTakerAmount = {};
+ this._cancelledTakerAmount = {};
+ }
}
diff --git a/packages/0x.js/src/types.ts b/packages/0x.js/src/types.ts
index 7eda4bea6..3c93910e9 100644
--- a/packages/0x.js/src/types.ts
+++ b/packages/0x.js/src/types.ts
@@ -3,41 +3,41 @@ import { BigNumber } from '@0xproject/utils';
import * as Web3 from 'web3';
export enum ZeroExError {
- ExchangeContractDoesNotExist = 'EXCHANGE_CONTRACT_DOES_NOT_EXIST',
- ZRXContractDoesNotExist = 'ZRX_CONTRACT_DOES_NOT_EXIST',
- EtherTokenContractDoesNotExist = 'ETHER_TOKEN_CONTRACT_DOES_NOT_EXIST',
- TokenTransferProxyContractDoesNotExist = 'TOKEN_TRANSFER_PROXY_CONTRACT_DOES_NOT_EXIST',
- TokenRegistryContractDoesNotExist = 'TOKEN_REGISTRY_CONTRACT_DOES_NOT_EXIST',
- TokenContractDoesNotExist = 'TOKEN_CONTRACT_DOES_NOT_EXIST',
- UnhandledError = 'UNHANDLED_ERROR',
- UserHasNoAssociatedAddress = 'USER_HAS_NO_ASSOCIATED_ADDRESSES',
- InvalidSignature = 'INVALID_SIGNATURE',
- ContractNotDeployedOnNetwork = 'CONTRACT_NOT_DEPLOYED_ON_NETWORK',
- InsufficientAllowanceForTransfer = 'INSUFFICIENT_ALLOWANCE_FOR_TRANSFER',
- InsufficientBalanceForTransfer = 'INSUFFICIENT_BALANCE_FOR_TRANSFER',
- InsufficientEthBalanceForDeposit = 'INSUFFICIENT_ETH_BALANCE_FOR_DEPOSIT',
- InsufficientWEthBalanceForWithdrawal = 'INSUFFICIENT_WETH_BALANCE_FOR_WITHDRAWAL',
- InvalidJump = 'INVALID_JUMP',
- OutOfGas = 'OUT_OF_GAS',
- NoNetworkId = 'NO_NETWORK_ID',
- SubscriptionNotFound = 'SUBSCRIPTION_NOT_FOUND',
- SubscriptionAlreadyPresent = 'SUBSCRIPTION_ALREADY_PRESENT',
- TransactionMiningTimeout = 'TRANSACTION_MINING_TIMEOUT',
+ ExchangeContractDoesNotExist = 'EXCHANGE_CONTRACT_DOES_NOT_EXIST',
+ ZRXContractDoesNotExist = 'ZRX_CONTRACT_DOES_NOT_EXIST',
+ EtherTokenContractDoesNotExist = 'ETHER_TOKEN_CONTRACT_DOES_NOT_EXIST',
+ TokenTransferProxyContractDoesNotExist = 'TOKEN_TRANSFER_PROXY_CONTRACT_DOES_NOT_EXIST',
+ TokenRegistryContractDoesNotExist = 'TOKEN_REGISTRY_CONTRACT_DOES_NOT_EXIST',
+ TokenContractDoesNotExist = 'TOKEN_CONTRACT_DOES_NOT_EXIST',
+ UnhandledError = 'UNHANDLED_ERROR',
+ UserHasNoAssociatedAddress = 'USER_HAS_NO_ASSOCIATED_ADDRESSES',
+ InvalidSignature = 'INVALID_SIGNATURE',
+ ContractNotDeployedOnNetwork = 'CONTRACT_NOT_DEPLOYED_ON_NETWORK',
+ InsufficientAllowanceForTransfer = 'INSUFFICIENT_ALLOWANCE_FOR_TRANSFER',
+ InsufficientBalanceForTransfer = 'INSUFFICIENT_BALANCE_FOR_TRANSFER',
+ InsufficientEthBalanceForDeposit = 'INSUFFICIENT_ETH_BALANCE_FOR_DEPOSIT',
+ InsufficientWEthBalanceForWithdrawal = 'INSUFFICIENT_WETH_BALANCE_FOR_WITHDRAWAL',
+ InvalidJump = 'INVALID_JUMP',
+ OutOfGas = 'OUT_OF_GAS',
+ NoNetworkId = 'NO_NETWORK_ID',
+ SubscriptionNotFound = 'SUBSCRIPTION_NOT_FOUND',
+ SubscriptionAlreadyPresent = 'SUBSCRIPTION_ALREADY_PRESENT',
+ TransactionMiningTimeout = 'TRANSACTION_MINING_TIMEOUT',
}
export enum InternalZeroExError {
- NoAbiDecoder = 'NO_ABI_DECODER',
- ZrxNotInTokenRegistry = 'ZRX_NOT_IN_TOKEN_REGISTRY',
- WethNotInTokenRegistry = 'WETH_NOT_IN_TOKEN_REGISTRY',
+ NoAbiDecoder = 'NO_ABI_DECODER',
+ ZrxNotInTokenRegistry = 'ZRX_NOT_IN_TOKEN_REGISTRY',
+ WethNotInTokenRegistry = 'WETH_NOT_IN_TOKEN_REGISTRY',
}
/**
* Elliptic Curve signature
*/
export interface ECSignature {
- v: number;
- r: string;
- s: string;
+ v: number;
+ r: string;
+ s: string;
}
export type OrderAddresses = [string, string, string, string, string];
@@ -46,214 +46,214 @@ export type OrderValues = [BigNumber, BigNumber, BigNumber, BigNumber, BigNumber
export type LogEvent = Web3.LogEntryEvent;
export interface DecodedLogEvent<ArgsType> {
- isRemoved: boolean;
- log: LogWithDecodedArgs<ArgsType>;
+ isRemoved: boolean;
+ log: LogWithDecodedArgs<ArgsType>;
}
export type EventCallback<ArgsType> = (err: null | Error, log?: DecodedLogEvent<ArgsType>) => void;
export type EventWatcherCallback = (err: null | Error, log?: LogEvent) => void;
export enum SolidityTypes {
- Address = 'address',
- Uint256 = 'uint256',
- Uint8 = 'uint8',
- Uint = 'uint',
+ Address = 'address',
+ Uint256 = 'uint256',
+ Uint8 = 'uint8',
+ Uint = 'uint',
}
export enum ExchangeContractErrCodes {
- ERROR_FILL_EXPIRED, // Order has already expired
- ERROR_FILL_NO_VALUE, // Order has already been fully filled or cancelled
- ERROR_FILL_TRUNCATION, // Rounding error too large
- ERROR_FILL_BALANCE_ALLOWANCE, // Insufficient balance or allowance for token transfer
- ERROR_CANCEL_EXPIRED, // Order has already expired
- ERROR_CANCEL_NO_VALUE, // Order has already been fully filled or cancelled
+ ERROR_FILL_EXPIRED, // Order has already expired
+ ERROR_FILL_NO_VALUE, // Order has already been fully filled or cancelled
+ ERROR_FILL_TRUNCATION, // Rounding error too large
+ ERROR_FILL_BALANCE_ALLOWANCE, // Insufficient balance or allowance for token transfer
+ ERROR_CANCEL_EXPIRED, // Order has already expired
+ ERROR_CANCEL_NO_VALUE, // Order has already been fully filled or cancelled
}
export enum ExchangeContractErrs {
- OrderFillExpired = 'ORDER_FILL_EXPIRED',
- OrderCancelExpired = 'ORDER_CANCEL_EXPIRED',
- OrderCancelAmountZero = 'ORDER_CANCEL_AMOUNT_ZERO',
- OrderAlreadyCancelledOrFilled = 'ORDER_ALREADY_CANCELLED_OR_FILLED',
- OrderFillAmountZero = 'ORDER_FILL_AMOUNT_ZERO',
- OrderRemainingFillAmountZero = 'ORDER_REMAINING_FILL_AMOUNT_ZERO',
- OrderFillRoundingError = 'ORDER_FILL_ROUNDING_ERROR',
- FillBalanceAllowanceError = 'FILL_BALANCE_ALLOWANCE_ERROR',
- InsufficientTakerBalance = 'INSUFFICIENT_TAKER_BALANCE',
- InsufficientTakerAllowance = 'INSUFFICIENT_TAKER_ALLOWANCE',
- InsufficientMakerBalance = 'INSUFFICIENT_MAKER_BALANCE',
- InsufficientMakerAllowance = 'INSUFFICIENT_MAKER_ALLOWANCE',
- InsufficientTakerFeeBalance = 'INSUFFICIENT_TAKER_FEE_BALANCE',
- InsufficientTakerFeeAllowance = 'INSUFFICIENT_TAKER_FEE_ALLOWANCE',
- InsufficientMakerFeeBalance = 'INSUFFICIENT_MAKER_FEE_BALANCE',
- InsufficientMakerFeeAllowance = 'INSUFFICIENT_MAKER_FEE_ALLOWANCE',
- TransactionSenderIsNotFillOrderTaker = 'TRANSACTION_SENDER_IS_NOT_FILL_ORDER_TAKER',
- MultipleMakersInSingleCancelBatchDisallowed = 'MULTIPLE_MAKERS_IN_SINGLE_CANCEL_BATCH_DISALLOWED',
- InsufficientRemainingFillAmount = 'INSUFFICIENT_REMAINING_FILL_AMOUNT',
- MultipleTakerTokensInFillUpToDisallowed = 'MULTIPLE_TAKER_TOKENS_IN_FILL_UP_TO_DISALLOWED',
- BatchOrdersMustHaveSameExchangeAddress = 'BATCH_ORDERS_MUST_HAVE_SAME_EXCHANGE_ADDRESS',
- BatchOrdersMustHaveAtLeastOneItem = 'BATCH_ORDERS_MUST_HAVE_AT_LEAST_ONE_ITEM',
+ OrderFillExpired = 'ORDER_FILL_EXPIRED',
+ OrderCancelExpired = 'ORDER_CANCEL_EXPIRED',
+ OrderCancelAmountZero = 'ORDER_CANCEL_AMOUNT_ZERO',
+ OrderAlreadyCancelledOrFilled = 'ORDER_ALREADY_CANCELLED_OR_FILLED',
+ OrderFillAmountZero = 'ORDER_FILL_AMOUNT_ZERO',
+ OrderRemainingFillAmountZero = 'ORDER_REMAINING_FILL_AMOUNT_ZERO',
+ OrderFillRoundingError = 'ORDER_FILL_ROUNDING_ERROR',
+ FillBalanceAllowanceError = 'FILL_BALANCE_ALLOWANCE_ERROR',
+ InsufficientTakerBalance = 'INSUFFICIENT_TAKER_BALANCE',
+ InsufficientTakerAllowance = 'INSUFFICIENT_TAKER_ALLOWANCE',
+ InsufficientMakerBalance = 'INSUFFICIENT_MAKER_BALANCE',
+ InsufficientMakerAllowance = 'INSUFFICIENT_MAKER_ALLOWANCE',
+ InsufficientTakerFeeBalance = 'INSUFFICIENT_TAKER_FEE_BALANCE',
+ InsufficientTakerFeeAllowance = 'INSUFFICIENT_TAKER_FEE_ALLOWANCE',
+ InsufficientMakerFeeBalance = 'INSUFFICIENT_MAKER_FEE_BALANCE',
+ InsufficientMakerFeeAllowance = 'INSUFFICIENT_MAKER_FEE_ALLOWANCE',
+ TransactionSenderIsNotFillOrderTaker = 'TRANSACTION_SENDER_IS_NOT_FILL_ORDER_TAKER',
+ MultipleMakersInSingleCancelBatchDisallowed = 'MULTIPLE_MAKERS_IN_SINGLE_CANCEL_BATCH_DISALLOWED',
+ InsufficientRemainingFillAmount = 'INSUFFICIENT_REMAINING_FILL_AMOUNT',
+ MultipleTakerTokensInFillUpToDisallowed = 'MULTIPLE_TAKER_TOKENS_IN_FILL_UP_TO_DISALLOWED',
+ BatchOrdersMustHaveSameExchangeAddress = 'BATCH_ORDERS_MUST_HAVE_SAME_EXCHANGE_ADDRESS',
+ BatchOrdersMustHaveAtLeastOneItem = 'BATCH_ORDERS_MUST_HAVE_AT_LEAST_ONE_ITEM',
}
export type RawLog = Web3.LogEntry;
export interface ContractEvent {
- logIndex: number;
- transactionIndex: number;
- transactionHash: string;
- blockHash: string;
- blockNumber: number;
- address: string;
- type: string;
- event: string;
- args: ContractEventArgs;
+ logIndex: number;
+ transactionIndex: number;
+ transactionHash: string;
+ blockHash: string;
+ blockNumber: number;
+ address: string;
+ type: string;
+ event: string;
+ args: ContractEventArgs;
}
export interface LogFillContractEventArgs {
- maker: string;
- taker: string;
- feeRecipient: string;
- makerToken: string;
- takerToken: string;
- filledMakerTokenAmount: BigNumber;
- filledTakerTokenAmount: BigNumber;
- paidMakerFee: BigNumber;
- paidTakerFee: BigNumber;
- tokens: string;
- orderHash: string;
+ maker: string;
+ taker: string;
+ feeRecipient: string;
+ makerToken: string;
+ takerToken: string;
+ filledMakerTokenAmount: BigNumber;
+ filledTakerTokenAmount: BigNumber;
+ paidMakerFee: BigNumber;
+ paidTakerFee: BigNumber;
+ tokens: string;
+ orderHash: string;
}
export interface LogCancelContractEventArgs {
- maker: string;
- feeRecipient: string;
- makerToken: string;
- takerToken: string;
- cancelledMakerTokenAmount: BigNumber;
- cancelledTakerTokenAmount: BigNumber;
- tokens: string;
- orderHash: string;
+ maker: string;
+ feeRecipient: string;
+ makerToken: string;
+ takerToken: string;
+ cancelledMakerTokenAmount: BigNumber;
+ cancelledTakerTokenAmount: BigNumber;
+ tokens: string;
+ orderHash: string;
}
export interface LogErrorContractEventArgs {
- errorId: BigNumber;
- orderHash: string;
+ errorId: BigNumber;
+ orderHash: string;
}
export type ExchangeContractEventArgs =
- | LogFillContractEventArgs
- | LogCancelContractEventArgs
- | LogErrorContractEventArgs;
+ | LogFillContractEventArgs
+ | LogCancelContractEventArgs
+ | LogErrorContractEventArgs;
export interface TransferContractEventArgs {
- _from: string;
- _to: string;
- _value: BigNumber;
+ _from: string;
+ _to: string;
+ _value: BigNumber;
}
export interface ApprovalContractEventArgs {
- _owner: string;
- _spender: string;
- _value: BigNumber;
+ _owner: string;
+ _spender: string;
+ _value: BigNumber;
}
export interface DepositContractEventArgs {
- _owner: string;
- _value: BigNumber;
+ _owner: string;
+ _value: BigNumber;
}
export interface WithdrawalContractEventArgs {
- _owner: string;
- _value: BigNumber;
+ _owner: string;
+ _value: BigNumber;
}
export type TokenContractEventArgs = TransferContractEventArgs | ApprovalContractEventArgs;
export type EtherTokenContractEventArgs =
- | TokenContractEventArgs
- | DepositContractEventArgs
- | WithdrawalContractEventArgs;
+ | TokenContractEventArgs
+ | DepositContractEventArgs
+ | WithdrawalContractEventArgs;
export type ContractEventArgs = ExchangeContractEventArgs | TokenContractEventArgs | EtherTokenContractEventArgs;
export type ContractEventArg = string | BigNumber;
export interface Order {
- maker: string;
- taker: string;
- makerFee: BigNumber;
- takerFee: BigNumber;
- makerTokenAmount: BigNumber;
- takerTokenAmount: BigNumber;
- makerTokenAddress: string;
- takerTokenAddress: string;
- salt: BigNumber;
- exchangeContractAddress: string;
- feeRecipient: string;
- expirationUnixTimestampSec: BigNumber;
+ maker: string;
+ taker: string;
+ makerFee: BigNumber;
+ takerFee: BigNumber;
+ makerTokenAmount: BigNumber;
+ takerTokenAmount: BigNumber;
+ makerTokenAddress: string;
+ takerTokenAddress: string;
+ salt: BigNumber;
+ exchangeContractAddress: string;
+ feeRecipient: string;
+ expirationUnixTimestampSec: BigNumber;
}
export interface SignedOrder extends Order {
- ecSignature: ECSignature;
+ ecSignature: ECSignature;
}
// [address, name, symbol, decimals, ipfsHash, swarmHash]
export type TokenMetadata = [string, string, string, BigNumber, string, string];
export interface Token {
- name: string;
- address: string;
- symbol: string;
- decimals: number;
+ name: string;
+ address: string;
+ symbol: string;
+ decimals: number;
}
export interface TxOpts {
- from: string;
- gas?: number;
- value?: BigNumber;
- gasPrice?: BigNumber;
+ from: string;
+ gas?: number;
+ value?: BigNumber;
+ gasPrice?: BigNumber;
}
export interface TokenAddressBySymbol {
- [symbol: string]: string;
+ [symbol: string]: string;
}
export enum ExchangeEvents {
- LogFill = 'LogFill',
- LogCancel = 'LogCancel',
- LogError = 'LogError',
+ LogFill = 'LogFill',
+ LogCancel = 'LogCancel',
+ LogError = 'LogError',
}
export enum TokenEvents {
- Transfer = 'Transfer',
- Approval = 'Approval',
+ Transfer = 'Transfer',
+ Approval = 'Approval',
}
export enum EtherTokenEvents {
- Transfer = 'Transfer',
- Approval = 'Approval',
- Deposit = 'Deposit',
- Withdrawal = 'Withdrawal',
+ Transfer = 'Transfer',
+ Approval = 'Approval',
+ Deposit = 'Deposit',
+ Withdrawal = 'Withdrawal',
}
export type ContractEvents = TokenEvents | ExchangeEvents | EtherTokenEvents;
export interface IndexedFilterValues {
- [index: string]: ContractEventArg;
+ [index: string]: ContractEventArg;
}
// Earliest is omitted by design. It is simply an alias for the `0` constant and
// is thus not very helpful. Moreover, this type is used in places that only accept
// `latest` or `pending`.
export enum BlockParamLiteral {
- Latest = 'latest',
- Pending = 'pending',
+ Latest = 'latest',
+ Pending = 'pending',
}
export type BlockParam = BlockParamLiteral | number;
export interface BlockRange {
- fromBlock: BlockParam;
- toBlock: BlockParam;
+ fromBlock: BlockParam;
+ toBlock: BlockParam;
}
export type DoneCallback = (err?: Error) => void;
export interface OrderCancellationRequest {
- order: Order | SignedOrder;
- takerTokenCancelAmount: BigNumber;
+ order: Order | SignedOrder;
+ takerTokenCancelAmount: BigNumber;
}
export interface OrderFillRequest {
- signedOrder: SignedOrder;
- takerTokenFillAmount: BigNumber;
+ signedOrder: SignedOrder;
+ takerTokenFillAmount: BigNumber;
}
export type AsyncMethod = (...args: any[]) => Promise<any>;
@@ -268,8 +268,8 @@ export type SyncMethod = (...args: any[]) => any;
export type Web3Provider = Web3.Provider;
export interface JSONRPCPayload {
- params: any[];
- method: string;
+ params: any[];
+ method: string;
}
/*
@@ -280,10 +280,10 @@ export interface JSONRPCPayload {
* cleanupJobIntervalMs: How often to run a cleanup job which revalidates all the orders. Defaults: 1h
*/
export interface OrderStateWatcherConfig {
- orderExpirationCheckingIntervalMs?: number;
- eventPollingIntervalMs?: number;
- expirationMarginMs?: number;
- cleanupJobIntervalMs?: number;
+ orderExpirationCheckingIntervalMs?: number;
+ eventPollingIntervalMs?: number;
+ expirationMarginMs?: number;
+ cleanupJobIntervalMs?: number;
}
/*
@@ -296,42 +296,42 @@ export interface OrderStateWatcherConfig {
* orderWatcherConfig: All the configs related to the orderWatcher
*/
export interface ZeroExConfig {
- networkId: number;
- gasPrice?: BigNumber;
- exchangeContractAddress?: string;
- zrxContractAddress?: string;
- tokenRegistryContractAddress?: string;
- tokenTransferProxyContractAddress?: string;
- orderWatcherConfig?: OrderStateWatcherConfig;
+ networkId: number;
+ gasPrice?: BigNumber;
+ exchangeContractAddress?: string;
+ zrxContractAddress?: string;
+ tokenRegistryContractAddress?: string;
+ tokenTransferProxyContractAddress?: string;
+ orderWatcherConfig?: OrderStateWatcherConfig;
}
export enum AbiType {
- Function = 'function',
- Constructor = 'constructor',
- Event = 'event',
- Fallback = 'fallback',
+ Function = 'function',
+ Constructor = 'constructor',
+ Event = 'event',
+ Fallback = 'fallback',
}
export interface DecodedLogArgs {
- [argName: string]: ContractEventArg;
+ [argName: string]: ContractEventArg;
}
export interface LogWithDecodedArgs<ArgsType> extends Web3.DecodedLogEntry<ArgsType> {}
export interface TransactionReceiptWithDecodedLogs extends TransactionReceipt {
- logs: Array<LogWithDecodedArgs<DecodedLogArgs> | Web3.LogEntry>;
+ logs: Array<LogWithDecodedArgs<DecodedLogArgs> | Web3.LogEntry>;
}
export type ArtifactContractName = 'ZRX' | 'TokenTransferProxy' | 'TokenRegistry' | 'Token' | 'Exchange' | 'EtherToken';
export interface Artifact {
- contract_name: ArtifactContractName;
- abi: Web3.ContractAbi;
- networks: {
- [networkId: number]: {
- address: string;
- };
- };
+ contract_name: ArtifactContractName;
+ abi: Web3.ContractAbi;
+ networks: {
+ [networkId: number]: {
+ address: string;
+ };
+ };
}
/*
@@ -341,7 +341,7 @@ export interface Artifact {
* allowance/balance to fill the entire remaining order amount.
*/
export interface ValidateOrderFillableOpts {
- expectedFillTakerTokenAmount?: BigNumber;
+ expectedFillTakerTokenAmount?: BigNumber;
}
/*
@@ -351,7 +351,7 @@ export interface ValidateOrderFillableOpts {
* flag when running Parity).
*/
export interface MethodOpts {
- defaultBlock?: Web3.BlockParam;
+ defaultBlock?: Web3.BlockParam;
}
/*
@@ -359,8 +359,8 @@ export interface MethodOpts {
* gasLimit: The amount of gas to send with a transaction
*/
export interface TransactionOpts {
- gasPrice?: BigNumber;
- gasLimit?: number;
+ gasPrice?: BigNumber;
+ gasLimit?: number;
}
/*
@@ -368,42 +368,42 @@ export interface TransactionOpts {
* broadcasting it. For example, order has a valid signature, maker has sufficient funds, etc. Default: true
*/
export interface OrderTransactionOpts extends TransactionOpts {
- shouldValidate?: boolean;
+ shouldValidate?: boolean;
}
export type FilterObject = Web3.FilterObject;
export enum TradeSide {
- Maker = 'maker',
- Taker = 'taker',
+ Maker = 'maker',
+ Taker = 'taker',
}
export enum TransferType {
- Trade = 'trade',
- Fee = 'fee',
+ Trade = 'trade',
+ Fee = 'fee',
}
export interface OrderRelevantState {
- makerBalance: BigNumber;
- makerProxyAllowance: BigNumber;
- makerFeeBalance: BigNumber;
- makerFeeProxyAllowance: BigNumber;
- filledTakerTokenAmount: BigNumber;
- cancelledTakerTokenAmount: BigNumber;
- remainingFillableMakerTokenAmount: BigNumber;
- remainingFillableTakerTokenAmount: BigNumber;
+ makerBalance: BigNumber;
+ makerProxyAllowance: BigNumber;
+ makerFeeBalance: BigNumber;
+ makerFeeProxyAllowance: BigNumber;
+ filledTakerTokenAmount: BigNumber;
+ cancelledTakerTokenAmount: BigNumber;
+ remainingFillableMakerTokenAmount: BigNumber;
+ remainingFillableTakerTokenAmount: BigNumber;
}
export interface OrderStateValid {
- isValid: true;
- orderHash: string;
- orderRelevantState: OrderRelevantState;
+ isValid: true;
+ orderHash: string;
+ orderRelevantState: OrderRelevantState;
}
export interface OrderStateInvalid {
- isValid: false;
- orderHash: string;
- error: ExchangeContractErrs;
+ isValid: false;
+ orderHash: string;
+ error: ExchangeContractErrs;
}
export type OrderState = OrderStateValid | OrderStateInvalid;
diff --git a/packages/0x.js/src/utils/abi_decoder.ts b/packages/0x.js/src/utils/abi_decoder.ts
index ace6dd165..bbd2a0b1d 100644
--- a/packages/0x.js/src/utils/abi_decoder.ts
+++ b/packages/0x.js/src/utils/abi_decoder.ts
@@ -6,67 +6,67 @@ import * as SolidityCoder from 'web3/lib/solidity/coder';
import { AbiType, ContractEventArgs, DecodedLogArgs, LogWithDecodedArgs, RawLog, SolidityTypes } from '../types';
export class AbiDecoder {
- private _savedABIs: Web3.AbiDefinition[] = [];
- private _methodIds: { [signatureHash: string]: Web3.EventAbi } = {};
- private static _padZeros(address: string) {
- let formatted = address;
- if (_.startsWith(formatted, '0x')) {
- formatted = formatted.slice(2);
- }
+ private _savedABIs: Web3.AbiDefinition[] = [];
+ private _methodIds: { [signatureHash: string]: Web3.EventAbi } = {};
+ private static _padZeros(address: string) {
+ let formatted = address;
+ if (_.startsWith(formatted, '0x')) {
+ formatted = formatted.slice(2);
+ }
- formatted = _.padStart(formatted, 40, '0');
- return `0x${formatted}`;
- }
- constructor(abiArrays: Web3.AbiDefinition[][]) {
- _.map(abiArrays, this._addABI.bind(this));
- }
- // This method can only decode logs from the 0x & ERC20 smart contracts
- public tryToDecodeLogOrNoop<ArgsType extends ContractEventArgs>(
- log: Web3.LogEntry,
- ): LogWithDecodedArgs<ArgsType> | RawLog {
- const methodId = log.topics[0];
- const event = this._methodIds[methodId];
- if (_.isUndefined(event)) {
- return log;
- }
- const logData = log.data;
- const decodedParams: DecodedLogArgs = {};
- let dataIndex = 0;
- let topicsIndex = 1;
+ formatted = _.padStart(formatted, 40, '0');
+ return `0x${formatted}`;
+ }
+ constructor(abiArrays: Web3.AbiDefinition[][]) {
+ _.map(abiArrays, this._addABI.bind(this));
+ }
+ // This method can only decode logs from the 0x & ERC20 smart contracts
+ public tryToDecodeLogOrNoop<ArgsType extends ContractEventArgs>(
+ log: Web3.LogEntry,
+ ): LogWithDecodedArgs<ArgsType> | RawLog {
+ const methodId = log.topics[0];
+ const event = this._methodIds[methodId];
+ if (_.isUndefined(event)) {
+ return log;
+ }
+ const logData = log.data;
+ const decodedParams: DecodedLogArgs = {};
+ let dataIndex = 0;
+ let topicsIndex = 1;
- const nonIndexedInputs = _.filter(event.inputs, input => !input.indexed);
- const dataTypes = _.map(nonIndexedInputs, input => input.type);
- const decodedData = SolidityCoder.decodeParams(dataTypes, logData.slice('0x'.length));
+ const nonIndexedInputs = _.filter(event.inputs, input => !input.indexed);
+ const dataTypes = _.map(nonIndexedInputs, input => input.type);
+ const decodedData = SolidityCoder.decodeParams(dataTypes, logData.slice('0x'.length));
- _.map(event.inputs, (param: Web3.EventParameter) => {
- // Indexed parameters are stored in topics. Non-indexed ones in decodedData
- let value = param.indexed ? log.topics[topicsIndex++] : decodedData[dataIndex++];
- if (param.type === SolidityTypes.Address) {
- value = AbiDecoder._padZeros(new BigNumber(value).toString(16));
- } else if (
- param.type === SolidityTypes.Uint256 ||
- param.type === SolidityTypes.Uint8 ||
- param.type === SolidityTypes.Uint
- ) {
- value = new BigNumber(value);
- }
- decodedParams[param.name] = value;
- });
+ _.map(event.inputs, (param: Web3.EventParameter) => {
+ // Indexed parameters are stored in topics. Non-indexed ones in decodedData
+ let value = param.indexed ? log.topics[topicsIndex++] : decodedData[dataIndex++];
+ if (param.type === SolidityTypes.Address) {
+ value = AbiDecoder._padZeros(new BigNumber(value).toString(16));
+ } else if (
+ param.type === SolidityTypes.Uint256 ||
+ param.type === SolidityTypes.Uint8 ||
+ param.type === SolidityTypes.Uint
+ ) {
+ value = new BigNumber(value);
+ }
+ decodedParams[param.name] = value;
+ });
- return {
- ...log,
- event: event.name,
- args: decodedParams,
- };
- }
- private _addABI(abiArray: Web3.AbiDefinition[]): void {
- _.map(abiArray, (abi: Web3.AbiDefinition) => {
- if (abi.type === AbiType.Event) {
- const signature = `${abi.name}(${_.map(abi.inputs, input => input.type).join(',')})`;
- const signatureHash = new Web3().sha3(signature);
- this._methodIds[signatureHash] = abi;
- }
- });
- this._savedABIs = this._savedABIs.concat(abiArray);
- }
+ return {
+ ...log,
+ event: event.name,
+ args: decodedParams,
+ };
+ }
+ private _addABI(abiArray: Web3.AbiDefinition[]): void {
+ _.map(abiArray, (abi: Web3.AbiDefinition) => {
+ if (abi.type === AbiType.Event) {
+ const signature = `${abi.name}(${_.map(abi.inputs, input => input.type).join(',')})`;
+ const signatureHash = new Web3().sha3(signature);
+ this._methodIds[signatureHash] = abi;
+ }
+ });
+ this._savedABIs = this._savedABIs.concat(abiArray);
+ }
}
diff --git a/packages/0x.js/src/utils/assert.ts b/packages/0x.js/src/utils/assert.ts
index 776ee7b79..c21f2dbca 100644
--- a/packages/0x.js/src/utils/assert.ts
+++ b/packages/0x.js/src/utils/assert.ts
@@ -11,25 +11,25 @@ import { ECSignature } from '../types';
import { signatureUtils } from '../utils/signature_utils';
export const assert = {
- ...sharedAssert,
- isValidSignature(orderHash: string, ecSignature: ECSignature, signerAddress: string) {
- const isValidSignature = signatureUtils.isValidSignature(orderHash, ecSignature, signerAddress);
- this.assert(isValidSignature, `Expected order with hash '${orderHash}' to have a valid signature`);
- },
- async isSenderAddressAsync(
- variableName: string,
- senderAddressHex: string,
- web3Wrapper: Web3Wrapper,
- ): Promise<void> {
- sharedAssert.isETHAddressHex(variableName, senderAddressHex);
- const isSenderAddressAvailable = await web3Wrapper.isSenderAddressAvailableAsync(senderAddressHex);
- sharedAssert.assert(
- isSenderAddressAvailable,
- `Specified ${variableName} ${senderAddressHex} isn't available through the supplied web3 provider`,
- );
- },
- async isUserAddressAvailableAsync(web3Wrapper: Web3Wrapper): Promise<void> {
- const availableAddresses = await web3Wrapper.getAvailableAddressesAsync();
- this.assert(!_.isEmpty(availableAddresses), 'No addresses were available on the provided web3 provider');
- },
+ ...sharedAssert,
+ isValidSignature(orderHash: string, ecSignature: ECSignature, signerAddress: string) {
+ const isValidSignature = signatureUtils.isValidSignature(orderHash, ecSignature, signerAddress);
+ this.assert(isValidSignature, `Expected order with hash '${orderHash}' to have a valid signature`);
+ },
+ async isSenderAddressAsync(
+ variableName: string,
+ senderAddressHex: string,
+ web3Wrapper: Web3Wrapper,
+ ): Promise<void> {
+ sharedAssert.isETHAddressHex(variableName, senderAddressHex);
+ const isSenderAddressAvailable = await web3Wrapper.isSenderAddressAvailableAsync(senderAddressHex);
+ sharedAssert.assert(
+ isSenderAddressAvailable,
+ `Specified ${variableName} ${senderAddressHex} isn't available through the supplied web3 provider`,
+ );
+ },
+ async isUserAddressAvailableAsync(web3Wrapper: Web3Wrapper): Promise<void> {
+ const availableAddresses = await web3Wrapper.getAvailableAddressesAsync();
+ this.assert(!_.isEmpty(availableAddresses), 'No addresses were available on the provided web3 provider');
+ },
};
diff --git a/packages/0x.js/src/utils/constants.ts b/packages/0x.js/src/utils/constants.ts
index f32847ec2..06beec8e2 100644
--- a/packages/0x.js/src/utils/constants.ts
+++ b/packages/0x.js/src/utils/constants.ts
@@ -1,12 +1,12 @@
import { BigNumber } from '@0xproject/utils';
export const constants = {
- NULL_ADDRESS: '0x0000000000000000000000000000000000000000',
- TESTRPC_NETWORK_ID: 50,
- MAX_DIGITS_IN_UNSIGNED_256_INT: 78,
- INVALID_JUMP_PATTERN: 'invalid JUMP at',
- OUT_OF_GAS_PATTERN: 'out of gas',
- INVALID_TAKER_FORMAT: 'instance.taker is not of a type(s) string',
- UNLIMITED_ALLOWANCE_IN_BASE_UNITS: new BigNumber(2).pow(256).minus(1),
- DEFAULT_BLOCK_POLLING_INTERVAL: 1000,
+ NULL_ADDRESS: '0x0000000000000000000000000000000000000000',
+ TESTRPC_NETWORK_ID: 50,
+ MAX_DIGITS_IN_UNSIGNED_256_INT: 78,
+ INVALID_JUMP_PATTERN: 'invalid JUMP at',
+ OUT_OF_GAS_PATTERN: 'out of gas',
+ INVALID_TAKER_FORMAT: 'instance.taker is not of a type(s) string',
+ UNLIMITED_ALLOWANCE_IN_BASE_UNITS: new BigNumber(2).pow(256).minus(1),
+ DEFAULT_BLOCK_POLLING_INTERVAL: 1000,
};
diff --git a/packages/0x.js/src/utils/decorators.ts b/packages/0x.js/src/utils/decorators.ts
index c1a022a81..f774d734e 100644
--- a/packages/0x.js/src/utils/decorators.ts
+++ b/packages/0x.js/src/utils/decorators.ts
@@ -7,85 +7,85 @@ import { constants } from './constants';
type ErrorTransformer = (err: Error) => Error;
const contractCallErrorTransformer = (error: Error) => {
- if (_.includes(error.message, constants.INVALID_JUMP_PATTERN)) {
- return new Error(ZeroExError.InvalidJump);
- }
- if (_.includes(error.message, constants.OUT_OF_GAS_PATTERN)) {
- return new Error(ZeroExError.OutOfGas);
- }
- return error;
+ if (_.includes(error.message, constants.INVALID_JUMP_PATTERN)) {
+ return new Error(ZeroExError.InvalidJump);
+ }
+ if (_.includes(error.message, constants.OUT_OF_GAS_PATTERN)) {
+ return new Error(ZeroExError.OutOfGas);
+ }
+ return error;
};
const schemaErrorTransformer = (error: Error) => {
- if (_.includes(error.message, constants.INVALID_TAKER_FORMAT)) {
- const errMsg =
- 'Order taker must be of type string. If you want anyone to be able to fill an order - pass ZeroEx.NULL_ADDRESS';
- return new Error(errMsg);
- }
- return error;
+ if (_.includes(error.message, constants.INVALID_TAKER_FORMAT)) {
+ const errMsg =
+ 'Order taker must be of type string. If you want anyone to be able to fill an order - pass ZeroEx.NULL_ADDRESS';
+ return new Error(errMsg);
+ }
+ return error;
};
/**
* Source: https://stackoverflow.com/a/29837695/3546986
*/
const asyncErrorHandlerFactory = (errorTransformer: ErrorTransformer) => {
- const asyncErrorHandlingDecorator = (
- target: object,
- key: string | symbol,
- descriptor: TypedPropertyDescriptor<AsyncMethod>,
- ) => {
- const originalMethod = descriptor.value as AsyncMethod;
+ const asyncErrorHandlingDecorator = (
+ target: object,
+ key: string | symbol,
+ descriptor: TypedPropertyDescriptor<AsyncMethod>,
+ ) => {
+ const originalMethod = descriptor.value as AsyncMethod;
- // Do not use arrow syntax here. Use a function expression in
- // order to use the correct value of `this` in this method
- // tslint:disable-next-line:only-arrow-functions
- descriptor.value = async function(...args: any[]) {
- try {
- const result = await originalMethod.apply(this, args);
- return result;
- } catch (error) {
- const transformedError = errorTransformer(error);
- throw transformedError;
- }
- };
+ // Do not use arrow syntax here. Use a function expression in
+ // order to use the correct value of `this` in this method
+ // tslint:disable-next-line:only-arrow-functions
+ descriptor.value = async function(...args: any[]) {
+ try {
+ const result = await originalMethod.apply(this, args);
+ return result;
+ } catch (error) {
+ const transformedError = errorTransformer(error);
+ throw transformedError;
+ }
+ };
- return descriptor;
- };
+ return descriptor;
+ };
- return asyncErrorHandlingDecorator;
+ return asyncErrorHandlingDecorator;
};
const syncErrorHandlerFactory = (errorTransformer: ErrorTransformer) => {
- const syncErrorHandlingDecorator = (
- target: object,
- key: string | symbol,
- descriptor: TypedPropertyDescriptor<SyncMethod>,
- ) => {
- const originalMethod = descriptor.value as SyncMethod;
+ const syncErrorHandlingDecorator = (
+ target: object,
+ key: string | symbol,
+ descriptor: TypedPropertyDescriptor<SyncMethod>,
+ ) => {
+ const originalMethod = descriptor.value as SyncMethod;
- // Do not use arrow syntax here. Use a function expression in
- // order to use the correct value of `this` in this method
- // tslint:disable-next-line:only-arrow-functions
- descriptor.value = function(...args: any[]) {
- try {
- const result = originalMethod.apply(this, args);
- return result;
- } catch (error) {
- const transformedError = errorTransformer(error);
- throw transformedError;
- }
- };
+ // Do not use arrow syntax here. Use a function expression in
+ // order to use the correct value of `this` in this method
+ // tslint:disable-next-line:only-arrow-functions
+ descriptor.value = function(...args: any[]) {
+ try {
+ const result = originalMethod.apply(this, args);
+ return result;
+ } catch (error) {
+ const transformedError = errorTransformer(error);
+ throw transformedError;
+ }
+ };
- return descriptor;
- };
+ return descriptor;
+ };
- return syncErrorHandlingDecorator;
+ return syncErrorHandlingDecorator;
};
// _.flow(f, g) = f ∘ g
const zeroExErrorTransformer = _.flow(schemaErrorTransformer, contractCallErrorTransformer);
export const decorators = {
- asyncZeroExErrorHandler: asyncErrorHandlerFactory(zeroExErrorTransformer),
- syncZeroExErrorHandler: syncErrorHandlerFactory(zeroExErrorTransformer),
+ asyncZeroExErrorHandler: asyncErrorHandlerFactory(zeroExErrorTransformer),
+ syncZeroExErrorHandler: syncErrorHandlerFactory(zeroExErrorTransformer),
};
diff --git a/packages/0x.js/src/utils/exchange_transfer_simulator.ts b/packages/0x.js/src/utils/exchange_transfer_simulator.ts
index c9a2f87ce..662cd210c 100644
--- a/packages/0x.js/src/utils/exchange_transfer_simulator.ts
+++ b/packages/0x.js/src/utils/exchange_transfer_simulator.ts
@@ -6,101 +6,101 @@ import { BalanceAndProxyAllowanceLazyStore } from '../stores/balance_proxy_allow
import { BlockParamLiteral, ExchangeContractErrs, TradeSide, TransferType } from '../types';
enum FailureReason {
- Balance = 'balance',
- ProxyAllowance = 'proxyAllowance',
+ Balance = 'balance',
+ ProxyAllowance = 'proxyAllowance',
}
const ERR_MSG_MAPPING = {
- [FailureReason.Balance]: {
- [TradeSide.Maker]: {
- [TransferType.Trade]: ExchangeContractErrs.InsufficientMakerBalance,
- [TransferType.Fee]: ExchangeContractErrs.InsufficientMakerFeeBalance,
- },
- [TradeSide.Taker]: {
- [TransferType.Trade]: ExchangeContractErrs.InsufficientTakerBalance,
- [TransferType.Fee]: ExchangeContractErrs.InsufficientTakerFeeBalance,
- },
- },
- [FailureReason.ProxyAllowance]: {
- [TradeSide.Maker]: {
- [TransferType.Trade]: ExchangeContractErrs.InsufficientMakerAllowance,
- [TransferType.Fee]: ExchangeContractErrs.InsufficientMakerFeeAllowance,
- },
- [TradeSide.Taker]: {
- [TransferType.Trade]: ExchangeContractErrs.InsufficientTakerAllowance,
- [TransferType.Fee]: ExchangeContractErrs.InsufficientTakerFeeAllowance,
- },
- },
+ [FailureReason.Balance]: {
+ [TradeSide.Maker]: {
+ [TransferType.Trade]: ExchangeContractErrs.InsufficientMakerBalance,
+ [TransferType.Fee]: ExchangeContractErrs.InsufficientMakerFeeBalance,
+ },
+ [TradeSide.Taker]: {
+ [TransferType.Trade]: ExchangeContractErrs.InsufficientTakerBalance,
+ [TransferType.Fee]: ExchangeContractErrs.InsufficientTakerFeeBalance,
+ },
+ },
+ [FailureReason.ProxyAllowance]: {
+ [TradeSide.Maker]: {
+ [TransferType.Trade]: ExchangeContractErrs.InsufficientMakerAllowance,
+ [TransferType.Fee]: ExchangeContractErrs.InsufficientMakerFeeAllowance,
+ },
+ [TradeSide.Taker]: {
+ [TransferType.Trade]: ExchangeContractErrs.InsufficientTakerAllowance,
+ [TransferType.Fee]: ExchangeContractErrs.InsufficientTakerFeeAllowance,
+ },
+ },
};
export class ExchangeTransferSimulator {
- private _store: BalanceAndProxyAllowanceLazyStore;
- private _UNLIMITED_ALLOWANCE_IN_BASE_UNITS: BigNumber;
- private static _throwValidationError(
- failureReason: FailureReason,
- tradeSide: TradeSide,
- transferType: TransferType,
- ): never {
- const errMsg = ERR_MSG_MAPPING[failureReason][tradeSide][transferType];
- throw new Error(errMsg);
- }
- constructor(token: TokenWrapper, defaultBlock: BlockParamLiteral) {
- this._store = new BalanceAndProxyAllowanceLazyStore(token, defaultBlock);
- this._UNLIMITED_ALLOWANCE_IN_BASE_UNITS = token.UNLIMITED_ALLOWANCE_IN_BASE_UNITS;
- }
- /**
- * Simulates transferFrom call performed by a proxy
- * @param tokenAddress Address of the token to be transferred
- * @param from Owner of the transferred tokens
- * @param to Recipient of the transferred tokens
- * @param amountInBaseUnits The amount of tokens being transferred
- * @param tradeSide Is Maker/Taker transferring
- * @param transferType Is it a fee payment or a value transfer
- */
- public async transferFromAsync(
- tokenAddress: string,
- from: string,
- to: string,
- amountInBaseUnits: BigNumber,
- tradeSide: TradeSide,
- transferType: TransferType,
- ): Promise<void> {
- const balance = await this._store.getBalanceAsync(tokenAddress, from);
- const proxyAllowance = await this._store.getProxyAllowanceAsync(tokenAddress, from);
- if (proxyAllowance.lessThan(amountInBaseUnits)) {
- ExchangeTransferSimulator._throwValidationError(FailureReason.ProxyAllowance, tradeSide, transferType);
- }
- if (balance.lessThan(amountInBaseUnits)) {
- ExchangeTransferSimulator._throwValidationError(FailureReason.Balance, tradeSide, transferType);
- }
- await this._decreaseProxyAllowanceAsync(tokenAddress, from, amountInBaseUnits);
- await this._decreaseBalanceAsync(tokenAddress, from, amountInBaseUnits);
- await this._increaseBalanceAsync(tokenAddress, to, amountInBaseUnits);
- }
- private async _decreaseProxyAllowanceAsync(
- tokenAddress: string,
- userAddress: string,
- amountInBaseUnits: BigNumber,
- ): Promise<void> {
- const proxyAllowance = await this._store.getProxyAllowanceAsync(tokenAddress, userAddress);
- if (!proxyAllowance.eq(this._UNLIMITED_ALLOWANCE_IN_BASE_UNITS)) {
- this._store.setProxyAllowance(tokenAddress, userAddress, proxyAllowance.minus(amountInBaseUnits));
- }
- }
- private async _increaseBalanceAsync(
- tokenAddress: string,
- userAddress: string,
- amountInBaseUnits: BigNumber,
- ): Promise<void> {
- const balance = await this._store.getBalanceAsync(tokenAddress, userAddress);
- this._store.setBalance(tokenAddress, userAddress, balance.plus(amountInBaseUnits));
- }
- private async _decreaseBalanceAsync(
- tokenAddress: string,
- userAddress: string,
- amountInBaseUnits: BigNumber,
- ): Promise<void> {
- const balance = await this._store.getBalanceAsync(tokenAddress, userAddress);
- this._store.setBalance(tokenAddress, userAddress, balance.minus(amountInBaseUnits));
- }
+ private _store: BalanceAndProxyAllowanceLazyStore;
+ private _UNLIMITED_ALLOWANCE_IN_BASE_UNITS: BigNumber;
+ private static _throwValidationError(
+ failureReason: FailureReason,
+ tradeSide: TradeSide,
+ transferType: TransferType,
+ ): never {
+ const errMsg = ERR_MSG_MAPPING[failureReason][tradeSide][transferType];
+ throw new Error(errMsg);
+ }
+ constructor(token: TokenWrapper, defaultBlock: BlockParamLiteral) {
+ this._store = new BalanceAndProxyAllowanceLazyStore(token, defaultBlock);
+ this._UNLIMITED_ALLOWANCE_IN_BASE_UNITS = token.UNLIMITED_ALLOWANCE_IN_BASE_UNITS;
+ }
+ /**
+ * Simulates transferFrom call performed by a proxy
+ * @param tokenAddress Address of the token to be transferred
+ * @param from Owner of the transferred tokens
+ * @param to Recipient of the transferred tokens
+ * @param amountInBaseUnits The amount of tokens being transferred
+ * @param tradeSide Is Maker/Taker transferring
+ * @param transferType Is it a fee payment or a value transfer
+ */
+ public async transferFromAsync(
+ tokenAddress: string,
+ from: string,
+ to: string,
+ amountInBaseUnits: BigNumber,
+ tradeSide: TradeSide,
+ transferType: TransferType,
+ ): Promise<void> {
+ const balance = await this._store.getBalanceAsync(tokenAddress, from);
+ const proxyAllowance = await this._store.getProxyAllowanceAsync(tokenAddress, from);
+ if (proxyAllowance.lessThan(amountInBaseUnits)) {
+ ExchangeTransferSimulator._throwValidationError(FailureReason.ProxyAllowance, tradeSide, transferType);
+ }
+ if (balance.lessThan(amountInBaseUnits)) {
+ ExchangeTransferSimulator._throwValidationError(FailureReason.Balance, tradeSide, transferType);
+ }
+ await this._decreaseProxyAllowanceAsync(tokenAddress, from, amountInBaseUnits);
+ await this._decreaseBalanceAsync(tokenAddress, from, amountInBaseUnits);
+ await this._increaseBalanceAsync(tokenAddress, to, amountInBaseUnits);
+ }
+ private async _decreaseProxyAllowanceAsync(
+ tokenAddress: string,
+ userAddress: string,
+ amountInBaseUnits: BigNumber,
+ ): Promise<void> {
+ const proxyAllowance = await this._store.getProxyAllowanceAsync(tokenAddress, userAddress);
+ if (!proxyAllowance.eq(this._UNLIMITED_ALLOWANCE_IN_BASE_UNITS)) {
+ this._store.setProxyAllowance(tokenAddress, userAddress, proxyAllowance.minus(amountInBaseUnits));
+ }
+ }
+ private async _increaseBalanceAsync(
+ tokenAddress: string,
+ userAddress: string,
+ amountInBaseUnits: BigNumber,
+ ): Promise<void> {
+ const balance = await this._store.getBalanceAsync(tokenAddress, userAddress);
+ this._store.setBalance(tokenAddress, userAddress, balance.plus(amountInBaseUnits));
+ }
+ private async _decreaseBalanceAsync(
+ tokenAddress: string,
+ userAddress: string,
+ amountInBaseUnits: BigNumber,
+ ): Promise<void> {
+ const balance = await this._store.getBalanceAsync(tokenAddress, userAddress);
+ this._store.setBalance(tokenAddress, userAddress, balance.minus(amountInBaseUnits));
+ }
}
diff --git a/packages/0x.js/src/utils/filter_utils.ts b/packages/0x.js/src/utils/filter_utils.ts
index c34ae8893..97205ace3 100644
--- a/packages/0x.js/src/utils/filter_utils.ts
+++ b/packages/0x.js/src/utils/filter_utils.ts
@@ -9,79 +9,79 @@ import { BlockRange, ContractEvents, IndexedFilterValues } from '../types';
const TOPIC_LENGTH = 32;
export const filterUtils = {
- generateUUID(): string {
- return uuid();
- },
- getFilter(
- address: string,
- eventName: ContractEvents,
- indexFilterValues: IndexedFilterValues,
- abi: Web3.ContractAbi,
- blockRange?: BlockRange,
- ): Web3.FilterObject {
- const eventAbi = _.find(abi, { name: eventName }) as Web3.EventAbi;
- const eventSignature = filterUtils.getEventSignatureFromAbiByName(eventAbi, eventName);
- const topicForEventSignature = ethUtil.addHexPrefix(jsSHA3.keccak256(eventSignature));
- const topicsForIndexedArgs = filterUtils.getTopicsForIndexedArgs(eventAbi, indexFilterValues);
- const topics = [topicForEventSignature, ...topicsForIndexedArgs];
- let filter: Web3.FilterObject = {
- address,
- topics,
- };
- if (!_.isUndefined(blockRange)) {
- filter = {
- ...blockRange,
- ...filter,
- };
- }
- return filter;
- },
- getEventSignatureFromAbiByName(eventAbi: Web3.EventAbi, eventName: ContractEvents): string {
- const types = _.map(eventAbi.inputs, 'type');
- const signature = `${eventAbi.name}(${types.join(',')})`;
- return signature;
- },
- getTopicsForIndexedArgs(abi: Web3.EventAbi, indexFilterValues: IndexedFilterValues): Array<string | null> {
- const topics: Array<string | null> = [];
- for (const eventInput of abi.inputs) {
- if (!eventInput.indexed) {
- continue;
- }
- if (_.isUndefined(indexFilterValues[eventInput.name])) {
- // Null is a wildcard topic in a JSON-RPC call
- topics.push(null);
- } else {
- const value = indexFilterValues[eventInput.name] as string;
- const buffer = ethUtil.toBuffer(value);
- const paddedBuffer = ethUtil.setLengthLeft(buffer, TOPIC_LENGTH);
- const topic = ethUtil.bufferToHex(paddedBuffer);
- topics.push(topic);
- }
- }
- return topics;
- },
- matchesFilter(log: Web3.LogEntry, filter: Web3.FilterObject): boolean {
- if (!_.isUndefined(filter.address) && log.address !== filter.address) {
- return false;
- }
- if (!_.isUndefined(filter.topics)) {
- return filterUtils.matchesTopics(log.topics, filter.topics);
- }
- return true;
- },
- matchesTopics(logTopics: string[], filterTopics: Array<string[] | string | null>): boolean {
- const matchesTopic = _.zipWith(logTopics, filterTopics, filterUtils.matchesTopic.bind(filterUtils));
- const matchesTopics = _.every(matchesTopic);
- return matchesTopics;
- },
- matchesTopic(logTopic: string, filterTopic: string[] | string | null): boolean {
- if (_.isArray(filterTopic)) {
- return _.includes(filterTopic, logTopic);
- }
- if (_.isString(filterTopic)) {
- return filterTopic === logTopic;
- }
- // null topic is a wildcard
- return true;
- },
+ generateUUID(): string {
+ return uuid();
+ },
+ getFilter(
+ address: string,
+ eventName: ContractEvents,
+ indexFilterValues: IndexedFilterValues,
+ abi: Web3.ContractAbi,
+ blockRange?: BlockRange,
+ ): Web3.FilterObject {
+ const eventAbi = _.find(abi, { name: eventName }) as Web3.EventAbi;
+ const eventSignature = filterUtils.getEventSignatureFromAbiByName(eventAbi, eventName);
+ const topicForEventSignature = ethUtil.addHexPrefix(jsSHA3.keccak256(eventSignature));
+ const topicsForIndexedArgs = filterUtils.getTopicsForIndexedArgs(eventAbi, indexFilterValues);
+ const topics = [topicForEventSignature, ...topicsForIndexedArgs];
+ let filter: Web3.FilterObject = {
+ address,
+ topics,
+ };
+ if (!_.isUndefined(blockRange)) {
+ filter = {
+ ...blockRange,
+ ...filter,
+ };
+ }
+ return filter;
+ },
+ getEventSignatureFromAbiByName(eventAbi: Web3.EventAbi, eventName: ContractEvents): string {
+ const types = _.map(eventAbi.inputs, 'type');
+ const signature = `${eventAbi.name}(${types.join(',')})`;
+ return signature;
+ },
+ getTopicsForIndexedArgs(abi: Web3.EventAbi, indexFilterValues: IndexedFilterValues): Array<string | null> {
+ const topics: Array<string | null> = [];
+ for (const eventInput of abi.inputs) {
+ if (!eventInput.indexed) {
+ continue;
+ }
+ if (_.isUndefined(indexFilterValues[eventInput.name])) {
+ // Null is a wildcard topic in a JSON-RPC call
+ topics.push(null);
+ } else {
+ const value = indexFilterValues[eventInput.name] as string;
+ const buffer = ethUtil.toBuffer(value);
+ const paddedBuffer = ethUtil.setLengthLeft(buffer, TOPIC_LENGTH);
+ const topic = ethUtil.bufferToHex(paddedBuffer);
+ topics.push(topic);
+ }
+ }
+ return topics;
+ },
+ matchesFilter(log: Web3.LogEntry, filter: Web3.FilterObject): boolean {
+ if (!_.isUndefined(filter.address) && log.address !== filter.address) {
+ return false;
+ }
+ if (!_.isUndefined(filter.topics)) {
+ return filterUtils.matchesTopics(log.topics, filter.topics);
+ }
+ return true;
+ },
+ matchesTopics(logTopics: string[], filterTopics: Array<string[] | string | null>): boolean {
+ const matchesTopic = _.zipWith(logTopics, filterTopics, filterUtils.matchesTopic.bind(filterUtils));
+ const matchesTopics = _.every(matchesTopic);
+ return matchesTopics;
+ },
+ matchesTopic(logTopic: string, filterTopic: string[] | string | null): boolean {
+ if (_.isArray(filterTopic)) {
+ return _.includes(filterTopic, logTopic);
+ }
+ if (_.isString(filterTopic)) {
+ return filterTopic === logTopic;
+ }
+ // null topic is a wildcard
+ return true;
+ },
};
diff --git a/packages/0x.js/src/utils/order_state_utils.ts b/packages/0x.js/src/utils/order_state_utils.ts
index 04d3b641e..b7a55ff42 100644
--- a/packages/0x.js/src/utils/order_state_utils.ts
+++ b/packages/0x.js/src/utils/order_state_utils.ts
@@ -7,138 +7,138 @@ import { RemainingFillableCalculator } from '../order_watcher/remaining_fillable
import { BalanceAndProxyAllowanceLazyStore } from '../stores/balance_proxy_allowance_lazy_store';
import { OrderFilledCancelledLazyStore } from '../stores/order_filled_cancelled_lazy_store';
import {
- ExchangeContractErrs,
- OrderRelevantState,
- OrderState,
- OrderStateInvalid,
- OrderStateValid,
- SignedOrder,
+ ExchangeContractErrs,
+ OrderRelevantState,
+ OrderState,
+ OrderStateInvalid,
+ OrderStateValid,
+ SignedOrder,
} from '../types';
const ACCEPTABLE_RELATIVE_ROUNDING_ERROR = 0.0001;
export class OrderStateUtils {
- private _balanceAndProxyAllowanceLazyStore: BalanceAndProxyAllowanceLazyStore;
- private _orderFilledCancelledLazyStore: OrderFilledCancelledLazyStore;
- private static _validateIfOrderIsValid(signedOrder: SignedOrder, orderRelevantState: OrderRelevantState): void {
- const unavailableTakerTokenAmount = orderRelevantState.cancelledTakerTokenAmount.add(
- orderRelevantState.filledTakerTokenAmount,
- );
- const availableTakerTokenAmount = signedOrder.takerTokenAmount.minus(unavailableTakerTokenAmount);
- if (availableTakerTokenAmount.eq(0)) {
- throw new Error(ExchangeContractErrs.OrderRemainingFillAmountZero);
- }
+ private _balanceAndProxyAllowanceLazyStore: BalanceAndProxyAllowanceLazyStore;
+ private _orderFilledCancelledLazyStore: OrderFilledCancelledLazyStore;
+ private static _validateIfOrderIsValid(signedOrder: SignedOrder, orderRelevantState: OrderRelevantState): void {
+ const unavailableTakerTokenAmount = orderRelevantState.cancelledTakerTokenAmount.add(
+ orderRelevantState.filledTakerTokenAmount,
+ );
+ const availableTakerTokenAmount = signedOrder.takerTokenAmount.minus(unavailableTakerTokenAmount);
+ if (availableTakerTokenAmount.eq(0)) {
+ throw new Error(ExchangeContractErrs.OrderRemainingFillAmountZero);
+ }
- if (orderRelevantState.makerBalance.eq(0)) {
- throw new Error(ExchangeContractErrs.InsufficientMakerBalance);
- }
- if (orderRelevantState.makerProxyAllowance.eq(0)) {
- throw new Error(ExchangeContractErrs.InsufficientMakerAllowance);
- }
- if (!signedOrder.makerFee.eq(0)) {
- if (orderRelevantState.makerFeeBalance.eq(0)) {
- throw new Error(ExchangeContractErrs.InsufficientMakerFeeBalance);
- }
- if (orderRelevantState.makerFeeProxyAllowance.eq(0)) {
- throw new Error(ExchangeContractErrs.InsufficientMakerFeeAllowance);
- }
- }
- const minFillableTakerTokenAmountWithinNoRoundingErrorRange = signedOrder.takerTokenAmount
- .dividedBy(ACCEPTABLE_RELATIVE_ROUNDING_ERROR)
- .dividedBy(signedOrder.makerTokenAmount);
- if (
- orderRelevantState.remainingFillableTakerTokenAmount.lessThan(
- minFillableTakerTokenAmountWithinNoRoundingErrorRange,
- )
- ) {
- throw new Error(ExchangeContractErrs.OrderFillRoundingError);
- }
- }
- constructor(
- balanceAndProxyAllowanceLazyStore: BalanceAndProxyAllowanceLazyStore,
- orderFilledCancelledLazyStore: OrderFilledCancelledLazyStore,
- ) {
- this._balanceAndProxyAllowanceLazyStore = balanceAndProxyAllowanceLazyStore;
- this._orderFilledCancelledLazyStore = orderFilledCancelledLazyStore;
- }
- public async getOrderStateAsync(signedOrder: SignedOrder): Promise<OrderState> {
- const orderRelevantState = await this.getOrderRelevantStateAsync(signedOrder);
- const orderHash = ZeroEx.getOrderHashHex(signedOrder);
- try {
- OrderStateUtils._validateIfOrderIsValid(signedOrder, orderRelevantState);
- const orderState: OrderStateValid = {
- isValid: true,
- orderHash,
- orderRelevantState,
- };
- return orderState;
- } catch (err) {
- const orderState: OrderStateInvalid = {
- isValid: false,
- orderHash,
- error: err.message,
- };
- return orderState;
- }
- }
- public async getOrderRelevantStateAsync(signedOrder: SignedOrder): Promise<OrderRelevantState> {
- // HACK: We access the private property here but otherwise the interface will be less nice.
- // If we pass it from the instantiator - there is no opportunity to get it there
- // because JS doesn't support async constructors.
- // Moreover - it's cached under the hood so it's equivalent to an async constructor.
- const exchange = (this._orderFilledCancelledLazyStore as any)._exchange as ExchangeWrapper;
- const zrxTokenAddress = exchange.getZRXTokenAddress();
- const orderHash = ZeroEx.getOrderHashHex(signedOrder);
- const makerBalance = await this._balanceAndProxyAllowanceLazyStore.getBalanceAsync(
- signedOrder.makerTokenAddress,
- signedOrder.maker,
- );
- const makerProxyAllowance = await this._balanceAndProxyAllowanceLazyStore.getProxyAllowanceAsync(
- signedOrder.makerTokenAddress,
- signedOrder.maker,
- );
- const makerFeeBalance = await this._balanceAndProxyAllowanceLazyStore.getBalanceAsync(
- zrxTokenAddress,
- signedOrder.maker,
- );
- const makerFeeProxyAllowance = await this._balanceAndProxyAllowanceLazyStore.getProxyAllowanceAsync(
- zrxTokenAddress,
- signedOrder.maker,
- );
- const filledTakerTokenAmount = await this._orderFilledCancelledLazyStore.getFilledTakerAmountAsync(orderHash);
- const cancelledTakerTokenAmount = await this._orderFilledCancelledLazyStore.getCancelledTakerAmountAsync(
- orderHash,
- );
- const unavailableTakerTokenAmount = await exchange.getUnavailableTakerAmountAsync(orderHash);
- const totalMakerTokenAmount = signedOrder.makerTokenAmount;
- const totalTakerTokenAmount = signedOrder.takerTokenAmount;
- const remainingTakerTokenAmount = totalTakerTokenAmount.minus(unavailableTakerTokenAmount);
- const remainingMakerTokenAmount = remainingTakerTokenAmount
- .times(totalMakerTokenAmount)
- .dividedToIntegerBy(totalTakerTokenAmount);
- const transferrableMakerTokenAmount = BigNumber.min([makerProxyAllowance, makerBalance]);
- const transferrableFeeTokenAmount = BigNumber.min([makerFeeProxyAllowance, makerFeeBalance]);
+ if (orderRelevantState.makerBalance.eq(0)) {
+ throw new Error(ExchangeContractErrs.InsufficientMakerBalance);
+ }
+ if (orderRelevantState.makerProxyAllowance.eq(0)) {
+ throw new Error(ExchangeContractErrs.InsufficientMakerAllowance);
+ }
+ if (!signedOrder.makerFee.eq(0)) {
+ if (orderRelevantState.makerFeeBalance.eq(0)) {
+ throw new Error(ExchangeContractErrs.InsufficientMakerFeeBalance);
+ }
+ if (orderRelevantState.makerFeeProxyAllowance.eq(0)) {
+ throw new Error(ExchangeContractErrs.InsufficientMakerFeeAllowance);
+ }
+ }
+ const minFillableTakerTokenAmountWithinNoRoundingErrorRange = signedOrder.takerTokenAmount
+ .dividedBy(ACCEPTABLE_RELATIVE_ROUNDING_ERROR)
+ .dividedBy(signedOrder.makerTokenAmount);
+ if (
+ orderRelevantState.remainingFillableTakerTokenAmount.lessThan(
+ minFillableTakerTokenAmountWithinNoRoundingErrorRange,
+ )
+ ) {
+ throw new Error(ExchangeContractErrs.OrderFillRoundingError);
+ }
+ }
+ constructor(
+ balanceAndProxyAllowanceLazyStore: BalanceAndProxyAllowanceLazyStore,
+ orderFilledCancelledLazyStore: OrderFilledCancelledLazyStore,
+ ) {
+ this._balanceAndProxyAllowanceLazyStore = balanceAndProxyAllowanceLazyStore;
+ this._orderFilledCancelledLazyStore = orderFilledCancelledLazyStore;
+ }
+ public async getOrderStateAsync(signedOrder: SignedOrder): Promise<OrderState> {
+ const orderRelevantState = await this.getOrderRelevantStateAsync(signedOrder);
+ const orderHash = ZeroEx.getOrderHashHex(signedOrder);
+ try {
+ OrderStateUtils._validateIfOrderIsValid(signedOrder, orderRelevantState);
+ const orderState: OrderStateValid = {
+ isValid: true,
+ orderHash,
+ orderRelevantState,
+ };
+ return orderState;
+ } catch (err) {
+ const orderState: OrderStateInvalid = {
+ isValid: false,
+ orderHash,
+ error: err.message,
+ };
+ return orderState;
+ }
+ }
+ public async getOrderRelevantStateAsync(signedOrder: SignedOrder): Promise<OrderRelevantState> {
+ // HACK: We access the private property here but otherwise the interface will be less nice.
+ // If we pass it from the instantiator - there is no opportunity to get it there
+ // because JS doesn't support async constructors.
+ // Moreover - it's cached under the hood so it's equivalent to an async constructor.
+ const exchange = (this._orderFilledCancelledLazyStore as any)._exchange as ExchangeWrapper;
+ const zrxTokenAddress = exchange.getZRXTokenAddress();
+ const orderHash = ZeroEx.getOrderHashHex(signedOrder);
+ const makerBalance = await this._balanceAndProxyAllowanceLazyStore.getBalanceAsync(
+ signedOrder.makerTokenAddress,
+ signedOrder.maker,
+ );
+ const makerProxyAllowance = await this._balanceAndProxyAllowanceLazyStore.getProxyAllowanceAsync(
+ signedOrder.makerTokenAddress,
+ signedOrder.maker,
+ );
+ const makerFeeBalance = await this._balanceAndProxyAllowanceLazyStore.getBalanceAsync(
+ zrxTokenAddress,
+ signedOrder.maker,
+ );
+ const makerFeeProxyAllowance = await this._balanceAndProxyAllowanceLazyStore.getProxyAllowanceAsync(
+ zrxTokenAddress,
+ signedOrder.maker,
+ );
+ const filledTakerTokenAmount = await this._orderFilledCancelledLazyStore.getFilledTakerAmountAsync(orderHash);
+ const cancelledTakerTokenAmount = await this._orderFilledCancelledLazyStore.getCancelledTakerAmountAsync(
+ orderHash,
+ );
+ const unavailableTakerTokenAmount = await exchange.getUnavailableTakerAmountAsync(orderHash);
+ const totalMakerTokenAmount = signedOrder.makerTokenAmount;
+ const totalTakerTokenAmount = signedOrder.takerTokenAmount;
+ const remainingTakerTokenAmount = totalTakerTokenAmount.minus(unavailableTakerTokenAmount);
+ const remainingMakerTokenAmount = remainingTakerTokenAmount
+ .times(totalMakerTokenAmount)
+ .dividedToIntegerBy(totalTakerTokenAmount);
+ const transferrableMakerTokenAmount = BigNumber.min([makerProxyAllowance, makerBalance]);
+ const transferrableFeeTokenAmount = BigNumber.min([makerFeeProxyAllowance, makerFeeBalance]);
- const isMakerTokenZRX = signedOrder.makerTokenAddress === zrxTokenAddress;
- const remainingFillableCalculator = new RemainingFillableCalculator(
- signedOrder,
- isMakerTokenZRX,
- transferrableMakerTokenAmount,
- transferrableFeeTokenAmount,
- remainingMakerTokenAmount,
- );
- const remainingFillableMakerTokenAmount = remainingFillableCalculator.computeRemainingMakerFillable();
- const remainingFillableTakerTokenAmount = remainingFillableCalculator.computeRemainingTakerFillable();
- const orderRelevantState = {
- makerBalance,
- makerProxyAllowance,
- makerFeeBalance,
- makerFeeProxyAllowance,
- filledTakerTokenAmount,
- cancelledTakerTokenAmount,
- remainingFillableMakerTokenAmount,
- remainingFillableTakerTokenAmount,
- };
- return orderRelevantState;
- }
+ const isMakerTokenZRX = signedOrder.makerTokenAddress === zrxTokenAddress;
+ const remainingFillableCalculator = new RemainingFillableCalculator(
+ signedOrder,
+ isMakerTokenZRX,
+ transferrableMakerTokenAmount,
+ transferrableFeeTokenAmount,
+ remainingMakerTokenAmount,
+ );
+ const remainingFillableMakerTokenAmount = remainingFillableCalculator.computeRemainingMakerFillable();
+ const remainingFillableTakerTokenAmount = remainingFillableCalculator.computeRemainingTakerFillable();
+ const orderRelevantState = {
+ makerBalance,
+ makerProxyAllowance,
+ makerFeeBalance,
+ makerFeeProxyAllowance,
+ filledTakerTokenAmount,
+ cancelledTakerTokenAmount,
+ remainingFillableMakerTokenAmount,
+ remainingFillableTakerTokenAmount,
+ };
+ return orderRelevantState;
+ }
}
diff --git a/packages/0x.js/src/utils/order_validation_utils.ts b/packages/0x.js/src/utils/order_validation_utils.ts
index 8ff22266f..917d414c8 100644
--- a/packages/0x.js/src/utils/order_validation_utils.ts
+++ b/packages/0x.js/src/utils/order_validation_utils.ts
@@ -10,207 +10,207 @@ import { utils } from '../utils/utils';
import { ExchangeTransferSimulator } from './exchange_transfer_simulator';
export class OrderValidationUtils {
- private _exchangeWrapper: ExchangeWrapper;
- public static validateCancelOrderThrowIfInvalid(
- order: Order,
- cancelTakerTokenAmount: BigNumber,
- unavailableTakerTokenAmount: BigNumber,
- ): void {
- if (cancelTakerTokenAmount.eq(0)) {
- throw new Error(ExchangeContractErrs.OrderCancelAmountZero);
- }
- if (order.takerTokenAmount.eq(unavailableTakerTokenAmount)) {
- throw new Error(ExchangeContractErrs.OrderAlreadyCancelledOrFilled);
- }
- const currentUnixTimestampSec = utils.getCurrentUnixTimestampSec();
- if (order.expirationUnixTimestampSec.lessThan(currentUnixTimestampSec)) {
- throw new Error(ExchangeContractErrs.OrderCancelExpired);
- }
- }
- public static async validateFillOrderBalancesAllowancesThrowIfInvalidAsync(
- exchangeTradeEmulator: ExchangeTransferSimulator,
- signedOrder: SignedOrder,
- fillTakerTokenAmount: BigNumber,
- senderAddress: string,
- zrxTokenAddress: string,
- ): Promise<void> {
- const fillMakerTokenAmount = OrderValidationUtils._getPartialAmount(
- fillTakerTokenAmount,
- signedOrder.takerTokenAmount,
- signedOrder.makerTokenAmount,
- );
- await exchangeTradeEmulator.transferFromAsync(
- signedOrder.makerTokenAddress,
- signedOrder.maker,
- senderAddress,
- fillMakerTokenAmount,
- TradeSide.Maker,
- TransferType.Trade,
- );
- await exchangeTradeEmulator.transferFromAsync(
- signedOrder.takerTokenAddress,
- senderAddress,
- signedOrder.maker,
- fillTakerTokenAmount,
- TradeSide.Taker,
- TransferType.Trade,
- );
- const makerFeeAmount = OrderValidationUtils._getPartialAmount(
- fillTakerTokenAmount,
- signedOrder.takerTokenAmount,
- signedOrder.makerFee,
- );
- await exchangeTradeEmulator.transferFromAsync(
- zrxTokenAddress,
- signedOrder.maker,
- signedOrder.feeRecipient,
- makerFeeAmount,
- TradeSide.Maker,
- TransferType.Fee,
- );
- const takerFeeAmount = OrderValidationUtils._getPartialAmount(
- fillTakerTokenAmount,
- signedOrder.takerTokenAmount,
- signedOrder.takerFee,
- );
- await exchangeTradeEmulator.transferFromAsync(
- zrxTokenAddress,
- senderAddress,
- signedOrder.feeRecipient,
- takerFeeAmount,
- TradeSide.Taker,
- TransferType.Fee,
- );
- }
- private static _validateRemainingFillAmountNotZeroOrThrow(
- takerTokenAmount: BigNumber,
- unavailableTakerTokenAmount: BigNumber,
- ) {
- if (takerTokenAmount.eq(unavailableTakerTokenAmount)) {
- throw new Error(ExchangeContractErrs.OrderRemainingFillAmountZero);
- }
- }
- private static _validateOrderNotExpiredOrThrow(expirationUnixTimestampSec: BigNumber) {
- const currentUnixTimestampSec = utils.getCurrentUnixTimestampSec();
- if (expirationUnixTimestampSec.lessThan(currentUnixTimestampSec)) {
- throw new Error(ExchangeContractErrs.OrderFillExpired);
- }
- }
- private static _getPartialAmount(numerator: BigNumber, denominator: BigNumber, target: BigNumber): BigNumber {
- const fillMakerTokenAmount = numerator
- .mul(target)
- .div(denominator)
- .round(0);
- return fillMakerTokenAmount;
- }
- constructor(exchangeWrapper: ExchangeWrapper) {
- this._exchangeWrapper = exchangeWrapper;
- }
- public async validateOrderFillableOrThrowAsync(
- exchangeTradeEmulator: ExchangeTransferSimulator,
- signedOrder: SignedOrder,
- zrxTokenAddress: string,
- expectedFillTakerTokenAmount?: BigNumber,
- ): Promise<void> {
- const orderHash = utils.getOrderHashHex(signedOrder);
- const unavailableTakerTokenAmount = await this._exchangeWrapper.getUnavailableTakerAmountAsync(orderHash);
- OrderValidationUtils._validateRemainingFillAmountNotZeroOrThrow(
- signedOrder.takerTokenAmount,
- unavailableTakerTokenAmount,
- );
- OrderValidationUtils._validateOrderNotExpiredOrThrow(signedOrder.expirationUnixTimestampSec);
- let fillTakerTokenAmount = signedOrder.takerTokenAmount.minus(unavailableTakerTokenAmount);
- if (!_.isUndefined(expectedFillTakerTokenAmount)) {
- fillTakerTokenAmount = expectedFillTakerTokenAmount;
- }
- const fillMakerTokenAmount = OrderValidationUtils._getPartialAmount(
- fillTakerTokenAmount,
- signedOrder.takerTokenAmount,
- signedOrder.makerTokenAmount,
- );
- await exchangeTradeEmulator.transferFromAsync(
- signedOrder.makerTokenAddress,
- signedOrder.maker,
- signedOrder.taker,
- fillMakerTokenAmount,
- TradeSide.Maker,
- TransferType.Trade,
- );
- const makerFeeAmount = OrderValidationUtils._getPartialAmount(
- fillTakerTokenAmount,
- signedOrder.takerTokenAmount,
- signedOrder.makerFee,
- );
- await exchangeTradeEmulator.transferFromAsync(
- zrxTokenAddress,
- signedOrder.maker,
- signedOrder.feeRecipient,
- makerFeeAmount,
- TradeSide.Maker,
- TransferType.Fee,
- );
- }
- public async validateFillOrderThrowIfInvalidAsync(
- exchangeTradeEmulator: ExchangeTransferSimulator,
- signedOrder: SignedOrder,
- fillTakerTokenAmount: BigNumber,
- takerAddress: string,
- zrxTokenAddress: string,
- ): Promise<BigNumber> {
- if (fillTakerTokenAmount.eq(0)) {
- throw new Error(ExchangeContractErrs.OrderFillAmountZero);
- }
- const orderHash = utils.getOrderHashHex(signedOrder);
- if (!ZeroEx.isValidSignature(orderHash, signedOrder.ecSignature, signedOrder.maker)) {
- throw new Error(ZeroExError.InvalidSignature);
- }
- const unavailableTakerTokenAmount = await this._exchangeWrapper.getUnavailableTakerAmountAsync(orderHash);
- OrderValidationUtils._validateRemainingFillAmountNotZeroOrThrow(
- signedOrder.takerTokenAmount,
- unavailableTakerTokenAmount,
- );
- if (signedOrder.taker !== constants.NULL_ADDRESS && signedOrder.taker !== takerAddress) {
- throw new Error(ExchangeContractErrs.TransactionSenderIsNotFillOrderTaker);
- }
- OrderValidationUtils._validateOrderNotExpiredOrThrow(signedOrder.expirationUnixTimestampSec);
- const remainingTakerTokenAmount = signedOrder.takerTokenAmount.minus(unavailableTakerTokenAmount);
- const filledTakerTokenAmount = remainingTakerTokenAmount.lessThan(fillTakerTokenAmount)
- ? remainingTakerTokenAmount
- : fillTakerTokenAmount;
- await OrderValidationUtils.validateFillOrderBalancesAllowancesThrowIfInvalidAsync(
- exchangeTradeEmulator,
- signedOrder,
- filledTakerTokenAmount,
- takerAddress,
- zrxTokenAddress,
- );
+ private _exchangeWrapper: ExchangeWrapper;
+ public static validateCancelOrderThrowIfInvalid(
+ order: Order,
+ cancelTakerTokenAmount: BigNumber,
+ unavailableTakerTokenAmount: BigNumber,
+ ): void {
+ if (cancelTakerTokenAmount.eq(0)) {
+ throw new Error(ExchangeContractErrs.OrderCancelAmountZero);
+ }
+ if (order.takerTokenAmount.eq(unavailableTakerTokenAmount)) {
+ throw new Error(ExchangeContractErrs.OrderAlreadyCancelledOrFilled);
+ }
+ const currentUnixTimestampSec = utils.getCurrentUnixTimestampSec();
+ if (order.expirationUnixTimestampSec.lessThan(currentUnixTimestampSec)) {
+ throw new Error(ExchangeContractErrs.OrderCancelExpired);
+ }
+ }
+ public static async validateFillOrderBalancesAllowancesThrowIfInvalidAsync(
+ exchangeTradeEmulator: ExchangeTransferSimulator,
+ signedOrder: SignedOrder,
+ fillTakerTokenAmount: BigNumber,
+ senderAddress: string,
+ zrxTokenAddress: string,
+ ): Promise<void> {
+ const fillMakerTokenAmount = OrderValidationUtils._getPartialAmount(
+ fillTakerTokenAmount,
+ signedOrder.takerTokenAmount,
+ signedOrder.makerTokenAmount,
+ );
+ await exchangeTradeEmulator.transferFromAsync(
+ signedOrder.makerTokenAddress,
+ signedOrder.maker,
+ senderAddress,
+ fillMakerTokenAmount,
+ TradeSide.Maker,
+ TransferType.Trade,
+ );
+ await exchangeTradeEmulator.transferFromAsync(
+ signedOrder.takerTokenAddress,
+ senderAddress,
+ signedOrder.maker,
+ fillTakerTokenAmount,
+ TradeSide.Taker,
+ TransferType.Trade,
+ );
+ const makerFeeAmount = OrderValidationUtils._getPartialAmount(
+ fillTakerTokenAmount,
+ signedOrder.takerTokenAmount,
+ signedOrder.makerFee,
+ );
+ await exchangeTradeEmulator.transferFromAsync(
+ zrxTokenAddress,
+ signedOrder.maker,
+ signedOrder.feeRecipient,
+ makerFeeAmount,
+ TradeSide.Maker,
+ TransferType.Fee,
+ );
+ const takerFeeAmount = OrderValidationUtils._getPartialAmount(
+ fillTakerTokenAmount,
+ signedOrder.takerTokenAmount,
+ signedOrder.takerFee,
+ );
+ await exchangeTradeEmulator.transferFromAsync(
+ zrxTokenAddress,
+ senderAddress,
+ signedOrder.feeRecipient,
+ takerFeeAmount,
+ TradeSide.Taker,
+ TransferType.Fee,
+ );
+ }
+ private static _validateRemainingFillAmountNotZeroOrThrow(
+ takerTokenAmount: BigNumber,
+ unavailableTakerTokenAmount: BigNumber,
+ ) {
+ if (takerTokenAmount.eq(unavailableTakerTokenAmount)) {
+ throw new Error(ExchangeContractErrs.OrderRemainingFillAmountZero);
+ }
+ }
+ private static _validateOrderNotExpiredOrThrow(expirationUnixTimestampSec: BigNumber) {
+ const currentUnixTimestampSec = utils.getCurrentUnixTimestampSec();
+ if (expirationUnixTimestampSec.lessThan(currentUnixTimestampSec)) {
+ throw new Error(ExchangeContractErrs.OrderFillExpired);
+ }
+ }
+ private static _getPartialAmount(numerator: BigNumber, denominator: BigNumber, target: BigNumber): BigNumber {
+ const fillMakerTokenAmount = numerator
+ .mul(target)
+ .div(denominator)
+ .round(0);
+ return fillMakerTokenAmount;
+ }
+ constructor(exchangeWrapper: ExchangeWrapper) {
+ this._exchangeWrapper = exchangeWrapper;
+ }
+ public async validateOrderFillableOrThrowAsync(
+ exchangeTradeEmulator: ExchangeTransferSimulator,
+ signedOrder: SignedOrder,
+ zrxTokenAddress: string,
+ expectedFillTakerTokenAmount?: BigNumber,
+ ): Promise<void> {
+ const orderHash = utils.getOrderHashHex(signedOrder);
+ const unavailableTakerTokenAmount = await this._exchangeWrapper.getUnavailableTakerAmountAsync(orderHash);
+ OrderValidationUtils._validateRemainingFillAmountNotZeroOrThrow(
+ signedOrder.takerTokenAmount,
+ unavailableTakerTokenAmount,
+ );
+ OrderValidationUtils._validateOrderNotExpiredOrThrow(signedOrder.expirationUnixTimestampSec);
+ let fillTakerTokenAmount = signedOrder.takerTokenAmount.minus(unavailableTakerTokenAmount);
+ if (!_.isUndefined(expectedFillTakerTokenAmount)) {
+ fillTakerTokenAmount = expectedFillTakerTokenAmount;
+ }
+ const fillMakerTokenAmount = OrderValidationUtils._getPartialAmount(
+ fillTakerTokenAmount,
+ signedOrder.takerTokenAmount,
+ signedOrder.makerTokenAmount,
+ );
+ await exchangeTradeEmulator.transferFromAsync(
+ signedOrder.makerTokenAddress,
+ signedOrder.maker,
+ signedOrder.taker,
+ fillMakerTokenAmount,
+ TradeSide.Maker,
+ TransferType.Trade,
+ );
+ const makerFeeAmount = OrderValidationUtils._getPartialAmount(
+ fillTakerTokenAmount,
+ signedOrder.takerTokenAmount,
+ signedOrder.makerFee,
+ );
+ await exchangeTradeEmulator.transferFromAsync(
+ zrxTokenAddress,
+ signedOrder.maker,
+ signedOrder.feeRecipient,
+ makerFeeAmount,
+ TradeSide.Maker,
+ TransferType.Fee,
+ );
+ }
+ public async validateFillOrderThrowIfInvalidAsync(
+ exchangeTradeEmulator: ExchangeTransferSimulator,
+ signedOrder: SignedOrder,
+ fillTakerTokenAmount: BigNumber,
+ takerAddress: string,
+ zrxTokenAddress: string,
+ ): Promise<BigNumber> {
+ if (fillTakerTokenAmount.eq(0)) {
+ throw new Error(ExchangeContractErrs.OrderFillAmountZero);
+ }
+ const orderHash = utils.getOrderHashHex(signedOrder);
+ if (!ZeroEx.isValidSignature(orderHash, signedOrder.ecSignature, signedOrder.maker)) {
+ throw new Error(ZeroExError.InvalidSignature);
+ }
+ const unavailableTakerTokenAmount = await this._exchangeWrapper.getUnavailableTakerAmountAsync(orderHash);
+ OrderValidationUtils._validateRemainingFillAmountNotZeroOrThrow(
+ signedOrder.takerTokenAmount,
+ unavailableTakerTokenAmount,
+ );
+ if (signedOrder.taker !== constants.NULL_ADDRESS && signedOrder.taker !== takerAddress) {
+ throw new Error(ExchangeContractErrs.TransactionSenderIsNotFillOrderTaker);
+ }
+ OrderValidationUtils._validateOrderNotExpiredOrThrow(signedOrder.expirationUnixTimestampSec);
+ const remainingTakerTokenAmount = signedOrder.takerTokenAmount.minus(unavailableTakerTokenAmount);
+ const filledTakerTokenAmount = remainingTakerTokenAmount.lessThan(fillTakerTokenAmount)
+ ? remainingTakerTokenAmount
+ : fillTakerTokenAmount;
+ await OrderValidationUtils.validateFillOrderBalancesAllowancesThrowIfInvalidAsync(
+ exchangeTradeEmulator,
+ signedOrder,
+ filledTakerTokenAmount,
+ takerAddress,
+ zrxTokenAddress,
+ );
- const wouldRoundingErrorOccur = await this._exchangeWrapper.isRoundingErrorAsync(
- filledTakerTokenAmount,
- signedOrder.takerTokenAmount,
- signedOrder.makerTokenAmount,
- );
- if (wouldRoundingErrorOccur) {
- throw new Error(ExchangeContractErrs.OrderFillRoundingError);
- }
- return filledTakerTokenAmount;
- }
- public async validateFillOrKillOrderThrowIfInvalidAsync(
- exchangeTradeEmulator: ExchangeTransferSimulator,
- signedOrder: SignedOrder,
- fillTakerTokenAmount: BigNumber,
- takerAddress: string,
- zrxTokenAddress: string,
- ): Promise<void> {
- const filledTakerTokenAmount = await this.validateFillOrderThrowIfInvalidAsync(
- exchangeTradeEmulator,
- signedOrder,
- fillTakerTokenAmount,
- takerAddress,
- zrxTokenAddress,
- );
- if (filledTakerTokenAmount !== fillTakerTokenAmount) {
- throw new Error(ExchangeContractErrs.InsufficientRemainingFillAmount);
- }
- }
+ const wouldRoundingErrorOccur = await this._exchangeWrapper.isRoundingErrorAsync(
+ filledTakerTokenAmount,
+ signedOrder.takerTokenAmount,
+ signedOrder.makerTokenAmount,
+ );
+ if (wouldRoundingErrorOccur) {
+ throw new Error(ExchangeContractErrs.OrderFillRoundingError);
+ }
+ return filledTakerTokenAmount;
+ }
+ public async validateFillOrKillOrderThrowIfInvalidAsync(
+ exchangeTradeEmulator: ExchangeTransferSimulator,
+ signedOrder: SignedOrder,
+ fillTakerTokenAmount: BigNumber,
+ takerAddress: string,
+ zrxTokenAddress: string,
+ ): Promise<void> {
+ const filledTakerTokenAmount = await this.validateFillOrderThrowIfInvalidAsync(
+ exchangeTradeEmulator,
+ signedOrder,
+ fillTakerTokenAmount,
+ takerAddress,
+ zrxTokenAddress,
+ );
+ if (filledTakerTokenAmount !== fillTakerTokenAmount) {
+ throw new Error(ExchangeContractErrs.InsufficientRemainingFillAmount);
+ }
+ }
}
diff --git a/packages/0x.js/src/utils/signature_utils.ts b/packages/0x.js/src/utils/signature_utils.ts
index 19b5c76c7..b0f1d61ef 100644
--- a/packages/0x.js/src/utils/signature_utils.ts
+++ b/packages/0x.js/src/utils/signature_utils.ts
@@ -3,44 +3,44 @@ import * as ethUtil from 'ethereumjs-util';
import { ECSignature } from '../types';
export const signatureUtils = {
- isValidSignature(data: string, signature: ECSignature, signerAddress: string): boolean {
- const dataBuff = ethUtil.toBuffer(data);
- const msgHashBuff = ethUtil.hashPersonalMessage(dataBuff);
- try {
- const pubKey = ethUtil.ecrecover(
- msgHashBuff,
- signature.v,
- ethUtil.toBuffer(signature.r),
- ethUtil.toBuffer(signature.s),
- );
- const retrievedAddress = ethUtil.bufferToHex(ethUtil.pubToAddress(pubKey));
- return retrievedAddress === signerAddress;
- } catch (err) {
- return false;
- }
- },
- parseSignatureHexAsVRS(signatureHex: string): ECSignature {
- const signatureBuffer = ethUtil.toBuffer(signatureHex);
- let v = signatureBuffer[0];
- if (v < 27) {
- v += 27;
- }
- const r = signatureBuffer.slice(1, 33);
- const s = signatureBuffer.slice(33, 65);
- const ecSignature: ECSignature = {
- v,
- r: ethUtil.bufferToHex(r),
- s: ethUtil.bufferToHex(s),
- };
- return ecSignature;
- },
- parseSignatureHexAsRSV(signatureHex: string): ECSignature {
- const { v, r, s } = ethUtil.fromRpcSig(signatureHex);
- const ecSignature: ECSignature = {
- v,
- r: ethUtil.bufferToHex(r),
- s: ethUtil.bufferToHex(s),
- };
- return ecSignature;
- },
+ isValidSignature(data: string, signature: ECSignature, signerAddress: string): boolean {
+ const dataBuff = ethUtil.toBuffer(data);
+ const msgHashBuff = ethUtil.hashPersonalMessage(dataBuff);
+ try {
+ const pubKey = ethUtil.ecrecover(
+ msgHashBuff,
+ signature.v,
+ ethUtil.toBuffer(signature.r),
+ ethUtil.toBuffer(signature.s),
+ );
+ const retrievedAddress = ethUtil.bufferToHex(ethUtil.pubToAddress(pubKey));
+ return retrievedAddress === signerAddress;
+ } catch (err) {
+ return false;
+ }
+ },
+ parseSignatureHexAsVRS(signatureHex: string): ECSignature {
+ const signatureBuffer = ethUtil.toBuffer(signatureHex);
+ let v = signatureBuffer[0];
+ if (v < 27) {
+ v += 27;
+ }
+ const r = signatureBuffer.slice(1, 33);
+ const s = signatureBuffer.slice(33, 65);
+ const ecSignature: ECSignature = {
+ v,
+ r: ethUtil.bufferToHex(r),
+ s: ethUtil.bufferToHex(s),
+ };
+ return ecSignature;
+ },
+ parseSignatureHexAsRSV(signatureHex: string): ECSignature {
+ const { v, r, s } = ethUtil.fromRpcSig(signatureHex);
+ const ecSignature: ECSignature = {
+ v,
+ r: ethUtil.bufferToHex(r),
+ s: ethUtil.bufferToHex(s),
+ };
+ return ecSignature;
+ },
};
diff --git a/packages/0x.js/src/utils/utils.ts b/packages/0x.js/src/utils/utils.ts
index ae07941ef..42cf5d956 100644
--- a/packages/0x.js/src/utils/utils.ts
+++ b/packages/0x.js/src/utils/utils.ts
@@ -7,62 +7,62 @@ import * as _ from 'lodash';
import { Order, SignedOrder, SolidityTypes } from '../types';
export const utils = {
- /**
- * Converts BigNumber instance to BN
- * The only reason we convert to BN is to remain compatible with `ethABI. soliditySHA3` that
- * expects values of Solidity type `uint` to be passed as type `BN`.
- * We do not use BN anywhere else in the codebase.
- */
- bigNumberToBN(value: BigNumber) {
- return new BN(value.toString(), 10);
- },
- consoleLog(message: string): void {
- // tslint:disable-next-line: no-console
- console.log(message);
- },
- spawnSwitchErr(name: string, value: any): Error {
- return new Error(`Unexpected switch value: ${value} encountered for ${name}`);
- },
- getOrderHashHex(order: Order | SignedOrder): string {
- const orderParts = [
- { value: order.exchangeContractAddress, type: SolidityTypes.Address },
- { value: order.maker, type: SolidityTypes.Address },
- { value: order.taker, type: SolidityTypes.Address },
- { value: order.makerTokenAddress, type: SolidityTypes.Address },
- { value: order.takerTokenAddress, type: SolidityTypes.Address },
- { value: order.feeRecipient, type: SolidityTypes.Address },
- {
- value: utils.bigNumberToBN(order.makerTokenAmount),
- type: SolidityTypes.Uint256,
- },
- {
- value: utils.bigNumberToBN(order.takerTokenAmount),
- type: SolidityTypes.Uint256,
- },
- {
- value: utils.bigNumberToBN(order.makerFee),
- type: SolidityTypes.Uint256,
- },
- {
- value: utils.bigNumberToBN(order.takerFee),
- type: SolidityTypes.Uint256,
- },
- {
- value: utils.bigNumberToBN(order.expirationUnixTimestampSec),
- type: SolidityTypes.Uint256,
- },
- { value: utils.bigNumberToBN(order.salt), type: SolidityTypes.Uint256 },
- ];
- const types = _.map(orderParts, o => o.type);
- const values = _.map(orderParts, o => o.value);
- const hashBuff = ethABI.soliditySHA3(types, values);
- const hashHex = ethUtil.bufferToHex(hashBuff);
- return hashHex;
- },
- getCurrentUnixTimestampSec(): BigNumber {
- return new BigNumber(Date.now() / 1000).round();
- },
- getCurrentUnixTimestampMs(): BigNumber {
- return new BigNumber(Date.now());
- },
+ /**
+ * Converts BigNumber instance to BN
+ * The only reason we convert to BN is to remain compatible with `ethABI. soliditySHA3` that
+ * expects values of Solidity type `uint` to be passed as type `BN`.
+ * We do not use BN anywhere else in the codebase.
+ */
+ bigNumberToBN(value: BigNumber) {
+ return new BN(value.toString(), 10);
+ },
+ consoleLog(message: string): void {
+ // tslint:disable-next-line: no-console
+ console.log(message);
+ },
+ spawnSwitchErr(name: string, value: any): Error {
+ return new Error(`Unexpected switch value: ${value} encountered for ${name}`);
+ },
+ getOrderHashHex(order: Order | SignedOrder): string {
+ const orderParts = [
+ { value: order.exchangeContractAddress, type: SolidityTypes.Address },
+ { value: order.maker, type: SolidityTypes.Address },
+ { value: order.taker, type: SolidityTypes.Address },
+ { value: order.makerTokenAddress, type: SolidityTypes.Address },
+ { value: order.takerTokenAddress, type: SolidityTypes.Address },
+ { value: order.feeRecipient, type: SolidityTypes.Address },
+ {
+ value: utils.bigNumberToBN(order.makerTokenAmount),
+ type: SolidityTypes.Uint256,
+ },
+ {
+ value: utils.bigNumberToBN(order.takerTokenAmount),
+ type: SolidityTypes.Uint256,
+ },
+ {
+ value: utils.bigNumberToBN(order.makerFee),
+ type: SolidityTypes.Uint256,
+ },
+ {
+ value: utils.bigNumberToBN(order.takerFee),
+ type: SolidityTypes.Uint256,
+ },
+ {
+ value: utils.bigNumberToBN(order.expirationUnixTimestampSec),
+ type: SolidityTypes.Uint256,
+ },
+ { value: utils.bigNumberToBN(order.salt), type: SolidityTypes.Uint256 },
+ ];
+ const types = _.map(orderParts, o => o.type);
+ const values = _.map(orderParts, o => o.value);
+ const hashBuff = ethABI.soliditySHA3(types, values);
+ const hashHex = ethUtil.bufferToHex(hashBuff);
+ return hashHex;
+ },
+ getCurrentUnixTimestampSec(): BigNumber {
+ return new BigNumber(Date.now() / 1000).round();
+ },
+ getCurrentUnixTimestampMs(): BigNumber {
+ return new BigNumber(Date.now());
+ },
};
diff --git a/packages/0x.js/test/0x.js_test.ts b/packages/0x.js/test/0x.js_test.ts
index 5ece5192f..927fe20be 100644
--- a/packages/0x.js/test/0x.js_test.ts
+++ b/packages/0x.js/test/0x.js_test.ts
@@ -19,278 +19,278 @@ const expect = chai.expect;
const SHOULD_ADD_PERSONAL_MESSAGE_PREFIX = false;
describe('ZeroEx library', () => {
- const web3 = web3Factory.create();
- const config = {
- networkId: constants.TESTRPC_NETWORK_ID,
- };
- const zeroEx = new ZeroEx(web3.currentProvider, config);
- describe('#setProvider', () => {
- it('overrides provider in nested web3s and invalidates contractInstances', async () => {
- // Instantiate the contract instances with the current provider
- await (zeroEx.exchange as any)._getExchangeContractAsync();
- await (zeroEx.tokenRegistry as any)._getTokenRegistryContractAsync();
- expect((zeroEx.exchange as any)._exchangeContractIfExists).to.not.be.undefined();
- expect((zeroEx.tokenRegistry as any)._tokenRegistryContractIfExists).to.not.be.undefined();
+ const web3 = web3Factory.create();
+ const config = {
+ networkId: constants.TESTRPC_NETWORK_ID,
+ };
+ const zeroEx = new ZeroEx(web3.currentProvider, config);
+ describe('#setProvider', () => {
+ it('overrides provider in nested web3s and invalidates contractInstances', async () => {
+ // Instantiate the contract instances with the current provider
+ await (zeroEx.exchange as any)._getExchangeContractAsync();
+ await (zeroEx.tokenRegistry as any)._getTokenRegistryContractAsync();
+ expect((zeroEx.exchange as any)._exchangeContractIfExists).to.not.be.undefined();
+ expect((zeroEx.tokenRegistry as any)._tokenRegistryContractIfExists).to.not.be.undefined();
- const newProvider = web3Factory.getRpcProvider();
- // Add property to newProvider so that we can differentiate it from old provider
- (newProvider as any).zeroExTestId = 1;
- zeroEx.setProvider(newProvider, constants.TESTRPC_NETWORK_ID);
+ const newProvider = web3Factory.getRpcProvider();
+ // Add property to newProvider so that we can differentiate it from old provider
+ (newProvider as any).zeroExTestId = 1;
+ zeroEx.setProvider(newProvider, constants.TESTRPC_NETWORK_ID);
- // Check that contractInstances with old provider are removed after provider update
- expect((zeroEx.exchange as any)._exchangeContractIfExists).to.be.undefined();
- expect((zeroEx.tokenRegistry as any)._tokenRegistryContractIfExists).to.be.undefined();
+ // Check that contractInstances with old provider are removed after provider update
+ expect((zeroEx.exchange as any)._exchangeContractIfExists).to.be.undefined();
+ expect((zeroEx.tokenRegistry as any)._tokenRegistryContractIfExists).to.be.undefined();
- // Check that all nested web3 wrapper instances return the updated provider
- const nestedWeb3WrapperProvider = (zeroEx as any)._web3Wrapper.getCurrentProvider();
- expect(nestedWeb3WrapperProvider.zeroExTestId).to.be.a('number');
- const exchangeWeb3WrapperProvider = (zeroEx.exchange as any)._web3Wrapper.getCurrentProvider();
- expect(exchangeWeb3WrapperProvider.zeroExTestId).to.be.a('number');
- const tokenRegistryWeb3WrapperProvider = (zeroEx.tokenRegistry as any)._web3Wrapper.getCurrentProvider();
- expect(tokenRegistryWeb3WrapperProvider.zeroExTestId).to.be.a('number');
- });
- });
- describe('#isValidSignature', () => {
- // The Exchange smart contract `isValidSignature` method only validates orderHashes and assumes
- // the length of the data is exactly 32 bytes. Thus for these tests, we use data of this size.
- const dataHex = '0x6927e990021d23b1eb7b8789f6a6feaf98fe104bb0cf8259421b79f9a34222b0';
- const signature = {
- v: 27,
- r: '0x61a3ed31b43c8780e905a260a35faefcc527be7516aa11c0256729b5b351bc33',
- s: '0x40349190569279751135161d22529dc25add4f6069af05be04cacbda2ace2254',
- };
- const address = '0x5409ed021d9299bf6814279a6a1411a7e866a631';
- it("should return false if the data doesn't pertain to the signature & address", async () => {
- expect(ZeroEx.isValidSignature('0x0', signature, address)).to.be.false();
- return expect(
- (zeroEx.exchange as any)._isValidSignatureUsingContractCallAsync('0x0', signature, address),
- ).to.become(false);
- });
- it("should return false if the address doesn't pertain to the signature & data", async () => {
- const validUnrelatedAddress = '0x8b0292b11a196601ed2ce54b665cafeca0347d42';
- expect(ZeroEx.isValidSignature(dataHex, signature, validUnrelatedAddress)).to.be.false();
- return expect(
- (zeroEx.exchange as any)._isValidSignatureUsingContractCallAsync(
- dataHex,
- signature,
- validUnrelatedAddress,
- ),
- ).to.become(false);
- });
- it("should return false if the signature doesn't pertain to the dataHex & address", async () => {
- const wrongSignature = _.assign({}, signature, { v: 28 });
- expect(ZeroEx.isValidSignature(dataHex, wrongSignature, address)).to.be.false();
- return expect(
- (zeroEx.exchange as any)._isValidSignatureUsingContractCallAsync(dataHex, wrongSignature, address),
- ).to.become(false);
- });
- it('should return true if the signature does pertain to the dataHex & address', async () => {
- const isValidSignatureLocal = ZeroEx.isValidSignature(dataHex, signature, address);
- expect(isValidSignatureLocal).to.be.true();
- return expect(
- (zeroEx.exchange as any)._isValidSignatureUsingContractCallAsync(dataHex, signature, address),
- ).to.become(true);
- });
- });
- describe('#generateSalt', () => {
- it('generates different salts', () => {
- const equal = ZeroEx.generatePseudoRandomSalt().eq(ZeroEx.generatePseudoRandomSalt());
- expect(equal).to.be.false();
- });
- it('generates salt in range [0..2^256)', () => {
- const salt = ZeroEx.generatePseudoRandomSalt();
- expect(salt.greaterThanOrEqualTo(0)).to.be.true();
- const twoPow256 = new BigNumber(2).pow(256);
- expect(salt.lessThan(twoPow256)).to.be.true();
- });
- });
- describe('#isValidOrderHash', () => {
- it('returns false if the value is not a hex string', () => {
- const isValid = ZeroEx.isValidOrderHash('not a hex');
- expect(isValid).to.be.false();
- });
- it('returns false if the length is wrong', () => {
- const isValid = ZeroEx.isValidOrderHash('0xdeadbeef');
- expect(isValid).to.be.false();
- });
- it('returns true if order hash is correct', () => {
- const isValid = ZeroEx.isValidOrderHash('0x' + Array(65).join('0'));
- expect(isValid).to.be.true();
- });
- });
- describe('#toUnitAmount', () => {
- it('should throw if invalid baseUnit amount supplied as argument', () => {
- const invalidBaseUnitAmount = new BigNumber(1000000000.4);
- const decimals = 6;
- expect(() => ZeroEx.toUnitAmount(invalidBaseUnitAmount, decimals)).to.throw(
- 'amount should be in baseUnits (no decimals), found value: 1000000000.4',
- );
- });
- it('Should return the expected unit amount for the decimals passed in', () => {
- const baseUnitAmount = new BigNumber(1000000000);
- const decimals = 6;
- const unitAmount = ZeroEx.toUnitAmount(baseUnitAmount, decimals);
- const expectedUnitAmount = new BigNumber(1000);
- expect(unitAmount).to.be.bignumber.equal(expectedUnitAmount);
- });
- });
- describe('#toBaseUnitAmount', () => {
- it('Should return the expected base unit amount for the decimals passed in', () => {
- const unitAmount = new BigNumber(1000);
- const decimals = 6;
- const baseUnitAmount = ZeroEx.toBaseUnitAmount(unitAmount, decimals);
- const expectedUnitAmount = new BigNumber(1000000000);
- expect(baseUnitAmount).to.be.bignumber.equal(expectedUnitAmount);
- });
- it('should throw if unitAmount has more decimals then specified as the max decimal precision', () => {
- const unitAmount = new BigNumber(0.823091);
- const decimals = 5;
- expect(() => ZeroEx.toBaseUnitAmount(unitAmount, decimals)).to.throw(
- 'Invalid unit amount: 0.823091 - Too many decimal places',
- );
- });
- });
- describe('#getOrderHashHex', () => {
- const expectedOrderHash = '0x39da987067a3c9e5f1617694f1301326ba8c8b0498ebef5df4863bed394e3c83';
- const fakeExchangeContractAddress = '0xb69e673309512a9d726f87304c6984054f87a93b';
- const order: Order = {
- maker: constants.NULL_ADDRESS,
- taker: constants.NULL_ADDRESS,
- feeRecipient: constants.NULL_ADDRESS,
- makerTokenAddress: constants.NULL_ADDRESS,
- takerTokenAddress: constants.NULL_ADDRESS,
- exchangeContractAddress: fakeExchangeContractAddress,
- salt: new BigNumber(0),
- makerFee: new BigNumber(0),
- takerFee: new BigNumber(0),
- makerTokenAmount: new BigNumber(0),
- takerTokenAmount: new BigNumber(0),
- expirationUnixTimestampSec: new BigNumber(0),
- };
- it('calculates the order hash', async () => {
- const orderHash = ZeroEx.getOrderHashHex(order);
- expect(orderHash).to.be.equal(expectedOrderHash);
- });
- it('throws a readable error message if taker format is invalid', async () => {
- const orderWithInvalidtakerFormat = {
- ...order,
- taker: (null as any) as string,
- };
- const expectedErrorMessage =
- 'Order taker must be of type string. If you want anyone to be able to fill an order - pass ZeroEx.NULL_ADDRESS';
- expect(() => ZeroEx.getOrderHashHex(orderWithInvalidtakerFormat)).to.throw(expectedErrorMessage);
- });
- });
- describe('#signOrderHashAsync', () => {
- let stubs: Sinon.SinonStub[] = [];
- let makerAddress: string;
- before(async () => {
- const availableAddreses = await zeroEx.getAvailableAddressesAsync();
- makerAddress = availableAddreses[0];
- });
- afterEach(() => {
- // clean up any stubs after the test has completed
- _.each(stubs, s => s.restore());
- stubs = [];
- });
- it('Should return the correct ECSignature', async () => {
- const orderHash = '0x6927e990021d23b1eb7b8789f6a6feaf98fe104bb0cf8259421b79f9a34222b0';
- const expectedECSignature = {
- v: 27,
- r: '0x61a3ed31b43c8780e905a260a35faefcc527be7516aa11c0256729b5b351bc33',
- s: '0x40349190569279751135161d22529dc25add4f6069af05be04cacbda2ace2254',
- };
- const ecSignature = await zeroEx.signOrderHashAsync(
- orderHash,
- makerAddress,
- SHOULD_ADD_PERSONAL_MESSAGE_PREFIX,
- );
- expect(ecSignature).to.deep.equal(expectedECSignature);
- });
- it('should return the correct ECSignature for signatureHex concatenated as R + S + V', async () => {
- const orderHash = '0x34decbedc118904df65f379a175bb39ca18209d6ce41d5ed549d54e6e0a95004';
- const signature =
- '0x22109d11d79cb8bf96ed88625e1cd9558800c4073332a9a02857499883ee5ce3050aa3cc1f2c435e67e114cdce54b9527b4f50548342401bc5d2b77adbdacb021b';
- const expectedECSignature = {
- v: 27,
- r: '0x22109d11d79cb8bf96ed88625e1cd9558800c4073332a9a02857499883ee5ce3',
- s: '0x050aa3cc1f2c435e67e114cdce54b9527b4f50548342401bc5d2b77adbdacb02',
- };
- stubs = [
- Sinon.stub((zeroEx as any)._web3Wrapper, 'signTransactionAsync').returns(Promise.resolve(signature)),
- Sinon.stub(ZeroEx, 'isValidSignature').returns(true),
- ];
+ // Check that all nested web3 wrapper instances return the updated provider
+ const nestedWeb3WrapperProvider = (zeroEx as any)._web3Wrapper.getCurrentProvider();
+ expect(nestedWeb3WrapperProvider.zeroExTestId).to.be.a('number');
+ const exchangeWeb3WrapperProvider = (zeroEx.exchange as any)._web3Wrapper.getCurrentProvider();
+ expect(exchangeWeb3WrapperProvider.zeroExTestId).to.be.a('number');
+ const tokenRegistryWeb3WrapperProvider = (zeroEx.tokenRegistry as any)._web3Wrapper.getCurrentProvider();
+ expect(tokenRegistryWeb3WrapperProvider.zeroExTestId).to.be.a('number');
+ });
+ });
+ describe('#isValidSignature', () => {
+ // The Exchange smart contract `isValidSignature` method only validates orderHashes and assumes
+ // the length of the data is exactly 32 bytes. Thus for these tests, we use data of this size.
+ const dataHex = '0x6927e990021d23b1eb7b8789f6a6feaf98fe104bb0cf8259421b79f9a34222b0';
+ const signature = {
+ v: 27,
+ r: '0x61a3ed31b43c8780e905a260a35faefcc527be7516aa11c0256729b5b351bc33',
+ s: '0x40349190569279751135161d22529dc25add4f6069af05be04cacbda2ace2254',
+ };
+ const address = '0x5409ed021d9299bf6814279a6a1411a7e866a631';
+ it("should return false if the data doesn't pertain to the signature & address", async () => {
+ expect(ZeroEx.isValidSignature('0x0', signature, address)).to.be.false();
+ return expect(
+ (zeroEx.exchange as any)._isValidSignatureUsingContractCallAsync('0x0', signature, address),
+ ).to.become(false);
+ });
+ it("should return false if the address doesn't pertain to the signature & data", async () => {
+ const validUnrelatedAddress = '0x8b0292b11a196601ed2ce54b665cafeca0347d42';
+ expect(ZeroEx.isValidSignature(dataHex, signature, validUnrelatedAddress)).to.be.false();
+ return expect(
+ (zeroEx.exchange as any)._isValidSignatureUsingContractCallAsync(
+ dataHex,
+ signature,
+ validUnrelatedAddress,
+ ),
+ ).to.become(false);
+ });
+ it("should return false if the signature doesn't pertain to the dataHex & address", async () => {
+ const wrongSignature = _.assign({}, signature, { v: 28 });
+ expect(ZeroEx.isValidSignature(dataHex, wrongSignature, address)).to.be.false();
+ return expect(
+ (zeroEx.exchange as any)._isValidSignatureUsingContractCallAsync(dataHex, wrongSignature, address),
+ ).to.become(false);
+ });
+ it('should return true if the signature does pertain to the dataHex & address', async () => {
+ const isValidSignatureLocal = ZeroEx.isValidSignature(dataHex, signature, address);
+ expect(isValidSignatureLocal).to.be.true();
+ return expect(
+ (zeroEx.exchange as any)._isValidSignatureUsingContractCallAsync(dataHex, signature, address),
+ ).to.become(true);
+ });
+ });
+ describe('#generateSalt', () => {
+ it('generates different salts', () => {
+ const equal = ZeroEx.generatePseudoRandomSalt().eq(ZeroEx.generatePseudoRandomSalt());
+ expect(equal).to.be.false();
+ });
+ it('generates salt in range [0..2^256)', () => {
+ const salt = ZeroEx.generatePseudoRandomSalt();
+ expect(salt.greaterThanOrEqualTo(0)).to.be.true();
+ const twoPow256 = new BigNumber(2).pow(256);
+ expect(salt.lessThan(twoPow256)).to.be.true();
+ });
+ });
+ describe('#isValidOrderHash', () => {
+ it('returns false if the value is not a hex string', () => {
+ const isValid = ZeroEx.isValidOrderHash('not a hex');
+ expect(isValid).to.be.false();
+ });
+ it('returns false if the length is wrong', () => {
+ const isValid = ZeroEx.isValidOrderHash('0xdeadbeef');
+ expect(isValid).to.be.false();
+ });
+ it('returns true if order hash is correct', () => {
+ const isValid = ZeroEx.isValidOrderHash('0x' + Array(65).join('0'));
+ expect(isValid).to.be.true();
+ });
+ });
+ describe('#toUnitAmount', () => {
+ it('should throw if invalid baseUnit amount supplied as argument', () => {
+ const invalidBaseUnitAmount = new BigNumber(1000000000.4);
+ const decimals = 6;
+ expect(() => ZeroEx.toUnitAmount(invalidBaseUnitAmount, decimals)).to.throw(
+ 'amount should be in baseUnits (no decimals), found value: 1000000000.4',
+ );
+ });
+ it('Should return the expected unit amount for the decimals passed in', () => {
+ const baseUnitAmount = new BigNumber(1000000000);
+ const decimals = 6;
+ const unitAmount = ZeroEx.toUnitAmount(baseUnitAmount, decimals);
+ const expectedUnitAmount = new BigNumber(1000);
+ expect(unitAmount).to.be.bignumber.equal(expectedUnitAmount);
+ });
+ });
+ describe('#toBaseUnitAmount', () => {
+ it('Should return the expected base unit amount for the decimals passed in', () => {
+ const unitAmount = new BigNumber(1000);
+ const decimals = 6;
+ const baseUnitAmount = ZeroEx.toBaseUnitAmount(unitAmount, decimals);
+ const expectedUnitAmount = new BigNumber(1000000000);
+ expect(baseUnitAmount).to.be.bignumber.equal(expectedUnitAmount);
+ });
+ it('should throw if unitAmount has more decimals then specified as the max decimal precision', () => {
+ const unitAmount = new BigNumber(0.823091);
+ const decimals = 5;
+ expect(() => ZeroEx.toBaseUnitAmount(unitAmount, decimals)).to.throw(
+ 'Invalid unit amount: 0.823091 - Too many decimal places',
+ );
+ });
+ });
+ describe('#getOrderHashHex', () => {
+ const expectedOrderHash = '0x39da987067a3c9e5f1617694f1301326ba8c8b0498ebef5df4863bed394e3c83';
+ const fakeExchangeContractAddress = '0xb69e673309512a9d726f87304c6984054f87a93b';
+ const order: Order = {
+ maker: constants.NULL_ADDRESS,
+ taker: constants.NULL_ADDRESS,
+ feeRecipient: constants.NULL_ADDRESS,
+ makerTokenAddress: constants.NULL_ADDRESS,
+ takerTokenAddress: constants.NULL_ADDRESS,
+ exchangeContractAddress: fakeExchangeContractAddress,
+ salt: new BigNumber(0),
+ makerFee: new BigNumber(0),
+ takerFee: new BigNumber(0),
+ makerTokenAmount: new BigNumber(0),
+ takerTokenAmount: new BigNumber(0),
+ expirationUnixTimestampSec: new BigNumber(0),
+ };
+ it('calculates the order hash', async () => {
+ const orderHash = ZeroEx.getOrderHashHex(order);
+ expect(orderHash).to.be.equal(expectedOrderHash);
+ });
+ it('throws a readable error message if taker format is invalid', async () => {
+ const orderWithInvalidtakerFormat = {
+ ...order,
+ taker: (null as any) as string,
+ };
+ const expectedErrorMessage =
+ 'Order taker must be of type string. If you want anyone to be able to fill an order - pass ZeroEx.NULL_ADDRESS';
+ expect(() => ZeroEx.getOrderHashHex(orderWithInvalidtakerFormat)).to.throw(expectedErrorMessage);
+ });
+ });
+ describe('#signOrderHashAsync', () => {
+ let stubs: Sinon.SinonStub[] = [];
+ let makerAddress: string;
+ before(async () => {
+ const availableAddreses = await zeroEx.getAvailableAddressesAsync();
+ makerAddress = availableAddreses[0];
+ });
+ afterEach(() => {
+ // clean up any stubs after the test has completed
+ _.each(stubs, s => s.restore());
+ stubs = [];
+ });
+ it('Should return the correct ECSignature', async () => {
+ const orderHash = '0x6927e990021d23b1eb7b8789f6a6feaf98fe104bb0cf8259421b79f9a34222b0';
+ const expectedECSignature = {
+ v: 27,
+ r: '0x61a3ed31b43c8780e905a260a35faefcc527be7516aa11c0256729b5b351bc33',
+ s: '0x40349190569279751135161d22529dc25add4f6069af05be04cacbda2ace2254',
+ };
+ const ecSignature = await zeroEx.signOrderHashAsync(
+ orderHash,
+ makerAddress,
+ SHOULD_ADD_PERSONAL_MESSAGE_PREFIX,
+ );
+ expect(ecSignature).to.deep.equal(expectedECSignature);
+ });
+ it('should return the correct ECSignature for signatureHex concatenated as R + S + V', async () => {
+ const orderHash = '0x34decbedc118904df65f379a175bb39ca18209d6ce41d5ed549d54e6e0a95004';
+ const signature =
+ '0x22109d11d79cb8bf96ed88625e1cd9558800c4073332a9a02857499883ee5ce3050aa3cc1f2c435e67e114cdce54b9527b4f50548342401bc5d2b77adbdacb021b';
+ const expectedECSignature = {
+ v: 27,
+ r: '0x22109d11d79cb8bf96ed88625e1cd9558800c4073332a9a02857499883ee5ce3',
+ s: '0x050aa3cc1f2c435e67e114cdce54b9527b4f50548342401bc5d2b77adbdacb02',
+ };
+ stubs = [
+ Sinon.stub((zeroEx as any)._web3Wrapper, 'signTransactionAsync').returns(Promise.resolve(signature)),
+ Sinon.stub(ZeroEx, 'isValidSignature').returns(true),
+ ];
- const ecSignature = await zeroEx.signOrderHashAsync(
- orderHash,
- makerAddress,
- SHOULD_ADD_PERSONAL_MESSAGE_PREFIX,
- );
- expect(ecSignature).to.deep.equal(expectedECSignature);
- });
- it('should return the correct ECSignature for signatureHex concatenated as V + R + S', async () => {
- const orderHash = '0xc793e33ffded933b76f2f48d9aa3339fc090399d5e7f5dec8d3660f5480793f7';
- const signature =
- '0x1bc80bedc6756722672753413efdd749b5adbd4fd552595f59c13427407ee9aee02dea66f25a608bbae457e020fb6decb763deb8b7192abab624997242da248960';
- const expectedECSignature = {
- v: 27,
- r: '0xc80bedc6756722672753413efdd749b5adbd4fd552595f59c13427407ee9aee0',
- s: '0x2dea66f25a608bbae457e020fb6decb763deb8b7192abab624997242da248960',
- };
- stubs = [
- Sinon.stub((zeroEx as any)._web3Wrapper, 'signTransactionAsync').returns(Promise.resolve(signature)),
- Sinon.stub(ZeroEx, 'isValidSignature').returns(true),
- ];
+ const ecSignature = await zeroEx.signOrderHashAsync(
+ orderHash,
+ makerAddress,
+ SHOULD_ADD_PERSONAL_MESSAGE_PREFIX,
+ );
+ expect(ecSignature).to.deep.equal(expectedECSignature);
+ });
+ it('should return the correct ECSignature for signatureHex concatenated as V + R + S', async () => {
+ const orderHash = '0xc793e33ffded933b76f2f48d9aa3339fc090399d5e7f5dec8d3660f5480793f7';
+ const signature =
+ '0x1bc80bedc6756722672753413efdd749b5adbd4fd552595f59c13427407ee9aee02dea66f25a608bbae457e020fb6decb763deb8b7192abab624997242da248960';
+ const expectedECSignature = {
+ v: 27,
+ r: '0xc80bedc6756722672753413efdd749b5adbd4fd552595f59c13427407ee9aee0',
+ s: '0x2dea66f25a608bbae457e020fb6decb763deb8b7192abab624997242da248960',
+ };
+ stubs = [
+ Sinon.stub((zeroEx as any)._web3Wrapper, 'signTransactionAsync').returns(Promise.resolve(signature)),
+ Sinon.stub(ZeroEx, 'isValidSignature').returns(true),
+ ];
- const ecSignature = await zeroEx.signOrderHashAsync(
- orderHash,
- makerAddress,
- SHOULD_ADD_PERSONAL_MESSAGE_PREFIX,
- );
- expect(ecSignature).to.deep.equal(expectedECSignature);
- });
- });
- describe('#awaitTransactionMinedAsync', () => {
- beforeEach(async () => {
- await blockchainLifecycle.startAsync();
- });
- afterEach(async () => {
- await blockchainLifecycle.revertAsync();
- });
- it('returns transaction receipt with decoded logs', async () => {
- const availableAddresses = await zeroEx.getAvailableAddressesAsync();
- const coinbase = availableAddresses[0];
- const tokens = await zeroEx.tokenRegistry.getTokensAsync();
- const tokenUtils = new TokenUtils(tokens);
- const zrxTokenAddress = tokenUtils.getProtocolTokenOrThrow().address;
- const proxyAddress = zeroEx.proxy.getContractAddress();
- const txHash = await zeroEx.token.setUnlimitedProxyAllowanceAsync(zrxTokenAddress, coinbase);
- const txReceiptWithDecodedLogs = await zeroEx.awaitTransactionMinedAsync(txHash);
- const log = txReceiptWithDecodedLogs.logs[0] as LogWithDecodedArgs<ApprovalContractEventArgs>;
- expect(log.event).to.be.equal(TokenEvents.Approval);
- expect(log.args._owner).to.be.equal(coinbase);
- expect(log.args._spender).to.be.equal(proxyAddress);
- expect(log.args._value).to.be.bignumber.equal(zeroEx.token.UNLIMITED_ALLOWANCE_IN_BASE_UNITS);
- });
- });
- describe('#config', () => {
- it('allows to specify exchange contract address', async () => {
- const zeroExConfig = {
- exchangeContractAddress: ZeroEx.NULL_ADDRESS,
- networkId: constants.TESTRPC_NETWORK_ID,
- };
- const zeroExWithWrongExchangeAddress = new ZeroEx(web3.currentProvider, zeroExConfig);
- expect(zeroExWithWrongExchangeAddress.exchange.getContractAddress()).to.be.equal(ZeroEx.NULL_ADDRESS);
- });
- it('allows to specify token registry token contract address', async () => {
- const zeroExConfig = {
- tokenRegistryContractAddress: ZeroEx.NULL_ADDRESS,
- networkId: constants.TESTRPC_NETWORK_ID,
- };
- const zeroExWithWrongTokenRegistryAddress = new ZeroEx(web3.currentProvider, zeroExConfig);
- expect(zeroExWithWrongTokenRegistryAddress.tokenRegistry.getContractAddress()).to.be.equal(
- ZeroEx.NULL_ADDRESS,
- );
- });
- });
+ const ecSignature = await zeroEx.signOrderHashAsync(
+ orderHash,
+ makerAddress,
+ SHOULD_ADD_PERSONAL_MESSAGE_PREFIX,
+ );
+ expect(ecSignature).to.deep.equal(expectedECSignature);
+ });
+ });
+ describe('#awaitTransactionMinedAsync', () => {
+ beforeEach(async () => {
+ await blockchainLifecycle.startAsync();
+ });
+ afterEach(async () => {
+ await blockchainLifecycle.revertAsync();
+ });
+ it('returns transaction receipt with decoded logs', async () => {
+ const availableAddresses = await zeroEx.getAvailableAddressesAsync();
+ const coinbase = availableAddresses[0];
+ const tokens = await zeroEx.tokenRegistry.getTokensAsync();
+ const tokenUtils = new TokenUtils(tokens);
+ const zrxTokenAddress = tokenUtils.getProtocolTokenOrThrow().address;
+ const proxyAddress = zeroEx.proxy.getContractAddress();
+ const txHash = await zeroEx.token.setUnlimitedProxyAllowanceAsync(zrxTokenAddress, coinbase);
+ const txReceiptWithDecodedLogs = await zeroEx.awaitTransactionMinedAsync(txHash);
+ const log = txReceiptWithDecodedLogs.logs[0] as LogWithDecodedArgs<ApprovalContractEventArgs>;
+ expect(log.event).to.be.equal(TokenEvents.Approval);
+ expect(log.args._owner).to.be.equal(coinbase);
+ expect(log.args._spender).to.be.equal(proxyAddress);
+ expect(log.args._value).to.be.bignumber.equal(zeroEx.token.UNLIMITED_ALLOWANCE_IN_BASE_UNITS);
+ });
+ });
+ describe('#config', () => {
+ it('allows to specify exchange contract address', async () => {
+ const zeroExConfig = {
+ exchangeContractAddress: ZeroEx.NULL_ADDRESS,
+ networkId: constants.TESTRPC_NETWORK_ID,
+ };
+ const zeroExWithWrongExchangeAddress = new ZeroEx(web3.currentProvider, zeroExConfig);
+ expect(zeroExWithWrongExchangeAddress.exchange.getContractAddress()).to.be.equal(ZeroEx.NULL_ADDRESS);
+ });
+ it('allows to specify token registry token contract address', async () => {
+ const zeroExConfig = {
+ tokenRegistryContractAddress: ZeroEx.NULL_ADDRESS,
+ networkId: constants.TESTRPC_NETWORK_ID,
+ };
+ const zeroExWithWrongTokenRegistryAddress = new ZeroEx(web3.currentProvider, zeroExConfig);
+ expect(zeroExWithWrongTokenRegistryAddress.tokenRegistry.getContractAddress()).to.be.equal(
+ ZeroEx.NULL_ADDRESS,
+ );
+ });
+ });
});
diff --git a/packages/0x.js/test/artifacts_test.ts b/packages/0x.js/test/artifacts_test.ts
index 3a791f436..e8ab9aa97 100644
--- a/packages/0x.js/test/artifacts_test.ts
+++ b/packages/0x.js/test/artifacts_test.ts
@@ -12,44 +12,44 @@ chaiSetup.configure();
const TIMEOUT = 10000;
describe('Artifacts', () => {
- describe('contracts are deployed on kovan', () => {
- const kovanRpcUrl = constants.KOVAN_RPC_URL;
- const packageJSONContent = fs.readFileSync('package.json', 'utf-8');
- const packageJSON = JSON.parse(packageJSONContent);
- const mnemonic = packageJSON.config.mnemonic;
- const web3Provider = new HDWalletProvider(mnemonic, kovanRpcUrl);
- const config = {
- networkId: constants.KOVAN_NETWORK_ID,
- };
- const zeroEx = new ZeroEx(web3Provider, config);
- it('token registry contract is deployed', async () => {
- await (zeroEx.tokenRegistry as any)._getTokenRegistryContractAsync();
- }).timeout(TIMEOUT);
- it('proxy contract is deployed', async () => {
- await (zeroEx.proxy as any)._getTokenTransferProxyContractAsync();
- }).timeout(TIMEOUT);
- it('exchange contract is deployed', async () => {
- await (zeroEx.exchange as any)._getExchangeContractAsync();
- }).timeout(TIMEOUT);
- });
- describe('contracts are deployed on ropsten', () => {
- const ropstenRpcUrl = constants.ROPSTEN_RPC_URL;
- const packageJSONContent = fs.readFileSync('package.json', 'utf-8');
- const packageJSON = JSON.parse(packageJSONContent);
- const mnemonic = packageJSON.config.mnemonic;
- const web3Provider = new HDWalletProvider(mnemonic, ropstenRpcUrl);
- const config = {
- networkId: constants.ROPSTEN_NETWORK_ID,
- };
- const zeroEx = new ZeroEx(web3Provider, config);
- it('token registry contract is deployed', async () => {
- await (zeroEx.tokenRegistry as any)._getTokenRegistryContractAsync();
- }).timeout(TIMEOUT);
- it('proxy contract is deployed', async () => {
- await (zeroEx.proxy as any)._getTokenTransferProxyContractAsync();
- }).timeout(TIMEOUT);
- it('exchange contract is deployed', async () => {
- await (zeroEx.exchange as any)._getExchangeContractAsync();
- }).timeout(TIMEOUT);
- });
+ describe('contracts are deployed on kovan', () => {
+ const kovanRpcUrl = constants.KOVAN_RPC_URL;
+ const packageJSONContent = fs.readFileSync('package.json', 'utf-8');
+ const packageJSON = JSON.parse(packageJSONContent);
+ const mnemonic = packageJSON.config.mnemonic;
+ const web3Provider = new HDWalletProvider(mnemonic, kovanRpcUrl);
+ const config = {
+ networkId: constants.KOVAN_NETWORK_ID,
+ };
+ const zeroEx = new ZeroEx(web3Provider, config);
+ it('token registry contract is deployed', async () => {
+ await (zeroEx.tokenRegistry as any)._getTokenRegistryContractAsync();
+ }).timeout(TIMEOUT);
+ it('proxy contract is deployed', async () => {
+ await (zeroEx.proxy as any)._getTokenTransferProxyContractAsync();
+ }).timeout(TIMEOUT);
+ it('exchange contract is deployed', async () => {
+ await (zeroEx.exchange as any)._getExchangeContractAsync();
+ }).timeout(TIMEOUT);
+ });
+ describe('contracts are deployed on ropsten', () => {
+ const ropstenRpcUrl = constants.ROPSTEN_RPC_URL;
+ const packageJSONContent = fs.readFileSync('package.json', 'utf-8');
+ const packageJSON = JSON.parse(packageJSONContent);
+ const mnemonic = packageJSON.config.mnemonic;
+ const web3Provider = new HDWalletProvider(mnemonic, ropstenRpcUrl);
+ const config = {
+ networkId: constants.ROPSTEN_NETWORK_ID,
+ };
+ const zeroEx = new ZeroEx(web3Provider, config);
+ it('token registry contract is deployed', async () => {
+ await (zeroEx.tokenRegistry as any)._getTokenRegistryContractAsync();
+ }).timeout(TIMEOUT);
+ it('proxy contract is deployed', async () => {
+ await (zeroEx.proxy as any)._getTokenTransferProxyContractAsync();
+ }).timeout(TIMEOUT);
+ it('exchange contract is deployed', async () => {
+ await (zeroEx.exchange as any)._getExchangeContractAsync();
+ }).timeout(TIMEOUT);
+ });
});
diff --git a/packages/0x.js/test/assert_test.ts b/packages/0x.js/test/assert_test.ts
index 2c72a5e88..1f2820070 100644
--- a/packages/0x.js/test/assert_test.ts
+++ b/packages/0x.js/test/assert_test.ts
@@ -10,34 +10,34 @@ import { web3Factory } from './utils/web3_factory';
const expect = chai.expect;
describe('Assertion library', () => {
- const web3 = web3Factory.create();
- const config = {
- networkId: constants.TESTRPC_NETWORK_ID,
- };
- const zeroEx = new ZeroEx(web3.currentProvider, config);
- describe('#isSenderAddressHexAsync', () => {
- it('throws when address is invalid', async () => {
- const address = '0xdeadbeef';
- const varName = 'address';
- return expect(
- assert.isSenderAddressAsync(varName, address, (zeroEx as any)._web3Wrapper),
- ).to.be.rejectedWith(`Expected ${varName} to be of type ETHAddressHex, encountered: ${address}`);
- });
- it('throws when address is unavailable', async () => {
- const validUnrelatedAddress = '0x8b0292b11a196601eddce54b665cafeca0347d42';
- const varName = 'address';
- return expect(
- assert.isSenderAddressAsync(varName, validUnrelatedAddress, (zeroEx as any)._web3Wrapper),
- ).to.be.rejectedWith(
- `Specified ${varName} ${validUnrelatedAddress} isn't available through the supplied web3 provider`,
- );
- });
- it("doesn't throw if address is available", async () => {
- const availableAddress = (await zeroEx.getAvailableAddressesAsync())[0];
- const varName = 'address';
- return expect(
- assert.isSenderAddressAsync(varName, availableAddress, (zeroEx as any)._web3Wrapper),
- ).to.become(undefined);
- });
- });
+ const web3 = web3Factory.create();
+ const config = {
+ networkId: constants.TESTRPC_NETWORK_ID,
+ };
+ const zeroEx = new ZeroEx(web3.currentProvider, config);
+ describe('#isSenderAddressHexAsync', () => {
+ it('throws when address is invalid', async () => {
+ const address = '0xdeadbeef';
+ const varName = 'address';
+ return expect(
+ assert.isSenderAddressAsync(varName, address, (zeroEx as any)._web3Wrapper),
+ ).to.be.rejectedWith(`Expected ${varName} to be of type ETHAddressHex, encountered: ${address}`);
+ });
+ it('throws when address is unavailable', async () => {
+ const validUnrelatedAddress = '0x8b0292b11a196601eddce54b665cafeca0347d42';
+ const varName = 'address';
+ return expect(
+ assert.isSenderAddressAsync(varName, validUnrelatedAddress, (zeroEx as any)._web3Wrapper),
+ ).to.be.rejectedWith(
+ `Specified ${varName} ${validUnrelatedAddress} isn't available through the supplied web3 provider`,
+ );
+ });
+ it("doesn't throw if address is available", async () => {
+ const availableAddress = (await zeroEx.getAvailableAddressesAsync())[0];
+ const varName = 'address';
+ return expect(
+ assert.isSenderAddressAsync(varName, availableAddress, (zeroEx as any)._web3Wrapper),
+ ).to.become(undefined);
+ });
+ });
});
diff --git a/packages/0x.js/test/ether_token_wrapper_test.ts b/packages/0x.js/test/ether_token_wrapper_test.ts
index 67e62dc12..b810fc9f1 100644
--- a/packages/0x.js/test/ether_token_wrapper_test.ts
+++ b/packages/0x.js/test/ether_token_wrapper_test.ts
@@ -5,17 +5,17 @@ import 'mocha';
import * as Web3 from 'web3';
import {
- ApprovalContractEventArgs,
- BlockParamLiteral,
- BlockRange,
- DecodedLogEvent,
- DepositContractEventArgs,
- EtherTokenEvents,
- Token,
- TransferContractEventArgs,
- WithdrawalContractEventArgs,
- ZeroEx,
- ZeroExError,
+ ApprovalContractEventArgs,
+ BlockParamLiteral,
+ BlockRange,
+ DecodedLogEvent,
+ DepositContractEventArgs,
+ EtherTokenEvents,
+ Token,
+ TransferContractEventArgs,
+ WithdrawalContractEventArgs,
+ ZeroEx,
+ ZeroExError,
} from '../src';
import { artifacts } from '../src/artifacts';
import { DoneCallback } from '../src/types';
@@ -37,339 +37,339 @@ const blockchainLifecycle = new BlockchainLifecycle(constants.RPC_URL);
const MAX_REASONABLE_GAS_COST_IN_WEI = 62517;
describe('EtherTokenWrapper', () => {
- let web3: Web3;
- let zeroEx: ZeroEx;
- let tokens: Token[];
- let userAddresses: string[];
- let addressWithETH: string;
- let wethContractAddress: string;
- let depositWeiAmount: BigNumber;
- let decimalPlaces: number;
- let addressWithoutFunds: string;
- const gasPrice = new BigNumber(1);
- const zeroExConfig = {
- gasPrice,
- networkId: constants.TESTRPC_NETWORK_ID,
- };
- const transferAmount = new BigNumber(42);
- const allowanceAmount = new BigNumber(42);
- const depositAmount = new BigNumber(42);
- const withdrawalAmount = new BigNumber(42);
- before(async () => {
- web3 = web3Factory.create();
- zeroEx = new ZeroEx(web3.currentProvider, zeroExConfig);
- tokens = await zeroEx.tokenRegistry.getTokensAsync();
- userAddresses = await zeroEx.getAvailableAddressesAsync();
- addressWithETH = userAddresses[0];
- wethContractAddress = (zeroEx.etherToken as any)._getContractAddress(artifacts.EtherTokenArtifact);
- depositWeiAmount = (zeroEx as any)._web3Wrapper.toWei(new BigNumber(5));
- decimalPlaces = 7;
- addressWithoutFunds = userAddresses[1];
- });
- beforeEach(async () => {
- await blockchainLifecycle.startAsync();
- });
- afterEach(async () => {
- await blockchainLifecycle.revertAsync();
- });
- describe('#depositAsync', () => {
- it('should successfully deposit ETH and issue Wrapped ETH tokens', async () => {
- const preETHBalance = await (zeroEx as any)._web3Wrapper.getBalanceInWeiAsync(addressWithETH);
- const preWETHBalance = await zeroEx.token.getBalanceAsync(wethContractAddress, addressWithETH);
- expect(preETHBalance).to.be.bignumber.gt(0);
- expect(preWETHBalance).to.be.bignumber.equal(0);
+ let web3: Web3;
+ let zeroEx: ZeroEx;
+ let tokens: Token[];
+ let userAddresses: string[];
+ let addressWithETH: string;
+ let wethContractAddress: string;
+ let depositWeiAmount: BigNumber;
+ let decimalPlaces: number;
+ let addressWithoutFunds: string;
+ const gasPrice = new BigNumber(1);
+ const zeroExConfig = {
+ gasPrice,
+ networkId: constants.TESTRPC_NETWORK_ID,
+ };
+ const transferAmount = new BigNumber(42);
+ const allowanceAmount = new BigNumber(42);
+ const depositAmount = new BigNumber(42);
+ const withdrawalAmount = new BigNumber(42);
+ before(async () => {
+ web3 = web3Factory.create();
+ zeroEx = new ZeroEx(web3.currentProvider, zeroExConfig);
+ tokens = await zeroEx.tokenRegistry.getTokensAsync();
+ userAddresses = await zeroEx.getAvailableAddressesAsync();
+ addressWithETH = userAddresses[0];
+ wethContractAddress = (zeroEx.etherToken as any)._getContractAddress(artifacts.EtherTokenArtifact);
+ depositWeiAmount = (zeroEx as any)._web3Wrapper.toWei(new BigNumber(5));
+ decimalPlaces = 7;
+ addressWithoutFunds = userAddresses[1];
+ });
+ beforeEach(async () => {
+ await blockchainLifecycle.startAsync();
+ });
+ afterEach(async () => {
+ await blockchainLifecycle.revertAsync();
+ });
+ describe('#depositAsync', () => {
+ it('should successfully deposit ETH and issue Wrapped ETH tokens', async () => {
+ const preETHBalance = await (zeroEx as any)._web3Wrapper.getBalanceInWeiAsync(addressWithETH);
+ const preWETHBalance = await zeroEx.token.getBalanceAsync(wethContractAddress, addressWithETH);
+ expect(preETHBalance).to.be.bignumber.gt(0);
+ expect(preWETHBalance).to.be.bignumber.equal(0);
- const txHash = await zeroEx.etherToken.depositAsync(wethContractAddress, depositWeiAmount, addressWithETH);
- await zeroEx.awaitTransactionMinedAsync(txHash);
+ const txHash = await zeroEx.etherToken.depositAsync(wethContractAddress, depositWeiAmount, addressWithETH);
+ await zeroEx.awaitTransactionMinedAsync(txHash);
- const postETHBalanceInWei = await (zeroEx as any)._web3Wrapper.getBalanceInWeiAsync(addressWithETH);
- const postWETHBalanceInBaseUnits = await zeroEx.token.getBalanceAsync(wethContractAddress, addressWithETH);
+ const postETHBalanceInWei = await (zeroEx as any)._web3Wrapper.getBalanceInWeiAsync(addressWithETH);
+ const postWETHBalanceInBaseUnits = await zeroEx.token.getBalanceAsync(wethContractAddress, addressWithETH);
- expect(postWETHBalanceInBaseUnits).to.be.bignumber.equal(depositWeiAmount);
- const remainingETHInWei = preETHBalance.minus(depositWeiAmount);
- const gasCost = remainingETHInWei.minus(postETHBalanceInWei);
- expect(gasCost).to.be.bignumber.lte(MAX_REASONABLE_GAS_COST_IN_WEI);
- });
- it('should throw if user has insufficient ETH balance for deposit', async () => {
- const preETHBalance = await (zeroEx as any)._web3Wrapper.getBalanceInWeiAsync(addressWithETH);
+ expect(postWETHBalanceInBaseUnits).to.be.bignumber.equal(depositWeiAmount);
+ const remainingETHInWei = preETHBalance.minus(depositWeiAmount);
+ const gasCost = remainingETHInWei.minus(postETHBalanceInWei);
+ expect(gasCost).to.be.bignumber.lte(MAX_REASONABLE_GAS_COST_IN_WEI);
+ });
+ it('should throw if user has insufficient ETH balance for deposit', async () => {
+ const preETHBalance = await (zeroEx as any)._web3Wrapper.getBalanceInWeiAsync(addressWithETH);
- const extraETHBalance = (zeroEx as any)._web3Wrapper.toWei(5, 'ether');
- const overETHBalanceinWei = preETHBalance.add(extraETHBalance);
+ const extraETHBalance = (zeroEx as any)._web3Wrapper.toWei(5, 'ether');
+ const overETHBalanceinWei = preETHBalance.add(extraETHBalance);
- return expect(
- zeroEx.etherToken.depositAsync(wethContractAddress, overETHBalanceinWei, addressWithETH),
- ).to.be.rejectedWith(ZeroExError.InsufficientEthBalanceForDeposit);
- });
- });
- describe('#withdrawAsync', () => {
- it('should successfully withdraw ETH in return for Wrapped ETH tokens', async () => {
- const ETHBalanceInWei = await (zeroEx as any)._web3Wrapper.getBalanceInWeiAsync(addressWithETH);
+ return expect(
+ zeroEx.etherToken.depositAsync(wethContractAddress, overETHBalanceinWei, addressWithETH),
+ ).to.be.rejectedWith(ZeroExError.InsufficientEthBalanceForDeposit);
+ });
+ });
+ describe('#withdrawAsync', () => {
+ it('should successfully withdraw ETH in return for Wrapped ETH tokens', async () => {
+ const ETHBalanceInWei = await (zeroEx as any)._web3Wrapper.getBalanceInWeiAsync(addressWithETH);
- await zeroEx.etherToken.depositAsync(wethContractAddress, depositWeiAmount, addressWithETH);
+ await zeroEx.etherToken.depositAsync(wethContractAddress, depositWeiAmount, addressWithETH);
- const expectedPreETHBalance = ETHBalanceInWei.minus(depositWeiAmount);
- const preETHBalance = await (zeroEx as any)._web3Wrapper.getBalanceInWeiAsync(addressWithETH);
- const preWETHBalance = await zeroEx.token.getBalanceAsync(wethContractAddress, addressWithETH);
- let gasCost = expectedPreETHBalance.minus(preETHBalance);
- expect(gasCost).to.be.bignumber.lte(MAX_REASONABLE_GAS_COST_IN_WEI);
- expect(preWETHBalance).to.be.bignumber.equal(depositWeiAmount);
+ const expectedPreETHBalance = ETHBalanceInWei.minus(depositWeiAmount);
+ const preETHBalance = await (zeroEx as any)._web3Wrapper.getBalanceInWeiAsync(addressWithETH);
+ const preWETHBalance = await zeroEx.token.getBalanceAsync(wethContractAddress, addressWithETH);
+ let gasCost = expectedPreETHBalance.minus(preETHBalance);
+ expect(gasCost).to.be.bignumber.lte(MAX_REASONABLE_GAS_COST_IN_WEI);
+ expect(preWETHBalance).to.be.bignumber.equal(depositWeiAmount);
- const txHash = await zeroEx.etherToken.withdrawAsync(wethContractAddress, depositWeiAmount, addressWithETH);
- await zeroEx.awaitTransactionMinedAsync(txHash);
+ const txHash = await zeroEx.etherToken.withdrawAsync(wethContractAddress, depositWeiAmount, addressWithETH);
+ await zeroEx.awaitTransactionMinedAsync(txHash);
- const postETHBalance = await (zeroEx as any)._web3Wrapper.getBalanceInWeiAsync(addressWithETH);
- const postWETHBalanceInBaseUnits = await zeroEx.token.getBalanceAsync(wethContractAddress, addressWithETH);
+ const postETHBalance = await (zeroEx as any)._web3Wrapper.getBalanceInWeiAsync(addressWithETH);
+ const postWETHBalanceInBaseUnits = await zeroEx.token.getBalanceAsync(wethContractAddress, addressWithETH);
- expect(postWETHBalanceInBaseUnits).to.be.bignumber.equal(0);
- const expectedETHBalance = preETHBalance.add(depositWeiAmount).round(decimalPlaces);
- gasCost = expectedETHBalance.minus(postETHBalance);
- expect(gasCost).to.be.bignumber.lte(MAX_REASONABLE_GAS_COST_IN_WEI);
- });
- it('should throw if user has insufficient WETH balance for withdrawl', async () => {
- const preWETHBalance = await zeroEx.token.getBalanceAsync(wethContractAddress, addressWithETH);
- expect(preWETHBalance).to.be.bignumber.equal(0);
+ expect(postWETHBalanceInBaseUnits).to.be.bignumber.equal(0);
+ const expectedETHBalance = preETHBalance.add(depositWeiAmount).round(decimalPlaces);
+ gasCost = expectedETHBalance.minus(postETHBalance);
+ expect(gasCost).to.be.bignumber.lte(MAX_REASONABLE_GAS_COST_IN_WEI);
+ });
+ it('should throw if user has insufficient WETH balance for withdrawl', async () => {
+ const preWETHBalance = await zeroEx.token.getBalanceAsync(wethContractAddress, addressWithETH);
+ expect(preWETHBalance).to.be.bignumber.equal(0);
- const overWETHBalance = preWETHBalance.add(999999999);
+ const overWETHBalance = preWETHBalance.add(999999999);
- return expect(
- zeroEx.etherToken.withdrawAsync(wethContractAddress, overWETHBalance, addressWithETH),
- ).to.be.rejectedWith(ZeroExError.InsufficientWEthBalanceForWithdrawal);
- });
- });
- describe('#subscribe', () => {
- const indexFilterValues = {};
- let etherTokenAddress: string;
- before(() => {
- const tokenUtils = new TokenUtils(tokens);
- const etherToken = tokenUtils.getWethTokenOrThrow();
- etherTokenAddress = etherToken.address;
- });
- afterEach(() => {
- zeroEx.etherToken.unsubscribeAll();
- });
- // Hack: Mocha does not allow a test to be both async and have a `done` callback
- // Since we need to await the receipt of the event in the `subscribe` callback,
- // we do need both. A hack is to make the top-level async fn w/ a done callback and then
- // wrap the rest of the test in an async block
- // Source: https://github.com/mochajs/mocha/issues/2407
- it('Should receive the Transfer event when tokens are transfered', (done: DoneCallback) => {
- (async () => {
- const callback = reportNodeCallbackErrors(done)(
- (logEvent: DecodedLogEvent<TransferContractEventArgs>) => {
- expect(logEvent).to.not.be.undefined();
- expect(logEvent.isRemoved).to.be.false();
- expect(logEvent.log.logIndex).to.be.equal(0);
- expect(logEvent.log.transactionIndex).to.be.equal(0);
- expect(logEvent.log.blockNumber).to.be.a('number');
- const args = logEvent.log.args;
- expect(args._from).to.be.equal(addressWithETH);
- expect(args._to).to.be.equal(addressWithoutFunds);
- expect(args._value).to.be.bignumber.equal(transferAmount);
- },
- );
- await zeroEx.etherToken.depositAsync(etherTokenAddress, transferAmount, addressWithETH);
- zeroEx.etherToken.subscribe(etherTokenAddress, EtherTokenEvents.Transfer, indexFilterValues, callback);
- await zeroEx.token.transferAsync(
- etherTokenAddress,
- addressWithETH,
- addressWithoutFunds,
- transferAmount,
- );
- })().catch(done);
- });
- it('Should receive the Approval event when allowance is being set', (done: DoneCallback) => {
- (async () => {
- const callback = reportNodeCallbackErrors(done)(
- (logEvent: DecodedLogEvent<ApprovalContractEventArgs>) => {
- expect(logEvent).to.not.be.undefined();
- expect(logEvent.isRemoved).to.be.false();
- const args = logEvent.log.args;
- expect(args._owner).to.be.equal(addressWithETH);
- expect(args._spender).to.be.equal(addressWithoutFunds);
- expect(args._value).to.be.bignumber.equal(allowanceAmount);
- },
- );
- zeroEx.etherToken.subscribe(etherTokenAddress, EtherTokenEvents.Approval, indexFilterValues, callback);
- await zeroEx.token.setAllowanceAsync(
- etherTokenAddress,
- addressWithETH,
- addressWithoutFunds,
- allowanceAmount,
- );
- })().catch(done);
- });
- it('Should receive the Deposit event when ether is being deposited', (done: DoneCallback) => {
- (async () => {
- const callback = reportNodeCallbackErrors(done)(
- (logEvent: DecodedLogEvent<DepositContractEventArgs>) => {
- expect(logEvent).to.not.be.undefined();
- expect(logEvent.isRemoved).to.be.false();
- const args = logEvent.log.args;
- expect(args._owner).to.be.equal(addressWithETH);
- expect(args._value).to.be.bignumber.equal(depositAmount);
- },
- );
- zeroEx.etherToken.subscribe(etherTokenAddress, EtherTokenEvents.Deposit, indexFilterValues, callback);
- await zeroEx.etherToken.depositAsync(etherTokenAddress, depositAmount, addressWithETH);
- })().catch(done);
- });
- it('Should receive the Withdrawal event when ether is being withdrawn', (done: DoneCallback) => {
- (async () => {
- const callback = reportNodeCallbackErrors(done)(
- (logEvent: DecodedLogEvent<WithdrawalContractEventArgs>) => {
- expect(logEvent).to.not.be.undefined();
- expect(logEvent.isRemoved).to.be.false();
- const args = logEvent.log.args;
- expect(args._owner).to.be.equal(addressWithETH);
- expect(args._value).to.be.bignumber.equal(depositAmount);
- },
- );
- await zeroEx.etherToken.depositAsync(etherTokenAddress, depositAmount, addressWithETH);
- zeroEx.etherToken.subscribe(
- etherTokenAddress,
- EtherTokenEvents.Withdrawal,
- indexFilterValues,
- callback,
- );
- await zeroEx.etherToken.withdrawAsync(etherTokenAddress, withdrawalAmount, addressWithETH);
- })().catch(done);
- });
- it('should cancel outstanding subscriptions when ZeroEx.setProvider is called', (done: DoneCallback) => {
- (async () => {
- const callbackNeverToBeCalled = reportNodeCallbackErrors(done)(
- (logEvent: DecodedLogEvent<ApprovalContractEventArgs>) => {
- done(new Error('Expected this subscription to have been cancelled'));
- },
- );
- zeroEx.etherToken.subscribe(
- etherTokenAddress,
- EtherTokenEvents.Transfer,
- indexFilterValues,
- callbackNeverToBeCalled,
- );
- const callbackToBeCalled = reportNodeCallbackErrors(done)();
- const newProvider = web3Factory.getRpcProvider();
- zeroEx.setProvider(newProvider, constants.TESTRPC_NETWORK_ID);
- await zeroEx.etherToken.depositAsync(etherTokenAddress, transferAmount, addressWithETH);
- zeroEx.etherToken.subscribe(
- etherTokenAddress,
- EtherTokenEvents.Transfer,
- indexFilterValues,
- callbackToBeCalled,
- );
- await zeroEx.token.transferAsync(
- etherTokenAddress,
- addressWithETH,
- addressWithoutFunds,
- transferAmount,
- );
- })().catch(done);
- });
- it('Should cancel subscription when unsubscribe called', (done: DoneCallback) => {
- (async () => {
- const callbackNeverToBeCalled = reportNodeCallbackErrors(done)(
- (logEvent: DecodedLogEvent<ApprovalContractEventArgs>) => {
- done(new Error('Expected this subscription to have been cancelled'));
- },
- );
- await zeroEx.etherToken.depositAsync(etherTokenAddress, transferAmount, addressWithETH);
- const subscriptionToken = zeroEx.etherToken.subscribe(
- etherTokenAddress,
- EtherTokenEvents.Transfer,
- indexFilterValues,
- callbackNeverToBeCalled,
- );
- zeroEx.etherToken.unsubscribe(subscriptionToken);
- await zeroEx.token.transferAsync(
- etherTokenAddress,
- addressWithETH,
- addressWithoutFunds,
- transferAmount,
- );
- done();
- })().catch(done);
- });
- });
- describe('#getLogsAsync', () => {
- let etherTokenAddress: string;
- let tokenTransferProxyAddress: string;
- const blockRange: BlockRange = {
- fromBlock: 0,
- toBlock: BlockParamLiteral.Latest,
- };
- let txHash: string;
- before(() => {
- addressWithETH = userAddresses[0];
- const tokenUtils = new TokenUtils(tokens);
- const etherToken = tokenUtils.getWethTokenOrThrow();
- etherTokenAddress = etherToken.address;
- tokenTransferProxyAddress = zeroEx.proxy.getContractAddress();
- });
- it('should get logs with decoded args emitted by Approval', async () => {
- txHash = await zeroEx.token.setUnlimitedProxyAllowanceAsync(etherTokenAddress, addressWithETH);
- await zeroEx.awaitTransactionMinedAsync(txHash);
- const eventName = EtherTokenEvents.Approval;
- const indexFilterValues = {};
- const logs = await zeroEx.etherToken.getLogsAsync<ApprovalContractEventArgs>(
- etherTokenAddress,
- eventName,
- blockRange,
- indexFilterValues,
- );
- expect(logs).to.have.length(1);
- const args = logs[0].args;
- expect(logs[0].event).to.be.equal(eventName);
- expect(args._owner).to.be.equal(addressWithETH);
- expect(args._spender).to.be.equal(tokenTransferProxyAddress);
- expect(args._value).to.be.bignumber.equal(zeroEx.token.UNLIMITED_ALLOWANCE_IN_BASE_UNITS);
- });
- it('should get logs with decoded args emitted by Deposit', async () => {
- await zeroEx.etherToken.depositAsync(etherTokenAddress, depositAmount, addressWithETH);
- const eventName = EtherTokenEvents.Deposit;
- const indexFilterValues = {};
- const logs = await zeroEx.etherToken.getLogsAsync<DepositContractEventArgs>(
- etherTokenAddress,
- eventName,
- blockRange,
- indexFilterValues,
- );
- expect(logs).to.have.length(1);
- const args = logs[0].args;
- expect(logs[0].event).to.be.equal(eventName);
- expect(args._owner).to.be.equal(addressWithETH);
- expect(args._value).to.be.bignumber.equal(depositAmount);
- });
- it('should only get the logs with the correct event name', async () => {
- txHash = await zeroEx.token.setUnlimitedProxyAllowanceAsync(etherTokenAddress, addressWithETH);
- await zeroEx.awaitTransactionMinedAsync(txHash);
- const differentEventName = EtherTokenEvents.Transfer;
- const indexFilterValues = {};
- const logs = await zeroEx.etherToken.getLogsAsync(
- etherTokenAddress,
- differentEventName,
- blockRange,
- indexFilterValues,
- );
- expect(logs).to.have.length(0);
- });
- it('should only get the logs with the correct indexed fields', async () => {
- txHash = await zeroEx.token.setUnlimitedProxyAllowanceAsync(etherTokenAddress, addressWithETH);
- await zeroEx.awaitTransactionMinedAsync(txHash);
- txHash = await zeroEx.token.setUnlimitedProxyAllowanceAsync(etherTokenAddress, addressWithoutFunds);
- await zeroEx.awaitTransactionMinedAsync(txHash);
- const eventName = EtherTokenEvents.Approval;
- const indexFilterValues = {
- _owner: addressWithETH,
- };
- const logs = await zeroEx.etherToken.getLogsAsync<ApprovalContractEventArgs>(
- etherTokenAddress,
- eventName,
- blockRange,
- indexFilterValues,
- );
- expect(logs).to.have.length(1);
- const args = logs[0].args;
- expect(args._owner).to.be.equal(addressWithETH);
- });
- });
+ return expect(
+ zeroEx.etherToken.withdrawAsync(wethContractAddress, overWETHBalance, addressWithETH),
+ ).to.be.rejectedWith(ZeroExError.InsufficientWEthBalanceForWithdrawal);
+ });
+ });
+ describe('#subscribe', () => {
+ const indexFilterValues = {};
+ let etherTokenAddress: string;
+ before(() => {
+ const tokenUtils = new TokenUtils(tokens);
+ const etherToken = tokenUtils.getWethTokenOrThrow();
+ etherTokenAddress = etherToken.address;
+ });
+ afterEach(() => {
+ zeroEx.etherToken.unsubscribeAll();
+ });
+ // Hack: Mocha does not allow a test to be both async and have a `done` callback
+ // Since we need to await the receipt of the event in the `subscribe` callback,
+ // we do need both. A hack is to make the top-level async fn w/ a done callback and then
+ // wrap the rest of the test in an async block
+ // Source: https://github.com/mochajs/mocha/issues/2407
+ it('Should receive the Transfer event when tokens are transfered', (done: DoneCallback) => {
+ (async () => {
+ const callback = reportNodeCallbackErrors(done)(
+ (logEvent: DecodedLogEvent<TransferContractEventArgs>) => {
+ expect(logEvent).to.not.be.undefined();
+ expect(logEvent.isRemoved).to.be.false();
+ expect(logEvent.log.logIndex).to.be.equal(0);
+ expect(logEvent.log.transactionIndex).to.be.equal(0);
+ expect(logEvent.log.blockNumber).to.be.a('number');
+ const args = logEvent.log.args;
+ expect(args._from).to.be.equal(addressWithETH);
+ expect(args._to).to.be.equal(addressWithoutFunds);
+ expect(args._value).to.be.bignumber.equal(transferAmount);
+ },
+ );
+ await zeroEx.etherToken.depositAsync(etherTokenAddress, transferAmount, addressWithETH);
+ zeroEx.etherToken.subscribe(etherTokenAddress, EtherTokenEvents.Transfer, indexFilterValues, callback);
+ await zeroEx.token.transferAsync(
+ etherTokenAddress,
+ addressWithETH,
+ addressWithoutFunds,
+ transferAmount,
+ );
+ })().catch(done);
+ });
+ it('Should receive the Approval event when allowance is being set', (done: DoneCallback) => {
+ (async () => {
+ const callback = reportNodeCallbackErrors(done)(
+ (logEvent: DecodedLogEvent<ApprovalContractEventArgs>) => {
+ expect(logEvent).to.not.be.undefined();
+ expect(logEvent.isRemoved).to.be.false();
+ const args = logEvent.log.args;
+ expect(args._owner).to.be.equal(addressWithETH);
+ expect(args._spender).to.be.equal(addressWithoutFunds);
+ expect(args._value).to.be.bignumber.equal(allowanceAmount);
+ },
+ );
+ zeroEx.etherToken.subscribe(etherTokenAddress, EtherTokenEvents.Approval, indexFilterValues, callback);
+ await zeroEx.token.setAllowanceAsync(
+ etherTokenAddress,
+ addressWithETH,
+ addressWithoutFunds,
+ allowanceAmount,
+ );
+ })().catch(done);
+ });
+ it('Should receive the Deposit event when ether is being deposited', (done: DoneCallback) => {
+ (async () => {
+ const callback = reportNodeCallbackErrors(done)(
+ (logEvent: DecodedLogEvent<DepositContractEventArgs>) => {
+ expect(logEvent).to.not.be.undefined();
+ expect(logEvent.isRemoved).to.be.false();
+ const args = logEvent.log.args;
+ expect(args._owner).to.be.equal(addressWithETH);
+ expect(args._value).to.be.bignumber.equal(depositAmount);
+ },
+ );
+ zeroEx.etherToken.subscribe(etherTokenAddress, EtherTokenEvents.Deposit, indexFilterValues, callback);
+ await zeroEx.etherToken.depositAsync(etherTokenAddress, depositAmount, addressWithETH);
+ })().catch(done);
+ });
+ it('Should receive the Withdrawal event when ether is being withdrawn', (done: DoneCallback) => {
+ (async () => {
+ const callback = reportNodeCallbackErrors(done)(
+ (logEvent: DecodedLogEvent<WithdrawalContractEventArgs>) => {
+ expect(logEvent).to.not.be.undefined();
+ expect(logEvent.isRemoved).to.be.false();
+ const args = logEvent.log.args;
+ expect(args._owner).to.be.equal(addressWithETH);
+ expect(args._value).to.be.bignumber.equal(depositAmount);
+ },
+ );
+ await zeroEx.etherToken.depositAsync(etherTokenAddress, depositAmount, addressWithETH);
+ zeroEx.etherToken.subscribe(
+ etherTokenAddress,
+ EtherTokenEvents.Withdrawal,
+ indexFilterValues,
+ callback,
+ );
+ await zeroEx.etherToken.withdrawAsync(etherTokenAddress, withdrawalAmount, addressWithETH);
+ })().catch(done);
+ });
+ it('should cancel outstanding subscriptions when ZeroEx.setProvider is called', (done: DoneCallback) => {
+ (async () => {
+ const callbackNeverToBeCalled = reportNodeCallbackErrors(done)(
+ (logEvent: DecodedLogEvent<ApprovalContractEventArgs>) => {
+ done(new Error('Expected this subscription to have been cancelled'));
+ },
+ );
+ zeroEx.etherToken.subscribe(
+ etherTokenAddress,
+ EtherTokenEvents.Transfer,
+ indexFilterValues,
+ callbackNeverToBeCalled,
+ );
+ const callbackToBeCalled = reportNodeCallbackErrors(done)();
+ const newProvider = web3Factory.getRpcProvider();
+ zeroEx.setProvider(newProvider, constants.TESTRPC_NETWORK_ID);
+ await zeroEx.etherToken.depositAsync(etherTokenAddress, transferAmount, addressWithETH);
+ zeroEx.etherToken.subscribe(
+ etherTokenAddress,
+ EtherTokenEvents.Transfer,
+ indexFilterValues,
+ callbackToBeCalled,
+ );
+ await zeroEx.token.transferAsync(
+ etherTokenAddress,
+ addressWithETH,
+ addressWithoutFunds,
+ transferAmount,
+ );
+ })().catch(done);
+ });
+ it('Should cancel subscription when unsubscribe called', (done: DoneCallback) => {
+ (async () => {
+ const callbackNeverToBeCalled = reportNodeCallbackErrors(done)(
+ (logEvent: DecodedLogEvent<ApprovalContractEventArgs>) => {
+ done(new Error('Expected this subscription to have been cancelled'));
+ },
+ );
+ await zeroEx.etherToken.depositAsync(etherTokenAddress, transferAmount, addressWithETH);
+ const subscriptionToken = zeroEx.etherToken.subscribe(
+ etherTokenAddress,
+ EtherTokenEvents.Transfer,
+ indexFilterValues,
+ callbackNeverToBeCalled,
+ );
+ zeroEx.etherToken.unsubscribe(subscriptionToken);
+ await zeroEx.token.transferAsync(
+ etherTokenAddress,
+ addressWithETH,
+ addressWithoutFunds,
+ transferAmount,
+ );
+ done();
+ })().catch(done);
+ });
+ });
+ describe('#getLogsAsync', () => {
+ let etherTokenAddress: string;
+ let tokenTransferProxyAddress: string;
+ const blockRange: BlockRange = {
+ fromBlock: 0,
+ toBlock: BlockParamLiteral.Latest,
+ };
+ let txHash: string;
+ before(() => {
+ addressWithETH = userAddresses[0];
+ const tokenUtils = new TokenUtils(tokens);
+ const etherToken = tokenUtils.getWethTokenOrThrow();
+ etherTokenAddress = etherToken.address;
+ tokenTransferProxyAddress = zeroEx.proxy.getContractAddress();
+ });
+ it('should get logs with decoded args emitted by Approval', async () => {
+ txHash = await zeroEx.token.setUnlimitedProxyAllowanceAsync(etherTokenAddress, addressWithETH);
+ await zeroEx.awaitTransactionMinedAsync(txHash);
+ const eventName = EtherTokenEvents.Approval;
+ const indexFilterValues = {};
+ const logs = await zeroEx.etherToken.getLogsAsync<ApprovalContractEventArgs>(
+ etherTokenAddress,
+ eventName,
+ blockRange,
+ indexFilterValues,
+ );
+ expect(logs).to.have.length(1);
+ const args = logs[0].args;
+ expect(logs[0].event).to.be.equal(eventName);
+ expect(args._owner).to.be.equal(addressWithETH);
+ expect(args._spender).to.be.equal(tokenTransferProxyAddress);
+ expect(args._value).to.be.bignumber.equal(zeroEx.token.UNLIMITED_ALLOWANCE_IN_BASE_UNITS);
+ });
+ it('should get logs with decoded args emitted by Deposit', async () => {
+ await zeroEx.etherToken.depositAsync(etherTokenAddress, depositAmount, addressWithETH);
+ const eventName = EtherTokenEvents.Deposit;
+ const indexFilterValues = {};
+ const logs = await zeroEx.etherToken.getLogsAsync<DepositContractEventArgs>(
+ etherTokenAddress,
+ eventName,
+ blockRange,
+ indexFilterValues,
+ );
+ expect(logs).to.have.length(1);
+ const args = logs[0].args;
+ expect(logs[0].event).to.be.equal(eventName);
+ expect(args._owner).to.be.equal(addressWithETH);
+ expect(args._value).to.be.bignumber.equal(depositAmount);
+ });
+ it('should only get the logs with the correct event name', async () => {
+ txHash = await zeroEx.token.setUnlimitedProxyAllowanceAsync(etherTokenAddress, addressWithETH);
+ await zeroEx.awaitTransactionMinedAsync(txHash);
+ const differentEventName = EtherTokenEvents.Transfer;
+ const indexFilterValues = {};
+ const logs = await zeroEx.etherToken.getLogsAsync(
+ etherTokenAddress,
+ differentEventName,
+ blockRange,
+ indexFilterValues,
+ );
+ expect(logs).to.have.length(0);
+ });
+ it('should only get the logs with the correct indexed fields', async () => {
+ txHash = await zeroEx.token.setUnlimitedProxyAllowanceAsync(etherTokenAddress, addressWithETH);
+ await zeroEx.awaitTransactionMinedAsync(txHash);
+ txHash = await zeroEx.token.setUnlimitedProxyAllowanceAsync(etherTokenAddress, addressWithoutFunds);
+ await zeroEx.awaitTransactionMinedAsync(txHash);
+ const eventName = EtherTokenEvents.Approval;
+ const indexFilterValues = {
+ _owner: addressWithETH,
+ };
+ const logs = await zeroEx.etherToken.getLogsAsync<ApprovalContractEventArgs>(
+ etherTokenAddress,
+ eventName,
+ blockRange,
+ indexFilterValues,
+ );
+ expect(logs).to.have.length(1);
+ const args = logs[0].args;
+ expect(args._owner).to.be.equal(addressWithETH);
+ });
+ });
});
diff --git a/packages/0x.js/test/event_watcher_test.ts b/packages/0x.js/test/event_watcher_test.ts
index f0c97eced..f92fb2b02 100644
--- a/packages/0x.js/test/event_watcher_test.ts
+++ b/packages/0x.js/test/event_watcher_test.ts
@@ -17,110 +17,110 @@ chaiSetup.configure();
const expect = chai.expect;
describe('EventWatcher', () => {
- let web3: Web3;
- let stubs: Sinon.SinonStub[] = [];
- let eventWatcher: EventWatcher;
- let web3Wrapper: Web3Wrapper;
- const logA: Web3.LogEntry = {
- address: '0x71d271f8b14adef568f8f28f1587ce7271ac4ca5',
- blockHash: null,
- blockNumber: null,
- data: '',
- logIndex: null,
- topics: [],
- transactionHash: '0x004881d38cd4a8f72f1a0d68c8b9b8124504706041ff37019c1d1ed6bfda8e17',
- transactionIndex: 0,
- };
- const logB: Web3.LogEntry = {
- address: '0x8d12a197cb00d4747a1fe03395095ce2a5cc6819',
- blockHash: null,
- blockNumber: null,
- data: '',
- logIndex: null,
- topics: ['0xf341246adaac6f497bc2a656f546ab9e182111d630394f0c57c710a59a2cb567'],
- transactionHash: '0x01ef3c048b18d9b09ea195b4ed94cf8dd5f3d857a1905ff886b152cfb1166f25',
- transactionIndex: 0,
- };
- const logC: Web3.LogEntry = {
- address: '0x1d271f8b174adef58f1587ce68f8f27271ac4ca5',
- blockHash: null,
- blockNumber: null,
- data: '',
- logIndex: null,
- topics: ['0xf341246adaac6f497bc2a656f546ab9e182111d630394f0c57c710a59a2cb567'],
- transactionHash: '0x01ef3c048b18d9b09ea195b4ed94cf8dd5f3d857a1905ff886b152cfb1166f25',
- transactionIndex: 0,
- };
- before(async () => {
- web3 = web3Factory.create();
- const pollingIntervalMs = 10;
- web3Wrapper = new Web3Wrapper(web3.currentProvider);
- eventWatcher = new EventWatcher(web3Wrapper, pollingIntervalMs);
- });
- afterEach(() => {
- // clean up any stubs after the test has completed
- _.each(stubs, s => s.restore());
- stubs = [];
- eventWatcher.unsubscribe();
- });
- it('correctly emits initial log events', (done: DoneCallback) => {
- const logs: Web3.LogEntry[] = [logA, logB];
- const expectedLogEvents = [
- {
- removed: false,
- ...logA,
- },
- {
- removed: false,
- ...logB,
- },
- ];
- const getLogsStub = Sinon.stub(web3Wrapper, 'getLogsAsync');
- getLogsStub.onCall(0).returns(logs);
- stubs.push(getLogsStub);
- const expectedToBeCalledOnce = false;
- const callback = reportNodeCallbackErrors(done, expectedToBeCalledOnce)((event: LogEvent) => {
- const expectedLogEvent = expectedLogEvents.shift();
- expect(event).to.be.deep.equal(expectedLogEvent);
- if (_.isEmpty(expectedLogEvents)) {
- done();
- }
- });
- eventWatcher.subscribe(callback);
- });
- it('correctly computes the difference and emits only changes', (done: DoneCallback) => {
- const initialLogs: Web3.LogEntry[] = [logA, logB];
- const changedLogs: Web3.LogEntry[] = [logA, logC];
- const expectedLogEvents = [
- {
- removed: false,
- ...logA,
- },
- {
- removed: false,
- ...logB,
- },
- {
- removed: true,
- ...logB,
- },
- {
- removed: false,
- ...logC,
- },
- ];
- const getLogsStub = Sinon.stub(web3Wrapper, 'getLogsAsync');
- getLogsStub.onCall(0).returns(initialLogs);
- getLogsStub.onCall(1).returns(changedLogs);
- stubs.push(getLogsStub);
- const expectedToBeCalledOnce = false;
- const callback = reportNodeCallbackErrors(done, expectedToBeCalledOnce)((event: LogEvent) => {
- const expectedLogEvent = expectedLogEvents.shift();
- expect(event).to.be.deep.equal(expectedLogEvent);
- if (_.isEmpty(expectedLogEvents)) {
- done();
- }
- });
- eventWatcher.subscribe(callback);
- });
+ let web3: Web3;
+ let stubs: Sinon.SinonStub[] = [];
+ let eventWatcher: EventWatcher;
+ let web3Wrapper: Web3Wrapper;
+ const logA: Web3.LogEntry = {
+ address: '0x71d271f8b14adef568f8f28f1587ce7271ac4ca5',
+ blockHash: null,
+ blockNumber: null,
+ data: '',
+ logIndex: null,
+ topics: [],
+ transactionHash: '0x004881d38cd4a8f72f1a0d68c8b9b8124504706041ff37019c1d1ed6bfda8e17',
+ transactionIndex: 0,
+ };
+ const logB: Web3.LogEntry = {
+ address: '0x8d12a197cb00d4747a1fe03395095ce2a5cc6819',
+ blockHash: null,
+ blockNumber: null,
+ data: '',
+ logIndex: null,
+ topics: ['0xf341246adaac6f497bc2a656f546ab9e182111d630394f0c57c710a59a2cb567'],
+ transactionHash: '0x01ef3c048b18d9b09ea195b4ed94cf8dd5f3d857a1905ff886b152cfb1166f25',
+ transactionIndex: 0,
+ };
+ const logC: Web3.LogEntry = {
+ address: '0x1d271f8b174adef58f1587ce68f8f27271ac4ca5',
+ blockHash: null,
+ blockNumber: null,
+ data: '',
+ logIndex: null,
+ topics: ['0xf341246adaac6f497bc2a656f546ab9e182111d630394f0c57c710a59a2cb567'],
+ transactionHash: '0x01ef3c048b18d9b09ea195b4ed94cf8dd5f3d857a1905ff886b152cfb1166f25',
+ transactionIndex: 0,
+ };
+ before(async () => {
+ web3 = web3Factory.create();
+ const pollingIntervalMs = 10;
+ web3Wrapper = new Web3Wrapper(web3.currentProvider);
+ eventWatcher = new EventWatcher(web3Wrapper, pollingIntervalMs);
+ });
+ afterEach(() => {
+ // clean up any stubs after the test has completed
+ _.each(stubs, s => s.restore());
+ stubs = [];
+ eventWatcher.unsubscribe();
+ });
+ it('correctly emits initial log events', (done: DoneCallback) => {
+ const logs: Web3.LogEntry[] = [logA, logB];
+ const expectedLogEvents = [
+ {
+ removed: false,
+ ...logA,
+ },
+ {
+ removed: false,
+ ...logB,
+ },
+ ];
+ const getLogsStub = Sinon.stub(web3Wrapper, 'getLogsAsync');
+ getLogsStub.onCall(0).returns(logs);
+ stubs.push(getLogsStub);
+ const expectedToBeCalledOnce = false;
+ const callback = reportNodeCallbackErrors(done, expectedToBeCalledOnce)((event: LogEvent) => {
+ const expectedLogEvent = expectedLogEvents.shift();
+ expect(event).to.be.deep.equal(expectedLogEvent);
+ if (_.isEmpty(expectedLogEvents)) {
+ done();
+ }
+ });
+ eventWatcher.subscribe(callback);
+ });
+ it('correctly computes the difference and emits only changes', (done: DoneCallback) => {
+ const initialLogs: Web3.LogEntry[] = [logA, logB];
+ const changedLogs: Web3.LogEntry[] = [logA, logC];
+ const expectedLogEvents = [
+ {
+ removed: false,
+ ...logA,
+ },
+ {
+ removed: false,
+ ...logB,
+ },
+ {
+ removed: true,
+ ...logB,
+ },
+ {
+ removed: false,
+ ...logC,
+ },
+ ];
+ const getLogsStub = Sinon.stub(web3Wrapper, 'getLogsAsync');
+ getLogsStub.onCall(0).returns(initialLogs);
+ getLogsStub.onCall(1).returns(changedLogs);
+ stubs.push(getLogsStub);
+ const expectedToBeCalledOnce = false;
+ const callback = reportNodeCallbackErrors(done, expectedToBeCalledOnce)((event: LogEvent) => {
+ const expectedLogEvent = expectedLogEvents.shift();
+ expect(event).to.be.deep.equal(expectedLogEvent);
+ if (_.isEmpty(expectedLogEvents)) {
+ done();
+ }
+ });
+ eventWatcher.subscribe(callback);
+ });
});
diff --git a/packages/0x.js/test/exchange_transfer_simulator_test.ts b/packages/0x.js/test/exchange_transfer_simulator_test.ts
index 9f4d76933..20b4a05ca 100644
--- a/packages/0x.js/test/exchange_transfer_simulator_test.ts
+++ b/packages/0x.js/test/exchange_transfer_simulator_test.ts
@@ -15,103 +15,103 @@ const expect = chai.expect;
const blockchainLifecycle = new BlockchainLifecycle(constants.RPC_URL);
describe('ExchangeTransferSimulator', () => {
- const web3 = web3Factory.create();
- const config = {
- networkId: constants.TESTRPC_NETWORK_ID,
- };
- const zeroEx = new ZeroEx(web3.currentProvider, config);
- const transferAmount = new BigNumber(5);
- let userAddresses: string[];
- let tokens: Token[];
- let coinbase: string;
- let sender: string;
- let recipient: string;
- let exampleTokenAddress: string;
- let exchangeTransferSimulator: ExchangeTransferSimulator;
- let txHash: string;
- before(async () => {
- userAddresses = await zeroEx.getAvailableAddressesAsync();
- [coinbase, sender, recipient] = userAddresses;
- tokens = await zeroEx.tokenRegistry.getTokensAsync();
- exampleTokenAddress = tokens[0].address;
- });
- beforeEach(async () => {
- await blockchainLifecycle.startAsync();
- });
- afterEach(async () => {
- await blockchainLifecycle.revertAsync();
- });
- describe('#transferFromAsync', () => {
- beforeEach(() => {
- exchangeTransferSimulator = new ExchangeTransferSimulator(zeroEx.token, BlockParamLiteral.Latest);
- });
- it("throws if the user doesn't have enough allowance", async () => {
- return expect(
- exchangeTransferSimulator.transferFromAsync(
- exampleTokenAddress,
- sender,
- recipient,
- transferAmount,
- TradeSide.Taker,
- TransferType.Trade,
- ),
- ).to.be.rejectedWith(ExchangeContractErrs.InsufficientTakerAllowance);
- });
- it("throws if the user doesn't have enough balance", async () => {
- txHash = await zeroEx.token.setProxyAllowanceAsync(exampleTokenAddress, sender, transferAmount);
- await zeroEx.awaitTransactionMinedAsync(txHash);
- return expect(
- exchangeTransferSimulator.transferFromAsync(
- exampleTokenAddress,
- sender,
- recipient,
- transferAmount,
- TradeSide.Maker,
- TransferType.Trade,
- ),
- ).to.be.rejectedWith(ExchangeContractErrs.InsufficientMakerBalance);
- });
- it('updates balances and proxyAllowance after transfer', async () => {
- txHash = await zeroEx.token.transferAsync(exampleTokenAddress, coinbase, sender, transferAmount);
- await zeroEx.awaitTransactionMinedAsync(txHash);
- txHash = await zeroEx.token.setProxyAllowanceAsync(exampleTokenAddress, sender, transferAmount);
- await zeroEx.awaitTransactionMinedAsync(txHash);
- await exchangeTransferSimulator.transferFromAsync(
- exampleTokenAddress,
- sender,
- recipient,
- transferAmount,
- TradeSide.Taker,
- TransferType.Trade,
- );
- const store = (exchangeTransferSimulator as any)._store;
- const senderBalance = await store.getBalanceAsync(exampleTokenAddress, sender);
- const recipientBalance = await store.getBalanceAsync(exampleTokenAddress, recipient);
- const senderProxyAllowance = await store.getProxyAllowanceAsync(exampleTokenAddress, sender);
- expect(senderBalance).to.be.bignumber.equal(0);
- expect(recipientBalance).to.be.bignumber.equal(transferAmount);
- expect(senderProxyAllowance).to.be.bignumber.equal(0);
- });
- it("doesn't update proxyAllowance after transfer if unlimited", async () => {
- txHash = await zeroEx.token.transferAsync(exampleTokenAddress, coinbase, sender, transferAmount);
- await zeroEx.awaitTransactionMinedAsync(txHash);
- txHash = await zeroEx.token.setUnlimitedProxyAllowanceAsync(exampleTokenAddress, sender);
- await zeroEx.awaitTransactionMinedAsync(txHash);
- await exchangeTransferSimulator.transferFromAsync(
- exampleTokenAddress,
- sender,
- recipient,
- transferAmount,
- TradeSide.Taker,
- TransferType.Trade,
- );
- const store = (exchangeTransferSimulator as any)._store;
- const senderBalance = await store.getBalanceAsync(exampleTokenAddress, sender);
- const recipientBalance = await store.getBalanceAsync(exampleTokenAddress, recipient);
- const senderProxyAllowance = await store.getProxyAllowanceAsync(exampleTokenAddress, sender);
- expect(senderBalance).to.be.bignumber.equal(0);
- expect(recipientBalance).to.be.bignumber.equal(transferAmount);
- expect(senderProxyAllowance).to.be.bignumber.equal(zeroEx.token.UNLIMITED_ALLOWANCE_IN_BASE_UNITS);
- });
- });
+ const web3 = web3Factory.create();
+ const config = {
+ networkId: constants.TESTRPC_NETWORK_ID,
+ };
+ const zeroEx = new ZeroEx(web3.currentProvider, config);
+ const transferAmount = new BigNumber(5);
+ let userAddresses: string[];
+ let tokens: Token[];
+ let coinbase: string;
+ let sender: string;
+ let recipient: string;
+ let exampleTokenAddress: string;
+ let exchangeTransferSimulator: ExchangeTransferSimulator;
+ let txHash: string;
+ before(async () => {
+ userAddresses = await zeroEx.getAvailableAddressesAsync();
+ [coinbase, sender, recipient] = userAddresses;
+ tokens = await zeroEx.tokenRegistry.getTokensAsync();
+ exampleTokenAddress = tokens[0].address;
+ });
+ beforeEach(async () => {
+ await blockchainLifecycle.startAsync();
+ });
+ afterEach(async () => {
+ await blockchainLifecycle.revertAsync();
+ });
+ describe('#transferFromAsync', () => {
+ beforeEach(() => {
+ exchangeTransferSimulator = new ExchangeTransferSimulator(zeroEx.token, BlockParamLiteral.Latest);
+ });
+ it("throws if the user doesn't have enough allowance", async () => {
+ return expect(
+ exchangeTransferSimulator.transferFromAsync(
+ exampleTokenAddress,
+ sender,
+ recipient,
+ transferAmount,
+ TradeSide.Taker,
+ TransferType.Trade,
+ ),
+ ).to.be.rejectedWith(ExchangeContractErrs.InsufficientTakerAllowance);
+ });
+ it("throws if the user doesn't have enough balance", async () => {
+ txHash = await zeroEx.token.setProxyAllowanceAsync(exampleTokenAddress, sender, transferAmount);
+ await zeroEx.awaitTransactionMinedAsync(txHash);
+ return expect(
+ exchangeTransferSimulator.transferFromAsync(
+ exampleTokenAddress,
+ sender,
+ recipient,
+ transferAmount,
+ TradeSide.Maker,
+ TransferType.Trade,
+ ),
+ ).to.be.rejectedWith(ExchangeContractErrs.InsufficientMakerBalance);
+ });
+ it('updates balances and proxyAllowance after transfer', async () => {
+ txHash = await zeroEx.token.transferAsync(exampleTokenAddress, coinbase, sender, transferAmount);
+ await zeroEx.awaitTransactionMinedAsync(txHash);
+ txHash = await zeroEx.token.setProxyAllowanceAsync(exampleTokenAddress, sender, transferAmount);
+ await zeroEx.awaitTransactionMinedAsync(txHash);
+ await exchangeTransferSimulator.transferFromAsync(
+ exampleTokenAddress,
+ sender,
+ recipient,
+ transferAmount,
+ TradeSide.Taker,
+ TransferType.Trade,
+ );
+ const store = (exchangeTransferSimulator as any)._store;
+ const senderBalance = await store.getBalanceAsync(exampleTokenAddress, sender);
+ const recipientBalance = await store.getBalanceAsync(exampleTokenAddress, recipient);
+ const senderProxyAllowance = await store.getProxyAllowanceAsync(exampleTokenAddress, sender);
+ expect(senderBalance).to.be.bignumber.equal(0);
+ expect(recipientBalance).to.be.bignumber.equal(transferAmount);
+ expect(senderProxyAllowance).to.be.bignumber.equal(0);
+ });
+ it("doesn't update proxyAllowance after transfer if unlimited", async () => {
+ txHash = await zeroEx.token.transferAsync(exampleTokenAddress, coinbase, sender, transferAmount);
+ await zeroEx.awaitTransactionMinedAsync(txHash);
+ txHash = await zeroEx.token.setUnlimitedProxyAllowanceAsync(exampleTokenAddress, sender);
+ await zeroEx.awaitTransactionMinedAsync(txHash);
+ await exchangeTransferSimulator.transferFromAsync(
+ exampleTokenAddress,
+ sender,
+ recipient,
+ transferAmount,
+ TradeSide.Taker,
+ TransferType.Trade,
+ );
+ const store = (exchangeTransferSimulator as any)._store;
+ const senderBalance = await store.getBalanceAsync(exampleTokenAddress, sender);
+ const recipientBalance = await store.getBalanceAsync(exampleTokenAddress, recipient);
+ const senderProxyAllowance = await store.getProxyAllowanceAsync(exampleTokenAddress, sender);
+ expect(senderBalance).to.be.bignumber.equal(0);
+ expect(recipientBalance).to.be.bignumber.equal(transferAmount);
+ expect(senderProxyAllowance).to.be.bignumber.equal(zeroEx.token.UNLIMITED_ALLOWANCE_IN_BASE_UNITS);
+ });
+ });
});
diff --git a/packages/0x.js/test/exchange_wrapper_test.ts b/packages/0x.js/test/exchange_wrapper_test.ts
index f67e61a55..7e0ffd818 100644
--- a/packages/0x.js/test/exchange_wrapper_test.ts
+++ b/packages/0x.js/test/exchange_wrapper_test.ts
@@ -6,17 +6,17 @@ import 'mocha';
import * as Web3 from 'web3';
import {
- BlockRange,
- DecodedLogEvent,
- ExchangeContractErrs,
- ExchangeEvents,
- LogCancelContractEventArgs,
- LogFillContractEventArgs,
- OrderCancellationRequest,
- OrderFillRequest,
- SignedOrder,
- Token,
- ZeroEx,
+ BlockRange,
+ DecodedLogEvent,
+ ExchangeContractErrs,
+ ExchangeEvents,
+ LogCancelContractEventArgs,
+ LogFillContractEventArgs,
+ OrderCancellationRequest,
+ OrderFillRequest,
+ SignedOrder,
+ Token,
+ ZeroEx,
} from '../src';
import { BlockParamLiteral, DoneCallback } from '../src/types';
@@ -34,1114 +34,1114 @@ const blockchainLifecycle = new BlockchainLifecycle(constants.RPC_URL);
const NON_EXISTENT_ORDER_HASH = '0x79370342234e7acd6bbeac335bd3bb1d368383294b64b8160a00f4060e4d3777';
describe('ExchangeWrapper', () => {
- let web3: Web3;
- let zeroEx: ZeroEx;
- let tokenUtils: TokenUtils;
- let tokens: Token[];
- let userAddresses: string[];
- let zrxTokenAddress: string;
- let fillScenarios: FillScenarios;
- let exchangeContractAddress: string;
- const config = {
- networkId: constants.TESTRPC_NETWORK_ID,
- };
- before(async () => {
- web3 = web3Factory.create();
- zeroEx = new ZeroEx(web3.currentProvider, config);
- exchangeContractAddress = zeroEx.exchange.getContractAddress();
- userAddresses = await zeroEx.getAvailableAddressesAsync();
- tokens = await zeroEx.tokenRegistry.getTokensAsync();
- tokenUtils = new TokenUtils(tokens);
- zrxTokenAddress = tokenUtils.getProtocolTokenOrThrow().address;
- fillScenarios = new FillScenarios(zeroEx, userAddresses, tokens, zrxTokenAddress, exchangeContractAddress);
- await fillScenarios.initTokenBalancesAsync();
- });
- beforeEach(async () => {
- await blockchainLifecycle.startAsync();
- });
- afterEach(async () => {
- await blockchainLifecycle.revertAsync();
- });
- describe('fillOrKill order(s)', () => {
- let makerTokenAddress: string;
- let takerTokenAddress: string;
- let coinbase: string;
- let makerAddress: string;
- let takerAddress: string;
- let feeRecipient: string;
- const takerTokenFillAmount = new BigNumber(5);
- before(async () => {
- [coinbase, makerAddress, takerAddress, feeRecipient] = userAddresses;
- tokens = await zeroEx.tokenRegistry.getTokensAsync();
- const [makerToken, takerToken] = tokenUtils.getDummyTokens();
- makerTokenAddress = makerToken.address;
- takerTokenAddress = takerToken.address;
- });
- describe('#batchFillOrKillAsync', () => {
- it('successfully batch fillOrKill', async () => {
- const fillableAmount = new BigNumber(5);
- const partialFillTakerAmount = new BigNumber(2);
- const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- fillableAmount,
- );
- const anotherSignedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- fillableAmount,
- );
- const orderFillRequests = [
- {
- signedOrder,
- takerTokenFillAmount: partialFillTakerAmount,
- },
- {
- signedOrder: anotherSignedOrder,
- takerTokenFillAmount: partialFillTakerAmount,
- },
- ];
- await zeroEx.exchange.batchFillOrKillAsync(orderFillRequests, takerAddress);
- });
- describe('order transaction options', () => {
- let signedOrder: SignedOrder;
- let orderFillRequests: OrderFillRequest[];
- const fillableAmount = new BigNumber(5);
- beforeEach(async () => {
- signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- fillableAmount,
- );
- orderFillRequests = [
- {
- signedOrder,
- takerTokenFillAmount: new BigNumber(0),
- },
- ];
- });
- it('should validate when orderTransactionOptions are not present', async () => {
- return expect(
- zeroEx.exchange.batchFillOrKillAsync(orderFillRequests, takerAddress),
- ).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero);
- });
- it('should validate when orderTransactionOptions specify to validate', async () => {
- return expect(
- zeroEx.exchange.batchFillOrKillAsync(orderFillRequests, takerAddress, {
- shouldValidate: true,
- }),
- ).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero);
- });
- it('should not validate when orderTransactionOptions specify not to validate', async () => {
- return expect(
- zeroEx.exchange.batchFillOrKillAsync(orderFillRequests, takerAddress, {
- shouldValidate: false,
- }),
- ).to.not.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero);
- });
- });
- });
- describe('#fillOrKillOrderAsync', () => {
- let signedOrder: SignedOrder;
- const fillableAmount = new BigNumber(5);
- beforeEach(async () => {
- signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- fillableAmount,
- );
- });
- describe('successful fills', () => {
- it('should fill a valid order', async () => {
- expect(await zeroEx.token.getBalanceAsync(makerTokenAddress, makerAddress)).to.be.bignumber.equal(
- fillableAmount,
- );
- expect(await zeroEx.token.getBalanceAsync(takerTokenAddress, makerAddress)).to.be.bignumber.equal(
- 0,
- );
- expect(await zeroEx.token.getBalanceAsync(makerTokenAddress, takerAddress)).to.be.bignumber.equal(
- 0,
- );
- expect(await zeroEx.token.getBalanceAsync(takerTokenAddress, takerAddress)).to.be.bignumber.equal(
- fillableAmount,
- );
- await zeroEx.exchange.fillOrKillOrderAsync(signedOrder, takerTokenFillAmount, takerAddress);
- expect(await zeroEx.token.getBalanceAsync(makerTokenAddress, makerAddress)).to.be.bignumber.equal(
- fillableAmount.minus(takerTokenFillAmount),
- );
- expect(await zeroEx.token.getBalanceAsync(takerTokenAddress, makerAddress)).to.be.bignumber.equal(
- takerTokenFillAmount,
- );
- expect(await zeroEx.token.getBalanceAsync(makerTokenAddress, takerAddress)).to.be.bignumber.equal(
- takerTokenFillAmount,
- );
- expect(await zeroEx.token.getBalanceAsync(takerTokenAddress, takerAddress)).to.be.bignumber.equal(
- fillableAmount.minus(takerTokenFillAmount),
- );
- });
- it('should partially fill a valid order', async () => {
- const partialFillAmount = new BigNumber(3);
- await zeroEx.exchange.fillOrKillOrderAsync(signedOrder, partialFillAmount, takerAddress);
- expect(await zeroEx.token.getBalanceAsync(makerTokenAddress, makerAddress)).to.be.bignumber.equal(
- fillableAmount.minus(partialFillAmount),
- );
- expect(await zeroEx.token.getBalanceAsync(takerTokenAddress, makerAddress)).to.be.bignumber.equal(
- partialFillAmount,
- );
- expect(await zeroEx.token.getBalanceAsync(makerTokenAddress, takerAddress)).to.be.bignumber.equal(
- partialFillAmount,
- );
- expect(await zeroEx.token.getBalanceAsync(takerTokenAddress, takerAddress)).to.be.bignumber.equal(
- fillableAmount.minus(partialFillAmount),
- );
- });
- });
- describe('order transaction options', () => {
- const emptyFillableAmount = new BigNumber(0);
- it('should validate when orderTransactionOptions are not present', async () => {
- return expect(
- zeroEx.exchange.fillOrKillOrderAsync(signedOrder, emptyFillableAmount, takerAddress),
- ).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero);
- });
- it('should validate when orderTransactionOptions specify to validate', async () => {
- return expect(
- zeroEx.exchange.fillOrKillOrderAsync(signedOrder, emptyFillableAmount, takerAddress, {
- shouldValidate: true,
- }),
- ).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero);
- });
- it('should not validate when orderTransactionOptions specify not to validate', async () => {
- return expect(
- zeroEx.exchange.fillOrKillOrderAsync(signedOrder, emptyFillableAmount, takerAddress, {
- shouldValidate: false,
- }),
- ).to.not.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero);
- });
- });
- });
- });
- describe('fill order(s)', () => {
- let makerTokenAddress: string;
- let takerTokenAddress: string;
- let coinbase: string;
- let makerAddress: string;
- let takerAddress: string;
- let feeRecipient: string;
- const fillableAmount = new BigNumber(5);
- const takerTokenFillAmount = new BigNumber(5);
- const shouldThrowOnInsufficientBalanceOrAllowance = true;
- before(async () => {
- [coinbase, makerAddress, takerAddress, feeRecipient] = userAddresses;
- tokens = await zeroEx.tokenRegistry.getTokensAsync();
- const [makerToken, takerToken] = tokenUtils.getDummyTokens();
- makerTokenAddress = makerToken.address;
- takerTokenAddress = takerToken.address;
- });
- describe('#fillOrderAsync', () => {
- describe('successful fills', () => {
- it('should fill a valid order', async () => {
- const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- fillableAmount,
- );
- expect(await zeroEx.token.getBalanceAsync(makerTokenAddress, makerAddress)).to.be.bignumber.equal(
- fillableAmount,
- );
- expect(await zeroEx.token.getBalanceAsync(takerTokenAddress, makerAddress)).to.be.bignumber.equal(
- 0,
- );
- expect(await zeroEx.token.getBalanceAsync(makerTokenAddress, takerAddress)).to.be.bignumber.equal(
- 0,
- );
- expect(await zeroEx.token.getBalanceAsync(takerTokenAddress, takerAddress)).to.be.bignumber.equal(
- fillableAmount,
- );
- const txHash = await zeroEx.exchange.fillOrderAsync(
- signedOrder,
- takerTokenFillAmount,
- shouldThrowOnInsufficientBalanceOrAllowance,
- takerAddress,
- );
- await zeroEx.awaitTransactionMinedAsync(txHash);
- expect(await zeroEx.token.getBalanceAsync(makerTokenAddress, makerAddress)).to.be.bignumber.equal(
- fillableAmount.minus(takerTokenFillAmount),
- );
- expect(await zeroEx.token.getBalanceAsync(takerTokenAddress, makerAddress)).to.be.bignumber.equal(
- takerTokenFillAmount,
- );
- expect(await zeroEx.token.getBalanceAsync(makerTokenAddress, takerAddress)).to.be.bignumber.equal(
- takerTokenFillAmount,
- );
- expect(await zeroEx.token.getBalanceAsync(takerTokenAddress, takerAddress)).to.be.bignumber.equal(
- fillableAmount.minus(takerTokenFillAmount),
- );
- });
- it('should partially fill the valid order', async () => {
- const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- fillableAmount,
- );
- const partialFillAmount = new BigNumber(3);
- const txHash = await zeroEx.exchange.fillOrderAsync(
- signedOrder,
- partialFillAmount,
- shouldThrowOnInsufficientBalanceOrAllowance,
- takerAddress,
- );
- await zeroEx.awaitTransactionMinedAsync(txHash);
- expect(await zeroEx.token.getBalanceAsync(makerTokenAddress, makerAddress)).to.be.bignumber.equal(
- fillableAmount.minus(partialFillAmount),
- );
- expect(await zeroEx.token.getBalanceAsync(takerTokenAddress, makerAddress)).to.be.bignumber.equal(
- partialFillAmount,
- );
- expect(await zeroEx.token.getBalanceAsync(makerTokenAddress, takerAddress)).to.be.bignumber.equal(
- partialFillAmount,
- );
- expect(await zeroEx.token.getBalanceAsync(takerTokenAddress, takerAddress)).to.be.bignumber.equal(
- fillableAmount.minus(partialFillAmount),
- );
- });
- it('should fill the valid orders with fees', async () => {
- const makerFee = new BigNumber(1);
- const takerFee = new BigNumber(2);
- const signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerFee,
- takerFee,
- makerAddress,
- takerAddress,
- fillableAmount,
- feeRecipient,
- );
- const txHash = await zeroEx.exchange.fillOrderAsync(
- signedOrder,
- takerTokenFillAmount,
- shouldThrowOnInsufficientBalanceOrAllowance,
- takerAddress,
- );
- await zeroEx.awaitTransactionMinedAsync(txHash);
- expect(await zeroEx.token.getBalanceAsync(zrxTokenAddress, feeRecipient)).to.be.bignumber.equal(
- makerFee.plus(takerFee),
- );
- });
- });
- describe('order transaction options', () => {
- let signedOrder: SignedOrder;
- const emptyFillTakerAmount = new BigNumber(0);
- beforeEach(async () => {
- signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- fillableAmount,
- );
- });
- it('should validate when orderTransactionOptions are not present', async () => {
- return expect(
- zeroEx.exchange.fillOrderAsync(
- signedOrder,
- emptyFillTakerAmount,
- shouldThrowOnInsufficientBalanceOrAllowance,
- takerAddress,
- ),
- ).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero);
- });
- it('should validate when orderTransactionOptions specify to validate', async () => {
- return expect(
- zeroEx.exchange.fillOrderAsync(
- signedOrder,
- emptyFillTakerAmount,
- shouldThrowOnInsufficientBalanceOrAllowance,
- takerAddress,
- {
- shouldValidate: true,
- },
- ),
- ).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero);
- });
- it('should not validate when orderTransactionOptions specify not to validate', async () => {
- return expect(
- zeroEx.exchange.fillOrderAsync(
- signedOrder,
- emptyFillTakerAmount,
- shouldThrowOnInsufficientBalanceOrAllowance,
- takerAddress,
- {
- shouldValidate: false,
- },
- ),
- ).to.not.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero);
- });
- });
- describe('negative fill amount', async () => {
- let signedOrder: SignedOrder;
- const negativeFillTakerAmount = new BigNumber(-100);
- beforeEach(async () => {
- signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- fillableAmount,
- );
- });
- it('should not allow the exchange wrapper to fill if amount is negative', async () => {
- return expect(
- zeroEx.exchange.fillOrderAsync(
- signedOrder,
- negativeFillTakerAmount,
- shouldThrowOnInsufficientBalanceOrAllowance,
- takerAddress,
- ),
- ).to.be.rejected();
- });
- });
- });
- describe('#batchFillOrdersAsync', () => {
- let signedOrder: SignedOrder;
- let signedOrderHashHex: string;
- let anotherSignedOrder: SignedOrder;
- let anotherOrderHashHex: string;
- let orderFillBatch: OrderFillRequest[];
- beforeEach(async () => {
- signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- fillableAmount,
- );
- signedOrderHashHex = ZeroEx.getOrderHashHex(signedOrder);
- anotherSignedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- fillableAmount,
- );
- anotherOrderHashHex = ZeroEx.getOrderHashHex(anotherSignedOrder);
- });
- describe('successful batch fills', () => {
- beforeEach(() => {
- orderFillBatch = [
- {
- signedOrder,
- takerTokenFillAmount,
- },
- {
- signedOrder: anotherSignedOrder,
- takerTokenFillAmount,
- },
- ];
- });
- it('should throw if a batch is empty', async () => {
- return expect(
- zeroEx.exchange.batchFillOrdersAsync(
- [],
- shouldThrowOnInsufficientBalanceOrAllowance,
- takerAddress,
- ),
- ).to.be.rejectedWith(ExchangeContractErrs.BatchOrdersMustHaveAtLeastOneItem);
- });
- it('should successfully fill multiple orders', async () => {
- const txHash = await zeroEx.exchange.batchFillOrdersAsync(
- orderFillBatch,
- shouldThrowOnInsufficientBalanceOrAllowance,
- takerAddress,
- );
- await zeroEx.awaitTransactionMinedAsync(txHash);
- const filledAmount = await zeroEx.exchange.getFilledTakerAmountAsync(signedOrderHashHex);
- const anotherFilledAmount = await zeroEx.exchange.getFilledTakerAmountAsync(anotherOrderHashHex);
- expect(filledAmount).to.be.bignumber.equal(takerTokenFillAmount);
- expect(anotherFilledAmount).to.be.bignumber.equal(takerTokenFillAmount);
- });
- });
- describe('order transaction options', () => {
- beforeEach(async () => {
- const emptyFillTakerAmount = new BigNumber(0);
- orderFillBatch = [
- {
- signedOrder,
- takerTokenFillAmount: emptyFillTakerAmount,
- },
- {
- signedOrder: anotherSignedOrder,
- takerTokenFillAmount: emptyFillTakerAmount,
- },
- ];
- });
- it('should validate when orderTransactionOptions are not present', async () => {
- return expect(
- zeroEx.exchange.batchFillOrdersAsync(
- orderFillBatch,
- shouldThrowOnInsufficientBalanceOrAllowance,
- takerAddress,
- ),
- ).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero);
- });
- it('should validate when orderTransactionOptions specify to validate', async () => {
- return expect(
- zeroEx.exchange.batchFillOrdersAsync(
- orderFillBatch,
- shouldThrowOnInsufficientBalanceOrAllowance,
- takerAddress,
- {
- shouldValidate: true,
- },
- ),
- ).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero);
- });
- it('should not validate when orderTransactionOptions specify not to validate', async () => {
- return expect(
- zeroEx.exchange.batchFillOrdersAsync(
- orderFillBatch,
- shouldThrowOnInsufficientBalanceOrAllowance,
- takerAddress,
- {
- shouldValidate: false,
- },
- ),
- ).to.not.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero);
- });
- });
- describe('negative batch fill amount', async () => {
- beforeEach(async () => {
- const negativeFillTakerAmount = new BigNumber(-100);
- orderFillBatch = [
- {
- signedOrder,
- takerTokenFillAmount,
- },
- {
- signedOrder: anotherSignedOrder,
- takerTokenFillAmount: negativeFillTakerAmount,
- },
- ];
- });
- it('should not allow the exchange wrapper to batch fill if any amount is negative', async () => {
- return expect(
- zeroEx.exchange.batchFillOrdersAsync(
- orderFillBatch,
- shouldThrowOnInsufficientBalanceOrAllowance,
- takerAddress,
- ),
- ).to.be.rejected();
- });
- });
- });
- describe('#fillOrdersUpTo', () => {
- let signedOrder: SignedOrder;
- let signedOrderHashHex: string;
- let anotherSignedOrder: SignedOrder;
- let anotherOrderHashHex: string;
- let signedOrders: SignedOrder[];
- const fillUpToAmount = fillableAmount.plus(fillableAmount).minus(1);
- beforeEach(async () => {
- signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- fillableAmount,
- );
- signedOrderHashHex = ZeroEx.getOrderHashHex(signedOrder);
- anotherSignedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- fillableAmount,
- );
- anotherOrderHashHex = ZeroEx.getOrderHashHex(anotherSignedOrder);
- signedOrders = [signedOrder, anotherSignedOrder];
- });
- describe('successful batch fills', () => {
- it('should throw if a batch is empty', async () => {
- return expect(
- zeroEx.exchange.fillOrdersUpToAsync(
- [],
- fillUpToAmount,
- shouldThrowOnInsufficientBalanceOrAllowance,
- takerAddress,
- ),
- ).to.be.rejectedWith(ExchangeContractErrs.BatchOrdersMustHaveAtLeastOneItem);
- });
- it('should successfully fill up to specified amount when all orders are fully funded', async () => {
- const txHash = await zeroEx.exchange.fillOrdersUpToAsync(
- signedOrders,
- fillUpToAmount,
- shouldThrowOnInsufficientBalanceOrAllowance,
- takerAddress,
- );
- await zeroEx.awaitTransactionMinedAsync(txHash);
- const filledAmount = await zeroEx.exchange.getFilledTakerAmountAsync(signedOrderHashHex);
- const anotherFilledAmount = await zeroEx.exchange.getFilledTakerAmountAsync(anotherOrderHashHex);
- expect(filledAmount).to.be.bignumber.equal(fillableAmount);
- const remainingFillAmount = fillableAmount.minus(1);
- expect(anotherFilledAmount).to.be.bignumber.equal(remainingFillAmount);
- });
- it('should successfully fill up to specified amount even if filling all orders would fail', async () => {
- const missingBalance = new BigNumber(1); // User will still have enough balance to fill up to 9,
- // but won't have 10 to fully fill all orders in a batch.
- await zeroEx.token.transferAsync(makerTokenAddress, makerAddress, coinbase, missingBalance);
- const txHash = await zeroEx.exchange.fillOrdersUpToAsync(
- signedOrders,
- fillUpToAmount,
- shouldThrowOnInsufficientBalanceOrAllowance,
- takerAddress,
- );
- await zeroEx.awaitTransactionMinedAsync(txHash);
- const filledAmount = await zeroEx.exchange.getFilledTakerAmountAsync(signedOrderHashHex);
- const anotherFilledAmount = await zeroEx.exchange.getFilledTakerAmountAsync(anotherOrderHashHex);
- expect(filledAmount).to.be.bignumber.equal(fillableAmount);
- const remainingFillAmount = fillableAmount.minus(1);
- expect(anotherFilledAmount).to.be.bignumber.equal(remainingFillAmount);
- });
- });
- describe('failed batch fills', () => {
- it("should fail validation if user doesn't have enough balance without fill up to", async () => {
- const missingBalance = new BigNumber(2); // User will only have enough balance to fill up to 8
- await zeroEx.token.transferAsync(makerTokenAddress, makerAddress, coinbase, missingBalance);
- return expect(
- zeroEx.exchange.fillOrdersUpToAsync(
- signedOrders,
- fillUpToAmount,
- shouldThrowOnInsufficientBalanceOrAllowance,
- takerAddress,
- ),
- ).to.be.rejectedWith(ExchangeContractErrs.InsufficientMakerBalance);
- });
- });
- describe('order transaction options', () => {
- const emptyFillUpToAmount = new BigNumber(0);
- it('should validate when orderTransactionOptions are not present', async () => {
- return expect(
- zeroEx.exchange.fillOrdersUpToAsync(
- signedOrders,
- emptyFillUpToAmount,
- shouldThrowOnInsufficientBalanceOrAllowance,
- takerAddress,
- ),
- ).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero);
- });
- it('should validate when orderTransactionOptions specify to validate', async () => {
- return expect(
- zeroEx.exchange.fillOrdersUpToAsync(
- signedOrders,
- emptyFillUpToAmount,
- shouldThrowOnInsufficientBalanceOrAllowance,
- takerAddress,
- {
- shouldValidate: true,
- },
- ),
- ).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero);
- });
- it('should not validate when orderTransactionOptions specify not to validate', async () => {
- return expect(
- zeroEx.exchange.fillOrdersUpToAsync(
- signedOrders,
- emptyFillUpToAmount,
- shouldThrowOnInsufficientBalanceOrAllowance,
- takerAddress,
- {
- shouldValidate: false,
- },
- ),
- ).to.not.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero);
- });
- });
- });
- });
- describe('cancel order(s)', () => {
- let makerTokenAddress: string;
- let takerTokenAddress: string;
- let coinbase: string;
- let makerAddress: string;
- let takerAddress: string;
- const fillableAmount = new BigNumber(5);
- let signedOrder: SignedOrder;
- let orderHashHex: string;
- const cancelAmount = new BigNumber(3);
- beforeEach(async () => {
- [coinbase, makerAddress, takerAddress] = userAddresses;
- const [makerToken, takerToken] = tokenUtils.getDummyTokens();
- makerTokenAddress = makerToken.address;
- takerTokenAddress = takerToken.address;
- signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- fillableAmount,
- );
- orderHashHex = ZeroEx.getOrderHashHex(signedOrder);
- });
- describe('#cancelOrderAsync', () => {
- describe('successful cancels', () => {
- it('should cancel an order', async () => {
- const txHash = await zeroEx.exchange.cancelOrderAsync(signedOrder, cancelAmount);
- await zeroEx.awaitTransactionMinedAsync(txHash);
- const cancelledAmount = await zeroEx.exchange.getCancelledTakerAmountAsync(orderHashHex);
- expect(cancelledAmount).to.be.bignumber.equal(cancelAmount);
- });
- });
- describe('order transaction options', () => {
- const emptyCancelTakerTokenAmount = new BigNumber(0);
- it('should validate when orderTransactionOptions are not present', async () => {
- return expect(
- zeroEx.exchange.cancelOrderAsync(signedOrder, emptyCancelTakerTokenAmount),
- ).to.be.rejectedWith(ExchangeContractErrs.OrderCancelAmountZero);
- });
- it('should validate when orderTransactionOptions specify to validate', async () => {
- return expect(
- zeroEx.exchange.cancelOrderAsync(signedOrder, emptyCancelTakerTokenAmount, {
- shouldValidate: true,
- }),
- ).to.be.rejectedWith(ExchangeContractErrs.OrderCancelAmountZero);
- });
- it('should not validate when orderTransactionOptions specify not to validate', async () => {
- return expect(
- zeroEx.exchange.cancelOrderAsync(signedOrder, emptyCancelTakerTokenAmount, {
- shouldValidate: false,
- }),
- ).to.not.be.rejectedWith(ExchangeContractErrs.OrderCancelAmountZero);
- });
- });
- });
- describe('#batchCancelOrdersAsync', () => {
- let anotherSignedOrder: SignedOrder;
- let anotherOrderHashHex: string;
- let cancelBatch: OrderCancellationRequest[];
- beforeEach(async () => {
- anotherSignedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- fillableAmount,
- );
- anotherOrderHashHex = ZeroEx.getOrderHashHex(anotherSignedOrder);
- cancelBatch = [
- {
- order: signedOrder,
- takerTokenCancelAmount: cancelAmount,
- },
- {
- order: anotherSignedOrder,
- takerTokenCancelAmount: cancelAmount,
- },
- ];
- });
- describe('failed batch cancels', () => {
- it('should throw when orders have different makers', async () => {
- const signedOrderWithDifferentMaker = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- takerAddress,
- takerAddress,
- fillableAmount,
- );
- return expect(
- zeroEx.exchange.batchCancelOrdersAsync([
- cancelBatch[0],
- {
- order: signedOrderWithDifferentMaker,
- takerTokenCancelAmount: cancelAmount,
- },
- ]),
- ).to.be.rejectedWith(ExchangeContractErrs.MultipleMakersInSingleCancelBatchDisallowed);
- });
- });
- describe('successful batch cancels', () => {
- it('should cancel a batch of orders', async () => {
- await zeroEx.exchange.batchCancelOrdersAsync(cancelBatch);
- const cancelledAmount = await zeroEx.exchange.getCancelledTakerAmountAsync(orderHashHex);
- const anotherCancelledAmount = await zeroEx.exchange.getCancelledTakerAmountAsync(
- anotherOrderHashHex,
- );
- expect(cancelledAmount).to.be.bignumber.equal(cancelAmount);
- expect(anotherCancelledAmount).to.be.bignumber.equal(cancelAmount);
- });
- });
- describe('order transaction options', () => {
- beforeEach(async () => {
- const emptyTakerTokenCancelAmount = new BigNumber(0);
- cancelBatch = [
- {
- order: signedOrder,
- takerTokenCancelAmount: emptyTakerTokenCancelAmount,
- },
- {
- order: anotherSignedOrder,
- takerTokenCancelAmount: emptyTakerTokenCancelAmount,
- },
- ];
- });
- it('should validate when orderTransactionOptions are not present', async () => {
- return expect(zeroEx.exchange.batchCancelOrdersAsync(cancelBatch)).to.be.rejectedWith(
- ExchangeContractErrs.OrderCancelAmountZero,
- );
- });
- it('should validate when orderTransactionOptions specify to validate', async () => {
- return expect(
- zeroEx.exchange.batchCancelOrdersAsync(cancelBatch, {
- shouldValidate: true,
- }),
- ).to.be.rejectedWith(ExchangeContractErrs.OrderCancelAmountZero);
- });
- it('should not validate when orderTransactionOptions specify not to validate', async () => {
- return expect(
- zeroEx.exchange.batchCancelOrdersAsync(cancelBatch, {
- shouldValidate: false,
- }),
- ).to.not.be.rejectedWith(ExchangeContractErrs.OrderCancelAmountZero);
- });
- });
- });
- });
- describe('tests that require partially filled order', () => {
- let makerTokenAddress: string;
- let takerTokenAddress: string;
- let takerAddress: string;
- let fillableAmount: BigNumber;
- let partialFillAmount: BigNumber;
- let signedOrder: SignedOrder;
- let orderHash: string;
- before(() => {
- takerAddress = userAddresses[1];
- tokenUtils = new TokenUtils(tokens);
- const [makerToken, takerToken] = tokenUtils.getDummyTokens();
- makerTokenAddress = makerToken.address;
- takerTokenAddress = takerToken.address;
- });
- beforeEach(async () => {
- fillableAmount = new BigNumber(5);
- partialFillAmount = new BigNumber(2);
- signedOrder = await fillScenarios.createPartiallyFilledSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- takerAddress,
- fillableAmount,
- partialFillAmount,
- );
- orderHash = ZeroEx.getOrderHashHex(signedOrder);
- });
- describe('#getUnavailableTakerAmountAsync', () => {
- it('should throw if passed an invalid orderHash', async () => {
- const invalidOrderHashHex = '0x123';
- return expect(zeroEx.exchange.getUnavailableTakerAmountAsync(invalidOrderHashHex)).to.be.rejected();
- });
- it('should return zero if passed a valid but non-existent orderHash', async () => {
- const unavailableValueT = await zeroEx.exchange.getUnavailableTakerAmountAsync(NON_EXISTENT_ORDER_HASH);
- expect(unavailableValueT).to.be.bignumber.equal(0);
- });
- it('should return the unavailableValueT for a valid and partially filled orderHash', async () => {
- const unavailableValueT = await zeroEx.exchange.getUnavailableTakerAmountAsync(orderHash);
- expect(unavailableValueT).to.be.bignumber.equal(partialFillAmount);
- });
- });
- describe('#getFilledTakerAmountAsync', () => {
- it('should throw if passed an invalid orderHash', async () => {
- const invalidOrderHashHex = '0x123';
- return expect(zeroEx.exchange.getFilledTakerAmountAsync(invalidOrderHashHex)).to.be.rejected();
- });
- it('should return zero if passed a valid but non-existent orderHash', async () => {
- const filledValueT = await zeroEx.exchange.getFilledTakerAmountAsync(NON_EXISTENT_ORDER_HASH);
- expect(filledValueT).to.be.bignumber.equal(0);
- });
- it('should return the filledValueT for a valid and partially filled orderHash', async () => {
- const filledValueT = await zeroEx.exchange.getFilledTakerAmountAsync(orderHash);
- expect(filledValueT).to.be.bignumber.equal(partialFillAmount);
- });
- });
- describe('#getCancelledTakerAmountAsync', () => {
- it('should throw if passed an invalid orderHash', async () => {
- const invalidOrderHashHex = '0x123';
- return expect(zeroEx.exchange.getCancelledTakerAmountAsync(invalidOrderHashHex)).to.be.rejected();
- });
- it('should return zero if passed a valid but non-existent orderHash', async () => {
- const cancelledValueT = await zeroEx.exchange.getCancelledTakerAmountAsync(NON_EXISTENT_ORDER_HASH);
- expect(cancelledValueT).to.be.bignumber.equal(0);
- });
- it('should return the cancelledValueT for a valid and partially filled orderHash', async () => {
- const cancelledValueT = await zeroEx.exchange.getCancelledTakerAmountAsync(orderHash);
- expect(cancelledValueT).to.be.bignumber.equal(0);
- });
- it('should return the cancelledValueT for a valid and cancelled orderHash', async () => {
- const cancelAmount = fillableAmount.minus(partialFillAmount);
- await zeroEx.exchange.cancelOrderAsync(signedOrder, cancelAmount);
- const cancelledValueT = await zeroEx.exchange.getCancelledTakerAmountAsync(orderHash);
- expect(cancelledValueT).to.be.bignumber.equal(cancelAmount);
- });
- });
- });
- describe('#subscribe', () => {
- const indexFilterValues = {};
- const shouldThrowOnInsufficientBalanceOrAllowance = true;
- let makerTokenAddress: string;
- let takerTokenAddress: string;
- let coinbase: string;
- let takerAddress: string;
- let makerAddress: string;
- let fillableAmount: BigNumber;
- let signedOrder: SignedOrder;
- const takerTokenFillAmountInBaseUnits = new BigNumber(1);
- const cancelTakerAmountInBaseUnits = new BigNumber(1);
- before(() => {
- [coinbase, makerAddress, takerAddress] = userAddresses;
- const [makerToken, takerToken] = tokenUtils.getDummyTokens();
- makerTokenAddress = makerToken.address;
- takerTokenAddress = takerToken.address;
- });
- beforeEach(async () => {
- fillableAmount = new BigNumber(5);
- signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- fillableAmount,
- );
- });
- afterEach(async () => {
- zeroEx.exchange.unsubscribeAll();
- });
- // Hack: Mocha does not allow a test to be both async and have a `done` callback
- // Since we need to await the receipt of the event in the `subscribe` callback,
- // we do need both. A hack is to make the top-level a sync fn w/ a done callback and then
- // wrap the rest of the test in an async block
- // Source: https://github.com/mochajs/mocha/issues/2407
- it('Should receive the LogFill event when an order is filled', (done: DoneCallback) => {
- (async () => {
- const callback = reportNodeCallbackErrors(done)(
- (logEvent: DecodedLogEvent<LogFillContractEventArgs>) => {
- expect(logEvent.log.event).to.be.equal(ExchangeEvents.LogFill);
- },
- );
- zeroEx.exchange.subscribe(ExchangeEvents.LogFill, indexFilterValues, callback);
- await zeroEx.exchange.fillOrderAsync(
- signedOrder,
- takerTokenFillAmountInBaseUnits,
- shouldThrowOnInsufficientBalanceOrAllowance,
- takerAddress,
- );
- })().catch(done);
- });
- it('Should receive the LogCancel event when an order is cancelled', (done: DoneCallback) => {
- (async () => {
- const callback = reportNodeCallbackErrors(done)(
- (logEvent: DecodedLogEvent<LogCancelContractEventArgs>) => {
- expect(logEvent.log.event).to.be.equal(ExchangeEvents.LogCancel);
- },
- );
- zeroEx.exchange.subscribe(ExchangeEvents.LogCancel, indexFilterValues, callback);
- await zeroEx.exchange.cancelOrderAsync(signedOrder, cancelTakerAmountInBaseUnits);
- })().catch(done);
- });
- it('Outstanding subscriptions are cancelled when zeroEx.setProvider called', (done: DoneCallback) => {
- (async () => {
- const callbackNeverToBeCalled = reportNodeCallbackErrors(done)(
- (logEvent: DecodedLogEvent<LogFillContractEventArgs>) => {
- done(new Error('Expected this subscription to have been cancelled'));
- },
- );
- zeroEx.exchange.subscribe(ExchangeEvents.LogFill, indexFilterValues, callbackNeverToBeCalled);
+ let web3: Web3;
+ let zeroEx: ZeroEx;
+ let tokenUtils: TokenUtils;
+ let tokens: Token[];
+ let userAddresses: string[];
+ let zrxTokenAddress: string;
+ let fillScenarios: FillScenarios;
+ let exchangeContractAddress: string;
+ const config = {
+ networkId: constants.TESTRPC_NETWORK_ID,
+ };
+ before(async () => {
+ web3 = web3Factory.create();
+ zeroEx = new ZeroEx(web3.currentProvider, config);
+ exchangeContractAddress = zeroEx.exchange.getContractAddress();
+ userAddresses = await zeroEx.getAvailableAddressesAsync();
+ tokens = await zeroEx.tokenRegistry.getTokensAsync();
+ tokenUtils = new TokenUtils(tokens);
+ zrxTokenAddress = tokenUtils.getProtocolTokenOrThrow().address;
+ fillScenarios = new FillScenarios(zeroEx, userAddresses, tokens, zrxTokenAddress, exchangeContractAddress);
+ await fillScenarios.initTokenBalancesAsync();
+ });
+ beforeEach(async () => {
+ await blockchainLifecycle.startAsync();
+ });
+ afterEach(async () => {
+ await blockchainLifecycle.revertAsync();
+ });
+ describe('fillOrKill order(s)', () => {
+ let makerTokenAddress: string;
+ let takerTokenAddress: string;
+ let coinbase: string;
+ let makerAddress: string;
+ let takerAddress: string;
+ let feeRecipient: string;
+ const takerTokenFillAmount = new BigNumber(5);
+ before(async () => {
+ [coinbase, makerAddress, takerAddress, feeRecipient] = userAddresses;
+ tokens = await zeroEx.tokenRegistry.getTokensAsync();
+ const [makerToken, takerToken] = tokenUtils.getDummyTokens();
+ makerTokenAddress = makerToken.address;
+ takerTokenAddress = takerToken.address;
+ });
+ describe('#batchFillOrKillAsync', () => {
+ it('successfully batch fillOrKill', async () => {
+ const fillableAmount = new BigNumber(5);
+ const partialFillTakerAmount = new BigNumber(2);
+ const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
+ makerTokenAddress,
+ takerTokenAddress,
+ makerAddress,
+ takerAddress,
+ fillableAmount,
+ );
+ const anotherSignedOrder = await fillScenarios.createFillableSignedOrderAsync(
+ makerTokenAddress,
+ takerTokenAddress,
+ makerAddress,
+ takerAddress,
+ fillableAmount,
+ );
+ const orderFillRequests = [
+ {
+ signedOrder,
+ takerTokenFillAmount: partialFillTakerAmount,
+ },
+ {
+ signedOrder: anotherSignedOrder,
+ takerTokenFillAmount: partialFillTakerAmount,
+ },
+ ];
+ await zeroEx.exchange.batchFillOrKillAsync(orderFillRequests, takerAddress);
+ });
+ describe('order transaction options', () => {
+ let signedOrder: SignedOrder;
+ let orderFillRequests: OrderFillRequest[];
+ const fillableAmount = new BigNumber(5);
+ beforeEach(async () => {
+ signedOrder = await fillScenarios.createFillableSignedOrderAsync(
+ makerTokenAddress,
+ takerTokenAddress,
+ makerAddress,
+ takerAddress,
+ fillableAmount,
+ );
+ orderFillRequests = [
+ {
+ signedOrder,
+ takerTokenFillAmount: new BigNumber(0),
+ },
+ ];
+ });
+ it('should validate when orderTransactionOptions are not present', async () => {
+ return expect(
+ zeroEx.exchange.batchFillOrKillAsync(orderFillRequests, takerAddress),
+ ).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero);
+ });
+ it('should validate when orderTransactionOptions specify to validate', async () => {
+ return expect(
+ zeroEx.exchange.batchFillOrKillAsync(orderFillRequests, takerAddress, {
+ shouldValidate: true,
+ }),
+ ).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero);
+ });
+ it('should not validate when orderTransactionOptions specify not to validate', async () => {
+ return expect(
+ zeroEx.exchange.batchFillOrKillAsync(orderFillRequests, takerAddress, {
+ shouldValidate: false,
+ }),
+ ).to.not.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero);
+ });
+ });
+ });
+ describe('#fillOrKillOrderAsync', () => {
+ let signedOrder: SignedOrder;
+ const fillableAmount = new BigNumber(5);
+ beforeEach(async () => {
+ signedOrder = await fillScenarios.createFillableSignedOrderAsync(
+ makerTokenAddress,
+ takerTokenAddress,
+ makerAddress,
+ takerAddress,
+ fillableAmount,
+ );
+ });
+ describe('successful fills', () => {
+ it('should fill a valid order', async () => {
+ expect(await zeroEx.token.getBalanceAsync(makerTokenAddress, makerAddress)).to.be.bignumber.equal(
+ fillableAmount,
+ );
+ expect(await zeroEx.token.getBalanceAsync(takerTokenAddress, makerAddress)).to.be.bignumber.equal(
+ 0,
+ );
+ expect(await zeroEx.token.getBalanceAsync(makerTokenAddress, takerAddress)).to.be.bignumber.equal(
+ 0,
+ );
+ expect(await zeroEx.token.getBalanceAsync(takerTokenAddress, takerAddress)).to.be.bignumber.equal(
+ fillableAmount,
+ );
+ await zeroEx.exchange.fillOrKillOrderAsync(signedOrder, takerTokenFillAmount, takerAddress);
+ expect(await zeroEx.token.getBalanceAsync(makerTokenAddress, makerAddress)).to.be.bignumber.equal(
+ fillableAmount.minus(takerTokenFillAmount),
+ );
+ expect(await zeroEx.token.getBalanceAsync(takerTokenAddress, makerAddress)).to.be.bignumber.equal(
+ takerTokenFillAmount,
+ );
+ expect(await zeroEx.token.getBalanceAsync(makerTokenAddress, takerAddress)).to.be.bignumber.equal(
+ takerTokenFillAmount,
+ );
+ expect(await zeroEx.token.getBalanceAsync(takerTokenAddress, takerAddress)).to.be.bignumber.equal(
+ fillableAmount.minus(takerTokenFillAmount),
+ );
+ });
+ it('should partially fill a valid order', async () => {
+ const partialFillAmount = new BigNumber(3);
+ await zeroEx.exchange.fillOrKillOrderAsync(signedOrder, partialFillAmount, takerAddress);
+ expect(await zeroEx.token.getBalanceAsync(makerTokenAddress, makerAddress)).to.be.bignumber.equal(
+ fillableAmount.minus(partialFillAmount),
+ );
+ expect(await zeroEx.token.getBalanceAsync(takerTokenAddress, makerAddress)).to.be.bignumber.equal(
+ partialFillAmount,
+ );
+ expect(await zeroEx.token.getBalanceAsync(makerTokenAddress, takerAddress)).to.be.bignumber.equal(
+ partialFillAmount,
+ );
+ expect(await zeroEx.token.getBalanceAsync(takerTokenAddress, takerAddress)).to.be.bignumber.equal(
+ fillableAmount.minus(partialFillAmount),
+ );
+ });
+ });
+ describe('order transaction options', () => {
+ const emptyFillableAmount = new BigNumber(0);
+ it('should validate when orderTransactionOptions are not present', async () => {
+ return expect(
+ zeroEx.exchange.fillOrKillOrderAsync(signedOrder, emptyFillableAmount, takerAddress),
+ ).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero);
+ });
+ it('should validate when orderTransactionOptions specify to validate', async () => {
+ return expect(
+ zeroEx.exchange.fillOrKillOrderAsync(signedOrder, emptyFillableAmount, takerAddress, {
+ shouldValidate: true,
+ }),
+ ).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero);
+ });
+ it('should not validate when orderTransactionOptions specify not to validate', async () => {
+ return expect(
+ zeroEx.exchange.fillOrKillOrderAsync(signedOrder, emptyFillableAmount, takerAddress, {
+ shouldValidate: false,
+ }),
+ ).to.not.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero);
+ });
+ });
+ });
+ });
+ describe('fill order(s)', () => {
+ let makerTokenAddress: string;
+ let takerTokenAddress: string;
+ let coinbase: string;
+ let makerAddress: string;
+ let takerAddress: string;
+ let feeRecipient: string;
+ const fillableAmount = new BigNumber(5);
+ const takerTokenFillAmount = new BigNumber(5);
+ const shouldThrowOnInsufficientBalanceOrAllowance = true;
+ before(async () => {
+ [coinbase, makerAddress, takerAddress, feeRecipient] = userAddresses;
+ tokens = await zeroEx.tokenRegistry.getTokensAsync();
+ const [makerToken, takerToken] = tokenUtils.getDummyTokens();
+ makerTokenAddress = makerToken.address;
+ takerTokenAddress = takerToken.address;
+ });
+ describe('#fillOrderAsync', () => {
+ describe('successful fills', () => {
+ it('should fill a valid order', async () => {
+ const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
+ makerTokenAddress,
+ takerTokenAddress,
+ makerAddress,
+ takerAddress,
+ fillableAmount,
+ );
+ expect(await zeroEx.token.getBalanceAsync(makerTokenAddress, makerAddress)).to.be.bignumber.equal(
+ fillableAmount,
+ );
+ expect(await zeroEx.token.getBalanceAsync(takerTokenAddress, makerAddress)).to.be.bignumber.equal(
+ 0,
+ );
+ expect(await zeroEx.token.getBalanceAsync(makerTokenAddress, takerAddress)).to.be.bignumber.equal(
+ 0,
+ );
+ expect(await zeroEx.token.getBalanceAsync(takerTokenAddress, takerAddress)).to.be.bignumber.equal(
+ fillableAmount,
+ );
+ const txHash = await zeroEx.exchange.fillOrderAsync(
+ signedOrder,
+ takerTokenFillAmount,
+ shouldThrowOnInsufficientBalanceOrAllowance,
+ takerAddress,
+ );
+ await zeroEx.awaitTransactionMinedAsync(txHash);
+ expect(await zeroEx.token.getBalanceAsync(makerTokenAddress, makerAddress)).to.be.bignumber.equal(
+ fillableAmount.minus(takerTokenFillAmount),
+ );
+ expect(await zeroEx.token.getBalanceAsync(takerTokenAddress, makerAddress)).to.be.bignumber.equal(
+ takerTokenFillAmount,
+ );
+ expect(await zeroEx.token.getBalanceAsync(makerTokenAddress, takerAddress)).to.be.bignumber.equal(
+ takerTokenFillAmount,
+ );
+ expect(await zeroEx.token.getBalanceAsync(takerTokenAddress, takerAddress)).to.be.bignumber.equal(
+ fillableAmount.minus(takerTokenFillAmount),
+ );
+ });
+ it('should partially fill the valid order', async () => {
+ const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
+ makerTokenAddress,
+ takerTokenAddress,
+ makerAddress,
+ takerAddress,
+ fillableAmount,
+ );
+ const partialFillAmount = new BigNumber(3);
+ const txHash = await zeroEx.exchange.fillOrderAsync(
+ signedOrder,
+ partialFillAmount,
+ shouldThrowOnInsufficientBalanceOrAllowance,
+ takerAddress,
+ );
+ await zeroEx.awaitTransactionMinedAsync(txHash);
+ expect(await zeroEx.token.getBalanceAsync(makerTokenAddress, makerAddress)).to.be.bignumber.equal(
+ fillableAmount.minus(partialFillAmount),
+ );
+ expect(await zeroEx.token.getBalanceAsync(takerTokenAddress, makerAddress)).to.be.bignumber.equal(
+ partialFillAmount,
+ );
+ expect(await zeroEx.token.getBalanceAsync(makerTokenAddress, takerAddress)).to.be.bignumber.equal(
+ partialFillAmount,
+ );
+ expect(await zeroEx.token.getBalanceAsync(takerTokenAddress, takerAddress)).to.be.bignumber.equal(
+ fillableAmount.minus(partialFillAmount),
+ );
+ });
+ it('should fill the valid orders with fees', async () => {
+ const makerFee = new BigNumber(1);
+ const takerFee = new BigNumber(2);
+ const signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync(
+ makerTokenAddress,
+ takerTokenAddress,
+ makerFee,
+ takerFee,
+ makerAddress,
+ takerAddress,
+ fillableAmount,
+ feeRecipient,
+ );
+ const txHash = await zeroEx.exchange.fillOrderAsync(
+ signedOrder,
+ takerTokenFillAmount,
+ shouldThrowOnInsufficientBalanceOrAllowance,
+ takerAddress,
+ );
+ await zeroEx.awaitTransactionMinedAsync(txHash);
+ expect(await zeroEx.token.getBalanceAsync(zrxTokenAddress, feeRecipient)).to.be.bignumber.equal(
+ makerFee.plus(takerFee),
+ );
+ });
+ });
+ describe('order transaction options', () => {
+ let signedOrder: SignedOrder;
+ const emptyFillTakerAmount = new BigNumber(0);
+ beforeEach(async () => {
+ signedOrder = await fillScenarios.createFillableSignedOrderAsync(
+ makerTokenAddress,
+ takerTokenAddress,
+ makerAddress,
+ takerAddress,
+ fillableAmount,
+ );
+ });
+ it('should validate when orderTransactionOptions are not present', async () => {
+ return expect(
+ zeroEx.exchange.fillOrderAsync(
+ signedOrder,
+ emptyFillTakerAmount,
+ shouldThrowOnInsufficientBalanceOrAllowance,
+ takerAddress,
+ ),
+ ).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero);
+ });
+ it('should validate when orderTransactionOptions specify to validate', async () => {
+ return expect(
+ zeroEx.exchange.fillOrderAsync(
+ signedOrder,
+ emptyFillTakerAmount,
+ shouldThrowOnInsufficientBalanceOrAllowance,
+ takerAddress,
+ {
+ shouldValidate: true,
+ },
+ ),
+ ).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero);
+ });
+ it('should not validate when orderTransactionOptions specify not to validate', async () => {
+ return expect(
+ zeroEx.exchange.fillOrderAsync(
+ signedOrder,
+ emptyFillTakerAmount,
+ shouldThrowOnInsufficientBalanceOrAllowance,
+ takerAddress,
+ {
+ shouldValidate: false,
+ },
+ ),
+ ).to.not.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero);
+ });
+ });
+ describe('negative fill amount', async () => {
+ let signedOrder: SignedOrder;
+ const negativeFillTakerAmount = new BigNumber(-100);
+ beforeEach(async () => {
+ signedOrder = await fillScenarios.createFillableSignedOrderAsync(
+ makerTokenAddress,
+ takerTokenAddress,
+ makerAddress,
+ takerAddress,
+ fillableAmount,
+ );
+ });
+ it('should not allow the exchange wrapper to fill if amount is negative', async () => {
+ return expect(
+ zeroEx.exchange.fillOrderAsync(
+ signedOrder,
+ negativeFillTakerAmount,
+ shouldThrowOnInsufficientBalanceOrAllowance,
+ takerAddress,
+ ),
+ ).to.be.rejected();
+ });
+ });
+ });
+ describe('#batchFillOrdersAsync', () => {
+ let signedOrder: SignedOrder;
+ let signedOrderHashHex: string;
+ let anotherSignedOrder: SignedOrder;
+ let anotherOrderHashHex: string;
+ let orderFillBatch: OrderFillRequest[];
+ beforeEach(async () => {
+ signedOrder = await fillScenarios.createFillableSignedOrderAsync(
+ makerTokenAddress,
+ takerTokenAddress,
+ makerAddress,
+ takerAddress,
+ fillableAmount,
+ );
+ signedOrderHashHex = ZeroEx.getOrderHashHex(signedOrder);
+ anotherSignedOrder = await fillScenarios.createFillableSignedOrderAsync(
+ makerTokenAddress,
+ takerTokenAddress,
+ makerAddress,
+ takerAddress,
+ fillableAmount,
+ );
+ anotherOrderHashHex = ZeroEx.getOrderHashHex(anotherSignedOrder);
+ });
+ describe('successful batch fills', () => {
+ beforeEach(() => {
+ orderFillBatch = [
+ {
+ signedOrder,
+ takerTokenFillAmount,
+ },
+ {
+ signedOrder: anotherSignedOrder,
+ takerTokenFillAmount,
+ },
+ ];
+ });
+ it('should throw if a batch is empty', async () => {
+ return expect(
+ zeroEx.exchange.batchFillOrdersAsync(
+ [],
+ shouldThrowOnInsufficientBalanceOrAllowance,
+ takerAddress,
+ ),
+ ).to.be.rejectedWith(ExchangeContractErrs.BatchOrdersMustHaveAtLeastOneItem);
+ });
+ it('should successfully fill multiple orders', async () => {
+ const txHash = await zeroEx.exchange.batchFillOrdersAsync(
+ orderFillBatch,
+ shouldThrowOnInsufficientBalanceOrAllowance,
+ takerAddress,
+ );
+ await zeroEx.awaitTransactionMinedAsync(txHash);
+ const filledAmount = await zeroEx.exchange.getFilledTakerAmountAsync(signedOrderHashHex);
+ const anotherFilledAmount = await zeroEx.exchange.getFilledTakerAmountAsync(anotherOrderHashHex);
+ expect(filledAmount).to.be.bignumber.equal(takerTokenFillAmount);
+ expect(anotherFilledAmount).to.be.bignumber.equal(takerTokenFillAmount);
+ });
+ });
+ describe('order transaction options', () => {
+ beforeEach(async () => {
+ const emptyFillTakerAmount = new BigNumber(0);
+ orderFillBatch = [
+ {
+ signedOrder,
+ takerTokenFillAmount: emptyFillTakerAmount,
+ },
+ {
+ signedOrder: anotherSignedOrder,
+ takerTokenFillAmount: emptyFillTakerAmount,
+ },
+ ];
+ });
+ it('should validate when orderTransactionOptions are not present', async () => {
+ return expect(
+ zeroEx.exchange.batchFillOrdersAsync(
+ orderFillBatch,
+ shouldThrowOnInsufficientBalanceOrAllowance,
+ takerAddress,
+ ),
+ ).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero);
+ });
+ it('should validate when orderTransactionOptions specify to validate', async () => {
+ return expect(
+ zeroEx.exchange.batchFillOrdersAsync(
+ orderFillBatch,
+ shouldThrowOnInsufficientBalanceOrAllowance,
+ takerAddress,
+ {
+ shouldValidate: true,
+ },
+ ),
+ ).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero);
+ });
+ it('should not validate when orderTransactionOptions specify not to validate', async () => {
+ return expect(
+ zeroEx.exchange.batchFillOrdersAsync(
+ orderFillBatch,
+ shouldThrowOnInsufficientBalanceOrAllowance,
+ takerAddress,
+ {
+ shouldValidate: false,
+ },
+ ),
+ ).to.not.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero);
+ });
+ });
+ describe('negative batch fill amount', async () => {
+ beforeEach(async () => {
+ const negativeFillTakerAmount = new BigNumber(-100);
+ orderFillBatch = [
+ {
+ signedOrder,
+ takerTokenFillAmount,
+ },
+ {
+ signedOrder: anotherSignedOrder,
+ takerTokenFillAmount: negativeFillTakerAmount,
+ },
+ ];
+ });
+ it('should not allow the exchange wrapper to batch fill if any amount is negative', async () => {
+ return expect(
+ zeroEx.exchange.batchFillOrdersAsync(
+ orderFillBatch,
+ shouldThrowOnInsufficientBalanceOrAllowance,
+ takerAddress,
+ ),
+ ).to.be.rejected();
+ });
+ });
+ });
+ describe('#fillOrdersUpTo', () => {
+ let signedOrder: SignedOrder;
+ let signedOrderHashHex: string;
+ let anotherSignedOrder: SignedOrder;
+ let anotherOrderHashHex: string;
+ let signedOrders: SignedOrder[];
+ const fillUpToAmount = fillableAmount.plus(fillableAmount).minus(1);
+ beforeEach(async () => {
+ signedOrder = await fillScenarios.createFillableSignedOrderAsync(
+ makerTokenAddress,
+ takerTokenAddress,
+ makerAddress,
+ takerAddress,
+ fillableAmount,
+ );
+ signedOrderHashHex = ZeroEx.getOrderHashHex(signedOrder);
+ anotherSignedOrder = await fillScenarios.createFillableSignedOrderAsync(
+ makerTokenAddress,
+ takerTokenAddress,
+ makerAddress,
+ takerAddress,
+ fillableAmount,
+ );
+ anotherOrderHashHex = ZeroEx.getOrderHashHex(anotherSignedOrder);
+ signedOrders = [signedOrder, anotherSignedOrder];
+ });
+ describe('successful batch fills', () => {
+ it('should throw if a batch is empty', async () => {
+ return expect(
+ zeroEx.exchange.fillOrdersUpToAsync(
+ [],
+ fillUpToAmount,
+ shouldThrowOnInsufficientBalanceOrAllowance,
+ takerAddress,
+ ),
+ ).to.be.rejectedWith(ExchangeContractErrs.BatchOrdersMustHaveAtLeastOneItem);
+ });
+ it('should successfully fill up to specified amount when all orders are fully funded', async () => {
+ const txHash = await zeroEx.exchange.fillOrdersUpToAsync(
+ signedOrders,
+ fillUpToAmount,
+ shouldThrowOnInsufficientBalanceOrAllowance,
+ takerAddress,
+ );
+ await zeroEx.awaitTransactionMinedAsync(txHash);
+ const filledAmount = await zeroEx.exchange.getFilledTakerAmountAsync(signedOrderHashHex);
+ const anotherFilledAmount = await zeroEx.exchange.getFilledTakerAmountAsync(anotherOrderHashHex);
+ expect(filledAmount).to.be.bignumber.equal(fillableAmount);
+ const remainingFillAmount = fillableAmount.minus(1);
+ expect(anotherFilledAmount).to.be.bignumber.equal(remainingFillAmount);
+ });
+ it('should successfully fill up to specified amount even if filling all orders would fail', async () => {
+ const missingBalance = new BigNumber(1); // User will still have enough balance to fill up to 9,
+ // but won't have 10 to fully fill all orders in a batch.
+ await zeroEx.token.transferAsync(makerTokenAddress, makerAddress, coinbase, missingBalance);
+ const txHash = await zeroEx.exchange.fillOrdersUpToAsync(
+ signedOrders,
+ fillUpToAmount,
+ shouldThrowOnInsufficientBalanceOrAllowance,
+ takerAddress,
+ );
+ await zeroEx.awaitTransactionMinedAsync(txHash);
+ const filledAmount = await zeroEx.exchange.getFilledTakerAmountAsync(signedOrderHashHex);
+ const anotherFilledAmount = await zeroEx.exchange.getFilledTakerAmountAsync(anotherOrderHashHex);
+ expect(filledAmount).to.be.bignumber.equal(fillableAmount);
+ const remainingFillAmount = fillableAmount.minus(1);
+ expect(anotherFilledAmount).to.be.bignumber.equal(remainingFillAmount);
+ });
+ });
+ describe('failed batch fills', () => {
+ it("should fail validation if user doesn't have enough balance without fill up to", async () => {
+ const missingBalance = new BigNumber(2); // User will only have enough balance to fill up to 8
+ await zeroEx.token.transferAsync(makerTokenAddress, makerAddress, coinbase, missingBalance);
+ return expect(
+ zeroEx.exchange.fillOrdersUpToAsync(
+ signedOrders,
+ fillUpToAmount,
+ shouldThrowOnInsufficientBalanceOrAllowance,
+ takerAddress,
+ ),
+ ).to.be.rejectedWith(ExchangeContractErrs.InsufficientMakerBalance);
+ });
+ });
+ describe('order transaction options', () => {
+ const emptyFillUpToAmount = new BigNumber(0);
+ it('should validate when orderTransactionOptions are not present', async () => {
+ return expect(
+ zeroEx.exchange.fillOrdersUpToAsync(
+ signedOrders,
+ emptyFillUpToAmount,
+ shouldThrowOnInsufficientBalanceOrAllowance,
+ takerAddress,
+ ),
+ ).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero);
+ });
+ it('should validate when orderTransactionOptions specify to validate', async () => {
+ return expect(
+ zeroEx.exchange.fillOrdersUpToAsync(
+ signedOrders,
+ emptyFillUpToAmount,
+ shouldThrowOnInsufficientBalanceOrAllowance,
+ takerAddress,
+ {
+ shouldValidate: true,
+ },
+ ),
+ ).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero);
+ });
+ it('should not validate when orderTransactionOptions specify not to validate', async () => {
+ return expect(
+ zeroEx.exchange.fillOrdersUpToAsync(
+ signedOrders,
+ emptyFillUpToAmount,
+ shouldThrowOnInsufficientBalanceOrAllowance,
+ takerAddress,
+ {
+ shouldValidate: false,
+ },
+ ),
+ ).to.not.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero);
+ });
+ });
+ });
+ });
+ describe('cancel order(s)', () => {
+ let makerTokenAddress: string;
+ let takerTokenAddress: string;
+ let coinbase: string;
+ let makerAddress: string;
+ let takerAddress: string;
+ const fillableAmount = new BigNumber(5);
+ let signedOrder: SignedOrder;
+ let orderHashHex: string;
+ const cancelAmount = new BigNumber(3);
+ beforeEach(async () => {
+ [coinbase, makerAddress, takerAddress] = userAddresses;
+ const [makerToken, takerToken] = tokenUtils.getDummyTokens();
+ makerTokenAddress = makerToken.address;
+ takerTokenAddress = takerToken.address;
+ signedOrder = await fillScenarios.createFillableSignedOrderAsync(
+ makerTokenAddress,
+ takerTokenAddress,
+ makerAddress,
+ takerAddress,
+ fillableAmount,
+ );
+ orderHashHex = ZeroEx.getOrderHashHex(signedOrder);
+ });
+ describe('#cancelOrderAsync', () => {
+ describe('successful cancels', () => {
+ it('should cancel an order', async () => {
+ const txHash = await zeroEx.exchange.cancelOrderAsync(signedOrder, cancelAmount);
+ await zeroEx.awaitTransactionMinedAsync(txHash);
+ const cancelledAmount = await zeroEx.exchange.getCancelledTakerAmountAsync(orderHashHex);
+ expect(cancelledAmount).to.be.bignumber.equal(cancelAmount);
+ });
+ });
+ describe('order transaction options', () => {
+ const emptyCancelTakerTokenAmount = new BigNumber(0);
+ it('should validate when orderTransactionOptions are not present', async () => {
+ return expect(
+ zeroEx.exchange.cancelOrderAsync(signedOrder, emptyCancelTakerTokenAmount),
+ ).to.be.rejectedWith(ExchangeContractErrs.OrderCancelAmountZero);
+ });
+ it('should validate when orderTransactionOptions specify to validate', async () => {
+ return expect(
+ zeroEx.exchange.cancelOrderAsync(signedOrder, emptyCancelTakerTokenAmount, {
+ shouldValidate: true,
+ }),
+ ).to.be.rejectedWith(ExchangeContractErrs.OrderCancelAmountZero);
+ });
+ it('should not validate when orderTransactionOptions specify not to validate', async () => {
+ return expect(
+ zeroEx.exchange.cancelOrderAsync(signedOrder, emptyCancelTakerTokenAmount, {
+ shouldValidate: false,
+ }),
+ ).to.not.be.rejectedWith(ExchangeContractErrs.OrderCancelAmountZero);
+ });
+ });
+ });
+ describe('#batchCancelOrdersAsync', () => {
+ let anotherSignedOrder: SignedOrder;
+ let anotherOrderHashHex: string;
+ let cancelBatch: OrderCancellationRequest[];
+ beforeEach(async () => {
+ anotherSignedOrder = await fillScenarios.createFillableSignedOrderAsync(
+ makerTokenAddress,
+ takerTokenAddress,
+ makerAddress,
+ takerAddress,
+ fillableAmount,
+ );
+ anotherOrderHashHex = ZeroEx.getOrderHashHex(anotherSignedOrder);
+ cancelBatch = [
+ {
+ order: signedOrder,
+ takerTokenCancelAmount: cancelAmount,
+ },
+ {
+ order: anotherSignedOrder,
+ takerTokenCancelAmount: cancelAmount,
+ },
+ ];
+ });
+ describe('failed batch cancels', () => {
+ it('should throw when orders have different makers', async () => {
+ const signedOrderWithDifferentMaker = await fillScenarios.createFillableSignedOrderAsync(
+ makerTokenAddress,
+ takerTokenAddress,
+ takerAddress,
+ takerAddress,
+ fillableAmount,
+ );
+ return expect(
+ zeroEx.exchange.batchCancelOrdersAsync([
+ cancelBatch[0],
+ {
+ order: signedOrderWithDifferentMaker,
+ takerTokenCancelAmount: cancelAmount,
+ },
+ ]),
+ ).to.be.rejectedWith(ExchangeContractErrs.MultipleMakersInSingleCancelBatchDisallowed);
+ });
+ });
+ describe('successful batch cancels', () => {
+ it('should cancel a batch of orders', async () => {
+ await zeroEx.exchange.batchCancelOrdersAsync(cancelBatch);
+ const cancelledAmount = await zeroEx.exchange.getCancelledTakerAmountAsync(orderHashHex);
+ const anotherCancelledAmount = await zeroEx.exchange.getCancelledTakerAmountAsync(
+ anotherOrderHashHex,
+ );
+ expect(cancelledAmount).to.be.bignumber.equal(cancelAmount);
+ expect(anotherCancelledAmount).to.be.bignumber.equal(cancelAmount);
+ });
+ });
+ describe('order transaction options', () => {
+ beforeEach(async () => {
+ const emptyTakerTokenCancelAmount = new BigNumber(0);
+ cancelBatch = [
+ {
+ order: signedOrder,
+ takerTokenCancelAmount: emptyTakerTokenCancelAmount,
+ },
+ {
+ order: anotherSignedOrder,
+ takerTokenCancelAmount: emptyTakerTokenCancelAmount,
+ },
+ ];
+ });
+ it('should validate when orderTransactionOptions are not present', async () => {
+ return expect(zeroEx.exchange.batchCancelOrdersAsync(cancelBatch)).to.be.rejectedWith(
+ ExchangeContractErrs.OrderCancelAmountZero,
+ );
+ });
+ it('should validate when orderTransactionOptions specify to validate', async () => {
+ return expect(
+ zeroEx.exchange.batchCancelOrdersAsync(cancelBatch, {
+ shouldValidate: true,
+ }),
+ ).to.be.rejectedWith(ExchangeContractErrs.OrderCancelAmountZero);
+ });
+ it('should not validate when orderTransactionOptions specify not to validate', async () => {
+ return expect(
+ zeroEx.exchange.batchCancelOrdersAsync(cancelBatch, {
+ shouldValidate: false,
+ }),
+ ).to.not.be.rejectedWith(ExchangeContractErrs.OrderCancelAmountZero);
+ });
+ });
+ });
+ });
+ describe('tests that require partially filled order', () => {
+ let makerTokenAddress: string;
+ let takerTokenAddress: string;
+ let takerAddress: string;
+ let fillableAmount: BigNumber;
+ let partialFillAmount: BigNumber;
+ let signedOrder: SignedOrder;
+ let orderHash: string;
+ before(() => {
+ takerAddress = userAddresses[1];
+ tokenUtils = new TokenUtils(tokens);
+ const [makerToken, takerToken] = tokenUtils.getDummyTokens();
+ makerTokenAddress = makerToken.address;
+ takerTokenAddress = takerToken.address;
+ });
+ beforeEach(async () => {
+ fillableAmount = new BigNumber(5);
+ partialFillAmount = new BigNumber(2);
+ signedOrder = await fillScenarios.createPartiallyFilledSignedOrderAsync(
+ makerTokenAddress,
+ takerTokenAddress,
+ takerAddress,
+ fillableAmount,
+ partialFillAmount,
+ );
+ orderHash = ZeroEx.getOrderHashHex(signedOrder);
+ });
+ describe('#getUnavailableTakerAmountAsync', () => {
+ it('should throw if passed an invalid orderHash', async () => {
+ const invalidOrderHashHex = '0x123';
+ return expect(zeroEx.exchange.getUnavailableTakerAmountAsync(invalidOrderHashHex)).to.be.rejected();
+ });
+ it('should return zero if passed a valid but non-existent orderHash', async () => {
+ const unavailableValueT = await zeroEx.exchange.getUnavailableTakerAmountAsync(NON_EXISTENT_ORDER_HASH);
+ expect(unavailableValueT).to.be.bignumber.equal(0);
+ });
+ it('should return the unavailableValueT for a valid and partially filled orderHash', async () => {
+ const unavailableValueT = await zeroEx.exchange.getUnavailableTakerAmountAsync(orderHash);
+ expect(unavailableValueT).to.be.bignumber.equal(partialFillAmount);
+ });
+ });
+ describe('#getFilledTakerAmountAsync', () => {
+ it('should throw if passed an invalid orderHash', async () => {
+ const invalidOrderHashHex = '0x123';
+ return expect(zeroEx.exchange.getFilledTakerAmountAsync(invalidOrderHashHex)).to.be.rejected();
+ });
+ it('should return zero if passed a valid but non-existent orderHash', async () => {
+ const filledValueT = await zeroEx.exchange.getFilledTakerAmountAsync(NON_EXISTENT_ORDER_HASH);
+ expect(filledValueT).to.be.bignumber.equal(0);
+ });
+ it('should return the filledValueT for a valid and partially filled orderHash', async () => {
+ const filledValueT = await zeroEx.exchange.getFilledTakerAmountAsync(orderHash);
+ expect(filledValueT).to.be.bignumber.equal(partialFillAmount);
+ });
+ });
+ describe('#getCancelledTakerAmountAsync', () => {
+ it('should throw if passed an invalid orderHash', async () => {
+ const invalidOrderHashHex = '0x123';
+ return expect(zeroEx.exchange.getCancelledTakerAmountAsync(invalidOrderHashHex)).to.be.rejected();
+ });
+ it('should return zero if passed a valid but non-existent orderHash', async () => {
+ const cancelledValueT = await zeroEx.exchange.getCancelledTakerAmountAsync(NON_EXISTENT_ORDER_HASH);
+ expect(cancelledValueT).to.be.bignumber.equal(0);
+ });
+ it('should return the cancelledValueT for a valid and partially filled orderHash', async () => {
+ const cancelledValueT = await zeroEx.exchange.getCancelledTakerAmountAsync(orderHash);
+ expect(cancelledValueT).to.be.bignumber.equal(0);
+ });
+ it('should return the cancelledValueT for a valid and cancelled orderHash', async () => {
+ const cancelAmount = fillableAmount.minus(partialFillAmount);
+ await zeroEx.exchange.cancelOrderAsync(signedOrder, cancelAmount);
+ const cancelledValueT = await zeroEx.exchange.getCancelledTakerAmountAsync(orderHash);
+ expect(cancelledValueT).to.be.bignumber.equal(cancelAmount);
+ });
+ });
+ });
+ describe('#subscribe', () => {
+ const indexFilterValues = {};
+ const shouldThrowOnInsufficientBalanceOrAllowance = true;
+ let makerTokenAddress: string;
+ let takerTokenAddress: string;
+ let coinbase: string;
+ let takerAddress: string;
+ let makerAddress: string;
+ let fillableAmount: BigNumber;
+ let signedOrder: SignedOrder;
+ const takerTokenFillAmountInBaseUnits = new BigNumber(1);
+ const cancelTakerAmountInBaseUnits = new BigNumber(1);
+ before(() => {
+ [coinbase, makerAddress, takerAddress] = userAddresses;
+ const [makerToken, takerToken] = tokenUtils.getDummyTokens();
+ makerTokenAddress = makerToken.address;
+ takerTokenAddress = takerToken.address;
+ });
+ beforeEach(async () => {
+ fillableAmount = new BigNumber(5);
+ signedOrder = await fillScenarios.createFillableSignedOrderAsync(
+ makerTokenAddress,
+ takerTokenAddress,
+ makerAddress,
+ takerAddress,
+ fillableAmount,
+ );
+ });
+ afterEach(async () => {
+ zeroEx.exchange.unsubscribeAll();
+ });
+ // Hack: Mocha does not allow a test to be both async and have a `done` callback
+ // Since we need to await the receipt of the event in the `subscribe` callback,
+ // we do need both. A hack is to make the top-level a sync fn w/ a done callback and then
+ // wrap the rest of the test in an async block
+ // Source: https://github.com/mochajs/mocha/issues/2407
+ it('Should receive the LogFill event when an order is filled', (done: DoneCallback) => {
+ (async () => {
+ const callback = reportNodeCallbackErrors(done)(
+ (logEvent: DecodedLogEvent<LogFillContractEventArgs>) => {
+ expect(logEvent.log.event).to.be.equal(ExchangeEvents.LogFill);
+ },
+ );
+ zeroEx.exchange.subscribe(ExchangeEvents.LogFill, indexFilterValues, callback);
+ await zeroEx.exchange.fillOrderAsync(
+ signedOrder,
+ takerTokenFillAmountInBaseUnits,
+ shouldThrowOnInsufficientBalanceOrAllowance,
+ takerAddress,
+ );
+ })().catch(done);
+ });
+ it('Should receive the LogCancel event when an order is cancelled', (done: DoneCallback) => {
+ (async () => {
+ const callback = reportNodeCallbackErrors(done)(
+ (logEvent: DecodedLogEvent<LogCancelContractEventArgs>) => {
+ expect(logEvent.log.event).to.be.equal(ExchangeEvents.LogCancel);
+ },
+ );
+ zeroEx.exchange.subscribe(ExchangeEvents.LogCancel, indexFilterValues, callback);
+ await zeroEx.exchange.cancelOrderAsync(signedOrder, cancelTakerAmountInBaseUnits);
+ })().catch(done);
+ });
+ it('Outstanding subscriptions are cancelled when zeroEx.setProvider called', (done: DoneCallback) => {
+ (async () => {
+ const callbackNeverToBeCalled = reportNodeCallbackErrors(done)(
+ (logEvent: DecodedLogEvent<LogFillContractEventArgs>) => {
+ done(new Error('Expected this subscription to have been cancelled'));
+ },
+ );
+ zeroEx.exchange.subscribe(ExchangeEvents.LogFill, indexFilterValues, callbackNeverToBeCalled);
- const newProvider = web3Factory.getRpcProvider();
- zeroEx.setProvider(newProvider, constants.TESTRPC_NETWORK_ID);
+ const newProvider = web3Factory.getRpcProvider();
+ zeroEx.setProvider(newProvider, constants.TESTRPC_NETWORK_ID);
- const callback = reportNodeCallbackErrors(done)(
- (logEvent: DecodedLogEvent<LogFillContractEventArgs>) => {
- expect(logEvent.log.event).to.be.equal(ExchangeEvents.LogFill);
- },
- );
- zeroEx.exchange.subscribe(ExchangeEvents.LogFill, indexFilterValues, callback);
- await zeroEx.exchange.fillOrderAsync(
- signedOrder,
- takerTokenFillAmountInBaseUnits,
- shouldThrowOnInsufficientBalanceOrAllowance,
- takerAddress,
- );
- })().catch(done);
- });
- it('Should cancel subscription when unsubscribe called', (done: DoneCallback) => {
- (async () => {
- const callbackNeverToBeCalled = reportNodeCallbackErrors(done)(
- (logEvent: DecodedLogEvent<LogFillContractEventArgs>) => {
- done(new Error('Expected this subscription to have been cancelled'));
- },
- );
- const subscriptionToken = zeroEx.exchange.subscribe(
- ExchangeEvents.LogFill,
- indexFilterValues,
- callbackNeverToBeCalled,
- );
- zeroEx.exchange.unsubscribe(subscriptionToken);
- await zeroEx.exchange.fillOrderAsync(
- signedOrder,
- takerTokenFillAmountInBaseUnits,
- shouldThrowOnInsufficientBalanceOrAllowance,
- takerAddress,
- );
- done();
- })().catch(done);
- });
- });
- describe('#getOrderHashHexUsingContractCallAsync', () => {
- let makerTokenAddress: string;
- let takerTokenAddress: string;
- let makerAddress: string;
- let takerAddress: string;
- const fillableAmount = new BigNumber(5);
- before(async () => {
- [, makerAddress, takerAddress] = userAddresses;
- const [makerToken, takerToken] = tokenUtils.getDummyTokens();
- makerTokenAddress = makerToken.address;
- takerTokenAddress = takerToken.address;
- });
- it("get's the same hash as the local function", async () => {
- const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- fillableAmount,
- );
- const orderHash = ZeroEx.getOrderHashHex(signedOrder);
- const orderHashFromContract = await (zeroEx.exchange as any)._getOrderHashHexUsingContractCallAsync(
- signedOrder,
- );
- expect(orderHash).to.equal(orderHashFromContract);
- });
- });
- describe('#getZRXTokenAddressAsync', () => {
- it('gets the same token as is in token registry', () => {
- const zrxAddress = zeroEx.exchange.getZRXTokenAddress();
- const zrxToken = tokenUtils.getProtocolTokenOrThrow();
- expect(zrxAddress).to.equal(zrxToken.address);
- });
- });
- describe('#getLogsAsync', () => {
- let makerTokenAddress: string;
- let takerTokenAddress: string;
- let makerAddress: string;
- let takerAddress: string;
- const fillableAmount = new BigNumber(5);
- const shouldThrowOnInsufficientBalanceOrAllowance = true;
- const blockRange: BlockRange = {
- fromBlock: 0,
- toBlock: BlockParamLiteral.Latest,
- };
- let txHash: string;
- before(async () => {
- [, makerAddress, takerAddress] = userAddresses;
- const [makerToken, takerToken] = tokenUtils.getDummyTokens();
- makerTokenAddress = makerToken.address;
- takerTokenAddress = takerToken.address;
- });
- it('should get logs with decoded args emitted by LogFill', async () => {
- const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- fillableAmount,
- );
- txHash = await zeroEx.exchange.fillOrderAsync(
- signedOrder,
- fillableAmount,
- shouldThrowOnInsufficientBalanceOrAllowance,
- takerAddress,
- );
- await zeroEx.awaitTransactionMinedAsync(txHash);
- const eventName = ExchangeEvents.LogFill;
- const indexFilterValues = {};
- const logs = await zeroEx.exchange.getLogsAsync(eventName, blockRange, indexFilterValues);
- expect(logs).to.have.length(1);
- expect(logs[0].event).to.be.equal(eventName);
- });
- it('should only get the logs with the correct event name', async () => {
- const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- fillableAmount,
- );
- txHash = await zeroEx.exchange.fillOrderAsync(
- signedOrder,
- fillableAmount,
- shouldThrowOnInsufficientBalanceOrAllowance,
- takerAddress,
- );
- await zeroEx.awaitTransactionMinedAsync(txHash);
- const differentEventName = ExchangeEvents.LogCancel;
- const indexFilterValues = {};
- const logs = await zeroEx.exchange.getLogsAsync(differentEventName, blockRange, indexFilterValues);
- expect(logs).to.have.length(0);
- });
- it('should only get the logs with the correct indexed fields', async () => {
- const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- fillableAmount,
- );
- txHash = await zeroEx.exchange.fillOrderAsync(
- signedOrder,
- fillableAmount,
- shouldThrowOnInsufficientBalanceOrAllowance,
- takerAddress,
- );
- await zeroEx.awaitTransactionMinedAsync(txHash);
+ const callback = reportNodeCallbackErrors(done)(
+ (logEvent: DecodedLogEvent<LogFillContractEventArgs>) => {
+ expect(logEvent.log.event).to.be.equal(ExchangeEvents.LogFill);
+ },
+ );
+ zeroEx.exchange.subscribe(ExchangeEvents.LogFill, indexFilterValues, callback);
+ await zeroEx.exchange.fillOrderAsync(
+ signedOrder,
+ takerTokenFillAmountInBaseUnits,
+ shouldThrowOnInsufficientBalanceOrAllowance,
+ takerAddress,
+ );
+ })().catch(done);
+ });
+ it('Should cancel subscription when unsubscribe called', (done: DoneCallback) => {
+ (async () => {
+ const callbackNeverToBeCalled = reportNodeCallbackErrors(done)(
+ (logEvent: DecodedLogEvent<LogFillContractEventArgs>) => {
+ done(new Error('Expected this subscription to have been cancelled'));
+ },
+ );
+ const subscriptionToken = zeroEx.exchange.subscribe(
+ ExchangeEvents.LogFill,
+ indexFilterValues,
+ callbackNeverToBeCalled,
+ );
+ zeroEx.exchange.unsubscribe(subscriptionToken);
+ await zeroEx.exchange.fillOrderAsync(
+ signedOrder,
+ takerTokenFillAmountInBaseUnits,
+ shouldThrowOnInsufficientBalanceOrAllowance,
+ takerAddress,
+ );
+ done();
+ })().catch(done);
+ });
+ });
+ describe('#getOrderHashHexUsingContractCallAsync', () => {
+ let makerTokenAddress: string;
+ let takerTokenAddress: string;
+ let makerAddress: string;
+ let takerAddress: string;
+ const fillableAmount = new BigNumber(5);
+ before(async () => {
+ [, makerAddress, takerAddress] = userAddresses;
+ const [makerToken, takerToken] = tokenUtils.getDummyTokens();
+ makerTokenAddress = makerToken.address;
+ takerTokenAddress = takerToken.address;
+ });
+ it("get's the same hash as the local function", async () => {
+ const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
+ makerTokenAddress,
+ takerTokenAddress,
+ makerAddress,
+ takerAddress,
+ fillableAmount,
+ );
+ const orderHash = ZeroEx.getOrderHashHex(signedOrder);
+ const orderHashFromContract = await (zeroEx.exchange as any)._getOrderHashHexUsingContractCallAsync(
+ signedOrder,
+ );
+ expect(orderHash).to.equal(orderHashFromContract);
+ });
+ });
+ describe('#getZRXTokenAddressAsync', () => {
+ it('gets the same token as is in token registry', () => {
+ const zrxAddress = zeroEx.exchange.getZRXTokenAddress();
+ const zrxToken = tokenUtils.getProtocolTokenOrThrow();
+ expect(zrxAddress).to.equal(zrxToken.address);
+ });
+ });
+ describe('#getLogsAsync', () => {
+ let makerTokenAddress: string;
+ let takerTokenAddress: string;
+ let makerAddress: string;
+ let takerAddress: string;
+ const fillableAmount = new BigNumber(5);
+ const shouldThrowOnInsufficientBalanceOrAllowance = true;
+ const blockRange: BlockRange = {
+ fromBlock: 0,
+ toBlock: BlockParamLiteral.Latest,
+ };
+ let txHash: string;
+ before(async () => {
+ [, makerAddress, takerAddress] = userAddresses;
+ const [makerToken, takerToken] = tokenUtils.getDummyTokens();
+ makerTokenAddress = makerToken.address;
+ takerTokenAddress = takerToken.address;
+ });
+ it('should get logs with decoded args emitted by LogFill', async () => {
+ const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
+ makerTokenAddress,
+ takerTokenAddress,
+ makerAddress,
+ takerAddress,
+ fillableAmount,
+ );
+ txHash = await zeroEx.exchange.fillOrderAsync(
+ signedOrder,
+ fillableAmount,
+ shouldThrowOnInsufficientBalanceOrAllowance,
+ takerAddress,
+ );
+ await zeroEx.awaitTransactionMinedAsync(txHash);
+ const eventName = ExchangeEvents.LogFill;
+ const indexFilterValues = {};
+ const logs = await zeroEx.exchange.getLogsAsync(eventName, blockRange, indexFilterValues);
+ expect(logs).to.have.length(1);
+ expect(logs[0].event).to.be.equal(eventName);
+ });
+ it('should only get the logs with the correct event name', async () => {
+ const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
+ makerTokenAddress,
+ takerTokenAddress,
+ makerAddress,
+ takerAddress,
+ fillableAmount,
+ );
+ txHash = await zeroEx.exchange.fillOrderAsync(
+ signedOrder,
+ fillableAmount,
+ shouldThrowOnInsufficientBalanceOrAllowance,
+ takerAddress,
+ );
+ await zeroEx.awaitTransactionMinedAsync(txHash);
+ const differentEventName = ExchangeEvents.LogCancel;
+ const indexFilterValues = {};
+ const logs = await zeroEx.exchange.getLogsAsync(differentEventName, blockRange, indexFilterValues);
+ expect(logs).to.have.length(0);
+ });
+ it('should only get the logs with the correct indexed fields', async () => {
+ const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
+ makerTokenAddress,
+ takerTokenAddress,
+ makerAddress,
+ takerAddress,
+ fillableAmount,
+ );
+ txHash = await zeroEx.exchange.fillOrderAsync(
+ signedOrder,
+ fillableAmount,
+ shouldThrowOnInsufficientBalanceOrAllowance,
+ takerAddress,
+ );
+ await zeroEx.awaitTransactionMinedAsync(txHash);
- const differentMakerAddress = userAddresses[2];
- const anotherSignedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- differentMakerAddress,
- takerAddress,
- fillableAmount,
- );
- txHash = await zeroEx.exchange.fillOrderAsync(
- anotherSignedOrder,
- fillableAmount,
- shouldThrowOnInsufficientBalanceOrAllowance,
- takerAddress,
- );
- await zeroEx.awaitTransactionMinedAsync(txHash);
+ const differentMakerAddress = userAddresses[2];
+ const anotherSignedOrder = await fillScenarios.createFillableSignedOrderAsync(
+ makerTokenAddress,
+ takerTokenAddress,
+ differentMakerAddress,
+ takerAddress,
+ fillableAmount,
+ );
+ txHash = await zeroEx.exchange.fillOrderAsync(
+ anotherSignedOrder,
+ fillableAmount,
+ shouldThrowOnInsufficientBalanceOrAllowance,
+ takerAddress,
+ );
+ await zeroEx.awaitTransactionMinedAsync(txHash);
- const eventName = ExchangeEvents.LogFill;
- const indexFilterValues = {
- maker: differentMakerAddress,
- };
- const logs = await zeroEx.exchange.getLogsAsync<LogFillContractEventArgs>(
- eventName,
- blockRange,
- indexFilterValues,
- );
- expect(logs).to.have.length(1);
- const args = logs[0].args;
- expect(args.maker).to.be.equal(differentMakerAddress);
- });
- });
+ const eventName = ExchangeEvents.LogFill;
+ const indexFilterValues = {
+ maker: differentMakerAddress,
+ };
+ const logs = await zeroEx.exchange.getLogsAsync<LogFillContractEventArgs>(
+ eventName,
+ blockRange,
+ indexFilterValues,
+ );
+ expect(logs).to.have.length(1);
+ const args = logs[0].args;
+ expect(args.maker).to.be.equal(differentMakerAddress);
+ });
+ });
}); // tslint:disable:max-file-line-count
diff --git a/packages/0x.js/test/expiration_watcher_test.ts b/packages/0x.js/test/expiration_watcher_test.ts
index a76d84da1..770615f88 100644
--- a/packages/0x.js/test/expiration_watcher_test.ts
+++ b/packages/0x.js/test/expiration_watcher_test.ts
@@ -24,137 +24,137 @@ const expect = chai.expect;
const blockchainLifecycle = new BlockchainLifecycle(testConstants.RPC_URL);
describe('ExpirationWatcher', () => {
- let web3: Web3;
- let zeroEx: ZeroEx;
- let tokenUtils: TokenUtils;
- let tokens: Token[];
- let userAddresses: string[];
- let zrxTokenAddress: string;
- let fillScenarios: FillScenarios;
- let exchangeContractAddress: string;
- let makerTokenAddress: string;
- let takerTokenAddress: string;
- let coinbase: string;
- let makerAddress: string;
- let takerAddress: string;
- let feeRecipient: string;
- const fillableAmount = new BigNumber(5);
- let currentUnixTimestampSec: BigNumber;
- let timer: Sinon.SinonFakeTimers;
- let expirationWatcher: ExpirationWatcher;
- before(async () => {
- web3 = web3Factory.create();
- const config = {
- networkId: constants.TESTRPC_NETWORK_ID,
- };
- zeroEx = new ZeroEx(web3.currentProvider, config);
- exchangeContractAddress = zeroEx.exchange.getContractAddress();
- userAddresses = await zeroEx.getAvailableAddressesAsync();
- tokens = await zeroEx.tokenRegistry.getTokensAsync();
- tokenUtils = new TokenUtils(tokens);
- zrxTokenAddress = tokenUtils.getProtocolTokenOrThrow().address;
- fillScenarios = new FillScenarios(zeroEx, userAddresses, tokens, zrxTokenAddress, exchangeContractAddress);
- [coinbase, makerAddress, takerAddress, feeRecipient] = userAddresses;
- tokens = await zeroEx.tokenRegistry.getTokensAsync();
- const [makerToken, takerToken] = tokenUtils.getDummyTokens();
- makerTokenAddress = makerToken.address;
- takerTokenAddress = takerToken.address;
- });
- beforeEach(async () => {
- await blockchainLifecycle.startAsync();
- const sinonTimerConfig = { shouldAdvanceTime: true } as any;
- // This constructor has incorrect types
- timer = Sinon.useFakeTimers(sinonTimerConfig);
- currentUnixTimestampSec = utils.getCurrentUnixTimestampSec();
- expirationWatcher = new ExpirationWatcher();
- });
- afterEach(async () => {
- await blockchainLifecycle.revertAsync();
- timer.restore();
- expirationWatcher.unsubscribe();
- });
- it('correctly emits events when order expires', (done: DoneCallback) => {
- (async () => {
- const orderLifetimeSec = 60;
- const expirationUnixTimestampSec = currentUnixTimestampSec.plus(orderLifetimeSec);
- const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- fillableAmount,
- expirationUnixTimestampSec,
- );
- const orderHash = ZeroEx.getOrderHashHex(signedOrder);
- expirationWatcher.addOrder(orderHash, signedOrder.expirationUnixTimestampSec.times(1000));
- const callbackAsync = reportNoErrorCallbackErrors(done)((hash: string) => {
- expect(hash).to.be.equal(orderHash);
- expect(utils.getCurrentUnixTimestampSec()).to.be.bignumber.gte(expirationUnixTimestampSec);
- });
- expirationWatcher.subscribe(callbackAsync);
- timer.tick(orderLifetimeSec * 1000);
- })().catch(done);
- });
- it("doesn't emit events before order expires", (done: DoneCallback) => {
- (async () => {
- const orderLifetimeSec = 60;
- const expirationUnixTimestampSec = currentUnixTimestampSec.plus(orderLifetimeSec);
- const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- fillableAmount,
- expirationUnixTimestampSec,
- );
- const orderHash = ZeroEx.getOrderHashHex(signedOrder);
- expirationWatcher.addOrder(orderHash, signedOrder.expirationUnixTimestampSec.times(1000));
- const callbackAsync = reportNoErrorCallbackErrors(done)(async (hash: string) => {
- done(new Error('Emitted expiration went before the order actually expired'));
- });
- expirationWatcher.subscribe(callbackAsync);
- const notEnoughTime = orderLifetimeSec - 1;
- timer.tick(notEnoughTime * 1000);
- done();
- })().catch(done);
- });
- it('emits events in correct order', (done: DoneCallback) => {
- (async () => {
- const order1Lifetime = 60;
- const order2Lifetime = 120;
- const order1ExpirationUnixTimestampSec = currentUnixTimestampSec.plus(order1Lifetime);
- const order2ExpirationUnixTimestampSec = currentUnixTimestampSec.plus(order2Lifetime);
- const signedOrder1 = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- fillableAmount,
- order1ExpirationUnixTimestampSec,
- );
- const signedOrder2 = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- fillableAmount,
- order2ExpirationUnixTimestampSec,
- );
- const orderHash1 = ZeroEx.getOrderHashHex(signedOrder1);
- const orderHash2 = ZeroEx.getOrderHashHex(signedOrder2);
- expirationWatcher.addOrder(orderHash2, signedOrder2.expirationUnixTimestampSec.times(1000));
- expirationWatcher.addOrder(orderHash1, signedOrder1.expirationUnixTimestampSec.times(1000));
- const expirationOrder = [orderHash1, orderHash2];
- const expectToBeCalledOnce = false;
- const callbackAsync = reportNoErrorCallbackErrors(done, expectToBeCalledOnce)((hash: string) => {
- const orderHash = expirationOrder.shift();
- expect(hash).to.be.equal(orderHash);
- if (_.isEmpty(expirationOrder)) {
- done();
- }
- });
- expirationWatcher.subscribe(callbackAsync);
- timer.tick(order2Lifetime * 1000);
- })().catch(done);
- });
+ let web3: Web3;
+ let zeroEx: ZeroEx;
+ let tokenUtils: TokenUtils;
+ let tokens: Token[];
+ let userAddresses: string[];
+ let zrxTokenAddress: string;
+ let fillScenarios: FillScenarios;
+ let exchangeContractAddress: string;
+ let makerTokenAddress: string;
+ let takerTokenAddress: string;
+ let coinbase: string;
+ let makerAddress: string;
+ let takerAddress: string;
+ let feeRecipient: string;
+ const fillableAmount = new BigNumber(5);
+ let currentUnixTimestampSec: BigNumber;
+ let timer: Sinon.SinonFakeTimers;
+ let expirationWatcher: ExpirationWatcher;
+ before(async () => {
+ web3 = web3Factory.create();
+ const config = {
+ networkId: constants.TESTRPC_NETWORK_ID,
+ };
+ zeroEx = new ZeroEx(web3.currentProvider, config);
+ exchangeContractAddress = zeroEx.exchange.getContractAddress();
+ userAddresses = await zeroEx.getAvailableAddressesAsync();
+ tokens = await zeroEx.tokenRegistry.getTokensAsync();
+ tokenUtils = new TokenUtils(tokens);
+ zrxTokenAddress = tokenUtils.getProtocolTokenOrThrow().address;
+ fillScenarios = new FillScenarios(zeroEx, userAddresses, tokens, zrxTokenAddress, exchangeContractAddress);
+ [coinbase, makerAddress, takerAddress, feeRecipient] = userAddresses;
+ tokens = await zeroEx.tokenRegistry.getTokensAsync();
+ const [makerToken, takerToken] = tokenUtils.getDummyTokens();
+ makerTokenAddress = makerToken.address;
+ takerTokenAddress = takerToken.address;
+ });
+ beforeEach(async () => {
+ await blockchainLifecycle.startAsync();
+ const sinonTimerConfig = { shouldAdvanceTime: true } as any;
+ // This constructor has incorrect types
+ timer = Sinon.useFakeTimers(sinonTimerConfig);
+ currentUnixTimestampSec = utils.getCurrentUnixTimestampSec();
+ expirationWatcher = new ExpirationWatcher();
+ });
+ afterEach(async () => {
+ await blockchainLifecycle.revertAsync();
+ timer.restore();
+ expirationWatcher.unsubscribe();
+ });
+ it('correctly emits events when order expires', (done: DoneCallback) => {
+ (async () => {
+ const orderLifetimeSec = 60;
+ const expirationUnixTimestampSec = currentUnixTimestampSec.plus(orderLifetimeSec);
+ const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
+ makerTokenAddress,
+ takerTokenAddress,
+ makerAddress,
+ takerAddress,
+ fillableAmount,
+ expirationUnixTimestampSec,
+ );
+ const orderHash = ZeroEx.getOrderHashHex(signedOrder);
+ expirationWatcher.addOrder(orderHash, signedOrder.expirationUnixTimestampSec.times(1000));
+ const callbackAsync = reportNoErrorCallbackErrors(done)((hash: string) => {
+ expect(hash).to.be.equal(orderHash);
+ expect(utils.getCurrentUnixTimestampSec()).to.be.bignumber.gte(expirationUnixTimestampSec);
+ });
+ expirationWatcher.subscribe(callbackAsync);
+ timer.tick(orderLifetimeSec * 1000);
+ })().catch(done);
+ });
+ it("doesn't emit events before order expires", (done: DoneCallback) => {
+ (async () => {
+ const orderLifetimeSec = 60;
+ const expirationUnixTimestampSec = currentUnixTimestampSec.plus(orderLifetimeSec);
+ const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
+ makerTokenAddress,
+ takerTokenAddress,
+ makerAddress,
+ takerAddress,
+ fillableAmount,
+ expirationUnixTimestampSec,
+ );
+ const orderHash = ZeroEx.getOrderHashHex(signedOrder);
+ expirationWatcher.addOrder(orderHash, signedOrder.expirationUnixTimestampSec.times(1000));
+ const callbackAsync = reportNoErrorCallbackErrors(done)(async (hash: string) => {
+ done(new Error('Emitted expiration went before the order actually expired'));
+ });
+ expirationWatcher.subscribe(callbackAsync);
+ const notEnoughTime = orderLifetimeSec - 1;
+ timer.tick(notEnoughTime * 1000);
+ done();
+ })().catch(done);
+ });
+ it('emits events in correct order', (done: DoneCallback) => {
+ (async () => {
+ const order1Lifetime = 60;
+ const order2Lifetime = 120;
+ const order1ExpirationUnixTimestampSec = currentUnixTimestampSec.plus(order1Lifetime);
+ const order2ExpirationUnixTimestampSec = currentUnixTimestampSec.plus(order2Lifetime);
+ const signedOrder1 = await fillScenarios.createFillableSignedOrderAsync(
+ makerTokenAddress,
+ takerTokenAddress,
+ makerAddress,
+ takerAddress,
+ fillableAmount,
+ order1ExpirationUnixTimestampSec,
+ );
+ const signedOrder2 = await fillScenarios.createFillableSignedOrderAsync(
+ makerTokenAddress,
+ takerTokenAddress,
+ makerAddress,
+ takerAddress,
+ fillableAmount,
+ order2ExpirationUnixTimestampSec,
+ );
+ const orderHash1 = ZeroEx.getOrderHashHex(signedOrder1);
+ const orderHash2 = ZeroEx.getOrderHashHex(signedOrder2);
+ expirationWatcher.addOrder(orderHash2, signedOrder2.expirationUnixTimestampSec.times(1000));
+ expirationWatcher.addOrder(orderHash1, signedOrder1.expirationUnixTimestampSec.times(1000));
+ const expirationOrder = [orderHash1, orderHash2];
+ const expectToBeCalledOnce = false;
+ const callbackAsync = reportNoErrorCallbackErrors(done, expectToBeCalledOnce)((hash: string) => {
+ const orderHash = expirationOrder.shift();
+ expect(hash).to.be.equal(orderHash);
+ if (_.isEmpty(expirationOrder)) {
+ done();
+ }
+ });
+ expirationWatcher.subscribe(callbackAsync);
+ timer.tick(order2Lifetime * 1000);
+ })().catch(done);
+ });
});
diff --git a/packages/0x.js/test/order_state_watcher_test.ts b/packages/0x.js/test/order_state_watcher_test.ts
index 22ed80446..2e9202fe2 100644
--- a/packages/0x.js/test/order_state_watcher_test.ts
+++ b/packages/0x.js/test/order_state_watcher_test.ts
@@ -6,14 +6,14 @@ import 'mocha';
import * as Web3 from 'web3';
import {
- ExchangeContractErrs,
- OrderState,
- OrderStateInvalid,
- OrderStateValid,
- SignedOrder,
- Token,
- ZeroEx,
- ZeroExError,
+ ExchangeContractErrs,
+ OrderState,
+ OrderStateInvalid,
+ OrderStateValid,
+ SignedOrder,
+ Token,
+ ZeroEx,
+ ZeroExError,
} from '../src';
import { DoneCallback } from '../src/types';
@@ -31,528 +31,528 @@ const expect = chai.expect;
const blockchainLifecycle = new BlockchainLifecycle(constants.RPC_URL);
describe('OrderStateWatcher', () => {
- let web3: Web3;
- let zeroEx: ZeroEx;
- let tokens: Token[];
- let tokenUtils: TokenUtils;
- let fillScenarios: FillScenarios;
- let userAddresses: string[];
- let zrxTokenAddress: string;
- let exchangeContractAddress: string;
- let makerToken: Token;
- let takerToken: Token;
- let maker: string;
- let taker: string;
- let signedOrder: SignedOrder;
- const config = {
- networkId: constants.TESTRPC_NETWORK_ID,
- };
- const decimals = constants.ZRX_DECIMALS;
- const fillableAmount = ZeroEx.toBaseUnitAmount(new BigNumber(5), decimals);
- before(async () => {
- web3 = web3Factory.create();
- zeroEx = new ZeroEx(web3.currentProvider, config);
- exchangeContractAddress = zeroEx.exchange.getContractAddress();
- userAddresses = await zeroEx.getAvailableAddressesAsync();
- [, maker, taker] = userAddresses;
- tokens = await zeroEx.tokenRegistry.getTokensAsync();
- tokenUtils = new TokenUtils(tokens);
- zrxTokenAddress = tokenUtils.getProtocolTokenOrThrow().address;
- fillScenarios = new FillScenarios(zeroEx, userAddresses, tokens, zrxTokenAddress, exchangeContractAddress);
- await fillScenarios.initTokenBalancesAsync();
- [makerToken, takerToken] = tokenUtils.getDummyTokens();
- });
- beforeEach(async () => {
- await blockchainLifecycle.startAsync();
- });
- afterEach(async () => {
- await blockchainLifecycle.revertAsync();
- });
- describe('#removeOrder', async () => {
- it('should successfully remove existing order', async () => {
- signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerToken.address,
- takerToken.address,
- maker,
- taker,
- fillableAmount,
- );
- const orderHash = ZeroEx.getOrderHashHex(signedOrder);
- zeroEx.orderStateWatcher.addOrder(signedOrder);
- expect((zeroEx.orderStateWatcher as any)._orderByOrderHash).to.include({
- [orderHash]: signedOrder,
- });
- let dependentOrderHashes = (zeroEx.orderStateWatcher as any)._dependentOrderHashes;
- expect(dependentOrderHashes[signedOrder.maker][signedOrder.makerTokenAddress]).to.have.keys(orderHash);
- zeroEx.orderStateWatcher.removeOrder(orderHash);
- expect((zeroEx.orderStateWatcher as any)._orderByOrderHash).to.not.include({
- [orderHash]: signedOrder,
- });
- dependentOrderHashes = (zeroEx.orderStateWatcher as any)._dependentOrderHashes;
- expect(dependentOrderHashes[signedOrder.maker]).to.be.undefined();
- });
- it('should no-op when removing a non-existing order', async () => {
- signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerToken.address,
- takerToken.address,
- maker,
- taker,
- fillableAmount,
- );
- const orderHash = ZeroEx.getOrderHashHex(signedOrder);
- const nonExistentOrderHash = `0x${orderHash
- .substr(2)
- .split('')
- .reverse()
- .join('')}`;
- zeroEx.orderStateWatcher.removeOrder(nonExistentOrderHash);
- });
- });
- describe('#subscribe', async () => {
- afterEach(async () => {
- zeroEx.orderStateWatcher.unsubscribe();
- });
- it('should fail when trying to subscribe twice', async () => {
- zeroEx.orderStateWatcher.subscribe(_.noop);
- expect(() => zeroEx.orderStateWatcher.subscribe(_.noop)).to.throw(ZeroExError.SubscriptionAlreadyPresent);
- });
- });
- describe('tests with cleanup', async () => {
- afterEach(async () => {
- zeroEx.orderStateWatcher.unsubscribe();
- const orderHash = ZeroEx.getOrderHashHex(signedOrder);
- zeroEx.orderStateWatcher.removeOrder(orderHash);
- });
- it('should emit orderStateInvalid when maker allowance set to 0 for watched order', (done: DoneCallback) => {
- (async () => {
- signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerToken.address,
- takerToken.address,
- maker,
- taker,
- fillableAmount,
- );
- const orderHash = ZeroEx.getOrderHashHex(signedOrder);
- zeroEx.orderStateWatcher.addOrder(signedOrder);
- const callback = reportNodeCallbackErrors(done)((orderState: OrderState) => {
- expect(orderState.isValid).to.be.false();
- const invalidOrderState = orderState as OrderStateInvalid;
- expect(invalidOrderState.orderHash).to.be.equal(orderHash);
- expect(invalidOrderState.error).to.be.equal(ExchangeContractErrs.InsufficientMakerAllowance);
- });
- zeroEx.orderStateWatcher.subscribe(callback);
- await zeroEx.token.setProxyAllowanceAsync(makerToken.address, maker, new BigNumber(0));
- })().catch(done);
- });
- it('should not emit an orderState event when irrelevant Transfer event received', (done: DoneCallback) => {
- (async () => {
- signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerToken.address,
- takerToken.address,
- maker,
- taker,
- fillableAmount,
- );
- zeroEx.orderStateWatcher.addOrder(signedOrder);
- const callback = reportNodeCallbackErrors(done)((orderState: OrderState) => {
- throw new Error('OrderState callback fired for irrelevant order');
- });
- zeroEx.orderStateWatcher.subscribe(callback);
- const notTheMaker = userAddresses[0];
- const anyRecipient = taker;
- const transferAmount = new BigNumber(2);
- await zeroEx.token.transferAsync(makerToken.address, notTheMaker, anyRecipient, transferAmount);
- setTimeout(() => {
- done();
- }, TIMEOUT_MS);
- })().catch(done);
- });
- it('should emit orderStateInvalid when maker moves balance backing watched order', (done: DoneCallback) => {
- (async () => {
- signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerToken.address,
- takerToken.address,
- maker,
- taker,
- fillableAmount,
- );
- const orderHash = ZeroEx.getOrderHashHex(signedOrder);
- zeroEx.orderStateWatcher.addOrder(signedOrder);
- const callback = reportNodeCallbackErrors(done)((orderState: OrderState) => {
- expect(orderState.isValid).to.be.false();
- const invalidOrderState = orderState as OrderStateInvalid;
- expect(invalidOrderState.orderHash).to.be.equal(orderHash);
- expect(invalidOrderState.error).to.be.equal(ExchangeContractErrs.InsufficientMakerBalance);
- });
- zeroEx.orderStateWatcher.subscribe(callback);
- const anyRecipient = taker;
- const makerBalance = await zeroEx.token.getBalanceAsync(makerToken.address, maker);
- await zeroEx.token.transferAsync(makerToken.address, maker, anyRecipient, makerBalance);
- })().catch(done);
- });
- it('should emit orderStateInvalid when watched order fully filled', (done: DoneCallback) => {
- (async () => {
- signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerToken.address,
- takerToken.address,
- maker,
- taker,
- fillableAmount,
- );
- const orderHash = ZeroEx.getOrderHashHex(signedOrder);
- zeroEx.orderStateWatcher.addOrder(signedOrder);
+ let web3: Web3;
+ let zeroEx: ZeroEx;
+ let tokens: Token[];
+ let tokenUtils: TokenUtils;
+ let fillScenarios: FillScenarios;
+ let userAddresses: string[];
+ let zrxTokenAddress: string;
+ let exchangeContractAddress: string;
+ let makerToken: Token;
+ let takerToken: Token;
+ let maker: string;
+ let taker: string;
+ let signedOrder: SignedOrder;
+ const config = {
+ networkId: constants.TESTRPC_NETWORK_ID,
+ };
+ const decimals = constants.ZRX_DECIMALS;
+ const fillableAmount = ZeroEx.toBaseUnitAmount(new BigNumber(5), decimals);
+ before(async () => {
+ web3 = web3Factory.create();
+ zeroEx = new ZeroEx(web3.currentProvider, config);
+ exchangeContractAddress = zeroEx.exchange.getContractAddress();
+ userAddresses = await zeroEx.getAvailableAddressesAsync();
+ [, maker, taker] = userAddresses;
+ tokens = await zeroEx.tokenRegistry.getTokensAsync();
+ tokenUtils = new TokenUtils(tokens);
+ zrxTokenAddress = tokenUtils.getProtocolTokenOrThrow().address;
+ fillScenarios = new FillScenarios(zeroEx, userAddresses, tokens, zrxTokenAddress, exchangeContractAddress);
+ await fillScenarios.initTokenBalancesAsync();
+ [makerToken, takerToken] = tokenUtils.getDummyTokens();
+ });
+ beforeEach(async () => {
+ await blockchainLifecycle.startAsync();
+ });
+ afterEach(async () => {
+ await blockchainLifecycle.revertAsync();
+ });
+ describe('#removeOrder', async () => {
+ it('should successfully remove existing order', async () => {
+ signedOrder = await fillScenarios.createFillableSignedOrderAsync(
+ makerToken.address,
+ takerToken.address,
+ maker,
+ taker,
+ fillableAmount,
+ );
+ const orderHash = ZeroEx.getOrderHashHex(signedOrder);
+ zeroEx.orderStateWatcher.addOrder(signedOrder);
+ expect((zeroEx.orderStateWatcher as any)._orderByOrderHash).to.include({
+ [orderHash]: signedOrder,
+ });
+ let dependentOrderHashes = (zeroEx.orderStateWatcher as any)._dependentOrderHashes;
+ expect(dependentOrderHashes[signedOrder.maker][signedOrder.makerTokenAddress]).to.have.keys(orderHash);
+ zeroEx.orderStateWatcher.removeOrder(orderHash);
+ expect((zeroEx.orderStateWatcher as any)._orderByOrderHash).to.not.include({
+ [orderHash]: signedOrder,
+ });
+ dependentOrderHashes = (zeroEx.orderStateWatcher as any)._dependentOrderHashes;
+ expect(dependentOrderHashes[signedOrder.maker]).to.be.undefined();
+ });
+ it('should no-op when removing a non-existing order', async () => {
+ signedOrder = await fillScenarios.createFillableSignedOrderAsync(
+ makerToken.address,
+ takerToken.address,
+ maker,
+ taker,
+ fillableAmount,
+ );
+ const orderHash = ZeroEx.getOrderHashHex(signedOrder);
+ const nonExistentOrderHash = `0x${orderHash
+ .substr(2)
+ .split('')
+ .reverse()
+ .join('')}`;
+ zeroEx.orderStateWatcher.removeOrder(nonExistentOrderHash);
+ });
+ });
+ describe('#subscribe', async () => {
+ afterEach(async () => {
+ zeroEx.orderStateWatcher.unsubscribe();
+ });
+ it('should fail when trying to subscribe twice', async () => {
+ zeroEx.orderStateWatcher.subscribe(_.noop);
+ expect(() => zeroEx.orderStateWatcher.subscribe(_.noop)).to.throw(ZeroExError.SubscriptionAlreadyPresent);
+ });
+ });
+ describe('tests with cleanup', async () => {
+ afterEach(async () => {
+ zeroEx.orderStateWatcher.unsubscribe();
+ const orderHash = ZeroEx.getOrderHashHex(signedOrder);
+ zeroEx.orderStateWatcher.removeOrder(orderHash);
+ });
+ it('should emit orderStateInvalid when maker allowance set to 0 for watched order', (done: DoneCallback) => {
+ (async () => {
+ signedOrder = await fillScenarios.createFillableSignedOrderAsync(
+ makerToken.address,
+ takerToken.address,
+ maker,
+ taker,
+ fillableAmount,
+ );
+ const orderHash = ZeroEx.getOrderHashHex(signedOrder);
+ zeroEx.orderStateWatcher.addOrder(signedOrder);
+ const callback = reportNodeCallbackErrors(done)((orderState: OrderState) => {
+ expect(orderState.isValid).to.be.false();
+ const invalidOrderState = orderState as OrderStateInvalid;
+ expect(invalidOrderState.orderHash).to.be.equal(orderHash);
+ expect(invalidOrderState.error).to.be.equal(ExchangeContractErrs.InsufficientMakerAllowance);
+ });
+ zeroEx.orderStateWatcher.subscribe(callback);
+ await zeroEx.token.setProxyAllowanceAsync(makerToken.address, maker, new BigNumber(0));
+ })().catch(done);
+ });
+ it('should not emit an orderState event when irrelevant Transfer event received', (done: DoneCallback) => {
+ (async () => {
+ signedOrder = await fillScenarios.createFillableSignedOrderAsync(
+ makerToken.address,
+ takerToken.address,
+ maker,
+ taker,
+ fillableAmount,
+ );
+ zeroEx.orderStateWatcher.addOrder(signedOrder);
+ const callback = reportNodeCallbackErrors(done)((orderState: OrderState) => {
+ throw new Error('OrderState callback fired for irrelevant order');
+ });
+ zeroEx.orderStateWatcher.subscribe(callback);
+ const notTheMaker = userAddresses[0];
+ const anyRecipient = taker;
+ const transferAmount = new BigNumber(2);
+ await zeroEx.token.transferAsync(makerToken.address, notTheMaker, anyRecipient, transferAmount);
+ setTimeout(() => {
+ done();
+ }, TIMEOUT_MS);
+ })().catch(done);
+ });
+ it('should emit orderStateInvalid when maker moves balance backing watched order', (done: DoneCallback) => {
+ (async () => {
+ signedOrder = await fillScenarios.createFillableSignedOrderAsync(
+ makerToken.address,
+ takerToken.address,
+ maker,
+ taker,
+ fillableAmount,
+ );
+ const orderHash = ZeroEx.getOrderHashHex(signedOrder);
+ zeroEx.orderStateWatcher.addOrder(signedOrder);
+ const callback = reportNodeCallbackErrors(done)((orderState: OrderState) => {
+ expect(orderState.isValid).to.be.false();
+ const invalidOrderState = orderState as OrderStateInvalid;
+ expect(invalidOrderState.orderHash).to.be.equal(orderHash);
+ expect(invalidOrderState.error).to.be.equal(ExchangeContractErrs.InsufficientMakerBalance);
+ });
+ zeroEx.orderStateWatcher.subscribe(callback);
+ const anyRecipient = taker;
+ const makerBalance = await zeroEx.token.getBalanceAsync(makerToken.address, maker);
+ await zeroEx.token.transferAsync(makerToken.address, maker, anyRecipient, makerBalance);
+ })().catch(done);
+ });
+ it('should emit orderStateInvalid when watched order fully filled', (done: DoneCallback) => {
+ (async () => {
+ signedOrder = await fillScenarios.createFillableSignedOrderAsync(
+ makerToken.address,
+ takerToken.address,
+ maker,
+ taker,
+ fillableAmount,
+ );
+ const orderHash = ZeroEx.getOrderHashHex(signedOrder);
+ zeroEx.orderStateWatcher.addOrder(signedOrder);
- const callback = reportNodeCallbackErrors(done)((orderState: OrderState) => {
- expect(orderState.isValid).to.be.false();
- const invalidOrderState = orderState as OrderStateInvalid;
- expect(invalidOrderState.orderHash).to.be.equal(orderHash);
- expect(invalidOrderState.error).to.be.equal(ExchangeContractErrs.OrderRemainingFillAmountZero);
- });
- zeroEx.orderStateWatcher.subscribe(callback);
+ const callback = reportNodeCallbackErrors(done)((orderState: OrderState) => {
+ expect(orderState.isValid).to.be.false();
+ const invalidOrderState = orderState as OrderStateInvalid;
+ expect(invalidOrderState.orderHash).to.be.equal(orderHash);
+ expect(invalidOrderState.error).to.be.equal(ExchangeContractErrs.OrderRemainingFillAmountZero);
+ });
+ zeroEx.orderStateWatcher.subscribe(callback);
- const shouldThrowOnInsufficientBalanceOrAllowance = true;
- await zeroEx.exchange.fillOrderAsync(
- signedOrder,
- fillableAmount,
- shouldThrowOnInsufficientBalanceOrAllowance,
- taker,
- );
- })().catch(done);
- });
- it('should emit orderStateValid when watched order partially filled', (done: DoneCallback) => {
- (async () => {
- signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerToken.address,
- takerToken.address,
- maker,
- taker,
- fillableAmount,
- );
+ const shouldThrowOnInsufficientBalanceOrAllowance = true;
+ await zeroEx.exchange.fillOrderAsync(
+ signedOrder,
+ fillableAmount,
+ shouldThrowOnInsufficientBalanceOrAllowance,
+ taker,
+ );
+ })().catch(done);
+ });
+ it('should emit orderStateValid when watched order partially filled', (done: DoneCallback) => {
+ (async () => {
+ signedOrder = await fillScenarios.createFillableSignedOrderAsync(
+ makerToken.address,
+ takerToken.address,
+ maker,
+ taker,
+ fillableAmount,
+ );
- const makerBalance = await zeroEx.token.getBalanceAsync(makerToken.address, maker);
- const fillAmountInBaseUnits = new BigNumber(2);
- const orderHash = ZeroEx.getOrderHashHex(signedOrder);
- zeroEx.orderStateWatcher.addOrder(signedOrder);
+ const makerBalance = await zeroEx.token.getBalanceAsync(makerToken.address, maker);
+ const fillAmountInBaseUnits = new BigNumber(2);
+ const orderHash = ZeroEx.getOrderHashHex(signedOrder);
+ zeroEx.orderStateWatcher.addOrder(signedOrder);
- const callback = reportNodeCallbackErrors(done)((orderState: OrderState) => {
- expect(orderState.isValid).to.be.true();
- const validOrderState = orderState as OrderStateValid;
- expect(validOrderState.orderHash).to.be.equal(orderHash);
- const orderRelevantState = validOrderState.orderRelevantState;
- const remainingMakerBalance = makerBalance.sub(fillAmountInBaseUnits);
- const remainingFillable = fillableAmount.minus(fillAmountInBaseUnits);
- expect(orderRelevantState.remainingFillableMakerTokenAmount).to.be.bignumber.equal(
- remainingFillable,
- );
- expect(orderRelevantState.remainingFillableTakerTokenAmount).to.be.bignumber.equal(
- remainingFillable,
- );
- expect(orderRelevantState.makerBalance).to.be.bignumber.equal(remainingMakerBalance);
- });
- zeroEx.orderStateWatcher.subscribe(callback);
- const shouldThrowOnInsufficientBalanceOrAllowance = true;
- await zeroEx.exchange.fillOrderAsync(
- signedOrder,
- fillAmountInBaseUnits,
- shouldThrowOnInsufficientBalanceOrAllowance,
- taker,
- );
- })().catch(done);
- });
- it('should trigger the callback when orders backing ZRX allowance changes', (done: DoneCallback) => {
- (async () => {
- const makerFee = ZeroEx.toBaseUnitAmount(new BigNumber(2), 18);
- const takerFee = ZeroEx.toBaseUnitAmount(new BigNumber(0), 18);
- signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync(
- makerToken.address,
- takerToken.address,
- makerFee,
- takerFee,
- maker,
- taker,
- fillableAmount,
- taker,
- );
- const callback = reportNodeCallbackErrors(done)();
- zeroEx.orderStateWatcher.addOrder(signedOrder);
- zeroEx.orderStateWatcher.subscribe(callback);
- await zeroEx.token.setProxyAllowanceAsync(zrxTokenAddress, maker, new BigNumber(0));
- })().catch(done);
- });
- describe('remainingFillable(M|T)akerTokenAmount', () => {
- it('should calculate correct remaining fillable', (done: DoneCallback) => {
- (async () => {
- const takerFillableAmount = ZeroEx.toBaseUnitAmount(new BigNumber(10), decimals);
- const makerFillableAmount = ZeroEx.toBaseUnitAmount(new BigNumber(20), decimals);
- signedOrder = await fillScenarios.createAsymmetricFillableSignedOrderAsync(
- makerToken.address,
- takerToken.address,
- maker,
- taker,
- makerFillableAmount,
- takerFillableAmount,
- );
- const fillAmountInBaseUnits = ZeroEx.toBaseUnitAmount(new BigNumber(2), decimals);
- const orderHash = ZeroEx.getOrderHashHex(signedOrder);
- zeroEx.orderStateWatcher.addOrder(signedOrder);
- const callback = reportNodeCallbackErrors(done)((orderState: OrderState) => {
- expect(orderState.isValid).to.be.true();
- const validOrderState = orderState as OrderStateValid;
- expect(validOrderState.orderHash).to.be.equal(orderHash);
- const orderRelevantState = validOrderState.orderRelevantState;
- expect(orderRelevantState.remainingFillableMakerTokenAmount).to.be.bignumber.equal(
- ZeroEx.toBaseUnitAmount(new BigNumber(16), decimals),
- );
- expect(orderRelevantState.remainingFillableTakerTokenAmount).to.be.bignumber.equal(
- ZeroEx.toBaseUnitAmount(new BigNumber(8), decimals),
- );
- });
- zeroEx.orderStateWatcher.subscribe(callback);
- const shouldThrowOnInsufficientBalanceOrAllowance = true;
- await zeroEx.exchange.fillOrderAsync(
- signedOrder,
- fillAmountInBaseUnits,
- shouldThrowOnInsufficientBalanceOrAllowance,
- taker,
- );
- })().catch(done);
- });
- it('should equal approved amount when approved amount is lowest', (done: DoneCallback) => {
- (async () => {
- signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerToken.address,
- takerToken.address,
- maker,
- taker,
- fillableAmount,
- );
+ const callback = reportNodeCallbackErrors(done)((orderState: OrderState) => {
+ expect(orderState.isValid).to.be.true();
+ const validOrderState = orderState as OrderStateValid;
+ expect(validOrderState.orderHash).to.be.equal(orderHash);
+ const orderRelevantState = validOrderState.orderRelevantState;
+ const remainingMakerBalance = makerBalance.sub(fillAmountInBaseUnits);
+ const remainingFillable = fillableAmount.minus(fillAmountInBaseUnits);
+ expect(orderRelevantState.remainingFillableMakerTokenAmount).to.be.bignumber.equal(
+ remainingFillable,
+ );
+ expect(orderRelevantState.remainingFillableTakerTokenAmount).to.be.bignumber.equal(
+ remainingFillable,
+ );
+ expect(orderRelevantState.makerBalance).to.be.bignumber.equal(remainingMakerBalance);
+ });
+ zeroEx.orderStateWatcher.subscribe(callback);
+ const shouldThrowOnInsufficientBalanceOrAllowance = true;
+ await zeroEx.exchange.fillOrderAsync(
+ signedOrder,
+ fillAmountInBaseUnits,
+ shouldThrowOnInsufficientBalanceOrAllowance,
+ taker,
+ );
+ })().catch(done);
+ });
+ it('should trigger the callback when orders backing ZRX allowance changes', (done: DoneCallback) => {
+ (async () => {
+ const makerFee = ZeroEx.toBaseUnitAmount(new BigNumber(2), 18);
+ const takerFee = ZeroEx.toBaseUnitAmount(new BigNumber(0), 18);
+ signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync(
+ makerToken.address,
+ takerToken.address,
+ makerFee,
+ takerFee,
+ maker,
+ taker,
+ fillableAmount,
+ taker,
+ );
+ const callback = reportNodeCallbackErrors(done)();
+ zeroEx.orderStateWatcher.addOrder(signedOrder);
+ zeroEx.orderStateWatcher.subscribe(callback);
+ await zeroEx.token.setProxyAllowanceAsync(zrxTokenAddress, maker, new BigNumber(0));
+ })().catch(done);
+ });
+ describe('remainingFillable(M|T)akerTokenAmount', () => {
+ it('should calculate correct remaining fillable', (done: DoneCallback) => {
+ (async () => {
+ const takerFillableAmount = ZeroEx.toBaseUnitAmount(new BigNumber(10), decimals);
+ const makerFillableAmount = ZeroEx.toBaseUnitAmount(new BigNumber(20), decimals);
+ signedOrder = await fillScenarios.createAsymmetricFillableSignedOrderAsync(
+ makerToken.address,
+ takerToken.address,
+ maker,
+ taker,
+ makerFillableAmount,
+ takerFillableAmount,
+ );
+ const fillAmountInBaseUnits = ZeroEx.toBaseUnitAmount(new BigNumber(2), decimals);
+ const orderHash = ZeroEx.getOrderHashHex(signedOrder);
+ zeroEx.orderStateWatcher.addOrder(signedOrder);
+ const callback = reportNodeCallbackErrors(done)((orderState: OrderState) => {
+ expect(orderState.isValid).to.be.true();
+ const validOrderState = orderState as OrderStateValid;
+ expect(validOrderState.orderHash).to.be.equal(orderHash);
+ const orderRelevantState = validOrderState.orderRelevantState;
+ expect(orderRelevantState.remainingFillableMakerTokenAmount).to.be.bignumber.equal(
+ ZeroEx.toBaseUnitAmount(new BigNumber(16), decimals),
+ );
+ expect(orderRelevantState.remainingFillableTakerTokenAmount).to.be.bignumber.equal(
+ ZeroEx.toBaseUnitAmount(new BigNumber(8), decimals),
+ );
+ });
+ zeroEx.orderStateWatcher.subscribe(callback);
+ const shouldThrowOnInsufficientBalanceOrAllowance = true;
+ await zeroEx.exchange.fillOrderAsync(
+ signedOrder,
+ fillAmountInBaseUnits,
+ shouldThrowOnInsufficientBalanceOrAllowance,
+ taker,
+ );
+ })().catch(done);
+ });
+ it('should equal approved amount when approved amount is lowest', (done: DoneCallback) => {
+ (async () => {
+ signedOrder = await fillScenarios.createFillableSignedOrderAsync(
+ makerToken.address,
+ takerToken.address,
+ maker,
+ taker,
+ fillableAmount,
+ );
- const changedMakerApprovalAmount = ZeroEx.toBaseUnitAmount(new BigNumber(3), decimals);
- zeroEx.orderStateWatcher.addOrder(signedOrder);
+ const changedMakerApprovalAmount = ZeroEx.toBaseUnitAmount(new BigNumber(3), decimals);
+ zeroEx.orderStateWatcher.addOrder(signedOrder);
- const callback = reportNodeCallbackErrors(done)((orderState: OrderState) => {
- const validOrderState = orderState as OrderStateValid;
- const orderRelevantState = validOrderState.orderRelevantState;
- expect(orderRelevantState.remainingFillableMakerTokenAmount).to.be.bignumber.equal(
- changedMakerApprovalAmount,
- );
- expect(orderRelevantState.remainingFillableTakerTokenAmount).to.be.bignumber.equal(
- changedMakerApprovalAmount,
- );
- });
- zeroEx.orderStateWatcher.subscribe(callback);
- await zeroEx.token.setProxyAllowanceAsync(makerToken.address, maker, changedMakerApprovalAmount);
- })().catch(done);
- });
- it('should equal balance amount when balance amount is lowest', (done: DoneCallback) => {
- (async () => {
- signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerToken.address,
- takerToken.address,
- maker,
- taker,
- fillableAmount,
- );
+ const callback = reportNodeCallbackErrors(done)((orderState: OrderState) => {
+ const validOrderState = orderState as OrderStateValid;
+ const orderRelevantState = validOrderState.orderRelevantState;
+ expect(orderRelevantState.remainingFillableMakerTokenAmount).to.be.bignumber.equal(
+ changedMakerApprovalAmount,
+ );
+ expect(orderRelevantState.remainingFillableTakerTokenAmount).to.be.bignumber.equal(
+ changedMakerApprovalAmount,
+ );
+ });
+ zeroEx.orderStateWatcher.subscribe(callback);
+ await zeroEx.token.setProxyAllowanceAsync(makerToken.address, maker, changedMakerApprovalAmount);
+ })().catch(done);
+ });
+ it('should equal balance amount when balance amount is lowest', (done: DoneCallback) => {
+ (async () => {
+ signedOrder = await fillScenarios.createFillableSignedOrderAsync(
+ makerToken.address,
+ takerToken.address,
+ maker,
+ taker,
+ fillableAmount,
+ );
- const makerBalance = await zeroEx.token.getBalanceAsync(makerToken.address, maker);
+ const makerBalance = await zeroEx.token.getBalanceAsync(makerToken.address, maker);
- const remainingAmount = ZeroEx.toBaseUnitAmount(new BigNumber(1), decimals);
- const transferAmount = makerBalance.sub(remainingAmount);
- zeroEx.orderStateWatcher.addOrder(signedOrder);
+ const remainingAmount = ZeroEx.toBaseUnitAmount(new BigNumber(1), decimals);
+ const transferAmount = makerBalance.sub(remainingAmount);
+ zeroEx.orderStateWatcher.addOrder(signedOrder);
- const callback = reportNodeCallbackErrors(done)((orderState: OrderState) => {
- expect(orderState.isValid).to.be.true();
- const validOrderState = orderState as OrderStateValid;
- const orderRelevantState = validOrderState.orderRelevantState;
- expect(orderRelevantState.remainingFillableMakerTokenAmount).to.be.bignumber.equal(
- remainingAmount,
- );
- expect(orderRelevantState.remainingFillableTakerTokenAmount).to.be.bignumber.equal(
- remainingAmount,
- );
- });
- zeroEx.orderStateWatcher.subscribe(callback);
- await zeroEx.token.transferAsync(makerToken.address, maker, ZeroEx.NULL_ADDRESS, transferAmount);
- })().catch(done);
- });
- it('should equal remaining amount when partially cancelled and order has fees', (done: DoneCallback) => {
- (async () => {
- const takerFee = ZeroEx.toBaseUnitAmount(new BigNumber(0), decimals);
- const makerFee = ZeroEx.toBaseUnitAmount(new BigNumber(5), decimals);
- const feeRecipient = taker;
- signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync(
- makerToken.address,
- takerToken.address,
- makerFee,
- takerFee,
- maker,
- taker,
- fillableAmount,
- feeRecipient,
- );
+ const callback = reportNodeCallbackErrors(done)((orderState: OrderState) => {
+ expect(orderState.isValid).to.be.true();
+ const validOrderState = orderState as OrderStateValid;
+ const orderRelevantState = validOrderState.orderRelevantState;
+ expect(orderRelevantState.remainingFillableMakerTokenAmount).to.be.bignumber.equal(
+ remainingAmount,
+ );
+ expect(orderRelevantState.remainingFillableTakerTokenAmount).to.be.bignumber.equal(
+ remainingAmount,
+ );
+ });
+ zeroEx.orderStateWatcher.subscribe(callback);
+ await zeroEx.token.transferAsync(makerToken.address, maker, ZeroEx.NULL_ADDRESS, transferAmount);
+ })().catch(done);
+ });
+ it('should equal remaining amount when partially cancelled and order has fees', (done: DoneCallback) => {
+ (async () => {
+ const takerFee = ZeroEx.toBaseUnitAmount(new BigNumber(0), decimals);
+ const makerFee = ZeroEx.toBaseUnitAmount(new BigNumber(5), decimals);
+ const feeRecipient = taker;
+ signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync(
+ makerToken.address,
+ takerToken.address,
+ makerFee,
+ takerFee,
+ maker,
+ taker,
+ fillableAmount,
+ feeRecipient,
+ );
- const remainingTokenAmount = ZeroEx.toBaseUnitAmount(new BigNumber(4), decimals);
- const transferTokenAmount = makerFee.sub(remainingTokenAmount);
- zeroEx.orderStateWatcher.addOrder(signedOrder);
+ const remainingTokenAmount = ZeroEx.toBaseUnitAmount(new BigNumber(4), decimals);
+ const transferTokenAmount = makerFee.sub(remainingTokenAmount);
+ zeroEx.orderStateWatcher.addOrder(signedOrder);
- const callback = reportNodeCallbackErrors(done)((orderState: OrderState) => {
- expect(orderState.isValid).to.be.true();
- const validOrderState = orderState as OrderStateValid;
- const orderRelevantState = validOrderState.orderRelevantState;
- expect(orderRelevantState.remainingFillableMakerTokenAmount).to.be.bignumber.equal(
- remainingTokenAmount,
- );
- });
- zeroEx.orderStateWatcher.subscribe(callback);
- await zeroEx.exchange.cancelOrderAsync(signedOrder, transferTokenAmount);
- })().catch(done);
- });
- it('should equal ratio amount when fee balance is lowered', (done: DoneCallback) => {
- (async () => {
- const takerFee = ZeroEx.toBaseUnitAmount(new BigNumber(0), decimals);
- const makerFee = ZeroEx.toBaseUnitAmount(new BigNumber(5), decimals);
- const feeRecipient = taker;
- signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync(
- makerToken.address,
- takerToken.address,
- makerFee,
- takerFee,
- maker,
- taker,
- fillableAmount,
- feeRecipient,
- );
+ const callback = reportNodeCallbackErrors(done)((orderState: OrderState) => {
+ expect(orderState.isValid).to.be.true();
+ const validOrderState = orderState as OrderStateValid;
+ const orderRelevantState = validOrderState.orderRelevantState;
+ expect(orderRelevantState.remainingFillableMakerTokenAmount).to.be.bignumber.equal(
+ remainingTokenAmount,
+ );
+ });
+ zeroEx.orderStateWatcher.subscribe(callback);
+ await zeroEx.exchange.cancelOrderAsync(signedOrder, transferTokenAmount);
+ })().catch(done);
+ });
+ it('should equal ratio amount when fee balance is lowered', (done: DoneCallback) => {
+ (async () => {
+ const takerFee = ZeroEx.toBaseUnitAmount(new BigNumber(0), decimals);
+ const makerFee = ZeroEx.toBaseUnitAmount(new BigNumber(5), decimals);
+ const feeRecipient = taker;
+ signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync(
+ makerToken.address,
+ takerToken.address,
+ makerFee,
+ takerFee,
+ maker,
+ taker,
+ fillableAmount,
+ feeRecipient,
+ );
- const remainingFeeAmount = ZeroEx.toBaseUnitAmount(new BigNumber(3), decimals);
+ const remainingFeeAmount = ZeroEx.toBaseUnitAmount(new BigNumber(3), decimals);
- const remainingTokenAmount = ZeroEx.toBaseUnitAmount(new BigNumber(4), decimals);
- const transferTokenAmount = makerFee.sub(remainingTokenAmount);
- zeroEx.orderStateWatcher.addOrder(signedOrder);
+ const remainingTokenAmount = ZeroEx.toBaseUnitAmount(new BigNumber(4), decimals);
+ const transferTokenAmount = makerFee.sub(remainingTokenAmount);
+ zeroEx.orderStateWatcher.addOrder(signedOrder);
- const callback = reportNodeCallbackErrors(done)((orderState: OrderState) => {
- const validOrderState = orderState as OrderStateValid;
- const orderRelevantState = validOrderState.orderRelevantState;
- expect(orderRelevantState.remainingFillableMakerTokenAmount).to.be.bignumber.equal(
- remainingFeeAmount,
- );
- });
- zeroEx.orderStateWatcher.subscribe(callback);
- await zeroEx.token.setProxyAllowanceAsync(zrxTokenAddress, maker, remainingFeeAmount);
- await zeroEx.token.transferAsync(
- makerToken.address,
- maker,
- ZeroEx.NULL_ADDRESS,
- transferTokenAmount,
- );
- })().catch(done);
- });
- it('should calculate full amount when all available and non-divisible', (done: DoneCallback) => {
- (async () => {
- const takerFee = ZeroEx.toBaseUnitAmount(new BigNumber(0), decimals);
- const makerFee = ZeroEx.toBaseUnitAmount(new BigNumber(2), decimals);
- const feeRecipient = taker;
- signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync(
- makerToken.address,
- takerToken.address,
- makerFee,
- takerFee,
- maker,
- taker,
- fillableAmount,
- feeRecipient,
- );
+ const callback = reportNodeCallbackErrors(done)((orderState: OrderState) => {
+ const validOrderState = orderState as OrderStateValid;
+ const orderRelevantState = validOrderState.orderRelevantState;
+ expect(orderRelevantState.remainingFillableMakerTokenAmount).to.be.bignumber.equal(
+ remainingFeeAmount,
+ );
+ });
+ zeroEx.orderStateWatcher.subscribe(callback);
+ await zeroEx.token.setProxyAllowanceAsync(zrxTokenAddress, maker, remainingFeeAmount);
+ await zeroEx.token.transferAsync(
+ makerToken.address,
+ maker,
+ ZeroEx.NULL_ADDRESS,
+ transferTokenAmount,
+ );
+ })().catch(done);
+ });
+ it('should calculate full amount when all available and non-divisible', (done: DoneCallback) => {
+ (async () => {
+ const takerFee = ZeroEx.toBaseUnitAmount(new BigNumber(0), decimals);
+ const makerFee = ZeroEx.toBaseUnitAmount(new BigNumber(2), decimals);
+ const feeRecipient = taker;
+ signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync(
+ makerToken.address,
+ takerToken.address,
+ makerFee,
+ takerFee,
+ maker,
+ taker,
+ fillableAmount,
+ feeRecipient,
+ );
- zeroEx.orderStateWatcher.addOrder(signedOrder);
+ zeroEx.orderStateWatcher.addOrder(signedOrder);
- const callback = reportNodeCallbackErrors(done)((orderState: OrderState) => {
- const validOrderState = orderState as OrderStateValid;
- const orderRelevantState = validOrderState.orderRelevantState;
- expect(orderRelevantState.remainingFillableMakerTokenAmount).to.be.bignumber.equal(
- fillableAmount,
- );
- });
- zeroEx.orderStateWatcher.subscribe(callback);
- await zeroEx.token.setProxyAllowanceAsync(
- makerToken.address,
- maker,
- ZeroEx.toBaseUnitAmount(new BigNumber(100), decimals),
- );
- })().catch(done);
- });
- });
- it('should emit orderStateInvalid when watched order cancelled', (done: DoneCallback) => {
- (async () => {
- signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerToken.address,
- takerToken.address,
- maker,
- taker,
- fillableAmount,
- );
- const orderHash = ZeroEx.getOrderHashHex(signedOrder);
- zeroEx.orderStateWatcher.addOrder(signedOrder);
+ const callback = reportNodeCallbackErrors(done)((orderState: OrderState) => {
+ const validOrderState = orderState as OrderStateValid;
+ const orderRelevantState = validOrderState.orderRelevantState;
+ expect(orderRelevantState.remainingFillableMakerTokenAmount).to.be.bignumber.equal(
+ fillableAmount,
+ );
+ });
+ zeroEx.orderStateWatcher.subscribe(callback);
+ await zeroEx.token.setProxyAllowanceAsync(
+ makerToken.address,
+ maker,
+ ZeroEx.toBaseUnitAmount(new BigNumber(100), decimals),
+ );
+ })().catch(done);
+ });
+ });
+ it('should emit orderStateInvalid when watched order cancelled', (done: DoneCallback) => {
+ (async () => {
+ signedOrder = await fillScenarios.createFillableSignedOrderAsync(
+ makerToken.address,
+ takerToken.address,
+ maker,
+ taker,
+ fillableAmount,
+ );
+ const orderHash = ZeroEx.getOrderHashHex(signedOrder);
+ zeroEx.orderStateWatcher.addOrder(signedOrder);
- const callback = reportNodeCallbackErrors(done)((orderState: OrderState) => {
- expect(orderState.isValid).to.be.false();
- const invalidOrderState = orderState as OrderStateInvalid;
- expect(invalidOrderState.orderHash).to.be.equal(orderHash);
- expect(invalidOrderState.error).to.be.equal(ExchangeContractErrs.OrderRemainingFillAmountZero);
- });
- zeroEx.orderStateWatcher.subscribe(callback);
+ const callback = reportNodeCallbackErrors(done)((orderState: OrderState) => {
+ expect(orderState.isValid).to.be.false();
+ const invalidOrderState = orderState as OrderStateInvalid;
+ expect(invalidOrderState.orderHash).to.be.equal(orderHash);
+ expect(invalidOrderState.error).to.be.equal(ExchangeContractErrs.OrderRemainingFillAmountZero);
+ });
+ zeroEx.orderStateWatcher.subscribe(callback);
- await zeroEx.exchange.cancelOrderAsync(signedOrder, fillableAmount);
- })().catch(done);
- });
- it('should emit orderStateInvalid when within rounding error range', (done: DoneCallback) => {
- (async () => {
- const remainingFillableAmountInBaseUnits = new BigNumber(100);
- signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerToken.address,
- takerToken.address,
- maker,
- taker,
- fillableAmount,
- );
- const orderHash = ZeroEx.getOrderHashHex(signedOrder);
- zeroEx.orderStateWatcher.addOrder(signedOrder);
+ await zeroEx.exchange.cancelOrderAsync(signedOrder, fillableAmount);
+ })().catch(done);
+ });
+ it('should emit orderStateInvalid when within rounding error range', (done: DoneCallback) => {
+ (async () => {
+ const remainingFillableAmountInBaseUnits = new BigNumber(100);
+ signedOrder = await fillScenarios.createFillableSignedOrderAsync(
+ makerToken.address,
+ takerToken.address,
+ maker,
+ taker,
+ fillableAmount,
+ );
+ const orderHash = ZeroEx.getOrderHashHex(signedOrder);
+ zeroEx.orderStateWatcher.addOrder(signedOrder);
- const callback = reportNodeCallbackErrors(done)((orderState: OrderState) => {
- expect(orderState.isValid).to.be.false();
- const invalidOrderState = orderState as OrderStateInvalid;
- expect(invalidOrderState.orderHash).to.be.equal(orderHash);
- expect(invalidOrderState.error).to.be.equal(ExchangeContractErrs.OrderFillRoundingError);
- });
- zeroEx.orderStateWatcher.subscribe(callback);
- await zeroEx.exchange.cancelOrderAsync(
- signedOrder,
- fillableAmount.minus(remainingFillableAmountInBaseUnits),
- );
- })().catch(done);
- });
- it('should emit orderStateValid when watched order partially cancelled', (done: DoneCallback) => {
- (async () => {
- signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerToken.address,
- takerToken.address,
- maker,
- taker,
- fillableAmount,
- );
+ const callback = reportNodeCallbackErrors(done)((orderState: OrderState) => {
+ expect(orderState.isValid).to.be.false();
+ const invalidOrderState = orderState as OrderStateInvalid;
+ expect(invalidOrderState.orderHash).to.be.equal(orderHash);
+ expect(invalidOrderState.error).to.be.equal(ExchangeContractErrs.OrderFillRoundingError);
+ });
+ zeroEx.orderStateWatcher.subscribe(callback);
+ await zeroEx.exchange.cancelOrderAsync(
+ signedOrder,
+ fillableAmount.minus(remainingFillableAmountInBaseUnits),
+ );
+ })().catch(done);
+ });
+ it('should emit orderStateValid when watched order partially cancelled', (done: DoneCallback) => {
+ (async () => {
+ signedOrder = await fillScenarios.createFillableSignedOrderAsync(
+ makerToken.address,
+ takerToken.address,
+ maker,
+ taker,
+ fillableAmount,
+ );
- const cancelAmountInBaseUnits = new BigNumber(2);
- const orderHash = ZeroEx.getOrderHashHex(signedOrder);
- zeroEx.orderStateWatcher.addOrder(signedOrder);
+ const cancelAmountInBaseUnits = new BigNumber(2);
+ const orderHash = ZeroEx.getOrderHashHex(signedOrder);
+ zeroEx.orderStateWatcher.addOrder(signedOrder);
- const callback = reportNodeCallbackErrors(done)((orderState: OrderState) => {
- expect(orderState.isValid).to.be.true();
- const validOrderState = orderState as OrderStateValid;
- expect(validOrderState.orderHash).to.be.equal(orderHash);
- const orderRelevantState = validOrderState.orderRelevantState;
- expect(orderRelevantState.cancelledTakerTokenAmount).to.be.bignumber.equal(cancelAmountInBaseUnits);
- });
- zeroEx.orderStateWatcher.subscribe(callback);
- await zeroEx.exchange.cancelOrderAsync(signedOrder, cancelAmountInBaseUnits);
- })().catch(done);
- });
- });
+ const callback = reportNodeCallbackErrors(done)((orderState: OrderState) => {
+ expect(orderState.isValid).to.be.true();
+ const validOrderState = orderState as OrderStateValid;
+ expect(validOrderState.orderHash).to.be.equal(orderHash);
+ const orderRelevantState = validOrderState.orderRelevantState;
+ expect(orderRelevantState.cancelledTakerTokenAmount).to.be.bignumber.equal(cancelAmountInBaseUnits);
+ });
+ zeroEx.orderStateWatcher.subscribe(callback);
+ await zeroEx.exchange.cancelOrderAsync(signedOrder, cancelAmountInBaseUnits);
+ })().catch(done);
+ });
+ });
}); // tslint:disable:max-file-line-count
diff --git a/packages/0x.js/test/order_validation_test.ts b/packages/0x.js/test/order_validation_test.ts
index 4e023a9cc..be3e0590c 100644
--- a/packages/0x.js/test/order_validation_test.ts
+++ b/packages/0x.js/test/order_validation_test.ts
@@ -20,455 +20,455 @@ const expect = chai.expect;
const blockchainLifecycle = new BlockchainLifecycle(constants.RPC_URL);
describe('OrderValidation', () => {
- let web3: Web3;
- let zeroEx: ZeroEx;
- let userAddresses: string[];
- let tokens: Token[];
- let tokenUtils: TokenUtils;
- let exchangeContractAddress: string;
- let zrxTokenAddress: string;
- let fillScenarios: FillScenarios;
- let makerTokenAddress: string;
- let takerTokenAddress: string;
- let coinbase: string;
- let makerAddress: string;
- let takerAddress: string;
- let feeRecipient: string;
- const fillableAmount = new BigNumber(5);
- const fillTakerAmount = new BigNumber(5);
- const config = {
- networkId: constants.TESTRPC_NETWORK_ID,
- };
- before(async () => {
- web3 = web3Factory.create();
- zeroEx = new ZeroEx(web3.currentProvider, config);
- exchangeContractAddress = zeroEx.exchange.getContractAddress();
- userAddresses = await zeroEx.getAvailableAddressesAsync();
- [coinbase, makerAddress, takerAddress, feeRecipient] = userAddresses;
- tokens = await zeroEx.tokenRegistry.getTokensAsync();
- tokenUtils = new TokenUtils(tokens);
- zrxTokenAddress = tokenUtils.getProtocolTokenOrThrow().address;
- fillScenarios = new FillScenarios(zeroEx, userAddresses, tokens, zrxTokenAddress, exchangeContractAddress);
- const [makerToken, takerToken] = tokenUtils.getDummyTokens();
- makerTokenAddress = makerToken.address;
- takerTokenAddress = takerToken.address;
- });
- beforeEach(async () => {
- await blockchainLifecycle.startAsync();
- });
- afterEach(async () => {
- await blockchainLifecycle.revertAsync();
- });
- describe('validateOrderFillableOrThrowAsync', () => {
- it('should succeed if the order is fillable', async () => {
- const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- fillableAmount,
- );
- await zeroEx.exchange.validateOrderFillableOrThrowAsync(signedOrder);
- });
- it('should succeed if the order is asymmetric and fillable', async () => {
- const makerFillableAmount = fillableAmount;
- const takerFillableAmount = fillableAmount.minus(4);
- const signedOrder = await fillScenarios.createAsymmetricFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- makerFillableAmount,
- takerFillableAmount,
- );
- await zeroEx.exchange.validateOrderFillableOrThrowAsync(signedOrder);
- });
- it('should throw when the order is fully filled or cancelled', async () => {
- const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- fillableAmount,
- );
- await zeroEx.exchange.cancelOrderAsync(signedOrder, fillableAmount);
- return expect(zeroEx.exchange.validateOrderFillableOrThrowAsync(signedOrder)).to.be.rejectedWith(
- ExchangeContractErrs.OrderRemainingFillAmountZero,
- );
- });
- it('should throw when order is expired', async () => {
- const expirationInPast = new BigNumber(1496826058); // 7th Jun 2017
- const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- fillableAmount,
- expirationInPast,
- );
- return expect(zeroEx.exchange.validateOrderFillableOrThrowAsync(signedOrder)).to.be.rejectedWith(
- ExchangeContractErrs.OrderFillExpired,
- );
- });
- });
- describe('validateFillOrderAndThrowIfInvalidAsync', () => {
- it('should throw when the fill amount is zero', async () => {
- const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- fillableAmount,
- );
- const zeroFillAmount = new BigNumber(0);
- return expect(
- zeroEx.exchange.validateFillOrderThrowIfInvalidAsync(signedOrder, zeroFillAmount, takerAddress),
- ).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero);
- });
- it('should throw when the signature is invalid', async () => {
- const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- fillableAmount,
- );
- // 27 <--> 28
- signedOrder.ecSignature.v = 28 - signedOrder.ecSignature.v + 27;
- return expect(
- zeroEx.exchange.validateFillOrderThrowIfInvalidAsync(signedOrder, fillableAmount, takerAddress),
- ).to.be.rejectedWith(ZeroExError.InvalidSignature);
- });
- it('should throw when the order is fully filled or cancelled', async () => {
- const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- fillableAmount,
- );
- await zeroEx.exchange.cancelOrderAsync(signedOrder, fillableAmount);
- return expect(
- zeroEx.exchange.validateFillOrderThrowIfInvalidAsync(signedOrder, fillableAmount, takerAddress),
- ).to.be.rejectedWith(ExchangeContractErrs.OrderRemainingFillAmountZero);
- });
- it('should throw when sender is not a taker', async () => {
- const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- fillableAmount,
- );
- const nonTakerAddress = userAddresses[6];
- return expect(
- zeroEx.exchange.validateFillOrderThrowIfInvalidAsync(signedOrder, fillTakerAmount, nonTakerAddress),
- ).to.be.rejectedWith(ExchangeContractErrs.TransactionSenderIsNotFillOrderTaker);
- });
- it('should throw when order is expired', async () => {
- const expirationInPast = new BigNumber(1496826058); // 7th Jun 2017
- const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- fillableAmount,
- expirationInPast,
- );
- return expect(
- zeroEx.exchange.validateFillOrderThrowIfInvalidAsync(signedOrder, fillTakerAmount, takerAddress),
- ).to.be.rejectedWith(ExchangeContractErrs.OrderFillExpired);
- });
- it('should throw when there a rounding error would have occurred', async () => {
- const makerAmount = new BigNumber(3);
- const takerAmount = new BigNumber(5);
- const signedOrder = await fillScenarios.createAsymmetricFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- makerAmount,
- takerAmount,
- );
- const fillTakerAmountThatCausesRoundingError = new BigNumber(3);
- return expect(
- zeroEx.exchange.validateFillOrderThrowIfInvalidAsync(
- signedOrder,
- fillTakerAmountThatCausesRoundingError,
- takerAddress,
- ),
- ).to.be.rejectedWith(ExchangeContractErrs.OrderFillRoundingError);
- });
- });
- describe('#validateFillOrKillOrderAndThrowIfInvalidAsync', () => {
- it('should throw if remaining fillAmount is less then the desired fillAmount', async () => {
- const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- fillableAmount,
- );
- const tooLargeFillAmount = new BigNumber(7);
- const fillAmountDifference = tooLargeFillAmount.minus(fillableAmount);
- await zeroEx.token.transferAsync(takerTokenAddress, coinbase, takerAddress, fillAmountDifference);
- await zeroEx.token.setProxyAllowanceAsync(takerTokenAddress, takerAddress, tooLargeFillAmount);
- await zeroEx.token.transferAsync(makerTokenAddress, coinbase, makerAddress, fillAmountDifference);
- await zeroEx.token.setProxyAllowanceAsync(makerTokenAddress, makerAddress, tooLargeFillAmount);
+ let web3: Web3;
+ let zeroEx: ZeroEx;
+ let userAddresses: string[];
+ let tokens: Token[];
+ let tokenUtils: TokenUtils;
+ let exchangeContractAddress: string;
+ let zrxTokenAddress: string;
+ let fillScenarios: FillScenarios;
+ let makerTokenAddress: string;
+ let takerTokenAddress: string;
+ let coinbase: string;
+ let makerAddress: string;
+ let takerAddress: string;
+ let feeRecipient: string;
+ const fillableAmount = new BigNumber(5);
+ const fillTakerAmount = new BigNumber(5);
+ const config = {
+ networkId: constants.TESTRPC_NETWORK_ID,
+ };
+ before(async () => {
+ web3 = web3Factory.create();
+ zeroEx = new ZeroEx(web3.currentProvider, config);
+ exchangeContractAddress = zeroEx.exchange.getContractAddress();
+ userAddresses = await zeroEx.getAvailableAddressesAsync();
+ [coinbase, makerAddress, takerAddress, feeRecipient] = userAddresses;
+ tokens = await zeroEx.tokenRegistry.getTokensAsync();
+ tokenUtils = new TokenUtils(tokens);
+ zrxTokenAddress = tokenUtils.getProtocolTokenOrThrow().address;
+ fillScenarios = new FillScenarios(zeroEx, userAddresses, tokens, zrxTokenAddress, exchangeContractAddress);
+ const [makerToken, takerToken] = tokenUtils.getDummyTokens();
+ makerTokenAddress = makerToken.address;
+ takerTokenAddress = takerToken.address;
+ });
+ beforeEach(async () => {
+ await blockchainLifecycle.startAsync();
+ });
+ afterEach(async () => {
+ await blockchainLifecycle.revertAsync();
+ });
+ describe('validateOrderFillableOrThrowAsync', () => {
+ it('should succeed if the order is fillable', async () => {
+ const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
+ makerTokenAddress,
+ takerTokenAddress,
+ makerAddress,
+ takerAddress,
+ fillableAmount,
+ );
+ await zeroEx.exchange.validateOrderFillableOrThrowAsync(signedOrder);
+ });
+ it('should succeed if the order is asymmetric and fillable', async () => {
+ const makerFillableAmount = fillableAmount;
+ const takerFillableAmount = fillableAmount.minus(4);
+ const signedOrder = await fillScenarios.createAsymmetricFillableSignedOrderAsync(
+ makerTokenAddress,
+ takerTokenAddress,
+ makerAddress,
+ takerAddress,
+ makerFillableAmount,
+ takerFillableAmount,
+ );
+ await zeroEx.exchange.validateOrderFillableOrThrowAsync(signedOrder);
+ });
+ it('should throw when the order is fully filled or cancelled', async () => {
+ const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
+ makerTokenAddress,
+ takerTokenAddress,
+ makerAddress,
+ takerAddress,
+ fillableAmount,
+ );
+ await zeroEx.exchange.cancelOrderAsync(signedOrder, fillableAmount);
+ return expect(zeroEx.exchange.validateOrderFillableOrThrowAsync(signedOrder)).to.be.rejectedWith(
+ ExchangeContractErrs.OrderRemainingFillAmountZero,
+ );
+ });
+ it('should throw when order is expired', async () => {
+ const expirationInPast = new BigNumber(1496826058); // 7th Jun 2017
+ const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
+ makerTokenAddress,
+ takerTokenAddress,
+ makerAddress,
+ takerAddress,
+ fillableAmount,
+ expirationInPast,
+ );
+ return expect(zeroEx.exchange.validateOrderFillableOrThrowAsync(signedOrder)).to.be.rejectedWith(
+ ExchangeContractErrs.OrderFillExpired,
+ );
+ });
+ });
+ describe('validateFillOrderAndThrowIfInvalidAsync', () => {
+ it('should throw when the fill amount is zero', async () => {
+ const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
+ makerTokenAddress,
+ takerTokenAddress,
+ makerAddress,
+ takerAddress,
+ fillableAmount,
+ );
+ const zeroFillAmount = new BigNumber(0);
+ return expect(
+ zeroEx.exchange.validateFillOrderThrowIfInvalidAsync(signedOrder, zeroFillAmount, takerAddress),
+ ).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero);
+ });
+ it('should throw when the signature is invalid', async () => {
+ const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
+ makerTokenAddress,
+ takerTokenAddress,
+ makerAddress,
+ takerAddress,
+ fillableAmount,
+ );
+ // 27 <--> 28
+ signedOrder.ecSignature.v = 28 - signedOrder.ecSignature.v + 27;
+ return expect(
+ zeroEx.exchange.validateFillOrderThrowIfInvalidAsync(signedOrder, fillableAmount, takerAddress),
+ ).to.be.rejectedWith(ZeroExError.InvalidSignature);
+ });
+ it('should throw when the order is fully filled or cancelled', async () => {
+ const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
+ makerTokenAddress,
+ takerTokenAddress,
+ makerAddress,
+ takerAddress,
+ fillableAmount,
+ );
+ await zeroEx.exchange.cancelOrderAsync(signedOrder, fillableAmount);
+ return expect(
+ zeroEx.exchange.validateFillOrderThrowIfInvalidAsync(signedOrder, fillableAmount, takerAddress),
+ ).to.be.rejectedWith(ExchangeContractErrs.OrderRemainingFillAmountZero);
+ });
+ it('should throw when sender is not a taker', async () => {
+ const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
+ makerTokenAddress,
+ takerTokenAddress,
+ makerAddress,
+ takerAddress,
+ fillableAmount,
+ );
+ const nonTakerAddress = userAddresses[6];
+ return expect(
+ zeroEx.exchange.validateFillOrderThrowIfInvalidAsync(signedOrder, fillTakerAmount, nonTakerAddress),
+ ).to.be.rejectedWith(ExchangeContractErrs.TransactionSenderIsNotFillOrderTaker);
+ });
+ it('should throw when order is expired', async () => {
+ const expirationInPast = new BigNumber(1496826058); // 7th Jun 2017
+ const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
+ makerTokenAddress,
+ takerTokenAddress,
+ makerAddress,
+ takerAddress,
+ fillableAmount,
+ expirationInPast,
+ );
+ return expect(
+ zeroEx.exchange.validateFillOrderThrowIfInvalidAsync(signedOrder, fillTakerAmount, takerAddress),
+ ).to.be.rejectedWith(ExchangeContractErrs.OrderFillExpired);
+ });
+ it('should throw when there a rounding error would have occurred', async () => {
+ const makerAmount = new BigNumber(3);
+ const takerAmount = new BigNumber(5);
+ const signedOrder = await fillScenarios.createAsymmetricFillableSignedOrderAsync(
+ makerTokenAddress,
+ takerTokenAddress,
+ makerAddress,
+ takerAddress,
+ makerAmount,
+ takerAmount,
+ );
+ const fillTakerAmountThatCausesRoundingError = new BigNumber(3);
+ return expect(
+ zeroEx.exchange.validateFillOrderThrowIfInvalidAsync(
+ signedOrder,
+ fillTakerAmountThatCausesRoundingError,
+ takerAddress,
+ ),
+ ).to.be.rejectedWith(ExchangeContractErrs.OrderFillRoundingError);
+ });
+ });
+ describe('#validateFillOrKillOrderAndThrowIfInvalidAsync', () => {
+ it('should throw if remaining fillAmount is less then the desired fillAmount', async () => {
+ const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
+ makerTokenAddress,
+ takerTokenAddress,
+ makerAddress,
+ takerAddress,
+ fillableAmount,
+ );
+ const tooLargeFillAmount = new BigNumber(7);
+ const fillAmountDifference = tooLargeFillAmount.minus(fillableAmount);
+ await zeroEx.token.transferAsync(takerTokenAddress, coinbase, takerAddress, fillAmountDifference);
+ await zeroEx.token.setProxyAllowanceAsync(takerTokenAddress, takerAddress, tooLargeFillAmount);
+ await zeroEx.token.transferAsync(makerTokenAddress, coinbase, makerAddress, fillAmountDifference);
+ await zeroEx.token.setProxyAllowanceAsync(makerTokenAddress, makerAddress, tooLargeFillAmount);
- return expect(
- zeroEx.exchange.validateFillOrKillOrderThrowIfInvalidAsync(
- signedOrder,
- tooLargeFillAmount,
- takerAddress,
- ),
- ).to.be.rejectedWith(ExchangeContractErrs.InsufficientRemainingFillAmount);
- });
- });
- describe('validateCancelOrderAndThrowIfInvalidAsync', () => {
- let signedOrder: SignedOrder;
- const cancelAmount = new BigNumber(3);
- beforeEach(async () => {
- [coinbase, makerAddress, takerAddress] = userAddresses;
- const [makerToken, takerToken] = tokenUtils.getDummyTokens();
- makerTokenAddress = makerToken.address;
- takerTokenAddress = takerToken.address;
- signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- fillableAmount,
- );
- });
- it('should throw when cancel amount is zero', async () => {
- const zeroCancelAmount = new BigNumber(0);
- return expect(
- zeroEx.exchange.validateCancelOrderThrowIfInvalidAsync(signedOrder, zeroCancelAmount),
- ).to.be.rejectedWith(ExchangeContractErrs.OrderCancelAmountZero);
- });
- it('should throw when order is expired', async () => {
- const expirationInPast = new BigNumber(1496826058); // 7th Jun 2017
- const expiredSignedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- fillableAmount,
- expirationInPast,
- );
- return expect(
- zeroEx.exchange.validateCancelOrderThrowIfInvalidAsync(expiredSignedOrder, cancelAmount),
- ).to.be.rejectedWith(ExchangeContractErrs.OrderCancelExpired);
- });
- it('should throw when order is already cancelled or filled', async () => {
- await zeroEx.exchange.cancelOrderAsync(signedOrder, fillableAmount);
- return expect(
- zeroEx.exchange.validateCancelOrderThrowIfInvalidAsync(signedOrder, fillableAmount),
- ).to.be.rejectedWith(ExchangeContractErrs.OrderAlreadyCancelledOrFilled);
- });
- });
- describe('#validateFillOrderBalancesAllowancesThrowIfInvalidAsync', () => {
- let exchangeTransferSimulator: ExchangeTransferSimulator;
- let transferFromAsync: Sinon.SinonSpy;
- const bigNumberMatch = (expected: BigNumber) => {
- return Sinon.match((value: BigNumber) => value.eq(expected));
- };
- beforeEach('create exchangeTransferSimulator', async () => {
- exchangeTransferSimulator = new ExchangeTransferSimulator(zeroEx.token, BlockParamLiteral.Latest);
- transferFromAsync = Sinon.spy();
- exchangeTransferSimulator.transferFromAsync = transferFromAsync as any;
- });
- it('should call exchangeTransferSimulator.transferFrom in a correct order', async () => {
- const makerFee = new BigNumber(2);
- const takerFee = new BigNumber(2);
- const signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerFee,
- takerFee,
- makerAddress,
- takerAddress,
- fillableAmount,
- feeRecipient,
- );
- await OrderValidationUtils.validateFillOrderBalancesAllowancesThrowIfInvalidAsync(
- exchangeTransferSimulator,
- signedOrder,
- fillableAmount,
- takerAddress,
- zrxTokenAddress,
- );
- expect(transferFromAsync.callCount).to.be.equal(4);
- expect(
- transferFromAsync
- .getCall(0)
- .calledWith(
- makerTokenAddress,
- makerAddress,
- takerAddress,
- bigNumberMatch(fillableAmount),
- TradeSide.Maker,
- TransferType.Trade,
- ),
- ).to.be.true();
- expect(
- transferFromAsync
- .getCall(1)
- .calledWith(
- takerTokenAddress,
- takerAddress,
- makerAddress,
- bigNumberMatch(fillableAmount),
- TradeSide.Taker,
- TransferType.Trade,
- ),
- ).to.be.true();
- expect(
- transferFromAsync
- .getCall(2)
- .calledWith(
- zrxTokenAddress,
- makerAddress,
- feeRecipient,
- bigNumberMatch(makerFee),
- TradeSide.Maker,
- TransferType.Fee,
- ),
- ).to.be.true();
- expect(
- transferFromAsync
- .getCall(3)
- .calledWith(
- zrxTokenAddress,
- takerAddress,
- feeRecipient,
- bigNumberMatch(takerFee),
- TradeSide.Taker,
- TransferType.Fee,
- ),
- ).to.be.true();
- });
- it('should call exchangeTransferSimulator.transferFrom with correct values for an open order', async () => {
- const makerFee = new BigNumber(2);
- const takerFee = new BigNumber(2);
- const signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerFee,
- takerFee,
- makerAddress,
- ZeroEx.NULL_ADDRESS,
- fillableAmount,
- feeRecipient,
- );
- await OrderValidationUtils.validateFillOrderBalancesAllowancesThrowIfInvalidAsync(
- exchangeTransferSimulator,
- signedOrder,
- fillableAmount,
- takerAddress,
- zrxTokenAddress,
- );
- expect(transferFromAsync.callCount).to.be.equal(4);
- expect(
- transferFromAsync
- .getCall(0)
- .calledWith(
- makerTokenAddress,
- makerAddress,
- takerAddress,
- bigNumberMatch(fillableAmount),
- TradeSide.Maker,
- TransferType.Trade,
- ),
- ).to.be.true();
- expect(
- transferFromAsync
- .getCall(1)
- .calledWith(
- takerTokenAddress,
- takerAddress,
- makerAddress,
- bigNumberMatch(fillableAmount),
- TradeSide.Taker,
- TransferType.Trade,
- ),
- ).to.be.true();
- expect(
- transferFromAsync
- .getCall(2)
- .calledWith(
- zrxTokenAddress,
- makerAddress,
- feeRecipient,
- bigNumberMatch(makerFee),
- TradeSide.Maker,
- TransferType.Fee,
- ),
- ).to.be.true();
- expect(
- transferFromAsync
- .getCall(3)
- .calledWith(
- zrxTokenAddress,
- takerAddress,
- feeRecipient,
- bigNumberMatch(takerFee),
- TradeSide.Taker,
- TransferType.Fee,
- ),
- ).to.be.true();
- });
- it('should correctly round the fillMakerTokenAmount', async () => {
- const makerTokenAmount = new BigNumber(3);
- const takerTokenAmount = new BigNumber(1);
- const signedOrder = await fillScenarios.createAsymmetricFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- makerTokenAmount,
- takerTokenAmount,
- );
- await OrderValidationUtils.validateFillOrderBalancesAllowancesThrowIfInvalidAsync(
- exchangeTransferSimulator,
- signedOrder,
- takerTokenAmount,
- takerAddress,
- zrxTokenAddress,
- );
- expect(transferFromAsync.callCount).to.be.equal(4);
- const makerFillAmount = transferFromAsync.getCall(0).args[3];
- expect(makerFillAmount).to.be.bignumber.equal(makerTokenAmount);
- });
- it('should correctly round the makerFeeAmount', async () => {
- const makerFee = new BigNumber(2);
- const takerFee = new BigNumber(4);
- const signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerFee,
- takerFee,
- makerAddress,
- takerAddress,
- fillableAmount,
- ZeroEx.NULL_ADDRESS,
- );
- const fillTakerTokenAmount = fillableAmount.div(2).round(0);
- await OrderValidationUtils.validateFillOrderBalancesAllowancesThrowIfInvalidAsync(
- exchangeTransferSimulator,
- signedOrder,
- fillTakerTokenAmount,
- takerAddress,
- zrxTokenAddress,
- );
- const makerPartialFee = makerFee.div(2);
- const takerPartialFee = takerFee.div(2);
- expect(transferFromAsync.callCount).to.be.equal(4);
- const partialMakerFee = transferFromAsync.getCall(2).args[3];
- expect(partialMakerFee).to.be.bignumber.equal(makerPartialFee);
- const partialTakerFee = transferFromAsync.getCall(3).args[3];
- expect(partialTakerFee).to.be.bignumber.equal(takerPartialFee);
- });
- });
+ return expect(
+ zeroEx.exchange.validateFillOrKillOrderThrowIfInvalidAsync(
+ signedOrder,
+ tooLargeFillAmount,
+ takerAddress,
+ ),
+ ).to.be.rejectedWith(ExchangeContractErrs.InsufficientRemainingFillAmount);
+ });
+ });
+ describe('validateCancelOrderAndThrowIfInvalidAsync', () => {
+ let signedOrder: SignedOrder;
+ const cancelAmount = new BigNumber(3);
+ beforeEach(async () => {
+ [coinbase, makerAddress, takerAddress] = userAddresses;
+ const [makerToken, takerToken] = tokenUtils.getDummyTokens();
+ makerTokenAddress = makerToken.address;
+ takerTokenAddress = takerToken.address;
+ signedOrder = await fillScenarios.createFillableSignedOrderAsync(
+ makerTokenAddress,
+ takerTokenAddress,
+ makerAddress,
+ takerAddress,
+ fillableAmount,
+ );
+ });
+ it('should throw when cancel amount is zero', async () => {
+ const zeroCancelAmount = new BigNumber(0);
+ return expect(
+ zeroEx.exchange.validateCancelOrderThrowIfInvalidAsync(signedOrder, zeroCancelAmount),
+ ).to.be.rejectedWith(ExchangeContractErrs.OrderCancelAmountZero);
+ });
+ it('should throw when order is expired', async () => {
+ const expirationInPast = new BigNumber(1496826058); // 7th Jun 2017
+ const expiredSignedOrder = await fillScenarios.createFillableSignedOrderAsync(
+ makerTokenAddress,
+ takerTokenAddress,
+ makerAddress,
+ takerAddress,
+ fillableAmount,
+ expirationInPast,
+ );
+ return expect(
+ zeroEx.exchange.validateCancelOrderThrowIfInvalidAsync(expiredSignedOrder, cancelAmount),
+ ).to.be.rejectedWith(ExchangeContractErrs.OrderCancelExpired);
+ });
+ it('should throw when order is already cancelled or filled', async () => {
+ await zeroEx.exchange.cancelOrderAsync(signedOrder, fillableAmount);
+ return expect(
+ zeroEx.exchange.validateCancelOrderThrowIfInvalidAsync(signedOrder, fillableAmount),
+ ).to.be.rejectedWith(ExchangeContractErrs.OrderAlreadyCancelledOrFilled);
+ });
+ });
+ describe('#validateFillOrderBalancesAllowancesThrowIfInvalidAsync', () => {
+ let exchangeTransferSimulator: ExchangeTransferSimulator;
+ let transferFromAsync: Sinon.SinonSpy;
+ const bigNumberMatch = (expected: BigNumber) => {
+ return Sinon.match((value: BigNumber) => value.eq(expected));
+ };
+ beforeEach('create exchangeTransferSimulator', async () => {
+ exchangeTransferSimulator = new ExchangeTransferSimulator(zeroEx.token, BlockParamLiteral.Latest);
+ transferFromAsync = Sinon.spy();
+ exchangeTransferSimulator.transferFromAsync = transferFromAsync as any;
+ });
+ it('should call exchangeTransferSimulator.transferFrom in a correct order', async () => {
+ const makerFee = new BigNumber(2);
+ const takerFee = new BigNumber(2);
+ const signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync(
+ makerTokenAddress,
+ takerTokenAddress,
+ makerFee,
+ takerFee,
+ makerAddress,
+ takerAddress,
+ fillableAmount,
+ feeRecipient,
+ );
+ await OrderValidationUtils.validateFillOrderBalancesAllowancesThrowIfInvalidAsync(
+ exchangeTransferSimulator,
+ signedOrder,
+ fillableAmount,
+ takerAddress,
+ zrxTokenAddress,
+ );
+ expect(transferFromAsync.callCount).to.be.equal(4);
+ expect(
+ transferFromAsync
+ .getCall(0)
+ .calledWith(
+ makerTokenAddress,
+ makerAddress,
+ takerAddress,
+ bigNumberMatch(fillableAmount),
+ TradeSide.Maker,
+ TransferType.Trade,
+ ),
+ ).to.be.true();
+ expect(
+ transferFromAsync
+ .getCall(1)
+ .calledWith(
+ takerTokenAddress,
+ takerAddress,
+ makerAddress,
+ bigNumberMatch(fillableAmount),
+ TradeSide.Taker,
+ TransferType.Trade,
+ ),
+ ).to.be.true();
+ expect(
+ transferFromAsync
+ .getCall(2)
+ .calledWith(
+ zrxTokenAddress,
+ makerAddress,
+ feeRecipient,
+ bigNumberMatch(makerFee),
+ TradeSide.Maker,
+ TransferType.Fee,
+ ),
+ ).to.be.true();
+ expect(
+ transferFromAsync
+ .getCall(3)
+ .calledWith(
+ zrxTokenAddress,
+ takerAddress,
+ feeRecipient,
+ bigNumberMatch(takerFee),
+ TradeSide.Taker,
+ TransferType.Fee,
+ ),
+ ).to.be.true();
+ });
+ it('should call exchangeTransferSimulator.transferFrom with correct values for an open order', async () => {
+ const makerFee = new BigNumber(2);
+ const takerFee = new BigNumber(2);
+ const signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync(
+ makerTokenAddress,
+ takerTokenAddress,
+ makerFee,
+ takerFee,
+ makerAddress,
+ ZeroEx.NULL_ADDRESS,
+ fillableAmount,
+ feeRecipient,
+ );
+ await OrderValidationUtils.validateFillOrderBalancesAllowancesThrowIfInvalidAsync(
+ exchangeTransferSimulator,
+ signedOrder,
+ fillableAmount,
+ takerAddress,
+ zrxTokenAddress,
+ );
+ expect(transferFromAsync.callCount).to.be.equal(4);
+ expect(
+ transferFromAsync
+ .getCall(0)
+ .calledWith(
+ makerTokenAddress,
+ makerAddress,
+ takerAddress,
+ bigNumberMatch(fillableAmount),
+ TradeSide.Maker,
+ TransferType.Trade,
+ ),
+ ).to.be.true();
+ expect(
+ transferFromAsync
+ .getCall(1)
+ .calledWith(
+ takerTokenAddress,
+ takerAddress,
+ makerAddress,
+ bigNumberMatch(fillableAmount),
+ TradeSide.Taker,
+ TransferType.Trade,
+ ),
+ ).to.be.true();
+ expect(
+ transferFromAsync
+ .getCall(2)
+ .calledWith(
+ zrxTokenAddress,
+ makerAddress,
+ feeRecipient,
+ bigNumberMatch(makerFee),
+ TradeSide.Maker,
+ TransferType.Fee,
+ ),
+ ).to.be.true();
+ expect(
+ transferFromAsync
+ .getCall(3)
+ .calledWith(
+ zrxTokenAddress,
+ takerAddress,
+ feeRecipient,
+ bigNumberMatch(takerFee),
+ TradeSide.Taker,
+ TransferType.Fee,
+ ),
+ ).to.be.true();
+ });
+ it('should correctly round the fillMakerTokenAmount', async () => {
+ const makerTokenAmount = new BigNumber(3);
+ const takerTokenAmount = new BigNumber(1);
+ const signedOrder = await fillScenarios.createAsymmetricFillableSignedOrderAsync(
+ makerTokenAddress,
+ takerTokenAddress,
+ makerAddress,
+ takerAddress,
+ makerTokenAmount,
+ takerTokenAmount,
+ );
+ await OrderValidationUtils.validateFillOrderBalancesAllowancesThrowIfInvalidAsync(
+ exchangeTransferSimulator,
+ signedOrder,
+ takerTokenAmount,
+ takerAddress,
+ zrxTokenAddress,
+ );
+ expect(transferFromAsync.callCount).to.be.equal(4);
+ const makerFillAmount = transferFromAsync.getCall(0).args[3];
+ expect(makerFillAmount).to.be.bignumber.equal(makerTokenAmount);
+ });
+ it('should correctly round the makerFeeAmount', async () => {
+ const makerFee = new BigNumber(2);
+ const takerFee = new BigNumber(4);
+ const signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync(
+ makerTokenAddress,
+ takerTokenAddress,
+ makerFee,
+ takerFee,
+ makerAddress,
+ takerAddress,
+ fillableAmount,
+ ZeroEx.NULL_ADDRESS,
+ );
+ const fillTakerTokenAmount = fillableAmount.div(2).round(0);
+ await OrderValidationUtils.validateFillOrderBalancesAllowancesThrowIfInvalidAsync(
+ exchangeTransferSimulator,
+ signedOrder,
+ fillTakerTokenAmount,
+ takerAddress,
+ zrxTokenAddress,
+ );
+ const makerPartialFee = makerFee.div(2);
+ const takerPartialFee = takerFee.div(2);
+ expect(transferFromAsync.callCount).to.be.equal(4);
+ const partialMakerFee = transferFromAsync.getCall(2).args[3];
+ expect(partialMakerFee).to.be.bignumber.equal(makerPartialFee);
+ const partialTakerFee = transferFromAsync.getCall(3).args[3];
+ expect(partialTakerFee).to.be.bignumber.equal(takerPartialFee);
+ });
+ });
});
diff --git a/packages/0x.js/test/remaining_fillable_calculator_test.ts b/packages/0x.js/test/remaining_fillable_calculator_test.ts
index 58e9dd93b..4c6b8f3ac 100644
--- a/packages/0x.js/test/remaining_fillable_calculator_test.ts
+++ b/packages/0x.js/test/remaining_fillable_calculator_test.ts
@@ -12,223 +12,223 @@ chaiSetup.configure();
const expect = chai.expect;
describe('RemainingFillableCalculator', () => {
- let calculator: RemainingFillableCalculator;
- let signedOrder: SignedOrder;
- let transferrableMakerTokenAmount: BigNumber;
- let transferrableMakerFeeTokenAmount: BigNumber;
- let remainingMakerTokenAmount: BigNumber;
- let makerAmount: BigNumber;
- let takerAmount: BigNumber;
- let makerFeeAmount: BigNumber;
- let isMakerTokenZRX: boolean;
- const makerToken: string = '0x1';
- const takerToken: string = '0x2';
- const decimals: number = 4;
- const zero: BigNumber = new BigNumber(0);
- const zeroAddress = '0x0';
- const signature: ECSignature = { v: 27, r: '', s: '' };
- beforeEach(async () => {
- [makerAmount, takerAmount, makerFeeAmount] = [
- ZeroEx.toBaseUnitAmount(new BigNumber(50), decimals),
- ZeroEx.toBaseUnitAmount(new BigNumber(5), decimals),
- ZeroEx.toBaseUnitAmount(new BigNumber(1), decimals),
- ];
- [transferrableMakerTokenAmount, transferrableMakerFeeTokenAmount] = [
- ZeroEx.toBaseUnitAmount(new BigNumber(50), decimals),
- ZeroEx.toBaseUnitAmount(new BigNumber(5), decimals),
- ];
- });
- function buildSignedOrder(): SignedOrder {
- return {
- ecSignature: signature,
- exchangeContractAddress: zeroAddress,
- feeRecipient: zeroAddress,
- maker: zeroAddress,
- taker: zeroAddress,
- makerFee: makerFeeAmount,
- takerFee: zero,
- makerTokenAmount: makerAmount,
- takerTokenAmount: takerAmount,
- makerTokenAddress: makerToken,
- takerTokenAddress: takerToken,
- salt: zero,
- expirationUnixTimestampSec: zero,
- };
- }
- describe('Maker token is NOT ZRX', () => {
- before(async () => {
- isMakerTokenZRX = false;
- });
- it('calculates the correct amount when unfilled and funds available', () => {
- signedOrder = buildSignedOrder();
- remainingMakerTokenAmount = signedOrder.makerTokenAmount;
- calculator = new RemainingFillableCalculator(
- signedOrder,
- isMakerTokenZRX,
- transferrableMakerTokenAmount,
- transferrableMakerFeeTokenAmount,
- remainingMakerTokenAmount,
- );
- expect(calculator.computeRemainingMakerFillable()).to.be.bignumber.equal(remainingMakerTokenAmount);
- });
- it('calculates the correct amount when partially filled and funds available', () => {
- signedOrder = buildSignedOrder();
- remainingMakerTokenAmount = ZeroEx.toBaseUnitAmount(new BigNumber(1), decimals);
- calculator = new RemainingFillableCalculator(
- signedOrder,
- isMakerTokenZRX,
- transferrableMakerTokenAmount,
- transferrableMakerFeeTokenAmount,
- remainingMakerTokenAmount,
- );
- expect(calculator.computeRemainingMakerFillable()).to.be.bignumber.equal(remainingMakerTokenAmount);
- });
- it('calculates the amount to be 0 when all fee funds are transferred', () => {
- signedOrder = buildSignedOrder();
- transferrableMakerFeeTokenAmount = zero;
- remainingMakerTokenAmount = signedOrder.makerTokenAmount;
- calculator = new RemainingFillableCalculator(
- signedOrder,
- isMakerTokenZRX,
- transferrableMakerTokenAmount,
- transferrableMakerFeeTokenAmount,
- remainingMakerTokenAmount,
- );
- expect(calculator.computeRemainingMakerFillable()).to.be.bignumber.equal(zero);
- });
- it('calculates the correct amount when balance is less than remaining fillable', () => {
- signedOrder = buildSignedOrder();
- const partiallyFilledAmount = ZeroEx.toBaseUnitAmount(new BigNumber(2), decimals);
- remainingMakerTokenAmount = signedOrder.makerTokenAmount.minus(partiallyFilledAmount);
- transferrableMakerTokenAmount = remainingMakerTokenAmount.minus(partiallyFilledAmount);
- calculator = new RemainingFillableCalculator(
- signedOrder,
- isMakerTokenZRX,
- transferrableMakerTokenAmount,
- transferrableMakerFeeTokenAmount,
- remainingMakerTokenAmount,
- );
- expect(calculator.computeRemainingMakerFillable()).to.be.bignumber.equal(transferrableMakerTokenAmount);
- });
- describe('Order to Fee Ratio is < 1', () => {
- beforeEach(async () => {
- [makerAmount, takerAmount, makerFeeAmount] = [
- ZeroEx.toBaseUnitAmount(new BigNumber(3), decimals),
- ZeroEx.toBaseUnitAmount(new BigNumber(6), decimals),
- ZeroEx.toBaseUnitAmount(new BigNumber(6), decimals),
- ];
- });
- it('calculates the correct amount when funds unavailable', () => {
- signedOrder = buildSignedOrder();
- remainingMakerTokenAmount = signedOrder.makerTokenAmount;
- const transferredAmount = ZeroEx.toBaseUnitAmount(new BigNumber(2), decimals);
- transferrableMakerTokenAmount = remainingMakerTokenAmount.minus(transferredAmount);
- calculator = new RemainingFillableCalculator(
- signedOrder,
- isMakerTokenZRX,
- transferrableMakerTokenAmount,
- transferrableMakerFeeTokenAmount,
- remainingMakerTokenAmount,
- );
- expect(calculator.computeRemainingMakerFillable()).to.be.bignumber.equal(transferrableMakerTokenAmount);
- });
- });
- describe('Ratio is not evenly divisble', () => {
- beforeEach(async () => {
- [makerAmount, takerAmount, makerFeeAmount] = [
- ZeroEx.toBaseUnitAmount(new BigNumber(3), decimals),
- ZeroEx.toBaseUnitAmount(new BigNumber(7), decimals),
- ZeroEx.toBaseUnitAmount(new BigNumber(7), decimals),
- ];
- });
- it('calculates the correct amount when funds unavailable', () => {
- signedOrder = buildSignedOrder();
- remainingMakerTokenAmount = signedOrder.makerTokenAmount;
- const transferredAmount = ZeroEx.toBaseUnitAmount(new BigNumber(2), decimals);
- transferrableMakerTokenAmount = remainingMakerTokenAmount.minus(transferredAmount);
- calculator = new RemainingFillableCalculator(
- signedOrder,
- isMakerTokenZRX,
- transferrableMakerTokenAmount,
- transferrableMakerFeeTokenAmount,
- remainingMakerTokenAmount,
- );
- const calculatedFillableAmount = calculator.computeRemainingMakerFillable();
- expect(calculatedFillableAmount.lessThanOrEqualTo(transferrableMakerTokenAmount)).to.be.true();
- expect(calculatedFillableAmount).to.be.bignumber.greaterThan(new BigNumber(0));
- const orderToFeeRatio = signedOrder.makerTokenAmount.dividedBy(signedOrder.makerFee);
- const calculatedFeeAmount = calculatedFillableAmount.dividedBy(orderToFeeRatio);
- expect(calculatedFeeAmount).to.be.bignumber.lessThan(transferrableMakerFeeTokenAmount);
- });
- });
- });
- describe('Maker Token is ZRX', () => {
- before(async () => {
- isMakerTokenZRX = true;
- });
- it('calculates the correct amount when unfilled and funds available', () => {
- signedOrder = buildSignedOrder();
- transferrableMakerTokenAmount = makerAmount.plus(makerFeeAmount);
- transferrableMakerFeeTokenAmount = transferrableMakerTokenAmount;
- remainingMakerTokenAmount = signedOrder.makerTokenAmount;
- calculator = new RemainingFillableCalculator(
- signedOrder,
- isMakerTokenZRX,
- transferrableMakerTokenAmount,
- transferrableMakerFeeTokenAmount,
- remainingMakerTokenAmount,
- );
- expect(calculator.computeRemainingMakerFillable()).to.be.bignumber.equal(remainingMakerTokenAmount);
- });
- it('calculates the correct amount when partially filled and funds available', () => {
- signedOrder = buildSignedOrder();
- remainingMakerTokenAmount = ZeroEx.toBaseUnitAmount(new BigNumber(1), decimals);
- calculator = new RemainingFillableCalculator(
- signedOrder,
- isMakerTokenZRX,
- transferrableMakerTokenAmount,
- transferrableMakerFeeTokenAmount,
- remainingMakerTokenAmount,
- );
- expect(calculator.computeRemainingMakerFillable()).to.be.bignumber.equal(remainingMakerTokenAmount);
- });
- it('calculates the amount to be 0 when all fee funds are transferred', () => {
- signedOrder = buildSignedOrder();
- transferrableMakerTokenAmount = zero;
- transferrableMakerFeeTokenAmount = zero;
- remainingMakerTokenAmount = signedOrder.makerTokenAmount;
- calculator = new RemainingFillableCalculator(
- signedOrder,
- isMakerTokenZRX,
- transferrableMakerTokenAmount,
- transferrableMakerFeeTokenAmount,
- remainingMakerTokenAmount,
- );
- expect(calculator.computeRemainingMakerFillable()).to.be.bignumber.equal(zero);
- });
- it('calculates the correct amount when balance is less than remaining fillable', () => {
- signedOrder = buildSignedOrder();
- const partiallyFilledAmount = ZeroEx.toBaseUnitAmount(new BigNumber(2), decimals);
- remainingMakerTokenAmount = signedOrder.makerTokenAmount.minus(partiallyFilledAmount);
- transferrableMakerTokenAmount = remainingMakerTokenAmount.minus(partiallyFilledAmount);
- transferrableMakerFeeTokenAmount = transferrableMakerTokenAmount;
+ let calculator: RemainingFillableCalculator;
+ let signedOrder: SignedOrder;
+ let transferrableMakerTokenAmount: BigNumber;
+ let transferrableMakerFeeTokenAmount: BigNumber;
+ let remainingMakerTokenAmount: BigNumber;
+ let makerAmount: BigNumber;
+ let takerAmount: BigNumber;
+ let makerFeeAmount: BigNumber;
+ let isMakerTokenZRX: boolean;
+ const makerToken: string = '0x1';
+ const takerToken: string = '0x2';
+ const decimals: number = 4;
+ const zero: BigNumber = new BigNumber(0);
+ const zeroAddress = '0x0';
+ const signature: ECSignature = { v: 27, r: '', s: '' };
+ beforeEach(async () => {
+ [makerAmount, takerAmount, makerFeeAmount] = [
+ ZeroEx.toBaseUnitAmount(new BigNumber(50), decimals),
+ ZeroEx.toBaseUnitAmount(new BigNumber(5), decimals),
+ ZeroEx.toBaseUnitAmount(new BigNumber(1), decimals),
+ ];
+ [transferrableMakerTokenAmount, transferrableMakerFeeTokenAmount] = [
+ ZeroEx.toBaseUnitAmount(new BigNumber(50), decimals),
+ ZeroEx.toBaseUnitAmount(new BigNumber(5), decimals),
+ ];
+ });
+ function buildSignedOrder(): SignedOrder {
+ return {
+ ecSignature: signature,
+ exchangeContractAddress: zeroAddress,
+ feeRecipient: zeroAddress,
+ maker: zeroAddress,
+ taker: zeroAddress,
+ makerFee: makerFeeAmount,
+ takerFee: zero,
+ makerTokenAmount: makerAmount,
+ takerTokenAmount: takerAmount,
+ makerTokenAddress: makerToken,
+ takerTokenAddress: takerToken,
+ salt: zero,
+ expirationUnixTimestampSec: zero,
+ };
+ }
+ describe('Maker token is NOT ZRX', () => {
+ before(async () => {
+ isMakerTokenZRX = false;
+ });
+ it('calculates the correct amount when unfilled and funds available', () => {
+ signedOrder = buildSignedOrder();
+ remainingMakerTokenAmount = signedOrder.makerTokenAmount;
+ calculator = new RemainingFillableCalculator(
+ signedOrder,
+ isMakerTokenZRX,
+ transferrableMakerTokenAmount,
+ transferrableMakerFeeTokenAmount,
+ remainingMakerTokenAmount,
+ );
+ expect(calculator.computeRemainingMakerFillable()).to.be.bignumber.equal(remainingMakerTokenAmount);
+ });
+ it('calculates the correct amount when partially filled and funds available', () => {
+ signedOrder = buildSignedOrder();
+ remainingMakerTokenAmount = ZeroEx.toBaseUnitAmount(new BigNumber(1), decimals);
+ calculator = new RemainingFillableCalculator(
+ signedOrder,
+ isMakerTokenZRX,
+ transferrableMakerTokenAmount,
+ transferrableMakerFeeTokenAmount,
+ remainingMakerTokenAmount,
+ );
+ expect(calculator.computeRemainingMakerFillable()).to.be.bignumber.equal(remainingMakerTokenAmount);
+ });
+ it('calculates the amount to be 0 when all fee funds are transferred', () => {
+ signedOrder = buildSignedOrder();
+ transferrableMakerFeeTokenAmount = zero;
+ remainingMakerTokenAmount = signedOrder.makerTokenAmount;
+ calculator = new RemainingFillableCalculator(
+ signedOrder,
+ isMakerTokenZRX,
+ transferrableMakerTokenAmount,
+ transferrableMakerFeeTokenAmount,
+ remainingMakerTokenAmount,
+ );
+ expect(calculator.computeRemainingMakerFillable()).to.be.bignumber.equal(zero);
+ });
+ it('calculates the correct amount when balance is less than remaining fillable', () => {
+ signedOrder = buildSignedOrder();
+ const partiallyFilledAmount = ZeroEx.toBaseUnitAmount(new BigNumber(2), decimals);
+ remainingMakerTokenAmount = signedOrder.makerTokenAmount.minus(partiallyFilledAmount);
+ transferrableMakerTokenAmount = remainingMakerTokenAmount.minus(partiallyFilledAmount);
+ calculator = new RemainingFillableCalculator(
+ signedOrder,
+ isMakerTokenZRX,
+ transferrableMakerTokenAmount,
+ transferrableMakerFeeTokenAmount,
+ remainingMakerTokenAmount,
+ );
+ expect(calculator.computeRemainingMakerFillable()).to.be.bignumber.equal(transferrableMakerTokenAmount);
+ });
+ describe('Order to Fee Ratio is < 1', () => {
+ beforeEach(async () => {
+ [makerAmount, takerAmount, makerFeeAmount] = [
+ ZeroEx.toBaseUnitAmount(new BigNumber(3), decimals),
+ ZeroEx.toBaseUnitAmount(new BigNumber(6), decimals),
+ ZeroEx.toBaseUnitAmount(new BigNumber(6), decimals),
+ ];
+ });
+ it('calculates the correct amount when funds unavailable', () => {
+ signedOrder = buildSignedOrder();
+ remainingMakerTokenAmount = signedOrder.makerTokenAmount;
+ const transferredAmount = ZeroEx.toBaseUnitAmount(new BigNumber(2), decimals);
+ transferrableMakerTokenAmount = remainingMakerTokenAmount.minus(transferredAmount);
+ calculator = new RemainingFillableCalculator(
+ signedOrder,
+ isMakerTokenZRX,
+ transferrableMakerTokenAmount,
+ transferrableMakerFeeTokenAmount,
+ remainingMakerTokenAmount,
+ );
+ expect(calculator.computeRemainingMakerFillable()).to.be.bignumber.equal(transferrableMakerTokenAmount);
+ });
+ });
+ describe('Ratio is not evenly divisble', () => {
+ beforeEach(async () => {
+ [makerAmount, takerAmount, makerFeeAmount] = [
+ ZeroEx.toBaseUnitAmount(new BigNumber(3), decimals),
+ ZeroEx.toBaseUnitAmount(new BigNumber(7), decimals),
+ ZeroEx.toBaseUnitAmount(new BigNumber(7), decimals),
+ ];
+ });
+ it('calculates the correct amount when funds unavailable', () => {
+ signedOrder = buildSignedOrder();
+ remainingMakerTokenAmount = signedOrder.makerTokenAmount;
+ const transferredAmount = ZeroEx.toBaseUnitAmount(new BigNumber(2), decimals);
+ transferrableMakerTokenAmount = remainingMakerTokenAmount.minus(transferredAmount);
+ calculator = new RemainingFillableCalculator(
+ signedOrder,
+ isMakerTokenZRX,
+ transferrableMakerTokenAmount,
+ transferrableMakerFeeTokenAmount,
+ remainingMakerTokenAmount,
+ );
+ const calculatedFillableAmount = calculator.computeRemainingMakerFillable();
+ expect(calculatedFillableAmount.lessThanOrEqualTo(transferrableMakerTokenAmount)).to.be.true();
+ expect(calculatedFillableAmount).to.be.bignumber.greaterThan(new BigNumber(0));
+ const orderToFeeRatio = signedOrder.makerTokenAmount.dividedBy(signedOrder.makerFee);
+ const calculatedFeeAmount = calculatedFillableAmount.dividedBy(orderToFeeRatio);
+ expect(calculatedFeeAmount).to.be.bignumber.lessThan(transferrableMakerFeeTokenAmount);
+ });
+ });
+ });
+ describe('Maker Token is ZRX', () => {
+ before(async () => {
+ isMakerTokenZRX = true;
+ });
+ it('calculates the correct amount when unfilled and funds available', () => {
+ signedOrder = buildSignedOrder();
+ transferrableMakerTokenAmount = makerAmount.plus(makerFeeAmount);
+ transferrableMakerFeeTokenAmount = transferrableMakerTokenAmount;
+ remainingMakerTokenAmount = signedOrder.makerTokenAmount;
+ calculator = new RemainingFillableCalculator(
+ signedOrder,
+ isMakerTokenZRX,
+ transferrableMakerTokenAmount,
+ transferrableMakerFeeTokenAmount,
+ remainingMakerTokenAmount,
+ );
+ expect(calculator.computeRemainingMakerFillable()).to.be.bignumber.equal(remainingMakerTokenAmount);
+ });
+ it('calculates the correct amount when partially filled and funds available', () => {
+ signedOrder = buildSignedOrder();
+ remainingMakerTokenAmount = ZeroEx.toBaseUnitAmount(new BigNumber(1), decimals);
+ calculator = new RemainingFillableCalculator(
+ signedOrder,
+ isMakerTokenZRX,
+ transferrableMakerTokenAmount,
+ transferrableMakerFeeTokenAmount,
+ remainingMakerTokenAmount,
+ );
+ expect(calculator.computeRemainingMakerFillable()).to.be.bignumber.equal(remainingMakerTokenAmount);
+ });
+ it('calculates the amount to be 0 when all fee funds are transferred', () => {
+ signedOrder = buildSignedOrder();
+ transferrableMakerTokenAmount = zero;
+ transferrableMakerFeeTokenAmount = zero;
+ remainingMakerTokenAmount = signedOrder.makerTokenAmount;
+ calculator = new RemainingFillableCalculator(
+ signedOrder,
+ isMakerTokenZRX,
+ transferrableMakerTokenAmount,
+ transferrableMakerFeeTokenAmount,
+ remainingMakerTokenAmount,
+ );
+ expect(calculator.computeRemainingMakerFillable()).to.be.bignumber.equal(zero);
+ });
+ it('calculates the correct amount when balance is less than remaining fillable', () => {
+ signedOrder = buildSignedOrder();
+ const partiallyFilledAmount = ZeroEx.toBaseUnitAmount(new BigNumber(2), decimals);
+ remainingMakerTokenAmount = signedOrder.makerTokenAmount.minus(partiallyFilledAmount);
+ transferrableMakerTokenAmount = remainingMakerTokenAmount.minus(partiallyFilledAmount);
+ transferrableMakerFeeTokenAmount = transferrableMakerTokenAmount;
- const orderToFeeRatio = signedOrder.makerTokenAmount.dividedToIntegerBy(signedOrder.makerFee);
- const expectedFillableAmount = new BigNumber(450980);
- calculator = new RemainingFillableCalculator(
- signedOrder,
- isMakerTokenZRX,
- transferrableMakerTokenAmount,
- transferrableMakerFeeTokenAmount,
- remainingMakerTokenAmount,
- );
- const calculatedFillableAmount = calculator.computeRemainingMakerFillable();
- const numberOfFillsInRatio = calculatedFillableAmount.dividedToIntegerBy(orderToFeeRatio);
- const calculatedFillableAmountPlusFees = calculatedFillableAmount.plus(numberOfFillsInRatio);
- expect(calculatedFillableAmountPlusFees).to.be.bignumber.lessThan(transferrableMakerTokenAmount);
- expect(calculatedFillableAmountPlusFees).to.be.bignumber.lessThan(remainingMakerTokenAmount);
- expect(calculatedFillableAmount).to.be.bignumber.equal(expectedFillableAmount);
- expect(numberOfFillsInRatio.decimalPlaces()).to.be.equal(0);
- });
- });
+ const orderToFeeRatio = signedOrder.makerTokenAmount.dividedToIntegerBy(signedOrder.makerFee);
+ const expectedFillableAmount = new BigNumber(450980);
+ calculator = new RemainingFillableCalculator(
+ signedOrder,
+ isMakerTokenZRX,
+ transferrableMakerTokenAmount,
+ transferrableMakerFeeTokenAmount,
+ remainingMakerTokenAmount,
+ );
+ const calculatedFillableAmount = calculator.computeRemainingMakerFillable();
+ const numberOfFillsInRatio = calculatedFillableAmount.dividedToIntegerBy(orderToFeeRatio);
+ const calculatedFillableAmountPlusFees = calculatedFillableAmount.plus(numberOfFillsInRatio);
+ expect(calculatedFillableAmountPlusFees).to.be.bignumber.lessThan(transferrableMakerTokenAmount);
+ expect(calculatedFillableAmountPlusFees).to.be.bignumber.lessThan(remainingMakerTokenAmount);
+ expect(calculatedFillableAmount).to.be.bignumber.equal(expectedFillableAmount);
+ expect(numberOfFillsInRatio.decimalPlaces()).to.be.equal(0);
+ });
+ });
});
diff --git a/packages/0x.js/test/subscription_test.ts b/packages/0x.js/test/subscription_test.ts
index f70ecd6cd..f4c6f748f 100644
--- a/packages/0x.js/test/subscription_test.ts
+++ b/packages/0x.js/test/subscription_test.ts
@@ -17,69 +17,69 @@ chaiSetup.configure();
const blockchainLifecycle = new BlockchainLifecycle(constants.RPC_URL);
describe('SubscriptionTest', () => {
- let web3: Web3;
- let zeroEx: ZeroEx;
- let userAddresses: string[];
- let tokens: Token[];
- let coinbase: string;
- let addressWithoutFunds: string;
- const config = {
- networkId: constants.TESTRPC_NETWORK_ID,
- };
- before(async () => {
- web3 = web3Factory.create();
- zeroEx = new ZeroEx(web3.currentProvider, config);
- userAddresses = await zeroEx.getAvailableAddressesAsync();
- tokens = await zeroEx.tokenRegistry.getTokensAsync();
- coinbase = userAddresses[0];
- addressWithoutFunds = userAddresses[1];
- });
- beforeEach(async () => {
- await blockchainLifecycle.startAsync();
- });
- afterEach(async () => {
- await blockchainLifecycle.revertAsync();
- });
- describe('#subscribe', () => {
- const indexFilterValues = {};
- let tokenAddress: string;
- const allowanceAmount = new BigNumber(42);
- let stubs: Sinon.SinonStub[] = [];
- before(() => {
- const token = tokens[0];
- tokenAddress = token.address;
- });
- afterEach(() => {
- zeroEx.token.unsubscribeAll();
- _.each(stubs, s => s.restore());
- stubs = [];
- });
- it('Should receive the Error when an error occurs while fetching the block', (done: DoneCallback) => {
- (async () => {
- const errMsg = 'Error fetching block';
- const callback = assertNodeCallbackError(done, errMsg);
- stubs = [Sinon.stub((zeroEx as any)._web3Wrapper, 'getBlockAsync').throws(new Error(errMsg))];
- zeroEx.token.subscribe(tokenAddress, TokenEvents.Approval, indexFilterValues, callback);
- await zeroEx.token.setAllowanceAsync(tokenAddress, coinbase, addressWithoutFunds, allowanceAmount);
- })().catch(done);
- });
- it('Should receive the Error when an error occurs while reconciling the new block', (done: DoneCallback) => {
- (async () => {
- const errMsg = 'Error fetching logs';
- const callback = assertNodeCallbackError(done, errMsg);
- stubs = [Sinon.stub((zeroEx as any)._web3Wrapper, 'getLogsAsync').throws(new Error(errMsg))];
- zeroEx.token.subscribe(tokenAddress, TokenEvents.Approval, indexFilterValues, callback);
- await zeroEx.token.setAllowanceAsync(tokenAddress, coinbase, addressWithoutFunds, allowanceAmount);
- })().catch(done);
- });
- it('Should allow unsubscribeAll to be called successfully after an error', (done: DoneCallback) => {
- (async () => {
- const callback = (err: Error | null, logEvent?: DecodedLogEvent<ApprovalContractEventArgs>) => _.noop;
- zeroEx.token.subscribe(tokenAddress, TokenEvents.Approval, indexFilterValues, callback);
- stubs = [Sinon.stub((zeroEx as any)._web3Wrapper, 'getBlockAsync').throws(new Error('JSON RPC error'))];
- zeroEx.token.unsubscribeAll();
- done();
- })().catch(done);
- });
- });
+ let web3: Web3;
+ let zeroEx: ZeroEx;
+ let userAddresses: string[];
+ let tokens: Token[];
+ let coinbase: string;
+ let addressWithoutFunds: string;
+ const config = {
+ networkId: constants.TESTRPC_NETWORK_ID,
+ };
+ before(async () => {
+ web3 = web3Factory.create();
+ zeroEx = new ZeroEx(web3.currentProvider, config);
+ userAddresses = await zeroEx.getAvailableAddressesAsync();
+ tokens = await zeroEx.tokenRegistry.getTokensAsync();
+ coinbase = userAddresses[0];
+ addressWithoutFunds = userAddresses[1];
+ });
+ beforeEach(async () => {
+ await blockchainLifecycle.startAsync();
+ });
+ afterEach(async () => {
+ await blockchainLifecycle.revertAsync();
+ });
+ describe('#subscribe', () => {
+ const indexFilterValues = {};
+ let tokenAddress: string;
+ const allowanceAmount = new BigNumber(42);
+ let stubs: Sinon.SinonStub[] = [];
+ before(() => {
+ const token = tokens[0];
+ tokenAddress = token.address;
+ });
+ afterEach(() => {
+ zeroEx.token.unsubscribeAll();
+ _.each(stubs, s => s.restore());
+ stubs = [];
+ });
+ it('Should receive the Error when an error occurs while fetching the block', (done: DoneCallback) => {
+ (async () => {
+ const errMsg = 'Error fetching block';
+ const callback = assertNodeCallbackError(done, errMsg);
+ stubs = [Sinon.stub((zeroEx as any)._web3Wrapper, 'getBlockAsync').throws(new Error(errMsg))];
+ zeroEx.token.subscribe(tokenAddress, TokenEvents.Approval, indexFilterValues, callback);
+ await zeroEx.token.setAllowanceAsync(tokenAddress, coinbase, addressWithoutFunds, allowanceAmount);
+ })().catch(done);
+ });
+ it('Should receive the Error when an error occurs while reconciling the new block', (done: DoneCallback) => {
+ (async () => {
+ const errMsg = 'Error fetching logs';
+ const callback = assertNodeCallbackError(done, errMsg);
+ stubs = [Sinon.stub((zeroEx as any)._web3Wrapper, 'getLogsAsync').throws(new Error(errMsg))];
+ zeroEx.token.subscribe(tokenAddress, TokenEvents.Approval, indexFilterValues, callback);
+ await zeroEx.token.setAllowanceAsync(tokenAddress, coinbase, addressWithoutFunds, allowanceAmount);
+ })().catch(done);
+ });
+ it('Should allow unsubscribeAll to be called successfully after an error', (done: DoneCallback) => {
+ (async () => {
+ const callback = (err: Error | null, logEvent?: DecodedLogEvent<ApprovalContractEventArgs>) => _.noop;
+ zeroEx.token.subscribe(tokenAddress, TokenEvents.Approval, indexFilterValues, callback);
+ stubs = [Sinon.stub((zeroEx as any)._web3Wrapper, 'getBlockAsync').throws(new Error('JSON RPC error'))];
+ zeroEx.token.unsubscribeAll();
+ done();
+ })().catch(done);
+ });
+ });
});
diff --git a/packages/0x.js/test/token_registry_wrapper_test.ts b/packages/0x.js/test/token_registry_wrapper_test.ts
index c798c00d7..0a170db8f 100644
--- a/packages/0x.js/test/token_registry_wrapper_test.ts
+++ b/packages/0x.js/test/token_registry_wrapper_test.ts
@@ -17,113 +17,113 @@ const blockchainLifecycle = new BlockchainLifecycle(constants.RPC_URL);
const TOKEN_REGISTRY_SIZE_AFTER_MIGRATION = 7;
describe('TokenRegistryWrapper', () => {
- let zeroEx: ZeroEx;
- let tokens: Token[];
- const tokenAddressBySymbol: { [symbol: string]: string } = {};
- const tokenAddressByName: { [symbol: string]: string } = {};
- const tokenBySymbol: { [symbol: string]: Token } = {};
- const tokenByName: { [symbol: string]: Token } = {};
- const registeredSymbol = 'ZRX';
- const registeredName = '0x Protocol Token';
- const unregisteredSymbol = 'MAL';
- const unregisteredName = 'Malicious Token';
- const config = {
- networkId: constants.TESTRPC_NETWORK_ID,
- };
- before(async () => {
- const web3 = web3Factory.create();
- zeroEx = new ZeroEx(web3.currentProvider, config);
- tokens = await zeroEx.tokenRegistry.getTokensAsync();
- _.map(tokens, token => {
- tokenAddressBySymbol[token.symbol] = token.address;
- tokenAddressByName[token.name] = token.address;
- tokenBySymbol[token.symbol] = token;
- tokenByName[token.name] = token;
- });
- });
- beforeEach(async () => {
- await blockchainLifecycle.startAsync();
- });
- afterEach(async () => {
- await blockchainLifecycle.revertAsync();
- });
- describe('#getTokensAsync', () => {
- it('should return all the tokens added to the tokenRegistry during the migration', async () => {
- expect(tokens).to.have.lengthOf(TOKEN_REGISTRY_SIZE_AFTER_MIGRATION);
+ let zeroEx: ZeroEx;
+ let tokens: Token[];
+ const tokenAddressBySymbol: { [symbol: string]: string } = {};
+ const tokenAddressByName: { [symbol: string]: string } = {};
+ const tokenBySymbol: { [symbol: string]: Token } = {};
+ const tokenByName: { [symbol: string]: Token } = {};
+ const registeredSymbol = 'ZRX';
+ const registeredName = '0x Protocol Token';
+ const unregisteredSymbol = 'MAL';
+ const unregisteredName = 'Malicious Token';
+ const config = {
+ networkId: constants.TESTRPC_NETWORK_ID,
+ };
+ before(async () => {
+ const web3 = web3Factory.create();
+ zeroEx = new ZeroEx(web3.currentProvider, config);
+ tokens = await zeroEx.tokenRegistry.getTokensAsync();
+ _.map(tokens, token => {
+ tokenAddressBySymbol[token.symbol] = token.address;
+ tokenAddressByName[token.name] = token.address;
+ tokenBySymbol[token.symbol] = token;
+ tokenByName[token.name] = token;
+ });
+ });
+ beforeEach(async () => {
+ await blockchainLifecycle.startAsync();
+ });
+ afterEach(async () => {
+ await blockchainLifecycle.revertAsync();
+ });
+ describe('#getTokensAsync', () => {
+ it('should return all the tokens added to the tokenRegistry during the migration', async () => {
+ expect(tokens).to.have.lengthOf(TOKEN_REGISTRY_SIZE_AFTER_MIGRATION);
- const schemaValidator = new SchemaValidator();
- _.each(tokens, token => {
- const validationResult = schemaValidator.validate(token, schemas.tokenSchema);
- expect(validationResult.errors).to.have.lengthOf(0);
- });
- });
- });
- describe('#getTokenAddressesAsync', () => {
- it('should return all the token addresses added to the tokenRegistry during the migration', async () => {
- const tokenAddresses = await zeroEx.tokenRegistry.getTokenAddressesAsync();
- expect(tokenAddresses).to.have.lengthOf(TOKEN_REGISTRY_SIZE_AFTER_MIGRATION);
+ const schemaValidator = new SchemaValidator();
+ _.each(tokens, token => {
+ const validationResult = schemaValidator.validate(token, schemas.tokenSchema);
+ expect(validationResult.errors).to.have.lengthOf(0);
+ });
+ });
+ });
+ describe('#getTokenAddressesAsync', () => {
+ it('should return all the token addresses added to the tokenRegistry during the migration', async () => {
+ const tokenAddresses = await zeroEx.tokenRegistry.getTokenAddressesAsync();
+ expect(tokenAddresses).to.have.lengthOf(TOKEN_REGISTRY_SIZE_AFTER_MIGRATION);
- const schemaValidator = new SchemaValidator();
- _.each(tokenAddresses, tokenAddress => {
- const validationResult = schemaValidator.validate(tokenAddress, schemas.addressSchema);
- expect(validationResult.errors).to.have.lengthOf(0);
- expect(tokenAddress).to.not.be.equal(ZeroEx.NULL_ADDRESS);
- });
- });
- });
- describe('#getTokenAddressBySymbol', () => {
- it('should return correct address for a token in the registry', async () => {
- const tokenAddress = await zeroEx.tokenRegistry.getTokenAddressBySymbolIfExistsAsync(registeredSymbol);
- expect(tokenAddress).to.be.equal(tokenAddressBySymbol[registeredSymbol]);
- });
- it('should return undefined for a token out of registry', async () => {
- const tokenAddress = await zeroEx.tokenRegistry.getTokenAddressBySymbolIfExistsAsync(unregisteredSymbol);
- expect(tokenAddress).to.be.undefined();
- });
- });
- describe('#getTokenAddressByName', () => {
- it('should return correct address for a token in the registry', async () => {
- const tokenAddress = await zeroEx.tokenRegistry.getTokenAddressByNameIfExistsAsync(registeredName);
- expect(tokenAddress).to.be.equal(tokenAddressByName[registeredName]);
- });
- it('should return undefined for a token out of registry', async () => {
- const tokenAddress = await zeroEx.tokenRegistry.getTokenAddressByNameIfExistsAsync(unregisteredName);
- expect(tokenAddress).to.be.undefined();
- });
- });
- describe('#getTokenBySymbol', () => {
- it('should return correct token for a token in the registry', async () => {
- const token = await zeroEx.tokenRegistry.getTokenBySymbolIfExistsAsync(registeredSymbol);
- expect(token).to.be.deep.equal(tokenBySymbol[registeredSymbol]);
- });
- it('should return undefined for a token out of registry', async () => {
- const token = await zeroEx.tokenRegistry.getTokenBySymbolIfExistsAsync(unregisteredSymbol);
- expect(token).to.be.undefined();
- });
- });
- describe('#getTokenByName', () => {
- it('should return correct token for a token in the registry', async () => {
- const token = await zeroEx.tokenRegistry.getTokenByNameIfExistsAsync(registeredName);
- expect(token).to.be.deep.equal(tokenByName[registeredName]);
- });
- it('should return undefined for a token out of registry', async () => {
- const token = await zeroEx.tokenRegistry.getTokenByNameIfExistsAsync(unregisteredName);
- expect(token).to.be.undefined();
- });
- });
- describe('#getTokenIfExistsAsync', () => {
- it('should return the token added to the tokenRegistry during the migration', async () => {
- const aToken = tokens[0];
+ const schemaValidator = new SchemaValidator();
+ _.each(tokenAddresses, tokenAddress => {
+ const validationResult = schemaValidator.validate(tokenAddress, schemas.addressSchema);
+ expect(validationResult.errors).to.have.lengthOf(0);
+ expect(tokenAddress).to.not.be.equal(ZeroEx.NULL_ADDRESS);
+ });
+ });
+ });
+ describe('#getTokenAddressBySymbol', () => {
+ it('should return correct address for a token in the registry', async () => {
+ const tokenAddress = await zeroEx.tokenRegistry.getTokenAddressBySymbolIfExistsAsync(registeredSymbol);
+ expect(tokenAddress).to.be.equal(tokenAddressBySymbol[registeredSymbol]);
+ });
+ it('should return undefined for a token out of registry', async () => {
+ const tokenAddress = await zeroEx.tokenRegistry.getTokenAddressBySymbolIfExistsAsync(unregisteredSymbol);
+ expect(tokenAddress).to.be.undefined();
+ });
+ });
+ describe('#getTokenAddressByName', () => {
+ it('should return correct address for a token in the registry', async () => {
+ const tokenAddress = await zeroEx.tokenRegistry.getTokenAddressByNameIfExistsAsync(registeredName);
+ expect(tokenAddress).to.be.equal(tokenAddressByName[registeredName]);
+ });
+ it('should return undefined for a token out of registry', async () => {
+ const tokenAddress = await zeroEx.tokenRegistry.getTokenAddressByNameIfExistsAsync(unregisteredName);
+ expect(tokenAddress).to.be.undefined();
+ });
+ });
+ describe('#getTokenBySymbol', () => {
+ it('should return correct token for a token in the registry', async () => {
+ const token = await zeroEx.tokenRegistry.getTokenBySymbolIfExistsAsync(registeredSymbol);
+ expect(token).to.be.deep.equal(tokenBySymbol[registeredSymbol]);
+ });
+ it('should return undefined for a token out of registry', async () => {
+ const token = await zeroEx.tokenRegistry.getTokenBySymbolIfExistsAsync(unregisteredSymbol);
+ expect(token).to.be.undefined();
+ });
+ });
+ describe('#getTokenByName', () => {
+ it('should return correct token for a token in the registry', async () => {
+ const token = await zeroEx.tokenRegistry.getTokenByNameIfExistsAsync(registeredName);
+ expect(token).to.be.deep.equal(tokenByName[registeredName]);
+ });
+ it('should return undefined for a token out of registry', async () => {
+ const token = await zeroEx.tokenRegistry.getTokenByNameIfExistsAsync(unregisteredName);
+ expect(token).to.be.undefined();
+ });
+ });
+ describe('#getTokenIfExistsAsync', () => {
+ it('should return the token added to the tokenRegistry during the migration', async () => {
+ const aToken = tokens[0];
- const token = await zeroEx.tokenRegistry.getTokenIfExistsAsync(aToken.address);
- const schemaValidator = new SchemaValidator();
- const validationResult = schemaValidator.validate(token, schemas.tokenSchema);
- expect(validationResult.errors).to.have.lengthOf(0);
- });
- it('should return return undefined when passed a token address not in the tokenRegistry', async () => {
- const unregisteredTokenAddress = '0x5409ed021d9299bf6814279a6a1411a7e866a631';
- const tokenIfExists = await zeroEx.tokenRegistry.getTokenIfExistsAsync(unregisteredTokenAddress);
- expect(tokenIfExists).to.be.undefined();
- });
- });
+ const token = await zeroEx.tokenRegistry.getTokenIfExistsAsync(aToken.address);
+ const schemaValidator = new SchemaValidator();
+ const validationResult = schemaValidator.validate(token, schemas.tokenSchema);
+ expect(validationResult.errors).to.have.lengthOf(0);
+ });
+ it('should return return undefined when passed a token address not in the tokenRegistry', async () => {
+ const unregisteredTokenAddress = '0x5409ed021d9299bf6814279a6a1411a7e866a631';
+ const tokenIfExists = await zeroEx.tokenRegistry.getTokenIfExistsAsync(unregisteredTokenAddress);
+ expect(tokenIfExists).to.be.undefined();
+ });
+ });
});
diff --git a/packages/0x.js/test/token_transfer_proxy_wrapper_test.ts b/packages/0x.js/test/token_transfer_proxy_wrapper_test.ts
index 622919be2..15bd7a8ba 100644
--- a/packages/0x.js/test/token_transfer_proxy_wrapper_test.ts
+++ b/packages/0x.js/test/token_transfer_proxy_wrapper_test.ts
@@ -10,27 +10,27 @@ chaiSetup.configure();
const expect = chai.expect;
describe('TokenTransferProxyWrapper', () => {
- let zeroEx: ZeroEx;
- const config = {
- networkId: constants.TESTRPC_NETWORK_ID,
- };
- before(async () => {
- const web3 = web3Factory.create();
- zeroEx = new ZeroEx(web3.currentProvider, config);
- });
- describe('#isAuthorizedAsync', () => {
- it('should return false if the address is not authorized', async () => {
- const isAuthorized = await zeroEx.proxy.isAuthorizedAsync(ZeroEx.NULL_ADDRESS);
- expect(isAuthorized).to.be.false();
- });
- });
- describe('#getAuthorizedAddressesAsync', () => {
- it('should return the list of authorized addresses', async () => {
- const authorizedAddresses = await zeroEx.proxy.getAuthorizedAddressesAsync();
- for (const authorizedAddress of authorizedAddresses) {
- const isAuthorized = await zeroEx.proxy.isAuthorizedAsync(authorizedAddress);
- expect(isAuthorized).to.be.true();
- }
- });
- });
+ let zeroEx: ZeroEx;
+ const config = {
+ networkId: constants.TESTRPC_NETWORK_ID,
+ };
+ before(async () => {
+ const web3 = web3Factory.create();
+ zeroEx = new ZeroEx(web3.currentProvider, config);
+ });
+ describe('#isAuthorizedAsync', () => {
+ it('should return false if the address is not authorized', async () => {
+ const isAuthorized = await zeroEx.proxy.isAuthorizedAsync(ZeroEx.NULL_ADDRESS);
+ expect(isAuthorized).to.be.false();
+ });
+ });
+ describe('#getAuthorizedAddressesAsync', () => {
+ it('should return the list of authorized addresses', async () => {
+ const authorizedAddresses = await zeroEx.proxy.getAuthorizedAddressesAsync();
+ for (const authorizedAddress of authorizedAddresses) {
+ const isAuthorized = await zeroEx.proxy.isAuthorizedAsync(authorizedAddress);
+ expect(isAuthorized).to.be.true();
+ }
+ });
+ });
});
diff --git a/packages/0x.js/test/token_wrapper_test.ts b/packages/0x.js/test/token_wrapper_test.ts
index 6919183cb..4ba1f07c5 100644
--- a/packages/0x.js/test/token_wrapper_test.ts
+++ b/packages/0x.js/test/token_wrapper_test.ts
@@ -6,15 +6,15 @@ import 'mocha';
import * as Web3 from 'web3';
import {
- ApprovalContractEventArgs,
- BlockParamLiteral,
- BlockRange,
- DecodedLogEvent,
- Token,
- TokenEvents,
- TransferContractEventArgs,
- ZeroEx,
- ZeroExError,
+ ApprovalContractEventArgs,
+ BlockParamLiteral,
+ BlockRange,
+ DecodedLogEvent,
+ Token,
+ TokenEvents,
+ TransferContractEventArgs,
+ ZeroEx,
+ ZeroExError,
} from '../src';
import { DoneCallback } from '../src/types';
@@ -29,493 +29,493 @@ const expect = chai.expect;
const blockchainLifecycle = new BlockchainLifecycle(constants.RPC_URL);
describe('TokenWrapper', () => {
- let web3: Web3;
- let zeroEx: ZeroEx;
- let userAddresses: string[];
- let tokens: Token[];
- let tokenUtils: TokenUtils;
- let coinbase: string;
- let addressWithoutFunds: string;
- let web3Wrapper: Web3Wrapper;
- const config = {
- networkId: constants.TESTRPC_NETWORK_ID,
- };
- before(async () => {
- web3 = web3Factory.create();
- zeroEx = new ZeroEx(web3.currentProvider, config);
- web3Wrapper = new Web3Wrapper(web3.currentProvider);
- userAddresses = await zeroEx.getAvailableAddressesAsync();
- tokens = await zeroEx.tokenRegistry.getTokensAsync();
- tokenUtils = new TokenUtils(tokens);
- coinbase = userAddresses[0];
- addressWithoutFunds = userAddresses[1];
- });
- beforeEach(async () => {
- await blockchainLifecycle.startAsync();
- });
- afterEach(async () => {
- await blockchainLifecycle.revertAsync();
- });
- describe('#transferAsync', () => {
- let token: Token;
- let transferAmount: BigNumber;
- before(() => {
- token = tokens[0];
- transferAmount = new BigNumber(42);
- });
- it('should successfully transfer tokens', async () => {
- const fromAddress = coinbase;
- const toAddress = addressWithoutFunds;
- const preBalance = await zeroEx.token.getBalanceAsync(token.address, toAddress);
- expect(preBalance).to.be.bignumber.equal(0);
- await zeroEx.token.transferAsync(token.address, fromAddress, toAddress, transferAmount);
- const postBalance = await zeroEx.token.getBalanceAsync(token.address, toAddress);
- return expect(postBalance).to.be.bignumber.equal(transferAmount);
- });
- it('should fail to transfer tokens if fromAddress has an insufficient balance', async () => {
- const fromAddress = addressWithoutFunds;
- const toAddress = coinbase;
- return expect(
- zeroEx.token.transferAsync(token.address, fromAddress, toAddress, transferAmount),
- ).to.be.rejectedWith(ZeroExError.InsufficientBalanceForTransfer);
- });
- it('should throw a CONTRACT_DOES_NOT_EXIST error for a non-existent token contract', async () => {
- const nonExistentTokenAddress = '0x9dd402f14d67e001d8efbe6583e51bf9706aa065';
- const fromAddress = coinbase;
- const toAddress = coinbase;
- return expect(
- zeroEx.token.transferAsync(nonExistentTokenAddress, fromAddress, toAddress, transferAmount),
- ).to.be.rejectedWith(ZeroExError.TokenContractDoesNotExist);
- });
- });
- describe('#transferFromAsync', () => {
- let token: Token;
- let toAddress: string;
- let senderAddress: string;
- before(async () => {
- token = tokens[0];
- toAddress = addressWithoutFunds;
- senderAddress = userAddresses[2];
- });
- it('should fail to transfer tokens if fromAddress has insufficient allowance set', async () => {
- const fromAddress = coinbase;
- const transferAmount = new BigNumber(42);
-
- const fromAddressBalance = await zeroEx.token.getBalanceAsync(token.address, fromAddress);
- expect(fromAddressBalance).to.be.bignumber.greaterThan(transferAmount);
-
- const fromAddressAllowance = await zeroEx.token.getAllowanceAsync(token.address, fromAddress, toAddress);
- expect(fromAddressAllowance).to.be.bignumber.equal(0);
-
- return expect(
- zeroEx.token.transferFromAsync(token.address, fromAddress, toAddress, senderAddress, transferAmount),
- ).to.be.rejectedWith(ZeroExError.InsufficientAllowanceForTransfer);
- });
- it('[regression] should fail to transfer tokens if set allowance for toAddress instead of senderAddress', async () => {
- const fromAddress = coinbase;
- const transferAmount = new BigNumber(42);
-
- await zeroEx.token.setAllowanceAsync(token.address, fromAddress, toAddress, transferAmount);
-
- return expect(
- zeroEx.token.transferFromAsync(token.address, fromAddress, toAddress, senderAddress, transferAmount),
- ).to.be.rejectedWith(ZeroExError.InsufficientAllowanceForTransfer);
- });
- it('should fail to transfer tokens if fromAddress has insufficient balance', async () => {
- const fromAddress = addressWithoutFunds;
- const transferAmount = new BigNumber(42);
-
- const fromAddressBalance = await zeroEx.token.getBalanceAsync(token.address, fromAddress);
- expect(fromAddressBalance).to.be.bignumber.equal(0);
-
- await zeroEx.token.setAllowanceAsync(token.address, fromAddress, senderAddress, transferAmount);
- const fromAddressAllowance = await zeroEx.token.getAllowanceAsync(
- token.address,
- fromAddress,
- senderAddress,
- );
- expect(fromAddressAllowance).to.be.bignumber.equal(transferAmount);
-
- return expect(
- zeroEx.token.transferFromAsync(token.address, fromAddress, toAddress, senderAddress, transferAmount),
- ).to.be.rejectedWith(ZeroExError.InsufficientBalanceForTransfer);
- });
- it('should successfully transfer tokens', async () => {
- const fromAddress = coinbase;
-
- const preBalance = await zeroEx.token.getBalanceAsync(token.address, toAddress);
- expect(preBalance).to.be.bignumber.equal(0);
-
- const transferAmount = new BigNumber(42);
- await zeroEx.token.setAllowanceAsync(token.address, fromAddress, senderAddress, transferAmount);
-
- await zeroEx.token.transferFromAsync(token.address, fromAddress, toAddress, senderAddress, transferAmount);
- const postBalance = await zeroEx.token.getBalanceAsync(token.address, toAddress);
- return expect(postBalance).to.be.bignumber.equal(transferAmount);
- });
- it('should throw a CONTRACT_DOES_NOT_EXIST error for a non-existent token contract', async () => {
- const fromAddress = coinbase;
- const nonExistentTokenAddress = '0x9dd402f14d67e001d8efbe6583e51bf9706aa065';
- return expect(
- zeroEx.token.transferFromAsync(
- nonExistentTokenAddress,
- fromAddress,
- toAddress,
- senderAddress,
- new BigNumber(42),
- ),
- ).to.be.rejectedWith(ZeroExError.TokenContractDoesNotExist);
- });
- });
- describe('#getBalanceAsync', () => {
- describe('With web3 provider with accounts', () => {
- it('should return the balance for an existing ERC20 token', async () => {
- const token = tokens[0];
- const ownerAddress = coinbase;
- const balance = await zeroEx.token.getBalanceAsync(token.address, ownerAddress);
- const expectedBalance = new BigNumber('1000000000000000000000000000');
- return expect(balance).to.be.bignumber.equal(expectedBalance);
- });
- it('should throw a CONTRACT_DOES_NOT_EXIST error for a non-existent token contract', async () => {
- const nonExistentTokenAddress = '0x9dd402f14d67e001d8efbe6583e51bf9706aa065';
- const ownerAddress = coinbase;
- return expect(zeroEx.token.getBalanceAsync(nonExistentTokenAddress, ownerAddress)).to.be.rejectedWith(
- ZeroExError.TokenContractDoesNotExist,
- );
- });
- it('should return a balance of 0 for a non-existent owner address', async () => {
- const token = tokens[0];
- const nonExistentOwner = '0x198c6ad858f213fb31b6fe809e25040e6b964593';
- const balance = await zeroEx.token.getBalanceAsync(token.address, nonExistentOwner);
- const expectedBalance = new BigNumber(0);
- return expect(balance).to.be.bignumber.equal(expectedBalance);
- });
- });
- describe('With web3 provider without accounts', () => {
- let zeroExWithoutAccounts: ZeroEx;
- before(async () => {
- const hasAddresses = false;
- const web3WithoutAccounts = web3Factory.create(hasAddresses);
- zeroExWithoutAccounts = new ZeroEx(web3WithoutAccounts.currentProvider, config);
- });
- it('should return balance even when called with Web3 provider instance without addresses', async () => {
- const token = tokens[0];
- const ownerAddress = coinbase;
- const balance = await zeroExWithoutAccounts.token.getBalanceAsync(token.address, ownerAddress);
- const expectedBalance = new BigNumber('1000000000000000000000000000');
- return expect(balance).to.be.bignumber.equal(expectedBalance);
- });
- });
- });
- describe('#setAllowanceAsync', () => {
- it("should set the spender's allowance", async () => {
- const token = tokens[0];
- const ownerAddress = coinbase;
- const spenderAddress = addressWithoutFunds;
-
- const allowanceBeforeSet = await zeroEx.token.getAllowanceAsync(
- token.address,
- ownerAddress,
- spenderAddress,
- );
- const expectedAllowanceBeforeAllowanceSet = new BigNumber(0);
- expect(allowanceBeforeSet).to.be.bignumber.equal(expectedAllowanceBeforeAllowanceSet);
-
- const amountInBaseUnits = new BigNumber(50);
- await zeroEx.token.setAllowanceAsync(token.address, ownerAddress, spenderAddress, amountInBaseUnits);
-
- const allowanceAfterSet = await zeroEx.token.getAllowanceAsync(token.address, ownerAddress, spenderAddress);
- const expectedAllowanceAfterAllowanceSet = amountInBaseUnits;
- return expect(allowanceAfterSet).to.be.bignumber.equal(expectedAllowanceAfterAllowanceSet);
- });
- });
- describe('#setUnlimitedAllowanceAsync', () => {
- it("should set the unlimited spender's allowance", async () => {
- const token = tokens[0];
- const ownerAddress = coinbase;
- const spenderAddress = addressWithoutFunds;
-
- await zeroEx.token.setUnlimitedAllowanceAsync(token.address, ownerAddress, spenderAddress);
- const allowance = await zeroEx.token.getAllowanceAsync(token.address, ownerAddress, spenderAddress);
- return expect(allowance).to.be.bignumber.equal(zeroEx.token.UNLIMITED_ALLOWANCE_IN_BASE_UNITS);
- });
- it('should reduce the gas cost for transfers including tokens with unlimited allowance support', async () => {
- const transferAmount = new BigNumber(5);
- const zrx = tokenUtils.getProtocolTokenOrThrow();
- const [, userWithNormalAllowance, userWithUnlimitedAllowance] = userAddresses;
- await zeroEx.token.setAllowanceAsync(zrx.address, coinbase, userWithNormalAllowance, transferAmount);
- await zeroEx.token.setUnlimitedAllowanceAsync(zrx.address, coinbase, userWithUnlimitedAllowance);
-
- const initBalanceWithNormalAllowance = await web3Wrapper.getBalanceInWeiAsync(userWithNormalAllowance);
- const initBalanceWithUnlimitedAllowance = await web3Wrapper.getBalanceInWeiAsync(
- userWithUnlimitedAllowance,
- );
-
- await zeroEx.token.transferFromAsync(
- zrx.address,
- coinbase,
- userWithNormalAllowance,
- userWithNormalAllowance,
- transferAmount,
- );
- await zeroEx.token.transferFromAsync(
- zrx.address,
- coinbase,
- userWithUnlimitedAllowance,
- userWithUnlimitedAllowance,
- transferAmount,
- );
-
- const finalBalanceWithNormalAllowance = await web3Wrapper.getBalanceInWeiAsync(userWithNormalAllowance);
- const finalBalanceWithUnlimitedAllowance = await web3Wrapper.getBalanceInWeiAsync(
- userWithUnlimitedAllowance,
- );
-
- const normalGasCost = initBalanceWithNormalAllowance.minus(finalBalanceWithNormalAllowance);
- const unlimitedGasCost = initBalanceWithUnlimitedAllowance.minus(finalBalanceWithUnlimitedAllowance);
-
- // In theory the gas cost with unlimited allowance should be smaller, but with testrpc it's actually bigger.
- // This needs to be investigated in ethereumjs-vm. This test is essentially a repro.
- // TODO: Make this test pass with inverted assertion.
- expect(unlimitedGasCost.toNumber()).to.be.gt(normalGasCost.toNumber());
- });
- });
- describe('#getAllowanceAsync', () => {
- describe('With web3 provider with accounts', () => {
- it('should get the proxy allowance', async () => {
- const token = tokens[0];
- const ownerAddress = coinbase;
- const spenderAddress = addressWithoutFunds;
-
- const amountInBaseUnits = new BigNumber(50);
- await zeroEx.token.setAllowanceAsync(token.address, ownerAddress, spenderAddress, amountInBaseUnits);
-
- const allowance = await zeroEx.token.getAllowanceAsync(token.address, ownerAddress, spenderAddress);
- const expectedAllowance = amountInBaseUnits;
- return expect(allowance).to.be.bignumber.equal(expectedAllowance);
- });
- it('should return 0 if no allowance set yet', async () => {
- const token = tokens[0];
- const ownerAddress = coinbase;
- const spenderAddress = addressWithoutFunds;
- const allowance = await zeroEx.token.getAllowanceAsync(token.address, ownerAddress, spenderAddress);
- const expectedAllowance = new BigNumber(0);
- return expect(allowance).to.be.bignumber.equal(expectedAllowance);
- });
- });
- describe('With web3 provider without accounts', () => {
- let zeroExWithoutAccounts: ZeroEx;
- before(async () => {
- const hasAddresses = false;
- const web3WithoutAccounts = web3Factory.create(hasAddresses);
- zeroExWithoutAccounts = new ZeroEx(web3WithoutAccounts.currentProvider, config);
- });
- it('should get the proxy allowance', async () => {
- const token = tokens[0];
- const ownerAddress = coinbase;
- const spenderAddress = addressWithoutFunds;
-
- const amountInBaseUnits = new BigNumber(50);
- await zeroEx.token.setAllowanceAsync(token.address, ownerAddress, spenderAddress, amountInBaseUnits);
-
- const allowance = await zeroExWithoutAccounts.token.getAllowanceAsync(
- token.address,
- ownerAddress,
- spenderAddress,
- );
- const expectedAllowance = amountInBaseUnits;
- return expect(allowance).to.be.bignumber.equal(expectedAllowance);
- });
- });
- });
- describe('#getProxyAllowanceAsync', () => {
- it('should get the proxy allowance', async () => {
- const token = tokens[0];
- const ownerAddress = coinbase;
-
- const amountInBaseUnits = new BigNumber(50);
- await zeroEx.token.setProxyAllowanceAsync(token.address, ownerAddress, amountInBaseUnits);
-
- const allowance = await zeroEx.token.getProxyAllowanceAsync(token.address, ownerAddress);
- const expectedAllowance = amountInBaseUnits;
- return expect(allowance).to.be.bignumber.equal(expectedAllowance);
- });
- });
- describe('#setProxyAllowanceAsync', () => {
- it('should set the proxy allowance', async () => {
- const token = tokens[0];
- const ownerAddress = coinbase;
-
- const allowanceBeforeSet = await zeroEx.token.getProxyAllowanceAsync(token.address, ownerAddress);
- const expectedAllowanceBeforeAllowanceSet = new BigNumber(0);
- expect(allowanceBeforeSet).to.be.bignumber.equal(expectedAllowanceBeforeAllowanceSet);
-
- const amountInBaseUnits = new BigNumber(50);
- await zeroEx.token.setProxyAllowanceAsync(token.address, ownerAddress, amountInBaseUnits);
-
- const allowanceAfterSet = await zeroEx.token.getProxyAllowanceAsync(token.address, ownerAddress);
- const expectedAllowanceAfterAllowanceSet = amountInBaseUnits;
- return expect(allowanceAfterSet).to.be.bignumber.equal(expectedAllowanceAfterAllowanceSet);
- });
- });
- describe('#setUnlimitedProxyAllowanceAsync', () => {
- it('should set the unlimited proxy allowance', async () => {
- const token = tokens[0];
- const ownerAddress = coinbase;
-
- await zeroEx.token.setUnlimitedProxyAllowanceAsync(token.address, ownerAddress);
- const allowance = await zeroEx.token.getProxyAllowanceAsync(token.address, ownerAddress);
- return expect(allowance).to.be.bignumber.equal(zeroEx.token.UNLIMITED_ALLOWANCE_IN_BASE_UNITS);
- });
- });
- describe('#subscribe', () => {
- const indexFilterValues = {};
- let tokenAddress: string;
- const transferAmount = new BigNumber(42);
- const allowanceAmount = new BigNumber(42);
- before(() => {
- const token = tokens[0];
- tokenAddress = token.address;
- });
- afterEach(() => {
- zeroEx.token.unsubscribeAll();
- });
- // Hack: Mocha does not allow a test to be both async and have a `done` callback
- // Since we need to await the receipt of the event in the `subscribe` callback,
- // we do need both. A hack is to make the top-level a sync fn w/ a done callback and then
- // wrap the rest of the test in an async block
- // Source: https://github.com/mochajs/mocha/issues/2407
- it('Should receive the Transfer event when tokens are transfered', (done: DoneCallback) => {
- (async () => {
- const callback = reportNodeCallbackErrors(done)(
- (logEvent: DecodedLogEvent<TransferContractEventArgs>) => {
- expect(logEvent.isRemoved).to.be.false();
- expect(logEvent.log.logIndex).to.be.equal(0);
- expect(logEvent.log.transactionIndex).to.be.equal(0);
- expect(logEvent.log.blockNumber).to.be.a('number');
- const args = logEvent.log.args;
- expect(args._from).to.be.equal(coinbase);
- expect(args._to).to.be.equal(addressWithoutFunds);
- expect(args._value).to.be.bignumber.equal(transferAmount);
- },
- );
- zeroEx.token.subscribe(tokenAddress, TokenEvents.Transfer, indexFilterValues, callback);
- await zeroEx.token.transferAsync(tokenAddress, coinbase, addressWithoutFunds, transferAmount);
- })().catch(done);
- });
- it('Should receive the Approval event when allowance is being set', (done: DoneCallback) => {
- (async () => {
- const callback = reportNodeCallbackErrors(done)(
- (logEvent: DecodedLogEvent<ApprovalContractEventArgs>) => {
- expect(logEvent).to.not.be.undefined();
- expect(logEvent.isRemoved).to.be.false();
- const args = logEvent.log.args;
- expect(args._owner).to.be.equal(coinbase);
- expect(args._spender).to.be.equal(addressWithoutFunds);
- expect(args._value).to.be.bignumber.equal(allowanceAmount);
- },
- );
- zeroEx.token.subscribe(tokenAddress, TokenEvents.Approval, indexFilterValues, callback);
- await zeroEx.token.setAllowanceAsync(tokenAddress, coinbase, addressWithoutFunds, allowanceAmount);
- })().catch(done);
- });
- it('Outstanding subscriptions are cancelled when zeroEx.setProvider called', (done: DoneCallback) => {
- (async () => {
- const callbackNeverToBeCalled = reportNodeCallbackErrors(done)(
- (logEvent: DecodedLogEvent<ApprovalContractEventArgs>) => {
- done(new Error('Expected this subscription to have been cancelled'));
- },
- );
- zeroEx.token.subscribe(tokenAddress, TokenEvents.Transfer, indexFilterValues, callbackNeverToBeCalled);
- const callbackToBeCalled = reportNodeCallbackErrors(done)();
- const newProvider = web3Factory.getRpcProvider();
- zeroEx.setProvider(newProvider, constants.TESTRPC_NETWORK_ID);
- zeroEx.token.subscribe(tokenAddress, TokenEvents.Transfer, indexFilterValues, callbackToBeCalled);
- await zeroEx.token.transferAsync(tokenAddress, coinbase, addressWithoutFunds, transferAmount);
- })().catch(done);
- });
- it('Should cancel subscription when unsubscribe called', (done: DoneCallback) => {
- (async () => {
- const callbackNeverToBeCalled = reportNodeCallbackErrors(done)(
- (logEvent: DecodedLogEvent<ApprovalContractEventArgs>) => {
- done(new Error('Expected this subscription to have been cancelled'));
- },
- );
- const subscriptionToken = zeroEx.token.subscribe(
- tokenAddress,
- TokenEvents.Transfer,
- indexFilterValues,
- callbackNeverToBeCalled,
- );
- zeroEx.token.unsubscribe(subscriptionToken);
- await zeroEx.token.transferAsync(tokenAddress, coinbase, addressWithoutFunds, transferAmount);
- done();
- })().catch(done);
- });
- });
- describe('#getLogsAsync', () => {
- let tokenAddress: string;
- let tokenTransferProxyAddress: string;
- const blockRange: BlockRange = {
- fromBlock: 0,
- toBlock: BlockParamLiteral.Latest,
- };
- let txHash: string;
- before(() => {
- const token = tokens[0];
- tokenAddress = token.address;
- tokenTransferProxyAddress = zeroEx.proxy.getContractAddress();
- });
- it('should get logs with decoded args emitted by Approval', async () => {
- txHash = await zeroEx.token.setUnlimitedProxyAllowanceAsync(tokenAddress, coinbase);
- await zeroEx.awaitTransactionMinedAsync(txHash);
- const eventName = TokenEvents.Approval;
- const indexFilterValues = {};
- const logs = await zeroEx.token.getLogsAsync<ApprovalContractEventArgs>(
- tokenAddress,
- eventName,
- blockRange,
- indexFilterValues,
- );
- expect(logs).to.have.length(1);
- const args = logs[0].args;
- expect(logs[0].event).to.be.equal(eventName);
- expect(args._owner).to.be.equal(coinbase);
- expect(args._spender).to.be.equal(tokenTransferProxyAddress);
- expect(args._value).to.be.bignumber.equal(zeroEx.token.UNLIMITED_ALLOWANCE_IN_BASE_UNITS);
- });
- it('should only get the logs with the correct event name', async () => {
- txHash = await zeroEx.token.setUnlimitedProxyAllowanceAsync(tokenAddress, coinbase);
- await zeroEx.awaitTransactionMinedAsync(txHash);
- const differentEventName = TokenEvents.Transfer;
- const indexFilterValues = {};
- const logs = await zeroEx.token.getLogsAsync(
- tokenAddress,
- differentEventName,
- blockRange,
- indexFilterValues,
- );
- expect(logs).to.have.length(0);
- });
- it('should only get the logs with the correct indexed fields', async () => {
- txHash = await zeroEx.token.setUnlimitedProxyAllowanceAsync(tokenAddress, coinbase);
- await zeroEx.awaitTransactionMinedAsync(txHash);
- txHash = await zeroEx.token.setUnlimitedProxyAllowanceAsync(tokenAddress, addressWithoutFunds);
- await zeroEx.awaitTransactionMinedAsync(txHash);
- const eventName = TokenEvents.Approval;
- const indexFilterValues = {
- _owner: coinbase,
- };
- const logs = await zeroEx.token.getLogsAsync<ApprovalContractEventArgs>(
- tokenAddress,
- eventName,
- blockRange,
- indexFilterValues,
- );
- expect(logs).to.have.length(1);
- const args = logs[0].args;
- expect(args._owner).to.be.equal(coinbase);
- });
- });
+ let web3: Web3;
+ let zeroEx: ZeroEx;
+ let userAddresses: string[];
+ let tokens: Token[];
+ let tokenUtils: TokenUtils;
+ let coinbase: string;
+ let addressWithoutFunds: string;
+ let web3Wrapper: Web3Wrapper;
+ const config = {
+ networkId: constants.TESTRPC_NETWORK_ID,
+ };
+ before(async () => {
+ web3 = web3Factory.create();
+ zeroEx = new ZeroEx(web3.currentProvider, config);
+ web3Wrapper = new Web3Wrapper(web3.currentProvider);
+ userAddresses = await zeroEx.getAvailableAddressesAsync();
+ tokens = await zeroEx.tokenRegistry.getTokensAsync();
+ tokenUtils = new TokenUtils(tokens);
+ coinbase = userAddresses[0];
+ addressWithoutFunds = userAddresses[1];
+ });
+ beforeEach(async () => {
+ await blockchainLifecycle.startAsync();
+ });
+ afterEach(async () => {
+ await blockchainLifecycle.revertAsync();
+ });
+ describe('#transferAsync', () => {
+ let token: Token;
+ let transferAmount: BigNumber;
+ before(() => {
+ token = tokens[0];
+ transferAmount = new BigNumber(42);
+ });
+ it('should successfully transfer tokens', async () => {
+ const fromAddress = coinbase;
+ const toAddress = addressWithoutFunds;
+ const preBalance = await zeroEx.token.getBalanceAsync(token.address, toAddress);
+ expect(preBalance).to.be.bignumber.equal(0);
+ await zeroEx.token.transferAsync(token.address, fromAddress, toAddress, transferAmount);
+ const postBalance = await zeroEx.token.getBalanceAsync(token.address, toAddress);
+ return expect(postBalance).to.be.bignumber.equal(transferAmount);
+ });
+ it('should fail to transfer tokens if fromAddress has an insufficient balance', async () => {
+ const fromAddress = addressWithoutFunds;
+ const toAddress = coinbase;
+ return expect(
+ zeroEx.token.transferAsync(token.address, fromAddress, toAddress, transferAmount),
+ ).to.be.rejectedWith(ZeroExError.InsufficientBalanceForTransfer);
+ });
+ it('should throw a CONTRACT_DOES_NOT_EXIST error for a non-existent token contract', async () => {
+ const nonExistentTokenAddress = '0x9dd402f14d67e001d8efbe6583e51bf9706aa065';
+ const fromAddress = coinbase;
+ const toAddress = coinbase;
+ return expect(
+ zeroEx.token.transferAsync(nonExistentTokenAddress, fromAddress, toAddress, transferAmount),
+ ).to.be.rejectedWith(ZeroExError.TokenContractDoesNotExist);
+ });
+ });
+ describe('#transferFromAsync', () => {
+ let token: Token;
+ let toAddress: string;
+ let senderAddress: string;
+ before(async () => {
+ token = tokens[0];
+ toAddress = addressWithoutFunds;
+ senderAddress = userAddresses[2];
+ });
+ it('should fail to transfer tokens if fromAddress has insufficient allowance set', async () => {
+ const fromAddress = coinbase;
+ const transferAmount = new BigNumber(42);
+
+ const fromAddressBalance = await zeroEx.token.getBalanceAsync(token.address, fromAddress);
+ expect(fromAddressBalance).to.be.bignumber.greaterThan(transferAmount);
+
+ const fromAddressAllowance = await zeroEx.token.getAllowanceAsync(token.address, fromAddress, toAddress);
+ expect(fromAddressAllowance).to.be.bignumber.equal(0);
+
+ return expect(
+ zeroEx.token.transferFromAsync(token.address, fromAddress, toAddress, senderAddress, transferAmount),
+ ).to.be.rejectedWith(ZeroExError.InsufficientAllowanceForTransfer);
+ });
+ it('[regression] should fail to transfer tokens if set allowance for toAddress instead of senderAddress', async () => {
+ const fromAddress = coinbase;
+ const transferAmount = new BigNumber(42);
+
+ await zeroEx.token.setAllowanceAsync(token.address, fromAddress, toAddress, transferAmount);
+
+ return expect(
+ zeroEx.token.transferFromAsync(token.address, fromAddress, toAddress, senderAddress, transferAmount),
+ ).to.be.rejectedWith(ZeroExError.InsufficientAllowanceForTransfer);
+ });
+ it('should fail to transfer tokens if fromAddress has insufficient balance', async () => {
+ const fromAddress = addressWithoutFunds;
+ const transferAmount = new BigNumber(42);
+
+ const fromAddressBalance = await zeroEx.token.getBalanceAsync(token.address, fromAddress);
+ expect(fromAddressBalance).to.be.bignumber.equal(0);
+
+ await zeroEx.token.setAllowanceAsync(token.address, fromAddress, senderAddress, transferAmount);
+ const fromAddressAllowance = await zeroEx.token.getAllowanceAsync(
+ token.address,
+ fromAddress,
+ senderAddress,
+ );
+ expect(fromAddressAllowance).to.be.bignumber.equal(transferAmount);
+
+ return expect(
+ zeroEx.token.transferFromAsync(token.address, fromAddress, toAddress, senderAddress, transferAmount),
+ ).to.be.rejectedWith(ZeroExError.InsufficientBalanceForTransfer);
+ });
+ it('should successfully transfer tokens', async () => {
+ const fromAddress = coinbase;
+
+ const preBalance = await zeroEx.token.getBalanceAsync(token.address, toAddress);
+ expect(preBalance).to.be.bignumber.equal(0);
+
+ const transferAmount = new BigNumber(42);
+ await zeroEx.token.setAllowanceAsync(token.address, fromAddress, senderAddress, transferAmount);
+
+ await zeroEx.token.transferFromAsync(token.address, fromAddress, toAddress, senderAddress, transferAmount);
+ const postBalance = await zeroEx.token.getBalanceAsync(token.address, toAddress);
+ return expect(postBalance).to.be.bignumber.equal(transferAmount);
+ });
+ it('should throw a CONTRACT_DOES_NOT_EXIST error for a non-existent token contract', async () => {
+ const fromAddress = coinbase;
+ const nonExistentTokenAddress = '0x9dd402f14d67e001d8efbe6583e51bf9706aa065';
+ return expect(
+ zeroEx.token.transferFromAsync(
+ nonExistentTokenAddress,
+ fromAddress,
+ toAddress,
+ senderAddress,
+ new BigNumber(42),
+ ),
+ ).to.be.rejectedWith(ZeroExError.TokenContractDoesNotExist);
+ });
+ });
+ describe('#getBalanceAsync', () => {
+ describe('With web3 provider with accounts', () => {
+ it('should return the balance for an existing ERC20 token', async () => {
+ const token = tokens[0];
+ const ownerAddress = coinbase;
+ const balance = await zeroEx.token.getBalanceAsync(token.address, ownerAddress);
+ const expectedBalance = new BigNumber('1000000000000000000000000000');
+ return expect(balance).to.be.bignumber.equal(expectedBalance);
+ });
+ it('should throw a CONTRACT_DOES_NOT_EXIST error for a non-existent token contract', async () => {
+ const nonExistentTokenAddress = '0x9dd402f14d67e001d8efbe6583e51bf9706aa065';
+ const ownerAddress = coinbase;
+ return expect(zeroEx.token.getBalanceAsync(nonExistentTokenAddress, ownerAddress)).to.be.rejectedWith(
+ ZeroExError.TokenContractDoesNotExist,
+ );
+ });
+ it('should return a balance of 0 for a non-existent owner address', async () => {
+ const token = tokens[0];
+ const nonExistentOwner = '0x198c6ad858f213fb31b6fe809e25040e6b964593';
+ const balance = await zeroEx.token.getBalanceAsync(token.address, nonExistentOwner);
+ const expectedBalance = new BigNumber(0);
+ return expect(balance).to.be.bignumber.equal(expectedBalance);
+ });
+ });
+ describe('With web3 provider without accounts', () => {
+ let zeroExWithoutAccounts: ZeroEx;
+ before(async () => {
+ const hasAddresses = false;
+ const web3WithoutAccounts = web3Factory.create(hasAddresses);
+ zeroExWithoutAccounts = new ZeroEx(web3WithoutAccounts.currentProvider, config);
+ });
+ it('should return balance even when called with Web3 provider instance without addresses', async () => {
+ const token = tokens[0];
+ const ownerAddress = coinbase;
+ const balance = await zeroExWithoutAccounts.token.getBalanceAsync(token.address, ownerAddress);
+ const expectedBalance = new BigNumber('1000000000000000000000000000');
+ return expect(balance).to.be.bignumber.equal(expectedBalance);
+ });
+ });
+ });
+ describe('#setAllowanceAsync', () => {
+ it("should set the spender's allowance", async () => {
+ const token = tokens[0];
+ const ownerAddress = coinbase;
+ const spenderAddress = addressWithoutFunds;
+
+ const allowanceBeforeSet = await zeroEx.token.getAllowanceAsync(
+ token.address,
+ ownerAddress,
+ spenderAddress,
+ );
+ const expectedAllowanceBeforeAllowanceSet = new BigNumber(0);
+ expect(allowanceBeforeSet).to.be.bignumber.equal(expectedAllowanceBeforeAllowanceSet);
+
+ const amountInBaseUnits = new BigNumber(50);
+ await zeroEx.token.setAllowanceAsync(token.address, ownerAddress, spenderAddress, amountInBaseUnits);
+
+ const allowanceAfterSet = await zeroEx.token.getAllowanceAsync(token.address, ownerAddress, spenderAddress);
+ const expectedAllowanceAfterAllowanceSet = amountInBaseUnits;
+ return expect(allowanceAfterSet).to.be.bignumber.equal(expectedAllowanceAfterAllowanceSet);
+ });
+ });
+ describe('#setUnlimitedAllowanceAsync', () => {
+ it("should set the unlimited spender's allowance", async () => {
+ const token = tokens[0];
+ const ownerAddress = coinbase;
+ const spenderAddress = addressWithoutFunds;
+
+ await zeroEx.token.setUnlimitedAllowanceAsync(token.address, ownerAddress, spenderAddress);
+ const allowance = await zeroEx.token.getAllowanceAsync(token.address, ownerAddress, spenderAddress);
+ return expect(allowance).to.be.bignumber.equal(zeroEx.token.UNLIMITED_ALLOWANCE_IN_BASE_UNITS);
+ });
+ it('should reduce the gas cost for transfers including tokens with unlimited allowance support', async () => {
+ const transferAmount = new BigNumber(5);
+ const zrx = tokenUtils.getProtocolTokenOrThrow();
+ const [, userWithNormalAllowance, userWithUnlimitedAllowance] = userAddresses;
+ await zeroEx.token.setAllowanceAsync(zrx.address, coinbase, userWithNormalAllowance, transferAmount);
+ await zeroEx.token.setUnlimitedAllowanceAsync(zrx.address, coinbase, userWithUnlimitedAllowance);
+
+ const initBalanceWithNormalAllowance = await web3Wrapper.getBalanceInWeiAsync(userWithNormalAllowance);
+ const initBalanceWithUnlimitedAllowance = await web3Wrapper.getBalanceInWeiAsync(
+ userWithUnlimitedAllowance,
+ );
+
+ await zeroEx.token.transferFromAsync(
+ zrx.address,
+ coinbase,
+ userWithNormalAllowance,
+ userWithNormalAllowance,
+ transferAmount,
+ );
+ await zeroEx.token.transferFromAsync(
+ zrx.address,
+ coinbase,
+ userWithUnlimitedAllowance,
+ userWithUnlimitedAllowance,
+ transferAmount,
+ );
+
+ const finalBalanceWithNormalAllowance = await web3Wrapper.getBalanceInWeiAsync(userWithNormalAllowance);
+ const finalBalanceWithUnlimitedAllowance = await web3Wrapper.getBalanceInWeiAsync(
+ userWithUnlimitedAllowance,
+ );
+
+ const normalGasCost = initBalanceWithNormalAllowance.minus(finalBalanceWithNormalAllowance);
+ const unlimitedGasCost = initBalanceWithUnlimitedAllowance.minus(finalBalanceWithUnlimitedAllowance);
+
+ // In theory the gas cost with unlimited allowance should be smaller, but with testrpc it's actually bigger.
+ // This needs to be investigated in ethereumjs-vm. This test is essentially a repro.
+ // TODO: Make this test pass with inverted assertion.
+ expect(unlimitedGasCost.toNumber()).to.be.gt(normalGasCost.toNumber());
+ });
+ });
+ describe('#getAllowanceAsync', () => {
+ describe('With web3 provider with accounts', () => {
+ it('should get the proxy allowance', async () => {
+ const token = tokens[0];
+ const ownerAddress = coinbase;
+ const spenderAddress = addressWithoutFunds;
+
+ const amountInBaseUnits = new BigNumber(50);
+ await zeroEx.token.setAllowanceAsync(token.address, ownerAddress, spenderAddress, amountInBaseUnits);
+
+ const allowance = await zeroEx.token.getAllowanceAsync(token.address, ownerAddress, spenderAddress);
+ const expectedAllowance = amountInBaseUnits;
+ return expect(allowance).to.be.bignumber.equal(expectedAllowance);
+ });
+ it('should return 0 if no allowance set yet', async () => {
+ const token = tokens[0];
+ const ownerAddress = coinbase;
+ const spenderAddress = addressWithoutFunds;
+ const allowance = await zeroEx.token.getAllowanceAsync(token.address, ownerAddress, spenderAddress);
+ const expectedAllowance = new BigNumber(0);
+ return expect(allowance).to.be.bignumber.equal(expectedAllowance);
+ });
+ });
+ describe('With web3 provider without accounts', () => {
+ let zeroExWithoutAccounts: ZeroEx;
+ before(async () => {
+ const hasAddresses = false;
+ const web3WithoutAccounts = web3Factory.create(hasAddresses);
+ zeroExWithoutAccounts = new ZeroEx(web3WithoutAccounts.currentProvider, config);
+ });
+ it('should get the proxy allowance', async () => {
+ const token = tokens[0];
+ const ownerAddress = coinbase;
+ const spenderAddress = addressWithoutFunds;
+
+ const amountInBaseUnits = new BigNumber(50);
+ await zeroEx.token.setAllowanceAsync(token.address, ownerAddress, spenderAddress, amountInBaseUnits);
+
+ const allowance = await zeroExWithoutAccounts.token.getAllowanceAsync(
+ token.address,
+ ownerAddress,
+ spenderAddress,
+ );
+ const expectedAllowance = amountInBaseUnits;
+ return expect(allowance).to.be.bignumber.equal(expectedAllowance);
+ });
+ });
+ });
+ describe('#getProxyAllowanceAsync', () => {
+ it('should get the proxy allowance', async () => {
+ const token = tokens[0];
+ const ownerAddress = coinbase;
+
+ const amountInBaseUnits = new BigNumber(50);
+ await zeroEx.token.setProxyAllowanceAsync(token.address, ownerAddress, amountInBaseUnits);
+
+ const allowance = await zeroEx.token.getProxyAllowanceAsync(token.address, ownerAddress);
+ const expectedAllowance = amountInBaseUnits;
+ return expect(allowance).to.be.bignumber.equal(expectedAllowance);
+ });
+ });
+ describe('#setProxyAllowanceAsync', () => {
+ it('should set the proxy allowance', async () => {
+ const token = tokens[0];
+ const ownerAddress = coinbase;
+
+ const allowanceBeforeSet = await zeroEx.token.getProxyAllowanceAsync(token.address, ownerAddress);
+ const expectedAllowanceBeforeAllowanceSet = new BigNumber(0);
+ expect(allowanceBeforeSet).to.be.bignumber.equal(expectedAllowanceBeforeAllowanceSet);
+
+ const amountInBaseUnits = new BigNumber(50);
+ await zeroEx.token.setProxyAllowanceAsync(token.address, ownerAddress, amountInBaseUnits);
+
+ const allowanceAfterSet = await zeroEx.token.getProxyAllowanceAsync(token.address, ownerAddress);
+ const expectedAllowanceAfterAllowanceSet = amountInBaseUnits;
+ return expect(allowanceAfterSet).to.be.bignumber.equal(expectedAllowanceAfterAllowanceSet);
+ });
+ });
+ describe('#setUnlimitedProxyAllowanceAsync', () => {
+ it('should set the unlimited proxy allowance', async () => {
+ const token = tokens[0];
+ const ownerAddress = coinbase;
+
+ await zeroEx.token.setUnlimitedProxyAllowanceAsync(token.address, ownerAddress);
+ const allowance = await zeroEx.token.getProxyAllowanceAsync(token.address, ownerAddress);
+ return expect(allowance).to.be.bignumber.equal(zeroEx.token.UNLIMITED_ALLOWANCE_IN_BASE_UNITS);
+ });
+ });
+ describe('#subscribe', () => {
+ const indexFilterValues = {};
+ let tokenAddress: string;
+ const transferAmount = new BigNumber(42);
+ const allowanceAmount = new BigNumber(42);
+ before(() => {
+ const token = tokens[0];
+ tokenAddress = token.address;
+ });
+ afterEach(() => {
+ zeroEx.token.unsubscribeAll();
+ });
+ // Hack: Mocha does not allow a test to be both async and have a `done` callback
+ // Since we need to await the receipt of the event in the `subscribe` callback,
+ // we do need both. A hack is to make the top-level a sync fn w/ a done callback and then
+ // wrap the rest of the test in an async block
+ // Source: https://github.com/mochajs/mocha/issues/2407
+ it('Should receive the Transfer event when tokens are transfered', (done: DoneCallback) => {
+ (async () => {
+ const callback = reportNodeCallbackErrors(done)(
+ (logEvent: DecodedLogEvent<TransferContractEventArgs>) => {
+ expect(logEvent.isRemoved).to.be.false();
+ expect(logEvent.log.logIndex).to.be.equal(0);
+ expect(logEvent.log.transactionIndex).to.be.equal(0);
+ expect(logEvent.log.blockNumber).to.be.a('number');
+ const args = logEvent.log.args;
+ expect(args._from).to.be.equal(coinbase);
+ expect(args._to).to.be.equal(addressWithoutFunds);
+ expect(args._value).to.be.bignumber.equal(transferAmount);
+ },
+ );
+ zeroEx.token.subscribe(tokenAddress, TokenEvents.Transfer, indexFilterValues, callback);
+ await zeroEx.token.transferAsync(tokenAddress, coinbase, addressWithoutFunds, transferAmount);
+ })().catch(done);
+ });
+ it('Should receive the Approval event when allowance is being set', (done: DoneCallback) => {
+ (async () => {
+ const callback = reportNodeCallbackErrors(done)(
+ (logEvent: DecodedLogEvent<ApprovalContractEventArgs>) => {
+ expect(logEvent).to.not.be.undefined();
+ expect(logEvent.isRemoved).to.be.false();
+ const args = logEvent.log.args;
+ expect(args._owner).to.be.equal(coinbase);
+ expect(args._spender).to.be.equal(addressWithoutFunds);
+ expect(args._value).to.be.bignumber.equal(allowanceAmount);
+ },
+ );
+ zeroEx.token.subscribe(tokenAddress, TokenEvents.Approval, indexFilterValues, callback);
+ await zeroEx.token.setAllowanceAsync(tokenAddress, coinbase, addressWithoutFunds, allowanceAmount);
+ })().catch(done);
+ });
+ it('Outstanding subscriptions are cancelled when zeroEx.setProvider called', (done: DoneCallback) => {
+ (async () => {
+ const callbackNeverToBeCalled = reportNodeCallbackErrors(done)(
+ (logEvent: DecodedLogEvent<ApprovalContractEventArgs>) => {
+ done(new Error('Expected this subscription to have been cancelled'));
+ },
+ );
+ zeroEx.token.subscribe(tokenAddress, TokenEvents.Transfer, indexFilterValues, callbackNeverToBeCalled);
+ const callbackToBeCalled = reportNodeCallbackErrors(done)();
+ const newProvider = web3Factory.getRpcProvider();
+ zeroEx.setProvider(newProvider, constants.TESTRPC_NETWORK_ID);
+ zeroEx.token.subscribe(tokenAddress, TokenEvents.Transfer, indexFilterValues, callbackToBeCalled);
+ await zeroEx.token.transferAsync(tokenAddress, coinbase, addressWithoutFunds, transferAmount);
+ })().catch(done);
+ });
+ it('Should cancel subscription when unsubscribe called', (done: DoneCallback) => {
+ (async () => {
+ const callbackNeverToBeCalled = reportNodeCallbackErrors(done)(
+ (logEvent: DecodedLogEvent<ApprovalContractEventArgs>) => {
+ done(new Error('Expected this subscription to have been cancelled'));
+ },
+ );
+ const subscriptionToken = zeroEx.token.subscribe(
+ tokenAddress,
+ TokenEvents.Transfer,
+ indexFilterValues,
+ callbackNeverToBeCalled,
+ );
+ zeroEx.token.unsubscribe(subscriptionToken);
+ await zeroEx.token.transferAsync(tokenAddress, coinbase, addressWithoutFunds, transferAmount);
+ done();
+ })().catch(done);
+ });
+ });
+ describe('#getLogsAsync', () => {
+ let tokenAddress: string;
+ let tokenTransferProxyAddress: string;
+ const blockRange: BlockRange = {
+ fromBlock: 0,
+ toBlock: BlockParamLiteral.Latest,
+ };
+ let txHash: string;
+ before(() => {
+ const token = tokens[0];
+ tokenAddress = token.address;
+ tokenTransferProxyAddress = zeroEx.proxy.getContractAddress();
+ });
+ it('should get logs with decoded args emitted by Approval', async () => {
+ txHash = await zeroEx.token.setUnlimitedProxyAllowanceAsync(tokenAddress, coinbase);
+ await zeroEx.awaitTransactionMinedAsync(txHash);
+ const eventName = TokenEvents.Approval;
+ const indexFilterValues = {};
+ const logs = await zeroEx.token.getLogsAsync<ApprovalContractEventArgs>(
+ tokenAddress,
+ eventName,
+ blockRange,
+ indexFilterValues,
+ );
+ expect(logs).to.have.length(1);
+ const args = logs[0].args;
+ expect(logs[0].event).to.be.equal(eventName);
+ expect(args._owner).to.be.equal(coinbase);
+ expect(args._spender).to.be.equal(tokenTransferProxyAddress);
+ expect(args._value).to.be.bignumber.equal(zeroEx.token.UNLIMITED_ALLOWANCE_IN_BASE_UNITS);
+ });
+ it('should only get the logs with the correct event name', async () => {
+ txHash = await zeroEx.token.setUnlimitedProxyAllowanceAsync(tokenAddress, coinbase);
+ await zeroEx.awaitTransactionMinedAsync(txHash);
+ const differentEventName = TokenEvents.Transfer;
+ const indexFilterValues = {};
+ const logs = await zeroEx.token.getLogsAsync(
+ tokenAddress,
+ differentEventName,
+ blockRange,
+ indexFilterValues,
+ );
+ expect(logs).to.have.length(0);
+ });
+ it('should only get the logs with the correct indexed fields', async () => {
+ txHash = await zeroEx.token.setUnlimitedProxyAllowanceAsync(tokenAddress, coinbase);
+ await zeroEx.awaitTransactionMinedAsync(txHash);
+ txHash = await zeroEx.token.setUnlimitedProxyAllowanceAsync(tokenAddress, addressWithoutFunds);
+ await zeroEx.awaitTransactionMinedAsync(txHash);
+ const eventName = TokenEvents.Approval;
+ const indexFilterValues = {
+ _owner: coinbase,
+ };
+ const logs = await zeroEx.token.getLogsAsync<ApprovalContractEventArgs>(
+ tokenAddress,
+ eventName,
+ blockRange,
+ indexFilterValues,
+ );
+ expect(logs).to.have.length(1);
+ const args = logs[0].args;
+ expect(args._owner).to.be.equal(coinbase);
+ });
+ });
});
// tslint:disable:max-file-line-count
diff --git a/packages/0x.js/test/utils/chai_setup.ts b/packages/0x.js/test/utils/chai_setup.ts
index e156b5f7c..078edd309 100644
--- a/packages/0x.js/test/utils/chai_setup.ts
+++ b/packages/0x.js/test/utils/chai_setup.ts
@@ -4,10 +4,10 @@ import ChaiBigNumber = require('chai-bignumber');
import * as dirtyChai from 'dirty-chai';
export const chaiSetup = {
- configure() {
- chai.config.includeStack = true;
- chai.use(ChaiBigNumber());
- chai.use(dirtyChai);
- chai.use(chaiAsPromised);
- },
+ configure() {
+ chai.config.includeStack = true;
+ chai.use(ChaiBigNumber());
+ chai.use(dirtyChai);
+ chai.use(chaiAsPromised);
+ },
};
diff --git a/packages/0x.js/test/utils/constants.ts b/packages/0x.js/test/utils/constants.ts
index b7161e9ab..a9e665c25 100644
--- a/packages/0x.js/test/utils/constants.ts
+++ b/packages/0x.js/test/utils/constants.ts
@@ -1,11 +1,11 @@
export const constants = {
- NULL_ADDRESS: '0x0000000000000000000000000000000000000000',
- RPC_URL: 'http://localhost:8545',
- ROPSTEN_NETWORK_ID: 3,
- KOVAN_NETWORK_ID: 42,
- TESTRPC_NETWORK_ID: 50,
- KOVAN_RPC_URL: 'https://kovan.infura.io/',
- ROPSTEN_RPC_URL: 'https://ropsten.infura.io/',
- ZRX_DECIMALS: 18,
- GAS_ESTIMATE: 500000,
+ NULL_ADDRESS: '0x0000000000000000000000000000000000000000',
+ RPC_URL: 'http://localhost:8545',
+ ROPSTEN_NETWORK_ID: 3,
+ KOVAN_NETWORK_ID: 42,
+ TESTRPC_NETWORK_ID: 50,
+ KOVAN_RPC_URL: 'https://kovan.infura.io/',
+ ROPSTEN_RPC_URL: 'https://ropsten.infura.io/',
+ ZRX_DECIMALS: 18,
+ GAS_ESTIMATE: 500000,
};
diff --git a/packages/0x.js/test/utils/fill_scenarios.ts b/packages/0x.js/test/utils/fill_scenarios.ts
index 1d4b8b6f4..1a61487f4 100644
--- a/packages/0x.js/test/utils/fill_scenarios.ts
+++ b/packages/0x.js/test/utils/fill_scenarios.ts
@@ -11,192 +11,192 @@ import { constants } from './constants';
const INITIAL_COINBASE_TOKEN_SUPPLY_IN_UNITS = new BigNumber(100);
export class FillScenarios {
- private _zeroEx: ZeroEx;
- private _userAddresses: string[];
- private _tokens: Token[];
- private _coinbase: string;
- private _zrxTokenAddress: string;
- private _exchangeContractAddress: string;
- constructor(
- zeroEx: ZeroEx,
- userAddresses: string[],
- tokens: Token[],
- zrxTokenAddress: string,
- exchangeContractAddress: string,
- ) {
- this._zeroEx = zeroEx;
- this._userAddresses = userAddresses;
- this._tokens = tokens;
- this._coinbase = userAddresses[0];
- this._zrxTokenAddress = zrxTokenAddress;
- this._exchangeContractAddress = exchangeContractAddress;
- }
- public async initTokenBalancesAsync() {
- const web3Wrapper = (this._zeroEx as any)._web3Wrapper as Web3Wrapper;
- for (const token of this._tokens) {
- if (token.symbol !== 'ZRX' && token.symbol !== 'WETH') {
- const contractInstance = web3Wrapper.getContractInstance(
- artifacts.DummyTokenArtifact.abi,
- token.address,
- );
- const defaults = {};
- const dummyToken = new DummyTokenContract(contractInstance, defaults);
- const tokenSupply = ZeroEx.toBaseUnitAmount(INITIAL_COINBASE_TOKEN_SUPPLY_IN_UNITS, token.decimals);
- const txHash = await dummyToken.setBalance.sendTransactionAsync(this._coinbase, tokenSupply, {
- from: this._coinbase,
- });
- await this._zeroEx.awaitTransactionMinedAsync(txHash);
- }
- }
- }
- public async createFillableSignedOrderAsync(
- makerTokenAddress: string,
- takerTokenAddress: string,
- makerAddress: string,
- takerAddress: string,
- fillableAmount: BigNumber,
- expirationUnixTimestampSec?: BigNumber,
- ): Promise<SignedOrder> {
- return this.createAsymmetricFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- fillableAmount,
- fillableAmount,
- expirationUnixTimestampSec,
- );
- }
- public async createFillableSignedOrderWithFeesAsync(
- makerTokenAddress: string,
- takerTokenAddress: string,
- makerFee: BigNumber,
- takerFee: BigNumber,
- makerAddress: string,
- takerAddress: string,
- fillableAmount: BigNumber,
- feeRecepient: string,
- expirationUnixTimestampSec?: BigNumber,
- ): Promise<SignedOrder> {
- return this._createAsymmetricFillableSignedOrderWithFeesAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerFee,
- takerFee,
- makerAddress,
- takerAddress,
- fillableAmount,
- fillableAmount,
- feeRecepient,
- expirationUnixTimestampSec,
- );
- }
- public async createAsymmetricFillableSignedOrderAsync(
- makerTokenAddress: string,
- takerTokenAddress: string,
- makerAddress: string,
- takerAddress: string,
- makerFillableAmount: BigNumber,
- takerFillableAmount: BigNumber,
- expirationUnixTimestampSec?: BigNumber,
- ): Promise<SignedOrder> {
- const makerFee = new BigNumber(0);
- const takerFee = new BigNumber(0);
- const feeRecepient = constants.NULL_ADDRESS;
- return this._createAsymmetricFillableSignedOrderWithFeesAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerFee,
- takerFee,
- makerAddress,
- takerAddress,
- makerFillableAmount,
- takerFillableAmount,
- feeRecepient,
- expirationUnixTimestampSec,
- );
- }
- public async createPartiallyFilledSignedOrderAsync(
- makerTokenAddress: string,
- takerTokenAddress: string,
- takerAddress: string,
- fillableAmount: BigNumber,
- partialFillAmount: BigNumber,
- ) {
- const [makerAddress] = this._userAddresses;
- const signedOrder = await this.createAsymmetricFillableSignedOrderAsync(
- makerTokenAddress,
- takerTokenAddress,
- makerAddress,
- takerAddress,
- fillableAmount,
- fillableAmount,
- );
- const shouldThrowOnInsufficientBalanceOrAllowance = false;
- await this._zeroEx.exchange.fillOrderAsync(
- signedOrder,
- partialFillAmount,
- shouldThrowOnInsufficientBalanceOrAllowance,
- takerAddress,
- );
- return signedOrder;
- }
- private async _createAsymmetricFillableSignedOrderWithFeesAsync(
- makerTokenAddress: string,
- takerTokenAddress: string,
- makerFee: BigNumber,
- takerFee: BigNumber,
- makerAddress: string,
- takerAddress: string,
- makerFillableAmount: BigNumber,
- takerFillableAmount: BigNumber,
- feeRecepient: string,
- expirationUnixTimestampSec?: BigNumber,
- ): Promise<SignedOrder> {
- await Promise.all([
- this._increaseBalanceAndAllowanceAsync(makerTokenAddress, makerAddress, makerFillableAmount),
- this._increaseBalanceAndAllowanceAsync(takerTokenAddress, takerAddress, takerFillableAmount),
- ]);
- await Promise.all([
- this._increaseBalanceAndAllowanceAsync(this._zrxTokenAddress, makerAddress, makerFee),
- this._increaseBalanceAndAllowanceAsync(this._zrxTokenAddress, takerAddress, takerFee),
- ]);
+ private _zeroEx: ZeroEx;
+ private _userAddresses: string[];
+ private _tokens: Token[];
+ private _coinbase: string;
+ private _zrxTokenAddress: string;
+ private _exchangeContractAddress: string;
+ constructor(
+ zeroEx: ZeroEx,
+ userAddresses: string[],
+ tokens: Token[],
+ zrxTokenAddress: string,
+ exchangeContractAddress: string,
+ ) {
+ this._zeroEx = zeroEx;
+ this._userAddresses = userAddresses;
+ this._tokens = tokens;
+ this._coinbase = userAddresses[0];
+ this._zrxTokenAddress = zrxTokenAddress;
+ this._exchangeContractAddress = exchangeContractAddress;
+ }
+ public async initTokenBalancesAsync() {
+ const web3Wrapper = (this._zeroEx as any)._web3Wrapper as Web3Wrapper;
+ for (const token of this._tokens) {
+ if (token.symbol !== 'ZRX' && token.symbol !== 'WETH') {
+ const contractInstance = web3Wrapper.getContractInstance(
+ artifacts.DummyTokenArtifact.abi,
+ token.address,
+ );
+ const defaults = {};
+ const dummyToken = new DummyTokenContract(contractInstance, defaults);
+ const tokenSupply = ZeroEx.toBaseUnitAmount(INITIAL_COINBASE_TOKEN_SUPPLY_IN_UNITS, token.decimals);
+ const txHash = await dummyToken.setBalance.sendTransactionAsync(this._coinbase, tokenSupply, {
+ from: this._coinbase,
+ });
+ await this._zeroEx.awaitTransactionMinedAsync(txHash);
+ }
+ }
+ }
+ public async createFillableSignedOrderAsync(
+ makerTokenAddress: string,
+ takerTokenAddress: string,
+ makerAddress: string,
+ takerAddress: string,
+ fillableAmount: BigNumber,
+ expirationUnixTimestampSec?: BigNumber,
+ ): Promise<SignedOrder> {
+ return this.createAsymmetricFillableSignedOrderAsync(
+ makerTokenAddress,
+ takerTokenAddress,
+ makerAddress,
+ takerAddress,
+ fillableAmount,
+ fillableAmount,
+ expirationUnixTimestampSec,
+ );
+ }
+ public async createFillableSignedOrderWithFeesAsync(
+ makerTokenAddress: string,
+ takerTokenAddress: string,
+ makerFee: BigNumber,
+ takerFee: BigNumber,
+ makerAddress: string,
+ takerAddress: string,
+ fillableAmount: BigNumber,
+ feeRecepient: string,
+ expirationUnixTimestampSec?: BigNumber,
+ ): Promise<SignedOrder> {
+ return this._createAsymmetricFillableSignedOrderWithFeesAsync(
+ makerTokenAddress,
+ takerTokenAddress,
+ makerFee,
+ takerFee,
+ makerAddress,
+ takerAddress,
+ fillableAmount,
+ fillableAmount,
+ feeRecepient,
+ expirationUnixTimestampSec,
+ );
+ }
+ public async createAsymmetricFillableSignedOrderAsync(
+ makerTokenAddress: string,
+ takerTokenAddress: string,
+ makerAddress: string,
+ takerAddress: string,
+ makerFillableAmount: BigNumber,
+ takerFillableAmount: BigNumber,
+ expirationUnixTimestampSec?: BigNumber,
+ ): Promise<SignedOrder> {
+ const makerFee = new BigNumber(0);
+ const takerFee = new BigNumber(0);
+ const feeRecepient = constants.NULL_ADDRESS;
+ return this._createAsymmetricFillableSignedOrderWithFeesAsync(
+ makerTokenAddress,
+ takerTokenAddress,
+ makerFee,
+ takerFee,
+ makerAddress,
+ takerAddress,
+ makerFillableAmount,
+ takerFillableAmount,
+ feeRecepient,
+ expirationUnixTimestampSec,
+ );
+ }
+ public async createPartiallyFilledSignedOrderAsync(
+ makerTokenAddress: string,
+ takerTokenAddress: string,
+ takerAddress: string,
+ fillableAmount: BigNumber,
+ partialFillAmount: BigNumber,
+ ) {
+ const [makerAddress] = this._userAddresses;
+ const signedOrder = await this.createAsymmetricFillableSignedOrderAsync(
+ makerTokenAddress,
+ takerTokenAddress,
+ makerAddress,
+ takerAddress,
+ fillableAmount,
+ fillableAmount,
+ );
+ const shouldThrowOnInsufficientBalanceOrAllowance = false;
+ await this._zeroEx.exchange.fillOrderAsync(
+ signedOrder,
+ partialFillAmount,
+ shouldThrowOnInsufficientBalanceOrAllowance,
+ takerAddress,
+ );
+ return signedOrder;
+ }
+ private async _createAsymmetricFillableSignedOrderWithFeesAsync(
+ makerTokenAddress: string,
+ takerTokenAddress: string,
+ makerFee: BigNumber,
+ takerFee: BigNumber,
+ makerAddress: string,
+ takerAddress: string,
+ makerFillableAmount: BigNumber,
+ takerFillableAmount: BigNumber,
+ feeRecepient: string,
+ expirationUnixTimestampSec?: BigNumber,
+ ): Promise<SignedOrder> {
+ await Promise.all([
+ this._increaseBalanceAndAllowanceAsync(makerTokenAddress, makerAddress, makerFillableAmount),
+ this._increaseBalanceAndAllowanceAsync(takerTokenAddress, takerAddress, takerFillableAmount),
+ ]);
+ await Promise.all([
+ this._increaseBalanceAndAllowanceAsync(this._zrxTokenAddress, makerAddress, makerFee),
+ this._increaseBalanceAndAllowanceAsync(this._zrxTokenAddress, takerAddress, takerFee),
+ ]);
- const signedOrder = await orderFactory.createSignedOrderAsync(
- this._zeroEx,
- makerAddress,
- takerAddress,
- makerFee,
- takerFee,
- makerFillableAmount,
- makerTokenAddress,
- takerFillableAmount,
- takerTokenAddress,
- this._exchangeContractAddress,
- feeRecepient,
- expirationUnixTimestampSec,
- );
- return signedOrder;
- }
- private async _increaseBalanceAndAllowanceAsync(
- tokenAddress: string,
- address: string,
- amount: BigNumber,
- ): Promise<void> {
- if (amount.isZero() || address === ZeroEx.NULL_ADDRESS) {
- return; // noop
- }
- await Promise.all([
- this._increaseBalanceAsync(tokenAddress, address, amount),
- this._increaseAllowanceAsync(tokenAddress, address, amount),
- ]);
- }
- private async _increaseBalanceAsync(tokenAddress: string, address: string, amount: BigNumber): Promise<void> {
- await this._zeroEx.token.transferAsync(tokenAddress, this._coinbase, address, amount);
- }
- private async _increaseAllowanceAsync(tokenAddress: string, address: string, amount: BigNumber): Promise<void> {
- const oldMakerAllowance = await this._zeroEx.token.getProxyAllowanceAsync(tokenAddress, address);
- const newMakerAllowance = oldMakerAllowance.plus(amount);
- await this._zeroEx.token.setProxyAllowanceAsync(tokenAddress, address, newMakerAllowance);
- }
+ const signedOrder = await orderFactory.createSignedOrderAsync(
+ this._zeroEx,
+ makerAddress,
+ takerAddress,
+ makerFee,
+ takerFee,
+ makerFillableAmount,
+ makerTokenAddress,
+ takerFillableAmount,
+ takerTokenAddress,
+ this._exchangeContractAddress,
+ feeRecepient,
+ expirationUnixTimestampSec,
+ );
+ return signedOrder;
+ }
+ private async _increaseBalanceAndAllowanceAsync(
+ tokenAddress: string,
+ address: string,
+ amount: BigNumber,
+ ): Promise<void> {
+ if (amount.isZero() || address === ZeroEx.NULL_ADDRESS) {
+ return; // noop
+ }
+ await Promise.all([
+ this._increaseBalanceAsync(tokenAddress, address, amount),
+ this._increaseAllowanceAsync(tokenAddress, address, amount),
+ ]);
+ }
+ private async _increaseBalanceAsync(tokenAddress: string, address: string, amount: BigNumber): Promise<void> {
+ await this._zeroEx.token.transferAsync(tokenAddress, this._coinbase, address, amount);
+ }
+ private async _increaseAllowanceAsync(tokenAddress: string, address: string, amount: BigNumber): Promise<void> {
+ const oldMakerAllowance = await this._zeroEx.token.getProxyAllowanceAsync(tokenAddress, address);
+ const newMakerAllowance = oldMakerAllowance.plus(amount);
+ await this._zeroEx.token.setProxyAllowanceAsync(tokenAddress, address, newMakerAllowance);
+ }
}
diff --git a/packages/0x.js/test/utils/order_factory.ts b/packages/0x.js/test/utils/order_factory.ts
index b65920e64..08f2081a4 100644
--- a/packages/0x.js/test/utils/order_factory.ts
+++ b/packages/0x.js/test/utils/order_factory.ts
@@ -6,41 +6,41 @@ import { SignedOrder, ZeroEx } from '../../src';
const SHOULD_ADD_PERSONAL_MESSAGE_PREFIX = false;
export const orderFactory = {
- async createSignedOrderAsync(
- zeroEx: ZeroEx,
- maker: string,
- taker: string,
- makerFee: BigNumber,
- takerFee: BigNumber,
- makerTokenAmount: BigNumber,
- makerTokenAddress: string,
- takerTokenAmount: BigNumber,
- takerTokenAddress: string,
- exchangeContractAddress: string,
- feeRecipient: string,
- expirationUnixTimestampSecIfExists?: BigNumber,
- ): Promise<SignedOrder> {
- const defaultExpirationUnixTimestampSec = new BigNumber(2524604400); // Close to infinite
- const expirationUnixTimestampSec = _.isUndefined(expirationUnixTimestampSecIfExists)
- ? defaultExpirationUnixTimestampSec
- : expirationUnixTimestampSecIfExists;
- const order = {
- maker,
- taker,
- makerFee,
- takerFee,
- makerTokenAmount,
- takerTokenAmount,
- makerTokenAddress,
- takerTokenAddress,
- salt: ZeroEx.generatePseudoRandomSalt(),
- exchangeContractAddress,
- feeRecipient,
- expirationUnixTimestampSec,
- };
- const orderHash = ZeroEx.getOrderHashHex(order);
- const ecSignature = await zeroEx.signOrderHashAsync(orderHash, maker, SHOULD_ADD_PERSONAL_MESSAGE_PREFIX);
- const signedOrder: SignedOrder = _.assign(order, { ecSignature });
- return signedOrder;
- },
+ async createSignedOrderAsync(
+ zeroEx: ZeroEx,
+ maker: string,
+ taker: string,
+ makerFee: BigNumber,
+ takerFee: BigNumber,
+ makerTokenAmount: BigNumber,
+ makerTokenAddress: string,
+ takerTokenAmount: BigNumber,
+ takerTokenAddress: string,
+ exchangeContractAddress: string,
+ feeRecipient: string,
+ expirationUnixTimestampSecIfExists?: BigNumber,
+ ): Promise<SignedOrder> {
+ const defaultExpirationUnixTimestampSec = new BigNumber(2524604400); // Close to infinite
+ const expirationUnixTimestampSec = _.isUndefined(expirationUnixTimestampSecIfExists)
+ ? defaultExpirationUnixTimestampSec
+ : expirationUnixTimestampSecIfExists;
+ const order = {
+ maker,
+ taker,
+ makerFee,
+ takerFee,
+ makerTokenAmount,
+ takerTokenAmount,
+ makerTokenAddress,
+ takerTokenAddress,
+ salt: ZeroEx.generatePseudoRandomSalt(),
+ exchangeContractAddress,
+ feeRecipient,
+ expirationUnixTimestampSec,
+ };
+ const orderHash = ZeroEx.getOrderHashHex(order);
+ const ecSignature = await zeroEx.signOrderHashAsync(orderHash, maker, SHOULD_ADD_PERSONAL_MESSAGE_PREFIX);
+ const signedOrder: SignedOrder = _.assign(order, { ecSignature });
+ return signedOrder;
+ },
};
diff --git a/packages/0x.js/test/utils/report_callback_errors.ts b/packages/0x.js/test/utils/report_callback_errors.ts
index 0b619f73e..27c9745c9 100644
--- a/packages/0x.js/test/utils/report_callback_errors.ts
+++ b/packages/0x.js/test/utils/report_callback_errors.ts
@@ -6,61 +6,61 @@ import { DoneCallback } from '../../src/types';
const expect = chai.expect;
export const reportNoErrorCallbackErrors = (done: DoneCallback, expectToBeCalledOnce = true) => {
- return <T>(f?: (value: T) => void) => {
- const wrapped = (value: T) => {
- if (_.isUndefined(f)) {
- done();
- return;
- }
- try {
- f(value);
- if (expectToBeCalledOnce) {
- done();
- }
- } catch (err) {
- done(err);
- }
- };
- return wrapped;
- };
+ return <T>(f?: (value: T) => void) => {
+ const wrapped = (value: T) => {
+ if (_.isUndefined(f)) {
+ done();
+ return;
+ }
+ try {
+ f(value);
+ if (expectToBeCalledOnce) {
+ done();
+ }
+ } catch (err) {
+ done(err);
+ }
+ };
+ return wrapped;
+ };
};
export const reportNodeCallbackErrors = (done: DoneCallback, expectToBeCalledOnce = true) => {
- return <T>(f?: (value: T) => void) => {
- const wrapped = (error: Error | null, value: T | undefined) => {
- if (!_.isNull(error)) {
- done(error);
- } else {
- if (_.isUndefined(f)) {
- done();
- return;
- }
- try {
- f(value as T);
- if (expectToBeCalledOnce) {
- done();
- }
- } catch (err) {
- done(err);
- }
- }
- };
- return wrapped;
- };
+ return <T>(f?: (value: T) => void) => {
+ const wrapped = (error: Error | null, value: T | undefined) => {
+ if (!_.isNull(error)) {
+ done(error);
+ } else {
+ if (_.isUndefined(f)) {
+ done();
+ return;
+ }
+ try {
+ f(value as T);
+ if (expectToBeCalledOnce) {
+ done();
+ }
+ } catch (err) {
+ done(err);
+ }
+ }
+ };
+ return wrapped;
+ };
};
export const assertNodeCallbackError = (done: DoneCallback, errMsg: string) => {
- const wrapped = <T>(error: Error | null, value: T | undefined) => {
- if (_.isNull(error)) {
- done(new Error('Expected callback to receive an error'));
- } else {
- try {
- expect(error.message).to.be.equal(errMsg);
- done();
- } catch (err) {
- done(err);
- }
- }
- };
- return wrapped;
+ const wrapped = <T>(error: Error | null, value: T | undefined) => {
+ if (_.isNull(error)) {
+ done(new Error('Expected callback to receive an error'));
+ } else {
+ try {
+ expect(error.message).to.be.equal(errMsg);
+ done();
+ } catch (err) {
+ done(err);
+ }
+ }
+ };
+ return wrapped;
};
diff --git a/packages/0x.js/test/utils/subproviders/empty_wallet_subprovider.ts b/packages/0x.js/test/utils/subproviders/empty_wallet_subprovider.ts
index 466b0f350..53f2be83d 100644
--- a/packages/0x.js/test/utils/subproviders/empty_wallet_subprovider.ts
+++ b/packages/0x.js/test/utils/subproviders/empty_wallet_subprovider.ts
@@ -6,22 +6,22 @@ import { JSONRPCPayload } from '../../../src/types';
* Source: https://github.com/MetaMask/provider-engine/blob/master/subproviders/subprovider.js
*/
export class EmptyWalletSubprovider {
- // This method needs to be here to satisfy the interface but linter wants it to be static.
- // tslint:disable-next-line:prefer-function-over-method
- public handleRequest(payload: JSONRPCPayload, next: () => void, end: (err: Error | null, result: any) => void) {
- switch (payload.method) {
- case 'eth_accounts':
- end(null, []);
- return;
+ // This method needs to be here to satisfy the interface but linter wants it to be static.
+ // tslint:disable-next-line:prefer-function-over-method
+ public handleRequest(payload: JSONRPCPayload, next: () => void, end: (err: Error | null, result: any) => void) {
+ switch (payload.method) {
+ case 'eth_accounts':
+ end(null, []);
+ return;
- default:
- next();
- return;
- }
- }
- // Required to implement this method despite not needing it for this subprovider
- // tslint:disable-next-line:prefer-function-over-method
- public setEngine(engine: any) {
- // noop
- }
+ default:
+ next();
+ return;
+ }
+ }
+ // Required to implement this method despite not needing it for this subprovider
+ // tslint:disable-next-line:prefer-function-over-method
+ public setEngine(engine: any) {
+ // noop
+ }
}
diff --git a/packages/0x.js/test/utils/subproviders/fake_gas_estimate_subprovider.ts b/packages/0x.js/test/utils/subproviders/fake_gas_estimate_subprovider.ts
index 307bfb188..e1113a851 100644
--- a/packages/0x.js/test/utils/subproviders/fake_gas_estimate_subprovider.ts
+++ b/packages/0x.js/test/utils/subproviders/fake_gas_estimate_subprovider.ts
@@ -9,26 +9,26 @@ import { JSONRPCPayload } from '../../../src/types';
* Source: https://github.com/MetaMask/provider-engine/blob/master/subproviders/subprovider.js
*/
export class FakeGasEstimateSubprovider {
- private _constantGasAmount: number;
- constructor(constantGasAmount: number) {
- this._constantGasAmount = constantGasAmount;
- }
- // This method needs to be here to satisfy the interface but linter wants it to be static.
- // tslint:disable-next-line:prefer-function-over-method
- public handleRequest(payload: JSONRPCPayload, next: () => void, end: (err: Error | null, result: any) => void) {
- switch (payload.method) {
- case 'eth_estimateGas':
- end(null, this._constantGasAmount);
- return;
+ private _constantGasAmount: number;
+ constructor(constantGasAmount: number) {
+ this._constantGasAmount = constantGasAmount;
+ }
+ // This method needs to be here to satisfy the interface but linter wants it to be static.
+ // tslint:disable-next-line:prefer-function-over-method
+ public handleRequest(payload: JSONRPCPayload, next: () => void, end: (err: Error | null, result: any) => void) {
+ switch (payload.method) {
+ case 'eth_estimateGas':
+ end(null, this._constantGasAmount);
+ return;
- default:
- next();
- return;
- }
- }
- // Required to implement this method despite not needing it for this subprovider
- // tslint:disable-next-line:prefer-function-over-method
- public setEngine(engine: any) {
- // noop
- }
+ default:
+ next();
+ return;
+ }
+ }
+ // Required to implement this method despite not needing it for this subprovider
+ // tslint:disable-next-line:prefer-function-over-method
+ public setEngine(engine: any) {
+ // noop
+ }
}
diff --git a/packages/0x.js/test/utils/token_utils.ts b/packages/0x.js/test/utils/token_utils.ts
index 9cbefcf8e..d3fc22ff4 100644
--- a/packages/0x.js/test/utils/token_utils.ts
+++ b/packages/0x.js/test/utils/token_utils.ts
@@ -6,28 +6,28 @@ const PROTOCOL_TOKEN_SYMBOL = 'ZRX';
const WETH_TOKEN_SYMBOL = 'WETH';
export class TokenUtils {
- private _tokens: Token[];
- constructor(tokens: Token[]) {
- this._tokens = tokens;
- }
- public getProtocolTokenOrThrow(): Token {
- const zrxToken = _.find(this._tokens, { symbol: PROTOCOL_TOKEN_SYMBOL });
- if (_.isUndefined(zrxToken)) {
- throw new Error(InternalZeroExError.ZrxNotInTokenRegistry);
- }
- return zrxToken;
- }
- public getWethTokenOrThrow(): Token {
- const wethToken = _.find(this._tokens, { symbol: WETH_TOKEN_SYMBOL });
- if (_.isUndefined(wethToken)) {
- throw new Error(InternalZeroExError.WethNotInTokenRegistry);
- }
- return wethToken;
- }
- public getDummyTokens(): Token[] {
- const dummyTokens = _.filter(this._tokens, token => {
- return !_.includes([PROTOCOL_TOKEN_SYMBOL, WETH_TOKEN_SYMBOL], token.symbol);
- });
- return dummyTokens;
- }
+ private _tokens: Token[];
+ constructor(tokens: Token[]) {
+ this._tokens = tokens;
+ }
+ public getProtocolTokenOrThrow(): Token {
+ const zrxToken = _.find(this._tokens, { symbol: PROTOCOL_TOKEN_SYMBOL });
+ if (_.isUndefined(zrxToken)) {
+ throw new Error(InternalZeroExError.ZrxNotInTokenRegistry);
+ }
+ return zrxToken;
+ }
+ public getWethTokenOrThrow(): Token {
+ const wethToken = _.find(this._tokens, { symbol: WETH_TOKEN_SYMBOL });
+ if (_.isUndefined(wethToken)) {
+ throw new Error(InternalZeroExError.WethNotInTokenRegistry);
+ }
+ return wethToken;
+ }
+ public getDummyTokens(): Token[] {
+ const dummyTokens = _.filter(this._tokens, token => {
+ return !_.includes([PROTOCOL_TOKEN_SYMBOL, WETH_TOKEN_SYMBOL], token.symbol);
+ });
+ return dummyTokens;
+ }
}
diff --git a/packages/0x.js/test/utils/web3_factory.ts b/packages/0x.js/test/utils/web3_factory.ts
index 640b96256..26c26e03d 100644
--- a/packages/0x.js/test/utils/web3_factory.ts
+++ b/packages/0x.js/test/utils/web3_factory.ts
@@ -19,24 +19,24 @@ import { constants } from './constants';
import * as Web3 from 'web3';
export const web3Factory = {
- create(hasAddresses: boolean = true): Web3 {
- const provider = this.getRpcProvider(hasAddresses);
- const web3 = new Web3();
- web3.setProvider(provider);
- return web3;
- },
- getRpcProvider(hasAddresses: boolean = true): Web3.Provider {
- const provider = new ProviderEngine();
- if (!hasAddresses) {
- provider.addProvider(new EmptyWalletSubprovider());
- }
- provider.addProvider(new FakeGasEstimateSubprovider(constants.GAS_ESTIMATE));
- provider.addProvider(
- new RpcSubprovider({
- rpcUrl: constants.RPC_URL,
- }),
- );
- provider.start();
- return provider;
- },
+ create(hasAddresses: boolean = true): Web3 {
+ const provider = this.getRpcProvider(hasAddresses);
+ const web3 = new Web3();
+ web3.setProvider(provider);
+ return web3;
+ },
+ getRpcProvider(hasAddresses: boolean = true): Web3.Provider {
+ const provider = new ProviderEngine();
+ if (!hasAddresses) {
+ provider.addProvider(new EmptyWalletSubprovider());
+ }
+ provider.addProvider(new FakeGasEstimateSubprovider(constants.GAS_ESTIMATE));
+ provider.addProvider(
+ new RpcSubprovider({
+ rpcUrl: constants.RPC_URL,
+ }),
+ );
+ provider.start();
+ return provider;
+ },
};
diff --git a/packages/0x.js/tsconfig.json b/packages/0x.js/tsconfig.json
index 0666fbd82..117f51e83 100644
--- a/packages/0x.js/tsconfig.json
+++ b/packages/0x.js/tsconfig.json
@@ -1,16 +1,16 @@
{
- "extends": "../../tsconfig",
- "compilerOptions": {
- "outDir": "lib",
- "noImplicitThis": false
- },
- "include": [
- "./src/**/*",
- "./test/**/*",
- "../../node_modules/types-bn/index.d.ts",
- "../../node_modules/types-ethereumjs-util/index.d.ts",
- "../../node_modules/web3-typescript-typings/index.d.ts",
- "../../node_modules/chai-typescript-typings/index.d.ts",
- "../../node_modules/chai-as-promised-typescript-typings/index.d.ts"
- ]
+ "extends": "../../tsconfig",
+ "compilerOptions": {
+ "outDir": "lib",
+ "noImplicitThis": false
+ },
+ "include": [
+ "./src/**/*",
+ "./test/**/*",
+ "../../node_modules/types-bn/index.d.ts",
+ "../../node_modules/types-ethereumjs-util/index.d.ts",
+ "../../node_modules/web3-typescript-typings/index.d.ts",
+ "../../node_modules/chai-typescript-typings/index.d.ts",
+ "../../node_modules/chai-as-promised-typescript-typings/index.d.ts"
+ ]
}
diff --git a/packages/0x.js/tslint.json b/packages/0x.js/tslint.json
index e63054bfc..ffaefe83a 100644
--- a/packages/0x.js/tslint.json
+++ b/packages/0x.js/tslint.json
@@ -1,3 +1,3 @@
{
- "extends": ["@0xproject/tslint-config"]
+ "extends": ["@0xproject/tslint-config"]
}
diff --git a/packages/abi-gen/package.json b/packages/abi-gen/package.json
index 58ee12220..31a0f7f55 100644
--- a/packages/abi-gen/package.json
+++ b/packages/abi-gen/package.json
@@ -1,48 +1,48 @@
{
- "name": "@0xproject/abi-gen",
- "version": "0.1.4",
- "description": "Generate contract wrappers from ABI and handlebars templates",
- "main": "lib/index.js",
- "types": "lib/index.d.ts",
- "scripts": {
- "lint": "tslint --project . 'src/**/*.ts'",
- "clean": "shx rm -rf lib",
- "build": "tsc"
- },
- "bin": {
- "abi-gen": "lib/index.js"
- },
- "repository": {
- "type": "git",
- "url": "https://github.com/0xProject/0x.js.git"
- },
- "license": "Apache-2.0",
- "bugs": {
- "url": "https://github.com/0xProject/0x.js/issues"
- },
- "homepage": "https://github.com/0xProject/0x.js/packages/abi-gen/README.md",
- "dependencies": {
- "@0xproject/utils": "^0.2.2",
- "chalk": "^2.3.0",
- "glob": "^7.1.2",
- "handlebars": "^4.0.11",
- "lodash": "^4.17.4",
- "mkdirp": "^0.5.1",
- "to-snake-case": "^1.0.0",
- "web3": "^0.20.0",
- "yargs": "^10.0.3"
- },
- "devDependencies": {
- "@0xproject/tslint-config": "^0.4.4",
- "@types/glob": "^5.0.33",
- "@types/handlebars": "^4.0.36",
- "@types/mkdirp": "^0.5.1",
- "@types/node": "^8.0.53",
- "@types/yargs": "^10.0.0",
- "npm-run-all": "^4.1.2",
- "shx": "^0.2.2",
- "tslint": "5.8.0",
- "typescript": "~2.6.1",
- "web3-typescript-typings": "^0.9.6"
- }
+ "name": "@0xproject/abi-gen",
+ "version": "0.1.4",
+ "description": "Generate contract wrappers from ABI and handlebars templates",
+ "main": "lib/index.js",
+ "types": "lib/index.d.ts",
+ "scripts": {
+ "lint": "tslint --project . 'src/**/*.ts'",
+ "clean": "shx rm -rf lib",
+ "build": "tsc"
+ },
+ "bin": {
+ "abi-gen": "lib/index.js"
+ },
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/0xProject/0x.js.git"
+ },
+ "license": "Apache-2.0",
+ "bugs": {
+ "url": "https://github.com/0xProject/0x.js/issues"
+ },
+ "homepage": "https://github.com/0xProject/0x.js/packages/abi-gen/README.md",
+ "dependencies": {
+ "@0xproject/utils": "^0.2.2",
+ "chalk": "^2.3.0",
+ "glob": "^7.1.2",
+ "handlebars": "^4.0.11",
+ "lodash": "^4.17.4",
+ "mkdirp": "^0.5.1",
+ "to-snake-case": "^1.0.0",
+ "web3": "^0.20.0",
+ "yargs": "^10.0.3"
+ },
+ "devDependencies": {
+ "@0xproject/tslint-config": "^0.4.4",
+ "@types/glob": "^5.0.33",
+ "@types/handlebars": "^4.0.36",
+ "@types/mkdirp": "^0.5.1",
+ "@types/node": "^8.0.53",
+ "@types/yargs": "^10.0.0",
+ "npm-run-all": "^4.1.2",
+ "shx": "^0.2.2",
+ "tslint": "5.8.0",
+ "typescript": "~2.6.1",
+ "web3-typescript-typings": "^0.9.6"
+ }
}
diff --git a/packages/abi-gen/src/globals.d.ts b/packages/abi-gen/src/globals.d.ts
index 0b46f2c40..39df3f852 100644
--- a/packages/abi-gen/src/globals.d.ts
+++ b/packages/abi-gen/src/globals.d.ts
@@ -1,4 +1,4 @@
declare function toSnakeCase(str: string): string;
declare module 'to-snake-case' {
- export = toSnakeCase;
+ export = toSnakeCase;
}
diff --git a/packages/abi-gen/src/index.ts b/packages/abi-gen/src/index.ts
index 15d218711..527af32b1 100644
--- a/packages/abi-gen/src/index.ts
+++ b/packages/abi-gen/src/index.ts
@@ -20,91 +20,91 @@ const ABI_TYPE_EVENT = 'event';
const MAIN_TEMPLATE_NAME = 'contract.mustache';
const args = yargs
- .option('abiGlob', {
- describe: 'Glob pattern to search for ABI JSON files',
- type: 'string',
- demand: true,
- })
- .option('templates', {
- describe: 'Folder where to search for templates',
- type: 'string',
- demand: true,
- })
- .option('output', {
- describe: 'Folder where to put the output files',
- type: 'string',
- demand: true,
- }).argv;
+ .option('abiGlob', {
+ describe: 'Glob pattern to search for ABI JSON files',
+ type: 'string',
+ demand: true,
+ })
+ .option('templates', {
+ describe: 'Folder where to search for templates',
+ type: 'string',
+ demand: true,
+ })
+ .option('output', {
+ describe: 'Folder where to put the output files',
+ type: 'string',
+ demand: true,
+ }).argv;
function writeOutputFile(name: string, renderedTsCode: string): void {
- const fileName = toSnakeCase(name);
- const filePath = `${args.output}/${fileName}.ts`;
- fs.writeFileSync(filePath, renderedTsCode);
- utils.log(`Created: ${chalk.bold(filePath)}`);
+ const fileName = toSnakeCase(name);
+ const filePath = `${args.output}/${fileName}.ts`;
+ fs.writeFileSync(filePath, renderedTsCode);
+ utils.log(`Created: ${chalk.bold(filePath)}`);
}
Handlebars.registerHelper('parameterType', utils.solTypeToTsType.bind(utils, ParamKind.Input));
Handlebars.registerHelper('returnType', utils.solTypeToTsType.bind(utils, ParamKind.Output));
const partialTemplateFileNames = globSync(`${args.templates}/partials/**/*.mustache`);
for (const partialTemplateFileName of partialTemplateFileNames) {
- const namedContent = utils.getNamedContent(partialTemplateFileName);
- Handlebars.registerPartial(namedContent.name, namedContent.content);
+ const namedContent = utils.getNamedContent(partialTemplateFileName);
+ Handlebars.registerPartial(namedContent.name, namedContent.content);
}
const mainTemplate = utils.getNamedContent(`${args.templates}/${MAIN_TEMPLATE_NAME}`);
const template = Handlebars.compile<ContextData>(mainTemplate.content);
const abiFileNames = globSync(args.abiGlob);
if (_.isEmpty(abiFileNames)) {
- utils.log(`${chalk.red(`No ABI files found.`)}`);
- utils.log(`Please make sure you've passed the correct folder name and that the files have
+ utils.log(`${chalk.red(`No ABI files found.`)}`);
+ utils.log(`Please make sure you've passed the correct folder name and that the files have
${chalk.bold('*.json')} extensions`);
- process.exit(1);
+ process.exit(1);
} else {
- utils.log(`Found ${chalk.green(`${abiFileNames.length}`)} ${chalk.bold('ABI')} files`);
- mkdirp.sync(args.output);
+ utils.log(`Found ${chalk.green(`${abiFileNames.length}`)} ${chalk.bold('ABI')} files`);
+ mkdirp.sync(args.output);
}
for (const abiFileName of abiFileNames) {
- const namedContent = utils.getNamedContent(abiFileName);
- utils.log(`Processing: ${chalk.bold(namedContent.name)}...`);
- const parsedContent = JSON.parse(namedContent.content);
- const ABI = _.isArray(parsedContent)
- ? parsedContent // ABI file
- : parsedContent.abi; // Truffle contracts file
- if (_.isUndefined(ABI)) {
- utils.log(`${chalk.red(`ABI not found in ${abiFileName}.`)}`);
- utils.log(`Please make sure your ABI file is either an array with ABI entries or an object with the abi key`);
- process.exit(1);
- }
+ const namedContent = utils.getNamedContent(abiFileName);
+ utils.log(`Processing: ${chalk.bold(namedContent.name)}...`);
+ const parsedContent = JSON.parse(namedContent.content);
+ const ABI = _.isArray(parsedContent)
+ ? parsedContent // ABI file
+ : parsedContent.abi; // Truffle contracts file
+ if (_.isUndefined(ABI)) {
+ utils.log(`${chalk.red(`ABI not found in ${abiFileName}.`)}`);
+ utils.log(`Please make sure your ABI file is either an array with ABI entries or an object with the abi key`);
+ process.exit(1);
+ }
- let ctor = ABI.find((abi: Web3.AbiDefinition) => abi.type === ABI_TYPE_CONSTRUCTOR) as Web3.ConstructorAbi;
- if (_.isUndefined(ctor)) {
- ctor = utils.getEmptyConstructor(); // The constructor exists, but it's implicit in JSON's ABI definition
- }
+ let ctor = ABI.find((abi: Web3.AbiDefinition) => abi.type === ABI_TYPE_CONSTRUCTOR) as Web3.ConstructorAbi;
+ if (_.isUndefined(ctor)) {
+ ctor = utils.getEmptyConstructor(); // The constructor exists, but it's implicit in JSON's ABI definition
+ }
- const methodAbis = ABI.filter((abi: Web3.AbiDefinition) => abi.type === ABI_TYPE_METHOD) as Web3.MethodAbi[];
- const methodsData = _.map(methodAbis, methodAbi => {
- _.map(methodAbi.inputs, input => {
- if (_.isEmpty(input.name)) {
- // Auto-generated getters don't have parameter names
- input.name = 'index';
- }
- });
- // This will make templates simpler
- const methodData = {
- ...methodAbi,
- singleReturnValue: methodAbi.outputs.length === 1,
- };
- return methodData;
- });
+ const methodAbis = ABI.filter((abi: Web3.AbiDefinition) => abi.type === ABI_TYPE_METHOD) as Web3.MethodAbi[];
+ const methodsData = _.map(methodAbis, methodAbi => {
+ _.map(methodAbi.inputs, input => {
+ if (_.isEmpty(input.name)) {
+ // Auto-generated getters don't have parameter names
+ input.name = 'index';
+ }
+ });
+ // This will make templates simpler
+ const methodData = {
+ ...methodAbi,
+ singleReturnValue: methodAbi.outputs.length === 1,
+ };
+ return methodData;
+ });
- const eventAbis = ABI.filter((abi: Web3.AbiDefinition) => abi.type === ABI_TYPE_EVENT) as Web3.EventAbi[];
+ const eventAbis = ABI.filter((abi: Web3.AbiDefinition) => abi.type === ABI_TYPE_EVENT) as Web3.EventAbi[];
- const contextData = {
- contractName: namedContent.name,
- ctor,
- methods: methodsData,
- events: eventAbis,
- };
- const renderedTsCode = template(contextData);
- writeOutputFile(namedContent.name, renderedTsCode);
+ const contextData = {
+ contractName: namedContent.name,
+ ctor,
+ methods: methodsData,
+ events: eventAbis,
+ };
+ const renderedTsCode = template(contextData);
+ writeOutputFile(namedContent.name, renderedTsCode);
}
diff --git a/packages/abi-gen/src/types.ts b/packages/abi-gen/src/types.ts
index 413f94467..e82ab824b 100644
--- a/packages/abi-gen/src/types.ts
+++ b/packages/abi-gen/src/types.ts
@@ -1,23 +1,23 @@
import * as Web3 from 'web3';
export enum ParamKind {
- Input = 'input',
- Output = 'output',
+ Input = 'input',
+ Output = 'output',
}
export enum AbiType {
- Function = 'function',
- Constructor = 'constructor',
- Event = 'event',
- Fallback = 'fallback',
+ Function = 'function',
+ Constructor = 'constructor',
+ Event = 'event',
+ Fallback = 'fallback',
}
export interface Method extends Web3.MethodAbi {
- singleReturnValue: boolean;
+ singleReturnValue: boolean;
}
export interface ContextData {
- contractName: string;
- methods: Method[];
- events: Web3.EventAbi[];
+ contractName: string;
+ methods: Method[];
+ events: Web3.EventAbi[];
}
diff --git a/packages/abi-gen/src/utils.ts b/packages/abi-gen/src/utils.ts
index 4e1216a77..14255643a 100644
--- a/packages/abi-gen/src/utils.ts
+++ b/packages/abi-gen/src/utils.ts
@@ -6,68 +6,68 @@ import * as Web3 from 'web3';
import { AbiType, ParamKind } from './types';
export const utils = {
- solTypeToTsType(paramKind: ParamKind, solType: string): string {
- const trailingArrayRegex = /\[\d*\]$/;
- if (solType.match(trailingArrayRegex)) {
- const arrayItemSolType = solType.replace(trailingArrayRegex, '');
- const arrayItemTsType = utils.solTypeToTsType(paramKind, arrayItemSolType);
- const arrayTsType = utils.isUnionType(arrayItemTsType)
- ? `Array<${arrayItemTsType}>`
- : `${arrayItemTsType}[]`;
- return arrayTsType;
- } else {
- const solTypeRegexToTsType = [
- { regex: '^string$', tsType: 'string' },
- { regex: '^address$', tsType: 'string' },
- { regex: '^bool$', tsType: 'boolean' },
- { regex: '^u?int\\d*$', tsType: 'BigNumber' },
- { regex: '^bytes\\d*$', tsType: 'string' },
- ];
- if (paramKind === ParamKind.Input) {
- // web3 allows to pass those an non-bignumbers and that's nice
- // but it always returns stuff as BigNumbers
- solTypeRegexToTsType.unshift({
- regex: '^u?int(8|16|32)?$',
- tsType: 'number|BigNumber',
- });
- }
- for (const regexAndTxType of solTypeRegexToTsType) {
- const { regex, tsType } = regexAndTxType;
- if (solType.match(regex)) {
- return tsType;
- }
- }
- throw new Error(`Unknown Solidity type found: ${solType}`);
- }
- },
- isUnionType(tsType: string): boolean {
- return tsType === 'number|BigNumber';
- },
- log(...args: any[]): void {
- console.log(...args); // tslint:disable-line:no-console
- },
- getPartialNameFromFileName(filename: string): string {
- const name = path.parse(filename).name;
- return name;
- },
- getNamedContent(filename: string): { name: string; content: string } {
- const name = utils.getPartialNameFromFileName(filename);
- try {
- const content = fs.readFileSync(filename).toString();
- return {
- name,
- content,
- };
- } catch (err) {
- throw new Error(`Failed to read ${filename}: ${err}`);
- }
- },
- getEmptyConstructor(): Web3.ConstructorAbi {
- return {
- type: AbiType.Constructor,
- stateMutability: 'nonpayable',
- payable: false,
- inputs: [],
- };
- },
+ solTypeToTsType(paramKind: ParamKind, solType: string): string {
+ const trailingArrayRegex = /\[\d*\]$/;
+ if (solType.match(trailingArrayRegex)) {
+ const arrayItemSolType = solType.replace(trailingArrayRegex, '');
+ const arrayItemTsType = utils.solTypeToTsType(paramKind, arrayItemSolType);
+ const arrayTsType = utils.isUnionType(arrayItemTsType)
+ ? `Array<${arrayItemTsType}>`
+ : `${arrayItemTsType}[]`;
+ return arrayTsType;
+ } else {
+ const solTypeRegexToTsType = [
+ { regex: '^string$', tsType: 'string' },
+ { regex: '^address$', tsType: 'string' },
+ { regex: '^bool$', tsType: 'boolean' },
+ { regex: '^u?int\\d*$', tsType: 'BigNumber' },
+ { regex: '^bytes\\d*$', tsType: 'string' },
+ ];
+ if (paramKind === ParamKind.Input) {
+ // web3 allows to pass those an non-bignumbers and that's nice
+ // but it always returns stuff as BigNumbers
+ solTypeRegexToTsType.unshift({
+ regex: '^u?int(8|16|32)?$',
+ tsType: 'number|BigNumber',
+ });
+ }
+ for (const regexAndTxType of solTypeRegexToTsType) {
+ const { regex, tsType } = regexAndTxType;
+ if (solType.match(regex)) {
+ return tsType;
+ }
+ }
+ throw new Error(`Unknown Solidity type found: ${solType}`);
+ }
+ },
+ isUnionType(tsType: string): boolean {
+ return tsType === 'number|BigNumber';
+ },
+ log(...args: any[]): void {
+ console.log(...args); // tslint:disable-line:no-console
+ },
+ getPartialNameFromFileName(filename: string): string {
+ const name = path.parse(filename).name;
+ return name;
+ },
+ getNamedContent(filename: string): { name: string; content: string } {
+ const name = utils.getPartialNameFromFileName(filename);
+ try {
+ const content = fs.readFileSync(filename).toString();
+ return {
+ name,
+ content,
+ };
+ } catch (err) {
+ throw new Error(`Failed to read ${filename}: ${err}`);
+ }
+ },
+ getEmptyConstructor(): Web3.ConstructorAbi {
+ return {
+ type: AbiType.Constructor,
+ stateMutability: 'nonpayable',
+ payable: false,
+ inputs: [],
+ };
+ },
};
diff --git a/packages/abi-gen/tsconfig.json b/packages/abi-gen/tsconfig.json
index 1f89fb820..5e0c7c6d3 100644
--- a/packages/abi-gen/tsconfig.json
+++ b/packages/abi-gen/tsconfig.json
@@ -1,7 +1,7 @@
{
- "extends": "../../tsconfig",
- "compilerOptions": {
- "outDir": "lib"
- },
- "include": ["./src/**/*", "./test/**/*", "../../node_modules/web3-typescript-typings/index.d.ts"]
+ "extends": "../../tsconfig",
+ "compilerOptions": {
+ "outDir": "lib"
+ },
+ "include": ["./src/**/*", "./test/**/*", "../../node_modules/web3-typescript-typings/index.d.ts"]
}
diff --git a/packages/abi-gen/tslint.json b/packages/abi-gen/tslint.json
index e63054bfc..ffaefe83a 100644
--- a/packages/abi-gen/tslint.json
+++ b/packages/abi-gen/tslint.json
@@ -1,3 +1,3 @@
{
- "extends": ["@0xproject/tslint-config"]
+ "extends": ["@0xproject/tslint-config"]
}
diff --git a/packages/assert/package.json b/packages/assert/package.json
index 0e10b3384..a2d195728 100644
--- a/packages/assert/package.json
+++ b/packages/assert/package.json
@@ -1,45 +1,45 @@
{
- "name": "@0xproject/assert",
- "version": "0.0.13",
- "description": "Provides a standard way of performing type and schema validation across 0x projects",
- "main": "lib/src/index.js",
- "types": "lib/src/index.d.ts",
- "scripts": {
- "build": "tsc",
- "clean": "shx rm -rf _bundles lib test_temp",
- "lint": "tslint --project . 'src/**/*.ts' 'test/**/*.ts'",
- "run_mocha": "mocha lib/test/**/*_test.js",
- "prepublishOnly": "run-p build",
- "test": "run-s clean build run_mocha",
- "test:circleci": "yarn test"
- },
- "license": "Apache-2.0",
- "repository": {
- "type": "git",
- "url": "https://github.com/0xProject/0x.js.git"
- },
- "bugs": {
- "url": "https://github.com/0xProject/0x.js/issues"
- },
- "homepage": "https://github.com/0xProject/0x.js/packages/assert/README.md",
- "devDependencies": {
- "@0xproject/tslint-config": "^0.4.4",
- "@types/lodash": "^4.14.86",
- "@types/mocha": "^2.2.42",
- "@types/valid-url": "^1.0.2",
- "chai": "^4.0.1",
- "chai-typescript-typings": "^0.0.2",
- "dirty-chai": "^2.0.1",
- "mocha": "^4.0.1",
- "npm-run-all": "^4.1.2",
- "shx": "^0.2.2",
- "tslint": "5.8.0",
- "typescript": "~2.6.1"
- },
- "dependencies": {
- "@0xproject/json-schemas": "^0.7.5",
- "@0xproject/utils": "^0.2.2",
- "lodash": "^4.17.4",
- "valid-url": "^1.0.9"
- }
+ "name": "@0xproject/assert",
+ "version": "0.0.13",
+ "description": "Provides a standard way of performing type and schema validation across 0x projects",
+ "main": "lib/src/index.js",
+ "types": "lib/src/index.d.ts",
+ "scripts": {
+ "build": "tsc",
+ "clean": "shx rm -rf _bundles lib test_temp",
+ "lint": "tslint --project . 'src/**/*.ts' 'test/**/*.ts'",
+ "run_mocha": "mocha lib/test/**/*_test.js",
+ "prepublishOnly": "run-p build",
+ "test": "run-s clean build run_mocha",
+ "test:circleci": "yarn test"
+ },
+ "license": "Apache-2.0",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/0xProject/0x.js.git"
+ },
+ "bugs": {
+ "url": "https://github.com/0xProject/0x.js/issues"
+ },
+ "homepage": "https://github.com/0xProject/0x.js/packages/assert/README.md",
+ "devDependencies": {
+ "@0xproject/tslint-config": "^0.4.4",
+ "@types/lodash": "^4.14.86",
+ "@types/mocha": "^2.2.42",
+ "@types/valid-url": "^1.0.2",
+ "chai": "^4.0.1",
+ "chai-typescript-typings": "^0.0.2",
+ "dirty-chai": "^2.0.1",
+ "mocha": "^4.0.1",
+ "npm-run-all": "^4.1.2",
+ "shx": "^0.2.2",
+ "tslint": "5.8.0",
+ "typescript": "~2.6.1"
+ },
+ "dependencies": {
+ "@0xproject/json-schemas": "^0.7.5",
+ "@0xproject/utils": "^0.2.2",
+ "lodash": "^4.17.4",
+ "valid-url": "^1.0.9"
+ }
}
diff --git a/packages/assert/src/index.ts b/packages/assert/src/index.ts
index 0578bd303..7ad574ec7 100644
--- a/packages/assert/src/index.ts
+++ b/packages/assert/src/index.ts
@@ -6,89 +6,89 @@ import * as validUrl from 'valid-url';
const HEX_REGEX = /^0x[0-9A-F]*$/i;
export const assert = {
- isBigNumber(variableName: string, value: BigNumber): void {
- const isBigNumber = _.isObject(value) && (value as any).isBigNumber;
- this.assert(isBigNumber, this.typeAssertionMessage(variableName, 'BigNumber', value));
- },
- isValidBaseUnitAmount(variableName: string, value: BigNumber) {
- assert.isBigNumber(variableName, value);
- const isNegative = value.lessThan(0);
- this.assert(!isNegative, `${variableName} cannot be a negative number, found value: ${value.toNumber()}`);
- const hasDecimals = value.decimalPlaces() !== 0;
- this.assert(
- !hasDecimals,
- `${variableName} should be in baseUnits (no decimals), found value: ${value.toNumber()}`,
- );
- },
- isString(variableName: string, value: string): void {
- this.assert(_.isString(value), this.typeAssertionMessage(variableName, 'string', value));
- },
- isFunction(variableName: string, value: any): void {
- this.assert(_.isFunction(value), this.typeAssertionMessage(variableName, 'function', value));
- },
- isHexString(variableName: string, value: string): void {
- this.assert(
- _.isString(value) && HEX_REGEX.test(value),
- this.typeAssertionMessage(variableName, 'HexString', value),
- );
- },
- isETHAddressHex(variableName: string, value: string): void {
- this.assert(addressUtils.isAddress(value), this.typeAssertionMessage(variableName, 'ETHAddressHex', value));
- this.assert(
- addressUtils.isAddress(value) && value.toLowerCase() === value,
- `Checksummed addresses are not supported. Convert ${variableName} to lower case before passing`,
- );
- },
- doesBelongToStringEnum(
- variableName: string,
- value: string,
- stringEnum: any /* There is no base type for every string enum */,
- ): void {
- const doesBelongToStringEnum = !_.isUndefined(stringEnum[value]);
- const enumValues = _.keys(stringEnum);
- const enumValuesAsStrings = _.map(enumValues, enumValue => `'${enumValue}'`);
- const enumValuesAsString = enumValuesAsStrings.join(', ');
- assert.assert(
- doesBelongToStringEnum,
- `Expected ${variableName} to be one of: ${enumValuesAsString}, encountered: ${value}`,
- );
- },
- hasAtMostOneUniqueValue(value: any[], errMsg: string): void {
- this.assert(_.uniq(value).length <= 1, errMsg);
- },
- isNumber(variableName: string, value: number): void {
- this.assert(_.isFinite(value), this.typeAssertionMessage(variableName, 'number', value));
- },
- isBoolean(variableName: string, value: boolean): void {
- this.assert(_.isBoolean(value), this.typeAssertionMessage(variableName, 'boolean', value));
- },
- isWeb3Provider(variableName: string, value: any): void {
- const isWeb3Provider = _.isFunction(value.send) || _.isFunction(value.sendAsync);
- this.assert(isWeb3Provider, this.typeAssertionMessage(variableName, 'Web3.Provider', value));
- },
- doesConformToSchema(variableName: string, value: any, schema: Schema): void {
- const schemaValidator = new SchemaValidator();
- const validationResult = schemaValidator.validate(value, schema);
- const hasValidationErrors = validationResult.errors.length > 0;
- const msg = `Expected ${variableName} to conform to schema ${schema.id}
+ isBigNumber(variableName: string, value: BigNumber): void {
+ const isBigNumber = _.isObject(value) && (value as any).isBigNumber;
+ this.assert(isBigNumber, this.typeAssertionMessage(variableName, 'BigNumber', value));
+ },
+ isValidBaseUnitAmount(variableName: string, value: BigNumber) {
+ assert.isBigNumber(variableName, value);
+ const isNegative = value.lessThan(0);
+ this.assert(!isNegative, `${variableName} cannot be a negative number, found value: ${value.toNumber()}`);
+ const hasDecimals = value.decimalPlaces() !== 0;
+ this.assert(
+ !hasDecimals,
+ `${variableName} should be in baseUnits (no decimals), found value: ${value.toNumber()}`,
+ );
+ },
+ isString(variableName: string, value: string): void {
+ this.assert(_.isString(value), this.typeAssertionMessage(variableName, 'string', value));
+ },
+ isFunction(variableName: string, value: any): void {
+ this.assert(_.isFunction(value), this.typeAssertionMessage(variableName, 'function', value));
+ },
+ isHexString(variableName: string, value: string): void {
+ this.assert(
+ _.isString(value) && HEX_REGEX.test(value),
+ this.typeAssertionMessage(variableName, 'HexString', value),
+ );
+ },
+ isETHAddressHex(variableName: string, value: string): void {
+ this.assert(addressUtils.isAddress(value), this.typeAssertionMessage(variableName, 'ETHAddressHex', value));
+ this.assert(
+ addressUtils.isAddress(value) && value.toLowerCase() === value,
+ `Checksummed addresses are not supported. Convert ${variableName} to lower case before passing`,
+ );
+ },
+ doesBelongToStringEnum(
+ variableName: string,
+ value: string,
+ stringEnum: any /* There is no base type for every string enum */,
+ ): void {
+ const doesBelongToStringEnum = !_.isUndefined(stringEnum[value]);
+ const enumValues = _.keys(stringEnum);
+ const enumValuesAsStrings = _.map(enumValues, enumValue => `'${enumValue}'`);
+ const enumValuesAsString = enumValuesAsStrings.join(', ');
+ assert.assert(
+ doesBelongToStringEnum,
+ `Expected ${variableName} to be one of: ${enumValuesAsString}, encountered: ${value}`,
+ );
+ },
+ hasAtMostOneUniqueValue(value: any[], errMsg: string): void {
+ this.assert(_.uniq(value).length <= 1, errMsg);
+ },
+ isNumber(variableName: string, value: number): void {
+ this.assert(_.isFinite(value), this.typeAssertionMessage(variableName, 'number', value));
+ },
+ isBoolean(variableName: string, value: boolean): void {
+ this.assert(_.isBoolean(value), this.typeAssertionMessage(variableName, 'boolean', value));
+ },
+ isWeb3Provider(variableName: string, value: any): void {
+ const isWeb3Provider = _.isFunction(value.send) || _.isFunction(value.sendAsync);
+ this.assert(isWeb3Provider, this.typeAssertionMessage(variableName, 'Web3.Provider', value));
+ },
+ doesConformToSchema(variableName: string, value: any, schema: Schema): void {
+ const schemaValidator = new SchemaValidator();
+ const validationResult = schemaValidator.validate(value, schema);
+ const hasValidationErrors = validationResult.errors.length > 0;
+ const msg = `Expected ${variableName} to conform to schema ${schema.id}
Encountered: ${JSON.stringify(value, null, '\t')}
Validation errors: ${validationResult.errors.join(', ')}`;
- this.assert(!hasValidationErrors, msg);
- },
- isHttpUrl(variableName: string, value: any): void {
- const isValidUrl = !_.isUndefined(validUrl.isWebUri(value));
- this.assert(isValidUrl, this.typeAssertionMessage(variableName, 'http url', value));
- },
- isUri(variableName: string, value: any): void {
- const isValidUri = !_.isUndefined(validUrl.isUri(value));
- this.assert(isValidUri, this.typeAssertionMessage(variableName, 'uri', value));
- },
- assert(condition: boolean, message: string): void {
- if (!condition) {
- throw new Error(message);
- }
- },
- typeAssertionMessage(variableName: string, type: string, value: any): string {
- return `Expected ${variableName} to be of type ${type}, encountered: ${value}`;
- },
+ this.assert(!hasValidationErrors, msg);
+ },
+ isHttpUrl(variableName: string, value: any): void {
+ const isValidUrl = !_.isUndefined(validUrl.isWebUri(value));
+ this.assert(isValidUrl, this.typeAssertionMessage(variableName, 'http url', value));
+ },
+ isUri(variableName: string, value: any): void {
+ const isValidUri = !_.isUndefined(validUrl.isUri(value));
+ this.assert(isValidUri, this.typeAssertionMessage(variableName, 'uri', value));
+ },
+ assert(condition: boolean, message: string): void {
+ if (!condition) {
+ throw new Error(message);
+ }
+ },
+ typeAssertionMessage(variableName: string, type: string, value: any): string {
+ return `Expected ${variableName} to be of type ${type}, encountered: ${value}`;
+ },
};
diff --git a/packages/assert/test/assert_test.ts b/packages/assert/test/assert_test.ts
index 0af493bda..b0fa398d6 100644
--- a/packages/assert/test/assert_test.ts
+++ b/packages/assert/test/assert_test.ts
@@ -11,243 +11,243 @@ chai.use(dirtyChai);
const expect = chai.expect;
describe('Assertions', () => {
- const variableName = 'variable';
- describe('#isBigNumber', () => {
- it('should not throw for valid input', () => {
- const validInputs = [new BigNumber(23), new BigNumber('45')];
- validInputs.forEach(input => expect(assert.isBigNumber.bind(assert, variableName, input)).to.not.throw());
- });
- it('should throw for invalid input', () => {
- const invalidInputs = ['test', 42, false, { random: 'test' }, undefined];
- invalidInputs.forEach(input => expect(assert.isBigNumber.bind(assert, variableName, input)).to.throw());
- });
- });
- describe('#isValidBaseUnitAmount', () => {
- it('should not throw for valid input', () => {
- const validInputs = [new BigNumber(23), new BigNumber('45000000')];
- validInputs.forEach(input =>
- expect(assert.isValidBaseUnitAmount.bind(assert, variableName, input)).to.not.throw(),
- );
- });
- it('should throw for invalid input', () => {
- const invalidInputs = [0, undefined, new BigNumber(3.145), 3.145, new BigNumber(-400)];
- invalidInputs.forEach(input =>
- expect(assert.isValidBaseUnitAmount.bind(assert, variableName, input)).to.throw(),
- );
- });
- });
- describe('#isString', () => {
- it('should not throw for valid input', () => {
- const validInputs = ['hello', 'goodbye'];
- validInputs.forEach(input => expect(assert.isString.bind(assert, variableName, input)).to.not.throw());
- });
- it('should throw for invalid input', () => {
- const invalidInputs = [42, false, { random: 'test' }, undefined, new BigNumber(45)];
- invalidInputs.forEach(input => expect(assert.isString.bind(assert, variableName, input)).to.throw());
- });
- });
- describe('#isFunction', () => {
- it('should not throw for valid input', () => {
- const validInputs = [BigNumber, assert.isString];
- validInputs.forEach(input => expect(assert.isFunction.bind(assert, variableName, input)).to.not.throw());
- });
- it('should throw for invalid input', () => {
- const invalidInputs = [42, false, { random: 'test' }, undefined, new BigNumber(45)];
- invalidInputs.forEach(input => expect(assert.isFunction.bind(assert, variableName, input)).to.throw());
- });
- });
- describe('#isHexString', () => {
- it('should not throw for valid input', () => {
- const validInputs = [
- '0x61a3ed31B43c8780e905a260a35faefEc527be7516aa11c0256729b5b351bc33',
- '0x40349190569279751135161d22529dc25add4f6069af05be04cacbda2ace2254',
- ];
- validInputs.forEach(input => expect(assert.isHexString.bind(assert, variableName, input)).to.not.throw());
- });
- it('should throw for invalid input', () => {
- const invalidInputs = [
- 42,
- false,
- { random: 'test' },
- undefined,
- new BigNumber(45),
- '0x61a3ed31B43c8780e905a260a35faYfEc527be7516aa11c0256729b5b351bc33',
- ];
- invalidInputs.forEach(input => expect(assert.isHexString.bind(assert, variableName, input)).to.throw());
- });
- });
- describe('#isETHAddressHex', () => {
- it('should not throw for valid input', () => {
- const validInputs = [
- '0x0000000000000000000000000000000000000000',
- '0x6fffd0ae3f7d88c9b4925323f54c6e4b2918c5fd',
- '0x12459c951127e0c374ff9105dda097662a027093',
- ];
- validInputs.forEach(input =>
- expect(assert.isETHAddressHex.bind(assert, variableName, input)).to.not.throw(),
- );
- });
- it('should throw for invalid input', () => {
- const invalidInputs = [
- 42,
- false,
- { random: 'test' },
- undefined,
- new BigNumber(45),
- '0x6FFFd0ae3f7d88c9b4925323f54c6e4b2918c5fd',
- '0x6FFFd0ae3f7d88c9b4925323f54c6e4',
- ];
- invalidInputs.forEach(input => expect(assert.isETHAddressHex.bind(assert, variableName, input)).to.throw());
- });
- });
- describe('#doesBelongToStringEnum', () => {
- enum TestEnums {
- Test1 = 'Test1',
- Test2 = 'Test2',
- }
- it('should not throw for valid input', () => {
- const validInputs = [TestEnums.Test1, TestEnums.Test2];
- validInputs.forEach(input =>
- expect(assert.doesBelongToStringEnum.bind(assert, variableName, input, TestEnums)).to.not.throw(),
- );
- });
- it('should throw for invalid input', () => {
- const invalidInputs = [42, false, { random: 'test' }, undefined, new BigNumber(45)];
- invalidInputs.forEach(input =>
- expect(assert.doesBelongToStringEnum.bind(assert, variableName, input, TestEnums)).to.throw(),
- );
- });
- });
- describe('#hasAtMostOneUniqueValue', () => {
- const errorMsg = 'more than one unique value';
- it('should not throw for valid input', () => {
- const validInputs = [['hello'], ['goodbye', 'goodbye', 'goodbye']];
- validInputs.forEach(input =>
- expect(assert.hasAtMostOneUniqueValue.bind(assert, input, errorMsg)).to.not.throw(),
- );
- });
- it('should throw for invalid input', () => {
- const invalidInputs = [['hello', 'goodbye'], ['goodbye', 42, false, false]];
- invalidInputs.forEach(input =>
- expect(assert.hasAtMostOneUniqueValue.bind(assert, input, errorMsg)).to.throw(),
- );
- });
- });
- describe('#isNumber', () => {
- it('should not throw for valid input', () => {
- const validInputs = [42, 0, 21e42];
- validInputs.forEach(input => expect(assert.isNumber.bind(assert, variableName, input)).to.not.throw());
- });
- it('should throw for invalid input', () => {
- const invalidInputs = [false, { random: 'test' }, undefined, new BigNumber(45)];
- invalidInputs.forEach(input => expect(assert.isNumber.bind(assert, variableName, input)).to.throw());
- });
- });
- describe('#isBoolean', () => {
- it('should not throw for valid input', () => {
- const validInputs = [true, false];
- validInputs.forEach(input => expect(assert.isBoolean.bind(assert, variableName, input)).to.not.throw());
- });
- it('should throw for invalid input', () => {
- const invalidInputs = [42, { random: 'test' }, undefined, new BigNumber(45)];
- invalidInputs.forEach(input => expect(assert.isBoolean.bind(assert, variableName, input)).to.throw());
- });
- });
- describe('#isWeb3Provider', () => {
- it('should not throw for valid input', () => {
- const validInputs = [{ send: () => 45 }, { sendAsync: () => 45 }];
- validInputs.forEach(input =>
- expect(assert.isWeb3Provider.bind(assert, variableName, input)).to.not.throw(),
- );
- });
- it('should throw for invalid input', () => {
- const invalidInputs = [42, { random: 'test' }, undefined, new BigNumber(45)];
- invalidInputs.forEach(input => expect(assert.isWeb3Provider.bind(assert, variableName, input)).to.throw());
- });
- });
- describe('#doesConformToSchema', () => {
- const schema = schemas.addressSchema;
- it('should not throw for valid input', () => {
- const validInputs = [
- '0x6fffd0ae3f7d88c9b4925323f54c6e4b2918c5fd',
- '0x12459c951127e0c374ff9105dda097662a027093',
- ];
- validInputs.forEach(input =>
- expect(assert.doesConformToSchema.bind(assert, variableName, input, schema)).to.not.throw(),
- );
- });
- it('should throw for invalid input', () => {
- const invalidInputs = [42, { random: 'test' }, undefined, new BigNumber(45)];
- invalidInputs.forEach(input =>
- expect(assert.doesConformToSchema.bind(assert, variableName, input, schema)).to.throw(),
- );
- });
- });
- describe('#isHttpUrl', () => {
- it('should not throw for valid input', () => {
- const validInputs = [
- 'http://www.google.com',
- 'https://api.example-relayer.net',
- 'https://api.radarrelay.com/0x/v0/',
- 'https://zeroex.beta.radarrelay.com:8000/0x/v0/',
- ];
- validInputs.forEach(input => expect(assert.isHttpUrl.bind(assert, variableName, input)).to.not.throw());
- });
- it('should throw for invalid input', () => {
- const invalidInputs = [
- 42,
- { random: 'test' },
- undefined,
- new BigNumber(45),
- 'ws://www.api.example-relayer.net',
- 'www.google.com',
- 'api.example-relayer.net',
- 'user:password@api.example-relayer.net',
- '//api.example-relayer.net',
- ];
- invalidInputs.forEach(input => expect(assert.isHttpUrl.bind(assert, variableName, input)).to.throw());
- });
- });
- describe('#isUri', () => {
- it('should not throw for valid input', () => {
- const validInputs = [
- 'http://www.google.com',
- 'https://api.example-relayer.net',
- 'https://api.radarrelay.com/0x/v0/',
- 'https://zeroex.beta.radarrelay.com:8000/0x/v0/',
- 'ws://www.api.example-relayer.net',
- 'wss://www.api.example-relayer.net',
- 'user:password@api.example-relayer.net',
- ];
- validInputs.forEach(input => expect(assert.isUri.bind(assert, variableName, input)).to.not.throw());
- });
- it('should throw for invalid input', () => {
- const invalidInputs = [
- 42,
- { random: 'test' },
- undefined,
- new BigNumber(45),
- 'www.google.com',
- 'api.example-relayer.net',
- '//api.example-relayer.net',
- ];
- invalidInputs.forEach(input => expect(assert.isUri.bind(assert, variableName, input)).to.throw());
- });
- });
- describe('#assert', () => {
- const assertMessage = 'assert not satisfied';
- it('should not throw for valid input', () => {
- expect(assert.assert.bind(assert, true, assertMessage)).to.not.throw();
- });
- it('should throw for invalid input', () => {
- expect(assert.assert.bind(assert, false, assertMessage)).to.throw();
- });
- });
- describe('#typeAssertionMessage', () => {
- it('should render correct message', () => {
- expect(assert.typeAssertionMessage('variable', 'string', 'number')).to.equal(
- `Expected variable to be of type string, encountered: number`,
- );
- });
- });
+ const variableName = 'variable';
+ describe('#isBigNumber', () => {
+ it('should not throw for valid input', () => {
+ const validInputs = [new BigNumber(23), new BigNumber('45')];
+ validInputs.forEach(input => expect(assert.isBigNumber.bind(assert, variableName, input)).to.not.throw());
+ });
+ it('should throw for invalid input', () => {
+ const invalidInputs = ['test', 42, false, { random: 'test' }, undefined];
+ invalidInputs.forEach(input => expect(assert.isBigNumber.bind(assert, variableName, input)).to.throw());
+ });
+ });
+ describe('#isValidBaseUnitAmount', () => {
+ it('should not throw for valid input', () => {
+ const validInputs = [new BigNumber(23), new BigNumber('45000000')];
+ validInputs.forEach(input =>
+ expect(assert.isValidBaseUnitAmount.bind(assert, variableName, input)).to.not.throw(),
+ );
+ });
+ it('should throw for invalid input', () => {
+ const invalidInputs = [0, undefined, new BigNumber(3.145), 3.145, new BigNumber(-400)];
+ invalidInputs.forEach(input =>
+ expect(assert.isValidBaseUnitAmount.bind(assert, variableName, input)).to.throw(),
+ );
+ });
+ });
+ describe('#isString', () => {
+ it('should not throw for valid input', () => {
+ const validInputs = ['hello', 'goodbye'];
+ validInputs.forEach(input => expect(assert.isString.bind(assert, variableName, input)).to.not.throw());
+ });
+ it('should throw for invalid input', () => {
+ const invalidInputs = [42, false, { random: 'test' }, undefined, new BigNumber(45)];
+ invalidInputs.forEach(input => expect(assert.isString.bind(assert, variableName, input)).to.throw());
+ });
+ });
+ describe('#isFunction', () => {
+ it('should not throw for valid input', () => {
+ const validInputs = [BigNumber, assert.isString];
+ validInputs.forEach(input => expect(assert.isFunction.bind(assert, variableName, input)).to.not.throw());
+ });
+ it('should throw for invalid input', () => {
+ const invalidInputs = [42, false, { random: 'test' }, undefined, new BigNumber(45)];
+ invalidInputs.forEach(input => expect(assert.isFunction.bind(assert, variableName, input)).to.throw());
+ });
+ });
+ describe('#isHexString', () => {
+ it('should not throw for valid input', () => {
+ const validInputs = [
+ '0x61a3ed31B43c8780e905a260a35faefEc527be7516aa11c0256729b5b351bc33',
+ '0x40349190569279751135161d22529dc25add4f6069af05be04cacbda2ace2254',
+ ];
+ validInputs.forEach(input => expect(assert.isHexString.bind(assert, variableName, input)).to.not.throw());
+ });
+ it('should throw for invalid input', () => {
+ const invalidInputs = [
+ 42,
+ false,
+ { random: 'test' },
+ undefined,
+ new BigNumber(45),
+ '0x61a3ed31B43c8780e905a260a35faYfEc527be7516aa11c0256729b5b351bc33',
+ ];
+ invalidInputs.forEach(input => expect(assert.isHexString.bind(assert, variableName, input)).to.throw());
+ });
+ });
+ describe('#isETHAddressHex', () => {
+ it('should not throw for valid input', () => {
+ const validInputs = [
+ '0x0000000000000000000000000000000000000000',
+ '0x6fffd0ae3f7d88c9b4925323f54c6e4b2918c5fd',
+ '0x12459c951127e0c374ff9105dda097662a027093',
+ ];
+ validInputs.forEach(input =>
+ expect(assert.isETHAddressHex.bind(assert, variableName, input)).to.not.throw(),
+ );
+ });
+ it('should throw for invalid input', () => {
+ const invalidInputs = [
+ 42,
+ false,
+ { random: 'test' },
+ undefined,
+ new BigNumber(45),
+ '0x6FFFd0ae3f7d88c9b4925323f54c6e4b2918c5fd',
+ '0x6FFFd0ae3f7d88c9b4925323f54c6e4',
+ ];
+ invalidInputs.forEach(input => expect(assert.isETHAddressHex.bind(assert, variableName, input)).to.throw());
+ });
+ });
+ describe('#doesBelongToStringEnum', () => {
+ enum TestEnums {
+ Test1 = 'Test1',
+ Test2 = 'Test2',
+ }
+ it('should not throw for valid input', () => {
+ const validInputs = [TestEnums.Test1, TestEnums.Test2];
+ validInputs.forEach(input =>
+ expect(assert.doesBelongToStringEnum.bind(assert, variableName, input, TestEnums)).to.not.throw(),
+ );
+ });
+ it('should throw for invalid input', () => {
+ const invalidInputs = [42, false, { random: 'test' }, undefined, new BigNumber(45)];
+ invalidInputs.forEach(input =>
+ expect(assert.doesBelongToStringEnum.bind(assert, variableName, input, TestEnums)).to.throw(),
+ );
+ });
+ });
+ describe('#hasAtMostOneUniqueValue', () => {
+ const errorMsg = 'more than one unique value';
+ it('should not throw for valid input', () => {
+ const validInputs = [['hello'], ['goodbye', 'goodbye', 'goodbye']];
+ validInputs.forEach(input =>
+ expect(assert.hasAtMostOneUniqueValue.bind(assert, input, errorMsg)).to.not.throw(),
+ );
+ });
+ it('should throw for invalid input', () => {
+ const invalidInputs = [['hello', 'goodbye'], ['goodbye', 42, false, false]];
+ invalidInputs.forEach(input =>
+ expect(assert.hasAtMostOneUniqueValue.bind(assert, input, errorMsg)).to.throw(),
+ );
+ });
+ });
+ describe('#isNumber', () => {
+ it('should not throw for valid input', () => {
+ const validInputs = [42, 0, 21e42];
+ validInputs.forEach(input => expect(assert.isNumber.bind(assert, variableName, input)).to.not.throw());
+ });
+ it('should throw for invalid input', () => {
+ const invalidInputs = [false, { random: 'test' }, undefined, new BigNumber(45)];
+ invalidInputs.forEach(input => expect(assert.isNumber.bind(assert, variableName, input)).to.throw());
+ });
+ });
+ describe('#isBoolean', () => {
+ it('should not throw for valid input', () => {
+ const validInputs = [true, false];
+ validInputs.forEach(input => expect(assert.isBoolean.bind(assert, variableName, input)).to.not.throw());
+ });
+ it('should throw for invalid input', () => {
+ const invalidInputs = [42, { random: 'test' }, undefined, new BigNumber(45)];
+ invalidInputs.forEach(input => expect(assert.isBoolean.bind(assert, variableName, input)).to.throw());
+ });
+ });
+ describe('#isWeb3Provider', () => {
+ it('should not throw for valid input', () => {
+ const validInputs = [{ send: () => 45 }, { sendAsync: () => 45 }];
+ validInputs.forEach(input =>
+ expect(assert.isWeb3Provider.bind(assert, variableName, input)).to.not.throw(),
+ );
+ });
+ it('should throw for invalid input', () => {
+ const invalidInputs = [42, { random: 'test' }, undefined, new BigNumber(45)];
+ invalidInputs.forEach(input => expect(assert.isWeb3Provider.bind(assert, variableName, input)).to.throw());
+ });
+ });
+ describe('#doesConformToSchema', () => {
+ const schema = schemas.addressSchema;
+ it('should not throw for valid input', () => {
+ const validInputs = [
+ '0x6fffd0ae3f7d88c9b4925323f54c6e4b2918c5fd',
+ '0x12459c951127e0c374ff9105dda097662a027093',
+ ];
+ validInputs.forEach(input =>
+ expect(assert.doesConformToSchema.bind(assert, variableName, input, schema)).to.not.throw(),
+ );
+ });
+ it('should throw for invalid input', () => {
+ const invalidInputs = [42, { random: 'test' }, undefined, new BigNumber(45)];
+ invalidInputs.forEach(input =>
+ expect(assert.doesConformToSchema.bind(assert, variableName, input, schema)).to.throw(),
+ );
+ });
+ });
+ describe('#isHttpUrl', () => {
+ it('should not throw for valid input', () => {
+ const validInputs = [
+ 'http://www.google.com',
+ 'https://api.example-relayer.net',
+ 'https://api.radarrelay.com/0x/v0/',
+ 'https://zeroex.beta.radarrelay.com:8000/0x/v0/',
+ ];
+ validInputs.forEach(input => expect(assert.isHttpUrl.bind(assert, variableName, input)).to.not.throw());
+ });
+ it('should throw for invalid input', () => {
+ const invalidInputs = [
+ 42,
+ { random: 'test' },
+ undefined,
+ new BigNumber(45),
+ 'ws://www.api.example-relayer.net',
+ 'www.google.com',
+ 'api.example-relayer.net',
+ 'user:password@api.example-relayer.net',
+ '//api.example-relayer.net',
+ ];
+ invalidInputs.forEach(input => expect(assert.isHttpUrl.bind(assert, variableName, input)).to.throw());
+ });
+ });
+ describe('#isUri', () => {
+ it('should not throw for valid input', () => {
+ const validInputs = [
+ 'http://www.google.com',
+ 'https://api.example-relayer.net',
+ 'https://api.radarrelay.com/0x/v0/',
+ 'https://zeroex.beta.radarrelay.com:8000/0x/v0/',
+ 'ws://www.api.example-relayer.net',
+ 'wss://www.api.example-relayer.net',
+ 'user:password@api.example-relayer.net',
+ ];
+ validInputs.forEach(input => expect(assert.isUri.bind(assert, variableName, input)).to.not.throw());
+ });
+ it('should throw for invalid input', () => {
+ const invalidInputs = [
+ 42,
+ { random: 'test' },
+ undefined,
+ new BigNumber(45),
+ 'www.google.com',
+ 'api.example-relayer.net',
+ '//api.example-relayer.net',
+ ];
+ invalidInputs.forEach(input => expect(assert.isUri.bind(assert, variableName, input)).to.throw());
+ });
+ });
+ describe('#assert', () => {
+ const assertMessage = 'assert not satisfied';
+ it('should not throw for valid input', () => {
+ expect(assert.assert.bind(assert, true, assertMessage)).to.not.throw();
+ });
+ it('should throw for invalid input', () => {
+ expect(assert.assert.bind(assert, false, assertMessage)).to.throw();
+ });
+ });
+ describe('#typeAssertionMessage', () => {
+ it('should render correct message', () => {
+ expect(assert.typeAssertionMessage('variable', 'string', 'number')).to.equal(
+ `Expected variable to be of type string, encountered: number`,
+ );
+ });
+ });
});
diff --git a/packages/assert/tsconfig.json b/packages/assert/tsconfig.json
index 8314a9459..88a467ccb 100644
--- a/packages/assert/tsconfig.json
+++ b/packages/assert/tsconfig.json
@@ -1,7 +1,7 @@
{
- "extends": "../../tsconfig",
- "compilerOptions": {
- "outDir": "lib"
- },
- "include": ["./src/**/*", "./test/**/*", "../../node_modules/chai-typescript-typings/index.d.ts"]
+ "extends": "../../tsconfig",
+ "compilerOptions": {
+ "outDir": "lib"
+ },
+ "include": ["./src/**/*", "./test/**/*", "../../node_modules/chai-typescript-typings/index.d.ts"]
}
diff --git a/packages/assert/tslint.json b/packages/assert/tslint.json
index e63054bfc..ffaefe83a 100644
--- a/packages/assert/tslint.json
+++ b/packages/assert/tslint.json
@@ -1,3 +1,3 @@
{
- "extends": ["@0xproject/tslint-config"]
+ "extends": ["@0xproject/tslint-config"]
}
diff --git a/packages/chai-as-promised-typescript-typings/index.d.ts b/packages/chai-as-promised-typescript-typings/index.d.ts
index 389a076c6..ba6dabdcc 100644
--- a/packages/chai-as-promised-typescript-typings/index.d.ts
+++ b/packages/chai-as-promised-typescript-typings/index.d.ts
@@ -4,265 +4,265 @@
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
declare module 'chai-as-promised' {
- function chaiAsPromised(chai: any, utils: any): void;
- namespace chaiAsPromised {
+ function chaiAsPromised(chai: any, utils: any): void;
+ namespace chaiAsPromised {
- }
- export = chaiAsPromised;
+ }
+ export = chaiAsPromised;
}
// tslint:disable:no-namespace ban-types member-ordering
declare namespace Chai {
- // For BDD API
- interface Assertion extends LanguageChains, NumericComparison, TypeComparison {
- eventually: PromisedAssertion;
- fulfilled: PromisedAssertion;
- become(expected: any): PromisedAssertion;
- rejected(): PromisedAssertion;
- rejectedWith(expected: any, message?: string | RegExp): PromisedAssertion;
- notify(fn: Function): PromisedAssertion;
- }
-
- // Eventually does not have .then(), but PromisedAssertion have.
- interface Eventually extends PromisedLanguageChains, PromisedNumericComparison, PromisedTypeComparison {
- // From chai-as-promised
- become(expected: PromiseLike<any>): PromisedAssertion;
- fulfilled: PromisedAssertion;
- rejected: () => PromisedAssertion;
- rejectedWith(expected: any, message?: string | RegExp): PromisedAssertion;
- notify(fn: Function): PromisedAssertion;
-
- // From chai
- not: PromisedAssertion;
- deep: PromisedDeep;
- all: PromisedKeyFilter;
- a: PromisedTypeComparison;
- an: PromisedTypeComparison;
- include: PromisedInclude;
- contain: PromisedInclude;
- ok: PromisedAssertion;
- true: () => PromisedAssertion;
- false: () => PromisedAssertion;
- null: PromisedAssertion;
- undefined: PromisedAssertion;
- exist: PromisedAssertion;
- empty: PromisedAssertion;
- arguments: PromisedAssertion;
- Arguments: PromisedAssertion;
- equal: PromisedEqual;
- equals: PromisedEqual;
- eq: PromisedEqual;
- eql: PromisedEqual;
- eqls: PromisedEqual;
- property: PromisedProperty;
- ownProperty: PromisedOwnProperty;
- haveOwnProperty: PromisedOwnProperty;
- length: PromisedLength;
- lengthOf: PromisedLength;
- match(regexp: RegExp | string, message?: string): PromisedAssertion;
- string(string: string, message?: string): PromisedAssertion;
- keys: PromisedKeys;
- key(string: string): PromisedAssertion;
- throw: PromisedThrow;
- throws: PromisedThrow;
- Throw: PromisedThrow;
- respondTo(method: string, message?: string): PromisedAssertion;
- itself: PromisedAssertion;
- satisfy(matcher: Function, message?: string): PromisedAssertion;
- closeTo(expected: number, delta: number, message?: string): PromisedAssertion;
- members: PromisedMembers;
- }
-
- interface PromisedAssertion extends Eventually, PromiseLike<any> {}
-
- interface PromisedLanguageChains {
- eventually: Eventually;
-
- // From chai
- to: PromisedAssertion;
- be: PromisedAssertion;
- been: PromisedAssertion;
- is: PromisedAssertion;
- that: PromisedAssertion;
- which: PromisedAssertion;
- and: PromisedAssertion;
- has: PromisedAssertion;
- have: PromisedAssertion;
- with: PromisedAssertion;
- at: PromisedAssertion;
- of: PromisedAssertion;
- same: PromisedAssertion;
- }
-
- interface PromisedNumericComparison {
- above: PromisedNumberComparer;
- gt: PromisedNumberComparer;
- greaterThan: PromisedNumberComparer;
- least: PromisedNumberComparer;
- gte: PromisedNumberComparer;
- below: PromisedNumberComparer;
- lt: PromisedNumberComparer;
- lessThan: PromisedNumberComparer;
- most: PromisedNumberComparer;
- lte: PromisedNumberComparer;
- within(start: number, finish: number, message?: string): PromisedAssertion;
- }
-
- type PromisedNumberComparer = (value: number, message?: string) => PromisedAssertion;
-
- interface PromisedTypeComparison {
- (type: string, message?: string): PromisedAssertion;
- instanceof: PromisedInstanceOf;
- instanceOf: PromisedInstanceOf;
- }
-
- type PromisedInstanceOf = (constructor: Object, message?: string) => PromisedAssertion;
-
- interface PromisedDeep {
- equal: PromisedEqual;
- include: PromisedInclude;
- property: PromisedProperty;
- }
-
- interface PromisedKeyFilter {
- keys: PromisedKeys;
- }
-
- type PromisedEqual = (value: any, message?: string) => PromisedAssertion;
-
- type PromisedProperty = (name: string, value?: any, message?: string) => PromisedAssertion;
-
- type PromisedOwnProperty = (name: string, message?: string) => PromisedAssertion;
-
- interface PromisedLength extends PromisedLanguageChains, PromisedNumericComparison {
- (length: number, message?: string): PromisedAssertion;
- }
-
- interface PromisedInclude {
- (value: Object | string | number, message?: string): PromisedAssertion;
- keys: PromisedKeys;
- members: PromisedMembers;
- all: PromisedKeyFilter;
- }
-
- interface PromisedKeys {
- (...keys: string[]): PromisedAssertion;
- (keys: any[]): PromisedAssertion;
- }
-
- interface PromisedThrow {
- (): PromisedAssertion;
- (expected: string | RegExp, message?: string): PromisedAssertion;
- (constructor: Error | Function, expected?: string | RegExp, message?: string): PromisedAssertion;
- }
-
- type PromisedMembers = (set: any[], message?: string) => PromisedAssertion;
-
- // For Assert API
- interface Assert {
- eventually: PromisedAssert;
- isFulfilled(promise: PromiseLike<any>, message?: string): PromiseLike<void>;
- becomes(promise: PromiseLike<any>, expected: any, message?: string): PromiseLike<void>;
- doesNotBecome(promise: PromiseLike<any>, expected: any, message?: string): PromiseLike<void>;
- isRejected(promise: PromiseLike<any>, message?: string): PromiseLike<void>;
- isRejected(promise: PromiseLike<any>, expected: any | RegExp, message?: string): PromiseLike<void>;
- notify(fn: Function): PromiseLike<void>;
- }
-
- export interface PromisedAssert {
- fail(actual?: any, expected?: any, msg?: string, operator?: string): PromiseLike<void>;
-
- ok(val: any, msg?: string): PromiseLike<void>;
- notOk(val: any, msg?: string): PromiseLike<void>;
-
- equal(act: any, exp: any, msg?: string): PromiseLike<void>;
- notEqual(act: any, exp: any, msg?: string): PromiseLike<void>;
-
- strictEqual(act: any, exp: any, msg?: string): PromiseLike<void>;
- notStrictEqual(act: any, exp: any, msg?: string): PromiseLike<void>;
-
- deepEqual(act: any, exp: any, msg?: string): PromiseLike<void>;
- notDeepEqual(act: any, exp: any, msg?: string): PromiseLike<void>;
-
- isTrue(val: any, msg?: string): PromiseLike<void>;
- isFalse(val: any, msg?: string): PromiseLike<void>;
+ // For BDD API
+ interface Assertion extends LanguageChains, NumericComparison, TypeComparison {
+ eventually: PromisedAssertion;
+ fulfilled: PromisedAssertion;
+ become(expected: any): PromisedAssertion;
+ rejected(): PromisedAssertion;
+ rejectedWith(expected: any, message?: string | RegExp): PromisedAssertion;
+ notify(fn: Function): PromisedAssertion;
+ }
+
+ // Eventually does not have .then(), but PromisedAssertion have.
+ interface Eventually extends PromisedLanguageChains, PromisedNumericComparison, PromisedTypeComparison {
+ // From chai-as-promised
+ become(expected: PromiseLike<any>): PromisedAssertion;
+ fulfilled: PromisedAssertion;
+ rejected: () => PromisedAssertion;
+ rejectedWith(expected: any, message?: string | RegExp): PromisedAssertion;
+ notify(fn: Function): PromisedAssertion;
+
+ // From chai
+ not: PromisedAssertion;
+ deep: PromisedDeep;
+ all: PromisedKeyFilter;
+ a: PromisedTypeComparison;
+ an: PromisedTypeComparison;
+ include: PromisedInclude;
+ contain: PromisedInclude;
+ ok: PromisedAssertion;
+ true: () => PromisedAssertion;
+ false: () => PromisedAssertion;
+ null: PromisedAssertion;
+ undefined: PromisedAssertion;
+ exist: PromisedAssertion;
+ empty: PromisedAssertion;
+ arguments: PromisedAssertion;
+ Arguments: PromisedAssertion;
+ equal: PromisedEqual;
+ equals: PromisedEqual;
+ eq: PromisedEqual;
+ eql: PromisedEqual;
+ eqls: PromisedEqual;
+ property: PromisedProperty;
+ ownProperty: PromisedOwnProperty;
+ haveOwnProperty: PromisedOwnProperty;
+ length: PromisedLength;
+ lengthOf: PromisedLength;
+ match(regexp: RegExp | string, message?: string): PromisedAssertion;
+ string(string: string, message?: string): PromisedAssertion;
+ keys: PromisedKeys;
+ key(string: string): PromisedAssertion;
+ throw: PromisedThrow;
+ throws: PromisedThrow;
+ Throw: PromisedThrow;
+ respondTo(method: string, message?: string): PromisedAssertion;
+ itself: PromisedAssertion;
+ satisfy(matcher: Function, message?: string): PromisedAssertion;
+ closeTo(expected: number, delta: number, message?: string): PromisedAssertion;
+ members: PromisedMembers;
+ }
+
+ interface PromisedAssertion extends Eventually, PromiseLike<any> {}
+
+ interface PromisedLanguageChains {
+ eventually: Eventually;
+
+ // From chai
+ to: PromisedAssertion;
+ be: PromisedAssertion;
+ been: PromisedAssertion;
+ is: PromisedAssertion;
+ that: PromisedAssertion;
+ which: PromisedAssertion;
+ and: PromisedAssertion;
+ has: PromisedAssertion;
+ have: PromisedAssertion;
+ with: PromisedAssertion;
+ at: PromisedAssertion;
+ of: PromisedAssertion;
+ same: PromisedAssertion;
+ }
+
+ interface PromisedNumericComparison {
+ above: PromisedNumberComparer;
+ gt: PromisedNumberComparer;
+ greaterThan: PromisedNumberComparer;
+ least: PromisedNumberComparer;
+ gte: PromisedNumberComparer;
+ below: PromisedNumberComparer;
+ lt: PromisedNumberComparer;
+ lessThan: PromisedNumberComparer;
+ most: PromisedNumberComparer;
+ lte: PromisedNumberComparer;
+ within(start: number, finish: number, message?: string): PromisedAssertion;
+ }
+
+ type PromisedNumberComparer = (value: number, message?: string) => PromisedAssertion;
+
+ interface PromisedTypeComparison {
+ (type: string, message?: string): PromisedAssertion;
+ instanceof: PromisedInstanceOf;
+ instanceOf: PromisedInstanceOf;
+ }
+
+ type PromisedInstanceOf = (constructor: Object, message?: string) => PromisedAssertion;
+
+ interface PromisedDeep {
+ equal: PromisedEqual;
+ include: PromisedInclude;
+ property: PromisedProperty;
+ }
+
+ interface PromisedKeyFilter {
+ keys: PromisedKeys;
+ }
+
+ type PromisedEqual = (value: any, message?: string) => PromisedAssertion;
+
+ type PromisedProperty = (name: string, value?: any, message?: string) => PromisedAssertion;
+
+ type PromisedOwnProperty = (name: string, message?: string) => PromisedAssertion;
+
+ interface PromisedLength extends PromisedLanguageChains, PromisedNumericComparison {
+ (length: number, message?: string): PromisedAssertion;
+ }
+
+ interface PromisedInclude {
+ (value: Object | string | number, message?: string): PromisedAssertion;
+ keys: PromisedKeys;
+ members: PromisedMembers;
+ all: PromisedKeyFilter;
+ }
+
+ interface PromisedKeys {
+ (...keys: string[]): PromisedAssertion;
+ (keys: any[]): PromisedAssertion;
+ }
+
+ interface PromisedThrow {
+ (): PromisedAssertion;
+ (expected: string | RegExp, message?: string): PromisedAssertion;
+ (constructor: Error | Function, expected?: string | RegExp, message?: string): PromisedAssertion;
+ }
+
+ type PromisedMembers = (set: any[], message?: string) => PromisedAssertion;
+
+ // For Assert API
+ interface Assert {
+ eventually: PromisedAssert;
+ isFulfilled(promise: PromiseLike<any>, message?: string): PromiseLike<void>;
+ becomes(promise: PromiseLike<any>, expected: any, message?: string): PromiseLike<void>;
+ doesNotBecome(promise: PromiseLike<any>, expected: any, message?: string): PromiseLike<void>;
+ isRejected(promise: PromiseLike<any>, message?: string): PromiseLike<void>;
+ isRejected(promise: PromiseLike<any>, expected: any | RegExp, message?: string): PromiseLike<void>;
+ notify(fn: Function): PromiseLike<void>;
+ }
+
+ export interface PromisedAssert {
+ fail(actual?: any, expected?: any, msg?: string, operator?: string): PromiseLike<void>;
+
+ ok(val: any, msg?: string): PromiseLike<void>;
+ notOk(val: any, msg?: string): PromiseLike<void>;
+
+ equal(act: any, exp: any, msg?: string): PromiseLike<void>;
+ notEqual(act: any, exp: any, msg?: string): PromiseLike<void>;
+
+ strictEqual(act: any, exp: any, msg?: string): PromiseLike<void>;
+ notStrictEqual(act: any, exp: any, msg?: string): PromiseLike<void>;
+
+ deepEqual(act: any, exp: any, msg?: string): PromiseLike<void>;
+ notDeepEqual(act: any, exp: any, msg?: string): PromiseLike<void>;
+
+ isTrue(val: any, msg?: string): PromiseLike<void>;
+ isFalse(val: any, msg?: string): PromiseLike<void>;
- isNull(val: any, msg?: string): PromiseLike<void>;
- isNotNull(val: any, msg?: string): PromiseLike<void>;
+ isNull(val: any, msg?: string): PromiseLike<void>;
+ isNotNull(val: any, msg?: string): PromiseLike<void>;
- isUndefined(val: any, msg?: string): PromiseLike<void>;
- isDefined(val: any, msg?: string): PromiseLike<void>;
+ isUndefined(val: any, msg?: string): PromiseLike<void>;
+ isDefined(val: any, msg?: string): PromiseLike<void>;
- isFunction(val: any, msg?: string): PromiseLike<void>;
- isNotFunction(val: any, msg?: string): PromiseLike<void>;
+ isFunction(val: any, msg?: string): PromiseLike<void>;
+ isNotFunction(val: any, msg?: string): PromiseLike<void>;
- isObject(val: any, msg?: string): PromiseLike<void>;
- isNotObject(val: any, msg?: string): PromiseLike<void>;
+ isObject(val: any, msg?: string): PromiseLike<void>;
+ isNotObject(val: any, msg?: string): PromiseLike<void>;
- isArray(val: any, msg?: string): PromiseLike<void>;
- isNotArray(val: any, msg?: string): PromiseLike<void>;
+ isArray(val: any, msg?: string): PromiseLike<void>;
+ isNotArray(val: any, msg?: string): PromiseLike<void>;
- isString(val: any, msg?: string): PromiseLike<void>;
- isNotString(val: any, msg?: string): PromiseLike<void>;
+ isString(val: any, msg?: string): PromiseLike<void>;
+ isNotString(val: any, msg?: string): PromiseLike<void>;
- isNumber(val: any, msg?: string): PromiseLike<void>;
- isNotNumber(val: any, msg?: string): PromiseLike<void>;
+ isNumber(val: any, msg?: string): PromiseLike<void>;
+ isNotNumber(val: any, msg?: string): PromiseLike<void>;
- isBoolean(val: any, msg?: string): PromiseLike<void>;
- isNotBoolean(val: any, msg?: string): PromiseLike<void>;
+ isBoolean(val: any, msg?: string): PromiseLike<void>;
+ isNotBoolean(val: any, msg?: string): PromiseLike<void>;
- typeOf(val: any, type: string, msg?: string): PromiseLike<void>;
- notTypeOf(val: any, type: string, msg?: string): PromiseLike<void>;
+ typeOf(val: any, type: string, msg?: string): PromiseLike<void>;
+ notTypeOf(val: any, type: string, msg?: string): PromiseLike<void>;
- instanceOf(val: any, type: Function, msg?: string): PromiseLike<void>;
- notInstanceOf(val: any, type: Function, msg?: string): PromiseLike<void>;
+ instanceOf(val: any, type: Function, msg?: string): PromiseLike<void>;
+ notInstanceOf(val: any, type: Function, msg?: string): PromiseLike<void>;
- include(exp: string | any[], inc: any, msg?: string): PromiseLike<void>;
+ include(exp: string | any[], inc: any, msg?: string): PromiseLike<void>;
- notInclude(exp: string | any[], inc: any, msg?: string): PromiseLike<void>;
+ notInclude(exp: string | any[], inc: any, msg?: string): PromiseLike<void>;
- match(exp: any, re: RegExp, msg?: string): PromiseLike<void>;
- notMatch(exp: any, re: RegExp, msg?: string): PromiseLike<void>;
+ match(exp: any, re: RegExp, msg?: string): PromiseLike<void>;
+ notMatch(exp: any, re: RegExp, msg?: string): PromiseLike<void>;
- property(obj: Object, prop: string, msg?: string): PromiseLike<void>;
- notProperty(obj: Object, prop: string, msg?: string): PromiseLike<void>;
- deepProperty(obj: Object, prop: string, msg?: string): PromiseLike<void>;
- notDeepProperty(obj: Object, prop: string, msg?: string): PromiseLike<void>;
+ property(obj: Object, prop: string, msg?: string): PromiseLike<void>;
+ notProperty(obj: Object, prop: string, msg?: string): PromiseLike<void>;
+ deepProperty(obj: Object, prop: string, msg?: string): PromiseLike<void>;
+ notDeepProperty(obj: Object, prop: string, msg?: string): PromiseLike<void>;
- propertyVal(obj: Object, prop: string, val: any, msg?: string): PromiseLike<void>;
- propertyNotVal(obj: Object, prop: string, val: any, msg?: string): PromiseLike<void>;
+ propertyVal(obj: Object, prop: string, val: any, msg?: string): PromiseLike<void>;
+ propertyNotVal(obj: Object, prop: string, val: any, msg?: string): PromiseLike<void>;
- deepPropertyVal(obj: Object, prop: string, val: any, msg?: string): PromiseLike<void>;
- deepPropertyNotVal(obj: Object, prop: string, val: any, msg?: string): PromiseLike<void>;
+ deepPropertyVal(obj: Object, prop: string, val: any, msg?: string): PromiseLike<void>;
+ deepPropertyNotVal(obj: Object, prop: string, val: any, msg?: string): PromiseLike<void>;
- lengthOf(exp: any, len: number, msg?: string): PromiseLike<void>;
- // alias frenzy
- throw(fn: Function, msg?: string): PromiseLike<void>;
- throw(fn: Function, regExp: RegExp): PromiseLike<void>;
- throw(fn: Function, errType: Function, msg?: string): PromiseLike<void>;
- throw(fn: Function, errType: Function, regExp: RegExp): PromiseLike<void>;
+ lengthOf(exp: any, len: number, msg?: string): PromiseLike<void>;
+ // alias frenzy
+ throw(fn: Function, msg?: string): PromiseLike<void>;
+ throw(fn: Function, regExp: RegExp): PromiseLike<void>;
+ throw(fn: Function, errType: Function, msg?: string): PromiseLike<void>;
+ throw(fn: Function, errType: Function, regExp: RegExp): PromiseLike<void>;
- throws(fn: Function, msg?: string): PromiseLike<void>;
- throws(fn: Function, regExp: RegExp): PromiseLike<void>;
- throws(fn: Function, errType: Function, msg?: string): PromiseLike<void>;
- throws(fn: Function, errType: Function, regExp: RegExp): PromiseLike<void>;
+ throws(fn: Function, msg?: string): PromiseLike<void>;
+ throws(fn: Function, regExp: RegExp): PromiseLike<void>;
+ throws(fn: Function, errType: Function, msg?: string): PromiseLike<void>;
+ throws(fn: Function, errType: Function, regExp: RegExp): PromiseLike<void>;
- Throw(fn: Function, msg?: string): PromiseLike<void>;
- Throw(fn: Function, regExp: RegExp): PromiseLike<void>;
- Throw(fn: Function, errType: Function, msg?: string): PromiseLike<void>;
- Throw(fn: Function, errType: Function, regExp: RegExp): PromiseLike<void>;
+ Throw(fn: Function, msg?: string): PromiseLike<void>;
+ Throw(fn: Function, regExp: RegExp): PromiseLike<void>;
+ Throw(fn: Function, errType: Function, msg?: string): PromiseLike<void>;
+ Throw(fn: Function, errType: Function, regExp: RegExp): PromiseLike<void>;
- doesNotThrow(fn: Function, msg?: string): PromiseLike<void>;
- doesNotThrow(fn: Function, regExp: RegExp): PromiseLike<void>;
- doesNotThrow(fn: Function, errType: Function, msg?: string): PromiseLike<void>;
- doesNotThrow(fn: Function, errType: Function, regExp: RegExp): PromiseLike<void>;
+ doesNotThrow(fn: Function, msg?: string): PromiseLike<void>;
+ doesNotThrow(fn: Function, regExp: RegExp): PromiseLike<void>;
+ doesNotThrow(fn: Function, errType: Function, msg?: string): PromiseLike<void>;
+ doesNotThrow(fn: Function, errType: Function, regExp: RegExp): PromiseLike<void>;
- operator(val: any, operator: string, val2: any, msg?: string): PromiseLike<void>;
- closeTo(act: number, exp: number, delta: number, msg?: string): PromiseLike<void>;
+ operator(val: any, operator: string, val2: any, msg?: string): PromiseLike<void>;
+ closeTo(act: number, exp: number, delta: number, msg?: string): PromiseLike<void>;
- sameMembers(set1: any[], set2: any[], msg?: string): PromiseLike<void>;
- includeMembers(set1: any[], set2: any[], msg?: string): PromiseLike<void>;
+ sameMembers(set1: any[], set2: any[], msg?: string): PromiseLike<void>;
+ includeMembers(set1: any[], set2: any[], msg?: string): PromiseLike<void>;
- ifError(val: any, msg?: string): PromiseLike<void>;
- }
+ ifError(val: any, msg?: string): PromiseLike<void>;
+ }
}
diff --git a/packages/chai-as-promised-typescript-typings/package.json b/packages/chai-as-promised-typescript-typings/package.json
index 77985de05..bea3d273d 100644
--- a/packages/chai-as-promised-typescript-typings/package.json
+++ b/packages/chai-as-promised-typescript-typings/package.json
@@ -1,21 +1,21 @@
{
- "name": "chai-as-promised-typescript-typings",
- "version": "0.0.6",
- "description": "Typescript type definitions for chai-as-promised",
- "main": "index.d.ts",
- "types": "index.d.ts",
- "repository": {
- "type": "git",
- "url": "git+https://github.com/0xProject/0x.js.git"
- },
- "author": "Fabio Berger",
- "contributors": ["Leonid Logvinov <logvinov.leon@gmail.com>"],
- "license": "Apache-2.0",
- "bugs": {
- "url": "https://github.com/0xProject/0x.js/issues"
- },
- "homepage": "https://github.com/0xProject/0x.js/packages/chai-as-promised-typescript-typings#readme",
- "dependencies": {
- "chai-typescript-typings": "^0.0.0"
- }
+ "name": "chai-as-promised-typescript-typings",
+ "version": "0.0.6",
+ "description": "Typescript type definitions for chai-as-promised",
+ "main": "index.d.ts",
+ "types": "index.d.ts",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/0xProject/0x.js.git"
+ },
+ "author": "Fabio Berger",
+ "contributors": ["Leonid Logvinov <logvinov.leon@gmail.com>"],
+ "license": "Apache-2.0",
+ "bugs": {
+ "url": "https://github.com/0xProject/0x.js/issues"
+ },
+ "homepage": "https://github.com/0xProject/0x.js/packages/chai-as-promised-typescript-typings#readme",
+ "dependencies": {
+ "chai-typescript-typings": "^0.0.0"
+ }
}
diff --git a/packages/chai-as-promised-typescript-typings/tslint.json b/packages/chai-as-promised-typescript-typings/tslint.json
index ef528b22e..9a93a1f74 100644
--- a/packages/chai-as-promised-typescript-typings/tslint.json
+++ b/packages/chai-as-promised-typescript-typings/tslint.json
@@ -1,3 +1,3 @@
{
- "extends": ["tslint-config-0xproject"]
+ "extends": ["tslint-config-0xproject"]
}
diff --git a/packages/chai-typescript-typings/index.d.ts b/packages/chai-typescript-typings/index.d.ts
index 73d87a887..8b3e4c079 100644
--- a/packages/chai-typescript-typings/index.d.ts
+++ b/packages/chai-typescript-typings/index.d.ts
@@ -12,1243 +12,1243 @@
// tslint:disable:no-namespace member-ordering ban-types unified-signatures variable-name callable-types
declare namespace Chai {
- interface ChaiStatic {
- expect: ExpectStatic;
- should(): Should;
- /**
- * Provides a way to extend the internals of Chai
- */
- use(fn: (chai: any, utils: any) => void): ChaiStatic;
- assert: AssertStatic;
- config: Config;
- AssertionError: typeof AssertionError;
- version: string;
- }
-
- export interface ExpectStatic extends AssertionStatic {
- fail(actual?: any, expected?: any, message?: string, operator?: Operator): void;
- }
-
- export interface AssertStatic extends Assert {}
-
- type AssertionStatic = (target: any, message?: string) => Assertion;
-
- export type Operator = string; // "==" | "===" | ">" | ">=" | "<" | "<=" | "!=" | "!==";
-
- export type OperatorComparable = boolean | null | number | string | undefined | Date;
-
- interface ShouldAssertion {
- equal(value1: any, value2: any, message?: string): void;
- Throw: ShouldThrow;
- throw: ShouldThrow;
- exist(value: any, message?: string): void;
- }
-
- interface Should extends ShouldAssertion {
- not: ShouldAssertion;
- fail(actual: any, expected: any, message?: string, operator?: Operator): void;
- }
-
- interface ShouldThrow {
- (actual: Function): void;
- (actual: Function, expected: string | RegExp, message?: string): void;
- (actual: Function, constructor: Error | Function, expected?: string | RegExp, message?: string): void;
- }
-
- interface Assertion extends LanguageChains, NumericComparison, TypeComparison {
- not: Assertion;
- deep: Deep;
- nested: Nested;
- any: KeyFilter;
- all: KeyFilter;
- a: TypeComparison;
- an: TypeComparison;
- include: Include;
- includes: Include;
- contain: Include;
- contains: Include;
- ok: Assertion;
- true: () => Assertion;
- false: () => Assertion;
- null: () => Assertion;
- undefined: () => Assertion;
- NaN: Assertion;
- exist: Assertion;
- empty: Assertion;
- arguments: Assertion;
- Arguments: Assertion;
- equal: Equal;
- equals: Equal;
- eq: Equal;
- eql: Equal;
- eqls: Equal;
- property: Property;
- ownProperty: OwnProperty;
- haveOwnProperty: OwnProperty;
- ownPropertyDescriptor: OwnPropertyDescriptor;
- haveOwnPropertyDescriptor: OwnPropertyDescriptor;
- length: Length;
- lengthOf: Length;
- match: Match;
- matches: Match;
- string(string: string, message?: string): Assertion;
- keys: Keys;
- key(string: string): Assertion;
- throw: (message?: string) => Assertion;
- throws: Throw;
- Throw: Throw;
- respondTo: RespondTo;
- respondsTo: RespondTo;
- itself: Assertion;
- satisfy: Satisfy;
- satisfies: Satisfy;
- closeTo: CloseTo;
- approximately: CloseTo;
- members: Members;
- increase: PropertyChange;
- increases: PropertyChange;
- decrease: PropertyChange;
- decreases: PropertyChange;
- change: PropertyChange;
- changes: PropertyChange;
- extensible: Assertion;
- sealed: Assertion;
- frozen: Assertion;
- oneOf(list: any[], message?: string): Assertion;
- }
-
- interface LanguageChains {
- to: Assertion;
- be: Assertion;
- been: Assertion;
- is: Assertion;
- that: Assertion;
- which: Assertion;
- and: Assertion;
- has: Assertion;
- have: Assertion;
- with: Assertion;
- at: Assertion;
- of: Assertion;
- same: Assertion;
- }
-
- interface NumericComparison {
- above: NumberComparer;
- gt: NumberComparer;
- greaterThan: NumberComparer;
- least: NumberComparer;
- gte: NumberComparer;
- below: NumberComparer;
- lt: NumberComparer;
- lessThan: NumberComparer;
- most: NumberComparer;
- lte: NumberComparer;
- within(start: number, finish: number, message?: string): Assertion;
- }
-
- interface NumberComparer {
- (value: number, message?: string): Assertion;
- }
-
- interface TypeComparison {
- (type: string, message?: string): Assertion;
- instanceof: InstanceOf;
- instanceOf: InstanceOf;
- }
-
- interface InstanceOf {
- (constructor: Object, message?: string): Assertion;
- }
-
- interface CloseTo {
- (expected: number, delta: number, message?: string): Assertion;
- }
-
- interface Nested {
- include: Include;
- property: Property;
- members: Members;
- }
-
- interface Deep {
- equal: Equal;
- equals: Equal;
- eq: Equal;
- include: Include;
- property: Property;
- members: Members;
- }
-
- interface KeyFilter {
- keys: Keys;
- }
-
- interface Equal {
- (value: any, message?: string): Assertion;
- }
-
- interface Property {
- (name: string, value?: any, message?: string): Assertion;
- }
-
- interface OwnProperty {
- (name: string, message?: string): Assertion;
- }
-
- interface OwnPropertyDescriptor {
- (name: string, descriptor: PropertyDescriptor, message?: string): Assertion;
- (name: string, message?: string): Assertion;
- }
-
- interface Length extends LanguageChains, NumericComparison {
- (length: number, message?: string): Assertion;
- }
-
- interface Include {
- (value: Object | string | number, message?: string): Assertion;
- keys: Keys;
- members: Members;
- any: KeyFilter;
- all: KeyFilter;
- }
-
- interface Match {
- (regexp: RegExp | string, message?: string): Assertion;
- }
-
- interface Keys {
- (...keys: string[]): Assertion;
- (keys: any[]): Assertion;
- (keys: Object): Assertion;
- }
-
- interface Throw {
- (): Assertion;
- (expected: string, message?: string): Assertion;
- (expected: RegExp, message?: string): Assertion;
- (constructor: Error, expected?: string, message?: string): Assertion;
- (constructor: Error, expected?: RegExp, message?: string): Assertion;
- (constructor: Function, expected?: string, message?: string): Assertion;
- (constructor: Function, expected?: RegExp, message?: string): Assertion;
- }
-
- interface RespondTo {
- (method: string, message?: string): Assertion;
- }
-
- interface Satisfy {
- (matcher: Function, message?: string): Assertion;
- }
-
- interface Members {
- (set: any[], message?: string): Assertion;
- }
-
- interface PropertyChange {
- (object: Object, property: string, message?: string): Assertion;
- }
-
- export interface Assert {
- /**
- * @param expression Expression to test for truthiness.
- * @param message Message to display on error.
- */
- (expression: any, message?: string): void;
-
- /**
- * Throws a failure.
- *
- * @type T Type of the objects.
- * @param actual Actual value.
- * @param expected Potential expected value.
- * @param message Message to display on error.
- * @param operator Comparison operator, if not strict equality.
- * @remarks Node.js assert module-compatible.
- */
- fail<T>(actual?: T, expected?: T, message?: string, operator?: Operator): void;
-
- /**
- * Asserts that object is truthy.
- *
- * @type T Type of object.
- * @param object Object to test.
- * @param message Message to display on error.
- */
- isOk<T>(value: T, message?: string): void;
-
- /**
- * Asserts that object is truthy.
- *
- * @type T Type of object.
- * @param object Object to test.
- * @param message Message to display on error.
- */
- ok<T>(value: T, message?: string): void;
-
- /**
- * Asserts that object is falsy.
- *
- * @type T Type of object.
- * @param object Object to test.
- * @param message Message to display on error.
- */
- isNotOk<T>(value: T, message?: string): void;
-
- /**
- * Asserts that object is falsy.
- *
- * @type T Type of object.
- * @param object Object to test.
- * @param message Message to display on error.
- */
- notOk<T>(value: T, message?: string): void;
-
- /**
- * Asserts non-strict equality (==) of actual and expected.
- *
- * @type T Type of the objects.
- * @param actual Actual value.
- * @param expected Potential expected value.
- * @param message Message to display on error.
- */
- equal<T>(actual: T, expected: T, message?: string): void;
-
- /**
- * Asserts non-strict inequality (==) of actual and expected.
- *
- * @type T Type of the objects.
- * @param actual Actual value.
- * @param expected Potential expected value.
- * @param message Message to display on error.
- */
- notEqual<T>(actual: T, expected: T, message?: string): void;
-
- /**
- * Asserts strict equality (===) of actual and expected.
- *
- * @type T Type of the objects.
- * @param actual Actual value.
- * @param expected Potential expected value.
- * @param message Message to display on error.
- */
- strictEqual<T>(actual: T, expected: T, message?: string): void;
-
- /**
- * Asserts strict inequality (==) of actual and expected.
- *
- * @type T Type of the objects.
- * @param actual Actual value.
- * @param expected Potential expected value.
- * @param message Message to display on error.
- */
- notStrictEqual<T>(actual: T, expected: T, message?: string): void;
-
- /**
- * Asserts that actual is deeply equal to expected.
- *
- * @type T Type of the objects.
- * @param actual Actual value.
- * @param expected Potential expected value.
- * @param message Message to display on error.
- */
- deepEqual<T>(actual: T, expected: T, message?: string): void;
-
- /**
- * Asserts that actual is not deeply equal to expected.
- *
- * @type T Type of the objects.
- * @param actual Actual value.
- * @param expected Potential expected value.
- * @param message Message to display on error.
- */
- notDeepEqual<T>(actual: T, expected: T, message?: string): void;
-
- /**
- * Asserts valueToCheck is strictly greater than (>) valueToBeAbove.
- *
- * @param valueToCheck Actual value.
- * @param valueToBeAbove Minimum Potential expected value.
- * @param message Message to display on error.
- */
- isAbove(valueToCheck: number, valueToBeAbove: number, message?: string): void;
-
- /**
- * Asserts valueToCheck is greater than or equal to (>=) valueToBeAtLeast.
- *
- * @param valueToCheck Actual value.
- * @param valueToBeAtLeast Minimum Potential expected value.
- * @param message Message to display on error.
- */
- isAtLeast(valueToCheck: number, valueToBeAtLeast: number, message?: string): void;
-
- /**
- * Asserts valueToCheck is strictly less than (<) valueToBeBelow.
- *
- * @param valueToCheck Actual value.
- * @param valueToBeBelow Minimum Potential expected value.
- * @param message Message to display on error.
- */
- isBelow(valueToCheck: number, valueToBeBelow: number, message?: string): void;
-
- /**
- * Asserts valueToCheck is greater than or equal to (>=) valueToBeAtMost.
- *
- * @param valueToCheck Actual value.
- * @param valueToBeAtMost Minimum Potential expected value.
- * @param message Message to display on error.
- */
- isAtMost(valueToCheck: number, valueToBeAtMost: number, message?: string): void;
-
- /**
- * Asserts that value is true.
- *
- * @type T Type of value.
- * @param value Actual value.
- * @param message Message to display on error.
- */
- isTrue<T>(value: T, message?: string): void;
-
- /**
- * Asserts that value is false.
- *
- * @type T Type of value.
- * @param value Actual value.
- * @param message Message to display on error.
- */
- isFalse<T>(value: T, message?: string): void;
-
- /**
- * Asserts that value is not true.
- *
- * @type T Type of value.
- * @param value Actual value.
- * @param message Message to display on error.
- */
- isNotTrue<T>(value: T, message?: string): void;
-
- /**
- * Asserts that value is not false.
- *
- * @type T Type of value.
- * @param value Actual value.
- * @param message Message to display on error.
- */
- isNotFalse<T>(value: T, message?: string): void;
-
- /**
- * Asserts that value is null.
- *
- * @type T Type of value.
- * @param value Actual value.
- * @param message Message to display on error.
- */
- isNull<T>(value: T, message?: string): void;
-
- /**
- * Asserts that value is not null.
- *
- * @type T Type of value.
- * @param value Actual value.
- * @param message Message to display on error.
- */
- isNotNull<T>(value: T, message?: string): void;
-
- /**
- * Asserts that value is not null.
- *
- * @type T Type of value.
- * @param value Actual value.
- * @param message Message to display on error.
- */
- isNaN<T>(value: T, message?: string): void;
-
- /**
- * Asserts that value is not null.
- *
- * @type T Type of value.
- * @param value Actual value.
- * @param message Message to display on error.
- */
- isNotNaN<T>(value: T, message?: string): void;
-
- /**
- * Asserts that value is undefined.
- *
- * @type T Type of value.
- * @param value Actual value.
- * @param message Message to display on error.
- */
- isUndefined<T>(value: T, message?: string): void;
-
- /**
- * Asserts that value is not undefined.
- *
- * @type T Type of value.
- * @param value Actual value.
- * @param message Message to display on error.
- */
- isDefined<T>(value: T, message?: string): void;
-
- /**
- * Asserts that value is a function.
- *
- * @type T Type of value.
- * @param value Actual value.
- * @param message Message to display on error.
- */
- isFunction<T>(value: T, message?: string): void;
-
- /**
- * Asserts that value is not a function.
- *
- * @type T Type of value.
- * @param value Actual value.
- * @param message Message to display on error.
- */
- isNotFunction<T>(value: T, message?: string): void;
-
- /**
- * Asserts that value is an object of type 'Object'
- * (as revealed by Object.prototype.toString).
- *
- * @type T Type of value.
- * @param value Actual value.
- * @param message Message to display on error.
- * @remarks The assertion does not match subclassed objects.
- */
- isObject<T>(value: T, message?: string): void;
-
- /**
- * Asserts that value is not an object of type 'Object'
- * (as revealed by Object.prototype.toString).
- *
- * @type T Type of value.
- * @param value Actual value.
- * @param message Message to display on error.
- */
- isNotObject<T>(value: T, message?: string): void;
-
- /**
- * Asserts that value is an array.
- *
- * @type T Type of value.
- * @param value Actual value.
- * @param message Message to display on error.
- */
- isArray<T>(value: T, message?: string): void;
-
- /**
- * Asserts that value is not an array.
- *
- * @type T Type of value.
- * @param value Actual value.
- * @param message Message to display on error.
- */
- isNotArray<T>(value: T, message?: string): void;
-
- /**
- * Asserts that value is a string.
- *
- * @type T Type of value.
- * @param value Actual value.
- * @param message Message to display on error.
- */
- isString<T>(value: T, message?: string): void;
-
- /**
- * Asserts that value is not a string.
- *
- * @type T Type of value.
- * @param value Actual value.
- * @param message Message to display on error.
- */
- isNotString<T>(value: T, message?: string): void;
-
- /**
- * Asserts that value is a number.
- *
- * @type T Type of value.
- * @param value Actual value.
- * @param message Message to display on error.
- */
- isNumber<T>(value: T, message?: string): void;
-
- /**
- * Asserts that value is not a number.
- *
- * @type T Type of value.
- * @param value Actual value.
- * @param message Message to display on error.
- */
- isNotNumber<T>(value: T, message?: string): void;
-
- /**
- * Asserts that value is a boolean.
- *
- * @type T Type of value.
- * @param value Actual value.
- * @param message Message to display on error.
- */
- isBoolean<T>(value: T, message?: string): void;
-
- /**
- * Asserts that value is not a boolean.
- *
- * @type T Type of value.
- * @param value Actual value.
- * @param message Message to display on error.
- */
- isNotBoolean<T>(value: T, message?: string): void;
-
- /**
- * Asserts that value's type is name, as determined by Object.prototype.toString.
- *
- * @type T Type of value.
- * @param value Actual value.
- * @param name Potential expected type name of value.
- * @param message Message to display on error.
- */
- typeOf<T>(value: T, name: string, message?: string): void;
-
- /**
- * Asserts that value's type is not name, as determined by Object.prototype.toString.
- *
- * @type T Type of value.
- * @param value Actual value.
- * @param name Potential expected type name of value.
- * @param message Message to display on error.
- */
- notTypeOf<T>(value: T, name: string, message?: string): void;
-
- /**
- * Asserts that value is an instance of constructor.
- *
- * @type T Type of value.
- * @param value Actual value.
- * @param constructor Potential expected contructor of value.
- * @param message Message to display on error.
- */
- instanceOf<T>(value: T, constructor: Function, message?: string): void;
-
- /**
- * Asserts that value is not an instance of constructor.
- *
- * @type T Type of value.
- * @param value Actual value.
- * @param constructor Potential expected contructor of value.
- * @param message Message to display on error.
- */
- notInstanceOf<T>(value: T, type: Function, message?: string): void;
-
- /**
- * Asserts that haystack includes needle.
- *
- * @param haystack Container string.
- * @param needle Potential expected substring of haystack.
- * @param message Message to display on error.
- */
- include(haystack: string, needle: string, message?: string): void;
-
- /**
- * Asserts that haystack includes needle.
- *
- * @type T Type of values in haystack.
- * @param haystack Container array.
- * @param needle Potential value contained in haystack.
- * @param message Message to display on error.
- */
- include<T>(haystack: T[], needle: T, message?: string): void;
-
- /**
- * Asserts that haystack does not include needle.
- *
- * @param haystack Container string.
- * @param needle Potential expected substring of haystack.
- * @param message Message to display on error.
- */
- notInclude(haystack: string, needle: any, message?: string): void;
-
- /**
- * Asserts that haystack does not include needle.
- *
- * @type T Type of values in haystack.
- * @param haystack Container array.
- * @param needle Potential value contained in haystack.
- * @param message Message to display on error.
- */
- notInclude(haystack: any[], needle: any, message?: string): void;
-
- /**
- * Asserts that value matches the regular expression regexp.
- *
- * @param value Actual value.
- * @param regexp Potential match of value.
- * @param message Message to display on error.
- */
- match(value: string, regexp: RegExp, message?: string): void;
-
- /**
- * Asserts that value does not match the regular expression regexp.
- *
- * @param value Actual value.
- * @param regexp Potential match of value.
- * @param message Message to display on error.
- */
- notMatch(expected: any, regexp: RegExp, message?: string): void;
-
- /**
- * Asserts that object has a property named by property.
- *
- * @type T Type of object.
- * @param object Container object.
- * @param property Potential contained property of object.
- * @param message Message to display on error.
- */
- property<T>(object: T, property: string /* keyof T */, message?: string): void;
-
- /**
- * Asserts that object has a property named by property.
- *
- * @type T Type of object.
- * @param object Container object.
- * @param property Potential contained property of object.
- * @param message Message to display on error.
- */
- notProperty<T>(object: T, property: string /* keyof T */, message?: string): void;
-
- /**
- * Asserts that object has a property named by property, which can be a string
- * using dot- and bracket-notation for deep reference.
- *
- * @type T Type of object.
- * @param object Container object.
- * @param property Potential contained property of object.
- * @param message Message to display on error.
- */
- deepProperty<T>(object: T, property: string, message?: string): void;
-
- /**
- * Asserts that object does not have a property named by property, which can be a
- * string using dot- and bracket-notation for deep reference.
- *
- * @type T Type of object.
- * @param object Container object.
- * @param property Potential contained property of object.
- * @param message Message to display on error.
- */
- notDeepProperty<T>(object: T, property: string, message?: string): void;
-
- /**
- * Asserts that object has a property named by property with value given by value.
- *
- * @type T Type of object.
- * @type V Type of value.
- * @param object Container object.
- * @param property Potential contained property of object.
- * @param value Potential expected property value.
- * @param message Message to display on error.
- */
- propertyVal<T, V>(object: T, property: string /* keyof T */, value: V, message?: string): void;
-
- /**
- * Asserts that object has a property named by property with value given by value.
- *
- * @type T Type of object.
- * @type V Type of value.
- * @param object Container object.
- * @param property Potential contained property of object.
- * @param value Potential expected property value.
- * @param message Message to display on error.
- */
- propertyNotVal<T, V>(object: T, property: string /* keyof T */, value: V, message?: string): void;
-
- /**
- * Asserts that object has a property named by property, which can be a string
- * using dot- and bracket-notation for deep reference.
- *
- * @type T Type of object.
- * @type V Type of value.
- * @param object Container object.
- * @param property Potential contained property of object.
- * @param value Potential expected property value.
- * @param message Message to display on error.
- */
- deepPropertyVal<T, V>(object: T, property: string, value: V, message?: string): void;
-
- /**
- * Asserts that object does not have a property named by property, which can be a
- * string using dot- and bracket-notation for deep reference.
- *
- * @type T Type of object.
- * @type V Type of value.
- * @param object Container object.
- * @param property Potential contained property of object.
- * @param value Potential expected property value.
- * @param message Message to display on error.
- */
- deepPropertyNotVal<T, V>(object: T, property: string, value: V, message?: string): void;
-
- /**
- * Asserts that object has a length property with the expected value.
- *
- * @type T Type of object.
- * @param object Container object.
- * @param length Potential expected length of object.
- * @param message Message to display on error.
- */
- lengthOf<T extends { readonly length?: number }>(object: T, length: number, message?: string): void;
-
- /**
- * Asserts that fn will throw an error.
- *
- * @param fn Function that may throw.
- * @param message Message to display on error.
- */
- throw(fn: Function, message?: string): void;
-
- /**
- * Asserts that function will throw an error with message matching regexp.
- *
- * @param fn Function that may throw.
- * @param regExp Potential expected message match.
- * @param message Message to display on error.
- */
- throw(fn: Function, regExp: RegExp): void;
-
- /**
- * Asserts that function will throw an error that is an instance of constructor.
- *
- * @param fn Function that may throw.
- * @param constructor Potential expected error constructor.
- * @param message Message to display on error.
- */
- throw(fn: Function, constructor: Function, message?: string): void;
-
- /**
- * Asserts that function will throw an error that is an instance of constructor
- * and an error with message matching regexp.
- *
- * @param fn Function that may throw.
- * @param constructor Potential expected error constructor.
- * @param message Message to display on error.
- */
- throw(fn: Function, constructor: Function, regExp: RegExp): void;
-
- /**
- * Asserts that fn will throw an error.
- *
- * @param fn Function that may throw.
- * @param message Message to display on error.
- */
- throws(fn: Function, message?: string): void;
-
- /**
- * Asserts that function will throw an error with message matching regexp.
- *
- * @param fn Function that may throw.
- * @param regExp Potential expected message match.
- * @param message Message to display on error.
- */
- throws(fn: Function, regExp: RegExp, message?: string): void;
-
- /**
- * Asserts that function will throw an error that is an instance of constructor.
- *
- * @param fn Function that may throw.
- * @param constructor Potential expected error constructor.
- * @param message Message to display on error.
- */
- throws(fn: Function, errType: Function, message?: string): void;
-
- /**
- * Asserts that function will throw an error that is an instance of constructor
- * and an error with message matching regexp.
- *
- * @param fn Function that may throw.
- * @param constructor Potential expected error constructor.
- * @param message Message to display on error.
- */
- throws(fn: Function, errType: Function, regExp: RegExp): void;
-
- /**
- * Asserts that fn will throw an error.
- *
- * @param fn Function that may throw.
- * @param message Message to display on error.
- */
- Throw(fn: Function, message?: string): void;
-
- /**
- * Asserts that function will throw an error with message matching regexp.
- *
- * @param fn Function that may throw.
- * @param regExp Potential expected message match.
- * @param message Message to display on error.
- */
- Throw(fn: Function, regExp: RegExp): void;
-
- /**
- * Asserts that function will throw an error that is an instance of constructor.
- *
- * @param fn Function that may throw.
- * @param constructor Potential expected error constructor.
- * @param message Message to display on error.
- */
- Throw(fn: Function, errType: Function, message?: string): void;
-
- /**
- * Asserts that function will throw an error that is an instance of constructor
- * and an error with message matching regexp.
- *
- * @param fn Function that may throw.
- * @param constructor Potential expected error constructor.
- * @param message Message to display on error.
- */
- Throw(fn: Function, errType: Function, regExp: RegExp): void;
-
- /**
- * Asserts that fn will not throw an error.
- *
- * @param fn Function that may throw.
- * @param message Message to display on error.
- */
- doesNotThrow(fn: Function, message?: string): void;
-
- /**
- * Asserts that function will throw an error with message matching regexp.
- *
- * @param fn Function that may throw.
- * @param regExp Potential expected message match.
- * @param message Message to display on error.
- */
- doesNotThrow(fn: Function, regExp: RegExp): void;
-
- /**
- * Asserts that function will throw an error that is an instance of constructor.
- *
- * @param fn Function that may throw.
- * @param constructor Potential expected error constructor.
- * @param message Message to display on error.
- */
- doesNotThrow(fn: Function, errType: Function, message?: string): void;
-
- /**
- * Asserts that function will throw an error that is an instance of constructor
- * and an error with message matching regexp.
- *
- * @param fn Function that may throw.
- * @param constructor Potential expected error constructor.
- * @param message Message to display on error.
- */
- doesNotThrow(fn: Function, errType: Function, regExp: RegExp): void;
-
- /**
- * Compares two values using operator.
- *
- * @param val1 Left value during comparison.
- * @param operator Comparison operator.
- * @param val2 Right value during comparison.
- * @param message Message to display on error.
- */
- operator(val1: OperatorComparable, operator: Operator, val2: OperatorComparable, message?: string): void;
-
- /**
- * Asserts that the target is equal to expected, to within a +/- delta range.
- *
- * @param actual Actual value
- * @param expected Potential expected value.
- * @param delta Maximum differenced between values.
- * @param message Message to display on error.
- */
- closeTo(actual: number, expected: number, delta: number, message?: string): void;
-
- /**
- * Asserts that the target is equal to expected, to within a +/- delta range.
- *
- * @param actual Actual value
- * @param expected Potential expected value.
- * @param delta Maximum differenced between values.
- * @param message Message to display on error.
- */
- approximately(act: number, exp: number, delta: number, message?: string): void;
-
- /**
- * Asserts that set1 and set2 have the same members. Order is not take into account.
- *
- * @type T Type of set values.
- * @param set1 Actual set of values.
- * @param set2 Potential expected set of values.
- * @param message Message to display on error.
- */
- sameMembers<T>(set1: T[], set2: T[], message?: string): void;
-
- /**
- * Asserts that set1 and set2 have the same members using deep equality checking.
- * Order is not take into account.
- *
- * @type T Type of set values.
- * @param set1 Actual set of values.
- * @param set2 Potential expected set of values.
- * @param message Message to display on error.
- */
- sameDeepMembers<T>(set1: T[], set2: T[], message?: string): void;
-
- /**
- * Asserts that subset is included in superset. Order is not take into account.
- *
- * @type T Type of set values.
- * @param superset Actual set of values.
- * @param subset Potential contained set of values.
- * @param message Message to display on error.
- */
- includeMembers<T>(superset: T[], subset: T[], message?: string): void;
-
- /**
- * Asserts that subset is included in superset using deep equality checking.
- * Order is not take into account.
- *
- * @type T Type of set values.
- * @param superset Actual set of values.
- * @param subset Potential contained set of values.
- * @param message Message to display on error.
- */
- includeDeepMembers<T>(superset: T[], subset: T[], message?: string): void;
-
- /**
- * Asserts that non-object, non-array value inList appears in the flat array list.
- *
- * @type T Type of list values.
- * @param inList Value expected to be in the list.
- * @param list List of values.
- * @param message Message to display on error.
- */
- oneOf<T>(inList: T, list: T[], message?: string): void;
-
- /**
- * Asserts that a function changes the value of a property.
- *
- * @type T Type of object.
- * @param modifier Function to run.
- * @param object Container object.
- * @param property Property of object expected to be modified.
- * @param message Message to display on error.
- */
- changes<T>(modifier: Function, object: T, property: string /* keyof T */, message?: string): void;
-
- /**
- * Asserts that a function does not change the value of a property.
- *
- * @type T Type of object.
- * @param modifier Function to run.
- * @param object Container object.
- * @param property Property of object expected not to be modified.
- * @param message Message to display on error.
- */
- doesNotChange<T>(modifier: Function, object: T, property: string /* keyof T */, message?: string): void;
-
- /**
- * Asserts that a function increases an object property.
- *
- * @type T Type of object.
- * @param modifier Function to run.
- * @param object Container object.
- * @param property Property of object expected to be increased.
- * @param message Message to display on error.
- */
- increases<T>(modifier: Function, object: T, property: string /* keyof T */, message?: string): void;
-
- /**
- * Asserts that a function does not increase an object property.
- *
- * @type T Type of object.
- * @param modifier Function to run.
- * @param object Container object.
- * @param property Property of object expected not to be increased.
- * @param message Message to display on error.
- */
- doesNotIncrease<T>(modifier: Function, object: T, property: string /* keyof T */, message?: string): void;
-
- /**
- * Asserts that a function decreases an object property.
- *
- * @type T Type of object.
- * @param modifier Function to run.
- * @param object Container object.
- * @param property Property of object expected to be decreased.
- * @param message Message to display on error.
- */
- decreases<T>(modifier: Function, object: T, property: string /* keyof T */, message?: string): void;
-
- /**
- * Asserts that a function does not decrease an object property.
- *
- * @type T Type of object.
- * @param modifier Function to run.
- * @param object Container object.
- * @param property Property of object expected not to be decreased.
- * @param message Message to display on error.
- */
- doesNotDecrease<T>(modifier: Function, object: T, property: string /* keyof T */, message?: string): void;
-
- /**
- * Asserts if value is not a false value, and throws if it is a true value.
- *
- * @type T Type of object.
- * @param object Actual value.
- * @param message Message to display on error.
- * @remarks This is added to allow for chai to be a drop-in replacement for
- * Node’s assert class.
- */
- ifError<T>(object: T, message?: string): void;
-
- /**
- * Asserts that object is extensible (can have new properties added to it).
- *
- * @type T Type of object
- * @param object Actual value.
- * @param message Message to display on error.
- */
- isExtensible<T>(object: T, message?: string): void;
-
- /**
- * Asserts that object is extensible (can have new properties added to it).
- *
- * @type T Type of object
- * @param object Actual value.
- * @param message Message to display on error.
- */
- extensible<T>(object: T, message?: string): void;
-
- /**
- * Asserts that object is not extensible.
- *
- * @type T Type of object
- * @param object Actual value.
- * @param message Message to display on error.
- */
- isNotExtensible<T>(object: T, message?: string): void;
-
- /**
- * Asserts that object is not extensible.
- *
- * @type T Type of object
- * @param object Actual value.
- * @param message Message to display on error.
- */
- notExtensible<T>(object: T, message?: string): void;
-
- /**
- * Asserts that object is sealed (can have new properties added to it
- * and its existing properties cannot be removed).
- *
- * @type T Type of object
- * @param object Actual value.
- * @param message Message to display on error.
- */
- isSealed<T>(object: T, message?: string): void;
-
- /**
- * Asserts that object is sealed (can have new properties added to it
- * and its existing properties cannot be removed).
- *
- * @type T Type of object
- * @param object Actual value.
- * @param message Message to display on error.
- */
- sealed<T>(object: T, message?: string): void;
-
- /**
- * Asserts that object is not sealed.
- *
- * @type T Type of object
- * @param object Actual value.
- * @param message Message to display on error.
- */
- isNotSealed<T>(object: T, message?: string): void;
-
- /**
- * Asserts that object is not sealed.
- *
- * @type T Type of object
- * @param object Actual value.
- * @param message Message to display on error.
- */
- notSealed<T>(object: T, message?: string): void;
-
- /**
- * Asserts that object is frozen (cannot have new properties added to it
- * and its existing properties cannot be removed).
- *
- * @type T Type of object
- * @param object Actual value.
- * @param message Message to display on error.
- */
- isFrozen<T>(object: T, message?: string): void;
-
- /**
- * Asserts that object is frozen (cannot have new properties added to it
- * and its existing properties cannot be removed).
- *
- * @type T Type of object
- * @param object Actual value.
- * @param message Message to display on error.
- */
- frozen<T>(object: T, message?: string): void;
-
- /**
- * Asserts that object is not frozen (cannot have new properties added to it
- * and its existing properties cannot be removed).
- *
- * @type T Type of object
- * @param object Actual value.
- * @param message Message to display on error.
- */
- isNotFrozen<T>(object: T, message?: string): void;
-
- /**
- * Asserts that object is not frozen (cannot have new properties added to it
- * and its existing properties cannot be removed).
- *
- * @type T Type of object
- * @param object Actual value.
- * @param message Message to display on error.
- */
- notFrozen<T>(object: T, message?: string): void;
- }
-
- export interface Config {
- /**
- * Default: false
- */
- includeStack: boolean;
-
- /**
- * Default: true
- */
- showDiff: boolean;
-
- /**
- * Default: 40
- */
- truncateThreshold: number;
- }
-
- export class AssertionError {
- constructor(message: string, _props?: any, ssf?: Function);
- public name: string;
- public message: string;
- public showDiff: boolean;
- public stack: string;
- }
+ interface ChaiStatic {
+ expect: ExpectStatic;
+ should(): Should;
+ /**
+ * Provides a way to extend the internals of Chai
+ */
+ use(fn: (chai: any, utils: any) => void): ChaiStatic;
+ assert: AssertStatic;
+ config: Config;
+ AssertionError: typeof AssertionError;
+ version: string;
+ }
+
+ export interface ExpectStatic extends AssertionStatic {
+ fail(actual?: any, expected?: any, message?: string, operator?: Operator): void;
+ }
+
+ export interface AssertStatic extends Assert {}
+
+ type AssertionStatic = (target: any, message?: string) => Assertion;
+
+ export type Operator = string; // "==" | "===" | ">" | ">=" | "<" | "<=" | "!=" | "!==";
+
+ export type OperatorComparable = boolean | null | number | string | undefined | Date;
+
+ interface ShouldAssertion {
+ equal(value1: any, value2: any, message?: string): void;
+ Throw: ShouldThrow;
+ throw: ShouldThrow;
+ exist(value: any, message?: string): void;
+ }
+
+ interface Should extends ShouldAssertion {
+ not: ShouldAssertion;
+ fail(actual: any, expected: any, message?: string, operator?: Operator): void;
+ }
+
+ interface ShouldThrow {
+ (actual: Function): void;
+ (actual: Function, expected: string | RegExp, message?: string): void;
+ (actual: Function, constructor: Error | Function, expected?: string | RegExp, message?: string): void;
+ }
+
+ interface Assertion extends LanguageChains, NumericComparison, TypeComparison {
+ not: Assertion;
+ deep: Deep;
+ nested: Nested;
+ any: KeyFilter;
+ all: KeyFilter;
+ a: TypeComparison;
+ an: TypeComparison;
+ include: Include;
+ includes: Include;
+ contain: Include;
+ contains: Include;
+ ok: Assertion;
+ true: () => Assertion;
+ false: () => Assertion;
+ null: () => Assertion;
+ undefined: () => Assertion;
+ NaN: Assertion;
+ exist: Assertion;
+ empty: Assertion;
+ arguments: Assertion;
+ Arguments: Assertion;
+ equal: Equal;
+ equals: Equal;
+ eq: Equal;
+ eql: Equal;
+ eqls: Equal;
+ property: Property;
+ ownProperty: OwnProperty;
+ haveOwnProperty: OwnProperty;
+ ownPropertyDescriptor: OwnPropertyDescriptor;
+ haveOwnPropertyDescriptor: OwnPropertyDescriptor;
+ length: Length;
+ lengthOf: Length;
+ match: Match;
+ matches: Match;
+ string(string: string, message?: string): Assertion;
+ keys: Keys;
+ key(string: string): Assertion;
+ throw: (message?: string) => Assertion;
+ throws: Throw;
+ Throw: Throw;
+ respondTo: RespondTo;
+ respondsTo: RespondTo;
+ itself: Assertion;
+ satisfy: Satisfy;
+ satisfies: Satisfy;
+ closeTo: CloseTo;
+ approximately: CloseTo;
+ members: Members;
+ increase: PropertyChange;
+ increases: PropertyChange;
+ decrease: PropertyChange;
+ decreases: PropertyChange;
+ change: PropertyChange;
+ changes: PropertyChange;
+ extensible: Assertion;
+ sealed: Assertion;
+ frozen: Assertion;
+ oneOf(list: any[], message?: string): Assertion;
+ }
+
+ interface LanguageChains {
+ to: Assertion;
+ be: Assertion;
+ been: Assertion;
+ is: Assertion;
+ that: Assertion;
+ which: Assertion;
+ and: Assertion;
+ has: Assertion;
+ have: Assertion;
+ with: Assertion;
+ at: Assertion;
+ of: Assertion;
+ same: Assertion;
+ }
+
+ interface NumericComparison {
+ above: NumberComparer;
+ gt: NumberComparer;
+ greaterThan: NumberComparer;
+ least: NumberComparer;
+ gte: NumberComparer;
+ below: NumberComparer;
+ lt: NumberComparer;
+ lessThan: NumberComparer;
+ most: NumberComparer;
+ lte: NumberComparer;
+ within(start: number, finish: number, message?: string): Assertion;
+ }
+
+ interface NumberComparer {
+ (value: number, message?: string): Assertion;
+ }
+
+ interface TypeComparison {
+ (type: string, message?: string): Assertion;
+ instanceof: InstanceOf;
+ instanceOf: InstanceOf;
+ }
+
+ interface InstanceOf {
+ (constructor: Object, message?: string): Assertion;
+ }
+
+ interface CloseTo {
+ (expected: number, delta: number, message?: string): Assertion;
+ }
+
+ interface Nested {
+ include: Include;
+ property: Property;
+ members: Members;
+ }
+
+ interface Deep {
+ equal: Equal;
+ equals: Equal;
+ eq: Equal;
+ include: Include;
+ property: Property;
+ members: Members;
+ }
+
+ interface KeyFilter {
+ keys: Keys;
+ }
+
+ interface Equal {
+ (value: any, message?: string): Assertion;
+ }
+
+ interface Property {
+ (name: string, value?: any, message?: string): Assertion;
+ }
+
+ interface OwnProperty {
+ (name: string, message?: string): Assertion;
+ }
+
+ interface OwnPropertyDescriptor {
+ (name: string, descriptor: PropertyDescriptor, message?: string): Assertion;
+ (name: string, message?: string): Assertion;
+ }
+
+ interface Length extends LanguageChains, NumericComparison {
+ (length: number, message?: string): Assertion;
+ }
+
+ interface Include {
+ (value: Object | string | number, message?: string): Assertion;
+ keys: Keys;
+ members: Members;
+ any: KeyFilter;
+ all: KeyFilter;
+ }
+
+ interface Match {
+ (regexp: RegExp | string, message?: string): Assertion;
+ }
+
+ interface Keys {
+ (...keys: string[]): Assertion;
+ (keys: any[]): Assertion;
+ (keys: Object): Assertion;
+ }
+
+ interface Throw {
+ (): Assertion;
+ (expected: string, message?: string): Assertion;
+ (expected: RegExp, message?: string): Assertion;
+ (constructor: Error, expected?: string, message?: string): Assertion;
+ (constructor: Error, expected?: RegExp, message?: string): Assertion;
+ (constructor: Function, expected?: string, message?: string): Assertion;
+ (constructor: Function, expected?: RegExp, message?: string): Assertion;
+ }
+
+ interface RespondTo {
+ (method: string, message?: string): Assertion;
+ }
+
+ interface Satisfy {
+ (matcher: Function, message?: string): Assertion;
+ }
+
+ interface Members {
+ (set: any[], message?: string): Assertion;
+ }
+
+ interface PropertyChange {
+ (object: Object, property: string, message?: string): Assertion;
+ }
+
+ export interface Assert {
+ /**
+ * @param expression Expression to test for truthiness.
+ * @param message Message to display on error.
+ */
+ (expression: any, message?: string): void;
+
+ /**
+ * Throws a failure.
+ *
+ * @type T Type of the objects.
+ * @param actual Actual value.
+ * @param expected Potential expected value.
+ * @param message Message to display on error.
+ * @param operator Comparison operator, if not strict equality.
+ * @remarks Node.js assert module-compatible.
+ */
+ fail<T>(actual?: T, expected?: T, message?: string, operator?: Operator): void;
+
+ /**
+ * Asserts that object is truthy.
+ *
+ * @type T Type of object.
+ * @param object Object to test.
+ * @param message Message to display on error.
+ */
+ isOk<T>(value: T, message?: string): void;
+
+ /**
+ * Asserts that object is truthy.
+ *
+ * @type T Type of object.
+ * @param object Object to test.
+ * @param message Message to display on error.
+ */
+ ok<T>(value: T, message?: string): void;
+
+ /**
+ * Asserts that object is falsy.
+ *
+ * @type T Type of object.
+ * @param object Object to test.
+ * @param message Message to display on error.
+ */
+ isNotOk<T>(value: T, message?: string): void;
+
+ /**
+ * Asserts that object is falsy.
+ *
+ * @type T Type of object.
+ * @param object Object to test.
+ * @param message Message to display on error.
+ */
+ notOk<T>(value: T, message?: string): void;
+
+ /**
+ * Asserts non-strict equality (==) of actual and expected.
+ *
+ * @type T Type of the objects.
+ * @param actual Actual value.
+ * @param expected Potential expected value.
+ * @param message Message to display on error.
+ */
+ equal<T>(actual: T, expected: T, message?: string): void;
+
+ /**
+ * Asserts non-strict inequality (==) of actual and expected.
+ *
+ * @type T Type of the objects.
+ * @param actual Actual value.
+ * @param expected Potential expected value.
+ * @param message Message to display on error.
+ */
+ notEqual<T>(actual: T, expected: T, message?: string): void;
+
+ /**
+ * Asserts strict equality (===) of actual and expected.
+ *
+ * @type T Type of the objects.
+ * @param actual Actual value.
+ * @param expected Potential expected value.
+ * @param message Message to display on error.
+ */
+ strictEqual<T>(actual: T, expected: T, message?: string): void;
+
+ /**
+ * Asserts strict inequality (==) of actual and expected.
+ *
+ * @type T Type of the objects.
+ * @param actual Actual value.
+ * @param expected Potential expected value.
+ * @param message Message to display on error.
+ */
+ notStrictEqual<T>(actual: T, expected: T, message?: string): void;
+
+ /**
+ * Asserts that actual is deeply equal to expected.
+ *
+ * @type T Type of the objects.
+ * @param actual Actual value.
+ * @param expected Potential expected value.
+ * @param message Message to display on error.
+ */
+ deepEqual<T>(actual: T, expected: T, message?: string): void;
+
+ /**
+ * Asserts that actual is not deeply equal to expected.
+ *
+ * @type T Type of the objects.
+ * @param actual Actual value.
+ * @param expected Potential expected value.
+ * @param message Message to display on error.
+ */
+ notDeepEqual<T>(actual: T, expected: T, message?: string): void;
+
+ /**
+ * Asserts valueToCheck is strictly greater than (>) valueToBeAbove.
+ *
+ * @param valueToCheck Actual value.
+ * @param valueToBeAbove Minimum Potential expected value.
+ * @param message Message to display on error.
+ */
+ isAbove(valueToCheck: number, valueToBeAbove: number, message?: string): void;
+
+ /**
+ * Asserts valueToCheck is greater than or equal to (>=) valueToBeAtLeast.
+ *
+ * @param valueToCheck Actual value.
+ * @param valueToBeAtLeast Minimum Potential expected value.
+ * @param message Message to display on error.
+ */
+ isAtLeast(valueToCheck: number, valueToBeAtLeast: number, message?: string): void;
+
+ /**
+ * Asserts valueToCheck is strictly less than (<) valueToBeBelow.
+ *
+ * @param valueToCheck Actual value.
+ * @param valueToBeBelow Minimum Potential expected value.
+ * @param message Message to display on error.
+ */
+ isBelow(valueToCheck: number, valueToBeBelow: number, message?: string): void;
+
+ /**
+ * Asserts valueToCheck is greater than or equal to (>=) valueToBeAtMost.
+ *
+ * @param valueToCheck Actual value.
+ * @param valueToBeAtMost Minimum Potential expected value.
+ * @param message Message to display on error.
+ */
+ isAtMost(valueToCheck: number, valueToBeAtMost: number, message?: string): void;
+
+ /**
+ * Asserts that value is true.
+ *
+ * @type T Type of value.
+ * @param value Actual value.
+ * @param message Message to display on error.
+ */
+ isTrue<T>(value: T, message?: string): void;
+
+ /**
+ * Asserts that value is false.
+ *
+ * @type T Type of value.
+ * @param value Actual value.
+ * @param message Message to display on error.
+ */
+ isFalse<T>(value: T, message?: string): void;
+
+ /**
+ * Asserts that value is not true.
+ *
+ * @type T Type of value.
+ * @param value Actual value.
+ * @param message Message to display on error.
+ */
+ isNotTrue<T>(value: T, message?: string): void;
+
+ /**
+ * Asserts that value is not false.
+ *
+ * @type T Type of value.
+ * @param value Actual value.
+ * @param message Message to display on error.
+ */
+ isNotFalse<T>(value: T, message?: string): void;
+
+ /**
+ * Asserts that value is null.
+ *
+ * @type T Type of value.
+ * @param value Actual value.
+ * @param message Message to display on error.
+ */
+ isNull<T>(value: T, message?: string): void;
+
+ /**
+ * Asserts that value is not null.
+ *
+ * @type T Type of value.
+ * @param value Actual value.
+ * @param message Message to display on error.
+ */
+ isNotNull<T>(value: T, message?: string): void;
+
+ /**
+ * Asserts that value is not null.
+ *
+ * @type T Type of value.
+ * @param value Actual value.
+ * @param message Message to display on error.
+ */
+ isNaN<T>(value: T, message?: string): void;
+
+ /**
+ * Asserts that value is not null.
+ *
+ * @type T Type of value.
+ * @param value Actual value.
+ * @param message Message to display on error.
+ */
+ isNotNaN<T>(value: T, message?: string): void;
+
+ /**
+ * Asserts that value is undefined.
+ *
+ * @type T Type of value.
+ * @param value Actual value.
+ * @param message Message to display on error.
+ */
+ isUndefined<T>(value: T, message?: string): void;
+
+ /**
+ * Asserts that value is not undefined.
+ *
+ * @type T Type of value.
+ * @param value Actual value.
+ * @param message Message to display on error.
+ */
+ isDefined<T>(value: T, message?: string): void;
+
+ /**
+ * Asserts that value is a function.
+ *
+ * @type T Type of value.
+ * @param value Actual value.
+ * @param message Message to display on error.
+ */
+ isFunction<T>(value: T, message?: string): void;
+
+ /**
+ * Asserts that value is not a function.
+ *
+ * @type T Type of value.
+ * @param value Actual value.
+ * @param message Message to display on error.
+ */
+ isNotFunction<T>(value: T, message?: string): void;
+
+ /**
+ * Asserts that value is an object of type 'Object'
+ * (as revealed by Object.prototype.toString).
+ *
+ * @type T Type of value.
+ * @param value Actual value.
+ * @param message Message to display on error.
+ * @remarks The assertion does not match subclassed objects.
+ */
+ isObject<T>(value: T, message?: string): void;
+
+ /**
+ * Asserts that value is not an object of type 'Object'
+ * (as revealed by Object.prototype.toString).
+ *
+ * @type T Type of value.
+ * @param value Actual value.
+ * @param message Message to display on error.
+ */
+ isNotObject<T>(value: T, message?: string): void;
+
+ /**
+ * Asserts that value is an array.
+ *
+ * @type T Type of value.
+ * @param value Actual value.
+ * @param message Message to display on error.
+ */
+ isArray<T>(value: T, message?: string): void;
+
+ /**
+ * Asserts that value is not an array.
+ *
+ * @type T Type of value.
+ * @param value Actual value.
+ * @param message Message to display on error.
+ */
+ isNotArray<T>(value: T, message?: string): void;
+
+ /**
+ * Asserts that value is a string.
+ *
+ * @type T Type of value.
+ * @param value Actual value.
+ * @param message Message to display on error.
+ */
+ isString<T>(value: T, message?: string): void;
+
+ /**
+ * Asserts that value is not a string.
+ *
+ * @type T Type of value.
+ * @param value Actual value.
+ * @param message Message to display on error.
+ */
+ isNotString<T>(value: T, message?: string): void;
+
+ /**
+ * Asserts that value is a number.
+ *
+ * @type T Type of value.
+ * @param value Actual value.
+ * @param message Message to display on error.
+ */
+ isNumber<T>(value: T, message?: string): void;
+
+ /**
+ * Asserts that value is not a number.
+ *
+ * @type T Type of value.
+ * @param value Actual value.
+ * @param message Message to display on error.
+ */
+ isNotNumber<T>(value: T, message?: string): void;
+
+ /**
+ * Asserts that value is a boolean.
+ *
+ * @type T Type of value.
+ * @param value Actual value.
+ * @param message Message to display on error.
+ */
+ isBoolean<T>(value: T, message?: string): void;
+
+ /**
+ * Asserts that value is not a boolean.
+ *
+ * @type T Type of value.
+ * @param value Actual value.
+ * @param message Message to display on error.
+ */
+ isNotBoolean<T>(value: T, message?: string): void;
+
+ /**
+ * Asserts that value's type is name, as determined by Object.prototype.toString.
+ *
+ * @type T Type of value.
+ * @param value Actual value.
+ * @param name Potential expected type name of value.
+ * @param message Message to display on error.
+ */
+ typeOf<T>(value: T, name: string, message?: string): void;
+
+ /**
+ * Asserts that value's type is not name, as determined by Object.prototype.toString.
+ *
+ * @type T Type of value.
+ * @param value Actual value.
+ * @param name Potential expected type name of value.
+ * @param message Message to display on error.
+ */
+ notTypeOf<T>(value: T, name: string, message?: string): void;
+
+ /**
+ * Asserts that value is an instance of constructor.
+ *
+ * @type T Type of value.
+ * @param value Actual value.
+ * @param constructor Potential expected contructor of value.
+ * @param message Message to display on error.
+ */
+ instanceOf<T>(value: T, constructor: Function, message?: string): void;
+
+ /**
+ * Asserts that value is not an instance of constructor.
+ *
+ * @type T Type of value.
+ * @param value Actual value.
+ * @param constructor Potential expected contructor of value.
+ * @param message Message to display on error.
+ */
+ notInstanceOf<T>(value: T, type: Function, message?: string): void;
+
+ /**
+ * Asserts that haystack includes needle.
+ *
+ * @param haystack Container string.
+ * @param needle Potential expected substring of haystack.
+ * @param message Message to display on error.
+ */
+ include(haystack: string, needle: string, message?: string): void;
+
+ /**
+ * Asserts that haystack includes needle.
+ *
+ * @type T Type of values in haystack.
+ * @param haystack Container array.
+ * @param needle Potential value contained in haystack.
+ * @param message Message to display on error.
+ */
+ include<T>(haystack: T[], needle: T, message?: string): void;
+
+ /**
+ * Asserts that haystack does not include needle.
+ *
+ * @param haystack Container string.
+ * @param needle Potential expected substring of haystack.
+ * @param message Message to display on error.
+ */
+ notInclude(haystack: string, needle: any, message?: string): void;
+
+ /**
+ * Asserts that haystack does not include needle.
+ *
+ * @type T Type of values in haystack.
+ * @param haystack Container array.
+ * @param needle Potential value contained in haystack.
+ * @param message Message to display on error.
+ */
+ notInclude(haystack: any[], needle: any, message?: string): void;
+
+ /**
+ * Asserts that value matches the regular expression regexp.
+ *
+ * @param value Actual value.
+ * @param regexp Potential match of value.
+ * @param message Message to display on error.
+ */
+ match(value: string, regexp: RegExp, message?: string): void;
+
+ /**
+ * Asserts that value does not match the regular expression regexp.
+ *
+ * @param value Actual value.
+ * @param regexp Potential match of value.
+ * @param message Message to display on error.
+ */
+ notMatch(expected: any, regexp: RegExp, message?: string): void;
+
+ /**
+ * Asserts that object has a property named by property.
+ *
+ * @type T Type of object.
+ * @param object Container object.
+ * @param property Potential contained property of object.
+ * @param message Message to display on error.
+ */
+ property<T>(object: T, property: string /* keyof T */, message?: string): void;
+
+ /**
+ * Asserts that object has a property named by property.
+ *
+ * @type T Type of object.
+ * @param object Container object.
+ * @param property Potential contained property of object.
+ * @param message Message to display on error.
+ */
+ notProperty<T>(object: T, property: string /* keyof T */, message?: string): void;
+
+ /**
+ * Asserts that object has a property named by property, which can be a string
+ * using dot- and bracket-notation for deep reference.
+ *
+ * @type T Type of object.
+ * @param object Container object.
+ * @param property Potential contained property of object.
+ * @param message Message to display on error.
+ */
+ deepProperty<T>(object: T, property: string, message?: string): void;
+
+ /**
+ * Asserts that object does not have a property named by property, which can be a
+ * string using dot- and bracket-notation for deep reference.
+ *
+ * @type T Type of object.
+ * @param object Container object.
+ * @param property Potential contained property of object.
+ * @param message Message to display on error.
+ */
+ notDeepProperty<T>(object: T, property: string, message?: string): void;
+
+ /**
+ * Asserts that object has a property named by property with value given by value.
+ *
+ * @type T Type of object.
+ * @type V Type of value.
+ * @param object Container object.
+ * @param property Potential contained property of object.
+ * @param value Potential expected property value.
+ * @param message Message to display on error.
+ */
+ propertyVal<T, V>(object: T, property: string /* keyof T */, value: V, message?: string): void;
+
+ /**
+ * Asserts that object has a property named by property with value given by value.
+ *
+ * @type T Type of object.
+ * @type V Type of value.
+ * @param object Container object.
+ * @param property Potential contained property of object.
+ * @param value Potential expected property value.
+ * @param message Message to display on error.
+ */
+ propertyNotVal<T, V>(object: T, property: string /* keyof T */, value: V, message?: string): void;
+
+ /**
+ * Asserts that object has a property named by property, which can be a string
+ * using dot- and bracket-notation for deep reference.
+ *
+ * @type T Type of object.
+ * @type V Type of value.
+ * @param object Container object.
+ * @param property Potential contained property of object.
+ * @param value Potential expected property value.
+ * @param message Message to display on error.
+ */
+ deepPropertyVal<T, V>(object: T, property: string, value: V, message?: string): void;
+
+ /**
+ * Asserts that object does not have a property named by property, which can be a
+ * string using dot- and bracket-notation for deep reference.
+ *
+ * @type T Type of object.
+ * @type V Type of value.
+ * @param object Container object.
+ * @param property Potential contained property of object.
+ * @param value Potential expected property value.
+ * @param message Message to display on error.
+ */
+ deepPropertyNotVal<T, V>(object: T, property: string, value: V, message?: string): void;
+
+ /**
+ * Asserts that object has a length property with the expected value.
+ *
+ * @type T Type of object.
+ * @param object Container object.
+ * @param length Potential expected length of object.
+ * @param message Message to display on error.
+ */
+ lengthOf<T extends { readonly length?: number }>(object: T, length: number, message?: string): void;
+
+ /**
+ * Asserts that fn will throw an error.
+ *
+ * @param fn Function that may throw.
+ * @param message Message to display on error.
+ */
+ throw(fn: Function, message?: string): void;
+
+ /**
+ * Asserts that function will throw an error with message matching regexp.
+ *
+ * @param fn Function that may throw.
+ * @param regExp Potential expected message match.
+ * @param message Message to display on error.
+ */
+ throw(fn: Function, regExp: RegExp): void;
+
+ /**
+ * Asserts that function will throw an error that is an instance of constructor.
+ *
+ * @param fn Function that may throw.
+ * @param constructor Potential expected error constructor.
+ * @param message Message to display on error.
+ */
+ throw(fn: Function, constructor: Function, message?: string): void;
+
+ /**
+ * Asserts that function will throw an error that is an instance of constructor
+ * and an error with message matching regexp.
+ *
+ * @param fn Function that may throw.
+ * @param constructor Potential expected error constructor.
+ * @param message Message to display on error.
+ */
+ throw(fn: Function, constructor: Function, regExp: RegExp): void;
+
+ /**
+ * Asserts that fn will throw an error.
+ *
+ * @param fn Function that may throw.
+ * @param message Message to display on error.
+ */
+ throws(fn: Function, message?: string): void;
+
+ /**
+ * Asserts that function will throw an error with message matching regexp.
+ *
+ * @param fn Function that may throw.
+ * @param regExp Potential expected message match.
+ * @param message Message to display on error.
+ */
+ throws(fn: Function, regExp: RegExp, message?: string): void;
+
+ /**
+ * Asserts that function will throw an error that is an instance of constructor.
+ *
+ * @param fn Function that may throw.
+ * @param constructor Potential expected error constructor.
+ * @param message Message to display on error.
+ */
+ throws(fn: Function, errType: Function, message?: string): void;
+
+ /**
+ * Asserts that function will throw an error that is an instance of constructor
+ * and an error with message matching regexp.
+ *
+ * @param fn Function that may throw.
+ * @param constructor Potential expected error constructor.
+ * @param message Message to display on error.
+ */
+ throws(fn: Function, errType: Function, regExp: RegExp): void;
+
+ /**
+ * Asserts that fn will throw an error.
+ *
+ * @param fn Function that may throw.
+ * @param message Message to display on error.
+ */
+ Throw(fn: Function, message?: string): void;
+
+ /**
+ * Asserts that function will throw an error with message matching regexp.
+ *
+ * @param fn Function that may throw.
+ * @param regExp Potential expected message match.
+ * @param message Message to display on error.
+ */
+ Throw(fn: Function, regExp: RegExp): void;
+
+ /**
+ * Asserts that function will throw an error that is an instance of constructor.
+ *
+ * @param fn Function that may throw.
+ * @param constructor Potential expected error constructor.
+ * @param message Message to display on error.
+ */
+ Throw(fn: Function, errType: Function, message?: string): void;
+
+ /**
+ * Asserts that function will throw an error that is an instance of constructor
+ * and an error with message matching regexp.
+ *
+ * @param fn Function that may throw.
+ * @param constructor Potential expected error constructor.
+ * @param message Message to display on error.
+ */
+ Throw(fn: Function, errType: Function, regExp: RegExp): void;
+
+ /**
+ * Asserts that fn will not throw an error.
+ *
+ * @param fn Function that may throw.
+ * @param message Message to display on error.
+ */
+ doesNotThrow(fn: Function, message?: string): void;
+
+ /**
+ * Asserts that function will throw an error with message matching regexp.
+ *
+ * @param fn Function that may throw.
+ * @param regExp Potential expected message match.
+ * @param message Message to display on error.
+ */
+ doesNotThrow(fn: Function, regExp: RegExp): void;
+
+ /**
+ * Asserts that function will throw an error that is an instance of constructor.
+ *
+ * @param fn Function that may throw.
+ * @param constructor Potential expected error constructor.
+ * @param message Message to display on error.
+ */
+ doesNotThrow(fn: Function, errType: Function, message?: string): void;
+
+ /**
+ * Asserts that function will throw an error that is an instance of constructor
+ * and an error with message matching regexp.
+ *
+ * @param fn Function that may throw.
+ * @param constructor Potential expected error constructor.
+ * @param message Message to display on error.
+ */
+ doesNotThrow(fn: Function, errType: Function, regExp: RegExp): void;
+
+ /**
+ * Compares two values using operator.
+ *
+ * @param val1 Left value during comparison.
+ * @param operator Comparison operator.
+ * @param val2 Right value during comparison.
+ * @param message Message to display on error.
+ */
+ operator(val1: OperatorComparable, operator: Operator, val2: OperatorComparable, message?: string): void;
+
+ /**
+ * Asserts that the target is equal to expected, to within a +/- delta range.
+ *
+ * @param actual Actual value
+ * @param expected Potential expected value.
+ * @param delta Maximum differenced between values.
+ * @param message Message to display on error.
+ */
+ closeTo(actual: number, expected: number, delta: number, message?: string): void;
+
+ /**
+ * Asserts that the target is equal to expected, to within a +/- delta range.
+ *
+ * @param actual Actual value
+ * @param expected Potential expected value.
+ * @param delta Maximum differenced between values.
+ * @param message Message to display on error.
+ */
+ approximately(act: number, exp: number, delta: number, message?: string): void;
+
+ /**
+ * Asserts that set1 and set2 have the same members. Order is not take into account.
+ *
+ * @type T Type of set values.
+ * @param set1 Actual set of values.
+ * @param set2 Potential expected set of values.
+ * @param message Message to display on error.
+ */
+ sameMembers<T>(set1: T[], set2: T[], message?: string): void;
+
+ /**
+ * Asserts that set1 and set2 have the same members using deep equality checking.
+ * Order is not take into account.
+ *
+ * @type T Type of set values.
+ * @param set1 Actual set of values.
+ * @param set2 Potential expected set of values.
+ * @param message Message to display on error.
+ */
+ sameDeepMembers<T>(set1: T[], set2: T[], message?: string): void;
+
+ /**
+ * Asserts that subset is included in superset. Order is not take into account.
+ *
+ * @type T Type of set values.
+ * @param superset Actual set of values.
+ * @param subset Potential contained set of values.
+ * @param message Message to display on error.
+ */
+ includeMembers<T>(superset: T[], subset: T[], message?: string): void;
+
+ /**
+ * Asserts that subset is included in superset using deep equality checking.
+ * Order is not take into account.
+ *
+ * @type T Type of set values.
+ * @param superset Actual set of values.
+ * @param subset Potential contained set of values.
+ * @param message Message to display on error.
+ */
+ includeDeepMembers<T>(superset: T[], subset: T[], message?: string): void;
+
+ /**
+ * Asserts that non-object, non-array value inList appears in the flat array list.
+ *
+ * @type T Type of list values.
+ * @param inList Value expected to be in the list.
+ * @param list List of values.
+ * @param message Message to display on error.
+ */
+ oneOf<T>(inList: T, list: T[], message?: string): void;
+
+ /**
+ * Asserts that a function changes the value of a property.
+ *
+ * @type T Type of object.
+ * @param modifier Function to run.
+ * @param object Container object.
+ * @param property Property of object expected to be modified.
+ * @param message Message to display on error.
+ */
+ changes<T>(modifier: Function, object: T, property: string /* keyof T */, message?: string): void;
+
+ /**
+ * Asserts that a function does not change the value of a property.
+ *
+ * @type T Type of object.
+ * @param modifier Function to run.
+ * @param object Container object.
+ * @param property Property of object expected not to be modified.
+ * @param message Message to display on error.
+ */
+ doesNotChange<T>(modifier: Function, object: T, property: string /* keyof T */, message?: string): void;
+
+ /**
+ * Asserts that a function increases an object property.
+ *
+ * @type T Type of object.
+ * @param modifier Function to run.
+ * @param object Container object.
+ * @param property Property of object expected to be increased.
+ * @param message Message to display on error.
+ */
+ increases<T>(modifier: Function, object: T, property: string /* keyof T */, message?: string): void;
+
+ /**
+ * Asserts that a function does not increase an object property.
+ *
+ * @type T Type of object.
+ * @param modifier Function to run.
+ * @param object Container object.
+ * @param property Property of object expected not to be increased.
+ * @param message Message to display on error.
+ */
+ doesNotIncrease<T>(modifier: Function, object: T, property: string /* keyof T */, message?: string): void;
+
+ /**
+ * Asserts that a function decreases an object property.
+ *
+ * @type T Type of object.
+ * @param modifier Function to run.
+ * @param object Container object.
+ * @param property Property of object expected to be decreased.
+ * @param message Message to display on error.
+ */
+ decreases<T>(modifier: Function, object: T, property: string /* keyof T */, message?: string): void;
+
+ /**
+ * Asserts that a function does not decrease an object property.
+ *
+ * @type T Type of object.
+ * @param modifier Function to run.
+ * @param object Container object.
+ * @param property Property of object expected not to be decreased.
+ * @param message Message to display on error.
+ */
+ doesNotDecrease<T>(modifier: Function, object: T, property: string /* keyof T */, message?: string): void;
+
+ /**
+ * Asserts if value is not a false value, and throws if it is a true value.
+ *
+ * @type T Type of object.
+ * @param object Actual value.
+ * @param message Message to display on error.
+ * @remarks This is added to allow for chai to be a drop-in replacement for
+ * Node’s assert class.
+ */
+ ifError<T>(object: T, message?: string): void;
+
+ /**
+ * Asserts that object is extensible (can have new properties added to it).
+ *
+ * @type T Type of object
+ * @param object Actual value.
+ * @param message Message to display on error.
+ */
+ isExtensible<T>(object: T, message?: string): void;
+
+ /**
+ * Asserts that object is extensible (can have new properties added to it).
+ *
+ * @type T Type of object
+ * @param object Actual value.
+ * @param message Message to display on error.
+ */
+ extensible<T>(object: T, message?: string): void;
+
+ /**
+ * Asserts that object is not extensible.
+ *
+ * @type T Type of object
+ * @param object Actual value.
+ * @param message Message to display on error.
+ */
+ isNotExtensible<T>(object: T, message?: string): void;
+
+ /**
+ * Asserts that object is not extensible.
+ *
+ * @type T Type of object
+ * @param object Actual value.
+ * @param message Message to display on error.
+ */
+ notExtensible<T>(object: T, message?: string): void;
+
+ /**
+ * Asserts that object is sealed (can have new properties added to it
+ * and its existing properties cannot be removed).
+ *
+ * @type T Type of object
+ * @param object Actual value.
+ * @param message Message to display on error.
+ */
+ isSealed<T>(object: T, message?: string): void;
+
+ /**
+ * Asserts that object is sealed (can have new properties added to it
+ * and its existing properties cannot be removed).
+ *
+ * @type T Type of object
+ * @param object Actual value.
+ * @param message Message to display on error.
+ */
+ sealed<T>(object: T, message?: string): void;
+
+ /**
+ * Asserts that object is not sealed.
+ *
+ * @type T Type of object
+ * @param object Actual value.
+ * @param message Message to display on error.
+ */
+ isNotSealed<T>(object: T, message?: string): void;
+
+ /**
+ * Asserts that object is not sealed.
+ *
+ * @type T Type of object
+ * @param object Actual value.
+ * @param message Message to display on error.
+ */
+ notSealed<T>(object: T, message?: string): void;
+
+ /**
+ * Asserts that object is frozen (cannot have new properties added to it
+ * and its existing properties cannot be removed).
+ *
+ * @type T Type of object
+ * @param object Actual value.
+ * @param message Message to display on error.
+ */
+ isFrozen<T>(object: T, message?: string): void;
+
+ /**
+ * Asserts that object is frozen (cannot have new properties added to it
+ * and its existing properties cannot be removed).
+ *
+ * @type T Type of object
+ * @param object Actual value.
+ * @param message Message to display on error.
+ */
+ frozen<T>(object: T, message?: string): void;
+
+ /**
+ * Asserts that object is not frozen (cannot have new properties added to it
+ * and its existing properties cannot be removed).
+ *
+ * @type T Type of object
+ * @param object Actual value.
+ * @param message Message to display on error.
+ */
+ isNotFrozen<T>(object: T, message?: string): void;
+
+ /**
+ * Asserts that object is not frozen (cannot have new properties added to it
+ * and its existing properties cannot be removed).
+ *
+ * @type T Type of object
+ * @param object Actual value.
+ * @param message Message to display on error.
+ */
+ notFrozen<T>(object: T, message?: string): void;
+ }
+
+ export interface Config {
+ /**
+ * Default: false
+ */
+ includeStack: boolean;
+
+ /**
+ * Default: true
+ */
+ showDiff: boolean;
+
+ /**
+ * Default: 40
+ */
+ truncateThreshold: number;
+ }
+
+ export class AssertionError {
+ constructor(message: string, _props?: any, ssf?: Function);
+ public name: string;
+ public message: string;
+ public showDiff: boolean;
+ public stack: string;
+ }
}
declare const chai: Chai.ChaiStatic;
declare module 'chai' {
- export = chai;
+ export = chai;
}
interface Object {
- should: Chai.Assertion;
+ should: Chai.Assertion;
}
diff --git a/packages/chai-typescript-typings/package.json b/packages/chai-typescript-typings/package.json
index f2136ffa4..db7cba863 100644
--- a/packages/chai-typescript-typings/package.json
+++ b/packages/chai-typescript-typings/package.json
@@ -1,16 +1,16 @@
{
- "name": "chai-typescript-typings",
- "version": "0.0.2",
- "description": "Typescript type definitions for chai",
- "main": "index.d.ts",
- "types": "index.d.ts",
- "repository": {
- "type": "git",
- "url": "git+https://github.com/0xProject/0x.js.git"
- },
- "license": "Apache-2.0",
- "bugs": {
- "url": "https://github.com/0xProject/0x.js/issues"
- },
- "homepage": "https://github.com/0xProject/0x.js/packages/chai-typescript-typings#readme"
+ "name": "chai-typescript-typings",
+ "version": "0.0.2",
+ "description": "Typescript type definitions for chai",
+ "main": "index.d.ts",
+ "types": "index.d.ts",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/0xProject/0x.js.git"
+ },
+ "license": "Apache-2.0",
+ "bugs": {
+ "url": "https://github.com/0xProject/0x.js/issues"
+ },
+ "homepage": "https://github.com/0xProject/0x.js/packages/chai-typescript-typings#readme"
}
diff --git a/packages/chai-typescript-typings/tslint.json b/packages/chai-typescript-typings/tslint.json
index ef528b22e..9a93a1f74 100644
--- a/packages/chai-typescript-typings/tslint.json
+++ b/packages/chai-typescript-typings/tslint.json
@@ -1,3 +1,3 @@
{
- "extends": ["tslint-config-0xproject"]
+ "extends": ["tslint-config-0xproject"]
}
diff --git a/packages/connect/package.json b/packages/connect/package.json
index 2eff3f681..104e28803 100644
--- a/packages/connect/package.json
+++ b/packages/connect/package.json
@@ -1,64 +1,64 @@
{
- "name": "@0xproject/connect",
- "version": "0.5.2",
- "description": "A javascript library for interacting with the standard relayer api",
- "keywords": ["connect", "0xproject", "ethereum", "tokens", "exchange"],
- "main": "lib/src/index.js",
- "types": "lib/src/index.d.ts",
- "scripts": {
- "build": "tsc",
- "clean": "shx rm -rf _bundles lib test_temp",
- "docs:json": "typedoc --excludePrivate --excludeExternals --target ES5 --json $JSON_FILE_PATH $PROJECT_DIR",
- "upload_docs_json":
- "aws s3 cp generated_docs/index.json $S3_URL --profile 0xproject --grants read=uri=http://acs.amazonaws.com/groups/global/AllUsers --content-type application/json",
- "copy_test_fixtures": "copyfiles -u 2 './test/fixtures/**/*.json' ./lib/test/fixtures",
- "lint": "tslint --project . 'src/**/*.ts' 'test/**/*.ts'",
- "run_mocha": "mocha lib/test/**/*_test.js",
- "test": "run-s clean build copy_test_fixtures run_mocha",
- "test:circleci": "yarn test"
- },
- "repository": {
- "type": "git",
- "url": "https://github.com/0xProject/0x.js.git"
- },
- "author": "Brandon Millman",
- "license": "Apache-2.0",
- "engines": {
- "node": ">=6.0.0"
- },
- "bugs": {
- "url": "https://github.com/0xProject/0x.js/issues"
- },
- "homepage": "https://github.com/0xProject/0x.js/packages/connect/README.md",
- "dependencies": {
- "@0xproject/assert": "^0.0.13",
- "@0xproject/json-schemas": "^0.7.5",
- "@0xproject/utils": "^0.2.2",
- "isomorphic-fetch": "^2.2.1",
- "lodash": "^4.17.4",
- "query-string": "^5.0.1",
- "websocket": "^1.0.25"
- },
- "devDependencies": {
- "@0xproject/tslint-config": "^0.4.4",
- "@types/fetch-mock": "^5.12.1",
- "@types/lodash": "^4.14.86",
- "@types/mocha": "^2.2.42",
- "@types/query-string": "^5.0.1",
- "@types/websocket": "^0.0.34",
- "chai": "^4.0.1",
- "chai-as-promised": "^7.1.0",
- "chai-as-promised-typescript-typings": "^0.0.6",
- "chai-typescript-typings": "^0.0.2",
- "copyfiles": "^1.2.0",
- "dirty-chai": "^2.0.1",
- "fetch-mock": "^5.13.1",
- "mocha": "^4.0.1",
- "npm-run-all": "^4.1.2",
- "shx": "^0.2.2",
- "tslint": "5.8.0",
- "typedoc": "~0.8.0",
- "typescript": "~2.6.1",
- "web3-typescript-typings": "^0.9.6"
- }
+ "name": "@0xproject/connect",
+ "version": "0.5.2",
+ "description": "A javascript library for interacting with the standard relayer api",
+ "keywords": ["connect", "0xproject", "ethereum", "tokens", "exchange"],
+ "main": "lib/src/index.js",
+ "types": "lib/src/index.d.ts",
+ "scripts": {
+ "build": "tsc",
+ "clean": "shx rm -rf _bundles lib test_temp",
+ "docs:json": "typedoc --excludePrivate --excludeExternals --target ES5 --json $JSON_FILE_PATH $PROJECT_DIR",
+ "upload_docs_json":
+ "aws s3 cp generated_docs/index.json $S3_URL --profile 0xproject --grants read=uri=http://acs.amazonaws.com/groups/global/AllUsers --content-type application/json",
+ "copy_test_fixtures": "copyfiles -u 2 './test/fixtures/**/*.json' ./lib/test/fixtures",
+ "lint": "tslint --project . 'src/**/*.ts' 'test/**/*.ts'",
+ "run_mocha": "mocha lib/test/**/*_test.js",
+ "test": "run-s clean build copy_test_fixtures run_mocha",
+ "test:circleci": "yarn test"
+ },
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/0xProject/0x.js.git"
+ },
+ "author": "Brandon Millman",
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=6.0.0"
+ },
+ "bugs": {
+ "url": "https://github.com/0xProject/0x.js/issues"
+ },
+ "homepage": "https://github.com/0xProject/0x.js/packages/connect/README.md",
+ "dependencies": {
+ "@0xproject/assert": "^0.0.13",
+ "@0xproject/json-schemas": "^0.7.5",
+ "@0xproject/utils": "^0.2.2",
+ "isomorphic-fetch": "^2.2.1",
+ "lodash": "^4.17.4",
+ "query-string": "^5.0.1",
+ "websocket": "^1.0.25"
+ },
+ "devDependencies": {
+ "@0xproject/tslint-config": "^0.4.4",
+ "@types/fetch-mock": "^5.12.1",
+ "@types/lodash": "^4.14.86",
+ "@types/mocha": "^2.2.42",
+ "@types/query-string": "^5.0.1",
+ "@types/websocket": "^0.0.34",
+ "chai": "^4.0.1",
+ "chai-as-promised": "^7.1.0",
+ "chai-as-promised-typescript-typings": "^0.0.6",
+ "chai-typescript-typings": "^0.0.2",
+ "copyfiles": "^1.2.0",
+ "dirty-chai": "^2.0.1",
+ "fetch-mock": "^5.13.1",
+ "mocha": "^4.0.1",
+ "npm-run-all": "^4.1.2",
+ "shx": "^0.2.2",
+ "tslint": "5.8.0",
+ "typedoc": "~0.8.0",
+ "typescript": "~2.6.1",
+ "web3-typescript-typings": "^0.9.6"
+ }
}
diff --git a/packages/connect/src/globals.d.ts b/packages/connect/src/globals.d.ts
index d2011026b..078e189cd 100644
--- a/packages/connect/src/globals.d.ts
+++ b/packages/connect/src/globals.d.ts
@@ -1,6 +1,6 @@
declare module 'dirty-chai';
declare module '*.json' {
- const value: any;
- export default value;
+ const value: any;
+ export default value;
}
diff --git a/packages/connect/src/http_client.ts b/packages/connect/src/http_client.ts
index 03c0b9702..3df77b0f0 100644
--- a/packages/connect/src/http_client.ts
+++ b/packages/connect/src/http_client.ts
@@ -6,17 +6,17 @@ import * as queryString from 'query-string';
import { schemas as clientSchemas } from './schemas/schemas';
import {
- Client,
- FeesRequest,
- FeesResponse,
- HttpRequestOptions,
- HttpRequestType,
- OrderbookRequest,
- OrderbookResponse,
- OrdersRequest,
- SignedOrder,
- TokenPairsItem,
- TokenPairsRequest,
+ Client,
+ FeesRequest,
+ FeesResponse,
+ HttpRequestOptions,
+ HttpRequestType,
+ OrderbookRequest,
+ OrderbookResponse,
+ OrdersRequest,
+ SignedOrder,
+ TokenPairsItem,
+ TokenPairsRequest,
} from './types';
import { relayerResponseJsonParsers } from './utils/relayer_response_json_parsers';
@@ -26,127 +26,127 @@ const TRAILING_SLASHES_REGEX = /\/+$/;
* that implement the standard relayer API v0
*/
export class HttpClient implements Client {
- private _apiEndpointUrl: string;
- /**
- * Instantiates a new HttpClient instance
- * @param url The relayer API base HTTP url you would like to interact with
- * @return An instance of HttpClient
- */
- constructor(url: string) {
- assert.isHttpUrl('url', url);
- this._apiEndpointUrl = url.replace(TRAILING_SLASHES_REGEX, ''); // remove trailing slashes
- }
- /**
- * Retrieve token pair info from the API
- * @param request A TokenPairsRequest instance describing specific token information
- * to retrieve
- * @return The resulting TokenPairsItems that match the request
- */
- public async getTokenPairsAsync(request?: TokenPairsRequest): Promise<TokenPairsItem[]> {
- if (!_.isUndefined(request)) {
- assert.doesConformToSchema('request', request, clientSchemas.relayerTokenPairsRequestSchema);
- }
- const requestOpts = {
- params: request,
- };
- const responseJson = await this._requestAsync('/token_pairs', HttpRequestType.Get, requestOpts);
- const tokenPairs = relayerResponseJsonParsers.parseTokenPairsJson(responseJson);
- return tokenPairs;
- }
- /**
- * Retrieve orders from the API
- * @param request An OrdersRequest instance describing specific orders to retrieve
- * @return The resulting SignedOrders that match the request
- */
- public async getOrdersAsync(request?: OrdersRequest): Promise<SignedOrder[]> {
- if (!_.isUndefined(request)) {
- assert.doesConformToSchema('request', request, clientSchemas.relayerOrdersRequestSchema);
- }
- const requestOpts = {
- params: request,
- };
- const responseJson = await this._requestAsync(`/orders`, HttpRequestType.Get, requestOpts);
- const orders = relayerResponseJsonParsers.parseOrdersJson(responseJson);
- return orders;
- }
- /**
- * Retrieve a specific order from the API
- * @param orderHash An orderHash generated from the desired order
- * @return The SignedOrder that matches the supplied orderHash
- */
- public async getOrderAsync(orderHash: string): Promise<SignedOrder> {
- assert.doesConformToSchema('orderHash', orderHash, schemas.orderHashSchema);
- const responseJson = await this._requestAsync(`/order/${orderHash}`, HttpRequestType.Get);
- const order = relayerResponseJsonParsers.parseOrderJson(responseJson);
- return order;
- }
- /**
- * Retrieve an orderbook from the API
- * @param request An OrderbookRequest instance describing the specific orderbook to retrieve
- * @return The resulting OrderbookResponse that matches the request
- */
- public async getOrderbookAsync(request: OrderbookRequest): Promise<OrderbookResponse> {
- assert.doesConformToSchema('request', request, clientSchemas.relayerOrderBookRequestSchema);
- const requestOpts = {
- params: request,
- };
- const responseJson = await this._requestAsync('/orderbook', HttpRequestType.Get, requestOpts);
- const orderbook = relayerResponseJsonParsers.parseOrderbookResponseJson(responseJson);
- return orderbook;
- }
- /**
- * Retrieve fee information from the API
- * @param request A FeesRequest instance describing the specific fees to retrieve
- * @return The resulting FeesResponse that matches the request
- */
- public async getFeesAsync(request: FeesRequest): Promise<FeesResponse> {
- assert.doesConformToSchema('request', request, schemas.relayerApiFeesPayloadSchema);
- const requestOpts = {
- payload: request,
- };
- const responseJson = await this._requestAsync('/fees', HttpRequestType.Post, requestOpts);
- const fees = relayerResponseJsonParsers.parseFeesResponseJson(responseJson);
- return fees;
- }
- /**
- * Submit a signed order to the API
- * @param signedOrder A SignedOrder instance to submit
- */
- public async submitOrderAsync(signedOrder: SignedOrder): Promise<void> {
- assert.doesConformToSchema('signedOrder', signedOrder, schemas.signedOrderSchema);
- const requestOpts = {
- payload: signedOrder,
- };
- await this._requestAsync('/order', HttpRequestType.Post, requestOpts);
- }
- private async _requestAsync(
- path: string,
- requestType: HttpRequestType,
- requestOptions?: HttpRequestOptions,
- ): Promise<any> {
- const params = _.get(requestOptions, 'params');
- const payload = _.get(requestOptions, 'payload');
- let query = '';
- if (!_.isUndefined(params) && !_.isEmpty(params)) {
- const stringifiedParams = queryString.stringify(params);
- query = `?${stringifiedParams}`;
- }
- const url = `${this._apiEndpointUrl}${path}${query}`;
- const headers = new Headers({
- 'content-type': 'application/json',
- });
- const response = await fetch(url, {
- method: requestType,
- body: JSON.stringify(payload),
- headers,
- });
- const json = await response.json();
- if (!response.ok) {
- const errorString = `${response.status} - ${response.statusText}\n${requestType} ${url}\n${JSON.stringify(
- json,
- )}`;
- throw Error(errorString);
- }
- return json;
- }
+ private _apiEndpointUrl: string;
+ /**
+ * Instantiates a new HttpClient instance
+ * @param url The relayer API base HTTP url you would like to interact with
+ * @return An instance of HttpClient
+ */
+ constructor(url: string) {
+ assert.isHttpUrl('url', url);
+ this._apiEndpointUrl = url.replace(TRAILING_SLASHES_REGEX, ''); // remove trailing slashes
+ }
+ /**
+ * Retrieve token pair info from the API
+ * @param request A TokenPairsRequest instance describing specific token information
+ * to retrieve
+ * @return The resulting TokenPairsItems that match the request
+ */
+ public async getTokenPairsAsync(request?: TokenPairsRequest): Promise<TokenPairsItem[]> {
+ if (!_.isUndefined(request)) {
+ assert.doesConformToSchema('request', request, clientSchemas.relayerTokenPairsRequestSchema);
+ }
+ const requestOpts = {
+ params: request,
+ };
+ const responseJson = await this._requestAsync('/token_pairs', HttpRequestType.Get, requestOpts);
+ const tokenPairs = relayerResponseJsonParsers.parseTokenPairsJson(responseJson);
+ return tokenPairs;
+ }
+ /**
+ * Retrieve orders from the API
+ * @param request An OrdersRequest instance describing specific orders to retrieve
+ * @return The resulting SignedOrders that match the request
+ */
+ public async getOrdersAsync(request?: OrdersRequest): Promise<SignedOrder[]> {
+ if (!_.isUndefined(request)) {
+ assert.doesConformToSchema('request', request, clientSchemas.relayerOrdersRequestSchema);
+ }
+ const requestOpts = {
+ params: request,
+ };
+ const responseJson = await this._requestAsync(`/orders`, HttpRequestType.Get, requestOpts);
+ const orders = relayerResponseJsonParsers.parseOrdersJson(responseJson);
+ return orders;
+ }
+ /**
+ * Retrieve a specific order from the API
+ * @param orderHash An orderHash generated from the desired order
+ * @return The SignedOrder that matches the supplied orderHash
+ */
+ public async getOrderAsync(orderHash: string): Promise<SignedOrder> {
+ assert.doesConformToSchema('orderHash', orderHash, schemas.orderHashSchema);
+ const responseJson = await this._requestAsync(`/order/${orderHash}`, HttpRequestType.Get);
+ const order = relayerResponseJsonParsers.parseOrderJson(responseJson);
+ return order;
+ }
+ /**
+ * Retrieve an orderbook from the API
+ * @param request An OrderbookRequest instance describing the specific orderbook to retrieve
+ * @return The resulting OrderbookResponse that matches the request
+ */
+ public async getOrderbookAsync(request: OrderbookRequest): Promise<OrderbookResponse> {
+ assert.doesConformToSchema('request', request, clientSchemas.relayerOrderBookRequestSchema);
+ const requestOpts = {
+ params: request,
+ };
+ const responseJson = await this._requestAsync('/orderbook', HttpRequestType.Get, requestOpts);
+ const orderbook = relayerResponseJsonParsers.parseOrderbookResponseJson(responseJson);
+ return orderbook;
+ }
+ /**
+ * Retrieve fee information from the API
+ * @param request A FeesRequest instance describing the specific fees to retrieve
+ * @return The resulting FeesResponse that matches the request
+ */
+ public async getFeesAsync(request: FeesRequest): Promise<FeesResponse> {
+ assert.doesConformToSchema('request', request, schemas.relayerApiFeesPayloadSchema);
+ const requestOpts = {
+ payload: request,
+ };
+ const responseJson = await this._requestAsync('/fees', HttpRequestType.Post, requestOpts);
+ const fees = relayerResponseJsonParsers.parseFeesResponseJson(responseJson);
+ return fees;
+ }
+ /**
+ * Submit a signed order to the API
+ * @param signedOrder A SignedOrder instance to submit
+ */
+ public async submitOrderAsync(signedOrder: SignedOrder): Promise<void> {
+ assert.doesConformToSchema('signedOrder', signedOrder, schemas.signedOrderSchema);
+ const requestOpts = {
+ payload: signedOrder,
+ };
+ await this._requestAsync('/order', HttpRequestType.Post, requestOpts);
+ }
+ private async _requestAsync(
+ path: string,
+ requestType: HttpRequestType,
+ requestOptions?: HttpRequestOptions,
+ ): Promise<any> {
+ const params = _.get(requestOptions, 'params');
+ const payload = _.get(requestOptions, 'payload');
+ let query = '';
+ if (!_.isUndefined(params) && !_.isEmpty(params)) {
+ const stringifiedParams = queryString.stringify(params);
+ query = `?${stringifiedParams}`;
+ }
+ const url = `${this._apiEndpointUrl}${path}${query}`;
+ const headers = new Headers({
+ 'content-type': 'application/json',
+ });
+ const response = await fetch(url, {
+ method: requestType,
+ body: JSON.stringify(payload),
+ headers,
+ });
+ const json = await response.json();
+ if (!response.ok) {
+ const errorString = `${response.status} - ${response.statusText}\n${requestType} ${url}\n${JSON.stringify(
+ json,
+ )}`;
+ throw Error(errorString);
+ }
+ return json;
+ }
}
diff --git a/packages/connect/src/index.ts b/packages/connect/src/index.ts
index d0cce9935..a492f5ae9 100644
--- a/packages/connect/src/index.ts
+++ b/packages/connect/src/index.ts
@@ -1,19 +1,19 @@
export { HttpClient } from './http_client';
export { WebSocketOrderbookChannel } from './ws_orderbook_channel';
export {
- Client,
- ECSignature,
- FeesRequest,
- FeesResponse,
- Order,
- OrderbookChannel,
- OrderbookChannelHandler,
- OrderbookChannelSubscriptionOpts,
- OrderbookRequest,
- OrderbookResponse,
- OrdersRequest,
- SignedOrder,
- TokenPairsItem,
- TokenPairsRequest,
- TokenTradeInfo,
+ Client,
+ ECSignature,
+ FeesRequest,
+ FeesResponse,
+ Order,
+ OrderbookChannel,
+ OrderbookChannelHandler,
+ OrderbookChannelSubscriptionOpts,
+ OrderbookRequest,
+ OrderbookResponse,
+ OrdersRequest,
+ SignedOrder,
+ TokenPairsItem,
+ TokenPairsRequest,
+ TokenTradeInfo,
} from './types';
diff --git a/packages/connect/src/schemas/relayer_fees_request_schema.ts b/packages/connect/src/schemas/relayer_fees_request_schema.ts
index bd0b91fa3..f20e077ba 100644
--- a/packages/connect/src/schemas/relayer_fees_request_schema.ts
+++ b/packages/connect/src/schemas/relayer_fees_request_schema.ts
@@ -1,8 +1,8 @@
export const relayerOrderBookRequestSchema = {
- id: '/RelayerOrderBookRequest',
- type: 'object',
- properties: {
- baseTokenAddress: { $ref: '/Address' },
- quoteTokenAddress: { $ref: '/Address' },
- },
+ id: '/RelayerOrderBookRequest',
+ type: 'object',
+ properties: {
+ baseTokenAddress: { $ref: '/Address' },
+ quoteTokenAddress: { $ref: '/Address' },
+ },
};
diff --git a/packages/connect/src/schemas/relayer_orderbook_request_schema.ts b/packages/connect/src/schemas/relayer_orderbook_request_schema.ts
index bd0b91fa3..f20e077ba 100644
--- a/packages/connect/src/schemas/relayer_orderbook_request_schema.ts
+++ b/packages/connect/src/schemas/relayer_orderbook_request_schema.ts
@@ -1,8 +1,8 @@
export const relayerOrderBookRequestSchema = {
- id: '/RelayerOrderBookRequest',
- type: 'object',
- properties: {
- baseTokenAddress: { $ref: '/Address' },
- quoteTokenAddress: { $ref: '/Address' },
- },
+ id: '/RelayerOrderBookRequest',
+ type: 'object',
+ properties: {
+ baseTokenAddress: { $ref: '/Address' },
+ quoteTokenAddress: { $ref: '/Address' },
+ },
};
diff --git a/packages/connect/src/schemas/relayer_orders_request_schema.ts b/packages/connect/src/schemas/relayer_orders_request_schema.ts
index 0471cb104..570238dae 100644
--- a/packages/connect/src/schemas/relayer_orders_request_schema.ts
+++ b/packages/connect/src/schemas/relayer_orders_request_schema.ts
@@ -1,16 +1,16 @@
export const relayerOrdersRequestSchema = {
- id: '/RelayerOrdersRequest',
- type: 'object',
- properties: {
- exchangeContractAddress: { $ref: '/Address' },
- tokenAddress: { $ref: '/Address' },
- makerTokenAddress: { $ref: '/Address' },
- takerTokenAddress: { $ref: '/Address' },
- tokenA: { $ref: '/Address' },
- tokenB: { $ref: '/Address' },
- maker: { $ref: '/Address' },
- taker: { $ref: '/Address' },
- trader: { $ref: '/Address' },
- feeRecipient: { $ref: '/Address' },
- },
+ id: '/RelayerOrdersRequest',
+ type: 'object',
+ properties: {
+ exchangeContractAddress: { $ref: '/Address' },
+ tokenAddress: { $ref: '/Address' },
+ makerTokenAddress: { $ref: '/Address' },
+ takerTokenAddress: { $ref: '/Address' },
+ tokenA: { $ref: '/Address' },
+ tokenB: { $ref: '/Address' },
+ maker: { $ref: '/Address' },
+ taker: { $ref: '/Address' },
+ trader: { $ref: '/Address' },
+ feeRecipient: { $ref: '/Address' },
+ },
};
diff --git a/packages/connect/src/schemas/relayer_token_pairs_request_schema.ts b/packages/connect/src/schemas/relayer_token_pairs_request_schema.ts
index cb79efdb3..379232204 100644
--- a/packages/connect/src/schemas/relayer_token_pairs_request_schema.ts
+++ b/packages/connect/src/schemas/relayer_token_pairs_request_schema.ts
@@ -1,8 +1,8 @@
export const relayerTokenPairsRequestSchema = {
- id: '/RelayerTokenPairsRequest',
- type: 'object',
- properties: {
- tokenA: { $ref: '/Address' },
- tokenB: { $ref: '/Address' },
- },
+ id: '/RelayerTokenPairsRequest',
+ type: 'object',
+ properties: {
+ tokenA: { $ref: '/Address' },
+ tokenB: { $ref: '/Address' },
+ },
};
diff --git a/packages/connect/src/schemas/schemas.ts b/packages/connect/src/schemas/schemas.ts
index 2982d81ab..288d6969d 100644
--- a/packages/connect/src/schemas/schemas.ts
+++ b/packages/connect/src/schemas/schemas.ts
@@ -3,7 +3,7 @@ import { relayerOrdersRequestSchema } from './relayer_orders_request_schema';
import { relayerTokenPairsRequestSchema } from './relayer_token_pairs_request_schema';
export const schemas = {
- relayerOrderBookRequestSchema,
- relayerOrdersRequestSchema,
- relayerTokenPairsRequestSchema,
+ relayerOrderBookRequestSchema,
+ relayerOrdersRequestSchema,
+ relayerTokenPairsRequestSchema,
};
diff --git a/packages/connect/src/types.ts b/packages/connect/src/types.ts
index 5f72f6e85..edb6c77a6 100644
--- a/packages/connect/src/types.ts
+++ b/packages/connect/src/types.ts
@@ -2,45 +2,45 @@ import { BigNumber } from '@0xproject/utils';
// TODO: Consolidate Order, SignedOrder and ECSignature into a shared package instead of duplicating them from 0x.js
export interface Order {
- maker: string;
- taker: string;
- makerFee: BigNumber;
- takerFee: BigNumber;
- makerTokenAmount: BigNumber;
- takerTokenAmount: BigNumber;
- makerTokenAddress: string;
- takerTokenAddress: string;
- salt: BigNumber;
- exchangeContractAddress: string;
- feeRecipient: string;
- expirationUnixTimestampSec: BigNumber;
+ maker: string;
+ taker: string;
+ makerFee: BigNumber;
+ takerFee: BigNumber;
+ makerTokenAmount: BigNumber;
+ takerTokenAmount: BigNumber;
+ makerTokenAddress: string;
+ takerTokenAddress: string;
+ salt: BigNumber;
+ exchangeContractAddress: string;
+ feeRecipient: string;
+ expirationUnixTimestampSec: BigNumber;
}
export interface SignedOrder extends Order {
- ecSignature: ECSignature;
+ ecSignature: ECSignature;
}
/**
* Elliptic Curve signature
*/
export interface ECSignature {
- v: number;
- r: string;
- s: string;
+ v: number;
+ r: string;
+ s: string;
}
export interface Client {
- getTokenPairsAsync: (request?: TokenPairsRequest) => Promise<TokenPairsItem[]>;
- getOrdersAsync: (request?: OrdersRequest) => Promise<SignedOrder[]>;
- getOrderAsync: (orderHash: string) => Promise<SignedOrder>;
- getOrderbookAsync: (request: OrderbookRequest) => Promise<OrderbookResponse>;
- getFeesAsync: (request: FeesRequest) => Promise<FeesResponse>;
- submitOrderAsync: (signedOrder: SignedOrder) => Promise<void>;
+ getTokenPairsAsync: (request?: TokenPairsRequest) => Promise<TokenPairsItem[]>;
+ getOrdersAsync: (request?: OrdersRequest) => Promise<SignedOrder[]>;
+ getOrderAsync: (orderHash: string) => Promise<SignedOrder>;
+ getOrderbookAsync: (request: OrderbookRequest) => Promise<OrderbookResponse>;
+ getFeesAsync: (request: FeesRequest) => Promise<FeesResponse>;
+ submitOrderAsync: (signedOrder: SignedOrder) => Promise<void>;
}
export interface OrderbookChannel {
- subscribe: (subscriptionOpts: OrderbookChannelSubscriptionOpts, handler: OrderbookChannelHandler) => void;
- close: () => void;
+ subscribe: (subscriptionOpts: OrderbookChannelSubscriptionOpts, handler: OrderbookChannelHandler) => void;
+ close: () => void;
}
/*
@@ -50,129 +50,129 @@ export interface OrderbookChannel {
* limit: Maximum number of bids and asks in orderbook snapshot
*/
export interface OrderbookChannelSubscriptionOpts {
- baseTokenAddress: string;
- quoteTokenAddress: string;
- snapshot: boolean;
- limit: number;
+ baseTokenAddress: string;
+ quoteTokenAddress: string;
+ snapshot: boolean;
+ limit: number;
}
export interface OrderbookChannelHandler {
- onSnapshot: (
- channel: OrderbookChannel,
- subscriptionOpts: OrderbookChannelSubscriptionOpts,
- snapshot: OrderbookResponse,
- ) => void;
- onUpdate: (
- channel: OrderbookChannel,
- subscriptionOpts: OrderbookChannelSubscriptionOpts,
- order: SignedOrder,
- ) => void;
- onError: (channel: OrderbookChannel, subscriptionOpts: OrderbookChannelSubscriptionOpts, err: Error) => void;
- onClose: (channel: OrderbookChannel, subscriptionOpts: OrderbookChannelSubscriptionOpts) => void;
+ onSnapshot: (
+ channel: OrderbookChannel,
+ subscriptionOpts: OrderbookChannelSubscriptionOpts,
+ snapshot: OrderbookResponse,
+ ) => void;
+ onUpdate: (
+ channel: OrderbookChannel,
+ subscriptionOpts: OrderbookChannelSubscriptionOpts,
+ order: SignedOrder,
+ ) => void;
+ onError: (channel: OrderbookChannel, subscriptionOpts: OrderbookChannelSubscriptionOpts, err: Error) => void;
+ onClose: (channel: OrderbookChannel, subscriptionOpts: OrderbookChannelSubscriptionOpts) => void;
}
export type OrderbookChannelMessage =
- | SnapshotOrderbookChannelMessage
- | UpdateOrderbookChannelMessage
- | UnknownOrderbookChannelMessage;
+ | SnapshotOrderbookChannelMessage
+ | UpdateOrderbookChannelMessage
+ | UnknownOrderbookChannelMessage;
export enum OrderbookChannelMessageTypes {
- Snapshot = 'snapshot',
- Update = 'update',
- Unknown = 'unknown',
+ Snapshot = 'snapshot',
+ Update = 'update',
+ Unknown = 'unknown',
}
export interface SnapshotOrderbookChannelMessage {
- type: OrderbookChannelMessageTypes.Snapshot;
- requestId: number;
- payload: OrderbookResponse;
+ type: OrderbookChannelMessageTypes.Snapshot;
+ requestId: number;
+ payload: OrderbookResponse;
}
export interface UpdateOrderbookChannelMessage {
- type: OrderbookChannelMessageTypes.Update;
- requestId: number;
- payload: SignedOrder;
+ type: OrderbookChannelMessageTypes.Update;
+ requestId: number;
+ payload: SignedOrder;
}
export interface UnknownOrderbookChannelMessage {
- type: OrderbookChannelMessageTypes.Unknown;
- requestId: number;
- payload: undefined;
+ type: OrderbookChannelMessageTypes.Unknown;
+ requestId: number;
+ payload: undefined;
}
export enum WebsocketConnectionEventType {
- Close = 'close',
- Error = 'error',
- Message = 'message',
+ Close = 'close',
+ Error = 'error',
+ Message = 'message',
}
export enum WebsocketClientEventType {
- Connect = 'connect',
- ConnectFailed = 'connectFailed',
+ Connect = 'connect',
+ ConnectFailed = 'connectFailed',
}
export interface TokenPairsRequest {
- tokenA?: string;
- tokenB?: string;
+ tokenA?: string;
+ tokenB?: string;
}
export interface TokenPairsItem {
- tokenA: TokenTradeInfo;
- tokenB: TokenTradeInfo;
+ tokenA: TokenTradeInfo;
+ tokenB: TokenTradeInfo;
}
export interface TokenTradeInfo {
- address: string;
- minAmount: BigNumber;
- maxAmount: BigNumber;
- precision: number;
+ address: string;
+ minAmount: BigNumber;
+ maxAmount: BigNumber;
+ precision: number;
}
export interface OrdersRequest {
- exchangeContractAddress?: string;
- tokenAddress?: string;
- makerTokenAddress?: string;
- takerTokenAddress?: string;
- maker?: string;
- taker?: string;
- trader?: string;
- feeRecipient?: string;
+ exchangeContractAddress?: string;
+ tokenAddress?: string;
+ makerTokenAddress?: string;
+ takerTokenAddress?: string;
+ maker?: string;
+ taker?: string;
+ trader?: string;
+ feeRecipient?: string;
}
export interface OrderbookRequest {
- baseTokenAddress: string;
- quoteTokenAddress: string;
+ baseTokenAddress: string;
+ quoteTokenAddress: string;
}
export interface OrderbookResponse {
- bids: SignedOrder[];
- asks: SignedOrder[];
+ bids: SignedOrder[];
+ asks: SignedOrder[];
}
export interface FeesRequest {
- exchangeContractAddress: string;
- maker: string;
- taker: string;
- makerTokenAddress: string;
- takerTokenAddress: string;
- makerTokenAmount: BigNumber;
- takerTokenAmount: BigNumber;
- expirationUnixTimestampSec: BigNumber;
- salt: BigNumber;
+ exchangeContractAddress: string;
+ maker: string;
+ taker: string;
+ makerTokenAddress: string;
+ takerTokenAddress: string;
+ makerTokenAmount: BigNumber;
+ takerTokenAmount: BigNumber;
+ expirationUnixTimestampSec: BigNumber;
+ salt: BigNumber;
}
export interface FeesResponse {
- feeRecipient: string;
- makerFee: BigNumber;
- takerFee: BigNumber;
+ feeRecipient: string;
+ makerFee: BigNumber;
+ takerFee: BigNumber;
}
export interface HttpRequestOptions {
- params?: object;
- payload?: object;
+ params?: object;
+ payload?: object;
}
export enum HttpRequestType {
- Get = 'GET',
- Post = 'POST',
+ Get = 'GET',
+ Post = 'POST',
}
diff --git a/packages/connect/src/utils/orderbook_channel_message_parser.ts b/packages/connect/src/utils/orderbook_channel_message_parser.ts
index c5dd2ee2e..9a9ca8901 100644
--- a/packages/connect/src/utils/orderbook_channel_message_parser.ts
+++ b/packages/connect/src/utils/orderbook_channel_message_parser.ts
@@ -7,31 +7,31 @@ import { OrderbookChannelMessage, OrderbookChannelMessageTypes } from '../types'
import { relayerResponseJsonParsers } from './relayer_response_json_parsers';
export const orderbookChannelMessageParser = {
- parse(utf8Data: string): OrderbookChannelMessage {
- const messageObj = JSON.parse(utf8Data);
- const type: string = _.get(messageObj, 'type');
- assert.assert(!_.isUndefined(type), `Message is missing a type parameter: ${utf8Data}`);
- assert.isString('type', type);
- switch (type) {
- case OrderbookChannelMessageTypes.Snapshot: {
- assert.doesConformToSchema('message', messageObj, schemas.relayerApiOrderbookChannelSnapshotSchema);
- const orderbookJson = messageObj.payload;
- const orderbook = relayerResponseJsonParsers.parseOrderbookResponseJson(orderbookJson);
- return _.assign(messageObj, { payload: orderbook });
- }
- case OrderbookChannelMessageTypes.Update: {
- assert.doesConformToSchema('message', messageObj, schemas.relayerApiOrderbookChannelUpdateSchema);
- const orderJson = messageObj.payload;
- const order = relayerResponseJsonParsers.parseOrderJson(orderJson);
- return _.assign(messageObj, { payload: order });
- }
- default: {
- return {
- type: OrderbookChannelMessageTypes.Unknown,
- requestId: 0,
- payload: undefined,
- };
- }
- }
- },
+ parse(utf8Data: string): OrderbookChannelMessage {
+ const messageObj = JSON.parse(utf8Data);
+ const type: string = _.get(messageObj, 'type');
+ assert.assert(!_.isUndefined(type), `Message is missing a type parameter: ${utf8Data}`);
+ assert.isString('type', type);
+ switch (type) {
+ case OrderbookChannelMessageTypes.Snapshot: {
+ assert.doesConformToSchema('message', messageObj, schemas.relayerApiOrderbookChannelSnapshotSchema);
+ const orderbookJson = messageObj.payload;
+ const orderbook = relayerResponseJsonParsers.parseOrderbookResponseJson(orderbookJson);
+ return _.assign(messageObj, { payload: orderbook });
+ }
+ case OrderbookChannelMessageTypes.Update: {
+ assert.doesConformToSchema('message', messageObj, schemas.relayerApiOrderbookChannelUpdateSchema);
+ const orderJson = messageObj.payload;
+ const order = relayerResponseJsonParsers.parseOrderJson(orderJson);
+ return _.assign(messageObj, { payload: order });
+ }
+ default: {
+ return {
+ type: OrderbookChannelMessageTypes.Unknown,
+ requestId: 0,
+ payload: undefined,
+ };
+ }
+ }
+ },
};
diff --git a/packages/connect/src/utils/relayer_response_json_parsers.ts b/packages/connect/src/utils/relayer_response_json_parsers.ts
index b6c5decaf..668461bf4 100644
--- a/packages/connect/src/utils/relayer_response_json_parsers.ts
+++ b/packages/connect/src/utils/relayer_response_json_parsers.ts
@@ -7,31 +7,31 @@ import { FeesResponse, OrderbookResponse, SignedOrder, TokenPairsItem } from '..
import { typeConverters } from './type_converters';
export const relayerResponseJsonParsers = {
- parseTokenPairsJson(json: any): TokenPairsItem[] {
- assert.doesConformToSchema('tokenPairs', json, schemas.relayerApiTokenPairsResponseSchema);
- return json.map((tokenPair: any) => {
- return typeConverters.convertStringsFieldsToBigNumbers(tokenPair, [
- 'tokenA.minAmount',
- 'tokenA.maxAmount',
- 'tokenB.minAmount',
- 'tokenB.maxAmount',
- ]);
- });
- },
- parseOrdersJson(json: any): SignedOrder[] {
- assert.doesConformToSchema('orders', json, schemas.signedOrdersSchema);
- return json.map((order: object) => typeConverters.convertOrderStringFieldsToBigNumber(order));
- },
- parseOrderJson(json: any): SignedOrder {
- assert.doesConformToSchema('order', json, schemas.signedOrderSchema);
- return typeConverters.convertOrderStringFieldsToBigNumber(json);
- },
- parseOrderbookResponseJson(json: any): OrderbookResponse {
- assert.doesConformToSchema('orderBook', json, schemas.relayerApiOrderBookResponseSchema);
- return typeConverters.convertOrderbookStringFieldsToBigNumber(json);
- },
- parseFeesResponseJson(json: any): FeesResponse {
- assert.doesConformToSchema('fees', json, schemas.relayerApiFeesResponseSchema);
- return typeConverters.convertStringsFieldsToBigNumbers(json, ['makerFee', 'takerFee']);
- },
+ parseTokenPairsJson(json: any): TokenPairsItem[] {
+ assert.doesConformToSchema('tokenPairs', json, schemas.relayerApiTokenPairsResponseSchema);
+ return json.map((tokenPair: any) => {
+ return typeConverters.convertStringsFieldsToBigNumbers(tokenPair, [
+ 'tokenA.minAmount',
+ 'tokenA.maxAmount',
+ 'tokenB.minAmount',
+ 'tokenB.maxAmount',
+ ]);
+ });
+ },
+ parseOrdersJson(json: any): SignedOrder[] {
+ assert.doesConformToSchema('orders', json, schemas.signedOrdersSchema);
+ return json.map((order: object) => typeConverters.convertOrderStringFieldsToBigNumber(order));
+ },
+ parseOrderJson(json: any): SignedOrder {
+ assert.doesConformToSchema('order', json, schemas.signedOrderSchema);
+ return typeConverters.convertOrderStringFieldsToBigNumber(json);
+ },
+ parseOrderbookResponseJson(json: any): OrderbookResponse {
+ assert.doesConformToSchema('orderBook', json, schemas.relayerApiOrderBookResponseSchema);
+ return typeConverters.convertOrderbookStringFieldsToBigNumber(json);
+ },
+ parseFeesResponseJson(json: any): FeesResponse {
+ assert.doesConformToSchema('fees', json, schemas.relayerApiFeesResponseSchema);
+ return typeConverters.convertStringsFieldsToBigNumbers(json, ['makerFee', 'takerFee']);
+ },
};
diff --git a/packages/connect/src/utils/type_converters.ts b/packages/connect/src/utils/type_converters.ts
index beea557c2..c1808ce8a 100644
--- a/packages/connect/src/utils/type_converters.ts
+++ b/packages/connect/src/utils/type_converters.ts
@@ -2,29 +2,29 @@ import { BigNumber } from '@0xproject/utils';
import * as _ from 'lodash';
export const typeConverters = {
- convertOrderbookStringFieldsToBigNumber(orderbook: any): any {
- const bids = _.get(orderbook, 'bids', []);
- const asks = _.get(orderbook, 'asks', []);
- return {
- bids: bids.map((order: any) => this.convertOrderStringFieldsToBigNumber(order)),
- asks: asks.map((order: any) => this.convertOrderStringFieldsToBigNumber(order)),
- };
- },
- convertOrderStringFieldsToBigNumber(order: any): any {
- return this.convertStringsFieldsToBigNumbers(order, [
- 'makerTokenAmount',
- 'takerTokenAmount',
- 'makerFee',
- 'takerFee',
- 'expirationUnixTimestampSec',
- 'salt',
- ]);
- },
- convertStringsFieldsToBigNumbers(obj: any, fields: string[]): any {
- const result = _.assign({}, obj);
- _.each(fields, field => {
- _.update(result, field, (value: string) => new BigNumber(value));
- });
- return result;
- },
+ convertOrderbookStringFieldsToBigNumber(orderbook: any): any {
+ const bids = _.get(orderbook, 'bids', []);
+ const asks = _.get(orderbook, 'asks', []);
+ return {
+ bids: bids.map((order: any) => this.convertOrderStringFieldsToBigNumber(order)),
+ asks: asks.map((order: any) => this.convertOrderStringFieldsToBigNumber(order)),
+ };
+ },
+ convertOrderStringFieldsToBigNumber(order: any): any {
+ return this.convertStringsFieldsToBigNumbers(order, [
+ 'makerTokenAmount',
+ 'takerTokenAmount',
+ 'makerFee',
+ 'takerFee',
+ 'expirationUnixTimestampSec',
+ 'salt',
+ ]);
+ },
+ convertStringsFieldsToBigNumbers(obj: any, fields: string[]): any {
+ const result = _.assign({}, obj);
+ _.each(fields, field => {
+ _.update(result, field, (value: string) => new BigNumber(value));
+ });
+ return result;
+ },
};
diff --git a/packages/connect/src/ws_orderbook_channel.ts b/packages/connect/src/ws_orderbook_channel.ts
index bbb47c649..822a022f4 100644
--- a/packages/connect/src/ws_orderbook_channel.ts
+++ b/packages/connect/src/ws_orderbook_channel.ts
@@ -4,12 +4,12 @@ import * as _ from 'lodash';
import * as WebSocket from 'websocket';
import {
- OrderbookChannel,
- OrderbookChannelHandler,
- OrderbookChannelMessageTypes,
- OrderbookChannelSubscriptionOpts,
- WebsocketClientEventType,
- WebsocketConnectionEventType,
+ OrderbookChannel,
+ OrderbookChannelHandler,
+ OrderbookChannelMessageTypes,
+ OrderbookChannelSubscriptionOpts,
+ WebsocketClientEventType,
+ WebsocketConnectionEventType,
} from './types';
import { orderbookChannelMessageParser } from './utils/orderbook_channel_message_parser';
@@ -18,117 +18,117 @@ import { orderbookChannelMessageParser } from './utils/orderbook_channel_message
* that implements the standard relayer API v0
*/
export class WebSocketOrderbookChannel implements OrderbookChannel {
- private _apiEndpointUrl: string;
- private _client: WebSocket.client;
- private _connectionIfExists?: WebSocket.connection;
- private _subscriptionCounter = 0;
- /**
- * Instantiates a new WebSocketOrderbookChannel instance
- * @param url The relayer API base WS url you would like to interact with
- * @return An instance of WebSocketOrderbookChannel
- */
- constructor(url: string) {
- assert.isUri('url', url);
- this._apiEndpointUrl = url;
- this._client = new WebSocket.client();
- }
- /**
- * Subscribe to orderbook snapshots and updates from the websocket
- * @param subscriptionOpts An OrderbookChannelSubscriptionOpts instance describing which
- * token pair to subscribe to
- * @param handler An OrderbookChannelHandler instance that responds to various
- * channel updates
- */
- public subscribe(subscriptionOpts: OrderbookChannelSubscriptionOpts, handler: OrderbookChannelHandler): void {
- assert.doesConformToSchema(
- 'subscriptionOpts',
- subscriptionOpts,
- schemas.relayerApiOrderbookChannelSubscribePayload,
- );
- assert.isFunction('handler.onSnapshot', _.get(handler, 'onSnapshot'));
- assert.isFunction('handler.onUpdate', _.get(handler, 'onUpdate'));
- assert.isFunction('handler.onError', _.get(handler, 'onError'));
- assert.isFunction('handler.onClose', _.get(handler, 'onClose'));
- this._subscriptionCounter += 1;
- const subscribeMessage = {
- type: 'subscribe',
- channel: 'orderbook',
- requestId: this._subscriptionCounter,
- payload: subscriptionOpts,
- };
- this._getConnection((error, connection) => {
- if (!_.isUndefined(error)) {
- handler.onError(this, subscriptionOpts, error);
- } else if (!_.isUndefined(connection) && connection.connected) {
- connection.on(WebsocketConnectionEventType.Error, wsError => {
- handler.onError(this, subscriptionOpts, wsError);
- });
- connection.on(WebsocketConnectionEventType.Close, () => {
- handler.onClose(this, subscriptionOpts);
- });
- connection.on(WebsocketConnectionEventType.Message, message => {
- this._handleWebSocketMessage(subscribeMessage.requestId, subscriptionOpts, message, handler);
- });
- connection.sendUTF(JSON.stringify(subscribeMessage));
- }
- });
- }
- /**
- * Close the websocket and stop receiving updates
- */
- public close() {
- if (!_.isUndefined(this._connectionIfExists)) {
- this._connectionIfExists.close();
- }
- }
- private _getConnection(callback: (error?: Error, connection?: WebSocket.connection) => void) {
- if (!_.isUndefined(this._connectionIfExists) && this._connectionIfExists.connected) {
- callback(undefined, this._connectionIfExists);
- } else {
- this._client.on(WebsocketClientEventType.Connect, connection => {
- this._connectionIfExists = connection;
- callback(undefined, this._connectionIfExists);
- });
- this._client.on(WebsocketClientEventType.ConnectFailed, error => {
- callback(error, undefined);
- });
- this._client.connect(this._apiEndpointUrl);
- }
- }
- private _handleWebSocketMessage(
- requestId: number,
- subscriptionOpts: OrderbookChannelSubscriptionOpts,
- message: WebSocket.IMessage,
- handler: OrderbookChannelHandler,
- ): void {
- if (!_.isUndefined(message.utf8Data)) {
- try {
- const utf8Data = message.utf8Data;
- const parserResult = orderbookChannelMessageParser.parse(utf8Data);
- if (parserResult.requestId === requestId) {
- switch (parserResult.type) {
- case OrderbookChannelMessageTypes.Snapshot: {
- handler.onSnapshot(this, subscriptionOpts, parserResult.payload);
- break;
- }
- case OrderbookChannelMessageTypes.Update: {
- handler.onUpdate(this, subscriptionOpts, parserResult.payload);
- break;
- }
- default: {
- handler.onError(
- this,
- subscriptionOpts,
- new Error(`Message has missing a type parameter: ${utf8Data}`),
- );
- }
- }
- }
- } catch (error) {
- handler.onError(this, subscriptionOpts, error);
- }
- } else {
- handler.onError(this, subscriptionOpts, new Error(`Message does not contain utf8Data`));
- }
- }
+ private _apiEndpointUrl: string;
+ private _client: WebSocket.client;
+ private _connectionIfExists?: WebSocket.connection;
+ private _subscriptionCounter = 0;
+ /**
+ * Instantiates a new WebSocketOrderbookChannel instance
+ * @param url The relayer API base WS url you would like to interact with
+ * @return An instance of WebSocketOrderbookChannel
+ */
+ constructor(url: string) {
+ assert.isUri('url', url);
+ this._apiEndpointUrl = url;
+ this._client = new WebSocket.client();
+ }
+ /**
+ * Subscribe to orderbook snapshots and updates from the websocket
+ * @param subscriptionOpts An OrderbookChannelSubscriptionOpts instance describing which
+ * token pair to subscribe to
+ * @param handler An OrderbookChannelHandler instance that responds to various
+ * channel updates
+ */
+ public subscribe(subscriptionOpts: OrderbookChannelSubscriptionOpts, handler: OrderbookChannelHandler): void {
+ assert.doesConformToSchema(
+ 'subscriptionOpts',
+ subscriptionOpts,
+ schemas.relayerApiOrderbookChannelSubscribePayload,
+ );
+ assert.isFunction('handler.onSnapshot', _.get(handler, 'onSnapshot'));
+ assert.isFunction('handler.onUpdate', _.get(handler, 'onUpdate'));
+ assert.isFunction('handler.onError', _.get(handler, 'onError'));
+ assert.isFunction('handler.onClose', _.get(handler, 'onClose'));
+ this._subscriptionCounter += 1;
+ const subscribeMessage = {
+ type: 'subscribe',
+ channel: 'orderbook',
+ requestId: this._subscriptionCounter,
+ payload: subscriptionOpts,
+ };
+ this._getConnection((error, connection) => {
+ if (!_.isUndefined(error)) {
+ handler.onError(this, subscriptionOpts, error);
+ } else if (!_.isUndefined(connection) && connection.connected) {
+ connection.on(WebsocketConnectionEventType.Error, wsError => {
+ handler.onError(this, subscriptionOpts, wsError);
+ });
+ connection.on(WebsocketConnectionEventType.Close, () => {
+ handler.onClose(this, subscriptionOpts);
+ });
+ connection.on(WebsocketConnectionEventType.Message, message => {
+ this._handleWebSocketMessage(subscribeMessage.requestId, subscriptionOpts, message, handler);
+ });
+ connection.sendUTF(JSON.stringify(subscribeMessage));
+ }
+ });
+ }
+ /**
+ * Close the websocket and stop receiving updates
+ */
+ public close() {
+ if (!_.isUndefined(this._connectionIfExists)) {
+ this._connectionIfExists.close();
+ }
+ }
+ private _getConnection(callback: (error?: Error, connection?: WebSocket.connection) => void) {
+ if (!_.isUndefined(this._connectionIfExists) && this._connectionIfExists.connected) {
+ callback(undefined, this._connectionIfExists);
+ } else {
+ this._client.on(WebsocketClientEventType.Connect, connection => {
+ this._connectionIfExists = connection;
+ callback(undefined, this._connectionIfExists);
+ });
+ this._client.on(WebsocketClientEventType.ConnectFailed, error => {
+ callback(error, undefined);
+ });
+ this._client.connect(this._apiEndpointUrl);
+ }
+ }
+ private _handleWebSocketMessage(
+ requestId: number,
+ subscriptionOpts: OrderbookChannelSubscriptionOpts,
+ message: WebSocket.IMessage,
+ handler: OrderbookChannelHandler,
+ ): void {
+ if (!_.isUndefined(message.utf8Data)) {
+ try {
+ const utf8Data = message.utf8Data;
+ const parserResult = orderbookChannelMessageParser.parse(utf8Data);
+ if (parserResult.requestId === requestId) {
+ switch (parserResult.type) {
+ case OrderbookChannelMessageTypes.Snapshot: {
+ handler.onSnapshot(this, subscriptionOpts, parserResult.payload);
+ break;
+ }
+ case OrderbookChannelMessageTypes.Update: {
+ handler.onUpdate(this, subscriptionOpts, parserResult.payload);
+ break;
+ }
+ default: {
+ handler.onError(
+ this,
+ subscriptionOpts,
+ new Error(`Message has missing a type parameter: ${utf8Data}`),
+ );
+ }
+ }
+ }
+ } catch (error) {
+ handler.onError(this, subscriptionOpts, error);
+ }
+ } else {
+ handler.onError(this, subscriptionOpts, new Error(`Message does not contain utf8Data`));
+ }
+ }
}
diff --git a/packages/connect/test/fixtures/standard_relayer_api/fees.json b/packages/connect/test/fixtures/standard_relayer_api/fees.json
index 9f24e93d2..483a74254 100644
--- a/packages/connect/test/fixtures/standard_relayer_api/fees.json
+++ b/packages/connect/test/fixtures/standard_relayer_api/fees.json
@@ -1,5 +1,5 @@
{
- "feeRecipient": "0x323b5d4c32345ced77393b3530b1eed0f346429d",
- "makerFee": "10000000000000000",
- "takerFee": "30000000000000000"
+ "feeRecipient": "0x323b5d4c32345ced77393b3530b1eed0f346429d",
+ "makerFee": "10000000000000000",
+ "takerFee": "30000000000000000"
}
diff --git a/packages/connect/test/fixtures/standard_relayer_api/fees.ts b/packages/connect/test/fixtures/standard_relayer_api/fees.ts
index 03981031a..fecbaacff 100644
--- a/packages/connect/test/fixtures/standard_relayer_api/fees.ts
+++ b/packages/connect/test/fixtures/standard_relayer_api/fees.ts
@@ -3,7 +3,7 @@ import { BigNumber } from '@0xproject/utils';
import { FeesResponse } from '../../../src/types';
export const feesResponse: FeesResponse = {
- feeRecipient: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
- makerFee: new BigNumber('10000000000000000'),
- takerFee: new BigNumber('30000000000000000'),
+ feeRecipient: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
+ makerFee: new BigNumber('10000000000000000'),
+ takerFee: new BigNumber('30000000000000000'),
};
diff --git a/packages/connect/test/fixtures/standard_relayer_api/order/0xabc67323774bdbd24d94f977fa9ac94a50f016026fd13f42990861238897721f.json b/packages/connect/test/fixtures/standard_relayer_api/order/0xabc67323774bdbd24d94f977fa9ac94a50f016026fd13f42990861238897721f.json
index c0423bf49..e84954b0d 100644
--- a/packages/connect/test/fixtures/standard_relayer_api/order/0xabc67323774bdbd24d94f977fa9ac94a50f016026fd13f42990861238897721f.json
+++ b/packages/connect/test/fixtures/standard_relayer_api/order/0xabc67323774bdbd24d94f977fa9ac94a50f016026fd13f42990861238897721f.json
@@ -1,19 +1,19 @@
{
- "maker": "0x9e56625509c2f60af937f23b7b532600390e8c8b",
- "taker": "0xa2b31dacf30a9c50ca473337c01d8a201ae33e32",
- "makerFee": "100000000000000",
- "takerFee": "200000000000000",
- "makerTokenAmount": "10000000000000000",
- "takerTokenAmount": "20000000000000000",
- "makerTokenAddress": "0x323b5d4c32345ced77393b3530b1eed0f346429d",
- "takerTokenAddress": "0xef7fff64389b814a946f3e92105513705ca6b990",
- "salt": "256",
- "feeRecipient": "0xb046140686d052fff581f63f8136cce132e857da",
- "exchangeContractAddress": "0x12459c951127e0c374ff9105dda097662a027093",
- "expirationUnixTimestampSec": "42",
- "ecSignature": {
- "v": 27,
- "r": "0x61a3ed31b43c8780e905a260a35faefcc527be7516aa11c0256729b5b351bc33",
- "s": "0x40349190569279751135161d22529dc25add4f6069af05be04cacbda2ace2254"
- }
+ "maker": "0x9e56625509c2f60af937f23b7b532600390e8c8b",
+ "taker": "0xa2b31dacf30a9c50ca473337c01d8a201ae33e32",
+ "makerFee": "100000000000000",
+ "takerFee": "200000000000000",
+ "makerTokenAmount": "10000000000000000",
+ "takerTokenAmount": "20000000000000000",
+ "makerTokenAddress": "0x323b5d4c32345ced77393b3530b1eed0f346429d",
+ "takerTokenAddress": "0xef7fff64389b814a946f3e92105513705ca6b990",
+ "salt": "256",
+ "feeRecipient": "0xb046140686d052fff581f63f8136cce132e857da",
+ "exchangeContractAddress": "0x12459c951127e0c374ff9105dda097662a027093",
+ "expirationUnixTimestampSec": "42",
+ "ecSignature": {
+ "v": 27,
+ "r": "0x61a3ed31b43c8780e905a260a35faefcc527be7516aa11c0256729b5b351bc33",
+ "s": "0x40349190569279751135161d22529dc25add4f6069af05be04cacbda2ace2254"
+ }
}
diff --git a/packages/connect/test/fixtures/standard_relayer_api/order/0xabc67323774bdbd24d94f977fa9ac94a50f016026fd13f42990861238897721f.ts b/packages/connect/test/fixtures/standard_relayer_api/order/0xabc67323774bdbd24d94f977fa9ac94a50f016026fd13f42990861238897721f.ts
index 459a87919..5a03a2ff6 100644
--- a/packages/connect/test/fixtures/standard_relayer_api/order/0xabc67323774bdbd24d94f977fa9ac94a50f016026fd13f42990861238897721f.ts
+++ b/packages/connect/test/fixtures/standard_relayer_api/order/0xabc67323774bdbd24d94f977fa9ac94a50f016026fd13f42990861238897721f.ts
@@ -1,21 +1,21 @@
import { BigNumber } from '@0xproject/utils';
export const orderResponse = {
- maker: '0x9e56625509c2f60af937f23b7b532600390e8c8b',
- taker: '0xa2b31dacf30a9c50ca473337c01d8a201ae33e32',
- makerFee: new BigNumber('100000000000000'),
- takerFee: new BigNumber('200000000000000'),
- makerTokenAmount: new BigNumber('10000000000000000'),
- takerTokenAmount: new BigNumber('20000000000000000'),
- makerTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
- takerTokenAddress: '0xef7fff64389b814a946f3e92105513705ca6b990',
- salt: new BigNumber('256'),
- feeRecipient: '0xb046140686d052fff581f63f8136cce132e857da',
- exchangeContractAddress: '0x12459c951127e0c374ff9105dda097662a027093',
- expirationUnixTimestampSec: new BigNumber('42'),
- ecSignature: {
- v: 27,
- r: '0x61a3ed31b43c8780e905a260a35faefcc527be7516aa11c0256729b5b351bc33',
- s: '0x40349190569279751135161d22529dc25add4f6069af05be04cacbda2ace2254',
- },
+ maker: '0x9e56625509c2f60af937f23b7b532600390e8c8b',
+ taker: '0xa2b31dacf30a9c50ca473337c01d8a201ae33e32',
+ makerFee: new BigNumber('100000000000000'),
+ takerFee: new BigNumber('200000000000000'),
+ makerTokenAmount: new BigNumber('10000000000000000'),
+ takerTokenAmount: new BigNumber('20000000000000000'),
+ makerTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
+ takerTokenAddress: '0xef7fff64389b814a946f3e92105513705ca6b990',
+ salt: new BigNumber('256'),
+ feeRecipient: '0xb046140686d052fff581f63f8136cce132e857da',
+ exchangeContractAddress: '0x12459c951127e0c374ff9105dda097662a027093',
+ expirationUnixTimestampSec: new BigNumber('42'),
+ ecSignature: {
+ v: 27,
+ r: '0x61a3ed31b43c8780e905a260a35faefcc527be7516aa11c0256729b5b351bc33',
+ s: '0x40349190569279751135161d22529dc25add4f6069af05be04cacbda2ace2254',
+ },
};
diff --git a/packages/connect/test/fixtures/standard_relayer_api/orderbook.json b/packages/connect/test/fixtures/standard_relayer_api/orderbook.json
index 569498312..825be34c2 100644
--- a/packages/connect/test/fixtures/standard_relayer_api/orderbook.json
+++ b/packages/connect/test/fixtures/standard_relayer_api/orderbook.json
@@ -1,44 +1,44 @@
{
- "bids": [
- {
- "maker": "0x9e56625509c2f60af937f23b7b532600390e8c8b",
- "taker": "0xa2b31dacf30a9c50ca473337c01d8a201ae33e32",
- "makerFee": "100000000000000",
- "takerFee": "200000000000000",
- "makerTokenAmount": "10000000000000000",
- "takerTokenAmount": "20000000000000000",
- "makerTokenAddress": "0x323b5d4c32345ced77393b3530b1eed0f346429d",
- "takerTokenAddress": "0xef7fff64389b814a946f3e92105513705ca6b990",
- "salt": "256",
- "feeRecipient": "0xb046140686d052fff581f63f8136cce132e857da",
- "exchangeContractAddress": "0x12459c951127e0c374ff9105dda097662a027093",
- "expirationUnixTimestampSec": "42",
- "ecSignature": {
- "v": 27,
- "r": "0x61a3ed31b43c8780e905a260a35faefcc527be7516aa11c0256729b5b351bc33",
- "s": "0x40349190569279751135161d22529dc25add4f6069af05be04cacbda2ace2254"
- }
- }
- ],
- "asks": [
- {
- "maker": "0x9e56625509c2f60af937f23b7b532600390e8c8b",
- "taker": "0xa2b31dacf30a9c50ca473337c01d8a201ae33e32",
- "makerFee": "100000000000000",
- "takerFee": "200000000000000",
- "makerTokenAmount": "10000000000000000",
- "takerTokenAmount": "20000000000000000",
- "makerTokenAddress": "0x323b5d4c32345ced77393b3530b1eed0f346429d",
- "takerTokenAddress": "0xef7fff64389b814a946f3e92105513705ca6b990",
- "salt": "256",
- "feeRecipient": "0xb046140686d052fff581f63f8136cce132e857da",
- "exchangeContractAddress": "0x12459c951127e0c374ff9105dda097662a027093",
- "expirationUnixTimestampSec": "42",
- "ecSignature": {
- "v": 27,
- "r": "0x61a3ed31b43c8780e905a260a35faefcc527be7516aa11c0256729b5b351bc33",
- "s": "0x40349190569279751135161d22529dc25add4f6069af05be04cacbda2ace2254"
- }
- }
- ]
+ "bids": [
+ {
+ "maker": "0x9e56625509c2f60af937f23b7b532600390e8c8b",
+ "taker": "0xa2b31dacf30a9c50ca473337c01d8a201ae33e32",
+ "makerFee": "100000000000000",
+ "takerFee": "200000000000000",
+ "makerTokenAmount": "10000000000000000",
+ "takerTokenAmount": "20000000000000000",
+ "makerTokenAddress": "0x323b5d4c32345ced77393b3530b1eed0f346429d",
+ "takerTokenAddress": "0xef7fff64389b814a946f3e92105513705ca6b990",
+ "salt": "256",
+ "feeRecipient": "0xb046140686d052fff581f63f8136cce132e857da",
+ "exchangeContractAddress": "0x12459c951127e0c374ff9105dda097662a027093",
+ "expirationUnixTimestampSec": "42",
+ "ecSignature": {
+ "v": 27,
+ "r": "0x61a3ed31b43c8780e905a260a35faefcc527be7516aa11c0256729b5b351bc33",
+ "s": "0x40349190569279751135161d22529dc25add4f6069af05be04cacbda2ace2254"
+ }
+ }
+ ],
+ "asks": [
+ {
+ "maker": "0x9e56625509c2f60af937f23b7b532600390e8c8b",
+ "taker": "0xa2b31dacf30a9c50ca473337c01d8a201ae33e32",
+ "makerFee": "100000000000000",
+ "takerFee": "200000000000000",
+ "makerTokenAmount": "10000000000000000",
+ "takerTokenAmount": "20000000000000000",
+ "makerTokenAddress": "0x323b5d4c32345ced77393b3530b1eed0f346429d",
+ "takerTokenAddress": "0xef7fff64389b814a946f3e92105513705ca6b990",
+ "salt": "256",
+ "feeRecipient": "0xb046140686d052fff581f63f8136cce132e857da",
+ "exchangeContractAddress": "0x12459c951127e0c374ff9105dda097662a027093",
+ "expirationUnixTimestampSec": "42",
+ "ecSignature": {
+ "v": 27,
+ "r": "0x61a3ed31b43c8780e905a260a35faefcc527be7516aa11c0256729b5b351bc33",
+ "s": "0x40349190569279751135161d22529dc25add4f6069af05be04cacbda2ace2254"
+ }
+ }
+ ]
}
diff --git a/packages/connect/test/fixtures/standard_relayer_api/orderbook.ts b/packages/connect/test/fixtures/standard_relayer_api/orderbook.ts
index 90d3d9220..6684ac2e5 100644
--- a/packages/connect/test/fixtures/standard_relayer_api/orderbook.ts
+++ b/packages/connect/test/fixtures/standard_relayer_api/orderbook.ts
@@ -1,46 +1,46 @@
import { BigNumber } from '@0xproject/utils';
export const orderbookResponse = {
- bids: [
- {
- maker: '0x9e56625509c2f60af937f23b7b532600390e8c8b',
- taker: '0xa2b31dacf30a9c50ca473337c01d8a201ae33e32',
- makerFee: new BigNumber('100000000000000'),
- takerFee: new BigNumber('200000000000000'),
- makerTokenAmount: new BigNumber('10000000000000000'),
- takerTokenAmount: new BigNumber('20000000000000000'),
- makerTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
- takerTokenAddress: '0xef7fff64389b814a946f3e92105513705ca6b990',
- salt: new BigNumber('256'),
- feeRecipient: '0xb046140686d052fff581f63f8136cce132e857da',
- exchangeContractAddress: '0x12459c951127e0c374ff9105dda097662a027093',
- expirationUnixTimestampSec: new BigNumber('42'),
- ecSignature: {
- v: 27,
- r: '0x61a3ed31b43c8780e905a260a35faefcc527be7516aa11c0256729b5b351bc33',
- s: '0x40349190569279751135161d22529dc25add4f6069af05be04cacbda2ace2254',
- },
- },
- ],
- asks: [
- {
- maker: '0x9e56625509c2f60af937f23b7b532600390e8c8b',
- taker: '0xa2b31dacf30a9c50ca473337c01d8a201ae33e32',
- makerFee: new BigNumber('100000000000000'),
- takerFee: new BigNumber('200000000000000'),
- makerTokenAmount: new BigNumber('10000000000000000'),
- takerTokenAmount: new BigNumber('20000000000000000'),
- makerTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
- takerTokenAddress: '0xef7fff64389b814a946f3e92105513705ca6b990',
- salt: new BigNumber('256'),
- feeRecipient: '0xb046140686d052fff581f63f8136cce132e857da',
- exchangeContractAddress: '0x12459c951127e0c374ff9105dda097662a027093',
- expirationUnixTimestampSec: new BigNumber('42'),
- ecSignature: {
- v: 27,
- r: '0x61a3ed31b43c8780e905a260a35faefcc527be7516aa11c0256729b5b351bc33',
- s: '0x40349190569279751135161d22529dc25add4f6069af05be04cacbda2ace2254',
- },
- },
- ],
+ bids: [
+ {
+ maker: '0x9e56625509c2f60af937f23b7b532600390e8c8b',
+ taker: '0xa2b31dacf30a9c50ca473337c01d8a201ae33e32',
+ makerFee: new BigNumber('100000000000000'),
+ takerFee: new BigNumber('200000000000000'),
+ makerTokenAmount: new BigNumber('10000000000000000'),
+ takerTokenAmount: new BigNumber('20000000000000000'),
+ makerTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
+ takerTokenAddress: '0xef7fff64389b814a946f3e92105513705ca6b990',
+ salt: new BigNumber('256'),
+ feeRecipient: '0xb046140686d052fff581f63f8136cce132e857da',
+ exchangeContractAddress: '0x12459c951127e0c374ff9105dda097662a027093',
+ expirationUnixTimestampSec: new BigNumber('42'),
+ ecSignature: {
+ v: 27,
+ r: '0x61a3ed31b43c8780e905a260a35faefcc527be7516aa11c0256729b5b351bc33',
+ s: '0x40349190569279751135161d22529dc25add4f6069af05be04cacbda2ace2254',
+ },
+ },
+ ],
+ asks: [
+ {
+ maker: '0x9e56625509c2f60af937f23b7b532600390e8c8b',
+ taker: '0xa2b31dacf30a9c50ca473337c01d8a201ae33e32',
+ makerFee: new BigNumber('100000000000000'),
+ takerFee: new BigNumber('200000000000000'),
+ makerTokenAmount: new BigNumber('10000000000000000'),
+ takerTokenAmount: new BigNumber('20000000000000000'),
+ makerTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
+ takerTokenAddress: '0xef7fff64389b814a946f3e92105513705ca6b990',
+ salt: new BigNumber('256'),
+ feeRecipient: '0xb046140686d052fff581f63f8136cce132e857da',
+ exchangeContractAddress: '0x12459c951127e0c374ff9105dda097662a027093',
+ expirationUnixTimestampSec: new BigNumber('42'),
+ ecSignature: {
+ v: 27,
+ r: '0x61a3ed31b43c8780e905a260a35faefcc527be7516aa11c0256729b5b351bc33',
+ s: '0x40349190569279751135161d22529dc25add4f6069af05be04cacbda2ace2254',
+ },
+ },
+ ],
};
diff --git a/packages/connect/test/fixtures/standard_relayer_api/orders.json b/packages/connect/test/fixtures/standard_relayer_api/orders.json
index 97d21abd8..cfa780dc4 100644
--- a/packages/connect/test/fixtures/standard_relayer_api/orders.json
+++ b/packages/connect/test/fixtures/standard_relayer_api/orders.json
@@ -1,21 +1,21 @@
[
- {
- "maker": "0x9e56625509c2f60af937f23b7b532600390e8c8b",
- "taker": "0xa2b31dacf30a9c50ca473337c01d8a201ae33e32",
- "makerFee": "100000000000000",
- "takerFee": "200000000000000",
- "makerTokenAmount": "10000000000000000",
- "takerTokenAmount": "20000000000000000",
- "makerTokenAddress": "0x323b5d4c32345ced77393b3530b1eed0f346429d",
- "takerTokenAddress": "0xef7fff64389b814a946f3e92105513705ca6b990",
- "salt": "256",
- "feeRecipient": "0x9e56625509c2f60af937f23b7b532600390e8c8b",
- "exchangeContractAddress": "0x9e56625509c2f60af937f23b7b532600390e8c8b",
- "expirationUnixTimestampSec": "42",
- "ecSignature": {
- "v": 27,
- "r": "0x61a3ed31b43c8780e905a260a35faefcc527be7516aa11c0256729b5b351bc33",
- "s": "0x40349190569279751135161d22529dc25add4f6069af05be04cacbda2ace2254"
- }
- }
+ {
+ "maker": "0x9e56625509c2f60af937f23b7b532600390e8c8b",
+ "taker": "0xa2b31dacf30a9c50ca473337c01d8a201ae33e32",
+ "makerFee": "100000000000000",
+ "takerFee": "200000000000000",
+ "makerTokenAmount": "10000000000000000",
+ "takerTokenAmount": "20000000000000000",
+ "makerTokenAddress": "0x323b5d4c32345ced77393b3530b1eed0f346429d",
+ "takerTokenAddress": "0xef7fff64389b814a946f3e92105513705ca6b990",
+ "salt": "256",
+ "feeRecipient": "0x9e56625509c2f60af937f23b7b532600390e8c8b",
+ "exchangeContractAddress": "0x9e56625509c2f60af937f23b7b532600390e8c8b",
+ "expirationUnixTimestampSec": "42",
+ "ecSignature": {
+ "v": 27,
+ "r": "0x61a3ed31b43c8780e905a260a35faefcc527be7516aa11c0256729b5b351bc33",
+ "s": "0x40349190569279751135161d22529dc25add4f6069af05be04cacbda2ace2254"
+ }
+ }
]
diff --git a/packages/connect/test/fixtures/standard_relayer_api/orders.ts b/packages/connect/test/fixtures/standard_relayer_api/orders.ts
index 701346971..5044777bd 100644
--- a/packages/connect/test/fixtures/standard_relayer_api/orders.ts
+++ b/packages/connect/test/fixtures/standard_relayer_api/orders.ts
@@ -1,23 +1,23 @@
import { BigNumber } from '@0xproject/utils';
export const ordersResponse = [
- {
- maker: '0x9e56625509c2f60af937f23b7b532600390e8c8b',
- taker: '0xa2b31dacf30a9c50ca473337c01d8a201ae33e32',
- makerFee: new BigNumber('100000000000000'),
- takerFee: new BigNumber('200000000000000'),
- makerTokenAmount: new BigNumber('10000000000000000'),
- takerTokenAmount: new BigNumber('20000000000000000'),
- makerTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
- takerTokenAddress: '0xef7fff64389b814a946f3e92105513705ca6b990',
- salt: new BigNumber('256'),
- feeRecipient: '0x9e56625509c2f60af937f23b7b532600390e8c8b',
- exchangeContractAddress: '0x9e56625509c2f60af937f23b7b532600390e8c8b',
- expirationUnixTimestampSec: new BigNumber('42'),
- ecSignature: {
- v: 27,
- r: '0x61a3ed31b43c8780e905a260a35faefcc527be7516aa11c0256729b5b351bc33',
- s: '0x40349190569279751135161d22529dc25add4f6069af05be04cacbda2ace2254',
- },
- },
+ {
+ maker: '0x9e56625509c2f60af937f23b7b532600390e8c8b',
+ taker: '0xa2b31dacf30a9c50ca473337c01d8a201ae33e32',
+ makerFee: new BigNumber('100000000000000'),
+ takerFee: new BigNumber('200000000000000'),
+ makerTokenAmount: new BigNumber('10000000000000000'),
+ takerTokenAmount: new BigNumber('20000000000000000'),
+ makerTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
+ takerTokenAddress: '0xef7fff64389b814a946f3e92105513705ca6b990',
+ salt: new BigNumber('256'),
+ feeRecipient: '0x9e56625509c2f60af937f23b7b532600390e8c8b',
+ exchangeContractAddress: '0x9e56625509c2f60af937f23b7b532600390e8c8b',
+ expirationUnixTimestampSec: new BigNumber('42'),
+ ecSignature: {
+ v: 27,
+ r: '0x61a3ed31b43c8780e905a260a35faefcc527be7516aa11c0256729b5b351bc33',
+ s: '0x40349190569279751135161d22529dc25add4f6069af05be04cacbda2ace2254',
+ },
+ },
];
diff --git a/packages/connect/test/fixtures/standard_relayer_api/token_pairs.json b/packages/connect/test/fixtures/standard_relayer_api/token_pairs.json
index 8d2ea328a..90f57a974 100644
--- a/packages/connect/test/fixtures/standard_relayer_api/token_pairs.json
+++ b/packages/connect/test/fixtures/standard_relayer_api/token_pairs.json
@@ -1,16 +1,16 @@
[
- {
- "tokenA": {
- "address": "0x323b5d4c32345ced77393b3530b1eed0f346429d",
- "minAmount": "0",
- "maxAmount": "10000000000000000000",
- "precision": 5
- },
- "tokenB": {
- "address": "0xef7fff64389b814a946f3e92105513705ca6b990",
- "minAmount": "0",
- "maxAmount": "50000000000000000000",
- "precision": 5
- }
- }
+ {
+ "tokenA": {
+ "address": "0x323b5d4c32345ced77393b3530b1eed0f346429d",
+ "minAmount": "0",
+ "maxAmount": "10000000000000000000",
+ "precision": 5
+ },
+ "tokenB": {
+ "address": "0xef7fff64389b814a946f3e92105513705ca6b990",
+ "minAmount": "0",
+ "maxAmount": "50000000000000000000",
+ "precision": 5
+ }
+ }
]
diff --git a/packages/connect/test/fixtures/standard_relayer_api/token_pairs.ts b/packages/connect/test/fixtures/standard_relayer_api/token_pairs.ts
index 80ecb8207..f48b1e877 100644
--- a/packages/connect/test/fixtures/standard_relayer_api/token_pairs.ts
+++ b/packages/connect/test/fixtures/standard_relayer_api/token_pairs.ts
@@ -3,18 +3,18 @@ import { BigNumber } from '@0xproject/utils';
import { TokenPairsItem } from '../../../src/types';
export const tokenPairsResponse: TokenPairsItem[] = [
- {
- tokenA: {
- address: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
- minAmount: new BigNumber(0),
- maxAmount: new BigNumber('10000000000000000000'),
- precision: 5,
- },
- tokenB: {
- address: '0xef7fff64389b814a946f3e92105513705ca6b990',
- minAmount: new BigNumber(0),
- maxAmount: new BigNumber('50000000000000000000'),
- precision: 5,
- },
- },
+ {
+ tokenA: {
+ address: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
+ minAmount: new BigNumber(0),
+ maxAmount: new BigNumber('10000000000000000000'),
+ precision: 5,
+ },
+ tokenB: {
+ address: '0xef7fff64389b814a946f3e92105513705ca6b990',
+ minAmount: new BigNumber(0),
+ maxAmount: new BigNumber('50000000000000000000'),
+ precision: 5,
+ },
+ },
];
diff --git a/packages/connect/test/http_client_test.ts b/packages/connect/test/http_client_test.ts
index 00bde2fc6..15759d911 100644
--- a/packages/connect/test/http_client_test.ts
+++ b/packages/connect/test/http_client_test.ts
@@ -24,128 +24,128 @@ chai.use(chaiAsPromised);
const expect = chai.expect;
describe('HttpClient', () => {
- const relayUrl = 'https://example.com';
- const relayerClient = new HttpClient(relayUrl);
- afterEach(() => {
- fetchMock.restore();
- });
- describe('#constructor', () => {
- it('should remove trailing slashes from api url', async () => {
- const urlWithTrailingSlash = 'https://slash.com/';
- const urlWithoutTrailingSlash = 'https://slash.com';
- const client = new HttpClient(urlWithTrailingSlash);
- const sanitizedUrl = (client as any)._apiEndpointUrl;
- expect(sanitizedUrl).to.be.deep.equal(urlWithoutTrailingSlash);
- });
- });
- describe('#getTokenPairsAsync', () => {
- const url = `${relayUrl}/token_pairs`;
- it('gets token pairs', async () => {
- fetchMock.get(url, tokenPairsResponseJSON);
- const tokenPairs = await relayerClient.getTokenPairsAsync();
- expect(tokenPairs).to.be.deep.equal(tokenPairsResponse);
- });
- it('gets specific token pairs for request', async () => {
- const tokenAddress = '0x323b5d4c32345ced77393b3530b1eed0f346429d';
- const tokenPairsRequest = {
- tokenA: tokenAddress,
- };
- const urlWithQuery = `${url}?tokenA=${tokenAddress}`;
- fetchMock.get(urlWithQuery, tokenPairsResponseJSON);
- const tokenPairs = await relayerClient.getTokenPairsAsync(tokenPairsRequest);
- expect(tokenPairs).to.be.deep.equal(tokenPairsResponse);
- });
- it('throws an error for invalid JSON response', async () => {
- fetchMock.get(url, { test: 'dummy' });
- expect(relayerClient.getTokenPairsAsync()).to.be.rejected();
- });
- });
- describe('#getOrdersAsync', () => {
- const url = `${relayUrl}/orders`;
- it('gets orders', async () => {
- fetchMock.get(url, ordersResponseJSON);
- const orders = await relayerClient.getOrdersAsync();
- expect(orders).to.be.deep.equal(ordersResponse);
- });
- it('gets specific orders for request', async () => {
- const tokenAddress = '0x323b5d4c32345ced77393b3530b1eed0f346429d';
- const ordersRequest = {
- tokenAddress,
- };
- const urlWithQuery = `${url}?tokenAddress=${tokenAddress}`;
- fetchMock.get(urlWithQuery, ordersResponseJSON);
- const orders = await relayerClient.getOrdersAsync(ordersRequest);
- expect(orders).to.be.deep.equal(ordersResponse);
- });
- it('throws an error for invalid JSON response', async () => {
- fetchMock.get(url, { test: 'dummy' });
- expect(relayerClient.getOrdersAsync()).to.be.rejected();
- });
- });
- describe('#getOrderAsync', () => {
- const orderHash = '0xabc67323774bdbd24d94f977fa9ac94a50f016026fd13f42990861238897721f';
- const url = `${relayUrl}/order/${orderHash}`;
- it('gets order', async () => {
- fetchMock.get(url, orderResponseJSON);
- const order = await relayerClient.getOrderAsync(orderHash);
- expect(order).to.be.deep.equal(orderResponse);
- });
- it('throws an error for invalid JSON response', async () => {
- fetchMock.get(url, { test: 'dummy' });
- expect(relayerClient.getOrderAsync(orderHash)).to.be.rejected();
- });
- });
- describe('#getOrderBookAsync', () => {
- const request = {
- baseTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
- quoteTokenAddress: '0xa2b31dacf30a9c50ca473337c01d8a201ae33e32',
- };
- const url = `${relayUrl}/orderbook?baseTokenAddress=${request.baseTokenAddress}&quoteTokenAddress=${
- request.quoteTokenAddress
- }`;
- it('gets order book', async () => {
- fetchMock.get(url, orderbookJSON);
- const orderbook = await relayerClient.getOrderbookAsync(request);
- expect(orderbook).to.be.deep.equal(orderbookResponse);
- });
- it('throws an error for invalid JSON response', async () => {
- fetchMock.get(url, { test: 'dummy' });
- expect(relayerClient.getOrderbookAsync(request)).to.be.rejected();
- });
- });
- describe('#getFeesAsync', () => {
- const request = {
- exchangeContractAddress: '0x12459c951127e0c374ff9105dda097662a027093',
- maker: '0x9e56625509c2f60af937f23b7b532600390e8c8b',
- taker: '0xa2b31dacf30a9c50ca473337c01d8a201ae33e32',
- makerTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
- takerTokenAddress: '0xef7fff64389b814a946f3e92105513705ca6b990',
- makerTokenAmount: new BigNumber('10000000000000000000'),
- takerTokenAmount: new BigNumber('30000000000000000000'),
- salt: new BigNumber('256'),
- expirationUnixTimestampSec: new BigNumber('42'),
- };
- const url = `${relayUrl}/fees`;
- it('gets fees', async () => {
- fetchMock.post(url, feesResponseJSON);
- const fees = await relayerClient.getFeesAsync(request);
- expect(fees).to.be.deep.equal(feesResponse);
- });
- it('does not mutate input', async () => {
- fetchMock.post(url, feesResponseJSON);
- const makerTokenAmountBefore = new BigNumber(request.makerTokenAmount);
- const takerTokenAmountBefore = new BigNumber(request.takerTokenAmount);
- const saltBefore = new BigNumber(request.salt);
- const expirationUnixTimestampSecBefore = new BigNumber(request.expirationUnixTimestampSec);
- await relayerClient.getFeesAsync(request);
- expect(makerTokenAmountBefore).to.be.deep.equal(request.makerTokenAmount);
- expect(takerTokenAmountBefore).to.be.deep.equal(request.takerTokenAmount);
- expect(saltBefore).to.be.deep.equal(request.salt);
- expect(expirationUnixTimestampSecBefore).to.be.deep.equal(request.expirationUnixTimestampSec);
- });
- it('throws an error for invalid JSON response', async () => {
- fetchMock.post(url, { test: 'dummy' });
- expect(relayerClient.getFeesAsync(request)).to.be.rejected();
- });
- });
+ const relayUrl = 'https://example.com';
+ const relayerClient = new HttpClient(relayUrl);
+ afterEach(() => {
+ fetchMock.restore();
+ });
+ describe('#constructor', () => {
+ it('should remove trailing slashes from api url', async () => {
+ const urlWithTrailingSlash = 'https://slash.com/';
+ const urlWithoutTrailingSlash = 'https://slash.com';
+ const client = new HttpClient(urlWithTrailingSlash);
+ const sanitizedUrl = (client as any)._apiEndpointUrl;
+ expect(sanitizedUrl).to.be.deep.equal(urlWithoutTrailingSlash);
+ });
+ });
+ describe('#getTokenPairsAsync', () => {
+ const url = `${relayUrl}/token_pairs`;
+ it('gets token pairs', async () => {
+ fetchMock.get(url, tokenPairsResponseJSON);
+ const tokenPairs = await relayerClient.getTokenPairsAsync();
+ expect(tokenPairs).to.be.deep.equal(tokenPairsResponse);
+ });
+ it('gets specific token pairs for request', async () => {
+ const tokenAddress = '0x323b5d4c32345ced77393b3530b1eed0f346429d';
+ const tokenPairsRequest = {
+ tokenA: tokenAddress,
+ };
+ const urlWithQuery = `${url}?tokenA=${tokenAddress}`;
+ fetchMock.get(urlWithQuery, tokenPairsResponseJSON);
+ const tokenPairs = await relayerClient.getTokenPairsAsync(tokenPairsRequest);
+ expect(tokenPairs).to.be.deep.equal(tokenPairsResponse);
+ });
+ it('throws an error for invalid JSON response', async () => {
+ fetchMock.get(url, { test: 'dummy' });
+ expect(relayerClient.getTokenPairsAsync()).to.be.rejected();
+ });
+ });
+ describe('#getOrdersAsync', () => {
+ const url = `${relayUrl}/orders`;
+ it('gets orders', async () => {
+ fetchMock.get(url, ordersResponseJSON);
+ const orders = await relayerClient.getOrdersAsync();
+ expect(orders).to.be.deep.equal(ordersResponse);
+ });
+ it('gets specific orders for request', async () => {
+ const tokenAddress = '0x323b5d4c32345ced77393b3530b1eed0f346429d';
+ const ordersRequest = {
+ tokenAddress,
+ };
+ const urlWithQuery = `${url}?tokenAddress=${tokenAddress}`;
+ fetchMock.get(urlWithQuery, ordersResponseJSON);
+ const orders = await relayerClient.getOrdersAsync(ordersRequest);
+ expect(orders).to.be.deep.equal(ordersResponse);
+ });
+ it('throws an error for invalid JSON response', async () => {
+ fetchMock.get(url, { test: 'dummy' });
+ expect(relayerClient.getOrdersAsync()).to.be.rejected();
+ });
+ });
+ describe('#getOrderAsync', () => {
+ const orderHash = '0xabc67323774bdbd24d94f977fa9ac94a50f016026fd13f42990861238897721f';
+ const url = `${relayUrl}/order/${orderHash}`;
+ it('gets order', async () => {
+ fetchMock.get(url, orderResponseJSON);
+ const order = await relayerClient.getOrderAsync(orderHash);
+ expect(order).to.be.deep.equal(orderResponse);
+ });
+ it('throws an error for invalid JSON response', async () => {
+ fetchMock.get(url, { test: 'dummy' });
+ expect(relayerClient.getOrderAsync(orderHash)).to.be.rejected();
+ });
+ });
+ describe('#getOrderBookAsync', () => {
+ const request = {
+ baseTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
+ quoteTokenAddress: '0xa2b31dacf30a9c50ca473337c01d8a201ae33e32',
+ };
+ const url = `${relayUrl}/orderbook?baseTokenAddress=${request.baseTokenAddress}&quoteTokenAddress=${
+ request.quoteTokenAddress
+ }`;
+ it('gets order book', async () => {
+ fetchMock.get(url, orderbookJSON);
+ const orderbook = await relayerClient.getOrderbookAsync(request);
+ expect(orderbook).to.be.deep.equal(orderbookResponse);
+ });
+ it('throws an error for invalid JSON response', async () => {
+ fetchMock.get(url, { test: 'dummy' });
+ expect(relayerClient.getOrderbookAsync(request)).to.be.rejected();
+ });
+ });
+ describe('#getFeesAsync', () => {
+ const request = {
+ exchangeContractAddress: '0x12459c951127e0c374ff9105dda097662a027093',
+ maker: '0x9e56625509c2f60af937f23b7b532600390e8c8b',
+ taker: '0xa2b31dacf30a9c50ca473337c01d8a201ae33e32',
+ makerTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
+ takerTokenAddress: '0xef7fff64389b814a946f3e92105513705ca6b990',
+ makerTokenAmount: new BigNumber('10000000000000000000'),
+ takerTokenAmount: new BigNumber('30000000000000000000'),
+ salt: new BigNumber('256'),
+ expirationUnixTimestampSec: new BigNumber('42'),
+ };
+ const url = `${relayUrl}/fees`;
+ it('gets fees', async () => {
+ fetchMock.post(url, feesResponseJSON);
+ const fees = await relayerClient.getFeesAsync(request);
+ expect(fees).to.be.deep.equal(feesResponse);
+ });
+ it('does not mutate input', async () => {
+ fetchMock.post(url, feesResponseJSON);
+ const makerTokenAmountBefore = new BigNumber(request.makerTokenAmount);
+ const takerTokenAmountBefore = new BigNumber(request.takerTokenAmount);
+ const saltBefore = new BigNumber(request.salt);
+ const expirationUnixTimestampSecBefore = new BigNumber(request.expirationUnixTimestampSec);
+ await relayerClient.getFeesAsync(request);
+ expect(makerTokenAmountBefore).to.be.deep.equal(request.makerTokenAmount);
+ expect(takerTokenAmountBefore).to.be.deep.equal(request.takerTokenAmount);
+ expect(saltBefore).to.be.deep.equal(request.salt);
+ expect(expirationUnixTimestampSecBefore).to.be.deep.equal(request.expirationUnixTimestampSec);
+ });
+ it('throws an error for invalid JSON response', async () => {
+ fetchMock.post(url, { test: 'dummy' });
+ expect(relayerClient.getFeesAsync(request)).to.be.rejected();
+ });
+ });
});
diff --git a/packages/connect/test/orderbook_channel_message_parsers_test.ts b/packages/connect/test/orderbook_channel_message_parsers_test.ts
index 04ca157a8..3e1f44384 100644
--- a/packages/connect/test/orderbook_channel_message_parsers_test.ts
+++ b/packages/connect/test/orderbook_channel_message_parsers_test.ts
@@ -7,13 +7,13 @@ import { orderbookChannelMessageParser } from '../src/utils/orderbook_channel_me
import { orderResponse } from './fixtures/standard_relayer_api/order/0xabc67323774bdbd24d94f977fa9ac94a50f016026fd13f42990861238897721f';
import { orderbookResponse } from './fixtures/standard_relayer_api/orderbook';
import {
- malformedSnapshotOrderbookChannelMessage,
- snapshotOrderbookChannelMessage,
+ malformedSnapshotOrderbookChannelMessage,
+ snapshotOrderbookChannelMessage,
} from './fixtures/standard_relayer_api/snapshot_orderbook_channel_message';
import { unknownOrderbookChannelMessage } from './fixtures/standard_relayer_api/unknown_orderbook_channel_message';
import {
- malformedUpdateOrderbookChannelMessage,
- updateOrderbookChannelMessage,
+ malformedUpdateOrderbookChannelMessage,
+ updateOrderbookChannelMessage,
} from './fixtures/standard_relayer_api/update_orderbook_channel_message';
chai.config.includeStack = true;
@@ -21,56 +21,56 @@ chai.use(dirtyChai);
const expect = chai.expect;
describe('orderbookChannelMessageParser', () => {
- describe('#parser', () => {
- it('parses snapshot messages', () => {
- const snapshotMessage = orderbookChannelMessageParser.parse(snapshotOrderbookChannelMessage);
- expect(snapshotMessage.type).to.be.equal('snapshot');
- expect(snapshotMessage.payload).to.be.deep.equal(orderbookResponse);
- });
- it('parses update messages', () => {
- const updateMessage = orderbookChannelMessageParser.parse(updateOrderbookChannelMessage);
- expect(updateMessage.type).to.be.equal('update');
- expect(updateMessage.payload).to.be.deep.equal(orderResponse);
- });
- it('returns unknown message for messages with unsupported types', () => {
- const unknownMessage = orderbookChannelMessageParser.parse(unknownOrderbookChannelMessage);
- expect(unknownMessage.type).to.be.equal('unknown');
- expect(unknownMessage.payload).to.be.undefined();
- });
- it('throws when message does not include a type', () => {
- const typelessMessage = `{
+ describe('#parser', () => {
+ it('parses snapshot messages', () => {
+ const snapshotMessage = orderbookChannelMessageParser.parse(snapshotOrderbookChannelMessage);
+ expect(snapshotMessage.type).to.be.equal('snapshot');
+ expect(snapshotMessage.payload).to.be.deep.equal(orderbookResponse);
+ });
+ it('parses update messages', () => {
+ const updateMessage = orderbookChannelMessageParser.parse(updateOrderbookChannelMessage);
+ expect(updateMessage.type).to.be.equal('update');
+ expect(updateMessage.payload).to.be.deep.equal(orderResponse);
+ });
+ it('returns unknown message for messages with unsupported types', () => {
+ const unknownMessage = orderbookChannelMessageParser.parse(unknownOrderbookChannelMessage);
+ expect(unknownMessage.type).to.be.equal('unknown');
+ expect(unknownMessage.payload).to.be.undefined();
+ });
+ it('throws when message does not include a type', () => {
+ const typelessMessage = `{
"channel": "orderbook",
"requestId": 1,
"payload": {}
}`;
- const badCall = () => orderbookChannelMessageParser.parse(typelessMessage);
- expect(badCall).throws(`Message is missing a type parameter: ${typelessMessage}`);
- });
- it('throws when type is not a string', () => {
- const messageWithBadType = `{
+ const badCall = () => orderbookChannelMessageParser.parse(typelessMessage);
+ expect(badCall).throws(`Message is missing a type parameter: ${typelessMessage}`);
+ });
+ it('throws when type is not a string', () => {
+ const messageWithBadType = `{
"type": 1,
"channel": "orderbook",
"requestId": 1,
"payload": {}
}`;
- const badCall = () => orderbookChannelMessageParser.parse(messageWithBadType);
- expect(badCall).throws('Expected type to be of type string, encountered: 1');
- });
- it('throws when snapshot message has malformed payload', () => {
- const badCall = () => orderbookChannelMessageParser.parse(malformedSnapshotOrderbookChannelMessage);
- // tslint:disable-next-line:max-line-length
- const errMsg =
- 'Validation errors: instance.payload requires property "bids", instance.payload requires property "asks"';
- expect(badCall).throws(errMsg);
- });
- it('throws when update message has malformed payload', () => {
- const badCall = () => orderbookChannelMessageParser.parse(malformedUpdateOrderbookChannelMessage);
- expect(badCall).throws(/^Expected message to conform to schema/);
- });
- it('throws when input message is not valid JSON', () => {
- const nonJsonString = 'h93b{sdfs9fsd f';
- const badCall = () => orderbookChannelMessageParser.parse(nonJsonString);
- expect(badCall).throws('Unexpected token h in JSON at position 0');
- });
- });
+ const badCall = () => orderbookChannelMessageParser.parse(messageWithBadType);
+ expect(badCall).throws('Expected type to be of type string, encountered: 1');
+ });
+ it('throws when snapshot message has malformed payload', () => {
+ const badCall = () => orderbookChannelMessageParser.parse(malformedSnapshotOrderbookChannelMessage);
+ // tslint:disable-next-line:max-line-length
+ const errMsg =
+ 'Validation errors: instance.payload requires property "bids", instance.payload requires property "asks"';
+ expect(badCall).throws(errMsg);
+ });
+ it('throws when update message has malformed payload', () => {
+ const badCall = () => orderbookChannelMessageParser.parse(malformedUpdateOrderbookChannelMessage);
+ expect(badCall).throws(/^Expected message to conform to schema/);
+ });
+ it('throws when input message is not valid JSON', () => {
+ const nonJsonString = 'h93b{sdfs9fsd f';
+ const badCall = () => orderbookChannelMessageParser.parse(nonJsonString);
+ expect(badCall).throws('Unexpected token h in JSON at position 0');
+ });
+ });
});
diff --git a/packages/connect/test/ws_orderbook_channel_test.ts b/packages/connect/test/ws_orderbook_channel_test.ts
index 0cd8cd12f..ce404d934 100644
--- a/packages/connect/test/ws_orderbook_channel_test.ts
+++ b/packages/connect/test/ws_orderbook_channel_test.ts
@@ -10,52 +10,52 @@ chai.use(dirtyChai);
const expect = chai.expect;
describe('WebSocketOrderbookChannel', () => {
- const websocketUrl = 'ws://localhost:8080';
- const orderbookChannel = new WebSocketOrderbookChannel(websocketUrl);
- const subscriptionOpts = {
- baseTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
- quoteTokenAddress: '0xef7fff64389b814a946f3e92105513705ca6b990',
- snapshot: true,
- limit: 100,
- };
- const emptyOrderbookChannelHandler = {
- onSnapshot: () => {
- _.noop();
- },
- onUpdate: () => {
- _.noop();
- },
- onError: () => {
- _.noop();
- },
- onClose: () => {
- _.noop();
- },
- };
- describe('#subscribe', () => {
- it('throws when subscriptionOpts does not conform to schema', () => {
- const badSubscribeCall = orderbookChannel.subscribe.bind(
- orderbookChannel,
- {},
- emptyOrderbookChannelHandler,
- );
- expect(badSubscribeCall).throws(
- 'Expected subscriptionOpts to conform to schema /RelayerApiOrderbookChannelSubscribePayload\nEncountered: {}\nValidation errors: instance requires property "baseTokenAddress", instance requires property "quoteTokenAddress"',
- );
- });
- it('throws when handler has the incorrect members', () => {
- const badSubscribeCall = orderbookChannel.subscribe.bind(orderbookChannel, subscriptionOpts, {});
- expect(badSubscribeCall).throws(
- 'Expected handler.onSnapshot to be of type function, encountered: undefined',
- );
- });
- it('does not throw when inputs are of correct types', () => {
- const goodSubscribeCall = orderbookChannel.subscribe.bind(
- orderbookChannel,
- subscriptionOpts,
- emptyOrderbookChannelHandler,
- );
- expect(goodSubscribeCall).to.not.throw();
- });
- });
+ const websocketUrl = 'ws://localhost:8080';
+ const orderbookChannel = new WebSocketOrderbookChannel(websocketUrl);
+ const subscriptionOpts = {
+ baseTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
+ quoteTokenAddress: '0xef7fff64389b814a946f3e92105513705ca6b990',
+ snapshot: true,
+ limit: 100,
+ };
+ const emptyOrderbookChannelHandler = {
+ onSnapshot: () => {
+ _.noop();
+ },
+ onUpdate: () => {
+ _.noop();
+ },
+ onError: () => {
+ _.noop();
+ },
+ onClose: () => {
+ _.noop();
+ },
+ };
+ describe('#subscribe', () => {
+ it('throws when subscriptionOpts does not conform to schema', () => {
+ const badSubscribeCall = orderbookChannel.subscribe.bind(
+ orderbookChannel,
+ {},
+ emptyOrderbookChannelHandler,
+ );
+ expect(badSubscribeCall).throws(
+ 'Expected subscriptionOpts to conform to schema /RelayerApiOrderbookChannelSubscribePayload\nEncountered: {}\nValidation errors: instance requires property "baseTokenAddress", instance requires property "quoteTokenAddress"',
+ );
+ });
+ it('throws when handler has the incorrect members', () => {
+ const badSubscribeCall = orderbookChannel.subscribe.bind(orderbookChannel, subscriptionOpts, {});
+ expect(badSubscribeCall).throws(
+ 'Expected handler.onSnapshot to be of type function, encountered: undefined',
+ );
+ });
+ it('does not throw when inputs are of correct types', () => {
+ const goodSubscribeCall = orderbookChannel.subscribe.bind(
+ orderbookChannel,
+ subscriptionOpts,
+ emptyOrderbookChannelHandler,
+ );
+ expect(goodSubscribeCall).to.not.throw();
+ });
+ });
});
diff --git a/packages/connect/tsconfig.json b/packages/connect/tsconfig.json
index ed8210b04..3c150236e 100644
--- a/packages/connect/tsconfig.json
+++ b/packages/connect/tsconfig.json
@@ -1,12 +1,12 @@
{
- "extends": "../../tsconfig",
- "compilerOptions": {
- "outDir": "lib"
- },
- "include": [
- "./src/**/*",
- "./test/**/*",
- "../../node_modules/chai-as-promised-typescript-typings/index.d.ts",
- "../../node_modules/chai-typescript-typings/index.d.ts"
- ]
+ "extends": "../../tsconfig",
+ "compilerOptions": {
+ "outDir": "lib"
+ },
+ "include": [
+ "./src/**/*",
+ "./test/**/*",
+ "../../node_modules/chai-as-promised-typescript-typings/index.d.ts",
+ "../../node_modules/chai-typescript-typings/index.d.ts"
+ ]
}
diff --git a/packages/connect/tslint.json b/packages/connect/tslint.json
index e63054bfc..ffaefe83a 100644
--- a/packages/connect/tslint.json
+++ b/packages/connect/tslint.json
@@ -1,3 +1,3 @@
{
- "extends": ["@0xproject/tslint-config"]
+ "extends": ["@0xproject/tslint-config"]
}
diff --git a/packages/contracts/globals.d.ts b/packages/contracts/globals.d.ts
index 1f451cf5b..2e5827324 100644
--- a/packages/contracts/globals.d.ts
+++ b/packages/contracts/globals.d.ts
@@ -6,31 +6,31 @@ declare module 'dirty-chai';
// disallow `namespace`, we disable tslint for the following.
/* tslint:disable */
declare namespace Chai {
- interface Assertion {
- bignumber: Assertion;
- }
+ interface Assertion {
+ bignumber: Assertion;
+ }
}
/* tslint:enable */
declare module '*.json' {
- const json: any;
- /* tslint:disable */
- export default json;
- /* tslint:enable */
+ const json: any;
+ /* tslint:disable */
+ export default json;
+ /* tslint:enable */
}
declare module 'solc' {
- export function compile(sources: any, optimizerEnabled: number, findImports: (importPath: string) => any): any;
- export function setupMethods(solcBin: any): any;
+ export function compile(sources: any, optimizerEnabled: number, findImports: (importPath: string) => any): any;
+ export function setupMethods(solcBin: any): any;
}
declare module 'web3-eth-abi' {
- export function encodeParameters(typesArray: string[], parameters: any[]): string;
+ export function encodeParameters(typesArray: string[], parameters: any[]): string;
}
declare module 'ethereumjs-abi' {
- const soliditySHA3: (argTypes: string[], args: any[]) => Buffer;
- const methodID: (name: string, types: string[]) => Buffer;
+ const soliditySHA3: (argTypes: string[], args: any[]) => Buffer;
+ const methodID: (name: string, types: string[]) => Buffer;
}
// Truffle injects the following into the global scope
diff --git a/packages/contracts/globalsAugment.d.ts b/packages/contracts/globalsAugment.d.ts
index c81a74844..9b16ce2ad 100644
--- a/packages/contracts/globalsAugment.d.ts
+++ b/packages/contracts/globalsAugment.d.ts
@@ -3,17 +3,17 @@ import { BigNumber } from '@0xproject/utils';
// HACK: This module overrides the Chai namespace so that we can use BigNumber types inside.
// Source: https://github.com/Microsoft/TypeScript/issues/7352#issuecomment-191547232
declare global {
- // HACK: In order to merge the bignumber declaration added by chai-bignumber to the chai Assertion
- // interface we must use `namespace` as the Chai definitelyTyped definition does. Since we otherwise
- // disallow `namespace`, we disable tslint for the following.
- /* tslint:disable */
- namespace Chai {
- interface NumberComparer {
- (value: number | BigNumber, message?: string): Assertion;
- }
- interface NumericComparison {
- greaterThan: NumberComparer;
- }
- }
- /* tslint:enable */
+ // HACK: In order to merge the bignumber declaration added by chai-bignumber to the chai Assertion
+ // interface we must use `namespace` as the Chai definitelyTyped definition does. Since we otherwise
+ // disallow `namespace`, we disable tslint for the following.
+ /* tslint:disable */
+ namespace Chai {
+ interface NumberComparer {
+ (value: number | BigNumber, message?: string): Assertion;
+ }
+ interface NumericComparison {
+ greaterThan: NumberComparer;
+ }
+ }
+ /* tslint:enable */
}
diff --git a/packages/contracts/migrations/1_initial_migration.ts b/packages/contracts/migrations/1_initial_migration.ts
index 73999dac5..8661ee218 100644
--- a/packages/contracts/migrations/1_initial_migration.ts
+++ b/packages/contracts/migrations/1_initial_migration.ts
@@ -2,5 +2,5 @@ import { Artifacts } from '../util/artifacts';
const { Migrations } = new Artifacts(artifacts);
module.exports = (deployer: any) => {
- deployer.deploy(Migrations);
+ deployer.deploy(Migrations);
};
diff --git a/packages/contracts/migrations/2_deploy_independent_contracts.ts b/packages/contracts/migrations/2_deploy_independent_contracts.ts
index 07e2f600f..ac1752347 100644
--- a/packages/contracts/migrations/2_deploy_independent_contracts.ts
+++ b/packages/contracts/migrations/2_deploy_independent_contracts.ts
@@ -4,38 +4,38 @@ const { MultiSigWalletWithTimeLock, TokenTransferProxy, EtherToken, TokenRegistr
let multiSigConfigByNetwork: MultiSigConfigByNetwork;
try {
- /* tslint:disable */
- const multiSigConfig = require('./config/multisig');
- multiSigConfigByNetwork = multiSigConfig.multiSig;
- /* tslint:enable */
+ /* tslint:disable */
+ const multiSigConfig = require('./config/multisig');
+ multiSigConfigByNetwork = multiSigConfig.multiSig;
+ /* tslint:enable */
} catch (e) {
- multiSigConfigByNetwork = {};
+ multiSigConfigByNetwork = {};
}
module.exports = (deployer: any, network: string, accounts: string[]) => {
- const defaultConfig = {
- owners: [accounts[0], accounts[1]],
- confirmationsRequired: 2,
- secondsRequired: 0,
- };
- const config = multiSigConfigByNetwork[network] || defaultConfig;
- if (network !== 'live') {
- deployer
- .deploy(MultiSigWalletWithTimeLock, config.owners, config.confirmationsRequired, config.secondsRequired)
- .then(() => {
- return deployer.deploy(TokenTransferProxy);
- })
- .then(() => {
- return deployer.deploy(TokenRegistry);
- })
- .then(() => {
- return deployer.deploy(EtherToken);
- });
- } else {
- deployer.deploy([
- [MultiSigWalletWithTimeLock, config.owners, config.confirmationsRequired, config.secondsRequired],
- TokenTransferProxy,
- TokenRegistry,
- ]);
- }
+ const defaultConfig = {
+ owners: [accounts[0], accounts[1]],
+ confirmationsRequired: 2,
+ secondsRequired: 0,
+ };
+ const config = multiSigConfigByNetwork[network] || defaultConfig;
+ if (network !== 'live') {
+ deployer
+ .deploy(MultiSigWalletWithTimeLock, config.owners, config.confirmationsRequired, config.secondsRequired)
+ .then(() => {
+ return deployer.deploy(TokenTransferProxy);
+ })
+ .then(() => {
+ return deployer.deploy(TokenRegistry);
+ })
+ .then(() => {
+ return deployer.deploy(EtherToken);
+ });
+ } else {
+ deployer.deploy([
+ [MultiSigWalletWithTimeLock, config.owners, config.confirmationsRequired, config.secondsRequired],
+ TokenTransferProxy,
+ TokenRegistry,
+ ]);
+ }
};
diff --git a/packages/contracts/migrations/3_register_tokens.ts b/packages/contracts/migrations/3_register_tokens.ts
index b36cea296..d5cf63f94 100644
--- a/packages/contracts/migrations/3_register_tokens.ts
+++ b/packages/contracts/migrations/3_register_tokens.ts
@@ -9,87 +9,87 @@ import { tokenInfo } from './config/token_info';
const { DummyToken, EtherToken, ZRXToken, TokenRegistry } = new Artifacts(artifacts);
module.exports = (deployer: any, network: string) => {
- const tokens = network === 'live' ? tokenInfo.live : tokenInfo.development;
- deployer
- .then(() => {
- return TokenRegistry.deployed();
- })
- .then((tokenRegistry: ContractInstance) => {
- if (network !== 'live') {
- const totalSupply = Math.pow(10, 18) * 1000000000;
- return Bluebird.each(
- tokens.map((token: Token) => DummyToken.new(token.name, token.symbol, token.decimals, totalSupply)),
- _.noop,
- ).then((dummyTokens: ContractInstance[]) => {
- const weth = {
- address: EtherToken.address,
- name: 'Ether Token',
- symbol: 'WETH',
- url: '',
- decimals: 18,
- ipfsHash: constants.NULL_BYTES,
- swarmHash: constants.NULL_BYTES,
- };
- return Bluebird.each(
- dummyTokens
- .map((tokenContract: ContractInstance, i: number) => {
- const token = tokens[i];
- return tokenRegistry.addToken(
- tokenContract.address,
- token.name,
- token.symbol,
- token.decimals,
- token.ipfsHash,
- token.swarmHash,
- );
- })
- .concat(
- tokenRegistry.addToken(
- weth.address,
- weth.name,
- weth.symbol,
- weth.decimals,
- weth.ipfsHash,
- weth.swarmHash,
- ),
- ),
- _.noop,
- );
- });
- } else {
- const zrx = {
- address: ZRXToken.address,
- name: '0x Protocol Token',
- symbol: 'ZRX',
- url: 'https://www.0xproject.com/',
- decimals: 18,
- ipfsHash: constants.NULL_BYTES,
- swarmHash: constants.NULL_BYTES,
- };
- return Bluebird.each(
- tokens
- .map((token: Token) => {
- return tokenRegistry.addToken(
- token.address,
- token.name,
- token.symbol,
- token.decimals,
- token.ipfsHash,
- token.swarmHash,
- );
- })
- .concat(
- tokenRegistry.addToken(
- zrx.address,
- zrx.name,
- zrx.symbol,
- zrx.decimals,
- zrx.ipfsHash,
- zrx.swarmHash,
- ),
- ),
- _.noop,
- );
- }
- });
+ const tokens = network === 'live' ? tokenInfo.live : tokenInfo.development;
+ deployer
+ .then(() => {
+ return TokenRegistry.deployed();
+ })
+ .then((tokenRegistry: ContractInstance) => {
+ if (network !== 'live') {
+ const totalSupply = Math.pow(10, 18) * 1000000000;
+ return Bluebird.each(
+ tokens.map((token: Token) => DummyToken.new(token.name, token.symbol, token.decimals, totalSupply)),
+ _.noop,
+ ).then((dummyTokens: ContractInstance[]) => {
+ const weth = {
+ address: EtherToken.address,
+ name: 'Ether Token',
+ symbol: 'WETH',
+ url: '',
+ decimals: 18,
+ ipfsHash: constants.NULL_BYTES,
+ swarmHash: constants.NULL_BYTES,
+ };
+ return Bluebird.each(
+ dummyTokens
+ .map((tokenContract: ContractInstance, i: number) => {
+ const token = tokens[i];
+ return tokenRegistry.addToken(
+ tokenContract.address,
+ token.name,
+ token.symbol,
+ token.decimals,
+ token.ipfsHash,
+ token.swarmHash,
+ );
+ })
+ .concat(
+ tokenRegistry.addToken(
+ weth.address,
+ weth.name,
+ weth.symbol,
+ weth.decimals,
+ weth.ipfsHash,
+ weth.swarmHash,
+ ),
+ ),
+ _.noop,
+ );
+ });
+ } else {
+ const zrx = {
+ address: ZRXToken.address,
+ name: '0x Protocol Token',
+ symbol: 'ZRX',
+ url: 'https://www.0xproject.com/',
+ decimals: 18,
+ ipfsHash: constants.NULL_BYTES,
+ swarmHash: constants.NULL_BYTES,
+ };
+ return Bluebird.each(
+ tokens
+ .map((token: Token) => {
+ return tokenRegistry.addToken(
+ token.address,
+ token.name,
+ token.symbol,
+ token.decimals,
+ token.ipfsHash,
+ token.swarmHash,
+ );
+ })
+ .concat(
+ tokenRegistry.addToken(
+ zrx.address,
+ zrx.name,
+ zrx.symbol,
+ zrx.decimals,
+ zrx.ipfsHash,
+ zrx.swarmHash,
+ ),
+ ),
+ _.noop,
+ );
+ }
+ });
};
diff --git a/packages/contracts/migrations/4_configure_proxy.ts b/packages/contracts/migrations/4_configure_proxy.ts
index ce34ddd32..ff3b844d6 100644
--- a/packages/contracts/migrations/4_configure_proxy.ts
+++ b/packages/contracts/migrations/4_configure_proxy.ts
@@ -4,19 +4,19 @@ const { TokenTransferProxy, Exchange, TokenRegistry } = new Artifacts(artifacts)
let tokenTransferProxy: ContractInstance;
module.exports = (deployer: any) => {
- deployer
- .then(async () => {
- return Promise.all([TokenTransferProxy.deployed(), TokenRegistry.deployed()]);
- })
- .then((instances: ContractInstance[]) => {
- let tokenRegistry: ContractInstance;
- [tokenTransferProxy, tokenRegistry] = instances;
- return tokenRegistry.getTokenAddressBySymbol('ZRX');
- })
- .then((ptAddress: string) => {
- return deployer.deploy(Exchange, ptAddress, tokenTransferProxy.address);
- })
- .then(() => {
- return tokenTransferProxy.addAuthorizedAddress(Exchange.address);
- });
+ deployer
+ .then(async () => {
+ return Promise.all([TokenTransferProxy.deployed(), TokenRegistry.deployed()]);
+ })
+ .then((instances: ContractInstance[]) => {
+ let tokenRegistry: ContractInstance;
+ [tokenTransferProxy, tokenRegistry] = instances;
+ return tokenRegistry.getTokenAddressBySymbol('ZRX');
+ })
+ .then((ptAddress: string) => {
+ return deployer.deploy(Exchange, ptAddress, tokenTransferProxy.address);
+ })
+ .then(() => {
+ return tokenTransferProxy.addAuthorizedAddress(Exchange.address);
+ });
};
diff --git a/packages/contracts/migrations/5_transfer_ownership.ts b/packages/contracts/migrations/5_transfer_ownership.ts
index 08b068e18..a27801de3 100644
--- a/packages/contracts/migrations/5_transfer_ownership.ts
+++ b/packages/contracts/migrations/5_transfer_ownership.ts
@@ -4,17 +4,17 @@ const { TokenTransferProxy, MultiSigWalletWithTimeLock, TokenRegistry } = new Ar
let tokenRegistry: ContractInstance;
module.exports = (deployer: any, network: string) => {
- if (network !== 'development') {
- deployer.then(async () => {
- return Promise.all([TokenTransferProxy.deployed(), TokenRegistry.deployed()])
- .then((instances: ContractInstance[]) => {
- let tokenTransferProxy: ContractInstance;
- [tokenTransferProxy, tokenRegistry] = instances;
- return tokenTransferProxy.transferOwnership(MultiSigWalletWithTimeLock.address);
- })
- .then(() => {
- return tokenRegistry.transferOwnership(MultiSigWalletWithTimeLock.address);
- });
- });
- }
+ if (network !== 'development') {
+ deployer.then(async () => {
+ return Promise.all([TokenTransferProxy.deployed(), TokenRegistry.deployed()])
+ .then((instances: ContractInstance[]) => {
+ let tokenTransferProxy: ContractInstance;
+ [tokenTransferProxy, tokenRegistry] = instances;
+ return tokenTransferProxy.transferOwnership(MultiSigWalletWithTimeLock.address);
+ })
+ .then(() => {
+ return tokenRegistry.transferOwnership(MultiSigWalletWithTimeLock.address);
+ });
+ });
+ }
};
diff --git a/packages/contracts/migrations/config/multisig_sample.ts b/packages/contracts/migrations/config/multisig_sample.ts
index 6ff5efc1c..97cdc2eae 100644
--- a/packages/contracts/migrations/config/multisig_sample.ts
+++ b/packages/contracts/migrations/config/multisig_sample.ts
@@ -2,9 +2,9 @@ import { MultiSigConfigByNetwork } from '../../util/types';
// Make a copy of this file named `multisig.js` and input custom params as needed
export const multiSig: MultiSigConfigByNetwork = {
- kovan: {
- owners: [],
- confirmationsRequired: 0,
- secondsRequired: 0,
- },
+ kovan: {
+ owners: [],
+ confirmationsRequired: 0,
+ secondsRequired: 0,
+ },
};
diff --git a/packages/contracts/migrations/config/token_info.ts b/packages/contracts/migrations/config/token_info.ts
index 9e211c489..6ae67175e 100644
--- a/packages/contracts/migrations/config/token_info.ts
+++ b/packages/contracts/migrations/config/token_info.ts
@@ -2,98 +2,98 @@ import { constants } from '../../util/constants';
import { TokenInfoByNetwork } from '../../util/types';
export const tokenInfo: TokenInfoByNetwork = {
- development: [
- {
- name: '0x Protocol Token',
- symbol: 'ZRX',
- decimals: 18,
- ipfsHash: constants.NULL_BYTES,
- swarmHash: constants.NULL_BYTES,
- },
- {
- name: 'Augur Reputation Token',
- symbol: 'REP',
- decimals: 18,
- ipfsHash: constants.NULL_BYTES,
- swarmHash: constants.NULL_BYTES,
- },
- {
- name: 'Digix DAO Token',
- symbol: 'DGD',
- decimals: 18,
- ipfsHash: constants.NULL_BYTES,
- swarmHash: constants.NULL_BYTES,
- },
- {
- name: 'Golem Network Token',
- symbol: 'GNT',
- decimals: 18,
- ipfsHash: constants.NULL_BYTES,
- swarmHash: constants.NULL_BYTES,
- },
- {
- name: 'MakerDAO',
- symbol: 'MKR',
- decimals: 18,
- ipfsHash: constants.NULL_BYTES,
- swarmHash: constants.NULL_BYTES,
- },
- {
- name: 'Melon Token',
- symbol: 'MLN',
- decimals: 18,
- ipfsHash: constants.NULL_BYTES,
- swarmHash: constants.NULL_BYTES,
- },
- ],
- live: [
- {
- address: '0xecf8f87f810ecf450940c9f60066b4a7a501d6a7',
- name: 'ETH Wrapper Token',
- symbol: 'WETH',
- decimals: 18,
- ipfsHash: constants.NULL_BYTES,
- swarmHash: constants.NULL_BYTES,
- },
- {
- address: '0x48c80f1f4d53d5951e5d5438b54cba84f29f32a5',
- name: 'Augur Reputation Token',
- symbol: 'REP',
- decimals: 18,
- ipfsHash: constants.NULL_BYTES,
- swarmHash: constants.NULL_BYTES,
- },
- {
- address: '0xe0b7927c4af23765cb51314a0e0521a9645f0e2a',
- name: 'Digix DAO Token',
- symbol: 'DGD',
- decimals: 18,
- ipfsHash: constants.NULL_BYTES,
- swarmHash: constants.NULL_BYTES,
- },
- {
- address: '0xa74476443119a942de498590fe1f2454d7d4ac0d',
- name: 'Golem Network Token',
- symbol: 'GNT',
- decimals: 18,
- ipfsHash: constants.NULL_BYTES,
- swarmHash: constants.NULL_BYTES,
- },
- {
- address: '0xc66ea802717bfb9833400264dd12c2bceaa34a6d',
- name: 'MakerDAO',
- symbol: 'MKR',
- decimals: 18,
- ipfsHash: constants.NULL_BYTES,
- swarmHash: constants.NULL_BYTES,
- },
- {
- address: '0xbeb9ef514a379b997e0798fdcc901ee474b6d9a1',
- name: 'Melon Token',
- symbol: 'MLN',
- decimals: 18,
- ipfsHash: constants.NULL_BYTES,
- swarmHash: constants.NULL_BYTES,
- },
- ],
+ development: [
+ {
+ name: '0x Protocol Token',
+ symbol: 'ZRX',
+ decimals: 18,
+ ipfsHash: constants.NULL_BYTES,
+ swarmHash: constants.NULL_BYTES,
+ },
+ {
+ name: 'Augur Reputation Token',
+ symbol: 'REP',
+ decimals: 18,
+ ipfsHash: constants.NULL_BYTES,
+ swarmHash: constants.NULL_BYTES,
+ },
+ {
+ name: 'Digix DAO Token',
+ symbol: 'DGD',
+ decimals: 18,
+ ipfsHash: constants.NULL_BYTES,
+ swarmHash: constants.NULL_BYTES,
+ },
+ {
+ name: 'Golem Network Token',
+ symbol: 'GNT',
+ decimals: 18,
+ ipfsHash: constants.NULL_BYTES,
+ swarmHash: constants.NULL_BYTES,
+ },
+ {
+ name: 'MakerDAO',
+ symbol: 'MKR',
+ decimals: 18,
+ ipfsHash: constants.NULL_BYTES,
+ swarmHash: constants.NULL_BYTES,
+ },
+ {
+ name: 'Melon Token',
+ symbol: 'MLN',
+ decimals: 18,
+ ipfsHash: constants.NULL_BYTES,
+ swarmHash: constants.NULL_BYTES,
+ },
+ ],
+ live: [
+ {
+ address: '0xecf8f87f810ecf450940c9f60066b4a7a501d6a7',
+ name: 'ETH Wrapper Token',
+ symbol: 'WETH',
+ decimals: 18,
+ ipfsHash: constants.NULL_BYTES,
+ swarmHash: constants.NULL_BYTES,
+ },
+ {
+ address: '0x48c80f1f4d53d5951e5d5438b54cba84f29f32a5',
+ name: 'Augur Reputation Token',
+ symbol: 'REP',
+ decimals: 18,
+ ipfsHash: constants.NULL_BYTES,
+ swarmHash: constants.NULL_BYTES,
+ },
+ {
+ address: '0xe0b7927c4af23765cb51314a0e0521a9645f0e2a',
+ name: 'Digix DAO Token',
+ symbol: 'DGD',
+ decimals: 18,
+ ipfsHash: constants.NULL_BYTES,
+ swarmHash: constants.NULL_BYTES,
+ },
+ {
+ address: '0xa74476443119a942de498590fe1f2454d7d4ac0d',
+ name: 'Golem Network Token',
+ symbol: 'GNT',
+ decimals: 18,
+ ipfsHash: constants.NULL_BYTES,
+ swarmHash: constants.NULL_BYTES,
+ },
+ {
+ address: '0xc66ea802717bfb9833400264dd12c2bceaa34a6d',
+ name: 'MakerDAO',
+ symbol: 'MKR',
+ decimals: 18,
+ ipfsHash: constants.NULL_BYTES,
+ swarmHash: constants.NULL_BYTES,
+ },
+ {
+ address: '0xbeb9ef514a379b997e0798fdcc901ee474b6d9a1',
+ name: 'Melon Token',
+ symbol: 'MLN',
+ decimals: 18,
+ ipfsHash: constants.NULL_BYTES,
+ swarmHash: constants.NULL_BYTES,
+ },
+ ],
};
diff --git a/packages/contracts/package.json b/packages/contracts/package.json
index b361c6622..468607c16 100644
--- a/packages/contracts/package.json
+++ b/packages/contracts/package.json
@@ -1,74 +1,74 @@
{
- "private": true,
- "name": "contracts",
- "version": "2.1.6",
- "description": "Smart contract components of 0x protocol",
- "main": "index.js",
- "directories": {
- "test": "test"
- },
- "scripts": {
- "build":
- "rm -rf ./lib; copyfiles ./build/**/* ./deploy/solc/solc_bin/* ./deploy/test/fixtures/contracts/**/* ./deploy/test/fixtures/contracts/* ./lib; tsc;",
- "test": "npm run build; truffle test",
- "compile:comment":
- "Yarn workspaces do not link binaries correctly so we need to reference them directly https://github.com/yarnpkg/yarn/issues/3846",
- "compile": "node ../deployer/lib/src/cli.js compile",
- "clean": "rm -rf ./lib",
- "migrate": "node ../deployer/lib/src/cli.js migrate",
- "lint": "tslint --project . 'migrations/**/*.ts' 'test/**/*.ts' 'util/**/*.ts' 'deploy/**/*.ts'",
- "test:circleci": "yarn test"
- },
- "repository": {
- "type": "git",
- "url": "https://github.com/0xProject/0x.js.git"
- },
- "author": "Amir Bandeali",
- "license": "Apache-2.0",
- "bugs": {
- "url": "https://github.com/0xProject/0x.js/issues"
- },
- "homepage": "https://github.com/0xProject/0x.js/packages/contracts/README.md",
- "devDependencies": {
- "@0xproject/dev-utils": "^0.0.7",
- "@0xproject/tslint-config": "^0.4.4",
- "@0xproject/types": "^0.1.6",
- "@types/bluebird": "^3.5.3",
- "@types/lodash": "^4.14.86",
- "@types/node": "^8.0.53",
- "@types/request-promise-native": "^1.0.2",
- "@types/yargs": "^10.0.0",
- "chai": "^4.0.1",
- "chai-as-promised": "^7.1.0",
- "chai-as-promised-typescript-typings": "^0.0.6",
- "chai-bignumber": "^2.0.1",
- "chai-typescript-typings": "^0.0.2",
- "copyfiles": "^1.2.0",
- "dirty-chai": "^2.0.1",
- "mocha": "^4.0.1",
- "solc": "^0.4.18",
- "truffle": "^4.0.1",
- "tslint": "5.8.0",
- "types-bn": "^0.0.1",
- "types-ethereumjs-util": "0xProject/types-ethereumjs-util",
- "typescript": "~2.6.1",
- "web3-typescript-typings": "^0.9.6",
- "yargs": "^10.0.3"
- },
- "dependencies": {
- "0x.js": "^0.30.2",
- "@0xproject/deployer": "^0.0.3",
- "@0xproject/json-schemas": "^0.7.5",
- "@0xproject/utils": "^0.2.2",
- "@0xproject/web3-wrapper": "^0.1.7",
- "bluebird": "^3.5.0",
- "bn.js": "^4.11.8",
- "ethereumjs-abi": "^0.6.4",
- "ethereumjs-util": "^5.1.1",
- "isomorphic-fetch": "^2.2.1",
- "lodash": "^4.17.4",
- "request": "^2.81.0",
- "web3": "^0.20.0",
- "web3-eth-abi": "^1.0.0-beta.24"
- }
+ "private": true,
+ "name": "contracts",
+ "version": "2.1.6",
+ "description": "Smart contract components of 0x protocol",
+ "main": "index.js",
+ "directories": {
+ "test": "test"
+ },
+ "scripts": {
+ "build":
+ "rm -rf ./lib; copyfiles ./build/**/* ./deploy/solc/solc_bin/* ./deploy/test/fixtures/contracts/**/* ./deploy/test/fixtures/contracts/* ./lib; tsc;",
+ "test": "npm run build; truffle test",
+ "compile:comment":
+ "Yarn workspaces do not link binaries correctly so we need to reference them directly https://github.com/yarnpkg/yarn/issues/3846",
+ "compile": "node ../deployer/lib/src/cli.js compile",
+ "clean": "rm -rf ./lib",
+ "migrate": "node ../deployer/lib/src/cli.js migrate",
+ "lint": "tslint --project . 'migrations/**/*.ts' 'test/**/*.ts' 'util/**/*.ts' 'deploy/**/*.ts'",
+ "test:circleci": "yarn test"
+ },
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/0xProject/0x.js.git"
+ },
+ "author": "Amir Bandeali",
+ "license": "Apache-2.0",
+ "bugs": {
+ "url": "https://github.com/0xProject/0x.js/issues"
+ },
+ "homepage": "https://github.com/0xProject/0x.js/packages/contracts/README.md",
+ "devDependencies": {
+ "@0xproject/dev-utils": "^0.0.7",
+ "@0xproject/tslint-config": "^0.4.4",
+ "@0xproject/types": "^0.1.6",
+ "@types/bluebird": "^3.5.3",
+ "@types/lodash": "^4.14.86",
+ "@types/node": "^8.0.53",
+ "@types/request-promise-native": "^1.0.2",
+ "@types/yargs": "^10.0.0",
+ "chai": "^4.0.1",
+ "chai-as-promised": "^7.1.0",
+ "chai-as-promised-typescript-typings": "^0.0.6",
+ "chai-bignumber": "^2.0.1",
+ "chai-typescript-typings": "^0.0.2",
+ "copyfiles": "^1.2.0",
+ "dirty-chai": "^2.0.1",
+ "mocha": "^4.0.1",
+ "solc": "^0.4.18",
+ "truffle": "^4.0.1",
+ "tslint": "5.8.0",
+ "types-bn": "^0.0.1",
+ "types-ethereumjs-util": "0xProject/types-ethereumjs-util",
+ "typescript": "~2.6.1",
+ "web3-typescript-typings": "^0.9.6",
+ "yargs": "^10.0.3"
+ },
+ "dependencies": {
+ "0x.js": "^0.30.2",
+ "@0xproject/deployer": "^0.0.3",
+ "@0xproject/json-schemas": "^0.7.5",
+ "@0xproject/utils": "^0.2.2",
+ "@0xproject/web3-wrapper": "^0.1.7",
+ "bluebird": "^3.5.0",
+ "bn.js": "^4.11.8",
+ "ethereumjs-abi": "^0.6.4",
+ "ethereumjs-util": "^5.1.1",
+ "isomorphic-fetch": "^2.2.1",
+ "lodash": "^4.17.4",
+ "request": "^2.81.0",
+ "web3": "^0.20.0",
+ "web3-eth-abi": "^1.0.0-beta.24"
+ }
}
diff --git a/packages/contracts/test/ts/ether_token.ts b/packages/contracts/test/ts/ether_token.ts
index c770b4001..f807cdaa3 100644
--- a/packages/contracts/test/ts/ether_token.ts
+++ b/packages/contracts/test/ts/ether_token.ts
@@ -18,107 +18,107 @@ const expect = chai.expect;
const web3: Web3 = (global as any).web3;
contract('EtherToken', (accounts: string[]) => {
- const account = accounts[0];
- const gasPrice = ZeroEx.toBaseUnitAmount(new BigNumber(20), 9);
- let zeroEx: ZeroEx;
- let etherTokenAddress: string;
-
- before(async () => {
- etherTokenAddress = EtherToken.address;
- zeroEx = new ZeroEx(web3.currentProvider, {
- gasPrice,
- networkId: constants.TESTRPC_NETWORK_ID,
- });
- });
-
- const sendTransactionAsync = promisify<string>(web3.eth.sendTransaction);
- const getEthBalanceAsync = async (owner: string) => {
- const balanceStr = await promisify<string>(web3.eth.getBalance)(owner);
- const balance = new BigNumber(balanceStr);
- return balance;
- };
-
- describe('deposit', () => {
- it('should throw if caller attempts to deposit more Ether than caller balance', async () => {
- const initEthBalance = await getEthBalanceAsync(account);
- const ethToDeposit = initEthBalance.plus(1);
-
- return expect(zeroEx.etherToken.depositAsync(etherTokenAddress, ethToDeposit, account)).to.be.rejectedWith(
- ZeroExError.InsufficientEthBalanceForDeposit,
- );
- });
-
- it('should convert deposited Ether to wrapped Ether tokens', async () => {
- const initEthBalance = await getEthBalanceAsync(account);
- const initEthTokenBalance = await zeroEx.token.getBalanceAsync(etherTokenAddress, account);
-
- const ethToDeposit = new BigNumber(web3.toWei(1, 'ether'));
-
- const txHash = await zeroEx.etherToken.depositAsync(etherTokenAddress, ethToDeposit, account);
- const receipt = await zeroEx.awaitTransactionMinedAsync(txHash);
-
- const ethSpentOnGas = gasPrice.times(receipt.gasUsed);
- const finalEthBalance = await getEthBalanceAsync(account);
- const finalEthTokenBalance = await zeroEx.token.getBalanceAsync(etherTokenAddress, account);
-
- expect(finalEthBalance).to.be.bignumber.equal(initEthBalance.minus(ethToDeposit.plus(ethSpentOnGas)));
- expect(finalEthTokenBalance).to.be.bignumber.equal(initEthTokenBalance.plus(ethToDeposit));
- });
- });
-
- describe('withdraw', () => {
- it('should throw if caller attempts to withdraw greater than caller balance', async () => {
- const initEthTokenBalance = await zeroEx.token.getBalanceAsync(etherTokenAddress, account);
- const ethTokensToWithdraw = initEthTokenBalance.plus(1);
-
- return expect(
- zeroEx.etherToken.withdrawAsync(etherTokenAddress, ethTokensToWithdraw, account),
- ).to.be.rejectedWith(ZeroExError.InsufficientWEthBalanceForWithdrawal);
- });
-
- it('should convert ether tokens to ether with sufficient balance', async () => {
- const initEthTokenBalance = await zeroEx.token.getBalanceAsync(etherTokenAddress, account);
- const initEthBalance = await getEthBalanceAsync(account);
- const ethTokensToWithdraw = initEthTokenBalance;
- expect(ethTokensToWithdraw).to.not.be.bignumber.equal(0);
- const txHash = await zeroEx.etherToken.withdrawAsync(etherTokenAddress, ethTokensToWithdraw, account, {
- gasLimit: constants.MAX_ETHERTOKEN_WITHDRAW_GAS,
- });
- const receipt = await zeroEx.awaitTransactionMinedAsync(txHash);
-
- const ethSpentOnGas = gasPrice.times(receipt.gasUsed);
- const finalEthBalance = await getEthBalanceAsync(account);
- const finalEthTokenBalance = await zeroEx.token.getBalanceAsync(etherTokenAddress, account);
-
- expect(finalEthBalance).to.be.bignumber.equal(
- initEthBalance.plus(ethTokensToWithdraw.minus(ethSpentOnGas)),
- );
- expect(finalEthTokenBalance).to.be.bignumber.equal(initEthTokenBalance.minus(ethTokensToWithdraw));
- });
- });
-
- describe('fallback', () => {
- it('should convert sent ether to ether tokens', async () => {
- const initEthBalance = await getEthBalanceAsync(account);
- const initEthTokenBalance = await zeroEx.token.getBalanceAsync(etherTokenAddress, account);
-
- const ethToDeposit = ZeroEx.toBaseUnitAmount(new BigNumber(1), 18);
-
- const txHash = await sendTransactionAsync({
- from: account,
- to: etherTokenAddress,
- value: ethToDeposit,
- gasPrice,
- });
-
- const receipt = await zeroEx.awaitTransactionMinedAsync(txHash);
-
- const ethSpentOnGas = gasPrice.times(receipt.gasUsed);
- const finalEthBalance = await getEthBalanceAsync(account);
- const finalEthTokenBalance = await zeroEx.token.getBalanceAsync(etherTokenAddress, account);
-
- expect(finalEthBalance).to.be.bignumber.equal(initEthBalance.minus(ethToDeposit.plus(ethSpentOnGas)));
- expect(finalEthTokenBalance).to.be.bignumber.equal(initEthTokenBalance.plus(ethToDeposit));
- });
- });
+ const account = accounts[0];
+ const gasPrice = ZeroEx.toBaseUnitAmount(new BigNumber(20), 9);
+ let zeroEx: ZeroEx;
+ let etherTokenAddress: string;
+
+ before(async () => {
+ etherTokenAddress = EtherToken.address;
+ zeroEx = new ZeroEx(web3.currentProvider, {
+ gasPrice,
+ networkId: constants.TESTRPC_NETWORK_ID,
+ });
+ });
+
+ const sendTransactionAsync = promisify<string>(web3.eth.sendTransaction);
+ const getEthBalanceAsync = async (owner: string) => {
+ const balanceStr = await promisify<string>(web3.eth.getBalance)(owner);
+ const balance = new BigNumber(balanceStr);
+ return balance;
+ };
+
+ describe('deposit', () => {
+ it('should throw if caller attempts to deposit more Ether than caller balance', async () => {
+ const initEthBalance = await getEthBalanceAsync(account);
+ const ethToDeposit = initEthBalance.plus(1);
+
+ return expect(zeroEx.etherToken.depositAsync(etherTokenAddress, ethToDeposit, account)).to.be.rejectedWith(
+ ZeroExError.InsufficientEthBalanceForDeposit,
+ );
+ });
+
+ it('should convert deposited Ether to wrapped Ether tokens', async () => {
+ const initEthBalance = await getEthBalanceAsync(account);
+ const initEthTokenBalance = await zeroEx.token.getBalanceAsync(etherTokenAddress, account);
+
+ const ethToDeposit = new BigNumber(web3.toWei(1, 'ether'));
+
+ const txHash = await zeroEx.etherToken.depositAsync(etherTokenAddress, ethToDeposit, account);
+ const receipt = await zeroEx.awaitTransactionMinedAsync(txHash);
+
+ const ethSpentOnGas = gasPrice.times(receipt.gasUsed);
+ const finalEthBalance = await getEthBalanceAsync(account);
+ const finalEthTokenBalance = await zeroEx.token.getBalanceAsync(etherTokenAddress, account);
+
+ expect(finalEthBalance).to.be.bignumber.equal(initEthBalance.minus(ethToDeposit.plus(ethSpentOnGas)));
+ expect(finalEthTokenBalance).to.be.bignumber.equal(initEthTokenBalance.plus(ethToDeposit));
+ });
+ });
+
+ describe('withdraw', () => {
+ it('should throw if caller attempts to withdraw greater than caller balance', async () => {
+ const initEthTokenBalance = await zeroEx.token.getBalanceAsync(etherTokenAddress, account);
+ const ethTokensToWithdraw = initEthTokenBalance.plus(1);
+
+ return expect(
+ zeroEx.etherToken.withdrawAsync(etherTokenAddress, ethTokensToWithdraw, account),
+ ).to.be.rejectedWith(ZeroExError.InsufficientWEthBalanceForWithdrawal);
+ });
+
+ it('should convert ether tokens to ether with sufficient balance', async () => {
+ const initEthTokenBalance = await zeroEx.token.getBalanceAsync(etherTokenAddress, account);
+ const initEthBalance = await getEthBalanceAsync(account);
+ const ethTokensToWithdraw = initEthTokenBalance;
+ expect(ethTokensToWithdraw).to.not.be.bignumber.equal(0);
+ const txHash = await zeroEx.etherToken.withdrawAsync(etherTokenAddress, ethTokensToWithdraw, account, {
+ gasLimit: constants.MAX_ETHERTOKEN_WITHDRAW_GAS,
+ });
+ const receipt = await zeroEx.awaitTransactionMinedAsync(txHash);
+
+ const ethSpentOnGas = gasPrice.times(receipt.gasUsed);
+ const finalEthBalance = await getEthBalanceAsync(account);
+ const finalEthTokenBalance = await zeroEx.token.getBalanceAsync(etherTokenAddress, account);
+
+ expect(finalEthBalance).to.be.bignumber.equal(
+ initEthBalance.plus(ethTokensToWithdraw.minus(ethSpentOnGas)),
+ );
+ expect(finalEthTokenBalance).to.be.bignumber.equal(initEthTokenBalance.minus(ethTokensToWithdraw));
+ });
+ });
+
+ describe('fallback', () => {
+ it('should convert sent ether to ether tokens', async () => {
+ const initEthBalance = await getEthBalanceAsync(account);
+ const initEthTokenBalance = await zeroEx.token.getBalanceAsync(etherTokenAddress, account);
+
+ const ethToDeposit = ZeroEx.toBaseUnitAmount(new BigNumber(1), 18);
+
+ const txHash = await sendTransactionAsync({
+ from: account,
+ to: etherTokenAddress,
+ value: ethToDeposit,
+ gasPrice,
+ });
+
+ const receipt = await zeroEx.awaitTransactionMinedAsync(txHash);
+
+ const ethSpentOnGas = gasPrice.times(receipt.gasUsed);
+ const finalEthBalance = await getEthBalanceAsync(account);
+ const finalEthTokenBalance = await zeroEx.token.getBalanceAsync(etherTokenAddress, account);
+
+ expect(finalEthBalance).to.be.bignumber.equal(initEthBalance.minus(ethToDeposit.plus(ethSpentOnGas)));
+ expect(finalEthTokenBalance).to.be.bignumber.equal(initEthTokenBalance.plus(ethToDeposit));
+ });
+ });
});
diff --git a/packages/contracts/test/ts/exchange/core.ts b/packages/contracts/test/ts/exchange/core.ts
index 36078aaa9..770ef0c43 100644
--- a/packages/contracts/test/ts/exchange/core.ts
+++ b/packages/contracts/test/ts/exchange/core.ts
@@ -23,830 +23,830 @@ const { Exchange, TokenTransferProxy, DummyToken, TokenRegistry, MaliciousToken
const web3: Web3 = (global as any).web3;
contract('Exchange', (accounts: string[]) => {
- const maker = accounts[0];
- const tokenOwner = accounts[0];
- const taker = accounts[1] || accounts[accounts.length - 1];
- const feeRecipient = accounts[2] || accounts[accounts.length - 1];
-
- const INITIAL_BALANCE = ZeroEx.toBaseUnitAmount(new BigNumber(10000), 18);
- const INITIAL_ALLOWANCE = ZeroEx.toBaseUnitAmount(new BigNumber(10000), 18);
-
- let rep: ContractInstance;
- let dgd: ContractInstance;
- let zrx: ContractInstance;
- let exchange: ContractInstance;
- let tokenRegistry: ContractInstance;
-
- let order: Order;
- let balances: BalancesByOwner;
- let exWrapper: ExchangeWrapper;
- let dmyBalances: Balances;
- let orderFactory: OrderFactory;
-
- let zeroEx: ZeroEx;
-
- before(async () => {
- [tokenRegistry, exchange] = await Promise.all([TokenRegistry.deployed(), Exchange.deployed()]);
- exWrapper = new ExchangeWrapper(exchange);
- zeroEx = new ZeroEx(web3.currentProvider, {
- exchangeContractAddress: exchange.address,
- networkId: constants.TESTRPC_NETWORK_ID,
- });
-
- const [repAddress, dgdAddress, zrxAddress] = await Promise.all([
- tokenRegistry.getTokenAddressBySymbol('REP'),
- tokenRegistry.getTokenAddressBySymbol('DGD'),
- tokenRegistry.getTokenAddressBySymbol('ZRX'),
- ]);
-
- const defaultOrderParams = {
- exchangeContractAddress: Exchange.address,
- maker,
- feeRecipient,
- makerToken: repAddress,
- takerToken: dgdAddress,
- makerTokenAmount: ZeroEx.toBaseUnitAmount(new BigNumber(100), 18),
- takerTokenAmount: ZeroEx.toBaseUnitAmount(new BigNumber(200), 18),
- makerFee: ZeroEx.toBaseUnitAmount(new BigNumber(1), 18),
- takerFee: ZeroEx.toBaseUnitAmount(new BigNumber(1), 18),
- };
- orderFactory = new OrderFactory(defaultOrderParams);
-
- [rep, dgd, zrx] = await Promise.all([
- DummyToken.at(repAddress),
- DummyToken.at(dgdAddress),
- DummyToken.at(zrxAddress),
- ]);
- dmyBalances = new Balances([rep, dgd, zrx], [maker, taker, feeRecipient]);
- await Promise.all([
- rep.approve(TokenTransferProxy.address, INITIAL_ALLOWANCE, {
- from: maker,
- }),
- rep.approve(TokenTransferProxy.address, INITIAL_ALLOWANCE, {
- from: taker,
- }),
- rep.setBalance(maker, INITIAL_BALANCE, { from: tokenOwner }),
- rep.setBalance(taker, INITIAL_BALANCE, { from: tokenOwner }),
- dgd.approve(TokenTransferProxy.address, INITIAL_ALLOWANCE, {
- from: maker,
- }),
- dgd.approve(TokenTransferProxy.address, INITIAL_ALLOWANCE, {
- from: taker,
- }),
- dgd.setBalance(maker, INITIAL_BALANCE, { from: tokenOwner }),
- dgd.setBalance(taker, INITIAL_BALANCE, { from: tokenOwner }),
- zrx.approve(TokenTransferProxy.address, INITIAL_ALLOWANCE, {
- from: maker,
- }),
- zrx.approve(TokenTransferProxy.address, INITIAL_ALLOWANCE, {
- from: taker,
- }),
- zrx.setBalance(maker, INITIAL_BALANCE, { from: tokenOwner }),
- zrx.setBalance(taker, INITIAL_BALANCE, { from: tokenOwner }),
- ]);
- });
-
- describe('internal functions', () => {
- it('should include transferViaTokenTransferProxy', () => {
- expect(exchange.transferViaTokenTransferProxy).to.be.undefined();
- });
-
- it('should include isTransferable', () => {
- expect(exchange.isTransferable).to.be.undefined();
- });
-
- it('should include getBalance', () => {
- expect(exchange.getBalance).to.be.undefined();
- });
-
- it('should include getAllowance', () => {
- expect(exchange.getAllowance).to.be.undefined();
- });
- });
-
- describe('fillOrder', () => {
- beforeEach(async () => {
- balances = await dmyBalances.getAsync();
- order = await orderFactory.newSignedOrderAsync();
- });
-
- it('should create an unfillable order', async () => {
- order = await orderFactory.newSignedOrderAsync({
- makerTokenAmount: new BigNumber(1001),
- takerTokenAmount: new BigNumber(3),
- });
-
- const filledTakerTokenAmountBefore = await zeroEx.exchange.getFilledTakerAmountAsync(
- order.params.orderHashHex,
- );
- expect(filledTakerTokenAmountBefore).to.be.bignumber.equal(0);
-
- const fillTakerTokenAmount1 = new BigNumber(2);
- await exWrapper.fillOrderAsync(order, taker, {
- fillTakerTokenAmount: fillTakerTokenAmount1,
- });
-
- const filledTakerTokenAmountAfter1 = await zeroEx.exchange.getFilledTakerAmountAsync(
- order.params.orderHashHex,
- );
- expect(filledTakerTokenAmountAfter1).to.be.bignumber.equal(fillTakerTokenAmount1);
-
- const fillTakerTokenAmount2 = new BigNumber(1);
- await exWrapper.fillOrderAsync(order, taker, {
- fillTakerTokenAmount: fillTakerTokenAmount2,
- });
-
- const filledTakerTokenAmountAfter2 = await zeroEx.exchange.getFilledTakerAmountAsync(
- order.params.orderHashHex,
- );
- expect(filledTakerTokenAmountAfter2).to.be.bignumber.equal(filledTakerTokenAmountAfter1);
- });
-
- it('should transfer the correct amounts when makerTokenAmount === takerTokenAmount', async () => {
- order = await orderFactory.newSignedOrderAsync({
- makerTokenAmount: ZeroEx.toBaseUnitAmount(new BigNumber(100), 18),
- takerTokenAmount: ZeroEx.toBaseUnitAmount(new BigNumber(100), 18),
- });
-
- const filledTakerTokenAmountBefore = await zeroEx.exchange.getFilledTakerAmountAsync(
- order.params.orderHashHex,
- );
- expect(filledTakerTokenAmountBefore).to.be.bignumber.equal(0);
-
- const fillTakerTokenAmount = order.params.takerTokenAmount.div(2);
- await exWrapper.fillOrderAsync(order, taker, { fillTakerTokenAmount });
-
- const filledTakerTokenAmountAfter = await zeroEx.exchange.getFilledTakerAmountAsync(
- order.params.orderHashHex,
- );
- expect(filledTakerTokenAmountAfter).to.be.bignumber.equal(fillTakerTokenAmount);
-
- const newBalances = await dmyBalances.getAsync();
-
- const fillMakerTokenAmount = fillTakerTokenAmount
- .times(order.params.makerTokenAmount)
- .dividedToIntegerBy(order.params.takerTokenAmount);
- const paidMakerFee = order.params.makerFee
- .times(fillMakerTokenAmount)
- .dividedToIntegerBy(order.params.makerTokenAmount);
- const paidTakerFee = order.params.takerFee
- .times(fillMakerTokenAmount)
- .dividedToIntegerBy(order.params.makerTokenAmount);
- expect(newBalances[maker][order.params.makerToken]).to.be.bignumber.equal(
- balances[maker][order.params.makerToken].minus(fillMakerTokenAmount),
- );
- expect(newBalances[maker][order.params.takerToken]).to.be.bignumber.equal(
- balances[maker][order.params.takerToken].add(fillTakerTokenAmount),
- );
- expect(newBalances[maker][zrx.address]).to.be.bignumber.equal(
- balances[maker][zrx.address].minus(paidMakerFee),
- );
- expect(newBalances[taker][order.params.takerToken]).to.be.bignumber.equal(
- balances[taker][order.params.takerToken].minus(fillTakerTokenAmount),
- );
- expect(newBalances[taker][order.params.makerToken]).to.be.bignumber.equal(
- balances[taker][order.params.makerToken].add(fillMakerTokenAmount),
- );
- expect(newBalances[taker][zrx.address]).to.be.bignumber.equal(
- balances[taker][zrx.address].minus(paidTakerFee),
- );
- expect(newBalances[feeRecipient][zrx.address]).to.be.bignumber.equal(
- balances[feeRecipient][zrx.address].add(paidMakerFee.add(paidTakerFee)),
- );
- });
-
- it('should transfer the correct amounts when makerTokenAmount > takerTokenAmount', async () => {
- order = await orderFactory.newSignedOrderAsync({
- makerTokenAmount: ZeroEx.toBaseUnitAmount(new BigNumber(200), 18),
- takerTokenAmount: ZeroEx.toBaseUnitAmount(new BigNumber(100), 18),
- });
-
- const filledTakerTokenAmountBefore = await zeroEx.exchange.getFilledTakerAmountAsync(
- order.params.orderHashHex,
- );
- expect(filledTakerTokenAmountBefore).to.be.bignumber.equal(0);
-
- const fillTakerTokenAmount = order.params.takerTokenAmount.div(2);
- await exWrapper.fillOrderAsync(order, taker, { fillTakerTokenAmount });
-
- const filledTakerTokenAmountAfter = await zeroEx.exchange.getFilledTakerAmountAsync(
- order.params.orderHashHex,
- );
- expect(filledTakerTokenAmountAfter).to.be.bignumber.equal(fillTakerTokenAmount);
-
- const newBalances = await dmyBalances.getAsync();
-
- const fillMakerTokenAmount = fillTakerTokenAmount
- .times(order.params.makerTokenAmount)
- .dividedToIntegerBy(order.params.takerTokenAmount);
- const paidMakerFee = order.params.makerFee
- .times(fillMakerTokenAmount)
- .dividedToIntegerBy(order.params.makerTokenAmount);
- const paidTakerFee = order.params.takerFee
- .times(fillMakerTokenAmount)
- .dividedToIntegerBy(order.params.makerTokenAmount);
- expect(newBalances[maker][order.params.makerToken]).to.be.bignumber.equal(
- balances[maker][order.params.makerToken].minus(fillMakerTokenAmount),
- );
- expect(newBalances[maker][order.params.takerToken]).to.be.bignumber.equal(
- balances[maker][order.params.takerToken].add(fillTakerTokenAmount),
- );
- expect(newBalances[maker][zrx.address]).to.be.bignumber.equal(
- balances[maker][zrx.address].minus(paidMakerFee),
- );
- expect(newBalances[taker][order.params.takerToken]).to.be.bignumber.equal(
- balances[taker][order.params.takerToken].minus(fillTakerTokenAmount),
- );
- expect(newBalances[taker][order.params.makerToken]).to.be.bignumber.equal(
- balances[taker][order.params.makerToken].add(fillMakerTokenAmount),
- );
- expect(newBalances[taker][zrx.address]).to.be.bignumber.equal(
- balances[taker][zrx.address].minus(paidTakerFee),
- );
- expect(newBalances[feeRecipient][zrx.address]).to.be.bignumber.equal(
- balances[feeRecipient][zrx.address].add(paidMakerFee.add(paidTakerFee)),
- );
- });
-
- it('should transfer the correct amounts when makerTokenAmount < takerTokenAmount', async () => {
- order = await orderFactory.newSignedOrderAsync({
- makerTokenAmount: ZeroEx.toBaseUnitAmount(new BigNumber(100), 18),
- takerTokenAmount: ZeroEx.toBaseUnitAmount(new BigNumber(200), 18),
- });
-
- const filledTakerTokenAmountBefore = await zeroEx.exchange.getFilledTakerAmountAsync(
- order.params.orderHashHex,
- );
- expect(filledTakerTokenAmountBefore).to.be.bignumber.equal(0);
-
- const fillTakerTokenAmount = order.params.takerTokenAmount.div(2);
- await exWrapper.fillOrderAsync(order, taker, { fillTakerTokenAmount });
-
- const filledTakerTokenAmountAfter = await zeroEx.exchange.getFilledTakerAmountAsync(
- order.params.orderHashHex,
- );
- expect(filledTakerTokenAmountAfter).to.be.bignumber.equal(fillTakerTokenAmount);
-
- const newBalances = await dmyBalances.getAsync();
-
- const fillMakerTokenAmount = fillTakerTokenAmount
- .times(order.params.makerTokenAmount)
- .dividedToIntegerBy(order.params.takerTokenAmount);
- const paidMakerFee = order.params.makerFee
- .times(fillMakerTokenAmount)
- .dividedToIntegerBy(order.params.makerTokenAmount);
- const paidTakerFee = order.params.takerFee
- .times(fillMakerTokenAmount)
- .dividedToIntegerBy(order.params.makerTokenAmount);
- expect(newBalances[maker][order.params.makerToken]).to.be.bignumber.equal(
- balances[maker][order.params.makerToken].minus(fillMakerTokenAmount),
- );
- expect(newBalances[maker][order.params.takerToken]).to.be.bignumber.equal(
- balances[maker][order.params.takerToken].add(fillTakerTokenAmount),
- );
- expect(newBalances[maker][zrx.address]).to.be.bignumber.equal(
- balances[maker][zrx.address].minus(paidMakerFee),
- );
- expect(newBalances[taker][order.params.takerToken]).to.be.bignumber.equal(
- balances[taker][order.params.takerToken].minus(fillTakerTokenAmount),
- );
- expect(newBalances[taker][order.params.makerToken]).to.be.bignumber.equal(
- balances[taker][order.params.makerToken].add(fillMakerTokenAmount),
- );
- expect(newBalances[taker][zrx.address]).to.be.bignumber.equal(
- balances[taker][zrx.address].minus(paidTakerFee),
- );
- expect(newBalances[feeRecipient][zrx.address]).to.be.bignumber.equal(
- balances[feeRecipient][zrx.address].add(paidMakerFee.add(paidTakerFee)),
- );
- });
-
- it('should transfer the correct amounts when taker is specified and order is claimed by taker', async () => {
- order = await orderFactory.newSignedOrderAsync({
- taker,
- makerTokenAmount: ZeroEx.toBaseUnitAmount(new BigNumber(100), 18),
- takerTokenAmount: ZeroEx.toBaseUnitAmount(new BigNumber(200), 18),
- });
-
- const filledTakerTokenAmountBefore = await zeroEx.exchange.getFilledTakerAmountAsync(
- order.params.orderHashHex,
- );
- expect(filledTakerTokenAmountBefore).to.be.bignumber.equal(0);
-
- const fillTakerTokenAmount = order.params.takerTokenAmount.div(2);
- await exWrapper.fillOrderAsync(order, taker, { fillTakerTokenAmount });
-
- const filledTakerTokenAmountAfter = await zeroEx.exchange.getFilledTakerAmountAsync(
- order.params.orderHashHex,
- );
- const expectedFillAmountTAfter = fillTakerTokenAmount.add(filledTakerTokenAmountBefore);
- expect(filledTakerTokenAmountAfter).to.be.bignumber.equal(expectedFillAmountTAfter);
-
- const newBalances = await dmyBalances.getAsync();
-
- const fillMakerTokenAmount = fillTakerTokenAmount
- .times(order.params.makerTokenAmount)
- .dividedToIntegerBy(order.params.takerTokenAmount);
- const paidMakerFee = order.params.makerFee
- .times(fillMakerTokenAmount)
- .dividedToIntegerBy(order.params.makerTokenAmount);
- const paidTakerFee = order.params.takerFee
- .times(fillMakerTokenAmount)
- .dividedToIntegerBy(order.params.makerTokenAmount);
- expect(newBalances[maker][order.params.makerToken]).to.be.bignumber.equal(
- balances[maker][order.params.makerToken].minus(fillMakerTokenAmount),
- );
- expect(newBalances[maker][order.params.takerToken]).to.be.bignumber.equal(
- balances[maker][order.params.takerToken].add(fillTakerTokenAmount),
- );
- expect(newBalances[maker][zrx.address]).to.be.bignumber.equal(
- balances[maker][zrx.address].minus(paidMakerFee),
- );
- expect(newBalances[taker][order.params.takerToken]).to.be.bignumber.equal(
- balances[taker][order.params.takerToken].minus(fillTakerTokenAmount),
- );
- expect(newBalances[taker][order.params.makerToken]).to.be.bignumber.equal(
- balances[taker][order.params.makerToken].add(fillMakerTokenAmount),
- );
- expect(newBalances[taker][zrx.address]).to.be.bignumber.equal(
- balances[taker][zrx.address].minus(paidTakerFee),
- );
- expect(newBalances[feeRecipient][zrx.address]).to.be.bignumber.equal(
- balances[feeRecipient][zrx.address].add(paidMakerFee.add(paidTakerFee)),
- );
- });
-
- it('should fill remaining value if fillTakerTokenAmount > remaining takerTokenAmount', async () => {
- const fillTakerTokenAmount = order.params.takerTokenAmount.div(2);
- await exWrapper.fillOrderAsync(order, taker, { fillTakerTokenAmount });
-
- const res = await exWrapper.fillOrderAsync(order, taker, {
- fillTakerTokenAmount: order.params.takerTokenAmount,
- });
-
- expect(res.logs[0].args.filledTakerTokenAmount).to.be.bignumber.equal(
- order.params.takerTokenAmount.minus(fillTakerTokenAmount),
- );
- const newBalances = await dmyBalances.getAsync();
-
- expect(newBalances[maker][order.params.makerToken]).to.be.bignumber.equal(
- balances[maker][order.params.makerToken].minus(order.params.makerTokenAmount),
- );
- expect(newBalances[maker][order.params.takerToken]).to.be.bignumber.equal(
- balances[maker][order.params.takerToken].add(order.params.takerTokenAmount),
- );
- expect(newBalances[maker][zrx.address]).to.be.bignumber.equal(
- balances[maker][zrx.address].minus(order.params.makerFee),
- );
- expect(newBalances[taker][order.params.takerToken]).to.be.bignumber.equal(
- balances[taker][order.params.takerToken].minus(order.params.takerTokenAmount),
- );
- expect(newBalances[taker][order.params.makerToken]).to.be.bignumber.equal(
- balances[taker][order.params.makerToken].add(order.params.makerTokenAmount),
- );
- expect(newBalances[taker][zrx.address]).to.be.bignumber.equal(
- balances[taker][zrx.address].minus(order.params.takerFee),
- );
- expect(newBalances[feeRecipient][zrx.address]).to.be.bignumber.equal(
- balances[feeRecipient][zrx.address].add(order.params.makerFee.add(order.params.takerFee)),
- );
- });
-
- it('should log 1 event with the correct arguments when order has a feeRecipient', async () => {
- const divisor = 2;
- const res = await exWrapper.fillOrderAsync(order, taker, {
- fillTakerTokenAmount: order.params.takerTokenAmount.div(divisor),
- });
- expect(res.logs).to.have.length(1);
-
- const logArgs = res.logs[0].args;
- const expectedFilledMakerTokenAmount = order.params.makerTokenAmount.div(divisor);
- const expectedFilledTakerTokenAmount = order.params.takerTokenAmount.div(divisor);
- const expectedFeeMPaid = order.params.makerFee.div(divisor);
- const expectedFeeTPaid = order.params.takerFee.div(divisor);
- const tokensHashBuff = crypto.solSHA3([order.params.makerToken, order.params.takerToken]);
- const expectedTokens = ethUtil.bufferToHex(tokensHashBuff);
-
- expect(order.params.maker).to.be.equal(logArgs.maker);
- expect(taker).to.be.equal(logArgs.taker);
- expect(order.params.feeRecipient).to.be.equal(logArgs.feeRecipient);
- expect(order.params.makerToken).to.be.equal(logArgs.makerToken);
- expect(order.params.takerToken).to.be.equal(logArgs.takerToken);
- expect(expectedFilledMakerTokenAmount).to.be.bignumber.equal(logArgs.filledMakerTokenAmount);
- expect(expectedFilledTakerTokenAmount).to.be.bignumber.equal(logArgs.filledTakerTokenAmount);
- expect(expectedFeeMPaid).to.be.bignumber.equal(logArgs.paidMakerFee);
- expect(expectedFeeTPaid).to.be.bignumber.equal(logArgs.paidTakerFee);
- expect(expectedTokens).to.be.equal(logArgs.tokens);
- expect(order.params.orderHashHex).to.be.equal(logArgs.orderHash);
- });
-
- it('should log 1 event with the correct arguments when order has no feeRecipient', async () => {
- order = await orderFactory.newSignedOrderAsync({
- feeRecipient: ZeroEx.NULL_ADDRESS,
- });
- const divisor = 2;
- const res = await exWrapper.fillOrderAsync(order, taker, {
- fillTakerTokenAmount: order.params.takerTokenAmount.div(divisor),
- });
- expect(res.logs).to.have.length(1);
-
- const logArgs = res.logs[0].args;
- const expectedFilledMakerTokenAmount = order.params.makerTokenAmount.div(divisor);
- const expectedFilledTakerTokenAmount = order.params.takerTokenAmount.div(divisor);
- const expectedFeeMPaid = new BigNumber(0);
- const expectedFeeTPaid = new BigNumber(0);
- const tokensHashBuff = crypto.solSHA3([order.params.makerToken, order.params.takerToken]);
- const expectedTokens = ethUtil.bufferToHex(tokensHashBuff);
-
- expect(order.params.maker).to.be.equal(logArgs.maker);
- expect(taker).to.be.equal(logArgs.taker);
- expect(order.params.feeRecipient).to.be.equal(logArgs.feeRecipient);
- expect(order.params.makerToken).to.be.equal(logArgs.makerToken);
- expect(order.params.takerToken).to.be.equal(logArgs.takerToken);
- expect(expectedFilledMakerTokenAmount).to.be.bignumber.equal(logArgs.filledMakerTokenAmount);
- expect(expectedFilledTakerTokenAmount).to.be.bignumber.equal(logArgs.filledTakerTokenAmount);
- expect(expectedFeeMPaid).to.be.bignumber.equal(logArgs.paidMakerFee);
- expect(expectedFeeTPaid).to.be.bignumber.equal(logArgs.paidTakerFee);
- expect(expectedTokens).to.be.equal(logArgs.tokens);
- expect(order.params.orderHashHex).to.be.equal(logArgs.orderHash);
- });
-
- it('should throw when taker is specified and order is claimed by other', async () => {
- order = await orderFactory.newSignedOrderAsync({
- taker: feeRecipient,
- makerTokenAmount: ZeroEx.toBaseUnitAmount(new BigNumber(100), 18),
- takerTokenAmount: ZeroEx.toBaseUnitAmount(new BigNumber(200), 18),
- });
-
- return expect(exWrapper.fillOrderAsync(order, taker)).to.be.rejectedWith(constants.REVERT);
- });
-
- it('should throw if signature is invalid', async () => {
- order = await orderFactory.newSignedOrderAsync({
- makerTokenAmount: ZeroEx.toBaseUnitAmount(new BigNumber(10), 18),
- });
-
- order.params.r = ethUtil.bufferToHex(ethUtil.sha3('invalidR'));
- order.params.s = ethUtil.bufferToHex(ethUtil.sha3('invalidS'));
- return expect(exWrapper.fillOrderAsync(order, taker)).to.be.rejectedWith(constants.REVERT);
- });
-
- it('should throw if makerTokenAmount is 0', async () => {
- order = await orderFactory.newSignedOrderAsync({
- makerTokenAmount: new BigNumber(0),
- });
-
- return expect(exWrapper.fillOrderAsync(order, taker)).to.be.rejectedWith(constants.REVERT);
- });
-
- it('should throw if takerTokenAmount is 0', async () => {
- order = await orderFactory.newSignedOrderAsync({
- takerTokenAmount: new BigNumber(0),
- });
-
- return expect(exWrapper.fillOrderAsync(order, taker)).to.be.rejectedWith(constants.REVERT);
- });
-
- it('should throw if fillTakerTokenAmount is 0', async () => {
- order = await orderFactory.newSignedOrderAsync();
-
- return expect(
- exWrapper.fillOrderAsync(order, taker, {
- fillTakerTokenAmount: new BigNumber(0),
- }),
- ).to.be.rejectedWith(constants.REVERT);
- });
-
- it('should not change balances if maker balances are too low to fill order and \
+ const maker = accounts[0];
+ const tokenOwner = accounts[0];
+ const taker = accounts[1] || accounts[accounts.length - 1];
+ const feeRecipient = accounts[2] || accounts[accounts.length - 1];
+
+ const INITIAL_BALANCE = ZeroEx.toBaseUnitAmount(new BigNumber(10000), 18);
+ const INITIAL_ALLOWANCE = ZeroEx.toBaseUnitAmount(new BigNumber(10000), 18);
+
+ let rep: ContractInstance;
+ let dgd: ContractInstance;
+ let zrx: ContractInstance;
+ let exchange: ContractInstance;
+ let tokenRegistry: ContractInstance;
+
+ let order: Order;
+ let balances: BalancesByOwner;
+ let exWrapper: ExchangeWrapper;
+ let dmyBalances: Balances;
+ let orderFactory: OrderFactory;
+
+ let zeroEx: ZeroEx;
+
+ before(async () => {
+ [tokenRegistry, exchange] = await Promise.all([TokenRegistry.deployed(), Exchange.deployed()]);
+ exWrapper = new ExchangeWrapper(exchange);
+ zeroEx = new ZeroEx(web3.currentProvider, {
+ exchangeContractAddress: exchange.address,
+ networkId: constants.TESTRPC_NETWORK_ID,
+ });
+
+ const [repAddress, dgdAddress, zrxAddress] = await Promise.all([
+ tokenRegistry.getTokenAddressBySymbol('REP'),
+ tokenRegistry.getTokenAddressBySymbol('DGD'),
+ tokenRegistry.getTokenAddressBySymbol('ZRX'),
+ ]);
+
+ const defaultOrderParams = {
+ exchangeContractAddress: Exchange.address,
+ maker,
+ feeRecipient,
+ makerToken: repAddress,
+ takerToken: dgdAddress,
+ makerTokenAmount: ZeroEx.toBaseUnitAmount(new BigNumber(100), 18),
+ takerTokenAmount: ZeroEx.toBaseUnitAmount(new BigNumber(200), 18),
+ makerFee: ZeroEx.toBaseUnitAmount(new BigNumber(1), 18),
+ takerFee: ZeroEx.toBaseUnitAmount(new BigNumber(1), 18),
+ };
+ orderFactory = new OrderFactory(defaultOrderParams);
+
+ [rep, dgd, zrx] = await Promise.all([
+ DummyToken.at(repAddress),
+ DummyToken.at(dgdAddress),
+ DummyToken.at(zrxAddress),
+ ]);
+ dmyBalances = new Balances([rep, dgd, zrx], [maker, taker, feeRecipient]);
+ await Promise.all([
+ rep.approve(TokenTransferProxy.address, INITIAL_ALLOWANCE, {
+ from: maker,
+ }),
+ rep.approve(TokenTransferProxy.address, INITIAL_ALLOWANCE, {
+ from: taker,
+ }),
+ rep.setBalance(maker, INITIAL_BALANCE, { from: tokenOwner }),
+ rep.setBalance(taker, INITIAL_BALANCE, { from: tokenOwner }),
+ dgd.approve(TokenTransferProxy.address, INITIAL_ALLOWANCE, {
+ from: maker,
+ }),
+ dgd.approve(TokenTransferProxy.address, INITIAL_ALLOWANCE, {
+ from: taker,
+ }),
+ dgd.setBalance(maker, INITIAL_BALANCE, { from: tokenOwner }),
+ dgd.setBalance(taker, INITIAL_BALANCE, { from: tokenOwner }),
+ zrx.approve(TokenTransferProxy.address, INITIAL_ALLOWANCE, {
+ from: maker,
+ }),
+ zrx.approve(TokenTransferProxy.address, INITIAL_ALLOWANCE, {
+ from: taker,
+ }),
+ zrx.setBalance(maker, INITIAL_BALANCE, { from: tokenOwner }),
+ zrx.setBalance(taker, INITIAL_BALANCE, { from: tokenOwner }),
+ ]);
+ });
+
+ describe('internal functions', () => {
+ it('should include transferViaTokenTransferProxy', () => {
+ expect(exchange.transferViaTokenTransferProxy).to.be.undefined();
+ });
+
+ it('should include isTransferable', () => {
+ expect(exchange.isTransferable).to.be.undefined();
+ });
+
+ it('should include getBalance', () => {
+ expect(exchange.getBalance).to.be.undefined();
+ });
+
+ it('should include getAllowance', () => {
+ expect(exchange.getAllowance).to.be.undefined();
+ });
+ });
+
+ describe('fillOrder', () => {
+ beforeEach(async () => {
+ balances = await dmyBalances.getAsync();
+ order = await orderFactory.newSignedOrderAsync();
+ });
+
+ it('should create an unfillable order', async () => {
+ order = await orderFactory.newSignedOrderAsync({
+ makerTokenAmount: new BigNumber(1001),
+ takerTokenAmount: new BigNumber(3),
+ });
+
+ const filledTakerTokenAmountBefore = await zeroEx.exchange.getFilledTakerAmountAsync(
+ order.params.orderHashHex,
+ );
+ expect(filledTakerTokenAmountBefore).to.be.bignumber.equal(0);
+
+ const fillTakerTokenAmount1 = new BigNumber(2);
+ await exWrapper.fillOrderAsync(order, taker, {
+ fillTakerTokenAmount: fillTakerTokenAmount1,
+ });
+
+ const filledTakerTokenAmountAfter1 = await zeroEx.exchange.getFilledTakerAmountAsync(
+ order.params.orderHashHex,
+ );
+ expect(filledTakerTokenAmountAfter1).to.be.bignumber.equal(fillTakerTokenAmount1);
+
+ const fillTakerTokenAmount2 = new BigNumber(1);
+ await exWrapper.fillOrderAsync(order, taker, {
+ fillTakerTokenAmount: fillTakerTokenAmount2,
+ });
+
+ const filledTakerTokenAmountAfter2 = await zeroEx.exchange.getFilledTakerAmountAsync(
+ order.params.orderHashHex,
+ );
+ expect(filledTakerTokenAmountAfter2).to.be.bignumber.equal(filledTakerTokenAmountAfter1);
+ });
+
+ it('should transfer the correct amounts when makerTokenAmount === takerTokenAmount', async () => {
+ order = await orderFactory.newSignedOrderAsync({
+ makerTokenAmount: ZeroEx.toBaseUnitAmount(new BigNumber(100), 18),
+ takerTokenAmount: ZeroEx.toBaseUnitAmount(new BigNumber(100), 18),
+ });
+
+ const filledTakerTokenAmountBefore = await zeroEx.exchange.getFilledTakerAmountAsync(
+ order.params.orderHashHex,
+ );
+ expect(filledTakerTokenAmountBefore).to.be.bignumber.equal(0);
+
+ const fillTakerTokenAmount = order.params.takerTokenAmount.div(2);
+ await exWrapper.fillOrderAsync(order, taker, { fillTakerTokenAmount });
+
+ const filledTakerTokenAmountAfter = await zeroEx.exchange.getFilledTakerAmountAsync(
+ order.params.orderHashHex,
+ );
+ expect(filledTakerTokenAmountAfter).to.be.bignumber.equal(fillTakerTokenAmount);
+
+ const newBalances = await dmyBalances.getAsync();
+
+ const fillMakerTokenAmount = fillTakerTokenAmount
+ .times(order.params.makerTokenAmount)
+ .dividedToIntegerBy(order.params.takerTokenAmount);
+ const paidMakerFee = order.params.makerFee
+ .times(fillMakerTokenAmount)
+ .dividedToIntegerBy(order.params.makerTokenAmount);
+ const paidTakerFee = order.params.takerFee
+ .times(fillMakerTokenAmount)
+ .dividedToIntegerBy(order.params.makerTokenAmount);
+ expect(newBalances[maker][order.params.makerToken]).to.be.bignumber.equal(
+ balances[maker][order.params.makerToken].minus(fillMakerTokenAmount),
+ );
+ expect(newBalances[maker][order.params.takerToken]).to.be.bignumber.equal(
+ balances[maker][order.params.takerToken].add(fillTakerTokenAmount),
+ );
+ expect(newBalances[maker][zrx.address]).to.be.bignumber.equal(
+ balances[maker][zrx.address].minus(paidMakerFee),
+ );
+ expect(newBalances[taker][order.params.takerToken]).to.be.bignumber.equal(
+ balances[taker][order.params.takerToken].minus(fillTakerTokenAmount),
+ );
+ expect(newBalances[taker][order.params.makerToken]).to.be.bignumber.equal(
+ balances[taker][order.params.makerToken].add(fillMakerTokenAmount),
+ );
+ expect(newBalances[taker][zrx.address]).to.be.bignumber.equal(
+ balances[taker][zrx.address].minus(paidTakerFee),
+ );
+ expect(newBalances[feeRecipient][zrx.address]).to.be.bignumber.equal(
+ balances[feeRecipient][zrx.address].add(paidMakerFee.add(paidTakerFee)),
+ );
+ });
+
+ it('should transfer the correct amounts when makerTokenAmount > takerTokenAmount', async () => {
+ order = await orderFactory.newSignedOrderAsync({
+ makerTokenAmount: ZeroEx.toBaseUnitAmount(new BigNumber(200), 18),
+ takerTokenAmount: ZeroEx.toBaseUnitAmount(new BigNumber(100), 18),
+ });
+
+ const filledTakerTokenAmountBefore = await zeroEx.exchange.getFilledTakerAmountAsync(
+ order.params.orderHashHex,
+ );
+ expect(filledTakerTokenAmountBefore).to.be.bignumber.equal(0);
+
+ const fillTakerTokenAmount = order.params.takerTokenAmount.div(2);
+ await exWrapper.fillOrderAsync(order, taker, { fillTakerTokenAmount });
+
+ const filledTakerTokenAmountAfter = await zeroEx.exchange.getFilledTakerAmountAsync(
+ order.params.orderHashHex,
+ );
+ expect(filledTakerTokenAmountAfter).to.be.bignumber.equal(fillTakerTokenAmount);
+
+ const newBalances = await dmyBalances.getAsync();
+
+ const fillMakerTokenAmount = fillTakerTokenAmount
+ .times(order.params.makerTokenAmount)
+ .dividedToIntegerBy(order.params.takerTokenAmount);
+ const paidMakerFee = order.params.makerFee
+ .times(fillMakerTokenAmount)
+ .dividedToIntegerBy(order.params.makerTokenAmount);
+ const paidTakerFee = order.params.takerFee
+ .times(fillMakerTokenAmount)
+ .dividedToIntegerBy(order.params.makerTokenAmount);
+ expect(newBalances[maker][order.params.makerToken]).to.be.bignumber.equal(
+ balances[maker][order.params.makerToken].minus(fillMakerTokenAmount),
+ );
+ expect(newBalances[maker][order.params.takerToken]).to.be.bignumber.equal(
+ balances[maker][order.params.takerToken].add(fillTakerTokenAmount),
+ );
+ expect(newBalances[maker][zrx.address]).to.be.bignumber.equal(
+ balances[maker][zrx.address].minus(paidMakerFee),
+ );
+ expect(newBalances[taker][order.params.takerToken]).to.be.bignumber.equal(
+ balances[taker][order.params.takerToken].minus(fillTakerTokenAmount),
+ );
+ expect(newBalances[taker][order.params.makerToken]).to.be.bignumber.equal(
+ balances[taker][order.params.makerToken].add(fillMakerTokenAmount),
+ );
+ expect(newBalances[taker][zrx.address]).to.be.bignumber.equal(
+ balances[taker][zrx.address].minus(paidTakerFee),
+ );
+ expect(newBalances[feeRecipient][zrx.address]).to.be.bignumber.equal(
+ balances[feeRecipient][zrx.address].add(paidMakerFee.add(paidTakerFee)),
+ );
+ });
+
+ it('should transfer the correct amounts when makerTokenAmount < takerTokenAmount', async () => {
+ order = await orderFactory.newSignedOrderAsync({
+ makerTokenAmount: ZeroEx.toBaseUnitAmount(new BigNumber(100), 18),
+ takerTokenAmount: ZeroEx.toBaseUnitAmount(new BigNumber(200), 18),
+ });
+
+ const filledTakerTokenAmountBefore = await zeroEx.exchange.getFilledTakerAmountAsync(
+ order.params.orderHashHex,
+ );
+ expect(filledTakerTokenAmountBefore).to.be.bignumber.equal(0);
+
+ const fillTakerTokenAmount = order.params.takerTokenAmount.div(2);
+ await exWrapper.fillOrderAsync(order, taker, { fillTakerTokenAmount });
+
+ const filledTakerTokenAmountAfter = await zeroEx.exchange.getFilledTakerAmountAsync(
+ order.params.orderHashHex,
+ );
+ expect(filledTakerTokenAmountAfter).to.be.bignumber.equal(fillTakerTokenAmount);
+
+ const newBalances = await dmyBalances.getAsync();
+
+ const fillMakerTokenAmount = fillTakerTokenAmount
+ .times(order.params.makerTokenAmount)
+ .dividedToIntegerBy(order.params.takerTokenAmount);
+ const paidMakerFee = order.params.makerFee
+ .times(fillMakerTokenAmount)
+ .dividedToIntegerBy(order.params.makerTokenAmount);
+ const paidTakerFee = order.params.takerFee
+ .times(fillMakerTokenAmount)
+ .dividedToIntegerBy(order.params.makerTokenAmount);
+ expect(newBalances[maker][order.params.makerToken]).to.be.bignumber.equal(
+ balances[maker][order.params.makerToken].minus(fillMakerTokenAmount),
+ );
+ expect(newBalances[maker][order.params.takerToken]).to.be.bignumber.equal(
+ balances[maker][order.params.takerToken].add(fillTakerTokenAmount),
+ );
+ expect(newBalances[maker][zrx.address]).to.be.bignumber.equal(
+ balances[maker][zrx.address].minus(paidMakerFee),
+ );
+ expect(newBalances[taker][order.params.takerToken]).to.be.bignumber.equal(
+ balances[taker][order.params.takerToken].minus(fillTakerTokenAmount),
+ );
+ expect(newBalances[taker][order.params.makerToken]).to.be.bignumber.equal(
+ balances[taker][order.params.makerToken].add(fillMakerTokenAmount),
+ );
+ expect(newBalances[taker][zrx.address]).to.be.bignumber.equal(
+ balances[taker][zrx.address].minus(paidTakerFee),
+ );
+ expect(newBalances[feeRecipient][zrx.address]).to.be.bignumber.equal(
+ balances[feeRecipient][zrx.address].add(paidMakerFee.add(paidTakerFee)),
+ );
+ });
+
+ it('should transfer the correct amounts when taker is specified and order is claimed by taker', async () => {
+ order = await orderFactory.newSignedOrderAsync({
+ taker,
+ makerTokenAmount: ZeroEx.toBaseUnitAmount(new BigNumber(100), 18),
+ takerTokenAmount: ZeroEx.toBaseUnitAmount(new BigNumber(200), 18),
+ });
+
+ const filledTakerTokenAmountBefore = await zeroEx.exchange.getFilledTakerAmountAsync(
+ order.params.orderHashHex,
+ );
+ expect(filledTakerTokenAmountBefore).to.be.bignumber.equal(0);
+
+ const fillTakerTokenAmount = order.params.takerTokenAmount.div(2);
+ await exWrapper.fillOrderAsync(order, taker, { fillTakerTokenAmount });
+
+ const filledTakerTokenAmountAfter = await zeroEx.exchange.getFilledTakerAmountAsync(
+ order.params.orderHashHex,
+ );
+ const expectedFillAmountTAfter = fillTakerTokenAmount.add(filledTakerTokenAmountBefore);
+ expect(filledTakerTokenAmountAfter).to.be.bignumber.equal(expectedFillAmountTAfter);
+
+ const newBalances = await dmyBalances.getAsync();
+
+ const fillMakerTokenAmount = fillTakerTokenAmount
+ .times(order.params.makerTokenAmount)
+ .dividedToIntegerBy(order.params.takerTokenAmount);
+ const paidMakerFee = order.params.makerFee
+ .times(fillMakerTokenAmount)
+ .dividedToIntegerBy(order.params.makerTokenAmount);
+ const paidTakerFee = order.params.takerFee
+ .times(fillMakerTokenAmount)
+ .dividedToIntegerBy(order.params.makerTokenAmount);
+ expect(newBalances[maker][order.params.makerToken]).to.be.bignumber.equal(
+ balances[maker][order.params.makerToken].minus(fillMakerTokenAmount),
+ );
+ expect(newBalances[maker][order.params.takerToken]).to.be.bignumber.equal(
+ balances[maker][order.params.takerToken].add(fillTakerTokenAmount),
+ );
+ expect(newBalances[maker][zrx.address]).to.be.bignumber.equal(
+ balances[maker][zrx.address].minus(paidMakerFee),
+ );
+ expect(newBalances[taker][order.params.takerToken]).to.be.bignumber.equal(
+ balances[taker][order.params.takerToken].minus(fillTakerTokenAmount),
+ );
+ expect(newBalances[taker][order.params.makerToken]).to.be.bignumber.equal(
+ balances[taker][order.params.makerToken].add(fillMakerTokenAmount),
+ );
+ expect(newBalances[taker][zrx.address]).to.be.bignumber.equal(
+ balances[taker][zrx.address].minus(paidTakerFee),
+ );
+ expect(newBalances[feeRecipient][zrx.address]).to.be.bignumber.equal(
+ balances[feeRecipient][zrx.address].add(paidMakerFee.add(paidTakerFee)),
+ );
+ });
+
+ it('should fill remaining value if fillTakerTokenAmount > remaining takerTokenAmount', async () => {
+ const fillTakerTokenAmount = order.params.takerTokenAmount.div(2);
+ await exWrapper.fillOrderAsync(order, taker, { fillTakerTokenAmount });
+
+ const res = await exWrapper.fillOrderAsync(order, taker, {
+ fillTakerTokenAmount: order.params.takerTokenAmount,
+ });
+
+ expect(res.logs[0].args.filledTakerTokenAmount).to.be.bignumber.equal(
+ order.params.takerTokenAmount.minus(fillTakerTokenAmount),
+ );
+ const newBalances = await dmyBalances.getAsync();
+
+ expect(newBalances[maker][order.params.makerToken]).to.be.bignumber.equal(
+ balances[maker][order.params.makerToken].minus(order.params.makerTokenAmount),
+ );
+ expect(newBalances[maker][order.params.takerToken]).to.be.bignumber.equal(
+ balances[maker][order.params.takerToken].add(order.params.takerTokenAmount),
+ );
+ expect(newBalances[maker][zrx.address]).to.be.bignumber.equal(
+ balances[maker][zrx.address].minus(order.params.makerFee),
+ );
+ expect(newBalances[taker][order.params.takerToken]).to.be.bignumber.equal(
+ balances[taker][order.params.takerToken].minus(order.params.takerTokenAmount),
+ );
+ expect(newBalances[taker][order.params.makerToken]).to.be.bignumber.equal(
+ balances[taker][order.params.makerToken].add(order.params.makerTokenAmount),
+ );
+ expect(newBalances[taker][zrx.address]).to.be.bignumber.equal(
+ balances[taker][zrx.address].minus(order.params.takerFee),
+ );
+ expect(newBalances[feeRecipient][zrx.address]).to.be.bignumber.equal(
+ balances[feeRecipient][zrx.address].add(order.params.makerFee.add(order.params.takerFee)),
+ );
+ });
+
+ it('should log 1 event with the correct arguments when order has a feeRecipient', async () => {
+ const divisor = 2;
+ const res = await exWrapper.fillOrderAsync(order, taker, {
+ fillTakerTokenAmount: order.params.takerTokenAmount.div(divisor),
+ });
+ expect(res.logs).to.have.length(1);
+
+ const logArgs = res.logs[0].args;
+ const expectedFilledMakerTokenAmount = order.params.makerTokenAmount.div(divisor);
+ const expectedFilledTakerTokenAmount = order.params.takerTokenAmount.div(divisor);
+ const expectedFeeMPaid = order.params.makerFee.div(divisor);
+ const expectedFeeTPaid = order.params.takerFee.div(divisor);
+ const tokensHashBuff = crypto.solSHA3([order.params.makerToken, order.params.takerToken]);
+ const expectedTokens = ethUtil.bufferToHex(tokensHashBuff);
+
+ expect(order.params.maker).to.be.equal(logArgs.maker);
+ expect(taker).to.be.equal(logArgs.taker);
+ expect(order.params.feeRecipient).to.be.equal(logArgs.feeRecipient);
+ expect(order.params.makerToken).to.be.equal(logArgs.makerToken);
+ expect(order.params.takerToken).to.be.equal(logArgs.takerToken);
+ expect(expectedFilledMakerTokenAmount).to.be.bignumber.equal(logArgs.filledMakerTokenAmount);
+ expect(expectedFilledTakerTokenAmount).to.be.bignumber.equal(logArgs.filledTakerTokenAmount);
+ expect(expectedFeeMPaid).to.be.bignumber.equal(logArgs.paidMakerFee);
+ expect(expectedFeeTPaid).to.be.bignumber.equal(logArgs.paidTakerFee);
+ expect(expectedTokens).to.be.equal(logArgs.tokens);
+ expect(order.params.orderHashHex).to.be.equal(logArgs.orderHash);
+ });
+
+ it('should log 1 event with the correct arguments when order has no feeRecipient', async () => {
+ order = await orderFactory.newSignedOrderAsync({
+ feeRecipient: ZeroEx.NULL_ADDRESS,
+ });
+ const divisor = 2;
+ const res = await exWrapper.fillOrderAsync(order, taker, {
+ fillTakerTokenAmount: order.params.takerTokenAmount.div(divisor),
+ });
+ expect(res.logs).to.have.length(1);
+
+ const logArgs = res.logs[0].args;
+ const expectedFilledMakerTokenAmount = order.params.makerTokenAmount.div(divisor);
+ const expectedFilledTakerTokenAmount = order.params.takerTokenAmount.div(divisor);
+ const expectedFeeMPaid = new BigNumber(0);
+ const expectedFeeTPaid = new BigNumber(0);
+ const tokensHashBuff = crypto.solSHA3([order.params.makerToken, order.params.takerToken]);
+ const expectedTokens = ethUtil.bufferToHex(tokensHashBuff);
+
+ expect(order.params.maker).to.be.equal(logArgs.maker);
+ expect(taker).to.be.equal(logArgs.taker);
+ expect(order.params.feeRecipient).to.be.equal(logArgs.feeRecipient);
+ expect(order.params.makerToken).to.be.equal(logArgs.makerToken);
+ expect(order.params.takerToken).to.be.equal(logArgs.takerToken);
+ expect(expectedFilledMakerTokenAmount).to.be.bignumber.equal(logArgs.filledMakerTokenAmount);
+ expect(expectedFilledTakerTokenAmount).to.be.bignumber.equal(logArgs.filledTakerTokenAmount);
+ expect(expectedFeeMPaid).to.be.bignumber.equal(logArgs.paidMakerFee);
+ expect(expectedFeeTPaid).to.be.bignumber.equal(logArgs.paidTakerFee);
+ expect(expectedTokens).to.be.equal(logArgs.tokens);
+ expect(order.params.orderHashHex).to.be.equal(logArgs.orderHash);
+ });
+
+ it('should throw when taker is specified and order is claimed by other', async () => {
+ order = await orderFactory.newSignedOrderAsync({
+ taker: feeRecipient,
+ makerTokenAmount: ZeroEx.toBaseUnitAmount(new BigNumber(100), 18),
+ takerTokenAmount: ZeroEx.toBaseUnitAmount(new BigNumber(200), 18),
+ });
+
+ return expect(exWrapper.fillOrderAsync(order, taker)).to.be.rejectedWith(constants.REVERT);
+ });
+
+ it('should throw if signature is invalid', async () => {
+ order = await orderFactory.newSignedOrderAsync({
+ makerTokenAmount: ZeroEx.toBaseUnitAmount(new BigNumber(10), 18),
+ });
+
+ order.params.r = ethUtil.bufferToHex(ethUtil.sha3('invalidR'));
+ order.params.s = ethUtil.bufferToHex(ethUtil.sha3('invalidS'));
+ return expect(exWrapper.fillOrderAsync(order, taker)).to.be.rejectedWith(constants.REVERT);
+ });
+
+ it('should throw if makerTokenAmount is 0', async () => {
+ order = await orderFactory.newSignedOrderAsync({
+ makerTokenAmount: new BigNumber(0),
+ });
+
+ return expect(exWrapper.fillOrderAsync(order, taker)).to.be.rejectedWith(constants.REVERT);
+ });
+
+ it('should throw if takerTokenAmount is 0', async () => {
+ order = await orderFactory.newSignedOrderAsync({
+ takerTokenAmount: new BigNumber(0),
+ });
+
+ return expect(exWrapper.fillOrderAsync(order, taker)).to.be.rejectedWith(constants.REVERT);
+ });
+
+ it('should throw if fillTakerTokenAmount is 0', async () => {
+ order = await orderFactory.newSignedOrderAsync();
+
+ return expect(
+ exWrapper.fillOrderAsync(order, taker, {
+ fillTakerTokenAmount: new BigNumber(0),
+ }),
+ ).to.be.rejectedWith(constants.REVERT);
+ });
+
+ it('should not change balances if maker balances are too low to fill order and \
shouldThrowOnInsufficientBalanceOrAllowance = false', async () => {
- order = await orderFactory.newSignedOrderAsync({
- makerTokenAmount: ZeroEx.toBaseUnitAmount(new BigNumber(100000), 18),
- });
+ order = await orderFactory.newSignedOrderAsync({
+ makerTokenAmount: ZeroEx.toBaseUnitAmount(new BigNumber(100000), 18),
+ });
- await exWrapper.fillOrderAsync(order, taker);
- const newBalances = await dmyBalances.getAsync();
- expect(newBalances).to.be.deep.equal(balances);
- });
+ await exWrapper.fillOrderAsync(order, taker);
+ const newBalances = await dmyBalances.getAsync();
+ expect(newBalances).to.be.deep.equal(balances);
+ });
- it('should throw if maker balances are too low to fill order and \
+ it('should throw if maker balances are too low to fill order and \
shouldThrowOnInsufficientBalanceOrAllowance = true', async () => {
- order = await orderFactory.newSignedOrderAsync({
- makerTokenAmount: ZeroEx.toBaseUnitAmount(new BigNumber(100000), 18),
- });
-
- return expect(
- exWrapper.fillOrderAsync(order, taker, {
- shouldThrowOnInsufficientBalanceOrAllowance: true,
- }),
- ).to.be.rejectedWith(constants.REVERT);
- });
-
- it('should not change balances if taker balances are too low to fill order and \
+ order = await orderFactory.newSignedOrderAsync({
+ makerTokenAmount: ZeroEx.toBaseUnitAmount(new BigNumber(100000), 18),
+ });
+
+ return expect(
+ exWrapper.fillOrderAsync(order, taker, {
+ shouldThrowOnInsufficientBalanceOrAllowance: true,
+ }),
+ ).to.be.rejectedWith(constants.REVERT);
+ });
+
+ it('should not change balances if taker balances are too low to fill order and \
shouldThrowOnInsufficientBalanceOrAllowance = false', async () => {
- order = await orderFactory.newSignedOrderAsync({
- takerTokenAmount: ZeroEx.toBaseUnitAmount(new BigNumber(100000), 18),
- });
+ order = await orderFactory.newSignedOrderAsync({
+ takerTokenAmount: ZeroEx.toBaseUnitAmount(new BigNumber(100000), 18),
+ });
- await exWrapper.fillOrderAsync(order, taker);
- const newBalances = await dmyBalances.getAsync();
- expect(newBalances).to.be.deep.equal(balances);
- });
+ await exWrapper.fillOrderAsync(order, taker);
+ const newBalances = await dmyBalances.getAsync();
+ expect(newBalances).to.be.deep.equal(balances);
+ });
- it('should throw if taker balances are too low to fill order and \
+ it('should throw if taker balances are too low to fill order and \
shouldThrowOnInsufficientBalanceOrAllowance = true', async () => {
- order = await orderFactory.newSignedOrderAsync({
- takerTokenAmount: ZeroEx.toBaseUnitAmount(new BigNumber(100000), 18),
- });
-
- return expect(
- exWrapper.fillOrderAsync(order, taker, {
- shouldThrowOnInsufficientBalanceOrAllowance: true,
- }),
- ).to.be.rejectedWith(constants.REVERT);
- });
-
- it('should not change balances if maker allowances are too low to fill order and \
+ order = await orderFactory.newSignedOrderAsync({
+ takerTokenAmount: ZeroEx.toBaseUnitAmount(new BigNumber(100000), 18),
+ });
+
+ return expect(
+ exWrapper.fillOrderAsync(order, taker, {
+ shouldThrowOnInsufficientBalanceOrAllowance: true,
+ }),
+ ).to.be.rejectedWith(constants.REVERT);
+ });
+
+ it('should not change balances if maker allowances are too low to fill order and \
shouldThrowOnInsufficientBalanceOrAllowance = false', async () => {
- await rep.approve(TokenTransferProxy.address, 0, { from: maker });
- await exWrapper.fillOrderAsync(order, taker);
- await rep.approve(TokenTransferProxy.address, INITIAL_ALLOWANCE, {
- from: maker,
- });
+ await rep.approve(TokenTransferProxy.address, 0, { from: maker });
+ await exWrapper.fillOrderAsync(order, taker);
+ await rep.approve(TokenTransferProxy.address, INITIAL_ALLOWANCE, {
+ from: maker,
+ });
- const newBalances = await dmyBalances.getAsync();
- expect(newBalances).to.be.deep.equal(balances);
- });
+ const newBalances = await dmyBalances.getAsync();
+ expect(newBalances).to.be.deep.equal(balances);
+ });
- it('should throw if maker allowances are too low to fill order and \
+ it('should throw if maker allowances are too low to fill order and \
shouldThrowOnInsufficientBalanceOrAllowance = true', async () => {
- await rep.approve(TokenTransferProxy.address, 0, { from: maker });
- expect(
- exWrapper.fillOrderAsync(order, taker, {
- shouldThrowOnInsufficientBalanceOrAllowance: true,
- }),
- ).to.be.rejectedWith(constants.REVERT);
- await rep.approve(TokenTransferProxy.address, INITIAL_ALLOWANCE, {
- from: maker,
- });
- });
-
- it('should not change balances if taker allowances are too low to fill order and \
+ await rep.approve(TokenTransferProxy.address, 0, { from: maker });
+ expect(
+ exWrapper.fillOrderAsync(order, taker, {
+ shouldThrowOnInsufficientBalanceOrAllowance: true,
+ }),
+ ).to.be.rejectedWith(constants.REVERT);
+ await rep.approve(TokenTransferProxy.address, INITIAL_ALLOWANCE, {
+ from: maker,
+ });
+ });
+
+ it('should not change balances if taker allowances are too low to fill order and \
shouldThrowOnInsufficientBalanceOrAllowance = false', async () => {
- await dgd.approve(TokenTransferProxy.address, 0, { from: taker });
- await exWrapper.fillOrderAsync(order, taker);
- await dgd.approve(TokenTransferProxy.address, INITIAL_ALLOWANCE, {
- from: taker,
- });
+ await dgd.approve(TokenTransferProxy.address, 0, { from: taker });
+ await exWrapper.fillOrderAsync(order, taker);
+ await dgd.approve(TokenTransferProxy.address, INITIAL_ALLOWANCE, {
+ from: taker,
+ });
- const newBalances = await dmyBalances.getAsync();
- expect(newBalances).to.be.deep.equal(balances);
- });
+ const newBalances = await dmyBalances.getAsync();
+ expect(newBalances).to.be.deep.equal(balances);
+ });
- it('should throw if taker allowances are too low to fill order and \
+ it('should throw if taker allowances are too low to fill order and \
shouldThrowOnInsufficientBalanceOrAllowance = true', async () => {
- await dgd.approve(TokenTransferProxy.address, 0, { from: taker });
- expect(
- exWrapper.fillOrderAsync(order, taker, {
- shouldThrowOnInsufficientBalanceOrAllowance: true,
- }),
- ).to.be.rejectedWith(constants.REVERT);
- await dgd.approve(TokenTransferProxy.address, INITIAL_ALLOWANCE, {
- from: taker,
- });
- });
-
- it('should not change balances if makerToken is ZRX, makerTokenAmount + makerFee > maker balance, \
+ await dgd.approve(TokenTransferProxy.address, 0, { from: taker });
+ expect(
+ exWrapper.fillOrderAsync(order, taker, {
+ shouldThrowOnInsufficientBalanceOrAllowance: true,
+ }),
+ ).to.be.rejectedWith(constants.REVERT);
+ await dgd.approve(TokenTransferProxy.address, INITIAL_ALLOWANCE, {
+ from: taker,
+ });
+ });
+
+ it('should not change balances if makerToken is ZRX, makerTokenAmount + makerFee > maker balance, \
and shouldThrowOnInsufficientBalanceOrAllowance = false', async () => {
- const makerZRXBalance = new BigNumber(balances[maker][zrx.address]);
- order = await orderFactory.newSignedOrderAsync({
- makerToken: zrx.address,
- makerTokenAmount: makerZRXBalance,
- makerFee: new BigNumber(1),
- });
- await exWrapper.fillOrderAsync(order, taker);
- const newBalances = await dmyBalances.getAsync();
- expect(newBalances).to.be.deep.equal(balances);
- });
-
- it('should not change balances if makerToken is ZRX, makerTokenAmount + makerFee > maker allowance, \
+ const makerZRXBalance = new BigNumber(balances[maker][zrx.address]);
+ order = await orderFactory.newSignedOrderAsync({
+ makerToken: zrx.address,
+ makerTokenAmount: makerZRXBalance,
+ makerFee: new BigNumber(1),
+ });
+ await exWrapper.fillOrderAsync(order, taker);
+ const newBalances = await dmyBalances.getAsync();
+ expect(newBalances).to.be.deep.equal(balances);
+ });
+
+ it('should not change balances if makerToken is ZRX, makerTokenAmount + makerFee > maker allowance, \
and shouldThrowOnInsufficientBalanceOrAllowance = false', async () => {
- const makerZRXAllowance = await zrx.allowance(maker, TokenTransferProxy.address);
- order = await orderFactory.newSignedOrderAsync({
- makerToken: zrx.address,
- makerTokenAmount: new BigNumber(makerZRXAllowance),
- makerFee: new BigNumber(1),
- });
- await exWrapper.fillOrderAsync(order, taker);
- const newBalances = await dmyBalances.getAsync();
- expect(newBalances).to.be.deep.equal(balances);
- });
-
- it('should not change balances if takerToken is ZRX, takerTokenAmount + takerFee > taker balance, \
+ const makerZRXAllowance = await zrx.allowance(maker, TokenTransferProxy.address);
+ order = await orderFactory.newSignedOrderAsync({
+ makerToken: zrx.address,
+ makerTokenAmount: new BigNumber(makerZRXAllowance),
+ makerFee: new BigNumber(1),
+ });
+ await exWrapper.fillOrderAsync(order, taker);
+ const newBalances = await dmyBalances.getAsync();
+ expect(newBalances).to.be.deep.equal(balances);
+ });
+
+ it('should not change balances if takerToken is ZRX, takerTokenAmount + takerFee > taker balance, \
and shouldThrowOnInsufficientBalanceOrAllowance = false', async () => {
- const takerZRXBalance = new BigNumber(balances[taker][zrx.address]);
- order = await orderFactory.newSignedOrderAsync({
- takerToken: zrx.address,
- takerTokenAmount: takerZRXBalance,
- takerFee: new BigNumber(1),
- });
- await exWrapper.fillOrderAsync(order, taker);
- const newBalances = await dmyBalances.getAsync();
- expect(newBalances).to.be.deep.equal(balances);
- });
-
- it('should not change balances if takerToken is ZRX, takerTokenAmount + takerFee > taker allowance, \
+ const takerZRXBalance = new BigNumber(balances[taker][zrx.address]);
+ order = await orderFactory.newSignedOrderAsync({
+ takerToken: zrx.address,
+ takerTokenAmount: takerZRXBalance,
+ takerFee: new BigNumber(1),
+ });
+ await exWrapper.fillOrderAsync(order, taker);
+ const newBalances = await dmyBalances.getAsync();
+ expect(newBalances).to.be.deep.equal(balances);
+ });
+
+ it('should not change balances if takerToken is ZRX, takerTokenAmount + takerFee > taker allowance, \
and shouldThrowOnInsufficientBalanceOrAllowance = false', async () => {
- const takerZRXAllowance = await zrx.allowance(taker, TokenTransferProxy.address);
- order = await orderFactory.newSignedOrderAsync({
- takerToken: zrx.address,
- takerTokenAmount: new BigNumber(takerZRXAllowance),
- takerFee: new BigNumber(1),
- });
- await exWrapper.fillOrderAsync(order, taker);
- const newBalances = await dmyBalances.getAsync();
- expect(newBalances).to.be.deep.equal(balances);
- });
-
- it('should throw if getBalance or getAllowance attempts to change state and \
+ const takerZRXAllowance = await zrx.allowance(taker, TokenTransferProxy.address);
+ order = await orderFactory.newSignedOrderAsync({
+ takerToken: zrx.address,
+ takerTokenAmount: new BigNumber(takerZRXAllowance),
+ takerFee: new BigNumber(1),
+ });
+ await exWrapper.fillOrderAsync(order, taker);
+ const newBalances = await dmyBalances.getAsync();
+ expect(newBalances).to.be.deep.equal(balances);
+ });
+
+ it('should throw if getBalance or getAllowance attempts to change state and \
shouldThrowOnInsufficientBalanceOrAllowance = false', async () => {
- const maliciousToken = await MaliciousToken.new();
- await maliciousToken.approve(TokenTransferProxy.address, INITIAL_ALLOWANCE, { from: taker });
-
- order = await orderFactory.newSignedOrderAsync({
- takerToken: maliciousToken.address,
- });
-
- return expect(
- exWrapper.fillOrderAsync(order, taker, {
- shouldThrowOnInsufficientBalanceOrAllowance: false,
- }),
- ).to.be.rejectedWith(constants.REVERT);
- });
-
- it('should not change balances if an order is expired', async () => {
- order = await orderFactory.newSignedOrderAsync({
- expirationTimestampInSec: new BigNumber(Math.floor((Date.now() - 10000) / 1000)),
- });
- await exWrapper.fillOrderAsync(order, taker);
-
- const newBalances = await dmyBalances.getAsync();
- expect(newBalances).to.be.deep.equal(balances);
- });
-
- it('should log an error event if an order is expired', async () => {
- order = await orderFactory.newSignedOrderAsync({
- expirationTimestampInSec: new BigNumber(Math.floor((Date.now() - 10000) / 1000)),
- });
-
- const res = await exWrapper.fillOrderAsync(order, taker);
- expect(res.logs).to.have.length(1);
- const errCode = res.logs[0].args.errorId.toNumber();
- expect(errCode).to.be.equal(ExchangeContractErrs.ERROR_ORDER_EXPIRED);
- });
-
- it('should log an error event if no value is filled', async () => {
- await exWrapper.fillOrderAsync(order, taker);
-
- const res = await exWrapper.fillOrderAsync(order, taker);
- expect(res.logs).to.have.length(1);
- const errCode = res.logs[0].args.errorId.toNumber();
- expect(errCode).to.be.equal(ExchangeContractErrs.ERROR_ORDER_FULLY_FILLED_OR_CANCELLED);
- });
- });
-
- describe('cancelOrder', () => {
- beforeEach(async () => {
- balances = await dmyBalances.getAsync();
- order = await orderFactory.newSignedOrderAsync();
- });
-
- it('should throw if not sent by maker', async () => {
- return expect(exWrapper.cancelOrderAsync(order, taker)).to.be.rejectedWith(constants.REVERT);
- });
-
- it('should throw if makerTokenAmount is 0', async () => {
- order = await orderFactory.newSignedOrderAsync({
- makerTokenAmount: new BigNumber(0),
- });
-
- return expect(exWrapper.cancelOrderAsync(order, maker)).to.be.rejectedWith(constants.REVERT);
- });
-
- it('should throw if takerTokenAmount is 0', async () => {
- order = await orderFactory.newSignedOrderAsync({
- takerTokenAmount: new BigNumber(0),
- });
-
- return expect(exWrapper.cancelOrderAsync(order, maker)).to.be.rejectedWith(constants.REVERT);
- });
-
- it('should throw if cancelTakerTokenAmount is 0', async () => {
- order = await orderFactory.newSignedOrderAsync();
-
- return expect(
- exWrapper.cancelOrderAsync(order, maker, {
- cancelTakerTokenAmount: new BigNumber(0),
- }),
- ).to.be.rejectedWith(constants.REVERT);
- });
-
- it('should be able to cancel a full order', async () => {
- await exWrapper.cancelOrderAsync(order, maker);
- await exWrapper.fillOrderAsync(order, taker, {
- fillTakerTokenAmount: order.params.takerTokenAmount.div(2),
- });
-
- const newBalances = await dmyBalances.getAsync();
- expect(newBalances).to.be.deep.equal(balances);
- });
-
- it('should be able to cancel part of an order', async () => {
- const cancelTakerTokenAmount = order.params.takerTokenAmount.div(2);
- await exWrapper.cancelOrderAsync(order, maker, {
- cancelTakerTokenAmount,
- });
-
- const res = await exWrapper.fillOrderAsync(order, taker, {
- fillTakerTokenAmount: order.params.takerTokenAmount,
- });
- expect(res.logs[0].args.filledTakerTokenAmount).to.be.bignumber.equal(
- order.params.takerTokenAmount.minus(cancelTakerTokenAmount),
- );
-
- const newBalances = await dmyBalances.getAsync();
- const cancelMakerTokenAmount = cancelTakerTokenAmount
- .times(order.params.makerTokenAmount)
- .dividedToIntegerBy(order.params.takerTokenAmount);
- const paidMakerFee = order.params.makerFee
- .times(cancelMakerTokenAmount)
- .dividedToIntegerBy(order.params.makerTokenAmount);
- const paidTakerFee = order.params.takerFee
- .times(cancelMakerTokenAmount)
- .dividedToIntegerBy(order.params.makerTokenAmount);
- expect(newBalances[maker][order.params.makerToken]).to.be.bignumber.equal(
- balances[maker][order.params.makerToken].minus(cancelMakerTokenAmount),
- );
- expect(newBalances[maker][order.params.takerToken]).to.be.bignumber.equal(
- balances[maker][order.params.takerToken].add(cancelTakerTokenAmount),
- );
- expect(newBalances[maker][zrx.address]).to.be.bignumber.equal(
- balances[maker][zrx.address].minus(paidMakerFee),
- );
- expect(newBalances[taker][order.params.takerToken]).to.be.bignumber.equal(
- balances[taker][order.params.takerToken].minus(cancelTakerTokenAmount),
- );
- expect(newBalances[taker][order.params.makerToken]).to.be.bignumber.equal(
- balances[taker][order.params.makerToken].add(cancelMakerTokenAmount),
- );
- expect(newBalances[taker][zrx.address]).to.be.bignumber.equal(
- balances[taker][zrx.address].minus(paidTakerFee),
- );
- expect(newBalances[feeRecipient][zrx.address]).to.be.bignumber.equal(
- balances[feeRecipient][zrx.address].add(paidMakerFee.add(paidTakerFee)),
- );
- });
-
- it('should log 1 event with correct arguments', async () => {
- const divisor = 2;
- const res = await exWrapper.cancelOrderAsync(order, maker, {
- cancelTakerTokenAmount: order.params.takerTokenAmount.div(divisor),
- });
- expect(res.logs).to.have.length(1);
-
- const logArgs = res.logs[0].args;
- const expectedCancelledMakerTokenAmount = order.params.makerTokenAmount.div(divisor);
- const expectedCancelledTakerTokenAmount = order.params.takerTokenAmount.div(divisor);
- const tokensHashBuff = crypto.solSHA3([order.params.makerToken, order.params.takerToken]);
- const expectedTokens = ethUtil.bufferToHex(tokensHashBuff);
-
- expect(order.params.maker).to.be.equal(logArgs.maker);
- expect(order.params.feeRecipient).to.be.equal(logArgs.feeRecipient);
- expect(order.params.makerToken).to.be.equal(logArgs.makerToken);
- expect(order.params.takerToken).to.be.equal(logArgs.takerToken);
- expect(expectedCancelledMakerTokenAmount).to.be.bignumber.equal(logArgs.cancelledMakerTokenAmount);
- expect(expectedCancelledTakerTokenAmount).to.be.bignumber.equal(logArgs.cancelledTakerTokenAmount);
- expect(expectedTokens).to.be.equal(logArgs.tokens);
- expect(order.params.orderHashHex).to.be.equal(logArgs.orderHash);
- });
-
- it('should not log events if no value is cancelled', async () => {
- await exWrapper.cancelOrderAsync(order, maker);
-
- const res = await exWrapper.cancelOrderAsync(order, maker);
- expect(res.logs).to.have.length(1);
- const errCode = res.logs[0].args.errorId.toNumber();
- expect(errCode).to.be.equal(ExchangeContractErrs.ERROR_ORDER_FULLY_FILLED_OR_CANCELLED);
- });
-
- it('should not log events if order is expired', async () => {
- order = await orderFactory.newSignedOrderAsync({
- expirationTimestampInSec: new BigNumber(Math.floor((Date.now() - 10000) / 1000)),
- });
-
- const res = await exWrapper.cancelOrderAsync(order, maker);
- expect(res.logs).to.have.length(1);
- const errCode = res.logs[0].args.errorId.toNumber();
- expect(errCode).to.be.equal(ExchangeContractErrs.ERROR_ORDER_EXPIRED);
- });
- });
+ const maliciousToken = await MaliciousToken.new();
+ await maliciousToken.approve(TokenTransferProxy.address, INITIAL_ALLOWANCE, { from: taker });
+
+ order = await orderFactory.newSignedOrderAsync({
+ takerToken: maliciousToken.address,
+ });
+
+ return expect(
+ exWrapper.fillOrderAsync(order, taker, {
+ shouldThrowOnInsufficientBalanceOrAllowance: false,
+ }),
+ ).to.be.rejectedWith(constants.REVERT);
+ });
+
+ it('should not change balances if an order is expired', async () => {
+ order = await orderFactory.newSignedOrderAsync({
+ expirationTimestampInSec: new BigNumber(Math.floor((Date.now() - 10000) / 1000)),
+ });
+ await exWrapper.fillOrderAsync(order, taker);
+
+ const newBalances = await dmyBalances.getAsync();
+ expect(newBalances).to.be.deep.equal(balances);
+ });
+
+ it('should log an error event if an order is expired', async () => {
+ order = await orderFactory.newSignedOrderAsync({
+ expirationTimestampInSec: new BigNumber(Math.floor((Date.now() - 10000) / 1000)),
+ });
+
+ const res = await exWrapper.fillOrderAsync(order, taker);
+ expect(res.logs).to.have.length(1);
+ const errCode = res.logs[0].args.errorId.toNumber();
+ expect(errCode).to.be.equal(ExchangeContractErrs.ERROR_ORDER_EXPIRED);
+ });
+
+ it('should log an error event if no value is filled', async () => {
+ await exWrapper.fillOrderAsync(order, taker);
+
+ const res = await exWrapper.fillOrderAsync(order, taker);
+ expect(res.logs).to.have.length(1);
+ const errCode = res.logs[0].args.errorId.toNumber();
+ expect(errCode).to.be.equal(ExchangeContractErrs.ERROR_ORDER_FULLY_FILLED_OR_CANCELLED);
+ });
+ });
+
+ describe('cancelOrder', () => {
+ beforeEach(async () => {
+ balances = await dmyBalances.getAsync();
+ order = await orderFactory.newSignedOrderAsync();
+ });
+
+ it('should throw if not sent by maker', async () => {
+ return expect(exWrapper.cancelOrderAsync(order, taker)).to.be.rejectedWith(constants.REVERT);
+ });
+
+ it('should throw if makerTokenAmount is 0', async () => {
+ order = await orderFactory.newSignedOrderAsync({
+ makerTokenAmount: new BigNumber(0),
+ });
+
+ return expect(exWrapper.cancelOrderAsync(order, maker)).to.be.rejectedWith(constants.REVERT);
+ });
+
+ it('should throw if takerTokenAmount is 0', async () => {
+ order = await orderFactory.newSignedOrderAsync({
+ takerTokenAmount: new BigNumber(0),
+ });
+
+ return expect(exWrapper.cancelOrderAsync(order, maker)).to.be.rejectedWith(constants.REVERT);
+ });
+
+ it('should throw if cancelTakerTokenAmount is 0', async () => {
+ order = await orderFactory.newSignedOrderAsync();
+
+ return expect(
+ exWrapper.cancelOrderAsync(order, maker, {
+ cancelTakerTokenAmount: new BigNumber(0),
+ }),
+ ).to.be.rejectedWith(constants.REVERT);
+ });
+
+ it('should be able to cancel a full order', async () => {
+ await exWrapper.cancelOrderAsync(order, maker);
+ await exWrapper.fillOrderAsync(order, taker, {
+ fillTakerTokenAmount: order.params.takerTokenAmount.div(2),
+ });
+
+ const newBalances = await dmyBalances.getAsync();
+ expect(newBalances).to.be.deep.equal(balances);
+ });
+
+ it('should be able to cancel part of an order', async () => {
+ const cancelTakerTokenAmount = order.params.takerTokenAmount.div(2);
+ await exWrapper.cancelOrderAsync(order, maker, {
+ cancelTakerTokenAmount,
+ });
+
+ const res = await exWrapper.fillOrderAsync(order, taker, {
+ fillTakerTokenAmount: order.params.takerTokenAmount,
+ });
+ expect(res.logs[0].args.filledTakerTokenAmount).to.be.bignumber.equal(
+ order.params.takerTokenAmount.minus(cancelTakerTokenAmount),
+ );
+
+ const newBalances = await dmyBalances.getAsync();
+ const cancelMakerTokenAmount = cancelTakerTokenAmount
+ .times(order.params.makerTokenAmount)
+ .dividedToIntegerBy(order.params.takerTokenAmount);
+ const paidMakerFee = order.params.makerFee
+ .times(cancelMakerTokenAmount)
+ .dividedToIntegerBy(order.params.makerTokenAmount);
+ const paidTakerFee = order.params.takerFee
+ .times(cancelMakerTokenAmount)
+ .dividedToIntegerBy(order.params.makerTokenAmount);
+ expect(newBalances[maker][order.params.makerToken]).to.be.bignumber.equal(
+ balances[maker][order.params.makerToken].minus(cancelMakerTokenAmount),
+ );
+ expect(newBalances[maker][order.params.takerToken]).to.be.bignumber.equal(
+ balances[maker][order.params.takerToken].add(cancelTakerTokenAmount),
+ );
+ expect(newBalances[maker][zrx.address]).to.be.bignumber.equal(
+ balances[maker][zrx.address].minus(paidMakerFee),
+ );
+ expect(newBalances[taker][order.params.takerToken]).to.be.bignumber.equal(
+ balances[taker][order.params.takerToken].minus(cancelTakerTokenAmount),
+ );
+ expect(newBalances[taker][order.params.makerToken]).to.be.bignumber.equal(
+ balances[taker][order.params.makerToken].add(cancelMakerTokenAmount),
+ );
+ expect(newBalances[taker][zrx.address]).to.be.bignumber.equal(
+ balances[taker][zrx.address].minus(paidTakerFee),
+ );
+ expect(newBalances[feeRecipient][zrx.address]).to.be.bignumber.equal(
+ balances[feeRecipient][zrx.address].add(paidMakerFee.add(paidTakerFee)),
+ );
+ });
+
+ it('should log 1 event with correct arguments', async () => {
+ const divisor = 2;
+ const res = await exWrapper.cancelOrderAsync(order, maker, {
+ cancelTakerTokenAmount: order.params.takerTokenAmount.div(divisor),
+ });
+ expect(res.logs).to.have.length(1);
+
+ const logArgs = res.logs[0].args;
+ const expectedCancelledMakerTokenAmount = order.params.makerTokenAmount.div(divisor);
+ const expectedCancelledTakerTokenAmount = order.params.takerTokenAmount.div(divisor);
+ const tokensHashBuff = crypto.solSHA3([order.params.makerToken, order.params.takerToken]);
+ const expectedTokens = ethUtil.bufferToHex(tokensHashBuff);
+
+ expect(order.params.maker).to.be.equal(logArgs.maker);
+ expect(order.params.feeRecipient).to.be.equal(logArgs.feeRecipient);
+ expect(order.params.makerToken).to.be.equal(logArgs.makerToken);
+ expect(order.params.takerToken).to.be.equal(logArgs.takerToken);
+ expect(expectedCancelledMakerTokenAmount).to.be.bignumber.equal(logArgs.cancelledMakerTokenAmount);
+ expect(expectedCancelledTakerTokenAmount).to.be.bignumber.equal(logArgs.cancelledTakerTokenAmount);
+ expect(expectedTokens).to.be.equal(logArgs.tokens);
+ expect(order.params.orderHashHex).to.be.equal(logArgs.orderHash);
+ });
+
+ it('should not log events if no value is cancelled', async () => {
+ await exWrapper.cancelOrderAsync(order, maker);
+
+ const res = await exWrapper.cancelOrderAsync(order, maker);
+ expect(res.logs).to.have.length(1);
+ const errCode = res.logs[0].args.errorId.toNumber();
+ expect(errCode).to.be.equal(ExchangeContractErrs.ERROR_ORDER_FULLY_FILLED_OR_CANCELLED);
+ });
+
+ it('should not log events if order is expired', async () => {
+ order = await orderFactory.newSignedOrderAsync({
+ expirationTimestampInSec: new BigNumber(Math.floor((Date.now() - 10000) / 1000)),
+ });
+
+ const res = await exWrapper.cancelOrderAsync(order, maker);
+ expect(res.logs).to.have.length(1);
+ const errCode = res.logs[0].args.errorId.toNumber();
+ expect(errCode).to.be.equal(ExchangeContractErrs.ERROR_ORDER_EXPIRED);
+ });
+ });
}); // tslint:disable-line:max-file-line-count
diff --git a/packages/contracts/test/ts/exchange/helpers.ts b/packages/contracts/test/ts/exchange/helpers.ts
index 993b4c7d5..95f68e419 100644
--- a/packages/contracts/test/ts/exchange/helpers.ts
+++ b/packages/contracts/test/ts/exchange/helpers.ts
@@ -15,153 +15,153 @@ const expect = chai.expect;
const { Exchange, TokenRegistry } = new Artifacts(artifacts);
contract('Exchange', (accounts: string[]) => {
- const maker = accounts[0];
- const feeRecipient = accounts[1] || accounts[accounts.length - 1];
-
- let order: Order;
- let exchangeWrapper: ExchangeWrapper;
- let orderFactory: OrderFactory;
-
- before(async () => {
- const [tokenRegistry, exchange] = await Promise.all([TokenRegistry.deployed(), Exchange.deployed()]);
- exchangeWrapper = new ExchangeWrapper(exchange);
- const [repAddress, dgdAddress] = await Promise.all([
- tokenRegistry.getTokenAddressBySymbol('REP'),
- tokenRegistry.getTokenAddressBySymbol('DGD'),
- ]);
- const defaultOrderParams = {
- exchangeContractAddress: Exchange.address,
- maker,
- feeRecipient,
- makerToken: repAddress,
- takerToken: dgdAddress,
- makerTokenAmount: ZeroEx.toBaseUnitAmount(new BigNumber(100), 18),
- takerTokenAmount: ZeroEx.toBaseUnitAmount(new BigNumber(200), 18),
- makerFee: ZeroEx.toBaseUnitAmount(new BigNumber(1), 18),
- takerFee: ZeroEx.toBaseUnitAmount(new BigNumber(1), 18),
- };
- orderFactory = new OrderFactory(defaultOrderParams);
- });
-
- beforeEach(async () => {
- order = await orderFactory.newSignedOrderAsync();
- });
-
- describe('getOrderHash', () => {
- it('should output the correct orderHash', async () => {
- const orderHashHex = await exchangeWrapper.getOrderHashAsync(order);
- expect(order.params.orderHashHex).to.be.equal(orderHashHex);
- });
- });
-
- describe('isValidSignature', () => {
- beforeEach(async () => {
- order = await orderFactory.newSignedOrderAsync();
- });
-
- it('should return true with a valid signature', async () => {
- const success = await exchangeWrapper.isValidSignatureAsync(order);
- const isValidSignature = order.isValidSignature();
- expect(isValidSignature).to.be.true();
- expect(success).to.be.true();
- });
-
- it('should return false with an invalid signature', async () => {
- order.params.r = ethUtil.bufferToHex(ethUtil.sha3('invalidR'));
- order.params.s = ethUtil.bufferToHex(ethUtil.sha3('invalidS'));
- const success = await exchangeWrapper.isValidSignatureAsync(order);
- expect(order.isValidSignature()).to.be.false();
- expect(success).to.be.false();
- });
- });
-
- describe('isRoundingError', () => {
- it('should return false if there is a rounding error of 0.1%', async () => {
- const numerator = new BigNumber(20);
- const denominator = new BigNumber(999);
- const target = new BigNumber(50);
- // rounding error = ((20*50/999) - floor(20*50/999)) / (20*50/999) = 0.1%
- const isRoundingError = await exchangeWrapper.isRoundingErrorAsync(numerator, denominator, target);
- expect(isRoundingError).to.be.false();
- });
-
- it('should return false if there is a rounding of 0.09%', async () => {
- const numerator = new BigNumber(20);
- const denominator = new BigNumber(9991);
- const target = new BigNumber(500);
- // rounding error = ((20*500/9991) - floor(20*500/9991)) / (20*500/9991) = 0.09%
- const isRoundingError = await exchangeWrapper.isRoundingErrorAsync(numerator, denominator, target);
- expect(isRoundingError).to.be.false();
- });
-
- it('should return true if there is a rounding error of 0.11%', async () => {
- const numerator = new BigNumber(20);
- const denominator = new BigNumber(9989);
- const target = new BigNumber(500);
- // rounding error = ((20*500/9989) - floor(20*500/9989)) / (20*500/9989) = 0.011%
- const isRoundingError = await exchangeWrapper.isRoundingErrorAsync(numerator, denominator, target);
- expect(isRoundingError).to.be.true();
- });
-
- it('should return true if there is a rounding error > 0.1%', async () => {
- const numerator = new BigNumber(3);
- const denominator = new BigNumber(7);
- const target = new BigNumber(10);
- // rounding error = ((3*10/7) - floor(3*10/7)) / (3*10/7) = 6.67%
- const isRoundingError = await exchangeWrapper.isRoundingErrorAsync(numerator, denominator, target);
- expect(isRoundingError).to.be.true();
- });
-
- it('should return false when there is no rounding error', async () => {
- const numerator = new BigNumber(1);
- const denominator = new BigNumber(2);
- const target = new BigNumber(10);
-
- const isRoundingError = await exchangeWrapper.isRoundingErrorAsync(numerator, denominator, target);
- expect(isRoundingError).to.be.false();
- });
-
- it('should return false when there is rounding error <= 0.1%', async () => {
- // randomly generated numbers
- const numerator = new BigNumber(76564);
- const denominator = new BigNumber(676373677);
- const target = new BigNumber(105762562);
- // rounding error = ((76564*105762562/676373677) - floor(76564*105762562/676373677)) /
- // (76564*105762562/676373677) = 0.0007%
- const isRoundingError = await exchangeWrapper.isRoundingErrorAsync(numerator, denominator, target);
- expect(isRoundingError).to.be.false();
- });
- });
-
- describe('getPartialAmount', () => {
- it('should return the numerator/denominator*target', async () => {
- const numerator = new BigNumber(1);
- const denominator = new BigNumber(2);
- const target = new BigNumber(10);
-
- const partialAmount = await exchangeWrapper.getPartialAmountAsync(numerator, denominator, target);
- const expectedPartialAmount = 5;
- expect(partialAmount).to.be.bignumber.equal(expectedPartialAmount);
- });
-
- it('should round down', async () => {
- const numerator = new BigNumber(2);
- const denominator = new BigNumber(3);
- const target = new BigNumber(10);
-
- const partialAmount = await exchangeWrapper.getPartialAmountAsync(numerator, denominator, target);
- const expectedPartialAmount = 6;
- expect(partialAmount).to.be.bignumber.equal(expectedPartialAmount);
- });
-
- it('should round .5 down', async () => {
- const numerator = new BigNumber(1);
- const denominator = new BigNumber(20);
- const target = new BigNumber(10);
-
- const partialAmount = await exchangeWrapper.getPartialAmountAsync(numerator, denominator, target);
- const expectedPartialAmount = 0;
- expect(partialAmount).to.be.bignumber.equal(expectedPartialAmount);
- });
- });
+ const maker = accounts[0];
+ const feeRecipient = accounts[1] || accounts[accounts.length - 1];
+
+ let order: Order;
+ let exchangeWrapper: ExchangeWrapper;
+ let orderFactory: OrderFactory;
+
+ before(async () => {
+ const [tokenRegistry, exchange] = await Promise.all([TokenRegistry.deployed(), Exchange.deployed()]);
+ exchangeWrapper = new ExchangeWrapper(exchange);
+ const [repAddress, dgdAddress] = await Promise.all([
+ tokenRegistry.getTokenAddressBySymbol('REP'),
+ tokenRegistry.getTokenAddressBySymbol('DGD'),
+ ]);
+ const defaultOrderParams = {
+ exchangeContractAddress: Exchange.address,
+ maker,
+ feeRecipient,
+ makerToken: repAddress,
+ takerToken: dgdAddress,
+ makerTokenAmount: ZeroEx.toBaseUnitAmount(new BigNumber(100), 18),
+ takerTokenAmount: ZeroEx.toBaseUnitAmount(new BigNumber(200), 18),
+ makerFee: ZeroEx.toBaseUnitAmount(new BigNumber(1), 18),
+ takerFee: ZeroEx.toBaseUnitAmount(new BigNumber(1), 18),
+ };
+ orderFactory = new OrderFactory(defaultOrderParams);
+ });
+
+ beforeEach(async () => {
+ order = await orderFactory.newSignedOrderAsync();
+ });
+
+ describe('getOrderHash', () => {
+ it('should output the correct orderHash', async () => {
+ const orderHashHex = await exchangeWrapper.getOrderHashAsync(order);
+ expect(order.params.orderHashHex).to.be.equal(orderHashHex);
+ });
+ });
+
+ describe('isValidSignature', () => {
+ beforeEach(async () => {
+ order = await orderFactory.newSignedOrderAsync();
+ });
+
+ it('should return true with a valid signature', async () => {
+ const success = await exchangeWrapper.isValidSignatureAsync(order);
+ const isValidSignature = order.isValidSignature();
+ expect(isValidSignature).to.be.true();
+ expect(success).to.be.true();
+ });
+
+ it('should return false with an invalid signature', async () => {
+ order.params.r = ethUtil.bufferToHex(ethUtil.sha3('invalidR'));
+ order.params.s = ethUtil.bufferToHex(ethUtil.sha3('invalidS'));
+ const success = await exchangeWrapper.isValidSignatureAsync(order);
+ expect(order.isValidSignature()).to.be.false();
+ expect(success).to.be.false();
+ });
+ });
+
+ describe('isRoundingError', () => {
+ it('should return false if there is a rounding error of 0.1%', async () => {
+ const numerator = new BigNumber(20);
+ const denominator = new BigNumber(999);
+ const target = new BigNumber(50);
+ // rounding error = ((20*50/999) - floor(20*50/999)) / (20*50/999) = 0.1%
+ const isRoundingError = await exchangeWrapper.isRoundingErrorAsync(numerator, denominator, target);
+ expect(isRoundingError).to.be.false();
+ });
+
+ it('should return false if there is a rounding of 0.09%', async () => {
+ const numerator = new BigNumber(20);
+ const denominator = new BigNumber(9991);
+ const target = new BigNumber(500);
+ // rounding error = ((20*500/9991) - floor(20*500/9991)) / (20*500/9991) = 0.09%
+ const isRoundingError = await exchangeWrapper.isRoundingErrorAsync(numerator, denominator, target);
+ expect(isRoundingError).to.be.false();
+ });
+
+ it('should return true if there is a rounding error of 0.11%', async () => {
+ const numerator = new BigNumber(20);
+ const denominator = new BigNumber(9989);
+ const target = new BigNumber(500);
+ // rounding error = ((20*500/9989) - floor(20*500/9989)) / (20*500/9989) = 0.011%
+ const isRoundingError = await exchangeWrapper.isRoundingErrorAsync(numerator, denominator, target);
+ expect(isRoundingError).to.be.true();
+ });
+
+ it('should return true if there is a rounding error > 0.1%', async () => {
+ const numerator = new BigNumber(3);
+ const denominator = new BigNumber(7);
+ const target = new BigNumber(10);
+ // rounding error = ((3*10/7) - floor(3*10/7)) / (3*10/7) = 6.67%
+ const isRoundingError = await exchangeWrapper.isRoundingErrorAsync(numerator, denominator, target);
+ expect(isRoundingError).to.be.true();
+ });
+
+ it('should return false when there is no rounding error', async () => {
+ const numerator = new BigNumber(1);
+ const denominator = new BigNumber(2);
+ const target = new BigNumber(10);
+
+ const isRoundingError = await exchangeWrapper.isRoundingErrorAsync(numerator, denominator, target);
+ expect(isRoundingError).to.be.false();
+ });
+
+ it('should return false when there is rounding error <= 0.1%', async () => {
+ // randomly generated numbers
+ const numerator = new BigNumber(76564);
+ const denominator = new BigNumber(676373677);
+ const target = new BigNumber(105762562);
+ // rounding error = ((76564*105762562/676373677) - floor(76564*105762562/676373677)) /
+ // (76564*105762562/676373677) = 0.0007%
+ const isRoundingError = await exchangeWrapper.isRoundingErrorAsync(numerator, denominator, target);
+ expect(isRoundingError).to.be.false();
+ });
+ });
+
+ describe('getPartialAmount', () => {
+ it('should return the numerator/denominator*target', async () => {
+ const numerator = new BigNumber(1);
+ const denominator = new BigNumber(2);
+ const target = new BigNumber(10);
+
+ const partialAmount = await exchangeWrapper.getPartialAmountAsync(numerator, denominator, target);
+ const expectedPartialAmount = 5;
+ expect(partialAmount).to.be.bignumber.equal(expectedPartialAmount);
+ });
+
+ it('should round down', async () => {
+ const numerator = new BigNumber(2);
+ const denominator = new BigNumber(3);
+ const target = new BigNumber(10);
+
+ const partialAmount = await exchangeWrapper.getPartialAmountAsync(numerator, denominator, target);
+ const expectedPartialAmount = 6;
+ expect(partialAmount).to.be.bignumber.equal(expectedPartialAmount);
+ });
+
+ it('should round .5 down', async () => {
+ const numerator = new BigNumber(1);
+ const denominator = new BigNumber(20);
+ const target = new BigNumber(10);
+
+ const partialAmount = await exchangeWrapper.getPartialAmountAsync(numerator, denominator, target);
+ const expectedPartialAmount = 0;
+ expect(partialAmount).to.be.bignumber.equal(expectedPartialAmount);
+ });
+ });
});
diff --git a/packages/contracts/test/ts/exchange/wrapper.ts b/packages/contracts/test/ts/exchange/wrapper.ts
index e0243fc1b..e69e08bcf 100644
--- a/packages/contracts/test/ts/exchange/wrapper.ts
+++ b/packages/contracts/test/ts/exchange/wrapper.ts
@@ -17,332 +17,332 @@ const expect = chai.expect;
const { Exchange, TokenTransferProxy, DummyToken, TokenRegistry } = new Artifacts(artifacts);
contract('Exchange', (accounts: string[]) => {
- const maker = accounts[0];
- const tokenOwner = accounts[0];
- const taker = accounts[1] || accounts[accounts.length - 1];
- const feeRecipient = accounts[2] || accounts[accounts.length - 1];
-
- const INIT_BAL = ZeroEx.toBaseUnitAmount(new BigNumber(10000), 18);
- const INIT_ALLOW = ZeroEx.toBaseUnitAmount(new BigNumber(10000), 18);
-
- let rep: ContractInstance;
- let dgd: ContractInstance;
- let zrx: ContractInstance;
- let exchange: ContractInstance;
- let tokenRegistry: ContractInstance;
-
- let balances: BalancesByOwner;
-
- let exWrapper: ExchangeWrapper;
- let dmyBalances: Balances;
- let orderFactory: OrderFactory;
-
- before(async () => {
- [tokenRegistry, exchange] = await Promise.all([TokenRegistry.deployed(), Exchange.deployed()]);
- exWrapper = new ExchangeWrapper(exchange);
- const [repAddress, dgdAddress, zrxAddress] = await Promise.all([
- tokenRegistry.getTokenAddressBySymbol('REP'),
- tokenRegistry.getTokenAddressBySymbol('DGD'),
- tokenRegistry.getTokenAddressBySymbol('ZRX'),
- ]);
-
- const defaultOrderParams = {
- exchangeContractAddress: Exchange.address,
- maker,
- feeRecipient,
- makerToken: repAddress,
- takerToken: dgdAddress,
- makerTokenAmount: ZeroEx.toBaseUnitAmount(new BigNumber(100), 18),
- takerTokenAmount: ZeroEx.toBaseUnitAmount(new BigNumber(200), 18),
- makerFee: ZeroEx.toBaseUnitAmount(new BigNumber(1), 18),
- takerFee: ZeroEx.toBaseUnitAmount(new BigNumber(1), 18),
- };
- orderFactory = new OrderFactory(defaultOrderParams);
-
- [rep, dgd, zrx] = await Promise.all([
- DummyToken.at(repAddress),
- DummyToken.at(dgdAddress),
- DummyToken.at(zrxAddress),
- ]);
- dmyBalances = new Balances([rep, dgd, zrx], [maker, taker, feeRecipient]);
- await Promise.all([
- rep.approve(TokenTransferProxy.address, INIT_ALLOW, { from: maker }),
- rep.approve(TokenTransferProxy.address, INIT_ALLOW, { from: taker }),
- rep.setBalance(maker, INIT_BAL, { from: tokenOwner }),
- rep.setBalance(taker, INIT_BAL, { from: tokenOwner }),
- dgd.approve(TokenTransferProxy.address, INIT_ALLOW, { from: maker }),
- dgd.approve(TokenTransferProxy.address, INIT_ALLOW, { from: taker }),
- dgd.setBalance(maker, INIT_BAL, { from: tokenOwner }),
- dgd.setBalance(taker, INIT_BAL, { from: tokenOwner }),
- zrx.approve(TokenTransferProxy.address, INIT_ALLOW, { from: maker }),
- zrx.approve(TokenTransferProxy.address, INIT_ALLOW, { from: taker }),
- zrx.setBalance(maker, INIT_BAL, { from: tokenOwner }),
- zrx.setBalance(taker, INIT_BAL, { from: tokenOwner }),
- ]);
- });
-
- describe('fillOrKillOrder', () => {
- beforeEach(async () => {
- balances = await dmyBalances.getAsync();
- });
-
- it('should transfer the correct amounts', async () => {
- const order = await orderFactory.newSignedOrderAsync({
- makerTokenAmount: ZeroEx.toBaseUnitAmount(new BigNumber(100), 18),
- takerTokenAmount: ZeroEx.toBaseUnitAmount(new BigNumber(200), 18),
- });
- const fillTakerTokenAmount = order.params.takerTokenAmount.div(2);
- await exWrapper.fillOrKillOrderAsync(order, taker, {
- fillTakerTokenAmount,
- });
-
- const newBalances = await dmyBalances.getAsync();
-
- const fillMakerTokenAmount = fillTakerTokenAmount
- .times(order.params.makerTokenAmount)
- .dividedToIntegerBy(order.params.takerTokenAmount);
- const makerFee = order.params.makerFee
- .times(fillMakerTokenAmount)
- .dividedToIntegerBy(order.params.makerTokenAmount);
- const takerFee = order.params.takerFee
- .times(fillMakerTokenAmount)
- .dividedToIntegerBy(order.params.makerTokenAmount);
- expect(newBalances[maker][order.params.makerToken]).to.be.bignumber.equal(
- balances[maker][order.params.makerToken].minus(fillMakerTokenAmount),
- );
- expect(newBalances[maker][order.params.takerToken]).to.be.bignumber.equal(
- balances[maker][order.params.takerToken].add(fillTakerTokenAmount),
- );
- expect(newBalances[maker][zrx.address]).to.be.bignumber.equal(balances[maker][zrx.address].minus(makerFee));
- expect(newBalances[taker][order.params.takerToken]).to.be.bignumber.equal(
- balances[taker][order.params.takerToken].minus(fillTakerTokenAmount),
- );
- expect(newBalances[taker][order.params.makerToken]).to.be.bignumber.equal(
- balances[taker][order.params.makerToken].add(fillMakerTokenAmount),
- );
- expect(newBalances[taker][zrx.address]).to.be.bignumber.equal(balances[taker][zrx.address].minus(takerFee));
- expect(newBalances[feeRecipient][zrx.address]).to.be.bignumber.equal(
- balances[feeRecipient][zrx.address].add(makerFee.add(takerFee)),
- );
- });
-
- it('should throw if an order is expired', async () => {
- const order = await orderFactory.newSignedOrderAsync({
- expirationTimestampInSec: new BigNumber(Math.floor((Date.now() - 10000) / 1000)),
- });
-
- return expect(exWrapper.fillOrKillOrderAsync(order, taker)).to.be.rejectedWith(constants.REVERT);
- });
-
- it('should throw if entire fillTakerTokenAmount not filled', async () => {
- const order = await orderFactory.newSignedOrderAsync();
-
- const from = taker;
- await exWrapper.fillOrderAsync(order, from, {
- fillTakerTokenAmount: order.params.takerTokenAmount.div(2),
- });
-
- return expect(exWrapper.fillOrKillOrderAsync(order, taker)).to.be.rejectedWith(constants.REVERT);
- });
- });
-
- describe('batch functions', () => {
- let orders: Order[];
- beforeEach(async () => {
- orders = await Promise.all([
- orderFactory.newSignedOrderAsync(),
- orderFactory.newSignedOrderAsync(),
- orderFactory.newSignedOrderAsync(),
- ]);
- balances = await dmyBalances.getAsync();
- });
-
- describe('batchFillOrders', () => {
- it('should transfer the correct amounts', async () => {
- const fillTakerTokenAmounts: BigNumber[] = [];
- const makerToken = rep.address;
- const takerToken = dgd.address;
- orders.forEach(order => {
- const fillTakerTokenAmount = order.params.takerTokenAmount.div(2);
- const fillMakerTokenAmount = fillTakerTokenAmount
- .times(order.params.makerTokenAmount)
- .dividedToIntegerBy(order.params.takerTokenAmount);
- const makerFee = order.params.makerFee
- .times(fillMakerTokenAmount)
- .dividedToIntegerBy(order.params.makerTokenAmount);
- const takerFee = order.params.takerFee
- .times(fillMakerTokenAmount)
- .dividedToIntegerBy(order.params.makerTokenAmount);
- fillTakerTokenAmounts.push(fillTakerTokenAmount);
- balances[maker][makerToken] = balances[maker][makerToken].minus(fillMakerTokenAmount);
- balances[maker][takerToken] = balances[maker][takerToken].add(fillTakerTokenAmount);
- balances[maker][zrx.address] = balances[maker][zrx.address].minus(makerFee);
- balances[taker][makerToken] = balances[taker][makerToken].add(fillMakerTokenAmount);
- balances[taker][takerToken] = balances[taker][takerToken].minus(fillTakerTokenAmount);
- balances[taker][zrx.address] = balances[taker][zrx.address].minus(takerFee);
- balances[feeRecipient][zrx.address] = balances[feeRecipient][zrx.address].add(
- makerFee.add(takerFee),
- );
- });
-
- await exWrapper.batchFillOrdersAsync(orders, taker, {
- fillTakerTokenAmounts,
- });
-
- const newBalances = await dmyBalances.getAsync();
- expect(newBalances).to.be.deep.equal(balances);
- });
- });
-
- describe('batchFillOrKillOrders', () => {
- it('should transfer the correct amounts', async () => {
- const fillTakerTokenAmounts: BigNumber[] = [];
- const makerToken = rep.address;
- const takerToken = dgd.address;
- orders.forEach(order => {
- const fillTakerTokenAmount = order.params.takerTokenAmount.div(2);
- const fillMakerTokenAmount = fillTakerTokenAmount
- .times(order.params.makerTokenAmount)
- .dividedToIntegerBy(order.params.takerTokenAmount);
- const makerFee = order.params.makerFee
- .times(fillMakerTokenAmount)
- .dividedToIntegerBy(order.params.makerTokenAmount);
- const takerFee = order.params.takerFee
- .times(fillMakerTokenAmount)
- .dividedToIntegerBy(order.params.makerTokenAmount);
- fillTakerTokenAmounts.push(fillTakerTokenAmount);
- balances[maker][makerToken] = balances[maker][makerToken].minus(fillMakerTokenAmount);
- balances[maker][takerToken] = balances[maker][takerToken].add(fillTakerTokenAmount);
- balances[maker][zrx.address] = balances[maker][zrx.address].minus(makerFee);
- balances[taker][makerToken] = balances[taker][makerToken].add(fillMakerTokenAmount);
- balances[taker][takerToken] = balances[taker][takerToken].minus(fillTakerTokenAmount);
- balances[taker][zrx.address] = balances[taker][zrx.address].minus(takerFee);
- balances[feeRecipient][zrx.address] = balances[feeRecipient][zrx.address].add(
- makerFee.add(takerFee),
- );
- });
-
- await exWrapper.batchFillOrKillOrdersAsync(orders, taker, {
- fillTakerTokenAmounts,
- });
-
- const newBalances = await dmyBalances.getAsync();
- expect(newBalances).to.be.deep.equal(balances);
- });
-
- it('should throw if a single order does not fill the expected amount', async () => {
- const fillTakerTokenAmounts: BigNumber[] = [];
- orders.forEach(order => {
- const fillTakerTokenAmount = order.params.takerTokenAmount.div(2);
- fillTakerTokenAmounts.push(fillTakerTokenAmount);
- });
-
- await exWrapper.fillOrKillOrderAsync(orders[0], taker);
-
- return expect(
- exWrapper.batchFillOrKillOrdersAsync(orders, taker, {
- fillTakerTokenAmounts,
- }),
- ).to.be.rejectedWith(constants.REVERT);
- });
- });
-
- describe('fillOrdersUpTo', () => {
- it('should stop when the entire fillTakerTokenAmount is filled', async () => {
- const fillTakerTokenAmount = orders[0].params.takerTokenAmount.plus(
- orders[1].params.takerTokenAmount.div(2),
- );
- await exWrapper.fillOrdersUpToAsync(orders, taker, {
- fillTakerTokenAmount,
- });
-
- const newBalances = await dmyBalances.getAsync();
-
- const fillMakerTokenAmount = orders[0].params.makerTokenAmount.add(
- orders[1].params.makerTokenAmount.dividedToIntegerBy(2),
- );
- const makerFee = orders[0].params.makerFee.add(orders[1].params.makerFee.dividedToIntegerBy(2));
- const takerFee = orders[0].params.takerFee.add(orders[1].params.takerFee.dividedToIntegerBy(2));
- expect(newBalances[maker][orders[0].params.makerToken]).to.be.bignumber.equal(
- balances[maker][orders[0].params.makerToken].minus(fillMakerTokenAmount),
- );
- expect(newBalances[maker][orders[0].params.takerToken]).to.be.bignumber.equal(
- balances[maker][orders[0].params.takerToken].add(fillTakerTokenAmount),
- );
- expect(newBalances[maker][zrx.address]).to.be.bignumber.equal(
- balances[maker][zrx.address].minus(makerFee),
- );
- expect(newBalances[taker][orders[0].params.takerToken]).to.be.bignumber.equal(
- balances[taker][orders[0].params.takerToken].minus(fillTakerTokenAmount),
- );
- expect(newBalances[taker][orders[0].params.makerToken]).to.be.bignumber.equal(
- balances[taker][orders[0].params.makerToken].add(fillMakerTokenAmount),
- );
- expect(newBalances[taker][zrx.address]).to.be.bignumber.equal(
- balances[taker][zrx.address].minus(takerFee),
- );
- expect(newBalances[feeRecipient][zrx.address]).to.be.bignumber.equal(
- balances[feeRecipient][zrx.address].add(makerFee.add(takerFee)),
- );
- });
-
- it('should fill all orders if cannot fill entire fillTakerTokenAmount', async () => {
- const fillTakerTokenAmount = ZeroEx.toBaseUnitAmount(new BigNumber(100000), 18);
- orders.forEach(order => {
- balances[maker][order.params.makerToken] = balances[maker][order.params.makerToken].minus(
- order.params.makerTokenAmount,
- );
- balances[maker][order.params.takerToken] = balances[maker][order.params.takerToken].add(
- order.params.takerTokenAmount,
- );
- balances[maker][zrx.address] = balances[maker][zrx.address].minus(order.params.makerFee);
- balances[taker][order.params.makerToken] = balances[taker][order.params.makerToken].add(
- order.params.makerTokenAmount,
- );
- balances[taker][order.params.takerToken] = balances[taker][order.params.takerToken].minus(
- order.params.takerTokenAmount,
- );
- balances[taker][zrx.address] = balances[taker][zrx.address].minus(order.params.takerFee);
- balances[feeRecipient][zrx.address] = balances[feeRecipient][zrx.address].add(
- order.params.makerFee.add(order.params.takerFee),
- );
- });
- await exWrapper.fillOrdersUpToAsync(orders, taker, {
- fillTakerTokenAmount,
- });
-
- const newBalances = await dmyBalances.getAsync();
- expect(newBalances).to.be.deep.equal(balances);
- });
-
- it('should throw when an order does not use the same takerToken', async () => {
- orders = await Promise.all([
- orderFactory.newSignedOrderAsync(),
- orderFactory.newSignedOrderAsync({ takerToken: zrx.address }),
- orderFactory.newSignedOrderAsync(),
- ]);
-
- return expect(
- exWrapper.fillOrdersUpToAsync(orders, taker, {
- fillTakerTokenAmount: ZeroEx.toBaseUnitAmount(new BigNumber(1000), 18),
- }),
- ).to.be.rejectedWith(constants.REVERT);
- });
- });
-
- describe('batchCancelOrders', () => {
- it('should be able to cancel multiple orders', async () => {
- const cancelTakerTokenAmounts = _.map(orders, order => order.params.takerTokenAmount);
- await exWrapper.batchCancelOrdersAsync(orders, maker, {
- cancelTakerTokenAmounts,
- });
-
- await exWrapper.batchFillOrdersAsync(orders, taker, {
- fillTakerTokenAmounts: cancelTakerTokenAmounts,
- });
- const newBalances = await dmyBalances.getAsync();
- expect(balances).to.be.deep.equal(newBalances);
- });
- });
- });
+ const maker = accounts[0];
+ const tokenOwner = accounts[0];
+ const taker = accounts[1] || accounts[accounts.length - 1];
+ const feeRecipient = accounts[2] || accounts[accounts.length - 1];
+
+ const INIT_BAL = ZeroEx.toBaseUnitAmount(new BigNumber(10000), 18);
+ const INIT_ALLOW = ZeroEx.toBaseUnitAmount(new BigNumber(10000), 18);
+
+ let rep: ContractInstance;
+ let dgd: ContractInstance;
+ let zrx: ContractInstance;
+ let exchange: ContractInstance;
+ let tokenRegistry: ContractInstance;
+
+ let balances: BalancesByOwner;
+
+ let exWrapper: ExchangeWrapper;
+ let dmyBalances: Balances;
+ let orderFactory: OrderFactory;
+
+ before(async () => {
+ [tokenRegistry, exchange] = await Promise.all([TokenRegistry.deployed(), Exchange.deployed()]);
+ exWrapper = new ExchangeWrapper(exchange);
+ const [repAddress, dgdAddress, zrxAddress] = await Promise.all([
+ tokenRegistry.getTokenAddressBySymbol('REP'),
+ tokenRegistry.getTokenAddressBySymbol('DGD'),
+ tokenRegistry.getTokenAddressBySymbol('ZRX'),
+ ]);
+
+ const defaultOrderParams = {
+ exchangeContractAddress: Exchange.address,
+ maker,
+ feeRecipient,
+ makerToken: repAddress,
+ takerToken: dgdAddress,
+ makerTokenAmount: ZeroEx.toBaseUnitAmount(new BigNumber(100), 18),
+ takerTokenAmount: ZeroEx.toBaseUnitAmount(new BigNumber(200), 18),
+ makerFee: ZeroEx.toBaseUnitAmount(new BigNumber(1), 18),
+ takerFee: ZeroEx.toBaseUnitAmount(new BigNumber(1), 18),
+ };
+ orderFactory = new OrderFactory(defaultOrderParams);
+
+ [rep, dgd, zrx] = await Promise.all([
+ DummyToken.at(repAddress),
+ DummyToken.at(dgdAddress),
+ DummyToken.at(zrxAddress),
+ ]);
+ dmyBalances = new Balances([rep, dgd, zrx], [maker, taker, feeRecipient]);
+ await Promise.all([
+ rep.approve(TokenTransferProxy.address, INIT_ALLOW, { from: maker }),
+ rep.approve(TokenTransferProxy.address, INIT_ALLOW, { from: taker }),
+ rep.setBalance(maker, INIT_BAL, { from: tokenOwner }),
+ rep.setBalance(taker, INIT_BAL, { from: tokenOwner }),
+ dgd.approve(TokenTransferProxy.address, INIT_ALLOW, { from: maker }),
+ dgd.approve(TokenTransferProxy.address, INIT_ALLOW, { from: taker }),
+ dgd.setBalance(maker, INIT_BAL, { from: tokenOwner }),
+ dgd.setBalance(taker, INIT_BAL, { from: tokenOwner }),
+ zrx.approve(TokenTransferProxy.address, INIT_ALLOW, { from: maker }),
+ zrx.approve(TokenTransferProxy.address, INIT_ALLOW, { from: taker }),
+ zrx.setBalance(maker, INIT_BAL, { from: tokenOwner }),
+ zrx.setBalance(taker, INIT_BAL, { from: tokenOwner }),
+ ]);
+ });
+
+ describe('fillOrKillOrder', () => {
+ beforeEach(async () => {
+ balances = await dmyBalances.getAsync();
+ });
+
+ it('should transfer the correct amounts', async () => {
+ const order = await orderFactory.newSignedOrderAsync({
+ makerTokenAmount: ZeroEx.toBaseUnitAmount(new BigNumber(100), 18),
+ takerTokenAmount: ZeroEx.toBaseUnitAmount(new BigNumber(200), 18),
+ });
+ const fillTakerTokenAmount = order.params.takerTokenAmount.div(2);
+ await exWrapper.fillOrKillOrderAsync(order, taker, {
+ fillTakerTokenAmount,
+ });
+
+ const newBalances = await dmyBalances.getAsync();
+
+ const fillMakerTokenAmount = fillTakerTokenAmount
+ .times(order.params.makerTokenAmount)
+ .dividedToIntegerBy(order.params.takerTokenAmount);
+ const makerFee = order.params.makerFee
+ .times(fillMakerTokenAmount)
+ .dividedToIntegerBy(order.params.makerTokenAmount);
+ const takerFee = order.params.takerFee
+ .times(fillMakerTokenAmount)
+ .dividedToIntegerBy(order.params.makerTokenAmount);
+ expect(newBalances[maker][order.params.makerToken]).to.be.bignumber.equal(
+ balances[maker][order.params.makerToken].minus(fillMakerTokenAmount),
+ );
+ expect(newBalances[maker][order.params.takerToken]).to.be.bignumber.equal(
+ balances[maker][order.params.takerToken].add(fillTakerTokenAmount),
+ );
+ expect(newBalances[maker][zrx.address]).to.be.bignumber.equal(balances[maker][zrx.address].minus(makerFee));
+ expect(newBalances[taker][order.params.takerToken]).to.be.bignumber.equal(
+ balances[taker][order.params.takerToken].minus(fillTakerTokenAmount),
+ );
+ expect(newBalances[taker][order.params.makerToken]).to.be.bignumber.equal(
+ balances[taker][order.params.makerToken].add(fillMakerTokenAmount),
+ );
+ expect(newBalances[taker][zrx.address]).to.be.bignumber.equal(balances[taker][zrx.address].minus(takerFee));
+ expect(newBalances[feeRecipient][zrx.address]).to.be.bignumber.equal(
+ balances[feeRecipient][zrx.address].add(makerFee.add(takerFee)),
+ );
+ });
+
+ it('should throw if an order is expired', async () => {
+ const order = await orderFactory.newSignedOrderAsync({
+ expirationTimestampInSec: new BigNumber(Math.floor((Date.now() - 10000) / 1000)),
+ });
+
+ return expect(exWrapper.fillOrKillOrderAsync(order, taker)).to.be.rejectedWith(constants.REVERT);
+ });
+
+ it('should throw if entire fillTakerTokenAmount not filled', async () => {
+ const order = await orderFactory.newSignedOrderAsync();
+
+ const from = taker;
+ await exWrapper.fillOrderAsync(order, from, {
+ fillTakerTokenAmount: order.params.takerTokenAmount.div(2),
+ });
+
+ return expect(exWrapper.fillOrKillOrderAsync(order, taker)).to.be.rejectedWith(constants.REVERT);
+ });
+ });
+
+ describe('batch functions', () => {
+ let orders: Order[];
+ beforeEach(async () => {
+ orders = await Promise.all([
+ orderFactory.newSignedOrderAsync(),
+ orderFactory.newSignedOrderAsync(),
+ orderFactory.newSignedOrderAsync(),
+ ]);
+ balances = await dmyBalances.getAsync();
+ });
+
+ describe('batchFillOrders', () => {
+ it('should transfer the correct amounts', async () => {
+ const fillTakerTokenAmounts: BigNumber[] = [];
+ const makerToken = rep.address;
+ const takerToken = dgd.address;
+ orders.forEach(order => {
+ const fillTakerTokenAmount = order.params.takerTokenAmount.div(2);
+ const fillMakerTokenAmount = fillTakerTokenAmount
+ .times(order.params.makerTokenAmount)
+ .dividedToIntegerBy(order.params.takerTokenAmount);
+ const makerFee = order.params.makerFee
+ .times(fillMakerTokenAmount)
+ .dividedToIntegerBy(order.params.makerTokenAmount);
+ const takerFee = order.params.takerFee
+ .times(fillMakerTokenAmount)
+ .dividedToIntegerBy(order.params.makerTokenAmount);
+ fillTakerTokenAmounts.push(fillTakerTokenAmount);
+ balances[maker][makerToken] = balances[maker][makerToken].minus(fillMakerTokenAmount);
+ balances[maker][takerToken] = balances[maker][takerToken].add(fillTakerTokenAmount);
+ balances[maker][zrx.address] = balances[maker][zrx.address].minus(makerFee);
+ balances[taker][makerToken] = balances[taker][makerToken].add(fillMakerTokenAmount);
+ balances[taker][takerToken] = balances[taker][takerToken].minus(fillTakerTokenAmount);
+ balances[taker][zrx.address] = balances[taker][zrx.address].minus(takerFee);
+ balances[feeRecipient][zrx.address] = balances[feeRecipient][zrx.address].add(
+ makerFee.add(takerFee),
+ );
+ });
+
+ await exWrapper.batchFillOrdersAsync(orders, taker, {
+ fillTakerTokenAmounts,
+ });
+
+ const newBalances = await dmyBalances.getAsync();
+ expect(newBalances).to.be.deep.equal(balances);
+ });
+ });
+
+ describe('batchFillOrKillOrders', () => {
+ it('should transfer the correct amounts', async () => {
+ const fillTakerTokenAmounts: BigNumber[] = [];
+ const makerToken = rep.address;
+ const takerToken = dgd.address;
+ orders.forEach(order => {
+ const fillTakerTokenAmount = order.params.takerTokenAmount.div(2);
+ const fillMakerTokenAmount = fillTakerTokenAmount
+ .times(order.params.makerTokenAmount)
+ .dividedToIntegerBy(order.params.takerTokenAmount);
+ const makerFee = order.params.makerFee
+ .times(fillMakerTokenAmount)
+ .dividedToIntegerBy(order.params.makerTokenAmount);
+ const takerFee = order.params.takerFee
+ .times(fillMakerTokenAmount)
+ .dividedToIntegerBy(order.params.makerTokenAmount);
+ fillTakerTokenAmounts.push(fillTakerTokenAmount);
+ balances[maker][makerToken] = balances[maker][makerToken].minus(fillMakerTokenAmount);
+ balances[maker][takerToken] = balances[maker][takerToken].add(fillTakerTokenAmount);
+ balances[maker][zrx.address] = balances[maker][zrx.address].minus(makerFee);
+ balances[taker][makerToken] = balances[taker][makerToken].add(fillMakerTokenAmount);
+ balances[taker][takerToken] = balances[taker][takerToken].minus(fillTakerTokenAmount);
+ balances[taker][zrx.address] = balances[taker][zrx.address].minus(takerFee);
+ balances[feeRecipient][zrx.address] = balances[feeRecipient][zrx.address].add(
+ makerFee.add(takerFee),
+ );
+ });
+
+ await exWrapper.batchFillOrKillOrdersAsync(orders, taker, {
+ fillTakerTokenAmounts,
+ });
+
+ const newBalances = await dmyBalances.getAsync();
+ expect(newBalances).to.be.deep.equal(balances);
+ });
+
+ it('should throw if a single order does not fill the expected amount', async () => {
+ const fillTakerTokenAmounts: BigNumber[] = [];
+ orders.forEach(order => {
+ const fillTakerTokenAmount = order.params.takerTokenAmount.div(2);
+ fillTakerTokenAmounts.push(fillTakerTokenAmount);
+ });
+
+ await exWrapper.fillOrKillOrderAsync(orders[0], taker);
+
+ return expect(
+ exWrapper.batchFillOrKillOrdersAsync(orders, taker, {
+ fillTakerTokenAmounts,
+ }),
+ ).to.be.rejectedWith(constants.REVERT);
+ });
+ });
+
+ describe('fillOrdersUpTo', () => {
+ it('should stop when the entire fillTakerTokenAmount is filled', async () => {
+ const fillTakerTokenAmount = orders[0].params.takerTokenAmount.plus(
+ orders[1].params.takerTokenAmount.div(2),
+ );
+ await exWrapper.fillOrdersUpToAsync(orders, taker, {
+ fillTakerTokenAmount,
+ });
+
+ const newBalances = await dmyBalances.getAsync();
+
+ const fillMakerTokenAmount = orders[0].params.makerTokenAmount.add(
+ orders[1].params.makerTokenAmount.dividedToIntegerBy(2),
+ );
+ const makerFee = orders[0].params.makerFee.add(orders[1].params.makerFee.dividedToIntegerBy(2));
+ const takerFee = orders[0].params.takerFee.add(orders[1].params.takerFee.dividedToIntegerBy(2));
+ expect(newBalances[maker][orders[0].params.makerToken]).to.be.bignumber.equal(
+ balances[maker][orders[0].params.makerToken].minus(fillMakerTokenAmount),
+ );
+ expect(newBalances[maker][orders[0].params.takerToken]).to.be.bignumber.equal(
+ balances[maker][orders[0].params.takerToken].add(fillTakerTokenAmount),
+ );
+ expect(newBalances[maker][zrx.address]).to.be.bignumber.equal(
+ balances[maker][zrx.address].minus(makerFee),
+ );
+ expect(newBalances[taker][orders[0].params.takerToken]).to.be.bignumber.equal(
+ balances[taker][orders[0].params.takerToken].minus(fillTakerTokenAmount),
+ );
+ expect(newBalances[taker][orders[0].params.makerToken]).to.be.bignumber.equal(
+ balances[taker][orders[0].params.makerToken].add(fillMakerTokenAmount),
+ );
+ expect(newBalances[taker][zrx.address]).to.be.bignumber.equal(
+ balances[taker][zrx.address].minus(takerFee),
+ );
+ expect(newBalances[feeRecipient][zrx.address]).to.be.bignumber.equal(
+ balances[feeRecipient][zrx.address].add(makerFee.add(takerFee)),
+ );
+ });
+
+ it('should fill all orders if cannot fill entire fillTakerTokenAmount', async () => {
+ const fillTakerTokenAmount = ZeroEx.toBaseUnitAmount(new BigNumber(100000), 18);
+ orders.forEach(order => {
+ balances[maker][order.params.makerToken] = balances[maker][order.params.makerToken].minus(
+ order.params.makerTokenAmount,
+ );
+ balances[maker][order.params.takerToken] = balances[maker][order.params.takerToken].add(
+ order.params.takerTokenAmount,
+ );
+ balances[maker][zrx.address] = balances[maker][zrx.address].minus(order.params.makerFee);
+ balances[taker][order.params.makerToken] = balances[taker][order.params.makerToken].add(
+ order.params.makerTokenAmount,
+ );
+ balances[taker][order.params.takerToken] = balances[taker][order.params.takerToken].minus(
+ order.params.takerTokenAmount,
+ );
+ balances[taker][zrx.address] = balances[taker][zrx.address].minus(order.params.takerFee);
+ balances[feeRecipient][zrx.address] = balances[feeRecipient][zrx.address].add(
+ order.params.makerFee.add(order.params.takerFee),
+ );
+ });
+ await exWrapper.fillOrdersUpToAsync(orders, taker, {
+ fillTakerTokenAmount,
+ });
+
+ const newBalances = await dmyBalances.getAsync();
+ expect(newBalances).to.be.deep.equal(balances);
+ });
+
+ it('should throw when an order does not use the same takerToken', async () => {
+ orders = await Promise.all([
+ orderFactory.newSignedOrderAsync(),
+ orderFactory.newSignedOrderAsync({ takerToken: zrx.address }),
+ orderFactory.newSignedOrderAsync(),
+ ]);
+
+ return expect(
+ exWrapper.fillOrdersUpToAsync(orders, taker, {
+ fillTakerTokenAmount: ZeroEx.toBaseUnitAmount(new BigNumber(1000), 18),
+ }),
+ ).to.be.rejectedWith(constants.REVERT);
+ });
+ });
+
+ describe('batchCancelOrders', () => {
+ it('should be able to cancel multiple orders', async () => {
+ const cancelTakerTokenAmounts = _.map(orders, order => order.params.takerTokenAmount);
+ await exWrapper.batchCancelOrdersAsync(orders, maker, {
+ cancelTakerTokenAmounts,
+ });
+
+ await exWrapper.batchFillOrdersAsync(orders, taker, {
+ fillTakerTokenAmounts: cancelTakerTokenAmounts,
+ });
+ const newBalances = await dmyBalances.getAsync();
+ expect(balances).to.be.deep.equal(newBalances);
+ });
+ });
+ });
});
diff --git a/packages/contracts/test/ts/multi_sig_with_time_lock.ts b/packages/contracts/test/ts/multi_sig_with_time_lock.ts
index fa2028437..ea939a758 100644
--- a/packages/contracts/test/ts/multi_sig_with_time_lock.ts
+++ b/packages/contracts/test/ts/multi_sig_with_time_lock.ts
@@ -23,93 +23,93 @@ const expect = chai.expect;
const web3: Web3 = (global as any).web3;
contract('MultiSigWalletWithTimeLock', (accounts: string[]) => {
- const owners = [accounts[0], accounts[1]];
- const SECONDS_TIME_LOCKED = 10000;
-
- let multiSig: ContractInstance;
- let multiSigWrapper: MultiSigWrapper;
- let txId: number;
- let initialSecondsTimeLocked: number;
- let rpc: RPC;
-
- before(async () => {
- multiSig = await MultiSigWalletWithTimeLock.deployed();
- multiSigWrapper = new MultiSigWrapper(multiSig);
-
- const secondsTimeLocked = await multiSig.secondsTimeLocked.call();
- initialSecondsTimeLocked = secondsTimeLocked.toNumber();
- const rpcUrl = `http://${truffleConf.networks.development.host}:${truffleConf.networks.development.port}`;
- rpc = new RPC(rpcUrl);
- });
-
- describe('changeTimeLock', () => {
- it('should throw when not called by wallet', async () => {
- return expect(multiSig.changeTimeLock(SECONDS_TIME_LOCKED, { from: owners[0] })).to.be.rejectedWith(
- constants.REVERT,
- );
- });
-
- it('should throw without enough confirmations', async () => {
- const destination = multiSig.address;
- const from = owners[0];
- const dataParams = {
- name: 'changeTimeLock',
- abi: MULTI_SIG_ABI,
- args: [SECONDS_TIME_LOCKED],
- };
- const subRes = await multiSigWrapper.submitTransactionAsync(destination, from, dataParams);
-
- txId = subRes.logs[0].args.transactionId.toNumber();
- return expect(multiSig.executeTransaction(txId)).to.be.rejectedWith(constants.REVERT);
- });
-
- it('should set confirmation time with enough confirmations', async () => {
- const res = await multiSig.confirmTransaction(txId, { from: owners[1] });
- expect(res.logs).to.have.length(2);
- const blockNum = await promisify<number>(web3.eth.getBlockNumber)();
- const blockInfo = await promisify<Web3.BlockWithoutTransactionData>(web3.eth.getBlock)(blockNum);
- const timestamp = new BigNumber(blockInfo.timestamp);
- const confirmationTimeBigNum = new BigNumber(await multiSig.confirmationTimes.call(txId));
-
- expect(timestamp).to.be.bignumber.equal(confirmationTimeBigNum);
- });
-
- it('should be executable with enough confirmations and secondsTimeLocked of 0', async () => {
- expect(initialSecondsTimeLocked).to.be.equal(0);
-
- const res = await multiSig.executeTransaction(txId);
- expect(res.logs).to.have.length(2);
-
- const secondsTimeLocked = new BigNumber(await multiSig.secondsTimeLocked.call());
- expect(secondsTimeLocked).to.be.bignumber.equal(SECONDS_TIME_LOCKED);
- });
-
- const newSecondsTimeLocked = 0;
- it('should throw if it has enough confirmations but is not past the time lock', async () => {
- const destination = multiSig.address;
- const from = owners[0];
- const dataParams = {
- name: 'changeTimeLock',
- abi: MULTI_SIG_ABI,
- args: [newSecondsTimeLocked],
- };
- const subRes = await multiSigWrapper.submitTransactionAsync(destination, from, dataParams);
-
- txId = subRes.logs[0].args.transactionId.toNumber();
- const confRes = await multiSig.confirmTransaction(txId, {
- from: owners[1],
- });
- expect(confRes.logs).to.have.length(2);
-
- return expect(multiSig.executeTransaction(txId)).to.be.rejectedWith(constants.REVERT);
- });
-
- it('should execute if it has enough confirmations and is past the time lock', async () => {
- await rpc.increaseTimeAsync(SECONDS_TIME_LOCKED);
- await multiSig.executeTransaction(txId);
-
- const secondsTimeLocked = new BigNumber(await multiSig.secondsTimeLocked.call());
- expect(secondsTimeLocked).to.be.bignumber.equal(newSecondsTimeLocked);
- });
- });
+ const owners = [accounts[0], accounts[1]];
+ const SECONDS_TIME_LOCKED = 10000;
+
+ let multiSig: ContractInstance;
+ let multiSigWrapper: MultiSigWrapper;
+ let txId: number;
+ let initialSecondsTimeLocked: number;
+ let rpc: RPC;
+
+ before(async () => {
+ multiSig = await MultiSigWalletWithTimeLock.deployed();
+ multiSigWrapper = new MultiSigWrapper(multiSig);
+
+ const secondsTimeLocked = await multiSig.secondsTimeLocked.call();
+ initialSecondsTimeLocked = secondsTimeLocked.toNumber();
+ const rpcUrl = `http://${truffleConf.networks.development.host}:${truffleConf.networks.development.port}`;
+ rpc = new RPC(rpcUrl);
+ });
+
+ describe('changeTimeLock', () => {
+ it('should throw when not called by wallet', async () => {
+ return expect(multiSig.changeTimeLock(SECONDS_TIME_LOCKED, { from: owners[0] })).to.be.rejectedWith(
+ constants.REVERT,
+ );
+ });
+
+ it('should throw without enough confirmations', async () => {
+ const destination = multiSig.address;
+ const from = owners[0];
+ const dataParams = {
+ name: 'changeTimeLock',
+ abi: MULTI_SIG_ABI,
+ args: [SECONDS_TIME_LOCKED],
+ };
+ const subRes = await multiSigWrapper.submitTransactionAsync(destination, from, dataParams);
+
+ txId = subRes.logs[0].args.transactionId.toNumber();
+ return expect(multiSig.executeTransaction(txId)).to.be.rejectedWith(constants.REVERT);
+ });
+
+ it('should set confirmation time with enough confirmations', async () => {
+ const res = await multiSig.confirmTransaction(txId, { from: owners[1] });
+ expect(res.logs).to.have.length(2);
+ const blockNum = await promisify<number>(web3.eth.getBlockNumber)();
+ const blockInfo = await promisify<Web3.BlockWithoutTransactionData>(web3.eth.getBlock)(blockNum);
+ const timestamp = new BigNumber(blockInfo.timestamp);
+ const confirmationTimeBigNum = new BigNumber(await multiSig.confirmationTimes.call(txId));
+
+ expect(timestamp).to.be.bignumber.equal(confirmationTimeBigNum);
+ });
+
+ it('should be executable with enough confirmations and secondsTimeLocked of 0', async () => {
+ expect(initialSecondsTimeLocked).to.be.equal(0);
+
+ const res = await multiSig.executeTransaction(txId);
+ expect(res.logs).to.have.length(2);
+
+ const secondsTimeLocked = new BigNumber(await multiSig.secondsTimeLocked.call());
+ expect(secondsTimeLocked).to.be.bignumber.equal(SECONDS_TIME_LOCKED);
+ });
+
+ const newSecondsTimeLocked = 0;
+ it('should throw if it has enough confirmations but is not past the time lock', async () => {
+ const destination = multiSig.address;
+ const from = owners[0];
+ const dataParams = {
+ name: 'changeTimeLock',
+ abi: MULTI_SIG_ABI,
+ args: [newSecondsTimeLocked],
+ };
+ const subRes = await multiSigWrapper.submitTransactionAsync(destination, from, dataParams);
+
+ txId = subRes.logs[0].args.transactionId.toNumber();
+ const confRes = await multiSig.confirmTransaction(txId, {
+ from: owners[1],
+ });
+ expect(confRes.logs).to.have.length(2);
+
+ return expect(multiSig.executeTransaction(txId)).to.be.rejectedWith(constants.REVERT);
+ });
+
+ it('should execute if it has enough confirmations and is past the time lock', async () => {
+ await rpc.increaseTimeAsync(SECONDS_TIME_LOCKED);
+ await multiSig.executeTransaction(txId);
+
+ const secondsTimeLocked = new BigNumber(await multiSig.secondsTimeLocked.call());
+ expect(secondsTimeLocked).to.be.bignumber.equal(newSecondsTimeLocked);
+ });
+ });
});
diff --git a/packages/contracts/test/ts/multi_sig_with_time_lock_except_remove_auth_addr.ts b/packages/contracts/test/ts/multi_sig_with_time_lock_except_remove_auth_addr.ts
index 9211cb1b6..62aa625fe 100644
--- a/packages/contracts/test/ts/multi_sig_with_time_lock_except_remove_auth_addr.ts
+++ b/packages/contracts/test/ts/multi_sig_with_time_lock_except_remove_auth_addr.ts
@@ -15,136 +15,136 @@ chaiSetup.configure();
const expect = chai.expect;
contract('MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress', (accounts: string[]) => {
- const owners = [accounts[0], accounts[1]];
- const requiredApprovals = 2;
- const SECONDS_TIME_LOCKED = 1000000;
-
- // initialize fake addresses
- const authorizedAddress = `0x${crypto
- .solSHA3([accounts[0]])
- .slice(0, 20)
- .toString('hex')}`;
- const unauthorizedAddress = `0x${crypto
- .solSHA3([accounts[1]])
- .slice(0, 20)
- .toString('hex')}`;
-
- let tokenTransferProxy: ContractInstance;
- let multiSig: ContractInstance;
- let multiSigWrapper: MultiSigWrapper;
-
- let validDestination: string;
-
- beforeEach(async () => {
- const initialOwner = accounts[0];
- tokenTransferProxy = await TokenTransferProxy.new({ from: initialOwner });
- await tokenTransferProxy.addAuthorizedAddress(authorizedAddress, {
- from: initialOwner,
- });
- multiSig = await MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress.new(
- owners,
- requiredApprovals,
- SECONDS_TIME_LOCKED,
- tokenTransferProxy.address,
- );
- await tokenTransferProxy.transferOwnership(multiSig.address, {
- from: initialOwner,
- });
- multiSigWrapper = new MultiSigWrapper(multiSig);
- validDestination = tokenTransferProxy.address;
- });
-
- describe('isFunctionRemoveAuthorizedAddress', () => {
- it('should throw if data is not for removeAuthorizedAddress', async () => {
- const data = MultiSigWrapper.encodeFnArgs('addAuthorizedAddress', PROXY_ABI, [owners[0]]);
- return expect(multiSig.isFunctionRemoveAuthorizedAddress.call(data)).to.be.rejectedWith(constants.REVERT);
- });
-
- it('should return true if data is for removeAuthorizedAddress', async () => {
- const data = MultiSigWrapper.encodeFnArgs('removeAuthorizedAddress', PROXY_ABI, [owners[0]]);
- const isFunctionRemoveAuthorizedAddress = await multiSig.isFunctionRemoveAuthorizedAddress.call(data);
- expect(isFunctionRemoveAuthorizedAddress).to.be.true();
- });
- });
-
- describe('executeRemoveAuthorizedAddress', () => {
- it('should throw without the required confirmations', async () => {
- const dataParams: TransactionDataParams = {
- name: 'removeAuthorizedAddress',
- abi: PROXY_ABI,
- args: [authorizedAddress],
- };
- const res = await multiSigWrapper.submitTransactionAsync(validDestination, owners[0], dataParams);
- const txId = res.logs[0].args.transactionId.toString();
-
- return expect(multiSig.executeRemoveAuthorizedAddress(txId)).to.be.rejectedWith(constants.REVERT);
- });
-
- it('should throw if tx destination is not the tokenTransferProxy', async () => {
- const invalidTokenTransferProxy = await TokenTransferProxy.new();
- const invalidDestination = invalidTokenTransferProxy.address;
- const dataParams: TransactionDataParams = {
- name: 'removeAuthorizedAddress',
- abi: PROXY_ABI,
- args: [authorizedAddress],
- };
- const res = await multiSigWrapper.submitTransactionAsync(invalidDestination, owners[0], dataParams);
- const txId = res.logs[0].args.transactionId.toString();
- await multiSig.confirmTransaction(txId, { from: owners[1] });
- const isConfirmed = await multiSig.isConfirmed.call(txId);
- expect(isConfirmed).to.be.true();
-
- return expect(multiSig.executeRemoveAuthorizedAddress(txId)).to.be.rejectedWith(constants.REVERT);
- });
-
- it('should throw if tx data is not for removeAuthorizedAddress', async () => {
- const dataParams: TransactionDataParams = {
- name: 'addAuthorizedAddress',
- abi: PROXY_ABI,
- args: [unauthorizedAddress],
- };
- const res = await multiSigWrapper.submitTransactionAsync(validDestination, owners[0], dataParams);
- const txId = res.logs[0].args.transactionId.toString();
- await multiSig.confirmTransaction(txId, { from: owners[1] });
- const isConfirmed = await multiSig.isConfirmed.call(txId);
- expect(isConfirmed).to.be.true();
-
- return expect(multiSig.executeRemoveAuthorizedAddress(txId)).to.be.rejectedWith(constants.REVERT);
- });
-
- it('should execute removeAuthorizedAddress for valid tokenTransferProxy if fully confirmed', async () => {
- const dataParams: TransactionDataParams = {
- name: 'removeAuthorizedAddress',
- abi: PROXY_ABI,
- args: [authorizedAddress],
- };
- const res = await multiSigWrapper.submitTransactionAsync(validDestination, owners[0], dataParams);
- const txId = res.logs[0].args.transactionId.toString();
- await multiSig.confirmTransaction(txId, { from: owners[1] });
- const isConfirmed = await multiSig.isConfirmed.call(txId);
- expect(isConfirmed).to.be.true();
- await multiSig.executeRemoveAuthorizedAddress(txId);
-
- const isAuthorized = await tokenTransferProxy.authorized.call(authorizedAddress);
- expect(isAuthorized).to.be.false();
- });
-
- it('should throw if already executed', async () => {
- const dataParams: TransactionDataParams = {
- name: 'removeAuthorizedAddress',
- abi: PROXY_ABI,
- args: [authorizedAddress],
- };
- const res = await multiSigWrapper.submitTransactionAsync(validDestination, owners[0], dataParams);
- const txId = res.logs[0].args.transactionId.toString();
- await multiSig.confirmTransaction(txId, { from: owners[1] });
- const isConfirmed = await multiSig.isConfirmed.call(txId);
- expect(isConfirmed).to.be.true();
- await multiSig.executeRemoveAuthorizedAddress(txId);
- const tx = await multiSig.transactions.call(txId);
- const isExecuted = tx[3];
- expect(isExecuted).to.be.true();
- return expect(multiSig.executeRemoveAuthorizedAddress(txId)).to.be.rejectedWith(constants.REVERT);
- });
- });
+ const owners = [accounts[0], accounts[1]];
+ const requiredApprovals = 2;
+ const SECONDS_TIME_LOCKED = 1000000;
+
+ // initialize fake addresses
+ const authorizedAddress = `0x${crypto
+ .solSHA3([accounts[0]])
+ .slice(0, 20)
+ .toString('hex')}`;
+ const unauthorizedAddress = `0x${crypto
+ .solSHA3([accounts[1]])
+ .slice(0, 20)
+ .toString('hex')}`;
+
+ let tokenTransferProxy: ContractInstance;
+ let multiSig: ContractInstance;
+ let multiSigWrapper: MultiSigWrapper;
+
+ let validDestination: string;
+
+ beforeEach(async () => {
+ const initialOwner = accounts[0];
+ tokenTransferProxy = await TokenTransferProxy.new({ from: initialOwner });
+ await tokenTransferProxy.addAuthorizedAddress(authorizedAddress, {
+ from: initialOwner,
+ });
+ multiSig = await MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress.new(
+ owners,
+ requiredApprovals,
+ SECONDS_TIME_LOCKED,
+ tokenTransferProxy.address,
+ );
+ await tokenTransferProxy.transferOwnership(multiSig.address, {
+ from: initialOwner,
+ });
+ multiSigWrapper = new MultiSigWrapper(multiSig);
+ validDestination = tokenTransferProxy.address;
+ });
+
+ describe('isFunctionRemoveAuthorizedAddress', () => {
+ it('should throw if data is not for removeAuthorizedAddress', async () => {
+ const data = MultiSigWrapper.encodeFnArgs('addAuthorizedAddress', PROXY_ABI, [owners[0]]);
+ return expect(multiSig.isFunctionRemoveAuthorizedAddress.call(data)).to.be.rejectedWith(constants.REVERT);
+ });
+
+ it('should return true if data is for removeAuthorizedAddress', async () => {
+ const data = MultiSigWrapper.encodeFnArgs('removeAuthorizedAddress', PROXY_ABI, [owners[0]]);
+ const isFunctionRemoveAuthorizedAddress = await multiSig.isFunctionRemoveAuthorizedAddress.call(data);
+ expect(isFunctionRemoveAuthorizedAddress).to.be.true();
+ });
+ });
+
+ describe('executeRemoveAuthorizedAddress', () => {
+ it('should throw without the required confirmations', async () => {
+ const dataParams: TransactionDataParams = {
+ name: 'removeAuthorizedAddress',
+ abi: PROXY_ABI,
+ args: [authorizedAddress],
+ };
+ const res = await multiSigWrapper.submitTransactionAsync(validDestination, owners[0], dataParams);
+ const txId = res.logs[0].args.transactionId.toString();
+
+ return expect(multiSig.executeRemoveAuthorizedAddress(txId)).to.be.rejectedWith(constants.REVERT);
+ });
+
+ it('should throw if tx destination is not the tokenTransferProxy', async () => {
+ const invalidTokenTransferProxy = await TokenTransferProxy.new();
+ const invalidDestination = invalidTokenTransferProxy.address;
+ const dataParams: TransactionDataParams = {
+ name: 'removeAuthorizedAddress',
+ abi: PROXY_ABI,
+ args: [authorizedAddress],
+ };
+ const res = await multiSigWrapper.submitTransactionAsync(invalidDestination, owners[0], dataParams);
+ const txId = res.logs[0].args.transactionId.toString();
+ await multiSig.confirmTransaction(txId, { from: owners[1] });
+ const isConfirmed = await multiSig.isConfirmed.call(txId);
+ expect(isConfirmed).to.be.true();
+
+ return expect(multiSig.executeRemoveAuthorizedAddress(txId)).to.be.rejectedWith(constants.REVERT);
+ });
+
+ it('should throw if tx data is not for removeAuthorizedAddress', async () => {
+ const dataParams: TransactionDataParams = {
+ name: 'addAuthorizedAddress',
+ abi: PROXY_ABI,
+ args: [unauthorizedAddress],
+ };
+ const res = await multiSigWrapper.submitTransactionAsync(validDestination, owners[0], dataParams);
+ const txId = res.logs[0].args.transactionId.toString();
+ await multiSig.confirmTransaction(txId, { from: owners[1] });
+ const isConfirmed = await multiSig.isConfirmed.call(txId);
+ expect(isConfirmed).to.be.true();
+
+ return expect(multiSig.executeRemoveAuthorizedAddress(txId)).to.be.rejectedWith(constants.REVERT);
+ });
+
+ it('should execute removeAuthorizedAddress for valid tokenTransferProxy if fully confirmed', async () => {
+ const dataParams: TransactionDataParams = {
+ name: 'removeAuthorizedAddress',
+ abi: PROXY_ABI,
+ args: [authorizedAddress],
+ };
+ const res = await multiSigWrapper.submitTransactionAsync(validDestination, owners[0], dataParams);
+ const txId = res.logs[0].args.transactionId.toString();
+ await multiSig.confirmTransaction(txId, { from: owners[1] });
+ const isConfirmed = await multiSig.isConfirmed.call(txId);
+ expect(isConfirmed).to.be.true();
+ await multiSig.executeRemoveAuthorizedAddress(txId);
+
+ const isAuthorized = await tokenTransferProxy.authorized.call(authorizedAddress);
+ expect(isAuthorized).to.be.false();
+ });
+
+ it('should throw if already executed', async () => {
+ const dataParams: TransactionDataParams = {
+ name: 'removeAuthorizedAddress',
+ abi: PROXY_ABI,
+ args: [authorizedAddress],
+ };
+ const res = await multiSigWrapper.submitTransactionAsync(validDestination, owners[0], dataParams);
+ const txId = res.logs[0].args.transactionId.toString();
+ await multiSig.confirmTransaction(txId, { from: owners[1] });
+ const isConfirmed = await multiSig.isConfirmed.call(txId);
+ expect(isConfirmed).to.be.true();
+ await multiSig.executeRemoveAuthorizedAddress(txId);
+ const tx = await multiSig.transactions.call(txId);
+ const isExecuted = tx[3];
+ expect(isExecuted).to.be.true();
+ return expect(multiSig.executeRemoveAuthorizedAddress(txId)).to.be.rejectedWith(constants.REVERT);
+ });
+ });
});
diff --git a/packages/contracts/test/ts/token_registry.ts b/packages/contracts/test/ts/token_registry.ts
index fadb276da..d1c551565 100644
--- a/packages/contracts/test/ts/token_registry.ts
+++ b/packages/contracts/test/ts/token_registry.ts
@@ -15,220 +15,220 @@ chaiSetup.configure();
const expect = chai.expect;
contract('TokenRegistry', (accounts: string[]) => {
- const owner = accounts[0];
- const notOwner = accounts[1];
-
- const tokenAddress1 = `0x${ethUtil.setLength(ethUtil.toBuffer('0x1'), 20, false).toString('hex')}`;
- const tokenAddress2 = `0x${ethUtil.setLength(ethUtil.toBuffer('0x2'), 20, false).toString('hex')}`;
-
- const token1 = {
- address: tokenAddress1,
- name: 'testToken1',
- symbol: 'TT1',
- decimals: 18,
- ipfsHash: `0x${ethUtil.sha3('ipfs1').toString('hex')}`,
- swarmHash: `0x${ethUtil.sha3('swarm1').toString('hex')}`,
- };
-
- const token2 = {
- address: tokenAddress2,
- name: 'testToken2',
- symbol: 'TT2',
- decimals: 18,
- ipfsHash: `0x${ethUtil.sha3('ipfs2').toString('hex')}`,
- swarmHash: `0x${ethUtil.sha3('swarm2').toString('hex')}`,
- };
-
- const nullToken = {
- address: ZeroEx.NULL_ADDRESS,
- name: '',
- symbol: '',
- decimals: 0,
- ipfsHash: constants.NULL_BYTES,
- swarmHash: constants.NULL_BYTES,
- };
-
- let tokenReg: ContractInstance;
- let tokenRegWrapper: TokenRegWrapper;
-
- beforeEach(async () => {
- tokenReg = await TokenRegistry.new();
- tokenRegWrapper = new TokenRegWrapper(tokenReg);
- });
-
- describe('addToken', () => {
- it('should throw when not called by owner', async () => {
- return expect(tokenRegWrapper.addTokenAsync(token1, notOwner)).to.be.rejectedWith(constants.REVERT);
- });
-
- it('should add token metadata when called by owner', async () => {
- await tokenRegWrapper.addTokenAsync(token1, owner);
- const tokenData = await tokenRegWrapper.getTokenMetaDataAsync(token1.address);
- expect(tokenData).to.be.deep.equal(token1);
- });
-
- it('should throw if token already exists', async () => {
- await tokenRegWrapper.addTokenAsync(token1, owner);
-
- return expect(tokenRegWrapper.addTokenAsync(token1, owner)).to.be.rejectedWith(constants.REVERT);
- });
-
- it('should throw if token address is null', async () => {
- return expect(tokenRegWrapper.addTokenAsync(nullToken, owner)).to.be.rejectedWith(constants.REVERT);
- });
-
- it('should throw if name already exists', async () => {
- await tokenRegWrapper.addTokenAsync(token1, owner);
- const duplicateNameToken = _.assign({}, token2, { name: token1.name });
-
- return expect(tokenRegWrapper.addTokenAsync(duplicateNameToken, owner)).to.be.rejectedWith(
- constants.REVERT,
- );
- });
-
- it('should throw if symbol already exists', async () => {
- await tokenRegWrapper.addTokenAsync(token1, owner);
- const duplicateSymbolToken = _.assign({}, token2, {
- symbol: token1.symbol,
- });
-
- return expect(tokenRegWrapper.addTokenAsync(duplicateSymbolToken, owner)).to.be.rejectedWith(
- constants.REVERT,
- );
- });
- });
-
- describe('after addToken', () => {
- beforeEach(async () => {
- await tokenRegWrapper.addTokenAsync(token1, owner);
- });
-
- describe('getTokenByName', () => {
- it('should return token metadata when given the token name', async () => {
- const tokenData = await tokenRegWrapper.getTokenByNameAsync(token1.name);
- expect(tokenData).to.be.deep.equal(token1);
- });
- });
-
- describe('getTokenBySymbol', () => {
- it('should return token metadata when given the token symbol', async () => {
- const tokenData = await tokenRegWrapper.getTokenBySymbolAsync(token1.symbol);
- expect(tokenData).to.be.deep.equal(token1);
- });
- });
-
- describe('setTokenName', () => {
- it('should throw when not called by owner', async () => {
- return expect(
- tokenReg.setTokenName(token1.address, token2.name, { from: notOwner }),
- ).to.be.rejectedWith(constants.REVERT);
- });
-
- it('should change the token name when called by owner', async () => {
- const res = await tokenReg.setTokenName(token1.address, token2.name, {
- from: owner,
- });
- expect(res.logs).to.have.length(1);
- const [newData, oldData] = await Promise.all([
- tokenRegWrapper.getTokenByNameAsync(token2.name),
- tokenRegWrapper.getTokenByNameAsync(token1.name),
- ]);
-
- const expectedNewData = _.assign({}, token1, { name: token2.name });
- const expectedOldData = nullToken;
- expect(newData).to.be.deep.equal(expectedNewData);
- expect(oldData).to.be.deep.equal(expectedOldData);
- });
-
- it('should throw if the name already exists', async () => {
- await tokenRegWrapper.addTokenAsync(token2, owner);
-
- return expect(tokenReg.setTokenName(token1.address, token2.name, { from: owner })).to.be.rejectedWith(
- constants.REVERT,
- );
- });
-
- it('should throw if token does not exist', async () => {
- return expect(
- tokenReg.setTokenName(nullToken.address, token2.name, { from: owner }),
- ).to.be.rejectedWith(constants.REVERT);
- });
- });
-
- describe('setTokenSymbol', () => {
- it('should throw when not called by owner', async () => {
- return expect(
- tokenReg.setTokenSymbol(token1.address, token2.symbol, {
- from: notOwner,
- }),
- ).to.be.rejectedWith(constants.REVERT);
- });
-
- it('should change the token symbol when called by owner', async () => {
- const res = await tokenReg.setTokenSymbol(token1.address, token2.symbol, { from: owner });
- expect(res.logs).to.have.length(1);
- const [newData, oldData] = await Promise.all([
- tokenRegWrapper.getTokenBySymbolAsync(token2.symbol),
- tokenRegWrapper.getTokenBySymbolAsync(token1.symbol),
- ]);
-
- const expectedNewData = _.assign({}, token1, { symbol: token2.symbol });
- const expectedOldData = nullToken;
- expect(newData).to.be.deep.equal(expectedNewData);
- expect(oldData).to.be.deep.equal(expectedOldData);
- });
-
- it('should throw if the symbol already exists', async () => {
- await tokenRegWrapper.addTokenAsync(token2, owner);
-
- return expect(
- tokenReg.setTokenSymbol(token1.address, token2.symbol, {
- from: owner,
- }),
- ).to.be.rejectedWith(constants.REVERT);
- });
-
- it('should throw if token does not exist', async () => {
- return expect(
- tokenReg.setTokenSymbol(nullToken.address, token2.symbol, {
- from: owner,
- }),
- ).to.be.rejectedWith(constants.REVERT);
- });
- });
-
- describe('removeToken', () => {
- it('should throw if not called by owner', async () => {
- const index = 0;
- return expect(tokenReg.removeToken(token1.address, index, { from: notOwner })).to.be.rejectedWith(
- constants.REVERT,
- );
- });
-
- it('should remove token metadata when called by owner', async () => {
- const index = 0;
- const res = await tokenReg.removeToken(token1.address, index, {
- from: owner,
- });
- expect(res.logs).to.have.length(1);
- const tokenData = await tokenRegWrapper.getTokenMetaDataAsync(token1.address);
- expect(tokenData).to.be.deep.equal(nullToken);
- });
-
- it('should throw if token does not exist', async () => {
- const index = 0;
- return expect(tokenReg.removeToken(nullToken.address, index, { from: owner })).to.be.rejectedWith(
- constants.REVERT,
- );
- });
-
- it('should throw if token at given index does not match address', async () => {
- await tokenRegWrapper.addTokenAsync(token2, owner);
- const incorrectIndex = 0;
- return expect(tokenReg.removeToken(token2.address, incorrectIndex, { from: owner })).to.be.rejectedWith(
- constants.REVERT,
- );
- });
- });
- });
+ const owner = accounts[0];
+ const notOwner = accounts[1];
+
+ const tokenAddress1 = `0x${ethUtil.setLength(ethUtil.toBuffer('0x1'), 20, false).toString('hex')}`;
+ const tokenAddress2 = `0x${ethUtil.setLength(ethUtil.toBuffer('0x2'), 20, false).toString('hex')}`;
+
+ const token1 = {
+ address: tokenAddress1,
+ name: 'testToken1',
+ symbol: 'TT1',
+ decimals: 18,
+ ipfsHash: `0x${ethUtil.sha3('ipfs1').toString('hex')}`,
+ swarmHash: `0x${ethUtil.sha3('swarm1').toString('hex')}`,
+ };
+
+ const token2 = {
+ address: tokenAddress2,
+ name: 'testToken2',
+ symbol: 'TT2',
+ decimals: 18,
+ ipfsHash: `0x${ethUtil.sha3('ipfs2').toString('hex')}`,
+ swarmHash: `0x${ethUtil.sha3('swarm2').toString('hex')}`,
+ };
+
+ const nullToken = {
+ address: ZeroEx.NULL_ADDRESS,
+ name: '',
+ symbol: '',
+ decimals: 0,
+ ipfsHash: constants.NULL_BYTES,
+ swarmHash: constants.NULL_BYTES,
+ };
+
+ let tokenReg: ContractInstance;
+ let tokenRegWrapper: TokenRegWrapper;
+
+ beforeEach(async () => {
+ tokenReg = await TokenRegistry.new();
+ tokenRegWrapper = new TokenRegWrapper(tokenReg);
+ });
+
+ describe('addToken', () => {
+ it('should throw when not called by owner', async () => {
+ return expect(tokenRegWrapper.addTokenAsync(token1, notOwner)).to.be.rejectedWith(constants.REVERT);
+ });
+
+ it('should add token metadata when called by owner', async () => {
+ await tokenRegWrapper.addTokenAsync(token1, owner);
+ const tokenData = await tokenRegWrapper.getTokenMetaDataAsync(token1.address);
+ expect(tokenData).to.be.deep.equal(token1);
+ });
+
+ it('should throw if token already exists', async () => {
+ await tokenRegWrapper.addTokenAsync(token1, owner);
+
+ return expect(tokenRegWrapper.addTokenAsync(token1, owner)).to.be.rejectedWith(constants.REVERT);
+ });
+
+ it('should throw if token address is null', async () => {
+ return expect(tokenRegWrapper.addTokenAsync(nullToken, owner)).to.be.rejectedWith(constants.REVERT);
+ });
+
+ it('should throw if name already exists', async () => {
+ await tokenRegWrapper.addTokenAsync(token1, owner);
+ const duplicateNameToken = _.assign({}, token2, { name: token1.name });
+
+ return expect(tokenRegWrapper.addTokenAsync(duplicateNameToken, owner)).to.be.rejectedWith(
+ constants.REVERT,
+ );
+ });
+
+ it('should throw if symbol already exists', async () => {
+ await tokenRegWrapper.addTokenAsync(token1, owner);
+ const duplicateSymbolToken = _.assign({}, token2, {
+ symbol: token1.symbol,
+ });
+
+ return expect(tokenRegWrapper.addTokenAsync(duplicateSymbolToken, owner)).to.be.rejectedWith(
+ constants.REVERT,
+ );
+ });
+ });
+
+ describe('after addToken', () => {
+ beforeEach(async () => {
+ await tokenRegWrapper.addTokenAsync(token1, owner);
+ });
+
+ describe('getTokenByName', () => {
+ it('should return token metadata when given the token name', async () => {
+ const tokenData = await tokenRegWrapper.getTokenByNameAsync(token1.name);
+ expect(tokenData).to.be.deep.equal(token1);
+ });
+ });
+
+ describe('getTokenBySymbol', () => {
+ it('should return token metadata when given the token symbol', async () => {
+ const tokenData = await tokenRegWrapper.getTokenBySymbolAsync(token1.symbol);
+ expect(tokenData).to.be.deep.equal(token1);
+ });
+ });
+
+ describe('setTokenName', () => {
+ it('should throw when not called by owner', async () => {
+ return expect(
+ tokenReg.setTokenName(token1.address, token2.name, { from: notOwner }),
+ ).to.be.rejectedWith(constants.REVERT);
+ });
+
+ it('should change the token name when called by owner', async () => {
+ const res = await tokenReg.setTokenName(token1.address, token2.name, {
+ from: owner,
+ });
+ expect(res.logs).to.have.length(1);
+ const [newData, oldData] = await Promise.all([
+ tokenRegWrapper.getTokenByNameAsync(token2.name),
+ tokenRegWrapper.getTokenByNameAsync(token1.name),
+ ]);
+
+ const expectedNewData = _.assign({}, token1, { name: token2.name });
+ const expectedOldData = nullToken;
+ expect(newData).to.be.deep.equal(expectedNewData);
+ expect(oldData).to.be.deep.equal(expectedOldData);
+ });
+
+ it('should throw if the name already exists', async () => {
+ await tokenRegWrapper.addTokenAsync(token2, owner);
+
+ return expect(tokenReg.setTokenName(token1.address, token2.name, { from: owner })).to.be.rejectedWith(
+ constants.REVERT,
+ );
+ });
+
+ it('should throw if token does not exist', async () => {
+ return expect(
+ tokenReg.setTokenName(nullToken.address, token2.name, { from: owner }),
+ ).to.be.rejectedWith(constants.REVERT);
+ });
+ });
+
+ describe('setTokenSymbol', () => {
+ it('should throw when not called by owner', async () => {
+ return expect(
+ tokenReg.setTokenSymbol(token1.address, token2.symbol, {
+ from: notOwner,
+ }),
+ ).to.be.rejectedWith(constants.REVERT);
+ });
+
+ it('should change the token symbol when called by owner', async () => {
+ const res = await tokenReg.setTokenSymbol(token1.address, token2.symbol, { from: owner });
+ expect(res.logs).to.have.length(1);
+ const [newData, oldData] = await Promise.all([
+ tokenRegWrapper.getTokenBySymbolAsync(token2.symbol),
+ tokenRegWrapper.getTokenBySymbolAsync(token1.symbol),
+ ]);
+
+ const expectedNewData = _.assign({}, token1, { symbol: token2.symbol });
+ const expectedOldData = nullToken;
+ expect(newData).to.be.deep.equal(expectedNewData);
+ expect(oldData).to.be.deep.equal(expectedOldData);
+ });
+
+ it('should throw if the symbol already exists', async () => {
+ await tokenRegWrapper.addTokenAsync(token2, owner);
+
+ return expect(
+ tokenReg.setTokenSymbol(token1.address, token2.symbol, {
+ from: owner,
+ }),
+ ).to.be.rejectedWith(constants.REVERT);
+ });
+
+ it('should throw if token does not exist', async () => {
+ return expect(
+ tokenReg.setTokenSymbol(nullToken.address, token2.symbol, {
+ from: owner,
+ }),
+ ).to.be.rejectedWith(constants.REVERT);
+ });
+ });
+
+ describe('removeToken', () => {
+ it('should throw if not called by owner', async () => {
+ const index = 0;
+ return expect(tokenReg.removeToken(token1.address, index, { from: notOwner })).to.be.rejectedWith(
+ constants.REVERT,
+ );
+ });
+
+ it('should remove token metadata when called by owner', async () => {
+ const index = 0;
+ const res = await tokenReg.removeToken(token1.address, index, {
+ from: owner,
+ });
+ expect(res.logs).to.have.length(1);
+ const tokenData = await tokenRegWrapper.getTokenMetaDataAsync(token1.address);
+ expect(tokenData).to.be.deep.equal(nullToken);
+ });
+
+ it('should throw if token does not exist', async () => {
+ const index = 0;
+ return expect(tokenReg.removeToken(nullToken.address, index, { from: owner })).to.be.rejectedWith(
+ constants.REVERT,
+ );
+ });
+
+ it('should throw if token at given index does not match address', async () => {
+ await tokenRegWrapper.addTokenAsync(token2, owner);
+ const incorrectIndex = 0;
+ return expect(tokenReg.removeToken(token2.address, incorrectIndex, { from: owner })).to.be.rejectedWith(
+ constants.REVERT,
+ );
+ });
+ });
+ });
});
diff --git a/packages/contracts/test/ts/token_transfer_proxy/auth.ts b/packages/contracts/test/ts/token_transfer_proxy/auth.ts
index 69a6c0ada..9ae0a8fc3 100644
--- a/packages/contracts/test/ts/token_transfer_proxy/auth.ts
+++ b/packages/contracts/test/ts/token_transfer_proxy/auth.ts
@@ -9,91 +9,91 @@ const expect = chai.expect;
const TokenTransferProxy = artifacts.require('./db/TokenTransferProxy.sol');
contract('TokenTransferProxy', (accounts: string[]) => {
- const owner = accounts[0];
- const notOwner = accounts[1];
+ const owner = accounts[0];
+ const notOwner = accounts[1];
- let tokenTransferProxy: ContractInstance;
- let authorized: string;
- let notAuthorized = owner;
+ let tokenTransferProxy: ContractInstance;
+ let authorized: string;
+ let notAuthorized = owner;
- before(async () => {
- tokenTransferProxy = await TokenTransferProxy.deployed();
- });
+ before(async () => {
+ tokenTransferProxy = await TokenTransferProxy.deployed();
+ });
- describe('addAuthorizedAddress', () => {
- it('should throw if not called by owner', async () => {
- return expect(tokenTransferProxy.addAuthorizedAddress(notOwner, { from: notOwner })).to.be.rejectedWith(
- constants.REVERT,
- );
- });
+ describe('addAuthorizedAddress', () => {
+ it('should throw if not called by owner', async () => {
+ return expect(tokenTransferProxy.addAuthorizedAddress(notOwner, { from: notOwner })).to.be.rejectedWith(
+ constants.REVERT,
+ );
+ });
- it('should allow owner to add an authorized address', async () => {
- await tokenTransferProxy.addAuthorizedAddress(notAuthorized, {
- from: owner,
- });
- authorized = notAuthorized;
- notAuthorized = null;
- const isAuthorized = await tokenTransferProxy.authorized.call(authorized);
- expect(isAuthorized).to.be.true();
- });
+ it('should allow owner to add an authorized address', async () => {
+ await tokenTransferProxy.addAuthorizedAddress(notAuthorized, {
+ from: owner,
+ });
+ authorized = notAuthorized;
+ notAuthorized = null;
+ const isAuthorized = await tokenTransferProxy.authorized.call(authorized);
+ expect(isAuthorized).to.be.true();
+ });
- it('should throw if owner attempts to authorize a duplicate address', async () => {
- return expect(tokenTransferProxy.addAuthorizedAddress(authorized, { from: owner })).to.be.rejectedWith(
- constants.REVERT,
- );
- });
- });
+ it('should throw if owner attempts to authorize a duplicate address', async () => {
+ return expect(tokenTransferProxy.addAuthorizedAddress(authorized, { from: owner })).to.be.rejectedWith(
+ constants.REVERT,
+ );
+ });
+ });
- describe('removeAuthorizedAddress', () => {
- it('should throw if not called by owner', async () => {
- return expect(
- tokenTransferProxy.removeAuthorizedAddress(authorized, {
- from: notOwner,
- }),
- ).to.be.rejectedWith(constants.REVERT);
- });
+ describe('removeAuthorizedAddress', () => {
+ it('should throw if not called by owner', async () => {
+ return expect(
+ tokenTransferProxy.removeAuthorizedAddress(authorized, {
+ from: notOwner,
+ }),
+ ).to.be.rejectedWith(constants.REVERT);
+ });
- it('should allow owner to remove an authorized address', async () => {
- await tokenTransferProxy.removeAuthorizedAddress(authorized, {
- from: owner,
- });
- notAuthorized = authorized;
- authorized = null;
+ it('should allow owner to remove an authorized address', async () => {
+ await tokenTransferProxy.removeAuthorizedAddress(authorized, {
+ from: owner,
+ });
+ notAuthorized = authorized;
+ authorized = null;
- const isAuthorized = await tokenTransferProxy.authorized.call(notAuthorized);
- expect(isAuthorized).to.be.false();
- });
+ const isAuthorized = await tokenTransferProxy.authorized.call(notAuthorized);
+ expect(isAuthorized).to.be.false();
+ });
- it('should throw if owner attempts to remove an address that is not authorized', async () => {
- return expect(
- tokenTransferProxy.removeAuthorizedAddress(notAuthorized, {
- from: owner,
- }),
- ).to.be.rejectedWith(constants.REVERT);
- });
- });
+ it('should throw if owner attempts to remove an address that is not authorized', async () => {
+ return expect(
+ tokenTransferProxy.removeAuthorizedAddress(notAuthorized, {
+ from: owner,
+ }),
+ ).to.be.rejectedWith(constants.REVERT);
+ });
+ });
- describe('getAuthorizedAddresses', () => {
- it('should return all authorized addresses', async () => {
- const initial = await tokenTransferProxy.getAuthorizedAddresses();
- expect(initial).to.have.length(1);
- await tokenTransferProxy.addAuthorizedAddress(notAuthorized, {
- from: owner,
- });
+ describe('getAuthorizedAddresses', () => {
+ it('should return all authorized addresses', async () => {
+ const initial = await tokenTransferProxy.getAuthorizedAddresses();
+ expect(initial).to.have.length(1);
+ await tokenTransferProxy.addAuthorizedAddress(notAuthorized, {
+ from: owner,
+ });
- authorized = notAuthorized;
- notAuthorized = null;
- const afterAdd = await tokenTransferProxy.getAuthorizedAddresses();
- expect(afterAdd).to.have.length(2);
- expect(afterAdd).to.include(authorized);
+ authorized = notAuthorized;
+ notAuthorized = null;
+ const afterAdd = await tokenTransferProxy.getAuthorizedAddresses();
+ expect(afterAdd).to.have.length(2);
+ expect(afterAdd).to.include(authorized);
- await tokenTransferProxy.removeAuthorizedAddress(authorized, {
- from: owner,
- });
- notAuthorized = authorized;
- authorized = null;
- const afterRemove = await tokenTransferProxy.getAuthorizedAddresses();
- expect(afterRemove).to.have.length(1);
- });
- });
+ await tokenTransferProxy.removeAuthorizedAddress(authorized, {
+ from: owner,
+ });
+ notAuthorized = authorized;
+ authorized = null;
+ const afterRemove = await tokenTransferProxy.getAuthorizedAddresses();
+ expect(afterRemove).to.have.length(1);
+ });
+ });
});
diff --git a/packages/contracts/test/ts/token_transfer_proxy/transfer_from.ts b/packages/contracts/test/ts/token_transfer_proxy/transfer_from.ts
index 847256598..e1aff6dae 100644
--- a/packages/contracts/test/ts/token_transfer_proxy/transfer_from.ts
+++ b/packages/contracts/test/ts/token_transfer_proxy/transfer_from.ts
@@ -11,63 +11,63 @@ const expect = chai.expect;
const { TokenTransferProxy, DummyToken, TokenRegistry } = new Artifacts(artifacts);
contract('TokenTransferProxy', (accounts: string[]) => {
- const INIT_BAL = 100000000;
- const INIT_ALLOW = 100000000;
+ const INIT_BAL = 100000000;
+ const INIT_ALLOW = 100000000;
- const owner = accounts[0];
- const notAuthorized = owner;
+ const owner = accounts[0];
+ const notAuthorized = owner;
- let tokenTransferProxy: ContractInstance;
- let tokenRegistry: ContractInstance;
- let rep: ContractInstance;
- let dmyBalances: Balances;
+ let tokenTransferProxy: ContractInstance;
+ let tokenRegistry: ContractInstance;
+ let rep: ContractInstance;
+ let dmyBalances: Balances;
- before(async () => {
- [tokenTransferProxy, tokenRegistry] = await Promise.all([
- TokenTransferProxy.deployed(),
- TokenRegistry.deployed(),
- ]);
- const repAddress = await tokenRegistry.getTokenAddressBySymbol('REP');
- rep = DummyToken.at(repAddress);
+ before(async () => {
+ [tokenTransferProxy, tokenRegistry] = await Promise.all([
+ TokenTransferProxy.deployed(),
+ TokenRegistry.deployed(),
+ ]);
+ const repAddress = await tokenRegistry.getTokenAddressBySymbol('REP');
+ rep = DummyToken.at(repAddress);
- dmyBalances = new Balances([rep], [accounts[0], accounts[1]]);
- await Promise.all([
- rep.approve(TokenTransferProxy.address, INIT_ALLOW, {
- from: accounts[0],
- }),
- rep.setBalance(accounts[0], INIT_BAL, { from: owner }),
- rep.approve(TokenTransferProxy.address, INIT_ALLOW, {
- from: accounts[1],
- }),
- rep.setBalance(accounts[1], INIT_BAL, { from: owner }),
- ]);
- });
+ dmyBalances = new Balances([rep], [accounts[0], accounts[1]]);
+ await Promise.all([
+ rep.approve(TokenTransferProxy.address, INIT_ALLOW, {
+ from: accounts[0],
+ }),
+ rep.setBalance(accounts[0], INIT_BAL, { from: owner }),
+ rep.approve(TokenTransferProxy.address, INIT_ALLOW, {
+ from: accounts[1],
+ }),
+ rep.setBalance(accounts[1], INIT_BAL, { from: owner }),
+ ]);
+ });
- describe('transferFrom', () => {
- it('should throw when called by an unauthorized address', async () => {
- expect(
- tokenTransferProxy.transferFrom(rep.address, accounts[0], accounts[1], 1000, { from: notAuthorized }),
- ).to.be.rejectedWith(constants.REVERT);
- });
+ describe('transferFrom', () => {
+ it('should throw when called by an unauthorized address', async () => {
+ expect(
+ tokenTransferProxy.transferFrom(rep.address, accounts[0], accounts[1], 1000, { from: notAuthorized }),
+ ).to.be.rejectedWith(constants.REVERT);
+ });
- it('should allow an authorized address to transfer', async () => {
- const balances = await dmyBalances.getAsync();
+ it('should allow an authorized address to transfer', async () => {
+ const balances = await dmyBalances.getAsync();
- await tokenTransferProxy.addAuthorizedAddress(notAuthorized, {
- from: owner,
- });
- const transferAmt = 10000;
- await tokenTransferProxy.transferFrom(rep.address, accounts[0], accounts[1], transferAmt, {
- from: notAuthorized,
- });
+ await tokenTransferProxy.addAuthorizedAddress(notAuthorized, {
+ from: owner,
+ });
+ const transferAmt = 10000;
+ await tokenTransferProxy.transferFrom(rep.address, accounts[0], accounts[1], transferAmt, {
+ from: notAuthorized,
+ });
- const newBalances = await dmyBalances.getAsync();
- expect(newBalances[accounts[0]][rep.address]).to.be.bignumber.equal(
- balances[accounts[0]][rep.address].minus(transferAmt),
- );
- expect(newBalances[accounts[1]][rep.address]).to.be.bignumber.equal(
- balances[accounts[1]][rep.address].add(transferAmt),
- );
- });
- });
+ const newBalances = await dmyBalances.getAsync();
+ expect(newBalances[accounts[0]][rep.address]).to.be.bignumber.equal(
+ balances[accounts[0]][rep.address].minus(transferAmt),
+ );
+ expect(newBalances[accounts[1]][rep.address]).to.be.bignumber.equal(
+ balances[accounts[1]][rep.address].add(transferAmt),
+ );
+ });
+ });
});
diff --git a/packages/contracts/test/ts/unlimited_allowance_token.ts b/packages/contracts/test/ts/unlimited_allowance_token.ts
index f2e070fbb..c90a52095 100644
--- a/packages/contracts/test/ts/unlimited_allowance_token.ts
+++ b/packages/contracts/test/ts/unlimited_allowance_token.ts
@@ -15,113 +15,113 @@ chaiSetup.configure();
const expect = chai.expect;
contract('UnlimitedAllowanceToken', (accounts: string[]) => {
- const config = {
- networkId: constants.TESTRPC_NETWORK_ID,
- };
- const zeroEx = new ZeroEx(web3.currentProvider, config);
- const owner = accounts[0];
- const spender = accounts[1];
-
- const MAX_MINT_VALUE = new BigNumber(100000000000000000000);
- let tokenAddress: string;
- let token: ContractInstance;
-
- beforeEach(async () => {
- token = await DummyToken.new({ from: owner });
- await token.mint(MAX_MINT_VALUE, { from: owner });
- tokenAddress = token.address;
- });
-
- describe('transfer', () => {
- it('should transfer balance from sender to receiver', async () => {
- const receiver = spender;
- const initOwnerBalance = await zeroEx.token.getBalanceAsync(tokenAddress, owner);
- const amountToTransfer = new BigNumber(1);
- await zeroEx.token.transferAsync(tokenAddress, owner, receiver, amountToTransfer);
- const finalOwnerBalance = await zeroEx.token.getBalanceAsync(tokenAddress, owner);
- const finalReceiverBalance = await zeroEx.token.getBalanceAsync(tokenAddress, receiver);
-
- const expectedFinalOwnerBalance = initOwnerBalance.minus(amountToTransfer);
- const expectedFinalReceiverBalance = amountToTransfer;
- expect(finalOwnerBalance).to.be.bignumber.equal(expectedFinalOwnerBalance);
- expect(finalReceiverBalance).to.be.bignumber.equal(expectedFinalReceiverBalance);
- });
-
- it('should return true on a 0 value transfer', async () => {
- const didReturnTrue = await token.transfer.call(spender, 0, {
- from: owner,
- });
- expect(didReturnTrue).to.be.true();
- });
- });
-
- describe('transferFrom', () => {
- it('should return false if owner has insufficient balance', async () => {
- const ownerBalance = await zeroEx.token.getBalanceAsync(tokenAddress, owner);
- const amountToTransfer = ownerBalance.plus(1);
- await zeroEx.token.setAllowanceAsync(tokenAddress, owner, spender, amountToTransfer);
- const didReturnTrue = await token.transferFrom.call(owner, spender, amountToTransfer, { from: spender });
- expect(didReturnTrue).to.be.false();
- });
-
- it('should return false if spender has insufficient allowance', async () => {
- const ownerBalance = await zeroEx.token.getBalanceAsync(tokenAddress, owner);
- const amountToTransfer = ownerBalance;
-
- const spenderAllowance = await zeroEx.token.getAllowanceAsync(tokenAddress, owner, spender);
- const spenderAllowanceIsInsufficient = spenderAllowance.cmp(amountToTransfer) < 0;
- expect(spenderAllowanceIsInsufficient).to.be.true();
-
- const didReturnTrue = await token.transferFrom.call(owner, spender, amountToTransfer, { from: spender });
- expect(didReturnTrue).to.be.false();
- });
-
- it('should return true on a 0 value transfer', async () => {
- const amountToTransfer = 0;
- const didReturnTrue = await token.transferFrom.call(owner, spender, amountToTransfer, { from: spender });
- expect(didReturnTrue).to.be.true();
- });
-
- it('should not modify spender allowance if spender allowance is 2^256 - 1', async () => {
- const initOwnerBalance = await zeroEx.token.getBalanceAsync(tokenAddress, owner);
- const amountToTransfer = initOwnerBalance;
- const initSpenderAllowance = zeroEx.token.UNLIMITED_ALLOWANCE_IN_BASE_UNITS;
- await zeroEx.token.setAllowanceAsync(tokenAddress, owner, spender, initSpenderAllowance);
- await zeroEx.token.transferFromAsync(tokenAddress, owner, spender, spender, amountToTransfer, {
- gasLimit: constants.MAX_TOKEN_TRANSFERFROM_GAS,
- });
-
- const newSpenderAllowance = await zeroEx.token.getAllowanceAsync(tokenAddress, owner, spender);
- expect(initSpenderAllowance).to.be.bignumber.equal(newSpenderAllowance);
- });
-
- it('should transfer the correct balances if spender has sufficient allowance', async () => {
- const initOwnerBalance = await zeroEx.token.getBalanceAsync(tokenAddress, owner);
- const amountToTransfer = initOwnerBalance;
- const initSpenderAllowance = initOwnerBalance;
- await zeroEx.token.setAllowanceAsync(tokenAddress, owner, spender, initSpenderAllowance);
- await zeroEx.token.transferFromAsync(tokenAddress, owner, spender, spender, amountToTransfer, {
- gasLimit: constants.MAX_TOKEN_TRANSFERFROM_GAS,
- });
-
- const newOwnerBalance = await zeroEx.token.getBalanceAsync(tokenAddress, owner);
- const newSpenderBalance = await zeroEx.token.getBalanceAsync(tokenAddress, spender);
-
- expect(newOwnerBalance).to.be.bignumber.equal(0);
- expect(newSpenderBalance).to.be.bignumber.equal(initOwnerBalance);
- });
-
- it('should modify allowance if spender has sufficient allowance less than 2^256 - 1', async () => {
- const initOwnerBalance = await zeroEx.token.getBalanceAsync(tokenAddress, owner);
- const amountToTransfer = initOwnerBalance;
- const initSpenderAllowance = initOwnerBalance;
- await zeroEx.token.setAllowanceAsync(tokenAddress, owner, spender, initSpenderAllowance);
- await zeroEx.token.transferFromAsync(tokenAddress, owner, spender, spender, amountToTransfer, {
- gasLimit: constants.MAX_TOKEN_TRANSFERFROM_GAS,
- });
-
- const newSpenderAllowance = await zeroEx.token.getAllowanceAsync(tokenAddress, owner, spender);
- expect(newSpenderAllowance).to.be.bignumber.equal(0);
- });
- });
+ const config = {
+ networkId: constants.TESTRPC_NETWORK_ID,
+ };
+ const zeroEx = new ZeroEx(web3.currentProvider, config);
+ const owner = accounts[0];
+ const spender = accounts[1];
+
+ const MAX_MINT_VALUE = new BigNumber(100000000000000000000);
+ let tokenAddress: string;
+ let token: ContractInstance;
+
+ beforeEach(async () => {
+ token = await DummyToken.new({ from: owner });
+ await token.mint(MAX_MINT_VALUE, { from: owner });
+ tokenAddress = token.address;
+ });
+
+ describe('transfer', () => {
+ it('should transfer balance from sender to receiver', async () => {
+ const receiver = spender;
+ const initOwnerBalance = await zeroEx.token.getBalanceAsync(tokenAddress, owner);
+ const amountToTransfer = new BigNumber(1);
+ await zeroEx.token.transferAsync(tokenAddress, owner, receiver, amountToTransfer);
+ const finalOwnerBalance = await zeroEx.token.getBalanceAsync(tokenAddress, owner);
+ const finalReceiverBalance = await zeroEx.token.getBalanceAsync(tokenAddress, receiver);
+
+ const expectedFinalOwnerBalance = initOwnerBalance.minus(amountToTransfer);
+ const expectedFinalReceiverBalance = amountToTransfer;
+ expect(finalOwnerBalance).to.be.bignumber.equal(expectedFinalOwnerBalance);
+ expect(finalReceiverBalance).to.be.bignumber.equal(expectedFinalReceiverBalance);
+ });
+
+ it('should return true on a 0 value transfer', async () => {
+ const didReturnTrue = await token.transfer.call(spender, 0, {
+ from: owner,
+ });
+ expect(didReturnTrue).to.be.true();
+ });
+ });
+
+ describe('transferFrom', () => {
+ it('should return false if owner has insufficient balance', async () => {
+ const ownerBalance = await zeroEx.token.getBalanceAsync(tokenAddress, owner);
+ const amountToTransfer = ownerBalance.plus(1);
+ await zeroEx.token.setAllowanceAsync(tokenAddress, owner, spender, amountToTransfer);
+ const didReturnTrue = await token.transferFrom.call(owner, spender, amountToTransfer, { from: spender });
+ expect(didReturnTrue).to.be.false();
+ });
+
+ it('should return false if spender has insufficient allowance', async () => {
+ const ownerBalance = await zeroEx.token.getBalanceAsync(tokenAddress, owner);
+ const amountToTransfer = ownerBalance;
+
+ const spenderAllowance = await zeroEx.token.getAllowanceAsync(tokenAddress, owner, spender);
+ const spenderAllowanceIsInsufficient = spenderAllowance.cmp(amountToTransfer) < 0;
+ expect(spenderAllowanceIsInsufficient).to.be.true();
+
+ const didReturnTrue = await token.transferFrom.call(owner, spender, amountToTransfer, { from: spender });
+ expect(didReturnTrue).to.be.false();
+ });
+
+ it('should return true on a 0 value transfer', async () => {
+ const amountToTransfer = 0;
+ const didReturnTrue = await token.transferFrom.call(owner, spender, amountToTransfer, { from: spender });
+ expect(didReturnTrue).to.be.true();
+ });
+
+ it('should not modify spender allowance if spender allowance is 2^256 - 1', async () => {
+ const initOwnerBalance = await zeroEx.token.getBalanceAsync(tokenAddress, owner);
+ const amountToTransfer = initOwnerBalance;
+ const initSpenderAllowance = zeroEx.token.UNLIMITED_ALLOWANCE_IN_BASE_UNITS;
+ await zeroEx.token.setAllowanceAsync(tokenAddress, owner, spender, initSpenderAllowance);
+ await zeroEx.token.transferFromAsync(tokenAddress, owner, spender, spender, amountToTransfer, {
+ gasLimit: constants.MAX_TOKEN_TRANSFERFROM_GAS,
+ });
+
+ const newSpenderAllowance = await zeroEx.token.getAllowanceAsync(tokenAddress, owner, spender);
+ expect(initSpenderAllowance).to.be.bignumber.equal(newSpenderAllowance);
+ });
+
+ it('should transfer the correct balances if spender has sufficient allowance', async () => {
+ const initOwnerBalance = await zeroEx.token.getBalanceAsync(tokenAddress, owner);
+ const amountToTransfer = initOwnerBalance;
+ const initSpenderAllowance = initOwnerBalance;
+ await zeroEx.token.setAllowanceAsync(tokenAddress, owner, spender, initSpenderAllowance);
+ await zeroEx.token.transferFromAsync(tokenAddress, owner, spender, spender, amountToTransfer, {
+ gasLimit: constants.MAX_TOKEN_TRANSFERFROM_GAS,
+ });
+
+ const newOwnerBalance = await zeroEx.token.getBalanceAsync(tokenAddress, owner);
+ const newSpenderBalance = await zeroEx.token.getBalanceAsync(tokenAddress, spender);
+
+ expect(newOwnerBalance).to.be.bignumber.equal(0);
+ expect(newSpenderBalance).to.be.bignumber.equal(initOwnerBalance);
+ });
+
+ it('should modify allowance if spender has sufficient allowance less than 2^256 - 1', async () => {
+ const initOwnerBalance = await zeroEx.token.getBalanceAsync(tokenAddress, owner);
+ const amountToTransfer = initOwnerBalance;
+ const initSpenderAllowance = initOwnerBalance;
+ await zeroEx.token.setAllowanceAsync(tokenAddress, owner, spender, initSpenderAllowance);
+ await zeroEx.token.transferFromAsync(tokenAddress, owner, spender, spender, amountToTransfer, {
+ gasLimit: constants.MAX_TOKEN_TRANSFERFROM_GAS,
+ });
+
+ const newSpenderAllowance = await zeroEx.token.getAllowanceAsync(tokenAddress, owner, spender);
+ expect(newSpenderAllowance).to.be.bignumber.equal(0);
+ });
+ });
});
diff --git a/packages/contracts/test/ts/unlimited_allowance_token_v2.ts b/packages/contracts/test/ts/unlimited_allowance_token_v2.ts
index 4f2ab2e1e..1b29a02ba 100644
--- a/packages/contracts/test/ts/unlimited_allowance_token_v2.ts
+++ b/packages/contracts/test/ts/unlimited_allowance_token_v2.ts
@@ -15,127 +15,127 @@ chaiSetup.configure();
const expect = chai.expect;
contract('UnlimitedAllowanceTokenV2', (accounts: string[]) => {
- const config = {
- networkId: constants.TESTRPC_NETWORK_ID,
- };
- const zeroEx = new ZeroEx(web3.currentProvider, config);
- const owner = accounts[0];
- const spender = accounts[1];
-
- const MAX_MINT_VALUE = new BigNumber(100000000000000000000);
- let tokenAddress: string;
- let token: ContractInstance;
-
- beforeEach(async () => {
- token = await DummyTokenV2.new({ from: owner });
- await token.mint(MAX_MINT_VALUE, { from: owner });
- tokenAddress = token.address;
- });
-
- describe('transfer', () => {
- it('should throw if owner has insufficient balance', async () => {
- const ownerBalance = await zeroEx.token.getBalanceAsync(tokenAddress, owner);
- const amountToTransfer = ownerBalance.plus(1);
- return expect(token.transfer.call(spender, amountToTransfer, { from: owner })).to.be.rejectedWith(
- constants.REVERT,
- );
- });
-
- it('should transfer balance from sender to receiver', async () => {
- const receiver = spender;
- const initOwnerBalance = await zeroEx.token.getBalanceAsync(tokenAddress, owner);
- const amountToTransfer = new BigNumber(1);
- await zeroEx.token.transferAsync(tokenAddress, owner, receiver, amountToTransfer);
- const finalOwnerBalance = await zeroEx.token.getBalanceAsync(tokenAddress, owner);
- const finalReceiverBalance = await zeroEx.token.getBalanceAsync(tokenAddress, receiver);
-
- const expectedFinalOwnerBalance = initOwnerBalance.minus(amountToTransfer);
- const expectedFinalReceiverBalance = amountToTransfer;
- expect(finalOwnerBalance).to.be.bignumber.equal(expectedFinalOwnerBalance);
- expect(finalReceiverBalance).to.be.bignumber.equal(expectedFinalReceiverBalance);
- });
-
- it('should return true on a 0 value transfer', async () => {
- const didReturnTrue = await token.transfer.call(spender, 0, {
- from: owner,
- });
- expect(didReturnTrue).to.be.true();
- });
- });
-
- describe('transferFrom', () => {
- it('should throw if owner has insufficient balance', async () => {
- const ownerBalance = await zeroEx.token.getBalanceAsync(tokenAddress, owner);
- const amountToTransfer = ownerBalance.plus(1);
- await zeroEx.token.setAllowanceAsync(tokenAddress, owner, spender, amountToTransfer);
- return expect(
- token.transferFrom.call(owner, spender, amountToTransfer, {
- from: spender,
- }),
- ).to.be.rejectedWith(constants.REVERT);
- });
-
- it('should throw if spender has insufficient allowance', async () => {
- const ownerBalance = await zeroEx.token.getBalanceAsync(tokenAddress, owner);
- const amountToTransfer = ownerBalance;
-
- const spenderAllowance = await zeroEx.token.getAllowanceAsync(tokenAddress, owner, spender);
- const spenderAllowanceIsInsufficient = spenderAllowance.cmp(amountToTransfer) < 0;
- expect(spenderAllowanceIsInsufficient).to.be.true();
-
- return expect(
- token.transferFrom.call(owner, spender, amountToTransfer, {
- from: spender,
- }),
- ).to.be.rejectedWith(constants.REVERT);
- });
-
- it('should return true on a 0 value transfer', async () => {
- const amountToTransfer = 0;
- const didReturnTrue = await token.transferFrom.call(owner, spender, amountToTransfer, { from: spender });
- expect(didReturnTrue).to.be.true();
- });
-
- it('should not modify spender allowance if spender allowance is 2^256 - 1', async () => {
- const initOwnerBalance = await zeroEx.token.getBalanceAsync(tokenAddress, owner);
- const amountToTransfer = initOwnerBalance;
- const initSpenderAllowance = zeroEx.token.UNLIMITED_ALLOWANCE_IN_BASE_UNITS;
- await zeroEx.token.setAllowanceAsync(tokenAddress, owner, spender, initSpenderAllowance);
- await zeroEx.token.transferFromAsync(tokenAddress, owner, spender, spender, amountToTransfer, {
- gasLimit: constants.MAX_TOKEN_TRANSFERFROM_GAS,
- });
-
- const newSpenderAllowance = await zeroEx.token.getAllowanceAsync(tokenAddress, owner, spender);
- expect(initSpenderAllowance).to.be.bignumber.equal(newSpenderAllowance);
- });
-
- it('should transfer the correct balances if spender has sufficient allowance', async () => {
- const initOwnerBalance = await zeroEx.token.getBalanceAsync(tokenAddress, owner);
- const amountToTransfer = initOwnerBalance;
- const initSpenderAllowance = initOwnerBalance;
- await zeroEx.token.setAllowanceAsync(tokenAddress, owner, spender, initSpenderAllowance);
- await zeroEx.token.transferFromAsync(tokenAddress, owner, spender, spender, amountToTransfer, {
- gasLimit: constants.MAX_TOKEN_TRANSFERFROM_GAS,
- });
-
- const newOwnerBalance = await zeroEx.token.getBalanceAsync(tokenAddress, owner);
- const newSpenderBalance = await zeroEx.token.getBalanceAsync(tokenAddress, spender);
-
- expect(newOwnerBalance).to.be.bignumber.equal(0);
- expect(newSpenderBalance).to.be.bignumber.equal(initOwnerBalance);
- });
-
- it('should modify allowance if spender has sufficient allowance less than 2^256 - 1', async () => {
- const initOwnerBalance = await zeroEx.token.getBalanceAsync(tokenAddress, owner);
- const amountToTransfer = initOwnerBalance;
- const initSpenderAllowance = initOwnerBalance;
- await zeroEx.token.setAllowanceAsync(tokenAddress, owner, spender, initSpenderAllowance);
- await zeroEx.token.transferFromAsync(tokenAddress, owner, spender, spender, amountToTransfer, {
- gasLimit: constants.MAX_TOKEN_TRANSFERFROM_GAS,
- });
-
- const newSpenderAllowance = await zeroEx.token.getAllowanceAsync(tokenAddress, owner, spender);
- expect(newSpenderAllowance).to.be.bignumber.equal(0);
- });
- });
+ const config = {
+ networkId: constants.TESTRPC_NETWORK_ID,
+ };
+ const zeroEx = new ZeroEx(web3.currentProvider, config);
+ const owner = accounts[0];
+ const spender = accounts[1];
+
+ const MAX_MINT_VALUE = new BigNumber(100000000000000000000);
+ let tokenAddress: string;
+ let token: ContractInstance;
+
+ beforeEach(async () => {
+ token = await DummyTokenV2.new({ from: owner });
+ await token.mint(MAX_MINT_VALUE, { from: owner });
+ tokenAddress = token.address;
+ });
+
+ describe('transfer', () => {
+ it('should throw if owner has insufficient balance', async () => {
+ const ownerBalance = await zeroEx.token.getBalanceAsync(tokenAddress, owner);
+ const amountToTransfer = ownerBalance.plus(1);
+ return expect(token.transfer.call(spender, amountToTransfer, { from: owner })).to.be.rejectedWith(
+ constants.REVERT,
+ );
+ });
+
+ it('should transfer balance from sender to receiver', async () => {
+ const receiver = spender;
+ const initOwnerBalance = await zeroEx.token.getBalanceAsync(tokenAddress, owner);
+ const amountToTransfer = new BigNumber(1);
+ await zeroEx.token.transferAsync(tokenAddress, owner, receiver, amountToTransfer);
+ const finalOwnerBalance = await zeroEx.token.getBalanceAsync(tokenAddress, owner);
+ const finalReceiverBalance = await zeroEx.token.getBalanceAsync(tokenAddress, receiver);
+
+ const expectedFinalOwnerBalance = initOwnerBalance.minus(amountToTransfer);
+ const expectedFinalReceiverBalance = amountToTransfer;
+ expect(finalOwnerBalance).to.be.bignumber.equal(expectedFinalOwnerBalance);
+ expect(finalReceiverBalance).to.be.bignumber.equal(expectedFinalReceiverBalance);
+ });
+
+ it('should return true on a 0 value transfer', async () => {
+ const didReturnTrue = await token.transfer.call(spender, 0, {
+ from: owner,
+ });
+ expect(didReturnTrue).to.be.true();
+ });
+ });
+
+ describe('transferFrom', () => {
+ it('should throw if owner has insufficient balance', async () => {
+ const ownerBalance = await zeroEx.token.getBalanceAsync(tokenAddress, owner);
+ const amountToTransfer = ownerBalance.plus(1);
+ await zeroEx.token.setAllowanceAsync(tokenAddress, owner, spender, amountToTransfer);
+ return expect(
+ token.transferFrom.call(owner, spender, amountToTransfer, {
+ from: spender,
+ }),
+ ).to.be.rejectedWith(constants.REVERT);
+ });
+
+ it('should throw if spender has insufficient allowance', async () => {
+ const ownerBalance = await zeroEx.token.getBalanceAsync(tokenAddress, owner);
+ const amountToTransfer = ownerBalance;
+
+ const spenderAllowance = await zeroEx.token.getAllowanceAsync(tokenAddress, owner, spender);
+ const spenderAllowanceIsInsufficient = spenderAllowance.cmp(amountToTransfer) < 0;
+ expect(spenderAllowanceIsInsufficient).to.be.true();
+
+ return expect(
+ token.transferFrom.call(owner, spender, amountToTransfer, {
+ from: spender,
+ }),
+ ).to.be.rejectedWith(constants.REVERT);
+ });
+
+ it('should return true on a 0 value transfer', async () => {
+ const amountToTransfer = 0;
+ const didReturnTrue = await token.transferFrom.call(owner, spender, amountToTransfer, { from: spender });
+ expect(didReturnTrue).to.be.true();
+ });
+
+ it('should not modify spender allowance if spender allowance is 2^256 - 1', async () => {
+ const initOwnerBalance = await zeroEx.token.getBalanceAsync(tokenAddress, owner);
+ const amountToTransfer = initOwnerBalance;
+ const initSpenderAllowance = zeroEx.token.UNLIMITED_ALLOWANCE_IN_BASE_UNITS;
+ await zeroEx.token.setAllowanceAsync(tokenAddress, owner, spender, initSpenderAllowance);
+ await zeroEx.token.transferFromAsync(tokenAddress, owner, spender, spender, amountToTransfer, {
+ gasLimit: constants.MAX_TOKEN_TRANSFERFROM_GAS,
+ });
+
+ const newSpenderAllowance = await zeroEx.token.getAllowanceAsync(tokenAddress, owner, spender);
+ expect(initSpenderAllowance).to.be.bignumber.equal(newSpenderAllowance);
+ });
+
+ it('should transfer the correct balances if spender has sufficient allowance', async () => {
+ const initOwnerBalance = await zeroEx.token.getBalanceAsync(tokenAddress, owner);
+ const amountToTransfer = initOwnerBalance;
+ const initSpenderAllowance = initOwnerBalance;
+ await zeroEx.token.setAllowanceAsync(tokenAddress, owner, spender, initSpenderAllowance);
+ await zeroEx.token.transferFromAsync(tokenAddress, owner, spender, spender, amountToTransfer, {
+ gasLimit: constants.MAX_TOKEN_TRANSFERFROM_GAS,
+ });
+
+ const newOwnerBalance = await zeroEx.token.getBalanceAsync(tokenAddress, owner);
+ const newSpenderBalance = await zeroEx.token.getBalanceAsync(tokenAddress, spender);
+
+ expect(newOwnerBalance).to.be.bignumber.equal(0);
+ expect(newSpenderBalance).to.be.bignumber.equal(initOwnerBalance);
+ });
+
+ it('should modify allowance if spender has sufficient allowance less than 2^256 - 1', async () => {
+ const initOwnerBalance = await zeroEx.token.getBalanceAsync(tokenAddress, owner);
+ const amountToTransfer = initOwnerBalance;
+ const initSpenderAllowance = initOwnerBalance;
+ await zeroEx.token.setAllowanceAsync(tokenAddress, owner, spender, initSpenderAllowance);
+ await zeroEx.token.transferFromAsync(tokenAddress, owner, spender, spender, amountToTransfer, {
+ gasLimit: constants.MAX_TOKEN_TRANSFERFROM_GAS,
+ });
+
+ const newSpenderAllowance = await zeroEx.token.getAllowanceAsync(tokenAddress, owner, spender);
+ expect(newSpenderAllowance).to.be.bignumber.equal(0);
+ });
+ });
});
diff --git a/packages/contracts/test/ts/utils/chai_setup.ts b/packages/contracts/test/ts/utils/chai_setup.ts
index e156b5f7c..078edd309 100644
--- a/packages/contracts/test/ts/utils/chai_setup.ts
+++ b/packages/contracts/test/ts/utils/chai_setup.ts
@@ -4,10 +4,10 @@ import ChaiBigNumber = require('chai-bignumber');
import * as dirtyChai from 'dirty-chai';
export const chaiSetup = {
- configure() {
- chai.config.includeStack = true;
- chai.use(ChaiBigNumber());
- chai.use(dirtyChai);
- chai.use(chaiAsPromised);
- },
+ configure() {
+ chai.config.includeStack = true;
+ chai.use(ChaiBigNumber());
+ chai.use(dirtyChai);
+ chai.use(chaiAsPromised);
+ },
};
diff --git a/packages/contracts/test/ts/zrx_token.ts b/packages/contracts/test/ts/zrx_token.ts
index a0b11cdb6..766c94c2a 100644
--- a/packages/contracts/test/ts/zrx_token.ts
+++ b/packages/contracts/test/ts/zrx_token.ts
@@ -15,161 +15,161 @@ const { Exchange, ZRXToken } = new Artifacts(artifacts);
const web3: Web3 = (global as any).web3;
contract('ZRXToken', (accounts: string[]) => {
- const owner = accounts[0];
- const spender = accounts[1];
- let zeroEx: ZeroEx;
-
- let MAX_UINT: BigNumber;
-
- let zrx: ContractInstance;
- let zrxAddress: string;
-
- beforeEach(async () => {
- zeroEx = new ZeroEx(web3.currentProvider, {
- exchangeContractAddress: Exchange.address,
- networkId: constants.TESTRPC_NETWORK_ID,
- });
- zrx = await ZRXToken.new();
- zrxAddress = zrx.address;
- MAX_UINT = zeroEx.token.UNLIMITED_ALLOWANCE_IN_BASE_UNITS;
- });
-
- describe('constants', () => {
- it('should have 18 decimals', async () => {
- const decimals = new BigNumber(await zrx.decimals.call());
- const expectedDecimals = 18;
- expect(decimals).to.be.bignumber.equal(expectedDecimals);
- });
-
- it('should have a total supply of 1 billion tokens', async () => {
- const totalSupply = new BigNumber(await zrx.totalSupply.call());
- const expectedTotalSupply = 1000000000;
- expect(ZeroEx.toUnitAmount(totalSupply, 18)).to.be.bignumber.equal(expectedTotalSupply);
- });
-
- it('should be named 0x Protocol Token', async () => {
- const name = await zrx.name.call();
- const expectedName = '0x Protocol Token';
- expect(name).to.be.equal(expectedName);
- });
-
- it('should have the symbol ZRX', async () => {
- const symbol = await zrx.symbol.call();
- const expectedSymbol = 'ZRX';
- expect(symbol).to.be.equal(expectedSymbol);
- });
- });
-
- describe('constructor', () => {
- it('should initialize owner balance to totalSupply', async () => {
- const ownerBalance = await zeroEx.token.getBalanceAsync(zrxAddress, owner);
- const totalSupply = new BigNumber(await zrx.totalSupply.call());
- expect(totalSupply).to.be.bignumber.equal(ownerBalance);
- });
- });
-
- describe('transfer', () => {
- it('should transfer balance from sender to receiver', async () => {
- const receiver = spender;
- const initOwnerBalance = await zeroEx.token.getBalanceAsync(zrxAddress, owner);
- const amountToTransfer = new BigNumber(1);
- const txHash = await zeroEx.token.transferAsync(zrxAddress, owner, receiver, amountToTransfer);
- await zeroEx.awaitTransactionMinedAsync(txHash);
- const finalOwnerBalance = await zeroEx.token.getBalanceAsync(zrxAddress, owner);
- const finalReceiverBalance = await zeroEx.token.getBalanceAsync(zrxAddress, receiver);
-
- const expectedFinalOwnerBalance = initOwnerBalance.minus(amountToTransfer);
- const expectedFinalReceiverBalance = amountToTransfer;
- expect(finalOwnerBalance).to.be.bignumber.equal(expectedFinalOwnerBalance);
- expect(finalReceiverBalance).to.be.bignumber.equal(expectedFinalReceiverBalance);
- });
-
- it('should return true on a 0 value transfer', async () => {
- const didReturnTrue = await zrx.transfer.call(spender, 0, {
- from: owner,
- });
- expect(didReturnTrue).to.be.true();
- });
- });
-
- describe('transferFrom', () => {
- it('should return false if owner has insufficient balance', async () => {
- const ownerBalance = await zeroEx.token.getBalanceAsync(zrxAddress, owner);
- const amountToTransfer = ownerBalance.plus(1);
- const txHash = await zeroEx.token.setAllowanceAsync(zrxAddress, owner, spender, amountToTransfer, {
- gasLimit: constants.MAX_TOKEN_APPROVE_GAS,
- });
- await zeroEx.awaitTransactionMinedAsync(txHash);
- const didReturnTrue = await zrx.transferFrom.call(owner, spender, amountToTransfer, { from: spender });
- expect(didReturnTrue).to.be.false();
- });
-
- it('should return false if spender has insufficient allowance', async () => {
- const ownerBalance = await zeroEx.token.getBalanceAsync(zrxAddress, owner);
- const amountToTransfer = ownerBalance;
-
- const spenderAllowance = await zeroEx.token.getAllowanceAsync(zrxAddress, owner, spender);
- const spenderAllowanceIsInsufficient = spenderAllowance.cmp(amountToTransfer) < 0;
- expect(spenderAllowanceIsInsufficient).to.be.true();
-
- const didReturnTrue = await zrx.transferFrom.call(owner, spender, amountToTransfer, { from: spender });
- expect(didReturnTrue).to.be.false();
- });
-
- it('should return true on a 0 value transfer', async () => {
- const amountToTransfer = 0;
- const didReturnTrue = await zrx.transferFrom.call(owner, spender, amountToTransfer, { from: spender });
- expect(didReturnTrue).to.be.true();
- });
-
- it('should not modify spender allowance if spender allowance is 2^256 - 1', async () => {
- const initOwnerBalance = await zeroEx.token.getBalanceAsync(zrxAddress, owner);
- const amountToTransfer = initOwnerBalance;
- const initSpenderAllowance = MAX_UINT;
- let txHash = await zeroEx.token.setAllowanceAsync(zrxAddress, owner, spender, initSpenderAllowance, {
- gasLimit: constants.MAX_TOKEN_APPROVE_GAS,
- });
- await zeroEx.awaitTransactionMinedAsync(txHash);
- txHash = await zeroEx.token.transferFromAsync(zrxAddress, owner, spender, spender, amountToTransfer, {
- gasLimit: constants.MAX_TOKEN_TRANSFERFROM_GAS,
- });
- await zeroEx.awaitTransactionMinedAsync(txHash);
-
- const newSpenderAllowance = await zeroEx.token.getAllowanceAsync(zrxAddress, owner, spender);
- expect(initSpenderAllowance).to.be.bignumber.equal(newSpenderAllowance);
- });
-
- it('should transfer the correct balances if spender has sufficient allowance', async () => {
- const initOwnerBalance = await zeroEx.token.getBalanceAsync(zrxAddress, owner);
- const initSpenderBalance = await zeroEx.token.getBalanceAsync(zrxAddress, spender);
- const amountToTransfer = initOwnerBalance;
- const initSpenderAllowance = initOwnerBalance;
- let txHash = await zeroEx.token.setAllowanceAsync(zrxAddress, owner, spender, initSpenderAllowance);
- await zeroEx.awaitTransactionMinedAsync(txHash);
- txHash = await zeroEx.token.transferFromAsync(zrxAddress, owner, spender, spender, amountToTransfer, {
- gasLimit: constants.MAX_TOKEN_TRANSFERFROM_GAS,
- });
- await zeroEx.awaitTransactionMinedAsync(txHash);
-
- const newOwnerBalance = await zeroEx.token.getBalanceAsync(zrxAddress, owner);
- const newSpenderBalance = await zeroEx.token.getBalanceAsync(zrxAddress, spender);
-
- expect(newOwnerBalance).to.be.bignumber.equal(0);
- expect(newSpenderBalance).to.be.bignumber.equal(initSpenderBalance.plus(initOwnerBalance));
- });
-
- it('should modify allowance if spender has sufficient allowance less than 2^256 - 1', async () => {
- const initOwnerBalance = await zeroEx.token.getBalanceAsync(zrxAddress, owner);
- const amountToTransfer = initOwnerBalance;
- let txHash = await zeroEx.token.setAllowanceAsync(zrxAddress, owner, spender, amountToTransfer);
- await zeroEx.awaitTransactionMinedAsync(txHash);
- txHash = await zeroEx.token.transferFromAsync(zrxAddress, owner, spender, spender, amountToTransfer, {
- gasLimit: constants.MAX_TOKEN_TRANSFERFROM_GAS,
- });
- await zeroEx.awaitTransactionMinedAsync(txHash);
-
- const newSpenderAllowance = await zeroEx.token.getAllowanceAsync(zrxAddress, owner, spender);
- expect(newSpenderAllowance).to.be.bignumber.equal(0);
- });
- });
+ const owner = accounts[0];
+ const spender = accounts[1];
+ let zeroEx: ZeroEx;
+
+ let MAX_UINT: BigNumber;
+
+ let zrx: ContractInstance;
+ let zrxAddress: string;
+
+ beforeEach(async () => {
+ zeroEx = new ZeroEx(web3.currentProvider, {
+ exchangeContractAddress: Exchange.address,
+ networkId: constants.TESTRPC_NETWORK_ID,
+ });
+ zrx = await ZRXToken.new();
+ zrxAddress = zrx.address;
+ MAX_UINT = zeroEx.token.UNLIMITED_ALLOWANCE_IN_BASE_UNITS;
+ });
+
+ describe('constants', () => {
+ it('should have 18 decimals', async () => {
+ const decimals = new BigNumber(await zrx.decimals.call());
+ const expectedDecimals = 18;
+ expect(decimals).to.be.bignumber.equal(expectedDecimals);
+ });
+
+ it('should have a total supply of 1 billion tokens', async () => {
+ const totalSupply = new BigNumber(await zrx.totalSupply.call());
+ const expectedTotalSupply = 1000000000;
+ expect(ZeroEx.toUnitAmount(totalSupply, 18)).to.be.bignumber.equal(expectedTotalSupply);
+ });
+
+ it('should be named 0x Protocol Token', async () => {
+ const name = await zrx.name.call();
+ const expectedName = '0x Protocol Token';
+ expect(name).to.be.equal(expectedName);
+ });
+
+ it('should have the symbol ZRX', async () => {
+ const symbol = await zrx.symbol.call();
+ const expectedSymbol = 'ZRX';
+ expect(symbol).to.be.equal(expectedSymbol);
+ });
+ });
+
+ describe('constructor', () => {
+ it('should initialize owner balance to totalSupply', async () => {
+ const ownerBalance = await zeroEx.token.getBalanceAsync(zrxAddress, owner);
+ const totalSupply = new BigNumber(await zrx.totalSupply.call());
+ expect(totalSupply).to.be.bignumber.equal(ownerBalance);
+ });
+ });
+
+ describe('transfer', () => {
+ it('should transfer balance from sender to receiver', async () => {
+ const receiver = spender;
+ const initOwnerBalance = await zeroEx.token.getBalanceAsync(zrxAddress, owner);
+ const amountToTransfer = new BigNumber(1);
+ const txHash = await zeroEx.token.transferAsync(zrxAddress, owner, receiver, amountToTransfer);
+ await zeroEx.awaitTransactionMinedAsync(txHash);
+ const finalOwnerBalance = await zeroEx.token.getBalanceAsync(zrxAddress, owner);
+ const finalReceiverBalance = await zeroEx.token.getBalanceAsync(zrxAddress, receiver);
+
+ const expectedFinalOwnerBalance = initOwnerBalance.minus(amountToTransfer);
+ const expectedFinalReceiverBalance = amountToTransfer;
+ expect(finalOwnerBalance).to.be.bignumber.equal(expectedFinalOwnerBalance);
+ expect(finalReceiverBalance).to.be.bignumber.equal(expectedFinalReceiverBalance);
+ });
+
+ it('should return true on a 0 value transfer', async () => {
+ const didReturnTrue = await zrx.transfer.call(spender, 0, {
+ from: owner,
+ });
+ expect(didReturnTrue).to.be.true();
+ });
+ });
+
+ describe('transferFrom', () => {
+ it('should return false if owner has insufficient balance', async () => {
+ const ownerBalance = await zeroEx.token.getBalanceAsync(zrxAddress, owner);
+ const amountToTransfer = ownerBalance.plus(1);
+ const txHash = await zeroEx.token.setAllowanceAsync(zrxAddress, owner, spender, amountToTransfer, {
+ gasLimit: constants.MAX_TOKEN_APPROVE_GAS,
+ });
+ await zeroEx.awaitTransactionMinedAsync(txHash);
+ const didReturnTrue = await zrx.transferFrom.call(owner, spender, amountToTransfer, { from: spender });
+ expect(didReturnTrue).to.be.false();
+ });
+
+ it('should return false if spender has insufficient allowance', async () => {
+ const ownerBalance = await zeroEx.token.getBalanceAsync(zrxAddress, owner);
+ const amountToTransfer = ownerBalance;
+
+ const spenderAllowance = await zeroEx.token.getAllowanceAsync(zrxAddress, owner, spender);
+ const spenderAllowanceIsInsufficient = spenderAllowance.cmp(amountToTransfer) < 0;
+ expect(spenderAllowanceIsInsufficient).to.be.true();
+
+ const didReturnTrue = await zrx.transferFrom.call(owner, spender, amountToTransfer, { from: spender });
+ expect(didReturnTrue).to.be.false();
+ });
+
+ it('should return true on a 0 value transfer', async () => {
+ const amountToTransfer = 0;
+ const didReturnTrue = await zrx.transferFrom.call(owner, spender, amountToTransfer, { from: spender });
+ expect(didReturnTrue).to.be.true();
+ });
+
+ it('should not modify spender allowance if spender allowance is 2^256 - 1', async () => {
+ const initOwnerBalance = await zeroEx.token.getBalanceAsync(zrxAddress, owner);
+ const amountToTransfer = initOwnerBalance;
+ const initSpenderAllowance = MAX_UINT;
+ let txHash = await zeroEx.token.setAllowanceAsync(zrxAddress, owner, spender, initSpenderAllowance, {
+ gasLimit: constants.MAX_TOKEN_APPROVE_GAS,
+ });
+ await zeroEx.awaitTransactionMinedAsync(txHash);
+ txHash = await zeroEx.token.transferFromAsync(zrxAddress, owner, spender, spender, amountToTransfer, {
+ gasLimit: constants.MAX_TOKEN_TRANSFERFROM_GAS,
+ });
+ await zeroEx.awaitTransactionMinedAsync(txHash);
+
+ const newSpenderAllowance = await zeroEx.token.getAllowanceAsync(zrxAddress, owner, spender);
+ expect(initSpenderAllowance).to.be.bignumber.equal(newSpenderAllowance);
+ });
+
+ it('should transfer the correct balances if spender has sufficient allowance', async () => {
+ const initOwnerBalance = await zeroEx.token.getBalanceAsync(zrxAddress, owner);
+ const initSpenderBalance = await zeroEx.token.getBalanceAsync(zrxAddress, spender);
+ const amountToTransfer = initOwnerBalance;
+ const initSpenderAllowance = initOwnerBalance;
+ let txHash = await zeroEx.token.setAllowanceAsync(zrxAddress, owner, spender, initSpenderAllowance);
+ await zeroEx.awaitTransactionMinedAsync(txHash);
+ txHash = await zeroEx.token.transferFromAsync(zrxAddress, owner, spender, spender, amountToTransfer, {
+ gasLimit: constants.MAX_TOKEN_TRANSFERFROM_GAS,
+ });
+ await zeroEx.awaitTransactionMinedAsync(txHash);
+
+ const newOwnerBalance = await zeroEx.token.getBalanceAsync(zrxAddress, owner);
+ const newSpenderBalance = await zeroEx.token.getBalanceAsync(zrxAddress, spender);
+
+ expect(newOwnerBalance).to.be.bignumber.equal(0);
+ expect(newSpenderBalance).to.be.bignumber.equal(initSpenderBalance.plus(initOwnerBalance));
+ });
+
+ it('should modify allowance if spender has sufficient allowance less than 2^256 - 1', async () => {
+ const initOwnerBalance = await zeroEx.token.getBalanceAsync(zrxAddress, owner);
+ const amountToTransfer = initOwnerBalance;
+ let txHash = await zeroEx.token.setAllowanceAsync(zrxAddress, owner, spender, amountToTransfer);
+ await zeroEx.awaitTransactionMinedAsync(txHash);
+ txHash = await zeroEx.token.transferFromAsync(zrxAddress, owner, spender, spender, amountToTransfer, {
+ gasLimit: constants.MAX_TOKEN_TRANSFERFROM_GAS,
+ });
+ await zeroEx.awaitTransactionMinedAsync(txHash);
+
+ const newSpenderAllowance = await zeroEx.token.getAllowanceAsync(zrxAddress, owner, spender);
+ expect(newSpenderAllowance).to.be.bignumber.equal(0);
+ });
+ });
});
diff --git a/packages/contracts/tsconfig.json b/packages/contracts/tsconfig.json
index e88b3c3f0..38008a542 100644
--- a/packages/contracts/tsconfig.json
+++ b/packages/contracts/tsconfig.json
@@ -1,25 +1,25 @@
{
- "extends": "../../tsconfig",
- "compilerOptions": {
- "outDir": "lib",
- "baseUrl": ".",
- "declaration": false,
- "strictNullChecks": false,
- "strictFunctionTypes": false,
- "allowJs": true
- },
- "include": [
- "../../node_modules/types-ethereumjs-util/index.d.ts",
- "../../node_modules/chai-typescript-typings/index.d.ts",
- "../../node_modules/web3-typescript-typings/index.d.ts",
- "../../node_modules/chai-as-promised-typescript-typings/index.d.ts",
- "../../node_modules/types-ethereumjs-util/index.d.ts",
- "../../node_modules/types-bn/index.d.ts",
- "./globals.d.ts",
- "./util/**/*",
- "./test/**/*",
- "./migrations/**/*",
- "./deploy/**/*"
- ],
- "exclude": ["./deploy/solc/solc_bin"]
+ "extends": "../../tsconfig",
+ "compilerOptions": {
+ "outDir": "lib",
+ "baseUrl": ".",
+ "declaration": false,
+ "strictNullChecks": false,
+ "strictFunctionTypes": false,
+ "allowJs": true
+ },
+ "include": [
+ "../../node_modules/types-ethereumjs-util/index.d.ts",
+ "../../node_modules/chai-typescript-typings/index.d.ts",
+ "../../node_modules/web3-typescript-typings/index.d.ts",
+ "../../node_modules/chai-as-promised-typescript-typings/index.d.ts",
+ "../../node_modules/types-ethereumjs-util/index.d.ts",
+ "../../node_modules/types-bn/index.d.ts",
+ "./globals.d.ts",
+ "./util/**/*",
+ "./test/**/*",
+ "./migrations/**/*",
+ "./deploy/**/*"
+ ],
+ "exclude": ["./deploy/solc/solc_bin"]
}
diff --git a/packages/contracts/tslint.json b/packages/contracts/tslint.json
index e63054bfc..ffaefe83a 100644
--- a/packages/contracts/tslint.json
+++ b/packages/contracts/tslint.json
@@ -1,3 +1,3 @@
{
- "extends": ["@0xproject/tslint-config"]
+ "extends": ["@0xproject/tslint-config"]
}
diff --git a/packages/contracts/util/artifacts.ts b/packages/contracts/util/artifacts.ts
index ab9518d41..ecb18cbce 100644
--- a/packages/contracts/util/artifacts.ts
+++ b/packages/contracts/util/artifacts.ts
@@ -1,28 +1,28 @@
export class Artifacts {
- public Migrations: any;
- public TokenTransferProxy: any;
- public TokenRegistry: any;
- public MultiSigWalletWithTimeLock: any;
- public Exchange: any;
- public ZRXToken: any;
- public DummyToken: any;
- public DummyTokenV2: any;
- public EtherToken: any;
- public MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress: any;
- public MaliciousToken: any;
- constructor(artifacts: any) {
- this.Migrations = artifacts.require('Migrations');
- this.TokenTransferProxy = artifacts.require('TokenTransferProxy');
- this.TokenRegistry = artifacts.require('TokenRegistry');
- this.MultiSigWalletWithTimeLock = artifacts.require('MultiSigWalletWithTimeLock');
- this.Exchange = artifacts.require('Exchange');
- this.ZRXToken = artifacts.require('ZRXToken');
- this.DummyToken = artifacts.require('DummyToken');
- this.DummyTokenV2 = artifacts.require('DummyToken_v2');
- this.EtherToken = artifacts.require('WETH9');
- this.MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress = artifacts.require(
- 'MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress',
- );
- this.MaliciousToken = artifacts.require('MaliciousToken');
- }
+ public Migrations: any;
+ public TokenTransferProxy: any;
+ public TokenRegistry: any;
+ public MultiSigWalletWithTimeLock: any;
+ public Exchange: any;
+ public ZRXToken: any;
+ public DummyToken: any;
+ public DummyTokenV2: any;
+ public EtherToken: any;
+ public MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress: any;
+ public MaliciousToken: any;
+ constructor(artifacts: any) {
+ this.Migrations = artifacts.require('Migrations');
+ this.TokenTransferProxy = artifacts.require('TokenTransferProxy');
+ this.TokenRegistry = artifacts.require('TokenRegistry');
+ this.MultiSigWalletWithTimeLock = artifacts.require('MultiSigWalletWithTimeLock');
+ this.Exchange = artifacts.require('Exchange');
+ this.ZRXToken = artifacts.require('ZRXToken');
+ this.DummyToken = artifacts.require('DummyToken');
+ this.DummyTokenV2 = artifacts.require('DummyToken_v2');
+ this.EtherToken = artifacts.require('WETH9');
+ this.MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress = artifacts.require(
+ 'MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress',
+ );
+ this.MaliciousToken = artifacts.require('MaliciousToken');
+ }
}
diff --git a/packages/contracts/util/balances.ts b/packages/contracts/util/balances.ts
index f8c103670..6a1659ab1 100644
--- a/packages/contracts/util/balances.ts
+++ b/packages/contracts/util/balances.ts
@@ -4,24 +4,24 @@ import * as _ from 'lodash';
import { BalancesByOwner, ContractInstance } from './types';
export class Balances {
- private _tokenContractInstances: ContractInstance[];
- private _ownerAddresses: string[];
- constructor(tokenContractInstances: ContractInstance[], ownerAddresses: string[]) {
- this._tokenContractInstances = tokenContractInstances;
- this._ownerAddresses = ownerAddresses;
- }
- public async getAsync(): Promise<BalancesByOwner> {
- const balancesByOwner: BalancesByOwner = {};
- for (const tokenContractInstance of this._tokenContractInstances) {
- for (const ownerAddress of this._ownerAddresses) {
- let balance = await tokenContractInstance.balanceOf(ownerAddress);
- balance = new BigNumber(balance);
- if (_.isUndefined(balancesByOwner[ownerAddress])) {
- balancesByOwner[ownerAddress] = {};
- }
- balancesByOwner[ownerAddress][tokenContractInstance.address] = balance;
- }
- }
- return balancesByOwner;
- }
+ private _tokenContractInstances: ContractInstance[];
+ private _ownerAddresses: string[];
+ constructor(tokenContractInstances: ContractInstance[], ownerAddresses: string[]) {
+ this._tokenContractInstances = tokenContractInstances;
+ this._ownerAddresses = ownerAddresses;
+ }
+ public async getAsync(): Promise<BalancesByOwner> {
+ const balancesByOwner: BalancesByOwner = {};
+ for (const tokenContractInstance of this._tokenContractInstances) {
+ for (const ownerAddress of this._ownerAddresses) {
+ let balance = await tokenContractInstance.balanceOf(ownerAddress);
+ balance = new BigNumber(balance);
+ if (_.isUndefined(balancesByOwner[ownerAddress])) {
+ balancesByOwner[ownerAddress] = {};
+ }
+ balancesByOwner[ownerAddress][tokenContractInstance.address] = balance;
+ }
+ }
+ return balancesByOwner;
+ }
}
diff --git a/packages/contracts/util/constants.ts b/packages/contracts/util/constants.ts
index 85fd31be3..e61b2f802 100644
--- a/packages/contracts/util/constants.ts
+++ b/packages/contracts/util/constants.ts
@@ -1,9 +1,9 @@
export const constants = {
- NULL_BYTES: '0x',
- INVALID_OPCODE: 'invalid opcode',
- REVERT: 'revert',
- TESTRPC_NETWORK_ID: 50,
- MAX_ETHERTOKEN_WITHDRAW_GAS: 43000,
- MAX_TOKEN_TRANSFERFROM_GAS: 80000,
- MAX_TOKEN_APPROVE_GAS: 60000,
+ NULL_BYTES: '0x',
+ INVALID_OPCODE: 'invalid opcode',
+ REVERT: 'revert',
+ TESTRPC_NETWORK_ID: 50,
+ MAX_ETHERTOKEN_WITHDRAW_GAS: 43000,
+ MAX_TOKEN_TRANSFERFROM_GAS: 80000,
+ MAX_TOKEN_APPROVE_GAS: 60000,
};
diff --git a/packages/contracts/util/crypto.ts b/packages/contracts/util/crypto.ts
index 29bbf206a..9173df643 100644
--- a/packages/contracts/util/crypto.ts
+++ b/packages/contracts/util/crypto.ts
@@ -4,7 +4,7 @@ import ethUtil = require('ethereumjs-util');
import * as _ from 'lodash';
export const crypto = {
- /*
+ /*
* We convert types from JS to Solidity as follows:
* BigNumber -> uint256
* number -> uint8
@@ -12,26 +12,26 @@ export const crypto = {
* boolean -> bool
* valid Ethereum address -> address
*/
- solSHA3(args: any[]): Buffer {
- const argTypes: string[] = [];
- _.each(args, (arg, i) => {
- const isNumber = _.isFinite(arg);
- if (isNumber) {
- argTypes.push('uint8');
- } else if (arg.isBigNumber) {
- argTypes.push('uint256');
- args[i] = new BN(arg.toString(10), 10);
- } else if (ethUtil.isValidAddress(arg)) {
- argTypes.push('address');
- } else if (_.isString(arg)) {
- argTypes.push('string');
- } else if (_.isBoolean(arg)) {
- argTypes.push('bool');
- } else {
- throw new Error(`Unable to guess arg type: ${arg}`);
- }
- });
- const hash = ABI.soliditySHA3(argTypes, args);
- return hash;
- },
+ solSHA3(args: any[]): Buffer {
+ const argTypes: string[] = [];
+ _.each(args, (arg, i) => {
+ const isNumber = _.isFinite(arg);
+ if (isNumber) {
+ argTypes.push('uint8');
+ } else if (arg.isBigNumber) {
+ argTypes.push('uint256');
+ args[i] = new BN(arg.toString(10), 10);
+ } else if (ethUtil.isValidAddress(arg)) {
+ argTypes.push('address');
+ } else if (_.isString(arg)) {
+ argTypes.push('string');
+ } else if (_.isBoolean(arg)) {
+ argTypes.push('bool');
+ } else {
+ throw new Error(`Unable to guess arg type: ${arg}`);
+ }
+ });
+ const hash = ABI.soliditySHA3(argTypes, args);
+ return hash;
+ },
};
diff --git a/packages/contracts/util/exchange_wrapper.ts b/packages/contracts/util/exchange_wrapper.ts
index 3a3c44aae..ca79f92c4 100644
--- a/packages/contracts/util/exchange_wrapper.ts
+++ b/packages/contracts/util/exchange_wrapper.ts
@@ -6,186 +6,186 @@ import { Order } from './order';
import { ContractInstance } from './types';
export class ExchangeWrapper {
- private _exchange: ContractInstance;
- constructor(exchangeContractInstance: ContractInstance) {
- this._exchange = exchangeContractInstance;
- }
- public async fillOrderAsync(
- order: Order,
- from: string,
- opts: {
- fillTakerTokenAmount?: BigNumber;
- shouldThrowOnInsufficientBalanceOrAllowance?: boolean;
- } = {},
- ) {
- const shouldThrowOnInsufficientBalanceOrAllowance = !!opts.shouldThrowOnInsufficientBalanceOrAllowance;
- const params = order.createFill(shouldThrowOnInsufficientBalanceOrAllowance, opts.fillTakerTokenAmount);
- const tx = await this._exchange.fillOrder(
- params.orderAddresses,
- params.orderValues,
- params.fillTakerTokenAmount,
- params.shouldThrowOnInsufficientBalanceOrAllowance,
- params.v,
- params.r,
- params.s,
- { from },
- );
- _.each(tx.logs, log => wrapLogBigNumbers(log));
- return tx;
- }
- public async cancelOrderAsync(order: Order, from: string, opts: { cancelTakerTokenAmount?: BigNumber } = {}) {
- const params = order.createCancel(opts.cancelTakerTokenAmount);
- const tx = await this._exchange.cancelOrder(
- params.orderAddresses,
- params.orderValues,
- params.cancelTakerTokenAmount,
- { from },
- );
- _.each(tx.logs, log => wrapLogBigNumbers(log));
- return tx;
- }
- public async fillOrKillOrderAsync(order: Order, from: string, opts: { fillTakerTokenAmount?: BigNumber } = {}) {
- const shouldThrowOnInsufficientBalanceOrAllowance = true;
- const params = order.createFill(shouldThrowOnInsufficientBalanceOrAllowance, opts.fillTakerTokenAmount);
- const tx = await this._exchange.fillOrKillOrder(
- params.orderAddresses,
- params.orderValues,
- params.fillTakerTokenAmount,
- params.v,
- params.r,
- params.s,
- { from },
- );
- _.each(tx.logs, log => wrapLogBigNumbers(log));
- return tx;
- }
- public async batchFillOrdersAsync(
- orders: Order[],
- from: string,
- opts: {
- fillTakerTokenAmounts?: BigNumber[];
- shouldThrowOnInsufficientBalanceOrAllowance?: boolean;
- } = {},
- ) {
- const shouldThrowOnInsufficientBalanceOrAllowance = !!opts.shouldThrowOnInsufficientBalanceOrAllowance;
- const params = formatters.createBatchFill(
- orders,
- shouldThrowOnInsufficientBalanceOrAllowance,
- opts.fillTakerTokenAmounts,
- );
- const tx = await this._exchange.batchFillOrders(
- params.orderAddresses,
- params.orderValues,
- params.fillTakerTokenAmounts,
- params.shouldThrowOnInsufficientBalanceOrAllowance,
- params.v,
- params.r,
- params.s,
- { from },
- );
- _.each(tx.logs, log => wrapLogBigNumbers(log));
- return tx;
- }
- public async batchFillOrKillOrdersAsync(
- orders: Order[],
- from: string,
- opts: { fillTakerTokenAmounts?: BigNumber[] } = {},
- ) {
- const params = formatters.createBatchFill(orders, undefined, opts.fillTakerTokenAmounts);
- const tx = await this._exchange.batchFillOrKillOrders(
- params.orderAddresses,
- params.orderValues,
- params.fillTakerTokenAmounts,
- params.v,
- params.r,
- params.s,
- { from },
- );
- _.each(tx.logs, log => wrapLogBigNumbers(log));
- return tx;
- }
- public async fillOrdersUpToAsync(
- orders: Order[],
- from: string,
- opts: {
- fillTakerTokenAmount?: BigNumber;
- shouldThrowOnInsufficientBalanceOrAllowance?: boolean;
- } = {},
- ) {
- const shouldThrowOnInsufficientBalanceOrAllowance = !!opts.shouldThrowOnInsufficientBalanceOrAllowance;
- const params = formatters.createFillUpTo(
- orders,
- shouldThrowOnInsufficientBalanceOrAllowance,
- opts.fillTakerTokenAmount,
- );
- const tx = await this._exchange.fillOrdersUpTo(
- params.orderAddresses,
- params.orderValues,
- params.fillTakerTokenAmount,
- params.shouldThrowOnInsufficientBalanceOrAllowance,
- params.v,
- params.r,
- params.s,
- { from },
- );
- _.each(tx.logs, log => wrapLogBigNumbers(log));
- return tx;
- }
- public async batchCancelOrdersAsync(
- orders: Order[],
- from: string,
- opts: { cancelTakerTokenAmounts?: BigNumber[] } = {},
- ) {
- const params = formatters.createBatchCancel(orders, opts.cancelTakerTokenAmounts);
- const tx = await this._exchange.batchCancelOrders(
- params.orderAddresses,
- params.orderValues,
- params.cancelTakerTokenAmounts,
- { from },
- );
- _.each(tx.logs, log => wrapLogBigNumbers(log));
- return tx;
- }
- public async getOrderHashAsync(order: Order): Promise<string> {
- const shouldThrowOnInsufficientBalanceOrAllowance = false;
- const params = order.createFill(shouldThrowOnInsufficientBalanceOrAllowance);
- const orderHash = await this._exchange.getOrderHash(params.orderAddresses, params.orderValues);
- return orderHash;
- }
- public async isValidSignatureAsync(order: Order): Promise<boolean> {
- const isValidSignature = await this._exchange.isValidSignature(
- order.params.maker,
- order.params.orderHashHex,
- order.params.v,
- order.params.r,
- order.params.s,
- );
- return isValidSignature;
- }
- public async isRoundingErrorAsync(
- numerator: BigNumber,
- denominator: BigNumber,
- target: BigNumber,
- ): Promise<boolean> {
- const isRoundingError = await this._exchange.isRoundingError(numerator, denominator, target);
- return isRoundingError;
- }
- public async getPartialAmountAsync(
- numerator: BigNumber,
- denominator: BigNumber,
- target: BigNumber,
- ): Promise<BigNumber> {
- const partialAmount = new BigNumber(await this._exchange.getPartialAmount(numerator, denominator, target));
- return partialAmount;
- }
+ private _exchange: ContractInstance;
+ constructor(exchangeContractInstance: ContractInstance) {
+ this._exchange = exchangeContractInstance;
+ }
+ public async fillOrderAsync(
+ order: Order,
+ from: string,
+ opts: {
+ fillTakerTokenAmount?: BigNumber;
+ shouldThrowOnInsufficientBalanceOrAllowance?: boolean;
+ } = {},
+ ) {
+ const shouldThrowOnInsufficientBalanceOrAllowance = !!opts.shouldThrowOnInsufficientBalanceOrAllowance;
+ const params = order.createFill(shouldThrowOnInsufficientBalanceOrAllowance, opts.fillTakerTokenAmount);
+ const tx = await this._exchange.fillOrder(
+ params.orderAddresses,
+ params.orderValues,
+ params.fillTakerTokenAmount,
+ params.shouldThrowOnInsufficientBalanceOrAllowance,
+ params.v,
+ params.r,
+ params.s,
+ { from },
+ );
+ _.each(tx.logs, log => wrapLogBigNumbers(log));
+ return tx;
+ }
+ public async cancelOrderAsync(order: Order, from: string, opts: { cancelTakerTokenAmount?: BigNumber } = {}) {
+ const params = order.createCancel(opts.cancelTakerTokenAmount);
+ const tx = await this._exchange.cancelOrder(
+ params.orderAddresses,
+ params.orderValues,
+ params.cancelTakerTokenAmount,
+ { from },
+ );
+ _.each(tx.logs, log => wrapLogBigNumbers(log));
+ return tx;
+ }
+ public async fillOrKillOrderAsync(order: Order, from: string, opts: { fillTakerTokenAmount?: BigNumber } = {}) {
+ const shouldThrowOnInsufficientBalanceOrAllowance = true;
+ const params = order.createFill(shouldThrowOnInsufficientBalanceOrAllowance, opts.fillTakerTokenAmount);
+ const tx = await this._exchange.fillOrKillOrder(
+ params.orderAddresses,
+ params.orderValues,
+ params.fillTakerTokenAmount,
+ params.v,
+ params.r,
+ params.s,
+ { from },
+ );
+ _.each(tx.logs, log => wrapLogBigNumbers(log));
+ return tx;
+ }
+ public async batchFillOrdersAsync(
+ orders: Order[],
+ from: string,
+ opts: {
+ fillTakerTokenAmounts?: BigNumber[];
+ shouldThrowOnInsufficientBalanceOrAllowance?: boolean;
+ } = {},
+ ) {
+ const shouldThrowOnInsufficientBalanceOrAllowance = !!opts.shouldThrowOnInsufficientBalanceOrAllowance;
+ const params = formatters.createBatchFill(
+ orders,
+ shouldThrowOnInsufficientBalanceOrAllowance,
+ opts.fillTakerTokenAmounts,
+ );
+ const tx = await this._exchange.batchFillOrders(
+ params.orderAddresses,
+ params.orderValues,
+ params.fillTakerTokenAmounts,
+ params.shouldThrowOnInsufficientBalanceOrAllowance,
+ params.v,
+ params.r,
+ params.s,
+ { from },
+ );
+ _.each(tx.logs, log => wrapLogBigNumbers(log));
+ return tx;
+ }
+ public async batchFillOrKillOrdersAsync(
+ orders: Order[],
+ from: string,
+ opts: { fillTakerTokenAmounts?: BigNumber[] } = {},
+ ) {
+ const params = formatters.createBatchFill(orders, undefined, opts.fillTakerTokenAmounts);
+ const tx = await this._exchange.batchFillOrKillOrders(
+ params.orderAddresses,
+ params.orderValues,
+ params.fillTakerTokenAmounts,
+ params.v,
+ params.r,
+ params.s,
+ { from },
+ );
+ _.each(tx.logs, log => wrapLogBigNumbers(log));
+ return tx;
+ }
+ public async fillOrdersUpToAsync(
+ orders: Order[],
+ from: string,
+ opts: {
+ fillTakerTokenAmount?: BigNumber;
+ shouldThrowOnInsufficientBalanceOrAllowance?: boolean;
+ } = {},
+ ) {
+ const shouldThrowOnInsufficientBalanceOrAllowance = !!opts.shouldThrowOnInsufficientBalanceOrAllowance;
+ const params = formatters.createFillUpTo(
+ orders,
+ shouldThrowOnInsufficientBalanceOrAllowance,
+ opts.fillTakerTokenAmount,
+ );
+ const tx = await this._exchange.fillOrdersUpTo(
+ params.orderAddresses,
+ params.orderValues,
+ params.fillTakerTokenAmount,
+ params.shouldThrowOnInsufficientBalanceOrAllowance,
+ params.v,
+ params.r,
+ params.s,
+ { from },
+ );
+ _.each(tx.logs, log => wrapLogBigNumbers(log));
+ return tx;
+ }
+ public async batchCancelOrdersAsync(
+ orders: Order[],
+ from: string,
+ opts: { cancelTakerTokenAmounts?: BigNumber[] } = {},
+ ) {
+ const params = formatters.createBatchCancel(orders, opts.cancelTakerTokenAmounts);
+ const tx = await this._exchange.batchCancelOrders(
+ params.orderAddresses,
+ params.orderValues,
+ params.cancelTakerTokenAmounts,
+ { from },
+ );
+ _.each(tx.logs, log => wrapLogBigNumbers(log));
+ return tx;
+ }
+ public async getOrderHashAsync(order: Order): Promise<string> {
+ const shouldThrowOnInsufficientBalanceOrAllowance = false;
+ const params = order.createFill(shouldThrowOnInsufficientBalanceOrAllowance);
+ const orderHash = await this._exchange.getOrderHash(params.orderAddresses, params.orderValues);
+ return orderHash;
+ }
+ public async isValidSignatureAsync(order: Order): Promise<boolean> {
+ const isValidSignature = await this._exchange.isValidSignature(
+ order.params.maker,
+ order.params.orderHashHex,
+ order.params.v,
+ order.params.r,
+ order.params.s,
+ );
+ return isValidSignature;
+ }
+ public async isRoundingErrorAsync(
+ numerator: BigNumber,
+ denominator: BigNumber,
+ target: BigNumber,
+ ): Promise<boolean> {
+ const isRoundingError = await this._exchange.isRoundingError(numerator, denominator, target);
+ return isRoundingError;
+ }
+ public async getPartialAmountAsync(
+ numerator: BigNumber,
+ denominator: BigNumber,
+ target: BigNumber,
+ ): Promise<BigNumber> {
+ const partialAmount = new BigNumber(await this._exchange.getPartialAmount(numerator, denominator, target));
+ return partialAmount;
+ }
}
function wrapLogBigNumbers(log: any): any {
- const argNames = _.keys(log.args);
- for (const argName of argNames) {
- const isWeb3BigNumber = _.startsWith(log.args[argName].constructor.toString(), 'function BigNumber(');
- if (isWeb3BigNumber) {
- log.args[argName] = new BigNumber(log.args[argName]);
- }
- }
+ const argNames = _.keys(log.args);
+ for (const argName of argNames) {
+ const isWeb3BigNumber = _.startsWith(log.args[argName].constructor.toString(), 'function BigNumber(');
+ if (isWeb3BigNumber) {
+ log.args[argName] = new BigNumber(log.args[argName]);
+ }
+ }
}
diff --git a/packages/contracts/util/formatters.ts b/packages/contracts/util/formatters.ts
index c452b8e79..0d0ef6df4 100644
--- a/packages/contracts/util/formatters.ts
+++ b/packages/contracts/util/formatters.ts
@@ -5,107 +5,107 @@ import { Order } from './order';
import { BatchCancelOrders, BatchFillOrders, FillOrdersUpTo } from './types';
export const formatters = {
- createBatchFill(
- orders: Order[],
- shouldThrowOnInsufficientBalanceOrAllowance: boolean,
- fillTakerTokenAmounts: BigNumber[] = [],
- ) {
- const batchFill: BatchFillOrders = {
- orderAddresses: [],
- orderValues: [],
- fillTakerTokenAmounts,
- shouldThrowOnInsufficientBalanceOrAllowance,
- v: [],
- r: [],
- s: [],
- };
- _.forEach(orders, order => {
- batchFill.orderAddresses.push([
- order.params.maker,
- order.params.taker,
- order.params.makerToken,
- order.params.takerToken,
- order.params.feeRecipient,
- ]);
- batchFill.orderValues.push([
- order.params.makerTokenAmount,
- order.params.takerTokenAmount,
- order.params.makerFee,
- order.params.takerFee,
- order.params.expirationTimestampInSec,
- order.params.salt,
- ]);
- batchFill.v.push(order.params.v);
- batchFill.r.push(order.params.r);
- batchFill.s.push(order.params.s);
- if (fillTakerTokenAmounts.length < orders.length) {
- batchFill.fillTakerTokenAmounts.push(order.params.takerTokenAmount);
- }
- });
- return batchFill;
- },
- createFillUpTo(
- orders: Order[],
- shouldThrowOnInsufficientBalanceOrAllowance: boolean,
- fillTakerTokenAmount: BigNumber,
- ) {
- const fillUpTo: FillOrdersUpTo = {
- orderAddresses: [],
- orderValues: [],
- fillTakerTokenAmount,
- shouldThrowOnInsufficientBalanceOrAllowance,
- v: [],
- r: [],
- s: [],
- };
- orders.forEach(order => {
- fillUpTo.orderAddresses.push([
- order.params.maker,
- order.params.taker,
- order.params.makerToken,
- order.params.takerToken,
- order.params.feeRecipient,
- ]);
- fillUpTo.orderValues.push([
- order.params.makerTokenAmount,
- order.params.takerTokenAmount,
- order.params.makerFee,
- order.params.takerFee,
- order.params.expirationTimestampInSec,
- order.params.salt,
- ]);
- fillUpTo.v.push(order.params.v);
- fillUpTo.r.push(order.params.r);
- fillUpTo.s.push(order.params.s);
- });
- return fillUpTo;
- },
- createBatchCancel(orders: Order[], cancelTakerTokenAmounts: BigNumber[] = []) {
- const batchCancel: BatchCancelOrders = {
- orderAddresses: [],
- orderValues: [],
- cancelTakerTokenAmounts,
- };
- orders.forEach(order => {
- batchCancel.orderAddresses.push([
- order.params.maker,
- order.params.taker,
- order.params.makerToken,
- order.params.takerToken,
- order.params.feeRecipient,
- ]);
- batchCancel.orderValues.push([
- order.params.makerTokenAmount,
- order.params.takerTokenAmount,
- order.params.makerFee,
- order.params.takerFee,
- order.params.expirationTimestampInSec,
- order.params.salt,
- ]);
- if (cancelTakerTokenAmounts.length < orders.length) {
- batchCancel.cancelTakerTokenAmounts.push(order.params.takerTokenAmount);
- }
- });
- return batchCancel;
- },
+ createBatchFill(
+ orders: Order[],
+ shouldThrowOnInsufficientBalanceOrAllowance: boolean,
+ fillTakerTokenAmounts: BigNumber[] = [],
+ ) {
+ const batchFill: BatchFillOrders = {
+ orderAddresses: [],
+ orderValues: [],
+ fillTakerTokenAmounts,
+ shouldThrowOnInsufficientBalanceOrAllowance,
+ v: [],
+ r: [],
+ s: [],
+ };
+ _.forEach(orders, order => {
+ batchFill.orderAddresses.push([
+ order.params.maker,
+ order.params.taker,
+ order.params.makerToken,
+ order.params.takerToken,
+ order.params.feeRecipient,
+ ]);
+ batchFill.orderValues.push([
+ order.params.makerTokenAmount,
+ order.params.takerTokenAmount,
+ order.params.makerFee,
+ order.params.takerFee,
+ order.params.expirationTimestampInSec,
+ order.params.salt,
+ ]);
+ batchFill.v.push(order.params.v);
+ batchFill.r.push(order.params.r);
+ batchFill.s.push(order.params.s);
+ if (fillTakerTokenAmounts.length < orders.length) {
+ batchFill.fillTakerTokenAmounts.push(order.params.takerTokenAmount);
+ }
+ });
+ return batchFill;
+ },
+ createFillUpTo(
+ orders: Order[],
+ shouldThrowOnInsufficientBalanceOrAllowance: boolean,
+ fillTakerTokenAmount: BigNumber,
+ ) {
+ const fillUpTo: FillOrdersUpTo = {
+ orderAddresses: [],
+ orderValues: [],
+ fillTakerTokenAmount,
+ shouldThrowOnInsufficientBalanceOrAllowance,
+ v: [],
+ r: [],
+ s: [],
+ };
+ orders.forEach(order => {
+ fillUpTo.orderAddresses.push([
+ order.params.maker,
+ order.params.taker,
+ order.params.makerToken,
+ order.params.takerToken,
+ order.params.feeRecipient,
+ ]);
+ fillUpTo.orderValues.push([
+ order.params.makerTokenAmount,
+ order.params.takerTokenAmount,
+ order.params.makerFee,
+ order.params.takerFee,
+ order.params.expirationTimestampInSec,
+ order.params.salt,
+ ]);
+ fillUpTo.v.push(order.params.v);
+ fillUpTo.r.push(order.params.r);
+ fillUpTo.s.push(order.params.s);
+ });
+ return fillUpTo;
+ },
+ createBatchCancel(orders: Order[], cancelTakerTokenAmounts: BigNumber[] = []) {
+ const batchCancel: BatchCancelOrders = {
+ orderAddresses: [],
+ orderValues: [],
+ cancelTakerTokenAmounts,
+ };
+ orders.forEach(order => {
+ batchCancel.orderAddresses.push([
+ order.params.maker,
+ order.params.taker,
+ order.params.makerToken,
+ order.params.takerToken,
+ order.params.feeRecipient,
+ ]);
+ batchCancel.orderValues.push([
+ order.params.makerTokenAmount,
+ order.params.takerTokenAmount,
+ order.params.makerFee,
+ order.params.takerFee,
+ order.params.expirationTimestampInSec,
+ order.params.salt,
+ ]);
+ if (cancelTakerTokenAmounts.length < orders.length) {
+ batchCancel.cancelTakerTokenAmounts.push(order.params.takerTokenAmount);
+ }
+ });
+ return batchCancel;
+ },
};
diff --git a/packages/contracts/util/multi_sig_wrapper.ts b/packages/contracts/util/multi_sig_wrapper.ts
index ca9f07ce5..0e2e671ec 100644
--- a/packages/contracts/util/multi_sig_wrapper.ts
+++ b/packages/contracts/util/multi_sig_wrapper.ts
@@ -6,34 +6,34 @@ import * as Web3 from 'web3';
import { ContractInstance, TransactionDataParams } from './types';
export class MultiSigWrapper {
- private _multiSig: ContractInstance;
- public static encodeFnArgs(name: string, abi: Web3.AbiDefinition[], args: any[]) {
- const abiEntity = _.find(abi, { name }) as Web3.MethodAbi;
- if (_.isUndefined(abiEntity)) {
- throw new Error(`Did not find abi entry for name: ${name}`);
- }
- const types = _.map(abiEntity.inputs, input => input.type);
- const funcSig = ethUtil.bufferToHex(ABI.methodID(name, types));
- const argsData = _.map(args, arg => {
- const target = _.isBoolean(arg) ? +arg : arg;
- const targetBuff = ethUtil.toBuffer(target);
- return ethUtil.setLengthLeft(targetBuff, 32).toString('hex');
- });
- return funcSig + argsData.join('');
- }
- constructor(multiSigContractInstance: ContractInstance) {
- this._multiSig = multiSigContractInstance;
- }
- public async submitTransactionAsync(
- destination: string,
- from: string,
- dataParams: TransactionDataParams,
- value: number = 0,
- ) {
- const { name, abi, args = [] } = dataParams;
- const encoded = MultiSigWrapper.encodeFnArgs(name, abi, args);
- return this._multiSig.submitTransaction(destination, value, encoded, {
- from,
- });
- }
+ private _multiSig: ContractInstance;
+ public static encodeFnArgs(name: string, abi: Web3.AbiDefinition[], args: any[]) {
+ const abiEntity = _.find(abi, { name }) as Web3.MethodAbi;
+ if (_.isUndefined(abiEntity)) {
+ throw new Error(`Did not find abi entry for name: ${name}`);
+ }
+ const types = _.map(abiEntity.inputs, input => input.type);
+ const funcSig = ethUtil.bufferToHex(ABI.methodID(name, types));
+ const argsData = _.map(args, arg => {
+ const target = _.isBoolean(arg) ? +arg : arg;
+ const targetBuff = ethUtil.toBuffer(target);
+ return ethUtil.setLengthLeft(targetBuff, 32).toString('hex');
+ });
+ return funcSig + argsData.join('');
+ }
+ constructor(multiSigContractInstance: ContractInstance) {
+ this._multiSig = multiSigContractInstance;
+ }
+ public async submitTransactionAsync(
+ destination: string,
+ from: string,
+ dataParams: TransactionDataParams,
+ value: number = 0,
+ ) {
+ const { name, abi, args = [] } = dataParams;
+ const encoded = MultiSigWrapper.encodeFnArgs(name, abi, args);
+ return this._multiSig.submitTransaction(destination, value, encoded, {
+ from,
+ });
+ }
}
diff --git a/packages/contracts/util/order.ts b/packages/contracts/util/order.ts
index 31194c03e..e202d485b 100644
--- a/packages/contracts/util/order.ts
+++ b/packages/contracts/util/order.ts
@@ -11,98 +11,98 @@ import { OrderParams } from './types';
const web3: Web3 = (global as any).web3;
export class Order {
- public params: OrderParams;
- constructor(params: OrderParams) {
- this.params = params;
- }
- public isValidSignature() {
- const { v, r, s } = this.params;
- if (_.isUndefined(v) || _.isUndefined(r) || _.isUndefined(s)) {
- throw new Error('Cannot call isValidSignature on unsigned order');
- }
- const orderHash = this._getOrderHash();
- const msgHash = ethUtil.hashPersonalMessage(ethUtil.toBuffer(orderHash));
- try {
- const pubKey = ethUtil.ecrecover(msgHash, v, ethUtil.toBuffer(r), ethUtil.toBuffer(s));
- const recoveredAddress = ethUtil.bufferToHex(ethUtil.pubToAddress(pubKey));
- return recoveredAddress === this.params.maker;
- } catch (err) {
- return false;
- }
- }
- public async signAsync() {
- const orderHash = this._getOrderHash();
- const signature = await promisify<string>(web3.eth.sign)(this.params.maker, orderHash);
- const { v, r, s } = ethUtil.fromRpcSig(signature);
- this.params = _.assign(this.params, {
- orderHashHex: orderHash,
- v,
- r: ethUtil.bufferToHex(r),
- s: ethUtil.bufferToHex(s),
- });
- }
- public createFill(shouldThrowOnInsufficientBalanceOrAllowance?: boolean, fillTakerTokenAmount?: BigNumber) {
- const fill = {
- orderAddresses: [
- this.params.maker,
- this.params.taker,
- this.params.makerToken,
- this.params.takerToken,
- this.params.feeRecipient,
- ],
- orderValues: [
- this.params.makerTokenAmount,
- this.params.takerTokenAmount,
- this.params.makerFee,
- this.params.takerFee,
- this.params.expirationTimestampInSec,
- this.params.salt,
- ],
- fillTakerTokenAmount: fillTakerTokenAmount || this.params.takerTokenAmount,
- shouldThrowOnInsufficientBalanceOrAllowance: !!shouldThrowOnInsufficientBalanceOrAllowance,
- v: this.params.v,
- r: this.params.r,
- s: this.params.s,
- };
- return fill;
- }
- public createCancel(cancelTakerTokenAmount?: BigNumber) {
- const cancel = {
- orderAddresses: [
- this.params.maker,
- this.params.taker,
- this.params.makerToken,
- this.params.takerToken,
- this.params.feeRecipient,
- ],
- orderValues: [
- this.params.makerTokenAmount,
- this.params.takerTokenAmount,
- this.params.makerFee,
- this.params.takerFee,
- this.params.expirationTimestampInSec,
- this.params.salt,
- ],
- cancelTakerTokenAmount: cancelTakerTokenAmount || this.params.takerTokenAmount,
- };
- return cancel;
- }
- private _getOrderHash(): string {
- const orderHash = crypto.solSHA3([
- this.params.exchangeContractAddress,
- this.params.maker,
- this.params.taker,
- this.params.makerToken,
- this.params.takerToken,
- this.params.feeRecipient,
- this.params.makerTokenAmount,
- this.params.takerTokenAmount,
- this.params.makerFee,
- this.params.takerFee,
- this.params.expirationTimestampInSec,
- this.params.salt,
- ]);
- const orderHashHex = ethUtil.bufferToHex(orderHash);
- return orderHashHex;
- }
+ public params: OrderParams;
+ constructor(params: OrderParams) {
+ this.params = params;
+ }
+ public isValidSignature() {
+ const { v, r, s } = this.params;
+ if (_.isUndefined(v) || _.isUndefined(r) || _.isUndefined(s)) {
+ throw new Error('Cannot call isValidSignature on unsigned order');
+ }
+ const orderHash = this._getOrderHash();
+ const msgHash = ethUtil.hashPersonalMessage(ethUtil.toBuffer(orderHash));
+ try {
+ const pubKey = ethUtil.ecrecover(msgHash, v, ethUtil.toBuffer(r), ethUtil.toBuffer(s));
+ const recoveredAddress = ethUtil.bufferToHex(ethUtil.pubToAddress(pubKey));
+ return recoveredAddress === this.params.maker;
+ } catch (err) {
+ return false;
+ }
+ }
+ public async signAsync() {
+ const orderHash = this._getOrderHash();
+ const signature = await promisify<string>(web3.eth.sign)(this.params.maker, orderHash);
+ const { v, r, s } = ethUtil.fromRpcSig(signature);
+ this.params = _.assign(this.params, {
+ orderHashHex: orderHash,
+ v,
+ r: ethUtil.bufferToHex(r),
+ s: ethUtil.bufferToHex(s),
+ });
+ }
+ public createFill(shouldThrowOnInsufficientBalanceOrAllowance?: boolean, fillTakerTokenAmount?: BigNumber) {
+ const fill = {
+ orderAddresses: [
+ this.params.maker,
+ this.params.taker,
+ this.params.makerToken,
+ this.params.takerToken,
+ this.params.feeRecipient,
+ ],
+ orderValues: [
+ this.params.makerTokenAmount,
+ this.params.takerTokenAmount,
+ this.params.makerFee,
+ this.params.takerFee,
+ this.params.expirationTimestampInSec,
+ this.params.salt,
+ ],
+ fillTakerTokenAmount: fillTakerTokenAmount || this.params.takerTokenAmount,
+ shouldThrowOnInsufficientBalanceOrAllowance: !!shouldThrowOnInsufficientBalanceOrAllowance,
+ v: this.params.v,
+ r: this.params.r,
+ s: this.params.s,
+ };
+ return fill;
+ }
+ public createCancel(cancelTakerTokenAmount?: BigNumber) {
+ const cancel = {
+ orderAddresses: [
+ this.params.maker,
+ this.params.taker,
+ this.params.makerToken,
+ this.params.takerToken,
+ this.params.feeRecipient,
+ ],
+ orderValues: [
+ this.params.makerTokenAmount,
+ this.params.takerTokenAmount,
+ this.params.makerFee,
+ this.params.takerFee,
+ this.params.expirationTimestampInSec,
+ this.params.salt,
+ ],
+ cancelTakerTokenAmount: cancelTakerTokenAmount || this.params.takerTokenAmount,
+ };
+ return cancel;
+ }
+ private _getOrderHash(): string {
+ const orderHash = crypto.solSHA3([
+ this.params.exchangeContractAddress,
+ this.params.maker,
+ this.params.taker,
+ this.params.makerToken,
+ this.params.takerToken,
+ this.params.feeRecipient,
+ this.params.makerTokenAmount,
+ this.params.takerTokenAmount,
+ this.params.makerFee,
+ this.params.takerFee,
+ this.params.expirationTimestampInSec,
+ this.params.salt,
+ ]);
+ const orderHashHex = ethUtil.bufferToHex(orderHash);
+ return orderHashHex;
+ }
}
diff --git a/packages/contracts/util/order_factory.ts b/packages/contracts/util/order_factory.ts
index 55034655b..a45877de0 100644
--- a/packages/contracts/util/order_factory.ts
+++ b/packages/contracts/util/order_factory.ts
@@ -6,24 +6,24 @@ import { Order } from './order';
import { DefaultOrderParams, OptionalOrderParams, OrderParams } from './types';
export class OrderFactory {
- private _defaultOrderParams: DefaultOrderParams;
- constructor(defaultOrderParams: DefaultOrderParams) {
- this._defaultOrderParams = defaultOrderParams;
- }
- public async newSignedOrderAsync(customOrderParams: OptionalOrderParams = {}) {
- const randomExpiration = new BigNumber(Math.floor((Date.now() + Math.random() * 100000000000) / 1000));
- const orderParams: OrderParams = _.assign(
- {},
- {
- expirationTimestampInSec: randomExpiration,
- salt: ZeroEx.generatePseudoRandomSalt(),
- taker: ZeroEx.NULL_ADDRESS,
- },
- this._defaultOrderParams,
- customOrderParams,
- );
- const order = new Order(orderParams);
- await order.signAsync();
- return order;
- }
+ private _defaultOrderParams: DefaultOrderParams;
+ constructor(defaultOrderParams: DefaultOrderParams) {
+ this._defaultOrderParams = defaultOrderParams;
+ }
+ public async newSignedOrderAsync(customOrderParams: OptionalOrderParams = {}) {
+ const randomExpiration = new BigNumber(Math.floor((Date.now() + Math.random() * 100000000000) / 1000));
+ const orderParams: OrderParams = _.assign(
+ {},
+ {
+ expirationTimestampInSec: randomExpiration,
+ salt: ZeroEx.generatePseudoRandomSalt(),
+ taker: ZeroEx.NULL_ADDRESS,
+ },
+ this._defaultOrderParams,
+ customOrderParams,
+ );
+ const order = new Order(orderParams);
+ await order.signAsync();
+ return order;
+ }
}
diff --git a/packages/contracts/util/token_registry_wrapper.ts b/packages/contracts/util/token_registry_wrapper.ts
index d1983db7c..07a577dea 100644
--- a/packages/contracts/util/token_registry_wrapper.ts
+++ b/packages/contracts/util/token_registry_wrapper.ts
@@ -1,56 +1,56 @@
import { ContractInstance, Token } from './types';
export class TokenRegWrapper {
- private _tokenReg: ContractInstance;
- constructor(tokenRegContractInstance: ContractInstance) {
- this._tokenReg = tokenRegContractInstance;
- }
- public addTokenAsync(token: Token, from: string) {
- const tx = this._tokenReg.addToken(
- token.address,
- token.name,
- token.symbol,
- token.decimals,
- token.ipfsHash,
- token.swarmHash,
- { from },
- );
- return tx;
- }
- public async getTokenMetaDataAsync(tokenAddress: string) {
- const data = await this._tokenReg.getTokenMetaData(tokenAddress);
- const token: Token = {
- address: data[0],
- name: data[1],
- symbol: data[2],
- decimals: data[3].toNumber(),
- ipfsHash: data[4],
- swarmHash: data[5],
- };
- return token;
- }
- public async getTokenByNameAsync(tokenName: string) {
- const data = await this._tokenReg.getTokenByName(tokenName);
- const token: Token = {
- address: data[0],
- name: data[1],
- symbol: data[2],
- decimals: data[3].toNumber(),
- ipfsHash: data[4],
- swarmHash: data[5],
- };
- return token;
- }
- public async getTokenBySymbolAsync(tokenSymbol: string) {
- const data = await this._tokenReg.getTokenBySymbol(tokenSymbol);
- const token: Token = {
- address: data[0],
- name: data[1],
- symbol: data[2],
- decimals: data[3].toNumber(),
- ipfsHash: data[4],
- swarmHash: data[5],
- };
- return token;
- }
+ private _tokenReg: ContractInstance;
+ constructor(tokenRegContractInstance: ContractInstance) {
+ this._tokenReg = tokenRegContractInstance;
+ }
+ public addTokenAsync(token: Token, from: string) {
+ const tx = this._tokenReg.addToken(
+ token.address,
+ token.name,
+ token.symbol,
+ token.decimals,
+ token.ipfsHash,
+ token.swarmHash,
+ { from },
+ );
+ return tx;
+ }
+ public async getTokenMetaDataAsync(tokenAddress: string) {
+ const data = await this._tokenReg.getTokenMetaData(tokenAddress);
+ const token: Token = {
+ address: data[0],
+ name: data[1],
+ symbol: data[2],
+ decimals: data[3].toNumber(),
+ ipfsHash: data[4],
+ swarmHash: data[5],
+ };
+ return token;
+ }
+ public async getTokenByNameAsync(tokenName: string) {
+ const data = await this._tokenReg.getTokenByName(tokenName);
+ const token: Token = {
+ address: data[0],
+ name: data[1],
+ symbol: data[2],
+ decimals: data[3].toNumber(),
+ ipfsHash: data[4],
+ swarmHash: data[5],
+ };
+ return token;
+ }
+ public async getTokenBySymbolAsync(tokenSymbol: string) {
+ const data = await this._tokenReg.getTokenBySymbol(tokenSymbol);
+ const token: Token = {
+ address: data[0],
+ name: data[1],
+ symbol: data[2],
+ decimals: data[3].toNumber(),
+ ipfsHash: data[4],
+ swarmHash: data[5],
+ };
+ return token;
+ }
}
diff --git a/packages/contracts/util/types.ts b/packages/contracts/util/types.ts
index 675f3f17d..e511ca9f4 100644
--- a/packages/contracts/util/types.ts
+++ b/packages/contracts/util/types.ts
@@ -2,118 +2,118 @@ import { BigNumber } from '@0xproject/utils';
import * as Web3 from 'web3';
export interface BalancesByOwner {
- [ownerAddress: string]: {
- [tokenAddress: string]: BigNumber;
- };
+ [ownerAddress: string]: {
+ [tokenAddress: string]: BigNumber;
+ };
}
export interface BatchFillOrders {
- orderAddresses: string[][];
- orderValues: BigNumber[][];
- fillTakerTokenAmounts: BigNumber[];
- shouldThrowOnInsufficientBalanceOrAllowance: boolean;
- v: number[];
- r: string[];
- s: string[];
+ orderAddresses: string[][];
+ orderValues: BigNumber[][];
+ fillTakerTokenAmounts: BigNumber[];
+ shouldThrowOnInsufficientBalanceOrAllowance: boolean;
+ v: number[];
+ r: string[];
+ s: string[];
}
export interface FillOrdersUpTo {
- orderAddresses: string[][];
- orderValues: BigNumber[][];
- fillTakerTokenAmount: BigNumber;
- shouldThrowOnInsufficientBalanceOrAllowance: boolean;
- v: number[];
- r: string[];
- s: string[];
+ orderAddresses: string[][];
+ orderValues: BigNumber[][];
+ fillTakerTokenAmount: BigNumber;
+ shouldThrowOnInsufficientBalanceOrAllowance: boolean;
+ v: number[];
+ r: string[];
+ s: string[];
}
export interface BatchCancelOrders {
- orderAddresses: string[][];
- orderValues: BigNumber[][];
- cancelTakerTokenAmounts: BigNumber[];
+ orderAddresses: string[][];
+ orderValues: BigNumber[][];
+ cancelTakerTokenAmounts: BigNumber[];
}
export interface DefaultOrderParams {
- exchangeContractAddress: string;
- maker: string;
- feeRecipient: string;
- makerToken: string;
- takerToken: string;
- makerTokenAmount: BigNumber;
- takerTokenAmount: BigNumber;
- makerFee: BigNumber;
- takerFee: BigNumber;
+ exchangeContractAddress: string;
+ maker: string;
+ feeRecipient: string;
+ makerToken: string;
+ takerToken: string;
+ makerTokenAmount: BigNumber;
+ takerTokenAmount: BigNumber;
+ makerFee: BigNumber;
+ takerFee: BigNumber;
}
export interface OptionalOrderParams {
- exchangeContractAddress?: string;
- maker?: string;
- taker?: string;
- feeRecipient?: string;
- makerToken?: string;
- takerToken?: string;
- makerTokenAmount?: BigNumber;
- takerTokenAmount?: BigNumber;
- makerFee?: BigNumber;
- takerFee?: BigNumber;
- expirationTimestampInSec?: BigNumber;
+ exchangeContractAddress?: string;
+ maker?: string;
+ taker?: string;
+ feeRecipient?: string;
+ makerToken?: string;
+ takerToken?: string;
+ makerTokenAmount?: BigNumber;
+ takerTokenAmount?: BigNumber;
+ makerFee?: BigNumber;
+ takerFee?: BigNumber;
+ expirationTimestampInSec?: BigNumber;
}
export interface OrderParams {
- exchangeContractAddress: string;
- maker: string;
- taker: string;
- feeRecipient: string;
- makerToken: string;
- takerToken: string;
- makerTokenAmount: BigNumber;
- takerTokenAmount: BigNumber;
- makerFee: BigNumber;
- takerFee: BigNumber;
- expirationTimestampInSec: BigNumber;
- salt: BigNumber;
- orderHashHex?: string;
- v?: number;
- r?: string;
- s?: string;
+ exchangeContractAddress: string;
+ maker: string;
+ taker: string;
+ feeRecipient: string;
+ makerToken: string;
+ takerToken: string;
+ makerTokenAmount: BigNumber;
+ takerTokenAmount: BigNumber;
+ makerFee: BigNumber;
+ takerFee: BigNumber;
+ expirationTimestampInSec: BigNumber;
+ salt: BigNumber;
+ orderHashHex?: string;
+ v?: number;
+ r?: string;
+ s?: string;
}
export interface TransactionDataParams {
- name: string;
- abi: Web3.AbiDefinition[];
- args: any[];
+ name: string;
+ abi: Web3.AbiDefinition[];
+ args: any[];
}
export interface MultiSigConfig {
- owners: string[];
- confirmationsRequired: number;
- secondsRequired: number;
+ owners: string[];
+ confirmationsRequired: number;
+ secondsRequired: number;
}
export interface MultiSigConfigByNetwork {
- [networkName: string]: MultiSigConfig;
+ [networkName: string]: MultiSigConfig;
}
export interface Token {
- address?: string;
- name: string;
- symbol: string;
- decimals: number;
- ipfsHash: string;
- swarmHash: string;
+ address?: string;
+ name: string;
+ symbol: string;
+ decimals: number;
+ ipfsHash: string;
+ swarmHash: string;
}
export interface TokenInfoByNetwork {
- development: Token[];
- live: Token[];
+ development: Token[];
+ live: Token[];
}
// Named type aliases to improve readability
export type ContractInstance = any;
export enum ExchangeContractErrs {
- ERROR_ORDER_EXPIRED,
- ERROR_ORDER_FULLY_FILLED_OR_CANCELLED,
- ERROR_ROUNDING_ERROR_TOO_LARGE,
- ERROR_INSUFFICIENT_BALANCE_OR_ALLOWANCE,
+ ERROR_ORDER_EXPIRED,
+ ERROR_ORDER_FULLY_FILLED_OR_CANCELLED,
+ ERROR_ROUNDING_ERROR_TOO_LARGE,
+ ERROR_INSUFFICIENT_BALANCE_OR_ALLOWANCE,
}
diff --git a/packages/deployer/package.json b/packages/deployer/package.json
index f78f49497..423d3bad8 100644
--- a/packages/deployer/package.json
+++ b/packages/deployer/package.json
@@ -1,43 +1,43 @@
{
- "name": "@0xproject/deployer",
- "version": "0.0.3",
- "description": "Smart contract deployer of 0x protocol",
- "main": "lib/src/cli.js",
- "scripts": {
- "build": "yarn clean && copyfiles 'test/fixtures/contracts/**/*' src/solc/solc_bin/* ./lib && tsc",
- "test": "npm run build; mocha lib/test/*_test.js",
- "compile": "npm run build; node lib/src/cli.js compile",
- "clean": "rm -rf ./lib",
- "migrate": "npm run build; node lib/src/cli.js migrate",
- "lint": "tslint --project . 'src/**/*.ts' 'test/**/*.ts'",
- "test:circleci": "yarn test"
- },
- "bin": {
- "0x-deployer": "lib/src/cli.js"
- },
- "repository": {
- "type": "git",
- "url": "https://github.com/0xProject/0x.js.git"
- },
- "author": "Amir Bandeali",
- "license": "Apache-2.0",
- "bugs": {
- "url": "https://github.com/0xProject/0x.js/issues"
- },
- "homepage": "https://github.com/0xProject/0x.js/packages/deployer/README.md",
- "devDependencies": {
- "copyfiles": "^1.2.0",
- "types-bn": "^0.0.1",
- "typescript": "~2.6.1",
- "web3-typescript-typings": "^0.9.6"
- },
- "dependencies": {
- "@0xproject/utils": "^0.1.3",
- "@0xproject/web3-wrapper": "^0.1.7",
- "lodash": "^4.17.4",
- "solc": "^0.4.18",
- "web3": "^0.20.0",
- "web3-eth-abi": "^1.0.0-beta.24",
- "yargs": "^10.0.3"
- }
+ "name": "@0xproject/deployer",
+ "version": "0.0.3",
+ "description": "Smart contract deployer of 0x protocol",
+ "main": "lib/src/cli.js",
+ "scripts": {
+ "build": "yarn clean && copyfiles 'test/fixtures/contracts/**/*' src/solc/solc_bin/* ./lib && tsc",
+ "test": "npm run build; mocha lib/test/*_test.js",
+ "compile": "npm run build; node lib/src/cli.js compile",
+ "clean": "rm -rf ./lib",
+ "migrate": "npm run build; node lib/src/cli.js migrate",
+ "lint": "tslint --project . 'src/**/*.ts' 'test/**/*.ts'",
+ "test:circleci": "yarn test"
+ },
+ "bin": {
+ "0x-deployer": "lib/src/cli.js"
+ },
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/0xProject/0x.js.git"
+ },
+ "author": "Amir Bandeali",
+ "license": "Apache-2.0",
+ "bugs": {
+ "url": "https://github.com/0xProject/0x.js/issues"
+ },
+ "homepage": "https://github.com/0xProject/0x.js/packages/deployer/README.md",
+ "devDependencies": {
+ "copyfiles": "^1.2.0",
+ "types-bn": "^0.0.1",
+ "typescript": "~2.6.1",
+ "web3-typescript-typings": "^0.9.6"
+ },
+ "dependencies": {
+ "@0xproject/utils": "^0.1.3",
+ "@0xproject/web3-wrapper": "^0.1.7",
+ "lodash": "^4.17.4",
+ "solc": "^0.4.18",
+ "web3": "^0.20.0",
+ "web3-eth-abi": "^1.0.0-beta.24",
+ "yargs": "^10.0.3"
+ }
}
diff --git a/packages/deployer/src/cli.ts b/packages/deployer/src/cli.ts
index f4241b1cc..decb37fdc 100644
--- a/packages/deployer/src/cli.ts
+++ b/packages/deployer/src/cli.ts
@@ -20,13 +20,13 @@ const DEFAULT_GAS_PRICE = (10 ** 9 * 2).toString();
* @param argv Instance of process.argv provided by yargs.
*/
async function onCompileCommand(argv: CliOptions): Promise<void> {
- const opts: CompilerOptions = {
- contractsDir: argv.contractsDir,
- networkId: argv.networkId,
- optimizerEnabled: argv.shouldOptimize ? 1 : 0,
- artifactsDir: argv.artifactsDir,
- };
- await commands.compileAsync(opts);
+ const opts: CompilerOptions = {
+ contractsDir: argv.contractsDir,
+ networkId: argv.networkId,
+ optimizerEnabled: argv.shouldOptimize ? 1 : 0,
+ artifactsDir: argv.artifactsDir,
+ };
+ await commands.compileAsync(opts);
}
/**
* Compiles all contracts and runs migration script with options passed in through CLI.
@@ -34,123 +34,123 @@ async function onCompileCommand(argv: CliOptions): Promise<void> {
* @param argv Instance of process.argv provided by yargs.
*/
async function onMigrateCommand(argv: CliOptions): Promise<void> {
- const url = `http://localhost:${argv.jsonrpcPort}`;
- const web3Provider = new Web3.providers.HttpProvider(url);
- const web3Wrapper = new Web3Wrapper(web3Provider);
- const networkId = await web3Wrapper.getNetworkIdAsync();
- const compilerOpts: CompilerOptions = {
- contractsDir: argv.contractsDir,
- networkId,
- optimizerEnabled: argv.shouldOptimize ? 1 : 0,
- artifactsDir: argv.artifactsDir,
- };
- await commands.compileAsync(compilerOpts);
+ const url = `http://localhost:${argv.jsonrpcPort}`;
+ const web3Provider = new Web3.providers.HttpProvider(url);
+ const web3Wrapper = new Web3Wrapper(web3Provider);
+ const networkId = await web3Wrapper.getNetworkIdAsync();
+ const compilerOpts: CompilerOptions = {
+ contractsDir: argv.contractsDir,
+ networkId,
+ optimizerEnabled: argv.shouldOptimize ? 1 : 0,
+ artifactsDir: argv.artifactsDir,
+ };
+ await commands.compileAsync(compilerOpts);
- const defaults = {
- gasPrice: new BigNumber(argv.gasPrice),
- from: argv.account,
- };
- const deployerOpts = {
- artifactsDir: argv.artifactsDir,
- jsonrpcPort: argv.jsonrpcPort,
- networkId,
- defaults,
- };
- await commands.migrateAsync(deployerOpts);
+ const defaults = {
+ gasPrice: new BigNumber(argv.gasPrice),
+ from: argv.account,
+ };
+ const deployerOpts = {
+ artifactsDir: argv.artifactsDir,
+ jsonrpcPort: argv.jsonrpcPort,
+ networkId,
+ defaults,
+ };
+ await commands.migrateAsync(deployerOpts);
}
/**
* Deploys a single contract with provided name and args.
* @param argv Instance of process.argv provided by yargs.
*/
async function onDeployCommand(argv: CliOptions): Promise<void> {
- const url = `http://localhost:${argv.jsonrpcPort}`;
- const web3Provider = new Web3.providers.HttpProvider(url);
- const web3Wrapper = new Web3Wrapper(web3Provider);
- const networkId = await web3Wrapper.getNetworkIdAsync();
- const compilerOpts: CompilerOptions = {
- contractsDir: argv.contractsDir,
- networkId,
- optimizerEnabled: argv.shouldOptimize ? 1 : 0,
- artifactsDir: argv.artifactsDir,
- };
- await commands.compileAsync(compilerOpts);
+ const url = `http://localhost:${argv.jsonrpcPort}`;
+ const web3Provider = new Web3.providers.HttpProvider(url);
+ const web3Wrapper = new Web3Wrapper(web3Provider);
+ const networkId = await web3Wrapper.getNetworkIdAsync();
+ const compilerOpts: CompilerOptions = {
+ contractsDir: argv.contractsDir,
+ networkId,
+ optimizerEnabled: argv.shouldOptimize ? 1 : 0,
+ artifactsDir: argv.artifactsDir,
+ };
+ await commands.compileAsync(compilerOpts);
- const defaults = {
- gasPrice: new BigNumber(argv.gasPrice),
- from: argv.account,
- };
- const deployerOpts: DeployerOptions = {
- artifactsDir: argv.artifactsDir,
- jsonrpcPort: argv.jsonrpcPort,
- networkId,
- defaults,
- };
- const deployerArgsString = argv.args;
- const deployerArgs = deployerArgsString.split(',');
- await commands.deployAsync(argv.contract, deployerArgs, deployerOpts);
+ const defaults = {
+ gasPrice: new BigNumber(argv.gasPrice),
+ from: argv.account,
+ };
+ const deployerOpts: DeployerOptions = {
+ artifactsDir: argv.artifactsDir,
+ jsonrpcPort: argv.jsonrpcPort,
+ networkId,
+ defaults,
+ };
+ const deployerArgsString = argv.args;
+ const deployerArgs = deployerArgsString.split(',');
+ await commands.deployAsync(argv.contract, deployerArgs, deployerOpts);
}
/**
* Provides extra required options for deploy command.
* @param yargsInstance yargs instance provided in builder function callback.
*/
function deployCommandBuilder(yargsInstance: any) {
- return yargsInstance
- .option('contract', {
- type: 'string',
- description: 'name of contract to deploy, exluding .sol extension',
- })
- .option('args', {
- type: 'string',
- description: 'comma separated list of constructor args to deploy contract with',
- })
- .demandOption(['contract', 'args'])
- .help().argv;
+ return yargsInstance
+ .option('contract', {
+ type: 'string',
+ description: 'name of contract to deploy, exluding .sol extension',
+ })
+ .option('args', {
+ type: 'string',
+ description: 'comma separated list of constructor args to deploy contract with',
+ })
+ .demandOption(['contract', 'args'])
+ .help().argv;
}
(() => {
- const identityCommandBuilder = _.identity;
- return yargs
- .option('contracts-dir', {
- type: 'string',
- default: DEFAULT_CONTRACTS_DIR,
- description: 'path of contracts directory to compile',
- })
- .option('network-id', {
- type: 'number',
- default: DEFAULT_NETWORK_ID,
- description: 'mainnet=1, kovan=42, testrpc=50',
- })
- .option('should-optimize', {
- type: 'boolean',
- default: DEFAULT_OPTIMIZER_ENABLED,
- description: 'enable optimizer',
- })
- .option('artifacts-dir', {
- type: 'string',
- default: DEFAULT_ARTIFACTS_DIR,
- description: 'path to write contracts artifacts to',
- })
- .option('jsonrpc-port', {
- type: 'number',
- default: DEFAULT_JSONRPC_PORT,
- description: 'port connected to JSON RPC',
- })
- .option('gas-price', {
- type: 'string',
- default: DEFAULT_GAS_PRICE,
- description: 'gasPrice to be used for transactions',
- })
- .option('account', {
- type: 'string',
- description: 'account to use for deploying contracts',
- })
- .command('compile', 'compile contracts', identityCommandBuilder, onCompileCommand)
- .command(
- 'migrate',
- 'compile and deploy contracts using migration scripts',
- identityCommandBuilder,
- onMigrateCommand,
- )
- .command('deploy', 'deploy a single contract with provided arguments', deployCommandBuilder, onDeployCommand)
- .help().argv;
+ const identityCommandBuilder = _.identity;
+ return yargs
+ .option('contracts-dir', {
+ type: 'string',
+ default: DEFAULT_CONTRACTS_DIR,
+ description: 'path of contracts directory to compile',
+ })
+ .option('network-id', {
+ type: 'number',
+ default: DEFAULT_NETWORK_ID,
+ description: 'mainnet=1, kovan=42, testrpc=50',
+ })
+ .option('should-optimize', {
+ type: 'boolean',
+ default: DEFAULT_OPTIMIZER_ENABLED,
+ description: 'enable optimizer',
+ })
+ .option('artifacts-dir', {
+ type: 'string',
+ default: DEFAULT_ARTIFACTS_DIR,
+ description: 'path to write contracts artifacts to',
+ })
+ .option('jsonrpc-port', {
+ type: 'number',
+ default: DEFAULT_JSONRPC_PORT,
+ description: 'port connected to JSON RPC',
+ })
+ .option('gas-price', {
+ type: 'string',
+ default: DEFAULT_GAS_PRICE,
+ description: 'gasPrice to be used for transactions',
+ })
+ .option('account', {
+ type: 'string',
+ description: 'account to use for deploying contracts',
+ })
+ .command('compile', 'compile contracts', identityCommandBuilder, onCompileCommand)
+ .command(
+ 'migrate',
+ 'compile and deploy contracts using migration scripts',
+ identityCommandBuilder,
+ onMigrateCommand,
+ )
+ .command('deploy', 'deploy a single contract with provided arguments', deployCommandBuilder, onDeployCommand)
+ .help().argv;
})();
diff --git a/packages/deployer/src/commands.ts b/packages/deployer/src/commands.ts
index 63e75685e..2acef8e8f 100644
--- a/packages/deployer/src/commands.ts
+++ b/packages/deployer/src/commands.ts
@@ -4,16 +4,16 @@ import { Deployer } from './deployer';
import { CompilerOptions, DeployerOptions } from './utils/types';
export const commands = {
- async compileAsync(opts: CompilerOptions): Promise<void> {
- const compiler = new Compiler(opts);
- await compiler.compileAllAsync();
- },
- async migrateAsync(opts: DeployerOptions): Promise<void> {
- const deployer = new Deployer(opts);
- await migrator.runMigrationsAsync(deployer);
- },
- async deployAsync(contractName: string, args: any[], opts: DeployerOptions): Promise<void> {
- const deployer = new Deployer(opts);
- await deployer.deployAndSaveAsync(contractName, args);
- },
+ async compileAsync(opts: CompilerOptions): Promise<void> {
+ const compiler = new Compiler(opts);
+ await compiler.compileAllAsync();
+ },
+ async migrateAsync(opts: DeployerOptions): Promise<void> {
+ const deployer = new Deployer(opts);
+ await migrator.runMigrationsAsync(deployer);
+ },
+ async deployAsync(contractName: string, args: any[], opts: DeployerOptions): Promise<void> {
+ const deployer = new Deployer(opts);
+ await deployer.deployAndSaveAsync(contractName, args);
+ },
};
diff --git a/packages/deployer/src/compiler.ts b/packages/deployer/src/compiler.ts
index bc003149c..63db6c865 100644
--- a/packages/deployer/src/compiler.ts
+++ b/packages/deployer/src/compiler.ts
@@ -7,245 +7,245 @@ import * as Web3 from 'web3';
import { binPaths } from './solc/bin_paths';
import { fsWrapper } from './utils/fs_wrapper';
import {
- CompilerOptions,
- ContractArtifact,
- ContractData,
- ContractNetworks,
- ContractSources,
- ImportContents,
+ CompilerOptions,
+ ContractArtifact,
+ ContractData,
+ ContractNetworks,
+ ContractSources,
+ ImportContents,
} from './utils/types';
import { utils } from './utils/utils';
const SOLIDITY_FILE_EXTENSION = '.sol';
export class Compiler {
- private _contractsDir: string;
- private _networkId: number;
- private _optimizerEnabled: number;
- private _artifactsDir: string;
- private _contractSourcesIfExists?: ContractSources;
- private _solcErrors: Set<string>;
- /**
- * Recursively retrieves Solidity source code from directory.
- * @param dirPath Directory to search.
- * @return Mapping of contract name to contract source.
- */
- private static async _getContractSourcesAsync(dirPath: string): Promise<ContractSources> {
- let dirContents: string[] = [];
- try {
- dirContents = await fsWrapper.readdirAsync(dirPath);
- } catch (err) {
- throw new Error(`No directory found at ${dirPath}`);
- }
- let sources: ContractSources = {};
- for (const name of dirContents) {
- const contentPath = `${dirPath}/${name}`;
- if (path.extname(name) === SOLIDITY_FILE_EXTENSION) {
- try {
- const opts = {
- encoding: 'utf8',
- };
- sources[name] = await fsWrapper.readFileAsync(contentPath, opts);
- utils.consoleLog(`Reading ${name} source...`);
- } catch (err) {
- utils.consoleLog(`Could not find file at ${contentPath}`);
- }
- } else {
- try {
- const nestedSources = await Compiler._getContractSourcesAsync(contentPath);
- sources = {
- ...sources,
- ...nestedSources,
- };
- } catch (err) {
- utils.consoleLog(`${contentPath} is not a directory or ${SOLIDITY_FILE_EXTENSION} file`);
- }
- }
- }
- return sources;
- }
- /**
- * Searches Solidity source code for compiler version.
- * @param source Source code of contract.
- * @return Solc compiler version.
- */
- private static _parseSolidityVersion(source: string): string {
- const solcVersionMatch = source.match(/(?:solidity\s\^?)([0-9]{1,2}[.][0-9]{1,2}[.][0-9]{1,2})/);
- if (_.isNull(solcVersionMatch)) {
- throw new Error('Could not find Solidity version in source');
- }
- const solcVersion = solcVersionMatch[1];
- return solcVersion;
- }
- /**
- * Normalizes the path found in the error message.
- * Example: converts 'base/Token.sol:6:46: Warning: Unused local variable'
- * to 'Token.sol:6:46: Warning: Unused local variable'
- * This is used to prevent logging the same error multiple times.
- * @param errMsg An error message from the compiled output.
- * @return The error message with directories truncated from the contract path.
- */
- private static _getNormalizedErrMsg(errMsg: string): string {
- const errPathMatch = errMsg.match(/(.*\.sol)/);
- if (_.isNull(errPathMatch)) {
- throw new Error('Could not find a path in error message');
- }
- const errPath = errPathMatch[0];
- const baseContract = path.basename(errPath);
- const normalizedErrMsg = errMsg.replace(errPath, baseContract);
- return normalizedErrMsg;
- }
- /**
- * Instantiates a new instance of the Compiler class.
- * @param opts Options specifying directories, network, and optimization settings.
- * @return An instance of the Compiler class.
- */
- constructor(opts: CompilerOptions) {
- this._contractsDir = opts.contractsDir;
- this._networkId = opts.networkId;
- this._optimizerEnabled = opts.optimizerEnabled;
- this._artifactsDir = opts.artifactsDir;
- this._solcErrors = new Set();
- }
- /**
- * Compiles all Solidity files found in contractsDir and writes JSON artifacts to artifactsDir.
- */
- public async compileAllAsync(): Promise<void> {
- await this._createArtifactsDirIfDoesNotExistAsync();
- this._contractSourcesIfExists = await Compiler._getContractSourcesAsync(this._contractsDir);
- const contractBaseNames = _.keys(this._contractSourcesIfExists);
- const compiledContractPromises = _.map(contractBaseNames, async (contractBaseName: string): Promise<void> => {
- return this._compileContractAsync(contractBaseName);
- });
- await Promise.all(compiledContractPromises);
+ private _contractsDir: string;
+ private _networkId: number;
+ private _optimizerEnabled: number;
+ private _artifactsDir: string;
+ private _contractSourcesIfExists?: ContractSources;
+ private _solcErrors: Set<string>;
+ /**
+ * Recursively retrieves Solidity source code from directory.
+ * @param dirPath Directory to search.
+ * @return Mapping of contract name to contract source.
+ */
+ private static async _getContractSourcesAsync(dirPath: string): Promise<ContractSources> {
+ let dirContents: string[] = [];
+ try {
+ dirContents = await fsWrapper.readdirAsync(dirPath);
+ } catch (err) {
+ throw new Error(`No directory found at ${dirPath}`);
+ }
+ let sources: ContractSources = {};
+ for (const name of dirContents) {
+ const contentPath = `${dirPath}/${name}`;
+ if (path.extname(name) === SOLIDITY_FILE_EXTENSION) {
+ try {
+ const opts = {
+ encoding: 'utf8',
+ };
+ sources[name] = await fsWrapper.readFileAsync(contentPath, opts);
+ utils.consoleLog(`Reading ${name} source...`);
+ } catch (err) {
+ utils.consoleLog(`Could not find file at ${contentPath}`);
+ }
+ } else {
+ try {
+ const nestedSources = await Compiler._getContractSourcesAsync(contentPath);
+ sources = {
+ ...sources,
+ ...nestedSources,
+ };
+ } catch (err) {
+ utils.consoleLog(`${contentPath} is not a directory or ${SOLIDITY_FILE_EXTENSION} file`);
+ }
+ }
+ }
+ return sources;
+ }
+ /**
+ * Searches Solidity source code for compiler version.
+ * @param source Source code of contract.
+ * @return Solc compiler version.
+ */
+ private static _parseSolidityVersion(source: string): string {
+ const solcVersionMatch = source.match(/(?:solidity\s\^?)([0-9]{1,2}[.][0-9]{1,2}[.][0-9]{1,2})/);
+ if (_.isNull(solcVersionMatch)) {
+ throw new Error('Could not find Solidity version in source');
+ }
+ const solcVersion = solcVersionMatch[1];
+ return solcVersion;
+ }
+ /**
+ * Normalizes the path found in the error message.
+ * Example: converts 'base/Token.sol:6:46: Warning: Unused local variable'
+ * to 'Token.sol:6:46: Warning: Unused local variable'
+ * This is used to prevent logging the same error multiple times.
+ * @param errMsg An error message from the compiled output.
+ * @return The error message with directories truncated from the contract path.
+ */
+ private static _getNormalizedErrMsg(errMsg: string): string {
+ const errPathMatch = errMsg.match(/(.*\.sol)/);
+ if (_.isNull(errPathMatch)) {
+ throw new Error('Could not find a path in error message');
+ }
+ const errPath = errPathMatch[0];
+ const baseContract = path.basename(errPath);
+ const normalizedErrMsg = errMsg.replace(errPath, baseContract);
+ return normalizedErrMsg;
+ }
+ /**
+ * Instantiates a new instance of the Compiler class.
+ * @param opts Options specifying directories, network, and optimization settings.
+ * @return An instance of the Compiler class.
+ */
+ constructor(opts: CompilerOptions) {
+ this._contractsDir = opts.contractsDir;
+ this._networkId = opts.networkId;
+ this._optimizerEnabled = opts.optimizerEnabled;
+ this._artifactsDir = opts.artifactsDir;
+ this._solcErrors = new Set();
+ }
+ /**
+ * Compiles all Solidity files found in contractsDir and writes JSON artifacts to artifactsDir.
+ */
+ public async compileAllAsync(): Promise<void> {
+ await this._createArtifactsDirIfDoesNotExistAsync();
+ this._contractSourcesIfExists = await Compiler._getContractSourcesAsync(this._contractsDir);
+ const contractBaseNames = _.keys(this._contractSourcesIfExists);
+ const compiledContractPromises = _.map(contractBaseNames, async (contractBaseName: string): Promise<void> => {
+ return this._compileContractAsync(contractBaseName);
+ });
+ await Promise.all(compiledContractPromises);
- this._solcErrors.forEach(errMsg => {
- utils.consoleLog(errMsg);
- });
- }
- /**
- * Compiles contract and saves artifact to artifactsDir.
- * @param contractBaseName Name of contract with '.sol' extension.
- */
- private async _compileContractAsync(contractBaseName: string): Promise<void> {
- if (_.isUndefined(this._contractSourcesIfExists)) {
- throw new Error('Contract sources not yet initialized');
- }
+ this._solcErrors.forEach(errMsg => {
+ utils.consoleLog(errMsg);
+ });
+ }
+ /**
+ * Compiles contract and saves artifact to artifactsDir.
+ * @param contractBaseName Name of contract with '.sol' extension.
+ */
+ private async _compileContractAsync(contractBaseName: string): Promise<void> {
+ if (_.isUndefined(this._contractSourcesIfExists)) {
+ throw new Error('Contract sources not yet initialized');
+ }
- const source = this._contractSourcesIfExists[contractBaseName];
- const contractName = path.basename(contractBaseName, SOLIDITY_FILE_EXTENSION);
- const currentArtifactPath = `${this._artifactsDir}/${contractName}.json`;
- const sourceHash = `0x${ethUtil.sha3(source).toString('hex')}`;
+ const source = this._contractSourcesIfExists[contractBaseName];
+ const contractName = path.basename(contractBaseName, SOLIDITY_FILE_EXTENSION);
+ const currentArtifactPath = `${this._artifactsDir}/${contractName}.json`;
+ const sourceHash = `0x${ethUtil.sha3(source).toString('hex')}`;
- let currentArtifactString: string;
- let currentArtifact: ContractArtifact;
- let oldNetworks: ContractNetworks;
- let shouldCompile: boolean;
- try {
- const opts = {
- encoding: 'utf8',
- };
- currentArtifactString = await fsWrapper.readFileAsync(currentArtifactPath, opts);
- currentArtifact = JSON.parse(currentArtifactString);
- oldNetworks = currentArtifact.networks;
- const oldNetwork: ContractData = oldNetworks[this._networkId];
- shouldCompile =
- _.isUndefined(oldNetwork) ||
- oldNetwork.keccak256 !== sourceHash ||
- oldNetwork.optimizer_enabled !== this._optimizerEnabled;
- } catch (err) {
- shouldCompile = true;
- }
+ let currentArtifactString: string;
+ let currentArtifact: ContractArtifact;
+ let oldNetworks: ContractNetworks;
+ let shouldCompile: boolean;
+ try {
+ const opts = {
+ encoding: 'utf8',
+ };
+ currentArtifactString = await fsWrapper.readFileAsync(currentArtifactPath, opts);
+ currentArtifact = JSON.parse(currentArtifactString);
+ oldNetworks = currentArtifact.networks;
+ const oldNetwork: ContractData = oldNetworks[this._networkId];
+ shouldCompile =
+ _.isUndefined(oldNetwork) ||
+ oldNetwork.keccak256 !== sourceHash ||
+ oldNetwork.optimizer_enabled !== this._optimizerEnabled;
+ } catch (err) {
+ shouldCompile = true;
+ }
- if (!shouldCompile) {
- return;
- }
+ if (!shouldCompile) {
+ return;
+ }
- const input = {
- [contractBaseName]: source,
- };
- const solcVersion = Compiler._parseSolidityVersion(source);
- const fullSolcVersion = binPaths[solcVersion];
- const solcBinPath = `./solc/solc_bin/${fullSolcVersion}`;
- const solcBin = require(solcBinPath);
- const solcInstance = solc.setupMethods(solcBin);
+ const input = {
+ [contractBaseName]: source,
+ };
+ const solcVersion = Compiler._parseSolidityVersion(source);
+ const fullSolcVersion = binPaths[solcVersion];
+ const solcBinPath = `./solc/solc_bin/${fullSolcVersion}`;
+ const solcBin = require(solcBinPath);
+ const solcInstance = solc.setupMethods(solcBin);
- utils.consoleLog(`Compiling ${contractBaseName}...`);
- const sourcesToCompile = {
- sources: input,
- };
- const compiled = solcInstance.compile(
- sourcesToCompile,
- this._optimizerEnabled,
- this._findImportsIfSourcesExist.bind(this),
- );
+ utils.consoleLog(`Compiling ${contractBaseName}...`);
+ const sourcesToCompile = {
+ sources: input,
+ };
+ const compiled = solcInstance.compile(
+ sourcesToCompile,
+ this._optimizerEnabled,
+ this._findImportsIfSourcesExist.bind(this),
+ );
- if (!_.isUndefined(compiled.errors)) {
- _.each(compiled.errors, errMsg => {
- const normalizedErrMsg = Compiler._getNormalizedErrMsg(errMsg);
- this._solcErrors.add(normalizedErrMsg);
- });
- }
+ if (!_.isUndefined(compiled.errors)) {
+ _.each(compiled.errors, errMsg => {
+ const normalizedErrMsg = Compiler._getNormalizedErrMsg(errMsg);
+ this._solcErrors.add(normalizedErrMsg);
+ });
+ }
- const contractIdentifier = `${contractBaseName}:${contractName}`;
- const abi: Web3.ContractAbi = JSON.parse(compiled.contracts[contractIdentifier].interface);
- const unlinked_binary = `0x${compiled.contracts[contractIdentifier].bytecode}`;
- const updated_at = Date.now();
- const contractData: ContractData = {
- solc_version: solcVersion,
- keccak256: sourceHash,
- optimizer_enabled: this._optimizerEnabled,
- abi,
- unlinked_binary,
- updated_at,
- };
+ const contractIdentifier = `${contractBaseName}:${contractName}`;
+ const abi: Web3.ContractAbi = JSON.parse(compiled.contracts[contractIdentifier].interface);
+ const unlinked_binary = `0x${compiled.contracts[contractIdentifier].bytecode}`;
+ const updated_at = Date.now();
+ const contractData: ContractData = {
+ solc_version: solcVersion,
+ keccak256: sourceHash,
+ optimizer_enabled: this._optimizerEnabled,
+ abi,
+ unlinked_binary,
+ updated_at,
+ };
- let newArtifact: ContractArtifact;
- if (!_.isUndefined(currentArtifactString)) {
- newArtifact = {
- ...currentArtifact,
- networks: {
- ...oldNetworks,
- [this._networkId]: contractData,
- },
- };
- } else {
- newArtifact = {
- contract_name: contractName,
- networks: {
- [this._networkId]: contractData,
- },
- };
- }
+ let newArtifact: ContractArtifact;
+ if (!_.isUndefined(currentArtifactString)) {
+ newArtifact = {
+ ...currentArtifact,
+ networks: {
+ ...oldNetworks,
+ [this._networkId]: contractData,
+ },
+ };
+ } else {
+ newArtifact = {
+ contract_name: contractName,
+ networks: {
+ [this._networkId]: contractData,
+ },
+ };
+ }
- const artifactString = utils.stringifyWithFormatting(newArtifact);
- await fsWrapper.writeFileAsync(currentArtifactPath, artifactString);
- utils.consoleLog(`${contractBaseName} artifact saved!`);
- }
- /**
- * Callback to resolve dependencies with `solc.compile`.
- * Throws error if contractSources not yet initialized.
- * @param importPath Path to an imported dependency.
- * @return Import contents object containing source code of dependency.
- */
- private _findImportsIfSourcesExist(importPath: string): ImportContents {
- if (_.isUndefined(this._contractSourcesIfExists)) {
- throw new Error('Contract sources not yet initialized');
- }
- const contractBaseName = path.basename(importPath);
- const source = this._contractSourcesIfExists[contractBaseName];
- const importContents: ImportContents = {
- contents: source,
- };
- return importContents;
- }
- /**
- * Creates the artifacts directory if it does not already exist.
- */
- private async _createArtifactsDirIfDoesNotExistAsync(): Promise<void> {
- if (!fsWrapper.doesPathExistSync(this._artifactsDir)) {
- utils.consoleLog('Creating artifacts directory...');
- await fsWrapper.mkdirAsync(this._artifactsDir);
- }
- }
+ const artifactString = utils.stringifyWithFormatting(newArtifact);
+ await fsWrapper.writeFileAsync(currentArtifactPath, artifactString);
+ utils.consoleLog(`${contractBaseName} artifact saved!`);
+ }
+ /**
+ * Callback to resolve dependencies with `solc.compile`.
+ * Throws error if contractSources not yet initialized.
+ * @param importPath Path to an imported dependency.
+ * @return Import contents object containing source code of dependency.
+ */
+ private _findImportsIfSourcesExist(importPath: string): ImportContents {
+ if (_.isUndefined(this._contractSourcesIfExists)) {
+ throw new Error('Contract sources not yet initialized');
+ }
+ const contractBaseName = path.basename(importPath);
+ const source = this._contractSourcesIfExists[contractBaseName];
+ const importContents: ImportContents = {
+ contents: source,
+ };
+ return importContents;
+ }
+ /**
+ * Creates the artifacts directory if it does not already exist.
+ */
+ private async _createArtifactsDirIfDoesNotExistAsync(): Promise<void> {
+ if (!fsWrapper.doesPathExistSync(this._artifactsDir)) {
+ utils.consoleLog('Creating artifacts directory...');
+ await fsWrapper.mkdirAsync(this._artifactsDir);
+ }
+ }
}
diff --git a/packages/deployer/src/deployer.ts b/packages/deployer/src/deployer.ts
index 34e20a197..6f03581e8 100644
--- a/packages/deployer/src/deployer.ts
+++ b/packages/deployer/src/deployer.ts
@@ -13,168 +13,168 @@ import { utils } from './utils/utils';
const EXTRA_GAS = 200000;
export class Deployer {
- public web3Wrapper: Web3Wrapper;
- private _artifactsDir: string;
- private _jsonrpcPort: number;
- private _networkId: number;
- private _defaults: Partial<TxData>;
+ public web3Wrapper: Web3Wrapper;
+ private _artifactsDir: string;
+ private _jsonrpcPort: number;
+ private _networkId: number;
+ private _defaults: Partial<TxData>;
- constructor(opts: DeployerOptions) {
- this._artifactsDir = opts.artifactsDir;
- this._jsonrpcPort = opts.jsonrpcPort;
- this._networkId = opts.networkId;
- const jsonrpcUrl = `http://localhost:${this._jsonrpcPort}`;
- const web3Provider = new Web3.providers.HttpProvider(jsonrpcUrl);
- this._defaults = opts.defaults;
- this.web3Wrapper = new Web3Wrapper(web3Provider, this._defaults);
- }
- /**
- * Loads contract artifact and deploys contract with given arguments.
- * @param contractName Name of the contract to deploy. Must match name of an artifact in artifacts directory.
- * @param args Array of contract constructor arguments.
- * @return Deployed contract instance.
- */
- public async deployAsync(contractName: string, args: any[] = []): Promise<Web3.ContractInstance> {
- const contractArtifact: ContractArtifact = this._loadContractArtifactIfExists(contractName);
- const contractData: ContractData = this._getContractDataFromArtifactIfExists(contractArtifact);
- const data = contractData.unlinked_binary;
- const from = await this._getFromAddressAsync();
- const gas = await this._getAllowableGasEstimateAsync(data);
- const txData = {
- gasPrice: this._defaults.gasPrice,
- from,
- data,
- gas,
- };
- const abi = contractData.abi;
- const web3ContractInstance = await this._deployFromAbiAsync(abi, args, txData);
- utils.consoleLog(`${contractName}.sol successfully deployed at ${web3ContractInstance.address}`);
- const contractInstance = new Contract(web3ContractInstance, this._defaults);
- return contractInstance;
- }
- /**
- * Loads contract artifact, deploys with given arguments, and saves updated data to artifact.
- * @param contractName Name of the contract to deploy. Must match name of an artifact in artifacts directory.
- * @param args Array of contract constructor arguments.
- * @return Deployed contract instance.
- */
- public async deployAndSaveAsync(contractName: string, args: any[] = []): Promise<Web3.ContractInstance> {
- const contractInstance = await this.deployAsync(contractName, args);
- await this._saveContractDataToArtifactAsync(contractName, contractInstance.address, args);
- return contractInstance;
- }
- /**
- * Deploys a contract given its ABI, arguments, and transaction data.
- * @param abi ABI of contract to deploy.
- * @param args Constructor arguments to use in deployment.
- * @param txData Tx options used for deployment.
- * @return Promise that resolves to a web3 contract instance.
- */
- private async _deployFromAbiAsync(abi: Web3.ContractAbi, args: any[], txData: Web3.TxData): Promise<any> {
- const contract: Web3.Contract<Web3.ContractInstance> = this.web3Wrapper.getContractFromAbi(abi);
- const deployPromise = new Promise((resolve, reject) => {
- /**
- * Contract is inferred as 'any' because TypeScript
- * is not able to read 'new' from the Contract interface
- */
- (contract as any).new(...args, txData, (err: Error, res: any): any => {
- if (err) {
- reject(err);
- } else if (_.isUndefined(res.address) && !_.isUndefined(res.transactionHash)) {
- utils.consoleLog(`transactionHash: ${res.transactionHash}`);
- } else {
- resolve(res);
- }
- });
- });
- return deployPromise;
- }
- /**
- * Updates a contract artifact's address and encoded constructor arguments.
- * @param contractName Name of contract. Must match an existing artifact.
- * @param contractAddress Contract address to save to artifact.
- * @param args Contract constructor arguments that will be encoded and saved to artifact.
- */
- private async _saveContractDataToArtifactAsync(
- contractName: string,
- contractAddress: string,
- args: any[],
- ): Promise<void> {
- const contractArtifact: ContractArtifact = this._loadContractArtifactIfExists(contractName);
- const contractData: ContractData = this._getContractDataFromArtifactIfExists(contractArtifact);
- const abi = contractData.abi;
- const encodedConstructorArgs = encoder.encodeConstructorArgsFromAbi(args, abi);
- const newContractData = {
- ...contractData,
- address: contractAddress,
- constructor_args: encodedConstructorArgs,
- };
- const newArtifact = {
- ...contractArtifact,
- networks: {
- ...contractArtifact.networks,
- [this._networkId]: newContractData,
- },
- };
- const artifactString = utils.stringifyWithFormatting(newArtifact);
- const artifactPath = `${this._artifactsDir}/${contractName}.json`;
- await fsWrapper.writeFileAsync(artifactPath, artifactString);
- }
- /**
- * Loads a contract artifact, if it exists.
- * @param contractName Name of the contract, without the extension.
- * @return The contract artifact.
- */
- private _loadContractArtifactIfExists(contractName: string): ContractArtifact {
- const artifactPath = `${this._artifactsDir}/${contractName}.json`;
- try {
- const contractArtifact: ContractArtifact = require(artifactPath);
- return contractArtifact;
- } catch (err) {
- throw new Error(`Artifact not found for contract: ${contractName}`);
- }
- }
- /**
- * Gets data for current networkId stored in artifact.
- * @param contractArtifact The contract artifact.
- * @return Network specific contract data.
- */
- private _getContractDataFromArtifactIfExists(contractArtifact: ContractArtifact): ContractData {
- const contractData = contractArtifact.networks[this._networkId];
- if (_.isUndefined(contractData)) {
- throw new Error(`Data not found in artifact for contract: ${contractArtifact.contract_name}`);
- }
- return contractData;
- }
- /**
- * Gets the address to use for sending a transaction.
- * @return The default from address. If not specified, returns the first address accessible by web3.
- */
- private async _getFromAddressAsync(): Promise<string> {
- let from: string;
- if (_.isUndefined(this._defaults.from)) {
- const accounts = await this.web3Wrapper.getAvailableAddressesAsync();
- from = accounts[0];
- } else {
- from = this._defaults.from;
- }
- return from;
- }
- /**
- * Estimates the gas required for a transaction.
- * If gas would be over the block gas limit, the max allowable gas is returned instead.
- * @param data Bytecode to estimate gas for.
- * @return Gas estimate for transaction data.
- */
- private async _getAllowableGasEstimateAsync(data: string): Promise<number> {
- const block = await this.web3Wrapper.getBlockAsync('latest');
- let gas: number;
- try {
- const gasEstimate: number = await this.web3Wrapper.estimateGasAsync(data);
- gas = Math.min(gasEstimate + EXTRA_GAS, block.gasLimit);
- } catch (err) {
- gas = block.gasLimit;
- }
- return gas;
- }
+ constructor(opts: DeployerOptions) {
+ this._artifactsDir = opts.artifactsDir;
+ this._jsonrpcPort = opts.jsonrpcPort;
+ this._networkId = opts.networkId;
+ const jsonrpcUrl = `http://localhost:${this._jsonrpcPort}`;
+ const web3Provider = new Web3.providers.HttpProvider(jsonrpcUrl);
+ this._defaults = opts.defaults;
+ this.web3Wrapper = new Web3Wrapper(web3Provider, this._defaults);
+ }
+ /**
+ * Loads contract artifact and deploys contract with given arguments.
+ * @param contractName Name of the contract to deploy. Must match name of an artifact in artifacts directory.
+ * @param args Array of contract constructor arguments.
+ * @return Deployed contract instance.
+ */
+ public async deployAsync(contractName: string, args: any[] = []): Promise<Web3.ContractInstance> {
+ const contractArtifact: ContractArtifact = this._loadContractArtifactIfExists(contractName);
+ const contractData: ContractData = this._getContractDataFromArtifactIfExists(contractArtifact);
+ const data = contractData.unlinked_binary;
+ const from = await this._getFromAddressAsync();
+ const gas = await this._getAllowableGasEstimateAsync(data);
+ const txData = {
+ gasPrice: this._defaults.gasPrice,
+ from,
+ data,
+ gas,
+ };
+ const abi = contractData.abi;
+ const web3ContractInstance = await this._deployFromAbiAsync(abi, args, txData);
+ utils.consoleLog(`${contractName}.sol successfully deployed at ${web3ContractInstance.address}`);
+ const contractInstance = new Contract(web3ContractInstance, this._defaults);
+ return contractInstance;
+ }
+ /**
+ * Loads contract artifact, deploys with given arguments, and saves updated data to artifact.
+ * @param contractName Name of the contract to deploy. Must match name of an artifact in artifacts directory.
+ * @param args Array of contract constructor arguments.
+ * @return Deployed contract instance.
+ */
+ public async deployAndSaveAsync(contractName: string, args: any[] = []): Promise<Web3.ContractInstance> {
+ const contractInstance = await this.deployAsync(contractName, args);
+ await this._saveContractDataToArtifactAsync(contractName, contractInstance.address, args);
+ return contractInstance;
+ }
+ /**
+ * Deploys a contract given its ABI, arguments, and transaction data.
+ * @param abi ABI of contract to deploy.
+ * @param args Constructor arguments to use in deployment.
+ * @param txData Tx options used for deployment.
+ * @return Promise that resolves to a web3 contract instance.
+ */
+ private async _deployFromAbiAsync(abi: Web3.ContractAbi, args: any[], txData: Web3.TxData): Promise<any> {
+ const contract: Web3.Contract<Web3.ContractInstance> = this.web3Wrapper.getContractFromAbi(abi);
+ const deployPromise = new Promise((resolve, reject) => {
+ /**
+ * Contract is inferred as 'any' because TypeScript
+ * is not able to read 'new' from the Contract interface
+ */
+ (contract as any).new(...args, txData, (err: Error, res: any): any => {
+ if (err) {
+ reject(err);
+ } else if (_.isUndefined(res.address) && !_.isUndefined(res.transactionHash)) {
+ utils.consoleLog(`transactionHash: ${res.transactionHash}`);
+ } else {
+ resolve(res);
+ }
+ });
+ });
+ return deployPromise;
+ }
+ /**
+ * Updates a contract artifact's address and encoded constructor arguments.
+ * @param contractName Name of contract. Must match an existing artifact.
+ * @param contractAddress Contract address to save to artifact.
+ * @param args Contract constructor arguments that will be encoded and saved to artifact.
+ */
+ private async _saveContractDataToArtifactAsync(
+ contractName: string,
+ contractAddress: string,
+ args: any[],
+ ): Promise<void> {
+ const contractArtifact: ContractArtifact = this._loadContractArtifactIfExists(contractName);
+ const contractData: ContractData = this._getContractDataFromArtifactIfExists(contractArtifact);
+ const abi = contractData.abi;
+ const encodedConstructorArgs = encoder.encodeConstructorArgsFromAbi(args, abi);
+ const newContractData = {
+ ...contractData,
+ address: contractAddress,
+ constructor_args: encodedConstructorArgs,
+ };
+ const newArtifact = {
+ ...contractArtifact,
+ networks: {
+ ...contractArtifact.networks,
+ [this._networkId]: newContractData,
+ },
+ };
+ const artifactString = utils.stringifyWithFormatting(newArtifact);
+ const artifactPath = `${this._artifactsDir}/${contractName}.json`;
+ await fsWrapper.writeFileAsync(artifactPath, artifactString);
+ }
+ /**
+ * Loads a contract artifact, if it exists.
+ * @param contractName Name of the contract, without the extension.
+ * @return The contract artifact.
+ */
+ private _loadContractArtifactIfExists(contractName: string): ContractArtifact {
+ const artifactPath = `${this._artifactsDir}/${contractName}.json`;
+ try {
+ const contractArtifact: ContractArtifact = require(artifactPath);
+ return contractArtifact;
+ } catch (err) {
+ throw new Error(`Artifact not found for contract: ${contractName}`);
+ }
+ }
+ /**
+ * Gets data for current networkId stored in artifact.
+ * @param contractArtifact The contract artifact.
+ * @return Network specific contract data.
+ */
+ private _getContractDataFromArtifactIfExists(contractArtifact: ContractArtifact): ContractData {
+ const contractData = contractArtifact.networks[this._networkId];
+ if (_.isUndefined(contractData)) {
+ throw new Error(`Data not found in artifact for contract: ${contractArtifact.contract_name}`);
+ }
+ return contractData;
+ }
+ /**
+ * Gets the address to use for sending a transaction.
+ * @return The default from address. If not specified, returns the first address accessible by web3.
+ */
+ private async _getFromAddressAsync(): Promise<string> {
+ let from: string;
+ if (_.isUndefined(this._defaults.from)) {
+ const accounts = await this.web3Wrapper.getAvailableAddressesAsync();
+ from = accounts[0];
+ } else {
+ from = this._defaults.from;
+ }
+ return from;
+ }
+ /**
+ * Estimates the gas required for a transaction.
+ * If gas would be over the block gas limit, the max allowable gas is returned instead.
+ * @param data Bytecode to estimate gas for.
+ * @return Gas estimate for transaction data.
+ */
+ private async _getAllowableGasEstimateAsync(data: string): Promise<number> {
+ const block = await this.web3Wrapper.getBlockAsync('latest');
+ let gas: number;
+ try {
+ const gasEstimate: number = await this.web3Wrapper.estimateGasAsync(data);
+ gas = Math.min(gasEstimate + EXTRA_GAS, block.gasLimit);
+ } catch (err) {
+ gas = block.gasLimit;
+ }
+ return gas;
+ }
}
diff --git a/packages/deployer/src/globals.d.ts b/packages/deployer/src/globals.d.ts
index d61f54870..0a35002da 100644
--- a/packages/deployer/src/globals.d.ts
+++ b/packages/deployer/src/globals.d.ts
@@ -1,8 +1,8 @@
declare module 'solc' {
- export function compile(sources: any, optimizerEnabled: number, findImports: (importPath: string) => any): any;
- export function setupMethods(solcBin: any): any;
+ export function compile(sources: any, optimizerEnabled: number, findImports: (importPath: string) => any): any;
+ export function setupMethods(solcBin: any): any;
}
declare module 'web3-eth-abi' {
- export function encodeParameters(typesArray: string[], parameters: any[]): string;
+ export function encodeParameters(typesArray: string[], parameters: any[]): string;
}
diff --git a/packages/deployer/src/migrations/config/multisig_sample.ts b/packages/deployer/src/migrations/config/multisig_sample.ts
index d65ab06d2..dabeb7a4f 100644
--- a/packages/deployer/src/migrations/config/multisig_sample.ts
+++ b/packages/deployer/src/migrations/config/multisig_sample.ts
@@ -2,9 +2,9 @@ import { MultiSigConfigByNetwork } from '../../types';
// Make a copy of this file named `multisig.js` and input custom params as needed
export const multiSig: MultiSigConfigByNetwork = {
- kovan: {
- owners: [],
- confirmationsRequired: 0,
- secondsRequired: 0,
- },
+ kovan: {
+ owners: [],
+ confirmationsRequired: 0,
+ secondsRequired: 0,
+ },
};
diff --git a/packages/deployer/src/migrations/config/token_info.ts b/packages/deployer/src/migrations/config/token_info.ts
index ebcd3f796..7e822fc3b 100644
--- a/packages/deployer/src/migrations/config/token_info.ts
+++ b/packages/deployer/src/migrations/config/token_info.ts
@@ -2,39 +2,39 @@ import { constants } from '../../utils/constants';
import { Token } from '../../types';
export const tokenInfo: Token[] = [
- {
- name: 'Augur Reputation Token',
- symbol: 'REP',
- decimals: 18,
- ipfsHash: constants.NULL_BYTES,
- swarmHash: constants.NULL_BYTES,
- },
- {
- name: 'Digix DAO Token',
- symbol: 'DGD',
- decimals: 18,
- ipfsHash: constants.NULL_BYTES,
- swarmHash: constants.NULL_BYTES,
- },
- {
- name: 'Golem Network Token',
- symbol: 'GNT',
- decimals: 18,
- ipfsHash: constants.NULL_BYTES,
- swarmHash: constants.NULL_BYTES,
- },
- {
- name: 'MakerDAO',
- symbol: 'MKR',
- decimals: 18,
- ipfsHash: constants.NULL_BYTES,
- swarmHash: constants.NULL_BYTES,
- },
- {
- name: 'Melon Token',
- symbol: 'MLN',
- decimals: 18,
- ipfsHash: constants.NULL_BYTES,
- swarmHash: constants.NULL_BYTES,
- },
+ {
+ name: 'Augur Reputation Token',
+ symbol: 'REP',
+ decimals: 18,
+ ipfsHash: constants.NULL_BYTES,
+ swarmHash: constants.NULL_BYTES,
+ },
+ {
+ name: 'Digix DAO Token',
+ symbol: 'DGD',
+ decimals: 18,
+ ipfsHash: constants.NULL_BYTES,
+ swarmHash: constants.NULL_BYTES,
+ },
+ {
+ name: 'Golem Network Token',
+ symbol: 'GNT',
+ decimals: 18,
+ ipfsHash: constants.NULL_BYTES,
+ swarmHash: constants.NULL_BYTES,
+ },
+ {
+ name: 'MakerDAO',
+ symbol: 'MKR',
+ decimals: 18,
+ ipfsHash: constants.NULL_BYTES,
+ swarmHash: constants.NULL_BYTES,
+ },
+ {
+ name: 'Melon Token',
+ symbol: 'MLN',
+ decimals: 18,
+ ipfsHash: constants.NULL_BYTES,
+ swarmHash: constants.NULL_BYTES,
+ },
];
diff --git a/packages/deployer/src/migrations/migrate.ts b/packages/deployer/src/migrations/migrate.ts
index 48cf93000..393806b45 100644
--- a/packages/deployer/src/migrations/migrate.ts
+++ b/packages/deployer/src/migrations/migrate.ts
@@ -7,84 +7,84 @@ import { constants } from '../utils/constants';
import { tokenInfo } from './config/token_info';
export const migrator = {
- /**
- * Custom migrations should be defined in this function. This will be called with the CLI 'migrate' command.
- * Some operations might be completed in parallel, but we don't do that on purpose.
- * That way the addresses are deterministic.
- * @param deployer Deployer instance.
- */
- async runMigrationsAsync(deployer: Deployer): Promise<void> {
- const web3Wrapper: Web3Wrapper = deployer.web3Wrapper;
- const accounts: string[] = await web3Wrapper.getAvailableAddressesAsync();
+ /**
+ * Custom migrations should be defined in this function. This will be called with the CLI 'migrate' command.
+ * Some operations might be completed in parallel, but we don't do that on purpose.
+ * That way the addresses are deterministic.
+ * @param deployer Deployer instance.
+ */
+ async runMigrationsAsync(deployer: Deployer): Promise<void> {
+ const web3Wrapper: Web3Wrapper = deployer.web3Wrapper;
+ const accounts: string[] = await web3Wrapper.getAvailableAddressesAsync();
- const tokenTransferProxy = await deployer.deployAndSaveAsync('TokenTransferProxy');
- const zrxToken = await deployer.deployAndSaveAsync('ZRXToken');
- const etherToken = await deployer.deployAndSaveAsync('WETH9');
- const tokenReg = await deployer.deployAndSaveAsync('TokenRegistry');
+ const tokenTransferProxy = await deployer.deployAndSaveAsync('TokenTransferProxy');
+ const zrxToken = await deployer.deployAndSaveAsync('ZRXToken');
+ const etherToken = await deployer.deployAndSaveAsync('WETH9');
+ const tokenReg = await deployer.deployAndSaveAsync('TokenRegistry');
- const exchangeArgs = [zrxToken.address, tokenTransferProxy.address];
- const owners = [accounts[0], accounts[1]];
- const confirmationsRequired = new BigNumber(2);
- const secondsRequired = new BigNumber(0);
- const multiSigArgs = [owners, confirmationsRequired, secondsRequired, tokenTransferProxy.address];
- const exchange = await deployer.deployAndSaveAsync('Exchange', exchangeArgs);
- const multiSig = await deployer.deployAndSaveAsync(
- 'MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress',
- multiSigArgs,
- );
+ const exchangeArgs = [zrxToken.address, tokenTransferProxy.address];
+ const owners = [accounts[0], accounts[1]];
+ const confirmationsRequired = new BigNumber(2);
+ const secondsRequired = new BigNumber(0);
+ const multiSigArgs = [owners, confirmationsRequired, secondsRequired, tokenTransferProxy.address];
+ const exchange = await deployer.deployAndSaveAsync('Exchange', exchangeArgs);
+ const multiSig = await deployer.deployAndSaveAsync(
+ 'MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress',
+ multiSigArgs,
+ );
- const owner = accounts[0];
- await tokenTransferProxy.addAuthorizedAddress.sendTransactionAsync(exchange.address, { from: owner });
- await tokenTransferProxy.transferOwnership.sendTransactionAsync(multiSig.address, { from: owner });
- const addTokenGasEstimate = await tokenReg.addToken.estimateGasAsync(
- zrxToken.address,
- tokenInfo[0].name,
- tokenInfo[0].symbol,
- tokenInfo[0].decimals,
- tokenInfo[0].ipfsHash,
- tokenInfo[0].swarmHash,
- { from: owner },
- );
- await tokenReg.addToken.sendTransactionAsync(
- zrxToken.address,
- '0x Protocol Token',
- 'ZRX',
- 18,
- constants.NULL_BYTES,
- constants.NULL_BYTES,
- {
- from: owner,
- gas: addTokenGasEstimate,
- },
- );
- await tokenReg.addToken.sendTransactionAsync(
- etherToken.address,
- 'Ether Token',
- 'WETH',
- 18,
- constants.NULL_BYTES,
- constants.NULL_BYTES,
- {
- from: owner,
- gas: addTokenGasEstimate,
- },
- );
- for (const token of tokenInfo) {
- const totalSupply = new BigNumber(0);
- const args = [token.name, token.symbol, token.decimals, totalSupply];
- const dummyToken = await deployer.deployAsync('DummyToken', args);
- await tokenReg.addToken.sendTransactionAsync(
- dummyToken.address,
- token.name,
- token.symbol,
- token.decimals,
- token.ipfsHash,
- token.swarmHash,
- {
- from: owner,
- gas: addTokenGasEstimate,
- },
- );
- }
- },
+ const owner = accounts[0];
+ await tokenTransferProxy.addAuthorizedAddress.sendTransactionAsync(exchange.address, { from: owner });
+ await tokenTransferProxy.transferOwnership.sendTransactionAsync(multiSig.address, { from: owner });
+ const addTokenGasEstimate = await tokenReg.addToken.estimateGasAsync(
+ zrxToken.address,
+ tokenInfo[0].name,
+ tokenInfo[0].symbol,
+ tokenInfo[0].decimals,
+ tokenInfo[0].ipfsHash,
+ tokenInfo[0].swarmHash,
+ { from: owner },
+ );
+ await tokenReg.addToken.sendTransactionAsync(
+ zrxToken.address,
+ '0x Protocol Token',
+ 'ZRX',
+ 18,
+ constants.NULL_BYTES,
+ constants.NULL_BYTES,
+ {
+ from: owner,
+ gas: addTokenGasEstimate,
+ },
+ );
+ await tokenReg.addToken.sendTransactionAsync(
+ etherToken.address,
+ 'Ether Token',
+ 'WETH',
+ 18,
+ constants.NULL_BYTES,
+ constants.NULL_BYTES,
+ {
+ from: owner,
+ gas: addTokenGasEstimate,
+ },
+ );
+ for (const token of tokenInfo) {
+ const totalSupply = new BigNumber(0);
+ const args = [token.name, token.symbol, token.decimals, totalSupply];
+ const dummyToken = await deployer.deployAsync('DummyToken', args);
+ await tokenReg.addToken.sendTransactionAsync(
+ dummyToken.address,
+ token.name,
+ token.symbol,
+ token.decimals,
+ token.ipfsHash,
+ token.swarmHash,
+ {
+ from: owner,
+ gas: addTokenGasEstimate,
+ },
+ );
+ }
+ },
};
diff --git a/packages/deployer/src/solc/bin_paths.ts b/packages/deployer/src/solc/bin_paths.ts
index 7a22f911a..e5b107101 100644
--- a/packages/deployer/src/solc/bin_paths.ts
+++ b/packages/deployer/src/solc/bin_paths.ts
@@ -1,15 +1,15 @@
export interface BinaryPaths {
- [key: string]: string;
+ [key: string]: string;
}
export const binPaths: BinaryPaths = {
- '0.4.10': 'soljson-v0.4.10+commit.f0d539ae.js',
- '0.4.11': 'soljson-v0.4.11+commit.68ef5810.js',
- '0.4.12': 'soljson-v0.4.12+commit.194ff033.js',
- '0.4.13': 'soljson-v0.4.13+commit.fb4cb1a.js',
- '0.4.14': 'soljson-v0.4.14+commit.c2215d46.js',
- '0.4.15': 'soljson-v0.4.15+commit.bbb8e64f.js',
- '0.4.16': 'soljson-v0.4.16+commit.d7661dd9.js',
- '0.4.17': 'soljson-v0.4.17+commit.bdeb9e52.js',
- '0.4.18': 'soljson-v0.4.18+commit.9cf6e910.js',
+ '0.4.10': 'soljson-v0.4.10+commit.f0d539ae.js',
+ '0.4.11': 'soljson-v0.4.11+commit.68ef5810.js',
+ '0.4.12': 'soljson-v0.4.12+commit.194ff033.js',
+ '0.4.13': 'soljson-v0.4.13+commit.fb4cb1a.js',
+ '0.4.14': 'soljson-v0.4.14+commit.c2215d46.js',
+ '0.4.15': 'soljson-v0.4.15+commit.bbb8e64f.js',
+ '0.4.16': 'soljson-v0.4.16+commit.d7661dd9.js',
+ '0.4.17': 'soljson-v0.4.17+commit.bdeb9e52.js',
+ '0.4.18': 'soljson-v0.4.18+commit.9cf6e910.js',
};
diff --git a/packages/deployer/src/types.ts b/packages/deployer/src/types.ts
index aeff39d46..58d1e5b4f 100644
--- a/packages/deployer/src/types.ts
+++ b/packages/deployer/src/types.ts
@@ -1,23 +1,23 @@
export interface MultiSigConfig {
- owners: string[];
- confirmationsRequired: number;
- secondsRequired: number;
+ owners: string[];
+ confirmationsRequired: number;
+ secondsRequired: number;
}
export interface MultiSigConfigByNetwork {
- [networkName: string]: MultiSigConfig;
+ [networkName: string]: MultiSigConfig;
}
export interface Token {
- address?: string;
- name: string;
- symbol: string;
- decimals: number;
- ipfsHash: string;
- swarmHash: string;
+ address?: string;
+ name: string;
+ symbol: string;
+ decimals: number;
+ ipfsHash: string;
+ swarmHash: string;
}
export interface TokenInfoByNetwork {
- development: Token[];
- live: Token[];
+ development: Token[];
+ live: Token[];
}
diff --git a/packages/deployer/src/utils/constants.ts b/packages/deployer/src/utils/constants.ts
index 9f22114e7..8871a470d 100644
--- a/packages/deployer/src/utils/constants.ts
+++ b/packages/deployer/src/utils/constants.ts
@@ -1,3 +1,3 @@
export const constants = {
- NULL_BYTES: '0x',
+ NULL_BYTES: '0x',
};
diff --git a/packages/deployer/src/utils/contract.ts b/packages/deployer/src/utils/contract.ts
index 473ca95c8..546e82dfb 100644
--- a/packages/deployer/src/utils/contract.ts
+++ b/packages/deployer/src/utils/contract.ts
@@ -6,76 +6,76 @@ import * as Web3 from 'web3';
import { AbiType } from './types';
export class Contract implements Web3.ContractInstance {
- public address: string;
- public abi: Web3.ContractAbi;
- private _contract: Web3.ContractInstance;
- private _defaults: Partial<Web3.TxData>;
- private _validator: SchemaValidator;
- // This class instance is going to be populated with functions and events depending on the ABI
- // and we don't know their types in advance
- [name: string]: any;
- constructor(web3ContractInstance: Web3.ContractInstance, defaults: Partial<Web3.TxData>) {
- this._contract = web3ContractInstance;
- this.address = web3ContractInstance.address;
- this.abi = web3ContractInstance.abi;
- this._defaults = defaults;
- this._populateEvents();
- this._populateFunctions();
- this._validator = new SchemaValidator();
- }
- private _populateFunctions(): void {
- const functionsAbi = _.filter(this.abi, abiPart => abiPart.type === AbiType.Function) as Web3.FunctionAbi[];
- _.forEach(functionsAbi, (functionAbi: Web3.MethodAbi) => {
- if (functionAbi.constant) {
- const cbStyleCallFunction = this._contract[functionAbi.name].call;
- this[functionAbi.name] = {
- callAsync: promisify(cbStyleCallFunction, this._contract),
- };
- } else {
- const cbStyleFunction = this._contract[functionAbi.name];
- const cbStyleEstimateGasFunction = this._contract[functionAbi.name].estimateGas;
- this[functionAbi.name] = {
- estimateGasAsync: promisify(cbStyleEstimateGasFunction, this._contract),
- sendTransactionAsync: this._promisifyWithDefaultParams(cbStyleFunction),
- };
- }
- });
- }
- private _populateEvents(): void {
- const eventsAbi = _.filter(this.abi, abiPart => abiPart.type === AbiType.Event) as Web3.EventAbi[];
- _.forEach(eventsAbi, (eventAbi: Web3.EventAbi) => {
- this[eventAbi.name] = this._contract[eventAbi.name];
- });
- }
- private _promisifyWithDefaultParams(fn: (...args: any[]) => void): (...args: any[]) => Promise<any> {
- const promisifiedWithDefaultParams = async (...args: any[]) => {
- const promise = new Promise((resolve, reject) => {
- const lastArg = args[args.length - 1];
- let txData: Partial<Web3.TxData> = {};
- if (this._isTxData(lastArg)) {
- txData = args.pop();
- }
- txData = {
- ...this._defaults,
- ...txData,
- };
- const callback = (err: Error, data: any) => {
- if (_.isNull(err)) {
- resolve(data);
- } else {
- reject(err);
- }
- };
- args.push(txData);
- args.push(callback);
- fn.apply(this._contract, args);
- });
- return promise;
- };
- return promisifiedWithDefaultParams;
- }
- private _isTxData(lastArg: any): boolean {
- const isValid = this._validator.isValid(lastArg, schemas.txDataSchema);
- return isValid;
- }
+ public address: string;
+ public abi: Web3.ContractAbi;
+ private _contract: Web3.ContractInstance;
+ private _defaults: Partial<Web3.TxData>;
+ private _validator: SchemaValidator;
+ // This class instance is going to be populated with functions and events depending on the ABI
+ // and we don't know their types in advance
+ [name: string]: any;
+ constructor(web3ContractInstance: Web3.ContractInstance, defaults: Partial<Web3.TxData>) {
+ this._contract = web3ContractInstance;
+ this.address = web3ContractInstance.address;
+ this.abi = web3ContractInstance.abi;
+ this._defaults = defaults;
+ this._populateEvents();
+ this._populateFunctions();
+ this._validator = new SchemaValidator();
+ }
+ private _populateFunctions(): void {
+ const functionsAbi = _.filter(this.abi, abiPart => abiPart.type === AbiType.Function) as Web3.FunctionAbi[];
+ _.forEach(functionsAbi, (functionAbi: Web3.MethodAbi) => {
+ if (functionAbi.constant) {
+ const cbStyleCallFunction = this._contract[functionAbi.name].call;
+ this[functionAbi.name] = {
+ callAsync: promisify(cbStyleCallFunction, this._contract),
+ };
+ } else {
+ const cbStyleFunction = this._contract[functionAbi.name];
+ const cbStyleEstimateGasFunction = this._contract[functionAbi.name].estimateGas;
+ this[functionAbi.name] = {
+ estimateGasAsync: promisify(cbStyleEstimateGasFunction, this._contract),
+ sendTransactionAsync: this._promisifyWithDefaultParams(cbStyleFunction),
+ };
+ }
+ });
+ }
+ private _populateEvents(): void {
+ const eventsAbi = _.filter(this.abi, abiPart => abiPart.type === AbiType.Event) as Web3.EventAbi[];
+ _.forEach(eventsAbi, (eventAbi: Web3.EventAbi) => {
+ this[eventAbi.name] = this._contract[eventAbi.name];
+ });
+ }
+ private _promisifyWithDefaultParams(fn: (...args: any[]) => void): (...args: any[]) => Promise<any> {
+ const promisifiedWithDefaultParams = async (...args: any[]) => {
+ const promise = new Promise((resolve, reject) => {
+ const lastArg = args[args.length - 1];
+ let txData: Partial<Web3.TxData> = {};
+ if (this._isTxData(lastArg)) {
+ txData = args.pop();
+ }
+ txData = {
+ ...this._defaults,
+ ...txData,
+ };
+ const callback = (err: Error, data: any) => {
+ if (_.isNull(err)) {
+ resolve(data);
+ } else {
+ reject(err);
+ }
+ };
+ args.push(txData);
+ args.push(callback);
+ fn.apply(this._contract, args);
+ });
+ return promise;
+ };
+ return promisifiedWithDefaultParams;
+ }
+ private _isTxData(lastArg: any): boolean {
+ const isValid = this._validator.isValid(lastArg, schemas.txDataSchema);
+ return isValid;
+ }
}
diff --git a/packages/deployer/src/utils/encoder.ts b/packages/deployer/src/utils/encoder.ts
index 30772b4bc..d5f807774 100644
--- a/packages/deployer/src/utils/encoder.ts
+++ b/packages/deployer/src/utils/encoder.ts
@@ -5,16 +5,16 @@ import * as web3Abi from 'web3-eth-abi';
import { AbiType } from './types';
export const encoder = {
- encodeConstructorArgsFromAbi(args: any[], abi: Web3.ContractAbi): string {
- const constructorTypes: string[] = [];
- _.each(abi, (element: Web3.AbiDefinition) => {
- if (element.type === AbiType.Constructor) {
- _.each(element.inputs, (input: Web3.FunctionParameter) => {
- constructorTypes.push(input.type);
- });
- }
- });
- const encodedParameters = web3Abi.encodeParameters(constructorTypes, args);
- return encodedParameters;
- },
+ encodeConstructorArgsFromAbi(args: any[], abi: Web3.ContractAbi): string {
+ const constructorTypes: string[] = [];
+ _.each(abi, (element: Web3.AbiDefinition) => {
+ if (element.type === AbiType.Constructor) {
+ _.each(element.inputs, (input: Web3.FunctionParameter) => {
+ constructorTypes.push(input.type);
+ });
+ }
+ });
+ const encodedParameters = web3Abi.encodeParameters(constructorTypes, args);
+ return encodedParameters;
+ },
};
diff --git a/packages/deployer/src/utils/fs_wrapper.ts b/packages/deployer/src/utils/fs_wrapper.ts
index 42bdc9969..34c7caa0e 100644
--- a/packages/deployer/src/utils/fs_wrapper.ts
+++ b/packages/deployer/src/utils/fs_wrapper.ts
@@ -2,10 +2,10 @@ import { promisify } from '@0xproject/utils';
import * as fs from 'fs';
export const fsWrapper = {
- readdirAsync: promisify<string[]>(fs.readdir),
- readFileAsync: promisify<string>(fs.readFile),
- writeFileAsync: promisify<undefined>(fs.writeFile),
- mkdirAsync: promisify<undefined>(fs.mkdir),
- doesPathExistSync: fs.existsSync,
- removeFileAsync: promisify<undefined>(fs.unlink),
+ readdirAsync: promisify<string[]>(fs.readdir),
+ readFileAsync: promisify<string>(fs.readFile),
+ writeFileAsync: promisify<undefined>(fs.writeFile),
+ mkdirAsync: promisify<undefined>(fs.mkdir),
+ doesPathExistSync: fs.existsSync,
+ removeFileAsync: promisify<undefined>(fs.unlink),
};
diff --git a/packages/deployer/src/utils/types.ts b/packages/deployer/src/utils/types.ts
index 9e1378e24..e054b9cc2 100644
--- a/packages/deployer/src/utils/types.ts
+++ b/packages/deployer/src/utils/types.ts
@@ -3,95 +3,95 @@ import * as Web3 from 'web3';
import * as yargs from 'yargs';
export enum AbiType {
- Function = 'function',
- Constructor = 'constructor',
- Event = 'event',
- Fallback = 'fallback',
+ Function = 'function',
+ Constructor = 'constructor',
+ Event = 'event',
+ Fallback = 'fallback',
}
export interface ContractArtifact {
- contract_name: string;
- networks: ContractNetworks;
+ contract_name: string;
+ networks: ContractNetworks;
}
export interface ContractNetworks {
- [key: number]: ContractData;
+ [key: number]: ContractData;
}
export interface ContractData {
- solc_version: string;
- optimizer_enabled: number;
- keccak256: string;
- abi: Web3.ContractAbi;
- unlinked_binary: string;
- address?: string;
- constructor_args?: string;
- updated_at: number;
+ solc_version: string;
+ optimizer_enabled: number;
+ keccak256: string;
+ abi: Web3.ContractAbi;
+ unlinked_binary: string;
+ address?: string;
+ constructor_args?: string;
+ updated_at: number;
}
export interface SolcErrors {
- [key: string]: boolean;
+ [key: string]: boolean;
}
export interface CliOptions extends yargs.Arguments {
- artifactsDir: string;
- contractsDir: string;
- jsonrpcPort: number;
- networkId: number;
- shouldOptimize: boolean;
- gasPrice: string;
- account?: string;
- contract?: string;
- args?: string;
+ artifactsDir: string;
+ contractsDir: string;
+ jsonrpcPort: number;
+ networkId: number;
+ shouldOptimize: boolean;
+ gasPrice: string;
+ account?: string;
+ contract?: string;
+ args?: string;
}
export interface CompilerOptions {
- contractsDir: string;
- networkId: number;
- optimizerEnabled: number;
- artifactsDir: string;
+ contractsDir: string;
+ networkId: number;
+ optimizerEnabled: number;
+ artifactsDir: string;
}
export interface DeployerOptions {
- artifactsDir: string;
- jsonrpcPort: number;
- networkId: number;
- defaults: Partial<TxData>;
+ artifactsDir: string;
+ jsonrpcPort: number;
+ networkId: number;
+ defaults: Partial<TxData>;
}
export interface ContractSources {
- [key: string]: string;
+ [key: string]: string;
}
export interface ImportContents {
- contents: string;
+ contents: string;
}
// TODO: Consolidate with 0x.js definitions once types are moved into a separate package.
export enum ZeroExError {
- ContractDoesNotExist = 'CONTRACT_DOES_NOT_EXIST',
- ExchangeContractDoesNotExist = 'EXCHANGE_CONTRACT_DOES_NOT_EXIST',
- UnhandledError = 'UNHANDLED_ERROR',
- UserHasNoAssociatedAddress = 'USER_HAS_NO_ASSOCIATED_ADDRESSES',
- InvalidSignature = 'INVALID_SIGNATURE',
- ContractNotDeployedOnNetwork = 'CONTRACT_NOT_DEPLOYED_ON_NETWORK',
- InsufficientAllowanceForTransfer = 'INSUFFICIENT_ALLOWANCE_FOR_TRANSFER',
- InsufficientBalanceForTransfer = 'INSUFFICIENT_BALANCE_FOR_TRANSFER',
- InsufficientEthBalanceForDeposit = 'INSUFFICIENT_ETH_BALANCE_FOR_DEPOSIT',
- InsufficientWEthBalanceForWithdrawal = 'INSUFFICIENT_WETH_BALANCE_FOR_WITHDRAWAL',
- InvalidJump = 'INVALID_JUMP',
- OutOfGas = 'OUT_OF_GAS',
- NoNetworkId = 'NO_NETWORK_ID',
- SubscriptionNotFound = 'SUBSCRIPTION_NOT_FOUND',
+ ContractDoesNotExist = 'CONTRACT_DOES_NOT_EXIST',
+ ExchangeContractDoesNotExist = 'EXCHANGE_CONTRACT_DOES_NOT_EXIST',
+ UnhandledError = 'UNHANDLED_ERROR',
+ UserHasNoAssociatedAddress = 'USER_HAS_NO_ASSOCIATED_ADDRESSES',
+ InvalidSignature = 'INVALID_SIGNATURE',
+ ContractNotDeployedOnNetwork = 'CONTRACT_NOT_DEPLOYED_ON_NETWORK',
+ InsufficientAllowanceForTransfer = 'INSUFFICIENT_ALLOWANCE_FOR_TRANSFER',
+ InsufficientBalanceForTransfer = 'INSUFFICIENT_BALANCE_FOR_TRANSFER',
+ InsufficientEthBalanceForDeposit = 'INSUFFICIENT_ETH_BALANCE_FOR_DEPOSIT',
+ InsufficientWEthBalanceForWithdrawal = 'INSUFFICIENT_WETH_BALANCE_FOR_WITHDRAWAL',
+ InvalidJump = 'INVALID_JUMP',
+ OutOfGas = 'OUT_OF_GAS',
+ NoNetworkId = 'NO_NETWORK_ID',
+ SubscriptionNotFound = 'SUBSCRIPTION_NOT_FOUND',
}
export interface Token {
- address?: string;
- name: string;
- symbol: string;
- decimals: number;
- ipfsHash: string;
- swarmHash: string;
+ address?: string;
+ name: string;
+ symbol: string;
+ decimals: number;
+ ipfsHash: string;
+ swarmHash: string;
}
export type DoneCallback = (err?: Error) => void;
diff --git a/packages/deployer/src/utils/utils.ts b/packages/deployer/src/utils/utils.ts
index 931ad6f73..4390d8813 100644
--- a/packages/deployer/src/utils/utils.ts
+++ b/packages/deployer/src/utils/utils.ts
@@ -1,13 +1,13 @@
export const utils = {
- consoleLog(message: string): void {
- /* tslint:disable */
- console.log(message);
- /* tslint:enable */
- },
- stringifyWithFormatting(obj: any): string {
- const jsonReplacer: null = null;
- const numberOfJsonSpaces = 4;
- const stringifiedObj = JSON.stringify(obj, jsonReplacer, numberOfJsonSpaces);
- return stringifiedObj;
- },
+ consoleLog(message: string): void {
+ /* tslint:disable */
+ console.log(message);
+ /* tslint:enable */
+ },
+ stringifyWithFormatting(obj: any): string {
+ const jsonReplacer: null = null;
+ const numberOfJsonSpaces = 4;
+ const stringifiedObj = JSON.stringify(obj, jsonReplacer, numberOfJsonSpaces);
+ return stringifiedObj;
+ },
};
diff --git a/packages/deployer/test/deploy_test.ts b/packages/deployer/test/deploy_test.ts
index 583601154..5df729a04 100644
--- a/packages/deployer/test/deploy_test.ts
+++ b/packages/deployer/test/deploy_test.ts
@@ -13,79 +13,79 @@ const artifactsDir = `${__dirname}/fixtures/artifacts`;
const contractsDir = `${__dirname}/fixtures/contracts`;
const exchangeArtifactPath = `${artifactsDir}/Exchange.json`;
const compilerOpts: CompilerOptions = {
- artifactsDir,
- contractsDir,
- networkId: constants.networkId,
- optimizerEnabled: constants.optimizerEnabled,
+ artifactsDir,
+ contractsDir,
+ networkId: constants.networkId,
+ optimizerEnabled: constants.optimizerEnabled,
};
const compiler = new Compiler(compilerOpts);
const deployerOpts = {
- artifactsDir,
- networkId: constants.networkId,
- jsonrpcPort: constants.jsonrpcPort,
- defaults: {
- gasPrice: constants.gasPrice,
- },
+ artifactsDir,
+ networkId: constants.networkId,
+ jsonrpcPort: constants.jsonrpcPort,
+ defaults: {
+ gasPrice: constants.gasPrice,
+ },
};
const deployer = new Deployer(deployerOpts);
/* tslint:disable */
beforeEach(function(done: DoneCallback) {
- this.timeout(constants.timeoutMs);
- (async () => {
- if (fsWrapper.doesPathExistSync(exchangeArtifactPath)) {
- await fsWrapper.removeFileAsync(exchangeArtifactPath);
- }
- await compiler.compileAllAsync();
- done();
- })().catch(done);
+ this.timeout(constants.timeoutMs);
+ (async () => {
+ if (fsWrapper.doesPathExistSync(exchangeArtifactPath)) {
+ await fsWrapper.removeFileAsync(exchangeArtifactPath);
+ }
+ await compiler.compileAllAsync();
+ done();
+ })().catch(done);
});
/* tslint:enable */
describe('#Compiler', () => {
- it('should create an Exchange artifact with the correct unlinked binary', async () => {
- const opts = {
- encoding: 'utf8',
- };
- const exchangeArtifactString = await fsWrapper.readFileAsync(exchangeArtifactPath, opts);
- const exchangeArtifact: ContractArtifact = JSON.parse(exchangeArtifactString);
- const exchangeContractData: ContractData = exchangeArtifact.networks[constants.networkId];
- // The last 43 bytes of the binaries are metadata which may not be equivalent
- const unlinkedBinaryWithoutMetadata = exchangeContractData.unlinked_binary.slice(0, -86);
- const exchangeBinaryWithoutMetadata = exchange_binary.slice(0, -86);
- expect(unlinkedBinaryWithoutMetadata).to.equal(exchangeBinaryWithoutMetadata);
- });
+ it('should create an Exchange artifact with the correct unlinked binary', async () => {
+ const opts = {
+ encoding: 'utf8',
+ };
+ const exchangeArtifactString = await fsWrapper.readFileAsync(exchangeArtifactPath, opts);
+ const exchangeArtifact: ContractArtifact = JSON.parse(exchangeArtifactString);
+ const exchangeContractData: ContractData = exchangeArtifact.networks[constants.networkId];
+ // The last 43 bytes of the binaries are metadata which may not be equivalent
+ const unlinkedBinaryWithoutMetadata = exchangeContractData.unlinked_binary.slice(0, -86);
+ const exchangeBinaryWithoutMetadata = exchange_binary.slice(0, -86);
+ expect(unlinkedBinaryWithoutMetadata).to.equal(exchangeBinaryWithoutMetadata);
+ });
});
describe('#Deployer', () => {
- describe('#deployAsync', () => {
- it('should deploy the Exchange contract without updating the Exchange artifact', async () => {
- const exchangeConstructorArgs = [constants.zrxTokenAddress, constants.tokenTransferProxyAddress];
- const exchangeContractInstance = await deployer.deployAsync('Exchange', exchangeConstructorArgs);
- const opts = {
- encoding: 'utf8',
- };
- const exchangeArtifactString = await fsWrapper.readFileAsync(exchangeArtifactPath, opts);
- const exchangeArtifact: ContractArtifact = JSON.parse(exchangeArtifactString);
- const exchangeContractData: ContractData = exchangeArtifact.networks[constants.networkId];
- const exchangeAddress = exchangeContractInstance.address;
- expect(exchangeAddress).to.not.equal(undefined);
- expect(exchangeContractData.address).to.equal(undefined);
- expect(exchangeContractData.constructor_args).to.equal(undefined);
- });
- });
- describe('#deployAndSaveAsync', () => {
- it('should save the correct contract address and constructor arguments to the Exchange artifact', async () => {
- const exchangeConstructorArgs = [constants.zrxTokenAddress, constants.tokenTransferProxyAddress];
- const exchangeContractInstance = await deployer.deployAndSaveAsync('Exchange', exchangeConstructorArgs);
- const opts = {
- encoding: 'utf8',
- };
- const exchangeArtifactString = await fsWrapper.readFileAsync(exchangeArtifactPath, opts);
- const exchangeArtifact: ContractArtifact = JSON.parse(exchangeArtifactString);
- const exchangeContractData: ContractData = exchangeArtifact.networks[constants.networkId];
- const exchangeAddress = exchangeContractInstance.address;
- expect(exchangeAddress).to.be.equal(exchangeContractData.address);
- expect(constructor_args).to.be.equal(exchangeContractData.constructor_args);
- });
- });
+ describe('#deployAsync', () => {
+ it('should deploy the Exchange contract without updating the Exchange artifact', async () => {
+ const exchangeConstructorArgs = [constants.zrxTokenAddress, constants.tokenTransferProxyAddress];
+ const exchangeContractInstance = await deployer.deployAsync('Exchange', exchangeConstructorArgs);
+ const opts = {
+ encoding: 'utf8',
+ };
+ const exchangeArtifactString = await fsWrapper.readFileAsync(exchangeArtifactPath, opts);
+ const exchangeArtifact: ContractArtifact = JSON.parse(exchangeArtifactString);
+ const exchangeContractData: ContractData = exchangeArtifact.networks[constants.networkId];
+ const exchangeAddress = exchangeContractInstance.address;
+ expect(exchangeAddress).to.not.equal(undefined);
+ expect(exchangeContractData.address).to.equal(undefined);
+ expect(exchangeContractData.constructor_args).to.equal(undefined);
+ });
+ });
+ describe('#deployAndSaveAsync', () => {
+ it('should save the correct contract address and constructor arguments to the Exchange artifact', async () => {
+ const exchangeConstructorArgs = [constants.zrxTokenAddress, constants.tokenTransferProxyAddress];
+ const exchangeContractInstance = await deployer.deployAndSaveAsync('Exchange', exchangeConstructorArgs);
+ const opts = {
+ encoding: 'utf8',
+ };
+ const exchangeArtifactString = await fsWrapper.readFileAsync(exchangeArtifactPath, opts);
+ const exchangeArtifact: ContractArtifact = JSON.parse(exchangeArtifactString);
+ const exchangeContractData: ContractData = exchangeArtifact.networks[constants.networkId];
+ const exchangeAddress = exchangeContractInstance.address;
+ expect(exchangeAddress).to.be.equal(exchangeContractData.address);
+ expect(constructor_args).to.be.equal(exchangeContractData.constructor_args);
+ });
+ });
});
diff --git a/packages/deployer/test/fixtures/exchange_bin.ts b/packages/deployer/test/fixtures/exchange_bin.ts
index 9c58b5e02..a6eae515e 100644
--- a/packages/deployer/test/fixtures/exchange_bin.ts
+++ b/packages/deployer/test/fixtures/exchange_bin.ts
@@ -1,4 +1,4 @@
export const constructor_args =
- '0x000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f4980000000000000000000000008da0d80f5007ef1e431dd2127178d224e32c2ef4';
+ '0x000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f4980000000000000000000000008da0d80f5007ef1e431dd2127178d224e32c2ef4';
export const exchange_binary =
- '0x6060604052341561000f57600080fd5b604051604080612c4d833981016040528080519060200190919080519060200190919050505b816000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555080600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505b50505b612b84806100c96000396000f300606060405236156100fa576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806314df96ee146100ff578063288cdc911461014c5780632ac1262214610187578063363349be146101c2578063394c21e7146103bc5780633b30ba591461044b5780634f150787146104a0578063741bcc93146106b25780637e9abb50146107535780638163681e1461078e57806398024a8b14610812578063add1cbc51461085b578063b7b2c7d6146108b0578063baa0181d14610acd578063bc61394a14610c1f578063cfc4d0ec14610cdf578063f06bbf7514610d6d578063ffa1ad7414610d9e575b600080fd5b341561010a57600080fd5b6101326004808035906020019091908035906020019091908035906020019091905050610e2d565b604051808215151515815260200191505060405180910390f35b341561015757600080fd5b610171600480803560001916906020019091905050610e7c565b6040518082815260200191505060405180910390f35b341561019257600080fd5b6101ac600480803560001916906020019091905050610e94565b6040518082815260200191505060405180910390f35b34156101cd57600080fd5b6103a660048080359060200190820180359060200190808060200260200160405190810160405280939291908181526020016000905b8282101561024857848483905060a002016005806020026040519081016040528092919082600560200280828437820191505050505081526020019060010190610203565b5050505050919080359060200190820180359060200190808060200260200160405190810160405280939291908181526020016000905b828210156102c457848483905060c00201600680602002604051908101604052809291908260066020028082843782019150505050508152602001906001019061027f565b5050505050919080359060200190919080351515906020019091908035906020019082018035906020019080806020026020016040519081016040528093929190818152602001838360200280828437820191505050505050919080359060200190820180359060200190808060200260200160405190810160405280939291908181526020018383602002808284378201915050505050509190803590602001908201803590602001908080602002602001604051908101604052809392919081815260200183836020028082843782019150505050505091905050610eac565b6040518082815260200191505060405180910390f35b34156103c757600080fd5b6104356004808060a001906005806020026040519081016040528092919082600560200280828437820191505050505091908060c001906006806020026040519081016040528092919082600660200280828437820191505050505091908035906020019091905050611013565b6040518082815260200191505060405180910390f35b341561045657600080fd5b61045e6114fb565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34156104ab57600080fd5b6106b060048080359060200190820180359060200190808060200260200160405190810160405280939291908181526020016000905b8282101561052657848483905060a0020160058060200260405190810160405280929190826005602002808284378201915050505050815260200190600101906104e1565b5050505050919080359060200190820180359060200190808060200260200160405190810160405280939291908181526020016000905b828210156105a257848483905060c00201600680602002604051908101604052809291908260066020028082843782019150505050508152602001906001019061055d565b50505050509190803590602001908201803590602001908080602002602001604051908101604052809392919081815260200183836020028082843782019150505050505091908035906020019082018035906020019080806020026020016040519081016040528093929190818152602001838360200280828437820191505050505050919080359060200190820180359060200190808060200260200160405190810160405280939291908181526020018383602002808284378201915050505050509190803590602001908201803590602001908080602002602001604051908101604052809392919081815260200183836020028082843782019150505050505091905050611520565b005b34156106bd57600080fd5b6107516004808060a001906005806020026040519081016040528092919082600560200280828437820191505050505091908060c00190600680602002604051908101604052809291908260066020028082843782019150505050509190803590602001909190803560ff1690602001909190803560001916906020019091908035600019169060200190919050506115df565b005b341561075e57600080fd5b610778600480803560001916906020019091905050611605565b6040518082815260200191505060405180910390f35b341561079957600080fd5b6107f8600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919080356000191690602001909190803560ff16906020019091908035600019169060200190919080356000191690602001909190505061164f565b604051808215151515815260200191505060405180910390f35b341561081d57600080fd5b6108456004808035906020019091908035906020019091908035906020019091905050611757565b6040518082815260200191505060405180910390f35b341561086657600080fd5b61086e611776565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34156108bb57600080fd5b610acb60048080359060200190820180359060200190808060200260200160405190810160405280939291908181526020016000905b8282101561093657848483905060a0020160058060200260405190810160405280929190826005602002808284378201915050505050815260200190600101906108f1565b5050505050919080359060200190820180359060200190808060200260200160405190810160405280939291908181526020016000905b828210156109b257848483905060c00201600680602002604051908101604052809291908260066020028082843782019150505050508152602001906001019061096d565b50505050509190803590602001908201803590602001908080602002602001604051908101604052809392919081815260200183836020028082843782019150505050505091908035151590602001909190803590602001908201803590602001908080602002602001604051908101604052809392919081815260200183836020028082843782019150505050505091908035906020019082018035906020019080806020026020016040519081016040528093929190818152602001838360200280828437820191505050505050919080359060200190820180359060200190808060200260200160405190810160405280939291908181526020018383602002808284378201915050505050509190505061179c565b005b3415610ad857600080fd5b610c1d60048080359060200190820180359060200190808060200260200160405190810160405280939291908181526020016000905b82821015610b5357848483905060a002016005806020026040519081016040528092919082600560200280828437820191505050505081526020019060010190610b0e565b5050505050919080359060200190820180359060200190808060200260200160405190810160405280939291908181526020016000905b82821015610bcf57848483905060c002016006806020026040519081016040528092919082600660200280828437820191505050505081526020019060010190610b8a565b5050505050919080359060200190820180359060200190808060200260200160405190810160405280939291908181526020018383602002808284378201915050505050509190505061185e565b005b3415610c2a57600080fd5b610cc96004808060a001906005806020026040519081016040528092919082600560200280828437820191505050505091908060c001906006806020026040519081016040528092919082600660200280828437820191505050505091908035906020019091908035151590602001909190803560ff1690602001909190803560001916906020019091908035600019169060200190919050506118d3565b6040518082815260200191505060405180910390f35b3415610cea57600080fd5b610d4f6004808060a001906005806020026040519081016040528092919082600560200280828437820191505050505091908060c001906006806020026040519081016040528092919082600660200280828437820191505050505091905050612073565b60405180826000191660001916815260200191505060405180910390f35b3415610d7857600080fd5b610d8061231f565b604051808261ffff1661ffff16815260200191505060405180910390f35b3415610da957600080fd5b610db1612325565b6040518080602001828103825283818151815260200191508051906020019080838360005b83811015610df25780820151818401525b602081019050610dd6565b50505050905090810190601f168015610e1f5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b60008060008486850991506000821415610e4a5760009250610e73565b610e69610e5a83620f424061235e565b610e64888761235e565b612392565b90506103e8811192505b50509392505050565b60026020528060005260406000206000915090505481565b60036020528060005260406000206000915090505481565b6000806000809150600090505b895181101561100257896000815181101515610ed157fe5b906020019060200201516003600581101515610ee957fe5b602002015173ffffffffffffffffffffffffffffffffffffffff168a82815181101515610f1257fe5b906020019060200201516003600581101515610f2a57fe5b602002015173ffffffffffffffffffffffffffffffffffffffff16141515610f5157600080fd5b610fe582610fe08c84815181101515610f6657fe5b906020019060200201518c85815181101515610f7e57fe5b90602001906020020151610f928d886123ae565b8c8c88815181101515610fa157fe5b906020019060200201518c89815181101515610fb957fe5b906020019060200201518c8a815181101515610fd157fe5b906020019060200201516118d3565b6123c8565b915087821415610ff457611002565b5b8080600101915050610eb9565b8192505b5050979650505050505050565b600061101d612a8c565b6000806101606040519081016040528088600060058110151561103c57fe5b602002015173ffffffffffffffffffffffffffffffffffffffff16815260200188600160058110151561106b57fe5b602002015173ffffffffffffffffffffffffffffffffffffffff16815260200188600260058110151561109a57fe5b602002015173ffffffffffffffffffffffffffffffffffffffff1681526020018860036005811015156110c957fe5b602002015173ffffffffffffffffffffffffffffffffffffffff1681526020018860046005811015156110f857fe5b602002015173ffffffffffffffffffffffffffffffffffffffff16815260200187600060068110151561112757fe5b6020020151815260200187600160068110151561114057fe5b6020020151815260200187600260068110151561115957fe5b6020020151815260200187600360068110151561117257fe5b6020020151815260200187600460068110151561118b57fe5b6020020151815260200161119f8989612073565b6000191681525092503373ffffffffffffffffffffffffffffffffffffffff16836000015173ffffffffffffffffffffffffffffffffffffffff161415156111e657600080fd5b60008360a001511180156111fe575060008360c00151115b801561120a5750600085115b151561121557600080fd5b8261012001514210151561127257826101400151600019166000600381111561123a57fe5b60ff167f36d86c59e00bd73dc19ba3adfe068e4b64ac7e92be35546adeddf1b956a87e9060405160405180910390a3600093506114f1565b61128d8360c00151611288856101400151611605565b6123ae565b915061129985836123e7565b905060008114156112f35782610140015160001916600160038111156112bb57fe5b60ff167f36d86c59e00bd73dc19ba3adfe068e4b64ac7e92be35546adeddf1b956a87e9060405160405180910390a3600093506114f1565b61131d600360008561014001516000191660001916815260200190815260200160002054826123c8565b60036000856101400151600019166000191681526020019081526020016000208190555082604001518360600151604051808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166c010000000000000000000000000281526014018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166c0100000000000000000000000002815260140192505050604051809103902060001916836080015173ffffffffffffffffffffffffffffffffffffffff16846000015173ffffffffffffffffffffffffffffffffffffffff167f67d66f160bc93d925d05dae1794c90d2d6d6688b29b84ff069398a9b0458713186604001518760600151611455878a60c001518b60a00151611757565b878a6101400151604051808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200184815260200183815260200182600019166000191681526020019550505050505060405180910390a48093505b5050509392505050565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60008090505b86518110156115d5576115c7878281518110151561154057fe5b90602001906020020151878381518110151561155857fe5b90602001906020020151878481518110151561157057fe5b90602001906020020151878581518110151561158857fe5b9060200190602002015187868151811015156115a057fe5b9060200190602002015187878151811015156115b857fe5b906020019060200201516115df565b5b8080600101915050611526565b5b50505050505050565b836115f087878760008888886118d3565b1415156115fc57600080fd5b5b505050505050565b600061164760026000846000191660001916815260200190815260200160002054600360008560001916600019168152602001908152602001600020546123c8565b90505b919050565b600060018560405180807f19457468657265756d205369676e6564204d6573736167653a0a333200000000815250601c0182600019166000191681526020019150506040518091039020858585604051600081526020016040526000604051602001526040518085600019166000191681526020018460ff1660ff16815260200183600019166000191681526020018260001916600019168152602001945050505050602060405160208103908084039060008661646e5a03f1151561171457600080fd5b50506020604051035173ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff161490505b95945050505050565b600061176c611766858461235e565b84612392565b90505b9392505050565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60008090505b87518110156118535761184488828151811015156117bc57fe5b9060200190602002015188838151811015156117d457fe5b9060200190602002015188848151811015156117ec57fe5b9060200190602002015188888681518110151561180557fe5b90602001906020020151888781518110151561181d57fe5b90602001906020020151888881518110151561183557fe5b906020019060200201516118d3565b505b80806001019150506117a2565b5b5050505050505050565b60008090505b83518110156118cc576118bd848281518110151561187e57fe5b90602001906020020151848381518110151561189657fe5b9060200190602002015184848151811015156118ae57fe5b90602001906020020151611013565b505b8080600101915050611864565b5b50505050565b60006118dd612a8c565b600080600080610160604051908101604052808e60006005811015156118ff57fe5b602002015173ffffffffffffffffffffffffffffffffffffffff1681526020018e600160058110151561192e57fe5b602002015173ffffffffffffffffffffffffffffffffffffffff1681526020018e600260058110151561195d57fe5b602002015173ffffffffffffffffffffffffffffffffffffffff1681526020018e600360058110151561198c57fe5b602002015173ffffffffffffffffffffffffffffffffffffffff1681526020018e60046005811015156119bb57fe5b602002015173ffffffffffffffffffffffffffffffffffffffff1681526020018d60006006811015156119ea57fe5b602002015181526020018d6001600681101515611a0357fe5b602002015181526020018d6002600681101515611a1c57fe5b602002015181526020018d6003600681101515611a3557fe5b602002015181526020018d6004600681101515611a4e57fe5b60200201518152602001611a628f8f612073565b600019168152509450600073ffffffffffffffffffffffffffffffffffffffff16856020015173ffffffffffffffffffffffffffffffffffffffff161480611ad957503373ffffffffffffffffffffffffffffffffffffffff16856020015173ffffffffffffffffffffffffffffffffffffffff16145b1515611ae457600080fd5b60008560a00151118015611afc575060008560c00151115b8015611b08575060008b115b1515611b1357600080fd5b611b2985600001518661014001518b8b8b61164f565b1515611b3457600080fd5b84610120015142101515611b91578461014001516000191660006003811115611b5957fe5b60ff167f36d86c59e00bd73dc19ba3adfe068e4b64ac7e92be35546adeddf1b956a87e9060405160405180910390a360009550612063565b611bac8560c00151611ba7876101400151611605565b6123ae565b9350611bb88b856123e7565b95506000861415611c12578461014001516000191660016003811115611bda57fe5b60ff167f36d86c59e00bd73dc19ba3adfe068e4b64ac7e92be35546adeddf1b956a87e9060405160405180910390a360009550612063565b611c25868660c001518760a00151610e2d565b15611c79578461014001516000191660026003811115611c4157fe5b60ff167f36d86c59e00bd73dc19ba3adfe068e4b64ac7e92be35546adeddf1b956a87e9060405160405180910390a360009550612063565b89158015611c8e5750611c8c8587612401565b155b15611ce15784610140015160001916600380811115611ca957fe5b60ff167f36d86c59e00bd73dc19ba3adfe068e4b64ac7e92be35546adeddf1b956a87e9060405160405180910390a360009550612063565b611cf4868660c001518760a00151611757565b9250611d20600260008761014001516000191660001916815260200190815260200160002054876123c8565b600260008761014001516000191660001916815260200190815260200160002081905550611d58856040015186600001513386612751565b1515611d6357600080fd5b611d77856060015133876000015189612751565b1515611d8257600080fd5b600073ffffffffffffffffffffffffffffffffffffffff16856080015173ffffffffffffffffffffffffffffffffffffffff16141515611e815760008560e001511115611e1f57611ddc868660c001518760e00151611757565b9150611e136000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff168660000151876080015185612751565b1515611e1e57600080fd5b5b60008561010001511115611e8057611e41868660c00151876101000151611757565b9050611e746000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1633876080015184612751565b1515611e7f57600080fd5b5b5b84604001518560600151604051808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166c010000000000000000000000000281526014018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166c0100000000000000000000000002815260140192505050604051809103902060001916856080015173ffffffffffffffffffffffffffffffffffffffff16866000015173ffffffffffffffffffffffffffffffffffffffff167f0d0b9391970d9a25552f37d436d2aae2925e2bfe1b2a923754bada030c498cb33389604001518a60600151898d8a8a8f6101400151604051808973ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200186815260200185815260200184815260200183815260200182600019166000191681526020019850505050505050505060405180910390a48595505b5050505050979650505050505050565b60003083600060058110151561208557fe5b602002015184600160058110151561209957fe5b60200201518560026005811015156120ad57fe5b60200201518660036005811015156120c157fe5b60200201518760046005811015156120d557fe5b60200201518760006006811015156120e957fe5b60200201518860016006811015156120fd57fe5b602002015189600260068110151561211157fe5b60200201518a600360068110151561212557fe5b60200201518b600460068110151561213957fe5b60200201518c600560068110151561214d57fe5b6020020151604051808d73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166c010000000000000000000000000281526014018c73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166c010000000000000000000000000281526014018b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166c010000000000000000000000000281526014018a73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166c010000000000000000000000000281526014018973ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166c010000000000000000000000000281526014018873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166c010000000000000000000000000281526014018781526020018681526020018581526020018481526020018381526020018281526020019c50505050505050505050505050604051809103902090505b92915050565b61138781565b6040805190810160405280600581526020017f312e302e3000000000000000000000000000000000000000000000000000000081525081565b6000808284029050600084148061237f575082848281151561237c57fe5b04145b151561238757fe5b8091505b5092915050565b60008082848115156123a057fe5b0490508091505b5092915050565b60008282111515156123bc57fe5b81830390505b92915050565b60008082840190508381101515156123dc57fe5b8091505b5092915050565b60008183106123f657816123f8565b825b90505b92915050565b60008060008060008060008060003397506124258a8c60c001518d60a00151611757565b9650600073ffffffffffffffffffffffffffffffffffffffff168b6080015173ffffffffffffffffffffffffffffffffffffffff161415156126d2576000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168b6040015173ffffffffffffffffffffffffffffffffffffffff161495506000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168b6060015173ffffffffffffffffffffffffffffffffffffffff161494506125208a8c60c001518d60e00151611757565b93506125368a8c60c001518d6101000151611757565b925085612543578361254e565b61254d87856123c8565b5b91508461255b5782612566565b6125658a846123c8565b5b9050816125986000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff168d600001516128ae565b10806125d15750816125cf6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff168d60000151612972565b105b806126055750806126036000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff168a6128ae565b105b806126395750806126376000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff168a612972565b105b156126475760009850612743565b851580156126805750866126638c604001518d600001516128ae565b108061267f57508661267d8c604001518d60000151612972565b105b5b1561268e5760009850612743565b841580156126bf5750896126a68c606001518a6128ae565b10806126be5750896126bc8c606001518a612972565b105b5b156126cd5760009850612743565b61273e565b866126e58c604001518d600001516128ae565b10806127015750866126ff8c604001518d60000151612972565b105b806127185750896127168c606001518a6128ae565b105b8061272f57508961272d8c606001518a612972565b105b1561273d5760009850612743565b5b600198505b505050505050505092915050565b6000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166315dacbea868686866000604051602001526040518563ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001828152602001945050505050602060405180830381600087803b151561288857600080fd5b6102c65a03f1151561289957600080fd5b5050506040518051905090505b949350505050565b60008273ffffffffffffffffffffffffffffffffffffffff166370a0823161138761ffff16846040518363ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001915050602060405180830381600088803b151561295157600080fd5b87f1151561295e57600080fd5b505050506040518051905090505b92915050565b60008273ffffffffffffffffffffffffffffffffffffffff1663dd62ed3e61138761ffff1684600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff166040518463ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200192505050602060405180830381600088803b1515612a6b57600080fd5b87f11515612a7857600080fd5b505050506040518051905090505b92915050565b61016060405190810160405280600073ffffffffffffffffffffffffffffffffffffffff168152602001600073ffffffffffffffffffffffffffffffffffffffff168152602001600073ffffffffffffffffffffffffffffffffffffffff168152602001600073ffffffffffffffffffffffffffffffffffffffff168152602001600073ffffffffffffffffffffffffffffffffffffffff16815260200160008152602001600081526020016000815260200160008152602001600081526020016000801916815250905600a165627a7a72305820df5cabdc3a116e993e10bfb14823d18d9b798038d4c463a1703f9a584c456b7e0029';
+ '0x6060604052341561000f57600080fd5b604051604080612c4d833981016040528080519060200190919080519060200190919050505b816000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555080600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505b50505b612b84806100c96000396000f300606060405236156100fa576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806314df96ee146100ff578063288cdc911461014c5780632ac1262214610187578063363349be146101c2578063394c21e7146103bc5780633b30ba591461044b5780634f150787146104a0578063741bcc93146106b25780637e9abb50146107535780638163681e1461078e57806398024a8b14610812578063add1cbc51461085b578063b7b2c7d6146108b0578063baa0181d14610acd578063bc61394a14610c1f578063cfc4d0ec14610cdf578063f06bbf7514610d6d578063ffa1ad7414610d9e575b600080fd5b341561010a57600080fd5b6101326004808035906020019091908035906020019091908035906020019091905050610e2d565b604051808215151515815260200191505060405180910390f35b341561015757600080fd5b610171600480803560001916906020019091905050610e7c565b6040518082815260200191505060405180910390f35b341561019257600080fd5b6101ac600480803560001916906020019091905050610e94565b6040518082815260200191505060405180910390f35b34156101cd57600080fd5b6103a660048080359060200190820180359060200190808060200260200160405190810160405280939291908181526020016000905b8282101561024857848483905060a002016005806020026040519081016040528092919082600560200280828437820191505050505081526020019060010190610203565b5050505050919080359060200190820180359060200190808060200260200160405190810160405280939291908181526020016000905b828210156102c457848483905060c00201600680602002604051908101604052809291908260066020028082843782019150505050508152602001906001019061027f565b5050505050919080359060200190919080351515906020019091908035906020019082018035906020019080806020026020016040519081016040528093929190818152602001838360200280828437820191505050505050919080359060200190820180359060200190808060200260200160405190810160405280939291908181526020018383602002808284378201915050505050509190803590602001908201803590602001908080602002602001604051908101604052809392919081815260200183836020028082843782019150505050505091905050610eac565b6040518082815260200191505060405180910390f35b34156103c757600080fd5b6104356004808060a001906005806020026040519081016040528092919082600560200280828437820191505050505091908060c001906006806020026040519081016040528092919082600660200280828437820191505050505091908035906020019091905050611013565b6040518082815260200191505060405180910390f35b341561045657600080fd5b61045e6114fb565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34156104ab57600080fd5b6106b060048080359060200190820180359060200190808060200260200160405190810160405280939291908181526020016000905b8282101561052657848483905060a0020160058060200260405190810160405280929190826005602002808284378201915050505050815260200190600101906104e1565b5050505050919080359060200190820180359060200190808060200260200160405190810160405280939291908181526020016000905b828210156105a257848483905060c00201600680602002604051908101604052809291908260066020028082843782019150505050508152602001906001019061055d565b50505050509190803590602001908201803590602001908080602002602001604051908101604052809392919081815260200183836020028082843782019150505050505091908035906020019082018035906020019080806020026020016040519081016040528093929190818152602001838360200280828437820191505050505050919080359060200190820180359060200190808060200260200160405190810160405280939291908181526020018383602002808284378201915050505050509190803590602001908201803590602001908080602002602001604051908101604052809392919081815260200183836020028082843782019150505050505091905050611520565b005b34156106bd57600080fd5b6107516004808060a001906005806020026040519081016040528092919082600560200280828437820191505050505091908060c00190600680602002604051908101604052809291908260066020028082843782019150505050509190803590602001909190803560ff1690602001909190803560001916906020019091908035600019169060200190919050506115df565b005b341561075e57600080fd5b610778600480803560001916906020019091905050611605565b6040518082815260200191505060405180910390f35b341561079957600080fd5b6107f8600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919080356000191690602001909190803560ff16906020019091908035600019169060200190919080356000191690602001909190505061164f565b604051808215151515815260200191505060405180910390f35b341561081d57600080fd5b6108456004808035906020019091908035906020019091908035906020019091905050611757565b6040518082815260200191505060405180910390f35b341561086657600080fd5b61086e611776565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34156108bb57600080fd5b610acb60048080359060200190820180359060200190808060200260200160405190810160405280939291908181526020016000905b8282101561093657848483905060a0020160058060200260405190810160405280929190826005602002808284378201915050505050815260200190600101906108f1565b5050505050919080359060200190820180359060200190808060200260200160405190810160405280939291908181526020016000905b828210156109b257848483905060c00201600680602002604051908101604052809291908260066020028082843782019150505050508152602001906001019061096d565b50505050509190803590602001908201803590602001908080602002602001604051908101604052809392919081815260200183836020028082843782019150505050505091908035151590602001909190803590602001908201803590602001908080602002602001604051908101604052809392919081815260200183836020028082843782019150505050505091908035906020019082018035906020019080806020026020016040519081016040528093929190818152602001838360200280828437820191505050505050919080359060200190820180359060200190808060200260200160405190810160405280939291908181526020018383602002808284378201915050505050509190505061179c565b005b3415610ad857600080fd5b610c1d60048080359060200190820180359060200190808060200260200160405190810160405280939291908181526020016000905b82821015610b5357848483905060a002016005806020026040519081016040528092919082600560200280828437820191505050505081526020019060010190610b0e565b5050505050919080359060200190820180359060200190808060200260200160405190810160405280939291908181526020016000905b82821015610bcf57848483905060c002016006806020026040519081016040528092919082600660200280828437820191505050505081526020019060010190610b8a565b5050505050919080359060200190820180359060200190808060200260200160405190810160405280939291908181526020018383602002808284378201915050505050509190505061185e565b005b3415610c2a57600080fd5b610cc96004808060a001906005806020026040519081016040528092919082600560200280828437820191505050505091908060c001906006806020026040519081016040528092919082600660200280828437820191505050505091908035906020019091908035151590602001909190803560ff1690602001909190803560001916906020019091908035600019169060200190919050506118d3565b6040518082815260200191505060405180910390f35b3415610cea57600080fd5b610d4f6004808060a001906005806020026040519081016040528092919082600560200280828437820191505050505091908060c001906006806020026040519081016040528092919082600660200280828437820191505050505091905050612073565b60405180826000191660001916815260200191505060405180910390f35b3415610d7857600080fd5b610d8061231f565b604051808261ffff1661ffff16815260200191505060405180910390f35b3415610da957600080fd5b610db1612325565b6040518080602001828103825283818151815260200191508051906020019080838360005b83811015610df25780820151818401525b602081019050610dd6565b50505050905090810190601f168015610e1f5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b60008060008486850991506000821415610e4a5760009250610e73565b610e69610e5a83620f424061235e565b610e64888761235e565b612392565b90506103e8811192505b50509392505050565b60026020528060005260406000206000915090505481565b60036020528060005260406000206000915090505481565b6000806000809150600090505b895181101561100257896000815181101515610ed157fe5b906020019060200201516003600581101515610ee957fe5b602002015173ffffffffffffffffffffffffffffffffffffffff168a82815181101515610f1257fe5b906020019060200201516003600581101515610f2a57fe5b602002015173ffffffffffffffffffffffffffffffffffffffff16141515610f5157600080fd5b610fe582610fe08c84815181101515610f6657fe5b906020019060200201518c85815181101515610f7e57fe5b90602001906020020151610f928d886123ae565b8c8c88815181101515610fa157fe5b906020019060200201518c89815181101515610fb957fe5b906020019060200201518c8a815181101515610fd157fe5b906020019060200201516118d3565b6123c8565b915087821415610ff457611002565b5b8080600101915050610eb9565b8192505b5050979650505050505050565b600061101d612a8c565b6000806101606040519081016040528088600060058110151561103c57fe5b602002015173ffffffffffffffffffffffffffffffffffffffff16815260200188600160058110151561106b57fe5b602002015173ffffffffffffffffffffffffffffffffffffffff16815260200188600260058110151561109a57fe5b602002015173ffffffffffffffffffffffffffffffffffffffff1681526020018860036005811015156110c957fe5b602002015173ffffffffffffffffffffffffffffffffffffffff1681526020018860046005811015156110f857fe5b602002015173ffffffffffffffffffffffffffffffffffffffff16815260200187600060068110151561112757fe5b6020020151815260200187600160068110151561114057fe5b6020020151815260200187600260068110151561115957fe5b6020020151815260200187600360068110151561117257fe5b6020020151815260200187600460068110151561118b57fe5b6020020151815260200161119f8989612073565b6000191681525092503373ffffffffffffffffffffffffffffffffffffffff16836000015173ffffffffffffffffffffffffffffffffffffffff161415156111e657600080fd5b60008360a001511180156111fe575060008360c00151115b801561120a5750600085115b151561121557600080fd5b8261012001514210151561127257826101400151600019166000600381111561123a57fe5b60ff167f36d86c59e00bd73dc19ba3adfe068e4b64ac7e92be35546adeddf1b956a87e9060405160405180910390a3600093506114f1565b61128d8360c00151611288856101400151611605565b6123ae565b915061129985836123e7565b905060008114156112f35782610140015160001916600160038111156112bb57fe5b60ff167f36d86c59e00bd73dc19ba3adfe068e4b64ac7e92be35546adeddf1b956a87e9060405160405180910390a3600093506114f1565b61131d600360008561014001516000191660001916815260200190815260200160002054826123c8565b60036000856101400151600019166000191681526020019081526020016000208190555082604001518360600151604051808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166c010000000000000000000000000281526014018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166c0100000000000000000000000002815260140192505050604051809103902060001916836080015173ffffffffffffffffffffffffffffffffffffffff16846000015173ffffffffffffffffffffffffffffffffffffffff167f67d66f160bc93d925d05dae1794c90d2d6d6688b29b84ff069398a9b0458713186604001518760600151611455878a60c001518b60a00151611757565b878a6101400151604051808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200184815260200183815260200182600019166000191681526020019550505050505060405180910390a48093505b5050509392505050565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60008090505b86518110156115d5576115c7878281518110151561154057fe5b90602001906020020151878381518110151561155857fe5b90602001906020020151878481518110151561157057fe5b90602001906020020151878581518110151561158857fe5b9060200190602002015187868151811015156115a057fe5b9060200190602002015187878151811015156115b857fe5b906020019060200201516115df565b5b8080600101915050611526565b5b50505050505050565b836115f087878760008888886118d3565b1415156115fc57600080fd5b5b505050505050565b600061164760026000846000191660001916815260200190815260200160002054600360008560001916600019168152602001908152602001600020546123c8565b90505b919050565b600060018560405180807f19457468657265756d205369676e6564204d6573736167653a0a333200000000815250601c0182600019166000191681526020019150506040518091039020858585604051600081526020016040526000604051602001526040518085600019166000191681526020018460ff1660ff16815260200183600019166000191681526020018260001916600019168152602001945050505050602060405160208103908084039060008661646e5a03f1151561171457600080fd5b50506020604051035173ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff161490505b95945050505050565b600061176c611766858461235e565b84612392565b90505b9392505050565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60008090505b87518110156118535761184488828151811015156117bc57fe5b9060200190602002015188838151811015156117d457fe5b9060200190602002015188848151811015156117ec57fe5b9060200190602002015188888681518110151561180557fe5b90602001906020020151888781518110151561181d57fe5b90602001906020020151888881518110151561183557fe5b906020019060200201516118d3565b505b80806001019150506117a2565b5b5050505050505050565b60008090505b83518110156118cc576118bd848281518110151561187e57fe5b90602001906020020151848381518110151561189657fe5b9060200190602002015184848151811015156118ae57fe5b90602001906020020151611013565b505b8080600101915050611864565b5b50505050565b60006118dd612a8c565b600080600080610160604051908101604052808e60006005811015156118ff57fe5b602002015173ffffffffffffffffffffffffffffffffffffffff1681526020018e600160058110151561192e57fe5b602002015173ffffffffffffffffffffffffffffffffffffffff1681526020018e600260058110151561195d57fe5b602002015173ffffffffffffffffffffffffffffffffffffffff1681526020018e600360058110151561198c57fe5b602002015173ffffffffffffffffffffffffffffffffffffffff1681526020018e60046005811015156119bb57fe5b602002015173ffffffffffffffffffffffffffffffffffffffff1681526020018d60006006811015156119ea57fe5b602002015181526020018d6001600681101515611a0357fe5b602002015181526020018d6002600681101515611a1c57fe5b602002015181526020018d6003600681101515611a3557fe5b602002015181526020018d6004600681101515611a4e57fe5b60200201518152602001611a628f8f612073565b600019168152509450600073ffffffffffffffffffffffffffffffffffffffff16856020015173ffffffffffffffffffffffffffffffffffffffff161480611ad957503373ffffffffffffffffffffffffffffffffffffffff16856020015173ffffffffffffffffffffffffffffffffffffffff16145b1515611ae457600080fd5b60008560a00151118015611afc575060008560c00151115b8015611b08575060008b115b1515611b1357600080fd5b611b2985600001518661014001518b8b8b61164f565b1515611b3457600080fd5b84610120015142101515611b91578461014001516000191660006003811115611b5957fe5b60ff167f36d86c59e00bd73dc19ba3adfe068e4b64ac7e92be35546adeddf1b956a87e9060405160405180910390a360009550612063565b611bac8560c00151611ba7876101400151611605565b6123ae565b9350611bb88b856123e7565b95506000861415611c12578461014001516000191660016003811115611bda57fe5b60ff167f36d86c59e00bd73dc19ba3adfe068e4b64ac7e92be35546adeddf1b956a87e9060405160405180910390a360009550612063565b611c25868660c001518760a00151610e2d565b15611c79578461014001516000191660026003811115611c4157fe5b60ff167f36d86c59e00bd73dc19ba3adfe068e4b64ac7e92be35546adeddf1b956a87e9060405160405180910390a360009550612063565b89158015611c8e5750611c8c8587612401565b155b15611ce15784610140015160001916600380811115611ca957fe5b60ff167f36d86c59e00bd73dc19ba3adfe068e4b64ac7e92be35546adeddf1b956a87e9060405160405180910390a360009550612063565b611cf4868660c001518760a00151611757565b9250611d20600260008761014001516000191660001916815260200190815260200160002054876123c8565b600260008761014001516000191660001916815260200190815260200160002081905550611d58856040015186600001513386612751565b1515611d6357600080fd5b611d77856060015133876000015189612751565b1515611d8257600080fd5b600073ffffffffffffffffffffffffffffffffffffffff16856080015173ffffffffffffffffffffffffffffffffffffffff16141515611e815760008560e001511115611e1f57611ddc868660c001518760e00151611757565b9150611e136000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff168660000151876080015185612751565b1515611e1e57600080fd5b5b60008561010001511115611e8057611e41868660c00151876101000151611757565b9050611e746000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1633876080015184612751565b1515611e7f57600080fd5b5b5b84604001518560600151604051808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166c010000000000000000000000000281526014018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166c0100000000000000000000000002815260140192505050604051809103902060001916856080015173ffffffffffffffffffffffffffffffffffffffff16866000015173ffffffffffffffffffffffffffffffffffffffff167f0d0b9391970d9a25552f37d436d2aae2925e2bfe1b2a923754bada030c498cb33389604001518a60600151898d8a8a8f6101400151604051808973ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200186815260200185815260200184815260200183815260200182600019166000191681526020019850505050505050505060405180910390a48595505b5050505050979650505050505050565b60003083600060058110151561208557fe5b602002015184600160058110151561209957fe5b60200201518560026005811015156120ad57fe5b60200201518660036005811015156120c157fe5b60200201518760046005811015156120d557fe5b60200201518760006006811015156120e957fe5b60200201518860016006811015156120fd57fe5b602002015189600260068110151561211157fe5b60200201518a600360068110151561212557fe5b60200201518b600460068110151561213957fe5b60200201518c600560068110151561214d57fe5b6020020151604051808d73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166c010000000000000000000000000281526014018c73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166c010000000000000000000000000281526014018b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166c010000000000000000000000000281526014018a73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166c010000000000000000000000000281526014018973ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166c010000000000000000000000000281526014018873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166c010000000000000000000000000281526014018781526020018681526020018581526020018481526020018381526020018281526020019c50505050505050505050505050604051809103902090505b92915050565b61138781565b6040805190810160405280600581526020017f312e302e3000000000000000000000000000000000000000000000000000000081525081565b6000808284029050600084148061237f575082848281151561237c57fe5b04145b151561238757fe5b8091505b5092915050565b60008082848115156123a057fe5b0490508091505b5092915050565b60008282111515156123bc57fe5b81830390505b92915050565b60008082840190508381101515156123dc57fe5b8091505b5092915050565b60008183106123f657816123f8565b825b90505b92915050565b60008060008060008060008060003397506124258a8c60c001518d60a00151611757565b9650600073ffffffffffffffffffffffffffffffffffffffff168b6080015173ffffffffffffffffffffffffffffffffffffffff161415156126d2576000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168b6040015173ffffffffffffffffffffffffffffffffffffffff161495506000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168b6060015173ffffffffffffffffffffffffffffffffffffffff161494506125208a8c60c001518d60e00151611757565b93506125368a8c60c001518d6101000151611757565b925085612543578361254e565b61254d87856123c8565b5b91508461255b5782612566565b6125658a846123c8565b5b9050816125986000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff168d600001516128ae565b10806125d15750816125cf6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff168d60000151612972565b105b806126055750806126036000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff168a6128ae565b105b806126395750806126376000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff168a612972565b105b156126475760009850612743565b851580156126805750866126638c604001518d600001516128ae565b108061267f57508661267d8c604001518d60000151612972565b105b5b1561268e5760009850612743565b841580156126bf5750896126a68c606001518a6128ae565b10806126be5750896126bc8c606001518a612972565b105b5b156126cd5760009850612743565b61273e565b866126e58c604001518d600001516128ae565b10806127015750866126ff8c604001518d60000151612972565b105b806127185750896127168c606001518a6128ae565b105b8061272f57508961272d8c606001518a612972565b105b1561273d5760009850612743565b5b600198505b505050505050505092915050565b6000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166315dacbea868686866000604051602001526040518563ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001828152602001945050505050602060405180830381600087803b151561288857600080fd5b6102c65a03f1151561289957600080fd5b5050506040518051905090505b949350505050565b60008273ffffffffffffffffffffffffffffffffffffffff166370a0823161138761ffff16846040518363ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001915050602060405180830381600088803b151561295157600080fd5b87f1151561295e57600080fd5b505050506040518051905090505b92915050565b60008273ffffffffffffffffffffffffffffffffffffffff1663dd62ed3e61138761ffff1684600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff166040518463ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200192505050602060405180830381600088803b1515612a6b57600080fd5b87f11515612a7857600080fd5b505050506040518051905090505b92915050565b61016060405190810160405280600073ffffffffffffffffffffffffffffffffffffffff168152602001600073ffffffffffffffffffffffffffffffffffffffff168152602001600073ffffffffffffffffffffffffffffffffffffffff168152602001600073ffffffffffffffffffffffffffffffffffffffff168152602001600073ffffffffffffffffffffffffffffffffffffffff16815260200160008152602001600081526020016000815260200160008152602001600081526020016000801916815250905600a165627a7a72305820df5cabdc3a116e993e10bfb14823d18d9b798038d4c463a1703f9a584c456b7e0029';
diff --git a/packages/deployer/test/util/constants.ts b/packages/deployer/test/util/constants.ts
index d69ce5385..d52076607 100644
--- a/packages/deployer/test/util/constants.ts
+++ b/packages/deployer/test/util/constants.ts
@@ -1,11 +1,11 @@
import { BigNumber } from '@0xproject/utils';
export const constants = {
- networkId: 0,
- jsonrpcPort: 8545,
- optimizerEnabled: 0,
- gasPrice: new BigNumber(20000000000),
- timeoutMs: 20000,
- zrxTokenAddress: '0xe41d2489571d322189246dafa5ebde1f4699f498',
- tokenTransferProxyAddress: '0x8da0d80f5007ef1e431dd2127178d224e32c2ef4',
+ networkId: 0,
+ jsonrpcPort: 8545,
+ optimizerEnabled: 0,
+ gasPrice: new BigNumber(20000000000),
+ timeoutMs: 20000,
+ zrxTokenAddress: '0xe41d2489571d322189246dafa5ebde1f4699f498',
+ tokenTransferProxyAddress: '0x8da0d80f5007ef1e431dd2127178d224e32c2ef4',
};
diff --git a/packages/deployer/tsconfig.json b/packages/deployer/tsconfig.json
index f11c9245e..4e1edb510 100644
--- a/packages/deployer/tsconfig.json
+++ b/packages/deployer/tsconfig.json
@@ -1,16 +1,16 @@
{
- "extends": "../../tsconfig",
- "compilerOptions": {
- "outDir": "lib",
- "strictFunctionTypes": false,
- "strictNullChecks": false
- },
- "include": [
- "./src/**/*",
- "./test/**/*",
- "../../node_modules/types-bn/index.d.ts",
- "../../node_modules/types-ethereumjs-util/index.d.ts",
- "../../node_modules/chai-typescript-typings/index.d.ts",
- "../../node_modules/web3-typescript-typings/index.d.ts"
- ]
+ "extends": "../../tsconfig",
+ "compilerOptions": {
+ "outDir": "lib",
+ "strictFunctionTypes": false,
+ "strictNullChecks": false
+ },
+ "include": [
+ "./src/**/*",
+ "./test/**/*",
+ "../../node_modules/types-bn/index.d.ts",
+ "../../node_modules/types-ethereumjs-util/index.d.ts",
+ "../../node_modules/chai-typescript-typings/index.d.ts",
+ "../../node_modules/web3-typescript-typings/index.d.ts"
+ ]
}
diff --git a/packages/dev-utils/package.json b/packages/dev-utils/package.json
index d7c764285..ea940fb1d 100644
--- a/packages/dev-utils/package.json
+++ b/packages/dev-utils/package.json
@@ -1,37 +1,37 @@
{
- "name": "@0xproject/dev-utils",
- "version": "0.0.7",
- "description": "0x dev TS utils",
- "main": "lib/index.js",
- "types": "lib/index.d.ts",
- "scripts": {
- "build": "tsc",
- "clean": "shx rm -rf lib",
- "lint": "tslint --project . 'src/**/*.ts'"
- },
- "license": "Apache-2.0",
- "repository": {
- "type": "git",
- "url": "https://github.com/0xProject/0x.js.git"
- },
- "bugs": {
- "url": "https://github.com/0xProject/0x.js/issues"
- },
- "homepage": "https://github.com/0xProject/0x.js/packages/dev-utils/README.md",
- "devDependencies": {
- "@0xproject/tslint-config": "^0.4.4",
- "@types/lodash": "^4.14.86",
- "npm-run-all": "^4.1.2",
- "shx": "^0.2.2",
- "tslint": "5.8.0",
- "types-bn": "^0.0.1",
- "types-ethereumjs-util": "0xProject/types-ethereumjs-util",
- "typescript": "~2.6.1"
- },
- "dependencies": {
- "@0xproject/utils": "^0.2.2",
- "ethereumjs-util": "^5.1.2",
- "lodash": "^4.17.4",
- "request-promise-native": "^1.0.5"
- }
+ "name": "@0xproject/dev-utils",
+ "version": "0.0.7",
+ "description": "0x dev TS utils",
+ "main": "lib/index.js",
+ "types": "lib/index.d.ts",
+ "scripts": {
+ "build": "tsc",
+ "clean": "shx rm -rf lib",
+ "lint": "tslint --project . 'src/**/*.ts'"
+ },
+ "license": "Apache-2.0",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/0xProject/0x.js.git"
+ },
+ "bugs": {
+ "url": "https://github.com/0xProject/0x.js/issues"
+ },
+ "homepage": "https://github.com/0xProject/0x.js/packages/dev-utils/README.md",
+ "devDependencies": {
+ "@0xproject/tslint-config": "^0.4.4",
+ "@types/lodash": "^4.14.86",
+ "npm-run-all": "^4.1.2",
+ "shx": "^0.2.2",
+ "tslint": "5.8.0",
+ "types-bn": "^0.0.1",
+ "types-ethereumjs-util": "0xProject/types-ethereumjs-util",
+ "typescript": "~2.6.1"
+ },
+ "dependencies": {
+ "@0xproject/utils": "^0.2.2",
+ "ethereumjs-util": "^5.1.2",
+ "lodash": "^4.17.4",
+ "request-promise-native": "^1.0.5"
+ }
}
diff --git a/packages/dev-utils/src/blockchain_lifecycle.ts b/packages/dev-utils/src/blockchain_lifecycle.ts
index fafa62c84..18f5d5c61 100644
--- a/packages/dev-utils/src/blockchain_lifecycle.ts
+++ b/packages/dev-utils/src/blockchain_lifecycle.ts
@@ -1,23 +1,23 @@
import { RPC } from './rpc';
export class BlockchainLifecycle {
- private _rpc: RPC;
- private _snapshotIdsStack: number[];
- constructor(url: string) {
- this._rpc = new RPC(url);
- this._snapshotIdsStack = [];
- }
- // TODO: In order to run these tests on an actual node, we should check if we are running against
- // TestRPC, if so, use snapshots, otherwise re-deploy contracts before every test
- public async startAsync(): Promise<void> {
- const snapshotId = await this._rpc.takeSnapshotAsync();
- this._snapshotIdsStack.push(snapshotId);
- }
- public async revertAsync(): Promise<void> {
- const snapshotId = this._snapshotIdsStack.pop() as number;
- const didRevert = await this._rpc.revertSnapshotAsync(snapshotId);
- if (!didRevert) {
- throw new Error(`Snapshot with id #${snapshotId} failed to revert`);
- }
- }
+ private _rpc: RPC;
+ private _snapshotIdsStack: number[];
+ constructor(url: string) {
+ this._rpc = new RPC(url);
+ this._snapshotIdsStack = [];
+ }
+ // TODO: In order to run these tests on an actual node, we should check if we are running against
+ // TestRPC, if so, use snapshots, otherwise re-deploy contracts before every test
+ public async startAsync(): Promise<void> {
+ const snapshotId = await this._rpc.takeSnapshotAsync();
+ this._snapshotIdsStack.push(snapshotId);
+ }
+ public async revertAsync(): Promise<void> {
+ const snapshotId = this._snapshotIdsStack.pop() as number;
+ const didRevert = await this._rpc.revertSnapshotAsync(snapshotId);
+ if (!didRevert) {
+ throw new Error(`Snapshot with id #${snapshotId} failed to revert`);
+ }
+ }
}
diff --git a/packages/dev-utils/src/rpc.ts b/packages/dev-utils/src/rpc.ts
index 6241ab971..36f8b1ef9 100644
--- a/packages/dev-utils/src/rpc.ts
+++ b/packages/dev-utils/src/rpc.ts
@@ -2,59 +2,59 @@ import * as ethUtil from 'ethereumjs-util';
import * as request from 'request-promise-native';
export class RPC {
- private _url: string;
- private _id: number;
- constructor(url: string) {
- this._url = url;
- this._id = 0;
- }
- public async takeSnapshotAsync(): Promise<number> {
- const method = 'evm_snapshot';
- const params: any[] = [];
- const payload = this._toPayload(method, params);
- const snapshotIdHex = await this._sendAsync(payload);
- const snapshotId = ethUtil.bufferToInt(ethUtil.toBuffer(snapshotIdHex));
- return snapshotId;
- }
- public async revertSnapshotAsync(snapshotId: number): Promise<boolean> {
- const method = 'evm_revert';
- const params = [snapshotId];
- const payload = this._toPayload(method, params);
- const didRevert = await this._sendAsync(payload);
- return didRevert;
- }
- public async increaseTimeAsync(time: number) {
- const method = 'evm_increaseTime';
- const params = [time];
- const payload = this._toPayload(method, params);
- return this._sendAsync(payload);
- }
- public async mineBlockAsync(): Promise<void> {
- const method = 'evm_mine';
- const params: any[] = [];
- const payload = this._toPayload(method, params);
- await this._sendAsync(payload);
- }
- private _toPayload(method: string, params: any[] = []): string {
- const payload = JSON.stringify({
- id: this._id,
- method,
- params,
- });
- this._id += 1;
- return payload;
- }
- private async _sendAsync(payload: string): Promise<any> {
- const opts = {
- method: 'POST',
- uri: this._url,
- body: payload,
- headers: {
- 'content-type': 'application/json',
- },
- };
- const bodyString = await request(opts);
- const body = JSON.parse(bodyString);
- return body.result;
- }
+ private _url: string;
+ private _id: number;
+ constructor(url: string) {
+ this._url = url;
+ this._id = 0;
+ }
+ public async takeSnapshotAsync(): Promise<number> {
+ const method = 'evm_snapshot';
+ const params: any[] = [];
+ const payload = this._toPayload(method, params);
+ const snapshotIdHex = await this._sendAsync(payload);
+ const snapshotId = ethUtil.bufferToInt(ethUtil.toBuffer(snapshotIdHex));
+ return snapshotId;
+ }
+ public async revertSnapshotAsync(snapshotId: number): Promise<boolean> {
+ const method = 'evm_revert';
+ const params = [snapshotId];
+ const payload = this._toPayload(method, params);
+ const didRevert = await this._sendAsync(payload);
+ return didRevert;
+ }
+ public async increaseTimeAsync(time: number) {
+ const method = 'evm_increaseTime';
+ const params = [time];
+ const payload = this._toPayload(method, params);
+ return this._sendAsync(payload);
+ }
+ public async mineBlockAsync(): Promise<void> {
+ const method = 'evm_mine';
+ const params: any[] = [];
+ const payload = this._toPayload(method, params);
+ await this._sendAsync(payload);
+ }
+ private _toPayload(method: string, params: any[] = []): string {
+ const payload = JSON.stringify({
+ id: this._id,
+ method,
+ params,
+ });
+ this._id += 1;
+ return payload;
+ }
+ private async _sendAsync(payload: string): Promise<any> {
+ const opts = {
+ method: 'POST',
+ uri: this._url,
+ body: payload,
+ headers: {
+ 'content-type': 'application/json',
+ },
+ };
+ const bodyString = await request(opts);
+ const body = JSON.parse(bodyString);
+ return body.result;
+ }
}
diff --git a/packages/dev-utils/tsconfig.json b/packages/dev-utils/tsconfig.json
index 83a0e2451..b28e45170 100644
--- a/packages/dev-utils/tsconfig.json
+++ b/packages/dev-utils/tsconfig.json
@@ -1,11 +1,11 @@
{
- "extends": "../../tsconfig",
- "compilerOptions": {
- "outDir": "lib"
- },
- "include": [
- "./src/**/*",
- "../../node_modules/types-bn/index.d.ts",
- "../../node_modules/types-ethereumjs-util/index.d.ts"
- ]
+ "extends": "../../tsconfig",
+ "compilerOptions": {
+ "outDir": "lib"
+ },
+ "include": [
+ "./src/**/*",
+ "../../node_modules/types-bn/index.d.ts",
+ "../../node_modules/types-ethereumjs-util/index.d.ts"
+ ]
}
diff --git a/packages/dev-utils/tslint.json b/packages/dev-utils/tslint.json
index e63054bfc..ffaefe83a 100644
--- a/packages/dev-utils/tslint.json
+++ b/packages/dev-utils/tslint.json
@@ -1,3 +1,3 @@
{
- "extends": ["@0xproject/tslint-config"]
+ "extends": ["@0xproject/tslint-config"]
}
diff --git a/packages/json-schemas/package.json b/packages/json-schemas/package.json
index 30cccb12f..d67cb64f7 100644
--- a/packages/json-schemas/package.json
+++ b/packages/json-schemas/package.json
@@ -1,45 +1,45 @@
{
- "name": "@0xproject/json-schemas",
- "version": "0.7.5",
- "description": "0x-related json schemas",
- "main": "lib/src/index.js",
- "types": "lib/src/index.d.ts",
- "scripts": {
- "lint": "tslint --project . 'src/**/*.ts' 'test/**/*.ts'",
- "test": "run-s clean build run_mocha",
- "test:circleci": "yarn test",
- "run_mocha": "mocha lib/test/**/*_test.js",
- "clean": "shx rm -rf _bundles lib test_temp",
- "build": "tsc"
- },
- "repository": {
- "type": "git",
- "url": "https://github.com/0xProject/0x.js.git"
- },
- "author": "",
- "license": "Apache-2.0",
- "bugs": {
- "url": "https://github.com/0xProject/0x.js/issues"
- },
- "homepage": "https://github.com/0xProject/0x.js/packages/json-schemas/README.md",
- "dependencies": {
- "jsonschema": "^1.2.0",
- "lodash.values": "^4.3.0"
- },
- "devDependencies": {
- "@0xproject/tslint-config": "^0.4.4",
- "@0xproject/utils": "^0.2.2",
- "@types/lodash.foreach": "^4.5.3",
- "@types/lodash.values": "^4.3.3",
- "@types/mocha": "^2.2.42",
- "chai": "^4.0.1",
- "chai-typescript-typings": "^0.0.2",
- "dirty-chai": "^2.0.1",
- "lodash.foreach": "^4.5.0",
- "mocha": "^4.0.1",
- "npm-run-all": "^4.1.2",
- "shx": "^0.2.2",
- "tslint": "5.8.0",
- "typescript": "~2.6.1"
- }
+ "name": "@0xproject/json-schemas",
+ "version": "0.7.5",
+ "description": "0x-related json schemas",
+ "main": "lib/src/index.js",
+ "types": "lib/src/index.d.ts",
+ "scripts": {
+ "lint": "tslint --project . 'src/**/*.ts' 'test/**/*.ts'",
+ "test": "run-s clean build run_mocha",
+ "test:circleci": "yarn test",
+ "run_mocha": "mocha lib/test/**/*_test.js",
+ "clean": "shx rm -rf _bundles lib test_temp",
+ "build": "tsc"
+ },
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/0xProject/0x.js.git"
+ },
+ "author": "",
+ "license": "Apache-2.0",
+ "bugs": {
+ "url": "https://github.com/0xProject/0x.js/issues"
+ },
+ "homepage": "https://github.com/0xProject/0x.js/packages/json-schemas/README.md",
+ "dependencies": {
+ "jsonschema": "^1.2.0",
+ "lodash.values": "^4.3.0"
+ },
+ "devDependencies": {
+ "@0xproject/tslint-config": "^0.4.4",
+ "@0xproject/utils": "^0.2.2",
+ "@types/lodash.foreach": "^4.5.3",
+ "@types/lodash.values": "^4.3.3",
+ "@types/mocha": "^2.2.42",
+ "chai": "^4.0.1",
+ "chai-typescript-typings": "^0.0.2",
+ "dirty-chai": "^2.0.1",
+ "lodash.foreach": "^4.5.0",
+ "mocha": "^4.0.1",
+ "npm-run-all": "^4.1.2",
+ "shx": "^0.2.2",
+ "tslint": "5.8.0",
+ "typescript": "~2.6.1"
+ }
}
diff --git a/packages/json-schemas/schemas/basic_type_schemas.ts b/packages/json-schemas/schemas/basic_type_schemas.ts
index aa2eea867..9d81ff333 100644
--- a/packages/json-schemas/schemas/basic_type_schemas.ts
+++ b/packages/json-schemas/schemas/basic_type_schemas.ts
@@ -1,11 +1,11 @@
export const addressSchema = {
- id: '/Address',
- type: 'string',
- pattern: '^0x[0-9a-f]{40}$',
+ id: '/Address',
+ type: 'string',
+ pattern: '^0x[0-9a-f]{40}$',
};
export const numberSchema = {
- id: '/Number',
- type: 'string',
- pattern: '^\\d+(\\.\\d+)?$',
+ id: '/Number',
+ type: 'string',
+ pattern: '^\\d+(\\.\\d+)?$',
};
diff --git a/packages/json-schemas/schemas/block_range_schema.ts b/packages/json-schemas/schemas/block_range_schema.ts
index 8697bba96..1f6a63151 100644
--- a/packages/json-schemas/schemas/block_range_schema.ts
+++ b/packages/json-schemas/schemas/block_range_schema.ts
@@ -1,20 +1,20 @@
export const blockParamSchema = {
- id: '/BlockParam',
- oneOf: [
- {
- type: 'number',
- },
- {
- enum: ['latest', 'earliest', 'pending'],
- },
- ],
+ id: '/BlockParam',
+ oneOf: [
+ {
+ type: 'number',
+ },
+ {
+ enum: ['latest', 'earliest', 'pending'],
+ },
+ ],
};
export const blockRangeSchema = {
- id: '/BlockRange',
- properties: {
- fromBlock: { $ref: '/BlockParam' },
- toBlock: { $ref: '/BlockParam' },
- },
- type: 'object',
+ id: '/BlockRange',
+ properties: {
+ fromBlock: { $ref: '/BlockParam' },
+ toBlock: { $ref: '/BlockParam' },
+ },
+ type: 'object',
};
diff --git a/packages/json-schemas/schemas/ec_signature_schema.ts b/packages/json-schemas/schemas/ec_signature_schema.ts
index 299ca283b..71b840dd8 100644
--- a/packages/json-schemas/schemas/ec_signature_schema.ts
+++ b/packages/json-schemas/schemas/ec_signature_schema.ts
@@ -1,20 +1,20 @@
export const ecSignatureParameterSchema = {
- id: '/ECSignatureParameter',
- type: 'string',
- pattern: '^0[xX][0-9A-Fa-f]{64}$',
+ id: '/ECSignatureParameter',
+ type: 'string',
+ pattern: '^0[xX][0-9A-Fa-f]{64}$',
};
export const ecSignatureSchema = {
- id: '/ECSignature',
- properties: {
- v: {
- type: 'number',
- minimum: 27,
- maximum: 28,
- },
- r: { $ref: '/ECSignatureParameter' },
- s: { $ref: '/ECSignatureParameter' },
- },
- required: ['v', 'r', 's'],
- type: 'object',
+ id: '/ECSignature',
+ properties: {
+ v: {
+ type: 'number',
+ minimum: 27,
+ maximum: 28,
+ },
+ r: { $ref: '/ECSignatureParameter' },
+ s: { $ref: '/ECSignatureParameter' },
+ },
+ required: ['v', 'r', 's'],
+ type: 'object',
};
diff --git a/packages/json-schemas/schemas/index_filter_values_schema.ts b/packages/json-schemas/schemas/index_filter_values_schema.ts
index 6e9f36c54..3374d63e0 100644
--- a/packages/json-schemas/schemas/index_filter_values_schema.ts
+++ b/packages/json-schemas/schemas/index_filter_values_schema.ts
@@ -1,7 +1,7 @@
export const indexFilterValuesSchema = {
- id: '/IndexFilterValues',
- additionalProperties: {
- oneOf: [{ $ref: '/Number' }, { $ref: '/Address' }, { $ref: '/OrderHashSchema' }],
- },
- type: 'object',
+ id: '/IndexFilterValues',
+ additionalProperties: {
+ oneOf: [{ $ref: '/Number' }, { $ref: '/Address' }, { $ref: '/OrderHashSchema' }],
+ },
+ type: 'object',
};
diff --git a/packages/json-schemas/schemas/order_cancel_schema.ts b/packages/json-schemas/schemas/order_cancel_schema.ts
index f2aa80e7a..ad23d01cc 100644
--- a/packages/json-schemas/schemas/order_cancel_schema.ts
+++ b/packages/json-schemas/schemas/order_cancel_schema.ts
@@ -1,12 +1,12 @@
export const orderCancellationRequestsSchema = {
- id: '/OrderCancellationRequests',
- type: 'array',
- items: {
- properties: {
- order: { $ref: '/Order' },
- takerTokenCancelAmount: { $ref: '/Number' },
- },
- required: ['order', 'takerTokenCancelAmount'],
- type: 'object',
- },
+ id: '/OrderCancellationRequests',
+ type: 'array',
+ items: {
+ properties: {
+ order: { $ref: '/Order' },
+ takerTokenCancelAmount: { $ref: '/Number' },
+ },
+ required: ['order', 'takerTokenCancelAmount'],
+ type: 'object',
+ },
};
diff --git a/packages/json-schemas/schemas/order_fill_or_kill_requests_schema.ts b/packages/json-schemas/schemas/order_fill_or_kill_requests_schema.ts
index f13bc9cc3..61f2c8849 100644
--- a/packages/json-schemas/schemas/order_fill_or_kill_requests_schema.ts
+++ b/packages/json-schemas/schemas/order_fill_or_kill_requests_schema.ts
@@ -1,12 +1,12 @@
export const orderFillOrKillRequestsSchema = {
- id: '/OrderFillOrKillRequests',
- type: 'array',
- items: {
- properties: {
- signedOrder: { $ref: '/SignedOrder' },
- fillTakerAmount: { $ref: '/Number' },
- },
- required: ['signedOrder', 'fillTakerAmount'],
- type: 'object',
- },
+ id: '/OrderFillOrKillRequests',
+ type: 'array',
+ items: {
+ properties: {
+ signedOrder: { $ref: '/SignedOrder' },
+ fillTakerAmount: { $ref: '/Number' },
+ },
+ required: ['signedOrder', 'fillTakerAmount'],
+ type: 'object',
+ },
};
diff --git a/packages/json-schemas/schemas/order_fill_requests_schema.ts b/packages/json-schemas/schemas/order_fill_requests_schema.ts
index 45cd927be..419d0670f 100644
--- a/packages/json-schemas/schemas/order_fill_requests_schema.ts
+++ b/packages/json-schemas/schemas/order_fill_requests_schema.ts
@@ -1,12 +1,12 @@
export const orderFillRequestsSchema = {
- id: '/OrderFillRequests',
- type: 'array',
- items: {
- properties: {
- signedOrder: { $ref: '/SignedOrder' },
- takerTokenFillAmount: { $ref: '/Number' },
- },
- required: ['signedOrder', 'takerTokenFillAmount'],
- type: 'object',
- },
+ id: '/OrderFillRequests',
+ type: 'array',
+ items: {
+ properties: {
+ signedOrder: { $ref: '/SignedOrder' },
+ takerTokenFillAmount: { $ref: '/Number' },
+ },
+ required: ['signedOrder', 'takerTokenFillAmount'],
+ type: 'object',
+ },
};
diff --git a/packages/json-schemas/schemas/order_hash_schema.ts b/packages/json-schemas/schemas/order_hash_schema.ts
index 5eed112ca..6af06927f 100644
--- a/packages/json-schemas/schemas/order_hash_schema.ts
+++ b/packages/json-schemas/schemas/order_hash_schema.ts
@@ -1,5 +1,5 @@
export const orderHashSchema = {
- id: '/OrderHashSchema',
- type: 'string',
- pattern: '^0x[0-9a-fA-F]{64}$',
+ id: '/OrderHashSchema',
+ type: 'string',
+ pattern: '^0x[0-9a-fA-F]{64}$',
};
diff --git a/packages/json-schemas/schemas/order_schemas.ts b/packages/json-schemas/schemas/order_schemas.ts
index ee5f2c137..6f17224ad 100644
--- a/packages/json-schemas/schemas/order_schemas.ts
+++ b/packages/json-schemas/schemas/order_schemas.ts
@@ -1,43 +1,43 @@
export const orderSchema = {
- id: '/Order',
- properties: {
- maker: { $ref: '/Address' },
- taker: { $ref: '/Address' },
- makerFee: { $ref: '/Number' },
- takerFee: { $ref: '/Number' },
- makerTokenAmount: { $ref: '/Number' },
- takerTokenAmount: { $ref: '/Number' },
- makerTokenAddress: { $ref: '/Address' },
- takerTokenAddress: { $ref: '/Address' },
- salt: { $ref: '/Number' },
- feeRecipient: { $ref: '/Address' },
- expirationUnixTimestampSec: { $ref: '/Number' },
- exchangeContractAddress: { $ref: '/Address' },
- },
- required: [
- 'maker',
- 'taker',
- 'makerFee',
- 'takerFee',
- 'makerTokenAmount',
- 'takerTokenAmount',
- 'salt',
- 'feeRecipient',
- 'expirationUnixTimestampSec',
- 'exchangeContractAddress',
- ],
- type: 'object',
+ id: '/Order',
+ properties: {
+ maker: { $ref: '/Address' },
+ taker: { $ref: '/Address' },
+ makerFee: { $ref: '/Number' },
+ takerFee: { $ref: '/Number' },
+ makerTokenAmount: { $ref: '/Number' },
+ takerTokenAmount: { $ref: '/Number' },
+ makerTokenAddress: { $ref: '/Address' },
+ takerTokenAddress: { $ref: '/Address' },
+ salt: { $ref: '/Number' },
+ feeRecipient: { $ref: '/Address' },
+ expirationUnixTimestampSec: { $ref: '/Number' },
+ exchangeContractAddress: { $ref: '/Address' },
+ },
+ required: [
+ 'maker',
+ 'taker',
+ 'makerFee',
+ 'takerFee',
+ 'makerTokenAmount',
+ 'takerTokenAmount',
+ 'salt',
+ 'feeRecipient',
+ 'expirationUnixTimestampSec',
+ 'exchangeContractAddress',
+ ],
+ type: 'object',
};
export const signedOrderSchema = {
- id: '/SignedOrder',
- allOf: [
- { $ref: '/Order' },
- {
- properties: {
- ecSignature: { $ref: '/ECSignature' },
- },
- required: ['ecSignature'],
- },
- ],
+ id: '/SignedOrder',
+ allOf: [
+ { $ref: '/Order' },
+ {
+ properties: {
+ ecSignature: { $ref: '/ECSignature' },
+ },
+ required: ['ecSignature'],
+ },
+ ],
};
diff --git a/packages/json-schemas/schemas/relayer_api_error_response_schema.ts b/packages/json-schemas/schemas/relayer_api_error_response_schema.ts
index 3d3a96364..27fdb166f 100644
--- a/packages/json-schemas/schemas/relayer_api_error_response_schema.ts
+++ b/packages/json-schemas/schemas/relayer_api_error_response_schema.ts
@@ -1,21 +1,21 @@
export const relayerApiErrorResponseSchema = {
- id: '/RelayerApiErrorResponse',
- type: 'object',
- properties: {
- code: { type: 'number' },
- reason: { type: 'string' },
- validationErrors: {
- type: 'array',
- items: {
- type: 'object',
- properties: {
- field: { type: 'string' },
- code: { type: 'number' },
- reason: { type: 'string' },
- },
- required: ['field', 'code', 'reason'],
- },
- },
- },
- required: ['code', 'reason'],
+ id: '/RelayerApiErrorResponse',
+ type: 'object',
+ properties: {
+ code: { type: 'number' },
+ reason: { type: 'string' },
+ validationErrors: {
+ type: 'array',
+ items: {
+ type: 'object',
+ properties: {
+ field: { type: 'string' },
+ code: { type: 'number' },
+ reason: { type: 'string' },
+ },
+ required: ['field', 'code', 'reason'],
+ },
+ },
+ },
+ required: ['code', 'reason'],
};
diff --git a/packages/json-schemas/schemas/relayer_api_fees_payload_schema.ts b/packages/json-schemas/schemas/relayer_api_fees_payload_schema.ts
index f79308594..eaaf777a1 100644
--- a/packages/json-schemas/schemas/relayer_api_fees_payload_schema.ts
+++ b/packages/json-schemas/schemas/relayer_api_fees_payload_schema.ts
@@ -1,24 +1,24 @@
export const relayerApiFeesPayloadSchema = {
- id: '/RelayerApiFeesPayload',
- type: 'object',
- properties: {
- exchangeContractAddress: { $ref: '/Address' },
- maker: { $ref: '/Address' },
- taker: { $ref: '/Address' },
- makerTokenAddress: { $ref: '/Address' },
- takerTokenAddress: { $ref: '/Address' },
- makerTokenAmount: { $ref: '/Number' },
- takerTokenAmount: { $ref: '/Number' },
- expirationUnixTimestampSec: { $ref: '/Number' },
- salt: { $ref: '/Number' },
- },
- required: [
- 'exchangeContractAddress',
- 'maker',
- 'taker',
- 'makerTokenAddress',
- 'takerTokenAddress',
- 'expirationUnixTimestampSec',
- 'salt',
- ],
+ id: '/RelayerApiFeesPayload',
+ type: 'object',
+ properties: {
+ exchangeContractAddress: { $ref: '/Address' },
+ maker: { $ref: '/Address' },
+ taker: { $ref: '/Address' },
+ makerTokenAddress: { $ref: '/Address' },
+ takerTokenAddress: { $ref: '/Address' },
+ makerTokenAmount: { $ref: '/Number' },
+ takerTokenAmount: { $ref: '/Number' },
+ expirationUnixTimestampSec: { $ref: '/Number' },
+ salt: { $ref: '/Number' },
+ },
+ required: [
+ 'exchangeContractAddress',
+ 'maker',
+ 'taker',
+ 'makerTokenAddress',
+ 'takerTokenAddress',
+ 'expirationUnixTimestampSec',
+ 'salt',
+ ],
};
diff --git a/packages/json-schemas/schemas/relayer_api_fees_response_schema.ts b/packages/json-schemas/schemas/relayer_api_fees_response_schema.ts
index d87c2fcdb..e7440613f 100644
--- a/packages/json-schemas/schemas/relayer_api_fees_response_schema.ts
+++ b/packages/json-schemas/schemas/relayer_api_fees_response_schema.ts
@@ -1,10 +1,10 @@
export const relayerApiFeesResponseSchema = {
- id: '/RelayerApiFeesResponse',
- type: 'object',
- properties: {
- makerFee: { $ref: '/Number' },
- takerFee: { $ref: '/Number' },
- feeRecipient: { $ref: '/Address' },
- },
- required: ['makerFee', 'takerFee', 'feeRecipient'],
+ id: '/RelayerApiFeesResponse',
+ type: 'object',
+ properties: {
+ makerFee: { $ref: '/Number' },
+ takerFee: { $ref: '/Number' },
+ feeRecipient: { $ref: '/Address' },
+ },
+ required: ['makerFee', 'takerFee', 'feeRecipient'],
};
diff --git a/packages/json-schemas/schemas/relayer_api_orberbook_channel_subscribe_schema.ts b/packages/json-schemas/schemas/relayer_api_orberbook_channel_subscribe_schema.ts
index 1d443c625..d93fa73d6 100644
--- a/packages/json-schemas/schemas/relayer_api_orberbook_channel_subscribe_schema.ts
+++ b/packages/json-schemas/schemas/relayer_api_orberbook_channel_subscribe_schema.ts
@@ -1,23 +1,23 @@
export const relayerApiOrderbookChannelSubscribeSchema = {
- id: '/RelayerApiOrderbookChannelSubscribe',
- type: 'object',
- properties: {
- type: { enum: ['subscribe'] },
- channel: { enum: ['orderbook'] },
- requestId: { type: 'number' },
- payload: { $ref: '/RelayerApiOrderbookChannelSubscribePayload' },
- },
- required: ['type', 'channel', 'requestId', 'payload'],
+ id: '/RelayerApiOrderbookChannelSubscribe',
+ type: 'object',
+ properties: {
+ type: { enum: ['subscribe'] },
+ channel: { enum: ['orderbook'] },
+ requestId: { type: 'number' },
+ payload: { $ref: '/RelayerApiOrderbookChannelSubscribePayload' },
+ },
+ required: ['type', 'channel', 'requestId', 'payload'],
};
export const relayerApiOrderbookChannelSubscribePayload = {
- id: '/RelayerApiOrderbookChannelSubscribePayload',
- type: 'object',
- properties: {
- baseTokenAddress: { $ref: '/Address' },
- quoteTokenAddress: { $ref: '/Address' },
- snapshot: { type: 'boolean' },
- limit: { type: 'number' },
- },
- required: ['baseTokenAddress', 'quoteTokenAddress'],
+ id: '/RelayerApiOrderbookChannelSubscribePayload',
+ type: 'object',
+ properties: {
+ baseTokenAddress: { $ref: '/Address' },
+ quoteTokenAddress: { $ref: '/Address' },
+ snapshot: { type: 'boolean' },
+ limit: { type: 'number' },
+ },
+ required: ['baseTokenAddress', 'quoteTokenAddress'],
};
diff --git a/packages/json-schemas/schemas/relayer_api_orderbook_channel_snapshot_schema.ts b/packages/json-schemas/schemas/relayer_api_orderbook_channel_snapshot_schema.ts
index e954185a8..fe1510d5b 100644
--- a/packages/json-schemas/schemas/relayer_api_orderbook_channel_snapshot_schema.ts
+++ b/packages/json-schemas/schemas/relayer_api_orderbook_channel_snapshot_schema.ts
@@ -1,21 +1,21 @@
export const relayerApiOrderbookChannelSnapshotSchema = {
- id: '/RelayerApiOrderbookChannelSnapshot',
- type: 'object',
- properties: {
- type: { enum: ['snapshot'] },
- channel: { enum: ['orderbook'] },
- requestId: { type: 'number' },
- payload: { $ref: '/RelayerApiOrderbookChannelSnapshotPayload' },
- },
- required: ['type', 'channel', 'requestId', 'payload'],
+ id: '/RelayerApiOrderbookChannelSnapshot',
+ type: 'object',
+ properties: {
+ type: { enum: ['snapshot'] },
+ channel: { enum: ['orderbook'] },
+ requestId: { type: 'number' },
+ payload: { $ref: '/RelayerApiOrderbookChannelSnapshotPayload' },
+ },
+ required: ['type', 'channel', 'requestId', 'payload'],
};
export const relayerApiOrderbookChannelSnapshotPayload = {
- id: '/RelayerApiOrderbookChannelSnapshotPayload',
- type: 'object',
- properties: {
- bids: { $ref: '/signedOrdersSchema' },
- asks: { $ref: '/signedOrdersSchema' },
- },
- required: ['bids', 'asks'],
+ id: '/RelayerApiOrderbookChannelSnapshotPayload',
+ type: 'object',
+ properties: {
+ bids: { $ref: '/signedOrdersSchema' },
+ asks: { $ref: '/signedOrdersSchema' },
+ },
+ required: ['bids', 'asks'],
};
diff --git a/packages/json-schemas/schemas/relayer_api_orderbook_channel_update_response_schema.ts b/packages/json-schemas/schemas/relayer_api_orderbook_channel_update_response_schema.ts
index 7c2ba6531..9a6d83d4c 100644
--- a/packages/json-schemas/schemas/relayer_api_orderbook_channel_update_response_schema.ts
+++ b/packages/json-schemas/schemas/relayer_api_orderbook_channel_update_response_schema.ts
@@ -1,11 +1,11 @@
export const relayerApiOrderbookChannelUpdateSchema = {
- id: '/RelayerApiOrderbookChannelUpdate',
- type: 'object',
- properties: {
- type: { enum: ['update'] },
- channel: { enum: ['orderbook'] },
- requestId: { type: 'number' },
- payload: { $ref: '/SignedOrder' },
- },
- required: ['type', 'channel', 'requestId', 'payload'],
+ id: '/RelayerApiOrderbookChannelUpdate',
+ type: 'object',
+ properties: {
+ type: { enum: ['update'] },
+ channel: { enum: ['orderbook'] },
+ requestId: { type: 'number' },
+ payload: { $ref: '/SignedOrder' },
+ },
+ required: ['type', 'channel', 'requestId', 'payload'],
};
diff --git a/packages/json-schemas/schemas/relayer_api_orderbook_response_schema.ts b/packages/json-schemas/schemas/relayer_api_orderbook_response_schema.ts
index d9abfc77d..5c409c807 100644
--- a/packages/json-schemas/schemas/relayer_api_orderbook_response_schema.ts
+++ b/packages/json-schemas/schemas/relayer_api_orderbook_response_schema.ts
@@ -1,9 +1,9 @@
export const relayerApiOrderBookResponseSchema = {
- id: '/RelayerApiOrderBookResponse',
- type: 'object',
- properties: {
- bids: { $ref: '/signedOrdersSchema' },
- asks: { $ref: '/signedOrdersSchema' },
- },
- required: ['bids', 'asks'],
+ id: '/RelayerApiOrderBookResponse',
+ type: 'object',
+ properties: {
+ bids: { $ref: '/signedOrdersSchema' },
+ asks: { $ref: '/signedOrdersSchema' },
+ },
+ required: ['bids', 'asks'],
};
diff --git a/packages/json-schemas/schemas/relayer_api_token_pairs_response_schema.ts b/packages/json-schemas/schemas/relayer_api_token_pairs_response_schema.ts
index 471bed7b4..5009c7955 100644
--- a/packages/json-schemas/schemas/relayer_api_token_pairs_response_schema.ts
+++ b/packages/json-schemas/schemas/relayer_api_token_pairs_response_schema.ts
@@ -1,24 +1,24 @@
export const relayerApiTokenPairsResponseSchema = {
- id: '/RelayerApiTokenPairsResponse',
- type: 'array',
- items: {
- properties: {
- tokenA: { $ref: '/RelayerApiTokenTradeInfo' },
- tokenB: { $ref: '/RelayerApiTokenTradeInfo' },
- },
- required: ['tokenA', 'tokenB'],
- type: 'object',
- },
+ id: '/RelayerApiTokenPairsResponse',
+ type: 'array',
+ items: {
+ properties: {
+ tokenA: { $ref: '/RelayerApiTokenTradeInfo' },
+ tokenB: { $ref: '/RelayerApiTokenTradeInfo' },
+ },
+ required: ['tokenA', 'tokenB'],
+ type: 'object',
+ },
};
export const relayerApiTokenTradeInfoSchema = {
- id: '/RelayerApiTokenTradeInfo',
- type: 'object',
- properties: {
- address: { $ref: '/Address' },
- minAmount: { $ref: '/Number' },
- maxAmount: { $ref: '/Number' },
- precision: { type: 'number' },
- },
- required: ['address'],
+ id: '/RelayerApiTokenTradeInfo',
+ type: 'object',
+ properties: {
+ address: { $ref: '/Address' },
+ minAmount: { $ref: '/Number' },
+ maxAmount: { $ref: '/Number' },
+ precision: { type: 'number' },
+ },
+ required: ['address'],
};
diff --git a/packages/json-schemas/schemas/signed_orders_schema.ts b/packages/json-schemas/schemas/signed_orders_schema.ts
index 7d66b7027..34d956836 100644
--- a/packages/json-schemas/schemas/signed_orders_schema.ts
+++ b/packages/json-schemas/schemas/signed_orders_schema.ts
@@ -1,5 +1,5 @@
export const signedOrdersSchema = {
- id: '/signedOrdersSchema',
- type: 'array',
- items: { $ref: '/SignedOrder' },
+ id: '/signedOrdersSchema',
+ type: 'array',
+ items: { $ref: '/SignedOrder' },
};
diff --git a/packages/json-schemas/schemas/token_schema.ts b/packages/json-schemas/schemas/token_schema.ts
index d2bb10f09..e64565c8b 100644
--- a/packages/json-schemas/schemas/token_schema.ts
+++ b/packages/json-schemas/schemas/token_schema.ts
@@ -1,11 +1,11 @@
export const tokenSchema = {
- id: '/Token',
- properties: {
- name: { type: 'string' },
- symbol: { type: 'string' },
- decimals: { type: 'number' },
- address: { $ref: '/Address' },
- },
- required: ['name', 'symbol', 'decimals', 'address'],
- type: 'object',
+ id: '/Token',
+ properties: {
+ name: { type: 'string' },
+ symbol: { type: 'string' },
+ decimals: { type: 'number' },
+ address: { $ref: '/Address' },
+ },
+ required: ['name', 'symbol', 'decimals', 'address'],
+ type: 'object',
};
diff --git a/packages/json-schemas/schemas/tx_data_schema.ts b/packages/json-schemas/schemas/tx_data_schema.ts
index 00c1d3e08..4274c553f 100644
--- a/packages/json-schemas/schemas/tx_data_schema.ts
+++ b/packages/json-schemas/schemas/tx_data_schema.ts
@@ -1,33 +1,33 @@
export const jsNumber = {
- id: '/JsNumber',
- type: 'number',
- minimum: 0,
+ id: '/JsNumber',
+ type: 'number',
+ minimum: 0,
};
export const txDataSchema = {
- id: '/TxData',
- properties: {
- from: { $ref: '/Address' },
- to: { $ref: '/Address' },
- value: {
- oneOf: [{ $ref: '/Number' }, { $ref: '/JsNumber' }],
- },
- gas: {
- oneOf: [{ $ref: '/Number' }, { $ref: '/JsNumber' }],
- },
- gasPrice: {
- oneOf: [{ $ref: '/Number' }, { $ref: '/JsNumber' }],
- },
- data: {
- type: 'string',
- pattern: '^0x[0-9a-f]*$',
- },
- nonce: {
- type: 'number',
- minimum: 0,
- },
- },
- required: ['from'],
- type: 'object',
- additionalProperties: false,
+ id: '/TxData',
+ properties: {
+ from: { $ref: '/Address' },
+ to: { $ref: '/Address' },
+ value: {
+ oneOf: [{ $ref: '/Number' }, { $ref: '/JsNumber' }],
+ },
+ gas: {
+ oneOf: [{ $ref: '/Number' }, { $ref: '/JsNumber' }],
+ },
+ gasPrice: {
+ oneOf: [{ $ref: '/Number' }, { $ref: '/JsNumber' }],
+ },
+ data: {
+ type: 'string',
+ pattern: '^0x[0-9a-f]*$',
+ },
+ nonce: {
+ type: 'number',
+ minimum: 0,
+ },
+ },
+ required: ['from'],
+ type: 'object',
+ additionalProperties: false,
};
diff --git a/packages/json-schemas/src/schema_validator.ts b/packages/json-schemas/src/schema_validator.ts
index 686c38d17..e13326d2a 100644
--- a/packages/json-schemas/src/schema_validator.ts
+++ b/packages/json-schemas/src/schema_validator.ts
@@ -4,26 +4,26 @@ import values = require('lodash.values');
import { schemas } from './schemas';
export class SchemaValidator {
- private _validator: Validator;
- constructor() {
- this._validator = new Validator();
- for (const schema of values(schemas)) {
- this._validator.addSchema(schema, schema.id);
- }
- }
- public addSchema(schema: Schema) {
- this._validator.addSchema(schema, schema.id);
- }
- // In order to validate a complex JS object using jsonschema, we must replace any complex
- // sub-types (e.g BigNumber) with a simpler string representation. Since BigNumber and other
- // complex types implement the `toString` method, we can stringify the object and
- // then parse it. The resultant object can then be checked using jsonschema.
- public validate(instance: any, schema: Schema): ValidatorResult {
- const jsonSchemaCompatibleObject = JSON.parse(JSON.stringify(instance));
- return this._validator.validate(jsonSchemaCompatibleObject, schema);
- }
- public isValid(instance: any, schema: Schema): boolean {
- const isValid = this.validate(instance, schema).errors.length === 0;
- return isValid;
- }
+ private _validator: Validator;
+ constructor() {
+ this._validator = new Validator();
+ for (const schema of values(schemas)) {
+ this._validator.addSchema(schema, schema.id);
+ }
+ }
+ public addSchema(schema: Schema) {
+ this._validator.addSchema(schema, schema.id);
+ }
+ // In order to validate a complex JS object using jsonschema, we must replace any complex
+ // sub-types (e.g BigNumber) with a simpler string representation. Since BigNumber and other
+ // complex types implement the `toString` method, we can stringify the object and
+ // then parse it. The resultant object can then be checked using jsonschema.
+ public validate(instance: any, schema: Schema): ValidatorResult {
+ const jsonSchemaCompatibleObject = JSON.parse(JSON.stringify(instance));
+ return this._validator.validate(jsonSchemaCompatibleObject, schema);
+ }
+ public isValid(instance: any, schema: Schema): boolean {
+ const isValid = this.validate(instance, schema).errors.length === 0;
+ return isValid;
+ }
}
diff --git a/packages/json-schemas/src/schemas.ts b/packages/json-schemas/src/schemas.ts
index bdc227aea..5cb07acfe 100644
--- a/packages/json-schemas/src/schemas.ts
+++ b/packages/json-schemas/src/schemas.ts
@@ -11,50 +11,50 @@ import { relayerApiErrorResponseSchema } from '../schemas/relayer_api_error_resp
import { relayerApiFeesPayloadSchema } from '../schemas/relayer_api_fees_payload_schema';
import { relayerApiFeesResponseSchema } from '../schemas/relayer_api_fees_response_schema';
import {
- relayerApiOrderbookChannelSubscribePayload,
- relayerApiOrderbookChannelSubscribeSchema,
+ relayerApiOrderbookChannelSubscribePayload,
+ relayerApiOrderbookChannelSubscribeSchema,
} from '../schemas/relayer_api_orberbook_channel_subscribe_schema';
import {
- relayerApiOrderbookChannelSnapshotPayload,
- relayerApiOrderbookChannelSnapshotSchema,
+ relayerApiOrderbookChannelSnapshotPayload,
+ relayerApiOrderbookChannelSnapshotSchema,
} from '../schemas/relayer_api_orderbook_channel_snapshot_schema';
import { relayerApiOrderbookChannelUpdateSchema } from '../schemas/relayer_api_orderbook_channel_update_response_schema';
import { relayerApiOrderBookResponseSchema } from '../schemas/relayer_api_orderbook_response_schema';
import {
- relayerApiTokenPairsResponseSchema,
- relayerApiTokenTradeInfoSchema,
+ relayerApiTokenPairsResponseSchema,
+ relayerApiTokenTradeInfoSchema,
} from '../schemas/relayer_api_token_pairs_response_schema';
import { signedOrdersSchema } from '../schemas/signed_orders_schema';
import { tokenSchema } from '../schemas/token_schema';
import { jsNumber, txDataSchema } from '../schemas/tx_data_schema';
export const schemas = {
- numberSchema,
- addressSchema,
- ecSignatureSchema,
- ecSignatureParameterSchema,
- indexFilterValuesSchema,
- orderCancellationRequestsSchema,
- orderFillOrKillRequestsSchema,
- orderFillRequestsSchema,
- orderHashSchema,
- orderSchema,
- signedOrderSchema,
- signedOrdersSchema,
- blockParamSchema,
- blockRangeSchema,
- tokenSchema,
- jsNumber,
- txDataSchema,
- relayerApiErrorResponseSchema,
- relayerApiFeesPayloadSchema,
- relayerApiFeesResponseSchema,
- relayerApiOrderBookResponseSchema,
- relayerApiTokenPairsResponseSchema,
- relayerApiTokenTradeInfoSchema,
- relayerApiOrderbookChannelSubscribeSchema,
- relayerApiOrderbookChannelSubscribePayload,
- relayerApiOrderbookChannelUpdateSchema,
- relayerApiOrderbookChannelSnapshotSchema,
- relayerApiOrderbookChannelSnapshotPayload,
+ numberSchema,
+ addressSchema,
+ ecSignatureSchema,
+ ecSignatureParameterSchema,
+ indexFilterValuesSchema,
+ orderCancellationRequestsSchema,
+ orderFillOrKillRequestsSchema,
+ orderFillRequestsSchema,
+ orderHashSchema,
+ orderSchema,
+ signedOrderSchema,
+ signedOrdersSchema,
+ blockParamSchema,
+ blockRangeSchema,
+ tokenSchema,
+ jsNumber,
+ txDataSchema,
+ relayerApiErrorResponseSchema,
+ relayerApiFeesPayloadSchema,
+ relayerApiFeesResponseSchema,
+ relayerApiOrderBookResponseSchema,
+ relayerApiTokenPairsResponseSchema,
+ relayerApiTokenTradeInfoSchema,
+ relayerApiOrderbookChannelSubscribeSchema,
+ relayerApiOrderbookChannelSubscribePayload,
+ relayerApiOrderbookChannelUpdateSchema,
+ relayerApiOrderbookChannelSnapshotSchema,
+ relayerApiOrderbookChannelSnapshotPayload,
};
diff --git a/packages/json-schemas/test/schema_test.ts b/packages/json-schemas/test/schema_test.ts
index a6162c8e3..7b058781d 100644
--- a/packages/json-schemas/test/schema_test.ts
+++ b/packages/json-schemas/test/schema_test.ts
@@ -11,906 +11,906 @@ chai.use(dirtyChai);
const expect = chai.expect;
const NULL_ADDRESS = '0x0000000000000000000000000000000000000000';
const {
- numberSchema,
- addressSchema,
- ecSignatureSchema,
- ecSignatureParameterSchema,
- orderCancellationRequestsSchema,
- orderFillOrKillRequestsSchema,
- orderFillRequestsSchema,
- orderHashSchema,
- orderSchema,
- signedOrderSchema,
- signedOrdersSchema,
- blockParamSchema,
- blockRangeSchema,
- tokenSchema,
- jsNumber,
- txDataSchema,
- relayerApiErrorResponseSchema,
- relayerApiOrderBookResponseSchema,
- relayerApiTokenPairsResponseSchema,
- relayerApiFeesPayloadSchema,
- relayerApiFeesResponseSchema,
- relayerApiOrderbookChannelSubscribeSchema,
- relayerApiOrderbookChannelUpdateSchema,
- relayerApiOrderbookChannelSnapshotSchema,
+ numberSchema,
+ addressSchema,
+ ecSignatureSchema,
+ ecSignatureParameterSchema,
+ orderCancellationRequestsSchema,
+ orderFillOrKillRequestsSchema,
+ orderFillRequestsSchema,
+ orderHashSchema,
+ orderSchema,
+ signedOrderSchema,
+ signedOrdersSchema,
+ blockParamSchema,
+ blockRangeSchema,
+ tokenSchema,
+ jsNumber,
+ txDataSchema,
+ relayerApiErrorResponseSchema,
+ relayerApiOrderBookResponseSchema,
+ relayerApiTokenPairsResponseSchema,
+ relayerApiFeesPayloadSchema,
+ relayerApiFeesResponseSchema,
+ relayerApiOrderbookChannelSubscribeSchema,
+ relayerApiOrderbookChannelUpdateSchema,
+ relayerApiOrderbookChannelSnapshotSchema,
} = schemas;
describe('Schema', () => {
- const validator = new SchemaValidator();
- const validateAgainstSchema = (testCases: any[], schema: any, shouldFail = false) => {
- forEach(testCases, (testCase: any) => {
- const validationResult = validator.validate(testCase, schema);
- const hasErrors = validationResult.errors.length !== 0;
- if (shouldFail) {
- if (!hasErrors) {
- throw new Error(
- `Expected testCase: ${JSON.stringify(testCase, null, '\t')} to fail and it didn't.`,
- );
- }
- } else {
- if (hasErrors) {
- throw new Error(JSON.stringify(validationResult.errors, null, '\t'));
- }
- }
- });
- };
- describe('#numberSchema', () => {
- it('should validate valid numbers', () => {
- const testCases = ['42', '0', '1.3', '0.2', '00.00'];
- validateAgainstSchema(testCases, numberSchema);
- });
- it('should fail for invalid numbers', () => {
- const testCases = ['.3', '1.', 'abacaba', 'и', '1..0'];
- const shouldFail = true;
- validateAgainstSchema(testCases, numberSchema, shouldFail);
- });
- });
- describe('#addressSchema', () => {
- it('should validate valid addresses', () => {
- const testCases = ['0x8b0292b11a196601ed2ce54b665cafeca0347d42', NULL_ADDRESS];
- validateAgainstSchema(testCases, addressSchema);
- });
- it('should fail for invalid addresses', () => {
- const testCases = [
- '0x',
- '0',
- '0x00',
- '0xzzzzzzB11a196601eD2ce54B665CaFEca0347D42',
- '0x8b0292B11a196601eD2ce54B665CaFEca0347D42',
- ];
- const shouldFail = true;
- validateAgainstSchema(testCases, addressSchema, shouldFail);
- });
- });
- describe('#ecSignatureParameterSchema', () => {
- it('should validate valid parameters', () => {
- const testCases = [
- '0x61a3ed31b43c8780e905a260a35faefcc527be7516aa11c0256729b5b351bc33',
- '0X40349190569279751135161d22529dc25add4f6069af05be04cacbda2ace2254',
- ];
- validateAgainstSchema(testCases, ecSignatureParameterSchema);
- });
- it('should fail for invalid parameters', () => {
- const testCases = [
- '0x61a3ed31b43c8780e905a260a35faefcc527be7516aa11c0256729b5b351bc3', // shorter
- '0xzzzz9190569279751135161d22529dc25add4f6069af05be04cacbda2ace2254', // invalid characters
- '40349190569279751135161d22529dc25add4f6069af05be04cacbda2ace2254', // no 0x
- ];
- const shouldFail = true;
- validateAgainstSchema(testCases, ecSignatureParameterSchema, shouldFail);
- });
- });
- describe('#ecSignatureSchema', () => {
- it('should validate valid signature', () => {
- const signature = {
- v: 27,
- r: '0x61a3ed31b43c8780e905a260a35faefcc527be7516aa11c0256729b5b351bc33',
- s: '0x40349190569279751135161d22529dc25add4f6069af05be04cacbda2ace2254',
- };
- const testCases = [
- signature,
- {
- ...signature,
- v: 28,
- },
- ];
- validateAgainstSchema(testCases, ecSignatureSchema);
- });
- it('should fail for invalid signature', () => {
- const v = 27;
- const r = '0x61a3ed31b43c8780e905a260a35faefcc527be7516aa11c0256729b5b351bc33';
- const s = '0x40349190569279751135161d22529dc25add4f6069af05be04cacbda2ace2254';
- const testCases = [{}, { v }, { r, s, v: 31 }];
- const shouldFail = true;
- validateAgainstSchema(testCases, ecSignatureSchema, shouldFail);
- });
- });
- describe('#orderHashSchema', () => {
- it('should validate valid order hash', () => {
- const testCases = [
- '0x61a3ed31B43c8780e905a260a35faefEc527be7516aa11c0256729b5b351bc33',
- '0x40349190569279751135161d22529dc25add4f6069af05be04cacbda2ace2254',
- ];
- validateAgainstSchema(testCases, orderHashSchema);
- });
- it('should fail for invalid order hash', () => {
- const testCases = [
- {},
- '0x',
- '0x8b0292B11a196601eD2ce54B665CaFEca0347D42',
- '61a3ed31B43c8780e905a260a35faefEc527be7516aa11c0256729b5b351bc33',
- ];
- const shouldFail = true;
- validateAgainstSchema(testCases, orderHashSchema, shouldFail);
- });
- });
- describe('#blockParamSchema', () => {
- it('should validate valid block param', () => {
- const testCases = [42, 'latest', 'pending', 'earliest'];
- validateAgainstSchema(testCases, blockParamSchema);
- });
- it('should fail for invalid block param', () => {
- const testCases = [{}, '42', 'pemding'];
- const shouldFail = true;
- validateAgainstSchema(testCases, blockParamSchema, shouldFail);
- });
- });
- describe('#blockRangeSchema', () => {
- it('should validate valid subscription opts', () => {
- const testCases = [{ fromBlock: 42, toBlock: 'latest' }, { fromBlock: 42 }, {}];
- validateAgainstSchema(testCases, blockRangeSchema);
- });
- it('should fail for invalid subscription opts', () => {
- const testCases = [{ fromBlock: '42' }];
- const shouldFail = true;
- validateAgainstSchema(testCases, blockRangeSchema, shouldFail);
- });
- });
- describe('#tokenSchema', () => {
- const token = {
- name: 'Zero Ex',
- symbol: 'ZRX',
- decimals: 100500,
- address: '0x8b0292b11a196601ed2ce54b665cafeca0347d42',
- url: 'https://0xproject.com',
- };
- it('should validate valid token', () => {
- const testCases = [token];
- validateAgainstSchema(testCases, tokenSchema);
- });
- it('should fail for invalid token', () => {
- const testCases = [
- {
- ...token,
- address: null,
- },
- {
- ...token,
- decimals: undefined,
- },
- [],
- 4,
- ];
- const shouldFail = true;
- validateAgainstSchema(testCases, tokenSchema, shouldFail);
- });
- });
- describe('order including schemas', () => {
- const order = {
- maker: NULL_ADDRESS,
- taker: NULL_ADDRESS,
- makerFee: '1',
- takerFee: '2',
- makerTokenAmount: '1',
- takerTokenAmount: '2',
- makerTokenAddress: NULL_ADDRESS,
- takerTokenAddress: NULL_ADDRESS,
- salt: '67006738228878699843088602623665307406148487219438534730168799356281242528500',
- feeRecipient: NULL_ADDRESS,
- exchangeContractAddress: NULL_ADDRESS,
- expirationUnixTimestampSec: '42',
- };
- describe('#orderSchema', () => {
- it('should validate valid order', () => {
- const testCases = [order];
- validateAgainstSchema(testCases, orderSchema);
- });
- it('should fail for invalid order', () => {
- const testCases = [
- {
- ...order,
- salt: undefined,
- },
- {
- ...order,
- salt: 'salt',
- },
- 'order',
- ];
- const shouldFail = true;
- validateAgainstSchema(testCases, orderSchema, shouldFail);
- });
- });
- describe('signed order including schemas', () => {
- const signedOrder = {
- ...order,
- ecSignature: {
- v: 27,
- r: '0x61a3ed31b43c8780e905a260a35faefcc527be7516aa11c0256729b5b351bc33',
- s: '0x40349190569279751135161d22529dc25add4f6069af05be04cacbda2ace2254',
- },
- };
- describe('#signedOrdersSchema', () => {
- it('should validate valid signed orders', () => {
- const testCases = [[signedOrder], []];
- validateAgainstSchema(testCases, signedOrdersSchema);
- });
- it('should fail for invalid signed orders', () => {
- const testCases = [[signedOrder, 1]];
- const shouldFail = true;
- validateAgainstSchema(testCases, signedOrdersSchema, shouldFail);
- });
- });
- describe('#signedOrderSchema', () => {
- it('should validate valid signed order', () => {
- const testCases = [signedOrder];
- validateAgainstSchema(testCases, signedOrderSchema);
- });
- it('should fail for invalid signed order', () => {
- const testCases = [
- {
- ...signedOrder,
- ecSignature: undefined,
- },
- ];
- const shouldFail = true;
- validateAgainstSchema(testCases, signedOrderSchema, shouldFail);
- });
- });
- describe('#orderFillOrKillRequestsSchema', () => {
- const orderFillOrKillRequests = [
- {
- signedOrder,
- fillTakerAmount: '5',
- },
- ];
- it('should validate valid order fill or kill requests', () => {
- const testCases = [orderFillOrKillRequests];
- validateAgainstSchema(testCases, orderFillOrKillRequestsSchema);
- });
- it('should fail for invalid order fill or kill requests', () => {
- const testCases = [
- [
- {
- ...orderFillOrKillRequests[0],
- fillTakerAmount: undefined,
- },
- ],
- ];
- const shouldFail = true;
- validateAgainstSchema(testCases, orderFillOrKillRequestsSchema, shouldFail);
- });
- });
- describe('#orderCancellationRequestsSchema', () => {
- const orderCancellationRequests = [
- {
- order,
- takerTokenCancelAmount: '5',
- },
- ];
- it('should validate valid order cancellation requests', () => {
- const testCases = [orderCancellationRequests];
- validateAgainstSchema(testCases, orderCancellationRequestsSchema);
- });
- it('should fail for invalid order cancellation requests', () => {
- const testCases = [
- [
- {
- ...orderCancellationRequests[0],
- takerTokenCancelAmount: undefined,
- },
- ],
- ];
- const shouldFail = true;
- validateAgainstSchema(testCases, orderCancellationRequestsSchema, shouldFail);
- });
- });
- describe('#orderFillRequestsSchema', () => {
- const orderFillRequests = [
- {
- signedOrder,
- takerTokenFillAmount: '5',
- },
- ];
- it('should validate valid order fill requests', () => {
- const testCases = [orderFillRequests];
- validateAgainstSchema(testCases, orderFillRequestsSchema);
- });
- it('should fail for invalid order fill requests', () => {
- const testCases = [
- [
- {
- ...orderFillRequests[0],
- takerTokenFillAmount: undefined,
- },
- ],
- ];
- const shouldFail = true;
- validateAgainstSchema(testCases, orderFillRequestsSchema, shouldFail);
- });
- });
- describe('#relayerApiOrderBookResponseSchema', () => {
- it('should validate valid order book responses', () => {
- const testCases = [
- {
- bids: [],
- asks: [],
- },
- {
- bids: [signedOrder, signedOrder],
- asks: [],
- },
- {
- bids: [],
- asks: [signedOrder, signedOrder],
- },
- {
- bids: [signedOrder],
- asks: [signedOrder, signedOrder],
- },
- ];
- validateAgainstSchema(testCases, relayerApiOrderBookResponseSchema);
- });
- it('should fail for invalid order fill requests', () => {
- const testCases = [
- {},
- {
- bids: [signedOrder, signedOrder],
- },
- {
- asks: [signedOrder, signedOrder],
- },
- {
- bids: signedOrder,
- asks: [signedOrder, signedOrder],
- },
- {
- bids: [signedOrder],
- asks: signedOrder,
- },
- ];
- const shouldFail = true;
- validateAgainstSchema(testCases, relayerApiOrderBookResponseSchema, shouldFail);
- });
- });
- describe('#relayerApiOrderbookChannelSubscribeSchema', () => {
- it('should validate valid orderbook channel websocket subscribe message', () => {
- const testCases = [
- {
- type: 'subscribe',
- channel: 'orderbook',
- requestId: 1,
- payload: {
- baseTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
- quoteTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
- snapshot: true,
- limit: 100,
- },
- },
- {
- type: 'subscribe',
- channel: 'orderbook',
- requestId: 1,
- payload: {
- baseTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
- quoteTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
- },
- },
- ];
- validateAgainstSchema(testCases, relayerApiOrderbookChannelSubscribeSchema);
- });
- it('should fail for invalid orderbook channel websocket subscribe message', () => {
- const checksummedAddress = '0xA2b31daCf30a9C50ca473337c01d8A201ae33e32';
- const testCases = [
- {
- type: 'subscribe',
- channel: 'orderbook',
- payload: {
- baseTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
- quoteTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
- snapshot: true,
- limit: 100,
- },
- },
- {
- type: 'foo',
- channel: 'orderbook',
- requestId: 1,
- payload: {
- baseTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
- quoteTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
- },
- },
- {
- type: 'subscribe',
- channel: 'bar',
- requestId: 1,
- payload: {
- baseTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
- quoteTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
- },
- },
- {
- type: 'subscribe',
- channel: 'orderbook',
- requestId: 1,
- payload: {
- baseTokenAddress: checksummedAddress,
- quoteTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
- },
- },
- {
- type: 'subscribe',
- channel: 'orderbook',
- requestId: 1,
- payload: {
- baseTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
- quoteTokenAddress: checksummedAddress,
- },
- },
- {
- type: 'subscribe',
- channel: 'orderbook',
- requestId: 1,
- payload: {
- quoteTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
- },
- },
- {
- type: 'subscribe',
- channel: 'orderbook',
- requestId: 1,
- payload: {
- baseTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
- },
- },
- {
- type: 'subscribe',
- channel: 'orderbook',
- requestId: 1,
- payload: {
- baseTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
- quoteTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
- snapshot: 'true',
- limit: 100,
- },
- },
- {
- type: 'subscribe',
- channel: 'orderbook',
- requestId: 1,
- payload: {
- baseTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
- quoteTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
- snapshot: true,
- limit: '100',
- },
- },
- ];
- const shouldFail = true;
- validateAgainstSchema(testCases, relayerApiOrderbookChannelSubscribeSchema, shouldFail);
- });
- });
- describe('#relayerApiOrderbookChannelSnapshotSchema', () => {
- it('should validate valid orderbook channel websocket snapshot message', () => {
- const testCases = [
- {
- type: 'snapshot',
- channel: 'orderbook',
- requestId: 2,
- payload: {
- bids: [],
- asks: [],
- },
- },
- {
- type: 'snapshot',
- channel: 'orderbook',
- requestId: 2,
- payload: {
- bids: [signedOrder],
- asks: [signedOrder],
- },
- },
- ];
- validateAgainstSchema(testCases, relayerApiOrderbookChannelSnapshotSchema);
- });
- it('should fail for invalid orderbook channel websocket snapshot message', () => {
- const testCases = [
- {
- type: 'foo',
- channel: 'orderbook',
- requestId: 2,
- payload: {
- bids: [signedOrder],
- asks: [signedOrder],
- },
- },
- {
- type: 'snapshot',
- channel: 'bar',
- requestId: 2,
- payload: {
- bids: [signedOrder],
- asks: [signedOrder],
- },
- },
- {
- type: 'snapshot',
- channel: 'orderbook',
- payload: {
- bids: [signedOrder],
- asks: [signedOrder],
- },
- },
- {
- type: 'snapshot',
- channel: 'orderbook',
- requestId: '2',
- payload: {
- bids: [signedOrder],
- asks: [signedOrder],
- },
- },
- {
- type: 'snapshot',
- channel: 'orderbook',
- requestId: 2,
- payload: {
- bids: [signedOrder],
- },
- },
- {
- type: 'snapshot',
- channel: 'orderbook',
- requestId: 2,
- payload: {
- asks: [signedOrder],
- },
- },
- {
- type: 'snapshot',
- channel: 'orderbook',
- requestId: 2,
- payload: {
- bids: [signedOrder],
- asks: [{}],
- },
- },
- {
- type: 'snapshot',
- channel: 'orderbook',
- requestId: 2,
- payload: {
- bids: [{}],
- asks: [signedOrder],
- },
- },
- ];
- const shouldFail = true;
- validateAgainstSchema(testCases, relayerApiOrderbookChannelSnapshotSchema, shouldFail);
- });
- });
- describe('#relayerApiOrderbookChannelUpdateSchema', () => {
- it('should validate valid orderbook channel websocket update message', () => {
- const testCases = [
- {
- type: 'update',
- channel: 'orderbook',
- requestId: 2,
- payload: signedOrder,
- },
- ];
- validateAgainstSchema(testCases, relayerApiOrderbookChannelUpdateSchema);
- });
- it('should fail for invalid orderbook channel websocket update message', () => {
- const testCases = [
- {
- type: 'foo',
- channel: 'orderbook',
- requestId: 2,
- payload: signedOrder,
- },
- {
- type: 'update',
- channel: 'bar',
- requestId: 2,
- payload: signedOrder,
- },
- {
- type: 'update',
- channel: 'orderbook',
- requestId: 2,
- payload: {},
- },
- ];
- const shouldFail = true;
- validateAgainstSchema(testCases, relayerApiOrderbookChannelUpdateSchema, shouldFail);
- });
- });
- });
- });
- describe('BigNumber serialization', () => {
- it('should correctly serialize BigNumbers', () => {
- const testCases = {
- '42': '42',
- '0': '0',
- '1.3': '1.3',
- '0.2': '0.2',
- '00.00': '0',
- '.3': '0.3',
- };
- forEach(testCases, (serialized: string, input: string) => {
- expect(JSON.parse(JSON.stringify(new BigNumber(input)))).to.be.equal(serialized);
- });
- });
- });
- describe('#relayerApiErrorResponseSchema', () => {
- it('should validate valid errorResponse', () => {
- const testCases = [
- {
- code: 102,
- reason: 'Order submission disabled',
- },
- {
- code: 101,
- reason: 'Validation failed',
- validationErrors: [
- {
- field: 'maker',
- code: 1002,
- reason: 'Invalid address',
- },
- ],
- },
- ];
- validateAgainstSchema(testCases, relayerApiErrorResponseSchema);
- });
- it('should fail for invalid error responses', () => {
- const testCases = [
- {},
- {
- code: 102,
- },
- {
- code: '102',
- reason: 'Order submission disabled',
- },
- {
- reason: 'Order submission disabled',
- },
- {
- code: 101,
- reason: 'Validation failed',
- validationErrors: [
- {
- field: 'maker',
- reason: 'Invalid address',
- },
- ],
- },
- {
- code: 101,
- reason: 'Validation failed',
- validationErrors: [
- {
- field: 'maker',
- code: '1002',
- reason: 'Invalid address',
- },
- ],
- },
- ];
- const shouldFail = true;
- validateAgainstSchema(testCases, relayerApiErrorResponseSchema, shouldFail);
- });
- });
- describe('#relayerApiFeesPayloadSchema', () => {
- it('should validate valid fees payloads', () => {
- const testCases = [
- {
- exchangeContractAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
- maker: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
- taker: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
- makerTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
- takerTokenAddress: '0xef7fff64389b814a946f3e92105513705ca6b990',
- makerTokenAmount: '10000000000000000000',
- takerTokenAmount: '30000000000000000000',
- expirationUnixTimestampSec: '42',
- salt: '67006738228878699843088602623665307406148487219438534730168799356281242528500',
- },
- ];
- validateAgainstSchema(testCases, relayerApiFeesPayloadSchema);
- });
- it('should fail for invalid fees payloads', () => {
- const checksummedAddress = '0xA2b31daCf30a9C50ca473337c01d8A201ae33e32';
- const testCases = [
- {},
- {
- takerTokenAddress: '0xef7fff64389b814a946f3e92105513705ca6b990',
- makerTokenAmount: '10000000000000000000',
- takerTokenAmount: '30000000000000000000',
- },
- {
- taker: checksummedAddress,
- makerTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
- takerTokenAddress: '0xef7fff64389b814a946f3e92105513705ca6b990',
- makerTokenAmount: '10000000000000000000',
- takerTokenAmount: '30000000000000000000',
- },
- {
- makerTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
- takerTokenAddress: '0xef7fff64389b814a946f3e92105513705ca6b990',
- makerTokenAmount: 10000000000000000000,
- takerTokenAmount: 30000000000000000000,
- },
- ];
- const shouldFail = true;
- validateAgainstSchema(testCases, relayerApiFeesPayloadSchema, shouldFail);
- });
- });
- describe('#relayerApiFeesResponseSchema', () => {
- it('should validate valid fees responses', () => {
- const testCases = [
- {
- makerFee: '10000000000000000',
- takerFee: '30000000000000000',
- feeRecipient: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
- },
- ];
- validateAgainstSchema(testCases, relayerApiFeesResponseSchema);
- });
- it('should fail for invalid fees responses', () => {
- const checksummedAddress = '0xA2b31daCf30a9C50ca473337c01d8A201ae33e32';
- const testCases = [
- {},
- {
- makerFee: 10000000000000000,
- takerFee: 30000000000000000,
- },
- {
- feeRecipient: checksummedAddress,
- takerToSpecify: checksummedAddress,
- makerFee: '10000000000000000',
- takerFee: '30000000000000000',
- },
- ];
- const shouldFail = true;
- validateAgainstSchema(testCases, relayerApiFeesResponseSchema, shouldFail);
- });
- });
- describe('#relayerApiTokenPairsResponseSchema', () => {
- it('should validate valid tokenPairs response', () => {
- const testCases = [
- [],
- [
- {
- tokenA: {
- address: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
- minAmount: '0',
- maxAmount: '10000000000000000000',
- precision: 5,
- },
- tokenB: {
- address: '0xef7fff64389b814a946f3e92105513705ca6b990',
- minAmount: '0',
- maxAmount: '50000000000000000000',
- precision: 5,
- },
- },
- ],
- [
- {
- tokenA: {
- address: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
- },
- tokenB: {
- address: '0xef7fff64389b814a946f3e92105513705ca6b990',
- },
- },
- ],
- ];
- validateAgainstSchema(testCases, relayerApiTokenPairsResponseSchema);
- });
- it('should fail for invalid tokenPairs responses', () => {
- const checksummedAddress = '0xA2b31daCf30a9C50ca473337c01d8A201ae33e32';
- const testCases = [
- [
- {
- tokenA: {
- address: checksummedAddress,
- },
- tokenB: {
- address: checksummedAddress,
- },
- },
- ],
- [
- {
- tokenA: {
- address: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
- minAmount: 0,
- maxAmount: 10000000000000000000,
- },
- tokenB: {
- address: '0xef7fff64389b814a946f3e92105513705ca6b990',
- minAmount: 0,
- maxAmount: 50000000000000000000,
- },
- },
- ],
- [
- {
- tokenA: {
- address: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
- precision: '5',
- },
- tokenB: {
- address: '0xef7fff64389b814a946f3e92105513705ca6b990',
- precision: '5',
- },
- },
- ],
- ];
- const shouldFail = true;
- validateAgainstSchema(testCases, relayerApiTokenPairsResponseSchema, shouldFail);
- });
- });
- describe('#jsNumberSchema', () => {
- it('should validate valid js number', () => {
- const testCases = [1, 42];
- validateAgainstSchema(testCases, jsNumber);
- });
- it('should fail for invalid js number', () => {
- const testCases = [NaN, -1, new BigNumber(1)];
- const shouldFail = true;
- validateAgainstSchema(testCases, jsNumber, shouldFail);
- });
- });
- describe('#txDataSchema', () => {
- it('should validate valid txData', () => {
- const testCases = [
- {
- from: NULL_ADDRESS,
- },
- {
- from: NULL_ADDRESS,
- gas: new BigNumber(42),
- },
- {
- from: NULL_ADDRESS,
- gas: 42,
- },
- ];
- validateAgainstSchema(testCases, txDataSchema);
- });
- it('should fail for invalid txData', () => {
- const testCases = [
- {
- gas: new BigNumber(42),
- },
- {
- from: NULL_ADDRESS,
- unknownProp: 'here',
- },
- {},
- [],
- new BigNumber(1),
- ];
- const shouldFail = true;
- validateAgainstSchema(testCases, txDataSchema, shouldFail);
- });
- });
+ const validator = new SchemaValidator();
+ const validateAgainstSchema = (testCases: any[], schema: any, shouldFail = false) => {
+ forEach(testCases, (testCase: any) => {
+ const validationResult = validator.validate(testCase, schema);
+ const hasErrors = validationResult.errors.length !== 0;
+ if (shouldFail) {
+ if (!hasErrors) {
+ throw new Error(
+ `Expected testCase: ${JSON.stringify(testCase, null, '\t')} to fail and it didn't.`,
+ );
+ }
+ } else {
+ if (hasErrors) {
+ throw new Error(JSON.stringify(validationResult.errors, null, '\t'));
+ }
+ }
+ });
+ };
+ describe('#numberSchema', () => {
+ it('should validate valid numbers', () => {
+ const testCases = ['42', '0', '1.3', '0.2', '00.00'];
+ validateAgainstSchema(testCases, numberSchema);
+ });
+ it('should fail for invalid numbers', () => {
+ const testCases = ['.3', '1.', 'abacaba', 'и', '1..0'];
+ const shouldFail = true;
+ validateAgainstSchema(testCases, numberSchema, shouldFail);
+ });
+ });
+ describe('#addressSchema', () => {
+ it('should validate valid addresses', () => {
+ const testCases = ['0x8b0292b11a196601ed2ce54b665cafeca0347d42', NULL_ADDRESS];
+ validateAgainstSchema(testCases, addressSchema);
+ });
+ it('should fail for invalid addresses', () => {
+ const testCases = [
+ '0x',
+ '0',
+ '0x00',
+ '0xzzzzzzB11a196601eD2ce54B665CaFEca0347D42',
+ '0x8b0292B11a196601eD2ce54B665CaFEca0347D42',
+ ];
+ const shouldFail = true;
+ validateAgainstSchema(testCases, addressSchema, shouldFail);
+ });
+ });
+ describe('#ecSignatureParameterSchema', () => {
+ it('should validate valid parameters', () => {
+ const testCases = [
+ '0x61a3ed31b43c8780e905a260a35faefcc527be7516aa11c0256729b5b351bc33',
+ '0X40349190569279751135161d22529dc25add4f6069af05be04cacbda2ace2254',
+ ];
+ validateAgainstSchema(testCases, ecSignatureParameterSchema);
+ });
+ it('should fail for invalid parameters', () => {
+ const testCases = [
+ '0x61a3ed31b43c8780e905a260a35faefcc527be7516aa11c0256729b5b351bc3', // shorter
+ '0xzzzz9190569279751135161d22529dc25add4f6069af05be04cacbda2ace2254', // invalid characters
+ '40349190569279751135161d22529dc25add4f6069af05be04cacbda2ace2254', // no 0x
+ ];
+ const shouldFail = true;
+ validateAgainstSchema(testCases, ecSignatureParameterSchema, shouldFail);
+ });
+ });
+ describe('#ecSignatureSchema', () => {
+ it('should validate valid signature', () => {
+ const signature = {
+ v: 27,
+ r: '0x61a3ed31b43c8780e905a260a35faefcc527be7516aa11c0256729b5b351bc33',
+ s: '0x40349190569279751135161d22529dc25add4f6069af05be04cacbda2ace2254',
+ };
+ const testCases = [
+ signature,
+ {
+ ...signature,
+ v: 28,
+ },
+ ];
+ validateAgainstSchema(testCases, ecSignatureSchema);
+ });
+ it('should fail for invalid signature', () => {
+ const v = 27;
+ const r = '0x61a3ed31b43c8780e905a260a35faefcc527be7516aa11c0256729b5b351bc33';
+ const s = '0x40349190569279751135161d22529dc25add4f6069af05be04cacbda2ace2254';
+ const testCases = [{}, { v }, { r, s, v: 31 }];
+ const shouldFail = true;
+ validateAgainstSchema(testCases, ecSignatureSchema, shouldFail);
+ });
+ });
+ describe('#orderHashSchema', () => {
+ it('should validate valid order hash', () => {
+ const testCases = [
+ '0x61a3ed31B43c8780e905a260a35faefEc527be7516aa11c0256729b5b351bc33',
+ '0x40349190569279751135161d22529dc25add4f6069af05be04cacbda2ace2254',
+ ];
+ validateAgainstSchema(testCases, orderHashSchema);
+ });
+ it('should fail for invalid order hash', () => {
+ const testCases = [
+ {},
+ '0x',
+ '0x8b0292B11a196601eD2ce54B665CaFEca0347D42',
+ '61a3ed31B43c8780e905a260a35faefEc527be7516aa11c0256729b5b351bc33',
+ ];
+ const shouldFail = true;
+ validateAgainstSchema(testCases, orderHashSchema, shouldFail);
+ });
+ });
+ describe('#blockParamSchema', () => {
+ it('should validate valid block param', () => {
+ const testCases = [42, 'latest', 'pending', 'earliest'];
+ validateAgainstSchema(testCases, blockParamSchema);
+ });
+ it('should fail for invalid block param', () => {
+ const testCases = [{}, '42', 'pemding'];
+ const shouldFail = true;
+ validateAgainstSchema(testCases, blockParamSchema, shouldFail);
+ });
+ });
+ describe('#blockRangeSchema', () => {
+ it('should validate valid subscription opts', () => {
+ const testCases = [{ fromBlock: 42, toBlock: 'latest' }, { fromBlock: 42 }, {}];
+ validateAgainstSchema(testCases, blockRangeSchema);
+ });
+ it('should fail for invalid subscription opts', () => {
+ const testCases = [{ fromBlock: '42' }];
+ const shouldFail = true;
+ validateAgainstSchema(testCases, blockRangeSchema, shouldFail);
+ });
+ });
+ describe('#tokenSchema', () => {
+ const token = {
+ name: 'Zero Ex',
+ symbol: 'ZRX',
+ decimals: 100500,
+ address: '0x8b0292b11a196601ed2ce54b665cafeca0347d42',
+ url: 'https://0xproject.com',
+ };
+ it('should validate valid token', () => {
+ const testCases = [token];
+ validateAgainstSchema(testCases, tokenSchema);
+ });
+ it('should fail for invalid token', () => {
+ const testCases = [
+ {
+ ...token,
+ address: null,
+ },
+ {
+ ...token,
+ decimals: undefined,
+ },
+ [],
+ 4,
+ ];
+ const shouldFail = true;
+ validateAgainstSchema(testCases, tokenSchema, shouldFail);
+ });
+ });
+ describe('order including schemas', () => {
+ const order = {
+ maker: NULL_ADDRESS,
+ taker: NULL_ADDRESS,
+ makerFee: '1',
+ takerFee: '2',
+ makerTokenAmount: '1',
+ takerTokenAmount: '2',
+ makerTokenAddress: NULL_ADDRESS,
+ takerTokenAddress: NULL_ADDRESS,
+ salt: '67006738228878699843088602623665307406148487219438534730168799356281242528500',
+ feeRecipient: NULL_ADDRESS,
+ exchangeContractAddress: NULL_ADDRESS,
+ expirationUnixTimestampSec: '42',
+ };
+ describe('#orderSchema', () => {
+ it('should validate valid order', () => {
+ const testCases = [order];
+ validateAgainstSchema(testCases, orderSchema);
+ });
+ it('should fail for invalid order', () => {
+ const testCases = [
+ {
+ ...order,
+ salt: undefined,
+ },
+ {
+ ...order,
+ salt: 'salt',
+ },
+ 'order',
+ ];
+ const shouldFail = true;
+ validateAgainstSchema(testCases, orderSchema, shouldFail);
+ });
+ });
+ describe('signed order including schemas', () => {
+ const signedOrder = {
+ ...order,
+ ecSignature: {
+ v: 27,
+ r: '0x61a3ed31b43c8780e905a260a35faefcc527be7516aa11c0256729b5b351bc33',
+ s: '0x40349190569279751135161d22529dc25add4f6069af05be04cacbda2ace2254',
+ },
+ };
+ describe('#signedOrdersSchema', () => {
+ it('should validate valid signed orders', () => {
+ const testCases = [[signedOrder], []];
+ validateAgainstSchema(testCases, signedOrdersSchema);
+ });
+ it('should fail for invalid signed orders', () => {
+ const testCases = [[signedOrder, 1]];
+ const shouldFail = true;
+ validateAgainstSchema(testCases, signedOrdersSchema, shouldFail);
+ });
+ });
+ describe('#signedOrderSchema', () => {
+ it('should validate valid signed order', () => {
+ const testCases = [signedOrder];
+ validateAgainstSchema(testCases, signedOrderSchema);
+ });
+ it('should fail for invalid signed order', () => {
+ const testCases = [
+ {
+ ...signedOrder,
+ ecSignature: undefined,
+ },
+ ];
+ const shouldFail = true;
+ validateAgainstSchema(testCases, signedOrderSchema, shouldFail);
+ });
+ });
+ describe('#orderFillOrKillRequestsSchema', () => {
+ const orderFillOrKillRequests = [
+ {
+ signedOrder,
+ fillTakerAmount: '5',
+ },
+ ];
+ it('should validate valid order fill or kill requests', () => {
+ const testCases = [orderFillOrKillRequests];
+ validateAgainstSchema(testCases, orderFillOrKillRequestsSchema);
+ });
+ it('should fail for invalid order fill or kill requests', () => {
+ const testCases = [
+ [
+ {
+ ...orderFillOrKillRequests[0],
+ fillTakerAmount: undefined,
+ },
+ ],
+ ];
+ const shouldFail = true;
+ validateAgainstSchema(testCases, orderFillOrKillRequestsSchema, shouldFail);
+ });
+ });
+ describe('#orderCancellationRequestsSchema', () => {
+ const orderCancellationRequests = [
+ {
+ order,
+ takerTokenCancelAmount: '5',
+ },
+ ];
+ it('should validate valid order cancellation requests', () => {
+ const testCases = [orderCancellationRequests];
+ validateAgainstSchema(testCases, orderCancellationRequestsSchema);
+ });
+ it('should fail for invalid order cancellation requests', () => {
+ const testCases = [
+ [
+ {
+ ...orderCancellationRequests[0],
+ takerTokenCancelAmount: undefined,
+ },
+ ],
+ ];
+ const shouldFail = true;
+ validateAgainstSchema(testCases, orderCancellationRequestsSchema, shouldFail);
+ });
+ });
+ describe('#orderFillRequestsSchema', () => {
+ const orderFillRequests = [
+ {
+ signedOrder,
+ takerTokenFillAmount: '5',
+ },
+ ];
+ it('should validate valid order fill requests', () => {
+ const testCases = [orderFillRequests];
+ validateAgainstSchema(testCases, orderFillRequestsSchema);
+ });
+ it('should fail for invalid order fill requests', () => {
+ const testCases = [
+ [
+ {
+ ...orderFillRequests[0],
+ takerTokenFillAmount: undefined,
+ },
+ ],
+ ];
+ const shouldFail = true;
+ validateAgainstSchema(testCases, orderFillRequestsSchema, shouldFail);
+ });
+ });
+ describe('#relayerApiOrderBookResponseSchema', () => {
+ it('should validate valid order book responses', () => {
+ const testCases = [
+ {
+ bids: [],
+ asks: [],
+ },
+ {
+ bids: [signedOrder, signedOrder],
+ asks: [],
+ },
+ {
+ bids: [],
+ asks: [signedOrder, signedOrder],
+ },
+ {
+ bids: [signedOrder],
+ asks: [signedOrder, signedOrder],
+ },
+ ];
+ validateAgainstSchema(testCases, relayerApiOrderBookResponseSchema);
+ });
+ it('should fail for invalid order fill requests', () => {
+ const testCases = [
+ {},
+ {
+ bids: [signedOrder, signedOrder],
+ },
+ {
+ asks: [signedOrder, signedOrder],
+ },
+ {
+ bids: signedOrder,
+ asks: [signedOrder, signedOrder],
+ },
+ {
+ bids: [signedOrder],
+ asks: signedOrder,
+ },
+ ];
+ const shouldFail = true;
+ validateAgainstSchema(testCases, relayerApiOrderBookResponseSchema, shouldFail);
+ });
+ });
+ describe('#relayerApiOrderbookChannelSubscribeSchema', () => {
+ it('should validate valid orderbook channel websocket subscribe message', () => {
+ const testCases = [
+ {
+ type: 'subscribe',
+ channel: 'orderbook',
+ requestId: 1,
+ payload: {
+ baseTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
+ quoteTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
+ snapshot: true,
+ limit: 100,
+ },
+ },
+ {
+ type: 'subscribe',
+ channel: 'orderbook',
+ requestId: 1,
+ payload: {
+ baseTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
+ quoteTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
+ },
+ },
+ ];
+ validateAgainstSchema(testCases, relayerApiOrderbookChannelSubscribeSchema);
+ });
+ it('should fail for invalid orderbook channel websocket subscribe message', () => {
+ const checksummedAddress = '0xA2b31daCf30a9C50ca473337c01d8A201ae33e32';
+ const testCases = [
+ {
+ type: 'subscribe',
+ channel: 'orderbook',
+ payload: {
+ baseTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
+ quoteTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
+ snapshot: true,
+ limit: 100,
+ },
+ },
+ {
+ type: 'foo',
+ channel: 'orderbook',
+ requestId: 1,
+ payload: {
+ baseTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
+ quoteTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
+ },
+ },
+ {
+ type: 'subscribe',
+ channel: 'bar',
+ requestId: 1,
+ payload: {
+ baseTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
+ quoteTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
+ },
+ },
+ {
+ type: 'subscribe',
+ channel: 'orderbook',
+ requestId: 1,
+ payload: {
+ baseTokenAddress: checksummedAddress,
+ quoteTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
+ },
+ },
+ {
+ type: 'subscribe',
+ channel: 'orderbook',
+ requestId: 1,
+ payload: {
+ baseTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
+ quoteTokenAddress: checksummedAddress,
+ },
+ },
+ {
+ type: 'subscribe',
+ channel: 'orderbook',
+ requestId: 1,
+ payload: {
+ quoteTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
+ },
+ },
+ {
+ type: 'subscribe',
+ channel: 'orderbook',
+ requestId: 1,
+ payload: {
+ baseTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
+ },
+ },
+ {
+ type: 'subscribe',
+ channel: 'orderbook',
+ requestId: 1,
+ payload: {
+ baseTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
+ quoteTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
+ snapshot: 'true',
+ limit: 100,
+ },
+ },
+ {
+ type: 'subscribe',
+ channel: 'orderbook',
+ requestId: 1,
+ payload: {
+ baseTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
+ quoteTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
+ snapshot: true,
+ limit: '100',
+ },
+ },
+ ];
+ const shouldFail = true;
+ validateAgainstSchema(testCases, relayerApiOrderbookChannelSubscribeSchema, shouldFail);
+ });
+ });
+ describe('#relayerApiOrderbookChannelSnapshotSchema', () => {
+ it('should validate valid orderbook channel websocket snapshot message', () => {
+ const testCases = [
+ {
+ type: 'snapshot',
+ channel: 'orderbook',
+ requestId: 2,
+ payload: {
+ bids: [],
+ asks: [],
+ },
+ },
+ {
+ type: 'snapshot',
+ channel: 'orderbook',
+ requestId: 2,
+ payload: {
+ bids: [signedOrder],
+ asks: [signedOrder],
+ },
+ },
+ ];
+ validateAgainstSchema(testCases, relayerApiOrderbookChannelSnapshotSchema);
+ });
+ it('should fail for invalid orderbook channel websocket snapshot message', () => {
+ const testCases = [
+ {
+ type: 'foo',
+ channel: 'orderbook',
+ requestId: 2,
+ payload: {
+ bids: [signedOrder],
+ asks: [signedOrder],
+ },
+ },
+ {
+ type: 'snapshot',
+ channel: 'bar',
+ requestId: 2,
+ payload: {
+ bids: [signedOrder],
+ asks: [signedOrder],
+ },
+ },
+ {
+ type: 'snapshot',
+ channel: 'orderbook',
+ payload: {
+ bids: [signedOrder],
+ asks: [signedOrder],
+ },
+ },
+ {
+ type: 'snapshot',
+ channel: 'orderbook',
+ requestId: '2',
+ payload: {
+ bids: [signedOrder],
+ asks: [signedOrder],
+ },
+ },
+ {
+ type: 'snapshot',
+ channel: 'orderbook',
+ requestId: 2,
+ payload: {
+ bids: [signedOrder],
+ },
+ },
+ {
+ type: 'snapshot',
+ channel: 'orderbook',
+ requestId: 2,
+ payload: {
+ asks: [signedOrder],
+ },
+ },
+ {
+ type: 'snapshot',
+ channel: 'orderbook',
+ requestId: 2,
+ payload: {
+ bids: [signedOrder],
+ asks: [{}],
+ },
+ },
+ {
+ type: 'snapshot',
+ channel: 'orderbook',
+ requestId: 2,
+ payload: {
+ bids: [{}],
+ asks: [signedOrder],
+ },
+ },
+ ];
+ const shouldFail = true;
+ validateAgainstSchema(testCases, relayerApiOrderbookChannelSnapshotSchema, shouldFail);
+ });
+ });
+ describe('#relayerApiOrderbookChannelUpdateSchema', () => {
+ it('should validate valid orderbook channel websocket update message', () => {
+ const testCases = [
+ {
+ type: 'update',
+ channel: 'orderbook',
+ requestId: 2,
+ payload: signedOrder,
+ },
+ ];
+ validateAgainstSchema(testCases, relayerApiOrderbookChannelUpdateSchema);
+ });
+ it('should fail for invalid orderbook channel websocket update message', () => {
+ const testCases = [
+ {
+ type: 'foo',
+ channel: 'orderbook',
+ requestId: 2,
+ payload: signedOrder,
+ },
+ {
+ type: 'update',
+ channel: 'bar',
+ requestId: 2,
+ payload: signedOrder,
+ },
+ {
+ type: 'update',
+ channel: 'orderbook',
+ requestId: 2,
+ payload: {},
+ },
+ ];
+ const shouldFail = true;
+ validateAgainstSchema(testCases, relayerApiOrderbookChannelUpdateSchema, shouldFail);
+ });
+ });
+ });
+ });
+ describe('BigNumber serialization', () => {
+ it('should correctly serialize BigNumbers', () => {
+ const testCases = {
+ '42': '42',
+ '0': '0',
+ '1.3': '1.3',
+ '0.2': '0.2',
+ '00.00': '0',
+ '.3': '0.3',
+ };
+ forEach(testCases, (serialized: string, input: string) => {
+ expect(JSON.parse(JSON.stringify(new BigNumber(input)))).to.be.equal(serialized);
+ });
+ });
+ });
+ describe('#relayerApiErrorResponseSchema', () => {
+ it('should validate valid errorResponse', () => {
+ const testCases = [
+ {
+ code: 102,
+ reason: 'Order submission disabled',
+ },
+ {
+ code: 101,
+ reason: 'Validation failed',
+ validationErrors: [
+ {
+ field: 'maker',
+ code: 1002,
+ reason: 'Invalid address',
+ },
+ ],
+ },
+ ];
+ validateAgainstSchema(testCases, relayerApiErrorResponseSchema);
+ });
+ it('should fail for invalid error responses', () => {
+ const testCases = [
+ {},
+ {
+ code: 102,
+ },
+ {
+ code: '102',
+ reason: 'Order submission disabled',
+ },
+ {
+ reason: 'Order submission disabled',
+ },
+ {
+ code: 101,
+ reason: 'Validation failed',
+ validationErrors: [
+ {
+ field: 'maker',
+ reason: 'Invalid address',
+ },
+ ],
+ },
+ {
+ code: 101,
+ reason: 'Validation failed',
+ validationErrors: [
+ {
+ field: 'maker',
+ code: '1002',
+ reason: 'Invalid address',
+ },
+ ],
+ },
+ ];
+ const shouldFail = true;
+ validateAgainstSchema(testCases, relayerApiErrorResponseSchema, shouldFail);
+ });
+ });
+ describe('#relayerApiFeesPayloadSchema', () => {
+ it('should validate valid fees payloads', () => {
+ const testCases = [
+ {
+ exchangeContractAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
+ maker: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
+ taker: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
+ makerTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
+ takerTokenAddress: '0xef7fff64389b814a946f3e92105513705ca6b990',
+ makerTokenAmount: '10000000000000000000',
+ takerTokenAmount: '30000000000000000000',
+ expirationUnixTimestampSec: '42',
+ salt: '67006738228878699843088602623665307406148487219438534730168799356281242528500',
+ },
+ ];
+ validateAgainstSchema(testCases, relayerApiFeesPayloadSchema);
+ });
+ it('should fail for invalid fees payloads', () => {
+ const checksummedAddress = '0xA2b31daCf30a9C50ca473337c01d8A201ae33e32';
+ const testCases = [
+ {},
+ {
+ takerTokenAddress: '0xef7fff64389b814a946f3e92105513705ca6b990',
+ makerTokenAmount: '10000000000000000000',
+ takerTokenAmount: '30000000000000000000',
+ },
+ {
+ taker: checksummedAddress,
+ makerTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
+ takerTokenAddress: '0xef7fff64389b814a946f3e92105513705ca6b990',
+ makerTokenAmount: '10000000000000000000',
+ takerTokenAmount: '30000000000000000000',
+ },
+ {
+ makerTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
+ takerTokenAddress: '0xef7fff64389b814a946f3e92105513705ca6b990',
+ makerTokenAmount: 10000000000000000000,
+ takerTokenAmount: 30000000000000000000,
+ },
+ ];
+ const shouldFail = true;
+ validateAgainstSchema(testCases, relayerApiFeesPayloadSchema, shouldFail);
+ });
+ });
+ describe('#relayerApiFeesResponseSchema', () => {
+ it('should validate valid fees responses', () => {
+ const testCases = [
+ {
+ makerFee: '10000000000000000',
+ takerFee: '30000000000000000',
+ feeRecipient: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
+ },
+ ];
+ validateAgainstSchema(testCases, relayerApiFeesResponseSchema);
+ });
+ it('should fail for invalid fees responses', () => {
+ const checksummedAddress = '0xA2b31daCf30a9C50ca473337c01d8A201ae33e32';
+ const testCases = [
+ {},
+ {
+ makerFee: 10000000000000000,
+ takerFee: 30000000000000000,
+ },
+ {
+ feeRecipient: checksummedAddress,
+ takerToSpecify: checksummedAddress,
+ makerFee: '10000000000000000',
+ takerFee: '30000000000000000',
+ },
+ ];
+ const shouldFail = true;
+ validateAgainstSchema(testCases, relayerApiFeesResponseSchema, shouldFail);
+ });
+ });
+ describe('#relayerApiTokenPairsResponseSchema', () => {
+ it('should validate valid tokenPairs response', () => {
+ const testCases = [
+ [],
+ [
+ {
+ tokenA: {
+ address: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
+ minAmount: '0',
+ maxAmount: '10000000000000000000',
+ precision: 5,
+ },
+ tokenB: {
+ address: '0xef7fff64389b814a946f3e92105513705ca6b990',
+ minAmount: '0',
+ maxAmount: '50000000000000000000',
+ precision: 5,
+ },
+ },
+ ],
+ [
+ {
+ tokenA: {
+ address: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
+ },
+ tokenB: {
+ address: '0xef7fff64389b814a946f3e92105513705ca6b990',
+ },
+ },
+ ],
+ ];
+ validateAgainstSchema(testCases, relayerApiTokenPairsResponseSchema);
+ });
+ it('should fail for invalid tokenPairs responses', () => {
+ const checksummedAddress = '0xA2b31daCf30a9C50ca473337c01d8A201ae33e32';
+ const testCases = [
+ [
+ {
+ tokenA: {
+ address: checksummedAddress,
+ },
+ tokenB: {
+ address: checksummedAddress,
+ },
+ },
+ ],
+ [
+ {
+ tokenA: {
+ address: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
+ minAmount: 0,
+ maxAmount: 10000000000000000000,
+ },
+ tokenB: {
+ address: '0xef7fff64389b814a946f3e92105513705ca6b990',
+ minAmount: 0,
+ maxAmount: 50000000000000000000,
+ },
+ },
+ ],
+ [
+ {
+ tokenA: {
+ address: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
+ precision: '5',
+ },
+ tokenB: {
+ address: '0xef7fff64389b814a946f3e92105513705ca6b990',
+ precision: '5',
+ },
+ },
+ ],
+ ];
+ const shouldFail = true;
+ validateAgainstSchema(testCases, relayerApiTokenPairsResponseSchema, shouldFail);
+ });
+ });
+ describe('#jsNumberSchema', () => {
+ it('should validate valid js number', () => {
+ const testCases = [1, 42];
+ validateAgainstSchema(testCases, jsNumber);
+ });
+ it('should fail for invalid js number', () => {
+ const testCases = [NaN, -1, new BigNumber(1)];
+ const shouldFail = true;
+ validateAgainstSchema(testCases, jsNumber, shouldFail);
+ });
+ });
+ describe('#txDataSchema', () => {
+ it('should validate valid txData', () => {
+ const testCases = [
+ {
+ from: NULL_ADDRESS,
+ },
+ {
+ from: NULL_ADDRESS,
+ gas: new BigNumber(42),
+ },
+ {
+ from: NULL_ADDRESS,
+ gas: 42,
+ },
+ ];
+ validateAgainstSchema(testCases, txDataSchema);
+ });
+ it('should fail for invalid txData', () => {
+ const testCases = [
+ {
+ gas: new BigNumber(42),
+ },
+ {
+ from: NULL_ADDRESS,
+ unknownProp: 'here',
+ },
+ {},
+ [],
+ new BigNumber(1),
+ ];
+ const shouldFail = true;
+ validateAgainstSchema(testCases, txDataSchema, shouldFail);
+ });
+ });
}); // tslint:disable:max-file-line-count
diff --git a/packages/json-schemas/tsconfig.json b/packages/json-schemas/tsconfig.json
index 8314a9459..88a467ccb 100644
--- a/packages/json-schemas/tsconfig.json
+++ b/packages/json-schemas/tsconfig.json
@@ -1,7 +1,7 @@
{
- "extends": "../../tsconfig",
- "compilerOptions": {
- "outDir": "lib"
- },
- "include": ["./src/**/*", "./test/**/*", "../../node_modules/chai-typescript-typings/index.d.ts"]
+ "extends": "../../tsconfig",
+ "compilerOptions": {
+ "outDir": "lib"
+ },
+ "include": ["./src/**/*", "./test/**/*", "../../node_modules/chai-typescript-typings/index.d.ts"]
}
diff --git a/packages/json-schemas/tslint.json b/packages/json-schemas/tslint.json
index e63054bfc..ffaefe83a 100644
--- a/packages/json-schemas/tslint.json
+++ b/packages/json-schemas/tslint.json
@@ -1,3 +1,3 @@
{
- "extends": ["@0xproject/tslint-config"]
+ "extends": ["@0xproject/tslint-config"]
}
diff --git a/packages/monorepo-scripts/package.json b/packages/monorepo-scripts/package.json
index 31eaf6eca..2bf2aa796 100644
--- a/packages/monorepo-scripts/package.json
+++ b/packages/monorepo-scripts/package.json
@@ -1,34 +1,34 @@
{
- "name": "@0xproject/monorepo-scripts",
- "version": "0.1.6",
- "private": true,
- "description": "Helper scripts for the monorepo",
- "scripts": {
- "deps_versions": "node ./lib/deps_versions.js",
- "lint": "tslint --project . 'src/**/*.ts'",
- "clean": "shx rm -rf lib",
- "build": "tsc"
- },
- "repository": {
- "type": "git",
- "url": "https://github.com/0xProject/0x.js.git"
- },
- "license": "Apache-2.0",
- "bugs": {
- "url": "https://github.com/0xProject/0x.js/issues"
- },
- "homepage": "https://github.com/0xProject/0x.js/packages/monorepo-scripts/README.md",
- "devDependencies": {
- "@0xproject/tslint-config": "^0.4.4",
- "@types/glob": "^5.0.33",
- "@types/node": "^8.0.53",
- "shx": "^0.2.2",
- "tslint": "5.8.0",
- "typescript": "~2.6.1"
- },
- "dependencies": {
- "chalk": "^2.3.0",
- "glob": "^7.1.2",
- "lodash": "^4.17.4"
- }
+ "name": "@0xproject/monorepo-scripts",
+ "version": "0.1.6",
+ "private": true,
+ "description": "Helper scripts for the monorepo",
+ "scripts": {
+ "deps_versions": "node ./lib/deps_versions.js",
+ "lint": "tslint --project . 'src/**/*.ts'",
+ "clean": "shx rm -rf lib",
+ "build": "tsc"
+ },
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/0xProject/0x.js.git"
+ },
+ "license": "Apache-2.0",
+ "bugs": {
+ "url": "https://github.com/0xProject/0x.js/issues"
+ },
+ "homepage": "https://github.com/0xProject/0x.js/packages/monorepo-scripts/README.md",
+ "devDependencies": {
+ "@0xproject/tslint-config": "^0.4.4",
+ "@types/glob": "^5.0.33",
+ "@types/node": "^8.0.53",
+ "shx": "^0.2.2",
+ "tslint": "5.8.0",
+ "typescript": "~2.6.1"
+ },
+ "dependencies": {
+ "chalk": "^2.3.0",
+ "glob": "^7.1.2",
+ "lodash": "^4.17.4"
+ }
}
diff --git a/packages/monorepo-scripts/src/deps_versions.ts b/packages/monorepo-scripts/src/deps_versions.ts
index 2c0aa6c54..5c9a2d6ff 100644
--- a/packages/monorepo-scripts/src/deps_versions.ts
+++ b/packages/monorepo-scripts/src/deps_versions.ts
@@ -6,51 +6,51 @@ import { sync as globSync } from 'glob';
import * as _ from 'lodash';
interface Dependencies {
- [depName: string]: string;
+ [depName: string]: string;
}
interface Versions {
- [packageName: string]: string;
+ [packageName: string]: string;
}
interface VersionsByDependency {
- [depName: string]: Versions;
+ [depName: string]: Versions;
}
const PACKAGE_JSON_GLOB = '../*/package.json';
function log(...args: any[]) {
- console.log(...args); // tslint:disable-line:no-console
+ console.log(...args); // tslint:disable-line:no-console
}
function getDependencies(path: string): Dependencies {
- const file = fs.readFileSync(path).toString();
- const parsed = JSON.parse(file);
- const dependencies = {
- ...parsed.dependencies,
- ...parsed.devDependencies,
- };
- return dependencies;
+ const file = fs.readFileSync(path).toString();
+ const parsed = JSON.parse(file);
+ const dependencies = {
+ ...parsed.dependencies,
+ ...parsed.devDependencies,
+ };
+ return dependencies;
}
const files = globSync(PACKAGE_JSON_GLOB);
const versionsByDependency: VersionsByDependency = {};
files.map(path => {
- const [_1, packageName, _2] = path.split('/');
- const dependencies = getDependencies(path);
- _.map(dependencies, (version: string, depName: string) => {
- if (_.isUndefined(versionsByDependency[depName])) {
- versionsByDependency[depName] = {};
- }
- versionsByDependency[depName][packageName] = version;
- });
+ const [_1, packageName, _2] = path.split('/');
+ const dependencies = getDependencies(path);
+ _.map(dependencies, (version: string, depName: string) => {
+ if (_.isUndefined(versionsByDependency[depName])) {
+ versionsByDependency[depName] = {};
+ }
+ versionsByDependency[depName][packageName] = version;
+ });
});
_.map(versionsByDependency, (versions: Versions, depName: string) => {
- if (_.uniq(_.values(versions)).length === 1) {
- delete versionsByDependency[depName];
- } else {
- log(chalk.bold(depName));
- _.map(versions, (version: string, packageName: string) => {
- log(`├── ${packageName} -> ${version}`);
- });
- }
+ if (_.uniq(_.values(versions)).length === 1) {
+ delete versionsByDependency[depName];
+ } else {
+ log(chalk.bold(depName));
+ _.map(versions, (version: string, packageName: string) => {
+ log(`├── ${packageName} -> ${version}`);
+ });
+ }
});
diff --git a/packages/monorepo-scripts/tsconfig.json b/packages/monorepo-scripts/tsconfig.json
index ad674c2b0..c56d255d5 100644
--- a/packages/monorepo-scripts/tsconfig.json
+++ b/packages/monorepo-scripts/tsconfig.json
@@ -1,7 +1,7 @@
{
- "extends": "../../tsconfig",
- "compilerOptions": {
- "outDir": "lib"
- },
- "include": ["./src/**/*"]
+ "extends": "../../tsconfig",
+ "compilerOptions": {
+ "outDir": "lib"
+ },
+ "include": ["./src/**/*"]
}
diff --git a/packages/monorepo-scripts/tslint.json b/packages/monorepo-scripts/tslint.json
index e63054bfc..ffaefe83a 100644
--- a/packages/monorepo-scripts/tslint.json
+++ b/packages/monorepo-scripts/tslint.json
@@ -1,3 +1,3 @@
{
- "extends": ["@0xproject/tslint-config"]
+ "extends": ["@0xproject/tslint-config"]
}
diff --git a/packages/subproviders/package.json b/packages/subproviders/package.json
index 1da019e2d..146c94496 100644
--- a/packages/subproviders/package.json
+++ b/packages/subproviders/package.json
@@ -1,55 +1,55 @@
{
- "name": "@0xproject/subproviders",
- "version": "0.3.3",
- "main": "lib/src/index.js",
- "types": "lib/src/index.d.ts",
- "license": "Apache-2.0",
- "scripts": {
- "clean": "shx rm -rf lib",
- "build": "tsc",
- "lint": "tslint --project . 'src/**/*.ts' 'test/**/*.ts'",
- "run_mocha_unit": "mocha lib/test/unit/**/*_test.js --timeout 10000 --bail --exit",
- "run_mocha_integration": "mocha lib/test/integration/**/*_test.js --timeout 10000 --bail --exit",
- "test": "npm run test:unit",
- "test:circleci": "npm run test:unit",
- "test:all": "run-s test:unit test:integration",
- "test:unit": "run-s clean build run_mocha_unit",
- "test:integration": "run-s clean build run_mocha_integration"
- },
- "dependencies": {
- "@0xproject/assert": "^0.0.13",
- "@0xproject/utils": "^0.2.2",
- "bn.js": "^4.11.8",
- "es6-promisify": "^5.0.0",
- "ethereumjs-tx": "^1.3.3",
- "ethereumjs-util": "^5.1.1",
- "hdkey": "^0.7.1",
- "ledgerco": "0xProject/ledger-node-js-api",
- "lodash": "^4.17.4",
- "semaphore-async-await": "^1.5.1",
- "web3": "^0.20.0",
- "web3-provider-engine": "^13.0.1"
- },
- "devDependencies": {
- "@0xproject/tslint-config": "^0.4.4",
- "@0xproject/utils": "^0.2.2",
- "@types/lodash": "^4.14.86",
- "@types/mocha": "^2.2.42",
- "@types/node": "^8.0.53",
- "awesome-typescript-loader": "^3.1.3",
- "chai": "^4.0.1",
- "chai-as-promised": "^7.1.0",
- "chai-as-promised-typescript-typings": "^0.0.6",
- "chai-typescript-typings": "^0.0.2",
- "dirty-chai": "^2.0.1",
- "mocha": "^4.0.1",
- "npm-run-all": "^4.1.2",
- "shx": "^0.2.2",
- "tslint": "5.8.0",
- "types-bn": "^0.0.1",
- "types-ethereumjs-util": "0xproject/types-ethereumjs-util",
- "typescript": "~2.6.1",
- "web3-typescript-typings": "^0.7.2",
- "webpack": "^3.1.0"
- }
+ "name": "@0xproject/subproviders",
+ "version": "0.3.3",
+ "main": "lib/src/index.js",
+ "types": "lib/src/index.d.ts",
+ "license": "Apache-2.0",
+ "scripts": {
+ "clean": "shx rm -rf lib",
+ "build": "tsc",
+ "lint": "tslint --project . 'src/**/*.ts' 'test/**/*.ts'",
+ "run_mocha_unit": "mocha lib/test/unit/**/*_test.js --timeout 10000 --bail --exit",
+ "run_mocha_integration": "mocha lib/test/integration/**/*_test.js --timeout 10000 --bail --exit",
+ "test": "npm run test:unit",
+ "test:circleci": "npm run test:unit",
+ "test:all": "run-s test:unit test:integration",
+ "test:unit": "run-s clean build run_mocha_unit",
+ "test:integration": "run-s clean build run_mocha_integration"
+ },
+ "dependencies": {
+ "@0xproject/assert": "^0.0.13",
+ "@0xproject/utils": "^0.2.2",
+ "bn.js": "^4.11.8",
+ "es6-promisify": "^5.0.0",
+ "ethereumjs-tx": "^1.3.3",
+ "ethereumjs-util": "^5.1.1",
+ "hdkey": "^0.7.1",
+ "ledgerco": "0xProject/ledger-node-js-api",
+ "lodash": "^4.17.4",
+ "semaphore-async-await": "^1.5.1",
+ "web3": "^0.20.0",
+ "web3-provider-engine": "^13.0.1"
+ },
+ "devDependencies": {
+ "@0xproject/tslint-config": "^0.4.4",
+ "@0xproject/utils": "^0.2.2",
+ "@types/lodash": "^4.14.86",
+ "@types/mocha": "^2.2.42",
+ "@types/node": "^8.0.53",
+ "awesome-typescript-loader": "^3.1.3",
+ "chai": "^4.0.1",
+ "chai-as-promised": "^7.1.0",
+ "chai-as-promised-typescript-typings": "^0.0.6",
+ "chai-typescript-typings": "^0.0.2",
+ "dirty-chai": "^2.0.1",
+ "mocha": "^4.0.1",
+ "npm-run-all": "^4.1.2",
+ "shx": "^0.2.2",
+ "tslint": "5.8.0",
+ "types-bn": "^0.0.1",
+ "types-ethereumjs-util": "0xproject/types-ethereumjs-util",
+ "typescript": "~2.6.1",
+ "web3-typescript-typings": "^0.7.2",
+ "webpack": "^3.1.0"
+ }
}
diff --git a/packages/subproviders/src/globals.d.ts b/packages/subproviders/src/globals.d.ts
index ed4fbc145..53457fa24 100644
--- a/packages/subproviders/src/globals.d.ts
+++ b/packages/subproviders/src/globals.d.ts
@@ -8,103 +8,103 @@ declare module 'es6-promisify';
// Ethereumjs-tx declarations
declare module 'ethereumjs-tx' {
- class EthereumTx {
- public raw: Buffer[];
- public r: Buffer;
- public s: Buffer;
- public v: Buffer;
- public serialize(): Buffer;
- constructor(txParams: any);
- }
- export = EthereumTx;
+ class EthereumTx {
+ public raw: Buffer[];
+ public r: Buffer;
+ public s: Buffer;
+ public v: Buffer;
+ public serialize(): Buffer;
+ constructor(txParams: any);
+ }
+ export = EthereumTx;
}
// Ledgerco declarations
interface ECSignatureString {
- v: string;
- r: string;
- s: string;
+ v: string;
+ r: string;
+ s: string;
}
interface ECSignature {
- v: number;
- r: string;
- s: string;
+ v: number;
+ r: string;
+ s: string;
}
declare module 'ledgerco' {
- interface comm {
- close_async(): Promise<void>;
- }
- export class comm_node implements comm {
- public static create_async(timeoutMilliseconds?: number): Promise<comm_node>;
- public close_async(): Promise<void>;
- }
- export class comm_u2f implements comm {
- public static create_async(): Promise<comm_u2f>;
- public close_async(): Promise<void>;
- }
- export class eth {
- public comm: comm;
- constructor(comm: comm);
- public getAddress_async(
- path: string,
- display?: boolean,
- chaincode?: boolean,
- ): Promise<{ publicKey: string; address: string; chainCode: string }>;
- public signTransaction_async(path: string, rawTxHex: string): Promise<ECSignatureString>;
- public getAppConfiguration_async(): Promise<{
- arbitraryDataEnabled: number;
- version: string;
- }>;
- public signPersonalMessage_async(path: string, messageHex: string): Promise<ECSignature>;
- }
+ interface comm {
+ close_async(): Promise<void>;
+ }
+ export class comm_node implements comm {
+ public static create_async(timeoutMilliseconds?: number): Promise<comm_node>;
+ public close_async(): Promise<void>;
+ }
+ export class comm_u2f implements comm {
+ public static create_async(): Promise<comm_u2f>;
+ public close_async(): Promise<void>;
+ }
+ export class eth {
+ public comm: comm;
+ constructor(comm: comm);
+ public getAddress_async(
+ path: string,
+ display?: boolean,
+ chaincode?: boolean,
+ ): Promise<{ publicKey: string; address: string; chainCode: string }>;
+ public signTransaction_async(path: string, rawTxHex: string): Promise<ECSignatureString>;
+ public getAppConfiguration_async(): Promise<{
+ arbitraryDataEnabled: number;
+ version: string;
+ }>;
+ public signPersonalMessage_async(path: string, messageHex: string): Promise<ECSignature>;
+ }
}
// Semaphore-async-await declarations
declare module 'semaphore-async-await' {
- class Semaphore {
- constructor(permits: number);
- public wait(): Promise<void>;
- public signal(): void;
- }
- export default Semaphore;
+ class Semaphore {
+ constructor(permits: number);
+ public wait(): Promise<void>;
+ public signal(): void;
+ }
+ export default Semaphore;
}
// web3-provider-engine declarations
declare module 'web3-provider-engine/subproviders/subprovider' {
- class Subprovider {}
- export = Subprovider;
+ class Subprovider {}
+ export = Subprovider;
}
declare module 'web3-provider-engine/subproviders/rpc' {
- import * as Web3 from 'web3';
- class RpcSubprovider {
- constructor(options: { rpcUrl: string });
- public handleRequest(
- payload: Web3.JSONRPCRequestPayload,
- next: () => void,
- end: (err: Error | null, data?: any) => void,
- ): void;
- }
- export = RpcSubprovider;
+ import * as Web3 from 'web3';
+ class RpcSubprovider {
+ constructor(options: { rpcUrl: string });
+ public handleRequest(
+ payload: Web3.JSONRPCRequestPayload,
+ next: () => void,
+ end: (err: Error | null, data?: any) => void,
+ ): void;
+ }
+ export = RpcSubprovider;
}
declare module 'web3-provider-engine' {
- class Web3ProviderEngine {
- public on(event: string, handler: () => void): void;
- public send(payload: any): void;
- public sendAsync(payload: any, callback: (error: any, response: any) => void): void;
- public addProvider(provider: any): void;
- public start(): void;
- public stop(): void;
- }
- export = Web3ProviderEngine;
+ class Web3ProviderEngine {
+ public on(event: string, handler: () => void): void;
+ public send(payload: any): void;
+ public sendAsync(payload: any, callback: (error: any, response: any) => void): void;
+ public addProvider(provider: any): void;
+ public start(): void;
+ public stop(): void;
+ }
+ export = Web3ProviderEngine;
}
// hdkey declarations
declare module 'hdkey' {
- class HDNode {
- public publicKey: Buffer;
- public chainCode: Buffer;
- public constructor();
- public derive(path: string): HDNode;
- }
- export = HDNode;
+ class HDNode {
+ public publicKey: Buffer;
+ public chainCode: Buffer;
+ public constructor();
+ public derive(path: string): HDNode;
+ }
+ export = HDNode;
}
diff --git a/packages/subproviders/src/index.ts b/packages/subproviders/src/index.ts
index ecba186df..720c4362f 100644
--- a/packages/subproviders/src/index.ts
+++ b/packages/subproviders/src/index.ts
@@ -1,7 +1,7 @@
import {
- comm_node as LedgerNodeCommunication,
- comm_u2f as LedgerBrowserCommunication,
- eth as LedgerEthereumClientFn,
+ comm_node as LedgerNodeCommunication,
+ comm_u2f as LedgerBrowserCommunication,
+ eth as LedgerEthereumClientFn,
} from 'ledgerco';
import { LedgerEthereumClient } from './types';
@@ -16,9 +16,9 @@ export { ECSignature, LedgerWalletSubprovider, LedgerCommunicationClient } from
* @return LedgerEthereumClient A browser client
*/
export async function ledgerEthereumBrowserClientFactoryAsync(): Promise<LedgerEthereumClient> {
- const ledgerConnection = await LedgerBrowserCommunication.create_async();
- const ledgerEthClient = new LedgerEthereumClientFn(ledgerConnection);
- return ledgerEthClient;
+ const ledgerConnection = await LedgerBrowserCommunication.create_async();
+ const ledgerEthClient = new LedgerEthereumClientFn(ledgerConnection);
+ return ledgerEthClient;
}
/**
@@ -26,7 +26,7 @@ export async function ledgerEthereumBrowserClientFactoryAsync(): Promise<LedgerE
* @return LedgerEthereumClient A Node.js client
*/
export async function ledgerEthereumNodeJsClientFactoryAsync(): Promise<LedgerEthereumClient> {
- const ledgerConnection = await LedgerNodeCommunication.create_async();
- const ledgerEthClient = new LedgerEthereumClientFn(ledgerConnection);
- return ledgerEthClient;
+ const ledgerConnection = await LedgerNodeCommunication.create_async();
+ const ledgerEthClient = new LedgerEthereumClientFn(ledgerConnection);
+ return ledgerEthClient;
}
diff --git a/packages/subproviders/src/subproviders/injected_web3.ts b/packages/subproviders/src/subproviders/injected_web3.ts
index dc1b8b24c..bd29acb22 100644
--- a/packages/subproviders/src/subproviders/injected_web3.ts
+++ b/packages/subproviders/src/subproviders/injected_web3.ts
@@ -9,41 +9,41 @@ import Web3ProviderEngine = require('web3-provider-engine');
* Source: https://github.com/MetaMask/provider-engine/blob/master/subproviders/subprovider.js
*/
export class InjectedWeb3Subprovider {
- private _injectedWeb3: Web3;
- constructor(injectedWeb3: Web3) {
- this._injectedWeb3 = injectedWeb3;
- }
- public handleRequest(
- payload: Web3.JSONRPCRequestPayload,
- next: () => void,
- end: (err: Error | null, result: any) => void,
- ) {
- switch (payload.method) {
- case 'web3_clientVersion':
- this._injectedWeb3.version.getNode(end);
- return;
- case 'eth_accounts':
- this._injectedWeb3.eth.getAccounts(end);
- return;
+ private _injectedWeb3: Web3;
+ constructor(injectedWeb3: Web3) {
+ this._injectedWeb3 = injectedWeb3;
+ }
+ public handleRequest(
+ payload: Web3.JSONRPCRequestPayload,
+ next: () => void,
+ end: (err: Error | null, result: any) => void,
+ ) {
+ switch (payload.method) {
+ case 'web3_clientVersion':
+ this._injectedWeb3.version.getNode(end);
+ return;
+ case 'eth_accounts':
+ this._injectedWeb3.eth.getAccounts(end);
+ return;
- case 'eth_sendTransaction':
- const [txParams] = payload.params;
- this._injectedWeb3.eth.sendTransaction(txParams, end);
- return;
+ case 'eth_sendTransaction':
+ const [txParams] = payload.params;
+ this._injectedWeb3.eth.sendTransaction(txParams, end);
+ return;
- case 'eth_sign':
- const [address, message] = payload.params;
- this._injectedWeb3.eth.sign(address, message, end);
- return;
+ case 'eth_sign':
+ const [address, message] = payload.params;
+ this._injectedWeb3.eth.sign(address, message, end);
+ return;
- default:
- next();
- return;
- }
- }
- // Required to implement this method despite not needing it for this subprovider
- // tslint:disable-next-line:prefer-function-over-method
- public setEngine(engine: Web3ProviderEngine) {
- // noop
- }
+ default:
+ next();
+ return;
+ }
+ }
+ // Required to implement this method despite not needing it for this subprovider
+ // tslint:disable-next-line:prefer-function-over-method
+ public setEngine(engine: Web3ProviderEngine) {
+ // noop
+ }
}
diff --git a/packages/subproviders/src/subproviders/ledger.ts b/packages/subproviders/src/subproviders/ledger.ts
index 00649873b..7267a793e 100644
--- a/packages/subproviders/src/subproviders/ledger.ts
+++ b/packages/subproviders/src/subproviders/ledger.ts
@@ -8,12 +8,12 @@ import Semaphore from 'semaphore-async-await';
import Web3 = require('web3');
import {
- LedgerEthereumClient,
- LedgerEthereumClientFactoryAsync,
- LedgerSubproviderConfigs,
- LedgerSubproviderErrors,
- PartialTxParams,
- ResponseWithTxParams,
+ LedgerEthereumClient,
+ LedgerEthereumClientFactoryAsync,
+ LedgerSubproviderConfigs,
+ LedgerSubproviderErrors,
+ PartialTxParams,
+ ResponseWithTxParams,
} from '../types';
import { Subprovider } from './subprovider';
@@ -24,284 +24,284 @@ const ASK_FOR_ON_DEVICE_CONFIRMATION = false;
const SHOULD_GET_CHAIN_CODE = true;
export class LedgerSubprovider extends Subprovider {
- private _nonceLock: Semaphore;
- private _connectionLock: Semaphore;
- private _networkId: number;
- private _derivationPath: string;
- private _derivationPathIndex: number;
- private _ledgerEthereumClientFactoryAsync: LedgerEthereumClientFactoryAsync;
- private _ledgerClientIfExists?: LedgerEthereumClient;
- private _shouldAlwaysAskForConfirmation: boolean;
- private static _validateSender(sender: string) {
- if (_.isUndefined(sender) || !addressUtils.isAddress(sender)) {
- throw new Error(LedgerSubproviderErrors.SenderInvalidOrNotSupplied);
- }
- }
- constructor(config: LedgerSubproviderConfigs) {
- super();
- this._nonceLock = new Semaphore(1);
- this._connectionLock = new Semaphore(1);
- this._networkId = config.networkId;
- this._ledgerEthereumClientFactoryAsync = config.ledgerEthereumClientFactoryAsync;
- this._derivationPath = config.derivationPath || DEFAULT_DERIVATION_PATH;
- this._shouldAlwaysAskForConfirmation =
- !_.isUndefined(config.accountFetchingConfigs) &&
- !_.isUndefined(config.accountFetchingConfigs.shouldAskForOnDeviceConfirmation)
- ? config.accountFetchingConfigs.shouldAskForOnDeviceConfirmation
- : ASK_FOR_ON_DEVICE_CONFIRMATION;
- this._derivationPathIndex = 0;
- }
- public getPath(): string {
- return this._derivationPath;
- }
- public setPath(derivationPath: string) {
- this._derivationPath = derivationPath;
- }
- public setPathIndex(pathIndex: number) {
- this._derivationPathIndex = pathIndex;
- }
- public async handleRequest(
- payload: Web3.JSONRPCRequestPayload,
- next: () => void,
- end: (err: Error | null, result?: any) => void,
- ) {
- let accounts;
- let txParams;
- switch (payload.method) {
- case 'eth_coinbase':
- try {
- accounts = await this.getAccountsAsync();
- end(null, accounts[0]);
- } catch (err) {
- end(err);
- }
- return;
+ private _nonceLock: Semaphore;
+ private _connectionLock: Semaphore;
+ private _networkId: number;
+ private _derivationPath: string;
+ private _derivationPathIndex: number;
+ private _ledgerEthereumClientFactoryAsync: LedgerEthereumClientFactoryAsync;
+ private _ledgerClientIfExists?: LedgerEthereumClient;
+ private _shouldAlwaysAskForConfirmation: boolean;
+ private static _validateSender(sender: string) {
+ if (_.isUndefined(sender) || !addressUtils.isAddress(sender)) {
+ throw new Error(LedgerSubproviderErrors.SenderInvalidOrNotSupplied);
+ }
+ }
+ constructor(config: LedgerSubproviderConfigs) {
+ super();
+ this._nonceLock = new Semaphore(1);
+ this._connectionLock = new Semaphore(1);
+ this._networkId = config.networkId;
+ this._ledgerEthereumClientFactoryAsync = config.ledgerEthereumClientFactoryAsync;
+ this._derivationPath = config.derivationPath || DEFAULT_DERIVATION_PATH;
+ this._shouldAlwaysAskForConfirmation =
+ !_.isUndefined(config.accountFetchingConfigs) &&
+ !_.isUndefined(config.accountFetchingConfigs.shouldAskForOnDeviceConfirmation)
+ ? config.accountFetchingConfigs.shouldAskForOnDeviceConfirmation
+ : ASK_FOR_ON_DEVICE_CONFIRMATION;
+ this._derivationPathIndex = 0;
+ }
+ public getPath(): string {
+ return this._derivationPath;
+ }
+ public setPath(derivationPath: string) {
+ this._derivationPath = derivationPath;
+ }
+ public setPathIndex(pathIndex: number) {
+ this._derivationPathIndex = pathIndex;
+ }
+ public async handleRequest(
+ payload: Web3.JSONRPCRequestPayload,
+ next: () => void,
+ end: (err: Error | null, result?: any) => void,
+ ) {
+ let accounts;
+ let txParams;
+ switch (payload.method) {
+ case 'eth_coinbase':
+ try {
+ accounts = await this.getAccountsAsync();
+ end(null, accounts[0]);
+ } catch (err) {
+ end(err);
+ }
+ return;
- case 'eth_accounts':
- try {
- accounts = await this.getAccountsAsync();
- end(null, accounts);
- } catch (err) {
- end(err);
- }
- return;
+ case 'eth_accounts':
+ try {
+ accounts = await this.getAccountsAsync();
+ end(null, accounts);
+ } catch (err) {
+ end(err);
+ }
+ return;
- case 'eth_sendTransaction':
- txParams = payload.params[0];
- try {
- LedgerSubprovider._validateSender(txParams.from);
- const result = await this._sendTransactionAsync(txParams);
- end(null, result);
- } catch (err) {
- end(err);
- }
- return;
+ case 'eth_sendTransaction':
+ txParams = payload.params[0];
+ try {
+ LedgerSubprovider._validateSender(txParams.from);
+ const result = await this._sendTransactionAsync(txParams);
+ end(null, result);
+ } catch (err) {
+ end(err);
+ }
+ return;
- case 'eth_signTransaction':
- txParams = payload.params[0];
- try {
- const result = await this._signTransactionWithoutSendingAsync(txParams);
- end(null, result);
- } catch (err) {
- end(err);
- }
- return;
+ case 'eth_signTransaction':
+ txParams = payload.params[0];
+ try {
+ const result = await this._signTransactionWithoutSendingAsync(txParams);
+ end(null, result);
+ } catch (err) {
+ end(err);
+ }
+ return;
- case 'eth_sign':
- case 'personal_sign':
- const data = payload.method === 'eth_sign' ? payload.params[1] : payload.params[0];
- try {
- if (_.isUndefined(data)) {
- throw new Error(LedgerSubproviderErrors.DataMissingForSignPersonalMessage);
- }
- assert.isHexString('data', data);
- const ecSignatureHex = await this.signPersonalMessageAsync(data);
- end(null, ecSignatureHex);
- } catch (err) {
- end(err);
- }
- return;
+ case 'eth_sign':
+ case 'personal_sign':
+ const data = payload.method === 'eth_sign' ? payload.params[1] : payload.params[0];
+ try {
+ if (_.isUndefined(data)) {
+ throw new Error(LedgerSubproviderErrors.DataMissingForSignPersonalMessage);
+ }
+ assert.isHexString('data', data);
+ const ecSignatureHex = await this.signPersonalMessageAsync(data);
+ end(null, ecSignatureHex);
+ } catch (err) {
+ end(err);
+ }
+ return;
- default:
- next();
- return;
- }
- }
- public async getAccountsAsync(): Promise<string[]> {
- this._ledgerClientIfExists = await this._createLedgerClientAsync();
+ default:
+ next();
+ return;
+ }
+ }
+ public async getAccountsAsync(): Promise<string[]> {
+ this._ledgerClientIfExists = await this._createLedgerClientAsync();
- let ledgerResponse;
- try {
- ledgerResponse = await this._ledgerClientIfExists.getAddress_async(
- this._derivationPath,
- this._shouldAlwaysAskForConfirmation,
- SHOULD_GET_CHAIN_CODE,
- );
- } finally {
- await this._destroyLedgerClientAsync();
- }
+ let ledgerResponse;
+ try {
+ ledgerResponse = await this._ledgerClientIfExists.getAddress_async(
+ this._derivationPath,
+ this._shouldAlwaysAskForConfirmation,
+ SHOULD_GET_CHAIN_CODE,
+ );
+ } finally {
+ await this._destroyLedgerClientAsync();
+ }
- const hdKey = new HDNode();
- hdKey.publicKey = new Buffer(ledgerResponse.publicKey, 'hex');
- hdKey.chainCode = new Buffer(ledgerResponse.chainCode, 'hex');
+ const hdKey = new HDNode();
+ hdKey.publicKey = new Buffer(ledgerResponse.publicKey, 'hex');
+ hdKey.chainCode = new Buffer(ledgerResponse.chainCode, 'hex');
- const accounts = [];
- for (let i = 0; i < NUM_ADDRESSES_TO_FETCH; i++) {
- const derivedHDNode = hdKey.derive(`m/${i + this._derivationPathIndex}`);
- const derivedPublicKey = derivedHDNode.publicKey;
- const shouldSanitizePublicKey = true;
- const ethereumAddressUnprefixed = ethUtil
- .publicToAddress(derivedPublicKey, shouldSanitizePublicKey)
- .toString('hex');
- const ethereumAddressPrefixed = ethUtil.addHexPrefix(ethereumAddressUnprefixed);
- accounts.push(ethereumAddressPrefixed.toLowerCase());
- }
- return accounts;
- }
- public async signTransactionAsync(txParams: PartialTxParams): Promise<string> {
- this._ledgerClientIfExists = await this._createLedgerClientAsync();
+ const accounts = [];
+ for (let i = 0; i < NUM_ADDRESSES_TO_FETCH; i++) {
+ const derivedHDNode = hdKey.derive(`m/${i + this._derivationPathIndex}`);
+ const derivedPublicKey = derivedHDNode.publicKey;
+ const shouldSanitizePublicKey = true;
+ const ethereumAddressUnprefixed = ethUtil
+ .publicToAddress(derivedPublicKey, shouldSanitizePublicKey)
+ .toString('hex');
+ const ethereumAddressPrefixed = ethUtil.addHexPrefix(ethereumAddressUnprefixed);
+ accounts.push(ethereumAddressPrefixed.toLowerCase());
+ }
+ return accounts;
+ }
+ public async signTransactionAsync(txParams: PartialTxParams): Promise<string> {
+ this._ledgerClientIfExists = await this._createLedgerClientAsync();
- const tx = new EthereumTx(txParams);
+ const tx = new EthereumTx(txParams);
- // Set the EIP155 bits
- tx.raw[6] = Buffer.from([this._networkId]); // v
- tx.raw[7] = Buffer.from([]); // r
- tx.raw[8] = Buffer.from([]); // s
+ // Set the EIP155 bits
+ tx.raw[6] = Buffer.from([this._networkId]); // v
+ tx.raw[7] = Buffer.from([]); // r
+ tx.raw[8] = Buffer.from([]); // s
- const txHex = tx.serialize().toString('hex');
- try {
- const derivationPath = this._getDerivationPath();
- const result = await this._ledgerClientIfExists.signTransaction_async(derivationPath, txHex);
- // Store signature in transaction
- tx.r = Buffer.from(result.r, 'hex');
- tx.s = Buffer.from(result.s, 'hex');
- tx.v = Buffer.from(result.v, 'hex');
+ const txHex = tx.serialize().toString('hex');
+ try {
+ const derivationPath = this._getDerivationPath();
+ const result = await this._ledgerClientIfExists.signTransaction_async(derivationPath, txHex);
+ // Store signature in transaction
+ tx.r = Buffer.from(result.r, 'hex');
+ tx.s = Buffer.from(result.s, 'hex');
+ tx.v = Buffer.from(result.v, 'hex');
- // EIP155: v should be chain_id * 2 + {35, 36}
- const signedChainId = Math.floor((tx.v[0] - 35) / 2);
- if (signedChainId !== this._networkId) {
- await this._destroyLedgerClientAsync();
- const err = new Error(LedgerSubproviderErrors.TooOldLedgerFirmware);
- throw err;
- }
+ // EIP155: v should be chain_id * 2 + {35, 36}
+ const signedChainId = Math.floor((tx.v[0] - 35) / 2);
+ if (signedChainId !== this._networkId) {
+ await this._destroyLedgerClientAsync();
+ const err = new Error(LedgerSubproviderErrors.TooOldLedgerFirmware);
+ throw err;
+ }
- const signedTxHex = `0x${tx.serialize().toString('hex')}`;
- await this._destroyLedgerClientAsync();
- return signedTxHex;
- } catch (err) {
- await this._destroyLedgerClientAsync();
- throw err;
- }
- }
- public async signPersonalMessageAsync(data: string): Promise<string> {
- this._ledgerClientIfExists = await this._createLedgerClientAsync();
- try {
- const derivationPath = this._getDerivationPath();
- const result = await this._ledgerClientIfExists.signPersonalMessage_async(
- derivationPath,
- ethUtil.stripHexPrefix(data),
- );
- const v = result.v - 27;
- let vHex = v.toString(16);
- if (vHex.length < 2) {
- vHex = `0${v}`;
- }
- const signature = `0x${result.r}${result.s}${vHex}`;
- await this._destroyLedgerClientAsync();
- return signature;
- } catch (err) {
- await this._destroyLedgerClientAsync();
- throw err;
- }
- }
- private _getDerivationPath() {
- const derivationPath = `${this.getPath()}/${this._derivationPathIndex}`;
- return derivationPath;
- }
- private async _createLedgerClientAsync(): Promise<LedgerEthereumClient> {
- await this._connectionLock.wait();
- if (!_.isUndefined(this._ledgerClientIfExists)) {
- this._connectionLock.signal();
- throw new Error(LedgerSubproviderErrors.MultipleOpenConnectionsDisallowed);
- }
- const ledgerEthereumClient = await this._ledgerEthereumClientFactoryAsync();
- this._connectionLock.signal();
- return ledgerEthereumClient;
- }
- private async _destroyLedgerClientAsync() {
- await this._connectionLock.wait();
- if (_.isUndefined(this._ledgerClientIfExists)) {
- this._connectionLock.signal();
- return;
- }
- await this._ledgerClientIfExists.comm.close_async();
- this._ledgerClientIfExists = undefined;
- this._connectionLock.signal();
- }
- private async _sendTransactionAsync(txParams: PartialTxParams): Promise<string> {
- await this._nonceLock.wait();
- try {
- // fill in the extras
- const filledParams = await this._populateMissingTxParamsAsync(txParams);
- // sign it
- const signedTx = await this.signTransactionAsync(filledParams);
- // emit a submit
- const payload = {
- method: 'eth_sendRawTransaction',
- params: [signedTx],
- };
- const result = await this.emitPayloadAsync(payload);
- this._nonceLock.signal();
- return result.result;
- } catch (err) {
- this._nonceLock.signal();
- throw err;
- }
- }
- private async _signTransactionWithoutSendingAsync(txParams: PartialTxParams): Promise<ResponseWithTxParams> {
- await this._nonceLock.wait();
- try {
- // fill in the extras
- const filledParams = await this._populateMissingTxParamsAsync(txParams);
- // sign it
- const signedTx = await this.signTransactionAsync(filledParams);
+ const signedTxHex = `0x${tx.serialize().toString('hex')}`;
+ await this._destroyLedgerClientAsync();
+ return signedTxHex;
+ } catch (err) {
+ await this._destroyLedgerClientAsync();
+ throw err;
+ }
+ }
+ public async signPersonalMessageAsync(data: string): Promise<string> {
+ this._ledgerClientIfExists = await this._createLedgerClientAsync();
+ try {
+ const derivationPath = this._getDerivationPath();
+ const result = await this._ledgerClientIfExists.signPersonalMessage_async(
+ derivationPath,
+ ethUtil.stripHexPrefix(data),
+ );
+ const v = result.v - 27;
+ let vHex = v.toString(16);
+ if (vHex.length < 2) {
+ vHex = `0${v}`;
+ }
+ const signature = `0x${result.r}${result.s}${vHex}`;
+ await this._destroyLedgerClientAsync();
+ return signature;
+ } catch (err) {
+ await this._destroyLedgerClientAsync();
+ throw err;
+ }
+ }
+ private _getDerivationPath() {
+ const derivationPath = `${this.getPath()}/${this._derivationPathIndex}`;
+ return derivationPath;
+ }
+ private async _createLedgerClientAsync(): Promise<LedgerEthereumClient> {
+ await this._connectionLock.wait();
+ if (!_.isUndefined(this._ledgerClientIfExists)) {
+ this._connectionLock.signal();
+ throw new Error(LedgerSubproviderErrors.MultipleOpenConnectionsDisallowed);
+ }
+ const ledgerEthereumClient = await this._ledgerEthereumClientFactoryAsync();
+ this._connectionLock.signal();
+ return ledgerEthereumClient;
+ }
+ private async _destroyLedgerClientAsync() {
+ await this._connectionLock.wait();
+ if (_.isUndefined(this._ledgerClientIfExists)) {
+ this._connectionLock.signal();
+ return;
+ }
+ await this._ledgerClientIfExists.comm.close_async();
+ this._ledgerClientIfExists = undefined;
+ this._connectionLock.signal();
+ }
+ private async _sendTransactionAsync(txParams: PartialTxParams): Promise<string> {
+ await this._nonceLock.wait();
+ try {
+ // fill in the extras
+ const filledParams = await this._populateMissingTxParamsAsync(txParams);
+ // sign it
+ const signedTx = await this.signTransactionAsync(filledParams);
+ // emit a submit
+ const payload = {
+ method: 'eth_sendRawTransaction',
+ params: [signedTx],
+ };
+ const result = await this.emitPayloadAsync(payload);
+ this._nonceLock.signal();
+ return result.result;
+ } catch (err) {
+ this._nonceLock.signal();
+ throw err;
+ }
+ }
+ private async _signTransactionWithoutSendingAsync(txParams: PartialTxParams): Promise<ResponseWithTxParams> {
+ await this._nonceLock.wait();
+ try {
+ // fill in the extras
+ const filledParams = await this._populateMissingTxParamsAsync(txParams);
+ // sign it
+ const signedTx = await this.signTransactionAsync(filledParams);
- this._nonceLock.signal();
- const result = {
- raw: signedTx,
- tx: txParams,
- };
- return result;
- } catch (err) {
- this._nonceLock.signal();
- throw err;
- }
- }
- private async _populateMissingTxParamsAsync(txParams: PartialTxParams): Promise<PartialTxParams> {
- if (_.isUndefined(txParams.gasPrice)) {
- const gasPriceResult = await this.emitPayloadAsync({
- method: 'eth_gasPrice',
- params: [],
- });
- const gasPrice = gasPriceResult.result.toString();
- txParams.gasPrice = gasPrice;
- }
- if (_.isUndefined(txParams.nonce)) {
- const nonceResult = await this.emitPayloadAsync({
- method: 'eth_getTransactionCount',
- params: [txParams.from, 'pending'],
- });
- const nonce = nonceResult.result;
- txParams.nonce = nonce;
- }
- if (_.isUndefined(txParams.gas)) {
- const gasResult = await this.emitPayloadAsync({
- method: 'eth_estimateGas',
- params: [txParams],
- });
- const gas = gasResult.result.toString();
- txParams.gas = gas;
- }
- return txParams;
- }
+ this._nonceLock.signal();
+ const result = {
+ raw: signedTx,
+ tx: txParams,
+ };
+ return result;
+ } catch (err) {
+ this._nonceLock.signal();
+ throw err;
+ }
+ }
+ private async _populateMissingTxParamsAsync(txParams: PartialTxParams): Promise<PartialTxParams> {
+ if (_.isUndefined(txParams.gasPrice)) {
+ const gasPriceResult = await this.emitPayloadAsync({
+ method: 'eth_gasPrice',
+ params: [],
+ });
+ const gasPrice = gasPriceResult.result.toString();
+ txParams.gasPrice = gasPrice;
+ }
+ if (_.isUndefined(txParams.nonce)) {
+ const nonceResult = await this.emitPayloadAsync({
+ method: 'eth_getTransactionCount',
+ params: [txParams.from, 'pending'],
+ });
+ const nonce = nonceResult.result;
+ txParams.nonce = nonce;
+ }
+ if (_.isUndefined(txParams.gas)) {
+ const gasResult = await this.emitPayloadAsync({
+ method: 'eth_estimateGas',
+ params: [txParams],
+ });
+ const gas = gasResult.result.toString();
+ txParams.gas = gas;
+ }
+ return txParams;
+ }
}
diff --git a/packages/subproviders/src/subproviders/redundant_rpc.ts b/packages/subproviders/src/subproviders/redundant_rpc.ts
index 92d7ae8ae..a3cb463a8 100644
--- a/packages/subproviders/src/subproviders/redundant_rpc.ts
+++ b/packages/subproviders/src/subproviders/redundant_rpc.ts
@@ -7,46 +7,46 @@ import { JSONRPCPayload } from '../types';
import { Subprovider } from './subprovider';
export class RedundantRPCSubprovider extends Subprovider {
- private _rpcs: RpcSubprovider[];
- private static async _firstSuccessAsync(
- rpcs: RpcSubprovider[],
- payload: JSONRPCPayload,
- next: () => void,
- ): Promise<any> {
- let lastErr: Error | undefined;
- for (const rpc of rpcs) {
- try {
- const data = await promisify(rpc.handleRequest.bind(rpc))(payload, next);
- return data;
- } catch (err) {
- lastErr = err;
- continue;
- }
- }
- if (!_.isUndefined(lastErr)) {
- throw lastErr;
- }
- }
- constructor(endpoints: string[]) {
- super();
- this._rpcs = _.map(endpoints, endpoint => {
- return new RpcSubprovider({
- rpcUrl: endpoint,
- });
- });
- }
- // tslint:disable-next-line:async-suffix
- public async handleRequest(
- payload: JSONRPCPayload,
- next: () => void,
- end: (err: Error | null, data?: any) => void,
- ): Promise<void> {
- const rpcsCopy = this._rpcs.slice();
- try {
- const data = await RedundantRPCSubprovider._firstSuccessAsync(rpcsCopy, payload, next);
- end(null, data);
- } catch (err) {
- end(err);
- }
- }
+ private _rpcs: RpcSubprovider[];
+ private static async _firstSuccessAsync(
+ rpcs: RpcSubprovider[],
+ payload: JSONRPCPayload,
+ next: () => void,
+ ): Promise<any> {
+ let lastErr: Error | undefined;
+ for (const rpc of rpcs) {
+ try {
+ const data = await promisify(rpc.handleRequest.bind(rpc))(payload, next);
+ return data;
+ } catch (err) {
+ lastErr = err;
+ continue;
+ }
+ }
+ if (!_.isUndefined(lastErr)) {
+ throw lastErr;
+ }
+ }
+ constructor(endpoints: string[]) {
+ super();
+ this._rpcs = _.map(endpoints, endpoint => {
+ return new RpcSubprovider({
+ rpcUrl: endpoint,
+ });
+ });
+ }
+ // tslint:disable-next-line:async-suffix
+ public async handleRequest(
+ payload: JSONRPCPayload,
+ next: () => void,
+ end: (err: Error | null, data?: any) => void,
+ ): Promise<void> {
+ const rpcsCopy = this._rpcs.slice();
+ try {
+ const data = await RedundantRPCSubprovider._firstSuccessAsync(rpcsCopy, payload, next);
+ end(null, data);
+ } catch (err) {
+ end(err);
+ }
+ }
}
diff --git a/packages/subproviders/src/subproviders/subprovider.ts b/packages/subproviders/src/subproviders/subprovider.ts
index b6bb16918..6435c9f65 100644
--- a/packages/subproviders/src/subproviders/subprovider.ts
+++ b/packages/subproviders/src/subproviders/subprovider.ts
@@ -8,33 +8,33 @@ import { JSONRPCPayload } from '../types';
* Altered version of: https://github.com/MetaMask/provider-engine/blob/master/subproviders/subprovider.js
*/
export class Subprovider {
- private _engine: any;
- // Ported from: https://github.com/MetaMask/provider-engine/blob/master/util/random-id.js
- private static _getRandomId() {
- const extraDigits = 3;
- // 13 time digits
- const datePart = new Date().getTime() * Math.pow(10, extraDigits);
- // 3 random digits
- const extraPart = Math.floor(Math.random() * Math.pow(10, extraDigits));
- // 16 digits
- return datePart + extraPart;
- }
- private static _createFinalPayload(payload: JSONRPCPayload): Web3.JSONRPCRequestPayload {
- const finalPayload = {
- // defaults
- id: Subprovider._getRandomId(),
- jsonrpc: '2.0',
- params: [],
- ...payload,
- };
- return finalPayload;
- }
- public setEngine(engine: any): void {
- this._engine = engine;
- }
- public async emitPayloadAsync(payload: JSONRPCPayload): Promise<any> {
- const finalPayload = Subprovider._createFinalPayload(payload);
- const response = await promisify(this._engine.sendAsync, this._engine)(finalPayload);
- return response;
- }
+ private _engine: any;
+ // Ported from: https://github.com/MetaMask/provider-engine/blob/master/util/random-id.js
+ private static _getRandomId() {
+ const extraDigits = 3;
+ // 13 time digits
+ const datePart = new Date().getTime() * Math.pow(10, extraDigits);
+ // 3 random digits
+ const extraPart = Math.floor(Math.random() * Math.pow(10, extraDigits));
+ // 16 digits
+ return datePart + extraPart;
+ }
+ private static _createFinalPayload(payload: JSONRPCPayload): Web3.JSONRPCRequestPayload {
+ const finalPayload = {
+ // defaults
+ id: Subprovider._getRandomId(),
+ jsonrpc: '2.0',
+ params: [],
+ ...payload,
+ };
+ return finalPayload;
+ }
+ public setEngine(engine: any): void {
+ this._engine = engine;
+ }
+ public async emitPayloadAsync(payload: JSONRPCPayload): Promise<any> {
+ const finalPayload = Subprovider._createFinalPayload(payload);
+ const response = await promisify(this._engine.sendAsync, this._engine)(finalPayload);
+ return response;
+ }
}
diff --git a/packages/subproviders/src/types.ts b/packages/subproviders/src/types.ts
index ec1eedd34..3db8be943 100644
--- a/packages/subproviders/src/types.ts
+++ b/packages/subproviders/src/types.ts
@@ -1,7 +1,7 @@
import * as _ from 'lodash';
export interface LedgerCommunicationClient {
- close_async: () => Promise<void>;
+ close_async: () => Promise<void>;
}
/*
@@ -10,28 +10,28 @@ export interface LedgerCommunicationClient {
* NodeJs and Browser communication are supported.
*/
export interface LedgerEthereumClient {
- // shouldGetChainCode is defined as `true` instead of `boolean` because other types rely on the assumption
- // that we get back the chain code and we don't have dependent types to express it properly
- getAddress_async: (
- derivationPath: string,
- askForDeviceConfirmation: boolean,
- shouldGetChainCode: true,
- ) => Promise<LedgerGetAddressResult>;
- signPersonalMessage_async: (derivationPath: string, messageHex: string) => Promise<ECSignature>;
- signTransaction_async: (derivationPath: string, txHex: string) => Promise<ECSignatureString>;
- comm: LedgerCommunicationClient;
+ // shouldGetChainCode is defined as `true` instead of `boolean` because other types rely on the assumption
+ // that we get back the chain code and we don't have dependent types to express it properly
+ getAddress_async: (
+ derivationPath: string,
+ askForDeviceConfirmation: boolean,
+ shouldGetChainCode: true,
+ ) => Promise<LedgerGetAddressResult>;
+ signPersonalMessage_async: (derivationPath: string, messageHex: string) => Promise<ECSignature>;
+ signTransaction_async: (derivationPath: string, txHex: string) => Promise<ECSignatureString>;
+ comm: LedgerCommunicationClient;
}
export interface ECSignatureString {
- v: string;
- r: string;
- s: string;
+ v: string;
+ r: string;
+ s: string;
}
export interface ECSignature {
- v: number;
- r: string;
- s: string;
+ v: number;
+ r: string;
+ s: string;
}
export type LedgerEthereumClientFactoryAsync = () => Promise<LedgerEthereumClient>;
@@ -43,10 +43,10 @@ export type LedgerEthereumClientFactoryAsync = () => Promise<LedgerEthereumClien
* accountFetchingConfigs: configs related to fetching accounts from a Ledger
*/
export interface LedgerSubproviderConfigs {
- networkId: number;
- ledgerEthereumClientFactoryAsync: LedgerEthereumClientFactoryAsync;
- derivationPath?: string;
- accountFetchingConfigs?: AccountFetchingConfigs;
+ networkId: number;
+ ledgerEthereumClientFactoryAsync: LedgerEthereumClientFactoryAsync;
+ derivationPath?: string;
+ accountFetchingConfigs?: AccountFetchingConfigs;
}
/*
@@ -55,60 +55,60 @@ export interface LedgerSubproviderConfigs {
* before fetching their addresses
*/
export interface AccountFetchingConfigs {
- numAddressesToReturn?: number;
- shouldAskForOnDeviceConfirmation?: boolean;
+ numAddressesToReturn?: number;
+ shouldAskForOnDeviceConfirmation?: boolean;
}
export interface SignatureData {
- hash: string;
- r: string;
- s: string;
- v: number;
+ hash: string;
+ r: string;
+ s: string;
+ v: number;
}
export interface LedgerGetAddressResult {
- address: string;
- publicKey: string;
- chainCode: string;
+ address: string;
+ publicKey: string;
+ chainCode: string;
}
export interface LedgerWalletSubprovider {
- getPath: () => string;
- setPath: (path: string) => void;
- setPathIndex: (pathIndex: number) => void;
+ getPath: () => string;
+ setPath: (path: string) => void;
+ setPathIndex: (pathIndex: number) => void;
}
export interface PartialTxParams {
- nonce: string;
- gasPrice?: string;
- gas: string;
- to: string;
- from?: string;
- value?: string;
- data?: string;
- chainId: number; // EIP 155 chainId - mainnet: 1, ropsten: 3
+ nonce: string;
+ gasPrice?: string;
+ gas: string;
+ to: string;
+ from?: string;
+ value?: string;
+ data?: string;
+ chainId: number; // EIP 155 chainId - mainnet: 1, ropsten: 3
}
export type DoneCallback = (err?: Error) => void;
export interface JSONRPCPayload {
- params: any[];
- method: string;
+ params: any[];
+ method: string;
}
export interface LedgerCommunication {
- close_async: () => Promise<void>;
+ close_async: () => Promise<void>;
}
export interface ResponseWithTxParams {
- raw: string;
- tx: PartialTxParams;
+ raw: string;
+ tx: PartialTxParams;
}
export enum LedgerSubproviderErrors {
- TooOldLedgerFirmware = 'TOO_OLD_LEDGER_FIRMWARE',
- FromAddressMissingOrInvalid = 'FROM_ADDRESS_MISSING_OR_INVALID',
- DataMissingForSignPersonalMessage = 'DATA_MISSING_FOR_SIGN_PERSONAL_MESSAGE',
- SenderInvalidOrNotSupplied = 'SENDER_INVALID_OR_NOT_SUPPLIED',
- MultipleOpenConnectionsDisallowed = 'MULTIPLE_OPEN_CONNECTIONS_DISALLOWED',
+ TooOldLedgerFirmware = 'TOO_OLD_LEDGER_FIRMWARE',
+ FromAddressMissingOrInvalid = 'FROM_ADDRESS_MISSING_OR_INVALID',
+ DataMissingForSignPersonalMessage = 'DATA_MISSING_FOR_SIGN_PERSONAL_MESSAGE',
+ SenderInvalidOrNotSupplied = 'SENDER_INVALID_OR_NOT_SUPPLIED',
+ MultipleOpenConnectionsDisallowed = 'MULTIPLE_OPEN_CONNECTIONS_DISALLOWED',
}
diff --git a/packages/subproviders/test/chai_setup.ts b/packages/subproviders/test/chai_setup.ts
index 941a141d5..a281bab6c 100644
--- a/packages/subproviders/test/chai_setup.ts
+++ b/packages/subproviders/test/chai_setup.ts
@@ -3,9 +3,9 @@ import chaiAsPromised = require('chai-as-promised');
import * as dirtyChai from 'dirty-chai';
export const chaiSetup = {
- configure() {
- chai.config.includeStack = true;
- chai.use(dirtyChai);
- chai.use(chaiAsPromised);
- },
+ configure() {
+ chai.config.includeStack = true;
+ chai.use(dirtyChai);
+ chai.use(chaiAsPromised);
+ },
};
diff --git a/packages/subproviders/test/integration/ledger_subprovider_test.ts b/packages/subproviders/test/integration/ledger_subprovider_test.ts
index 6c1de46aa..628b532d7 100644
--- a/packages/subproviders/test/integration/ledger_subprovider_test.ts
+++ b/packages/subproviders/test/integration/ledger_subprovider_test.ts
@@ -17,168 +17,168 @@ const expect = chai.expect;
const TEST_RPC_ACCOUNT_0 = '0x5409ed021d9299bf6814279a6a1411a7e866a631';
describe('LedgerSubprovider', () => {
- let ledgerSubprovider: LedgerSubprovider;
- const networkId: number = 42;
- before(async () => {
- ledgerSubprovider = new LedgerSubprovider({
- networkId,
- ledgerEthereumClientFactoryAsync: ledgerEthereumNodeJsClientFactoryAsync,
- });
- });
- describe('direct method calls', () => {
- it('returns a list of accounts', async () => {
- const accounts = await ledgerSubprovider.getAccountsAsync();
- expect(accounts[0]).to.not.be.an('undefined');
- expect(accounts.length).to.be.equal(10);
- });
- it('signs a personal message', async () => {
- const data = ethUtils.bufferToHex(ethUtils.toBuffer('hello world'));
- const ecSignatureHex = await ledgerSubprovider.signPersonalMessageAsync(data);
- expect(ecSignatureHex.length).to.be.equal(132);
- expect(ecSignatureHex.substr(0, 2)).to.be.equal('0x');
- });
- it('signs a transaction', async () => {
- const tx = {
- nonce: '0x00',
- gas: '0x2710',
- to: '0x0000000000000000000000000000000000000000',
- value: '0x00',
- chainId: 3,
- };
- const txHex = await ledgerSubprovider.signTransactionAsync(tx);
- expect(txHex).to.be.equal(
- '0xf85f8080822710940000000000000000000000000000000000000000808077a088a95ef1378487bc82be558e82c8478baf840c545d5b887536bb1da63673a98ba0019f4a4b9a107d1e6752bf7f701e275f28c13791d6e76af895b07373462cefaa',
- );
- });
- });
- describe('calls through a provider', () => {
- let defaultProvider: Web3ProviderEngine;
- let ledgerProvider: Web3ProviderEngine;
- before(() => {
- ledgerProvider = new Web3ProviderEngine();
- ledgerProvider.addProvider(ledgerSubprovider);
- const httpProvider = new RpcSubprovider({
- rpcUrl: 'http://localhost:8545',
- });
- ledgerProvider.addProvider(httpProvider);
- ledgerProvider.start();
+ let ledgerSubprovider: LedgerSubprovider;
+ const networkId: number = 42;
+ before(async () => {
+ ledgerSubprovider = new LedgerSubprovider({
+ networkId,
+ ledgerEthereumClientFactoryAsync: ledgerEthereumNodeJsClientFactoryAsync,
+ });
+ });
+ describe('direct method calls', () => {
+ it('returns a list of accounts', async () => {
+ const accounts = await ledgerSubprovider.getAccountsAsync();
+ expect(accounts[0]).to.not.be.an('undefined');
+ expect(accounts.length).to.be.equal(10);
+ });
+ it('signs a personal message', async () => {
+ const data = ethUtils.bufferToHex(ethUtils.toBuffer('hello world'));
+ const ecSignatureHex = await ledgerSubprovider.signPersonalMessageAsync(data);
+ expect(ecSignatureHex.length).to.be.equal(132);
+ expect(ecSignatureHex.substr(0, 2)).to.be.equal('0x');
+ });
+ it('signs a transaction', async () => {
+ const tx = {
+ nonce: '0x00',
+ gas: '0x2710',
+ to: '0x0000000000000000000000000000000000000000',
+ value: '0x00',
+ chainId: 3,
+ };
+ const txHex = await ledgerSubprovider.signTransactionAsync(tx);
+ expect(txHex).to.be.equal(
+ '0xf85f8080822710940000000000000000000000000000000000000000808077a088a95ef1378487bc82be558e82c8478baf840c545d5b887536bb1da63673a98ba0019f4a4b9a107d1e6752bf7f701e275f28c13791d6e76af895b07373462cefaa',
+ );
+ });
+ });
+ describe('calls through a provider', () => {
+ let defaultProvider: Web3ProviderEngine;
+ let ledgerProvider: Web3ProviderEngine;
+ before(() => {
+ ledgerProvider = new Web3ProviderEngine();
+ ledgerProvider.addProvider(ledgerSubprovider);
+ const httpProvider = new RpcSubprovider({
+ rpcUrl: 'http://localhost:8545',
+ });
+ ledgerProvider.addProvider(httpProvider);
+ ledgerProvider.start();
- defaultProvider = new Web3ProviderEngine();
- defaultProvider.addProvider(httpProvider);
- defaultProvider.start();
- });
- it('returns a list of accounts', (done: DoneCallback) => {
- const payload = {
- jsonrpc: '2.0',
- method: 'eth_accounts',
- params: [],
- id: 1,
- };
- const callback = reportCallbackErrors(done)((err: Error, response: Web3.JSONRPCResponsePayload) => {
- expect(err).to.be.a('null');
- expect(response.result.length).to.be.equal(10);
- done();
- });
- ledgerProvider.sendAsync(payload, callback);
- });
- it('signs a personal message with eth_sign', (done: DoneCallback) => {
- (async () => {
- const messageHex = ethUtils.bufferToHex(ethUtils.toBuffer('hello world'));
- const accounts = await ledgerSubprovider.getAccountsAsync();
- const signer = accounts[0];
- const payload = {
- jsonrpc: '2.0',
- method: 'eth_sign',
- params: [signer, messageHex],
- id: 1,
- };
- const callback = reportCallbackErrors(done)((err: Error, response: Web3.JSONRPCResponsePayload) => {
- expect(err).to.be.a('null');
- expect(response.result.length).to.be.equal(132);
- expect(response.result.substr(0, 2)).to.be.equal('0x');
- done();
- });
- ledgerProvider.sendAsync(payload, callback);
- })().catch(done);
- });
- it('signs a personal message with personal_sign', (done: DoneCallback) => {
- (async () => {
- const messageHex = ethUtils.bufferToHex(ethUtils.toBuffer('hello world'));
- const accounts = await ledgerSubprovider.getAccountsAsync();
- const signer = accounts[0];
- const payload = {
- jsonrpc: '2.0',
- method: 'personal_sign',
- params: [messageHex, signer],
- id: 1,
- };
- const callback = reportCallbackErrors(done)((err: Error, response: Web3.JSONRPCResponsePayload) => {
- expect(err).to.be.a('null');
- expect(response.result.length).to.be.equal(132);
- expect(response.result.substr(0, 2)).to.be.equal('0x');
- done();
- });
- ledgerProvider.sendAsync(payload, callback);
- })().catch(done);
- });
- it('signs a transaction', (done: DoneCallback) => {
- const tx = {
- to: '0xafa3f8684e54059998bc3a7b0d2b0da075154d66',
- value: '0x00',
- };
- const payload = {
- jsonrpc: '2.0',
- method: 'eth_signTransaction',
- params: [tx],
- id: 1,
- };
- const callback = reportCallbackErrors(done)((err: Error, response: Web3.JSONRPCResponsePayload) => {
- expect(err).to.be.a('null');
- expect(response.result.raw.length).to.be.equal(206);
- expect(response.result.raw.substr(0, 2)).to.be.equal('0x');
- done();
- });
- ledgerProvider.sendAsync(payload, callback);
- });
- it('signs and sends a transaction', (done: DoneCallback) => {
- (async () => {
- const accounts = await ledgerSubprovider.getAccountsAsync();
+ defaultProvider = new Web3ProviderEngine();
+ defaultProvider.addProvider(httpProvider);
+ defaultProvider.start();
+ });
+ it('returns a list of accounts', (done: DoneCallback) => {
+ const payload = {
+ jsonrpc: '2.0',
+ method: 'eth_accounts',
+ params: [],
+ id: 1,
+ };
+ const callback = reportCallbackErrors(done)((err: Error, response: Web3.JSONRPCResponsePayload) => {
+ expect(err).to.be.a('null');
+ expect(response.result.length).to.be.equal(10);
+ done();
+ });
+ ledgerProvider.sendAsync(payload, callback);
+ });
+ it('signs a personal message with eth_sign', (done: DoneCallback) => {
+ (async () => {
+ const messageHex = ethUtils.bufferToHex(ethUtils.toBuffer('hello world'));
+ const accounts = await ledgerSubprovider.getAccountsAsync();
+ const signer = accounts[0];
+ const payload = {
+ jsonrpc: '2.0',
+ method: 'eth_sign',
+ params: [signer, messageHex],
+ id: 1,
+ };
+ const callback = reportCallbackErrors(done)((err: Error, response: Web3.JSONRPCResponsePayload) => {
+ expect(err).to.be.a('null');
+ expect(response.result.length).to.be.equal(132);
+ expect(response.result.substr(0, 2)).to.be.equal('0x');
+ done();
+ });
+ ledgerProvider.sendAsync(payload, callback);
+ })().catch(done);
+ });
+ it('signs a personal message with personal_sign', (done: DoneCallback) => {
+ (async () => {
+ const messageHex = ethUtils.bufferToHex(ethUtils.toBuffer('hello world'));
+ const accounts = await ledgerSubprovider.getAccountsAsync();
+ const signer = accounts[0];
+ const payload = {
+ jsonrpc: '2.0',
+ method: 'personal_sign',
+ params: [messageHex, signer],
+ id: 1,
+ };
+ const callback = reportCallbackErrors(done)((err: Error, response: Web3.JSONRPCResponsePayload) => {
+ expect(err).to.be.a('null');
+ expect(response.result.length).to.be.equal(132);
+ expect(response.result.substr(0, 2)).to.be.equal('0x');
+ done();
+ });
+ ledgerProvider.sendAsync(payload, callback);
+ })().catch(done);
+ });
+ it('signs a transaction', (done: DoneCallback) => {
+ const tx = {
+ to: '0xafa3f8684e54059998bc3a7b0d2b0da075154d66',
+ value: '0x00',
+ };
+ const payload = {
+ jsonrpc: '2.0',
+ method: 'eth_signTransaction',
+ params: [tx],
+ id: 1,
+ };
+ const callback = reportCallbackErrors(done)((err: Error, response: Web3.JSONRPCResponsePayload) => {
+ expect(err).to.be.a('null');
+ expect(response.result.raw.length).to.be.equal(206);
+ expect(response.result.raw.substr(0, 2)).to.be.equal('0x');
+ done();
+ });
+ ledgerProvider.sendAsync(payload, callback);
+ });
+ it('signs and sends a transaction', (done: DoneCallback) => {
+ (async () => {
+ const accounts = await ledgerSubprovider.getAccountsAsync();
- // Give first account on Ledger sufficient ETH to complete tx send
- let tx = {
- to: accounts[0],
- from: TEST_RPC_ACCOUNT_0,
- value: '0x8ac7230489e80000', // 10 ETH
- };
- let payload = {
- jsonrpc: '2.0',
- method: 'eth_sendTransaction',
- params: [tx],
- id: 1,
- };
- await promisify(defaultProvider.sendAsync, defaultProvider)(payload);
+ // Give first account on Ledger sufficient ETH to complete tx send
+ let tx = {
+ to: accounts[0],
+ from: TEST_RPC_ACCOUNT_0,
+ value: '0x8ac7230489e80000', // 10 ETH
+ };
+ let payload = {
+ jsonrpc: '2.0',
+ method: 'eth_sendTransaction',
+ params: [tx],
+ id: 1,
+ };
+ await promisify(defaultProvider.sendAsync, defaultProvider)(payload);
- // Send transaction from Ledger
- tx = {
- to: '0xafa3f8684e54059998bc3a7b0d2b0da075154d66',
- from: accounts[0],
- value: '0xde0b6b3a7640000',
- };
- payload = {
- jsonrpc: '2.0',
- method: 'eth_sendTransaction',
- params: [tx],
- id: 1,
- };
- const callback = reportCallbackErrors(done)((err: Error, response: Web3.JSONRPCResponsePayload) => {
- expect(err).to.be.a('null');
- const result = response.result.result;
- expect(result.length).to.be.equal(66);
- expect(result.substr(0, 2)).to.be.equal('0x');
- done();
- });
- ledgerProvider.sendAsync(payload, callback);
- })().catch(done);
- });
- });
+ // Send transaction from Ledger
+ tx = {
+ to: '0xafa3f8684e54059998bc3a7b0d2b0da075154d66',
+ from: accounts[0],
+ value: '0xde0b6b3a7640000',
+ };
+ payload = {
+ jsonrpc: '2.0',
+ method: 'eth_sendTransaction',
+ params: [tx],
+ id: 1,
+ };
+ const callback = reportCallbackErrors(done)((err: Error, response: Web3.JSONRPCResponsePayload) => {
+ expect(err).to.be.a('null');
+ const result = response.result.result;
+ expect(result.length).to.be.equal(66);
+ expect(result.substr(0, 2)).to.be.equal('0x');
+ done();
+ });
+ ledgerProvider.sendAsync(payload, callback);
+ })().catch(done);
+ });
+ });
});
diff --git a/packages/subproviders/test/unit/ledger_subprovider_test.ts b/packages/subproviders/test/unit/ledger_subprovider_test.ts
index 101b6b906..1c70dd3a6 100644
--- a/packages/subproviders/test/unit/ledger_subprovider_test.ts
+++ b/packages/subproviders/test/unit/ledger_subprovider_test.ts
@@ -15,230 +15,230 @@ const expect = chai.expect;
const FAKE_ADDRESS = '0xb088a3bc93f71b4de97b9de773e9647645983688';
describe('LedgerSubprovider', () => {
- const networkId: number = 42;
- let ledgerSubprovider: LedgerSubprovider;
- before(async () => {
- const ledgerEthereumClientFactoryAsync = async () => {
- // tslint:disable:no-object-literal-type-assertion
- const ledgerEthClient = {
- getAddress_async: async () => {
- const publicKey =
- '04f428290f4c5ed6a198f71b8205f488141dbb3f0840c923bbfa798ecbee6370986c03b5575d94d506772fb48a6a44e345e4ebd4f028a6f609c44b655d6d3e71a1';
- const chainCode = 'ac055a5537c0c7e9e02d14a197cad6b857836da2a12043b46912a37d959b5ae8';
- const address = '0xBa388BA5e5EEF2c6cE42d831c2B3A28D3c99bdB1';
- return {
- publicKey,
- address,
- chainCode,
- };
- },
- signPersonalMessage_async: async () => {
- const ecSignature = {
- v: 28,
- r: 'a6cc284bff14b42bdf5e9286730c152be91719d478605ec46b3bebcd0ae49148',
- s: '0652a1a7b742ceb0213d1e744316e285f41f878d8af0b8e632cbca4c279132d0',
- };
- return ecSignature;
- },
- signTransaction_async: async (derivationPath: string, txHex: string) => {
- const ecSignature = {
- v: '77',
- r: '88a95ef1378487bc82be558e82c8478baf840c545d5b887536bb1da63673a98b',
- s: '019f4a4b9a107d1e6752bf7f701e275f28c13791d6e76af895b07373462cefaa',
- };
- return ecSignature;
- },
- comm: {
- close_async: _.noop,
- } as LedgerCommunicationClient,
- };
- // tslint:enable:no-object-literal-type-assertion
- return ledgerEthClient;
- };
- ledgerSubprovider = new LedgerSubprovider({
- networkId,
- ledgerEthereumClientFactoryAsync,
- });
- });
- describe('direct method calls', () => {
- describe('success cases', () => {
- it('returns a list of accounts', async () => {
- const accounts = await ledgerSubprovider.getAccountsAsync();
- expect(accounts[0]).to.be.equal(FAKE_ADDRESS);
- expect(accounts.length).to.be.equal(10);
- });
- it('signs a personal message', async () => {
- const data = ethUtils.bufferToHex(ethUtils.toBuffer('hello world'));
- const ecSignatureHex = await ledgerSubprovider.signPersonalMessageAsync(data);
- expect(ecSignatureHex).to.be.equal(
- '0xa6cc284bff14b42bdf5e9286730c152be91719d478605ec46b3bebcd0ae491480652a1a7b742ceb0213d1e744316e285f41f878d8af0b8e632cbca4c279132d001',
- );
- });
- });
- describe('failure cases', () => {
- it('cannot open multiple simultaneous connections to the Ledger device', async () => {
- const data = ethUtils.bufferToHex(ethUtils.toBuffer('hello world'));
- return expect(
- Promise.all([
- ledgerSubprovider.getAccountsAsync(),
- ledgerSubprovider.signPersonalMessageAsync(data),
- ]),
- ).to.be.rejectedWith(LedgerSubproviderErrors.MultipleOpenConnectionsDisallowed);
- });
- });
- });
- describe('calls through a provider', () => {
- let provider: Web3ProviderEngine;
- before(() => {
- provider = new Web3ProviderEngine();
- provider.addProvider(ledgerSubprovider);
- const httpProvider = new RpcSubprovider({
- rpcUrl: 'http://localhost:8545',
- });
- provider.addProvider(httpProvider);
- provider.start();
- });
- describe('success cases', () => {
- it('returns a list of accounts', (done: DoneCallback) => {
- const payload = {
- jsonrpc: '2.0',
- method: 'eth_accounts',
- params: [],
- id: 1,
- };
- const callback = reportCallbackErrors(done)((err: Error, response: Web3.JSONRPCResponsePayload) => {
- expect(err).to.be.a('null');
- expect(response.result.length).to.be.equal(10);
- expect(response.result[0]).to.be.equal(FAKE_ADDRESS);
- done();
- });
- provider.sendAsync(payload, callback);
- });
- it('signs a personal message with eth_sign', (done: DoneCallback) => {
- const messageHex = ethUtils.bufferToHex(ethUtils.toBuffer('hello world'));
- const payload = {
- jsonrpc: '2.0',
- method: 'eth_sign',
- params: ['0x0000000000000000000000000000000000000000', messageHex],
- id: 1,
- };
- const callback = reportCallbackErrors(done)((err: Error, response: Web3.JSONRPCResponsePayload) => {
- expect(err).to.be.a('null');
- expect(response.result).to.be.equal(
- '0xa6cc284bff14b42bdf5e9286730c152be91719d478605ec46b3bebcd0ae491480652a1a7b742ceb0213d1e744316e285f41f878d8af0b8e632cbca4c279132d001',
- );
- done();
- });
- provider.sendAsync(payload, callback);
- });
- it('signs a personal message with personal_sign', (done: DoneCallback) => {
- const messageHex = ethUtils.bufferToHex(ethUtils.toBuffer('hello world'));
- const payload = {
- jsonrpc: '2.0',
- method: 'personal_sign',
- params: [messageHex, '0x0000000000000000000000000000000000000000'],
- id: 1,
- };
- const callback = reportCallbackErrors(done)((err: Error, response: Web3.JSONRPCResponsePayload) => {
- expect(err).to.be.a('null');
- expect(response.result).to.be.equal(
- '0xa6cc284bff14b42bdf5e9286730c152be91719d478605ec46b3bebcd0ae491480652a1a7b742ceb0213d1e744316e285f41f878d8af0b8e632cbca4c279132d001',
- );
- done();
- });
- provider.sendAsync(payload, callback);
- });
- it('signs a transaction', (done: DoneCallback) => {
- const tx = {
- to: '0xafa3f8684e54059998bc3a7b0d2b0da075154d66',
- value: '0x00',
- gasPrice: '0x00',
- nonce: '0x00',
- gas: '0x00',
- };
- const payload = {
- jsonrpc: '2.0',
- method: 'eth_signTransaction',
- params: [tx],
- id: 1,
- };
- const callback = reportCallbackErrors(done)((err: Error, response: Web3.JSONRPCResponsePayload) => {
- expect(err).to.be.a('null');
- expect(response.result.raw.length).to.be.equal(192);
- expect(response.result.raw.substr(0, 2)).to.be.equal('0x');
- done();
- });
- provider.sendAsync(payload, callback);
- });
- });
- describe('failure cases', () => {
- it('should throw if `data` param not hex when calling eth_sign', (done: DoneCallback) => {
- const nonHexMessage = 'hello world';
- const payload = {
- jsonrpc: '2.0',
- method: 'eth_sign',
- params: ['0x0000000000000000000000000000000000000000', nonHexMessage],
- id: 1,
- };
- const callback = reportCallbackErrors(done)((err: Error, response: Web3.JSONRPCResponsePayload) => {
- expect(err).to.not.be.a('null');
- expect(err.message).to.be.equal('Expected data to be of type HexString, encountered: hello world');
- done();
- });
- provider.sendAsync(payload, callback);
- });
- it('should throw if `data` param not hex when calling personal_sign', (done: DoneCallback) => {
- const nonHexMessage = 'hello world';
- const payload = {
- jsonrpc: '2.0',
- method: 'personal_sign',
- params: [nonHexMessage, '0x0000000000000000000000000000000000000000'],
- id: 1,
- };
- const callback = reportCallbackErrors(done)((err: Error, response: Web3.JSONRPCResponsePayload) => {
- expect(err).to.not.be.a('null');
- expect(err.message).to.be.equal('Expected data to be of type HexString, encountered: hello world');
- done();
- });
- provider.sendAsync(payload, callback);
- });
- it('should throw if `from` param missing when calling eth_sendTransaction', (done: DoneCallback) => {
- const tx = {
- to: '0xafa3f8684e54059998bc3a7b0d2b0da075154d66',
- value: '0xde0b6b3a7640000',
- };
- const payload = {
- jsonrpc: '2.0',
- method: 'eth_sendTransaction',
- params: [tx],
- id: 1,
- };
- const callback = reportCallbackErrors(done)((err: Error, response: Web3.JSONRPCResponsePayload) => {
- expect(err).to.not.be.a('null');
- expect(err.message).to.be.equal(LedgerSubproviderErrors.SenderInvalidOrNotSupplied);
- done();
- });
- provider.sendAsync(payload, callback);
- });
- it('should throw if `from` param invalid address when calling eth_sendTransaction', (done: DoneCallback) => {
- const tx = {
- to: '0xafa3f8684e54059998bc3a7b0d2b0da075154d66',
- from: '0xIncorrectEthereumAddress',
- value: '0xde0b6b3a7640000',
- };
- const payload = {
- jsonrpc: '2.0',
- method: 'eth_sendTransaction',
- params: [tx],
- id: 1,
- };
- const callback = reportCallbackErrors(done)((err: Error, response: Web3.JSONRPCResponsePayload) => {
- expect(err).to.not.be.a('null');
- expect(err.message).to.be.equal(LedgerSubproviderErrors.SenderInvalidOrNotSupplied);
- done();
- });
- provider.sendAsync(payload, callback);
- });
- });
- });
+ const networkId: number = 42;
+ let ledgerSubprovider: LedgerSubprovider;
+ before(async () => {
+ const ledgerEthereumClientFactoryAsync = async () => {
+ // tslint:disable:no-object-literal-type-assertion
+ const ledgerEthClient = {
+ getAddress_async: async () => {
+ const publicKey =
+ '04f428290f4c5ed6a198f71b8205f488141dbb3f0840c923bbfa798ecbee6370986c03b5575d94d506772fb48a6a44e345e4ebd4f028a6f609c44b655d6d3e71a1';
+ const chainCode = 'ac055a5537c0c7e9e02d14a197cad6b857836da2a12043b46912a37d959b5ae8';
+ const address = '0xBa388BA5e5EEF2c6cE42d831c2B3A28D3c99bdB1';
+ return {
+ publicKey,
+ address,
+ chainCode,
+ };
+ },
+ signPersonalMessage_async: async () => {
+ const ecSignature = {
+ v: 28,
+ r: 'a6cc284bff14b42bdf5e9286730c152be91719d478605ec46b3bebcd0ae49148',
+ s: '0652a1a7b742ceb0213d1e744316e285f41f878d8af0b8e632cbca4c279132d0',
+ };
+ return ecSignature;
+ },
+ signTransaction_async: async (derivationPath: string, txHex: string) => {
+ const ecSignature = {
+ v: '77',
+ r: '88a95ef1378487bc82be558e82c8478baf840c545d5b887536bb1da63673a98b',
+ s: '019f4a4b9a107d1e6752bf7f701e275f28c13791d6e76af895b07373462cefaa',
+ };
+ return ecSignature;
+ },
+ comm: {
+ close_async: _.noop,
+ } as LedgerCommunicationClient,
+ };
+ // tslint:enable:no-object-literal-type-assertion
+ return ledgerEthClient;
+ };
+ ledgerSubprovider = new LedgerSubprovider({
+ networkId,
+ ledgerEthereumClientFactoryAsync,
+ });
+ });
+ describe('direct method calls', () => {
+ describe('success cases', () => {
+ it('returns a list of accounts', async () => {
+ const accounts = await ledgerSubprovider.getAccountsAsync();
+ expect(accounts[0]).to.be.equal(FAKE_ADDRESS);
+ expect(accounts.length).to.be.equal(10);
+ });
+ it('signs a personal message', async () => {
+ const data = ethUtils.bufferToHex(ethUtils.toBuffer('hello world'));
+ const ecSignatureHex = await ledgerSubprovider.signPersonalMessageAsync(data);
+ expect(ecSignatureHex).to.be.equal(
+ '0xa6cc284bff14b42bdf5e9286730c152be91719d478605ec46b3bebcd0ae491480652a1a7b742ceb0213d1e744316e285f41f878d8af0b8e632cbca4c279132d001',
+ );
+ });
+ });
+ describe('failure cases', () => {
+ it('cannot open multiple simultaneous connections to the Ledger device', async () => {
+ const data = ethUtils.bufferToHex(ethUtils.toBuffer('hello world'));
+ return expect(
+ Promise.all([
+ ledgerSubprovider.getAccountsAsync(),
+ ledgerSubprovider.signPersonalMessageAsync(data),
+ ]),
+ ).to.be.rejectedWith(LedgerSubproviderErrors.MultipleOpenConnectionsDisallowed);
+ });
+ });
+ });
+ describe('calls through a provider', () => {
+ let provider: Web3ProviderEngine;
+ before(() => {
+ provider = new Web3ProviderEngine();
+ provider.addProvider(ledgerSubprovider);
+ const httpProvider = new RpcSubprovider({
+ rpcUrl: 'http://localhost:8545',
+ });
+ provider.addProvider(httpProvider);
+ provider.start();
+ });
+ describe('success cases', () => {
+ it('returns a list of accounts', (done: DoneCallback) => {
+ const payload = {
+ jsonrpc: '2.0',
+ method: 'eth_accounts',
+ params: [],
+ id: 1,
+ };
+ const callback = reportCallbackErrors(done)((err: Error, response: Web3.JSONRPCResponsePayload) => {
+ expect(err).to.be.a('null');
+ expect(response.result.length).to.be.equal(10);
+ expect(response.result[0]).to.be.equal(FAKE_ADDRESS);
+ done();
+ });
+ provider.sendAsync(payload, callback);
+ });
+ it('signs a personal message with eth_sign', (done: DoneCallback) => {
+ const messageHex = ethUtils.bufferToHex(ethUtils.toBuffer('hello world'));
+ const payload = {
+ jsonrpc: '2.0',
+ method: 'eth_sign',
+ params: ['0x0000000000000000000000000000000000000000', messageHex],
+ id: 1,
+ };
+ const callback = reportCallbackErrors(done)((err: Error, response: Web3.JSONRPCResponsePayload) => {
+ expect(err).to.be.a('null');
+ expect(response.result).to.be.equal(
+ '0xa6cc284bff14b42bdf5e9286730c152be91719d478605ec46b3bebcd0ae491480652a1a7b742ceb0213d1e744316e285f41f878d8af0b8e632cbca4c279132d001',
+ );
+ done();
+ });
+ provider.sendAsync(payload, callback);
+ });
+ it('signs a personal message with personal_sign', (done: DoneCallback) => {
+ const messageHex = ethUtils.bufferToHex(ethUtils.toBuffer('hello world'));
+ const payload = {
+ jsonrpc: '2.0',
+ method: 'personal_sign',
+ params: [messageHex, '0x0000000000000000000000000000000000000000'],
+ id: 1,
+ };
+ const callback = reportCallbackErrors(done)((err: Error, response: Web3.JSONRPCResponsePayload) => {
+ expect(err).to.be.a('null');
+ expect(response.result).to.be.equal(
+ '0xa6cc284bff14b42bdf5e9286730c152be91719d478605ec46b3bebcd0ae491480652a1a7b742ceb0213d1e744316e285f41f878d8af0b8e632cbca4c279132d001',
+ );
+ done();
+ });
+ provider.sendAsync(payload, callback);
+ });
+ it('signs a transaction', (done: DoneCallback) => {
+ const tx = {
+ to: '0xafa3f8684e54059998bc3a7b0d2b0da075154d66',
+ value: '0x00',
+ gasPrice: '0x00',
+ nonce: '0x00',
+ gas: '0x00',
+ };
+ const payload = {
+ jsonrpc: '2.0',
+ method: 'eth_signTransaction',
+ params: [tx],
+ id: 1,
+ };
+ const callback = reportCallbackErrors(done)((err: Error, response: Web3.JSONRPCResponsePayload) => {
+ expect(err).to.be.a('null');
+ expect(response.result.raw.length).to.be.equal(192);
+ expect(response.result.raw.substr(0, 2)).to.be.equal('0x');
+ done();
+ });
+ provider.sendAsync(payload, callback);
+ });
+ });
+ describe('failure cases', () => {
+ it('should throw if `data` param not hex when calling eth_sign', (done: DoneCallback) => {
+ const nonHexMessage = 'hello world';
+ const payload = {
+ jsonrpc: '2.0',
+ method: 'eth_sign',
+ params: ['0x0000000000000000000000000000000000000000', nonHexMessage],
+ id: 1,
+ };
+ const callback = reportCallbackErrors(done)((err: Error, response: Web3.JSONRPCResponsePayload) => {
+ expect(err).to.not.be.a('null');
+ expect(err.message).to.be.equal('Expected data to be of type HexString, encountered: hello world');
+ done();
+ });
+ provider.sendAsync(payload, callback);
+ });
+ it('should throw if `data` param not hex when calling personal_sign', (done: DoneCallback) => {
+ const nonHexMessage = 'hello world';
+ const payload = {
+ jsonrpc: '2.0',
+ method: 'personal_sign',
+ params: [nonHexMessage, '0x0000000000000000000000000000000000000000'],
+ id: 1,
+ };
+ const callback = reportCallbackErrors(done)((err: Error, response: Web3.JSONRPCResponsePayload) => {
+ expect(err).to.not.be.a('null');
+ expect(err.message).to.be.equal('Expected data to be of type HexString, encountered: hello world');
+ done();
+ });
+ provider.sendAsync(payload, callback);
+ });
+ it('should throw if `from` param missing when calling eth_sendTransaction', (done: DoneCallback) => {
+ const tx = {
+ to: '0xafa3f8684e54059998bc3a7b0d2b0da075154d66',
+ value: '0xde0b6b3a7640000',
+ };
+ const payload = {
+ jsonrpc: '2.0',
+ method: 'eth_sendTransaction',
+ params: [tx],
+ id: 1,
+ };
+ const callback = reportCallbackErrors(done)((err: Error, response: Web3.JSONRPCResponsePayload) => {
+ expect(err).to.not.be.a('null');
+ expect(err.message).to.be.equal(LedgerSubproviderErrors.SenderInvalidOrNotSupplied);
+ done();
+ });
+ provider.sendAsync(payload, callback);
+ });
+ it('should throw if `from` param invalid address when calling eth_sendTransaction', (done: DoneCallback) => {
+ const tx = {
+ to: '0xafa3f8684e54059998bc3a7b0d2b0da075154d66',
+ from: '0xIncorrectEthereumAddress',
+ value: '0xde0b6b3a7640000',
+ };
+ const payload = {
+ jsonrpc: '2.0',
+ method: 'eth_sendTransaction',
+ params: [tx],
+ id: 1,
+ };
+ const callback = reportCallbackErrors(done)((err: Error, response: Web3.JSONRPCResponsePayload) => {
+ expect(err).to.not.be.a('null');
+ expect(err.message).to.be.equal(LedgerSubproviderErrors.SenderInvalidOrNotSupplied);
+ done();
+ });
+ provider.sendAsync(payload, callback);
+ });
+ });
+ });
});
diff --git a/packages/subproviders/test/unit/redundant_rpc_subprovider_test.ts b/packages/subproviders/test/unit/redundant_rpc_subprovider_test.ts
index defb741dd..c3170745c 100644
--- a/packages/subproviders/test/unit/redundant_rpc_subprovider_test.ts
+++ b/packages/subproviders/test/unit/redundant_rpc_subprovider_test.ts
@@ -12,45 +12,45 @@ const expect = chai.expect;
chaiSetup.configure();
describe('RedundantRpcSubprovider', () => {
- let provider: Web3ProviderEngine;
- it('succeeds when supplied a healthy endpoint', (done: DoneCallback) => {
- provider = new Web3ProviderEngine();
- const endpoints = ['http://localhost:8545'];
- const redundantSubprovider = new RedundantRPCSubprovider(endpoints);
- provider.addProvider(redundantSubprovider);
- provider.start();
+ let provider: Web3ProviderEngine;
+ it('succeeds when supplied a healthy endpoint', (done: DoneCallback) => {
+ provider = new Web3ProviderEngine();
+ const endpoints = ['http://localhost:8545'];
+ const redundantSubprovider = new RedundantRPCSubprovider(endpoints);
+ provider.addProvider(redundantSubprovider);
+ provider.start();
- const payload = {
- jsonrpc: '2.0',
- method: 'eth_accounts',
- params: [],
- id: 1,
- };
- const callback = reportCallbackErrors(done)((err: Error, response: Web3.JSONRPCResponsePayload) => {
- expect(err).to.be.a('null');
- expect(response.result.length).to.be.equal(10);
- done();
- });
- provider.sendAsync(payload, callback);
- });
- it('succeeds when supplied at least one healthy endpoint', (done: DoneCallback) => {
- provider = new Web3ProviderEngine();
- const endpoints = ['http://does-not-exist:3000', 'http://localhost:8545'];
- const redundantSubprovider = new RedundantRPCSubprovider(endpoints);
- provider.addProvider(redundantSubprovider);
- provider.start();
+ const payload = {
+ jsonrpc: '2.0',
+ method: 'eth_accounts',
+ params: [],
+ id: 1,
+ };
+ const callback = reportCallbackErrors(done)((err: Error, response: Web3.JSONRPCResponsePayload) => {
+ expect(err).to.be.a('null');
+ expect(response.result.length).to.be.equal(10);
+ done();
+ });
+ provider.sendAsync(payload, callback);
+ });
+ it('succeeds when supplied at least one healthy endpoint', (done: DoneCallback) => {
+ provider = new Web3ProviderEngine();
+ const endpoints = ['http://does-not-exist:3000', 'http://localhost:8545'];
+ const redundantSubprovider = new RedundantRPCSubprovider(endpoints);
+ provider.addProvider(redundantSubprovider);
+ provider.start();
- const payload = {
- jsonrpc: '2.0',
- method: 'eth_accounts',
- params: [],
- id: 1,
- };
- const callback = reportCallbackErrors(done)((err: Error, response: Web3.JSONRPCResponsePayload) => {
- expect(err).to.be.a('null');
- expect(response.result.length).to.be.equal(10);
- done();
- });
- provider.sendAsync(payload, callback);
- });
+ const payload = {
+ jsonrpc: '2.0',
+ method: 'eth_accounts',
+ params: [],
+ id: 1,
+ };
+ const callback = reportCallbackErrors(done)((err: Error, response: Web3.JSONRPCResponsePayload) => {
+ expect(err).to.be.a('null');
+ expect(response.result.length).to.be.equal(10);
+ done();
+ });
+ provider.sendAsync(payload, callback);
+ });
});
diff --git a/packages/subproviders/test/utils/report_callback_errors.ts b/packages/subproviders/test/utils/report_callback_errors.ts
index e6aadfa84..8a8f4d966 100644
--- a/packages/subproviders/test/utils/report_callback_errors.ts
+++ b/packages/subproviders/test/utils/report_callback_errors.ts
@@ -1,14 +1,14 @@
import { DoneCallback } from '../../src/types';
export const reportCallbackErrors = (done: DoneCallback) => {
- return (f: (...args: any[]) => void) => {
- const wrapped = async (...args: any[]) => {
- try {
- f(...args);
- } catch (err) {
- done(err);
- }
- };
- return wrapped;
- };
+ return (f: (...args: any[]) => void) => {
+ const wrapped = async (...args: any[]) => {
+ try {
+ f(...args);
+ } catch (err) {
+ done(err);
+ }
+ };
+ return wrapped;
+ };
};
diff --git a/packages/subproviders/tsconfig.json b/packages/subproviders/tsconfig.json
index 4d92e88a5..9a65a0a97 100644
--- a/packages/subproviders/tsconfig.json
+++ b/packages/subproviders/tsconfig.json
@@ -1,15 +1,15 @@
{
- "extends": "../../tsconfig",
- "compilerOptions": {
- "outDir": "lib"
- },
- "include": [
- "./src/**/*",
- "./test/**/*",
- "../../node_modules/chai-typescript-typings/index.d.ts",
- "../../node_modules/web3-typescript-typings/index.d.ts",
- "../../node_modules/types-bn/index.d.ts",
- "../../node_modules/types-ethereumjs-util/index.d.ts",
- "../../node_modules/chai-as-promised-typescript-typings/index.d.ts"
- ]
+ "extends": "../../tsconfig",
+ "compilerOptions": {
+ "outDir": "lib"
+ },
+ "include": [
+ "./src/**/*",
+ "./test/**/*",
+ "../../node_modules/chai-typescript-typings/index.d.ts",
+ "../../node_modules/web3-typescript-typings/index.d.ts",
+ "../../node_modules/types-bn/index.d.ts",
+ "../../node_modules/types-ethereumjs-util/index.d.ts",
+ "../../node_modules/chai-as-promised-typescript-typings/index.d.ts"
+ ]
}
diff --git a/packages/subproviders/tslint.json b/packages/subproviders/tslint.json
index e63054bfc..ffaefe83a 100644
--- a/packages/subproviders/tslint.json
+++ b/packages/subproviders/tslint.json
@@ -1,3 +1,3 @@
{
- "extends": ["@0xproject/tslint-config"]
+ "extends": ["@0xproject/tslint-config"]
}
diff --git a/packages/testnet-faucets/README.md b/packages/testnet-faucets/README.md
index 36322e76e..a52d16a00 100644
--- a/packages/testnet-faucets/README.md
+++ b/packages/testnet-faucets/README.md
@@ -63,26 +63,26 @@ Returns a JSON payload describing the state of the queues for each network. For
```json
{
- "3": {
- "ether": {
- "full": false,
- "size": 4
- },
- "zrx": {
- "full": false,
- "size": 6
- }
- },
- "42": {
- "ether": {
- "full": false,
- "size": 8
- },
- "zrx": {
- "full": false,
- "size": 20
- }
- }
+ "3": {
+ "ether": {
+ "full": false,
+ "size": 4
+ },
+ "zrx": {
+ "full": false,
+ "size": 6
+ }
+ },
+ "42": {
+ "ether": {
+ "full": false,
+ "size": 8
+ },
+ "zrx": {
+ "full": false,
+ "size": 20
+ }
+ }
}
```
diff --git a/packages/testnet-faucets/package.json b/packages/testnet-faucets/package.json
index 4f8bcfdcf..6c27df0d5 100644
--- a/packages/testnet-faucets/package.json
+++ b/packages/testnet-faucets/package.json
@@ -1,43 +1,43 @@
{
- "private": true,
- "name": "@0xproject/testnet-faucets",
- "version": "1.0.7",
- "description": "A faucet micro-service that dispenses test ERC20 tokens or Ether",
- "main": "server.js",
- "scripts": {
- "build": "node ../../node_modules/gulp/bin/gulp.js build",
- "dev": "node ../../node_modules/gulp/bin/gulp.js run",
- "start": "node ./bin/server.js",
- "lint": "tslint --project . 'src/**/*.ts'",
- "clean": "shx rm -rf bin"
- },
- "author": "Fabio Berger",
- "license": "Apache-2.0",
- "dependencies": {
- "0x.js": "^0.30.2",
- "@0xproject/utils": "^0.2.2",
- "body-parser": "^1.17.1",
- "ethereumjs-tx": "^1.3.3",
- "express": "^4.15.2",
- "lodash": "^4.17.4",
- "rollbar": "^0.6.5",
- "web3": "^0.20.0",
- "web3-provider-engine": "^13.0.1"
- },
- "devDependencies": {
- "@0xproject/tslint-config": "^0.4.4",
- "@types/body-parser": "^1.16.1",
- "@types/express": "^4.0.35",
- "@types/lodash": "^4.14.86",
- "awesome-typescript-loader": "^3.1.3",
- "gulp": "^3.9.1",
- "nodemon": "^1.11.0",
- "shx": "^0.2.2",
- "source-map-loader": "^0.1.6",
- "tslint": "5.8.0",
- "typescript": "~2.6.1",
- "web3-typescript-typings": "^0.9.6",
- "webpack": "^3.1.0",
- "webpack-node-externals": "^1.6.0"
- }
+ "private": true,
+ "name": "@0xproject/testnet-faucets",
+ "version": "1.0.7",
+ "description": "A faucet micro-service that dispenses test ERC20 tokens or Ether",
+ "main": "server.js",
+ "scripts": {
+ "build": "node ../../node_modules/gulp/bin/gulp.js build",
+ "dev": "node ../../node_modules/gulp/bin/gulp.js run",
+ "start": "node ./bin/server.js",
+ "lint": "tslint --project . 'src/**/*.ts'",
+ "clean": "shx rm -rf bin"
+ },
+ "author": "Fabio Berger",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "0x.js": "^0.30.2",
+ "@0xproject/utils": "^0.2.2",
+ "body-parser": "^1.17.1",
+ "ethereumjs-tx": "^1.3.3",
+ "express": "^4.15.2",
+ "lodash": "^4.17.4",
+ "rollbar": "^0.6.5",
+ "web3": "^0.20.0",
+ "web3-provider-engine": "^13.0.1"
+ },
+ "devDependencies": {
+ "@0xproject/tslint-config": "^0.4.4",
+ "@types/body-parser": "^1.16.1",
+ "@types/express": "^4.0.35",
+ "@types/lodash": "^4.14.86",
+ "awesome-typescript-loader": "^3.1.3",
+ "gulp": "^3.9.1",
+ "nodemon": "^1.11.0",
+ "shx": "^0.2.2",
+ "source-map-loader": "^0.1.6",
+ "tslint": "5.8.0",
+ "typescript": "~2.6.1",
+ "web3-typescript-typings": "^0.9.6",
+ "webpack": "^3.1.0",
+ "webpack-node-externals": "^1.6.0"
+ }
}
diff --git a/packages/testnet-faucets/src/ts/configs.ts b/packages/testnet-faucets/src/ts/configs.ts
index 038df5322..038c8e22a 100644
--- a/packages/testnet-faucets/src/ts/configs.ts
+++ b/packages/testnet-faucets/src/ts/configs.ts
@@ -1,7 +1,7 @@
export const configs = {
- DISPENSER_ADDRESS: (process.env.DISPENSER_ADDRESS as string).toLowerCase(),
- DISPENSER_PRIVATE_KEY: process.env.DISPENSER_PRIVATE_KEY,
- ENVIRONMENT: process.env.FAUCET_ENVIRONMENT,
- INFURA_API_KEY: process.env.INFURA_API_KEY,
- ROLLBAR_ACCESS_KEY: process.env.FAUCET_ROLLBAR_ACCESS_KEY,
+ DISPENSER_ADDRESS: (process.env.DISPENSER_ADDRESS as string).toLowerCase(),
+ DISPENSER_PRIVATE_KEY: process.env.DISPENSER_PRIVATE_KEY,
+ ENVIRONMENT: process.env.FAUCET_ENVIRONMENT,
+ INFURA_API_KEY: process.env.INFURA_API_KEY,
+ ROLLBAR_ACCESS_KEY: process.env.FAUCET_ROLLBAR_ACCESS_KEY,
};
diff --git a/packages/testnet-faucets/src/ts/error_reporter.ts b/packages/testnet-faucets/src/ts/error_reporter.ts
index 0cef6cfa8..6865d3893 100644
--- a/packages/testnet-faucets/src/ts/error_reporter.ts
+++ b/packages/testnet-faucets/src/ts/error_reporter.ts
@@ -5,36 +5,36 @@ import { configs } from './configs';
import { utils } from './utils';
export const errorReporter = {
- setup() {
- rollbar.init(configs.ROLLBAR_ACCESS_KEY, {
- environment: configs.ENVIRONMENT,
- });
+ setup() {
+ rollbar.init(configs.ROLLBAR_ACCESS_KEY, {
+ environment: configs.ENVIRONMENT,
+ });
- rollbar.handleUncaughtExceptions(configs.ROLLBAR_ACCESS_KEY);
+ rollbar.handleUncaughtExceptions(configs.ROLLBAR_ACCESS_KEY);
- process.on('unhandledRejection', async (err: Error) => {
- utils.consoleLog(`Uncaught exception ${err}. Stack: ${err.stack}`);
- await this.reportAsync(err);
- process.exit(1);
- });
- },
- async reportAsync(err: Error, req?: express.Request): Promise<any> {
- if (configs.ENVIRONMENT === 'development') {
- return; // Do not log development environment errors
- }
+ process.on('unhandledRejection', async (err: Error) => {
+ utils.consoleLog(`Uncaught exception ${err}. Stack: ${err.stack}`);
+ await this.reportAsync(err);
+ process.exit(1);
+ });
+ },
+ async reportAsync(err: Error, req?: express.Request): Promise<any> {
+ if (configs.ENVIRONMENT === 'development') {
+ return; // Do not log development environment errors
+ }
- return new Promise((resolve, reject) => {
- rollbar.handleError(err, req, (rollbarErr: Error) => {
- if (rollbarErr) {
- utils.consoleLog(`Error reporting to rollbar, ignoring: ${rollbarErr}`);
- reject(rollbarErr);
- } else {
- resolve();
- }
- });
- });
- },
- errorHandler() {
- return rollbar.errorHandler(configs.ROLLBAR_ACCESS_KEY);
- },
+ return new Promise((resolve, reject) => {
+ rollbar.handleError(err, req, (rollbarErr: Error) => {
+ if (rollbarErr) {
+ utils.consoleLog(`Error reporting to rollbar, ignoring: ${rollbarErr}`);
+ reject(rollbarErr);
+ } else {
+ resolve();
+ }
+ });
+ });
+ },
+ errorHandler() {
+ return rollbar.errorHandler(configs.ROLLBAR_ACCESS_KEY);
+ },
};
diff --git a/packages/testnet-faucets/src/ts/ether_request_queue.ts b/packages/testnet-faucets/src/ts/ether_request_queue.ts
index aa75a00e4..1c4b19ab9 100644
--- a/packages/testnet-faucets/src/ts/ether_request_queue.ts
+++ b/packages/testnet-faucets/src/ts/ether_request_queue.ts
@@ -9,19 +9,19 @@ import { utils } from './utils';
const DISPENSE_AMOUNT_ETHER = 0.1;
export class EtherRequestQueue extends RequestQueue {
- protected async processNextRequestFireAndForgetAsync(recipientAddress: string) {
- utils.consoleLog(`Processing ETH ${recipientAddress}`);
- const sendTransactionAsync = promisify(this.web3.eth.sendTransaction);
- try {
- const txHash = await sendTransactionAsync({
- from: configs.DISPENSER_ADDRESS,
- to: recipientAddress,
- value: this.web3.toWei(DISPENSE_AMOUNT_ETHER, 'ether'),
- });
- utils.consoleLog(`Sent ${DISPENSE_AMOUNT_ETHER} ETH to ${recipientAddress} tx: ${txHash}`);
- } catch (err) {
- utils.consoleLog(`Unexpected err: ${err} - ${JSON.stringify(err)}`);
- await errorReporter.reportAsync(err);
- }
- }
+ protected async processNextRequestFireAndForgetAsync(recipientAddress: string) {
+ utils.consoleLog(`Processing ETH ${recipientAddress}`);
+ const sendTransactionAsync = promisify(this.web3.eth.sendTransaction);
+ try {
+ const txHash = await sendTransactionAsync({
+ from: configs.DISPENSER_ADDRESS,
+ to: recipientAddress,
+ value: this.web3.toWei(DISPENSE_AMOUNT_ETHER, 'ether'),
+ });
+ utils.consoleLog(`Sent ${DISPENSE_AMOUNT_ETHER} ETH to ${recipientAddress} tx: ${txHash}`);
+ } catch (err) {
+ utils.consoleLog(`Unexpected err: ${err} - ${JSON.stringify(err)}`);
+ await errorReporter.reportAsync(err);
+ }
+ }
}
diff --git a/packages/testnet-faucets/src/ts/global.d.ts b/packages/testnet-faucets/src/ts/global.d.ts
index 90866565d..97cd35680 100644
--- a/packages/testnet-faucets/src/ts/global.d.ts
+++ b/packages/testnet-faucets/src/ts/global.d.ts
@@ -5,22 +5,22 @@ declare module 'web3-provider-engine/subproviders/nonce-tracker';
declare module 'web3-provider-engine/subproviders/hooked-wallet';
declare module '*.json' {
- const json: any;
- /* tslint:disable */
- export default json;
- /* tslint:enable */
+ const json: any;
+ /* tslint:disable */
+ export default json;
+ /* tslint:enable */
}
// Ethereumjs-tx declarations
declare module 'ethereumjs-tx' {
- class EthereumTx {
- public raw: Buffer[];
- public r: Buffer;
- public s: Buffer;
- public v: Buffer;
- public serialize(): Buffer;
- public sign(buffer: Buffer): void;
- constructor(txParams: any);
- }
- export = EthereumTx;
+ class EthereumTx {
+ public raw: Buffer[];
+ public r: Buffer;
+ public s: Buffer;
+ public v: Buffer;
+ public serialize(): Buffer;
+ public sign(buffer: Buffer): void;
+ constructor(txParams: any);
+ }
+ export = EthereumTx;
}
diff --git a/packages/testnet-faucets/src/ts/handler.ts b/packages/testnet-faucets/src/ts/handler.ts
index 455a925e3..bf5b3e81e 100644
--- a/packages/testnet-faucets/src/ts/handler.ts
+++ b/packages/testnet-faucets/src/ts/handler.ts
@@ -20,95 +20,95 @@ import { ZRXRequestQueue } from './zrx_request_queue';
import * as Web3 from 'web3';
interface RequestQueueByNetworkId {
- [networkId: string]: RequestQueue;
+ [networkId: string]: RequestQueue;
}
enum QueueType {
- ETH = 'ETH',
- ZRX = 'ZRX',
+ ETH = 'ETH',
+ ZRX = 'ZRX',
}
const DEFAULT_NETWORK_ID = 42; // kovan
export class Handler {
- private _etherRequestQueueByNetworkId: RequestQueueByNetworkId = {};
- private _zrxRequestQueueByNetworkId: RequestQueueByNetworkId = {};
- constructor() {
- _.forIn(rpcUrls, (rpcUrl: string, networkId: string) => {
- const providerObj = this._createProviderEngine(rpcUrl);
- const web3 = new Web3(providerObj);
- this._etherRequestQueueByNetworkId[networkId] = new EtherRequestQueue(web3);
- this._zrxRequestQueueByNetworkId[networkId] = new ZRXRequestQueue(web3, +networkId);
- });
- }
- public getQueueInfo(req: express.Request, res: express.Response) {
- res.setHeader('Content-Type', 'application/json');
- const queueInfo = _.mapValues(rpcUrls, (rpcUrl: string, networkId: string) => {
- utils.consoleLog(networkId);
- const etherRequestQueue = this._etherRequestQueueByNetworkId[networkId];
- const zrxRequestQueue = this._zrxRequestQueueByNetworkId[networkId];
- return {
- ether: {
- full: etherRequestQueue.isFull(),
- size: etherRequestQueue.size(),
- },
- zrx: {
- full: zrxRequestQueue.isFull(),
- size: zrxRequestQueue.size(),
- },
- };
- });
- const payload = JSON.stringify(queueInfo);
- res.status(200).send(payload);
- }
- public dispenseEther(req: express.Request, res: express.Response) {
- this._dispense(req, res, this._etherRequestQueueByNetworkId, QueueType.ETH);
- }
- public dispenseZRX(req: express.Request, res: express.Response) {
- this._dispense(req, res, this._zrxRequestQueueByNetworkId, QueueType.ZRX);
- }
- private _dispense(
- req: express.Request,
- res: express.Response,
- requestQueueByNetworkId: RequestQueueByNetworkId,
- queueType: QueueType,
- ) {
- const recipientAddress = req.params.recipient;
- if (_.isUndefined(recipientAddress) || !this._isValidEthereumAddress(recipientAddress)) {
- res.status(400).send('INVALID_RECIPIENT_ADDRESS');
- return;
- }
- const networkId = _.get(req.query, 'networkId', DEFAULT_NETWORK_ID);
- const requestQueue = _.get(requestQueueByNetworkId, networkId);
- if (_.isUndefined(requestQueue)) {
- res.status(400).send('INVALID_NETWORK_ID');
- return;
- }
- const lowerCaseRecipientAddress = recipientAddress.toLowerCase();
- const didAddToQueue = requestQueue.add(lowerCaseRecipientAddress);
- if (!didAddToQueue) {
- res.status(503).send('QUEUE_IS_FULL');
- return;
- }
- utils.consoleLog(`Added ${lowerCaseRecipientAddress} to queue: ${queueType} networkId: ${networkId}`);
- res.status(200).end();
- }
- // tslint:disable-next-line:prefer-function-over-method
- private _createProviderEngine(rpcUrl: string) {
- const engine = new ProviderEngine();
- engine.addProvider(new NonceSubprovider());
- engine.addProvider(new HookedWalletSubprovider(idManagement));
- engine.addProvider(
- new RpcSubprovider({
- rpcUrl,
- }),
- );
- engine.start();
- return engine;
- }
- // tslint:disable-next-line:prefer-function-over-method
- private _isValidEthereumAddress(address: string): boolean {
- const lowercaseAddress = address.toLowerCase();
- return addressUtils.isAddress(lowercaseAddress);
- }
+ private _etherRequestQueueByNetworkId: RequestQueueByNetworkId = {};
+ private _zrxRequestQueueByNetworkId: RequestQueueByNetworkId = {};
+ constructor() {
+ _.forIn(rpcUrls, (rpcUrl: string, networkId: string) => {
+ const providerObj = this._createProviderEngine(rpcUrl);
+ const web3 = new Web3(providerObj);
+ this._etherRequestQueueByNetworkId[networkId] = new EtherRequestQueue(web3);
+ this._zrxRequestQueueByNetworkId[networkId] = new ZRXRequestQueue(web3, +networkId);
+ });
+ }
+ public getQueueInfo(req: express.Request, res: express.Response) {
+ res.setHeader('Content-Type', 'application/json');
+ const queueInfo = _.mapValues(rpcUrls, (rpcUrl: string, networkId: string) => {
+ utils.consoleLog(networkId);
+ const etherRequestQueue = this._etherRequestQueueByNetworkId[networkId];
+ const zrxRequestQueue = this._zrxRequestQueueByNetworkId[networkId];
+ return {
+ ether: {
+ full: etherRequestQueue.isFull(),
+ size: etherRequestQueue.size(),
+ },
+ zrx: {
+ full: zrxRequestQueue.isFull(),
+ size: zrxRequestQueue.size(),
+ },
+ };
+ });
+ const payload = JSON.stringify(queueInfo);
+ res.status(200).send(payload);
+ }
+ public dispenseEther(req: express.Request, res: express.Response) {
+ this._dispense(req, res, this._etherRequestQueueByNetworkId, QueueType.ETH);
+ }
+ public dispenseZRX(req: express.Request, res: express.Response) {
+ this._dispense(req, res, this._zrxRequestQueueByNetworkId, QueueType.ZRX);
+ }
+ private _dispense(
+ req: express.Request,
+ res: express.Response,
+ requestQueueByNetworkId: RequestQueueByNetworkId,
+ queueType: QueueType,
+ ) {
+ const recipientAddress = req.params.recipient;
+ if (_.isUndefined(recipientAddress) || !this._isValidEthereumAddress(recipientAddress)) {
+ res.status(400).send('INVALID_RECIPIENT_ADDRESS');
+ return;
+ }
+ const networkId = _.get(req.query, 'networkId', DEFAULT_NETWORK_ID);
+ const requestQueue = _.get(requestQueueByNetworkId, networkId);
+ if (_.isUndefined(requestQueue)) {
+ res.status(400).send('INVALID_NETWORK_ID');
+ return;
+ }
+ const lowerCaseRecipientAddress = recipientAddress.toLowerCase();
+ const didAddToQueue = requestQueue.add(lowerCaseRecipientAddress);
+ if (!didAddToQueue) {
+ res.status(503).send('QUEUE_IS_FULL');
+ return;
+ }
+ utils.consoleLog(`Added ${lowerCaseRecipientAddress} to queue: ${queueType} networkId: ${networkId}`);
+ res.status(200).end();
+ }
+ // tslint:disable-next-line:prefer-function-over-method
+ private _createProviderEngine(rpcUrl: string) {
+ const engine = new ProviderEngine();
+ engine.addProvider(new NonceSubprovider());
+ engine.addProvider(new HookedWalletSubprovider(idManagement));
+ engine.addProvider(
+ new RpcSubprovider({
+ rpcUrl,
+ }),
+ );
+ engine.start();
+ return engine;
+ }
+ // tslint:disable-next-line:prefer-function-over-method
+ private _isValidEthereumAddress(address: string): boolean {
+ const lowercaseAddress = address.toLowerCase();
+ return addressUtils.isAddress(lowercaseAddress);
+ }
}
diff --git a/packages/testnet-faucets/src/ts/id_management.ts b/packages/testnet-faucets/src/ts/id_management.ts
index 2f04b5ff9..db9b610a3 100644
--- a/packages/testnet-faucets/src/ts/id_management.ts
+++ b/packages/testnet-faucets/src/ts/id_management.ts
@@ -5,17 +5,17 @@ import { configs } from './configs';
type Callback = (err: Error | null, accounts: any) => void;
export const idManagement = {
- getAccounts(callback: Callback) {
- callback(null, [configs.DISPENSER_ADDRESS]);
- },
- approveTransaction(txData: object, callback: Callback) {
- callback(null, true);
- },
- signTransaction(txData: object, callback: Callback) {
- const tx = new EthereumTx(txData);
- const privateKeyBuffer = new Buffer(configs.DISPENSER_PRIVATE_KEY as string, 'hex');
- tx.sign(privateKeyBuffer);
- const rawTx = `0x${tx.serialize().toString('hex')}`;
- callback(null, rawTx);
- },
+ getAccounts(callback: Callback) {
+ callback(null, [configs.DISPENSER_ADDRESS]);
+ },
+ approveTransaction(txData: object, callback: Callback) {
+ callback(null, true);
+ },
+ signTransaction(txData: object, callback: Callback) {
+ const tx = new EthereumTx(txData);
+ const privateKeyBuffer = new Buffer(configs.DISPENSER_PRIVATE_KEY as string, 'hex');
+ tx.sign(privateKeyBuffer);
+ const rawTx = `0x${tx.serialize().toString('hex')}`;
+ callback(null, rawTx);
+ },
};
diff --git a/packages/testnet-faucets/src/ts/request_queue.ts b/packages/testnet-faucets/src/ts/request_queue.ts
index 37b5505f8..20f2833a1 100644
--- a/packages/testnet-faucets/src/ts/request_queue.ts
+++ b/packages/testnet-faucets/src/ts/request_queue.ts
@@ -11,46 +11,46 @@ const MAX_QUEUE_SIZE = 500;
const DEFAULT_QUEUE_INTERVAL_MS = 1000;
export class RequestQueue {
- protected queueIntervalMs: number;
- protected queue: string[];
- protected queueIntervalId: NodeJS.Timer;
- protected web3: Web3;
- constructor(web3: any) {
- this.queueIntervalMs = DEFAULT_QUEUE_INTERVAL_MS;
- this.queue = [];
+ protected queueIntervalMs: number;
+ protected queue: string[];
+ protected queueIntervalId: NodeJS.Timer;
+ protected web3: Web3;
+ constructor(web3: any) {
+ this.queueIntervalMs = DEFAULT_QUEUE_INTERVAL_MS;
+ this.queue = [];
- this.web3 = web3;
+ this.web3 = web3;
- this.start();
- }
- public add(recipientAddress: string): boolean {
- if (this.isFull()) {
- return false;
- }
- this.queue.push(recipientAddress);
- return true;
- }
- public size(): number {
- return this.queue.length;
- }
- public isFull(): boolean {
- return this.size() >= MAX_QUEUE_SIZE;
- }
- protected start() {
- this.queueIntervalId = timers.setInterval(() => {
- const recipientAddress = this.queue.shift();
- if (_.isUndefined(recipientAddress)) {
- return;
- }
- // tslint:disable-next-line:no-floating-promises
- this.processNextRequestFireAndForgetAsync(recipientAddress);
- }, this.queueIntervalMs);
- }
- protected stop() {
- clearInterval(this.queueIntervalId);
- }
- // tslint:disable-next-line:prefer-function-over-method
- protected async processNextRequestFireAndForgetAsync(recipientAddress: string) {
- throw new Error('Expected processNextRequestFireAndForgetAsync to be implemented by a subclass');
- }
+ this.start();
+ }
+ public add(recipientAddress: string): boolean {
+ if (this.isFull()) {
+ return false;
+ }
+ this.queue.push(recipientAddress);
+ return true;
+ }
+ public size(): number {
+ return this.queue.length;
+ }
+ public isFull(): boolean {
+ return this.size() >= MAX_QUEUE_SIZE;
+ }
+ protected start() {
+ this.queueIntervalId = timers.setInterval(() => {
+ const recipientAddress = this.queue.shift();
+ if (_.isUndefined(recipientAddress)) {
+ return;
+ }
+ // tslint:disable-next-line:no-floating-promises
+ this.processNextRequestFireAndForgetAsync(recipientAddress);
+ }, this.queueIntervalMs);
+ }
+ protected stop() {
+ clearInterval(this.queueIntervalId);
+ }
+ // tslint:disable-next-line:prefer-function-over-method
+ protected async processNextRequestFireAndForgetAsync(recipientAddress: string) {
+ throw new Error('Expected processNextRequestFireAndForgetAsync to be implemented by a subclass');
+ }
}
diff --git a/packages/testnet-faucets/src/ts/rpc_urls.ts b/packages/testnet-faucets/src/ts/rpc_urls.ts
index 9e12bca37..25a3b938f 100644
--- a/packages/testnet-faucets/src/ts/rpc_urls.ts
+++ b/packages/testnet-faucets/src/ts/rpc_urls.ts
@@ -1,13 +1,13 @@
import { configs } from './configs';
const productionRpcUrls = {
- '2': `https://ropsten.infura.io/${configs.INFURA_API_KEY}`,
- '3': `https://rinkeby.infura.io/${configs.INFURA_API_KEY}`,
- '42': `https://kovan.infura.io/${configs.INFURA_API_KEY}`,
+ '2': `https://ropsten.infura.io/${configs.INFURA_API_KEY}`,
+ '3': `https://rinkeby.infura.io/${configs.INFURA_API_KEY}`,
+ '42': `https://kovan.infura.io/${configs.INFURA_API_KEY}`,
};
const developmentRpcUrls = {
- '50': 'http://127.0.0.1:8545',
+ '50': 'http://127.0.0.1:8545',
};
export const rpcUrls = configs.ENVIRONMENT === 'development' ? developmentRpcUrls : productionRpcUrls;
diff --git a/packages/testnet-faucets/src/ts/server.ts b/packages/testnet-faucets/src/ts/server.ts
index 6fb555538..26edfff5a 100644
--- a/packages/testnet-faucets/src/ts/server.ts
+++ b/packages/testnet-faucets/src/ts/server.ts
@@ -10,14 +10,14 @@ errorReporter.setup();
const app = express();
app.use(bodyParser.json()); // for parsing application/json
app.use((req, res, next) => {
- res.header('Access-Control-Allow-Origin', '*');
- res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept');
- next();
+ res.header('Access-Control-Allow-Origin', '*');
+ res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept');
+ next();
});
const handler = new Handler();
app.get('/ping', (req: express.Request, res: express.Response) => {
- res.status(200).send('pong');
+ res.status(200).send('pong');
});
app.get('/info', handler.getQueueInfo.bind(handler));
app.get('/ether/:recipient', handler.dispenseEther.bind(handler));
diff --git a/packages/testnet-faucets/src/ts/utils.ts b/packages/testnet-faucets/src/ts/utils.ts
index 821fe453c..893f82ca3 100644
--- a/packages/testnet-faucets/src/ts/utils.ts
+++ b/packages/testnet-faucets/src/ts/utils.ts
@@ -1,7 +1,7 @@
export const utils = {
- consoleLog(message: string) {
- /* tslint:disable */
- console.log(message);
- /* tslint:enable */
- },
+ consoleLog(message: string) {
+ /* tslint:disable */
+ console.log(message);
+ /* tslint:enable */
+ },
};
diff --git a/packages/testnet-faucets/src/ts/zrx_request_queue.ts b/packages/testnet-faucets/src/ts/zrx_request_queue.ts
index b81a44cd1..3d73f9dd2 100644
--- a/packages/testnet-faucets/src/ts/zrx_request_queue.ts
+++ b/packages/testnet-faucets/src/ts/zrx_request_queue.ts
@@ -17,30 +17,30 @@ const DISPENSE_AMOUNT_ZRX = new BigNumber(0.1);
const QUEUE_INTERVAL_MS = 5000;
export class ZRXRequestQueue extends RequestQueue {
- private _zeroEx: ZeroEx;
- constructor(web3: Web3, networkId: number) {
- super(web3);
- this.queueIntervalMs = QUEUE_INTERVAL_MS;
- const zeroExConfig = {
- networkId,
- };
- this._zeroEx = new ZeroEx(web3.currentProvider, zeroExConfig);
- }
- protected async processNextRequestFireAndForgetAsync(recipientAddress: string) {
- utils.consoleLog(`Processing ZRX ${recipientAddress}`);
- const baseUnitAmount = ZeroEx.toBaseUnitAmount(DISPENSE_AMOUNT_ZRX, 18);
- try {
- const zrxTokenAddress = this._zeroEx.exchange.getZRXTokenAddress();
- const txHash = await this._zeroEx.token.transferAsync(
- zrxTokenAddress,
- configs.DISPENSER_ADDRESS,
- recipientAddress,
- baseUnitAmount,
- );
- utils.consoleLog(`Sent ${DISPENSE_AMOUNT_ZRX} ZRX to ${recipientAddress} tx: ${txHash}`);
- } catch (err) {
- utils.consoleLog(`Unexpected err: ${err} - ${JSON.stringify(err)}`);
- await errorReporter.reportAsync(err);
- }
- }
+ private _zeroEx: ZeroEx;
+ constructor(web3: Web3, networkId: number) {
+ super(web3);
+ this.queueIntervalMs = QUEUE_INTERVAL_MS;
+ const zeroExConfig = {
+ networkId,
+ };
+ this._zeroEx = new ZeroEx(web3.currentProvider, zeroExConfig);
+ }
+ protected async processNextRequestFireAndForgetAsync(recipientAddress: string) {
+ utils.consoleLog(`Processing ZRX ${recipientAddress}`);
+ const baseUnitAmount = ZeroEx.toBaseUnitAmount(DISPENSE_AMOUNT_ZRX, 18);
+ try {
+ const zrxTokenAddress = this._zeroEx.exchange.getZRXTokenAddress();
+ const txHash = await this._zeroEx.token.transferAsync(
+ zrxTokenAddress,
+ configs.DISPENSER_ADDRESS,
+ recipientAddress,
+ baseUnitAmount,
+ );
+ utils.consoleLog(`Sent ${DISPENSE_AMOUNT_ZRX} ZRX to ${recipientAddress} tx: ${txHash}`);
+ } catch (err) {
+ utils.consoleLog(`Unexpected err: ${err} - ${JSON.stringify(err)}`);
+ await errorReporter.reportAsync(err);
+ }
+ }
}
diff --git a/packages/testnet-faucets/tsconfig.json b/packages/testnet-faucets/tsconfig.json
index e3353cc95..7f0c084ff 100644
--- a/packages/testnet-faucets/tsconfig.json
+++ b/packages/testnet-faucets/tsconfig.json
@@ -1,7 +1,7 @@
{
- "extends": "../../tsconfig",
- "compilerOptions": {
- "outDir": "lib"
- },
- "include": ["./src/ts/**/*", "../../node_modules/web3-typescript-typings/index.d.ts"]
+ "extends": "../../tsconfig",
+ "compilerOptions": {
+ "outDir": "lib"
+ },
+ "include": ["./src/ts/**/*", "../../node_modules/web3-typescript-typings/index.d.ts"]
}
diff --git a/packages/testnet-faucets/tslint.json b/packages/testnet-faucets/tslint.json
index e63054bfc..ffaefe83a 100644
--- a/packages/testnet-faucets/tslint.json
+++ b/packages/testnet-faucets/tslint.json
@@ -1,3 +1,3 @@
{
- "extends": ["@0xproject/tslint-config"]
+ "extends": ["@0xproject/tslint-config"]
}
diff --git a/packages/tslint-config/README.md b/packages/tslint-config/README.md
index 71fdd9772..8a6fa8a2f 100644
--- a/packages/tslint-config/README.md
+++ b/packages/tslint-config/README.md
@@ -14,7 +14,7 @@ Add the following to your `tslint.json` file
```json
{
- "extends": ["@0xproject/tslint-config"]
+ "extends": ["@0xproject/tslint-config"]
}
```
diff --git a/packages/tslint-config/package.json b/packages/tslint-config/package.json
index 95bd67ac6..8dd884370 100644
--- a/packages/tslint-config/package.json
+++ b/packages/tslint-config/package.json
@@ -1,37 +1,37 @@
{
- "name": "@0xproject/tslint-config",
- "version": "0.4.4",
- "description": "Lint rules related to 0xProject for TSLint",
- "main": "tslint.json",
- "scripts": {
- "build": "tsc",
- "clean": "shx rm -rf lib",
- "lint": "tslint --project . 'rules/**/*.ts'"
- },
- "files": ["tslint.js", "README.md", "LICENSE"],
- "repository": {
- "type": "git",
- "url": "git://github.com/0xProject/0x.js.git"
- },
- "keywords": ["tslint", "config", "0xProject", "typescript", "ts"],
- "author": {
- "name": "Fabio Berger",
- "email": "fabio@0xproject.com"
- },
- "license": "Apache-2.0",
- "bugs": {
- "url": "https://github.com/0xProject/0x.js/issues"
- },
- "homepage": "https://github.com/0xProject/0x.js/packages/tslint-config/README.md",
- "devDependencies": {
- "@types/lodash": "^4.14.86",
- "shx": "^0.2.2",
- "tslint": "5.8.0",
- "tslint-eslint-rules": "^4.1.1",
- "typescript": "~2.6.1"
- },
- "dependencies": {
- "lodash": "^4.17.4",
- "tslint-react": "^3.2.0"
- }
+ "name": "@0xproject/tslint-config",
+ "version": "0.4.4",
+ "description": "Lint rules related to 0xProject for TSLint",
+ "main": "tslint.json",
+ "scripts": {
+ "build": "tsc",
+ "clean": "shx rm -rf lib",
+ "lint": "tslint --project . 'rules/**/*.ts'"
+ },
+ "files": ["tslint.js", "README.md", "LICENSE"],
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/0xProject/0x.js.git"
+ },
+ "keywords": ["tslint", "config", "0xProject", "typescript", "ts"],
+ "author": {
+ "name": "Fabio Berger",
+ "email": "fabio@0xproject.com"
+ },
+ "license": "Apache-2.0",
+ "bugs": {
+ "url": "https://github.com/0xProject/0x.js/issues"
+ },
+ "homepage": "https://github.com/0xProject/0x.js/packages/tslint-config/README.md",
+ "devDependencies": {
+ "@types/lodash": "^4.14.86",
+ "shx": "^0.2.2",
+ "tslint": "5.8.0",
+ "tslint-eslint-rules": "^4.1.1",
+ "typescript": "~2.6.1"
+ },
+ "dependencies": {
+ "lodash": "^4.17.4",
+ "tslint-react": "^3.2.0"
+ }
}
diff --git a/packages/tslint-config/rules/asyncSuffixRule.ts b/packages/tslint-config/rules/asyncSuffixRule.ts
index 8af9d88d4..5215c7151 100644
--- a/packages/tslint-config/rules/asyncSuffixRule.ts
+++ b/packages/tslint-config/rules/asyncSuffixRule.ts
@@ -4,7 +4,7 @@ import * as ts from 'typescript';
import { AsyncSuffixWalker } from './walkers/async_suffix';
export class Rule extends Lint.Rules.AbstractRule {
- public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] {
- return this.applyWithWalker(new AsyncSuffixWalker(sourceFile, this.getOptions()));
- }
+ public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] {
+ return this.applyWithWalker(new AsyncSuffixWalker(sourceFile, this.getOptions()));
+ }
}
diff --git a/packages/tslint-config/rules/underscorePrivatesRule.ts b/packages/tslint-config/rules/underscorePrivatesRule.ts
index 4d9ee69f7..472ea09ff 100644
--- a/packages/tslint-config/rules/underscorePrivatesRule.ts
+++ b/packages/tslint-config/rules/underscorePrivatesRule.ts
@@ -4,58 +4,58 @@ import * as ts from 'typescript';
const UNDERSCORE = '_';
type RelevantClassMember =
- | ts.MethodDeclaration
- | ts.PropertyDeclaration
- | ts.GetAccessorDeclaration
- | ts.SetAccessorDeclaration;
+ | ts.MethodDeclaration
+ | ts.PropertyDeclaration
+ | ts.GetAccessorDeclaration
+ | ts.SetAccessorDeclaration;
// Copied from: https://github.com/DanielRosenwasser/underscore-privates-tslint-rule
// The version on github is not published on npm
export class Rule extends Lint.Rules.AbstractRule {
- public static FAILURE_STRING = 'private members must be prefixed with an underscore';
+ public static FAILURE_STRING = 'private members must be prefixed with an underscore';
- public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] {
- return this.applyWithFunction(sourceFile, walk);
- }
+ public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] {
+ return this.applyWithFunction(sourceFile, walk);
+ }
}
function walk(ctx: Lint.WalkContext<void>): void {
- traverse(ctx.sourceFile);
+ traverse(ctx.sourceFile);
- function traverse(node: ts.Node): void {
- checkNodeForViolations(ctx, node);
- return ts.forEachChild(node, traverse);
- }
+ function traverse(node: ts.Node): void {
+ checkNodeForViolations(ctx, node);
+ return ts.forEachChild(node, traverse);
+ }
}
function checkNodeForViolations(ctx: Lint.WalkContext<void>, node: ts.Node): void {
- if (!isRelevantClassMember(node)) {
- return;
- }
- // The declaration might have a computed property name or a numeric name.
- const name = node.name;
- if (!nameIsIdentifier(name)) {
- return;
- }
- if (!nameStartsWithUnderscore(name.text) && memberIsPrivate(node)) {
- ctx.addFailureAtNode(name, Rule.FAILURE_STRING);
- }
+ if (!isRelevantClassMember(node)) {
+ return;
+ }
+ // The declaration might have a computed property name or a numeric name.
+ const name = node.name;
+ if (!nameIsIdentifier(name)) {
+ return;
+ }
+ if (!nameStartsWithUnderscore(name.text) && memberIsPrivate(node)) {
+ ctx.addFailureAtNode(name, Rule.FAILURE_STRING);
+ }
}
function isRelevantClassMember(node: ts.Node): node is RelevantClassMember {
- switch (node.kind) {
- case ts.SyntaxKind.MethodDeclaration:
- case ts.SyntaxKind.PropertyDeclaration:
- case ts.SyntaxKind.GetAccessor:
- case ts.SyntaxKind.SetAccessor:
- return true;
- default:
- return false;
- }
+ switch (node.kind) {
+ case ts.SyntaxKind.MethodDeclaration:
+ case ts.SyntaxKind.PropertyDeclaration:
+ case ts.SyntaxKind.GetAccessor:
+ case ts.SyntaxKind.SetAccessor:
+ return true;
+ default:
+ return false;
+ }
}
function nameStartsWithUnderscore(text: string) {
- return text.charCodeAt(0) === UNDERSCORE.charCodeAt(0);
+ return text.charCodeAt(0) === UNDERSCORE.charCodeAt(0);
}
function memberIsPrivate(node: ts.Declaration) {
- return Lint.hasModifier(node.modifiers, ts.SyntaxKind.PrivateKeyword);
+ return Lint.hasModifier(node.modifiers, ts.SyntaxKind.PrivateKeyword);
}
function nameIsIdentifier(node: ts.Node): node is ts.Identifier {
- return node.kind === ts.SyntaxKind.Identifier;
+ return node.kind === ts.SyntaxKind.Identifier;
}
diff --git a/packages/tslint-config/rules/walkers/async_suffix.ts b/packages/tslint-config/rules/walkers/async_suffix.ts
index e1138de8d..eaec9c5f6 100644
--- a/packages/tslint-config/rules/walkers/async_suffix.ts
+++ b/packages/tslint-config/rules/walkers/async_suffix.ts
@@ -3,24 +3,24 @@ import * as Lint from 'tslint';
import * as ts from 'typescript';
export class AsyncSuffixWalker extends Lint.RuleWalker {
- public static FAILURE_STRING = 'async functions must have an Async suffix';
- public visitMethodDeclaration(node: ts.MethodDeclaration): void {
- const methodNameNode = node.name;
- const methodName = methodNameNode.getText();
- if (!_.isUndefined(node.type)) {
- if (node.type.kind === ts.SyntaxKind.TypeReference) {
- // tslint:disable-next-line:no-unnecessary-type-assertion
- const returnTypeName = (node.type as ts.TypeReferenceNode).typeName.getText();
- if (returnTypeName === 'Promise' && !methodName.endsWith('Async')) {
- const failure = this.createFailure(
- methodNameNode.getStart(),
- methodNameNode.getWidth(),
- AsyncSuffixWalker.FAILURE_STRING,
- );
- this.addFailure(failure);
- }
- }
- }
- super.visitMethodDeclaration(node);
- }
+ public static FAILURE_STRING = 'async functions must have an Async suffix';
+ public visitMethodDeclaration(node: ts.MethodDeclaration): void {
+ const methodNameNode = node.name;
+ const methodName = methodNameNode.getText();
+ if (!_.isUndefined(node.type)) {
+ if (node.type.kind === ts.SyntaxKind.TypeReference) {
+ // tslint:disable-next-line:no-unnecessary-type-assertion
+ const returnTypeName = (node.type as ts.TypeReferenceNode).typeName.getText();
+ if (returnTypeName === 'Promise' && !methodName.endsWith('Async')) {
+ const failure = this.createFailure(
+ methodNameNode.getStart(),
+ methodNameNode.getWidth(),
+ AsyncSuffixWalker.FAILURE_STRING,
+ );
+ this.addFailure(failure);
+ }
+ }
+ }
+ super.visitMethodDeclaration(node);
+ }
}
diff --git a/packages/tslint-config/tsconfig.json b/packages/tslint-config/tsconfig.json
index 83db0fcd2..15da53092 100644
--- a/packages/tslint-config/tsconfig.json
+++ b/packages/tslint-config/tsconfig.json
@@ -1,7 +1,7 @@
{
- "extends": "../../tsconfig",
- "compilerOptions": {
- "outDir": "lib"
- },
- "include": ["./rules/**/*"]
+ "extends": "../../tsconfig",
+ "compilerOptions": {
+ "outDir": "lib"
+ },
+ "include": ["./rules/**/*"]
}
diff --git a/packages/tslint-config/tslint.json b/packages/tslint-config/tslint.json
index f2a9558b9..971588b08 100644
--- a/packages/tslint-config/tslint.json
+++ b/packages/tslint-config/tslint.json
@@ -1,101 +1,101 @@
{
- "extends": ["tslint:latest", "tslint-react", "tslint-eslint-rules"],
- "rules": {
- "adjacent-overload-signatures": true,
- "arrow-parens": [true, "ban-single-arg-parens"],
- "arrow-return-shorthand": true,
- "async-suffix": true,
- "await-promise": true,
- "binary-expression-operand-order": true,
- "callable-types": true,
- "class-name": true,
- "completed-docs": [
- true,
- {
- "functions": { "visibilities": ["exported"] },
- "methods": { "locations": "instance", "privacies": ["public", "protected"] }
- }
- ],
- "curly": true,
- "eofline": true,
- "encoding": true,
- "import-spacing": true,
- "indent": [true, "spaces", 4],
- "interface-name": false,
- "interface-over-type-literal": true,
- "linebreak-style": [true, "LF"],
- "max-classes-per-file": false,
- "max-classes-per-file": [true, 1],
- "max-line-length": false,
- "max-file-line-count": [true, 500],
- "member-access": true,
- "member-ordering": [true, "public-before-private", "static-before-instance", "variables-before-functions"],
- "newline-before-return": false,
- "new-parens": true,
- "no-angle-bracket-type-assertion": true,
- "no-boolean-literal-compare": true,
- "no-default-export": true,
- "no-empty-interface": false,
- "no-floating-promises": true,
- "no-non-null-assertion": true,
- "no-parameter-reassignment": true,
- "no-redundant-jsdoc": true,
- "no-return-await": true,
- "no-string-throw": true,
- "no-submodule-imports": false,
- "no-unnecessary-type-assertion": true,
- "no-unused-variable": [true, { "ignore-pattern": "^_\\d*" }],
- "no-implicit-dependencies": [true, "dev"],
- "number-literal-format": true,
- "object-literal-sort-keys": false,
- "object-literal-key-quotes": false,
- "ordered-imports": [
- true,
- {
- "grouped-imports": true
- }
- ],
- "prefer-const": true,
- "prefer-for-of": true,
- "prefer-function-over-method": true,
- "promise-function-async": true,
- "quotemark": [true, "single", "avoid-escape", "jsx-double"],
- "restrict-plus-operands": true,
- "semicolon": [true, "always"],
- "space-before-function-paren": [
- true,
- {
- "anonymous": "never",
- "named": "never",
- "method": "never",
- "constructor": "never",
- "asyncArrow": "always"
- }
- ],
- "space-within-parens": false,
- "type-literal-delimiter": true,
- "underscore-privates": true,
- "variable-name": [true, "ban-keywords", "allow-pascal-case"],
- "whitespace": [
- true,
- "check-branch",
- "check-decl",
- "check-operator",
- "check-separator",
- "check-rest-spread",
- "check-type",
- "check-typecast",
- "check-preblock"
- ],
- "jsx-alignment": true,
- "jsx-boolean-value": true,
- "jsx-curly-spacing": [true, "never"],
- "jsx-no-lambda": true,
- "jsx-no-multiline-js": false,
- "jsx-no-string-ref": true,
- "jsx-self-close": true,
- "jsx-wrap-multiline": false,
- "jsx-no-bind": false
- },
- "rulesDirectory": "lib"
+ "extends": ["tslint:latest", "tslint-react", "tslint-eslint-rules"],
+ "rules": {
+ "adjacent-overload-signatures": true,
+ "arrow-parens": [true, "ban-single-arg-parens"],
+ "arrow-return-shorthand": true,
+ "async-suffix": true,
+ "await-promise": true,
+ "binary-expression-operand-order": true,
+ "callable-types": true,
+ "class-name": true,
+ "completed-docs": [
+ true,
+ {
+ "functions": { "visibilities": ["exported"] },
+ "methods": { "locations": "instance", "privacies": ["public", "protected"] }
+ }
+ ],
+ "curly": true,
+ "eofline": true,
+ "encoding": true,
+ "import-spacing": true,
+ "indent": [true, "spaces", 4],
+ "interface-name": false,
+ "interface-over-type-literal": true,
+ "linebreak-style": [true, "LF"],
+ "max-classes-per-file": false,
+ "max-classes-per-file": [true, 1],
+ "max-line-length": false,
+ "max-file-line-count": [true, 500],
+ "member-access": true,
+ "member-ordering": [true, "public-before-private", "static-before-instance", "variables-before-functions"],
+ "newline-before-return": false,
+ "new-parens": true,
+ "no-angle-bracket-type-assertion": true,
+ "no-boolean-literal-compare": true,
+ "no-default-export": true,
+ "no-empty-interface": false,
+ "no-floating-promises": true,
+ "no-non-null-assertion": true,
+ "no-parameter-reassignment": true,
+ "no-redundant-jsdoc": true,
+ "no-return-await": true,
+ "no-string-throw": true,
+ "no-submodule-imports": false,
+ "no-unnecessary-type-assertion": true,
+ "no-unused-variable": [true, { "ignore-pattern": "^_\\d*" }],
+ "no-implicit-dependencies": [true, "dev"],
+ "number-literal-format": true,
+ "object-literal-sort-keys": false,
+ "object-literal-key-quotes": false,
+ "ordered-imports": [
+ true,
+ {
+ "grouped-imports": true
+ }
+ ],
+ "prefer-const": true,
+ "prefer-for-of": true,
+ "prefer-function-over-method": true,
+ "promise-function-async": true,
+ "quotemark": [true, "single", "avoid-escape", "jsx-double"],
+ "restrict-plus-operands": true,
+ "semicolon": [true, "always"],
+ "space-before-function-paren": [
+ true,
+ {
+ "anonymous": "never",
+ "named": "never",
+ "method": "never",
+ "constructor": "never",
+ "asyncArrow": "always"
+ }
+ ],
+ "space-within-parens": false,
+ "type-literal-delimiter": true,
+ "underscore-privates": true,
+ "variable-name": [true, "ban-keywords", "allow-pascal-case"],
+ "whitespace": [
+ true,
+ "check-branch",
+ "check-decl",
+ "check-operator",
+ "check-separator",
+ "check-rest-spread",
+ "check-type",
+ "check-typecast",
+ "check-preblock"
+ ],
+ "jsx-alignment": true,
+ "jsx-boolean-value": true,
+ "jsx-curly-spacing": [true, "never"],
+ "jsx-no-lambda": true,
+ "jsx-no-multiline-js": false,
+ "jsx-no-string-ref": true,
+ "jsx-self-close": true,
+ "jsx-wrap-multiline": false,
+ "jsx-no-bind": false
+ },
+ "rulesDirectory": "lib"
}
diff --git a/packages/types/package.json b/packages/types/package.json
index a7df01bde..42a2be4e0 100644
--- a/packages/types/package.json
+++ b/packages/types/package.json
@@ -1,31 +1,31 @@
{
- "name": "@0xproject/types",
- "version": "0.1.6",
- "description": "0x types",
- "main": "lib/index.js",
- "types": "lib/index.d.ts",
- "scripts": {
- "build": "tsc",
- "clean": "shx rm -rf lib",
- "lint": "tslint --project . 'src/**/*.ts'"
- },
- "license": "Apache-2.0",
- "repository": {
- "type": "git",
- "url": "https://github.com/0xProject/0x.js.git"
- },
- "bugs": {
- "url": "https://github.com/0xProject/0x.js/issues"
- },
- "homepage": "https://github.com/0xProject/0x.js/packages/types/README.md",
- "devDependencies": {
- "@0xproject/tslint-config": "^0.4.4",
- "shx": "^0.2.2",
- "tslint": "5.8.0",
- "typescript": "~2.6.1"
- },
- "dependencies": {
- "@0xproject/utils": "^0.2.2",
- "web3": "^0.20.0"
- }
+ "name": "@0xproject/types",
+ "version": "0.1.6",
+ "description": "0x types",
+ "main": "lib/index.js",
+ "types": "lib/index.d.ts",
+ "scripts": {
+ "build": "tsc",
+ "clean": "shx rm -rf lib",
+ "lint": "tslint --project . 'src/**/*.ts'"
+ },
+ "license": "Apache-2.0",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/0xProject/0x.js.git"
+ },
+ "bugs": {
+ "url": "https://github.com/0xProject/0x.js/issues"
+ },
+ "homepage": "https://github.com/0xProject/0x.js/packages/types/README.md",
+ "devDependencies": {
+ "@0xproject/tslint-config": "^0.4.4",
+ "shx": "^0.2.2",
+ "tslint": "5.8.0",
+ "typescript": "~2.6.1"
+ },
+ "dependencies": {
+ "@0xproject/utils": "^0.2.2",
+ "web3": "^0.20.0"
+ }
}
diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts
index f8d211052..9cf9bc7af 100644
--- a/packages/types/src/index.ts
+++ b/packages/types/src/index.ts
@@ -2,26 +2,26 @@ import { BigNumber } from '@0xproject/utils';
import * as Web3 from 'web3';
export interface TxData {
- from?: string;
- gas?: number;
- gasPrice?: BigNumber;
- nonce?: number;
+ from?: string;
+ gas?: number;
+ gasPrice?: BigNumber;
+ nonce?: number;
}
export interface TxDataPayable extends TxData {
- value?: BigNumber;
+ value?: BigNumber;
}
export interface TransactionReceipt {
- blockHash: string;
- blockNumber: number;
- transactionHash: string;
- transactionIndex: number;
- from: string;
- to: string;
- status: null | 0 | 1;
- cumulativeGasUsed: number;
- gasUsed: number;
- contractAddress: string | null;
- logs: Web3.LogEntry[];
+ blockHash: string;
+ blockNumber: number;
+ transactionHash: string;
+ transactionIndex: number;
+ from: string;
+ to: string;
+ status: null | 0 | 1;
+ cumulativeGasUsed: number;
+ gasUsed: number;
+ contractAddress: string | null;
+ logs: Web3.LogEntry[];
}
diff --git a/packages/types/tsconfig.json b/packages/types/tsconfig.json
index fc88c962d..3d967d05f 100644
--- a/packages/types/tsconfig.json
+++ b/packages/types/tsconfig.json
@@ -1,7 +1,7 @@
{
- "extends": "../../tsconfig",
- "compilerOptions": {
- "outDir": "lib"
- },
- "include": ["./src/**/*", "../../node_modules/web3-typescript-typings/index.d.ts"]
+ "extends": "../../tsconfig",
+ "compilerOptions": {
+ "outDir": "lib"
+ },
+ "include": ["./src/**/*", "../../node_modules/web3-typescript-typings/index.d.ts"]
}
diff --git a/packages/types/tslint.json b/packages/types/tslint.json
index e63054bfc..ffaefe83a 100644
--- a/packages/types/tslint.json
+++ b/packages/types/tslint.json
@@ -1,3 +1,3 @@
{
- "extends": ["@0xproject/tslint-config"]
+ "extends": ["@0xproject/tslint-config"]
}
diff --git a/packages/utils/package.json b/packages/utils/package.json
index 7a5a10337..47a297859 100644
--- a/packages/utils/package.json
+++ b/packages/utils/package.json
@@ -1,34 +1,34 @@
{
- "name": "@0xproject/utils",
- "version": "0.2.2",
- "description": "0x TS utils",
- "main": "lib/index.js",
- "types": "lib/index.d.ts",
- "scripts": {
- "build": "tsc",
- "clean": "shx rm -rf lib",
- "lint": "tslint --project . 'src/**/*.ts'"
- },
- "license": "Apache-2.0",
- "repository": {
- "type": "git",
- "url": "https://github.com/0xProject/0x.js.git"
- },
- "bugs": {
- "url": "https://github.com/0xProject/0x.js/issues"
- },
- "homepage": "https://github.com/0xProject/0x.js/packages/utils/README.md",
- "devDependencies": {
- "@0xproject/tslint-config": "^0.4.4",
- "@types/lodash": "^4.14.86",
- "npm-run-all": "^4.1.2",
- "shx": "^0.2.2",
- "tslint": "5.8.0",
- "typescript": "~2.6.1"
- },
- "dependencies": {
- "bignumber.js": "~4.1.0",
- "js-sha3": "^0.7.0",
- "lodash": "^4.17.4"
- }
+ "name": "@0xproject/utils",
+ "version": "0.2.2",
+ "description": "0x TS utils",
+ "main": "lib/index.js",
+ "types": "lib/index.d.ts",
+ "scripts": {
+ "build": "tsc",
+ "clean": "shx rm -rf lib",
+ "lint": "tslint --project . 'src/**/*.ts'"
+ },
+ "license": "Apache-2.0",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/0xProject/0x.js.git"
+ },
+ "bugs": {
+ "url": "https://github.com/0xProject/0x.js/issues"
+ },
+ "homepage": "https://github.com/0xProject/0x.js/packages/utils/README.md",
+ "devDependencies": {
+ "@0xproject/tslint-config": "^0.4.4",
+ "@types/lodash": "^4.14.86",
+ "npm-run-all": "^4.1.2",
+ "shx": "^0.2.2",
+ "tslint": "5.8.0",
+ "typescript": "~2.6.1"
+ },
+ "dependencies": {
+ "bignumber.js": "~4.1.0",
+ "js-sha3": "^0.7.0",
+ "lodash": "^4.17.4"
+ }
}
diff --git a/packages/utils/src/address_utils.ts b/packages/utils/src/address_utils.ts
index 07042cb43..f94985441 100644
--- a/packages/utils/src/address_utils.ts
+++ b/packages/utils/src/address_utils.ts
@@ -4,33 +4,33 @@ const BASIC_ADDRESS_REGEX = /^(0x)?[0-9a-f]{40}$/i;
const SAME_CASE_ADDRESS_REGEX = /^(0x)?([0-9a-f]{40}|[0-9A-F]{40})$/;
export const addressUtils = {
- isChecksumAddress(address: string): boolean {
- // Check each case
- const unprefixedAddress = address.replace('0x', '');
- const addressHash = jsSHA3.keccak256(unprefixedAddress.toLowerCase());
+ isChecksumAddress(address: string): boolean {
+ // Check each case
+ const unprefixedAddress = address.replace('0x', '');
+ const addressHash = jsSHA3.keccak256(unprefixedAddress.toLowerCase());
- for (let i = 0; i < 40; i++) {
- // The nth letter should be uppercase if the nth digit of casemap is 1
- if (
- (parseInt(addressHash[i], 16) > 7 && unprefixedAddress[i].toUpperCase() !== unprefixedAddress[i]) ||
- (parseInt(addressHash[i], 16) <= 7 && unprefixedAddress[i].toLowerCase() !== unprefixedAddress[i])
- ) {
- return false;
- }
- }
- return true;
- },
- isAddress(address: string): boolean {
- if (!BASIC_ADDRESS_REGEX.test(address)) {
- // Check if it has the basic requirements of an address
- return false;
- } else if (SAME_CASE_ADDRESS_REGEX.test(address)) {
- // If it's all small caps or all all caps, return true
- return true;
- } else {
- // Otherwise check each case
- const isValidChecksummedAddress = addressUtils.isChecksumAddress(address);
- return isValidChecksummedAddress;
- }
- },
+ for (let i = 0; i < 40; i++) {
+ // The nth letter should be uppercase if the nth digit of casemap is 1
+ if (
+ (parseInt(addressHash[i], 16) > 7 && unprefixedAddress[i].toUpperCase() !== unprefixedAddress[i]) ||
+ (parseInt(addressHash[i], 16) <= 7 && unprefixedAddress[i].toLowerCase() !== unprefixedAddress[i])
+ ) {
+ return false;
+ }
+ }
+ return true;
+ },
+ isAddress(address: string): boolean {
+ if (!BASIC_ADDRESS_REGEX.test(address)) {
+ // Check if it has the basic requirements of an address
+ return false;
+ } else if (SAME_CASE_ADDRESS_REGEX.test(address)) {
+ // If it's all small caps or all all caps, return true
+ return true;
+ } else {
+ // Otherwise check each case
+ const isValidChecksummedAddress = addressUtils.isChecksumAddress(address);
+ return isValidChecksummedAddress;
+ }
+ },
};
diff --git a/packages/utils/src/class_utils.ts b/packages/utils/src/class_utils.ts
index 7fb011f91..04e60ee57 100644
--- a/packages/utils/src/class_utils.ts
+++ b/packages/utils/src/class_utils.ts
@@ -1,18 +1,18 @@
import * as _ from 'lodash';
export const classUtils = {
- // This is useful for classes that have nested methods. Nested methods don't get bound out of the box.
- bindAll(self: any, exclude: string[] = ['contructor'], thisArg?: any): void {
- for (const key of Object.getOwnPropertyNames(self)) {
- const val = self[key];
- if (!_.includes(exclude, key)) {
- if (_.isFunction(val)) {
- self[key] = val.bind(thisArg || self);
- } else if (_.isObject(val)) {
- classUtils.bindAll(val, exclude, self);
- }
- }
- }
- return self;
- },
+ // This is useful for classes that have nested methods. Nested methods don't get bound out of the box.
+ bindAll(self: any, exclude: string[] = ['contructor'], thisArg?: any): void {
+ for (const key of Object.getOwnPropertyNames(self)) {
+ const val = self[key];
+ if (!_.includes(exclude, key)) {
+ if (_.isFunction(val)) {
+ self[key] = val.bind(thisArg || self);
+ } else if (_.isObject(val)) {
+ classUtils.bindAll(val, exclude, self);
+ }
+ }
+ }
+ return self;
+ },
};
diff --git a/packages/utils/src/configured_bignumber.ts b/packages/utils/src/configured_bignumber.ts
index d819e9365..e44c062c2 100644
--- a/packages/utils/src/configured_bignumber.ts
+++ b/packages/utils/src/configured_bignumber.ts
@@ -3,7 +3,7 @@ import { BigNumber } from 'bignumber.js';
// By default BigNumber's `toString` method converts to exponential notation if the value has
// more then 20 digits. We want to avoid this behavior, so we set EXPONENTIAL_AT to a high number
BigNumber.config({
- EXPONENTIAL_AT: 1000,
+ EXPONENTIAL_AT: 1000,
});
export { BigNumber };
diff --git a/packages/utils/src/interval_utils.ts b/packages/utils/src/interval_utils.ts
index 65e51677b..ebecc7015 100644
--- a/packages/utils/src/interval_utils.ts
+++ b/packages/utils/src/interval_utils.ts
@@ -1,37 +1,37 @@
import * as _ from 'lodash';
export const intervalUtils = {
- setAsyncExcludingInterval(fn: () => Promise<void>, intervalMs: number, onError: (err: Error) => void) {
- let locked = false;
- const intervalId = setInterval(async () => {
- if (locked) {
- return;
- } else {
- locked = true;
- try {
- await fn();
- } catch (err) {
- onError(err);
- }
- locked = false;
- }
- }, intervalMs);
- return intervalId;
- },
- clearAsyncExcludingInterval(intervalId: NodeJS.Timer): void {
- clearInterval(intervalId);
- },
- setInterval(fn: () => void, intervalMs: number, onError: (err: Error) => void) {
- const intervalId = setInterval(() => {
- try {
- fn();
- } catch (err) {
- onError(err);
- }
- }, intervalMs);
- return intervalId;
- },
- clearInterval(intervalId: NodeJS.Timer): void {
- clearInterval(intervalId);
- },
+ setAsyncExcludingInterval(fn: () => Promise<void>, intervalMs: number, onError: (err: Error) => void) {
+ let locked = false;
+ const intervalId = setInterval(async () => {
+ if (locked) {
+ return;
+ } else {
+ locked = true;
+ try {
+ await fn();
+ } catch (err) {
+ onError(err);
+ }
+ locked = false;
+ }
+ }, intervalMs);
+ return intervalId;
+ },
+ clearAsyncExcludingInterval(intervalId: NodeJS.Timer): void {
+ clearInterval(intervalId);
+ },
+ setInterval(fn: () => void, intervalMs: number, onError: (err: Error) => void) {
+ const intervalId = setInterval(() => {
+ try {
+ fn();
+ } catch (err) {
+ onError(err);
+ }
+ }, intervalMs);
+ return intervalId;
+ },
+ clearInterval(intervalId: NodeJS.Timer): void {
+ clearInterval(intervalId);
+ },
};
diff --git a/packages/utils/src/promisify.ts b/packages/utils/src/promisify.ts
index 6b29664f9..29d626b61 100644
--- a/packages/utils/src/promisify.ts
+++ b/packages/utils/src/promisify.ts
@@ -7,13 +7,13 @@ import * as _ from 'lodash';
*/
// HACK: This can't be properly typed without variadic kinds https://github.com/Microsoft/TypeScript/issues/5453
export function promisify<T>(originalFn: (...args: any[]) => void, thisArg?: any): (...callArgs: any[]) => Promise<T> {
- const promisifiedFunction = async (...callArgs: any[]): Promise<T> => {
- return new Promise<T>((resolve, reject) => {
- const callback = (err: Error | null, data?: T) => {
- _.isNull(err) ? resolve(data) : reject(err);
- };
- originalFn.apply(thisArg, [...callArgs, callback]);
- });
- };
- return promisifiedFunction;
+ const promisifiedFunction = async (...callArgs: any[]): Promise<T> => {
+ return new Promise<T>((resolve, reject) => {
+ const callback = (err: Error | null, data?: T) => {
+ _.isNull(err) ? resolve(data) : reject(err);
+ };
+ originalFn.apply(thisArg, [...callArgs, callback]);
+ });
+ };
+ return promisifiedFunction;
}
diff --git a/packages/utils/tsconfig.json b/packages/utils/tsconfig.json
index fc88c962d..3d967d05f 100644
--- a/packages/utils/tsconfig.json
+++ b/packages/utils/tsconfig.json
@@ -1,7 +1,7 @@
{
- "extends": "../../tsconfig",
- "compilerOptions": {
- "outDir": "lib"
- },
- "include": ["./src/**/*", "../../node_modules/web3-typescript-typings/index.d.ts"]
+ "extends": "../../tsconfig",
+ "compilerOptions": {
+ "outDir": "lib"
+ },
+ "include": ["./src/**/*", "../../node_modules/web3-typescript-typings/index.d.ts"]
}
diff --git a/packages/utils/tslint.json b/packages/utils/tslint.json
index e63054bfc..ffaefe83a 100644
--- a/packages/utils/tslint.json
+++ b/packages/utils/tslint.json
@@ -1,3 +1,3 @@
{
- "extends": ["@0xproject/tslint-config"]
+ "extends": ["@0xproject/tslint-config"]
}
diff --git a/packages/web3-typescript-typings/index.d.ts b/packages/web3-typescript-typings/index.d.ts
index b610d0e48..cd34759ed 100644
--- a/packages/web3-typescript-typings/index.d.ts
+++ b/packages/web3-typescript-typings/index.d.ts
@@ -1,422 +1,422 @@
declare module 'web3' {
- import * as BigNumber from 'bignumber.js';
-
- type MixedData = string | number | object | any[] | BigNumber.BigNumber;
-
- class Web3 {
- public static providers: typeof providers;
- public currentProvider: Web3.Provider;
-
- public eth: Web3.EthApi;
- public personal: Web3.PersonalApi | undefined;
- public version: Web3.VersionApi;
- public net: Web3.NetApi;
-
- public constructor(provider?: Web3.Provider);
-
- public isConnected(): boolean;
- public setProvider(provider: Web3.Provider): void;
- public reset(keepIsSyncing: boolean): void;
- public toHex(data: MixedData): string;
- public toAscii(hex: string): string;
- public fromAscii(ascii: string, padding?: number): string;
- public toDecimal(hex: string): number;
- public fromDecimal(value: number | string): string;
- public fromWei(value: number | string, unit: Web3.Unit): string;
- public fromWei(value: BigNumber.BigNumber, unit: Web3.Unit): BigNumber.BigNumber;
- public toWei(amount: number | string, unit: Web3.Unit): string;
- public toWei(amount: BigNumber.BigNumber, unit: Web3.Unit): BigNumber.BigNumber;
- public toBigNumber(value: number | string): BigNumber.BigNumber;
- public isAddress(address: string): boolean;
- public isChecksumAddress(address: string): boolean;
- public sha3(value: string, options?: Web3.Sha3Options): string;
- }
-
- namespace providers {
- class HttpProvider implements Web3.Provider {
- constructor(url?: string, timeout?: number, username?: string, password?: string);
- public sendAsync(
- payload: Web3.JSONRPCRequestPayload,
- callback: (err: Error, result: Web3.JSONRPCResponsePayload) => void,
- ): void;
- }
- }
-
- namespace Web3 {
- type ContractAbi = AbiDefinition[];
-
- type AbiDefinition = FunctionAbi | EventAbi;
-
- type FunctionAbi = MethodAbi | ConstructorAbi | FallbackAbi;
-
- enum AbiType {
- Function = 'function',
- Constructor = 'constructor',
- Event = 'event',
- Fallback = 'fallback',
- }
-
- type ConstructorStateMutability = 'nonpayable' | 'payable';
- type StateMutability = 'pure' | 'view' | ConstructorStateMutability;
-
- interface MethodAbi {
- type: AbiType.Function;
- name: string;
- inputs: FunctionParameter[];
- outputs: FunctionParameter[];
- constant: boolean;
- stateMutability: StateMutability;
- payable: boolean;
- }
-
- interface ConstructorAbi {
- type: AbiType.Constructor;
- inputs: FunctionParameter[];
- payable: boolean;
- stateMutability: ConstructorStateMutability;
- }
-
- interface FallbackAbi {
- type: AbiType.Fallback;
- payable: boolean;
- }
-
- interface EventParameter {
- name: string;
- type: string;
- indexed: boolean;
- }
-
- interface EventAbi {
- type: AbiType.Event;
- name: string;
- inputs: EventParameter[];
- anonymous: boolean;
- }
-
- interface FunctionParameter {
- name: string;
- type: string;
- }
-
- interface ContractInstance {
- address: string;
- abi: Web3.ContractAbi;
- [name: string]: any;
- }
-
- interface Contract<A extends ContractInstance> {
- at(address: string): A;
- getData(...args: any[]): string;
- 'new'(...args: any[]): A;
- }
-
- interface FilterObject {
- fromBlock?: number | string;
- toBlock?: number | string;
- address?: string;
- topics?: LogTopic[];
- }
-
- type LogTopic = null | string | string[];
-
- interface DecodedLogEntry<A> extends LogEntry {
- event: string;
- args: A;
- }
-
- interface DecodedLogEntryEvent<A> extends DecodedLogEntry<A> {
- removed: boolean;
- }
-
- interface LogEntryEvent extends LogEntry {
- removed: boolean;
- }
-
- interface FilterResult {
- get(callback: () => void): void;
- watch(callback: (err: Error, result: LogEntryEvent) => void): void;
- stopWatching(callback?: () => void): void;
- }
-
- export interface JSONRPCRequestPayload {
- params: any[];
- method: string;
- id: number;
- jsonrpc: string;
- }
-
- export interface JSONRPCResponsePayload {
- result: any;
- id: number;
- jsonrpc: string;
- }
-
- interface Provider {
- sendAsync(
- payload: JSONRPCRequestPayload,
- callback: (err: Error, result: JSONRPCResponsePayload) => void,
- ): void;
- }
-
- interface Sha3Options {
- encoding: 'hex';
- }
-
- interface EthApi {
- coinbase: string;
- mining: boolean;
- hashrate: number;
- gasPrice: BigNumber.BigNumber;
- accounts: string[];
- blockNumber: number;
- defaultAccount?: string;
- defaultBlock: Web3.BlockParam;
- syncing: Web3.SyncingResult;
- compile: {
- solidity(sourceString: string, cb?: (err: Error, result: any) => void): object;
- };
- getMining(cd: (err: Error, mining: boolean) => void): void;
- getHashrate(cd: (err: Error, hashrate: number) => void): void;
- getGasPrice(cd: (err: Error, gasPrice: BigNumber.BigNumber) => void): void;
- getAccounts(cd: (err: Error, accounts: string[]) => void): void;
- getBlockNumber(callback: (err: Error, blockNumber: number) => void): void;
- getSyncing(cd: (err: Error, syncing: Web3.SyncingResult) => void): void;
- isSyncing(cb: (err: Error, isSyncing: boolean, syncingState: Web3.SyncingState) => void): Web3.IsSyncing;
-
- getBlock(hashStringOrBlockNumber: string | Web3.BlockParam): Web3.BlockWithoutTransactionData;
- getBlock(
- hashStringOrBlockNumber: string | Web3.BlockParam,
- callback: (err: Error, blockObj: Web3.BlockWithoutTransactionData) => void,
- ): void;
- getBlock(
- hashStringOrBlockNumber: string | Web3.BlockParam,
- returnTransactionObjects: true,
- ): Web3.BlockWithTransactionData;
- getBlock(
- hashStringOrBlockNumber: string | Web3.BlockParam,
- returnTransactionObjects: true,
- callback: (err: Error, blockObj: Web3.BlockWithTransactionData) => void,
- ): void;
-
- getBlockTransactionCount(hashStringOrBlockNumber: string | Web3.BlockParam): number;
- getBlockTransactionCount(
- hashStringOrBlockNumber: string | Web3.BlockParam,
- callback: (err: Error, blockTransactionCount: number) => void,
- ): void;
-
- // TODO returnTransactionObjects
- getUncle(
- hashStringOrBlockNumber: string | Web3.BlockParam,
- uncleNumber: number,
- ): Web3.BlockWithoutTransactionData;
- getUncle(
- hashStringOrBlockNumber: string | Web3.BlockParam,
- uncleNumber: number,
- callback: (err: Error, uncle: Web3.BlockWithoutTransactionData) => void,
- ): void;
-
- getTransaction(transactionHash: string): Web3.Transaction;
- getTransaction(
- transactionHash: string,
- callback: (err: Error, transaction: Web3.Transaction) => void,
- ): void;
-
- getTransactionFromBlock(
- hashStringOrBlockNumber: string | Web3.BlockParam,
- indexNumber: number,
- ): Web3.Transaction;
- getTransactionFromBlock(
- hashStringOrBlockNumber: string | Web3.BlockParam,
- indexNumber: number,
- callback: (err: Error, transaction: Web3.Transaction) => void,
- ): void;
-
- contract(abi: Web3.AbiDefinition[]): Web3.Contract<any>;
-
- // TODO block param
- getBalance(addressHexString: string): BigNumber.BigNumber;
- getBalance(addressHexString: string, callback: (err: Error, result: BigNumber.BigNumber) => void): void;
-
- // TODO block param
- getStorageAt(address: string, position: number): string;
- getStorageAt(address: string, position: number, callback: (err: Error, storage: string) => void): void;
-
- // TODO block param
- getCode(addressHexString: string): string;
- getCode(addressHexString: string, callback: (err: Error, code: string) => void): void;
-
- filter(value: string | Web3.FilterObject): Web3.FilterResult;
-
- sendTransaction(txData: Web3.TxData): string;
- sendTransaction(txData: Web3.TxData, callback: (err: Error, value: string) => void): void;
-
- sendRawTransaction(rawTxData: string): string;
- sendRawTransaction(rawTxData: string, callback: (err: Error, value: string) => void): void;
-
- sign(address: string, data: string): string;
- sign(address: string, data: string, callback: (err: Error, signature: string) => void): void;
-
- getTransactionReceipt(txHash: string): Web3.TransactionReceipt | null;
- getTransactionReceipt(
- txHash: string,
- callback: (err: Error, receipt: Web3.TransactionReceipt | null) => void,
- ): void;
-
- // TODO block param
- call(callData: Web3.CallData): string;
- call(callData: Web3.CallData, callback: (err: Error, result: string) => void): void;
-
- estimateGas(callData: Web3.CallData): number;
- estimateGas(callData: Web3.CallData, callback: (err: Error, gas: number) => void): void;
-
- // TODO defaultBlock
- getTransactionCount(address: string): number;
- getTransactionCount(address: string, callback: (err: Error, count: number) => void): void;
- }
-
- interface VersionApi {
- api: string;
- network: string;
- node: string;
- ethereum: string;
- whisper: string;
- getNetwork(cd: (err: Error, networkId: string) => void): void;
- getNode(cd: (err: Error, nodeVersion: string) => void): void;
- getEthereum(cd: (err: Error, ethereum: string) => void): void;
- getWhisper(cd: (err: Error, whisper: string) => void): void;
- }
-
- interface PersonalApi {
- listAccounts: string[] | undefined;
- newAccount(password?: string): string;
- unlockAccount(address: string, password?: string, duration?: number): boolean;
- lockAccount(address: string): boolean;
- sign(message: string, account: string, password: string): string;
- sign(hexMessage: string, account: string, callback: (error: Error, signature: string) => void): void;
- }
-
- interface NetApi {
- listening: boolean;
- peerCount: boolean;
- getListening(cd: (err: Error, listening: boolean) => void): void;
- getPeerCount(cd: (err: Error, peerCount: number) => void): void;
- }
-
- type BlockParam = number | 'earliest' | 'latest' | 'pending';
-
- type Unit =
- | 'kwei'
- | 'ada'
- | 'mwei'
- | 'babbage'
- | 'gwei'
- | 'shannon'
- | 'szabo'
- | 'finney'
- | 'ether'
- | 'kether'
- | 'grand'
- | 'einstein'
- | 'mether'
- | 'gether'
- | 'tether';
-
- interface SyncingState {
- startingBlock: number;
- currentBlock: number;
- highestBlock: number;
- }
- type SyncingResult = false | SyncingState;
-
- interface IsSyncing {
- addCallback(cb: (err: Error, isSyncing: boolean, syncingState: SyncingState) => void): void;
- stopWatching(): void;
- }
-
- interface AbstractBlock {
- number: number | null;
- hash: string | null;
- parentHash: string;
- nonce: string | null;
- sha3Uncles: string;
- logsBloom: string | null;
- transactionsRoot: string;
- stateRoot: string;
- miner: string;
- difficulty: BigNumber.BigNumber;
- totalDifficulty: BigNumber.BigNumber;
- extraData: string;
- size: number;
- gasLimit: number;
- gasUsed: number;
- timestamp: number;
- uncles: string[];
- }
- interface BlockWithoutTransactionData extends AbstractBlock {
- transactions: string[];
- }
- interface BlockWithTransactionData extends AbstractBlock {
- transactions: Transaction[];
- }
-
- interface Transaction {
- hash: string;
- nonce: number;
- blockHash: string | null;
- blockNumber: number | null;
- transactionIndex: number | null;
- from: string;
- to: string | null;
- value: BigNumber.BigNumber;
- gasPrice: BigNumber.BigNumber;
- gas: number;
- input: string;
- }
-
- interface CallTxDataBase {
- to?: string;
- value?: number | string | BigNumber.BigNumber;
- gas?: number | string | BigNumber.BigNumber;
- gasPrice?: number | string | BigNumber.BigNumber;
- data?: string;
- nonce?: number;
- }
-
- interface TxData extends CallTxDataBase {
- from: string;
- }
-
- interface CallData extends CallTxDataBase {
- from?: string;
- }
-
- interface TransactionReceipt {
- blockHash: string;
- blockNumber: number;
- transactionHash: string;
- transactionIndex: number;
- from: string;
- to: string;
- status: null | string | 0 | 1;
- cumulativeGasUsed: number;
- gasUsed: number;
- contractAddress: string | null;
- logs: LogEntry[];
- }
-
- interface LogEntry {
- logIndex: number | null;
- transactionIndex: number | null;
- transactionHash: string;
- blockHash: string | null;
- blockNumber: number | null;
- address: string;
- data: string;
- topics: string[];
- }
- }
- /* tslint:disable */
- export = Web3;
- /* tslint:enable */
+ import * as BigNumber from 'bignumber.js';
+
+ type MixedData = string | number | object | any[] | BigNumber.BigNumber;
+
+ class Web3 {
+ public static providers: typeof providers;
+ public currentProvider: Web3.Provider;
+
+ public eth: Web3.EthApi;
+ public personal: Web3.PersonalApi | undefined;
+ public version: Web3.VersionApi;
+ public net: Web3.NetApi;
+
+ public constructor(provider?: Web3.Provider);
+
+ public isConnected(): boolean;
+ public setProvider(provider: Web3.Provider): void;
+ public reset(keepIsSyncing: boolean): void;
+ public toHex(data: MixedData): string;
+ public toAscii(hex: string): string;
+ public fromAscii(ascii: string, padding?: number): string;
+ public toDecimal(hex: string): number;
+ public fromDecimal(value: number | string): string;
+ public fromWei(value: number | string, unit: Web3.Unit): string;
+ public fromWei(value: BigNumber.BigNumber, unit: Web3.Unit): BigNumber.BigNumber;
+ public toWei(amount: number | string, unit: Web3.Unit): string;
+ public toWei(amount: BigNumber.BigNumber, unit: Web3.Unit): BigNumber.BigNumber;
+ public toBigNumber(value: number | string): BigNumber.BigNumber;
+ public isAddress(address: string): boolean;
+ public isChecksumAddress(address: string): boolean;
+ public sha3(value: string, options?: Web3.Sha3Options): string;
+ }
+
+ namespace providers {
+ class HttpProvider implements Web3.Provider {
+ constructor(url?: string, timeout?: number, username?: string, password?: string);
+ public sendAsync(
+ payload: Web3.JSONRPCRequestPayload,
+ callback: (err: Error, result: Web3.JSONRPCResponsePayload) => void,
+ ): void;
+ }
+ }
+
+ namespace Web3 {
+ type ContractAbi = AbiDefinition[];
+
+ type AbiDefinition = FunctionAbi | EventAbi;
+
+ type FunctionAbi = MethodAbi | ConstructorAbi | FallbackAbi;
+
+ enum AbiType {
+ Function = 'function',
+ Constructor = 'constructor',
+ Event = 'event',
+ Fallback = 'fallback',
+ }
+
+ type ConstructorStateMutability = 'nonpayable' | 'payable';
+ type StateMutability = 'pure' | 'view' | ConstructorStateMutability;
+
+ interface MethodAbi {
+ type: AbiType.Function;
+ name: string;
+ inputs: FunctionParameter[];
+ outputs: FunctionParameter[];
+ constant: boolean;
+ stateMutability: StateMutability;
+ payable: boolean;
+ }
+
+ interface ConstructorAbi {
+ type: AbiType.Constructor;
+ inputs: FunctionParameter[];
+ payable: boolean;
+ stateMutability: ConstructorStateMutability;
+ }
+
+ interface FallbackAbi {
+ type: AbiType.Fallback;
+ payable: boolean;
+ }
+
+ interface EventParameter {
+ name: string;
+ type: string;
+ indexed: boolean;
+ }
+
+ interface EventAbi {
+ type: AbiType.Event;
+ name: string;
+ inputs: EventParameter[];
+ anonymous: boolean;
+ }
+
+ interface FunctionParameter {
+ name: string;
+ type: string;
+ }
+
+ interface ContractInstance {
+ address: string;
+ abi: Web3.ContractAbi;
+ [name: string]: any;
+ }
+
+ interface Contract<A extends ContractInstance> {
+ at(address: string): A;
+ getData(...args: any[]): string;
+ 'new'(...args: any[]): A;
+ }
+
+ interface FilterObject {
+ fromBlock?: number | string;
+ toBlock?: number | string;
+ address?: string;
+ topics?: LogTopic[];
+ }
+
+ type LogTopic = null | string | string[];
+
+ interface DecodedLogEntry<A> extends LogEntry {
+ event: string;
+ args: A;
+ }
+
+ interface DecodedLogEntryEvent<A> extends DecodedLogEntry<A> {
+ removed: boolean;
+ }
+
+ interface LogEntryEvent extends LogEntry {
+ removed: boolean;
+ }
+
+ interface FilterResult {
+ get(callback: () => void): void;
+ watch(callback: (err: Error, result: LogEntryEvent) => void): void;
+ stopWatching(callback?: () => void): void;
+ }
+
+ export interface JSONRPCRequestPayload {
+ params: any[];
+ method: string;
+ id: number;
+ jsonrpc: string;
+ }
+
+ export interface JSONRPCResponsePayload {
+ result: any;
+ id: number;
+ jsonrpc: string;
+ }
+
+ interface Provider {
+ sendAsync(
+ payload: JSONRPCRequestPayload,
+ callback: (err: Error, result: JSONRPCResponsePayload) => void,
+ ): void;
+ }
+
+ interface Sha3Options {
+ encoding: 'hex';
+ }
+
+ interface EthApi {
+ coinbase: string;
+ mining: boolean;
+ hashrate: number;
+ gasPrice: BigNumber.BigNumber;
+ accounts: string[];
+ blockNumber: number;
+ defaultAccount?: string;
+ defaultBlock: Web3.BlockParam;
+ syncing: Web3.SyncingResult;
+ compile: {
+ solidity(sourceString: string, cb?: (err: Error, result: any) => void): object;
+ };
+ getMining(cd: (err: Error, mining: boolean) => void): void;
+ getHashrate(cd: (err: Error, hashrate: number) => void): void;
+ getGasPrice(cd: (err: Error, gasPrice: BigNumber.BigNumber) => void): void;
+ getAccounts(cd: (err: Error, accounts: string[]) => void): void;
+ getBlockNumber(callback: (err: Error, blockNumber: number) => void): void;
+ getSyncing(cd: (err: Error, syncing: Web3.SyncingResult) => void): void;
+ isSyncing(cb: (err: Error, isSyncing: boolean, syncingState: Web3.SyncingState) => void): Web3.IsSyncing;
+
+ getBlock(hashStringOrBlockNumber: string | Web3.BlockParam): Web3.BlockWithoutTransactionData;
+ getBlock(
+ hashStringOrBlockNumber: string | Web3.BlockParam,
+ callback: (err: Error, blockObj: Web3.BlockWithoutTransactionData) => void,
+ ): void;
+ getBlock(
+ hashStringOrBlockNumber: string | Web3.BlockParam,
+ returnTransactionObjects: true,
+ ): Web3.BlockWithTransactionData;
+ getBlock(
+ hashStringOrBlockNumber: string | Web3.BlockParam,
+ returnTransactionObjects: true,
+ callback: (err: Error, blockObj: Web3.BlockWithTransactionData) => void,
+ ): void;
+
+ getBlockTransactionCount(hashStringOrBlockNumber: string | Web3.BlockParam): number;
+ getBlockTransactionCount(
+ hashStringOrBlockNumber: string | Web3.BlockParam,
+ callback: (err: Error, blockTransactionCount: number) => void,
+ ): void;
+
+ // TODO returnTransactionObjects
+ getUncle(
+ hashStringOrBlockNumber: string | Web3.BlockParam,
+ uncleNumber: number,
+ ): Web3.BlockWithoutTransactionData;
+ getUncle(
+ hashStringOrBlockNumber: string | Web3.BlockParam,
+ uncleNumber: number,
+ callback: (err: Error, uncle: Web3.BlockWithoutTransactionData) => void,
+ ): void;
+
+ getTransaction(transactionHash: string): Web3.Transaction;
+ getTransaction(
+ transactionHash: string,
+ callback: (err: Error, transaction: Web3.Transaction) => void,
+ ): void;
+
+ getTransactionFromBlock(
+ hashStringOrBlockNumber: string | Web3.BlockParam,
+ indexNumber: number,
+ ): Web3.Transaction;
+ getTransactionFromBlock(
+ hashStringOrBlockNumber: string | Web3.BlockParam,
+ indexNumber: number,
+ callback: (err: Error, transaction: Web3.Transaction) => void,
+ ): void;
+
+ contract(abi: Web3.AbiDefinition[]): Web3.Contract<any>;
+
+ // TODO block param
+ getBalance(addressHexString: string): BigNumber.BigNumber;
+ getBalance(addressHexString: string, callback: (err: Error, result: BigNumber.BigNumber) => void): void;
+
+ // TODO block param
+ getStorageAt(address: string, position: number): string;
+ getStorageAt(address: string, position: number, callback: (err: Error, storage: string) => void): void;
+
+ // TODO block param
+ getCode(addressHexString: string): string;
+ getCode(addressHexString: string, callback: (err: Error, code: string) => void): void;
+
+ filter(value: string | Web3.FilterObject): Web3.FilterResult;
+
+ sendTransaction(txData: Web3.TxData): string;
+ sendTransaction(txData: Web3.TxData, callback: (err: Error, value: string) => void): void;
+
+ sendRawTransaction(rawTxData: string): string;
+ sendRawTransaction(rawTxData: string, callback: (err: Error, value: string) => void): void;
+
+ sign(address: string, data: string): string;
+ sign(address: string, data: string, callback: (err: Error, signature: string) => void): void;
+
+ getTransactionReceipt(txHash: string): Web3.TransactionReceipt | null;
+ getTransactionReceipt(
+ txHash: string,
+ callback: (err: Error, receipt: Web3.TransactionReceipt | null) => void,
+ ): void;
+
+ // TODO block param
+ call(callData: Web3.CallData): string;
+ call(callData: Web3.CallData, callback: (err: Error, result: string) => void): void;
+
+ estimateGas(callData: Web3.CallData): number;
+ estimateGas(callData: Web3.CallData, callback: (err: Error, gas: number) => void): void;
+
+ // TODO defaultBlock
+ getTransactionCount(address: string): number;
+ getTransactionCount(address: string, callback: (err: Error, count: number) => void): void;
+ }
+
+ interface VersionApi {
+ api: string;
+ network: string;
+ node: string;
+ ethereum: string;
+ whisper: string;
+ getNetwork(cd: (err: Error, networkId: string) => void): void;
+ getNode(cd: (err: Error, nodeVersion: string) => void): void;
+ getEthereum(cd: (err: Error, ethereum: string) => void): void;
+ getWhisper(cd: (err: Error, whisper: string) => void): void;
+ }
+
+ interface PersonalApi {
+ listAccounts: string[] | undefined;
+ newAccount(password?: string): string;
+ unlockAccount(address: string, password?: string, duration?: number): boolean;
+ lockAccount(address: string): boolean;
+ sign(message: string, account: string, password: string): string;
+ sign(hexMessage: string, account: string, callback: (error: Error, signature: string) => void): void;
+ }
+
+ interface NetApi {
+ listening: boolean;
+ peerCount: boolean;
+ getListening(cd: (err: Error, listening: boolean) => void): void;
+ getPeerCount(cd: (err: Error, peerCount: number) => void): void;
+ }
+
+ type BlockParam = number | 'earliest' | 'latest' | 'pending';
+
+ type Unit =
+ | 'kwei'
+ | 'ada'
+ | 'mwei'
+ | 'babbage'
+ | 'gwei'
+ | 'shannon'
+ | 'szabo'
+ | 'finney'
+ | 'ether'
+ | 'kether'
+ | 'grand'
+ | 'einstein'
+ | 'mether'
+ | 'gether'
+ | 'tether';
+
+ interface SyncingState {
+ startingBlock: number;
+ currentBlock: number;
+ highestBlock: number;
+ }
+ type SyncingResult = false | SyncingState;
+
+ interface IsSyncing {
+ addCallback(cb: (err: Error, isSyncing: boolean, syncingState: SyncingState) => void): void;
+ stopWatching(): void;
+ }
+
+ interface AbstractBlock {
+ number: number | null;
+ hash: string | null;
+ parentHash: string;
+ nonce: string | null;
+ sha3Uncles: string;
+ logsBloom: string | null;
+ transactionsRoot: string;
+ stateRoot: string;
+ miner: string;
+ difficulty: BigNumber.BigNumber;
+ totalDifficulty: BigNumber.BigNumber;
+ extraData: string;
+ size: number;
+ gasLimit: number;
+ gasUsed: number;
+ timestamp: number;
+ uncles: string[];
+ }
+ interface BlockWithoutTransactionData extends AbstractBlock {
+ transactions: string[];
+ }
+ interface BlockWithTransactionData extends AbstractBlock {
+ transactions: Transaction[];
+ }
+
+ interface Transaction {
+ hash: string;
+ nonce: number;
+ blockHash: string | null;
+ blockNumber: number | null;
+ transactionIndex: number | null;
+ from: string;
+ to: string | null;
+ value: BigNumber.BigNumber;
+ gasPrice: BigNumber.BigNumber;
+ gas: number;
+ input: string;
+ }
+
+ interface CallTxDataBase {
+ to?: string;
+ value?: number | string | BigNumber.BigNumber;
+ gas?: number | string | BigNumber.BigNumber;
+ gasPrice?: number | string | BigNumber.BigNumber;
+ data?: string;
+ nonce?: number;
+ }
+
+ interface TxData extends CallTxDataBase {
+ from: string;
+ }
+
+ interface CallData extends CallTxDataBase {
+ from?: string;
+ }
+
+ interface TransactionReceipt {
+ blockHash: string;
+ blockNumber: number;
+ transactionHash: string;
+ transactionIndex: number;
+ from: string;
+ to: string;
+ status: null | string | 0 | 1;
+ cumulativeGasUsed: number;
+ gasUsed: number;
+ contractAddress: string | null;
+ logs: LogEntry[];
+ }
+
+ interface LogEntry {
+ logIndex: number | null;
+ transactionIndex: number | null;
+ transactionHash: string;
+ blockHash: string | null;
+ blockNumber: number | null;
+ address: string;
+ data: string;
+ topics: string[];
+ }
+ }
+ /* tslint:disable */
+ export = Web3;
+ /* tslint:enable */
}
diff --git a/packages/web3-typescript-typings/package.json b/packages/web3-typescript-typings/package.json
index edfdd9701..e0e433c93 100644
--- a/packages/web3-typescript-typings/package.json
+++ b/packages/web3-typescript-typings/package.json
@@ -1,30 +1,30 @@
{
- "name": "web3-typescript-typings",
- "version": "0.9.6",
- "description": "Typescript type definitions for web3",
- "main": "index.d.ts",
- "types": "index.d.ts",
- "scripts": {
- "lint": "tslint index.d.ts"
- },
- "repository": {
- "type": "git",
- "url": "git+https://github.com/0xProject/0x.js.git"
- },
- "author": "Fabio Berger",
- "contributors": ["Leonid Logvinov <logvinov.leon@gmail.com>"],
- "license": "Apache-2.0",
- "bugs": {
- "url": "https://github.com/0xProject/0x.js/issues"
- },
- "homepage": "https://github.com/0xProject/0x.js/packages/web3-typescript-typings#readme",
- "devDependencies": {
- "@types/bignumber.js": "^4.0.2",
- "tslint": "^5.5.0",
- "tslint-config-0xproject": "^0.0.2",
- "typescript": "~2.6.1"
- },
- "dependencies": {
- "bignumber.js": "~4.1.0"
- }
+ "name": "web3-typescript-typings",
+ "version": "0.9.6",
+ "description": "Typescript type definitions for web3",
+ "main": "index.d.ts",
+ "types": "index.d.ts",
+ "scripts": {
+ "lint": "tslint index.d.ts"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/0xProject/0x.js.git"
+ },
+ "author": "Fabio Berger",
+ "contributors": ["Leonid Logvinov <logvinov.leon@gmail.com>"],
+ "license": "Apache-2.0",
+ "bugs": {
+ "url": "https://github.com/0xProject/0x.js/issues"
+ },
+ "homepage": "https://github.com/0xProject/0x.js/packages/web3-typescript-typings#readme",
+ "devDependencies": {
+ "@types/bignumber.js": "^4.0.2",
+ "tslint": "^5.5.0",
+ "tslint-config-0xproject": "^0.0.2",
+ "typescript": "~2.6.1"
+ },
+ "dependencies": {
+ "bignumber.js": "~4.1.0"
+ }
}
diff --git a/packages/web3-typescript-typings/tslint.json b/packages/web3-typescript-typings/tslint.json
index ef528b22e..9a93a1f74 100644
--- a/packages/web3-typescript-typings/tslint.json
+++ b/packages/web3-typescript-typings/tslint.json
@@ -1,3 +1,3 @@
{
- "extends": ["tslint-config-0xproject"]
+ "extends": ["tslint-config-0xproject"]
}
diff --git a/packages/web3-wrapper/package.json b/packages/web3-wrapper/package.json
index d45480b35..6a65582f9 100644
--- a/packages/web3-wrapper/package.json
+++ b/packages/web3-wrapper/package.json
@@ -1,36 +1,36 @@
{
- "name": "@0xproject/web3-wrapper",
- "version": "0.1.7",
- "description": "Wraps around web3 and gives a nicer interface",
- "main": "lib/index.js",
- "types": "lib/index.d.ts",
- "scripts": {
- "build": "tsc",
- "clean": "shx rm -rf lib",
- "lint": "tslint --project . 'src/**/*.ts'"
- },
- "license": "Apache-2.0",
- "repository": {
- "type": "git",
- "url": "https://github.com/0xProject/0x.js.git"
- },
- "bugs": {
- "url": "https://github.com/0xProject/0x.js/issues"
- },
- "homepage": "https://github.com/0xProject/0x.js/packages/web3-wrapper/README.md",
- "devDependencies": {
- "@0xproject/tslint-config": "^0.4.4",
- "@0xproject/types": "^0.1.6",
- "@types/lodash": "^4.14.86",
- "npm-run-all": "^4.1.2",
- "shx": "^0.2.2",
- "tslint": "5.8.0",
- "typescript": "~2.6.1",
- "web3-typescript-typings": "^0.9.6"
- },
- "dependencies": {
- "@0xproject/utils": "^0.2.2",
- "lodash": "^4.17.4",
- "web3": "^0.20.0"
- }
+ "name": "@0xproject/web3-wrapper",
+ "version": "0.1.7",
+ "description": "Wraps around web3 and gives a nicer interface",
+ "main": "lib/index.js",
+ "types": "lib/index.d.ts",
+ "scripts": {
+ "build": "tsc",
+ "clean": "shx rm -rf lib",
+ "lint": "tslint --project . 'src/**/*.ts'"
+ },
+ "license": "Apache-2.0",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/0xProject/0x.js.git"
+ },
+ "bugs": {
+ "url": "https://github.com/0xProject/0x.js/issues"
+ },
+ "homepage": "https://github.com/0xProject/0x.js/packages/web3-wrapper/README.md",
+ "devDependencies": {
+ "@0xproject/tslint-config": "^0.4.4",
+ "@0xproject/types": "^0.1.6",
+ "@types/lodash": "^4.14.86",
+ "npm-run-all": "^4.1.2",
+ "shx": "^0.2.2",
+ "tslint": "5.8.0",
+ "typescript": "~2.6.1",
+ "web3-typescript-typings": "^0.9.6"
+ },
+ "dependencies": {
+ "@0xproject/utils": "^0.2.2",
+ "lodash": "^4.17.4",
+ "web3": "^0.20.0"
+ }
}
diff --git a/packages/web3-wrapper/src/index.ts b/packages/web3-wrapper/src/index.ts
index 859333448..c4826f2be 100644
--- a/packages/web3-wrapper/src/index.ts
+++ b/packages/web3-wrapper/src/index.ts
@@ -4,170 +4,170 @@ import * as _ from 'lodash';
import * as Web3 from 'web3';
interface RawLogEntry {
- logIndex: string | null;
- transactionIndex: string | null;
- transactionHash: string;
- blockHash: string | null;
- blockNumber: string | null;
- address: string;
- data: string;
- topics: string[];
+ logIndex: string | null;
+ transactionIndex: string | null;
+ transactionHash: string;
+ blockHash: string | null;
+ blockNumber: string | null;
+ address: string;
+ data: string;
+ topics: string[];
}
export class Web3Wrapper {
- private _web3: Web3;
- private _defaults: Partial<TxData>;
- private _jsonRpcRequestId: number;
- constructor(provider: Web3.Provider, defaults?: Partial<TxData>) {
- if (_.isUndefined((provider as any).sendAsync)) {
- // Web3@1.0 provider doesn't support synchronous http requests,
- // so it only has an async `send` method, instead of a `send` and `sendAsync` in web3@0.x.x`
- // We re-assign the send method so that Web3@1.0 providers work with 0x.js
- (provider as any).sendAsync = (provider as any).send;
- }
- this._web3 = new Web3();
- this._web3.setProvider(provider);
- this._defaults = defaults || {};
- this._jsonRpcRequestId = 0;
- }
- public getContractDefaults(): Partial<TxData> {
- return this._defaults;
- }
- public setProvider(provider: Web3.Provider) {
- this._web3.setProvider(provider);
- }
- public isAddress(address: string): boolean {
- return this._web3.isAddress(address);
- }
- public async isSenderAddressAvailableAsync(senderAddress: string): Promise<boolean> {
- const addresses = await this.getAvailableAddressesAsync();
- return _.includes(addresses, senderAddress);
- }
- public async getNodeVersionAsync(): Promise<string> {
- const nodeVersion = await promisify<string>(this._web3.version.getNode)();
- return nodeVersion;
- }
- public async getNetworkIdAsync(): Promise<number> {
- const networkIdStr = await promisify<string>(this._web3.version.getNetwork)();
- const networkId = _.parseInt(networkIdStr);
- return networkId;
- }
- public async getTransactionReceiptAsync(txHash: string): Promise<TransactionReceipt> {
- const transactionReceipt = await promisify<TransactionReceipt>(this._web3.eth.getTransactionReceipt)(txHash);
- if (!_.isNull(transactionReceipt)) {
- transactionReceipt.status = this._normalizeTxReceiptStatus(transactionReceipt.status);
- }
- return transactionReceipt;
- }
- public getCurrentProvider(): Web3.Provider {
- return this._web3.currentProvider;
- }
- public toWei(ethAmount: BigNumber): BigNumber {
- const balanceWei = this._web3.toWei(ethAmount, 'ether');
- return balanceWei;
- }
- public async getBalanceInWeiAsync(owner: string): Promise<BigNumber> {
- let balanceInWei = await promisify<BigNumber>(this._web3.eth.getBalance)(owner);
- // Rewrap in a new BigNumber
- balanceInWei = new BigNumber(balanceInWei);
- return balanceInWei;
- }
- public async doesContractExistAtAddressAsync(address: string): Promise<boolean> {
- const code = await promisify<string>(this._web3.eth.getCode)(address);
- // Regex matches 0x0, 0x00, 0x in order to accommodate poorly implemented clients
- const codeIsEmpty = /^0x0{0,40}$/i.test(code);
- return !codeIsEmpty;
- }
- public async signTransactionAsync(address: string, message: string): Promise<string> {
- const signData = await promisify<string>(this._web3.eth.sign)(address, message);
- return signData;
- }
- public async getBlockNumberAsync(): Promise<number> {
- const blockNumber = await promisify<number>(this._web3.eth.getBlockNumber)();
- return blockNumber;
- }
- public async getBlockAsync(blockParam: string | Web3.BlockParam): Promise<Web3.BlockWithoutTransactionData> {
- const block = await promisify<Web3.BlockWithoutTransactionData>(this._web3.eth.getBlock)(blockParam);
- return block;
- }
- public async getBlockTimestampAsync(blockParam: string | Web3.BlockParam): Promise<number> {
- const { timestamp } = await this.getBlockAsync(blockParam);
- return timestamp;
- }
- public async getAvailableAddressesAsync(): Promise<string[]> {
- const addresses = await promisify<string[]>(this._web3.eth.getAccounts)();
- return addresses;
- }
- public async getLogsAsync(filter: Web3.FilterObject): Promise<Web3.LogEntry[]> {
- let fromBlock = filter.fromBlock;
- if (_.isNumber(fromBlock)) {
- fromBlock = this._web3.toHex(fromBlock);
- }
- let toBlock = filter.toBlock;
- if (_.isNumber(toBlock)) {
- toBlock = this._web3.toHex(toBlock);
- }
- const serializedFilter = {
- ...filter,
- fromBlock,
- toBlock,
- };
- const payload = {
- jsonrpc: '2.0',
- id: this._jsonRpcRequestId++,
- method: 'eth_getLogs',
- params: [serializedFilter],
- };
- const rawLogs = await this._sendRawPayloadAsync<RawLogEntry[]>(payload);
- const formattedLogs = _.map(rawLogs, this._formatLog.bind(this));
- return formattedLogs;
- }
- public getContractFromAbi(abi: Web3.ContractAbi): Web3.Contract<any> {
- const web3Contract = this._web3.eth.contract(abi);
- return web3Contract;
- }
- public getContractInstance(abi: Web3.ContractAbi, address: string): Web3.ContractInstance {
- const web3ContractInstance = this.getContractFromAbi(abi).at(address);
- return web3ContractInstance;
- }
- public async estimateGasAsync(data: string): Promise<number> {
- const gas = await promisify<number>(this._web3.eth.estimateGas)({ data });
- return gas;
- }
- private async _sendRawPayloadAsync<A>(payload: Web3.JSONRPCRequestPayload): Promise<A> {
- const sendAsync = this._web3.currentProvider.sendAsync.bind(this._web3.currentProvider);
- const response = await promisify<Web3.JSONRPCResponsePayload>(sendAsync)(payload);
- const result = response.result;
- return result;
- }
- private _normalizeTxReceiptStatus(status: undefined | null | string | 0 | 1): null | 0 | 1 {
- // Transaction status might have four values
- // undefined - Testrpc and other old clients
- // null - New clients on old transactions
- // number - Parity
- // hex - Geth
- if (_.isString(status)) {
- return this._web3.toDecimal(status) as 0 | 1;
- } else if (_.isUndefined(status)) {
- return null;
- } else {
- return status;
- }
- }
- private _formatLog(rawLog: RawLogEntry): Web3.LogEntry {
- const formattedLog = {
- ...rawLog,
- logIndex: this._hexToDecimal(rawLog.logIndex),
- blockNumber: this._hexToDecimal(rawLog.blockNumber),
- transactionIndex: this._hexToDecimal(rawLog.transactionIndex),
- };
- return formattedLog;
- }
- private _hexToDecimal(hex: string | null): number | null {
- if (_.isNull(hex)) {
- return null;
- }
- const decimal = this._web3.toDecimal(hex);
- return decimal;
- }
+ private _web3: Web3;
+ private _defaults: Partial<TxData>;
+ private _jsonRpcRequestId: number;
+ constructor(provider: Web3.Provider, defaults?: Partial<TxData>) {
+ if (_.isUndefined((provider as any).sendAsync)) {
+ // Web3@1.0 provider doesn't support synchronous http requests,
+ // so it only has an async `send` method, instead of a `send` and `sendAsync` in web3@0.x.x`
+ // We re-assign the send method so that Web3@1.0 providers work with 0x.js
+ (provider as any).sendAsync = (provider as any).send;
+ }
+ this._web3 = new Web3();
+ this._web3.setProvider(provider);
+ this._defaults = defaults || {};
+ this._jsonRpcRequestId = 0;
+ }
+ public getContractDefaults(): Partial<TxData> {
+ return this._defaults;
+ }
+ public setProvider(provider: Web3.Provider) {
+ this._web3.setProvider(provider);
+ }
+ public isAddress(address: string): boolean {
+ return this._web3.isAddress(address);
+ }
+ public async isSenderAddressAvailableAsync(senderAddress: string): Promise<boolean> {
+ const addresses = await this.getAvailableAddressesAsync();
+ return _.includes(addresses, senderAddress);
+ }
+ public async getNodeVersionAsync(): Promise<string> {
+ const nodeVersion = await promisify<string>(this._web3.version.getNode)();
+ return nodeVersion;
+ }
+ public async getNetworkIdAsync(): Promise<number> {
+ const networkIdStr = await promisify<string>(this._web3.version.getNetwork)();
+ const networkId = _.parseInt(networkIdStr);
+ return networkId;
+ }
+ public async getTransactionReceiptAsync(txHash: string): Promise<TransactionReceipt> {
+ const transactionReceipt = await promisify<TransactionReceipt>(this._web3.eth.getTransactionReceipt)(txHash);
+ if (!_.isNull(transactionReceipt)) {
+ transactionReceipt.status = this._normalizeTxReceiptStatus(transactionReceipt.status);
+ }
+ return transactionReceipt;
+ }
+ public getCurrentProvider(): Web3.Provider {
+ return this._web3.currentProvider;
+ }
+ public toWei(ethAmount: BigNumber): BigNumber {
+ const balanceWei = this._web3.toWei(ethAmount, 'ether');
+ return balanceWei;
+ }
+ public async getBalanceInWeiAsync(owner: string): Promise<BigNumber> {
+ let balanceInWei = await promisify<BigNumber>(this._web3.eth.getBalance)(owner);
+ // Rewrap in a new BigNumber
+ balanceInWei = new BigNumber(balanceInWei);
+ return balanceInWei;
+ }
+ public async doesContractExistAtAddressAsync(address: string): Promise<boolean> {
+ const code = await promisify<string>(this._web3.eth.getCode)(address);
+ // Regex matches 0x0, 0x00, 0x in order to accommodate poorly implemented clients
+ const codeIsEmpty = /^0x0{0,40}$/i.test(code);
+ return !codeIsEmpty;
+ }
+ public async signTransactionAsync(address: string, message: string): Promise<string> {
+ const signData = await promisify<string>(this._web3.eth.sign)(address, message);
+ return signData;
+ }
+ public async getBlockNumberAsync(): Promise<number> {
+ const blockNumber = await promisify<number>(this._web3.eth.getBlockNumber)();
+ return blockNumber;
+ }
+ public async getBlockAsync(blockParam: string | Web3.BlockParam): Promise<Web3.BlockWithoutTransactionData> {
+ const block = await promisify<Web3.BlockWithoutTransactionData>(this._web3.eth.getBlock)(blockParam);
+ return block;
+ }
+ public async getBlockTimestampAsync(blockParam: string | Web3.BlockParam): Promise<number> {
+ const { timestamp } = await this.getBlockAsync(blockParam);
+ return timestamp;
+ }
+ public async getAvailableAddressesAsync(): Promise<string[]> {
+ const addresses = await promisify<string[]>(this._web3.eth.getAccounts)();
+ return addresses;
+ }
+ public async getLogsAsync(filter: Web3.FilterObject): Promise<Web3.LogEntry[]> {
+ let fromBlock = filter.fromBlock;
+ if (_.isNumber(fromBlock)) {
+ fromBlock = this._web3.toHex(fromBlock);
+ }
+ let toBlock = filter.toBlock;
+ if (_.isNumber(toBlock)) {
+ toBlock = this._web3.toHex(toBlock);
+ }
+ const serializedFilter = {
+ ...filter,
+ fromBlock,
+ toBlock,
+ };
+ const payload = {
+ jsonrpc: '2.0',
+ id: this._jsonRpcRequestId++,
+ method: 'eth_getLogs',
+ params: [serializedFilter],
+ };
+ const rawLogs = await this._sendRawPayloadAsync<RawLogEntry[]>(payload);
+ const formattedLogs = _.map(rawLogs, this._formatLog.bind(this));
+ return formattedLogs;
+ }
+ public getContractFromAbi(abi: Web3.ContractAbi): Web3.Contract<any> {
+ const web3Contract = this._web3.eth.contract(abi);
+ return web3Contract;
+ }
+ public getContractInstance(abi: Web3.ContractAbi, address: string): Web3.ContractInstance {
+ const web3ContractInstance = this.getContractFromAbi(abi).at(address);
+ return web3ContractInstance;
+ }
+ public async estimateGasAsync(data: string): Promise<number> {
+ const gas = await promisify<number>(this._web3.eth.estimateGas)({ data });
+ return gas;
+ }
+ private async _sendRawPayloadAsync<A>(payload: Web3.JSONRPCRequestPayload): Promise<A> {
+ const sendAsync = this._web3.currentProvider.sendAsync.bind(this._web3.currentProvider);
+ const response = await promisify<Web3.JSONRPCResponsePayload>(sendAsync)(payload);
+ const result = response.result;
+ return result;
+ }
+ private _normalizeTxReceiptStatus(status: undefined | null | string | 0 | 1): null | 0 | 1 {
+ // Transaction status might have four values
+ // undefined - Testrpc and other old clients
+ // null - New clients on old transactions
+ // number - Parity
+ // hex - Geth
+ if (_.isString(status)) {
+ return this._web3.toDecimal(status) as 0 | 1;
+ } else if (_.isUndefined(status)) {
+ return null;
+ } else {
+ return status;
+ }
+ }
+ private _formatLog(rawLog: RawLogEntry): Web3.LogEntry {
+ const formattedLog = {
+ ...rawLog,
+ logIndex: this._hexToDecimal(rawLog.logIndex),
+ blockNumber: this._hexToDecimal(rawLog.blockNumber),
+ transactionIndex: this._hexToDecimal(rawLog.transactionIndex),
+ };
+ return formattedLog;
+ }
+ private _hexToDecimal(hex: string | null): number | null {
+ if (_.isNull(hex)) {
+ return null;
+ }
+ const decimal = this._web3.toDecimal(hex);
+ return decimal;
+ }
}
diff --git a/packages/web3-wrapper/tsconfig.json b/packages/web3-wrapper/tsconfig.json
index fc88c962d..3d967d05f 100644
--- a/packages/web3-wrapper/tsconfig.json
+++ b/packages/web3-wrapper/tsconfig.json
@@ -1,7 +1,7 @@
{
- "extends": "../../tsconfig",
- "compilerOptions": {
- "outDir": "lib"
- },
- "include": ["./src/**/*", "../../node_modules/web3-typescript-typings/index.d.ts"]
+ "extends": "../../tsconfig",
+ "compilerOptions": {
+ "outDir": "lib"
+ },
+ "include": ["./src/**/*", "../../node_modules/web3-typescript-typings/index.d.ts"]
}
diff --git a/packages/web3-wrapper/tslint.json b/packages/web3-wrapper/tslint.json
index e63054bfc..ffaefe83a 100644
--- a/packages/web3-wrapper/tslint.json
+++ b/packages/web3-wrapper/tslint.json
@@ -1,3 +1,3 @@
{
- "extends": ["@0xproject/tslint-config"]
+ "extends": ["@0xproject/tslint-config"]
}
diff --git a/packages/website/contracts/Mintable.json b/packages/website/contracts/Mintable.json
index eaadcf68f..dc46dc829 100644
--- a/packages/website/contracts/Mintable.json
+++ b/packages/website/contracts/Mintable.json
@@ -1,190 +1,190 @@
{
- "contract_name": "Mintable",
- "abi": [
- {
- "constant": false,
- "inputs": [
- {
- "name": "_spender",
- "type": "address"
- },
- {
- "name": "_value",
- "type": "uint256"
- }
- ],
- "name": "approve",
- "outputs": [
- {
- "name": "",
- "type": "bool"
- }
- ],
- "payable": false,
- "type": "function"
- },
- {
- "constant": true,
- "inputs": [],
- "name": "totalSupply",
- "outputs": [
- {
- "name": "",
- "type": "uint256"
- }
- ],
- "payable": false,
- "type": "function"
- },
- {
- "constant": false,
- "inputs": [
- {
- "name": "_from",
- "type": "address"
- },
- {
- "name": "_to",
- "type": "address"
- },
- {
- "name": "_value",
- "type": "uint256"
- }
- ],
- "name": "transferFrom",
- "outputs": [
- {
- "name": "",
- "type": "bool"
- }
- ],
- "payable": false,
- "type": "function"
- },
- {
- "constant": true,
- "inputs": [
- {
- "name": "_owner",
- "type": "address"
- }
- ],
- "name": "balanceOf",
- "outputs": [
- {
- "name": "",
- "type": "uint256"
- }
- ],
- "payable": false,
- "type": "function"
- },
- {
- "constant": false,
- "inputs": [
- {
- "name": "_value",
- "type": "uint256"
- }
- ],
- "name": "mint",
- "outputs": [],
- "payable": false,
- "type": "function"
- },
- {
- "constant": false,
- "inputs": [
- {
- "name": "_to",
- "type": "address"
- },
- {
- "name": "_value",
- "type": "uint256"
- }
- ],
- "name": "transfer",
- "outputs": [
- {
- "name": "",
- "type": "bool"
- }
- ],
- "payable": false,
- "type": "function"
- },
- {
- "constant": true,
- "inputs": [
- {
- "name": "_owner",
- "type": "address"
- },
- {
- "name": "_spender",
- "type": "address"
- }
- ],
- "name": "allowance",
- "outputs": [
- {
- "name": "",
- "type": "uint256"
- }
- ],
- "payable": false,
- "type": "function"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "name": "_from",
- "type": "address"
- },
- {
- "indexed": true,
- "name": "_to",
- "type": "address"
- },
- {
- "indexed": false,
- "name": "_value",
- "type": "uint256"
- }
- ],
- "name": "Transfer",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "name": "_owner",
- "type": "address"
- },
- {
- "indexed": true,
- "name": "_spender",
- "type": "address"
- },
- {
- "indexed": false,
- "name": "_value",
- "type": "uint256"
- }
- ],
- "name": "Approval",
- "type": "event"
- }
- ],
- "unlinked_binary":
- "0x6060604052341561000c57fe5b5b6105018061001c6000396000f300606060405236156100675763ffffffff60e060020a600035041663095ea7b3811461006957806318160ddd1461009c57806323b872dd146100be57806370a08231146100f7578063a0712d6814610125578063a9059cbb1461013a578063dd62ed3e1461016d575bfe5b341561007157fe5b610088600160a060020a03600435166024356101a1565b604080519115158252519081900360200190f35b34156100a457fe5b6100ac61020c565b60408051918252519081900360200190f35b34156100c657fe5b610088600160a060020a0360043581169060243516604435610212565b604080519115158252519081900360200190f35b34156100ff57fe5b6100ac600160a060020a0360043516610335565b60408051918252519081900360200190f35b341561012d57fe5b610138600435610354565b005b341561014257fe5b610088600160a060020a03600435166024356103bc565b604080519115158252519081900360200190f35b341561017557fe5b6100ac600160a060020a036004358116906024351661046e565b60408051918252519081900360200190f35b600160a060020a03338116600081815260016020908152604080832094871680845294825280832086905580518681529051929493927f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925929181900390910190a35060015b92915050565b60025481565b600160a060020a03808416600081815260016020908152604080832033909516835293815283822054928252819052918220548390108015906102555750828110155b801561027b5750600160a060020a03841660009081526020819052604090205483810110155b1561032757600160a060020a03808516600090815260208190526040808220805487019055918716815220805484900390556000198110156102e557600160a060020a03808616600090815260016020908152604080832033909416835292905220805484900390555b83600160a060020a031685600160a060020a03166000805160206104b6833981519152856040518082815260200191505060405180910390a36001915061032c565b600091505b5b509392505050565b600160a060020a0381166000908152602081905260409020545b919050565b68056bc75e2d6310000081111561036b5760006000fd5b600160a060020a03331660009081526020819052604090205461038f90829061049b565b600160a060020a0333166000908152602081905260409020556002546103b5908261049b565b6002555b50565b600160a060020a0333166000908152602081905260408120548290108015906103ff5750600160a060020a03831660009081526020819052604090205482810110155b1561045f57600160a060020a0333811660008181526020818152604080832080548890039055938716808352918490208054870190558351868152935191936000805160206104b6833981519152929081900390910190a3506001610206565b506000610206565b5b92915050565b600160a060020a038083166000908152600160209081526040808320938516835292905220545b92915050565b6000828201838110156104aa57fe5b8091505b50929150505600ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa165627a7a72305820998c8326b9629e063eb4867166e72c68a8c2e3ebca6a9d35ebc78c041c7aa47b0029",
- "networks": {},
- "schema_version": "0.0.5",
- "updated_at": 1503413048892
+ "contract_name": "Mintable",
+ "abi": [
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "name": "_spender",
+ "type": "address"
+ },
+ {
+ "name": "_value",
+ "type": "uint256"
+ }
+ ],
+ "name": "approve",
+ "outputs": [
+ {
+ "name": "",
+ "type": "bool"
+ }
+ ],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "totalSupply",
+ "outputs": [
+ {
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "name": "_from",
+ "type": "address"
+ },
+ {
+ "name": "_to",
+ "type": "address"
+ },
+ {
+ "name": "_value",
+ "type": "uint256"
+ }
+ ],
+ "name": "transferFrom",
+ "outputs": [
+ {
+ "name": "",
+ "type": "bool"
+ }
+ ],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "name": "_owner",
+ "type": "address"
+ }
+ ],
+ "name": "balanceOf",
+ "outputs": [
+ {
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "name": "_value",
+ "type": "uint256"
+ }
+ ],
+ "name": "mint",
+ "outputs": [],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "name": "_to",
+ "type": "address"
+ },
+ {
+ "name": "_value",
+ "type": "uint256"
+ }
+ ],
+ "name": "transfer",
+ "outputs": [
+ {
+ "name": "",
+ "type": "bool"
+ }
+ ],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "name": "_owner",
+ "type": "address"
+ },
+ {
+ "name": "_spender",
+ "type": "address"
+ }
+ ],
+ "name": "allowance",
+ "outputs": [
+ {
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "type": "function"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "name": "_from",
+ "type": "address"
+ },
+ {
+ "indexed": true,
+ "name": "_to",
+ "type": "address"
+ },
+ {
+ "indexed": false,
+ "name": "_value",
+ "type": "uint256"
+ }
+ ],
+ "name": "Transfer",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "name": "_owner",
+ "type": "address"
+ },
+ {
+ "indexed": true,
+ "name": "_spender",
+ "type": "address"
+ },
+ {
+ "indexed": false,
+ "name": "_value",
+ "type": "uint256"
+ }
+ ],
+ "name": "Approval",
+ "type": "event"
+ }
+ ],
+ "unlinked_binary":
+ "0x6060604052341561000c57fe5b5b6105018061001c6000396000f300606060405236156100675763ffffffff60e060020a600035041663095ea7b3811461006957806318160ddd1461009c57806323b872dd146100be57806370a08231146100f7578063a0712d6814610125578063a9059cbb1461013a578063dd62ed3e1461016d575bfe5b341561007157fe5b610088600160a060020a03600435166024356101a1565b604080519115158252519081900360200190f35b34156100a457fe5b6100ac61020c565b60408051918252519081900360200190f35b34156100c657fe5b610088600160a060020a0360043581169060243516604435610212565b604080519115158252519081900360200190f35b34156100ff57fe5b6100ac600160a060020a0360043516610335565b60408051918252519081900360200190f35b341561012d57fe5b610138600435610354565b005b341561014257fe5b610088600160a060020a03600435166024356103bc565b604080519115158252519081900360200190f35b341561017557fe5b6100ac600160a060020a036004358116906024351661046e565b60408051918252519081900360200190f35b600160a060020a03338116600081815260016020908152604080832094871680845294825280832086905580518681529051929493927f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925929181900390910190a35060015b92915050565b60025481565b600160a060020a03808416600081815260016020908152604080832033909516835293815283822054928252819052918220548390108015906102555750828110155b801561027b5750600160a060020a03841660009081526020819052604090205483810110155b1561032757600160a060020a03808516600090815260208190526040808220805487019055918716815220805484900390556000198110156102e557600160a060020a03808616600090815260016020908152604080832033909416835292905220805484900390555b83600160a060020a031685600160a060020a03166000805160206104b6833981519152856040518082815260200191505060405180910390a36001915061032c565b600091505b5b509392505050565b600160a060020a0381166000908152602081905260409020545b919050565b68056bc75e2d6310000081111561036b5760006000fd5b600160a060020a03331660009081526020819052604090205461038f90829061049b565b600160a060020a0333166000908152602081905260409020556002546103b5908261049b565b6002555b50565b600160a060020a0333166000908152602081905260408120548290108015906103ff5750600160a060020a03831660009081526020819052604090205482810110155b1561045f57600160a060020a0333811660008181526020818152604080832080548890039055938716808352918490208054870190558351868152935191936000805160206104b6833981519152929081900390910190a3506001610206565b506000610206565b5b92915050565b600160a060020a038083166000908152600160209081526040808320938516835292905220545b92915050565b6000828201838110156104aa57fe5b8091505b50929150505600ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa165627a7a72305820998c8326b9629e063eb4867166e72c68a8c2e3ebca6a9d35ebc78c041c7aa47b0029",
+ "networks": {},
+ "schema_version": "0.0.5",
+ "updated_at": 1503413048892
}
diff --git a/packages/website/md/docs/0xjs/async.md b/packages/website/md/docs/0xjs/async.md
index fa92e5bab..8abaef331 100644
--- a/packages/website/md/docs/0xjs/async.md
+++ b/packages/website/md/docs/0xjs/async.md
@@ -14,13 +14,13 @@ _Promise syntax:_
```javascript
zeroEx
- .getAvailableAddressesAsync()
- .then(function(availableAddresses) {
- console.log(availableAddresses);
- })
- .catch(function(error) {
- console.log('Caught error: ', error);
- });
+ .getAvailableAddressesAsync()
+ .then(function(availableAddresses) {
+ console.log(availableAddresses);
+ })
+ .catch(function(error) {
+ console.log('Caught error: ', error);
+ });
```
As is the convention with promise-based libraries, if an error occurs, it is thrown. It is the callers responsibility to catch thrown errors and to handle them appropriately.
diff --git a/packages/website/package.json b/packages/website/package.json
index ac1bd2c13..6db74a20e 100644
--- a/packages/website/package.json
+++ b/packages/website/package.json
@@ -1,109 +1,109 @@
{
- "name": "@0xproject/website",
- "version": "0.0.9",
- "private": true,
- "description": "Website and 0x portal dapp",
- "scripts": {
- "build": "NODE_ENV=production webpack; exit 0;",
- "clean": "shx rm -f public/bundle*",
- "lint": "tslint --project . 'ts/**/*.ts' 'ts/**/*.tsx'",
- "dev": "webpack-dev-server --content-base public --https",
- "update_contracts":
- "for i in ${npm_package_config_artifacts}; do copyfiles -u 4 ../contracts/build/contracts/$i.json ../website/contracts; done;",
- "deploy_staging":
- "npm run build; aws s3 sync ./public/. s3://staging-0xproject --profile 0xproject --region us-east-1 --grants read=uri=http://acs.amazonaws.com/groups/global/AllUsers",
- "deploy_live":
- "npm run build; aws s3 sync ./public/. s3://0xproject.com --profile 0xproject --region us-east-1 --grants read=uri=http://acs.amazonaws.com/groups/global/AllUsers"
- },
- "config": {
- "artifacts": "Mintable"
- },
- "author": "Fabio Berger",
- "license": "Apache-2.0",
- "dependencies": {
- "0x.js": "^0.30.2",
- "@0xproject/subproviders": "^0.3.3",
- "@0xproject/utils": "^0.2.2",
- "accounting": "^0.4.1",
- "basscss": "^8.0.3",
- "blockies": "^0.0.2",
- "compare-versions": "^3.0.1",
- "dateformat": "^2.0.0",
- "deep-equal": "^1.0.1",
- "dharma-loan-frame": "^0.0.12",
- "ethereumjs-tx": "^1.3.3",
- "ethereumjs-util": "^5.1.1",
- "find-versions": "^2.0.0",
- "is-mobile": "^0.2.2",
- "jsonschema": "^1.2.0",
- "ledgerco": "0xProject/ledger-node-js-api",
- "less": "^2.7.2",
- "lodash": "^4.17.4",
- "material-ui": "^0.17.1",
- "moment": "^2.18.1",
- "query-string": "^5.0.1",
- "react": "15.6.1",
- "react-copy-to-clipboard": "^4.2.3",
- "react-document-title": "^2.0.3",
- "react-dom": "15.6.1",
- "react-highlight": "^0.10.0",
- "react-html5video": "^2.1.0",
- "react-inlinesvg": "^0.5.5",
- "react-markdown": "^2.5.0",
- "react-recaptcha": "^2.3.2",
- "react-redux": "^5.0.3",
- "react-router-dom": "^4.1.1",
- "react-router-hash-link": "^1.1.0",
- "react-scroll": "^1.5.2",
- "react-tap-event-plugin": "^2.0.1",
- "react-tooltip": "^3.2.7",
- "react-waypoint": "^7.0.4",
- "redux": "^3.6.0",
- "scroll-to-element": "^2.0.0",
- "semver-sort": "0.0.4",
- "thenby": "^1.2.3",
- "truffle-contract": "2.0.1",
- "tslint-config-0xproject": "^0.0.2",
- "web3": "^0.20.0",
- "web3-provider-engine": "^13.0.1",
- "whatwg-fetch": "^2.0.3",
- "xml-js": "^1.3.2"
- },
- "devDependencies": {
- "@types/accounting": "^0.4.1",
- "@types/dateformat": "^1.0.1",
- "@types/deep-equal": "^1.0.0",
- "@types/jsonschema": "^1.1.1",
- "@types/lodash": "^4.14.86",
- "@types/material-ui": "0.18.0",
- "@types/moment": "^2.13.0",
- "@types/node": "^8.0.53",
- "@types/query-string": "^5.0.1",
- "@types/react": "^15.0.15",
- "@types/react-copy-to-clipboard": "^4.2.0",
- "@types/react-dom": "^0.14.23",
- "@types/react-redux": "^4.4.37",
- "@types/react-router-dom": "^4.0.4",
- "@types/react-scroll": "0.0.31",
- "@types/react-tap-event-plugin": "0.0.30",
- "@types/redux": "^3.6.0",
- "awesome-typescript-loader": "^3.1.3",
- "copy-webpack-plugin": "^4.0.1",
- "copyfiles": "^1.2.0",
- "css-loader": "0.23.x",
- "exports-loader": "0.6.x",
- "imports-loader": "0.6.x",
- "json-loader": "^0.5.4",
- "less-loader": "^2.2.3",
- "raw-loader": "^0.5.1",
- "shx": "^0.2.2",
- "source-map-loader": "^0.1.6",
- "style-loader": "0.13.x",
- "tslint": "5.8.0",
- "typescript": "~2.6.1",
- "web3-typescript-typings": "^0.9.6",
- "webpack": "^3.1.0",
- "webpack-dev-middleware": "^1.10.0",
- "webpack-dev-server": "^2.5.0"
- }
+ "name": "@0xproject/website",
+ "version": "0.0.9",
+ "private": true,
+ "description": "Website and 0x portal dapp",
+ "scripts": {
+ "build": "NODE_ENV=production webpack; exit 0;",
+ "clean": "shx rm -f public/bundle*",
+ "lint": "tslint --project . 'ts/**/*.ts' 'ts/**/*.tsx'",
+ "dev": "webpack-dev-server --content-base public --https",
+ "update_contracts":
+ "for i in ${npm_package_config_artifacts}; do copyfiles -u 4 ../contracts/build/contracts/$i.json ../website/contracts; done;",
+ "deploy_staging":
+ "npm run build; aws s3 sync ./public/. s3://staging-0xproject --profile 0xproject --region us-east-1 --grants read=uri=http://acs.amazonaws.com/groups/global/AllUsers",
+ "deploy_live":
+ "npm run build; aws s3 sync ./public/. s3://0xproject.com --profile 0xproject --region us-east-1 --grants read=uri=http://acs.amazonaws.com/groups/global/AllUsers"
+ },
+ "config": {
+ "artifacts": "Mintable"
+ },
+ "author": "Fabio Berger",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "0x.js": "^0.30.2",
+ "@0xproject/subproviders": "^0.3.3",
+ "@0xproject/utils": "^0.2.2",
+ "accounting": "^0.4.1",
+ "basscss": "^8.0.3",
+ "blockies": "^0.0.2",
+ "compare-versions": "^3.0.1",
+ "dateformat": "^2.0.0",
+ "deep-equal": "^1.0.1",
+ "dharma-loan-frame": "^0.0.12",
+ "ethereumjs-tx": "^1.3.3",
+ "ethereumjs-util": "^5.1.1",
+ "find-versions": "^2.0.0",
+ "is-mobile": "^0.2.2",
+ "jsonschema": "^1.2.0",
+ "ledgerco": "0xProject/ledger-node-js-api",
+ "less": "^2.7.2",
+ "lodash": "^4.17.4",
+ "material-ui": "^0.17.1",
+ "moment": "^2.18.1",
+ "query-string": "^5.0.1",
+ "react": "15.6.1",
+ "react-copy-to-clipboard": "^4.2.3",
+ "react-document-title": "^2.0.3",
+ "react-dom": "15.6.1",
+ "react-highlight": "^0.10.0",
+ "react-html5video": "^2.1.0",
+ "react-inlinesvg": "^0.5.5",
+ "react-markdown": "^2.5.0",
+ "react-recaptcha": "^2.3.2",
+ "react-redux": "^5.0.3",
+ "react-router-dom": "^4.1.1",
+ "react-router-hash-link": "^1.1.0",
+ "react-scroll": "^1.5.2",
+ "react-tap-event-plugin": "^2.0.1",
+ "react-tooltip": "^3.2.7",
+ "react-waypoint": "^7.0.4",
+ "redux": "^3.6.0",
+ "scroll-to-element": "^2.0.0",
+ "semver-sort": "0.0.4",
+ "thenby": "^1.2.3",
+ "truffle-contract": "2.0.1",
+ "tslint-config-0xproject": "^0.0.2",
+ "web3": "^0.20.0",
+ "web3-provider-engine": "^13.0.1",
+ "whatwg-fetch": "^2.0.3",
+ "xml-js": "^1.3.2"
+ },
+ "devDependencies": {
+ "@types/accounting": "^0.4.1",
+ "@types/dateformat": "^1.0.1",
+ "@types/deep-equal": "^1.0.0",
+ "@types/jsonschema": "^1.1.1",
+ "@types/lodash": "^4.14.86",
+ "@types/material-ui": "0.18.0",
+ "@types/moment": "^2.13.0",
+ "@types/node": "^8.0.53",
+ "@types/query-string": "^5.0.1",
+ "@types/react": "^15.0.15",
+ "@types/react-copy-to-clipboard": "^4.2.0",
+ "@types/react-dom": "^0.14.23",
+ "@types/react-redux": "^4.4.37",
+ "@types/react-router-dom": "^4.0.4",
+ "@types/react-scroll": "0.0.31",
+ "@types/react-tap-event-plugin": "0.0.30",
+ "@types/redux": "^3.6.0",
+ "awesome-typescript-loader": "^3.1.3",
+ "copy-webpack-plugin": "^4.0.1",
+ "copyfiles": "^1.2.0",
+ "css-loader": "0.23.x",
+ "exports-loader": "0.6.x",
+ "imports-loader": "0.6.x",
+ "json-loader": "^0.5.4",
+ "less-loader": "^2.2.3",
+ "raw-loader": "^0.5.1",
+ "shx": "^0.2.2",
+ "source-map-loader": "^0.1.6",
+ "style-loader": "0.13.x",
+ "tslint": "5.8.0",
+ "typescript": "~2.6.1",
+ "web3-typescript-typings": "^0.9.6",
+ "webpack": "^3.1.0",
+ "webpack-dev-middleware": "^1.10.0",
+ "webpack-dev-server": "^2.5.0"
+ }
}
diff --git a/packages/website/ts/blockchain.ts b/packages/website/ts/blockchain.ts
index 711c3329d..5530701c0 100644
--- a/packages/website/ts/blockchain.ts
+++ b/packages/website/ts/blockchain.ts
@@ -1,25 +1,25 @@
import {
- BlockParam,
- BlockRange,
- DecodedLogEvent,
- ExchangeContractEventArgs,
- ExchangeEvents,
- IndexedFilterValues,
- LogCancelContractEventArgs,
- LogFillContractEventArgs,
- LogWithDecodedArgs,
- Order,
- SignedOrder,
- Token as ZeroExToken,
- TransactionReceiptWithDecodedLogs,
- ZeroEx,
+ BlockParam,
+ BlockRange,
+ DecodedLogEvent,
+ ExchangeContractEventArgs,
+ ExchangeEvents,
+ IndexedFilterValues,
+ LogCancelContractEventArgs,
+ LogFillContractEventArgs,
+ LogWithDecodedArgs,
+ Order,
+ SignedOrder,
+ Token as ZeroExToken,
+ TransactionReceiptWithDecodedLogs,
+ ZeroEx,
} from '0x.js';
import {
- InjectedWeb3Subprovider,
- ledgerEthereumBrowserClientFactoryAsync,
- LedgerSubprovider,
- LedgerWalletSubprovider,
- RedundantRPCSubprovider,
+ InjectedWeb3Subprovider,
+ ledgerEthereumBrowserClientFactoryAsync,
+ LedgerSubprovider,
+ LedgerWalletSubprovider,
+ RedundantRPCSubprovider,
} from '@0xproject/subproviders';
import { BigNumber, intervalUtils, promisify } from '@0xproject/utils';
import * as _ from 'lodash';
@@ -31,16 +31,16 @@ import { trackedTokenStorage } from 'ts/local_storage/tracked_token_storage';
import { tradeHistoryStorage } from 'ts/local_storage/trade_history_storage';
import { Dispatcher } from 'ts/redux/dispatcher';
import {
- BlockchainCallErrs,
- BlockchainErrs,
- ContractInstance,
- EtherscanLinkSuffixes,
- ProviderType,
- Side,
- SignatureData,
- Token,
- TokenByAddress,
- TokenStateByAddress,
+ BlockchainCallErrs,
+ BlockchainErrs,
+ ContractInstance,
+ EtherscanLinkSuffixes,
+ ProviderType,
+ Side,
+ SignatureData,
+ Token,
+ TokenByAddress,
+ TokenStateByAddress,
} from 'ts/types';
import { configs } from 'ts/utils/configs';
import { constants } from 'ts/utils/constants';
@@ -56,727 +56,727 @@ import * as MintableArtifacts from '../contracts/Mintable.json';
const BLOCK_NUMBER_BACK_TRACK = 50;
export class Blockchain {
- public networkId: number;
- public nodeVersion: string;
- private _zeroEx: ZeroEx;
- private _dispatcher: Dispatcher;
- private _web3Wrapper?: Web3Wrapper;
- private _exchangeAddress: string;
- private _userAddress: string;
- private _cachedProvider: Web3.Provider;
- private _ledgerSubprovider: LedgerWalletSubprovider;
- private _zrxPollIntervalId: NodeJS.Timer;
- private static async _onPageLoadAsync(): Promise<void> {
- if (document.readyState === 'complete') {
- return; // Already loaded
- }
- return new Promise<void>((resolve, reject) => {
- window.onload = () => resolve();
- });
- }
- private static _getNameGivenProvider(provider: Web3.Provider): string {
- if (!_.isUndefined((provider as any).isMetaMask)) {
- return constants.PROVIDER_NAME_METAMASK;
- }
-
- // HACK: We use the fact that Parity Signer's provider is an instance of their
- // internal `Web3FrameProvider` class.
- const isParitySigner = _.startsWith(provider.constructor.toString(), 'function Web3FrameProvider');
- if (isParitySigner) {
- return constants.PROVIDER_NAME_PARITY_SIGNER;
- }
-
- return constants.PROVIDER_NAME_GENERIC;
- }
- private static async _getProviderAsync(injectedWeb3: Web3, networkIdIfExists: number) {
- const doesInjectedWeb3Exist = !_.isUndefined(injectedWeb3);
- const publicNodeUrlsIfExistsForNetworkId = configs.PUBLIC_NODE_URLS_BY_NETWORK_ID[networkIdIfExists];
- const isPublicNodeAvailableForNetworkId = !_.isUndefined(publicNodeUrlsIfExistsForNetworkId);
-
- let provider;
- if (doesInjectedWeb3Exist && isPublicNodeAvailableForNetworkId) {
- // We catch all requests involving a users account and send it to the injectedWeb3
- // instance. All other requests go to the public hosted node.
- provider = new ProviderEngine();
- provider.addProvider(new InjectedWeb3Subprovider(injectedWeb3));
- provider.addProvider(new FilterSubprovider());
- provider.addProvider(new RedundantRPCSubprovider(publicNodeUrlsIfExistsForNetworkId));
- provider.start();
- } else if (doesInjectedWeb3Exist) {
- // Since no public node for this network, all requests go to injectedWeb3 instance
- provider = injectedWeb3.currentProvider;
- } else {
- // If no injectedWeb3 instance, all requests fallback to our public hosted mainnet/testnet node
- // We do this so that users can still browse the 0x Portal DApp even if they do not have web3
- // injected into their browser.
- provider = new ProviderEngine();
- provider.addProvider(new FilterSubprovider());
- const networkId = configs.IS_MAINNET_ENABLED ? constants.NETWORK_ID_MAINNET : constants.NETWORK_ID_TESTNET;
- provider.addProvider(new RedundantRPCSubprovider(configs.PUBLIC_NODE_URLS_BY_NETWORK_ID[networkId]));
- provider.start();
- }
-
- return provider;
- }
- constructor(dispatcher: Dispatcher, isSalePage: boolean = false) {
- this._dispatcher = dispatcher;
- this._userAddress = '';
- // tslint:disable-next-line:no-floating-promises
- this._onPageLoadInitFireAndForgetAsync();
- }
- public async networkIdUpdatedFireAndForgetAsync(newNetworkId: number) {
- const isConnected = !_.isUndefined(newNetworkId);
- if (!isConnected) {
- this.networkId = newNetworkId;
- this._dispatcher.encounteredBlockchainError(BlockchainErrs.DisconnectedFromEthereumNode);
- this._dispatcher.updateShouldBlockchainErrDialogBeOpen(true);
- } else if (this.networkId !== newNetworkId) {
- this.networkId = newNetworkId;
- this._dispatcher.encounteredBlockchainError(BlockchainErrs.NoError);
- await this._fetchTokenInformationAsync();
- await this._rehydrateStoreWithContractEvents();
- }
- }
- public async userAddressUpdatedFireAndForgetAsync(newUserAddress: string) {
- if (this._userAddress !== newUserAddress) {
- this._userAddress = newUserAddress;
- await this._fetchTokenInformationAsync();
- await this._rehydrateStoreWithContractEvents();
- }
- }
- public async nodeVersionUpdatedFireAndForgetAsync(nodeVersion: string) {
- if (this.nodeVersion !== nodeVersion) {
- this.nodeVersion = nodeVersion;
- }
- }
- public async isAddressInTokenRegistryAsync(tokenAddress: string): Promise<boolean> {
- utils.assert(!_.isUndefined(this._zeroEx), 'ZeroEx must be instantiated.');
- // HACK: temporarily whitelist the new WETH token address `as if` they were
- // already in the tokenRegistry.
- // TODO: Remove this hack once we've updated the TokenRegistries
- // Airtable task: https://airtable.com/tblFe0Q9JuKJPYbTn/viwsOG2Y97qdIeCIO/recv3VGmIorFzHBVz
- if (configs.SHOULD_DEPRECATE_OLD_WETH_TOKEN && tokenAddress === configs.NEW_WRAPPED_ETHERS[this.networkId]) {
- return true;
- }
- const tokenIfExists = await this._zeroEx.tokenRegistry.getTokenIfExistsAsync(tokenAddress);
- return !_.isUndefined(tokenIfExists);
- }
- public getLedgerDerivationPathIfExists(): string {
- if (_.isUndefined(this._ledgerSubprovider)) {
- return undefined;
- }
- const path = this._ledgerSubprovider.getPath();
- return path;
- }
- public updateLedgerDerivationPathIfExists(path: string) {
- if (_.isUndefined(this._ledgerSubprovider)) {
- return; // noop
- }
- this._ledgerSubprovider.setPath(path);
- }
- public updateLedgerDerivationIndex(pathIndex: number) {
- if (_.isUndefined(this._ledgerSubprovider)) {
- return; // noop
- }
- this._ledgerSubprovider.setPathIndex(pathIndex);
- }
- public async providerTypeUpdatedFireAndForgetAsync(providerType: ProviderType) {
- utils.assert(!_.isUndefined(this._zeroEx), 'ZeroEx must be instantiated.');
- // Should actually be Web3.Provider|ProviderEngine union type but it causes issues
- // later on in the logic.
- let provider;
- switch (providerType) {
- case ProviderType.Ledger: {
- const isU2FSupported = await utils.isU2FSupportedAsync();
- if (!isU2FSupported) {
- throw new Error('Cannot update providerType to LEDGER without U2F support');
- }
-
- // Cache injected provider so that we can switch the user back to it easily
- this._cachedProvider = this._web3Wrapper.getProviderObj();
-
- this._dispatcher.updateUserAddress(''); // Clear old userAddress
-
- provider = new ProviderEngine();
- const ledgerWalletConfigs = {
- networkId: this.networkId,
- ledgerEthereumClientFactoryAsync: ledgerEthereumBrowserClientFactoryAsync,
- };
- this._ledgerSubprovider = new LedgerSubprovider(ledgerWalletConfigs);
- provider.addProvider(this._ledgerSubprovider);
- provider.addProvider(new FilterSubprovider());
- const networkId = configs.IS_MAINNET_ENABLED
- ? constants.NETWORK_ID_MAINNET
- : constants.NETWORK_ID_TESTNET;
- provider.addProvider(new RedundantRPCSubprovider(configs.PUBLIC_NODE_URLS_BY_NETWORK_ID[networkId]));
- provider.start();
- this._web3Wrapper.destroy();
- const shouldPollUserAddress = false;
- this._web3Wrapper = new Web3Wrapper(this._dispatcher, provider, this.networkId, shouldPollUserAddress);
- this._zeroEx.setProvider(provider, networkId);
- await this._postInstantiationOrUpdatingProviderZeroExAsync();
- break;
- }
-
- case ProviderType.Injected: {
- if (_.isUndefined(this._cachedProvider)) {
- return; // Going from injected to injected, so we noop
- }
- provider = this._cachedProvider;
- const shouldPollUserAddress = true;
- this._web3Wrapper = new Web3Wrapper(this._dispatcher, provider, this.networkId, shouldPollUserAddress);
- this._zeroEx.setProvider(provider, this.networkId);
- await this._postInstantiationOrUpdatingProviderZeroExAsync();
- delete this._ledgerSubprovider;
- delete this._cachedProvider;
- break;
- }
-
- default:
- throw utils.spawnSwitchErr('providerType', providerType);
- }
-
- await this._fetchTokenInformationAsync();
- }
- public async setProxyAllowanceAsync(token: Token, amountInBaseUnits: BigNumber): Promise<void> {
- utils.assert(this.isValidAddress(token.address), BlockchainCallErrs.TokenAddressIsInvalid);
- utils.assert(this._doesUserAddressExist(), BlockchainCallErrs.UserHasNoAssociatedAddresses);
- utils.assert(!_.isUndefined(this._zeroEx), 'ZeroEx must be instantiated.');
-
- const txHash = await this._zeroEx.token.setProxyAllowanceAsync(
- token.address,
- this._userAddress,
- amountInBaseUnits,
- );
- await this._showEtherScanLinkAndAwaitTransactionMinedAsync(txHash);
- const allowance = amountInBaseUnits;
- this._dispatcher.replaceTokenAllowanceByAddress(token.address, allowance);
- }
- public async transferAsync(token: Token, toAddress: string, amountInBaseUnits: BigNumber): Promise<void> {
- const txHash = await this._zeroEx.token.transferAsync(
- token.address,
- this._userAddress,
- toAddress,
- amountInBaseUnits,
- );
- await this._showEtherScanLinkAndAwaitTransactionMinedAsync(txHash);
- const etherScanLinkIfExists = utils.getEtherScanLinkIfExists(txHash, this.networkId, EtherscanLinkSuffixes.Tx);
- this._dispatcher.showFlashMessage(
- React.createElement(TokenSendCompleted, {
- etherScanLinkIfExists,
- token,
- toAddress,
- amountInBaseUnits,
- }),
- );
- }
- public portalOrderToSignedOrder(
- maker: string,
- taker: string,
- makerTokenAddress: string,
- takerTokenAddress: string,
- makerTokenAmount: BigNumber,
- takerTokenAmount: BigNumber,
- makerFee: BigNumber,
- takerFee: BigNumber,
- expirationUnixTimestampSec: BigNumber,
- feeRecipient: string,
- signatureData: SignatureData,
- salt: BigNumber,
- ): SignedOrder {
- const ecSignature = signatureData;
- const exchangeContractAddress = this.getExchangeContractAddressIfExists();
- const takerOrNullAddress = _.isEmpty(taker) ? constants.NULL_ADDRESS : taker;
- const signedOrder = {
- ecSignature,
- exchangeContractAddress,
- expirationUnixTimestampSec,
- feeRecipient,
- maker,
- makerFee,
- makerTokenAddress,
- makerTokenAmount,
- salt,
- taker: takerOrNullAddress,
- takerFee,
- takerTokenAddress,
- takerTokenAmount,
- };
- return signedOrder;
- }
- public async fillOrderAsync(signedOrder: SignedOrder, fillTakerTokenAmount: BigNumber): Promise<BigNumber> {
- utils.assert(this._doesUserAddressExist(), BlockchainCallErrs.UserHasNoAssociatedAddresses);
-
- const shouldThrowOnInsufficientBalanceOrAllowance = true;
-
- const txHash = await this._zeroEx.exchange.fillOrderAsync(
- signedOrder,
- fillTakerTokenAmount,
- shouldThrowOnInsufficientBalanceOrAllowance,
- this._userAddress,
- );
- const receipt = await this._showEtherScanLinkAndAwaitTransactionMinedAsync(txHash);
- const logs: Array<LogWithDecodedArgs<ExchangeContractEventArgs>> = receipt.logs as any;
- this._zeroEx.exchange.throwLogErrorsAsErrors(logs);
- const logFill = _.find(logs, { event: 'LogFill' });
- const args = (logFill.args as any) as LogFillContractEventArgs;
- const filledTakerTokenAmount = args.filledTakerTokenAmount;
- return filledTakerTokenAmount;
- }
- public async cancelOrderAsync(signedOrder: SignedOrder, cancelTakerTokenAmount: BigNumber): Promise<BigNumber> {
- const txHash = await this._zeroEx.exchange.cancelOrderAsync(signedOrder, cancelTakerTokenAmount);
- const receipt = await this._showEtherScanLinkAndAwaitTransactionMinedAsync(txHash);
- const logs: Array<LogWithDecodedArgs<ExchangeContractEventArgs>> = receipt.logs as any;
- this._zeroEx.exchange.throwLogErrorsAsErrors(logs);
- const logCancel = _.find(logs, { event: ExchangeEvents.LogCancel });
- const args = (logCancel.args as any) as LogCancelContractEventArgs;
- const cancelledTakerTokenAmount = args.cancelledTakerTokenAmount;
- return cancelledTakerTokenAmount;
- }
- public async getUnavailableTakerAmountAsync(orderHash: string): Promise<BigNumber> {
- utils.assert(ZeroEx.isValidOrderHash(orderHash), 'Must be valid orderHash');
- utils.assert(!_.isUndefined(this._zeroEx), 'ZeroEx must be instantiated.');
- const unavailableTakerAmount = await this._zeroEx.exchange.getUnavailableTakerAmountAsync(orderHash);
- return unavailableTakerAmount;
- }
- public getExchangeContractAddressIfExists() {
- return this._exchangeAddress;
- }
- public async validateFillOrderThrowIfInvalidAsync(
- signedOrder: SignedOrder,
- fillTakerTokenAmount: BigNumber,
- takerAddress: string,
- ): Promise<void> {
- await this._zeroEx.exchange.validateFillOrderThrowIfInvalidAsync(
- signedOrder,
- fillTakerTokenAmount,
- takerAddress,
- );
- }
- public async validateCancelOrderThrowIfInvalidAsync(
- order: Order,
- cancelTakerTokenAmount: BigNumber,
- ): Promise<void> {
- await this._zeroEx.exchange.validateCancelOrderThrowIfInvalidAsync(order, cancelTakerTokenAmount);
- }
- public isValidAddress(address: string): boolean {
- const lowercaseAddress = address.toLowerCase();
- return this._web3Wrapper.isAddress(lowercaseAddress);
- }
- public async pollTokenBalanceAsync(token: Token) {
- utils.assert(this._doesUserAddressExist(), BlockchainCallErrs.UserHasNoAssociatedAddresses);
-
- const [currBalance] = await this.getTokenBalanceAndAllowanceAsync(this._userAddress, token.address);
-
- this._zrxPollIntervalId = intervalUtils.setAsyncExcludingInterval(
- async () => {
- const [balance] = await this.getTokenBalanceAndAllowanceAsync(this._userAddress, token.address);
- if (!balance.eq(currBalance)) {
- this._dispatcher.replaceTokenBalanceByAddress(token.address, balance);
- intervalUtils.clearAsyncExcludingInterval(this._zrxPollIntervalId);
- delete this._zrxPollIntervalId;
- }
- },
- 5000,
- (err: Error) => {
- utils.consoleLog(`Polling tokenBalance failed: ${err}`);
- intervalUtils.clearAsyncExcludingInterval(this._zrxPollIntervalId);
- delete this._zrxPollIntervalId;
- },
- );
- }
- public async signOrderHashAsync(orderHash: string): Promise<SignatureData> {
- utils.assert(!_.isUndefined(this._zeroEx), 'ZeroEx must be instantiated.');
- const makerAddress = this._userAddress;
- // If makerAddress is undefined, this means they have a web3 instance injected into their browser
- // but no account addresses associated with it.
- if (_.isUndefined(makerAddress)) {
- throw new Error('Tried to send a sign request but user has no associated addresses');
- }
- const ecSignature = await this._zeroEx.signOrderHashAsync(orderHash, makerAddress);
- const signatureData = _.extend({}, ecSignature, {
- hash: orderHash,
- });
- this._dispatcher.updateSignatureData(signatureData);
- return signatureData;
- }
- public async mintTestTokensAsync(token: Token) {
- utils.assert(this._doesUserAddressExist(), BlockchainCallErrs.UserHasNoAssociatedAddresses);
-
- const mintableContract = await this._instantiateContractIfExistsAsync(MintableArtifacts, token.address);
- await mintableContract.mint(constants.MINT_AMOUNT, {
- from: this._userAddress,
- });
- const balanceDelta = constants.MINT_AMOUNT;
- this._dispatcher.updateTokenBalanceByAddress(token.address, balanceDelta);
- }
- public async getBalanceInEthAsync(owner: string): Promise<BigNumber> {
- const balance = await this._web3Wrapper.getBalanceInEthAsync(owner);
- return balance;
- }
- public async convertEthToWrappedEthTokensAsync(etherTokenAddress: string, amount: BigNumber): Promise<void> {
- utils.assert(!_.isUndefined(this._zeroEx), 'ZeroEx must be instantiated.');
- utils.assert(this._doesUserAddressExist(), BlockchainCallErrs.UserHasNoAssociatedAddresses);
-
- const txHash = await this._zeroEx.etherToken.depositAsync(etherTokenAddress, amount, this._userAddress);
- await this._showEtherScanLinkAndAwaitTransactionMinedAsync(txHash);
- }
- public async convertWrappedEthTokensToEthAsync(etherTokenAddress: string, amount: BigNumber): Promise<void> {
- utils.assert(!_.isUndefined(this._zeroEx), 'ZeroEx must be instantiated.');
- utils.assert(this._doesUserAddressExist(), BlockchainCallErrs.UserHasNoAssociatedAddresses);
-
- const txHash = await this._zeroEx.etherToken.withdrawAsync(etherTokenAddress, amount, this._userAddress);
- await this._showEtherScanLinkAndAwaitTransactionMinedAsync(txHash);
- }
- public async doesContractExistAtAddressAsync(address: string) {
- const doesContractExist = await this._web3Wrapper.doesContractExistAtAddressAsync(address);
- return doesContractExist;
- }
- public async getCurrentUserTokenBalanceAndAllowanceAsync(tokenAddress: string): Promise<BigNumber[]> {
- const tokenBalanceAndAllowance = await this.getTokenBalanceAndAllowanceAsync(this._userAddress, tokenAddress);
- return tokenBalanceAndAllowance;
- }
- public async getTokenBalanceAndAllowanceAsync(ownerAddress: string, tokenAddress: string): Promise<BigNumber[]> {
- utils.assert(!_.isUndefined(this._zeroEx), 'ZeroEx must be instantiated.');
-
- if (_.isEmpty(ownerAddress)) {
- const zero = new BigNumber(0);
- return [zero, zero];
- }
- let balance = new BigNumber(0);
- let allowance = new BigNumber(0);
- if (this._doesUserAddressExist()) {
- balance = await this._zeroEx.token.getBalanceAsync(tokenAddress, ownerAddress);
- allowance = await this._zeroEx.token.getProxyAllowanceAsync(tokenAddress, ownerAddress);
- }
- return [balance, allowance];
- }
- public async updateTokenBalancesAndAllowancesAsync(tokens: Token[]) {
- const tokenStateByAddress: TokenStateByAddress = {};
- for (const token of tokens) {
- let balance = new BigNumber(0);
- let allowance = new BigNumber(0);
- if (this._doesUserAddressExist()) {
- [balance, allowance] = await this.getTokenBalanceAndAllowanceAsync(this._userAddress, token.address);
- }
- const tokenState = {
- balance,
- allowance,
- };
- tokenStateByAddress[token.address] = tokenState;
- }
- this._dispatcher.updateTokenStateByAddress(tokenStateByAddress);
- }
- public async getUserAccountsAsync() {
- utils.assert(!_.isUndefined(this._zeroEx), 'ZeroEx must be instantiated.');
- const userAccountsIfExists = await this._zeroEx.getAvailableAddressesAsync();
- return userAccountsIfExists;
- }
- // HACK: When a user is using a Ledger, we simply dispatch the selected userAddress, which
- // by-passes the web3Wrapper logic for updating the prevUserAddress. We therefore need to
- // manually update it. This should only be called by the LedgerConfigDialog.
- public updateWeb3WrapperPrevUserAddress(newUserAddress: string) {
- this._web3Wrapper.updatePrevUserAddress(newUserAddress);
- }
- public destroy() {
- intervalUtils.clearAsyncExcludingInterval(this._zrxPollIntervalId);
- this._web3Wrapper.destroy();
- this._stopWatchingExchangeLogFillEvents();
- }
- private async _showEtherScanLinkAndAwaitTransactionMinedAsync(
- txHash: string,
- ): Promise<TransactionReceiptWithDecodedLogs> {
- const etherScanLinkIfExists = utils.getEtherScanLinkIfExists(txHash, this.networkId, EtherscanLinkSuffixes.Tx);
- this._dispatcher.showFlashMessage(
- React.createElement(TransactionSubmitted, {
- etherScanLinkIfExists,
- }),
- );
- const receipt = await this._zeroEx.awaitTransactionMinedAsync(txHash);
- return receipt;
- }
- private _doesUserAddressExist(): boolean {
- return this._userAddress !== '';
- }
- private async _rehydrateStoreWithContractEvents() {
- // Ensure we are only ever listening to one set of events
- this._stopWatchingExchangeLogFillEvents();
-
- if (!this._doesUserAddressExist()) {
- return; // short-circuit
- }
-
- if (!_.isUndefined(this._zeroEx)) {
- // Since we do not have an index on the `taker` address and want to show
- // transactions where an account is either the `maker` or `taker`, we loop
- // through all fill events, and filter/cache them client-side.
- const filterIndexObj = {};
- await this._startListeningForExchangeLogFillEventsAsync(filterIndexObj);
- }
- }
- private async _startListeningForExchangeLogFillEventsAsync(indexFilterValues: IndexedFilterValues): Promise<void> {
- utils.assert(!_.isUndefined(this._zeroEx), 'ZeroEx must be instantiated.');
- utils.assert(this._doesUserAddressExist(), BlockchainCallErrs.UserHasNoAssociatedAddresses);
-
- // Fetch historical logs
- await this._fetchHistoricalExchangeLogFillEventsAsync(indexFilterValues);
-
- // Start a subscription for new logs
- this._zeroEx.exchange.subscribe(
- ExchangeEvents.LogFill,
- indexFilterValues,
- async (err: Error, decodedLogEvent: DecodedLogEvent<LogFillContractEventArgs>) => {
- if (err) {
- // Note: it's not entirely clear from the documentation which
- // errors will be thrown by `watch`. For now, let's log the error
- // to rollbar and stop watching when one occurs
- // tslint:disable-next-line:no-floating-promises
- errorReporter.reportAsync(err); // fire and forget
- return;
- } else {
- const decodedLog = decodedLogEvent.log;
- if (!this._doesLogEventInvolveUser(decodedLog)) {
- return; // We aren't interested in the fill event
- }
- this._updateLatestFillsBlockIfNeeded(decodedLog.blockNumber);
- const fill = await this._convertDecodedLogToFillAsync(decodedLog);
- if (decodedLogEvent.isRemoved) {
- tradeHistoryStorage.removeFillFromUser(this._userAddress, this.networkId, fill);
- } else {
- tradeHistoryStorage.addFillToUser(this._userAddress, this.networkId, fill);
- }
- }
- },
- );
- }
- private async _fetchHistoricalExchangeLogFillEventsAsync(indexFilterValues: IndexedFilterValues) {
- const fromBlock = tradeHistoryStorage.getFillsLatestBlock(this._userAddress, this.networkId);
- const blockRange: BlockRange = {
- fromBlock,
- toBlock: 'latest' as BlockParam,
- };
- const decodedLogs = await this._zeroEx.exchange.getLogsAsync<LogFillContractEventArgs>(
- ExchangeEvents.LogFill,
- blockRange,
- indexFilterValues,
- );
- for (const decodedLog of decodedLogs) {
- if (!this._doesLogEventInvolveUser(decodedLog)) {
- continue; // We aren't interested in the fill event
- }
- this._updateLatestFillsBlockIfNeeded(decodedLog.blockNumber);
- const fill = await this._convertDecodedLogToFillAsync(decodedLog);
- tradeHistoryStorage.addFillToUser(this._userAddress, this.networkId, fill);
- }
- }
- private async _convertDecodedLogToFillAsync(decodedLog: LogWithDecodedArgs<LogFillContractEventArgs>) {
- const args = decodedLog.args;
- const blockTimestamp = await this._web3Wrapper.getBlockTimestampAsync(decodedLog.blockHash);
- const fill = {
- filledTakerTokenAmount: args.filledTakerTokenAmount,
- filledMakerTokenAmount: args.filledMakerTokenAmount,
- logIndex: decodedLog.logIndex,
- maker: args.maker,
- orderHash: args.orderHash,
- taker: args.taker,
- makerToken: args.makerToken,
- takerToken: args.takerToken,
- paidMakerFee: args.paidMakerFee,
- paidTakerFee: args.paidTakerFee,
- transactionHash: decodedLog.transactionHash,
- blockTimestamp,
- };
- return fill;
- }
- private _doesLogEventInvolveUser(decodedLog: LogWithDecodedArgs<LogFillContractEventArgs>) {
- const args = decodedLog.args;
- const isUserMakerOrTaker = args.maker === this._userAddress || args.taker === this._userAddress;
- return isUserMakerOrTaker;
- }
- private _updateLatestFillsBlockIfNeeded(blockNumber: number) {
- const isBlockPending = _.isNull(blockNumber);
- if (!isBlockPending) {
- // Hack: I've observed the behavior where a client won't register certain fill events
- // and lowering the cache blockNumber fixes the issue. As a quick fix for now, simply
- // set the cached blockNumber 50 below the one returned. This way, upon refreshing, a user
- // would still attempt to re-fetch events from the previous 50 blocks, but won't need to
- // re-fetch all events in all blocks.
- // TODO: Debug if this is a race condition, and apply a more precise fix
- const blockNumberToSet =
- blockNumber - BLOCK_NUMBER_BACK_TRACK < 0 ? 0 : blockNumber - BLOCK_NUMBER_BACK_TRACK;
- tradeHistoryStorage.setFillsLatestBlock(this._userAddress, this.networkId, blockNumberToSet);
- }
- }
- private _stopWatchingExchangeLogFillEvents(): void {
- this._zeroEx.exchange.unsubscribeAll();
- }
- private async _getTokenRegistryTokensByAddressAsync(): Promise<TokenByAddress> {
- utils.assert(!_.isUndefined(this._zeroEx), 'ZeroEx must be instantiated.');
- const tokenRegistryTokens = await this._zeroEx.tokenRegistry.getTokensAsync();
-
- const tokenByAddress: TokenByAddress = {};
- _.each(tokenRegistryTokens, (t: ZeroExToken, i: number) => {
- // HACK: For now we have a hard-coded list of iconUrls for the dummyTokens
- // TODO: Refactor this out and pull the iconUrl directly from the TokenRegistry
- const iconUrl = configs.ICON_URL_BY_SYMBOL[t.symbol];
- // HACK: Temporarily we hijack the WETH addresses fetched from the tokenRegistry
- // so that we can take our time with actually updating it. This ensures that when
- // we deploy the new WETH page, everyone will re-fill their trackedTokens with the
- // new canonical WETH.
- // TODO: Remove this hack once we've updated the TokenRegistries
- // Airtable task: https://airtable.com/tblFe0Q9JuKJPYbTn/viwsOG2Y97qdIeCIO/recv3VGmIorFzHBVz
- let address = t.address;
- if (configs.SHOULD_DEPRECATE_OLD_WETH_TOKEN && t.symbol === 'WETH') {
- const newEtherTokenAddressIfExists = configs.NEW_WRAPPED_ETHERS[this.networkId];
- if (!_.isUndefined(newEtherTokenAddressIfExists)) {
- address = newEtherTokenAddressIfExists;
- }
- }
- const token: Token = {
- iconUrl,
- address,
- name: t.name,
- symbol: t.symbol,
- decimals: t.decimals,
- isTracked: false,
- isRegistered: true,
- };
- tokenByAddress[token.address] = token;
- });
- return tokenByAddress;
- }
- private async _onPageLoadInitFireAndForgetAsync() {
- await Blockchain._onPageLoadAsync(); // wait for page to load
-
- // Hack: We need to know the networkId the injectedWeb3 is connected to (if it is defined) in
- // order to properly instantiate the web3Wrapper. Since we must use the async call, we cannot
- // retrieve it from within the web3Wrapper constructor. This is and should remain the only
- // call to a web3 instance outside of web3Wrapper in the entire dapp.
- // In addition, if the user has an injectedWeb3 instance that is disconnected from a backing
- // Ethereum node, this call will throw. We need to handle this case gracefully
- const injectedWeb3 = (window as any).web3;
- let networkIdIfExists: number;
- if (!_.isUndefined(injectedWeb3)) {
- try {
- networkIdIfExists = _.parseInt(await promisify<string>(injectedWeb3.version.getNetwork)());
- } catch (err) {
- // Ignore error and proceed with networkId undefined
- }
- }
-
- const provider = await Blockchain._getProviderAsync(injectedWeb3, networkIdIfExists);
- const networkId = !_.isUndefined(networkIdIfExists)
- ? networkIdIfExists
- : configs.IS_MAINNET_ENABLED ? constants.NETWORK_ID_MAINNET : constants.NETWORK_ID_TESTNET;
- const zeroExConfigs = {
- networkId,
- };
- this._zeroEx = new ZeroEx(provider, zeroExConfigs);
- this._updateProviderName(injectedWeb3);
- const shouldPollUserAddress = true;
- this._web3Wrapper = new Web3Wrapper(this._dispatcher, provider, networkId, shouldPollUserAddress);
- await this._postInstantiationOrUpdatingProviderZeroExAsync();
- }
- // This method should always be run after instantiating or updating the provider
- // of the ZeroEx instance.
- private async _postInstantiationOrUpdatingProviderZeroExAsync() {
- utils.assert(!_.isUndefined(this._zeroEx), 'ZeroEx must be instantiated.');
- this._exchangeAddress = this._zeroEx.exchange.getContractAddress();
- }
- private _updateProviderName(injectedWeb3: Web3) {
- const doesInjectedWeb3Exist = !_.isUndefined(injectedWeb3);
- const providerName = doesInjectedWeb3Exist
- ? Blockchain._getNameGivenProvider(injectedWeb3.currentProvider)
- : constants.PROVIDER_NAME_PUBLIC;
- this._dispatcher.updateInjectedProviderName(providerName);
- }
- private async _fetchTokenInformationAsync() {
- utils.assert(
- !_.isUndefined(this.networkId),
- 'Cannot call fetchTokenInformationAsync if disconnected from Ethereum node',
- );
-
- this._dispatcher.updateBlockchainIsLoaded(false);
- this._dispatcher.clearTokenByAddress();
-
- const tokenRegistryTokensByAddress = await this._getTokenRegistryTokensByAddressAsync();
-
- // HACK: We need to fetch the userAddress here because otherwise we cannot save the
- // tracked tokens in localStorage under the users address nor fetch the token
- // balances and allowances and we need to do this in order not to trigger the blockchain
- // loading dialog to show up twice. First to load the contracts, and second to load the
- // balances and allowances.
- this._userAddress = await this._web3Wrapper.getFirstAccountIfExistsAsync();
- if (!_.isEmpty(this._userAddress)) {
- this._dispatcher.updateUserAddress(this._userAddress);
- }
-
- let trackedTokensIfExists = trackedTokenStorage.getTrackedTokensIfExists(this._userAddress, this.networkId);
- const tokenRegistryTokens = _.values(tokenRegistryTokensByAddress);
- if (_.isUndefined(trackedTokensIfExists)) {
- trackedTokensIfExists = _.map(configs.DEFAULT_TRACKED_TOKEN_SYMBOLS, symbol => {
- const token = _.find(tokenRegistryTokens, t => t.symbol === symbol);
- token.isTracked = true;
- return token;
- });
- _.each(trackedTokensIfExists, token => {
- trackedTokenStorage.addTrackedTokenToUser(this._userAddress, this.networkId, token);
- });
- } else {
- // Properly set all tokenRegistry tokens `isTracked` to true if they are in the existing trackedTokens array
- _.each(trackedTokensIfExists, trackedToken => {
- if (!_.isUndefined(tokenRegistryTokensByAddress[trackedToken.address])) {
- tokenRegistryTokensByAddress[trackedToken.address].isTracked = true;
- }
- });
- }
- const allTokens = _.uniq([...tokenRegistryTokens, ...trackedTokensIfExists]);
- this._dispatcher.updateTokenByAddress(allTokens);
-
- // Get balance/allowance for tracked tokens
- await this.updateTokenBalancesAndAllowancesAsync(trackedTokensIfExists);
-
- const mostPopularTradingPairTokens: Token[] = [
- _.find(allTokens, { symbol: configs.DEFAULT_TRACKED_TOKEN_SYMBOLS[0] }),
- _.find(allTokens, { symbol: configs.DEFAULT_TRACKED_TOKEN_SYMBOLS[1] }),
- ];
- this._dispatcher.updateChosenAssetTokenAddress(Side.Deposit, mostPopularTradingPairTokens[0].address);
- this._dispatcher.updateChosenAssetTokenAddress(Side.Receive, mostPopularTradingPairTokens[1].address);
- this._dispatcher.updateBlockchainIsLoaded(true);
- }
- private async _instantiateContractIfExistsAsync(artifact: any, address?: string): Promise<ContractInstance> {
- const c = await contract(artifact);
- const providerObj = this._web3Wrapper.getProviderObj();
- c.setProvider(providerObj);
-
- const artifactNetworkConfigs = artifact.networks[this.networkId];
- let contractAddress;
- if (!_.isUndefined(address)) {
- contractAddress = address;
- } else if (!_.isUndefined(artifactNetworkConfigs)) {
- contractAddress = artifactNetworkConfigs.address;
- }
-
- if (!_.isUndefined(contractAddress)) {
- const doesContractExist = await this.doesContractExistAtAddressAsync(contractAddress);
- if (!doesContractExist) {
- utils.consoleLog(`Contract does not exist: ${artifact.contract_name} at ${contractAddress}`);
- throw new Error(BlockchainCallErrs.ContractDoesNotExist);
- }
- }
-
- try {
- const contractInstance = _.isUndefined(address) ? await c.deployed() : await c.at(address);
- return contractInstance;
- } catch (err) {
- const errMsg = `${err}`;
- utils.consoleLog(`Notice: Error encountered: ${err} ${err.stack}`);
- if (_.includes(errMsg, 'not been deployed to detected network')) {
- throw new Error(BlockchainCallErrs.ContractDoesNotExist);
- } else {
- await errorReporter.reportAsync(err);
- throw new Error(BlockchainCallErrs.UnhandledError);
- }
- }
- }
+ public networkId: number;
+ public nodeVersion: string;
+ private _zeroEx: ZeroEx;
+ private _dispatcher: Dispatcher;
+ private _web3Wrapper?: Web3Wrapper;
+ private _exchangeAddress: string;
+ private _userAddress: string;
+ private _cachedProvider: Web3.Provider;
+ private _ledgerSubprovider: LedgerWalletSubprovider;
+ private _zrxPollIntervalId: NodeJS.Timer;
+ private static async _onPageLoadAsync(): Promise<void> {
+ if (document.readyState === 'complete') {
+ return; // Already loaded
+ }
+ return new Promise<void>((resolve, reject) => {
+ window.onload = () => resolve();
+ });
+ }
+ private static _getNameGivenProvider(provider: Web3.Provider): string {
+ if (!_.isUndefined((provider as any).isMetaMask)) {
+ return constants.PROVIDER_NAME_METAMASK;
+ }
+
+ // HACK: We use the fact that Parity Signer's provider is an instance of their
+ // internal `Web3FrameProvider` class.
+ const isParitySigner = _.startsWith(provider.constructor.toString(), 'function Web3FrameProvider');
+ if (isParitySigner) {
+ return constants.PROVIDER_NAME_PARITY_SIGNER;
+ }
+
+ return constants.PROVIDER_NAME_GENERIC;
+ }
+ private static async _getProviderAsync(injectedWeb3: Web3, networkIdIfExists: number) {
+ const doesInjectedWeb3Exist = !_.isUndefined(injectedWeb3);
+ const publicNodeUrlsIfExistsForNetworkId = configs.PUBLIC_NODE_URLS_BY_NETWORK_ID[networkIdIfExists];
+ const isPublicNodeAvailableForNetworkId = !_.isUndefined(publicNodeUrlsIfExistsForNetworkId);
+
+ let provider;
+ if (doesInjectedWeb3Exist && isPublicNodeAvailableForNetworkId) {
+ // We catch all requests involving a users account and send it to the injectedWeb3
+ // instance. All other requests go to the public hosted node.
+ provider = new ProviderEngine();
+ provider.addProvider(new InjectedWeb3Subprovider(injectedWeb3));
+ provider.addProvider(new FilterSubprovider());
+ provider.addProvider(new RedundantRPCSubprovider(publicNodeUrlsIfExistsForNetworkId));
+ provider.start();
+ } else if (doesInjectedWeb3Exist) {
+ // Since no public node for this network, all requests go to injectedWeb3 instance
+ provider = injectedWeb3.currentProvider;
+ } else {
+ // If no injectedWeb3 instance, all requests fallback to our public hosted mainnet/testnet node
+ // We do this so that users can still browse the 0x Portal DApp even if they do not have web3
+ // injected into their browser.
+ provider = new ProviderEngine();
+ provider.addProvider(new FilterSubprovider());
+ const networkId = configs.IS_MAINNET_ENABLED ? constants.NETWORK_ID_MAINNET : constants.NETWORK_ID_TESTNET;
+ provider.addProvider(new RedundantRPCSubprovider(configs.PUBLIC_NODE_URLS_BY_NETWORK_ID[networkId]));
+ provider.start();
+ }
+
+ return provider;
+ }
+ constructor(dispatcher: Dispatcher, isSalePage: boolean = false) {
+ this._dispatcher = dispatcher;
+ this._userAddress = '';
+ // tslint:disable-next-line:no-floating-promises
+ this._onPageLoadInitFireAndForgetAsync();
+ }
+ public async networkIdUpdatedFireAndForgetAsync(newNetworkId: number) {
+ const isConnected = !_.isUndefined(newNetworkId);
+ if (!isConnected) {
+ this.networkId = newNetworkId;
+ this._dispatcher.encounteredBlockchainError(BlockchainErrs.DisconnectedFromEthereumNode);
+ this._dispatcher.updateShouldBlockchainErrDialogBeOpen(true);
+ } else if (this.networkId !== newNetworkId) {
+ this.networkId = newNetworkId;
+ this._dispatcher.encounteredBlockchainError(BlockchainErrs.NoError);
+ await this._fetchTokenInformationAsync();
+ await this._rehydrateStoreWithContractEvents();
+ }
+ }
+ public async userAddressUpdatedFireAndForgetAsync(newUserAddress: string) {
+ if (this._userAddress !== newUserAddress) {
+ this._userAddress = newUserAddress;
+ await this._fetchTokenInformationAsync();
+ await this._rehydrateStoreWithContractEvents();
+ }
+ }
+ public async nodeVersionUpdatedFireAndForgetAsync(nodeVersion: string) {
+ if (this.nodeVersion !== nodeVersion) {
+ this.nodeVersion = nodeVersion;
+ }
+ }
+ public async isAddressInTokenRegistryAsync(tokenAddress: string): Promise<boolean> {
+ utils.assert(!_.isUndefined(this._zeroEx), 'ZeroEx must be instantiated.');
+ // HACK: temporarily whitelist the new WETH token address `as if` they were
+ // already in the tokenRegistry.
+ // TODO: Remove this hack once we've updated the TokenRegistries
+ // Airtable task: https://airtable.com/tblFe0Q9JuKJPYbTn/viwsOG2Y97qdIeCIO/recv3VGmIorFzHBVz
+ if (configs.SHOULD_DEPRECATE_OLD_WETH_TOKEN && tokenAddress === configs.NEW_WRAPPED_ETHERS[this.networkId]) {
+ return true;
+ }
+ const tokenIfExists = await this._zeroEx.tokenRegistry.getTokenIfExistsAsync(tokenAddress);
+ return !_.isUndefined(tokenIfExists);
+ }
+ public getLedgerDerivationPathIfExists(): string {
+ if (_.isUndefined(this._ledgerSubprovider)) {
+ return undefined;
+ }
+ const path = this._ledgerSubprovider.getPath();
+ return path;
+ }
+ public updateLedgerDerivationPathIfExists(path: string) {
+ if (_.isUndefined(this._ledgerSubprovider)) {
+ return; // noop
+ }
+ this._ledgerSubprovider.setPath(path);
+ }
+ public updateLedgerDerivationIndex(pathIndex: number) {
+ if (_.isUndefined(this._ledgerSubprovider)) {
+ return; // noop
+ }
+ this._ledgerSubprovider.setPathIndex(pathIndex);
+ }
+ public async providerTypeUpdatedFireAndForgetAsync(providerType: ProviderType) {
+ utils.assert(!_.isUndefined(this._zeroEx), 'ZeroEx must be instantiated.');
+ // Should actually be Web3.Provider|ProviderEngine union type but it causes issues
+ // later on in the logic.
+ let provider;
+ switch (providerType) {
+ case ProviderType.Ledger: {
+ const isU2FSupported = await utils.isU2FSupportedAsync();
+ if (!isU2FSupported) {
+ throw new Error('Cannot update providerType to LEDGER without U2F support');
+ }
+
+ // Cache injected provider so that we can switch the user back to it easily
+ this._cachedProvider = this._web3Wrapper.getProviderObj();
+
+ this._dispatcher.updateUserAddress(''); // Clear old userAddress
+
+ provider = new ProviderEngine();
+ const ledgerWalletConfigs = {
+ networkId: this.networkId,
+ ledgerEthereumClientFactoryAsync: ledgerEthereumBrowserClientFactoryAsync,
+ };
+ this._ledgerSubprovider = new LedgerSubprovider(ledgerWalletConfigs);
+ provider.addProvider(this._ledgerSubprovider);
+ provider.addProvider(new FilterSubprovider());
+ const networkId = configs.IS_MAINNET_ENABLED
+ ? constants.NETWORK_ID_MAINNET
+ : constants.NETWORK_ID_TESTNET;
+ provider.addProvider(new RedundantRPCSubprovider(configs.PUBLIC_NODE_URLS_BY_NETWORK_ID[networkId]));
+ provider.start();
+ this._web3Wrapper.destroy();
+ const shouldPollUserAddress = false;
+ this._web3Wrapper = new Web3Wrapper(this._dispatcher, provider, this.networkId, shouldPollUserAddress);
+ this._zeroEx.setProvider(provider, networkId);
+ await this._postInstantiationOrUpdatingProviderZeroExAsync();
+ break;
+ }
+
+ case ProviderType.Injected: {
+ if (_.isUndefined(this._cachedProvider)) {
+ return; // Going from injected to injected, so we noop
+ }
+ provider = this._cachedProvider;
+ const shouldPollUserAddress = true;
+ this._web3Wrapper = new Web3Wrapper(this._dispatcher, provider, this.networkId, shouldPollUserAddress);
+ this._zeroEx.setProvider(provider, this.networkId);
+ await this._postInstantiationOrUpdatingProviderZeroExAsync();
+ delete this._ledgerSubprovider;
+ delete this._cachedProvider;
+ break;
+ }
+
+ default:
+ throw utils.spawnSwitchErr('providerType', providerType);
+ }
+
+ await this._fetchTokenInformationAsync();
+ }
+ public async setProxyAllowanceAsync(token: Token, amountInBaseUnits: BigNumber): Promise<void> {
+ utils.assert(this.isValidAddress(token.address), BlockchainCallErrs.TokenAddressIsInvalid);
+ utils.assert(this._doesUserAddressExist(), BlockchainCallErrs.UserHasNoAssociatedAddresses);
+ utils.assert(!_.isUndefined(this._zeroEx), 'ZeroEx must be instantiated.');
+
+ const txHash = await this._zeroEx.token.setProxyAllowanceAsync(
+ token.address,
+ this._userAddress,
+ amountInBaseUnits,
+ );
+ await this._showEtherScanLinkAndAwaitTransactionMinedAsync(txHash);
+ const allowance = amountInBaseUnits;
+ this._dispatcher.replaceTokenAllowanceByAddress(token.address, allowance);
+ }
+ public async transferAsync(token: Token, toAddress: string, amountInBaseUnits: BigNumber): Promise<void> {
+ const txHash = await this._zeroEx.token.transferAsync(
+ token.address,
+ this._userAddress,
+ toAddress,
+ amountInBaseUnits,
+ );
+ await this._showEtherScanLinkAndAwaitTransactionMinedAsync(txHash);
+ const etherScanLinkIfExists = utils.getEtherScanLinkIfExists(txHash, this.networkId, EtherscanLinkSuffixes.Tx);
+ this._dispatcher.showFlashMessage(
+ React.createElement(TokenSendCompleted, {
+ etherScanLinkIfExists,
+ token,
+ toAddress,
+ amountInBaseUnits,
+ }),
+ );
+ }
+ public portalOrderToSignedOrder(
+ maker: string,
+ taker: string,
+ makerTokenAddress: string,
+ takerTokenAddress: string,
+ makerTokenAmount: BigNumber,
+ takerTokenAmount: BigNumber,
+ makerFee: BigNumber,
+ takerFee: BigNumber,
+ expirationUnixTimestampSec: BigNumber,
+ feeRecipient: string,
+ signatureData: SignatureData,
+ salt: BigNumber,
+ ): SignedOrder {
+ const ecSignature = signatureData;
+ const exchangeContractAddress = this.getExchangeContractAddressIfExists();
+ const takerOrNullAddress = _.isEmpty(taker) ? constants.NULL_ADDRESS : taker;
+ const signedOrder = {
+ ecSignature,
+ exchangeContractAddress,
+ expirationUnixTimestampSec,
+ feeRecipient,
+ maker,
+ makerFee,
+ makerTokenAddress,
+ makerTokenAmount,
+ salt,
+ taker: takerOrNullAddress,
+ takerFee,
+ takerTokenAddress,
+ takerTokenAmount,
+ };
+ return signedOrder;
+ }
+ public async fillOrderAsync(signedOrder: SignedOrder, fillTakerTokenAmount: BigNumber): Promise<BigNumber> {
+ utils.assert(this._doesUserAddressExist(), BlockchainCallErrs.UserHasNoAssociatedAddresses);
+
+ const shouldThrowOnInsufficientBalanceOrAllowance = true;
+
+ const txHash = await this._zeroEx.exchange.fillOrderAsync(
+ signedOrder,
+ fillTakerTokenAmount,
+ shouldThrowOnInsufficientBalanceOrAllowance,
+ this._userAddress,
+ );
+ const receipt = await this._showEtherScanLinkAndAwaitTransactionMinedAsync(txHash);
+ const logs: Array<LogWithDecodedArgs<ExchangeContractEventArgs>> = receipt.logs as any;
+ this._zeroEx.exchange.throwLogErrorsAsErrors(logs);
+ const logFill = _.find(logs, { event: 'LogFill' });
+ const args = (logFill.args as any) as LogFillContractEventArgs;
+ const filledTakerTokenAmount = args.filledTakerTokenAmount;
+ return filledTakerTokenAmount;
+ }
+ public async cancelOrderAsync(signedOrder: SignedOrder, cancelTakerTokenAmount: BigNumber): Promise<BigNumber> {
+ const txHash = await this._zeroEx.exchange.cancelOrderAsync(signedOrder, cancelTakerTokenAmount);
+ const receipt = await this._showEtherScanLinkAndAwaitTransactionMinedAsync(txHash);
+ const logs: Array<LogWithDecodedArgs<ExchangeContractEventArgs>> = receipt.logs as any;
+ this._zeroEx.exchange.throwLogErrorsAsErrors(logs);
+ const logCancel = _.find(logs, { event: ExchangeEvents.LogCancel });
+ const args = (logCancel.args as any) as LogCancelContractEventArgs;
+ const cancelledTakerTokenAmount = args.cancelledTakerTokenAmount;
+ return cancelledTakerTokenAmount;
+ }
+ public async getUnavailableTakerAmountAsync(orderHash: string): Promise<BigNumber> {
+ utils.assert(ZeroEx.isValidOrderHash(orderHash), 'Must be valid orderHash');
+ utils.assert(!_.isUndefined(this._zeroEx), 'ZeroEx must be instantiated.');
+ const unavailableTakerAmount = await this._zeroEx.exchange.getUnavailableTakerAmountAsync(orderHash);
+ return unavailableTakerAmount;
+ }
+ public getExchangeContractAddressIfExists() {
+ return this._exchangeAddress;
+ }
+ public async validateFillOrderThrowIfInvalidAsync(
+ signedOrder: SignedOrder,
+ fillTakerTokenAmount: BigNumber,
+ takerAddress: string,
+ ): Promise<void> {
+ await this._zeroEx.exchange.validateFillOrderThrowIfInvalidAsync(
+ signedOrder,
+ fillTakerTokenAmount,
+ takerAddress,
+ );
+ }
+ public async validateCancelOrderThrowIfInvalidAsync(
+ order: Order,
+ cancelTakerTokenAmount: BigNumber,
+ ): Promise<void> {
+ await this._zeroEx.exchange.validateCancelOrderThrowIfInvalidAsync(order, cancelTakerTokenAmount);
+ }
+ public isValidAddress(address: string): boolean {
+ const lowercaseAddress = address.toLowerCase();
+ return this._web3Wrapper.isAddress(lowercaseAddress);
+ }
+ public async pollTokenBalanceAsync(token: Token) {
+ utils.assert(this._doesUserAddressExist(), BlockchainCallErrs.UserHasNoAssociatedAddresses);
+
+ const [currBalance] = await this.getTokenBalanceAndAllowanceAsync(this._userAddress, token.address);
+
+ this._zrxPollIntervalId = intervalUtils.setAsyncExcludingInterval(
+ async () => {
+ const [balance] = await this.getTokenBalanceAndAllowanceAsync(this._userAddress, token.address);
+ if (!balance.eq(currBalance)) {
+ this._dispatcher.replaceTokenBalanceByAddress(token.address, balance);
+ intervalUtils.clearAsyncExcludingInterval(this._zrxPollIntervalId);
+ delete this._zrxPollIntervalId;
+ }
+ },
+ 5000,
+ (err: Error) => {
+ utils.consoleLog(`Polling tokenBalance failed: ${err}`);
+ intervalUtils.clearAsyncExcludingInterval(this._zrxPollIntervalId);
+ delete this._zrxPollIntervalId;
+ },
+ );
+ }
+ public async signOrderHashAsync(orderHash: string): Promise<SignatureData> {
+ utils.assert(!_.isUndefined(this._zeroEx), 'ZeroEx must be instantiated.');
+ const makerAddress = this._userAddress;
+ // If makerAddress is undefined, this means they have a web3 instance injected into their browser
+ // but no account addresses associated with it.
+ if (_.isUndefined(makerAddress)) {
+ throw new Error('Tried to send a sign request but user has no associated addresses');
+ }
+ const ecSignature = await this._zeroEx.signOrderHashAsync(orderHash, makerAddress);
+ const signatureData = _.extend({}, ecSignature, {
+ hash: orderHash,
+ });
+ this._dispatcher.updateSignatureData(signatureData);
+ return signatureData;
+ }
+ public async mintTestTokensAsync(token: Token) {
+ utils.assert(this._doesUserAddressExist(), BlockchainCallErrs.UserHasNoAssociatedAddresses);
+
+ const mintableContract = await this._instantiateContractIfExistsAsync(MintableArtifacts, token.address);
+ await mintableContract.mint(constants.MINT_AMOUNT, {
+ from: this._userAddress,
+ });
+ const balanceDelta = constants.MINT_AMOUNT;
+ this._dispatcher.updateTokenBalanceByAddress(token.address, balanceDelta);
+ }
+ public async getBalanceInEthAsync(owner: string): Promise<BigNumber> {
+ const balance = await this._web3Wrapper.getBalanceInEthAsync(owner);
+ return balance;
+ }
+ public async convertEthToWrappedEthTokensAsync(etherTokenAddress: string, amount: BigNumber): Promise<void> {
+ utils.assert(!_.isUndefined(this._zeroEx), 'ZeroEx must be instantiated.');
+ utils.assert(this._doesUserAddressExist(), BlockchainCallErrs.UserHasNoAssociatedAddresses);
+
+ const txHash = await this._zeroEx.etherToken.depositAsync(etherTokenAddress, amount, this._userAddress);
+ await this._showEtherScanLinkAndAwaitTransactionMinedAsync(txHash);
+ }
+ public async convertWrappedEthTokensToEthAsync(etherTokenAddress: string, amount: BigNumber): Promise<void> {
+ utils.assert(!_.isUndefined(this._zeroEx), 'ZeroEx must be instantiated.');
+ utils.assert(this._doesUserAddressExist(), BlockchainCallErrs.UserHasNoAssociatedAddresses);
+
+ const txHash = await this._zeroEx.etherToken.withdrawAsync(etherTokenAddress, amount, this._userAddress);
+ await this._showEtherScanLinkAndAwaitTransactionMinedAsync(txHash);
+ }
+ public async doesContractExistAtAddressAsync(address: string) {
+ const doesContractExist = await this._web3Wrapper.doesContractExistAtAddressAsync(address);
+ return doesContractExist;
+ }
+ public async getCurrentUserTokenBalanceAndAllowanceAsync(tokenAddress: string): Promise<BigNumber[]> {
+ const tokenBalanceAndAllowance = await this.getTokenBalanceAndAllowanceAsync(this._userAddress, tokenAddress);
+ return tokenBalanceAndAllowance;
+ }
+ public async getTokenBalanceAndAllowanceAsync(ownerAddress: string, tokenAddress: string): Promise<BigNumber[]> {
+ utils.assert(!_.isUndefined(this._zeroEx), 'ZeroEx must be instantiated.');
+
+ if (_.isEmpty(ownerAddress)) {
+ const zero = new BigNumber(0);
+ return [zero, zero];
+ }
+ let balance = new BigNumber(0);
+ let allowance = new BigNumber(0);
+ if (this._doesUserAddressExist()) {
+ balance = await this._zeroEx.token.getBalanceAsync(tokenAddress, ownerAddress);
+ allowance = await this._zeroEx.token.getProxyAllowanceAsync(tokenAddress, ownerAddress);
+ }
+ return [balance, allowance];
+ }
+ public async updateTokenBalancesAndAllowancesAsync(tokens: Token[]) {
+ const tokenStateByAddress: TokenStateByAddress = {};
+ for (const token of tokens) {
+ let balance = new BigNumber(0);
+ let allowance = new BigNumber(0);
+ if (this._doesUserAddressExist()) {
+ [balance, allowance] = await this.getTokenBalanceAndAllowanceAsync(this._userAddress, token.address);
+ }
+ const tokenState = {
+ balance,
+ allowance,
+ };
+ tokenStateByAddress[token.address] = tokenState;
+ }
+ this._dispatcher.updateTokenStateByAddress(tokenStateByAddress);
+ }
+ public async getUserAccountsAsync() {
+ utils.assert(!_.isUndefined(this._zeroEx), 'ZeroEx must be instantiated.');
+ const userAccountsIfExists = await this._zeroEx.getAvailableAddressesAsync();
+ return userAccountsIfExists;
+ }
+ // HACK: When a user is using a Ledger, we simply dispatch the selected userAddress, which
+ // by-passes the web3Wrapper logic for updating the prevUserAddress. We therefore need to
+ // manually update it. This should only be called by the LedgerConfigDialog.
+ public updateWeb3WrapperPrevUserAddress(newUserAddress: string) {
+ this._web3Wrapper.updatePrevUserAddress(newUserAddress);
+ }
+ public destroy() {
+ intervalUtils.clearAsyncExcludingInterval(this._zrxPollIntervalId);
+ this._web3Wrapper.destroy();
+ this._stopWatchingExchangeLogFillEvents();
+ }
+ private async _showEtherScanLinkAndAwaitTransactionMinedAsync(
+ txHash: string,
+ ): Promise<TransactionReceiptWithDecodedLogs> {
+ const etherScanLinkIfExists = utils.getEtherScanLinkIfExists(txHash, this.networkId, EtherscanLinkSuffixes.Tx);
+ this._dispatcher.showFlashMessage(
+ React.createElement(TransactionSubmitted, {
+ etherScanLinkIfExists,
+ }),
+ );
+ const receipt = await this._zeroEx.awaitTransactionMinedAsync(txHash);
+ return receipt;
+ }
+ private _doesUserAddressExist(): boolean {
+ return this._userAddress !== '';
+ }
+ private async _rehydrateStoreWithContractEvents() {
+ // Ensure we are only ever listening to one set of events
+ this._stopWatchingExchangeLogFillEvents();
+
+ if (!this._doesUserAddressExist()) {
+ return; // short-circuit
+ }
+
+ if (!_.isUndefined(this._zeroEx)) {
+ // Since we do not have an index on the `taker` address and want to show
+ // transactions where an account is either the `maker` or `taker`, we loop
+ // through all fill events, and filter/cache them client-side.
+ const filterIndexObj = {};
+ await this._startListeningForExchangeLogFillEventsAsync(filterIndexObj);
+ }
+ }
+ private async _startListeningForExchangeLogFillEventsAsync(indexFilterValues: IndexedFilterValues): Promise<void> {
+ utils.assert(!_.isUndefined(this._zeroEx), 'ZeroEx must be instantiated.');
+ utils.assert(this._doesUserAddressExist(), BlockchainCallErrs.UserHasNoAssociatedAddresses);
+
+ // Fetch historical logs
+ await this._fetchHistoricalExchangeLogFillEventsAsync(indexFilterValues);
+
+ // Start a subscription for new logs
+ this._zeroEx.exchange.subscribe(
+ ExchangeEvents.LogFill,
+ indexFilterValues,
+ async (err: Error, decodedLogEvent: DecodedLogEvent<LogFillContractEventArgs>) => {
+ if (err) {
+ // Note: it's not entirely clear from the documentation which
+ // errors will be thrown by `watch`. For now, let's log the error
+ // to rollbar and stop watching when one occurs
+ // tslint:disable-next-line:no-floating-promises
+ errorReporter.reportAsync(err); // fire and forget
+ return;
+ } else {
+ const decodedLog = decodedLogEvent.log;
+ if (!this._doesLogEventInvolveUser(decodedLog)) {
+ return; // We aren't interested in the fill event
+ }
+ this._updateLatestFillsBlockIfNeeded(decodedLog.blockNumber);
+ const fill = await this._convertDecodedLogToFillAsync(decodedLog);
+ if (decodedLogEvent.isRemoved) {
+ tradeHistoryStorage.removeFillFromUser(this._userAddress, this.networkId, fill);
+ } else {
+ tradeHistoryStorage.addFillToUser(this._userAddress, this.networkId, fill);
+ }
+ }
+ },
+ );
+ }
+ private async _fetchHistoricalExchangeLogFillEventsAsync(indexFilterValues: IndexedFilterValues) {
+ const fromBlock = tradeHistoryStorage.getFillsLatestBlock(this._userAddress, this.networkId);
+ const blockRange: BlockRange = {
+ fromBlock,
+ toBlock: 'latest' as BlockParam,
+ };
+ const decodedLogs = await this._zeroEx.exchange.getLogsAsync<LogFillContractEventArgs>(
+ ExchangeEvents.LogFill,
+ blockRange,
+ indexFilterValues,
+ );
+ for (const decodedLog of decodedLogs) {
+ if (!this._doesLogEventInvolveUser(decodedLog)) {
+ continue; // We aren't interested in the fill event
+ }
+ this._updateLatestFillsBlockIfNeeded(decodedLog.blockNumber);
+ const fill = await this._convertDecodedLogToFillAsync(decodedLog);
+ tradeHistoryStorage.addFillToUser(this._userAddress, this.networkId, fill);
+ }
+ }
+ private async _convertDecodedLogToFillAsync(decodedLog: LogWithDecodedArgs<LogFillContractEventArgs>) {
+ const args = decodedLog.args;
+ const blockTimestamp = await this._web3Wrapper.getBlockTimestampAsync(decodedLog.blockHash);
+ const fill = {
+ filledTakerTokenAmount: args.filledTakerTokenAmount,
+ filledMakerTokenAmount: args.filledMakerTokenAmount,
+ logIndex: decodedLog.logIndex,
+ maker: args.maker,
+ orderHash: args.orderHash,
+ taker: args.taker,
+ makerToken: args.makerToken,
+ takerToken: args.takerToken,
+ paidMakerFee: args.paidMakerFee,
+ paidTakerFee: args.paidTakerFee,
+ transactionHash: decodedLog.transactionHash,
+ blockTimestamp,
+ };
+ return fill;
+ }
+ private _doesLogEventInvolveUser(decodedLog: LogWithDecodedArgs<LogFillContractEventArgs>) {
+ const args = decodedLog.args;
+ const isUserMakerOrTaker = args.maker === this._userAddress || args.taker === this._userAddress;
+ return isUserMakerOrTaker;
+ }
+ private _updateLatestFillsBlockIfNeeded(blockNumber: number) {
+ const isBlockPending = _.isNull(blockNumber);
+ if (!isBlockPending) {
+ // Hack: I've observed the behavior where a client won't register certain fill events
+ // and lowering the cache blockNumber fixes the issue. As a quick fix for now, simply
+ // set the cached blockNumber 50 below the one returned. This way, upon refreshing, a user
+ // would still attempt to re-fetch events from the previous 50 blocks, but won't need to
+ // re-fetch all events in all blocks.
+ // TODO: Debug if this is a race condition, and apply a more precise fix
+ const blockNumberToSet =
+ blockNumber - BLOCK_NUMBER_BACK_TRACK < 0 ? 0 : blockNumber - BLOCK_NUMBER_BACK_TRACK;
+ tradeHistoryStorage.setFillsLatestBlock(this._userAddress, this.networkId, blockNumberToSet);
+ }
+ }
+ private _stopWatchingExchangeLogFillEvents(): void {
+ this._zeroEx.exchange.unsubscribeAll();
+ }
+ private async _getTokenRegistryTokensByAddressAsync(): Promise<TokenByAddress> {
+ utils.assert(!_.isUndefined(this._zeroEx), 'ZeroEx must be instantiated.');
+ const tokenRegistryTokens = await this._zeroEx.tokenRegistry.getTokensAsync();
+
+ const tokenByAddress: TokenByAddress = {};
+ _.each(tokenRegistryTokens, (t: ZeroExToken, i: number) => {
+ // HACK: For now we have a hard-coded list of iconUrls for the dummyTokens
+ // TODO: Refactor this out and pull the iconUrl directly from the TokenRegistry
+ const iconUrl = configs.ICON_URL_BY_SYMBOL[t.symbol];
+ // HACK: Temporarily we hijack the WETH addresses fetched from the tokenRegistry
+ // so that we can take our time with actually updating it. This ensures that when
+ // we deploy the new WETH page, everyone will re-fill their trackedTokens with the
+ // new canonical WETH.
+ // TODO: Remove this hack once we've updated the TokenRegistries
+ // Airtable task: https://airtable.com/tblFe0Q9JuKJPYbTn/viwsOG2Y97qdIeCIO/recv3VGmIorFzHBVz
+ let address = t.address;
+ if (configs.SHOULD_DEPRECATE_OLD_WETH_TOKEN && t.symbol === 'WETH') {
+ const newEtherTokenAddressIfExists = configs.NEW_WRAPPED_ETHERS[this.networkId];
+ if (!_.isUndefined(newEtherTokenAddressIfExists)) {
+ address = newEtherTokenAddressIfExists;
+ }
+ }
+ const token: Token = {
+ iconUrl,
+ address,
+ name: t.name,
+ symbol: t.symbol,
+ decimals: t.decimals,
+ isTracked: false,
+ isRegistered: true,
+ };
+ tokenByAddress[token.address] = token;
+ });
+ return tokenByAddress;
+ }
+ private async _onPageLoadInitFireAndForgetAsync() {
+ await Blockchain._onPageLoadAsync(); // wait for page to load
+
+ // Hack: We need to know the networkId the injectedWeb3 is connected to (if it is defined) in
+ // order to properly instantiate the web3Wrapper. Since we must use the async call, we cannot
+ // retrieve it from within the web3Wrapper constructor. This is and should remain the only
+ // call to a web3 instance outside of web3Wrapper in the entire dapp.
+ // In addition, if the user has an injectedWeb3 instance that is disconnected from a backing
+ // Ethereum node, this call will throw. We need to handle this case gracefully
+ const injectedWeb3 = (window as any).web3;
+ let networkIdIfExists: number;
+ if (!_.isUndefined(injectedWeb3)) {
+ try {
+ networkIdIfExists = _.parseInt(await promisify<string>(injectedWeb3.version.getNetwork)());
+ } catch (err) {
+ // Ignore error and proceed with networkId undefined
+ }
+ }
+
+ const provider = await Blockchain._getProviderAsync(injectedWeb3, networkIdIfExists);
+ const networkId = !_.isUndefined(networkIdIfExists)
+ ? networkIdIfExists
+ : configs.IS_MAINNET_ENABLED ? constants.NETWORK_ID_MAINNET : constants.NETWORK_ID_TESTNET;
+ const zeroExConfigs = {
+ networkId,
+ };
+ this._zeroEx = new ZeroEx(provider, zeroExConfigs);
+ this._updateProviderName(injectedWeb3);
+ const shouldPollUserAddress = true;
+ this._web3Wrapper = new Web3Wrapper(this._dispatcher, provider, networkId, shouldPollUserAddress);
+ await this._postInstantiationOrUpdatingProviderZeroExAsync();
+ }
+ // This method should always be run after instantiating or updating the provider
+ // of the ZeroEx instance.
+ private async _postInstantiationOrUpdatingProviderZeroExAsync() {
+ utils.assert(!_.isUndefined(this._zeroEx), 'ZeroEx must be instantiated.');
+ this._exchangeAddress = this._zeroEx.exchange.getContractAddress();
+ }
+ private _updateProviderName(injectedWeb3: Web3) {
+ const doesInjectedWeb3Exist = !_.isUndefined(injectedWeb3);
+ const providerName = doesInjectedWeb3Exist
+ ? Blockchain._getNameGivenProvider(injectedWeb3.currentProvider)
+ : constants.PROVIDER_NAME_PUBLIC;
+ this._dispatcher.updateInjectedProviderName(providerName);
+ }
+ private async _fetchTokenInformationAsync() {
+ utils.assert(
+ !_.isUndefined(this.networkId),
+ 'Cannot call fetchTokenInformationAsync if disconnected from Ethereum node',
+ );
+
+ this._dispatcher.updateBlockchainIsLoaded(false);
+ this._dispatcher.clearTokenByAddress();
+
+ const tokenRegistryTokensByAddress = await this._getTokenRegistryTokensByAddressAsync();
+
+ // HACK: We need to fetch the userAddress here because otherwise we cannot save the
+ // tracked tokens in localStorage under the users address nor fetch the token
+ // balances and allowances and we need to do this in order not to trigger the blockchain
+ // loading dialog to show up twice. First to load the contracts, and second to load the
+ // balances and allowances.
+ this._userAddress = await this._web3Wrapper.getFirstAccountIfExistsAsync();
+ if (!_.isEmpty(this._userAddress)) {
+ this._dispatcher.updateUserAddress(this._userAddress);
+ }
+
+ let trackedTokensIfExists = trackedTokenStorage.getTrackedTokensIfExists(this._userAddress, this.networkId);
+ const tokenRegistryTokens = _.values(tokenRegistryTokensByAddress);
+ if (_.isUndefined(trackedTokensIfExists)) {
+ trackedTokensIfExists = _.map(configs.DEFAULT_TRACKED_TOKEN_SYMBOLS, symbol => {
+ const token = _.find(tokenRegistryTokens, t => t.symbol === symbol);
+ token.isTracked = true;
+ return token;
+ });
+ _.each(trackedTokensIfExists, token => {
+ trackedTokenStorage.addTrackedTokenToUser(this._userAddress, this.networkId, token);
+ });
+ } else {
+ // Properly set all tokenRegistry tokens `isTracked` to true if they are in the existing trackedTokens array
+ _.each(trackedTokensIfExists, trackedToken => {
+ if (!_.isUndefined(tokenRegistryTokensByAddress[trackedToken.address])) {
+ tokenRegistryTokensByAddress[trackedToken.address].isTracked = true;
+ }
+ });
+ }
+ const allTokens = _.uniq([...tokenRegistryTokens, ...trackedTokensIfExists]);
+ this._dispatcher.updateTokenByAddress(allTokens);
+
+ // Get balance/allowance for tracked tokens
+ await this.updateTokenBalancesAndAllowancesAsync(trackedTokensIfExists);
+
+ const mostPopularTradingPairTokens: Token[] = [
+ _.find(allTokens, { symbol: configs.DEFAULT_TRACKED_TOKEN_SYMBOLS[0] }),
+ _.find(allTokens, { symbol: configs.DEFAULT_TRACKED_TOKEN_SYMBOLS[1] }),
+ ];
+ this._dispatcher.updateChosenAssetTokenAddress(Side.Deposit, mostPopularTradingPairTokens[0].address);
+ this._dispatcher.updateChosenAssetTokenAddress(Side.Receive, mostPopularTradingPairTokens[1].address);
+ this._dispatcher.updateBlockchainIsLoaded(true);
+ }
+ private async _instantiateContractIfExistsAsync(artifact: any, address?: string): Promise<ContractInstance> {
+ const c = await contract(artifact);
+ const providerObj = this._web3Wrapper.getProviderObj();
+ c.setProvider(providerObj);
+
+ const artifactNetworkConfigs = artifact.networks[this.networkId];
+ let contractAddress;
+ if (!_.isUndefined(address)) {
+ contractAddress = address;
+ } else if (!_.isUndefined(artifactNetworkConfigs)) {
+ contractAddress = artifactNetworkConfigs.address;
+ }
+
+ if (!_.isUndefined(contractAddress)) {
+ const doesContractExist = await this.doesContractExistAtAddressAsync(contractAddress);
+ if (!doesContractExist) {
+ utils.consoleLog(`Contract does not exist: ${artifact.contract_name} at ${contractAddress}`);
+ throw new Error(BlockchainCallErrs.ContractDoesNotExist);
+ }
+ }
+
+ try {
+ const contractInstance = _.isUndefined(address) ? await c.deployed() : await c.at(address);
+ return contractInstance;
+ } catch (err) {
+ const errMsg = `${err}`;
+ utils.consoleLog(`Notice: Error encountered: ${err} ${err.stack}`);
+ if (_.includes(errMsg, 'not been deployed to detected network')) {
+ throw new Error(BlockchainCallErrs.ContractDoesNotExist);
+ } else {
+ await errorReporter.reportAsync(err);
+ throw new Error(BlockchainCallErrs.UnhandledError);
+ }
+ }
+ }
} // tslint:disable:max-file-line-count
diff --git a/packages/website/ts/components/dialogs/blockchain_err_dialog.tsx b/packages/website/ts/components/dialogs/blockchain_err_dialog.tsx
index e0f61a29b..f555ca6b1 100644
--- a/packages/website/ts/components/dialogs/blockchain_err_dialog.tsx
+++ b/packages/website/ts/components/dialogs/blockchain_err_dialog.tsx
@@ -9,150 +9,150 @@ import { configs } from 'ts/utils/configs';
import { constants } from 'ts/utils/constants';
interface BlockchainErrDialogProps {
- blockchain: Blockchain;
- blockchainErr: BlockchainErrs;
- isOpen: boolean;
- userAddress: string;
- toggleDialogFn: (isOpen: boolean) => void;
- networkId: number;
+ blockchain: Blockchain;
+ blockchainErr: BlockchainErrs;
+ isOpen: boolean;
+ userAddress: string;
+ toggleDialogFn: (isOpen: boolean) => void;
+ networkId: number;
}
export class BlockchainErrDialog extends React.Component<BlockchainErrDialogProps, undefined> {
- public render() {
- const dialogActions = [
- <FlatButton
- key="blockchainErrOk"
- label="Ok"
- primary={true}
- onTouchTap={this.props.toggleDialogFn.bind(this.props.toggleDialogFn, false)}
- />,
- ];
+ public render() {
+ const dialogActions = [
+ <FlatButton
+ key="blockchainErrOk"
+ label="Ok"
+ primary={true}
+ onTouchTap={this.props.toggleDialogFn.bind(this.props.toggleDialogFn, false)}
+ />,
+ ];
- const hasWalletAddress = this.props.userAddress !== '';
- return (
- <Dialog
- title={this._getTitle(hasWalletAddress)}
- titleStyle={{ fontWeight: 100 }}
- actions={dialogActions}
- open={this.props.isOpen}
- contentStyle={{ width: 400 }}
- onRequestClose={this.props.toggleDialogFn.bind(this.props.toggleDialogFn, false)}
- autoScrollBodyContent={true}
- >
- <div className="pt2" style={{ color: colors.grey700 }}>
- {this._renderExplanation(hasWalletAddress)}
- </div>
- </Dialog>
- );
- }
- private _getTitle(hasWalletAddress: boolean) {
- if (this.props.blockchainErr === BlockchainErrs.AContractNotDeployedOnNetwork) {
- return '0x smart contracts not found';
- } else if (!hasWalletAddress) {
- return 'Enable wallet communication';
- } else if (this.props.blockchainErr === BlockchainErrs.DisconnectedFromEthereumNode) {
- return 'Disconnected from Ethereum network';
- } else {
- return 'Unexpected error';
- }
- }
- private _renderExplanation(hasWalletAddress: boolean) {
- if (this.props.blockchainErr === BlockchainErrs.AContractNotDeployedOnNetwork) {
- return this._renderContractsNotDeployedExplanation();
- } else if (!hasWalletAddress) {
- return this._renderNoWalletFoundExplanation();
- } else if (this.props.blockchainErr === BlockchainErrs.DisconnectedFromEthereumNode) {
- return this._renderDisconnectedFromNode();
- } else {
- return this._renderUnexpectedErrorExplanation();
- }
- }
- private _renderDisconnectedFromNode() {
- return (
- <div>
- You were disconnected from the backing Ethereum node. If using{' '}
- <a href={constants.URL_METAMASK_CHROME_STORE} target="_blank">
- Metamask
- </a>{' '}
- or{' '}
- <a href={constants.URL_MIST_DOWNLOAD} target="_blank">
- Mist
- </a>{' '}
- try refreshing the page. If using a locally hosted Ethereum node, make sure it's still running.
- </div>
- );
- }
- private _renderUnexpectedErrorExplanation() {
- return <div>We encountered an unexpected error. Please try refreshing the page.</div>;
- }
- private _renderNoWalletFoundExplanation() {
- return (
- <div>
- <div>
- We were unable to access an Ethereum wallet you control. In order to interact with the 0x portal
- dApp, we need a way to interact with one of your Ethereum wallets. There are two easy ways you can
- enable us to do that:
- </div>
- <h4>1. Metamask chrome extension</h4>
- <div>
- You can install the{' '}
- <a href={constants.URL_METAMASK_CHROME_STORE} target="_blank">
- Metamask
- </a>{' '}
- Chrome extension Ethereum wallet. Once installed and set up, refresh this page.
- <div className="pt1">
- <span className="bold">Note:</span> If you already have Metamask installed, make sure it is
- unlocked.
- </div>
- </div>
- <h4>Parity Signer</h4>
- <div>
- The{' '}
- <a href={constants.URL_PARITY_CHROME_STORE} target="_blank">
- Parity Signer Chrome extension
- </a>{' '}
- lets you connect to a locally running Parity node. Make sure you have started your local Parity node
- with {configs.IS_MAINNET_ENABLED && '`parity ui` or'} `parity --chain kovan ui` in order to connect
- to {configs.IS_MAINNET_ENABLED ? 'mainnet or Kovan respectively.' : 'Kovan.'}
- </div>
- <div className="pt2">
- <span className="bold">Note:</span> If you have done one of the above steps and are still seeing
- this message, we might still be unable to retrieve an Ethereum address by calling
- `web3.eth.accounts`. Make sure you have created at least one Ethereum address.
- </div>
- </div>
- );
- }
- private _renderContractsNotDeployedExplanation() {
- return (
- <div>
- <div>
- The 0x smart contracts are not deployed on the Ethereum network you are currently connected to
- (network Id: {this.props.networkId}). In order to use the 0x portal dApp, please connect to the{' '}
- {constants.TESTNET_NAME} testnet (network Id: {constants.NETWORK_ID_TESTNET})
- {configs.IS_MAINNET_ENABLED
- ? ` or ${constants.MAINNET_NAME} (network Id: ${constants.NETWORK_ID_MAINNET}).`
- : `.`}
- </div>
- <h4>Metamask</h4>
- <div>
- If you are using{' '}
- <a href={constants.URL_METAMASK_CHROME_STORE} target="_blank">
- Metamask
- </a>, you can switch networks in the top left corner of the extension popover.
- </div>
- <h4>Parity Signer</h4>
- <div>
- If using the{' '}
- <a href={constants.URL_PARITY_CHROME_STORE} target="_blank">
- Parity Signer Chrome extension
- </a>, make sure to start your local Parity node with{' '}
- {configs.IS_MAINNET_ENABLED
- ? '`parity ui` or `parity --chain Kovan ui` in order to connect to mainnet \
+ const hasWalletAddress = this.props.userAddress !== '';
+ return (
+ <Dialog
+ title={this._getTitle(hasWalletAddress)}
+ titleStyle={{ fontWeight: 100 }}
+ actions={dialogActions}
+ open={this.props.isOpen}
+ contentStyle={{ width: 400 }}
+ onRequestClose={this.props.toggleDialogFn.bind(this.props.toggleDialogFn, false)}
+ autoScrollBodyContent={true}
+ >
+ <div className="pt2" style={{ color: colors.grey700 }}>
+ {this._renderExplanation(hasWalletAddress)}
+ </div>
+ </Dialog>
+ );
+ }
+ private _getTitle(hasWalletAddress: boolean) {
+ if (this.props.blockchainErr === BlockchainErrs.AContractNotDeployedOnNetwork) {
+ return '0x smart contracts not found';
+ } else if (!hasWalletAddress) {
+ return 'Enable wallet communication';
+ } else if (this.props.blockchainErr === BlockchainErrs.DisconnectedFromEthereumNode) {
+ return 'Disconnected from Ethereum network';
+ } else {
+ return 'Unexpected error';
+ }
+ }
+ private _renderExplanation(hasWalletAddress: boolean) {
+ if (this.props.blockchainErr === BlockchainErrs.AContractNotDeployedOnNetwork) {
+ return this._renderContractsNotDeployedExplanation();
+ } else if (!hasWalletAddress) {
+ return this._renderNoWalletFoundExplanation();
+ } else if (this.props.blockchainErr === BlockchainErrs.DisconnectedFromEthereumNode) {
+ return this._renderDisconnectedFromNode();
+ } else {
+ return this._renderUnexpectedErrorExplanation();
+ }
+ }
+ private _renderDisconnectedFromNode() {
+ return (
+ <div>
+ You were disconnected from the backing Ethereum node. If using{' '}
+ <a href={constants.URL_METAMASK_CHROME_STORE} target="_blank">
+ Metamask
+ </a>{' '}
+ or{' '}
+ <a href={constants.URL_MIST_DOWNLOAD} target="_blank">
+ Mist
+ </a>{' '}
+ try refreshing the page. If using a locally hosted Ethereum node, make sure it's still running.
+ </div>
+ );
+ }
+ private _renderUnexpectedErrorExplanation() {
+ return <div>We encountered an unexpected error. Please try refreshing the page.</div>;
+ }
+ private _renderNoWalletFoundExplanation() {
+ return (
+ <div>
+ <div>
+ We were unable to access an Ethereum wallet you control. In order to interact with the 0x portal
+ dApp, we need a way to interact with one of your Ethereum wallets. There are two easy ways you can
+ enable us to do that:
+ </div>
+ <h4>1. Metamask chrome extension</h4>
+ <div>
+ You can install the{' '}
+ <a href={constants.URL_METAMASK_CHROME_STORE} target="_blank">
+ Metamask
+ </a>{' '}
+ Chrome extension Ethereum wallet. Once installed and set up, refresh this page.
+ <div className="pt1">
+ <span className="bold">Note:</span> If you already have Metamask installed, make sure it is
+ unlocked.
+ </div>
+ </div>
+ <h4>Parity Signer</h4>
+ <div>
+ The{' '}
+ <a href={constants.URL_PARITY_CHROME_STORE} target="_blank">
+ Parity Signer Chrome extension
+ </a>{' '}
+ lets you connect to a locally running Parity node. Make sure you have started your local Parity node
+ with {configs.IS_MAINNET_ENABLED && '`parity ui` or'} `parity --chain kovan ui` in order to connect
+ to {configs.IS_MAINNET_ENABLED ? 'mainnet or Kovan respectively.' : 'Kovan.'}
+ </div>
+ <div className="pt2">
+ <span className="bold">Note:</span> If you have done one of the above steps and are still seeing
+ this message, we might still be unable to retrieve an Ethereum address by calling
+ `web3.eth.accounts`. Make sure you have created at least one Ethereum address.
+ </div>
+ </div>
+ );
+ }
+ private _renderContractsNotDeployedExplanation() {
+ return (
+ <div>
+ <div>
+ The 0x smart contracts are not deployed on the Ethereum network you are currently connected to
+ (network Id: {this.props.networkId}). In order to use the 0x portal dApp, please connect to the{' '}
+ {constants.TESTNET_NAME} testnet (network Id: {constants.NETWORK_ID_TESTNET})
+ {configs.IS_MAINNET_ENABLED
+ ? ` or ${constants.MAINNET_NAME} (network Id: ${constants.NETWORK_ID_MAINNET}).`
+ : `.`}
+ </div>
+ <h4>Metamask</h4>
+ <div>
+ If you are using{' '}
+ <a href={constants.URL_METAMASK_CHROME_STORE} target="_blank">
+ Metamask
+ </a>, you can switch networks in the top left corner of the extension popover.
+ </div>
+ <h4>Parity Signer</h4>
+ <div>
+ If using the{' '}
+ <a href={constants.URL_PARITY_CHROME_STORE} target="_blank">
+ Parity Signer Chrome extension
+ </a>, make sure to start your local Parity node with{' '}
+ {configs.IS_MAINNET_ENABLED
+ ? '`parity ui` or `parity --chain Kovan ui` in order to connect to mainnet \
or Kovan respectively.'
- : '`parity --chain kovan ui` in order to connect to Kovan.'}
- </div>
- </div>
- );
- }
+ : '`parity --chain kovan ui` in order to connect to Kovan.'}
+ </div>
+ </div>
+ );
+ }
}
diff --git a/packages/website/ts/components/dialogs/eth_weth_conversion_dialog.tsx b/packages/website/ts/components/dialogs/eth_weth_conversion_dialog.tsx
index 45ba5cc9e..661cc1d8c 100644
--- a/packages/website/ts/components/dialogs/eth_weth_conversion_dialog.tsx
+++ b/packages/website/ts/components/dialogs/eth_weth_conversion_dialog.tsx
@@ -8,156 +8,156 @@ import { Side, Token, TokenState } from 'ts/types';
import { colors } from 'ts/utils/colors';
interface EthWethConversionDialogProps {
- direction: Side;
- onComplete: (direction: Side, value: BigNumber) => void;
- onCancelled: () => void;
- isOpen: boolean;
- token: Token;
- tokenState: TokenState;
- etherBalance: BigNumber;
+ direction: Side;
+ onComplete: (direction: Side, value: BigNumber) => void;
+ onCancelled: () => void;
+ isOpen: boolean;
+ token: Token;
+ tokenState: TokenState;
+ etherBalance: BigNumber;
}
interface EthWethConversionDialogState {
- value?: BigNumber;
- shouldShowIncompleteErrs: boolean;
- hasErrors: boolean;
+ value?: BigNumber;
+ shouldShowIncompleteErrs: boolean;
+ hasErrors: boolean;
}
export class EthWethConversionDialog extends React.Component<
- EthWethConversionDialogProps,
- EthWethConversionDialogState
+ EthWethConversionDialogProps,
+ EthWethConversionDialogState
> {
- constructor() {
- super();
- this.state = {
- shouldShowIncompleteErrs: false,
- hasErrors: false,
- };
- }
- public render() {
- const convertDialogActions = [
- <FlatButton key="cancel" label="Cancel" onTouchTap={this._onCancel.bind(this)} />,
- <FlatButton key="convert" label="Convert" primary={true} onTouchTap={this._onConvertClick.bind(this)} />,
- ];
- const title = this.props.direction === Side.Deposit ? 'Wrap ETH' : 'Unwrap WETH';
- return (
- <Dialog
- title={title}
- titleStyle={{ fontWeight: 100 }}
- actions={convertDialogActions}
- contentStyle={{ width: 448 }}
- open={this.props.isOpen}
- >
- {this._renderConversionDialogBody()}
- </Dialog>
- );
- }
- private _renderConversionDialogBody() {
- const explanation =
- this.props.direction === Side.Deposit
- ? 'Convert your Ether into a tokenized, tradable form.'
- : "Convert your Wrapped Ether back into it's native form.";
- const isWrappedVersion = this.props.direction === Side.Receive;
- return (
- <div>
- <div className="pb2">{explanation}</div>
- <div className="mx-auto" style={{ maxWidth: 312 }}>
- <div className="flex">
- {this._renderCurrency(isWrappedVersion)}
- <div style={{ paddingTop: 68 }}>
- <i style={{ fontSize: 28, color: colors.darkBlue }} className="zmdi zmdi-arrow-right" />
- </div>
- {this._renderCurrency(!isWrappedVersion)}
- </div>
- <div className="pt2 mx-auto" style={{ width: 245 }}>
- {this.props.direction === Side.Receive ? (
- <TokenAmountInput
- token={this.props.token}
- tokenState={this.props.tokenState}
- shouldShowIncompleteErrs={this.state.shouldShowIncompleteErrs}
- shouldCheckBalance={true}
- shouldCheckAllowance={false}
- onChange={this._onValueChange.bind(this)}
- amount={this.state.value}
- onVisitBalancesPageClick={this.props.onCancelled}
- />
- ) : (
- <EthAmountInput
- balance={this.props.etherBalance}
- amount={this.state.value}
- onChange={this._onValueChange.bind(this)}
- shouldCheckBalance={true}
- shouldShowIncompleteErrs={this.state.shouldShowIncompleteErrs}
- onVisitBalancesPageClick={this.props.onCancelled}
- />
- )}
- <div className="pt1" style={{ fontSize: 12 }}>
- <div className="left">1 ETH = 1 WETH</div>
- {this.props.direction === Side.Receive && (
- <div
- className="right"
- onClick={this._onMaxClick.bind(this)}
- style={{
- color: colors.darkBlue,
- textDecoration: 'underline',
- cursor: 'pointer',
- }}
- >
- Max
- </div>
- )}
- </div>
- </div>
- </div>
- </div>
- );
- }
- private _renderCurrency(isWrappedVersion: boolean) {
- const name = isWrappedVersion ? 'Wrapped Ether' : 'Ether';
- const iconUrl = isWrappedVersion ? '/images/token_icons/ether_erc20.png' : '/images/ether.png';
- const symbol = isWrappedVersion ? 'WETH' : 'ETH';
- return (
- <div className="mx-auto pt2">
- <div className="center" style={{ color: colors.darkBlue }}>
- {name}
- </div>
- <div className="center py2">
- <img src={iconUrl} style={{ width: 60 }} />
- </div>
- <div className="center" style={{ fontSize: 12 }}>
- ({symbol})
- </div>
- </div>
- );
- }
- private _onMaxClick() {
- this.setState({
- value: this.props.tokenState.balance,
- });
- }
- private _onValueChange(isValid: boolean, amount?: BigNumber) {
- this.setState({
- value: amount,
- hasErrors: !isValid,
- });
- }
- private _onConvertClick() {
- if (this.state.hasErrors) {
- this.setState({
- shouldShowIncompleteErrs: true,
- });
- } else {
- const value = this.state.value;
- this.setState({
- value: undefined,
- });
- this.props.onComplete(this.props.direction, value);
- }
- }
- private _onCancel() {
- this.setState({
- value: undefined,
- });
- this.props.onCancelled();
- }
+ constructor() {
+ super();
+ this.state = {
+ shouldShowIncompleteErrs: false,
+ hasErrors: false,
+ };
+ }
+ public render() {
+ const convertDialogActions = [
+ <FlatButton key="cancel" label="Cancel" onTouchTap={this._onCancel.bind(this)} />,
+ <FlatButton key="convert" label="Convert" primary={true} onTouchTap={this._onConvertClick.bind(this)} />,
+ ];
+ const title = this.props.direction === Side.Deposit ? 'Wrap ETH' : 'Unwrap WETH';
+ return (
+ <Dialog
+ title={title}
+ titleStyle={{ fontWeight: 100 }}
+ actions={convertDialogActions}
+ contentStyle={{ width: 448 }}
+ open={this.props.isOpen}
+ >
+ {this._renderConversionDialogBody()}
+ </Dialog>
+ );
+ }
+ private _renderConversionDialogBody() {
+ const explanation =
+ this.props.direction === Side.Deposit
+ ? 'Convert your Ether into a tokenized, tradable form.'
+ : "Convert your Wrapped Ether back into it's native form.";
+ const isWrappedVersion = this.props.direction === Side.Receive;
+ return (
+ <div>
+ <div className="pb2">{explanation}</div>
+ <div className="mx-auto" style={{ maxWidth: 312 }}>
+ <div className="flex">
+ {this._renderCurrency(isWrappedVersion)}
+ <div style={{ paddingTop: 68 }}>
+ <i style={{ fontSize: 28, color: colors.darkBlue }} className="zmdi zmdi-arrow-right" />
+ </div>
+ {this._renderCurrency(!isWrappedVersion)}
+ </div>
+ <div className="pt2 mx-auto" style={{ width: 245 }}>
+ {this.props.direction === Side.Receive ? (
+ <TokenAmountInput
+ token={this.props.token}
+ tokenState={this.props.tokenState}
+ shouldShowIncompleteErrs={this.state.shouldShowIncompleteErrs}
+ shouldCheckBalance={true}
+ shouldCheckAllowance={false}
+ onChange={this._onValueChange.bind(this)}
+ amount={this.state.value}
+ onVisitBalancesPageClick={this.props.onCancelled}
+ />
+ ) : (
+ <EthAmountInput
+ balance={this.props.etherBalance}
+ amount={this.state.value}
+ onChange={this._onValueChange.bind(this)}
+ shouldCheckBalance={true}
+ shouldShowIncompleteErrs={this.state.shouldShowIncompleteErrs}
+ onVisitBalancesPageClick={this.props.onCancelled}
+ />
+ )}
+ <div className="pt1" style={{ fontSize: 12 }}>
+ <div className="left">1 ETH = 1 WETH</div>
+ {this.props.direction === Side.Receive && (
+ <div
+ className="right"
+ onClick={this._onMaxClick.bind(this)}
+ style={{
+ color: colors.darkBlue,
+ textDecoration: 'underline',
+ cursor: 'pointer',
+ }}
+ >
+ Max
+ </div>
+ )}
+ </div>
+ </div>
+ </div>
+ </div>
+ );
+ }
+ private _renderCurrency(isWrappedVersion: boolean) {
+ const name = isWrappedVersion ? 'Wrapped Ether' : 'Ether';
+ const iconUrl = isWrappedVersion ? '/images/token_icons/ether_erc20.png' : '/images/ether.png';
+ const symbol = isWrappedVersion ? 'WETH' : 'ETH';
+ return (
+ <div className="mx-auto pt2">
+ <div className="center" style={{ color: colors.darkBlue }}>
+ {name}
+ </div>
+ <div className="center py2">
+ <img src={iconUrl} style={{ width: 60 }} />
+ </div>
+ <div className="center" style={{ fontSize: 12 }}>
+ ({symbol})
+ </div>
+ </div>
+ );
+ }
+ private _onMaxClick() {
+ this.setState({
+ value: this.props.tokenState.balance,
+ });
+ }
+ private _onValueChange(isValid: boolean, amount?: BigNumber) {
+ this.setState({
+ value: amount,
+ hasErrors: !isValid,
+ });
+ }
+ private _onConvertClick() {
+ if (this.state.hasErrors) {
+ this.setState({
+ shouldShowIncompleteErrs: true,
+ });
+ } else {
+ const value = this.state.value;
+ this.setState({
+ value: undefined,
+ });
+ this.props.onComplete(this.props.direction, value);
+ }
+ }
+ private _onCancel() {
+ this.setState({
+ value: undefined,
+ });
+ this.props.onCancelled();
+ }
}
diff --git a/packages/website/ts/components/dialogs/ledger_config_dialog.tsx b/packages/website/ts/components/dialogs/ledger_config_dialog.tsx
index 8b7760a1a..60db93c52 100644
--- a/packages/website/ts/components/dialogs/ledger_config_dialog.tsx
+++ b/packages/website/ts/components/dialogs/ledger_config_dialog.tsx
@@ -17,245 +17,245 @@ import { utils } from 'ts/utils/utils';
const VALID_ETHEREUM_DERIVATION_PATH_PREFIX = `44'/60'`;
enum LedgerSteps {
- CONNECT,
- SELECT_ADDRESS,
+ CONNECT,
+ SELECT_ADDRESS,
}
interface LedgerConfigDialogProps {
- isOpen: boolean;
- toggleDialogFn: (isOpen: boolean) => void;
- dispatcher: Dispatcher;
- blockchain: Blockchain;
- networkId: number;
+ isOpen: boolean;
+ toggleDialogFn: (isOpen: boolean) => void;
+ dispatcher: Dispatcher;
+ blockchain: Blockchain;
+ networkId: number;
}
interface LedgerConfigDialogState {
- didConnectFail: boolean;
- stepIndex: LedgerSteps;
- userAddresses: string[];
- addressBalances: BigNumber[];
- derivationPath: string;
- derivationErrMsg: string;
+ didConnectFail: boolean;
+ stepIndex: LedgerSteps;
+ userAddresses: string[];
+ addressBalances: BigNumber[];
+ derivationPath: string;
+ derivationErrMsg: string;
}
export class LedgerConfigDialog extends React.Component<LedgerConfigDialogProps, LedgerConfigDialogState> {
- constructor(props: LedgerConfigDialogProps) {
- super(props);
- this.state = {
- didConnectFail: false,
- stepIndex: LedgerSteps.CONNECT,
- userAddresses: [],
- addressBalances: [],
- derivationPath: configs.DEFAULT_DERIVATION_PATH,
- derivationErrMsg: '',
- };
- }
- public render() {
- const dialogActions = [
- <FlatButton key="ledgerConnectCancel" label="Cancel" onTouchTap={this._onClose.bind(this)} />,
- ];
- const dialogTitle =
- this.state.stepIndex === LedgerSteps.CONNECT ? 'Connect to your Ledger' : 'Select desired address';
- return (
- <Dialog
- title={dialogTitle}
- titleStyle={{ fontWeight: 100 }}
- actions={dialogActions}
- open={this.props.isOpen}
- onRequestClose={this._onClose.bind(this)}
- autoScrollBodyContent={true}
- bodyStyle={{ paddingBottom: 0 }}
- >
- <div style={{ color: colors.grey700, paddingTop: 1 }}>
- {this.state.stepIndex === LedgerSteps.CONNECT && this._renderConnectStep()}
- {this.state.stepIndex === LedgerSteps.SELECT_ADDRESS && this._renderSelectAddressStep()}
- </div>
- </Dialog>
- );
- }
- private _renderConnectStep() {
- return (
- <div>
- <div className="h4 pt3">Follow these instructions before proceeding:</div>
- <ol>
- <li className="pb1">Connect your Ledger Nano S & Open the Ethereum application</li>
- <li className="pb1">Verify that Browser Support is enabled in Settings</li>
- <li className="pb1">
- If no Browser Support is found in settings, verify that you have{' '}
- <a href="https://www.ledgerwallet.com/apps/manager" target="_blank">
- Firmware >1.2
- </a>
- </li>
- </ol>
- <div className="center pb3">
- <LifeCycleRaisedButton
- isPrimary={true}
- labelReady="Connect to Ledger"
- labelLoading="Connecting..."
- labelComplete="Connected!"
- onClickAsyncFn={this._onConnectLedgerClickAsync.bind(this, true)}
- />
- {this.state.didConnectFail && (
- <div className="pt2 left-align" style={{ color: colors.red200 }}>
- Failed to connect. Follow the instructions and try again.
- </div>
- )}
- </div>
- </div>
- );
- }
- private _renderSelectAddressStep() {
- return (
- <div>
- <div>
- <Table bodyStyle={{ height: 300 }} onRowSelection={this._onAddressSelected.bind(this)}>
- <TableHeader displaySelectAll={false}>
- <TableRow>
- <TableHeaderColumn colSpan={2}>Address</TableHeaderColumn>
- <TableHeaderColumn>Balance</TableHeaderColumn>
- </TableRow>
- </TableHeader>
- <TableBody>{this._renderAddressTableRows()}</TableBody>
- </Table>
- </div>
- <div className="flex pt2" style={{ height: 100 }}>
- <div className="overflow-hidden" style={{ width: 180 }}>
- <TextField
- floatingLabelFixed={true}
- floatingLabelStyle={{ color: colors.grey }}
- floatingLabelText="Update path derivation (advanced)"
- value={this.state.derivationPath}
- errorText={this.state.derivationErrMsg}
- onChange={this._onDerivationPathChanged.bind(this)}
- />
- </div>
- <div className="pl2" style={{ paddingTop: 28 }}>
- <LifeCycleRaisedButton
- labelReady="Update"
- labelLoading="Updating..."
- labelComplete="Updated!"
- onClickAsyncFn={this._onFetchAddressesForDerivationPathAsync.bind(this)}
- />
- </div>
- </div>
- </div>
- );
- }
- private _renderAddressTableRows() {
- const rows = _.map(this.state.userAddresses, (userAddress: string, i: number) => {
- const balance = this.state.addressBalances[i];
- const addressTooltipId = `address-${userAddress}`;
- const balanceTooltipId = `balance-${userAddress}`;
- const networkName = constants.NETWORK_NAME_BY_ID[this.props.networkId];
- // We specifically prefix kovan ETH.
- // TODO: We should probably add prefixes for all networks
- const isKovanNetwork = networkName === 'Kovan';
- const balanceString = `${balance.toString()} ${isKovanNetwork ? 'Kovan ' : ''}ETH`;
- return (
- <TableRow key={userAddress} style={{ height: 40 }}>
- <TableRowColumn colSpan={2}>
- <div data-tip={true} data-for={addressTooltipId}>
- {userAddress}
- </div>
- <ReactTooltip id={addressTooltipId}>{userAddress}</ReactTooltip>
- </TableRowColumn>
- <TableRowColumn>
- <div data-tip={true} data-for={balanceTooltipId}>
- {balanceString}
- </div>
- <ReactTooltip id={balanceTooltipId}>{balanceString}</ReactTooltip>
- </TableRowColumn>
- </TableRow>
- );
- });
- return rows;
- }
- private _onClose() {
- this.setState({
- didConnectFail: false,
- });
- const isOpen = false;
- this.props.toggleDialogFn(isOpen);
- }
- private _onAddressSelected(selectedRowIndexes: number[]) {
- const selectedRowIndex = selectedRowIndexes[0];
- this.props.blockchain.updateLedgerDerivationIndex(selectedRowIndex);
- const selectedAddress = this.state.userAddresses[selectedRowIndex];
- const selectAddressBalance = this.state.addressBalances[selectedRowIndex];
- this.props.dispatcher.updateUserAddress(selectedAddress);
- this.props.blockchain.updateWeb3WrapperPrevUserAddress(selectedAddress);
- this.props.dispatcher.updateUserEtherBalance(selectAddressBalance);
- this.setState({
- stepIndex: LedgerSteps.CONNECT,
- });
- const isOpen = false;
- this.props.toggleDialogFn(isOpen);
- }
- private async _onFetchAddressesForDerivationPathAsync(): Promise<boolean> {
- const currentlySetPath = this.props.blockchain.getLedgerDerivationPathIfExists();
- let didSucceed;
- if (currentlySetPath === this.state.derivationPath) {
- didSucceed = true;
- return didSucceed;
- }
- this.props.blockchain.updateLedgerDerivationPathIfExists(this.state.derivationPath);
- didSucceed = await this._fetchAddressesAndBalancesAsync();
- if (!didSucceed) {
- this.setState({
- derivationErrMsg: 'Failed to connect to Ledger.',
- });
- }
- return didSucceed;
- }
- private async _fetchAddressesAndBalancesAsync() {
- let userAddresses: string[];
- const addressBalances: BigNumber[] = [];
- try {
- userAddresses = await this._getUserAddressesAsync();
- for (const address of userAddresses) {
- const balance = await this.props.blockchain.getBalanceInEthAsync(address);
- addressBalances.push(balance);
- }
- } catch (err) {
- utils.consoleLog(`Ledger error: ${JSON.stringify(err)}`);
- this.setState({
- didConnectFail: true,
- });
- return false;
- }
- this.setState({
- userAddresses,
- addressBalances,
- });
- return true;
- }
- private _onDerivationPathChanged(e: any, derivationPath: string) {
- let derivationErrMsg = '';
- if (!_.startsWith(derivationPath, VALID_ETHEREUM_DERIVATION_PATH_PREFIX)) {
- derivationErrMsg = 'Must be valid Ethereum path.';
- }
+ constructor(props: LedgerConfigDialogProps) {
+ super(props);
+ this.state = {
+ didConnectFail: false,
+ stepIndex: LedgerSteps.CONNECT,
+ userAddresses: [],
+ addressBalances: [],
+ derivationPath: configs.DEFAULT_DERIVATION_PATH,
+ derivationErrMsg: '',
+ };
+ }
+ public render() {
+ const dialogActions = [
+ <FlatButton key="ledgerConnectCancel" label="Cancel" onTouchTap={this._onClose.bind(this)} />,
+ ];
+ const dialogTitle =
+ this.state.stepIndex === LedgerSteps.CONNECT ? 'Connect to your Ledger' : 'Select desired address';
+ return (
+ <Dialog
+ title={dialogTitle}
+ titleStyle={{ fontWeight: 100 }}
+ actions={dialogActions}
+ open={this.props.isOpen}
+ onRequestClose={this._onClose.bind(this)}
+ autoScrollBodyContent={true}
+ bodyStyle={{ paddingBottom: 0 }}
+ >
+ <div style={{ color: colors.grey700, paddingTop: 1 }}>
+ {this.state.stepIndex === LedgerSteps.CONNECT && this._renderConnectStep()}
+ {this.state.stepIndex === LedgerSteps.SELECT_ADDRESS && this._renderSelectAddressStep()}
+ </div>
+ </Dialog>
+ );
+ }
+ private _renderConnectStep() {
+ return (
+ <div>
+ <div className="h4 pt3">Follow these instructions before proceeding:</div>
+ <ol>
+ <li className="pb1">Connect your Ledger Nano S & Open the Ethereum application</li>
+ <li className="pb1">Verify that Browser Support is enabled in Settings</li>
+ <li className="pb1">
+ If no Browser Support is found in settings, verify that you have{' '}
+ <a href="https://www.ledgerwallet.com/apps/manager" target="_blank">
+ Firmware >1.2
+ </a>
+ </li>
+ </ol>
+ <div className="center pb3">
+ <LifeCycleRaisedButton
+ isPrimary={true}
+ labelReady="Connect to Ledger"
+ labelLoading="Connecting..."
+ labelComplete="Connected!"
+ onClickAsyncFn={this._onConnectLedgerClickAsync.bind(this, true)}
+ />
+ {this.state.didConnectFail && (
+ <div className="pt2 left-align" style={{ color: colors.red200 }}>
+ Failed to connect. Follow the instructions and try again.
+ </div>
+ )}
+ </div>
+ </div>
+ );
+ }
+ private _renderSelectAddressStep() {
+ return (
+ <div>
+ <div>
+ <Table bodyStyle={{ height: 300 }} onRowSelection={this._onAddressSelected.bind(this)}>
+ <TableHeader displaySelectAll={false}>
+ <TableRow>
+ <TableHeaderColumn colSpan={2}>Address</TableHeaderColumn>
+ <TableHeaderColumn>Balance</TableHeaderColumn>
+ </TableRow>
+ </TableHeader>
+ <TableBody>{this._renderAddressTableRows()}</TableBody>
+ </Table>
+ </div>
+ <div className="flex pt2" style={{ height: 100 }}>
+ <div className="overflow-hidden" style={{ width: 180 }}>
+ <TextField
+ floatingLabelFixed={true}
+ floatingLabelStyle={{ color: colors.grey }}
+ floatingLabelText="Update path derivation (advanced)"
+ value={this.state.derivationPath}
+ errorText={this.state.derivationErrMsg}
+ onChange={this._onDerivationPathChanged.bind(this)}
+ />
+ </div>
+ <div className="pl2" style={{ paddingTop: 28 }}>
+ <LifeCycleRaisedButton
+ labelReady="Update"
+ labelLoading="Updating..."
+ labelComplete="Updated!"
+ onClickAsyncFn={this._onFetchAddressesForDerivationPathAsync.bind(this)}
+ />
+ </div>
+ </div>
+ </div>
+ );
+ }
+ private _renderAddressTableRows() {
+ const rows = _.map(this.state.userAddresses, (userAddress: string, i: number) => {
+ const balance = this.state.addressBalances[i];
+ const addressTooltipId = `address-${userAddress}`;
+ const balanceTooltipId = `balance-${userAddress}`;
+ const networkName = constants.NETWORK_NAME_BY_ID[this.props.networkId];
+ // We specifically prefix kovan ETH.
+ // TODO: We should probably add prefixes for all networks
+ const isKovanNetwork = networkName === 'Kovan';
+ const balanceString = `${balance.toString()} ${isKovanNetwork ? 'Kovan ' : ''}ETH`;
+ return (
+ <TableRow key={userAddress} style={{ height: 40 }}>
+ <TableRowColumn colSpan={2}>
+ <div data-tip={true} data-for={addressTooltipId}>
+ {userAddress}
+ </div>
+ <ReactTooltip id={addressTooltipId}>{userAddress}</ReactTooltip>
+ </TableRowColumn>
+ <TableRowColumn>
+ <div data-tip={true} data-for={balanceTooltipId}>
+ {balanceString}
+ </div>
+ <ReactTooltip id={balanceTooltipId}>{balanceString}</ReactTooltip>
+ </TableRowColumn>
+ </TableRow>
+ );
+ });
+ return rows;
+ }
+ private _onClose() {
+ this.setState({
+ didConnectFail: false,
+ });
+ const isOpen = false;
+ this.props.toggleDialogFn(isOpen);
+ }
+ private _onAddressSelected(selectedRowIndexes: number[]) {
+ const selectedRowIndex = selectedRowIndexes[0];
+ this.props.blockchain.updateLedgerDerivationIndex(selectedRowIndex);
+ const selectedAddress = this.state.userAddresses[selectedRowIndex];
+ const selectAddressBalance = this.state.addressBalances[selectedRowIndex];
+ this.props.dispatcher.updateUserAddress(selectedAddress);
+ this.props.blockchain.updateWeb3WrapperPrevUserAddress(selectedAddress);
+ this.props.dispatcher.updateUserEtherBalance(selectAddressBalance);
+ this.setState({
+ stepIndex: LedgerSteps.CONNECT,
+ });
+ const isOpen = false;
+ this.props.toggleDialogFn(isOpen);
+ }
+ private async _onFetchAddressesForDerivationPathAsync(): Promise<boolean> {
+ const currentlySetPath = this.props.blockchain.getLedgerDerivationPathIfExists();
+ let didSucceed;
+ if (currentlySetPath === this.state.derivationPath) {
+ didSucceed = true;
+ return didSucceed;
+ }
+ this.props.blockchain.updateLedgerDerivationPathIfExists(this.state.derivationPath);
+ didSucceed = await this._fetchAddressesAndBalancesAsync();
+ if (!didSucceed) {
+ this.setState({
+ derivationErrMsg: 'Failed to connect to Ledger.',
+ });
+ }
+ return didSucceed;
+ }
+ private async _fetchAddressesAndBalancesAsync() {
+ let userAddresses: string[];
+ const addressBalances: BigNumber[] = [];
+ try {
+ userAddresses = await this._getUserAddressesAsync();
+ for (const address of userAddresses) {
+ const balance = await this.props.blockchain.getBalanceInEthAsync(address);
+ addressBalances.push(balance);
+ }
+ } catch (err) {
+ utils.consoleLog(`Ledger error: ${JSON.stringify(err)}`);
+ this.setState({
+ didConnectFail: true,
+ });
+ return false;
+ }
+ this.setState({
+ userAddresses,
+ addressBalances,
+ });
+ return true;
+ }
+ private _onDerivationPathChanged(e: any, derivationPath: string) {
+ let derivationErrMsg = '';
+ if (!_.startsWith(derivationPath, VALID_ETHEREUM_DERIVATION_PATH_PREFIX)) {
+ derivationErrMsg = 'Must be valid Ethereum path.';
+ }
- this.setState({
- derivationPath,
- derivationErrMsg,
- });
- }
- private async _onConnectLedgerClickAsync() {
- const didSucceed = await this._fetchAddressesAndBalancesAsync();
- if (didSucceed) {
- this.setState({
- stepIndex: LedgerSteps.SELECT_ADDRESS,
- });
- }
- return didSucceed;
- }
- private async _getUserAddressesAsync(): Promise<string[]> {
- let userAddresses: string[];
- userAddresses = await this.props.blockchain.getUserAccountsAsync();
+ this.setState({
+ derivationPath,
+ derivationErrMsg,
+ });
+ }
+ private async _onConnectLedgerClickAsync() {
+ const didSucceed = await this._fetchAddressesAndBalancesAsync();
+ if (didSucceed) {
+ this.setState({
+ stepIndex: LedgerSteps.SELECT_ADDRESS,
+ });
+ }
+ return didSucceed;
+ }
+ private async _getUserAddressesAsync(): Promise<string[]> {
+ let userAddresses: string[];
+ userAddresses = await this.props.blockchain.getUserAccountsAsync();
- if (_.isEmpty(userAddresses)) {
- throw new Error('No addresses retrieved.');
- }
- return userAddresses;
- }
+ if (_.isEmpty(userAddresses)) {
+ throw new Error('No addresses retrieved.');
+ }
+ return userAddresses;
+ }
}
diff --git a/packages/website/ts/components/dialogs/portal_disclaimer_dialog.tsx b/packages/website/ts/components/dialogs/portal_disclaimer_dialog.tsx
index 1c5efc978..3ecc454a0 100644
--- a/packages/website/ts/components/dialogs/portal_disclaimer_dialog.tsx
+++ b/packages/website/ts/components/dialogs/portal_disclaimer_dialog.tsx
@@ -4,33 +4,33 @@ import * as React from 'react';
import { colors } from 'ts/utils/colors';
interface PortalDisclaimerDialogProps {
- isOpen: boolean;
- onToggleDialog: () => void;
+ isOpen: boolean;
+ onToggleDialog: () => void;
}
export function PortalDisclaimerDialog(props: PortalDisclaimerDialogProps) {
- return (
- <Dialog
- title="0x Portal Disclaimer"
- titleStyle={{ fontWeight: 100 }}
- actions={[<FlatButton key="portalAgree" label="I Agree" onTouchTap={props.onToggleDialog} />]}
- open={props.isOpen}
- onRequestClose={props.onToggleDialog}
- autoScrollBodyContent={true}
- modal={true}
- >
- <div className="pt2" style={{ color: colors.grey700 }}>
- <div>
- 0x Portal is a free software-based tool intended to help users to buy and sell ERC20-compatible
- blockchain tokens through the 0x protocol on a purely peer-to-peer basis. 0x portal is not a
- regulated marketplace, exchange or intermediary of any kind, and therefore, you should only use 0x
- portal to exchange tokens that are not securities, commodity interests, or any other form of
- regulated instrument. 0x has not attempted to screen or otherwise limit the tokens that you may
- enter in 0x Portal. By clicking “I Agree” below, you understand that you are solely responsible for
- using 0x Portal and buying and selling tokens using 0x Portal in compliance with all applicable laws
- and regulations.
- </div>
- </div>
- </Dialog>
- );
+ return (
+ <Dialog
+ title="0x Portal Disclaimer"
+ titleStyle={{ fontWeight: 100 }}
+ actions={[<FlatButton key="portalAgree" label="I Agree" onTouchTap={props.onToggleDialog} />]}
+ open={props.isOpen}
+ onRequestClose={props.onToggleDialog}
+ autoScrollBodyContent={true}
+ modal={true}
+ >
+ <div className="pt2" style={{ color: colors.grey700 }}>
+ <div>
+ 0x Portal is a free software-based tool intended to help users to buy and sell ERC20-compatible
+ blockchain tokens through the 0x protocol on a purely peer-to-peer basis. 0x portal is not a
+ regulated marketplace, exchange or intermediary of any kind, and therefore, you should only use 0x
+ portal to exchange tokens that are not securities, commodity interests, or any other form of
+ regulated instrument. 0x has not attempted to screen or otherwise limit the tokens that you may
+ enter in 0x Portal. By clicking “I Agree” below, you understand that you are solely responsible for
+ using 0x Portal and buying and selling tokens using 0x Portal in compliance with all applicable laws
+ and regulations.
+ </div>
+ </div>
+ </Dialog>
+ );
}
diff --git a/packages/website/ts/components/dialogs/send_dialog.tsx b/packages/website/ts/components/dialogs/send_dialog.tsx
index b9022cd9b..b3dbce598 100644
--- a/packages/website/ts/components/dialogs/send_dialog.tsx
+++ b/packages/website/ts/components/dialogs/send_dialog.tsx
@@ -8,110 +8,110 @@ import { TokenAmountInput } from 'ts/components/inputs/token_amount_input';
import { Token, TokenState } from 'ts/types';
interface SendDialogProps {
- onComplete: (recipient: string, value: BigNumber) => void;
- onCancelled: () => void;
- isOpen: boolean;
- token: Token;
- tokenState: TokenState;
+ onComplete: (recipient: string, value: BigNumber) => void;
+ onCancelled: () => void;
+ isOpen: boolean;
+ token: Token;
+ tokenState: TokenState;
}
interface SendDialogState {
- value?: BigNumber;
- recipient: string;
- shouldShowIncompleteErrs: boolean;
- isAmountValid: boolean;
+ value?: BigNumber;
+ recipient: string;
+ shouldShowIncompleteErrs: boolean;
+ isAmountValid: boolean;
}
export class SendDialog extends React.Component<SendDialogProps, SendDialogState> {
- constructor() {
- super();
- this.state = {
- recipient: '',
- shouldShowIncompleteErrs: false,
- isAmountValid: false,
- };
- }
- public render() {
- const transferDialogActions = [
- <FlatButton key="cancelTransfer" label="Cancel" onTouchTap={this._onCancel.bind(this)} />,
- <FlatButton
- key="sendTransfer"
- disabled={this._hasErrors()}
- label="Send"
- primary={true}
- onTouchTap={this._onSendClick.bind(this)}
- />,
- ];
- return (
- <Dialog
- title="I want to send"
- titleStyle={{ fontWeight: 100 }}
- actions={transferDialogActions}
- open={this.props.isOpen}
- >
- {this._renderSendDialogBody()}
- </Dialog>
- );
- }
- private _renderSendDialogBody() {
- return (
- <div className="mx-auto" style={{ maxWidth: 300 }}>
- <div style={{ height: 80 }}>
- <AddressInput
- initialAddress={this.state.recipient}
- updateAddress={this._onRecipientChange.bind(this)}
- isRequired={true}
- label={'Recipient address'}
- hintText={'Address'}
- />
- </div>
- <TokenAmountInput
- label="Amount to send"
- token={this.props.token}
- tokenState={this.props.tokenState}
- shouldShowIncompleteErrs={this.state.shouldShowIncompleteErrs}
- shouldCheckBalance={true}
- shouldCheckAllowance={false}
- onChange={this._onValueChange.bind(this)}
- amount={this.state.value}
- onVisitBalancesPageClick={this.props.onCancelled}
- />
- </div>
- );
- }
- private _onRecipientChange(recipient?: string) {
- this.setState({
- shouldShowIncompleteErrs: false,
- recipient,
- });
- }
- private _onValueChange(isValid: boolean, amount?: BigNumber) {
- this.setState({
- isAmountValid: isValid,
- value: amount,
- });
- }
- private _onSendClick() {
- if (this._hasErrors()) {
- this.setState({
- shouldShowIncompleteErrs: true,
- });
- } else {
- const value = this.state.value;
- this.setState({
- recipient: undefined,
- value: undefined,
- });
- this.props.onComplete(this.state.recipient, value);
- }
- }
- private _onCancel() {
- this.setState({
- value: undefined,
- });
- this.props.onCancelled();
- }
- private _hasErrors() {
- return _.isUndefined(this.state.recipient) || _.isUndefined(this.state.value) || !this.state.isAmountValid;
- }
+ constructor() {
+ super();
+ this.state = {
+ recipient: '',
+ shouldShowIncompleteErrs: false,
+ isAmountValid: false,
+ };
+ }
+ public render() {
+ const transferDialogActions = [
+ <FlatButton key="cancelTransfer" label="Cancel" onTouchTap={this._onCancel.bind(this)} />,
+ <FlatButton
+ key="sendTransfer"
+ disabled={this._hasErrors()}
+ label="Send"
+ primary={true}
+ onTouchTap={this._onSendClick.bind(this)}
+ />,
+ ];
+ return (
+ <Dialog
+ title="I want to send"
+ titleStyle={{ fontWeight: 100 }}
+ actions={transferDialogActions}
+ open={this.props.isOpen}
+ >
+ {this._renderSendDialogBody()}
+ </Dialog>
+ );
+ }
+ private _renderSendDialogBody() {
+ return (
+ <div className="mx-auto" style={{ maxWidth: 300 }}>
+ <div style={{ height: 80 }}>
+ <AddressInput
+ initialAddress={this.state.recipient}
+ updateAddress={this._onRecipientChange.bind(this)}
+ isRequired={true}
+ label={'Recipient address'}
+ hintText={'Address'}
+ />
+ </div>
+ <TokenAmountInput
+ label="Amount to send"
+ token={this.props.token}
+ tokenState={this.props.tokenState}
+ shouldShowIncompleteErrs={this.state.shouldShowIncompleteErrs}
+ shouldCheckBalance={true}
+ shouldCheckAllowance={false}
+ onChange={this._onValueChange.bind(this)}
+ amount={this.state.value}
+ onVisitBalancesPageClick={this.props.onCancelled}
+ />
+ </div>
+ );
+ }
+ private _onRecipientChange(recipient?: string) {
+ this.setState({
+ shouldShowIncompleteErrs: false,
+ recipient,
+ });
+ }
+ private _onValueChange(isValid: boolean, amount?: BigNumber) {
+ this.setState({
+ isAmountValid: isValid,
+ value: amount,
+ });
+ }
+ private _onSendClick() {
+ if (this._hasErrors()) {
+ this.setState({
+ shouldShowIncompleteErrs: true,
+ });
+ } else {
+ const value = this.state.value;
+ this.setState({
+ recipient: undefined,
+ value: undefined,
+ });
+ this.props.onComplete(this.state.recipient, value);
+ }
+ }
+ private _onCancel() {
+ this.setState({
+ value: undefined,
+ });
+ this.props.onCancelled();
+ }
+ private _hasErrors() {
+ return _.isUndefined(this.state.recipient) || _.isUndefined(this.state.value) || !this.state.isAmountValid;
+ }
}
diff --git a/packages/website/ts/components/dialogs/track_token_confirmation_dialog.tsx b/packages/website/ts/components/dialogs/track_token_confirmation_dialog.tsx
index b1804e95c..3f29d46f8 100644
--- a/packages/website/ts/components/dialogs/track_token_confirmation_dialog.tsx
+++ b/packages/website/ts/components/dialogs/track_token_confirmation_dialog.tsx
@@ -9,94 +9,94 @@ import { Dispatcher } from 'ts/redux/dispatcher';
import { Token, TokenByAddress } from 'ts/types';
interface TrackTokenConfirmationDialogProps {
- tokens: Token[];
- tokenByAddress: TokenByAddress;
- isOpen: boolean;
- onToggleDialog: (didConfirmTokenTracking: boolean) => void;
- dispatcher: Dispatcher;
- networkId: number;
- blockchain: Blockchain;
- userAddress: string;
+ tokens: Token[];
+ tokenByAddress: TokenByAddress;
+ isOpen: boolean;
+ onToggleDialog: (didConfirmTokenTracking: boolean) => void;
+ dispatcher: Dispatcher;
+ networkId: number;
+ blockchain: Blockchain;
+ userAddress: string;
}
interface TrackTokenConfirmationDialogState {
- isAddingTokenToTracked: boolean;
+ isAddingTokenToTracked: boolean;
}
export class TrackTokenConfirmationDialog extends React.Component<
- TrackTokenConfirmationDialogProps,
- TrackTokenConfirmationDialogState
+ TrackTokenConfirmationDialogProps,
+ TrackTokenConfirmationDialogState
> {
- constructor(props: TrackTokenConfirmationDialogProps) {
- super(props);
- this.state = {
- isAddingTokenToTracked: false,
- };
- }
- public render() {
- const tokens = this.props.tokens;
- return (
- <Dialog
- title="Tracking confirmation"
- titleStyle={{ fontWeight: 100 }}
- actions={[
- <FlatButton
- key="trackNo"
- label="No"
- onTouchTap={this._onTrackConfirmationRespondedAsync.bind(this, false)}
- />,
- <FlatButton
- key="trackYes"
- label="Yes"
- onTouchTap={this._onTrackConfirmationRespondedAsync.bind(this, true)}
- />,
- ]}
- open={this.props.isOpen}
- onRequestClose={this.props.onToggleDialog.bind(this, false)}
- autoScrollBodyContent={true}
- >
- <div className="pt2">
- <TrackTokenConfirmation
- tokens={tokens}
- networkId={this.props.networkId}
- tokenByAddress={this.props.tokenByAddress}
- isAddingTokenToTracked={this.state.isAddingTokenToTracked}
- />
- </div>
- </Dialog>
- );
- }
- private async _onTrackConfirmationRespondedAsync(didUserAcceptTracking: boolean) {
- if (!didUserAcceptTracking) {
- this.props.onToggleDialog(didUserAcceptTracking);
- return;
- }
- this.setState({
- isAddingTokenToTracked: true,
- });
- for (const token of this.props.tokens) {
- const newTokenEntry = {
- ...token,
- };
+ constructor(props: TrackTokenConfirmationDialogProps) {
+ super(props);
+ this.state = {
+ isAddingTokenToTracked: false,
+ };
+ }
+ public render() {
+ const tokens = this.props.tokens;
+ return (
+ <Dialog
+ title="Tracking confirmation"
+ titleStyle={{ fontWeight: 100 }}
+ actions={[
+ <FlatButton
+ key="trackNo"
+ label="No"
+ onTouchTap={this._onTrackConfirmationRespondedAsync.bind(this, false)}
+ />,
+ <FlatButton
+ key="trackYes"
+ label="Yes"
+ onTouchTap={this._onTrackConfirmationRespondedAsync.bind(this, true)}
+ />,
+ ]}
+ open={this.props.isOpen}
+ onRequestClose={this.props.onToggleDialog.bind(this, false)}
+ autoScrollBodyContent={true}
+ >
+ <div className="pt2">
+ <TrackTokenConfirmation
+ tokens={tokens}
+ networkId={this.props.networkId}
+ tokenByAddress={this.props.tokenByAddress}
+ isAddingTokenToTracked={this.state.isAddingTokenToTracked}
+ />
+ </div>
+ </Dialog>
+ );
+ }
+ private async _onTrackConfirmationRespondedAsync(didUserAcceptTracking: boolean) {
+ if (!didUserAcceptTracking) {
+ this.props.onToggleDialog(didUserAcceptTracking);
+ return;
+ }
+ this.setState({
+ isAddingTokenToTracked: true,
+ });
+ for (const token of this.props.tokens) {
+ const newTokenEntry = {
+ ...token,
+ };
- newTokenEntry.isTracked = true;
- trackedTokenStorage.addTrackedTokenToUser(this.props.userAddress, this.props.networkId, newTokenEntry);
- this.props.dispatcher.updateTokenByAddress([newTokenEntry]);
+ newTokenEntry.isTracked = true;
+ trackedTokenStorage.addTrackedTokenToUser(this.props.userAddress, this.props.networkId, newTokenEntry);
+ this.props.dispatcher.updateTokenByAddress([newTokenEntry]);
- const [balance, allowance] = await this.props.blockchain.getCurrentUserTokenBalanceAndAllowanceAsync(
- token.address,
- );
- this.props.dispatcher.updateTokenStateByAddress({
- [token.address]: {
- balance,
- allowance,
- },
- });
- }
+ const [balance, allowance] = await this.props.blockchain.getCurrentUserTokenBalanceAndAllowanceAsync(
+ token.address,
+ );
+ this.props.dispatcher.updateTokenStateByAddress({
+ [token.address]: {
+ balance,
+ allowance,
+ },
+ });
+ }
- this.setState({
- isAddingTokenToTracked: false,
- });
- this.props.onToggleDialog(didUserAcceptTracking);
- }
+ this.setState({
+ isAddingTokenToTracked: false,
+ });
+ this.props.onToggleDialog(didUserAcceptTracking);
+ }
}
diff --git a/packages/website/ts/components/dialogs/u2f_not_supported_dialog.tsx b/packages/website/ts/components/dialogs/u2f_not_supported_dialog.tsx
index 2ea51d07b..098e3e26d 100644
--- a/packages/website/ts/components/dialogs/u2f_not_supported_dialog.tsx
+++ b/packages/website/ts/components/dialogs/u2f_not_supported_dialog.tsx
@@ -5,42 +5,42 @@ import { colors } from 'ts/utils/colors';
import { constants } from 'ts/utils/constants';
interface U2fNotSupportedDialogProps {
- isOpen: boolean;
- onToggleDialog: () => void;
+ isOpen: boolean;
+ onToggleDialog: () => void;
}
export function U2fNotSupportedDialog(props: U2fNotSupportedDialogProps) {
- return (
- <Dialog
- title="U2F Not Supported"
- titleStyle={{ fontWeight: 100 }}
- actions={[<FlatButton key="u2fNo" label="Ok" onTouchTap={props.onToggleDialog.bind(this)} />]}
- open={props.isOpen}
- onRequestClose={props.onToggleDialog.bind(this)}
- autoScrollBodyContent={true}
- >
- <div className="pt2" style={{ color: colors.grey700 }}>
- <div>
- It looks like your browser does not support U2F connections required for us to communicate with your
- hardware wallet. Please use a browser that supports U2F connections and try again.
- </div>
- <div>
- <ul>
- <li className="pb1">Chrome version 38 or later</li>
- <li className="pb1">Opera version 40 of later</li>
- <li>
- Firefox with{' '}
- <a
- href={constants.URL_FIREFOX_U2F_ADDON}
- target="_blank"
- style={{ textDecoration: 'underline' }}
- >
- this extension
- </a>.
- </li>
- </ul>
- </div>
- </div>
- </Dialog>
- );
+ return (
+ <Dialog
+ title="U2F Not Supported"
+ titleStyle={{ fontWeight: 100 }}
+ actions={[<FlatButton key="u2fNo" label="Ok" onTouchTap={props.onToggleDialog.bind(this)} />]}
+ open={props.isOpen}
+ onRequestClose={props.onToggleDialog.bind(this)}
+ autoScrollBodyContent={true}
+ >
+ <div className="pt2" style={{ color: colors.grey700 }}>
+ <div>
+ It looks like your browser does not support U2F connections required for us to communicate with your
+ hardware wallet. Please use a browser that supports U2F connections and try again.
+ </div>
+ <div>
+ <ul>
+ <li className="pb1">Chrome version 38 or later</li>
+ <li className="pb1">Opera version 40 of later</li>
+ <li>
+ Firefox with{' '}
+ <a
+ href={constants.URL_FIREFOX_U2F_ADDON}
+ target="_blank"
+ style={{ textDecoration: 'underline' }}
+ >
+ this extension
+ </a>.
+ </li>
+ </ul>
+ </div>
+ </div>
+ </Dialog>
+ );
}
diff --git a/packages/website/ts/components/dialogs/wrapped_eth_section_notice_dialog.tsx b/packages/website/ts/components/dialogs/wrapped_eth_section_notice_dialog.tsx
index 98436eb50..9e91ff12d 100644
--- a/packages/website/ts/components/dialogs/wrapped_eth_section_notice_dialog.tsx
+++ b/packages/website/ts/components/dialogs/wrapped_eth_section_notice_dialog.tsx
@@ -4,30 +4,30 @@ import { colors } from 'material-ui/styles';
import * as React from 'react';
interface WrappedEthSectionNoticeDialogProps {
- isOpen: boolean;
- onToggleDialog: () => void;
+ isOpen: boolean;
+ onToggleDialog: () => void;
}
export function WrappedEthSectionNoticeDialog(props: WrappedEthSectionNoticeDialogProps) {
- return (
- <Dialog
- title="Dedicated Wrapped Ether Section"
- titleStyle={{ fontWeight: 100 }}
- actions={[
- <FlatButton key="acknowledgeWrapEthSection" label="Sounds good" onTouchTap={props.onToggleDialog} />,
- ]}
- open={props.isOpen}
- onRequestClose={props.onToggleDialog}
- autoScrollBodyContent={true}
- modal={true}
- >
- <div className="pt2" style={{ color: colors.grey700 }}>
- <div>
- We have recently updated the Wrapped Ether token (WETH) used by 0x Portal. Don't worry, unwrapping
- Ether tied to the old Wrapped Ether token can be done at any time by clicking on the "Wrap ETH"
- section in the menu to the left.
- </div>
- </div>
- </Dialog>
- );
+ return (
+ <Dialog
+ title="Dedicated Wrapped Ether Section"
+ titleStyle={{ fontWeight: 100 }}
+ actions={[
+ <FlatButton key="acknowledgeWrapEthSection" label="Sounds good" onTouchTap={props.onToggleDialog} />,
+ ]}
+ open={props.isOpen}
+ onRequestClose={props.onToggleDialog}
+ autoScrollBodyContent={true}
+ modal={true}
+ >
+ <div className="pt2" style={{ color: colors.grey700 }}>
+ <div>
+ We have recently updated the Wrapped Ether token (WETH) used by 0x Portal. Don't worry, unwrapping
+ Ether tied to the old Wrapped Ether token can be done at any time by clicking on the "Wrap ETH"
+ section in the menu to the left.
+ </div>
+ </div>
+ </Dialog>
+ );
}
diff --git a/packages/website/ts/components/eth_weth_conversion_button.tsx b/packages/website/ts/components/eth_weth_conversion_button.tsx
index af1b33eef..300e71f1f 100644
--- a/packages/website/ts/components/eth_weth_conversion_button.tsx
+++ b/packages/website/ts/components/eth_weth_conversion_button.tsx
@@ -12,115 +12,115 @@ import { errorReporter } from 'ts/utils/error_reporter';
import { utils } from 'ts/utils/utils';
interface EthWethConversionButtonProps {
- direction: Side;
- ethToken: Token;
- ethTokenState: TokenState;
- dispatcher: Dispatcher;
- blockchain: Blockchain;
- userEtherBalance: BigNumber;
- isOutdatedWrappedEther: boolean;
- onConversionSuccessful?: () => void;
- isDisabled?: boolean;
+ direction: Side;
+ ethToken: Token;
+ ethTokenState: TokenState;
+ dispatcher: Dispatcher;
+ blockchain: Blockchain;
+ userEtherBalance: BigNumber;
+ isOutdatedWrappedEther: boolean;
+ onConversionSuccessful?: () => void;
+ isDisabled?: boolean;
}
interface EthWethConversionButtonState {
- isEthConversionDialogVisible: boolean;
- isEthConversionHappening: boolean;
+ isEthConversionDialogVisible: boolean;
+ isEthConversionHappening: boolean;
}
export class EthWethConversionButton extends React.Component<
- EthWethConversionButtonProps,
- EthWethConversionButtonState
+ EthWethConversionButtonProps,
+ EthWethConversionButtonState
> {
- public static defaultProps: Partial<EthWethConversionButtonProps> = {
- isDisabled: false,
- onConversionSuccessful: _.noop,
- };
- public constructor(props: EthWethConversionButtonProps) {
- super(props);
- this.state = {
- isEthConversionDialogVisible: false,
- isEthConversionHappening: false,
- };
- }
- public render() {
- const labelStyle = this.state.isEthConversionHappening ? { fontSize: 10 } : {};
- let callToActionLabel;
- let inProgressLabel;
- if (this.props.direction === Side.Deposit) {
- callToActionLabel = 'Wrap';
- inProgressLabel = 'Wrapping...';
- } else {
- callToActionLabel = 'Unwrap';
- inProgressLabel = 'Unwrapping...';
- }
- return (
- <div>
- <RaisedButton
- style={{ width: '100%' }}
- labelStyle={labelStyle}
- disabled={this.props.isDisabled || this.state.isEthConversionHappening}
- label={this.state.isEthConversionHappening ? inProgressLabel : callToActionLabel}
- onClick={this._toggleConversionDialog.bind(this)}
- />
- <EthWethConversionDialog
- direction={this.props.direction}
- isOpen={this.state.isEthConversionDialogVisible}
- onComplete={this._onConversionAmountSelectedAsync.bind(this)}
- onCancelled={this._toggleConversionDialog.bind(this)}
- etherBalance={this.props.userEtherBalance}
- token={this.props.ethToken}
- tokenState={this.props.ethTokenState}
- />
- </div>
- );
- }
- private _toggleConversionDialog() {
- this.setState({
- isEthConversionDialogVisible: !this.state.isEthConversionDialogVisible,
- });
- }
- private async _onConversionAmountSelectedAsync(direction: Side, value: BigNumber) {
- this.setState({
- isEthConversionHappening: true,
- });
- this._toggleConversionDialog();
- const token = this.props.ethToken;
- const tokenState = this.props.ethTokenState;
- let balance = tokenState.balance;
- try {
- if (direction === Side.Deposit) {
- await this.props.blockchain.convertEthToWrappedEthTokensAsync(token.address, value);
- const ethAmount = ZeroEx.toUnitAmount(value, constants.DECIMAL_PLACES_ETH);
- this.props.dispatcher.showFlashMessage(`Successfully wrapped ${ethAmount.toString()} ETH to WETH`);
- balance = balance.plus(value);
- } else {
- await this.props.blockchain.convertWrappedEthTokensToEthAsync(token.address, value);
- const tokenAmount = ZeroEx.toUnitAmount(value, token.decimals);
- this.props.dispatcher.showFlashMessage(`Successfully unwrapped ${tokenAmount.toString()} WETH to ETH`);
- balance = balance.minus(value);
- }
- if (!this.props.isOutdatedWrappedEther) {
- this.props.dispatcher.replaceTokenBalanceByAddress(token.address, balance);
- }
- this.props.onConversionSuccessful();
- } catch (err) {
- const errMsg = `${err}`;
- if (_.includes(errMsg, BlockchainCallErrs.UserHasNoAssociatedAddresses)) {
- this.props.dispatcher.updateShouldBlockchainErrDialogBeOpen(true);
- } else if (!_.includes(errMsg, 'User denied transaction')) {
- utils.consoleLog(`Unexpected error encountered: ${err}`);
- utils.consoleLog(err.stack);
- const errorMsg =
- direction === Side.Deposit
- ? 'Failed to wrap your ETH. Please try again.'
- : 'Failed to unwrap your WETH. Please try again.';
- this.props.dispatcher.showFlashMessage(errorMsg);
- await errorReporter.reportAsync(err);
- }
- }
- this.setState({
- isEthConversionHappening: false,
- });
- }
+ public static defaultProps: Partial<EthWethConversionButtonProps> = {
+ isDisabled: false,
+ onConversionSuccessful: _.noop,
+ };
+ public constructor(props: EthWethConversionButtonProps) {
+ super(props);
+ this.state = {
+ isEthConversionDialogVisible: false,
+ isEthConversionHappening: false,
+ };
+ }
+ public render() {
+ const labelStyle = this.state.isEthConversionHappening ? { fontSize: 10 } : {};
+ let callToActionLabel;
+ let inProgressLabel;
+ if (this.props.direction === Side.Deposit) {
+ callToActionLabel = 'Wrap';
+ inProgressLabel = 'Wrapping...';
+ } else {
+ callToActionLabel = 'Unwrap';
+ inProgressLabel = 'Unwrapping...';
+ }
+ return (
+ <div>
+ <RaisedButton
+ style={{ width: '100%' }}
+ labelStyle={labelStyle}
+ disabled={this.props.isDisabled || this.state.isEthConversionHappening}
+ label={this.state.isEthConversionHappening ? inProgressLabel : callToActionLabel}
+ onClick={this._toggleConversionDialog.bind(this)}
+ />
+ <EthWethConversionDialog
+ direction={this.props.direction}
+ isOpen={this.state.isEthConversionDialogVisible}
+ onComplete={this._onConversionAmountSelectedAsync.bind(this)}
+ onCancelled={this._toggleConversionDialog.bind(this)}
+ etherBalance={this.props.userEtherBalance}
+ token={this.props.ethToken}
+ tokenState={this.props.ethTokenState}
+ />
+ </div>
+ );
+ }
+ private _toggleConversionDialog() {
+ this.setState({
+ isEthConversionDialogVisible: !this.state.isEthConversionDialogVisible,
+ });
+ }
+ private async _onConversionAmountSelectedAsync(direction: Side, value: BigNumber) {
+ this.setState({
+ isEthConversionHappening: true,
+ });
+ this._toggleConversionDialog();
+ const token = this.props.ethToken;
+ const tokenState = this.props.ethTokenState;
+ let balance = tokenState.balance;
+ try {
+ if (direction === Side.Deposit) {
+ await this.props.blockchain.convertEthToWrappedEthTokensAsync(token.address, value);
+ const ethAmount = ZeroEx.toUnitAmount(value, constants.DECIMAL_PLACES_ETH);
+ this.props.dispatcher.showFlashMessage(`Successfully wrapped ${ethAmount.toString()} ETH to WETH`);
+ balance = balance.plus(value);
+ } else {
+ await this.props.blockchain.convertWrappedEthTokensToEthAsync(token.address, value);
+ const tokenAmount = ZeroEx.toUnitAmount(value, token.decimals);
+ this.props.dispatcher.showFlashMessage(`Successfully unwrapped ${tokenAmount.toString()} WETH to ETH`);
+ balance = balance.minus(value);
+ }
+ if (!this.props.isOutdatedWrappedEther) {
+ this.props.dispatcher.replaceTokenBalanceByAddress(token.address, balance);
+ }
+ this.props.onConversionSuccessful();
+ } catch (err) {
+ const errMsg = `${err}`;
+ if (_.includes(errMsg, BlockchainCallErrs.UserHasNoAssociatedAddresses)) {
+ this.props.dispatcher.updateShouldBlockchainErrDialogBeOpen(true);
+ } else if (!_.includes(errMsg, 'User denied transaction')) {
+ utils.consoleLog(`Unexpected error encountered: ${err}`);
+ utils.consoleLog(err.stack);
+ const errorMsg =
+ direction === Side.Deposit
+ ? 'Failed to wrap your ETH. Please try again.'
+ : 'Failed to unwrap your WETH. Please try again.';
+ this.props.dispatcher.showFlashMessage(errorMsg);
+ await errorReporter.reportAsync(err);
+ }
+ }
+ this.setState({
+ isEthConversionHappening: false,
+ });
+ }
}
diff --git a/packages/website/ts/components/eth_wrappers.tsx b/packages/website/ts/components/eth_wrappers.tsx
index 1593d51f0..d074ec787 100644
--- a/packages/website/ts/components/eth_wrappers.tsx
+++ b/packages/website/ts/components/eth_wrappers.tsx
@@ -10,13 +10,13 @@ import { Blockchain } from 'ts/blockchain';
import { EthWethConversionButton } from 'ts/components/eth_weth_conversion_button';
import { Dispatcher } from 'ts/redux/dispatcher';
import {
- EtherscanLinkSuffixes,
- OutdatedWrappedEtherByNetworkId,
- Side,
- Token,
- TokenByAddress,
- TokenState,
- TokenStateByAddress,
+ EtherscanLinkSuffixes,
+ OutdatedWrappedEtherByNetworkId,
+ Side,
+ Token,
+ TokenByAddress,
+ TokenState,
+ TokenStateByAddress,
} from 'ts/types';
import { colors } from 'ts/utils/colors';
import { configs } from 'ts/utils/configs';
@@ -30,345 +30,345 @@ const ETHER_ICON_PATH = '/images/ether.png';
const OUTDATED_WETH_ICON_PATH = '/images/wrapped_eth_gray.png';
interface OutdatedWETHAddressToIsStateLoaded {
- [address: string]: boolean;
+ [address: string]: boolean;
}
interface OutdatedWETHStateByAddress {
- [address: string]: TokenState;
+ [address: string]: TokenState;
}
interface EthWrappersProps {
- networkId: number;
- blockchain: Blockchain;
- dispatcher: Dispatcher;
- tokenByAddress: TokenByAddress;
- tokenStateByAddress: TokenStateByAddress;
- userAddress: string;
- userEtherBalance: BigNumber;
+ networkId: number;
+ blockchain: Blockchain;
+ dispatcher: Dispatcher;
+ tokenByAddress: TokenByAddress;
+ tokenStateByAddress: TokenStateByAddress;
+ userAddress: string;
+ userEtherBalance: BigNumber;
}
interface EthWrappersState {
- outdatedWETHAddressToIsStateLoaded: OutdatedWETHAddressToIsStateLoaded;
- outdatedWETHStateByAddress: OutdatedWETHStateByAddress;
+ outdatedWETHAddressToIsStateLoaded: OutdatedWETHAddressToIsStateLoaded;
+ outdatedWETHStateByAddress: OutdatedWETHStateByAddress;
}
export class EthWrappers extends React.Component<EthWrappersProps, EthWrappersState> {
- constructor(props: EthWrappersProps) {
- super(props);
- const outdatedWETHAddresses = this._getOutdatedWETHAddresses();
- const outdatedWETHAddressToIsStateLoaded: OutdatedWETHAddressToIsStateLoaded = {};
- const outdatedWETHStateByAddress: OutdatedWETHStateByAddress = {};
- _.each(outdatedWETHAddresses, outdatedWETHAddress => {
- outdatedWETHAddressToIsStateLoaded[outdatedWETHAddress] = false;
- outdatedWETHStateByAddress[outdatedWETHAddress] = {
- balance: new BigNumber(0),
- allowance: new BigNumber(0),
- };
- });
- this.state = {
- outdatedWETHAddressToIsStateLoaded,
- outdatedWETHStateByAddress,
- };
- }
- public componentDidMount() {
- window.scrollTo(0, 0);
- // tslint:disable-next-line:no-floating-promises
- this._fetchOutdatedWETHStateAsync();
- }
- public render() {
- const tokens = _.values(this.props.tokenByAddress);
- const etherToken = _.find(tokens, { symbol: 'WETH' });
- const etherTokenState = this.props.tokenStateByAddress[etherToken.address];
- const wethBalance = ZeroEx.toUnitAmount(etherTokenState.balance, constants.DECIMAL_PLACES_ETH);
- const isBidirectional = true;
- const etherscanUrl = utils.getEtherScanLinkIfExists(
- etherToken.address,
- this.props.networkId,
- EtherscanLinkSuffixes.Address,
- );
- const tokenLabel = this._renderToken('Wrapped Ether', etherToken.address, configs.ICON_URL_BY_SYMBOL.WETH);
- return (
- <div className="clearfix lg-px4 md-px4 sm-px2" style={{ minHeight: 600 }}>
- <div className="relative">
- <h3>ETH Wrapper</h3>
- <div className="absolute" style={{ top: 0, right: 0 }}>
- <a target="_blank" href={constants.URL_WETH_IO} style={{ color: colors.grey }}>
- <div className="flex">
- <div>About Wrapped ETH</div>
- <div className="pl1">
- <i className="zmdi zmdi-open-in-new" />
- </div>
- </div>
- </a>
- </div>
- </div>
- <Divider />
- <div>
- <div className="py2">Wrap ETH into an ERC20-compliant Ether token. 1 ETH = 1 WETH.</div>
- <div>
- <Table selectable={false} style={{ backgroundColor: colors.grey50 }}>
- <TableHeader displaySelectAll={false} adjustForCheckbox={false}>
- <TableRow>
- <TableHeaderColumn>ETH Token</TableHeaderColumn>
- <TableHeaderColumn>Balance</TableHeaderColumn>
- <TableHeaderColumn className="center">
- {this._renderActionColumnTitle(isBidirectional)}
- </TableHeaderColumn>
- </TableRow>
- </TableHeader>
- <TableBody displayRowCheckbox={false}>
- <TableRow key="ETH">
- <TableRowColumn className="py1">
- <div className="flex">
- <img
- style={{
- width: ICON_DIMENSION,
- height: ICON_DIMENSION,
- }}
- src={ETHER_ICON_PATH}
- />
- <div className="ml2 sm-hide xs-hide" style={{ marginTop: 12 }}>
- ETH
- </div>
- </div>
- </TableRowColumn>
- <TableRowColumn>
- {this.props.userEtherBalance.toFixed(PRECISION)} ETH
- </TableRowColumn>
- <TableRowColumn>
- <EthWethConversionButton
- isOutdatedWrappedEther={false}
- direction={Side.Deposit}
- ethToken={etherToken}
- ethTokenState={etherTokenState}
- dispatcher={this.props.dispatcher}
- blockchain={this.props.blockchain}
- userEtherBalance={this.props.userEtherBalance}
- />
- </TableRowColumn>
- </TableRow>
- <TableRow key="WETH">
- <TableRowColumn className="py1">
- {this._renderTokenLink(tokenLabel, etherscanUrl)}
- </TableRowColumn>
- <TableRowColumn>{wethBalance.toFixed(PRECISION)} WETH</TableRowColumn>
- <TableRowColumn>
- <EthWethConversionButton
- isOutdatedWrappedEther={false}
- direction={Side.Receive}
- ethToken={etherToken}
- ethTokenState={etherTokenState}
- dispatcher={this.props.dispatcher}
- blockchain={this.props.blockchain}
- userEtherBalance={this.props.userEtherBalance}
- />
- </TableRowColumn>
- </TableRow>
- </TableBody>
- </Table>
- </div>
- </div>
- <div>
- <h4>Outdated WETH</h4>
- <Divider />
- <div className="pt2" style={{ lineHeight: 1.5 }}>
- The{' '}
- <a href="https://blog.0xproject.com/canonical-weth-a9aa7d0279dd" target="_blank">
- canonical WETH
- </a>{' '}
- contract is updated when necessary. Unwrap outdated WETH in order to
 retrieve your ETH and move
- it to the updated WETH token.
- </div>
- <div>
- <Table selectable={false} style={{ backgroundColor: colors.grey50 }}>
- <TableHeader displaySelectAll={false} adjustForCheckbox={false}>
- <TableRow>
- <TableHeaderColumn>WETH Version</TableHeaderColumn>
- <TableHeaderColumn>Balance</TableHeaderColumn>
- <TableHeaderColumn className="center">
- {this._renderActionColumnTitle(!isBidirectional)}
- </TableHeaderColumn>
- </TableRow>
- </TableHeader>
- <TableBody displayRowCheckbox={false}>
- {this._renderOutdatedWeths(etherToken, etherTokenState)}
- </TableBody>
- </Table>
- </div>
- </div>
- </div>
- );
- }
- private _renderActionColumnTitle(isBidirectional: boolean) {
- let iconClass = 'zmdi-long-arrow-right';
- let leftSymbol = 'WETH';
- let rightSymbol = 'ETH';
- if (isBidirectional) {
- iconClass = 'zmdi-swap';
- leftSymbol = 'ETH';
- rightSymbol = 'WETH';
- }
- return (
- <div className="flex mx-auto" style={{ width: 85 }}>
- <div style={{ paddingTop: 3 }}>{leftSymbol}</div>
- <div className="px1">
- <i style={{ fontSize: 18 }} className={`zmdi ${iconClass}`} />
- </div>
- <div style={{ paddingTop: 3 }}>{rightSymbol}</div>
- </div>
- );
- }
- private _renderOutdatedWeths(etherToken: Token, etherTokenState: TokenState) {
- const rows = _.map(
- configs.OUTDATED_WRAPPED_ETHERS,
- (outdatedWETHByNetworkId: OutdatedWrappedEtherByNetworkId) => {
- const outdatedWETHIfExists = outdatedWETHByNetworkId[this.props.networkId];
- if (_.isUndefined(outdatedWETHIfExists)) {
- return null; // noop
- }
- const timestampMsRange = outdatedWETHIfExists.timestampMsRange;
- let dateRange: string;
- if (!_.isUndefined(timestampMsRange)) {
- const startMoment = moment(timestampMsRange.startTimestampMs);
- const endMoment = moment(timestampMsRange.endTimestampMs);
- dateRange = `${startMoment.format(DATE_FORMAT)}-${endMoment.format(DATE_FORMAT)}`;
- } else {
- dateRange = '-';
- }
- const outdatedEtherToken = {
- ...etherToken,
- address: outdatedWETHIfExists.address,
- };
- const isStateLoaded = this.state.outdatedWETHAddressToIsStateLoaded[outdatedWETHIfExists.address];
- const outdatedEtherTokenState = this.state.outdatedWETHStateByAddress[outdatedWETHIfExists.address];
- const balanceInEthIfExists = isStateLoaded
- ? ZeroEx.toUnitAmount(outdatedEtherTokenState.balance, constants.DECIMAL_PLACES_ETH).toFixed(
- PRECISION,
- )
- : undefined;
- const onConversionSuccessful = this._onOutdatedConversionSuccessfulAsync.bind(
- this,
- outdatedWETHIfExists.address,
- );
- const etherscanUrl = utils.getEtherScanLinkIfExists(
- outdatedWETHIfExists.address,
- this.props.networkId,
- EtherscanLinkSuffixes.Address,
- );
- const tokenLabel = this._renderToken(dateRange, outdatedEtherToken.address, OUTDATED_WETH_ICON_PATH);
- return (
- <TableRow key={`weth-${outdatedWETHIfExists.address}`}>
- <TableRowColumn className="py1">
- {this._renderTokenLink(tokenLabel, etherscanUrl)}
- </TableRowColumn>
- <TableRowColumn>
- {isStateLoaded ? (
- `${balanceInEthIfExists} WETH`
- ) : (
- <i className="zmdi zmdi-spinner zmdi-hc-spin" />
- )}
- </TableRowColumn>
- <TableRowColumn>
- <EthWethConversionButton
- isDisabled={!isStateLoaded}
- isOutdatedWrappedEther={true}
- direction={Side.Receive}
- ethToken={outdatedEtherToken}
- ethTokenState={outdatedEtherTokenState}
- dispatcher={this.props.dispatcher}
- blockchain={this.props.blockchain}
- userEtherBalance={this.props.userEtherBalance}
- onConversionSuccessful={onConversionSuccessful}
- />
- </TableRowColumn>
- </TableRow>
- );
- },
- );
- return rows;
- }
- private _renderTokenLink(tokenLabel: React.ReactNode, etherscanUrl: string) {
- return (
- <span>
- {_.isUndefined(etherscanUrl) ? (
- tokenLabel
- ) : (
- <a href={etherscanUrl} target="_blank" style={{ textDecoration: 'none' }}>
- {tokenLabel}
- </a>
- )}
- </span>
- );
- }
- private _renderToken(name: string, address: string, imgPath: string) {
- const tooltipId = `tooltip-${address}`;
- return (
- <div className="flex">
- <img style={{ width: ICON_DIMENSION, height: ICON_DIMENSION }} src={imgPath} />
- <div className="ml2 sm-hide xs-hide" style={{ marginTop: 12 }}>
- <span data-tip={true} data-for={tooltipId}>
- {name}
- </span>
- <ReactTooltip id={tooltipId}>{address}</ReactTooltip>
- </div>
- </div>
- );
- }
- private async _onOutdatedConversionSuccessfulAsync(outdatedWETHAddress: string) {
- this.setState({
- outdatedWETHAddressToIsStateLoaded: {
- ...this.state.outdatedWETHAddressToIsStateLoaded,
- [outdatedWETHAddress]: false,
- },
- });
- const [balance, allowance] = await this.props.blockchain.getTokenBalanceAndAllowanceAsync(
- this.props.userAddress,
- outdatedWETHAddress,
- );
- this.setState({
- outdatedWETHAddressToIsStateLoaded: {
- ...this.state.outdatedWETHAddressToIsStateLoaded,
- [outdatedWETHAddress]: true,
- },
- outdatedWETHStateByAddress: {
- ...this.state.outdatedWETHStateByAddress,
- [outdatedWETHAddress]: {
- balance,
- allowance,
- },
- },
- });
- }
- private async _fetchOutdatedWETHStateAsync() {
- const outdatedWETHAddresses = this._getOutdatedWETHAddresses();
- const outdatedWETHAddressToIsStateLoaded: OutdatedWETHAddressToIsStateLoaded = {};
- const outdatedWETHStateByAddress: OutdatedWETHStateByAddress = {};
- for (const address of outdatedWETHAddresses) {
- const [balance, allowance] = await this.props.blockchain.getTokenBalanceAndAllowanceAsync(
- this.props.userAddress,
- address,
- );
- outdatedWETHStateByAddress[address] = {
- balance,
- allowance,
- };
- outdatedWETHAddressToIsStateLoaded[address] = true;
- }
- this.setState({
- outdatedWETHStateByAddress,
- outdatedWETHAddressToIsStateLoaded,
- });
- }
- private _getOutdatedWETHAddresses(): string[] {
- const outdatedWETHAddresses = _.compact(
- _.map(configs.OUTDATED_WRAPPED_ETHERS, outdatedWrappedEtherByNetwork => {
- const outdatedWrappedEtherIfExists = outdatedWrappedEtherByNetwork[this.props.networkId];
- if (_.isUndefined(outdatedWrappedEtherIfExists)) {
- return undefined;
- }
- const address = outdatedWrappedEtherIfExists.address;
- return address;
- }),
- );
- return outdatedWETHAddresses;
- }
+ constructor(props: EthWrappersProps) {
+ super(props);
+ const outdatedWETHAddresses = this._getOutdatedWETHAddresses();
+ const outdatedWETHAddressToIsStateLoaded: OutdatedWETHAddressToIsStateLoaded = {};
+ const outdatedWETHStateByAddress: OutdatedWETHStateByAddress = {};
+ _.each(outdatedWETHAddresses, outdatedWETHAddress => {
+ outdatedWETHAddressToIsStateLoaded[outdatedWETHAddress] = false;
+ outdatedWETHStateByAddress[outdatedWETHAddress] = {
+ balance: new BigNumber(0),
+ allowance: new BigNumber(0),
+ };
+ });
+ this.state = {
+ outdatedWETHAddressToIsStateLoaded,
+ outdatedWETHStateByAddress,
+ };
+ }
+ public componentDidMount() {
+ window.scrollTo(0, 0);
+ // tslint:disable-next-line:no-floating-promises
+ this._fetchOutdatedWETHStateAsync();
+ }
+ public render() {
+ const tokens = _.values(this.props.tokenByAddress);
+ const etherToken = _.find(tokens, { symbol: 'WETH' });
+ const etherTokenState = this.props.tokenStateByAddress[etherToken.address];
+ const wethBalance = ZeroEx.toUnitAmount(etherTokenState.balance, constants.DECIMAL_PLACES_ETH);
+ const isBidirectional = true;
+ const etherscanUrl = utils.getEtherScanLinkIfExists(
+ etherToken.address,
+ this.props.networkId,
+ EtherscanLinkSuffixes.Address,
+ );
+ const tokenLabel = this._renderToken('Wrapped Ether', etherToken.address, configs.ICON_URL_BY_SYMBOL.WETH);
+ return (
+ <div className="clearfix lg-px4 md-px4 sm-px2" style={{ minHeight: 600 }}>
+ <div className="relative">
+ <h3>ETH Wrapper</h3>
+ <div className="absolute" style={{ top: 0, right: 0 }}>
+ <a target="_blank" href={constants.URL_WETH_IO} style={{ color: colors.grey }}>
+ <div className="flex">
+ <div>About Wrapped ETH</div>
+ <div className="pl1">
+ <i className="zmdi zmdi-open-in-new" />
+ </div>
+ </div>
+ </a>
+ </div>
+ </div>
+ <Divider />
+ <div>
+ <div className="py2">Wrap ETH into an ERC20-compliant Ether token. 1 ETH = 1 WETH.</div>
+ <div>
+ <Table selectable={false} style={{ backgroundColor: colors.grey50 }}>
+ <TableHeader displaySelectAll={false} adjustForCheckbox={false}>
+ <TableRow>
+ <TableHeaderColumn>ETH Token</TableHeaderColumn>
+ <TableHeaderColumn>Balance</TableHeaderColumn>
+ <TableHeaderColumn className="center">
+ {this._renderActionColumnTitle(isBidirectional)}
+ </TableHeaderColumn>
+ </TableRow>
+ </TableHeader>
+ <TableBody displayRowCheckbox={false}>
+ <TableRow key="ETH">
+ <TableRowColumn className="py1">
+ <div className="flex">
+ <img
+ style={{
+ width: ICON_DIMENSION,
+ height: ICON_DIMENSION,
+ }}
+ src={ETHER_ICON_PATH}
+ />
+ <div className="ml2 sm-hide xs-hide" style={{ marginTop: 12 }}>
+ ETH
+ </div>
+ </div>
+ </TableRowColumn>
+ <TableRowColumn>
+ {this.props.userEtherBalance.toFixed(PRECISION)} ETH
+ </TableRowColumn>
+ <TableRowColumn>
+ <EthWethConversionButton
+ isOutdatedWrappedEther={false}
+ direction={Side.Deposit}
+ ethToken={etherToken}
+ ethTokenState={etherTokenState}
+ dispatcher={this.props.dispatcher}
+ blockchain={this.props.blockchain}
+ userEtherBalance={this.props.userEtherBalance}
+ />
+ </TableRowColumn>
+ </TableRow>
+ <TableRow key="WETH">
+ <TableRowColumn className="py1">
+ {this._renderTokenLink(tokenLabel, etherscanUrl)}
+ </TableRowColumn>
+ <TableRowColumn>{wethBalance.toFixed(PRECISION)} WETH</TableRowColumn>
+ <TableRowColumn>
+ <EthWethConversionButton
+ isOutdatedWrappedEther={false}
+ direction={Side.Receive}
+ ethToken={etherToken}
+ ethTokenState={etherTokenState}
+ dispatcher={this.props.dispatcher}
+ blockchain={this.props.blockchain}
+ userEtherBalance={this.props.userEtherBalance}
+ />
+ </TableRowColumn>
+ </TableRow>
+ </TableBody>
+ </Table>
+ </div>
+ </div>
+ <div>
+ <h4>Outdated WETH</h4>
+ <Divider />
+ <div className="pt2" style={{ lineHeight: 1.5 }}>
+ The{' '}
+ <a href="https://blog.0xproject.com/canonical-weth-a9aa7d0279dd" target="_blank">
+ canonical WETH
+ </a>{' '}
+ contract is updated when necessary. Unwrap outdated WETH in order to
 retrieve your ETH and move
+ it to the updated WETH token.
+ </div>
+ <div>
+ <Table selectable={false} style={{ backgroundColor: colors.grey50 }}>
+ <TableHeader displaySelectAll={false} adjustForCheckbox={false}>
+ <TableRow>
+ <TableHeaderColumn>WETH Version</TableHeaderColumn>
+ <TableHeaderColumn>Balance</TableHeaderColumn>
+ <TableHeaderColumn className="center">
+ {this._renderActionColumnTitle(!isBidirectional)}
+ </TableHeaderColumn>
+ </TableRow>
+ </TableHeader>
+ <TableBody displayRowCheckbox={false}>
+ {this._renderOutdatedWeths(etherToken, etherTokenState)}
+ </TableBody>
+ </Table>
+ </div>
+ </div>
+ </div>
+ );
+ }
+ private _renderActionColumnTitle(isBidirectional: boolean) {
+ let iconClass = 'zmdi-long-arrow-right';
+ let leftSymbol = 'WETH';
+ let rightSymbol = 'ETH';
+ if (isBidirectional) {
+ iconClass = 'zmdi-swap';
+ leftSymbol = 'ETH';
+ rightSymbol = 'WETH';
+ }
+ return (
+ <div className="flex mx-auto" style={{ width: 85 }}>
+ <div style={{ paddingTop: 3 }}>{leftSymbol}</div>
+ <div className="px1">
+ <i style={{ fontSize: 18 }} className={`zmdi ${iconClass}`} />
+ </div>
+ <div style={{ paddingTop: 3 }}>{rightSymbol}</div>
+ </div>
+ );
+ }
+ private _renderOutdatedWeths(etherToken: Token, etherTokenState: TokenState) {
+ const rows = _.map(
+ configs.OUTDATED_WRAPPED_ETHERS,
+ (outdatedWETHByNetworkId: OutdatedWrappedEtherByNetworkId) => {
+ const outdatedWETHIfExists = outdatedWETHByNetworkId[this.props.networkId];
+ if (_.isUndefined(outdatedWETHIfExists)) {
+ return null; // noop
+ }
+ const timestampMsRange = outdatedWETHIfExists.timestampMsRange;
+ let dateRange: string;
+ if (!_.isUndefined(timestampMsRange)) {
+ const startMoment = moment(timestampMsRange.startTimestampMs);
+ const endMoment = moment(timestampMsRange.endTimestampMs);
+ dateRange = `${startMoment.format(DATE_FORMAT)}-${endMoment.format(DATE_FORMAT)}`;
+ } else {
+ dateRange = '-';
+ }
+ const outdatedEtherToken = {
+ ...etherToken,
+ address: outdatedWETHIfExists.address,
+ };
+ const isStateLoaded = this.state.outdatedWETHAddressToIsStateLoaded[outdatedWETHIfExists.address];
+ const outdatedEtherTokenState = this.state.outdatedWETHStateByAddress[outdatedWETHIfExists.address];
+ const balanceInEthIfExists = isStateLoaded
+ ? ZeroEx.toUnitAmount(outdatedEtherTokenState.balance, constants.DECIMAL_PLACES_ETH).toFixed(
+ PRECISION,
+ )
+ : undefined;
+ const onConversionSuccessful = this._onOutdatedConversionSuccessfulAsync.bind(
+ this,
+ outdatedWETHIfExists.address,
+ );
+ const etherscanUrl = utils.getEtherScanLinkIfExists(
+ outdatedWETHIfExists.address,
+ this.props.networkId,
+ EtherscanLinkSuffixes.Address,
+ );
+ const tokenLabel = this._renderToken(dateRange, outdatedEtherToken.address, OUTDATED_WETH_ICON_PATH);
+ return (
+ <TableRow key={`weth-${outdatedWETHIfExists.address}`}>
+ <TableRowColumn className="py1">
+ {this._renderTokenLink(tokenLabel, etherscanUrl)}
+ </TableRowColumn>
+ <TableRowColumn>
+ {isStateLoaded ? (
+ `${balanceInEthIfExists} WETH`
+ ) : (
+ <i className="zmdi zmdi-spinner zmdi-hc-spin" />
+ )}
+ </TableRowColumn>
+ <TableRowColumn>
+ <EthWethConversionButton
+ isDisabled={!isStateLoaded}
+ isOutdatedWrappedEther={true}
+ direction={Side.Receive}
+ ethToken={outdatedEtherToken}
+ ethTokenState={outdatedEtherTokenState}
+ dispatcher={this.props.dispatcher}
+ blockchain={this.props.blockchain}
+ userEtherBalance={this.props.userEtherBalance}
+ onConversionSuccessful={onConversionSuccessful}
+ />
+ </TableRowColumn>
+ </TableRow>
+ );
+ },
+ );
+ return rows;
+ }
+ private _renderTokenLink(tokenLabel: React.ReactNode, etherscanUrl: string) {
+ return (
+ <span>
+ {_.isUndefined(etherscanUrl) ? (
+ tokenLabel
+ ) : (
+ <a href={etherscanUrl} target="_blank" style={{ textDecoration: 'none' }}>
+ {tokenLabel}
+ </a>
+ )}
+ </span>
+ );
+ }
+ private _renderToken(name: string, address: string, imgPath: string) {
+ const tooltipId = `tooltip-${address}`;
+ return (
+ <div className="flex">
+ <img style={{ width: ICON_DIMENSION, height: ICON_DIMENSION }} src={imgPath} />
+ <div className="ml2 sm-hide xs-hide" style={{ marginTop: 12 }}>
+ <span data-tip={true} data-for={tooltipId}>
+ {name}
+ </span>
+ <ReactTooltip id={tooltipId}>{address}</ReactTooltip>
+ </div>
+ </div>
+ );
+ }
+ private async _onOutdatedConversionSuccessfulAsync(outdatedWETHAddress: string) {
+ this.setState({
+ outdatedWETHAddressToIsStateLoaded: {
+ ...this.state.outdatedWETHAddressToIsStateLoaded,
+ [outdatedWETHAddress]: false,
+ },
+ });
+ const [balance, allowance] = await this.props.blockchain.getTokenBalanceAndAllowanceAsync(
+ this.props.userAddress,
+ outdatedWETHAddress,
+ );
+ this.setState({
+ outdatedWETHAddressToIsStateLoaded: {
+ ...this.state.outdatedWETHAddressToIsStateLoaded,
+ [outdatedWETHAddress]: true,
+ },
+ outdatedWETHStateByAddress: {
+ ...this.state.outdatedWETHStateByAddress,
+ [outdatedWETHAddress]: {
+ balance,
+ allowance,
+ },
+ },
+ });
+ }
+ private async _fetchOutdatedWETHStateAsync() {
+ const outdatedWETHAddresses = this._getOutdatedWETHAddresses();
+ const outdatedWETHAddressToIsStateLoaded: OutdatedWETHAddressToIsStateLoaded = {};
+ const outdatedWETHStateByAddress: OutdatedWETHStateByAddress = {};
+ for (const address of outdatedWETHAddresses) {
+ const [balance, allowance] = await this.props.blockchain.getTokenBalanceAndAllowanceAsync(
+ this.props.userAddress,
+ address,
+ );
+ outdatedWETHStateByAddress[address] = {
+ balance,
+ allowance,
+ };
+ outdatedWETHAddressToIsStateLoaded[address] = true;
+ }
+ this.setState({
+ outdatedWETHStateByAddress,
+ outdatedWETHAddressToIsStateLoaded,
+ });
+ }
+ private _getOutdatedWETHAddresses(): string[] {
+ const outdatedWETHAddresses = _.compact(
+ _.map(configs.OUTDATED_WRAPPED_ETHERS, outdatedWrappedEtherByNetwork => {
+ const outdatedWrappedEtherIfExists = outdatedWrappedEtherByNetwork[this.props.networkId];
+ if (_.isUndefined(outdatedWrappedEtherIfExists)) {
+ return undefined;
+ }
+ const address = outdatedWrappedEtherIfExists.address;
+ return address;
+ }),
+ );
+ return outdatedWETHAddresses;
+ }
} // tslint:disable:max-file-line-count
diff --git a/packages/website/ts/components/fill_order.tsx b/packages/website/ts/components/fill_order.tsx
index 249ee419e..1a150e9ee 100644
--- a/packages/website/ts/components/fill_order.tsx
+++ b/packages/website/ts/components/fill_order.tsx
@@ -26,665 +26,665 @@ import { errorReporter } from 'ts/utils/error_reporter';
import { utils } from 'ts/utils/utils';
interface FillOrderProps {
- blockchain: Blockchain;
- blockchainErr: BlockchainErrs;
- orderFillAmount: BigNumber;
- isOrderInUrl: boolean;
- networkId: number;
- userAddress: string;
- tokenByAddress: TokenByAddress;
- tokenStateByAddress: TokenStateByAddress;
- initialOrder: Order;
- dispatcher: Dispatcher;
+ blockchain: Blockchain;
+ blockchainErr: BlockchainErrs;
+ orderFillAmount: BigNumber;
+ isOrderInUrl: boolean;
+ networkId: number;
+ userAddress: string;
+ tokenByAddress: TokenByAddress;
+ tokenStateByAddress: TokenStateByAddress;
+ initialOrder: Order;
+ dispatcher: Dispatcher;
}
interface FillOrderState {
- didOrderValidationRun: boolean;
- areAllInvolvedTokensTracked: boolean;
- globalErrMsg: string;
- orderJSON: string;
- orderJSONErrMsg: string;
- parsedOrder: Order;
- didFillOrderSucceed: boolean;
- didCancelOrderSucceed: boolean;
- unavailableTakerAmount: BigNumber;
- isMakerTokenAddressInRegistry: boolean;
- isTakerTokenAddressInRegistry: boolean;
- isFillWarningDialogOpen: boolean;
- isFilling: boolean;
- isCancelling: boolean;
- isConfirmingTokenTracking: boolean;
- tokensToTrack: Token[];
+ didOrderValidationRun: boolean;
+ areAllInvolvedTokensTracked: boolean;
+ globalErrMsg: string;
+ orderJSON: string;
+ orderJSONErrMsg: string;
+ parsedOrder: Order;
+ didFillOrderSucceed: boolean;
+ didCancelOrderSucceed: boolean;
+ unavailableTakerAmount: BigNumber;
+ isMakerTokenAddressInRegistry: boolean;
+ isTakerTokenAddressInRegistry: boolean;
+ isFillWarningDialogOpen: boolean;
+ isFilling: boolean;
+ isCancelling: boolean;
+ isConfirmingTokenTracking: boolean;
+ tokensToTrack: Token[];
}
export class FillOrder extends React.Component<FillOrderProps, FillOrderState> {
- private _validator: SchemaValidator;
- constructor(props: FillOrderProps) {
- super(props);
- this.state = {
- globalErrMsg: '',
- didOrderValidationRun: false,
- areAllInvolvedTokensTracked: false,
- didFillOrderSucceed: false,
- didCancelOrderSucceed: false,
- orderJSON: _.isUndefined(this.props.initialOrder) ? '' : JSON.stringify(this.props.initialOrder),
- orderJSONErrMsg: '',
- parsedOrder: this.props.initialOrder,
- unavailableTakerAmount: new BigNumber(0),
- isMakerTokenAddressInRegistry: false,
- isTakerTokenAddressInRegistry: false,
- isFillWarningDialogOpen: false,
- isFilling: false,
- isCancelling: false,
- isConfirmingTokenTracking: false,
- tokensToTrack: [],
- };
- this._validator = new SchemaValidator();
- }
- public componentWillMount() {
- if (!_.isEmpty(this.state.orderJSON)) {
- // tslint:disable-next-line:no-floating-promises
- this._validateFillOrderFireAndForgetAsync(this.state.orderJSON);
- }
- }
- public componentDidMount() {
- window.scrollTo(0, 0);
- }
- public render() {
- return (
- <div className="clearfix lg-px4 md-px4 sm-px2" style={{ minHeight: 600 }}>
- <h3>Fill an order</h3>
- <Divider />
- <div>
- {!this.props.isOrderInUrl && (
- <div>
- <div className="pt2 pb2">Paste an order JSON snippet below to begin</div>
- <div className="pb2">Order JSON</div>
- <FillOrderJSON
- blockchain={this.props.blockchain}
- tokenByAddress={this.props.tokenByAddress}
- networkId={this.props.networkId}
- orderJSON={this.state.orderJSON}
- onFillOrderJSONChanged={this._onFillOrderJSONChanged.bind(this)}
- />
- {this._renderOrderJsonNotices()}
- </div>
- )}
- <div>
- {!_.isUndefined(this.state.parsedOrder) &&
- this.state.didOrderValidationRun &&
- this.state.areAllInvolvedTokensTracked &&
- this._renderVisualOrder()}
- </div>
- {this.props.isOrderInUrl && (
- <div className="pt2">
- <Card
- style={{
- boxShadow: 'none',
- backgroundColor: 'none',
- border: '1px solid #eceaea',
- }}
- >
- <CardHeader title="Order JSON" actAsExpander={true} showExpandableButton={true} />
- <CardText expandable={true}>
- <FillOrderJSON
- blockchain={this.props.blockchain}
- tokenByAddress={this.props.tokenByAddress}
- networkId={this.props.networkId}
- orderJSON={this.state.orderJSON}
- onFillOrderJSONChanged={this._onFillOrderJSONChanged.bind(this)}
- />
- </CardText>
- </Card>
- {this._renderOrderJsonNotices()}
- </div>
- )}
- </div>
- <FillWarningDialog
- isOpen={this.state.isFillWarningDialogOpen}
- onToggleDialog={this._onFillWarningClosed.bind(this)}
- />
- <TrackTokenConfirmationDialog
- userAddress={this.props.userAddress}
- networkId={this.props.networkId}
- blockchain={this.props.blockchain}
- tokenByAddress={this.props.tokenByAddress}
- dispatcher={this.props.dispatcher}
- tokens={this.state.tokensToTrack}
- isOpen={this.state.isConfirmingTokenTracking}
- onToggleDialog={this._onToggleTrackConfirmDialog.bind(this)}
- />
- </div>
- );
- }
- private _renderOrderJsonNotices() {
- return (
- <div>
- {!_.isUndefined(this.props.initialOrder) &&
- !this.state.didOrderValidationRun && (
- <div className="pt2">
- <span className="pr1">
- <i className="zmdi zmdi-spinner zmdi-hc-spin" />
- </span>
- <span>Validating order...</span>
- </div>
- )}
- {!_.isEmpty(this.state.orderJSONErrMsg) && (
- <Alert type={AlertTypes.ERROR} message={this.state.orderJSONErrMsg} />
- )}
- </div>
- );
- }
- private _renderVisualOrder() {
- const takerTokenAddress = this.state.parsedOrder.taker.token.address;
- const takerToken = this.props.tokenByAddress[takerTokenAddress];
- const orderTakerAmount = new BigNumber(this.state.parsedOrder.taker.amount);
- const orderMakerAmount = new BigNumber(this.state.parsedOrder.maker.amount);
- const takerAssetToken = {
- amount: orderTakerAmount.minus(this.state.unavailableTakerAmount),
- symbol: takerToken.symbol,
- };
- const fillToken = this.props.tokenByAddress[takerToken.address];
- const fillTokenState = this.props.tokenStateByAddress[takerToken.address];
- const makerTokenAddress = this.state.parsedOrder.maker.token.address;
- const makerToken = this.props.tokenByAddress[makerTokenAddress];
- const makerAssetToken = {
- amount: orderMakerAmount.times(takerAssetToken.amount).div(orderTakerAmount),
- symbol: makerToken.symbol,
- };
- const fillAssetToken = {
- amount: this.props.orderFillAmount,
- symbol: takerToken.symbol,
- };
- const orderTaker = !_.isEmpty(this.state.parsedOrder.taker.address)
- ? this.state.parsedOrder.taker.address
- : this.props.userAddress;
- const parsedOrderExpiration = new BigNumber(this.state.parsedOrder.expiration);
- const exchangeRate = orderMakerAmount.div(orderTakerAmount);
+ private _validator: SchemaValidator;
+ constructor(props: FillOrderProps) {
+ super(props);
+ this.state = {
+ globalErrMsg: '',
+ didOrderValidationRun: false,
+ areAllInvolvedTokensTracked: false,
+ didFillOrderSucceed: false,
+ didCancelOrderSucceed: false,
+ orderJSON: _.isUndefined(this.props.initialOrder) ? '' : JSON.stringify(this.props.initialOrder),
+ orderJSONErrMsg: '',
+ parsedOrder: this.props.initialOrder,
+ unavailableTakerAmount: new BigNumber(0),
+ isMakerTokenAddressInRegistry: false,
+ isTakerTokenAddressInRegistry: false,
+ isFillWarningDialogOpen: false,
+ isFilling: false,
+ isCancelling: false,
+ isConfirmingTokenTracking: false,
+ tokensToTrack: [],
+ };
+ this._validator = new SchemaValidator();
+ }
+ public componentWillMount() {
+ if (!_.isEmpty(this.state.orderJSON)) {
+ // tslint:disable-next-line:no-floating-promises
+ this._validateFillOrderFireAndForgetAsync(this.state.orderJSON);
+ }
+ }
+ public componentDidMount() {
+ window.scrollTo(0, 0);
+ }
+ public render() {
+ return (
+ <div className="clearfix lg-px4 md-px4 sm-px2" style={{ minHeight: 600 }}>
+ <h3>Fill an order</h3>
+ <Divider />
+ <div>
+ {!this.props.isOrderInUrl && (
+ <div>
+ <div className="pt2 pb2">Paste an order JSON snippet below to begin</div>
+ <div className="pb2">Order JSON</div>
+ <FillOrderJSON
+ blockchain={this.props.blockchain}
+ tokenByAddress={this.props.tokenByAddress}
+ networkId={this.props.networkId}
+ orderJSON={this.state.orderJSON}
+ onFillOrderJSONChanged={this._onFillOrderJSONChanged.bind(this)}
+ />
+ {this._renderOrderJsonNotices()}
+ </div>
+ )}
+ <div>
+ {!_.isUndefined(this.state.parsedOrder) &&
+ this.state.didOrderValidationRun &&
+ this.state.areAllInvolvedTokensTracked &&
+ this._renderVisualOrder()}
+ </div>
+ {this.props.isOrderInUrl && (
+ <div className="pt2">
+ <Card
+ style={{
+ boxShadow: 'none',
+ backgroundColor: 'none',
+ border: '1px solid #eceaea',
+ }}
+ >
+ <CardHeader title="Order JSON" actAsExpander={true} showExpandableButton={true} />
+ <CardText expandable={true}>
+ <FillOrderJSON
+ blockchain={this.props.blockchain}
+ tokenByAddress={this.props.tokenByAddress}
+ networkId={this.props.networkId}
+ orderJSON={this.state.orderJSON}
+ onFillOrderJSONChanged={this._onFillOrderJSONChanged.bind(this)}
+ />
+ </CardText>
+ </Card>
+ {this._renderOrderJsonNotices()}
+ </div>
+ )}
+ </div>
+ <FillWarningDialog
+ isOpen={this.state.isFillWarningDialogOpen}
+ onToggleDialog={this._onFillWarningClosed.bind(this)}
+ />
+ <TrackTokenConfirmationDialog
+ userAddress={this.props.userAddress}
+ networkId={this.props.networkId}
+ blockchain={this.props.blockchain}
+ tokenByAddress={this.props.tokenByAddress}
+ dispatcher={this.props.dispatcher}
+ tokens={this.state.tokensToTrack}
+ isOpen={this.state.isConfirmingTokenTracking}
+ onToggleDialog={this._onToggleTrackConfirmDialog.bind(this)}
+ />
+ </div>
+ );
+ }
+ private _renderOrderJsonNotices() {
+ return (
+ <div>
+ {!_.isUndefined(this.props.initialOrder) &&
+ !this.state.didOrderValidationRun && (
+ <div className="pt2">
+ <span className="pr1">
+ <i className="zmdi zmdi-spinner zmdi-hc-spin" />
+ </span>
+ <span>Validating order...</span>
+ </div>
+ )}
+ {!_.isEmpty(this.state.orderJSONErrMsg) && (
+ <Alert type={AlertTypes.ERROR} message={this.state.orderJSONErrMsg} />
+ )}
+ </div>
+ );
+ }
+ private _renderVisualOrder() {
+ const takerTokenAddress = this.state.parsedOrder.taker.token.address;
+ const takerToken = this.props.tokenByAddress[takerTokenAddress];
+ const orderTakerAmount = new BigNumber(this.state.parsedOrder.taker.amount);
+ const orderMakerAmount = new BigNumber(this.state.parsedOrder.maker.amount);
+ const takerAssetToken = {
+ amount: orderTakerAmount.minus(this.state.unavailableTakerAmount),
+ symbol: takerToken.symbol,
+ };
+ const fillToken = this.props.tokenByAddress[takerToken.address];
+ const fillTokenState = this.props.tokenStateByAddress[takerToken.address];
+ const makerTokenAddress = this.state.parsedOrder.maker.token.address;
+ const makerToken = this.props.tokenByAddress[makerTokenAddress];
+ const makerAssetToken = {
+ amount: orderMakerAmount.times(takerAssetToken.amount).div(orderTakerAmount),
+ symbol: makerToken.symbol,
+ };
+ const fillAssetToken = {
+ amount: this.props.orderFillAmount,
+ symbol: takerToken.symbol,
+ };
+ const orderTaker = !_.isEmpty(this.state.parsedOrder.taker.address)
+ ? this.state.parsedOrder.taker.address
+ : this.props.userAddress;
+ const parsedOrderExpiration = new BigNumber(this.state.parsedOrder.expiration);
+ const exchangeRate = orderMakerAmount.div(orderTakerAmount);
- let orderReceiveAmount = 0;
- if (!_.isUndefined(this.props.orderFillAmount)) {
- const orderReceiveAmountBigNumber = exchangeRate.mul(this.props.orderFillAmount);
- orderReceiveAmount = this._formatCurrencyAmount(orderReceiveAmountBigNumber, makerToken.decimals);
- }
- const isUserMaker =
- !_.isUndefined(this.state.parsedOrder) && this.state.parsedOrder.maker.address === this.props.userAddress;
- const expiryDate = utils.convertToReadableDateTimeFromUnixTimestamp(parsedOrderExpiration);
- return (
- <div className="pt3 pb1">
- <div className="clearfix pb2" style={{ width: '100%' }}>
- <div className="inline left">Order details</div>
- <div className="inline right" style={{ minWidth: 208 }}>
- <div className="col col-4 pl2" style={{ color: colors.grey }}>
- Maker:
- </div>
- <div className="col col-2 pr1">
- <Identicon address={this.state.parsedOrder.maker.address} diameter={23} />
- </div>
- <div className="col col-6">
- <EthereumAddress
- address={this.state.parsedOrder.maker.address}
- networkId={this.props.networkId}
- />
- </div>
- </div>
- </div>
- <div className="lg-px4 md-px4 sm-px0">
- <div className="lg-px4 md-px4 sm-px1 pt1">
- <VisualOrder
- orderTakerAddress={orderTaker}
- orderMakerAddress={this.state.parsedOrder.maker.address}
- makerAssetToken={makerAssetToken}
- takerAssetToken={takerAssetToken}
- tokenByAddress={this.props.tokenByAddress}
- makerToken={makerToken}
- takerToken={takerToken}
- networkId={this.props.networkId}
- isMakerTokenAddressInRegistry={this.state.isMakerTokenAddressInRegistry}
- isTakerTokenAddressInRegistry={this.state.isTakerTokenAddressInRegistry}
- />
- <div className="center pt3 pb2">Expires: {expiryDate} UTC</div>
- </div>
- </div>
- {!isUserMaker && (
- <div className="clearfix mx-auto relative" style={{ width: 235, height: 108 }}>
- <TokenAmountInput
- label="Fill amount"
- onChange={this._onFillAmountChange.bind(this)}
- shouldShowIncompleteErrs={false}
- token={fillToken}
- tokenState={fillTokenState}
- amount={fillAssetToken.amount}
- shouldCheckBalance={true}
- shouldCheckAllowance={true}
- />
- <div
- className="absolute sm-hide xs-hide"
- style={{
- color: colors.grey400,
- right: -247,
- top: 39,
- width: 242,
- }}
- >
- = {accounting.formatNumber(orderReceiveAmount, 6)} {makerToken.symbol}
- </div>
- </div>
- )}
- <div>
- {isUserMaker ? (
- <div>
- <RaisedButton
- style={{ width: '100%' }}
- disabled={this.state.isCancelling}
- label={this.state.isCancelling ? 'Cancelling order...' : 'Cancel order'}
- onClick={this._onCancelOrderClickFireAndForgetAsync.bind(this)}
- />
- {this.state.didCancelOrderSucceed && (
- <Alert type={AlertTypes.SUCCESS} message={this._renderCancelSuccessMsg()} />
- )}
- </div>
- ) : (
- <div>
- <RaisedButton
- style={{ width: '100%' }}
- disabled={this.state.isFilling}
- label={this.state.isFilling ? 'Filling order...' : 'Fill order'}
- onClick={this._onFillOrderClick.bind(this)}
- />
- {!_.isEmpty(this.state.globalErrMsg) && (
- <Alert type={AlertTypes.ERROR} message={this.state.globalErrMsg} />
- )}
- {this.state.didFillOrderSucceed && (
- <Alert type={AlertTypes.SUCCESS} message={this._renderFillSuccessMsg()} />
- )}
- </div>
- )}
- </div>
- </div>
- );
- }
- private _renderFillSuccessMsg() {
- return (
- <div>
- Order successfully filled. See the trade details in your{' '}
- <Link to={`${WebsitePaths.Portal}/trades`} style={{ color: colors.white }}>
- trade history
- </Link>
- </div>
- );
- }
- private _renderCancelSuccessMsg() {
- return <div>Order successfully cancelled.</div>;
- }
- private _onFillOrderClick() {
- if (!this.state.isMakerTokenAddressInRegistry || !this.state.isTakerTokenAddressInRegistry) {
- this.setState({
- isFillWarningDialogOpen: true,
- });
- } else {
- // tslint:disable-next-line:no-floating-promises
- this._onFillOrderClickFireAndForgetAsync();
- }
- }
- private _onFillWarningClosed(didUserCancel: boolean) {
- this.setState({
- isFillWarningDialogOpen: false,
- });
- if (!didUserCancel) {
- // tslint:disable-next-line:no-floating-promises
- this._onFillOrderClickFireAndForgetAsync();
- }
- }
- private _onFillAmountChange(isValid: boolean, amount?: BigNumber) {
- this.props.dispatcher.updateOrderFillAmount(amount);
- }
- private _onFillOrderJSONChanged(event: any) {
- const orderJSON = event.target.value;
- this.setState({
- didOrderValidationRun: _.isEmpty(orderJSON) && _.isEmpty(this.state.orderJSONErrMsg),
- didFillOrderSucceed: false,
- });
- // tslint:disable-next-line:no-floating-promises
- this._validateFillOrderFireAndForgetAsync(orderJSON);
- }
- private async _checkForUntrackedTokensAndAskToAdd() {
- if (!_.isEmpty(this.state.orderJSONErrMsg)) {
- return;
- }
+ let orderReceiveAmount = 0;
+ if (!_.isUndefined(this.props.orderFillAmount)) {
+ const orderReceiveAmountBigNumber = exchangeRate.mul(this.props.orderFillAmount);
+ orderReceiveAmount = this._formatCurrencyAmount(orderReceiveAmountBigNumber, makerToken.decimals);
+ }
+ const isUserMaker =
+ !_.isUndefined(this.state.parsedOrder) && this.state.parsedOrder.maker.address === this.props.userAddress;
+ const expiryDate = utils.convertToReadableDateTimeFromUnixTimestamp(parsedOrderExpiration);
+ return (
+ <div className="pt3 pb1">
+ <div className="clearfix pb2" style={{ width: '100%' }}>
+ <div className="inline left">Order details</div>
+ <div className="inline right" style={{ minWidth: 208 }}>
+ <div className="col col-4 pl2" style={{ color: colors.grey }}>
+ Maker:
+ </div>
+ <div className="col col-2 pr1">
+ <Identicon address={this.state.parsedOrder.maker.address} diameter={23} />
+ </div>
+ <div className="col col-6">
+ <EthereumAddress
+ address={this.state.parsedOrder.maker.address}
+ networkId={this.props.networkId}
+ />
+ </div>
+ </div>
+ </div>
+ <div className="lg-px4 md-px4 sm-px0">
+ <div className="lg-px4 md-px4 sm-px1 pt1">
+ <VisualOrder
+ orderTakerAddress={orderTaker}
+ orderMakerAddress={this.state.parsedOrder.maker.address}
+ makerAssetToken={makerAssetToken}
+ takerAssetToken={takerAssetToken}
+ tokenByAddress={this.props.tokenByAddress}
+ makerToken={makerToken}
+ takerToken={takerToken}
+ networkId={this.props.networkId}
+ isMakerTokenAddressInRegistry={this.state.isMakerTokenAddressInRegistry}
+ isTakerTokenAddressInRegistry={this.state.isTakerTokenAddressInRegistry}
+ />
+ <div className="center pt3 pb2">Expires: {expiryDate} UTC</div>
+ </div>
+ </div>
+ {!isUserMaker && (
+ <div className="clearfix mx-auto relative" style={{ width: 235, height: 108 }}>
+ <TokenAmountInput
+ label="Fill amount"
+ onChange={this._onFillAmountChange.bind(this)}
+ shouldShowIncompleteErrs={false}
+ token={fillToken}
+ tokenState={fillTokenState}
+ amount={fillAssetToken.amount}
+ shouldCheckBalance={true}
+ shouldCheckAllowance={true}
+ />
+ <div
+ className="absolute sm-hide xs-hide"
+ style={{
+ color: colors.grey400,
+ right: -247,
+ top: 39,
+ width: 242,
+ }}
+ >
+ = {accounting.formatNumber(orderReceiveAmount, 6)} {makerToken.symbol}
+ </div>
+ </div>
+ )}
+ <div>
+ {isUserMaker ? (
+ <div>
+ <RaisedButton
+ style={{ width: '100%' }}
+ disabled={this.state.isCancelling}
+ label={this.state.isCancelling ? 'Cancelling order...' : 'Cancel order'}
+ onClick={this._onCancelOrderClickFireAndForgetAsync.bind(this)}
+ />
+ {this.state.didCancelOrderSucceed && (
+ <Alert type={AlertTypes.SUCCESS} message={this._renderCancelSuccessMsg()} />
+ )}
+ </div>
+ ) : (
+ <div>
+ <RaisedButton
+ style={{ width: '100%' }}
+ disabled={this.state.isFilling}
+ label={this.state.isFilling ? 'Filling order...' : 'Fill order'}
+ onClick={this._onFillOrderClick.bind(this)}
+ />
+ {!_.isEmpty(this.state.globalErrMsg) && (
+ <Alert type={AlertTypes.ERROR} message={this.state.globalErrMsg} />
+ )}
+ {this.state.didFillOrderSucceed && (
+ <Alert type={AlertTypes.SUCCESS} message={this._renderFillSuccessMsg()} />
+ )}
+ </div>
+ )}
+ </div>
+ </div>
+ );
+ }
+ private _renderFillSuccessMsg() {
+ return (
+ <div>
+ Order successfully filled. See the trade details in your{' '}
+ <Link to={`${WebsitePaths.Portal}/trades`} style={{ color: colors.white }}>
+ trade history
+ </Link>
+ </div>
+ );
+ }
+ private _renderCancelSuccessMsg() {
+ return <div>Order successfully cancelled.</div>;
+ }
+ private _onFillOrderClick() {
+ if (!this.state.isMakerTokenAddressInRegistry || !this.state.isTakerTokenAddressInRegistry) {
+ this.setState({
+ isFillWarningDialogOpen: true,
+ });
+ } else {
+ // tslint:disable-next-line:no-floating-promises
+ this._onFillOrderClickFireAndForgetAsync();
+ }
+ }
+ private _onFillWarningClosed(didUserCancel: boolean) {
+ this.setState({
+ isFillWarningDialogOpen: false,
+ });
+ if (!didUserCancel) {
+ // tslint:disable-next-line:no-floating-promises
+ this._onFillOrderClickFireAndForgetAsync();
+ }
+ }
+ private _onFillAmountChange(isValid: boolean, amount?: BigNumber) {
+ this.props.dispatcher.updateOrderFillAmount(amount);
+ }
+ private _onFillOrderJSONChanged(event: any) {
+ const orderJSON = event.target.value;
+ this.setState({
+ didOrderValidationRun: _.isEmpty(orderJSON) && _.isEmpty(this.state.orderJSONErrMsg),
+ didFillOrderSucceed: false,
+ });
+ // tslint:disable-next-line:no-floating-promises
+ this._validateFillOrderFireAndForgetAsync(orderJSON);
+ }
+ private async _checkForUntrackedTokensAndAskToAdd() {
+ if (!_.isEmpty(this.state.orderJSONErrMsg)) {
+ return;
+ }
- const makerTokenIfExists = this.props.tokenByAddress[this.state.parsedOrder.maker.token.address];
- const takerTokenIfExists = this.props.tokenByAddress[this.state.parsedOrder.taker.token.address];
+ const makerTokenIfExists = this.props.tokenByAddress[this.state.parsedOrder.maker.token.address];
+ const takerTokenIfExists = this.props.tokenByAddress[this.state.parsedOrder.taker.token.address];
- const tokensToTrack = [];
- const isUnseenMakerToken = _.isUndefined(makerTokenIfExists);
- const isMakerTokenTracked = !_.isUndefined(makerTokenIfExists) && makerTokenIfExists.isTracked;
- if (isUnseenMakerToken) {
- tokensToTrack.push({
- ...this.state.parsedOrder.maker.token,
- iconUrl: undefined,
- isTracked: false,
- isRegistered: false,
- });
- } else if (!isMakerTokenTracked) {
- tokensToTrack.push(makerTokenIfExists);
- }
- const isUnseenTakerToken = _.isUndefined(takerTokenIfExists);
- const isTakerTokenTracked = !_.isUndefined(takerTokenIfExists) && takerTokenIfExists.isTracked;
- if (isUnseenTakerToken) {
- tokensToTrack.push({
- ...this.state.parsedOrder.taker.token,
- iconUrl: undefined,
- isTracked: false,
- isRegistered: false,
- });
- } else if (!isTakerTokenTracked) {
- tokensToTrack.push(takerTokenIfExists);
- }
- if (!_.isEmpty(tokensToTrack)) {
- this.setState({
- isConfirmingTokenTracking: true,
- tokensToTrack,
- });
- } else {
- this.setState({
- areAllInvolvedTokensTracked: true,
- });
- }
- }
- private async _validateFillOrderFireAndForgetAsync(orderJSON: string) {
- let orderJSONErrMsg = '';
- let parsedOrder: Order;
- try {
- const order = JSON.parse(orderJSON);
- const validationResult = this._validator.validate(order, orderSchema);
- if (validationResult.errors.length > 0) {
- orderJSONErrMsg = 'Submitted order JSON is not a valid order';
- utils.consoleLog(`Unexpected order JSON validation error: ${validationResult.errors.join(', ')}`);
- return;
- }
- parsedOrder = order;
+ const tokensToTrack = [];
+ const isUnseenMakerToken = _.isUndefined(makerTokenIfExists);
+ const isMakerTokenTracked = !_.isUndefined(makerTokenIfExists) && makerTokenIfExists.isTracked;
+ if (isUnseenMakerToken) {
+ tokensToTrack.push({
+ ...this.state.parsedOrder.maker.token,
+ iconUrl: undefined,
+ isTracked: false,
+ isRegistered: false,
+ });
+ } else if (!isMakerTokenTracked) {
+ tokensToTrack.push(makerTokenIfExists);
+ }
+ const isUnseenTakerToken = _.isUndefined(takerTokenIfExists);
+ const isTakerTokenTracked = !_.isUndefined(takerTokenIfExists) && takerTokenIfExists.isTracked;
+ if (isUnseenTakerToken) {
+ tokensToTrack.push({
+ ...this.state.parsedOrder.taker.token,
+ iconUrl: undefined,
+ isTracked: false,
+ isRegistered: false,
+ });
+ } else if (!isTakerTokenTracked) {
+ tokensToTrack.push(takerTokenIfExists);
+ }
+ if (!_.isEmpty(tokensToTrack)) {
+ this.setState({
+ isConfirmingTokenTracking: true,
+ tokensToTrack,
+ });
+ } else {
+ this.setState({
+ areAllInvolvedTokensTracked: true,
+ });
+ }
+ }
+ private async _validateFillOrderFireAndForgetAsync(orderJSON: string) {
+ let orderJSONErrMsg = '';
+ let parsedOrder: Order;
+ try {
+ const order = JSON.parse(orderJSON);
+ const validationResult = this._validator.validate(order, orderSchema);
+ if (validationResult.errors.length > 0) {
+ orderJSONErrMsg = 'Submitted order JSON is not a valid order';
+ utils.consoleLog(`Unexpected order JSON validation error: ${validationResult.errors.join(', ')}`);
+ return;
+ }
+ parsedOrder = order;
- const exchangeContractAddr = this.props.blockchain.getExchangeContractAddressIfExists();
- const makerAmount = new BigNumber(parsedOrder.maker.amount);
- const takerAmount = new BigNumber(parsedOrder.taker.amount);
- const expiration = new BigNumber(parsedOrder.expiration);
- const salt = new BigNumber(parsedOrder.salt);
- const parsedMakerFee = new BigNumber(parsedOrder.maker.feeAmount);
- const parsedTakerFee = new BigNumber(parsedOrder.taker.feeAmount);
+ const exchangeContractAddr = this.props.blockchain.getExchangeContractAddressIfExists();
+ const makerAmount = new BigNumber(parsedOrder.maker.amount);
+ const takerAmount = new BigNumber(parsedOrder.taker.amount);
+ const expiration = new BigNumber(parsedOrder.expiration);
+ const salt = new BigNumber(parsedOrder.salt);
+ const parsedMakerFee = new BigNumber(parsedOrder.maker.feeAmount);
+ const parsedTakerFee = new BigNumber(parsedOrder.taker.feeAmount);
- const zeroExOrder: ZeroExOrder = {
- exchangeContractAddress: parsedOrder.exchangeContract,
- expirationUnixTimestampSec: expiration,
- feeRecipient: parsedOrder.feeRecipient,
- maker: parsedOrder.maker.address,
- makerFee: parsedMakerFee,
- makerTokenAddress: parsedOrder.maker.token.address,
- makerTokenAmount: makerAmount,
- salt,
- taker: _.isEmpty(parsedOrder.taker.address) ? constants.NULL_ADDRESS : parsedOrder.taker.address,
- takerFee: parsedTakerFee,
- takerTokenAddress: parsedOrder.taker.token.address,
- takerTokenAmount: takerAmount,
- };
- const orderHash = ZeroEx.getOrderHashHex(zeroExOrder);
+ const zeroExOrder: ZeroExOrder = {
+ exchangeContractAddress: parsedOrder.exchangeContract,
+ expirationUnixTimestampSec: expiration,
+ feeRecipient: parsedOrder.feeRecipient,
+ maker: parsedOrder.maker.address,
+ makerFee: parsedMakerFee,
+ makerTokenAddress: parsedOrder.maker.token.address,
+ makerTokenAmount: makerAmount,
+ salt,
+ taker: _.isEmpty(parsedOrder.taker.address) ? constants.NULL_ADDRESS : parsedOrder.taker.address,
+ takerFee: parsedTakerFee,
+ takerTokenAddress: parsedOrder.taker.token.address,
+ takerTokenAmount: takerAmount,
+ };
+ const orderHash = ZeroEx.getOrderHashHex(zeroExOrder);
- const signature = parsedOrder.signature;
- const isValidSignature = ZeroEx.isValidSignature(signature.hash, signature, parsedOrder.maker.address);
- if (this.props.networkId !== parsedOrder.networkId) {
- orderJSONErrMsg = `This order was made on another Ethereum network
+ const signature = parsedOrder.signature;
+ const isValidSignature = ZeroEx.isValidSignature(signature.hash, signature, parsedOrder.maker.address);
+ if (this.props.networkId !== parsedOrder.networkId) {
+ orderJSONErrMsg = `This order was made on another Ethereum network
(id: ${parsedOrder.networkId}). Connect to this network to fill.`;
- parsedOrder = undefined;
- } else if (exchangeContractAddr !== parsedOrder.exchangeContract) {
- orderJSONErrMsg = 'This order was made using a deprecated 0x Exchange contract.';
- parsedOrder = undefined;
- } else if (orderHash !== signature.hash) {
- orderJSONErrMsg = 'Order hash does not match supplied plaintext values';
- parsedOrder = undefined;
- } else if (!isValidSignature) {
- orderJSONErrMsg = 'Order signature is invalid';
- parsedOrder = undefined;
- } else {
- // Update user supplied order cache so that if they navigate away from fill view
- // e.g to set a token allowance, when they come back, the fill order persists
- this.props.dispatcher.updateUserSuppliedOrderCache(parsedOrder);
- }
- } catch (err) {
- utils.consoleLog(`Validate order err: ${err}`);
- if (!_.isEmpty(orderJSON)) {
- orderJSONErrMsg = 'Submitted order JSON is not valid JSON';
- }
- this.setState({
- didOrderValidationRun: true,
- orderJSON,
- orderJSONErrMsg,
- parsedOrder,
- });
- return;
- }
+ parsedOrder = undefined;
+ } else if (exchangeContractAddr !== parsedOrder.exchangeContract) {
+ orderJSONErrMsg = 'This order was made using a deprecated 0x Exchange contract.';
+ parsedOrder = undefined;
+ } else if (orderHash !== signature.hash) {
+ orderJSONErrMsg = 'Order hash does not match supplied plaintext values';
+ parsedOrder = undefined;
+ } else if (!isValidSignature) {
+ orderJSONErrMsg = 'Order signature is invalid';
+ parsedOrder = undefined;
+ } else {
+ // Update user supplied order cache so that if they navigate away from fill view
+ // e.g to set a token allowance, when they come back, the fill order persists
+ this.props.dispatcher.updateUserSuppliedOrderCache(parsedOrder);
+ }
+ } catch (err) {
+ utils.consoleLog(`Validate order err: ${err}`);
+ if (!_.isEmpty(orderJSON)) {
+ orderJSONErrMsg = 'Submitted order JSON is not valid JSON';
+ }
+ this.setState({
+ didOrderValidationRun: true,
+ orderJSON,
+ orderJSONErrMsg,
+ parsedOrder,
+ });
+ return;
+ }
- let unavailableTakerAmount = new BigNumber(0);
- if (!_.isEmpty(orderJSONErrMsg)) {
- // Clear cache entry if user updates orderJSON to invalid entry
- this.props.dispatcher.updateUserSuppliedOrderCache(undefined);
- } else {
- const orderHash = parsedOrder.signature.hash;
- unavailableTakerAmount = await this.props.blockchain.getUnavailableTakerAmountAsync(orderHash);
- const isMakerTokenAddressInRegistry = await this.props.blockchain.isAddressInTokenRegistryAsync(
- parsedOrder.maker.token.address,
- );
- const isTakerTokenAddressInRegistry = await this.props.blockchain.isAddressInTokenRegistryAsync(
- parsedOrder.taker.token.address,
- );
- this.setState({
- isMakerTokenAddressInRegistry,
- isTakerTokenAddressInRegistry,
- });
- }
+ let unavailableTakerAmount = new BigNumber(0);
+ if (!_.isEmpty(orderJSONErrMsg)) {
+ // Clear cache entry if user updates orderJSON to invalid entry
+ this.props.dispatcher.updateUserSuppliedOrderCache(undefined);
+ } else {
+ const orderHash = parsedOrder.signature.hash;
+ unavailableTakerAmount = await this.props.blockchain.getUnavailableTakerAmountAsync(orderHash);
+ const isMakerTokenAddressInRegistry = await this.props.blockchain.isAddressInTokenRegistryAsync(
+ parsedOrder.maker.token.address,
+ );
+ const isTakerTokenAddressInRegistry = await this.props.blockchain.isAddressInTokenRegistryAsync(
+ parsedOrder.taker.token.address,
+ );
+ this.setState({
+ isMakerTokenAddressInRegistry,
+ isTakerTokenAddressInRegistry,
+ });
+ }
- this.setState({
- didOrderValidationRun: true,
- orderJSON,
- orderJSONErrMsg,
- parsedOrder,
- unavailableTakerAmount,
- });
+ this.setState({
+ didOrderValidationRun: true,
+ orderJSON,
+ orderJSONErrMsg,
+ parsedOrder,
+ unavailableTakerAmount,
+ });
- await this._checkForUntrackedTokensAndAskToAdd();
- }
- private async _onFillOrderClickFireAndForgetAsync(): Promise<void> {
- if (this.props.blockchainErr !== BlockchainErrs.NoError || _.isEmpty(this.props.userAddress)) {
- this.props.dispatcher.updateShouldBlockchainErrDialogBeOpen(true);
- return;
- }
+ await this._checkForUntrackedTokensAndAskToAdd();
+ }
+ private async _onFillOrderClickFireAndForgetAsync(): Promise<void> {
+ if (this.props.blockchainErr !== BlockchainErrs.NoError || _.isEmpty(this.props.userAddress)) {
+ this.props.dispatcher.updateShouldBlockchainErrDialogBeOpen(true);
+ return;
+ }
- this.setState({
- isFilling: true,
- didFillOrderSucceed: false,
- });
+ this.setState({
+ isFilling: true,
+ didFillOrderSucceed: false,
+ });
- const parsedOrder = this.state.parsedOrder;
- const takerFillAmount = this.props.orderFillAmount;
+ const parsedOrder = this.state.parsedOrder;
+ const takerFillAmount = this.props.orderFillAmount;
- if (_.isUndefined(this.props.userAddress)) {
- this.props.dispatcher.updateShouldBlockchainErrDialogBeOpen(true);
- this.setState({
- isFilling: false,
- });
- return;
- }
- let globalErrMsg = '';
+ if (_.isUndefined(this.props.userAddress)) {
+ this.props.dispatcher.updateShouldBlockchainErrDialogBeOpen(true);
+ this.setState({
+ isFilling: false,
+ });
+ return;
+ }
+ let globalErrMsg = '';
- if (_.isUndefined(takerFillAmount)) {
- globalErrMsg = 'You must specify a fill amount';
- }
+ if (_.isUndefined(takerFillAmount)) {
+ globalErrMsg = 'You must specify a fill amount';
+ }
- const signedOrder = this.props.blockchain.portalOrderToSignedOrder(
- parsedOrder.maker.address,
- parsedOrder.taker.address,
- parsedOrder.maker.token.address,
- parsedOrder.taker.token.address,
- new BigNumber(parsedOrder.maker.amount),
- new BigNumber(parsedOrder.taker.amount),
- new BigNumber(parsedOrder.maker.feeAmount),
- new BigNumber(parsedOrder.taker.feeAmount),
- new BigNumber(this.state.parsedOrder.expiration),
- parsedOrder.feeRecipient,
- parsedOrder.signature,
- new BigNumber(parsedOrder.salt),
- );
- if (_.isEmpty(globalErrMsg)) {
- try {
- await this.props.blockchain.validateFillOrderThrowIfInvalidAsync(
- signedOrder,
- takerFillAmount,
- this.props.userAddress,
- );
- } catch (err) {
- globalErrMsg = utils.zeroExErrToHumanReadableErrMsg(err.message, parsedOrder.taker.address);
- }
- }
- if (!_.isEmpty(globalErrMsg)) {
- this.setState({
- isFilling: false,
- globalErrMsg,
- });
- return;
- }
- try {
- const orderFilledAmount: BigNumber = await this.props.blockchain.fillOrderAsync(
- signedOrder,
- this.props.orderFillAmount,
- );
- // After fill completes, let's update the token balances
- const makerToken = this.props.tokenByAddress[parsedOrder.maker.token.address];
- const takerToken = this.props.tokenByAddress[parsedOrder.taker.token.address];
- const tokens = [makerToken, takerToken];
- await this.props.blockchain.updateTokenBalancesAndAllowancesAsync(tokens);
- this.setState({
- isFilling: false,
- didFillOrderSucceed: true,
- globalErrMsg: '',
- unavailableTakerAmount: this.state.unavailableTakerAmount.plus(orderFilledAmount),
- });
- return;
- } catch (err) {
- this.setState({
- isFilling: false,
- });
- const errMsg = `${err}`;
- if (_.includes(errMsg, 'User denied transaction signature')) {
- return;
- }
- globalErrMsg = 'Failed to fill order, please refresh and try again';
- utils.consoleLog(`${err}`);
- this.setState({
- globalErrMsg,
- });
- await errorReporter.reportAsync(err);
- return;
- }
- }
- private async _onCancelOrderClickFireAndForgetAsync(): Promise<void> {
- if (this.props.blockchainErr !== BlockchainErrs.NoError || _.isEmpty(this.props.userAddress)) {
- this.props.dispatcher.updateShouldBlockchainErrDialogBeOpen(true);
- return;
- }
+ const signedOrder = this.props.blockchain.portalOrderToSignedOrder(
+ parsedOrder.maker.address,
+ parsedOrder.taker.address,
+ parsedOrder.maker.token.address,
+ parsedOrder.taker.token.address,
+ new BigNumber(parsedOrder.maker.amount),
+ new BigNumber(parsedOrder.taker.amount),
+ new BigNumber(parsedOrder.maker.feeAmount),
+ new BigNumber(parsedOrder.taker.feeAmount),
+ new BigNumber(this.state.parsedOrder.expiration),
+ parsedOrder.feeRecipient,
+ parsedOrder.signature,
+ new BigNumber(parsedOrder.salt),
+ );
+ if (_.isEmpty(globalErrMsg)) {
+ try {
+ await this.props.blockchain.validateFillOrderThrowIfInvalidAsync(
+ signedOrder,
+ takerFillAmount,
+ this.props.userAddress,
+ );
+ } catch (err) {
+ globalErrMsg = utils.zeroExErrToHumanReadableErrMsg(err.message, parsedOrder.taker.address);
+ }
+ }
+ if (!_.isEmpty(globalErrMsg)) {
+ this.setState({
+ isFilling: false,
+ globalErrMsg,
+ });
+ return;
+ }
+ try {
+ const orderFilledAmount: BigNumber = await this.props.blockchain.fillOrderAsync(
+ signedOrder,
+ this.props.orderFillAmount,
+ );
+ // After fill completes, let's update the token balances
+ const makerToken = this.props.tokenByAddress[parsedOrder.maker.token.address];
+ const takerToken = this.props.tokenByAddress[parsedOrder.taker.token.address];
+ const tokens = [makerToken, takerToken];
+ await this.props.blockchain.updateTokenBalancesAndAllowancesAsync(tokens);
+ this.setState({
+ isFilling: false,
+ didFillOrderSucceed: true,
+ globalErrMsg: '',
+ unavailableTakerAmount: this.state.unavailableTakerAmount.plus(orderFilledAmount),
+ });
+ return;
+ } catch (err) {
+ this.setState({
+ isFilling: false,
+ });
+ const errMsg = `${err}`;
+ if (_.includes(errMsg, 'User denied transaction signature')) {
+ return;
+ }
+ globalErrMsg = 'Failed to fill order, please refresh and try again';
+ utils.consoleLog(`${err}`);
+ this.setState({
+ globalErrMsg,
+ });
+ await errorReporter.reportAsync(err);
+ return;
+ }
+ }
+ private async _onCancelOrderClickFireAndForgetAsync(): Promise<void> {
+ if (this.props.blockchainErr !== BlockchainErrs.NoError || _.isEmpty(this.props.userAddress)) {
+ this.props.dispatcher.updateShouldBlockchainErrDialogBeOpen(true);
+ return;
+ }
- this.setState({
- isCancelling: true,
- didCancelOrderSucceed: false,
- });
+ this.setState({
+ isCancelling: true,
+ didCancelOrderSucceed: false,
+ });
- const parsedOrder = this.state.parsedOrder;
- const orderHash = parsedOrder.signature.hash;
- const takerAddress = this.props.userAddress;
+ const parsedOrder = this.state.parsedOrder;
+ const orderHash = parsedOrder.signature.hash;
+ const takerAddress = this.props.userAddress;
- if (_.isUndefined(takerAddress)) {
- this.props.dispatcher.updateShouldBlockchainErrDialogBeOpen(true);
- this.setState({
- isFilling: false,
- });
- return;
- }
- let globalErrMsg = '';
+ if (_.isUndefined(takerAddress)) {
+ this.props.dispatcher.updateShouldBlockchainErrDialogBeOpen(true);
+ this.setState({
+ isFilling: false,
+ });
+ return;
+ }
+ let globalErrMsg = '';
- const takerTokenAmount = new BigNumber(parsedOrder.taker.amount);
+ const takerTokenAmount = new BigNumber(parsedOrder.taker.amount);
- const signedOrder = this.props.blockchain.portalOrderToSignedOrder(
- parsedOrder.maker.address,
- parsedOrder.taker.address,
- parsedOrder.maker.token.address,
- parsedOrder.taker.token.address,
- new BigNumber(parsedOrder.maker.amount),
- takerTokenAmount,
- new BigNumber(parsedOrder.maker.feeAmount),
- new BigNumber(parsedOrder.taker.feeAmount),
- new BigNumber(this.state.parsedOrder.expiration),
- parsedOrder.feeRecipient,
- parsedOrder.signature,
- new BigNumber(parsedOrder.salt),
- );
- const unavailableTakerAmount = await this.props.blockchain.getUnavailableTakerAmountAsync(orderHash);
- const availableTakerTokenAmount = takerTokenAmount.minus(unavailableTakerAmount);
- try {
- await this.props.blockchain.validateCancelOrderThrowIfInvalidAsync(signedOrder, availableTakerTokenAmount);
- } catch (err) {
- globalErrMsg = utils.zeroExErrToHumanReadableErrMsg(err.message, parsedOrder.taker.address);
- }
- if (!_.isEmpty(globalErrMsg)) {
- this.setState({
- isCancelling: false,
- globalErrMsg,
- });
- return;
- }
- try {
- await this.props.blockchain.cancelOrderAsync(signedOrder, availableTakerTokenAmount);
- this.setState({
- isCancelling: false,
- didCancelOrderSucceed: true,
- globalErrMsg: '',
- unavailableTakerAmount: takerTokenAmount,
- });
- return;
- } catch (err) {
- this.setState({
- isCancelling: false,
- });
- const errMsg = `${err}`;
- if (_.includes(errMsg, 'User denied transaction signature')) {
- return;
- }
- globalErrMsg = 'Failed to cancel order, please refresh and try again';
- utils.consoleLog(`${err}`);
- this.setState({
- globalErrMsg,
- });
- await errorReporter.reportAsync(err);
- return;
- }
- }
- private _formatCurrencyAmount(amount: BigNumber, decimals: number): number {
- const unitAmount = ZeroEx.toUnitAmount(amount, decimals);
- const roundedUnitAmount = Math.round(unitAmount.toNumber() * 100000) / 100000;
- return roundedUnitAmount;
- }
- private _onToggleTrackConfirmDialog(didConfirmTokenTracking: boolean) {
- if (!didConfirmTokenTracking) {
- this.setState({
- orderJSON: '',
- orderJSONErrMsg: '',
- parsedOrder: undefined,
- });
- } else {
- this.setState({
- areAllInvolvedTokensTracked: true,
- });
- }
- this.setState({
- isConfirmingTokenTracking: !this.state.isConfirmingTokenTracking,
- tokensToTrack: [],
- });
- }
+ const signedOrder = this.props.blockchain.portalOrderToSignedOrder(
+ parsedOrder.maker.address,
+ parsedOrder.taker.address,
+ parsedOrder.maker.token.address,
+ parsedOrder.taker.token.address,
+ new BigNumber(parsedOrder.maker.amount),
+ takerTokenAmount,
+ new BigNumber(parsedOrder.maker.feeAmount),
+ new BigNumber(parsedOrder.taker.feeAmount),
+ new BigNumber(this.state.parsedOrder.expiration),
+ parsedOrder.feeRecipient,
+ parsedOrder.signature,
+ new BigNumber(parsedOrder.salt),
+ );
+ const unavailableTakerAmount = await this.props.blockchain.getUnavailableTakerAmountAsync(orderHash);
+ const availableTakerTokenAmount = takerTokenAmount.minus(unavailableTakerAmount);
+ try {
+ await this.props.blockchain.validateCancelOrderThrowIfInvalidAsync(signedOrder, availableTakerTokenAmount);
+ } catch (err) {
+ globalErrMsg = utils.zeroExErrToHumanReadableErrMsg(err.message, parsedOrder.taker.address);
+ }
+ if (!_.isEmpty(globalErrMsg)) {
+ this.setState({
+ isCancelling: false,
+ globalErrMsg,
+ });
+ return;
+ }
+ try {
+ await this.props.blockchain.cancelOrderAsync(signedOrder, availableTakerTokenAmount);
+ this.setState({
+ isCancelling: false,
+ didCancelOrderSucceed: true,
+ globalErrMsg: '',
+ unavailableTakerAmount: takerTokenAmount,
+ });
+ return;
+ } catch (err) {
+ this.setState({
+ isCancelling: false,
+ });
+ const errMsg = `${err}`;
+ if (_.includes(errMsg, 'User denied transaction signature')) {
+ return;
+ }
+ globalErrMsg = 'Failed to cancel order, please refresh and try again';
+ utils.consoleLog(`${err}`);
+ this.setState({
+ globalErrMsg,
+ });
+ await errorReporter.reportAsync(err);
+ return;
+ }
+ }
+ private _formatCurrencyAmount(amount: BigNumber, decimals: number): number {
+ const unitAmount = ZeroEx.toUnitAmount(amount, decimals);
+ const roundedUnitAmount = Math.round(unitAmount.toNumber() * 100000) / 100000;
+ return roundedUnitAmount;
+ }
+ private _onToggleTrackConfirmDialog(didConfirmTokenTracking: boolean) {
+ if (!didConfirmTokenTracking) {
+ this.setState({
+ orderJSON: '',
+ orderJSONErrMsg: '',
+ parsedOrder: undefined,
+ });
+ } else {
+ this.setState({
+ areAllInvolvedTokensTracked: true,
+ });
+ }
+ this.setState({
+ isConfirmingTokenTracking: !this.state.isConfirmingTokenTracking,
+ tokensToTrack: [],
+ });
+ }
} // tslint:disable:max-file-line-count
diff --git a/packages/website/ts/components/fill_order_json.tsx b/packages/website/ts/components/fill_order_json.tsx
index 3446b8a39..f8e43481a 100644
--- a/packages/website/ts/components/fill_order_json.tsx
+++ b/packages/website/ts/components/fill_order_json.tsx
@@ -10,71 +10,71 @@ import { constants } from 'ts/utils/constants';
import { utils } from 'ts/utils/utils';
interface FillOrderJSONProps {
- blockchain: Blockchain;
- tokenByAddress: TokenByAddress;
- networkId: number;
- orderJSON: string;
- onFillOrderJSONChanged: (event: any) => void;
+ blockchain: Blockchain;
+ tokenByAddress: TokenByAddress;
+ networkId: number;
+ orderJSON: string;
+ onFillOrderJSONChanged: (event: any) => void;
}
interface FillOrderJSONState {}
export class FillOrderJSON extends React.Component<FillOrderJSONProps, FillOrderJSONState> {
- public render() {
- const tokenAddresses = _.keys(this.props.tokenByAddress);
- const exchangeContract = this.props.blockchain.getExchangeContractAddressIfExists();
- const hintSideToAssetToken = {
- [Side.Deposit]: {
- amount: new BigNumber(35),
- address: tokenAddresses[0],
- },
- [Side.Receive]: {
- amount: new BigNumber(89),
- address: tokenAddresses[1],
- },
- };
- const hintOrderExpiryTimestamp = utils.initialOrderExpiryUnixTimestampSec();
- const hintSignatureData = {
- hash: '0xf965a9978a0381ab58f5a2408ad967c...',
- r: '0xf01103f759e2289a28593eaf22e5820032...',
- s: '937862111edcba395f8a9e0cc1b2c5e12320...',
- v: 27,
- };
- const hintSalt = ZeroEx.generatePseudoRandomSalt();
- const feeRecipient = constants.NULL_ADDRESS;
- const hintOrder = utils.generateOrder(
- this.props.networkId,
- exchangeContract,
- hintSideToAssetToken,
- hintOrderExpiryTimestamp,
- '',
- '',
- constants.MAKER_FEE,
- constants.TAKER_FEE,
- feeRecipient,
- hintSignatureData,
- this.props.tokenByAddress,
- hintSalt,
- );
- const hintOrderJSON = `${JSON.stringify(hintOrder, null, '\t').substring(0, 500)}...`;
- return (
- <div>
- <Paper className="p1 overflow-hidden" style={{ height: 164 }}>
- <TextField
- id="orderJSON"
- hintStyle={{ bottom: 0, top: 0 }}
- fullWidth={true}
- value={this.props.orderJSON}
- onChange={this.props.onFillOrderJSONChanged.bind(this)}
- hintText={hintOrderJSON}
- multiLine={true}
- rows={6}
- rowsMax={6}
- underlineStyle={{ display: 'none' }}
- textareaStyle={{ marginTop: 0 }}
- />
- </Paper>
- </div>
- );
- }
+ public render() {
+ const tokenAddresses = _.keys(this.props.tokenByAddress);
+ const exchangeContract = this.props.blockchain.getExchangeContractAddressIfExists();
+ const hintSideToAssetToken = {
+ [Side.Deposit]: {
+ amount: new BigNumber(35),
+ address: tokenAddresses[0],
+ },
+ [Side.Receive]: {
+ amount: new BigNumber(89),
+ address: tokenAddresses[1],
+ },
+ };
+ const hintOrderExpiryTimestamp = utils.initialOrderExpiryUnixTimestampSec();
+ const hintSignatureData = {
+ hash: '0xf965a9978a0381ab58f5a2408ad967c...',
+ r: '0xf01103f759e2289a28593eaf22e5820032...',
+ s: '937862111edcba395f8a9e0cc1b2c5e12320...',
+ v: 27,
+ };
+ const hintSalt = ZeroEx.generatePseudoRandomSalt();
+ const feeRecipient = constants.NULL_ADDRESS;
+ const hintOrder = utils.generateOrder(
+ this.props.networkId,
+ exchangeContract,
+ hintSideToAssetToken,
+ hintOrderExpiryTimestamp,
+ '',
+ '',
+ constants.MAKER_FEE,
+ constants.TAKER_FEE,
+ feeRecipient,
+ hintSignatureData,
+ this.props.tokenByAddress,
+ hintSalt,
+ );
+ const hintOrderJSON = `${JSON.stringify(hintOrder, null, '\t').substring(0, 500)}...`;
+ return (
+ <div>
+ <Paper className="p1 overflow-hidden" style={{ height: 164 }}>
+ <TextField
+ id="orderJSON"
+ hintStyle={{ bottom: 0, top: 0 }}
+ fullWidth={true}
+ value={this.props.orderJSON}
+ onChange={this.props.onFillOrderJSONChanged.bind(this)}
+ hintText={hintOrderJSON}
+ multiLine={true}
+ rows={6}
+ rowsMax={6}
+ underlineStyle={{ display: 'none' }}
+ textareaStyle={{ marginTop: 0 }}
+ />
+ </Paper>
+ </div>
+ );
+ }
}
diff --git a/packages/website/ts/components/fill_warning_dialog.tsx b/packages/website/ts/components/fill_warning_dialog.tsx
index 40b04723d..165d21b34 100644
--- a/packages/website/ts/components/fill_warning_dialog.tsx
+++ b/packages/website/ts/components/fill_warning_dialog.tsx
@@ -4,42 +4,42 @@ import * as React from 'react';
import { colors } from 'ts/utils/colors';
interface FillWarningDialogProps {
- isOpen: boolean;
- onToggleDialog: (didUserCancel: boolean) => void;
+ isOpen: boolean;
+ onToggleDialog: (didUserCancel: boolean) => void;
}
export function FillWarningDialog(props: FillWarningDialogProps) {
- const didCancel = true;
- return (
- <Dialog
- title="Warning"
- titleStyle={{ fontWeight: 100, color: colors.red500 }}
- actions={[
- <FlatButton
- key="fillWarningCancel"
- label="Cancel"
- onTouchTap={props.onToggleDialog.bind(this, didCancel)}
- />,
- <FlatButton
- key="fillWarningContinue"
- label="Fill Order"
- onTouchTap={props.onToggleDialog.bind(this, !didCancel)}
- />,
- ]}
- open={props.isOpen}
- onRequestClose={props.onToggleDialog.bind(this)}
- autoScrollBodyContent={true}
- modal={true}
- >
- <div className="pt2" style={{ color: colors.grey700 }}>
- <div>
- At least one of the tokens in this order was not found in the token registry smart contract and may
- be counterfeit. It is your responsibility to verify the token addresses on Etherscan (
- <a href="https://0xproject.com/wiki#Verifying-Custom-Tokens" target="_blank">
- See this how-to guide
- </a>) before filling an order. <b>This action may result in the loss of funds</b>.
- </div>
- </div>
- </Dialog>
- );
+ const didCancel = true;
+ return (
+ <Dialog
+ title="Warning"
+ titleStyle={{ fontWeight: 100, color: colors.red500 }}
+ actions={[
+ <FlatButton
+ key="fillWarningCancel"
+ label="Cancel"
+ onTouchTap={props.onToggleDialog.bind(this, didCancel)}
+ />,
+ <FlatButton
+ key="fillWarningContinue"
+ label="Fill Order"
+ onTouchTap={props.onToggleDialog.bind(this, !didCancel)}
+ />,
+ ]}
+ open={props.isOpen}
+ onRequestClose={props.onToggleDialog.bind(this)}
+ autoScrollBodyContent={true}
+ modal={true}
+ >
+ <div className="pt2" style={{ color: colors.grey700 }}>
+ <div>
+ At least one of the tokens in this order was not found in the token registry smart contract and may
+ be counterfeit. It is your responsibility to verify the token addresses on Etherscan (
+ <a href="https://0xproject.com/wiki#Verifying-Custom-Tokens" target="_blank">
+ See this how-to guide
+ </a>) before filling an order. <b>This action may result in the loss of funds</b>.
+ </div>
+ </div>
+ </Dialog>
+ );
}
diff --git a/packages/website/ts/components/flash_messages/token_send_completed.tsx b/packages/website/ts/components/flash_messages/token_send_completed.tsx
index 07fca1ddc..18f371624 100644
--- a/packages/website/ts/components/flash_messages/token_send_completed.tsx
+++ b/packages/website/ts/components/flash_messages/token_send_completed.tsx
@@ -7,28 +7,28 @@ import { colors } from 'ts/utils/colors';
import { utils } from 'ts/utils/utils';
interface TokenSendCompletedProps {
- etherScanLinkIfExists?: string;
- token: Token;
- toAddress: string;
- amountInBaseUnits: BigNumber;
+ etherScanLinkIfExists?: string;
+ token: Token;
+ toAddress: string;
+ amountInBaseUnits: BigNumber;
}
interface TokenSendCompletedState {}
export class TokenSendCompleted extends React.Component<TokenSendCompletedProps, TokenSendCompletedState> {
- public render() {
- const etherScanLink = !_.isUndefined(this.props.etherScanLinkIfExists) && (
- <a style={{ color: colors.white }} href={`${this.props.etherScanLinkIfExists}`} target="_blank">
- Verify on Etherscan
- </a>
- );
- const amountInUnits = ZeroEx.toUnitAmount(this.props.amountInBaseUnits, this.props.token.decimals);
- const truncatedAddress = utils.getAddressBeginAndEnd(this.props.toAddress);
- return (
- <div>
- {`Sent ${amountInUnits} ${this.props.token.symbol} to ${truncatedAddress}: `}
- {etherScanLink}
- </div>
- );
- }
+ public render() {
+ const etherScanLink = !_.isUndefined(this.props.etherScanLinkIfExists) && (
+ <a style={{ color: colors.white }} href={`${this.props.etherScanLinkIfExists}`} target="_blank">
+ Verify on Etherscan
+ </a>
+ );
+ const amountInUnits = ZeroEx.toUnitAmount(this.props.amountInBaseUnits, this.props.token.decimals);
+ const truncatedAddress = utils.getAddressBeginAndEnd(this.props.toAddress);
+ return (
+ <div>
+ {`Sent ${amountInUnits} ${this.props.token.symbol} to ${truncatedAddress}: `}
+ {etherScanLink}
+ </div>
+ );
+ }
}
diff --git a/packages/website/ts/components/flash_messages/transaction_submitted.tsx b/packages/website/ts/components/flash_messages/transaction_submitted.tsx
index 14464b923..862e382dd 100644
--- a/packages/website/ts/components/flash_messages/transaction_submitted.tsx
+++ b/packages/website/ts/components/flash_messages/transaction_submitted.tsx
@@ -3,24 +3,24 @@ import * as React from 'react';
import { colors } from 'ts/utils/colors';
interface TransactionSubmittedProps {
- etherScanLinkIfExists?: string;
+ etherScanLinkIfExists?: string;
}
interface TransactionSubmittedState {}
export class TransactionSubmitted extends React.Component<TransactionSubmittedProps, TransactionSubmittedState> {
- public render() {
- if (_.isUndefined(this.props.etherScanLinkIfExists)) {
- return <div>Transaction submitted to the network</div>;
- } else {
- return (
- <div>
- Transaction submitted to the network:{' '}
- <a style={{ color: colors.white }} href={`${this.props.etherScanLinkIfExists}`} target="_blank">
- Verify on Etherscan
- </a>
- </div>
- );
- }
- }
+ public render() {
+ if (_.isUndefined(this.props.etherScanLinkIfExists)) {
+ return <div>Transaction submitted to the network</div>;
+ } else {
+ return (
+ <div>
+ Transaction submitted to the network:{' '}
+ <a style={{ color: colors.white }} href={`${this.props.etherScanLinkIfExists}`} target="_blank">
+ Verify on Etherscan
+ </a>
+ </div>
+ );
+ }
+ }
}
diff --git a/packages/website/ts/components/footer.tsx b/packages/website/ts/components/footer.tsx
index 3a342591c..a0f1a0c96 100644
--- a/packages/website/ts/components/footer.tsx
+++ b/packages/website/ts/components/footer.tsx
@@ -6,106 +6,106 @@ import { colors } from 'ts/utils/colors';
import { constants } from 'ts/utils/constants';
interface MenuItemsBySection {
- [sectionName: string]: FooterMenuItem[];
+ [sectionName: string]: FooterMenuItem[];
}
interface FooterMenuItem {
- title: string;
- path?: string;
- isExternal?: boolean;
+ title: string;
+ path?: string;
+ isExternal?: boolean;
}
enum Sections {
- Documentation = 'Documentation',
- Community = 'Community',
- Organization = 'Organization',
+ Documentation = 'Documentation',
+ Community = 'Community',
+ Organization = 'Organization',
}
const ICON_DIMENSION = 16;
const menuItemsBySection: MenuItemsBySection = {
- Documentation: [
- {
- title: '0x.js',
- path: WebsitePaths.ZeroExJs,
- },
- {
- title: '0x Smart Contracts',
- path: WebsitePaths.SmartContracts,
- },
- {
- title: '0x Connect',
- path: WebsitePaths.Connect,
- },
- {
- title: 'Whitepaper',
- path: WebsitePaths.Whitepaper,
- isExternal: true,
- },
- {
- title: 'Wiki',
- path: WebsitePaths.Wiki,
- },
- {
- title: 'FAQ',
- path: WebsitePaths.FAQ,
- },
- ],
- Community: [
- {
- title: 'Rocket.chat',
- isExternal: true,
- path: constants.URL_ZEROEX_CHAT,
- },
- {
- title: 'Blog',
- isExternal: true,
- path: constants.URL_BLOG,
- },
- {
- title: 'Twitter',
- isExternal: true,
- path: constants.URL_TWITTER,
- },
- {
- title: 'Reddit',
- isExternal: true,
- path: constants.URL_REDDIT,
- },
- {
- title: 'Forum',
- isExternal: true,
- path: constants.URL_DISCOURSE_FORUM,
- },
- ],
- Organization: [
- {
- title: 'About',
- isExternal: false,
- path: WebsitePaths.About,
- },
- {
- title: 'Careers',
- isExternal: true,
- path: constants.URL_ANGELLIST,
- },
- {
- title: 'Contact',
- isExternal: true,
- path: 'mailto:team@0xproject.com',
- },
- ],
+ Documentation: [
+ {
+ title: '0x.js',
+ path: WebsitePaths.ZeroExJs,
+ },
+ {
+ title: '0x Smart Contracts',
+ path: WebsitePaths.SmartContracts,
+ },
+ {
+ title: '0x Connect',
+ path: WebsitePaths.Connect,
+ },
+ {
+ title: 'Whitepaper',
+ path: WebsitePaths.Whitepaper,
+ isExternal: true,
+ },
+ {
+ title: 'Wiki',
+ path: WebsitePaths.Wiki,
+ },
+ {
+ title: 'FAQ',
+ path: WebsitePaths.FAQ,
+ },
+ ],
+ Community: [
+ {
+ title: 'Rocket.chat',
+ isExternal: true,
+ path: constants.URL_ZEROEX_CHAT,
+ },
+ {
+ title: 'Blog',
+ isExternal: true,
+ path: constants.URL_BLOG,
+ },
+ {
+ title: 'Twitter',
+ isExternal: true,
+ path: constants.URL_TWITTER,
+ },
+ {
+ title: 'Reddit',
+ isExternal: true,
+ path: constants.URL_REDDIT,
+ },
+ {
+ title: 'Forum',
+ isExternal: true,
+ path: constants.URL_DISCOURSE_FORUM,
+ },
+ ],
+ Organization: [
+ {
+ title: 'About',
+ isExternal: false,
+ path: WebsitePaths.About,
+ },
+ {
+ title: 'Careers',
+ isExternal: true,
+ path: constants.URL_ANGELLIST,
+ },
+ {
+ title: 'Contact',
+ isExternal: true,
+ path: 'mailto:team@0xproject.com',
+ },
+ ],
};
const linkStyle = {
- color: colors.white,
- cursor: 'pointer',
+ color: colors.white,
+ cursor: 'pointer',
};
const titleToIcon: { [title: string]: string } = {
- 'Rocket.chat': 'rocketchat.png',
- Blog: 'medium.png',
- Twitter: 'twitter.png',
- Reddit: 'reddit.png',
- Forum: 'discourse.png',
+ 'Rocket.chat': 'rocketchat.png',
+ Blog: 'medium.png',
+ Twitter: 'twitter.png',
+ Reddit: 'reddit.png',
+ Forum: 'discourse.png',
};
export interface FooterProps {}
@@ -113,100 +113,100 @@ export interface FooterProps {}
interface FooterState {}
export class Footer extends React.Component<FooterProps, FooterState> {
- public render() {
- return (
- <div className="relative pb4 pt2" style={{ backgroundColor: colors.darkerGrey }}>
- <div className="mx-auto max-width-4 md-px2 lg-px0 py4 clearfix" style={{ color: colors.white }}>
- <div className="col lg-col-4 md-col-4 col-12 left">
- <div className="sm-mx-auto" style={{ width: 148 }}>
- <div>
- <img src="/images/protocol_logo_white.png" height="30" />
- </div>
- <div
- style={{
- fontSize: 11,
- color: colors.grey,
- paddingLeft: 37,
- paddingTop: 2,
- }}
- >
- © ZeroEx, Intl.
- </div>
- </div>
- </div>
- <div className="col lg-col-8 md-col-8 col-12 lg-pl4 md-pl4">
- <div className="col lg-col-4 md-col-4 col-12">
- <div className="lg-right md-right sm-center">
- {this._renderHeader(Sections.Documentation)}
- {_.map(menuItemsBySection[Sections.Documentation], this._renderMenuItem.bind(this))}
- </div>
- </div>
- <div className="col lg-col-4 md-col-4 col-12 lg-pr2 md-pr2">
- <div className="lg-right md-right sm-center">
- {this._renderHeader(Sections.Community)}
- {_.map(menuItemsBySection[Sections.Community], this._renderMenuItem.bind(this))}
- </div>
- </div>
- <div className="col lg-col-4 md-col-4 col-12">
- <div className="lg-right md-right sm-center">
- {this._renderHeader(Sections.Organization)}
- {_.map(menuItemsBySection[Sections.Organization], this._renderMenuItem.bind(this))}
- </div>
- </div>
- </div>
- </div>
- </div>
- );
- }
- private _renderIcon(fileName: string) {
- return (
- <div style={{ height: ICON_DIMENSION, width: ICON_DIMENSION }}>
- <img src={`/images/social/${fileName}`} style={{ width: ICON_DIMENSION }} />
- </div>
- );
- }
- private _renderMenuItem(item: FooterMenuItem) {
- const iconIfExists = titleToIcon[item.title];
- return (
- <div key={item.title} className="sm-center" style={{ fontSize: 13, paddingTop: 25 }}>
- {item.isExternal ? (
- <a className="text-decoration-none" style={linkStyle} target="_blank" href={item.path}>
- {!_.isUndefined(iconIfExists) ? (
- <div className="sm-mx-auto" style={{ width: 65 }}>
- <div className="flex">
- <div className="pr1">{this._renderIcon(iconIfExists)}</div>
- <div>{item.title}</div>
- </div>
- </div>
- ) : (
- item.title
- )}
- </a>
- ) : (
- <Link to={item.path} style={linkStyle} className="text-decoration-none">
- <div>
- {!_.isUndefined(iconIfExists) && (
- <div className="pr1">{this._renderIcon(iconIfExists)}</div>
- )}
- {item.title}
- </div>
- </Link>
- )}
- </div>
- );
- }
- private _renderHeader(title: string) {
- const headerStyle = {
- textTransform: 'uppercase',
- color: colors.grey400,
- letterSpacing: 2,
- fontFamily: 'Roboto Mono',
- fontSize: 13,
- };
- return (
- <div className="lg-pb2 md-pb2 sm-pt4" style={headerStyle}>
- {title}
- </div>
- );
- }
+ public render() {
+ return (
+ <div className="relative pb4 pt2" style={{ backgroundColor: colors.darkerGrey }}>
+ <div className="mx-auto max-width-4 md-px2 lg-px0 py4 clearfix" style={{ color: colors.white }}>
+ <div className="col lg-col-4 md-col-4 col-12 left">
+ <div className="sm-mx-auto" style={{ width: 148 }}>
+ <div>
+ <img src="/images/protocol_logo_white.png" height="30" />
+ </div>
+ <div
+ style={{
+ fontSize: 11,
+ color: colors.grey,
+ paddingLeft: 37,
+ paddingTop: 2,
+ }}
+ >
+ © ZeroEx, Intl.
+ </div>
+ </div>
+ </div>
+ <div className="col lg-col-8 md-col-8 col-12 lg-pl4 md-pl4">
+ <div className="col lg-col-4 md-col-4 col-12">
+ <div className="lg-right md-right sm-center">
+ {this._renderHeader(Sections.Documentation)}
+ {_.map(menuItemsBySection[Sections.Documentation], this._renderMenuItem.bind(this))}
+ </div>
+ </div>
+ <div className="col lg-col-4 md-col-4 col-12 lg-pr2 md-pr2">
+ <div className="lg-right md-right sm-center">
+ {this._renderHeader(Sections.Community)}
+ {_.map(menuItemsBySection[Sections.Community], this._renderMenuItem.bind(this))}
+ </div>
+ </div>
+ <div className="col lg-col-4 md-col-4 col-12">
+ <div className="lg-right md-right sm-center">
+ {this._renderHeader(Sections.Organization)}
+ {_.map(menuItemsBySection[Sections.Organization], this._renderMenuItem.bind(this))}
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ );
+ }
+ private _renderIcon(fileName: string) {
+ return (
+ <div style={{ height: ICON_DIMENSION, width: ICON_DIMENSION }}>
+ <img src={`/images/social/${fileName}`} style={{ width: ICON_DIMENSION }} />
+ </div>
+ );
+ }
+ private _renderMenuItem(item: FooterMenuItem) {
+ const iconIfExists = titleToIcon[item.title];
+ return (
+ <div key={item.title} className="sm-center" style={{ fontSize: 13, paddingTop: 25 }}>
+ {item.isExternal ? (
+ <a className="text-decoration-none" style={linkStyle} target="_blank" href={item.path}>
+ {!_.isUndefined(iconIfExists) ? (
+ <div className="sm-mx-auto" style={{ width: 65 }}>
+ <div className="flex">
+ <div className="pr1">{this._renderIcon(iconIfExists)}</div>
+ <div>{item.title}</div>
+ </div>
+ </div>
+ ) : (
+ item.title
+ )}
+ </a>
+ ) : (
+ <Link to={item.path} style={linkStyle} className="text-decoration-none">
+ <div>
+ {!_.isUndefined(iconIfExists) && (
+ <div className="pr1">{this._renderIcon(iconIfExists)}</div>
+ )}
+ {item.title}
+ </div>
+ </Link>
+ )}
+ </div>
+ );
+ }
+ private _renderHeader(title: string) {
+ const headerStyle = {
+ textTransform: 'uppercase',
+ color: colors.grey400,
+ letterSpacing: 2,
+ fontFamily: 'Roboto Mono',
+ fontSize: 13,
+ };
+ return (
+ <div className="lg-pb2 md-pb2 sm-pt4" style={headerStyle}>
+ {title}
+ </div>
+ );
+ }
}
diff --git a/packages/website/ts/components/generate_order/asset_picker.tsx b/packages/website/ts/components/generate_order/asset_picker.tsx
index 5eed2fabf..df7d87cfd 100644
--- a/packages/website/ts/components/generate_order/asset_picker.tsx
+++ b/packages/website/ts/components/generate_order/asset_picker.tsx
@@ -13,264 +13,264 @@ import { DialogConfigs, Token, TokenByAddress, TokenState, TokenVisibility } fro
const TOKEN_ICON_DIMENSION = 100;
const TILE_DIMENSION = 146;
enum AssetViews {
- ASSET_PICKER = 'ASSET_PICKER',
- NEW_TOKEN_FORM = 'NEW_TOKEN_FORM',
- CONFIRM_TRACK_TOKEN = 'CONFIRM_TRACK_TOKEN',
+ ASSET_PICKER = 'ASSET_PICKER',
+ NEW_TOKEN_FORM = 'NEW_TOKEN_FORM',
+ CONFIRM_TRACK_TOKEN = 'CONFIRM_TRACK_TOKEN',
}
interface AssetPickerProps {
- userAddress: string;
- blockchain: Blockchain;
- dispatcher: Dispatcher;
- networkId: number;
- isOpen: boolean;
- currentTokenAddress: string;
- onTokenChosen: (tokenAddress: string) => void;
- tokenByAddress: TokenByAddress;
- tokenVisibility?: TokenVisibility;
+ userAddress: string;
+ blockchain: Blockchain;
+ dispatcher: Dispatcher;
+ networkId: number;
+ isOpen: boolean;
+ currentTokenAddress: string;
+ onTokenChosen: (tokenAddress: string) => void;
+ tokenByAddress: TokenByAddress;
+ tokenVisibility?: TokenVisibility;
}
interface AssetPickerState {
- assetView: AssetViews;
- hoveredAddress: string | undefined;
- chosenTrackTokenAddress: string;
- isAddingTokenToTracked: boolean;
+ assetView: AssetViews;
+ hoveredAddress: string | undefined;
+ chosenTrackTokenAddress: string;
+ isAddingTokenToTracked: boolean;
}
export class AssetPicker extends React.Component<AssetPickerProps, AssetPickerState> {
- public static defaultProps: Partial<AssetPickerProps> = {
- tokenVisibility: TokenVisibility.ALL,
- };
- private _dialogConfigsByAssetView: { [assetView: string]: DialogConfigs };
- constructor(props: AssetPickerProps) {
- super(props);
- this.state = {
- assetView: AssetViews.ASSET_PICKER,
- hoveredAddress: undefined,
- chosenTrackTokenAddress: undefined,
- isAddingTokenToTracked: false,
- };
- this._dialogConfigsByAssetView = {
- [AssetViews.ASSET_PICKER]: {
- title: 'Select token',
- isModal: false,
- actions: [],
- },
- [AssetViews.NEW_TOKEN_FORM]: {
- title: 'Add an ERC20 token',
- isModal: false,
- actions: [],
- },
- [AssetViews.CONFIRM_TRACK_TOKEN]: {
- title: 'Tracking confirmation',
- isModal: true,
- actions: [
- <FlatButton
- key="noTracking"
- label="No"
- onTouchTap={this._onTrackConfirmationRespondedAsync.bind(this, false)}
- />,
- <FlatButton
- key="yesTrack"
- label="Yes"
- onTouchTap={this._onTrackConfirmationRespondedAsync.bind(this, true)}
- />,
- ],
- },
- };
- }
- public render() {
- const dialogConfigs: DialogConfigs = this._dialogConfigsByAssetView[this.state.assetView];
- return (
- <Dialog
- title={dialogConfigs.title}
- titleStyle={{ fontWeight: 100 }}
- modal={dialogConfigs.isModal}
- open={this.props.isOpen}
- actions={dialogConfigs.actions}
- onRequestClose={this._onCloseDialog.bind(this)}
- >
- {this.state.assetView === AssetViews.ASSET_PICKER && this._renderAssetPicker()}
- {this.state.assetView === AssetViews.NEW_TOKEN_FORM && (
- <NewTokenForm
- blockchain={this.props.blockchain}
- onNewTokenSubmitted={this._onNewTokenSubmitted.bind(this)}
- tokenByAddress={this.props.tokenByAddress}
- />
- )}
- {this.state.assetView === AssetViews.CONFIRM_TRACK_TOKEN && this._renderConfirmTrackToken()}
- </Dialog>
- );
- }
- private _renderConfirmTrackToken() {
- const token = this.props.tokenByAddress[this.state.chosenTrackTokenAddress];
- return (
- <TrackTokenConfirmation
- tokens={[token]}
- tokenByAddress={this.props.tokenByAddress}
- networkId={this.props.networkId}
- isAddingTokenToTracked={this.state.isAddingTokenToTracked}
- />
- );
- }
- private _renderAssetPicker() {
- return (
- <div
- className="clearfix flex flex-wrap"
- style={{
- overflowY: 'auto',
- maxWidth: 720,
- maxHeight: 356,
- marginBottom: 10,
- }}
- >
- {this._renderGridTiles()}
- </div>
- );
- }
- private _renderGridTiles() {
- let isHovered;
- let tileStyles;
- const gridTiles = _.map(this.props.tokenByAddress, (token: Token, address: string) => {
- if (
- (this.props.tokenVisibility === TokenVisibility.TRACKED && !token.isTracked) ||
- (this.props.tokenVisibility === TokenVisibility.UNTRACKED && token.isTracked)
- ) {
- return null; // Skip
- }
- isHovered = this.state.hoveredAddress === address;
- tileStyles = {
- cursor: 'pointer',
- opacity: isHovered ? 0.6 : 1,
- };
- return (
- <div
- key={address}
- style={{
- width: TILE_DIMENSION,
- height: TILE_DIMENSION,
- ...tileStyles,
- }}
- className="p2 mx-auto"
- onClick={this._onChooseToken.bind(this, address)}
- onMouseEnter={this._onToggleHover.bind(this, address, true)}
- onMouseLeave={this._onToggleHover.bind(this, address, false)}
- >
- <div className="p1 center">
- <TokenIcon token={token} diameter={TOKEN_ICON_DIMENSION} />
- </div>
- <div className="center">{token.name}</div>
- </div>
- );
- });
- const otherTokenKey = 'otherToken';
- isHovered = this.state.hoveredAddress === otherTokenKey;
- tileStyles = {
- cursor: 'pointer',
- opacity: isHovered ? 0.6 : 1,
- };
- if (this.props.tokenVisibility !== TokenVisibility.TRACKED) {
- gridTiles.push(
- <div
- key={otherTokenKey}
- style={{
- width: TILE_DIMENSION,
- height: TILE_DIMENSION,
- ...tileStyles,
- }}
- className="p2 mx-auto"
- onClick={this._onCustomAssetChosen.bind(this)}
- onMouseEnter={this._onToggleHover.bind(this, otherTokenKey, true)}
- onMouseLeave={this._onToggleHover.bind(this, otherTokenKey, false)}
- >
- <div className="p1 center">
- <i
- style={{ fontSize: 105, paddingLeft: 1, paddingRight: 1 }}
- className="zmdi zmdi-plus-circle"
- />
- </div>
- <div className="center">Other ERC20 Token</div>
- </div>,
- );
- }
- return gridTiles;
- }
- private _onToggleHover(address: string, isHovered: boolean) {
- const hoveredAddress = isHovered ? address : undefined;
- this.setState({
- hoveredAddress,
- });
- }
- private _onCloseDialog() {
- this.setState({
- assetView: AssetViews.ASSET_PICKER,
- });
- this.props.onTokenChosen(this.props.currentTokenAddress);
- }
- private _onChooseToken(tokenAddress: string) {
- const token = this.props.tokenByAddress[tokenAddress];
- if (token.isTracked) {
- this.props.onTokenChosen(tokenAddress);
- } else {
- this.setState({
- assetView: AssetViews.CONFIRM_TRACK_TOKEN,
- chosenTrackTokenAddress: tokenAddress,
- });
- }
- }
- private _onCustomAssetChosen() {
- this.setState({
- assetView: AssetViews.NEW_TOKEN_FORM,
- });
- }
- private _onNewTokenSubmitted(newToken: Token, newTokenState: TokenState) {
- this.props.dispatcher.updateTokenStateByAddress({
- [newToken.address]: newTokenState,
- });
- trackedTokenStorage.addTrackedTokenToUser(this.props.userAddress, this.props.networkId, newToken);
- this.props.dispatcher.addTokenToTokenByAddress(newToken);
- this.setState({
- assetView: AssetViews.ASSET_PICKER,
- });
- this.props.onTokenChosen(newToken.address);
- }
- private async _onTrackConfirmationRespondedAsync(didUserAcceptTracking: boolean) {
- if (!didUserAcceptTracking) {
- this.setState({
- isAddingTokenToTracked: false,
- assetView: AssetViews.ASSET_PICKER,
- chosenTrackTokenAddress: undefined,
- });
- this._onCloseDialog();
- return;
- }
- this.setState({
- isAddingTokenToTracked: true,
- });
- const tokenAddress = this.state.chosenTrackTokenAddress;
- const token = this.props.tokenByAddress[tokenAddress];
- const newTokenEntry = {
- ...token,
- };
+ public static defaultProps: Partial<AssetPickerProps> = {
+ tokenVisibility: TokenVisibility.ALL,
+ };
+ private _dialogConfigsByAssetView: { [assetView: string]: DialogConfigs };
+ constructor(props: AssetPickerProps) {
+ super(props);
+ this.state = {
+ assetView: AssetViews.ASSET_PICKER,
+ hoveredAddress: undefined,
+ chosenTrackTokenAddress: undefined,
+ isAddingTokenToTracked: false,
+ };
+ this._dialogConfigsByAssetView = {
+ [AssetViews.ASSET_PICKER]: {
+ title: 'Select token',
+ isModal: false,
+ actions: [],
+ },
+ [AssetViews.NEW_TOKEN_FORM]: {
+ title: 'Add an ERC20 token',
+ isModal: false,
+ actions: [],
+ },
+ [AssetViews.CONFIRM_TRACK_TOKEN]: {
+ title: 'Tracking confirmation',
+ isModal: true,
+ actions: [
+ <FlatButton
+ key="noTracking"
+ label="No"
+ onTouchTap={this._onTrackConfirmationRespondedAsync.bind(this, false)}
+ />,
+ <FlatButton
+ key="yesTrack"
+ label="Yes"
+ onTouchTap={this._onTrackConfirmationRespondedAsync.bind(this, true)}
+ />,
+ ],
+ },
+ };
+ }
+ public render() {
+ const dialogConfigs: DialogConfigs = this._dialogConfigsByAssetView[this.state.assetView];
+ return (
+ <Dialog
+ title={dialogConfigs.title}
+ titleStyle={{ fontWeight: 100 }}
+ modal={dialogConfigs.isModal}
+ open={this.props.isOpen}
+ actions={dialogConfigs.actions}
+ onRequestClose={this._onCloseDialog.bind(this)}
+ >
+ {this.state.assetView === AssetViews.ASSET_PICKER && this._renderAssetPicker()}
+ {this.state.assetView === AssetViews.NEW_TOKEN_FORM && (
+ <NewTokenForm
+ blockchain={this.props.blockchain}
+ onNewTokenSubmitted={this._onNewTokenSubmitted.bind(this)}
+ tokenByAddress={this.props.tokenByAddress}
+ />
+ )}
+ {this.state.assetView === AssetViews.CONFIRM_TRACK_TOKEN && this._renderConfirmTrackToken()}
+ </Dialog>
+ );
+ }
+ private _renderConfirmTrackToken() {
+ const token = this.props.tokenByAddress[this.state.chosenTrackTokenAddress];
+ return (
+ <TrackTokenConfirmation
+ tokens={[token]}
+ tokenByAddress={this.props.tokenByAddress}
+ networkId={this.props.networkId}
+ isAddingTokenToTracked={this.state.isAddingTokenToTracked}
+ />
+ );
+ }
+ private _renderAssetPicker() {
+ return (
+ <div
+ className="clearfix flex flex-wrap"
+ style={{
+ overflowY: 'auto',
+ maxWidth: 720,
+ maxHeight: 356,
+ marginBottom: 10,
+ }}
+ >
+ {this._renderGridTiles()}
+ </div>
+ );
+ }
+ private _renderGridTiles() {
+ let isHovered;
+ let tileStyles;
+ const gridTiles = _.map(this.props.tokenByAddress, (token: Token, address: string) => {
+ if (
+ (this.props.tokenVisibility === TokenVisibility.TRACKED && !token.isTracked) ||
+ (this.props.tokenVisibility === TokenVisibility.UNTRACKED && token.isTracked)
+ ) {
+ return null; // Skip
+ }
+ isHovered = this.state.hoveredAddress === address;
+ tileStyles = {
+ cursor: 'pointer',
+ opacity: isHovered ? 0.6 : 1,
+ };
+ return (
+ <div
+ key={address}
+ style={{
+ width: TILE_DIMENSION,
+ height: TILE_DIMENSION,
+ ...tileStyles,
+ }}
+ className="p2 mx-auto"
+ onClick={this._onChooseToken.bind(this, address)}
+ onMouseEnter={this._onToggleHover.bind(this, address, true)}
+ onMouseLeave={this._onToggleHover.bind(this, address, false)}
+ >
+ <div className="p1 center">
+ <TokenIcon token={token} diameter={TOKEN_ICON_DIMENSION} />
+ </div>
+ <div className="center">{token.name}</div>
+ </div>
+ );
+ });
+ const otherTokenKey = 'otherToken';
+ isHovered = this.state.hoveredAddress === otherTokenKey;
+ tileStyles = {
+ cursor: 'pointer',
+ opacity: isHovered ? 0.6 : 1,
+ };
+ if (this.props.tokenVisibility !== TokenVisibility.TRACKED) {
+ gridTiles.push(
+ <div
+ key={otherTokenKey}
+ style={{
+ width: TILE_DIMENSION,
+ height: TILE_DIMENSION,
+ ...tileStyles,
+ }}
+ className="p2 mx-auto"
+ onClick={this._onCustomAssetChosen.bind(this)}
+ onMouseEnter={this._onToggleHover.bind(this, otherTokenKey, true)}
+ onMouseLeave={this._onToggleHover.bind(this, otherTokenKey, false)}
+ >
+ <div className="p1 center">
+ <i
+ style={{ fontSize: 105, paddingLeft: 1, paddingRight: 1 }}
+ className="zmdi zmdi-plus-circle"
+ />
+ </div>
+ <div className="center">Other ERC20 Token</div>
+ </div>,
+ );
+ }
+ return gridTiles;
+ }
+ private _onToggleHover(address: string, isHovered: boolean) {
+ const hoveredAddress = isHovered ? address : undefined;
+ this.setState({
+ hoveredAddress,
+ });
+ }
+ private _onCloseDialog() {
+ this.setState({
+ assetView: AssetViews.ASSET_PICKER,
+ });
+ this.props.onTokenChosen(this.props.currentTokenAddress);
+ }
+ private _onChooseToken(tokenAddress: string) {
+ const token = this.props.tokenByAddress[tokenAddress];
+ if (token.isTracked) {
+ this.props.onTokenChosen(tokenAddress);
+ } else {
+ this.setState({
+ assetView: AssetViews.CONFIRM_TRACK_TOKEN,
+ chosenTrackTokenAddress: tokenAddress,
+ });
+ }
+ }
+ private _onCustomAssetChosen() {
+ this.setState({
+ assetView: AssetViews.NEW_TOKEN_FORM,
+ });
+ }
+ private _onNewTokenSubmitted(newToken: Token, newTokenState: TokenState) {
+ this.props.dispatcher.updateTokenStateByAddress({
+ [newToken.address]: newTokenState,
+ });
+ trackedTokenStorage.addTrackedTokenToUser(this.props.userAddress, this.props.networkId, newToken);
+ this.props.dispatcher.addTokenToTokenByAddress(newToken);
+ this.setState({
+ assetView: AssetViews.ASSET_PICKER,
+ });
+ this.props.onTokenChosen(newToken.address);
+ }
+ private async _onTrackConfirmationRespondedAsync(didUserAcceptTracking: boolean) {
+ if (!didUserAcceptTracking) {
+ this.setState({
+ isAddingTokenToTracked: false,
+ assetView: AssetViews.ASSET_PICKER,
+ chosenTrackTokenAddress: undefined,
+ });
+ this._onCloseDialog();
+ return;
+ }
+ this.setState({
+ isAddingTokenToTracked: true,
+ });
+ const tokenAddress = this.state.chosenTrackTokenAddress;
+ const token = this.props.tokenByAddress[tokenAddress];
+ const newTokenEntry = {
+ ...token,
+ };
- newTokenEntry.isTracked = true;
- trackedTokenStorage.addTrackedTokenToUser(this.props.userAddress, this.props.networkId, newTokenEntry);
+ newTokenEntry.isTracked = true;
+ trackedTokenStorage.addTrackedTokenToUser(this.props.userAddress, this.props.networkId, newTokenEntry);
- const [balance, allowance] = await this.props.blockchain.getCurrentUserTokenBalanceAndAllowanceAsync(
- token.address,
- );
- this.props.dispatcher.updateTokenStateByAddress({
- [token.address]: {
- balance,
- allowance,
- },
- });
- this.props.dispatcher.updateTokenByAddress([newTokenEntry]);
- this.setState({
- isAddingTokenToTracked: false,
- assetView: AssetViews.ASSET_PICKER,
- chosenTrackTokenAddress: undefined,
- });
- this.props.onTokenChosen(tokenAddress);
- }
+ const [balance, allowance] = await this.props.blockchain.getCurrentUserTokenBalanceAndAllowanceAsync(
+ token.address,
+ );
+ this.props.dispatcher.updateTokenStateByAddress({
+ [token.address]: {
+ balance,
+ allowance,
+ },
+ });
+ this.props.dispatcher.updateTokenByAddress([newTokenEntry]);
+ this.setState({
+ isAddingTokenToTracked: false,
+ assetView: AssetViews.ASSET_PICKER,
+ chosenTrackTokenAddress: undefined,
+ });
+ this.props.onTokenChosen(tokenAddress);
+ }
}
diff --git a/packages/website/ts/components/generate_order/generate_order_form.tsx b/packages/website/ts/components/generate_order/generate_order_form.tsx
index b10b2d609..3ae0d48a7 100644
--- a/packages/website/ts/components/generate_order/generate_order_form.tsx
+++ b/packages/website/ts/components/generate_order/generate_order_form.tsx
@@ -19,335 +19,335 @@ import { Dispatcher } from 'ts/redux/dispatcher';
import { orderSchema } from 'ts/schemas/order_schema';
import { SchemaValidator } from 'ts/schemas/validator';
import {
- AlertTypes,
- BlockchainErrs,
- HashData,
- Side,
- SideToAssetToken,
- SignatureData,
- Token,
- TokenByAddress,
- TokenStateByAddress,
+ AlertTypes,
+ BlockchainErrs,
+ HashData,
+ Side,
+ SideToAssetToken,
+ SignatureData,
+ Token,
+ TokenByAddress,
+ TokenStateByAddress,
} from 'ts/types';
import { colors } from 'ts/utils/colors';
import { errorReporter } from 'ts/utils/error_reporter';
import { utils } from 'ts/utils/utils';
enum SigningState {
- UNSIGNED,
- SIGNING,
- SIGNED,
+ UNSIGNED,
+ SIGNING,
+ SIGNED,
}
interface GenerateOrderFormProps {
- blockchain: Blockchain;
- blockchainErr: BlockchainErrs;
- blockchainIsLoaded: boolean;
- dispatcher: Dispatcher;
- hashData: HashData;
- orderExpiryTimestamp: BigNumber;
- networkId: number;
- userAddress: string;
- orderSignatureData: SignatureData;
- orderTakerAddress: string;
- orderSalt: BigNumber;
- sideToAssetToken: SideToAssetToken;
- tokenByAddress: TokenByAddress;
- tokenStateByAddress: TokenStateByAddress;
+ blockchain: Blockchain;
+ blockchainErr: BlockchainErrs;
+ blockchainIsLoaded: boolean;
+ dispatcher: Dispatcher;
+ hashData: HashData;
+ orderExpiryTimestamp: BigNumber;
+ networkId: number;
+ userAddress: string;
+ orderSignatureData: SignatureData;
+ orderTakerAddress: string;
+ orderSalt: BigNumber;
+ sideToAssetToken: SideToAssetToken;
+ tokenByAddress: TokenByAddress;
+ tokenStateByAddress: TokenStateByAddress;
}
interface GenerateOrderFormState {
- globalErrMsg: string;
- shouldShowIncompleteErrs: boolean;
- signingState: SigningState;
+ globalErrMsg: string;
+ shouldShowIncompleteErrs: boolean;
+ signingState: SigningState;
}
export class GenerateOrderForm extends React.Component<GenerateOrderFormProps, GenerateOrderFormState> {
- private _validator: SchemaValidator;
- constructor(props: GenerateOrderFormProps) {
- super(props);
- this.state = {
- globalErrMsg: '',
- shouldShowIncompleteErrs: false,
- signingState: SigningState.UNSIGNED,
- };
- this._validator = new SchemaValidator();
- }
- public componentDidMount() {
- window.scrollTo(0, 0);
- }
- public render() {
- const dispatcher = this.props.dispatcher;
- const depositTokenAddress = this.props.sideToAssetToken[Side.Deposit].address;
- const depositToken = this.props.tokenByAddress[depositTokenAddress];
- const depositTokenState = this.props.tokenStateByAddress[depositTokenAddress];
- const receiveTokenAddress = this.props.sideToAssetToken[Side.Receive].address;
- const receiveToken = this.props.tokenByAddress[receiveTokenAddress];
- const receiveTokenState = this.props.tokenStateByAddress[receiveTokenAddress];
- const takerExplanation =
- 'If a taker is specified, only they are<br> \
+ private _validator: SchemaValidator;
+ constructor(props: GenerateOrderFormProps) {
+ super(props);
+ this.state = {
+ globalErrMsg: '',
+ shouldShowIncompleteErrs: false,
+ signingState: SigningState.UNSIGNED,
+ };
+ this._validator = new SchemaValidator();
+ }
+ public componentDidMount() {
+ window.scrollTo(0, 0);
+ }
+ public render() {
+ const dispatcher = this.props.dispatcher;
+ const depositTokenAddress = this.props.sideToAssetToken[Side.Deposit].address;
+ const depositToken = this.props.tokenByAddress[depositTokenAddress];
+ const depositTokenState = this.props.tokenStateByAddress[depositTokenAddress];
+ const receiveTokenAddress = this.props.sideToAssetToken[Side.Receive].address;
+ const receiveToken = this.props.tokenByAddress[receiveTokenAddress];
+ const receiveTokenState = this.props.tokenStateByAddress[receiveTokenAddress];
+ const takerExplanation =
+ 'If a taker is specified, only they are<br> \
allowed to fill this order. If no taker is<br> \
specified, anyone is able to fill it.';
- const exchangeContractIfExists = this.props.blockchain.getExchangeContractAddressIfExists();
- return (
- <div className="clearfix mb2 lg-px4 md-px4 sm-px2">
- <h3>Generate an order</h3>
- <Divider />
- <div className="mx-auto" style={{ maxWidth: 580 }}>
- <div className="pt3">
- <div className="mx-auto clearfix">
- <div className="lg-col md-col lg-col-5 md-col-5 sm-col sm-col-5 sm-pb2">
- <TokenInput
- userAddress={this.props.userAddress}
- blockchain={this.props.blockchain}
- blockchainErr={this.props.blockchainErr}
- dispatcher={this.props.dispatcher}
- label="Selling"
- side={Side.Deposit}
- networkId={this.props.networkId}
- assetToken={this.props.sideToAssetToken[Side.Deposit]}
- updateChosenAssetToken={dispatcher.updateChosenAssetToken.bind(dispatcher)}
- tokenByAddress={this.props.tokenByAddress}
- />
- <TokenAmountInput
- label="Sell amount"
- token={depositToken}
- tokenState={depositTokenState}
- amount={this.props.sideToAssetToken[Side.Deposit].amount}
- onChange={this._onTokenAmountChange.bind(this, depositToken, Side.Deposit)}
- shouldShowIncompleteErrs={this.state.shouldShowIncompleteErrs}
- shouldCheckBalance={true}
- shouldCheckAllowance={true}
- />
- </div>
- <div className="lg-col md-col lg-col-2 md-col-2 sm-col sm-col-2 xs-hide">
- <div className="p1">
- <SwapIcon swapTokensFn={dispatcher.swapAssetTokenSymbols.bind(dispatcher)} />
- </div>
- </div>
- <div className="lg-col md-col lg-col-5 md-col-5 sm-col sm-col-5 sm-pb2">
- <TokenInput
- userAddress={this.props.userAddress}
- blockchain={this.props.blockchain}
- blockchainErr={this.props.blockchainErr}
- dispatcher={this.props.dispatcher}
- label="Buying"
- side={Side.Receive}
- networkId={this.props.networkId}
- assetToken={this.props.sideToAssetToken[Side.Receive]}
- updateChosenAssetToken={dispatcher.updateChosenAssetToken.bind(dispatcher)}
- tokenByAddress={this.props.tokenByAddress}
- />
- <TokenAmountInput
- label="Receive amount"
- token={receiveToken}
- tokenState={receiveTokenState}
- amount={this.props.sideToAssetToken[Side.Receive].amount}
- onChange={this._onTokenAmountChange.bind(this, receiveToken, Side.Receive)}
- shouldShowIncompleteErrs={this.state.shouldShowIncompleteErrs}
- shouldCheckBalance={false}
- shouldCheckAllowance={false}
- />
- </div>
- </div>
- </div>
- <div className="pt1 sm-pb2 lg-px4 md-px4">
- <div className="lg-px3 md-px3">
- <div style={{ fontSize: 12, color: colors.grey }}>Expiration</div>
- <ExpirationInput
- orderExpiryTimestamp={this.props.orderExpiryTimestamp}
- updateOrderExpiry={dispatcher.updateOrderExpiry.bind(dispatcher)}
- />
- </div>
- </div>
- <div className="pt1 flex mx-auto">
- <IdenticonAddressInput
- label="Taker"
- initialAddress={this.props.orderTakerAddress}
- updateOrderAddress={this._updateOrderAddress.bind(this)}
- />
- <div className="pt3">
- <div className="pl1">
- <HelpTooltip explanation={takerExplanation} />
- </div>
- </div>
- </div>
- <div>
- <HashInput
- blockchain={this.props.blockchain}
- blockchainIsLoaded={this.props.blockchainIsLoaded}
- hashData={this.props.hashData}
- label="Order Hash"
- />
- </div>
- <div className="pt2">
- <div className="center">
- <LifeCycleRaisedButton
- labelReady="Sign hash"
- labelLoading="Signing..."
- labelComplete="Hash signed!"
- onClickAsyncFn={this._onSignClickedAsync.bind(this)}
- />
- </div>
- {this.state.globalErrMsg !== '' && (
- <Alert type={AlertTypes.ERROR} message={this.state.globalErrMsg} />
- )}
- </div>
- </div>
- <Dialog
- title="Order JSON"
- titleStyle={{ fontWeight: 100 }}
- modal={false}
- open={this.state.signingState === SigningState.SIGNED}
- onRequestClose={this._onCloseOrderJSONDialog.bind(this)}
- >
- <OrderJSON
- exchangeContractIfExists={exchangeContractIfExists}
- orderExpiryTimestamp={this.props.orderExpiryTimestamp}
- orderSignatureData={this.props.orderSignatureData}
- orderTakerAddress={this.props.orderTakerAddress}
- orderMakerAddress={this.props.userAddress}
- orderSalt={this.props.orderSalt}
- orderMakerFee={this.props.hashData.makerFee}
- orderTakerFee={this.props.hashData.takerFee}
- orderFeeRecipient={this.props.hashData.feeRecipientAddress}
- networkId={this.props.networkId}
- sideToAssetToken={this.props.sideToAssetToken}
- tokenByAddress={this.props.tokenByAddress}
- />
- </Dialog>
- </div>
- );
- }
- private _onTokenAmountChange(token: Token, side: Side, isValid: boolean, amount?: BigNumber) {
- this.props.dispatcher.updateChosenAssetToken(side, {
- address: token.address,
- amount,
- });
- }
- private _onCloseOrderJSONDialog() {
- // Upon closing the order JSON dialog, we update the orderSalt stored in the Redux store
- // with a new value so that if a user signs the identical order again, the newly signed
- // orderHash will not collide with the previously generated orderHash.
- this.props.dispatcher.updateOrderSalt(ZeroEx.generatePseudoRandomSalt());
- this.setState({
- signingState: SigningState.UNSIGNED,
- });
- }
- private async _onSignClickedAsync(): Promise<boolean> {
- if (this.props.blockchainErr !== BlockchainErrs.NoError) {
- this.props.dispatcher.updateShouldBlockchainErrDialogBeOpen(true);
- return false;
- }
+ const exchangeContractIfExists = this.props.blockchain.getExchangeContractAddressIfExists();
+ return (
+ <div className="clearfix mb2 lg-px4 md-px4 sm-px2">
+ <h3>Generate an order</h3>
+ <Divider />
+ <div className="mx-auto" style={{ maxWidth: 580 }}>
+ <div className="pt3">
+ <div className="mx-auto clearfix">
+ <div className="lg-col md-col lg-col-5 md-col-5 sm-col sm-col-5 sm-pb2">
+ <TokenInput
+ userAddress={this.props.userAddress}
+ blockchain={this.props.blockchain}
+ blockchainErr={this.props.blockchainErr}
+ dispatcher={this.props.dispatcher}
+ label="Selling"
+ side={Side.Deposit}
+ networkId={this.props.networkId}
+ assetToken={this.props.sideToAssetToken[Side.Deposit]}
+ updateChosenAssetToken={dispatcher.updateChosenAssetToken.bind(dispatcher)}
+ tokenByAddress={this.props.tokenByAddress}
+ />
+ <TokenAmountInput
+ label="Sell amount"
+ token={depositToken}
+ tokenState={depositTokenState}
+ amount={this.props.sideToAssetToken[Side.Deposit].amount}
+ onChange={this._onTokenAmountChange.bind(this, depositToken, Side.Deposit)}
+ shouldShowIncompleteErrs={this.state.shouldShowIncompleteErrs}
+ shouldCheckBalance={true}
+ shouldCheckAllowance={true}
+ />
+ </div>
+ <div className="lg-col md-col lg-col-2 md-col-2 sm-col sm-col-2 xs-hide">
+ <div className="p1">
+ <SwapIcon swapTokensFn={dispatcher.swapAssetTokenSymbols.bind(dispatcher)} />
+ </div>
+ </div>
+ <div className="lg-col md-col lg-col-5 md-col-5 sm-col sm-col-5 sm-pb2">
+ <TokenInput
+ userAddress={this.props.userAddress}
+ blockchain={this.props.blockchain}
+ blockchainErr={this.props.blockchainErr}
+ dispatcher={this.props.dispatcher}
+ label="Buying"
+ side={Side.Receive}
+ networkId={this.props.networkId}
+ assetToken={this.props.sideToAssetToken[Side.Receive]}
+ updateChosenAssetToken={dispatcher.updateChosenAssetToken.bind(dispatcher)}
+ tokenByAddress={this.props.tokenByAddress}
+ />
+ <TokenAmountInput
+ label="Receive amount"
+ token={receiveToken}
+ tokenState={receiveTokenState}
+ amount={this.props.sideToAssetToken[Side.Receive].amount}
+ onChange={this._onTokenAmountChange.bind(this, receiveToken, Side.Receive)}
+ shouldShowIncompleteErrs={this.state.shouldShowIncompleteErrs}
+ shouldCheckBalance={false}
+ shouldCheckAllowance={false}
+ />
+ </div>
+ </div>
+ </div>
+ <div className="pt1 sm-pb2 lg-px4 md-px4">
+ <div className="lg-px3 md-px3">
+ <div style={{ fontSize: 12, color: colors.grey }}>Expiration</div>
+ <ExpirationInput
+ orderExpiryTimestamp={this.props.orderExpiryTimestamp}
+ updateOrderExpiry={dispatcher.updateOrderExpiry.bind(dispatcher)}
+ />
+ </div>
+ </div>
+ <div className="pt1 flex mx-auto">
+ <IdenticonAddressInput
+ label="Taker"
+ initialAddress={this.props.orderTakerAddress}
+ updateOrderAddress={this._updateOrderAddress.bind(this)}
+ />
+ <div className="pt3">
+ <div className="pl1">
+ <HelpTooltip explanation={takerExplanation} />
+ </div>
+ </div>
+ </div>
+ <div>
+ <HashInput
+ blockchain={this.props.blockchain}
+ blockchainIsLoaded={this.props.blockchainIsLoaded}
+ hashData={this.props.hashData}
+ label="Order Hash"
+ />
+ </div>
+ <div className="pt2">
+ <div className="center">
+ <LifeCycleRaisedButton
+ labelReady="Sign hash"
+ labelLoading="Signing..."
+ labelComplete="Hash signed!"
+ onClickAsyncFn={this._onSignClickedAsync.bind(this)}
+ />
+ </div>
+ {this.state.globalErrMsg !== '' && (
+ <Alert type={AlertTypes.ERROR} message={this.state.globalErrMsg} />
+ )}
+ </div>
+ </div>
+ <Dialog
+ title="Order JSON"
+ titleStyle={{ fontWeight: 100 }}
+ modal={false}
+ open={this.state.signingState === SigningState.SIGNED}
+ onRequestClose={this._onCloseOrderJSONDialog.bind(this)}
+ >
+ <OrderJSON
+ exchangeContractIfExists={exchangeContractIfExists}
+ orderExpiryTimestamp={this.props.orderExpiryTimestamp}
+ orderSignatureData={this.props.orderSignatureData}
+ orderTakerAddress={this.props.orderTakerAddress}
+ orderMakerAddress={this.props.userAddress}
+ orderSalt={this.props.orderSalt}
+ orderMakerFee={this.props.hashData.makerFee}
+ orderTakerFee={this.props.hashData.takerFee}
+ orderFeeRecipient={this.props.hashData.feeRecipientAddress}
+ networkId={this.props.networkId}
+ sideToAssetToken={this.props.sideToAssetToken}
+ tokenByAddress={this.props.tokenByAddress}
+ />
+ </Dialog>
+ </div>
+ );
+ }
+ private _onTokenAmountChange(token: Token, side: Side, isValid: boolean, amount?: BigNumber) {
+ this.props.dispatcher.updateChosenAssetToken(side, {
+ address: token.address,
+ amount,
+ });
+ }
+ private _onCloseOrderJSONDialog() {
+ // Upon closing the order JSON dialog, we update the orderSalt stored in the Redux store
+ // with a new value so that if a user signs the identical order again, the newly signed
+ // orderHash will not collide with the previously generated orderHash.
+ this.props.dispatcher.updateOrderSalt(ZeroEx.generatePseudoRandomSalt());
+ this.setState({
+ signingState: SigningState.UNSIGNED,
+ });
+ }
+ private async _onSignClickedAsync(): Promise<boolean> {
+ if (this.props.blockchainErr !== BlockchainErrs.NoError) {
+ this.props.dispatcher.updateShouldBlockchainErrDialogBeOpen(true);
+ return false;
+ }
- // Check if all required inputs were supplied
- const debitToken = this.props.sideToAssetToken[Side.Deposit];
- const debitBalance = this.props.tokenStateByAddress[debitToken.address].balance;
- const debitAllowance = this.props.tokenStateByAddress[debitToken.address].allowance;
- const receiveAmount = this.props.sideToAssetToken[Side.Receive].amount;
- if (
- !_.isUndefined(debitToken.amount) &&
- !_.isUndefined(receiveAmount) &&
- debitToken.amount.gt(0) &&
- receiveAmount.gt(0) &&
- this.props.userAddress !== '' &&
- debitBalance.gte(debitToken.amount) &&
- debitAllowance.gte(debitToken.amount)
- ) {
- const didSignSuccessfully = await this._signTransactionAsync();
- if (didSignSuccessfully) {
- this.setState({
- globalErrMsg: '',
- shouldShowIncompleteErrs: false,
- });
- }
- return didSignSuccessfully;
- } else {
- let globalErrMsg = 'You must fix the above errors in order to generate a valid order';
- if (this.props.userAddress === '') {
- globalErrMsg = 'You must enable wallet communication';
- this.props.dispatcher.updateShouldBlockchainErrDialogBeOpen(true);
- }
- this.setState({
- globalErrMsg,
- shouldShowIncompleteErrs: true,
- });
- return false;
- }
- }
- private async _signTransactionAsync(): Promise<boolean> {
- this.setState({
- signingState: SigningState.SIGNING,
- });
- const exchangeContractAddr = this.props.blockchain.getExchangeContractAddressIfExists();
- if (_.isUndefined(exchangeContractAddr)) {
- this.props.dispatcher.updateShouldBlockchainErrDialogBeOpen(true);
- this.setState({
- signingState: SigningState.UNSIGNED,
- });
- return false;
- }
- const hashData = this.props.hashData;
+ // Check if all required inputs were supplied
+ const debitToken = this.props.sideToAssetToken[Side.Deposit];
+ const debitBalance = this.props.tokenStateByAddress[debitToken.address].balance;
+ const debitAllowance = this.props.tokenStateByAddress[debitToken.address].allowance;
+ const receiveAmount = this.props.sideToAssetToken[Side.Receive].amount;
+ if (
+ !_.isUndefined(debitToken.amount) &&
+ !_.isUndefined(receiveAmount) &&
+ debitToken.amount.gt(0) &&
+ receiveAmount.gt(0) &&
+ this.props.userAddress !== '' &&
+ debitBalance.gte(debitToken.amount) &&
+ debitAllowance.gte(debitToken.amount)
+ ) {
+ const didSignSuccessfully = await this._signTransactionAsync();
+ if (didSignSuccessfully) {
+ this.setState({
+ globalErrMsg: '',
+ shouldShowIncompleteErrs: false,
+ });
+ }
+ return didSignSuccessfully;
+ } else {
+ let globalErrMsg = 'You must fix the above errors in order to generate a valid order';
+ if (this.props.userAddress === '') {
+ globalErrMsg = 'You must enable wallet communication';
+ this.props.dispatcher.updateShouldBlockchainErrDialogBeOpen(true);
+ }
+ this.setState({
+ globalErrMsg,
+ shouldShowIncompleteErrs: true,
+ });
+ return false;
+ }
+ }
+ private async _signTransactionAsync(): Promise<boolean> {
+ this.setState({
+ signingState: SigningState.SIGNING,
+ });
+ const exchangeContractAddr = this.props.blockchain.getExchangeContractAddressIfExists();
+ if (_.isUndefined(exchangeContractAddr)) {
+ this.props.dispatcher.updateShouldBlockchainErrDialogBeOpen(true);
+ this.setState({
+ signingState: SigningState.UNSIGNED,
+ });
+ return false;
+ }
+ const hashData = this.props.hashData;
- const zeroExOrder: Order = {
- exchangeContractAddress: exchangeContractAddr,
- expirationUnixTimestampSec: hashData.orderExpiryTimestamp,
- feeRecipient: hashData.feeRecipientAddress,
- maker: hashData.orderMakerAddress,
- makerFee: hashData.makerFee,
- makerTokenAddress: hashData.depositTokenContractAddr,
- makerTokenAmount: hashData.depositAmount,
- salt: hashData.orderSalt,
- taker: hashData.orderTakerAddress,
- takerFee: hashData.takerFee,
- takerTokenAddress: hashData.receiveTokenContractAddr,
- takerTokenAmount: hashData.receiveAmount,
- };
- const orderHash = ZeroEx.getOrderHashHex(zeroExOrder);
+ const zeroExOrder: Order = {
+ exchangeContractAddress: exchangeContractAddr,
+ expirationUnixTimestampSec: hashData.orderExpiryTimestamp,
+ feeRecipient: hashData.feeRecipientAddress,
+ maker: hashData.orderMakerAddress,
+ makerFee: hashData.makerFee,
+ makerTokenAddress: hashData.depositTokenContractAddr,
+ makerTokenAmount: hashData.depositAmount,
+ salt: hashData.orderSalt,
+ taker: hashData.orderTakerAddress,
+ takerFee: hashData.takerFee,
+ takerTokenAddress: hashData.receiveTokenContractAddr,
+ takerTokenAmount: hashData.receiveAmount,
+ };
+ const orderHash = ZeroEx.getOrderHashHex(zeroExOrder);
- let globalErrMsg = '';
- try {
- const signatureData = await this.props.blockchain.signOrderHashAsync(orderHash);
- const order = utils.generateOrder(
- this.props.networkId,
- exchangeContractAddr,
- this.props.sideToAssetToken,
- hashData.orderExpiryTimestamp,
- this.props.orderTakerAddress,
- this.props.userAddress,
- hashData.makerFee,
- hashData.takerFee,
- hashData.feeRecipientAddress,
- signatureData,
- this.props.tokenByAddress,
- hashData.orderSalt,
- );
- const validationResult = this._validator.validate(order, orderSchema);
- if (validationResult.errors.length > 0) {
- globalErrMsg = 'Order signing failed. Please refresh and try again';
- utils.consoleLog(`Unexpected error occured: Order validation failed:
+ let globalErrMsg = '';
+ try {
+ const signatureData = await this.props.blockchain.signOrderHashAsync(orderHash);
+ const order = utils.generateOrder(
+ this.props.networkId,
+ exchangeContractAddr,
+ this.props.sideToAssetToken,
+ hashData.orderExpiryTimestamp,
+ this.props.orderTakerAddress,
+ this.props.userAddress,
+ hashData.makerFee,
+ hashData.takerFee,
+ hashData.feeRecipientAddress,
+ signatureData,
+ this.props.tokenByAddress,
+ hashData.orderSalt,
+ );
+ const validationResult = this._validator.validate(order, orderSchema);
+ if (validationResult.errors.length > 0) {
+ globalErrMsg = 'Order signing failed. Please refresh and try again';
+ utils.consoleLog(`Unexpected error occured: Order validation failed:
${validationResult.errors}`);
- }
- } catch (err) {
- const errMsg = `${err}`;
- if (utils.didUserDenyWeb3Request(errMsg)) {
- globalErrMsg = 'User denied sign request';
- } else {
- globalErrMsg = 'An unexpected error occured. Please try refreshing the page';
- utils.consoleLog(`Unexpected error occured: ${err}`);
- utils.consoleLog(err.stack);
- await errorReporter.reportAsync(err);
- }
- }
- this.setState({
- signingState: globalErrMsg === '' ? SigningState.SIGNED : SigningState.UNSIGNED,
- globalErrMsg,
- });
- return globalErrMsg === '';
- }
- private _updateOrderAddress(address?: string): void {
- if (!_.isUndefined(address)) {
- this.props.dispatcher.updateOrderTakerAddress(address);
- }
- }
+ }
+ } catch (err) {
+ const errMsg = `${err}`;
+ if (utils.didUserDenyWeb3Request(errMsg)) {
+ globalErrMsg = 'User denied sign request';
+ } else {
+ globalErrMsg = 'An unexpected error occured. Please try refreshing the page';
+ utils.consoleLog(`Unexpected error occured: ${err}`);
+ utils.consoleLog(err.stack);
+ await errorReporter.reportAsync(err);
+ }
+ }
+ this.setState({
+ signingState: globalErrMsg === '' ? SigningState.SIGNED : SigningState.UNSIGNED,
+ globalErrMsg,
+ });
+ return globalErrMsg === '';
+ }
+ private _updateOrderAddress(address?: string): void {
+ if (!_.isUndefined(address)) {
+ this.props.dispatcher.updateOrderTakerAddress(address);
+ }
+ }
}
diff --git a/packages/website/ts/components/generate_order/new_token_form.tsx b/packages/website/ts/components/generate_order/new_token_form.tsx
index d61aac92a..63645be9a 100644
--- a/packages/website/ts/components/generate_order/new_token_form.tsx
+++ b/packages/website/ts/components/generate_order/new_token_form.tsx
@@ -11,227 +11,227 @@ import { AlertTypes, Token, TokenByAddress, TokenState } from 'ts/types';
import { colors } from 'ts/utils/colors';
interface NewTokenFormProps {
- blockchain: Blockchain;
- tokenByAddress: TokenByAddress;
- onNewTokenSubmitted: (token: Token, tokenState: TokenState) => void;
+ blockchain: Blockchain;
+ tokenByAddress: TokenByAddress;
+ onNewTokenSubmitted: (token: Token, tokenState: TokenState) => void;
}
interface NewTokenFormState {
- globalErrMsg: string;
- name: string;
- nameErrText: string;
- symbol: string;
- symbolErrText: string;
- address: string;
- shouldShowAddressIncompleteErr: boolean;
- decimals: string;
- decimalsErrText: string;
+ globalErrMsg: string;
+ name: string;
+ nameErrText: string;
+ symbol: string;
+ symbolErrText: string;
+ address: string;
+ shouldShowAddressIncompleteErr: boolean;
+ decimals: string;
+ decimalsErrText: string;
}
export class NewTokenForm extends React.Component<NewTokenFormProps, NewTokenFormState> {
- constructor(props: NewTokenFormProps) {
- super(props);
- this.state = {
- address: '',
- globalErrMsg: '',
- name: '',
- nameErrText: '',
- shouldShowAddressIncompleteErr: false,
- symbol: '',
- symbolErrText: '',
- decimals: '18',
- decimalsErrText: '',
- };
- }
- public render() {
- return (
- <div className="mx-auto pb2" style={{ width: 256 }}>
- <div>
- <TextField
- floatingLabelFixed={true}
- floatingLabelStyle={{ color: colors.grey }}
- floatingLabelText={<RequiredLabel label="Name" />}
- value={this.state.name}
- errorText={this.state.nameErrText}
- onChange={this._onTokenNameChanged.bind(this)}
- />
- </div>
- <div>
- <TextField
- floatingLabelFixed={true}
- floatingLabelStyle={{ color: colors.grey }}
- floatingLabelText={<RequiredLabel label="Symbol" />}
- value={this.state.symbol}
- errorText={this.state.symbolErrText}
- onChange={this._onTokenSymbolChanged.bind(this)}
- />
- </div>
- <div>
- <AddressInput
- isRequired={true}
- label="Contract address"
- initialAddress=""
- shouldShowIncompleteErrs={this.state.shouldShowAddressIncompleteErr}
- updateAddress={this._onTokenAddressChanged.bind(this)}
- />
- </div>
- <div>
- <TextField
- floatingLabelFixed={true}
- floatingLabelStyle={{ color: colors.grey }}
- floatingLabelText={<RequiredLabel label="Decimals" />}
- value={this.state.decimals}
- errorText={this.state.decimalsErrText}
- onChange={this._onTokenDecimalsChanged.bind(this)}
- />
- </div>
- <div className="pt2 mx-auto" style={{ width: 120 }}>
- <LifeCycleRaisedButton
- labelReady="Add"
- labelLoading="Adding..."
- labelComplete="Added!"
- onClickAsyncFn={this._onAddNewTokenClickAsync.bind(this)}
- />
- </div>
- {this.state.globalErrMsg !== '' && <Alert type={AlertTypes.ERROR} message={this.state.globalErrMsg} />}
- </div>
- );
- }
- private async _onAddNewTokenClickAsync() {
- // Trigger validation of name and symbol
- this._onTokenNameChanged(undefined, this.state.name);
- this._onTokenSymbolChanged(undefined, this.state.symbol);
- this._onTokenDecimalsChanged(undefined, this.state.decimals);
+ constructor(props: NewTokenFormProps) {
+ super(props);
+ this.state = {
+ address: '',
+ globalErrMsg: '',
+ name: '',
+ nameErrText: '',
+ shouldShowAddressIncompleteErr: false,
+ symbol: '',
+ symbolErrText: '',
+ decimals: '18',
+ decimalsErrText: '',
+ };
+ }
+ public render() {
+ return (
+ <div className="mx-auto pb2" style={{ width: 256 }}>
+ <div>
+ <TextField
+ floatingLabelFixed={true}
+ floatingLabelStyle={{ color: colors.grey }}
+ floatingLabelText={<RequiredLabel label="Name" />}
+ value={this.state.name}
+ errorText={this.state.nameErrText}
+ onChange={this._onTokenNameChanged.bind(this)}
+ />
+ </div>
+ <div>
+ <TextField
+ floatingLabelFixed={true}
+ floatingLabelStyle={{ color: colors.grey }}
+ floatingLabelText={<RequiredLabel label="Symbol" />}
+ value={this.state.symbol}
+ errorText={this.state.symbolErrText}
+ onChange={this._onTokenSymbolChanged.bind(this)}
+ />
+ </div>
+ <div>
+ <AddressInput
+ isRequired={true}
+ label="Contract address"
+ initialAddress=""
+ shouldShowIncompleteErrs={this.state.shouldShowAddressIncompleteErr}
+ updateAddress={this._onTokenAddressChanged.bind(this)}
+ />
+ </div>
+ <div>
+ <TextField
+ floatingLabelFixed={true}
+ floatingLabelStyle={{ color: colors.grey }}
+ floatingLabelText={<RequiredLabel label="Decimals" />}
+ value={this.state.decimals}
+ errorText={this.state.decimalsErrText}
+ onChange={this._onTokenDecimalsChanged.bind(this)}
+ />
+ </div>
+ <div className="pt2 mx-auto" style={{ width: 120 }}>
+ <LifeCycleRaisedButton
+ labelReady="Add"
+ labelLoading="Adding..."
+ labelComplete="Added!"
+ onClickAsyncFn={this._onAddNewTokenClickAsync.bind(this)}
+ />
+ </div>
+ {this.state.globalErrMsg !== '' && <Alert type={AlertTypes.ERROR} message={this.state.globalErrMsg} />}
+ </div>
+ );
+ }
+ private async _onAddNewTokenClickAsync() {
+ // Trigger validation of name and symbol
+ this._onTokenNameChanged(undefined, this.state.name);
+ this._onTokenSymbolChanged(undefined, this.state.symbol);
+ this._onTokenDecimalsChanged(undefined, this.state.decimals);
- const isAddressIncomplete = this.state.address === '';
- let doesContractExist = false;
- if (!isAddressIncomplete) {
- doesContractExist = await this.props.blockchain.doesContractExistAtAddressAsync(this.state.address);
- }
+ const isAddressIncomplete = this.state.address === '';
+ let doesContractExist = false;
+ if (!isAddressIncomplete) {
+ doesContractExist = await this.props.blockchain.doesContractExistAtAddressAsync(this.state.address);
+ }
- let hasBalanceAllowanceErr = false;
- let balance = new BigNumber(0);
- let allowance = new BigNumber(0);
- if (doesContractExist) {
- try {
- [balance, allowance] = await this.props.blockchain.getCurrentUserTokenBalanceAndAllowanceAsync(
- this.state.address,
- );
- } catch (err) {
- hasBalanceAllowanceErr = true;
- }
- }
+ let hasBalanceAllowanceErr = false;
+ let balance = new BigNumber(0);
+ let allowance = new BigNumber(0);
+ if (doesContractExist) {
+ try {
+ [balance, allowance] = await this.props.blockchain.getCurrentUserTokenBalanceAndAllowanceAsync(
+ this.state.address,
+ );
+ } catch (err) {
+ hasBalanceAllowanceErr = true;
+ }
+ }
- let globalErrMsg = '';
- if (
- this.state.nameErrText !== '' ||
- this.state.symbolErrText !== '' ||
- this.state.decimalsErrText !== '' ||
- isAddressIncomplete
- ) {
- globalErrMsg = 'Please fix the above issues';
- } else if (!doesContractExist) {
- globalErrMsg = 'No contract found at supplied address';
- } else if (hasBalanceAllowanceErr) {
- globalErrMsg = 'Unsuccessful call to `balanceOf` and/or `allowance` on supplied contract address';
- } else if (!isAddressIncomplete && !_.isUndefined(this.props.tokenByAddress[this.state.address])) {
- globalErrMsg = 'A token already exists with this address';
- }
+ let globalErrMsg = '';
+ if (
+ this.state.nameErrText !== '' ||
+ this.state.symbolErrText !== '' ||
+ this.state.decimalsErrText !== '' ||
+ isAddressIncomplete
+ ) {
+ globalErrMsg = 'Please fix the above issues';
+ } else if (!doesContractExist) {
+ globalErrMsg = 'No contract found at supplied address';
+ } else if (hasBalanceAllowanceErr) {
+ globalErrMsg = 'Unsuccessful call to `balanceOf` and/or `allowance` on supplied contract address';
+ } else if (!isAddressIncomplete && !_.isUndefined(this.props.tokenByAddress[this.state.address])) {
+ globalErrMsg = 'A token already exists with this address';
+ }
- if (globalErrMsg !== '') {
- this.setState({
- globalErrMsg,
- shouldShowAddressIncompleteErr: isAddressIncomplete,
- });
- return;
- }
+ if (globalErrMsg !== '') {
+ this.setState({
+ globalErrMsg,
+ shouldShowAddressIncompleteErr: isAddressIncomplete,
+ });
+ return;
+ }
- const newToken: Token = {
- address: this.state.address,
- decimals: _.parseInt(this.state.decimals),
- iconUrl: undefined,
- name: this.state.name,
- symbol: this.state.symbol.toUpperCase(),
- isTracked: true,
- isRegistered: false,
- };
- const newTokenState: TokenState = {
- balance,
- allowance,
- };
- this.props.onNewTokenSubmitted(newToken, newTokenState);
- }
- private _onTokenNameChanged(e: any, name: string) {
- let nameErrText = '';
- const maxLength = 30;
- const tokens = _.values(this.props.tokenByAddress);
- const tokenWithNameIfExists = _.find(tokens, { name });
- const tokenWithNameExists = !_.isUndefined(tokenWithNameIfExists);
- if (name === '') {
- nameErrText = 'Name is required';
- } else if (!this._isValidName(name)) {
- nameErrText = 'Name should only contain letters, digits and spaces';
- } else if (name.length > maxLength) {
- nameErrText = `Max length is ${maxLength}`;
- } else if (tokenWithNameExists) {
- nameErrText = 'Token with this name already exists';
- }
+ const newToken: Token = {
+ address: this.state.address,
+ decimals: _.parseInt(this.state.decimals),
+ iconUrl: undefined,
+ name: this.state.name,
+ symbol: this.state.symbol.toUpperCase(),
+ isTracked: true,
+ isRegistered: false,
+ };
+ const newTokenState: TokenState = {
+ balance,
+ allowance,
+ };
+ this.props.onNewTokenSubmitted(newToken, newTokenState);
+ }
+ private _onTokenNameChanged(e: any, name: string) {
+ let nameErrText = '';
+ const maxLength = 30;
+ const tokens = _.values(this.props.tokenByAddress);
+ const tokenWithNameIfExists = _.find(tokens, { name });
+ const tokenWithNameExists = !_.isUndefined(tokenWithNameIfExists);
+ if (name === '') {
+ nameErrText = 'Name is required';
+ } else if (!this._isValidName(name)) {
+ nameErrText = 'Name should only contain letters, digits and spaces';
+ } else if (name.length > maxLength) {
+ nameErrText = `Max length is ${maxLength}`;
+ } else if (tokenWithNameExists) {
+ nameErrText = 'Token with this name already exists';
+ }
- this.setState({
- name,
- nameErrText,
- });
- }
- private _onTokenSymbolChanged(e: any, symbol: string) {
- let symbolErrText = '';
- const maxLength = 5;
- const tokens = _.values(this.props.tokenByAddress);
- const tokenWithSymbolExists = !_.isUndefined(_.find(tokens, { symbol }));
- if (symbol === '') {
- symbolErrText = 'Symbol is required';
- } else if (!this._isAlphanumeric(symbol)) {
- symbolErrText = 'Can only include alphanumeric characters';
- } else if (symbol.length > maxLength) {
- symbolErrText = `Max length is ${maxLength}`;
- } else if (tokenWithSymbolExists) {
- symbolErrText = 'Token with symbol already exists';
- }
+ this.setState({
+ name,
+ nameErrText,
+ });
+ }
+ private _onTokenSymbolChanged(e: any, symbol: string) {
+ let symbolErrText = '';
+ const maxLength = 5;
+ const tokens = _.values(this.props.tokenByAddress);
+ const tokenWithSymbolExists = !_.isUndefined(_.find(tokens, { symbol }));
+ if (symbol === '') {
+ symbolErrText = 'Symbol is required';
+ } else if (!this._isAlphanumeric(symbol)) {
+ symbolErrText = 'Can only include alphanumeric characters';
+ } else if (symbol.length > maxLength) {
+ symbolErrText = `Max length is ${maxLength}`;
+ } else if (tokenWithSymbolExists) {
+ symbolErrText = 'Token with symbol already exists';
+ }
- this.setState({
- symbol,
- symbolErrText,
- });
- }
- private _onTokenDecimalsChanged(e: any, decimals: string) {
- let decimalsErrText = '';
- const maxLength = 2;
- if (decimals === '') {
- decimalsErrText = 'Decimals is required';
- } else if (!this._isInteger(decimals)) {
- decimalsErrText = 'Must be an integer';
- } else if (decimals.length > maxLength) {
- decimalsErrText = `Max length is ${maxLength}`;
- }
+ this.setState({
+ symbol,
+ symbolErrText,
+ });
+ }
+ private _onTokenDecimalsChanged(e: any, decimals: string) {
+ let decimalsErrText = '';
+ const maxLength = 2;
+ if (decimals === '') {
+ decimalsErrText = 'Decimals is required';
+ } else if (!this._isInteger(decimals)) {
+ decimalsErrText = 'Must be an integer';
+ } else if (decimals.length > maxLength) {
+ decimalsErrText = `Max length is ${maxLength}`;
+ }
- this.setState({
- decimals,
- decimalsErrText,
- });
- }
- private _onTokenAddressChanged(address?: string) {
- if (!_.isUndefined(address)) {
- this.setState({
- address,
- });
- }
- }
- private _isValidName(input: string) {
- return /^[a-z0-9 ]+$/i.test(input);
- }
- private _isInteger(input: string) {
- return /^[0-9]+$/i.test(input);
- }
- private _isAlphanumeric(input: string) {
- return /^[a-zA-Z0-9]+$/i.test(input);
- }
+ this.setState({
+ decimals,
+ decimalsErrText,
+ });
+ }
+ private _onTokenAddressChanged(address?: string) {
+ if (!_.isUndefined(address)) {
+ this.setState({
+ address,
+ });
+ }
+ }
+ private _isValidName(input: string) {
+ return /^[a-z0-9 ]+$/i.test(input);
+ }
+ private _isInteger(input: string) {
+ return /^[0-9]+$/i.test(input);
+ }
+ private _isAlphanumeric(input: string) {
+ return /^[a-zA-Z0-9]+$/i.test(input);
+ }
}
diff --git a/packages/website/ts/components/inputs/address_input.tsx b/packages/website/ts/components/inputs/address_input.tsx
index 236bf9a00..dd4131140 100644
--- a/packages/website/ts/components/inputs/address_input.tsx
+++ b/packages/website/ts/components/inputs/address_input.tsx
@@ -6,66 +6,66 @@ import { RequiredLabel } from 'ts/components/ui/required_label';
import { colors } from 'ts/utils/colors';
interface AddressInputProps {
- disabled?: boolean;
- initialAddress: string;
- isRequired?: boolean;
- hintText?: string;
- shouldHideLabel?: boolean;
- label?: string;
- shouldShowIncompleteErrs?: boolean;
- updateAddress: (address?: string) => void;
+ disabled?: boolean;
+ initialAddress: string;
+ isRequired?: boolean;
+ hintText?: string;
+ shouldHideLabel?: boolean;
+ label?: string;
+ shouldShowIncompleteErrs?: boolean;
+ updateAddress: (address?: string) => void;
}
interface AddressInputState {
- address: string;
- errMsg: string;
+ address: string;
+ errMsg: string;
}
export class AddressInput extends React.Component<AddressInputProps, AddressInputState> {
- constructor(props: AddressInputProps) {
- super(props);
- this.state = {
- address: this.props.initialAddress,
- errMsg: '',
- };
- }
- public componentWillReceiveProps(nextProps: AddressInputProps) {
- if (nextProps.shouldShowIncompleteErrs && this.props.isRequired && this.state.address === '') {
- this.setState({
- errMsg: 'Address is required',
- });
- }
- }
- public render() {
- const label = this.props.isRequired ? <RequiredLabel label={this.props.label} /> : this.props.label;
- const labelDisplay = this.props.shouldHideLabel ? 'hidden' : 'block';
- const hintText = this.props.hintText ? this.props.hintText : '';
- return (
- <div className="overflow-hidden">
- <TextField
- id={`address-field-${this.props.label}`}
- disabled={_.isUndefined(this.props.disabled) ? false : this.props.disabled}
- fullWidth={true}
- hintText={hintText}
- floatingLabelFixed={true}
- floatingLabelStyle={{ color: colors.grey, display: labelDisplay }}
- floatingLabelText={label}
- errorText={this.state.errMsg}
- value={this.state.address}
- onChange={this._onOrderTakerAddressUpdated.bind(this)}
- />
- </div>
- );
- }
- private _onOrderTakerAddressUpdated(e: any) {
- const address = e.target.value.toLowerCase();
- const isValidAddress = addressUtils.isAddress(address) || address === '';
- const errMsg = isValidAddress ? '' : 'Invalid ethereum address';
- this.setState({
- address,
- errMsg,
- });
- const addressIfValid = isValidAddress ? address : undefined;
- this.props.updateAddress(addressIfValid);
- }
+ constructor(props: AddressInputProps) {
+ super(props);
+ this.state = {
+ address: this.props.initialAddress,
+ errMsg: '',
+ };
+ }
+ public componentWillReceiveProps(nextProps: AddressInputProps) {
+ if (nextProps.shouldShowIncompleteErrs && this.props.isRequired && this.state.address === '') {
+ this.setState({
+ errMsg: 'Address is required',
+ });
+ }
+ }
+ public render() {
+ const label = this.props.isRequired ? <RequiredLabel label={this.props.label} /> : this.props.label;
+ const labelDisplay = this.props.shouldHideLabel ? 'hidden' : 'block';
+ const hintText = this.props.hintText ? this.props.hintText : '';
+ return (
+ <div className="overflow-hidden">
+ <TextField
+ id={`address-field-${this.props.label}`}
+ disabled={_.isUndefined(this.props.disabled) ? false : this.props.disabled}
+ fullWidth={true}
+ hintText={hintText}
+ floatingLabelFixed={true}
+ floatingLabelStyle={{ color: colors.grey, display: labelDisplay }}
+ floatingLabelText={label}
+ errorText={this.state.errMsg}
+ value={this.state.address}
+ onChange={this._onOrderTakerAddressUpdated.bind(this)}
+ />
+ </div>
+ );
+ }
+ private _onOrderTakerAddressUpdated(e: any) {
+ const address = e.target.value.toLowerCase();
+ const isValidAddress = addressUtils.isAddress(address) || address === '';
+ const errMsg = isValidAddress ? '' : 'Invalid ethereum address';
+ this.setState({
+ address,
+ errMsg,
+ });
+ const addressIfValid = isValidAddress ? address : undefined;
+ this.props.updateAddress(addressIfValid);
+ }
}
diff --git a/packages/website/ts/components/inputs/allowance_toggle.tsx b/packages/website/ts/components/inputs/allowance_toggle.tsx
index 245784824..da46db4f4 100644
--- a/packages/website/ts/components/inputs/allowance_toggle.tsx
+++ b/packages/website/ts/components/inputs/allowance_toggle.tsx
@@ -11,83 +11,83 @@ import { utils } from 'ts/utils/utils';
const DEFAULT_ALLOWANCE_AMOUNT_IN_BASE_UNITS = new BigNumber(2).pow(256).minus(1);
interface AllowanceToggleProps {
- blockchain: Blockchain;
- dispatcher: Dispatcher;
- onErrorOccurred: (errType: BalanceErrs) => void;
- token: Token;
- tokenState: TokenState;
- userAddress: string;
+ blockchain: Blockchain;
+ dispatcher: Dispatcher;
+ onErrorOccurred: (errType: BalanceErrs) => void;
+ token: Token;
+ tokenState: TokenState;
+ userAddress: string;
}
interface AllowanceToggleState {
- isSpinnerVisible: boolean;
- prevAllowance: BigNumber;
+ isSpinnerVisible: boolean;
+ prevAllowance: BigNumber;
}
export class AllowanceToggle extends React.Component<AllowanceToggleProps, AllowanceToggleState> {
- constructor(props: AllowanceToggleProps) {
- super(props);
- this.state = {
- isSpinnerVisible: false,
- prevAllowance: props.tokenState.allowance,
- };
- }
- public componentWillReceiveProps(nextProps: AllowanceToggleProps) {
- if (!nextProps.tokenState.allowance.eq(this.state.prevAllowance)) {
- this.setState({
- isSpinnerVisible: false,
- prevAllowance: nextProps.tokenState.allowance,
- });
- }
- }
- public render() {
- return (
- <div className="flex">
- <div>
- <Toggle
- disabled={this.state.isSpinnerVisible}
- toggled={this._isAllowanceSet()}
- onToggle={this._onToggleAllowanceAsync.bind(this)}
- />
- </div>
- {this.state.isSpinnerVisible && (
- <div className="pl1" style={{ paddingTop: 3 }}>
- <i className="zmdi zmdi-spinner zmdi-hc-spin" />
- </div>
- )}
- </div>
- );
- }
- private async _onToggleAllowanceAsync(): Promise<void> {
- if (this.props.userAddress === '') {
- this.props.dispatcher.updateShouldBlockchainErrDialogBeOpen(true);
- }
+ constructor(props: AllowanceToggleProps) {
+ super(props);
+ this.state = {
+ isSpinnerVisible: false,
+ prevAllowance: props.tokenState.allowance,
+ };
+ }
+ public componentWillReceiveProps(nextProps: AllowanceToggleProps) {
+ if (!nextProps.tokenState.allowance.eq(this.state.prevAllowance)) {
+ this.setState({
+ isSpinnerVisible: false,
+ prevAllowance: nextProps.tokenState.allowance,
+ });
+ }
+ }
+ public render() {
+ return (
+ <div className="flex">
+ <div>
+ <Toggle
+ disabled={this.state.isSpinnerVisible}
+ toggled={this._isAllowanceSet()}
+ onToggle={this._onToggleAllowanceAsync.bind(this)}
+ />
+ </div>
+ {this.state.isSpinnerVisible && (
+ <div className="pl1" style={{ paddingTop: 3 }}>
+ <i className="zmdi zmdi-spinner zmdi-hc-spin" />
+ </div>
+ )}
+ </div>
+ );
+ }
+ private async _onToggleAllowanceAsync(): Promise<void> {
+ if (this.props.userAddress === '') {
+ this.props.dispatcher.updateShouldBlockchainErrDialogBeOpen(true);
+ }
- this.setState({
- isSpinnerVisible: true,
- });
+ this.setState({
+ isSpinnerVisible: true,
+ });
- let newAllowanceAmountInBaseUnits = new BigNumber(0);
- if (!this._isAllowanceSet()) {
- newAllowanceAmountInBaseUnits = DEFAULT_ALLOWANCE_AMOUNT_IN_BASE_UNITS;
- }
- try {
- await this.props.blockchain.setProxyAllowanceAsync(this.props.token, newAllowanceAmountInBaseUnits);
- } catch (err) {
- this.setState({
- isSpinnerVisible: false,
- });
- const errMsg = `${err}`;
- if (_.includes(errMsg, 'User denied transaction')) {
- return;
- }
- utils.consoleLog(`Unexpected error encountered: ${err}`);
- utils.consoleLog(err.stack);
- this.props.onErrorOccurred(BalanceErrs.allowanceSettingFailed);
- await errorReporter.reportAsync(err);
- }
- }
- private _isAllowanceSet() {
- return !this.props.tokenState.allowance.eq(0);
- }
+ let newAllowanceAmountInBaseUnits = new BigNumber(0);
+ if (!this._isAllowanceSet()) {
+ newAllowanceAmountInBaseUnits = DEFAULT_ALLOWANCE_AMOUNT_IN_BASE_UNITS;
+ }
+ try {
+ await this.props.blockchain.setProxyAllowanceAsync(this.props.token, newAllowanceAmountInBaseUnits);
+ } catch (err) {
+ this.setState({
+ isSpinnerVisible: false,
+ });
+ const errMsg = `${err}`;
+ if (_.includes(errMsg, 'User denied transaction')) {
+ return;
+ }
+ utils.consoleLog(`Unexpected error encountered: ${err}`);
+ utils.consoleLog(err.stack);
+ this.props.onErrorOccurred(BalanceErrs.allowanceSettingFailed);
+ await errorReporter.reportAsync(err);
+ }
+ }
+ private _isAllowanceSet() {
+ return !this.props.tokenState.allowance.eq(0);
+ }
}
diff --git a/packages/website/ts/components/inputs/balance_bounded_input.tsx b/packages/website/ts/components/inputs/balance_bounded_input.tsx
index 5cc91994e..ddc434b51 100644
--- a/packages/website/ts/components/inputs/balance_bounded_input.tsx
+++ b/packages/website/ts/components/inputs/balance_bounded_input.tsx
@@ -9,143 +9,143 @@ import { colors } from 'ts/utils/colors';
import { utils } from 'ts/utils/utils';
interface BalanceBoundedInputProps {
- label?: string;
- balance: BigNumber;
- amount?: BigNumber;
- onChange: ValidatedBigNumberCallback;
- shouldShowIncompleteErrs?: boolean;
- shouldCheckBalance: boolean;
- validate?: (amount: BigNumber) => InputErrMsg;
- onVisitBalancesPageClick?: () => void;
- shouldHideVisitBalancesLink?: boolean;
+ label?: string;
+ balance: BigNumber;
+ amount?: BigNumber;
+ onChange: ValidatedBigNumberCallback;
+ shouldShowIncompleteErrs?: boolean;
+ shouldCheckBalance: boolean;
+ validate?: (amount: BigNumber) => InputErrMsg;
+ onVisitBalancesPageClick?: () => void;
+ shouldHideVisitBalancesLink?: boolean;
}
interface BalanceBoundedInputState {
- errMsg: InputErrMsg;
- amountString: string;
+ errMsg: InputErrMsg;
+ amountString: string;
}
export class BalanceBoundedInput extends React.Component<BalanceBoundedInputProps, BalanceBoundedInputState> {
- public static defaultProps: Partial<BalanceBoundedInputProps> = {
- shouldShowIncompleteErrs: false,
- shouldHideVisitBalancesLink: false,
- };
- constructor(props: BalanceBoundedInputProps) {
- super(props);
- const amountString = this.props.amount ? this.props.amount.toString() : '';
- this.state = {
- errMsg: this._validate(amountString, props.balance),
- amountString,
- };
- }
- public componentWillReceiveProps(nextProps: BalanceBoundedInputProps) {
- if (nextProps === this.props) {
- return;
- }
- const isCurrentAmountNumeric = utils.isNumeric(this.state.amountString);
- if (!_.isUndefined(nextProps.amount)) {
- let shouldResetState = false;
- if (!isCurrentAmountNumeric) {
- shouldResetState = true;
- } else {
- const currentAmount = new BigNumber(this.state.amountString);
- if (!currentAmount.eq(nextProps.amount) || !nextProps.balance.eq(this.props.balance)) {
- shouldResetState = true;
- }
- }
- if (shouldResetState) {
- const amountString = nextProps.amount.toString();
- this.setState({
- errMsg: this._validate(amountString, nextProps.balance),
- amountString,
- });
- }
- } else if (isCurrentAmountNumeric) {
- const amountString = '';
- this.setState({
- errMsg: this._validate(amountString, nextProps.balance),
- amountString,
- });
- }
- }
- public render() {
- let errorText = this.state.errMsg;
- if (this.props.shouldShowIncompleteErrs && this.state.amountString === '') {
- errorText = 'This field is required';
- }
- let label: React.ReactNode | string = '';
- if (!_.isUndefined(this.props.label)) {
- label = <RequiredLabel label={this.props.label} />;
- }
- return (
- <TextField
- fullWidth={true}
- floatingLabelText={label}
- floatingLabelFixed={true}
- floatingLabelStyle={{ color: colors.grey, width: 206 }}
- errorText={errorText}
- value={this.state.amountString}
- hintText={<span style={{ textTransform: 'capitalize' }}>amount</span>}
- onChange={this._onValueChange.bind(this)}
- underlineStyle={{ width: 'calc(100% + 50px)' }}
- />
- );
- }
- private _onValueChange(e: any, amountString: string) {
- const errMsg = this._validate(amountString, this.props.balance);
- this.setState(
- {
- amountString,
- errMsg,
- },
- () => {
- const isValid = _.isUndefined(errMsg);
- if (utils.isNumeric(amountString)) {
- this.props.onChange(isValid, new BigNumber(amountString));
- } else {
- this.props.onChange(isValid);
- }
- },
- );
- }
- private _validate(amountString: string, balance: BigNumber): InputErrMsg {
- if (!utils.isNumeric(amountString)) {
- return amountString !== '' ? 'Must be a number' : '';
- }
- const amount = new BigNumber(amountString);
- if (amount.eq(0)) {
- return 'Cannot be zero';
- }
- if (this.props.shouldCheckBalance && amount.gt(balance)) {
- return <span>Insufficient balance. {this._renderIncreaseBalanceLink()}</span>;
- }
- const errMsg = _.isUndefined(this.props.validate) ? undefined : this.props.validate(amount);
- return errMsg;
- }
- private _renderIncreaseBalanceLink() {
- if (this.props.shouldHideVisitBalancesLink) {
- return null;
- }
+ public static defaultProps: Partial<BalanceBoundedInputProps> = {
+ shouldShowIncompleteErrs: false,
+ shouldHideVisitBalancesLink: false,
+ };
+ constructor(props: BalanceBoundedInputProps) {
+ super(props);
+ const amountString = this.props.amount ? this.props.amount.toString() : '';
+ this.state = {
+ errMsg: this._validate(amountString, props.balance),
+ amountString,
+ };
+ }
+ public componentWillReceiveProps(nextProps: BalanceBoundedInputProps) {
+ if (nextProps === this.props) {
+ return;
+ }
+ const isCurrentAmountNumeric = utils.isNumeric(this.state.amountString);
+ if (!_.isUndefined(nextProps.amount)) {
+ let shouldResetState = false;
+ if (!isCurrentAmountNumeric) {
+ shouldResetState = true;
+ } else {
+ const currentAmount = new BigNumber(this.state.amountString);
+ if (!currentAmount.eq(nextProps.amount) || !nextProps.balance.eq(this.props.balance)) {
+ shouldResetState = true;
+ }
+ }
+ if (shouldResetState) {
+ const amountString = nextProps.amount.toString();
+ this.setState({
+ errMsg: this._validate(amountString, nextProps.balance),
+ amountString,
+ });
+ }
+ } else if (isCurrentAmountNumeric) {
+ const amountString = '';
+ this.setState({
+ errMsg: this._validate(amountString, nextProps.balance),
+ amountString,
+ });
+ }
+ }
+ public render() {
+ let errorText = this.state.errMsg;
+ if (this.props.shouldShowIncompleteErrs && this.state.amountString === '') {
+ errorText = 'This field is required';
+ }
+ let label: React.ReactNode | string = '';
+ if (!_.isUndefined(this.props.label)) {
+ label = <RequiredLabel label={this.props.label} />;
+ }
+ return (
+ <TextField
+ fullWidth={true}
+ floatingLabelText={label}
+ floatingLabelFixed={true}
+ floatingLabelStyle={{ color: colors.grey, width: 206 }}
+ errorText={errorText}
+ value={this.state.amountString}
+ hintText={<span style={{ textTransform: 'capitalize' }}>amount</span>}
+ onChange={this._onValueChange.bind(this)}
+ underlineStyle={{ width: 'calc(100% + 50px)' }}
+ />
+ );
+ }
+ private _onValueChange(e: any, amountString: string) {
+ const errMsg = this._validate(amountString, this.props.balance);
+ this.setState(
+ {
+ amountString,
+ errMsg,
+ },
+ () => {
+ const isValid = _.isUndefined(errMsg);
+ if (utils.isNumeric(amountString)) {
+ this.props.onChange(isValid, new BigNumber(amountString));
+ } else {
+ this.props.onChange(isValid);
+ }
+ },
+ );
+ }
+ private _validate(amountString: string, balance: BigNumber): InputErrMsg {
+ if (!utils.isNumeric(amountString)) {
+ return amountString !== '' ? 'Must be a number' : '';
+ }
+ const amount = new BigNumber(amountString);
+ if (amount.eq(0)) {
+ return 'Cannot be zero';
+ }
+ if (this.props.shouldCheckBalance && amount.gt(balance)) {
+ return <span>Insufficient balance. {this._renderIncreaseBalanceLink()}</span>;
+ }
+ const errMsg = _.isUndefined(this.props.validate) ? undefined : this.props.validate(amount);
+ return errMsg;
+ }
+ private _renderIncreaseBalanceLink() {
+ if (this.props.shouldHideVisitBalancesLink) {
+ return null;
+ }
- const increaseBalanceText = 'Increase balance';
- const linkStyle = {
- cursor: 'pointer',
- color: colors.darkestGrey,
- textDecoration: 'underline',
- display: 'inline',
- };
- if (_.isUndefined(this.props.onVisitBalancesPageClick)) {
- return (
- <Link to={`${WebsitePaths.Portal}/balances`} style={linkStyle}>
- {increaseBalanceText}
- </Link>
- );
- } else {
- return (
- <div onClick={this.props.onVisitBalancesPageClick} style={linkStyle}>
- {increaseBalanceText}
- </div>
- );
- }
- }
+ const increaseBalanceText = 'Increase balance';
+ const linkStyle = {
+ cursor: 'pointer',
+ color: colors.darkestGrey,
+ textDecoration: 'underline',
+ display: 'inline',
+ };
+ if (_.isUndefined(this.props.onVisitBalancesPageClick)) {
+ return (
+ <Link to={`${WebsitePaths.Portal}/balances`} style={linkStyle}>
+ {increaseBalanceText}
+ </Link>
+ );
+ } else {
+ return (
+ <div onClick={this.props.onVisitBalancesPageClick} style={linkStyle}>
+ {increaseBalanceText}
+ </div>
+ );
+ }
+ }
}
diff --git a/packages/website/ts/components/inputs/eth_amount_input.tsx b/packages/website/ts/components/inputs/eth_amount_input.tsx
index 7f9747094..a66f92c8c 100644
--- a/packages/website/ts/components/inputs/eth_amount_input.tsx
+++ b/packages/website/ts/components/inputs/eth_amount_input.tsx
@@ -7,43 +7,43 @@ import { ValidatedBigNumberCallback } from 'ts/types';
import { constants } from 'ts/utils/constants';
interface EthAmountInputProps {
- label?: string;
- balance: BigNumber;
- amount?: BigNumber;
- onChange: ValidatedBigNumberCallback;
- shouldShowIncompleteErrs: boolean;
- onVisitBalancesPageClick?: () => void;
- shouldCheckBalance: boolean;
- shouldHideVisitBalancesLink?: boolean;
+ label?: string;
+ balance: BigNumber;
+ amount?: BigNumber;
+ onChange: ValidatedBigNumberCallback;
+ shouldShowIncompleteErrs: boolean;
+ onVisitBalancesPageClick?: () => void;
+ shouldCheckBalance: boolean;
+ shouldHideVisitBalancesLink?: boolean;
}
interface EthAmountInputState {}
export class EthAmountInput extends React.Component<EthAmountInputProps, EthAmountInputState> {
- public render() {
- const amount = this.props.amount
- ? ZeroEx.toUnitAmount(this.props.amount, constants.DECIMAL_PLACES_ETH)
- : undefined;
- return (
- <div className="flex overflow-hidden" style={{ height: 63 }}>
- <BalanceBoundedInput
- label={this.props.label}
- balance={this.props.balance}
- amount={amount}
- onChange={this._onChange.bind(this)}
- shouldCheckBalance={this.props.shouldCheckBalance}
- shouldShowIncompleteErrs={this.props.shouldShowIncompleteErrs}
- onVisitBalancesPageClick={this.props.onVisitBalancesPageClick}
- shouldHideVisitBalancesLink={this.props.shouldHideVisitBalancesLink}
- />
- <div style={{ paddingTop: _.isUndefined(this.props.label) ? 15 : 40 }}>ETH</div>
- </div>
- );
- }
- private _onChange(isValid: boolean, amount?: BigNumber) {
- const baseUnitAmountIfExists = _.isUndefined(amount)
- ? undefined
- : ZeroEx.toBaseUnitAmount(amount, constants.DECIMAL_PLACES_ETH);
- this.props.onChange(isValid, baseUnitAmountIfExists);
- }
+ public render() {
+ const amount = this.props.amount
+ ? ZeroEx.toUnitAmount(this.props.amount, constants.DECIMAL_PLACES_ETH)
+ : undefined;
+ return (
+ <div className="flex overflow-hidden" style={{ height: 63 }}>
+ <BalanceBoundedInput
+ label={this.props.label}
+ balance={this.props.balance}
+ amount={amount}
+ onChange={this._onChange.bind(this)}
+ shouldCheckBalance={this.props.shouldCheckBalance}
+ shouldShowIncompleteErrs={this.props.shouldShowIncompleteErrs}
+ onVisitBalancesPageClick={this.props.onVisitBalancesPageClick}
+ shouldHideVisitBalancesLink={this.props.shouldHideVisitBalancesLink}
+ />
+ <div style={{ paddingTop: _.isUndefined(this.props.label) ? 15 : 40 }}>ETH</div>
+ </div>
+ );
+ }
+ private _onChange(isValid: boolean, amount?: BigNumber) {
+ const baseUnitAmountIfExists = _.isUndefined(amount)
+ ? undefined
+ : ZeroEx.toBaseUnitAmount(amount, constants.DECIMAL_PLACES_ETH);
+ this.props.onChange(isValid, baseUnitAmountIfExists);
+ }
}
diff --git a/packages/website/ts/components/inputs/expiration_input.tsx b/packages/website/ts/components/inputs/expiration_input.tsx
index cb4ed7bd0..e473648d2 100644
--- a/packages/website/ts/components/inputs/expiration_input.tsx
+++ b/packages/website/ts/components/inputs/expiration_input.tsx
@@ -7,94 +7,94 @@ import * as React from 'react';
import { utils } from 'ts/utils/utils';
interface ExpirationInputProps {
- orderExpiryTimestamp: BigNumber;
- updateOrderExpiry: (unixTimestampSec: BigNumber) => void;
+ orderExpiryTimestamp: BigNumber;
+ updateOrderExpiry: (unixTimestampSec: BigNumber) => void;
}
interface ExpirationInputState {
- dateMoment: moment.Moment;
- timeMoment: moment.Moment;
+ dateMoment: moment.Moment;
+ timeMoment: moment.Moment;
}
export class ExpirationInput extends React.Component<ExpirationInputProps, ExpirationInputState> {
- private _earliestPickableMoment: moment.Moment;
- constructor(props: ExpirationInputProps) {
- super(props);
- // Set the earliest pickable date to today at 00:00, so users can only pick the current or later dates
- this._earliestPickableMoment = moment().startOf('day');
- const expirationMoment = utils.convertToMomentFromUnixTimestamp(props.orderExpiryTimestamp);
- const initialOrderExpiryTimestamp = utils.initialOrderExpiryUnixTimestampSec();
- const didUserSetExpiry = !initialOrderExpiryTimestamp.eq(props.orderExpiryTimestamp);
- this.state = {
- dateMoment: didUserSetExpiry ? expirationMoment : undefined,
- timeMoment: didUserSetExpiry ? expirationMoment : undefined,
- };
- }
- public render() {
- const date = this.state.dateMoment ? this.state.dateMoment.toDate() : undefined;
- const time = this.state.timeMoment ? this.state.timeMoment.toDate() : undefined;
- return (
- <div className="clearfix">
- <div className="col col-6 overflow-hidden pr3 flex relative">
- <DatePicker
- className="overflow-hidden"
- hintText="Date"
- mode="landscape"
- autoOk={true}
- value={date}
- onChange={this._onDateChanged.bind(this)}
- shouldDisableDate={this._shouldDisableDate.bind(this)}
- />
- <div className="absolute" style={{ fontSize: 20, right: 40, top: 13, pointerEvents: 'none' }}>
- <i className="zmdi zmdi-calendar" />
- </div>
- </div>
- <div className="col col-5 overflow-hidden flex relative">
- <TimePicker
- className="overflow-hidden"
- hintText="Time"
- autoOk={true}
- value={time}
- onChange={this._onTimeChanged.bind(this)}
- />
- <div className="absolute" style={{ fontSize: 20, right: 9, top: 13, pointerEvents: 'none' }}>
- <i className="zmdi zmdi-time" />
- </div>
- </div>
- <div onClick={this._clearDates.bind(this)} className="col col-1 pt2" style={{ textAlign: 'right' }}>
- <i style={{ fontSize: 16, cursor: 'pointer' }} className="zmdi zmdi-close" />
- </div>
- </div>
- );
- }
- private _shouldDisableDate(date: Date): boolean {
- return moment(date)
- .startOf('day')
- .isBefore(this._earliestPickableMoment);
- }
- private _clearDates() {
- this.setState({
- dateMoment: undefined,
- timeMoment: undefined,
- });
- const defaultDateTime = utils.initialOrderExpiryUnixTimestampSec();
- this.props.updateOrderExpiry(defaultDateTime);
- }
- private _onDateChanged(e: any, date: Date) {
- const dateMoment = moment(date);
- this.setState({
- dateMoment,
- });
- const timestamp = utils.convertToUnixTimestampSeconds(dateMoment, this.state.timeMoment);
- this.props.updateOrderExpiry(timestamp);
- }
- private _onTimeChanged(e: any, time: Date) {
- const timeMoment = moment(time);
- this.setState({
- timeMoment,
- });
- const dateMoment = _.isUndefined(this.state.dateMoment) ? moment() : this.state.dateMoment;
- const timestamp = utils.convertToUnixTimestampSeconds(dateMoment, timeMoment);
- this.props.updateOrderExpiry(timestamp);
- }
+ private _earliestPickableMoment: moment.Moment;
+ constructor(props: ExpirationInputProps) {
+ super(props);
+ // Set the earliest pickable date to today at 00:00, so users can only pick the current or later dates
+ this._earliestPickableMoment = moment().startOf('day');
+ const expirationMoment = utils.convertToMomentFromUnixTimestamp(props.orderExpiryTimestamp);
+ const initialOrderExpiryTimestamp = utils.initialOrderExpiryUnixTimestampSec();
+ const didUserSetExpiry = !initialOrderExpiryTimestamp.eq(props.orderExpiryTimestamp);
+ this.state = {
+ dateMoment: didUserSetExpiry ? expirationMoment : undefined,
+ timeMoment: didUserSetExpiry ? expirationMoment : undefined,
+ };
+ }
+ public render() {
+ const date = this.state.dateMoment ? this.state.dateMoment.toDate() : undefined;
+ const time = this.state.timeMoment ? this.state.timeMoment.toDate() : undefined;
+ return (
+ <div className="clearfix">
+ <div className="col col-6 overflow-hidden pr3 flex relative">
+ <DatePicker
+ className="overflow-hidden"
+ hintText="Date"
+ mode="landscape"
+ autoOk={true}
+ value={date}
+ onChange={this._onDateChanged.bind(this)}
+ shouldDisableDate={this._shouldDisableDate.bind(this)}
+ />
+ <div className="absolute" style={{ fontSize: 20, right: 40, top: 13, pointerEvents: 'none' }}>
+ <i className="zmdi zmdi-calendar" />
+ </div>
+ </div>
+ <div className="col col-5 overflow-hidden flex relative">
+ <TimePicker
+ className="overflow-hidden"
+ hintText="Time"
+ autoOk={true}
+ value={time}
+ onChange={this._onTimeChanged.bind(this)}
+ />
+ <div className="absolute" style={{ fontSize: 20, right: 9, top: 13, pointerEvents: 'none' }}>
+ <i className="zmdi zmdi-time" />
+ </div>
+ </div>
+ <div onClick={this._clearDates.bind(this)} className="col col-1 pt2" style={{ textAlign: 'right' }}>
+ <i style={{ fontSize: 16, cursor: 'pointer' }} className="zmdi zmdi-close" />
+ </div>
+ </div>
+ );
+ }
+ private _shouldDisableDate(date: Date): boolean {
+ return moment(date)
+ .startOf('day')
+ .isBefore(this._earliestPickableMoment);
+ }
+ private _clearDates() {
+ this.setState({
+ dateMoment: undefined,
+ timeMoment: undefined,
+ });
+ const defaultDateTime = utils.initialOrderExpiryUnixTimestampSec();
+ this.props.updateOrderExpiry(defaultDateTime);
+ }
+ private _onDateChanged(e: any, date: Date) {
+ const dateMoment = moment(date);
+ this.setState({
+ dateMoment,
+ });
+ const timestamp = utils.convertToUnixTimestampSeconds(dateMoment, this.state.timeMoment);
+ this.props.updateOrderExpiry(timestamp);
+ }
+ private _onTimeChanged(e: any, time: Date) {
+ const timeMoment = moment(time);
+ this.setState({
+ timeMoment,
+ });
+ const dateMoment = _.isUndefined(this.state.dateMoment) ? moment() : this.state.dateMoment;
+ const timestamp = utils.convertToUnixTimestampSeconds(dateMoment, timeMoment);
+ this.props.updateOrderExpiry(timestamp);
+ }
}
diff --git a/packages/website/ts/components/inputs/hash_input.tsx b/packages/website/ts/components/inputs/hash_input.tsx
index 36d7e6140..5a3d34fe6 100644
--- a/packages/website/ts/components/inputs/hash_input.tsx
+++ b/packages/website/ts/components/inputs/hash_input.tsx
@@ -8,55 +8,55 @@ import { HashData, Styles } from 'ts/types';
import { constants } from 'ts/utils/constants';
const styles: Styles = {
- textField: {
- overflow: 'hidden',
- paddingTop: 8,
- textOverflow: 'ellipsis',
- whiteSpace: 'nowrap',
- },
+ textField: {
+ overflow: 'hidden',
+ paddingTop: 8,
+ textOverflow: 'ellipsis',
+ whiteSpace: 'nowrap',
+ },
};
interface HashInputProps {
- blockchain: Blockchain;
- blockchainIsLoaded: boolean;
- hashData: HashData;
- label: string;
+ blockchain: Blockchain;
+ blockchainIsLoaded: boolean;
+ hashData: HashData;
+ label: string;
}
interface HashInputState {}
export class HashInput extends React.Component<HashInputProps, HashInputState> {
- public render() {
- const msgHashHex = this.props.blockchainIsLoaded ? this._generateMessageHashHex() : '';
- return (
- <div>
- <FakeTextField label={this.props.label}>
- <div style={styles.textField} data-tip={true} data-for="hashTooltip">
- {msgHashHex}
- </div>
- </FakeTextField>
- <ReactTooltip id="hashTooltip">{msgHashHex}</ReactTooltip>
- </div>
- );
- }
- private _generateMessageHashHex() {
- const exchangeContractAddress = this.props.blockchain.getExchangeContractAddressIfExists();
- const hashData = this.props.hashData;
- const order: Order = {
- exchangeContractAddress,
- expirationUnixTimestampSec: hashData.orderExpiryTimestamp,
- feeRecipient: hashData.feeRecipientAddress,
- maker: _.isEmpty(hashData.orderMakerAddress) ? constants.NULL_ADDRESS : hashData.orderMakerAddress,
- makerFee: hashData.makerFee,
- makerTokenAddress: hashData.depositTokenContractAddr,
- makerTokenAmount: hashData.depositAmount,
- salt: hashData.orderSalt,
- taker: hashData.orderTakerAddress,
- takerFee: hashData.takerFee,
- takerTokenAddress: hashData.receiveTokenContractAddr,
- takerTokenAmount: hashData.receiveAmount,
- };
- const orderHash = ZeroEx.getOrderHashHex(order);
- return orderHash;
- }
+ public render() {
+ const msgHashHex = this.props.blockchainIsLoaded ? this._generateMessageHashHex() : '';
+ return (
+ <div>
+ <FakeTextField label={this.props.label}>
+ <div style={styles.textField} data-tip={true} data-for="hashTooltip">
+ {msgHashHex}
+ </div>
+ </FakeTextField>
+ <ReactTooltip id="hashTooltip">{msgHashHex}</ReactTooltip>
+ </div>
+ );
+ }
+ private _generateMessageHashHex() {
+ const exchangeContractAddress = this.props.blockchain.getExchangeContractAddressIfExists();
+ const hashData = this.props.hashData;
+ const order: Order = {
+ exchangeContractAddress,
+ expirationUnixTimestampSec: hashData.orderExpiryTimestamp,
+ feeRecipient: hashData.feeRecipientAddress,
+ maker: _.isEmpty(hashData.orderMakerAddress) ? constants.NULL_ADDRESS : hashData.orderMakerAddress,
+ makerFee: hashData.makerFee,
+ makerTokenAddress: hashData.depositTokenContractAddr,
+ makerTokenAmount: hashData.depositAmount,
+ salt: hashData.orderSalt,
+ taker: hashData.orderTakerAddress,
+ takerFee: hashData.takerFee,
+ takerTokenAddress: hashData.receiveTokenContractAddr,
+ takerTokenAmount: hashData.receiveAmount,
+ };
+ const orderHash = ZeroEx.getOrderHashHex(order);
+ return orderHash;
+ }
}
diff --git a/packages/website/ts/components/inputs/identicon_address_input.tsx b/packages/website/ts/components/inputs/identicon_address_input.tsx
index f14cb4e9c..4cf9af64d 100644
--- a/packages/website/ts/components/inputs/identicon_address_input.tsx
+++ b/packages/website/ts/components/inputs/identicon_address_input.tsx
@@ -6,48 +6,48 @@ import { InputLabel } from 'ts/components/ui/input_label';
import { RequiredLabel } from 'ts/components/ui/required_label';
interface IdenticonAddressInputProps {
- initialAddress: string;
- isRequired?: boolean;
- label: string;
- updateOrderAddress: (address?: string) => void;
+ initialAddress: string;
+ isRequired?: boolean;
+ label: string;
+ updateOrderAddress: (address?: string) => void;
}
interface IdenticonAddressInputState {
- address: string;
+ address: string;
}
export class IdenticonAddressInput extends React.Component<IdenticonAddressInputProps, IdenticonAddressInputState> {
- constructor(props: IdenticonAddressInputProps) {
- super(props);
- this.state = {
- address: props.initialAddress,
- };
- }
- public render() {
- const label = this.props.isRequired ? <RequiredLabel label={this.props.label} /> : this.props.label;
- return (
- <div className="relative" style={{ width: '100%' }}>
- <InputLabel text={label} />
- <div className="flex">
- <div className="col col-1 pb1 pr1" style={{ paddingTop: 13 }}>
- <Identicon address={this.state.address} diameter={26} />
- </div>
- <div className="col col-11 pb1 pl1" style={{ height: 65 }}>
- <AddressInput
- hintText="e.g 0x75bE4F78AA3699B3A348c84bDB2a96c3Db..."
- shouldHideLabel={true}
- initialAddress={this.props.initialAddress}
- updateAddress={this._updateAddress.bind(this)}
- />
- </div>
- </div>
- </div>
- );
- }
- private _updateAddress(address?: string): void {
- this.setState({
- address,
- });
- this.props.updateOrderAddress(address);
- }
+ constructor(props: IdenticonAddressInputProps) {
+ super(props);
+ this.state = {
+ address: props.initialAddress,
+ };
+ }
+ public render() {
+ const label = this.props.isRequired ? <RequiredLabel label={this.props.label} /> : this.props.label;
+ return (
+ <div className="relative" style={{ width: '100%' }}>
+ <InputLabel text={label} />
+ <div className="flex">
+ <div className="col col-1 pb1 pr1" style={{ paddingTop: 13 }}>
+ <Identicon address={this.state.address} diameter={26} />
+ </div>
+ <div className="col col-11 pb1 pl1" style={{ height: 65 }}>
+ <AddressInput
+ hintText="e.g 0x75bE4F78AA3699B3A348c84bDB2a96c3Db..."
+ shouldHideLabel={true}
+ initialAddress={this.props.initialAddress}
+ updateAddress={this._updateAddress.bind(this)}
+ />
+ </div>
+ </div>
+ </div>
+ );
+ }
+ private _updateAddress(address?: string): void {
+ this.setState({
+ address,
+ });
+ this.props.updateOrderAddress(address);
+ }
}
diff --git a/packages/website/ts/components/inputs/token_amount_input.tsx b/packages/website/ts/components/inputs/token_amount_input.tsx
index 0a71b2c00..63966d759 100644
--- a/packages/website/ts/components/inputs/token_amount_input.tsx
+++ b/packages/website/ts/components/inputs/token_amount_input.tsx
@@ -8,63 +8,63 @@ import { InputErrMsg, Token, TokenState, ValidatedBigNumberCallback, WebsitePath
import { colors } from 'ts/utils/colors';
interface TokenAmountInputProps {
- token: Token;
- tokenState: TokenState;
- label?: string;
- amount?: BigNumber;
- shouldShowIncompleteErrs: boolean;
- shouldCheckBalance: boolean;
- shouldCheckAllowance: boolean;
- onChange: ValidatedBigNumberCallback;
- onVisitBalancesPageClick?: () => void;
+ token: Token;
+ tokenState: TokenState;
+ label?: string;
+ amount?: BigNumber;
+ shouldShowIncompleteErrs: boolean;
+ shouldCheckBalance: boolean;
+ shouldCheckAllowance: boolean;
+ onChange: ValidatedBigNumberCallback;
+ onVisitBalancesPageClick?: () => void;
}
interface TokenAmountInputState {}
export class TokenAmountInput extends React.Component<TokenAmountInputProps, TokenAmountInputState> {
- public render() {
- const amount = this.props.amount
- ? ZeroEx.toUnitAmount(this.props.amount, this.props.token.decimals)
- : undefined;
- const hasLabel = !_.isUndefined(this.props.label);
- return (
- <div className="flex overflow-hidden" style={{ height: hasLabel ? 84 : 62 }}>
- <BalanceBoundedInput
- label={this.props.label}
- amount={amount}
- balance={ZeroEx.toUnitAmount(this.props.tokenState.balance, this.props.token.decimals)}
- onChange={this._onChange.bind(this)}
- validate={this._validate.bind(this)}
- shouldCheckBalance={this.props.shouldCheckBalance}
- shouldShowIncompleteErrs={this.props.shouldShowIncompleteErrs}
- onVisitBalancesPageClick={this.props.onVisitBalancesPageClick}
- />
- <div style={{ paddingTop: hasLabel ? 39 : 14 }}>{this.props.token.symbol}</div>
- </div>
- );
- }
- private _onChange(isValid: boolean, amount?: BigNumber) {
- let baseUnitAmount;
- if (!_.isUndefined(amount)) {
- baseUnitAmount = ZeroEx.toBaseUnitAmount(amount, this.props.token.decimals);
- }
- this.props.onChange(isValid, baseUnitAmount);
- }
- private _validate(amount: BigNumber): InputErrMsg {
- if (this.props.shouldCheckAllowance && amount.gt(this.props.tokenState.allowance)) {
- return (
- <span>
- Insufficient allowance.{' '}
- <Link
- to={`${WebsitePaths.Portal}/balances`}
- style={{ cursor: 'pointer', color: colors.darkestGrey }}
- >
- Set allowance
- </Link>
- </span>
- );
- } else {
- return undefined;
- }
- }
+ public render() {
+ const amount = this.props.amount
+ ? ZeroEx.toUnitAmount(this.props.amount, this.props.token.decimals)
+ : undefined;
+ const hasLabel = !_.isUndefined(this.props.label);
+ return (
+ <div className="flex overflow-hidden" style={{ height: hasLabel ? 84 : 62 }}>
+ <BalanceBoundedInput
+ label={this.props.label}
+ amount={amount}
+ balance={ZeroEx.toUnitAmount(this.props.tokenState.balance, this.props.token.decimals)}
+ onChange={this._onChange.bind(this)}
+ validate={this._validate.bind(this)}
+ shouldCheckBalance={this.props.shouldCheckBalance}
+ shouldShowIncompleteErrs={this.props.shouldShowIncompleteErrs}
+ onVisitBalancesPageClick={this.props.onVisitBalancesPageClick}
+ />
+ <div style={{ paddingTop: hasLabel ? 39 : 14 }}>{this.props.token.symbol}</div>
+ </div>
+ );
+ }
+ private _onChange(isValid: boolean, amount?: BigNumber) {
+ let baseUnitAmount;
+ if (!_.isUndefined(amount)) {
+ baseUnitAmount = ZeroEx.toBaseUnitAmount(amount, this.props.token.decimals);
+ }
+ this.props.onChange(isValid, baseUnitAmount);
+ }
+ private _validate(amount: BigNumber): InputErrMsg {
+ if (this.props.shouldCheckAllowance && amount.gt(this.props.tokenState.allowance)) {
+ return (
+ <span>
+ Insufficient allowance.{' '}
+ <Link
+ to={`${WebsitePaths.Portal}/balances`}
+ style={{ cursor: 'pointer', color: colors.darkestGrey }}
+ >
+ Set allowance
+ </Link>
+ </span>
+ );
+ } else {
+ return undefined;
+ }
+ }
}
diff --git a/packages/website/ts/components/inputs/token_input.tsx b/packages/website/ts/components/inputs/token_input.tsx
index 3aceacb22..5df19b28c 100644
--- a/packages/website/ts/components/inputs/token_input.tsx
+++ b/packages/website/ts/components/inputs/token_input.tsx
@@ -12,93 +12,93 @@ import { colors } from 'ts/utils/colors';
const TOKEN_ICON_DIMENSION = 80;
interface TokenInputProps {
- blockchain: Blockchain;
- blockchainErr: BlockchainErrs;
- dispatcher: Dispatcher;
- label: string;
- side: Side;
- networkId: number;
- assetToken: AssetToken;
- updateChosenAssetToken: (side: Side, token: AssetToken) => void;
- tokenByAddress: TokenByAddress;
- userAddress: string;
+ blockchain: Blockchain;
+ blockchainErr: BlockchainErrs;
+ dispatcher: Dispatcher;
+ label: string;
+ side: Side;
+ networkId: number;
+ assetToken: AssetToken;
+ updateChosenAssetToken: (side: Side, token: AssetToken) => void;
+ tokenByAddress: TokenByAddress;
+ userAddress: string;
}
interface TokenInputState {
- isHoveringIcon: boolean;
- isPickerOpen: boolean;
- trackCandidateTokenIfExists?: Token;
+ isHoveringIcon: boolean;
+ isPickerOpen: boolean;
+ trackCandidateTokenIfExists?: Token;
}
export class TokenInput extends React.Component<TokenInputProps, TokenInputState> {
- constructor(props: TokenInputProps) {
- super(props);
- this.state = {
- isHoveringIcon: false,
- isPickerOpen: false,
- };
- }
- public render() {
- const token = this.props.tokenByAddress[this.props.assetToken.address];
- const iconStyles = {
- cursor: 'pointer',
- opacity: this.state.isHoveringIcon ? 0.5 : 1,
- };
- return (
- <div className="relative">
- <div className="pb1">
- <InputLabel text={this.props.label} />
- </div>
- <Paper
- zDepth={1}
- style={{ cursor: 'pointer' }}
- onMouseEnter={this._onToggleHover.bind(this, true)}
- onMouseLeave={this._onToggleHover.bind(this, false)}
- onClick={this._onAssetClicked.bind(this)}
- >
- <div className="mx-auto pt2" style={{ width: TOKEN_ICON_DIMENSION, ...iconStyles }}>
- <TokenIcon token={token} diameter={TOKEN_ICON_DIMENSION} />
- </div>
- <div className="py1 center" style={{ color: colors.grey }}>
- {token.name}
- </div>
- </Paper>
- <AssetPicker
- userAddress={this.props.userAddress}
- networkId={this.props.networkId}
- blockchain={this.props.blockchain}
- dispatcher={this.props.dispatcher}
- isOpen={this.state.isPickerOpen}
- currentTokenAddress={this.props.assetToken.address}
- onTokenChosen={this._onTokenChosen.bind(this)}
- tokenByAddress={this.props.tokenByAddress}
- />
- </div>
- );
- }
- private _onTokenChosen(tokenAddress: string) {
- const assetToken: AssetToken = {
- address: tokenAddress,
- amount: this.props.assetToken.amount,
- };
- this.props.updateChosenAssetToken(this.props.side, assetToken);
- this.setState({
- isPickerOpen: false,
- });
- }
- private _onToggleHover(isHoveringIcon: boolean) {
- this.setState({
- isHoveringIcon,
- });
- }
- private _onAssetClicked() {
- if (this.props.blockchainErr !== BlockchainErrs.NoError) {
- this.props.dispatcher.updateShouldBlockchainErrDialogBeOpen(true);
- return;
- }
+ constructor(props: TokenInputProps) {
+ super(props);
+ this.state = {
+ isHoveringIcon: false,
+ isPickerOpen: false,
+ };
+ }
+ public render() {
+ const token = this.props.tokenByAddress[this.props.assetToken.address];
+ const iconStyles = {
+ cursor: 'pointer',
+ opacity: this.state.isHoveringIcon ? 0.5 : 1,
+ };
+ return (
+ <div className="relative">
+ <div className="pb1">
+ <InputLabel text={this.props.label} />
+ </div>
+ <Paper
+ zDepth={1}
+ style={{ cursor: 'pointer' }}
+ onMouseEnter={this._onToggleHover.bind(this, true)}
+ onMouseLeave={this._onToggleHover.bind(this, false)}
+ onClick={this._onAssetClicked.bind(this)}
+ >
+ <div className="mx-auto pt2" style={{ width: TOKEN_ICON_DIMENSION, ...iconStyles }}>
+ <TokenIcon token={token} diameter={TOKEN_ICON_DIMENSION} />
+ </div>
+ <div className="py1 center" style={{ color: colors.grey }}>
+ {token.name}
+ </div>
+ </Paper>
+ <AssetPicker
+ userAddress={this.props.userAddress}
+ networkId={this.props.networkId}
+ blockchain={this.props.blockchain}
+ dispatcher={this.props.dispatcher}
+ isOpen={this.state.isPickerOpen}
+ currentTokenAddress={this.props.assetToken.address}
+ onTokenChosen={this._onTokenChosen.bind(this)}
+ tokenByAddress={this.props.tokenByAddress}
+ />
+ </div>
+ );
+ }
+ private _onTokenChosen(tokenAddress: string) {
+ const assetToken: AssetToken = {
+ address: tokenAddress,
+ amount: this.props.assetToken.amount,
+ };
+ this.props.updateChosenAssetToken(this.props.side, assetToken);
+ this.setState({
+ isPickerOpen: false,
+ });
+ }
+ private _onToggleHover(isHoveringIcon: boolean) {
+ this.setState({
+ isHoveringIcon,
+ });
+ }
+ private _onAssetClicked() {
+ if (this.props.blockchainErr !== BlockchainErrs.NoError) {
+ this.props.dispatcher.updateShouldBlockchainErrDialogBeOpen(true);
+ return;
+ }
- this.setState({
- isPickerOpen: true,
- });
- }
+ this.setState({
+ isPickerOpen: true,
+ });
+ }
}
diff --git a/packages/website/ts/components/order_json.tsx b/packages/website/ts/components/order_json.tsx
index 1640a178e..1b6b32a04 100644
--- a/packages/website/ts/components/order_json.tsx
+++ b/packages/website/ts/components/order_json.tsx
@@ -11,172 +11,172 @@ import { errorReporter } from 'ts/utils/error_reporter';
import { utils } from 'ts/utils/utils';
interface OrderJSONProps {
- exchangeContractIfExists: string;
- orderExpiryTimestamp: BigNumber;
- orderSignatureData: SignatureData;
- orderTakerAddress: string;
- orderMakerAddress: string;
- orderSalt: BigNumber;
- orderMakerFee: BigNumber;
- orderTakerFee: BigNumber;
- orderFeeRecipient: string;
- networkId: number;
- sideToAssetToken: SideToAssetToken;
- tokenByAddress: TokenByAddress;
+ exchangeContractIfExists: string;
+ orderExpiryTimestamp: BigNumber;
+ orderSignatureData: SignatureData;
+ orderTakerAddress: string;
+ orderMakerAddress: string;
+ orderSalt: BigNumber;
+ orderMakerFee: BigNumber;
+ orderTakerFee: BigNumber;
+ orderFeeRecipient: string;
+ networkId: number;
+ sideToAssetToken: SideToAssetToken;
+ tokenByAddress: TokenByAddress;
}
interface OrderJSONState {
- shareLink: string;
+ shareLink: string;
}
export class OrderJSON extends React.Component<OrderJSONProps, OrderJSONState> {
- constructor(props: OrderJSONProps) {
- super(props);
- this.state = {
- shareLink: '',
- };
- // tslint:disable-next-line:no-floating-promises
- this._setShareLinkAsync();
- }
- public render() {
- const order = utils.generateOrder(
- this.props.networkId,
- this.props.exchangeContractIfExists,
- this.props.sideToAssetToken,
- this.props.orderExpiryTimestamp,
- this.props.orderTakerAddress,
- this.props.orderMakerAddress,
- this.props.orderMakerFee,
- this.props.orderTakerFee,
- this.props.orderFeeRecipient,
- this.props.orderSignatureData,
- this.props.tokenByAddress,
- this.props.orderSalt,
- );
- const orderJSON = JSON.stringify(order);
- return (
- <div>
- <div className="pb2">
- You have successfully generated and cryptographically signed an order! The following JSON contains
- the order parameters and cryptographic signature that your counterparty will need to execute a trade
- with you.
- </div>
- <div className="pb2 flex">
- <div className="inline-block pl1" style={{ top: 1 }}>
- <CopyIcon data={orderJSON} callToAction="Copy" />
- </div>
- </div>
- <Paper className="center overflow-hidden">
- <TextField
- id="orderJSON"
- style={{ width: 710 }}
- value={JSON.stringify(order, null, '\t')}
- multiLine={true}
- rows={2}
- rowsMax={8}
- underlineStyle={{ display: 'none' }}
- />
- </Paper>
- <div className="pt3 pb2 center">
- <div>Share your signed order!</div>
- <div>
- <div className="mx-auto overflow-hidden" style={{ width: 152 }}>
- <TextField id={`${this.state.shareLink}-bitly`} value={this.state.shareLink} />
- </div>
- </div>
- <div className="mx-auto pt1 flex" style={{ width: 91 }}>
- <div>
- <i
- style={{ cursor: 'pointer', fontSize: 29 }}
- onClick={this._shareViaFacebook.bind(this)}
- className="zmdi zmdi-facebook-box"
- />
- </div>
- <div className="pl1" style={{ position: 'relative', width: 28 }}>
- <i
- style={{
- cursor: 'pointer',
- fontSize: 32,
- position: 'absolute',
- top: -2,
- left: 8,
- }}
- onClick={this._shareViaEmailAsync.bind(this)}
- className="zmdi zmdi-email"
- />
- </div>
- <div className="pl1">
- <i
- style={{ cursor: 'pointer', fontSize: 29 }}
- onClick={this._shareViaTwitterAsync.bind(this)}
- className="zmdi zmdi-twitter-box"
- />
- </div>
- </div>
- </div>
- </div>
- );
- }
- private async _shareViaTwitterAsync() {
- const tweetText = encodeURIComponent(`Fill my order using the 0x protocol: ${this.state.shareLink}`);
- window.open(`https://twitter.com/intent/tweet?text=${tweetText}`, 'Share your order', 'width=500,height=400');
- }
- private async _shareViaFacebook() {
- (window as any).FB.ui(
- {
- display: 'popup',
- href: this.state.shareLink,
- method: 'share',
- },
- _.noop,
- );
- }
- private async _shareViaEmailAsync() {
- const encodedSubject = encodeURIComponent("Let's trade using the 0x protocol");
- const encodedBody = encodeURIComponent(`I generated an order with the 0x protocol.
+ constructor(props: OrderJSONProps) {
+ super(props);
+ this.state = {
+ shareLink: '',
+ };
+ // tslint:disable-next-line:no-floating-promises
+ this._setShareLinkAsync();
+ }
+ public render() {
+ const order = utils.generateOrder(
+ this.props.networkId,
+ this.props.exchangeContractIfExists,
+ this.props.sideToAssetToken,
+ this.props.orderExpiryTimestamp,
+ this.props.orderTakerAddress,
+ this.props.orderMakerAddress,
+ this.props.orderMakerFee,
+ this.props.orderTakerFee,
+ this.props.orderFeeRecipient,
+ this.props.orderSignatureData,
+ this.props.tokenByAddress,
+ this.props.orderSalt,
+ );
+ const orderJSON = JSON.stringify(order);
+ return (
+ <div>
+ <div className="pb2">
+ You have successfully generated and cryptographically signed an order! The following JSON contains
+ the order parameters and cryptographic signature that your counterparty will need to execute a trade
+ with you.
+ </div>
+ <div className="pb2 flex">
+ <div className="inline-block pl1" style={{ top: 1 }}>
+ <CopyIcon data={orderJSON} callToAction="Copy" />
+ </div>
+ </div>
+ <Paper className="center overflow-hidden">
+ <TextField
+ id="orderJSON"
+ style={{ width: 710 }}
+ value={JSON.stringify(order, null, '\t')}
+ multiLine={true}
+ rows={2}
+ rowsMax={8}
+ underlineStyle={{ display: 'none' }}
+ />
+ </Paper>
+ <div className="pt3 pb2 center">
+ <div>Share your signed order!</div>
+ <div>
+ <div className="mx-auto overflow-hidden" style={{ width: 152 }}>
+ <TextField id={`${this.state.shareLink}-bitly`} value={this.state.shareLink} />
+ </div>
+ </div>
+ <div className="mx-auto pt1 flex" style={{ width: 91 }}>
+ <div>
+ <i
+ style={{ cursor: 'pointer', fontSize: 29 }}
+ onClick={this._shareViaFacebook.bind(this)}
+ className="zmdi zmdi-facebook-box"
+ />
+ </div>
+ <div className="pl1" style={{ position: 'relative', width: 28 }}>
+ <i
+ style={{
+ cursor: 'pointer',
+ fontSize: 32,
+ position: 'absolute',
+ top: -2,
+ left: 8,
+ }}
+ onClick={this._shareViaEmailAsync.bind(this)}
+ className="zmdi zmdi-email"
+ />
+ </div>
+ <div className="pl1">
+ <i
+ style={{ cursor: 'pointer', fontSize: 29 }}
+ onClick={this._shareViaTwitterAsync.bind(this)}
+ className="zmdi zmdi-twitter-box"
+ />
+ </div>
+ </div>
+ </div>
+ </div>
+ );
+ }
+ private async _shareViaTwitterAsync() {
+ const tweetText = encodeURIComponent(`Fill my order using the 0x protocol: ${this.state.shareLink}`);
+ window.open(`https://twitter.com/intent/tweet?text=${tweetText}`, 'Share your order', 'width=500,height=400');
+ }
+ private async _shareViaFacebook() {
+ (window as any).FB.ui(
+ {
+ display: 'popup',
+ href: this.state.shareLink,
+ method: 'share',
+ },
+ _.noop,
+ );
+ }
+ private async _shareViaEmailAsync() {
+ const encodedSubject = encodeURIComponent("Let's trade using the 0x protocol");
+ const encodedBody = encodeURIComponent(`I generated an order with the 0x protocol.
You can see and fill it here: ${this.state.shareLink}`);
- const mailToLink = `mailto:mail@example.org?subject=${encodedSubject}&body=${encodedBody}`;
- window.open(mailToLink, '_blank');
- }
- private async _setShareLinkAsync() {
- const shareLink = await this._generateShareLinkAsync();
- this.setState({
- shareLink,
- });
- }
- private async _generateShareLinkAsync(): Promise<string> {
- const longUrl = encodeURIComponent(this._getOrderUrl());
- const bitlyRequestUrl = `${constants.URL_BITLY_API}/v3/shorten?access_token=${
- configs.BITLY_ACCESS_TOKEN
- }&longUrl=${longUrl}`;
- const response = await fetch(bitlyRequestUrl);
- const responseBody = await response.text();
- const bodyObj = JSON.parse(responseBody);
- if (response.status !== 200 || bodyObj.status_code !== 200) {
- // TODO: Show error message in UI
- utils.consoleLog(`Unexpected status code: ${response.status} -> ${responseBody}`);
- await errorReporter.reportAsync(new Error(`Bitly returned non-200: ${JSON.stringify(response)}`));
- return '';
- }
- return bodyObj.data.url;
- }
- private _getOrderUrl() {
- const order = utils.generateOrder(
- this.props.networkId,
- this.props.exchangeContractIfExists,
- this.props.sideToAssetToken,
- this.props.orderExpiryTimestamp,
- this.props.orderTakerAddress,
- this.props.orderMakerAddress,
- this.props.orderMakerFee,
- this.props.orderTakerFee,
- this.props.orderFeeRecipient,
- this.props.orderSignatureData,
- this.props.tokenByAddress,
- this.props.orderSalt,
- );
- const orderJSONString = JSON.stringify(order);
- const orderUrl = `${configs.BASE_URL}${WebsitePaths.Portal}/fill?order=${orderJSONString}`;
- return orderUrl;
- }
+ const mailToLink = `mailto:mail@example.org?subject=${encodedSubject}&body=${encodedBody}`;
+ window.open(mailToLink, '_blank');
+ }
+ private async _setShareLinkAsync() {
+ const shareLink = await this._generateShareLinkAsync();
+ this.setState({
+ shareLink,
+ });
+ }
+ private async _generateShareLinkAsync(): Promise<string> {
+ const longUrl = encodeURIComponent(this._getOrderUrl());
+ const bitlyRequestUrl = `${constants.URL_BITLY_API}/v3/shorten?access_token=${
+ configs.BITLY_ACCESS_TOKEN
+ }&longUrl=${longUrl}`;
+ const response = await fetch(bitlyRequestUrl);
+ const responseBody = await response.text();
+ const bodyObj = JSON.parse(responseBody);
+ if (response.status !== 200 || bodyObj.status_code !== 200) {
+ // TODO: Show error message in UI
+ utils.consoleLog(`Unexpected status code: ${response.status} -> ${responseBody}`);
+ await errorReporter.reportAsync(new Error(`Bitly returned non-200: ${JSON.stringify(response)}`));
+ return '';
+ }
+ return bodyObj.data.url;
+ }
+ private _getOrderUrl() {
+ const order = utils.generateOrder(
+ this.props.networkId,
+ this.props.exchangeContractIfExists,
+ this.props.sideToAssetToken,
+ this.props.orderExpiryTimestamp,
+ this.props.orderTakerAddress,
+ this.props.orderMakerAddress,
+ this.props.orderMakerFee,
+ this.props.orderTakerFee,
+ this.props.orderFeeRecipient,
+ this.props.orderSignatureData,
+ this.props.tokenByAddress,
+ this.props.orderSalt,
+ );
+ const orderJSONString = JSON.stringify(order);
+ const orderUrl = `${configs.BASE_URL}${WebsitePaths.Portal}/fill?order=${orderJSONString}`;
+ return orderUrl;
+ }
}
diff --git a/packages/website/ts/components/portal.tsx b/packages/website/ts/components/portal.tsx
index e163a1fa2..e2e28e8b6 100644
--- a/packages/website/ts/components/portal.tsx
+++ b/packages/website/ts/components/portal.tsx
@@ -23,14 +23,14 @@ import { Dispatcher } from 'ts/redux/dispatcher';
import { orderSchema } from 'ts/schemas/order_schema';
import { SchemaValidator } from 'ts/schemas/validator';
import {
- BlockchainErrs,
- HashData,
- Order,
- ScreenWidths,
- Token,
- TokenByAddress,
- TokenStateByAddress,
- WebsitePaths,
+ BlockchainErrs,
+ HashData,
+ Order,
+ ScreenWidths,
+ Token,
+ TokenByAddress,
+ TokenStateByAddress,
+ WebsitePaths,
} from 'ts/types';
import { colors } from 'ts/utils/colors';
import { configs } from 'ts/utils/configs';
@@ -42,320 +42,320 @@ const THROTTLE_TIMEOUT = 100;
export interface PortalPassedProps {}
export interface PortalAllProps {
- blockchainErr: BlockchainErrs;
- blockchainIsLoaded: boolean;
- dispatcher: Dispatcher;
- hashData: HashData;
- networkId: number;
- nodeVersion: string;
- orderFillAmount: BigNumber;
- screenWidth: ScreenWidths;
- tokenByAddress: TokenByAddress;
- tokenStateByAddress: TokenStateByAddress;
- userEtherBalance: BigNumber;
- userAddress: string;
- shouldBlockchainErrDialogBeOpen: boolean;
- userSuppliedOrderCache: Order;
- location: Location;
- flashMessage?: string | React.ReactNode;
+ blockchainErr: BlockchainErrs;
+ blockchainIsLoaded: boolean;
+ dispatcher: Dispatcher;
+ hashData: HashData;
+ networkId: number;
+ nodeVersion: string;
+ orderFillAmount: BigNumber;
+ screenWidth: ScreenWidths;
+ tokenByAddress: TokenByAddress;
+ tokenStateByAddress: TokenStateByAddress;
+ userEtherBalance: BigNumber;
+ userAddress: string;
+ shouldBlockchainErrDialogBeOpen: boolean;
+ userSuppliedOrderCache: Order;
+ location: Location;
+ flashMessage?: string | React.ReactNode;
}
interface PortalAllState {
- prevNetworkId: number;
- prevNodeVersion: string;
- prevUserAddress: string;
- prevPathname: string;
- isDisclaimerDialogOpen: boolean;
- isWethNoticeDialogOpen: boolean;
+ prevNetworkId: number;
+ prevNodeVersion: string;
+ prevUserAddress: string;
+ prevPathname: string;
+ isDisclaimerDialogOpen: boolean;
+ isWethNoticeDialogOpen: boolean;
}
export class Portal extends React.Component<PortalAllProps, PortalAllState> {
- private _blockchain: Blockchain;
- private _sharedOrderIfExists: Order;
- private _throttledScreenWidthUpdate: () => void;
- public static hasAlreadyDismissedWethNotice() {
- const didDismissWethNotice = localStorage.getItemIfExists(constants.LOCAL_STORAGE_KEY_DISMISS_WETH_NOTICE);
- const hasAlreadyDismissedWethNotice = !_.isUndefined(didDismissWethNotice) && !_.isEmpty(didDismissWethNotice);
- return hasAlreadyDismissedWethNotice;
- }
- constructor(props: PortalAllProps) {
- super(props);
- this._sharedOrderIfExists = this._getSharedOrderIfExists();
- this._throttledScreenWidthUpdate = _.throttle(this._updateScreenWidth.bind(this), THROTTLE_TIMEOUT);
+ private _blockchain: Blockchain;
+ private _sharedOrderIfExists: Order;
+ private _throttledScreenWidthUpdate: () => void;
+ public static hasAlreadyDismissedWethNotice() {
+ const didDismissWethNotice = localStorage.getItemIfExists(constants.LOCAL_STORAGE_KEY_DISMISS_WETH_NOTICE);
+ const hasAlreadyDismissedWethNotice = !_.isUndefined(didDismissWethNotice) && !_.isEmpty(didDismissWethNotice);
+ return hasAlreadyDismissedWethNotice;
+ }
+ constructor(props: PortalAllProps) {
+ super(props);
+ this._sharedOrderIfExists = this._getSharedOrderIfExists();
+ this._throttledScreenWidthUpdate = _.throttle(this._updateScreenWidth.bind(this), THROTTLE_TIMEOUT);
- const isViewingBalances = _.includes(props.location.pathname, `${WebsitePaths.Portal}/balances`);
- const hasAlreadyDismissedWethNotice = Portal.hasAlreadyDismissedWethNotice();
+ const isViewingBalances = _.includes(props.location.pathname, `${WebsitePaths.Portal}/balances`);
+ const hasAlreadyDismissedWethNotice = Portal.hasAlreadyDismissedWethNotice();
- const didAcceptPortalDisclaimer = localStorage.getItemIfExists(constants.LOCAL_STORAGE_KEY_ACCEPT_DISCLAIMER);
- const hasAcceptedDisclaimer =
- !_.isUndefined(didAcceptPortalDisclaimer) && !_.isEmpty(didAcceptPortalDisclaimer);
- this.state = {
- prevNetworkId: this.props.networkId,
- prevNodeVersion: this.props.nodeVersion,
- prevUserAddress: this.props.userAddress,
- prevPathname: this.props.location.pathname,
- isDisclaimerDialogOpen: !hasAcceptedDisclaimer,
- isWethNoticeDialogOpen: !hasAlreadyDismissedWethNotice && isViewingBalances,
- };
- }
- public componentDidMount() {
- window.addEventListener('resize', this._throttledScreenWidthUpdate);
- window.scrollTo(0, 0);
- }
- public componentWillMount() {
- this._blockchain = new Blockchain(this.props.dispatcher);
- }
- public componentWillUnmount() {
- this._blockchain.destroy();
- window.removeEventListener('resize', this._throttledScreenWidthUpdate);
- // We re-set the entire redux state when the portal is unmounted so that when it is re-rendered
- // the initialization process always occurs from the same base state. This helps avoid
- // initialization inconsistencies (i.e While the portal was unrendered, the user might have
- // become disconnected from their backing Ethereum node, changes user accounts, etc...)
- this.props.dispatcher.resetState();
- }
- public componentWillReceiveProps(nextProps: PortalAllProps) {
- if (nextProps.networkId !== this.state.prevNetworkId) {
- // tslint:disable-next-line:no-floating-promises
- this._blockchain.networkIdUpdatedFireAndForgetAsync(nextProps.networkId);
- this.setState({
- prevNetworkId: nextProps.networkId,
- });
- }
- if (nextProps.userAddress !== this.state.prevUserAddress) {
- // tslint:disable-next-line:no-floating-promises
- this._blockchain.userAddressUpdatedFireAndForgetAsync(nextProps.userAddress);
- if (!_.isEmpty(nextProps.userAddress) && nextProps.blockchainIsLoaded) {
- const tokens = _.values(nextProps.tokenByAddress);
- // tslint:disable-next-line:no-floating-promises
- this._updateBalanceAndAllowanceWithLoadingScreenAsync(tokens);
- }
- this.setState({
- prevUserAddress: nextProps.userAddress,
- });
- }
- if (nextProps.nodeVersion !== this.state.prevNodeVersion) {
- // tslint:disable-next-line:no-floating-promises
- this._blockchain.nodeVersionUpdatedFireAndForgetAsync(nextProps.nodeVersion);
- }
- if (nextProps.location.pathname !== this.state.prevPathname) {
- const isViewingBalances = _.includes(nextProps.location.pathname, `${WebsitePaths.Portal}/balances`);
- const hasAlreadyDismissedWethNotice = Portal.hasAlreadyDismissedWethNotice();
- this.setState({
- prevPathname: nextProps.location.pathname,
- isWethNoticeDialogOpen: !hasAlreadyDismissedWethNotice && isViewingBalances,
- });
- }
- }
- public render() {
- const updateShouldBlockchainErrDialogBeOpen = this.props.dispatcher.updateShouldBlockchainErrDialogBeOpen.bind(
- this.props.dispatcher,
- );
- const portalStyle: React.CSSProperties = {
- minHeight: '100vh',
- display: 'flex',
- flexDirection: 'column',
- justifyContent: 'space-between',
- };
- const portalMenuContainerStyle: React.CSSProperties = {
- overflow: 'hidden',
- backgroundColor: colors.darkestGrey,
- color: colors.white,
- };
- return (
- <div style={portalStyle}>
- <DocumentTitle title="0x Portal DApp" />
- <TopBar
- userAddress={this.props.userAddress}
- blockchainIsLoaded={this.props.blockchainIsLoaded}
- location={this.props.location}
- />
- <div id="portal" className="mx-auto max-width-4" style={{ width: '100%' }}>
- <Paper className="mb3 mt2">
- {!configs.IS_MAINNET_ENABLED && this.props.networkId === constants.NETWORK_ID_MAINNET ? (
- <div className="p3 center">
- <div className="h2 py2">Mainnet unavailable</div>
- <div className="mx-auto pb2 pt2">
- <img src="/images/zrx_token.png" style={{ width: 150 }} />
- </div>
- <div>
- 0x portal is currently unavailable on the Ethereum mainnet.
- <div>To try it out, switch to the Kovan test network (networkId: 42).</div>
- <div className="py2">Check back soon!</div>
- </div>
- </div>
- ) : (
- <div className="mx-auto flex">
- <div className="col col-2 pr2 pt1 sm-hide xs-hide" style={portalMenuContainerStyle}>
- <PortalMenu menuItemStyle={{ color: colors.white }} />
- </div>
- <div className="col col-12 lg-col-10 md-col-10 sm-col sm-col-12">
- <div className="py2" style={{ backgroundColor: colors.grey50 }}>
- {this.props.blockchainIsLoaded ? (
- <Switch>
- <Route
- path={`${WebsitePaths.Portal}/weth`}
- render={this._renderEthWrapper.bind(this)}
- />
- <Route
- path={`${WebsitePaths.Portal}/fill`}
- render={this._renderFillOrder.bind(this)}
- />
- <Route
- path={`${WebsitePaths.Portal}/balances`}
- render={this._renderTokenBalances.bind(this)}
- />
- <Route
- path={`${WebsitePaths.Portal}/trades`}
- component={this._renderTradeHistory.bind(this)}
- />
- <Route
- path={`${WebsitePaths.Home}`}
- render={this._renderGenerateOrderForm.bind(this)}
- />
- </Switch>
- ) : (
- <Loading />
- )}
- </div>
- </div>
- </div>
- )}
- </Paper>
- <BlockchainErrDialog
- blockchain={this._blockchain}
- blockchainErr={this.props.blockchainErr}
- isOpen={this.props.shouldBlockchainErrDialogBeOpen}
- userAddress={this.props.userAddress}
- toggleDialogFn={updateShouldBlockchainErrDialogBeOpen}
- networkId={this.props.networkId}
- />
- <WrappedEthSectionNoticeDialog
- isOpen={this.state.isWethNoticeDialogOpen}
- onToggleDialog={this._onWethNoticeAccepted.bind(this)}
- />
- <PortalDisclaimerDialog
- isOpen={this.state.isDisclaimerDialogOpen}
- onToggleDialog={this._onPortalDisclaimerAccepted.bind(this)}
- />
- <FlashMessage dispatcher={this.props.dispatcher} flashMessage={this.props.flashMessage} />
- </div>
- <Footer />
- </div>
- );
- }
- private _renderEthWrapper() {
- return (
- <EthWrappers
- networkId={this.props.networkId}
- blockchain={this._blockchain}
- dispatcher={this.props.dispatcher}
- tokenByAddress={this.props.tokenByAddress}
- tokenStateByAddress={this.props.tokenStateByAddress}
- userAddress={this.props.userAddress}
- userEtherBalance={this.props.userEtherBalance}
- />
- );
- }
- private _renderTradeHistory() {
- return (
- <TradeHistory
- tokenByAddress={this.props.tokenByAddress}
- userAddress={this.props.userAddress}
- networkId={this.props.networkId}
- />
- );
- }
- private _renderTokenBalances() {
- return (
- <TokenBalances
- blockchain={this._blockchain}
- blockchainErr={this.props.blockchainErr}
- blockchainIsLoaded={this.props.blockchainIsLoaded}
- dispatcher={this.props.dispatcher}
- screenWidth={this.props.screenWidth}
- tokenByAddress={this.props.tokenByAddress}
- tokenStateByAddress={this.props.tokenStateByAddress}
- userAddress={this.props.userAddress}
- userEtherBalance={this.props.userEtherBalance}
- networkId={this.props.networkId}
- />
- );
- }
- private _renderFillOrder(match: any, location: Location, history: History) {
- const initialFillOrder = !_.isUndefined(this.props.userSuppliedOrderCache)
- ? this.props.userSuppliedOrderCache
- : this._sharedOrderIfExists;
- return (
- <FillOrder
- blockchain={this._blockchain}
- blockchainErr={this.props.blockchainErr}
- initialOrder={initialFillOrder}
- isOrderInUrl={!_.isUndefined(this._sharedOrderIfExists)}
- orderFillAmount={this.props.orderFillAmount}
- networkId={this.props.networkId}
- userAddress={this.props.userAddress}
- tokenByAddress={this.props.tokenByAddress}
- tokenStateByAddress={this.props.tokenStateByAddress}
- dispatcher={this.props.dispatcher}
- />
- );
- }
- private _renderGenerateOrderForm(match: any, location: Location, history: History) {
- return (
- <GenerateOrderForm
- blockchain={this._blockchain}
- hashData={this.props.hashData}
- dispatcher={this.props.dispatcher}
- />
- );
- }
- private _onPortalDisclaimerAccepted() {
- localStorage.setItem(constants.LOCAL_STORAGE_KEY_ACCEPT_DISCLAIMER, 'set');
- this.setState({
- isDisclaimerDialogOpen: false,
- });
- }
- private _onWethNoticeAccepted() {
- localStorage.setItem(constants.LOCAL_STORAGE_KEY_DISMISS_WETH_NOTICE, 'set');
- this.setState({
- isWethNoticeDialogOpen: false,
- });
- }
- private _getSharedOrderIfExists(): Order | undefined {
- const queryString = window.location.search;
- if (queryString.length === 0) {
- return undefined;
- }
- const queryParams = queryString.substring(1).split('&');
- const orderQueryParam = _.find(queryParams, queryParam => {
- const queryPair = queryParam.split('=');
- return queryPair[0] === 'order';
- });
- if (_.isUndefined(orderQueryParam)) {
- return undefined;
- }
- const orderPair = orderQueryParam.split('=');
- if (orderPair.length !== 2) {
- return undefined;
- }
+ const didAcceptPortalDisclaimer = localStorage.getItemIfExists(constants.LOCAL_STORAGE_KEY_ACCEPT_DISCLAIMER);
+ const hasAcceptedDisclaimer =
+ !_.isUndefined(didAcceptPortalDisclaimer) && !_.isEmpty(didAcceptPortalDisclaimer);
+ this.state = {
+ prevNetworkId: this.props.networkId,
+ prevNodeVersion: this.props.nodeVersion,
+ prevUserAddress: this.props.userAddress,
+ prevPathname: this.props.location.pathname,
+ isDisclaimerDialogOpen: !hasAcceptedDisclaimer,
+ isWethNoticeDialogOpen: !hasAlreadyDismissedWethNotice && isViewingBalances,
+ };
+ }
+ public componentDidMount() {
+ window.addEventListener('resize', this._throttledScreenWidthUpdate);
+ window.scrollTo(0, 0);
+ }
+ public componentWillMount() {
+ this._blockchain = new Blockchain(this.props.dispatcher);
+ }
+ public componentWillUnmount() {
+ this._blockchain.destroy();
+ window.removeEventListener('resize', this._throttledScreenWidthUpdate);
+ // We re-set the entire redux state when the portal is unmounted so that when it is re-rendered
+ // the initialization process always occurs from the same base state. This helps avoid
+ // initialization inconsistencies (i.e While the portal was unrendered, the user might have
+ // become disconnected from their backing Ethereum node, changes user accounts, etc...)
+ this.props.dispatcher.resetState();
+ }
+ public componentWillReceiveProps(nextProps: PortalAllProps) {
+ if (nextProps.networkId !== this.state.prevNetworkId) {
+ // tslint:disable-next-line:no-floating-promises
+ this._blockchain.networkIdUpdatedFireAndForgetAsync(nextProps.networkId);
+ this.setState({
+ prevNetworkId: nextProps.networkId,
+ });
+ }
+ if (nextProps.userAddress !== this.state.prevUserAddress) {
+ // tslint:disable-next-line:no-floating-promises
+ this._blockchain.userAddressUpdatedFireAndForgetAsync(nextProps.userAddress);
+ if (!_.isEmpty(nextProps.userAddress) && nextProps.blockchainIsLoaded) {
+ const tokens = _.values(nextProps.tokenByAddress);
+ // tslint:disable-next-line:no-floating-promises
+ this._updateBalanceAndAllowanceWithLoadingScreenAsync(tokens);
+ }
+ this.setState({
+ prevUserAddress: nextProps.userAddress,
+ });
+ }
+ if (nextProps.nodeVersion !== this.state.prevNodeVersion) {
+ // tslint:disable-next-line:no-floating-promises
+ this._blockchain.nodeVersionUpdatedFireAndForgetAsync(nextProps.nodeVersion);
+ }
+ if (nextProps.location.pathname !== this.state.prevPathname) {
+ const isViewingBalances = _.includes(nextProps.location.pathname, `${WebsitePaths.Portal}/balances`);
+ const hasAlreadyDismissedWethNotice = Portal.hasAlreadyDismissedWethNotice();
+ this.setState({
+ prevPathname: nextProps.location.pathname,
+ isWethNoticeDialogOpen: !hasAlreadyDismissedWethNotice && isViewingBalances,
+ });
+ }
+ }
+ public render() {
+ const updateShouldBlockchainErrDialogBeOpen = this.props.dispatcher.updateShouldBlockchainErrDialogBeOpen.bind(
+ this.props.dispatcher,
+ );
+ const portalStyle: React.CSSProperties = {
+ minHeight: '100vh',
+ display: 'flex',
+ flexDirection: 'column',
+ justifyContent: 'space-between',
+ };
+ const portalMenuContainerStyle: React.CSSProperties = {
+ overflow: 'hidden',
+ backgroundColor: colors.darkestGrey,
+ color: colors.white,
+ };
+ return (
+ <div style={portalStyle}>
+ <DocumentTitle title="0x Portal DApp" />
+ <TopBar
+ userAddress={this.props.userAddress}
+ blockchainIsLoaded={this.props.blockchainIsLoaded}
+ location={this.props.location}
+ />
+ <div id="portal" className="mx-auto max-width-4" style={{ width: '100%' }}>
+ <Paper className="mb3 mt2">
+ {!configs.IS_MAINNET_ENABLED && this.props.networkId === constants.NETWORK_ID_MAINNET ? (
+ <div className="p3 center">
+ <div className="h2 py2">Mainnet unavailable</div>
+ <div className="mx-auto pb2 pt2">
+ <img src="/images/zrx_token.png" style={{ width: 150 }} />
+ </div>
+ <div>
+ 0x portal is currently unavailable on the Ethereum mainnet.
+ <div>To try it out, switch to the Kovan test network (networkId: 42).</div>
+ <div className="py2">Check back soon!</div>
+ </div>
+ </div>
+ ) : (
+ <div className="mx-auto flex">
+ <div className="col col-2 pr2 pt1 sm-hide xs-hide" style={portalMenuContainerStyle}>
+ <PortalMenu menuItemStyle={{ color: colors.white }} />
+ </div>
+ <div className="col col-12 lg-col-10 md-col-10 sm-col sm-col-12">
+ <div className="py2" style={{ backgroundColor: colors.grey50 }}>
+ {this.props.blockchainIsLoaded ? (
+ <Switch>
+ <Route
+ path={`${WebsitePaths.Portal}/weth`}
+ render={this._renderEthWrapper.bind(this)}
+ />
+ <Route
+ path={`${WebsitePaths.Portal}/fill`}
+ render={this._renderFillOrder.bind(this)}
+ />
+ <Route
+ path={`${WebsitePaths.Portal}/balances`}
+ render={this._renderTokenBalances.bind(this)}
+ />
+ <Route
+ path={`${WebsitePaths.Portal}/trades`}
+ component={this._renderTradeHistory.bind(this)}
+ />
+ <Route
+ path={`${WebsitePaths.Home}`}
+ render={this._renderGenerateOrderForm.bind(this)}
+ />
+ </Switch>
+ ) : (
+ <Loading />
+ )}
+ </div>
+ </div>
+ </div>
+ )}
+ </Paper>
+ <BlockchainErrDialog
+ blockchain={this._blockchain}
+ blockchainErr={this.props.blockchainErr}
+ isOpen={this.props.shouldBlockchainErrDialogBeOpen}
+ userAddress={this.props.userAddress}
+ toggleDialogFn={updateShouldBlockchainErrDialogBeOpen}
+ networkId={this.props.networkId}
+ />
+ <WrappedEthSectionNoticeDialog
+ isOpen={this.state.isWethNoticeDialogOpen}
+ onToggleDialog={this._onWethNoticeAccepted.bind(this)}
+ />
+ <PortalDisclaimerDialog
+ isOpen={this.state.isDisclaimerDialogOpen}
+ onToggleDialog={this._onPortalDisclaimerAccepted.bind(this)}
+ />
+ <FlashMessage dispatcher={this.props.dispatcher} flashMessage={this.props.flashMessage} />
+ </div>
+ <Footer />
+ </div>
+ );
+ }
+ private _renderEthWrapper() {
+ return (
+ <EthWrappers
+ networkId={this.props.networkId}
+ blockchain={this._blockchain}
+ dispatcher={this.props.dispatcher}
+ tokenByAddress={this.props.tokenByAddress}
+ tokenStateByAddress={this.props.tokenStateByAddress}
+ userAddress={this.props.userAddress}
+ userEtherBalance={this.props.userEtherBalance}
+ />
+ );
+ }
+ private _renderTradeHistory() {
+ return (
+ <TradeHistory
+ tokenByAddress={this.props.tokenByAddress}
+ userAddress={this.props.userAddress}
+ networkId={this.props.networkId}
+ />
+ );
+ }
+ private _renderTokenBalances() {
+ return (
+ <TokenBalances
+ blockchain={this._blockchain}
+ blockchainErr={this.props.blockchainErr}
+ blockchainIsLoaded={this.props.blockchainIsLoaded}
+ dispatcher={this.props.dispatcher}
+ screenWidth={this.props.screenWidth}
+ tokenByAddress={this.props.tokenByAddress}
+ tokenStateByAddress={this.props.tokenStateByAddress}
+ userAddress={this.props.userAddress}
+ userEtherBalance={this.props.userEtherBalance}
+ networkId={this.props.networkId}
+ />
+ );
+ }
+ private _renderFillOrder(match: any, location: Location, history: History) {
+ const initialFillOrder = !_.isUndefined(this.props.userSuppliedOrderCache)
+ ? this.props.userSuppliedOrderCache
+ : this._sharedOrderIfExists;
+ return (
+ <FillOrder
+ blockchain={this._blockchain}
+ blockchainErr={this.props.blockchainErr}
+ initialOrder={initialFillOrder}
+ isOrderInUrl={!_.isUndefined(this._sharedOrderIfExists)}
+ orderFillAmount={this.props.orderFillAmount}
+ networkId={this.props.networkId}
+ userAddress={this.props.userAddress}
+ tokenByAddress={this.props.tokenByAddress}
+ tokenStateByAddress={this.props.tokenStateByAddress}
+ dispatcher={this.props.dispatcher}
+ />
+ );
+ }
+ private _renderGenerateOrderForm(match: any, location: Location, history: History) {
+ return (
+ <GenerateOrderForm
+ blockchain={this._blockchain}
+ hashData={this.props.hashData}
+ dispatcher={this.props.dispatcher}
+ />
+ );
+ }
+ private _onPortalDisclaimerAccepted() {
+ localStorage.setItem(constants.LOCAL_STORAGE_KEY_ACCEPT_DISCLAIMER, 'set');
+ this.setState({
+ isDisclaimerDialogOpen: false,
+ });
+ }
+ private _onWethNoticeAccepted() {
+ localStorage.setItem(constants.LOCAL_STORAGE_KEY_DISMISS_WETH_NOTICE, 'set');
+ this.setState({
+ isWethNoticeDialogOpen: false,
+ });
+ }
+ private _getSharedOrderIfExists(): Order | undefined {
+ const queryString = window.location.search;
+ if (queryString.length === 0) {
+ return undefined;
+ }
+ const queryParams = queryString.substring(1).split('&');
+ const orderQueryParam = _.find(queryParams, queryParam => {
+ const queryPair = queryParam.split('=');
+ return queryPair[0] === 'order';
+ });
+ if (_.isUndefined(orderQueryParam)) {
+ return undefined;
+ }
+ const orderPair = orderQueryParam.split('=');
+ if (orderPair.length !== 2) {
+ return undefined;
+ }
- const validator = new SchemaValidator();
- const order = JSON.parse(decodeURIComponent(orderPair[1]));
- const validationResult = validator.validate(order, orderSchema);
- if (validationResult.errors.length > 0) {
- utils.consoleLog(`Invalid shared order: ${validationResult.errors}`);
- return undefined;
- }
- return order;
- }
- private _updateScreenWidth() {
- const newScreenWidth = utils.getScreenWidth();
- this.props.dispatcher.updateScreenWidth(newScreenWidth);
- }
- private async _updateBalanceAndAllowanceWithLoadingScreenAsync(tokens: Token[]) {
- this.props.dispatcher.updateBlockchainIsLoaded(false);
- await this._blockchain.updateTokenBalancesAndAllowancesAsync(tokens);
- this.props.dispatcher.updateBlockchainIsLoaded(true);
- }
+ const validator = new SchemaValidator();
+ const order = JSON.parse(decodeURIComponent(orderPair[1]));
+ const validationResult = validator.validate(order, orderSchema);
+ if (validationResult.errors.length > 0) {
+ utils.consoleLog(`Invalid shared order: ${validationResult.errors}`);
+ return undefined;
+ }
+ return order;
+ }
+ private _updateScreenWidth() {
+ const newScreenWidth = utils.getScreenWidth();
+ this.props.dispatcher.updateScreenWidth(newScreenWidth);
+ }
+ private async _updateBalanceAndAllowanceWithLoadingScreenAsync(tokens: Token[]) {
+ this.props.dispatcher.updateBlockchainIsLoaded(false);
+ await this._blockchain.updateTokenBalancesAndAllowancesAsync(tokens);
+ this.props.dispatcher.updateBlockchainIsLoaded(true);
+ }
}
diff --git a/packages/website/ts/components/portal_menu.tsx b/packages/website/ts/components/portal_menu.tsx
index b025f527e..a2f9340c8 100644
--- a/packages/website/ts/components/portal_menu.tsx
+++ b/packages/website/ts/components/portal_menu.tsx
@@ -4,70 +4,70 @@ import { MenuItem } from 'ts/components/ui/menu_item';
import { WebsitePaths } from 'ts/types';
export interface PortalMenuProps {
- menuItemStyle: React.CSSProperties;
- onClick?: () => void;
+ menuItemStyle: React.CSSProperties;
+ onClick?: () => void;
}
interface PortalMenuState {}
export class PortalMenu extends React.Component<PortalMenuProps, PortalMenuState> {
- public static defaultProps: Partial<PortalMenuProps> = {
- onClick: _.noop,
- };
- public render() {
- return (
- <div>
- <MenuItem
- style={this.props.menuItemStyle}
- className="py2"
- to={`${WebsitePaths.Portal}`}
- onClick={this.props.onClick.bind(this)}
- >
- {this._renderMenuItemWithIcon('Generate order', 'zmdi-arrow-right-top')}
- </MenuItem>
- <MenuItem
- style={this.props.menuItemStyle}
- className="py2"
- to={`${WebsitePaths.Portal}/fill`}
- onClick={this.props.onClick.bind(this)}
- >
- {this._renderMenuItemWithIcon('Fill order', 'zmdi-arrow-left-bottom')}
- </MenuItem>
- <MenuItem
- style={this.props.menuItemStyle}
- className="py2"
- to={`${WebsitePaths.Portal}/balances`}
- onClick={this.props.onClick.bind(this)}
- >
- {this._renderMenuItemWithIcon('Balances', 'zmdi-balance-wallet')}
- </MenuItem>
- <MenuItem
- style={this.props.menuItemStyle}
- className="py2"
- to={`${WebsitePaths.Portal}/trades`}
- onClick={this.props.onClick.bind(this)}
- >
- {this._renderMenuItemWithIcon('Trade history', 'zmdi-format-list-bulleted')}
- </MenuItem>
- <MenuItem
- style={this.props.menuItemStyle}
- className="py2"
- to={`${WebsitePaths.Portal}/weth`}
- onClick={this.props.onClick.bind(this)}
- >
- {this._renderMenuItemWithIcon('Wrap ETH', 'zmdi-circle-o')}
- </MenuItem>
- </div>
- );
- }
- private _renderMenuItemWithIcon(title: string, iconName: string) {
- return (
- <div className="flex" style={{ fontWeight: 100 }}>
- <div className="pr1 pl2">
- <i style={{ fontSize: 20 }} className={`zmdi ${iconName}`} />
- </div>
- <div className="pl1">{title}</div>
- </div>
- );
- }
+ public static defaultProps: Partial<PortalMenuProps> = {
+ onClick: _.noop,
+ };
+ public render() {
+ return (
+ <div>
+ <MenuItem
+ style={this.props.menuItemStyle}
+ className="py2"
+ to={`${WebsitePaths.Portal}`}
+ onClick={this.props.onClick.bind(this)}
+ >
+ {this._renderMenuItemWithIcon('Generate order', 'zmdi-arrow-right-top')}
+ </MenuItem>
+ <MenuItem
+ style={this.props.menuItemStyle}
+ className="py2"
+ to={`${WebsitePaths.Portal}/fill`}
+ onClick={this.props.onClick.bind(this)}
+ >
+ {this._renderMenuItemWithIcon('Fill order', 'zmdi-arrow-left-bottom')}
+ </MenuItem>
+ <MenuItem
+ style={this.props.menuItemStyle}
+ className="py2"
+ to={`${WebsitePaths.Portal}/balances`}
+ onClick={this.props.onClick.bind(this)}
+ >
+ {this._renderMenuItemWithIcon('Balances', 'zmdi-balance-wallet')}
+ </MenuItem>
+ <MenuItem
+ style={this.props.menuItemStyle}
+ className="py2"
+ to={`${WebsitePaths.Portal}/trades`}
+ onClick={this.props.onClick.bind(this)}
+ >
+ {this._renderMenuItemWithIcon('Trade history', 'zmdi-format-list-bulleted')}
+ </MenuItem>
+ <MenuItem
+ style={this.props.menuItemStyle}
+ className="py2"
+ to={`${WebsitePaths.Portal}/weth`}
+ onClick={this.props.onClick.bind(this)}
+ >
+ {this._renderMenuItemWithIcon('Wrap ETH', 'zmdi-circle-o')}
+ </MenuItem>
+ </div>
+ );
+ }
+ private _renderMenuItemWithIcon(title: string, iconName: string) {
+ return (
+ <div className="flex" style={{ fontWeight: 100 }}>
+ <div className="pr1 pl2">
+ <i style={{ fontSize: 20 }} className={`zmdi ${iconName}`} />
+ </div>
+ <div className="pl1">{title}</div>
+ </div>
+ );
+ }
}
diff --git a/packages/website/ts/components/send_button.tsx b/packages/website/ts/components/send_button.tsx
index f97d9250b..f94ec346a 100644
--- a/packages/website/ts/components/send_button.tsx
+++ b/packages/website/ts/components/send_button.tsx
@@ -10,78 +10,78 @@ import { errorReporter } from 'ts/utils/error_reporter';
import { utils } from 'ts/utils/utils';
interface SendButtonProps {
- token: Token;
- tokenState: TokenState;
- dispatcher: Dispatcher;
- blockchain: Blockchain;
- onError: () => void;
+ token: Token;
+ tokenState: TokenState;
+ dispatcher: Dispatcher;
+ blockchain: Blockchain;
+ onError: () => void;
}
interface SendButtonState {
- isSendDialogVisible: boolean;
- isSending: boolean;
+ isSendDialogVisible: boolean;
+ isSending: boolean;
}
export class SendButton extends React.Component<SendButtonProps, SendButtonState> {
- public constructor(props: SendButtonProps) {
- super(props);
- this.state = {
- isSendDialogVisible: false,
- isSending: false,
- };
- }
- public render() {
- const labelStyle = this.state.isSending ? { fontSize: 10 } : {};
- return (
- <div>
- <RaisedButton
- style={{ width: '100%' }}
- labelStyle={labelStyle}
- disabled={this.state.isSending}
- label={this.state.isSending ? 'Sending...' : 'Send'}
- onClick={this._toggleSendDialog.bind(this)}
- />
- <SendDialog
- isOpen={this.state.isSendDialogVisible}
- onComplete={this._onSendAmountSelectedAsync.bind(this)}
- onCancelled={this._toggleSendDialog.bind(this)}
- token={this.props.token}
- tokenState={this.props.tokenState}
- />
- </div>
- );
- }
- private _toggleSendDialog() {
- this.setState({
- isSendDialogVisible: !this.state.isSendDialogVisible,
- });
- }
- private async _onSendAmountSelectedAsync(recipient: string, value: BigNumber) {
- this.setState({
- isSending: true,
- });
- this._toggleSendDialog();
- const token = this.props.token;
- const tokenState = this.props.tokenState;
- let balance = tokenState.balance;
- try {
- await this.props.blockchain.transferAsync(token, recipient, value);
- balance = balance.minus(value);
- this.props.dispatcher.replaceTokenBalanceByAddress(token.address, balance);
- } catch (err) {
- const errMsg = `${err}`;
- if (_.includes(errMsg, BlockchainCallErrs.UserHasNoAssociatedAddresses)) {
- this.props.dispatcher.updateShouldBlockchainErrDialogBeOpen(true);
- return;
- } else if (!_.includes(errMsg, 'User denied transaction')) {
- utils.consoleLog(`Unexpected error encountered: ${err}`);
- utils.consoleLog(err.stack);
- this.props.onError();
- await errorReporter.reportAsync(err);
- }
- }
- this.setState({
- isSending: false,
- });
- }
+ public constructor(props: SendButtonProps) {
+ super(props);
+ this.state = {
+ isSendDialogVisible: false,
+ isSending: false,
+ };
+ }
+ public render() {
+ const labelStyle = this.state.isSending ? { fontSize: 10 } : {};
+ return (
+ <div>
+ <RaisedButton
+ style={{ width: '100%' }}
+ labelStyle={labelStyle}
+ disabled={this.state.isSending}
+ label={this.state.isSending ? 'Sending...' : 'Send'}
+ onClick={this._toggleSendDialog.bind(this)}
+ />
+ <SendDialog
+ isOpen={this.state.isSendDialogVisible}
+ onComplete={this._onSendAmountSelectedAsync.bind(this)}
+ onCancelled={this._toggleSendDialog.bind(this)}
+ token={this.props.token}
+ tokenState={this.props.tokenState}
+ />
+ </div>
+ );
+ }
+ private _toggleSendDialog() {
+ this.setState({
+ isSendDialogVisible: !this.state.isSendDialogVisible,
+ });
+ }
+ private async _onSendAmountSelectedAsync(recipient: string, value: BigNumber) {
+ this.setState({
+ isSending: true,
+ });
+ this._toggleSendDialog();
+ const token = this.props.token;
+ const tokenState = this.props.tokenState;
+ let balance = tokenState.balance;
+ try {
+ await this.props.blockchain.transferAsync(token, recipient, value);
+ balance = balance.minus(value);
+ this.props.dispatcher.replaceTokenBalanceByAddress(token.address, balance);
+ } catch (err) {
+ const errMsg = `${err}`;
+ if (_.includes(errMsg, BlockchainCallErrs.UserHasNoAssociatedAddresses)) {
+ this.props.dispatcher.updateShouldBlockchainErrDialogBeOpen(true);
+ return;
+ } else if (!_.includes(errMsg, 'User denied transaction')) {
+ utils.consoleLog(`Unexpected error encountered: ${err}`);
+ utils.consoleLog(err.stack);
+ this.props.onError();
+ await errorReporter.reportAsync(err);
+ }
+ }
+ this.setState({
+ isSending: false,
+ });
+ }
}
diff --git a/packages/website/ts/components/token_balances.tsx b/packages/website/ts/components/token_balances.tsx
index 480652c34..2cef413c7 100644
--- a/packages/website/ts/components/token_balances.tsx
+++ b/packages/website/ts/components/token_balances.tsx
@@ -23,16 +23,16 @@ import { TokenIcon } from 'ts/components/ui/token_icon';
import { trackedTokenStorage } from 'ts/local_storage/tracked_token_storage';
import { Dispatcher } from 'ts/redux/dispatcher';
import {
- BalanceErrs,
- BlockchainCallErrs,
- BlockchainErrs,
- EtherscanLinkSuffixes,
- ScreenWidths,
- Styles,
- Token,
- TokenByAddress,
- TokenStateByAddress,
- TokenVisibility,
+ BalanceErrs,
+ BlockchainCallErrs,
+ BlockchainErrs,
+ EtherscanLinkSuffixes,
+ ScreenWidths,
+ Styles,
+ Token,
+ TokenByAddress,
+ TokenStateByAddress,
+ TokenVisibility,
} from 'ts/types';
import { colors } from 'ts/utils/colors';
import { configs } from 'ts/utils/configs';
@@ -53,554 +53,554 @@ const TOKEN_COL_SPAN_LG = 2;
const TOKEN_COL_SPAN_SM = 1;
const styles: Styles = {
- bgColor: {
- backgroundColor: colors.grey50,
- },
+ bgColor: {
+ backgroundColor: colors.grey50,
+ },
};
interface TokenBalancesProps {
- blockchain: Blockchain;
- blockchainErr: BlockchainErrs;
- blockchainIsLoaded: boolean;
- dispatcher: Dispatcher;
- screenWidth: ScreenWidths;
- tokenByAddress: TokenByAddress;
- tokenStateByAddress: TokenStateByAddress;
- userAddress: string;
- userEtherBalance: BigNumber;
- networkId: number;
+ blockchain: Blockchain;
+ blockchainErr: BlockchainErrs;
+ blockchainIsLoaded: boolean;
+ dispatcher: Dispatcher;
+ screenWidth: ScreenWidths;
+ tokenByAddress: TokenByAddress;
+ tokenStateByAddress: TokenStateByAddress;
+ userAddress: string;
+ userEtherBalance: BigNumber;
+ networkId: number;
}
interface TokenBalancesState {
- errorType: BalanceErrs;
- isBalanceSpinnerVisible: boolean;
- isDharmaDialogVisible: boolean;
- isZRXSpinnerVisible: boolean;
- currentZrxBalance?: BigNumber;
- isTokenPickerOpen: boolean;
- isAddingToken: boolean;
+ errorType: BalanceErrs;
+ isBalanceSpinnerVisible: boolean;
+ isDharmaDialogVisible: boolean;
+ isZRXSpinnerVisible: boolean;
+ currentZrxBalance?: BigNumber;
+ isTokenPickerOpen: boolean;
+ isAddingToken: boolean;
}
export class TokenBalances extends React.Component<TokenBalancesProps, TokenBalancesState> {
- public constructor(props: TokenBalancesProps) {
- super(props);
- this.state = {
- errorType: undefined,
- isBalanceSpinnerVisible: false,
- isZRXSpinnerVisible: false,
- isDharmaDialogVisible: DharmaLoanFrame.isAuthTokenPresent(),
- isTokenPickerOpen: false,
- isAddingToken: false,
- };
- }
- public componentWillReceiveProps(nextProps: TokenBalancesProps) {
- if (nextProps.userEtherBalance !== this.props.userEtherBalance) {
- if (this.state.isBalanceSpinnerVisible) {
- const receivedAmount = nextProps.userEtherBalance.minus(this.props.userEtherBalance);
- this.props.dispatcher.showFlashMessage(`Received ${receivedAmount.toString(10)} Kovan Ether`);
- }
- this.setState({
- isBalanceSpinnerVisible: false,
- });
- }
- const nextZrxToken = _.find(_.values(nextProps.tokenByAddress), t => t.symbol === ZRX_TOKEN_SYMBOL);
- const nextZrxTokenBalance = nextProps.tokenStateByAddress[nextZrxToken.address].balance;
- if (!_.isUndefined(this.state.currentZrxBalance) && !nextZrxTokenBalance.eq(this.state.currentZrxBalance)) {
- if (this.state.isZRXSpinnerVisible) {
- const receivedAmount = nextZrxTokenBalance.minus(this.state.currentZrxBalance);
- const receiveAmountInUnits = ZeroEx.toUnitAmount(receivedAmount, constants.DECIMAL_PLACES_ZRX);
- this.props.dispatcher.showFlashMessage(`Received ${receiveAmountInUnits.toString(10)} Kovan ZRX`);
- }
- this.setState({
- isZRXSpinnerVisible: false,
- currentZrxBalance: undefined,
- });
- }
- }
- public componentDidMount() {
- window.scrollTo(0, 0);
- }
- public render() {
- const errorDialogActions = [
- <FlatButton
- key="errorOkBtn"
- label="Ok"
- primary={true}
- onTouchTap={this._onErrorDialogToggle.bind(this, false)}
- />,
- ];
- const dharmaDialogActions = [
- <FlatButton
- key="dharmaCloseBtn"
- label="Close"
- primary={true}
- onTouchTap={this._onDharmaDialogToggle.bind(this, false)}
- />,
- ];
- const isTestNetwork = this.props.networkId === constants.NETWORK_ID_TESTNET;
- const dharmaButtonColumnStyle = {
- paddingLeft: 3,
- display: isTestNetwork ? 'table-cell' : 'none',
- };
- const stubColumnStyle = {
- display: isTestNetwork ? 'none' : 'table-cell',
- };
- const allTokenRowHeight = _.size(this.props.tokenByAddress) * TOKEN_TABLE_ROW_HEIGHT;
- const tokenTableHeight =
- allTokenRowHeight < MAX_TOKEN_TABLE_HEIGHT ? allTokenRowHeight : MAX_TOKEN_TABLE_HEIGHT;
- const isSmallScreen = this.props.screenWidth === ScreenWidths.Sm;
- const tokenColSpan = isSmallScreen ? TOKEN_COL_SPAN_SM : TOKEN_COL_SPAN_LG;
- const dharmaLoanExplanation =
- 'If you need access to larger amounts of ether,<br> \
+ public constructor(props: TokenBalancesProps) {
+ super(props);
+ this.state = {
+ errorType: undefined,
+ isBalanceSpinnerVisible: false,
+ isZRXSpinnerVisible: false,
+ isDharmaDialogVisible: DharmaLoanFrame.isAuthTokenPresent(),
+ isTokenPickerOpen: false,
+ isAddingToken: false,
+ };
+ }
+ public componentWillReceiveProps(nextProps: TokenBalancesProps) {
+ if (nextProps.userEtherBalance !== this.props.userEtherBalance) {
+ if (this.state.isBalanceSpinnerVisible) {
+ const receivedAmount = nextProps.userEtherBalance.minus(this.props.userEtherBalance);
+ this.props.dispatcher.showFlashMessage(`Received ${receivedAmount.toString(10)} Kovan Ether`);
+ }
+ this.setState({
+ isBalanceSpinnerVisible: false,
+ });
+ }
+ const nextZrxToken = _.find(_.values(nextProps.tokenByAddress), t => t.symbol === ZRX_TOKEN_SYMBOL);
+ const nextZrxTokenBalance = nextProps.tokenStateByAddress[nextZrxToken.address].balance;
+ if (!_.isUndefined(this.state.currentZrxBalance) && !nextZrxTokenBalance.eq(this.state.currentZrxBalance)) {
+ if (this.state.isZRXSpinnerVisible) {
+ const receivedAmount = nextZrxTokenBalance.minus(this.state.currentZrxBalance);
+ const receiveAmountInUnits = ZeroEx.toUnitAmount(receivedAmount, constants.DECIMAL_PLACES_ZRX);
+ this.props.dispatcher.showFlashMessage(`Received ${receiveAmountInUnits.toString(10)} Kovan ZRX`);
+ }
+ this.setState({
+ isZRXSpinnerVisible: false,
+ currentZrxBalance: undefined,
+ });
+ }
+ }
+ public componentDidMount() {
+ window.scrollTo(0, 0);
+ }
+ public render() {
+ const errorDialogActions = [
+ <FlatButton
+ key="errorOkBtn"
+ label="Ok"
+ primary={true}
+ onTouchTap={this._onErrorDialogToggle.bind(this, false)}
+ />,
+ ];
+ const dharmaDialogActions = [
+ <FlatButton
+ key="dharmaCloseBtn"
+ label="Close"
+ primary={true}
+ onTouchTap={this._onDharmaDialogToggle.bind(this, false)}
+ />,
+ ];
+ const isTestNetwork = this.props.networkId === constants.NETWORK_ID_TESTNET;
+ const dharmaButtonColumnStyle = {
+ paddingLeft: 3,
+ display: isTestNetwork ? 'table-cell' : 'none',
+ };
+ const stubColumnStyle = {
+ display: isTestNetwork ? 'none' : 'table-cell',
+ };
+ const allTokenRowHeight = _.size(this.props.tokenByAddress) * TOKEN_TABLE_ROW_HEIGHT;
+ const tokenTableHeight =
+ allTokenRowHeight < MAX_TOKEN_TABLE_HEIGHT ? allTokenRowHeight : MAX_TOKEN_TABLE_HEIGHT;
+ const isSmallScreen = this.props.screenWidth === ScreenWidths.Sm;
+ const tokenColSpan = isSmallScreen ? TOKEN_COL_SPAN_SM : TOKEN_COL_SPAN_LG;
+ const dharmaLoanExplanation =
+ 'If you need access to larger amounts of ether,<br> \
you can request a loan from the Dharma Loan<br> \
network. Your loan should be funded in 5<br> \
minutes or less.';
- const allowanceExplanation =
- '0x smart contracts require access to your<br> \
+ const allowanceExplanation =
+ '0x smart contracts require access to your<br> \
token balances in order to execute trades.<br> \
Toggling sets an allowance for the<br> \
smart contract so you can start trading that token.';
- return (
- <div className="lg-px4 md-px4 sm-px1 pb2">
- <h3>{isTestNetwork ? 'Test ether' : 'Ether'}</h3>
- <Divider />
- <div className="pt2 pb2">
- {isTestNetwork
- ? 'In order to try out the 0x Portal Dapp, request some test ether to pay for \
+ return (
+ <div className="lg-px4 md-px4 sm-px1 pb2">
+ <h3>{isTestNetwork ? 'Test ether' : 'Ether'}</h3>
+ <Divider />
+ <div className="pt2 pb2">
+ {isTestNetwork
+ ? 'In order to try out the 0x Portal Dapp, request some test ether to pay for \
gas costs. It might take a bit of time for the test ether to show up.'
- : 'Ether must be converted to Ether Tokens in order to be tradable via 0x. \
+ : 'Ether must be converted to Ether Tokens in order to be tradable via 0x. \
You can convert between Ether and Ether Tokens from the "Wrap ETH" tab.'}
- </div>
- <Table selectable={false} style={styles.bgColor}>
- <TableHeader displaySelectAll={false} adjustForCheckbox={false}>
- <TableRow>
- <TableHeaderColumn>Currency</TableHeaderColumn>
- <TableHeaderColumn>Balance</TableHeaderColumn>
- <TableRowColumn className="sm-hide xs-hide" style={stubColumnStyle} />
- {isTestNetwork && (
- <TableHeaderColumn style={{ paddingLeft: 3 }}>
- {isSmallScreen ? 'Faucet' : 'Request from faucet'}
- </TableHeaderColumn>
- )}
- {isTestNetwork && (
- <TableHeaderColumn style={dharmaButtonColumnStyle}>
- {isSmallScreen ? 'Loan' : 'Request Dharma loan'}
- <HelpTooltip style={{ paddingLeft: 4 }} explanation={dharmaLoanExplanation} />
- </TableHeaderColumn>
- )}
- </TableRow>
- </TableHeader>
- <TableBody displayRowCheckbox={false}>
- <TableRow key="ETH">
- <TableRowColumn className="py1">
- <img style={{ width: ICON_DIMENSION, height: ICON_DIMENSION }} src={ETHER_ICON_PATH} />
- </TableRowColumn>
- <TableRowColumn>
- {this.props.userEtherBalance.toFixed(PRECISION)} ETH
- {this.state.isBalanceSpinnerVisible && (
- <span className="pl1">
- <i className="zmdi zmdi-spinner zmdi-hc-spin" />
- </span>
- )}
- </TableRowColumn>
- <TableRowColumn className="sm-hide xs-hide" style={stubColumnStyle} />
- {isTestNetwork && (
- <TableRowColumn style={{ paddingLeft: 3 }}>
- <LifeCycleRaisedButton
- labelReady="Request"
- labelLoading="Sending..."
- labelComplete="Sent!"
- onClickAsyncFn={this._faucetRequestAsync.bind(this, true)}
- />
- </TableRowColumn>
- )}
- {isTestNetwork && (
- <TableRowColumn style={dharmaButtonColumnStyle}>
- <RaisedButton
- label="Request"
- style={{ width: '100%' }}
- onTouchTap={this._onDharmaDialogToggle.bind(this)}
- />
- </TableRowColumn>
- )}
- </TableRow>
- </TableBody>
- </Table>
- <div className="clearfix" style={{ paddingBottom: 1 }}>
- <div className="col col-10">
- <h3 className="pt2">{isTestNetwork ? 'Test tokens' : 'Tokens'}</h3>
- </div>
- <div className="col col-1 pt3 align-right">
- <FloatingActionButton mini={true} zDepth={0} onClick={this._onAddTokenClicked.bind(this)}>
- <ContentAdd />
- </FloatingActionButton>
- </div>
- <div className="col col-1 pt3 align-right">
- <FloatingActionButton mini={true} zDepth={0} onClick={this._onRemoveTokenClicked.bind(this)}>
- <ContentRemove />
- </FloatingActionButton>
- </div>
- </div>
- <Divider />
- <div className="pt2 pb2">
- {isTestNetwork
- ? "Mint some test tokens you'd like to use to generate or fill an order using 0x."
- : "Set trading permissions for a token you'd like to start trading."}
- </div>
- <Table selectable={false} bodyStyle={{ height: tokenTableHeight }} style={styles.bgColor}>
- <TableHeader displaySelectAll={false} adjustForCheckbox={false}>
- <TableRow>
- <TableHeaderColumn colSpan={tokenColSpan}>Token</TableHeaderColumn>
- <TableHeaderColumn style={{ paddingLeft: 3 }}>Balance</TableHeaderColumn>
- <TableHeaderColumn>
- <div className="inline-block">Allowance</div>
- <HelpTooltip style={{ paddingLeft: 4 }} explanation={allowanceExplanation} />
- </TableHeaderColumn>
- <TableHeaderColumn>Action</TableHeaderColumn>
- {this.props.screenWidth !== ScreenWidths.Sm && <TableHeaderColumn>Send</TableHeaderColumn>}
- </TableRow>
- </TableHeader>
- <TableBody displayRowCheckbox={false}>{this._renderTokenTableRows()}</TableBody>
- </Table>
- <Dialog
- title="Oh oh"
- titleStyle={{ fontWeight: 100 }}
- actions={errorDialogActions}
- open={!_.isUndefined(this.state.errorType)}
- onRequestClose={this._onErrorDialogToggle.bind(this, false)}
- >
- {this._renderErrorDialogBody()}
- </Dialog>
- <Dialog
- title="Request Dharma Loan"
- titleStyle={{ fontWeight: 100, backgroundColor: colors.white }}
- bodyStyle={{ backgroundColor: colors.dharmaDarkGrey }}
- actionsContainerStyle={{ backgroundColor: colors.white }}
- autoScrollBodyContent={true}
- actions={dharmaDialogActions}
- open={this.state.isDharmaDialogVisible}
- >
- {this._renderDharmaLoanFrame()}
- </Dialog>
- <AssetPicker
- userAddress={this.props.userAddress}
- networkId={this.props.networkId}
- blockchain={this.props.blockchain}
- dispatcher={this.props.dispatcher}
- isOpen={this.state.isTokenPickerOpen}
- currentTokenAddress={''}
- onTokenChosen={this._onAssetTokenPicked.bind(this)}
- tokenByAddress={this.props.tokenByAddress}
- tokenVisibility={this.state.isAddingToken ? TokenVisibility.UNTRACKED : TokenVisibility.TRACKED}
- />
- </div>
- );
- }
- private _renderTokenTableRows() {
- if (!this.props.blockchainIsLoaded || this.props.blockchainErr !== BlockchainErrs.NoError) {
- return '';
- }
- const isSmallScreen = this.props.screenWidth === ScreenWidths.Sm;
- const tokenColSpan = isSmallScreen ? TOKEN_COL_SPAN_SM : TOKEN_COL_SPAN_LG;
- const actionPaddingX = isSmallScreen ? 2 : 24;
- const allTokens = _.values(this.props.tokenByAddress);
- const trackedTokens = _.filter(allTokens, t => t.isTracked);
- const trackedTokensStartingWithEtherToken = trackedTokens.sort(
- firstBy((t: Token) => t.symbol !== ETHER_TOKEN_SYMBOL)
- .thenBy((t: Token) => t.symbol !== ZRX_TOKEN_SYMBOL)
- .thenBy('address'),
- );
- const tableRows = _.map(
- trackedTokensStartingWithEtherToken,
- this._renderTokenRow.bind(this, tokenColSpan, actionPaddingX),
- );
- return tableRows;
- }
- private _renderTokenRow(tokenColSpan: number, actionPaddingX: number, token: Token) {
- const tokenState = this.props.tokenStateByAddress[token.address];
- const tokenLink = utils.getEtherScanLinkIfExists(
- token.address,
- this.props.networkId,
- EtherscanLinkSuffixes.Address,
- );
- const isMintable =
- _.includes(configs.SYMBOLS_OF_MINTABLE_TOKENS, token.symbol) &&
- this.props.networkId !== constants.NETWORK_ID_MAINNET;
- return (
- <TableRow key={token.address} style={{ height: TOKEN_TABLE_ROW_HEIGHT }}>
- <TableRowColumn colSpan={tokenColSpan}>
- {_.isUndefined(tokenLink) ? (
- this._renderTokenName(token)
- ) : (
- <a href={tokenLink} target="_blank" style={{ textDecoration: 'none' }}>
- {this._renderTokenName(token)}
- </a>
- )}
- </TableRowColumn>
- <TableRowColumn style={{ paddingRight: 3, paddingLeft: 3 }}>
- {this._renderAmount(tokenState.balance, token.decimals)} {token.symbol}
- {this.state.isZRXSpinnerVisible &&
- token.symbol === ZRX_TOKEN_SYMBOL && (
- <span className="pl1">
- <i className="zmdi zmdi-spinner zmdi-hc-spin" />
- </span>
- )}
- </TableRowColumn>
- <TableRowColumn>
- <AllowanceToggle
- blockchain={this.props.blockchain}
- dispatcher={this.props.dispatcher}
- token={token}
- tokenState={tokenState}
- onErrorOccurred={this._onErrorOccurred.bind(this)}
- userAddress={this.props.userAddress}
- />
- </TableRowColumn>
- <TableRowColumn style={{ paddingLeft: actionPaddingX, paddingRight: actionPaddingX }}>
- {isMintable && (
- <LifeCycleRaisedButton
- labelReady="Mint"
- labelLoading={<span style={{ fontSize: 12 }}>Minting...</span>}
- labelComplete="Minted!"
- onClickAsyncFn={this._onMintTestTokensAsync.bind(this, token)}
- />
- )}
- {token.symbol === ZRX_TOKEN_SYMBOL &&
- this.props.networkId === constants.NETWORK_ID_TESTNET && (
- <LifeCycleRaisedButton
- labelReady="Request"
- labelLoading="Sending..."
- labelComplete="Sent!"
- onClickAsyncFn={this._faucetRequestAsync.bind(this, false)}
- />
- )}
- </TableRowColumn>
- {this.props.screenWidth !== ScreenWidths.Sm && (
- <TableRowColumn
- style={{
- paddingLeft: actionPaddingX,
- paddingRight: actionPaddingX,
- }}
- >
- <SendButton
- blockchain={this.props.blockchain}
- dispatcher={this.props.dispatcher}
- token={token}
- tokenState={tokenState}
- onError={this._onSendFailed.bind(this)}
- />
- </TableRowColumn>
- )}
- </TableRow>
- );
- }
- private _onAssetTokenPicked(tokenAddress: string) {
- if (_.isEmpty(tokenAddress)) {
- this.setState({
- isTokenPickerOpen: false,
- });
- return;
- }
- const token = this.props.tokenByAddress[tokenAddress];
- const isDefaultTrackedToken = _.includes(configs.DEFAULT_TRACKED_TOKEN_SYMBOLS, token.symbol);
- if (!this.state.isAddingToken && !isDefaultTrackedToken) {
- if (token.isRegistered) {
- // Remove the token from tracked tokens
- const newToken = {
- ...token,
- isTracked: false,
- };
- this.props.dispatcher.updateTokenByAddress([newToken]);
- } else {
- this.props.dispatcher.removeTokenToTokenByAddress(token);
- }
- this.props.dispatcher.removeFromTokenStateByAddress(tokenAddress);
- trackedTokenStorage.removeTrackedToken(this.props.userAddress, this.props.networkId, tokenAddress);
- } else if (isDefaultTrackedToken) {
- this.props.dispatcher.showFlashMessage(`Cannot remove ${token.name} because it's a default token`);
- }
- this.setState({
- isTokenPickerOpen: false,
- });
- }
- private _onSendFailed() {
- this.setState({
- errorType: BalanceErrs.sendFailed,
- });
- }
- private _renderAmount(amount: BigNumber, decimals: number) {
- const unitAmount = ZeroEx.toUnitAmount(amount, decimals);
- return unitAmount.toNumber().toFixed(PRECISION);
- }
- private _renderTokenName(token: Token) {
- const tooltipId = `tooltip-${token.address}`;
- return (
- <div className="flex">
- <TokenIcon token={token} diameter={ICON_DIMENSION} />
- <div data-tip={true} data-for={tooltipId} className="mt2 ml2 sm-hide xs-hide">
- {token.name}
- </div>
- <ReactTooltip id={tooltipId}>{token.address}</ReactTooltip>
- </div>
- );
- }
- private _renderErrorDialogBody() {
- switch (this.state.errorType) {
- case BalanceErrs.incorrectNetworkForFaucet:
- return (
- <div>
- Our faucet can only send test Ether to addresses on the {constants.TESTNET_NAME} testnet
- (networkId {constants.NETWORK_ID_TESTNET}). Please make sure you are connected to the{' '}
- {constants.TESTNET_NAME} testnet and try requesting ether again.
- </div>
- );
+ </div>
+ <Table selectable={false} style={styles.bgColor}>
+ <TableHeader displaySelectAll={false} adjustForCheckbox={false}>
+ <TableRow>
+ <TableHeaderColumn>Currency</TableHeaderColumn>
+ <TableHeaderColumn>Balance</TableHeaderColumn>
+ <TableRowColumn className="sm-hide xs-hide" style={stubColumnStyle} />
+ {isTestNetwork && (
+ <TableHeaderColumn style={{ paddingLeft: 3 }}>
+ {isSmallScreen ? 'Faucet' : 'Request from faucet'}
+ </TableHeaderColumn>
+ )}
+ {isTestNetwork && (
+ <TableHeaderColumn style={dharmaButtonColumnStyle}>
+ {isSmallScreen ? 'Loan' : 'Request Dharma loan'}
+ <HelpTooltip style={{ paddingLeft: 4 }} explanation={dharmaLoanExplanation} />
+ </TableHeaderColumn>
+ )}
+ </TableRow>
+ </TableHeader>
+ <TableBody displayRowCheckbox={false}>
+ <TableRow key="ETH">
+ <TableRowColumn className="py1">
+ <img style={{ width: ICON_DIMENSION, height: ICON_DIMENSION }} src={ETHER_ICON_PATH} />
+ </TableRowColumn>
+ <TableRowColumn>
+ {this.props.userEtherBalance.toFixed(PRECISION)} ETH
+ {this.state.isBalanceSpinnerVisible && (
+ <span className="pl1">
+ <i className="zmdi zmdi-spinner zmdi-hc-spin" />
+ </span>
+ )}
+ </TableRowColumn>
+ <TableRowColumn className="sm-hide xs-hide" style={stubColumnStyle} />
+ {isTestNetwork && (
+ <TableRowColumn style={{ paddingLeft: 3 }}>
+ <LifeCycleRaisedButton
+ labelReady="Request"
+ labelLoading="Sending..."
+ labelComplete="Sent!"
+ onClickAsyncFn={this._faucetRequestAsync.bind(this, true)}
+ />
+ </TableRowColumn>
+ )}
+ {isTestNetwork && (
+ <TableRowColumn style={dharmaButtonColumnStyle}>
+ <RaisedButton
+ label="Request"
+ style={{ width: '100%' }}
+ onTouchTap={this._onDharmaDialogToggle.bind(this)}
+ />
+ </TableRowColumn>
+ )}
+ </TableRow>
+ </TableBody>
+ </Table>
+ <div className="clearfix" style={{ paddingBottom: 1 }}>
+ <div className="col col-10">
+ <h3 className="pt2">{isTestNetwork ? 'Test tokens' : 'Tokens'}</h3>
+ </div>
+ <div className="col col-1 pt3 align-right">
+ <FloatingActionButton mini={true} zDepth={0} onClick={this._onAddTokenClicked.bind(this)}>
+ <ContentAdd />
+ </FloatingActionButton>
+ </div>
+ <div className="col col-1 pt3 align-right">
+ <FloatingActionButton mini={true} zDepth={0} onClick={this._onRemoveTokenClicked.bind(this)}>
+ <ContentRemove />
+ </FloatingActionButton>
+ </div>
+ </div>
+ <Divider />
+ <div className="pt2 pb2">
+ {isTestNetwork
+ ? "Mint some test tokens you'd like to use to generate or fill an order using 0x."
+ : "Set trading permissions for a token you'd like to start trading."}
+ </div>
+ <Table selectable={false} bodyStyle={{ height: tokenTableHeight }} style={styles.bgColor}>
+ <TableHeader displaySelectAll={false} adjustForCheckbox={false}>
+ <TableRow>
+ <TableHeaderColumn colSpan={tokenColSpan}>Token</TableHeaderColumn>
+ <TableHeaderColumn style={{ paddingLeft: 3 }}>Balance</TableHeaderColumn>
+ <TableHeaderColumn>
+ <div className="inline-block">Allowance</div>
+ <HelpTooltip style={{ paddingLeft: 4 }} explanation={allowanceExplanation} />
+ </TableHeaderColumn>
+ <TableHeaderColumn>Action</TableHeaderColumn>
+ {this.props.screenWidth !== ScreenWidths.Sm && <TableHeaderColumn>Send</TableHeaderColumn>}
+ </TableRow>
+ </TableHeader>
+ <TableBody displayRowCheckbox={false}>{this._renderTokenTableRows()}</TableBody>
+ </Table>
+ <Dialog
+ title="Oh oh"
+ titleStyle={{ fontWeight: 100 }}
+ actions={errorDialogActions}
+ open={!_.isUndefined(this.state.errorType)}
+ onRequestClose={this._onErrorDialogToggle.bind(this, false)}
+ >
+ {this._renderErrorDialogBody()}
+ </Dialog>
+ <Dialog
+ title="Request Dharma Loan"
+ titleStyle={{ fontWeight: 100, backgroundColor: colors.white }}
+ bodyStyle={{ backgroundColor: colors.dharmaDarkGrey }}
+ actionsContainerStyle={{ backgroundColor: colors.white }}
+ autoScrollBodyContent={true}
+ actions={dharmaDialogActions}
+ open={this.state.isDharmaDialogVisible}
+ >
+ {this._renderDharmaLoanFrame()}
+ </Dialog>
+ <AssetPicker
+ userAddress={this.props.userAddress}
+ networkId={this.props.networkId}
+ blockchain={this.props.blockchain}
+ dispatcher={this.props.dispatcher}
+ isOpen={this.state.isTokenPickerOpen}
+ currentTokenAddress={''}
+ onTokenChosen={this._onAssetTokenPicked.bind(this)}
+ tokenByAddress={this.props.tokenByAddress}
+ tokenVisibility={this.state.isAddingToken ? TokenVisibility.UNTRACKED : TokenVisibility.TRACKED}
+ />
+ </div>
+ );
+ }
+ private _renderTokenTableRows() {
+ if (!this.props.blockchainIsLoaded || this.props.blockchainErr !== BlockchainErrs.NoError) {
+ return '';
+ }
+ const isSmallScreen = this.props.screenWidth === ScreenWidths.Sm;
+ const tokenColSpan = isSmallScreen ? TOKEN_COL_SPAN_SM : TOKEN_COL_SPAN_LG;
+ const actionPaddingX = isSmallScreen ? 2 : 24;
+ const allTokens = _.values(this.props.tokenByAddress);
+ const trackedTokens = _.filter(allTokens, t => t.isTracked);
+ const trackedTokensStartingWithEtherToken = trackedTokens.sort(
+ firstBy((t: Token) => t.symbol !== ETHER_TOKEN_SYMBOL)
+ .thenBy((t: Token) => t.symbol !== ZRX_TOKEN_SYMBOL)
+ .thenBy('address'),
+ );
+ const tableRows = _.map(
+ trackedTokensStartingWithEtherToken,
+ this._renderTokenRow.bind(this, tokenColSpan, actionPaddingX),
+ );
+ return tableRows;
+ }
+ private _renderTokenRow(tokenColSpan: number, actionPaddingX: number, token: Token) {
+ const tokenState = this.props.tokenStateByAddress[token.address];
+ const tokenLink = utils.getEtherScanLinkIfExists(
+ token.address,
+ this.props.networkId,
+ EtherscanLinkSuffixes.Address,
+ );
+ const isMintable =
+ _.includes(configs.SYMBOLS_OF_MINTABLE_TOKENS, token.symbol) &&
+ this.props.networkId !== constants.NETWORK_ID_MAINNET;
+ return (
+ <TableRow key={token.address} style={{ height: TOKEN_TABLE_ROW_HEIGHT }}>
+ <TableRowColumn colSpan={tokenColSpan}>
+ {_.isUndefined(tokenLink) ? (
+ this._renderTokenName(token)
+ ) : (
+ <a href={tokenLink} target="_blank" style={{ textDecoration: 'none' }}>
+ {this._renderTokenName(token)}
+ </a>
+ )}
+ </TableRowColumn>
+ <TableRowColumn style={{ paddingRight: 3, paddingLeft: 3 }}>
+ {this._renderAmount(tokenState.balance, token.decimals)} {token.symbol}
+ {this.state.isZRXSpinnerVisible &&
+ token.symbol === ZRX_TOKEN_SYMBOL && (
+ <span className="pl1">
+ <i className="zmdi zmdi-spinner zmdi-hc-spin" />
+ </span>
+ )}
+ </TableRowColumn>
+ <TableRowColumn>
+ <AllowanceToggle
+ blockchain={this.props.blockchain}
+ dispatcher={this.props.dispatcher}
+ token={token}
+ tokenState={tokenState}
+ onErrorOccurred={this._onErrorOccurred.bind(this)}
+ userAddress={this.props.userAddress}
+ />
+ </TableRowColumn>
+ <TableRowColumn style={{ paddingLeft: actionPaddingX, paddingRight: actionPaddingX }}>
+ {isMintable && (
+ <LifeCycleRaisedButton
+ labelReady="Mint"
+ labelLoading={<span style={{ fontSize: 12 }}>Minting...</span>}
+ labelComplete="Minted!"
+ onClickAsyncFn={this._onMintTestTokensAsync.bind(this, token)}
+ />
+ )}
+ {token.symbol === ZRX_TOKEN_SYMBOL &&
+ this.props.networkId === constants.NETWORK_ID_TESTNET && (
+ <LifeCycleRaisedButton
+ labelReady="Request"
+ labelLoading="Sending..."
+ labelComplete="Sent!"
+ onClickAsyncFn={this._faucetRequestAsync.bind(this, false)}
+ />
+ )}
+ </TableRowColumn>
+ {this.props.screenWidth !== ScreenWidths.Sm && (
+ <TableRowColumn
+ style={{
+ paddingLeft: actionPaddingX,
+ paddingRight: actionPaddingX,
+ }}
+ >
+ <SendButton
+ blockchain={this.props.blockchain}
+ dispatcher={this.props.dispatcher}
+ token={token}
+ tokenState={tokenState}
+ onError={this._onSendFailed.bind(this)}
+ />
+ </TableRowColumn>
+ )}
+ </TableRow>
+ );
+ }
+ private _onAssetTokenPicked(tokenAddress: string) {
+ if (_.isEmpty(tokenAddress)) {
+ this.setState({
+ isTokenPickerOpen: false,
+ });
+ return;
+ }
+ const token = this.props.tokenByAddress[tokenAddress];
+ const isDefaultTrackedToken = _.includes(configs.DEFAULT_TRACKED_TOKEN_SYMBOLS, token.symbol);
+ if (!this.state.isAddingToken && !isDefaultTrackedToken) {
+ if (token.isRegistered) {
+ // Remove the token from tracked tokens
+ const newToken = {
+ ...token,
+ isTracked: false,
+ };
+ this.props.dispatcher.updateTokenByAddress([newToken]);
+ } else {
+ this.props.dispatcher.removeTokenToTokenByAddress(token);
+ }
+ this.props.dispatcher.removeFromTokenStateByAddress(tokenAddress);
+ trackedTokenStorage.removeTrackedToken(this.props.userAddress, this.props.networkId, tokenAddress);
+ } else if (isDefaultTrackedToken) {
+ this.props.dispatcher.showFlashMessage(`Cannot remove ${token.name} because it's a default token`);
+ }
+ this.setState({
+ isTokenPickerOpen: false,
+ });
+ }
+ private _onSendFailed() {
+ this.setState({
+ errorType: BalanceErrs.sendFailed,
+ });
+ }
+ private _renderAmount(amount: BigNumber, decimals: number) {
+ const unitAmount = ZeroEx.toUnitAmount(amount, decimals);
+ return unitAmount.toNumber().toFixed(PRECISION);
+ }
+ private _renderTokenName(token: Token) {
+ const tooltipId = `tooltip-${token.address}`;
+ return (
+ <div className="flex">
+ <TokenIcon token={token} diameter={ICON_DIMENSION} />
+ <div data-tip={true} data-for={tooltipId} className="mt2 ml2 sm-hide xs-hide">
+ {token.name}
+ </div>
+ <ReactTooltip id={tooltipId}>{token.address}</ReactTooltip>
+ </div>
+ );
+ }
+ private _renderErrorDialogBody() {
+ switch (this.state.errorType) {
+ case BalanceErrs.incorrectNetworkForFaucet:
+ return (
+ <div>
+ Our faucet can only send test Ether to addresses on the {constants.TESTNET_NAME} testnet
+ (networkId {constants.NETWORK_ID_TESTNET}). Please make sure you are connected to the{' '}
+ {constants.TESTNET_NAME} testnet and try requesting ether again.
+ </div>
+ );
- case BalanceErrs.faucetRequestFailed:
- return (
- <div>
- An unexpected error occurred while trying to request test Ether from our faucet. Please refresh
- the page and try again.
- </div>
- );
+ case BalanceErrs.faucetRequestFailed:
+ return (
+ <div>
+ An unexpected error occurred while trying to request test Ether from our faucet. Please refresh
+ the page and try again.
+ </div>
+ );
- case BalanceErrs.faucetQueueIsFull:
- return <div>Our test Ether faucet queue is full. Please try requesting test Ether again later.</div>;
+ case BalanceErrs.faucetQueueIsFull:
+ return <div>Our test Ether faucet queue is full. Please try requesting test Ether again later.</div>;
- case BalanceErrs.mintingFailed:
- return <div>Minting your test tokens failed unexpectedly. Please refresh the page and try again.</div>;
+ case BalanceErrs.mintingFailed:
+ return <div>Minting your test tokens failed unexpectedly. Please refresh the page and try again.</div>;
- case BalanceErrs.allowanceSettingFailed:
- return (
- <div>
- An unexpected error occurred while trying to set your test token allowance. Please refresh the
- page and try again.
- </div>
- );
+ case BalanceErrs.allowanceSettingFailed:
+ return (
+ <div>
+ An unexpected error occurred while trying to set your test token allowance. Please refresh the
+ page and try again.
+ </div>
+ );
- case undefined:
- return null; // No error to show
+ case undefined:
+ return null; // No error to show
- default:
- throw utils.spawnSwitchErr('errorType', this.state.errorType);
- }
- }
- private _renderDharmaLoanFrame() {
- if (utils.isUserOnMobile()) {
- return (
- <h4 style={{ textAlign: 'center' }}>
- We apologize -- Dharma loan requests are not available on mobile yet. Please try again through your
- desktop browser.
- </h4>
- );
- } else {
- return (
- <DharmaLoanFrame
- partner="0x"
- env={utils.getCurrentEnvironment()}
- screenWidth={this.props.screenWidth}
- />
- );
- }
- }
- private _onErrorOccurred(errorType: BalanceErrs) {
- this.setState({
- errorType,
- });
- }
- private async _onMintTestTokensAsync(token: Token): Promise<boolean> {
- try {
- await this.props.blockchain.mintTestTokensAsync(token);
- const amount = ZeroEx.toUnitAmount(constants.MINT_AMOUNT, token.decimals);
- this.props.dispatcher.showFlashMessage(`Successfully minted ${amount.toString(10)} ${token.symbol}`);
- return true;
- } catch (err) {
- const errMsg = `${err}`;
- if (_.includes(errMsg, BlockchainCallErrs.UserHasNoAssociatedAddresses)) {
- this.props.dispatcher.updateShouldBlockchainErrDialogBeOpen(true);
- return false;
- }
- if (_.includes(errMsg, 'User denied transaction')) {
- return false;
- }
- utils.consoleLog(`Unexpected error encountered: ${err}`);
- utils.consoleLog(err.stack);
- this.setState({
- errorType: BalanceErrs.mintingFailed,
- });
- await errorReporter.reportAsync(err);
- return false;
- }
- }
- private async _faucetRequestAsync(isEtherRequest: boolean): Promise<boolean> {
- if (this.props.userAddress === '') {
- this.props.dispatcher.updateShouldBlockchainErrDialogBeOpen(true);
- return false;
- }
+ default:
+ throw utils.spawnSwitchErr('errorType', this.state.errorType);
+ }
+ }
+ private _renderDharmaLoanFrame() {
+ if (utils.isUserOnMobile()) {
+ return (
+ <h4 style={{ textAlign: 'center' }}>
+ We apologize -- Dharma loan requests are not available on mobile yet. Please try again through your
+ desktop browser.
+ </h4>
+ );
+ } else {
+ return (
+ <DharmaLoanFrame
+ partner="0x"
+ env={utils.getCurrentEnvironment()}
+ screenWidth={this.props.screenWidth}
+ />
+ );
+ }
+ }
+ private _onErrorOccurred(errorType: BalanceErrs) {
+ this.setState({
+ errorType,
+ });
+ }
+ private async _onMintTestTokensAsync(token: Token): Promise<boolean> {
+ try {
+ await this.props.blockchain.mintTestTokensAsync(token);
+ const amount = ZeroEx.toUnitAmount(constants.MINT_AMOUNT, token.decimals);
+ this.props.dispatcher.showFlashMessage(`Successfully minted ${amount.toString(10)} ${token.symbol}`);
+ return true;
+ } catch (err) {
+ const errMsg = `${err}`;
+ if (_.includes(errMsg, BlockchainCallErrs.UserHasNoAssociatedAddresses)) {
+ this.props.dispatcher.updateShouldBlockchainErrDialogBeOpen(true);
+ return false;
+ }
+ if (_.includes(errMsg, 'User denied transaction')) {
+ return false;
+ }
+ utils.consoleLog(`Unexpected error encountered: ${err}`);
+ utils.consoleLog(err.stack);
+ this.setState({
+ errorType: BalanceErrs.mintingFailed,
+ });
+ await errorReporter.reportAsync(err);
+ return false;
+ }
+ }
+ private async _faucetRequestAsync(isEtherRequest: boolean): Promise<boolean> {
+ if (this.props.userAddress === '') {
+ this.props.dispatcher.updateShouldBlockchainErrDialogBeOpen(true);
+ return false;
+ }
- // If on another network other then the testnet our faucet serves test ether
- // from, we must show user an error message
- if (this.props.blockchain.networkId !== constants.NETWORK_ID_TESTNET) {
- this.setState({
- errorType: BalanceErrs.incorrectNetworkForFaucet,
- });
- return false;
- }
+ // If on another network other then the testnet our faucet serves test ether
+ // from, we must show user an error message
+ if (this.props.blockchain.networkId !== constants.NETWORK_ID_TESTNET) {
+ this.setState({
+ errorType: BalanceErrs.incorrectNetworkForFaucet,
+ });
+ return false;
+ }
- await utils.sleepAsync(ARTIFICIAL_FAUCET_REQUEST_DELAY);
+ await utils.sleepAsync(ARTIFICIAL_FAUCET_REQUEST_DELAY);
- const segment = isEtherRequest ? 'ether' : 'zrx';
- const response = await fetch(`${constants.URL_ETHER_FAUCET}/${segment}/${this.props.userAddress}`);
- const responseBody = await response.text();
- if (response.status !== constants.SUCCESS_STATUS) {
- utils.consoleLog(`Unexpected status code: ${response.status} -> ${responseBody}`);
- const errorType =
- response.status === constants.UNAVAILABLE_STATUS
- ? BalanceErrs.faucetQueueIsFull
- : BalanceErrs.faucetRequestFailed;
- this.setState({
- errorType,
- });
- await errorReporter.reportAsync(new Error(`Faucet returned non-200: ${JSON.stringify(response)}`));
- return false;
- }
+ const segment = isEtherRequest ? 'ether' : 'zrx';
+ const response = await fetch(`${constants.URL_ETHER_FAUCET}/${segment}/${this.props.userAddress}`);
+ const responseBody = await response.text();
+ if (response.status !== constants.SUCCESS_STATUS) {
+ utils.consoleLog(`Unexpected status code: ${response.status} -> ${responseBody}`);
+ const errorType =
+ response.status === constants.UNAVAILABLE_STATUS
+ ? BalanceErrs.faucetQueueIsFull
+ : BalanceErrs.faucetRequestFailed;
+ this.setState({
+ errorType,
+ });
+ await errorReporter.reportAsync(new Error(`Faucet returned non-200: ${JSON.stringify(response)}`));
+ return false;
+ }
- if (isEtherRequest) {
- this.setState({
- isBalanceSpinnerVisible: true,
- });
- } else {
- const tokens = _.values(this.props.tokenByAddress);
- const zrxToken = _.find(tokens, t => t.symbol === ZRX_TOKEN_SYMBOL);
- const zrxTokenState = this.props.tokenStateByAddress[zrxToken.address];
- this.setState({
- isZRXSpinnerVisible: true,
- currentZrxBalance: zrxTokenState.balance,
- });
- // tslint:disable-next-line:no-floating-promises
- this.props.blockchain.pollTokenBalanceAsync(zrxToken);
- }
- return true;
- }
- private _onErrorDialogToggle(isOpen: boolean) {
- this.setState({
- errorType: undefined,
- });
- }
- private _onDharmaDialogToggle() {
- this.setState({
- isDharmaDialogVisible: !this.state.isDharmaDialogVisible,
- });
- }
- private _onAddTokenClicked() {
- this.setState({
- isTokenPickerOpen: true,
- isAddingToken: true,
- });
- }
- private _onRemoveTokenClicked() {
- this.setState({
- isTokenPickerOpen: true,
- isAddingToken: false,
- });
- }
+ if (isEtherRequest) {
+ this.setState({
+ isBalanceSpinnerVisible: true,
+ });
+ } else {
+ const tokens = _.values(this.props.tokenByAddress);
+ const zrxToken = _.find(tokens, t => t.symbol === ZRX_TOKEN_SYMBOL);
+ const zrxTokenState = this.props.tokenStateByAddress[zrxToken.address];
+ this.setState({
+ isZRXSpinnerVisible: true,
+ currentZrxBalance: zrxTokenState.balance,
+ });
+ // tslint:disable-next-line:no-floating-promises
+ this.props.blockchain.pollTokenBalanceAsync(zrxToken);
+ }
+ return true;
+ }
+ private _onErrorDialogToggle(isOpen: boolean) {
+ this.setState({
+ errorType: undefined,
+ });
+ }
+ private _onDharmaDialogToggle() {
+ this.setState({
+ isDharmaDialogVisible: !this.state.isDharmaDialogVisible,
+ });
+ }
+ private _onAddTokenClicked() {
+ this.setState({
+ isTokenPickerOpen: true,
+ isAddingToken: true,
+ });
+ }
+ private _onRemoveTokenClicked() {
+ this.setState({
+ isTokenPickerOpen: true,
+ isAddingToken: false,
+ });
+ }
} // tslint:disable:max-file-line-count
diff --git a/packages/website/ts/components/top_bar.tsx b/packages/website/ts/components/top_bar.tsx
index 1f111cb07..11d3e7cc2 100644
--- a/packages/website/ts/components/top_bar.tsx
+++ b/packages/website/ts/components/top_bar.tsx
@@ -15,333 +15,333 @@ import { colors } from 'ts/utils/colors';
import { constants } from 'ts/utils/constants';
interface TopBarProps {
- userAddress?: string;
- blockchainIsLoaded: boolean;
- location: Location;
- docsVersion?: string;
- availableDocVersions?: string[];
- menu?: DocsMenu;
- menuSubsectionsBySection?: MenuSubsectionsBySection;
- shouldFullWidth?: boolean;
- docsInfo?: DocsInfo;
- style?: React.CSSProperties;
- isNightVersion?: boolean;
+ userAddress?: string;
+ blockchainIsLoaded: boolean;
+ location: Location;
+ docsVersion?: string;
+ availableDocVersions?: string[];
+ menu?: DocsMenu;
+ menuSubsectionsBySection?: MenuSubsectionsBySection;
+ shouldFullWidth?: boolean;
+ docsInfo?: DocsInfo;
+ style?: React.CSSProperties;
+ isNightVersion?: boolean;
}
interface TopBarState {
- isDrawerOpen: boolean;
+ isDrawerOpen: boolean;
}
const styles: Styles = {
- address: {
- marginRight: 12,
- overflow: 'hidden',
- paddingTop: 4,
- textOverflow: 'ellipsis',
- whiteSpace: 'nowrap',
- width: 70,
- },
- topBar: {
- backgroundcolor: colors.white,
- height: 59,
- width: '100%',
- position: 'relative',
- top: 0,
- zIndex: 1100,
- paddingBottom: 1,
- },
- bottomBar: {
- boxShadow: 'rgba(0, 0, 0, 0.187647) 0px 1px 3px',
- },
- menuItem: {
- fontSize: 14,
- color: colors.darkestGrey,
- paddingTop: 6,
- paddingBottom: 6,
- marginTop: 17,
- cursor: 'pointer',
- fontWeight: 400,
- },
+ address: {
+ marginRight: 12,
+ overflow: 'hidden',
+ paddingTop: 4,
+ textOverflow: 'ellipsis',
+ whiteSpace: 'nowrap',
+ width: 70,
+ },
+ topBar: {
+ backgroundcolor: colors.white,
+ height: 59,
+ width: '100%',
+ position: 'relative',
+ top: 0,
+ zIndex: 1100,
+ paddingBottom: 1,
+ },
+ bottomBar: {
+ boxShadow: 'rgba(0, 0, 0, 0.187647) 0px 1px 3px',
+ },
+ menuItem: {
+ fontSize: 14,
+ color: colors.darkestGrey,
+ paddingTop: 6,
+ paddingBottom: 6,
+ marginTop: 17,
+ cursor: 'pointer',
+ fontWeight: 400,
+ },
};
export class TopBar extends React.Component<TopBarProps, TopBarState> {
- public static defaultProps: Partial<TopBarProps> = {
- shouldFullWidth: false,
- style: {},
- isNightVersion: false,
- };
- constructor(props: TopBarProps) {
- super(props);
- this.state = {
- isDrawerOpen: false,
- };
- }
- public render() {
- const isNightVersion = this.props.isNightVersion;
- const isFullWidthPage = this.props.shouldFullWidth;
- const parentClassNames = `flex mx-auto ${isFullWidthPage ? 'pl2' : 'max-width-4'}`;
- const developerSectionMenuItems = [
- <Link key="subMenuItem-zeroEx" to={WebsitePaths.ZeroExJs} className="text-decoration-none">
- <MenuItem style={{ fontSize: styles.menuItem.fontSize }} primaryText="0x.js" />
- </Link>,
- <Link key="subMenuItem-smartContracts" to={WebsitePaths.SmartContracts} className="text-decoration-none">
- <MenuItem style={{ fontSize: styles.menuItem.fontSize }} primaryText="Smart Contracts" />
- </Link>,
- <Link key="subMenuItem-0xconnect" to={WebsitePaths.Connect} className="text-decoration-none">
- <MenuItem style={{ fontSize: styles.menuItem.fontSize }} primaryText="0x Connect" />
- </Link>,
- <a
- key="subMenuItem-standard-relayer-api"
- target="_blank"
- className="text-decoration-none"
- href={constants.URL_STANDARD_RELAYER_API_GITHUB}
- >
- <MenuItem style={{ fontSize: styles.menuItem.fontSize }} primaryText="Standard Relayer API" />
- </a>,
- <a
- key="subMenuItem-github"
- target="_blank"
- className="text-decoration-none"
- href={constants.URL_GITHUB_ORG}
- >
- <MenuItem style={{ fontSize: styles.menuItem.fontSize }} primaryText="GitHub" />
- </a>,
- <a
- key="subMenuItem-whitePaper"
- target="_blank"
- className="text-decoration-none"
- href={`${WebsitePaths.Whitepaper}`}
- >
- <MenuItem style={{ fontSize: styles.menuItem.fontSize }} primaryText="Whitepaper" />
- </a>,
- ];
- const bottomBorderStyle = this._shouldDisplayBottomBar() ? styles.bottomBar : {};
- const fullWidthClasses = isFullWidthPage ? 'pr4' : '';
- const logoUrl = isNightVersion ? '/images/protocol_logo_white.png' : '/images/protocol_logo_black.png';
- const menuClasses = `col col-${isFullWidthPage ? '4' : '5'} ${fullWidthClasses} lg-pr0 md-pr2 sm-hide xs-hide`;
- const menuIconStyle = {
- fontSize: 25,
- color: isNightVersion ? 'white' : 'black',
- cursor: 'pointer',
- paddingTop: 16,
- };
- return (
- <div style={{ ...styles.topBar, ...bottomBorderStyle, ...this.props.style }} className="pb1">
- <div className={parentClassNames}>
- <div className="col col-2 sm-pl2 md-pl2 lg-pl0" style={{ paddingTop: 15 }}>
- <Link to={`${WebsitePaths.Home}`} className="text-decoration-none">
- <img src={logoUrl} height="30" />
- </Link>
- </div>
- <div className={`col col-${isFullWidthPage ? '8' : '9'} lg-hide md-hide`} />
- <div className={`col col-${isFullWidthPage ? '6' : '5'} sm-hide xs-hide`} />
- {!this._isViewingPortal() && (
- <div className={menuClasses}>
- <div className="flex justify-between">
- <DropDownMenuItem
- title="Developers"
- subMenuItems={developerSectionMenuItems}
- style={styles.menuItem}
- isNightVersion={isNightVersion}
- />
- <TopBarMenuItem
- title="Wiki"
- path={`${WebsitePaths.Wiki}`}
- style={styles.menuItem}
- isNightVersion={isNightVersion}
- />
- <TopBarMenuItem
- title="About"
- path={`${WebsitePaths.About}`}
- style={styles.menuItem}
- isNightVersion={isNightVersion}
- />
- <TopBarMenuItem
- title="Portal DApp"
- path={`${WebsitePaths.Portal}`}
- isPrimary={true}
- style={styles.menuItem}
- className={`${isFullWidthPage && 'md-hide'}`}
- isNightVersion={isNightVersion}
- />
- </div>
- </div>
- )}
- {this.props.blockchainIsLoaded &&
- !_.isEmpty(this.props.userAddress) && (
- <div className="col col-5 sm-hide xs-hide">{this._renderUser()}</div>
- )}
- <div className={`col ${isFullWidthPage ? 'col-2 pl2' : 'col-1'} md-hide lg-hide`}>
- <div style={menuIconStyle}>
- <i className="zmdi zmdi-menu" onClick={this._onMenuButtonClick.bind(this)} />
- </div>
- </div>
- </div>
- {this._renderDrawer()}
- </div>
- );
- }
- private _renderDrawer() {
- return (
- <Drawer
- open={this.state.isDrawerOpen}
- docked={false}
- openSecondary={true}
- onRequestChange={this._onMenuButtonClick.bind(this)}
- >
- {this._renderPortalMenu()}
- {this._renderDocsMenu()}
- {this._renderWiki()}
- <div className="pl1 py1 mt3" style={{ backgroundColor: colors.lightGrey }}>
- Website
- </div>
- <Link to={WebsitePaths.Home} className="text-decoration-none">
- <MenuItem className="py2">Home</MenuItem>
- </Link>
- <Link to={`${WebsitePaths.Wiki}`} className="text-decoration-none">
- <MenuItem className="py2">Wiki</MenuItem>
- </Link>
- {!this._isViewing0xjsDocs() && (
- <Link to={WebsitePaths.ZeroExJs} className="text-decoration-none">
- <MenuItem className="py2">0x.js Docs</MenuItem>
- </Link>
- )}
- {!this._isViewingConnectDocs() && (
- <Link to={WebsitePaths.Connect} className="text-decoration-none">
- <MenuItem className="py2">0x Connect Docs</MenuItem>
- </Link>
- )}
- {!this._isViewingSmartContractsDocs() && (
- <Link to={WebsitePaths.SmartContracts} className="text-decoration-none">
- <MenuItem className="py2">Smart Contract Docs</MenuItem>
- </Link>
- )}
- {!this._isViewingPortal() && (
- <Link to={`${WebsitePaths.Portal}`} className="text-decoration-none">
- <MenuItem className="py2">Portal DApp</MenuItem>
- </Link>
- )}
- <a className="text-decoration-none" target="_blank" href={`${WebsitePaths.Whitepaper}`}>
- <MenuItem className="py2">Whitepaper</MenuItem>
- </a>
- <Link to={`${WebsitePaths.About}`} className="text-decoration-none">
- <MenuItem className="py2">About</MenuItem>
- </Link>
- <a className="text-decoration-none" target="_blank" href={constants.URL_BLOG}>
- <MenuItem className="py2">Blog</MenuItem>
- </a>
- <Link to={`${WebsitePaths.FAQ}`} className="text-decoration-none">
- <MenuItem className="py2" onTouchTap={this._onMenuButtonClick.bind(this)}>
- FAQ
- </MenuItem>
- </Link>
- </Drawer>
- );
- }
- private _renderDocsMenu(): React.ReactNode {
- if (
- (!this._isViewing0xjsDocs() && !this._isViewingSmartContractsDocs() && !this._isViewingConnectDocs()) ||
- _.isUndefined(this.props.menu)
- ) {
- return undefined;
- }
+ public static defaultProps: Partial<TopBarProps> = {
+ shouldFullWidth: false,
+ style: {},
+ isNightVersion: false,
+ };
+ constructor(props: TopBarProps) {
+ super(props);
+ this.state = {
+ isDrawerOpen: false,
+ };
+ }
+ public render() {
+ const isNightVersion = this.props.isNightVersion;
+ const isFullWidthPage = this.props.shouldFullWidth;
+ const parentClassNames = `flex mx-auto ${isFullWidthPage ? 'pl2' : 'max-width-4'}`;
+ const developerSectionMenuItems = [
+ <Link key="subMenuItem-zeroEx" to={WebsitePaths.ZeroExJs} className="text-decoration-none">
+ <MenuItem style={{ fontSize: styles.menuItem.fontSize }} primaryText="0x.js" />
+ </Link>,
+ <Link key="subMenuItem-smartContracts" to={WebsitePaths.SmartContracts} className="text-decoration-none">
+ <MenuItem style={{ fontSize: styles.menuItem.fontSize }} primaryText="Smart Contracts" />
+ </Link>,
+ <Link key="subMenuItem-0xconnect" to={WebsitePaths.Connect} className="text-decoration-none">
+ <MenuItem style={{ fontSize: styles.menuItem.fontSize }} primaryText="0x Connect" />
+ </Link>,
+ <a
+ key="subMenuItem-standard-relayer-api"
+ target="_blank"
+ className="text-decoration-none"
+ href={constants.URL_STANDARD_RELAYER_API_GITHUB}
+ >
+ <MenuItem style={{ fontSize: styles.menuItem.fontSize }} primaryText="Standard Relayer API" />
+ </a>,
+ <a
+ key="subMenuItem-github"
+ target="_blank"
+ className="text-decoration-none"
+ href={constants.URL_GITHUB_ORG}
+ >
+ <MenuItem style={{ fontSize: styles.menuItem.fontSize }} primaryText="GitHub" />
+ </a>,
+ <a
+ key="subMenuItem-whitePaper"
+ target="_blank"
+ className="text-decoration-none"
+ href={`${WebsitePaths.Whitepaper}`}
+ >
+ <MenuItem style={{ fontSize: styles.menuItem.fontSize }} primaryText="Whitepaper" />
+ </a>,
+ ];
+ const bottomBorderStyle = this._shouldDisplayBottomBar() ? styles.bottomBar : {};
+ const fullWidthClasses = isFullWidthPage ? 'pr4' : '';
+ const logoUrl = isNightVersion ? '/images/protocol_logo_white.png' : '/images/protocol_logo_black.png';
+ const menuClasses = `col col-${isFullWidthPage ? '4' : '5'} ${fullWidthClasses} lg-pr0 md-pr2 sm-hide xs-hide`;
+ const menuIconStyle = {
+ fontSize: 25,
+ color: isNightVersion ? 'white' : 'black',
+ cursor: 'pointer',
+ paddingTop: 16,
+ };
+ return (
+ <div style={{ ...styles.topBar, ...bottomBorderStyle, ...this.props.style }} className="pb1">
+ <div className={parentClassNames}>
+ <div className="col col-2 sm-pl2 md-pl2 lg-pl0" style={{ paddingTop: 15 }}>
+ <Link to={`${WebsitePaths.Home}`} className="text-decoration-none">
+ <img src={logoUrl} height="30" />
+ </Link>
+ </div>
+ <div className={`col col-${isFullWidthPage ? '8' : '9'} lg-hide md-hide`} />
+ <div className={`col col-${isFullWidthPage ? '6' : '5'} sm-hide xs-hide`} />
+ {!this._isViewingPortal() && (
+ <div className={menuClasses}>
+ <div className="flex justify-between">
+ <DropDownMenuItem
+ title="Developers"
+ subMenuItems={developerSectionMenuItems}
+ style={styles.menuItem}
+ isNightVersion={isNightVersion}
+ />
+ <TopBarMenuItem
+ title="Wiki"
+ path={`${WebsitePaths.Wiki}`}
+ style={styles.menuItem}
+ isNightVersion={isNightVersion}
+ />
+ <TopBarMenuItem
+ title="About"
+ path={`${WebsitePaths.About}`}
+ style={styles.menuItem}
+ isNightVersion={isNightVersion}
+ />
+ <TopBarMenuItem
+ title="Portal DApp"
+ path={`${WebsitePaths.Portal}`}
+ isPrimary={true}
+ style={styles.menuItem}
+ className={`${isFullWidthPage && 'md-hide'}`}
+ isNightVersion={isNightVersion}
+ />
+ </div>
+ </div>
+ )}
+ {this.props.blockchainIsLoaded &&
+ !_.isEmpty(this.props.userAddress) && (
+ <div className="col col-5 sm-hide xs-hide">{this._renderUser()}</div>
+ )}
+ <div className={`col ${isFullWidthPage ? 'col-2 pl2' : 'col-1'} md-hide lg-hide`}>
+ <div style={menuIconStyle}>
+ <i className="zmdi zmdi-menu" onClick={this._onMenuButtonClick.bind(this)} />
+ </div>
+ </div>
+ </div>
+ {this._renderDrawer()}
+ </div>
+ );
+ }
+ private _renderDrawer() {
+ return (
+ <Drawer
+ open={this.state.isDrawerOpen}
+ docked={false}
+ openSecondary={true}
+ onRequestChange={this._onMenuButtonClick.bind(this)}
+ >
+ {this._renderPortalMenu()}
+ {this._renderDocsMenu()}
+ {this._renderWiki()}
+ <div className="pl1 py1 mt3" style={{ backgroundColor: colors.lightGrey }}>
+ Website
+ </div>
+ <Link to={WebsitePaths.Home} className="text-decoration-none">
+ <MenuItem className="py2">Home</MenuItem>
+ </Link>
+ <Link to={`${WebsitePaths.Wiki}`} className="text-decoration-none">
+ <MenuItem className="py2">Wiki</MenuItem>
+ </Link>
+ {!this._isViewing0xjsDocs() && (
+ <Link to={WebsitePaths.ZeroExJs} className="text-decoration-none">
+ <MenuItem className="py2">0x.js Docs</MenuItem>
+ </Link>
+ )}
+ {!this._isViewingConnectDocs() && (
+ <Link to={WebsitePaths.Connect} className="text-decoration-none">
+ <MenuItem className="py2">0x Connect Docs</MenuItem>
+ </Link>
+ )}
+ {!this._isViewingSmartContractsDocs() && (
+ <Link to={WebsitePaths.SmartContracts} className="text-decoration-none">
+ <MenuItem className="py2">Smart Contract Docs</MenuItem>
+ </Link>
+ )}
+ {!this._isViewingPortal() && (
+ <Link to={`${WebsitePaths.Portal}`} className="text-decoration-none">
+ <MenuItem className="py2">Portal DApp</MenuItem>
+ </Link>
+ )}
+ <a className="text-decoration-none" target="_blank" href={`${WebsitePaths.Whitepaper}`}>
+ <MenuItem className="py2">Whitepaper</MenuItem>
+ </a>
+ <Link to={`${WebsitePaths.About}`} className="text-decoration-none">
+ <MenuItem className="py2">About</MenuItem>
+ </Link>
+ <a className="text-decoration-none" target="_blank" href={constants.URL_BLOG}>
+ <MenuItem className="py2">Blog</MenuItem>
+ </a>
+ <Link to={`${WebsitePaths.FAQ}`} className="text-decoration-none">
+ <MenuItem className="py2" onTouchTap={this._onMenuButtonClick.bind(this)}>
+ FAQ
+ </MenuItem>
+ </Link>
+ </Drawer>
+ );
+ }
+ private _renderDocsMenu(): React.ReactNode {
+ if (
+ (!this._isViewing0xjsDocs() && !this._isViewingSmartContractsDocs() && !this._isViewingConnectDocs()) ||
+ _.isUndefined(this.props.menu)
+ ) {
+ return undefined;
+ }
- const sectionTitle = `${this.props.docsInfo.displayName} Docs`;
- return (
- <div className="lg-hide md-hide">
- <div className="pl1 py1" style={{ backgroundColor: colors.lightGrey }}>
- {sectionTitle}
- </div>
- <NestedSidebarMenu
- topLevelMenu={this.props.menu}
- menuSubsectionsBySection={this.props.menuSubsectionsBySection}
- shouldDisplaySectionHeaders={false}
- onMenuItemClick={this._onMenuButtonClick.bind(this)}
- selectedVersion={this.props.docsVersion}
- docPath={this.props.docsInfo.websitePath}
- versions={this.props.availableDocVersions}
- />
- </div>
- );
- }
- private _renderWiki(): React.ReactNode {
- if (!this._isViewingWiki()) {
- return undefined;
- }
+ const sectionTitle = `${this.props.docsInfo.displayName} Docs`;
+ return (
+ <div className="lg-hide md-hide">
+ <div className="pl1 py1" style={{ backgroundColor: colors.lightGrey }}>
+ {sectionTitle}
+ </div>
+ <NestedSidebarMenu
+ topLevelMenu={this.props.menu}
+ menuSubsectionsBySection={this.props.menuSubsectionsBySection}
+ shouldDisplaySectionHeaders={false}
+ onMenuItemClick={this._onMenuButtonClick.bind(this)}
+ selectedVersion={this.props.docsVersion}
+ docPath={this.props.docsInfo.websitePath}
+ versions={this.props.availableDocVersions}
+ />
+ </div>
+ );
+ }
+ private _renderWiki(): React.ReactNode {
+ if (!this._isViewingWiki()) {
+ return undefined;
+ }
- return (
- <div className="lg-hide md-hide">
- <div className="pl1 py1" style={{ backgroundColor: colors.lightGrey }}>
- 0x Protocol Wiki
- </div>
- <NestedSidebarMenu
- topLevelMenu={this.props.menuSubsectionsBySection}
- menuSubsectionsBySection={this.props.menuSubsectionsBySection}
- shouldDisplaySectionHeaders={false}
- onMenuItemClick={this._onMenuButtonClick.bind(this)}
- />
- </div>
- );
- }
- private _renderPortalMenu(): React.ReactNode {
- if (!this._isViewingPortal()) {
- return undefined;
- }
+ return (
+ <div className="lg-hide md-hide">
+ <div className="pl1 py1" style={{ backgroundColor: colors.lightGrey }}>
+ 0x Protocol Wiki
+ </div>
+ <NestedSidebarMenu
+ topLevelMenu={this.props.menuSubsectionsBySection}
+ menuSubsectionsBySection={this.props.menuSubsectionsBySection}
+ shouldDisplaySectionHeaders={false}
+ onMenuItemClick={this._onMenuButtonClick.bind(this)}
+ />
+ </div>
+ );
+ }
+ private _renderPortalMenu(): React.ReactNode {
+ if (!this._isViewingPortal()) {
+ return undefined;
+ }
- return (
- <div className="lg-hide md-hide">
- <div className="pl1 py1" style={{ backgroundColor: colors.lightGrey }}>
- Portal DApp
- </div>
- <PortalMenu menuItemStyle={{ color: 'black' }} onClick={this._onMenuButtonClick.bind(this)} />
- </div>
- );
- }
- private _renderUser() {
- const userAddress = this.props.userAddress;
- const identiconDiameter = 26;
- return (
- <div className="flex right lg-pr0 md-pr2 sm-pr2" style={{ paddingTop: 16 }}>
- <div style={styles.address} data-tip={true} data-for="userAddressTooltip">
- {!_.isEmpty(userAddress) ? userAddress : ''}
- </div>
- <ReactTooltip id="userAddressTooltip">{userAddress}</ReactTooltip>
- <div>
- <Identicon address={userAddress} diameter={identiconDiameter} />
- </div>
- </div>
- );
- }
- private _onMenuButtonClick() {
- this.setState({
- isDrawerOpen: !this.state.isDrawerOpen,
- });
- }
- private _isViewingPortal() {
- return _.includes(this.props.location.pathname, WebsitePaths.Portal);
- }
- private _isViewingFAQ() {
- return _.includes(this.props.location.pathname, WebsitePaths.FAQ);
- }
- private _isViewing0xjsDocs() {
- return _.includes(this.props.location.pathname, WebsitePaths.ZeroExJs);
- }
- private _isViewingConnectDocs() {
- return _.includes(this.props.location.pathname, WebsitePaths.Connect);
- }
- private _isViewingSmartContractsDocs() {
- return _.includes(this.props.location.pathname, WebsitePaths.SmartContracts);
- }
- private _isViewingWiki() {
- return _.includes(this.props.location.pathname, WebsitePaths.Wiki);
- }
- private _shouldDisplayBottomBar() {
- return (
- this._isViewingWiki() ||
- this._isViewing0xjsDocs() ||
- this._isViewingFAQ() ||
- this._isViewingSmartContractsDocs() ||
- this._isViewingConnectDocs()
- );
- }
+ return (
+ <div className="lg-hide md-hide">
+ <div className="pl1 py1" style={{ backgroundColor: colors.lightGrey }}>
+ Portal DApp
+ </div>
+ <PortalMenu menuItemStyle={{ color: 'black' }} onClick={this._onMenuButtonClick.bind(this)} />
+ </div>
+ );
+ }
+ private _renderUser() {
+ const userAddress = this.props.userAddress;
+ const identiconDiameter = 26;
+ return (
+ <div className="flex right lg-pr0 md-pr2 sm-pr2" style={{ paddingTop: 16 }}>
+ <div style={styles.address} data-tip={true} data-for="userAddressTooltip">
+ {!_.isEmpty(userAddress) ? userAddress : ''}
+ </div>
+ <ReactTooltip id="userAddressTooltip">{userAddress}</ReactTooltip>
+ <div>
+ <Identicon address={userAddress} diameter={identiconDiameter} />
+ </div>
+ </div>
+ );
+ }
+ private _onMenuButtonClick() {
+ this.setState({
+ isDrawerOpen: !this.state.isDrawerOpen,
+ });
+ }
+ private _isViewingPortal() {
+ return _.includes(this.props.location.pathname, WebsitePaths.Portal);
+ }
+ private _isViewingFAQ() {
+ return _.includes(this.props.location.pathname, WebsitePaths.FAQ);
+ }
+ private _isViewing0xjsDocs() {
+ return _.includes(this.props.location.pathname, WebsitePaths.ZeroExJs);
+ }
+ private _isViewingConnectDocs() {
+ return _.includes(this.props.location.pathname, WebsitePaths.Connect);
+ }
+ private _isViewingSmartContractsDocs() {
+ return _.includes(this.props.location.pathname, WebsitePaths.SmartContracts);
+ }
+ private _isViewingWiki() {
+ return _.includes(this.props.location.pathname, WebsitePaths.Wiki);
+ }
+ private _shouldDisplayBottomBar() {
+ return (
+ this._isViewingWiki() ||
+ this._isViewing0xjsDocs() ||
+ this._isViewingFAQ() ||
+ this._isViewingSmartContractsDocs() ||
+ this._isViewingConnectDocs()
+ );
+ }
}
diff --git a/packages/website/ts/components/top_bar_menu_item.tsx b/packages/website/ts/components/top_bar_menu_item.tsx
index 0138740ba..96ee86142 100644
--- a/packages/website/ts/components/top_bar_menu_item.tsx
+++ b/packages/website/ts/components/top_bar_menu_item.tsx
@@ -4,49 +4,49 @@ import { Link } from 'react-router-dom';
import { colors } from 'ts/utils/colors';
const DEFAULT_STYLE = {
- color: colors.darkestGrey,
+ color: colors.darkestGrey,
};
interface TopBarMenuItemProps {
- title: string;
- path?: string;
- isPrimary?: boolean;
- style?: React.CSSProperties;
- className?: string;
- isNightVersion?: boolean;
+ title: string;
+ path?: string;
+ isPrimary?: boolean;
+ style?: React.CSSProperties;
+ className?: string;
+ isNightVersion?: boolean;
}
interface TopBarMenuItemState {}
export class TopBarMenuItem extends React.Component<TopBarMenuItemProps, TopBarMenuItemState> {
- public static defaultProps: Partial<TopBarMenuItemProps> = {
- isPrimary: false,
- style: DEFAULT_STYLE,
- className: '',
- isNightVersion: false,
- };
- public render() {
- const primaryStyles = this.props.isPrimary
- ? {
- borderRadius: 4,
- border: `1px solid ${this.props.isNightVersion ? colors.grey : colors.greyishPink}`,
- marginTop: 15,
- paddingLeft: 9,
- paddingRight: 9,
- width: 77,
- }
- : {};
- const menuItemColor = this.props.isNightVersion ? 'white' : this.props.style.color;
- const linkColor = _.isUndefined(menuItemColor) ? colors.darkestGrey : menuItemColor;
- return (
- <div
- className={`center ${this.props.className}`}
- style={{ ...this.props.style, ...primaryStyles, color: menuItemColor }}
- >
- <Link to={this.props.path} className="text-decoration-none" style={{ color: linkColor }}>
- {this.props.title}
- </Link>
- </div>
- );
- }
+ public static defaultProps: Partial<TopBarMenuItemProps> = {
+ isPrimary: false,
+ style: DEFAULT_STYLE,
+ className: '',
+ isNightVersion: false,
+ };
+ public render() {
+ const primaryStyles = this.props.isPrimary
+ ? {
+ borderRadius: 4,
+ border: `1px solid ${this.props.isNightVersion ? colors.grey : colors.greyishPink}`,
+ marginTop: 15,
+ paddingLeft: 9,
+ paddingRight: 9,
+ width: 77,
+ }
+ : {};
+ const menuItemColor = this.props.isNightVersion ? 'white' : this.props.style.color;
+ const linkColor = _.isUndefined(menuItemColor) ? colors.darkestGrey : menuItemColor;
+ return (
+ <div
+ className={`center ${this.props.className}`}
+ style={{ ...this.props.style, ...primaryStyles, color: menuItemColor }}
+ >
+ <Link to={this.props.path} className="text-decoration-none" style={{ color: linkColor }}>
+ {this.props.title}
+ </Link>
+ </div>
+ );
+ }
}
diff --git a/packages/website/ts/components/track_token_confirmation.tsx b/packages/website/ts/components/track_token_confirmation.tsx
index 1887bd11c..76971aefa 100644
--- a/packages/website/ts/components/track_token_confirmation.tsx
+++ b/packages/website/ts/components/track_token_confirmation.tsx
@@ -6,56 +6,56 @@ import { colors } from 'ts/utils/colors';
import { utils } from 'ts/utils/utils';
interface TrackTokenConfirmationProps {
- tokens: Token[];
- tokenByAddress: TokenByAddress;
- networkId: number;
- isAddingTokenToTracked: boolean;
+ tokens: Token[];
+ tokenByAddress: TokenByAddress;
+ networkId: number;
+ isAddingTokenToTracked: boolean;
}
interface TrackTokenConfirmationState {}
export class TrackTokenConfirmation extends React.Component<TrackTokenConfirmationProps, TrackTokenConfirmationState> {
- public render() {
- const isMultipleTokens = this.props.tokens.length > 1;
- const allTokens = _.values(this.props.tokenByAddress);
- return (
- <div style={{ color: colors.grey700 }}>
- {this.props.isAddingTokenToTracked ? (
- <div className="py4 my4 center">
- <span className="pr1">
- <i className="zmdi zmdi-spinner zmdi-hc-spin" />
- </span>
- <span>Adding token{isMultipleTokens && 's'}...</span>
- </div>
- ) : (
- <div>
- <div>You do not currently track the following token{isMultipleTokens && 's'}:</div>
- <div className="py2 clearfix mx-auto center" style={{ width: 355 }}>
- {_.map(this.props.tokens, (token: Token) => (
- <div
- key={`token-profile-${token.name}`}
- className={`col col-${isMultipleTokens ? '6' : '12'} px2`}
- >
- <Party
- label={token.name}
- address={token.address}
- networkId={this.props.networkId}
- alternativeImage={token.iconUrl}
- isInTokenRegistry={token.isRegistered}
- hasUniqueNameAndSymbol={utils.hasUniqueNameAndSymbol(allTokens, token)}
- />
- </div>
- ))}
- </div>
- <div>
- Tracking a token adds it to the balances section of 0x Portal and allows you to
- generate/fill orders involving the token
- {isMultipleTokens && 's'}. Would you like to start tracking{' '}
- {isMultipleTokens ? 'these' : 'this'} token?
- </div>
- </div>
- )}
- </div>
- );
- }
+ public render() {
+ const isMultipleTokens = this.props.tokens.length > 1;
+ const allTokens = _.values(this.props.tokenByAddress);
+ return (
+ <div style={{ color: colors.grey700 }}>
+ {this.props.isAddingTokenToTracked ? (
+ <div className="py4 my4 center">
+ <span className="pr1">
+ <i className="zmdi zmdi-spinner zmdi-hc-spin" />
+ </span>
+ <span>Adding token{isMultipleTokens && 's'}...</span>
+ </div>
+ ) : (
+ <div>
+ <div>You do not currently track the following token{isMultipleTokens && 's'}:</div>
+ <div className="py2 clearfix mx-auto center" style={{ width: 355 }}>
+ {_.map(this.props.tokens, (token: Token) => (
+ <div
+ key={`token-profile-${token.name}`}
+ className={`col col-${isMultipleTokens ? '6' : '12'} px2`}
+ >
+ <Party
+ label={token.name}
+ address={token.address}
+ networkId={this.props.networkId}
+ alternativeImage={token.iconUrl}
+ isInTokenRegistry={token.isRegistered}
+ hasUniqueNameAndSymbol={utils.hasUniqueNameAndSymbol(allTokens, token)}
+ />
+ </div>
+ ))}
+ </div>
+ <div>
+ Tracking a token adds it to the balances section of 0x Portal and allows you to
+ generate/fill orders involving the token
+ {isMultipleTokens && 's'}. Would you like to start tracking{' '}
+ {isMultipleTokens ? 'these' : 'this'} token?
+ </div>
+ </div>
+ )}
+ </div>
+ );
+ }
}
diff --git a/packages/website/ts/components/trade_history/trade_history.tsx b/packages/website/ts/components/trade_history/trade_history.tsx
index 3963135f7..635358627 100644
--- a/packages/website/ts/components/trade_history/trade_history.tsx
+++ b/packages/website/ts/components/trade_history/trade_history.tsx
@@ -10,106 +10,106 @@ import { utils } from 'ts/utils/utils';
const FILL_POLLING_INTERVAL = 1000;
interface TradeHistoryProps {
- tokenByAddress: TokenByAddress;
- userAddress: string;
- networkId: number;
+ tokenByAddress: TokenByAddress;
+ userAddress: string;
+ networkId: number;
}
interface TradeHistoryState {
- sortedFills: Fill[];
+ sortedFills: Fill[];
}
export class TradeHistory extends React.Component<TradeHistoryProps, TradeHistoryState> {
- private _fillPollingIntervalId: number;
- public constructor(props: TradeHistoryProps) {
- super(props);
- const sortedFills = this._getSortedFills();
- this.state = {
- sortedFills,
- };
- }
- public componentWillMount() {
- this._startPollingForFills();
- }
- public componentWillUnmount() {
- this._stopPollingForFills();
- }
- public componentDidMount() {
- window.scrollTo(0, 0);
- }
- public render() {
- return (
- <div className="lg-px4 md-px4 sm-px2">
- <h3>Trade history</h3>
- <Divider />
- <div className="pt2" style={{ height: 608, overflow: 'scroll' }}>
- {this._renderTrades()}
- </div>
- </div>
- );
- }
- private _renderTrades() {
- const numNonCustomFills = this._numFillsWithoutCustomERC20Tokens();
- if (numNonCustomFills === 0) {
- return this._renderEmptyNotice();
- }
+ private _fillPollingIntervalId: number;
+ public constructor(props: TradeHistoryProps) {
+ super(props);
+ const sortedFills = this._getSortedFills();
+ this.state = {
+ sortedFills,
+ };
+ }
+ public componentWillMount() {
+ this._startPollingForFills();
+ }
+ public componentWillUnmount() {
+ this._stopPollingForFills();
+ }
+ public componentDidMount() {
+ window.scrollTo(0, 0);
+ }
+ public render() {
+ return (
+ <div className="lg-px4 md-px4 sm-px2">
+ <h3>Trade history</h3>
+ <Divider />
+ <div className="pt2" style={{ height: 608, overflow: 'scroll' }}>
+ {this._renderTrades()}
+ </div>
+ </div>
+ );
+ }
+ private _renderTrades() {
+ const numNonCustomFills = this._numFillsWithoutCustomERC20Tokens();
+ if (numNonCustomFills === 0) {
+ return this._renderEmptyNotice();
+ }
- return _.map(this.state.sortedFills, (fill, index) => {
- return (
- <TradeHistoryItem
- key={`${fill.orderHash}-${fill.filledTakerTokenAmount}-${index}`}
- fill={fill}
- tokenByAddress={this.props.tokenByAddress}
- userAddress={this.props.userAddress}
- networkId={this.props.networkId}
- />
- );
- });
- }
- private _renderEmptyNotice() {
- return (
- <Paper className="mt1 p2 mx-auto center" style={{ width: '80%' }}>
- No filled orders yet.
- </Paper>
- );
- }
- private _numFillsWithoutCustomERC20Tokens() {
- let numNonCustomFills = 0;
- const tokens = _.values(this.props.tokenByAddress);
- _.each(this.state.sortedFills, fill => {
- const takerToken = _.find(tokens, token => {
- return token.address === fill.takerToken;
- });
- const makerToken = _.find(tokens, token => {
- return token.address === fill.makerToken;
- });
- // For now we don't show history items for orders using custom ERC20
- // tokens the client does not know how to display.
- // TODO: Try to retrieve the name/symbol of an unknown token in order to display it
- // Be sure to remove similar logic in trade_history_item.tsx
- if (!_.isUndefined(takerToken) && !_.isUndefined(makerToken)) {
- numNonCustomFills += 1;
- }
- });
- return numNonCustomFills;
- }
- private _startPollingForFills() {
- this._fillPollingIntervalId = window.setInterval(() => {
- const sortedFills = this._getSortedFills();
- if (!utils.deepEqual(sortedFills, this.state.sortedFills)) {
- this.setState({
- sortedFills,
- });
- }
- }, FILL_POLLING_INTERVAL);
- }
- private _stopPollingForFills() {
- clearInterval(this._fillPollingIntervalId);
- }
- private _getSortedFills() {
- const fillsByHash = tradeHistoryStorage.getUserFillsByHash(this.props.userAddress, this.props.networkId);
- const fills = _.values(fillsByHash);
- const sortedFills = _.sortBy(fills, [(fill: Fill) => fill.blockTimestamp * -1]);
- return sortedFills;
- }
+ return _.map(this.state.sortedFills, (fill, index) => {
+ return (
+ <TradeHistoryItem
+ key={`${fill.orderHash}-${fill.filledTakerTokenAmount}-${index}`}
+ fill={fill}
+ tokenByAddress={this.props.tokenByAddress}
+ userAddress={this.props.userAddress}
+ networkId={this.props.networkId}
+ />
+ );
+ });
+ }
+ private _renderEmptyNotice() {
+ return (
+ <Paper className="mt1 p2 mx-auto center" style={{ width: '80%' }}>
+ No filled orders yet.
+ </Paper>
+ );
+ }
+ private _numFillsWithoutCustomERC20Tokens() {
+ let numNonCustomFills = 0;
+ const tokens = _.values(this.props.tokenByAddress);
+ _.each(this.state.sortedFills, fill => {
+ const takerToken = _.find(tokens, token => {
+ return token.address === fill.takerToken;
+ });
+ const makerToken = _.find(tokens, token => {
+ return token.address === fill.makerToken;
+ });
+ // For now we don't show history items for orders using custom ERC20
+ // tokens the client does not know how to display.
+ // TODO: Try to retrieve the name/symbol of an unknown token in order to display it
+ // Be sure to remove similar logic in trade_history_item.tsx
+ if (!_.isUndefined(takerToken) && !_.isUndefined(makerToken)) {
+ numNonCustomFills += 1;
+ }
+ });
+ return numNonCustomFills;
+ }
+ private _startPollingForFills() {
+ this._fillPollingIntervalId = window.setInterval(() => {
+ const sortedFills = this._getSortedFills();
+ if (!utils.deepEqual(sortedFills, this.state.sortedFills)) {
+ this.setState({
+ sortedFills,
+ });
+ }
+ }, FILL_POLLING_INTERVAL);
+ }
+ private _stopPollingForFills() {
+ clearInterval(this._fillPollingIntervalId);
+ }
+ private _getSortedFills() {
+ const fillsByHash = tradeHistoryStorage.getUserFillsByHash(this.props.userAddress, this.props.networkId);
+ const fills = _.values(fillsByHash);
+ const sortedFills = _.sortBy(fills, [(fill: Fill) => fill.blockTimestamp * -1]);
+ return sortedFills;
+ }
}
diff --git a/packages/website/ts/components/trade_history/trade_history_item.tsx b/packages/website/ts/components/trade_history/trade_history_item.tsx
index 118bde3b9..7e42e64e6 100644
--- a/packages/website/ts/components/trade_history/trade_history_item.tsx
+++ b/packages/website/ts/components/trade_history/trade_history_item.tsx
@@ -14,157 +14,157 @@ const PRECISION = 5;
const IDENTICON_DIAMETER = 40;
interface TradeHistoryItemProps {
- fill: Fill;
- tokenByAddress: TokenByAddress;
- userAddress: string;
- networkId: number;
+ fill: Fill;
+ tokenByAddress: TokenByAddress;
+ userAddress: string;
+ networkId: number;
}
interface TradeHistoryItemState {}
export class TradeHistoryItem extends React.Component<TradeHistoryItemProps, TradeHistoryItemState> {
- public render() {
- const fill = this.props.fill;
- const tokens = _.values(this.props.tokenByAddress);
- const takerToken = _.find(tokens, token => {
- return token.address === fill.takerToken;
- });
- const makerToken = _.find(tokens, token => {
- return token.address === fill.makerToken;
- });
- // For now we don't show history items for orders using custom ERC20
- // tokens the client does not know how to display.
- // TODO: Try to retrieve the name/symbol of an unknown token in order to display it
- // Be sure to remove similar logic in trade_history.tsx
- if (_.isUndefined(takerToken) || _.isUndefined(makerToken)) {
- return null;
- }
+ public render() {
+ const fill = this.props.fill;
+ const tokens = _.values(this.props.tokenByAddress);
+ const takerToken = _.find(tokens, token => {
+ return token.address === fill.takerToken;
+ });
+ const makerToken = _.find(tokens, token => {
+ return token.address === fill.makerToken;
+ });
+ // For now we don't show history items for orders using custom ERC20
+ // tokens the client does not know how to display.
+ // TODO: Try to retrieve the name/symbol of an unknown token in order to display it
+ // Be sure to remove similar logic in trade_history.tsx
+ if (_.isUndefined(takerToken) || _.isUndefined(makerToken)) {
+ return null;
+ }
- const amountColStyle: React.CSSProperties = {
- fontWeight: 100,
- display: 'inline-block',
- };
- const amountColClassNames =
- 'col col-12 lg-col-4 md-col-4 lg-py2 md-py2 sm-py1 lg-pr2 md-pr2 \
+ const amountColStyle: React.CSSProperties = {
+ fontWeight: 100,
+ display: 'inline-block',
+ };
+ const amountColClassNames =
+ 'col col-12 lg-col-4 md-col-4 lg-py2 md-py2 sm-py1 lg-pr2 md-pr2 \
lg-right-align md-right-align sm-center';
- return (
- <Paper className="py1" style={{ margin: '3px 3px 15px 3px' }}>
- <div className="clearfix">
- <div className="col col-12 lg-col-1 md-col-1 pt2 lg-pl3 md-pl3">{this._renderDate()}</div>
- <div
- className="col col-12 lg-col-6 md-col-6 lg-pl3 md-pl3"
- style={{ fontSize: 12, fontWeight: 100 }}
- >
- <div className="flex sm-mx-auto xs-mx-auto" style={{ paddingTop: 4, width: 224 }}>
- <Party
- label="Maker"
- address={fill.maker}
- identiconDiameter={IDENTICON_DIAMETER}
- networkId={this.props.networkId}
- />
- <i style={{ fontSize: 30 }} className="zmdi zmdi-swap py3" />
- <Party
- label="Taker"
- address={fill.taker}
- identiconDiameter={IDENTICON_DIAMETER}
- networkId={this.props.networkId}
- />
- </div>
- </div>
- <div className={amountColClassNames} style={amountColStyle}>
- {this._renderAmounts(makerToken, takerToken)}
- </div>
- <div className="col col-12 lg-col-1 md-col-1 lg-pr3 md-pr3 lg-py3 md-py3 sm-pb1 sm-center">
- <div className="pt1 lg-right md-right sm-mx-auto" style={{ width: 13 }}>
- <EtherScanIcon
- addressOrTxHash={fill.transactionHash}
- networkId={this.props.networkId}
- etherscanLinkSuffixes={EtherscanLinkSuffixes.Tx}
- />
- </div>
- </div>
- </div>
- </Paper>
- );
- }
- private _renderAmounts(makerToken: Token, takerToken: Token) {
- const fill = this.props.fill;
- const filledTakerTokenAmountInUnits = ZeroEx.toUnitAmount(fill.filledTakerTokenAmount, takerToken.decimals);
- const filledMakerTokenAmountInUnits = ZeroEx.toUnitAmount(fill.filledMakerTokenAmount, takerToken.decimals);
- let exchangeRate = filledTakerTokenAmountInUnits.div(filledMakerTokenAmountInUnits);
- const fillMakerTokenAmount = ZeroEx.toBaseUnitAmount(filledMakerTokenAmountInUnits, makerToken.decimals);
+ return (
+ <Paper className="py1" style={{ margin: '3px 3px 15px 3px' }}>
+ <div className="clearfix">
+ <div className="col col-12 lg-col-1 md-col-1 pt2 lg-pl3 md-pl3">{this._renderDate()}</div>
+ <div
+ className="col col-12 lg-col-6 md-col-6 lg-pl3 md-pl3"
+ style={{ fontSize: 12, fontWeight: 100 }}
+ >
+ <div className="flex sm-mx-auto xs-mx-auto" style={{ paddingTop: 4, width: 224 }}>
+ <Party
+ label="Maker"
+ address={fill.maker}
+ identiconDiameter={IDENTICON_DIAMETER}
+ networkId={this.props.networkId}
+ />
+ <i style={{ fontSize: 30 }} className="zmdi zmdi-swap py3" />
+ <Party
+ label="Taker"
+ address={fill.taker}
+ identiconDiameter={IDENTICON_DIAMETER}
+ networkId={this.props.networkId}
+ />
+ </div>
+ </div>
+ <div className={amountColClassNames} style={amountColStyle}>
+ {this._renderAmounts(makerToken, takerToken)}
+ </div>
+ <div className="col col-12 lg-col-1 md-col-1 lg-pr3 md-pr3 lg-py3 md-py3 sm-pb1 sm-center">
+ <div className="pt1 lg-right md-right sm-mx-auto" style={{ width: 13 }}>
+ <EtherScanIcon
+ addressOrTxHash={fill.transactionHash}
+ networkId={this.props.networkId}
+ etherscanLinkSuffixes={EtherscanLinkSuffixes.Tx}
+ />
+ </div>
+ </div>
+ </div>
+ </Paper>
+ );
+ }
+ private _renderAmounts(makerToken: Token, takerToken: Token) {
+ const fill = this.props.fill;
+ const filledTakerTokenAmountInUnits = ZeroEx.toUnitAmount(fill.filledTakerTokenAmount, takerToken.decimals);
+ const filledMakerTokenAmountInUnits = ZeroEx.toUnitAmount(fill.filledMakerTokenAmount, takerToken.decimals);
+ let exchangeRate = filledTakerTokenAmountInUnits.div(filledMakerTokenAmountInUnits);
+ const fillMakerTokenAmount = ZeroEx.toBaseUnitAmount(filledMakerTokenAmountInUnits, makerToken.decimals);
- let receiveAmount;
- let receiveToken;
- let givenAmount;
- let givenToken;
- if (this.props.userAddress === fill.maker && this.props.userAddress === fill.taker) {
- receiveAmount = new BigNumber(0);
- givenAmount = new BigNumber(0);
- receiveToken = makerToken;
- givenToken = takerToken;
- } else if (this.props.userAddress === fill.maker) {
- receiveAmount = fill.filledTakerTokenAmount;
- givenAmount = fillMakerTokenAmount;
- receiveToken = takerToken;
- givenToken = makerToken;
- exchangeRate = new BigNumber(1).div(exchangeRate);
- } else if (this.props.userAddress === fill.taker) {
- receiveAmount = fillMakerTokenAmount;
- givenAmount = fill.filledTakerTokenAmount;
- receiveToken = makerToken;
- givenToken = takerToken;
- } else {
- // This condition should never be hit
- throw new Error("Found Fill that wasn't performed by this user");
- }
+ let receiveAmount;
+ let receiveToken;
+ let givenAmount;
+ let givenToken;
+ if (this.props.userAddress === fill.maker && this.props.userAddress === fill.taker) {
+ receiveAmount = new BigNumber(0);
+ givenAmount = new BigNumber(0);
+ receiveToken = makerToken;
+ givenToken = takerToken;
+ } else if (this.props.userAddress === fill.maker) {
+ receiveAmount = fill.filledTakerTokenAmount;
+ givenAmount = fillMakerTokenAmount;
+ receiveToken = takerToken;
+ givenToken = makerToken;
+ exchangeRate = new BigNumber(1).div(exchangeRate);
+ } else if (this.props.userAddress === fill.taker) {
+ receiveAmount = fillMakerTokenAmount;
+ givenAmount = fill.filledTakerTokenAmount;
+ receiveToken = makerToken;
+ givenToken = takerToken;
+ } else {
+ // This condition should never be hit
+ throw new Error("Found Fill that wasn't performed by this user");
+ }
- return (
- <div>
- <div style={{ color: colors.green400, fontSize: 16 }}>
- <span>+ </span>
- {this._renderAmount(receiveAmount, receiveToken.symbol, receiveToken.decimals)}
- </div>
- <div className="pb1 inline-block" style={{ color: colors.red200, fontSize: 16 }}>
- <span>- </span>
- {this._renderAmount(givenAmount, givenToken.symbol, givenToken.decimals)}
- </div>
- <div style={{ color: colors.grey400, fontSize: 14 }}>
- {exchangeRate.toFixed(PRECISION)} {givenToken.symbol}/{receiveToken.symbol}
- </div>
- </div>
- );
- }
- private _renderDate() {
- const blockMoment = moment.unix(this.props.fill.blockTimestamp);
- if (!blockMoment.isValid()) {
- return null;
- }
+ return (
+ <div>
+ <div style={{ color: colors.green400, fontSize: 16 }}>
+ <span>+ </span>
+ {this._renderAmount(receiveAmount, receiveToken.symbol, receiveToken.decimals)}
+ </div>
+ <div className="pb1 inline-block" style={{ color: colors.red200, fontSize: 16 }}>
+ <span>- </span>
+ {this._renderAmount(givenAmount, givenToken.symbol, givenToken.decimals)}
+ </div>
+ <div style={{ color: colors.grey400, fontSize: 14 }}>
+ {exchangeRate.toFixed(PRECISION)} {givenToken.symbol}/{receiveToken.symbol}
+ </div>
+ </div>
+ );
+ }
+ private _renderDate() {
+ const blockMoment = moment.unix(this.props.fill.blockTimestamp);
+ if (!blockMoment.isValid()) {
+ return null;
+ }
- const dayOfMonth = blockMoment.format('D');
- const monthAbreviation = blockMoment.format('MMM');
- const formattedBlockDate = blockMoment.format('H:mmA - MMMM D, YYYY');
- const dateTooltipId = `${this.props.fill.transactionHash}-date`;
+ const dayOfMonth = blockMoment.format('D');
+ const monthAbreviation = blockMoment.format('MMM');
+ const formattedBlockDate = blockMoment.format('H:mmA - MMMM D, YYYY');
+ const dateTooltipId = `${this.props.fill.transactionHash}-date`;
- return (
- <div data-tip={true} data-for={dateTooltipId}>
- <div className="center pt1" style={{ fontSize: 13 }}>
- {monthAbreviation}
- </div>
- <div className="center" style={{ fontSize: 24, fontWeight: 100 }}>
- {dayOfMonth}
- </div>
- <ReactTooltip id={dateTooltipId}>{formattedBlockDate}</ReactTooltip>
- </div>
- );
- }
- private _renderAmount(amount: BigNumber, symbol: string, decimals: number) {
- const unitAmount = ZeroEx.toUnitAmount(amount, decimals);
- return (
- <span>
- {unitAmount.toFixed(PRECISION)} {symbol}
- </span>
- );
- }
+ return (
+ <div data-tip={true} data-for={dateTooltipId}>
+ <div className="center pt1" style={{ fontSize: 13 }}>
+ {monthAbreviation}
+ </div>
+ <div className="center" style={{ fontSize: 24, fontWeight: 100 }}>
+ {dayOfMonth}
+ </div>
+ <ReactTooltip id={dateTooltipId}>{formattedBlockDate}</ReactTooltip>
+ </div>
+ );
+ }
+ private _renderAmount(amount: BigNumber, symbol: string, decimals: number) {
+ const unitAmount = ZeroEx.toUnitAmount(amount, decimals);
+ return (
+ <span>
+ {unitAmount.toFixed(PRECISION)} {symbol}
+ </span>
+ );
+ }
}
diff --git a/packages/website/ts/components/ui/alert.tsx b/packages/website/ts/components/ui/alert.tsx
index 91ef8f76e..54881b499 100644
--- a/packages/website/ts/components/ui/alert.tsx
+++ b/packages/website/ts/components/ui/alert.tsx
@@ -3,23 +3,23 @@ import { AlertTypes } from 'ts/types';
import { colors } from 'ts/utils/colors';
interface AlertProps {
- type: AlertTypes;
- message: string | React.ReactNode;
+ type: AlertTypes;
+ message: string | React.ReactNode;
}
export function Alert(props: AlertProps) {
- const isAlert = props.type === AlertTypes.ERROR;
- const errMsgStyles = {
- background: isAlert ? colors.red200 : colors.lightestGreen,
- color: colors.white,
- marginTop: 10,
- padding: 4,
- paddingLeft: 8,
- };
+ const isAlert = props.type === AlertTypes.ERROR;
+ const errMsgStyles = {
+ background: isAlert ? colors.red200 : colors.lightestGreen,
+ color: colors.white,
+ marginTop: 10,
+ padding: 4,
+ paddingLeft: 8,
+ };
- return (
- <div className="rounded center" style={errMsgStyles}>
- {props.message}
- </div>
- );
+ return (
+ <div className="rounded center" style={errMsgStyles}>
+ {props.message}
+ </div>
+ );
}
diff --git a/packages/website/ts/components/ui/badge.tsx b/packages/website/ts/components/ui/badge.tsx
index 1d2d81af6..7f7ea006e 100644
--- a/packages/website/ts/components/ui/badge.tsx
+++ b/packages/website/ts/components/ui/badge.tsx
@@ -3,55 +3,55 @@ import * as React from 'react';
import { Styles } from 'ts/types';
const styles: Styles = {
- badge: {
- width: 50,
- fontSize: 11,
- height: 10,
- borderRadius: 5,
- marginTop: 25,
- lineHeight: 0.9,
- fontFamily: 'Roboto Mono',
- marginLeft: 3,
- marginRight: 3,
- },
+ badge: {
+ width: 50,
+ fontSize: 11,
+ height: 10,
+ borderRadius: 5,
+ marginTop: 25,
+ lineHeight: 0.9,
+ fontFamily: 'Roboto Mono',
+ marginLeft: 3,
+ marginRight: 3,
+ },
};
interface BadgeProps {
- title: string;
- backgroundColor: string;
+ title: string;
+ backgroundColor: string;
}
interface BadgeState {
- isHovering: boolean;
+ isHovering: boolean;
}
export class Badge extends React.Component<BadgeProps, BadgeState> {
- constructor(props: BadgeProps) {
- super(props);
- this.state = {
- isHovering: false,
- };
- }
- public render() {
- const badgeStyle = {
- ...styles.badge,
- backgroundColor: this.props.backgroundColor,
- opacity: this.state.isHovering ? 0.7 : 1,
- };
- return (
- <div
- className="p1 center"
- style={badgeStyle}
- onMouseOver={this._setHoverState.bind(this, true)}
- onMouseOut={this._setHoverState.bind(this, false)}
- >
- {this.props.title}
- </div>
- );
- }
- private _setHoverState(isHovering: boolean) {
- this.setState({
- isHovering,
- });
- }
+ constructor(props: BadgeProps) {
+ super(props);
+ this.state = {
+ isHovering: false,
+ };
+ }
+ public render() {
+ const badgeStyle = {
+ ...styles.badge,
+ backgroundColor: this.props.backgroundColor,
+ opacity: this.state.isHovering ? 0.7 : 1,
+ };
+ return (
+ <div
+ className="p1 center"
+ style={badgeStyle}
+ onMouseOver={this._setHoverState.bind(this, true)}
+ onMouseOut={this._setHoverState.bind(this, false)}
+ >
+ {this.props.title}
+ </div>
+ );
+ }
+ private _setHoverState(isHovering: boolean) {
+ this.setState({
+ isHovering,
+ });
+ }
}
diff --git a/packages/website/ts/components/ui/copy_icon.tsx b/packages/website/ts/components/ui/copy_icon.tsx
index 72ab77e1f..df55e0922 100644
--- a/packages/website/ts/components/ui/copy_icon.tsx
+++ b/packages/website/ts/components/ui/copy_icon.tsx
@@ -6,74 +6,74 @@ import ReactTooltip = require('react-tooltip');
import { colors } from 'ts/utils/colors';
interface CopyIconProps {
- data: string;
- callToAction?: string;
+ data: string;
+ callToAction?: string;
}
interface CopyIconState {
- isHovering: boolean;
+ isHovering: boolean;
}
export class CopyIcon extends React.Component<CopyIconProps, CopyIconState> {
- private _copyTooltipTimeoutId: number;
- private _copyable: HTMLInputElement;
- constructor(props: CopyIconProps) {
- super(props);
- this.state = {
- isHovering: false,
- };
- }
- public componentDidUpdate() {
- // Remove tooltip if hover away
- if (!this.state.isHovering && this._copyTooltipTimeoutId) {
- clearInterval(this._copyTooltipTimeoutId);
- this._hideTooltip();
- }
- }
- public render() {
- return (
- <div className="inline-block">
- <CopyToClipboard text={this.props.data} onCopy={this._onCopy.bind(this)}>
- <div
- className="inline flex"
- style={{ cursor: 'pointer', color: colors.amber600 }}
- ref={this._setRefToProperty.bind(this)}
- data-tip={true}
- data-for="copy"
- data-event="click"
- data-iscapture={true} // This let's the click event continue to propogate
- onMouseOver={this._setHoverState.bind(this, true)}
- onMouseOut={this._setHoverState.bind(this, false)}
- >
- <div>
- <i style={{ fontSize: 15 }} className="zmdi zmdi-copy" />
- </div>
- {this.props.callToAction && <div className="pl1">{this.props.callToAction}</div>}
- </div>
- </CopyToClipboard>
- <ReactTooltip id="copy">Copied!</ReactTooltip>
- </div>
- );
- }
- private _setRefToProperty(el: HTMLInputElement) {
- this._copyable = el;
- }
- private _setHoverState(isHovering: boolean) {
- this.setState({
- isHovering,
- });
- }
- private _onCopy() {
- if (this._copyTooltipTimeoutId) {
- clearInterval(this._copyTooltipTimeoutId);
- }
+ private _copyTooltipTimeoutId: number;
+ private _copyable: HTMLInputElement;
+ constructor(props: CopyIconProps) {
+ super(props);
+ this.state = {
+ isHovering: false,
+ };
+ }
+ public componentDidUpdate() {
+ // Remove tooltip if hover away
+ if (!this.state.isHovering && this._copyTooltipTimeoutId) {
+ clearInterval(this._copyTooltipTimeoutId);
+ this._hideTooltip();
+ }
+ }
+ public render() {
+ return (
+ <div className="inline-block">
+ <CopyToClipboard text={this.props.data} onCopy={this._onCopy.bind(this)}>
+ <div
+ className="inline flex"
+ style={{ cursor: 'pointer', color: colors.amber600 }}
+ ref={this._setRefToProperty.bind(this)}
+ data-tip={true}
+ data-for="copy"
+ data-event="click"
+ data-iscapture={true} // This let's the click event continue to propogate
+ onMouseOver={this._setHoverState.bind(this, true)}
+ onMouseOut={this._setHoverState.bind(this, false)}
+ >
+ <div>
+ <i style={{ fontSize: 15 }} className="zmdi zmdi-copy" />
+ </div>
+ {this.props.callToAction && <div className="pl1">{this.props.callToAction}</div>}
+ </div>
+ </CopyToClipboard>
+ <ReactTooltip id="copy">Copied!</ReactTooltip>
+ </div>
+ );
+ }
+ private _setRefToProperty(el: HTMLInputElement) {
+ this._copyable = el;
+ }
+ private _setHoverState(isHovering: boolean) {
+ this.setState({
+ isHovering,
+ });
+ }
+ private _onCopy() {
+ if (this._copyTooltipTimeoutId) {
+ clearInterval(this._copyTooltipTimeoutId);
+ }
- const tooltipLifespanMs = 1000;
- this._copyTooltipTimeoutId = window.setTimeout(() => {
- this._hideTooltip();
- }, tooltipLifespanMs);
- }
- private _hideTooltip() {
- ReactTooltip.hide(ReactDOM.findDOMNode(this._copyable));
- }
+ const tooltipLifespanMs = 1000;
+ this._copyTooltipTimeoutId = window.setTimeout(() => {
+ this._hideTooltip();
+ }, tooltipLifespanMs);
+ }
+ private _hideTooltip() {
+ ReactTooltip.hide(ReactDOM.findDOMNode(this._copyable));
+ }
}
diff --git a/packages/website/ts/components/ui/drop_down_menu_item.tsx b/packages/website/ts/components/ui/drop_down_menu_item.tsx
index 64f88f318..a578fb4f9 100644
--- a/packages/website/ts/components/ui/drop_down_menu_item.tsx
+++ b/packages/website/ts/components/ui/drop_down_menu_item.tsx
@@ -6,99 +6,99 @@ import { colors } from 'ts/utils/colors';
const CHECK_CLOSE_POPOVER_INTERVAL_MS = 300;
const DEFAULT_STYLE = {
- fontSize: 14,
+ fontSize: 14,
};
interface DropDownMenuItemProps {
- title: string;
- subMenuItems: React.ReactNode[];
- style?: React.CSSProperties;
- menuItemStyle?: React.CSSProperties;
- isNightVersion?: boolean;
+ title: string;
+ subMenuItems: React.ReactNode[];
+ style?: React.CSSProperties;
+ menuItemStyle?: React.CSSProperties;
+ isNightVersion?: boolean;
}
interface DropDownMenuItemState {
- isDropDownOpen: boolean;
- anchorEl?: HTMLInputElement;
+ isDropDownOpen: boolean;
+ anchorEl?: HTMLInputElement;
}
export class DropDownMenuItem extends React.Component<DropDownMenuItemProps, DropDownMenuItemState> {
- public static defaultProps: Partial<DropDownMenuItemProps> = {
- style: DEFAULT_STYLE,
- menuItemStyle: DEFAULT_STYLE,
- isNightVersion: false,
- };
- private _isHovering: boolean;
- private _popoverCloseCheckIntervalId: number;
- constructor(props: DropDownMenuItemProps) {
- super(props);
- this.state = {
- isDropDownOpen: false,
- };
- }
- public componentDidMount() {
- this._popoverCloseCheckIntervalId = window.setInterval(() => {
- this._checkIfShouldClosePopover();
- }, CHECK_CLOSE_POPOVER_INTERVAL_MS);
- }
- public componentWillUnmount() {
- window.clearInterval(this._popoverCloseCheckIntervalId);
- }
- public render() {
- const colorStyle = this.props.isNightVersion ? 'white' : this.props.style.color;
- return (
- <div
- style={{ ...this.props.style, color: colorStyle }}
- onMouseEnter={this._onHover.bind(this)}
- onMouseLeave={this._onHoverOff.bind(this)}
- >
- <div className="flex relative">
- <div style={{ paddingRight: 10 }}>{this.props.title}</div>
- <div className="absolute" style={{ paddingLeft: 3, right: 3, top: -2 }}>
- <i className="zmdi zmdi-caret-right" style={{ fontSize: 22 }} />
- </div>
- </div>
- <Popover
- open={this.state.isDropDownOpen}
- anchorEl={this.state.anchorEl}
- anchorOrigin={{ horizontal: 'middle', vertical: 'bottom' }}
- targetOrigin={{ horizontal: 'middle', vertical: 'top' }}
- onRequestClose={this._closePopover.bind(this)}
- useLayerForClickAway={false}
- >
- <div onMouseEnter={this._onHover.bind(this)} onMouseLeave={this._onHoverOff.bind(this)}>
- <Menu style={{ color: colors.grey }}>{this.props.subMenuItems}</Menu>
- </div>
- </Popover>
- </div>
- );
- }
- private _onHover(event: React.FormEvent<HTMLInputElement>) {
- this._isHovering = true;
- this._checkIfShouldOpenPopover(event);
- }
- private _checkIfShouldOpenPopover(event: React.FormEvent<HTMLInputElement>) {
- if (this.state.isDropDownOpen) {
- return; // noop
- }
+ public static defaultProps: Partial<DropDownMenuItemProps> = {
+ style: DEFAULT_STYLE,
+ menuItemStyle: DEFAULT_STYLE,
+ isNightVersion: false,
+ };
+ private _isHovering: boolean;
+ private _popoverCloseCheckIntervalId: number;
+ constructor(props: DropDownMenuItemProps) {
+ super(props);
+ this.state = {
+ isDropDownOpen: false,
+ };
+ }
+ public componentDidMount() {
+ this._popoverCloseCheckIntervalId = window.setInterval(() => {
+ this._checkIfShouldClosePopover();
+ }, CHECK_CLOSE_POPOVER_INTERVAL_MS);
+ }
+ public componentWillUnmount() {
+ window.clearInterval(this._popoverCloseCheckIntervalId);
+ }
+ public render() {
+ const colorStyle = this.props.isNightVersion ? 'white' : this.props.style.color;
+ return (
+ <div
+ style={{ ...this.props.style, color: colorStyle }}
+ onMouseEnter={this._onHover.bind(this)}
+ onMouseLeave={this._onHoverOff.bind(this)}
+ >
+ <div className="flex relative">
+ <div style={{ paddingRight: 10 }}>{this.props.title}</div>
+ <div className="absolute" style={{ paddingLeft: 3, right: 3, top: -2 }}>
+ <i className="zmdi zmdi-caret-right" style={{ fontSize: 22 }} />
+ </div>
+ </div>
+ <Popover
+ open={this.state.isDropDownOpen}
+ anchorEl={this.state.anchorEl}
+ anchorOrigin={{ horizontal: 'middle', vertical: 'bottom' }}
+ targetOrigin={{ horizontal: 'middle', vertical: 'top' }}
+ onRequestClose={this._closePopover.bind(this)}
+ useLayerForClickAway={false}
+ >
+ <div onMouseEnter={this._onHover.bind(this)} onMouseLeave={this._onHoverOff.bind(this)}>
+ <Menu style={{ color: colors.grey }}>{this.props.subMenuItems}</Menu>
+ </div>
+ </Popover>
+ </div>
+ );
+ }
+ private _onHover(event: React.FormEvent<HTMLInputElement>) {
+ this._isHovering = true;
+ this._checkIfShouldOpenPopover(event);
+ }
+ private _checkIfShouldOpenPopover(event: React.FormEvent<HTMLInputElement>) {
+ if (this.state.isDropDownOpen) {
+ return; // noop
+ }
- this.setState({
- isDropDownOpen: true,
- anchorEl: event.currentTarget,
- });
- }
- private _onHoverOff(event: React.FormEvent<HTMLInputElement>) {
- this._isHovering = false;
- }
- private _checkIfShouldClosePopover() {
- if (!this.state.isDropDownOpen || this._isHovering) {
- return; // noop
- }
- this._closePopover();
- }
- private _closePopover() {
- this.setState({
- isDropDownOpen: false,
- });
- }
+ this.setState({
+ isDropDownOpen: true,
+ anchorEl: event.currentTarget,
+ });
+ }
+ private _onHoverOff(event: React.FormEvent<HTMLInputElement>) {
+ this._isHovering = false;
+ }
+ private _checkIfShouldClosePopover() {
+ if (!this.state.isDropDownOpen || this._isHovering) {
+ return; // noop
+ }
+ this._closePopover();
+ }
+ private _closePopover() {
+ this.setState({
+ isDropDownOpen: false,
+ });
+ }
}
diff --git a/packages/website/ts/components/ui/ethereum_address.tsx b/packages/website/ts/components/ui/ethereum_address.tsx
index ba51135be..b75d97e39 100644
--- a/packages/website/ts/components/ui/ethereum_address.tsx
+++ b/packages/website/ts/components/ui/ethereum_address.tsx
@@ -5,26 +5,26 @@ import { EtherscanLinkSuffixes } from 'ts/types';
import { utils } from 'ts/utils/utils';
interface EthereumAddressProps {
- address: string;
- networkId: number;
+ address: string;
+ networkId: number;
}
export const EthereumAddress = (props: EthereumAddressProps) => {
- const tooltipId = `${props.address}-ethereum-address`;
- const truncatedAddress = utils.getAddressBeginAndEnd(props.address);
- return (
- <div>
- <div className="inline" style={{ fontSize: 13 }} data-tip={true} data-for={tooltipId}>
- {truncatedAddress}
- </div>
- <div className="pl1 inline">
- <EtherScanIcon
- addressOrTxHash={props.address}
- networkId={props.networkId}
- etherscanLinkSuffixes={EtherscanLinkSuffixes.Address}
- />
- </div>
- <ReactTooltip id={tooltipId}>{props.address}</ReactTooltip>
- </div>
- );
+ const tooltipId = `${props.address}-ethereum-address`;
+ const truncatedAddress = utils.getAddressBeginAndEnd(props.address);
+ return (
+ <div>
+ <div className="inline" style={{ fontSize: 13 }} data-tip={true} data-for={tooltipId}>
+ {truncatedAddress}
+ </div>
+ <div className="pl1 inline">
+ <EtherScanIcon
+ addressOrTxHash={props.address}
+ networkId={props.networkId}
+ etherscanLinkSuffixes={EtherscanLinkSuffixes.Address}
+ />
+ </div>
+ <ReactTooltip id={tooltipId}>{props.address}</ReactTooltip>
+ </div>
+ );
};
diff --git a/packages/website/ts/components/ui/etherscan_icon.tsx b/packages/website/ts/components/ui/etherscan_icon.tsx
index 5b224c3e1..3b17bd0fa 100644
--- a/packages/website/ts/components/ui/etherscan_icon.tsx
+++ b/packages/website/ts/components/ui/etherscan_icon.tsx
@@ -6,36 +6,36 @@ import { colors } from 'ts/utils/colors';
import { utils } from 'ts/utils/utils';
interface EtherScanIconProps {
- addressOrTxHash: string;
- etherscanLinkSuffixes: EtherscanLinkSuffixes;
- networkId: number;
+ addressOrTxHash: string;
+ etherscanLinkSuffixes: EtherscanLinkSuffixes;
+ networkId: number;
}
export const EtherScanIcon = (props: EtherScanIconProps) => {
- const etherscanLinkIfExists = utils.getEtherScanLinkIfExists(
- props.addressOrTxHash,
- props.networkId,
- EtherscanLinkSuffixes.Address,
- );
- const transactionTooltipId = `${props.addressOrTxHash}-etherscan-icon-tooltip`;
- return (
- <div className="inline">
- {!_.isUndefined(etherscanLinkIfExists) ? (
- <a href={etherscanLinkIfExists} target="_blank">
- {renderIcon()}
- </a>
- ) : (
- <div className="inline" data-tip={true} data-for={transactionTooltipId}>
- {renderIcon()}
- <ReactTooltip id={transactionTooltipId}>
- Your network (id: {props.networkId}) is not supported by Etherscan
- </ReactTooltip>
- </div>
- )}
- </div>
- );
+ const etherscanLinkIfExists = utils.getEtherScanLinkIfExists(
+ props.addressOrTxHash,
+ props.networkId,
+ EtherscanLinkSuffixes.Address,
+ );
+ const transactionTooltipId = `${props.addressOrTxHash}-etherscan-icon-tooltip`;
+ return (
+ <div className="inline">
+ {!_.isUndefined(etherscanLinkIfExists) ? (
+ <a href={etherscanLinkIfExists} target="_blank">
+ {renderIcon()}
+ </a>
+ ) : (
+ <div className="inline" data-tip={true} data-for={transactionTooltipId}>
+ {renderIcon()}
+ <ReactTooltip id={transactionTooltipId}>
+ Your network (id: {props.networkId}) is not supported by Etherscan
+ </ReactTooltip>
+ </div>
+ )}
+ </div>
+ );
};
function renderIcon() {
- return <i style={{ color: colors.amber600 }} className="zmdi zmdi-open-in-new" />;
+ return <i style={{ color: colors.amber600 }} className="zmdi zmdi-open-in-new" />;
}
diff --git a/packages/website/ts/components/ui/fake_text_field.tsx b/packages/website/ts/components/ui/fake_text_field.tsx
index 6d321bb46..f3d9410f6 100644
--- a/packages/website/ts/components/ui/fake_text_field.tsx
+++ b/packages/website/ts/components/ui/fake_text_field.tsx
@@ -3,32 +3,32 @@ import { InputLabel } from 'ts/components/ui/input_label';
import { Styles } from 'ts/types';
const styles: Styles = {
- hr: {
- borderBottom: '1px solid rgb(224, 224, 224)',
- borderLeft: 'none rgb(224, 224, 224)',
- borderRight: 'none rgb(224, 224, 224)',
- borderTop: 'none rgb(224, 224, 224)',
- bottom: 6,
- boxSizing: 'content-box',
- margin: 0,
- position: 'absolute',
- width: '100%',
- },
+ hr: {
+ borderBottom: '1px solid rgb(224, 224, 224)',
+ borderLeft: 'none rgb(224, 224, 224)',
+ borderRight: 'none rgb(224, 224, 224)',
+ borderTop: 'none rgb(224, 224, 224)',
+ bottom: 6,
+ boxSizing: 'content-box',
+ margin: 0,
+ position: 'absolute',
+ width: '100%',
+ },
};
interface FakeTextFieldProps {
- label?: React.ReactNode | string;
- children?: any;
+ label?: React.ReactNode | string;
+ children?: any;
}
export function FakeTextField(props: FakeTextFieldProps) {
- return (
- <div className="relative">
- {props.label !== '' && <InputLabel text={props.label} />}
- <div className="pb2" style={{ height: 23 }}>
- {props.children}
- </div>
- <hr style={styles.hr} />
- </div>
- );
+ return (
+ <div className="relative">
+ {props.label !== '' && <InputLabel text={props.label} />}
+ <div className="pb2" style={{ height: 23 }}>
+ {props.children}
+ </div>
+ <hr style={styles.hr} />
+ </div>
+ );
}
diff --git a/packages/website/ts/components/ui/flash_message.tsx b/packages/website/ts/components/ui/flash_message.tsx
index 57a66d21f..2cb1fc764 100644
--- a/packages/website/ts/components/ui/flash_message.tsx
+++ b/packages/website/ts/components/ui/flash_message.tsx
@@ -6,35 +6,35 @@ import { Dispatcher } from 'ts/redux/dispatcher';
const SHOW_DURATION_MS = 4000;
interface FlashMessageProps {
- dispatcher: Dispatcher;
- flashMessage?: string | React.ReactNode;
- showDurationMs?: number;
- bodyStyle?: React.CSSProperties;
+ dispatcher: Dispatcher;
+ flashMessage?: string | React.ReactNode;
+ showDurationMs?: number;
+ bodyStyle?: React.CSSProperties;
}
interface FlashMessageState {}
export class FlashMessage extends React.Component<FlashMessageProps, FlashMessageState> {
- public static defaultProps: Partial<FlashMessageProps> = {
- showDurationMs: SHOW_DURATION_MS,
- bodyStyle: {},
- };
- public render() {
- if (!_.isUndefined(this.props.flashMessage)) {
- return (
- <Snackbar
- open={true}
- message={this.props.flashMessage}
- autoHideDuration={this.props.showDurationMs}
- onRequestClose={this._onClose.bind(this)}
- bodyStyle={this.props.bodyStyle}
- />
- );
- } else {
- return null;
- }
- }
- private _onClose() {
- this.props.dispatcher.hideFlashMessage();
- }
+ public static defaultProps: Partial<FlashMessageProps> = {
+ showDurationMs: SHOW_DURATION_MS,
+ bodyStyle: {},
+ };
+ public render() {
+ if (!_.isUndefined(this.props.flashMessage)) {
+ return (
+ <Snackbar
+ open={true}
+ message={this.props.flashMessage}
+ autoHideDuration={this.props.showDurationMs}
+ onRequestClose={this._onClose.bind(this)}
+ bodyStyle={this.props.bodyStyle}
+ />
+ );
+ } else {
+ return null;
+ }
+ }
+ private _onClose() {
+ this.props.dispatcher.hideFlashMessage();
+ }
}
diff --git a/packages/website/ts/components/ui/help_tooltip.tsx b/packages/website/ts/components/ui/help_tooltip.tsx
index c946a70cb..d827eebb9 100644
--- a/packages/website/ts/components/ui/help_tooltip.tsx
+++ b/packages/website/ts/components/ui/help_tooltip.tsx
@@ -2,21 +2,21 @@ import * as React from 'react';
import ReactTooltip = require('react-tooltip');
interface HelpTooltipProps {
- style?: React.CSSProperties;
- explanation: React.ReactNode;
+ style?: React.CSSProperties;
+ explanation: React.ReactNode;
}
export const HelpTooltip = (props: HelpTooltipProps) => {
- return (
- <div
- style={{ ...props.style }}
- className="inline-block"
- data-tip={props.explanation}
- data-for="helpTooltip"
- data-multiline={true}
- >
- <i style={{ fontSize: 16 }} className="zmdi zmdi-help" />
- <ReactTooltip id="helpTooltip" />
- </div>
- );
+ return (
+ <div
+ style={{ ...props.style }}
+ className="inline-block"
+ data-tip={props.explanation}
+ data-for="helpTooltip"
+ data-multiline={true}
+ >
+ <i style={{ fontSize: 16 }} className="zmdi zmdi-help" />
+ <ReactTooltip id="helpTooltip" />
+ </div>
+ );
};
diff --git a/packages/website/ts/components/ui/identicon.tsx b/packages/website/ts/components/ui/identicon.tsx
index bad7a657d..bad6c2a78 100644
--- a/packages/website/ts/components/ui/identicon.tsx
+++ b/packages/website/ts/components/ui/identicon.tsx
@@ -4,45 +4,45 @@ import * as React from 'react';
import { constants } from 'ts/utils/constants';
interface IdenticonProps {
- address: string;
- diameter: number;
- style?: React.CSSProperties;
+ address: string;
+ diameter: number;
+ style?: React.CSSProperties;
}
interface IdenticonState {}
export class Identicon extends React.Component<IdenticonProps, IdenticonState> {
- public static defaultProps: Partial<IdenticonProps> = {
- style: {},
- };
- public render() {
- let address = this.props.address;
- if (_.isEmpty(address)) {
- address = constants.NULL_ADDRESS;
- }
- const diameter = this.props.diameter;
- const icon = blockies({
- seed: address.toLowerCase(),
- });
- return (
- <div
- className="circle mx-auto relative transitionFix"
- style={{
- width: diameter,
- height: diameter,
- overflow: 'hidden',
- ...this.props.style,
- }}
- >
- <img
- src={icon.toDataURL()}
- style={{
- width: diameter,
- height: diameter,
- imageRendering: 'pixelated',
- }}
- />
- </div>
- );
- }
+ public static defaultProps: Partial<IdenticonProps> = {
+ style: {},
+ };
+ public render() {
+ let address = this.props.address;
+ if (_.isEmpty(address)) {
+ address = constants.NULL_ADDRESS;
+ }
+ const diameter = this.props.diameter;
+ const icon = blockies({
+ seed: address.toLowerCase(),
+ });
+ return (
+ <div
+ className="circle mx-auto relative transitionFix"
+ style={{
+ width: diameter,
+ height: diameter,
+ overflow: 'hidden',
+ ...this.props.style,
+ }}
+ >
+ <img
+ src={icon.toDataURL()}
+ style={{
+ width: diameter,
+ height: diameter,
+ imageRendering: 'pixelated',
+ }}
+ />
+ </div>
+ );
+ }
}
diff --git a/packages/website/ts/components/ui/input_label.tsx b/packages/website/ts/components/ui/input_label.tsx
index 1cbda6692..e2009ad20 100644
--- a/packages/website/ts/components/ui/input_label.tsx
+++ b/packages/website/ts/components/ui/input_label.tsx
@@ -2,24 +2,24 @@ import * as React from 'react';
import { colors } from 'ts/utils/colors';
export interface InputLabelProps {
- text: string | Element | React.ReactNode;
+ text: string | Element | React.ReactNode;
}
const styles = {
- label: {
- color: colors.grey,
- fontSize: 12,
- pointerEvents: 'none',
- textAlign: 'left',
- transform: 'scale(0.75) translate(0px, -28px)',
- transformOrigin: 'left top 0px',
- transition: 'all 450ms cubic-bezier(0.23, 1, 0.32, 1) 0ms',
- userSelect: 'none',
- width: 240,
- zIndex: 1,
- },
+ label: {
+ color: colors.grey,
+ fontSize: 12,
+ pointerEvents: 'none',
+ textAlign: 'left',
+ transform: 'scale(0.75) translate(0px, -28px)',
+ transformOrigin: 'left top 0px',
+ transition: 'all 450ms cubic-bezier(0.23, 1, 0.32, 1) 0ms',
+ userSelect: 'none',
+ width: 240,
+ zIndex: 1,
+ },
};
export const InputLabel = (props: InputLabelProps) => {
- return <label style={styles.label}>{props.text}</label>;
+ return <label style={styles.label}>{props.text}</label>;
};
diff --git a/packages/website/ts/components/ui/lifecycle_raised_button.tsx b/packages/website/ts/components/ui/lifecycle_raised_button.tsx
index fd23912f1..8ff856a75 100644
--- a/packages/website/ts/components/ui/lifecycle_raised_button.tsx
+++ b/packages/website/ts/components/ui/lifecycle_raised_button.tsx
@@ -7,97 +7,97 @@ import { utils } from 'ts/utils/utils';
const COMPLETE_STATE_SHOW_LENGTH_MS = 2000;
enum ButtonState {
- READY,
- LOADING,
- COMPLETE,
+ READY,
+ LOADING,
+ COMPLETE,
}
interface LifeCycleRaisedButtonProps {
- isHidden?: boolean;
- isDisabled?: boolean;
- isPrimary?: boolean;
- labelReady: React.ReactNode | string;
- labelLoading: React.ReactNode | string;
- labelComplete: React.ReactNode | string;
- onClickAsyncFn: () => Promise<boolean>;
- backgroundColor?: string;
- labelColor?: string;
+ isHidden?: boolean;
+ isDisabled?: boolean;
+ isPrimary?: boolean;
+ labelReady: React.ReactNode | string;
+ labelLoading: React.ReactNode | string;
+ labelComplete: React.ReactNode | string;
+ onClickAsyncFn: () => Promise<boolean>;
+ backgroundColor?: string;
+ labelColor?: string;
}
interface LifeCycleRaisedButtonState {
- buttonState: ButtonState;
+ buttonState: ButtonState;
}
export class LifeCycleRaisedButton extends React.Component<LifeCycleRaisedButtonProps, LifeCycleRaisedButtonState> {
- public static defaultProps: Partial<LifeCycleRaisedButtonProps> = {
- isDisabled: false,
- backgroundColor: colors.white,
- labelColor: colors.darkGrey,
- };
- private _buttonTimeoutId: number;
- private _didUnmount: boolean;
- constructor(props: LifeCycleRaisedButtonProps) {
- super(props);
- this.state = {
- buttonState: ButtonState.READY,
- };
- }
- public componentWillUnmount() {
- clearTimeout(this._buttonTimeoutId);
- this._didUnmount = true;
- }
- public render() {
- if (this.props.isHidden) {
- return <span />;
- }
+ public static defaultProps: Partial<LifeCycleRaisedButtonProps> = {
+ isDisabled: false,
+ backgroundColor: colors.white,
+ labelColor: colors.darkGrey,
+ };
+ private _buttonTimeoutId: number;
+ private _didUnmount: boolean;
+ constructor(props: LifeCycleRaisedButtonProps) {
+ super(props);
+ this.state = {
+ buttonState: ButtonState.READY,
+ };
+ }
+ public componentWillUnmount() {
+ clearTimeout(this._buttonTimeoutId);
+ this._didUnmount = true;
+ }
+ public render() {
+ if (this.props.isHidden) {
+ return <span />;
+ }
- let label;
- switch (this.state.buttonState) {
- case ButtonState.READY:
- label = this.props.labelReady;
- break;
- case ButtonState.LOADING:
- label = this.props.labelLoading;
- break;
- case ButtonState.COMPLETE:
- label = this.props.labelComplete;
- break;
- default:
- throw utils.spawnSwitchErr('ButtonState', this.state.buttonState);
- }
- return (
- <RaisedButton
- primary={this.props.isPrimary}
- label={label}
- style={{ width: '100%' }}
- backgroundColor={this.props.backgroundColor}
- labelColor={this.props.labelColor}
- onTouchTap={this.onClickAsync.bind(this)}
- disabled={this.props.isDisabled || this.state.buttonState !== ButtonState.READY}
- />
- );
- }
- public async onClickAsync() {
- this.setState({
- buttonState: ButtonState.LOADING,
- });
- const didSucceed = await this.props.onClickAsyncFn();
- if (this._didUnmount) {
- return; // noop since unmount called before async callback returned.
- }
- if (didSucceed) {
- this.setState({
- buttonState: ButtonState.COMPLETE,
- });
- this._buttonTimeoutId = window.setTimeout(() => {
- this.setState({
- buttonState: ButtonState.READY,
- });
- }, COMPLETE_STATE_SHOW_LENGTH_MS);
- } else {
- this.setState({
- buttonState: ButtonState.READY,
- });
- }
- }
+ let label;
+ switch (this.state.buttonState) {
+ case ButtonState.READY:
+ label = this.props.labelReady;
+ break;
+ case ButtonState.LOADING:
+ label = this.props.labelLoading;
+ break;
+ case ButtonState.COMPLETE:
+ label = this.props.labelComplete;
+ break;
+ default:
+ throw utils.spawnSwitchErr('ButtonState', this.state.buttonState);
+ }
+ return (
+ <RaisedButton
+ primary={this.props.isPrimary}
+ label={label}
+ style={{ width: '100%' }}
+ backgroundColor={this.props.backgroundColor}
+ labelColor={this.props.labelColor}
+ onTouchTap={this.onClickAsync.bind(this)}
+ disabled={this.props.isDisabled || this.state.buttonState !== ButtonState.READY}
+ />
+ );
+ }
+ public async onClickAsync() {
+ this.setState({
+ buttonState: ButtonState.LOADING,
+ });
+ const didSucceed = await this.props.onClickAsyncFn();
+ if (this._didUnmount) {
+ return; // noop since unmount called before async callback returned.
+ }
+ if (didSucceed) {
+ this.setState({
+ buttonState: ButtonState.COMPLETE,
+ });
+ this._buttonTimeoutId = window.setTimeout(() => {
+ this.setState({
+ buttonState: ButtonState.READY,
+ });
+ }, COMPLETE_STATE_SHOW_LENGTH_MS);
+ } else {
+ this.setState({
+ buttonState: ButtonState.READY,
+ });
+ }
+ }
}
diff --git a/packages/website/ts/components/ui/loading.tsx b/packages/website/ts/components/ui/loading.tsx
index e9bfe3316..aa319e9e9 100644
--- a/packages/website/ts/components/ui/loading.tsx
+++ b/packages/website/ts/components/ui/loading.tsx
@@ -10,30 +10,30 @@ interface LoadingProps {}
interface LoadingState {}
export class Loading extends React.Component<LoadingProps, LoadingState> {
- public render() {
- return (
- <div className="pt4 sm-px2 sm-pt2 sm-m1" style={{ height: 500 }}>
- <Paper className="mx-auto" style={{ maxWidth: 400 }}>
- {utils.isUserOnMobile() ? (
- <img className="p1" src="/gifs/0xAnimation.gif" width="96%" />
- ) : (
- <div style={{ pointerEvents: 'none' }}>
- <Video
- autoPlay={true}
- loop={true}
- muted={true}
- controls={[]}
- poster="/images/loading_poster.png"
- >
- <source src="/videos/0xAnimation.mp4" type="video/mp4" />
- </Video>
- </div>
- )}
- <div className="center pt2" style={{ paddingBottom: 11 }}>
- Connecting to the blockchain...
- </div>
- </Paper>
- </div>
- );
- }
+ public render() {
+ return (
+ <div className="pt4 sm-px2 sm-pt2 sm-m1" style={{ height: 500 }}>
+ <Paper className="mx-auto" style={{ maxWidth: 400 }}>
+ {utils.isUserOnMobile() ? (
+ <img className="p1" src="/gifs/0xAnimation.gif" width="96%" />
+ ) : (
+ <div style={{ pointerEvents: 'none' }}>
+ <Video
+ autoPlay={true}
+ loop={true}
+ muted={true}
+ controls={[]}
+ poster="/images/loading_poster.png"
+ >
+ <source src="/videos/0xAnimation.mp4" type="video/mp4" />
+ </Video>
+ </div>
+ )}
+ <div className="center pt2" style={{ paddingBottom: 11 }}>
+ Connecting to the blockchain...
+ </div>
+ </Paper>
+ </div>
+ );
+ }
}
diff --git a/packages/website/ts/components/ui/menu_item.tsx b/packages/website/ts/components/ui/menu_item.tsx
index 956b5eae8..3482f436c 100644
--- a/packages/website/ts/components/ui/menu_item.tsx
+++ b/packages/website/ts/components/ui/menu_item.tsx
@@ -3,49 +3,49 @@ import * as React from 'react';
import { Link } from 'react-router-dom';
interface MenuItemProps {
- to: string;
- style?: React.CSSProperties;
- onClick?: () => void;
- className?: string;
+ to: string;
+ style?: React.CSSProperties;
+ onClick?: () => void;
+ className?: string;
}
interface MenuItemState {
- isHovering: boolean;
+ isHovering: boolean;
}
export class MenuItem extends React.Component<MenuItemProps, MenuItemState> {
- public static defaultProps: Partial<MenuItemProps> = {
- onClick: _.noop,
- className: '',
- };
- public constructor(props: MenuItemProps) {
- super(props);
- this.state = {
- isHovering: false,
- };
- }
- public render() {
- const menuItemStyles = {
- cursor: 'pointer',
- opacity: this.state.isHovering ? 0.5 : 1,
- };
- return (
- <Link to={this.props.to} style={{ textDecoration: 'none', ...this.props.style }}>
- <div
- onClick={this.props.onClick.bind(this)}
- className={`mx-auto ${this.props.className}`}
- style={menuItemStyles}
- onMouseEnter={this._onToggleHover.bind(this, true)}
- onMouseLeave={this._onToggleHover.bind(this, false)}
- >
- {this.props.children}
- </div>
- </Link>
- );
- }
- private _onToggleHover(isHovering: boolean) {
- this.setState({
- isHovering,
- });
- }
+ public static defaultProps: Partial<MenuItemProps> = {
+ onClick: _.noop,
+ className: '',
+ };
+ public constructor(props: MenuItemProps) {
+ super(props);
+ this.state = {
+ isHovering: false,
+ };
+ }
+ public render() {
+ const menuItemStyles = {
+ cursor: 'pointer',
+ opacity: this.state.isHovering ? 0.5 : 1,
+ };
+ return (
+ <Link to={this.props.to} style={{ textDecoration: 'none', ...this.props.style }}>
+ <div
+ onClick={this.props.onClick.bind(this)}
+ className={`mx-auto ${this.props.className}`}
+ style={menuItemStyles}
+ onMouseEnter={this._onToggleHover.bind(this, true)}
+ onMouseLeave={this._onToggleHover.bind(this, false)}
+ >
+ {this.props.children}
+ </div>
+ </Link>
+ );
+ }
+ private _onToggleHover(isHovering: boolean) {
+ this.setState({
+ isHovering,
+ });
+ }
}
diff --git a/packages/website/ts/components/ui/party.tsx b/packages/website/ts/components/ui/party.tsx
index ef3c7b425..ca2577b61 100644
--- a/packages/website/ts/components/ui/party.tsx
+++ b/packages/website/ts/components/ui/party.tsx
@@ -11,129 +11,129 @@ const IMAGE_DIMENSION = 100;
const IDENTICON_DIAMETER = 95;
interface PartyProps {
- label: string;
- address: string;
- networkId: number;
- alternativeImage?: string;
- identiconDiameter?: number;
- identiconStyle?: React.CSSProperties;
- isInTokenRegistry?: boolean;
- hasUniqueNameAndSymbol?: boolean;
+ label: string;
+ address: string;
+ networkId: number;
+ alternativeImage?: string;
+ identiconDiameter?: number;
+ identiconStyle?: React.CSSProperties;
+ isInTokenRegistry?: boolean;
+ hasUniqueNameAndSymbol?: boolean;
}
interface PartyState {}
export class Party extends React.Component<PartyProps, PartyState> {
- public static defaultProps: Partial<PartyProps> = {
- identiconStyle: {},
- identiconDiameter: IDENTICON_DIAMETER,
- };
- public render() {
- const label = this.props.label;
- const address = this.props.address;
- const identiconDiameter = this.props.identiconDiameter;
- const emptyIdenticonStyles = {
- width: identiconDiameter,
- height: identiconDiameter,
- backgroundColor: 'lightgray',
- marginTop: 13,
- marginBottom: 10,
- };
- const tokenImageStyle = {
- width: IMAGE_DIMENSION,
- height: IMAGE_DIMENSION,
- };
- const etherscanLinkIfExists = utils.getEtherScanLinkIfExists(
- this.props.address,
- this.props.networkId,
- EtherscanLinkSuffixes.Address,
- );
- const isRegistered = this.props.isInTokenRegistry;
- const registeredTooltipId = `${this.props.address}-${isRegistered}-registeredTooltip`;
- const uniqueNameAndSymbolTooltipId = `${this.props.address}-${isRegistered}-uniqueTooltip`;
- return (
- <div style={{ overflow: 'hidden' }}>
- <div className="pb1 center">{label}</div>
- {_.isEmpty(address) ? (
- <div className="circle mx-auto" style={emptyIdenticonStyles} />
- ) : (
- <a href={etherscanLinkIfExists} target="_blank">
- {isRegistered && !_.isUndefined(this.props.alternativeImage) ? (
- <img style={tokenImageStyle} src={this.props.alternativeImage} />
- ) : (
- <div className="mx-auto" style={{ height: identiconDiameter, width: identiconDiameter }}>
- <Identicon
- address={this.props.address}
- diameter={identiconDiameter}
- style={this.props.identiconStyle}
- />
- </div>
- )}
- </a>
- )}
- <div className="mx-auto center pt1">
- <div style={{ height: 25 }}>
- <EthereumAddress address={address} networkId={this.props.networkId} />
- </div>
- {!_.isUndefined(this.props.isInTokenRegistry) && (
- <div>
- <div
- data-tip={true}
- data-for={registeredTooltipId}
- className="mx-auto"
- style={{ fontSize: 13, width: 127 }}
- >
- <span
- style={{
- color: isRegistered ? colors.brightGreen : colors.red500,
- }}
- >
- <i
- className={`zmdi ${isRegistered ? 'zmdi-check-circle' : 'zmdi-alert-triangle'}`}
- />
- </span>{' '}
- <span>{isRegistered ? 'Registered' : 'Unregistered'} token</span>
- <ReactTooltip id={registeredTooltipId}>
- {isRegistered ? (
- <div>
- This token address was found in the token registry<br />
- smart contract and is therefore believed to be a<br />
- legitimate token.
- </div>
- ) : (
- <div>
- This token is not included in the token registry<br />
- smart contract. We cannot guarantee the legitimacy<br />
- of this token. Make sure to verify its address on Etherscan.
- </div>
- )}
- </ReactTooltip>
- </div>
- </div>
- )}
- {!_.isUndefined(this.props.hasUniqueNameAndSymbol) &&
- !this.props.hasUniqueNameAndSymbol && (
- <div>
- <div
- data-tip={true}
- data-for={uniqueNameAndSymbolTooltipId}
- className="mx-auto"
- style={{ fontSize: 13, width: 127 }}
- >
- <span style={{ color: colors.red500 }}>
- <i className="zmdi zmdi-alert-octagon" />
- </span>{' '}
- <span>Suspicious token</span>
- <ReactTooltip id={uniqueNameAndSymbolTooltipId}>
- This token shares it's name, symbol or both with<br />
- a token in the 0x Token Registry but it has a different<br />
- smart contract address. This is most likely a scam token!
- </ReactTooltip>
- </div>
- </div>
- )}
- </div>
- </div>
- );
- }
+ public static defaultProps: Partial<PartyProps> = {
+ identiconStyle: {},
+ identiconDiameter: IDENTICON_DIAMETER,
+ };
+ public render() {
+ const label = this.props.label;
+ const address = this.props.address;
+ const identiconDiameter = this.props.identiconDiameter;
+ const emptyIdenticonStyles = {
+ width: identiconDiameter,
+ height: identiconDiameter,
+ backgroundColor: 'lightgray',
+ marginTop: 13,
+ marginBottom: 10,
+ };
+ const tokenImageStyle = {
+ width: IMAGE_DIMENSION,
+ height: IMAGE_DIMENSION,
+ };
+ const etherscanLinkIfExists = utils.getEtherScanLinkIfExists(
+ this.props.address,
+ this.props.networkId,
+ EtherscanLinkSuffixes.Address,
+ );
+ const isRegistered = this.props.isInTokenRegistry;
+ const registeredTooltipId = `${this.props.address}-${isRegistered}-registeredTooltip`;
+ const uniqueNameAndSymbolTooltipId = `${this.props.address}-${isRegistered}-uniqueTooltip`;
+ return (
+ <div style={{ overflow: 'hidden' }}>
+ <div className="pb1 center">{label}</div>
+ {_.isEmpty(address) ? (
+ <div className="circle mx-auto" style={emptyIdenticonStyles} />
+ ) : (
+ <a href={etherscanLinkIfExists} target="_blank">
+ {isRegistered && !_.isUndefined(this.props.alternativeImage) ? (
+ <img style={tokenImageStyle} src={this.props.alternativeImage} />
+ ) : (
+ <div className="mx-auto" style={{ height: identiconDiameter, width: identiconDiameter }}>
+ <Identicon
+ address={this.props.address}
+ diameter={identiconDiameter}
+ style={this.props.identiconStyle}
+ />
+ </div>
+ )}
+ </a>
+ )}
+ <div className="mx-auto center pt1">
+ <div style={{ height: 25 }}>
+ <EthereumAddress address={address} networkId={this.props.networkId} />
+ </div>
+ {!_.isUndefined(this.props.isInTokenRegistry) && (
+ <div>
+ <div
+ data-tip={true}
+ data-for={registeredTooltipId}
+ className="mx-auto"
+ style={{ fontSize: 13, width: 127 }}
+ >
+ <span
+ style={{
+ color: isRegistered ? colors.brightGreen : colors.red500,
+ }}
+ >
+ <i
+ className={`zmdi ${isRegistered ? 'zmdi-check-circle' : 'zmdi-alert-triangle'}`}
+ />
+ </span>{' '}
+ <span>{isRegistered ? 'Registered' : 'Unregistered'} token</span>
+ <ReactTooltip id={registeredTooltipId}>
+ {isRegistered ? (
+ <div>
+ This token address was found in the token registry<br />
+ smart contract and is therefore believed to be a<br />
+ legitimate token.
+ </div>
+ ) : (
+ <div>
+ This token is not included in the token registry<br />
+ smart contract. We cannot guarantee the legitimacy<br />
+ of this token. Make sure to verify its address on Etherscan.
+ </div>
+ )}
+ </ReactTooltip>
+ </div>
+ </div>
+ )}
+ {!_.isUndefined(this.props.hasUniqueNameAndSymbol) &&
+ !this.props.hasUniqueNameAndSymbol && (
+ <div>
+ <div
+ data-tip={true}
+ data-for={uniqueNameAndSymbolTooltipId}
+ className="mx-auto"
+ style={{ fontSize: 13, width: 127 }}
+ >
+ <span style={{ color: colors.red500 }}>
+ <i className="zmdi zmdi-alert-octagon" />
+ </span>{' '}
+ <span>Suspicious token</span>
+ <ReactTooltip id={uniqueNameAndSymbolTooltipId}>
+ This token shares it's name, symbol or both with<br />
+ a token in the 0x Token Registry but it has a different<br />
+ smart contract address. This is most likely a scam token!
+ </ReactTooltip>
+ </div>
+ </div>
+ )}
+ </div>
+ </div>
+ );
+ }
}
diff --git a/packages/website/ts/components/ui/required_label.tsx b/packages/website/ts/components/ui/required_label.tsx
index 638683427..a5e7a22ce 100644
--- a/packages/website/ts/components/ui/required_label.tsx
+++ b/packages/website/ts/components/ui/required_label.tsx
@@ -2,14 +2,14 @@ import * as React from 'react';
import { colors } from 'ts/utils/colors';
export interface RequiredLabelProps {
- label: string | React.ReactNode;
+ label: string | React.ReactNode;
}
export const RequiredLabel = (props: RequiredLabelProps) => {
- return (
- <span>
- {props.label}
- <span style={{ color: colors.red600 }}>*</span>
- </span>
- );
+ return (
+ <span>
+ {props.label}
+ <span style={{ color: colors.red600 }}>*</span>
+ </span>
+ );
};
diff --git a/packages/website/ts/components/ui/simple_loading.tsx b/packages/website/ts/components/ui/simple_loading.tsx
index 9f1fd5a13..81744196d 100644
--- a/packages/website/ts/components/ui/simple_loading.tsx
+++ b/packages/website/ts/components/ui/simple_loading.tsx
@@ -2,16 +2,16 @@ import CircularProgress from 'material-ui/CircularProgress';
import * as React from 'react';
export interface SimpleLoadingProps {
- message: string;
+ message: string;
}
export const SimpleLoading = (props: SimpleLoadingProps) => {
- return (
- <div className="mx-auto pt3" style={{ maxWidth: 400, height: 409 }}>
- <div className="relative" style={{ top: '50%', transform: 'translateY(-50%)', height: 95 }}>
- <CircularProgress />
- <div className="pt3 pb3">{props.message}</div>
- </div>
- </div>
- );
+ return (
+ <div className="mx-auto pt3" style={{ maxWidth: 400, height: 409 }}>
+ <div className="relative" style={{ top: '50%', transform: 'translateY(-50%)', height: 95 }}>
+ <CircularProgress />
+ <div className="pt3 pb3">{props.message}</div>
+ </div>
+ </div>
+ );
};
diff --git a/packages/website/ts/components/ui/swap_icon.tsx b/packages/website/ts/components/ui/swap_icon.tsx
index 99e3450de..c41592287 100644
--- a/packages/website/ts/components/ui/swap_icon.tsx
+++ b/packages/website/ts/components/ui/swap_icon.tsx
@@ -3,40 +3,40 @@ import * as React from 'react';
import { colors } from 'ts/utils/colors';
interface SwapIconProps {
- swapTokensFn: () => void;
+ swapTokensFn: () => void;
}
interface SwapIconState {
- isHovering: boolean;
+ isHovering: boolean;
}
export class SwapIcon extends React.Component<SwapIconProps, SwapIconState> {
- public constructor(props: SwapIconProps) {
- super(props);
- this.state = {
- isHovering: false,
- };
- }
- public render() {
- const swapStyles = {
- color: this.state.isHovering ? colors.amber600 : colors.amber800,
- fontSize: 50,
- };
- return (
- <div
- className="mx-auto pt4"
- style={{ cursor: 'pointer', height: 50, width: 37.5 }}
- onClick={this.props.swapTokensFn}
- onMouseEnter={this._onToggleHover.bind(this, true)}
- onMouseLeave={this._onToggleHover.bind(this, false)}
- >
- <i style={swapStyles} className="zmdi zmdi-swap" />
- </div>
- );
- }
- private _onToggleHover(isHovering: boolean) {
- this.setState({
- isHovering,
- });
- }
+ public constructor(props: SwapIconProps) {
+ super(props);
+ this.state = {
+ isHovering: false,
+ };
+ }
+ public render() {
+ const swapStyles = {
+ color: this.state.isHovering ? colors.amber600 : colors.amber800,
+ fontSize: 50,
+ };
+ return (
+ <div
+ className="mx-auto pt4"
+ style={{ cursor: 'pointer', height: 50, width: 37.5 }}
+ onClick={this.props.swapTokensFn}
+ onMouseEnter={this._onToggleHover.bind(this, true)}
+ onMouseLeave={this._onToggleHover.bind(this, false)}
+ >
+ <i style={swapStyles} className="zmdi zmdi-swap" />
+ </div>
+ );
+ }
+ private _onToggleHover(isHovering: boolean) {
+ this.setState({
+ isHovering,
+ });
+ }
}
diff --git a/packages/website/ts/components/ui/token_icon.tsx b/packages/website/ts/components/ui/token_icon.tsx
index a729821ce..ff57a96de 100644
--- a/packages/website/ts/components/ui/token_icon.tsx
+++ b/packages/website/ts/components/ui/token_icon.tsx
@@ -4,24 +4,24 @@ import { Identicon } from 'ts/components/ui/identicon';
import { Token } from 'ts/types';
interface TokenIconProps {
- token: Token;
- diameter: number;
+ token: Token;
+ diameter: number;
}
interface TokenIconState {}
export class TokenIcon extends React.Component<TokenIconProps, TokenIconState> {
- public render() {
- const token = this.props.token;
- const diameter = this.props.diameter;
- return (
- <div>
- {token.isRegistered && !_.isUndefined(token.iconUrl) ? (
- <img style={{ width: diameter, height: diameter }} src={token.iconUrl} />
- ) : (
- <Identicon address={token.address} diameter={diameter} />
- )}
- </div>
- );
- }
+ public render() {
+ const token = this.props.token;
+ const diameter = this.props.diameter;
+ return (
+ <div>
+ {token.isRegistered && !_.isUndefined(token.iconUrl) ? (
+ <img style={{ width: diameter, height: diameter }} src={token.iconUrl} />
+ ) : (
+ <Identicon address={token.address} diameter={diameter} />
+ )}
+ </div>
+ );
+ }
}
diff --git a/packages/website/ts/components/visual_order.tsx b/packages/website/ts/components/visual_order.tsx
index a9779ac62..092954086 100644
--- a/packages/website/ts/components/visual_order.tsx
+++ b/packages/website/ts/components/visual_order.tsx
@@ -8,69 +8,69 @@ import { utils } from 'ts/utils/utils';
const PRECISION = 5;
interface VisualOrderProps {
- orderTakerAddress: string;
- orderMakerAddress: string;
- makerAssetToken: AssetToken;
- takerAssetToken: AssetToken;
- makerToken: Token;
- takerToken: Token;
- networkId: number;
- tokenByAddress: TokenByAddress;
- isMakerTokenAddressInRegistry: boolean;
- isTakerTokenAddressInRegistry: boolean;
+ orderTakerAddress: string;
+ orderMakerAddress: string;
+ makerAssetToken: AssetToken;
+ takerAssetToken: AssetToken;
+ makerToken: Token;
+ takerToken: Token;
+ networkId: number;
+ tokenByAddress: TokenByAddress;
+ isMakerTokenAddressInRegistry: boolean;
+ isTakerTokenAddressInRegistry: boolean;
}
interface VisualOrderState {}
export class VisualOrder extends React.Component<VisualOrderProps, VisualOrderState> {
- public render() {
- const allTokens = _.values(this.props.tokenByAddress);
- const makerImage = this.props.makerToken.iconUrl;
- const takerImage = this.props.takerToken.iconUrl;
- return (
- <div>
- <div className="clearfix">
- <div className="col col-5 center">
- <Party
- label="Send"
- address={this.props.takerToken.address}
- alternativeImage={takerImage}
- networkId={this.props.networkId}
- isInTokenRegistry={this.props.isTakerTokenAddressInRegistry}
- hasUniqueNameAndSymbol={utils.hasUniqueNameAndSymbol(allTokens, this.props.takerToken)}
- />
- </div>
- <div className="col col-2 center pt1">
- <div className="pb1">
- {this._renderAmount(this.props.takerAssetToken, this.props.takerToken)}
- </div>
- <div className="lg-p2 md-p2 sm-p1">
- <img src="/images/trade_arrows.png" style={{ width: 47 }} />
- </div>
- <div className="pt1">
- {this._renderAmount(this.props.makerAssetToken, this.props.makerToken)}
- </div>
- </div>
- <div className="col col-5 center">
- <Party
- label="Receive"
- address={this.props.makerToken.address}
- alternativeImage={makerImage}
- networkId={this.props.networkId}
- isInTokenRegistry={this.props.isMakerTokenAddressInRegistry}
- hasUniqueNameAndSymbol={utils.hasUniqueNameAndSymbol(allTokens, this.props.makerToken)}
- />
- </div>
- </div>
- </div>
- );
- }
- private _renderAmount(assetToken: AssetToken, token: Token) {
- const unitAmount = ZeroEx.toUnitAmount(assetToken.amount, token.decimals);
- return (
- <div style={{ fontSize: 13 }}>
- {unitAmount.toNumber().toFixed(PRECISION)} {token.symbol}
- </div>
- );
- }
+ public render() {
+ const allTokens = _.values(this.props.tokenByAddress);
+ const makerImage = this.props.makerToken.iconUrl;
+ const takerImage = this.props.takerToken.iconUrl;
+ return (
+ <div>
+ <div className="clearfix">
+ <div className="col col-5 center">
+ <Party
+ label="Send"
+ address={this.props.takerToken.address}
+ alternativeImage={takerImage}
+ networkId={this.props.networkId}
+ isInTokenRegistry={this.props.isTakerTokenAddressInRegistry}
+ hasUniqueNameAndSymbol={utils.hasUniqueNameAndSymbol(allTokens, this.props.takerToken)}
+ />
+ </div>
+ <div className="col col-2 center pt1">
+ <div className="pb1">
+ {this._renderAmount(this.props.takerAssetToken, this.props.takerToken)}
+ </div>
+ <div className="lg-p2 md-p2 sm-p1">
+ <img src="/images/trade_arrows.png" style={{ width: 47 }} />
+ </div>
+ <div className="pt1">
+ {this._renderAmount(this.props.makerAssetToken, this.props.makerToken)}
+ </div>
+ </div>
+ <div className="col col-5 center">
+ <Party
+ label="Receive"
+ address={this.props.makerToken.address}
+ alternativeImage={makerImage}
+ networkId={this.props.networkId}
+ isInTokenRegistry={this.props.isMakerTokenAddressInRegistry}
+ hasUniqueNameAndSymbol={utils.hasUniqueNameAndSymbol(allTokens, this.props.makerToken)}
+ />
+ </div>
+ </div>
+ </div>
+ );
+ }
+ private _renderAmount(assetToken: AssetToken, token: Token) {
+ const unitAmount = ZeroEx.toUnitAmount(assetToken.amount, token.decimals);
+ return (
+ <div style={{ fontSize: 13 }}>
+ {unitAmount.toNumber().toFixed(PRECISION)} {token.symbol}
+ </div>
+ );
+ }
}
diff --git a/packages/website/ts/containers/connect_documentation.tsx b/packages/website/ts/containers/connect_documentation.tsx
index 2f5326d57..3e02a7d05 100644
--- a/packages/website/ts/containers/connect_documentation.tsx
+++ b/packages/website/ts/containers/connect_documentation.tsx
@@ -16,81 +16,81 @@ const InstallationMarkdown = require('md/docs/connect/installation');
/* tslint:enable:no-var-requires */
const connectDocSections = {
- introduction: 'introduction',
- installation: 'installation',
- httpClient: 'httpClient',
- webSocketOrderbookChannel: 'webSocketOrderbookChannel',
- types: constants.TYPES_SECTION_NAME,
+ introduction: 'introduction',
+ installation: 'installation',
+ httpClient: 'httpClient',
+ webSocketOrderbookChannel: 'webSocketOrderbookChannel',
+ types: constants.TYPES_SECTION_NAME,
};
const docsInfoConfig: DocsInfoConfig = {
- displayName: '0x Connect',
- subPackageName: 'connect',
- packageUrl: 'https://github.com/0xProject/0x.js',
- websitePath: WebsitePaths.Connect,
- docsJsonRoot: 'https://s3.amazonaws.com/connect-docs-jsons',
- menu: {
- introduction: [connectDocSections.introduction],
- install: [connectDocSections.installation],
- httpClient: [connectDocSections.httpClient],
- webSocketOrderbookChannel: [connectDocSections.webSocketOrderbookChannel],
- types: [connectDocSections.types],
- },
- sectionNameToMarkdown: {
- [connectDocSections.introduction]: IntroMarkdown,
- [connectDocSections.installation]: InstallationMarkdown,
- },
- // Note: This needs to be kept in sync with the types exported in index.ts. Unfortunately there is
- // currently no way to extract the re-exported types from index.ts via TypeDoc :(
- publicTypes: [
- 'Client',
- 'FeesRequest',
- 'FeesResponse',
- 'OrderbookChannel',
- 'OrderbookChannelHandler',
- 'OrderbookChannelSubscriptionOpts',
- 'OrderbookRequest',
- 'OrderbookResponse',
- 'OrdersRequest',
- 'TokenPairsItem',
- 'TokenPairsRequest',
- 'TokenTradeInfo',
- 'Order',
- 'SignedOrder',
- 'ECSignature',
- ],
- sectionNameToModulePath: {
- [connectDocSections.httpClient]: ['"src/http_client"'],
- [connectDocSections.webSocketOrderbookChannel]: ['"src/ws_orderbook_channel"'],
- [connectDocSections.types]: ['"src/types"'],
- },
- menuSubsectionToVersionWhenIntroduced: {},
- sections: connectDocSections,
- visibleConstructors: [connectDocSections.httpClient, connectDocSections.webSocketOrderbookChannel],
- convertToDocAgnosticFormatFn: typeDocUtils.convertToDocAgnosticFormat.bind(typeDocUtils),
+ displayName: '0x Connect',
+ subPackageName: 'connect',
+ packageUrl: 'https://github.com/0xProject/0x.js',
+ websitePath: WebsitePaths.Connect,
+ docsJsonRoot: 'https://s3.amazonaws.com/connect-docs-jsons',
+ menu: {
+ introduction: [connectDocSections.introduction],
+ install: [connectDocSections.installation],
+ httpClient: [connectDocSections.httpClient],
+ webSocketOrderbookChannel: [connectDocSections.webSocketOrderbookChannel],
+ types: [connectDocSections.types],
+ },
+ sectionNameToMarkdown: {
+ [connectDocSections.introduction]: IntroMarkdown,
+ [connectDocSections.installation]: InstallationMarkdown,
+ },
+ // Note: This needs to be kept in sync with the types exported in index.ts. Unfortunately there is
+ // currently no way to extract the re-exported types from index.ts via TypeDoc :(
+ publicTypes: [
+ 'Client',
+ 'FeesRequest',
+ 'FeesResponse',
+ 'OrderbookChannel',
+ 'OrderbookChannelHandler',
+ 'OrderbookChannelSubscriptionOpts',
+ 'OrderbookRequest',
+ 'OrderbookResponse',
+ 'OrdersRequest',
+ 'TokenPairsItem',
+ 'TokenPairsRequest',
+ 'TokenTradeInfo',
+ 'Order',
+ 'SignedOrder',
+ 'ECSignature',
+ ],
+ sectionNameToModulePath: {
+ [connectDocSections.httpClient]: ['"src/http_client"'],
+ [connectDocSections.webSocketOrderbookChannel]: ['"src/ws_orderbook_channel"'],
+ [connectDocSections.types]: ['"src/types"'],
+ },
+ menuSubsectionToVersionWhenIntroduced: {},
+ sections: connectDocSections,
+ visibleConstructors: [connectDocSections.httpClient, connectDocSections.webSocketOrderbookChannel],
+ convertToDocAgnosticFormatFn: typeDocUtils.convertToDocAgnosticFormat.bind(typeDocUtils),
};
const docsInfo = new DocsInfo(docsInfoConfig);
interface ConnectedState {
- docsVersion: string;
- availableDocVersions: string[];
- docsInfo: DocsInfo;
+ docsVersion: string;
+ availableDocVersions: string[];
+ docsInfo: DocsInfo;
}
interface ConnectedDispatch {
- dispatcher: Dispatcher;
+ dispatcher: Dispatcher;
}
const mapStateToProps = (state: State, ownProps: DocumentationAllProps): ConnectedState => ({
- docsVersion: state.docsVersion,
- availableDocVersions: state.availableDocVersions,
- docsInfo,
+ docsVersion: state.docsVersion,
+ availableDocVersions: state.availableDocVersions,
+ docsInfo,
});
const mapDispatchToProps = (dispatch: Dispatch<State>): ConnectedDispatch => ({
- dispatcher: new Dispatcher(dispatch),
+ dispatcher: new Dispatcher(dispatch),
});
export const Documentation: React.ComponentClass<DocumentationAllProps> = connect(mapStateToProps, mapDispatchToProps)(
- DocumentationComponent,
+ DocumentationComponent,
);
diff --git a/packages/website/ts/containers/generate_order_form.tsx b/packages/website/ts/containers/generate_order_form.tsx
index c2b781557..3fd31087f 100644
--- a/packages/website/ts/containers/generate_order_form.tsx
+++ b/packages/website/ts/containers/generate_order_form.tsx
@@ -7,48 +7,48 @@ import { GenerateOrderForm as GenerateOrderFormComponent } from 'ts/components/g
import { Dispatcher } from 'ts/redux/dispatcher';
import { State } from 'ts/redux/reducer';
import {
- BlockchainErrs,
- HashData,
- SideToAssetToken,
- SignatureData,
- TokenByAddress,
- TokenStateByAddress,
+ BlockchainErrs,
+ HashData,
+ SideToAssetToken,
+ SignatureData,
+ TokenByAddress,
+ TokenStateByAddress,
} from 'ts/types';
interface GenerateOrderFormProps {
- blockchain: Blockchain;
- hashData: HashData;
- dispatcher: Dispatcher;
+ blockchain: Blockchain;
+ hashData: HashData;
+ dispatcher: Dispatcher;
}
interface ConnectedState {
- blockchainErr: BlockchainErrs;
- blockchainIsLoaded: boolean;
- orderExpiryTimestamp: BigNumber;
- orderSignatureData: SignatureData;
- userAddress: string;
- orderTakerAddress: string;
- orderSalt: BigNumber;
- networkId: number;
- sideToAssetToken: SideToAssetToken;
- tokenByAddress: TokenByAddress;
- tokenStateByAddress: TokenStateByAddress;
+ blockchainErr: BlockchainErrs;
+ blockchainIsLoaded: boolean;
+ orderExpiryTimestamp: BigNumber;
+ orderSignatureData: SignatureData;
+ userAddress: string;
+ orderTakerAddress: string;
+ orderSalt: BigNumber;
+ networkId: number;
+ sideToAssetToken: SideToAssetToken;
+ tokenByAddress: TokenByAddress;
+ tokenStateByAddress: TokenStateByAddress;
}
const mapStateToProps = (state: State, ownProps: GenerateOrderFormProps): ConnectedState => ({
- blockchainErr: state.blockchainErr,
- blockchainIsLoaded: state.blockchainIsLoaded,
- orderExpiryTimestamp: state.orderExpiryTimestamp,
- orderSignatureData: state.orderSignatureData,
- orderTakerAddress: state.orderTakerAddress,
- orderSalt: state.orderSalt,
- networkId: state.networkId,
- sideToAssetToken: state.sideToAssetToken,
- tokenByAddress: state.tokenByAddress,
- tokenStateByAddress: state.tokenStateByAddress,
- userAddress: state.userAddress,
+ blockchainErr: state.blockchainErr,
+ blockchainIsLoaded: state.blockchainIsLoaded,
+ orderExpiryTimestamp: state.orderExpiryTimestamp,
+ orderSignatureData: state.orderSignatureData,
+ orderTakerAddress: state.orderTakerAddress,
+ orderSalt: state.orderSalt,
+ networkId: state.networkId,
+ sideToAssetToken: state.sideToAssetToken,
+ tokenByAddress: state.tokenByAddress,
+ tokenStateByAddress: state.tokenStateByAddress,
+ userAddress: state.userAddress,
});
export const GenerateOrderForm: React.ComponentClass<GenerateOrderFormProps> = connect(mapStateToProps)(
- GenerateOrderFormComponent,
+ GenerateOrderFormComponent,
);
diff --git a/packages/website/ts/containers/portal.tsx b/packages/website/ts/containers/portal.tsx
index bc7aa213c..f0247935b 100644
--- a/packages/website/ts/containers/portal.tsx
+++ b/packages/website/ts/containers/portal.tsx
@@ -10,72 +10,72 @@ import { BlockchainErrs, HashData, Order, ScreenWidths, Side, TokenByAddress, To
import { constants } from 'ts/utils/constants';
interface ConnectedState {
- blockchainErr: BlockchainErrs;
- blockchainIsLoaded: boolean;
- hashData: HashData;
- networkId: number;
- nodeVersion: string;
- orderFillAmount: BigNumber;
- tokenByAddress: TokenByAddress;
- tokenStateByAddress: TokenStateByAddress;
- userEtherBalance: BigNumber;
- screenWidth: ScreenWidths;
- shouldBlockchainErrDialogBeOpen: boolean;
- userAddress: string;
- userSuppliedOrderCache: Order;
- flashMessage?: string | React.ReactNode;
+ blockchainErr: BlockchainErrs;
+ blockchainIsLoaded: boolean;
+ hashData: HashData;
+ networkId: number;
+ nodeVersion: string;
+ orderFillAmount: BigNumber;
+ tokenByAddress: TokenByAddress;
+ tokenStateByAddress: TokenStateByAddress;
+ userEtherBalance: BigNumber;
+ screenWidth: ScreenWidths;
+ shouldBlockchainErrDialogBeOpen: boolean;
+ userAddress: string;
+ userSuppliedOrderCache: Order;
+ flashMessage?: string | React.ReactNode;
}
interface ConnectedDispatch {
- dispatcher: Dispatcher;
+ dispatcher: Dispatcher;
}
const mapStateToProps = (state: State, ownProps: PortalComponentAllProps): ConnectedState => {
- const receiveAssetToken = state.sideToAssetToken[Side.Receive];
- const depositAssetToken = state.sideToAssetToken[Side.Deposit];
- const receiveAddress = !_.isUndefined(receiveAssetToken.address)
- ? receiveAssetToken.address
- : constants.NULL_ADDRESS;
- const depositAddress = !_.isUndefined(depositAssetToken.address)
- ? depositAssetToken.address
- : constants.NULL_ADDRESS;
- const receiveAmount = !_.isUndefined(receiveAssetToken.amount) ? receiveAssetToken.amount : new BigNumber(0);
- const depositAmount = !_.isUndefined(depositAssetToken.amount) ? depositAssetToken.amount : new BigNumber(0);
- const hashData = {
- depositAmount,
- depositTokenContractAddr: depositAddress,
- feeRecipientAddress: constants.NULL_ADDRESS,
- makerFee: constants.MAKER_FEE,
- orderExpiryTimestamp: state.orderExpiryTimestamp,
- orderMakerAddress: state.userAddress,
- orderTakerAddress: state.orderTakerAddress !== '' ? state.orderTakerAddress : constants.NULL_ADDRESS,
- receiveAmount,
- receiveTokenContractAddr: receiveAddress,
- takerFee: constants.TAKER_FEE,
- orderSalt: state.orderSalt,
- };
- return {
- blockchainErr: state.blockchainErr,
- blockchainIsLoaded: state.blockchainIsLoaded,
- networkId: state.networkId,
- nodeVersion: state.nodeVersion,
- orderFillAmount: state.orderFillAmount,
- hashData,
- screenWidth: state.screenWidth,
- shouldBlockchainErrDialogBeOpen: state.shouldBlockchainErrDialogBeOpen,
- tokenByAddress: state.tokenByAddress,
- tokenStateByAddress: state.tokenStateByAddress,
- userAddress: state.userAddress,
- userEtherBalance: state.userEtherBalance,
- userSuppliedOrderCache: state.userSuppliedOrderCache,
- flashMessage: state.flashMessage,
- };
+ const receiveAssetToken = state.sideToAssetToken[Side.Receive];
+ const depositAssetToken = state.sideToAssetToken[Side.Deposit];
+ const receiveAddress = !_.isUndefined(receiveAssetToken.address)
+ ? receiveAssetToken.address
+ : constants.NULL_ADDRESS;
+ const depositAddress = !_.isUndefined(depositAssetToken.address)
+ ? depositAssetToken.address
+ : constants.NULL_ADDRESS;
+ const receiveAmount = !_.isUndefined(receiveAssetToken.amount) ? receiveAssetToken.amount : new BigNumber(0);
+ const depositAmount = !_.isUndefined(depositAssetToken.amount) ? depositAssetToken.amount : new BigNumber(0);
+ const hashData = {
+ depositAmount,
+ depositTokenContractAddr: depositAddress,
+ feeRecipientAddress: constants.NULL_ADDRESS,
+ makerFee: constants.MAKER_FEE,
+ orderExpiryTimestamp: state.orderExpiryTimestamp,
+ orderMakerAddress: state.userAddress,
+ orderTakerAddress: state.orderTakerAddress !== '' ? state.orderTakerAddress : constants.NULL_ADDRESS,
+ receiveAmount,
+ receiveTokenContractAddr: receiveAddress,
+ takerFee: constants.TAKER_FEE,
+ orderSalt: state.orderSalt,
+ };
+ return {
+ blockchainErr: state.blockchainErr,
+ blockchainIsLoaded: state.blockchainIsLoaded,
+ networkId: state.networkId,
+ nodeVersion: state.nodeVersion,
+ orderFillAmount: state.orderFillAmount,
+ hashData,
+ screenWidth: state.screenWidth,
+ shouldBlockchainErrDialogBeOpen: state.shouldBlockchainErrDialogBeOpen,
+ tokenByAddress: state.tokenByAddress,
+ tokenStateByAddress: state.tokenStateByAddress,
+ userAddress: state.userAddress,
+ userEtherBalance: state.userEtherBalance,
+ userSuppliedOrderCache: state.userSuppliedOrderCache,
+ flashMessage: state.flashMessage,
+ };
};
const mapDispatchToProps = (dispatch: Dispatch<State>): ConnectedDispatch => ({
- dispatcher: new Dispatcher(dispatch),
+ dispatcher: new Dispatcher(dispatch),
});
export const Portal: React.ComponentClass<PortalComponentAllProps> = connect(mapStateToProps, mapDispatchToProps)(
- PortalComponent,
+ PortalComponent,
);
diff --git a/packages/website/ts/containers/smart_contracts_documentation.tsx b/packages/website/ts/containers/smart_contracts_documentation.tsx
index addfd1af9..8be33b546 100644
--- a/packages/website/ts/containers/smart_contracts_documentation.tsx
+++ b/packages/website/ts/containers/smart_contracts_documentation.tsx
@@ -14,49 +14,49 @@ const IntroMarkdown = require('md/docs/smart_contracts/introduction');
/* tslint:enable:no-var-requires */
const docsInfoConfig: DocsInfoConfig = {
- displayName: '0x Smart Contracts',
- packageUrl: 'https://github.com/0xProject/contracts',
- websitePath: WebsitePaths.SmartContracts,
- docsJsonRoot: 'https://s3.amazonaws.com/smart-contracts-docs-json',
- menu: {
- introduction: [Sections.Introduction],
- contracts: [Sections.Exchange, Sections.TokenRegistry, Sections.ZRXToken, Sections.TokenTransferProxy],
- },
- sectionNameToMarkdown: {
- [Sections.Introduction]: IntroMarkdown,
- },
- sections: {
- Introduction: Sections.Introduction,
- Exchange: Sections.Exchange,
- TokenTransferProxy: Sections.TokenTransferProxy,
- TokenRegistry: Sections.TokenRegistry,
- ZRXToken: Sections.ZRXToken,
- },
- visibleConstructors: [Sections.Exchange, Sections.TokenRegistry, Sections.ZRXToken, Sections.TokenTransferProxy],
- convertToDocAgnosticFormatFn: doxityUtils.convertToDocAgnosticFormat.bind(doxityUtils),
+ displayName: '0x Smart Contracts',
+ packageUrl: 'https://github.com/0xProject/contracts',
+ websitePath: WebsitePaths.SmartContracts,
+ docsJsonRoot: 'https://s3.amazonaws.com/smart-contracts-docs-json',
+ menu: {
+ introduction: [Sections.Introduction],
+ contracts: [Sections.Exchange, Sections.TokenRegistry, Sections.ZRXToken, Sections.TokenTransferProxy],
+ },
+ sectionNameToMarkdown: {
+ [Sections.Introduction]: IntroMarkdown,
+ },
+ sections: {
+ Introduction: Sections.Introduction,
+ Exchange: Sections.Exchange,
+ TokenTransferProxy: Sections.TokenTransferProxy,
+ TokenRegistry: Sections.TokenRegistry,
+ ZRXToken: Sections.ZRXToken,
+ },
+ visibleConstructors: [Sections.Exchange, Sections.TokenRegistry, Sections.ZRXToken, Sections.TokenTransferProxy],
+ convertToDocAgnosticFormatFn: doxityUtils.convertToDocAgnosticFormat.bind(doxityUtils),
};
const docsInfo = new DocsInfo(docsInfoConfig);
interface ConnectedState {
- docsVersion: string;
- availableDocVersions: string[];
+ docsVersion: string;
+ availableDocVersions: string[];
}
interface ConnectedDispatch {
- dispatcher: Dispatcher;
- docsInfo: DocsInfo;
+ dispatcher: Dispatcher;
+ docsInfo: DocsInfo;
}
const mapStateToProps = (state: State, ownProps: DocumentationAllProps): ConnectedState => ({
- docsVersion: state.docsVersion,
- availableDocVersions: state.availableDocVersions,
+ docsVersion: state.docsVersion,
+ availableDocVersions: state.availableDocVersions,
});
const mapDispatchToProps = (dispatch: Dispatch<State>): ConnectedDispatch => ({
- dispatcher: new Dispatcher(dispatch),
- docsInfo,
+ dispatcher: new Dispatcher(dispatch),
+ docsInfo,
});
export const Documentation: React.ComponentClass<DocumentationAllProps> = connect(mapStateToProps, mapDispatchToProps)(
- DocumentationComponent,
+ DocumentationComponent,
);
diff --git a/packages/website/ts/containers/zero_ex_js_documentation.tsx b/packages/website/ts/containers/zero_ex_js_documentation.tsx
index d416cbc12..8ae6a7b73 100644
--- a/packages/website/ts/containers/zero_ex_js_documentation.tsx
+++ b/packages/website/ts/containers/zero_ex_js_documentation.tsx
@@ -19,152 +19,152 @@ const versioningMarkdown = require('md/docs/0xjs/versioning');
/* tslint:enable:no-var-requires */
const zeroExJsDocSections = {
- introduction: 'introduction',
- installation: 'installation',
- testrpc: 'testrpc',
- async: 'async',
- errors: 'errors',
- versioning: 'versioning',
- zeroEx: 'zeroEx',
- exchange: 'exchange',
- token: 'token',
- tokenRegistry: 'tokenRegistry',
- etherToken: 'etherToken',
- proxy: 'proxy',
- orderWatcher: 'orderWatcher',
- types: constants.TYPES_SECTION_NAME,
+ introduction: 'introduction',
+ installation: 'installation',
+ testrpc: 'testrpc',
+ async: 'async',
+ errors: 'errors',
+ versioning: 'versioning',
+ zeroEx: 'zeroEx',
+ exchange: 'exchange',
+ token: 'token',
+ tokenRegistry: 'tokenRegistry',
+ etherToken: 'etherToken',
+ proxy: 'proxy',
+ orderWatcher: 'orderWatcher',
+ types: constants.TYPES_SECTION_NAME,
};
const docsInfoConfig: DocsInfoConfig = {
- displayName: '0x.js',
- packageUrl: 'https://github.com/0xProject/0x.js',
- subPackageName: '0x.js',
- websitePath: WebsitePaths.ZeroExJs,
- docsJsonRoot: 'https://s3.amazonaws.com/0xjs-docs-jsons',
- menu: {
- introduction: [zeroExJsDocSections.introduction],
- install: [zeroExJsDocSections.installation],
- topics: [zeroExJsDocSections.async, zeroExJsDocSections.errors, zeroExJsDocSections.versioning],
- zeroEx: [zeroExJsDocSections.zeroEx],
- contracts: [
- zeroExJsDocSections.exchange,
- zeroExJsDocSections.token,
- zeroExJsDocSections.tokenRegistry,
- zeroExJsDocSections.etherToken,
- zeroExJsDocSections.proxy,
- ],
- orderWatcher: [zeroExJsDocSections.orderWatcher],
- types: [zeroExJsDocSections.types],
- },
- sectionNameToMarkdown: {
- [zeroExJsDocSections.introduction]: IntroMarkdown,
- [zeroExJsDocSections.installation]: InstallationMarkdown,
- [zeroExJsDocSections.async]: AsyncMarkdown,
- [zeroExJsDocSections.errors]: ErrorsMarkdown,
- [zeroExJsDocSections.versioning]: versioningMarkdown,
- },
- // Note: This needs to be kept in sync with the types exported in index.ts. Unfortunately there is
- // currently no way to extract the re-exported types from index.ts via TypeDoc :(
- publicTypes: [
- 'Order',
- 'SignedOrder',
- 'ECSignature',
- 'ZeroExError',
- 'EventCallback',
- 'EventCallbackAsync',
- 'EventCallbackSync',
- 'ExchangeContractErrs',
- 'ContractEvent',
- 'Token',
- 'ExchangeEvents',
- 'IndexedFilterValues',
- 'SubscriptionOpts',
- 'BlockRange',
- 'BlockParam',
- 'OrderFillOrKillRequest',
- 'OrderCancellationRequest',
- 'OrderFillRequest',
- 'ContractEventEmitter',
- 'Web3Provider',
- 'ContractEventArgs',
- 'LogCancelArgs',
- 'LogFillArgs',
- 'LogErrorContractEventArgs',
- 'LogFillContractEventArgs',
- 'LogCancelContractEventArgs',
- 'EtherTokenContractEventArgs',
- 'WithdrawalContractEventArgs',
- 'DepositContractEventArgs',
- 'TokenEvents',
- 'ExchangeContractEventArgs',
- 'TransferContractEventArgs',
- 'ApprovalContractEventArgs',
- 'TokenContractEventArgs',
- 'ZeroExConfig',
- 'TransactionReceiptWithDecodedLogs',
- 'LogWithDecodedArgs',
- 'EtherTokenEvents',
- 'BlockParamLiteral',
- 'DecodedLogArgs',
- 'MethodOpts',
- 'ValidateOrderFillableOpts',
- 'OrderTransactionOpts',
- 'TransactionOpts',
- 'ContractEventArg',
- 'LogEvent',
- 'LogEntry',
- 'DecodedLogEvent',
- 'EventWatcherCallback',
- 'OnOrderStateChangeCallback',
- 'OrderStateValid',
- 'OrderStateInvalid',
- 'OrderState',
- 'FilterObject',
- ],
- sectionNameToModulePath: {
- [zeroExJsDocSections.zeroEx]: ['"src/0x"'],
- [zeroExJsDocSections.exchange]: ['"src/contract_wrappers/exchange_wrapper"'],
- [zeroExJsDocSections.tokenRegistry]: ['"src/contract_wrappers/token_registry_wrapper"'],
- [zeroExJsDocSections.token]: ['"src/contract_wrappers/token_wrapper"'],
- [zeroExJsDocSections.etherToken]: ['"src/contract_wrappers/ether_token_wrapper"'],
- [zeroExJsDocSections.proxy]: [
- '"src/contract_wrappers/proxy_wrapper"',
- '"src/contract_wrappers/token_transfer_proxy_wrapper"',
- ],
- [zeroExJsDocSections.orderWatcher]: ['"src/order_watcher/order_state_watcher"'],
- [zeroExJsDocSections.types]: ['"src/types"'],
- },
- menuSubsectionToVersionWhenIntroduced: {
- [zeroExJsDocSections.etherToken]: '0.7.1',
- [zeroExJsDocSections.proxy]: '0.8.0',
- [zeroExJsDocSections.orderWatcher]: '0.27.1',
- },
- sections: zeroExJsDocSections,
- visibleConstructors: [zeroExJsDocSections.zeroEx],
- convertToDocAgnosticFormatFn: typeDocUtils.convertToDocAgnosticFormat.bind(typeDocUtils),
+ displayName: '0x.js',
+ packageUrl: 'https://github.com/0xProject/0x.js',
+ subPackageName: '0x.js',
+ websitePath: WebsitePaths.ZeroExJs,
+ docsJsonRoot: 'https://s3.amazonaws.com/0xjs-docs-jsons',
+ menu: {
+ introduction: [zeroExJsDocSections.introduction],
+ install: [zeroExJsDocSections.installation],
+ topics: [zeroExJsDocSections.async, zeroExJsDocSections.errors, zeroExJsDocSections.versioning],
+ zeroEx: [zeroExJsDocSections.zeroEx],
+ contracts: [
+ zeroExJsDocSections.exchange,
+ zeroExJsDocSections.token,
+ zeroExJsDocSections.tokenRegistry,
+ zeroExJsDocSections.etherToken,
+ zeroExJsDocSections.proxy,
+ ],
+ orderWatcher: [zeroExJsDocSections.orderWatcher],
+ types: [zeroExJsDocSections.types],
+ },
+ sectionNameToMarkdown: {
+ [zeroExJsDocSections.introduction]: IntroMarkdown,
+ [zeroExJsDocSections.installation]: InstallationMarkdown,
+ [zeroExJsDocSections.async]: AsyncMarkdown,
+ [zeroExJsDocSections.errors]: ErrorsMarkdown,
+ [zeroExJsDocSections.versioning]: versioningMarkdown,
+ },
+ // Note: This needs to be kept in sync with the types exported in index.ts. Unfortunately there is
+ // currently no way to extract the re-exported types from index.ts via TypeDoc :(
+ publicTypes: [
+ 'Order',
+ 'SignedOrder',
+ 'ECSignature',
+ 'ZeroExError',
+ 'EventCallback',
+ 'EventCallbackAsync',
+ 'EventCallbackSync',
+ 'ExchangeContractErrs',
+ 'ContractEvent',
+ 'Token',
+ 'ExchangeEvents',
+ 'IndexedFilterValues',
+ 'SubscriptionOpts',
+ 'BlockRange',
+ 'BlockParam',
+ 'OrderFillOrKillRequest',
+ 'OrderCancellationRequest',
+ 'OrderFillRequest',
+ 'ContractEventEmitter',
+ 'Web3Provider',
+ 'ContractEventArgs',
+ 'LogCancelArgs',
+ 'LogFillArgs',
+ 'LogErrorContractEventArgs',
+ 'LogFillContractEventArgs',
+ 'LogCancelContractEventArgs',
+ 'EtherTokenContractEventArgs',
+ 'WithdrawalContractEventArgs',
+ 'DepositContractEventArgs',
+ 'TokenEvents',
+ 'ExchangeContractEventArgs',
+ 'TransferContractEventArgs',
+ 'ApprovalContractEventArgs',
+ 'TokenContractEventArgs',
+ 'ZeroExConfig',
+ 'TransactionReceiptWithDecodedLogs',
+ 'LogWithDecodedArgs',
+ 'EtherTokenEvents',
+ 'BlockParamLiteral',
+ 'DecodedLogArgs',
+ 'MethodOpts',
+ 'ValidateOrderFillableOpts',
+ 'OrderTransactionOpts',
+ 'TransactionOpts',
+ 'ContractEventArg',
+ 'LogEvent',
+ 'LogEntry',
+ 'DecodedLogEvent',
+ 'EventWatcherCallback',
+ 'OnOrderStateChangeCallback',
+ 'OrderStateValid',
+ 'OrderStateInvalid',
+ 'OrderState',
+ 'FilterObject',
+ ],
+ sectionNameToModulePath: {
+ [zeroExJsDocSections.zeroEx]: ['"src/0x"'],
+ [zeroExJsDocSections.exchange]: ['"src/contract_wrappers/exchange_wrapper"'],
+ [zeroExJsDocSections.tokenRegistry]: ['"src/contract_wrappers/token_registry_wrapper"'],
+ [zeroExJsDocSections.token]: ['"src/contract_wrappers/token_wrapper"'],
+ [zeroExJsDocSections.etherToken]: ['"src/contract_wrappers/ether_token_wrapper"'],
+ [zeroExJsDocSections.proxy]: [
+ '"src/contract_wrappers/proxy_wrapper"',
+ '"src/contract_wrappers/token_transfer_proxy_wrapper"',
+ ],
+ [zeroExJsDocSections.orderWatcher]: ['"src/order_watcher/order_state_watcher"'],
+ [zeroExJsDocSections.types]: ['"src/types"'],
+ },
+ menuSubsectionToVersionWhenIntroduced: {
+ [zeroExJsDocSections.etherToken]: '0.7.1',
+ [zeroExJsDocSections.proxy]: '0.8.0',
+ [zeroExJsDocSections.orderWatcher]: '0.27.1',
+ },
+ sections: zeroExJsDocSections,
+ visibleConstructors: [zeroExJsDocSections.zeroEx],
+ convertToDocAgnosticFormatFn: typeDocUtils.convertToDocAgnosticFormat.bind(typeDocUtils),
};
const docsInfo = new DocsInfo(docsInfoConfig);
interface ConnectedState {
- docsVersion: string;
- availableDocVersions: string[];
- docsInfo: DocsInfo;
+ docsVersion: string;
+ availableDocVersions: string[];
+ docsInfo: DocsInfo;
}
interface ConnectedDispatch {
- dispatcher: Dispatcher;
+ dispatcher: Dispatcher;
}
const mapStateToProps = (state: State, ownProps: DocumentationAllProps): ConnectedState => ({
- docsVersion: state.docsVersion,
- availableDocVersions: state.availableDocVersions,
- docsInfo,
+ docsVersion: state.docsVersion,
+ availableDocVersions: state.availableDocVersions,
+ docsInfo,
});
const mapDispatchToProps = (dispatch: Dispatch<State>): ConnectedDispatch => ({
- dispatcher: new Dispatcher(dispatch),
+ dispatcher: new Dispatcher(dispatch),
});
export const Documentation: React.ComponentClass<DocumentationAllProps> = connect(mapStateToProps, mapDispatchToProps)(
- DocumentationComponent,
+ DocumentationComponent,
);
diff --git a/packages/website/ts/globals.d.ts b/packages/website/ts/globals.d.ts
index 736b23d9d..383e5cbe0 100644
--- a/packages/website/ts/globals.d.ts
+++ b/packages/website/ts/globals.d.ts
@@ -14,10 +14,10 @@ declare module 'ledgerco';
declare module 'ethereumjs-tx';
declare module '*.json' {
- const json: any;
- /* tslint:disable */
- export default json;
- /* tslint:enable */
+ const json: any;
+ /* tslint:disable */
+ export default json;
+ /* tslint:enable */
}
// tslint:disable:max-classes-per-file
@@ -25,137 +25,137 @@ declare module '*.json' {
// find-version declarations
declare function findVersions(version: string): string[];
declare module 'find-versions' {
- export = findVersions;
+ export = findVersions;
}
// compare-version declarations
declare function compareVersions(firstVersion: string, secondVersion: string): number;
declare module 'compare-versions' {
- export = compareVersions;
+ export = compareVersions;
}
// semver-sort declarations
declare module 'semver-sort' {
- const desc: (versions: string[]) => string[];
+ const desc: (versions: string[]) => string[];
}
// xml-js declarations
declare interface XML2JSONOpts {
- compact?: boolean;
- spaces?: number;
+ compact?: boolean;
+ spaces?: number;
}
declare module 'xml-js' {
- const xml2json: (xml: string, opts: XML2JSONOpts) => string;
+ const xml2json: (xml: string, opts: XML2JSONOpts) => string;
}
// This will be defined by default in TS 2.4
// Source: https://github.com/Microsoft/TypeScript/issues/12364
interface System {
- import<T>(module: string): Promise<T>;
+ import<T>(module: string): Promise<T>;
}
declare var System: System;
// jsonschema declarations
// Source: https://github.com/tdegrunt/jsonschema/blob/master/lib/index.d.ts
declare interface Schema {
- id?: string;
- $schema?: string;
- title?: string;
- description?: string;
- multipleOf?: number;
- maximum?: number;
- exclusiveMaximum?: boolean;
- minimum?: number;
- exclusiveMinimum?: boolean;
- maxLength?: number;
- minLength?: number;
- pattern?: string;
- additionalItems?: boolean | Schema;
- items?: Schema | Schema[];
- maxItems?: number;
- minItems?: number;
- uniqueItems?: boolean;
- maxProperties?: number;
- minProperties?: number;
- required?: string[];
- additionalProperties?: boolean | Schema;
- definitions?: {
- [name: string]: Schema;
- };
- properties?: {
- [name: string]: Schema;
- };
- patternProperties?: {
- [name: string]: Schema;
- };
- dependencies?: {
- [name: string]: Schema | string[];
- };
- enum?: any[];
- type?: string | string[];
- allOf?: Schema[];
- anyOf?: Schema[];
- oneOf?: Schema[];
- not?: Schema;
- // This is the only property that's not defined in https://github.com/tdegrunt/jsonschema/blob/master/lib/index.d.ts
- // There is an open issue for that: https://github.com/tdegrunt/jsonschema/issues/194
- // There is also an opened PR: https://github.com/tdegrunt/jsonschema/pull/218/files
- // As soon as it gets merged we should be good to use types from 'jsonschema' package
- $ref?: string;
+ id?: string;
+ $schema?: string;
+ title?: string;
+ description?: string;
+ multipleOf?: number;
+ maximum?: number;
+ exclusiveMaximum?: boolean;
+ minimum?: number;
+ exclusiveMinimum?: boolean;
+ maxLength?: number;
+ minLength?: number;
+ pattern?: string;
+ additionalItems?: boolean | Schema;
+ items?: Schema | Schema[];
+ maxItems?: number;
+ minItems?: number;
+ uniqueItems?: boolean;
+ maxProperties?: number;
+ minProperties?: number;
+ required?: string[];
+ additionalProperties?: boolean | Schema;
+ definitions?: {
+ [name: string]: Schema;
+ };
+ properties?: {
+ [name: string]: Schema;
+ };
+ patternProperties?: {
+ [name: string]: Schema;
+ };
+ dependencies?: {
+ [name: string]: Schema | string[];
+ };
+ enum?: any[];
+ type?: string | string[];
+ allOf?: Schema[];
+ anyOf?: Schema[];
+ oneOf?: Schema[];
+ not?: Schema;
+ // This is the only property that's not defined in https://github.com/tdegrunt/jsonschema/blob/master/lib/index.d.ts
+ // There is an open issue for that: https://github.com/tdegrunt/jsonschema/issues/194
+ // There is also an opened PR: https://github.com/tdegrunt/jsonschema/pull/218/files
+ // As soon as it gets merged we should be good to use types from 'jsonschema' package
+ $ref?: string;
}
// blockies declarations
declare interface BlockiesIcon {
- toDataURL(): string;
+ toDataURL(): string;
}
declare interface BlockiesConfig {
- seed: string;
+ seed: string;
}
declare function blockies(config: BlockiesConfig): BlockiesIcon;
declare module 'blockies' {
- export = blockies;
+ export = blockies;
}
// is-mobile declarations
declare function isMobile(): boolean;
declare module 'is-mobile' {
- export = isMobile;
+ export = isMobile;
}
// web3-provider-engine declarations
declare class Subprovider {}
declare module 'web3-provider-engine/subproviders/subprovider' {
- export = Subprovider;
+ export = Subprovider;
}
declare module 'web3-provider-engine/subproviders/rpc' {
- import * as Web3 from 'web3';
- class RpcSubprovider {
- constructor(options: { rpcUrl: string });
- public handleRequest(
- payload: Web3.JSONRPCRequestPayload,
- next: () => void,
- end: (err: Error | null, data?: any) => void,
- ): void;
- }
- export = RpcSubprovider;
+ import * as Web3 from 'web3';
+ class RpcSubprovider {
+ constructor(options: { rpcUrl: string });
+ public handleRequest(
+ payload: Web3.JSONRPCRequestPayload,
+ next: () => void,
+ end: (err: Error | null, data?: any) => void,
+ ): void;
+ }
+ export = RpcSubprovider;
}
declare module 'web3-provider-engine' {
- class Web3ProviderEngine {
- public on(event: string, handler: () => void): void;
- public send(payload: any): void;
- public sendAsync(payload: any, callback: (error: any, response: any) => void): void;
- public addProvider(provider: any): void;
- public start(): void;
- public stop(): void;
- }
- export = Web3ProviderEngine;
+ class Web3ProviderEngine {
+ public on(event: string, handler: () => void): void;
+ public send(payload: any): void;
+ public sendAsync(payload: any, callback: (error: any, response: any) => void): void;
+ public addProvider(provider: any): void;
+ public start(): void;
+ public stop(): void;
+ }
+ export = Web3ProviderEngine;
}
declare interface Artifact {
- abi: any;
- networks: {
- [networkId: number]: {
- address: string;
- };
- };
+ abi: any;
+ networks: {
+ [networkId: number]: {
+ address: string;
+ };
+ };
}
diff --git a/packages/website/ts/index.tsx b/packages/website/ts/index.tsx
index df45c80bd..ffb551561 100644
--- a/packages/website/ts/index.tsx
+++ b/packages/website/ts/index.tsx
@@ -32,44 +32,44 @@ import 'less/all.less';
// At the same time webpack statically parses for System.import() to determine bundle chunk split points
// so each lazy import needs it's own `System.import()` declaration.
const LazyPortal = createLazyComponent('Portal', async () =>
- System.import<any>(/* webpackChunkName: "portal" */ 'ts/containers/portal'),
+ System.import<any>(/* webpackChunkName: "portal" */ 'ts/containers/portal'),
);
const LazyZeroExJSDocumentation = createLazyComponent('Documentation', async () =>
- System.import<any>(/* webpackChunkName: "zeroExDocs" */ 'ts/containers/zero_ex_js_documentation'),
+ System.import<any>(/* webpackChunkName: "zeroExDocs" */ 'ts/containers/zero_ex_js_documentation'),
);
const LazySmartContractsDocumentation = createLazyComponent('Documentation', async () =>
- System.import<any>(/* webpackChunkName: "smartContractDocs" */ 'ts/containers/smart_contracts_documentation'),
+ System.import<any>(/* webpackChunkName: "smartContractDocs" */ 'ts/containers/smart_contracts_documentation'),
);
const LazyConnectDocumentation = createLazyComponent('Documentation', async () =>
- System.import<any>(/* webpackChunkName: "connectDocs" */ 'ts/containers/connect_documentation'),
+ System.import<any>(/* webpackChunkName: "connectDocs" */ 'ts/containers/connect_documentation'),
);
const store: ReduxStore<State> = createStore(reducer);
render(
- <Router>
- <div>
- <MuiThemeProvider muiTheme={muiTheme}>
- <Provider store={store}>
- <div>
- <Switch>
- <Route exact={true} path="/" component={Landing as any} />
- <Redirect from="/otc" to={`${WebsitePaths.Portal}`} />
- <Route path={`${WebsitePaths.Portal}`} component={LazyPortal} />
- <Route path={`${WebsitePaths.FAQ}`} component={FAQ as any} />
- <Route path={`${WebsitePaths.About}`} component={About as any} />
- <Route path={`${WebsitePaths.Wiki}`} component={Wiki as any} />
- <Route path={`${WebsitePaths.ZeroExJs}/:version?`} component={LazyZeroExJSDocumentation} />
- <Route path={`${WebsitePaths.Connect}/:version?`} component={LazyConnectDocumentation} />
- <Route
- path={`${WebsitePaths.SmartContracts}/:version?`}
- component={LazySmartContractsDocumentation}
- />
- <Route component={NotFound as any} />
- </Switch>
- </div>
- </Provider>
- </MuiThemeProvider>
- </div>
- </Router>,
- document.getElementById('app'),
+ <Router>
+ <div>
+ <MuiThemeProvider muiTheme={muiTheme}>
+ <Provider store={store}>
+ <div>
+ <Switch>
+ <Route exact={true} path="/" component={Landing as any} />
+ <Redirect from="/otc" to={`${WebsitePaths.Portal}`} />
+ <Route path={`${WebsitePaths.Portal}`} component={LazyPortal} />
+ <Route path={`${WebsitePaths.FAQ}`} component={FAQ as any} />
+ <Route path={`${WebsitePaths.About}`} component={About as any} />
+ <Route path={`${WebsitePaths.Wiki}`} component={Wiki as any} />
+ <Route path={`${WebsitePaths.ZeroExJs}/:version?`} component={LazyZeroExJSDocumentation} />
+ <Route path={`${WebsitePaths.Connect}/:version?`} component={LazyConnectDocumentation} />
+ <Route
+ path={`${WebsitePaths.SmartContracts}/:version?`}
+ component={LazySmartContractsDocumentation}
+ />
+ <Route component={NotFound as any} />
+ </Switch>
+ </div>
+ </Provider>
+ </MuiThemeProvider>
+ </div>
+ </Router>,
+ document.getElementById('app'),
);
diff --git a/packages/website/ts/lazy_component.tsx b/packages/website/ts/lazy_component.tsx
index 5efebb667..48800c2dd 100644
--- a/packages/website/ts/lazy_component.tsx
+++ b/packages/website/ts/lazy_component.tsx
@@ -2,12 +2,12 @@ import * as _ from 'lodash';
import * as React from 'react';
interface LazyComponentProps {
- reactComponentPromise: Promise<React.ComponentClass<any>>;
- reactComponentProps: any;
+ reactComponentPromise: Promise<React.ComponentClass<any>>;
+ reactComponentProps: any;
}
interface LazyComponentState {
- component?: React.ComponentClass<any>;
+ component?: React.ComponentClass<any>;
}
/**
@@ -15,33 +15,33 @@ interface LazyComponentState {
* Source: https://reacttraining.com/react-router/web/guides/code-splitting
*/
export class LazyComponent extends React.Component<LazyComponentProps, LazyComponentState> {
- constructor(props: LazyComponentProps) {
- super(props);
- this.state = {
- component: undefined,
- };
- }
- public componentWillMount() {
- // tslint:disable-next-line:no-floating-promises
- this._loadComponentFireAndForgetAsync(this.props);
- }
- public componentWillReceiveProps(nextProps: LazyComponentProps) {
- if (nextProps.reactComponentPromise !== this.props.reactComponentPromise) {
- // tslint:disable-next-line:no-floating-promises
- this._loadComponentFireAndForgetAsync(nextProps);
- }
- }
- public render() {
- return _.isUndefined(this.state.component)
- ? null
- : React.createElement(this.state.component, this.props.reactComponentProps);
- }
- private async _loadComponentFireAndForgetAsync(props: LazyComponentProps) {
- const component = await props.reactComponentPromise;
- this.setState({
- component,
- });
- }
+ constructor(props: LazyComponentProps) {
+ super(props);
+ this.state = {
+ component: undefined,
+ };
+ }
+ public componentWillMount() {
+ // tslint:disable-next-line:no-floating-promises
+ this._loadComponentFireAndForgetAsync(this.props);
+ }
+ public componentWillReceiveProps(nextProps: LazyComponentProps) {
+ if (nextProps.reactComponentPromise !== this.props.reactComponentPromise) {
+ // tslint:disable-next-line:no-floating-promises
+ this._loadComponentFireAndForgetAsync(nextProps);
+ }
+ }
+ public render() {
+ return _.isUndefined(this.state.component)
+ ? null
+ : React.createElement(this.state.component, this.props.reactComponentProps);
+ }
+ private async _loadComponentFireAndForgetAsync(props: LazyComponentProps) {
+ const component = await props.reactComponentPromise;
+ this.setState({
+ component,
+ });
+ }
}
/**
@@ -52,15 +52,15 @@ export class LazyComponent extends React.Component<LazyComponentProps, LazyCompo
* @example `const LazyPortal = createLazyComponent('Portal', () => System.import<any>('ts/containers/portal'));``
*/
export const createLazyComponent = (componentName: string, lazyImport: () => Promise<any>) => {
- return (props: any) => {
- const reactComponentPromise = (async (): Promise<React.ComponentClass<any>> => {
- const mod = await lazyImport();
- const component = mod[componentName];
- if (_.isUndefined(component)) {
- throw new Error(`Did not find exported component: ${componentName}`);
- }
- return component;
- })();
- return <LazyComponent reactComponentPromise={reactComponentPromise} reactComponentProps={props} />;
- };
+ return (props: any) => {
+ const reactComponentPromise = (async (): Promise<React.ComponentClass<any>> => {
+ const mod = await lazyImport();
+ const component = mod[componentName];
+ if (_.isUndefined(component)) {
+ throw new Error(`Did not find exported component: ${componentName}`);
+ }
+ return component;
+ })();
+ return <LazyComponent reactComponentPromise={reactComponentPromise} reactComponentProps={props} />;
+ };
};
diff --git a/packages/website/ts/local_storage/local_storage.ts b/packages/website/ts/local_storage/local_storage.ts
index 4e5b77a0a..d94e6877b 100644
--- a/packages/website/ts/local_storage/local_storage.ts
+++ b/packages/website/ts/local_storage/local_storage.ts
@@ -1,35 +1,35 @@
import * as _ from 'lodash';
export const localStorage = {
- doesExist() {
- return !!window.localStorage;
- },
- getItemIfExists(key: string): string {
- if (!this.doesExist) {
- return undefined;
- }
- const item = window.localStorage.getItem(key);
- if (_.isNull(item) || item === 'undefined') {
- return '';
- }
- return item;
- },
- setItem(key: string, value: string) {
- if (!this.doesExist || _.isUndefined(value)) {
- return;
- }
- window.localStorage.setItem(key, value);
- },
- removeItem(key: string) {
- if (!this.doesExist) {
- return;
- }
- window.localStorage.removeItem(key);
- },
- getAllKeys(): string[] {
- if (!this.doesExist) {
- return [];
- }
- return _.keys(window.localStorage);
- },
+ doesExist() {
+ return !!window.localStorage;
+ },
+ getItemIfExists(key: string): string {
+ if (!this.doesExist) {
+ return undefined;
+ }
+ const item = window.localStorage.getItem(key);
+ if (_.isNull(item) || item === 'undefined') {
+ return '';
+ }
+ return item;
+ },
+ setItem(key: string, value: string) {
+ if (!this.doesExist || _.isUndefined(value)) {
+ return;
+ }
+ window.localStorage.setItem(key, value);
+ },
+ removeItem(key: string) {
+ if (!this.doesExist) {
+ return;
+ }
+ window.localStorage.removeItem(key);
+ },
+ getAllKeys(): string[] {
+ if (!this.doesExist) {
+ return [];
+ }
+ return _.keys(window.localStorage);
+ },
};
diff --git a/packages/website/ts/local_storage/tracked_token_storage.ts b/packages/website/ts/local_storage/tracked_token_storage.ts
index 2c62084e0..7733e8436 100644
--- a/packages/website/ts/local_storage/tracked_token_storage.ts
+++ b/packages/website/ts/local_storage/tracked_token_storage.ts
@@ -7,61 +7,61 @@ const TRACKED_TOKENS_KEY = 'trackedTokens';
const TRACKED_TOKENS_CLEAR_KEY = 'lastClearTrackedTokensDate';
export const trackedTokenStorage = {
- // Clear trackedTokens localStorage if we've updated the config variable in an update
- // that introduced a backward incompatible change requiring the tracked tokens to be re-set
- clearIfRequired(): void {
- const lastClearFillDate = localStorage.getItemIfExists(TRACKED_TOKENS_CLEAR_KEY);
- if (lastClearFillDate !== configs.LAST_LOCAL_STORAGE_TRACKED_TOKEN_CLEARANCE_DATE) {
- localStorage.removeItem(TRACKED_TOKENS_KEY);
- }
- localStorage.setItem(TRACKED_TOKENS_CLEAR_KEY, configs.LAST_LOCAL_STORAGE_TRACKED_TOKEN_CLEARANCE_DATE);
- },
- addTrackedTokenToUser(userAddress: string, networkId: number, token: Token): void {
- const trackedTokensByUserAddress = this.getTrackedTokensByUserAddress();
- let trackedTokensByNetworkId = trackedTokensByUserAddress[userAddress];
- if (_.isUndefined(trackedTokensByNetworkId)) {
- trackedTokensByNetworkId = {};
- }
- const trackedTokens = !_.isUndefined(trackedTokensByNetworkId[networkId])
- ? trackedTokensByNetworkId[networkId]
- : [];
- trackedTokens.push(token);
- trackedTokensByNetworkId[networkId] = trackedTokens;
- trackedTokensByUserAddress[userAddress] = trackedTokensByNetworkId;
- const trackedTokensByUserAddressJSONString = JSON.stringify(trackedTokensByUserAddress);
- localStorage.setItem(TRACKED_TOKENS_KEY, trackedTokensByUserAddressJSONString);
- },
- getTrackedTokensByUserAddress(): TrackedTokensByUserAddress {
- const trackedTokensJSONString = localStorage.getItemIfExists(TRACKED_TOKENS_KEY);
- if (_.isEmpty(trackedTokensJSONString)) {
- return {};
- }
- const trackedTokensByUserAddress = JSON.parse(trackedTokensJSONString);
- return trackedTokensByUserAddress;
- },
- getTrackedTokensIfExists(userAddress: string, networkId: number): Token[] {
- const trackedTokensJSONString = localStorage.getItemIfExists(TRACKED_TOKENS_KEY);
- if (_.isEmpty(trackedTokensJSONString)) {
- return undefined;
- }
- const trackedTokensByUserAddress = JSON.parse(trackedTokensJSONString);
- const trackedTokensByNetworkId = trackedTokensByUserAddress[userAddress];
- if (_.isUndefined(trackedTokensByNetworkId)) {
- return undefined;
- }
- const trackedTokens = trackedTokensByNetworkId[networkId];
- return trackedTokens;
- },
- removeTrackedToken(userAddress: string, networkId: number, tokenAddress: string): void {
- const trackedTokensByUserAddress = this.getTrackedTokensByUserAddress();
- const trackedTokensByNetworkId = trackedTokensByUserAddress[userAddress];
- const trackedTokens = trackedTokensByNetworkId[networkId];
- const remainingTrackedTokens = _.filter(trackedTokens, (token: Token) => {
- return token.address !== tokenAddress;
- });
- trackedTokensByNetworkId[networkId] = remainingTrackedTokens;
- trackedTokensByUserAddress[userAddress] = trackedTokensByNetworkId;
- const trackedTokensByUserAddressJSONString = JSON.stringify(trackedTokensByUserAddress);
- localStorage.setItem(TRACKED_TOKENS_KEY, trackedTokensByUserAddressJSONString);
- },
+ // Clear trackedTokens localStorage if we've updated the config variable in an update
+ // that introduced a backward incompatible change requiring the tracked tokens to be re-set
+ clearIfRequired(): void {
+ const lastClearFillDate = localStorage.getItemIfExists(TRACKED_TOKENS_CLEAR_KEY);
+ if (lastClearFillDate !== configs.LAST_LOCAL_STORAGE_TRACKED_TOKEN_CLEARANCE_DATE) {
+ localStorage.removeItem(TRACKED_TOKENS_KEY);
+ }
+ localStorage.setItem(TRACKED_TOKENS_CLEAR_KEY, configs.LAST_LOCAL_STORAGE_TRACKED_TOKEN_CLEARANCE_DATE);
+ },
+ addTrackedTokenToUser(userAddress: string, networkId: number, token: Token): void {
+ const trackedTokensByUserAddress = this.getTrackedTokensByUserAddress();
+ let trackedTokensByNetworkId = trackedTokensByUserAddress[userAddress];
+ if (_.isUndefined(trackedTokensByNetworkId)) {
+ trackedTokensByNetworkId = {};
+ }
+ const trackedTokens = !_.isUndefined(trackedTokensByNetworkId[networkId])
+ ? trackedTokensByNetworkId[networkId]
+ : [];
+ trackedTokens.push(token);
+ trackedTokensByNetworkId[networkId] = trackedTokens;
+ trackedTokensByUserAddress[userAddress] = trackedTokensByNetworkId;
+ const trackedTokensByUserAddressJSONString = JSON.stringify(trackedTokensByUserAddress);
+ localStorage.setItem(TRACKED_TOKENS_KEY, trackedTokensByUserAddressJSONString);
+ },
+ getTrackedTokensByUserAddress(): TrackedTokensByUserAddress {
+ const trackedTokensJSONString = localStorage.getItemIfExists(TRACKED_TOKENS_KEY);
+ if (_.isEmpty(trackedTokensJSONString)) {
+ return {};
+ }
+ const trackedTokensByUserAddress = JSON.parse(trackedTokensJSONString);
+ return trackedTokensByUserAddress;
+ },
+ getTrackedTokensIfExists(userAddress: string, networkId: number): Token[] {
+ const trackedTokensJSONString = localStorage.getItemIfExists(TRACKED_TOKENS_KEY);
+ if (_.isEmpty(trackedTokensJSONString)) {
+ return undefined;
+ }
+ const trackedTokensByUserAddress = JSON.parse(trackedTokensJSONString);
+ const trackedTokensByNetworkId = trackedTokensByUserAddress[userAddress];
+ if (_.isUndefined(trackedTokensByNetworkId)) {
+ return undefined;
+ }
+ const trackedTokens = trackedTokensByNetworkId[networkId];
+ return trackedTokens;
+ },
+ removeTrackedToken(userAddress: string, networkId: number, tokenAddress: string): void {
+ const trackedTokensByUserAddress = this.getTrackedTokensByUserAddress();
+ const trackedTokensByNetworkId = trackedTokensByUserAddress[userAddress];
+ const trackedTokens = trackedTokensByNetworkId[networkId];
+ const remainingTrackedTokens = _.filter(trackedTokens, (token: Token) => {
+ return token.address !== tokenAddress;
+ });
+ trackedTokensByNetworkId[networkId] = remainingTrackedTokens;
+ trackedTokensByUserAddress[userAddress] = trackedTokensByNetworkId;
+ const trackedTokensByUserAddressJSONString = JSON.stringify(trackedTokensByUserAddress);
+ localStorage.setItem(TRACKED_TOKENS_KEY, trackedTokensByUserAddressJSONString);
+ },
};
diff --git a/packages/website/ts/local_storage/trade_history_storage.tsx b/packages/website/ts/local_storage/trade_history_storage.tsx
index b20244b29..df731236e 100644
--- a/packages/website/ts/local_storage/trade_history_storage.tsx
+++ b/packages/website/ts/local_storage/trade_history_storage.tsx
@@ -11,84 +11,84 @@ const FILLS_LATEST_BLOCK = 'fillsLatestBlock';
const FILL_CLEAR_KEY = 'lastClearFillDate';
export const tradeHistoryStorage = {
- // Clear all fill related localStorage if we've updated the config variable in an update
- // that introduced a backward incompatible change requiring the user to re-fetch the fills from
- // the blockchain
- clearIfRequired() {
- const lastClearFillDate = localStorage.getItemIfExists(FILL_CLEAR_KEY);
- if (lastClearFillDate !== configs.LAST_LOCAL_STORAGE_FILL_CLEARANCE_DATE) {
- const localStorageKeys = localStorage.getAllKeys();
- _.each(localStorageKeys, key => {
- if (_.startsWith(key, `${FILLS_KEY}-`) || _.startsWith(key, `${FILLS_LATEST_BLOCK}-`)) {
- localStorage.removeItem(key);
- }
- });
- }
- localStorage.setItem(FILL_CLEAR_KEY, configs.LAST_LOCAL_STORAGE_FILL_CLEARANCE_DATE);
- },
- addFillToUser(userAddress: string, networkId: number, fill: Fill) {
- const fillsByHash = this.getUserFillsByHash(userAddress, networkId);
- const fillHash = this._getFillHash(fill);
- const doesFillExist = !_.isUndefined(fillsByHash[fillHash]);
- if (doesFillExist) {
- return; // noop
- }
- fillsByHash[fillHash] = fill;
- const userFillsJSONString = JSON.stringify(fillsByHash);
- const userFillsKey = this._getUserFillsKey(userAddress, networkId);
- localStorage.setItem(userFillsKey, userFillsJSONString);
- },
- removeFillFromUser(userAddress: string, networkId: number, fill: Fill) {
- const fillsByHash = this.getUserFillsByHash(userAddress, networkId);
- const fillHash = this._getFillHash(fill);
- const doesFillExist = !_.isUndefined(fillsByHash[fillHash]);
- if (!doesFillExist) {
- return; // noop
- }
- delete fillsByHash[fillHash];
- const userFillsJSONString = JSON.stringify(fillsByHash);
- const userFillsKey = this._getUserFillsKey(userAddress, networkId);
- localStorage.setItem(userFillsKey, userFillsJSONString);
- },
- getUserFillsByHash(userAddress: string, networkId: number): { [fillHash: string]: Fill } {
- const userFillsKey = this._getUserFillsKey(userAddress, networkId);
- const userFillsJSONString = localStorage.getItemIfExists(userFillsKey);
- if (_.isEmpty(userFillsJSONString)) {
- return {};
- }
- const userFillsByHash = JSON.parse(userFillsJSONString);
- _.each(userFillsByHash, (fill, hash) => {
- fill.paidMakerFee = new BigNumber(fill.paidMakerFee);
- fill.paidTakerFee = new BigNumber(fill.paidTakerFee);
- fill.filledTakerTokenAmount = new BigNumber(fill.filledTakerTokenAmount);
- fill.filledMakerTokenAmount = new BigNumber(fill.filledMakerTokenAmount);
- });
- return userFillsByHash;
- },
- getFillsLatestBlock(userAddress: string, networkId: number): number {
- const userFillsLatestBlockKey = this._getFillsLatestBlockKey(userAddress, networkId);
- const blockNumberStr = localStorage.getItemIfExists(userFillsLatestBlockKey);
- if (_.isEmpty(blockNumberStr)) {
- return constants.GENESIS_ORDER_BLOCK_BY_NETWORK_ID[networkId];
- }
- const blockNumber = _.parseInt(blockNumberStr);
- return blockNumber;
- },
- setFillsLatestBlock(userAddress: string, networkId: number, blockNumber: number) {
- const userFillsLatestBlockKey = this._getFillsLatestBlockKey(userAddress, networkId);
- localStorage.setItem(userFillsLatestBlockKey, `${blockNumber}`);
- },
- _getUserFillsKey(userAddress: string, networkId: number) {
- const userFillsKey = `${FILLS_KEY}-${userAddress}-${networkId}`;
- return userFillsKey;
- },
- _getFillsLatestBlockKey(userAddress: string, networkId: number) {
- const userFillsLatestBlockKey = `${FILLS_LATEST_BLOCK}-${userAddress}-${networkId}`;
- return userFillsLatestBlockKey;
- },
- _getFillHash(fill: Fill): string {
- const fillJSON = JSON.stringify(fill);
- const fillHash = ethUtil.sha256(fillJSON);
- return fillHash.toString('hex');
- },
+ // Clear all fill related localStorage if we've updated the config variable in an update
+ // that introduced a backward incompatible change requiring the user to re-fetch the fills from
+ // the blockchain
+ clearIfRequired() {
+ const lastClearFillDate = localStorage.getItemIfExists(FILL_CLEAR_KEY);
+ if (lastClearFillDate !== configs.LAST_LOCAL_STORAGE_FILL_CLEARANCE_DATE) {
+ const localStorageKeys = localStorage.getAllKeys();
+ _.each(localStorageKeys, key => {
+ if (_.startsWith(key, `${FILLS_KEY}-`) || _.startsWith(key, `${FILLS_LATEST_BLOCK}-`)) {
+ localStorage.removeItem(key);
+ }
+ });
+ }
+ localStorage.setItem(FILL_CLEAR_KEY, configs.LAST_LOCAL_STORAGE_FILL_CLEARANCE_DATE);
+ },
+ addFillToUser(userAddress: string, networkId: number, fill: Fill) {
+ const fillsByHash = this.getUserFillsByHash(userAddress, networkId);
+ const fillHash = this._getFillHash(fill);
+ const doesFillExist = !_.isUndefined(fillsByHash[fillHash]);
+ if (doesFillExist) {
+ return; // noop
+ }
+ fillsByHash[fillHash] = fill;
+ const userFillsJSONString = JSON.stringify(fillsByHash);
+ const userFillsKey = this._getUserFillsKey(userAddress, networkId);
+ localStorage.setItem(userFillsKey, userFillsJSONString);
+ },
+ removeFillFromUser(userAddress: string, networkId: number, fill: Fill) {
+ const fillsByHash = this.getUserFillsByHash(userAddress, networkId);
+ const fillHash = this._getFillHash(fill);
+ const doesFillExist = !_.isUndefined(fillsByHash[fillHash]);
+ if (!doesFillExist) {
+ return; // noop
+ }
+ delete fillsByHash[fillHash];
+ const userFillsJSONString = JSON.stringify(fillsByHash);
+ const userFillsKey = this._getUserFillsKey(userAddress, networkId);
+ localStorage.setItem(userFillsKey, userFillsJSONString);
+ },
+ getUserFillsByHash(userAddress: string, networkId: number): { [fillHash: string]: Fill } {
+ const userFillsKey = this._getUserFillsKey(userAddress, networkId);
+ const userFillsJSONString = localStorage.getItemIfExists(userFillsKey);
+ if (_.isEmpty(userFillsJSONString)) {
+ return {};
+ }
+ const userFillsByHash = JSON.parse(userFillsJSONString);
+ _.each(userFillsByHash, (fill, hash) => {
+ fill.paidMakerFee = new BigNumber(fill.paidMakerFee);
+ fill.paidTakerFee = new BigNumber(fill.paidTakerFee);
+ fill.filledTakerTokenAmount = new BigNumber(fill.filledTakerTokenAmount);
+ fill.filledMakerTokenAmount = new BigNumber(fill.filledMakerTokenAmount);
+ });
+ return userFillsByHash;
+ },
+ getFillsLatestBlock(userAddress: string, networkId: number): number {
+ const userFillsLatestBlockKey = this._getFillsLatestBlockKey(userAddress, networkId);
+ const blockNumberStr = localStorage.getItemIfExists(userFillsLatestBlockKey);
+ if (_.isEmpty(blockNumberStr)) {
+ return constants.GENESIS_ORDER_BLOCK_BY_NETWORK_ID[networkId];
+ }
+ const blockNumber = _.parseInt(blockNumberStr);
+ return blockNumber;
+ },
+ setFillsLatestBlock(userAddress: string, networkId: number, blockNumber: number) {
+ const userFillsLatestBlockKey = this._getFillsLatestBlockKey(userAddress, networkId);
+ localStorage.setItem(userFillsLatestBlockKey, `${blockNumber}`);
+ },
+ _getUserFillsKey(userAddress: string, networkId: number) {
+ const userFillsKey = `${FILLS_KEY}-${userAddress}-${networkId}`;
+ return userFillsKey;
+ },
+ _getFillsLatestBlockKey(userAddress: string, networkId: number) {
+ const userFillsLatestBlockKey = `${FILLS_LATEST_BLOCK}-${userAddress}-${networkId}`;
+ return userFillsLatestBlockKey;
+ },
+ _getFillHash(fill: Fill): string {
+ const fillJSON = JSON.stringify(fill);
+ const fillHash = ethUtil.sha256(fillJSON);
+ return fillHash.toString('hex');
+ },
};
diff --git a/packages/website/ts/pages/about/about.tsx b/packages/website/ts/pages/about/about.tsx
index 411456427..c929673f5 100644
--- a/packages/website/ts/pages/about/about.tsx
+++ b/packages/website/ts/pages/about/about.tsx
@@ -10,239 +10,239 @@ import { constants } from 'ts/utils/constants';
import { utils } from 'ts/utils/utils';
const teamRow1: ProfileInfo[] = [
- {
- name: 'Will Warren',
- title: 'Co-founder & CEO',
- description: `Smart contract R&D. Previously applied physics at Los Alamos \
+ {
+ name: 'Will Warren',
+ title: 'Co-founder & CEO',
+ description: `Smart contract R&D. Previously applied physics at Los Alamos \
Nat Lab. Mechanical engineering at UC San Diego. PhD dropout.`,
- image: '/images/team/will.jpg',
- linkedIn: 'https://www.linkedin.com/in/will-warren-92aab62b/',
- github: 'https://github.com/willwarren89',
- medium: 'https://medium.com/@willwarren89',
- },
- {
- name: 'Amir Bandeali',
- title: 'Co-founder & CTO',
- description: `Smart contract R&D. Previously fixed income trader at DRW. \
+ image: '/images/team/will.jpg',
+ linkedIn: 'https://www.linkedin.com/in/will-warren-92aab62b/',
+ github: 'https://github.com/willwarren89',
+ medium: 'https://medium.com/@willwarren89',
+ },
+ {
+ name: 'Amir Bandeali',
+ title: 'Co-founder & CTO',
+ description: `Smart contract R&D. Previously fixed income trader at DRW. \
Finance at University of Illinois, Urbana-Champaign.`,
- image: '/images/team/amir.jpeg',
- linkedIn: 'https://www.linkedin.com/in/abandeali1/',
- github: 'https://github.com/abandeali1',
- medium: 'https://medium.com/@abandeali1',
- },
- {
- name: 'Fabio Berger',
- title: 'Senior Engineer',
- description: `Full-stack blockchain engineer. Previously software engineer \
+ image: '/images/team/amir.jpeg',
+ linkedIn: 'https://www.linkedin.com/in/abandeali1/',
+ github: 'https://github.com/abandeali1',
+ medium: 'https://medium.com/@abandeali1',
+ },
+ {
+ name: 'Fabio Berger',
+ title: 'Senior Engineer',
+ description: `Full-stack blockchain engineer. Previously software engineer \
at Airtable and founder of WealthLift. Computer science at Duke.`,
- image: '/images/team/fabio.jpg',
- linkedIn: 'https://www.linkedin.com/in/fabio-berger-03ab261a/',
- github: 'https://github.com/fabioberger',
- medium: 'https://medium.com/@fabioberger',
- },
+ image: '/images/team/fabio.jpg',
+ linkedIn: 'https://www.linkedin.com/in/fabio-berger-03ab261a/',
+ github: 'https://github.com/fabioberger',
+ medium: 'https://medium.com/@fabioberger',
+ },
];
const teamRow2: ProfileInfo[] = [
- {
- name: 'Alex Xu',
- title: 'Director of Operations',
- description: `Strategy and operations. Previously digital marketing at Google \
+ {
+ name: 'Alex Xu',
+ title: 'Director of Operations',
+ description: `Strategy and operations. Previously digital marketing at Google \
and vendor management at Amazon. Economics at UC San Diego.`,
- image: '/images/team/alex.jpg',
- linkedIn: 'https://www.linkedin.com/in/alex-xu/',
- github: '',
- medium: 'https://medium.com/@aqxu',
- },
- {
- name: 'Leonid Logvinov',
- title: 'Engineer',
- description: `Full-stack blockchain engineer. Previously blockchain engineer \
+ image: '/images/team/alex.jpg',
+ linkedIn: 'https://www.linkedin.com/in/alex-xu/',
+ github: '',
+ medium: 'https://medium.com/@aqxu',
+ },
+ {
+ name: 'Leonid Logvinov',
+ title: 'Engineer',
+ description: `Full-stack blockchain engineer. Previously blockchain engineer \
at Neufund. Computer science at University of Warsaw.`,
- image: '/images/team/leonid.png',
- linkedIn: 'https://www.linkedin.com/in/leonidlogvinov/',
- github: 'https://github.com/LogvinovLeon',
- medium: 'https://medium.com/@Logvinov',
- },
- {
- name: 'Ben Burns',
- title: 'Designer',
- description: `Product, motion, and graphic designer. Previously designer \
+ image: '/images/team/leonid.png',
+ linkedIn: 'https://www.linkedin.com/in/leonidlogvinov/',
+ github: 'https://github.com/LogvinovLeon',
+ medium: 'https://medium.com/@Logvinov',
+ },
+ {
+ name: 'Ben Burns',
+ title: 'Designer',
+ description: `Product, motion, and graphic designer. Previously designer \
at Airtable and Apple. Digital Design at University of Cincinnati.`,
- image: '/images/team/ben.jpg',
- linkedIn: 'https://www.linkedin.com/in/ben-burns-30170478/',
- github: '',
- medium: '',
- },
+ image: '/images/team/ben.jpg',
+ linkedIn: 'https://www.linkedin.com/in/ben-burns-30170478/',
+ github: '',
+ medium: '',
+ },
];
const teamRow3: ProfileInfo[] = [
- {
- name: 'Brandon Millman',
- title: 'Senior Engineer',
- description: `Full-stack engineer. Previously senior software engineer at \
+ {
+ name: 'Brandon Millman',
+ title: 'Senior Engineer',
+ description: `Full-stack engineer. Previously senior software engineer at \
Twitter. Electrical and Computer Engineering at Duke.`,
- image: '/images/team/brandon.png',
- linkedIn: 'https://www.linkedin.com/in/brandon-millman-b093a022/',
- github: 'https://github.com/BMillman19',
- medium: 'https://medium.com/@bchillman',
- },
- {
- name: 'Tom Schmidt',
- title: 'Product Manager',
- description: `Previously engineering at Apple, product management at Facebook and Instagram. Computer Science at Stanford.`,
- image: '/images/team/tom.jpg',
- linkedIn: 'https://www.linkedin.com/in/tomhschmidt/',
- github: 'https://github.com/tomhschmidt',
- medium: '',
- },
- {
- name: 'Jacob Evans',
- title: 'Blockchain Engineer',
- description: `Previously software engineer at Qantas and RSA Security.`,
- image: '/images/team/jacob.jpg',
- linkedIn: 'https://www.linkedin.com/in/dekzter/',
- github: 'https://github.com/dekz',
- medium: '',
- },
+ image: '/images/team/brandon.png',
+ linkedIn: 'https://www.linkedin.com/in/brandon-millman-b093a022/',
+ github: 'https://github.com/BMillman19',
+ medium: 'https://medium.com/@bchillman',
+ },
+ {
+ name: 'Tom Schmidt',
+ title: 'Product Manager',
+ description: `Previously engineering at Apple, product management at Facebook and Instagram. Computer Science at Stanford.`,
+ image: '/images/team/tom.jpg',
+ linkedIn: 'https://www.linkedin.com/in/tomhschmidt/',
+ github: 'https://github.com/tomhschmidt',
+ medium: '',
+ },
+ {
+ name: 'Jacob Evans',
+ title: 'Blockchain Engineer',
+ description: `Previously software engineer at Qantas and RSA Security.`,
+ image: '/images/team/jacob.jpg',
+ linkedIn: 'https://www.linkedin.com/in/dekzter/',
+ github: 'https://github.com/dekz',
+ medium: '',
+ },
];
const advisors: ProfileInfo[] = [
- {
- name: 'Fred Ehrsam',
- description: 'Co-founder of Coinbase. Previously FX trader at Goldman Sachs.',
- image: '/images/advisors/fred.jpg',
- linkedIn: 'https://www.linkedin.com/in/fredehrsam/',
- medium: 'https://medium.com/@FEhrsam',
- twitter: 'https://twitter.com/FEhrsam',
- },
- {
- name: 'Olaf Carlson-Wee',
- image: '/images/advisors/olaf.png',
- description: 'Founder of Polychain Capital. First hire at Coinbase. Angel investor.',
- linkedIn: 'https://www.linkedin.com/in/olafcw/',
- angellist: 'https://angel.co/olafcw',
- },
- {
- name: 'Joey Krug',
- description: `Co-CIO at Pantera Capital. Founder of Augur. Thiel 20 Under 20 Fellow.`,
- image: '/images/advisors/joey.jpg',
- linkedIn: 'https://www.linkedin.com/in/joeykrug/',
- github: 'https://github.com/joeykrug',
- angellist: 'https://angel.co/joeykrug',
- },
- {
- name: 'Linda Xie',
- description: 'Co-founder of Scalar Capital. Previously PM at Coinbase.',
- image: '/images/advisors/linda.jpg',
- linkedIn: 'https://www.linkedin.com/in/lindaxie/',
- medium: 'https://medium.com/@linda.xie',
- twitter: 'https://twitter.com/ljxie',
- },
+ {
+ name: 'Fred Ehrsam',
+ description: 'Co-founder of Coinbase. Previously FX trader at Goldman Sachs.',
+ image: '/images/advisors/fred.jpg',
+ linkedIn: 'https://www.linkedin.com/in/fredehrsam/',
+ medium: 'https://medium.com/@FEhrsam',
+ twitter: 'https://twitter.com/FEhrsam',
+ },
+ {
+ name: 'Olaf Carlson-Wee',
+ image: '/images/advisors/olaf.png',
+ description: 'Founder of Polychain Capital. First hire at Coinbase. Angel investor.',
+ linkedIn: 'https://www.linkedin.com/in/olafcw/',
+ angellist: 'https://angel.co/olafcw',
+ },
+ {
+ name: 'Joey Krug',
+ description: `Co-CIO at Pantera Capital. Founder of Augur. Thiel 20 Under 20 Fellow.`,
+ image: '/images/advisors/joey.jpg',
+ linkedIn: 'https://www.linkedin.com/in/joeykrug/',
+ github: 'https://github.com/joeykrug',
+ angellist: 'https://angel.co/joeykrug',
+ },
+ {
+ name: 'Linda Xie',
+ description: 'Co-founder of Scalar Capital. Previously PM at Coinbase.',
+ image: '/images/advisors/linda.jpg',
+ linkedIn: 'https://www.linkedin.com/in/lindaxie/',
+ medium: 'https://medium.com/@linda.xie',
+ twitter: 'https://twitter.com/ljxie',
+ },
];
export interface AboutProps {
- source: string;
- location: Location;
+ source: string;
+ location: Location;
}
interface AboutState {}
const styles: Styles = {
- header: {
- fontFamily: 'Roboto Mono',
- fontSize: 36,
- color: 'black',
- paddingTop: 110,
- },
- weAreHiring: {
- fontSize: 30,
- color: colors.darkestGrey,
- fontFamily: 'Roboto Mono',
- letterSpacing: 7.5,
- },
+ header: {
+ fontFamily: 'Roboto Mono',
+ fontSize: 36,
+ color: 'black',
+ paddingTop: 110,
+ },
+ weAreHiring: {
+ fontSize: 30,
+ color: colors.darkestGrey,
+ fontFamily: 'Roboto Mono',
+ letterSpacing: 7.5,
+ },
};
export class About extends React.Component<AboutProps, AboutState> {
- public componentDidMount() {
- window.scrollTo(0, 0);
- }
- public render() {
- return (
- <div style={{ backgroundColor: colors.lightestGrey }}>
- <DocumentTitle title="0x About Us" />
- <TopBar
- blockchainIsLoaded={false}
- location={this.props.location}
- style={{ backgroundColor: colors.lightestGrey }}
- />
- <div id="about" className="mx-auto max-width-4 py4" style={{ color: colors.grey800 }}>
- <div className="mx-auto pb4 sm-px3" style={{ maxWidth: 435 }}>
- <div style={styles.header}>About us:</div>
- <div
- className="pt3"
- style={{
- fontSize: 17,
- color: colors.darkestGrey,
- lineHeight: 1.5,
- }}
- >
- Our team is a diverse and globally distributed group with backgrounds in engineering,
- research, business and design. We are passionate about decentralized technology and its
- potential to act as an equalizing force in the world.
- </div>
- </div>
- <div className="pt3 md-px4 lg-px0">
- <div className="clearfix pb3">{this._renderProfiles(teamRow1)}</div>
- <div className="clearfix">{this._renderProfiles(teamRow2)}</div>
- <div className="clearfix">{this._renderProfiles(teamRow3)}</div>
- </div>
- <div className="pt3 pb2">
- <div
- className="pt2 pb3 sm-center md-pl4 lg-pl0 md-ml3"
- style={{
- color: colors.grey,
- fontSize: 24,
- fontFamily: 'Roboto Mono',
- }}
- >
- Advisors:
- </div>
- <div className="clearfix">{this._renderProfiles(advisors)}</div>
- </div>
- <div className="mx-auto py4 sm-px3" style={{ maxWidth: 308 }}>
- <div className="pb2" style={styles.weAreHiring}>
- WE'RE HIRING
- </div>
- <div
- className="pb4 mb4"
- style={{
- fontSize: 16,
- color: colors.darkestGrey,
- lineHeight: 1.5,
- letterSpacing: '0.5px',
- }}
- >
- We are seeking outstanding candidates to{' '}
- <a href={constants.URL_ANGELLIST} target="_blank" style={{ color: 'black' }}>
- join our team
- </a>
- . We value passion, diversity and unique perspectives.
- </div>
- </div>
- </div>
- <Footer />
- </div>
- );
- }
- private _renderProfiles(profiles: ProfileInfo[]) {
- const numIndiv = profiles.length;
- const colSize = utils.getColSize(numIndiv);
- return _.map(profiles, profile => {
- return (
- <div key={`profile-${profile.name}`}>
- <Profile colSize={colSize} profileInfo={profile} />
- </div>
- );
- });
- }
+ public componentDidMount() {
+ window.scrollTo(0, 0);
+ }
+ public render() {
+ return (
+ <div style={{ backgroundColor: colors.lightestGrey }}>
+ <DocumentTitle title="0x About Us" />
+ <TopBar
+ blockchainIsLoaded={false}
+ location={this.props.location}
+ style={{ backgroundColor: colors.lightestGrey }}
+ />
+ <div id="about" className="mx-auto max-width-4 py4" style={{ color: colors.grey800 }}>
+ <div className="mx-auto pb4 sm-px3" style={{ maxWidth: 435 }}>
+ <div style={styles.header}>About us:</div>
+ <div
+ className="pt3"
+ style={{
+ fontSize: 17,
+ color: colors.darkestGrey,
+ lineHeight: 1.5,
+ }}
+ >
+ Our team is a diverse and globally distributed group with backgrounds in engineering,
+ research, business and design. We are passionate about decentralized technology and its
+ potential to act as an equalizing force in the world.
+ </div>
+ </div>
+ <div className="pt3 md-px4 lg-px0">
+ <div className="clearfix pb3">{this._renderProfiles(teamRow1)}</div>
+ <div className="clearfix">{this._renderProfiles(teamRow2)}</div>
+ <div className="clearfix">{this._renderProfiles(teamRow3)}</div>
+ </div>
+ <div className="pt3 pb2">
+ <div
+ className="pt2 pb3 sm-center md-pl4 lg-pl0 md-ml3"
+ style={{
+ color: colors.grey,
+ fontSize: 24,
+ fontFamily: 'Roboto Mono',
+ }}
+ >
+ Advisors:
+ </div>
+ <div className="clearfix">{this._renderProfiles(advisors)}</div>
+ </div>
+ <div className="mx-auto py4 sm-px3" style={{ maxWidth: 308 }}>
+ <div className="pb2" style={styles.weAreHiring}>
+ WE'RE HIRING
+ </div>
+ <div
+ className="pb4 mb4"
+ style={{
+ fontSize: 16,
+ color: colors.darkestGrey,
+ lineHeight: 1.5,
+ letterSpacing: '0.5px',
+ }}
+ >
+ We are seeking outstanding candidates to{' '}
+ <a href={constants.URL_ANGELLIST} target="_blank" style={{ color: 'black' }}>
+ join our team
+ </a>
+ . We value passion, diversity and unique perspectives.
+ </div>
+ </div>
+ </div>
+ <Footer />
+ </div>
+ );
+ }
+ private _renderProfiles(profiles: ProfileInfo[]) {
+ const numIndiv = profiles.length;
+ const colSize = utils.getColSize(numIndiv);
+ return _.map(profiles, profile => {
+ return (
+ <div key={`profile-${profile.name}`}>
+ <Profile colSize={colSize} profileInfo={profile} />
+ </div>
+ );
+ });
+ }
}
diff --git a/packages/website/ts/pages/about/profile.tsx b/packages/website/ts/pages/about/profile.tsx
index f1830851f..18b4e0d5a 100644
--- a/packages/website/ts/pages/about/profile.tsx
+++ b/packages/website/ts/pages/about/profile.tsx
@@ -5,75 +5,75 @@ import { colors } from 'ts/utils/colors';
const IMAGE_DIMENSION = 149;
const styles: Styles = {
- subheader: {
- textTransform: 'uppercase',
- fontSize: 32,
- margin: 0,
- },
- imageContainer: {
- width: IMAGE_DIMENSION,
- height: IMAGE_DIMENSION,
- boxShadow: 'rgba(0, 0, 0, 0.19) 2px 5px 10px',
- },
+ subheader: {
+ textTransform: 'uppercase',
+ fontSize: 32,
+ margin: 0,
+ },
+ imageContainer: {
+ width: IMAGE_DIMENSION,
+ height: IMAGE_DIMENSION,
+ boxShadow: 'rgba(0, 0, 0, 0.19) 2px 5px 10px',
+ },
};
interface ProfileProps {
- colSize: number;
- profileInfo: ProfileInfo;
+ colSize: number;
+ profileInfo: ProfileInfo;
}
export function Profile(props: ProfileProps) {
- return (
- <div className={`lg-col md-col lg-col-${props.colSize} md-col-6`}>
- <div style={{ maxWidth: 300 }} className="mx-auto lg-px3 md-px3 sm-px4 sm-pb3">
- <div className="circle overflow-hidden mx-auto" style={styles.imageContainer}>
- <img width={IMAGE_DIMENSION} src={props.profileInfo.image} />
- </div>
- <div className="center" style={{ fontSize: 18, fontWeight: 'bold', paddingTop: 20 }}>
- {props.profileInfo.name}
- </div>
- {!_.isUndefined(props.profileInfo.title) && (
- <div
- className="pt1 center"
- style={{
- fontSize: 14,
- fontFamily: 'Roboto Mono',
- color: colors.darkGrey,
- }}
- >
- {props.profileInfo.title.toUpperCase()}
- </div>
- )}
- <div style={{ minHeight: 60, lineHeight: 1.4 }} className="pt1 pb2 mx-auto lg-h6 md-h6 sm-h5 sm-center">
- {props.profileInfo.description}
- </div>
- <div className="flex pb3 mx-auto sm-hide xs-hide" style={{ width: 280, opacity: 0.5 }}>
- {renderSocialMediaIcons(props.profileInfo)}
- </div>
- </div>
- </div>
- );
+ return (
+ <div className={`lg-col md-col lg-col-${props.colSize} md-col-6`}>
+ <div style={{ maxWidth: 300 }} className="mx-auto lg-px3 md-px3 sm-px4 sm-pb3">
+ <div className="circle overflow-hidden mx-auto" style={styles.imageContainer}>
+ <img width={IMAGE_DIMENSION} src={props.profileInfo.image} />
+ </div>
+ <div className="center" style={{ fontSize: 18, fontWeight: 'bold', paddingTop: 20 }}>
+ {props.profileInfo.name}
+ </div>
+ {!_.isUndefined(props.profileInfo.title) && (
+ <div
+ className="pt1 center"
+ style={{
+ fontSize: 14,
+ fontFamily: 'Roboto Mono',
+ color: colors.darkGrey,
+ }}
+ >
+ {props.profileInfo.title.toUpperCase()}
+ </div>
+ )}
+ <div style={{ minHeight: 60, lineHeight: 1.4 }} className="pt1 pb2 mx-auto lg-h6 md-h6 sm-h5 sm-center">
+ {props.profileInfo.description}
+ </div>
+ <div className="flex pb3 mx-auto sm-hide xs-hide" style={{ width: 280, opacity: 0.5 }}>
+ {renderSocialMediaIcons(props.profileInfo)}
+ </div>
+ </div>
+ </div>
+ );
}
function renderSocialMediaIcons(profileInfo: ProfileInfo) {
- const icons = [
- renderSocialMediaIcon('zmdi-github-box', profileInfo.github),
- renderSocialMediaIcon('zmdi-linkedin-box', profileInfo.linkedIn),
- renderSocialMediaIcon('zmdi-twitter-box', profileInfo.twitter),
- ];
- return icons;
+ const icons = [
+ renderSocialMediaIcon('zmdi-github-box', profileInfo.github),
+ renderSocialMediaIcon('zmdi-linkedin-box', profileInfo.linkedIn),
+ renderSocialMediaIcon('zmdi-twitter-box', profileInfo.twitter),
+ ];
+ return icons;
}
function renderSocialMediaIcon(iconName: string, url: string) {
- if (_.isEmpty(url)) {
- return null;
- }
+ if (_.isEmpty(url)) {
+ return null;
+ }
- return (
- <div key={url} className="pr1">
- <a href={url} style={{ color: 'inherit' }} target="_blank" className="text-decoration-none">
- <i className={`zmdi ${iconName}`} style={{ ...styles.socalIcon }} />
- </a>
- </div>
- );
+ return (
+ <div key={url} className="pr1">
+ <a href={url} style={{ color: 'inherit' }} target="_blank" className="text-decoration-none">
+ <i className={`zmdi ${iconName}`} style={{ ...styles.socalIcon }} />
+ </a>
+ </div>
+ );
}
diff --git a/packages/website/ts/pages/documentation/comment.tsx b/packages/website/ts/pages/documentation/comment.tsx
index 6be39f586..23cfd96bd 100644
--- a/packages/website/ts/pages/documentation/comment.tsx
+++ b/packages/website/ts/pages/documentation/comment.tsx
@@ -4,20 +4,20 @@ import * as ReactMarkdown from 'react-markdown';
import { MarkdownCodeBlock } from 'ts/pages/shared/markdown_code_block';
interface CommentProps {
- comment: string;
- className?: string;
+ comment: string;
+ className?: string;
}
const defaultProps = {
- className: '',
+ className: '',
};
export const Comment: React.SFC<CommentProps> = (props: CommentProps) => {
- return (
- <div className={`${props.className} comment`}>
- <ReactMarkdown source={props.comment} renderers={{ CodeBlock: MarkdownCodeBlock }} />
- </div>
- );
+ return (
+ <div className={`${props.className} comment`}>
+ <ReactMarkdown source={props.comment} renderers={{ CodeBlock: MarkdownCodeBlock }} />
+ </div>
+ );
};
Comment.defaultProps = defaultProps;
diff --git a/packages/website/ts/pages/documentation/custom_enum.tsx b/packages/website/ts/pages/documentation/custom_enum.tsx
index 0006b8d7e..8d50a2f52 100644
--- a/packages/website/ts/pages/documentation/custom_enum.tsx
+++ b/packages/website/ts/pages/documentation/custom_enum.tsx
@@ -6,27 +6,27 @@ import { utils } from 'ts/utils/utils';
const STRING_ENUM_CODE_PREFIX = ' strEnum(';
interface CustomEnumProps {
- type: CustomType;
+ type: CustomType;
}
// This component renders custom string enums that was a work-around for versions of
// TypeScript <2.4.0 that did not support them natively. We keep it around to support
// older versions of 0x.js <0.9.0
export function CustomEnum(props: CustomEnumProps) {
- const type = props.type;
- if (!_.startsWith(type.defaultValue, STRING_ENUM_CODE_PREFIX)) {
- utils.consoleLog('We do not yet support `Variable` types that are not strEnums');
- return null;
- }
- // Remove the prefix and postfix, leaving only the strEnum values without quotes.
- const enumValues = type.defaultValue.slice(10, -3).replace(/'/g, '');
- return (
- <span>
- {`{`}
- {'\t'}
- {enumValues}
- <br />
- {`}`}
- </span>
- );
+ const type = props.type;
+ if (!_.startsWith(type.defaultValue, STRING_ENUM_CODE_PREFIX)) {
+ utils.consoleLog('We do not yet support `Variable` types that are not strEnums');
+ return null;
+ }
+ // Remove the prefix and postfix, leaving only the strEnum values without quotes.
+ const enumValues = type.defaultValue.slice(10, -3).replace(/'/g, '');
+ return (
+ <span>
+ {`{`}
+ {'\t'}
+ {enumValues}
+ <br />
+ {`}`}
+ </span>
+ );
}
diff --git a/packages/website/ts/pages/documentation/docs_info.ts b/packages/website/ts/pages/documentation/docs_info.ts
index e1080f3a0..4b1ec122a 100644
--- a/packages/website/ts/pages/documentation/docs_info.ts
+++ b/packages/website/ts/pages/documentation/docs_info.ts
@@ -1,111 +1,111 @@
import compareVersions = require('compare-versions');
import * as _ from 'lodash';
import {
- DocAgnosticFormat,
- DocsInfoConfig,
- DocsMenu,
- DoxityDocObj,
- MenuSubsectionsBySection,
- SectionsMap,
- TypeDocNode,
+ DocAgnosticFormat,
+ DocsInfoConfig,
+ DocsMenu,
+ DoxityDocObj,
+ MenuSubsectionsBySection,
+ SectionsMap,
+ TypeDocNode,
} from 'ts/types';
export class DocsInfo {
- public displayName: string;
- public packageUrl: string;
- public subPackageName?: string;
- public websitePath: string;
- public docsJsonRoot: string;
- public menu: DocsMenu;
- public sections: SectionsMap;
- public sectionNameToMarkdown: { [sectionName: string]: string };
- private _docsInfo: DocsInfoConfig;
- constructor(config: DocsInfoConfig) {
- this.displayName = config.displayName;
- this.packageUrl = config.packageUrl;
- this.subPackageName = config.subPackageName;
- this.websitePath = config.websitePath;
- this.docsJsonRoot = config.docsJsonRoot;
- this.sections = config.sections;
- this.sectionNameToMarkdown = config.sectionNameToMarkdown;
- this._docsInfo = config;
- }
- public isPublicType(typeName: string): boolean {
- if (_.isUndefined(this._docsInfo.publicTypes)) {
- return false;
- }
- const isPublic = _.includes(this._docsInfo.publicTypes, typeName);
- return isPublic;
- }
- public getModulePathsIfExists(sectionName: string): string[] {
- const modulePathsIfExists = this._docsInfo.sectionNameToModulePath[sectionName];
- return modulePathsIfExists;
- }
- public getMenu(selectedVersion?: string): { [section: string]: string[] } {
- if (_.isUndefined(selectedVersion) || _.isUndefined(this._docsInfo.menuSubsectionToVersionWhenIntroduced)) {
- return this._docsInfo.menu;
- }
+ public displayName: string;
+ public packageUrl: string;
+ public subPackageName?: string;
+ public websitePath: string;
+ public docsJsonRoot: string;
+ public menu: DocsMenu;
+ public sections: SectionsMap;
+ public sectionNameToMarkdown: { [sectionName: string]: string };
+ private _docsInfo: DocsInfoConfig;
+ constructor(config: DocsInfoConfig) {
+ this.displayName = config.displayName;
+ this.packageUrl = config.packageUrl;
+ this.subPackageName = config.subPackageName;
+ this.websitePath = config.websitePath;
+ this.docsJsonRoot = config.docsJsonRoot;
+ this.sections = config.sections;
+ this.sectionNameToMarkdown = config.sectionNameToMarkdown;
+ this._docsInfo = config;
+ }
+ public isPublicType(typeName: string): boolean {
+ if (_.isUndefined(this._docsInfo.publicTypes)) {
+ return false;
+ }
+ const isPublic = _.includes(this._docsInfo.publicTypes, typeName);
+ return isPublic;
+ }
+ public getModulePathsIfExists(sectionName: string): string[] {
+ const modulePathsIfExists = this._docsInfo.sectionNameToModulePath[sectionName];
+ return modulePathsIfExists;
+ }
+ public getMenu(selectedVersion?: string): { [section: string]: string[] } {
+ if (_.isUndefined(selectedVersion) || _.isUndefined(this._docsInfo.menuSubsectionToVersionWhenIntroduced)) {
+ return this._docsInfo.menu;
+ }
- const finalMenu = _.cloneDeep(this._docsInfo.menu);
- if (_.isUndefined(finalMenu.contracts)) {
- return finalMenu;
- }
+ const finalMenu = _.cloneDeep(this._docsInfo.menu);
+ if (_.isUndefined(finalMenu.contracts)) {
+ return finalMenu;
+ }
- // TODO: refactor to include more sections then simply the `contracts` section
- finalMenu.contracts = _.filter(finalMenu.contracts, (contractName: string) => {
- const versionIntroducedIfExists = this._docsInfo.menuSubsectionToVersionWhenIntroduced[contractName];
- if (!_.isUndefined(versionIntroducedIfExists)) {
- const existsInSelectedVersion = compareVersions(selectedVersion, versionIntroducedIfExists) >= 0;
- return existsInSelectedVersion;
- } else {
- return true;
- }
- });
- return finalMenu;
- }
- public getMenuSubsectionsBySection(docAgnosticFormat?: DocAgnosticFormat): MenuSubsectionsBySection {
- const menuSubsectionsBySection = {} as MenuSubsectionsBySection;
- if (_.isUndefined(docAgnosticFormat)) {
- return menuSubsectionsBySection;
- }
+ // TODO: refactor to include more sections then simply the `contracts` section
+ finalMenu.contracts = _.filter(finalMenu.contracts, (contractName: string) => {
+ const versionIntroducedIfExists = this._docsInfo.menuSubsectionToVersionWhenIntroduced[contractName];
+ if (!_.isUndefined(versionIntroducedIfExists)) {
+ const existsInSelectedVersion = compareVersions(selectedVersion, versionIntroducedIfExists) >= 0;
+ return existsInSelectedVersion;
+ } else {
+ return true;
+ }
+ });
+ return finalMenu;
+ }
+ public getMenuSubsectionsBySection(docAgnosticFormat?: DocAgnosticFormat): MenuSubsectionsBySection {
+ const menuSubsectionsBySection = {} as MenuSubsectionsBySection;
+ if (_.isUndefined(docAgnosticFormat)) {
+ return menuSubsectionsBySection;
+ }
- const docSections = _.keys(this.sections);
- _.each(docSections, sectionName => {
- const docSection = docAgnosticFormat[sectionName];
- if (_.isUndefined(docSection)) {
- return; // no-op
- }
+ const docSections = _.keys(this.sections);
+ _.each(docSections, sectionName => {
+ const docSection = docAgnosticFormat[sectionName];
+ if (_.isUndefined(docSection)) {
+ return; // no-op
+ }
- if (!_.isUndefined(this.sections.types) && sectionName === this.sections.types) {
- const sortedTypesNames = _.sortBy(docSection.types, 'name');
- const typeNames = _.map(sortedTypesNames, t => t.name);
- menuSubsectionsBySection[sectionName] = typeNames;
- } else {
- let eventNames: string[] = [];
- if (!_.isUndefined(docSection.events)) {
- const sortedEventNames = _.sortBy(docSection.events, 'name');
- eventNames = _.map(sortedEventNames, m => m.name);
- }
- const sortedMethodNames = _.sortBy(docSection.methods, 'name');
- const methodNames = _.map(sortedMethodNames, m => m.name);
- menuSubsectionsBySection[sectionName] = [...methodNames, ...eventNames];
- }
- });
- return menuSubsectionsBySection;
- }
- public getTypeDefinitionsByName(docAgnosticFormat: DocAgnosticFormat) {
- if (_.isUndefined(this.sections.types)) {
- return {};
- }
+ if (!_.isUndefined(this.sections.types) && sectionName === this.sections.types) {
+ const sortedTypesNames = _.sortBy(docSection.types, 'name');
+ const typeNames = _.map(sortedTypesNames, t => t.name);
+ menuSubsectionsBySection[sectionName] = typeNames;
+ } else {
+ let eventNames: string[] = [];
+ if (!_.isUndefined(docSection.events)) {
+ const sortedEventNames = _.sortBy(docSection.events, 'name');
+ eventNames = _.map(sortedEventNames, m => m.name);
+ }
+ const sortedMethodNames = _.sortBy(docSection.methods, 'name');
+ const methodNames = _.map(sortedMethodNames, m => m.name);
+ menuSubsectionsBySection[sectionName] = [...methodNames, ...eventNames];
+ }
+ });
+ return menuSubsectionsBySection;
+ }
+ public getTypeDefinitionsByName(docAgnosticFormat: DocAgnosticFormat) {
+ if (_.isUndefined(this.sections.types)) {
+ return {};
+ }
- const typeDocSection = docAgnosticFormat[this.sections.types];
- const typeDefinitionByName = _.keyBy(typeDocSection.types, 'name');
- return typeDefinitionByName;
- }
- public isVisibleConstructor(sectionName: string): boolean {
- return _.includes(this._docsInfo.visibleConstructors, sectionName);
- }
- public convertToDocAgnosticFormat(docObj: DoxityDocObj | TypeDocNode): DocAgnosticFormat {
- return this._docsInfo.convertToDocAgnosticFormatFn(docObj, this);
- }
+ const typeDocSection = docAgnosticFormat[this.sections.types];
+ const typeDefinitionByName = _.keyBy(typeDocSection.types, 'name');
+ return typeDefinitionByName;
+ }
+ public isVisibleConstructor(sectionName: string): boolean {
+ return _.includes(this._docsInfo.visibleConstructors, sectionName);
+ }
+ public convertToDocAgnosticFormat(docObj: DoxityDocObj | TypeDocNode): DocAgnosticFormat {
+ return this._docsInfo.convertToDocAgnosticFormatFn(docObj, this);
+ }
}
diff --git a/packages/website/ts/pages/documentation/documentation.tsx b/packages/website/ts/pages/documentation/documentation.tsx
index 0fc41d775..2315847ad 100644
--- a/packages/website/ts/pages/documentation/documentation.tsx
+++ b/packages/website/ts/pages/documentation/documentation.tsx
@@ -19,17 +19,17 @@ import { NestedSidebarMenu } from 'ts/pages/shared/nested_sidebar_menu';
import { SectionHeader } from 'ts/pages/shared/section_header';
import { Dispatcher } from 'ts/redux/dispatcher';
import {
- AddressByContractName,
- DocAgnosticFormat,
- DoxityDocObj,
- EtherscanLinkSuffixes,
- Event,
- Networks,
- Property,
- SolidityMethod,
- Styles,
- TypeDefinitionByName,
- TypescriptMethod,
+ AddressByContractName,
+ DocAgnosticFormat,
+ DoxityDocObj,
+ EtherscanLinkSuffixes,
+ Event,
+ Networks,
+ Property,
+ SolidityMethod,
+ Styles,
+ TypeDefinitionByName,
+ TypescriptMethod,
} from 'ts/types';
import { colors } from 'ts/utils/colors';
import { configs } from 'ts/utils/configs';
@@ -40,340 +40,340 @@ import { utils } from 'ts/utils/utils';
const SCROLL_TOP_ID = 'docsScrollTop';
const networkNameToColor: { [network: string]: string } = {
- [Networks.kovan]: colors.purple,
- [Networks.ropsten]: colors.red,
- [Networks.mainnet]: colors.turquois,
+ [Networks.kovan]: colors.purple,
+ [Networks.ropsten]: colors.red,
+ [Networks.mainnet]: colors.turquois,
};
export interface DocumentationAllProps {
- source: string;
- location: Location;
- dispatcher: Dispatcher;
- docsVersion: string;
- availableDocVersions: string[];
- docsInfo: DocsInfo;
+ source: string;
+ location: Location;
+ dispatcher: Dispatcher;
+ docsVersion: string;
+ availableDocVersions: string[];
+ docsInfo: DocsInfo;
}
interface DocumentationState {
- docAgnosticFormat?: DocAgnosticFormat;
+ docAgnosticFormat?: DocAgnosticFormat;
}
const styles: Styles = {
- mainContainers: {
- position: 'absolute',
- top: 1,
- left: 0,
- bottom: 0,
- right: 0,
- overflowZ: 'hidden',
- overflowY: 'scroll',
- minHeight: 'calc(100vh - 1px)',
- WebkitOverflowScrolling: 'touch',
- },
- menuContainer: {
- borderColor: colors.grey300,
- maxWidth: 330,
- marginLeft: 20,
- },
+ mainContainers: {
+ position: 'absolute',
+ top: 1,
+ left: 0,
+ bottom: 0,
+ right: 0,
+ overflowZ: 'hidden',
+ overflowY: 'scroll',
+ minHeight: 'calc(100vh - 1px)',
+ WebkitOverflowScrolling: 'touch',
+ },
+ menuContainer: {
+ borderColor: colors.grey300,
+ maxWidth: 330,
+ marginLeft: 20,
+ },
};
export class Documentation extends React.Component<DocumentationAllProps, DocumentationState> {
- constructor(props: DocumentationAllProps) {
- super(props);
- this.state = {
- docAgnosticFormat: undefined,
- };
- }
- public componentWillMount() {
- const pathName = this.props.location.pathname;
- const lastSegment = pathName.substr(pathName.lastIndexOf('/') + 1);
- const versions = findVersions(lastSegment);
- const preferredVersionIfExists = versions.length > 0 ? versions[0] : undefined;
- // tslint:disable-next-line:no-floating-promises
- this._fetchJSONDocsFireAndForgetAsync(preferredVersionIfExists);
- }
- public render() {
- const menuSubsectionsBySection = _.isUndefined(this.state.docAgnosticFormat)
- ? {}
- : this.props.docsInfo.getMenuSubsectionsBySection(this.state.docAgnosticFormat);
- return (
- <div>
- <DocumentTitle title={`${this.props.docsInfo.displayName} Documentation`} />
- <TopBar
- blockchainIsLoaded={false}
- location={this.props.location}
- docsVersion={this.props.docsVersion}
- availableDocVersions={this.props.availableDocVersions}
- menu={this.props.docsInfo.getMenu(this.props.docsVersion)}
- menuSubsectionsBySection={menuSubsectionsBySection}
- shouldFullWidth={true}
- docsInfo={this.props.docsInfo}
- />
- {_.isUndefined(this.state.docAgnosticFormat) ? (
- <div className="col col-12" style={styles.mainContainers}>
- <div
- className="relative sm-px2 sm-pt2 sm-m1"
- style={{ height: 122, top: '50%', transform: 'translateY(-50%)' }}
- >
- <div className="center pb2">
- <CircularProgress size={40} thickness={5} />
- </div>
- <div className="center pt2" style={{ paddingBottom: 11 }}>
- Loading documentation...
- </div>
- </div>
- </div>
- ) : (
- <div className="mx-auto flex" style={{ color: colors.grey800, height: 43 }}>
- <div className="relative col md-col-3 lg-col-3 lg-pl0 md-pl1 sm-hide xs-hide">
- <div
- className="border-right absolute"
- style={{ ...styles.menuContainer, ...styles.mainContainers }}
- >
- <NestedSidebarMenu
- selectedVersion={this.props.docsVersion}
- versions={this.props.availableDocVersions}
- topLevelMenu={this.props.docsInfo.getMenu(this.props.docsVersion)}
- menuSubsectionsBySection={menuSubsectionsBySection}
- docPath={this.props.docsInfo.websitePath}
- />
- </div>
- </div>
- <div className="relative col lg-col-9 md-col-9 sm-col-12 col-12">
- <div id="documentation" style={styles.mainContainers} className="absolute">
- <div id={SCROLL_TOP_ID} />
- <h1 className="md-pl2 sm-pl3">
- <a href={this.props.docsInfo.packageUrl} target="_blank">
- {this.props.docsInfo.displayName}
- </a>
- </h1>
- {this._renderDocumentation()}
- </div>
- </div>
- </div>
- )}
- </div>
- );
- }
- private _renderDocumentation(): React.ReactNode {
- const subMenus = _.values(this.props.docsInfo.getMenu());
- const orderedSectionNames = _.flatten(subMenus);
+ constructor(props: DocumentationAllProps) {
+ super(props);
+ this.state = {
+ docAgnosticFormat: undefined,
+ };
+ }
+ public componentWillMount() {
+ const pathName = this.props.location.pathname;
+ const lastSegment = pathName.substr(pathName.lastIndexOf('/') + 1);
+ const versions = findVersions(lastSegment);
+ const preferredVersionIfExists = versions.length > 0 ? versions[0] : undefined;
+ // tslint:disable-next-line:no-floating-promises
+ this._fetchJSONDocsFireAndForgetAsync(preferredVersionIfExists);
+ }
+ public render() {
+ const menuSubsectionsBySection = _.isUndefined(this.state.docAgnosticFormat)
+ ? {}
+ : this.props.docsInfo.getMenuSubsectionsBySection(this.state.docAgnosticFormat);
+ return (
+ <div>
+ <DocumentTitle title={`${this.props.docsInfo.displayName} Documentation`} />
+ <TopBar
+ blockchainIsLoaded={false}
+ location={this.props.location}
+ docsVersion={this.props.docsVersion}
+ availableDocVersions={this.props.availableDocVersions}
+ menu={this.props.docsInfo.getMenu(this.props.docsVersion)}
+ menuSubsectionsBySection={menuSubsectionsBySection}
+ shouldFullWidth={true}
+ docsInfo={this.props.docsInfo}
+ />
+ {_.isUndefined(this.state.docAgnosticFormat) ? (
+ <div className="col col-12" style={styles.mainContainers}>
+ <div
+ className="relative sm-px2 sm-pt2 sm-m1"
+ style={{ height: 122, top: '50%', transform: 'translateY(-50%)' }}
+ >
+ <div className="center pb2">
+ <CircularProgress size={40} thickness={5} />
+ </div>
+ <div className="center pt2" style={{ paddingBottom: 11 }}>
+ Loading documentation...
+ </div>
+ </div>
+ </div>
+ ) : (
+ <div className="mx-auto flex" style={{ color: colors.grey800, height: 43 }}>
+ <div className="relative col md-col-3 lg-col-3 lg-pl0 md-pl1 sm-hide xs-hide">
+ <div
+ className="border-right absolute"
+ style={{ ...styles.menuContainer, ...styles.mainContainers }}
+ >
+ <NestedSidebarMenu
+ selectedVersion={this.props.docsVersion}
+ versions={this.props.availableDocVersions}
+ topLevelMenu={this.props.docsInfo.getMenu(this.props.docsVersion)}
+ menuSubsectionsBySection={menuSubsectionsBySection}
+ docPath={this.props.docsInfo.websitePath}
+ />
+ </div>
+ </div>
+ <div className="relative col lg-col-9 md-col-9 sm-col-12 col-12">
+ <div id="documentation" style={styles.mainContainers} className="absolute">
+ <div id={SCROLL_TOP_ID} />
+ <h1 className="md-pl2 sm-pl3">
+ <a href={this.props.docsInfo.packageUrl} target="_blank">
+ {this.props.docsInfo.displayName}
+ </a>
+ </h1>
+ {this._renderDocumentation()}
+ </div>
+ </div>
+ </div>
+ )}
+ </div>
+ );
+ }
+ private _renderDocumentation(): React.ReactNode {
+ const subMenus = _.values(this.props.docsInfo.getMenu());
+ const orderedSectionNames = _.flatten(subMenus);
- const typeDefinitionByName = this.props.docsInfo.getTypeDefinitionsByName(this.state.docAgnosticFormat);
- const renderedSections = _.map(orderedSectionNames, this._renderSection.bind(this, typeDefinitionByName));
+ const typeDefinitionByName = this.props.docsInfo.getTypeDefinitionsByName(this.state.docAgnosticFormat);
+ const renderedSections = _.map(orderedSectionNames, this._renderSection.bind(this, typeDefinitionByName));
- return renderedSections;
- }
- private _renderSection(typeDefinitionByName: TypeDefinitionByName, sectionName: string): React.ReactNode {
- const markdownFileIfExists = this.props.docsInfo.sectionNameToMarkdown[sectionName];
- if (!_.isUndefined(markdownFileIfExists)) {
- return (
- <MarkdownSection
- key={`markdown-section-${sectionName}`}
- sectionName={sectionName}
- markdownContent={markdownFileIfExists}
- />
- );
- }
+ return renderedSections;
+ }
+ private _renderSection(typeDefinitionByName: TypeDefinitionByName, sectionName: string): React.ReactNode {
+ const markdownFileIfExists = this.props.docsInfo.sectionNameToMarkdown[sectionName];
+ if (!_.isUndefined(markdownFileIfExists)) {
+ return (
+ <MarkdownSection
+ key={`markdown-section-${sectionName}`}
+ sectionName={sectionName}
+ markdownContent={markdownFileIfExists}
+ />
+ );
+ }
- const docSection = this.state.docAgnosticFormat[sectionName];
- if (_.isUndefined(docSection)) {
- return null;
- }
+ const docSection = this.state.docAgnosticFormat[sectionName];
+ if (_.isUndefined(docSection)) {
+ return null;
+ }
- const sortedTypes = _.sortBy(docSection.types, 'name');
- const typeDefs = _.map(sortedTypes, customType => {
- return (
- <TypeDefinition
- sectionName={sectionName}
- key={`type-${customType.name}`}
- customType={customType}
- docsInfo={this.props.docsInfo}
- />
- );
- });
+ const sortedTypes = _.sortBy(docSection.types, 'name');
+ const typeDefs = _.map(sortedTypes, customType => {
+ return (
+ <TypeDefinition
+ sectionName={sectionName}
+ key={`type-${customType.name}`}
+ customType={customType}
+ docsInfo={this.props.docsInfo}
+ />
+ );
+ });
- const sortedProperties = _.sortBy(docSection.properties, 'name');
- const propertyDefs = _.map(sortedProperties, this._renderProperty.bind(this, sectionName));
+ const sortedProperties = _.sortBy(docSection.properties, 'name');
+ const propertyDefs = _.map(sortedProperties, this._renderProperty.bind(this, sectionName));
- const sortedMethods = _.sortBy(docSection.methods, 'name');
- const methodDefs = _.map(sortedMethods, method => {
- const isConstructor = false;
- return this._renderMethodBlocks(method, sectionName, isConstructor, typeDefinitionByName);
- });
+ const sortedMethods = _.sortBy(docSection.methods, 'name');
+ const methodDefs = _.map(sortedMethods, method => {
+ const isConstructor = false;
+ return this._renderMethodBlocks(method, sectionName, isConstructor, typeDefinitionByName);
+ });
- const sortedEvents = _.sortBy(docSection.events, 'name');
- const eventDefs = _.map(sortedEvents, (event: Event, i: number) => {
- return (
- <EventDefinition
- key={`event-${event.name}-${i}`}
- event={event}
- sectionName={sectionName}
- docsInfo={this.props.docsInfo}
- />
- );
- });
- return (
- <div key={`section-${sectionName}`} className="py2 pr3 md-pl2 sm-pl3">
- <div className="flex">
- <div style={{ marginRight: 7 }}>
- <SectionHeader sectionName={sectionName} />
- </div>
- {this._renderNetworkBadgesIfExists(sectionName)}
- </div>
- {docSection.comment && <Comment comment={docSection.comment} />}
- {docSection.constructors.length > 0 &&
- this.props.docsInfo.isVisibleConstructor(sectionName) && (
- <div>
- <h2 className="thin">Constructor</h2>
- {this._renderConstructors(docSection.constructors, sectionName, typeDefinitionByName)}
- </div>
- )}
- {docSection.properties.length > 0 && (
- <div>
- <h2 className="thin">Properties</h2>
- <div>{propertyDefs}</div>
- </div>
- )}
- {docSection.methods.length > 0 && (
- <div>
- <h2 className="thin">Methods</h2>
- <div>{methodDefs}</div>
- </div>
- )}
- {!_.isUndefined(docSection.events) &&
- docSection.events.length > 0 && (
- <div>
- <h2 className="thin">Events</h2>
- <div>{eventDefs}</div>
- </div>
- )}
- {!_.isUndefined(typeDefs) &&
- typeDefs.length > 0 && (
- <div>
- <div>{typeDefs}</div>
- </div>
- )}
- </div>
- );
- }
- private _renderNetworkBadgesIfExists(sectionName: string) {
- const networkToAddressByContractName = configs.CONTRACT_ADDRESS[this.props.docsVersion];
- const badges = _.map(
- networkToAddressByContractName,
- (addressByContractName: AddressByContractName, networkName: string) => {
- const contractAddress = addressByContractName[sectionName];
- if (_.isUndefined(contractAddress)) {
- return null;
- }
- const linkIfExists = utils.getEtherScanLinkIfExists(
- contractAddress,
- constants.NETWORK_ID_BY_NAME[networkName],
- EtherscanLinkSuffixes.Address,
- );
- return (
- <a
- key={`badge-${networkName}-${sectionName}`}
- href={linkIfExists}
- target="_blank"
- style={{ color: colors.white, textDecoration: 'none' }}
- >
- <Badge title={networkName} backgroundColor={networkNameToColor[networkName]} />
- </a>
- );
- },
- );
- return badges;
- }
- private _renderConstructors(
- constructors: SolidityMethod[] | TypescriptMethod[],
- sectionName: string,
- typeDefinitionByName: TypeDefinitionByName,
- ): React.ReactNode {
- const constructorDefs = _.map(constructors, constructor => {
- return this._renderMethodBlocks(constructor, sectionName, constructor.isConstructor, typeDefinitionByName);
- });
- return <div>{constructorDefs}</div>;
- }
- private _renderProperty(sectionName: string, property: Property): React.ReactNode {
- return (
- <div key={`property-${property.name}-${property.type.name}`} className="pb3">
- <code className="hljs">
- {property.name}:
- <Type type={property.type} sectionName={sectionName} docsInfo={this.props.docsInfo} />
- </code>
- {property.source && (
- <SourceLink
- version={this.props.docsVersion}
- source={property.source}
- baseUrl={this.props.docsInfo.packageUrl}
- subPackageName={this.props.docsInfo.subPackageName}
- />
- )}
- {property.comment && <Comment comment={property.comment} className="py2" />}
- </div>
- );
- }
- private _renderMethodBlocks(
- method: SolidityMethod | TypescriptMethod,
- sectionName: string,
- isConstructor: boolean,
- typeDefinitionByName: TypeDefinitionByName,
- ): React.ReactNode {
- return (
- <MethodBlock
- key={`method-${method.name}-${sectionName}`}
- sectionName={sectionName}
- method={method}
- typeDefinitionByName={typeDefinitionByName}
- libraryVersion={this.props.docsVersion}
- docsInfo={this.props.docsInfo}
- />
- );
- }
- private _scrollToHash(): void {
- const hashWithPrefix = this.props.location.hash;
- let hash = hashWithPrefix.slice(1);
- if (_.isEmpty(hash)) {
- hash = SCROLL_TOP_ID; // scroll to the top
- }
+ const sortedEvents = _.sortBy(docSection.events, 'name');
+ const eventDefs = _.map(sortedEvents, (event: Event, i: number) => {
+ return (
+ <EventDefinition
+ key={`event-${event.name}-${i}`}
+ event={event}
+ sectionName={sectionName}
+ docsInfo={this.props.docsInfo}
+ />
+ );
+ });
+ return (
+ <div key={`section-${sectionName}`} className="py2 pr3 md-pl2 sm-pl3">
+ <div className="flex">
+ <div style={{ marginRight: 7 }}>
+ <SectionHeader sectionName={sectionName} />
+ </div>
+ {this._renderNetworkBadgesIfExists(sectionName)}
+ </div>
+ {docSection.comment && <Comment comment={docSection.comment} />}
+ {docSection.constructors.length > 0 &&
+ this.props.docsInfo.isVisibleConstructor(sectionName) && (
+ <div>
+ <h2 className="thin">Constructor</h2>
+ {this._renderConstructors(docSection.constructors, sectionName, typeDefinitionByName)}
+ </div>
+ )}
+ {docSection.properties.length > 0 && (
+ <div>
+ <h2 className="thin">Properties</h2>
+ <div>{propertyDefs}</div>
+ </div>
+ )}
+ {docSection.methods.length > 0 && (
+ <div>
+ <h2 className="thin">Methods</h2>
+ <div>{methodDefs}</div>
+ </div>
+ )}
+ {!_.isUndefined(docSection.events) &&
+ docSection.events.length > 0 && (
+ <div>
+ <h2 className="thin">Events</h2>
+ <div>{eventDefs}</div>
+ </div>
+ )}
+ {!_.isUndefined(typeDefs) &&
+ typeDefs.length > 0 && (
+ <div>
+ <div>{typeDefs}</div>
+ </div>
+ )}
+ </div>
+ );
+ }
+ private _renderNetworkBadgesIfExists(sectionName: string) {
+ const networkToAddressByContractName = configs.CONTRACT_ADDRESS[this.props.docsVersion];
+ const badges = _.map(
+ networkToAddressByContractName,
+ (addressByContractName: AddressByContractName, networkName: string) => {
+ const contractAddress = addressByContractName[sectionName];
+ if (_.isUndefined(contractAddress)) {
+ return null;
+ }
+ const linkIfExists = utils.getEtherScanLinkIfExists(
+ contractAddress,
+ constants.NETWORK_ID_BY_NAME[networkName],
+ EtherscanLinkSuffixes.Address,
+ );
+ return (
+ <a
+ key={`badge-${networkName}-${sectionName}`}
+ href={linkIfExists}
+ target="_blank"
+ style={{ color: colors.white, textDecoration: 'none' }}
+ >
+ <Badge title={networkName} backgroundColor={networkNameToColor[networkName]} />
+ </a>
+ );
+ },
+ );
+ return badges;
+ }
+ private _renderConstructors(
+ constructors: SolidityMethod[] | TypescriptMethod[],
+ sectionName: string,
+ typeDefinitionByName: TypeDefinitionByName,
+ ): React.ReactNode {
+ const constructorDefs = _.map(constructors, constructor => {
+ return this._renderMethodBlocks(constructor, sectionName, constructor.isConstructor, typeDefinitionByName);
+ });
+ return <div>{constructorDefs}</div>;
+ }
+ private _renderProperty(sectionName: string, property: Property): React.ReactNode {
+ return (
+ <div key={`property-${property.name}-${property.type.name}`} className="pb3">
+ <code className="hljs">
+ {property.name}:
+ <Type type={property.type} sectionName={sectionName} docsInfo={this.props.docsInfo} />
+ </code>
+ {property.source && (
+ <SourceLink
+ version={this.props.docsVersion}
+ source={property.source}
+ baseUrl={this.props.docsInfo.packageUrl}
+ subPackageName={this.props.docsInfo.subPackageName}
+ />
+ )}
+ {property.comment && <Comment comment={property.comment} className="py2" />}
+ </div>
+ );
+ }
+ private _renderMethodBlocks(
+ method: SolidityMethod | TypescriptMethod,
+ sectionName: string,
+ isConstructor: boolean,
+ typeDefinitionByName: TypeDefinitionByName,
+ ): React.ReactNode {
+ return (
+ <MethodBlock
+ key={`method-${method.name}-${sectionName}`}
+ sectionName={sectionName}
+ method={method}
+ typeDefinitionByName={typeDefinitionByName}
+ libraryVersion={this.props.docsVersion}
+ docsInfo={this.props.docsInfo}
+ />
+ );
+ }
+ private _scrollToHash(): void {
+ const hashWithPrefix = this.props.location.hash;
+ let hash = hashWithPrefix.slice(1);
+ if (_.isEmpty(hash)) {
+ hash = SCROLL_TOP_ID; // scroll to the top
+ }
- scroller.scrollTo(hash, {
- duration: 0,
- offset: 0,
- containerId: 'documentation',
- });
- }
- private async _fetchJSONDocsFireAndForgetAsync(preferredVersionIfExists?: string): Promise<void> {
- const versionToFileName = await docUtils.getVersionToFileNameAsync(this.props.docsInfo.docsJsonRoot);
- const versions = _.keys(versionToFileName);
- this.props.dispatcher.updateAvailableDocVersions(versions);
- const sortedVersions = semverSort.desc(versions);
- const latestVersion = sortedVersions[0];
+ scroller.scrollTo(hash, {
+ duration: 0,
+ offset: 0,
+ containerId: 'documentation',
+ });
+ }
+ private async _fetchJSONDocsFireAndForgetAsync(preferredVersionIfExists?: string): Promise<void> {
+ const versionToFileName = await docUtils.getVersionToFileNameAsync(this.props.docsInfo.docsJsonRoot);
+ const versions = _.keys(versionToFileName);
+ this.props.dispatcher.updateAvailableDocVersions(versions);
+ const sortedVersions = semverSort.desc(versions);
+ const latestVersion = sortedVersions[0];
- let versionToFetch = latestVersion;
- if (!_.isUndefined(preferredVersionIfExists)) {
- const preferredVersionFileNameIfExists = versionToFileName[preferredVersionIfExists];
- if (!_.isUndefined(preferredVersionFileNameIfExists)) {
- versionToFetch = preferredVersionIfExists;
- }
- }
- this.props.dispatcher.updateCurrentDocsVersion(versionToFetch);
+ let versionToFetch = latestVersion;
+ if (!_.isUndefined(preferredVersionIfExists)) {
+ const preferredVersionFileNameIfExists = versionToFileName[preferredVersionIfExists];
+ if (!_.isUndefined(preferredVersionFileNameIfExists)) {
+ versionToFetch = preferredVersionIfExists;
+ }
+ }
+ this.props.dispatcher.updateCurrentDocsVersion(versionToFetch);
- const versionFileNameToFetch = versionToFileName[versionToFetch];
- const versionDocObj = await docUtils.getJSONDocFileAsync(
- versionFileNameToFetch,
- this.props.docsInfo.docsJsonRoot,
- );
- const docAgnosticFormat = this.props.docsInfo.convertToDocAgnosticFormat(versionDocObj as DoxityDocObj);
+ const versionFileNameToFetch = versionToFileName[versionToFetch];
+ const versionDocObj = await docUtils.getJSONDocFileAsync(
+ versionFileNameToFetch,
+ this.props.docsInfo.docsJsonRoot,
+ );
+ const docAgnosticFormat = this.props.docsInfo.convertToDocAgnosticFormat(versionDocObj as DoxityDocObj);
- this.setState(
- {
- docAgnosticFormat,
- },
- () => {
- this._scrollToHash();
- },
- );
- }
+ this.setState(
+ {
+ docAgnosticFormat,
+ },
+ () => {
+ this._scrollToHash();
+ },
+ );
+ }
}
diff --git a/packages/website/ts/pages/documentation/enum.tsx b/packages/website/ts/pages/documentation/enum.tsx
index cfcc7f8d7..7dfdee771 100644
--- a/packages/website/ts/pages/documentation/enum.tsx
+++ b/packages/website/ts/pages/documentation/enum.tsx
@@ -3,20 +3,20 @@ import * as React from 'react';
import { EnumValue } from 'ts/types';
interface EnumProps {
- values: EnumValue[];
+ values: EnumValue[];
}
export function Enum(props: EnumProps) {
- const values = _.map(props.values, (value, i) => {
- const defaultValueIfAny = !_.isUndefined(value.defaultValue) ? ` = ${value.defaultValue}` : '';
- return `\n\t${value.name}${defaultValueIfAny},`;
- });
- return (
- <span>
- {`{`}
- {values}
- <br />
- {`}`}
- </span>
- );
+ const values = _.map(props.values, (value, i) => {
+ const defaultValueIfAny = !_.isUndefined(value.defaultValue) ? ` = ${value.defaultValue}` : '';
+ return `\n\t${value.name}${defaultValueIfAny},`;
+ });
+ return (
+ <span>
+ {`{`}
+ {values}
+ <br />
+ {`}`}
+ </span>
+ );
}
diff --git a/packages/website/ts/pages/documentation/event_definition.tsx b/packages/website/ts/pages/documentation/event_definition.tsx
index 9274ae512..0e53e38e7 100644
--- a/packages/website/ts/pages/documentation/event_definition.tsx
+++ b/packages/website/ts/pages/documentation/event_definition.tsx
@@ -7,76 +7,76 @@ import { Event, EventArg, HeaderSizes } from 'ts/types';
import { colors } from 'ts/utils/colors';
interface EventDefinitionProps {
- event: Event;
- sectionName: string;
- docsInfo: DocsInfo;
+ event: Event;
+ sectionName: string;
+ docsInfo: DocsInfo;
}
interface EventDefinitionState {
- shouldShowAnchor: boolean;
+ shouldShowAnchor: boolean;
}
export class EventDefinition extends React.Component<EventDefinitionProps, EventDefinitionState> {
- constructor(props: EventDefinitionProps) {
- super(props);
- this.state = {
- shouldShowAnchor: false,
- };
- }
- public render() {
- const event = this.props.event;
- return (
- <div
- id={`${this.props.sectionName}-${event.name}`}
- className="pb2"
- style={{ overflow: 'hidden', width: '100%' }}
- onMouseOver={this._setAnchorVisibility.bind(this, true)}
- onMouseOut={this._setAnchorVisibility.bind(this, false)}
- >
- <AnchorTitle
- headerSize={HeaderSizes.H3}
- title={`Event ${event.name}`}
- id={event.name}
- shouldShowAnchor={this.state.shouldShowAnchor}
- />
- <div style={{ fontSize: 16 }}>
- <pre>
- <code className="hljs">{this._renderEventCode()}</code>
- </pre>
- </div>
- </div>
- );
- }
- private _renderEventCode() {
- const indexed = <span style={{ color: colors.green }}> indexed</span>;
- const eventArgs = _.map(this.props.event.eventArgs, (eventArg: EventArg) => {
- const type = (
- <Type type={eventArg.type} sectionName={this.props.sectionName} docsInfo={this.props.docsInfo} />
- );
- return (
- <span key={`eventArg-${eventArg.name}`}>
- {eventArg.name}
- {eventArg.isIndexed ? indexed : ''}: {type},
- </span>
- );
- });
- const argList = _.reduce(eventArgs, (prev: React.ReactNode, curr: React.ReactNode) => {
- return [prev, '\n\t', curr];
- });
- return (
- <span>
- {`{`}
- <br />
- {'\t'}
- {argList}
- <br />
- {`}`}
- </span>
- );
- }
- private _setAnchorVisibility(shouldShowAnchor: boolean) {
- this.setState({
- shouldShowAnchor,
- });
- }
+ constructor(props: EventDefinitionProps) {
+ super(props);
+ this.state = {
+ shouldShowAnchor: false,
+ };
+ }
+ public render() {
+ const event = this.props.event;
+ return (
+ <div
+ id={`${this.props.sectionName}-${event.name}`}
+ className="pb2"
+ style={{ overflow: 'hidden', width: '100%' }}
+ onMouseOver={this._setAnchorVisibility.bind(this, true)}
+ onMouseOut={this._setAnchorVisibility.bind(this, false)}
+ >
+ <AnchorTitle
+ headerSize={HeaderSizes.H3}
+ title={`Event ${event.name}`}
+ id={event.name}
+ shouldShowAnchor={this.state.shouldShowAnchor}
+ />
+ <div style={{ fontSize: 16 }}>
+ <pre>
+ <code className="hljs">{this._renderEventCode()}</code>
+ </pre>
+ </div>
+ </div>
+ );
+ }
+ private _renderEventCode() {
+ const indexed = <span style={{ color: colors.green }}> indexed</span>;
+ const eventArgs = _.map(this.props.event.eventArgs, (eventArg: EventArg) => {
+ const type = (
+ <Type type={eventArg.type} sectionName={this.props.sectionName} docsInfo={this.props.docsInfo} />
+ );
+ return (
+ <span key={`eventArg-${eventArg.name}`}>
+ {eventArg.name}
+ {eventArg.isIndexed ? indexed : ''}: {type},
+ </span>
+ );
+ });
+ const argList = _.reduce(eventArgs, (prev: React.ReactNode, curr: React.ReactNode) => {
+ return [prev, '\n\t', curr];
+ });
+ return (
+ <span>
+ {`{`}
+ <br />
+ {'\t'}
+ {argList}
+ <br />
+ {`}`}
+ </span>
+ );
+ }
+ private _setAnchorVisibility(shouldShowAnchor: boolean) {
+ this.setState({
+ shouldShowAnchor,
+ });
+ }
}
diff --git a/packages/website/ts/pages/documentation/interface.tsx b/packages/website/ts/pages/documentation/interface.tsx
index ee07a2c50..16a772125 100644
--- a/packages/website/ts/pages/documentation/interface.tsx
+++ b/packages/website/ts/pages/documentation/interface.tsx
@@ -6,56 +6,56 @@ import { Type } from 'ts/pages/documentation/type';
import { CustomType, TypeDocTypes } from 'ts/types';
interface InterfaceProps {
- type: CustomType;
- sectionName: string;
- docsInfo: DocsInfo;
+ type: CustomType;
+ sectionName: string;
+ docsInfo: DocsInfo;
}
export function Interface(props: InterfaceProps) {
- const type = props.type;
- const properties = _.map(type.children, property => {
- return (
- <span key={`property-${property.name}-${property.type}-${type.name}`}>
- {property.name}:{' '}
- {property.type.typeDocType !== TypeDocTypes.Reflection ? (
- <Type type={property.type} sectionName={props.sectionName} docsInfo={props.docsInfo} />
- ) : (
- <MethodSignature
- method={property.type.method}
- sectionName={props.sectionName}
- shouldHideMethodName={true}
- shouldUseArrowSyntax={true}
- docsInfo={props.docsInfo}
- />
- )},
- </span>
- );
- });
- const hasIndexSignature = !_.isUndefined(type.indexSignature);
- if (hasIndexSignature) {
- const is = type.indexSignature;
- const param = (
- <span key={`indexSigParams-${is.keyName}-${is.keyType}-${type.name}`}>
- {is.keyName}: <Type type={is.keyType} sectionName={props.sectionName} docsInfo={props.docsInfo} />
- </span>
- );
- properties.push(
- <span key={`indexSignature-${type.name}-${is.keyType.name}`}>
- [{param}]: {is.valueName},
- </span>,
- );
- }
- const propertyList = _.reduce(properties, (prev: React.ReactNode, curr: React.ReactNode) => {
- return [prev, '\n\t', curr];
- });
- return (
- <span>
- {`{`}
- <br />
- {'\t'}
- {propertyList}
- <br />
- {`}`}
- </span>
- );
+ const type = props.type;
+ const properties = _.map(type.children, property => {
+ return (
+ <span key={`property-${property.name}-${property.type}-${type.name}`}>
+ {property.name}:{' '}
+ {property.type.typeDocType !== TypeDocTypes.Reflection ? (
+ <Type type={property.type} sectionName={props.sectionName} docsInfo={props.docsInfo} />
+ ) : (
+ <MethodSignature
+ method={property.type.method}
+ sectionName={props.sectionName}
+ shouldHideMethodName={true}
+ shouldUseArrowSyntax={true}
+ docsInfo={props.docsInfo}
+ />
+ )},
+ </span>
+ );
+ });
+ const hasIndexSignature = !_.isUndefined(type.indexSignature);
+ if (hasIndexSignature) {
+ const is = type.indexSignature;
+ const param = (
+ <span key={`indexSigParams-${is.keyName}-${is.keyType}-${type.name}`}>
+ {is.keyName}: <Type type={is.keyType} sectionName={props.sectionName} docsInfo={props.docsInfo} />
+ </span>
+ );
+ properties.push(
+ <span key={`indexSignature-${type.name}-${is.keyType.name}`}>
+ [{param}]: {is.valueName},
+ </span>,
+ );
+ }
+ const propertyList = _.reduce(properties, (prev: React.ReactNode, curr: React.ReactNode) => {
+ return [prev, '\n\t', curr];
+ });
+ return (
+ <span>
+ {`{`}
+ <br />
+ {'\t'}
+ {propertyList}
+ <br />
+ {`}`}
+ </span>
+ );
}
diff --git a/packages/website/ts/pages/documentation/method_block.tsx b/packages/website/ts/pages/documentation/method_block.tsx
index fb03cf5be..dfde5931b 100644
--- a/packages/website/ts/pages/documentation/method_block.tsx
+++ b/packages/website/ts/pages/documentation/method_block.tsx
@@ -10,133 +10,133 @@ import { colors } from 'ts/utils/colors';
import { typeDocUtils } from 'ts/utils/typedoc_utils';
interface MethodBlockProps {
- method: SolidityMethod | TypescriptMethod;
- sectionName: string;
- libraryVersion: string;
- typeDefinitionByName: TypeDefinitionByName;
- docsInfo: DocsInfo;
+ method: SolidityMethod | TypescriptMethod;
+ sectionName: string;
+ libraryVersion: string;
+ typeDefinitionByName: TypeDefinitionByName;
+ docsInfo: DocsInfo;
}
interface MethodBlockState {
- shouldShowAnchor: boolean;
+ shouldShowAnchor: boolean;
}
const styles: Styles = {
- chip: {
- fontSize: 13,
- backgroundColor: colors.lightBlueA700,
- color: colors.white,
- height: 11,
- borderRadius: 14,
- marginTop: 19,
- lineHeight: 0.8,
- },
+ chip: {
+ fontSize: 13,
+ backgroundColor: colors.lightBlueA700,
+ color: colors.white,
+ height: 11,
+ borderRadius: 14,
+ marginTop: 19,
+ lineHeight: 0.8,
+ },
};
export class MethodBlock extends React.Component<MethodBlockProps, MethodBlockState> {
- constructor(props: MethodBlockProps) {
- super(props);
- this.state = {
- shouldShowAnchor: false,
- };
- }
- public render() {
- const method = this.props.method;
- if (typeDocUtils.isPrivateOrProtectedProperty(method.name)) {
- return null;
- }
+ constructor(props: MethodBlockProps) {
+ super(props);
+ this.state = {
+ shouldShowAnchor: false,
+ };
+ }
+ public render() {
+ const method = this.props.method;
+ if (typeDocUtils.isPrivateOrProtectedProperty(method.name)) {
+ return null;
+ }
- return (
- <div
- id={`${this.props.sectionName}-${method.name}`}
- style={{ overflow: 'hidden', width: '100%' }}
- className="pb4"
- onMouseOver={this._setAnchorVisibility.bind(this, true)}
- onMouseOut={this._setAnchorVisibility.bind(this, false)}
- >
- {!method.isConstructor && (
- <div className="flex">
- {(method as TypescriptMethod).isStatic && this._renderChip('Static')}
- {(method as SolidityMethod).isConstant && this._renderChip('Constant')}
- {(method as SolidityMethod).isPayable && this._renderChip('Payable')}
- <AnchorTitle
- headerSize={HeaderSizes.H3}
- title={method.name}
- id={`${this.props.sectionName}-${method.name}`}
- shouldShowAnchor={this.state.shouldShowAnchor}
- />
- </div>
- )}
- <code className="hljs">
- <MethodSignature
- method={method}
- sectionName={this.props.sectionName}
- typeDefinitionByName={this.props.typeDefinitionByName}
- docsInfo={this.props.docsInfo}
- />
- </code>
- {(method as TypescriptMethod).source && (
- <SourceLink
- version={this.props.libraryVersion}
- source={(method as TypescriptMethod).source}
- baseUrl={this.props.docsInfo.packageUrl}
- subPackageName={this.props.docsInfo.subPackageName}
- />
- )}
- {method.comment && <Comment comment={method.comment} className="py2" />}
- {method.parameters &&
- !_.isEmpty(method.parameters) && (
- <div>
- <h4 className="pb1 thin" style={{ borderBottom: '1px solid #e1e8ed' }}>
- ARGUMENTS
- </h4>
- {this._renderParameterDescriptions(method.parameters)}
- </div>
- )}
- {method.returnComment && (
- <div className="pt1 comment">
- <h4 className="pb1 thin" style={{ borderBottom: '1px solid #e1e8ed' }}>
- RETURNS
- </h4>
- <Comment comment={method.returnComment} />
- </div>
- )}
- </div>
- );
- }
- private _renderChip(text: string) {
- return (
- <div className="p1 mr1" style={styles.chip}>
- {text}
- </div>
- );
- }
- private _renderParameterDescriptions(parameters: Parameter[]) {
- const descriptions = _.map(parameters, parameter => {
- const isOptional = parameter.isOptional;
- return (
- <div
- key={`param-description-${parameter.name}`}
- className="flex pb1 mb2"
- style={{ borderBottom: '1px solid #f0f4f7' }}
- >
- <div className="pl2 col lg-col-4 md-col-4 sm-col-12 col-12">
- <div className="bold">{parameter.name}</div>
- <div className="pt1" style={{ color: colors.grey, fontSize: 14 }}>
- {isOptional && 'optional'}
- </div>
- </div>
- <div className="col lg-col-8 md-col-8 sm-col-12 col-12">
- {parameter.comment && <Comment comment={parameter.comment} />}
- </div>
- </div>
- );
- });
- return descriptions;
- }
- private _setAnchorVisibility(shouldShowAnchor: boolean) {
- this.setState({
- shouldShowAnchor,
- });
- }
+ return (
+ <div
+ id={`${this.props.sectionName}-${method.name}`}
+ style={{ overflow: 'hidden', width: '100%' }}
+ className="pb4"
+ onMouseOver={this._setAnchorVisibility.bind(this, true)}
+ onMouseOut={this._setAnchorVisibility.bind(this, false)}
+ >
+ {!method.isConstructor && (
+ <div className="flex">
+ {(method as TypescriptMethod).isStatic && this._renderChip('Static')}
+ {(method as SolidityMethod).isConstant && this._renderChip('Constant')}
+ {(method as SolidityMethod).isPayable && this._renderChip('Payable')}
+ <AnchorTitle
+ headerSize={HeaderSizes.H3}
+ title={method.name}
+ id={`${this.props.sectionName}-${method.name}`}
+ shouldShowAnchor={this.state.shouldShowAnchor}
+ />
+ </div>
+ )}
+ <code className="hljs">
+ <MethodSignature
+ method={method}
+ sectionName={this.props.sectionName}
+ typeDefinitionByName={this.props.typeDefinitionByName}
+ docsInfo={this.props.docsInfo}
+ />
+ </code>
+ {(method as TypescriptMethod).source && (
+ <SourceLink
+ version={this.props.libraryVersion}
+ source={(method as TypescriptMethod).source}
+ baseUrl={this.props.docsInfo.packageUrl}
+ subPackageName={this.props.docsInfo.subPackageName}
+ />
+ )}
+ {method.comment && <Comment comment={method.comment} className="py2" />}
+ {method.parameters &&
+ !_.isEmpty(method.parameters) && (
+ <div>
+ <h4 className="pb1 thin" style={{ borderBottom: '1px solid #e1e8ed' }}>
+ ARGUMENTS
+ </h4>
+ {this._renderParameterDescriptions(method.parameters)}
+ </div>
+ )}
+ {method.returnComment && (
+ <div className="pt1 comment">
+ <h4 className="pb1 thin" style={{ borderBottom: '1px solid #e1e8ed' }}>
+ RETURNS
+ </h4>
+ <Comment comment={method.returnComment} />
+ </div>
+ )}
+ </div>
+ );
+ }
+ private _renderChip(text: string) {
+ return (
+ <div className="p1 mr1" style={styles.chip}>
+ {text}
+ </div>
+ );
+ }
+ private _renderParameterDescriptions(parameters: Parameter[]) {
+ const descriptions = _.map(parameters, parameter => {
+ const isOptional = parameter.isOptional;
+ return (
+ <div
+ key={`param-description-${parameter.name}`}
+ className="flex pb1 mb2"
+ style={{ borderBottom: '1px solid #f0f4f7' }}
+ >
+ <div className="pl2 col lg-col-4 md-col-4 sm-col-12 col-12">
+ <div className="bold">{parameter.name}</div>
+ <div className="pt1" style={{ color: colors.grey, fontSize: 14 }}>
+ {isOptional && 'optional'}
+ </div>
+ </div>
+ <div className="col lg-col-8 md-col-8 sm-col-12 col-12">
+ {parameter.comment && <Comment comment={parameter.comment} />}
+ </div>
+ </div>
+ );
+ });
+ return descriptions;
+ }
+ private _setAnchorVisibility(shouldShowAnchor: boolean) {
+ this.setState({
+ shouldShowAnchor,
+ });
+ }
}
diff --git a/packages/website/ts/pages/documentation/method_signature.tsx b/packages/website/ts/pages/documentation/method_signature.tsx
index 7c6bf96d2..041dcd093 100644
--- a/packages/website/ts/pages/documentation/method_signature.tsx
+++ b/packages/website/ts/pages/documentation/method_signature.tsx
@@ -6,94 +6,94 @@ import { Parameter, SolidityMethod, TypeDefinitionByName, TypescriptMethod } fro
import { constants } from 'ts/utils/constants';
interface MethodSignatureProps {
- method: TypescriptMethod | SolidityMethod;
- sectionName: string;
- shouldHideMethodName?: boolean;
- shouldUseArrowSyntax?: boolean;
- typeDefinitionByName?: TypeDefinitionByName;
- docsInfo: DocsInfo;
+ method: TypescriptMethod | SolidityMethod;
+ sectionName: string;
+ shouldHideMethodName?: boolean;
+ shouldUseArrowSyntax?: boolean;
+ typeDefinitionByName?: TypeDefinitionByName;
+ docsInfo: DocsInfo;
}
const defaultProps = {
- shouldHideMethodName: false,
- shouldUseArrowSyntax: false,
+ shouldHideMethodName: false,
+ shouldUseArrowSyntax: false,
};
export const MethodSignature: React.SFC<MethodSignatureProps> = (props: MethodSignatureProps) => {
- const sectionName = constants.TYPES_SECTION_NAME;
- const parameters = renderParameters(props.method, props.docsInfo, sectionName, props.typeDefinitionByName);
- const paramString = _.reduce(parameters, (prev: React.ReactNode, curr: React.ReactNode) => {
- return [prev, ', ', curr];
- });
- const methodName = props.shouldHideMethodName ? '' : props.method.name;
- const typeParameterIfExists = _.isUndefined((props.method as TypescriptMethod).typeParameter)
- ? undefined
- : renderTypeParameter(props.method, props.docsInfo, sectionName, props.typeDefinitionByName);
- return (
- <span>
- {props.method.callPath}
- {methodName}
- {typeParameterIfExists}({paramString})
- {props.shouldUseArrowSyntax ? ' => ' : ': '}{' '}
- {props.method.returnType && (
- <Type
- type={props.method.returnType}
- sectionName={sectionName}
- typeDefinitionByName={props.typeDefinitionByName}
- docsInfo={props.docsInfo}
- />
- )}
- </span>
- );
+ const sectionName = constants.TYPES_SECTION_NAME;
+ const parameters = renderParameters(props.method, props.docsInfo, sectionName, props.typeDefinitionByName);
+ const paramString = _.reduce(parameters, (prev: React.ReactNode, curr: React.ReactNode) => {
+ return [prev, ', ', curr];
+ });
+ const methodName = props.shouldHideMethodName ? '' : props.method.name;
+ const typeParameterIfExists = _.isUndefined((props.method as TypescriptMethod).typeParameter)
+ ? undefined
+ : renderTypeParameter(props.method, props.docsInfo, sectionName, props.typeDefinitionByName);
+ return (
+ <span>
+ {props.method.callPath}
+ {methodName}
+ {typeParameterIfExists}({paramString})
+ {props.shouldUseArrowSyntax ? ' => ' : ': '}{' '}
+ {props.method.returnType && (
+ <Type
+ type={props.method.returnType}
+ sectionName={sectionName}
+ typeDefinitionByName={props.typeDefinitionByName}
+ docsInfo={props.docsInfo}
+ />
+ )}
+ </span>
+ );
};
MethodSignature.defaultProps = defaultProps;
function renderParameters(
- method: TypescriptMethod | SolidityMethod,
- docsInfo: DocsInfo,
- sectionName: string,
- typeDefinitionByName?: TypeDefinitionByName,
+ method: TypescriptMethod | SolidityMethod,
+ docsInfo: DocsInfo,
+ sectionName: string,
+ typeDefinitionByName?: TypeDefinitionByName,
) {
- const parameters = method.parameters;
- const params = _.map(parameters, (p: Parameter) => {
- const isOptional = p.isOptional;
- const type = (
- <Type
- type={p.type}
- sectionName={sectionName}
- typeDefinitionByName={typeDefinitionByName}
- docsInfo={docsInfo}
- />
- );
- return (
- <span key={`param-${p.type}-${p.name}`}>
- {p.name}
- {isOptional && '?'}: {type}
- </span>
- );
- });
- return params;
+ const parameters = method.parameters;
+ const params = _.map(parameters, (p: Parameter) => {
+ const isOptional = p.isOptional;
+ const type = (
+ <Type
+ type={p.type}
+ sectionName={sectionName}
+ typeDefinitionByName={typeDefinitionByName}
+ docsInfo={docsInfo}
+ />
+ );
+ return (
+ <span key={`param-${p.type}-${p.name}`}>
+ {p.name}
+ {isOptional && '?'}: {type}
+ </span>
+ );
+ });
+ return params;
}
function renderTypeParameter(
- method: TypescriptMethod,
- docsInfo: DocsInfo,
- sectionName: string,
- typeDefinitionByName?: TypeDefinitionByName,
+ method: TypescriptMethod,
+ docsInfo: DocsInfo,
+ sectionName: string,
+ typeDefinitionByName?: TypeDefinitionByName,
) {
- const typeParameter = method.typeParameter;
- const typeParam = (
- <span>
- {`<${typeParameter.name} extends `}
- <Type
- type={typeParameter.type}
- sectionName={sectionName}
- typeDefinitionByName={typeDefinitionByName}
- docsInfo={docsInfo}
- />
- {`>`}
- </span>
- );
- return typeParam;
+ const typeParameter = method.typeParameter;
+ const typeParam = (
+ <span>
+ {`<${typeParameter.name} extends `}
+ <Type
+ type={typeParameter.type}
+ sectionName={sectionName}
+ typeDefinitionByName={typeDefinitionByName}
+ docsInfo={docsInfo}
+ />
+ {`>`}
+ </span>
+ );
+ return typeParam;
}
diff --git a/packages/website/ts/pages/documentation/source_link.tsx b/packages/website/ts/pages/documentation/source_link.tsx
index 32126b7da..6588ee39e 100644
--- a/packages/website/ts/pages/documentation/source_link.tsx
+++ b/packages/website/ts/pages/documentation/source_link.tsx
@@ -4,28 +4,28 @@ import { Source } from 'ts/types';
import { colors } from 'ts/utils/colors';
interface SourceLinkProps {
- source: Source;
- baseUrl: string;
- version: string;
- subPackageName: string;
+ source: Source;
+ baseUrl: string;
+ version: string;
+ subPackageName: string;
}
const packagesWithNamespace = ['connect'];
export function SourceLink(props: SourceLinkProps) {
- const src = props.source;
- const url = props.baseUrl;
- const pkg = props.subPackageName;
- let tagPrefix = pkg;
- if (_.includes(packagesWithNamespace, pkg)) {
- tagPrefix = `@0xproject/${pkg}`;
- }
- const sourceCodeUrl = `${url}/blob/${tagPrefix}%40${props.version}/packages/${pkg}/${src.fileName}#L${src.line}`;
- return (
- <div className="pt2" style={{ fontSize: 14 }}>
- <a href={sourceCodeUrl} target="_blank" className="underline" style={{ color: colors.grey }}>
- Source
- </a>
- </div>
- );
+ const src = props.source;
+ const url = props.baseUrl;
+ const pkg = props.subPackageName;
+ let tagPrefix = pkg;
+ if (_.includes(packagesWithNamespace, pkg)) {
+ tagPrefix = `@0xproject/${pkg}`;
+ }
+ const sourceCodeUrl = `${url}/blob/${tagPrefix}%40${props.version}/packages/${pkg}/${src.fileName}#L${src.line}`;
+ return (
+ <div className="pt2" style={{ fontSize: 14 }}>
+ <a href={sourceCodeUrl} target="_blank" className="underline" style={{ color: colors.grey }}>
+ Source
+ </a>
+ </div>
+ );
}
diff --git a/packages/website/ts/pages/documentation/type.tsx b/packages/website/ts/pages/documentation/type.tsx
index 9a2696e22..e989e7129 100644
--- a/packages/website/ts/pages/documentation/type.tsx
+++ b/packages/website/ts/pages/documentation/type.tsx
@@ -11,202 +11,202 @@ import { utils } from 'ts/utils/utils';
// Some types reference other libraries. For these types, we want to link the user to the relevant documentation.
const typeToUrl: { [typeName: string]: string } = {
- Web3: constants.URL_WEB3_DOCS,
- Provider: constants.URL_WEB3_PROVIDER_DOCS,
- BigNumber: constants.URL_BIGNUMBERJS_GITHUB,
- DecodedLogEntryEvent: constants.URL_WEB3_DECODED_LOG_ENTRY_EVENT,
- LogEntryEvent: constants.URL_WEB3_LOG_ENTRY_EVENT,
+ Web3: constants.URL_WEB3_DOCS,
+ Provider: constants.URL_WEB3_PROVIDER_DOCS,
+ BigNumber: constants.URL_BIGNUMBERJS_GITHUB,
+ DecodedLogEntryEvent: constants.URL_WEB3_DECODED_LOG_ENTRY_EVENT,
+ LogEntryEvent: constants.URL_WEB3_LOG_ENTRY_EVENT,
};
const typePrefix: { [typeName: string]: string } = {
- Provider: 'Web3',
- DecodedLogEntryEvent: 'Web3',
- LogEntryEvent: 'Web3',
+ Provider: 'Web3',
+ DecodedLogEntryEvent: 'Web3',
+ LogEntryEvent: 'Web3',
};
const typeToSection: { [typeName: string]: string } = {
- ExchangeWrapper: 'exchange',
- TokenWrapper: 'token',
- TokenRegistryWrapper: 'tokenRegistry',
- EtherTokenWrapper: 'etherToken',
- ProxyWrapper: 'proxy',
- TokenTransferProxyWrapper: 'proxy',
- OrderStateWatcher: 'orderWatcher',
+ ExchangeWrapper: 'exchange',
+ TokenWrapper: 'token',
+ TokenRegistryWrapper: 'tokenRegistry',
+ EtherTokenWrapper: 'etherToken',
+ ProxyWrapper: 'proxy',
+ TokenTransferProxyWrapper: 'proxy',
+ OrderStateWatcher: 'orderWatcher',
};
interface TypeProps {
- type: TypeDef;
- docsInfo: DocsInfo;
- sectionName: string;
- typeDefinitionByName?: TypeDefinitionByName;
+ type: TypeDef;
+ docsInfo: DocsInfo;
+ sectionName: string;
+ typeDefinitionByName?: TypeDefinitionByName;
}
// The return type needs to be `any` here so that we can recursively define <Type /> components within
// <Type /> components (e.g when rendering the union type).
export function Type(props: TypeProps): any {
- const type = props.type;
- const isReference = type.typeDocType === TypeDocTypes.Reference;
- const isArray = type.typeDocType === TypeDocTypes.Array;
- let typeNameColor = 'inherit';
- let typeName: string | React.ReactNode;
- let typeArgs: React.ReactNode[] = [];
- switch (type.typeDocType) {
- case TypeDocTypes.Intrinsic:
- case TypeDocTypes.Unknown:
- typeName = type.name;
- typeNameColor = colors.orange;
- break;
+ const type = props.type;
+ const isReference = type.typeDocType === TypeDocTypes.Reference;
+ const isArray = type.typeDocType === TypeDocTypes.Array;
+ let typeNameColor = 'inherit';
+ let typeName: string | React.ReactNode;
+ let typeArgs: React.ReactNode[] = [];
+ switch (type.typeDocType) {
+ case TypeDocTypes.Intrinsic:
+ case TypeDocTypes.Unknown:
+ typeName = type.name;
+ typeNameColor = colors.orange;
+ break;
- case TypeDocTypes.Reference:
- typeName = type.name;
- typeArgs = _.map(type.typeArguments, (arg: TypeDef) => {
- if (arg.typeDocType === TypeDocTypes.Array) {
- const key = `type-${arg.elementType.name}-${arg.elementType.typeDocType}`;
- return (
- <span>
- <Type
- key={key}
- type={arg.elementType}
- sectionName={props.sectionName}
- typeDefinitionByName={props.typeDefinitionByName}
- docsInfo={props.docsInfo}
- />[]
- </span>
- );
- } else {
- const subType = (
- <Type
- key={`type-${arg.name}-${arg.value}-${arg.typeDocType}`}
- type={arg}
- sectionName={props.sectionName}
- typeDefinitionByName={props.typeDefinitionByName}
- docsInfo={props.docsInfo}
- />
- );
- return subType;
- }
- });
- break;
+ case TypeDocTypes.Reference:
+ typeName = type.name;
+ typeArgs = _.map(type.typeArguments, (arg: TypeDef) => {
+ if (arg.typeDocType === TypeDocTypes.Array) {
+ const key = `type-${arg.elementType.name}-${arg.elementType.typeDocType}`;
+ return (
+ <span>
+ <Type
+ key={key}
+ type={arg.elementType}
+ sectionName={props.sectionName}
+ typeDefinitionByName={props.typeDefinitionByName}
+ docsInfo={props.docsInfo}
+ />[]
+ </span>
+ );
+ } else {
+ const subType = (
+ <Type
+ key={`type-${arg.name}-${arg.value}-${arg.typeDocType}`}
+ type={arg}
+ sectionName={props.sectionName}
+ typeDefinitionByName={props.typeDefinitionByName}
+ docsInfo={props.docsInfo}
+ />
+ );
+ return subType;
+ }
+ });
+ break;
- case TypeDocTypes.StringLiteral:
- typeName = `'${type.value}'`;
- typeNameColor = colors.green;
- break;
+ case TypeDocTypes.StringLiteral:
+ typeName = `'${type.value}'`;
+ typeNameColor = colors.green;
+ break;
- case TypeDocTypes.Array:
- typeName = type.elementType.name;
- break;
+ case TypeDocTypes.Array:
+ typeName = type.elementType.name;
+ break;
- case TypeDocTypes.Union:
- const unionTypes = _.map(type.types, t => {
- return (
- <Type
- key={`type-${t.name}-${t.value}-${t.typeDocType}`}
- type={t}
- sectionName={props.sectionName}
- typeDefinitionByName={props.typeDefinitionByName}
- docsInfo={props.docsInfo}
- />
- );
- });
- typeName = _.reduce(unionTypes, (prev: React.ReactNode, curr: React.ReactNode) => {
- return [prev, '|', curr];
- });
- break;
+ case TypeDocTypes.Union:
+ const unionTypes = _.map(type.types, t => {
+ return (
+ <Type
+ key={`type-${t.name}-${t.value}-${t.typeDocType}`}
+ type={t}
+ sectionName={props.sectionName}
+ typeDefinitionByName={props.typeDefinitionByName}
+ docsInfo={props.docsInfo}
+ />
+ );
+ });
+ typeName = _.reduce(unionTypes, (prev: React.ReactNode, curr: React.ReactNode) => {
+ return [prev, '|', curr];
+ });
+ break;
- case TypeDocTypes.TypeParameter:
- typeName = type.name;
- break;
+ case TypeDocTypes.TypeParameter:
+ typeName = type.name;
+ break;
- default:
- throw utils.spawnSwitchErr('type.typeDocType', type.typeDocType);
- }
- // HACK: Normalize BigNumber to simply BigNumber. For some reason the type
- // name is unpredictably one or the other.
- if (typeName === 'BigNumber') {
- typeName = 'BigNumber';
- }
- const commaSeparatedTypeArgs = _.reduce(typeArgs, (prev: React.ReactNode, curr: React.ReactNode) => {
- return [prev, ', ', curr];
- });
+ default:
+ throw utils.spawnSwitchErr('type.typeDocType', type.typeDocType);
+ }
+ // HACK: Normalize BigNumber to simply BigNumber. For some reason the type
+ // name is unpredictably one or the other.
+ if (typeName === 'BigNumber') {
+ typeName = 'BigNumber';
+ }
+ const commaSeparatedTypeArgs = _.reduce(typeArgs, (prev: React.ReactNode, curr: React.ReactNode) => {
+ return [prev, ', ', curr];
+ });
- const typeNameUrlIfExists = typeToUrl[typeName as string];
- const typePrefixIfExists = typePrefix[typeName as string];
- const sectionNameIfExists = typeToSection[typeName as string];
- if (!_.isUndefined(typeNameUrlIfExists)) {
- typeName = (
- <a
- href={typeNameUrlIfExists}
- target="_blank"
- className="text-decoration-none"
- style={{ color: colors.lightBlueA700 }}
- >
- {!_.isUndefined(typePrefixIfExists) ? `${typePrefixIfExists}.` : ''}
- {typeName}
- </a>
- );
- } else if (
- (isReference || isArray) &&
- (props.docsInfo.isPublicType(typeName as string) || !_.isUndefined(sectionNameIfExists))
- ) {
- const id = Math.random().toString();
- const typeDefinitionAnchorId = _.isUndefined(sectionNameIfExists)
- ? `${props.sectionName}-${typeName}`
- : sectionNameIfExists;
- let typeDefinition;
- if (props.typeDefinitionByName) {
- typeDefinition = props.typeDefinitionByName[typeName as string];
- }
- typeName = (
- <ScrollLink
- to={typeDefinitionAnchorId}
- offset={0}
- duration={constants.DOCS_SCROLL_DURATION_MS}
- containerId={constants.DOCS_CONTAINER_ID}
- >
- {_.isUndefined(typeDefinition) || utils.isUserOnMobile() ? (
- <span
- onClick={utils.setUrlHash.bind(null, typeDefinitionAnchorId)}
- style={{ color: colors.lightBlueA700, cursor: 'pointer' }}
- >
- {typeName}
- </span>
- ) : (
- <span
- data-tip={true}
- data-for={id}
- onClick={utils.setUrlHash.bind(null, typeDefinitionAnchorId)}
- style={{
- color: colors.lightBlueA700,
- cursor: 'pointer',
- display: 'inline-block',
- }}
- >
- {typeName}
- <ReactTooltip type="light" effect="solid" id={id} className="typeTooltip">
- <TypeDefinition
- sectionName={props.sectionName}
- customType={typeDefinition}
- shouldAddId={false}
- docsInfo={props.docsInfo}
- />
- </ReactTooltip>
- </span>
- )}
- </ScrollLink>
- );
- }
- return (
- <span>
- <span style={{ color: typeNameColor }}>{typeName}</span>
- {isArray && '[]'}
- {!_.isEmpty(typeArgs) && (
- <span>
- {'<'}
- {commaSeparatedTypeArgs}
- {'>'}
- </span>
- )}
- </span>
- );
+ const typeNameUrlIfExists = typeToUrl[typeName as string];
+ const typePrefixIfExists = typePrefix[typeName as string];
+ const sectionNameIfExists = typeToSection[typeName as string];
+ if (!_.isUndefined(typeNameUrlIfExists)) {
+ typeName = (
+ <a
+ href={typeNameUrlIfExists}
+ target="_blank"
+ className="text-decoration-none"
+ style={{ color: colors.lightBlueA700 }}
+ >
+ {!_.isUndefined(typePrefixIfExists) ? `${typePrefixIfExists}.` : ''}
+ {typeName}
+ </a>
+ );
+ } else if (
+ (isReference || isArray) &&
+ (props.docsInfo.isPublicType(typeName as string) || !_.isUndefined(sectionNameIfExists))
+ ) {
+ const id = Math.random().toString();
+ const typeDefinitionAnchorId = _.isUndefined(sectionNameIfExists)
+ ? `${props.sectionName}-${typeName}`
+ : sectionNameIfExists;
+ let typeDefinition;
+ if (props.typeDefinitionByName) {
+ typeDefinition = props.typeDefinitionByName[typeName as string];
+ }
+ typeName = (
+ <ScrollLink
+ to={typeDefinitionAnchorId}
+ offset={0}
+ duration={constants.DOCS_SCROLL_DURATION_MS}
+ containerId={constants.DOCS_CONTAINER_ID}
+ >
+ {_.isUndefined(typeDefinition) || utils.isUserOnMobile() ? (
+ <span
+ onClick={utils.setUrlHash.bind(null, typeDefinitionAnchorId)}
+ style={{ color: colors.lightBlueA700, cursor: 'pointer' }}
+ >
+ {typeName}
+ </span>
+ ) : (
+ <span
+ data-tip={true}
+ data-for={id}
+ onClick={utils.setUrlHash.bind(null, typeDefinitionAnchorId)}
+ style={{
+ color: colors.lightBlueA700,
+ cursor: 'pointer',
+ display: 'inline-block',
+ }}
+ >
+ {typeName}
+ <ReactTooltip type="light" effect="solid" id={id} className="typeTooltip">
+ <TypeDefinition
+ sectionName={props.sectionName}
+ customType={typeDefinition}
+ shouldAddId={false}
+ docsInfo={props.docsInfo}
+ />
+ </ReactTooltip>
+ </span>
+ )}
+ </ScrollLink>
+ );
+ }
+ return (
+ <span>
+ <span style={{ color: typeNameColor }}>{typeName}</span>
+ {isArray && '[]'}
+ {!_.isEmpty(typeArgs) && (
+ <span>
+ {'<'}
+ {commaSeparatedTypeArgs}
+ {'>'}
+ </span>
+ )}
+ </span>
+ );
}
diff --git a/packages/website/ts/pages/documentation/type_definition.tsx b/packages/website/ts/pages/documentation/type_definition.tsx
index 356926157..d46eec76c 100644
--- a/packages/website/ts/pages/documentation/type_definition.tsx
+++ b/packages/website/ts/pages/documentation/type_definition.tsx
@@ -13,113 +13,113 @@ import { colors } from 'ts/utils/colors';
import { utils } from 'ts/utils/utils';
interface TypeDefinitionProps {
- sectionName: string;
- customType: CustomType;
- shouldAddId?: boolean;
- docsInfo: DocsInfo;
+ sectionName: string;
+ customType: CustomType;
+ shouldAddId?: boolean;
+ docsInfo: DocsInfo;
}
interface TypeDefinitionState {
- shouldShowAnchor: boolean;
+ shouldShowAnchor: boolean;
}
export class TypeDefinition extends React.Component<TypeDefinitionProps, TypeDefinitionState> {
- public static defaultProps: Partial<TypeDefinitionProps> = {
- shouldAddId: true,
- };
- constructor(props: TypeDefinitionProps) {
- super(props);
- this.state = {
- shouldShowAnchor: false,
- };
- }
- public render() {
- const customType = this.props.customType;
- if (!this.props.docsInfo.isPublicType(customType.name)) {
- return null; // no-op
- }
+ public static defaultProps: Partial<TypeDefinitionProps> = {
+ shouldAddId: true,
+ };
+ constructor(props: TypeDefinitionProps) {
+ super(props);
+ this.state = {
+ shouldShowAnchor: false,
+ };
+ }
+ public render() {
+ const customType = this.props.customType;
+ if (!this.props.docsInfo.isPublicType(customType.name)) {
+ return null; // no-op
+ }
- let typePrefix: string;
- let codeSnippet: React.ReactNode;
- switch (customType.kindString) {
- case KindString.Interface:
- typePrefix = 'Interface';
- codeSnippet = (
- <Interface type={customType} sectionName={this.props.sectionName} docsInfo={this.props.docsInfo} />
- );
- break;
+ let typePrefix: string;
+ let codeSnippet: React.ReactNode;
+ switch (customType.kindString) {
+ case KindString.Interface:
+ typePrefix = 'Interface';
+ codeSnippet = (
+ <Interface type={customType} sectionName={this.props.sectionName} docsInfo={this.props.docsInfo} />
+ );
+ break;
- case KindString.Variable:
- typePrefix = 'Enum';
- codeSnippet = <CustomEnum type={customType} />;
- break;
+ case KindString.Variable:
+ typePrefix = 'Enum';
+ codeSnippet = <CustomEnum type={customType} />;
+ break;
- case KindString.Enumeration:
- typePrefix = 'Enum';
- const enumValues = _.map(customType.children, (c: CustomTypeChild) => {
- return {
- name: c.name,
- defaultValue: c.defaultValue,
- };
- });
- codeSnippet = <Enum values={enumValues} />;
- break;
+ case KindString.Enumeration:
+ typePrefix = 'Enum';
+ const enumValues = _.map(customType.children, (c: CustomTypeChild) => {
+ return {
+ name: c.name,
+ defaultValue: c.defaultValue,
+ };
+ });
+ codeSnippet = <Enum values={enumValues} />;
+ break;
- case KindString.TypeAlias:
- typePrefix = 'Type Alias';
- codeSnippet = (
- <span>
- <span style={{ color: colors.lightPurple }}>type</span> {customType.name} ={' '}
- {customType.type.typeDocType !== TypeDocTypes.Reflection ? (
- <Type
- type={customType.type}
- sectionName={this.props.sectionName}
- docsInfo={this.props.docsInfo}
- />
- ) : (
- <MethodSignature
- method={customType.type.method}
- sectionName={this.props.sectionName}
- shouldHideMethodName={true}
- shouldUseArrowSyntax={true}
- docsInfo={this.props.docsInfo}
- />
- )}
- </span>
- );
- break;
+ case KindString.TypeAlias:
+ typePrefix = 'Type Alias';
+ codeSnippet = (
+ <span>
+ <span style={{ color: colors.lightPurple }}>type</span> {customType.name} ={' '}
+ {customType.type.typeDocType !== TypeDocTypes.Reflection ? (
+ <Type
+ type={customType.type}
+ sectionName={this.props.sectionName}
+ docsInfo={this.props.docsInfo}
+ />
+ ) : (
+ <MethodSignature
+ method={customType.type.method}
+ sectionName={this.props.sectionName}
+ shouldHideMethodName={true}
+ shouldUseArrowSyntax={true}
+ docsInfo={this.props.docsInfo}
+ />
+ )}
+ </span>
+ );
+ break;
- default:
- throw utils.spawnSwitchErr('type.kindString', customType.kindString);
- }
+ default:
+ throw utils.spawnSwitchErr('type.kindString', customType.kindString);
+ }
- const typeDefinitionAnchorId = `${this.props.sectionName}-${customType.name}`;
- return (
- <div
- id={this.props.shouldAddId ? typeDefinitionAnchorId : ''}
- className="pb2"
- style={{ overflow: 'hidden', width: '100%' }}
- onMouseOver={this._setAnchorVisibility.bind(this, true)}
- onMouseOut={this._setAnchorVisibility.bind(this, false)}
- >
- <AnchorTitle
- headerSize={HeaderSizes.H3}
- title={`${typePrefix} ${customType.name}`}
- id={this.props.shouldAddId ? typeDefinitionAnchorId : ''}
- shouldShowAnchor={this.state.shouldShowAnchor}
- />
- <div style={{ fontSize: 16 }}>
- <pre>
- <code className="hljs">{codeSnippet}</code>
- </pre>
- </div>
- {customType.comment && <Comment comment={customType.comment} className="py2" />}
- </div>
- );
- }
- private _setAnchorVisibility(shouldShowAnchor: boolean) {
- this.setState({
- shouldShowAnchor,
- });
- }
+ const typeDefinitionAnchorId = `${this.props.sectionName}-${customType.name}`;
+ return (
+ <div
+ id={this.props.shouldAddId ? typeDefinitionAnchorId : ''}
+ className="pb2"
+ style={{ overflow: 'hidden', width: '100%' }}
+ onMouseOver={this._setAnchorVisibility.bind(this, true)}
+ onMouseOut={this._setAnchorVisibility.bind(this, false)}
+ >
+ <AnchorTitle
+ headerSize={HeaderSizes.H3}
+ title={`${typePrefix} ${customType.name}`}
+ id={this.props.shouldAddId ? typeDefinitionAnchorId : ''}
+ shouldShowAnchor={this.state.shouldShowAnchor}
+ />
+ <div style={{ fontSize: 16 }}>
+ <pre>
+ <code className="hljs">{codeSnippet}</code>
+ </pre>
+ </div>
+ {customType.comment && <Comment comment={customType.comment} className="py2" />}
+ </div>
+ );
+ }
+ private _setAnchorVisibility(shouldShowAnchor: boolean) {
+ this.setState({
+ shouldShowAnchor,
+ });
+ }
}
diff --git a/packages/website/ts/pages/faq/faq.tsx b/packages/website/ts/pages/faq/faq.tsx
index f437f5a8d..b4b5214a2 100644
--- a/packages/website/ts/pages/faq/faq.tsx
+++ b/packages/website/ts/pages/faq/faq.tsx
@@ -10,438 +10,438 @@ import { configs } from 'ts/utils/configs';
import { constants } from 'ts/utils/constants';
export interface FAQProps {
- source: string;
- location: Location;
+ source: string;
+ location: Location;
}
interface FAQState {}
const styles: Styles = {
- thin: {
- fontWeight: 100,
- },
+ thin: {
+ fontWeight: 100,
+ },
};
const sections: FAQSection[] = [
- {
- name: '0x Protocol',
- questions: [
- {
- prompt: 'What is 0x?',
- answer: (
- <div>
- At its core, 0x is an open and non-rent seeking protocol that facilitates trustless, low
- friction exchange of Ethereum-based assets. Developers can use 0x as a platform to build
- exchange applications on top of (<a
- href={`${configs.BASE_URL}${WebsitePaths.ZeroExJs}#introduction`}
- target="blank"
- >
- 0x.js
- </a>{' '}
- is a Javascript library for interacting with the 0x protocol). For end users, 0x will be the
- infrastructure of a wide variety of user-facing applications i.e.{' '}
- <a href={`${configs.BASE_URL}${WebsitePaths.Portal}`} target="blank">
- 0x Portal
- </a>, a decentralized application that facilitates trustless trading of Ethereum-based tokens
- between known counterparties.
- </div>
- ),
- },
- {
- prompt: 'What problem does 0x solve?',
- answer: (
- <div>
- In the two years since the Ethereum blockchain’s genesis block, numerous decentralized
- applications (dApps) have created Ethereum smart contracts for peer-to-peer exchange. Rapid
- iteration and a lack of best practices have left the blockchain scattered with proprietary and
- application-specific implementations. As a result, end users are exposed to numerous smart
- contracts of varying quality and security, with unique configuration processes and learning
- curves, all of which implement the same functionality. This approach imposes unnecessary costs
- on the network by fragmenting end users according to the particular dApp each user happens to be
- using, eliminating valuable network effects around liquidity. 0x is the solution to this problem
- by acting as modular, unopinionated building blocks that may be assembled and reconfigured.
- </div>
- ),
- },
- {
- prompt: 'How is 0x different from a centralized exchange like Poloniex or ShapeShift?',
- answer: (
- <div>
- <ul>
- <li>0x is a protocol for exchange, not a user-facing exchange application.</li>
- <li>
- 0x is decentralized and trustless; there is no central party which can be hacked, run
- away with customer funds or be subjected to government regulations. Hacks of Mt. Gox,
- Shapeshift and Bitfinex have demonstrated that these types of systemic risks are
- palpable.
- </li>
- <li>
- Rather than a proprietary system that exists to extract rent for its owners, 0x is
- public infrastructure that is funded by a globally distributed community of
- stakeholders. While the protocol is free to use, it enables for-profit user-facing
- exchange applications to be built on top of the protocol.
- </li>
- </ul>
- </div>
- ),
- },
- {
- prompt: 'If 0x protocol is free to use, where do transaction fees come in?',
- answer: (
- <div>
- 0x protocol uses off-chain order books to massively reduce friction costs for market makers and
- ensure that the blockchain is only used for trade settlement. Hosting and maintaining an
- off-chain order book is a service; to incent “Relayers” to provide this service they must be
- able to charge transaction fees on trading activity. Relayers are free to set their transaction
- fees to any value they desire. We expect Relayers to be highly competitive and transaction fees
- to approach an efficient economic equilibrium over time.
- </div>
- ),
- },
- {
- prompt: 'What are the differences between 0x protocol and state channels?',
- answer: (
- <div>
- <div>
- Participants in a state channel pass cryptographically signed messages back and forth,
- accumulating intermediate state changes without publishing them to the canonical chain until
- the channel is closed. State channels are ideal for “bar tab” applications where numerous
- intermediate state changes may be accumulated off-chain before being settled by a final
- on-chain transaction (i.e. day trading, poker, turn-based games).
- </div>
- <ul>
- <li>
- While state channels drastically reduce the number of on-chain transactions for specific
- use cases, numerous on-chain transactions and a security deposit are required to open
- and safely close a state channel making them less efficient than 0x for executing
- one-time trades.
- </li>
- <li>
- State channels are isolated from the Ethereum blockchain meaning that they cannot
- interact with smart contracts. 0x is designed to be integrated directly into smart
- contracts so trades can be executed programmatically in a single line of Solidity code.
- </li>
- </ul>
- </div>
- ),
- },
- {
- prompt: 'What types of digital assets are supported by 0x?',
- answer: (
- <div>
- 0x supports all Ethereum-based assets that adhere to the ERC20 token standard. There are many
- ERC20 tokens, worth a combined $2.2B, and more tokens are created each month. We believe that,
- by 2020, thousands of assets will be tokenized and moved onto the Ethereum blockchain including
- traditional securities such as equities, bonds and derivatives, fiat currencies and scarce
- digital goods such as video game items. In the future, cross-blockchain solutions such as{' '}
- <a href="https://cosmos.network/" target="_blank">
- Cosmos
- </a>{' '}
- and{' '}
- <a href="http://polkadot.io/" target="_blank">
- Polkadot
- </a>{' '}
- will allow cryptocurrencies to freely move between blockchains and, naturally, currencies such
- as Bitcoin will end up being represented as ERC20 tokens on the Ethereum blockchain.
- </div>
- ),
- },
- {
- prompt: '0x is open source: what prevents someone from forking the protocol?',
- answer: (
- <div>
- Ethereum and Bitcoin are both open source protocols. Each protocol has been forked, but the
- resulting clone networks have seen little adoption (as measured by transaction count or market
- cap). This is because users have little to no incentive to switch over to a clone network if the
- original has initial network effects and a talented developer team behind it. An exception is in
- the case that a protocol includes a controversial feature such as a method of rent extraction or
- a monetary policy that favors one group of users over another (Zcash developer subsidy - for
- better or worse - resulted in Zclassic). Perceived inequality can provide a strong enough
- incentive that users will fork the original protocol’s codebase and spin up a new network that
- eliminates the controversial feature. In the case of 0x, there is no rent extraction and no
- users are given special permissions. 0x protocol is upgradable. Cutting-edge technical
- capabilities can be integrated into 0x via decentralized governance (see section below),
- eliminating incentives to fork off of the original protocol and sacrifice the network effects
- surrounding liquidity that result from the shared protocol and settlement layer.
- </div>
- ),
- },
- ],
- },
- {
- name: '0x Token (ZRX)',
- questions: [
- {
- prompt: 'Explain how the 0x protocol token (zrx) works.',
- answer: (
- <div>
- <div>
- 0x protocol token (ZRX) is utilized in two ways: 1) to solve the{' '}
- <a href="https://en.wikipedia.org/wiki/Coordination_game" target="_blank">
- coordination problem
- </a>{' '}
- and drive network effects around liquidity, creating a feedback loop where early adopters of
- the protocol benefit from wider adoption and 2) to be used for decentralized governance over
- 0x protocol's update mechanism. In more detail:
- </div>
- <ul>
- <li>
- ZRX tokens are used by Makers and Takers (market participants that generate and consume
- orders, respectively) to pay transaction fees to Relayers (entities that host and
- maintain public order books).
- </li>
- <li>
- ZRX tokens are used for decentralized governance over 0x protocol’s update mechanism
- which allows its underlying smart contracts to be replaced and improved over time. An
- update mechanism is needed because 0x is built upon Ethereum’s rapidly evolving
- technology stack, decentralized governance is needed because 0x protocol’s smart
- contracts will have access to user funds and numerous dApps will need to plug into 0x
- smart contracts. Decentralized governance ensures that this update process is secure and
- minimizes disruption to the network.
- </li>
- </ul>
- </div>
- ),
- },
- {
- prompt: 'Why must transaction fees be denominated in 0x token (ZRX) rather than ETH?',
- answer: (
- <div>
- 0x protocol’s decentralized update mechanism is analogous to proof-of-stake. To maximize the
- alignment of stakeholder and end user incentives, the staked asset must provide utility within
- the protocol.
- </div>
- ),
- },
- {
- prompt: 'How will decentralized governance work?',
- answer: (
- <div>
- Decentralized governance is an ongoing focus of research; it will involve token voting with ZRX.
- Ultimately the solution will maximize security while also maximizing the protocol’s ability to
- absorb new innovations. Until the governance structure is formalized and encoded within 0x DAO,
- a multi-sig will be used as a placeholder.
- </div>
- ),
- },
- ],
- },
- {
- name: 'ZRX Token Launch and Fund Use',
- questions: [
- {
- prompt: 'What is the total supply of ZRX tokens?',
- answer: <div>1,000,000,000 ZRX. Fixed supply.</div>,
- },
- {
- prompt: 'When is the Token Launch? will there be a pre-sale?',
- answer: <div>The token launch will be on August 15th, 2017. There will not be a pre-sale.</div>,
- },
- {
- prompt: 'What will the token launch proceeds be used for?',
- answer: (
- <div>
- 100% of the proceeds raised in the token launch will be used to fund the development of free and
- open source software, tools and infrastructure that support the protocol and surrounding
- ecosystem. Check out our{' '}
- <a
- href="https://docs.google.com/document/d/1_RVa-_bkU92fWRsC8eNy4vYjcTt-WC8GtqyyjbTd-oY"
- target="_blank"
- >
- development roadmap
- </a>.
- </div>
- ),
- },
- {
- prompt: 'What will be the initial distribution of ZRX tokens?',
- answer: (
- <div>
- <div className="center" style={{ width: '100%' }}>
- <img style={{ width: 350 }} src="/images/zrx_pie_chart.png" />
- </div>
- <div className="py1">
- <div className="bold pb1">Token Launch (50%)</div>
- <div>
- ZRX is inherently a governance token that plays a critical role in the process of
- upgrading 0x protocol. We are fully committed to formulating a functional and
- theoretically sound governance model and we plan to dedicate significant resources to
- R&D.
- </div>
- </div>
- <div className="py1">
- <div className="bold pb1">Retained by 0x (15%)</div>
- <div>
- The 0x core development team will be able to sustain itself for approximately five years
- using funds raised through the token launch. If 0x protocol proves to be as foundational
- a technology as we believe it to be, the retained ZRX tokens will allow the 0x core
- development team to sustain operations beyond the first 5 years.
- </div>
- </div>
- <div className="py1">
- <div className="bold pb1">Developer Fund (15%)</div>
- <div>
- The Developer Fund will be used to make targeted capital injections into high potential
- projects and teams that are attempting to grow the 0x ecosystem, strategic partnerships,
- hackathon prizes and community development activities.
- </div>
- </div>
- <div className="py1">
- <div className="bold pb1">Founding Team (10%)</div>
- <div>
- The founding team’s allocation of ZRX will vest over a traditional 4 year vesting
- schedule with a one year cliff. We believe this should be standard practice for any team
- that is committed to making their project a long term success.
- </div>
- </div>
- <div className="py1">
- <div className="bold pb1">Early Backers & Advisors (10%)</div>
- <div>
- Our backers and advisors have provided capital, resources and guidance that have allowed
- us to fill out our team, setup a robust legal entity and build a fully functional
- product before launching a token. As a result, we have a proven track record and can
- offer a token that holds genuine utility.
- </div>
- </div>
- </div>
- ),
- },
- {
- prompt: 'Can I mine ZRX tokens?',
- answer: (
- <div>
- No, the total supply of ZRX tokens is fixed and there is no continuous issuance model. Users
- that facilitate trading over 0x protocol by operating a Relayer earn transaction fees
- denominated in ZRX; as more trading activity is generated, more transaction fees are earned.
- </div>
- ),
- },
- {
- prompt: 'Will there be a lockup period for ZRX tokens sold in the token launch?',
- answer: <div>No, ZRX tokens sold in the token launch will immediately be liquid.</div>,
- },
- {
- prompt: 'Will there be a lockup period for tokens allocated to the founding team?',
- answer: (
- <div>
- Yes. ZRX tokens allocated to founders, advisors and staff members will be released over a 4 year
- vesting schedule with a 25% cliff upon completion of the initial token launch and 25% released
- each subsequent year in monthly installments. Staff members hired after the token launch will
- have a 4 year vesting schedule with a one year cliff.
- </div>
- ),
- },
- {
- prompt: 'Which cryptocurrencies will be accepted in the token launch?',
- answer: <div>ETH.</div>,
- },
- {
- prompt: 'When will 0x be live?',
- answer: (
- <div>
- An alpha version of 0x has been live on our private test network since January 2017. Version 1.0
- of 0x protocol will be deployed to the canonical Ethereum blockchain after a round of security
- audits and prior to the public token launch. 0x will be using the 0x protocol during our token
- launch.
- </div>
- ),
- },
- {
- prompt: 'Where can I find a development roadmap?',
- answer: (
- <div>
- Check it out{' '}
- <a
- href="https://drive.google.com/open?id=14IP1N8mt3YdsAoqYTyruMnZswpklUs3THyS1VXx71fo"
- target="_blank"
- >
- here
- </a>.
- </div>
- ),
- },
- ],
- },
- {
- name: 'Team',
- questions: [
- {
- prompt: 'Where is 0x based?',
- answer: <div>0x was founded in SF and is driven by a diverse group of contributors.</div>,
- },
- {
- prompt: 'How can I get involved?',
- answer: (
- <div>
- Join our{' '}
- <a href={constants.URL_ZEROEX_CHAT} target="_blank">
- Rocket.chat
- </a>! As an open source project, 0x will rely on a worldwide community of passionate developers
- to contribute proposals, ideas and code.
- </div>
- ),
- },
- {
- prompt: 'Why the name 0x?',
- answer: (
- <div>
- 0x is the prefix for hexadecimal numeric constants including Ethereum addresses. In a more
- abstract context, as the first open protocol for exchange 0x represents the beginning of the end
- for the exchange industry’s rent seeking oligopoly: zero exchange.
- </div>
- ),
- },
- {
- prompt: 'How do you pronounce 0x?',
- answer: <div>We pronounce 0x as “zero-ex,” but you are free to pronounce it however you please.</div>,
- },
- ],
- },
+ {
+ name: '0x Protocol',
+ questions: [
+ {
+ prompt: 'What is 0x?',
+ answer: (
+ <div>
+ At its core, 0x is an open and non-rent seeking protocol that facilitates trustless, low
+ friction exchange of Ethereum-based assets. Developers can use 0x as a platform to build
+ exchange applications on top of (<a
+ href={`${configs.BASE_URL}${WebsitePaths.ZeroExJs}#introduction`}
+ target="blank"
+ >
+ 0x.js
+ </a>{' '}
+ is a Javascript library for interacting with the 0x protocol). For end users, 0x will be the
+ infrastructure of a wide variety of user-facing applications i.e.{' '}
+ <a href={`${configs.BASE_URL}${WebsitePaths.Portal}`} target="blank">
+ 0x Portal
+ </a>, a decentralized application that facilitates trustless trading of Ethereum-based tokens
+ between known counterparties.
+ </div>
+ ),
+ },
+ {
+ prompt: 'What problem does 0x solve?',
+ answer: (
+ <div>
+ In the two years since the Ethereum blockchain’s genesis block, numerous decentralized
+ applications (dApps) have created Ethereum smart contracts for peer-to-peer exchange. Rapid
+ iteration and a lack of best practices have left the blockchain scattered with proprietary and
+ application-specific implementations. As a result, end users are exposed to numerous smart
+ contracts of varying quality and security, with unique configuration processes and learning
+ curves, all of which implement the same functionality. This approach imposes unnecessary costs
+ on the network by fragmenting end users according to the particular dApp each user happens to be
+ using, eliminating valuable network effects around liquidity. 0x is the solution to this problem
+ by acting as modular, unopinionated building blocks that may be assembled and reconfigured.
+ </div>
+ ),
+ },
+ {
+ prompt: 'How is 0x different from a centralized exchange like Poloniex or ShapeShift?',
+ answer: (
+ <div>
+ <ul>
+ <li>0x is a protocol for exchange, not a user-facing exchange application.</li>
+ <li>
+ 0x is decentralized and trustless; there is no central party which can be hacked, run
+ away with customer funds or be subjected to government regulations. Hacks of Mt. Gox,
+ Shapeshift and Bitfinex have demonstrated that these types of systemic risks are
+ palpable.
+ </li>
+ <li>
+ Rather than a proprietary system that exists to extract rent for its owners, 0x is
+ public infrastructure that is funded by a globally distributed community of
+ stakeholders. While the protocol is free to use, it enables for-profit user-facing
+ exchange applications to be built on top of the protocol.
+ </li>
+ </ul>
+ </div>
+ ),
+ },
+ {
+ prompt: 'If 0x protocol is free to use, where do transaction fees come in?',
+ answer: (
+ <div>
+ 0x protocol uses off-chain order books to massively reduce friction costs for market makers and
+ ensure that the blockchain is only used for trade settlement. Hosting and maintaining an
+ off-chain order book is a service; to incent “Relayers” to provide this service they must be
+ able to charge transaction fees on trading activity. Relayers are free to set their transaction
+ fees to any value they desire. We expect Relayers to be highly competitive and transaction fees
+ to approach an efficient economic equilibrium over time.
+ </div>
+ ),
+ },
+ {
+ prompt: 'What are the differences between 0x protocol and state channels?',
+ answer: (
+ <div>
+ <div>
+ Participants in a state channel pass cryptographically signed messages back and forth,
+ accumulating intermediate state changes without publishing them to the canonical chain until
+ the channel is closed. State channels are ideal for “bar tab” applications where numerous
+ intermediate state changes may be accumulated off-chain before being settled by a final
+ on-chain transaction (i.e. day trading, poker, turn-based games).
+ </div>
+ <ul>
+ <li>
+ While state channels drastically reduce the number of on-chain transactions for specific
+ use cases, numerous on-chain transactions and a security deposit are required to open
+ and safely close a state channel making them less efficient than 0x for executing
+ one-time trades.
+ </li>
+ <li>
+ State channels are isolated from the Ethereum blockchain meaning that they cannot
+ interact with smart contracts. 0x is designed to be integrated directly into smart
+ contracts so trades can be executed programmatically in a single line of Solidity code.
+ </li>
+ </ul>
+ </div>
+ ),
+ },
+ {
+ prompt: 'What types of digital assets are supported by 0x?',
+ answer: (
+ <div>
+ 0x supports all Ethereum-based assets that adhere to the ERC20 token standard. There are many
+ ERC20 tokens, worth a combined $2.2B, and more tokens are created each month. We believe that,
+ by 2020, thousands of assets will be tokenized and moved onto the Ethereum blockchain including
+ traditional securities such as equities, bonds and derivatives, fiat currencies and scarce
+ digital goods such as video game items. In the future, cross-blockchain solutions such as{' '}
+ <a href="https://cosmos.network/" target="_blank">
+ Cosmos
+ </a>{' '}
+ and{' '}
+ <a href="http://polkadot.io/" target="_blank">
+ Polkadot
+ </a>{' '}
+ will allow cryptocurrencies to freely move between blockchains and, naturally, currencies such
+ as Bitcoin will end up being represented as ERC20 tokens on the Ethereum blockchain.
+ </div>
+ ),
+ },
+ {
+ prompt: '0x is open source: what prevents someone from forking the protocol?',
+ answer: (
+ <div>
+ Ethereum and Bitcoin are both open source protocols. Each protocol has been forked, but the
+ resulting clone networks have seen little adoption (as measured by transaction count or market
+ cap). This is because users have little to no incentive to switch over to a clone network if the
+ original has initial network effects and a talented developer team behind it. An exception is in
+ the case that a protocol includes a controversial feature such as a method of rent extraction or
+ a monetary policy that favors one group of users over another (Zcash developer subsidy - for
+ better or worse - resulted in Zclassic). Perceived inequality can provide a strong enough
+ incentive that users will fork the original protocol’s codebase and spin up a new network that
+ eliminates the controversial feature. In the case of 0x, there is no rent extraction and no
+ users are given special permissions. 0x protocol is upgradable. Cutting-edge technical
+ capabilities can be integrated into 0x via decentralized governance (see section below),
+ eliminating incentives to fork off of the original protocol and sacrifice the network effects
+ surrounding liquidity that result from the shared protocol and settlement layer.
+ </div>
+ ),
+ },
+ ],
+ },
+ {
+ name: '0x Token (ZRX)',
+ questions: [
+ {
+ prompt: 'Explain how the 0x protocol token (zrx) works.',
+ answer: (
+ <div>
+ <div>
+ 0x protocol token (ZRX) is utilized in two ways: 1) to solve the{' '}
+ <a href="https://en.wikipedia.org/wiki/Coordination_game" target="_blank">
+ coordination problem
+ </a>{' '}
+ and drive network effects around liquidity, creating a feedback loop where early adopters of
+ the protocol benefit from wider adoption and 2) to be used for decentralized governance over
+ 0x protocol's update mechanism. In more detail:
+ </div>
+ <ul>
+ <li>
+ ZRX tokens are used by Makers and Takers (market participants that generate and consume
+ orders, respectively) to pay transaction fees to Relayers (entities that host and
+ maintain public order books).
+ </li>
+ <li>
+ ZRX tokens are used for decentralized governance over 0x protocol’s update mechanism
+ which allows its underlying smart contracts to be replaced and improved over time. An
+ update mechanism is needed because 0x is built upon Ethereum’s rapidly evolving
+ technology stack, decentralized governance is needed because 0x protocol’s smart
+ contracts will have access to user funds and numerous dApps will need to plug into 0x
+ smart contracts. Decentralized governance ensures that this update process is secure and
+ minimizes disruption to the network.
+ </li>
+ </ul>
+ </div>
+ ),
+ },
+ {
+ prompt: 'Why must transaction fees be denominated in 0x token (ZRX) rather than ETH?',
+ answer: (
+ <div>
+ 0x protocol’s decentralized update mechanism is analogous to proof-of-stake. To maximize the
+ alignment of stakeholder and end user incentives, the staked asset must provide utility within
+ the protocol.
+ </div>
+ ),
+ },
+ {
+ prompt: 'How will decentralized governance work?',
+ answer: (
+ <div>
+ Decentralized governance is an ongoing focus of research; it will involve token voting with ZRX.
+ Ultimately the solution will maximize security while also maximizing the protocol’s ability to
+ absorb new innovations. Until the governance structure is formalized and encoded within 0x DAO,
+ a multi-sig will be used as a placeholder.
+ </div>
+ ),
+ },
+ ],
+ },
+ {
+ name: 'ZRX Token Launch and Fund Use',
+ questions: [
+ {
+ prompt: 'What is the total supply of ZRX tokens?',
+ answer: <div>1,000,000,000 ZRX. Fixed supply.</div>,
+ },
+ {
+ prompt: 'When is the Token Launch? will there be a pre-sale?',
+ answer: <div>The token launch will be on August 15th, 2017. There will not be a pre-sale.</div>,
+ },
+ {
+ prompt: 'What will the token launch proceeds be used for?',
+ answer: (
+ <div>
+ 100% of the proceeds raised in the token launch will be used to fund the development of free and
+ open source software, tools and infrastructure that support the protocol and surrounding
+ ecosystem. Check out our{' '}
+ <a
+ href="https://docs.google.com/document/d/1_RVa-_bkU92fWRsC8eNy4vYjcTt-WC8GtqyyjbTd-oY"
+ target="_blank"
+ >
+ development roadmap
+ </a>.
+ </div>
+ ),
+ },
+ {
+ prompt: 'What will be the initial distribution of ZRX tokens?',
+ answer: (
+ <div>
+ <div className="center" style={{ width: '100%' }}>
+ <img style={{ width: 350 }} src="/images/zrx_pie_chart.png" />
+ </div>
+ <div className="py1">
+ <div className="bold pb1">Token Launch (50%)</div>
+ <div>
+ ZRX is inherently a governance token that plays a critical role in the process of
+ upgrading 0x protocol. We are fully committed to formulating a functional and
+ theoretically sound governance model and we plan to dedicate significant resources to
+ R&D.
+ </div>
+ </div>
+ <div className="py1">
+ <div className="bold pb1">Retained by 0x (15%)</div>
+ <div>
+ The 0x core development team will be able to sustain itself for approximately five years
+ using funds raised through the token launch. If 0x protocol proves to be as foundational
+ a technology as we believe it to be, the retained ZRX tokens will allow the 0x core
+ development team to sustain operations beyond the first 5 years.
+ </div>
+ </div>
+ <div className="py1">
+ <div className="bold pb1">Developer Fund (15%)</div>
+ <div>
+ The Developer Fund will be used to make targeted capital injections into high potential
+ projects and teams that are attempting to grow the 0x ecosystem, strategic partnerships,
+ hackathon prizes and community development activities.
+ </div>
+ </div>
+ <div className="py1">
+ <div className="bold pb1">Founding Team (10%)</div>
+ <div>
+ The founding team’s allocation of ZRX will vest over a traditional 4 year vesting
+ schedule with a one year cliff. We believe this should be standard practice for any team
+ that is committed to making their project a long term success.
+ </div>
+ </div>
+ <div className="py1">
+ <div className="bold pb1">Early Backers & Advisors (10%)</div>
+ <div>
+ Our backers and advisors have provided capital, resources and guidance that have allowed
+ us to fill out our team, setup a robust legal entity and build a fully functional
+ product before launching a token. As a result, we have a proven track record and can
+ offer a token that holds genuine utility.
+ </div>
+ </div>
+ </div>
+ ),
+ },
+ {
+ prompt: 'Can I mine ZRX tokens?',
+ answer: (
+ <div>
+ No, the total supply of ZRX tokens is fixed and there is no continuous issuance model. Users
+ that facilitate trading over 0x protocol by operating a Relayer earn transaction fees
+ denominated in ZRX; as more trading activity is generated, more transaction fees are earned.
+ </div>
+ ),
+ },
+ {
+ prompt: 'Will there be a lockup period for ZRX tokens sold in the token launch?',
+ answer: <div>No, ZRX tokens sold in the token launch will immediately be liquid.</div>,
+ },
+ {
+ prompt: 'Will there be a lockup period for tokens allocated to the founding team?',
+ answer: (
+ <div>
+ Yes. ZRX tokens allocated to founders, advisors and staff members will be released over a 4 year
+ vesting schedule with a 25% cliff upon completion of the initial token launch and 25% released
+ each subsequent year in monthly installments. Staff members hired after the token launch will
+ have a 4 year vesting schedule with a one year cliff.
+ </div>
+ ),
+ },
+ {
+ prompt: 'Which cryptocurrencies will be accepted in the token launch?',
+ answer: <div>ETH.</div>,
+ },
+ {
+ prompt: 'When will 0x be live?',
+ answer: (
+ <div>
+ An alpha version of 0x has been live on our private test network since January 2017. Version 1.0
+ of 0x protocol will be deployed to the canonical Ethereum blockchain after a round of security
+ audits and prior to the public token launch. 0x will be using the 0x protocol during our token
+ launch.
+ </div>
+ ),
+ },
+ {
+ prompt: 'Where can I find a development roadmap?',
+ answer: (
+ <div>
+ Check it out{' '}
+ <a
+ href="https://drive.google.com/open?id=14IP1N8mt3YdsAoqYTyruMnZswpklUs3THyS1VXx71fo"
+ target="_blank"
+ >
+ here
+ </a>.
+ </div>
+ ),
+ },
+ ],
+ },
+ {
+ name: 'Team',
+ questions: [
+ {
+ prompt: 'Where is 0x based?',
+ answer: <div>0x was founded in SF and is driven by a diverse group of contributors.</div>,
+ },
+ {
+ prompt: 'How can I get involved?',
+ answer: (
+ <div>
+ Join our{' '}
+ <a href={constants.URL_ZEROEX_CHAT} target="_blank">
+ Rocket.chat
+ </a>! As an open source project, 0x will rely on a worldwide community of passionate developers
+ to contribute proposals, ideas and code.
+ </div>
+ ),
+ },
+ {
+ prompt: 'Why the name 0x?',
+ answer: (
+ <div>
+ 0x is the prefix for hexadecimal numeric constants including Ethereum addresses. In a more
+ abstract context, as the first open protocol for exchange 0x represents the beginning of the end
+ for the exchange industry’s rent seeking oligopoly: zero exchange.
+ </div>
+ ),
+ },
+ {
+ prompt: 'How do you pronounce 0x?',
+ answer: <div>We pronounce 0x as “zero-ex,” but you are free to pronounce it however you please.</div>,
+ },
+ ],
+ },
];
export class FAQ extends React.Component<FAQProps, FAQState> {
- public componentDidMount() {
- window.scrollTo(0, 0);
- }
- public render() {
- return (
- <div>
- <DocumentTitle title="0x FAQ" />
- <TopBar blockchainIsLoaded={false} location={this.props.location} />
- <div id="faq" className="mx-auto max-width-4 pt4" style={{ color: colors.grey800 }}>
- <h1 className="center" style={{ ...styles.thin }}>
- 0x FAQ
- </h1>
- <div className="sm-px2 md-px2 lg-px0 pb4">{this._renderSections()}</div>
- </div>
- <Footer />
- </div>
- );
- }
- private _renderSections() {
- const renderedSections = _.map(sections, (section: FAQSection, i: number) => {
- const isFirstSection = i === 0;
- return (
- <div key={section.name}>
- <h3>{section.name}</h3>
- {this._renderQuestions(section.questions, isFirstSection)}
- </div>
- );
- });
- return renderedSections;
- }
- private _renderQuestions(questions: FAQQuestion[], isFirstSection: boolean) {
- const renderedQuestions = _.map(questions, (question: FAQQuestion, i: number) => {
- const isFirstQuestion = i === 0;
- return (
- <Question
- key={question.prompt}
- prompt={question.prompt}
- answer={question.answer}
- shouldDisplayExpanded={isFirstSection && isFirstQuestion}
- />
- );
- });
- return renderedQuestions;
- }
+ public componentDidMount() {
+ window.scrollTo(0, 0);
+ }
+ public render() {
+ return (
+ <div>
+ <DocumentTitle title="0x FAQ" />
+ <TopBar blockchainIsLoaded={false} location={this.props.location} />
+ <div id="faq" className="mx-auto max-width-4 pt4" style={{ color: colors.grey800 }}>
+ <h1 className="center" style={{ ...styles.thin }}>
+ 0x FAQ
+ </h1>
+ <div className="sm-px2 md-px2 lg-px0 pb4">{this._renderSections()}</div>
+ </div>
+ <Footer />
+ </div>
+ );
+ }
+ private _renderSections() {
+ const renderedSections = _.map(sections, (section: FAQSection, i: number) => {
+ const isFirstSection = i === 0;
+ return (
+ <div key={section.name}>
+ <h3>{section.name}</h3>
+ {this._renderQuestions(section.questions, isFirstSection)}
+ </div>
+ );
+ });
+ return renderedSections;
+ }
+ private _renderQuestions(questions: FAQQuestion[], isFirstSection: boolean) {
+ const renderedQuestions = _.map(questions, (question: FAQQuestion, i: number) => {
+ const isFirstQuestion = i === 0;
+ return (
+ <Question
+ key={question.prompt}
+ prompt={question.prompt}
+ answer={question.answer}
+ shouldDisplayExpanded={isFirstSection && isFirstQuestion}
+ />
+ );
+ });
+ return renderedQuestions;
+ }
}
diff --git a/packages/website/ts/pages/faq/question.tsx b/packages/website/ts/pages/faq/question.tsx
index 58cf674ef..988c04bc9 100644
--- a/packages/website/ts/pages/faq/question.tsx
+++ b/packages/website/ts/pages/faq/question.tsx
@@ -4,48 +4,48 @@ import * as React from 'react';
import { colors } from 'ts/utils/colors';
export interface QuestionProps {
- prompt: string;
- answer: React.ReactNode;
- shouldDisplayExpanded: boolean;
+ prompt: string;
+ answer: React.ReactNode;
+ shouldDisplayExpanded: boolean;
}
interface QuestionState {
- isExpanded: boolean;
+ isExpanded: boolean;
}
export class Question extends React.Component<QuestionProps, QuestionState> {
- constructor(props: QuestionProps) {
- super(props);
- this.state = {
- isExpanded: props.shouldDisplayExpanded,
- };
- }
- public render() {
- return (
- <div className="py1">
- <Card
- initiallyExpanded={this.props.shouldDisplayExpanded}
- onExpandChange={this._onExchangeChange.bind(this)}
- >
- <CardHeader
- title={this.props.prompt}
- style={{
- borderBottom: this.state.isExpanded ? '1px solid rgba(0, 0, 0, 0.19)' : 'none',
- }}
- titleStyle={{ color: colors.darkerGrey }}
- actAsExpander={true}
- showExpandableButton={true}
- />
- <CardText expandable={true}>
- <div style={{ lineHeight: 1.4 }}>{this.props.answer}</div>
- </CardText>
- </Card>
- </div>
- );
- }
- private _onExchangeChange() {
- this.setState({
- isExpanded: !this.state.isExpanded,
- });
- }
+ constructor(props: QuestionProps) {
+ super(props);
+ this.state = {
+ isExpanded: props.shouldDisplayExpanded,
+ };
+ }
+ public render() {
+ return (
+ <div className="py1">
+ <Card
+ initiallyExpanded={this.props.shouldDisplayExpanded}
+ onExpandChange={this._onExchangeChange.bind(this)}
+ >
+ <CardHeader
+ title={this.props.prompt}
+ style={{
+ borderBottom: this.state.isExpanded ? '1px solid rgba(0, 0, 0, 0.19)' : 'none',
+ }}
+ titleStyle={{ color: colors.darkerGrey }}
+ actAsExpander={true}
+ showExpandableButton={true}
+ />
+ <CardText expandable={true}>
+ <div style={{ lineHeight: 1.4 }}>{this.props.answer}</div>
+ </CardText>
+ </Card>
+ </div>
+ );
+ }
+ private _onExchangeChange() {
+ this.setState({
+ isExpanded: !this.state.isExpanded,
+ });
+ }
}
diff --git a/packages/website/ts/pages/landing/landing.tsx b/packages/website/ts/pages/landing/landing.tsx
index 742d94a0f..ca76497df 100644
--- a/packages/website/ts/pages/landing/landing.tsx
+++ b/packages/website/ts/pages/landing/landing.tsx
@@ -11,755 +11,755 @@ import { constants } from 'ts/utils/constants';
import { utils } from 'ts/utils/utils';
interface BoxContent {
- title: string;
- description: string;
- imageUrl: string;
- classNames: string;
+ title: string;
+ description: string;
+ imageUrl: string;
+ classNames: string;
}
interface AssetType {
- title: string;
- imageUrl: string;
- style?: React.CSSProperties;
+ title: string;
+ imageUrl: string;
+ style?: React.CSSProperties;
}
interface UseCase {
- imageUrl: string;
- type: string;
- description: string;
- classNames: string;
- style?: React.CSSProperties;
- projectIconUrls: string[];
+ imageUrl: string;
+ type: string;
+ description: string;
+ classNames: string;
+ style?: React.CSSProperties;
+ projectIconUrls: string[];
}
interface Project {
- logoFileName: string;
- projectUrl: string;
+ logoFileName: string;
+ projectUrl: string;
}
const THROTTLE_TIMEOUT = 100;
const boxContents: BoxContent[] = [
- {
- title: 'Trustless exchange',
- description:
- "Built on Ethereum's distributed network with no centralized \
+ {
+ title: 'Trustless exchange',
+ description:
+ "Built on Ethereum's distributed network with no centralized \
point of failure and no down time, each trade is settled atomically \
and without counterparty risk.",
- imageUrl: '/images/landing/distributed_network.png',
- classNames: '',
- },
- {
- title: 'Shared liquidity',
- description:
- 'By sharing a standard API, relayers can easily aggregate liquidity pools, \
+ imageUrl: '/images/landing/distributed_network.png',
+ classNames: '',
+ },
+ {
+ title: 'Shared liquidity',
+ description:
+ 'By sharing a standard API, relayers can easily aggregate liquidity pools, \
creating network effects around liquidity that compound as more relayers come online.',
- imageUrl: '/images/landing/liquidity.png',
- classNames: 'mx-auto',
- },
- {
- title: 'Open source',
- description:
- '0x is open source, permissionless and free to use. Trade directly with a known \
+ imageUrl: '/images/landing/liquidity.png',
+ classNames: 'mx-auto',
+ },
+ {
+ title: 'Open source',
+ description:
+ '0x is open source, permissionless and free to use. Trade directly with a known \
counterparty for free or pay a relayer some ZRX tokens to access their liquidity \
pool.',
- imageUrl: '/images/landing/open_source.png',
- classNames: 'right',
- },
+ imageUrl: '/images/landing/open_source.png',
+ classNames: 'right',
+ },
];
const projects: Project[] = [
- {
- logoFileName: 'ethfinex-top.png',
- projectUrl: constants.PROJECT_URL_ETHFINEX,
- },
- {
- logoFileName: 'radar_relay_top.png',
- projectUrl: constants.PROJECT_URL_RADAR_RELAY,
- },
- {
- logoFileName: 'paradex_top.png',
- projectUrl: constants.PROJECT_URL_PARADEX,
- },
- {
- logoFileName: 'the_ocean.png',
- projectUrl: constants.PROJECT_URL_0CEAN,
- },
- {
- logoFileName: 'dydx.png',
- projectUrl: constants.PROJECT_URL_DYDX,
- },
- {
- logoFileName: 'melonport.png',
- projectUrl: constants.PROJECT_URL_MELONPORT,
- },
- {
- logoFileName: 'maker.png',
- projectUrl: constants.PROJECT_URL_MAKER,
- },
- {
- logoFileName: 'dharma.png',
- projectUrl: constants.PROJECT_URL_DHARMA,
- },
- {
- logoFileName: 'lendroid.png',
- projectUrl: constants.PROJECT_URL_LENDROID,
- },
- {
- logoFileName: 'district0x.png',
- projectUrl: constants.PROJECT_URL_DISTRICT_0X,
- },
- {
- logoFileName: 'aragon.png',
- projectUrl: constants.PROJECT_URL_ARAGON,
- },
- {
- logoFileName: 'blocknet.png',
- projectUrl: constants.PROJECT_URL_BLOCKNET,
- },
- {
- logoFileName: 'status.png',
- projectUrl: constants.PROJECT_URL_STATUS,
- },
- {
- logoFileName: 'augur.png',
- projectUrl: constants.PROJECT_URL_AUGUR,
- },
- {
- logoFileName: 'anx.png',
- projectUrl: constants.PROJECT_URL_OPEN_ANX,
- },
- {
- logoFileName: 'auctus.png',
- projectUrl: constants.PROJECT_URL_AUCTUS,
- },
+ {
+ logoFileName: 'ethfinex-top.png',
+ projectUrl: constants.PROJECT_URL_ETHFINEX,
+ },
+ {
+ logoFileName: 'radar_relay_top.png',
+ projectUrl: constants.PROJECT_URL_RADAR_RELAY,
+ },
+ {
+ logoFileName: 'paradex_top.png',
+ projectUrl: constants.PROJECT_URL_PARADEX,
+ },
+ {
+ logoFileName: 'the_ocean.png',
+ projectUrl: constants.PROJECT_URL_0CEAN,
+ },
+ {
+ logoFileName: 'dydx.png',
+ projectUrl: constants.PROJECT_URL_DYDX,
+ },
+ {
+ logoFileName: 'melonport.png',
+ projectUrl: constants.PROJECT_URL_MELONPORT,
+ },
+ {
+ logoFileName: 'maker.png',
+ projectUrl: constants.PROJECT_URL_MAKER,
+ },
+ {
+ logoFileName: 'dharma.png',
+ projectUrl: constants.PROJECT_URL_DHARMA,
+ },
+ {
+ logoFileName: 'lendroid.png',
+ projectUrl: constants.PROJECT_URL_LENDROID,
+ },
+ {
+ logoFileName: 'district0x.png',
+ projectUrl: constants.PROJECT_URL_DISTRICT_0X,
+ },
+ {
+ logoFileName: 'aragon.png',
+ projectUrl: constants.PROJECT_URL_ARAGON,
+ },
+ {
+ logoFileName: 'blocknet.png',
+ projectUrl: constants.PROJECT_URL_BLOCKNET,
+ },
+ {
+ logoFileName: 'status.png',
+ projectUrl: constants.PROJECT_URL_STATUS,
+ },
+ {
+ logoFileName: 'augur.png',
+ projectUrl: constants.PROJECT_URL_AUGUR,
+ },
+ {
+ logoFileName: 'anx.png',
+ projectUrl: constants.PROJECT_URL_OPEN_ANX,
+ },
+ {
+ logoFileName: 'auctus.png',
+ projectUrl: constants.PROJECT_URL_AUCTUS,
+ },
];
export interface LandingProps {
- location: Location;
+ location: Location;
}
interface LandingState {
- screenWidth: ScreenWidths;
+ screenWidth: ScreenWidths;
}
export class Landing extends React.Component<LandingProps, LandingState> {
- private _throttledScreenWidthUpdate: () => void;
- constructor(props: LandingProps) {
- super(props);
- this.state = {
- screenWidth: utils.getScreenWidth(),
- };
- this._throttledScreenWidthUpdate = _.throttle(this._updateScreenWidth.bind(this), THROTTLE_TIMEOUT);
- }
- public componentDidMount() {
- window.addEventListener('resize', this._throttledScreenWidthUpdate);
- window.scrollTo(0, 0);
- }
- public componentWillUnmount() {
- window.removeEventListener('resize', this._throttledScreenWidthUpdate);
- }
- public render() {
- return (
- <div id="landing" className="clearfix" style={{ color: colors.grey500 }}>
- <DocumentTitle title="0x Protocol" />
- <TopBar
- blockchainIsLoaded={false}
- location={this.props.location}
- isNightVersion={true}
- style={{ backgroundColor: colors.heroGrey, position: 'relative' }}
- />
- {this._renderHero()}
- {this._renderProjects()}
- {this._renderTokenizationSection()}
- {this._renderProtocolSection()}
- {this._renderInfoBoxes()}
- {this._renderBuildingBlocksSection()}
- {this._renderUseCases()}
- {this._renderCallToAction()}
- <Footer />
- </div>
- );
- }
- private _renderHero() {
- const isSmallScreen = this.state.screenWidth === ScreenWidths.Sm;
- const buttonLabelStyle: React.CSSProperties = {
- textTransform: 'none',
- fontSize: isSmallScreen ? 12 : 14,
- fontWeight: 400,
- };
- const lightButtonStyle: React.CSSProperties = {
- borderRadius: 6,
- border: '1px solid #D8D8D8',
- lineHeight: '33px',
- height: 38,
- };
- const left = 'col lg-col-7 md-col-7 col-12 lg-pt4 md-pt4 sm-pt0 mt1 lg-pl4 md-pl4 sm-pl0 sm-px3 sm-center';
- return (
- <div className="clearfix py4" style={{ backgroundColor: colors.heroGrey }}>
- <div className="mx-auto max-width-4 clearfix">
- <div className="lg-pt4 md-pt4 sm-pt2 lg-pb4 md-pb4 lg-my4 md-my4 sm-mt2 sm-mb4 clearfix">
- <div className="col lg-col-5 md-col-5 col-12 sm-center">
- <img src="/images/landing/hero_chip_image.png" height={isSmallScreen ? 300 : 395} />
- </div>
- <div className={left} style={{ color: colors.white }}>
- <div style={{ paddingLeft: isSmallScreen ? 0 : 12 }}>
- <div
- className="sm-pb2"
- style={{
- fontFamily: 'Roboto Mono',
- fontSize: isSmallScreen ? 26 : 34,
- }}
- >
- Powering decentralized exchange
- </div>
- <div
- className="pt2 h5 sm-mx-auto"
- style={{
- maxWidth: 446,
- fontFamily: 'Roboto Mono',
- lineHeight: 1.7,
- fontWeight: 300,
- }}
- >
- 0x is an open, permissionless protocol allowing for ERC20 tokens to be traded on the
- Ethereum blockchain.
- </div>
- <div className="pt3 clearfix sm-mx-auto" style={{ maxWidth: 342 }}>
- <div className="lg-pr2 md-pr2 col col-6 sm-center">
- <Link to={WebsitePaths.ZeroExJs} className="text-decoration-none">
- <RaisedButton
- style={{ borderRadius: 6, minWidth: 157.36 }}
- buttonStyle={{ borderRadius: 6 }}
- labelStyle={buttonLabelStyle}
- label="Build on 0x"
- onClick={_.noop}
- />
- </Link>
- </div>
- <div className="col col-6 sm-center">
- <a
- href={constants.URL_ZEROEX_CHAT}
- target="_blank"
- className="text-decoration-none"
- >
- <RaisedButton
- style={{ borderRadius: 6, minWidth: 150 }}
- buttonStyle={lightButtonStyle}
- labelColor="white"
- backgroundColor={colors.heroGrey}
- labelStyle={buttonLabelStyle}
- label="Join the community"
- onClick={_.noop}
- />
- </a>
- </div>
- </div>
- </div>
- </div>
- </div>
- </div>
- </div>
- );
- }
- private _renderProjects() {
- const isSmallScreen = this.state.screenWidth === ScreenWidths.Sm;
- const isMediumScreen = this.state.screenWidth === ScreenWidths.Md;
- const projectList = _.map(projects, (project: Project, i: number) => {
- const colWidth = isSmallScreen ? 3 : isMediumScreen ? 4 : 2 - i % 2;
- return (
- <div key={`project-${project.logoFileName}`} className={`col col-${colWidth} center`}>
- <div>
- <a href={project.projectUrl} target="_blank" className="text-decoration-none">
- <img
- src={`/images/landing/project_logos/${project.logoFileName}`}
- height={isSmallScreen ? 60 : 92}
- />
- </a>
- </div>
- </div>
- );
- });
- const titleStyle: React.CSSProperties = {
- fontFamily: 'Roboto Mono',
- color: colors.grey,
- textTransform: 'uppercase',
- fontWeight: 300,
- letterSpacing: 3,
- };
- return (
- <div className="clearfix py4" style={{ backgroundColor: colors.projectsGrey }}>
- <div className="mx-auto max-width-4 clearfix sm-px3">
- <div className="h4 pb3 md-pl3 sm-pl2" style={titleStyle}>
- Projects building on 0x
- </div>
- <div className="clearfix">{projectList}</div>
- <div
- className="pt3 mx-auto center"
- style={{
- color: colors.landingLinkGrey,
- fontFamily: 'Roboto Mono',
- maxWidth: 300,
- fontSize: 14,
- }}
- >
- view the{' '}
- <Link
- to={`${WebsitePaths.Wiki}#List-of-Projects-Using-0x-Protocol`}
- className="text-decoration-none underline"
- style={{ color: colors.landingLinkGrey }}
- >
- full list
- </Link>
- </div>
- </div>
- </div>
- );
- }
- private _renderTokenizationSection() {
- const isSmallScreen = this.state.screenWidth === ScreenWidths.Sm;
- return (
- <div className="clearfix lg-py4 md-py4 sm-pb4 sm-pt2" style={{ backgroundColor: colors.grey100 }}>
- <div className="mx-auto max-width-4 py4 clearfix">
- {isSmallScreen && this._renderTokenCloud()}
- <div className="col lg-col-6 md-col-6 col-12">
- <div className="mx-auto" style={{ maxWidth: 385, paddingTop: 7 }}>
- <div className="lg-h1 md-h1 sm-h2 sm-center sm-pt3" style={{ fontFamily: 'Roboto Mono' }}>
- The world's value is becoming tokenized
- </div>
- <div
- className="pb2 lg-pt2 md-pt2 sm-pt3 sm-px3 h5 sm-center"
- style={{ fontFamily: 'Roboto Mono', lineHeight: 1.7 }}
- >
- {isSmallScreen ? (
- <span>
- The Ethereum blockchain is an open, borderless financial system that represents
- a wide variety of assets as cryptographic tokens. In the future, most digital
- assets and goods will be tokenized.
- </span>
- ) : (
- <div>
- <div>
- The Ethereum blockchain is an open, borderless financial system that
- represents
- </div>
- <div>
- a wide variety of assets as cryptographic tokens. In the future, most
- digital assets and goods will be tokenized.
- </div>
- </div>
- )}
- </div>
- <div className="flex pt1 sm-px3">{this._renderAssetTypes()}</div>
- </div>
- </div>
- {!isSmallScreen && this._renderTokenCloud()}
- </div>
- </div>
- );
- }
- private _renderProtocolSection() {
- const isSmallScreen = this.state.screenWidth === ScreenWidths.Sm;
- return (
- <div className="clearfix lg-py4 md-py4 sm-pt4" style={{ backgroundColor: colors.heroGrey }}>
- <div className="mx-auto max-width-4 lg-py4 md-py4 sm-pt4 clearfix">
- <div className="col lg-col-6 md-col-6 col-12 sm-center">
- <img src="/images/landing/relayer_diagram.png" height={isSmallScreen ? 326 : 426} />
- </div>
- <div
- className="col lg-col-6 md-col-6 col-12 lg-pr3 md-pr3 sm-mx-auto"
- style={{
- color: colors.beigeWhite,
- paddingTop: 8,
- maxWidth: isSmallScreen ? 'none' : 445,
- }}
- >
- <div className="lg-h1 md-h1 sm-h2 pb1 sm-pt3 sm-center" style={{ fontFamily: 'Roboto Mono' }}>
- <div>Off-chain order relay</div>
- <div>On-chain settlement</div>
- </div>
- <div
- className="pb2 pt2 h5 sm-center sm-px3 sm-mx-auto"
- style={{
- fontFamily: 'Roboto Mono',
- lineHeight: 1.7,
- fontWeight: 300,
- maxWidth: 445,
- }}
- >
- In 0x protocol, orders are transported off-chain, massively reducing gas costs and
- eliminating blockchain bloat. Relayers help broadcast orders and collect a fee each time
- they facilitate a trade. Anyone can build a relayer.
- </div>
- <div
- className="pt3 sm-mx-auto sm-px3"
- style={{
- color: colors.landingLinkGrey,
- maxWidth: isSmallScreen ? 412 : 'none',
- }}
- >
- <div className="flex" style={{ fontSize: 18 }}>
- <div
- className="lg-h4 md-h4 sm-h5"
- style={{
- letterSpacing: isSmallScreen ? 1 : 3,
- fontFamily: 'Roboto Mono',
- }}
- >
- RELAYERS BUILDING ON 0X
- </div>
- <div className="h5" style={{ marginLeft: isSmallScreen ? 26 : 49 }}>
- <Link
- to={`${WebsitePaths.Wiki}#List-of-Projects-Using-0x-Protocol`}
- className="text-decoration-none underline"
- style={{
- color: colors.landingLinkGrey,
- fontFamily: 'Roboto Mono',
- }}
- >
- view all
- </Link>
- </div>
- </div>
- <div className="lg-flex md-flex sm-clearfix pt3" style={{ opacity: 0.4 }}>
- <div className="col col-4 sm-center">
- <img
- src="/images/landing/ethfinex.png"
- style={{ height: isSmallScreen ? 85 : 107 }}
- />
- </div>
- <div className="col col-4 center">
- <img
- src="/images/landing/radar_relay.png"
- style={{ height: isSmallScreen ? 85 : 107 }}
- />
- </div>
- <div className="col col-4 sm-center" style={{ textAlign: 'right' }}>
- <img
- src="/images/landing/paradex.png"
- style={{ height: isSmallScreen ? 85 : 107 }}
- />
- </div>
- </div>
- </div>
- </div>
- </div>
- </div>
- );
- }
- private _renderBuildingBlocksSection() {
- const isSmallScreen = this.state.screenWidth === ScreenWidths.Sm;
- const descriptionStyle: React.CSSProperties = {
- fontFamily: 'Roboto Mono',
- lineHeight: isSmallScreen ? 1.5 : 2,
- fontWeight: 300,
- fontSize: 15,
- maxWidth: isSmallScreen ? 375 : 'none',
- };
- const callToActionStyle: React.CSSProperties = {
- fontFamily: 'Roboto Mono',
- fontSize: 15,
- fontWeight: 300,
- maxWidth: isSmallScreen ? 375 : 441,
- };
- return (
- <div className="clearfix lg-pt4 md-pt4" style={{ backgroundColor: colors.heroGrey }}>
- <div className="mx-auto max-width-4 lg-pt4 md-pt4 lg-mb4 md-mb4 sm-mb2 clearfix">
- {isSmallScreen && this._renderBlockChipImage()}
- <div
- className="col lg-col-6 md-col-6 col-12 lg-pr3 md-pr3 sm-px3"
- style={{ color: colors.beigeWhite }}
- >
- <div
- className="pb1 lg-pt4 md-pt4 sm-pt3 lg-h1 md-h1 sm-h2 sm-px3 sm-center"
- style={{ fontFamily: 'Roboto Mono' }}
- >
- A building block for dApps
- </div>
- <div className="pb3 pt2 sm-mx-auto sm-center" style={descriptionStyle}>
- 0x protocol is a pluggable building block for dApps that require exchange functionality.
- Join the many developers that are already using 0x in their web applications and smart
- contracts.
- </div>
- <div className="sm-mx-auto sm-center" style={callToActionStyle}>
- Learn how in our{' '}
- <Link
- to={WebsitePaths.ZeroExJs}
- className="text-decoration-none underline"
- style={{ color: colors.beigeWhite, fontFamily: 'Roboto Mono' }}
- >
- 0x.js
- </Link>{' '}
- and{' '}
- <Link
- to={WebsitePaths.SmartContracts}
- className="text-decoration-none underline"
- style={{ color: colors.beigeWhite, fontFamily: 'Roboto Mono' }}
- >
- smart contract
- </Link>{' '}
- docs
- </div>
- </div>
- {!isSmallScreen && this._renderBlockChipImage()}
- </div>
- </div>
- );
- }
- private _renderBlockChipImage() {
- const isSmallScreen = this.state.screenWidth === ScreenWidths.Sm;
- return (
- <div className="col lg-col-6 md-col-6 col-12 sm-center">
- <img src="/images/landing/0x_chips.png" height={isSmallScreen ? 240 : 368} />
- </div>
- );
- }
- private _renderTokenCloud() {
- const isSmallScreen = this.state.screenWidth === ScreenWidths.Sm;
- return (
- <div className="col lg-col-6 md-col-6 col-12 center">
- <img src="/images/landing/tokenized_world.png" height={isSmallScreen ? 280 : 364.5} />
- </div>
- );
- }
- private _renderAssetTypes() {
- const isSmallScreen = this.state.screenWidth === ScreenWidths.Sm;
- const assetTypes: AssetType[] = [
- {
- title: 'Currency',
- imageUrl: '/images/landing/currency.png',
- },
- {
- title: 'Traditional assets',
- imageUrl: '/images/landing/stocks.png',
- style: {
- paddingLeft: isSmallScreen ? 41 : 56,
- paddingRight: isSmallScreen ? 41 : 56,
- },
- },
- {
- title: 'Digital goods',
- imageUrl: '/images/landing/digital_goods.png',
- },
- ];
- const assets = _.map(assetTypes, (assetType: AssetType) => {
- const style = _.isUndefined(assetType.style) ? {} : assetType.style;
- return (
- <div key={`asset-${assetType.title}`} className="center" style={{ opacity: 0.8, ...style }}>
- <div>
- <img src={assetType.imageUrl} height="80" />
- </div>
- <div
- style={{
- fontFamily: 'Roboto Mono',
- fontSize: 13.5,
- fontWeight: 400,
- opacity: 0.75,
- }}
- >
- {assetType.title}
- </div>
- </div>
- );
- });
- return assets;
- }
- private _renderInfoBoxes() {
- const isSmallScreen = this.state.screenWidth === ScreenWidths.Sm;
- const boxStyle: React.CSSProperties = {
- maxWidth: 252,
- height: 386,
- backgroundColor: colors.grey50,
- borderRadius: 5,
- padding: '10px 24px 24px',
- };
- const boxes = _.map(boxContents, (boxContent: BoxContent) => {
- return (
- <div key={`box-${boxContent.title}`} className="col lg-col-4 md-col-4 col-12 sm-pb4">
- <div className={`center sm-mx-auto ${!isSmallScreen && boxContent.classNames}`} style={boxStyle}>
- <div>
- <img src={boxContent.imageUrl} style={{ height: 210 }} />
- </div>
- <div className="h3" style={{ color: 'black', fontFamily: 'Roboto Mono' }}>
- {boxContent.title}
- </div>
- <div className="pt2 pb2" style={{ fontFamily: 'Roboto Mono', fontSize: 14 }}>
- {boxContent.description}
- </div>
- </div>
- </div>
- );
- });
- return (
- <div className="clearfix" style={{ backgroundColor: colors.heroGrey }}>
- <div className="mx-auto py4 sm-mt2 clearfix" style={{ maxWidth: '60em' }}>
- {boxes}
- </div>
- </div>
- );
- }
- private _renderUseCases() {
- const isSmallScreen = this.state.screenWidth === ScreenWidths.Sm;
+ private _throttledScreenWidthUpdate: () => void;
+ constructor(props: LandingProps) {
+ super(props);
+ this.state = {
+ screenWidth: utils.getScreenWidth(),
+ };
+ this._throttledScreenWidthUpdate = _.throttle(this._updateScreenWidth.bind(this), THROTTLE_TIMEOUT);
+ }
+ public componentDidMount() {
+ window.addEventListener('resize', this._throttledScreenWidthUpdate);
+ window.scrollTo(0, 0);
+ }
+ public componentWillUnmount() {
+ window.removeEventListener('resize', this._throttledScreenWidthUpdate);
+ }
+ public render() {
+ return (
+ <div id="landing" className="clearfix" style={{ color: colors.grey500 }}>
+ <DocumentTitle title="0x Protocol" />
+ <TopBar
+ blockchainIsLoaded={false}
+ location={this.props.location}
+ isNightVersion={true}
+ style={{ backgroundColor: colors.heroGrey, position: 'relative' }}
+ />
+ {this._renderHero()}
+ {this._renderProjects()}
+ {this._renderTokenizationSection()}
+ {this._renderProtocolSection()}
+ {this._renderInfoBoxes()}
+ {this._renderBuildingBlocksSection()}
+ {this._renderUseCases()}
+ {this._renderCallToAction()}
+ <Footer />
+ </div>
+ );
+ }
+ private _renderHero() {
+ const isSmallScreen = this.state.screenWidth === ScreenWidths.Sm;
+ const buttonLabelStyle: React.CSSProperties = {
+ textTransform: 'none',
+ fontSize: isSmallScreen ? 12 : 14,
+ fontWeight: 400,
+ };
+ const lightButtonStyle: React.CSSProperties = {
+ borderRadius: 6,
+ border: '1px solid #D8D8D8',
+ lineHeight: '33px',
+ height: 38,
+ };
+ const left = 'col lg-col-7 md-col-7 col-12 lg-pt4 md-pt4 sm-pt0 mt1 lg-pl4 md-pl4 sm-pl0 sm-px3 sm-center';
+ return (
+ <div className="clearfix py4" style={{ backgroundColor: colors.heroGrey }}>
+ <div className="mx-auto max-width-4 clearfix">
+ <div className="lg-pt4 md-pt4 sm-pt2 lg-pb4 md-pb4 lg-my4 md-my4 sm-mt2 sm-mb4 clearfix">
+ <div className="col lg-col-5 md-col-5 col-12 sm-center">
+ <img src="/images/landing/hero_chip_image.png" height={isSmallScreen ? 300 : 395} />
+ </div>
+ <div className={left} style={{ color: colors.white }}>
+ <div style={{ paddingLeft: isSmallScreen ? 0 : 12 }}>
+ <div
+ className="sm-pb2"
+ style={{
+ fontFamily: 'Roboto Mono',
+ fontSize: isSmallScreen ? 26 : 34,
+ }}
+ >
+ Powering decentralized exchange
+ </div>
+ <div
+ className="pt2 h5 sm-mx-auto"
+ style={{
+ maxWidth: 446,
+ fontFamily: 'Roboto Mono',
+ lineHeight: 1.7,
+ fontWeight: 300,
+ }}
+ >
+ 0x is an open, permissionless protocol allowing for ERC20 tokens to be traded on the
+ Ethereum blockchain.
+ </div>
+ <div className="pt3 clearfix sm-mx-auto" style={{ maxWidth: 342 }}>
+ <div className="lg-pr2 md-pr2 col col-6 sm-center">
+ <Link to={WebsitePaths.ZeroExJs} className="text-decoration-none">
+ <RaisedButton
+ style={{ borderRadius: 6, minWidth: 157.36 }}
+ buttonStyle={{ borderRadius: 6 }}
+ labelStyle={buttonLabelStyle}
+ label="Build on 0x"
+ onClick={_.noop}
+ />
+ </Link>
+ </div>
+ <div className="col col-6 sm-center">
+ <a
+ href={constants.URL_ZEROEX_CHAT}
+ target="_blank"
+ className="text-decoration-none"
+ >
+ <RaisedButton
+ style={{ borderRadius: 6, minWidth: 150 }}
+ buttonStyle={lightButtonStyle}
+ labelColor="white"
+ backgroundColor={colors.heroGrey}
+ labelStyle={buttonLabelStyle}
+ label="Join the community"
+ onClick={_.noop}
+ />
+ </a>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ );
+ }
+ private _renderProjects() {
+ const isSmallScreen = this.state.screenWidth === ScreenWidths.Sm;
+ const isMediumScreen = this.state.screenWidth === ScreenWidths.Md;
+ const projectList = _.map(projects, (project: Project, i: number) => {
+ const colWidth = isSmallScreen ? 3 : isMediumScreen ? 4 : 2 - i % 2;
+ return (
+ <div key={`project-${project.logoFileName}`} className={`col col-${colWidth} center`}>
+ <div>
+ <a href={project.projectUrl} target="_blank" className="text-decoration-none">
+ <img
+ src={`/images/landing/project_logos/${project.logoFileName}`}
+ height={isSmallScreen ? 60 : 92}
+ />
+ </a>
+ </div>
+ </div>
+ );
+ });
+ const titleStyle: React.CSSProperties = {
+ fontFamily: 'Roboto Mono',
+ color: colors.grey,
+ textTransform: 'uppercase',
+ fontWeight: 300,
+ letterSpacing: 3,
+ };
+ return (
+ <div className="clearfix py4" style={{ backgroundColor: colors.projectsGrey }}>
+ <div className="mx-auto max-width-4 clearfix sm-px3">
+ <div className="h4 pb3 md-pl3 sm-pl2" style={titleStyle}>
+ Projects building on 0x
+ </div>
+ <div className="clearfix">{projectList}</div>
+ <div
+ className="pt3 mx-auto center"
+ style={{
+ color: colors.landingLinkGrey,
+ fontFamily: 'Roboto Mono',
+ maxWidth: 300,
+ fontSize: 14,
+ }}
+ >
+ view the{' '}
+ <Link
+ to={`${WebsitePaths.Wiki}#List-of-Projects-Using-0x-Protocol`}
+ className="text-decoration-none underline"
+ style={{ color: colors.landingLinkGrey }}
+ >
+ full list
+ </Link>
+ </div>
+ </div>
+ </div>
+ );
+ }
+ private _renderTokenizationSection() {
+ const isSmallScreen = this.state.screenWidth === ScreenWidths.Sm;
+ return (
+ <div className="clearfix lg-py4 md-py4 sm-pb4 sm-pt2" style={{ backgroundColor: colors.grey100 }}>
+ <div className="mx-auto max-width-4 py4 clearfix">
+ {isSmallScreen && this._renderTokenCloud()}
+ <div className="col lg-col-6 md-col-6 col-12">
+ <div className="mx-auto" style={{ maxWidth: 385, paddingTop: 7 }}>
+ <div className="lg-h1 md-h1 sm-h2 sm-center sm-pt3" style={{ fontFamily: 'Roboto Mono' }}>
+ The world's value is becoming tokenized
+ </div>
+ <div
+ className="pb2 lg-pt2 md-pt2 sm-pt3 sm-px3 h5 sm-center"
+ style={{ fontFamily: 'Roboto Mono', lineHeight: 1.7 }}
+ >
+ {isSmallScreen ? (
+ <span>
+ The Ethereum blockchain is an open, borderless financial system that represents
+ a wide variety of assets as cryptographic tokens. In the future, most digital
+ assets and goods will be tokenized.
+ </span>
+ ) : (
+ <div>
+ <div>
+ The Ethereum blockchain is an open, borderless financial system that
+ represents
+ </div>
+ <div>
+ a wide variety of assets as cryptographic tokens. In the future, most
+ digital assets and goods will be tokenized.
+ </div>
+ </div>
+ )}
+ </div>
+ <div className="flex pt1 sm-px3">{this._renderAssetTypes()}</div>
+ </div>
+ </div>
+ {!isSmallScreen && this._renderTokenCloud()}
+ </div>
+ </div>
+ );
+ }
+ private _renderProtocolSection() {
+ const isSmallScreen = this.state.screenWidth === ScreenWidths.Sm;
+ return (
+ <div className="clearfix lg-py4 md-py4 sm-pt4" style={{ backgroundColor: colors.heroGrey }}>
+ <div className="mx-auto max-width-4 lg-py4 md-py4 sm-pt4 clearfix">
+ <div className="col lg-col-6 md-col-6 col-12 sm-center">
+ <img src="/images/landing/relayer_diagram.png" height={isSmallScreen ? 326 : 426} />
+ </div>
+ <div
+ className="col lg-col-6 md-col-6 col-12 lg-pr3 md-pr3 sm-mx-auto"
+ style={{
+ color: colors.beigeWhite,
+ paddingTop: 8,
+ maxWidth: isSmallScreen ? 'none' : 445,
+ }}
+ >
+ <div className="lg-h1 md-h1 sm-h2 pb1 sm-pt3 sm-center" style={{ fontFamily: 'Roboto Mono' }}>
+ <div>Off-chain order relay</div>
+ <div>On-chain settlement</div>
+ </div>
+ <div
+ className="pb2 pt2 h5 sm-center sm-px3 sm-mx-auto"
+ style={{
+ fontFamily: 'Roboto Mono',
+ lineHeight: 1.7,
+ fontWeight: 300,
+ maxWidth: 445,
+ }}
+ >
+ In 0x protocol, orders are transported off-chain, massively reducing gas costs and
+ eliminating blockchain bloat. Relayers help broadcast orders and collect a fee each time
+ they facilitate a trade. Anyone can build a relayer.
+ </div>
+ <div
+ className="pt3 sm-mx-auto sm-px3"
+ style={{
+ color: colors.landingLinkGrey,
+ maxWidth: isSmallScreen ? 412 : 'none',
+ }}
+ >
+ <div className="flex" style={{ fontSize: 18 }}>
+ <div
+ className="lg-h4 md-h4 sm-h5"
+ style={{
+ letterSpacing: isSmallScreen ? 1 : 3,
+ fontFamily: 'Roboto Mono',
+ }}
+ >
+ RELAYERS BUILDING ON 0X
+ </div>
+ <div className="h5" style={{ marginLeft: isSmallScreen ? 26 : 49 }}>
+ <Link
+ to={`${WebsitePaths.Wiki}#List-of-Projects-Using-0x-Protocol`}
+ className="text-decoration-none underline"
+ style={{
+ color: colors.landingLinkGrey,
+ fontFamily: 'Roboto Mono',
+ }}
+ >
+ view all
+ </Link>
+ </div>
+ </div>
+ <div className="lg-flex md-flex sm-clearfix pt3" style={{ opacity: 0.4 }}>
+ <div className="col col-4 sm-center">
+ <img
+ src="/images/landing/ethfinex.png"
+ style={{ height: isSmallScreen ? 85 : 107 }}
+ />
+ </div>
+ <div className="col col-4 center">
+ <img
+ src="/images/landing/radar_relay.png"
+ style={{ height: isSmallScreen ? 85 : 107 }}
+ />
+ </div>
+ <div className="col col-4 sm-center" style={{ textAlign: 'right' }}>
+ <img
+ src="/images/landing/paradex.png"
+ style={{ height: isSmallScreen ? 85 : 107 }}
+ />
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ );
+ }
+ private _renderBuildingBlocksSection() {
+ const isSmallScreen = this.state.screenWidth === ScreenWidths.Sm;
+ const descriptionStyle: React.CSSProperties = {
+ fontFamily: 'Roboto Mono',
+ lineHeight: isSmallScreen ? 1.5 : 2,
+ fontWeight: 300,
+ fontSize: 15,
+ maxWidth: isSmallScreen ? 375 : 'none',
+ };
+ const callToActionStyle: React.CSSProperties = {
+ fontFamily: 'Roboto Mono',
+ fontSize: 15,
+ fontWeight: 300,
+ maxWidth: isSmallScreen ? 375 : 441,
+ };
+ return (
+ <div className="clearfix lg-pt4 md-pt4" style={{ backgroundColor: colors.heroGrey }}>
+ <div className="mx-auto max-width-4 lg-pt4 md-pt4 lg-mb4 md-mb4 sm-mb2 clearfix">
+ {isSmallScreen && this._renderBlockChipImage()}
+ <div
+ className="col lg-col-6 md-col-6 col-12 lg-pr3 md-pr3 sm-px3"
+ style={{ color: colors.beigeWhite }}
+ >
+ <div
+ className="pb1 lg-pt4 md-pt4 sm-pt3 lg-h1 md-h1 sm-h2 sm-px3 sm-center"
+ style={{ fontFamily: 'Roboto Mono' }}
+ >
+ A building block for dApps
+ </div>
+ <div className="pb3 pt2 sm-mx-auto sm-center" style={descriptionStyle}>
+ 0x protocol is a pluggable building block for dApps that require exchange functionality.
+ Join the many developers that are already using 0x in their web applications and smart
+ contracts.
+ </div>
+ <div className="sm-mx-auto sm-center" style={callToActionStyle}>
+ Learn how in our{' '}
+ <Link
+ to={WebsitePaths.ZeroExJs}
+ className="text-decoration-none underline"
+ style={{ color: colors.beigeWhite, fontFamily: 'Roboto Mono' }}
+ >
+ 0x.js
+ </Link>{' '}
+ and{' '}
+ <Link
+ to={WebsitePaths.SmartContracts}
+ className="text-decoration-none underline"
+ style={{ color: colors.beigeWhite, fontFamily: 'Roboto Mono' }}
+ >
+ smart contract
+ </Link>{' '}
+ docs
+ </div>
+ </div>
+ {!isSmallScreen && this._renderBlockChipImage()}
+ </div>
+ </div>
+ );
+ }
+ private _renderBlockChipImage() {
+ const isSmallScreen = this.state.screenWidth === ScreenWidths.Sm;
+ return (
+ <div className="col lg-col-6 md-col-6 col-12 sm-center">
+ <img src="/images/landing/0x_chips.png" height={isSmallScreen ? 240 : 368} />
+ </div>
+ );
+ }
+ private _renderTokenCloud() {
+ const isSmallScreen = this.state.screenWidth === ScreenWidths.Sm;
+ return (
+ <div className="col lg-col-6 md-col-6 col-12 center">
+ <img src="/images/landing/tokenized_world.png" height={isSmallScreen ? 280 : 364.5} />
+ </div>
+ );
+ }
+ private _renderAssetTypes() {
+ const isSmallScreen = this.state.screenWidth === ScreenWidths.Sm;
+ const assetTypes: AssetType[] = [
+ {
+ title: 'Currency',
+ imageUrl: '/images/landing/currency.png',
+ },
+ {
+ title: 'Traditional assets',
+ imageUrl: '/images/landing/stocks.png',
+ style: {
+ paddingLeft: isSmallScreen ? 41 : 56,
+ paddingRight: isSmallScreen ? 41 : 56,
+ },
+ },
+ {
+ title: 'Digital goods',
+ imageUrl: '/images/landing/digital_goods.png',
+ },
+ ];
+ const assets = _.map(assetTypes, (assetType: AssetType) => {
+ const style = _.isUndefined(assetType.style) ? {} : assetType.style;
+ return (
+ <div key={`asset-${assetType.title}`} className="center" style={{ opacity: 0.8, ...style }}>
+ <div>
+ <img src={assetType.imageUrl} height="80" />
+ </div>
+ <div
+ style={{
+ fontFamily: 'Roboto Mono',
+ fontSize: 13.5,
+ fontWeight: 400,
+ opacity: 0.75,
+ }}
+ >
+ {assetType.title}
+ </div>
+ </div>
+ );
+ });
+ return assets;
+ }
+ private _renderInfoBoxes() {
+ const isSmallScreen = this.state.screenWidth === ScreenWidths.Sm;
+ const boxStyle: React.CSSProperties = {
+ maxWidth: 252,
+ height: 386,
+ backgroundColor: colors.grey50,
+ borderRadius: 5,
+ padding: '10px 24px 24px',
+ };
+ const boxes = _.map(boxContents, (boxContent: BoxContent) => {
+ return (
+ <div key={`box-${boxContent.title}`} className="col lg-col-4 md-col-4 col-12 sm-pb4">
+ <div className={`center sm-mx-auto ${!isSmallScreen && boxContent.classNames}`} style={boxStyle}>
+ <div>
+ <img src={boxContent.imageUrl} style={{ height: 210 }} />
+ </div>
+ <div className="h3" style={{ color: 'black', fontFamily: 'Roboto Mono' }}>
+ {boxContent.title}
+ </div>
+ <div className="pt2 pb2" style={{ fontFamily: 'Roboto Mono', fontSize: 14 }}>
+ {boxContent.description}
+ </div>
+ </div>
+ </div>
+ );
+ });
+ return (
+ <div className="clearfix" style={{ backgroundColor: colors.heroGrey }}>
+ <div className="mx-auto py4 sm-mt2 clearfix" style={{ maxWidth: '60em' }}>
+ {boxes}
+ </div>
+ </div>
+ );
+ }
+ private _renderUseCases() {
+ const isSmallScreen = this.state.screenWidth === ScreenWidths.Sm;
- const useCases: UseCase[] = [
- {
- imageUrl: '/images/landing/governance_icon.png',
- type: 'Decentralized governance',
- description:
- 'Decentralized organizations use tokens to represent ownership and \
+ const useCases: UseCase[] = [
+ {
+ imageUrl: '/images/landing/governance_icon.png',
+ type: 'Decentralized governance',
+ description:
+ 'Decentralized organizations use tokens to represent ownership and \
guide their governance logic. 0x allows decentralized organizations \
to seamlessly and safely trade ownership for startup capital.',
- projectIconUrls: ['/images/landing/aragon.png'],
- classNames: 'lg-px2 md-px2',
- },
- {
- imageUrl: '/images/landing/prediction_market_icon.png',
- type: 'Prediction markets',
- description:
- 'Decentralized prediction market platforms generate sets of tokens that \
+ projectIconUrls: ['/images/landing/aragon.png'],
+ classNames: 'lg-px2 md-px2',
+ },
+ {
+ imageUrl: '/images/landing/prediction_market_icon.png',
+ type: 'Prediction markets',
+ description:
+ 'Decentralized prediction market platforms generate sets of tokens that \
represent a financial stake in the outcomes of real-world events. 0x allows \
these tokens to be instantly tradable.',
- projectIconUrls: ['/images/landing/augur.png'],
- classNames: 'lg-px2 md-px2',
- },
- {
- imageUrl: '/images/landing/stable_tokens_icon.png',
- type: 'Stable tokens',
- description:
- 'Novel economic constructs such as stable coins require efficient, liquid \
+ projectIconUrls: ['/images/landing/augur.png'],
+ classNames: 'lg-px2 md-px2',
+ },
+ {
+ imageUrl: '/images/landing/stable_tokens_icon.png',
+ type: 'Stable tokens',
+ description:
+ 'Novel economic constructs such as stable coins require efficient, liquid \
markets to succeed. 0x will facilitate the underlying economic mechanisms \
that allow these tokens to remain stable.',
- projectIconUrls: ['/images/landing/maker.png'],
- classNames: 'lg-px2 md-px2',
- },
- {
- imageUrl: '/images/landing/loans_icon.png',
- type: 'Decentralized loans',
- description:
- 'Efficient lending requires liquid markets where investors can buy and re-sell loans. \
+ projectIconUrls: ['/images/landing/maker.png'],
+ classNames: 'lg-px2 md-px2',
+ },
+ {
+ imageUrl: '/images/landing/loans_icon.png',
+ type: 'Decentralized loans',
+ description:
+ 'Efficient lending requires liquid markets where investors can buy and re-sell loans. \
0x enables an ecosystem of lenders to self-organize and efficiently determine \
market prices for all outstanding loans.',
- projectIconUrls: ['/images/landing/dharma.png', '/images/landing/lendroid.png'],
- classNames: 'lg-pr2 md-pr2 lg-col-6 md-col-6',
- style: {
- width: 291,
- float: 'right',
- marginTop: !isSmallScreen ? 38 : 0,
- },
- },
- {
- imageUrl: '/images/landing/fund_management_icon.png',
- type: 'Fund management',
- description:
- 'Decentralized fund management limits fund managers to investing in pre-agreed \
+ projectIconUrls: ['/images/landing/dharma.png', '/images/landing/lendroid.png'],
+ classNames: 'lg-pr2 md-pr2 lg-col-6 md-col-6',
+ style: {
+ width: 291,
+ float: 'right',
+ marginTop: !isSmallScreen ? 38 : 0,
+ },
+ },
+ {
+ imageUrl: '/images/landing/fund_management_icon.png',
+ type: 'Fund management',
+ description:
+ 'Decentralized fund management limits fund managers to investing in pre-agreed \
upon asset classes. Embedding 0x into fund management smart contracts enables \
them to enforce these security constraints.',
- projectIconUrls: ['/images/landing/melonport.png'],
- classNames: 'lg-pl2 md-pl2 lg-col-6 md-col-6',
- style: { width: 291, marginTop: !isSmallScreen ? 38 : 0 },
- },
- ];
+ projectIconUrls: ['/images/landing/melonport.png'],
+ classNames: 'lg-pl2 md-pl2 lg-col-6 md-col-6',
+ style: { width: 291, marginTop: !isSmallScreen ? 38 : 0 },
+ },
+ ];
- const cases = _.map(useCases, (useCase: UseCase) => {
- const style = _.isUndefined(useCase.style) || isSmallScreen ? {} : useCase.style;
- const useCaseBoxStyle = {
- color: colors.grey,
- border: '1px solid #565656',
- borderRadius: 4,
- maxWidth: isSmallScreen ? 375 : 'none',
- ...style,
- };
- const typeStyle: React.CSSProperties = {
- color: colors.lightGrey,
- fontSize: 13,
- textTransform: 'uppercase',
- fontFamily: 'Roboto Mono',
- fontWeight: 300,
- };
- return (
- <div
- key={`useCase-${useCase.type}`}
- className={`col lg-col-4 md-col-4 col-12 sm-pt3 sm-px3 sm-pb3 ${useCase.classNames}`}
- >
- <div className="relative p2 pb2 sm-mx-auto" style={useCaseBoxStyle}>
- <div className="absolute center" style={{ top: -35, width: 'calc(100% - 32px)' }}>
- <img src={useCase.imageUrl} style={{ height: 50 }} />
- </div>
- <div className="pt2 center" style={typeStyle}>
- {useCase.type}
- </div>
- <div
- className="pt2"
- style={{
- lineHeight: 1.5,
- fontSize: 14,
- overflow: 'hidden',
- height: 104,
- }}
- >
- {useCase.description}
- </div>
- </div>
- </div>
- );
- });
- return (
- <div className="clearfix pb4 lg-pt2 md-pt2 sm-pt4" style={{ backgroundColor: colors.heroGrey }}>
- <div className="mx-auto pb4 pt3 mt1 sm-mt2 clearfix" style={{ maxWidth: '67em' }}>
- {cases}
- </div>
- </div>
- );
- }
- private _renderCallToAction() {
- const isSmallScreen = this.state.screenWidth === ScreenWidths.Sm;
- const buttonLabelStyle: React.CSSProperties = {
- textTransform: 'none',
- fontSize: 15,
- fontWeight: 400,
- };
- const lightButtonStyle: React.CSSProperties = {
- borderRadius: 6,
- border: '1px solid #a0a0a0',
- lineHeight: '33px',
- height: 49,
- };
- const callToActionClassNames =
- 'col lg-col-8 md-col-8 col-12 lg-pr3 md-pr3 \
+ const cases = _.map(useCases, (useCase: UseCase) => {
+ const style = _.isUndefined(useCase.style) || isSmallScreen ? {} : useCase.style;
+ const useCaseBoxStyle = {
+ color: colors.grey,
+ border: '1px solid #565656',
+ borderRadius: 4,
+ maxWidth: isSmallScreen ? 375 : 'none',
+ ...style,
+ };
+ const typeStyle: React.CSSProperties = {
+ color: colors.lightGrey,
+ fontSize: 13,
+ textTransform: 'uppercase',
+ fontFamily: 'Roboto Mono',
+ fontWeight: 300,
+ };
+ return (
+ <div
+ key={`useCase-${useCase.type}`}
+ className={`col lg-col-4 md-col-4 col-12 sm-pt3 sm-px3 sm-pb3 ${useCase.classNames}`}
+ >
+ <div className="relative p2 pb2 sm-mx-auto" style={useCaseBoxStyle}>
+ <div className="absolute center" style={{ top: -35, width: 'calc(100% - 32px)' }}>
+ <img src={useCase.imageUrl} style={{ height: 50 }} />
+ </div>
+ <div className="pt2 center" style={typeStyle}>
+ {useCase.type}
+ </div>
+ <div
+ className="pt2"
+ style={{
+ lineHeight: 1.5,
+ fontSize: 14,
+ overflow: 'hidden',
+ height: 104,
+ }}
+ >
+ {useCase.description}
+ </div>
+ </div>
+ </div>
+ );
+ });
+ return (
+ <div className="clearfix pb4 lg-pt2 md-pt2 sm-pt4" style={{ backgroundColor: colors.heroGrey }}>
+ <div className="mx-auto pb4 pt3 mt1 sm-mt2 clearfix" style={{ maxWidth: '67em' }}>
+ {cases}
+ </div>
+ </div>
+ );
+ }
+ private _renderCallToAction() {
+ const isSmallScreen = this.state.screenWidth === ScreenWidths.Sm;
+ const buttonLabelStyle: React.CSSProperties = {
+ textTransform: 'none',
+ fontSize: 15,
+ fontWeight: 400,
+ };
+ const lightButtonStyle: React.CSSProperties = {
+ borderRadius: 6,
+ border: '1px solid #a0a0a0',
+ lineHeight: '33px',
+ height: 49,
+ };
+ const callToActionClassNames =
+ 'col lg-col-8 md-col-8 col-12 lg-pr3 md-pr3 \
lg-right-align md-right-align sm-center sm-px3 h4';
- return (
- <div className="clearfix pb4" style={{ backgroundColor: colors.heroGrey }}>
- <div className="mx-auto max-width-4 pb4 mb3 clearfix">
- <div
- className={callToActionClassNames}
- style={{
- fontFamily: 'Roboto Mono',
- color: colors.white,
- lineHeight: isSmallScreen ? 1.7 : 3,
- }}
- >
- Get started on building the decentralized future
- </div>
- <div className="col lg-col-4 md-col-4 col-12 sm-center sm-pt2">
- <Link to={WebsitePaths.ZeroExJs} className="text-decoration-none">
- <RaisedButton
- style={{ borderRadius: 6, minWidth: 150 }}
- buttonStyle={lightButtonStyle}
- labelColor={colors.white}
- backgroundColor={colors.heroGrey}
- labelStyle={buttonLabelStyle}
- label="Build on 0x"
- onClick={_.noop}
- />
- </Link>
- </div>
- </div>
- </div>
- );
- }
- private _updateScreenWidth() {
- const newScreenWidth = utils.getScreenWidth();
- if (newScreenWidth !== this.state.screenWidth) {
- this.setState({
- screenWidth: newScreenWidth,
- });
- }
- }
+ return (
+ <div className="clearfix pb4" style={{ backgroundColor: colors.heroGrey }}>
+ <div className="mx-auto max-width-4 pb4 mb3 clearfix">
+ <div
+ className={callToActionClassNames}
+ style={{
+ fontFamily: 'Roboto Mono',
+ color: colors.white,
+ lineHeight: isSmallScreen ? 1.7 : 3,
+ }}
+ >
+ Get started on building the decentralized future
+ </div>
+ <div className="col lg-col-4 md-col-4 col-12 sm-center sm-pt2">
+ <Link to={WebsitePaths.ZeroExJs} className="text-decoration-none">
+ <RaisedButton
+ style={{ borderRadius: 6, minWidth: 150 }}
+ buttonStyle={lightButtonStyle}
+ labelColor={colors.white}
+ backgroundColor={colors.heroGrey}
+ labelStyle={buttonLabelStyle}
+ label="Build on 0x"
+ onClick={_.noop}
+ />
+ </Link>
+ </div>
+ </div>
+ </div>
+ );
+ }
+ private _updateScreenWidth() {
+ const newScreenWidth = utils.getScreenWidth();
+ if (newScreenWidth !== this.state.screenWidth) {
+ this.setState({
+ screenWidth: newScreenWidth,
+ });
+ }
+ }
} // tslint:disable:max-file-line-count
diff --git a/packages/website/ts/pages/not_found.tsx b/packages/website/ts/pages/not_found.tsx
index 9d8d4142d..ff277c377 100644
--- a/packages/website/ts/pages/not_found.tsx
+++ b/packages/website/ts/pages/not_found.tsx
@@ -5,38 +5,38 @@ import { TopBar } from 'ts/components/top_bar';
import { Styles } from 'ts/types';
export interface NotFoundProps {
- location: Location;
+ location: Location;
}
interface NotFoundState {}
const styles: Styles = {
- thin: {
- fontWeight: 100,
- },
+ thin: {
+ fontWeight: 100,
+ },
};
export class NotFound extends React.Component<NotFoundProps, NotFoundState> {
- public render() {
- return (
- <div>
- <TopBar blockchainIsLoaded={false} location={this.props.location} />
- <div className="mx-auto max-width-4 py4">
- <div className="center py4">
- <div className="py4">
- <div className="py4">
- <h1 style={{ ...styles.thin }}>404 Not Found</h1>
- <div className="py1">
- <div className="py3">
- Hm... looks like we couldn't find what you are looking for.
- </div>
- </div>
- </div>
- </div>
- </div>
- </div>
- <Footer />
- </div>
- );
- }
+ public render() {
+ return (
+ <div>
+ <TopBar blockchainIsLoaded={false} location={this.props.location} />
+ <div className="mx-auto max-width-4 py4">
+ <div className="center py4">
+ <div className="py4">
+ <div className="py4">
+ <h1 style={{ ...styles.thin }}>404 Not Found</h1>
+ <div className="py1">
+ <div className="py3">
+ Hm... looks like we couldn't find what you are looking for.
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ <Footer />
+ </div>
+ );
+ }
}
diff --git a/packages/website/ts/pages/shared/anchor_title.tsx b/packages/website/ts/pages/shared/anchor_title.tsx
index 118aa0ea5..db5be1f59 100644
--- a/packages/website/ts/pages/shared/anchor_title.tsx
+++ b/packages/website/ts/pages/shared/anchor_title.tsx
@@ -5,87 +5,87 @@ import { constants } from 'ts/utils/constants';
import { utils } from 'ts/utils/utils';
const headerSizeToScrollOffset: { [headerSize: string]: number } = {
- h2: -20,
- h3: 0,
+ h2: -20,
+ h3: 0,
};
interface AnchorTitleProps {
- title: string | React.ReactNode;
- id: string;
- headerSize: HeaderSizes;
- shouldShowAnchor: boolean;
+ title: string | React.ReactNode;
+ id: string;
+ headerSize: HeaderSizes;
+ shouldShowAnchor: boolean;
}
interface AnchorTitleState {
- isHovering: boolean;
+ isHovering: boolean;
}
const styles: Styles = {
- anchor: {
- fontSize: 20,
- transform: 'rotate(45deg)',
- cursor: 'pointer',
- },
- headers: {
- WebkitMarginStart: 0,
- WebkitMarginEnd: 0,
- fontWeight: 'bold',
- display: 'block',
- },
- h1: {
- fontSize: '1.8em',
- WebkitMarginBefore: '0.83em',
- WebkitMarginAfter: '0.83em',
- },
- h2: {
- fontSize: '1.5em',
- WebkitMarginBefore: '0.83em',
- WebkitMarginAfter: '0.83em',
- },
- h3: {
- fontSize: '1.17em',
- WebkitMarginBefore: '1em',
- WebkitMarginAfter: '1em',
- },
+ anchor: {
+ fontSize: 20,
+ transform: 'rotate(45deg)',
+ cursor: 'pointer',
+ },
+ headers: {
+ WebkitMarginStart: 0,
+ WebkitMarginEnd: 0,
+ fontWeight: 'bold',
+ display: 'block',
+ },
+ h1: {
+ fontSize: '1.8em',
+ WebkitMarginBefore: '0.83em',
+ WebkitMarginAfter: '0.83em',
+ },
+ h2: {
+ fontSize: '1.5em',
+ WebkitMarginBefore: '0.83em',
+ WebkitMarginAfter: '0.83em',
+ },
+ h3: {
+ fontSize: '1.17em',
+ WebkitMarginBefore: '1em',
+ WebkitMarginAfter: '1em',
+ },
};
export class AnchorTitle extends React.Component<AnchorTitleProps, AnchorTitleState> {
- constructor(props: AnchorTitleProps) {
- super(props);
- this.state = {
- isHovering: false,
- };
- }
- public render() {
- let opacity = 0;
- if (this.props.shouldShowAnchor) {
- opacity = this.state.isHovering ? 0.6 : 1;
- }
- return (
- <div className="relative flex" style={{ ...styles[this.props.headerSize], ...styles.headers }}>
- <div className="inline-block" style={{ paddingRight: 4 }}>
- {this.props.title}
- </div>
- <ScrollLink
- to={this.props.id}
- offset={headerSizeToScrollOffset[this.props.headerSize]}
- duration={constants.DOCS_SCROLL_DURATION_MS}
- containerId={constants.DOCS_CONTAINER_ID}
- >
- <i
- className="zmdi zmdi-link"
- onClick={utils.setUrlHash.bind(utils, this.props.id)}
- style={{ ...styles.anchor, opacity }}
- onMouseOver={this._setHoverState.bind(this, true)}
- onMouseOut={this._setHoverState.bind(this, false)}
- />
- </ScrollLink>
- </div>
- );
- }
- private _setHoverState(isHovering: boolean) {
- this.setState({
- isHovering,
- });
- }
+ constructor(props: AnchorTitleProps) {
+ super(props);
+ this.state = {
+ isHovering: false,
+ };
+ }
+ public render() {
+ let opacity = 0;
+ if (this.props.shouldShowAnchor) {
+ opacity = this.state.isHovering ? 0.6 : 1;
+ }
+ return (
+ <div className="relative flex" style={{ ...styles[this.props.headerSize], ...styles.headers }}>
+ <div className="inline-block" style={{ paddingRight: 4 }}>
+ {this.props.title}
+ </div>
+ <ScrollLink
+ to={this.props.id}
+ offset={headerSizeToScrollOffset[this.props.headerSize]}
+ duration={constants.DOCS_SCROLL_DURATION_MS}
+ containerId={constants.DOCS_CONTAINER_ID}
+ >
+ <i
+ className="zmdi zmdi-link"
+ onClick={utils.setUrlHash.bind(utils, this.props.id)}
+ style={{ ...styles.anchor, opacity }}
+ onMouseOver={this._setHoverState.bind(this, true)}
+ onMouseOut={this._setHoverState.bind(this, false)}
+ />
+ </ScrollLink>
+ </div>
+ );
+ }
+ private _setHoverState(isHovering: boolean) {
+ this.setState({
+ isHovering,
+ });
+ }
}
diff --git a/packages/website/ts/pages/shared/markdown_code_block.tsx b/packages/website/ts/pages/shared/markdown_code_block.tsx
index a9d95979b..be96fda16 100644
--- a/packages/website/ts/pages/shared/markdown_code_block.tsx
+++ b/packages/website/ts/pages/shared/markdown_code_block.tsx
@@ -3,23 +3,23 @@ import * as React from 'react';
import * as HighLight from 'react-highlight';
interface MarkdownCodeBlockProps {
- literal: string;
- language: string;
+ literal: string;
+ language: string;
}
interface MarkdownCodeBlockState {}
export class MarkdownCodeBlock extends React.Component<MarkdownCodeBlockProps, MarkdownCodeBlockState> {
- // Re-rendering a codeblock causes any use selection to become de-selected. This is annoying when trying
- // to copy-paste code examples. We therefore noop re-renders on this component if it's props haven't changed.
- public shouldComponentUpdate(nextProps: MarkdownCodeBlockProps, nextState: MarkdownCodeBlockState) {
- return nextProps.literal !== this.props.literal || nextProps.language !== this.props.language;
- }
- public render() {
- return (
- <span style={{ fontSize: 16 }}>
- <HighLight className={this.props.language || 'javascript'}>{this.props.literal}</HighLight>
- </span>
- );
- }
+ // Re-rendering a codeblock causes any use selection to become de-selected. This is annoying when trying
+ // to copy-paste code examples. We therefore noop re-renders on this component if it's props haven't changed.
+ public shouldComponentUpdate(nextProps: MarkdownCodeBlockProps, nextState: MarkdownCodeBlockState) {
+ return nextProps.literal !== this.props.literal || nextProps.language !== this.props.language;
+ }
+ public render() {
+ return (
+ <span style={{ fontSize: 16 }}>
+ <HighLight className={this.props.language || 'javascript'}>{this.props.literal}</HighLight>
+ </span>
+ );
+ }
}
diff --git a/packages/website/ts/pages/shared/markdown_section.tsx b/packages/website/ts/pages/shared/markdown_section.tsx
index c875ab736..5487dc8cc 100644
--- a/packages/website/ts/pages/shared/markdown_section.tsx
+++ b/packages/website/ts/pages/shared/markdown_section.tsx
@@ -9,66 +9,66 @@ import { HeaderSizes } from 'ts/types';
import { utils } from 'ts/utils/utils';
interface MarkdownSectionProps {
- sectionName: string;
- markdownContent: string;
- headerSize?: HeaderSizes;
- githubLink?: string;
+ sectionName: string;
+ markdownContent: string;
+ headerSize?: HeaderSizes;
+ githubLink?: string;
}
interface MarkdownSectionState {
- shouldShowAnchor: boolean;
+ shouldShowAnchor: boolean;
}
export class MarkdownSection extends React.Component<MarkdownSectionProps, MarkdownSectionState> {
- public static defaultProps: Partial<MarkdownSectionProps> = {
- headerSize: HeaderSizes.H3,
- };
- constructor(props: MarkdownSectionProps) {
- super(props);
- this.state = {
- shouldShowAnchor: false,
- };
- }
- public render() {
- const sectionName = this.props.sectionName;
- const id = utils.getIdFromName(sectionName);
- return (
- <div
- className="pt2 pr3 md-pl2 sm-pl3 overflow-hidden"
- onMouseOver={this._setAnchorVisibility.bind(this, true)}
- onMouseOut={this._setAnchorVisibility.bind(this, false)}
- >
- <ScrollElement name={id}>
- <div className="clearfix">
- <div className="col lg-col-8 md-col-8 sm-col-12">
- <span style={{ textTransform: 'capitalize' }}>
- <AnchorTitle
- headerSize={this.props.headerSize}
- title={sectionName}
- id={id}
- shouldShowAnchor={this.state.shouldShowAnchor}
- />
- </span>
- </div>
- <div className="col col-4 sm-hide xs-hide py2 right-align">
- {!_.isUndefined(this.props.githubLink) && (
- <RaisedButton
- href={this.props.githubLink}
- target="_blank"
- label="Edit on Github"
- icon={<i className="zmdi zmdi-github" style={{ fontSize: 23 }} />}
- />
- )}
- </div>
- </div>
- <ReactMarkdown source={this.props.markdownContent} renderers={{ CodeBlock: MarkdownCodeBlock }} />
- </ScrollElement>
- </div>
- );
- }
- private _setAnchorVisibility(shouldShowAnchor: boolean) {
- this.setState({
- shouldShowAnchor,
- });
- }
+ public static defaultProps: Partial<MarkdownSectionProps> = {
+ headerSize: HeaderSizes.H3,
+ };
+ constructor(props: MarkdownSectionProps) {
+ super(props);
+ this.state = {
+ shouldShowAnchor: false,
+ };
+ }
+ public render() {
+ const sectionName = this.props.sectionName;
+ const id = utils.getIdFromName(sectionName);
+ return (
+ <div
+ className="pt2 pr3 md-pl2 sm-pl3 overflow-hidden"
+ onMouseOver={this._setAnchorVisibility.bind(this, true)}
+ onMouseOut={this._setAnchorVisibility.bind(this, false)}
+ >
+ <ScrollElement name={id}>
+ <div className="clearfix">
+ <div className="col lg-col-8 md-col-8 sm-col-12">
+ <span style={{ textTransform: 'capitalize' }}>
+ <AnchorTitle
+ headerSize={this.props.headerSize}
+ title={sectionName}
+ id={id}
+ shouldShowAnchor={this.state.shouldShowAnchor}
+ />
+ </span>
+ </div>
+ <div className="col col-4 sm-hide xs-hide py2 right-align">
+ {!_.isUndefined(this.props.githubLink) && (
+ <RaisedButton
+ href={this.props.githubLink}
+ target="_blank"
+ label="Edit on Github"
+ icon={<i className="zmdi zmdi-github" style={{ fontSize: 23 }} />}
+ />
+ )}
+ </div>
+ </div>
+ <ReactMarkdown source={this.props.markdownContent} renderers={{ CodeBlock: MarkdownCodeBlock }} />
+ </ScrollElement>
+ </div>
+ );
+ }
+ private _setAnchorVisibility(shouldShowAnchor: boolean) {
+ this.setState({
+ shouldShowAnchor,
+ });
+ }
}
diff --git a/packages/website/ts/pages/shared/nested_sidebar_menu.tsx b/packages/website/ts/pages/shared/nested_sidebar_menu.tsx
index cd7ea68e6..849c33504 100644
--- a/packages/website/ts/pages/shared/nested_sidebar_menu.tsx
+++ b/packages/website/ts/pages/shared/nested_sidebar_menu.tsx
@@ -9,146 +9,146 @@ import { constants } from 'ts/utils/constants';
import { utils } from 'ts/utils/utils';
interface NestedSidebarMenuProps {
- topLevelMenu: { [topLevel: string]: string[] };
- menuSubsectionsBySection: MenuSubsectionsBySection;
- shouldDisplaySectionHeaders?: boolean;
- onMenuItemClick?: () => void;
- selectedVersion?: string;
- versions?: string[];
- docPath?: string;
- isSectionHeaderClickable?: boolean;
+ topLevelMenu: { [topLevel: string]: string[] };
+ menuSubsectionsBySection: MenuSubsectionsBySection;
+ shouldDisplaySectionHeaders?: boolean;
+ onMenuItemClick?: () => void;
+ selectedVersion?: string;
+ versions?: string[];
+ docPath?: string;
+ isSectionHeaderClickable?: boolean;
}
interface NestedSidebarMenuState {}
const styles: Styles = {
- menuItemWithHeaders: {
- minHeight: 0,
- },
- menuItemWithoutHeaders: {
- minHeight: 48,
- },
- menuItemInnerDivWithHeaders: {
- lineHeight: 2,
- },
+ menuItemWithHeaders: {
+ minHeight: 0,
+ },
+ menuItemWithoutHeaders: {
+ minHeight: 48,
+ },
+ menuItemInnerDivWithHeaders: {
+ lineHeight: 2,
+ },
};
export class NestedSidebarMenu extends React.Component<NestedSidebarMenuProps, NestedSidebarMenuState> {
- public static defaultProps: Partial<NestedSidebarMenuProps> = {
- shouldDisplaySectionHeaders: true,
- onMenuItemClick: _.noop,
- };
- public render() {
- const navigation = _.map(this.props.topLevelMenu, (menuItems: string[], sectionName: string) => {
- const finalSectionName = sectionName.replace(/-/g, ' ');
- if (this.props.shouldDisplaySectionHeaders) {
- const id = utils.getIdFromName(sectionName);
- return (
- <div key={`section-${sectionName}`} className="py1">
- <ScrollLink
- to={id}
- offset={-20}
- duration={constants.DOCS_SCROLL_DURATION_MS}
- containerId={constants.DOCS_CONTAINER_ID}
- >
- <div style={{ color: colors.grey, cursor: 'pointer' }} className="pb1">
- {finalSectionName.toUpperCase()}
- </div>
- </ScrollLink>
- {this._renderMenuItems(menuItems)}
- </div>
- );
- } else {
- return <div key={`section-${sectionName}`}>{this._renderMenuItems(menuItems)}</div>;
- }
- });
- return (
- <div>
- {!_.isUndefined(this.props.versions) &&
- !_.isUndefined(this.props.selectedVersion) &&
- !_.isUndefined(this.props.docPath) && (
- <VersionDropDown
- selectedVersion={this.props.selectedVersion}
- versions={this.props.versions}
- docPath={this.props.docPath}
- />
- )}
- {navigation}
- </div>
- );
- }
- private _renderMenuItems(menuItemNames: string[]): React.ReactNode[] {
- const menuItemStyles = this.props.shouldDisplaySectionHeaders
- ? styles.menuItemWithHeaders
- : styles.menuItemWithoutHeaders;
- const menuItemInnerDivStyles = this.props.shouldDisplaySectionHeaders ? styles.menuItemInnerDivWithHeaders : {};
- const menuItems = _.map(menuItemNames, menuItemName => {
- const id = utils.getIdFromName(menuItemName);
- return (
- <div key={menuItemName}>
- <ScrollLink
- key={`menuItem-${menuItemName}`}
- to={id}
- offset={-10}
- duration={constants.DOCS_SCROLL_DURATION_MS}
- containerId={constants.DOCS_CONTAINER_ID}
- >
- <MenuItem
- onTouchTap={this._onMenuItemClick.bind(this, menuItemName)}
- style={menuItemStyles}
- innerDivStyle={menuItemInnerDivStyles}
- >
- <span style={{ textTransform: 'capitalize' }}>{menuItemName}</span>
- </MenuItem>
- </ScrollLink>
- {this._renderMenuItemSubsections(menuItemName)}
- </div>
- );
- });
- return menuItems;
- }
- private _renderMenuItemSubsections(menuItemName: string): React.ReactNode {
- if (_.isUndefined(this.props.menuSubsectionsBySection[menuItemName])) {
- return null;
- }
- return this._renderMenuSubsectionsBySection(menuItemName, this.props.menuSubsectionsBySection[menuItemName]);
- }
- private _renderMenuSubsectionsBySection(menuItemName: string, entityNames: string[]): React.ReactNode {
- return (
- <ul style={{ margin: 0, listStyleType: 'none', paddingLeft: 0 }} key={menuItemName}>
- {_.map(entityNames, entityName => {
- const name = `${menuItemName}-${entityName}`;
- const id = utils.getIdFromName(name);
- return (
- <li key={`menuItem-${entityName}`}>
- <ScrollLink
- to={id}
- offset={0}
- duration={constants.DOCS_SCROLL_DURATION_MS}
- containerId={constants.DOCS_CONTAINER_ID}
- onTouchTap={this._onMenuItemClick.bind(this, name)}
- >
- <MenuItem
- onTouchTap={this._onMenuItemClick.bind(this, name)}
- style={{ minHeight: 35 }}
- innerDivStyle={{
- paddingLeft: 36,
- fontSize: 14,
- lineHeight: '35px',
- }}
- >
- {entityName}
- </MenuItem>
- </ScrollLink>
- </li>
- );
- })}
- </ul>
- );
- }
- private _onMenuItemClick(name: string): void {
- const id = utils.getIdFromName(name);
- utils.setUrlHash(id);
- this.props.onMenuItemClick();
- }
+ public static defaultProps: Partial<NestedSidebarMenuProps> = {
+ shouldDisplaySectionHeaders: true,
+ onMenuItemClick: _.noop,
+ };
+ public render() {
+ const navigation = _.map(this.props.topLevelMenu, (menuItems: string[], sectionName: string) => {
+ const finalSectionName = sectionName.replace(/-/g, ' ');
+ if (this.props.shouldDisplaySectionHeaders) {
+ const id = utils.getIdFromName(sectionName);
+ return (
+ <div key={`section-${sectionName}`} className="py1">
+ <ScrollLink
+ to={id}
+ offset={-20}
+ duration={constants.DOCS_SCROLL_DURATION_MS}
+ containerId={constants.DOCS_CONTAINER_ID}
+ >
+ <div style={{ color: colors.grey, cursor: 'pointer' }} className="pb1">
+ {finalSectionName.toUpperCase()}
+ </div>
+ </ScrollLink>
+ {this._renderMenuItems(menuItems)}
+ </div>
+ );
+ } else {
+ return <div key={`section-${sectionName}`}>{this._renderMenuItems(menuItems)}</div>;
+ }
+ });
+ return (
+ <div>
+ {!_.isUndefined(this.props.versions) &&
+ !_.isUndefined(this.props.selectedVersion) &&
+ !_.isUndefined(this.props.docPath) && (
+ <VersionDropDown
+ selectedVersion={this.props.selectedVersion}
+ versions={this.props.versions}
+ docPath={this.props.docPath}
+ />
+ )}
+ {navigation}
+ </div>
+ );
+ }
+ private _renderMenuItems(menuItemNames: string[]): React.ReactNode[] {
+ const menuItemStyles = this.props.shouldDisplaySectionHeaders
+ ? styles.menuItemWithHeaders
+ : styles.menuItemWithoutHeaders;
+ const menuItemInnerDivStyles = this.props.shouldDisplaySectionHeaders ? styles.menuItemInnerDivWithHeaders : {};
+ const menuItems = _.map(menuItemNames, menuItemName => {
+ const id = utils.getIdFromName(menuItemName);
+ return (
+ <div key={menuItemName}>
+ <ScrollLink
+ key={`menuItem-${menuItemName}`}
+ to={id}
+ offset={-10}
+ duration={constants.DOCS_SCROLL_DURATION_MS}
+ containerId={constants.DOCS_CONTAINER_ID}
+ >
+ <MenuItem
+ onTouchTap={this._onMenuItemClick.bind(this, menuItemName)}
+ style={menuItemStyles}
+ innerDivStyle={menuItemInnerDivStyles}
+ >
+ <span style={{ textTransform: 'capitalize' }}>{menuItemName}</span>
+ </MenuItem>
+ </ScrollLink>
+ {this._renderMenuItemSubsections(menuItemName)}
+ </div>
+ );
+ });
+ return menuItems;
+ }
+ private _renderMenuItemSubsections(menuItemName: string): React.ReactNode {
+ if (_.isUndefined(this.props.menuSubsectionsBySection[menuItemName])) {
+ return null;
+ }
+ return this._renderMenuSubsectionsBySection(menuItemName, this.props.menuSubsectionsBySection[menuItemName]);
+ }
+ private _renderMenuSubsectionsBySection(menuItemName: string, entityNames: string[]): React.ReactNode {
+ return (
+ <ul style={{ margin: 0, listStyleType: 'none', paddingLeft: 0 }} key={menuItemName}>
+ {_.map(entityNames, entityName => {
+ const name = `${menuItemName}-${entityName}`;
+ const id = utils.getIdFromName(name);
+ return (
+ <li key={`menuItem-${entityName}`}>
+ <ScrollLink
+ to={id}
+ offset={0}
+ duration={constants.DOCS_SCROLL_DURATION_MS}
+ containerId={constants.DOCS_CONTAINER_ID}
+ onTouchTap={this._onMenuItemClick.bind(this, name)}
+ >
+ <MenuItem
+ onTouchTap={this._onMenuItemClick.bind(this, name)}
+ style={{ minHeight: 35 }}
+ innerDivStyle={{
+ paddingLeft: 36,
+ fontSize: 14,
+ lineHeight: '35px',
+ }}
+ >
+ {entityName}
+ </MenuItem>
+ </ScrollLink>
+ </li>
+ );
+ })}
+ </ul>
+ );
+ }
+ private _onMenuItemClick(name: string): void {
+ const id = utils.getIdFromName(name);
+ utils.setUrlHash(id);
+ this.props.onMenuItemClick();
+ }
}
diff --git a/packages/website/ts/pages/shared/section_header.tsx b/packages/website/ts/pages/shared/section_header.tsx
index f9aa1a5e6..a5f5f52cf 100644
--- a/packages/website/ts/pages/shared/section_header.tsx
+++ b/packages/website/ts/pages/shared/section_header.tsx
@@ -5,46 +5,46 @@ import { HeaderSizes } from 'ts/types';
import { utils } from 'ts/utils/utils';
interface SectionHeaderProps {
- sectionName: string;
- headerSize?: HeaderSizes;
+ sectionName: string;
+ headerSize?: HeaderSizes;
}
interface SectionHeaderState {
- shouldShowAnchor: boolean;
+ shouldShowAnchor: boolean;
}
export class SectionHeader extends React.Component<SectionHeaderProps, SectionHeaderState> {
- public static defaultProps: Partial<SectionHeaderProps> = {
- headerSize: HeaderSizes.H2,
- };
- constructor(props: SectionHeaderProps) {
- super(props);
- this.state = {
- shouldShowAnchor: false,
- };
- }
- public render() {
- const sectionName = this.props.sectionName.replace(/-/g, ' ');
- const id = utils.getIdFromName(sectionName);
- return (
- <div
- onMouseOver={this._setAnchorVisibility.bind(this, true)}
- onMouseOut={this._setAnchorVisibility.bind(this, false)}
- >
- <ScrollElement name={id}>
- <AnchorTitle
- headerSize={this.props.headerSize}
- title={<span style={{ textTransform: 'capitalize' }}>{sectionName}</span>}
- id={id}
- shouldShowAnchor={this.state.shouldShowAnchor}
- />
- </ScrollElement>
- </div>
- );
- }
- private _setAnchorVisibility(shouldShowAnchor: boolean) {
- this.setState({
- shouldShowAnchor,
- });
- }
+ public static defaultProps: Partial<SectionHeaderProps> = {
+ headerSize: HeaderSizes.H2,
+ };
+ constructor(props: SectionHeaderProps) {
+ super(props);
+ this.state = {
+ shouldShowAnchor: false,
+ };
+ }
+ public render() {
+ const sectionName = this.props.sectionName.replace(/-/g, ' ');
+ const id = utils.getIdFromName(sectionName);
+ return (
+ <div
+ onMouseOver={this._setAnchorVisibility.bind(this, true)}
+ onMouseOut={this._setAnchorVisibility.bind(this, false)}
+ >
+ <ScrollElement name={id}>
+ <AnchorTitle
+ headerSize={this.props.headerSize}
+ title={<span style={{ textTransform: 'capitalize' }}>{sectionName}</span>}
+ id={id}
+ shouldShowAnchor={this.state.shouldShowAnchor}
+ />
+ </ScrollElement>
+ </div>
+ );
+ }
+ private _setAnchorVisibility(shouldShowAnchor: boolean) {
+ this.setState({
+ shouldShowAnchor,
+ });
+ }
}
diff --git a/packages/website/ts/pages/shared/version_drop_down.tsx b/packages/website/ts/pages/shared/version_drop_down.tsx
index a647252ba..b922e1048 100644
--- a/packages/website/ts/pages/shared/version_drop_down.tsx
+++ b/packages/website/ts/pages/shared/version_drop_down.tsx
@@ -4,34 +4,34 @@ import MenuItem from 'material-ui/MenuItem';
import * as React from 'react';
interface VersionDropDownProps {
- selectedVersion: string;
- versions: string[];
- docPath: string;
+ selectedVersion: string;
+ versions: string[];
+ docPath: string;
}
interface VersionDropDownState {}
export class VersionDropDown extends React.Component<VersionDropDownProps, VersionDropDownState> {
- public render() {
- return (
- <div className="mx-auto" style={{ width: 120 }}>
- <DropDownMenu
- maxHeight={300}
- value={this.props.selectedVersion}
- onChange={this._updateSelectedVersion.bind(this)}
- >
- {this._renderDropDownItems()}
- </DropDownMenu>
- </div>
- );
- }
- private _renderDropDownItems() {
- const items = _.map(this.props.versions, version => {
- return <MenuItem key={version} value={version} primaryText={`v${version}`} />;
- });
- return items;
- }
- private _updateSelectedVersion(e: any, index: number, value: string) {
- window.location.href = `${this.props.docPath}/${value}${window.location.hash}`;
- }
+ public render() {
+ return (
+ <div className="mx-auto" style={{ width: 120 }}>
+ <DropDownMenu
+ maxHeight={300}
+ value={this.props.selectedVersion}
+ onChange={this._updateSelectedVersion.bind(this)}
+ >
+ {this._renderDropDownItems()}
+ </DropDownMenu>
+ </div>
+ );
+ }
+ private _renderDropDownItems() {
+ const items = _.map(this.props.versions, version => {
+ return <MenuItem key={version} value={version} primaryText={`v${version}`} />;
+ });
+ return items;
+ }
+ private _updateSelectedVersion(e: any, index: number, value: string) {
+ window.location.href = `${this.props.docPath}/${value}${window.location.hash}`;
+ }
}
diff --git a/packages/website/ts/pages/wiki/wiki.tsx b/packages/website/ts/pages/wiki/wiki.tsx
index dba5ed6a0..d065614ba 100644
--- a/packages/website/ts/pages/wiki/wiki.tsx
+++ b/packages/website/ts/pages/wiki/wiki.tsx
@@ -16,186 +16,186 @@ import { utils } from 'ts/utils/utils';
const WIKI_NOT_READY_BACKOUT_TIMEOUT_MS = 5000;
export interface WikiProps {
- source: string;
- location: Location;
+ source: string;
+ location: Location;
}
interface WikiState {
- articlesBySection: ArticlesBySection;
+ articlesBySection: ArticlesBySection;
}
const styles: Styles = {
- mainContainers: {
- position: 'absolute',
- top: 1,
- left: 0,
- bottom: 0,
- right: 0,
- overflowZ: 'hidden',
- overflowY: 'scroll',
- minHeight: 'calc(100vh - 1px)',
- WebkitOverflowScrolling: 'touch',
- },
- menuContainer: {
- borderColor: colors.grey300,
- maxWidth: 330,
- marginLeft: 20,
- },
+ mainContainers: {
+ position: 'absolute',
+ top: 1,
+ left: 0,
+ bottom: 0,
+ right: 0,
+ overflowZ: 'hidden',
+ overflowY: 'scroll',
+ minHeight: 'calc(100vh - 1px)',
+ WebkitOverflowScrolling: 'touch',
+ },
+ menuContainer: {
+ borderColor: colors.grey300,
+ maxWidth: 330,
+ marginLeft: 20,
+ },
};
export class Wiki extends React.Component<WikiProps, WikiState> {
- private _wikiBackoffTimeoutId: number;
- constructor(props: WikiProps) {
- super(props);
- this.state = {
- articlesBySection: undefined,
- };
- }
- public componentWillMount() {
- // tslint:disable-next-line:no-floating-promises
- this._fetchArticlesBySectionAsync();
- }
- public componentWillUnmount() {
- clearTimeout(this._wikiBackoffTimeoutId);
- }
- public render() {
- const menuSubsectionsBySection = _.isUndefined(this.state.articlesBySection)
- ? {}
- : this._getMenuSubsectionsBySection(this.state.articlesBySection);
- return (
- <div>
- <DocumentTitle title="0x Protocol Wiki" />
- <TopBar
- blockchainIsLoaded={false}
- location={this.props.location}
- menuSubsectionsBySection={menuSubsectionsBySection}
- shouldFullWidth={true}
- />
- {_.isUndefined(this.state.articlesBySection) ? (
- <div className="col col-12" style={styles.mainContainers}>
- <div
- className="relative sm-px2 sm-pt2 sm-m1"
- style={{ height: 122, top: '50%', transform: 'translateY(-50%)' }}
- >
- <div className="center pb2">
- <CircularProgress size={40} thickness={5} />
- </div>
- <div className="center pt2" style={{ paddingBottom: 11 }}>
- Loading wiki...
- </div>
- </div>
- </div>
- ) : (
- <div className="mx-auto flex" style={{ color: colors.grey800, height: 43 }}>
- <div className="relative col md-col-3 lg-col-3 lg-pl0 md-pl1 sm-hide xs-hide">
- <div
- className="border-right absolute pt2"
- style={{ ...styles.menuContainer, ...styles.mainContainers }}
- >
- <NestedSidebarMenu
- topLevelMenu={menuSubsectionsBySection}
- menuSubsectionsBySection={menuSubsectionsBySection}
- isSectionHeaderClickable={true}
- />
- </div>
- </div>
- <div className="relative col lg-col-9 md-col-9 sm-col-12 col-12">
- <div id="documentation" style={styles.mainContainers} className="absolute">
- <div id="0xProtocolWiki" />
- <h1 className="md-pl2 sm-pl3">
- <a href={constants.URL_GITHUB_WIKI} target="_blank">
- 0x Protocol Wiki
- </a>
- </h1>
- <div id="wiki">{this._renderWikiArticles()}</div>
- </div>
- </div>
- </div>
- )}
- </div>
- );
- }
- private _renderWikiArticles(): React.ReactNode {
- const sectionNames = _.keys(this.state.articlesBySection);
- const sections = _.map(sectionNames, sectionName => this._renderSection(sectionName));
- return sections;
- }
- private _renderSection(sectionName: string) {
- const articles = this.state.articlesBySection[sectionName];
- const renderedArticles = _.map(articles, (article: Article) => {
- const githubLink = `${constants.URL_GITHUB_WIKI}/edit/master/${sectionName}/${article.fileName}`;
- return (
- <div key={`markdown-section-${article.title}`}>
- <MarkdownSection
- sectionName={article.title}
- markdownContent={article.content}
- headerSize={HeaderSizes.H2}
- githubLink={githubLink}
- />
- <div className="mb4 mt3 p3 center" style={{ backgroundColor: colors.lightestGrey }}>
- See a way to make this article better?{' '}
- <a href={githubLink} target="_blank">
- Edit here →
- </a>
- </div>
- </div>
- );
- });
- return (
- <div key={`section-${sectionName}`} className="py2 pr3 md-pl2 sm-pl3">
- <SectionHeader sectionName={sectionName} headerSize={HeaderSizes.H1} />
- {renderedArticles}
- </div>
- );
- }
- private _scrollToHash(): void {
- const hashWithPrefix = this.props.location.hash;
- let hash = hashWithPrefix.slice(1);
- if (_.isEmpty(hash)) {
- hash = '0xProtocolWiki'; // scroll to the top
- }
+ private _wikiBackoffTimeoutId: number;
+ constructor(props: WikiProps) {
+ super(props);
+ this.state = {
+ articlesBySection: undefined,
+ };
+ }
+ public componentWillMount() {
+ // tslint:disable-next-line:no-floating-promises
+ this._fetchArticlesBySectionAsync();
+ }
+ public componentWillUnmount() {
+ clearTimeout(this._wikiBackoffTimeoutId);
+ }
+ public render() {
+ const menuSubsectionsBySection = _.isUndefined(this.state.articlesBySection)
+ ? {}
+ : this._getMenuSubsectionsBySection(this.state.articlesBySection);
+ return (
+ <div>
+ <DocumentTitle title="0x Protocol Wiki" />
+ <TopBar
+ blockchainIsLoaded={false}
+ location={this.props.location}
+ menuSubsectionsBySection={menuSubsectionsBySection}
+ shouldFullWidth={true}
+ />
+ {_.isUndefined(this.state.articlesBySection) ? (
+ <div className="col col-12" style={styles.mainContainers}>
+ <div
+ className="relative sm-px2 sm-pt2 sm-m1"
+ style={{ height: 122, top: '50%', transform: 'translateY(-50%)' }}
+ >
+ <div className="center pb2">
+ <CircularProgress size={40} thickness={5} />
+ </div>
+ <div className="center pt2" style={{ paddingBottom: 11 }}>
+ Loading wiki...
+ </div>
+ </div>
+ </div>
+ ) : (
+ <div className="mx-auto flex" style={{ color: colors.grey800, height: 43 }}>
+ <div className="relative col md-col-3 lg-col-3 lg-pl0 md-pl1 sm-hide xs-hide">
+ <div
+ className="border-right absolute pt2"
+ style={{ ...styles.menuContainer, ...styles.mainContainers }}
+ >
+ <NestedSidebarMenu
+ topLevelMenu={menuSubsectionsBySection}
+ menuSubsectionsBySection={menuSubsectionsBySection}
+ isSectionHeaderClickable={true}
+ />
+ </div>
+ </div>
+ <div className="relative col lg-col-9 md-col-9 sm-col-12 col-12">
+ <div id="documentation" style={styles.mainContainers} className="absolute">
+ <div id="0xProtocolWiki" />
+ <h1 className="md-pl2 sm-pl3">
+ <a href={constants.URL_GITHUB_WIKI} target="_blank">
+ 0x Protocol Wiki
+ </a>
+ </h1>
+ <div id="wiki">{this._renderWikiArticles()}</div>
+ </div>
+ </div>
+ </div>
+ )}
+ </div>
+ );
+ }
+ private _renderWikiArticles(): React.ReactNode {
+ const sectionNames = _.keys(this.state.articlesBySection);
+ const sections = _.map(sectionNames, sectionName => this._renderSection(sectionName));
+ return sections;
+ }
+ private _renderSection(sectionName: string) {
+ const articles = this.state.articlesBySection[sectionName];
+ const renderedArticles = _.map(articles, (article: Article) => {
+ const githubLink = `${constants.URL_GITHUB_WIKI}/edit/master/${sectionName}/${article.fileName}`;
+ return (
+ <div key={`markdown-section-${article.title}`}>
+ <MarkdownSection
+ sectionName={article.title}
+ markdownContent={article.content}
+ headerSize={HeaderSizes.H2}
+ githubLink={githubLink}
+ />
+ <div className="mb4 mt3 p3 center" style={{ backgroundColor: colors.lightestGrey }}>
+ See a way to make this article better?{' '}
+ <a href={githubLink} target="_blank">
+ Edit here →
+ </a>
+ </div>
+ </div>
+ );
+ });
+ return (
+ <div key={`section-${sectionName}`} className="py2 pr3 md-pl2 sm-pl3">
+ <SectionHeader sectionName={sectionName} headerSize={HeaderSizes.H1} />
+ {renderedArticles}
+ </div>
+ );
+ }
+ private _scrollToHash(): void {
+ const hashWithPrefix = this.props.location.hash;
+ let hash = hashWithPrefix.slice(1);
+ if (_.isEmpty(hash)) {
+ hash = '0xProtocolWiki'; // scroll to the top
+ }
- scroller.scrollTo(hash, {
- duration: 0,
- offset: 0,
- containerId: 'documentation',
- });
- }
- private async _fetchArticlesBySectionAsync(): Promise<void> {
- const endpoint = `${configs.BACKEND_BASE_URL}${WebsitePaths.Wiki}`;
- const response = await fetch(endpoint);
- if (response.status === constants.HTTP_NO_CONTENT_STATUS_CODE) {
- // We need to backoff and try fetching again later
- this._wikiBackoffTimeoutId = window.setTimeout(() => {
- // tslint:disable-next-line:no-floating-promises
- this._fetchArticlesBySectionAsync();
- }, WIKI_NOT_READY_BACKOUT_TIMEOUT_MS);
- return;
- }
- if (response.status !== 200) {
- // TODO: Show the user an error message when the wiki fail to load
- const errMsg = await response.text();
- utils.consoleLog(`Failed to load wiki: ${response.status} ${errMsg}`);
- return;
- }
- const articlesBySection = await response.json();
- this.setState(
- {
- articlesBySection,
- },
- () => {
- this._scrollToHash();
- },
- );
- }
- private _getMenuSubsectionsBySection(articlesBySection: ArticlesBySection) {
- const sectionNames = _.keys(articlesBySection);
- const menuSubsectionsBySection: { [section: string]: string[] } = {};
- for (const sectionName of sectionNames) {
- const articles = articlesBySection[sectionName];
- const articleNames = _.map(articles, article => article.title);
- menuSubsectionsBySection[sectionName] = articleNames;
- }
- return menuSubsectionsBySection;
- }
+ scroller.scrollTo(hash, {
+ duration: 0,
+ offset: 0,
+ containerId: 'documentation',
+ });
+ }
+ private async _fetchArticlesBySectionAsync(): Promise<void> {
+ const endpoint = `${configs.BACKEND_BASE_URL}${WebsitePaths.Wiki}`;
+ const response = await fetch(endpoint);
+ if (response.status === constants.HTTP_NO_CONTENT_STATUS_CODE) {
+ // We need to backoff and try fetching again later
+ this._wikiBackoffTimeoutId = window.setTimeout(() => {
+ // tslint:disable-next-line:no-floating-promises
+ this._fetchArticlesBySectionAsync();
+ }, WIKI_NOT_READY_BACKOUT_TIMEOUT_MS);
+ return;
+ }
+ if (response.status !== 200) {
+ // TODO: Show the user an error message when the wiki fail to load
+ const errMsg = await response.text();
+ utils.consoleLog(`Failed to load wiki: ${response.status} ${errMsg}`);
+ return;
+ }
+ const articlesBySection = await response.json();
+ this.setState(
+ {
+ articlesBySection,
+ },
+ () => {
+ this._scrollToHash();
+ },
+ );
+ }
+ private _getMenuSubsectionsBySection(articlesBySection: ArticlesBySection) {
+ const sectionNames = _.keys(articlesBySection);
+ const menuSubsectionsBySection: { [section: string]: string[] } = {};
+ for (const sectionName of sectionNames) {
+ const articles = articlesBySection[sectionName];
+ const articleNames = _.map(articles, article => article.title);
+ menuSubsectionsBySection[sectionName] = articleNames;
+ }
+ return menuSubsectionsBySection;
+ }
}
diff --git a/packages/website/ts/redux/dispatcher.ts b/packages/website/ts/redux/dispatcher.ts
index 19300e242..42989e5e1 100644
--- a/packages/website/ts/redux/dispatcher.ts
+++ b/packages/website/ts/redux/dispatcher.ts
@@ -2,235 +2,235 @@ import { BigNumber } from '@0xproject/utils';
import { Dispatch } from 'redux';
import { State } from 'ts/redux/reducer';
import {
- ActionTypes,
- AssetToken,
- BlockchainErrs,
- Order,
- ProviderType,
- ScreenWidths,
- Side,
- SignatureData,
- Token,
- TokenStateByAddress,
+ ActionTypes,
+ AssetToken,
+ BlockchainErrs,
+ Order,
+ ProviderType,
+ ScreenWidths,
+ Side,
+ SignatureData,
+ Token,
+ TokenStateByAddress,
} from 'ts/types';
export class Dispatcher {
- private _dispatch: Dispatch<State>;
- constructor(dispatch: Dispatch<State>) {
- this._dispatch = dispatch;
- }
- // Portal
- public resetState() {
- this._dispatch({
- type: ActionTypes.ResetState,
- });
- }
- public updateNodeVersion(nodeVersion: string) {
- this._dispatch({
- data: nodeVersion,
- type: ActionTypes.UpdateNodeVersion,
- });
- }
- public updateScreenWidth(screenWidth: ScreenWidths) {
- this._dispatch({
- data: screenWidth,
- type: ActionTypes.UpdateScreenWidth,
- });
- }
- public swapAssetTokenSymbols() {
- this._dispatch({
- type: ActionTypes.SwapAssetTokens,
- });
- }
- public updateOrderSalt(salt: BigNumber) {
- this._dispatch({
- data: salt,
- type: ActionTypes.UpdateOrderSalt,
- });
- }
- public updateUserSuppliedOrderCache(order: Order) {
- this._dispatch({
- data: order,
- type: ActionTypes.UpdateUserSuppliedOrderCache,
- });
- }
- public updateShouldBlockchainErrDialogBeOpen(shouldBeOpen: boolean) {
- this._dispatch({
- data: shouldBeOpen,
- type: ActionTypes.UpdateShouldBlockchainErrDialogBeOpen,
- });
- }
- public updateChosenAssetToken(side: Side, token: AssetToken) {
- this._dispatch({
- data: {
- side,
- token,
- },
- type: ActionTypes.UpdateChosenAssetToken,
- });
- }
- public updateChosenAssetTokenAddress(side: Side, address: string) {
- this._dispatch({
- data: {
- address,
- side,
- },
- type: ActionTypes.UpdateChosenAssetTokenAddress,
- });
- }
- public updateOrderTakerAddress(address: string) {
- this._dispatch({
- data: address,
- type: ActionTypes.UpdateOrderTakerAddress,
- });
- }
- public updateUserAddress(address: string) {
- this._dispatch({
- data: address,
- type: ActionTypes.UpdateUserAddress,
- });
- }
- public updateOrderExpiry(unixTimestampSec: BigNumber) {
- this._dispatch({
- data: unixTimestampSec,
- type: ActionTypes.UpdateOrderExpiry,
- });
- }
- public encounteredBlockchainError(err: BlockchainErrs) {
- this._dispatch({
- data: err,
- type: ActionTypes.BlockchainErrEncountered,
- });
- }
- public updateBlockchainIsLoaded(isLoaded: boolean) {
- this._dispatch({
- data: isLoaded,
- type: ActionTypes.UpdateBlockchainIsLoaded,
- });
- }
- public addTokenToTokenByAddress(token: Token) {
- this._dispatch({
- data: token,
- type: ActionTypes.AddTokenToTokenByAddress,
- });
- }
- public removeTokenToTokenByAddress(token: Token) {
- this._dispatch({
- data: token,
- type: ActionTypes.RemoveTokenFromTokenByAddress,
- });
- }
- public clearTokenByAddress() {
- this._dispatch({
- type: ActionTypes.ClearTokenByAddress,
- });
- }
- public updateTokenByAddress(tokens: Token[]) {
- this._dispatch({
- data: tokens,
- type: ActionTypes.UpdateTokenByAddress,
- });
- }
- public updateTokenStateByAddress(tokenStateByAddress: TokenStateByAddress) {
- this._dispatch({
- data: tokenStateByAddress,
- type: ActionTypes.UpdateTokenStateByAddress,
- });
- }
- public removeFromTokenStateByAddress(tokenAddress: string) {
- this._dispatch({
- data: tokenAddress,
- type: ActionTypes.RemoveFromTokenStateByAddress,
- });
- }
- public replaceTokenAllowanceByAddress(address: string, allowance: BigNumber) {
- this._dispatch({
- data: {
- address,
- allowance,
- },
- type: ActionTypes.ReplaceTokenAllowanceByAddress,
- });
- }
- public replaceTokenBalanceByAddress(address: string, balance: BigNumber) {
- this._dispatch({
- data: {
- address,
- balance,
- },
- type: ActionTypes.ReplaceTokenBalanceByAddress,
- });
- }
- public updateTokenBalanceByAddress(address: string, balanceDelta: BigNumber) {
- this._dispatch({
- data: {
- address,
- balanceDelta,
- },
- type: ActionTypes.UpdateTokenBalanceByAddress,
- });
- }
- public updateSignatureData(signatureData: SignatureData) {
- this._dispatch({
- data: signatureData,
- type: ActionTypes.UpdateOrderSignatureData,
- });
- }
- public updateUserEtherBalance(balance: BigNumber) {
- this._dispatch({
- data: balance,
- type: ActionTypes.UpdateUserEtherBalance,
- });
- }
- public updateNetworkId(networkId: number) {
- this._dispatch({
- data: networkId,
- type: ActionTypes.UpdateNetworkId,
- });
- }
- public updateOrderFillAmount(amount: BigNumber) {
- this._dispatch({
- data: amount,
- type: ActionTypes.UpdateOrderFillAmount,
- });
- }
+ private _dispatch: Dispatch<State>;
+ constructor(dispatch: Dispatch<State>) {
+ this._dispatch = dispatch;
+ }
+ // Portal
+ public resetState() {
+ this._dispatch({
+ type: ActionTypes.ResetState,
+ });
+ }
+ public updateNodeVersion(nodeVersion: string) {
+ this._dispatch({
+ data: nodeVersion,
+ type: ActionTypes.UpdateNodeVersion,
+ });
+ }
+ public updateScreenWidth(screenWidth: ScreenWidths) {
+ this._dispatch({
+ data: screenWidth,
+ type: ActionTypes.UpdateScreenWidth,
+ });
+ }
+ public swapAssetTokenSymbols() {
+ this._dispatch({
+ type: ActionTypes.SwapAssetTokens,
+ });
+ }
+ public updateOrderSalt(salt: BigNumber) {
+ this._dispatch({
+ data: salt,
+ type: ActionTypes.UpdateOrderSalt,
+ });
+ }
+ public updateUserSuppliedOrderCache(order: Order) {
+ this._dispatch({
+ data: order,
+ type: ActionTypes.UpdateUserSuppliedOrderCache,
+ });
+ }
+ public updateShouldBlockchainErrDialogBeOpen(shouldBeOpen: boolean) {
+ this._dispatch({
+ data: shouldBeOpen,
+ type: ActionTypes.UpdateShouldBlockchainErrDialogBeOpen,
+ });
+ }
+ public updateChosenAssetToken(side: Side, token: AssetToken) {
+ this._dispatch({
+ data: {
+ side,
+ token,
+ },
+ type: ActionTypes.UpdateChosenAssetToken,
+ });
+ }
+ public updateChosenAssetTokenAddress(side: Side, address: string) {
+ this._dispatch({
+ data: {
+ address,
+ side,
+ },
+ type: ActionTypes.UpdateChosenAssetTokenAddress,
+ });
+ }
+ public updateOrderTakerAddress(address: string) {
+ this._dispatch({
+ data: address,
+ type: ActionTypes.UpdateOrderTakerAddress,
+ });
+ }
+ public updateUserAddress(address: string) {
+ this._dispatch({
+ data: address,
+ type: ActionTypes.UpdateUserAddress,
+ });
+ }
+ public updateOrderExpiry(unixTimestampSec: BigNumber) {
+ this._dispatch({
+ data: unixTimestampSec,
+ type: ActionTypes.UpdateOrderExpiry,
+ });
+ }
+ public encounteredBlockchainError(err: BlockchainErrs) {
+ this._dispatch({
+ data: err,
+ type: ActionTypes.BlockchainErrEncountered,
+ });
+ }
+ public updateBlockchainIsLoaded(isLoaded: boolean) {
+ this._dispatch({
+ data: isLoaded,
+ type: ActionTypes.UpdateBlockchainIsLoaded,
+ });
+ }
+ public addTokenToTokenByAddress(token: Token) {
+ this._dispatch({
+ data: token,
+ type: ActionTypes.AddTokenToTokenByAddress,
+ });
+ }
+ public removeTokenToTokenByAddress(token: Token) {
+ this._dispatch({
+ data: token,
+ type: ActionTypes.RemoveTokenFromTokenByAddress,
+ });
+ }
+ public clearTokenByAddress() {
+ this._dispatch({
+ type: ActionTypes.ClearTokenByAddress,
+ });
+ }
+ public updateTokenByAddress(tokens: Token[]) {
+ this._dispatch({
+ data: tokens,
+ type: ActionTypes.UpdateTokenByAddress,
+ });
+ }
+ public updateTokenStateByAddress(tokenStateByAddress: TokenStateByAddress) {
+ this._dispatch({
+ data: tokenStateByAddress,
+ type: ActionTypes.UpdateTokenStateByAddress,
+ });
+ }
+ public removeFromTokenStateByAddress(tokenAddress: string) {
+ this._dispatch({
+ data: tokenAddress,
+ type: ActionTypes.RemoveFromTokenStateByAddress,
+ });
+ }
+ public replaceTokenAllowanceByAddress(address: string, allowance: BigNumber) {
+ this._dispatch({
+ data: {
+ address,
+ allowance,
+ },
+ type: ActionTypes.ReplaceTokenAllowanceByAddress,
+ });
+ }
+ public replaceTokenBalanceByAddress(address: string, balance: BigNumber) {
+ this._dispatch({
+ data: {
+ address,
+ balance,
+ },
+ type: ActionTypes.ReplaceTokenBalanceByAddress,
+ });
+ }
+ public updateTokenBalanceByAddress(address: string, balanceDelta: BigNumber) {
+ this._dispatch({
+ data: {
+ address,
+ balanceDelta,
+ },
+ type: ActionTypes.UpdateTokenBalanceByAddress,
+ });
+ }
+ public updateSignatureData(signatureData: SignatureData) {
+ this._dispatch({
+ data: signatureData,
+ type: ActionTypes.UpdateOrderSignatureData,
+ });
+ }
+ public updateUserEtherBalance(balance: BigNumber) {
+ this._dispatch({
+ data: balance,
+ type: ActionTypes.UpdateUserEtherBalance,
+ });
+ }
+ public updateNetworkId(networkId: number) {
+ this._dispatch({
+ data: networkId,
+ type: ActionTypes.UpdateNetworkId,
+ });
+ }
+ public updateOrderFillAmount(amount: BigNumber) {
+ this._dispatch({
+ data: amount,
+ type: ActionTypes.UpdateOrderFillAmount,
+ });
+ }
- // Docs
- public updateCurrentDocsVersion(version: string) {
- this._dispatch({
- data: version,
- type: ActionTypes.UpdateLibraryVersion,
- });
- }
- public updateAvailableDocVersions(versions: string[]) {
- this._dispatch({
- data: versions,
- type: ActionTypes.UpdateAvailableLibraryVersions,
- });
- }
+ // Docs
+ public updateCurrentDocsVersion(version: string) {
+ this._dispatch({
+ data: version,
+ type: ActionTypes.UpdateLibraryVersion,
+ });
+ }
+ public updateAvailableDocVersions(versions: string[]) {
+ this._dispatch({
+ data: versions,
+ type: ActionTypes.UpdateAvailableLibraryVersions,
+ });
+ }
- // Shared
- public showFlashMessage(msg: string | React.ReactNode) {
- this._dispatch({
- data: msg,
- type: ActionTypes.ShowFlashMessage,
- });
- }
- public hideFlashMessage() {
- this._dispatch({
- type: ActionTypes.HideFlashMessage,
- });
- }
- public updateProviderType(providerType: ProviderType) {
- this._dispatch({
- type: ActionTypes.UpdateProviderType,
- data: providerType,
- });
- }
- public updateInjectedProviderName(injectedProviderName: string) {
- this._dispatch({
- type: ActionTypes.UpdateInjectedProviderName,
- data: injectedProviderName,
- });
- }
+ // Shared
+ public showFlashMessage(msg: string | React.ReactNode) {
+ this._dispatch({
+ data: msg,
+ type: ActionTypes.ShowFlashMessage,
+ });
+ }
+ public hideFlashMessage() {
+ this._dispatch({
+ type: ActionTypes.HideFlashMessage,
+ });
+ }
+ public updateProviderType(providerType: ProviderType) {
+ this._dispatch({
+ type: ActionTypes.UpdateProviderType,
+ data: providerType,
+ });
+ }
+ public updateInjectedProviderName(injectedProviderName: string) {
+ this._dispatch({
+ type: ActionTypes.UpdateInjectedProviderName,
+ data: injectedProviderName,
+ });
+ }
}
diff --git a/packages/website/ts/redux/reducer.ts b/packages/website/ts/redux/reducer.ts
index cc467eadd..06ac8b670 100644
--- a/packages/website/ts/redux/reducer.ts
+++ b/packages/website/ts/redux/reducer.ts
@@ -2,18 +2,18 @@ import { ZeroEx } from '0x.js';
import { BigNumber } from '@0xproject/utils';
import * as _ from 'lodash';
import {
- Action,
- ActionTypes,
- BlockchainErrs,
- Order,
- ProviderType,
- ScreenWidths,
- Side,
- SideToAssetToken,
- SignatureData,
- TokenByAddress,
- TokenState,
- TokenStateByAddress,
+ Action,
+ ActionTypes,
+ BlockchainErrs,
+ Order,
+ ProviderType,
+ ScreenWidths,
+ Side,
+ SideToAssetToken,
+ SignatureData,
+ TokenByAddress,
+ TokenState,
+ TokenStateByAddress,
} from 'ts/types';
import { utils } from 'ts/utils/utils';
@@ -23,367 +23,367 @@ import { utils } from 'ts/utils/utils';
const DEFAULT_DOCS_VERSION = '0.0.0';
export interface State {
- // Portal
- blockchainErr: BlockchainErrs;
- blockchainIsLoaded: boolean;
- networkId: number;
- orderExpiryTimestamp: BigNumber;
- orderFillAmount: BigNumber;
- orderTakerAddress: string;
- orderSignatureData: SignatureData;
- orderSalt: BigNumber;
- nodeVersion: string;
- screenWidth: ScreenWidths;
- shouldBlockchainErrDialogBeOpen: boolean;
- sideToAssetToken: SideToAssetToken;
- tokenByAddress: TokenByAddress;
- tokenStateByAddress: TokenStateByAddress;
- userAddress: string;
- userEtherBalance: BigNumber;
- // Note: cache of supplied orderJSON in fill order step. Do not use for anything else.
- userSuppliedOrderCache: Order;
-
- // Docs
- docsVersion: string;
- availableDocVersions: string[];
-
- // Shared
- flashMessage: string | React.ReactNode;
- providerType: ProviderType;
- injectedProviderName: string;
+ // Portal
+ blockchainErr: BlockchainErrs;
+ blockchainIsLoaded: boolean;
+ networkId: number;
+ orderExpiryTimestamp: BigNumber;
+ orderFillAmount: BigNumber;
+ orderTakerAddress: string;
+ orderSignatureData: SignatureData;
+ orderSalt: BigNumber;
+ nodeVersion: string;
+ screenWidth: ScreenWidths;
+ shouldBlockchainErrDialogBeOpen: boolean;
+ sideToAssetToken: SideToAssetToken;
+ tokenByAddress: TokenByAddress;
+ tokenStateByAddress: TokenStateByAddress;
+ userAddress: string;
+ userEtherBalance: BigNumber;
+ // Note: cache of supplied orderJSON in fill order step. Do not use for anything else.
+ userSuppliedOrderCache: Order;
+
+ // Docs
+ docsVersion: string;
+ availableDocVersions: string[];
+
+ // Shared
+ flashMessage: string | React.ReactNode;
+ providerType: ProviderType;
+ injectedProviderName: string;
}
const INITIAL_STATE: State = {
- // Portal
- blockchainErr: BlockchainErrs.NoError,
- blockchainIsLoaded: false,
- networkId: undefined,
- orderExpiryTimestamp: utils.initialOrderExpiryUnixTimestampSec(),
- orderFillAmount: undefined,
- orderSignatureData: {
- hash: '',
- r: '',
- s: '',
- v: 27,
- },
- orderTakerAddress: '',
- orderSalt: ZeroEx.generatePseudoRandomSalt(),
- nodeVersion: undefined,
- screenWidth: utils.getScreenWidth(),
- shouldBlockchainErrDialogBeOpen: false,
- sideToAssetToken: {
- [Side.Deposit]: {},
- [Side.Receive]: {},
- },
- tokenByAddress: {},
- tokenStateByAddress: {},
- userAddress: '',
- userEtherBalance: new BigNumber(0),
- userSuppliedOrderCache: undefined,
-
- // Docs
- docsVersion: DEFAULT_DOCS_VERSION,
- availableDocVersions: [DEFAULT_DOCS_VERSION],
-
- // Shared
- flashMessage: undefined,
- providerType: ProviderType.Injected,
- injectedProviderName: '',
+ // Portal
+ blockchainErr: BlockchainErrs.NoError,
+ blockchainIsLoaded: false,
+ networkId: undefined,
+ orderExpiryTimestamp: utils.initialOrderExpiryUnixTimestampSec(),
+ orderFillAmount: undefined,
+ orderSignatureData: {
+ hash: '',
+ r: '',
+ s: '',
+ v: 27,
+ },
+ orderTakerAddress: '',
+ orderSalt: ZeroEx.generatePseudoRandomSalt(),
+ nodeVersion: undefined,
+ screenWidth: utils.getScreenWidth(),
+ shouldBlockchainErrDialogBeOpen: false,
+ sideToAssetToken: {
+ [Side.Deposit]: {},
+ [Side.Receive]: {},
+ },
+ tokenByAddress: {},
+ tokenStateByAddress: {},
+ userAddress: '',
+ userEtherBalance: new BigNumber(0),
+ userSuppliedOrderCache: undefined,
+
+ // Docs
+ docsVersion: DEFAULT_DOCS_VERSION,
+ availableDocVersions: [DEFAULT_DOCS_VERSION],
+
+ // Shared
+ flashMessage: undefined,
+ providerType: ProviderType.Injected,
+ injectedProviderName: '',
};
export function reducer(state: State = INITIAL_STATE, action: Action) {
- switch (action.type) {
- // Portal
- case ActionTypes.ResetState:
- return INITIAL_STATE;
-
- case ActionTypes.UpdateOrderSalt: {
- return {
- ...state,
- orderSalt: action.data,
- };
- }
-
- case ActionTypes.UpdateNodeVersion: {
- return {
- ...state,
- nodeVersion: action.data,
- };
- }
-
- case ActionTypes.UpdateOrderFillAmount: {
- return {
- ...state,
- orderFillAmount: action.data,
- };
- }
-
- case ActionTypes.UpdateShouldBlockchainErrDialogBeOpen: {
- return {
- ...state,
- shouldBlockchainErrDialogBeOpen: action.data,
- };
- }
-
- case ActionTypes.UpdateUserEtherBalance: {
- return {
- ...state,
- userEtherBalance: action.data,
- };
- }
-
- case ActionTypes.UpdateUserSuppliedOrderCache: {
- return {
- ...state,
- userSuppliedOrderCache: action.data,
- };
- }
-
- case ActionTypes.ClearTokenByAddress: {
- return {
- ...state,
- tokenByAddress: {},
- };
- }
-
- case ActionTypes.AddTokenToTokenByAddress: {
- const newTokenByAddress = state.tokenByAddress;
- newTokenByAddress[action.data.address] = action.data;
- return {
- ...state,
- tokenByAddress: newTokenByAddress,
- };
- }
-
- case ActionTypes.RemoveTokenFromTokenByAddress: {
- const newTokenByAddress = state.tokenByAddress;
- delete newTokenByAddress[action.data.address];
- return {
- ...state,
- tokenByAddress: newTokenByAddress,
- };
- }
-
- case ActionTypes.UpdateTokenByAddress: {
- const tokenByAddress = state.tokenByAddress;
- const tokens = action.data;
- _.each(tokens, token => {
- const updatedToken = {
- ...tokenByAddress[token.address],
- ...token,
- };
- tokenByAddress[token.address] = updatedToken;
- });
- return {
- ...state,
- tokenByAddress,
- };
- }
-
- case ActionTypes.UpdateTokenStateByAddress: {
- const tokenStateByAddress = state.tokenStateByAddress;
- const updatedTokenStateByAddress = action.data;
- _.each(updatedTokenStateByAddress, (tokenState: TokenState, address: string) => {
- const updatedTokenState = {
- ...tokenStateByAddress[address],
- ...tokenState,
- };
- tokenStateByAddress[address] = updatedTokenState;
- });
- return {
- ...state,
- tokenStateByAddress,
- };
- }
-
- case ActionTypes.RemoveFromTokenStateByAddress: {
- const tokenStateByAddress = state.tokenStateByAddress;
- const tokenAddress = action.data;
- delete tokenStateByAddress[tokenAddress];
- return {
- ...state,
- tokenStateByAddress,
- };
- }
-
- case ActionTypes.ReplaceTokenAllowanceByAddress: {
- const tokenStateByAddress = state.tokenStateByAddress;
- const allowance = action.data.allowance;
- const tokenAddress = action.data.address;
- tokenStateByAddress[tokenAddress] = {
- ...tokenStateByAddress[tokenAddress],
- allowance,
- };
- return {
- ...state,
- tokenStateByAddress,
- };
- }
-
- case ActionTypes.ReplaceTokenBalanceByAddress: {
- const tokenStateByAddress = state.tokenStateByAddress;
- const balance = action.data.balance;
- const tokenAddress = action.data.address;
- tokenStateByAddress[tokenAddress] = {
- ...tokenStateByAddress[tokenAddress],
- balance,
- };
- return {
- ...state,
- tokenStateByAddress,
- };
- }
-
- case ActionTypes.UpdateTokenBalanceByAddress: {
- const tokenStateByAddress = state.tokenStateByAddress;
- const balanceDelta = action.data.balanceDelta;
- const tokenAddress = action.data.address;
- const currBalance = tokenStateByAddress[tokenAddress].balance;
- tokenStateByAddress[tokenAddress] = {
- ...tokenStateByAddress[tokenAddress],
- balance: currBalance.plus(balanceDelta),
- };
- return {
- ...state,
- tokenStateByAddress,
- };
- }
-
- case ActionTypes.UpdateOrderSignatureData: {
- return {
- ...state,
- orderSignatureData: action.data,
- };
- }
-
- case ActionTypes.UpdateScreenWidth: {
- return {
- ...state,
- screenWidth: action.data,
- };
- }
-
- case ActionTypes.UpdateBlockchainIsLoaded: {
- return {
- ...state,
- blockchainIsLoaded: action.data,
- };
- }
-
- case ActionTypes.BlockchainErrEncountered: {
- return {
- ...state,
- blockchainErr: action.data,
- };
- }
-
- case ActionTypes.UpdateNetworkId: {
- return {
- ...state,
- networkId: action.data,
- };
- }
-
- case ActionTypes.UpdateChosenAssetToken: {
- const newSideToAssetToken = {
- ...state.sideToAssetToken,
- [action.data.side]: action.data.token,
- };
- return {
- ...state,
- sideToAssetToken: newSideToAssetToken,
- };
- }
-
- case ActionTypes.UpdateChosenAssetTokenAddress: {
- const newAssetToken = state.sideToAssetToken[action.data.side];
- newAssetToken.address = action.data.address;
- const newSideToAssetToken = {
- ...state.sideToAssetToken,
- [action.data.side]: newAssetToken,
- };
- return {
- ...state,
- sideToAssetToken: newSideToAssetToken,
- };
- }
-
- case ActionTypes.SwapAssetTokens: {
- const newSideToAssetToken = {
- [Side.Deposit]: state.sideToAssetToken[Side.Receive],
- [Side.Receive]: state.sideToAssetToken[Side.Deposit],
- };
- return {
- ...state,
- sideToAssetToken: newSideToAssetToken,
- };
- }
-
- case ActionTypes.UpdateOrderExpiry: {
- return {
- ...state,
- orderExpiryTimestamp: action.data,
- };
- }
-
- case ActionTypes.UpdateOrderTakerAddress: {
- return {
- ...state,
- orderTakerAddress: action.data,
- };
- }
-
- case ActionTypes.UpdateUserAddress: {
- return {
- ...state,
- userAddress: action.data,
- };
- }
-
- // Docs
- case ActionTypes.UpdateLibraryVersion: {
- return {
- ...state,
- docsVersion: action.data,
- };
- }
- case ActionTypes.UpdateAvailableLibraryVersions: {
- return {
- ...state,
- availableDocVersions: action.data,
- };
- }
-
- // Shared
- case ActionTypes.ShowFlashMessage: {
- return {
- ...state,
- flashMessage: action.data,
- };
- }
-
- case ActionTypes.HideFlashMessage: {
- return {
- ...state,
- flashMessage: undefined,
- };
- }
-
- case ActionTypes.UpdateProviderType: {
- return {
- ...state,
- providerType: action.data,
- };
- }
-
- case ActionTypes.UpdateInjectedProviderName: {
- return {
- ...state,
- injectedProviderName: action.data,
- };
- }
-
- default:
- return state;
- }
+ switch (action.type) {
+ // Portal
+ case ActionTypes.ResetState:
+ return INITIAL_STATE;
+
+ case ActionTypes.UpdateOrderSalt: {
+ return {
+ ...state,
+ orderSalt: action.data,
+ };
+ }
+
+ case ActionTypes.UpdateNodeVersion: {
+ return {
+ ...state,
+ nodeVersion: action.data,
+ };
+ }
+
+ case ActionTypes.UpdateOrderFillAmount: {
+ return {
+ ...state,
+ orderFillAmount: action.data,
+ };
+ }
+
+ case ActionTypes.UpdateShouldBlockchainErrDialogBeOpen: {
+ return {
+ ...state,
+ shouldBlockchainErrDialogBeOpen: action.data,
+ };
+ }
+
+ case ActionTypes.UpdateUserEtherBalance: {
+ return {
+ ...state,
+ userEtherBalance: action.data,
+ };
+ }
+
+ case ActionTypes.UpdateUserSuppliedOrderCache: {
+ return {
+ ...state,
+ userSuppliedOrderCache: action.data,
+ };
+ }
+
+ case ActionTypes.ClearTokenByAddress: {
+ return {
+ ...state,
+ tokenByAddress: {},
+ };
+ }
+
+ case ActionTypes.AddTokenToTokenByAddress: {
+ const newTokenByAddress = state.tokenByAddress;
+ newTokenByAddress[action.data.address] = action.data;
+ return {
+ ...state,
+ tokenByAddress: newTokenByAddress,
+ };
+ }
+
+ case ActionTypes.RemoveTokenFromTokenByAddress: {
+ const newTokenByAddress = state.tokenByAddress;
+ delete newTokenByAddress[action.data.address];
+ return {
+ ...state,
+ tokenByAddress: newTokenByAddress,
+ };
+ }
+
+ case ActionTypes.UpdateTokenByAddress: {
+ const tokenByAddress = state.tokenByAddress;
+ const tokens = action.data;
+ _.each(tokens, token => {
+ const updatedToken = {
+ ...tokenByAddress[token.address],
+ ...token,
+ };
+ tokenByAddress[token.address] = updatedToken;
+ });
+ return {
+ ...state,
+ tokenByAddress,
+ };
+ }
+
+ case ActionTypes.UpdateTokenStateByAddress: {
+ const tokenStateByAddress = state.tokenStateByAddress;
+ const updatedTokenStateByAddress = action.data;
+ _.each(updatedTokenStateByAddress, (tokenState: TokenState, address: string) => {
+ const updatedTokenState = {
+ ...tokenStateByAddress[address],
+ ...tokenState,
+ };
+ tokenStateByAddress[address] = updatedTokenState;
+ });
+ return {
+ ...state,
+ tokenStateByAddress,
+ };
+ }
+
+ case ActionTypes.RemoveFromTokenStateByAddress: {
+ const tokenStateByAddress = state.tokenStateByAddress;
+ const tokenAddress = action.data;
+ delete tokenStateByAddress[tokenAddress];
+ return {
+ ...state,
+ tokenStateByAddress,
+ };
+ }
+
+ case ActionTypes.ReplaceTokenAllowanceByAddress: {
+ const tokenStateByAddress = state.tokenStateByAddress;
+ const allowance = action.data.allowance;
+ const tokenAddress = action.data.address;
+ tokenStateByAddress[tokenAddress] = {
+ ...tokenStateByAddress[tokenAddress],
+ allowance,
+ };
+ return {
+ ...state,
+ tokenStateByAddress,
+ };
+ }
+
+ case ActionTypes.ReplaceTokenBalanceByAddress: {
+ const tokenStateByAddress = state.tokenStateByAddress;
+ const balance = action.data.balance;
+ const tokenAddress = action.data.address;
+ tokenStateByAddress[tokenAddress] = {
+ ...tokenStateByAddress[tokenAddress],
+ balance,
+ };
+ return {
+ ...state,
+ tokenStateByAddress,
+ };
+ }
+
+ case ActionTypes.UpdateTokenBalanceByAddress: {
+ const tokenStateByAddress = state.tokenStateByAddress;
+ const balanceDelta = action.data.balanceDelta;
+ const tokenAddress = action.data.address;
+ const currBalance = tokenStateByAddress[tokenAddress].balance;
+ tokenStateByAddress[tokenAddress] = {
+ ...tokenStateByAddress[tokenAddress],
+ balance: currBalance.plus(balanceDelta),
+ };
+ return {
+ ...state,
+ tokenStateByAddress,
+ };
+ }
+
+ case ActionTypes.UpdateOrderSignatureData: {
+ return {
+ ...state,
+ orderSignatureData: action.data,
+ };
+ }
+
+ case ActionTypes.UpdateScreenWidth: {
+ return {
+ ...state,
+ screenWidth: action.data,
+ };
+ }
+
+ case ActionTypes.UpdateBlockchainIsLoaded: {
+ return {
+ ...state,
+ blockchainIsLoaded: action.data,
+ };
+ }
+
+ case ActionTypes.BlockchainErrEncountered: {
+ return {
+ ...state,
+ blockchainErr: action.data,
+ };
+ }
+
+ case ActionTypes.UpdateNetworkId: {
+ return {
+ ...state,
+ networkId: action.data,
+ };
+ }
+
+ case ActionTypes.UpdateChosenAssetToken: {
+ const newSideToAssetToken = {
+ ...state.sideToAssetToken,
+ [action.data.side]: action.data.token,
+ };
+ return {
+ ...state,
+ sideToAssetToken: newSideToAssetToken,
+ };
+ }
+
+ case ActionTypes.UpdateChosenAssetTokenAddress: {
+ const newAssetToken = state.sideToAssetToken[action.data.side];
+ newAssetToken.address = action.data.address;
+ const newSideToAssetToken = {
+ ...state.sideToAssetToken,
+ [action.data.side]: newAssetToken,
+ };
+ return {
+ ...state,
+ sideToAssetToken: newSideToAssetToken,
+ };
+ }
+
+ case ActionTypes.SwapAssetTokens: {
+ const newSideToAssetToken = {
+ [Side.Deposit]: state.sideToAssetToken[Side.Receive],
+ [Side.Receive]: state.sideToAssetToken[Side.Deposit],
+ };
+ return {
+ ...state,
+ sideToAssetToken: newSideToAssetToken,
+ };
+ }
+
+ case ActionTypes.UpdateOrderExpiry: {
+ return {
+ ...state,
+ orderExpiryTimestamp: action.data,
+ };
+ }
+
+ case ActionTypes.UpdateOrderTakerAddress: {
+ return {
+ ...state,
+ orderTakerAddress: action.data,
+ };
+ }
+
+ case ActionTypes.UpdateUserAddress: {
+ return {
+ ...state,
+ userAddress: action.data,
+ };
+ }
+
+ // Docs
+ case ActionTypes.UpdateLibraryVersion: {
+ return {
+ ...state,
+ docsVersion: action.data,
+ };
+ }
+ case ActionTypes.UpdateAvailableLibraryVersions: {
+ return {
+ ...state,
+ availableDocVersions: action.data,
+ };
+ }
+
+ // Shared
+ case ActionTypes.ShowFlashMessage: {
+ return {
+ ...state,
+ flashMessage: action.data,
+ };
+ }
+
+ case ActionTypes.HideFlashMessage: {
+ return {
+ ...state,
+ flashMessage: undefined,
+ };
+ }
+
+ case ActionTypes.UpdateProviderType: {
+ return {
+ ...state,
+ providerType: action.data,
+ };
+ }
+
+ case ActionTypes.UpdateInjectedProviderName: {
+ return {
+ ...state,
+ injectedProviderName: action.data,
+ };
+ }
+
+ default:
+ return state;
+ }
}
diff --git a/packages/website/ts/schemas/order_schema.ts b/packages/website/ts/schemas/order_schema.ts
index fd0bf113a..bfbf9eb8b 100644
--- a/packages/website/ts/schemas/order_schema.ts
+++ b/packages/website/ts/schemas/order_schema.ts
@@ -1,15 +1,15 @@
export const orderSchema = {
- id: '/Order',
- properties: {
- maker: { $ref: '/OrderTaker' },
- taker: { $ref: '/OrderTaker' },
- salt: { type: 'string' },
- signature: { $ref: '/SignatureData' },
- expiration: { type: 'string' },
- feeRecipient: { type: 'string' },
- exchangeContract: { type: 'string' },
- networkId: { type: 'number' },
- },
- required: ['maker', 'taker', 'salt', 'signature', 'expiration', 'feeRecipient', 'exchangeContract', 'networkId'],
- type: 'object',
+ id: '/Order',
+ properties: {
+ maker: { $ref: '/OrderTaker' },
+ taker: { $ref: '/OrderTaker' },
+ salt: { type: 'string' },
+ signature: { $ref: '/SignatureData' },
+ expiration: { type: 'string' },
+ feeRecipient: { type: 'string' },
+ exchangeContract: { type: 'string' },
+ networkId: { type: 'number' },
+ },
+ required: ['maker', 'taker', 'salt', 'signature', 'expiration', 'feeRecipient', 'exchangeContract', 'networkId'],
+ type: 'object',
};
diff --git a/packages/website/ts/schemas/order_taker_schema.ts b/packages/website/ts/schemas/order_taker_schema.ts
index c84ec4a9f..c784c29c5 100644
--- a/packages/website/ts/schemas/order_taker_schema.ts
+++ b/packages/website/ts/schemas/order_taker_schema.ts
@@ -1,11 +1,11 @@
export const orderTakerSchema = {
- id: '/OrderTaker',
- properties: {
- address: { type: 'string' },
- token: { $ref: '/Token' },
- amount: { type: 'string' },
- feeAmount: { type: 'string' },
- },
- required: ['address', 'token', 'amount', 'feeAmount'],
- type: 'object',
+ id: '/OrderTaker',
+ properties: {
+ address: { type: 'string' },
+ token: { $ref: '/Token' },
+ amount: { type: 'string' },
+ feeAmount: { type: 'string' },
+ },
+ required: ['address', 'token', 'amount', 'feeAmount'],
+ type: 'object',
};
diff --git a/packages/website/ts/schemas/signature_data_schema.ts b/packages/website/ts/schemas/signature_data_schema.ts
index 8cafff9e8..8d3f15926 100644
--- a/packages/website/ts/schemas/signature_data_schema.ts
+++ b/packages/website/ts/schemas/signature_data_schema.ts
@@ -1,11 +1,11 @@
export const signatureDataSchema = {
- id: '/SignatureData',
- properties: {
- hash: { type: 'string' },
- r: { type: 'string' },
- s: { type: 'string' },
- v: { type: 'number' },
- },
- required: ['hash', 'r', 's', 'v'],
- type: 'object',
+ id: '/SignatureData',
+ properties: {
+ hash: { type: 'string' },
+ r: { type: 'string' },
+ s: { type: 'string' },
+ v: { type: 'number' },
+ },
+ required: ['hash', 'r', 's', 'v'],
+ type: 'object',
};
diff --git a/packages/website/ts/schemas/token_schema.ts b/packages/website/ts/schemas/token_schema.ts
index 6a3bed786..92b53a463 100644
--- a/packages/website/ts/schemas/token_schema.ts
+++ b/packages/website/ts/schemas/token_schema.ts
@@ -1,11 +1,11 @@
export const tokenSchema = {
- id: '/Token',
- properties: {
- name: { type: 'string' },
- symbol: { type: 'string' },
- decimals: { type: 'number' },
- address: { type: 'string' },
- },
- required: ['name', 'symbol', 'decimals', 'address'],
- type: 'object',
+ id: '/Token',
+ properties: {
+ name: { type: 'string' },
+ symbol: { type: 'string' },
+ decimals: { type: 'number' },
+ address: { type: 'string' },
+ },
+ required: ['name', 'symbol', 'decimals', 'address'],
+ type: 'object',
};
diff --git a/packages/website/ts/schemas/validator.ts b/packages/website/ts/schemas/validator.ts
index a3aaafc98..5177501c6 100644
--- a/packages/website/ts/schemas/validator.ts
+++ b/packages/website/ts/schemas/validator.ts
@@ -5,15 +5,15 @@ import { signatureDataSchema } from 'ts/schemas/signature_data_schema';
import { tokenSchema } from 'ts/schemas/token_schema';
export class SchemaValidator {
- private _validator: Validator;
- constructor() {
- this._validator = new Validator();
- this._validator.addSchema(signatureDataSchema as JSONSchema, signatureDataSchema.id);
- this._validator.addSchema(tokenSchema as JSONSchema, tokenSchema.id);
- this._validator.addSchema(orderTakerSchema as JSONSchema, orderTakerSchema.id);
- this._validator.addSchema(orderSchema as JSONSchema, orderSchema.id);
- }
- public validate(instance: object, schema: Schema) {
- return this._validator.validate(instance, schema);
- }
+ private _validator: Validator;
+ constructor() {
+ this._validator = new Validator();
+ this._validator.addSchema(signatureDataSchema as JSONSchema, signatureDataSchema.id);
+ this._validator.addSchema(tokenSchema as JSONSchema, tokenSchema.id);
+ this._validator.addSchema(orderTakerSchema as JSONSchema, orderTakerSchema.id);
+ this._validator.addSchema(orderSchema as JSONSchema, orderSchema.id);
+ }
+ public validate(instance: object, schema: Schema) {
+ return this._validator.validate(instance, schema);
+ }
}
diff --git a/packages/website/ts/types.ts b/packages/website/ts/types.ts
index a853792cb..f873f95fa 100644
--- a/packages/website/ts/types.ts
+++ b/packages/website/ts/types.ts
@@ -2,680 +2,680 @@ import { BigNumber } from '@0xproject/utils';
import * as _ from 'lodash';
export enum Side {
- Receive = 'RECEIVE',
- Deposit = 'DEPOSIT',
+ Receive = 'RECEIVE',
+ Deposit = 'DEPOSIT',
}
export interface Token {
- iconUrl?: string;
- name: string;
- address: string;
- symbol: string;
- decimals: number;
- isTracked: boolean;
- isRegistered: boolean;
+ iconUrl?: string;
+ name: string;
+ address: string;
+ symbol: string;
+ decimals: number;
+ isTracked: boolean;
+ isRegistered: boolean;
}
export interface TokenByAddress {
- [address: string]: Token;
+ [address: string]: Token;
}
export interface TokenState {
- allowance: BigNumber;
- balance: BigNumber;
+ allowance: BigNumber;
+ balance: BigNumber;
}
export interface TokenStateByAddress {
- [address: string]: TokenState;
+ [address: string]: TokenState;
}
export interface AssetToken {
- address?: string;
- amount?: BigNumber;
+ address?: string;
+ amount?: BigNumber;
}
export interface SideToAssetToken {
- [side: string]: AssetToken;
+ [side: string]: AssetToken;
}
export interface SignatureData {
- hash: string;
- r: string;
- s: string;
- v: number;
+ hash: string;
+ r: string;
+ s: string;
+ v: number;
}
export interface HashData {
- depositAmount: BigNumber;
- depositTokenContractAddr: string;
- feeRecipientAddress: string;
- makerFee: BigNumber;
- orderExpiryTimestamp: BigNumber;
- orderMakerAddress: string;
- orderTakerAddress: string;
- receiveAmount: BigNumber;
- receiveTokenContractAddr: string;
- takerFee: BigNumber;
- orderSalt: BigNumber;
+ depositAmount: BigNumber;
+ depositTokenContractAddr: string;
+ feeRecipientAddress: string;
+ makerFee: BigNumber;
+ orderExpiryTimestamp: BigNumber;
+ orderMakerAddress: string;
+ orderTakerAddress: string;
+ receiveAmount: BigNumber;
+ receiveTokenContractAddr: string;
+ takerFee: BigNumber;
+ orderSalt: BigNumber;
}
export interface OrderToken {
- name: string;
- symbol: string;
- decimals: number;
- address: string;
+ name: string;
+ symbol: string;
+ decimals: number;
+ address: string;
}
export interface OrderParty {
- address: string;
- token: OrderToken;
- amount: string;
- feeAmount: string;
+ address: string;
+ token: OrderToken;
+ amount: string;
+ feeAmount: string;
}
export interface Order {
- maker: OrderParty;
- taker: OrderParty;
- expiration: string;
- feeRecipient: string;
- salt: string;
- signature: SignatureData;
- exchangeContract: string;
- networkId: number;
+ maker: OrderParty;
+ taker: OrderParty;
+ expiration: string;
+ feeRecipient: string;
+ salt: string;
+ signature: SignatureData;
+ exchangeContract: string;
+ networkId: number;
}
export interface Fill {
- logIndex: number;
- maker: string;
- taker: string;
- makerToken: string;
- takerToken: string;
- filledMakerTokenAmount: BigNumber;
- filledTakerTokenAmount: BigNumber;
- paidMakerFee: BigNumber;
- paidTakerFee: BigNumber;
- orderHash: string;
- transactionHash: string;
- blockTimestamp: number;
+ logIndex: number;
+ maker: string;
+ taker: string;
+ makerToken: string;
+ takerToken: string;
+ filledMakerTokenAmount: BigNumber;
+ filledTakerTokenAmount: BigNumber;
+ paidMakerFee: BigNumber;
+ paidTakerFee: BigNumber;
+ orderHash: string;
+ transactionHash: string;
+ blockTimestamp: number;
}
export enum BalanceErrs {
- incorrectNetworkForFaucet,
- faucetRequestFailed,
- faucetQueueIsFull,
- mintingFailed,
- sendFailed,
- allowanceSettingFailed,
+ incorrectNetworkForFaucet,
+ faucetRequestFailed,
+ faucetQueueIsFull,
+ mintingFailed,
+ sendFailed,
+ allowanceSettingFailed,
}
export enum ActionTypes {
- // Portal
- UpdateScreenWidth = 'UPDATE_SCREEN_WIDTH',
- UpdateNodeVersion = 'UPDATE_NODE_VERSION',
- ResetState = 'RESET_STATE',
- AddTokenToTokenByAddress = 'ADD_TOKEN_TO_TOKEN_BY_ADDRESS',
- BlockchainErrEncountered = 'BLOCKCHAIN_ERR_ENCOUNTERED',
- ClearTokenByAddress = 'CLEAR_TOKEN_BY_ADDRESS',
- UpdateBlockchainIsLoaded = 'UPDATE_BLOCKCHAIN_IS_LOADED',
- UpdateNetworkId = 'UPDATE_NETWORK_ID',
- UpdateChosenAssetToken = 'UPDATE_CHOSEN_ASSET_TOKEN',
- UpdateChosenAssetTokenAddress = 'UPDATE_CHOSEN_ASSET_TOKEN_ADDRESS',
- UpdateOrderTakerAddress = 'UPDATE_ORDER_TAKER_ADDRESS',
- UpdateOrderSalt = 'UPDATE_ORDER_SALT',
- UpdateOrderSignatureData = 'UPDATE_ORDER_SIGNATURE_DATA',
- UpdateTokenByAddress = 'UPDATE_TOKEN_BY_ADDRESS',
- RemoveTokenFromTokenByAddress = 'REMOVE_TOKEN_FROM_TOKEN_BY_ADDRESS',
- UpdateTokenStateByAddress = 'UPDATE_TOKEN_STATE_BY_ADDRESS',
- RemoveFromTokenStateByAddress = 'REMOVE_FROM_TOKEN_STATE_BY_ADDRESS',
- ReplaceTokenAllowanceByAddress = 'REPLACE_TOKEN_ALLOWANCE_BY_ADDRESS',
- ReplaceTokenBalanceByAddress = 'REPLACE_TOKEN_BALANCE_BY_ADDRESS',
- UpdateTokenBalanceByAddress = 'UPDATE_TOKEN_BALANCE_BY_ADDRESS',
- UpdateOrderExpiry = 'UPDATE_ORDER_EXPIRY',
- SwapAssetTokens = 'SWAP_ASSET_TOKENS',
- UpdateUserAddress = 'UPDATE_USER_ADDRESS',
- UpdateUserEtherBalance = 'UPDATE_USER_ETHER_BALANCE',
- UpdateUserSuppliedOrderCache = 'UPDATE_USER_SUPPLIED_ORDER_CACHE',
- UpdateOrderFillAmount = 'UPDATE_ORDER_FILL_AMOUNT',
- UpdateShouldBlockchainErrDialogBeOpen = 'UPDATE_SHOULD_BLOCKCHAIN_ERR_DIALOG_BE_OPEN',
-
- // Docs
- UpdateLibraryVersion = 'UPDATE_LIBRARY_VERSION',
- UpdateAvailableLibraryVersions = 'UPDATE_AVAILABLE_LIBRARY_VERSIONS',
-
- // Shared
- ShowFlashMessage = 'SHOW_FLASH_MESSAGE',
- HideFlashMessage = 'HIDE_FLASH_MESSAGE',
- UpdateProviderType = 'UPDATE_PROVIDER_TYPE',
- UpdateInjectedProviderName = 'UPDATE_INJECTED_PROVIDER_NAME',
+ // Portal
+ UpdateScreenWidth = 'UPDATE_SCREEN_WIDTH',
+ UpdateNodeVersion = 'UPDATE_NODE_VERSION',
+ ResetState = 'RESET_STATE',
+ AddTokenToTokenByAddress = 'ADD_TOKEN_TO_TOKEN_BY_ADDRESS',
+ BlockchainErrEncountered = 'BLOCKCHAIN_ERR_ENCOUNTERED',
+ ClearTokenByAddress = 'CLEAR_TOKEN_BY_ADDRESS',
+ UpdateBlockchainIsLoaded = 'UPDATE_BLOCKCHAIN_IS_LOADED',
+ UpdateNetworkId = 'UPDATE_NETWORK_ID',
+ UpdateChosenAssetToken = 'UPDATE_CHOSEN_ASSET_TOKEN',
+ UpdateChosenAssetTokenAddress = 'UPDATE_CHOSEN_ASSET_TOKEN_ADDRESS',
+ UpdateOrderTakerAddress = 'UPDATE_ORDER_TAKER_ADDRESS',
+ UpdateOrderSalt = 'UPDATE_ORDER_SALT',
+ UpdateOrderSignatureData = 'UPDATE_ORDER_SIGNATURE_DATA',
+ UpdateTokenByAddress = 'UPDATE_TOKEN_BY_ADDRESS',
+ RemoveTokenFromTokenByAddress = 'REMOVE_TOKEN_FROM_TOKEN_BY_ADDRESS',
+ UpdateTokenStateByAddress = 'UPDATE_TOKEN_STATE_BY_ADDRESS',
+ RemoveFromTokenStateByAddress = 'REMOVE_FROM_TOKEN_STATE_BY_ADDRESS',
+ ReplaceTokenAllowanceByAddress = 'REPLACE_TOKEN_ALLOWANCE_BY_ADDRESS',
+ ReplaceTokenBalanceByAddress = 'REPLACE_TOKEN_BALANCE_BY_ADDRESS',
+ UpdateTokenBalanceByAddress = 'UPDATE_TOKEN_BALANCE_BY_ADDRESS',
+ UpdateOrderExpiry = 'UPDATE_ORDER_EXPIRY',
+ SwapAssetTokens = 'SWAP_ASSET_TOKENS',
+ UpdateUserAddress = 'UPDATE_USER_ADDRESS',
+ UpdateUserEtherBalance = 'UPDATE_USER_ETHER_BALANCE',
+ UpdateUserSuppliedOrderCache = 'UPDATE_USER_SUPPLIED_ORDER_CACHE',
+ UpdateOrderFillAmount = 'UPDATE_ORDER_FILL_AMOUNT',
+ UpdateShouldBlockchainErrDialogBeOpen = 'UPDATE_SHOULD_BLOCKCHAIN_ERR_DIALOG_BE_OPEN',
+
+ // Docs
+ UpdateLibraryVersion = 'UPDATE_LIBRARY_VERSION',
+ UpdateAvailableLibraryVersions = 'UPDATE_AVAILABLE_LIBRARY_VERSIONS',
+
+ // Shared
+ ShowFlashMessage = 'SHOW_FLASH_MESSAGE',
+ HideFlashMessage = 'HIDE_FLASH_MESSAGE',
+ UpdateProviderType = 'UPDATE_PROVIDER_TYPE',
+ UpdateInjectedProviderName = 'UPDATE_INJECTED_PROVIDER_NAME',
}
export interface Action {
- type: ActionTypes;
- data?: any;
+ type: ActionTypes;
+ data?: any;
}
export interface TrackedTokensByNetworkId {
- [networkId: number]: Token[];
+ [networkId: number]: Token[];
}
export interface TrackedTokensByUserAddress {
- [userAddress: string]: TrackedTokensByNetworkId;
+ [userAddress: string]: TrackedTokensByNetworkId;
}
export interface Styles {
- [name: string]: React.CSSProperties;
+ [name: string]: React.CSSProperties;
}
export interface ProfileInfo {
- name: string;
- title?: string;
- description: string;
- image: string;
- linkedIn?: string;
- github?: string;
- angellist?: string;
- medium?: string;
- twitter?: string;
+ name: string;
+ title?: string;
+ description: string;
+ image: string;
+ linkedIn?: string;
+ github?: string;
+ angellist?: string;
+ medium?: string;
+ twitter?: string;
}
export interface Partner {
- name: string;
- logo: string;
- url: string;
+ name: string;
+ logo: string;
+ url: string;
}
export interface Statistic {
- title: string;
- figure: string;
+ title: string;
+ figure: string;
}
export interface StatisticByKey {
- [key: string]: Statistic;
+ [key: string]: Statistic;
}
export interface ERC20MarketInfo {
- etherMarketCapUsd: number;
- numLiquidERC20Tokens: number;
- marketCapERC20TokensUsd: number;
+ etherMarketCapUsd: number;
+ numLiquidERC20Tokens: number;
+ marketCapERC20TokensUsd: number;
}
export enum ExchangeContractErrs {
- OrderFillExpired = 'ORDER_FILL_EXPIRED',
- OrderAlreadyCancelledOrFilled = 'ORDER_ALREADY_CANCELLED_OR_FILLED',
- OrderRemainingFillAmountZero = 'ORDER_REMAINING_FILL_AMOUNT_ZERO',
- OrderFillRoundingError = 'ORDER_FILL_ROUNDING_ERROR',
- FillBalanceAllowanceError = 'FILL_BALANCE_ALLOWANCE_ERROR',
- InsufficientTakerBalance = 'INSUFFICIENT_TAKER_BALANCE',
- InsufficientTakerAllowance = 'INSUFFICIENT_TAKER_ALLOWANCE',
- InsufficientMakerBalance = 'INSUFFICIENT_MAKER_BALANCE',
- InsufficientMakerAllowance = 'INSUFFICIENT_MAKER_ALLOWANCE',
- TransactionSenderIsNotFillOrderTaker = 'TRANSACTION_SENDER_IS_NOT_FILL_ORDER_TAKER',
- InsufficientRemainingFillAmount = 'INSUFFICIENT_REMAINING_FILL_AMOUNT',
+ OrderFillExpired = 'ORDER_FILL_EXPIRED',
+ OrderAlreadyCancelledOrFilled = 'ORDER_ALREADY_CANCELLED_OR_FILLED',
+ OrderRemainingFillAmountZero = 'ORDER_REMAINING_FILL_AMOUNT_ZERO',
+ OrderFillRoundingError = 'ORDER_FILL_ROUNDING_ERROR',
+ FillBalanceAllowanceError = 'FILL_BALANCE_ALLOWANCE_ERROR',
+ InsufficientTakerBalance = 'INSUFFICIENT_TAKER_BALANCE',
+ InsufficientTakerAllowance = 'INSUFFICIENT_TAKER_ALLOWANCE',
+ InsufficientMakerBalance = 'INSUFFICIENT_MAKER_BALANCE',
+ InsufficientMakerAllowance = 'INSUFFICIENT_MAKER_ALLOWANCE',
+ TransactionSenderIsNotFillOrderTaker = 'TRANSACTION_SENDER_IS_NOT_FILL_ORDER_TAKER',
+ InsufficientRemainingFillAmount = 'INSUFFICIENT_REMAINING_FILL_AMOUNT',
}
export interface ContractResponse {
- logs: ContractEvent[];
+ logs: ContractEvent[];
}
export interface ContractEvent {
- event: string;
- args: any;
+ event: string;
+ args: any;
}
export type InputErrMsg = React.ReactNode | string | undefined;
export type ValidatedBigNumberCallback = (isValid: boolean, amount?: BigNumber) => void;
export enum ScreenWidths {
- Sm = 'SM',
- Md = 'MD',
- Lg = 'LG',
+ Sm = 'SM',
+ Md = 'MD',
+ Lg = 'LG',
}
export enum AlertTypes {
- ERROR,
- SUCCESS,
+ ERROR,
+ SUCCESS,
}
export enum EtherscanLinkSuffixes {
- Address = 'address',
- Tx = 'tx',
+ Address = 'address',
+ Tx = 'tx',
}
export enum BlockchainErrs {
- AContractNotDeployedOnNetwork = 'A_CONTRACT_NOT_DEPLOYED_ON_NETWORK',
- DisconnectedFromEthereumNode = 'DISCONNECTED_FROM_ETHEREUM_NODE',
- NoError = 'NO_ERROR',
+ AContractNotDeployedOnNetwork = 'A_CONTRACT_NOT_DEPLOYED_ON_NETWORK',
+ DisconnectedFromEthereumNode = 'DISCONNECTED_FROM_ETHEREUM_NODE',
+ NoError = 'NO_ERROR',
}
export enum BlockchainCallErrs {
- ContractDoesNotExist = 'CONTRACT_DOES_NOT_EXIST',
- UserHasNoAssociatedAddresses = 'USER_HAS_NO_ASSOCIATED_ADDRESSES',
- UnhandledError = 'UNHANDLED_ERROR',
- TokenAddressIsInvalid = 'TOKEN_ADDRESS_IS_INVALID',
+ ContractDoesNotExist = 'CONTRACT_DOES_NOT_EXIST',
+ UserHasNoAssociatedAddresses = 'USER_HAS_NO_ASSOCIATED_ADDRESSES',
+ UnhandledError = 'UNHANDLED_ERROR',
+ TokenAddressIsInvalid = 'TOKEN_ADDRESS_IS_INVALID',
}
// Exception: We don't make the values uppercase because these KindString's need to
// match up those returned by TypeDoc
export enum KindString {
- Constructor = 'Constructor',
- Property = 'Property',
- Method = 'Method',
- Interface = 'Interface',
- TypeAlias = 'Type alias',
- Variable = 'Variable',
- Function = 'Function',
- Enumeration = 'Enumeration',
+ Constructor = 'Constructor',
+ Property = 'Property',
+ Method = 'Method',
+ Interface = 'Interface',
+ TypeAlias = 'Type alias',
+ Variable = 'Variable',
+ Function = 'Function',
+ Enumeration = 'Enumeration',
}
export interface EnumValue {
- name: string;
- defaultValue?: string;
+ name: string;
+ defaultValue?: string;
}
export enum Environments {
- DEVELOPMENT,
- PRODUCTION,
+ DEVELOPMENT,
+ PRODUCTION,
}
export type ContractInstance = any; // TODO: add type definition for Contract
export interface TypeDocType {
- type: TypeDocTypes;
- value: string;
- name: string;
- types: TypeDocType[];
- typeArguments?: TypeDocType[];
- declaration: TypeDocNode;
- elementType?: TypeDocType;
+ type: TypeDocTypes;
+ value: string;
+ name: string;
+ types: TypeDocType[];
+ typeArguments?: TypeDocType[];
+ declaration: TypeDocNode;
+ elementType?: TypeDocType;
}
export interface TypeDocFlags {
- isStatic?: boolean;
- isOptional?: boolean;
- isPublic?: boolean;
+ isStatic?: boolean;
+ isOptional?: boolean;
+ isPublic?: boolean;
}
export interface TypeDocGroup {
- title: string;
- children: number[];
+ title: string;
+ children: number[];
}
export interface TypeDocNode {
- id?: number;
- name?: string;
- kind?: string;
- defaultValue?: string;
- kindString?: string;
- type?: TypeDocType;
- fileName?: string;
- line?: number;
- comment?: TypeDocNode;
- text?: string;
- shortText?: string;
- returns?: string;
- declaration: TypeDocNode;
- flags?: TypeDocFlags;
- indexSignature?: TypeDocNode[];
- signatures?: TypeDocNode[];
- parameters?: TypeDocNode[];
- typeParameter?: TypeDocNode[];
- sources?: TypeDocNode[];
- children?: TypeDocNode[];
- groups?: TypeDocGroup[];
+ id?: number;
+ name?: string;
+ kind?: string;
+ defaultValue?: string;
+ kindString?: string;
+ type?: TypeDocType;
+ fileName?: string;
+ line?: number;
+ comment?: TypeDocNode;
+ text?: string;
+ shortText?: string;
+ returns?: string;
+ declaration: TypeDocNode;
+ flags?: TypeDocFlags;
+ indexSignature?: TypeDocNode[];
+ signatures?: TypeDocNode[];
+ parameters?: TypeDocNode[];
+ typeParameter?: TypeDocNode[];
+ sources?: TypeDocNode[];
+ children?: TypeDocNode[];
+ groups?: TypeDocGroup[];
}
export enum TypeDocTypes {
- Intrinsic = 'intrinsic',
- Reference = 'reference',
- Array = 'array',
- StringLiteral = 'stringLiteral',
- Reflection = 'reflection',
- Union = 'union',
- TypeParameter = 'typeParameter',
- Unknown = 'unknown',
+ Intrinsic = 'intrinsic',
+ Reference = 'reference',
+ Array = 'array',
+ StringLiteral = 'stringLiteral',
+ Reflection = 'reflection',
+ Union = 'union',
+ TypeParameter = 'typeParameter',
+ Unknown = 'unknown',
}
export interface DocAgnosticFormat {
- [sectionName: string]: DocSection;
+ [sectionName: string]: DocSection;
}
export interface DocSection {
- comment: string;
- constructors: Array<TypescriptMethod | SolidityMethod>;
- methods: Array<TypescriptMethod | SolidityMethod>;
- properties: Property[];
- types: CustomType[];
- events?: Event[];
+ comment: string;
+ constructors: Array<TypescriptMethod | SolidityMethod>;
+ methods: Array<TypescriptMethod | SolidityMethod>;
+ properties: Property[];
+ types: CustomType[];
+ events?: Event[];
}
export interface Event {
- name: string;
- eventArgs: EventArg[];
+ name: string;
+ eventArgs: EventArg[];
}
export interface EventArg {
- isIndexed: boolean;
- name: string;
- type: Type;
+ isIndexed: boolean;
+ name: string;
+ type: Type;
}
export interface Property {
- name: string;
- type: Type;
- source?: Source;
- comment?: string;
+ name: string;
+ type: Type;
+ source?: Source;
+ comment?: string;
}
export interface BaseMethod {
- isConstructor: boolean;
- name: string;
- returnComment?: string | undefined;
- callPath: string;
- parameters: Parameter[];
- returnType: Type;
- comment?: string;
+ isConstructor: boolean;
+ name: string;
+ returnComment?: string | undefined;
+ callPath: string;
+ parameters: Parameter[];
+ returnType: Type;
+ comment?: string;
}
export interface TypescriptMethod extends BaseMethod {
- source?: Source;
- isStatic?: boolean;
- typeParameter?: TypeParameter;
+ source?: Source;
+ isStatic?: boolean;
+ typeParameter?: TypeParameter;
}
export interface SolidityMethod extends BaseMethod {
- isConstant?: boolean;
- isPayable?: boolean;
+ isConstant?: boolean;
+ isPayable?: boolean;
}
export interface Source {
- fileName: string;
- line: number;
+ fileName: string;
+ line: number;
}
export interface Parameter {
- name: string;
- comment: string;
- isOptional: boolean;
- type: Type;
+ name: string;
+ comment: string;
+ isOptional: boolean;
+ type: Type;
}
export interface TypeParameter {
- name: string;
- type: Type;
+ name: string;
+ type: Type;
}
export interface Type {
- name: string;
- typeDocType: TypeDocTypes;
- value?: string;
- typeArguments?: Type[];
- elementType?: ElementType;
- types?: Type[];
- method?: TypescriptMethod;
+ name: string;
+ typeDocType: TypeDocTypes;
+ value?: string;
+ typeArguments?: Type[];
+ elementType?: ElementType;
+ types?: Type[];
+ method?: TypescriptMethod;
}
export interface ElementType {
- name: string;
- typeDocType: TypeDocTypes;
+ name: string;
+ typeDocType: TypeDocTypes;
}
export interface IndexSignature {
- keyName: string;
- keyType: Type;
- valueName: string;
+ keyName: string;
+ keyType: Type;
+ valueName: string;
}
export interface CustomType {
- name: string;
- kindString: string;
- type?: Type;
- method?: TypescriptMethod;
- indexSignature?: IndexSignature;
- defaultValue?: string;
- comment?: string;
- children?: CustomTypeChild[];
+ name: string;
+ kindString: string;
+ type?: Type;
+ method?: TypescriptMethod;
+ indexSignature?: IndexSignature;
+ defaultValue?: string;
+ comment?: string;
+ children?: CustomTypeChild[];
}
export interface CustomTypeChild {
- name: string;
- type?: Type;
- defaultValue?: string;
+ name: string;
+ type?: Type;
+ defaultValue?: string;
}
export interface FAQQuestion {
- prompt: string;
- answer: React.ReactNode;
+ prompt: string;
+ answer: React.ReactNode;
}
export interface FAQSection {
- name: string;
- questions: FAQQuestion[];
+ name: string;
+ questions: FAQQuestion[];
}
export interface S3FileObject {
- Key: {
- _text: string;
- };
+ Key: {
+ _text: string;
+ };
}
export interface MenuSubsectionsBySection {
- [section: string]: string[];
+ [section: string]: string[];
}
export enum ProviderType {
- Injected = 'INJECTED',
- Ledger = 'LEDGER',
+ Injected = 'INJECTED',
+ Ledger = 'LEDGER',
}
export interface Fact {
- title: string;
- explanation: string;
- image: string;
+ title: string;
+ explanation: string;
+ image: string;
}
interface LedgerGetAddressResult {
- address: string;
+ address: string;
}
interface LedgerSignResult {
- v: string;
- r: string;
- s: string;
+ v: string;
+ r: string;
+ s: string;
}
interface LedgerCommunication {
- close_async: () => Promise<void>;
+ close_async: () => Promise<void>;
}
export interface LedgerEthConnection {
- getAddress_async: (
- derivationPath: string,
- askForDeviceConfirmation: boolean,
- shouldGetChainCode: boolean,
- ) => Promise<LedgerGetAddressResult>;
- signPersonalMessage_async: (derivationPath: string, messageHex: string) => Promise<LedgerSignResult>;
- signTransaction_async: (derivationPath: string, txHex: string) => Promise<LedgerSignResult>;
- comm: LedgerCommunication;
+ getAddress_async: (
+ derivationPath: string,
+ askForDeviceConfirmation: boolean,
+ shouldGetChainCode: boolean,
+ ) => Promise<LedgerGetAddressResult>;
+ signPersonalMessage_async: (derivationPath: string, messageHex: string) => Promise<LedgerSignResult>;
+ signTransaction_async: (derivationPath: string, txHex: string) => Promise<LedgerSignResult>;
+ comm: LedgerCommunication;
}
export interface SignPersonalMessageParams {
- data: string;
+ data: string;
}
export interface TxParams {
- nonce: string;
- gasPrice?: number;
- gasLimit: string;
- to: string;
- value?: string;
- data?: string;
- chainId: number; // EIP 155 chainId - mainnet: 1, ropsten: 3
+ nonce: string;
+ gasPrice?: number;
+ gasLimit: string;
+ to: string;
+ value?: string;
+ data?: string;
+ chainId: number; // EIP 155 chainId - mainnet: 1, ropsten: 3
}
export interface PublicNodeUrlsByNetworkId {
- [networkId: number]: string[];
+ [networkId: number]: string[];
}
export interface JSONRPCPayload {
- params: any[];
- method: string;
+ params: any[];
+ method: string;
}
export interface BlogPost {
- image: string;
- date: string;
- title: string;
- description: string;
- url: string;
+ image: string;
+ date: string;
+ title: string;
+ description: string;
+ url: string;
}
export interface TypeDefinitionByName {
- [typeName: string]: CustomType;
+ [typeName: string]: CustomType;
}
export interface Article {
- section: string;
- title: string;
- content: string;
- fileName: string;
+ section: string;
+ title: string;
+ content: string;
+ fileName: string;
}
export interface ArticlesBySection {
- [section: string]: Article[];
+ [section: string]: Article[];
}
export interface DialogConfigs {
- title: string;
- isModal: boolean;
- actions: any[];
+ title: string;
+ isModal: boolean;
+ actions: any[];
}
export enum TokenVisibility {
- ALL = 'ALL',
- UNTRACKED = 'UNTRACKED',
- TRACKED = 'TRACKED',
+ ALL = 'ALL',
+ UNTRACKED = 'UNTRACKED',
+ TRACKED = 'TRACKED',
}
export enum HeaderSizes {
- H1 = 'h1',
- H2 = 'h2',
- H3 = 'h3',
+ H1 = 'h1',
+ H2 = 'h2',
+ H3 = 'h3',
}
export interface DoxityDocObj {
- [contractName: string]: DoxityContractObj;
+ [contractName: string]: DoxityContractObj;
}
export interface DoxityContractObj {
- title: string;
- fileName: string;
- name: string;
- abiDocs: DoxityAbiDoc[];
+ title: string;
+ fileName: string;
+ name: string;
+ abiDocs: DoxityAbiDoc[];
}
export interface DoxityAbiDoc {
- constant: boolean;
- inputs: DoxityInput[];
- name: string;
- outputs: DoxityOutput[];
- payable: boolean;
- type: string;
- details?: string;
- return?: string;
+ constant: boolean;
+ inputs: DoxityInput[];
+ name: string;
+ outputs: DoxityOutput[];
+ payable: boolean;
+ type: string;
+ details?: string;
+ return?: string;
}
export interface DoxityOutput {
- name: string;
- type: string;
+ name: string;
+ type: string;
}
export interface DoxityInput {
- name: string;
- type: string;
- description: string;
- indexed?: boolean;
+ name: string;
+ type: string;
+ description: string;
+ indexed?: boolean;
}
export interface VersionToFileName {
- [version: string]: string;
+ [version: string]: string;
}
export enum Docs {
- ZeroExJs,
- SmartContracts,
+ ZeroExJs,
+ SmartContracts,
}
export interface ContractAddresses {
- [version: string]: {
- [network: string]: AddressByContractName;
- };
+ [version: string]: {
+ [network: string]: AddressByContractName;
+ };
}
export interface AddressByContractName {
- [contractName: string]: string;
+ [contractName: string]: string;
}
export enum Networks {
- mainnet = 'Mainnet',
- kovan = 'Kovan',
- ropsten = 'Ropsten',
- rinkeby = 'Rinkeby',
+ mainnet = 'Mainnet',
+ kovan = 'Kovan',
+ ropsten = 'Ropsten',
+ rinkeby = 'Rinkeby',
}
export enum AbiTypes {
- Constructor = 'constructor',
- Function = 'function',
- Event = 'event',
+ Constructor = 'constructor',
+ Function = 'function',
+ Event = 'event',
}
export enum WebsitePaths {
- Portal = '/portal',
- Wiki = '/wiki',
- ZeroExJs = '/docs/0xjs',
- Home = '/',
- FAQ = '/faq',
- About = '/about',
- Whitepaper = '/pdfs/0x_white_paper.pdf',
- SmartContracts = '/docs/contracts',
- Connect = '/docs/connect',
+ Portal = '/portal',
+ Wiki = '/wiki',
+ ZeroExJs = '/docs/0xjs',
+ Home = '/',
+ FAQ = '/faq',
+ About = '/about',
+ Whitepaper = '/pdfs/0x_white_paper.pdf',
+ SmartContracts = '/docs/contracts',
+ Connect = '/docs/connect',
}
export interface DocsMenu {
- [sectionName: string]: string[];
+ [sectionName: string]: string[];
}
export interface SectionsMap {
- [sectionName: string]: string;
+ [sectionName: string]: string;
}
export interface DocsInfoConfig {
- displayName: string;
- packageUrl: string;
- websitePath: string;
- docsJsonRoot: string;
- menu: DocsMenu;
- sections: SectionsMap;
- sectionNameToMarkdown: { [sectionName: string]: string };
- visibleConstructors: string[];
- convertToDocAgnosticFormatFn: (docObj: DoxityDocObj | TypeDocNode, docsInfo?: any) => DocAgnosticFormat;
- subPackageName?: string;
- publicTypes?: string[];
- sectionNameToModulePath?: { [sectionName: string]: string[] };
- menuSubsectionToVersionWhenIntroduced?: { [sectionName: string]: string };
+ displayName: string;
+ packageUrl: string;
+ websitePath: string;
+ docsJsonRoot: string;
+ menu: DocsMenu;
+ sections: SectionsMap;
+ sectionNameToMarkdown: { [sectionName: string]: string };
+ visibleConstructors: string[];
+ convertToDocAgnosticFormatFn: (docObj: DoxityDocObj | TypeDocNode, docsInfo?: any) => DocAgnosticFormat;
+ subPackageName?: string;
+ publicTypes?: string[];
+ sectionNameToModulePath?: { [sectionName: string]: string[] };
+ menuSubsectionToVersionWhenIntroduced?: { [sectionName: string]: string };
}
export interface TimestampMsRange {
- startTimestampMs: number;
- endTimestampMs: number;
+ startTimestampMs: number;
+ endTimestampMs: number;
}
export interface OutdatedWrappedEtherByNetworkId {
- [networkId: number]: {
- address: string;
- timestampMsRange: TimestampMsRange;
- };
+ [networkId: number]: {
+ address: string;
+ timestampMsRange: TimestampMsRange;
+ };
}
export enum SmartContractDocSections {
- Introduction = 'Introduction',
- Exchange = 'Exchange',
- TokenTransferProxy = 'TokenTransferProxy',
- TokenRegistry = 'TokenRegistry',
- ZRXToken = 'ZRXToken',
+ Introduction = 'Introduction',
+ Exchange = 'Exchange',
+ TokenTransferProxy = 'TokenTransferProxy',
+ TokenRegistry = 'TokenRegistry',
+ ZRXToken = 'ZRXToken',
}
// tslint:disable:max-file-line-count
diff --git a/packages/website/ts/utils/colors.ts b/packages/website/ts/utils/colors.ts
index dabc1fd54..58ce667e3 100644
--- a/packages/website/ts/utils/colors.ts
+++ b/packages/website/ts/utils/colors.ts
@@ -1,43 +1,43 @@
import { colors as materialUiColors } from 'material-ui/styles';
export const colors = {
- ...materialUiColors,
- grey50: '#FAFAFA',
- grey100: '#F5F5F5',
- lightestGrey: '#F0F0F0',
- greyishPink: '#E6E5E5',
- grey300: '#E0E0E0',
- beigeWhite: '#E4E4E4',
- grey400: '#BDBDBD',
- lightGrey: '#BBBBBB',
- grey500: '#9E9E9E',
- grey: '#A5A5A5',
- darkGrey: '#818181',
- landingLinkGrey: '#919191',
- grey700: '#616161',
- grey800: '#424242',
- darkerGrey: '#393939',
- heroGrey: '#404040',
- projectsGrey: '#343333',
- darkestGrey: '#272727',
- dharmaDarkGrey: '#252525',
- lightBlue: '#60A4F4',
- lightBlueA700: '#0091EA',
- darkBlue: '#4D5481',
- turquois: '#058789',
- lightPurple: '#A81CA6',
- purple: '#690596',
- red200: '#EF9A9A',
- red: '#E91751',
- red500: '#F44336',
- red600: '#E53935',
- limeGreen: '#66DE75',
- lightGreen: '#4DC55C',
- lightestGreen: '#89C774',
- brightGreen: '#00C33E',
- green400: '#66BB6A',
- green: '#4DA24B',
- amber600: '#FFB300',
- orange: '#E69D00',
- amber800: '#FF8F00',
+ ...materialUiColors,
+ grey50: '#FAFAFA',
+ grey100: '#F5F5F5',
+ lightestGrey: '#F0F0F0',
+ greyishPink: '#E6E5E5',
+ grey300: '#E0E0E0',
+ beigeWhite: '#E4E4E4',
+ grey400: '#BDBDBD',
+ lightGrey: '#BBBBBB',
+ grey500: '#9E9E9E',
+ grey: '#A5A5A5',
+ darkGrey: '#818181',
+ landingLinkGrey: '#919191',
+ grey700: '#616161',
+ grey800: '#424242',
+ darkerGrey: '#393939',
+ heroGrey: '#404040',
+ projectsGrey: '#343333',
+ darkestGrey: '#272727',
+ dharmaDarkGrey: '#252525',
+ lightBlue: '#60A4F4',
+ lightBlueA700: '#0091EA',
+ darkBlue: '#4D5481',
+ turquois: '#058789',
+ lightPurple: '#A81CA6',
+ purple: '#690596',
+ red200: '#EF9A9A',
+ red: '#E91751',
+ red500: '#F44336',
+ red600: '#E53935',
+ limeGreen: '#66DE75',
+ lightGreen: '#4DC55C',
+ lightestGreen: '#89C774',
+ brightGreen: '#00C33E',
+ green400: '#66BB6A',
+ green: '#4DA24B',
+ amber600: '#FFB300',
+ orange: '#E69D00',
+ amber800: '#FF8F00',
};
diff --git a/packages/website/ts/utils/configs.ts b/packages/website/ts/utils/configs.ts
index 6327ea05a..3d37a89ab 100644
--- a/packages/website/ts/utils/configs.ts
+++ b/packages/website/ts/utils/configs.ts
@@ -1,126 +1,126 @@
import * as _ from 'lodash';
import {
- ContractAddresses,
- Environments,
- Networks,
- OutdatedWrappedEtherByNetworkId,
- PublicNodeUrlsByNetworkId,
- SmartContractDocSections,
+ ContractAddresses,
+ Environments,
+ Networks,
+ OutdatedWrappedEtherByNetworkId,
+ PublicNodeUrlsByNetworkId,
+ SmartContractDocSections,
} from 'ts/types';
const BASE_URL = window.location.origin;
const isDevelopment = _.includes(
- ['https://0xproject.localhost:3572', 'https://localhost:3572', 'https://127.0.0.1'],
- BASE_URL,
+ ['https://0xproject.localhost:3572', 'https://localhost:3572', 'https://127.0.0.1'],
+ BASE_URL,
);
const INFURA_API_KEY = 'T5WSC8cautR4KXyYgsRs';
export const configs = {
- BACKEND_BASE_URL: isDevelopment ? 'https://localhost:3001' : 'https://website-api.0xproject.com',
- BASE_URL,
- BITLY_ACCESS_TOKEN: 'ffc4c1a31e5143848fb7c523b39f91b9b213d208',
- CONTRACT_ADDRESS: {
- '1.0.0': {
- [Networks.mainnet]: {
- [SmartContractDocSections.Exchange]: '0x12459c951127e0c374ff9105dda097662a027093',
- [SmartContractDocSections.TokenTransferProxy]: '0x8da0d80f5007ef1e431dd2127178d224e32c2ef4',
- [SmartContractDocSections.ZRXToken]: '0xe41d2489571d322189246dafa5ebde1f4699f498',
- [SmartContractDocSections.TokenRegistry]: '0x926a74c5c36adf004c87399e65f75628b0f98d2c',
- },
- [Networks.ropsten]: {
- [SmartContractDocSections.Exchange]: '0x479cc461fecd078f766ecc58533d6f69580cf3ac',
- [SmartContractDocSections.TokenTransferProxy]: '0x4e9aad8184de8833365fea970cd9149372fdf1e6',
- [SmartContractDocSections.ZRXToken]: '0xa8e9fa8f91e5ae138c74648c9c304f1c75003a8d',
- [SmartContractDocSections.TokenRegistry]: '0x6b1a50f0bb5a7995444bd3877b22dc89c62843ed',
- },
- [Networks.kovan]: {
- [SmartContractDocSections.Exchange]: '0x90fe2af704b34e0224bf2299c838e04d4dcf1364',
- [SmartContractDocSections.TokenTransferProxy]: '0x087Eed4Bc1ee3DE49BeFbd66C662B434B15d49d4',
- [SmartContractDocSections.ZRXToken]: '0x6ff6c0ff1d68b964901f986d4c9fa3ac68346570',
- [SmartContractDocSections.TokenRegistry]: '0xf18e504561f4347bea557f3d4558f559dddbae7f',
- },
- },
- } as ContractAddresses,
- DEFAULT_DERIVATION_PATH: `44'/60'/0'`,
- // WARNING: ZRX & WETH MUST always be default trackedTokens
- DEFAULT_TRACKED_TOKEN_SYMBOLS: ['WETH', 'ZRX'],
- DOMAIN_STAGING: 'staging-0xproject.s3-website-us-east-1.amazonaws.com',
- DOMAIN_DEVELOPMENT: '0xproject.localhost:3572',
- DOMAIN_PRODUCTION: '0xproject.com',
- ENVIRONMENT: isDevelopment ? Environments.DEVELOPMENT : Environments.PRODUCTION,
- ICON_URL_BY_SYMBOL: {
- REP: '/images/token_icons/augur.png',
- DGD: '/images/token_icons/digixdao.png',
- WETH: '/images/token_icons/ether_erc20.png',
- MLN: '/images/token_icons/melon.png',
- GNT: '/images/token_icons/golem.png',
- MKR: '/images/token_icons/makerdao.png',
- ZRX: '/images/token_icons/zero_ex.png',
- ANT: '/images/token_icons/aragon.png',
- BNT: '/images/token_icons/bancor.png',
- BAT: '/images/token_icons/basicattentiontoken.png',
- CVC: '/images/token_icons/civic.png',
- EOS: '/images/token_icons/eos.png',
- FUN: '/images/token_icons/funfair.png',
- GNO: '/images/token_icons/gnosis.png',
- ICN: '/images/token_icons/iconomi.png',
- OMG: '/images/token_icons/omisego.png',
- SNT: '/images/token_icons/status.png',
- STORJ: '/images/token_icons/storjcoinx.png',
- PAY: '/images/token_icons/tenx.png',
- QTUM: '/images/token_icons/qtum.png',
- DNT: '/images/token_icons/district0x.png',
- SNGLS: '/images/token_icons/singularity.png',
- EDG: '/images/token_icons/edgeless.png',
- '1ST': '/images/token_icons/firstblood.jpg',
- WINGS: '/images/token_icons/wings.png',
- BQX: '/images/token_icons/bitquence.png',
- LUN: '/images/token_icons/lunyr.png',
- RLC: '/images/token_icons/iexec.png',
- MCO: '/images/token_icons/monaco.png',
- ADT: '/images/token_icons/adtoken.png',
- CFI: '/images/token_icons/cofound-it.png',
- ROL: '/images/token_icons/etheroll.png',
- WGNT: '/images/token_icons/golem.png',
- MTL: '/images/token_icons/metal.png',
- NMR: '/images/token_icons/numeraire.png',
- SAN: '/images/token_icons/santiment.png',
- TAAS: '/images/token_icons/taas.png',
- TKN: '/images/token_icons/tokencard.png',
- TRST: '/images/token_icons/trust.png',
- } as { [symbol: string]: string },
- IS_MAINNET_ENABLED: true,
- LAST_LOCAL_STORAGE_FILL_CLEARANCE_DATE: '2017-11-22',
- LAST_LOCAL_STORAGE_TRACKED_TOKEN_CLEARANCE_DATE: '2017-12-19',
- // NEW_WRAPPED_ETHERS is temporary until we remove the SHOULD_DEPRECATE_OLD_WETH_TOKEN flag
- // and add the new WETHs to the tokenRegistry
- NEW_WRAPPED_ETHERS: {
- 1: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2',
- 42: '0xd0a1e359811322d97991e03f863a0c30c2cf029c',
- } as { [networkId: string]: string },
- OUTDATED_WRAPPED_ETHERS: [
- {
- 42: {
- address: '0x05d090b51c40b020eab3bfcb6a2dff130df22e9c',
- timestampMsRange: {
- startTimestampMs: 1502455607000,
- endTimestampMs: 1513790926000,
- },
- },
- 1: {
- address: '0x2956356cd2a2bf3202f771f50d3d14a367b48070',
- timestampMsRange: {
- startTimestampMs: 1502455607000,
- endTimestampMs: 1513790926000,
- },
- },
- },
- ] as OutdatedWrappedEtherByNetworkId[],
- // The order matters. We first try first node and only then fall back to others.
- PUBLIC_NODE_URLS_BY_NETWORK_ID: {
- [1]: [`https://mainnet.infura.io/${INFURA_API_KEY}`, 'https://mainnet.0xproject.com'],
- [42]: [`https://kovan.infura.io/${INFURA_API_KEY}`, 'https://kovan.0xproject.com'],
- } as PublicNodeUrlsByNetworkId,
- SHOULD_DEPRECATE_OLD_WETH_TOKEN: true,
- SYMBOLS_OF_MINTABLE_TOKENS: ['MKR', 'MLN', 'GNT', 'DGD', 'REP'],
+ BACKEND_BASE_URL: isDevelopment ? 'https://localhost:3001' : 'https://website-api.0xproject.com',
+ BASE_URL,
+ BITLY_ACCESS_TOKEN: 'ffc4c1a31e5143848fb7c523b39f91b9b213d208',
+ CONTRACT_ADDRESS: {
+ '1.0.0': {
+ [Networks.mainnet]: {
+ [SmartContractDocSections.Exchange]: '0x12459c951127e0c374ff9105dda097662a027093',
+ [SmartContractDocSections.TokenTransferProxy]: '0x8da0d80f5007ef1e431dd2127178d224e32c2ef4',
+ [SmartContractDocSections.ZRXToken]: '0xe41d2489571d322189246dafa5ebde1f4699f498',
+ [SmartContractDocSections.TokenRegistry]: '0x926a74c5c36adf004c87399e65f75628b0f98d2c',
+ },
+ [Networks.ropsten]: {
+ [SmartContractDocSections.Exchange]: '0x479cc461fecd078f766ecc58533d6f69580cf3ac',
+ [SmartContractDocSections.TokenTransferProxy]: '0x4e9aad8184de8833365fea970cd9149372fdf1e6',
+ [SmartContractDocSections.ZRXToken]: '0xa8e9fa8f91e5ae138c74648c9c304f1c75003a8d',
+ [SmartContractDocSections.TokenRegistry]: '0x6b1a50f0bb5a7995444bd3877b22dc89c62843ed',
+ },
+ [Networks.kovan]: {
+ [SmartContractDocSections.Exchange]: '0x90fe2af704b34e0224bf2299c838e04d4dcf1364',
+ [SmartContractDocSections.TokenTransferProxy]: '0x087Eed4Bc1ee3DE49BeFbd66C662B434B15d49d4',
+ [SmartContractDocSections.ZRXToken]: '0x6ff6c0ff1d68b964901f986d4c9fa3ac68346570',
+ [SmartContractDocSections.TokenRegistry]: '0xf18e504561f4347bea557f3d4558f559dddbae7f',
+ },
+ },
+ } as ContractAddresses,
+ DEFAULT_DERIVATION_PATH: `44'/60'/0'`,
+ // WARNING: ZRX & WETH MUST always be default trackedTokens
+ DEFAULT_TRACKED_TOKEN_SYMBOLS: ['WETH', 'ZRX'],
+ DOMAIN_STAGING: 'staging-0xproject.s3-website-us-east-1.amazonaws.com',
+ DOMAIN_DEVELOPMENT: '0xproject.localhost:3572',
+ DOMAIN_PRODUCTION: '0xproject.com',
+ ENVIRONMENT: isDevelopment ? Environments.DEVELOPMENT : Environments.PRODUCTION,
+ ICON_URL_BY_SYMBOL: {
+ REP: '/images/token_icons/augur.png',
+ DGD: '/images/token_icons/digixdao.png',
+ WETH: '/images/token_icons/ether_erc20.png',
+ MLN: '/images/token_icons/melon.png',
+ GNT: '/images/token_icons/golem.png',
+ MKR: '/images/token_icons/makerdao.png',
+ ZRX: '/images/token_icons/zero_ex.png',
+ ANT: '/images/token_icons/aragon.png',
+ BNT: '/images/token_icons/bancor.png',
+ BAT: '/images/token_icons/basicattentiontoken.png',
+ CVC: '/images/token_icons/civic.png',
+ EOS: '/images/token_icons/eos.png',
+ FUN: '/images/token_icons/funfair.png',
+ GNO: '/images/token_icons/gnosis.png',
+ ICN: '/images/token_icons/iconomi.png',
+ OMG: '/images/token_icons/omisego.png',
+ SNT: '/images/token_icons/status.png',
+ STORJ: '/images/token_icons/storjcoinx.png',
+ PAY: '/images/token_icons/tenx.png',
+ QTUM: '/images/token_icons/qtum.png',
+ DNT: '/images/token_icons/district0x.png',
+ SNGLS: '/images/token_icons/singularity.png',
+ EDG: '/images/token_icons/edgeless.png',
+ '1ST': '/images/token_icons/firstblood.jpg',
+ WINGS: '/images/token_icons/wings.png',
+ BQX: '/images/token_icons/bitquence.png',
+ LUN: '/images/token_icons/lunyr.png',
+ RLC: '/images/token_icons/iexec.png',
+ MCO: '/images/token_icons/monaco.png',
+ ADT: '/images/token_icons/adtoken.png',
+ CFI: '/images/token_icons/cofound-it.png',
+ ROL: '/images/token_icons/etheroll.png',
+ WGNT: '/images/token_icons/golem.png',
+ MTL: '/images/token_icons/metal.png',
+ NMR: '/images/token_icons/numeraire.png',
+ SAN: '/images/token_icons/santiment.png',
+ TAAS: '/images/token_icons/taas.png',
+ TKN: '/images/token_icons/tokencard.png',
+ TRST: '/images/token_icons/trust.png',
+ } as { [symbol: string]: string },
+ IS_MAINNET_ENABLED: true,
+ LAST_LOCAL_STORAGE_FILL_CLEARANCE_DATE: '2017-11-22',
+ LAST_LOCAL_STORAGE_TRACKED_TOKEN_CLEARANCE_DATE: '2017-12-19',
+ // NEW_WRAPPED_ETHERS is temporary until we remove the SHOULD_DEPRECATE_OLD_WETH_TOKEN flag
+ // and add the new WETHs to the tokenRegistry
+ NEW_WRAPPED_ETHERS: {
+ 1: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2',
+ 42: '0xd0a1e359811322d97991e03f863a0c30c2cf029c',
+ } as { [networkId: string]: string },
+ OUTDATED_WRAPPED_ETHERS: [
+ {
+ 42: {
+ address: '0x05d090b51c40b020eab3bfcb6a2dff130df22e9c',
+ timestampMsRange: {
+ startTimestampMs: 1502455607000,
+ endTimestampMs: 1513790926000,
+ },
+ },
+ 1: {
+ address: '0x2956356cd2a2bf3202f771f50d3d14a367b48070',
+ timestampMsRange: {
+ startTimestampMs: 1502455607000,
+ endTimestampMs: 1513790926000,
+ },
+ },
+ },
+ ] as OutdatedWrappedEtherByNetworkId[],
+ // The order matters. We first try first node and only then fall back to others.
+ PUBLIC_NODE_URLS_BY_NETWORK_ID: {
+ [1]: [`https://mainnet.infura.io/${INFURA_API_KEY}`, 'https://mainnet.0xproject.com'],
+ [42]: [`https://kovan.infura.io/${INFURA_API_KEY}`, 'https://kovan.0xproject.com'],
+ } as PublicNodeUrlsByNetworkId,
+ SHOULD_DEPRECATE_OLD_WETH_TOKEN: true,
+ SYMBOLS_OF_MINTABLE_TOKENS: ['MKR', 'MLN', 'GNT', 'DGD', 'REP'],
};
diff --git a/packages/website/ts/utils/constants.ts b/packages/website/ts/utils/constants.ts
index bb6407ec5..dded82114 100644
--- a/packages/website/ts/utils/constants.ts
+++ b/packages/website/ts/utils/constants.ts
@@ -2,86 +2,86 @@ import { BigNumber } from '@0xproject/utils';
import { Networks } from 'ts/types';
export const constants = {
- DECIMAL_PLACES_ETH: 18,
- DECIMAL_PLACES_ZRX: 18,
- DOCS_SCROLL_DURATION_MS: 0,
- DOCS_CONTAINER_ID: 'documentation',
- GENESIS_ORDER_BLOCK_BY_NETWORK_ID: {
- 1: 4145578,
- 42: 3117574,
- 50: 0,
- } as { [networkId: number]: number },
- HOME_SCROLL_DURATION_MS: 500,
- HTTP_NO_CONTENT_STATUS_CODE: 204,
- LOCAL_STORAGE_KEY_ACCEPT_DISCLAIMER: 'didAcceptPortalDisclaimer',
- LOCAL_STORAGE_KEY_DISMISS_WETH_NOTICE: 'hasDismissedWethNotice',
- MAKER_FEE: new BigNumber(0),
- MAINNET_NAME: 'Main network',
- MINT_AMOUNT: new BigNumber('100000000000000000000'),
- NETWORK_ID_MAINNET: 1,
- NETWORK_ID_TESTNET: 42,
- NETWORK_ID_TESTRPC: 50,
- NETWORK_NAME_BY_ID: {
- 1: Networks.mainnet,
- 3: Networks.ropsten,
- 4: Networks.rinkeby,
- 42: Networks.kovan,
- } as { [symbol: number]: string },
- NETWORK_ID_BY_NAME: {
- [Networks.mainnet]: 1,
- [Networks.ropsten]: 3,
- [Networks.rinkeby]: 4,
- [Networks.kovan]: 42,
- } as { [networkName: string]: number },
- NULL_ADDRESS: '0x0000000000000000000000000000000000000000',
- PROVIDER_NAME_LEDGER: 'Ledger',
- PROVIDER_NAME_METAMASK: 'Metamask',
- PROVIDER_NAME_PARITY_SIGNER: 'Parity Signer',
- PROVIDER_NAME_GENERIC: 'Injected Web3',
- PROVIDER_NAME_PUBLIC: '0x Public',
- ROLLBAR_ACCESS_TOKEN: 'a6619002b51c4464928201e6ea94de65',
- SUCCESS_STATUS: 200,
- UNAVAILABLE_STATUS: 503,
- TAKER_FEE: new BigNumber(0),
- TESTNET_NAME: 'Kovan',
- TYPES_SECTION_NAME: 'types',
- PROJECT_URL_ETHFINEX: 'https://www.bitfinex.com/ethfinex',
- PROJECT_URL_RADAR_RELAY: 'https://radarrelay.com',
- PROJECT_URL_PARADEX: 'https://paradex.io',
- PROJECT_URL_DYDX: 'https://dydx.exchange',
- PROJECT_URL_MELONPORT: 'https://melonport.com',
- PROJECT_URL_DISTRICT_0X: 'https://district0x.io',
- PROJECT_URL_DHARMA: 'https://dharma.io',
- PROJECT_URL_LENDROID: 'https://lendroid.com',
- PROJECT_URL_MAKER: 'https://makerdao.com',
- PROJECT_URL_ARAGON: 'https://aragon.one',
- PROJECT_URL_BLOCKNET: 'https://blocknet.co',
- PROJECT_URL_0CEAN: 'http://the0cean.com',
- PROJECT_URL_STATUS: 'https://status.im',
- PROJECT_URL_AUGUR: 'https://augur.net',
- PROJECT_URL_AUCTUS: 'https://auctus.org',
- PROJECT_URL_OPEN_ANX: 'https://www.openanx.org',
- URL_ANGELLIST: 'https://angel.co/0xproject/jobs',
- URL_BIGNUMBERJS_GITHUB: 'http://mikemcl.github.io/bignumber.js',
- URL_BITLY_API: 'https://api-ssl.bitly.com',
- URL_BLOG: 'https://blog.0xproject.com/latest',
- URL_DISCOURSE_FORUM: 'https://forum.0xproject.com',
- URL_FIREFOX_U2F_ADDON: 'https://addons.mozilla.org/en-US/firefox/addon/u2f-support-add-on/',
- URL_ETHER_FAUCET: 'https://faucet.0xproject.com',
- URL_GITHUB_ORG: 'https://github.com/0xProject',
- URL_GITHUB_WIKI: 'https://github.com/0xProject/wiki',
- URL_METAMASK_CHROME_STORE: 'https://chrome.google.com/webstore/detail/metamask/nkbihfbeogaeaoehlefnkodbefgpgknn',
- URL_MIST_DOWNLOAD: 'https://github.com/ethereum/mist/releases',
- URL_PARITY_CHROME_STORE:
- 'https://chrome.google.com/webstore/detail/parity-ethereum-integrati/himekenlppkgeaoeddcliojfddemadig',
- URL_REDDIT: 'https://reddit.com/r/0xproject',
- URL_STANDARD_RELAYER_API_GITHUB: 'https://github.com/0xProject/standard-relayer-api/blob/master/README.md',
- URL_TWITTER: 'https://twitter.com/0xproject',
- URL_WEB3_DOCS: 'https://github.com/ethereum/wiki/wiki/JavaScript-API',
- URL_WEB3_DECODED_LOG_ENTRY_EVENT:
- 'https://github.com/0xProject/web3-typescript-typings/blob/f5bcb96/index.d.ts#L123',
- URL_WEB3_LOG_ENTRY_EVENT: 'https://github.com/0xProject/web3-typescript-typings/blob/f5bcb96/index.d.ts#L127',
- URL_WEB3_PROVIDER_DOCS: 'https://github.com/0xProject/web3-typescript-typings/blob/f5bcb96/index.d.ts#L150',
- URL_WETH_IO: 'https://weth.io/',
- URL_ZEROEX_CHAT: 'https://chat.0xproject.com',
+ DECIMAL_PLACES_ETH: 18,
+ DECIMAL_PLACES_ZRX: 18,
+ DOCS_SCROLL_DURATION_MS: 0,
+ DOCS_CONTAINER_ID: 'documentation',
+ GENESIS_ORDER_BLOCK_BY_NETWORK_ID: {
+ 1: 4145578,
+ 42: 3117574,
+ 50: 0,
+ } as { [networkId: number]: number },
+ HOME_SCROLL_DURATION_MS: 500,
+ HTTP_NO_CONTENT_STATUS_CODE: 204,
+ LOCAL_STORAGE_KEY_ACCEPT_DISCLAIMER: 'didAcceptPortalDisclaimer',
+ LOCAL_STORAGE_KEY_DISMISS_WETH_NOTICE: 'hasDismissedWethNotice',
+ MAKER_FEE: new BigNumber(0),
+ MAINNET_NAME: 'Main network',
+ MINT_AMOUNT: new BigNumber('100000000000000000000'),
+ NETWORK_ID_MAINNET: 1,
+ NETWORK_ID_TESTNET: 42,
+ NETWORK_ID_TESTRPC: 50,
+ NETWORK_NAME_BY_ID: {
+ 1: Networks.mainnet,
+ 3: Networks.ropsten,
+ 4: Networks.rinkeby,
+ 42: Networks.kovan,
+ } as { [symbol: number]: string },
+ NETWORK_ID_BY_NAME: {
+ [Networks.mainnet]: 1,
+ [Networks.ropsten]: 3,
+ [Networks.rinkeby]: 4,
+ [Networks.kovan]: 42,
+ } as { [networkName: string]: number },
+ NULL_ADDRESS: '0x0000000000000000000000000000000000000000',
+ PROVIDER_NAME_LEDGER: 'Ledger',
+ PROVIDER_NAME_METAMASK: 'Metamask',
+ PROVIDER_NAME_PARITY_SIGNER: 'Parity Signer',
+ PROVIDER_NAME_GENERIC: 'Injected Web3',
+ PROVIDER_NAME_PUBLIC: '0x Public',
+ ROLLBAR_ACCESS_TOKEN: 'a6619002b51c4464928201e6ea94de65',
+ SUCCESS_STATUS: 200,
+ UNAVAILABLE_STATUS: 503,
+ TAKER_FEE: new BigNumber(0),
+ TESTNET_NAME: 'Kovan',
+ TYPES_SECTION_NAME: 'types',
+ PROJECT_URL_ETHFINEX: 'https://www.bitfinex.com/ethfinex',
+ PROJECT_URL_RADAR_RELAY: 'https://radarrelay.com',
+ PROJECT_URL_PARADEX: 'https://paradex.io',
+ PROJECT_URL_DYDX: 'https://dydx.exchange',
+ PROJECT_URL_MELONPORT: 'https://melonport.com',
+ PROJECT_URL_DISTRICT_0X: 'https://district0x.io',
+ PROJECT_URL_DHARMA: 'https://dharma.io',
+ PROJECT_URL_LENDROID: 'https://lendroid.com',
+ PROJECT_URL_MAKER: 'https://makerdao.com',
+ PROJECT_URL_ARAGON: 'https://aragon.one',
+ PROJECT_URL_BLOCKNET: 'https://blocknet.co',
+ PROJECT_URL_0CEAN: 'http://the0cean.com',
+ PROJECT_URL_STATUS: 'https://status.im',
+ PROJECT_URL_AUGUR: 'https://augur.net',
+ PROJECT_URL_AUCTUS: 'https://auctus.org',
+ PROJECT_URL_OPEN_ANX: 'https://www.openanx.org',
+ URL_ANGELLIST: 'https://angel.co/0xproject/jobs',
+ URL_BIGNUMBERJS_GITHUB: 'http://mikemcl.github.io/bignumber.js',
+ URL_BITLY_API: 'https://api-ssl.bitly.com',
+ URL_BLOG: 'https://blog.0xproject.com/latest',
+ URL_DISCOURSE_FORUM: 'https://forum.0xproject.com',
+ URL_FIREFOX_U2F_ADDON: 'https://addons.mozilla.org/en-US/firefox/addon/u2f-support-add-on/',
+ URL_ETHER_FAUCET: 'https://faucet.0xproject.com',
+ URL_GITHUB_ORG: 'https://github.com/0xProject',
+ URL_GITHUB_WIKI: 'https://github.com/0xProject/wiki',
+ URL_METAMASK_CHROME_STORE: 'https://chrome.google.com/webstore/detail/metamask/nkbihfbeogaeaoehlefnkodbefgpgknn',
+ URL_MIST_DOWNLOAD: 'https://github.com/ethereum/mist/releases',
+ URL_PARITY_CHROME_STORE:
+ 'https://chrome.google.com/webstore/detail/parity-ethereum-integrati/himekenlppkgeaoeddcliojfddemadig',
+ URL_REDDIT: 'https://reddit.com/r/0xproject',
+ URL_STANDARD_RELAYER_API_GITHUB: 'https://github.com/0xProject/standard-relayer-api/blob/master/README.md',
+ URL_TWITTER: 'https://twitter.com/0xproject',
+ URL_WEB3_DOCS: 'https://github.com/ethereum/wiki/wiki/JavaScript-API',
+ URL_WEB3_DECODED_LOG_ENTRY_EVENT:
+ 'https://github.com/0xProject/web3-typescript-typings/blob/f5bcb96/index.d.ts#L123',
+ URL_WEB3_LOG_ENTRY_EVENT: 'https://github.com/0xProject/web3-typescript-typings/blob/f5bcb96/index.d.ts#L127',
+ URL_WEB3_PROVIDER_DOCS: 'https://github.com/0xProject/web3-typescript-typings/blob/f5bcb96/index.d.ts#L150',
+ URL_WETH_IO: 'https://weth.io/',
+ URL_ZEROEX_CHAT: 'https://chat.0xproject.com',
};
diff --git a/packages/website/ts/utils/doc_utils.ts b/packages/website/ts/utils/doc_utils.ts
index 18bf276b4..1f5f75ee2 100644
--- a/packages/website/ts/utils/doc_utils.ts
+++ b/packages/website/ts/utils/doc_utils.ts
@@ -5,47 +5,47 @@ import { utils } from 'ts/utils/utils';
import convert = require('xml-js');
export const docUtils = {
- async getVersionToFileNameAsync(s3DocJsonRoot: string): Promise<VersionToFileName> {
- const versionFileNames = await this.getVersionFileNamesAsync(s3DocJsonRoot);
- const versionToFileName: VersionToFileName = {};
- _.each(versionFileNames, fileName => {
- const [version] = findVersions(fileName);
- versionToFileName[version] = fileName;
- });
- return versionToFileName;
- },
- async getVersionFileNamesAsync(s3DocJsonRoot: string): Promise<string[]> {
- const response = await fetch(s3DocJsonRoot);
- if (response.status !== 200) {
- // TODO: Show the user an error message when the docs fail to load
- const errMsg = await response.text();
- utils.consoleLog(`Failed to load JSON file list: ${response.status} ${errMsg}`);
- throw new Error(errMsg);
- }
- const responseXML = await response.text();
- const responseJSONString = convert.xml2json(responseXML, {
- compact: true,
- });
- const responseObj = JSON.parse(responseJSONString);
- const fileObjs: S3FileObject[] = _.isArray(responseObj.ListBucketResult.Contents)
- ? (responseObj.ListBucketResult.Contents as S3FileObject[])
- : [responseObj.ListBucketResult.Contents];
+ async getVersionToFileNameAsync(s3DocJsonRoot: string): Promise<VersionToFileName> {
+ const versionFileNames = await this.getVersionFileNamesAsync(s3DocJsonRoot);
+ const versionToFileName: VersionToFileName = {};
+ _.each(versionFileNames, fileName => {
+ const [version] = findVersions(fileName);
+ versionToFileName[version] = fileName;
+ });
+ return versionToFileName;
+ },
+ async getVersionFileNamesAsync(s3DocJsonRoot: string): Promise<string[]> {
+ const response = await fetch(s3DocJsonRoot);
+ if (response.status !== 200) {
+ // TODO: Show the user an error message when the docs fail to load
+ const errMsg = await response.text();
+ utils.consoleLog(`Failed to load JSON file list: ${response.status} ${errMsg}`);
+ throw new Error(errMsg);
+ }
+ const responseXML = await response.text();
+ const responseJSONString = convert.xml2json(responseXML, {
+ compact: true,
+ });
+ const responseObj = JSON.parse(responseJSONString);
+ const fileObjs: S3FileObject[] = _.isArray(responseObj.ListBucketResult.Contents)
+ ? (responseObj.ListBucketResult.Contents as S3FileObject[])
+ : [responseObj.ListBucketResult.Contents];
- const versionFileNames = _.map(fileObjs, fileObj => {
- return fileObj.Key._text;
- });
- return versionFileNames;
- },
- async getJSONDocFileAsync(fileName: string, s3DocJsonRoot: string): Promise<TypeDocNode | DoxityDocObj> {
- const endpoint = `${s3DocJsonRoot}/${fileName}`;
- const response = await fetch(endpoint);
- if (response.status !== 200) {
- // TODO: Show the user an error message when the docs fail to load
- const errMsg = await response.text();
- utils.consoleLog(`Failed to load Doc JSON: ${response.status} ${errMsg}`);
- throw new Error(errMsg);
- }
- const jsonDocObj = await response.json();
- return jsonDocObj;
- },
+ const versionFileNames = _.map(fileObjs, fileObj => {
+ return fileObj.Key._text;
+ });
+ return versionFileNames;
+ },
+ async getJSONDocFileAsync(fileName: string, s3DocJsonRoot: string): Promise<TypeDocNode | DoxityDocObj> {
+ const endpoint = `${s3DocJsonRoot}/${fileName}`;
+ const response = await fetch(endpoint);
+ if (response.status !== 200) {
+ // TODO: Show the user an error message when the docs fail to load
+ const errMsg = await response.text();
+ utils.consoleLog(`Failed to load Doc JSON: ${response.status} ${errMsg}`);
+ throw new Error(errMsg);
+ }
+ const jsonDocObj = await response.json();
+ return jsonDocObj;
+ },
};
diff --git a/packages/website/ts/utils/doxity_utils.ts b/packages/website/ts/utils/doxity_utils.ts
index f26534d51..5f1d02132 100644
--- a/packages/website/ts/utils/doxity_utils.ts
+++ b/packages/website/ts/utils/doxity_utils.ts
@@ -1,168 +1,168 @@
import * as _ from 'lodash';
import {
- AbiTypes,
- DocAgnosticFormat,
- DocSection,
- DoxityAbiDoc,
- DoxityContractObj,
- DoxityDocObj,
- DoxityInput,
- EventArg,
- Parameter,
- Property,
- SolidityMethod,
- Type,
- TypeDocTypes,
+ AbiTypes,
+ DocAgnosticFormat,
+ DocSection,
+ DoxityAbiDoc,
+ DoxityContractObj,
+ DoxityDocObj,
+ DoxityInput,
+ EventArg,
+ Parameter,
+ Property,
+ SolidityMethod,
+ Type,
+ TypeDocTypes,
} from 'ts/types';
export const doxityUtils = {
- convertToDocAgnosticFormat(doxityDocObj: DoxityDocObj): DocAgnosticFormat {
- const docAgnosticFormat: DocAgnosticFormat = {};
- _.each(doxityDocObj, (doxityContractObj: DoxityContractObj, contractName: string) => {
- const doxityConstructor = _.find(doxityContractObj.abiDocs, (abiDoc: DoxityAbiDoc) => {
- return abiDoc.type === AbiTypes.Constructor;
- });
- const constructors = [];
- if (!_.isUndefined(doxityConstructor)) {
- const constructor = {
- isConstructor: true,
- name: doxityContractObj.name,
- comment: doxityConstructor.details,
- returnComment: doxityConstructor.return,
- callPath: '',
- parameters: this._convertParameters(doxityConstructor.inputs),
- returnType: this._convertType(doxityContractObj.name),
- };
- constructors.push(constructor);
- }
+ convertToDocAgnosticFormat(doxityDocObj: DoxityDocObj): DocAgnosticFormat {
+ const docAgnosticFormat: DocAgnosticFormat = {};
+ _.each(doxityDocObj, (doxityContractObj: DoxityContractObj, contractName: string) => {
+ const doxityConstructor = _.find(doxityContractObj.abiDocs, (abiDoc: DoxityAbiDoc) => {
+ return abiDoc.type === AbiTypes.Constructor;
+ });
+ const constructors = [];
+ if (!_.isUndefined(doxityConstructor)) {
+ const constructor = {
+ isConstructor: true,
+ name: doxityContractObj.name,
+ comment: doxityConstructor.details,
+ returnComment: doxityConstructor.return,
+ callPath: '',
+ parameters: this._convertParameters(doxityConstructor.inputs),
+ returnType: this._convertType(doxityContractObj.name),
+ };
+ constructors.push(constructor);
+ }
- const doxityMethods: DoxityAbiDoc[] = _.filter<DoxityAbiDoc>(
- doxityContractObj.abiDocs,
- (abiDoc: DoxityAbiDoc) => {
- return this._isMethod(abiDoc);
- },
- );
- const methods: SolidityMethod[] = _.map<DoxityAbiDoc, SolidityMethod>(
- doxityMethods,
- (doxityMethod: DoxityAbiDoc) => {
- // We assume that none of our functions returns more then a single value
- const outputIfExists = !_.isUndefined(doxityMethod.outputs) ? doxityMethod.outputs[0] : undefined;
- const returnTypeIfExists = !_.isUndefined(outputIfExists)
- ? this._convertType(outputIfExists.type)
- : undefined;
- // For ZRXToken, we want to convert it to zrxToken, rather then simply zRXToken
- const callPath =
- contractName !== 'ZRXToken'
- ? `${contractName[0].toLowerCase()}${contractName.slice(1)}.`
- : `${contractName.slice(0, 3).toLowerCase()}${contractName.slice(3)}.`;
- const method = {
- isConstructor: false,
- isConstant: doxityMethod.constant,
- isPayable: doxityMethod.payable,
- name: doxityMethod.name,
- comment: doxityMethod.details,
- returnComment: doxityMethod.return,
- callPath,
- parameters: this._convertParameters(doxityMethod.inputs),
- returnType: returnTypeIfExists,
- };
- return method;
- },
- );
+ const doxityMethods: DoxityAbiDoc[] = _.filter<DoxityAbiDoc>(
+ doxityContractObj.abiDocs,
+ (abiDoc: DoxityAbiDoc) => {
+ return this._isMethod(abiDoc);
+ },
+ );
+ const methods: SolidityMethod[] = _.map<DoxityAbiDoc, SolidityMethod>(
+ doxityMethods,
+ (doxityMethod: DoxityAbiDoc) => {
+ // We assume that none of our functions returns more then a single value
+ const outputIfExists = !_.isUndefined(doxityMethod.outputs) ? doxityMethod.outputs[0] : undefined;
+ const returnTypeIfExists = !_.isUndefined(outputIfExists)
+ ? this._convertType(outputIfExists.type)
+ : undefined;
+ // For ZRXToken, we want to convert it to zrxToken, rather then simply zRXToken
+ const callPath =
+ contractName !== 'ZRXToken'
+ ? `${contractName[0].toLowerCase()}${contractName.slice(1)}.`
+ : `${contractName.slice(0, 3).toLowerCase()}${contractName.slice(3)}.`;
+ const method = {
+ isConstructor: false,
+ isConstant: doxityMethod.constant,
+ isPayable: doxityMethod.payable,
+ name: doxityMethod.name,
+ comment: doxityMethod.details,
+ returnComment: doxityMethod.return,
+ callPath,
+ parameters: this._convertParameters(doxityMethod.inputs),
+ returnType: returnTypeIfExists,
+ };
+ return method;
+ },
+ );
- const doxityProperties: DoxityAbiDoc[] = _.filter<DoxityAbiDoc>(
- doxityContractObj.abiDocs,
- (abiDoc: DoxityAbiDoc) => {
- return this._isProperty(abiDoc);
- },
- );
- const properties = _.map<DoxityAbiDoc, Property>(doxityProperties, (doxityProperty: DoxityAbiDoc) => {
- // We assume that none of our functions return more then a single return value
- let typeName = doxityProperty.outputs[0].type;
- if (!_.isEmpty(doxityProperty.inputs)) {
- // Properties never have more then a single input
- typeName = `(${doxityProperty.inputs[0].type} => ${typeName})`;
- }
- const property = {
- name: doxityProperty.name,
- type: this._convertType(typeName),
- comment: doxityProperty.details,
- };
- return property;
- });
+ const doxityProperties: DoxityAbiDoc[] = _.filter<DoxityAbiDoc>(
+ doxityContractObj.abiDocs,
+ (abiDoc: DoxityAbiDoc) => {
+ return this._isProperty(abiDoc);
+ },
+ );
+ const properties = _.map<DoxityAbiDoc, Property>(doxityProperties, (doxityProperty: DoxityAbiDoc) => {
+ // We assume that none of our functions return more then a single return value
+ let typeName = doxityProperty.outputs[0].type;
+ if (!_.isEmpty(doxityProperty.inputs)) {
+ // Properties never have more then a single input
+ typeName = `(${doxityProperty.inputs[0].type} => ${typeName})`;
+ }
+ const property = {
+ name: doxityProperty.name,
+ type: this._convertType(typeName),
+ comment: doxityProperty.details,
+ };
+ return property;
+ });
- const doxityEvents = _.filter(
- doxityContractObj.abiDocs,
- (abiDoc: DoxityAbiDoc) => abiDoc.type === AbiTypes.Event,
- );
- const events = _.map(doxityEvents, doxityEvent => {
- const event = {
- name: doxityEvent.name,
- eventArgs: this._convertEventArgs(doxityEvent.inputs),
- };
- return event;
- });
+ const doxityEvents = _.filter(
+ doxityContractObj.abiDocs,
+ (abiDoc: DoxityAbiDoc) => abiDoc.type === AbiTypes.Event,
+ );
+ const events = _.map(doxityEvents, doxityEvent => {
+ const event = {
+ name: doxityEvent.name,
+ eventArgs: this._convertEventArgs(doxityEvent.inputs),
+ };
+ return event;
+ });
- const docSection: DocSection = {
- comment: doxityContractObj.title,
- constructors,
- methods,
- properties,
- types: [],
- events,
- };
- docAgnosticFormat[contractName] = docSection;
- });
- return docAgnosticFormat;
- },
- _convertParameters(inputs: DoxityInput[]): Parameter[] {
- const parameters = _.map(inputs, input => {
- const parameter = {
- name: input.name,
- comment: input.description,
- isOptional: false,
- type: this._convertType(input.type),
- };
- return parameter;
- });
- return parameters;
- },
- _convertType(typeName: string): Type {
- const type = {
- name: typeName,
- typeDocType: TypeDocTypes.Intrinsic,
- };
- return type;
- },
- _isMethod(abiDoc: DoxityAbiDoc) {
- if (abiDoc.type !== AbiTypes.Function) {
- return false;
- }
- const hasInputs = !_.isEmpty(abiDoc.inputs);
- const hasNamedOutputIfExists = !hasInputs || !_.isEmpty(abiDoc.inputs[0].name);
- const isNameAllCaps = abiDoc.name === abiDoc.name.toUpperCase();
- const isMethod = hasNamedOutputIfExists && !isNameAllCaps;
- return isMethod;
- },
- _isProperty(abiDoc: DoxityAbiDoc) {
- if (abiDoc.type !== AbiTypes.Function) {
- return false;
- }
- const hasInputs = !_.isEmpty(abiDoc.inputs);
- const hasNamedOutputIfExists = !hasInputs || !_.isEmpty(abiDoc.inputs[0].name);
- const isNameAllCaps = abiDoc.name === abiDoc.name.toUpperCase();
- const isProperty = !hasNamedOutputIfExists || isNameAllCaps;
- return isProperty;
- },
- _convertEventArgs(inputs: DoxityInput[]): EventArg[] {
- const eventArgs = _.map(inputs, input => {
- const eventArg = {
- isIndexed: input.indexed,
- name: input.name,
- type: this._convertType(input.type),
- };
- return eventArg;
- });
- return eventArgs;
- },
+ const docSection: DocSection = {
+ comment: doxityContractObj.title,
+ constructors,
+ methods,
+ properties,
+ types: [],
+ events,
+ };
+ docAgnosticFormat[contractName] = docSection;
+ });
+ return docAgnosticFormat;
+ },
+ _convertParameters(inputs: DoxityInput[]): Parameter[] {
+ const parameters = _.map(inputs, input => {
+ const parameter = {
+ name: input.name,
+ comment: input.description,
+ isOptional: false,
+ type: this._convertType(input.type),
+ };
+ return parameter;
+ });
+ return parameters;
+ },
+ _convertType(typeName: string): Type {
+ const type = {
+ name: typeName,
+ typeDocType: TypeDocTypes.Intrinsic,
+ };
+ return type;
+ },
+ _isMethod(abiDoc: DoxityAbiDoc) {
+ if (abiDoc.type !== AbiTypes.Function) {
+ return false;
+ }
+ const hasInputs = !_.isEmpty(abiDoc.inputs);
+ const hasNamedOutputIfExists = !hasInputs || !_.isEmpty(abiDoc.inputs[0].name);
+ const isNameAllCaps = abiDoc.name === abiDoc.name.toUpperCase();
+ const isMethod = hasNamedOutputIfExists && !isNameAllCaps;
+ return isMethod;
+ },
+ _isProperty(abiDoc: DoxityAbiDoc) {
+ if (abiDoc.type !== AbiTypes.Function) {
+ return false;
+ }
+ const hasInputs = !_.isEmpty(abiDoc.inputs);
+ const hasNamedOutputIfExists = !hasInputs || !_.isEmpty(abiDoc.inputs[0].name);
+ const isNameAllCaps = abiDoc.name === abiDoc.name.toUpperCase();
+ const isProperty = !hasNamedOutputIfExists || isNameAllCaps;
+ return isProperty;
+ },
+ _convertEventArgs(inputs: DoxityInput[]): EventArg[] {
+ const eventArgs = _.map(inputs, input => {
+ const eventArg = {
+ isIndexed: input.indexed,
+ name: input.name,
+ type: this._convertType(input.type),
+ };
+ return eventArg;
+ });
+ return eventArgs;
+ },
};
diff --git a/packages/website/ts/utils/error_reporter.ts b/packages/website/ts/utils/error_reporter.ts
index 08d99e405..0bd247c5b 100644
--- a/packages/website/ts/utils/error_reporter.ts
+++ b/packages/website/ts/utils/error_reporter.ts
@@ -6,47 +6,47 @@ import { utils } from 'ts/utils/utils';
// Suggested way to include Rollbar with Webpack
// https://github.com/rollbar/rollbar.js/tree/master/examples/webpack
const rollbarConfig = {
- accessToken: constants.ROLLBAR_ACCESS_TOKEN,
- captureUncaught: true,
- captureUnhandledRejections: true,
- itemsPerMinute: 10,
- maxItems: 500,
- payload: {
- environment: configs.ENVIRONMENT,
- },
- uncaughtErrorLevel: 'error',
- hostWhiteList: [configs.DOMAIN_PRODUCTION, configs.DOMAIN_STAGING],
- 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)',
- ],
+ accessToken: constants.ROLLBAR_ACCESS_TOKEN,
+ captureUncaught: true,
+ captureUnhandledRejections: true,
+ itemsPerMinute: 10,
+ maxItems: 500,
+ payload: {
+ environment: configs.ENVIRONMENT,
+ },
+ uncaughtErrorLevel: 'error',
+ hostWhiteList: [configs.DOMAIN_PRODUCTION, configs.DOMAIN_STAGING],
+ 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)',
+ ],
};
import Rollbar = require('../../public/js/rollbar.umd.nojson.min.js');
const rollbar = Rollbar.init(rollbarConfig);
export const errorReporter = {
- async reportAsync(err: Error): Promise<any> {
- if (configs.ENVIRONMENT === Environments.DEVELOPMENT) {
- return; // Let's not log development errors to rollbar
- }
+ async reportAsync(err: Error): Promise<any> {
+ if (configs.ENVIRONMENT === Environments.DEVELOPMENT) {
+ return; // Let's not log development errors to rollbar
+ }
- return new Promise((resolve, reject) => {
- rollbar.error(err, (rollbarErr: Error) => {
- if (rollbarErr) {
- utils.consoleLog(`Error reporting to rollbar, ignoring: ${rollbarErr}`);
- // We never want to reject and cause the app to throw because of rollbar
- resolve();
- } else {
- resolve();
- }
- });
- });
- },
+ return new Promise((resolve, reject) => {
+ rollbar.error(err, (rollbarErr: Error) => {
+ if (rollbarErr) {
+ utils.consoleLog(`Error reporting to rollbar, ignoring: ${rollbarErr}`);
+ // We never want to reject and cause the app to throw because of rollbar
+ resolve();
+ } else {
+ resolve();
+ }
+ });
+ });
+ },
};
diff --git a/packages/website/ts/utils/mui_theme.ts b/packages/website/ts/utils/mui_theme.ts
index 565d1ae4f..d73e80606 100644
--- a/packages/website/ts/utils/mui_theme.ts
+++ b/packages/website/ts/utils/mui_theme.ts
@@ -2,34 +2,34 @@ import { getMuiTheme } from 'material-ui/styles';
import { colors } from 'ts/utils/colors';
export const muiTheme = getMuiTheme({
- appBar: {
- height: 45,
- color: colors.white,
- textColor: colors.black,
- },
- palette: {
- pickerHeaderColor: colors.lightBlue,
- primary1Color: colors.lightBlue,
- primary2Color: colors.lightBlue,
- textColor: colors.grey700,
- },
- datePicker: {
- color: colors.grey700,
- textColor: colors.white,
- calendarTextColor: colors.white,
- selectColor: colors.darkestGrey,
- selectTextColor: colors.white,
- },
- timePicker: {
- color: colors.grey700,
- textColor: colors.white,
- accentColor: colors.white,
- headerColor: colors.darkestGrey,
- selectColor: colors.darkestGrey,
- selectTextColor: colors.darkestGrey,
- },
- toggle: {
- thumbOnColor: colors.limeGreen,
- trackOnColor: colors.lightGreen,
- },
+ appBar: {
+ height: 45,
+ color: colors.white,
+ textColor: colors.black,
+ },
+ palette: {
+ pickerHeaderColor: colors.lightBlue,
+ primary1Color: colors.lightBlue,
+ primary2Color: colors.lightBlue,
+ textColor: colors.grey700,
+ },
+ datePicker: {
+ color: colors.grey700,
+ textColor: colors.white,
+ calendarTextColor: colors.white,
+ selectColor: colors.darkestGrey,
+ selectTextColor: colors.white,
+ },
+ timePicker: {
+ color: colors.grey700,
+ textColor: colors.white,
+ accentColor: colors.white,
+ headerColor: colors.darkestGrey,
+ selectColor: colors.darkestGrey,
+ selectTextColor: colors.darkestGrey,
+ },
+ toggle: {
+ thumbOnColor: colors.limeGreen,
+ trackOnColor: colors.lightGreen,
+ },
});
diff --git a/packages/website/ts/utils/typedoc_utils.ts b/packages/website/ts/utils/typedoc_utils.ts
index b0c152891..11ec8da58 100644
--- a/packages/website/ts/utils/typedoc_utils.ts
+++ b/packages/website/ts/utils/typedoc_utils.ts
@@ -1,365 +1,365 @@
import * as _ from 'lodash';
import { DocsInfo } from 'ts/pages/documentation/docs_info';
import {
- CustomType,
- CustomTypeChild,
- DocAgnosticFormat,
- DocSection,
- IndexSignature,
- KindString,
- Parameter,
- Property,
- SectionsMap,
- Type,
- TypeDocNode,
- TypeDocType,
- TypeParameter,
- TypescriptMethod,
+ CustomType,
+ CustomTypeChild,
+ DocAgnosticFormat,
+ DocSection,
+ IndexSignature,
+ KindString,
+ Parameter,
+ Property,
+ SectionsMap,
+ Type,
+ TypeDocNode,
+ TypeDocType,
+ TypeParameter,
+ TypescriptMethod,
} from 'ts/types';
import { utils } from 'ts/utils/utils';
export const typeDocUtils = {
- isType(entity: TypeDocNode): boolean {
- return (
- entity.kindString === KindString.Interface ||
- entity.kindString === KindString.Function ||
- entity.kindString === KindString.TypeAlias ||
- entity.kindString === KindString.Variable ||
- entity.kindString === KindString.Enumeration
- );
- },
- isMethod(entity: TypeDocNode): boolean {
- return entity.kindString === KindString.Method;
- },
- isConstructor(entity: TypeDocNode): boolean {
- return entity.kindString === KindString.Constructor;
- },
- isProperty(entity: TypeDocNode): boolean {
- return entity.kindString === KindString.Property;
- },
- isPrivateOrProtectedProperty(propertyName: string): boolean {
- return _.startsWith(propertyName, '_');
- },
- getModuleDefinitionBySectionNameIfExists(
- versionDocObj: TypeDocNode,
- modulePaths: string[],
- ): TypeDocNode | undefined {
- const modules = versionDocObj.children;
- for (const mod of modules) {
- if (_.includes(modulePaths, mod.name)) {
- const moduleWithName = mod;
- return moduleWithName;
- }
- }
- return undefined;
- },
- convertToDocAgnosticFormat(typeDocJson: TypeDocNode, docsInfo: DocsInfo): DocAgnosticFormat {
- const subMenus = _.values(docsInfo.getMenu());
- const orderedSectionNames = _.flatten(subMenus);
- const docAgnosticFormat: DocAgnosticFormat = {};
- _.each(orderedSectionNames, sectionName => {
- const modulePathsIfExists = docsInfo.getModulePathsIfExists(sectionName);
- if (_.isUndefined(modulePathsIfExists)) {
- return; // no-op
- }
- const packageDefinitionIfExists = typeDocUtils.getModuleDefinitionBySectionNameIfExists(
- typeDocJson,
- modulePathsIfExists,
- );
- if (_.isUndefined(packageDefinitionIfExists)) {
- return; // no-op
- }
+ isType(entity: TypeDocNode): boolean {
+ return (
+ entity.kindString === KindString.Interface ||
+ entity.kindString === KindString.Function ||
+ entity.kindString === KindString.TypeAlias ||
+ entity.kindString === KindString.Variable ||
+ entity.kindString === KindString.Enumeration
+ );
+ },
+ isMethod(entity: TypeDocNode): boolean {
+ return entity.kindString === KindString.Method;
+ },
+ isConstructor(entity: TypeDocNode): boolean {
+ return entity.kindString === KindString.Constructor;
+ },
+ isProperty(entity: TypeDocNode): boolean {
+ return entity.kindString === KindString.Property;
+ },
+ isPrivateOrProtectedProperty(propertyName: string): boolean {
+ return _.startsWith(propertyName, '_');
+ },
+ getModuleDefinitionBySectionNameIfExists(
+ versionDocObj: TypeDocNode,
+ modulePaths: string[],
+ ): TypeDocNode | undefined {
+ const modules = versionDocObj.children;
+ for (const mod of modules) {
+ if (_.includes(modulePaths, mod.name)) {
+ const moduleWithName = mod;
+ return moduleWithName;
+ }
+ }
+ return undefined;
+ },
+ convertToDocAgnosticFormat(typeDocJson: TypeDocNode, docsInfo: DocsInfo): DocAgnosticFormat {
+ const subMenus = _.values(docsInfo.getMenu());
+ const orderedSectionNames = _.flatten(subMenus);
+ const docAgnosticFormat: DocAgnosticFormat = {};
+ _.each(orderedSectionNames, sectionName => {
+ const modulePathsIfExists = docsInfo.getModulePathsIfExists(sectionName);
+ if (_.isUndefined(modulePathsIfExists)) {
+ return; // no-op
+ }
+ const packageDefinitionIfExists = typeDocUtils.getModuleDefinitionBySectionNameIfExists(
+ typeDocJson,
+ modulePathsIfExists,
+ );
+ if (_.isUndefined(packageDefinitionIfExists)) {
+ return; // no-op
+ }
- // Since the `types.ts` file is the only file that does not export a module/class but
- // instead has each type export itself, we do not need to go down two levels of nesting
- // for it.
- let entities;
- let packageComment = '';
- if (sectionName === docsInfo.sections.types) {
- entities = packageDefinitionIfExists.children;
- } else {
- entities = packageDefinitionIfExists.children[0].children;
- const commentObj = packageDefinitionIfExists.children[0].comment;
- packageComment = !_.isUndefined(commentObj) ? commentObj.shortText : packageComment;
- }
+ // Since the `types.ts` file is the only file that does not export a module/class but
+ // instead has each type export itself, we do not need to go down two levels of nesting
+ // for it.
+ let entities;
+ let packageComment = '';
+ if (sectionName === docsInfo.sections.types) {
+ entities = packageDefinitionIfExists.children;
+ } else {
+ entities = packageDefinitionIfExists.children[0].children;
+ const commentObj = packageDefinitionIfExists.children[0].comment;
+ packageComment = !_.isUndefined(commentObj) ? commentObj.shortText : packageComment;
+ }
- const docSection = typeDocUtils._convertEntitiesToDocSection(entities, docsInfo, sectionName);
- docSection.comment = packageComment;
- docAgnosticFormat[sectionName] = docSection;
- });
- return docAgnosticFormat;
- },
- _convertEntitiesToDocSection(entities: TypeDocNode[], docsInfo: DocsInfo, sectionName: string) {
- const docSection: DocSection = {
- comment: '',
- constructors: [],
- methods: [],
- properties: [],
- types: [],
- };
+ const docSection = typeDocUtils._convertEntitiesToDocSection(entities, docsInfo, sectionName);
+ docSection.comment = packageComment;
+ docAgnosticFormat[sectionName] = docSection;
+ });
+ return docAgnosticFormat;
+ },
+ _convertEntitiesToDocSection(entities: TypeDocNode[], docsInfo: DocsInfo, sectionName: string) {
+ const docSection: DocSection = {
+ comment: '',
+ constructors: [],
+ methods: [],
+ properties: [],
+ types: [],
+ };
- let isConstructor;
- _.each(entities, entity => {
- switch (entity.kindString) {
- case KindString.Constructor:
- isConstructor = true;
- const constructor = typeDocUtils._convertMethod(
- entity,
- isConstructor,
- docsInfo.sections,
- sectionName,
- docsInfo.subPackageName,
- );
- docSection.constructors.push(constructor);
- break;
+ let isConstructor;
+ _.each(entities, entity => {
+ switch (entity.kindString) {
+ case KindString.Constructor:
+ isConstructor = true;
+ const constructor = typeDocUtils._convertMethod(
+ entity,
+ isConstructor,
+ docsInfo.sections,
+ sectionName,
+ docsInfo.subPackageName,
+ );
+ docSection.constructors.push(constructor);
+ break;
- case KindString.Method:
- if (entity.flags.isPublic) {
- isConstructor = false;
- const method = typeDocUtils._convertMethod(
- entity,
- isConstructor,
- docsInfo.sections,
- sectionName,
- docsInfo.subPackageName,
- );
- docSection.methods.push(method);
- }
- break;
+ case KindString.Method:
+ if (entity.flags.isPublic) {
+ isConstructor = false;
+ const method = typeDocUtils._convertMethod(
+ entity,
+ isConstructor,
+ docsInfo.sections,
+ sectionName,
+ docsInfo.subPackageName,
+ );
+ docSection.methods.push(method);
+ }
+ break;
- case KindString.Property:
- if (!typeDocUtils.isPrivateOrProtectedProperty(entity.name)) {
- const property = typeDocUtils._convertProperty(
- entity,
- docsInfo.sections,
- sectionName,
- docsInfo.subPackageName,
- );
- docSection.properties.push(property);
- }
- break;
+ case KindString.Property:
+ if (!typeDocUtils.isPrivateOrProtectedProperty(entity.name)) {
+ const property = typeDocUtils._convertProperty(
+ entity,
+ docsInfo.sections,
+ sectionName,
+ docsInfo.subPackageName,
+ );
+ docSection.properties.push(property);
+ }
+ break;
- case KindString.Interface:
- case KindString.Function:
- case KindString.Variable:
- case KindString.Enumeration:
- case KindString.TypeAlias:
- if (docsInfo.isPublicType(entity.name)) {
- const customType = typeDocUtils._convertCustomType(
- entity,
- docsInfo.sections,
- sectionName,
- docsInfo.subPackageName,
- );
- docSection.types.push(customType);
- }
- break;
+ case KindString.Interface:
+ case KindString.Function:
+ case KindString.Variable:
+ case KindString.Enumeration:
+ case KindString.TypeAlias:
+ if (docsInfo.isPublicType(entity.name)) {
+ const customType = typeDocUtils._convertCustomType(
+ entity,
+ docsInfo.sections,
+ sectionName,
+ docsInfo.subPackageName,
+ );
+ docSection.types.push(customType);
+ }
+ break;
- default:
- throw utils.spawnSwitchErr('kindString', entity.kindString);
- }
- });
- return docSection;
- },
- _convertCustomType(
- entity: TypeDocNode,
- sections: SectionsMap,
- sectionName: string,
- subPackageName: string,
- ): CustomType {
- const typeIfExists = !_.isUndefined(entity.type)
- ? typeDocUtils._convertType(entity.type, sections, sectionName, subPackageName)
- : undefined;
- const isConstructor = false;
- const methodIfExists = !_.isUndefined(entity.declaration)
- ? typeDocUtils._convertMethod(entity.declaration, isConstructor, sections, sectionName, subPackageName)
- : undefined;
- const indexSignatureIfExists = !_.isUndefined(entity.indexSignature)
- ? typeDocUtils._convertIndexSignature(entity.indexSignature[0], sections, sectionName, subPackageName)
- : undefined;
- const commentIfExists =
- !_.isUndefined(entity.comment) && !_.isUndefined(entity.comment.shortText)
- ? entity.comment.shortText
- : undefined;
+ default:
+ throw utils.spawnSwitchErr('kindString', entity.kindString);
+ }
+ });
+ return docSection;
+ },
+ _convertCustomType(
+ entity: TypeDocNode,
+ sections: SectionsMap,
+ sectionName: string,
+ subPackageName: string,
+ ): CustomType {
+ const typeIfExists = !_.isUndefined(entity.type)
+ ? typeDocUtils._convertType(entity.type, sections, sectionName, subPackageName)
+ : undefined;
+ const isConstructor = false;
+ const methodIfExists = !_.isUndefined(entity.declaration)
+ ? typeDocUtils._convertMethod(entity.declaration, isConstructor, sections, sectionName, subPackageName)
+ : undefined;
+ const indexSignatureIfExists = !_.isUndefined(entity.indexSignature)
+ ? typeDocUtils._convertIndexSignature(entity.indexSignature[0], sections, sectionName, subPackageName)
+ : undefined;
+ const commentIfExists =
+ !_.isUndefined(entity.comment) && !_.isUndefined(entity.comment.shortText)
+ ? entity.comment.shortText
+ : undefined;
- const childrenIfExist = !_.isUndefined(entity.children)
- ? _.map(entity.children, (child: TypeDocNode) => {
- const childTypeIfExists = !_.isUndefined(child.type)
- ? typeDocUtils._convertType(child.type, sections, sectionName, subPackageName)
- : undefined;
- const c: CustomTypeChild = {
- name: child.name,
- type: childTypeIfExists,
- defaultValue: child.defaultValue,
- };
- return c;
- })
- : undefined;
+ const childrenIfExist = !_.isUndefined(entity.children)
+ ? _.map(entity.children, (child: TypeDocNode) => {
+ const childTypeIfExists = !_.isUndefined(child.type)
+ ? typeDocUtils._convertType(child.type, sections, sectionName, subPackageName)
+ : undefined;
+ const c: CustomTypeChild = {
+ name: child.name,
+ type: childTypeIfExists,
+ defaultValue: child.defaultValue,
+ };
+ return c;
+ })
+ : undefined;
- const customType = {
- name: entity.name,
- kindString: entity.kindString,
- type: typeIfExists,
- method: methodIfExists,
- indexSignature: indexSignatureIfExists,
- defaultValue: entity.defaultValue,
- comment: commentIfExists,
- children: childrenIfExist,
- };
- return customType;
- },
- _convertIndexSignature(
- entity: TypeDocNode,
- sections: SectionsMap,
- sectionName: string,
- subPackageName: string,
- ): IndexSignature {
- const key = entity.parameters[0];
- const indexSignature = {
- keyName: key.name,
- keyType: typeDocUtils._convertType(key.type, sections, sectionName, subPackageName),
- valueName: entity.type.name,
- };
- return indexSignature;
- },
- _convertProperty(
- entity: TypeDocNode,
- sections: SectionsMap,
- sectionName: string,
- subPackageName: string,
- ): Property {
- const source = entity.sources[0];
- const commentIfExists = !_.isUndefined(entity.comment) ? entity.comment.shortText : undefined;
- const property = {
- name: entity.name,
- type: typeDocUtils._convertType(entity.type, sections, sectionName, subPackageName),
- source: {
- fileName: source.fileName,
- line: source.line,
- },
- comment: commentIfExists,
- };
- return property;
- },
- _convertMethod(
- entity: TypeDocNode,
- isConstructor: boolean,
- sections: SectionsMap,
- sectionName: string,
- subPackageName: string,
- ): TypescriptMethod {
- const signature = entity.signatures[0];
- const source = entity.sources[0];
- const hasComment = !_.isUndefined(signature.comment);
- const isStatic = _.isUndefined(entity.flags.isStatic) ? false : entity.flags.isStatic;
+ const customType = {
+ name: entity.name,
+ kindString: entity.kindString,
+ type: typeIfExists,
+ method: methodIfExists,
+ indexSignature: indexSignatureIfExists,
+ defaultValue: entity.defaultValue,
+ comment: commentIfExists,
+ children: childrenIfExist,
+ };
+ return customType;
+ },
+ _convertIndexSignature(
+ entity: TypeDocNode,
+ sections: SectionsMap,
+ sectionName: string,
+ subPackageName: string,
+ ): IndexSignature {
+ const key = entity.parameters[0];
+ const indexSignature = {
+ keyName: key.name,
+ keyType: typeDocUtils._convertType(key.type, sections, sectionName, subPackageName),
+ valueName: entity.type.name,
+ };
+ return indexSignature;
+ },
+ _convertProperty(
+ entity: TypeDocNode,
+ sections: SectionsMap,
+ sectionName: string,
+ subPackageName: string,
+ ): Property {
+ const source = entity.sources[0];
+ const commentIfExists = !_.isUndefined(entity.comment) ? entity.comment.shortText : undefined;
+ const property = {
+ name: entity.name,
+ type: typeDocUtils._convertType(entity.type, sections, sectionName, subPackageName),
+ source: {
+ fileName: source.fileName,
+ line: source.line,
+ },
+ comment: commentIfExists,
+ };
+ return property;
+ },
+ _convertMethod(
+ entity: TypeDocNode,
+ isConstructor: boolean,
+ sections: SectionsMap,
+ sectionName: string,
+ subPackageName: string,
+ ): TypescriptMethod {
+ const signature = entity.signatures[0];
+ const source = entity.sources[0];
+ const hasComment = !_.isUndefined(signature.comment);
+ const isStatic = _.isUndefined(entity.flags.isStatic) ? false : entity.flags.isStatic;
- // HACK: we use the fact that the sectionName is the same as the property name at the top-level
- // of the public interface. In the future, we shouldn't use this hack but rather get it from the JSON.
- let callPath;
- if (isConstructor || entity.name === '__type') {
- callPath = '';
- } else if (subPackageName === '0x.js') {
- const topLevelInterface = isStatic ? 'ZeroEx.' : 'zeroEx.';
- callPath =
- !_.isUndefined(sections.zeroEx) && sectionName !== sections.zeroEx
- ? `${topLevelInterface}${sectionName}.`
- : topLevelInterface;
- } else {
- callPath = `${sectionName}.`;
- }
+ // HACK: we use the fact that the sectionName is the same as the property name at the top-level
+ // of the public interface. In the future, we shouldn't use this hack but rather get it from the JSON.
+ let callPath;
+ if (isConstructor || entity.name === '__type') {
+ callPath = '';
+ } else if (subPackageName === '0x.js') {
+ const topLevelInterface = isStatic ? 'ZeroEx.' : 'zeroEx.';
+ callPath =
+ !_.isUndefined(sections.zeroEx) && sectionName !== sections.zeroEx
+ ? `${topLevelInterface}${sectionName}.`
+ : topLevelInterface;
+ } else {
+ callPath = `${sectionName}.`;
+ }
- const parameters = _.map(signature.parameters, param => {
- return typeDocUtils._convertParameter(param, sections, sectionName, subPackageName);
- });
- const returnType = typeDocUtils._convertType(signature.type, sections, sectionName, subPackageName);
- const typeParameter = _.isUndefined(signature.typeParameter)
- ? undefined
- : typeDocUtils._convertTypeParameter(signature.typeParameter[0], sections, sectionName, subPackageName);
+ const parameters = _.map(signature.parameters, param => {
+ return typeDocUtils._convertParameter(param, sections, sectionName, subPackageName);
+ });
+ const returnType = typeDocUtils._convertType(signature.type, sections, sectionName, subPackageName);
+ const typeParameter = _.isUndefined(signature.typeParameter)
+ ? undefined
+ : typeDocUtils._convertTypeParameter(signature.typeParameter[0], sections, sectionName, subPackageName);
- const method = {
- isConstructor,
- isStatic,
- name: signature.name,
- comment: hasComment ? signature.comment.shortText : undefined,
- returnComment: hasComment && signature.comment.returns ? signature.comment.returns : undefined,
- source: {
- fileName: source.fileName,
- line: source.line,
- },
- callPath,
- parameters,
- returnType,
- typeParameter,
- };
- return method;
- },
- _convertTypeParameter(
- entity: TypeDocNode,
- sections: SectionsMap,
- sectionName: string,
- subPackageName: string,
- ): TypeParameter {
- const type = typeDocUtils._convertType(entity.type, sections, sectionName, subPackageName);
- const parameter = {
- name: entity.name,
- type,
- };
- return parameter;
- },
- _convertParameter(
- entity: TypeDocNode,
- sections: SectionsMap,
- sectionName: string,
- subPackageName: string,
- ): Parameter {
- let comment = '<No comment>';
- if (entity.comment && entity.comment.shortText) {
- comment = entity.comment.shortText;
- } else if (entity.comment && entity.comment.text) {
- comment = entity.comment.text;
- }
+ const method = {
+ isConstructor,
+ isStatic,
+ name: signature.name,
+ comment: hasComment ? signature.comment.shortText : undefined,
+ returnComment: hasComment && signature.comment.returns ? signature.comment.returns : undefined,
+ source: {
+ fileName: source.fileName,
+ line: source.line,
+ },
+ callPath,
+ parameters,
+ returnType,
+ typeParameter,
+ };
+ return method;
+ },
+ _convertTypeParameter(
+ entity: TypeDocNode,
+ sections: SectionsMap,
+ sectionName: string,
+ subPackageName: string,
+ ): TypeParameter {
+ const type = typeDocUtils._convertType(entity.type, sections, sectionName, subPackageName);
+ const parameter = {
+ name: entity.name,
+ type,
+ };
+ return parameter;
+ },
+ _convertParameter(
+ entity: TypeDocNode,
+ sections: SectionsMap,
+ sectionName: string,
+ subPackageName: string,
+ ): Parameter {
+ let comment = '<No comment>';
+ if (entity.comment && entity.comment.shortText) {
+ comment = entity.comment.shortText;
+ } else if (entity.comment && entity.comment.text) {
+ comment = entity.comment.text;
+ }
- const isOptional = !_.isUndefined(entity.flags.isOptional) ? entity.flags.isOptional : false;
+ const isOptional = !_.isUndefined(entity.flags.isOptional) ? entity.flags.isOptional : false;
- const type = typeDocUtils._convertType(entity.type, sections, sectionName, subPackageName);
+ const type = typeDocUtils._convertType(entity.type, sections, sectionName, subPackageName);
- const parameter = {
- name: entity.name,
- comment,
- isOptional,
- type,
- };
- return parameter;
- },
- _convertType(entity: TypeDocType, sections: SectionsMap, sectionName: string, subPackageName: string): Type {
- const typeArguments = _.map(entity.typeArguments, typeArgument => {
- return typeDocUtils._convertType(typeArgument, sections, sectionName, subPackageName);
- });
- const types = _.map(entity.types, t => {
- return typeDocUtils._convertType(t, sections, sectionName, subPackageName);
- });
+ const parameter = {
+ name: entity.name,
+ comment,
+ isOptional,
+ type,
+ };
+ return parameter;
+ },
+ _convertType(entity: TypeDocType, sections: SectionsMap, sectionName: string, subPackageName: string): Type {
+ const typeArguments = _.map(entity.typeArguments, typeArgument => {
+ return typeDocUtils._convertType(typeArgument, sections, sectionName, subPackageName);
+ });
+ const types = _.map(entity.types, t => {
+ return typeDocUtils._convertType(t, sections, sectionName, subPackageName);
+ });
- const isConstructor = false;
- const methodIfExists = !_.isUndefined(entity.declaration)
- ? typeDocUtils._convertMethod(entity.declaration, isConstructor, sections, sectionName, subPackageName)
- : undefined;
+ const isConstructor = false;
+ const methodIfExists = !_.isUndefined(entity.declaration)
+ ? typeDocUtils._convertMethod(entity.declaration, isConstructor, sections, sectionName, subPackageName)
+ : undefined;
- const elementTypeIfExists = !_.isUndefined(entity.elementType)
- ? {
- name: entity.elementType.name,
- typeDocType: entity.elementType.type,
- }
- : undefined;
+ const elementTypeIfExists = !_.isUndefined(entity.elementType)
+ ? {
+ name: entity.elementType.name,
+ typeDocType: entity.elementType.type,
+ }
+ : undefined;
- const type = {
- name: entity.name,
- value: entity.value,
- typeDocType: entity.type,
- typeArguments,
- elementType: elementTypeIfExists,
- types,
- method: methodIfExists,
- };
- return type;
- },
+ const type = {
+ name: entity.name,
+ value: entity.value,
+ typeDocType: entity.type,
+ typeArguments,
+ elementType: elementTypeIfExists,
+ types,
+ method: methodIfExists,
+ };
+ return type;
+ },
};
diff --git a/packages/website/ts/utils/utils.ts b/packages/website/ts/utils/utils.ts
index ea5b689ae..13a6d6ae2 100644
--- a/packages/website/ts/utils/utils.ts
+++ b/packages/website/ts/utils/utils.ts
@@ -5,15 +5,15 @@ import isMobile = require('is-mobile');
import * as _ from 'lodash';
import * as moment from 'moment';
import {
- EtherscanLinkSuffixes,
- Networks,
- Order,
- ScreenWidths,
- Side,
- SideToAssetToken,
- SignatureData,
- Token,
- TokenByAddress,
+ EtherscanLinkSuffixes,
+ Networks,
+ Order,
+ ScreenWidths,
+ Side,
+ SideToAssetToken,
+ SignatureData,
+ Token,
+ TokenByAddress,
} from 'ts/types';
import { configs } from 'ts/utils/configs';
import { constants } from 'ts/utils/constants';
@@ -23,257 +23,257 @@ const LG_MIN_EM = 64;
const MD_MIN_EM = 52;
export const utils = {
- assert(condition: boolean, message: string) {
- if (!condition) {
- throw new Error(message);
- }
- },
- spawnSwitchErr(name: string, value: any) {
- return new Error(`Unexpected switch value: ${value} encountered for ${name}`);
- },
- isNumeric(n: string) {
- return !isNaN(parseFloat(n)) && isFinite(Number(n));
- },
- // This default unix timestamp is used for orders where the user does not specify an expiry date.
- // It is a fixed constant so that both the redux store's INITIAL_STATE and components can check for
- // whether a user has set an expiry date or not. It is set unrealistically high so as not to collide
- // with actual values a user would select.
- initialOrderExpiryUnixTimestampSec(): BigNumber {
- const m = moment('2050-01-01');
- return new BigNumber(m.unix());
- },
- convertToUnixTimestampSeconds(date: moment.Moment, time?: moment.Moment): BigNumber {
- const finalMoment = date;
- if (!_.isUndefined(time)) {
- finalMoment.hours(time.hours());
- finalMoment.minutes(time.minutes());
- }
- return new BigNumber(finalMoment.unix());
- },
- convertToMomentFromUnixTimestamp(unixTimestampSec: BigNumber): moment.Moment {
- return moment.unix(unixTimestampSec.toNumber());
- },
- convertToReadableDateTimeFromUnixTimestamp(unixTimestampSec: BigNumber): string {
- const m = this.convertToMomentFromUnixTimestamp(unixTimestampSec);
- const formattedDate: string = m.format('h:MMa MMMM D YYYY');
- return formattedDate;
- },
- generateOrder(
- networkId: number,
- exchangeContract: string,
- sideToAssetToken: SideToAssetToken,
- orderExpiryTimestamp: BigNumber,
- orderTakerAddress: string,
- orderMakerAddress: string,
- makerFee: BigNumber,
- takerFee: BigNumber,
- feeRecipient: string,
- signatureData: SignatureData,
- tokenByAddress: TokenByAddress,
- orderSalt: BigNumber,
- ): Order {
- const makerToken = tokenByAddress[sideToAssetToken[Side.Deposit].address];
- const takerToken = tokenByAddress[sideToAssetToken[Side.Receive].address];
- const order = {
- maker: {
- address: orderMakerAddress,
- token: {
- name: makerToken.name,
- symbol: makerToken.symbol,
- decimals: makerToken.decimals,
- address: makerToken.address,
- },
- amount: sideToAssetToken[Side.Deposit].amount.toString(),
- feeAmount: makerFee.toString(),
- },
- taker: {
- address: orderTakerAddress,
- token: {
- name: takerToken.name,
- symbol: takerToken.symbol,
- decimals: takerToken.decimals,
- address: takerToken.address,
- },
- amount: sideToAssetToken[Side.Receive].amount.toString(),
- feeAmount: takerFee.toString(),
- },
- expiration: orderExpiryTimestamp.toString(),
- feeRecipient,
- salt: orderSalt.toString(),
- signature: signatureData,
- exchangeContract,
- networkId,
- };
- return order;
- },
- consoleLog(message: string) {
- /* tslint:disable */
- console.log(message);
- /* tslint:enable */
- },
- async sleepAsync(ms: number) {
- return new Promise(resolve => setTimeout(resolve, ms));
- },
- deepEqual(actual: any, expected: any, opts?: { strict: boolean }) {
- return deepEqual(actual, expected, opts);
- },
- getColSize(items: number) {
- const bassCssGridSize = 12; // Source: http://basscss.com/#basscss-grid
- const colSize = bassCssGridSize / items;
- if (!_.isInteger(colSize)) {
- throw new Error(`Number of cols must be divisible by ${bassCssGridSize}`);
- }
- return colSize;
- },
- getScreenWidth() {
- const documentEl = document.documentElement;
- const body = document.getElementsByTagName('body')[0];
- const widthInPx = window.innerWidth || documentEl.clientWidth || body.clientWidth;
- const bodyStyles: any = window.getComputedStyle(document.querySelector('body'));
- const widthInEm = widthInPx / parseFloat(bodyStyles['font-size']);
+ assert(condition: boolean, message: string) {
+ if (!condition) {
+ throw new Error(message);
+ }
+ },
+ spawnSwitchErr(name: string, value: any) {
+ return new Error(`Unexpected switch value: ${value} encountered for ${name}`);
+ },
+ isNumeric(n: string) {
+ return !isNaN(parseFloat(n)) && isFinite(Number(n));
+ },
+ // This default unix timestamp is used for orders where the user does not specify an expiry date.
+ // It is a fixed constant so that both the redux store's INITIAL_STATE and components can check for
+ // whether a user has set an expiry date or not. It is set unrealistically high so as not to collide
+ // with actual values a user would select.
+ initialOrderExpiryUnixTimestampSec(): BigNumber {
+ const m = moment('2050-01-01');
+ return new BigNumber(m.unix());
+ },
+ convertToUnixTimestampSeconds(date: moment.Moment, time?: moment.Moment): BigNumber {
+ const finalMoment = date;
+ if (!_.isUndefined(time)) {
+ finalMoment.hours(time.hours());
+ finalMoment.minutes(time.minutes());
+ }
+ return new BigNumber(finalMoment.unix());
+ },
+ convertToMomentFromUnixTimestamp(unixTimestampSec: BigNumber): moment.Moment {
+ return moment.unix(unixTimestampSec.toNumber());
+ },
+ convertToReadableDateTimeFromUnixTimestamp(unixTimestampSec: BigNumber): string {
+ const m = this.convertToMomentFromUnixTimestamp(unixTimestampSec);
+ const formattedDate: string = m.format('h:MMa MMMM D YYYY');
+ return formattedDate;
+ },
+ generateOrder(
+ networkId: number,
+ exchangeContract: string,
+ sideToAssetToken: SideToAssetToken,
+ orderExpiryTimestamp: BigNumber,
+ orderTakerAddress: string,
+ orderMakerAddress: string,
+ makerFee: BigNumber,
+ takerFee: BigNumber,
+ feeRecipient: string,
+ signatureData: SignatureData,
+ tokenByAddress: TokenByAddress,
+ orderSalt: BigNumber,
+ ): Order {
+ const makerToken = tokenByAddress[sideToAssetToken[Side.Deposit].address];
+ const takerToken = tokenByAddress[sideToAssetToken[Side.Receive].address];
+ const order = {
+ maker: {
+ address: orderMakerAddress,
+ token: {
+ name: makerToken.name,
+ symbol: makerToken.symbol,
+ decimals: makerToken.decimals,
+ address: makerToken.address,
+ },
+ amount: sideToAssetToken[Side.Deposit].amount.toString(),
+ feeAmount: makerFee.toString(),
+ },
+ taker: {
+ address: orderTakerAddress,
+ token: {
+ name: takerToken.name,
+ symbol: takerToken.symbol,
+ decimals: takerToken.decimals,
+ address: takerToken.address,
+ },
+ amount: sideToAssetToken[Side.Receive].amount.toString(),
+ feeAmount: takerFee.toString(),
+ },
+ expiration: orderExpiryTimestamp.toString(),
+ feeRecipient,
+ salt: orderSalt.toString(),
+ signature: signatureData,
+ exchangeContract,
+ networkId,
+ };
+ return order;
+ },
+ consoleLog(message: string) {
+ /* tslint:disable */
+ console.log(message);
+ /* tslint:enable */
+ },
+ async sleepAsync(ms: number) {
+ return new Promise(resolve => setTimeout(resolve, ms));
+ },
+ deepEqual(actual: any, expected: any, opts?: { strict: boolean }) {
+ return deepEqual(actual, expected, opts);
+ },
+ getColSize(items: number) {
+ const bassCssGridSize = 12; // Source: http://basscss.com/#basscss-grid
+ const colSize = bassCssGridSize / items;
+ if (!_.isInteger(colSize)) {
+ throw new Error(`Number of cols must be divisible by ${bassCssGridSize}`);
+ }
+ return colSize;
+ },
+ getScreenWidth() {
+ const documentEl = document.documentElement;
+ const body = document.getElementsByTagName('body')[0];
+ const widthInPx = window.innerWidth || documentEl.clientWidth || body.clientWidth;
+ const bodyStyles: any = window.getComputedStyle(document.querySelector('body'));
+ const widthInEm = widthInPx / parseFloat(bodyStyles['font-size']);
- // This logic mirrors the CSS media queries in BassCSS for the `lg-`, `md-` and `sm-` CSS
- // class prefixes. Do not edit these.
- if (widthInEm > LG_MIN_EM) {
- return ScreenWidths.Lg;
- } else if (widthInEm > MD_MIN_EM) {
- return ScreenWidths.Md;
- } else {
- return ScreenWidths.Sm;
- }
- },
- isUserOnMobile(): boolean {
- const isUserOnMobile = isMobile();
- return isUserOnMobile;
- },
- getEtherScanLinkIfExists(addressOrTxHash: string, networkId: number, suffix: EtherscanLinkSuffixes): string {
- const networkName = constants.NETWORK_NAME_BY_ID[networkId];
- if (_.isUndefined(networkName)) {
- return undefined;
- }
- const etherScanPrefix = networkName === Networks.mainnet ? '' : `${networkName.toLowerCase()}.`;
- return `https://${etherScanPrefix}etherscan.io/${suffix}/${addressOrTxHash}`;
- },
- setUrlHash(anchorId: string) {
- window.location.hash = anchorId;
- },
- async isU2FSupportedAsync(): Promise<boolean> {
- const w = window as any;
- return new Promise((resolve: (isSupported: boolean) => void) => {
- if (w.u2f && !w.u2f.getApiVersion) {
- // u2f object was found (Firefox with extension)
- resolve(true);
- } else {
- // u2f object was not found. Using Google polyfill
- // HACK: u2f.getApiVersion will simply not return a version if the
- // U2F call fails for any reason. Because of this, we set a hard 3sec
- // timeout to the request on our end.
- const getApiVersionTimeoutMs = 3000;
- const intervalId = setTimeout(() => {
- resolve(false);
- }, getApiVersionTimeoutMs);
- u2f.getApiVersion((version: number) => {
- clearTimeout(intervalId);
- resolve(true);
- });
- }
- });
- },
- // This checks the error message returned from an injected Web3 instance on the page
- // after a user was prompted to sign a message or send a transaction and decided to
- // reject the request.
- didUserDenyWeb3Request(errMsg: string) {
- const metamaskDenialErrMsg = 'User denied message';
- const paritySignerDenialErrMsg = 'Request has been rejected';
- const ledgerDenialErrMsg = 'Invalid status 6985';
- const isUserDeniedErrMsg =
- _.includes(errMsg, metamaskDenialErrMsg) ||
- _.includes(errMsg, paritySignerDenialErrMsg) ||
- _.includes(errMsg, ledgerDenialErrMsg);
- return isUserDeniedErrMsg;
- },
- getCurrentEnvironment() {
- switch (location.host) {
- case configs.DOMAIN_DEVELOPMENT:
- return 'development';
- case configs.DOMAIN_STAGING:
- return 'staging';
- case configs.DOMAIN_PRODUCTION:
- return 'production';
- default:
- return 'production';
- }
- },
- getIdFromName(name: string) {
- const id = name.replace(/ /g, '-');
- return id;
- },
- getAddressBeginAndEnd(address: string): string {
- const truncatedAddress = `${address.substring(0, 6)}...${address.substr(-4)}`; // 0x3d5a...b287
- return truncatedAddress;
- },
- hasUniqueNameAndSymbol(tokens: Token[], token: Token) {
- if (token.isRegistered) {
- return true; // Since it's registered, it is the canonical token
- }
- const registeredTokens = _.filter(tokens, t => t.isRegistered);
- const tokenWithSameNameIfExists = _.find(registeredTokens, {
- name: token.name,
- });
- const isUniqueName = _.isUndefined(tokenWithSameNameIfExists);
- const tokenWithSameSymbolIfExists = _.find(registeredTokens, {
- name: token.symbol,
- });
- const isUniqueSymbol = _.isUndefined(tokenWithSameSymbolIfExists);
- return isUniqueName && isUniqueSymbol;
- },
- zeroExErrToHumanReadableErrMsg(error: ZeroExError | ExchangeContractErrs, takerAddress: string): string {
- const ZeroExErrorToHumanReadableError: { [error: string]: string } = {
- [ZeroExError.ExchangeContractDoesNotExist]: 'Exchange contract does not exist',
- [ZeroExError.EtherTokenContractDoesNotExist]: 'EtherToken contract does not exist',
- [ZeroExError.TokenTransferProxyContractDoesNotExist]: 'TokenTransferProxy contract does not exist',
- [ZeroExError.TokenRegistryContractDoesNotExist]: 'TokenRegistry contract does not exist',
- [ZeroExError.TokenContractDoesNotExist]: 'Token contract does not exist',
- [ZeroExError.ZRXContractDoesNotExist]: 'ZRX contract does not exist',
- [ZeroExError.UnhandledError]: 'Unhandled error occured',
- [ZeroExError.UserHasNoAssociatedAddress]: 'User has no addresses available',
- [ZeroExError.InvalidSignature]: 'Order signature is not valid',
- [ZeroExError.ContractNotDeployedOnNetwork]: 'Contract is not deployed on the detected network',
- [ZeroExError.InvalidJump]: 'Invalid jump occured while executing the transaction',
- [ZeroExError.OutOfGas]: 'Transaction ran out of gas',
- [ZeroExError.NoNetworkId]: 'No network id detected',
- };
- const exchangeContractErrorToHumanReadableError: {
- [error: string]: string;
- } = {
- [ExchangeContractErrs.OrderFillExpired]: 'This order has expired',
- [ExchangeContractErrs.OrderCancelExpired]: 'This order has expired',
- [ExchangeContractErrs.OrderCancelAmountZero]: "Order cancel amount can't be 0",
- [ExchangeContractErrs.OrderAlreadyCancelledOrFilled]:
- 'This order has already been completely filled or cancelled',
- [ExchangeContractErrs.OrderFillAmountZero]: "Order fill amount can't be 0",
- [ExchangeContractErrs.OrderRemainingFillAmountZero]:
- 'This order has already been completely filled or cancelled',
- [ExchangeContractErrs.OrderFillRoundingError]: 'Rounding error will occur when filling this order',
- [ExchangeContractErrs.InsufficientTakerBalance]:
- 'Taker no longer has a sufficient balance to complete this order',
- [ExchangeContractErrs.InsufficientTakerAllowance]:
- 'Taker no longer has a sufficient allowance to complete this order',
- [ExchangeContractErrs.InsufficientMakerBalance]:
- 'Maker no longer has a sufficient balance to complete this order',
- [ExchangeContractErrs.InsufficientMakerAllowance]:
- 'Maker no longer has a sufficient allowance to complete this order',
- [ExchangeContractErrs.InsufficientTakerFeeBalance]: 'Taker no longer has a sufficient balance to pay fees',
- [ExchangeContractErrs.InsufficientTakerFeeAllowance]:
- 'Taker no longer has a sufficient allowance to pay fees',
- [ExchangeContractErrs.InsufficientMakerFeeBalance]: 'Maker no longer has a sufficient balance to pay fees',
- [ExchangeContractErrs.InsufficientMakerFeeAllowance]:
- 'Maker no longer has a sufficient allowance to pay fees',
- [ExchangeContractErrs.TransactionSenderIsNotFillOrderTaker]: `This order can only be filled by ${takerAddress}`,
- [ExchangeContractErrs.InsufficientRemainingFillAmount]: 'Insufficient remaining fill amount',
- };
- const humanReadableErrorMsg =
- exchangeContractErrorToHumanReadableError[error] || ZeroExErrorToHumanReadableError[error];
- return humanReadableErrorMsg;
- },
+ // This logic mirrors the CSS media queries in BassCSS for the `lg-`, `md-` and `sm-` CSS
+ // class prefixes. Do not edit these.
+ if (widthInEm > LG_MIN_EM) {
+ return ScreenWidths.Lg;
+ } else if (widthInEm > MD_MIN_EM) {
+ return ScreenWidths.Md;
+ } else {
+ return ScreenWidths.Sm;
+ }
+ },
+ isUserOnMobile(): boolean {
+ const isUserOnMobile = isMobile();
+ return isUserOnMobile;
+ },
+ getEtherScanLinkIfExists(addressOrTxHash: string, networkId: number, suffix: EtherscanLinkSuffixes): string {
+ const networkName = constants.NETWORK_NAME_BY_ID[networkId];
+ if (_.isUndefined(networkName)) {
+ return undefined;
+ }
+ const etherScanPrefix = networkName === Networks.mainnet ? '' : `${networkName.toLowerCase()}.`;
+ return `https://${etherScanPrefix}etherscan.io/${suffix}/${addressOrTxHash}`;
+ },
+ setUrlHash(anchorId: string) {
+ window.location.hash = anchorId;
+ },
+ async isU2FSupportedAsync(): Promise<boolean> {
+ const w = window as any;
+ return new Promise((resolve: (isSupported: boolean) => void) => {
+ if (w.u2f && !w.u2f.getApiVersion) {
+ // u2f object was found (Firefox with extension)
+ resolve(true);
+ } else {
+ // u2f object was not found. Using Google polyfill
+ // HACK: u2f.getApiVersion will simply not return a version if the
+ // U2F call fails for any reason. Because of this, we set a hard 3sec
+ // timeout to the request on our end.
+ const getApiVersionTimeoutMs = 3000;
+ const intervalId = setTimeout(() => {
+ resolve(false);
+ }, getApiVersionTimeoutMs);
+ u2f.getApiVersion((version: number) => {
+ clearTimeout(intervalId);
+ resolve(true);
+ });
+ }
+ });
+ },
+ // This checks the error message returned from an injected Web3 instance on the page
+ // after a user was prompted to sign a message or send a transaction and decided to
+ // reject the request.
+ didUserDenyWeb3Request(errMsg: string) {
+ const metamaskDenialErrMsg = 'User denied message';
+ const paritySignerDenialErrMsg = 'Request has been rejected';
+ const ledgerDenialErrMsg = 'Invalid status 6985';
+ const isUserDeniedErrMsg =
+ _.includes(errMsg, metamaskDenialErrMsg) ||
+ _.includes(errMsg, paritySignerDenialErrMsg) ||
+ _.includes(errMsg, ledgerDenialErrMsg);
+ return isUserDeniedErrMsg;
+ },
+ getCurrentEnvironment() {
+ switch (location.host) {
+ case configs.DOMAIN_DEVELOPMENT:
+ return 'development';
+ case configs.DOMAIN_STAGING:
+ return 'staging';
+ case configs.DOMAIN_PRODUCTION:
+ return 'production';
+ default:
+ return 'production';
+ }
+ },
+ getIdFromName(name: string) {
+ const id = name.replace(/ /g, '-');
+ return id;
+ },
+ getAddressBeginAndEnd(address: string): string {
+ const truncatedAddress = `${address.substring(0, 6)}...${address.substr(-4)}`; // 0x3d5a...b287
+ return truncatedAddress;
+ },
+ hasUniqueNameAndSymbol(tokens: Token[], token: Token) {
+ if (token.isRegistered) {
+ return true; // Since it's registered, it is the canonical token
+ }
+ const registeredTokens = _.filter(tokens, t => t.isRegistered);
+ const tokenWithSameNameIfExists = _.find(registeredTokens, {
+ name: token.name,
+ });
+ const isUniqueName = _.isUndefined(tokenWithSameNameIfExists);
+ const tokenWithSameSymbolIfExists = _.find(registeredTokens, {
+ name: token.symbol,
+ });
+ const isUniqueSymbol = _.isUndefined(tokenWithSameSymbolIfExists);
+ return isUniqueName && isUniqueSymbol;
+ },
+ zeroExErrToHumanReadableErrMsg(error: ZeroExError | ExchangeContractErrs, takerAddress: string): string {
+ const ZeroExErrorToHumanReadableError: { [error: string]: string } = {
+ [ZeroExError.ExchangeContractDoesNotExist]: 'Exchange contract does not exist',
+ [ZeroExError.EtherTokenContractDoesNotExist]: 'EtherToken contract does not exist',
+ [ZeroExError.TokenTransferProxyContractDoesNotExist]: 'TokenTransferProxy contract does not exist',
+ [ZeroExError.TokenRegistryContractDoesNotExist]: 'TokenRegistry contract does not exist',
+ [ZeroExError.TokenContractDoesNotExist]: 'Token contract does not exist',
+ [ZeroExError.ZRXContractDoesNotExist]: 'ZRX contract does not exist',
+ [ZeroExError.UnhandledError]: 'Unhandled error occured',
+ [ZeroExError.UserHasNoAssociatedAddress]: 'User has no addresses available',
+ [ZeroExError.InvalidSignature]: 'Order signature is not valid',
+ [ZeroExError.ContractNotDeployedOnNetwork]: 'Contract is not deployed on the detected network',
+ [ZeroExError.InvalidJump]: 'Invalid jump occured while executing the transaction',
+ [ZeroExError.OutOfGas]: 'Transaction ran out of gas',
+ [ZeroExError.NoNetworkId]: 'No network id detected',
+ };
+ const exchangeContractErrorToHumanReadableError: {
+ [error: string]: string;
+ } = {
+ [ExchangeContractErrs.OrderFillExpired]: 'This order has expired',
+ [ExchangeContractErrs.OrderCancelExpired]: 'This order has expired',
+ [ExchangeContractErrs.OrderCancelAmountZero]: "Order cancel amount can't be 0",
+ [ExchangeContractErrs.OrderAlreadyCancelledOrFilled]:
+ 'This order has already been completely filled or cancelled',
+ [ExchangeContractErrs.OrderFillAmountZero]: "Order fill amount can't be 0",
+ [ExchangeContractErrs.OrderRemainingFillAmountZero]:
+ 'This order has already been completely filled or cancelled',
+ [ExchangeContractErrs.OrderFillRoundingError]: 'Rounding error will occur when filling this order',
+ [ExchangeContractErrs.InsufficientTakerBalance]:
+ 'Taker no longer has a sufficient balance to complete this order',
+ [ExchangeContractErrs.InsufficientTakerAllowance]:
+ 'Taker no longer has a sufficient allowance to complete this order',
+ [ExchangeContractErrs.InsufficientMakerBalance]:
+ 'Maker no longer has a sufficient balance to complete this order',
+ [ExchangeContractErrs.InsufficientMakerAllowance]:
+ 'Maker no longer has a sufficient allowance to complete this order',
+ [ExchangeContractErrs.InsufficientTakerFeeBalance]: 'Taker no longer has a sufficient balance to pay fees',
+ [ExchangeContractErrs.InsufficientTakerFeeAllowance]:
+ 'Taker no longer has a sufficient allowance to pay fees',
+ [ExchangeContractErrs.InsufficientMakerFeeBalance]: 'Maker no longer has a sufficient balance to pay fees',
+ [ExchangeContractErrs.InsufficientMakerFeeAllowance]:
+ 'Maker no longer has a sufficient allowance to pay fees',
+ [ExchangeContractErrs.TransactionSenderIsNotFillOrderTaker]: `This order can only be filled by ${takerAddress}`,
+ [ExchangeContractErrs.InsufficientRemainingFillAmount]: 'Insufficient remaining fill amount',
+ };
+ const humanReadableErrorMsg =
+ exchangeContractErrorToHumanReadableError[error] || ZeroExErrorToHumanReadableError[error];
+ return humanReadableErrorMsg;
+ },
};
diff --git a/packages/website/ts/web3_wrapper.ts b/packages/website/ts/web3_wrapper.ts
index 0cd2a5cc2..415df6e8b 100644
--- a/packages/website/ts/web3_wrapper.ts
+++ b/packages/website/ts/web3_wrapper.ts
@@ -5,154 +5,154 @@ import { utils } from 'ts/utils/utils';
import * as Web3 from 'web3';
export class Web3Wrapper {
- private _dispatcher: Dispatcher;
- private _web3: Web3;
- private _prevNetworkId: number;
- private _shouldPollUserAddress: boolean;
- private _watchNetworkAndBalanceIntervalId: NodeJS.Timer;
- private _prevUserEtherBalanceInEth: BigNumber;
- private _prevUserAddress: string;
- constructor(
- dispatcher: Dispatcher,
- provider: Web3.Provider,
- networkIdIfExists: number,
- shouldPollUserAddress: boolean,
- ) {
- this._dispatcher = dispatcher;
- this._prevNetworkId = networkIdIfExists;
- this._shouldPollUserAddress = shouldPollUserAddress;
+ private _dispatcher: Dispatcher;
+ private _web3: Web3;
+ private _prevNetworkId: number;
+ private _shouldPollUserAddress: boolean;
+ private _watchNetworkAndBalanceIntervalId: NodeJS.Timer;
+ private _prevUserEtherBalanceInEth: BigNumber;
+ private _prevUserAddress: string;
+ constructor(
+ dispatcher: Dispatcher,
+ provider: Web3.Provider,
+ networkIdIfExists: number,
+ shouldPollUserAddress: boolean,
+ ) {
+ this._dispatcher = dispatcher;
+ this._prevNetworkId = networkIdIfExists;
+ this._shouldPollUserAddress = shouldPollUserAddress;
- this._web3 = new Web3();
- this._web3.setProvider(provider);
+ this._web3 = new Web3();
+ this._web3.setProvider(provider);
- // tslint:disable-next-line:no-floating-promises
- this._startEmittingNetworkConnectionAndUserBalanceStateAsync();
- }
- public isAddress(address: string) {
- return this._web3.isAddress(address);
- }
- public async getAccountsAsync(): Promise<string[]> {
- const addresses = await promisify<string[]>(this._web3.eth.getAccounts)();
- return addresses;
- }
- public async getFirstAccountIfExistsAsync() {
- const addresses = await this.getAccountsAsync();
- if (_.isEmpty(addresses)) {
- return '';
- }
- return addresses[0];
- }
- public async getNodeVersionAsync(): Promise<string> {
- const nodeVersion = await promisify<string>(this._web3.version.getNode)();
- return nodeVersion;
- }
- public getProviderObj() {
- return this._web3.currentProvider;
- }
- public async getNetworkIdIfExists() {
- try {
- const networkId = await this._getNetworkAsync();
- return Number(networkId);
- } catch (err) {
- return undefined;
- }
- }
- public async getBalanceInEthAsync(owner: string): Promise<BigNumber> {
- const balanceInWei: BigNumber = await promisify<BigNumber>(this._web3.eth.getBalance)(owner);
- const balanceEthOldBigNumber = this._web3.fromWei(balanceInWei, 'ether');
- const balanceEth = new BigNumber(balanceEthOldBigNumber);
- return balanceEth;
- }
- public async doesContractExistAtAddressAsync(address: string): Promise<boolean> {
- const code = await promisify<string>(this._web3.eth.getCode)(address);
- // Regex matches 0x0, 0x00, 0x in order to accomodate poorly implemented clients
- const zeroHexAddressRegex = /^0[xX][0]*$/;
- const didFindCode = _.isNull(code.match(zeroHexAddressRegex));
- return didFindCode;
- }
- public async signTransactionAsync(address: string, message: string): Promise<string> {
- const signData = await promisify<string>(this._web3.eth.sign)(address, message);
- return signData;
- }
- public async getBlockTimestampAsync(blockHash: string): Promise<number> {
- const { timestamp } = await promisify<Web3.BlockWithoutTransactionData>(this._web3.eth.getBlock)(blockHash);
- return timestamp;
- }
- public destroy() {
- this._stopEmittingNetworkConnectionAndUserBalanceStateAsync();
- // HACK: stop() is only available on providerEngine instances
- const provider = this._web3.currentProvider;
- if (!_.isUndefined((provider as any).stop)) {
- (provider as any).stop();
- }
- }
- // This should only be called from the LedgerConfigDialog
- public updatePrevUserAddress(userAddress: string) {
- this._prevUserAddress = userAddress;
- }
- private async _getNetworkAsync() {
- const networkId = await promisify(this._web3.version.getNetwork)();
- return networkId;
- }
- private async _startEmittingNetworkConnectionAndUserBalanceStateAsync() {
- if (!_.isUndefined(this._watchNetworkAndBalanceIntervalId)) {
- return; // we are already emitting the state
- }
+ // tslint:disable-next-line:no-floating-promises
+ this._startEmittingNetworkConnectionAndUserBalanceStateAsync();
+ }
+ public isAddress(address: string) {
+ return this._web3.isAddress(address);
+ }
+ public async getAccountsAsync(): Promise<string[]> {
+ const addresses = await promisify<string[]>(this._web3.eth.getAccounts)();
+ return addresses;
+ }
+ public async getFirstAccountIfExistsAsync() {
+ const addresses = await this.getAccountsAsync();
+ if (_.isEmpty(addresses)) {
+ return '';
+ }
+ return addresses[0];
+ }
+ public async getNodeVersionAsync(): Promise<string> {
+ const nodeVersion = await promisify<string>(this._web3.version.getNode)();
+ return nodeVersion;
+ }
+ public getProviderObj() {
+ return this._web3.currentProvider;
+ }
+ public async getNetworkIdIfExists() {
+ try {
+ const networkId = await this._getNetworkAsync();
+ return Number(networkId);
+ } catch (err) {
+ return undefined;
+ }
+ }
+ public async getBalanceInEthAsync(owner: string): Promise<BigNumber> {
+ const balanceInWei: BigNumber = await promisify<BigNumber>(this._web3.eth.getBalance)(owner);
+ const balanceEthOldBigNumber = this._web3.fromWei(balanceInWei, 'ether');
+ const balanceEth = new BigNumber(balanceEthOldBigNumber);
+ return balanceEth;
+ }
+ public async doesContractExistAtAddressAsync(address: string): Promise<boolean> {
+ const code = await promisify<string>(this._web3.eth.getCode)(address);
+ // Regex matches 0x0, 0x00, 0x in order to accomodate poorly implemented clients
+ const zeroHexAddressRegex = /^0[xX][0]*$/;
+ const didFindCode = _.isNull(code.match(zeroHexAddressRegex));
+ return didFindCode;
+ }
+ public async signTransactionAsync(address: string, message: string): Promise<string> {
+ const signData = await promisify<string>(this._web3.eth.sign)(address, message);
+ return signData;
+ }
+ public async getBlockTimestampAsync(blockHash: string): Promise<number> {
+ const { timestamp } = await promisify<Web3.BlockWithoutTransactionData>(this._web3.eth.getBlock)(blockHash);
+ return timestamp;
+ }
+ public destroy() {
+ this._stopEmittingNetworkConnectionAndUserBalanceStateAsync();
+ // HACK: stop() is only available on providerEngine instances
+ const provider = this._web3.currentProvider;
+ if (!_.isUndefined((provider as any).stop)) {
+ (provider as any).stop();
+ }
+ }
+ // This should only be called from the LedgerConfigDialog
+ public updatePrevUserAddress(userAddress: string) {
+ this._prevUserAddress = userAddress;
+ }
+ private async _getNetworkAsync() {
+ const networkId = await promisify(this._web3.version.getNetwork)();
+ return networkId;
+ }
+ private async _startEmittingNetworkConnectionAndUserBalanceStateAsync() {
+ if (!_.isUndefined(this._watchNetworkAndBalanceIntervalId)) {
+ return; // we are already emitting the state
+ }
- let prevNodeVersion: string;
- this._prevUserEtherBalanceInEth = new BigNumber(0);
- this._dispatcher.updateNetworkId(this._prevNetworkId);
- this._watchNetworkAndBalanceIntervalId = intervalUtils.setAsyncExcludingInterval(
- async () => {
- // Check for network state changes
- const currentNetworkId = await this.getNetworkIdIfExists();
- if (currentNetworkId !== this._prevNetworkId) {
- this._prevNetworkId = currentNetworkId;
- this._dispatcher.updateNetworkId(currentNetworkId);
- }
+ let prevNodeVersion: string;
+ this._prevUserEtherBalanceInEth = new BigNumber(0);
+ this._dispatcher.updateNetworkId(this._prevNetworkId);
+ this._watchNetworkAndBalanceIntervalId = intervalUtils.setAsyncExcludingInterval(
+ async () => {
+ // Check for network state changes
+ const currentNetworkId = await this.getNetworkIdIfExists();
+ if (currentNetworkId !== this._prevNetworkId) {
+ this._prevNetworkId = currentNetworkId;
+ this._dispatcher.updateNetworkId(currentNetworkId);
+ }
- // Check for node version changes
- const currentNodeVersion = await this.getNodeVersionAsync();
- if (currentNodeVersion !== prevNodeVersion) {
- prevNodeVersion = currentNodeVersion;
- this._dispatcher.updateNodeVersion(currentNodeVersion);
- }
+ // Check for node version changes
+ const currentNodeVersion = await this.getNodeVersionAsync();
+ if (currentNodeVersion !== prevNodeVersion) {
+ prevNodeVersion = currentNodeVersion;
+ this._dispatcher.updateNodeVersion(currentNodeVersion);
+ }
- if (this._shouldPollUserAddress) {
- const userAddressIfExists = await this.getFirstAccountIfExistsAsync();
- // Update makerAddress on network change
- if (this._prevUserAddress !== userAddressIfExists) {
- this._prevUserAddress = userAddressIfExists;
- this._dispatcher.updateUserAddress(userAddressIfExists);
- }
+ if (this._shouldPollUserAddress) {
+ const userAddressIfExists = await this.getFirstAccountIfExistsAsync();
+ // Update makerAddress on network change
+ if (this._prevUserAddress !== userAddressIfExists) {
+ this._prevUserAddress = userAddressIfExists;
+ this._dispatcher.updateUserAddress(userAddressIfExists);
+ }
- // Check for user ether balance changes
- if (userAddressIfExists !== '') {
- await this._updateUserEtherBalanceAsync(userAddressIfExists);
- }
- } else {
- // This logic is primarily for the Ledger, since we don't regularly poll for the address
- // we simply update the balance for the last fetched address.
- if (!_.isEmpty(this._prevUserAddress)) {
- await this._updateUserEtherBalanceAsync(this._prevUserAddress);
- }
- }
- },
- 5000,
- (err: Error) => {
- utils.consoleLog(`Watching network and balances failed: ${err}`);
- this._stopEmittingNetworkConnectionAndUserBalanceStateAsync();
- },
- );
- }
- private async _updateUserEtherBalanceAsync(userAddress: string) {
- const balance = await this.getBalanceInEthAsync(userAddress);
- if (!balance.eq(this._prevUserEtherBalanceInEth)) {
- this._prevUserEtherBalanceInEth = balance;
- this._dispatcher.updateUserEtherBalance(balance);
- }
- }
- private _stopEmittingNetworkConnectionAndUserBalanceStateAsync() {
- intervalUtils.clearAsyncExcludingInterval(this._watchNetworkAndBalanceIntervalId);
- }
+ // Check for user ether balance changes
+ if (userAddressIfExists !== '') {
+ await this._updateUserEtherBalanceAsync(userAddressIfExists);
+ }
+ } else {
+ // This logic is primarily for the Ledger, since we don't regularly poll for the address
+ // we simply update the balance for the last fetched address.
+ if (!_.isEmpty(this._prevUserAddress)) {
+ await this._updateUserEtherBalanceAsync(this._prevUserAddress);
+ }
+ }
+ },
+ 5000,
+ (err: Error) => {
+ utils.consoleLog(`Watching network and balances failed: ${err}`);
+ this._stopEmittingNetworkConnectionAndUserBalanceStateAsync();
+ },
+ );
+ }
+ private async _updateUserEtherBalanceAsync(userAddress: string) {
+ const balance = await this.getBalanceInEthAsync(userAddress);
+ if (!balance.eq(this._prevUserEtherBalanceInEth)) {
+ this._prevUserEtherBalanceInEth = balance;
+ this._dispatcher.updateUserEtherBalance(balance);
+ }
+ }
+ private _stopEmittingNetworkConnectionAndUserBalanceStateAsync() {
+ intervalUtils.clearAsyncExcludingInterval(this._watchNetworkAndBalanceIntervalId);
+ }
}
diff --git a/packages/website/tsconfig.json b/packages/website/tsconfig.json
index 0ed2794d8..38b177d0b 100644
--- a/packages/website/tsconfig.json
+++ b/packages/website/tsconfig.json
@@ -1,17 +1,17 @@
{
- "extends": "../../tsconfig",
- "compilerOptions": {
- "allowSyntheticDefaultImports": true,
- "outDir": "./transpiled/",
- "jsx": "react",
- "baseUrl": "./",
- "allowJs": true,
- "strictNullChecks": false,
- "noImplicitThis": false,
- "declaration": false,
- "paths": {
- "*": ["node_modules/@types/*", "*"]
- }
- },
- "include": ["./ts/**/*", "../../node_modules/web3-typescript-typings/index.d.ts"]
+ "extends": "../../tsconfig",
+ "compilerOptions": {
+ "allowSyntheticDefaultImports": true,
+ "outDir": "./transpiled/",
+ "jsx": "react",
+ "baseUrl": "./",
+ "allowJs": true,
+ "strictNullChecks": false,
+ "noImplicitThis": false,
+ "declaration": false,
+ "paths": {
+ "*": ["node_modules/@types/*", "*"]
+ }
+ },
+ "include": ["./ts/**/*", "../../node_modules/web3-typescript-typings/index.d.ts"]
}
diff --git a/packages/website/tslint.json b/packages/website/tslint.json
index fcbd0391e..d6a5f5031 100644
--- a/packages/website/tslint.json
+++ b/packages/website/tslint.json
@@ -1,9 +1,9 @@
{
- "extends": ["@0xproject/tslint-config"],
- "rules": {
- "no-implicit-dependencies": false,
- "no-object-literal-type-assertion": false,
- "completed-docs": false,
- "prefer-function-over-method": false
- }
+ "extends": ["@0xproject/tslint-config"],
+ "rules": {
+ "no-implicit-dependencies": false,
+ "no-object-literal-type-assertion": false,
+ "completed-docs": false,
+ "prefer-function-over-method": false
+ }
}