From 957853788d2faabe42b09d52c50dd43d17f5cfdf Mon Sep 17 00:00:00 2001 From: Kyu Date: Tue, 15 Nov 2022 14:02:33 -0800 Subject: [PATCH] chore: Delete `asset-swapper` [GOV-6] (#617) * Delete `asset-swapper` * Remove `package/asset-swapper` in CODEOWNERS --- CODEOWNERS | 2 - packages/asset-swapper/.gitignore | 2 - packages/asset-swapper/.npmignore | 11 - packages/asset-swapper/.solhint.json | 21 - packages/asset-swapper/CHANGELOG.json | 2448 ------------ packages/asset-swapper/CHANGELOG.md | 893 ----- packages/asset-swapper/README.md | 86 - packages/asset-swapper/compiler.json | 23 - .../contracts/src/ApproximateBuys.sol | 148 - .../contracts/src/BalanceChecker.sol | 123 - .../contracts/src/BalancerSampler.sol | 197 - .../contracts/src/BalancerV2BatchSampler.sol | 105 - .../contracts/src/BalancerV2Common.sol | 41 - .../contracts/src/BalancerV2Sampler.sol | 142 - .../contracts/src/BancorSampler.sol | 141 - .../contracts/src/BancorV3Sampler.sol | 120 - .../contracts/src/CompoundSampler.sol | 96 - .../contracts/src/CurveSampler.sol | 161 - .../contracts/src/DODOSampler.sol | 211 -- .../contracts/src/DODOV2Sampler.sol | 204 - .../contracts/src/ERC20BridgeSampler.sol | 105 - .../asset-swapper/contracts/src/FakeTaker.sol | 27 - .../contracts/src/GMXSampler.sol | 96 - .../contracts/src/KyberDmmSampler.sol | 179 - .../contracts/src/LidoSampler.sol | 119 - .../src/LiquidityProviderSampler.sol | 132 - .../contracts/src/MStableSampler.sol | 127 - .../contracts/src/MakerPSMSampler.sol | 267 -- .../contracts/src/MooniswapSampler.sol | 169 - .../contracts/src/NativeOrderSampler.sol | 239 -- .../contracts/src/PlatypusSampler.sol | 89 - .../contracts/src/SamplerUtils.sol | 58 - .../contracts/src/ShellSampler.sol | 126 - .../contracts/src/SynthetixSampler.sol | 173 - .../contracts/src/TwoHopSampler.sol | 124 - .../contracts/src/UniswapSampler.sol | 214 -- .../contracts/src/UniswapV2Sampler.sol | 102 - .../contracts/src/UniswapV3Sampler.sol | 363 -- .../contracts/src/UtilitySampler.sol | 95 - .../contracts/src/VelodromeSampler.sol | 134 - .../contracts/src/WooPPSampler.sol | 121 - .../contracts/src/interfaces/IBalancer.sol | 44 - .../src/interfaces/IBalancerV2Vault.sol | 54 - .../contracts/src/interfaces/IBancor.sol | 33 - .../contracts/src/interfaces/IBancorV3.sol | 43 - .../contracts/src/interfaces/ICurve.sol | 70 - .../contracts/src/interfaces/IGMX.sol | 23 - .../contracts/src/interfaces/IMStable.sol | 33 - .../contracts/src/interfaces/IMooniswap.sol | 38 - .../contracts/src/interfaces/IMultiBridge.sol | 59 - .../contracts/src/interfaces/IPlatypus.sol | 11 - .../contracts/src/interfaces/IShell.sol | 43 - .../src/interfaces/IUniswapExchangeQuotes.sol | 52 - .../src/interfaces/IUniswapV2Router01.sol | 34 - .../contracts/test/TestNativeOrderSampler.sol | 135 - packages/asset-swapper/coverage/.gitkeep | 0 packages/asset-swapper/docs/reference.mdx | 3368 ----------------- packages/asset-swapper/package-lock.json | 1839 --------- packages/asset-swapper/package.json | 95 - packages/asset-swapper/src/artifacts.ts | 15 - packages/asset-swapper/src/constants.ts | 128 - packages/asset-swapper/src/errors.ts | 22 - packages/asset-swapper/src/globals.d.ts | 8 - packages/asset-swapper/src/index.ts | 196 - .../src/noop_samplers/AaveV2Sampler.ts | 57 - .../src/noop_samplers/GeistSampler.ts | 57 - .../exchange_proxy_swap_quote_consumer.ts | 733 ---- .../src/quote_consumers/multiplex_encoders.ts | 36 - .../quote_consumers/quote_consumer_utils.ts | 170 - .../quote_consumers/swap_quote_consumer.ts | 65 - packages/asset-swapper/src/swap_quoter.ts | 620 --- packages/asset-swapper/src/types.ts | 467 --- .../src/utils/affiliate_fee_utils.ts | 29 - .../src/utils/alt_mm_implementation_utils.ts | 279 -- packages/asset-swapper/src/utils/assert.ts | 22 - .../asset-swapper/src/utils/irfq_client.ts | 59 - .../aave_reserves_cache.ts | 105 - .../market_operation_utils/bancor_service.ts | 34 - .../bridge_source_utils.ts | 574 --- .../comparison_price.ts | 92 - .../compound_ctoken_cache.ts | 77 - .../utils/market_operation_utils/constants.ts | 2773 -------------- .../src/utils/market_operation_utils/fills.ts | 133 - .../market_operation_utils/geist_utils.ts | 36 - .../identity_fill_adjustor.ts | 13 - .../src/utils/market_operation_utils/index.ts | 870 ----- .../liquidity_provider_utils.ts | 26 - .../market_operation_utils/multihop_utils.ts | 71 - .../utils/market_operation_utils/orders.ts | 628 --- .../src/utils/market_operation_utils/path.ts | 165 - .../market_operation_utils/path_optimizer.ts | 444 --- .../pools_cache/balancer_pools_cache.ts | 128 - .../pools_cache/balancer_sor_v2.ts | 149 - .../pools_cache/balancer_v2_pools_cache.ts | 182 - .../balancer_v2_swap_info_cache.ts | 189 - .../pools_cache/index.ts | 3 - .../pools_cache/no_op_pools_cache.ts | 21 - .../pools_cache/pair_swaps_cache.ts | 88 - .../pools_cache/pools_cache.ts | 85 - .../pools_cache/sgPoolDataService.ts | 114 - .../market_operation_utils/rate_utils.ts | 116 - .../utils/market_operation_utils/sampler.ts | 199 - .../sampler_contract_operation.ts | 62 - .../sampler_no_operation.ts | 36 - .../sampler_operations.ts | 2212 ----------- .../market_operation_utils/source_filters.ts | 95 - .../src/utils/market_operation_utils/types.ts | 678 ---- .../src/utils/protocol_fee_utils.ts | 93 - .../src/utils/quote_report_generator.ts | 434 --- .../src/utils/quote_requestor.ts | 800 ---- .../src/utils/quote_simulation.ts | 312 -- .../src/utils/rfq_client_mappers.ts | 16 - .../src/utils/rfq_maker_blacklist.ts | 43 - .../asset-swapper/src/utils/rfqt_mocker.ts | 47 - .../src/utils/token_adjacency_graph.ts | 84 - packages/asset-swapper/src/utils/utils.ts | 100 - packages/asset-swapper/src/wrappers.ts | 8 - packages/asset-swapper/test/artifacts.ts | 103 - .../test/comparison_price_test.ts | 158 - .../test/contracts/balance_checker_test.ts | 132 - .../test/contracts/bridge_adapter_test.ts | 298 -- .../contracts/bridge_sampler_mainnet_test.ts | 80 - .../contracts/erc20_bridge_sampler_test.ts | 983 ----- .../contracts/native_order_sampler_test.ts | 233 -- .../asset-swapper/test/dex_sampler_test.ts | 533 --- ...exchange_proxy_swap_quote_consumer_test.ts | 511 --- packages/asset-swapper/test/global_hooks.ts | 6 - .../test/market_operation_utils_test.ts | 1640 -------- .../asset-swapper/test/pools_cache_test.ts | 60 - .../test/protocol_fee_utils_test.ts | 45 - .../test/quote_report_generator_test.ts | 376 -- .../test/quote_requestor_test.ts | 1148 ------ .../test/quote_simulation_test.ts | 927 ----- .../test/rfq_maker_blacklist_test.ts | 40 - .../test/token_adjacency_graph_test.ts | 108 - .../asset-swapper/test/utils/chai_setup.ts | 13 - .../test/utils/mock_sampler_contract.ts | 233 -- .../asset-swapper/test/utils/swap_quote.ts | 63 - .../asset-swapper/test/utils/test_helpers.ts | 95 - packages/asset-swapper/test/utils/utils.ts | 30 - .../asset-swapper/test/utils/web3_wrapper.ts | 8 - packages/asset-swapper/test/wrappers.ts | 52 - packages/asset-swapper/tsconfig.json | 57 - packages/asset-swapper/tslint.json | 12 - packages/asset-swapper/typedoc-tsconfig.json | 7 - packages/asset-swapper/webpack.config.js | 66 - 146 files changed, 37523 deletions(-) delete mode 100644 packages/asset-swapper/.gitignore delete mode 100644 packages/asset-swapper/.npmignore delete mode 100644 packages/asset-swapper/.solhint.json delete mode 100644 packages/asset-swapper/CHANGELOG.json delete mode 100644 packages/asset-swapper/CHANGELOG.md delete mode 100644 packages/asset-swapper/README.md delete mode 100644 packages/asset-swapper/compiler.json delete mode 100644 packages/asset-swapper/contracts/src/ApproximateBuys.sol delete mode 100644 packages/asset-swapper/contracts/src/BalanceChecker.sol delete mode 100644 packages/asset-swapper/contracts/src/BalancerSampler.sol delete mode 100644 packages/asset-swapper/contracts/src/BalancerV2BatchSampler.sol delete mode 100644 packages/asset-swapper/contracts/src/BalancerV2Common.sol delete mode 100644 packages/asset-swapper/contracts/src/BalancerV2Sampler.sol delete mode 100644 packages/asset-swapper/contracts/src/BancorSampler.sol delete mode 100644 packages/asset-swapper/contracts/src/BancorV3Sampler.sol delete mode 100644 packages/asset-swapper/contracts/src/CompoundSampler.sol delete mode 100644 packages/asset-swapper/contracts/src/CurveSampler.sol delete mode 100644 packages/asset-swapper/contracts/src/DODOSampler.sol delete mode 100644 packages/asset-swapper/contracts/src/DODOV2Sampler.sol delete mode 100644 packages/asset-swapper/contracts/src/ERC20BridgeSampler.sol delete mode 100644 packages/asset-swapper/contracts/src/FakeTaker.sol delete mode 100644 packages/asset-swapper/contracts/src/GMXSampler.sol delete mode 100644 packages/asset-swapper/contracts/src/KyberDmmSampler.sol delete mode 100644 packages/asset-swapper/contracts/src/LidoSampler.sol delete mode 100644 packages/asset-swapper/contracts/src/LiquidityProviderSampler.sol delete mode 100644 packages/asset-swapper/contracts/src/MStableSampler.sol delete mode 100644 packages/asset-swapper/contracts/src/MakerPSMSampler.sol delete mode 100644 packages/asset-swapper/contracts/src/MooniswapSampler.sol delete mode 100644 packages/asset-swapper/contracts/src/NativeOrderSampler.sol delete mode 100644 packages/asset-swapper/contracts/src/PlatypusSampler.sol delete mode 100644 packages/asset-swapper/contracts/src/SamplerUtils.sol delete mode 100644 packages/asset-swapper/contracts/src/ShellSampler.sol delete mode 100644 packages/asset-swapper/contracts/src/SynthetixSampler.sol delete mode 100644 packages/asset-swapper/contracts/src/TwoHopSampler.sol delete mode 100644 packages/asset-swapper/contracts/src/UniswapSampler.sol delete mode 100644 packages/asset-swapper/contracts/src/UniswapV2Sampler.sol delete mode 100644 packages/asset-swapper/contracts/src/UniswapV3Sampler.sol delete mode 100644 packages/asset-swapper/contracts/src/UtilitySampler.sol delete mode 100644 packages/asset-swapper/contracts/src/VelodromeSampler.sol delete mode 100644 packages/asset-swapper/contracts/src/WooPPSampler.sol delete mode 100644 packages/asset-swapper/contracts/src/interfaces/IBalancer.sol delete mode 100644 packages/asset-swapper/contracts/src/interfaces/IBalancerV2Vault.sol delete mode 100644 packages/asset-swapper/contracts/src/interfaces/IBancor.sol delete mode 100644 packages/asset-swapper/contracts/src/interfaces/IBancorV3.sol delete mode 100644 packages/asset-swapper/contracts/src/interfaces/ICurve.sol delete mode 100644 packages/asset-swapper/contracts/src/interfaces/IGMX.sol delete mode 100644 packages/asset-swapper/contracts/src/interfaces/IMStable.sol delete mode 100644 packages/asset-swapper/contracts/src/interfaces/IMooniswap.sol delete mode 100644 packages/asset-swapper/contracts/src/interfaces/IMultiBridge.sol delete mode 100644 packages/asset-swapper/contracts/src/interfaces/IPlatypus.sol delete mode 100644 packages/asset-swapper/contracts/src/interfaces/IShell.sol delete mode 100644 packages/asset-swapper/contracts/src/interfaces/IUniswapExchangeQuotes.sol delete mode 100644 packages/asset-swapper/contracts/src/interfaces/IUniswapV2Router01.sol delete mode 100644 packages/asset-swapper/contracts/test/TestNativeOrderSampler.sol delete mode 100644 packages/asset-swapper/coverage/.gitkeep delete mode 100644 packages/asset-swapper/docs/reference.mdx delete mode 100644 packages/asset-swapper/package-lock.json delete mode 100644 packages/asset-swapper/package.json delete mode 100644 packages/asset-swapper/src/artifacts.ts delete mode 100644 packages/asset-swapper/src/constants.ts delete mode 100644 packages/asset-swapper/src/errors.ts delete mode 100644 packages/asset-swapper/src/globals.d.ts delete mode 100644 packages/asset-swapper/src/index.ts delete mode 100644 packages/asset-swapper/src/noop_samplers/AaveV2Sampler.ts delete mode 100644 packages/asset-swapper/src/noop_samplers/GeistSampler.ts delete mode 100644 packages/asset-swapper/src/quote_consumers/exchange_proxy_swap_quote_consumer.ts delete mode 100644 packages/asset-swapper/src/quote_consumers/multiplex_encoders.ts delete mode 100644 packages/asset-swapper/src/quote_consumers/quote_consumer_utils.ts delete mode 100644 packages/asset-swapper/src/quote_consumers/swap_quote_consumer.ts delete mode 100644 packages/asset-swapper/src/swap_quoter.ts delete mode 100644 packages/asset-swapper/src/types.ts delete mode 100644 packages/asset-swapper/src/utils/affiliate_fee_utils.ts delete mode 100644 packages/asset-swapper/src/utils/alt_mm_implementation_utils.ts delete mode 100644 packages/asset-swapper/src/utils/assert.ts delete mode 100644 packages/asset-swapper/src/utils/irfq_client.ts delete mode 100644 packages/asset-swapper/src/utils/market_operation_utils/aave_reserves_cache.ts delete mode 100644 packages/asset-swapper/src/utils/market_operation_utils/bancor_service.ts delete mode 100644 packages/asset-swapper/src/utils/market_operation_utils/bridge_source_utils.ts delete mode 100644 packages/asset-swapper/src/utils/market_operation_utils/comparison_price.ts delete mode 100644 packages/asset-swapper/src/utils/market_operation_utils/compound_ctoken_cache.ts delete mode 100644 packages/asset-swapper/src/utils/market_operation_utils/constants.ts delete mode 100644 packages/asset-swapper/src/utils/market_operation_utils/fills.ts delete mode 100644 packages/asset-swapper/src/utils/market_operation_utils/geist_utils.ts delete mode 100644 packages/asset-swapper/src/utils/market_operation_utils/identity_fill_adjustor.ts delete mode 100644 packages/asset-swapper/src/utils/market_operation_utils/index.ts delete mode 100644 packages/asset-swapper/src/utils/market_operation_utils/liquidity_provider_utils.ts delete mode 100644 packages/asset-swapper/src/utils/market_operation_utils/multihop_utils.ts delete mode 100644 packages/asset-swapper/src/utils/market_operation_utils/orders.ts delete mode 100644 packages/asset-swapper/src/utils/market_operation_utils/path.ts delete mode 100644 packages/asset-swapper/src/utils/market_operation_utils/path_optimizer.ts delete mode 100644 packages/asset-swapper/src/utils/market_operation_utils/pools_cache/balancer_pools_cache.ts delete mode 100644 packages/asset-swapper/src/utils/market_operation_utils/pools_cache/balancer_sor_v2.ts delete mode 100644 packages/asset-swapper/src/utils/market_operation_utils/pools_cache/balancer_v2_pools_cache.ts delete mode 100644 packages/asset-swapper/src/utils/market_operation_utils/pools_cache/balancer_v2_swap_info_cache.ts delete mode 100644 packages/asset-swapper/src/utils/market_operation_utils/pools_cache/index.ts delete mode 100644 packages/asset-swapper/src/utils/market_operation_utils/pools_cache/no_op_pools_cache.ts delete mode 100644 packages/asset-swapper/src/utils/market_operation_utils/pools_cache/pair_swaps_cache.ts delete mode 100644 packages/asset-swapper/src/utils/market_operation_utils/pools_cache/pools_cache.ts delete mode 100644 packages/asset-swapper/src/utils/market_operation_utils/pools_cache/sgPoolDataService.ts delete mode 100644 packages/asset-swapper/src/utils/market_operation_utils/rate_utils.ts delete mode 100644 packages/asset-swapper/src/utils/market_operation_utils/sampler.ts delete mode 100644 packages/asset-swapper/src/utils/market_operation_utils/sampler_contract_operation.ts delete mode 100644 packages/asset-swapper/src/utils/market_operation_utils/sampler_no_operation.ts delete mode 100644 packages/asset-swapper/src/utils/market_operation_utils/sampler_operations.ts delete mode 100644 packages/asset-swapper/src/utils/market_operation_utils/source_filters.ts delete mode 100644 packages/asset-swapper/src/utils/market_operation_utils/types.ts delete mode 100644 packages/asset-swapper/src/utils/protocol_fee_utils.ts delete mode 100644 packages/asset-swapper/src/utils/quote_report_generator.ts delete mode 100644 packages/asset-swapper/src/utils/quote_requestor.ts delete mode 100644 packages/asset-swapper/src/utils/quote_simulation.ts delete mode 100644 packages/asset-swapper/src/utils/rfq_client_mappers.ts delete mode 100644 packages/asset-swapper/src/utils/rfq_maker_blacklist.ts delete mode 100644 packages/asset-swapper/src/utils/rfqt_mocker.ts delete mode 100644 packages/asset-swapper/src/utils/token_adjacency_graph.ts delete mode 100644 packages/asset-swapper/src/utils/utils.ts delete mode 100644 packages/asset-swapper/src/wrappers.ts delete mode 100644 packages/asset-swapper/test/artifacts.ts delete mode 100644 packages/asset-swapper/test/comparison_price_test.ts delete mode 100644 packages/asset-swapper/test/contracts/balance_checker_test.ts delete mode 100644 packages/asset-swapper/test/contracts/bridge_adapter_test.ts delete mode 100644 packages/asset-swapper/test/contracts/bridge_sampler_mainnet_test.ts delete mode 100644 packages/asset-swapper/test/contracts/erc20_bridge_sampler_test.ts delete mode 100644 packages/asset-swapper/test/contracts/native_order_sampler_test.ts delete mode 100644 packages/asset-swapper/test/dex_sampler_test.ts delete mode 100644 packages/asset-swapper/test/exchange_proxy_swap_quote_consumer_test.ts delete mode 100644 packages/asset-swapper/test/global_hooks.ts delete mode 100644 packages/asset-swapper/test/market_operation_utils_test.ts delete mode 100644 packages/asset-swapper/test/pools_cache_test.ts delete mode 100644 packages/asset-swapper/test/protocol_fee_utils_test.ts delete mode 100644 packages/asset-swapper/test/quote_report_generator_test.ts delete mode 100644 packages/asset-swapper/test/quote_requestor_test.ts delete mode 100644 packages/asset-swapper/test/quote_simulation_test.ts delete mode 100644 packages/asset-swapper/test/rfq_maker_blacklist_test.ts delete mode 100644 packages/asset-swapper/test/token_adjacency_graph_test.ts delete mode 100644 packages/asset-swapper/test/utils/chai_setup.ts delete mode 100644 packages/asset-swapper/test/utils/mock_sampler_contract.ts delete mode 100644 packages/asset-swapper/test/utils/swap_quote.ts delete mode 100644 packages/asset-swapper/test/utils/test_helpers.ts delete mode 100644 packages/asset-swapper/test/utils/utils.ts delete mode 100644 packages/asset-swapper/test/utils/web3_wrapper.ts delete mode 100644 packages/asset-swapper/test/wrappers.ts delete mode 100644 packages/asset-swapper/tsconfig.json delete mode 100644 packages/asset-swapper/tslint.json delete mode 100644 packages/asset-swapper/typedoc-tsconfig.json delete mode 100644 packages/asset-swapper/webpack.config.js diff --git a/CODEOWNERS b/CODEOWNERS index de256ab3fc..c97af726ef 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -6,8 +6,6 @@ # https://git-scm.com/docs/gitignore#_pattern_format -packages/asset-swapper/ @dekz @dextracker @kyu-c - # Dev tools & setup .circleci/ @dekz diff --git a/packages/asset-swapper/.gitignore b/packages/asset-swapper/.gitignore deleted file mode 100644 index 2bd6a3f7e3..0000000000 --- a/packages/asset-swapper/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -**/generated-artifacts -**/generated-wrappers diff --git a/packages/asset-swapper/.npmignore b/packages/asset-swapper/.npmignore deleted file mode 100644 index 62439cf8d0..0000000000 --- a/packages/asset-swapper/.npmignore +++ /dev/null @@ -1,11 +0,0 @@ -# Blacklist all files -.* -* -# Whitelist lib -!lib/**/* -# Whitelist Solidity contracts -!contracts/src/**/* -# Blacklist tests and publish scripts -/lib/test/* -/lib/monorepo_scripts/ -# Package specific ignore diff --git a/packages/asset-swapper/.solhint.json b/packages/asset-swapper/.solhint.json deleted file mode 100644 index 3090d981a1..0000000000 --- a/packages/asset-swapper/.solhint.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "extends": "default", - "rules": { - "avoid-low-level-calls": false, - "avoid-tx-origin": "warn", - "bracket-align": false, - "code-complexity": false, - "compiler-fixed": false, - "const-name-snakecase": "error", - "expression-indent": "error", - "function-max-lines": false, - "func-order": "error", - "indent": ["error", 4], - "max-line-length": ["warn", 160], - "no-inline-assembly": false, - "quotes": ["error", "double"], - "separate-by-one-line-in-contract": "error", - "space-after-comma": "error", - "statement-indent": "error" - } -} diff --git a/packages/asset-swapper/CHANGELOG.json b/packages/asset-swapper/CHANGELOG.json deleted file mode 100644 index 97d50596e8..0000000000 --- a/packages/asset-swapper/CHANGELOG.json +++ /dev/null @@ -1,2448 +0,0 @@ -[ - { - "version": "16.66.4", - "changes": [ - { - "note": "Offboard Cream", - "pr": 546 - }, - { - "note": "Change WooFi gas estimates", - "pr": 551 - } - ], - "timestamp": 1661145612 - }, - { - "timestamp": 1660093941, - "version": "16.66.3", - "changes": [ - { - "note": "Dependencies updated" - } - ] - }, - { - "version": "16.66.2", - "changes": [ - { - "note": "Upgrade dependency", - "pr": 543 - } - ], - "timestamp": 1660073235 - }, - { - "version": "16.66.1", - "changes": [ - { - "note": "Upgrade dependency", - "pr": 538 - } - ], - "timestamp": 1659926840 - }, - { - "version": "16.66.0", - "changes": [ - { - "note": "Add WOOFi support", - "pr": 513 - } - ], - "timestamp": 1659750766 - }, - { - "version": "16.65.0", - "changes": [ - { - "note": "Use 0x gas api instead of eth gas station api", - "pr": 532 - } - ], - "timestamp": 1659391840 - }, - { - "version": "16.64.0", - "changes": [ - { - "note": "Refactor `TokenAdjacency` and `TokenAdjacencyBuilder`", - "pr": 517 - }, - { - "note": "Add Synthetix support`", - "pr": 518 - }, - { - "note": "Replace Beethoven X subgraph URL", - "pr": 519 - }, - { - "note": "Remove Mooniswap on Ethereum mainnet", - "pr": 529 - } - ], - "timestamp": 1658950329 - }, - { - "version": "16.63.1", - "changes": [ - { - "note": "Better error handling for balancer cache", - "pr": 515 - } - ], - "timestamp": 1657661207 - }, - { - "version": "16.63.0", - "changes": [ - { - "note": "Remove JS router", - "pr": 480 - }, - { - "note": "Removed Median price in favour of best gas adjusted price", - "pr": 480 - } - ], - "timestamp": 1656491792 - }, - { - "version": "16.62.2", - "changes": [ - { - "note": "Offboard Smoothy and ComethSwap", - "pr": 509 - } - ] - }, - { - "version": "16.62.1", - "changes": [ - { - "note": "Remove nUSD from intermediate liquidity to save on sampler gas", - "pr": 505 - } - ], - "timestamp": 1655253622 - }, - { - "version": "16.62.0", - "changes": [ - { - "note": "Add MDEX on BSC", - "pr": 496 - }, - { - "note": "Add KnightSwap on BSC", - "pr": 498 - }, - { - "note": "Add Velodrome support on Optimism", - "pr": 494 - }, - { - "note": "Do not send empty entries on Quote Report", - "pr": 501 - }, - { - "note": "KnightSwap/Mdex cosmetic change", - "pr": 502 - }, - { - "note": "Offboard JetSwap, CafeSwap, JulSwap, and PolyDex", - "pr": 503 - } - ], - "timestamp": 1655244958 - }, - { - "version": "16.61.0", - "changes": [ - { - "note": "Add stETH wrap/unwrap support", - "pr": 476 - }, - { - "note": "Offboard/clean up Oasis, CoFix, and legacy Kyber", - "pr": 482 - }, - { - "note": "Add MeshSwap on Polygon", - "pr": 491 - } - ], - "timestamp": 1654284040 - }, - { - "version": "16.60.1", - "changes": [ - { - "note": "Alias Balancer sor to the old version", - "pr": 481 - } - ], - "timestamp": 1652931596 - }, - { - "version": "16.60.0", - "changes": [ - { - "note": "Add BiSwap on BSC", - "pr": 467 - }, - { - "note": "Add GMX and Platypus on Avalanche and Enable KyberDMM on bsc", - "pr": 478 - }, - { - "note": "Add Yoshi Exchange support in Fantom", - "pr": 473 - }, - { - "note": "Fix KyberDMM gas underestimation", - "pr": 479 - } - ], - "timestamp": 1652919697 - }, - { - "version": "16.59.0", - "changes": [ - { - "note": "Remove SnowSwap on mainnet", - "pr": 468 - }, - { - "note": "Offboard Swerve Finance and LinkSwap", - "pr": 469 - }, - { - "note": "Offboard Eth2Dai", - "pr": 470 - }, - { - "note": "Add an optional IRfqClient for SwapQuoter#getSwapQuoteAsync", - "pr": 467 - } - ], - "timestamp": 1652400434 - }, - { - "version": "16.58.0", - "changes": [ - { - "note": "Update Saddle pools on Mainnet", - "pr": 450 - } - ] - }, - { - "version": "16.57.3", - "changes": [ - { - "note": "Fix a runtime error related to BalancerV2SwapInfoCache", - "pr": 472 - } - ], - "timestamp": 1652146864 - }, - { - "version": "16.57.2", - "changes": [ - { - "note": "Fix missing AMM quotes on indicative Quote Reports", - "pr": 466 - } - ], - "timestamp": 1651526551 - }, - { - "version": "16.57.1", - "changes": [ - { - "note": "Added QUICK/ANY pair on Polygon", - "pr": 464 - }, - { - "note": "Added cvxFXS/FXS curve pool on mainnet", - "pr": 465 - } - ] - }, - { - "version": "16.57.0", - "changes": [ - { - "note": "Add BalancerV2 batch swap support", - "pr": 462 - } - ], - "timestamp": 1650611093 - }, - { - "version": "16.56.0", - "changes": [ - { - "note": "Add estimatedGas to ExtendedQuoteReport", - "pr": 463 - } - ], - "timestamp": 1650575781 - }, - { - "version": "16.55.0", - "changes": [ - { - "note": "Fix fillRfqOrder VIP being used for swaps that need transformERC20", - "pr": 461 - } - ], - "timestamp": 1649347667 - }, - { - "version": "16.54.0", - "changes": [ - { - "note": "Add true VIP support for eligible RFQt swaps", - "pr": 458 - } - ], - "timestamp": 1649215576 - }, - { - "version": "16.53.0", - "changes": [ - { - "note": "Adds support for STG/USDC pool on Curve Mainnet", - "pr": 451 - }, - { - "note": "Use neon-router in asset-swapper tests", - "pr": 453 - }, - { - "note": "Add sampler blocknumber to quote report data", - "pr": 448 - } - ], - "timestamp": 1648739346 - }, - { - "version": "16.52.0", - "changes": [ - { - "note": "Adds support for mobius money on celo", - "pr": 423 - } - ] - }, - { - "version": "16.51.0", - "changes": [ - { - "note": "Added `Curve` `YFI-ETH` pool", - "pr": 444 - } - ], - "timestamp": 1646888282 - }, - { - "version": "16.50.3", - "changes": [ - { - "note": "Routing glue optimization", - "pr": 439 - }, - { - "note": "Move VIP source routing into neon-router & disable fallback orders for native/plp", - "pr": 440 - } - ], - "timestamp": 1646837959 - }, - { - "version": "16.50.2", - "changes": [ - { - "note": "Update `Uniswap_V3` address on `Ropsten`", - "pr": 441 - } - ], - "timestamp": 1646617024 - }, - { - "version": "16.50.1", - "changes": [ - { - "note": "Add BTRFLY/WETH Curve pool on mainnet", - "pr": 437 - }, - { - "note": "Lower Uniswap V3 Sampler gas allowance", - "pr": 438 - } - ], - "timestamp": 1646312638 - }, - { - "version": "16.50.0", - "changes": [ - { - "note": "Adding support for Geist on `Fantom`", - "pr": 398 - }, - { - "note": "Improve Uniswap V3 gas schedule", - "pr": 424 - } - ], - "timestamp": 1646225739 - }, - { - "version": "16.49.9", - "changes": [ - { - "note": "Fix native order scaling & filter out 1 wei quotes", - "pr": "430" - } - ], - "timestamp": 1645696356 - }, - { - "timestamp": 1645569128, - "version": "16.49.8", - "changes": [ - { - "note": "Dependencies updated" - } - ] - }, - { - "version": "16.49.7", - "changes": [ - { - "note": "Fix native order handling for very small quotes and bump `neon-router` dependency", - "pr": 425 - } - ], - "timestamp": 1645534245 - }, - { - "version": "16.49.6", - "changes": [ - { - "note": "Fixed btrfly routing to include the ohmV2/dai, ohmV2/btfly, and ohmV2/weth pools", - "pr": 427 - } - ], - "timestamp": 1645113751 - }, - { - "version": "16.49.5", - "changes": [ - { - "note": "Fix scaling 1 base unit to 0, round output to base units", - "pr": 422 - } - ], - "timestamp": 1644844353 - }, - { - "version": "16.49.4", - "changes": [ - { - "note": "Reverts 'Improve Uniswap V3 gas schedule' due to issue with buys", - "pr": 419 - } - ], - "timestamp": 1644507275 - }, - { - "version": "16.49.3", - "changes": [ - { - "note": "Fix `slippage` inconsistency when recalculated in exchange proxy quote consumer", - "pr": 412 - }, - { - "note": "Fix incorrect output scaling when input is less than desired amount, update fast-abi", - "pr": 401 - }, - { - "note": "Improve Uniswap V3 gas schedule", - "pr": 397 - }, - { - "note": "Fix add Native as VIP and use Path to compare all sources vs vip only", - "pr": 413 - } - ], - "timestamp": 1644495123 - }, - { - "version": "16.49.2", - "changes": [ - { - "note": "Fix ABI encoding error with two hop buys due to applying slippage to uint(-1) values", - "pr": 410 - } - ], - "timestamp": 1643653482 - }, - { - "version": "16.49.1", - "changes": [ - { - "note": "Fix WorstCaseQuoteInfo encoding bug", - "pr": 402 - } - ], - "timestamp": 1643613597 - }, - { - "version": "16.49.0", - "changes": [ - { - "note": "Add more curve pools", - "pr": 409 - } - ], - "timestamp": 1643407900 - }, - { - "version": "16.48.0", - "changes": [ - { - "note": "Use `MIM` as an intermediate asset on `Fantom`", - "pr": 405 - } - ], - "timestamp": 1643148019 - }, - { - "version": "16.47.0", - "changes": [ - { - "note": "Adding support for Synapse on all networks", - "pr": 400 - } - ], - "timestamp": 1643136662 - }, - { - "version": "16.46.0", - "changes": [ - { - "note": "Enable `Curve` ETH/CVX pool", - "pr": 394 - } - ], - "timestamp": 1641863395 - }, - { - "version": "16.45.2", - "changes": [ - { - "note": "Handle 0 output samples and negative adjusted rate native orders in routing", - "pr": 387 - } - ], - "timestamp": 1641827361 - }, - { - "version": "16.45.1", - "changes": [ - { - "note": "Update `Celo` intermediate tokens", - "pr": 390 - } - ], - "timestamp": 1641359319 - }, - { - "version": "16.45.0", - "changes": [ - { - "note": "Capture router timings", - "pr": 388 - } - ], - "timestamp": 1641308410 - }, - { - "version": "16.44.0", - "changes": [ - { - "note": "Update neon-router and use router estimated output amount", - "pr": 354 - } - ], - "timestamp": 1640778328 - }, - { - "version": "16.43.0", - "changes": [ - { - "note": "`UniswapV3` support for `Optimism`", - "pr": 385 - } - ], - "timestamp": 1640364306 - }, - { - "version": "16.42.0", - "changes": [ - { - "note": "`UniswapV3` support for `Polygon`", - "pr": 382 - }, - { - "note": "Update `Beethoven` Graphql url", - "pr": 383 - } - ], - "timestamp": 1640124159 - }, - { - "version": "16.41.0", - "changes": [ - { - "note": "Update mcusd contract address, and made celo native asset", - "pr": 376 - } - ], - "timestamp": 1638827302 - }, - { - "version": "16.40.0", - "changes": [ - { - "note": "Add `AaveV2` and `Compound` deposit/withdrawal liquidity source", - "pr": 321 - } - ], - "timestamp": 1638390144 - }, - { - "version": "16.39.0", - "changes": [ - { - "note": "Curve ETH/CRV pool", - "pr": 378 - } - ] - }, - { - "version": "16.38.0", - "changes": [ - { - "note": "Capture sampler metrics", - "pr": 374 - } - ], - "timestamp": 1638228231 - }, - { - "version": "16.37.0", - "changes": [ - { - "note": "Changed Sushiswap router address", - "pr": 373 - } - ], - "timestamp": 1637349338 - }, - { - "version": "16.36.0", - "changes": [ - { - "note": "Specify liquid routes for FEI/TRIBE FXS/FRAX and OHM/FRAX", - "pr": 371 - } - ], - "timestamp": 1637290768 - }, - { - "version": "16.35.0", - "changes": [ - { - "note": "Add Beethoven X, MorpheusSwap and JetSwap to Fantom", - "pr": 370 - } - ], - "timestamp": 1637206290 - }, - { - "version": "16.34.0", - "changes": [ - { - "note": "Add support Celo", - "pr": 367 - } - ], - "timestamp": 1637102971 - }, - { - "version": "16.33.0", - "changes": [ - { - "note": "Add support for Uniswap V3 1 bps pools", - "pr": 366 - } - ], - "timestamp": 1637065617 - }, - { - "version": "16.32.0", - "changes": [ - { - "note": "Extended Quote Report", - "pr": 361 - } - ], - "timestamp": 1636480845 - }, - { - "timestamp": 1635903615, - "version": "16.31.0", - "changes": [ - { - "note": "Added `Curve`, `Curve_V2` and `KyberDmm` to Avalanche", - "pr": 363 - } - ] - }, - { - "timestamp": 1635903615, - "version": "16.30.1", - "changes": [ - { - "note": "Dependencies updated" - } - ] - }, - { - "version": "16.30.0", - "changes": [ - { - "note": "Fantom deployment", - "pr": 347 - } - ], - "timestamp": 1634668033 - }, - { - "version": "16.29.3", - "changes": [ - { - "note": "Update neon-router version and address breaking changes", - "pr": 344 - } - ], - "timestamp": 1634553393 - }, - { - "version": "16.29.2", - "changes": [ - { - "note": "Check MAX_IN_RATIO in sampleBuysFromBalancer", - "pr": 338 - }, - { - "note": "Go back to using transformERC20 (instead of transformERC20Staging)", - "pr": 343 - } - ], - "timestamp": 1634147078 - }, - { - "version": "16.29.1", - "changes": [ - { - "note": "Remove `Clipper` as a custom liquidity source", - "pr": 335 - } - ], - "timestamp": 1633374058 - }, - { - "version": "16.29.0", - "changes": [ - { - "note": "Initial integration of neon-router (behind feature flag)", - "pr": 295 - } - ], - "timestamp": 1633350101 - }, - { - "version": "16.28.0", - "changes": [ - { - "note": "Update ExchangeProxySwapQuoteConsumer for Multiplex V2 and friends", - "pr": 282 - } - ], - "timestamp": 1632957537 - }, - { - "version": "16.27.5", - "changes": [ - { - "note": "Remove protocol fees by setting `PROTOCOL_FEE_MULTIPLIER` to 0", - "pr": 333 - } - ] - }, - { - "timestamp": 1631710679, - "version": "16.27.4", - "changes": [ - { - "note": "Dependencies updated" - } - ] - }, - { - "timestamp": 1631646242, - "version": "16.27.3", - "changes": [ - { - "note": "Dependencies updated" - } - ] - }, - { - "timestamp": 1631639620, - "version": "16.27.2", - "changes": [ - { - "note": "Dependencies updated" - } - ] - }, - { - "version": "16.27.1", - "changes": [ - { - "note": "Fix ApproximateBuys sampler to terminate if the buy amount is not met", - "pr": 319 - } - ], - "timestamp": 1631120757 - }, - { - "version": "16.27.0", - "changes": [ - { - "note": "Avalanche deployment", - "pr": 312 - } - ], - "timestamp": 1630459879 - }, - { - "version": "16.26.2", - "changes": [ - { - "note": "chore: Curve new pools (CVX-CRX, MIM, atricrypto3)" - } - ], - "timestamp": 1630393585 - }, - { - "timestamp": 1629414734, - "version": "16.26.1", - "changes": [ - { - "note": "Dependencies updated" - } - ] - }, - { - "version": "16.26.0", - "changes": [ - { - "note": "feat: Enable partial Native fills to be consumed, previously for v3 they were dropped", - "pr": 309 - }, - { - "note": "feat: Modify Intermediate tokens to be a union", - "pr": 309 - }, - { - "note": "feat: Retire Eth2Dai/Oasis", - "pr": 309 - } - ], - "timestamp": 1629353596 - }, - { - "version": "16.25.0", - "changes": [ - { - "note": "Fix: fallback fills which have not been used, unique id by source-index" - } - ], - "timestamp": 1629079369 - }, - { - "timestamp": 1628665757, - "version": "16.24.1", - "changes": [ - { - "note": "Dependencies updated" - } - ] - }, - { - "version": "16.24.0", - "changes": [ - { - "note": "Add `Clipper` as a custom liquidity source", - "pr": 299 - }, - { - "note": "Added `Curve` `Tricrypto2` and `ESD` v2", - "pr": 302 - } - ], - "timestamp": 1628225642 - }, - { - "version": "16.23.1", - "changes": [ - { - "note": "Fix fill amount rounding error when covnerting fills to orders.", - "pr": 296 - } - ], - "timestamp": 1627572227 - }, - { - "version": "16.23.0", - "changes": [ - { - "note": "ACryptoS", - "pr": 284 - } - ], - "timestamp": 1626473497 - }, - { - "version": "16.22.0", - "changes": [ - { - "note": "IronSwap", - "pr": 281 - } - ], - "timestamp": 1626214787 - }, - { - "version": "16.21.0", - "changes": [ - { - "note": "JetSwap", - "pr": 280 - } - ], - "timestamp": 1625904026 - }, - { - "version": "16.20.0", - "changes": [ - { - "note": "ShibaSwap", - "pr": 276 - } - ], - "timestamp": 1625607277 - }, - { - "version": "16.19.1", - "changes": [ - { - "note": "Fix LiquidityProvider fallback", - "pr": 272 - } - ], - "timestamp": 1625544188 - }, - { - "version": "16.19.0", - "changes": [ - { - "note": "Add LiquidityProvider to Polygon sources", - "pr": 270 - } - ], - "timestamp": 1625190486 - }, - { - "version": "6.18.3", - "changes": [ - { - "note": "Polygon Balance V2" - } - ], - "timestamp": 1624987208 - }, - { - "timestamp": 1624562704, - "version": "6.18.2", - "changes": [ - { - "note": "Dependencies updated" - } - ] - }, - { - "version": "6.18.1", - "changes": [ - { - "note": "FirebirdOneSwap, ApeSwap. New hop tokens: DFYN, BANANA, WEXPOLY", - "pr": 265 - } - ], - "timestamp": 1624405040 - }, - { - "version": "6.18.0", - "changes": [ - { - "note": "Add Lido stETH deposit integration", - "pr": 260 - } - ], - "timestamp": 1624356181 - }, - { - "version": "6.17.3", - "changes": [ - { - "note": "QUICK, TITAN, IRON as intermediate tokens, integrating WaultSwap and Polydex for Polygon, Curve renBTC pool" - } - ], - "timestamp": 1623807529 - }, - { - "timestamp": 1623382456, - "version": "6.17.2", - "changes": [ - { - "note": "Dependencies updated" - } - ] - }, - { - "timestamp": 1622609597, - "version": "6.17.1", - "changes": [ - { - "note": "Dependencies updated" - } - ] - }, - { - "version": "6.17.0", - "changes": [ - { - "note": "Re-enable liquidity provider and update KNC address", - "pr": 253 - } - ], - "timestamp": 1622154125 - }, - { - "version": "6.16.0", - "changes": [ - { - "note": "Add support for the Polygon chain", - "pr": 240 - } - ], - "timestamp": 1621944788 - }, - { - "version": "6.15.0", - "changes": [ - { - "note": "Fix KyberDmm", - "pr": 236 - }, - { - "note": "Re-enable KyberDmm", - "pr": 247 - }, - { - "note": "Add Huobi Token to liquidity provider tokens", - "pr": 246 - }, - { - "note": "Temporarily disable specific LiquidityProvider" - } - ], - "timestamp": 1621600614 - }, - { - "version": "6.14.0", - "changes": [ - { - "note": "Add support for additional sources and intermediate tokens on Ropsten", - "pr": 231 - }, - { - "note": "Add UniswapV3 VIP support", - "pr": 237 - } - ], - "timestamp": 1620810800 - }, - { - "version": "6.13.0", - "changes": [ - { - "note": "Add LiquidityProvider to BSC sources", - "pr": 234 - } - ], - "timestamp": 1620703098 - }, - { - "version": "6.12.0", - "changes": [ - { - "note": "`TwoHopSampler` to use `call` over `staticcall` in order to support sources like `Uniswap_V3` and `Balancer_V2`", - "pr": 233 - } - ], - "timestamp": 1620610602 - }, - { - "version": "6.11.0", - "changes": [ - { - "note": "Add price comparisons data separate from the quote report", - "pr": 219 - }, - { - "note": "Add caching for top Balancer V2 pools on startup and during regular intervals", - "pr": 228 - }, - { - "note": "Tweak compiler settings for smaller sampler bytecode", - "pr": 229 - }, - { - "note": "Fix Multiplex multihop encoding for ETH buys/sells", - "pr": 230 - }, - { - "note": "Fix Sampler address override for Ganache", - "pr": 232 - } - ], - "timestamp": 1620362129 - }, - { - "version": "6.10.0", - "changes": [ - { - "note": "Reactivate PancakeSwapV2 and BakerySwap VIP on BSC", - "pr": 222 - }, - { - "note": "Add LUSD Curve pool", - "pr": 218 - }, - { - "note": "Fix exchangeProxyGasOverhead for fallback path", - "pr": 215 - }, - { - "note": "Enable ETH based Curve pools", - "pr": 220 - }, - { - "note": "Reactivate PancakeSwapV2 and BakerySwap VIP on BSC", - "pr": 222 - }, - { - "note": "Disable WETH based SnowSwap pools", - "pr": 220 - }, - { - "note": "PLP now includes a fallback due to observed collisions", - "pr": 223 - }, - { - "note": "Add Balancer V2 integration", - "pr": 206 - }, - { - "note": "Re-work the PoolCache for Balancer et al", - "pr": 226 - } - ], - "timestamp": 1620214333 - }, - { - "version": "6.9.1", - "changes": [ - { - "note": "Temporarily remove PancakeV2 and BakerySwap from VIP" - } - ], - "timestamp": 1619830995 - }, - { - "version": "6.9.0", - "changes": [ - { - "note": "Remove conflicting Kyber reserve", - "pr": 216 - } - ], - "timestamp": 1619825976 - }, - { - "version": "6.8.0", - "changes": [ - { - "note": "Prune paths which cannot improve the best path", - "pr": 183 - }, - { - "note": "Use FastABI for Sampler ABI encoding and decoding", - "pr": 183 - } - ], - "timestamp": 1619596077 - }, - { - "version": "6.7.0", - "changes": [ - { - "note": "Support PancakeSwap V2", - "pr": 211 - } - ], - "timestamp": 1619481586 - }, - { - "version": "6.6.1", - "changes": [ - { - "note": "Fixing Positive Slippage logic to not force the EP route", - "pr": 209 - } - ] - }, - { - "version": "6.6.0", - "changes": [ - { - "note": "Support `Ropsten` network", - "pr": 203 - }, - { - "note": "BSC Uniswap clones (ApeSwap, CafeSwap, CheeseSwap, JulSwap), Saddle BTC pool, Curve gas schedule", - "pr": 208 - } - ], - "timestamp": 1618592834 - }, - { - "version": "6.5.3", - "changes": [ - { - "note": "Apply slippage to bridge orders in consumer", - "pr": 198 - } - ], - "timestamp": 1618433771 - }, - { - "timestamp": 1618314654, - "version": "6.5.2", - "changes": [ - { - "note": "Dependencies updated" - } - ] - }, - { - "timestamp": 1618259868, - "version": "6.5.1", - "changes": [ - { - "note": "Dependencies updated" - } - ] - }, - { - "version": "6.5.0", - "changes": [ - { - "note": "Add Kyber DMM to Ethereum mainnet", - "pr": 194 - }, - { - "note": "Add default LiquidityProvider registry and allow LiquidityProvider gasCost to be a function of tokens", - "pr": 196 - } - ], - "timestamp": 1617913615 - }, - { - "version": "6.4.0", - "changes": [ - { - "note": "Added Component, Smoothy, Saddle, Curve open pools, tweeks gas schedule, adding SushiSwap as a fee quote source", - "pr": 182 - }, - { - "note": "Use SOURCE_FLAGS.rfqOrder in comparisonPrice", - "pr": 177 - }, - { - "note": "Add a cancel token to ensure timeouts are respected", - "pr": 176 - }, - { - "note": "Rename {Rfqt=>Rfq} for many types in Asset Swapper", - "pr": 179 - }, - { - "note": "improve logging for alt RFQ requests", - "pr": 158 - }, - { - "note": "Use new bridge source ID encoding.", - "pr": 162 - }, - { - "note": "Refactor to provide chain id specific addresses", - "pr": 163 - }, - { - "note": "Added PancakeSwap and BakerySwap on Chain 56", - "pr": 163 - }, - { - "note": "Added Nerve and Dodo (v1) to BSC", - "pr": 181 - } - ], - "timestamp": 1617311315 - }, - { - "version": "6.3.0", - "changes": [ - { - "note": "Add MooniswapLiquidityProvider \"direct\" route to EP consumer.", - "pr": 143 - }, - { - "note": "Enable the ability to send RFQT requests thru a proxy", - "pr": 159 - }, - { - "note": "Add support for MultiplexFeature", - "pr": 168 - } - ], - "timestamp": 1616005394 - }, - { - "version": "6.2.0", - "changes": [ - { - "note": "drop curve Y and BUSD pools", - "pr": 161 - } - ], - "timestamp": 1614645844 - }, - { - "version": "6.1.0", - "changes": [ - { - "note": "Filter MultiHop where second source is not present", - "pr": 138 - }, - { - "note": "Add CurveLiquidityProvider \"direct\" route to EP consumer.", - "pr": 127 - }, - { - "note": "Fix compiler error on `ILiquidityProvider` call", - "pr": 127 - }, - { - "note": "Add deployed `CurveLiquidityProvider` addresses", - "pr": 144 - }, - { - "note": "Support `Mirror Protocol` with hops to `UST`", - "pr": 142 - }, - { - "note": "Fix protocol fee in fee schedule for `RfqOrder`", - "pr": 146 - }, - { - "note": "Special case BNB in uni v1 sampler", - "pr": 147 - }, - { - "note": "Create `FakeTaker` contract to get result data and gas used", - "pr": 151 - }, - { - "note": "Added support for `Dodo` v2", - "pr": 152 - }, - { - "note": "Added support for `Linkswap`", - "pr": 153 - }, - { - "note": "Re-add WBTC in default intermediate hops", - "pr": 154 - }, - { - "note": "Add an alternative RFQ market making implementation", - "pr": 139 - }, - { - "note": "Added an opt-in `PositiveSlippageAffiliateFee`", - "pr": 101 - } - ], - "timestamp": 1614141718 - }, - { - "version": "6.0.0", - "changes": [ - { - "note": "Pull top 250 Balancer pairs on initialization", - "pr": 113 - }, - { - "note": "Support v4 `RFQ` and `Limit` orders", - "pr": 113 - }, - { - "note": "Refactor to consume latest `FillQuoteTransformer`", - "pr": 113 - }, - { - "note": "Enable `fillData` for all sources, no longer optional", - "pr": 113 - }, - { - "note": "Support `tx.origin` in RFQT quote requestor", - "pr": 113 - } - ], - "timestamp": 1612950500 - }, - { - "version": "5.8.2", - "changes": [ - { - "note": "Fix error when Multihop data is not present", - "pr": 80 - } - ], - "timestamp": 1611869778 - }, - { - "timestamp": 1611648096, - "version": "5.8.1", - "changes": [ - { - "note": "Dependencies updated" - } - ] - }, - { - "version": "5.8.0", - "changes": [ - { - "note": "Automatically Discover Kyber reserves for tokens using `getTradingReserves`", - "pr": 111 - }, - { - "note": "Return `CallResults` from the Sampler", - "pr": 111 - } - ], - "timestamp": 1610510890 - }, - { - "version": "5.7.0", - "changes": [ - { - "note": "Add SPDX license identifiers to solidity files", - "pr": 105 - } - ] - }, - { - "timestamp": 1609802516, - "version": "5.6.2", - "changes": [ - { - "note": "Dependencies updated" - } - ] - }, - { - "version": "5.6.1", - "changes": [ - { - "note": "Fix fillAmount `ExchangeProxySwapQuoteConsumer` encoding when quote is a BuyQuote" - } - ], - "timestamp": 1609387311 - }, - { - "version": "5.6.0", - "changes": [ - { - "note": "Added Mooniswap V2 factory address", - "pr": 100 - } - ], - "timestamp": 1609113560 - }, - { - "timestamp": 1608692071, - "version": "5.5.3", - "changes": [ - { - "note": "Dependencies updated" - } - ] - }, - { - "timestamp": 1608245516, - "version": "5.5.2", - "changes": [ - { - "note": "Dependencies updated" - } - ] - }, - { - "timestamp": 1608149382, - "version": "5.5.1", - "changes": [ - { - "note": "Dependencies updated" - } - ] - }, - { - "version": "5.5.0", - "changes": [ - { - "note": "Bancor now supported in all pairs", - "pr": 88 - } - ], - "timestamp": 1608105788 - }, - { - "timestamp": 1607485227, - "version": "5.4.2", - "changes": [ - { - "note": "Dependencies updated" - } - ] - }, - { - "timestamp": 1607381756, - "version": "5.4.1", - "changes": [ - { - "note": "Dependencies updated" - } - ] - }, - { - "version": "5.4.0", - "changes": [ - { - "note": "Add `takerAssetToEthRate` and `makerAssetToEthRate` to swap quote response", - "pr": 49 - } - ], - "timestamp": 1607373752 - }, - { - "timestamp": 1607036724, - "version": "5.3.1", - "changes": [ - { - "note": "Dependencies updated" - } - ] - }, - { - "version": "5.3.0", - "changes": [ - { - "note": "Added Crypto.com", - "pr": 43 - }, - { - "note": "Add `getQuoteInfoMinBuyAmount` to quote consumer utils", - "pr": 62 - }, - { - "note": "Add `unoptimizedQuoteInfo` and `unoptimizedOrders` to SwapQuoteBase", - "pr": 62 - }, - { - "note": "Add `unoptimizedPath` to OptimizerResult", - "pr": 62 - }, - { - "note": "Enable PLP VIP feature and add gasCost field to LiquidityProviderRegistry", - "pr": 65 - } - ], - "timestamp": 1606961263 - }, - { - "version": "5.2.0", - "changes": [ - { - "note": "Update Gas schedules", - "pr": 34 - }, - { - "note": "Return the maker/taker token decimals from the sampler as part of the `SwapQuote`", - "pr": 34 - }, - { - "note": "Disable off-chain sampling for Balancer and CREAM", - "pr": 41 - } - ], - "timestamp": 1605763885 - }, - { - "version": "5.1.1", - "changes": [ - { - "note": "Disable PLP VIP feature in EP swap quote consumer", - "pr": 36 - } - ], - "timestamp": 1605320370 - }, - { - "version": "5.1.0", - "changes": [ - { - "note": "Add support for LiquidityProvider feature in the swap quote consumer", - "pr": 16 - }, - { - "note": "Remove support for MultiBridge 😞", - "pr": 16 - } - ], - "timestamp": 1605302002 - }, - { - "timestamp": 1604620645, - "version": "5.0.3", - "changes": [ - { - "note": "Dependencies updated" - } - ] - }, - { - "timestamp": 1604385937, - "version": "5.0.2", - "changes": [ - { - "note": "Dependencies updated" - }, - { - "note": "adding Curve pools: PAX, hBTC, metapools: gUSD, hUSD, USDn, mUSD, tBTC", - "pr": 26 - } - ] - }, - { - "timestamp": 1604376968, - "version": "5.0.1", - "changes": [ - { - "note": "Dependencies updated" - } - ] - }, - { - "version": "5.0.0", - "changes": [ - { - "note": "Support multiple `Shells` by supplying the `pool` address", - "pr": 17 - }, - { - "note": "Make use of Token Adjacency in more places. Moved as a parameter for the quote", - "pr": 24 - } - ], - "timestamp": 1604355662 - }, - { - "version": "4.8.1", - "changes": [ - { - "note": "Fix Gas schedule with `SnowSwap` and `Bancor`", - "pr": 15 - } - ], - "timestamp": 1603851023 - }, - { - "version": "4.8.0", - "changes": [ - { - "note": "Moved Bridge addresses into Asset-swapper", - "pr": 4 - }, - { - "note": "Updated Sampler to Solidity 0.6", - "pr": 4 - } - ], - "timestamp": 1603833198 - }, - { - "timestamp": 1603487270, - "version": "4.7.1", - "changes": [ - { - "note": "Dependencies updated" - } - ] - }, - { - "version": "4.7.0", - "changes": [ - { - "note": "Return quoteReport from SwapQuoter functions", - "pr": 2627 - }, - { - "note": "Allow an empty override for sampler overrides", - "pr": 2637 - }, - { - "note": "Potentially heavy CPU functions inside the optimizer now yield to the event loop. As such they are now async.", - "pr": 2637 - }, - { - "note": "Support more varied curves", - "pr": 2633 - }, - { - "note": "Make path optimization go faster", - "pr": 2640 - }, - { - "note": "Adds `getBidAskLiquidityForMakerTakerAssetPairAsync` to return more detailed sample information", - "pr": 2641 - }, - { - "note": "Fix regression where a split on the same source was collapsed into a single fill", - "pr": 2654 - }, - { - "note": "Add support for buy token affiliate fees", - "pr": 2658 - }, - { - "note": "Fix optimization of buy paths", - "pr": 2655 - }, - { - "note": "Fix depth buy scale", - "pr": 2659 - }, - { - "note": "Adjust fill by ethToInputRate when ethToOutputRate is 0", - "pr": 2660 - }, - { - "note": "Add Bancor as liquidity source", - "pr": 2650 - }, - { - "note": "Added `mStable`", - "pr": 2662 - }, - { - "note": "Merge `erc20-bridge-sampler` into this package", - "pr": 2664 - }, - { - "note": "Added `Mooniswap`", - "pr": 2675 - }, - { - "note": "Stop requiring takerAddress for RFQ-T indicative quotes", - "pr": 2684 - }, - { - "note": "Added two-hop support", - "pr": 2647 - }, - { - "note": "Move ERC20BridgeSampler interfaces into `interfaces` directory", - "pr": 2647 - }, - { - "note": "Use on-chain sampling (sometimes) for Balancer", - "pr": 2647 - }, - { - "note": "Re-worked `Kyber` quotes supporting multiple reserves", - "pr": 2683 - }, - { - "note": "Enable Quote Report to be generated with an option `shouldGenerateQuoteReport`. Default is `false`", - "pr": 2687 - }, - { - "note": "Add `refundReceiver` to `ExchangeProxySwapQuoteConsumer` options.", - "pr": 2657 - }, - { - "note": "Use `IZeroExContract` in EP swap quote consumer.", - "pr": 2657 - }, - { - "note": "Set `rfqtTakerAddress` to null in EP consumer", - "pr": 2692 - }, - { - "note": "Return Mooniswap pool in sampler and encode it in bridge data", - "pr": 2692 - }, - { - "note": "Added `Swerve`", - "pr": 2698 - }, - { - "note": "Added `SushiSwap`", - "pr": 2698 - }, - { - "note": "Add uniswap VIP support", - "pr": 2703 - }, - { - "note": "Add `includedSources` support", - "pr": 2703 - }, - { - "note": "Added `Curve` Tripool", - "pr": 2708 - }, - { - "note": "Pass back fillData from quote reporter", - "pr": 2702 - }, - { - "note": "Fix Balancer sampling", - "pr": 2711 - }, - { - "note": "Respect max slippage in EP consumer", - "pr": 2712 - }, - { - "note": "Introduced Path class, exchangeProxyOverhead parameter", - "pr": 2691 - }, - { - "note": "Added `Shell`", - "pr": 2722 - }, - { - "note": "Fix exchange proxy overhead gas being scaled by gas price", - "pr": 2723 - }, - { - "note": "Remove 0x-API swap/v0-specifc code from asset-swapper", - "pr": 2725 - }, - { - "note": "Added `DODO`", - "pr": 2701 - }, - { - "note": "Fix for some edge cases with `includedSources` and `MultiHop`", - "pr": 2730 - }, - { - "note": "Introduced `excludedFeeSources` to disable sources when determining the price of an asset in ETH", - "pr": 2731 - }, - { - "note": "Support `DODO` Trade Allowed parameter to automatically disable the pool", - "pr": 2732 - }, - { - "note": "Added `SwerveBridge` and `SnowSwapBridge` deployed addresses", - "pr": 7 - } - ], - "timestamp": 1603265572 - }, - { - "version": "4.6.0", - "changes": [ - { - "note": "Use internal Eth Gas Station proxy", - "pr": 2614 - }, - { - "note": "Renamed RFQT request parameters", - "pr": 2582 - }, - { - "note": "Fix worst case asset amount calculations.", - "pr": 2615 - }, - { - "note": "Specify EthGasStation url as an optional parameter", - "pr": 2617 - }, - { - "note": "Singleton Gas Price Oracle", - "pr": 2619 - }, - { - "note": "\"Fix\" forwarder buys of low decimal tokens.", - "pr": 2618 - }, - { - "note": "Add Balancer support", - "pr": 2613 - }, - { - "note": "Consolidate UniswapV2 sources, Curve sources in `ERC20BridgeSource` enum", - "pr": 2613 - }, - { - "note": "Change gas/fee schedule values from constants to functions returning numbers", - "pr": 2613 - }, - { - "note": "Specify overrides to the ERC20Sampler contract, by default the latest bytecode is the override", - "pr": 2629 - } - ], - "timestamp": 1594788383 - }, - { - "version": "4.5.0", - "changes": [ - { - "note": "Add support for private liquidity providers", - "pr": 2505 - }, - { - "note": "Big refactor of market operation utils", - "pr": 2513 - }, - { - "note": "Remove `dustFractionThreshold`, `noConflicts` options.", - "pr": 2513 - }, - { - "note": "Revamp fill optimization algorithm", - "pr": 2513 - }, - { - "note": "Add fallback orders to quotes via `allowFallback` option.", - "pr": 2513 - }, - { - "note": "Add `maxFallbackSlippage` option.", - "pr": 2513 - }, - { - "note": "Fix fee schedule not being scaled by gas price.", - "pr": 2522 - }, - { - "note": "Fix quote optimizer bug not properly accounting for fees.", - "pr": 2526 - }, - { - "note": "Fix `getBatchMarketBuyOrdersAsync` throwing NO_OPTIMAL_PATH", - "pr": 2533 - }, - { - "note": "Add DFB support + refactor swap quote calculator utils", - "pr": 2536 - }, - { - "note": "Add support for RFQ-T, querying maker-hosted endpoints for quotes to be submitted by the taker", - "pr": 2541 - }, - { - "note": "Add support for indicative (non-committal) quotes via RFQ-T", - "pr": 2555 - }, - { - "note": "Collapse `LiquidityProvider` into `DexForwarderBridge`", - "pr": 2560 - }, - { - "note": "Added Curve `sUSD`", - "pr": 2563 - }, - { - "note": "Fix sporadically failing quote simulation tests", - "pr": 2564 - }, - { - "note": "Apply Native order penalty inline with the target amount", - "pr": 2565 - }, - { - "note": "Remove Kyber exclusion when Uniswap/Eth2Dai is present", - "pr": 2575 - }, - { - "note": "Expose fills object in asset-swapper quote orders", - "pr": 2583 - }, - { - "note": "Increase timeout for tests", - "pr": 2587 - }, - { - "note": "Add support for Uniswap V2", - "pr": 2599 - }, - { - "note": "Add support for MultiBridge", - "pr": 2593 - }, - { - "note": "Fix Uniswap V2 path ordering", - "pr": 2601 - }, - { - "note": "Add exchange proxy support", - "pr": 2591 - } - ], - "timestamp": 1592969527 - }, - { - "version": "4.4.0", - "changes": [ - { - "note": "Add support for ERC721 assets", - "pr": 2491 - }, - { - "note": "Add destroy for gas heartbeat", - "pr": 2492 - }, - { - "note": "Added `BUSD` Curve", - "pr": 2506 - }, - { - "note": "Updated `Compound` Curve address", - "pr": 2506 - } - ], - "timestamp": 1583220306 - }, - { - "version": "4.3.2", - "changes": [ - { - "note": "Fix order native pruning by fill amount", - "pr": 2500 - } - ], - "timestamp": 1582837861 - }, - { - "timestamp": 1582677073, - "version": "4.3.1", - "changes": [ - { - "note": "Dependencies updated" - } - ] - }, - { - "version": "4.3.0", - "changes": [ - { - "note": "Add `fees` to `GetMarketOrdersOpts`", - "pr": 2481 - }, - { - "note": "Incorporate fees into fill optimization", - "pr": 2481 - } - ], - "timestamp": 1582623685 - }, - { - "version": "4.2.0", - "changes": [ - { - "note": "Use `batchCall()` version of the `ERC20BridgeSampler` contract", - "pr": 2477 - }, - { - "note": "Support for sampling Curve contracts", - "pr": 2483 - } - ], - "timestamp": 1581748629 - }, - { - "timestamp": 1581204851, - "version": "4.1.2", - "changes": [ - { - "note": "Dependencies updated" - } - ] - }, - { - "version": "4.1.1", - "changes": [ - { - "note": "Fix bug with liquidity source breakdown", - "pr": 2472 - }, - { - "note": "Prune orders before creating a dummy order for the Sampler", - "pr": 2470 - }, - { - "note": "Bump sampler gas limit to 60e6", - "pr": 2471 - } - ], - "timestamp": 1580988106 - }, - { - "version": "4.1.0", - "changes": [ - { - "note": "Allow contract addresses to be passed as optional constructor ags instead of hardcoding", - "pr": 2461 - }, - { - "note": "Add swap quote liquidity source breakdown", - "pr": 2465 - } - ], - "timestamp": 1580811564 - }, - { - "version": "4.0.1", - "changes": [ - { - "note": "Fix underestimated protocol fee in worst case quote.", - "pr": 2452 - } - ], - "timestamp": 1579744659 - }, - { - "version": "4.0.0", - "changes": [ - { - "note": "Upgrade to new `Forwarder` contract with flat affiliate fees.", - "pr": 2432 - }, - { - "note": "Remove `getSmartContractParamsOrThrow()` from `SwapQuoteConsumer`s.", - "pr": 2432 - }, - { - "note": "Added `getBatchMarketBuySwapQuoteForAssetDataAsync` on `SwapQuoter`", - "pr": 2427 - }, - { - "note": "Add exponential sampling distribution and `sampleDistributionBase` option to `SwapQuoter`", - "pr": 2427 - }, - { - "note": "Compute more accurate best quote price", - "pr": 2427 - }, - { - "note": "Change Exchange sell function from `marketSellOrdersNoThrow` to `marketSellOrdersFillOrKill`", - "pr": 2450 - } - ], - "timestamp": 1579682890 - }, - { - "version": "3.0.3", - "changes": [ - { - "note": "Ignore zero sample results from the sampler contract.", - "pr": 2406 - }, - { - "note": "Increase default `runLimit` from `1024` to `4096`.", - "pr": 2406 - }, - { - "note": "Increase default `numSamples` from `8` to `10`", - "pr": 2406 - }, - { - "note": "Fix ordering of optimized orders.", - "pr": 2406 - }, - { - "note": "Fix best and worst quotes being reversed sometimes.", - "pr": 2406 - }, - { - "note": "Fix rounding of quoted asset amounts.", - "pr": 2406 - }, - { - "note": "Undo bridge slippage in best case quote calculation.", - "pr": 2406 - }, - { - "note": "Compare equivalent asset data when validating quotes and checking fee asset data.", - "pr": 2421 - } - ], - "timestamp": 1578272714 - }, - { - "version": "3.0.2", - "changes": [ - { - "note": "Fix gasPrice from `ethgasstation` to be in WEI instead of GWEI", - "pr": 2393 - }, - { - "note": "Add aggregator utils", - "pr": 2353 - } - ], - "timestamp": 1576540892 - }, - { - "timestamp": 1575931811, - "version": "3.0.1", - "changes": [ - { - "note": "Dependencies updated" - } - ] - }, - { - "version": "3.0.0", - "changes": [ - { - "note": "Refactor of logic for marketBuy/marketSell order pruning and selecting, introduced protocol fees, and refactored types used by the package", - "pr": 2272 - }, - { - "note": "Incorporate paying protocol fees.", - "pr": 2350 - }, - { - "note": "Update BigNumber version to ~9.0.0", - "pr": 2342 - }, - { - "note": "All references to network ID have been removed, and references to chain ID have been introduced instead", - "pr": 2313 - } - ], - "timestamp": 1575296764 - }, - { - "version": "2.1.0-beta.4", - "changes": [ - { - "note": "Dependencies updated" - } - ], - "timestamp": 1575290197 - }, - { - "version": "2.1.0-beta.3", - "changes": [ - { - "note": "Refactor of logic for marketBuy/marketSell order pruning and selecting, introduced protocol fees, and refactored types used by the package", - "pr": 2272 - }, - { - "note": "Incorporate paying protocol fees.", - "pr": 2350 - } - ], - "timestamp": 1574238768 - }, - { - "version": "2.1.0-beta.2", - "changes": [ - { - "note": "Update BigNumber version to ~9.0.0", - "pr": 2342 - } - ], - "timestamp": 1573159180 - }, - { - "version": "2.1.0-beta.1", - "changes": [ - { - "note": "All references to network ID have been removed, and references to chain ID have been introduced instead", - "pr": 2313 - } - ], - "timestamp": 1573159180 - }, - { - "version": "2.1.0-beta.0", - "changes": [ - { - "note": "Dependencies updated" - } - ], - "timestamp": 1570135330 - }, - { - "version": "2.0.0", - "changes": [ - { - "note": "AssetSwapper to use `@0x/orderbook` to fetch and subscribe to order updates", - "pr": 2056 - } - ], - "timestamp": 1568744790 - }, - { - "timestamp": 1567521715, - "version": "1.0.3", - "changes": [ - { - "note": "Dependencies updated" - } - ] - }, - { - "timestamp": 1566446343, - "version": "1.0.2", - "changes": [ - { - "note": "Dependencies updated" - } - ] - }, - { - "timestamp": 1565296576, - "version": "1.0.1", - "changes": [ - { - "note": "Dependencies updated" - } - ] - }, - { - "version": "1.0.0", - "changes": [ - { - "note": "Added optimization utils to consumer output", - "pr": 1988 - }, - { - "note": "Expanded test coverage", - "pr": 1980 - } - ], - "timestamp": 1564604963 - }, - { - "timestamp": 1563957393, - "version": "0.0.5", - "changes": [ - { - "note": "Dependencies updated" - } - ] - }, - { - "timestamp": 1563193019, - "version": "0.0.4", - "changes": [ - { - "note": "Switched MarketOperation type to enum and expanded default constants configuration", - "pr": 1959 - }, - { - "note": "Added additional options to control asset-swapper behavior and optimized consumer output", - "pr": 1966 - } - ] - }, - { - "timestamp": 1563047529, - "version": "0.0.3", - "changes": [ - { - "note": "Dependencies updated" - } - ] - }, - { - "timestamp": 1563006338, - "version": "0.0.2", - "changes": [ - { - "note": "Dependencies updated" - } - ] - }, - { - "version": "0.0.1", - "changes": [ - { - "note": "Refactored asset-buyer into asset-swapper to support ERC<>ERC marketSell and marketBuy operations", - "pr": 1845 - } - ] - } -] diff --git a/packages/asset-swapper/CHANGELOG.md b/packages/asset-swapper/CHANGELOG.md deleted file mode 100644 index 344b37fd98..0000000000 --- a/packages/asset-swapper/CHANGELOG.md +++ /dev/null @@ -1,893 +0,0 @@ - - -CHANGELOG - -## v16.66.4 - _August 22, 2022_ - - * Offboard Cream (#546) - * Change WooFi gas estimates (#551) - -## v16.66.3 - _August 10, 2022_ - - * Dependencies updated - -## v16.66.2 - _August 9, 2022_ - - * Upgrade dependency (#543) - -## v16.66.1 - _August 8, 2022_ - - * Upgrade dependency (#538) - -## v16.66.0 - _August 6, 2022_ - - * Add WOOFi support (#513) - -## v16.65.0 - _August 1, 2022_ - - * Use 0x gas api instead of eth gas station api (#532) - -## v16.64.0 - _July 27, 2022_ - - * Refactor `TokenAdjacency` and `TokenAdjacencyBuilder` (#517) - * Add Synthetix support` (#518) - * Replace Beethoven X subgraph URL (#519) - * Remove Mooniswap on Ethereum mainnet (#529) - -## v16.63.1 - _July 12, 2022_ - - * Better error handling for balancer cache (#515) - -## v16.63.0 - _June 29, 2022_ - - * Remove JS router (#480) - * Removed Median price in favour of best gas adjusted price (#480) - -## v16.62.2 - _Invalid date_ - - * Offboard Smoothy and ComethSwap (#509) - -## v16.62.1 - _June 15, 2022_ - - * Remove nUSD from intermediate liquidity to save on sampler gas (#505) - -## v16.62.0 - _June 14, 2022_ - - * Add MDEX on BSC (#496) - * Add KnightSwap on BSC (#498) - * Add Velodrome support on Optimism (#494) - * Do not send empty entries on Quote Report (#501) - * KnightSwap/Mdex cosmetic change (#502) - * Offboard JetSwap, CafeSwap, JulSwap, and PolyDex (#503) - -## v16.61.0 - _June 3, 2022_ - - * Add stETH wrap/unwrap support (#476) - * Offboard/clean up Oasis, CoFix, and legacy Kyber (#482) - * Add MeshSwap on Polygon (#491) - -## v16.60.1 - _May 19, 2022_ - - * Alias Balancer sor to the old version (#481) - -## v16.60.0 - _May 19, 2022_ - - * Add BiSwap on BSC (#467) - * Add GMX and Platypus on Avalanche and Enable KyberDMM on bsc (#478) - * Add Yoshi Exchange support in Fantom (#473) - * Fix KyberDMM gas underestimation (#479) - -## v16.59.0 - _May 13, 2022_ - - * Remove SnowSwap on mainnet (#468) - * Offboard Swerve Finance and LinkSwap (#469) - * Offboard Eth2Dai (#470) - * Add an optional IRfqClient for SwapQuoter#getSwapQuoteAsync (#467) - -## v16.58.0 - _Invalid date_ - - * Update Saddle pools on Mainnet (#450) - -## v16.57.3 - _May 10, 2022_ - - * Fix a runtime error related to BalancerV2SwapInfoCache (#472) - -## v16.57.2 - _May 2, 2022_ - - * Fix missing AMM quotes on indicative Quote Reports (#466) - -## v16.57.1 - _Invalid date_ - - * Added QUICK/ANY pair on Polygon (#464) - * Added cvxFXS/FXS curve pool on mainnet (#465) - -## v16.57.0 - _April 22, 2022_ - - * Add BalancerV2 batch swap support (#462) - -## v16.56.0 - _April 21, 2022_ - - * Add estimatedGas to ExtendedQuoteReport (#463) - -## v16.55.0 - _April 7, 2022_ - - * Fix fillRfqOrder VIP being used for swaps that need transformERC20 (#461) - -## v16.54.0 - _April 6, 2022_ - - * Add true VIP support for eligible RFQt swaps (#458) - -## v16.53.0 - _March 31, 2022_ - - * Adds support for STG/USDC pool on Curve Mainnet (#451) - * Use neon-router in asset-swapper tests (#453) - * Add sampler blocknumber to quote report data (#448) - -## v16.52.0 - _Invalid date_ - - * Adds support for mobius money on celo (#423) - -## v16.51.0 - _March 10, 2022_ - - * Added `Curve` `YFI-ETH` pool (#444) - -## v16.50.3 - _March 9, 2022_ - - * Routing glue optimization (#439) - * Move VIP source routing into neon-router & disable fallback orders for native/plp (#440) - -## v16.50.2 - _March 7, 2022_ - - * Update `Uniswap_V3` address on `Ropsten` (#441) - -## v16.50.1 - _March 3, 2022_ - - * Add BTRFLY/WETH Curve pool on mainnet (#437) - * Lower Uniswap V3 Sampler gas allowance (#438) - -## v16.50.0 - _March 2, 2022_ - - * Adding support for Geist on `Fantom` (#398) - * Improve Uniswap V3 gas schedule (#424) - -## v16.49.9 - _February 24, 2022_ - - * Fix native order scaling & filter out 1 wei quotes (#430) - -## v16.49.8 - _February 22, 2022_ - - * Dependencies updated - -## v16.49.7 - _February 22, 2022_ - - * Fix native order handling for very small quotes and bump `neon-router` dependency (#425) - -## v16.49.6 - _February 17, 2022_ - - * Fixed btrfly routing to include the ohmV2/dai, ohmV2/btfly, and ohmV2/weth pools (#427) - -## v16.49.5 - _February 14, 2022_ - - * Fix scaling 1 base unit to 0, round output to base units (#422) - -## v16.49.4 - _February 10, 2022_ - - * Reverts 'Improve Uniswap V3 gas schedule' due to issue with buys (#419) - -## v16.49.3 - _February 10, 2022_ - - * Fix `slippage` inconsistency when recalculated in exchange proxy quote consumer (#412) - * Fix incorrect output scaling when input is less than desired amount, update fast-abi (#401) - * Improve Uniswap V3 gas schedule (#397) - * Fix add Native as VIP and use Path to compare all sources vs vip only (#413) - -## v16.49.2 - _January 31, 2022_ - - * Fix ABI encoding error with two hop buys due to applying slippage to uint(-1) values (#410) - -## v16.49.1 - _January 31, 2022_ - - * Fix WorstCaseQuoteInfo encoding bug (#402) - -## v16.49.0 - _January 28, 2022_ - - * Add more curve pools (#409) - -## v16.48.0 - _January 25, 2022_ - - * Use `MIM` as an intermediate asset on `Fantom` (#405) - -## v16.47.0 - _January 25, 2022_ - - * Adding support for Synapse on all networks (#400) - -## v16.46.0 - _January 11, 2022_ - - * Enable `Curve` ETH/CVX pool (#394) - -## v16.45.2 - _January 10, 2022_ - - * Handle 0 output samples and negative adjusted rate native orders in routing (#387) - -## v16.45.1 - _January 5, 2022_ - - * Update `Celo` intermediate tokens (#390) - -## v16.45.0 - _January 4, 2022_ - - * Capture router timings (#388) - -## v16.44.0 - _December 29, 2021_ - - * Update neon-router and use router estimated output amount (#354) - -## v16.43.0 - _December 24, 2021_ - - * `UniswapV3` support for `Optimism` (#385) - -## v16.42.0 - _December 21, 2021_ - - * `UniswapV3` support for `Polygon` (#382) - * Update `Beethoven` Graphql url (#383) - -## v16.41.0 - _December 6, 2021_ - - * Update mcusd contract address, and made celo native asset (#376) - -## v16.40.0 - _December 1, 2021_ - - * Add `AaveV2` and `Compound` deposit/withdrawal liquidity source (#321) - -## v16.39.0 - _Invalid date_ - - * Curve ETH/CRV pool (#378) - -## v16.38.0 - _November 29, 2021_ - - * Capture sampler metrics (#374) - -## v16.37.0 - _November 19, 2021_ - - * Changed Sushiswap router address (#373) - -## v16.36.0 - _November 19, 2021_ - - * Specify liquid routes for FEI/TRIBE FXS/FRAX and OHM/FRAX (#371) - -## v16.35.0 - _November 18, 2021_ - - * Add Beethoven X, MorpheusSwap and JetSwap to Fantom (#370) - -## v16.34.0 - _November 16, 2021_ - - * Add support Celo (#367) - -## v16.33.0 - _November 16, 2021_ - - * Add support for Uniswap V3 1 bps pools (#366) - -## v16.32.0 - _November 9, 2021_ - - * Extended Quote Report (#361) - -## v16.31.0 - _November 3, 2021_ - - * Added `Curve`, `Curve_V2` and `KyberDmm` to Avalanche (#363) - -## v16.30.1 - _November 3, 2021_ - - * Dependencies updated - -## v16.30.0 - _October 19, 2021_ - - * Fantom deployment (#347) - -## v16.29.3 - _October 18, 2021_ - - * Update neon-router version and address breaking changes (#344) - -## v16.29.2 - _October 13, 2021_ - - * Check MAX_IN_RATIO in sampleBuysFromBalancer (#338) - * Go back to using transformERC20 (instead of transformERC20Staging) (#343) - -## v16.29.1 - _October 4, 2021_ - - * Remove `Clipper` as a custom liquidity source (#335) - -## v16.29.0 - _October 4, 2021_ - - * Initial integration of neon-router (behind feature flag) (#295) - -## v16.28.0 - _September 29, 2021_ - - * Update ExchangeProxySwapQuoteConsumer for Multiplex V2 and friends (#282) - -## v16.27.5 - _Invalid date_ - - * Remove protocol fees by setting `PROTOCOL_FEE_MULTIPLIER` to 0 (#333) - -## v16.27.4 - _September 15, 2021_ - - * Dependencies updated - -## v16.27.3 - _September 14, 2021_ - - * Dependencies updated - -## v16.27.2 - _September 14, 2021_ - - * Dependencies updated - -## v16.27.1 - _September 8, 2021_ - - * Fix ApproximateBuys sampler to terminate if the buy amount is not met (#319) - -## v16.27.0 - _September 1, 2021_ - - * Avalanche deployment (#312) - -## v16.26.2 - _August 31, 2021_ - - * chore: Curve new pools (CVX-CRX, MIM, atricrypto3) - -## v16.26.1 - _August 19, 2021_ - - * Dependencies updated - -## v16.26.0 - _August 19, 2021_ - - * feat: Enable partial Native fills to be consumed, previously for v3 they were dropped (#309) - * feat: Modify Intermediate tokens to be a union (#309) - * feat: Retire Eth2Dai/Oasis (#309) - -## v16.25.0 - _August 16, 2021_ - - * Fix: fallback fills which have not been used, unique id by source-index - -## v16.24.1 - _August 11, 2021_ - - * Dependencies updated - -## v16.24.0 - _August 6, 2021_ - - * Add `Clipper` as a custom liquidity source (#299) - * Added `Curve` `Tricrypto2` and `ESD` v2 (#302) - -## v16.23.1 - _July 29, 2021_ - - * Fix fill amount rounding error when covnerting fills to orders. (#296) - -## v16.23.0 - _July 16, 2021_ - - * ACryptoS (#284) - -## v16.22.0 - _July 13, 2021_ - - * IronSwap (#281) - -## v16.21.0 - _July 10, 2021_ - - * JetSwap (#280) - -## v16.20.0 - _July 6, 2021_ - - * ShibaSwap (#276) - -## v16.19.1 - _July 6, 2021_ - - * Fix LiquidityProvider fallback (#272) - -## v16.19.0 - _July 2, 2021_ - - * Add LiquidityProvider to Polygon sources (#270) - -## v6.18.3 - _June 29, 2021_ - - * Polygon Balance V2 - -## v6.18.2 - _June 24, 2021_ - - * Dependencies updated - -## v6.18.1 - _June 22, 2021_ - - * FirebirdOneSwap, ApeSwap. New hop tokens: DFYN, BANANA, WEXPOLY (#265) - -## v6.18.0 - _June 22, 2021_ - - * Add Lido stETH deposit integration (#260) - -## v6.17.3 - _June 16, 2021_ - - * QUICK, TITAN, IRON as intermediate tokens, integrating WaultSwap and Polydex for Polygon, Curve renBTC pool - -## v6.17.2 - _June 11, 2021_ - - * Dependencies updated - -## v6.17.1 - _June 2, 2021_ - - * Dependencies updated - -## v6.17.0 - _May 27, 2021_ - - * Re-enable liquidity provider and update KNC address (#253) - -## v6.16.0 - _May 25, 2021_ - - * Add support for the Polygon chain (#240) - -## v6.15.0 - _May 21, 2021_ - - * Fix KyberDmm (#236) - * Re-enable KyberDmm (#247) - * Add Huobi Token to liquidity provider tokens (#246) - * Temporarily disable specific LiquidityProvider - -## v6.14.0 - _May 12, 2021_ - - * Add support for additional sources and intermediate tokens on Ropsten (#231) - * Add UniswapV3 VIP support (#237) - -## v6.13.0 - _May 11, 2021_ - - * Add LiquidityProvider to BSC sources (#234) - -## v6.12.0 - _May 10, 2021_ - - * `TwoHopSampler` to use `call` over `staticcall` in order to support sources like `Uniswap_V3` and `Balancer_V2` (#233) - -## v6.11.0 - _May 7, 2021_ - - * Add price comparisons data separate from the quote report (#219) - * Add caching for top Balancer V2 pools on startup and during regular intervals (#228) - * Tweak compiler settings for smaller sampler bytecode (#229) - * Fix Multiplex multihop encoding for ETH buys/sells (#230) - * Fix Sampler address override for Ganache (#232) - -## v6.10.0 - _May 5, 2021_ - - * Reactivate PancakeSwapV2 and BakerySwap VIP on BSC (#222) - * Add LUSD Curve pool (#218) - * Fix exchangeProxyGasOverhead for fallback path (#215) - * Enable ETH based Curve pools (#220) - * Reactivate PancakeSwapV2 and BakerySwap VIP on BSC (#222) - * Disable WETH based SnowSwap pools (#220) - * PLP now includes a fallback due to observed collisions (#223) - * Add Balancer V2 integration (#206) - * Re-work the PoolCache for Balancer et al (#226) - -## v6.9.1 - _May 1, 2021_ - - * Temporarily remove PancakeV2 and BakerySwap from VIP - -## v6.9.0 - _April 30, 2021_ - - * Remove conflicting Kyber reserve (#216) - -## v6.8.0 - _April 28, 2021_ - - * Prune paths which cannot improve the best path (#183) - * Use FastABI for Sampler ABI encoding and decoding (#183) - -## v6.7.0 - _April 26, 2021_ - - * Support PancakeSwap V2 (#211) - -## v6.6.1 - _Invalid date_ - - * Fixing Positive Slippage logic to not force the EP route (#209) - -## v6.6.0 - _April 16, 2021_ - - * Support `Ropsten` network (#203) - * BSC Uniswap clones (ApeSwap, CafeSwap, CheeseSwap, JulSwap), Saddle BTC pool, Curve gas schedule (#208) - -## v6.5.3 - _April 14, 2021_ - - * Apply slippage to bridge orders in consumer (#198) - -## v6.5.2 - _April 13, 2021_ - - * Dependencies updated - -## v6.5.1 - _April 12, 2021_ - - * Dependencies updated - -## v6.5.0 - _April 8, 2021_ - - * Add Kyber DMM to Ethereum mainnet (#194) - * Add default LiquidityProvider registry and allow LiquidityProvider gasCost to be a function of tokens (#196) - -## v6.4.0 - _April 1, 2021_ - - * Added Component, Smoothy, Saddle, Curve open pools, tweeks gas schedule, adding SushiSwap as a fee quote source (#182) - * Use SOURCE_FLAGS.rfqOrder in comparisonPrice (#177) - * Add a cancel token to ensure timeouts are respected (#176) - * Rename {Rfqt=>Rfq} for many types in Asset Swapper (#179) - * improve logging for alt RFQ requests (#158) - * Use new bridge source ID encoding. (#162) - * Refactor to provide chain id specific addresses (#163) - * Added PancakeSwap and BakerySwap on Chain 56 (#163) - * Added Nerve and Dodo (v1) to BSC (#181) - -## v6.3.0 - _March 17, 2021_ - - * Add MooniswapLiquidityProvider "direct" route to EP consumer. (#143) - * Enable the ability to send RFQT requests thru a proxy (#159) - * Add support for MultiplexFeature (#168) - -## v6.2.0 - _March 2, 2021_ - - * drop curve Y and BUSD pools (#161) - -## v6.1.0 - _February 24, 2021_ - - * Filter MultiHop where second source is not present (#138) - * Add CurveLiquidityProvider "direct" route to EP consumer. (#127) - * Fix compiler error on `ILiquidityProvider` call (#127) - * Add deployed `CurveLiquidityProvider` addresses (#144) - * Support `Mirror Protocol` with hops to `UST` (#142) - * Fix protocol fee in fee schedule for `RfqOrder` (#146) - * Special case BNB in uni v1 sampler (#147) - * Create `FakeTaker` contract to get result data and gas used (#151) - * Added support for `Dodo` v2 (#152) - * Added support for `Linkswap` (#153) - * Re-add WBTC in default intermediate hops (#154) - * Add an alternative RFQ market making implementation (#139) - * Added an opt-in `PositiveSlippageAffiliateFee` (#101) - -## v6.0.0 - _February 10, 2021_ - - * Pull top 250 Balancer pairs on initialization (#113) - * Support v4 `RFQ` and `Limit` orders (#113) - * Refactor to consume latest `FillQuoteTransformer` (#113) - * Enable `fillData` for all sources, no longer optional (#113) - * Support `tx.origin` in RFQT quote requestor (#113) - -## v5.8.2 - _January 28, 2021_ - - * Fix error when Multihop data is not present (#80) - -## v5.8.1 - _January 26, 2021_ - - * Dependencies updated - -## v5.8.0 - _January 13, 2021_ - - * Automatically Discover Kyber reserves for tokens using `getTradingReserves` (#111) - * Return `CallResults` from the Sampler (#111) - -## v5.7.0 - _Invalid date_ - - * Add SPDX license identifiers to solidity files (#105) - -## v5.6.2 - _January 4, 2021_ - - * Dependencies updated - -## v5.6.1 - _December 31, 2020_ - - * Fix fillAmount `ExchangeProxySwapQuoteConsumer` encoding when quote is a BuyQuote - -## v5.6.0 - _December 27, 2020_ - - * Added Mooniswap V2 factory address (#100) - -## v5.5.3 - _December 23, 2020_ - - * Dependencies updated - -## v5.5.2 - _December 17, 2020_ - - * Dependencies updated - -## v5.5.1 - _December 16, 2020_ - - * Dependencies updated - -## v5.5.0 - _December 16, 2020_ - - * Bancor now supported in all pairs (#88) - -## v5.4.2 - _December 9, 2020_ - - * Dependencies updated - -## v5.4.1 - _December 7, 2020_ - - * Dependencies updated - -## v5.4.0 - _December 7, 2020_ - - * Add `takerAssetToEthRate` and `makerAssetToEthRate` to swap quote response (#49) - -## v5.3.1 - _December 3, 2020_ - - * Dependencies updated - -## v5.3.0 - _December 3, 2020_ - - * Added Crypto.com (#43) - * Add `getQuoteInfoMinBuyAmount` to quote consumer utils (#62) - * Add `unoptimizedQuoteInfo` and `unoptimizedOrders` to SwapQuoteBase (#62) - * Add `unoptimizedPath` to OptimizerResult (#62) - * Enable PLP VIP feature and add gasCost field to LiquidityProviderRegistry (#65) - -## v5.2.0 - _November 19, 2020_ - - * Update Gas schedules (#34) - * Return the maker/taker token decimals from the sampler as part of the `SwapQuote` (#34) - * Disable off-chain sampling for Balancer and CREAM (#41) - -## v5.1.1 - _November 14, 2020_ - - * Disable PLP VIP feature in EP swap quote consumer (#36) - -## v5.1.0 - _November 13, 2020_ - - * Add support for LiquidityProvider feature in the swap quote consumer (#16) - * Remove support for MultiBridge 😞 (#16) - -## v5.0.3 - _November 5, 2020_ - - * Dependencies updated - -## v5.0.2 - _November 3, 2020_ - - * Dependencies updated - * adding Curve pools: PAX, hBTC, metapools: gUSD, hUSD, USDn, mUSD, tBTC (#26) - -## v5.0.1 - _November 3, 2020_ - - * Dependencies updated - -## v5.0.0 - _November 2, 2020_ - - * Support multiple `Shells` by supplying the `pool` address (#17) - * Make use of Token Adjacency in more places. Moved as a parameter for the quote (#24) - -## v4.8.1 - _October 28, 2020_ - - * Fix Gas schedule with `SnowSwap` and `Bancor` (#15) - -## v4.8.0 - _October 27, 2020_ - - * Moved Bridge addresses into Asset-swapper (#4) - * Updated Sampler to Solidity 0.6 (#4) - -## v4.7.1 - _October 23, 2020_ - - * Dependencies updated - -## v4.7.0 - _October 21, 2020_ - - * Return quoteReport from SwapQuoter functions (#2627) - * Allow an empty override for sampler overrides (#2637) - * Potentially heavy CPU functions inside the optimizer now yield to the event loop. As such they are now async. (#2637) - * Support more varied curves (#2633) - * Make path optimization go faster (#2640) - * Adds `getBidAskLiquidityForMakerTakerAssetPairAsync` to return more detailed sample information (#2641) - * Fix regression where a split on the same source was collapsed into a single fill (#2654) - * Add support for buy token affiliate fees (#2658) - * Fix optimization of buy paths (#2655) - * Fix depth buy scale (#2659) - * Adjust fill by ethToInputRate when ethToOutputRate is 0 (#2660) - * Add Bancor as liquidity source (#2650) - * Added `mStable` (#2662) - * Merge `erc20-bridge-sampler` into this package (#2664) - * Added `Mooniswap` (#2675) - * Stop requiring takerAddress for RFQ-T indicative quotes (#2684) - * Added two-hop support (#2647) - * Move ERC20BridgeSampler interfaces into `interfaces` directory (#2647) - * Use on-chain sampling (sometimes) for Balancer (#2647) - * Re-worked `Kyber` quotes supporting multiple reserves (#2683) - * Enable Quote Report to be generated with an option `shouldGenerateQuoteReport`. Default is `false` (#2687) - * Add `refundReceiver` to `ExchangeProxySwapQuoteConsumer` options. (#2657) - * Use `IZeroExContract` in EP swap quote consumer. (#2657) - * Set `rfqtTakerAddress` to null in EP consumer (#2692) - * Return Mooniswap pool in sampler and encode it in bridge data (#2692) - * Added `Swerve` (#2698) - * Added `SushiSwap` (#2698) - * Add uniswap VIP support (#2703) - * Add `includedSources` support (#2703) - * Added `Curve` Tripool (#2708) - * Pass back fillData from quote reporter (#2702) - * Fix Balancer sampling (#2711) - * Respect max slippage in EP consumer (#2712) - * Introduced Path class, exchangeProxyOverhead parameter (#2691) - * Added `Shell` (#2722) - * Fix exchange proxy overhead gas being scaled by gas price (#2723) - * Remove 0x-API swap/v0-specifc code from asset-swapper (#2725) - * Added `DODO` (#2701) - * Fix for some edge cases with `includedSources` and `MultiHop` (#2730) - * Introduced `excludedFeeSources` to disable sources when determining the price of an asset in ETH (#2731) - * Support `DODO` Trade Allowed parameter to automatically disable the pool (#2732) - * Added `SwerveBridge` and `SnowSwapBridge` deployed addresses (#7) - -## v4.6.0 - _July 15, 2020_ - - * Use internal Eth Gas Station proxy (#2614) - * Renamed RFQT request parameters (#2582) - * Fix worst case asset amount calculations. (#2615) - * Specify EthGasStation url as an optional parameter (#2617) - * Singleton Gas Price Oracle (#2619) - * "Fix" forwarder buys of low decimal tokens. (#2618) - * Add Balancer support (#2613) - * Consolidate UniswapV2 sources, Curve sources in `ERC20BridgeSource` enum (#2613) - * Change gas/fee schedule values from constants to functions returning numbers (#2613) - * Specify overrides to the ERC20Sampler contract, by default the latest bytecode is the override (#2629) - -## v4.5.0 - _June 24, 2020_ - - * Add support for private liquidity providers (#2505) - * Big refactor of market operation utils (#2513) - * Remove `dustFractionThreshold`, `noConflicts` options. (#2513) - * Revamp fill optimization algorithm (#2513) - * Add fallback orders to quotes via `allowFallback` option. (#2513) - * Add `maxFallbackSlippage` option. (#2513) - * Fix fee schedule not being scaled by gas price. (#2522) - * Fix quote optimizer bug not properly accounting for fees. (#2526) - * Fix `getBatchMarketBuyOrdersAsync` throwing NO_OPTIMAL_PATH (#2533) - * Add DFB support + refactor swap quote calculator utils (#2536) - * Add support for RFQ-T, querying maker-hosted endpoints for quotes to be submitted by the taker (#2541) - * Add support for indicative (non-committal) quotes via RFQ-T (#2555) - * Collapse `LiquidityProvider` into `DexForwarderBridge` (#2560) - * Added Curve `sUSD` (#2563) - * Fix sporadically failing quote simulation tests (#2564) - * Apply Native order penalty inline with the target amount (#2565) - * Remove Kyber exclusion when Uniswap/Eth2Dai is present (#2575) - * Expose fills object in asset-swapper quote orders (#2583) - * Increase timeout for tests (#2587) - * Add support for Uniswap V2 (#2599) - * Add support for MultiBridge (#2593) - * Fix Uniswap V2 path ordering (#2601) - * Add exchange proxy support (#2591) - -## v4.4.0 - _March 3, 2020_ - - * Add support for ERC721 assets (#2491) - * Add destroy for gas heartbeat (#2492) - * Added `BUSD` Curve (#2506) - * Updated `Compound` Curve address (#2506) - -## v4.3.2 - _February 27, 2020_ - - * Fix order native pruning by fill amount (#2500) - -## v4.3.1 - _February 26, 2020_ - - * Dependencies updated - -## v4.3.0 - _February 25, 2020_ - - * Add `fees` to `GetMarketOrdersOpts` (#2481) - * Incorporate fees into fill optimization (#2481) - -## v4.2.0 - _February 15, 2020_ - - * Use `batchCall()` version of the `ERC20BridgeSampler` contract (#2477) - * Support for sampling Curve contracts (#2483) - -## v4.1.2 - _February 8, 2020_ - - * Dependencies updated - -## v4.1.1 - _February 6, 2020_ - - * Fix bug with liquidity source breakdown (#2472) - * Prune orders before creating a dummy order for the Sampler (#2470) - * Bump sampler gas limit to 60e6 (#2471) - -## v4.1.0 - _February 4, 2020_ - - * Allow contract addresses to be passed as optional constructor ags instead of hardcoding (#2461) - * Add swap quote liquidity source breakdown (#2465) - -## v4.0.1 - _January 23, 2020_ - - * Fix underestimated protocol fee in worst case quote. (#2452) - -## v4.0.0 - _January 22, 2020_ - - * Upgrade to new `Forwarder` contract with flat affiliate fees. (#2432) - * Remove `getSmartContractParamsOrThrow()` from `SwapQuoteConsumer`s. (#2432) - * Added `getBatchMarketBuySwapQuoteForAssetDataAsync` on `SwapQuoter` (#2427) - * Add exponential sampling distribution and `sampleDistributionBase` option to `SwapQuoter` (#2427) - * Compute more accurate best quote price (#2427) - * Change Exchange sell function from `marketSellOrdersNoThrow` to `marketSellOrdersFillOrKill` (#2450) - -## v3.0.3 - _January 6, 2020_ - - * Ignore zero sample results from the sampler contract. (#2406) - * Increase default `runLimit` from `1024` to `4096`. (#2406) - * Increase default `numSamples` from `8` to `10` (#2406) - * Fix ordering of optimized orders. (#2406) - * Fix best and worst quotes being reversed sometimes. (#2406) - * Fix rounding of quoted asset amounts. (#2406) - * Undo bridge slippage in best case quote calculation. (#2406) - * Compare equivalent asset data when validating quotes and checking fee asset data. (#2421) - -## v3.0.2 - _December 17, 2019_ - - * Fix gasPrice from `ethgasstation` to be in WEI instead of GWEI (#2393) - * Add aggregator utils (#2353) - -## v3.0.1 - _December 9, 2019_ - - * Dependencies updated - -## v3.0.0 - _December 2, 2019_ - - * Refactor of logic for marketBuy/marketSell order pruning and selecting, introduced protocol fees, and refactored types used by the package (#2272) - * Incorporate paying protocol fees. (#2350) - * Update BigNumber version to ~9.0.0 (#2342) - * All references to network ID have been removed, and references to chain ID have been introduced instead (#2313) - -## v2.1.0-beta.4 - _December 2, 2019_ - - * Dependencies updated - -## v2.1.0-beta.3 - _November 20, 2019_ - - * Refactor of logic for marketBuy/marketSell order pruning and selecting, introduced protocol fees, and refactored types used by the package (#2272) - * Incorporate paying protocol fees. (#2350) - -## v2.1.0-beta.2 - _November 7, 2019_ - - * Update BigNumber version to ~9.0.0 (#2342) - -## v2.1.0-beta.1 - _November 7, 2019_ - - * All references to network ID have been removed, and references to chain ID have been introduced instead (#2313) - -## v2.1.0-beta.0 - _October 3, 2019_ - - * Dependencies updated - -## v2.0.0 - _September 17, 2019_ - - * AssetSwapper to use `@0x/orderbook` to fetch and subscribe to order updates (#2056) - -## v1.0.3 - _September 3, 2019_ - - * Dependencies updated - -## v1.0.2 - _August 22, 2019_ - - * Dependencies updated - -## v1.0.1 - _August 8, 2019_ - - * Dependencies updated - -## v1.0.0 - _July 31, 2019_ - - * Added optimization utils to consumer output (#1988) - * Expanded test coverage (#1980) - -## v0.0.5 - _July 24, 2019_ - - * Dependencies updated - -## v0.0.4 - _July 15, 2019_ - - * Switched MarketOperation type to enum and expanded default constants configuration (#1959) - * Added additional options to control asset-swapper behavior and optimized consumer output (#1966) - -## v0.0.3 - _July 13, 2019_ - - * Dependencies updated - -## v0.0.2 - _July 13, 2019_ - - * Dependencies updated - -## v0.0.1 - _Invalid date_ - - * Refactored asset-buyer into asset-swapper to support ERC<>ERC marketSell and marketBuy operations (#1845) diff --git a/packages/asset-swapper/README.md b/packages/asset-swapper/README.md deleted file mode 100644 index c3db13b284..0000000000 --- a/packages/asset-swapper/README.md +++ /dev/null @@ -1,86 +0,0 @@ -> :warning: **@0x/asset-swapper has been deprecated!** The `asset-swapper` code has been moved to [0x-api](https://github.com/0xProject/0x-api). Please do not open a PR with `asset-swapper` changes. - -## @0x/asset-swapper - -Convenience package for swapping assets represented on the Ethereum blockchain using 0x. The package helps to perform all the off-chain computations to execute a marketBuy or marketSell function execution with 0x exchange contracts, or 0x extension contracts. Given some liquidity (0x signed orders), it helps estimate the cost of buying or selling a certain asset (giving a range) and then provide varying consumable outputs to execute the buy or sell. - -Asset-swapper integrates with the [Standard Relayer API](https://github.com/0xProject/standard-relayer-api)(in the future Mesh as well) and takes care of sourcing liquidity, order-pruning, and order-validation. The final result is a library that tells you what assets are available, provides a quote based on specified assets, and provide varying consumable metadata that can be used both on-chain in smart contracts or off-chain through web3 to swap a desired amount of ERC20 for another ERC20 asset. - -## Installation - -```bash -yarn add @0x/asset-swapper -``` - -**Import** - -```typescript -import { SwapQuoter } from '@0x/asset-swapper'; -``` - -or - -```javascript -var SwapQuoter = require('@0x/asset-swapper').SwapQuoter; -var SwapQuoteConsumer = require('@0x/asset-swapper').SwapQuoteConsumer; -``` - -If your project is in [TypeScript](https://www.typescriptlang.org/), add the following to your `tsconfig.json`: - -```json -"compilerOptions": { - "typeRoots": ["node_modules/@0x/typescript-typings/types", "node_modules/@types"], -} -``` - -## Contributing - -We welcome improvements and fixes from the wider community! To report bugs within this package, please create an issue in this repository. - -Please read our [contribution guidelines](../../CONTRIBUTING.md) before getting started. - -### Install dependencies - -If you don't have yarn workspaces enabled (Yarn < v1.0) - enable them: - -```bash -yarn config set workspaces-experimental true -``` - -Then install dependencies - -```bash -yarn install -``` - -### Build - -To build this package and all other monorepo packages that it depends on, run the following from the monorepo root directory: - -```bash -PKG=@0x/asset-swapper yarn build -``` - -Or continuously rebuild on change: - -```bash -PKG=@0x/asset-swapper yarn watch -``` - -### Clean - -```bash -yarn clean -``` - -### Lint - -```bash -yarn lint -``` - -### Run Tests - -```bash -yarn test -``` diff --git a/packages/asset-swapper/compiler.json b/packages/asset-swapper/compiler.json deleted file mode 100644 index 8cc3f1c58b..0000000000 --- a/packages/asset-swapper/compiler.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "artifactsDir": "./test/generated-artifacts", - "contractsDir": "./contracts", - "useDockerisedSolc": false, - "isOfflineMode": false, - "shouldSaveStandardInput": true, - "compilerSettings": { - "evmVersion": "istanbul", - "optimizer": { "enabled": true, "runs": 200, "details": { "yul": false, "deduplicate": true } }, - "outputSelection": { - "*": { - "*": [ - "abi", - "devdoc", - "evm.bytecode.object", - "evm.bytecode.sourceMap", - "evm.deployedBytecode.object", - "evm.deployedBytecode.sourceMap" - ] - } - } - } -} diff --git a/packages/asset-swapper/contracts/src/ApproximateBuys.sol b/packages/asset-swapper/contracts/src/ApproximateBuys.sol deleted file mode 100644 index 7f8d9ddff4..0000000000 --- a/packages/asset-swapper/contracts/src/ApproximateBuys.sol +++ /dev/null @@ -1,148 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -/* - - Copyright 2020 ZeroEx Intl. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -pragma solidity ^0.6; -pragma experimental ABIEncoderV2; - -import "@0x/contracts-utils/contracts/src/v06/LibMathV06.sol"; - - -contract ApproximateBuys { - - /// @dev Information computing buy quotes for sources that do not have native - /// buy quote support. - struct ApproximateBuyQuoteOpts { - // Arbitrary maker token data to pass to `getSellQuoteCallback`. - bytes makerTokenData; - // Arbitrary taker token data to pass to `getSellQuoteCallback`. - bytes takerTokenData; - // Callback to retrieve a sell quote. - function (bytes memory, bytes memory, uint256) - internal - view - returns (uint256) getSellQuoteCallback; - } - - uint256 private constant ONE_HUNDED_PERCENT_BPS = 1e4; - /// @dev Maximum approximate (positive) error rate when approximating a buy quote. - uint256 private constant APPROXIMATE_BUY_TARGET_EPSILON_BPS = 0.0005e4; - /// @dev Maximum iterations to perform when approximating a buy quote. - uint256 private constant APPROXIMATE_BUY_MAX_ITERATIONS = 5; - - function _sampleApproximateBuys( - ApproximateBuyQuoteOpts memory opts, - uint256[] memory makerTokenAmounts - ) - internal - view - returns (uint256[] memory takerTokenAmounts) - { - takerTokenAmounts = new uint256[](makerTokenAmounts.length); - if (makerTokenAmounts.length == 0) { - return takerTokenAmounts; - } - - uint256 sellAmount = opts.getSellQuoteCallback( - opts.makerTokenData, - opts.takerTokenData, - makerTokenAmounts[0] - ); - if (sellAmount == 0) { - return takerTokenAmounts; - } - - uint256 buyAmount = opts.getSellQuoteCallback( - opts.takerTokenData, - opts.makerTokenData, - sellAmount - ); - if (buyAmount == 0) { - return takerTokenAmounts; - } - - for (uint256 i = 0; i < makerTokenAmounts.length; i++) { - uint256 eps = 0; - for (uint256 iter = 0; iter < APPROXIMATE_BUY_MAX_ITERATIONS; iter++) { - // adjustedSellAmount = previousSellAmount * (target/actual) * JUMP_MULTIPLIER - sellAmount = _safeGetPartialAmountCeil( - makerTokenAmounts[i], - buyAmount, - sellAmount - ); - if (sellAmount == 0) { - break; - } - sellAmount = _safeGetPartialAmountCeil( - (ONE_HUNDED_PERCENT_BPS + APPROXIMATE_BUY_TARGET_EPSILON_BPS), - ONE_HUNDED_PERCENT_BPS, - sellAmount - ); - if (sellAmount == 0) { - break; - } - uint256 _buyAmount = opts.getSellQuoteCallback( - opts.takerTokenData, - opts.makerTokenData, - sellAmount - ); - if (_buyAmount == 0) { - break; - } - // We re-use buyAmount next iteration, only assign if it is - // non zero - buyAmount = _buyAmount; - // If we've reached our goal, exit early - if (buyAmount >= makerTokenAmounts[i]) { - eps = - (buyAmount - makerTokenAmounts[i]) * ONE_HUNDED_PERCENT_BPS / - makerTokenAmounts[i]; - if (eps <= APPROXIMATE_BUY_TARGET_EPSILON_BPS) { - break; - } - } - } - if (eps == 0 || eps > APPROXIMATE_BUY_TARGET_EPSILON_BPS) { - break; - } - // We do our best to close in on the requested amount, but we can either over buy or under buy and exit - // if we hit a max iteration limit - // We scale the sell amount to get the approximate target - takerTokenAmounts[i] = _safeGetPartialAmountCeil( - makerTokenAmounts[i], - buyAmount, - sellAmount - ); - } - } - - function _safeGetPartialAmountCeil( - uint256 numerator, - uint256 denominator, - uint256 target - ) - internal - view - returns (uint256 partialAmount) - { - if (numerator == 0 || target == 0 || denominator == 0) return 0; - uint256 c = numerator * target; - if (c / numerator != target) return 0; - return (c + (denominator - 1)) / denominator; - } -} diff --git a/packages/asset-swapper/contracts/src/BalanceChecker.sol b/packages/asset-swapper/contracts/src/BalanceChecker.sol deleted file mode 100644 index bfbc970e66..0000000000 --- a/packages/asset-swapper/contracts/src/BalanceChecker.sol +++ /dev/null @@ -1,123 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -/* - - Copyright 2020 ZeroEx Intl. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -pragma solidity ^0.6; - -// ERC20 contract interface -abstract contract IToken { - /// @dev Query the balance of owner - /// @param _owner The address from which the balance will be retrieved - /// @return Balance of owner - function balanceOf(address _owner) public virtual view returns (uint256); - - /// @param _owner The address of the account owning tokens - /// @param _spender The address of the account able to transfer the tokens - /// @return Amount of remaining tokens allowed to spent - function allowance(address _owner, address _spender) public virtual view returns (uint256); -} - -contract BalanceChecker { - /* - Check the token balances of wallet-token pairs. - Pass 0xeee... as a "token" address to get ETH balance. - Possible error throws: - - extremely large arrays for user and or tokens (gas cost too high) - - Returns a one-dimensional that's user.length long. - */ - function balances(address[] calldata users, address[] calldata tokens) external view returns (uint256[] memory) { - // make sure the users array and tokens array are of equal length - require(users.length == tokens.length, "users array is a different length than the tokens array"); - - uint256[] memory addrBalances = new uint256[](users.length); - - for(uint i = 0; i < users.length; i++) { - if (tokens[i] != address(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE)) { - addrBalances[i] = IToken(tokens[i]).balanceOf(users[i]); - } else { - addrBalances[i] = users[i].balance; // ETH balance - } - } - - return addrBalances; - } - - /* - Check the token balances of wallet-token pairs with a spender contract for an allowance check. - Pass 0xeee... as a "token" address to get ETH balance. - Possible error throws: - - extremely large arrays for user and or tokens (gas cost too high) - - Returns a one-dimensional that's user.length long. It is the lesser of balance and allowance - */ - function getMinOfBalancesOrAllowances(address[] calldata users, address[] calldata tokens, address spender) external view returns (uint256[] memory) { - // make sure the users array and tokens array are of equal length - require(users.length == tokens.length, "users array is a different length than the tokens array"); - - uint256[] memory addrBalances = new uint256[](users.length); - - for(uint i = 0; i < users.length; i++) { - if (tokens[i] != address(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE)) { - uint256 balance; - uint256 allowance; - balance = IToken(tokens[i]).balanceOf(users[i]); - allowance = IToken(tokens[i]).allowance(users[i], spender); - if (allowance < balance) { - addrBalances[i] = allowance; - } else { - addrBalances[i] = balance; - } - } else { - addrBalances[i] = users[i].balance; // ETH balance - } - } - - return addrBalances; - } - - /* - Check the allowances of an array of owner-spender-tokens - - Returns 0 for 0xeee... (ETH) - Possible error throws: - - extremely large arrays for user and or tokens (gas cost too high) - - Returns a one-dimensional array that's owners.length long. - */ - function allowances(address[] calldata owners, address[] calldata spenders, address[] calldata tokens) external view returns (uint256[] memory) { - // make sure the arrays are all of equal length - require(owners.length == spenders.length, "all arrays must be of equal length"); - require(owners.length == tokens.length, "all arrays must be of equal length"); - - uint256[] memory addrAllowances = new uint256[](owners.length); - - for(uint i = 0; i < owners.length; i++) { - if (tokens[i] != address(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE)) { - addrAllowances[i] = IToken(tokens[i]).allowance(owners[i], spenders[i]); - } else { - // ETH - addrAllowances[i] = 0; - } - } - - return addrAllowances; - } - - -} diff --git a/packages/asset-swapper/contracts/src/BalancerSampler.sol b/packages/asset-swapper/contracts/src/BalancerSampler.sol deleted file mode 100644 index acd9e7acb1..0000000000 --- a/packages/asset-swapper/contracts/src/BalancerSampler.sol +++ /dev/null @@ -1,197 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -/* - - Copyright 2020 ZeroEx Intl. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -pragma solidity ^0.6; -pragma experimental ABIEncoderV2; - -import "./interfaces/IBalancer.sol"; - - -contract BalancerSampler { - - /// @dev Base gas limit for Balancer calls. - uint256 constant private BALANCER_CALL_GAS = 300e3; // 300k - - // Balancer math constants - // https://github.com/balancer-labs/balancer-core/blob/master/contracts/BConst.sol - uint256 constant private BONE = 10 ** 18; - uint256 constant private MAX_IN_RATIO = BONE / 2; - uint256 constant private MAX_OUT_RATIO = (BONE / 3) + 1 wei; - - struct BalancerState { - uint256 takerTokenBalance; - uint256 makerTokenBalance; - uint256 takerTokenWeight; - uint256 makerTokenWeight; - uint256 swapFee; - } - - /// @dev Sample sell quotes from Balancer. - /// @param poolAddress Address of the Balancer pool to query. - /// @param takerToken Address of the taker token (what to sell). - /// @param makerToken Address of the maker token (what to buy). - /// @param takerTokenAmounts Taker token sell amount for each sample. - /// @return makerTokenAmounts Maker amounts bought at each taker token - /// amount. - function sampleSellsFromBalancer( - address poolAddress, - address takerToken, - address makerToken, - uint256[] memory takerTokenAmounts - ) - public - view - returns (uint256[] memory makerTokenAmounts) - { - IBalancer pool = IBalancer(poolAddress); - uint256 numSamples = takerTokenAmounts.length; - makerTokenAmounts = new uint256[](numSamples); - if (!pool.isBound(takerToken) || !pool.isBound(makerToken)) { - return makerTokenAmounts; - } - - BalancerState memory poolState; - poolState.takerTokenBalance = pool.getBalance(takerToken); - poolState.makerTokenBalance = pool.getBalance(makerToken); - poolState.takerTokenWeight = pool.getDenormalizedWeight(takerToken); - poolState.makerTokenWeight = pool.getDenormalizedWeight(makerToken); - poolState.swapFee = pool.getSwapFee(); - - for (uint256 i = 0; i < numSamples; i++) { - // Handles this revert scenario: - // https://github.com/balancer-labs/balancer-core/blob/master/contracts/BPool.sol#L443 - if (takerTokenAmounts[i] > _bmul(poolState.takerTokenBalance, MAX_IN_RATIO)) { - break; - } - try - pool.calcOutGivenIn - {gas: BALANCER_CALL_GAS} - ( - poolState.takerTokenBalance, - poolState.takerTokenWeight, - poolState.makerTokenBalance, - poolState.makerTokenWeight, - takerTokenAmounts[i], - poolState.swapFee - ) - returns (uint256 amount) - { - makerTokenAmounts[i] = amount; - // Break early if there are 0 amounts - if (makerTokenAmounts[i] == 0) { - break; - } - } catch (bytes memory) { - // Swallow failures, leaving all results as zero. - break; - } - } - } - - /// @dev Sample buy quotes from Balancer. - /// @param poolAddress Address of the Balancer pool to query. - /// @param takerToken Address of the taker token (what to sell). - /// @param makerToken Address of the maker token (what to buy). - /// @param makerTokenAmounts Maker token buy amount for each sample. - /// @return takerTokenAmounts Taker amounts sold at each maker token - /// amount. - function sampleBuysFromBalancer( - address poolAddress, - address takerToken, - address makerToken, - uint256[] memory makerTokenAmounts - ) - public - view - returns (uint256[] memory takerTokenAmounts) - { - IBalancer pool = IBalancer(poolAddress); - uint256 numSamples = makerTokenAmounts.length; - takerTokenAmounts = new uint256[](numSamples); - if (!pool.isBound(takerToken) || !pool.isBound(makerToken)) { - return takerTokenAmounts; - } - - BalancerState memory poolState; - poolState.takerTokenBalance = pool.getBalance(takerToken); - poolState.makerTokenBalance = pool.getBalance(makerToken); - poolState.takerTokenWeight = pool.getDenormalizedWeight(takerToken); - poolState.makerTokenWeight = pool.getDenormalizedWeight(makerToken); - poolState.swapFee = pool.getSwapFee(); - - for (uint256 i = 0; i < numSamples; i++) { - // Handles this revert scenario: - // https://github.com/balancer-labs/balancer-core/blob/master/contracts/BPool.sol#L505 - if (makerTokenAmounts[i] > _bmul(poolState.makerTokenBalance, MAX_OUT_RATIO)) { - break; - } - try - pool.calcInGivenOut - {gas: BALANCER_CALL_GAS} - ( - poolState.takerTokenBalance, - poolState.takerTokenWeight, - poolState.makerTokenBalance, - poolState.makerTokenWeight, - makerTokenAmounts[i], - poolState.swapFee - ) - returns (uint256 amount) - { - // Handles this revert scenario: - // https://github.com/balancer-labs/balancer-core/blob/master/contracts/BPool.sol#L443 - if (amount > _bmul(poolState.takerTokenBalance, MAX_IN_RATIO)) { - break; - } - - takerTokenAmounts[i] = amount; - // Break early if there are 0 amounts - if (takerTokenAmounts[i] == 0) { - break; - } - } catch (bytes memory) { - // Swallow failures, leaving all results as zero. - break; - } - } - } - - /// @dev Hacked version of Balancer's `bmul` function, returning 0 instead - /// of reverting. - /// https://github.com/balancer-labs/balancer-core/blob/master/contracts/BNum.sol#L63-L73 - /// @param a The first operand. - /// @param b The second operand. - /// @param c The result of the multiplication, or 0 if `bmul` would've reverted. - function _bmul(uint256 a, uint256 b) - private - pure - returns (uint256 c) - { - uint c0 = a * b; - if (a != 0 && c0 / a != b) { - return 0; - } - uint c1 = c0 + (BONE / 2); - if (c1 < c0) { - return 0; - } - uint c2 = c1 / BONE; - return c2; - } -} diff --git a/packages/asset-swapper/contracts/src/BalancerV2BatchSampler.sol b/packages/asset-swapper/contracts/src/BalancerV2BatchSampler.sol deleted file mode 100644 index f99284861d..0000000000 --- a/packages/asset-swapper/contracts/src/BalancerV2BatchSampler.sol +++ /dev/null @@ -1,105 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -/* - - Copyright 2021 ZeroEx Intl. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -pragma solidity ^0.6; -pragma experimental ABIEncoderV2; - -import "./interfaces/IBalancerV2Vault.sol"; -import "./BalancerV2Common.sol"; - -contract BalancerV2BatchSampler is BalancerV2Common { - - // Replaces amount for first step with each takerTokenAmount and calls queryBatchSwap using supplied steps - /// @dev Sample sell quotes from Balancer V2 supporting multihops. - /// @param swapSteps Array of swap steps (can be >= 1). - /// @param swapAssets Array of token address for swaps. - /// @param takerTokenAmounts Taker token sell amount for each sample. - function sampleMultihopSellsFromBalancerV2( - IBalancerV2Vault vault, - IBalancerV2Vault.BatchSwapStep[] memory swapSteps, - address[] memory swapAssets, - uint256[] memory takerTokenAmounts - ) - public - returns (uint256[] memory makerTokenAmounts) - { - uint256 numSamples = takerTokenAmounts.length; - makerTokenAmounts = new uint256[](numSamples); - IBalancerV2Vault.FundManagement memory swapFunds = - _createSwapFunds(); - - for (uint256 i = 0; i < numSamples; i++) { - swapSteps[0].amount = takerTokenAmounts[i]; - try - // For sells we specify the takerToken which is what the vault will receive from the trade - vault.queryBatchSwap(IBalancerV2Vault.SwapKind.GIVEN_IN, swapSteps, swapAssets, swapFunds) - // amounts represent pool balance deltas from the swap (incoming balance, outgoing balance) - returns (int256[] memory amounts) { - // Outgoing balance is negative so we need to flip the sign - // Note - queryBatchSwap will return a delta for each token in the assets array and last asset should be tokenOut - int256 amountOutFromPool = amounts[amounts.length - 1] * -1; - if (amountOutFromPool <= 0) { - break; - } - makerTokenAmounts[i] = uint256(amountOutFromPool); - } catch { - // Swallow failures, leaving all results as zero. - break; - } - } - } - - // Replaces amount for first step with each makerTokenAmount and calls queryBatchSwap using supplied steps - /// @dev Sample buy quotes from Balancer V2 supporting multihops. - /// @param swapSteps Array of swap steps (can be >= 1). - /// @param swapAssets Array of token address for swaps. - /// @param makerTokenAmounts Maker token buy amount for each sample. - function sampleMultihopBuysFromBalancerV2( - IBalancerV2Vault vault, - IBalancerV2Vault.BatchSwapStep[] memory swapSteps, - address[] memory swapAssets, - uint256[] memory makerTokenAmounts - ) - public - returns (uint256[] memory takerTokenAmounts) - { - uint256 numSamples = makerTokenAmounts.length; - takerTokenAmounts = new uint256[](numSamples); - IBalancerV2Vault.FundManagement memory swapFunds = - _createSwapFunds(); - - for (uint256 i = 0; i < numSamples; i++) { - swapSteps[0].amount = makerTokenAmounts[i]; - try - // Uses GIVEN_OUT type for Buy - vault.queryBatchSwap(IBalancerV2Vault.SwapKind.GIVEN_OUT, swapSteps, swapAssets, swapFunds) - // amounts represent pool balance deltas from the swap (incoming balance, outgoing balance) - returns (int256[] memory amounts) { - int256 amountIntoPool = amounts[0]; - if (amountIntoPool <= 0) { - break; - } - takerTokenAmounts[i] = uint256(amountIntoPool); - } catch { - // Swallow failures, leaving all results as zero. - break; - } - } - } -} diff --git a/packages/asset-swapper/contracts/src/BalancerV2Common.sol b/packages/asset-swapper/contracts/src/BalancerV2Common.sol deleted file mode 100644 index d01a1b2195..0000000000 --- a/packages/asset-swapper/contracts/src/BalancerV2Common.sol +++ /dev/null @@ -1,41 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -/* - - Copyright 2021 ZeroEx Intl. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -pragma solidity ^0.6; -pragma experimental ABIEncoderV2; - -import "./interfaces/IBalancerV2Vault.sol"; - - -contract BalancerV2Common { - - function _createSwapFunds() - internal - view - returns (IBalancerV2Vault.FundManagement memory) - { - return - IBalancerV2Vault.FundManagement({ - sender: address(this), - fromInternalBalance: false, - recipient: payable(address(this)), - toInternalBalance: false - }); - } -} diff --git a/packages/asset-swapper/contracts/src/BalancerV2Sampler.sol b/packages/asset-swapper/contracts/src/BalancerV2Sampler.sol deleted file mode 100644 index a6cfdaaa3b..0000000000 --- a/packages/asset-swapper/contracts/src/BalancerV2Sampler.sol +++ /dev/null @@ -1,142 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -/* - - Copyright 2021 ZeroEx Intl. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -pragma solidity ^0.6; -pragma experimental ABIEncoderV2; - -import "./SamplerUtils.sol"; -import "./interfaces/IBalancerV2Vault.sol"; -import "./BalancerV2Common.sol"; - - -contract BalancerV2Sampler is SamplerUtils, BalancerV2Common { - - /// @dev Sample sell quotes from Balancer V2. - /// @param poolInfo Struct with pool related data - /// @param takerToken Address of the taker token (what to sell). - /// @param makerToken Address of the maker token (what to buy). - /// @param takerTokenAmounts Taker token sell amount for each sample. - /// @return makerTokenAmounts Maker amounts bought at each taker token - /// amount. - function sampleSellsFromBalancerV2( - IBalancerV2Vault.BalancerV2PoolInfo memory poolInfo, - address takerToken, - address makerToken, - uint256[] memory takerTokenAmounts - ) - public - returns (uint256[] memory makerTokenAmounts) - { - _assertValidPair(makerToken, takerToken); - IBalancerV2Vault vault = IBalancerV2Vault(poolInfo.vault); - address[] memory swapAssets = new address[](2); - swapAssets[0] = takerToken; - swapAssets[1] = makerToken; - - uint256 numSamples = takerTokenAmounts.length; - makerTokenAmounts = new uint256[](numSamples); - IBalancerV2Vault.FundManagement memory swapFunds = - _createSwapFunds(); - - for (uint256 i = 0; i < numSamples; i++) { - IBalancerV2Vault.BatchSwapStep[] memory swapSteps = - _createSwapSteps(poolInfo, takerTokenAmounts[i]); - - try - // For sells we specify the takerToken which is what the vault will receive from the trade - vault.queryBatchSwap(IBalancerV2Vault.SwapKind.GIVEN_IN, swapSteps, swapAssets, swapFunds) - // amounts represent pool balance deltas from the swap (incoming balance, outgoing balance) - returns (int256[] memory amounts) { - // Outgoing balance is negative so we need to flip the sign - int256 amountOutFromPool = amounts[amounts.length - 1] * -1; - if (amountOutFromPool <= 0) { - break; - } - makerTokenAmounts[i] = uint256(amountOutFromPool); - } catch (bytes memory) { - // Swallow failures, leaving all results as zero. - break; - } - } - } - - /// @dev Sample buy quotes from Balancer V2. - /// @param poolInfo Struct with pool related data - /// @param takerToken Address of the taker token (what to sell). - /// @param makerToken Address of the maker token (what to buy). - /// @param makerTokenAmounts Maker token buy amount for each sample. - /// @return takerTokenAmounts Taker amounts sold at each maker token - /// amount. - function sampleBuysFromBalancerV2( - IBalancerV2Vault.BalancerV2PoolInfo memory poolInfo, - address takerToken, - address makerToken, - uint256[] memory makerTokenAmounts - ) - public - returns (uint256[] memory takerTokenAmounts) - { - _assertValidPair(makerToken, takerToken); - IBalancerV2Vault vault = IBalancerV2Vault(poolInfo.vault); - address[] memory swapAssets = new address[](2); - swapAssets[0] = takerToken; - swapAssets[1] = makerToken; - - uint256 numSamples = makerTokenAmounts.length; - takerTokenAmounts = new uint256[](numSamples); - IBalancerV2Vault.FundManagement memory swapFunds = - _createSwapFunds(); - - for (uint256 i = 0; i < numSamples; i++) { - IBalancerV2Vault.BatchSwapStep[] memory swapSteps = - _createSwapSteps(poolInfo, makerTokenAmounts[i]); - - try - // For buys we specify the makerToken which is what taker will receive from the trade - vault.queryBatchSwap(IBalancerV2Vault.SwapKind.GIVEN_OUT, swapSteps, swapAssets, swapFunds) - returns (int256[] memory amounts) { - int256 amountIntoPool = amounts[0]; - if (amountIntoPool <= 0) { - break; - } - takerTokenAmounts[i] = uint256(amountIntoPool); - } catch (bytes memory) { - // Swallow failures, leaving all results as zero. - break; - } - } - } - - function _createSwapSteps( - IBalancerV2Vault.BalancerV2PoolInfo memory poolInfo, - uint256 amount - ) private pure returns (IBalancerV2Vault.BatchSwapStep[] memory) { - IBalancerV2Vault.BatchSwapStep[] memory swapSteps = - new IBalancerV2Vault.BatchSwapStep[](1); - swapSteps[0] = IBalancerV2Vault.BatchSwapStep({ - poolId: poolInfo.poolId, - assetInIndex: 0, - assetOutIndex: 1, - amount: amount, - userData: "" - }); - - return swapSteps; - } -} diff --git a/packages/asset-swapper/contracts/src/BancorSampler.sol b/packages/asset-swapper/contracts/src/BancorSampler.sol deleted file mode 100644 index 349c1c7631..0000000000 --- a/packages/asset-swapper/contracts/src/BancorSampler.sol +++ /dev/null @@ -1,141 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -/* - - Copyright 2020 ZeroEx Intl. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -pragma solidity ^0.6; -pragma experimental ABIEncoderV2; - -import "./interfaces/IBancor.sol"; - - -contract BancorSampler { - - /// @dev Base gas limit for Bancor calls. - uint256 constant private BANCOR_CALL_GAS = 300e3; // 300k - - struct BancorSamplerOpts { - IBancorRegistry registry; - address[][] paths; - } - - /// @dev Sample sell quotes from Bancor. - /// @param opts BancorSamplerOpts The Bancor registry contract address and paths - /// @param takerToken Address of the taker token (what to sell). - /// @param makerToken Address of the maker token (what to buy). - /// @param takerTokenAmounts Taker token sell amount for each sample. - /// @return bancorNetwork the Bancor Network address - /// @return path the selected conversion path from bancor - /// @return makerTokenAmounts Maker amounts bought at each taker token - /// amount. - function sampleSellsFromBancor( - BancorSamplerOpts memory opts, - address takerToken, - address makerToken, - uint256[] memory takerTokenAmounts - ) - public - view - returns (address bancorNetwork, address[] memory path, uint256[] memory makerTokenAmounts) - { - if (opts.paths.length == 0) { - return (bancorNetwork, path, makerTokenAmounts); - } - (bancorNetwork, path) = _findBestPath(opts, takerToken, makerToken, takerTokenAmounts); - makerTokenAmounts = new uint256[](takerTokenAmounts.length); - - for (uint256 i = 0; i < makerTokenAmounts.length; i++) { - try - IBancorNetwork(bancorNetwork) - .rateByPath - {gas: BANCOR_CALL_GAS} - (path, takerTokenAmounts[i]) - returns (uint256 amount) - { - makerTokenAmounts[i] = amount; - // Break early if there are 0 amounts - if (makerTokenAmounts[i] == 0) { - break; - } - } catch { - // Swallow failures, leaving all results as zero. - break; - } - } - return (bancorNetwork, path, makerTokenAmounts); - } - - /// @dev Sample buy quotes from Bancor. Unimplemented - /// @param opts BancorSamplerOpts The Bancor registry contract address and paths - /// @param takerToken Address of the taker token (what to sell). - /// @param makerToken Address of the maker token (what to buy). - /// @param makerTokenAmounts Maker token buy amount for each sample. - /// @return bancorNetwork the Bancor Network address - /// @return path the selected conversion path from bancor - /// @return takerTokenAmounts Taker amounts sold at each maker token - /// amount. - function sampleBuysFromBancor( - BancorSamplerOpts memory opts, - address takerToken, - address makerToken, - uint256[] memory makerTokenAmounts - ) - public - view - returns (address bancorNetwork, address[] memory path, uint256[] memory takerTokenAmounts) - { - } - - function _findBestPath( - BancorSamplerOpts memory opts, - address takerToken, - address makerToken, - uint256[] memory takerTokenAmounts - ) - internal - view - returns (address bancorNetwork, address[] memory path) - { - bancorNetwork = opts.registry.getAddress(opts.registry.BANCOR_NETWORK()); - if (opts.paths.length == 0) { - return (bancorNetwork, path); - } - uint256 maxBoughtAmount = 0; - // Find the best path by selling the largest taker amount - for (uint256 i = 0; i < opts.paths.length; i++) { - if (opts.paths[i].length < 2) { - continue; - } - - try - IBancorNetwork(bancorNetwork) - .rateByPath - {gas: BANCOR_CALL_GAS} - (opts.paths[i], takerTokenAmounts[takerTokenAmounts.length-1]) - returns (uint256 amount) - { - if (amount > maxBoughtAmount) { - maxBoughtAmount = amount; - path = opts.paths[i]; - } - } catch { - // Swallow failures, leaving all results as zero. - continue; - } - } - } -} diff --git a/packages/asset-swapper/contracts/src/BancorV3Sampler.sol b/packages/asset-swapper/contracts/src/BancorV3Sampler.sol deleted file mode 100644 index 3a54179d9c..0000000000 --- a/packages/asset-swapper/contracts/src/BancorV3Sampler.sol +++ /dev/null @@ -1,120 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -/* - - Copyright 2022 ZeroEx Intl. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -pragma solidity ^0.6; -pragma experimental ABIEncoderV2; - -import "./interfaces/IBancorV3.sol"; - - -contract BancorV3Sampler -{ - /// @dev Gas limit for BancorV3 calls. - uint256 constant private BancorV3_CALL_GAS = 150e3; // 150k - - address constant public ETH = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; - - /// @dev Sample sell quotes from BancorV3. - /// @param weth The WETH contract address - /// @param router Router to look up tokens and amounts - /// @param path Token route. Should be takerToken -> makerToken - /// @param takerTokenAmounts Taker token sell amount for each sample. - /// @return makerTokenAmounts Maker amounts bought at each taker token - /// amount. - function sampleSellsFromBancorV3( - address weth, - address router, - address[] memory path, - uint256[] memory takerTokenAmounts - ) - public - view - returns (uint256[] memory makerTokenAmounts) - { - uint256 numSamples = takerTokenAmounts.length; - makerTokenAmounts = new uint256[](numSamples); - - if(path[0] == weth){ - path[0] = ETH; - } - if(path[1] == weth){ - path[1] = ETH; - } - - for (uint256 i = 0; i < numSamples; i++) { - try - IBancorV3(router).tradeOutputBySourceAmount(path[0], path[1], takerTokenAmounts[i]) - returns (uint256 amount) - { - makerTokenAmounts[i] = amount; - // Break early if there are 0 amounts - if (makerTokenAmounts[i] == 0) { - break; - } - } catch (bytes memory) { - // Swallow failures, leaving all results as zero. - break; - } - } - } - - /// @dev Sample buy quotes from BancorV3. - /// @param weth The WETH contract address - /// @param router Router to look up tokens and amounts - /// @param path Token route. Should be takerToken -> makerToken. - /// @param makerTokenAmounts Maker token buy amount for each sample. - /// @return takerTokenAmounts Taker amounts sold at each maker token - /// amount. - function sampleBuysFromBancorV3( - address weth, - address router, - address[] memory path, - uint256[] memory makerTokenAmounts - ) - public - view - returns (uint256[] memory takerTokenAmounts) - { - uint256 numSamples = makerTokenAmounts.length; - takerTokenAmounts = new uint256[](numSamples); - - if(path[0] == weth){ - path[0] = ETH; - } - if(path[1] == weth){ - path[1] = ETH; - } - - for (uint256 i = 0; i < numSamples; i++) { - try - IBancorV3(router).tradeInputByTargetAmount(path[0], path[1], makerTokenAmounts[i]) - returns (uint256 amount) - { - takerTokenAmounts[i] = amount; - // Break early if there are 0 amounts - if (takerTokenAmounts[i] == 0) { - break; - } - } catch (bytes memory) { - // Swallow failures, leaving all results as zero. - break; - } - } - } -} \ No newline at end of file diff --git a/packages/asset-swapper/contracts/src/CompoundSampler.sol b/packages/asset-swapper/contracts/src/CompoundSampler.sol deleted file mode 100644 index 2f68f59c7d..0000000000 --- a/packages/asset-swapper/contracts/src/CompoundSampler.sol +++ /dev/null @@ -1,96 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -/* - - Copyright 2021 ZeroEx Intl. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -pragma solidity ^0.6; -pragma experimental ABIEncoderV2; - -import "./SamplerUtils.sol"; -import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol"; - -// Minimal CToken interface -interface ICToken { - function mint(uint mintAmount) external returns (uint); - function redeem(uint redeemTokens) external returns (uint); - function redeemUnderlying(uint redeemAmount) external returns (uint); - function exchangeRateStored() external view returns (uint); - function decimals() external view returns (uint8); -} - -contract CompoundSampler is SamplerUtils { - uint256 constant private EXCHANGE_RATE_SCALE = 1e10; - - function sampleSellsFromCompound( - ICToken cToken, - IERC20TokenV06 takerToken, - IERC20TokenV06 makerToken, - uint256[] memory takerTokenAmounts - ) - public - view - returns (uint256[] memory makerTokenAmounts) - { - uint256 numSamples = takerTokenAmounts.length; - makerTokenAmounts = new uint256[](numSamples); - // Exchange rate is scaled by 1 * 10^(18 - 8 + Underlying Token Decimals - uint256 exchangeRate = cToken.exchangeRateStored(); - uint256 cTokenDecimals = uint256(cToken.decimals()); - - if (address(makerToken) == address(cToken)) { - // mint - for (uint256 i = 0; i < numSamples; i++) { - makerTokenAmounts[i] = (takerTokenAmounts[i] * EXCHANGE_RATE_SCALE * 10 ** cTokenDecimals) / exchangeRate; - } - - } else if (address(takerToken) == address(cToken)) { - // redeem - for (uint256 i = 0; i < numSamples; i++) { - makerTokenAmounts[i] = (takerTokenAmounts[i] * exchangeRate) / (EXCHANGE_RATE_SCALE * 10 ** cTokenDecimals); - } - } - } - - function sampleBuysFromCompound( - ICToken cToken, - IERC20TokenV06 takerToken, - IERC20TokenV06 makerToken, - uint256[] memory makerTokenAmounts - ) - public - view - returns (uint256[] memory takerTokenAmounts) - { - uint256 numSamples = makerTokenAmounts.length; - takerTokenAmounts = new uint256[](numSamples); - // Exchange rate is scaled by 1 * 10^(18 - 8 + Underlying Token Decimals - uint256 exchangeRate = cToken.exchangeRateStored(); - uint256 cTokenDecimals = uint256(cToken.decimals()); - - if (address(makerToken) == address(cToken)) { - // mint - for (uint256 i = 0; i < numSamples; i++) { - takerTokenAmounts[i] = makerTokenAmounts[i] * exchangeRate / (EXCHANGE_RATE_SCALE * 10 ** cTokenDecimals); - } - } else if (address(takerToken) == address(cToken)) { - // redeem - for (uint256 i = 0; i < numSamples; i++) { - takerTokenAmounts[i] = (makerTokenAmounts[i] * EXCHANGE_RATE_SCALE * 10 ** cTokenDecimals)/exchangeRate; - } - } - } -} diff --git a/packages/asset-swapper/contracts/src/CurveSampler.sol b/packages/asset-swapper/contracts/src/CurveSampler.sol deleted file mode 100644 index ce2e5ae7c6..0000000000 --- a/packages/asset-swapper/contracts/src/CurveSampler.sol +++ /dev/null @@ -1,161 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -/* - - Copyright 2020 ZeroEx Intl. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -pragma solidity ^0.6; -pragma experimental ABIEncoderV2; - -import "./interfaces/ICurve.sol"; -import "./ApproximateBuys.sol"; -import "./SamplerUtils.sol"; - - -contract CurveSampler is - SamplerUtils, - ApproximateBuys -{ - /// @dev Information for sampling from curve sources. - struct CurveInfo { - address poolAddress; - bytes4 sellQuoteFunctionSelector; - bytes4 buyQuoteFunctionSelector; - } - - /// @dev Base gas limit for Curve calls. Some Curves have multiple tokens - /// So a reasonable ceil is 150k per token. Biggest Curve has 4 tokens. - uint256 constant private CURVE_CALL_GAS = 2000e3; // Was 600k for Curve but SnowSwap is using 1500k+ - - /// @dev Sample sell quotes from Curve. - /// @param curveInfo Curve information specific to this token pair. - /// @param fromTokenIdx Index of the taker token (what to sell). - /// @param toTokenIdx Index of the maker token (what to buy). - /// @param takerTokenAmounts Taker token sell amount for each sample. - /// @return makerTokenAmounts Maker amounts bought at each taker token - /// amount. - function sampleSellsFromCurve( - CurveInfo memory curveInfo, - int128 fromTokenIdx, - int128 toTokenIdx, - uint256[] memory takerTokenAmounts - ) - public - view - returns (uint256[] memory makerTokenAmounts) - { - uint256 numSamples = takerTokenAmounts.length; - makerTokenAmounts = new uint256[](numSamples); - for (uint256 i = 0; i < numSamples; i++) { - (bool didSucceed, bytes memory resultData) = - curveInfo.poolAddress.staticcall.gas(CURVE_CALL_GAS)( - abi.encodeWithSelector( - curveInfo.sellQuoteFunctionSelector, - fromTokenIdx, - toTokenIdx, - takerTokenAmounts[i] - )); - uint256 buyAmount = 0; - if (didSucceed) { - buyAmount = abi.decode(resultData, (uint256)); - } - makerTokenAmounts[i] = buyAmount; - // Break early if there are 0 amounts - if (makerTokenAmounts[i] == 0) { - break; - } - } - } - - /// @dev Sample buy quotes from Curve. - /// @param curveInfo Curve information specific to this token pair. - /// @param fromTokenIdx Index of the taker token (what to sell). - /// @param toTokenIdx Index of the maker token (what to buy). - /// @param makerTokenAmounts Maker token buy amount for each sample. - /// @return takerTokenAmounts Taker amounts sold at each maker token - /// amount. - function sampleBuysFromCurve( - CurveInfo memory curveInfo, - int128 fromTokenIdx, - int128 toTokenIdx, - uint256[] memory makerTokenAmounts - ) - public - view - returns (uint256[] memory takerTokenAmounts) - { - if (curveInfo.buyQuoteFunctionSelector == bytes4(0)) { - // Buys not supported on this curve, so approximate it. - return _sampleApproximateBuys( - ApproximateBuyQuoteOpts({ - makerTokenData: abi.encode(toTokenIdx, curveInfo), - takerTokenData: abi.encode(fromTokenIdx, curveInfo), - getSellQuoteCallback: _sampleSellForApproximateBuyFromCurve - }), - makerTokenAmounts - ); - } - uint256 numSamples = makerTokenAmounts.length; - takerTokenAmounts = new uint256[](numSamples); - for (uint256 i = 0; i < numSamples; i++) { - (bool didSucceed, bytes memory resultData) = - curveInfo.poolAddress.staticcall.gas(CURVE_CALL_GAS)( - abi.encodeWithSelector( - curveInfo.buyQuoteFunctionSelector, - fromTokenIdx, - toTokenIdx, - makerTokenAmounts[i] - )); - uint256 sellAmount = 0; - if (didSucceed) { - sellAmount = abi.decode(resultData, (uint256)); - } - takerTokenAmounts[i] = sellAmount; - // Break early if there are 0 amounts - if (takerTokenAmounts[i] == 0) { - break; - } - } - } - - function _sampleSellForApproximateBuyFromCurve( - bytes memory takerTokenData, - bytes memory makerTokenData, - uint256 sellAmount - ) - private - view - returns (uint256 buyAmount) - { - (int128 takerTokenIdx, CurveInfo memory curveInfo) = - abi.decode(takerTokenData, (int128, CurveInfo)); - (int128 makerTokenIdx) = - abi.decode(makerTokenData, (int128)); - (bool success, bytes memory resultData) = - address(this).staticcall(abi.encodeWithSelector( - this.sampleSellsFromCurve.selector, - curveInfo, - takerTokenIdx, - makerTokenIdx, - _toSingleValueArray(sellAmount) - )); - if (!success) { - return 0; - } - - return abi.decode(resultData, (uint256[]))[0]; - } -} diff --git a/packages/asset-swapper/contracts/src/DODOSampler.sol b/packages/asset-swapper/contracts/src/DODOSampler.sol deleted file mode 100644 index 49e8ecf66b..0000000000 --- a/packages/asset-swapper/contracts/src/DODOSampler.sol +++ /dev/null @@ -1,211 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -/* - - Copyright 2020 ZeroEx Intl. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -pragma solidity ^0.6; -pragma experimental ABIEncoderV2; - -import "./ApproximateBuys.sol"; -import "./SamplerUtils.sol"; - - -interface IDODOZoo { - function getDODO(address baseToken, address quoteToken) external view returns (address); -} - -interface IDODOHelper { - function querySellQuoteToken(address dodo, uint256 amount) external view returns (uint256); -} - -interface IDODO { - function querySellBaseToken(uint256 amount) external view returns (uint256); - function _TRADE_ALLOWED_() external view returns (bool); -} - -contract DODOSampler is - SamplerUtils, - ApproximateBuys -{ - - /// @dev Gas limit for DODO calls. - uint256 constant private DODO_CALL_GAS = 300e3; // 300k - struct DODOSamplerOpts { - address registry; - address helper; - } - - /// @dev Sample sell quotes from DODO. - /// @param opts DODOSamplerOpts DODO Registry and helper addresses - /// @param takerToken Address of the taker token (what to sell). - /// @param makerToken Address of the maker token (what to buy). - /// @param takerTokenAmounts Taker token sell amount for each sample. - /// @return sellBase whether the bridge needs to sell the base token - /// @return pool the DODO pool address - /// @return makerTokenAmounts Maker amounts bought at each taker token - /// amount. - function sampleSellsFromDODO( - DODOSamplerOpts memory opts, - address takerToken, - address makerToken, - uint256[] memory takerTokenAmounts - ) - public - view - returns (bool sellBase, address pool, uint256[] memory makerTokenAmounts) - { - _assertValidPair(makerToken, takerToken); - uint256 numSamples = takerTokenAmounts.length; - makerTokenAmounts = new uint256[](numSamples); - - pool = IDODOZoo(opts.registry).getDODO(takerToken, makerToken); - address baseToken; - // If pool exists we have the correct order of Base/Quote - if (pool != address(0)) { - baseToken = takerToken; - sellBase = true; - } else { - pool = IDODOZoo(opts.registry).getDODO(makerToken, takerToken); - // No pool either direction - if (address(pool) == address(0)) { - return (sellBase, pool, makerTokenAmounts); - } - baseToken = makerToken; - sellBase = false; - } - - // DODO Pool has been disabled - if (!IDODO(pool)._TRADE_ALLOWED_()) { - return (sellBase, pool, makerTokenAmounts); - } - - for (uint256 i = 0; i < numSamples; i++) { - uint256 buyAmount = _sampleSellForApproximateBuyFromDODO( - abi.encode(takerToken, pool, baseToken, opts.helper), // taker token data - abi.encode(makerToken, pool, baseToken, opts.helper), // maker token data - takerTokenAmounts[i] - ); - makerTokenAmounts[i] = buyAmount; - // Break early if there are 0 amounts - if (makerTokenAmounts[i] == 0) { - break; - } - } - } - - /// @dev Sample buy quotes from DODO. - /// @param opts DODOSamplerOpts DODO Registry and helper addresses - /// @param takerToken Address of the taker token (what to sell). - /// @param makerToken Address of the maker token (what to buy). - /// @param makerTokenAmounts Maker token sell amount for each sample. - /// @return sellBase whether the bridge needs to sell the base token - /// @return pool the DODO pool address - /// @return takerTokenAmounts Taker amounts sold at each maker token - /// amount. - function sampleBuysFromDODO( - DODOSamplerOpts memory opts, - address takerToken, - address makerToken, - uint256[] memory makerTokenAmounts - ) - public - view - returns (bool sellBase, address pool, uint256[] memory takerTokenAmounts) - { - _assertValidPair(makerToken, takerToken); - uint256 numSamples = makerTokenAmounts.length; - takerTokenAmounts = new uint256[](numSamples); - - // Pool is BASE/QUOTE - // Look up the pool from the taker/maker combination - pool = IDODOZoo(opts.registry).getDODO(takerToken, makerToken); - address baseToken; - // If pool exists we have the correct order of Base/Quote - if (pool != address(0)) { - baseToken = takerToken; - sellBase = true; - } else { - // Look up the pool from the maker/taker combination - pool = IDODOZoo(opts.registry).getDODO(makerToken, takerToken); - // No pool either direction - if (address(pool) == address(0)) { - return (sellBase, pool, takerTokenAmounts); - } - baseToken = makerToken; - sellBase = false; - } - - // DODO Pool has been disabled - if (!IDODO(pool)._TRADE_ALLOWED_()) { - return (sellBase, pool, takerTokenAmounts); - } - - takerTokenAmounts = _sampleApproximateBuys( - ApproximateBuyQuoteOpts({ - makerTokenData: abi.encode(makerToken, pool, baseToken, opts.helper), - takerTokenData: abi.encode(takerToken, pool, baseToken, opts.helper), - getSellQuoteCallback: _sampleSellForApproximateBuyFromDODO - }), - makerTokenAmounts - ); - } - - function _sampleSellForApproximateBuyFromDODO( - bytes memory takerTokenData, - bytes memory /* makerTokenData */, - uint256 sellAmount - ) - private - view - returns (uint256) - { - (address takerToken, address pool, address baseToken, address helper) = abi.decode( - takerTokenData, - (address, address, address, address) - ); - - // We will get called to sell both the taker token and also to sell the maker token - if (takerToken == baseToken) { - // If base token then use the original query on the pool - try - IDODO(pool).querySellBaseToken - {gas: DODO_CALL_GAS} - (sellAmount) - returns (uint256 amount) - { - return amount; - } catch (bytes memory) { - // Swallow failures, leaving all results as zero. - return 0; - } - } else { - // If quote token then use helper, this is less accurate - try - IDODOHelper(helper).querySellQuoteToken - {gas: DODO_CALL_GAS} - (pool, sellAmount) - returns (uint256 amount) - { - return amount; - } catch (bytes memory) { - // Swallow failures, leaving all results as zero. - return 0; - } - } - } - -} diff --git a/packages/asset-swapper/contracts/src/DODOV2Sampler.sol b/packages/asset-swapper/contracts/src/DODOV2Sampler.sol deleted file mode 100644 index 6b99135cc9..0000000000 --- a/packages/asset-swapper/contracts/src/DODOV2Sampler.sol +++ /dev/null @@ -1,204 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -/* - - Copyright 2021 ZeroEx Intl. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -pragma solidity ^0.6; -pragma experimental ABIEncoderV2; - -import "./ApproximateBuys.sol"; -import "./SamplerUtils.sol"; - -interface IDODOV2Registry { - function getDODOPool(address baseToken, address quoteToken) - external - view - returns (address[] memory machines); -} - -interface IDODOV2Pool { - function querySellBase(address trader, uint256 payBaseAmount) - external - view - returns (uint256 receiveQuoteAmount, uint256 mtFee); - - function querySellQuote(address trader, uint256 payQuoteAmount) - external - view - returns (uint256 receiveBaseAmount, uint256 mtFee); -} - -contract DODOV2Sampler is - SamplerUtils, - ApproximateBuys -{ - - /// @dev Gas limit for DODO V2 calls. - uint256 constant private DODO_V2_CALL_GAS = 300e3; // 300k - - /// @dev Sample sell quotes from DODO V2. - /// @param registry Address of the registry to look up. - /// @param offset offset index for the pool in the registry. - /// @param takerToken Address of the taker token (what to sell). - /// @param makerToken Address of the maker token (what to buy). - /// @param takerTokenAmounts Taker token sell amount for each sample. - /// @return sellBase whether the bridge needs to sell the base token - /// @return pool the DODO pool address - /// @return makerTokenAmounts Maker amounts bought at each taker token - /// amount. - function sampleSellsFromDODOV2( - address registry, - uint256 offset, - address takerToken, - address makerToken, - uint256[] memory takerTokenAmounts - ) - public - view - returns (bool sellBase, address pool, uint256[] memory makerTokenAmounts) - { - _assertValidPair(makerToken, takerToken); - - uint256 numSamples = takerTokenAmounts.length; - makerTokenAmounts = new uint256[](numSamples); - - (pool, sellBase) = _getNextDODOV2Pool(registry, offset, takerToken, makerToken); - if (pool == address(0)) { - return (sellBase, pool, makerTokenAmounts); - } - - for (uint256 i = 0; i < numSamples; i++) { - uint256 buyAmount = _sampleSellForApproximateBuyFromDODOV2( - abi.encode(takerToken, pool, sellBase), // taker token data - abi.encode(makerToken, pool, sellBase), // maker token data - takerTokenAmounts[i] - ); - makerTokenAmounts[i] = buyAmount; - // Break early if there are 0 amounts - if (makerTokenAmounts[i] == 0) { - break; - } - } - } - - /// @dev Sample buy quotes from DODO. - /// @param registry Address of the registry to look up. - /// @param offset offset index for the pool in the registry. - /// @param takerToken Address of the taker token (what to sell). - /// @param makerToken Address of the maker token (what to buy). - /// @param makerTokenAmounts Maker token sell amount for each sample. - /// @return sellBase whether the bridge needs to sell the base token - /// @return pool the DODO pool address - /// @return takerTokenAmounts Taker amounts sold at each maker token - /// amount. - function sampleBuysFromDODOV2( - address registry, - uint256 offset, - address takerToken, - address makerToken, - uint256[] memory makerTokenAmounts - ) - public - view - returns (bool sellBase, address pool, uint256[] memory takerTokenAmounts) - { - _assertValidPair(makerToken, takerToken); - (pool, sellBase) = _getNextDODOV2Pool(registry, offset, takerToken, makerToken); - if (pool == address(0)) { - return (sellBase, pool, takerTokenAmounts); - } - uint256 numSamples = makerTokenAmounts.length; - takerTokenAmounts = new uint256[](numSamples); - - takerTokenAmounts = _sampleApproximateBuys( - ApproximateBuyQuoteOpts({ - makerTokenData: abi.encode(makerToken, pool, !sellBase), - takerTokenData: abi.encode(takerToken, pool, sellBase), - getSellQuoteCallback: _sampleSellForApproximateBuyFromDODOV2 - }), - makerTokenAmounts - ); - } - - function _sampleSellForApproximateBuyFromDODOV2( - bytes memory takerTokenData, - bytes memory /* makerTokenData */, - uint256 sellAmount - ) - private - view - returns (uint256) - { - (address takerToken, address pool, bool sellBase) = abi.decode( - takerTokenData, - (address, address, bool) - ); - - // We will get called to sell both the taker token and also to sell the maker token - // since we use approximate buy for sell and buy functions - if (sellBase) { - try - IDODOV2Pool(pool).querySellBase - { gas: DODO_V2_CALL_GAS } - (address(0), sellAmount) - returns (uint256 amount, uint256) - { - return amount; - } catch { - return 0; - } - } else { - try - IDODOV2Pool(pool).querySellQuote - { gas: DODO_V2_CALL_GAS } - (address(0), sellAmount) - returns (uint256 amount, uint256) - { - return amount; - } catch { - return 0; - } - } - } - - function _getNextDODOV2Pool( - address registry, - uint256 offset, - address takerToken, - address makerToken - ) - internal - view - returns (address machine, bool sellBase) - { - // Query in base -> quote direction, if a pool is found then we are selling the base - address[] memory machines = IDODOV2Registry(registry).getDODOPool(takerToken, makerToken); - sellBase = true; - if (machines.length == 0) { - // Query in quote -> base direction, if a pool is found then we are selling the quote - machines = IDODOV2Registry(registry).getDODOPool(makerToken, takerToken); - sellBase = false; - } - - if (offset >= machines.length) { - return (address(0), false); - } - - machine = machines[offset]; - } - -} diff --git a/packages/asset-swapper/contracts/src/ERC20BridgeSampler.sol b/packages/asset-swapper/contracts/src/ERC20BridgeSampler.sol deleted file mode 100644 index 01195bfbe1..0000000000 --- a/packages/asset-swapper/contracts/src/ERC20BridgeSampler.sol +++ /dev/null @@ -1,105 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -/* - - Copyright 2020 ZeroEx Intl. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -pragma solidity ^0.6; -pragma experimental ABIEncoderV2; - -import "./BalancerSampler.sol"; -import "./BalancerV2Sampler.sol"; -import "./BalancerV2BatchSampler.sol"; -import "./BancorSampler.sol"; -import "./BancorV3Sampler.sol"; -import "./CompoundSampler.sol"; -import "./CurveSampler.sol"; -import "./DODOSampler.sol"; -import "./DODOV2Sampler.sol"; -import "./GMXSampler.sol"; -import "./KyberDmmSampler.sol"; -import "./LidoSampler.sol"; -import "./LiquidityProviderSampler.sol"; -import "./MakerPSMSampler.sol"; -import "./MStableSampler.sol"; -import "./MooniswapSampler.sol"; -import "./NativeOrderSampler.sol"; -import "./PlatypusSampler.sol"; -import "./ShellSampler.sol"; -import "./SynthetixSampler.sol"; -import "./TwoHopSampler.sol"; -import "./UniswapSampler.sol"; -import "./UniswapV2Sampler.sol"; -import "./UniswapV3Sampler.sol"; -import "./VelodromeSampler.sol"; -import "./WooPPSampler.sol"; -import "./UtilitySampler.sol"; - - -contract ERC20BridgeSampler is - BalancerSampler, - BalancerV2Sampler, - BalancerV2BatchSampler, - BancorSampler, - BancorV3Sampler, - CompoundSampler, - CurveSampler, - DODOSampler, - DODOV2Sampler, - GMXSampler, - KyberDmmSampler, - LidoSampler, - LiquidityProviderSampler, - MakerPSMSampler, - MStableSampler, - MooniswapSampler, - NativeOrderSampler, - PlatypusSampler, - ShellSampler, - SynthetixSampler, - TwoHopSampler, - UniswapSampler, - UniswapV2Sampler, - UniswapV3Sampler, - VelodromeSampler, - WooPPSampler, - UtilitySampler -{ - - struct CallResults { - bytes data; - bool success; - } - - /// @dev Call multiple public functions on this contract in a single transaction. - /// @param callDatas ABI-encoded call data for each function call. - /// @return callResults ABI-encoded results data for each call. - function batchCall(bytes[] calldata callDatas) - external - returns (CallResults[] memory callResults) - { - callResults = new CallResults[](callDatas.length); - for (uint256 i = 0; i != callDatas.length; ++i) { - callResults[i].success = true; - if (callDatas[i].length == 0) { - continue; - } - (callResults[i].success, callResults[i].data) = address(this).call(callDatas[i]); - } - } - - receive() external payable {} -} diff --git a/packages/asset-swapper/contracts/src/FakeTaker.sol b/packages/asset-swapper/contracts/src/FakeTaker.sol deleted file mode 100644 index 086c5a1590..0000000000 --- a/packages/asset-swapper/contracts/src/FakeTaker.sol +++ /dev/null @@ -1,27 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.6; -pragma experimental ABIEncoderV2; - -contract FakeTaker { - - struct Result { - bool success; - bytes resultData; - uint256 gasUsed; - } - - receive() payable external {} - - function execute(address payable to, bytes calldata data) - public - payable - returns (Result memory result) - { - uint256 gasBefore = gasleft(); - ( - result.success, - result.resultData - ) = to.call{ value: msg.value }(data); - result.gasUsed = gasBefore - gasleft(); - } -} diff --git a/packages/asset-swapper/contracts/src/GMXSampler.sol b/packages/asset-swapper/contracts/src/GMXSampler.sol deleted file mode 100644 index 89a6180211..0000000000 --- a/packages/asset-swapper/contracts/src/GMXSampler.sol +++ /dev/null @@ -1,96 +0,0 @@ -pragma solidity ^0.6; -pragma experimental ABIEncoderV2; - -import "./interfaces/IGMX.sol"; -import "./ApproximateBuys.sol"; -import "./SamplerUtils.sol"; - -contract GMXSampler is - SamplerUtils, - ApproximateBuys -{ - struct GMXInfo { - address reader; - address vault; - address[] path; - } - - function sampleSellsFromGMX( - address reader, - address vault, - address[] memory path, - uint256[] memory takerTokenAmounts - ) - public - view - returns (uint256[] memory makerTokenAmounts) - { - uint256 numSamples = takerTokenAmounts.length; - makerTokenAmounts = new uint256[](numSamples); - for (uint256 i = 0; i < numSamples; i++) { - try - IGMX(reader).getAmountOut(IVault(vault), path[0], path[1], takerTokenAmounts[i]) - returns (uint256 amountAfterFees, uint256 feeAmount) - { - makerTokenAmounts[i] = amountAfterFees; - // Break early if there are 0 amounts - if (makerTokenAmounts[i] == 0) { - break; - } - } catch (bytes memory) { - // Swallow failures, leaving all results as zero. - break; - } - } - } - - function sampleBuysFromGMX( - address reader, - address vault, - address[] memory path, - uint256[] memory makerTokenAmounts - ) - public - view - returns (uint256[] memory takerTokenAmounts) - { - address[] memory invertBuyPath = new address[](2); - invertBuyPath[0] = path[1]; - invertBuyPath[1] = path[0]; - return _sampleApproximateBuys( - ApproximateBuyQuoteOpts({ - makerTokenData: abi.encode(reader, vault, invertBuyPath), - takerTokenData: abi.encode(reader, vault, path), - getSellQuoteCallback: _sampleSellForApproximateBuyFromGMX - }), - makerTokenAmounts - ); - } - - - function _sampleSellForApproximateBuyFromGMX( - bytes memory takerTokenData, - bytes memory makerTokenData, - uint256 sellAmount - ) - private - view - returns (uint256 buyAmount) - { - (address _reader, address _vault, address[] memory _path ) = abi.decode(takerTokenData, (address, address, address[])); - - (bool success, bytes memory resultData) = address(this).staticcall(abi.encodeWithSelector( - this.sampleSellsFromGMX.selector, - _reader, - _vault, - _path, - _toSingleValueArray(sellAmount) - )); - if(!success) { - return 0; - } - - return abi.decode(resultData, (uint256[]))[0]; - } - -} diff --git a/packages/asset-swapper/contracts/src/KyberDmmSampler.sol b/packages/asset-swapper/contracts/src/KyberDmmSampler.sol deleted file mode 100644 index 29ea8c01b3..0000000000 --- a/packages/asset-swapper/contracts/src/KyberDmmSampler.sol +++ /dev/null @@ -1,179 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -/* - - Copyright 2020 ZeroEx Intl. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -pragma solidity ^0.6; -pragma experimental ABIEncoderV2; - -interface IKyberDmmPool { - - function totalSupply() - external - view - returns (uint256); -} - -interface IKyberDmmFactory { - - function getPools(address token0, address token1) - external - view - returns (address[] memory _tokenPools); -} - -interface IKyberDmmRouter { - - function factory() external view returns (address); - - function getAmountsOut(uint256 amountIn, address[] calldata pools, address[] calldata path) - external - view - returns (uint256[] memory amounts); - - function getAmountsIn(uint256 amountOut, address[] calldata pools, address[] calldata path) - external - view - returns (uint256[] memory amounts); -} - - - -contract KyberDmmSampler -{ - /// @dev Gas limit for KyberDmm calls. - uint256 constant private KYBER_DMM_CALL_GAS = 150e3; // 150k - - /// @dev Sample sell quotes from KyberDmm. - /// @param router Router to look up tokens and amounts - /// @param path Token route. Should be takerToken -> makerToken - /// @param takerTokenAmounts Taker token sell amount for each sample. - /// @return pools The pool addresses involved in the multi path trade - /// @return makerTokenAmounts Maker amounts bought at each taker token - /// amount. - function sampleSellsFromKyberDmm( - address router, - address[] memory path, - uint256[] memory takerTokenAmounts - ) - public - view - returns (address[] memory pools, uint256[] memory makerTokenAmounts) - { - uint256 numSamples = takerTokenAmounts.length; - makerTokenAmounts = new uint256[](numSamples); - pools = _getKyberDmmPools(router, path); - if (pools.length == 0) { - return (pools, makerTokenAmounts); - } - for (uint256 i = 0; i < numSamples; i++) { - try - IKyberDmmRouter(router).getAmountsOut - {gas: KYBER_DMM_CALL_GAS} - (takerTokenAmounts[i], pools, path) - returns (uint256[] memory amounts) - { - makerTokenAmounts[i] = amounts[path.length - 1]; - // Break early if there are 0 amounts - if (makerTokenAmounts[i] == 0) { - break; - } - } catch (bytes memory) { - // Swallow failures, leaving all results as zero. - break; - } - } - } - - /// @dev Sample buy quotes from KyberDmm. - /// @param router Router to look up tokens and amounts - /// @param path Token route. Should be takerToken -> makerToken. - /// @param makerTokenAmounts Maker token buy amount for each sample. - /// @return pools The pool addresses involved in the multi path trade - /// @return takerTokenAmounts Taker amounts sold at each maker token - /// amount. - function sampleBuysFromKyberDmm( - address router, - address[] memory path, - uint256[] memory makerTokenAmounts - ) - public - view - returns (address[] memory pools, uint256[] memory takerTokenAmounts) - { - uint256 numSamples = makerTokenAmounts.length; - takerTokenAmounts = new uint256[](numSamples); - pools = _getKyberDmmPools(router, path); - if (pools.length == 0) { - return (pools, takerTokenAmounts); - } - for (uint256 i = 0; i < numSamples; i++) { - try - IKyberDmmRouter(router).getAmountsIn - {gas: KYBER_DMM_CALL_GAS} - (makerTokenAmounts[i], pools, path) - returns (uint256[] memory amounts) - { - takerTokenAmounts[i] = amounts[0]; - // Break early if there are 0 amounts - if (takerTokenAmounts[i] == 0) { - break; - } - } catch (bytes memory) { - // Swallow failures, leaving all results as zero. - break; - } - } - } - - function _getKyberDmmPools( - address router, - address[] memory path - ) - private - view - returns (address[] memory pools) - { - IKyberDmmFactory factory = IKyberDmmFactory(IKyberDmmRouter(router).factory()); - pools = new address[](path.length - 1); - for (uint256 i = 0; i < pools.length; i++) { - // find the best pool - address[] memory allPools; - try - factory.getPools - {gas: KYBER_DMM_CALL_GAS} - (path[i], path[i + 1]) - returns (address[] memory allPools) - { - if (allPools.length == 0) { - return new address[](0); - } - - uint256 maxSupply = 0; - for (uint256 j = 0; j < allPools.length; j++) { - uint256 totalSupply = IKyberDmmPool(allPools[j]).totalSupply(); - if (totalSupply > maxSupply) { - maxSupply = totalSupply; - pools[i] = allPools[j]; - } - } - } catch (bytes memory) { - return new address[](0); - } - } - } -} diff --git a/packages/asset-swapper/contracts/src/LidoSampler.sol b/packages/asset-swapper/contracts/src/LidoSampler.sol deleted file mode 100644 index 27e23f703a..0000000000 --- a/packages/asset-swapper/contracts/src/LidoSampler.sol +++ /dev/null @@ -1,119 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -/* - - Copyright 2021 ZeroEx Intl. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -pragma solidity ^0.6; -pragma experimental ABIEncoderV2; - -import "./SamplerUtils.sol"; - - -interface IWstETH { - function getWstETHByStETH(uint256 _stETHAmount) external view returns (uint256); - function getStETHByWstETH(uint256 _wstETHAmount) external view returns (uint256); -} - - -contract LidoSampler is SamplerUtils { - struct LidoInfo { - address stEthToken; - address wethToken; - address wstEthToken; - } - - /// @dev Sample sell quotes from Lido - /// @param lidoInfo Info regarding a specific Lido deployment - /// @param takerToken Address of the taker token (what to sell). - /// @param makerToken Address of the maker token (what to buy). - /// @param takerTokenAmounts Taker token sell amount for each sample. - /// @return makerTokenAmounts Maker amounts bought at each taker token - /// amount. - function sampleSellsFromLido( - LidoInfo memory lidoInfo, - address takerToken, - address makerToken, - uint256[] memory takerTokenAmounts - ) - public - view - returns (uint256[] memory) - { - _assertValidPair(makerToken, takerToken); - - if (takerToken == lidoInfo.wethToken && makerToken == address(lidoInfo.stEthToken)) { - // Minting stETH is always 1:1 therefore we can just return the same amounts back. - return takerTokenAmounts; - } - - return _sampleSellsForWrapped(lidoInfo, takerToken, makerToken, takerTokenAmounts); - } - - /// @dev Sample buy quotes from Lido. - /// @param lidoInfo Info regarding a specific Lido deployment - /// @param takerToken Address of the taker token (what to sell). - /// @param makerToken Address of the maker token (what to buy). - /// @param makerTokenAmounts Maker token buy amount for each sample. - /// @return takerTokenAmounts Taker amounts sold at each maker token - /// amount. - function sampleBuysFromLido( - LidoInfo memory lidoInfo, - address takerToken, - address makerToken, - uint256[] memory makerTokenAmounts - ) - public - view - returns (uint256[] memory) - { - if (takerToken == lidoInfo.wethToken && makerToken == address(lidoInfo.stEthToken)) { - // Minting stETH is always 1:1 therefore we can just return the same amounts back. - return makerTokenAmounts; - } - - // Swap out `makerToken` and `takerToken` and re-use `_sampleSellsForWrapped`. - return _sampleSellsForWrapped(lidoInfo, makerToken, takerToken, makerTokenAmounts); - } - - function _sampleSellsForWrapped( - LidoInfo memory lidoInfo, - address takerToken, - address makerToken, - uint256[] memory takerTokenAmounts - ) private view returns (uint256[] memory) { - IWstETH wstETH = IWstETH(lidoInfo.wstEthToken); - uint256 numSamples = takerTokenAmounts.length; - uint256[] memory makerTokenAmounts = new uint256[](numSamples); - - if (takerToken == lidoInfo.stEthToken && makerToken == lidoInfo.wstEthToken) { - for (uint256 i = 0; i < numSamples; i++) { - makerTokenAmounts[i] = wstETH.getWstETHByStETH(takerTokenAmounts[i]); - } - return makerTokenAmounts; - } - - if (takerToken == lidoInfo.wstEthToken && makerToken == lidoInfo.stEthToken) { - for (uint256 i = 0; i < numSamples; i++) { - makerTokenAmounts[i] = wstETH.getStETHByWstETH(takerTokenAmounts[i]); - } - return makerTokenAmounts; - } - - // Returns 0 values. - return makerTokenAmounts; - } -} diff --git a/packages/asset-swapper/contracts/src/LiquidityProviderSampler.sol b/packages/asset-swapper/contracts/src/LiquidityProviderSampler.sol deleted file mode 100644 index a5c871c6e9..0000000000 --- a/packages/asset-swapper/contracts/src/LiquidityProviderSampler.sol +++ /dev/null @@ -1,132 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -/* - - Copyright 2020 ZeroEx Intl. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -pragma solidity ^0.6; -pragma experimental ABIEncoderV2; - -import "@0x/contracts-utils/contracts/src/v06/LibBytesV06.sol"; -import "@0x/contracts-zero-ex/contracts/src/vendor/ILiquidityProvider.sol"; -import "./ApproximateBuys.sol"; -import "./SamplerUtils.sol"; - - -contract LiquidityProviderSampler is - SamplerUtils, - ApproximateBuys -{ - /// @dev Default gas limit for liquidity provider calls. - uint256 constant private DEFAULT_CALL_GAS = 400e3; // 400k - - /// @dev Sample sell quotes from an arbitrary on-chain liquidity provider. - /// @param providerAddress Address of the liquidity provider. - /// @param takerToken Address of the taker token (what to sell). - /// @param makerToken Address of the maker token (what to buy). - /// @param takerTokenAmounts Taker token sell amount for each sample. - /// @return makerTokenAmounts Maker amounts bought at each taker token - /// amount. - function sampleSellsFromLiquidityProvider( - address providerAddress, - address takerToken, - address makerToken, - uint256[] memory takerTokenAmounts - ) - public - view - returns (uint256[] memory makerTokenAmounts) - { - // Initialize array of maker token amounts. - uint256 numSamples = takerTokenAmounts.length; - makerTokenAmounts = new uint256[](numSamples); - - for (uint256 i = 0; i < numSamples; i++) { - try - ILiquidityProvider(providerAddress).getSellQuote - {gas: DEFAULT_CALL_GAS} - ( - IERC20TokenV06(takerToken), - IERC20TokenV06(makerToken), - takerTokenAmounts[i] - ) - returns (uint256 amount) - { - makerTokenAmounts[i] = amount; - // Break early if there are 0 amounts - if (makerTokenAmounts[i] == 0) { - break; - } - } catch (bytes memory) { - // Swallow failures, leaving all results as zero. - break; - } - } - } - - /// @dev Sample buy quotes from an arbitrary on-chain liquidity provider. - /// @param providerAddress Address of the liquidity provider. - /// @param takerToken Address of the taker token (what to sell). - /// @param makerToken Address of the maker token (what to buy). - /// @param makerTokenAmounts Maker token buy amount for each sample. - /// @return takerTokenAmounts Taker amounts sold at each maker token - /// amount. - function sampleBuysFromLiquidityProvider( - address providerAddress, - address takerToken, - address makerToken, - uint256[] memory makerTokenAmounts - ) - public - view - returns (uint256[] memory takerTokenAmounts) - { - takerTokenAmounts = _sampleApproximateBuys( - ApproximateBuyQuoteOpts({ - makerTokenData: abi.encode(makerToken, providerAddress), - takerTokenData: abi.encode(takerToken, providerAddress), - getSellQuoteCallback: _sampleSellForApproximateBuyFromLiquidityProvider - }), - makerTokenAmounts - ); - } - - function _sampleSellForApproximateBuyFromLiquidityProvider( - bytes memory takerTokenData, - bytes memory makerTokenData, - uint256 sellAmount - ) - private - view - returns (uint256 buyAmount) - { - (address takerToken, address providerAddress) = - abi.decode(takerTokenData, (address, address)); - (address makerToken) = - abi.decode(makerTokenData, (address)); - try - this.sampleSellsFromLiquidityProvider - {gas: DEFAULT_CALL_GAS} - (providerAddress, takerToken, makerToken, _toSingleValueArray(sellAmount)) - returns (uint256[] memory amounts) - { - return amounts[0]; - } catch (bytes memory) { - // Swallow failures, leaving all results as zero. - return 0; - } - } -} diff --git a/packages/asset-swapper/contracts/src/MStableSampler.sol b/packages/asset-swapper/contracts/src/MStableSampler.sol deleted file mode 100644 index 0947bc3767..0000000000 --- a/packages/asset-swapper/contracts/src/MStableSampler.sol +++ /dev/null @@ -1,127 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -/* - - Copyright 2020 ZeroEx Intl. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -pragma solidity ^0.6; -pragma experimental ABIEncoderV2; - -import "./interfaces/IMStable.sol"; -import "./ApproximateBuys.sol"; -import "./SamplerUtils.sol"; - - -contract MStableSampler is - SamplerUtils, - ApproximateBuys -{ - /// @dev Default gas limit for mStable calls. - uint256 constant private DEFAULT_CALL_GAS = 800e3; // 800k - - /// @dev Sample sell quotes from the mStable contract - /// @param router Address of the mStable contract - /// @param takerToken Address of the taker token (what to sell). - /// @param makerToken Address of the maker token (what to buy). - /// @param takerTokenAmounts Taker token sell amount for each sample. - /// @return makerTokenAmounts Maker amounts bought at each taker token - /// amount. - function sampleSellsFromMStable( - address router, - address takerToken, - address makerToken, - uint256[] memory takerTokenAmounts - ) - public - view - returns (uint256[] memory makerTokenAmounts) - { - _assertValidPair(makerToken, takerToken); - // Initialize array of maker token amounts. - uint256 numSamples = takerTokenAmounts.length; - makerTokenAmounts = new uint256[](numSamples); - - for (uint256 i = 0; i < numSamples; i++) { - try - IMStable(router).getSwapOutput - {gas: DEFAULT_CALL_GAS} - (takerToken, makerToken, takerTokenAmounts[i]) - returns (uint256 amount) - { - makerTokenAmounts[i] = amount; - // Break early if there are 0 amounts - if (makerTokenAmounts[i] == 0) { - break; - } - } catch (bytes memory) { - // Swallow failures, leaving all results as zero. - break; - } - } - } - - /// @dev Sample buy quotes from MStable contract - /// @param router Address of the mStable contract - /// @param takerToken Address of the taker token (what to sell). - /// @param makerToken Address of the maker token (what to buy). - /// @param makerTokenAmounts Maker token buy amount for each sample. - /// @return takerTokenAmounts Taker amounts sold at each maker token - /// amount. - function sampleBuysFromMStable( - address router, - address takerToken, - address makerToken, - uint256[] memory makerTokenAmounts - ) - public - view - returns (uint256[] memory takerTokenAmounts) - { - return _sampleApproximateBuys( - ApproximateBuyQuoteOpts({ - makerTokenData: abi.encode(makerToken, router), - takerTokenData: abi.encode(takerToken, router), - getSellQuoteCallback: _sampleSellForApproximateBuyFromMStable - }), - makerTokenAmounts - ); - } - - function _sampleSellForApproximateBuyFromMStable( - bytes memory takerTokenData, - bytes memory makerTokenData, - uint256 sellAmount - ) - private - view - returns (uint256 buyAmount) - { - (address takerToken, address router) = - abi.decode(takerTokenData, (address, address)); - (address makerToken) = - abi.decode(makerTokenData, (address)); - try - this.sampleSellsFromMStable - (router, takerToken, makerToken, _toSingleValueArray(sellAmount)) - returns (uint256[] memory amounts) - { - return amounts[0]; - } catch (bytes memory) { - // Swallow failures, leaving all results as zero. - return 0; - } - } -} diff --git a/packages/asset-swapper/contracts/src/MakerPSMSampler.sol b/packages/asset-swapper/contracts/src/MakerPSMSampler.sol deleted file mode 100644 index 5adcf28716..0000000000 --- a/packages/asset-swapper/contracts/src/MakerPSMSampler.sol +++ /dev/null @@ -1,267 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -/* - - Copyright 2021 ZeroEx Intl. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -pragma solidity ^0.6; -pragma experimental ABIEncoderV2; - -import "./SamplerUtils.sol"; -import "@0x/contracts-utils/contracts/src/v06/LibMathV06.sol"; - -interface IPSM { - // @dev Get the fee for selling USDC to DAI in PSM - // @return tin toll in [wad] - function tin() external view returns (uint256); - // @dev Get the fee for selling DAI to USDC in PSM - // @return tout toll out [wad] - function tout() external view returns (uint256); - - // @dev Get the address of the PSM state Vat - // @return address of the Vat - function vat() external view returns (address); - - // @dev Get the address of the underlying vault powering PSM - // @return address of gemJoin contract - function gemJoin() external view returns (address); - - // @dev Get the address of DAI - // @return address of DAI contract - function dai() external view returns (address); - - // @dev Sell USDC for DAI - // @param usr The address of the account trading USDC for DAI. - // @param gemAmt The amount of USDC to sell in USDC base units - function sellGem( - address usr, - uint256 gemAmt - ) external; - // @dev Buy USDC for DAI - // @param usr The address of the account trading DAI for USDC - // @param gemAmt The amount of USDC to buy in USDC base units - function buyGem( - address usr, - uint256 gemAmt - ) external; -} - -interface IVAT { - // @dev Get a collateral type by identifier - // @param ilkIdentifier bytes32 identifier. Example: ethers.utils.formatBytes32String("PSM-USDC-A") - // @return ilk - // @return ilk.Art Total Normalised Debt in wad - // @return ilk.rate Accumulated Rates in ray - // @return ilk.spot Price with Safety Margin in ray - // @return ilk.line Debt Ceiling in rad - // @return ilk.dust Urn Debt Floor in rad - function ilks( - bytes32 ilkIdentifier - ) external view returns ( - uint256 Art, - uint256 rate, - uint256 spot, - uint256 line, - uint256 dust - ); -} - -contract MakerPSMSampler is - SamplerUtils -{ - using LibSafeMathV06 for uint256; - - /// @dev Information about which PSM module to use - struct MakerPsmInfo { - address psmAddress; - bytes32 ilkIdentifier; - address gemTokenAddress; - } - - /// @dev Gas limit for MakerPsm calls. - uint256 constant private MAKER_PSM_CALL_GAS = 300e3; // 300k - - - // Maker units - // wad: fixed point decimal with 18 decimals (for basic quantities, e.g. balances) - uint256 constant private WAD = 10 ** 18; - // ray: fixed point decimal with 27 decimals (for precise quantites, e.g. ratios) - uint256 constant private RAY = 10 ** 27; - // rad: fixed point decimal with 45 decimals (result of integer multiplication with a wad and a ray) - uint256 constant private RAD = 10 ** 45; - // See https://github.com/makerdao/dss/blob/master/DEVELOPING.m - - /// @dev Sample sell quotes from Maker PSM - function sampleSellsFromMakerPsm( - MakerPsmInfo memory psmInfo, - address takerToken, - address makerToken, - uint256[] memory takerTokenAmounts - ) - public - view - returns (uint256[] memory makerTokenAmounts) - { - _assertValidPair(makerToken, takerToken); - IPSM psm = IPSM(psmInfo.psmAddress); - IVAT vat = IVAT(psm.vat()); - - - uint256 numSamples = takerTokenAmounts.length; - makerTokenAmounts = new uint256[](numSamples); - - if (makerToken != psm.dai() && takerToken != psm.dai()) { - return makerTokenAmounts; - } - - for (uint256 i = 0; i < numSamples; i++) { - uint256 buyAmount = _samplePSMSell(psmInfo, makerToken, takerToken, takerTokenAmounts[i], psm, vat); - - if (buyAmount == 0) { - break; - } - makerTokenAmounts[i] = buyAmount; - } - } - - function sampleBuysFromMakerPsm( - MakerPsmInfo memory psmInfo, - address takerToken, - address makerToken, - uint256[] memory makerTokenAmounts - ) - public - view - returns (uint256[] memory takerTokenAmounts) - { - _assertValidPair(makerToken, takerToken); - IPSM psm = IPSM(psmInfo.psmAddress); - IVAT vat = IVAT(psm.vat()); - - uint256 numSamples = makerTokenAmounts.length; - takerTokenAmounts = new uint256[](numSamples); - if (makerToken != psm.dai() && takerToken != psm.dai()) { - return takerTokenAmounts; - } - - for (uint256 i = 0; i < numSamples; i++) { - uint256 sellAmount = _samplePSMBuy(psmInfo, makerToken, takerToken, makerTokenAmounts[i], psm, vat); - - if (sellAmount == 0) { - break; - } - - takerTokenAmounts[i] = sellAmount; - } - - } - - function _samplePSMSell(MakerPsmInfo memory psmInfo, address makerToken, address takerToken, uint256 takerTokenAmount, IPSM psm, IVAT vat) - private - view - returns (uint256) - { - (uint256 totalDebtInWad,,, uint256 debtCeilingInRad, uint256 debtFloorInRad) = vat.ilks(psmInfo.ilkIdentifier); - uint256 gemTokenBaseUnit = uint256(1e6); - - if (takerToken == psmInfo.gemTokenAddress) { - // Simulate sellGem - // Selling USDC to the PSM, increasing the total debt - // Convert USDC 6 decimals to 18 decimals [wad] - uint256 takerTokenAmountInWad = takerTokenAmount.safeMul(1e12); - - uint256 newTotalDebtInRad = totalDebtInWad.safeAdd(takerTokenAmountInWad).safeMul(RAY); - - // PSM is too full to fit - if (newTotalDebtInRad >= debtCeilingInRad) { - return 0; - } - - uint256 feeInWad = takerTokenAmountInWad.safeMul(psm.tin()).safeDiv(WAD); - uint256 makerTokenAmountInWad = takerTokenAmountInWad.safeSub(feeInWad); - - return makerTokenAmountInWad; - } else if (makerToken == psmInfo.gemTokenAddress) { - // Simulate buyGem - // Buying USDC from the PSM, decreasing the total debt - // Selling DAI for USDC, already in 18 decimals [wad] - uint256 takerTokenAmountInWad = takerTokenAmount; - if (takerTokenAmountInWad > totalDebtInWad) { - return 0; - } - uint256 newTotalDebtInRad = totalDebtInWad.safeSub(takerTokenAmountInWad).safeMul(RAY); - - // PSM is empty, not enough USDC to buy from it - if (newTotalDebtInRad <= debtFloorInRad) { - return 0; - } - - uint256 feeDivisorInWad = WAD.safeAdd(psm.tout()); // eg. 1.001 * 10 ** 18 with 0.1% tout; - uint256 makerTokenAmountInGemTokenBaseUnits = takerTokenAmountInWad.safeMul(gemTokenBaseUnit).safeDiv(feeDivisorInWad); - - return makerTokenAmountInGemTokenBaseUnits; - } - - return 0; - } - - function _samplePSMBuy(MakerPsmInfo memory psmInfo, address makerToken, address takerToken, uint256 makerTokenAmount, IPSM psm, IVAT vat) - private - view - returns (uint256) - { - (uint256 totalDebtInWad,,, uint256 debtCeilingInRad, uint256 debtFloorInRad) = vat.ilks(psmInfo.ilkIdentifier); - - if (takerToken == psmInfo.gemTokenAddress) { - // Simulate sellGem - // Selling USDC to the PSM, increasing the total debt - uint256 makerTokenAmountInWad = makerTokenAmount; - uint256 feeDivisorInWad = WAD.safeSub(psm.tin()); // eg. 0.999 * 10 ** 18 with 0.1% tin; - uint256 takerTokenAmountInWad = makerTokenAmountInWad.safeMul(WAD).safeDiv(feeDivisorInWad); - uint256 newTotalDebtInRad = totalDebtInWad.safeAdd(takerTokenAmountInWad).safeMul(RAY); - - // PSM is too full to fit - if (newTotalDebtInRad >= debtCeilingInRad) { - return 0; - } - - uint256 takerTokenAmountInGemInGemBaseUnits = (takerTokenAmountInWad.safeDiv(1e12)).safeAdd(1); // Add 1 to deal with cut off decimals converting to lower decimals - - return takerTokenAmountInGemInGemBaseUnits; - } else if (makerToken == psmInfo.gemTokenAddress) { - // Simulate buyGem - // Buying USDC from the PSM, decreasing the total debt - uint256 makerTokenAmountInWad = makerTokenAmount.safeMul(1e12); - uint256 feeMultiplierInWad = WAD.safeAdd(psm.tout()); // eg. 1.001 * 10 ** 18 with 0.1% tout; - uint256 takerTokenAmountInWad = makerTokenAmountInWad.safeMul(feeMultiplierInWad).safeDiv(WAD); - if (takerTokenAmountInWad > totalDebtInWad) { - return 0; - } - uint256 newTotalDebtInRad = totalDebtInWad.safeSub(takerTokenAmountInWad).safeMul(RAY); - - // PSM is empty, not enough USDC to buy - if (newTotalDebtInRad <= debtFloorInRad) { - return 0; - } - - - return takerTokenAmountInWad; - } - - return 0; - } - -} diff --git a/packages/asset-swapper/contracts/src/MooniswapSampler.sol b/packages/asset-swapper/contracts/src/MooniswapSampler.sol deleted file mode 100644 index 32ee080450..0000000000 --- a/packages/asset-swapper/contracts/src/MooniswapSampler.sol +++ /dev/null @@ -1,169 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -/* - - Copyright 2020 ZeroEx Intl. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -pragma solidity ^0.6; -pragma experimental ABIEncoderV2; - -import "./interfaces/IMooniswap.sol"; -import "./ApproximateBuys.sol"; -import "./SamplerUtils.sol"; - - -contract MooniswapSampler is - SamplerUtils, - ApproximateBuys -{ - /// @dev Gas limit for Mooniswap calls. - uint256 constant private MOONISWAP_CALL_GAS = 150e3; // 150k - - /// @dev Sample sell quotes from Mooniswap. - /// @param registry Address of the Mooniswap Registry. - /// @param takerToken Address of the taker token (what to sell). - /// @param makerToken Address of the maker token (what to buy). - /// @param takerTokenAmounts Taker token sell amount for each sample. - /// @return pool The contract address for the pool - /// @return makerTokenAmounts Maker amounts bought at each taker token - /// amount. - function sampleSellsFromMooniswap( - address registry, - address takerToken, - address makerToken, - uint256[] memory takerTokenAmounts - ) - public - view - returns (IMooniswap pool, uint256[] memory makerTokenAmounts) - { - _assertValidPair(makerToken, takerToken); - uint256 numSamples = takerTokenAmounts.length; - makerTokenAmounts = new uint256[](numSamples); - - for (uint256 i = 0; i < numSamples; i++) { - uint256 buyAmount = sampleSingleSellFromMooniswapPool( - registry, - takerToken, - makerToken, - takerTokenAmounts[i] - ); - makerTokenAmounts[i] = buyAmount; - // Break early if there are 0 amounts - if (makerTokenAmounts[i] == 0) { - break; - } - } - - pool = IMooniswap( - IMooniswapRegistry(registry).pools(takerToken, makerToken) - ); - } - - function sampleSingleSellFromMooniswapPool( - address registry, - address mooniswapTakerToken, - address mooniswapMakerToken, - uint256 takerTokenAmount - ) - public - view - returns (uint256) - { - // Find the pool for the pair. - IMooniswap pool = IMooniswap( - IMooniswapRegistry(registry).pools(mooniswapTakerToken, mooniswapMakerToken) - ); - // If there is no pool then return early - if (address(pool) == address(0)) { - return 0; - } - uint256 poolBalance = mooniswapTakerToken == address(0) - ? address(pool).balance - : IERC20TokenV06(mooniswapTakerToken).balanceOf(address(pool)); - // If the pool balance is smaller than the sell amount - // don't sample to avoid multiplication overflow in buys - if (poolBalance < takerTokenAmount) { - return 0; - } - try - pool.getReturn - {gas: MOONISWAP_CALL_GAS} - (mooniswapTakerToken, mooniswapMakerToken, takerTokenAmount) - returns (uint256 amount) - { - return amount; - } catch (bytes memory) { - // Swallow failures, leaving all results as zero. - return 0; - } - } - - /// @dev Sample buy quotes from Mooniswap. - /// @param registry Address of the Mooniswap Registry. - /// @param takerToken Address of the taker token (what to sell). - /// @param makerToken Address of the maker token (what to buy). - /// @param makerTokenAmounts Maker token sell amount for each sample. - /// @return pool The contract address for the pool - /// @return takerTokenAmounts Taker amounts sold at each maker token - /// amount. - function sampleBuysFromMooniswap( - address registry, - address takerToken, - address makerToken, - uint256[] memory makerTokenAmounts - ) - public - view - returns (IMooniswap pool, uint256[] memory takerTokenAmounts) - { - _assertValidPair(makerToken, takerToken); - uint256 numSamples = makerTokenAmounts.length; - takerTokenAmounts = new uint256[](numSamples); - - takerTokenAmounts = _sampleApproximateBuys( - ApproximateBuyQuoteOpts({ - makerTokenData: abi.encode(registry, makerToken), - takerTokenData: abi.encode(registry, takerToken), - getSellQuoteCallback: _sampleSellForApproximateBuyFromMooniswap - }), - makerTokenAmounts - ); - - pool = IMooniswap( - IMooniswapRegistry(registry).pools(takerToken, makerToken) - ); - } - - function _sampleSellForApproximateBuyFromMooniswap( - bytes memory takerTokenData, - bytes memory makerTokenData, - uint256 sellAmount - ) - private - view - returns (uint256 buyAmount) - { - (address registry, address mooniswapTakerToken) = abi.decode(takerTokenData, (address, address)); - (address _registry, address mooniswapMakerToken) = abi.decode(makerTokenData, (address, address)); - return sampleSingleSellFromMooniswapPool( - registry, - mooniswapTakerToken, - mooniswapMakerToken, - sellAmount - ); - } -} diff --git a/packages/asset-swapper/contracts/src/NativeOrderSampler.sol b/packages/asset-swapper/contracts/src/NativeOrderSampler.sol deleted file mode 100644 index 43c84272fe..0000000000 --- a/packages/asset-swapper/contracts/src/NativeOrderSampler.sol +++ /dev/null @@ -1,239 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -/* - - Copyright 2021 ZeroEx Intl. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -pragma solidity ^0.6; -pragma experimental ABIEncoderV2; - -import "@0x/contracts-erc20/contracts/src/v06/LibERC20TokenV06.sol"; -import "@0x/contracts-utils/contracts/src/v06/LibMathV06.sol"; -import "@0x/contracts-utils/contracts/src/v06/LibBytesV06.sol"; -import "@0x/contracts-utils/contracts/src/v06/LibSafeMathV06.sol"; - - -interface IExchange { - - enum OrderStatus { - INVALID, - FILLABLE, - FILLED, - CANCELLED, - EXPIRED - } - - /// @dev A standard OTC or OO limit order. - struct LimitOrder { - IERC20TokenV06 makerToken; - IERC20TokenV06 takerToken; - uint128 makerAmount; - uint128 takerAmount; - uint128 takerTokenFeeAmount; - address maker; - address taker; - address sender; - address feeRecipient; - bytes32 pool; - uint64 expiry; - uint256 salt; - } - - /// @dev An RFQ limit order. - struct RfqOrder { - IERC20TokenV06 makerToken; - IERC20TokenV06 takerToken; - uint128 makerAmount; - uint128 takerAmount; - address maker; - address taker; - address txOrigin; - bytes32 pool; - uint64 expiry; - uint256 salt; - } - - /// @dev Info on a limit or RFQ order. - struct OrderInfo { - bytes32 orderHash; - OrderStatus status; - uint128 takerTokenFilledAmount; - } - - /// @dev Allowed signature types. - enum SignatureType { - ILLEGAL, - INVALID, - EIP712, - ETHSIGN - } - - /// @dev Encoded EC signature. - struct Signature { - // How to validate the signature. - SignatureType signatureType; - // EC Signature data. - uint8 v; - // EC Signature data. - bytes32 r; - // EC Signature data. - bytes32 s; - } - - /// @dev Get the order info for a limit order. - /// @param order The limit order. - /// @return orderInfo Info about the order. - function getLimitOrderInfo(LimitOrder memory order) - external - view - returns (OrderInfo memory orderInfo); - - /// @dev Get order info, fillable amount, and signature validity for a limit order. - /// Fillable amount is determined using balances and allowances of the maker. - /// @param order The limit order. - /// @param signature The order signature. - /// @return orderInfo Info about the order. - /// @return actualFillableTakerTokenAmount How much of the order is fillable - /// based on maker funds, in taker tokens. - /// @return isSignatureValid Whether the signature is valid. - function getLimitOrderRelevantState( - LimitOrder memory order, - Signature calldata signature - ) - external - view - returns ( - OrderInfo memory orderInfo, - uint128 actualFillableTakerTokenAmount, - bool isSignatureValid - ); -} - -contract NativeOrderSampler { - using LibSafeMathV06 for uint256; - using LibBytesV06 for bytes; - - /// @dev Gas limit for calls to `getOrderFillableTakerAmount()`. - uint256 constant internal DEFAULT_CALL_GAS = 200e3; // 200k - - /// @dev Queries the fillable taker asset amounts of native orders. - /// Effectively ignores orders that have empty signatures or - /// maker/taker asset amounts (returning 0). - /// @param orders Native limit orders to query. - /// @param orderSignatures Signatures for each respective order in `orders`. - /// @param exchange The V4 exchange. - /// @return orderFillableTakerAssetAmounts How much taker asset can be filled - /// by each order in `orders`. - function getLimitOrderFillableTakerAssetAmounts( - IExchange.LimitOrder[] memory orders, - IExchange.Signature[] memory orderSignatures, - IExchange exchange - ) - public - view - returns (uint256[] memory orderFillableTakerAssetAmounts) - { - orderFillableTakerAssetAmounts = new uint256[](orders.length); - for (uint256 i = 0; i != orders.length; i++) { - try - this.getLimitOrderFillableTakerAmount - {gas: DEFAULT_CALL_GAS} - ( - orders[i], - orderSignatures[i], - exchange - ) - returns (uint256 amount) - { - orderFillableTakerAssetAmounts[i] = amount; - } catch (bytes memory) { - // Swallow failures, leaving all results as zero. - orderFillableTakerAssetAmounts[i] = 0; - } - } - } - - /// @dev Queries the fillable taker asset amounts of native orders. - /// Effectively ignores orders that have empty signatures or - /// @param orders Native orders to query. - /// @param orderSignatures Signatures for each respective order in `orders`. - /// @param exchange The V4 exchange. - /// @return orderFillableMakerAssetAmounts How much maker asset can be filled - /// by each order in `orders`. - function getLimitOrderFillableMakerAssetAmounts( - IExchange.LimitOrder[] memory orders, - IExchange.Signature[] memory orderSignatures, - IExchange exchange - ) - public - view - returns (uint256[] memory orderFillableMakerAssetAmounts) - { - orderFillableMakerAssetAmounts = getLimitOrderFillableTakerAssetAmounts( - orders, - orderSignatures, - exchange - ); - // `orderFillableMakerAssetAmounts` now holds taker asset amounts, so - // convert them to maker asset amounts. - for (uint256 i = 0; i < orders.length; ++i) { - if (orderFillableMakerAssetAmounts[i] != 0) { - orderFillableMakerAssetAmounts[i] = LibMathV06.getPartialAmountCeil( - orderFillableMakerAssetAmounts[i], - orders[i].takerAmount, - orders[i].makerAmount - ); - } - } - } - - /// @dev Get the fillable taker amount of an order, taking into account - /// order state, maker fees, and maker balances. - function getLimitOrderFillableTakerAmount( - IExchange.LimitOrder memory order, - IExchange.Signature memory signature, - IExchange exchange - ) - virtual - public - view - returns (uint256 fillableTakerAmount) - { - if (signature.signatureType == IExchange.SignatureType.ILLEGAL || - signature.signatureType == IExchange.SignatureType.INVALID || - order.makerAmount == 0 || - order.takerAmount == 0) - { - return 0; - } - - ( - IExchange.OrderInfo memory orderInfo, - uint128 remainingFillableTakerAmount, - bool isSignatureValid - ) = exchange.getLimitOrderRelevantState(order, signature); - - if ( - orderInfo.status != IExchange.OrderStatus.FILLABLE || - !isSignatureValid || - order.makerToken == IERC20TokenV06(0) - ) { - return 0; - } - - fillableTakerAmount = uint256(remainingFillableTakerAmount); - } -} diff --git a/packages/asset-swapper/contracts/src/PlatypusSampler.sol b/packages/asset-swapper/contracts/src/PlatypusSampler.sol deleted file mode 100644 index d286a9e2e0..0000000000 --- a/packages/asset-swapper/contracts/src/PlatypusSampler.sol +++ /dev/null @@ -1,89 +0,0 @@ -pragma solidity ^0.6; -pragma experimental ABIEncoderV2; - -import "./interfaces/IPlatypus.sol"; -import "./ApproximateBuys.sol"; -import "./SamplerUtils.sol"; - - -contract PlatypusSampler is - SamplerUtils, - ApproximateBuys -{ - - function sampleSellsFromPlatypus( - address pool, - address[] memory path, - uint256[] memory takerTokenAmounts - ) - public - view - returns (uint256[] memory makerTokenAmounts) - { - - uint256 numSamples = takerTokenAmounts.length; - makerTokenAmounts = new uint256[](numSamples); - for (uint256 i = 0; i < numSamples; i++) { - try - IPlatypus(pool).quotePotentialSwap(path[0], path[1], takerTokenAmounts[i]) - returns (uint256 amountAfterFees, uint256 feeAmount) - { - makerTokenAmounts[i] = amountAfterFees; - // Break early if there are 0 amounts - if (makerTokenAmounts[i] == 0) { - break; - } - } catch (bytes memory result) { - // Swallow failures, leaving all results as zero. - break; - } - } - } - - function sampleBuysFromPlatypus( - address pool, - address[] memory path, - uint256[] memory makerTokenAmounts - ) - public - view - returns (uint256[] memory takerTokenAmounts) - { - address[] memory invertBuyPath = new address[](2); - invertBuyPath[0] = path[1]; - invertBuyPath[1] = path[0]; - return _sampleApproximateBuys( - ApproximateBuyQuoteOpts({ - makerTokenData: abi.encode(pool, invertBuyPath), - takerTokenData: abi.encode(pool, path), - getSellQuoteCallback: _sampleSellForApproximateBuyFromPlatypus - }), - makerTokenAmounts - ); - } - - - function _sampleSellForApproximateBuyFromPlatypus( - bytes memory makerTokenData, - bytes memory takerTokenData, - uint256 sellAmount - ) - private - view - returns (uint256 buyAmount) - { - (address _pool, address[] memory _path ) = abi.decode(makerTokenData, (address, address[])); - - (bool success, bytes memory resultData) = address(this).staticcall(abi.encodeWithSelector( - this.sampleSellsFromPlatypus.selector, - _pool, - _path, - _toSingleValueArray(sellAmount) - )); - if(!success) { - return 0; - } - - return abi.decode(resultData, (uint256[]))[0]; - } -} diff --git a/packages/asset-swapper/contracts/src/SamplerUtils.sol b/packages/asset-swapper/contracts/src/SamplerUtils.sol deleted file mode 100644 index b505512b6b..0000000000 --- a/packages/asset-swapper/contracts/src/SamplerUtils.sol +++ /dev/null @@ -1,58 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -/* - - Copyright 2020 ZeroEx Intl. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -pragma solidity ^0.6; -pragma experimental ABIEncoderV2; - -import "@0x/contracts-erc20/contracts/src/v06/LibERC20TokenV06.sol"; - - -contract SamplerUtils { - - /// @dev Overridable way to get token decimals. - /// @param tokenAddress Address of the token. - /// @return decimals The decimal places for the token. - function _getTokenDecimals(address tokenAddress) - virtual - internal - view - returns (uint8 decimals) - { - return LibERC20TokenV06.compatDecimals(IERC20TokenV06(tokenAddress)); - } - - function _toSingleValueArray(uint256 v) - internal - pure - returns (uint256[] memory arr) - { - arr = new uint256[](1); - arr[0] = v; - } - - /// @dev Assert that the tokens in a trade pair are valid. - /// @param makerToken Address of the maker token. - /// @param takerToken Address of the taker token. - function _assertValidPair(address makerToken, address takerToken) - internal - pure - { - require(makerToken != takerToken, "ERC20BridgeSampler/INVALID_TOKEN_PAIR"); - } -} diff --git a/packages/asset-swapper/contracts/src/ShellSampler.sol b/packages/asset-swapper/contracts/src/ShellSampler.sol deleted file mode 100644 index f580e12cc2..0000000000 --- a/packages/asset-swapper/contracts/src/ShellSampler.sol +++ /dev/null @@ -1,126 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -/* - - Copyright 2020 ZeroEx Intl. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -pragma solidity ^0.6; -pragma experimental ABIEncoderV2; - -import "./ApproximateBuys.sol"; -import "./interfaces/IShell.sol"; -import "./SamplerUtils.sol"; - - -contract ShellSampler is - SamplerUtils, - ApproximateBuys -{ - - struct ShellInfo { - address poolAddress; - } - - /// @dev Default gas limit for Shell calls. - uint256 constant private DEFAULT_CALL_GAS = 300e3; // 300k - - /// @dev Sample sell quotes from the Shell pool contract - /// @param pool Address of the Shell pool contract - /// @param takerToken Address of the taker token (what to sell). - /// @param makerToken Address of the maker token (what to buy). - /// @param takerTokenAmounts Taker token sell amount for each sample. - /// @return makerTokenAmounts Maker amounts bought at each taker token - /// amount. - function sampleSellsFromShell( - address pool, - address takerToken, - address makerToken, - uint256[] memory takerTokenAmounts - ) - public - view - returns (uint256[] memory makerTokenAmounts) - { - // Initialize array of maker token amounts. - uint256 numSamples = takerTokenAmounts.length; - makerTokenAmounts = new uint256[](numSamples); - - for (uint256 i = 0; i < numSamples; i++) { - try - IShell(pool).viewOriginSwap - {gas: DEFAULT_CALL_GAS} - (takerToken, makerToken, takerTokenAmounts[i]) - returns (uint256 amount) - { - makerTokenAmounts[i] = amount; - } catch (bytes memory) { - // Swallow failures, leaving all results as zero. - break; - } - } - } - - /// @dev Sample buy quotes from Shell pool contract - /// @param pool Address of the Shell pool contract - /// @param takerToken Address of the taker token (what to sell). - /// @param makerToken Address of the maker token (what to buy). - /// @param makerTokenAmounts Maker token buy amount for each sample. - /// @return takerTokenAmounts Taker amounts sold at each maker token - /// amount. - function sampleBuysFromShell( - address pool, - address takerToken, - address makerToken, - uint256[] memory makerTokenAmounts - ) - public - view - returns (uint256[] memory takerTokenAmounts) - { - return _sampleApproximateBuys( - ApproximateBuyQuoteOpts({ - makerTokenData: abi.encode(makerToken, pool), - takerTokenData: abi.encode(takerToken, pool), - getSellQuoteCallback: _sampleSellForApproximateBuyFromShell - }), - makerTokenAmounts - ); - } - - function _sampleSellForApproximateBuyFromShell( - bytes memory takerTokenData, - bytes memory makerTokenData, - uint256 sellAmount - ) - private - view - returns (uint256 buyAmount) - { - (address takerToken, address pool) = abi.decode(takerTokenData, (address, address)); - (address makerToken) = abi.decode(makerTokenData, (address)); - - try - this.sampleSellsFromShell - (pool, takerToken, makerToken, _toSingleValueArray(sellAmount)) - returns (uint256[] memory amounts) - { - return amounts[0]; - } catch (bytes memory) { - // Swallow failures, leaving all results as zero. - return 0; - } - } -} diff --git a/packages/asset-swapper/contracts/src/SynthetixSampler.sol b/packages/asset-swapper/contracts/src/SynthetixSampler.sol deleted file mode 100644 index 38e45ebc6b..0000000000 --- a/packages/asset-swapper/contracts/src/SynthetixSampler.sol +++ /dev/null @@ -1,173 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -/* - - Copyright 2022 ZeroEx Intl. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -pragma solidity ^0.6; -pragma experimental ABIEncoderV2; - -interface IReadProxyAddressResolver { - function target() external view returns (address); -} - -interface IAddressResolver { - function getAddress(bytes32 name) external view returns (address); -} - -interface IExchanger { - // Ethereum Mainnet - function getAmountsForAtomicExchange( - uint256 sourceAmount, - bytes32 sourceCurrencyKey, - bytes32 destinationCurrencyKey - ) - external - view - returns ( - uint256 amountReceived, - uint256 fee, - uint256 exchangeFeeRate - ); - - // Optimism - function getAmountsForExchange( - uint256 sourceAmount, - bytes32 sourceCurrencyKey, - bytes32 destinationCurrencyKey - ) - external - view - returns ( - uint256 amountReceived, - uint256 fee, - uint256 exchangeFeeRate - ); -} - -contract SynthetixSampler { - - /// @dev Sample sell quotes from Synthetix Atomic Swap. - /// @param takerTokenSymbol Symbol (currency key) of the taker token (what to sell). - /// @param makerTokenSymbol Symbol (currency key) of the maker token (what to buy). - /// @param takerTokenAmounts Taker token sell amount for each sample (sorted in ascending order). - /// @return synthetix Synthetix address. - /// @return makerTokenAmounts Maker amounts bought at each taker token amount. - function sampleSellsFromSynthetix( - IReadProxyAddressResolver readProxy, - bytes32 takerTokenSymbol, - bytes32 makerTokenSymbol, - uint256[] memory takerTokenAmounts - ) public view returns (address synthetix, uint256[] memory makerTokenAmounts) { - synthetix = getSynthetixAddress(readProxy); - uint256 numSamples = takerTokenAmounts.length; - makerTokenAmounts = new uint256[](numSamples); - if (numSamples == 0) { - return (synthetix, makerTokenAmounts); - } - - makerTokenAmounts[0] = exchange( - readProxy, - takerTokenAmounts[0], - takerTokenSymbol, - makerTokenSymbol - ); - - // Synthetix atomic swap has a fixed rate. Calculate the rest based on the first value (and save gas). - for (uint256 i = 1; i < numSamples; i++) { - makerTokenAmounts[i] = - (makerTokenAmounts[0] * takerTokenAmounts[i]) / - takerTokenAmounts[0]; - } - } - - /// @dev Sample buy quotes from Synthetix Atomic Swap. - /// @param takerTokenSymbol Symbol (currency key) of the taker token (what to sell). - /// @param makerTokenSymbol Symbol (currency key) of the maker token (what to buy). - /// @param makerTokenAmounts Maker token buy amount for each sample (sorted in ascending order). - /// @return synthetix Synthetix address. - /// @return takerTokenAmounts Taker amounts sold at each maker token amount. - function sampleBuysFromSynthetix( - IReadProxyAddressResolver readProxy, - bytes32 takerTokenSymbol, - bytes32 makerTokenSymbol, - uint256[] memory makerTokenAmounts - ) public view returns (address synthetix, uint256[] memory takerTokenAmounts) { - synthetix = getSynthetixAddress(readProxy); - // Since Synthetix atomic have a fixed rate, we can pick any reasonablely size takerTokenAmount (fixed to 1 ether here) and calculate the rest. - uint256 amountReceivedForEther = exchange( - readProxy, - 1 ether, - takerTokenSymbol, - makerTokenSymbol - ); - - uint256 numSamples = makerTokenAmounts.length; - takerTokenAmounts = new uint256[](numSamples); - - for (uint256 i = 0; i < numSamples; i++) { - takerTokenAmounts[i] = - (1 ether * makerTokenAmounts[i]) / - amountReceivedForEther; - } - } - - function exchange( - IReadProxyAddressResolver readProxy, - uint256 sourceAmount, - bytes32 sourceCurrencyKey, - bytes32 destinationCurrencyKey - ) private view returns (uint256 amountReceived) { - IExchanger exchanger = getExchanger(readProxy); - uint256 chainId; - assembly { - chainId := chainid() - } - - if (chainId == 1) { - (amountReceived, , ) = exchanger.getAmountsForAtomicExchange( - sourceAmount, - sourceCurrencyKey, - destinationCurrencyKey - ); - } else { - (amountReceived, , ) = exchanger.getAmountsForExchange( - sourceAmount, - sourceCurrencyKey, - destinationCurrencyKey - ); - } - } - - function getSynthetixAddress(IReadProxyAddressResolver readProxy) - private - view - returns (address) - { - return IAddressResolver(readProxy.target()).getAddress("Synthetix"); - } - - function getExchanger(IReadProxyAddressResolver readProxy) - private - view - returns (IExchanger) - { - return - IExchanger( - IAddressResolver(readProxy.target()).getAddress("Exchanger") - ); - } -} diff --git a/packages/asset-swapper/contracts/src/TwoHopSampler.sol b/packages/asset-swapper/contracts/src/TwoHopSampler.sol deleted file mode 100644 index a98fbc352c..0000000000 --- a/packages/asset-swapper/contracts/src/TwoHopSampler.sol +++ /dev/null @@ -1,124 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -/* - - Copyright 2020 ZeroEx Intl. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -pragma solidity ^0.6; -pragma experimental ABIEncoderV2; - -import "@0x/contracts-utils/contracts/src/v06/LibBytesV06.sol"; - - -contract TwoHopSampler { - using LibBytesV06 for bytes; - - struct HopInfo { - uint256 sourceIndex; - bytes returnData; - } - - function sampleTwoHopSell( - bytes[] memory firstHopCalls, - bytes[] memory secondHopCalls, - uint256 sellAmount - ) - public - returns ( - HopInfo memory firstHop, - HopInfo memory secondHop, - uint256 buyAmount - ) - { - uint256 intermediateAssetAmount = 0; - for (uint256 i = 0; i != firstHopCalls.length; ++i) { - firstHopCalls[i].writeUint256(firstHopCalls[i].length - 32, sellAmount); - (bool didSucceed, bytes memory returnData) = address(this).call(firstHopCalls[i]); - if (didSucceed) { - uint256 amount = returnData.readUint256(returnData.length - 32); - if (amount > intermediateAssetAmount) { - intermediateAssetAmount = amount; - firstHop.sourceIndex = i; - firstHop.returnData = returnData; - } - } - } - if (intermediateAssetAmount == 0) { - return (firstHop, secondHop, buyAmount); - } - for (uint256 j = 0; j != secondHopCalls.length; ++j) { - secondHopCalls[j].writeUint256(secondHopCalls[j].length - 32, intermediateAssetAmount); - (bool didSucceed, bytes memory returnData) = address(this).call(secondHopCalls[j]); - if (didSucceed) { - uint256 amount = returnData.readUint256(returnData.length - 32); - if (amount > buyAmount) { - buyAmount = amount; - secondHop.sourceIndex = j; - secondHop.returnData = returnData; - } - } - } - } - - function sampleTwoHopBuy( - bytes[] memory firstHopCalls, - bytes[] memory secondHopCalls, - uint256 buyAmount - ) - public - returns ( - HopInfo memory firstHop, - HopInfo memory secondHop, - uint256 sellAmount - ) - { - sellAmount = uint256(-1); - uint256 intermediateAssetAmount = uint256(-1); - for (uint256 j = 0; j != secondHopCalls.length; ++j) { - secondHopCalls[j].writeUint256(secondHopCalls[j].length - 32, buyAmount); - (bool didSucceed, bytes memory returnData) = address(this).call(secondHopCalls[j]); - if (didSucceed) { - uint256 amount = returnData.readUint256(returnData.length - 32); - if ( - amount > 0 && - amount < intermediateAssetAmount - ) { - intermediateAssetAmount = amount; - secondHop.sourceIndex = j; - secondHop.returnData = returnData; - } - } - } - if (intermediateAssetAmount == uint256(-1)) { - return (firstHop, secondHop, sellAmount); - } - for (uint256 i = 0; i != firstHopCalls.length; ++i) { - firstHopCalls[i].writeUint256(firstHopCalls[i].length - 32, intermediateAssetAmount); - (bool didSucceed, bytes memory returnData) = address(this).call(firstHopCalls[i]); - if (didSucceed) { - uint256 amount = returnData.readUint256(returnData.length - 32); - if ( - amount > 0 && - amount < sellAmount - ) { - sellAmount = amount; - firstHop.sourceIndex = i; - firstHop.returnData = returnData; - } - } - } - } -} diff --git a/packages/asset-swapper/contracts/src/UniswapSampler.sol b/packages/asset-swapper/contracts/src/UniswapSampler.sol deleted file mode 100644 index 329461cef9..0000000000 --- a/packages/asset-swapper/contracts/src/UniswapSampler.sol +++ /dev/null @@ -1,214 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -/* - - Copyright 2020 ZeroEx Intl. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -pragma solidity ^0.6; -pragma experimental ABIEncoderV2; - -import "./interfaces/IUniswapExchangeQuotes.sol"; -import "./SamplerUtils.sol"; - - -interface IUniswapExchangeFactory { - - /// @dev Get the exchange for a token. - /// @param tokenAddress The address of the token contract. - function getExchange(address tokenAddress) - external - view - returns (address); -} - - -contract UniswapSampler is - SamplerUtils -{ - /// @dev Gas limit for Uniswap calls. - uint256 constant private UNISWAP_CALL_GAS = 150e3; // 150k - - /// @dev Sample sell quotes from Uniswap. - /// @param router Address of the Uniswap Router - /// @param takerToken Address of the taker token (what to sell). - /// @param makerToken Address of the maker token (what to buy). - /// @param takerTokenAmounts Taker token sell amount for each sample. - /// @return makerTokenAmounts Maker amounts bought at each taker token - /// amount. - function sampleSellsFromUniswap( - address router, - address takerToken, - address makerToken, - uint256[] memory takerTokenAmounts - ) - public - view - returns (uint256[] memory makerTokenAmounts) - { - _assertValidPair(makerToken, takerToken); - uint256 numSamples = takerTokenAmounts.length; - makerTokenAmounts = new uint256[](numSamples); - - IUniswapExchangeQuotes takerTokenExchange = takerToken == address(0) ? - IUniswapExchangeQuotes(0) : _getUniswapExchange(router, takerToken); - IUniswapExchangeQuotes makerTokenExchange = makerToken == address(0) ? - IUniswapExchangeQuotes(0) : _getUniswapExchange(router, makerToken); - for (uint256 i = 0; i < numSamples; i++) { - bool didSucceed = true; - if (makerToken == address(0)) { - (makerTokenAmounts[i], didSucceed) = _callUniswapExchangePriceFunction( - address(takerTokenExchange), - takerTokenExchange.getTokenToEthInputPrice.selector, - takerTokenAmounts[i] - ); - } else if (takerToken == address(0)) { - (makerTokenAmounts[i], didSucceed) = _callUniswapExchangePriceFunction( - address(makerTokenExchange), - makerTokenExchange.getEthToTokenInputPrice.selector, - takerTokenAmounts[i] - ); - } else { - uint256 ethBought; - (ethBought, didSucceed) = _callUniswapExchangePriceFunction( - address(takerTokenExchange), - takerTokenExchange.getTokenToEthInputPrice.selector, - takerTokenAmounts[i] - ); - if (ethBought != 0) { - (makerTokenAmounts[i], didSucceed) = _callUniswapExchangePriceFunction( - address(makerTokenExchange), - makerTokenExchange.getEthToTokenInputPrice.selector, - ethBought - ); - } else { - makerTokenAmounts[i] = 0; - } - } - // Break early if amounts are 0 - if (!didSucceed || makerTokenAmounts[i] == 0) { - break; - } - } - } - - /// @dev Sample buy quotes from Uniswap. - /// @param takerToken Address of the taker token (what to sell). - /// @param makerToken Address of the maker token (what to buy). - /// @param makerTokenAmounts Maker token sell amount for each sample. - /// @return takerTokenAmounts Taker amounts sold at each maker token - /// amount. - function sampleBuysFromUniswap( - address router, - address takerToken, - address makerToken, - uint256[] memory makerTokenAmounts - ) - public - view - returns (uint256[] memory takerTokenAmounts) - { - _assertValidPair(makerToken, takerToken); - uint256 numSamples = makerTokenAmounts.length; - takerTokenAmounts = new uint256[](numSamples); - - IUniswapExchangeQuotes takerTokenExchange = takerToken == address(0) ? - IUniswapExchangeQuotes(0) : _getUniswapExchange(router, takerToken); - IUniswapExchangeQuotes makerTokenExchange = makerToken == address(0) ? - IUniswapExchangeQuotes(0) : _getUniswapExchange(router, makerToken); - for (uint256 i = 0; i < numSamples; i++) { - bool didSucceed = true; - if (makerToken == address(0)) { - (takerTokenAmounts[i], didSucceed) = _callUniswapExchangePriceFunction( - address(takerTokenExchange), - takerTokenExchange.getTokenToEthOutputPrice.selector, - makerTokenAmounts[i] - ); - } else if (takerToken == address(0)) { - (takerTokenAmounts[i], didSucceed) = _callUniswapExchangePriceFunction( - address(makerTokenExchange), - makerTokenExchange.getEthToTokenOutputPrice.selector, - makerTokenAmounts[i] - ); - } else { - uint256 ethSold; - (ethSold, didSucceed) = _callUniswapExchangePriceFunction( - address(makerTokenExchange), - makerTokenExchange.getEthToTokenOutputPrice.selector, - makerTokenAmounts[i] - ); - if (ethSold != 0) { - (takerTokenAmounts[i], didSucceed) = _callUniswapExchangePriceFunction( - address(takerTokenExchange), - takerTokenExchange.getTokenToEthOutputPrice.selector, - ethSold - ); - } else { - takerTokenAmounts[i] = 0; - } - } - // Break early if amounts are 0 - if (!didSucceed || takerTokenAmounts[i] == 0) { - break; - } - } - } - - /// @dev Gracefully calls a Uniswap pricing function. - /// @param uniswapExchangeAddress Address of an `IUniswapExchangeQuotes` exchange. - /// @param functionSelector Selector of the target function. - /// @param inputAmount Quantity parameter particular to the pricing function. - /// @return outputAmount The returned amount from the function call. Will be - /// zero if the call fails or if `uniswapExchangeAddress` is zero. - function _callUniswapExchangePriceFunction( - address uniswapExchangeAddress, - bytes4 functionSelector, - uint256 inputAmount - ) - private - view - returns (uint256 outputAmount, bool didSucceed) - { - if (uniswapExchangeAddress == address(0)) { - return (outputAmount, didSucceed); - } - bytes memory resultData; - (didSucceed, resultData) = - uniswapExchangeAddress.staticcall.gas(UNISWAP_CALL_GAS)( - abi.encodeWithSelector( - functionSelector, - inputAmount - )); - if (didSucceed) { - outputAmount = abi.decode(resultData, (uint256)); - } - } - - /// @dev Retrive an existing Uniswap exchange contract. - /// Throws if the exchange does not exist. - /// @param router Address of the Uniswap router. - /// @param tokenAddress Address of the token contract. - /// @return exchange `IUniswapExchangeQuotes` for the token. - function _getUniswapExchange(address router, address tokenAddress) - private - view - returns (IUniswapExchangeQuotes exchange) - { - exchange = IUniswapExchangeQuotes( - address(IUniswapExchangeFactory(router) - .getExchange(tokenAddress)) - ); - } -} diff --git a/packages/asset-swapper/contracts/src/UniswapV2Sampler.sol b/packages/asset-swapper/contracts/src/UniswapV2Sampler.sol deleted file mode 100644 index de2f2ee29b..0000000000 --- a/packages/asset-swapper/contracts/src/UniswapV2Sampler.sol +++ /dev/null @@ -1,102 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -/* - - Copyright 2020 ZeroEx Intl. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -pragma solidity ^0.6; -pragma experimental ABIEncoderV2; - -import "./interfaces/IUniswapV2Router01.sol"; - - -contract UniswapV2Sampler -{ - /// @dev Gas limit for UniswapV2 calls. - uint256 constant private UNISWAPV2_CALL_GAS = 150e3; // 150k - - /// @dev Sample sell quotes from UniswapV2. - /// @param router Router to look up tokens and amounts - /// @param path Token route. Should be takerToken -> makerToken - /// @param takerTokenAmounts Taker token sell amount for each sample. - /// @return makerTokenAmounts Maker amounts bought at each taker token - /// amount. - function sampleSellsFromUniswapV2( - address router, - address[] memory path, - uint256[] memory takerTokenAmounts - ) - public - view - returns (uint256[] memory makerTokenAmounts) - { - uint256 numSamples = takerTokenAmounts.length; - makerTokenAmounts = new uint256[](numSamples); - for (uint256 i = 0; i < numSamples; i++) { - try - IUniswapV2Router01(router).getAmountsOut - {gas: UNISWAPV2_CALL_GAS} - (takerTokenAmounts[i], path) - returns (uint256[] memory amounts) - { - makerTokenAmounts[i] = amounts[path.length - 1]; - // Break early if there are 0 amounts - if (makerTokenAmounts[i] == 0) { - break; - } - } catch (bytes memory) { - // Swallow failures, leaving all results as zero. - break; - } - } - } - - /// @dev Sample buy quotes from UniswapV2. - /// @param router Router to look up tokens and amounts - /// @param path Token route. Should be takerToken -> makerToken. - /// @param makerTokenAmounts Maker token buy amount for each sample. - /// @return takerTokenAmounts Taker amounts sold at each maker token - /// amount. - function sampleBuysFromUniswapV2( - address router, - address[] memory path, - uint256[] memory makerTokenAmounts - ) - public - view - returns (uint256[] memory takerTokenAmounts) - { - uint256 numSamples = makerTokenAmounts.length; - takerTokenAmounts = new uint256[](numSamples); - for (uint256 i = 0; i < numSamples; i++) { - try - IUniswapV2Router01(router).getAmountsIn - {gas: UNISWAPV2_CALL_GAS} - (makerTokenAmounts[i], path) - returns (uint256[] memory amounts) - { - takerTokenAmounts[i] = amounts[0]; - // Break early if there are 0 amounts - if (takerTokenAmounts[i] == 0) { - break; - } - } catch (bytes memory) { - // Swallow failures, leaving all results as zero. - break; - } - } - } -} diff --git a/packages/asset-swapper/contracts/src/UniswapV3Sampler.sol b/packages/asset-swapper/contracts/src/UniswapV3Sampler.sol deleted file mode 100644 index 7eee3cedf6..0000000000 --- a/packages/asset-swapper/contracts/src/UniswapV3Sampler.sol +++ /dev/null @@ -1,363 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -/* - - Copyright 2021 ZeroEx Intl. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -pragma solidity ^0.6; -pragma experimental ABIEncoderV2; - -import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol"; - -interface IUniswapV3QuoterV2 { - function factory() - external - view - returns (IUniswapV3Factory factory); - - // @notice Returns the amount out received for a given exact input swap without executing the swap - // @param path The path of the swap, i.e. each token pair and the pool fee - // @param amountIn The amount of the first token to swap - // @return amountOut The amount of the last token that would be received - // @return sqrtPriceX96AfterList List of the sqrt price after the swap for each pool in the path - // @return initializedTicksCrossedList List of the initialized ticks that the swap crossed for each pool in the path - // @return gasEstimate The estimate of the gas that the swap consumes - function quoteExactInput(bytes memory path, uint256 amountIn) - external - returns ( - uint256 amountOut, - uint160[] memory sqrtPriceX96AfterList, - uint32[] memory initializedTicksCrossedList, - uint256 gasEstimate - ); - - // @notice Returns the amount in required for a given exact output swap without executing the swap - // @param path The path of the swap, i.e. each token pair and the pool fee. Path must be provided in reverse order - // @param amountOut The amount of the last token to receive - // @return amountIn The amount of first token required to be paid - // @return sqrtPriceX96AfterList List of the sqrt price after the swap for each pool in the path - // @return initializedTicksCrossedList List of the initialized ticks that the swap crossed for each pool in the path - // @return gasEstimate The estimate of the gas that the swap consumes - function quoteExactOutput(bytes memory path, uint256 amountOut) - external - returns ( - uint256 amountIn, - uint160[] memory sqrtPriceX96AfterList, - uint32[] memory initializedTicksCrossedList, - uint256 gasEstimate - ); -} - -interface IUniswapV3Factory { - function getPool(IERC20TokenV06 a, IERC20TokenV06 b, uint24 fee) - external - view - returns (IUniswapV3Pool pool); -} - -interface IUniswapV3Pool { - function token0() external view returns (IERC20TokenV06); - function token1() external view returns (IERC20TokenV06); - function fee() external view returns (uint24); -} - -contract UniswapV3Sampler -{ - /// @dev Gas limit for UniswapV3 calls. This is 100% a guess. - uint256 constant private QUOTE_GAS = 700e3; - - /// @dev Sample sell quotes from UniswapV3. - /// @param quoter UniswapV3 Quoter contract. - /// @param path Token route. Should be takerToken -> makerToken - /// @param takerTokenAmounts Taker token sell amount for each sample. - /// @return uniswapPaths The encoded uniswap path for each sample. - /// @return uniswapGasUsed Estimated amount of gas used - /// @return makerTokenAmounts Maker amounts bought at each taker token - /// amount. - function sampleSellsFromUniswapV3( - IUniswapV3QuoterV2 quoter, - IERC20TokenV06[] memory path, - uint256[] memory takerTokenAmounts - ) - public - returns ( - bytes[] memory uniswapPaths, - uint256[] memory uniswapGasUsed, - uint256[] memory makerTokenAmounts - ) - { - IUniswapV3Pool[][] memory poolPaths = - _getValidPoolPaths(quoter.factory(), path, 0); - - makerTokenAmounts = new uint256[](takerTokenAmounts.length); - uniswapPaths = new bytes[](takerTokenAmounts.length); - uniswapGasUsed = new uint256[](takerTokenAmounts.length); - - for (uint256 i = 0; i < takerTokenAmounts.length; ++i) { - // Pick the best result from all the paths. - uint256 topBuyAmount = 0; - for (uint256 j = 0; j < poolPaths.length; ++j) { - bytes memory uniswapPath = _toUniswapPath(path, poolPaths[j]); - try quoter.quoteExactInput - { gas: QUOTE_GAS } - (uniswapPath, takerTokenAmounts[i]) - returns ( - uint256 buyAmount, - uint160[] memory, /* sqrtPriceX96AfterList */ - uint32[] memory, /* initializedTicksCrossedList */ - uint256 gasUsed - ) - { - if (topBuyAmount <= buyAmount) { - topBuyAmount = buyAmount; - uniswapPaths[i] = uniswapPath; - uniswapGasUsed[i] = gasUsed; - } - } catch {} - } - // Break early if we can't complete the sells. - if (topBuyAmount == 0) { - // HACK(kimpers): To avoid too many local variables, paths and gas used is set directly in the loop - // then reset if no valid valid quote was found - uniswapPaths[i] = ""; - uniswapGasUsed[i] = 0; - break; - } - makerTokenAmounts[i] = topBuyAmount; - } - } - - /// @dev Sample buy quotes from UniswapV3. - /// @param quoter UniswapV3 Quoter contract. - /// @param path Token route. Should be takerToken -> makerToken. - /// @param makerTokenAmounts Maker token buy amount for each sample. - /// @return uniswapPaths The encoded uniswap path for each sample. - /// @return uniswapGasUsed Estimated amount of gas used - /// @return takerTokenAmounts Taker amounts sold at each maker token - /// amount. - function sampleBuysFromUniswapV3( - IUniswapV3QuoterV2 quoter, - IERC20TokenV06[] memory path, - uint256[] memory makerTokenAmounts - ) - public - returns ( - bytes[] memory uniswapPaths, - uint256[] memory uniswapGasUsed, - uint256[] memory takerTokenAmounts - ) - { - IUniswapV3Pool[][] memory poolPaths = - _getValidPoolPaths(quoter.factory(), path, 0); - IERC20TokenV06[] memory reversedPath = _reverseTokenPath(path); - - takerTokenAmounts = new uint256[](makerTokenAmounts.length); - uniswapPaths = new bytes[](makerTokenAmounts.length); - uniswapGasUsed = new uint256[](makerTokenAmounts.length); - - for (uint256 i = 0; i < makerTokenAmounts.length; ++i) { - // Pick the best result from all the paths. - uint256 topSellAmount = 0; - for (uint256 j = 0; j < poolPaths.length; ++j) { - // quoter requires path to be reversed for buys. - bytes memory uniswapPath = _toUniswapPath( - reversedPath, - _reversePoolPath(poolPaths[j]) - ); - try - quoter.quoteExactOutput - { gas: QUOTE_GAS } - (uniswapPath, makerTokenAmounts[i]) - returns ( - uint256 sellAmount, - uint160[] memory, /* sqrtPriceX96AfterList */ - uint32[] memory, /* initializedTicksCrossedList */ - uint256 gasUsed - ) - { - if (topSellAmount == 0 || topSellAmount >= sellAmount) { - topSellAmount = sellAmount; - // But the output path should still be encoded for sells. - uniswapPaths[i] = _toUniswapPath(path, poolPaths[j]); - uniswapGasUsed[i] = gasUsed; - } - } catch {} - } - // Break early if we can't complete the buys. - if (topSellAmount == 0) { - // HACK(kimpers): To avoid too many local variables, paths and gas used is set directly in the loop - // then reset if no valid valid quote was found - uniswapPaths[i] = ""; - uniswapGasUsed[i] = 0; - break; - } - takerTokenAmounts[i] = topSellAmount; - } - } - - function _getValidPoolPaths( - IUniswapV3Factory factory, - IERC20TokenV06[] memory tokenPath, - uint256 startIndex - ) - private - view - returns (IUniswapV3Pool[][] memory poolPaths) - { - require( - tokenPath.length - startIndex >= 2, - "UniswapV3Sampler/tokenPath too short" - ); - uint24[4] memory validPoolFees = [ - // The launch pool fees. Could get hairier if they add more. - uint24(0.0001e6), - uint24(0.0005e6), - uint24(0.003e6), - uint24(0.01e6) - ]; - IUniswapV3Pool[] memory validPools = - new IUniswapV3Pool[](validPoolFees.length); - uint256 numValidPools = 0; - { - IERC20TokenV06 inputToken = tokenPath[startIndex]; - IERC20TokenV06 outputToken = tokenPath[startIndex + 1]; - for (uint256 i = 0; i < validPoolFees.length; ++i) { - IUniswapV3Pool pool = - factory.getPool(inputToken, outputToken, validPoolFees[i]); - if (_isValidPool(pool)) { - validPools[numValidPools++] = pool; - } - } - } - if (numValidPools == 0) { - // No valid pools for this hop. - return poolPaths; - } - if (startIndex + 2 == tokenPath.length) { - // End of path. - poolPaths = new IUniswapV3Pool[][](numValidPools); - for (uint256 i = 0; i < numValidPools; ++i) { - poolPaths[i] = new IUniswapV3Pool[](1); - poolPaths[i][0] = validPools[i]; - } - return poolPaths; - } - // Get paths for subsequent hops. - IUniswapV3Pool[][] memory subsequentPoolPaths = - _getValidPoolPaths(factory, tokenPath, startIndex + 1); - if (subsequentPoolPaths.length == 0) { - // Could not complete the path. - return poolPaths; - } - // Combine our pools with the next hop paths. - poolPaths = new IUniswapV3Pool[][]( - numValidPools * subsequentPoolPaths.length - ); - for (uint256 i = 0; i < numValidPools; ++i) { - for (uint256 j = 0; j < subsequentPoolPaths.length; ++j) { - uint256 o = i * subsequentPoolPaths.length + j; - // Prepend pool to the subsequent path. - poolPaths[o] = - new IUniswapV3Pool[](1 + subsequentPoolPaths[j].length); - poolPaths[o][0] = validPools[i]; - for (uint256 k = 0; k < subsequentPoolPaths[j].length; ++k) { - poolPaths[o][1 + k] = subsequentPoolPaths[j][k]; - } - } - } - return poolPaths; - } - - function _reverseTokenPath(IERC20TokenV06[] memory tokenPath) - private - pure - returns (IERC20TokenV06[] memory reversed) - { - reversed = new IERC20TokenV06[](tokenPath.length); - for (uint256 i = 0; i < tokenPath.length; ++i) { - reversed[i] = tokenPath[tokenPath.length - i - 1]; - } - } - - function _reversePoolPath(IUniswapV3Pool[] memory poolPath) - private - pure - returns (IUniswapV3Pool[] memory reversed) - { - reversed = new IUniswapV3Pool[](poolPath.length); - for (uint256 i = 0; i < poolPath.length; ++i) { - reversed[i] = poolPath[poolPath.length - i - 1]; - } - } - - function _isValidPool(IUniswapV3Pool pool) - private - view - returns (bool isValid) - { - // Check if it has been deployed. - { - uint256 codeSize; - assembly { - codeSize := extcodesize(pool) - } - if (codeSize == 0) { - return false; - } - } - // Must have a balance of both tokens. - if (pool.token0().balanceOf(address(pool)) == 0) { - return false; - } - if (pool.token1().balanceOf(address(pool)) == 0) { - return false; - } - return true; - } - - function _toUniswapPath( - IERC20TokenV06[] memory tokenPath, - IUniswapV3Pool[] memory poolPath - ) - private - view - returns (bytes memory uniswapPath) - { - require( - tokenPath.length >= 2 && tokenPath.length == poolPath.length + 1, - "UniswapV3Sampler/invalid path lengths" - ); - // Uniswap paths are tightly packed as: - // [token0, token0token1PairFee, token1, token1Token2PairFee, token2, ...] - uniswapPath = new bytes(tokenPath.length * 20 + poolPath.length * 3); - uint256 o; - assembly { o := add(uniswapPath, 32) } - for (uint256 i = 0; i < tokenPath.length; ++i) { - if (i > 0) { - uint24 poolFee = poolPath[i - 1].fee(); - assembly { - mstore(o, shl(232, poolFee)) - o := add(o, 3) - } - } - IERC20TokenV06 token = tokenPath[i]; - assembly { - mstore(o, shl(96, token)) - o := add(o, 20) - } - } - } -} diff --git a/packages/asset-swapper/contracts/src/UtilitySampler.sol b/packages/asset-swapper/contracts/src/UtilitySampler.sol deleted file mode 100644 index bbc3c5a1ad..0000000000 --- a/packages/asset-swapper/contracts/src/UtilitySampler.sol +++ /dev/null @@ -1,95 +0,0 @@ - -// SPDX-License-Identifier: Apache-2.0 -/* - - Copyright 2021 ZeroEx Intl. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -pragma solidity ^0.6; -pragma experimental ABIEncoderV2; - -import "@0x/contracts-erc20/contracts/src/v06/LibERC20TokenV06.sol"; - -contract UtilitySampler { - - using LibERC20TokenV06 for IERC20TokenV06; - - IERC20TokenV06 private immutable UTILITY_ETH_ADDRESS = IERC20TokenV06(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE); - - function getTokenDecimals(IERC20TokenV06[] memory tokens) - public - view - returns (uint256[] memory decimals) - { - decimals = new uint256[](tokens.length); - for (uint256 i = 0; i != tokens.length; i++) { - decimals[i] = tokens[i] == UTILITY_ETH_ADDRESS - ? 18 - : tokens[i].compatDecimals(); - } - } - - function getBalanceOf(IERC20TokenV06[] memory tokens, address account) - public - view - returns (uint256[] memory balances) - { - balances = new uint256[](tokens.length); - for (uint256 i = 0; i != tokens.length; i++) { - balances[i] = tokens[i] == UTILITY_ETH_ADDRESS - ? account.balance - : tokens[i].compatBalanceOf(account); - } - } - - function getAllowanceOf(IERC20TokenV06[] memory tokens, address account, address spender) - public - view - returns (uint256[] memory allowances) - { - allowances = new uint256[](tokens.length); - for (uint256 i = 0; i != tokens.length; i++) { - allowances[i] = tokens[i] == UTILITY_ETH_ADDRESS - ? 0 - : tokens[i].compatAllowance(account, spender); - } - } - - function isContract(address account) - public - view - returns (bool) - { - uint256 size; - assembly { size := extcodesize(account) } - return size > 0; - } - - function getGasLeft() - public - returns (uint256) - { - return gasleft(); - } - - function getBlockNumber() - public - view - returns (uint256) - { - return block.number; - } -} \ No newline at end of file diff --git a/packages/asset-swapper/contracts/src/VelodromeSampler.sol b/packages/asset-swapper/contracts/src/VelodromeSampler.sol deleted file mode 100644 index 8640f9e1c7..0000000000 --- a/packages/asset-swapper/contracts/src/VelodromeSampler.sol +++ /dev/null @@ -1,134 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -/* - - Copyright 2022 ZeroEx Intl. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -pragma solidity ^0.6; -pragma experimental ABIEncoderV2; - -import './ApproximateBuys.sol'; -import './SamplerUtils.sol'; - -struct VeloRoute { - address from; - address to; - bool stable; -} - -interface IVelodromeRouter { - function getAmountOut( - uint256 amountIn, - address tokenIn, - address tokenOut - ) external view returns (uint256 amount, bool stable); - - function getAmountsOut(uint256 amountIn, VeloRoute[] calldata routes) - external - view - returns (uint256[] memory amounts); -} - -contract VelodromeSampler is SamplerUtils, ApproximateBuys { - /// @dev Sample sell quotes from Velodrome - /// @param router Address of Velodrome router. - /// @param takerToken Address of the taker token (what to sell). - /// @param makerToken Address of the maker token (what to buy). - /// @param takerTokenAmounts Taker token sell amount for each sample (sorted in ascending order). - /// @return stable Whether the pool is a stable pool (vs volatile). - /// @return makerTokenAmounts Maker amounts bought at each taker token amount. - function sampleSellsFromVelodrome( - IVelodromeRouter router, - address takerToken, - address makerToken, - uint256[] memory takerTokenAmounts - ) public view returns (bool stable, uint256[] memory makerTokenAmounts) { - _assertValidPair(makerToken, takerToken); - uint256 numSamples = takerTokenAmounts.length; - makerTokenAmounts = new uint256[](numSamples); - - // Sampling should not mix stable and volatile pools. - // Find the most liquid pool based on max(takerTokenAmounts) and stick with it. - stable = _isMostLiquidPoolStablePool(router, takerToken, makerToken, takerTokenAmounts); - VeloRoute[] memory routes = new VeloRoute[](1); - routes[0] = VeloRoute({ from: takerToken, to: makerToken, stable: stable }); - - for (uint256 i = 0; i < numSamples; i++) { - makerTokenAmounts[i] = router.getAmountsOut(takerTokenAmounts[i], routes)[1]; - // Break early if there are 0 amounts - if (makerTokenAmounts[i] == 0) { - break; - } - } - } - - /// @dev Sample buy quotes from Velodrome. - /// @param router Address of Velodrome router. - /// @param takerToken Address of the taker token (what to sell). - /// @param makerToken Address of the maker token (what to buy). - /// @param makerTokenAmounts Maker token buy amount for each sample. - /// @return stable Whether the pool is a stable pool (vs volatile). - /// @return takerTokenAmounts Taker amounts sold at each maker token amount. - function sampleBuysFromVelodrome( - IVelodromeRouter router, - address takerToken, - address makerToken, - uint256[] memory makerTokenAmounts - ) public view returns (bool stable, uint256[] memory takerTokenAmounts) { - _assertValidPair(makerToken, takerToken); - - // Sampling should not mix stable and volatile pools. - // Find the most liquid pool based on the reverse swap (maker -> taker) and stick with it. - stable = _isMostLiquidPoolStablePool(router, makerToken, takerToken, makerTokenAmounts); - - takerTokenAmounts = _sampleApproximateBuys( - ApproximateBuyQuoteOpts({ - takerTokenData: abi.encode(router, VeloRoute({ from: takerToken, to: makerToken, stable: stable })), - makerTokenData: abi.encode(router, VeloRoute({ from: makerToken, to: takerToken, stable: stable })), - getSellQuoteCallback: _sampleSellForApproximateBuyFromVelodrome - }), - makerTokenAmounts - ); - } - - function _sampleSellForApproximateBuyFromVelodrome( - bytes memory takerTokenData, - bytes memory, /* makerTokenData */ - uint256 sellAmount - ) internal view returns (uint256) { - (IVelodromeRouter router, VeloRoute memory route) = abi.decode(takerTokenData, (IVelodromeRouter, VeloRoute)); - - VeloRoute[] memory routes = new VeloRoute[](1); - routes[0] = route; - return router.getAmountsOut(sellAmount, routes)[1]; - } - - /// @dev Returns whether the most liquid pool is a stable pool. - /// @param router Address of Velodrome router. - /// @param takerToken Address of the taker token (what to sell). - /// @param makerToken Address of the maker token (what to buy). - /// @param takerTokenAmounts Taker token buy amount for each sample (sorted in ascending order) - /// @return stable Whether the pool is a stable pool (vs volatile). - function _isMostLiquidPoolStablePool( - IVelodromeRouter router, - address takerToken, - address makerToken, - uint256[] memory takerTokenAmounts - ) internal view returns (bool stable) { - uint256 numSamples = takerTokenAmounts.length; - (, stable) = router.getAmountOut(takerTokenAmounts[numSamples - 1], takerToken, makerToken); - } -} diff --git a/packages/asset-swapper/contracts/src/WooPPSampler.sol b/packages/asset-swapper/contracts/src/WooPPSampler.sol deleted file mode 100644 index 4d40c6dde7..0000000000 --- a/packages/asset-swapper/contracts/src/WooPPSampler.sol +++ /dev/null @@ -1,121 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.6; -pragma experimental ABIEncoderV2; -import "./SamplerUtils.sol"; -import "./ApproximateBuys.sol"; - -interface IWooPP { - /// @dev get the quote token address (immutable) - /// @return address of quote token - function quoteToken() external view returns (address); - - /// @dev Query the amount for selling the base token amount. - /// @param baseToken the base token to sell - /// @param baseAmount the amount to sell - /// @return quoteAmount the swapped quote amount - function querySellBase(address baseToken, uint256 baseAmount) external view returns (uint256 quoteAmount); - - /// @dev Query the amount for selling the quote token. - /// @param baseToken the base token to receive (buy) - /// @param quoteAmount the amount to sell - /// @return baseAmount the swapped base token amount - function querySellQuote(address baseToken, uint256 quoteAmount) external view returns (uint256 baseAmount); -} - -contract WooPPSampler is SamplerUtils, ApproximateBuys{ - - function query( - uint amountIn, - address tokenIn, - address tokenOut, - address pool - ) internal view returns (uint256 amountOut) { - if (amountIn == 0) { - return 0; - } - address quoteToken = IWooPP(pool).quoteToken(); - if (tokenIn == quoteToken) { - amountOut = IWooPP(pool).querySellQuote(tokenOut, amountIn); - } else if (tokenOut == quoteToken) { - amountOut = IWooPP(pool).querySellBase(tokenIn, amountIn); - } else { - uint quoteAmount = IWooPP(pool).querySellBase(tokenIn, amountIn); - amountOut = IWooPP(pool).querySellQuote(tokenOut, quoteAmount); - } - } - - /// @dev Sample sell quotes from WooFI. - /// @param pool Address of the pool we are sampling from - /// @param takerToken Address of the taker token (what to sell). - /// @param makerToken Address of the maker token (what to buy). - /// @param takerTokenAmounts Taker token sell amount for each sample (sorted in ascending order). - /// @return makerTokenAmounts Maker amounts bought at each taker token - /// amount. - function sampleSellsFromWooPP( - address pool, - address takerToken, - address makerToken, - uint256[] memory takerTokenAmounts - ) - public - view - returns (uint256[] memory makerTokenAmounts) - { - uint256 numSamples = takerTokenAmounts.length; - makerTokenAmounts = new uint256[](numSamples); - for (uint256 i = 0; i < numSamples; i++) { - makerTokenAmounts[i] = query(takerTokenAmounts[i], takerToken, makerToken, pool); - - if (makerTokenAmounts[i] == 0) { - break; - } - } - } - - /// @dev Sample buy quotes from WooFI. - /// @param pool Address of the pool we are sampling from - /// @param takerToken Address of the taker token (what to sell). - /// @param makerToken Address of the maker token (what to buy). - /// @param makerTokenAmounts Maker token sell amount for each sample (sorted in ascending order). - /// @return takerTokenAmounts Taker amounts bought at each taker token - /// amount. - function sampleBuysFromWooPP( - address pool, - address takerToken, - address makerToken, - uint256[] memory makerTokenAmounts - ) - public - view - returns (uint256[] memory takerTokenAmounts) - { - uint256 numSamples = makerTokenAmounts.length; - takerTokenAmounts = _sampleApproximateBuys( - ApproximateBuyQuoteOpts({ - takerTokenData: abi.encode(pool,takerToken, makerToken), - makerTokenData: abi.encode(pool, makerToken, takerToken), - getSellQuoteCallback: _sampleSellForApproximateBuyFromWoofi - }), - makerTokenAmounts - ); - } - - function _sampleSellForApproximateBuyFromWoofi( - bytes memory takerTokenData, - bytes memory makerTokenData, - uint256 sellAmount - ) internal view returns (uint256) { - (address _pool, address _takerToken, address _makerToken) = abi.decode(takerTokenData, (address, address, address)); - (bool success, bytes memory resultData) = address(this).staticcall(abi.encodeWithSelector( - this.sampleSellsFromWooPP.selector, - _pool, - _takerToken, - _makerToken, - _toSingleValueArray(sellAmount) - )); - if(!success) { - return 0; - } - return abi.decode(resultData, (uint256[]))[0]; - } -} diff --git a/packages/asset-swapper/contracts/src/interfaces/IBalancer.sol b/packages/asset-swapper/contracts/src/interfaces/IBalancer.sol deleted file mode 100644 index 31a90776c5..0000000000 --- a/packages/asset-swapper/contracts/src/interfaces/IBalancer.sol +++ /dev/null @@ -1,44 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -/* - - Copyright 2020 ZeroEx Intl. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -pragma solidity ^0.6; - - -interface IBalancer { - function isBound(address t) external view returns (bool); - function getDenormalizedWeight(address token) external view returns (uint256); - function getBalance(address token) external view returns (uint256); - function getSwapFee() external view returns (uint256); - function calcOutGivenIn( - uint256 tokenBalanceIn, - uint256 tokenWeightIn, - uint256 tokenBalanceOut, - uint256 tokenWeightOut, - uint256 tokenAmountIn, - uint256 swapFee - ) external pure returns (uint256 tokenAmountOut); - function calcInGivenOut( - uint256 tokenBalanceIn, - uint256 tokenWeightIn, - uint256 tokenBalanceOut, - uint256 tokenWeightOut, - uint256 tokenAmountOut, - uint256 swapFee - ) external pure returns (uint256 tokenAmountIn); -} diff --git a/packages/asset-swapper/contracts/src/interfaces/IBalancerV2Vault.sol b/packages/asset-swapper/contracts/src/interfaces/IBalancerV2Vault.sol deleted file mode 100644 index 14f45f19b8..0000000000 --- a/packages/asset-swapper/contracts/src/interfaces/IBalancerV2Vault.sol +++ /dev/null @@ -1,54 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -/* - - Copyright 2020 ZeroEx Intl. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -pragma solidity ^0.6; -pragma experimental ABIEncoderV2; - -/// @dev Minimal Balancer V2 Vault interface -/// for documentation refer to https://github.com/balancer-labs/balancer-core-v2/blob/master/contracts/vault/interfaces/IVault.sol -interface IBalancerV2Vault { - enum SwapKind { GIVEN_IN, GIVEN_OUT } - - struct BatchSwapStep { - bytes32 poolId; - uint256 assetInIndex; - uint256 assetOutIndex; - uint256 amount; - bytes userData; - } - - struct FundManagement { - address sender; - bool fromInternalBalance; - address payable recipient; - bool toInternalBalance; - } - - struct BalancerV2PoolInfo { - bytes32 poolId; - address vault; - } - - function queryBatchSwap( - SwapKind kind, - BatchSwapStep[] calldata swaps, - address[] calldata assets, - FundManagement calldata funds - ) external returns (int256[] memory assetDeltas); -} diff --git a/packages/asset-swapper/contracts/src/interfaces/IBancor.sol b/packages/asset-swapper/contracts/src/interfaces/IBancor.sol deleted file mode 100644 index df64503d80..0000000000 --- a/packages/asset-swapper/contracts/src/interfaces/IBancor.sol +++ /dev/null @@ -1,33 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -/* - - Copyright 2020 ZeroEx Intl. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -pragma solidity ^0.6; - - -interface IBancor {} - -interface IBancorNetwork { - function conversionPath(address _sourceToken, address _targetToken) external view returns (address[] memory); - function rateByPath(address[] memory _path, uint256 _amount) external view returns (uint256); -} - -interface IBancorRegistry { - function getAddress(bytes32 _contractName) external view returns (address); - function BANCOR_NETWORK() external view returns (bytes32); -} diff --git a/packages/asset-swapper/contracts/src/interfaces/IBancorV3.sol b/packages/asset-swapper/contracts/src/interfaces/IBancorV3.sol deleted file mode 100644 index 0b5bed9f8f..0000000000 --- a/packages/asset-swapper/contracts/src/interfaces/IBancorV3.sol +++ /dev/null @@ -1,43 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -/* - - Copyright 2022 ZeroEx Intl. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -pragma solidity ^0.6; -pragma experimental ABIEncoderV2; - -interface IBancorV3 { - - /** - * @dev returns the output amount when trading by providing the source amount - */ - function tradeOutputBySourceAmount( - address sourceToken, - address targetToken, - uint256 sourceAmount - ) external view returns (uint256); - - /** - * @dev returns the input amount when trading by providing the target amount - */ - function tradeInputByTargetAmount( - address sourceToken, - address targetToken, - uint256 targetAmount - ) external view returns (uint256); - -} \ No newline at end of file diff --git a/packages/asset-swapper/contracts/src/interfaces/ICurve.sol b/packages/asset-swapper/contracts/src/interfaces/ICurve.sol deleted file mode 100644 index 3720ddcfdc..0000000000 --- a/packages/asset-swapper/contracts/src/interfaces/ICurve.sol +++ /dev/null @@ -1,70 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -/* - - Copyright 2020 ZeroEx Intl. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -pragma solidity ^0.6; - -interface ICurve { - - /// @dev Sell `sellAmount` of `fromToken` token and receive `toToken` token. - /// This function exists on later versions of Curve (USDC/DAI/USDT) - /// @param i The token index being sold. - /// @param j The token index being bought. - /// @param sellAmount The amount of token being bought. - /// @param minBuyAmount The minimum buy amount of the token being bought. - function exchange_underlying( - int128 i, - int128 j, - uint256 sellAmount, - uint256 minBuyAmount - ) - external; - - /// @dev Get the amount of `toToken` by selling `sellAmount` of `fromToken` - /// @param i The token index being sold. - /// @param j The token index being bought. - /// @param sellAmount The amount of token being bought. - function get_dy_underlying( - int128 i, - int128 j, - uint256 sellAmount - ) - external - returns (uint256 dy); - - /// @dev Get the amount of `fromToken` by buying `buyAmount` of `toToken` - /// This function exists on later versions of Curve (USDC/DAI/USDT) - /// @param i The token index being sold. - /// @param j The token index being bought. - /// @param buyAmount The amount of token being bought. - function get_dx_underlying( - int128 i, - int128 j, - uint256 buyAmount - ) - external - returns (uint256 dx); - - /// @dev Get the underlying token address from the token index - /// @param i The token index. - function underlying_coins( - int128 i - ) - external - returns (address tokenAddress); -} diff --git a/packages/asset-swapper/contracts/src/interfaces/IGMX.sol b/packages/asset-swapper/contracts/src/interfaces/IGMX.sol deleted file mode 100644 index 12b1c3d87c..0000000000 --- a/packages/asset-swapper/contracts/src/interfaces/IGMX.sol +++ /dev/null @@ -1,23 +0,0 @@ -pragma solidity ^0.6; -pragma experimental ABIEncoderV2; - -interface IGMX { - function getMaxAmountIn(IVault _vault, address _tokenIn, address _tokenOut) - external - view - returns (uint256); - - function getAmountOut(IVault _vault, address _tokenIn, address _tokenOut, uint256 _amountIn) - external - view - returns (uint256, uint256); -} - -interface IVault { - function getFeeBasisPoints(address _token, uint256 _usdgDelta, uint256 _feeBasisPoints, uint256 _taxBasisPoints, bool _increment) external view returns (uint256); - function stableSwapFeeBasisPoints() external view returns (uint256); - function stableTokens(address _token) external view returns (bool); - function tokenDecimals(address _token) external view returns (uint256); - function getMaxPrice(address _token) external view returns (uint256); - function getMinPrice(address _token) external view returns (uint256); -} \ No newline at end of file diff --git a/packages/asset-swapper/contracts/src/interfaces/IMStable.sol b/packages/asset-swapper/contracts/src/interfaces/IMStable.sol deleted file mode 100644 index cae1bbb99b..0000000000 --- a/packages/asset-swapper/contracts/src/interfaces/IMStable.sol +++ /dev/null @@ -1,33 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -/* - - Copyright 2020 ZeroEx Intl. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -pragma solidity ^0.6; - - -interface IMStable { - - function getSwapOutput( - address _input, - address _output, - uint256 _quantity - ) - external - view - returns (uint256 swapOutput); -} diff --git a/packages/asset-swapper/contracts/src/interfaces/IMooniswap.sol b/packages/asset-swapper/contracts/src/interfaces/IMooniswap.sol deleted file mode 100644 index 93a8fff596..0000000000 --- a/packages/asset-swapper/contracts/src/interfaces/IMooniswap.sol +++ /dev/null @@ -1,38 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -/* - - Copyright 2020 ZeroEx Intl. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -pragma solidity ^0.6; - - -interface IMooniswapRegistry { - - function pools(address token1, address token2) external view returns(address); -} - -interface IMooniswap { - - function getReturn( - address fromToken, - address destToken, - uint256 amount - ) - external - view - returns(uint256 returnAmount); -} diff --git a/packages/asset-swapper/contracts/src/interfaces/IMultiBridge.sol b/packages/asset-swapper/contracts/src/interfaces/IMultiBridge.sol deleted file mode 100644 index 1bad277f59..0000000000 --- a/packages/asset-swapper/contracts/src/interfaces/IMultiBridge.sol +++ /dev/null @@ -1,59 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -/* - - Copyright 2020 ZeroEx Intl. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -pragma solidity ^0.6; - - -interface IMultiBridge { - - /// @dev Transfers `amount` of the ERC20 `tokenAddress` from `from` to `to`. - /// @param tokenAddress The address of the ERC20 token to transfer. - /// @param from Address to transfer asset from. - /// @param to Address to transfer asset to. - /// @param amount Amount of asset to transfer. - /// @param bridgeData Arbitrary asset data needed by the bridge contract. - /// @return success The magic bytes `0xdc1600f3` if successful. - function bridgeTransferFrom( - address tokenAddress, - address from, - address to, - uint256 amount, - bytes calldata bridgeData - ) - external - returns (bytes4 success); - - /// @dev Quotes the amount of `makerToken` that would be obtained by - /// selling `sellAmount` of `takerToken`. - /// @param takerToken Address of the taker token (what to sell). - /// @param intermediateToken The address of the intermediate token to - /// use in an indirect route. - /// @param makerToken Address of the maker token (what to buy). - /// @param sellAmount Amount of `takerToken` to sell. - /// @return makerTokenAmount Amount of `makerToken` that would be obtained. - function getSellQuote( - address takerToken, - address intermediateToken, - address makerToken, - uint256 sellAmount - ) - external - view - returns (uint256 makerTokenAmount); -} diff --git a/packages/asset-swapper/contracts/src/interfaces/IPlatypus.sol b/packages/asset-swapper/contracts/src/interfaces/IPlatypus.sol deleted file mode 100644 index d764de70f4..0000000000 --- a/packages/asset-swapper/contracts/src/interfaces/IPlatypus.sol +++ /dev/null @@ -1,11 +0,0 @@ -pragma solidity ^0.6; - -interface IPlatypus { - function quotePotentialSwap( - address fromToken, - address toToken, - uint256 fromAmount - ) external view returns (uint256 potentialOutcome, uint256 haircut); - - function assetOf(address token) external view returns (address); -} \ No newline at end of file diff --git a/packages/asset-swapper/contracts/src/interfaces/IShell.sol b/packages/asset-swapper/contracts/src/interfaces/IShell.sol deleted file mode 100644 index 103b2778af..0000000000 --- a/packages/asset-swapper/contracts/src/interfaces/IShell.sol +++ /dev/null @@ -1,43 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -/* - - Copyright 2020 ZeroEx Intl. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -pragma solidity ^0.6; - - -interface IShell { - - function viewOriginSwap ( - address from, - address to, - uint256 fromAmount - ) - external - view - returns (uint256 toAmount); - - function viewTargetSwap ( - address from, - address to, - uint256 toAmount - ) - external - view - returns (uint256 fromAmount); -} - diff --git a/packages/asset-swapper/contracts/src/interfaces/IUniswapExchangeQuotes.sol b/packages/asset-swapper/contracts/src/interfaces/IUniswapExchangeQuotes.sol deleted file mode 100644 index 5143824643..0000000000 --- a/packages/asset-swapper/contracts/src/interfaces/IUniswapExchangeQuotes.sol +++ /dev/null @@ -1,52 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -/* - - Copyright 2020 ZeroEx Intl. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -pragma solidity ^0.6; - - -interface IUniswapExchangeQuotes { - - function getEthToTokenInputPrice( - uint256 ethSold - ) - external - view - returns (uint256 tokensBought); - - function getEthToTokenOutputPrice( - uint256 tokensBought - ) - external - view - returns (uint256 ethSold); - - function getTokenToEthInputPrice( - uint256 tokensSold - ) - external - view - returns (uint256 ethBought); - - function getTokenToEthOutputPrice( - uint256 ethBought - ) - external - view - returns (uint256 tokensSold); -} diff --git a/packages/asset-swapper/contracts/src/interfaces/IUniswapV2Router01.sol b/packages/asset-swapper/contracts/src/interfaces/IUniswapV2Router01.sol deleted file mode 100644 index ab63971992..0000000000 --- a/packages/asset-swapper/contracts/src/interfaces/IUniswapV2Router01.sol +++ /dev/null @@ -1,34 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -/* - - Copyright 2020 ZeroEx Intl. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -pragma solidity ^0.6; - - -interface IUniswapV2Router01 { - - function getAmountsOut(uint256 amountIn, address[] calldata path) - external - view - returns (uint256[] memory amounts); - - function getAmountsIn(uint256 amountOut, address[] calldata path) - external - view - returns (uint256[] memory amounts); -} diff --git a/packages/asset-swapper/contracts/test/TestNativeOrderSampler.sol b/packages/asset-swapper/contracts/test/TestNativeOrderSampler.sol deleted file mode 100644 index 39cb0b2af0..0000000000 --- a/packages/asset-swapper/contracts/test/TestNativeOrderSampler.sol +++ /dev/null @@ -1,135 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -/* - - Copyright 2019 ZeroEx Intl. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ -pragma solidity ^0.6; -pragma experimental ABIEncoderV2; - -import "../src/NativeOrderSampler.sol"; -import "../src/UtilitySampler.sol"; - - -contract TestNativeOrderSamplerToken { - mapping (address => uint256) public balanceOf; - mapping (address => mapping(address => uint256)) public allowance; - - function setBalanceAndAllowance( - address owner, - address spender, - uint256 balance, - uint256 allowance_ - ) - external - { - balanceOf[owner] = balance; - allowance[owner][spender] = allowance_; - } -} - -contract TestNativeOrderSampler is - NativeOrderSampler, - UtilitySampler -{ - uint8 private constant MAX_ORDER_STATUS = uint8(IExchange.OrderStatus.CANCELLED) + 1; - bytes32 private constant VALID_SIGNATURE_HASH = bytes32(hex"01"); - - function createTokens(uint256 count) - external - returns (TestNativeOrderSamplerToken[] memory tokens) - { - tokens = new TestNativeOrderSamplerToken[](count); - for (uint256 i = 0; i < count; ++i) { - tokens[i] = new TestNativeOrderSamplerToken(); - } - } - - function setTokenBalanceAndAllowance( - TestNativeOrderSamplerToken token, - address owner, - address spender, - uint256 balance, - uint256 allowance - ) - external - { - token.setBalanceAndAllowance(owner, spender, balance, allowance); - } - - // IExchange.getLimitOrderRelevantState() - function getLimitOrderRelevantState( - IExchange.LimitOrder memory order, - IExchange.Signature calldata signature - ) - external - view - returns ( - IExchange.OrderInfo memory orderInfo, - uint128 actualFillableTakerTokenAmount, - bool isSignatureValid - ) - { - // The order salt determines everything. - orderInfo.orderHash = keccak256(abi.encode(order.salt)); - if (uint8(order.salt) == 0xFF) { - orderInfo.status = IExchange.OrderStatus.FILLED; - } else { - orderInfo.status = IExchange.OrderStatus.FILLABLE; - } - - isSignatureValid = signature.r == VALID_SIGNATURE_HASH; - - // The expiration time is the filled taker asset amount. - orderInfo.takerTokenFilledAmount = uint128(order.expiry); - - // Calculate how much is fillable in maker terms given the filled taker amount - uint256 fillableMakerTokenAmount = LibMathV06.getPartialAmountFloor( - uint256( - order.takerAmount - - orderInfo.takerTokenFilledAmount - ), - uint256(order.takerAmount), - uint256(order.makerAmount) - ); - - // Take the min of the balance/allowance and the fillable maker amount - fillableMakerTokenAmount = LibSafeMathV06.min256( - fillableMakerTokenAmount, - _getSpendableERC20BalanceOf(order.makerToken, order.maker) - ); - - // Convert to taker terms - actualFillableTakerTokenAmount = LibMathV06.getPartialAmountCeil( - fillableMakerTokenAmount, - uint256(order.makerAmount), - uint256(order.takerAmount) - ).safeDowncastToUint128(); - } - - function _getSpendableERC20BalanceOf( - IERC20TokenV06 token, - address owner - ) - internal - view - returns (uint256) - { - return LibSafeMathV06.min256( - token.allowance(owner, address(this)), - token.balanceOf(owner) - ); - } -} diff --git a/packages/asset-swapper/coverage/.gitkeep b/packages/asset-swapper/coverage/.gitkeep deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/packages/asset-swapper/docs/reference.mdx b/packages/asset-swapper/docs/reference.mdx deleted file mode 100644 index 52b4cb9576..0000000000 --- a/packages/asset-swapper/docs/reference.mdx +++ /dev/null @@ -1,3368 +0,0 @@ -# Interface: Web3JsV1Provider - -Web3.js version 1 provider interface -This provider interface was implemented in the pre-1.0Beta releases for Web3.js. -This interface allowed sending synchonous requests, support for which was later dropped. - - -## Methods - -### send - -▸ **send**(`payload`: [JSONRPCRequestPayload](_ethereum_types_src_index_.jsonrpcrequestpayload.md)): *[JSONRPCResponsePayload](#class-jsonrpcresponsepayload)* - -*Defined in [ethereum-types/src/index.ts:45](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/ethereum-types/src/index.ts#L45)* - -**Parameters:** - -Name | Type | ------- | ------ | -`payload` | [JSONRPCRequestPayload](#class-jsonrpcrequestpayload) | - -**Returns:** *[JSONRPCResponsePayload](#class-jsonrpcresponsepayload)* - -___ - -### sendAsync - -▸ **sendAsync**(`payload`: [JSONRPCRequestPayload](_ethereum_types_src_index_.jsonrpcrequestpayload.md), `callback`: [JSONRPCErrorCallback](#jsonrpcerrorcallback)): *void* - -*Defined in [ethereum-types/src/index.ts:44](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/ethereum-types/src/index.ts#L44)* - -**Parameters:** - -Name | Type | ------- | ------ | -`payload` | [JSONRPCRequestPayload](#class-jsonrpcrequestpayload) | -`callback` | [JSONRPCErrorCallback](#jsonrpcerrorcallback) | - -**Returns:** *void* - -
- -# Class: SwapQuoteConsumer - - -## Implements - -* [SwapQuoteConsumerBase](#interface-swapquoteconsumerbase)‹*[SmartContractParams](#smartcontractparams)*› - - -## Constructors - - - -\+ **new SwapQuoteConsumer**(`supportedProvider`: [SupportedProvider](#supportedprovider), `options`: `Partial`): *[SwapQuoteConsumer](#class-swapquoteconsumer)* - -*Defined in [asset-swapper/src/quote_consumers/swap_quote_consumer.ts:39](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/quote_consumers/swap_quote_consumer.ts#L39)* - -**Parameters:** - -Name | Type | Default | ------- | ------ | ------ | -`supportedProvider` | [SupportedProvider](#supportedprovider) | - | -`options` | `Partial` | {} | - -**Returns:** *[SwapQuoteConsumer](#class-swapquoteconsumer)* - -## Properties - -### chainId - -• **chainId**: *number* - -*Defined in [asset-swapper/src/quote_consumers/swap_quote_consumer.ts:28](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/quote_consumers/swap_quote_consumer.ts#L28)* - -___ - -### provider - -• **provider**: *`ZeroExProvider`* - -*Defined in [asset-swapper/src/quote_consumers/swap_quote_consumer.ts:27](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/quote_consumers/swap_quote_consumer.ts#L27)* - -## Methods - -### executeSwapQuoteOrThrowAsync - -▸ **executeSwapQuoteOrThrowAsync**(`quote`: [SwapQuote](#swapquote), `opts`: `Partial`): *`Promise`* - -*Implementation of [SwapQuoteConsumerBase](#interface-swapquoteconsumerbase)* - -*Defined in [asset-swapper/src/quote_consumers/swap_quote_consumer.ts:89](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/quote_consumers/swap_quote_consumer.ts#L89)* - -Given a SwapQuote and desired rate (in takerAsset), attempt to execute the swap with 0x extension or exchange contract. - -**Parameters:** - -Name | Type | Default | Description | ------- | ------ | ------ | ------ | -`quote` | [SwapQuote](#swapquote) | - | An object that conforms to SwapQuote. See type definition for more information. | -`opts` | `Partial` | {} | Options for getting CalldataInfo. See type definition for more information. | - -**Returns:** *`Promise`* - -___ - -### getCalldataOrThrowAsync - -▸ **getCalldataOrThrowAsync**(`quote`: [SwapQuote](#swapquote), `opts`: `Partial`): *`Promise`* - -*Implementation of [SwapQuoteConsumerBase](#interface-swapquoteconsumerbase)* - -*Defined in [asset-swapper/src/quote_consumers/swap_quote_consumer.ts:61](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/quote_consumers/swap_quote_consumer.ts#L61)* - -Given a SwapQuote, returns 'CalldataInfo' for a 0x extesion or exchange call. See type definition of CalldataInfo for more information. - -**Parameters:** - -Name | Type | Default | Description | ------- | ------ | ------ | ------ | -`quote` | [SwapQuote](#swapquote) | - | An object that conforms to SwapQuote. See type definition for more information. | -`opts` | `Partial` | {} | Options for getting SmartContractParams. See type definition for more information. | - -**Returns:** *`Promise`* - -___ - -### getOptimalExtensionContractTypeAsync - -▸ **getOptimalExtensionContractTypeAsync**(`quote`: [SwapQuote](#swapquote), `opts`: `Partial`): *`Promise`* - -*Defined in [asset-swapper/src/quote_consumers/swap_quote_consumer.ts:103](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/quote_consumers/swap_quote_consumer.ts#L103)* - -Given a SwapQuote, returns optimal 0x protocol interface (extension or no extension) to perform the swap. - -**Parameters:** - -Name | Type | Default | Description | ------- | ------ | ------ | ------ | -`quote` | [SwapQuote](#swapquote) | - | An object that conforms to SwapQuote. See type definition for more information. | -`opts` | `Partial` | {} | Options for getting optimal exteion contract to fill quote. See type definition for more information. | - -**Returns:** *`Promise`* - -___ - -### getSmartContractParamsOrThrowAsync - -▸ **getSmartContractParamsOrThrowAsync**(`quote`: [SwapQuote](#swapquote), `opts`: `Partial`): *`Promise>`* - -*Implementation of [SwapQuoteConsumerBase](#interface-swapquoteconsumerbase)* - -*Defined in [asset-swapper/src/quote_consumers/swap_quote_consumer.ts:75](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/quote_consumers/swap_quote_consumer.ts#L75)* - -Given a SwapQuote, returns 'SmartContractParamsInfo' for a 0x extension or exchange call. See type definition of SmartContractParamsInfo for more information. - -**Parameters:** - -Name | Type | Default | Description | ------- | ------ | ------ | ------ | -`quote` | [SwapQuote](#swapquote) | - | An object that conforms to SwapQuote. See type definition for more information. | -`opts` | `Partial` | {} | Options for getting SmartContractParams. See type definition for more information. | - -**Returns:** *`Promise>`* - -___ - -### `Static` getSwapQuoteConsumer - -▸ **getSwapQuoteConsumer**(`supportedProvider`: [SupportedProvider](#supportedprovider), `options`: `Partial`): *[SwapQuoteConsumer](#class-swapquoteconsumer)* - -*Defined in [asset-swapper/src/quote_consumers/swap_quote_consumer.ts:34](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/quote_consumers/swap_quote_consumer.ts#L34)* - -**Parameters:** - -Name | Type | Default | ------- | ------ | ------ | -`supportedProvider` | [SupportedProvider](#supportedprovider) | - | -`options` | `Partial` | {} | - -**Returns:** *[SwapQuoteConsumer](#class-swapquoteconsumer)* - -
- -# Class: SwapQuoter - - -## Constructors - - - -\+ **new SwapQuoter**(`supportedProvider`: [SupportedProvider](#supportedprovider), `orderbook`: `Orderbook`, `options`: `Partial`): *[SwapQuoter](#class-swapquoter)* - -*Defined in [asset-swapper/src/swap_quoter.ts:129](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/swap_quoter.ts#L129)* - -Instantiates a new SwapQuoter instance - -**Parameters:** - -Name | Type | Default | Description | ------- | ------ | ------ | ------ | -`supportedProvider` | [SupportedProvider](#supportedprovider) | - | The Provider instance you would like to use for interacting with the Ethereum network. | -`orderbook` | `Orderbook` | - | An object that conforms to Orderbook, see type for definition. | -`options` | `Partial` | {} | Initialization options for the SwapQuoter. See type definition for details. | - -**Returns:** *[SwapQuoter](#class-swapquoter)* - -An instance of SwapQuoter - -## Properties - -### expiryBufferMs - -• **expiryBufferMs**: *number* - -*Defined in [asset-swapper/src/swap_quoter.ts:32](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/swap_quoter.ts#L32)* - -___ - -### orderbook - -• **orderbook**: *`Orderbook`* - -*Defined in [asset-swapper/src/swap_quoter.ts:31](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/swap_quoter.ts#L31)* - -___ - -### permittedOrderFeeTypes - -• **permittedOrderFeeTypes**: *`Set`* - -*Defined in [asset-swapper/src/swap_quoter.ts:33](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/swap_quoter.ts#L33)* - -___ - -### provider - -• **provider**: *`ZeroExProvider`* - -*Defined in [asset-swapper/src/swap_quoter.ts:30](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/swap_quoter.ts#L30)* - -## Methods - -### destroyAsync - -▸ **destroyAsync**(): *`Promise`* - -*Defined in [asset-swapper/src/swap_quoter.ts:393](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/swap_quoter.ts#L393)* - -Destroys any subscriptions or connections. - -**Returns:** *`Promise`* - -___ - -### getAvailableMakerAssetDatasAsync - -▸ **getAvailableMakerAssetDatasAsync**(`takerAssetData`: string): *`Promise`* - -*Defined in [asset-swapper/src/swap_quoter.ts:321](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/swap_quoter.ts#L321)* - -Get the asset data of all assets that are purchaseable with takerAssetData in the order provider passed in at init. - -**Parameters:** - -Name | Type | ------- | ------ | -`takerAssetData` | string | - -**Returns:** *`Promise`* - -An array of asset data strings that are purchaseable with takerAssetData. - -___ - -### getAvailableTakerAssetDatasAsync - -▸ **getAvailableTakerAssetDatasAsync**(`makerAssetData`: string): *`Promise`* - -*Defined in [asset-swapper/src/swap_quoter.ts:306](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/swap_quoter.ts#L306)* - -Get the asset data of all assets that can be used to purchase makerAssetData in the order provider passed in at init. - -**Parameters:** - -Name | Type | ------- | ------ | -`makerAssetData` | string | - -**Returns:** *`Promise`* - -An array of asset data strings that can purchase makerAssetData. - -___ - -### getEtherTokenAssetDataOrThrowAsync - -▸ **getEtherTokenAssetDataOrThrowAsync**(): *`Promise`* - -*Defined in [asset-swapper/src/swap_quoter.ts:400](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/swap_quoter.ts#L400)* - -Utility function to get assetData for Ether token. - -**Returns:** *`Promise`* - -___ - -### getLiquidityForMakerTakerAssetDataPairAsync - -▸ **getLiquidityForMakerTakerAssetDataPairAsync**(`makerAssetData`: string, `takerAssetData`: string): *`Promise`* - -*Defined in [asset-swapper/src/swap_quoter.ts:281](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/swap_quoter.ts#L281)* - -Returns information about available liquidity for an asset -Does not factor in slippage or fees - -**Parameters:** - -Name | Type | Description | ------- | ------ | ------ | -`makerAssetData` | string | The makerAssetData of the desired asset to swap for (for more info: https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md). | -`takerAssetData` | string | The takerAssetData of the asset to swap makerAssetData for (for more info: https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md). | - -**Returns:** *`Promise`* - -An object that conforms to LiquidityForTakerMakerAssetDataPair that satisfies the request. See type definition for more information. - -___ - -### getMarketBuySwapQuoteAsync - -▸ **getMarketBuySwapQuoteAsync**(`makerTokenAddress`: string, `takerTokenAddress`: string, `makerAssetBuyAmount`: `BigNumber`, `options`: `Partial`): *`Promise`* - -*Defined in [asset-swapper/src/swap_quoter.ts:223](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/swap_quoter.ts#L223)* - -Get a `SwapQuote` containing all information relevant to fulfilling a swap between a desired ERC20 token address and ERC20 owned by a provided address. -You can then pass the `SwapQuote` to a `SwapQuoteConsumer` to execute a buy, or process SwapQuote for on-chain consumption. - -**Parameters:** - -Name | Type | Default | Description | ------- | ------ | ------ | ------ | -`makerTokenAddress` | string | - | The address of the maker asset | -`takerTokenAddress` | string | - | The address of the taker asset | -`makerAssetBuyAmount` | `BigNumber` | - | The amount of maker asset to swap for. | -`options` | `Partial` | {} | Options for the request. See type definition for more information. | - -**Returns:** *`Promise`* - -An object that conforms to SwapQuote that satisfies the request. See type definition for more information. - -___ - -### getMarketBuySwapQuoteForAssetDataAsync - -▸ **getMarketBuySwapQuoteForAssetDataAsync**(`makerAssetData`: string, `takerAssetData`: string, `makerAssetBuyAmount`: `BigNumber`, `options`: `Partial`): *`Promise`* - -*Defined in [asset-swapper/src/swap_quoter.ts:198](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/swap_quoter.ts#L198)* - -Get a `SwapQuote` containing all information relevant to fulfilling a swap between a desired ERC20 token address and ERC20 owned by a provided address. -You can then pass the `SwapQuote` to a `SwapQuoteConsumer` to execute a buy, or process SwapQuote for on-chain consumption. - -**Parameters:** - -Name | Type | Default | Description | ------- | ------ | ------ | ------ | -`makerAssetData` | string | - | The makerAssetData of the desired asset to swap for (for more info: https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md). | -`takerAssetData` | string | - | The takerAssetData of the asset to swap makerAssetData for (for more info: https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md). | -`makerAssetBuyAmount` | `BigNumber` | - | The amount of maker asset to swap for. | -`options` | `Partial` | {} | Options for the request. See type definition for more information. | - -**Returns:** *`Promise`* - -An object that conforms to SwapQuote that satisfies the request. See type definition for more information. - -___ - -### getMarketSellSwapQuoteAsync - -▸ **getMarketSellSwapQuoteAsync**(`makerTokenAddress`: string, `takerTokenAddress`: string, `takerAssetSellAmount`: `BigNumber`, `options`: `Partial`): *`Promise`* - -*Defined in [asset-swapper/src/swap_quoter.ts:253](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/swap_quoter.ts#L253)* - -Get a `SwapQuote` containing all information relevant to fulfilling a swap between a desired ERC20 token address and ERC20 owned by a provided address. -You can then pass the `SwapQuote` to a `SwapQuoteConsumer` to execute a buy, or process SwapQuote for on-chain consumption. - -**Parameters:** - -Name | Type | Default | Description | ------- | ------ | ------ | ------ | -`makerTokenAddress` | string | - | The address of the maker asset | -`takerTokenAddress` | string | - | The address of the taker asset | -`takerAssetSellAmount` | `BigNumber` | - | The amount of taker asset to sell. | -`options` | `Partial` | {} | Options for the request. See type definition for more information. | - -**Returns:** *`Promise`* - -An object that conforms to SwapQuote that satisfies the request. See type definition for more information. - -___ - -### getMarketSellSwapQuoteForAssetDataAsync - -▸ **getMarketSellSwapQuoteForAssetDataAsync**(`makerAssetData`: string, `takerAssetData`: string, `takerAssetSellAmount`: `BigNumber`, `options`: `Partial`): *`Promise`* - -*Defined in [asset-swapper/src/swap_quoter.ts:172](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/swap_quoter.ts#L172)* - -Get a `SwapQuote` containing all information relevant to fulfilling a swap between a desired ERC20 token address and ERC20 owned by a provided address. -You can then pass the `SwapQuote` to a `SwapQuoteConsumer` to execute a buy, or process SwapQuote for on-chain consumption. - -**Parameters:** - -Name | Type | Default | Description | ------- | ------ | ------ | ------ | -`makerAssetData` | string | - | The makerAssetData of the desired asset to swap for (for more info: https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md). | -`takerAssetData` | string | - | The takerAssetData of the asset to swap makerAssetData for (for more info: https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md). | -`takerAssetSellAmount` | `BigNumber` | - | The amount of taker asset to swap for. | -`options` | `Partial` | {} | Options for the request. See type definition for more information. | - -**Returns:** *`Promise`* - -An object that conforms to SwapQuote that satisfies the request. See type definition for more information. - -___ - -### getPrunedSignedOrdersAsync - -▸ **getPrunedSignedOrdersAsync**(`makerAssetData`: string, `takerAssetData`: string): *`Promise`* - -*Defined in [asset-swapper/src/swap_quoter.ts:355](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/swap_quoter.ts#L355)* - -Grab orders from the order provider, prunes for valid orders with provided OrderPruner options - -**Parameters:** - -Name | Type | Description | ------- | ------ | ------ | -`makerAssetData` | string | The makerAssetData of the desired asset to swap for (for more info: https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md). | -`takerAssetData` | string | The takerAssetData of the asset to swap makerAssetData for (for more info: https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md). | - -**Returns:** *`Promise`* - -___ - -### isSwapQuoteFillableByTakerAddressAsync - -▸ **isSwapQuoteFillableByTakerAddressAsync**(`swapQuote`: [SwapQuote](#swapquote), `takerAddress`: string): *`Promise<[boolean, boolean]>`* - -*Defined in [asset-swapper/src/swap_quoter.ts:376](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/swap_quoter.ts#L376)* - -Util function to check if takerAddress's allowance is enough for 0x exchange contracts to conduct the swap specified by the swapQuote. - -**Parameters:** - -Name | Type | Description | ------- | ------ | ------ | -`swapQuote` | [SwapQuote](#swapquote) | The swapQuote in question to check enough allowance enabled for 0x exchange contracts to conduct the swap. | -`takerAddress` | string | The address of the taker of the provided swapQuote | - -**Returns:** *`Promise<[boolean, boolean]>`* - -___ - -### isTakerMakerAssetDataPairAvailableAsync - -▸ **isTakerMakerAssetDataPairAvailableAsync**(`makerAssetData`: string, `takerAssetData`: string): *`Promise`* - -*Defined in [asset-swapper/src/swap_quoter.ts:338](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/swap_quoter.ts#L338)* - -Validates the taker + maker asset pair is available from the order provider provided to `SwapQuote`. - -**Parameters:** - -Name | Type | Description | ------- | ------ | ------ | -`makerAssetData` | string | The makerAssetData of the desired asset to swap for (for more info: https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md). | -`takerAssetData` | string | The takerAssetData of the asset to swap makerAssetData for (for more info: https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md). | - -**Returns:** *`Promise`* - -A boolean on if the taker, maker pair exists - -___ - -### `Static` getSwapQuoterForMeshEndpoint - -▸ **getSwapQuoterForMeshEndpoint**(`supportedProvider`: [SupportedProvider](#supportedprovider), `meshEndpoint`: string, `options`: `Partial`): *[SwapQuoter](#class-swapquoter)* - -*Defined in [asset-swapper/src/swap_quoter.ts:116](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/swap_quoter.ts#L116)* - -Instantiates a new SwapQuoter instance given a 0x Mesh endpoint. This pulls all available liquidity stored in Mesh - -**Parameters:** - -Name | Type | Default | Description | ------- | ------ | ------ | ------ | -`supportedProvider` | [SupportedProvider](#supportedprovider) | - | The Provider instance you would like to use for interacting with the Ethereum network. | -`meshEndpoint` | string | - | The standard relayer API base HTTP url you would like to source orders from. | -`options` | `Partial` | {} | Initialization options for the SwapQuoter. See type definition for details. | - -**Returns:** *[SwapQuoter](#class-swapquoter)* - -An instance of SwapQuoter - -___ - -### `Static` getSwapQuoterForProvidedOrders - -▸ **getSwapQuoterForProvidedOrders**(`supportedProvider`: [SupportedProvider](#supportedprovider), `orders`: `SignedOrder`[], `options`: `Partial`): *[SwapQuoter](#class-swapquoter)* - -*Defined in [asset-swapper/src/swap_quoter.ts:44](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/swap_quoter.ts#L44)* - -Instantiates a new SwapQuoter instance given existing liquidity in the form of orders and feeOrders. - -**Parameters:** - -Name | Type | Default | Description | ------- | ------ | ------ | ------ | -`supportedProvider` | [SupportedProvider](#supportedprovider) | - | The Provider instance you would like to use for interacting with the Ethereum network. | -`orders` | `SignedOrder`[] | - | A non-empty array of objects that conform to SignedOrder. All orders must have the same makerAssetData and takerAssetData. | -`options` | `Partial` | {} | Initialization options for the SwapQuoter. See type definition for details. | - -**Returns:** *[SwapQuoter](#class-swapquoter)* - -An instance of SwapQuoter - -___ - -### `Static` getSwapQuoterForStandardRelayerAPIUrl - -▸ **getSwapQuoterForStandardRelayerAPIUrl**(`supportedProvider`: [SupportedProvider](#supportedprovider), `sraApiUrl`: string, `options`: `Partial`): *[SwapQuoter](#class-swapquoter)* - -*Defined in [asset-swapper/src/swap_quoter.ts:64](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/swap_quoter.ts#L64)* - -Instantiates a new SwapQuoter instance given a [Standard Relayer API](https://github.com/0xProject/standard-relayer-api) endpoint - -**Parameters:** - -Name | Type | Default | Description | ------- | ------ | ------ | ------ | -`supportedProvider` | [SupportedProvider](#supportedprovider) | - | The Provider instance you would like to use for interacting with the Ethereum network. | -`sraApiUrl` | string | - | The standard relayer API base HTTP url you would like to source orders from. | -`options` | `Partial` | {} | Initialization options for the SwapQuoter. See type definition for details. | - -**Returns:** *[SwapQuoter](#class-swapquoter)* - -An instance of SwapQuoter - -___ - -### `Static` getSwapQuoterForStandardRelayerAPIWebsocket - -▸ **getSwapQuoterForStandardRelayerAPIWebsocket**(`supportedProvider`: [SupportedProvider](#supportedprovider), `sraApiUrl`: string, `sraWebsocketAPIUrl`: string, `options`: `Partial`): *[SwapQuoter](#class-swapquoter)* - -*Defined in [asset-swapper/src/swap_quoter.ts:91](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/swap_quoter.ts#L91)* - -Instantiates a new SwapQuoter instance given a [Standard Relayer API](https://github.com/0xProject/standard-relayer-api) endpoint -and a websocket endpoint. This is more effecient than `getSwapQuoterForStandardRelayerAPIUrl` when requesting multiple quotes. - -**Parameters:** - -Name | Type | Default | Description | ------- | ------ | ------ | ------ | -`supportedProvider` | [SupportedProvider](#supportedprovider) | - | The Provider instance you would like to use for interacting with the Ethereum network. | -`sraApiUrl` | string | - | The standard relayer API base HTTP url you would like to source orders from. | -`sraWebsocketAPIUrl` | string | - | - | -`options` | `Partial` | {} | Initialization options for the SwapQuoter. See type definition for details. | - -**Returns:** *[SwapQuoter](#class-swapquoter)* - -An instance of SwapQuoter - -
- -# Class: BaseOrderProvider - - -## Constructors - - - -\+ **new BaseOrderProvider**(`orderStore`: [OrderStore](_orderbook_src_order_store_.orderstore.md)): *[BaseOrderProvider](#class-baseorderprovider)* - -*Defined in [orderbook/src/order_provider/base_order_provider.ts:12](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/orderbook/src/order_provider/base_order_provider.ts#L12)* - -**Parameters:** - -Name | Type | ------- | ------ | -`orderStore` | [OrderStore](#class-orderstore) | - -**Returns:** *[BaseOrderProvider](#class-baseorderprovider)* - -## Properties - -### _orderStore - -• **_orderStore**: *[OrderStore](#class-orderstore)* - -*Defined in [orderbook/src/order_provider/base_order_provider.ts:12](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/orderbook/src/order_provider/base_order_provider.ts#L12)* - -## Methods - -### `Abstract` addOrdersAsync - -▸ **addOrdersAsync**(`orders`: `SignedOrder`[]): *`Promise`* - -*Defined in [orderbook/src/order_provider/base_order_provider.ts:27](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/orderbook/src/order_provider/base_order_provider.ts#L27)* - -**Parameters:** - -Name | Type | ------- | ------ | -`orders` | `SignedOrder`[] | - -**Returns:** *`Promise`* - -___ - -### `Abstract` createSubscriptionForAssetPairAsync - -▸ **createSubscriptionForAssetPairAsync**(`makerAssetData`: string, `takerAssetData`: string): *`Promise`* - -*Defined in [orderbook/src/order_provider/base_order_provider.ts:18](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/orderbook/src/order_provider/base_order_provider.ts#L18)* - -**Parameters:** - -Name | Type | ------- | ------ | -`makerAssetData` | string | -`takerAssetData` | string | - -**Returns:** *`Promise`* - -___ - -### `Abstract` destroyAsync - -▸ **destroyAsync**(): *`Promise`* - -*Defined in [orderbook/src/order_provider/base_order_provider.ts:25](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/orderbook/src/order_provider/base_order_provider.ts#L25)* - -**Returns:** *`Promise`* - -___ - -### `Abstract` getAvailableAssetDatasAsync - -▸ **getAvailableAssetDatasAsync**(): *`Promise`* - -*Defined in [orderbook/src/order_provider/base_order_provider.ts:23](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/orderbook/src/order_provider/base_order_provider.ts#L23)* - -**Returns:** *`Promise`* - -
- -# Class: OrderSet - - -## Constructors - - - -\+ **new OrderSet**(`orders`: `APIOrder`[]): *[OrderSet](#class-orderset)* - -*Defined in [orderbook/src/order_set.ts:6](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/orderbook/src/order_set.ts#L6)* - -**Parameters:** - -Name | Type | Default | ------- | ------ | ------ | -`orders` | `APIOrder`[] | [] | - -**Returns:** *[OrderSet](#class-orderset)* - -## Methods - -### add - -â–¸ **add**(`item`: `APIOrder`): *void* - -*Defined in [orderbook/src/order_set.ts:19](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/orderbook/src/order_set.ts#L19)* - -**Parameters:** - -Name | Type | ------- | ------ | -`item` | `APIOrder` | - -**Returns:** *void* - -___ - -### addMany - -â–¸ **addMany**(`items`: `APIOrder`[]): *void* - -*Defined in [orderbook/src/order_set.ts:25](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/orderbook/src/order_set.ts#L25)* - -**Parameters:** - -Name | Type | ------- | ------ | -`items` | `APIOrder`[] | - -**Returns:** *void* - -___ - -### delete - -â–¸ **delete**(`item`: `APIOrder`): *boolean* - -*Defined in [orderbook/src/order_set.ts:57](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/orderbook/src/order_set.ts#L57)* - -**Parameters:** - -Name | Type | ------- | ------ | -`item` | `APIOrder` | - -**Returns:** *boolean* - -___ - -### deleteMany - -â–¸ **deleteMany**(`items`: `APIOrder`[]): *void* - -*Defined in [orderbook/src/order_set.ts:61](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/orderbook/src/order_set.ts#L61)* - -**Parameters:** - -Name | Type | ------- | ------ | -`items` | `APIOrder`[] | - -**Returns:** *void* - -___ - -### diff - -â–¸ **diff**(`other`: [OrderSet](#class-orderset)): *object* - -*Defined in [orderbook/src/order_set.ts:35](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/orderbook/src/order_set.ts#L35)* - -**Parameters:** - -Name | Type | ------- | ------ | -`other` | [OrderSet](#class-orderset) | - -**Returns:** *object* - -___ - -### has - -â–¸ **has**(`order`: `APIOrder`): *boolean* - -*Defined in [orderbook/src/order_set.ts:31](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/orderbook/src/order_set.ts#L31)* - -**Parameters:** - -Name | Type | ------- | ------ | -`order` | `APIOrder` | - -**Returns:** *boolean* - -___ - -### size - -â–¸ **size**(): *number* - -*Defined in [orderbook/src/order_set.ts:15](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/orderbook/src/order_set.ts#L15)* - -**Returns:** *number* - -___ - -### values - -â–¸ **values**(): *`IterableIterator`* - -*Defined in [orderbook/src/order_set.ts:53](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/orderbook/src/order_set.ts#L53)* - -**Returns:** *`IterableIterator`* - -
- -# Class: OrderStore - - -## Methods - -### getOrderSetForAssetPair - -â–¸ **getOrderSetForAssetPair**(`assetPairKey`: string): *[OrderSet](#class-orderset)* - -*Defined in [orderbook/src/order_store.ts:19](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/orderbook/src/order_store.ts#L19)* - -**Parameters:** - -Name | Type | ------- | ------ | -`assetPairKey` | string | - -**Returns:** *[OrderSet](#class-orderset)* - -___ - -### getOrderSetForAssets - -â–¸ **getOrderSetForAssets**(`makerAssetData`: string, `takerAssetData`: string): *[OrderSet](#class-orderset)* - -*Defined in [orderbook/src/order_store.ts:15](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/orderbook/src/order_store.ts#L15)* - -**Parameters:** - -Name | Type | ------- | ------ | -`makerAssetData` | string | -`takerAssetData` | string | - -**Returns:** *[OrderSet](#class-orderset)* - -___ - -### has - -â–¸ **has**(`assetPairKey`: string): *boolean* - -*Defined in [orderbook/src/order_store.ts:34](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/orderbook/src/order_store.ts#L34)* - -**Parameters:** - -Name | Type | ------- | ------ | -`assetPairKey` | string | - -**Returns:** *boolean* - -___ - -### keys - -â–¸ **keys**(): *`IterableIterator`* - -*Defined in [orderbook/src/order_store.ts:40](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/orderbook/src/order_store.ts#L40)* - -**Returns:** *`IterableIterator`* - -___ - -### update - -â–¸ **update**(`addedRemoved`: [AddedRemovedOrders](#interface-addedremovedorders)): *void* - -*Defined in [orderbook/src/order_store.ts:28](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/orderbook/src/order_store.ts#L28)* - -**Parameters:** - -Name | Type | ------- | ------ | -`addedRemoved` | [AddedRemovedOrders](#interface-addedremovedorders) | - -**Returns:** *void* - -___ - -### values - -â–¸ **values**(`assetPairKey`: string): *`APIOrder`[]* - -*Defined in [orderbook/src/order_store.ts:37](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/orderbook/src/order_store.ts#L37)* - -**Parameters:** - -Name | Type | ------- | ------ | -`assetPairKey` | string | - -**Returns:** *`APIOrder`[]* - -___ - -### `Static` assetPairKeyToAssets - -â–¸ **assetPairKeyToAssets**(`assetPairKey`: string): *string[]* - -*Defined in [orderbook/src/order_store.ts:12](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/orderbook/src/order_store.ts#L12)* - -**Parameters:** - -Name | Type | ------- | ------ | -`assetPairKey` | string | - -**Returns:** *string[]* - -___ - -### `Static` getKeyForAssetPair - -â–¸ **getKeyForAssetPair**(`makerAssetData`: string, `takerAssetData`: string): *string* - -*Defined in [orderbook/src/order_store.ts:9](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/orderbook/src/order_store.ts#L9)* - -**Parameters:** - -Name | Type | ------- | ------ | -`makerAssetData` | string | -`takerAssetData` | string | - -**Returns:** *string* - -
- -# Class: Orderbook - - -## Constructors - - - -\+ **new Orderbook**(`orderProvider`: [BaseOrderProvider](_orderbook_src_order_provider_base_order_provider_.baseorderprovider.md), `orderStore`: [OrderStore](_orderbook_src_order_store_.orderstore.md)): *[Orderbook](#class-orderbook)* - -*Defined in [orderbook/src/orderbook.ts:55](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/orderbook/src/orderbook.ts#L55)* - -Creates an Orderbook with the order provider. All order updates are stored -in the `OrderStore`. - -**Parameters:** - -Name | Type | Description | ------- | ------ | ------ | -`orderProvider` | [BaseOrderProvider](#class-baseorderprovider) | the order provider, e.g SRAWebbsocketOrderProvider | -`orderStore` | [OrderStore](#class-orderstore) | the order store where orders are added and deleted | - -**Returns:** *[Orderbook](#class-orderbook)* - -## Methods - -### addOrdersAsync - -â–¸ **addOrdersAsync**(`orders`: `SignedOrder`[]): *`Promise`* - -*Defined in [orderbook/src/orderbook.ts:98](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/orderbook/src/orderbook.ts#L98)* - -Adds the orders to the Order Provider. All accepted orders will be returned -and rejected orders will be returned with an message indicating a reason for its rejection - -**Parameters:** - -Name | Type | Description | ------- | ------ | ------ | -`orders` | `SignedOrder`[] | The set of Orders to add to the Order Provider | - -**Returns:** *`Promise`* - -___ - -### destroyAsync - -â–¸ **destroyAsync**(): *`Promise`* - -*Defined in [orderbook/src/orderbook.ts:104](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/orderbook/src/orderbook.ts#L104)* - -Destroys any subscriptions or connections. - -**Returns:** *`Promise`* - -___ - -### getAvailableAssetDatasAsync - -â–¸ **getAvailableAssetDatasAsync**(): *`Promise`* - -*Defined in [orderbook/src/orderbook.ts:90](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/orderbook/src/orderbook.ts#L90)* - -Returns all of the Available Asset Pairs for the provided Order Provider. - -**Returns:** *`Promise`* - -___ - -### getOrdersAsync - -â–¸ **getOrdersAsync**(`makerAssetData`: string, `takerAssetData`: string): *`Promise`* - -*Defined in [orderbook/src/orderbook.ts:75](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/orderbook/src/orderbook.ts#L75)* - -Returns all orders where the order.makerAssetData == makerAssetData and -order.takerAssetData == takerAssetData. This pair is then subscribed to -and all future updates will be stored. The first request -to `getOrdersAsync` might fetch the orders from the Order Provider and create a subscription. -Subsequent requests will be quick and up to date and synced with the Order Provider state. - -**Parameters:** - -Name | Type | Description | ------- | ------ | ------ | -`makerAssetData` | string | the maker asset data | -`takerAssetData` | string | the taker asset data | - -**Returns:** *`Promise`* - -___ - -### `Static` getOrderbookForMeshProvider - -â–¸ **getOrderbookForMeshProvider**(`opts`: [MeshOrderProviderOpts](#interface-meshorderprovideropts)): *[Orderbook](#class-orderbook)* - -*Defined in [orderbook/src/orderbook.ts:52](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/orderbook/src/orderbook.ts#L52)* - -Creates an Orderbook with a Mesh Order Provider. This Provider fetches ALL orders -and subscribes to updates on ALL orders. - -**Parameters:** - -Name | Type | Description | ------- | ------ | ------ | -`opts` | [MeshOrderProviderOpts](#interface-meshorderprovideropts) | the `MeshOrderProviderOpts` | - -**Returns:** *[Orderbook](#class-orderbook)* - -___ - -### `Static` getOrderbookForPollingProvider - -â–¸ **getOrderbookForPollingProvider**(`opts`: [SRAPollingOrderProviderOpts](#interface-srapollingorderprovideropts)): *[Orderbook](#class-orderbook)* - -*Defined in [orderbook/src/orderbook.ts:43](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/orderbook/src/orderbook.ts#L43)* - -Creates an Orderbook with SRA Polling Provider. This Provider simply polls every interval. - -**Parameters:** - -Name | Type | Description | ------- | ------ | ------ | -`opts` | [SRAPollingOrderProviderOpts](#interface-srapollingorderprovideropts) | the `SRAPollingOrderProviderOpts` | - -**Returns:** *[Orderbook](#class-orderbook)* - -___ - -### `Static` getOrderbookForProvidedOrders - -â–¸ **getOrderbookForProvidedOrders**(`orders`: `SignedOrder`[]): *[Orderbook](#class-orderbook)* - -*Defined in [orderbook/src/orderbook.ts:26](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/orderbook/src/orderbook.ts#L26)* - -Creates an Orderbook with the provided orders. This provider simply stores the -orders and allows querying. No validation or subscriptions occur. - -**Parameters:** - -Name | Type | Description | ------- | ------ | ------ | -`orders` | `SignedOrder`[] | the set of SignedOrders | - -**Returns:** *[Orderbook](#class-orderbook)* - -___ - -### `Static` getOrderbookForWebsocketProvider - -â–¸ **getOrderbookForWebsocketProvider**(`opts`: [SRAWebsocketOrderProviderOpts](#interface-srawebsocketorderprovideropts)): *[Orderbook](#class-orderbook)* - -*Defined in [orderbook/src/orderbook.ts:35](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/orderbook/src/orderbook.ts#L35)* - -Creates an Orderbook with the SRA Websocket Provider. This Provider fetches orders via -the SRA http endpoint and then subscribes to the asset pair for future updates. - -**Parameters:** - -Name | Type | Description | ------- | ------ | ------ | -`opts` | [SRAWebsocketOrderProviderOpts](#interface-srawebsocketorderprovideropts) | the `SRAWebsocketOrderProviderOpts` | - -**Returns:** *[Orderbook](#class-orderbook)* - -
- -# Enumeration: ExtensionContractType - -Represents the varying smart contracts that can consume a valid swap quote - - -## Enumeration members - -### Forwarder - -• **Forwarder**: = "FORWARDER" - -*Defined in [asset-swapper/src/types.ts:106](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L106)* - -___ - -### None - -• **None**: = "NONE" - -*Defined in [asset-swapper/src/types.ts:107](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L107)* - -
- - - - - - - -# Enumeration: SwapQuoterError - -Possible error messages thrown by an SwapQuoter instance or associated static methods. - - -## Enumeration members - -### AssetUnavailable - -• **AssetUnavailable**: = "ASSET_UNAVAILABLE" - -*Defined in [asset-swapper/src/types.ts:298](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L298)* - -___ - -### InsufficientAssetLiquidity - -• **InsufficientAssetLiquidity**: = "INSUFFICIENT_ASSET_LIQUIDITY" - -*Defined in [asset-swapper/src/types.ts:297](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L297)* - -___ - -### NoEtherTokenContractFound - -• **NoEtherTokenContractFound**: = "NO_ETHER_TOKEN_CONTRACT_FOUND" - -*Defined in [asset-swapper/src/types.ts:295](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L295)* - -___ - -### NoGasPriceProvidedOrEstimated - -• **NoGasPriceProvidedOrEstimated**: = "NO_GAS_PRICE_PROVIDED_OR_ESTIMATED" - -*Defined in [asset-swapper/src/types.ts:299](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L299)* - -___ - -### StandardRelayerApiError - -• **StandardRelayerApiError**: = "STANDARD_RELAYER_API_ERROR" - -*Defined in [asset-swapper/src/types.ts:296](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L296)* - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -# Interface: CalldataInfo - -Represents the metadata to call a smart contract with calldata. -calldataHexString: The hexstring of the calldata. -methodAbi: The ABI of the smart contract method to call. -toAddress: The contract address to call. -ethAmount: The eth amount in wei to send with the smart contract call. - - -## Properties - -### calldataHexString - -• **calldataHexString**: *string* - -*Defined in [asset-swapper/src/types.ts:55](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L55)* - -___ - -### ethAmount - -• **ethAmount**: *`BigNumber`* - -*Defined in [asset-swapper/src/types.ts:58](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L58)* - -___ - -### methodAbi - -• **methodAbi**: *`MethodAbi`* - -*Defined in [asset-swapper/src/types.ts:56](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L56)* - -___ - -### toAddress - -• **toAddress**: *string* - -*Defined in [asset-swapper/src/types.ts:57](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L57)* - -
- - - - - - - - - - - - - -# Interface: GetExtensionContractTypeOpts - - -## Properties - -### `Optional` ethAmount - -• **ethAmount**? : *`BigNumber`* - -*Defined in [asset-swapper/src/types.ts:209](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L209)* - -___ - -### `Optional` takerAddress - -• **takerAddress**? : *undefined | string* - -*Defined in [asset-swapper/src/types.ts:208](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L208)* - -
- -# Interface: LiquidityForTakerMakerAssetDataPair - -Represents available liquidity for a given assetData. - - -## Properties - -### makerAssetAvailableInBaseUnits - -• **makerAssetAvailableInBaseUnits**: *`BigNumber`* - -*Defined in [asset-swapper/src/types.ts:306](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L306)* - -___ - -### takerAssetAvailableInBaseUnits - -• **takerAssetAvailableInBaseUnits**: *`BigNumber`* - -*Defined in [asset-swapper/src/types.ts:307](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L307)* - -
- -# Interface: MarketBuySwapQuote - -makerAssetFillAmount: The amount of makerAsset bought with takerAsset. -type: Specified MarketOperation the SwapQuote is provided for - - -## Properties - -### bestCaseQuoteInfo - -• **bestCaseQuoteInfo**: *[SwapQuoteInfo](#class-swapquoteinfo)* - -*Inherited from [SwapQuoteBase](#interface-swapquotebase).[bestCaseQuoteInfo](#bestcasequoteinfo)* - -*Defined in [asset-swapper/src/types.ts:223](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L223)* - -___ - -### makerAssetData - -• **makerAssetData**: *string* - -*Inherited from [SwapQuoteBase](#interface-swapquotebase).[makerAssetData](#makerassetdata)* - -*Defined in [asset-swapper/src/types.ts:221](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L221)* - -___ - -### makerAssetFillAmount - -• **makerAssetFillAmount**: *`BigNumber`* - -*Defined in [asset-swapper/src/types.ts:241](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L241)* - -___ - -### orders - -• **orders**: *`SignedOrder`[]* - -*Inherited from [SwapQuoteBase](#interface-swapquotebase).[orders](#orders)* - -*Defined in [asset-swapper/src/types.ts:222](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L222)* - -___ - -### takerAssetData - -• **takerAssetData**: *string* - -*Inherited from [SwapQuoteBase](#interface-swapquotebase).[takerAssetData](#takerassetdata)* - -*Defined in [asset-swapper/src/types.ts:220](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L220)* - -___ - -### type - -• **type**: *[Buy](#buy)* - -*Defined in [asset-swapper/src/types.ts:242](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L242)* - -___ - -### worstCaseQuoteInfo - -• **worstCaseQuoteInfo**: *[SwapQuoteInfo](#class-swapquoteinfo)* - -*Inherited from [SwapQuoteBase](#interface-swapquotebase).[worstCaseQuoteInfo](#worstcasequoteinfo)* - -*Defined in [asset-swapper/src/types.ts:224](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L224)* - -
- -# Interface: MarketSellSwapQuote - -takerAssetFillAmount: The amount of takerAsset sold for makerAsset. -type: Specified MarketOperation the SwapQuote is provided for - - -## Properties - -### bestCaseQuoteInfo - -• **bestCaseQuoteInfo**: *[SwapQuoteInfo](#class-swapquoteinfo)* - -*Inherited from [SwapQuoteBase](#interface-swapquotebase).[bestCaseQuoteInfo](#bestcasequoteinfo)* - -*Defined in [asset-swapper/src/types.ts:223](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L223)* - -___ - -### makerAssetData - -• **makerAssetData**: *string* - -*Inherited from [SwapQuoteBase](#interface-swapquotebase).[makerAssetData](#makerassetdata)* - -*Defined in [asset-swapper/src/types.ts:221](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L221)* - -___ - -### orders - -• **orders**: *`SignedOrder`[]* - -*Inherited from [SwapQuoteBase](#interface-swapquotebase).[orders](#orders)* - -*Defined in [asset-swapper/src/types.ts:222](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L222)* - -___ - -### takerAssetData - -• **takerAssetData**: *string* - -*Inherited from [SwapQuoteBase](#interface-swapquotebase).[takerAssetData](#takerassetdata)* - -*Defined in [asset-swapper/src/types.ts:220](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L220)* - -___ - -### takerAssetFillAmount - -• **takerAssetFillAmount**: *`BigNumber`* - -*Defined in [asset-swapper/src/types.ts:232](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L232)* - -___ - -### type - -• **type**: *[Sell](#sell)* - -*Defined in [asset-swapper/src/types.ts:233](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L233)* - -___ - -### worstCaseQuoteInfo - -• **worstCaseQuoteInfo**: *[SwapQuoteInfo](#class-swapquoteinfo)* - -*Inherited from [SwapQuoteBase](#interface-swapquotebase).[worstCaseQuoteInfo](#worstcasequoteinfo)* - -*Defined in [asset-swapper/src/types.ts:224](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L224)* - -
- - - - - - - -# Interface: PrunedSignedOrder - -fillableMakerAssetAmount: Amount of makerAsset that is fillable -fillableTakerAssetAmount: Amount of takerAsset that is fillable -fillableTakerFeeAmount: Amount of takerFee paid to fill fillableTakerAssetAmount - - -## Properties - -### chainId - -• **chainId**: *number* - - - -Defined in types/lib/index.d.ts:4 - -___ - -### exchangeAddress - -• **exchangeAddress**: *string* - - - -Defined in types/lib/index.d.ts:5 - -___ - -### expirationTimeSeconds - -• **expirationTimeSeconds**: *`BigNumber`* - - - -Defined in types/lib/index.d.ts:14 - -___ - -### feeRecipientAddress - -• **feeRecipientAddress**: *string* - - - -Defined in types/lib/index.d.ts:8 - -___ - -### fillableMakerAssetAmount - -• **fillableMakerAssetAmount**: *`BigNumber`* - -*Defined in [asset-swapper/src/types.ts:42](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L42)* - -___ - -### fillableTakerAssetAmount - -• **fillableTakerAssetAmount**: *`BigNumber`* - -*Defined in [asset-swapper/src/types.ts:43](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L43)* - -___ - -### fillableTakerFeeAmount - -• **fillableTakerFeeAmount**: *`BigNumber`* - -*Defined in [asset-swapper/src/types.ts:44](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L44)* - -___ - -### makerAddress - -• **makerAddress**: *string* - - - -Defined in types/lib/index.d.ts:6 - -___ - -### makerAssetAmount - -• **makerAssetAmount**: *`BigNumber`* - - - -Defined in types/lib/index.d.ts:10 - -___ - -### makerAssetData - -• **makerAssetData**: *string* - - - -Defined in types/lib/index.d.ts:16 - -___ - -### makerFee - -• **makerFee**: *`BigNumber`* - - - -Defined in types/lib/index.d.ts:12 - -___ - -### makerFeeAssetData - -• **makerFeeAssetData**: *string* - - - -Defined in types/lib/index.d.ts:18 - -___ - -### salt - -• **salt**: *`BigNumber`* - - - -Defined in types/lib/index.d.ts:15 - -___ - -### senderAddress - -• **senderAddress**: *string* - - - -Defined in types/lib/index.d.ts:9 - -___ - -### signature - -• **signature**: *string* - - - -Defined in types/lib/index.d.ts:22 - -___ - -### takerAddress - -• **takerAddress**: *string* - - - -Defined in types/lib/index.d.ts:7 - -___ - -### takerAssetAmount - -• **takerAssetAmount**: *`BigNumber`* - - - -Defined in types/lib/index.d.ts:11 - -___ - -### takerAssetData - -• **takerAssetData**: *string* - - - -Defined in types/lib/index.d.ts:17 - -___ - -### takerFee - -• **takerFee**: *`BigNumber`* - - - -Defined in types/lib/index.d.ts:13 - -___ - -### takerFeeAssetData - -• **takerFeeAssetData**: *string* - - - -Defined in types/lib/index.d.ts:19 - -
- - - -# Interface: SmartContractParamsInfo <**T**> - -Represents the metadata to call a smart contract with parameters. -params: The metadata object containing all the input parameters of a smart contract call. -toAddress: The contract address to call. -ethAmount: If provided, the eth amount in wei to send with the smart contract call. -methodAbi: The ABI of the smart contract method to call with params. - -## Type parameters - -▪ **T** - - -## Properties - -### ethAmount - -• **ethAmount**: *`BigNumber`* - -*Defined in [asset-swapper/src/types.ts:71](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L71)* - -___ - -### methodAbi - -• **methodAbi**: *`MethodAbi`* - -*Defined in [asset-swapper/src/types.ts:72](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L72)* - -___ - -### params - -• **params**: *`T`* - -*Defined in [asset-swapper/src/types.ts:69](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L69)* - -___ - -### toAddress - -• **toAddress**: *string* - -*Defined in [asset-swapper/src/types.ts:70](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L70)* - -
- - - -# Interface: SwapQuoteConsumerBase <**T**> - -Interface that varying SwapQuoteConsumers adhere to (exchange consumer, router consumer, forwarder consumer, coordinator consumer) -getCalldataOrThrow: Get CalldataInfo to swap for tokens with provided SwapQuote. Throws if invalid SwapQuote is provided. -getSmartContractParamsOrThrow: Get SmartContractParamsInfo to swap for tokens with provided SwapQuote. Throws if invalid SwapQuote is provided. -executeSwapQuoteOrThrowAsync: Executes a web3 transaction to swap for tokens with provided SwapQuote. Throws if invalid SwapQuote is provided. - -## Type parameters - -â–ª **T** - - -## Implemented by - -* [SwapQuoteConsumer](#class-swapquoteconsumer) - - -## Methods - -### executeSwapQuoteOrThrowAsync - -â–¸ **executeSwapQuoteOrThrowAsync**(`quote`: [SwapQuote](#swapquote), `opts`: `Partial`): *`Promise`* - -*Defined in [asset-swapper/src/types.ts:159](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L159)* - -**Parameters:** - -Name | Type | ------- | ------ | -`quote` | [SwapQuote](#swapquote) | -`opts` | `Partial` | - -**Returns:** *`Promise`* - -___ - -### getCalldataOrThrowAsync - -â–¸ **getCalldataOrThrowAsync**(`quote`: [SwapQuote](#swapquote), `opts`: `Partial`): *`Promise`* - -*Defined in [asset-swapper/src/types.ts:154](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L154)* - -**Parameters:** - -Name | Type | ------- | ------ | -`quote` | [SwapQuote](#swapquote) | -`opts` | `Partial` | - -**Returns:** *`Promise`* - -___ - -### getSmartContractParamsOrThrowAsync - -â–¸ **getSmartContractParamsOrThrowAsync**(`quote`: [SwapQuote](#swapquote), `opts`: `Partial`): *`Promise>`* - -*Defined in [asset-swapper/src/types.ts:155](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L155)* - -**Parameters:** - -Name | Type | ------- | ------ | -`quote` | [SwapQuote](#swapquote) | -`opts` | `Partial` | - -**Returns:** *`Promise>`* - -
- -# Interface: SwapQuoteConsumerOpts - -chainId: The chainId that the desired orders should be for. - - -## Properties - -### chainId - -• **chainId**: *number* - -*Defined in [asset-swapper/src/types.ts:166](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L166)* - -
- -# Interface: SwapQuoteConsumingOpts - - -## Properties - -### useExtensionContract - -• **useExtensionContract**: *[ExtensionContractType](#enumeration-extensioncontracttype)* - -*Defined in [asset-swapper/src/types.ts:202](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L202)* - -
- -# Interface: SwapQuoteExecutionOpts - -takerAddress: The address to perform the buy. Defaults to the first available address from the provider. -gasLimit: The amount of gas to send with a transaction (in Gwei). Defaults to an eth_estimateGas rpc call. -gasPrice: Gas price in Wei to use for a transaction -ethAmount: The amount of eth sent with the execution of a swap - - -## Properties - -### `Optional` ethAmount - -• **ethAmount**? : *`BigNumber`* - -*Defined in [asset-swapper/src/types.ts:184](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L184)* - -___ - -### `Optional` gasLimit - -• **gasLimit**? : *undefined | number* - -*Defined in [asset-swapper/src/types.ts:182](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L182)* - -___ - -### `Optional` gasPrice - -• **gasPrice**? : *`BigNumber`* - -*Defined in [asset-swapper/src/types.ts:183](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L183)* - -___ - -### `Optional` takerAddress - -• **takerAddress**? : *undefined | string* - -*Defined in [asset-swapper/src/types.ts:181](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L181)* - -
- -# Interface: SwapQuoteGetOutputOpts - -Represents the options provided to a generic SwapQuoteConsumer - - -## Hierarchy - -* **SwapQuoteInfo** - - -## Properties - -### feeTakerAssetAmount - -• **feeTakerAssetAmount**: *`BigNumber`* - -*Defined in [asset-swapper/src/types.ts:253](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L253)* - -___ - -### makerAssetAmount - -• **makerAssetAmount**: *`BigNumber`* - -*Defined in [asset-swapper/src/types.ts:256](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L256)* - -___ - -### protocolFeeInEthAmount - -• **protocolFeeInEthAmount**: *`BigNumber`* - -*Defined in [asset-swapper/src/types.ts:257](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L257)* - -___ - -### takerAssetAmount - -• **takerAssetAmount**: *`BigNumber`* - -*Defined in [asset-swapper/src/types.ts:254](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L254)* - -___ - -### totalTakerAssetAmount - -• **totalTakerAssetAmount**: *`BigNumber`* - -*Defined in [asset-swapper/src/types.ts:255](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L255)* - -
- -# Interface: SwapQuoteRequestOpts - -slippagePercentage: The percentage buffer to add to account for slippage. Affects max ETH price estimates. Defaults to 0.01 (1%). -gasPrice: gas price to determine protocolFee amount, default to ethGasStation fast amount - - -## Properties - -### `Optional` gasPrice - -• **gasPrice**? : *`BigNumber`* - -*Defined in [asset-swapper/src/types.ts:266](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L266)* - -___ - -### slippagePercentage - -• **slippagePercentage**: *number* - -*Defined in [asset-swapper/src/types.ts:265](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L265)* - -
- -# Interface: SwapQuoterOpts - -chainId: The ethereum chain id. Defaults to 1 (mainnet). -orderRefreshIntervalMs: The interval in ms that getBuyQuoteAsync should trigger an refresh of orders and order states. Defaults to 10000ms (10s). -expiryBufferMs: The number of seconds to add when calculating whether an order is expired or not. Defaults to 300s (5m). - - -## Properties - -### chainId - -• **chainId**: *number* - -*Defined in [asset-swapper/src/types.ts:275](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L275)* - -___ - -### expiryBufferMs - -• **expiryBufferMs**: *number* - -*Overrides [OrderPrunerOpts](_asset_swapper_src_types_.orderpruneropts.md).[expiryBufferMs](#expirybufferms)* - -*Defined in [asset-swapper/src/types.ts:277](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L277)* - -___ - -### orderRefreshIntervalMs - -• **orderRefreshIntervalMs**: *number* - -*Defined in [asset-swapper/src/types.ts:276](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L276)* - -___ - -### permittedOrderFeeTypes - -• **permittedOrderFeeTypes**: *`Set`* - -*Inherited from [OrderPrunerOpts](#interface-orderpruneropts).[permittedOrderFeeTypes](#permittedorderfeetypes)* - -*Defined in [asset-swapper/src/types.ts:11](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L11)* - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# Interface: DataItem - - -## Properties - -### `Optional` components - -• **components**? : *[DataItem](#class-dataitem)[]* - -*Defined in [ethereum-types/src/index.ts:137](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/ethereum-types/src/index.ts#L137)* - -___ - -### name - -• **name**: *string* - -*Defined in [ethereum-types/src/index.ts:135](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/ethereum-types/src/index.ts#L135)* - -___ - -### type - -• **type**: *string* - -*Defined in [ethereum-types/src/index.ts:136](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/ethereum-types/src/index.ts#L136)* - -
- - - - - - - -# Interface: EIP1193Provider - - -## Properties - -### isEIP1193 - -• **isEIP1193**: *boolean* - -*Defined in [ethereum-types/src/index.ts:73](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/ethereum-types/src/index.ts#L73)* - -## Methods - -### on - -▸ **on**(`event`: [EIP1193Event](#eip1193event), `listener`: function): *this* - -*Defined in [ethereum-types/src/index.ts:75](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/ethereum-types/src/index.ts#L75)* - -**Parameters:** - -▪ **event**: *[EIP1193Event](#eip1193event)* - -▪ **listener**: *function* - -▸ (`result`: any): *void* - -**Parameters:** - -Name | Type | ------- | ------ | -`result` | any | - -**Returns:** *this* - -___ - -### send - -▸ **send**(`method`: string, `params?`: any[]): *`Promise`* - -*Defined in [ethereum-types/src/index.ts:74](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/ethereum-types/src/index.ts#L74)* - -**Parameters:** - -Name | Type | ------- | ------ | -`method` | string | -`params?` | any[] | - -**Returns:** *`Promise`* - -
- - - - - - - - - - - - - -# Interface: GanacheProvider - - -## Methods - -### sendAsync - -â–¸ **sendAsync**(`payload`: [JSONRPCRequestPayload](_ethereum_types_src_index_.jsonrpcrequestpayload.md), `callback`: [JSONRPCErrorCallback](#jsonrpcerrorcallback)): *void* - -*Defined in [ethereum-types/src/index.ts:14](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/ethereum-types/src/index.ts#L14)* - -**Parameters:** - -Name | Type | ------- | ------ | -`payload` | [JSONRPCRequestPayload](#class-jsonrpcrequestpayload) | -`callback` | [JSONRPCErrorCallback](#jsonrpcerrorcallback) | - -**Returns:** *void* - -
- - - -# Interface: JSONRPCRequestPayload - - -## Properties - -### id - -• **id**: *number* - -*Defined in [ethereum-types/src/index.ts:330](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/ethereum-types/src/index.ts#L330)* - -___ - -### jsonrpc - -• **jsonrpc**: *string* - -*Defined in [ethereum-types/src/index.ts:331](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/ethereum-types/src/index.ts#L331)* - -___ - -### method - -• **method**: *string* - -*Defined in [ethereum-types/src/index.ts:329](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/ethereum-types/src/index.ts#L329)* - -___ - -### params - -• **params**: *any[]* - -*Defined in [ethereum-types/src/index.ts:328](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/ethereum-types/src/index.ts#L328)* - -
- -# Interface: JSONRPCResponseError - - -## Properties - -### code - -• **code**: *number* - -*Defined in [ethereum-types/src/index.ts:336](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/ethereum-types/src/index.ts#L336)* - -___ - -### message - -• **message**: *string* - -*Defined in [ethereum-types/src/index.ts:335](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/ethereum-types/src/index.ts#L335)* - -
- -# Interface: JSONRPCResponsePayload - - -## Properties - -### `Optional` error - -• **error**? : *[JSONRPCResponseError](#class-jsonrpcresponseerror)* - -*Defined in [ethereum-types/src/index.ts:343](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/ethereum-types/src/index.ts#L343)* - -___ - -### id - -• **id**: *number* - -*Defined in [ethereum-types/src/index.ts:341](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/ethereum-types/src/index.ts#L341)* - -___ - -### jsonrpc - -• **jsonrpc**: *string* - -*Defined in [ethereum-types/src/index.ts:342](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/ethereum-types/src/index.ts#L342)* - -___ - -### result - -• **result**: *any* - -*Defined in [ethereum-types/src/index.ts:340](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/ethereum-types/src/index.ts#L340)* - -
- - - - - - - -# Interface: MethodAbi - - -## Properties - -### constant - -• **constant**: *boolean* - -*Defined in [ethereum-types/src/index.ts:94](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/ethereum-types/src/index.ts#L94)* - -___ - -### inputs - -• **inputs**: *[DataItem](#class-dataitem)[]* - -*Defined in [ethereum-types/src/index.ts:92](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/ethereum-types/src/index.ts#L92)* - -___ - -### name - -• **name**: *string* - -*Defined in [ethereum-types/src/index.ts:91](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/ethereum-types/src/index.ts#L91)* - -___ - -### outputs - -• **outputs**: *[DataItem](#class-dataitem)[]* - -*Defined in [ethereum-types/src/index.ts:93](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/ethereum-types/src/index.ts#L93)* - -___ - -### payable - -• **payable**: *boolean* - -*Defined in [ethereum-types/src/index.ts:96](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/ethereum-types/src/index.ts#L96)* - -___ - -### stateMutability - -• **stateMutability**: *[StateMutability](#statemutability)* - -*Defined in [ethereum-types/src/index.ts:95](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/ethereum-types/src/index.ts#L95)* - -___ - -### type - -• **type**: *string* - -*Defined in [ethereum-types/src/index.ts:90](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/ethereum-types/src/index.ts#L90)* - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# Interface: TupleDataItem - - -## Properties - -### components - -• **components**: *[DataItem](#class-dataitem)[]* - -*Overrides [DataItem](_ethereum_types_src_index_.dataitem.md).[components](#optional-components)* - -*Defined in [ethereum-types/src/index.ts:141](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/ethereum-types/src/index.ts#L141)* - -___ - -### name - -• **name**: *string* - -*Inherited from [DataItem](#interface-dataitem).[name](#name)* - -*Defined in [ethereum-types/src/index.ts:135](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/ethereum-types/src/index.ts#L135)* - -___ - -### type - -• **type**: *string* - -*Inherited from [DataItem](#interface-dataitem).[type](#type)* - -*Defined in [ethereum-types/src/index.ts:136](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/ethereum-types/src/index.ts#L136)* - -
- - - - - -# Class: InsufficientAssetLiquidityError - -Error class representing insufficient asset liquidity - - -## Constructors - - - -\+ **new InsufficientAssetLiquidityError**(`amountAvailableToFill`: `BigNumber`): *[InsufficientAssetLiquidityError](#class-insufficientassetliquidityerror)* - -*Defined in [asset-swapper/src/errors.ts:12](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/errors.ts#L12)* - -**Parameters:** - -Name | Type | Description | ------- | ------ | ------ | -`amountAvailableToFill` | `BigNumber` | The amount availabe to fill (in base units) factoring in slippage | - -**Returns:** *[InsufficientAssetLiquidityError](#class-insufficientassetliquidityerror)* - -## Properties - -### amountAvailableToFill - -• **amountAvailableToFill**: *`BigNumber`* - -*Defined in [asset-swapper/src/errors.ts:12](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/errors.ts#L12)* - -The amount availabe to fill (in base units) factoring in slippage. - -___ - -### message - -• **message**: *string* - - - - - -___ - -### name - -• **name**: *string* - - - - - -___ - -### `Optional` stack - -• **stack**? : *undefined | string* - - - -*Overrides void* - - - -___ - -### `Static` Error - -▪ **Error**: *`ErrorConstructor`* - - - -
- -# Interface: Web3JsV2Provider - -Web3.js version 2 provider interface -This provider interface was used in a couple of Web3.js 1.0 beta releases -before the first attempts to conform to EIP1193 - - -## Methods - -### send - -â–¸ **send**(`payload`: [JSONRPCRequestPayload](_ethereum_types_src_index_.jsonrpcrequestpayload.md), `callback`: [JSONRPCErrorCallback](#jsonrpcerrorcallback)): *void* - -*Defined in [ethereum-types/src/index.ts:54](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/ethereum-types/src/index.ts#L54)* - -**Parameters:** - -Name | Type | ------- | ------ | -`payload` | [JSONRPCRequestPayload](#class-jsonrpcrequestpayload) | -`callback` | [JSONRPCErrorCallback](#jsonrpcerrorcallback) | - -**Returns:** *void* - -
- -# Interface: Web3JsV3Provider - -Web3.js version 3 provider interface -This provider interface was implemented with the hopes for conforming to the EIP1193 spec, -however it does not conform entirely. - - -## Methods - -### send - -â–¸ **send**(`method`: string, `params?`: any[]): *`Promise`* - -*Defined in [ethereum-types/src/index.ts:63](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/ethereum-types/src/index.ts#L63)* - -**Parameters:** - -Name | Type | ------- | ------ | -`method` | string | -`params?` | any[] | - -**Returns:** *`Promise`* - -
- -# Interface: ZeroExProvider - -The interface for the provider used internally by 0x libraries -Any property we use from any SupportedProvider should we explicitly -add here - - -## Properties - -### `Optional` isMetaMask - -• **isMetaMask**? : *undefined | false | true* - -*Defined in [ethereum-types/src/index.ts:31](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/ethereum-types/src/index.ts#L31)* - -___ - -### `Optional` isParity - -• **isParity**? : *undefined | false | true* - -*Defined in [ethereum-types/src/index.ts:32](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/ethereum-types/src/index.ts#L32)* - -___ - -### `Optional` isZeroExProvider - -• **isZeroExProvider**? : *undefined | false | true* - -*Defined in [ethereum-types/src/index.ts:30](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/ethereum-types/src/index.ts#L30)* - -## Methods - -### `Optional` enable - -▸ **enable**(): *`Promise`* - -*Defined in [ethereum-types/src/index.ts:34](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/ethereum-types/src/index.ts#L34)* - -**Returns:** *`Promise`* - -___ - -### sendAsync - -▸ **sendAsync**(`payload`: [JSONRPCRequestPayload](_ethereum_types_src_index_.jsonrpcrequestpayload.md), `callback`: [JSONRPCErrorCallback](#jsonrpcerrorcallback)): *void* - -*Defined in [ethereum-types/src/index.ts:35](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/ethereum-types/src/index.ts#L35)* - -**Parameters:** - -Name | Type | ------- | ------ | -`payload` | [JSONRPCRequestPayload](#class-jsonrpcrequestpayload) | -`callback` | [JSONRPCErrorCallback](#jsonrpcerrorcallback) | - -**Returns:** *void* - -___ - -### `Optional` stop - -▸ **stop**(): *void* - -*Defined in [ethereum-types/src/index.ts:33](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/ethereum-types/src/index.ts#L33)* - -**Returns:** *void* - -
- -# Interface: AcceptedRejectedOrders - - -## Properties - -### accepted - -• **accepted**: *`SignedOrder`[]* - -*Defined in [orderbook/src/types.ts:15](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/orderbook/src/types.ts#L15)* - -___ - -### rejected - -• **rejected**: *[RejectedOrder](#class-rejectedorder)[]* - -*Defined in [orderbook/src/types.ts:16](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/orderbook/src/types.ts#L16)* - -
- -# Interface: AddedRemovedOrders - - -## Properties - -### added - -• **added**: *`APIOrder`[]* - -*Defined in [orderbook/src/types.ts:6](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/orderbook/src/types.ts#L6)* - -___ - -### assetPairKey - -• **assetPairKey**: *string* - -*Defined in [orderbook/src/types.ts:5](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/orderbook/src/types.ts#L5)* - -___ - -### removed - -• **removed**: *`APIOrder`[]* - -*Defined in [orderbook/src/types.ts:7](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/orderbook/src/types.ts#L7)* - -
- -# Interface: MeshOrderProviderOpts - -Constructor options for a Mesh Order Provider - - -## Properties - -### websocketEndpoint - -• **websocketEndpoint**: *string* - -*Defined in [orderbook/src/types.ts:52](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/orderbook/src/types.ts#L52)* - -___ - -### `Optional` wsOpts - -• **wsOpts**? : *`WSOpts`* - -*Defined in [orderbook/src/types.ts:54](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/orderbook/src/types.ts#L54)* - -
- -# Interface: RejectedOrder - - -## Properties - -### message - -• **message**: *string* - -*Defined in [orderbook/src/types.ts:11](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/orderbook/src/types.ts#L11)* - -___ - -### order - -• **order**: *`SignedOrder`* - -*Defined in [orderbook/src/types.ts:12](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/orderbook/src/types.ts#L12)* - -
- -# Interface: SRAPollingOrderProviderOpts - -Constructor options for a SRA Polling Order Provider - - -## Properties - -### `Optional` chainId - -• **chainId**? : *undefined | number* - -*Defined in [orderbook/src/types.ts:44](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/orderbook/src/types.ts#L44)* - -___ - -### httpEndpoint - -• **httpEndpoint**: *string* - -*Defined in [orderbook/src/types.ts:38](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/orderbook/src/types.ts#L38)* - -___ - -### `Optional` perPage - -• **perPage**? : *undefined | number* - -*Defined in [orderbook/src/types.ts:42](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/orderbook/src/types.ts#L42)* - -___ - -### pollingIntervalMs - -• **pollingIntervalMs**: *number* - -*Defined in [orderbook/src/types.ts:40](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/orderbook/src/types.ts#L40)* - -
- -# Interface: SRAWebsocketOrderProviderOpts - -Constructor options for a SRA Websocket Order Provider - - -## Properties - -### `Optional` chainId - -• **chainId**? : *undefined | number* - -*Defined in [orderbook/src/types.ts:30](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/orderbook/src/types.ts#L30)* - -___ - -### httpEndpoint - -• **httpEndpoint**: *string* - -*Defined in [orderbook/src/types.ts:26](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/orderbook/src/types.ts#L26)* - -___ - -### websocketEndpoint - -• **websocketEndpoint**: *string* - -*Defined in [orderbook/src/types.ts:28](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/orderbook/src/types.ts#L28)* - -
- -# Interface: APIOrder - - -## Properties - -### metaData - -• **metaData**: *object* - -*Defined in [types/src/index.ts:403](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/types/src/index.ts#L403)* - -___ - -### order - -• **order**: *[SignedOrder](#class-signedorder)* - -*Defined in [types/src/index.ts:402](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/types/src/index.ts#L402)* - -
- - - -# Interface: Asset - - -## Properties - -### assetData - -• **assetData**: *string* - -*Defined in [types/src/index.ts:419](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/types/src/index.ts#L419)* - -___ - -### maxAmount - -• **maxAmount**: *`BigNumber`* - -*Defined in [types/src/index.ts:421](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/types/src/index.ts#L421)* - -___ - -### minAmount - -• **minAmount**: *`BigNumber`* - -*Defined in [types/src/index.ts:420](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/types/src/index.ts#L420)* - -___ - -### precision - -• **precision**: *number* - -*Defined in [types/src/index.ts:422](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/types/src/index.ts#L422)* - -
- -# Interface: AssetPairsItem - - -## Properties - -### assetDataA - -• **assetDataA**: *[Asset](#class-asset)* - -*Defined in [types/src/index.ts:414](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/types/src/index.ts#L414)* - -___ - -### assetDataB - -• **assetDataB**: *[Asset](#class-asset)* - -*Defined in [types/src/index.ts:415](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/types/src/index.ts#L415)* - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# Interface: SignedOrder - - -## Properties - -### chainId - -• **chainId**: *number* - -*Inherited from [Order](#interface-order).[chainId](#chainid)* - -*Defined in [types/src/index.ts:14](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/types/src/index.ts#L14)* - -___ - -### exchangeAddress - -• **exchangeAddress**: *string* - -*Inherited from [Order](#interface-order).[exchangeAddress](#exchangeaddress)* - -*Defined in [types/src/index.ts:15](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/types/src/index.ts#L15)* - -___ - -### expirationTimeSeconds - -• **expirationTimeSeconds**: *`BigNumber`* - -*Inherited from [Order](#interface-order).[expirationTimeSeconds](#expirationtimeseconds)* - -*Defined in [types/src/index.ts:24](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/types/src/index.ts#L24)* - -___ - -### feeRecipientAddress - -• **feeRecipientAddress**: *string* - -*Inherited from [Order](#interface-order).[feeRecipientAddress](#feerecipientaddress)* - -*Defined in [types/src/index.ts:18](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/types/src/index.ts#L18)* - -___ - -### makerAddress - -• **makerAddress**: *string* - -*Inherited from [Order](#interface-order).[makerAddress](#makeraddress)* - -*Defined in [types/src/index.ts:16](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/types/src/index.ts#L16)* - -___ - -### makerAssetAmount - -• **makerAssetAmount**: *`BigNumber`* - -*Inherited from [Order](#interface-order).[makerAssetAmount](#makerassetamount)* - -*Defined in [types/src/index.ts:20](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/types/src/index.ts#L20)* - -___ - -### makerAssetData - -• **makerAssetData**: *string* - -*Inherited from [Order](#interface-order).[makerAssetData](#makerassetdata)* - -*Defined in [types/src/index.ts:26](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/types/src/index.ts#L26)* - -___ - -### makerFee - -• **makerFee**: *`BigNumber`* - -*Inherited from [Order](#interface-order).[makerFee](#makerfee)* - -*Defined in [types/src/index.ts:22](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/types/src/index.ts#L22)* - -___ - -### makerFeeAssetData - -• **makerFeeAssetData**: *string* - -*Inherited from [Order](#interface-order).[makerFeeAssetData](#makerfeeassetdata)* - -*Defined in [types/src/index.ts:28](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/types/src/index.ts#L28)* - -___ - -### salt - -• **salt**: *`BigNumber`* - -*Inherited from [Order](#interface-order).[salt](#salt)* - -*Defined in [types/src/index.ts:25](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/types/src/index.ts#L25)* - -___ - -### senderAddress - -• **senderAddress**: *string* - -*Inherited from [Order](#interface-order).[senderAddress](#senderaddress)* - -*Defined in [types/src/index.ts:19](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/types/src/index.ts#L19)* - -___ - -### signature - -• **signature**: *string* - -*Defined in [types/src/index.ts:33](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/types/src/index.ts#L33)* - -___ - -### takerAddress - -• **takerAddress**: *string* - -*Inherited from [Order](#interface-order).[takerAddress](#takeraddress)* - -*Defined in [types/src/index.ts:17](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/types/src/index.ts#L17)* - -___ - -### takerAssetAmount - -• **takerAssetAmount**: *`BigNumber`* - -*Inherited from [Order](#interface-order).[takerAssetAmount](#takerassetamount)* - -*Defined in [types/src/index.ts:21](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/types/src/index.ts#L21)* - -___ - -### takerAssetData - -• **takerAssetData**: *string* - -*Inherited from [Order](#interface-order).[takerAssetData](#takerassetdata)* - -*Defined in [types/src/index.ts:27](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/types/src/index.ts#L27)* - -___ - -### takerFee - -• **takerFee**: *`BigNumber`* - -*Inherited from [Order](#interface-order).[takerFee](#takerfee)* - -*Defined in [types/src/index.ts:23](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/types/src/index.ts#L23)* - -___ - -### takerFeeAssetData - -• **takerFeeAssetData**: *string* - -*Inherited from [Order](#interface-order).[takerFeeAssetData](#takerfeeassetdata)* - -*Defined in [types/src/index.ts:29](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/types/src/index.ts#L29)* - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -## Type aliases - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- - - -
- - - - -## Type aliases - - - - - - - -### SwapQuote - -Ƭ **SwapQuote**: *[MarketBuySwapQuote](#interface-marketbuyswapquote) | [MarketSellSwapQuote](#interface-marketsellswapquote)* - -*Defined in [asset-swapper/src/types.ts:205](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/types.ts#L205)* - -
- - - - -## Object literals - -### `Const` affiliateFeeUtils - -#### â–ª **affiliateFeeUtils**: *object* - -*Defined in [asset-swapper/src/utils/affiliate_fee_utils.ts:7](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/utils/affiliate_fee_utils.ts#L7)* - -#### getTotalEthAmountWithAffiliateFee - -â–¸ **getTotalEthAmountWithAffiliateFee**(`swapQuoteInfo`: [SwapQuoteInfo](#interface-swapquoteinfo), `feePercentage`: number): *`BigNumber`* - -*Defined in [asset-swapper/src/utils/affiliate_fee_utils.ts:13](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/utils/affiliate_fee_utils.ts#L13)* - -Get the amount of eth to send for a forwarder contract call (includes takerAssetAmount, protocol fees, and specified affiliate fee amount) - -**Parameters:** - -Name | Type | Description | ------- | ------ | ------ | -`swapQuoteInfo` | [SwapQuoteInfo](#interface-swapquoteinfo) | SwapQuoteInfo to generate total eth amount from | -`feePercentage` | number | Percentage of additive fees to apply to totalTakerAssetAmount + protocol fee. (max 5%) | - -**Returns:** *`BigNumber`* - -
- - - - -## Object literals - -#### `Const` protocolFeeUtils - -#### â–ª **protocolFeeUtils**: *object* - -*Defined in [asset-swapper/src/utils/protocol_fee_utils.ts:9](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/utils/protocol_fee_utils.ts#L9)* - -#### calculateWorstCaseProtocolFee - -â–¸ **calculateWorstCaseProtocolFee**<**T**>(`orders`: `T`[], `gasPrice`: `BigNumber`): *`BigNumber`* - -*Defined in [asset-swapper/src/utils/protocol_fee_utils.ts:27](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/utils/protocol_fee_utils.ts#L27)* - -Calculates protocol fee with protofol fee multiplier for each fill. - -**Type parameters:** - -â–ª **T**: *`Order`* - -**Parameters:** - -Name | Type | ------- | ------ | -`orders` | `T`[] | -`gasPrice` | `BigNumber` | - -**Returns:** *`BigNumber`* - -#### getGasPriceEstimationOrThrowAsync - -â–¸ **getGasPriceEstimationOrThrowAsync**(): *`Promise`* - -*Defined in [asset-swapper/src/utils/protocol_fee_utils.ts:13](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/asset-swapper/src/utils/protocol_fee_utils.ts#L13)* - -Gets 'fast' gas price from Eth Gas Station. - -**Returns:** *`Promise`* - -
- - - - -## Type aliases - - - - - -### ConstructorStateMutability - -Ƭ **ConstructorStateMutability**: *"nonpayable" | "payable"* - -*Defined in [ethereum-types/src/index.ts:84](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/ethereum-types/src/index.ts#L84)* - -___ - - - - - - - -### EIP1193Event - -Ƭ **EIP1193Event**: *"accountsChanged" | "networkChanged" | "close" | "connect" | "notification"* - -*Defined in [ethereum-types/src/index.ts:70](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/ethereum-types/src/index.ts#L70)* - -Interface for providers that conform to EIP 1193 -Source: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1193.md - -___ - - - - - - - -### JSONRPCErrorCallback - -Ƭ **JSONRPCErrorCallback**: *function* - -*Defined in [ethereum-types/src/index.ts:3](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/ethereum-types/src/index.ts#L3)* - -#### Type declaration: - -▸ (`err`: `Error` | null, `result?`: [JSONRPCResponsePayload](#interface-jsonrpcresponsepayload)): *void* - -**Parameters:** - -Name | Type | ------- | ------ | -`err` | `Error` \| null | -`result?` | [JSONRPCResponsePayload](#interface-jsonrpcresponsepayload) | - -___ - - - - - - - - - -### StateMutability - -Ƭ **StateMutability**: *"pure" | "view" | [ConstructorStateMutability](#constructorstatemutability)* - -*Defined in [ethereum-types/src/index.ts:85](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/ethereum-types/src/index.ts#L85)* - -___ - -### SupportedProvider - -Ƭ **SupportedProvider**: *[Web3JsProvider](_ethereum_types_src_index_.md#web3jsprovider) | [GanacheProvider](#interface-ganacheprovider) | [EIP1193Provider](#interface-eip1193provider) | [ZeroExProvider](#interface-zeroexprovider)* - -*Defined in [ethereum-types/src/index.ts:9](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/ethereum-types/src/index.ts#L9)* - -Do not create your own provider. Use an existing provider from a Web3 or ProviderEngine library -Read more about Providers in the guides section of the 0x docs. - -___ - - - - - -### Web3JsProvider - -Ƭ **Web3JsProvider**: *[Web3JsV1Provider](#interface-web3jsv1provider) | [Web3JsV2Provider](#interface-web3jsv2provider) | [Web3JsV3Provider](#interface-web3jsv3provider)* - -*Defined in [ethereum-types/src/index.ts:11](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/ethereum-types/src/index.ts#L11)* - -
- - - - -## Variables - -### `Const` DEFAULT_TOKEN_PRECISION - -• **DEFAULT_TOKEN_PRECISION**: *`18`* = 18 - -*Defined in [orderbook/src/order_provider/base_order_provider.ts:9](https://github.com/0xProject/0x-monorepo/blob/36d7afd2a/packages/orderbook/src/order_provider/base_order_provider.ts#L9)* - -
- - - -
- - - -
- - - -
- - - - -## Type aliases - - diff --git a/packages/asset-swapper/package-lock.json b/packages/asset-swapper/package-lock.json deleted file mode 100644 index 5bfd95859e..0000000000 --- a/packages/asset-swapper/package-lock.json +++ /dev/null @@ -1,1839 +0,0 @@ -{ - "name": "@0x/asset-swapper", - "version": "2.1.0-beta.0", - "lockfileVersion": 1, - "requires": true, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true - }, - "arr-flatten": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", - "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", - "dev": true - }, - "arr-union": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", - "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", - "dev": true - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true - }, - "assign-symbols": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", - "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", - "dev": true - }, - "atob": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", - "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", - "dev": true - }, - "base": { - "version": "0.11.2", - "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", - "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", - "dev": true, - "requires": { - "cache-base": "^1.0.1", - "class-utils": "^0.3.5", - "component-emitter": "^1.2.1", - "define-property": "^1.0.0", - "isobject": "^3.0.1", - "mixin-deep": "^1.2.0", - "pascalcase": "^0.1.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } - } - }, - "big.js": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", - "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", - "dev": true - }, - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "cache-base": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", - "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", - "dev": true, - "requires": { - "collection-visit": "^1.0.0", - "component-emitter": "^1.2.1", - "get-value": "^2.0.6", - "has-value": "^1.0.0", - "isobject": "^3.0.1", - "set-value": "^2.0.0", - "to-object-path": "^0.3.0", - "union-value": "^1.0.0", - "unset-value": "^1.0.0" - } - }, - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "dependencies": { - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "class-utils": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", - "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", - "dev": true, - "requires": { - "arr-union": "^3.1.0", - "define-property": "^0.2.5", - "isobject": "^3.0.0", - "static-extend": "^0.1.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - } - } - }, - "cliui": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", - "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", - "dev": true, - "requires": { - "string-width": "^3.1.0", - "strip-ansi": "^5.2.0", - "wrap-ansi": "^5.1.0" - } - }, - "collection-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", - "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", - "dev": true, - "requires": { - "map-visit": "^1.0.0", - "object-visit": "^1.0.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true - }, - "component-emitter": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", - "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", - "dev": true - }, - "copy-descriptor": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", - "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", - "dev": true - }, - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", - "dev": true - }, - "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "dev": true, - "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", - "dev": true - }, - "decode-uri-component": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", - "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", - "dev": true - }, - "define-property": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", - "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", - "dev": true, - "requires": { - "is-descriptor": "^1.0.2", - "isobject": "^3.0.1" - }, - "dependencies": { - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } - } - }, - "detect-file": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz", - "integrity": "sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=", - "dev": true - }, - "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", - "dev": true - }, - "emojis-list": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", - "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=", - "dev": true - }, - "end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "dev": true, - "requires": { - "once": "^1.4.0" - } - }, - "enhanced-resolve": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.1.0.tgz", - "integrity": "sha512-F/7vkyTtyc/llOIn8oWclcB25KdRaiPBpZYDgJHgh/UHtpgT2p2eldQgtQnLtUvfMKPKxbRaQM/hHkvLHt1Vng==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "memory-fs": "^0.4.0", - "tapable": "^1.0.0" - } - }, - "errno": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", - "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==", - "dev": true, - "requires": { - "prr": "~1.0.1" - } - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true - }, - "execa": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", - "dev": true, - "requires": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - } - }, - "expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", - "dev": true, - "requires": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "expand-tilde": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", - "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=", - "dev": true, - "requires": { - "homedir-polyfill": "^1.0.1" - } - }, - "extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", - "dev": true, - "requires": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" - }, - "dependencies": { - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dev": true, - "requires": { - "is-plain-object": "^2.0.4" - } - } - } - }, - "extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "dev": true, - "requires": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } - } - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - }, - "findup-sync": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-3.0.0.tgz", - "integrity": "sha512-YbffarhcicEhOrm4CtrwdKBdCuz576RLdhJDsIfvNtxUuhdRet1qZcsMjqbePtAseKdAnDyM/IyXbu7PRPRLYg==", - "dev": true, - "requires": { - "detect-file": "^1.0.0", - "is-glob": "^4.0.0", - "micromatch": "^3.0.4", - "resolve-dir": "^1.0.1" - } - }, - "for-in": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", - "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", - "dev": true - }, - "fragment-cache": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", - "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", - "dev": true, - "requires": { - "map-cache": "^0.2.2" - } - }, - "get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true - }, - "get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "dev": true, - "requires": { - "pump": "^3.0.0" - } - }, - "get-value": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", - "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", - "dev": true - }, - "global-modules": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", - "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==", - "dev": true, - "requires": { - "global-prefix": "^3.0.0" - }, - "dependencies": { - "global-prefix": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", - "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", - "dev": true, - "requires": { - "ini": "^1.3.5", - "kind-of": "^6.0.2", - "which": "^1.3.1" - } - } - } - }, - "global-prefix": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", - "integrity": "sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=", - "dev": true, - "requires": { - "expand-tilde": "^2.0.2", - "homedir-polyfill": "^1.0.1", - "ini": "^1.3.4", - "is-windows": "^1.0.1", - "which": "^1.2.14" - } - }, - "graceful-fs": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.2.tgz", - "integrity": "sha512-IItsdsea19BoLC7ELy13q1iJFNmd7ofZH5+X/pJr90/nRoPEX0DJo1dHDbgtYWOhJhcCgMDTOw84RZ72q6lB+Q==", - "dev": true - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "has-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", - "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", - "dev": true, - "requires": { - "get-value": "^2.0.6", - "has-values": "^1.0.0", - "isobject": "^3.0.0" - } - }, - "has-values": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", - "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", - "dev": true, - "requires": { - "is-number": "^3.0.0", - "kind-of": "^4.0.0" - }, - "dependencies": { - "kind-of": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", - "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "homedir-polyfill": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", - "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", - "dev": true, - "requires": { - "parse-passwd": "^1.0.0" - } - }, - "import-local": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz", - "integrity": "sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ==", - "dev": true, - "requires": { - "pkg-dir": "^3.0.0", - "resolve-cwd": "^2.0.0" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "ini": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", - "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", - "dev": true - }, - "interpret": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.2.0.tgz", - "integrity": "sha512-mT34yGKMNceBQUoVn7iCDKDntA7SC6gycMAWzGx1z/CMCTV7b2AAtXlo3nRyHZ1FelRkQbQjprHSYGwzLtkVbw==", - "dev": true - }, - "invert-kv": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", - "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==", - "dev": true - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", - "dev": true - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - }, - "dependencies": { - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - } - } - }, - "is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", - "dev": true - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "dev": true, - "requires": { - "isobject": "^3.0.1" - } - }, - "is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", - "dev": true - }, - "is-windows": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", - "dev": true - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - }, - "json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", - "dev": true, - "requires": { - "minimist": "^1.2.0" - } - }, - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", - "dev": true - }, - "lcid": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", - "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", - "dev": true, - "requires": { - "invert-kv": "^2.0.0" - } - }, - "loader-utils": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz", - "integrity": "sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==", - "dev": true, - "requires": { - "big.js": "^5.2.2", - "emojis-list": "^2.0.0", - "json5": "^1.0.1" - } - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "map-age-cleaner": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", - "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", - "dev": true, - "requires": { - "p-defer": "^1.0.0" - } - }, - "map-cache": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", - "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", - "dev": true - }, - "map-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", - "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", - "dev": true, - "requires": { - "object-visit": "^1.0.0" - } - }, - "mem": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz", - "integrity": "sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==", - "dev": true, - "requires": { - "map-age-cleaner": "^0.1.1", - "mimic-fn": "^2.0.0", - "p-is-promise": "^2.0.0" - } - }, - "memory-fs": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", - "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=", - "dev": true, - "requires": { - "errno": "^0.1.3", - "readable-stream": "^2.0.1" - } - }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dev": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - } - }, - "mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true - }, - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true - }, - "mixin-deep": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", - "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", - "dev": true, - "requires": { - "for-in": "^1.0.2", - "is-extendable": "^1.0.1" - }, - "dependencies": { - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dev": true, - "requires": { - "is-plain-object": "^2.0.4" - } - } - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "nanomatch": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", - "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", - "dev": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "fragment-cache": "^0.2.1", - "is-windows": "^1.0.2", - "kind-of": "^6.0.2", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - } - }, - "nice-try": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", - "dev": true - }, - "npm-run-path": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", - "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", - "dev": true, - "requires": { - "path-key": "^2.0.0" - } - }, - "object-copy": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", - "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", - "dev": true, - "requires": { - "copy-descriptor": "^0.1.0", - "define-property": "^0.2.5", - "kind-of": "^3.0.3" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "object-visit": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", - "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", - "dev": true, - "requires": { - "isobject": "^3.0.0" - } - }, - "object.pick": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", - "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", - "dev": true, - "requires": { - "isobject": "^3.0.1" - } - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, - "requires": { - "wrappy": "1" - } - }, - "os-locale": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", - "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", - "dev": true, - "requires": { - "execa": "^1.0.0", - "lcid": "^2.0.0", - "mem": "^4.0.0" - } - }, - "p-defer": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", - "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=", - "dev": true - }, - "p-finally": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", - "dev": true - }, - "p-is-promise": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.1.0.tgz", - "integrity": "sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg==", - "dev": true - }, - "p-limit": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.1.tgz", - "integrity": "sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "requires": { - "p-limit": "^2.0.0" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true - }, - "parse-passwd": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", - "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=", - "dev": true - }, - "pascalcase": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", - "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", - "dev": true - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true - }, - "path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", - "dev": true - }, - "pkg-dir": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", - "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", - "dev": true, - "requires": { - "find-up": "^3.0.0" - } - }, - "posix-character-classes": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", - "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", - "dev": true - }, - "process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "dev": true - }, - "prr": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", - "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=", - "dev": true - }, - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "readable-stream": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "regex-not": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", - "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", - "dev": true, - "requires": { - "extend-shallow": "^3.0.2", - "safe-regex": "^1.1.0" - } - }, - "repeat-element": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", - "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==", - "dev": true - }, - "repeat-string": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", - "dev": true - }, - "require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", - "dev": true - }, - "require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", - "dev": true - }, - "resolve-cwd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz", - "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=", - "dev": true, - "requires": { - "resolve-from": "^3.0.0" - } - }, - "resolve-dir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", - "integrity": "sha1-eaQGRMNivoLybv/nOcm7U4IEb0M=", - "dev": true, - "requires": { - "expand-tilde": "^2.0.0", - "global-modules": "^1.0.0" - }, - "dependencies": { - "global-modules": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", - "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", - "dev": true, - "requires": { - "global-prefix": "^1.0.1", - "is-windows": "^1.0.1", - "resolve-dir": "^1.0.0" - } - } - } - }, - "resolve-from": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", - "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", - "dev": true - }, - "resolve-url": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", - "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", - "dev": true - }, - "ret": { - "version": "0.1.15", - "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", - "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", - "dev": true - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "safe-regex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", - "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", - "dev": true, - "requires": { - "ret": "~0.1.10" - } - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - }, - "set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", - "dev": true - }, - "set-value": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", - "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.3", - "split-string": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", - "dev": true, - "requires": { - "shebang-regex": "^1.0.0" - } - }, - "shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", - "dev": true - }, - "signal-exit": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", - "dev": true - }, - "snapdragon": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", - "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", - "dev": true, - "requires": { - "base": "^0.11.1", - "debug": "^2.2.0", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "map-cache": "^0.2.2", - "source-map": "^0.5.6", - "source-map-resolve": "^0.5.0", - "use": "^3.1.0" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "snapdragon-node": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", - "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", - "dev": true, - "requires": { - "define-property": "^1.0.0", - "isobject": "^3.0.0", - "snapdragon-util": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } - } - }, - "snapdragon-util": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", - "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", - "dev": true, - "requires": { - "kind-of": "^3.2.0" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - }, - "source-map-resolve": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz", - "integrity": "sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==", - "dev": true, - "requires": { - "atob": "^2.1.1", - "decode-uri-component": "^0.2.0", - "resolve-url": "^0.2.1", - "source-map-url": "^0.4.0", - "urix": "^0.1.0" - } - }, - "source-map-url": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", - "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", - "dev": true - }, - "split-string": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", - "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", - "dev": true, - "requires": { - "extend-shallow": "^3.0.0" - } - }, - "static-extend": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", - "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", - "dev": true, - "requires": { - "define-property": "^0.2.5", - "object-copy": "^0.1.0" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - } - } - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } - }, - "strip-eof": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", - "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", - "dev": true - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - }, - "tapable": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", - "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==", - "dev": true - }, - "to-object-path": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", - "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "to-regex": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", - "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", - "dev": true, - "requires": { - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "regex-not": "^1.0.2", - "safe-regex": "^1.1.0" - } - }, - "to-regex-range": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", - "dev": true, - "requires": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" - } - }, - "union-value": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", - "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", - "dev": true, - "requires": { - "arr-union": "^3.1.0", - "get-value": "^2.0.6", - "is-extendable": "^0.1.1", - "set-value": "^2.0.1" - } - }, - "unset-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", - "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", - "dev": true, - "requires": { - "has-value": "^0.3.1", - "isobject": "^3.0.0" - }, - "dependencies": { - "has-value": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", - "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", - "dev": true, - "requires": { - "get-value": "^2.0.3", - "has-values": "^0.1.4", - "isobject": "^2.0.0" - }, - "dependencies": { - "isobject": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", - "dev": true, - "requires": { - "isarray": "1.0.0" - } - } - } - }, - "has-values": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", - "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", - "dev": true - } - } - }, - "urix": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", - "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", - "dev": true - }, - "use": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", - "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", - "dev": true - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", - "dev": true - }, - "v8-compile-cache": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.0.3.tgz", - "integrity": "sha512-CNmdbwQMBjwr9Gsmohvm0pbL954tJrNzf6gWL3K+QMQf00PF7ERGrEiLgjuU3mKreLC2MeGhUsNV9ybTbLgd3w==", - "dev": true - }, - "webpack-cli": { - "version": "3.3.9", - "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-3.3.9.tgz", - "integrity": "sha512-xwnSxWl8nZtBl/AFJCOn9pG7s5CYUYdZxmmukv+fAHLcBIHM36dImfpQg3WfShZXeArkWlf6QRw24Klcsv8a5A==", - "dev": true, - "requires": { - "chalk": "2.4.2", - "cross-spawn": "6.0.5", - "enhanced-resolve": "4.1.0", - "findup-sync": "3.0.0", - "global-modules": "2.0.0", - "import-local": "2.0.0", - "interpret": "1.2.0", - "loader-utils": "1.2.3", - "supports-color": "6.1.0", - "v8-compile-cache": "2.0.3", - "yargs": "13.2.4" - } - }, - "which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, - "which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", - "dev": true - }, - "wrap-ansi": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", - "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.0", - "string-width": "^3.0.0", - "strip-ansi": "^5.0.0" - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true - }, - "y18n": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", - "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", - "dev": true - }, - "yargs": { - "version": "13.2.4", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.2.4.tgz", - "integrity": "sha512-HG/DWAJa1PAnHT9JAhNa8AbAv3FPaiLzioSjCcmuXXhP8MlpHO5vwls4g4j6n30Z74GVQj8Xa62dWVx1QCGklg==", - "dev": true, - "requires": { - "cliui": "^5.0.0", - "find-up": "^3.0.0", - "get-caller-file": "^2.0.1", - "os-locale": "^3.1.0", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^3.0.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^13.1.0" - } - }, - "yargs-parser": { - "version": "13.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.1.tgz", - "integrity": "sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ==", - "dev": true, - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } - } - } -} diff --git a/packages/asset-swapper/package.json b/packages/asset-swapper/package.json deleted file mode 100644 index f862f59b3d..0000000000 --- a/packages/asset-swapper/package.json +++ /dev/null @@ -1,95 +0,0 @@ -{ - "name": "@0x/asset-swapper", - "version": "16.66.19", - "private": true, - "engines": { - "node": ">=6.12" - }, - "description": "Convenience package for discovering and buying assets on-chain and off-chain.", - "main": "lib/src/index.js", - "types": "lib/src/index.d.ts", - "scripts": {}, - "config": { - "publicInterfaceContracts": "ERC20BridgeSampler,BalanceChecker,FakeTaker", - "abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually.", - "abis": "./test/generated-artifacts/@(ApproximateBuys|BalanceChecker|BalancerSampler|BalancerV2BatchSampler|BalancerV2Common|BalancerV2Sampler|BancorSampler|BancorV3Sampler|CompoundSampler|CurveSampler|DODOSampler|DODOV2Sampler|ERC20BridgeSampler|FakeTaker|GMXSampler|IBalancer|IBalancerV2Vault|IBancor|IBancorV3|ICurve|IGMX|IMStable|IMooniswap|IMultiBridge|IPlatypus|IShell|IUniswapExchangeQuotes|IUniswapV2Router01|KyberDmmSampler|LidoSampler|LiquidityProviderSampler|MStableSampler|MakerPSMSampler|MooniswapSampler|NativeOrderSampler|PlatypusSampler|SamplerUtils|ShellSampler|SynthetixSampler|TestNativeOrderSampler|TwoHopSampler|UniswapSampler|UniswapV2Sampler|UniswapV3Sampler|UtilitySampler|VelodromeSampler|WooPPSampler).json", - "postpublish": { - "assets": [] - } - }, - "repository": { - "type": "git", - "url": "https://github.com/0xProject/protocol.git" - }, - "author": "David Sun", - "license": "Apache-2.0", - "bugs": { - "url": "https://github.com/0xProject/protocol/issues" - }, - "homepage": "https://0x.org/asset-swapper", - "gitpkg": { - "registry": "git@github.com:0xProject/gitpkg-registry.git" - }, - "dependencies": { - "@0x/assert": "^3.0.35", - "@0x/base-contract": "^7.0.0", - "@0x/contract-addresses": "^7.4.0", - "@0x/contract-wrappers": "^13.22.11", - "@0x/contracts-erc20": "^3.3.52", - "@0x/contracts-zero-ex": "^0.38.1", - "@0x/dev-utils": "^5.0.0", - "@0x/fast-abi": "^0.0.5", - "@0x/json-schemas": "^6.4.4", - "@0x/neon-router": "^0.3.5", - "@0x/protocol-utils": "^11.17.1", - "@0x/quote-server": "^8.0.0", - "@0x/types": "^3.3.6", - "@0x/utils": "^7.0.0", - "@0x/web3-wrapper": "^8.0.0", - "@balancer-labs/sdk": "0.1.6", - "@bancor/sdk": "0.2.9", - "@ethersproject/abi": "^5.0.1", - "@ethersproject/address": "^5.0.1", - "@ethersproject/contracts": "^5.0.1", - "@ethersproject/providers": "^5.0.4", - "@ethersproject/strings": "^5.0.10", - "axios": "^0.21.1", - "axios-mock-adapter": "^1.19.0", - "balancer-labs-sor-v1": "npm:@balancer-labs/sor@0.3.2", - "ethereum-types": "^3.7.1", - "graphql": "^15.4.0", - "graphql-request": "^3.4.0", - "heartbeats": "^5.0.1", - "lodash": "^4.17.15", - "msw": "^0.44.2" - }, - "devDependencies": { - "@0x/abi-gen": "^5.8.1", - "@0x/contracts-gen": "^2.0.47", - "@0x/contracts-test-utils": "^5.4.42", - "@0x/sol-compiler": "^4.8.2", - "@0x/subproviders": "^7.0.0", - "@0x/ts-doc-gen": "^0.0.28", - "@0x/tslint-config": "^4.1.4", - "@types/lodash": "4.14.137", - "@types/mocha": "^5.2.7", - "@types/node": "12.12.54", - "chai": "^4.0.1", - "chai-as-promised": "^7.1.0", - "chai-bignumber": "^3.0.0", - "dirty-chai": "^2.0.1", - "gitpkg": "https://github.com/0xProject/gitpkg.git", - "mocha": "^6.2.0", - "npm-run-all": "^4.1.2", - "nyc": "^11.0.1", - "shx": "^0.2.2", - "tslint": "^6.1.3", - "typedoc": "~0.16.11", - "typemoq": "^2.1.0", - "typescript": "4.6.3" - }, - "publishConfig": { - "access": "public" - }, - "gitHead": "4f91bfd907996b2f4dd383778b50c479c2602b56" -} diff --git a/packages/asset-swapper/src/artifacts.ts b/packages/asset-swapper/src/artifacts.ts deleted file mode 100644 index 9ea4a461e9..0000000000 --- a/packages/asset-swapper/src/artifacts.ts +++ /dev/null @@ -1,15 +0,0 @@ -/* - * ----------------------------------------------------------------------------- - * Warning: This file is auto-generated by contracts-gen. Don't edit manually. - * ----------------------------------------------------------------------------- - */ -import { ContractArtifact } from 'ethereum-types'; - -import * as BalanceChecker from '../generated-artifacts/BalanceChecker.json'; -import * as ERC20BridgeSampler from '../generated-artifacts/ERC20BridgeSampler.json'; -import * as FakeTaker from '../generated-artifacts/FakeTaker.json'; -export const artifacts = { - ERC20BridgeSampler: ERC20BridgeSampler as ContractArtifact, - BalanceChecker: BalanceChecker as ContractArtifact, - FakeTaker: FakeTaker as ContractArtifact, -}; diff --git a/packages/asset-swapper/src/constants.ts b/packages/asset-swapper/src/constants.ts deleted file mode 100644 index c78f2b692e..0000000000 --- a/packages/asset-swapper/src/constants.ts +++ /dev/null @@ -1,128 +0,0 @@ -import { ChainId } from '@0x/contract-addresses'; -import { SignatureType } from '@0x/protocol-utils'; -import { BigNumber, logUtils } from '@0x/utils'; - -import { - AffiliateFeeType, - ExchangeProxyContractOpts, - LogFunction, - OrderPrunerOpts, - OrderPrunerPermittedFeeTypes, - RfqRequestOpts, - SwapQuoteGetOutputOpts, - SwapQuoteRequestOpts, - SwapQuoterOpts, -} from './types'; -import { - DEFAULT_GET_MARKET_ORDERS_OPTS, - DEFAULT_INTERMEDIATE_TOKENS_BY_CHAIN_ID, - DEFAULT_TOKEN_ADJACENCY_GRAPH_BY_CHAIN_ID, -} from './utils/market_operation_utils/constants'; - -const ZERO_EX_GAS_API_URL = 'https://gas.api.0x.org/source/median'; -const NULL_BYTES = '0x'; -const NULL_ERC20_ASSET_DATA = '0xf47261b00000000000000000000000000000000000000000000000000000000000000000'; -const NULL_ADDRESS = '0x0000000000000000000000000000000000000000'; -const MAINNET_CHAIN_ID = 1; -const ONE_SECOND_MS = 1000; -const ONE_MINUTE_SECS = 60; -const ONE_MINUTE_MS = ONE_SECOND_MS * ONE_MINUTE_SECS; -const DEFAULT_PER_PAGE = 1000; -const ALT_MM_IMPUTED_INDICATIVE_EXPIRY_SECONDS = 180; - -const DEFAULT_ORDER_PRUNER_OPTS: OrderPrunerOpts = { - expiryBufferMs: 120000, // 2 minutes - permittedOrderFeeTypes: new Set([OrderPrunerPermittedFeeTypes.NoFees]), // Default asset-swapper for CFL oriented fee types -}; - -// 6 seconds polling interval -const PROTOCOL_FEE_UTILS_POLLING_INTERVAL_IN_MS = 6000; -const PROTOCOL_FEE_MULTIPLIER = new BigNumber(0); - -// default 50% buffer for selecting native orders to be aggregated with other sources -const MARKET_UTILS_AMOUNT_BUFFER_PERCENTAGE = 0.5; - -export const ZERO_AMOUNT = new BigNumber(0); -const DEFAULT_SWAP_QUOTER_OPTS: SwapQuoterOpts = { - chainId: ChainId.Mainnet, - orderRefreshIntervalMs: 10000, // 10 seconds - ...DEFAULT_ORDER_PRUNER_OPTS, - samplerGasLimit: 500e6, - zeroExGasApiUrl: ZERO_EX_GAS_API_URL, - rfqt: { - integratorsWhitelist: [], - makerAssetOfferings: {}, - txOriginBlacklist: new Set(), - }, - tokenAdjacencyGraph: DEFAULT_TOKEN_ADJACENCY_GRAPH_BY_CHAIN_ID[ChainId.Mainnet], -}; - -const DEFAULT_EXCHANGE_PROXY_EXTENSION_CONTRACT_OPTS: ExchangeProxyContractOpts = { - isFromETH: false, - isToETH: false, - affiliateFee: { - feeType: AffiliateFeeType.None, - recipient: NULL_ADDRESS, - buyTokenFeeAmount: ZERO_AMOUNT, - sellTokenFeeAmount: ZERO_AMOUNT, - }, - refundReceiver: NULL_ADDRESS, - isMetaTransaction: false, - shouldSellEntireBalance: false, -}; - -const DEFAULT_EXCHANGE_PROXY_SWAP_QUOTE_GET_OPTS: SwapQuoteGetOutputOpts = { - extensionContractOpts: DEFAULT_EXCHANGE_PROXY_EXTENSION_CONTRACT_OPTS, -}; - -const DEFAULT_SWAP_QUOTE_REQUEST_OPTS: SwapQuoteRequestOpts = { - ...DEFAULT_GET_MARKET_ORDERS_OPTS, -}; - -const DEFAULT_RFQT_REQUEST_OPTS: Partial = { - makerEndpointMaxResponseTimeMs: 1000, -}; - -export const DEFAULT_INFO_LOGGER: LogFunction = (obj, msg) => - logUtils.log(`${msg ? `${msg}: ` : ''}${JSON.stringify(obj)}`); -export const DEFAULT_WARNING_LOGGER: LogFunction = (obj, msg) => - logUtils.warn(`${msg ? `${msg}: ` : ''}${JSON.stringify(obj)}`); - -const EMPTY_BYTES32 = '0x0000000000000000000000000000000000000000000000000000000000000000'; -export const INVALID_SIGNATURE = { signatureType: SignatureType.Invalid, v: 1, r: EMPTY_BYTES32, s: EMPTY_BYTES32 }; - -export { DEFAULT_FEE_SCHEDULE, DEFAULT_GAS_SCHEDULE } from './utils/market_operation_utils/constants'; - -export const POSITIVE_SLIPPAGE_FEE_TRANSFORMER_GAS = new BigNumber(30000); - -export const KEEP_ALIVE_TTL = 5 * 60 * ONE_SECOND_MS; - -export const constants = { - ZERO_EX_GAS_API_URL, - PROTOCOL_FEE_MULTIPLIER, - POSITIVE_SLIPPAGE_FEE_TRANSFORMER_GAS, - NULL_BYTES, - ZERO_AMOUNT, - NULL_ADDRESS, - MAINNET_CHAIN_ID, - DEFAULT_ORDER_PRUNER_OPTS, - ETHER_TOKEN_DECIMALS: 18, - ONE_AMOUNT: new BigNumber(1), - ONE_SECOND_MS, - ONE_MINUTE_MS, - DEFAULT_SWAP_QUOTER_OPTS, - DEFAULT_INTERMEDIATE_TOKENS_BY_CHAIN_ID, - DEFAULT_SWAP_QUOTE_REQUEST_OPTS, - DEFAULT_EXCHANGE_PROXY_SWAP_QUOTE_GET_OPTS, - DEFAULT_EXCHANGE_PROXY_EXTENSION_CONTRACT_OPTS, - DEFAULT_PER_PAGE, - DEFAULT_RFQT_REQUEST_OPTS, - NULL_ERC20_ASSET_DATA, - PROTOCOL_FEE_UTILS_POLLING_INTERVAL_IN_MS, - MARKET_UTILS_AMOUNT_BUFFER_PERCENTAGE, - BRIDGE_ASSET_DATA_PREFIX: '0xdc1600f3', - DEFAULT_INFO_LOGGER, - DEFAULT_WARNING_LOGGER, - EMPTY_BYTES32, - ALT_MM_IMPUTED_INDICATIVE_EXPIRY_SECONDS, -}; diff --git a/packages/asset-swapper/src/errors.ts b/packages/asset-swapper/src/errors.ts deleted file mode 100644 index 8ce06f2905..0000000000 --- a/packages/asset-swapper/src/errors.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { BigNumber } from '@0x/utils'; - -import { SwapQuoterError } from './types'; - -/** - * Error class representing insufficient asset liquidity - */ -export class InsufficientAssetLiquidityError extends Error { - /** - * The amount availabe to fill (in base units) factoring in slippage. - */ - public amountAvailableToFill: BigNumber; - /** - * @param amountAvailableToFill The amount availabe to fill (in base units) factoring in slippage - */ - constructor(amountAvailableToFill: BigNumber) { - super(SwapQuoterError.InsufficientAssetLiquidity); - this.amountAvailableToFill = amountAvailableToFill; - // Setting prototype so instanceof works. See https://github.com/Microsoft/TypeScript/wiki/Breaking-Changes#extending-built-ins-like-error-array-and-map-may-no-longer-work - Object.setPrototypeOf(this, InsufficientAssetLiquidityError.prototype); - } -} diff --git a/packages/asset-swapper/src/globals.d.ts b/packages/asset-swapper/src/globals.d.ts deleted file mode 100644 index c5df161a64..0000000000 --- a/packages/asset-swapper/src/globals.d.ts +++ /dev/null @@ -1,8 +0,0 @@ -declare module '*.json' { - const json: any; - /* tslint:disable */ - export default json; - /* tslint:enable */ -} - -declare module 'heartbeats'; diff --git a/packages/asset-swapper/src/index.ts b/packages/asset-swapper/src/index.ts deleted file mode 100644 index 7f1dc8fe78..0000000000 --- a/packages/asset-swapper/src/index.ts +++ /dev/null @@ -1,196 +0,0 @@ -export { - AwaitTransactionSuccessOpts, - ContractFunctionObj, - ContractTxFunctionObj, - SendTransactionOpts, -} from '@0x/base-contract'; -export { ContractAddresses, ChainId, getContractAddressesForChainOrThrow } from '@0x/contract-addresses'; -export { - V4RFQFirmQuote, - V4RFQIndicativeQuote, - V4SignedRfqOrder, - TakerRequestQueryParamsUnnested as TakerRequestQueryParams, -} from '@0x/quote-server'; -export { Asset, AssetPairsItem, DecodedLogEvent, EventCallback, IndexedFilterValues } from '@0x/types'; -export { BigNumber } from '@0x/utils'; -export { - RfqOrderFields, - LimitOrderFields, - FillQuoteTransformerOrderType, - RfqOrder, - LimitOrder, - Signature, - SignatureType, -} from '@0x/protocol-utils'; -export { AxiosInstance } from 'axios'; -export { - AbiDefinition, - BlockParam, - BlockParamLiteral, - CallData, - CompilerOpts, - CompilerSettings, - CompilerSettingsMetadata, - ConstructorAbi, - ConstructorStateMutability, - ContractAbi, - ContractArtifact, - ContractChainData, - ContractChains, - ContractEventArg, - DataItem, - DecodedLogArgs, - DevdocOutput, - EIP1193Event, - EIP1193Provider, - EventAbi, - EventParameter, - EvmBytecodeOutput, - EvmBytecodeOutputLinkReferences, - EvmOutput, - FallbackAbi, - FunctionAbi, - GanacheProvider, - GethCallOverrides, - JSONRPCErrorCallback, - JSONRPCRequestPayload, - JSONRPCResponseError, - JSONRPCResponsePayload, - LogWithDecodedArgs, - MethodAbi, - OptimizerSettings, - OutputField, - ParamDescription, - RevertErrorAbi, - StandardContractOutput, - StateMutability, - SupportedProvider, - TupleDataItem, - TxData, - TxDataPayable, - Web3JsProvider, - Web3JsV1Provider, - Web3JsV2Provider, - Web3JsV3Provider, - ZeroExProvider, -} from 'ethereum-types'; -export { artifacts } from './artifacts'; -export { InsufficientAssetLiquidityError } from './errors'; -export { SwapQuoteConsumer } from './quote_consumers/swap_quote_consumer'; -export { SwapQuoter, Orderbook } from './swap_quoter'; -export { - AltOffering, - AltRfqMakerAssetOfferings, - AffiliateFeeType, - AffiliateFeeAmount, - AssetSwapperContractAddresses, - CalldataInfo, - ExchangeProxyContractOpts, - ExchangeProxyRefundReceiver, - GetExtensionContractTypeOpts, - Integrator, - LogFunction, - MarketBuySwapQuote, - MarketOperation, - MarketSellSwapQuote, - MockedRfqQuoteResponse, - OrderPrunerPermittedFeeTypes, - RfqMakerAssetOfferings, - RfqFirmQuoteValidator, - RfqRequestOpts, - SamplerOverrides, - SignedNativeOrder, - SignedOrder, - SwapQuote, - SwapQuoteConsumerBase, - SwapQuoteConsumerError, - SwapQuoteConsumerOpts, - SwapQuoteExecutionOpts, - SwapQuoteGetOutputOpts, - SwapQuoteInfo, - SwapQuoteOrdersBreakdown, - SwapQuoteRequestOpts, - SwapQuoterError, - SwapQuoterOpts, - SwapQuoterRfqOpts, - SamplerMetrics, -} from './types'; -export { affiliateFeeUtils } from './utils/affiliate_fee_utils'; -export { - IRfqClient, - RfqClientV1Price, - RfqClientV1PriceRequest, - RfqClientV1PriceResponse, - RfqClientV1Quote, - RfqClientV1QuoteRequest, - RfqClientV1QuoteResponse, -} from './utils/irfq_client'; -export { - DEFAULT_TOKEN_ADJACENCY_GRAPH_BY_CHAIN_ID, - DEFAULT_GAS_SCHEDULE, - SOURCE_FLAGS, - BUY_SOURCE_FILTER_BY_CHAIN_ID, - SELL_SOURCE_FILTER_BY_CHAIN_ID, - NATIVE_FEE_TOKEN_BY_CHAIN_ID, - ZERO_AMOUNT, -} from './utils/market_operation_utils/constants'; -export { - Parameters, - SamplerContractCall, - SamplerContractOperation, -} from './utils/market_operation_utils/sampler_contract_operation'; -export { - BalancerFillData, - BancorFillData, - CurveFillData, - CurveFunctionSelectors, - CurveInfo, - DexSample, - DODOFillData, - ERC20BridgeSource, - ExchangeProxyOverhead, - FeeSchedule, - GasSchedule, - Fill, - FillAdjustor, - FillData, - GetMarketOrdersRfqOpts, - LiquidityProviderFillData, - LiquidityProviderRegistry, - MooniswapFillData, - MultiHopFillData, - NativeRfqOrderFillData, - NativeLimitOrderFillData, - NativeFillData, - OptimizedMarketOrder, - SourceQuoteOperation, - UniswapV2FillData, -} from './utils/market_operation_utils/types'; - -export { TokenAdjacencyGraph, TokenAdjacencyGraphBuilder } from './utils/token_adjacency_graph'; -export { IdentityFillAdjustor } from './utils/market_operation_utils/identity_fill_adjustor'; -export { ProtocolFeeUtils } from './utils/protocol_fee_utils'; -export { - BridgeQuoteReportEntry, - jsonifyFillData, - MultiHopQuoteReportEntry, - NativeLimitOrderQuoteReportEntry, - NativeRfqOrderQuoteReportEntry, - QuoteReport, - QuoteReportEntry, - ExtendedQuoteReport, - ExtendedQuoteReportSources, - ExtendedQuoteReportEntry, - ExtendedQuoteReportIndexedEntry, - ExtendedQuoteReportIndexedEntryOutbound, - PriceComparisonsReport, -} from './utils/quote_report_generator'; -export { QuoteRequestor, V4RFQIndicativeQuoteMM } from './utils/quote_requestor'; -export { ERC20BridgeSamplerContract, BalanceCheckerContract, FakeTakerContract } from './wrappers'; -import { ERC20BridgeSource } from './utils/market_operation_utils/types'; -export type Native = ERC20BridgeSource.Native; -export type MultiHop = ERC20BridgeSource.MultiHop; - -export { rfqtMocker, RfqtQuoteEndpoint } from './utils/rfqt_mocker'; - -export { adjustOutput } from './utils/market_operation_utils/fills'; diff --git a/packages/asset-swapper/src/noop_samplers/AaveV2Sampler.ts b/packages/asset-swapper/src/noop_samplers/AaveV2Sampler.ts deleted file mode 100644 index c56f12601e..0000000000 --- a/packages/asset-swapper/src/noop_samplers/AaveV2Sampler.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { BigNumber } from '@0x/utils'; - -import { ZERO_AMOUNT } from '../constants'; -export interface AaveInfo { - lendingPool: string; - aToken: string; - underlyingToken: string; -} -// tslint:disable-next-line:no-unnecessary-class -export class AaveV2Sampler { - public static sampleSellsFromAaveV2( - aaveInfo: AaveInfo, - takerToken: string, - makerToken: string, - takerTokenAmounts: BigNumber[], - ): BigNumber[] { - // Deposit/Withdrawal underlying <-> aToken is always 1:1 - if ( - (takerToken.toLowerCase() === aaveInfo.aToken.toLowerCase() && - makerToken.toLowerCase() === aaveInfo.underlyingToken.toLowerCase()) || - (takerToken.toLowerCase() === aaveInfo.underlyingToken.toLowerCase() && - makerToken.toLowerCase() === aaveInfo.aToken.toLowerCase()) - ) { - return takerTokenAmounts; - } - - // Not matching the reserve return 0 results - const numSamples = takerTokenAmounts.length; - - const makerTokenAmounts = new Array(numSamples); - makerTokenAmounts.fill(ZERO_AMOUNT); - return makerTokenAmounts; - } - - public static sampleBuysFromAaveV2( - aaveInfo: AaveInfo, - takerToken: string, - makerToken: string, - makerTokenAmounts: BigNumber[], - ): BigNumber[] { - // Deposit/Withdrawal underlying <-> aToken is always 1:1 - if ( - (takerToken.toLowerCase() === aaveInfo.aToken.toLowerCase() && - makerToken.toLowerCase() === aaveInfo.underlyingToken.toLowerCase()) || - (takerToken.toLowerCase() === aaveInfo.underlyingToken.toLowerCase() && - makerToken.toLowerCase() === aaveInfo.aToken.toLowerCase()) - ) { - return makerTokenAmounts; - } - - // Not matching the reserve return 0 results - const numSamples = makerTokenAmounts.length; - const takerTokenAmounts = new Array(numSamples); - takerTokenAmounts.fill(ZERO_AMOUNT); - return takerTokenAmounts; - } -} diff --git a/packages/asset-swapper/src/noop_samplers/GeistSampler.ts b/packages/asset-swapper/src/noop_samplers/GeistSampler.ts deleted file mode 100644 index 3e43de6efe..0000000000 --- a/packages/asset-swapper/src/noop_samplers/GeistSampler.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { BigNumber } from '@0x/utils'; - -import { ZERO_AMOUNT } from '../constants'; -export interface GeistInfo { - lendingPool: string; - gToken: string; - underlyingToken: string; -} -// tslint:disable-next-line:no-unnecessary-class -export class GeistSampler { - public static sampleSellsFromGeist( - geistInfo: GeistInfo, - takerToken: string, - makerToken: string, - takerTokenAmounts: BigNumber[], - ): BigNumber[] { - // Deposit/Withdrawal underlying <-> gToken is always 1:1 - if ( - (takerToken.toLowerCase() === geistInfo.gToken.toLowerCase() && - makerToken.toLowerCase() === geistInfo.underlyingToken.toLowerCase()) || - (takerToken.toLowerCase() === geistInfo.underlyingToken.toLowerCase() && - makerToken.toLowerCase() === geistInfo.gToken.toLowerCase()) - ) { - return takerTokenAmounts; - } - - // Not matching the reserve return 0 results - const numSamples = takerTokenAmounts.length; - - const makerTokenAmounts = new Array(numSamples); - makerTokenAmounts.fill(ZERO_AMOUNT); - return makerTokenAmounts; - } - - public static sampleBuysFromGeist( - geistInfo: GeistInfo, - takerToken: string, - makerToken: string, - makerTokenAmounts: BigNumber[], - ): BigNumber[] { - // Deposit/Withdrawal underlying <-> gToken is always 1:1 - if ( - (takerToken.toLowerCase() === geistInfo.gToken.toLowerCase() && - makerToken.toLowerCase() === geistInfo.underlyingToken.toLowerCase()) || - (takerToken.toLowerCase() === geistInfo.underlyingToken.toLowerCase() && - makerToken.toLowerCase() === geistInfo.gToken.toLowerCase()) - ) { - return makerTokenAmounts; - } - - // Not matching the reserve return 0 results - const numSamples = makerTokenAmounts.length; - const takerTokenAmounts = new Array(numSamples); - takerTokenAmounts.fill(ZERO_AMOUNT); - return takerTokenAmounts; - } -} diff --git a/packages/asset-swapper/src/quote_consumers/exchange_proxy_swap_quote_consumer.ts b/packages/asset-swapper/src/quote_consumers/exchange_proxy_swap_quote_consumer.ts deleted file mode 100644 index aafb6cfaf0..0000000000 --- a/packages/asset-swapper/src/quote_consumers/exchange_proxy_swap_quote_consumer.ts +++ /dev/null @@ -1,733 +0,0 @@ -import { ChainId, ContractAddresses } from '@0x/contract-addresses'; -import { IZeroExContract } from '@0x/contract-wrappers'; -import { - encodeAffiliateFeeTransformerData, - encodeCurveLiquidityProviderData, - encodeFillQuoteTransformerData, - encodePayTakerTransformerData, - encodePositiveSlippageFeeTransformerData, - encodeWethTransformerData, - ETH_TOKEN_ADDRESS, - FillQuoteTransformerOrderType, - FillQuoteTransformerSide, - findTransformerNonce, -} from '@0x/protocol-utils'; -import { BigNumber } from '@0x/utils'; -import * as _ from 'lodash'; - -import { constants, POSITIVE_SLIPPAGE_FEE_TRANSFORMER_GAS } from '../constants'; -import { - AffiliateFeeType, - CalldataInfo, - ExchangeProxyContractOpts, - MarketBuySwapQuote, - MarketOperation, - MarketSellSwapQuote, - SwapQuote, - SwapQuoteConsumerBase, - SwapQuoteConsumerOpts, - SwapQuoteExecutionOpts, - SwapQuoteGetOutputOpts, -} from '../types'; -import { assert } from '../utils/assert'; -import { - CURVE_LIQUIDITY_PROVIDER_BY_CHAIN_ID, - NATIVE_FEE_TOKEN_BY_CHAIN_ID, -} from '../utils/market_operation_utils/constants'; -import { - CurveFillData, - ERC20BridgeSource, - FinalUniswapV3FillData, - LiquidityProviderFillData, - NativeRfqOrderFillData, - OptimizedMarketBridgeOrder, - OptimizedMarketOrder, - UniswapV2FillData, -} from '../utils/market_operation_utils/types'; - -import { - multiplexPlpEncoder, - multiplexRfqEncoder, - MultiplexSubcall, - multiplexTransformERC20Encoder, - multiplexUniswapEncoder, -} from './multiplex_encoders'; -import { - getFQTTransformerDataFromOptimizedOrders, - isBuyQuote, - isDirectSwapCompatible, - isMultiplexBatchFillCompatible, - isMultiplexMultiHopFillCompatible, - requiresTransformERC20, -} from './quote_consumer_utils'; - -const MAX_UINT256 = new BigNumber(2).pow(256).minus(1); -const { NULL_ADDRESS, NULL_BYTES, ZERO_AMOUNT } = constants; - -// use the same order in IPancakeSwapFeature.sol -const PANCAKE_SWAP_FORKS = [ - ERC20BridgeSource.PancakeSwap, - ERC20BridgeSource.PancakeSwapV2, - ERC20BridgeSource.BakerySwap, - ERC20BridgeSource.SushiSwap, - ERC20BridgeSource.ApeSwap, - ERC20BridgeSource.CheeseSwap, -]; -const FAKE_PROVIDER: any = { - sendAsync(): void { - return; - }, -}; - -export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase { - public readonly chainId: ChainId; - public readonly transformerNonces: { - wethTransformer: number; - payTakerTransformer: number; - fillQuoteTransformer: number; - affiliateFeeTransformer: number; - positiveSlippageFeeTransformer: number; - }; - - private readonly _exchangeProxy: IZeroExContract; - - constructor(public readonly contractAddresses: ContractAddresses, options: Partial = {}) { - const { chainId } = _.merge({}, constants.DEFAULT_SWAP_QUOTER_OPTS, options); - assert.isNumber('chainId', chainId); - this.chainId = chainId; - this.contractAddresses = contractAddresses; - this._exchangeProxy = new IZeroExContract(contractAddresses.exchangeProxy, FAKE_PROVIDER); - this.transformerNonces = { - wethTransformer: findTransformerNonce( - contractAddresses.transformers.wethTransformer, - contractAddresses.exchangeProxyTransformerDeployer, - ), - payTakerTransformer: findTransformerNonce( - contractAddresses.transformers.payTakerTransformer, - contractAddresses.exchangeProxyTransformerDeployer, - ), - fillQuoteTransformer: findTransformerNonce( - contractAddresses.transformers.fillQuoteTransformer, - contractAddresses.exchangeProxyTransformerDeployer, - ), - affiliateFeeTransformer: findTransformerNonce( - contractAddresses.transformers.affiliateFeeTransformer, - contractAddresses.exchangeProxyTransformerDeployer, - ), - positiveSlippageFeeTransformer: findTransformerNonce( - contractAddresses.transformers.positiveSlippageFeeTransformer, - contractAddresses.exchangeProxyTransformerDeployer, - ), - }; - } - - public async getCalldataOrThrowAsync( - quote: MarketBuySwapQuote | MarketSellSwapQuote, - opts: Partial = {}, - ): Promise { - const optsWithDefaults: ExchangeProxyContractOpts = { - ...constants.DEFAULT_EXCHANGE_PROXY_EXTENSION_CONTRACT_OPTS, - ...opts.extensionContractOpts, - }; - // tslint:disable-next-line:no-object-literal-type-assertion - const { refundReceiver, affiliateFee, isFromETH, isToETH, shouldSellEntireBalance } = optsWithDefaults; - - const sellToken = quote.takerToken; - const buyToken = quote.makerToken; - - // Take the bounds from the worst case - const sellAmount = BigNumber.max( - quote.bestCaseQuoteInfo.totalTakerAmount, - quote.worstCaseQuoteInfo.totalTakerAmount, - ); - let minBuyAmount = quote.worstCaseQuoteInfo.makerAmount; - let ethAmount = quote.worstCaseQuoteInfo.protocolFeeInWeiAmount; - - if (isFromETH) { - ethAmount = ethAmount.plus(sellAmount); - } - - const slippedOrders = slipNonNativeOrders(quote); - - // VIP routes. - if ( - this.chainId === ChainId.Mainnet && - isDirectSwapCompatible(quote, optsWithDefaults, [ERC20BridgeSource.UniswapV2, ERC20BridgeSource.SushiSwap]) - ) { - const source = slippedOrders[0].source; - const fillData = (slippedOrders[0] as OptimizedMarketBridgeOrder).fillData; - return { - calldataHexString: this._exchangeProxy - .sellToUniswap( - fillData.tokenAddressPath.map((a, i) => { - if (i === 0 && isFromETH) { - return ETH_TOKEN_ADDRESS; - } - if (i === fillData.tokenAddressPath.length - 1 && isToETH) { - return ETH_TOKEN_ADDRESS; - } - return a; - }), - sellAmount, - minBuyAmount, - source === ERC20BridgeSource.SushiSwap, - ) - .getABIEncodedTransactionData(), - ethAmount: isFromETH ? sellAmount : ZERO_AMOUNT, - toAddress: this._exchangeProxy.address, - allowanceTarget: this._exchangeProxy.address, - gasOverhead: ZERO_AMOUNT, - }; - } - - if ( - this.chainId === ChainId.Mainnet && - isDirectSwapCompatible(quote, optsWithDefaults, [ERC20BridgeSource.UniswapV3]) - ) { - const fillData = (slippedOrders[0] as OptimizedMarketBridgeOrder).fillData; - let _calldataHexString; - if (isFromETH) { - _calldataHexString = this._exchangeProxy - .sellEthForTokenToUniswapV3(fillData.uniswapPath, minBuyAmount, NULL_ADDRESS) - .getABIEncodedTransactionData(); - } else if (isToETH) { - _calldataHexString = this._exchangeProxy - .sellTokenForEthToUniswapV3(fillData.uniswapPath, sellAmount, minBuyAmount, NULL_ADDRESS) - .getABIEncodedTransactionData(); - } else { - _calldataHexString = this._exchangeProxy - .sellTokenForTokenToUniswapV3(fillData.uniswapPath, sellAmount, minBuyAmount, NULL_ADDRESS) - .getABIEncodedTransactionData(); - } - return { - calldataHexString: _calldataHexString, - ethAmount: isFromETH ? sellAmount : ZERO_AMOUNT, - toAddress: this._exchangeProxy.address, - allowanceTarget: this._exchangeProxy.address, - gasOverhead: ZERO_AMOUNT, - }; - } - - if ( - this.chainId === ChainId.BSC && - isDirectSwapCompatible(quote, optsWithDefaults, [ - ERC20BridgeSource.PancakeSwap, - ERC20BridgeSource.PancakeSwapV2, - ERC20BridgeSource.BakerySwap, - ERC20BridgeSource.SushiSwap, - ERC20BridgeSource.ApeSwap, - ERC20BridgeSource.CheeseSwap, - ]) - ) { - const source = slippedOrders[0].source; - const fillData = (slippedOrders[0] as OptimizedMarketBridgeOrder).fillData; - return { - calldataHexString: this._exchangeProxy - .sellToPancakeSwap( - fillData.tokenAddressPath.map((a, i) => { - if (i === 0 && isFromETH) { - return ETH_TOKEN_ADDRESS; - } - if (i === fillData.tokenAddressPath.length - 1 && isToETH) { - return ETH_TOKEN_ADDRESS; - } - return a; - }), - sellAmount, - minBuyAmount, - PANCAKE_SWAP_FORKS.indexOf(source), - ) - .getABIEncodedTransactionData(), - ethAmount: isFromETH ? sellAmount : ZERO_AMOUNT, - toAddress: this._exchangeProxy.address, - allowanceTarget: this._exchangeProxy.address, - gasOverhead: ZERO_AMOUNT, - }; - } - - if ( - [ChainId.Mainnet, ChainId.BSC].includes(this.chainId) && - isDirectSwapCompatible(quote, optsWithDefaults, [ERC20BridgeSource.LiquidityProvider]) - ) { - const fillData = (slippedOrders[0] as OptimizedMarketBridgeOrder).fillData; - const target = fillData.poolAddress; - return { - calldataHexString: this._exchangeProxy - .sellToLiquidityProvider( - isFromETH ? ETH_TOKEN_ADDRESS : sellToken, - isToETH ? ETH_TOKEN_ADDRESS : buyToken, - target, - NULL_ADDRESS, - sellAmount, - minBuyAmount, - NULL_BYTES, - ) - .getABIEncodedTransactionData(), - ethAmount: isFromETH ? sellAmount : ZERO_AMOUNT, - toAddress: this._exchangeProxy.address, - allowanceTarget: this._exchangeProxy.address, - gasOverhead: ZERO_AMOUNT, - }; - } - - if ( - this.chainId === ChainId.Mainnet && - isDirectSwapCompatible(quote, optsWithDefaults, [ERC20BridgeSource.Curve]) && - // Curve VIP cannot currently support WETH buy/sell as the functionality needs to WITHDRAW or DEPOSIT - // into WETH prior/post the trade. - // ETH buy/sell is supported - ![sellToken, buyToken].includes(NATIVE_FEE_TOKEN_BY_CHAIN_ID[ChainId.Mainnet]) - ) { - const fillData = slippedOrders[0].fillData as CurveFillData; - return { - calldataHexString: this._exchangeProxy - .sellToLiquidityProvider( - isFromETH ? ETH_TOKEN_ADDRESS : sellToken, - isToETH ? ETH_TOKEN_ADDRESS : buyToken, - CURVE_LIQUIDITY_PROVIDER_BY_CHAIN_ID[this.chainId], - NULL_ADDRESS, - sellAmount, - minBuyAmount, - encodeCurveLiquidityProviderData({ - curveAddress: fillData.pool.poolAddress, - exchangeFunctionSelector: fillData.pool.exchangeFunctionSelector, - fromCoinIdx: new BigNumber(fillData.fromTokenIdx), - toCoinIdx: new BigNumber(fillData.toTokenIdx), - }), - ) - .getABIEncodedTransactionData(), - ethAmount: isFromETH ? sellAmount : ZERO_AMOUNT, - toAddress: this._exchangeProxy.address, - allowanceTarget: this._exchangeProxy.address, - gasOverhead: ZERO_AMOUNT, - }; - } - - // RFQT VIP - if ( - [ChainId.Mainnet, ChainId.Polygon].includes(this.chainId) && - !isToETH && - !isFromETH && - quote.orders.every(o => o.type === FillQuoteTransformerOrderType.Rfq) && - !requiresTransformERC20(optsWithDefaults) - ) { - const rfqOrdersData = quote.orders.map(o => o.fillData as NativeRfqOrderFillData); - const fillAmountPerOrder = (() => { - // Don't think order taker amounts are clipped to actual sell amount - // (the last one might be too large) so figure them out manually. - let remaining = sellAmount; - const fillAmounts = []; - for (const o of quote.orders) { - const fillAmount = BigNumber.min(o.takerAmount, remaining); - fillAmounts.push(fillAmount); - remaining = remaining.minus(fillAmount); - } - return fillAmounts; - })(); - const callData = - quote.orders.length === 1 - ? this._exchangeProxy - .fillRfqOrder(rfqOrdersData[0].order, rfqOrdersData[0].signature, fillAmountPerOrder[0]) - .getABIEncodedTransactionData() - : this._exchangeProxy - .batchFillRfqOrders( - rfqOrdersData.map(d => d.order), - rfqOrdersData.map(d => d.signature), - fillAmountPerOrder, - true, - ) - .getABIEncodedTransactionData(); - return { - calldataHexString: callData, - ethAmount: ZERO_AMOUNT, - toAddress: this._exchangeProxy.address, - allowanceTarget: this._exchangeProxy.address, - gasOverhead: ZERO_AMOUNT, - }; - } - - if (this.chainId === ChainId.Mainnet && isMultiplexBatchFillCompatible(quote, optsWithDefaults)) { - return { - calldataHexString: this._encodeMultiplexBatchFillCalldata( - { ...quote, orders: slippedOrders }, - optsWithDefaults, - ), - ethAmount, - toAddress: this._exchangeProxy.address, - allowanceTarget: this._exchangeProxy.address, - gasOverhead: ZERO_AMOUNT, - }; - } - if (this.chainId === ChainId.Mainnet && isMultiplexMultiHopFillCompatible(quote, optsWithDefaults)) { - return { - calldataHexString: this._encodeMultiplexMultiHopFillCalldata( - { ...quote, orders: slippedOrders }, - optsWithDefaults, - ), - ethAmount, - toAddress: this._exchangeProxy.address, - allowanceTarget: this._exchangeProxy.address, - gasOverhead: ZERO_AMOUNT, - }; - } - - // Build up the transforms. - const transforms = []; - // Create a WETH wrapper if coming from ETH. - // Dont add the wethTransformer to CELO. There is no wrap/unwrap logic for CELO. - if (isFromETH && this.chainId !== ChainId.Celo) { - transforms.push({ - deploymentNonce: this.transformerNonces.wethTransformer, - data: encodeWethTransformerData({ - token: ETH_TOKEN_ADDRESS, - amount: shouldSellEntireBalance ? MAX_UINT256 : sellAmount, - }), - }); - } - - // If it's two hop we have an intermediate token this is needed to encode the individual FQT - // and we also want to ensure no dust amount is left in the flash wallet - const intermediateToken = quote.isTwoHop ? slippedOrders[0].makerToken : NULL_ADDRESS; - // This transformer will fill the quote. - if (quote.isTwoHop) { - const [firstHopOrder, secondHopOrder] = slippedOrders; - transforms.push({ - deploymentNonce: this.transformerNonces.fillQuoteTransformer, - data: encodeFillQuoteTransformerData({ - side: FillQuoteTransformerSide.Sell, - sellToken, - buyToken: intermediateToken, - ...getFQTTransformerDataFromOptimizedOrders([firstHopOrder]), - refundReceiver: refundReceiver || NULL_ADDRESS, - fillAmount: shouldSellEntireBalance ? MAX_UINT256 : firstHopOrder.takerAmount, - }), - }); - transforms.push({ - deploymentNonce: this.transformerNonces.fillQuoteTransformer, - data: encodeFillQuoteTransformerData({ - side: FillQuoteTransformerSide.Sell, - buyToken, - sellToken: intermediateToken, - ...getFQTTransformerDataFromOptimizedOrders([secondHopOrder]), - refundReceiver: refundReceiver || NULL_ADDRESS, - fillAmount: MAX_UINT256, - }), - }); - } else { - const fillAmount = isBuyQuote(quote) ? quote.makerTokenFillAmount : quote.takerTokenFillAmount; - transforms.push({ - deploymentNonce: this.transformerNonces.fillQuoteTransformer, - data: encodeFillQuoteTransformerData({ - side: isBuyQuote(quote) ? FillQuoteTransformerSide.Buy : FillQuoteTransformerSide.Sell, - sellToken, - buyToken, - ...getFQTTransformerDataFromOptimizedOrders(slippedOrders), - refundReceiver: refundReceiver || NULL_ADDRESS, - fillAmount: !isBuyQuote(quote) && shouldSellEntireBalance ? MAX_UINT256 : fillAmount, - }), - }); - } - // Create a WETH unwrapper if going to ETH. - // Dont add the wethTransformer on CELO. There is no wrap/unwrap logic for CELO. - if (isToETH && this.chainId !== ChainId.Celo) { - transforms.push({ - deploymentNonce: this.transformerNonces.wethTransformer, - data: encodeWethTransformerData({ - token: NATIVE_FEE_TOKEN_BY_CHAIN_ID[this.chainId], - amount: MAX_UINT256, - }), - }); - } - - const { feeType, buyTokenFeeAmount, sellTokenFeeAmount, recipient: feeRecipient } = affiliateFee; - let gasOverhead = ZERO_AMOUNT; - if (feeType === AffiliateFeeType.PositiveSlippageFee && feeRecipient !== NULL_ADDRESS) { - // bestCaseAmountWithSurplus is used to cover gas cost of sending positive slipapge fee to fee recipient - // this helps avoid sending dust amounts which are not worth the gas cost to transfer - let bestCaseAmountWithSurplus = quote.bestCaseQuoteInfo.makerAmount - .plus( - POSITIVE_SLIPPAGE_FEE_TRANSFORMER_GAS.multipliedBy(quote.gasPrice).multipliedBy( - quote.makerAmountPerEth, - ), - ) - .integerValue(); - // In the event makerAmountPerEth is unknown, we only allow for positive slippage which is greater than - // the best case amount - bestCaseAmountWithSurplus = BigNumber.max(bestCaseAmountWithSurplus, quote.bestCaseQuoteInfo.makerAmount); - transforms.push({ - deploymentNonce: this.transformerNonces.positiveSlippageFeeTransformer, - data: encodePositiveSlippageFeeTransformerData({ - token: isToETH ? ETH_TOKEN_ADDRESS : buyToken, - bestCaseAmount: BigNumber.max(bestCaseAmountWithSurplus, quote.bestCaseQuoteInfo.makerAmount), - recipient: feeRecipient, - }), - }); - // This may not be visible at eth_estimateGas time, so we explicitly add overhead - gasOverhead = POSITIVE_SLIPPAGE_FEE_TRANSFORMER_GAS; - } else if (feeType === AffiliateFeeType.PercentageFee && feeRecipient !== NULL_ADDRESS) { - // This transformer pays affiliate fees. - if (buyTokenFeeAmount.isGreaterThan(0)) { - transforms.push({ - deploymentNonce: this.transformerNonces.affiliateFeeTransformer, - data: encodeAffiliateFeeTransformerData({ - fees: [ - { - token: isToETH ? ETH_TOKEN_ADDRESS : buyToken, - amount: buyTokenFeeAmount, - recipient: feeRecipient, - }, - ], - }), - }); - // Adjust the minimum buy amount by the fee. - minBuyAmount = BigNumber.max(0, minBuyAmount.minus(buyTokenFeeAmount)); - } - if (sellTokenFeeAmount.isGreaterThan(0)) { - throw new Error('Affiliate fees denominated in sell token are not yet supported'); - } - } - - // Return any unspent sell tokens. - const payTakerTokens = [sellToken]; - // Return any unspent intermediate tokens for two-hop swaps. - if (quote.isTwoHop) { - payTakerTokens.push(intermediateToken); - } - // Return any unspent ETH. If ETH is the buy token, it will - // be returned in TransformERC20Feature rather than PayTakerTransformer. - if (!isToETH) { - payTakerTokens.push(ETH_TOKEN_ADDRESS); - } - // The final transformer will send all funds to the taker. - transforms.push({ - deploymentNonce: this.transformerNonces.payTakerTransformer, - data: encodePayTakerTransformerData({ - tokens: payTakerTokens, - amounts: [], - }), - }); - const TO_ETH_ADDRESS = this.chainId === ChainId.Celo ? this.contractAddresses.etherToken : ETH_TOKEN_ADDRESS; - const calldataHexString = this._exchangeProxy - .transformERC20( - isFromETH ? ETH_TOKEN_ADDRESS : sellToken, - isToETH ? TO_ETH_ADDRESS : buyToken, - shouldSellEntireBalance ? MAX_UINT256 : sellAmount, - minBuyAmount, - transforms, - ) - .getABIEncodedTransactionData(); - - return { - calldataHexString, - ethAmount, - toAddress: this._exchangeProxy.address, - allowanceTarget: this._exchangeProxy.address, - gasOverhead, - }; - } - - // tslint:disable-next-line:prefer-function-over-method - public async executeSwapQuoteOrThrowAsync( - _quote: SwapQuote, - _opts: Partial, - ): Promise { - throw new Error('Execution not supported for Exchange Proxy quotes'); - } - - private _encodeMultiplexBatchFillCalldata(quote: SwapQuote, opts: ExchangeProxyContractOpts): string { - const subcalls = []; - for_loop: for (const [i, order] of quote.orders.entries()) { - switch_statement: switch (order.source) { - case ERC20BridgeSource.Native: - if (order.type !== FillQuoteTransformerOrderType.Rfq) { - // Should never happen because we check `isMultiplexBatchFillCompatible` - // before calling this function. - throw new Error('Multiplex batch fill only supported for RFQ native orders'); - } - subcalls.push({ - id: MultiplexSubcall.Rfq, - sellAmount: order.takerAmount, - data: multiplexRfqEncoder.encode({ - order: order.fillData.order, - signature: order.fillData.signature, - }), - }); - break switch_statement; - case ERC20BridgeSource.UniswapV2: - case ERC20BridgeSource.SushiSwap: - subcalls.push({ - id: MultiplexSubcall.UniswapV2, - sellAmount: order.takerAmount, - data: multiplexUniswapEncoder.encode({ - tokens: (order.fillData as UniswapV2FillData).tokenAddressPath, - isSushi: order.source === ERC20BridgeSource.SushiSwap, - }), - }); - break switch_statement; - case ERC20BridgeSource.LiquidityProvider: - subcalls.push({ - id: MultiplexSubcall.LiquidityProvider, - sellAmount: order.takerAmount, - data: multiplexPlpEncoder.encode({ - provider: (order.fillData as LiquidityProviderFillData).poolAddress, - auxiliaryData: NULL_BYTES, - }), - }); - break switch_statement; - case ERC20BridgeSource.UniswapV3: - const fillData = (order as OptimizedMarketBridgeOrder).fillData; - subcalls.push({ - id: MultiplexSubcall.UniswapV3, - sellAmount: order.takerAmount, - data: fillData.uniswapPath, - }); - break switch_statement; - default: - const fqtData = encodeFillQuoteTransformerData({ - side: FillQuoteTransformerSide.Sell, - sellToken: quote.takerToken, - buyToken: quote.makerToken, - ...getFQTTransformerDataFromOptimizedOrders(quote.orders.slice(i)), - refundReceiver: NULL_ADDRESS, - fillAmount: MAX_UINT256, - }); - const transformations = [ - { deploymentNonce: this.transformerNonces.fillQuoteTransformer, data: fqtData }, - { - deploymentNonce: this.transformerNonces.payTakerTransformer, - data: encodePayTakerTransformerData({ - tokens: [quote.takerToken], - amounts: [], - }), - }, - ]; - subcalls.push({ - id: MultiplexSubcall.TransformERC20, - sellAmount: BigNumber.sum(...quote.orders.slice(i).map(o => o.takerAmount)), - data: multiplexTransformERC20Encoder.encode({ - transformations, - }), - }); - break for_loop; - } - } - if (opts.isFromETH) { - return this._exchangeProxy - .multiplexBatchSellEthForToken(quote.makerToken, subcalls, quote.worstCaseQuoteInfo.makerAmount) - .getABIEncodedTransactionData(); - } else if (opts.isToETH) { - return this._exchangeProxy - .multiplexBatchSellTokenForEth( - quote.takerToken, - subcalls, - quote.worstCaseQuoteInfo.totalTakerAmount, - quote.worstCaseQuoteInfo.makerAmount, - ) - .getABIEncodedTransactionData(); - } else { - return this._exchangeProxy - .multiplexBatchSellTokenForToken( - quote.takerToken, - quote.makerToken, - subcalls, - quote.worstCaseQuoteInfo.totalTakerAmount, - quote.worstCaseQuoteInfo.makerAmount, - ) - .getABIEncodedTransactionData(); - } - } - - private _encodeMultiplexMultiHopFillCalldata(quote: SwapQuote, opts: ExchangeProxyContractOpts): string { - const subcalls = []; - const [firstHopOrder, secondHopOrder] = quote.orders; - const intermediateToken = firstHopOrder.makerToken; - const tokens = [quote.takerToken, intermediateToken, quote.makerToken]; - - for (const order of [firstHopOrder, secondHopOrder]) { - switch (order.source) { - case ERC20BridgeSource.UniswapV2: - case ERC20BridgeSource.SushiSwap: - subcalls.push({ - id: MultiplexSubcall.UniswapV2, - data: multiplexUniswapEncoder.encode({ - tokens: (order.fillData as UniswapV2FillData).tokenAddressPath, - isSushi: order.source === ERC20BridgeSource.SushiSwap, - }), - }); - break; - case ERC20BridgeSource.LiquidityProvider: - subcalls.push({ - id: MultiplexSubcall.LiquidityProvider, - data: multiplexPlpEncoder.encode({ - provider: (order.fillData as LiquidityProviderFillData).poolAddress, - auxiliaryData: NULL_BYTES, - }), - }); - break; - case ERC20BridgeSource.UniswapV3: - subcalls.push({ - id: MultiplexSubcall.UniswapV3, - data: (order.fillData as FinalUniswapV3FillData).uniswapPath, - }); - break; - default: - // Should never happen because we check `isMultiplexMultiHopFillCompatible` - // before calling this function. - throw new Error(`Multiplex multi-hop unsupported source: ${order.source}`); - } - } - if (opts.isFromETH) { - return this._exchangeProxy - .multiplexMultiHopSellEthForToken(tokens, subcalls, quote.worstCaseQuoteInfo.makerAmount) - .getABIEncodedTransactionData(); - } else if (opts.isToETH) { - return this._exchangeProxy - .multiplexMultiHopSellTokenForEth( - tokens, - subcalls, - quote.worstCaseQuoteInfo.totalTakerAmount, - quote.worstCaseQuoteInfo.makerAmount, - ) - .getABIEncodedTransactionData(); - } else { - return this._exchangeProxy - .multiplexMultiHopSellTokenForToken( - tokens, - subcalls, - quote.worstCaseQuoteInfo.totalTakerAmount, - quote.worstCaseQuoteInfo.makerAmount, - ) - .getABIEncodedTransactionData(); - } - } -} - -function slipNonNativeOrders(quote: MarketSellSwapQuote | MarketBuySwapQuote): OptimizedMarketOrder[] { - const slippage = getMaxQuoteSlippageRate(quote); - if (slippage === 0) { - return quote.orders; - } - return quote.orders.map(o => { - if (o.source === ERC20BridgeSource.Native) { - return o; - } - return { - ...o, - ...(quote.type === MarketOperation.Sell - ? { - makerAmount: o.makerAmount.eq(MAX_UINT256) - ? MAX_UINT256 - : o.makerAmount.times(1 - slippage).integerValue(BigNumber.ROUND_DOWN), - } - : { - takerAmount: o.takerAmount.eq(MAX_UINT256) - ? MAX_UINT256 - : o.takerAmount.times(1 + slippage).integerValue(BigNumber.ROUND_UP), - }), - }; - }); -} - -function getMaxQuoteSlippageRate(quote: MarketBuySwapQuote | MarketSellSwapQuote): number { - return quote.worstCaseQuoteInfo.slippage; -} diff --git a/packages/asset-swapper/src/quote_consumers/multiplex_encoders.ts b/packages/asset-swapper/src/quote_consumers/multiplex_encoders.ts deleted file mode 100644 index 0f26153719..0000000000 --- a/packages/asset-swapper/src/quote_consumers/multiplex_encoders.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { RfqOrder, SIGNATURE_ABI } from '@0x/protocol-utils'; -import { AbiEncoder } from '@0x/utils'; - -export enum MultiplexSubcall { - Invalid, - Rfq, - Otc, - UniswapV2, - UniswapV3, - LiquidityProvider, - TransformERC20, - BatchSell, - MultiHopSell, -} -export const multiplexTransformERC20Encoder = AbiEncoder.create([ - { - name: 'transformations', - type: 'tuple[]', - components: [ - { name: 'deploymentNonce', type: 'uint32' }, - { name: 'data', type: 'bytes' }, - ], - }, -]); -export const multiplexRfqEncoder = AbiEncoder.create([ - { name: 'order', type: 'tuple', components: RfqOrder.STRUCT_ABI }, - { name: 'signature', type: 'tuple', components: SIGNATURE_ABI }, -]); -export const multiplexUniswapEncoder = AbiEncoder.create([ - { name: 'tokens', type: 'address[]' }, - { name: 'isSushi', type: 'bool' }, -]); -export const multiplexPlpEncoder = AbiEncoder.create([ - { name: 'provider', type: 'address' }, - { name: 'auxiliaryData', type: 'bytes' }, -]); diff --git a/packages/asset-swapper/src/quote_consumers/quote_consumer_utils.ts b/packages/asset-swapper/src/quote_consumers/quote_consumer_utils.ts deleted file mode 100644 index bc31f9022e..0000000000 --- a/packages/asset-swapper/src/quote_consumers/quote_consumer_utils.ts +++ /dev/null @@ -1,170 +0,0 @@ -import { FillQuoteTransformerData, FillQuoteTransformerOrderType } from '@0x/protocol-utils'; - -import { ExchangeProxyContractOpts, MarketBuySwapQuote, MarketOperation, SwapQuote } from '../types'; -import { - createBridgeDataForBridgeOrder, - getErc20BridgeSourceToBridgeSource, -} from '../utils/market_operation_utils/orders'; -import { - ERC20BridgeSource, - NativeLimitOrderFillData, - NativeRfqOrderFillData, - OptimizedMarketBridgeOrder, - OptimizedMarketOrder, - OptimizedMarketOrderBase, -} from '../utils/market_operation_utils/types'; - -const MULTIPLEX_BATCH_FILL_SOURCES = [ - ERC20BridgeSource.UniswapV2, - ERC20BridgeSource.SushiSwap, - ERC20BridgeSource.LiquidityProvider, - ERC20BridgeSource.Native, - ERC20BridgeSource.UniswapV3, -]; - -/** - * Returns true iff a quote can be filled via `MultiplexFeature.batchFill`. - */ -export function isMultiplexBatchFillCompatible(quote: SwapQuote, opts: ExchangeProxyContractOpts): boolean { - if (requiresTransformERC20(opts)) { - return false; - } - if (quote.isTwoHop) { - return false; - } - if (quote.orders.map(o => o.type).includes(FillQuoteTransformerOrderType.Limit)) { - return false; - } - // Use Multiplex if the non-fallback sources are a subset of - // {UniswapV2, Sushiswap, RFQ, PLP, UniswapV3} - const nonFallbackSources = Object.keys(quote.sourceBreakdown); - return nonFallbackSources.every(source => MULTIPLEX_BATCH_FILL_SOURCES.includes(source as ERC20BridgeSource)); -} - -const MULTIPLEX_MULTIHOP_FILL_SOURCES = [ - ERC20BridgeSource.UniswapV2, - ERC20BridgeSource.SushiSwap, - ERC20BridgeSource.LiquidityProvider, - ERC20BridgeSource.UniswapV3, -]; - -/** - * Returns true iff a quote can be filled via `MultiplexFeature.multiHopFill`. - */ -export function isMultiplexMultiHopFillCompatible(quote: SwapQuote, opts: ExchangeProxyContractOpts): boolean { - if (requiresTransformERC20(opts)) { - return false; - } - if (!quote.isTwoHop) { - return false; - } - const [firstHopOrder, secondHopOrder] = quote.orders; - return ( - MULTIPLEX_MULTIHOP_FILL_SOURCES.includes(firstHopOrder.source) && - MULTIPLEX_MULTIHOP_FILL_SOURCES.includes(secondHopOrder.source) - ); -} - -/** - * Returns true iff a quote can be filled via a VIP feature. - */ - -export function isDirectSwapCompatible( - quote: SwapQuote, - opts: ExchangeProxyContractOpts, - directSources: ERC20BridgeSource[], -): boolean { - if (requiresTransformERC20(opts)) { - return false; - } - // Must be a single order. - if (quote.orders.length !== 1) { - return false; - } - const order = quote.orders[0]; - if (!directSources.includes(order.source)) { - return false; - } - return true; -} - -/** - * Whether a quote is a market buy or not. - */ -export function isBuyQuote(quote: SwapQuote): quote is MarketBuySwapQuote { - return quote.type === MarketOperation.Buy; -} - -function isOptimizedBridgeOrder(x: OptimizedMarketOrder): x is OptimizedMarketBridgeOrder { - return x.type === FillQuoteTransformerOrderType.Bridge; -} - -function isOptimizedLimitOrder(x: OptimizedMarketOrder): x is OptimizedMarketOrderBase { - return x.type === FillQuoteTransformerOrderType.Limit; -} - -function isOptimizedRfqOrder(x: OptimizedMarketOrder): x is OptimizedMarketOrderBase { - return x.type === FillQuoteTransformerOrderType.Rfq; -} - -/** - * Converts the given `OptimizedMarketOrder`s into bridge, limit, and RFQ orders for - * FillQuoteTransformer. - */ -export function getFQTTransformerDataFromOptimizedOrders( - orders: OptimizedMarketOrder[], -): Pick { - const fqtData: Pick = { - bridgeOrders: [], - limitOrders: [], - rfqOrders: [], - fillSequence: [], - }; - - for (const order of orders) { - if (isOptimizedBridgeOrder(order)) { - fqtData.bridgeOrders.push({ - bridgeData: createBridgeDataForBridgeOrder(order), - makerTokenAmount: order.makerAmount, - takerTokenAmount: order.takerAmount, - source: getErc20BridgeSourceToBridgeSource(order.source), - }); - } else if (isOptimizedLimitOrder(order)) { - fqtData.limitOrders.push({ - order: order.fillData.order, - signature: order.fillData.signature, - maxTakerTokenFillAmount: order.takerAmount, - }); - } else if (isOptimizedRfqOrder(order)) { - fqtData.rfqOrders.push({ - order: order.fillData.order, - signature: order.fillData.signature, - maxTakerTokenFillAmount: order.takerAmount, - }); - } else { - // Should never happen - throw new Error('Unknown Order type'); - } - fqtData.fillSequence.push(order.type); - } - return fqtData; -} - -/** - * Returns true if swap quote must go through `tranformERC20`. - */ -export function requiresTransformERC20(opts: ExchangeProxyContractOpts): boolean { - // Is a mtx. - if (opts.isMetaTransaction) { - return true; - } - // Has an affiliate fee. - if (!opts.affiliateFee.buyTokenFeeAmount.eq(0) || !opts.affiliateFee.sellTokenFeeAmount.eq(0)) { - return true; - } - // VIP does not support selling the entire balance - if (opts.shouldSellEntireBalance) { - return true; - } - return false; -} diff --git a/packages/asset-swapper/src/quote_consumers/swap_quote_consumer.ts b/packages/asset-swapper/src/quote_consumers/swap_quote_consumer.ts deleted file mode 100644 index 4f91003407..0000000000 --- a/packages/asset-swapper/src/quote_consumers/swap_quote_consumer.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { ContractAddresses, getContractAddressesForChainOrThrow } from '@0x/contract-addresses'; -import * as _ from 'lodash'; - -import { constants } from '../constants'; -import { - CalldataInfo, - SwapQuote, - SwapQuoteConsumerBase, - SwapQuoteConsumerOpts, - SwapQuoteExecutionOpts, - SwapQuoteGetOutputOpts, -} from '../types'; -import { assert } from '../utils/assert'; - -import { ExchangeProxySwapQuoteConsumer } from './exchange_proxy_swap_quote_consumer'; - -export class SwapQuoteConsumer implements SwapQuoteConsumerBase { - public readonly chainId: number; - - private readonly _contractAddresses: ContractAddresses; - private readonly _exchangeProxyConsumer: ExchangeProxySwapQuoteConsumer; - - public static getSwapQuoteConsumer(options: Partial = {}): SwapQuoteConsumer { - return new SwapQuoteConsumer(options); - } - - constructor(options: Partial = {}) { - const { chainId } = _.merge({}, constants.DEFAULT_SWAP_QUOTER_OPTS, options); - assert.isNumber('chainId', chainId); - - this.chainId = chainId; - this._contractAddresses = options.contractAddresses || getContractAddressesForChainOrThrow(chainId); - this._exchangeProxyConsumer = new ExchangeProxySwapQuoteConsumer(this._contractAddresses, options); - } - - /** - * Given a SwapQuote, returns 'CalldataInfo' for a 0x extesion or exchange call. See type definition of CalldataInfo for more information. - * @param quote An object that conforms to SwapQuote. See type definition for more information. - * @param opts Options for getting SmartContractParams. See type definition for more information. - */ - public async getCalldataOrThrowAsync( - quote: SwapQuote, - opts: Partial = {}, - ): Promise { - const consumer = await this._getConsumerForSwapQuoteAsync(opts); - return consumer.getCalldataOrThrowAsync(quote, opts); - } - - /** - * Given a SwapQuote and desired rate (in takerAsset), attempt to execute the swap with 0x extension or exchange contract. - * @param quote An object that conforms to SwapQuote. See type definition for more information. - * @param opts Options for getting CalldataInfo. See type definition for more information. - */ - public async executeSwapQuoteOrThrowAsync( - quote: SwapQuote, - opts: Partial = {}, - ): Promise { - const consumer = await this._getConsumerForSwapQuoteAsync(opts); - return consumer.executeSwapQuoteOrThrowAsync(quote, opts); - } - - private async _getConsumerForSwapQuoteAsync(opts: Partial): Promise { - return this._exchangeProxyConsumer; - } -} diff --git a/packages/asset-swapper/src/swap_quoter.ts b/packages/asset-swapper/src/swap_quoter.ts deleted file mode 100644 index b2fad503f8..0000000000 --- a/packages/asset-swapper/src/swap_quoter.ts +++ /dev/null @@ -1,620 +0,0 @@ -import { ChainId, getContractAddressesForChainOrThrow } from '@0x/contract-addresses'; -import { FastABI } from '@0x/fast-abi'; -import { FillQuoteTransformerOrderType, LimitOrder } from '@0x/protocol-utils'; -import { BigNumber, providerUtils } from '@0x/utils'; -import Axios, { AxiosInstance } from 'axios'; -import { BlockParamLiteral, MethodAbi, SupportedProvider, ZeroExProvider } from 'ethereum-types'; -import { Agent as HttpAgent } from 'http'; -import { Agent as HttpsAgent } from 'https'; -import * as _ from 'lodash'; - -import { artifacts } from './artifacts'; -import { constants, INVALID_SIGNATURE, KEEP_ALIVE_TTL } from './constants'; -import { - AssetSwapperContractAddresses, - MarketBuySwapQuote, - MarketOperation, - OrderPrunerPermittedFeeTypes, - RfqRequestOpts, - SignedNativeOrder, - SwapQuote, - SwapQuoteInfo, - SwapQuoteOrdersBreakdown, - SwapQuoteRequestOpts, - SwapQuoterOpts, - SwapQuoterRfqOpts, -} from './types'; -import { assert } from './utils/assert'; -import { IRfqClient } from './utils/irfq_client'; -import { MarketOperationUtils } from './utils/market_operation_utils'; -import { BancorService } from './utils/market_operation_utils/bancor_service'; -import { SAMPLER_ADDRESS, SOURCE_FLAGS, ZERO_AMOUNT } from './utils/market_operation_utils/constants'; -import { DexOrderSampler } from './utils/market_operation_utils/sampler'; -import { SourceFilters } from './utils/market_operation_utils/source_filters'; -import { - ERC20BridgeSource, - FillData, - GasSchedule, - GetMarketOrdersOpts, - OptimizedMarketOrder, - OptimizerResultWithReport, -} from './utils/market_operation_utils/types'; -import { ProtocolFeeUtils } from './utils/protocol_fee_utils'; -import { QuoteRequestor } from './utils/quote_requestor'; -import { QuoteFillResult, simulateBestCaseFill, simulateWorstCaseFill } from './utils/quote_simulation'; -import { ERC20BridgeSamplerContract } from './wrappers'; - -export abstract class Orderbook { - public abstract getOrdersAsync( - makerToken: string, - takerToken: string, - pruneFn?: (o: SignedNativeOrder) => boolean, - ): Promise; - public abstract getBatchOrdersAsync( - makerTokens: string[], - takerToken: string, - pruneFn?: (o: SignedNativeOrder) => boolean, - ): Promise; - // tslint:disable-next-line:prefer-function-over-method - public async destroyAsync(): Promise { - return; - } -} - -// tslint:disable:max-classes-per-file -export class SwapQuoter { - public readonly provider: ZeroExProvider; - public readonly orderbook: Orderbook; - public readonly expiryBufferMs: number; - public readonly chainId: number; - public readonly permittedOrderFeeTypes: Set; - private readonly _contractAddresses: AssetSwapperContractAddresses; - private readonly _protocolFeeUtils: ProtocolFeeUtils; - private readonly _marketOperationUtils: MarketOperationUtils; - private readonly _rfqtOptions?: SwapQuoterRfqOpts; - private readonly _quoteRequestorHttpClient: AxiosInstance; - private readonly _integratorIdsSet: Set; - - /** - * Instantiates a new SwapQuoter instance - * @param supportedProvider The Provider instance you would like to use for interacting with the Ethereum network. - * @param orderbook An object that conforms to Orderbook, see type for definition. - * @param options Initialization options for the SwapQuoter. See type definition for details. - * - * @return An instance of SwapQuoter - */ - constructor(supportedProvider: SupportedProvider, orderbook: Orderbook, options: Partial = {}) { - const { - chainId, - expiryBufferMs, - permittedOrderFeeTypes, - samplerGasLimit, - rfqt, - tokenAdjacencyGraph, - liquidityProviderRegistry, - } = { ...constants.DEFAULT_SWAP_QUOTER_OPTS, ...options }; - const provider = providerUtils.standardizeOrThrow(supportedProvider); - assert.isValidOrderbook('orderbook', orderbook); - assert.isNumber('chainId', chainId); - assert.isNumber('expiryBufferMs', expiryBufferMs); - this.chainId = chainId; - this.provider = provider; - this.orderbook = orderbook; - this.expiryBufferMs = expiryBufferMs; - this.permittedOrderFeeTypes = permittedOrderFeeTypes; - - this._rfqtOptions = rfqt; - this._contractAddresses = options.contractAddresses || { - ...getContractAddressesForChainOrThrow(chainId), - }; - this._protocolFeeUtils = ProtocolFeeUtils.getInstance( - constants.PROTOCOL_FEE_UTILS_POLLING_INTERVAL_IN_MS, - options.zeroExGasApiUrl, - ); - // Allow the sampler bytecode to be overwritten using geths override functionality - const samplerBytecode = _.get(artifacts.ERC20BridgeSampler, 'compilerOutput.evm.deployedBytecode.object'); - // Allow address of the Sampler to be overridden, i.e in Ganache where overrides do not work - const samplerAddress = (options.samplerOverrides && options.samplerOverrides.to) || SAMPLER_ADDRESS; - const defaultCodeOverrides = samplerBytecode - ? { - [samplerAddress]: { code: samplerBytecode }, - } - : {}; - const samplerOverrides = _.assign( - { block: BlockParamLiteral.Latest, overrides: defaultCodeOverrides }, - options.samplerOverrides, - ); - const fastAbi = new FastABI(ERC20BridgeSamplerContract.ABI() as MethodAbi[], { BigNumber }); - const samplerContract = new ERC20BridgeSamplerContract( - samplerAddress, - this.provider, - { - gas: samplerGasLimit, - }, - {}, - undefined, - { - encodeInput: (fnName: string, values: any) => fastAbi.encodeInput(fnName, values), - decodeOutput: (fnName: string, data: string) => fastAbi.decodeOutput(fnName, data), - }, - ); - - this._marketOperationUtils = new MarketOperationUtils( - new DexOrderSampler( - this.chainId, - samplerContract, - samplerOverrides, - undefined, // pools caches for balancer - tokenAdjacencyGraph, - liquidityProviderRegistry, - this.chainId === ChainId.Mainnet // Enable Bancor only on Mainnet - ? async () => BancorService.createAsync(provider) - : async () => undefined, - ), - this._contractAddresses, - { - chainId, - exchangeAddress: this._contractAddresses.exchange, - }, - ); - - this._quoteRequestorHttpClient = Axios.create({ - httpAgent: new HttpAgent({ keepAlive: true, timeout: KEEP_ALIVE_TTL }), - httpsAgent: new HttpsAgent({ keepAlive: true, timeout: KEEP_ALIVE_TTL }), - ...(rfqt ? rfqt.axiosInstanceOpts : {}), - }); - - const integratorIds = this._rfqtOptions?.integratorsWhitelist.map(integrator => integrator.integratorId) || []; - this._integratorIdsSet = new Set(integratorIds); - } - - public async getBatchMarketBuySwapQuoteAsync( - makerTokens: string[], - targetTakerToken: string, - makerTokenBuyAmounts: BigNumber[], - options: Partial = {}, - ): Promise { - makerTokenBuyAmounts.map((a, i) => assert.isBigNumber(`makerAssetBuyAmounts[${i}]`, a)); - let gasPrice: BigNumber; - if (!!options.gasPrice) { - gasPrice = options.gasPrice; - assert.isBigNumber('gasPrice', gasPrice); - } else { - gasPrice = await this.getGasPriceEstimationOrThrowAsync(); - } - - const allOrders = await this.orderbook.getBatchOrdersAsync( - makerTokens, - targetTakerToken, - this._limitOrderPruningFn, - ); - - // Orders could be missing from the orderbook, so we create a dummy one as a placeholder - allOrders.forEach((orders: SignedNativeOrder[], i: number) => { - if (!orders || orders.length === 0) { - allOrders[i] = [createDummyOrder(makerTokens[i], targetTakerToken)]; - } - }); - - const opts = { ...constants.DEFAULT_SWAP_QUOTE_REQUEST_OPTS, ...options }; - const optimizerResults = await this._marketOperationUtils.getBatchMarketBuyOrdersAsync( - allOrders, - makerTokenBuyAmounts, - opts as GetMarketOrdersOpts, - ); - - const batchSwapQuotes = await Promise.all( - optimizerResults.map(async (result, i) => { - if (result) { - const { makerToken, takerToken } = allOrders[i][0].order; - return createSwapQuote( - result, - makerToken, - takerToken, - MarketOperation.Buy, - makerTokenBuyAmounts[i], - gasPrice, - opts.gasSchedule, - opts.bridgeSlippage, - ); - } else { - return undefined; - } - }), - ); - return batchSwapQuotes.filter(x => x !== undefined) as MarketBuySwapQuote[]; - } - - /** - * Returns the recommended gas price for a fast transaction - */ - public async getGasPriceEstimationOrThrowAsync(): Promise { - return this._protocolFeeUtils.getGasPriceEstimationOrThrowAsync(); - } - - /** - * Destroys any subscriptions or connections. - */ - public async destroyAsync(): Promise { - await this._protocolFeeUtils.destroyAsync(); - await this.orderbook.destroyAsync(); - } - - /** - * Utility function to get Ether token address - */ - public getEtherToken(): string { - return this._contractAddresses.etherToken; - } - - /** - * Get a `SwapQuote` containing all information relevant to fulfilling a swap between a desired ERC20 token address and ERC20 owned by a provided address. - * You can then pass the `SwapQuote` to a `SwapQuoteConsumer` to execute a buy, or process SwapQuote for on-chain consumption. - * @param makerToken The address of the maker asset - * @param takerToken The address of the taker asset - * @param assetFillAmount If a buy, the amount of maker asset to buy. If a sell, the amount of taker asset to sell. - * @param marketOperation Either a Buy or a Sell quote - * @param options Options for the request. See type definition for more information. - * - * @return An object that conforms to SwapQuote that satisfies the request. See type definition for more information. - */ - public async getSwapQuoteAsync( - makerToken: string, - takerToken: string, - assetFillAmount: BigNumber, - marketOperation: MarketOperation, - options: Partial, - rfqClient?: IRfqClient | undefined, - ): Promise { - assert.isETHAddressHex('makerToken', makerToken); - assert.isETHAddressHex('takerToken', takerToken); - assert.isBigNumber('assetFillAmount', assetFillAmount); - const opts = _.merge({}, constants.DEFAULT_SWAP_QUOTE_REQUEST_OPTS, options); - let gasPrice: BigNumber; - if (!!opts.gasPrice) { - gasPrice = opts.gasPrice; - assert.isBigNumber('gasPrice', gasPrice); - } else { - gasPrice = await this.getGasPriceEstimationOrThrowAsync(); - } - - const sourceFilters = new SourceFilters([], opts.excludedSources, opts.includedSources); - - opts.rfqt = this._validateRfqtOpts(sourceFilters, opts.rfqt); - const rfqtOptions = this._rfqtOptions; - - // Get SRA orders (limit orders) - const shouldSkipOpenOrderbook = - !sourceFilters.isAllowed(ERC20BridgeSource.Native) || - (opts.rfqt && opts.rfqt.nativeExclusivelyRFQ === true); - const nativeOrders = shouldSkipOpenOrderbook - ? await Promise.resolve([]) - : await this.orderbook.getOrdersAsync(makerToken, takerToken, this._limitOrderPruningFn); - - // if no native orders, pass in a dummy order for the sampler to have required metadata for sampling - if (nativeOrders.length === 0) { - nativeOrders.push(createDummyOrder(makerToken, takerToken)); - } - - // ** Prepare options for fetching market side liquidity ** - // Scale fees by gas price. - const cloneOpts = _.omit(opts, 'gasPrice') as GetMarketOrdersOpts; - const calcOpts: GetMarketOrdersOpts = { - ...cloneOpts, - gasPrice, - feeSchedule: _.mapValues(opts.gasSchedule, gasCost => (fillData: FillData) => { - const gas = gasCost ? gasCost(fillData) : 0; - const fee = gasPrice.times(gas); - return { gas, fee }; - }), - exchangeProxyOverhead: flags => gasPrice.times(opts.exchangeProxyOverhead(flags)), - }; - // pass the QuoteRequestor on if rfqt enabled - if (calcOpts.rfqt !== undefined) { - calcOpts.rfqt.quoteRequestor = new QuoteRequestor( - rfqtOptions?.makerAssetOfferings || {}, - {}, - this._quoteRequestorHttpClient, - rfqtOptions?.altRfqCreds, - rfqtOptions?.warningLogger, - rfqtOptions?.infoLogger, - this.expiryBufferMs, - rfqtOptions?.metricsProxy, - ); - calcOpts.rfqt.rfqClient = rfqClient; - } - - const result: OptimizerResultWithReport = await this._marketOperationUtils.getOptimizerResultAsync( - nativeOrders, - assetFillAmount, - marketOperation, - calcOpts, - ); - - const swapQuote = createSwapQuote( - result, - makerToken, - takerToken, - marketOperation, - assetFillAmount, - gasPrice, - opts.gasSchedule, - opts.bridgeSlippage, - ); - - // Use the raw gas, not scaled by gas price - const exchangeProxyOverhead = opts.exchangeProxyOverhead(result.sourceFlags).toNumber(); - swapQuote.bestCaseQuoteInfo.gas += exchangeProxyOverhead; - swapQuote.worstCaseQuoteInfo.gas += exchangeProxyOverhead; - - return swapQuote; - } - - private readonly _limitOrderPruningFn = (limitOrder: SignedNativeOrder) => { - const order = new LimitOrder(limitOrder.order); - const isOpenOrder = order.taker === constants.NULL_ADDRESS; - const willOrderExpire = order.willExpire(this.expiryBufferMs / constants.ONE_SECOND_MS); // tslint:disable-line:boolean-naming - const isFeeTypeAllowed = - this.permittedOrderFeeTypes.has(OrderPrunerPermittedFeeTypes.NoFees) && - order.takerTokenFeeAmount.eq(constants.ZERO_AMOUNT); - return isOpenOrder && !willOrderExpire && isFeeTypeAllowed; - }; // tslint:disable-line:semicolon - - private _isIntegratorIdWhitelisted(integratorId: string | undefined): boolean { - if (!integratorId) { - return false; - } - return this._integratorIdsSet.has(integratorId); - } - - private _isTxOriginBlacklisted(txOrigin: string | undefined): boolean { - if (!txOrigin) { - return false; - } - const blacklistedTxOrigins = this._rfqtOptions ? this._rfqtOptions.txOriginBlacklist : new Set(); - return blacklistedTxOrigins.has(txOrigin.toLowerCase()); - } - - private _validateRfqtOpts( - sourceFilters: SourceFilters, - rfqt: RfqRequestOpts | undefined, - ): RfqRequestOpts | undefined { - if (!rfqt) { - return rfqt; - } - // tslint:disable-next-line: boolean-naming - const { integrator, nativeExclusivelyRFQ, intentOnFilling, txOrigin } = rfqt; - // If RFQ-T is enabled and `nativeExclusivelyRFQ` is set, then `ERC20BridgeSource.Native` should - // never be excluded. - if (nativeExclusivelyRFQ === true && !sourceFilters.isAllowed(ERC20BridgeSource.Native)) { - throw new Error('Native liquidity cannot be excluded if "rfqt.nativeExclusivelyRFQ" is set'); - } - - // If an integrator ID was provided, but the ID is not whitelisted, raise a warning and disable RFQ - if (!this._isIntegratorIdWhitelisted(integrator.integratorId)) { - if (this._rfqtOptions && this._rfqtOptions.warningLogger) { - this._rfqtOptions.warningLogger( - { - ...integrator, - }, - 'Attempt at using an RFQ API key that is not whitelisted. Disabling RFQ for the request lifetime.', - ); - } - return undefined; - } - - // If the requested tx origin is blacklisted, raise a warning and disable RFQ - if (this._isTxOriginBlacklisted(txOrigin)) { - if (this._rfqtOptions && this._rfqtOptions.warningLogger) { - this._rfqtOptions.warningLogger( - { - txOrigin, - }, - 'Attempt at using a tx Origin that is blacklisted. Disabling RFQ for the request lifetime.', - ); - } - return undefined; - } - - // Otherwise check other RFQ options - if ( - intentOnFilling && // The requestor is asking for a firm quote - this._isIntegratorIdWhitelisted(integrator.integratorId) && // A valid API key was provided - sourceFilters.isAllowed(ERC20BridgeSource.Native) // Native liquidity is not excluded - ) { - if (!txOrigin || txOrigin === constants.NULL_ADDRESS) { - throw new Error('RFQ-T firm quote requests must specify a tx origin'); - } - } - - return rfqt; - } -} -// tslint:disable-next-line: max-file-line-count - -// begin formatting and report generation functions -function createSwapQuote( - optimizerResult: OptimizerResultWithReport, - makerToken: string, - takerToken: string, - operation: MarketOperation, - assetFillAmount: BigNumber, - gasPrice: BigNumber, - gasSchedule: GasSchedule, - slippage: number, -): SwapQuote { - const { - optimizedOrders, - quoteReport, - extendedQuoteReportSources, - sourceFlags, - takerAmountPerEth, - makerAmountPerEth, - priceComparisonsReport, - } = optimizerResult; - const isTwoHop = sourceFlags === SOURCE_FLAGS[ERC20BridgeSource.MultiHop]; - - // Calculate quote info - const { bestCaseQuoteInfo, worstCaseQuoteInfo, sourceBreakdown } = isTwoHop - ? calculateTwoHopQuoteInfo(optimizedOrders, operation, gasSchedule, slippage) - : calculateQuoteInfo(optimizedOrders, operation, assetFillAmount, gasPrice, gasSchedule, slippage); - - // Put together the swap quote - const { makerTokenDecimals, takerTokenDecimals, blockNumber } = optimizerResult.marketSideLiquidity; - const swapQuote = { - makerToken, - takerToken, - gasPrice, - orders: optimizedOrders, - bestCaseQuoteInfo, - worstCaseQuoteInfo, - sourceBreakdown, - makerTokenDecimals, - takerTokenDecimals, - takerAmountPerEth, - makerAmountPerEth, - quoteReport, - extendedQuoteReportSources, - isTwoHop, - priceComparisonsReport, - blockNumber, - }; - - if (operation === MarketOperation.Buy) { - return { - ...swapQuote, - type: MarketOperation.Buy, - makerTokenFillAmount: assetFillAmount, - }; - } else { - return { - ...swapQuote, - type: MarketOperation.Sell, - takerTokenFillAmount: assetFillAmount, - }; - } -} - -function calculateQuoteInfo( - optimizedOrders: OptimizedMarketOrder[], - operation: MarketOperation, - assetFillAmount: BigNumber, - gasPrice: BigNumber, - gasSchedule: GasSchedule, - slippage: number, -): { bestCaseQuoteInfo: SwapQuoteInfo; worstCaseQuoteInfo: SwapQuoteInfo; sourceBreakdown: SwapQuoteOrdersBreakdown } { - const bestCaseFillResult = simulateBestCaseFill({ - gasPrice, - orders: optimizedOrders, - side: operation, - fillAmount: assetFillAmount, - opts: { gasSchedule }, - }); - - const worstCaseFillResult = simulateWorstCaseFill({ - gasPrice, - orders: optimizedOrders, - side: operation, - fillAmount: assetFillAmount, - opts: { gasSchedule, slippage }, - }); - - return { - bestCaseQuoteInfo: fillResultsToQuoteInfo(bestCaseFillResult, 0), - worstCaseQuoteInfo: fillResultsToQuoteInfo(worstCaseFillResult, slippage), - sourceBreakdown: getSwapQuoteOrdersBreakdown(bestCaseFillResult.fillAmountBySource), - }; -} - -function calculateTwoHopQuoteInfo( - optimizedOrders: OptimizedMarketOrder[], - operation: MarketOperation, - gasSchedule: GasSchedule, - slippage: number, -): { bestCaseQuoteInfo: SwapQuoteInfo; worstCaseQuoteInfo: SwapQuoteInfo; sourceBreakdown: SwapQuoteOrdersBreakdown } { - const [firstHopOrder, secondHopOrder] = optimizedOrders; - const gas = new BigNumber( - gasSchedule[ERC20BridgeSource.MultiHop]!({ - firstHopSource: _.pick(firstHopOrder, 'source', 'fillData'), - secondHopSource: _.pick(secondHopOrder, 'source', 'fillData'), - }), - ).toNumber(); - const isSell = operation === MarketOperation.Sell; - - return { - bestCaseQuoteInfo: { - makerAmount: isSell ? secondHopOrder.fill.output : secondHopOrder.fill.input, - takerAmount: isSell ? firstHopOrder.fill.input : firstHopOrder.fill.output, - totalTakerAmount: isSell ? firstHopOrder.fill.input : firstHopOrder.fill.output, - feeTakerTokenAmount: constants.ZERO_AMOUNT, - protocolFeeInWeiAmount: constants.ZERO_AMOUNT, - gas, - slippage: 0, - }, - // TODO jacob consolidate this with quote simulation worstCase - worstCaseQuoteInfo: { - makerAmount: isSell - ? secondHopOrder.makerAmount.times(1 - slippage).integerValue() - : secondHopOrder.makerAmount, - takerAmount: isSell - ? firstHopOrder.takerAmount - : firstHopOrder.takerAmount.times(1 + slippage).integerValue(BigNumber.ROUND_UP), - totalTakerAmount: isSell - ? firstHopOrder.takerAmount - : firstHopOrder.takerAmount.times(1 + slippage).integerValue(BigNumber.ROUND_UP), - feeTakerTokenAmount: constants.ZERO_AMOUNT, - protocolFeeInWeiAmount: constants.ZERO_AMOUNT, - gas, - slippage, - }, - sourceBreakdown: { - [ERC20BridgeSource.MultiHop]: { - proportion: new BigNumber(1), - intermediateToken: secondHopOrder.takerToken, - hops: [firstHopOrder.source, secondHopOrder.source], - }, - }, - }; -} - -function getSwapQuoteOrdersBreakdown(fillAmountBySource: { [source: string]: BigNumber }): SwapQuoteOrdersBreakdown { - const totalFillAmount = BigNumber.sum(...Object.values(fillAmountBySource)); - const breakdown: SwapQuoteOrdersBreakdown = {}; - Object.entries(fillAmountBySource).forEach(([s, fillAmount]) => { - const source = s as keyof SwapQuoteOrdersBreakdown; - if (source === ERC20BridgeSource.MultiHop) { - // TODO jacob has a different breakdown - } else { - breakdown[source] = fillAmount.div(totalFillAmount); - } - }); - return breakdown; -} - -function fillResultsToQuoteInfo(fr: QuoteFillResult, slippage: number): SwapQuoteInfo { - return { - makerAmount: fr.totalMakerAssetAmount, - takerAmount: fr.takerAssetAmount, - totalTakerAmount: fr.totalTakerAssetAmount, - feeTakerTokenAmount: fr.takerFeeTakerAssetAmount, - protocolFeeInWeiAmount: fr.protocolFeeAmount, - gas: fr.gas, - slippage, - }; -} - -function createDummyOrder(makerToken: string, takerToken: string): SignedNativeOrder { - return { - type: FillQuoteTransformerOrderType.Limit, - order: { - ...new LimitOrder({ - makerToken, - takerToken, - makerAmount: ZERO_AMOUNT, - takerAmount: ZERO_AMOUNT, - takerTokenFeeAmount: ZERO_AMOUNT, - }), - }, - signature: INVALID_SIGNATURE, - }; -} diff --git a/packages/asset-swapper/src/types.ts b/packages/asset-swapper/src/types.ts deleted file mode 100644 index 52bc48d761..0000000000 --- a/packages/asset-swapper/src/types.ts +++ /dev/null @@ -1,467 +0,0 @@ -import { ChainId } from '@0x/contract-addresses'; -import { BlockParam, ContractAddresses, GethCallOverrides } from '@0x/contract-wrappers'; -import { - FillQuoteTransformerOrderType, - LimitOrderFields, - RfqOrder, - RfqOrderFields, - Signature, -} from '@0x/protocol-utils'; -import { TakerRequestQueryParamsUnnested, V4SignedRfqOrder } from '@0x/quote-server'; -import { Fee } from '@0x/quote-server/lib/src/types'; -import { BigNumber } from '@0x/utils'; -import { AxiosRequestConfig } from 'axios'; - -import { - ERC20BridgeSource, - GetMarketOrdersOpts, - LiquidityProviderRegistry, - OptimizedMarketOrder, -} from './utils/market_operation_utils/types'; -import { ExtendedQuoteReportSources, PriceComparisonsReport, QuoteReport } from './utils/quote_report_generator'; -import { MetricsProxy } from './utils/quote_requestor'; -import { TokenAdjacencyGraph } from './utils/token_adjacency_graph'; -export { SamplerMetrics } from './utils/market_operation_utils/types'; - -export type Address = string; - -/** - * expiryBufferMs: The number of seconds to add when calculating whether an order is expired or not. Defaults to 300s (5m). - * permittedOrderFeeTypes: A set of all the takerFee types that OrderPruner will filter for - */ -export interface OrderPrunerOpts { - expiryBufferMs: number; - permittedOrderFeeTypes: Set; -} - -export interface SignedOrder { - order: T; - type: FillQuoteTransformerOrderType.Limit | FillQuoteTransformerOrderType.Rfq; - signature: Signature; -} - -export type SignedNativeOrder = SignedOrder | SignedOrder; -export type NativeOrderWithFillableAmounts = SignedNativeOrder & NativeOrderFillableAmountFields; - -/** - * fillableMakerAmount: Amount of makerAsset that is fillable - * fillableTakerAmount: Amount of takerAsset that is fillable - * fillableTakerFeeAmount: Amount of takerFee paid to fill fillableTakerAmount - */ -export interface NativeOrderFillableAmountFields { - fillableMakerAmount: BigNumber; - fillableTakerAmount: BigNumber; - fillableTakerFeeAmount: BigNumber; -} - -/** - * Represents the metadata to call a smart contract with calldata. - * calldataHexString: The hexstring of the calldata. - * toAddress: The contract address to call. - * ethAmount: The eth amount in wei to send with the smart contract call. - * allowanceTarget: The address the taker should grant an allowance to. - * gasOverhead: The gas overhead needed to be added to the gas limit to allow for optional - * operations which may not visible at eth_estimateGas time - */ -export interface CalldataInfo { - calldataHexString: string; - toAddress: string; - ethAmount: BigNumber; - allowanceTarget: string; - gasOverhead: BigNumber; -} - -/** - * Interface that varying SwapQuoteConsumers adhere to (exchange consumer, router consumer, forwarder consumer, coordinator consumer) - * getCalldataOrThrow: Get CalldataInfo to swap for tokens with provided SwapQuote. Throws if invalid SwapQuote is provided. - * executeSwapQuoteOrThrowAsync: Executes a web3 transaction to swap for tokens with provided SwapQuote. Throws if invalid SwapQuote is provided. - */ -export interface SwapQuoteConsumerBase { - getCalldataOrThrowAsync(quote: SwapQuote, opts: Partial): Promise; - executeSwapQuoteOrThrowAsync(quote: SwapQuote, opts: Partial): Promise; -} - -/** - * chainId: The chainId that the desired orders should be for. - */ -export interface SwapQuoteConsumerOpts { - chainId: number; - contractAddresses?: ContractAddresses; -} - -/** - * Represents the options provided to a generic SwapQuoteConsumer - */ -export interface SwapQuoteGetOutputOpts { - extensionContractOpts?: ExchangeProxyContractOpts | any; -} - -/** - * ethAmount: The amount of eth sent with the execution of a swap. - * takerAddress: The address to perform the buy. Defaults to the first available address from the provider. - * gasLimit: The amount of gas to send with a transaction (in Gwei). Defaults to an eth_estimateGas rpc call. - */ -export interface SwapQuoteExecutionOpts extends SwapQuoteGetOutputOpts { - ethAmount?: BigNumber; - takerAddress?: string; - gasLimit?: number; -} - -export enum AffiliateFeeType { - None, - PercentageFee, - PositiveSlippageFee, -} - -export interface AffiliateFeeAmount { - feeType: AffiliateFeeType; - recipient: string; - buyTokenFeeAmount: BigNumber; - sellTokenFeeAmount: BigNumber; -} - -/** - * Automatically resolved protocol fee refund receiver addresses. - */ -export enum ExchangeProxyRefundReceiver { - // Refund to the taker address. - Taker = '0x0000000000000000000000000000000000000001', - // Refund to the sender address. - Sender = '0x0000000000000000000000000000000000000002', -} - -/** - * @param isFromETH Whether the input token is ETH. - * @param isToETH Whether the output token is ETH. - * @param affiliateFee Fee denominated in taker or maker asset to send to specified recipient. - * @param refundReceiver The receiver of unspent protocol fees. - * May be a valid address or one of: - * `address(0)`: Stay in flash wallet. - * `address(1)`: Send to the taker. - * `address(2)`: Send to the sender (caller of `transformERC20()`). - * @param shouldSellEntireBalance Whether the entire balance of the caller should be sold. Used - * for contracts where the balance at transaction time is different to the quote amount. - * This foregos certain VIP routes which do not support this feature. - */ -export interface ExchangeProxyContractOpts { - isFromETH: boolean; - isToETH: boolean; - affiliateFee: AffiliateFeeAmount; - refundReceiver: string | ExchangeProxyRefundReceiver; - isMetaTransaction: boolean; - shouldSellEntireBalance: boolean; -} - -export interface GetExtensionContractTypeOpts { - takerAddress?: string; - ethAmount?: BigNumber; -} - -/** - * takerToken: Address of the taker asset. - * makerToken: Address of the maker asset. - * gasPrice: gas price used to determine protocolFee amount, default to ethGasStation fast amount. - * orders: An array of objects conforming to OptimizedMarketOrder. These orders can be used to cover the requested assetBuyAmount plus slippage. - * bestCaseQuoteInfo: Info about the best case price for the asset. - * worstCaseQuoteInfo: Info about the worst case price for the asset. - */ -export interface SwapQuoteBase { - takerToken: string; - makerToken: string; - gasPrice: BigNumber; - orders: OptimizedMarketOrder[]; - bestCaseQuoteInfo: SwapQuoteInfo; - worstCaseQuoteInfo: SwapQuoteInfo; - sourceBreakdown: SwapQuoteOrdersBreakdown; - quoteReport?: QuoteReport; - extendedQuoteReportSources?: ExtendedQuoteReportSources; - priceComparisonsReport?: PriceComparisonsReport; - isTwoHop: boolean; - makerTokenDecimals: number; - takerTokenDecimals: number; - takerAmountPerEth: BigNumber; - makerAmountPerEth: BigNumber; - blockNumber: number; -} - -/** - * takerAssetFillAmount: The amount of takerAsset sold for makerAsset. - * type: Specified MarketOperation the SwapQuote is provided for - */ -export interface MarketSellSwapQuote extends SwapQuoteBase { - takerTokenFillAmount: BigNumber; - type: MarketOperation.Sell; -} - -/** - * makerAssetFillAmount: The amount of makerAsset bought with takerAsset. - * type: Specified MarketOperation the SwapQuote is provided for - */ -export interface MarketBuySwapQuote extends SwapQuoteBase { - makerTokenFillAmount: BigNumber; - type: MarketOperation.Buy; -} - -export type SwapQuote = MarketBuySwapQuote | MarketSellSwapQuote; - -/** - * feeTakerTokenAmount: The amount of takerAsset reserved for paying takerFees when swapping for desired assets. - * takerTokenAmount: The amount of takerAsset swapped for desired makerAsset. - * totalTakerTokenAmount: The total amount of takerAsset required to complete the swap (filling orders, and paying takerFees). - * makerTokenAmount: The amount of makerAsset that will be acquired through the swap. - * protocolFeeInWeiAmount: The amount of ETH to pay (in WEI) as protocol fee to perform the swap for desired asset. - * gas: Amount of estimated gas needed to fill the quote. - * slippage: Amount of slippage to allow for. - */ -export interface SwapQuoteInfo { - feeTakerTokenAmount: BigNumber; - takerAmount: BigNumber; - totalTakerAmount: BigNumber; - makerAmount: BigNumber; - protocolFeeInWeiAmount: BigNumber; - gas: number; - slippage: number; -} - -/** - * percentage breakdown of each liquidity source used in quote - */ -export type SwapQuoteOrdersBreakdown = Partial< - { [key in Exclude]: BigNumber } & { - [ERC20BridgeSource.MultiHop]: { - proportion: BigNumber; - intermediateToken: string; - hops: ERC20BridgeSource[]; - }; - } ->; - -/** - * nativeExclusivelyRFQ: if set to `true`, Swap quote will exclude Open Orderbook liquidity. - * If set to `true` and `ERC20BridgeSource.Native` is part of the `excludedSources` - * array in `SwapQuoteRequestOpts`, an Error will be raised. - */ - -export interface RfqmRequestOptions extends RfqRequestOpts { - isLastLook: true; - fee: Fee; -} - -export interface RfqRequestOpts { - takerAddress: string; - txOrigin: string; - integrator: Integrator; - intentOnFilling: boolean; - isIndicative?: boolean; - makerEndpointMaxResponseTimeMs?: number; - nativeExclusivelyRFQ?: boolean; - altRfqAssetOfferings?: AltRfqMakerAssetOfferings; - isLastLook?: boolean; - fee?: Fee; -} - -/** - * gasPrice: gas price to determine protocolFee amount, default to ethGasStation fast amount - */ -export interface SwapQuoteRequestOpts extends Omit { - gasPrice?: BigNumber; - rfqt?: RfqRequestOpts; -} - -/** - * A mapping from RFQ-T/M quote provider URLs to the trading pairs they support. - * The value type represents an array of supported asset pairs, with each array element encoded as a 2-element array of token addresses. - */ -export interface RfqMakerAssetOfferings { - [endpoint: string]: Array<[string, string]>; -} -export interface AltOffering { - id: string; - baseAsset: string; - quoteAsset: string; - baseAssetDecimals: number; - quoteAssetDecimals: number; -} -export interface AltRfqMakerAssetOfferings { - [endpoint: string]: AltOffering[]; -} -export enum RfqPairType { - Standard = 'standard', - Alt = 'alt', -} -export interface TypedMakerUrl { - url: string; - pairType: RfqPairType; -} - -export type LogFunction = (obj: object, msg?: string, ...args: any[]) => void; - -export interface RfqFirmQuoteValidator { - getRfqtTakerFillableAmountsAsync(quotes: RfqOrder[]): Promise; -} - -export interface Integrator { - integratorId: string; - label: string; - whitelistIntegratorUrls?: string[]; -} - -export interface SwapQuoterRfqOpts { - integratorsWhitelist: Integrator[]; - makerAssetOfferings: RfqMakerAssetOfferings; - txOriginBlacklist: Set; - altRfqCreds?: { - altRfqApiKey: string; - altRfqProfile: string; - }; - warningLogger?: LogFunction; - infoLogger?: LogFunction; - metricsProxy?: MetricsProxy; - axiosInstanceOpts?: AxiosRequestConfig; -} - -export type AssetSwapperContractAddresses = ContractAddresses; - -/** - * chainId: The ethereum chain id. Defaults to 1 (mainnet). - * orderRefreshIntervalMs: The interval in ms that getBuyQuoteAsync should trigger an refresh of orders and order states. Defaults to 10000ms (10s). - * expiryBufferMs: The number of seconds to add when calculating whether an order is expired or not. Defaults to 300s (5m). - * contractAddresses: Optionally override the contract addresses used for the chain - * samplerGasLimit: The gas limit used when querying the sampler contract. Defaults to 36e6 - */ -export interface SwapQuoterOpts extends OrderPrunerOpts { - chainId: ChainId; - orderRefreshIntervalMs: number; - expiryBufferMs: number; - ethereumRpcUrl?: string; - contractAddresses?: AssetSwapperContractAddresses; - samplerGasLimit?: number; - multiBridgeAddress?: string; - zeroExGasApiUrl?: string; - rfqt?: SwapQuoterRfqOpts; - samplerOverrides?: SamplerOverrides; - tokenAdjacencyGraph?: TokenAdjacencyGraph; - liquidityProviderRegistry?: LiquidityProviderRegistry; -} - -/** - * Possible error messages thrown by an SwapQuoterConsumer instance or associated static methods. - */ -export enum SwapQuoteConsumerError { - InvalidMarketSellOrMarketBuySwapQuote = 'INVALID_MARKET_BUY_SELL_SWAP_QUOTE', - InvalidForwarderSwapQuote = 'INVALID_FORWARDER_SWAP_QUOTE_PROVIDED', - NoAddressAvailable = 'NO_ADDRESS_AVAILABLE', - SignatureRequestDenied = 'SIGNATURE_REQUEST_DENIED', - TransactionValueTooLow = 'TRANSACTION_VALUE_TOO_LOW', -} - -/** - * Possible error messages thrown by an SwapQuoter instance or associated static methods. - */ -export enum SwapQuoterError { - NoEtherTokenContractFound = 'NO_ETHER_TOKEN_CONTRACT_FOUND', - StandardRelayerApiError = 'STANDARD_RELAYER_API_ERROR', - InsufficientAssetLiquidity = 'INSUFFICIENT_ASSET_LIQUIDITY', - AssetUnavailable = 'ASSET_UNAVAILABLE', - NoGasPriceProvidedOrEstimated = 'NO_GAS_PRICE_PROVIDED_OR_ESTIMATED', - AssetDataUnsupported = 'ASSET_DATA_UNSUPPORTED', -} - -/** - * Represents two main market operations supported by asset-swapper. - */ -export enum MarketOperation { - Sell = 'Sell', - Buy = 'Buy', -} - -/** - * Represents varying order takerFee types that can be pruned for by OrderPruner. - */ -export enum OrderPrunerPermittedFeeTypes { - NoFees = 'NO_FEES', - TakerDenominatedTakerFee = 'TAKER_DENOMINATED_TAKER_FEE', -} - -/** - * Represents a mocked RFQ-T/M maker responses. - */ -export interface MockedRfqQuoteResponse { - endpoint: string; - requestApiKey: string; - requestParams: TakerRequestQueryParamsUnnested; - responseData: any; - responseCode: number; - callback?: (config: any) => Promise; -} - -/** - * Represents a mocked RFQ-T/M alternative maker responses. - */ -export interface AltMockedRfqQuoteResponse { - endpoint: string; - mmApiKey: string; - requestData: AltQuoteRequestData; - responseData: any; - responseCode: number; -} - -export interface SamplerOverrides { - overrides: GethCallOverrides; - block: BlockParam; - to?: string; -} - -export interface SamplerCallResult { - success: boolean; - data: string; -} - -export type Omit = Pick>; - -export enum AltQuoteModel { - Firm = 'firm', - Indicative = 'indicative', -} - -export enum AltQuoteSide { - Buy = 'buy', - Sell = 'sell', -} - -export interface AltQuoteRequestData { - market: string; - model: AltQuoteModel; - profile: string; - side: AltQuoteSide; - value?: string; - amount?: string; - meta: { - txOrigin: string; - taker: string; - client: string; - existingOrder?: { - price: string; - value?: string; - amount?: string; - }; - }; -} - -export interface AltBaseRfqResponse extends AltQuoteRequestData { - id: string; - price?: string; -} - -export interface AltIndicativeQuoteResponse extends AltBaseRfqResponse { - model: AltQuoteModel.Indicative; - status: 'live' | 'rejected'; -} - -export interface AltFirmQuoteResponse extends AltBaseRfqResponse { - model: AltQuoteModel.Firm; - data: { - '0xv4order': V4SignedRfqOrder; - }; - status: 'active' | 'rejected'; -} diff --git a/packages/asset-swapper/src/utils/affiliate_fee_utils.ts b/packages/asset-swapper/src/utils/affiliate_fee_utils.ts deleted file mode 100644 index 2f4bc93123..0000000000 --- a/packages/asset-swapper/src/utils/affiliate_fee_utils.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { BigNumber } from '@0x/utils'; - -import { SwapQuoteInfo } from '../types'; - -import { assert } from './assert'; - -export const affiliateFeeUtils = { - /** - * Get the amount of eth to send for a forwarder contract call (includes takerAssetAmount, protocol fees, and specified affiliate fee amount) - * @param swapQuoteInfo SwapQuoteInfo to generate total eth amount from - * @param feePercentage Percentage of additive fees to apply to totalTakerAssetAmount + protocol fee. - */ - getTotalEthAmountWithAffiliateFee(swapQuoteInfo: SwapQuoteInfo, feePercentage: number): BigNumber { - const ethAmount = swapQuoteInfo.protocolFeeInWeiAmount.plus(swapQuoteInfo.totalTakerAmount); - const ethAmountWithFees = ethAmount.plus(affiliateFeeUtils.getFeeAmount(swapQuoteInfo, feePercentage)); - return ethAmountWithFees; - }, - /** - * Get the affiliate fee owed to the forwarder fee recipient. - * @param swapQuoteInfo SwapQuoteInfo to generate total eth amount from - * @param feePercentage Percentage of additive fees to apply to totalTakerAssetAmount + protocol fee. - */ - getFeeAmount(swapQuoteInfo: SwapQuoteInfo, feePercentage: number): BigNumber { - assert.assert(feePercentage >= 0, 'feePercentage must be >= 0'); - const ethAmount = swapQuoteInfo.protocolFeeInWeiAmount.plus(swapQuoteInfo.totalTakerAmount); - // HACK(dekz): This is actually in WEI amount not ETH - return ethAmount.times(feePercentage).integerValue(BigNumber.ROUND_UP); - }, -}; diff --git a/packages/asset-swapper/src/utils/alt_mm_implementation_utils.ts b/packages/asset-swapper/src/utils/alt_mm_implementation_utils.ts deleted file mode 100644 index 75c0f9e2e7..0000000000 --- a/packages/asset-swapper/src/utils/alt_mm_implementation_utils.ts +++ /dev/null @@ -1,279 +0,0 @@ -import { Web3Wrapper } from '@0x/dev-utils'; -import { TakerRequestQueryParamsUnnested, V4RFQFirmQuote, V4RFQIndicativeQuote } from '@0x/quote-server'; -import { BigNumber } from '@0x/utils'; -import { AxiosInstance, CancelToken } from 'axios'; - -import { constants } from '../constants'; -import { - AltFirmQuoteResponse, - AltIndicativeQuoteResponse, - AltOffering, - AltQuoteModel, - AltQuoteRequestData, - AltQuoteSide, - AltRfqMakerAssetOfferings, - LogFunction, -} from '../types'; - -const SUCCESS_CODE = 201; - -/** - * Returns the AltOffering if it exists for a given pair - */ -export function getAltMarketInfo( - offerings: AltOffering[], - buyTokenAddress: string, - sellTokenAddress: string, -): AltOffering | undefined { - for (const offering of offerings) { - if ( - (buyTokenAddress.toLowerCase() === offering.baseAsset.toLowerCase() && - sellTokenAddress.toLowerCase() === offering.quoteAsset.toLowerCase()) || - (sellTokenAddress.toLowerCase() === offering.baseAsset.toLowerCase() && - buyTokenAddress.toLowerCase() === offering.quoteAsset.toLowerCase()) - ) { - return offering; - } - } - return undefined; -} - -function parseFirmQuoteResponseFromAltMM(altFirmQuoteReponse: AltFirmQuoteResponse): V4RFQFirmQuote { - return { - signedOrder: altFirmQuoteReponse.data['0xv4order'], - }; -} - -function parseIndicativeQuoteResponseFromAltMM( - altIndicativeQuoteResponse: AltIndicativeQuoteResponse, - altPair: AltOffering, - makerToken: string, - takerToken: string, -): V4RFQIndicativeQuote { - let makerAmount: BigNumber; - let takerAmount: BigNumber; - let quoteAmount: BigNumber; - let baseAmount: BigNumber; - - if (!altIndicativeQuoteResponse.price) { - throw new Error('Price not returned by alt MM'); - } - if (altIndicativeQuoteResponse.amount) { - // if amount is specified, amount is the base token amount - baseAmount = Web3Wrapper.toBaseUnitAmount( - new BigNumber(altIndicativeQuoteResponse.amount), - altPair.baseAssetDecimals, - ); - // if amount is specified, use the price (quote/base) to get the quote amount - quoteAmount = Web3Wrapper.toBaseUnitAmount( - new BigNumber(altIndicativeQuoteResponse.amount) - .times(new BigNumber(altIndicativeQuoteResponse.price)) - .decimalPlaces(altPair.quoteAssetDecimals, BigNumber.ROUND_DOWN), - altPair.quoteAssetDecimals, - ); - } else if (altIndicativeQuoteResponse.value) { - // if value is specified, value is the quote token amount - quoteAmount = Web3Wrapper.toBaseUnitAmount( - new BigNumber(altIndicativeQuoteResponse.value), - altPair.quoteAssetDecimals, - ); - // if value is specified, use the price (quote/base) to get the base amount - baseAmount = Web3Wrapper.toBaseUnitAmount( - new BigNumber(altIndicativeQuoteResponse.value) - .dividedBy(new BigNumber(altIndicativeQuoteResponse.price)) - .decimalPlaces(altPair.baseAssetDecimals, BigNumber.ROUND_DOWN), - altPair.baseAssetDecimals, - ); - } else { - throw new Error('neither amount or value were specified'); - } - if (makerToken.toLowerCase() === altPair.baseAsset.toLowerCase()) { - makerAmount = baseAmount; - takerAmount = quoteAmount; - } else if (makerToken.toLowerCase() === altPair.quoteAsset.toLowerCase()) { - makerAmount = quoteAmount; - takerAmount = baseAmount; - } else { - throw new Error(`Base, quote tokens don't align with maker, taker tokens`); - } - - return { - makerToken, - makerAmount, - takerToken, - takerAmount, - // HACK: alt implementation does not return an expiration with indicative quotes - // return now + { IMPUTED EXPIRY SECONDS } to have it included after order checks - expiry: new BigNumber(Date.now() / 1000) - .integerValue(BigNumber.ROUND_DOWN) - .plus(constants.ALT_MM_IMPUTED_INDICATIVE_EXPIRY_SECONDS), - }; -} - -/** - * Turn a standard quote request into an alt quote request - * and return the appropriate standard quote response - */ -export async function returnQuoteFromAltMMAsync( - url: string, - apiKey: string, - profile: string, - integratorKey: string, - quoteModel: AltQuoteModel, - makerToken: string, - takerToken: string, - maxResponseTimeMs: number, - altRfqAssetOfferings: AltRfqMakerAssetOfferings, - takerRequestQueryParams: TakerRequestQueryParamsUnnested, - axiosInstance: AxiosInstance, - warningLogger: LogFunction, - cancelToken: CancelToken, -): Promise<{ data: ResponseT; status: number }> { - const altPair = getAltMarketInfo( - altRfqAssetOfferings[url], - takerRequestQueryParams.buyTokenAddress, - takerRequestQueryParams.sellTokenAddress, - ); - - if (!altPair) { - throw new Error(`Alt pair not found`); - } - const side = altPair.baseAsset === takerRequestQueryParams.buyTokenAddress ? AltQuoteSide.Sell : AltQuoteSide.Buy; - - // comparison price needs to be quote/base - // in the standard implementation, it's maker/taker - let altComparisonPrice: string | undefined; - if (altPair.quoteAsset === makerToken) { - altComparisonPrice = takerRequestQueryParams.comparisonPrice - ? takerRequestQueryParams.comparisonPrice - : undefined; - } else { - altComparisonPrice = takerRequestQueryParams.comparisonPrice - ? new BigNumber(takerRequestQueryParams.comparisonPrice).pow(-1).toString() - : undefined; - } - - let data: AltQuoteRequestData; - data = { - market: `${altPair.id}`, - model: quoteModel, - profile, - side, - meta: { - txOrigin: takerRequestQueryParams.txOrigin!, - taker: takerRequestQueryParams.takerAddress, - client: integratorKey, - }, - }; - - // specify a comparison price if it exists - if (altComparisonPrice) { - data.meta.existingOrder = { - price: altComparisonPrice, - }; - } - - // need to specify amount or value - // amount is units of the base asset - // value is units of the quote asset - let requestSize: string; - if (takerRequestQueryParams.buyAmountBaseUnits) { - requestSize = Web3Wrapper.toUnitAmount( - new BigNumber(takerRequestQueryParams.buyAmountBaseUnits), - takerRequestQueryParams.buyTokenAddress === altPair.baseAsset - ? altPair.baseAssetDecimals - : altPair.quoteAssetDecimals, - ).toString(); - if (takerRequestQueryParams.buyTokenAddress === altPair.baseAsset) { - data.amount = requestSize; - // add to 'existing order' if there is a comparison price - if (data.meta.existingOrder) { - data.meta.existingOrder.amount = requestSize; - } - } else { - data.value = requestSize; - // add to 'existing order' if there is a comparison price - if (data.meta.existingOrder) { - data.meta.existingOrder.value = requestSize; - } - } - } else if (takerRequestQueryParams.sellAmountBaseUnits) { - requestSize = Web3Wrapper.toUnitAmount( - new BigNumber(takerRequestQueryParams.sellAmountBaseUnits), - takerRequestQueryParams.sellTokenAddress === altPair.baseAsset - ? altPair.baseAssetDecimals - : altPair.quoteAssetDecimals, - ).toString(); - if (takerRequestQueryParams.sellTokenAddress === altPair.baseAsset) { - data.amount = requestSize; - if (data.meta.existingOrder) { - data.meta.existingOrder.amount = requestSize; - } - } else { - data.value = requestSize; - if (data.meta.existingOrder) { - data.meta.existingOrder.value = requestSize; - } - } - } - - const response = await axiosInstance - .post(`${url}/quotes`, data, { - headers: { Authorization: `Bearer ${apiKey}` }, - timeout: maxResponseTimeMs, - cancelToken, - }) - .catch(err => { - if (err.response) { - // request was made and market maker responded - warningLogger( - { data: err.response.data, status: err.response.status, headers: err.response.headers }, - `Alt RFQ MM request failed`, - ); - } else if (err.request) { - warningLogger({}, 'Alt RFQ MM no response received'); - } else { - warningLogger({ err: err.message }, 'Failed to construct Alt RFQ MM request'); - } - throw new Error(`Alt RFQ MM request failed`); - }); - - // empty response will get filtered out in validation - const emptyResponse = {}; - - if (response.status !== SUCCESS_CODE) { - const rejectedRequestInfo = { - status: response.status, - message: response.data, - }; - warningLogger(rejectedRequestInfo, `Alt RFQ MM did not return a status of ${SUCCESS_CODE}`); - return { - data: (emptyResponse as unknown) as ResponseT, - status: response.status, - }; - } - // successful handling but no quote is indicated by status = 'rejected' - if (response.data.status === 'rejected') { - warningLogger( - response.data.id, - `Alt RFQ MM handled the request successfully but did not return a quote (status = 'rejected')`, - ); - return { - data: (emptyResponse as unknown) as ResponseT, - // hack: set the http status to 204 no content so we can more - // easily track when no quote is returned - status: 204, - }; - } - - const parsedResponse = - quoteModel === 'firm' - ? parseFirmQuoteResponseFromAltMM(response.data) - : parseIndicativeQuoteResponseFromAltMM(response.data, altPair, makerToken, takerToken); - - return { - // hack to appease type checking - data: (parsedResponse as unknown) as ResponseT, - status: response.status, - }; -} diff --git a/packages/asset-swapper/src/utils/assert.ts b/packages/asset-swapper/src/utils/assert.ts deleted file mode 100644 index 7392a4fc1c..0000000000 --- a/packages/asset-swapper/src/utils/assert.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { assert as sharedAssert } from '@0x/assert'; -import * as _ from 'lodash'; - -import { Orderbook } from '../swap_quoter'; - -export const assert = { - ...sharedAssert, - isValidOrderbook(variableName: string, orderFetcher: Orderbook): void { - sharedAssert.isFunction(`${variableName}.getOrdersAsync`, orderFetcher.getOrdersAsync.bind(orderFetcher)); - sharedAssert.isFunction( - `${variableName}.getBatchOrdersAsync`, - orderFetcher.getBatchOrdersAsync.bind(orderFetcher), - ); - }, - isValidPercentage(variableName: string, percentage: number): void { - assert.isNumber(variableName, percentage); - assert.assert( - percentage >= 0 && percentage <= 1, - `Expected ${variableName} to be between 0 and 1, but is ${percentage}`, - ); - }, -}; diff --git a/packages/asset-swapper/src/utils/irfq_client.ts b/packages/asset-swapper/src/utils/irfq_client.ts deleted file mode 100644 index d59cf5f874..0000000000 --- a/packages/asset-swapper/src/utils/irfq_client.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { RfqOrder, Signature } from '@0x/protocol-utils'; -import { BigNumber } from '@0x/utils'; - -import { AltRfqMakerAssetOfferings } from '../types'; - -export interface RfqClientV1PriceRequest { - altRfqAssetOfferings: AltRfqMakerAssetOfferings | undefined; - assetFillAmount: BigNumber; - chainId: number; - comparisonPrice: BigNumber | undefined; - integratorId: string; - intentOnFilling: boolean; - makerToken: string; - marketOperation: 'Sell' | 'Buy'; - takerAddress: string; - takerToken: string; - txOrigin: string; -} - -export interface RfqClientV1QuoteRequest extends RfqClientV1PriceRequest {} - -export interface RfqClientV1Price { - expiry: BigNumber; - kind: 'rfq' | 'otc'; - makerAmount: BigNumber; - makerToken: string; - makerUri: string; - takerAmount: BigNumber; - takerToken: string; -} - -export interface RfqClientV1PriceResponse { - prices: RfqClientV1Price[]; -} - -export interface RfqClientV1Quote { - makerUri: string; - order: RfqOrder; - signature: Signature; -} - -export interface RfqClientV1QuoteResponse { - quotes: RfqClientV1Quote[]; -} - -/** - * IRfqClient is an interface that defines how to connect with an Rfq system. - */ -export interface IRfqClient { - /** - * Fetches a list of "indicative quotes" or prices from a remote Rfq server - */ - getV1PricesAsync(request: RfqClientV1PriceRequest): Promise; - - /** - * Fetches a list of "firm quotes" or signed quotes from a remote Rfq server. - */ - getV1QuotesAsync(request: RfqClientV1QuoteRequest): Promise; -} diff --git a/packages/asset-swapper/src/utils/market_operation_utils/aave_reserves_cache.ts b/packages/asset-swapper/src/utils/market_operation_utils/aave_reserves_cache.ts deleted file mode 100644 index ac99a889db..0000000000 --- a/packages/asset-swapper/src/utils/market_operation_utils/aave_reserves_cache.ts +++ /dev/null @@ -1,105 +0,0 @@ -import { logUtils } from '@0x/utils'; -import { gql, request } from 'graphql-request'; - -import { constants } from '../../constants'; - -const RESERVES_GQL_QUERY = gql` - { - reserves( - first: 300 - where: { isActive: true, isFrozen: false } - orderBy: totalLiquidity - orderDirection: desc - ) { - id - underlyingAsset - aToken { - id - } - pool { - id - lendingPool - } - } - } -`; - -export interface AaveReserve { - id: string; - underlyingAsset: string; - aToken: { - id: string; - }; - pool: { - id: string; - lendingPool: string; - }; -} - -interface Cache { - [key: string]: AaveReserve[]; -} - -const RESERVES_REFRESH_INTERVAL_MS = 30 * constants.ONE_MINUTE_MS; - -/** - * Fetches Aave V2 reserve information from the official subgraph(s). - * The reserve information is updated every 30 minutes and cached - * so that it can be accessed with the underlying token's address - */ -export class AaveV2ReservesCache { - private _cache: Cache = {}; - constructor(private readonly _subgraphUrl: string) { - const resfreshReserves = async () => this.fetchAndUpdateReservesAsync(); - // tslint:disable-next-line:no-floating-promises - resfreshReserves(); - setInterval(resfreshReserves, RESERVES_REFRESH_INTERVAL_MS); - } - /** - * Fetches Aave V2 reserves from the subgraph and updates the cache - */ - public async fetchAndUpdateReservesAsync(): Promise { - try { - const { reserves } = await request<{ reserves: AaveReserve[] }>(this._subgraphUrl, RESERVES_GQL_QUERY); - const newCache = reserves.reduce((memo, reserve) => { - const underlyingAsset = reserve.underlyingAsset.toLowerCase(); - if (!memo[underlyingAsset]) { - memo[underlyingAsset] = []; - } - - memo[underlyingAsset].push(reserve); - return memo; - }, {}); - - this._cache = newCache; - } catch (err) { - logUtils.warn(`Failed to update Aave V2 reserves cache: ${err.message}`); - // Empty cache just to be safe - this._cache = {}; - } - } - public get(takerToken: string, makerToken: string): AaveReserve | undefined { - // Deposit takerToken into reserve - if (this._cache[takerToken.toLowerCase()]) { - const matchingReserve = this._cache[takerToken.toLowerCase()].find( - r => r.aToken.id === makerToken.toLowerCase(), - ); - if (matchingReserve) { - return matchingReserve; - } - } - - // Withdraw makerToken from reserve - if (this._cache[makerToken.toLowerCase()]) { - const matchingReserve = this._cache[makerToken.toLowerCase()].find( - r => r.aToken.id === takerToken.toLowerCase(), - ); - if (matchingReserve) { - return matchingReserve; - } - } - - // No match - return undefined; - } -} diff --git a/packages/asset-swapper/src/utils/market_operation_utils/bancor_service.ts b/packages/asset-swapper/src/utils/market_operation_utils/bancor_service.ts deleted file mode 100644 index eccea376eb..0000000000 --- a/packages/asset-swapper/src/utils/market_operation_utils/bancor_service.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { SupportedProvider } from '@0x/dev-utils'; -import { SDK } from '@bancor/sdk'; -import { Ethereum } from '@bancor/sdk/dist/blockchains/ethereum'; -import { BlockchainType } from '@bancor/sdk/dist/types'; - -import { MAINNET_TOKENS } from './constants'; - -const findToken = (tokenAddress: string, graph: object): string => - // If we're looking for WETH it is stored by Bancor as the 0xeee address - tokenAddress.toLowerCase() === MAINNET_TOKENS.WETH.toLowerCase() - ? '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE' - : Object.keys(graph).filter(k => k.toLowerCase() === tokenAddress.toLowerCase())[0]; - -export class BancorService { - public static async createAsync(provider: SupportedProvider): Promise { - const sdk = await SDK.create({ ethereumNodeEndpoint: provider }); - const service = new BancorService(sdk); - return service; - } - - constructor(public sdk: SDK) {} - public getPaths(_fromToken: string, _toToken: string): string[][] { - // HACK: We reach into the blockchain object and pull in it's cache of tokens - // and we use it's internal non-async getPathsFunc - try { - const blockchain = this.sdk._core.blockchains[BlockchainType.Ethereum] as Ethereum; - const fromToken = findToken(_fromToken, blockchain.graph); - const toToken = findToken(_toToken, blockchain.graph); - return blockchain.getPathsFunc.bind(blockchain)(fromToken, toToken); - } catch (e) { - return []; - } - } -} diff --git a/packages/asset-swapper/src/utils/market_operation_utils/bridge_source_utils.ts b/packages/asset-swapper/src/utils/market_operation_utils/bridge_source_utils.ts deleted file mode 100644 index b129372dff..0000000000 --- a/packages/asset-swapper/src/utils/market_operation_utils/bridge_source_utils.ts +++ /dev/null @@ -1,574 +0,0 @@ -import { ChainId } from '@0x/contract-addresses'; -import { BigNumber } from '@0x/utils'; - -import { - ACRYPTOS_BSC_INFOS, - APESWAP_ROUTER_BY_CHAIN_ID, - BAKERYSWAP_ROUTER_BY_CHAIN_ID, - BELT_BSC_INFOS, - BISWAP_ROUTER_BY_CHAIN_ID, - CHEESESWAP_ROUTER_BY_CHAIN_ID, - COMPONENT_POOLS_BY_CHAIN_ID, - CRYPTO_COM_ROUTER_BY_CHAIN_ID, - CURVE_AVALANCHE_INFOS, - CURVE_FANTOM_INFOS, - CURVE_MAINNET_INFOS, - CURVE_OPTIMISM_INFOS, - CURVE_POLYGON_INFOS, - CURVE_V2_AVALANCHE_INFOS, - CURVE_V2_FANTOM_INFOS, - CURVE_V2_MAINNET_INFOS, - CURVE_V2_POLYGON_INFOS, - DFYN_ROUTER_BY_CHAIN_ID, - ELLIPSIS_BSC_INFOS, - FIREBIRDONESWAP_BSC_INFOS, - FIREBIRDONESWAP_POLYGON_INFOS, - IRONSWAP_POLYGON_INFOS, - KNIGHTSWAP_ROUTER_BY_CHAIN_ID, - MAX_DODOV2_POOLS_QUERIED, - MDEX_ROUTER_BY_CHAIN_ID, - MESHSWAP_ROUTER_BY_CHAIN_ID, - MOBIUSMONEY_CELO_INFOS, - MORPHEUSSWAP_ROUTER_BY_CHAIN_ID, - MSTABLE_POOLS_BY_CHAIN_ID, - NERVE_BSC_INFOS, - NULL_ADDRESS, - PANCAKESWAPV2_ROUTER_BY_CHAIN_ID, - PANCAKESWAP_ROUTER_BY_CHAIN_ID, - PANGOLIN_ROUTER_BY_CHAIN_ID, - PLATYPUS_AVALANCHE_INFOS, - QUICKSWAP_ROUTER_BY_CHAIN_ID, - SADDLE_MAINNET_INFOS, - SHELL_POOLS_BY_CHAIN_ID, - SHIBASWAP_ROUTER_BY_CHAIN_ID, - SPIRITSWAP_ROUTER_BY_CHAIN_ID, - SPOOKYSWAP_ROUTER_BY_CHAIN_ID, - SUSHISWAP_ROUTER_BY_CHAIN_ID, - SYNAPSE_AVALANCHE_INFOS, - SYNAPSE_BSC_INFOS, - SYNAPSE_FANTOM_INFOS, - SYNAPSE_MAINNET_INFOS, - SYNAPSE_OPTIMISM_INFOS, - SYNAPSE_POLYGON_INFOS, - TRADER_JOE_ROUTER_BY_CHAIN_ID, - UBESWAP_ROUTER_BY_CHAIN_ID, - UNISWAPV2_ROUTER_BY_CHAIN_ID, - WAULTSWAP_ROUTER_BY_CHAIN_ID, - XSIGMA_MAINNET_INFOS, - YOSHI_ROUTER_BY_CHAIN_ID, -} from './constants'; -import { CurveInfo, ERC20BridgeSource, PlatypusInfo } from './types'; - -// tslint:disable-next-line: completed-docs ban-types -export function isValidAddress(address: string | String): address is string { - return (typeof address === 'string' || address instanceof String) && address.toString() !== NULL_ADDRESS; -} - -// tslint:disable completed-docs -export function getDodoV2Offsets(): BigNumber[] { - return Array(MAX_DODOV2_POOLS_QUERIED) - .fill(0) - .map((_v, i) => new BigNumber(i)); -} - -// tslint:disable completed-docs -export function getShellsForPair(chainId: ChainId, takerToken: string, makerToken: string): string[] { - if (chainId !== ChainId.Mainnet) { - return []; - } - return Object.values(SHELL_POOLS_BY_CHAIN_ID[chainId]) - .filter(c => [makerToken, takerToken].every(t => c.tokens.includes(t))) - .map(i => i.poolAddress); -} - -// tslint:disable completed-docs -export function getComponentForPair(chainId: ChainId, takerToken: string, makerToken: string): string[] { - if (chainId !== ChainId.Mainnet) { - return []; - } - return Object.values(COMPONENT_POOLS_BY_CHAIN_ID[chainId]) - .filter(c => [makerToken, takerToken].every(t => c.tokens.includes(t))) - .map(i => i.poolAddress); -} - -// tslint:disable completed-docs -export function getMStableForPair(chainId: ChainId, takerToken: string, makerToken: string): string[] { - if (chainId !== ChainId.Mainnet && chainId !== ChainId.Polygon) { - return []; - } - return Object.values(MSTABLE_POOLS_BY_CHAIN_ID[chainId]) - .filter(c => [makerToken, takerToken].every(t => c.tokens.includes(t))) - .map(i => i.poolAddress); -} - -// tslint:disable completed-docs -export function getCurveInfosForPair(chainId: ChainId, takerToken: string, makerToken: string): CurveInfo[] { - switch (chainId) { - case ChainId.Mainnet: - return Object.values(CURVE_MAINNET_INFOS).filter(c => - [makerToken, takerToken].every( - t => - (c.tokens.includes(t) && c.metaTokens === undefined) || - (c.tokens.includes(t) && - [makerToken, takerToken].filter(v => c.metaTokens?.includes(v)).length > 0), - ), - ); - case ChainId.Polygon: - return Object.values(CURVE_POLYGON_INFOS).filter(c => - [makerToken, takerToken].every( - t => - (c.tokens.includes(t) && c.metaTokens === undefined) || - (c.tokens.includes(t) && - [makerToken, takerToken].filter(v => c.metaTokens?.includes(v)).length > 0), - ), - ); - case ChainId.Fantom: - return Object.values(CURVE_FANTOM_INFOS).filter(c => - [makerToken, takerToken].every( - t => - (c.tokens.includes(t) && c.metaTokens === undefined) || - (c.tokens.includes(t) && - [makerToken, takerToken].filter(v => c.metaTokens?.includes(v)).length > 0), - ), - ); - case ChainId.Avalanche: - return Object.values(CURVE_AVALANCHE_INFOS).filter(c => - [makerToken, takerToken].every( - t => - (c.tokens.includes(t) && c.metaTokens === undefined) || - (c.tokens.includes(t) && - [makerToken, takerToken].filter(v => c.metaTokens?.includes(v)).length > 0), - ), - ); - case ChainId.Optimism: - return Object.values(CURVE_OPTIMISM_INFOS).filter(c => - [makerToken, takerToken].every( - t => - (c.tokens.includes(t) && c.metaTokens === undefined) || - (c.tokens.includes(t) && - [makerToken, takerToken].filter(v => c.metaTokens?.includes(v)).length > 0), - ), - ); - default: - return []; - } -} - -// tslint:disable completed-docs -export function getCurveV2InfosForPair(chainId: ChainId, takerToken: string, makerToken: string): CurveInfo[] { - switch (chainId) { - case ChainId.Mainnet: - return Object.values(CURVE_V2_MAINNET_INFOS).filter(c => - [makerToken, takerToken].every( - t => - (c.tokens.includes(t) && c.metaTokens === undefined) || - (c.tokens.includes(t) && - [makerToken, takerToken].filter(v => c.metaTokens?.includes(v)).length > 0), - ), - ); - case ChainId.Polygon: - return Object.values(CURVE_V2_POLYGON_INFOS).filter(c => - [makerToken, takerToken].every( - t => - (c.tokens.includes(t) && c.metaTokens === undefined) || - (c.tokens.includes(t) && - [makerToken, takerToken].filter(v => c.metaTokens?.includes(v)).length > 0), - ), - ); - case ChainId.Fantom: - return Object.values(CURVE_V2_FANTOM_INFOS).filter(c => - [makerToken, takerToken].every( - t => - (c.tokens.includes(t) && c.metaTokens === undefined) || - (c.tokens.includes(t) && - [makerToken, takerToken].filter(v => c.metaTokens?.includes(v)).length > 0), - ), - ); - case ChainId.Avalanche: - return Object.values(CURVE_V2_AVALANCHE_INFOS).filter(c => - [makerToken, takerToken].every( - t => - (c.tokens.includes(t) && c.metaTokens === undefined) || - (c.tokens.includes(t) && - [makerToken, takerToken].filter(v => c.metaTokens?.includes(v)).length > 0), - ), - ); - default: - return []; - } -} - -export function getNerveInfosForPair(chainId: ChainId, takerToken: string, makerToken: string): CurveInfo[] { - if (chainId !== ChainId.BSC) { - return []; - } - return Object.values(NERVE_BSC_INFOS).filter(c => - [makerToken, takerToken].every( - t => - (c.tokens.includes(t) && c.metaTokens === undefined) || - (c.tokens.includes(t) && [makerToken, takerToken].filter(v => c.metaTokens?.includes(v)).length > 0), - ), - ); -} - -export function getSynapseInfosForPair(chainId: ChainId, takerToken: string, makerToken: string): CurveInfo[] { - switch (chainId) { - case ChainId.Mainnet: - return Object.values(SYNAPSE_MAINNET_INFOS).filter(c => - [makerToken, takerToken].every( - t => - (c.tokens.includes(t) && c.metaTokens === undefined) || - (c.tokens.includes(t) && - [makerToken, takerToken].filter(v => c.metaTokens?.includes(v)).length > 0), - ), - ); - case ChainId.Optimism: - return Object.values(SYNAPSE_OPTIMISM_INFOS).filter(c => - [makerToken, takerToken].every( - t => - (c.tokens.includes(t) && c.metaTokens === undefined) || - (c.tokens.includes(t) && - [makerToken, takerToken].filter(v => c.metaTokens?.includes(v)).length > 0), - ), - ); - case ChainId.BSC: - return Object.values(SYNAPSE_BSC_INFOS).filter(c => - [makerToken, takerToken].every( - t => - (c.tokens.includes(t) && c.metaTokens === undefined) || - (c.tokens.includes(t) && - [makerToken, takerToken].filter(v => c.metaTokens?.includes(v)).length > 0), - ), - ); - case ChainId.Polygon: - return Object.values(SYNAPSE_POLYGON_INFOS).filter(c => - [makerToken, takerToken].every( - t => - (c.tokens.includes(t) && c.metaTokens === undefined) || - (c.tokens.includes(t) && - [makerToken, takerToken].filter(v => c.metaTokens?.includes(v)).length > 0), - ), - ); - case ChainId.Fantom: - return Object.values(SYNAPSE_FANTOM_INFOS).filter(c => - [makerToken, takerToken].every( - t => - (c.tokens.includes(t) && c.metaTokens === undefined) || - (c.tokens.includes(t) && - [makerToken, takerToken].filter(v => c.metaTokens?.includes(v)).length > 0), - ), - ); - case ChainId.Avalanche: - return Object.values(SYNAPSE_AVALANCHE_INFOS).filter(c => - [makerToken, takerToken].every( - t => - (c.tokens.includes(t) && c.metaTokens === undefined) || - (c.tokens.includes(t) && - [makerToken, takerToken].filter(v => c.metaTokens?.includes(v)).length > 0), - ), - ); - default: - return []; - } -} - -export function getFirebirdOneSwapInfosForPair(chainId: ChainId, takerToken: string, makerToken: string): CurveInfo[] { - if (chainId === ChainId.BSC) { - return Object.values(FIREBIRDONESWAP_BSC_INFOS).filter(c => - [makerToken, takerToken].every( - t => - (c.tokens.includes(t) && c.metaTokens === undefined) || - (c.tokens.includes(t) && - [makerToken, takerToken].filter(v => c.metaTokens?.includes(v)).length > 0), - ), - ); - } else if (chainId === ChainId.Polygon) { - return Object.values(FIREBIRDONESWAP_POLYGON_INFOS).filter(c => - [makerToken, takerToken].every( - t => - (c.tokens.includes(t) && c.metaTokens === undefined) || - (c.tokens.includes(t) && - [makerToken, takerToken].filter(v => c.metaTokens?.includes(v)).length > 0), - ), - ); - } else { - return []; - } -} - -export function getBeltInfosForPair(chainId: ChainId, takerToken: string, makerToken: string): CurveInfo[] { - if (chainId !== ChainId.BSC) { - return []; - } - return Object.values(BELT_BSC_INFOS).filter(c => - [makerToken, takerToken].every( - t => - (c.tokens.includes(t) && c.metaTokens === undefined) || - (c.tokens.includes(t) && [makerToken, takerToken].filter(v => c.metaTokens?.includes(v)).length > 0), - ), - ); -} - -export function getEllipsisInfosForPair(chainId: ChainId, takerToken: string, makerToken: string): CurveInfo[] { - if (chainId !== ChainId.BSC) { - return []; - } - return Object.values(ELLIPSIS_BSC_INFOS).filter(c => - [makerToken, takerToken].every( - t => - (c.tokens.includes(t) && c.metaTokens === undefined) || - (c.tokens.includes(t) && [makerToken, takerToken].filter(v => c.metaTokens?.includes(v)).length > 0), - ), - ); -} - -export function getSaddleInfosForPair(chainId: ChainId, takerToken: string, makerToken: string): CurveInfo[] { - if (chainId !== ChainId.Mainnet) { - return []; - } - return Object.values(SADDLE_MAINNET_INFOS).filter(c => - [makerToken, takerToken].every( - t => - (c.tokens.includes(t) && c.metaTokens === undefined) || - (c.tokens.includes(t) && [makerToken, takerToken].filter(v => c.metaTokens?.includes(v)).length > 0), - ), - ); -} - -export function getIronSwapInfosForPair(chainId: ChainId, takerToken: string, makerToken: string): CurveInfo[] { - if (chainId !== ChainId.Polygon) { - return []; - } - return Object.values(IRONSWAP_POLYGON_INFOS).filter(c => - [makerToken, takerToken].every( - t => - (c.tokens.includes(t) && c.metaTokens === undefined) || - (c.tokens.includes(t) && [makerToken, takerToken].filter(v => c.metaTokens?.includes(v)).length > 0), - ), - ); -} - -export function getXSigmaInfosForPair(chainId: ChainId, takerToken: string, makerToken: string): CurveInfo[] { - if (chainId !== ChainId.Mainnet) { - return []; - } - return Object.values(XSIGMA_MAINNET_INFOS).filter(c => - [makerToken, takerToken].every( - t => - (c.tokens.includes(t) && c.metaTokens === undefined) || - (c.tokens.includes(t) && [makerToken, takerToken].filter(v => c.metaTokens?.includes(v)).length > 0), - ), - ); -} - -export function getAcryptosInfosForPair(chainId: ChainId, takerToken: string, makerToken: string): CurveInfo[] { - if (chainId !== ChainId.BSC) { - return []; - } - return Object.values(ACRYPTOS_BSC_INFOS).filter(c => - [makerToken, takerToken].every( - t => - (c.tokens.includes(t) && c.metaTokens === undefined) || - (c.tokens.includes(t) && [makerToken, takerToken].filter(v => c.metaTokens?.includes(v)).length > 0), - ), - ); -} -export function getMobiusMoneyInfoForPair(chainId: ChainId, takerToken: string, makerToken: string): CurveInfo[] { - if (chainId !== ChainId.Celo) { - return []; - } - return Object.values(MOBIUSMONEY_CELO_INFOS).filter(c => - [makerToken, takerToken].every( - t => - (c.tokens.includes(t) && c.metaTokens === undefined) || - (c.tokens.includes(t) && [makerToken, takerToken].filter(v => c.metaTokens?.includes(v)).length > 0), - ), - ); -} - -export function getPlatypusInfoForPair(chainId: ChainId, takerToken: string, makerToken: string): PlatypusInfo[] { - if (chainId !== ChainId.Avalanche) { - return []; - } - return Object.values(PLATYPUS_AVALANCHE_INFOS).filter(c => - [makerToken, takerToken].every(t => c.tokens.includes(t)), - ); -} - -export function getShellLikeInfosForPair( - chainId: ChainId, - takerToken: string, - makerToken: string, - source: ERC20BridgeSource.Shell | ERC20BridgeSource.Component | ERC20BridgeSource.MStable, -): string[] { - switch (source) { - case ERC20BridgeSource.Shell: - return getShellsForPair(chainId, takerToken, makerToken); - case ERC20BridgeSource.Component: - return getComponentForPair(chainId, takerToken, makerToken); - case ERC20BridgeSource.MStable: - return getMStableForPair(chainId, takerToken, makerToken); - default: - throw new Error(`Unknown Shell like source ${source}`); - } -} - -export interface CurveDetailedInfo extends CurveInfo { - makerTokenIdx: number; - takerTokenIdx: number; -} - -export function getCurveLikeInfosForPair( - chainId: ChainId, - takerToken: string, - makerToken: string, - source: - | ERC20BridgeSource.Curve - | ERC20BridgeSource.CurveV2 - | ERC20BridgeSource.Nerve - | ERC20BridgeSource.Synapse - | ERC20BridgeSource.Belt - | ERC20BridgeSource.Ellipsis - | ERC20BridgeSource.Saddle - | ERC20BridgeSource.IronSwap - | ERC20BridgeSource.XSigma - | ERC20BridgeSource.FirebirdOneSwap - | ERC20BridgeSource.ACryptos - | ERC20BridgeSource.MobiusMoney, -): CurveDetailedInfo[] { - let pools: CurveInfo[] = []; - switch (source) { - case ERC20BridgeSource.Curve: - pools = getCurveInfosForPair(chainId, takerToken, makerToken); - break; - case ERC20BridgeSource.CurveV2: - pools = getCurveV2InfosForPair(chainId, takerToken, makerToken); - break; - case ERC20BridgeSource.Nerve: - pools = getNerveInfosForPair(chainId, takerToken, makerToken); - break; - case ERC20BridgeSource.Synapse: - pools = getSynapseInfosForPair(chainId, takerToken, makerToken); - break; - case ERC20BridgeSource.Belt: - pools = getBeltInfosForPair(chainId, takerToken, makerToken); - break; - case ERC20BridgeSource.Ellipsis: - pools = getEllipsisInfosForPair(chainId, takerToken, makerToken); - break; - case ERC20BridgeSource.Saddle: - pools = getSaddleInfosForPair(chainId, takerToken, makerToken); - break; - case ERC20BridgeSource.XSigma: - pools = getXSigmaInfosForPair(chainId, takerToken, makerToken); - break; - case ERC20BridgeSource.FirebirdOneSwap: - pools = getFirebirdOneSwapInfosForPair(chainId, takerToken, makerToken); - break; - case ERC20BridgeSource.IronSwap: - pools = getIronSwapInfosForPair(chainId, takerToken, makerToken); - break; - case ERC20BridgeSource.ACryptos: - pools = getAcryptosInfosForPair(chainId, takerToken, makerToken); - break; - case ERC20BridgeSource.MobiusMoney: - pools = getMobiusMoneyInfoForPair(chainId, takerToken, makerToken); - break; - default: - throw new Error(`Unknown Curve like source ${source}`); - } - return pools.map(pool => ({ - ...pool, - makerTokenIdx: pool.tokens.indexOf(makerToken), - takerTokenIdx: pool.tokens.indexOf(takerToken), - })); -} - -export function uniswapV2LikeRouterAddress( - chainId: ChainId, - source: - | ERC20BridgeSource.UniswapV2 - | ERC20BridgeSource.SushiSwap - | ERC20BridgeSource.CryptoCom - | ERC20BridgeSource.PancakeSwap - | ERC20BridgeSource.PancakeSwapV2 - | ERC20BridgeSource.BakerySwap - | ERC20BridgeSource.ApeSwap - | ERC20BridgeSource.CheeseSwap - | ERC20BridgeSource.QuickSwap - | ERC20BridgeSource.Dfyn - | ERC20BridgeSource.WaultSwap - | ERC20BridgeSource.ShibaSwap - | ERC20BridgeSource.TraderJoe - | ERC20BridgeSource.Pangolin - | ERC20BridgeSource.UbeSwap - | ERC20BridgeSource.MorpheusSwap - | ERC20BridgeSource.SpookySwap - | ERC20BridgeSource.SpiritSwap - | ERC20BridgeSource.BiSwap - | ERC20BridgeSource.Yoshi - | ERC20BridgeSource.MDex - | ERC20BridgeSource.KnightSwap - | ERC20BridgeSource.MeshSwap, -): string { - switch (source) { - case ERC20BridgeSource.UniswapV2: - return UNISWAPV2_ROUTER_BY_CHAIN_ID[chainId]; - case ERC20BridgeSource.SushiSwap: - return SUSHISWAP_ROUTER_BY_CHAIN_ID[chainId]; - case ERC20BridgeSource.CryptoCom: - return CRYPTO_COM_ROUTER_BY_CHAIN_ID[chainId]; - case ERC20BridgeSource.PancakeSwap: - return PANCAKESWAP_ROUTER_BY_CHAIN_ID[chainId]; - case ERC20BridgeSource.PancakeSwapV2: - return PANCAKESWAPV2_ROUTER_BY_CHAIN_ID[chainId]; - case ERC20BridgeSource.BakerySwap: - return BAKERYSWAP_ROUTER_BY_CHAIN_ID[chainId]; - case ERC20BridgeSource.ApeSwap: - return APESWAP_ROUTER_BY_CHAIN_ID[chainId]; - case ERC20BridgeSource.CheeseSwap: - return CHEESESWAP_ROUTER_BY_CHAIN_ID[chainId]; - case ERC20BridgeSource.QuickSwap: - return QUICKSWAP_ROUTER_BY_CHAIN_ID[chainId]; - case ERC20BridgeSource.Dfyn: - return DFYN_ROUTER_BY_CHAIN_ID[chainId]; - case ERC20BridgeSource.WaultSwap: - return WAULTSWAP_ROUTER_BY_CHAIN_ID[chainId]; - case ERC20BridgeSource.ShibaSwap: - return SHIBASWAP_ROUTER_BY_CHAIN_ID[chainId]; - case ERC20BridgeSource.Pangolin: - return PANGOLIN_ROUTER_BY_CHAIN_ID[chainId]; - case ERC20BridgeSource.TraderJoe: - return TRADER_JOE_ROUTER_BY_CHAIN_ID[chainId]; - case ERC20BridgeSource.UbeSwap: - return UBESWAP_ROUTER_BY_CHAIN_ID[chainId]; - case ERC20BridgeSource.MorpheusSwap: - return MORPHEUSSWAP_ROUTER_BY_CHAIN_ID[chainId]; - case ERC20BridgeSource.SpookySwap: - return SPOOKYSWAP_ROUTER_BY_CHAIN_ID[chainId]; - case ERC20BridgeSource.SpiritSwap: - return SPIRITSWAP_ROUTER_BY_CHAIN_ID[chainId]; - case ERC20BridgeSource.BiSwap: - return BISWAP_ROUTER_BY_CHAIN_ID[chainId]; - case ERC20BridgeSource.Yoshi: - return YOSHI_ROUTER_BY_CHAIN_ID[chainId]; - case ERC20BridgeSource.MeshSwap: - return MESHSWAP_ROUTER_BY_CHAIN_ID[chainId]; - case ERC20BridgeSource.MDex: - return MDEX_ROUTER_BY_CHAIN_ID[chainId]; - case ERC20BridgeSource.KnightSwap: - return KNIGHTSWAP_ROUTER_BY_CHAIN_ID[chainId]; - default: - throw new Error(`Unknown UniswapV2 like source ${source}`); - } -} - -const BAD_TOKENS_BY_SOURCE: Partial<{ [key in ERC20BridgeSource]: string[] }> = { - [ERC20BridgeSource.Uniswap]: [ - '0xb8c77482e45f1f44de1745f52c74426c631bdd52', // BNB - ], -}; - -export function isBadTokenForSource(token: string, source: ERC20BridgeSource): boolean { - return (BAD_TOKENS_BY_SOURCE[source] || []).includes(token.toLowerCase()); -} diff --git a/packages/asset-swapper/src/utils/market_operation_utils/comparison_price.ts b/packages/asset-swapper/src/utils/market_operation_utils/comparison_price.ts deleted file mode 100644 index 2fbbe05605..0000000000 --- a/packages/asset-swapper/src/utils/market_operation_utils/comparison_price.ts +++ /dev/null @@ -1,92 +0,0 @@ -import { Web3Wrapper } from '@0x/dev-utils'; -import { FillQuoteTransformerOrderType } from '@0x/protocol-utils'; -import { BigNumber, logUtils } from '@0x/utils'; -import * as _ from 'lodash'; - -import { MarketOperation } from '../../types'; - -import { COMPARISON_PRICE_DECIMALS, SOURCE_FLAGS } from './constants'; -import { - ComparisonPrice, - ERC20BridgeSource, - ExchangeProxyOverhead, - FeeEstimate, - FeeSchedule, - MarketSideLiquidity, -} from './types'; - -/** - * Takes in an optimizer response and returns a price for RFQT MMs to beat - * returns the price of the taker asset in terms of the maker asset - * So the RFQT MM should aim for a higher price - * @param adjustedRate the adjusted rate (accounting for fees) from the optimizer, maker/taker - * @param amount the amount specified by the client - * @param marketSideLiquidity the results from querying liquidity sources - * @param feeSchedule the fee schedule passed to the Optimizer - * @return ComparisonPrice object with the prices for RFQ MMs to beat - */ -export function getComparisonPrices( - adjustedRate: BigNumber, - amount: BigNumber, - marketSideLiquidity: MarketSideLiquidity, - feeSchedule: FeeSchedule, - exchangeProxyOverhead: ExchangeProxyOverhead, -): ComparisonPrice { - let wholeOrder: BigNumber | undefined; - let feeInEth: BigNumber | number; - - // HACK: get the fee penalty of a single 0x native order - // The FeeSchedule function takes in a `FillData` object and returns a fee estimate in ETH - // We don't have fill data here, we just want the cost of a single native order, so we pass in undefined - // This works because the feeSchedule returns a constant for Native orders, this will need - // to be tweaked if the feeSchedule for native orders uses the fillData passed in - // 2 potential issues: there is no native fee schedule or the fee schedule depends on fill data - if (feeSchedule[ERC20BridgeSource.Native] === undefined) { - logUtils.warn('ComparisonPrice function did not find native order fee schedule'); - - return { wholeOrder }; - } else { - try { - const fillFeeInEth = new BigNumber( - (feeSchedule[ERC20BridgeSource.Native] as FeeEstimate)({ type: FillQuoteTransformerOrderType.Rfq }).fee, - ); - const exchangeProxyOverheadInEth = new BigNumber(exchangeProxyOverhead(SOURCE_FLAGS.RfqOrder)); - feeInEth = fillFeeInEth.plus(exchangeProxyOverheadInEth); - } catch { - logUtils.warn('Native order fee schedule requires fill data'); - - return { wholeOrder }; - } - } - - // Calc native order fee penalty in output unit (maker units for sells, taker unit for buys) - const feePenalty = !marketSideLiquidity.outputAmountPerEth.isZero() - ? marketSideLiquidity.outputAmountPerEth.times(feeInEth) - : // if it's a sell, the input token is the taker token - marketSideLiquidity.inputAmountPerEth - .times(feeInEth) - .times(marketSideLiquidity.side === MarketOperation.Sell ? adjustedRate : adjustedRate.pow(-1)); - - // the adjusted rate is defined as maker/taker - // input is the taker token for sells, input is the maker token for buys - const orderMakerAmount = - marketSideLiquidity.side === MarketOperation.Sell ? adjustedRate.times(amount).plus(feePenalty) : amount; - const orderTakerAmount = - marketSideLiquidity.side === MarketOperation.Sell ? amount : amount.dividedBy(adjustedRate).minus(feePenalty); - - if (orderTakerAmount.gt(0) && orderMakerAmount.gt(0)) { - const optimalMakerUnitAmount = Web3Wrapper.toUnitAmount( - // round up maker amount -- err to giving more competitive price - orderMakerAmount.integerValue(BigNumber.ROUND_UP), - marketSideLiquidity.makerTokenDecimals, - ); - const optimalTakerUnitAmount = Web3Wrapper.toUnitAmount( - // round down taker amount -- err to giving more competitive price - orderTakerAmount.integerValue(BigNumber.ROUND_DOWN), - marketSideLiquidity.takerTokenDecimals, - ); - wholeOrder = optimalMakerUnitAmount.div(optimalTakerUnitAmount).decimalPlaces(COMPARISON_PRICE_DECIMALS); - } - - return { wholeOrder }; -} diff --git a/packages/asset-swapper/src/utils/market_operation_utils/compound_ctoken_cache.ts b/packages/asset-swapper/src/utils/market_operation_utils/compound_ctoken_cache.ts deleted file mode 100644 index cb07bef487..0000000000 --- a/packages/asset-swapper/src/utils/market_operation_utils/compound_ctoken_cache.ts +++ /dev/null @@ -1,77 +0,0 @@ -import { logUtils } from '@0x/utils'; -import axios from 'axios'; - -import { constants } from '../../constants'; - -export interface CToken { - tokenAddress: string; - underlyingAddress: string; -} - -interface CTokenApiResponse { - cToken: Array<{ - token_address: string; - underlying_address: string; - }>; -} - -interface Cache { - [key: string]: CToken; -} - -const CTOKEN_REFRESH_INTERVAL_MS = 30 * constants.ONE_MINUTE_MS; - -/** - * Fetches a list of CTokens from Compound's official API. - * The token information is updated every 30 minutes and cached - * so that it can be accessed with the underlying token's address. - */ -export class CompoundCTokenCache { - private _cache: Cache = {}; - constructor(private readonly _apiUrl: string, private readonly _wethAddress: string) { - const refreshCTokenCache = async () => this.fetchAndUpdateCTokensAsync(); - // tslint:disable-next-line:no-floating-promises - refreshCTokenCache(); - setInterval(refreshCTokenCache, CTOKEN_REFRESH_INTERVAL_MS); - } - - public async fetchAndUpdateCTokensAsync(): Promise { - try { - const { data } = await axios.get(`${this._apiUrl}/ctoken`); - const newCache = data?.cToken.reduce((memo, cToken) => { - // NOTE: Re-map cETH with null underlying token address to WETH address (we only handle WETH internally) - const underlyingAddressClean = cToken.underlying_address - ? cToken.underlying_address.toLowerCase() - : this._wethAddress; - - const tokenData: CToken = { - tokenAddress: cToken.token_address.toLowerCase(), - underlyingAddress: underlyingAddressClean, - }; - memo[underlyingAddressClean] = tokenData; - return memo; - }, {}); - - this._cache = newCache; - } catch (err) { - logUtils.warn(`Failed to update Compound cToken cache: ${err.message}`); - // NOTE: Safe to keep already cached data as tokens should only be added to the list - } - } - public get(takerToken: string, makerToken: string): CToken | undefined { - // mint cToken - let cToken = this._cache[takerToken.toLowerCase()]; - if (cToken && makerToken.toLowerCase() === cToken.tokenAddress.toLowerCase()) { - return cToken; - } - - // redeem cToken - cToken = this._cache[makerToken.toLowerCase()]; - if (cToken && takerToken.toLowerCase() === cToken.tokenAddress.toLowerCase()) { - return cToken; - } - - // No match - return undefined; - } -} diff --git a/packages/asset-swapper/src/utils/market_operation_utils/constants.ts b/packages/asset-swapper/src/utils/market_operation_utils/constants.ts deleted file mode 100644 index 55727d2b39..0000000000 --- a/packages/asset-swapper/src/utils/market_operation_utils/constants.ts +++ /dev/null @@ -1,2773 +0,0 @@ -import { ChainId, getContractAddressesForChainOrThrow } from '@0x/contract-addresses'; -import { FillQuoteTransformerOrderType } from '@0x/protocol-utils'; -import { BigNumber } from '@0x/utils'; -import { formatBytes32String, parseBytes32String } from '@ethersproject/strings'; - -import { TokenAdjacencyGraph, TokenAdjacencyGraphBuilder } from '../token_adjacency_graph'; - -import { IdentityFillAdjustor } from './identity_fill_adjustor'; -import { SourceFilters } from './source_filters'; -import { - AaveV2FillData, - BalancerV2BatchSwapFillData, - BancorFillData, - CompoundFillData, - CurveFillData, - CurveFunctionSelectors, - CurveInfo, - DODOFillData, - ERC20BridgeSource, - FeeSchedule, - FillData, - FinalUniswapV3FillData, - GasSchedule, - GeistFillData, - GetMarketOrdersOpts, - isFinalUniswapV3FillData, - LidoFillData, - LidoInfo, - LiquidityProviderFillData, - LiquidityProviderRegistry, - MakerPsmFillData, - MultiHopFillData, - PlatypusInfo, - PsmInfo, - SynthetixFillData, - UniswapV2FillData, - UniswapV3FillData, - WOOFiFillData, -} from './types'; - -// tslint:disable: no-bitwise - -export const ERC20_PROXY_ID = '0xf47261b0'; -export const WALLET_SIGNATURE = '0x04'; -export const ONE_ETHER = new BigNumber(1e18); -export const NEGATIVE_INF = new BigNumber('-Infinity'); -export const POSITIVE_INF = new BigNumber('Infinity'); -export const ZERO_AMOUNT = new BigNumber(0); -export const MAX_UINT256 = new BigNumber(2).pow(256).minus(1); -export const ONE_HOUR_IN_SECONDS = 60 * 60; -export const ONE_SECOND_MS = 1000; -export const NULL_BYTES = '0x'; -export const NULL_ADDRESS = '0x0000000000000000000000000000000000000000'; -export const SAMPLER_ADDRESS = '0x5555555555555555555555555555555555555555'; -export const COMPARISON_PRICE_DECIMALS = 10; - -// TODO(kimpers): Consolidate this implementation with the one in @0x/token-metadata -function valueByChainId(rest: Partial<{ [key in ChainId]: T }>, defaultValue: T): { [key in ChainId]: T } { - // TODO I don't like this but iterating through enums is weird - return { - [ChainId.Mainnet]: defaultValue, - [ChainId.Ropsten]: defaultValue, - [ChainId.Rinkeby]: defaultValue, - [ChainId.Goerli]: defaultValue, - [ChainId.Kovan]: defaultValue, - [ChainId.Ganache]: defaultValue, - [ChainId.BSC]: defaultValue, - [ChainId.Polygon]: defaultValue, - [ChainId.PolygonMumbai]: defaultValue, - [ChainId.Avalanche]: defaultValue, - [ChainId.Fantom]: defaultValue, - [ChainId.Celo]: defaultValue, - [ChainId.Optimism]: defaultValue, - [ChainId.ArbitrumRinkeby]: defaultValue, - [ChainId.Arbitrum]: defaultValue, - ...(rest || {}), - }; -} - -/** - * Valid sources for market sell. - */ -export const SELL_SOURCE_FILTER_BY_CHAIN_ID = valueByChainId( - { - [ChainId.Mainnet]: new SourceFilters([ - ERC20BridgeSource.Native, - ERC20BridgeSource.Uniswap, - ERC20BridgeSource.UniswapV2, - ERC20BridgeSource.Curve, - ERC20BridgeSource.Balancer, - ERC20BridgeSource.BalancerV2, - ERC20BridgeSource.Bancor, - ERC20BridgeSource.BancorV3, - ERC20BridgeSource.MStable, - ERC20BridgeSource.SushiSwap, - ERC20BridgeSource.Shell, - ERC20BridgeSource.MultiHop, - ERC20BridgeSource.Dodo, - ERC20BridgeSource.DodoV2, - ERC20BridgeSource.LiquidityProvider, - ERC20BridgeSource.CryptoCom, - ERC20BridgeSource.Lido, - ERC20BridgeSource.MakerPsm, - ERC20BridgeSource.KyberDmm, - ERC20BridgeSource.Component, - ERC20BridgeSource.Saddle, - ERC20BridgeSource.XSigma, - ERC20BridgeSource.UniswapV3, - ERC20BridgeSource.CurveV2, - ERC20BridgeSource.ShibaSwap, - ERC20BridgeSource.Synapse, - ERC20BridgeSource.Synthetix, - // TODO: enable after FQT has been redeployed on Ethereum mainnet - // ERC20BridgeSource.AaveV2, - // ERC20BridgeSource.Compound, - ]), - [ChainId.Ropsten]: new SourceFilters([ - ERC20BridgeSource.Native, - ERC20BridgeSource.SushiSwap, - ERC20BridgeSource.Uniswap, - ERC20BridgeSource.UniswapV2, - ERC20BridgeSource.UniswapV3, - ERC20BridgeSource.Curve, - ]), - [ChainId.Rinkeby]: new SourceFilters([ERC20BridgeSource.Native]), - [ChainId.Kovan]: new SourceFilters([ERC20BridgeSource.Native]), - [ChainId.Goerli]: new SourceFilters([ - ERC20BridgeSource.Native, - ERC20BridgeSource.SushiSwap, - ERC20BridgeSource.Uniswap, - ERC20BridgeSource.UniswapV2, - ERC20BridgeSource.UniswapV3, - ]), - [ChainId.PolygonMumbai]: new SourceFilters([ERC20BridgeSource.Native, ERC20BridgeSource.UniswapV3]), - [ChainId.Ganache]: new SourceFilters([ERC20BridgeSource.Native]), - [ChainId.BSC]: new SourceFilters([ - ERC20BridgeSource.BakerySwap, - ERC20BridgeSource.Belt, - ERC20BridgeSource.Dodo, - ERC20BridgeSource.DodoV2, - ERC20BridgeSource.Ellipsis, - ERC20BridgeSource.Mooniswap, - ERC20BridgeSource.MultiHop, - ERC20BridgeSource.Nerve, - ERC20BridgeSource.Synapse, - ERC20BridgeSource.PancakeSwap, - ERC20BridgeSource.PancakeSwapV2, - ERC20BridgeSource.SushiSwap, - ERC20BridgeSource.ApeSwap, - ERC20BridgeSource.CheeseSwap, - ERC20BridgeSource.LiquidityProvider, - ERC20BridgeSource.WaultSwap, - ERC20BridgeSource.FirebirdOneSwap, - ERC20BridgeSource.ACryptos, - ERC20BridgeSource.KyberDmm, - ERC20BridgeSource.BiSwap, - ERC20BridgeSource.MDex, - ERC20BridgeSource.KnightSwap, - ERC20BridgeSource.WOOFi, - ]), - [ChainId.Polygon]: new SourceFilters([ - ERC20BridgeSource.SushiSwap, - ERC20BridgeSource.QuickSwap, - ERC20BridgeSource.Dfyn, - ERC20BridgeSource.MStable, - ERC20BridgeSource.Curve, - ERC20BridgeSource.DodoV2, - ERC20BridgeSource.Dodo, - ERC20BridgeSource.CurveV2, - ERC20BridgeSource.WaultSwap, - ERC20BridgeSource.ApeSwap, - ERC20BridgeSource.FirebirdOneSwap, - ERC20BridgeSource.BalancerV2, - ERC20BridgeSource.KyberDmm, - ERC20BridgeSource.LiquidityProvider, - ERC20BridgeSource.MultiHop, - ERC20BridgeSource.IronSwap, - ERC20BridgeSource.AaveV2, - ERC20BridgeSource.UniswapV3, - ERC20BridgeSource.Synapse, - ERC20BridgeSource.MeshSwap, - ERC20BridgeSource.WOOFi, - ]), - [ChainId.Avalanche]: new SourceFilters([ - ERC20BridgeSource.MultiHop, - ERC20BridgeSource.Pangolin, - ERC20BridgeSource.TraderJoe, - ERC20BridgeSource.SushiSwap, - ERC20BridgeSource.Curve, - ERC20BridgeSource.CurveV2, - ERC20BridgeSource.KyberDmm, - ERC20BridgeSource.AaveV2, - ERC20BridgeSource.Synapse, - ERC20BridgeSource.GMX, - ERC20BridgeSource.Platypus, - ERC20BridgeSource.WOOFi, - ]), - [ChainId.Fantom]: new SourceFilters([ - ERC20BridgeSource.MultiHop, - ERC20BridgeSource.Beethovenx, - ERC20BridgeSource.Curve, - ERC20BridgeSource.CurveV2, - ERC20BridgeSource.Geist, - ERC20BridgeSource.MorpheusSwap, - ERC20BridgeSource.SpiritSwap, - ERC20BridgeSource.SpookySwap, - ERC20BridgeSource.SushiSwap, - ERC20BridgeSource.Synapse, - ERC20BridgeSource.Yoshi, - ERC20BridgeSource.WOOFi, - ]), - [ChainId.Celo]: new SourceFilters([ - ERC20BridgeSource.UbeSwap, - ERC20BridgeSource.SushiSwap, - ERC20BridgeSource.MultiHop, - ERC20BridgeSource.MobiusMoney, - ]), - [ChainId.Optimism]: new SourceFilters([ - ERC20BridgeSource.UniswapV3, - ERC20BridgeSource.Synapse, - ERC20BridgeSource.Curve, - ERC20BridgeSource.CurveV2, - ERC20BridgeSource.MultiHop, - ERC20BridgeSource.Velodrome, - ERC20BridgeSource.Synthetix, - ]), - [ChainId.ArbitrumRinkeby]: new SourceFilters([ERC20BridgeSource.Native, ERC20BridgeSource.UniswapV3]), - }, - new SourceFilters([]), -); - -/** - * Valid sources for market buy. - */ -export const BUY_SOURCE_FILTER_BY_CHAIN_ID = valueByChainId( - { - [ChainId.Mainnet]: new SourceFilters([ - ERC20BridgeSource.Native, - ERC20BridgeSource.Uniswap, - ERC20BridgeSource.UniswapV2, - ERC20BridgeSource.Curve, - ERC20BridgeSource.Balancer, - ERC20BridgeSource.BalancerV2, - // ERC20BridgeSource.Bancor, // FIXME: Bancor Buys not implemented in Sampler - ERC20BridgeSource.BancorV3, - ERC20BridgeSource.MStable, - ERC20BridgeSource.Shell, - ERC20BridgeSource.SushiSwap, - ERC20BridgeSource.MultiHop, - ERC20BridgeSource.Dodo, - ERC20BridgeSource.DodoV2, - ERC20BridgeSource.Lido, - ERC20BridgeSource.LiquidityProvider, - ERC20BridgeSource.CryptoCom, - ERC20BridgeSource.MakerPsm, - ERC20BridgeSource.KyberDmm, - ERC20BridgeSource.Component, - ERC20BridgeSource.Saddle, - ERC20BridgeSource.XSigma, - ERC20BridgeSource.UniswapV3, - ERC20BridgeSource.CurveV2, - ERC20BridgeSource.ShibaSwap, - ERC20BridgeSource.Synapse, - ERC20BridgeSource.Synthetix, - // TODO: enable after FQT has been redeployed on Ethereum mainnet - // ERC20BridgeSource.AaveV2, - // ERC20BridgeSource.Compound, - ]), - [ChainId.Ropsten]: new SourceFilters([ - ERC20BridgeSource.Native, - ERC20BridgeSource.SushiSwap, - ERC20BridgeSource.Uniswap, - ERC20BridgeSource.UniswapV2, - ERC20BridgeSource.UniswapV3, - ERC20BridgeSource.Curve, - ]), - [ChainId.Goerli]: new SourceFilters([ - ERC20BridgeSource.Native, - ERC20BridgeSource.SushiSwap, - ERC20BridgeSource.Uniswap, - ERC20BridgeSource.UniswapV2, - ERC20BridgeSource.UniswapV3, - ]), - [ChainId.PolygonMumbai]: new SourceFilters([ERC20BridgeSource.Native, ERC20BridgeSource.UniswapV3]), - [ChainId.Rinkeby]: new SourceFilters([ERC20BridgeSource.Native]), - [ChainId.Kovan]: new SourceFilters([ERC20BridgeSource.Native]), - [ChainId.Ganache]: new SourceFilters([ERC20BridgeSource.Native]), - [ChainId.BSC]: new SourceFilters([ - ERC20BridgeSource.BakerySwap, - ERC20BridgeSource.Belt, - ERC20BridgeSource.Dodo, - ERC20BridgeSource.DodoV2, - ERC20BridgeSource.Ellipsis, - ERC20BridgeSource.Mooniswap, - ERC20BridgeSource.MultiHop, - ERC20BridgeSource.Nerve, - ERC20BridgeSource.Synapse, - ERC20BridgeSource.PancakeSwap, - ERC20BridgeSource.PancakeSwapV2, - ERC20BridgeSource.SushiSwap, - ERC20BridgeSource.ApeSwap, - ERC20BridgeSource.CheeseSwap, - ERC20BridgeSource.LiquidityProvider, - ERC20BridgeSource.WaultSwap, - ERC20BridgeSource.FirebirdOneSwap, - ERC20BridgeSource.ACryptos, - ERC20BridgeSource.KyberDmm, - ERC20BridgeSource.BiSwap, - ERC20BridgeSource.MDex, - ERC20BridgeSource.KnightSwap, - ERC20BridgeSource.WOOFi, - ]), - [ChainId.Polygon]: new SourceFilters([ - ERC20BridgeSource.SushiSwap, - ERC20BridgeSource.QuickSwap, - ERC20BridgeSource.Dfyn, - ERC20BridgeSource.MStable, - ERC20BridgeSource.Curve, - ERC20BridgeSource.DodoV2, - ERC20BridgeSource.Dodo, - ERC20BridgeSource.CurveV2, - ERC20BridgeSource.WaultSwap, - ERC20BridgeSource.ApeSwap, - ERC20BridgeSource.FirebirdOneSwap, - ERC20BridgeSource.BalancerV2, - ERC20BridgeSource.KyberDmm, - ERC20BridgeSource.LiquidityProvider, - ERC20BridgeSource.MultiHop, - ERC20BridgeSource.IronSwap, - ERC20BridgeSource.AaveV2, - ERC20BridgeSource.UniswapV3, - ERC20BridgeSource.Synapse, - ERC20BridgeSource.MeshSwap, - ERC20BridgeSource.WOOFi, - ]), - [ChainId.Avalanche]: new SourceFilters([ - ERC20BridgeSource.MultiHop, - ERC20BridgeSource.Pangolin, - ERC20BridgeSource.TraderJoe, - ERC20BridgeSource.SushiSwap, - ERC20BridgeSource.Curve, - ERC20BridgeSource.CurveV2, - ERC20BridgeSource.KyberDmm, - ERC20BridgeSource.AaveV2, - ERC20BridgeSource.Synapse, - ERC20BridgeSource.GMX, - ERC20BridgeSource.Platypus, - ERC20BridgeSource.WOOFi, - ]), - [ChainId.Fantom]: new SourceFilters([ - ERC20BridgeSource.MultiHop, - ERC20BridgeSource.Beethovenx, - ERC20BridgeSource.Curve, - ERC20BridgeSource.CurveV2, - ERC20BridgeSource.Geist, - ERC20BridgeSource.MorpheusSwap, - ERC20BridgeSource.SpiritSwap, - ERC20BridgeSource.SpookySwap, - ERC20BridgeSource.SushiSwap, - ERC20BridgeSource.Synapse, - ERC20BridgeSource.Yoshi, - ERC20BridgeSource.WOOFi, - ]), - [ChainId.Celo]: new SourceFilters([ - ERC20BridgeSource.UbeSwap, - ERC20BridgeSource.SushiSwap, - ERC20BridgeSource.MultiHop, - ERC20BridgeSource.MobiusMoney, - ]), - [ChainId.Optimism]: new SourceFilters([ - ERC20BridgeSource.UniswapV3, - ERC20BridgeSource.Synapse, - ERC20BridgeSource.Curve, - ERC20BridgeSource.CurveV2, - ERC20BridgeSource.MultiHop, - ERC20BridgeSource.Velodrome, - ERC20BridgeSource.Synthetix, - ]), - [ChainId.ArbitrumRinkeby]: new SourceFilters([ERC20BridgeSource.Native, ERC20BridgeSource.UniswapV3]), - }, - new SourceFilters([]), -); - -/** - * 0x Protocol Fee Multiplier - */ -export const PROTOCOL_FEE_MULTIPLIER = new BigNumber(0); - -/** - * Sources to poll for ETH fee price estimates. - */ -export const FEE_QUOTE_SOURCES_BY_CHAIN_ID = valueByChainId( - { - [ChainId.Mainnet]: [ERC20BridgeSource.UniswapV2, ERC20BridgeSource.SushiSwap, ERC20BridgeSource.UniswapV3], - [ChainId.BSC]: [ERC20BridgeSource.PancakeSwap, ERC20BridgeSource.Mooniswap, ERC20BridgeSource.SushiSwap], - [ChainId.Ropsten]: [ERC20BridgeSource.UniswapV2, ERC20BridgeSource.SushiSwap], - [ChainId.Goerli]: [ERC20BridgeSource.UniswapV2, ERC20BridgeSource.SushiSwap], - [ChainId.PolygonMumbai]: [ERC20BridgeSource.UniswapV3], - [ChainId.Polygon]: [ERC20BridgeSource.QuickSwap, ERC20BridgeSource.SushiSwap, ERC20BridgeSource.UniswapV3], - [ChainId.Avalanche]: [ERC20BridgeSource.Pangolin, ERC20BridgeSource.TraderJoe, ERC20BridgeSource.SushiSwap], - [ChainId.Fantom]: [ERC20BridgeSource.SpiritSwap, ERC20BridgeSource.SpookySwap, ERC20BridgeSource.SushiSwap], - [ChainId.Celo]: [ERC20BridgeSource.UbeSwap, ERC20BridgeSource.SushiSwap], - [ChainId.Optimism]: [ERC20BridgeSource.UniswapV3], - [ChainId.ArbitrumRinkeby]: [ERC20BridgeSource.UniswapV3], - }, - [], -); - -// HACK(mzhu25): Limit and RFQ orders need to be treated as different sources -// when computing the exchange proxy gas overhead. -export const SOURCE_FLAGS: { [key in ERC20BridgeSource]: bigint } & { - RfqOrder: bigint; - LimitOrder: bigint; -} = Object.assign( - {}, - ...['RfqOrder', 'LimitOrder', ...Object.values(ERC20BridgeSource)].map((source, index) => ({ - [source]: source === ERC20BridgeSource.Native ? BigInt(0) : BigInt(1) << BigInt(index), - })), -); - -// Mainnet tokens -// Not an exhaustive list, just enough so we don't repeat ourselves -export const MAINNET_TOKENS = { - WETH: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', - // Stable Coins - DAI: '0x6b175474e89094c44da98b954eedeac495271d0f', - USDC: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', - USDT: '0xdac17f958d2ee523a2206206994597c13d831ec7', - sUSD: '0x57ab1ec28d129707052df4df418d58a2d46d5f51', - BUSD: '0x4fabb145d64652a948d72533023f6e7a623c7c53', - TUSD: '0x0000000000085d4780b73119b644ae5ecd22b376', - PAX: '0x8e870d67f660d95d5be530380d0ec0bd388289e1', - GUSD: '0x056fd409e1d7a124bd7017459dfea2f387b6d5cd', - HUSD: '0xdf574c24545e5ffecb9a659c229253d4111d87e1', - mUSD: '0xe2f2a5c287993345a840db3b0845fbc70f5935a5', - USDN: '0x674c6ad92fd080e4004b2312b45f796a192d27a0', - dUSD: '0x5bc25f649fc4e26069ddf4cf4010f9f706c23831', - USDP: '0x1456688345527be1f37e9e627da0837d6f08c925', - // Bitcoins - WBTC: '0x2260fac5e5542a773aa44fbcfedf7c193bc2c599', - RenBTC: '0xeb4c2781e4eba804ce9a9803c67d0893436bb27d', - sBTC: '0xfe18be6b3bd88a2d2a7f928d00292e7a9963cfc6', - tBTC: '0x8daebade922df735c38c80c7ebd708af50815faa', - tBTCv2: '0x18084fbA666a33d37592fA2633fD49a74DD93a88', - hBTC: '0x0316eb71485b0ab14103307bf65a021042c6d380', - pBTC: '0x5228a22e72ccc52d415ecfd199f99d0665e7733b', - bBTC: '0x9be89d2a4cd102d8fecc6bf9da793be995c22541', - oBTC: '0x8064d9ae6cdf087b1bcd5bdf3531bd5d8c537a68', - // aTokens (Aave) - aDAI: '0x028171bca77440897b824ca71d1c56cac55b68a3', - aUSDC: '0xbcca60bb61934080951369a648fb03df4f96263c', - aUSDT: '0x3ed3b47dd13ec9a98b44e6204a523e766b225811', - aSUSD: '0x6c5024cd4f8a59110119c56f8933403a539555eb', - // Other - MKR: '0x9f8f72aa9304c8b593d555f12ef6589cc3a579a2', - EURS: '0xdb25f211ab05b1c97d595516f45794528a807ad8', - sEUR: '0xd71ecff9342a5ced620049e616c5035f1db98620', - sETH: '0x5e74c9036fb86bd7ecdcb084a0673efc32ea31cb', - sJPY: '0xf6b1c627e95bfc3c1b4c9b825a032ff0fbf3e07d', - sGBP: '0x97fe22e7341a0cd8db6f6c021a24dc8f4dad855f', - sAUD: '0xf48e200eaf9906362bb1442fca31e0835773b8b4', - sKRW: '0x269895a3df4d73b077fc823dd6da1b95f72aaf9b', - sCHF: '0x0f83287ff768d1c1e17a42f44d644d7f22e8ee1d', - stETH: '0xae7ab96520de3a18e5e111b5eaab095312d7fe84', - wstETH: '0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0', - LINK: '0x514910771af9ca656af840dff83e8264ecf986ca', - MANA: '0x0f5d2fb29fb7d3cfee444a200298f468908cc942', - KNC: '0xdefa4e8a7bcba345f687a2f1456f5edd9ce97202', - AAVE: '0x7fc66500c84a76ad7e9c93437bfc5ac33e2ddae9', - sLINK: '0xbbc455cb4f1b9e4bfc4b73970d360c8f032efee6', - yUSD: '0x5dbcf33d8c2e976c6b560249878e6f1491bca25c', - ybCRV: '0x2994529c0652d127b7842094103715ec5299bbed', - yCRV: '0xdf5e0e81dff6faf3a7e52ba697820c5e32d806a8', - bCRV: '0x3b3ac5386837dc563660fb6a0937dfaa5924333b', - yDAI: '0xacd43e627e64355f1861cec6d3a6688b31a6f952', - yUSDC: '0x597ad1e0c13bfe8025993d9e79c69e1c0233522e', - yUSDT: '0x2f08119c6f07c006695e079aafc638b8789faf18', - yTUSD: '0x37d19d1c4e1fa9dc47bd1ea12f742a0887eda74a', - crETH: '0xcbc1065255cbc3ab41a6868c22d1f1c573ab89fd', - ankrETH: '0xe95a203b1a91a908f9b9ce46459d101078c2c3cb', - vETH: '0x898bad2774eb97cf6b94605677f43b41871410b1', - alETH: '0x0100546f2cd4c9d97f798ffc9755e47865ff7ee6', - HT: '0x6f259637dcD74C767781E37Bc6133cd6A68aa161', - UST: '0xa47c8bf37f92abed4a126bda807a7b7498661acd', - // StableSwap "open pools" (crv.finance) - STABLEx: '0xcd91538b91b4ba7797d39a2f66e63810b50a33d0', - alUSD: '0xbc6da0fe9ad5f3b0d58160288917aa56653660e9', - // Frax ecosystem - FRAX: '0x853d955acef822db058eb8505911ed77f175b99e', - cvxFXS: '0xfeef77d3f69374f66429c91d732a244f074bdf74', - FXS: '0x3432b6a60d23ca0dfca7761b7ab56459d9c964d0', - OHM: '0x383518188c0c6d7730d91b2c03a03c837814a899', - OHMV2: '0x64aa3364f17a4d01c6f1751fd97c2bd3d7e7f1d5', - BTRFLY: '0xc0d4ceb216b3ba9c3701b291766fdcba977cec3a', - // Stargate - STG: '0xaf5191b0de278c7286d6c7cc6ab6bb8a73ba2cd6', - // - LUSD: '0x5f98805a4e8be255a32880fdec7f6728c6568ba0', - // Fei Ecosystem - FEI: '0x956f47f50a910163d8bf957cf5846d573e7f87ca', - TRIBE: '0xc7283b66eb1eb5fb86327f08e1b5816b0720212b', - // - DSU: '0x605d26fbd5be761089281d5cec2ce86eea667109', - ESS: '0x24ae124c4cc33d6791f8e8b63520ed7107ac8b3e', - cvxCRV: '0x62b9c7356a2dc64a1969e19c23e4f579f9810aa7', - CRV: '0xd533a949740bb3306d119cc777fa900ba034cd52', - MIM: '0x99d8a9c45b2eca8864373a26d1459e3dff1e17f3', - EURT: '0xc581b735a1688071a1746c968e0798d642ede491', - // Synapse ecosystem - nUSD: '0x1b84765de8b7566e4ceaf4d0fd3c5af52d3dde4f', - CVX: '0x4e3fbd56cd56c3e72c1403e103b45db9da5b9d2b', - UST_WORMHOLE: '0xa693b19d2931d498c5b318df961919bb4aee87a5', - RAI: '0x03ab458634910aad20ef5f1c8ee96f1d6ac54919', - DOLA: '0x865377367054516e17014ccded1e7d814edc9ce4', - OUSD: '0x2a8e1e676ec238d8a992307b495b45b3feaa5e86', - agEUR: '0x1a7e4e63778b4f12a199c062f3efdd288afcbce8', - ibEUR: '0x96e61422b6a9ba0e068b6c5add4ffabc6a4aae27', - YFI: '0x0bc529c00c6401aef6d220be8c6ea1667f6ad93e', -}; - -export const BSC_TOKENS = { - WBNB: '0xbb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c', - BUSD: '0xe9e7cea3dedca5984780bafc599bd69add087d56', - USDT: '0x55d398326f99059ff775485246999027b3197955', - USDC: '0x8ac76a51cc950d9822d68b83fe1ad97b32cd580d', - DAI: '0x1af3f329e8be154074d8769d1ffa4ee058b1dbc3', - PAX: '0xb7f8cd00c5a06c0537e2abff0b58033d02e5e094', - UST: '0x23396cf899ca06c4472205fc903bdb4de249d6fc', - VAI: '0x4bd17003473389a42daf6a0a729f6fdb328bbbd7', - WEX: '0xa9c41a46a6b3531d28d5c32f6633dd2ff05dfb90', - WETH: '0x2170ed0880ac9a755fd29b2688956bd959f933f8', - BTCB: '0x7130d2a12b9bcbfae4f2634d864a1ee1ce3ead9c', - renBTC: '0xfce146bf3146100cfe5db4129cf6c82b0ef4ad8c', - pBTC: '0xed28a457a5a76596ac48d87c0f577020f6ea1c4c', - nUSD: '0x23b891e5c62e0955ae2bd185990103928ab817b3', - BSW: '0x965F527D9159dCe6288a2219DB51fc6Eef120dD1', - WOO: '0x4691937a7508860f876c9c0a2a617e7d9e945d4b', -}; - -export const POLYGON_TOKENS = { - DAI: '0x8f3cf7ad23cd3cadbd9735aff958023239c6a063', - USDC: '0x2791bca1f2de4661ed88a30c99a7a9449aa84174', - USDT: '0xc2132d05d31c914a87c6611c10748aeb04b58e8f', - amDAI: '0x27f8d03b3a2196956ed754badc28d73be8830a6e', - amUSDC: '0x1a13f4ca1d028320a707d99520abfefca3998b7f', - amUSDT: '0x60d55f02a771d515e077c9c2403a1ef324885cec', - WBTC: '0x1bfd67037b42cf73acf2047067bd4f2c47d9bfd6', - WMATIC: '0x0d500b1d8e8ef31e21c99d1db9a6444d3adf1270', - WETH: '0x7ceb23fd6bc0add59e62ac25578270cff1b9f619', - renBTC: '0xdbf31df14b66535af65aac99c32e9ea844e14501', - QUICK: '0x831753dd7087cac61ab5644b308642cc1c33dc13', - DFYN: '0xc168e40227e4ebd8c1cae80f7a55a4f0e6d66c97', - BANANA: '0x5d47baba0d66083c52009271faf3f50dcc01023c', - WEXPOLY: '0x4c4bf319237d98a30a929a96112effa8da3510eb', - nUSD: '0xb6c473756050de474286bed418b77aeac39b02af', - ANY: '0x6aB6d61428fde76768D7b45D8BFeec19c6eF91A8', - WOO: '0x1b815d120b3ef02039ee11dc2d33de7aa4a8c603', -}; - -export const AVALANCHE_TOKENS = { - WAVAX: '0xb31f66aa3c1e785363f0875a1b74e27b85fd66c7', - WETH: '0x49d5c2bdffac6ce2bfdb6640f4f80f226bc10bab', - WBTC: '0x50b7545627a5162f82a992c33b87adc75187b218', - DAI: '0xd586e7f844cea2f87f50152665bcbc2c279d8d70', - // bridged USDC - USDC: '0xa7d7079b0fead91f3e65f86e8915cb59c1a4c664', - // native USDC on Avalanche usdc.e - nUSDC: '0xb97ef9ef8734c71904d8002f8b6bc66dd9c48a6e', - // usdt.e - USDt: '0x9702230a8ea53601f5cd2dc00fdbc13d4df4a8c7', - USDT: '0xc7198437980c041c805a1edcba50c1ce5db95118', - aDAI: '0x47afa96cdc9fab46904a55a6ad4bf6660b53c38a', - aUSDC: '0x46a51127c3ce23fb7ab1de06226147f446e4a857', - aUSDT: '0x532e6537fea298397212f09a61e03311686f548e', - nETH: '0x19e1ae0ee35c0404f835521146206595d37981ae', - nUSD: '0xcfc37a6ab183dd4aed08c204d1c2773c0b1bdf46', - aWETH: '0x53f7c5869a859f0aec3d334ee8b4cf01e3492f21', - MIM: '0x130966628846bfd36ff31a822705796e8cb8c18d', - MAG: '0x1d60109178C48E4A937D8AB71699D8eBb6F7c5dE', - sAVAX: '0x2b2c81e08f1af8835a78bb2a90ae924ace0ea4be', - UST: '0xb599c3590f42f8f995ecfa0f85d2980b76862fc1', - FRAX: '0xd24c2ad096400b6fbcd2ad8b24e7acbc21a1da64', - YUSD: '0x111111111111ed1d73f860f57b2798b683f2d325', - WOO: '0xabc9547b534519ff73921b1fba6e672b5f58d083', -}; - -export const CELO_TOKENS = { - WCELO: '0x471ece3750da237f93b8e339c536989b8978a438', - // Some of these tokens are Optics bridge? tokens which - // had an issue and migrated from v1 to v2 - WETHv1: '0xe919f65739c26a42616b7b8eedc6b5524d1e3ac4', - oWETH: '0x122013fd7df1c6f636a5bb8f03108e876548b455', - WBTC: '0xbaab46e28388d2779e6e31fd00cf0e5ad95e327b', - cUSD: '0x765de816845861e75a25fca122bb6898b8b1282a', - // ?? - cBTC: '0xd629eb00deced2a080b7ec630ef6ac117e614f1b', - cETH: '0x2def4285787d58a2f811af24755a8150622f4361', - UBE: '0x00be915b9dcf56a3cbe739d9b9c202ca692409ec', - // Moolah - mCELO: '0x7d00cd74ff385c955ea3d79e47bf06bd7386387d', - mCUSD: '0x918146359264c492bd6934071c6bd31c854edbc3', - mCEUR: '0xe273ad7ee11dcfaa87383ad5977ee1504ac07568', - amCUSD: '0x64defa3544c695db8c535d289d843a189aa26b98', - MOO: '0x17700282592d6917f6a73d0bf8accf4d578c131e', - - // - wBTC: '0x2260fac5e5542a773aa44fbcfedf7c193bc2c599', - wETH: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', - wBTCO: '0xbe50a3013a1c94768a1abb78c3cb79ab28fc1ace', - pUSDC: '0xcc82628f6a8defa1e2b0ad7ed448bef3647f7941', - cUSDC: '0x2a3684e9dc20b857375ea04235f2f7edbe818fa7', - cUSDC_V2: '0xef4229c8c3250c675f21bcefa42f58efbff6002a', - pUSDC_V2: '0x1bfc26ce035c368503fae319cc2596716428ca44', - pUSD: '0xeadf4a7168a82d30ba0619e64d5bcf5b30b45226', - pCELO: '0x301a61d01a63c8d670c2b8a43f37d12ef181f997', - aaUSDC: '0xb70e0a782b058bfdb0d109a3599bec1f19328e36', - asUSDC: '0xcd7d7ff64746c1909e44db8e95331f9316478817', - mcUSDT: '0xcfffe0c89a779c09df3df5624f54cdf7ef5fdd5d', - mcUSDC: '0x93db49be12b864019da9cb147ba75cdc0506190e', - DAI: '0x90ca507a5d4458a4c6c6249d186b6dcb02a5bccd', -}; - -export const FANTOM_TOKENS = { - WFTM: '0x21be370d5312f44cb42ce377bc9b8a0cef1a4c83', - WETH: '0x74b23882a30290451a17c44f4f05243b6b58c76d', - USDC: '0x04068da6c83afcfa0e13ba15a6696662335d5b75', - DAI: '0x8d11ec38a3eb5e956b052f67da8bdc9bef8abf3e', - fUSDT: '0x049d68029688eabf473097a2fc38ef61633a3c7a', - WBTC: '0x321162cd933e2be498cd2267a90534a804051b11', - WCRV: '0x1e4f97b9f9f913c46f1632781732927b9019c68b', - renBTC: '0xdbf31df14b66535af65aac99c32e9ea844e14501', - MIM: '0x82f0b8b456c1a451378467398982d4834b6829c1', - nUSD: '0xed2a7edd7413021d440b09d654f3b87712abab66', - nETH: '0x67c10c397dd0ba417329543c1a40eb48aaa7cd00', - gfUSDT: '0x940f41f0ec9ba1a34cf001cc03347ac092f5f6b5', - gUSDC: '0xe578c856933d8e1082740bf7661e379aa2a30b26', - gDAI: '0x07e6332dd090d287d3489245038daf987955dcfb', - FRAX: '0xdc301622e621166bd8e82f2ca0a26c13ad0be355', - gFTM: '0x39b3bd37208cbade74d0fcbdbb12d606295b430a', - gETH: '0x25c130b2624cf12a4ea30143ef50c5d68cefa22f', - gWBTC: '0x38aca5484b8603373acc6961ecd57a6a594510a3', - gCRV: '0x690754a168b022331caa2467207c61919b3f8a98', - gMIM: '0xc664fc7b8487a3e10824cda768c1d239f2403bbe', - WOO: '0x6626c47c00f1d87902fc13eecfac3ed06d5e8d8a', -}; - -export const OPTIMISM_TOKENS = { - WETH: '0x4200000000000000000000000000000000000006', - USDC: '0x7f5c764cbc14f9669b88837ca1490cca17c31607', - USDT: '0x94b008aa00579c1307b0ef2c499ad98a8ce58e58', - DAI: '0xda10009cbd5d07dd0cecc66161fc93d7c9000da1', - WBTC: '0x68f180fcce6836688e9084f035309e29bf0a2095', - nETH: '0x809dc529f07651bd43a172e8db6f4a7a0d771036', - sWETH: '0x121ab82b49b2bc4c7901ca46b8277962b4350204', - // Synthetix synths: - sAAVE: '0x00b8d5a5e1ac97cb4341c4bc4367443c8776e8d9', - sAVAX: '0xb2b42b231c68cbb0b4bf2ffebf57782fd97d3da4', - sBTC: '0x298b9b95708152ff6968aafd889c6586e9169f1d', - sETH: '0xe405de8f52ba7559f9df3c368500b6e6ae6cee49', - sEUR: '0xfbc4198702e81ae77c06d58f81b629bdf36f0a71', - sLINK: '0xc5db22719a06418028a40a9b5e9a7c02959d0d08', - sMATIC: '0x81ddfac111913d3d5218dea999216323b7cd6356', - sSOL: '0x8b2f7ae8ca8ee8428b6d76de88326bb413db2766', - sUNI: '0xf5a6115aa582fd1beea22bc93b7dc7a785f60d03', - sUSD: '0x8c6f28f2f1a3c87f0f938b96d27520d9751ec8d9', -}; - -export const GEIST_FANTOM_POOLS = { - lendingPool: '0x9fad24f572045c7869117160a571b2e50b10d068', -}; - -export const CURVE_POOLS = { - compound: '0xa2b47e3d5c44877cca798226b7b8118f9bfb7a56', // 0.Compound - // 1.USDT is dead - PAX: '0x06364f10b501e868329afbc005b3492902d6c763', // 2.PAX - // 3.y is dead - // 3.bUSD is dead - sUSD: '0xa5407eae9ba41422680e2e00537571bcc53efbfd', // 5.sUSD - renBTC: '0x93054188d876f558f4a66b2ef1d97d16edf0895b', // 6.ren - sBTC: '0x7fc77b5c7614e1533320ea6ddc2eb61fa00a9714', // 7.sbtc - HBTC: '0x4ca9b3063ec5866a4b82e437059d2c43d1be596f', // 8.hbtc - TRI: '0xbebc44782c7db0a1a60cb6fe97d0b483032ff1c7', // 9.3pool - GUSD: '0x4f062658eaaf2c1ccf8c8e36d6824cdf41167956', // 10.gusd - HUSD: '0x3ef6a01a0f81d6046290f3e2a8c5b843e738e604', // 11.husd - // 12.usdk is dead - USDN: '0x0f9cb53ebe405d49a0bbdbd291a65ff571bc83e1', // 13.usdn - // 14.linkusd is dead - mUSD: '0x8474ddbe98f5aa3179b3b3f5942d724afcdec9f6', // 15.musd - // 16.rsv is dead - dUSD: '0x8038c01a0390a8c547446a0b2c18fc9aefecc10c', // 17.dusd - tBTC: '0xc25099792e9349c7dd09759744ea681c7de2cb66', // 18.tbtc - pBTC: '0x7f55dde206dbad629c080068923b36fe9d6bdbef', // 19.pbtc - bBTC: '0x071c661b4deefb59e2a3ddb20db036821eee8f4b', // 20.bbtc - oBTC: '0xd81da8d904b52208541bade1bd6595d8a251f8dd', // 21.obtc - UST: '0x890f4e345b1daed0367a877a1612f86a1f86985f', // 22.ust - eurs: '0x0ce6a5ff5217e38315f87032cf90686c96627caa', // 23.eurs - seth: '0xc5424b857f758e906013f3555dad202e4bdb4567', // 24.seth - aave: '0xdebf20617708857ebe4f679508e7b7863a8a8eee', // 25.aave - steth: '0xdc24316b9ae028f1497c275eb9192a3ea0f67022', // 26.stETH - saave: '0xeb16ae0052ed37f479f7fe63849198df1765a733', // saave - ankreth: '0xa96a65c051bf88b4095ee1f2451c2a9d43f53ae2', // ankreth - USDP: '0x42d7025938bec20b69cbae5a77421082407f053a', // usdp - ib: '0x2dded6da1bf5dbdf597c45fcfaa3194e53ecfeaf', // iron bank - link: '0xf178c0b5bb7e7abf4e12a4838c7b7c5ba2c623c0', // link - btrflyweth: '0xf43b15ab692fde1f9c24a9fce700adcc809d5391', // redacted cartel - stgusdc: '0x3211c6cbef1429da3d0d58494938299c92ad5860', // stargate - // StableSwap "open pools" (crv.finance) - TUSD: '0xecd5e75afb02efa118af914515d6521aabd189f1', - STABLEx: '0x3252efd4ea2d6c78091a1f43982ee2c3659cc3d1', - alUSD: '0x43b4fdfd4ff969587185cdb6f0bd875c5fc83f8c', - FRAX: '0xd632f22692fac7611d2aa1c0d552930d43caed3b', - LUSD: '0xed279fdd11ca84beef15af5d39bb4d4bee23f0ca', - BUSD: '0x4807862aa8b2bf68830e4c8dc86d0e9a998e085a', - DSU3CRV: '0x6ec80df362d7042c50d4469bcfbc174c9dd9109a', - cvxcrv: '0x9d0464996170c6b9e75eed71c68b99ddedf279e8', - cvxfxs: '0xd658a338613198204dca1143ac3f01a722b5d94a', - mim: '0x5a6a4d54456819380173272a5e8e9b9904bdf41b', - eurt: '0xfd5db7463a3ab53fd211b4af195c5bccc1a03890', - ethcrv: '0x8301ae4fc9c624d1d396cbdaa1ed877821d7c511', - ethcvx: '0xb576491f1e6e5e62f1d8f26062ee822b40b0e0d4', - fei_tri: '0x06cb22615ba53e60d67bf6c341a0fd5e718e1655', - rai_tri: '0x618788357d0ebd8a37e763adab3bc575d54c2c7d', - DOLA_tri: '0xaa5a67c256e27a5d80712c51971408db3370927d', - OUSD_tri: '0x87650d7bbfc3a9f10587d7778206671719d9910d', - d3pool: '0xbaaa1f5dba42c3389bdbc2c9d2de134f5cd0dc89', - triEURpool: '0xb9446c4ef5ebe66268da6700d26f96273de3d571', - ibEURsEUR: '0x19b080fe1ffa0553469d20ca36219f17fcf03859', - wethyfi: '0xc26b89a667578ec7b3f11b2f98d6fd15c07c54ba', -}; - -export const CURVE_V2_POOLS = { - tricrypto: '0x80466c64868e1ab14a1ddf27a676c3fcbe638fe5', - tricrypto2: '0xd51a44d3fae010294c616388b506acda1bfaae46', -}; - -export const CURVE_POLYGON_POOLS = { - aave: '0x445fe580ef8d70ff569ab36e80c647af338db351', - ren: '0xc2d95eef97ec6c17551d45e77b590dc1f9117c67', -}; - -export const CURVE_V2_POLYGON_POOLS = { - atricrypto3: '0x1d8b86e3d88cdb2d34688e87e72f388cb541b7c8', -}; - -export const CURVE_AVALANCHE_POOLS = { - aave: '0x7f90122bf0700f9e7e1f688fe926940e8839f353', - mim: '0xaea2e71b631fa93683bcf256a8689dfa0e094fcd', - USDC: '0x3a43a5851a3e3e0e25a3c1089670269786be1577', -}; - -export const CURVE_V2_AVALANCHE_POOLS = { - atricrypto: '0x58e57ca18b7a47112b877e31929798cd3d703b0f', -}; - -export const CURVE_FANTOM_POOLS = { - fUSDT: '0x92d5ebf3593a92888c25c0abef126583d4b5312e', - twoPool: '0x27e611fd27b276acbd5ffd632e5eaebec9761e40', - ren: '0x3ef6a01a0f81d6046290f3e2a8c5b843e738e604', - tri_v2: '0x2dd7c9371965472e5a5fd28fbe165007c61439e1', - geist: '0x0fa949783947bf6c1b171db13aeacbb488845b3f', - FRAX_twoPool: '0x7a656b342e14f745e2b164890e88017e27ae7320', -}; - -export const CURVE_V2_FANTOM_POOLS = { - tricrypto: '0x3a1659ddcf2339be3aea159ca010979fb49155ff', -}; - -export const CURVE_OPTIMISM_POOLS = { - tri: '0x1337bedc9d22ecbe766df105c9623922a27963ec', -}; - -export const SADDLE_POOLS = { - stablesV2: '0xaCb83E0633d6605c5001e2Ab59EF3C745547C8C7', - bitcoinsV2: '0xdf3309771d2BF82cb2B6C56F9f5365C8bD97c4f2', - alETH: '0xa6018520eaacc06c30ff2e1b3ee2c7c22e64196a', - d4: '0xc69ddcd4dfef25d8a793241834d4cc4b3668ead6', -}; - -export const IRONSWAP_POOLS = { - is3usd: '0x837503e8a8753ae17fb8c8151b8e6f586defcb57', -}; - -export const NERVE_POOLS = { - threePool: '0x1b3771a66ee31180906972580ade9b81afc5fcdc', -}; - -export const SYNAPSE_MAINNET_POOLS = { - nUSDLP: '0x1116898dda4015ed8ddefb84b6e8bc24528af2d8', -}; - -export const SYNAPSE_OPTIMISM_POOLS = { - nETHLP: '0xe27bff97ce92c3e1ff7aa9f86781fdd6d48f5ee9', -}; - -export const SYNAPSE_BSC_POOLS = { - nUSDLP: '0x28ec0b36f0819ecb5005cab836f4ed5a2eca4d13', -}; - -export const SYNAPSE_POLYGON_POOLS = { - nUSDLP: '0x85fcd7dd0a1e1a9fcd5fd886ed522de8221c3ee5', -}; - -export const SYNAPSE_FANTOM_POOLS = { - nUSDLP: '0x2913e812cf0dcca30fb28e6cac3d2dcff4497688', - nETHLP: '0x8d9ba570d6cb60c7e3e0f31343efe75ab8e65fb1', -}; - -export const SYNAPSE_AVALANCHE_POOLS = { - nUSDLP: '0xed2a7edd7413021d440b09d654f3b87712abab66', - nETHLP: '0x77a7e60555bc18b4be44c181b2575eee46212d44', -}; - -export const SYNAPSE_ARBITRUM_POOLS = { - nUSDLP: '0x0db3fe3b770c95a0b99d1ed6f2627933466c0dd8', - nETHLP: '0xd70a52248e546a3b260849386410c7170c7bd1e9', -}; - -export const BELT_POOLS = { - vPool: '0xf16d312d119c13dd27fd0dc814b0bcdcaaa62dfd', -}; - -export const ELLIPSIS_POOLS = { - threePool: '0x160caed03795365f3a589f10c379ffa7d75d4e76', -}; - -export const XSIGMA_POOLS = { - stable: '0x3333333ACdEdBbC9Ad7bda0876e60714195681c5', -}; - -export const FIREBIRDONESWAP_BSC_POOLS = { - oneswap: '0x01c9475dbd36e46d1961572c8de24b74616bae9e', -}; - -export const FIREBIRDONESWAP_POLYGON_POOLS = { - oneswap: '0x01c9475dbd36e46d1961572c8de24b74616bae9e', -}; -export const MOBIUSMONEY_CELO_POOLS = { - usdc_optics_v2: '0x9906589ea8fd27504974b7e8201df5bbde986b03', - dai_optics_v2: '0xf3f65dfe0c8c8f2986da0fec159abe6fd4e700b4', - weth_optics_v2: '0x74ef28d635c6c5800dd3cd62d4c4f8752daacb09', - pusdc_optics_v2: '0xcce0d62ce14fb3e4363eb92db37ff3630836c252', - usdc_allbridge_solana: '0x63c1914bf00a9b395a2bf89aada55a5615a3656e', - usdc_poly_optics: '0xa2f0e57d4ceacf025e81c76f28b9ad6e9fbe8735', -}; - -export const ACRYPTOS_POOLS = { - acs4usd: '0xb3f0c9ea1f05e312093fdb031e789a756659b0ac', - acs4vai: '0x191409d5a4effe25b0f4240557ba2192d18a191e', - acs4ust: '0x99c92765efc472a9709ced86310d64c4573c4b77', - acs3btc: '0xbe7caa236544d1b9a0e7f91e94b9f5bfd3b5ca81', -}; - -export const PLATYPUS_AVALANCHE_POOLS = { - usd: '0x66357dcace80431aee0a7507e2e361b7e2402370', - yusd: '0xc828d995c686aaba78a4ac89dfc8ec0ff4c5be83', - frax: '0xb8e567fc23c39c94a1f6359509d7b43d1fbed824', - mim: '0x30c30d826be87cd0a4b90855c2f38f7fcfe4eaa7', - sAVAX: '0x4658ea7e9960d6158a261104aaa160cc953bb6ba', -}; - -export const WOOFI_POOL_BY_CHAIN_ID = valueByChainId( - { - [ChainId.BSC]: '0xbf365ce9cfcb2d5855521985e351ba3bcf77fd3f', - [ChainId.Fantom]: '0x9503e7517d3c5bc4f9e4a1c6ae4f8b33ac2546f2', - [ChainId.Avalanche]: '0x1df3009c57a8b143c6246149f00b090bce3b8f88', - [ChainId.Polygon]: '0x7400b665c8f4f3a951a99f1ee9872efb8778723d', - }, - NULL_ADDRESS, -); - -export const WOOFI_SUPPORTED_TOKENS = new Set([ - BSC_TOKENS.USDT, - BSC_TOKENS.WBNB, - BSC_TOKENS.WOO, - BSC_TOKENS.WETH, - BSC_TOKENS.BTCB, - AVALANCHE_TOKENS.nUSDC, - AVALANCHE_TOKENS.WAVAX, - AVALANCHE_TOKENS.WBTC, - AVALANCHE_TOKENS.WETH, - AVALANCHE_TOKENS.WOO, - FANTOM_TOKENS.USDC, - FANTOM_TOKENS.WFTM, - FANTOM_TOKENS.WETH, - FANTOM_TOKENS.WBTC, - FANTOM_TOKENS.WOO, - POLYGON_TOKENS.USDC, - POLYGON_TOKENS.WMATIC, - POLYGON_TOKENS.WBTC, - POLYGON_TOKENS.WETH, - POLYGON_TOKENS.WOO, -]); - -export const DEFAULT_INTERMEDIATE_TOKENS_BY_CHAIN_ID = valueByChainId( - { - [ChainId.Mainnet]: [ - MAINNET_TOKENS.WETH, - MAINNET_TOKENS.USDT, - MAINNET_TOKENS.DAI, - MAINNET_TOKENS.USDC, - MAINNET_TOKENS.WBTC, - ], - [ChainId.BSC]: [ - BSC_TOKENS.WBNB, - BSC_TOKENS.BUSD, - BSC_TOKENS.DAI, - BSC_TOKENS.USDC, - BSC_TOKENS.WETH, - BSC_TOKENS.USDT, - BSC_TOKENS.WEX, - ], - [ChainId.Ropsten]: [ - getContractAddressesForChainOrThrow(ChainId.Ropsten).etherToken, - '0xad6d458402f60fd3bd25163575031acdce07538d', // DAI - '0x07865c6e87b9f70255377e024ace6630c1eaa37f', // USDC - ], - [ChainId.Goerli]: [ - getContractAddressesForChainOrThrow(ChainId.Goerli).etherToken, - '0x11fE4B6AE13d2a6055C8D9cF65c55bac32B5d844', // DAI - '0x07865c6E87B9F70255377e024ace6630C1Eaa37F', // USDC - ], - [ChainId.ArbitrumRinkeby]: [ - getContractAddressesForChainOrThrow(ChainId.ArbitrumRinkeby).etherToken, - '0x237b3B5238D2022aA80cAd1f67dAE53f353F74bF', // USDT - '0xF61Cffd6071a8DB7cD5E8DF1D3A5450D9903cF1c', // USDC - ], - [ChainId.PolygonMumbai]: [ - getContractAddressesForChainOrThrow(ChainId.PolygonMumbai).etherToken, - '0xe6b8a5CF854791412c1f6EFC7CAf629f5Df1c747', // USDC - ], - [ChainId.Polygon]: [ - POLYGON_TOKENS.WMATIC, - POLYGON_TOKENS.WETH, - POLYGON_TOKENS.USDC, - POLYGON_TOKENS.DAI, - POLYGON_TOKENS.USDT, - POLYGON_TOKENS.WBTC, - ], - [ChainId.Avalanche]: [ - AVALANCHE_TOKENS.WAVAX, - AVALANCHE_TOKENS.WETH, - AVALANCHE_TOKENS.DAI, - AVALANCHE_TOKENS.USDT, - AVALANCHE_TOKENS.USDC, - AVALANCHE_TOKENS.nUSD, - AVALANCHE_TOKENS.nETH, - AVALANCHE_TOKENS.aWETH, - AVALANCHE_TOKENS.MIM, - ], - [ChainId.Fantom]: [ - FANTOM_TOKENS.WFTM, - FANTOM_TOKENS.WETH, - FANTOM_TOKENS.DAI, - FANTOM_TOKENS.USDC, - FANTOM_TOKENS.nUSD, - FANTOM_TOKENS.nETH, - FANTOM_TOKENS.MIM, - ], - [ChainId.Celo]: [ - CELO_TOKENS.WCELO, - CELO_TOKENS.mCUSD, - CELO_TOKENS.WETHv1, - CELO_TOKENS.amCUSD, - CELO_TOKENS.WBTC, - ], - [ChainId.Optimism]: [ - OPTIMISM_TOKENS.WETH, - OPTIMISM_TOKENS.DAI, - OPTIMISM_TOKENS.USDC, - OPTIMISM_TOKENS.USDT, - OPTIMISM_TOKENS.nETH, - OPTIMISM_TOKENS.sWETH, - ], - }, - [], -); - -// Note be careful here as a UNION is performed when finding intermediary tokens -// attaching to a default intermediary token (stables or ETH etc) can have a large impact -export const DEFAULT_TOKEN_ADJACENCY_GRAPH_BY_CHAIN_ID = valueByChainId( - { - [ChainId.Mainnet]: new TokenAdjacencyGraphBuilder(DEFAULT_INTERMEDIATE_TOKENS_BY_CHAIN_ID[ChainId.Mainnet]) - .tap(builder => { - // Convex and Curve - builder.addBidirectional(MAINNET_TOKENS.cvxCRV, MAINNET_TOKENS.CRV); - // Convex and FXS - builder.addBidirectional(MAINNET_TOKENS.cvxFXS, MAINNET_TOKENS.FXS); - // FEI TRIBE liquid in UniV2 - builder.addBidirectional(MAINNET_TOKENS.FEI, MAINNET_TOKENS.TRIBE); - // FRAX ecosystem - builder.addBidirectional(MAINNET_TOKENS.FRAX, MAINNET_TOKENS.FXS); - builder.addBidirectional(MAINNET_TOKENS.FRAX, MAINNET_TOKENS.OHM); - // REDACTED CARTEL - builder.addBidirectional(MAINNET_TOKENS.OHMV2, MAINNET_TOKENS.BTRFLY); - // Lido - builder.addBidirectional(MAINNET_TOKENS.stETH, MAINNET_TOKENS.wstETH); - // Synthetix Atomic Swap - builder.addCompleteSubgraph([ - MAINNET_TOKENS.sBTC, - MAINNET_TOKENS.sETH, - MAINNET_TOKENS.sUSD, - MAINNET_TOKENS.sEUR, - MAINNET_TOKENS.sJPY, - MAINNET_TOKENS.sGBP, - MAINNET_TOKENS.sAUD, - MAINNET_TOKENS.sKRW, - MAINNET_TOKENS.sCHF, - ]); - }) - .build(), - [ChainId.BSC]: new TokenAdjacencyGraphBuilder(DEFAULT_INTERMEDIATE_TOKENS_BY_CHAIN_ID[ChainId.BSC]).build(), - [ChainId.Polygon]: new TokenAdjacencyGraphBuilder(DEFAULT_INTERMEDIATE_TOKENS_BY_CHAIN_ID[ChainId.Polygon]) - .tap(builder => { - builder.addBidirectional(POLYGON_TOKENS.QUICK, POLYGON_TOKENS.ANY); - }) - .build(), - [ChainId.Avalanche]: new TokenAdjacencyGraphBuilder(DEFAULT_INTERMEDIATE_TOKENS_BY_CHAIN_ID[ChainId.Avalanche]) - .tap(builder => { - // Synapse nETH/aWETH pool - builder.addBidirectional(AVALANCHE_TOKENS.aWETH, AVALANCHE_TOKENS.nETH); - // Trader Joe MAG/MIM pool - builder.addBidirectional(AVALANCHE_TOKENS.MIM, AVALANCHE_TOKENS.MAG); - }) - .build(), - [ChainId.Fantom]: new TokenAdjacencyGraphBuilder( - DEFAULT_INTERMEDIATE_TOKENS_BY_CHAIN_ID[ChainId.Fantom], - ).build(), - [ChainId.Celo]: new TokenAdjacencyGraphBuilder(DEFAULT_INTERMEDIATE_TOKENS_BY_CHAIN_ID[ChainId.Celo]).build(), - [ChainId.Optimism]: new TokenAdjacencyGraphBuilder(DEFAULT_INTERMEDIATE_TOKENS_BY_CHAIN_ID[ChainId.Optimism]) - .tap(builder => { - // Synthetix Atomic Swap - builder.addCompleteSubgraph([ - OPTIMISM_TOKENS.sAAVE, - OPTIMISM_TOKENS.sAVAX, - OPTIMISM_TOKENS.sBTC, - OPTIMISM_TOKENS.sETH, - OPTIMISM_TOKENS.sEUR, - OPTIMISM_TOKENS.sLINK, - OPTIMISM_TOKENS.sMATIC, - OPTIMISM_TOKENS.sSOL, - OPTIMISM_TOKENS.sUNI, - OPTIMISM_TOKENS.sUSD, - ]); - }) - .build(), - }, - TokenAdjacencyGraph.getEmptyGraph(), -); - -export const NATIVE_FEE_TOKEN_BY_CHAIN_ID = valueByChainId( - { - [ChainId.Mainnet]: getContractAddressesForChainOrThrow(ChainId.Mainnet).etherToken, - [ChainId.BSC]: getContractAddressesForChainOrThrow(ChainId.BSC).etherToken, - [ChainId.Ganache]: getContractAddressesForChainOrThrow(ChainId.Ganache).etherToken, - [ChainId.Ropsten]: getContractAddressesForChainOrThrow(ChainId.Ropsten).etherToken, - [ChainId.Goerli]: getContractAddressesForChainOrThrow(ChainId.Goerli).etherToken, - [ChainId.PolygonMumbai]: getContractAddressesForChainOrThrow(ChainId.PolygonMumbai).etherToken, - [ChainId.ArbitrumRinkeby]: getContractAddressesForChainOrThrow(ChainId.ArbitrumRinkeby).etherToken, - [ChainId.Rinkeby]: getContractAddressesForChainOrThrow(ChainId.Rinkeby).etherToken, - [ChainId.Kovan]: getContractAddressesForChainOrThrow(ChainId.Kovan).etherToken, - [ChainId.Polygon]: getContractAddressesForChainOrThrow(ChainId.Polygon).etherToken, - [ChainId.Avalanche]: getContractAddressesForChainOrThrow(ChainId.Avalanche).etherToken, - [ChainId.Fantom]: getContractAddressesForChainOrThrow(ChainId.Fantom).etherToken, - [ChainId.Celo]: getContractAddressesForChainOrThrow(ChainId.Celo).etherToken, - [ChainId.Optimism]: getContractAddressesForChainOrThrow(ChainId.Optimism).etherToken, - }, - NULL_ADDRESS, -); - -export const NATIVE_FEE_TOKEN_AMOUNT_BY_CHAIN_ID = valueByChainId( - { [ChainId.Mainnet]: ONE_ETHER.times(0.1) }, - ONE_ETHER, -); - -// Order dependent -const CURVE_TRI_POOL_MAINNET_TOKENS = [MAINNET_TOKENS.DAI, MAINNET_TOKENS.USDC, MAINNET_TOKENS.USDT]; -const CURVE_TRI_BTC_POOL_TOKEN = [MAINNET_TOKENS.RenBTC, MAINNET_TOKENS.WBTC, MAINNET_TOKENS.sBTC]; -const CURVE_POLYGON_ATRICRYPTO_UNDERLYING_TOKENS = [POLYGON_TOKENS.DAI, POLYGON_TOKENS.USDC, POLYGON_TOKENS.USDT]; -const CURVE_POLYGON_ATRICRYPTO_TOKENS = [POLYGON_TOKENS.amDAI, POLYGON_TOKENS.amUSDC, POLYGON_TOKENS.amUSDT]; -const CURVE_FANTOM_TWO_POOL_TOKENS = [FANTOM_TOKENS.DAI, FANTOM_TOKENS.USDC]; - -const createCurveExchangePool = (info: { tokens: string[]; pool: string; gasSchedule: number }) => ({ - exchangeFunctionSelector: CurveFunctionSelectors.exchange, - sellQuoteFunctionSelector: CurveFunctionSelectors.get_dy, - buyQuoteFunctionSelector: CurveFunctionSelectors.None, - tokens: info.tokens, - metaTokens: undefined, - poolAddress: info.pool, - gasSchedule: info.gasSchedule, -}); - -const createCurveExchangeUnderlyingPool = (info: { tokens: string[]; pool: string; gasSchedule: number }) => ({ - exchangeFunctionSelector: CurveFunctionSelectors.exchange_underlying, - sellQuoteFunctionSelector: CurveFunctionSelectors.get_dy_underlying, - buyQuoteFunctionSelector: CurveFunctionSelectors.None, - tokens: info.tokens, - metaTokens: undefined, - poolAddress: info.pool, - gasSchedule: info.gasSchedule, -}); - -const createCurveMetaTriPool = (info: { tokens: string[]; pool: string; gasSchedule: number }) => ({ - exchangeFunctionSelector: CurveFunctionSelectors.exchange_underlying, - sellQuoteFunctionSelector: CurveFunctionSelectors.get_dy_underlying, - buyQuoteFunctionSelector: CurveFunctionSelectors.None, - tokens: [...info.tokens, ...CURVE_TRI_POOL_MAINNET_TOKENS], - metaTokens: info.tokens, - poolAddress: info.pool, - gasSchedule: info.gasSchedule, -}); - -const createCurveMetaTriBtcPool = (info: { tokens: string[]; pool: string; gasSchedule: number }) => ({ - exchangeFunctionSelector: CurveFunctionSelectors.exchange_underlying, - sellQuoteFunctionSelector: CurveFunctionSelectors.get_dy_underlying, - buyQuoteFunctionSelector: CurveFunctionSelectors.None, - tokens: [...info.tokens, ...CURVE_TRI_BTC_POOL_TOKEN], - metaTokens: info.tokens, - poolAddress: info.pool, - gasSchedule: info.gasSchedule, -}); - -const createCurveMetaTwoPoolFantom = (info: { tokens: string[]; pool: string; gasSchedule: number }) => ({ - exchangeFunctionSelector: CurveFunctionSelectors.exchange_underlying, - sellQuoteFunctionSelector: CurveFunctionSelectors.get_dy_underlying, - buyQuoteFunctionSelector: CurveFunctionSelectors.None, - tokens: [...info.tokens, ...CURVE_FANTOM_TWO_POOL_TOKENS], - metaTokens: info.tokens, - poolAddress: info.pool, - gasSchedule: info.gasSchedule, -}); - -const createCurveExchangeV2Pool = (info: { tokens: string[]; pool: string; gasSchedule: number }) => ({ - exchangeFunctionSelector: CurveFunctionSelectors.exchange_v2, - sellQuoteFunctionSelector: CurveFunctionSelectors.get_dy_v2, - buyQuoteFunctionSelector: CurveFunctionSelectors.None, - tokens: info.tokens, - metaTokens: undefined, - poolAddress: info.pool, - gasSchedule: info.gasSchedule, -}); - -const createCurveV2MetaTriPool = (info: { tokens: string[]; pool: string; gasSchedule: number }) => ({ - exchangeFunctionSelector: CurveFunctionSelectors.exchange_underlying_v2, - sellQuoteFunctionSelector: CurveFunctionSelectors.get_dy_underlying_v2, - buyQuoteFunctionSelector: CurveFunctionSelectors.None, - tokens: [...CURVE_POLYGON_ATRICRYPTO_UNDERLYING_TOKENS, ...info.tokens], - metaTokens: info.tokens, - poolAddress: info.pool, - gasSchedule: info.gasSchedule, -}); - -const createCurveFactoryCryptoExchangePool = (info: { tokens: string[]; pool: string; gasSchedule: number }) => ({ - exchangeFunctionSelector: CurveFunctionSelectors.exchange_underlying_uint256, - sellQuoteFunctionSelector: CurveFunctionSelectors.get_dy_uint256, - buyQuoteFunctionSelector: CurveFunctionSelectors.None, - tokens: info.tokens, - metaTokens: undefined, - poolAddress: info.pool, - gasSchedule: info.gasSchedule, -}); -const MOBIUSMONEY_CELO_SHARED: CurveInfo = { - exchangeFunctionSelector: CurveFunctionSelectors.swap, - sellQuoteFunctionSelector: CurveFunctionSelectors.calculateSwap, - buyQuoteFunctionSelector: CurveFunctionSelectors.None, - metaTokens: undefined, - gasSchedule: 150e3, - poolAddress: NULL_ADDRESS, - tokens: [], -}; - -/** - * Mainnet Curve configuration - * The tokens are in order of their index, which each curve defines - * I.e DaiUsdc curve has DAI as index 0 and USDC as index 1 - */ -export const CURVE_MAINNET_INFOS: { [name: string]: CurveInfo } = { - [CURVE_POOLS.compound]: createCurveExchangeUnderlyingPool({ - tokens: [MAINNET_TOKENS.DAI, MAINNET_TOKENS.USDC], - pool: CURVE_POOLS.compound, - gasSchedule: 587e3, - }), - [CURVE_POOLS.PAX]: createCurveExchangeUnderlyingPool({ - tokens: [MAINNET_TOKENS.DAI, MAINNET_TOKENS.USDC, MAINNET_TOKENS.USDT, MAINNET_TOKENS.PAX], - pool: CURVE_POOLS.PAX, - gasSchedule: 742e3, - }), - [CURVE_POOLS.sUSD]: createCurveExchangeUnderlyingPool({ - tokens: [MAINNET_TOKENS.DAI, MAINNET_TOKENS.USDC, MAINNET_TOKENS.USDT, MAINNET_TOKENS.sUSD], - pool: CURVE_POOLS.sUSD, - gasSchedule: 302e3, - }), - [CURVE_POOLS.renBTC]: createCurveExchangePool({ - tokens: [MAINNET_TOKENS.RenBTC, MAINNET_TOKENS.WBTC], - pool: CURVE_POOLS.renBTC, - gasSchedule: 171e3, - }), - [CURVE_POOLS.sBTC]: createCurveExchangePool({ - tokens: [MAINNET_TOKENS.RenBTC, MAINNET_TOKENS.WBTC, MAINNET_TOKENS.sBTC], - pool: CURVE_POOLS.sBTC, - gasSchedule: 327e3, - }), - [CURVE_POOLS.HBTC]: createCurveExchangePool({ - tokens: [MAINNET_TOKENS.hBTC, MAINNET_TOKENS.WBTC], - pool: CURVE_POOLS.HBTC, - gasSchedule: 210e3, - }), - [CURVE_POOLS.TRI]: createCurveExchangePool({ - tokens: [MAINNET_TOKENS.DAI, MAINNET_TOKENS.USDC, MAINNET_TOKENS.USDT], - pool: CURVE_POOLS.TRI, - gasSchedule: 176e3, - }), - [CURVE_POOLS.GUSD]: createCurveMetaTriPool({ - tokens: [MAINNET_TOKENS.GUSD], - pool: CURVE_POOLS.GUSD, - gasSchedule: 411e3, - }), - [CURVE_POOLS.HUSD]: createCurveMetaTriPool({ - tokens: [MAINNET_TOKENS.HUSD], - pool: CURVE_POOLS.HUSD, - gasSchedule: 396e3, - }), - [CURVE_POOLS.USDN]: createCurveMetaTriPool({ - tokens: [MAINNET_TOKENS.USDN], - pool: CURVE_POOLS.USDN, - gasSchedule: 398e3, - }), - [CURVE_POOLS.mUSD]: createCurveMetaTriPool({ - tokens: [MAINNET_TOKENS.mUSD], - pool: CURVE_POOLS.mUSD, - gasSchedule: 385e3, - }), - [CURVE_POOLS.dUSD]: createCurveMetaTriPool({ - tokens: [MAINNET_TOKENS.dUSD], - pool: CURVE_POOLS.dUSD, - gasSchedule: 371e3, - }), - [CURVE_POOLS.tBTC]: createCurveMetaTriBtcPool({ - tokens: [MAINNET_TOKENS.tBTC], - pool: CURVE_POOLS.tBTC, - gasSchedule: 482e3, - }), - [CURVE_POOLS.pBTC]: createCurveMetaTriBtcPool({ - tokens: [MAINNET_TOKENS.pBTC], - pool: CURVE_POOLS.pBTC, - gasSchedule: 503e3, - }), - [CURVE_POOLS.bBTC]: createCurveMetaTriBtcPool({ - tokens: [MAINNET_TOKENS.bBTC], - pool: CURVE_POOLS.bBTC, - gasSchedule: 497e3, - }), - [CURVE_POOLS.oBTC]: createCurveMetaTriBtcPool({ - tokens: [MAINNET_TOKENS.oBTC], - pool: CURVE_POOLS.oBTC, - gasSchedule: 488e3, - }), - [CURVE_POOLS.UST]: createCurveMetaTriPool({ - tokens: [MAINNET_TOKENS.UST], - pool: CURVE_POOLS.UST, - gasSchedule: 340e3, - }), - [CURVE_POOLS.eurs]: createCurveExchangePool({ - tokens: [MAINNET_TOKENS.EURS, MAINNET_TOKENS.sEUR], - pool: CURVE_POOLS.eurs, - gasSchedule: 320e3, - }), - [CURVE_POOLS.eurt]: createCurveExchangePool({ - tokens: [MAINNET_TOKENS.EURT, MAINNET_TOKENS.sEUR], - pool: CURVE_POOLS.eurt, - gasSchedule: 320e3, - }), - [CURVE_POOLS.aave]: createCurveExchangeUnderlyingPool({ - tokens: [MAINNET_TOKENS.DAI, MAINNET_TOKENS.USDC, MAINNET_TOKENS.USDT], - pool: CURVE_POOLS.aave, - gasSchedule: 580e3, - }), - [CURVE_POOLS.aave]: createCurveExchangePool({ - tokens: [MAINNET_TOKENS.aDAI, MAINNET_TOKENS.aUSDC, MAINNET_TOKENS.aUSDT], - pool: CURVE_POOLS.aave, - gasSchedule: 580e3, - }), - [CURVE_POOLS.saave]: createCurveExchangeUnderlyingPool({ - tokens: [MAINNET_TOKENS.DAI, MAINNET_TOKENS.sUSD], - pool: CURVE_POOLS.saave, - gasSchedule: 580e3, - }), - [CURVE_POOLS.saave]: createCurveExchangePool({ - tokens: [MAINNET_TOKENS.aDAI, MAINNET_TOKENS.aSUSD], - pool: CURVE_POOLS.saave, - gasSchedule: 580e3, - }), - [CURVE_POOLS.USDP]: createCurveMetaTriPool({ - tokens: [MAINNET_TOKENS.USDP], - pool: CURVE_POOLS.USDP, - gasSchedule: 374e3, - }), - [CURVE_POOLS.ib]: createCurveExchangeUnderlyingPool({ - tokens: [MAINNET_TOKENS.DAI, MAINNET_TOKENS.USDC, MAINNET_TOKENS.USDT], - pool: CURVE_POOLS.ib, - gasSchedule: 646e3, - }), - [CURVE_POOLS.link]: createCurveExchangePool({ - tokens: [MAINNET_TOKENS.LINK, MAINNET_TOKENS.sLINK], - pool: CURVE_POOLS.link, - gasSchedule: 319e3, - }), - [CURVE_POOLS.TUSD]: createCurveMetaTriPool({ - tokens: [MAINNET_TOKENS.TUSD], - pool: CURVE_POOLS.TUSD, - gasSchedule: 404e3, - }), - [CURVE_POOLS.STABLEx]: createCurveMetaTriPool({ - tokens: [MAINNET_TOKENS.STABLEx], - pool: CURVE_POOLS.STABLEx, - gasSchedule: 397e3, - }), - [CURVE_POOLS.alUSD]: createCurveMetaTriPool({ - tokens: [MAINNET_TOKENS.alUSD], - pool: CURVE_POOLS.alUSD, - gasSchedule: 387e3, - }), - [CURVE_POOLS.FRAX]: createCurveMetaTriPool({ - tokens: [MAINNET_TOKENS.FRAX], - pool: CURVE_POOLS.FRAX, - gasSchedule: 387e3, - }), - [CURVE_POOLS.LUSD]: createCurveMetaTriPool({ - tokens: [MAINNET_TOKENS.LUSD], - pool: CURVE_POOLS.LUSD, - gasSchedule: 387e3, - }), - [CURVE_POOLS.BUSD]: createCurveMetaTriPool({ - tokens: [MAINNET_TOKENS.BUSD], - pool: CURVE_POOLS.BUSD, - gasSchedule: 387e3, - }), - [CURVE_POOLS.steth]: createCurveExchangePool({ - // This pool uses ETH - tokens: [MAINNET_TOKENS.WETH, MAINNET_TOKENS.stETH], - pool: CURVE_POOLS.steth, - gasSchedule: 151e3, - }), - [CURVE_POOLS.seth]: createCurveExchangePool({ - // This pool uses ETH - tokens: [MAINNET_TOKENS.WETH, MAINNET_TOKENS.sETH], - pool: CURVE_POOLS.seth, - gasSchedule: 187e3, - }), - [CURVE_POOLS.ankreth]: createCurveExchangePool({ - // This pool uses ETH - tokens: [MAINNET_TOKENS.WETH, MAINNET_TOKENS.ankrETH], - pool: CURVE_POOLS.ankreth, - gasSchedule: 125e3, - }), - [CURVE_POOLS.DSU3CRV]: createCurveMetaTriPool({ - tokens: [MAINNET_TOKENS.DSU], - pool: CURVE_POOLS.DSU3CRV, - gasSchedule: 387e3, - }), - [CURVE_POOLS.mim]: createCurveMetaTriPool({ - tokens: [MAINNET_TOKENS.MIM], - pool: CURVE_POOLS.mim, - gasSchedule: 300e3, - }), - [CURVE_POOLS.cvxcrv]: createCurveExchangePool({ - tokens: [MAINNET_TOKENS.CRV, MAINNET_TOKENS.cvxCRV], - pool: CURVE_POOLS.cvxcrv, - gasSchedule: 105e3, - }), - [CURVE_POOLS.ethcrv]: { - ...createCurveExchangePool({ - // This pool uses ETH - tokens: [MAINNET_TOKENS.WETH, MAINNET_TOKENS.CRV], - pool: CURVE_POOLS.ethcrv, - gasSchedule: 350e3, - }), - // This pool has a custom get_dy and exchange selector with uint256 - sellQuoteFunctionSelector: CurveFunctionSelectors.get_dy_uint256, - exchangeFunctionSelector: CurveFunctionSelectors.exchange_underlying_uint256, - }, - [CURVE_POOLS.ethcvx]: { - ...createCurveExchangePool({ - // This pool uses ETH - tokens: [MAINNET_TOKENS.WETH, MAINNET_TOKENS.CVX], - pool: CURVE_POOLS.ethcvx, - gasSchedule: 350e3, - }), - // This pool has a custom get_dy and exchange selector with uint256 - sellQuoteFunctionSelector: CurveFunctionSelectors.get_dy_uint256, - exchangeFunctionSelector: CurveFunctionSelectors.exchange_underlying_uint256, - }, - [CURVE_POOLS.fei_tri]: createCurveMetaTriPool({ - tokens: [MAINNET_TOKENS.FEI], - pool: CURVE_POOLS.fei_tri, - gasSchedule: 340e3, - }), - [CURVE_POOLS.rai_tri]: createCurveMetaTriPool({ - tokens: [MAINNET_TOKENS.RAI], - pool: CURVE_POOLS.rai_tri, - gasSchedule: 340e3, - }), - [CURVE_POOLS.DOLA_tri]: createCurveMetaTriPool({ - tokens: [MAINNET_TOKENS.DOLA], - pool: CURVE_POOLS.DOLA_tri, - gasSchedule: 340e3, - }), - [CURVE_POOLS.OUSD_tri]: createCurveMetaTriPool({ - tokens: [MAINNET_TOKENS.OUSD], - pool: CURVE_POOLS.OUSD_tri, - gasSchedule: 340e3, - }), - [CURVE_POOLS.d3pool]: createCurveExchangePool({ - tokens: [MAINNET_TOKENS.FRAX, MAINNET_TOKENS.FEI, MAINNET_TOKENS.alUSD], - pool: CURVE_POOLS.d3pool, - gasSchedule: 176e3, - }), - [CURVE_POOLS.triEURpool]: createCurveExchangePool({ - tokens: [MAINNET_TOKENS.agEUR, MAINNET_TOKENS.EURT, MAINNET_TOKENS.EURS], - pool: CURVE_POOLS.triEURpool, - gasSchedule: 176e3, - }), - [CURVE_POOLS.ibEURsEUR]: createCurveExchangePool({ - tokens: [MAINNET_TOKENS.ibEUR, MAINNET_TOKENS.sEUR], - pool: CURVE_POOLS.ibEURsEUR, - gasSchedule: 176e3, - }), - [CURVE_POOLS.btrflyweth]: createCurveFactoryCryptoExchangePool({ - tokens: [MAINNET_TOKENS.WETH, MAINNET_TOKENS.BTRFLY], - pool: CURVE_POOLS.btrflyweth, - gasSchedule: 250e3, - }), - [CURVE_POOLS.wethyfi]: createCurveFactoryCryptoExchangePool({ - tokens: [MAINNET_TOKENS.WETH, MAINNET_TOKENS.YFI], - pool: CURVE_POOLS.wethyfi, - gasSchedule: 250e3, - }), - [CURVE_POOLS.stgusdc]: createCurveFactoryCryptoExchangePool({ - tokens: [MAINNET_TOKENS.STG, MAINNET_TOKENS.USDC], - pool: CURVE_POOLS.stgusdc, - gasSchedule: 250e3, - }), - [CURVE_POOLS.cvxfxs]: createCurveFactoryCryptoExchangePool({ - tokens: [MAINNET_TOKENS.FXS, MAINNET_TOKENS.cvxFXS], - pool: CURVE_POOLS.cvxfxs, - gasSchedule: 390e3, - }), -}; - -export const CURVE_V2_MAINNET_INFOS: { [name: string]: CurveInfo } = { - [CURVE_V2_POOLS.tricrypto]: createCurveExchangeV2Pool({ - tokens: [MAINNET_TOKENS.USDT, MAINNET_TOKENS.WBTC, MAINNET_TOKENS.WETH], - pool: CURVE_V2_POOLS.tricrypto, - gasSchedule: 300e3, - }), - [CURVE_V2_POOLS.tricrypto2]: createCurveExchangeV2Pool({ - tokens: [MAINNET_TOKENS.USDT, MAINNET_TOKENS.WBTC, MAINNET_TOKENS.WETH], - pool: CURVE_V2_POOLS.tricrypto2, - gasSchedule: 300e3, - }), -}; - -export const CURVE_POLYGON_INFOS: { [name: string]: CurveInfo } = { - ['aave_exchangeunderlying']: createCurveExchangeUnderlyingPool({ - tokens: CURVE_POLYGON_ATRICRYPTO_UNDERLYING_TOKENS, - pool: CURVE_POLYGON_POOLS.aave, - gasSchedule: 300e3, - }), - ['aave_exchange']: createCurveExchangePool({ - tokens: CURVE_POLYGON_ATRICRYPTO_TOKENS, - pool: CURVE_POLYGON_POOLS.aave, - gasSchedule: 150e3, - }), - [CURVE_POLYGON_POOLS.ren]: createCurveExchangeUnderlyingPool({ - tokens: [POLYGON_TOKENS.WBTC, POLYGON_TOKENS.renBTC], - pool: CURVE_POLYGON_POOLS.ren, - gasSchedule: 350e3, - }), -}; - -export const CURVE_V2_POLYGON_INFOS: { [name: string]: CurveInfo } = { - [CURVE_V2_POLYGON_POOLS.atricrypto3]: createCurveV2MetaTriPool({ - tokens: [POLYGON_TOKENS.WBTC, POLYGON_TOKENS.WETH], - pool: CURVE_V2_POLYGON_POOLS.atricrypto3, - gasSchedule: 300e3, - }), -}; - -export const CURVE_AVALANCHE_INFOS: { [name: string]: CurveInfo } = { - ['aave_exchangeunderlying']: createCurveExchangeUnderlyingPool({ - tokens: [AVALANCHE_TOKENS.DAI, AVALANCHE_TOKENS.USDC, AVALANCHE_TOKENS.USDT], - pool: CURVE_AVALANCHE_POOLS.aave, - gasSchedule: 850e3, - }), - ['aave_exchange']: createCurveExchangePool({ - tokens: [AVALANCHE_TOKENS.aDAI, AVALANCHE_TOKENS.aUSDC, AVALANCHE_TOKENS.aUSDT], - pool: CURVE_AVALANCHE_POOLS.aave, - gasSchedule: 150e3, - }), - [CURVE_AVALANCHE_POOLS.mim]: createCurveExchangePool({ - tokens: [AVALANCHE_TOKENS.MIM, AVALANCHE_TOKENS.USDT, AVALANCHE_TOKENS.USDC], - pool: CURVE_AVALANCHE_POOLS.mim, - gasSchedule: 150e3, - }), - [CURVE_AVALANCHE_POOLS.USDC]: createCurveExchangePool({ - tokens: [AVALANCHE_TOKENS.USDC, AVALANCHE_TOKENS.nUSDC], - pool: CURVE_AVALANCHE_POOLS.USDC, - gasSchedule: 150e3, - }), -}; - -export const CURVE_V2_AVALANCHE_INFOS: { [name: string]: CurveInfo } = { - [CURVE_V2_AVALANCHE_POOLS.atricrypto]: { - exchangeFunctionSelector: CurveFunctionSelectors.exchange_underlying_v2, - sellQuoteFunctionSelector: CurveFunctionSelectors.get_dy_underlying_v2, - buyQuoteFunctionSelector: CurveFunctionSelectors.None, - tokens: [ - AVALANCHE_TOKENS.DAI, - AVALANCHE_TOKENS.USDC, - AVALANCHE_TOKENS.USDT, - AVALANCHE_TOKENS.WBTC, - AVALANCHE_TOKENS.WETH, - ], - metaTokens: undefined, - poolAddress: CURVE_V2_AVALANCHE_POOLS.atricrypto, - gasSchedule: 1300e3, - }, -}; - -// TODO: modify gasSchedule -export const CURVE_FANTOM_INFOS: { [name: string]: CurveInfo } = { - [CURVE_FANTOM_POOLS.ren]: createCurveExchangePool({ - tokens: [FANTOM_TOKENS.WBTC, FANTOM_TOKENS.renBTC], - pool: CURVE_FANTOM_POOLS.ren, - gasSchedule: 171e3, - }), - [CURVE_FANTOM_POOLS.twoPool]: createCurveExchangePool({ - tokens: [FANTOM_TOKENS.DAI, FANTOM_TOKENS.USDC], - pool: CURVE_FANTOM_POOLS.twoPool, - gasSchedule: 176e3, - }), - [CURVE_FANTOM_POOLS.fUSDT]: createCurveExchangeUnderlyingPool({ - tokens: [FANTOM_TOKENS.fUSDT, FANTOM_TOKENS.DAI, FANTOM_TOKENS.USDC], - pool: CURVE_FANTOM_POOLS.fUSDT, - gasSchedule: 587e3, - }), - [CURVE_FANTOM_POOLS.tri_v2]: createCurveExchangePool({ - tokens: [FANTOM_TOKENS.MIM, FANTOM_TOKENS.fUSDT, FANTOM_TOKENS.USDC], - pool: CURVE_FANTOM_POOLS.tri_v2, - gasSchedule: 176e3, - }), - ['geist_exchangeunderlying']: createCurveExchangeUnderlyingPool({ - tokens: [FANTOM_TOKENS.DAI, FANTOM_TOKENS.USDC, FANTOM_TOKENS.fUSDT], - pool: CURVE_FANTOM_POOLS.geist, - gasSchedule: 850e3, - }), - ['geist_exchange']: createCurveExchangePool({ - tokens: [FANTOM_TOKENS.gDAI, FANTOM_TOKENS.gUSDC, FANTOM_TOKENS.gfUSDT], - pool: CURVE_FANTOM_POOLS.geist, - gasSchedule: 150e3, - }), - [CURVE_FANTOM_POOLS.FRAX_twoPool]: createCurveMetaTwoPoolFantom({ - tokens: [FANTOM_TOKENS.FRAX], - pool: CURVE_FANTOM_POOLS.FRAX_twoPool, - gasSchedule: 411e3, - }), -}; - -export const CURVE_V2_FANTOM_INFOS: { [name: string]: CurveInfo } = { - [CURVE_V2_FANTOM_POOLS.tricrypto]: createCurveExchangeV2Pool({ - tokens: [FANTOM_TOKENS.fUSDT, FANTOM_TOKENS.WBTC, FANTOM_TOKENS.WETH], - pool: CURVE_V2_FANTOM_POOLS.tricrypto, - gasSchedule: 300e3, - }), -}; - -export const CURVE_OPTIMISM_INFOS: { [name: string]: CurveInfo } = { - [CURVE_OPTIMISM_POOLS.tri]: createCurveExchangePool({ - tokens: [OPTIMISM_TOKENS.DAI, OPTIMISM_TOKENS.USDC, OPTIMISM_TOKENS.USDT], - pool: CURVE_OPTIMISM_POOLS.tri, - gasSchedule: 150e3, - }), -}; - -export const BELT_BSC_INFOS: { [name: string]: CurveInfo } = { - [BELT_POOLS.vPool]: createCurveExchangeUnderlyingPool({ - tokens: [BSC_TOKENS.DAI, BSC_TOKENS.USDC, BSC_TOKENS.USDT, BSC_TOKENS.BUSD], - pool: BELT_POOLS.vPool, - gasSchedule: 4490e3, - }), -}; - -export const ELLIPSIS_BSC_INFOS: { [name: string]: CurveInfo } = { - [ELLIPSIS_POOLS.threePool]: createCurveExchangePool({ - tokens: [BSC_TOKENS.BUSD, BSC_TOKENS.USDC, BSC_TOKENS.USDT], - pool: ELLIPSIS_POOLS.threePool, - gasSchedule: 140e3, - }), -}; - -export const XSIGMA_MAINNET_INFOS: { [name: string]: CurveInfo } = { - [XSIGMA_POOLS.stable]: createCurveExchangePool({ - tokens: [MAINNET_TOKENS.DAI, MAINNET_TOKENS.USDC, MAINNET_TOKENS.USDT], - pool: XSIGMA_POOLS.stable, - gasSchedule: 150e3, - }), -}; - -// Curve-like sources using custom selectors -export const SADDLE_MAINNET_INFOS: { [name: string]: CurveInfo } = { - [SADDLE_POOLS.stablesV2]: { - exchangeFunctionSelector: CurveFunctionSelectors.swap, - sellQuoteFunctionSelector: CurveFunctionSelectors.calculateSwap, - buyQuoteFunctionSelector: CurveFunctionSelectors.None, - poolAddress: SADDLE_POOLS.stablesV2, - tokens: [MAINNET_TOKENS.DAI, MAINNET_TOKENS.USDC, MAINNET_TOKENS.USDT], - metaTokens: undefined, - gasSchedule: 150e3, - }, - [SADDLE_POOLS.bitcoinsV2]: { - exchangeFunctionSelector: CurveFunctionSelectors.swap, - sellQuoteFunctionSelector: CurveFunctionSelectors.calculateSwap, - buyQuoteFunctionSelector: CurveFunctionSelectors.None, - poolAddress: SADDLE_POOLS.bitcoinsV2, - tokens: [MAINNET_TOKENS.WBTC, MAINNET_TOKENS.RenBTC, MAINNET_TOKENS.sBTC], - metaTokens: undefined, - gasSchedule: 150e3, - }, - [SADDLE_POOLS.alETH]: { - exchangeFunctionSelector: CurveFunctionSelectors.swap, - sellQuoteFunctionSelector: CurveFunctionSelectors.calculateSwap, - buyQuoteFunctionSelector: CurveFunctionSelectors.None, - poolAddress: SADDLE_POOLS.alETH, - tokens: [MAINNET_TOKENS.WETH, MAINNET_TOKENS.alETH, MAINNET_TOKENS.sETH], - metaTokens: undefined, - gasSchedule: 200e3, - }, - [SADDLE_POOLS.d4]: { - exchangeFunctionSelector: CurveFunctionSelectors.swap, - sellQuoteFunctionSelector: CurveFunctionSelectors.calculateSwap, - buyQuoteFunctionSelector: CurveFunctionSelectors.None, - poolAddress: SADDLE_POOLS.d4, - tokens: [MAINNET_TOKENS.alUSD, MAINNET_TOKENS.FEI, MAINNET_TOKENS.FRAX, MAINNET_TOKENS.LUSD], - metaTokens: undefined, - gasSchedule: 150e3, - }, -}; - -export const IRONSWAP_POLYGON_INFOS: { [name: string]: CurveInfo } = { - [IRONSWAP_POOLS.is3usd]: { - exchangeFunctionSelector: CurveFunctionSelectors.swap, - sellQuoteFunctionSelector: CurveFunctionSelectors.calculateSwap, - buyQuoteFunctionSelector: CurveFunctionSelectors.None, - poolAddress: IRONSWAP_POOLS.is3usd, - tokens: [POLYGON_TOKENS.USDC, POLYGON_TOKENS.USDT, POLYGON_TOKENS.DAI], - metaTokens: undefined, - gasSchedule: 150e3, - }, -}; - -export const NERVE_BSC_INFOS: { [name: string]: CurveInfo } = { - [NERVE_POOLS.threePool]: { - exchangeFunctionSelector: CurveFunctionSelectors.swap, - sellQuoteFunctionSelector: CurveFunctionSelectors.calculateSwap, - buyQuoteFunctionSelector: CurveFunctionSelectors.None, - poolAddress: NERVE_POOLS.threePool, - tokens: [BSC_TOKENS.BUSD, BSC_TOKENS.USDT, BSC_TOKENS.USDC], - metaTokens: undefined, - gasSchedule: 140e3, - }, -}; - -export const SYNAPSE_BSC_INFOS: { [name: string]: CurveInfo } = { - [SYNAPSE_BSC_POOLS.nUSDLP]: { - exchangeFunctionSelector: CurveFunctionSelectors.swap, - sellQuoteFunctionSelector: CurveFunctionSelectors.calculateSwap, - buyQuoteFunctionSelector: CurveFunctionSelectors.None, - poolAddress: SYNAPSE_BSC_POOLS.nUSDLP, - tokens: [BSC_TOKENS.nUSD, BSC_TOKENS.BUSD, BSC_TOKENS.USDC, BSC_TOKENS.USDT], - metaTokens: undefined, - gasSchedule: 140e3, - }, -}; - -export const SYNAPSE_FANTOM_INFOS: { [name: string]: CurveInfo } = { - [SYNAPSE_FANTOM_POOLS.nUSDLP]: { - exchangeFunctionSelector: CurveFunctionSelectors.swap, - sellQuoteFunctionSelector: CurveFunctionSelectors.calculateSwap, - buyQuoteFunctionSelector: CurveFunctionSelectors.None, - poolAddress: SYNAPSE_FANTOM_POOLS.nUSDLP, - tokens: [FANTOM_TOKENS.nUSD, FANTOM_TOKENS.MIM, FANTOM_TOKENS.USDC, FANTOM_TOKENS.fUSDT], - metaTokens: undefined, - gasSchedule: 140e3, - }, -}; - -export const SYNAPSE_MAINNET_INFOS: { [name: string]: CurveInfo } = { - [SYNAPSE_MAINNET_POOLS.nUSDLP]: { - exchangeFunctionSelector: CurveFunctionSelectors.swap, - sellQuoteFunctionSelector: CurveFunctionSelectors.calculateSwap, - buyQuoteFunctionSelector: CurveFunctionSelectors.None, - poolAddress: SYNAPSE_MAINNET_POOLS.nUSDLP, - tokens: [MAINNET_TOKENS.DAI, MAINNET_TOKENS.USDC, MAINNET_TOKENS.USDT], - metaTokens: undefined, - gasSchedule: 140e3, - }, -}; - -export const SYNAPSE_OPTIMISM_INFOS: { [name: string]: CurveInfo } = { - [SYNAPSE_OPTIMISM_POOLS.nETHLP]: { - exchangeFunctionSelector: CurveFunctionSelectors.swap, - sellQuoteFunctionSelector: CurveFunctionSelectors.calculateSwap, - buyQuoteFunctionSelector: CurveFunctionSelectors.None, - poolAddress: SYNAPSE_OPTIMISM_POOLS.nETHLP, - tokens: [OPTIMISM_TOKENS.nETH, OPTIMISM_TOKENS.sWETH], - metaTokens: undefined, - gasSchedule: 140e3, - }, -}; - -export const SYNAPSE_POLYGON_INFOS: { [name: string]: CurveInfo } = { - [SYNAPSE_POLYGON_POOLS.nUSDLP]: { - exchangeFunctionSelector: CurveFunctionSelectors.swap, - sellQuoteFunctionSelector: CurveFunctionSelectors.calculateSwap, - buyQuoteFunctionSelector: CurveFunctionSelectors.None, - poolAddress: SYNAPSE_POLYGON_POOLS.nUSDLP, - tokens: [POLYGON_TOKENS.nUSD, POLYGON_TOKENS.DAI, POLYGON_TOKENS.USDC, POLYGON_TOKENS.USDT], - metaTokens: undefined, - gasSchedule: 140e3, - }, -}; - -export const SYNAPSE_AVALANCHE_INFOS: { [name: string]: CurveInfo } = { - [SYNAPSE_AVALANCHE_POOLS.nUSDLP]: { - exchangeFunctionSelector: CurveFunctionSelectors.swap, - sellQuoteFunctionSelector: CurveFunctionSelectors.calculateSwap, - buyQuoteFunctionSelector: CurveFunctionSelectors.None, - poolAddress: SYNAPSE_AVALANCHE_POOLS.nUSDLP, - tokens: [AVALANCHE_TOKENS.nUSD, AVALANCHE_TOKENS.DAI, AVALANCHE_TOKENS.USDC, AVALANCHE_TOKENS.USDT], - metaTokens: undefined, - gasSchedule: 140e3, - }, - [SYNAPSE_AVALANCHE_POOLS.nETHLP]: { - exchangeFunctionSelector: CurveFunctionSelectors.swap, - sellQuoteFunctionSelector: CurveFunctionSelectors.calculateSwap, - buyQuoteFunctionSelector: CurveFunctionSelectors.None, - poolAddress: SYNAPSE_AVALANCHE_POOLS.nETHLP, - tokens: [AVALANCHE_TOKENS.nETH, AVALANCHE_TOKENS.aWETH], - metaTokens: undefined, - gasSchedule: 140e3, - }, -}; - -export const FIREBIRDONESWAP_BSC_INFOS: { [name: string]: CurveInfo } = { - [FIREBIRDONESWAP_BSC_POOLS.oneswap]: { - exchangeFunctionSelector: CurveFunctionSelectors.swap, - sellQuoteFunctionSelector: CurveFunctionSelectors.calculateSwap, - buyQuoteFunctionSelector: CurveFunctionSelectors.None, - poolAddress: FIREBIRDONESWAP_BSC_POOLS.oneswap, - tokens: [BSC_TOKENS.BUSD, BSC_TOKENS.USDT, BSC_TOKENS.DAI, BSC_TOKENS.USDC], - metaTokens: undefined, - gasSchedule: 100e3, - }, -}; - -export const FIREBIRDONESWAP_POLYGON_INFOS: { [name: string]: CurveInfo } = { - [FIREBIRDONESWAP_POLYGON_POOLS.oneswap]: { - exchangeFunctionSelector: CurveFunctionSelectors.swap, - sellQuoteFunctionSelector: CurveFunctionSelectors.calculateSwap, - buyQuoteFunctionSelector: CurveFunctionSelectors.None, - poolAddress: FIREBIRDONESWAP_POLYGON_POOLS.oneswap, - tokens: [POLYGON_TOKENS.DAI, POLYGON_TOKENS.USDC, POLYGON_TOKENS.USDT], - metaTokens: undefined, - gasSchedule: 100e3, - }, -}; - -export const MOBIUSMONEY_CELO_INFOS: { [name: string]: CurveInfo } = { - [MOBIUSMONEY_CELO_POOLS.usdc_optics_v2]: { - ...MOBIUSMONEY_CELO_SHARED, - poolAddress: MOBIUSMONEY_CELO_POOLS.usdc_optics_v2, - tokens: [CELO_TOKENS.cUSD, CELO_TOKENS.cUSDC_V2], - }, - [MOBIUSMONEY_CELO_POOLS.weth_optics_v2]: { - ...MOBIUSMONEY_CELO_SHARED, - poolAddress: MOBIUSMONEY_CELO_POOLS.weth_optics_v2, - tokens: [CELO_TOKENS.cETH, CELO_TOKENS.oWETH], - }, - [MOBIUSMONEY_CELO_POOLS.pusdc_optics_v2]: { - ...MOBIUSMONEY_CELO_SHARED, - poolAddress: MOBIUSMONEY_CELO_POOLS.pusdc_optics_v2, - tokens: [CELO_TOKENS.cUSD, CELO_TOKENS.pUSDC_V2], - }, - [MOBIUSMONEY_CELO_POOLS.usdc_allbridge_solana]: { - ...MOBIUSMONEY_CELO_SHARED, - poolAddress: MOBIUSMONEY_CELO_POOLS.usdc_allbridge_solana, - tokens: [CELO_TOKENS.cUSD, CELO_TOKENS.asUSDC], - }, - [MOBIUSMONEY_CELO_POOLS.usdc_poly_optics]: { - ...MOBIUSMONEY_CELO_SHARED, - poolAddress: MOBIUSMONEY_CELO_POOLS.usdc_poly_optics, - tokens: [CELO_TOKENS.cUSD, CELO_TOKENS.pUSD], - }, - [MOBIUSMONEY_CELO_POOLS.dai_optics_v2]: { - ...MOBIUSMONEY_CELO_SHARED, - poolAddress: MOBIUSMONEY_CELO_POOLS.dai_optics_v2, - tokens: [CELO_TOKENS.cUSD, CELO_TOKENS.DAI], - }, -}; - -const ACRYPTOS_ACS4USD_POOL_BSC_TOKENS = [BSC_TOKENS.BUSD, BSC_TOKENS.USDT, BSC_TOKENS.DAI, BSC_TOKENS.USDC]; - -const createAcryptosMetaUsdPool = (info: { tokens: string[]; pool: string; gasSchedule: number }) => ({ - exchangeFunctionSelector: CurveFunctionSelectors.exchange_underlying, - sellQuoteFunctionSelector: CurveFunctionSelectors.get_dy_underlying, - buyQuoteFunctionSelector: CurveFunctionSelectors.None, - tokens: [...info.tokens, ...ACRYPTOS_ACS4USD_POOL_BSC_TOKENS], - metaTokens: info.tokens, - poolAddress: info.pool, - gasSchedule: info.gasSchedule, -}); - -export const ACRYPTOS_BSC_INFOS: { [name: string]: CurveInfo } = { - [ACRYPTOS_POOLS.acs4usd]: createCurveExchangePool({ - tokens: ACRYPTOS_ACS4USD_POOL_BSC_TOKENS, - pool: ACRYPTOS_POOLS.acs4usd, - gasSchedule: 145e3, - }), - - [ACRYPTOS_POOLS.acs4vai]: createAcryptosMetaUsdPool({ - tokens: [BSC_TOKENS.VAI], - pool: ACRYPTOS_POOLS.acs4vai, - gasSchedule: 300e3, - }), - - [ACRYPTOS_POOLS.acs4ust]: createAcryptosMetaUsdPool({ - tokens: [BSC_TOKENS.UST], - pool: ACRYPTOS_POOLS.acs4ust, - gasSchedule: 300e3, - }), - - [ACRYPTOS_POOLS.acs3btc]: createCurveExchangePool({ - tokens: [BSC_TOKENS.BTCB, BSC_TOKENS.renBTC, BSC_TOKENS.pBTC], - pool: ACRYPTOS_POOLS.acs3btc, - gasSchedule: 145e3, - }), -}; - -export const PLATYPUS_AVALANCHE_INFOS: { [name: string]: PlatypusInfo } = { - [PLATYPUS_AVALANCHE_POOLS.usd]: { - poolAddress: PLATYPUS_AVALANCHE_POOLS.usd, - tokens: [ - AVALANCHE_TOKENS.USDT, - AVALANCHE_TOKENS.USDC, - AVALANCHE_TOKENS.DAI, - AVALANCHE_TOKENS.nUSDC, - AVALANCHE_TOKENS.USDt, - ], - gasSchedule: 300e3, - }, - [PLATYPUS_AVALANCHE_POOLS.yusd]: { - poolAddress: PLATYPUS_AVALANCHE_POOLS.yusd, - tokens: [AVALANCHE_TOKENS.YUSD, AVALANCHE_TOKENS.nUSDC], - gasSchedule: 300e3, - }, - [PLATYPUS_AVALANCHE_POOLS.frax]: { - poolAddress: PLATYPUS_AVALANCHE_POOLS.frax, - tokens: [AVALANCHE_TOKENS.FRAX, AVALANCHE_TOKENS.nUSDC], - gasSchedule: 300e3, - }, - [PLATYPUS_AVALANCHE_POOLS.mim]: { - poolAddress: PLATYPUS_AVALANCHE_POOLS.mim, - tokens: [AVALANCHE_TOKENS.MIM, AVALANCHE_TOKENS.nUSDC], - gasSchedule: 300e3, - }, - [PLATYPUS_AVALANCHE_POOLS.sAVAX]: { - poolAddress: PLATYPUS_AVALANCHE_POOLS.sAVAX, - tokens: [AVALANCHE_TOKENS.WAVAX, AVALANCHE_TOKENS.sAVAX], - gasSchedule: 300e3, - }, -}; - -export const LIQUIDITY_PROVIDER_REGISTRY_BY_CHAIN_ID = valueByChainId( - { - [ChainId.Mainnet]: { - ['0x1d0d407c5af8c86f0a6494de86e56ae21e46a951']: { - tokens: [ - MAINNET_TOKENS.WETH, - MAINNET_TOKENS.USDC, - MAINNET_TOKENS.USDT, - MAINNET_TOKENS.WBTC, - MAINNET_TOKENS.PAX, - MAINNET_TOKENS.LINK, - MAINNET_TOKENS.KNC, - MAINNET_TOKENS.MANA, - MAINNET_TOKENS.DAI, - MAINNET_TOKENS.BUSD, - MAINNET_TOKENS.AAVE, - MAINNET_TOKENS.HT, - ], - gasCost: (takerToken: string, makerToken: string) => - [takerToken, makerToken].includes(MAINNET_TOKENS.WETH) ? 160e3 : 280e3, - }, - }, - }, - {}, -); - -export const UNISWAPV1_ROUTER_BY_CHAIN_ID = valueByChainId( - { - [ChainId.Mainnet]: '0xc0a47dfe034b400b47bdad5fecda2621de6c4d95', - [ChainId.Ropsten]: '0x9c83dce8ca20e9aaf9d3efc003b2ea62abc08351', - [ChainId.Goerli]: '0x6Ce570d02D73d4c384b46135E87f8C592A8c86dA', - }, - NULL_ADDRESS, -); - -export const UNISWAPV2_ROUTER_BY_CHAIN_ID = valueByChainId( - { - [ChainId.Mainnet]: '0xf164fc0ec4e93095b804a4795bbe1e041497b92a', - [ChainId.Ropsten]: '0xf164fc0ec4e93095b804a4795bbe1e041497b92a', - [ChainId.Goerli]: '0xf164fc0ec4e93095b804a4795bbe1e041497b92a', - }, - NULL_ADDRESS, -); - -export const SUSHISWAP_ROUTER_BY_CHAIN_ID = valueByChainId( - { - [ChainId.Mainnet]: '0xd9e1ce17f2641f24ae83637ab66a2cca9c378b9f', - [ChainId.BSC]: '0x1b02da8cb0d097eb8d57a175b88c7d8b47997506', - [ChainId.Ropsten]: '0x1b02da8cb0d097eb8d57a175b88c7d8b47997506', - [ChainId.Goerli]: '0x1b02da8cb0d097eb8d57a175b88c7d8b47997506', - [ChainId.Polygon]: '0x1b02da8cb0d097eb8d57a175b88c7d8b47997506', - [ChainId.Avalanche]: '0x1b02da8cb0d097eb8d57a175b88c7d8b47997506', - [ChainId.Fantom]: '0x1b02da8cb0d097eb8d57a175b88c7d8b47997506', - [ChainId.Celo]: '0x1421bde4b10e8dd459b3bcb598810b1337d56842', - }, - NULL_ADDRESS, -); - -export const CRYPTO_COM_ROUTER_BY_CHAIN_ID = valueByChainId( - { - [ChainId.Mainnet]: '0xceb90e4c17d626be0facd78b79c9c87d7ca181b3', - }, - NULL_ADDRESS, -); - -export const SHIBASWAP_ROUTER_BY_CHAIN_ID = valueByChainId( - { - [ChainId.Mainnet]: '0x03f7724180aa6b939894b5ca4314783b0b36b329', - }, - NULL_ADDRESS, -); - -export const MSTABLE_POOLS_BY_CHAIN_ID = valueByChainId( - { - [ChainId.Mainnet]: { - mUSD: { - poolAddress: '0xe2f2a5c287993345a840db3b0845fbc70f5935a5', - tokens: [MAINNET_TOKENS.DAI, MAINNET_TOKENS.USDC, MAINNET_TOKENS.USDT], - }, - mBTC: { - poolAddress: '0x945facb997494cc2570096c74b5f66a3507330a1', - tokens: [MAINNET_TOKENS.WBTC, MAINNET_TOKENS.RenBTC, MAINNET_TOKENS.sBTC], - }, - }, - [ChainId.Polygon]: { - mUSD: { - poolAddress: '0xe840b73e5287865eec17d250bfb1536704b43b21', - tokens: [POLYGON_TOKENS.DAI, POLYGON_TOKENS.USDC, POLYGON_TOKENS.USDT], - }, - mBTC: { - poolAddress: NULL_ADDRESS, - tokens: [] as string[], - }, - }, - }, - { - mUSD: { - poolAddress: NULL_ADDRESS, - tokens: [] as string[], - }, - mBTC: { - poolAddress: NULL_ADDRESS, - tokens: [] as string[], - }, - }, -); - -export const KYBER_DMM_ROUTER_BY_CHAIN_ID = valueByChainId( - { - [ChainId.Mainnet]: '0x1c87257f5e8609940bc751a07bb085bb7f8cdbe6', - [ChainId.Polygon]: '0x546c79662e028b661dfb4767664d0273184e4dd1', - [ChainId.BSC]: '0x78df70615ffc8066cc0887917f2cd72092c86409', - [ChainId.Avalanche]: '0x8efa5a9ad6d594cf76830267077b78ce0bc5a5f8', - [ChainId.Fantom]: '0x5d5a5a0a465129848c2549669e12cdc2f8de039a', - }, - NULL_ADDRESS, -); - -export const BISWAP_ROUTER_BY_CHAIN_ID = valueByChainId( - { - [ChainId.BSC]: '0x3a6d8ca21d1cf76f653a67577fa0d27453350dd8', - }, - NULL_ADDRESS, -); - -export const MDEX_ROUTER_BY_CHAIN_ID = valueByChainId( - { - [ChainId.BSC]: '0x7dae51bd3e3376b8c7c4900e9107f12be3af1ba8', - }, - NULL_ADDRESS, -); - -export const KNIGHTSWAP_ROUTER_BY_CHAIN_ID = valueByChainId( - { - [ChainId.BSC]: '0x05e61e0cdcd2170a76f9568a110cee3afdd6c46f', - }, - NULL_ADDRESS, -); - -export const MOONISWAP_REGISTRIES_BY_CHAIN_ID = valueByChainId( - { - [ChainId.BSC]: ['0xd41b24bba51fac0e4827b6f94c0d6ddeb183cd64'], - }, - [] as string[], -); - -export const DODOV1_CONFIG_BY_CHAIN_ID = valueByChainId( - { - [ChainId.Mainnet]: { - helper: '0x533da777aedce766ceae696bf90f8541a4ba80eb', - registry: '0x3A97247DF274a17C59A3bd12735ea3FcDFb49950', - }, - [ChainId.BSC]: { - helper: '0x0f859706aee7fcf61d5a8939e8cb9dbb6c1eda33', - registry: '0xca459456a45e300aa7ef447dbb60f87cccb42828', - }, - [ChainId.Polygon]: { - helper: '0xdfaf9584f5d229a9dbe5978523317820a8897c5a', - registry: '0x357c5e9cfa8b834edcef7c7aabd8f9db09119d11', - }, - }, - { helper: NULL_ADDRESS, registry: NULL_ADDRESS }, -); - -export const DODOV2_FACTORIES_BY_CHAIN_ID = valueByChainId( - { - [ChainId.Mainnet]: [ - '0x6b4fa0bc61eddc928e0df9c7f01e407bfcd3e5ef', // Private Pool - '0x72d220ce168c4f361dd4dee5d826a01ad8598f6c', // Vending Machine - '0x6fddb76c93299d985f4d3fc7ac468f9a168577a4', // Stability Pool - ], - [ChainId.BSC]: [ - '0xafe0a75dffb395eaabd0a7e1bbbd0b11f8609eef', // Private Pool - '0x790b4a80fb1094589a3c0efc8740aa9b0c1733fb', // Vending Machine - '0x0fb9815938ad069bf90e14fe6c596c514bede767', // Stability Pool - ], - [ChainId.Polygon]: [ - '0x95e887adf9eaa22cc1c6e3cb7f07adc95b4b25a8', // Private Pool - '0x79887f65f83bdf15bcc8736b5e5bcdb48fb8fe13', // Vending Machine - '0x43c49f8dd240e1545f147211ec9f917376ac1e87', // Stability Pool - ], - }, - [] as string[], -); -export const MAX_DODOV2_POOLS_QUERIED = 3; - -export const CURVE_LIQUIDITY_PROVIDER_BY_CHAIN_ID = valueByChainId( - { - [ChainId.Mainnet]: '0x561b94454b65614ae3db0897b74303f4acf7cc75', - [ChainId.Ropsten]: '0xae241c6fc7f28f6dc0cb58b4112ba7f63fcaf5e2', - }, - NULL_ADDRESS, -); - -export const MAKER_PSM_INFO_BY_CHAIN_ID = valueByChainId( - { - [ChainId.Mainnet]: { - // Currently only USDC is supported - gemTokenAddress: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', - ilkIdentifier: formatBytes32String('PSM-USDC-A'), - psmAddress: '0x89b78cfa322f6c5de0abceecab66aee45393cc5a', - }, - }, - { - gemTokenAddress: NULL_ADDRESS, - ilkIdentifier: NULL_BYTES, - psmAddress: NULL_ADDRESS, - }, -); - -export const BANCOR_REGISTRY_BY_CHAIN_ID = valueByChainId( - { - [ChainId.Mainnet]: '0x52Ae12ABe5D8BD778BD5397F99cA900624CfADD4', - }, - NULL_ADDRESS, -); - -export const BANCORV3_NETWORK_BY_CHAIN_ID = valueByChainId( - { - [ChainId.Mainnet]: '0xeef417e1d5cc832e619ae18d2f140de2999dd4fb', - }, - NULL_ADDRESS, -); - -export const BANCORV3_NETWORK_INFO_BY_CHAIN_ID = valueByChainId( - { - [ChainId.Mainnet]: '0x8e303d296851b320e6a697bacb979d13c9d6e760', - }, - NULL_ADDRESS, -); - -export const SHELL_POOLS_BY_CHAIN_ID = valueByChainId( - { - [ChainId.Mainnet]: { - StableCoins: { - poolAddress: '0x8f26d7bab7a73309141a291525c965ecdea7bf42', - tokens: [MAINNET_TOKENS.USDC, MAINNET_TOKENS.USDT, MAINNET_TOKENS.sUSD, MAINNET_TOKENS.DAI], - }, - Bitcoin: { - poolAddress: '0xc2d019b901f8d4fdb2b9a65b5d226ad88c66ee8d', - tokens: [MAINNET_TOKENS.RenBTC, MAINNET_TOKENS.WBTC, MAINNET_TOKENS.sBTC], - }, - }, - }, - { - StableCoins: { - poolAddress: NULL_ADDRESS, - tokens: [] as string[], - }, - Bitcoin: { - poolAddress: NULL_ADDRESS, - tokens: [] as string[], - }, - }, -); - -export const COMPONENT_POOLS_BY_CHAIN_ID = valueByChainId( - { - [ChainId.Mainnet]: { - USDP_USDC_USDT: { - poolAddress: '0x49519631b404e06ca79c9c7b0dc91648d86f08db', - tokens: [MAINNET_TOKENS.USDP, MAINNET_TOKENS.USDC, MAINNET_TOKENS.USDT], - }, - USDP_DAI_SUSD: { - poolAddress: '0x6477960dd932d29518d7e8087d5ea3d11e606068', - tokens: [MAINNET_TOKENS.USDP, MAINNET_TOKENS.DAI, MAINNET_TOKENS.sUSD], - }, - }, - }, - { - USDP_USDC_USDT: { - poolAddress: NULL_ADDRESS, - tokens: [] as string[], - }, - USDP_DAI_SUSD: { - poolAddress: NULL_ADDRESS, - tokens: [] as string[], - }, - }, -); - -export const GEIST_INFO_ADDRESS_BY_CHAIN_ID = valueByChainId( - { - [ChainId.Fantom]: '0xd8321aa83fb0a4ecd6348d4577431310a6e0814d', - }, - NULL_ADDRESS, -); - -export const BALANCER_V2_VAULT_ADDRESS_BY_CHAIN = valueByChainId( - { - [ChainId.Mainnet]: '0xba12222222228d8ba445958a75a0704d566bf2c8', - [ChainId.Polygon]: '0xba12222222228d8ba445958a75a0704d566bf2c8', - }, - NULL_ADDRESS, -); - -export const BEETHOVEN_X_VAULT_ADDRESS_BY_CHAIN = valueByChainId( - { - [ChainId.Fantom]: '0x20dd72ed959b6147912c2e529f0a0c651c33c9ce', - }, - NULL_ADDRESS, -); - -export const LIDO_INFO_BY_CHAIN = valueByChainId( - { - [ChainId.Mainnet]: { - stEthToken: MAINNET_TOKENS.stETH, - wstEthToken: MAINNET_TOKENS.wstETH, - wethToken: MAINNET_TOKENS.WETH, - }, - }, - { - wstEthToken: NULL_ADDRESS, - stEthToken: NULL_ADDRESS, - wethToken: NULL_ADDRESS, - }, -); - -export const BALANCER_TOP_POOLS_FETCHED = 250; -export const BALANCER_MAX_POOLS_FETCHED = 3; - -export const BALANCER_V2_SUBGRAPH_URL_BY_CHAIN = valueByChainId( - { - [ChainId.Mainnet]: 'https://api.thegraph.com/subgraphs/name/balancer-labs/balancer-v2', - [ChainId.Polygon]: 'https://api.thegraph.com/subgraphs/name/balancer-labs/balancer-polygon-v2', - }, - null, -); - -export const UNISWAPV3_CONFIG_BY_CHAIN_ID = valueByChainId( - { - [ChainId.Mainnet]: { - quoter: '0x61ffe014ba17989e743c5f6cb21bf9697530b21e', - router: '0xe592427a0aece92de3edee1f18e0157c05861564', - }, - [ChainId.Ropsten]: { - quoter: '0x61ffe014ba17989e743c5f6cb21bf9697530b21e', - router: '0xe592427a0aece92de3edee1f18e0157c05861564', - }, - [ChainId.Goerli]: { - quoter: '0x61ffe014ba17989e743c5f6cb21bf9697530b21e', - router: '0xe592427a0aece92de3edee1f18e0157c05861564', - }, - [ChainId.PolygonMumbai]: { - quoter: '0x61ffe014ba17989e743c5f6cb21bf9697530b21e', - router: '0xe592427a0aece92de3edee1f18e0157c05861564', - }, - [ChainId.Polygon]: { - quoter: '0x61ffe014ba17989e743c5f6cb21bf9697530b21e', - router: '0xe592427a0aece92de3edee1f18e0157c05861564', - }, - [ChainId.Optimism]: { - quoter: '0x61ffe014ba17989e743c5f6cb21bf9697530b21e', - router: '0xe592427a0aece92de3edee1f18e0157c05861564', - }, - [ChainId.ArbitrumRinkeby]: { - quoter: '0x61ffe014ba17989e743c5f6cb21bf9697530b21e', - router: '0xe592427a0aece92de3edee1f18e0157c05861564', - }, - }, - { quoter: NULL_ADDRESS, router: NULL_ADDRESS }, -); - -export const AAVE_V2_SUBGRAPH_URL_BY_CHAIN_ID = valueByChainId( - { - // TODO: enable after FQT has been redeployed on Ethereum mainnet - // [ChainId.Mainnet]: 'https://api.thegraph.com/subgraphs/name/aave/protocol-v2', - [ChainId.Polygon]: 'https://api.thegraph.com/subgraphs/name/aave/aave-v2-matic', - [ChainId.Avalanche]: 'https://api.thegraph.com/subgraphs/name/aave/protocol-v2-avalanche', - }, - null, -); - -export const COMPOUND_API_URL_BY_CHAIN_ID = valueByChainId( - { - // TODO: enable after FQT has been redeployed on Ethereum mainnet - // [ChainId.Mainnet]: 'https://api.compound.finance/api/v2', - }, - null, -); - -// -// BSC -// -export const PANCAKESWAP_ROUTER_BY_CHAIN_ID = valueByChainId( - { - [ChainId.BSC]: '0x05ff2b0db69458a0750badebc4f9e13add608c7f', - }, - NULL_ADDRESS, -); - -export const PANCAKESWAPV2_ROUTER_BY_CHAIN_ID = valueByChainId( - { - [ChainId.BSC]: '0x10ed43c718714eb63d5aa57b78b54704e256024e', - }, - NULL_ADDRESS, -); - -export const BAKERYSWAP_ROUTER_BY_CHAIN_ID = valueByChainId( - { - [ChainId.BSC]: '0xcde540d7eafe93ac5fe6233bee57e1270d3e330f', - }, - NULL_ADDRESS, -); - -export const APESWAP_ROUTER_BY_CHAIN_ID = valueByChainId( - { - [ChainId.BSC]: '0xc0788a3ad43d79aa53b09c2eacc313a787d1d607', - [ChainId.Polygon]: '0xc0788a3ad43d79aa53b09c2eacc313a787d1d607', - }, - NULL_ADDRESS, -); - -export const CHEESESWAP_ROUTER_BY_CHAIN_ID = valueByChainId( - { - [ChainId.BSC]: '0x3047799262d8d2ef41ed2a222205968bc9b0d895', - }, - NULL_ADDRESS, -); - -// -// Polygon -// -export const QUICKSWAP_ROUTER_BY_CHAIN_ID = valueByChainId( - { - [ChainId.Polygon]: '0xa5e0829caced8ffdd4de3c43696c57f7d7a678ff', - }, - NULL_ADDRESS, -); - -export const DFYN_ROUTER_BY_CHAIN_ID = valueByChainId( - { - [ChainId.Polygon]: '0xa102072a4c07f06ec3b4900fdc4c7b80b6c57429', - }, - NULL_ADDRESS, -); - -export const WAULTSWAP_ROUTER_BY_CHAIN_ID = valueByChainId( - { - [ChainId.BSC]: '0xd48745e39bbed146eec15b79cbf964884f9877c2', - [ChainId.Polygon]: '0x3a1d87f206d12415f5b0a33e786967680aab4f6d', - }, - NULL_ADDRESS, -); - -export const MESHSWAP_ROUTER_BY_CHAIN_ID = valueByChainId( - { - [ChainId.Polygon]: '0x10f4a785f458bc144e3706575924889954946639', - }, - NULL_ADDRESS, -); - -export const PANGOLIN_ROUTER_BY_CHAIN_ID = valueByChainId( - { - [ChainId.Avalanche]: '0xe54ca86531e17ef3616d22ca28b0d458b6c89106', - }, - NULL_ADDRESS, -); - -export const TRADER_JOE_ROUTER_BY_CHAIN_ID = valueByChainId( - { - [ChainId.Avalanche]: '0x60ae616a2155ee3d9a68541ba4544862310933d4', - }, - NULL_ADDRESS, -); - -export const UBESWAP_ROUTER_BY_CHAIN_ID = valueByChainId( - { - [ChainId.Celo]: '0x7d28570135a2b1930f331c507f65039d4937f66c', - }, - NULL_ADDRESS, -); - -export const MORPHEUSSWAP_ROUTER_BY_CHAIN_ID = valueByChainId( - { - [ChainId.Fantom]: '0x8ac868293d97761a1fed6d4a01e9ff17c5594aa3', - }, - NULL_ADDRESS, -); - -export const SPIRITSWAP_ROUTER_BY_CHAIN_ID = valueByChainId( - { - [ChainId.Fantom]: '0x16327e3fbdaca3bcf7e38f5af2599d2ddc33ae52', - }, - NULL_ADDRESS, -); - -export const SPOOKYSWAP_ROUTER_BY_CHAIN_ID = valueByChainId( - { - [ChainId.Fantom]: '0xf491e7b69e4244ad4002bc14e878a34207e38c29', - }, - NULL_ADDRESS, -); - -export const GMX_ROUTER_BY_CHAIN_ID = valueByChainId( - { - [ChainId.Avalanche]: '0x5f719c2f1095f7b9fc68a68e35b51194f4b6abe8', - }, - NULL_ADDRESS, -); - -export const GMX_READER_BY_CHAIN_ID = valueByChainId( - { - [ChainId.Avalanche]: '0x67b789d48c926006f5132bfce4e976f0a7a63d5d', - }, - NULL_ADDRESS, -); - -export const GMX_VAULT_BY_CHAIN_ID = valueByChainId( - { - [ChainId.Avalanche]: '0x9ab2de34a33fb459b538c43f251eb825645e8595', - }, - NULL_ADDRESS, -); - -export const PLATYPUS_ROUTER_BY_CHAIN_ID = valueByChainId( - { - [ChainId.Avalanche]: '0x73256ec7575d999c360c1eec118ecbefd8da7d12', - }, - NULL_ADDRESS, -); - -export const YOSHI_ROUTER_BY_CHAIN_ID = valueByChainId( - { - [ChainId.Fantom]: '0xe4a4642b19c4d0cba965673cd51422b1eda0a78d', - }, - NULL_ADDRESS, -); - -export const VELODROME_ROUTER_BY_CHAIN_ID = valueByChainId( - { - [ChainId.Optimism]: '0xa132dab612db5cb9fc9ac426a0cc215a3423f9c9', - }, - NULL_ADDRESS, -); - -export const SYNTHETIX_READ_PROXY_BY_CHAIN_ID = valueByChainId( - { - [ChainId.Mainnet]: '0x4e3b31eb0e5cb73641ee1e65e7dcefe520ba3ef2', - [ChainId.Optimism]: '0x1cb059b7e74fd21665968c908806143e744d5f30', - }, - NULL_ADDRESS, -); - -export const SYNTHETIX_CURRENCY_KEYS_BY_CHAIN_ID = valueByChainId>( - { - // There is no easy way to find out what synths are supported on mainnet. - // The below list is based on https://sips.synthetix.io/sccp/sccp-190. - [ChainId.Mainnet]: new Map([ - [MAINNET_TOKENS.sAUD, 'sAUD'], - [MAINNET_TOKENS.sBTC, 'sBTC'], - [MAINNET_TOKENS.sCHF, 'sCHF'], - [MAINNET_TOKENS.sETH, 'sETH'], - [MAINNET_TOKENS.sEUR, 'sEUR'], - [MAINNET_TOKENS.sGBP, 'sGBP'], - [MAINNET_TOKENS.sJPY, 'sJPY'], - [MAINNET_TOKENS.sKRW, 'sKRW'], - [MAINNET_TOKENS.sUSD, 'sUSD'], - ]), - // Supported assets can be find through SynthUtil::synthsRates. - // Low liquidity tokens can be excluded. - [ChainId.Optimism]: new Map([ - [OPTIMISM_TOKENS.sAAVE, 'sAAVE'], - [OPTIMISM_TOKENS.sAVAX, 'sAVAX'], - [OPTIMISM_TOKENS.sBTC, 'sBTC'], - [OPTIMISM_TOKENS.sETH, 'sETH'], - [OPTIMISM_TOKENS.sEUR, 'sEUR'], - [OPTIMISM_TOKENS.sLINK, 'sLINK'], - [OPTIMISM_TOKENS.sMATIC, 'sMATIC'], - [OPTIMISM_TOKENS.sSOL, 'sSOL'], - [OPTIMISM_TOKENS.sUNI, 'sUNI'], - [OPTIMISM_TOKENS.sUSD, 'sUSD'], - ]), - }, - new Map(), -); - -export const VIP_ERC20_BRIDGE_SOURCES_BY_CHAIN_ID = valueByChainId( - { - [ChainId.Mainnet]: [ - ERC20BridgeSource.UniswapV2, - ERC20BridgeSource.SushiSwap, - ERC20BridgeSource.UniswapV3, - ERC20BridgeSource.Curve, - ERC20BridgeSource.LiquidityProvider, - ERC20BridgeSource.Native, - ], - [ChainId.BSC]: [ - ERC20BridgeSource.PancakeSwap, - ERC20BridgeSource.PancakeSwapV2, - ERC20BridgeSource.BakerySwap, - ERC20BridgeSource.SushiSwap, - ERC20BridgeSource.ApeSwap, - ERC20BridgeSource.CheeseSwap, - ERC20BridgeSource.LiquidityProvider, - ERC20BridgeSource.Native, - ], - }, - [], -); - -const uniswapV2CloneGasSchedule = (fillData?: FillData) => { - // TODO: Different base cost if to/from ETH. - let gas = 90e3; - const path = (fillData as UniswapV2FillData).tokenAddressPath; - if (path.length > 2) { - gas += (path.length - 2) * 60e3; // +60k for each hop. - } - return gas; -}; - -/** - * Calculated gross gas cost of the underlying exchange. - * The cost of switching from one source to another, assuming - * we are in the middle of a transaction. - * I.e remove the overhead cost of ExchangeProxy (130k) and - * the ethereum transaction cost (21k) - */ -export const DEFAULT_GAS_SCHEDULE: Required = { - [ERC20BridgeSource.Native]: fillData => { - // TODO jacob re-order imports so there is no circular rependency with SignedNativeOrder - const nativeFillData = fillData as { type: FillQuoteTransformerOrderType }; - return nativeFillData && nativeFillData.type === FillQuoteTransformerOrderType.Limit - ? PROTOCOL_FEE_MULTIPLIER.plus(100e3).toNumber() - : // TODO jacob revisit wth v4 LimitOrders - 100e3; - }, - [ERC20BridgeSource.Uniswap]: () => 90e3, - [ERC20BridgeSource.LiquidityProvider]: fillData => { - return (fillData as LiquidityProviderFillData).gasCost || 100e3; - }, - [ERC20BridgeSource.Curve]: fillData => (fillData as CurveFillData).pool.gasSchedule, - [ERC20BridgeSource.CurveV2]: fillData => (fillData as CurveFillData).pool.gasSchedule, - [ERC20BridgeSource.Nerve]: fillData => (fillData as CurveFillData).pool.gasSchedule, - [ERC20BridgeSource.Synapse]: fillData => (fillData as CurveFillData).pool.gasSchedule, - [ERC20BridgeSource.Belt]: fillData => (fillData as CurveFillData).pool.gasSchedule, - [ERC20BridgeSource.Ellipsis]: fillData => (fillData as CurveFillData).pool.gasSchedule, - [ERC20BridgeSource.Saddle]: fillData => (fillData as CurveFillData).pool.gasSchedule, - [ERC20BridgeSource.IronSwap]: fillData => (fillData as CurveFillData).pool.gasSchedule, - [ERC20BridgeSource.XSigma]: fillData => (fillData as CurveFillData).pool.gasSchedule, - [ERC20BridgeSource.FirebirdOneSwap]: fillData => (fillData as CurveFillData).pool.gasSchedule, - [ERC20BridgeSource.MobiusMoney]: fillData => (fillData as CurveFillData).pool.gasSchedule, - [ERC20BridgeSource.MultiBridge]: () => 350e3, - [ERC20BridgeSource.UniswapV2]: uniswapV2CloneGasSchedule, - [ERC20BridgeSource.SushiSwap]: uniswapV2CloneGasSchedule, - [ERC20BridgeSource.CryptoCom]: uniswapV2CloneGasSchedule, - [ERC20BridgeSource.ShibaSwap]: uniswapV2CloneGasSchedule, - [ERC20BridgeSource.BiSwap]: uniswapV2CloneGasSchedule, - [ERC20BridgeSource.MDex]: uniswapV2CloneGasSchedule, - [ERC20BridgeSource.KnightSwap]: uniswapV2CloneGasSchedule, - [ERC20BridgeSource.Balancer]: () => 120e3, - [ERC20BridgeSource.BalancerV2]: (fillData?: FillData) => { - return 100e3 + ((fillData as BalancerV2BatchSwapFillData).swapSteps.length - 1) * 50e3; - }, - [ERC20BridgeSource.MStable]: () => 200e3, - [ERC20BridgeSource.MakerPsm]: (fillData?: FillData) => { - const psmFillData = fillData as MakerPsmFillData; - return psmFillData.takerToken === psmFillData.gemTokenAddress ? 210e3 : 290e3; - }, - [ERC20BridgeSource.Mooniswap]: () => 130e3, - [ERC20BridgeSource.Shell]: () => 170e3, - [ERC20BridgeSource.Component]: () => 188e3, - [ERC20BridgeSource.MultiHop]: (fillData?: FillData) => { - const firstHop = (fillData as MultiHopFillData).firstHopSource; - const secondHop = (fillData as MultiHopFillData).secondHopSource; - const firstHopGas = DEFAULT_GAS_SCHEDULE[firstHop.source](firstHop.fillData); - const secondHopGas = DEFAULT_GAS_SCHEDULE[secondHop.source](secondHop.fillData); - return new BigNumber(firstHopGas) - .plus(secondHopGas) - .plus(30e3) - .toNumber(); - }, - [ERC20BridgeSource.Dodo]: (fillData?: FillData) => { - const isSellBase = (fillData as DODOFillData).isSellBase; - // Sell base is cheaper as it is natively supported - // sell quote requires additional calculation and overhead - return isSellBase ? 180e3 : 300e3; - }, - [ERC20BridgeSource.DodoV2]: (_fillData?: FillData) => 100e3, - [ERC20BridgeSource.Bancor]: (fillData?: FillData) => { - let gas = 200e3; - const path = (fillData as BancorFillData).path; - if (path.length > 2) { - gas += (path.length - 2) * 60e3; // +60k for each hop. - } - return gas; - }, - [ERC20BridgeSource.BancorV3]: () => 250e3, // revisit gas costs with wrap/unwrap - [ERC20BridgeSource.KyberDmm]: (fillData?: FillData) => { - let gas = 170e3; - const path = (fillData as UniswapV2FillData).tokenAddressPath; - if (path.length > 2) { - gas += (path.length - 2) * 65e3; // +65k for each hop. - } - return gas; - }, - [ERC20BridgeSource.UniswapV3]: (fillData?: FillData) => { - const uniFillData = fillData as UniswapV3FillData | FinalUniswapV3FillData; - // NOTE: This base value was heuristically chosen by looking at how much it generally - // underestimated gas usage - const base = 34e3; // 34k base - let gas = base; - if (isFinalUniswapV3FillData(uniFillData)) { - gas += uniFillData.gasUsed; - } else { - // NOTE: We don't actually know which of the paths would be used in the router - // therefore we estimate using the median of gas usage returned from UniswapV3 - // For the best case scenario (least amount of hops & ticks) this will - // overestimate the gas usage - const pathAmountsWithGasUsed = uniFillData.pathAmounts.filter(p => p.gasUsed > 0); - const medianGasUsedForPath = - pathAmountsWithGasUsed[Math.floor(pathAmountsWithGasUsed.length / 2)]?.gasUsed ?? 0; - gas += medianGasUsedForPath; - } - - // If we for some reason could not read `gasUsed` when sampling - // fall back to legacy gas estimation - if (gas === base) { - gas = 100e3; - const path = uniFillData.tokenAddressPath; - if (path.length > 2) { - gas += (path.length - 2) * 32e3; // +32k for each hop. - } - } - - return gas; - }, - [ERC20BridgeSource.Lido]: (fillData?: FillData) => { - const lidoFillData = fillData as LidoFillData; - const wethAddress = NATIVE_FEE_TOKEN_BY_CHAIN_ID[ChainId.Mainnet]; - // WETH -> stETH - if (lidoFillData.takerToken === wethAddress) { - return 226e3; - } else if (lidoFillData.takerToken === lidoFillData.stEthTokenAddress) { - return 120e3; - } else { - return 95e3; - } - }, - [ERC20BridgeSource.AaveV2]: (fillData?: FillData) => { - const aaveFillData = fillData as AaveV2FillData; - // NOTE: The Aave deposit method is more expensive than the withdraw - return aaveFillData.takerToken === aaveFillData.underlyingToken ? 400e3 : 300e3; - }, - [ERC20BridgeSource.Geist]: (fillData?: FillData) => { - const geistFillData = fillData as GeistFillData; - return geistFillData.takerToken === geistFillData.underlyingToken ? 400e3 : 300e3; - }, - [ERC20BridgeSource.Compound]: (fillData?: FillData) => { - // NOTE: cETH is handled differently than other cTokens - const wethAddress = NATIVE_FEE_TOKEN_BY_CHAIN_ID[ChainId.Mainnet]; - const compoundFillData = fillData as CompoundFillData; - if (compoundFillData.takerToken === compoundFillData.cToken) { - return compoundFillData.makerToken === wethAddress ? 120e3 : 150e3; - } else { - return compoundFillData.takerToken === wethAddress ? 210e3 : 250e3; - } - }, - [ERC20BridgeSource.Synthetix]: (fillData?: FillData) => { - const { chainId, makerTokenSymbolBytes32, takerTokenSymbolBytes32 } = fillData as SynthetixFillData; - const makerTokenSymbol = parseBytes32String(makerTokenSymbolBytes32); - const takerTokenSymbol = parseBytes32String(takerTokenSymbolBytes32); - - // Gas cost widely varies by token on mainnet. - if (chainId === ChainId.Mainnet) { - if (takerTokenSymbol === 'sBTC' || makerTokenSymbol === 'sBTC') { - return 800e3; - } - if (takerTokenSymbol === 'sETH' || makerTokenSymbol === 'sETH') { - return 700e3; - } - return 580e3; - } - - // Optimism - if (takerTokenSymbol === 'sUSD' || makerTokenSymbol === 'sUSD') { - return 480e3; - } - return 580e3; - }, - // - // BSC - // - [ERC20BridgeSource.PancakeSwap]: uniswapV2CloneGasSchedule, - [ERC20BridgeSource.PancakeSwapV2]: uniswapV2CloneGasSchedule, - [ERC20BridgeSource.BakerySwap]: uniswapV2CloneGasSchedule, - [ERC20BridgeSource.ApeSwap]: uniswapV2CloneGasSchedule, - [ERC20BridgeSource.CheeseSwap]: uniswapV2CloneGasSchedule, - [ERC20BridgeSource.WaultSwap]: uniswapV2CloneGasSchedule, - [ERC20BridgeSource.ACryptos]: fillData => (fillData as CurveFillData).pool.gasSchedule, - [ERC20BridgeSource.WOOFi]: (fillData?: FillData) => { - const woofiFillData = fillData as WOOFiFillData; - const quoteTokenAddresses = [BSC_TOKENS.USDT, AVALANCHE_TOKENS.nUSDC, FANTOM_TOKENS.USDC, POLYGON_TOKENS.USDC]; - const hasQuoteToken = - quoteTokenAddresses.includes(woofiFillData.takerToken) || - quoteTokenAddresses.includes(woofiFillData.makerToken); - if (woofiFillData.chainId === ChainId.BSC) { - if (hasQuoteToken) { - return 550e3; - } else { - return 100e4; - } - } else if (woofiFillData.chainId === ChainId.Avalanche) { - if (hasQuoteToken) { - return 300e3; - } else { - return 550e3; - } - } else if (woofiFillData.chainId === ChainId.Polygon) { - if (hasQuoteToken) { - return 500e3; - } else { - return 700e3; - } - } else { - // Fantom - if (hasQuoteToken) { - return 400e3; - } else { - return 600e3; - } - } - }, - // - // Polygon - // - [ERC20BridgeSource.QuickSwap]: uniswapV2CloneGasSchedule, - [ERC20BridgeSource.Dfyn]: uniswapV2CloneGasSchedule, - [ERC20BridgeSource.MeshSwap]: uniswapV2CloneGasSchedule, - - // - // Avalanche - // - [ERC20BridgeSource.Pangolin]: uniswapV2CloneGasSchedule, - [ERC20BridgeSource.TraderJoe]: uniswapV2CloneGasSchedule, - [ERC20BridgeSource.GMX]: () => 450e3, - [ERC20BridgeSource.Platypus]: () => 450e3, - - // - // Celo - // - [ERC20BridgeSource.UbeSwap]: uniswapV2CloneGasSchedule, - - // - // Fantom - // - [ERC20BridgeSource.MorpheusSwap]: uniswapV2CloneGasSchedule, - [ERC20BridgeSource.SpiritSwap]: uniswapV2CloneGasSchedule, - [ERC20BridgeSource.SpookySwap]: uniswapV2CloneGasSchedule, - [ERC20BridgeSource.Yoshi]: uniswapV2CloneGasSchedule, - [ERC20BridgeSource.Beethovenx]: () => 100e3, - - // - // Optimism - // - [ERC20BridgeSource.Velodrome]: () => 160e3, -}; - -export const DEFAULT_FEE_SCHEDULE: Required = Object.keys(DEFAULT_GAS_SCHEDULE).reduce((acc, key) => { - acc[key as ERC20BridgeSource] = (fillData: FillData) => { - return { - gas: DEFAULT_GAS_SCHEDULE[key as ERC20BridgeSource](fillData), - fee: ZERO_AMOUNT, - }; - }; - return acc; - // tslint:disable-next-line:no-object-literal-type-assertion -}, {} as Required); - -export const POSITIVE_SLIPPAGE_FEE_TRANSFORMER_GAS = new BigNumber(20000); - -export const DEFAULT_FEE_ESTIMATE = { gas: 0, fee: ZERO_AMOUNT }; - -export const DEFAULT_GET_MARKET_ORDERS_OPTS: Omit = { - runLimit: 2 ** 15, - excludedSources: [], - excludedFeeSources: [], - includedSources: [], - bridgeSlippage: 0.005, - maxFallbackSlippage: 0.05, - numSamples: 13, - sampleDistributionBase: 1.05, - feeSchedule: DEFAULT_FEE_SCHEDULE, - gasSchedule: DEFAULT_GAS_SCHEDULE, - exchangeProxyOverhead: () => ZERO_AMOUNT, - allowFallback: true, - shouldGenerateQuoteReport: true, - shouldIncludePriceComparisonsReport: false, - tokenAdjacencyGraph: TokenAdjacencyGraph.getEmptyGraph(), - neonRouterNumSamples: 14, - fillAdjustor: new IdentityFillAdjustor(), -}; diff --git a/packages/asset-swapper/src/utils/market_operation_utils/fills.ts b/packages/asset-swapper/src/utils/market_operation_utils/fills.ts deleted file mode 100644 index a57ee022f2..0000000000 --- a/packages/asset-swapper/src/utils/market_operation_utils/fills.ts +++ /dev/null @@ -1,133 +0,0 @@ -import { FillQuoteTransformerOrderType } from '@0x/protocol-utils'; -import { BigNumber, hexUtils } from '@0x/utils'; - -import { MarketOperation, NativeOrderWithFillableAmounts } from '../../types'; - -import { DEFAULT_FEE_ESTIMATE, POSITIVE_INF, SOURCE_FLAGS } from './constants'; -import { DexSample, ERC20BridgeSource, FeeSchedule, Fill } from './types'; - -// tslint:disable: prefer-for-of no-bitwise completed-docs - -/** - * Converts the ETH value to an amount in output tokens. - * - * By default this prefers the outputAmountPerEth, but if this value - * is zero it will utilize the inputAmountPerEth and input. - */ -export function ethToOutputAmount({ - input, - output, - ethAmount, - inputAmountPerEth, - outputAmountPerEth, -}: { - input: BigNumber; - output: BigNumber; - inputAmountPerEth: BigNumber; - outputAmountPerEth: BigNumber; - ethAmount: BigNumber | number; -}): BigNumber { - return !outputAmountPerEth.isZero() - ? outputAmountPerEth.times(ethAmount).integerValue() - : inputAmountPerEth.times(ethAmount).times(output.dividedToIntegerBy(input)); -} - -export function nativeOrderToFill( - side: MarketOperation, - order: NativeOrderWithFillableAmounts, - targetInput: BigNumber = POSITIVE_INF, - outputAmountPerEth: BigNumber, - inputAmountPerEth: BigNumber, - fees: FeeSchedule, - filterNegativeAdjustedRateOrders: boolean = true, -): Fill | undefined { - const sourcePathId = hexUtils.random(); - // Create a single path from all orders. - const { fillableTakerAmount, fillableTakerFeeAmount, fillableMakerAmount, type } = order; - const makerAmount = fillableMakerAmount; - const takerAmount = fillableTakerAmount.plus(fillableTakerFeeAmount); - const input = side === MarketOperation.Sell ? takerAmount : makerAmount; - const output = side === MarketOperation.Sell ? makerAmount : takerAmount; - const { fee, gas } = - fees[ERC20BridgeSource.Native] === undefined ? DEFAULT_FEE_ESTIMATE : fees[ERC20BridgeSource.Native]!(order); - const outputPenalty = ethToOutputAmount({ - input, - output, - inputAmountPerEth, - outputAmountPerEth, - ethAmount: fee, - }); - // targetInput can be less than the order size - // whilst the penalty is constant, it affects the adjusted output - // only up until the target has been exhausted. - // A large order and an order at the exact target should be penalized - // the same. - const clippedInput = BigNumber.min(targetInput, input); - // scale the clipped output inline with the input - const clippedOutput = clippedInput.dividedBy(input).times(output); - const adjustedOutput = - side === MarketOperation.Sell ? clippedOutput.minus(outputPenalty) : clippedOutput.plus(outputPenalty); - const adjustedRate = - side === MarketOperation.Sell ? adjustedOutput.div(clippedInput) : clippedInput.div(adjustedOutput); - // Optionally skip orders with rates that are <= 0. - if (filterNegativeAdjustedRateOrders && adjustedRate.lte(0)) { - return undefined; - } - - return { - sourcePathId, - adjustedOutput, - input: clippedInput, - output: clippedOutput, - flags: SOURCE_FLAGS[type === FillQuoteTransformerOrderType.Rfq ? 'RfqOrder' : 'LimitOrder'], - source: ERC20BridgeSource.Native, - type, - fillData: { ...order }, - gas, - }; -} - -export function dexSampleToFill( - side: MarketOperation, - sample: DexSample, - outputAmountPerEth: BigNumber, - inputAmountPerEth: BigNumber, - fees: FeeSchedule, -): Fill { - const sourcePathId = hexUtils.random(); - const { source, fillData } = sample; - const input = sample.input; - const output = sample.output; - const { fee, gas } = - fees[source] === undefined ? DEFAULT_FEE_ESTIMATE : fees[source]!(sample.fillData) || DEFAULT_FEE_ESTIMATE; - - const penalty = ethToOutputAmount({ - input, - output, - inputAmountPerEth, - outputAmountPerEth, - ethAmount: fee, - }); - - return { - sourcePathId, - input, - output, - adjustedOutput: adjustOutput(side, output, penalty), - source, - fillData, - type: FillQuoteTransformerOrderType.Bridge, - flags: SOURCE_FLAGS[source], - gas, - }; -} - -/** - * Adjusts the output depending on whether this is a buy or a sell. - * - * If it is a sell, than output is lowered by the adjustment. - * If it is a buy, than output is increased by adjustment. - */ -export function adjustOutput(side: MarketOperation, output: BigNumber, penalty: BigNumber): BigNumber { - return side === MarketOperation.Sell ? output.minus(penalty) : output.plus(penalty); -} diff --git a/packages/asset-swapper/src/utils/market_operation_utils/geist_utils.ts b/packages/asset-swapper/src/utils/market_operation_utils/geist_utils.ts deleted file mode 100644 index db572c803e..0000000000 --- a/packages/asset-swapper/src/utils/market_operation_utils/geist_utils.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { FANTOM_TOKENS, GEIST_FANTOM_POOLS } from './constants'; -import { GeistInfo } from './types'; - -const gTokenToUnderlyingToken = new Map([ - [FANTOM_TOKENS.gFTM, FANTOM_TOKENS.WFTM], - [FANTOM_TOKENS.gfUSDT, FANTOM_TOKENS.fUSDT], - [FANTOM_TOKENS.gDAI, FANTOM_TOKENS.DAI], - [FANTOM_TOKENS.gUSDC, FANTOM_TOKENS.USDC], - [FANTOM_TOKENS.gETH, FANTOM_TOKENS.WETH], - [FANTOM_TOKENS.gWBTC, FANTOM_TOKENS.WBTC], - [FANTOM_TOKENS.gCRV, FANTOM_TOKENS.WCRV], - [FANTOM_TOKENS.gMIM, FANTOM_TOKENS.MIM], -]); - -/** - * Returns GeistInfo for a certain pair if that pair exists on Geist - */ -export function getGeistInfoForPair(takerToken: string, makerToken: string): GeistInfo | undefined { - let gToken; - let underlyingToken; - if (gTokenToUnderlyingToken.get(takerToken) === makerToken) { - gToken = takerToken; - underlyingToken = makerToken; - } else if (gTokenToUnderlyingToken.get(makerToken) === takerToken) { - gToken = makerToken; - underlyingToken = takerToken; - } else { - return undefined; - } - - return { - lendingPool: GEIST_FANTOM_POOLS.lendingPool, - gToken, - underlyingToken, - }; -} diff --git a/packages/asset-swapper/src/utils/market_operation_utils/identity_fill_adjustor.ts b/packages/asset-swapper/src/utils/market_operation_utils/identity_fill_adjustor.ts deleted file mode 100644 index fafba4afc7..0000000000 --- a/packages/asset-swapper/src/utils/market_operation_utils/identity_fill_adjustor.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { BigNumber } from '@0x/utils'; - -import { MarketOperation } from '../../types'; - -import { Fill, FillAdjustor } from './types'; - -// tslint:disable:prefer-function-over-method - -export class IdentityFillAdjustor implements FillAdjustor { - public adjustFills(side: MarketOperation, fills: Fill[], amount: BigNumber): Fill[] { - return fills; - } -} diff --git a/packages/asset-swapper/src/utils/market_operation_utils/index.ts b/packages/asset-swapper/src/utils/market_operation_utils/index.ts deleted file mode 100644 index ea85a759d0..0000000000 --- a/packages/asset-swapper/src/utils/market_operation_utils/index.ts +++ /dev/null @@ -1,870 +0,0 @@ -import { FillQuoteTransformerOrderType, RfqOrder } from '@0x/protocol-utils'; -import { BigNumber, NULL_ADDRESS } from '@0x/utils'; -import * as _ from 'lodash'; - -import { DEFAULT_INFO_LOGGER, INVALID_SIGNATURE } from '../../constants'; -import { - AltRfqMakerAssetOfferings, - AssetSwapperContractAddresses, - MarketOperation, - NativeOrderWithFillableAmounts, - SignedNativeOrder, -} from '../../types'; -import { getAltMarketInfo } from '../alt_mm_implementation_utils'; -import { QuoteRequestor, V4RFQIndicativeQuoteMM } from '../quote_requestor'; -import { toSignedNativeOrder } from '../rfq_client_mappers'; -import { - getNativeAdjustedFillableAmountsFromMakerAmount, - getNativeAdjustedFillableAmountsFromTakerAmount, - getNativeAdjustedMakerFillAmount, -} from '../utils'; - -import { - dexSampleToReportSource, - ExtendedQuoteReportSources, - generateExtendedQuoteReportSources, - generateQuoteReport, - multiHopSampleToReportSource, - nativeOrderToReportEntry, - PriceComparisonsReport, - QuoteReport, -} from './../quote_report_generator'; -import { getComparisonPrices } from './comparison_price'; -import { - BUY_SOURCE_FILTER_BY_CHAIN_ID, - DEFAULT_GET_MARKET_ORDERS_OPTS, - FEE_QUOTE_SOURCES_BY_CHAIN_ID, - NATIVE_FEE_TOKEN_AMOUNT_BY_CHAIN_ID, - NATIVE_FEE_TOKEN_BY_CHAIN_ID, - SELL_SOURCE_FILTER_BY_CHAIN_ID, - SOURCE_FLAGS, - ZERO_AMOUNT, -} from './constants'; -import { IdentityFillAdjustor } from './identity_fill_adjustor'; -import { getBestTwoHopQuote } from './multihop_utils'; -import { createOrdersFromTwoHopSample } from './orders'; -import { Path, PathPenaltyOpts } from './path'; -import { findOptimalPathFromSamples } from './path_optimizer'; -import { DexOrderSampler, getSampleAmounts } from './sampler'; -import { SourceFilters } from './source_filters'; -import { - AggregationError, - DexSample, - ERC20BridgeSource, - GenerateOptimizedOrdersOpts, - GetMarketOrdersOpts, - MarketSideLiquidity, - OptimizerResult, - OptimizerResultWithReport, - OrderDomain, -} from './types'; - -// tslint:disable:boolean-naming - -export class MarketOperationUtils { - private readonly _sellSources: SourceFilters; - private readonly _buySources: SourceFilters; - private readonly _feeSources: SourceFilters; - private readonly _nativeFeeToken: string; - private readonly _nativeFeeTokenAmount: BigNumber; - - private static _computeQuoteReport( - quoteRequestor: QuoteRequestor | undefined, - marketSideLiquidity: MarketSideLiquidity, - optimizerResult: OptimizerResult, - comparisonPrice?: BigNumber | undefined, - ): QuoteReport { - const { side, quotes } = marketSideLiquidity; - const { liquidityDelivered } = optimizerResult; - return generateQuoteReport(side, quotes.nativeOrders, liquidityDelivered, comparisonPrice, quoteRequestor); - } - - private static _computeExtendedQuoteReportSources( - quoteRequestor: QuoteRequestor | undefined, - marketSideLiquidity: MarketSideLiquidity, - amount: BigNumber, - optimizerResult: OptimizerResult, - comparisonPrice?: BigNumber | undefined, - ): ExtendedQuoteReportSources { - const { side, quotes } = marketSideLiquidity; - const { liquidityDelivered } = optimizerResult; - return generateExtendedQuoteReportSources( - side, - quotes, - liquidityDelivered, - amount, - comparisonPrice, - quoteRequestor, - ); - } - - private static _computePriceComparisonsReport( - quoteRequestor: QuoteRequestor | undefined, - marketSideLiquidity: MarketSideLiquidity, - comparisonPrice?: BigNumber | undefined, - ): PriceComparisonsReport { - const { side, quotes } = marketSideLiquidity; - const dexSources = _.flatten(quotes.dexQuotes).map(quote => dexSampleToReportSource(quote, side)); - const multiHopSources = quotes.twoHopQuotes.map(quote => multiHopSampleToReportSource(quote, side)); - const nativeSources = quotes.nativeOrders.map(order => - nativeOrderToReportEntry( - order.type, - order as any, - order.fillableTakerAmount, - comparisonPrice, - quoteRequestor, - ), - ); - - return { dexSources, multiHopSources, nativeSources }; - } - - constructor( - private readonly _sampler: DexOrderSampler, - private readonly contractAddresses: AssetSwapperContractAddresses, - private readonly _orderDomain: OrderDomain, - ) { - this._buySources = BUY_SOURCE_FILTER_BY_CHAIN_ID[_sampler.chainId]; - this._sellSources = SELL_SOURCE_FILTER_BY_CHAIN_ID[_sampler.chainId]; - this._feeSources = new SourceFilters(FEE_QUOTE_SOURCES_BY_CHAIN_ID[_sampler.chainId]); - this._nativeFeeToken = NATIVE_FEE_TOKEN_BY_CHAIN_ID[_sampler.chainId]; - this._nativeFeeTokenAmount = NATIVE_FEE_TOKEN_AMOUNT_BY_CHAIN_ID[_sampler.chainId]; - } - - /** - * Gets the liquidity available for a market sell operation - * @param nativeOrders Native orders. Assumes LimitOrders not RfqOrders - * @param takerAmount Amount of taker asset to sell. - * @param opts Options object. - * @return MarketSideLiquidity. - */ - public async getMarketSellLiquidityAsync( - nativeOrders: SignedNativeOrder[], - takerAmount: BigNumber, - opts?: Partial, - ): Promise { - const _opts = { ...DEFAULT_GET_MARKET_ORDERS_OPTS, ...opts }; - const { makerToken, takerToken } = nativeOrders[0].order; - const sampleAmounts = getSampleAmounts(takerAmount, _opts.numSamples, _opts.sampleDistributionBase); - - const requestFilters = new SourceFilters().exclude(_opts.excludedSources).include(_opts.includedSources); - const quoteSourceFilters = this._sellSources.merge(requestFilters); - const feeSourceFilters = this._feeSources.exclude(_opts.excludedFeeSources); - - // Used to determine whether the tx origin is an EOA or a contract - const txOrigin = (_opts.rfqt && _opts.rfqt.txOrigin) || NULL_ADDRESS; - - // Call the sampler contract. - const samplerPromise = this._sampler.executeAsync( - this._sampler.getBlockNumber(), - this._sampler.getGasLeft(), - this._sampler.getTokenDecimals([makerToken, takerToken]), - // Get native order fillable amounts. - this._sampler.getLimitOrderFillableTakerAmounts(nativeOrders, this.contractAddresses.exchangeProxy), - // Get ETH -> maker token price. - this._sampler.getBestNativeTokenSellRate( - feeSourceFilters.sources, - makerToken, - this._nativeFeeToken, - this._nativeFeeTokenAmount, - _opts.feeSchedule, - ), - // Get ETH -> taker token price. - this._sampler.getBestNativeTokenSellRate( - feeSourceFilters.sources, - takerToken, - this._nativeFeeToken, - this._nativeFeeTokenAmount, - _opts.feeSchedule, - ), - // Get sell quotes for taker -> maker. - this._sampler.getSellQuotes(quoteSourceFilters.sources, makerToken, takerToken, sampleAmounts), - this._sampler.getTwoHopSellQuotes( - quoteSourceFilters.isAllowed(ERC20BridgeSource.MultiHop) ? quoteSourceFilters.sources : [], - makerToken, - takerToken, - takerAmount, - ), - this._sampler.isAddressContract(txOrigin), - this._sampler.getGasLeft(), - ); - - // Refresh the cached pools asynchronously if required - void this._refreshPoolCacheIfRequiredAsync(takerToken, makerToken); - - const [ - [ - blockNumber, - gasBefore, - tokenDecimals, - orderFillableTakerAmounts, - outputAmountPerEth, - inputAmountPerEth, - dexQuotes, - rawTwoHopQuotes, - isTxOriginContract, - gasAfter, - ], - ] = await Promise.all([samplerPromise]); - - // Log the gas metrics - _opts.samplerMetrics?.logGasDetails({ gasBefore, gasAfter }); - _opts.samplerMetrics?.logBlockNumber(blockNumber); - - // Filter out any invalid two hop quotes where we couldn't find a route - const twoHopQuotes = rawTwoHopQuotes.filter( - q => q && q.fillData && q.fillData.firstHopSource && q.fillData.secondHopSource, - ); - - const [makerTokenDecimals, takerTokenDecimals] = tokenDecimals; - - const isRfqSupported = !!(_opts.rfqt && !isTxOriginContract); - const limitOrdersWithFillableAmounts = nativeOrders.map((order, i) => ({ - ...order, - ...getNativeAdjustedFillableAmountsFromTakerAmount(order, orderFillableTakerAmounts[i]), - })); - - return { - side: MarketOperation.Sell, - inputAmount: takerAmount, - inputToken: takerToken, - outputToken: makerToken, - outputAmountPerEth, - inputAmountPerEth, - quoteSourceFilters, - makerTokenDecimals: makerTokenDecimals.toNumber(), - takerTokenDecimals: takerTokenDecimals.toNumber(), - quotes: { - nativeOrders: limitOrdersWithFillableAmounts, - rfqtIndicativeQuotes: [], - twoHopQuotes, - dexQuotes, - }, - isRfqSupported, - blockNumber: blockNumber.toNumber(), - }; - } - - /** - * Gets the liquidity available for a market buy operation - * @param nativeOrders Native orders. Assumes LimitOrders not RfqOrders - * @param makerAmount Amount of maker asset to buy. - * @param opts Options object. - * @return MarketSideLiquidity. - */ - public async getMarketBuyLiquidityAsync( - nativeOrders: SignedNativeOrder[], - makerAmount: BigNumber, - opts?: Partial, - ): Promise { - const _opts = { ...DEFAULT_GET_MARKET_ORDERS_OPTS, ...opts }; - const { makerToken, takerToken } = nativeOrders[0].order; - const sampleAmounts = getSampleAmounts(makerAmount, _opts.numSamples, _opts.sampleDistributionBase); - - const requestFilters = new SourceFilters().exclude(_opts.excludedSources).include(_opts.includedSources); - const quoteSourceFilters = this._buySources.merge(requestFilters); - const feeSourceFilters = this._feeSources.exclude(_opts.excludedFeeSources); - - // Used to determine whether the tx origin is an EOA or a contract - const txOrigin = (_opts.rfqt && _opts.rfqt.txOrigin) || NULL_ADDRESS; - - // Call the sampler contract. - const samplerPromise = this._sampler.executeAsync( - this._sampler.getBlockNumber(), - this._sampler.getTokenDecimals([makerToken, takerToken]), - // Get native order fillable amounts. - this._sampler.getLimitOrderFillableMakerAmounts(nativeOrders, this.contractAddresses.exchangeProxy), - // Get ETH -> makerToken token price. - this._sampler.getBestNativeTokenSellRate( - feeSourceFilters.sources, - makerToken, - this._nativeFeeToken, - this._nativeFeeTokenAmount, - _opts.feeSchedule, - ), - // Get ETH -> taker token price. - this._sampler.getBestNativeTokenSellRate( - feeSourceFilters.sources, - takerToken, - this._nativeFeeToken, - this._nativeFeeTokenAmount, - _opts.feeSchedule, - ), - // Get buy quotes for taker -> maker. - this._sampler.getBuyQuotes(quoteSourceFilters.sources, makerToken, takerToken, sampleAmounts), - this._sampler.getTwoHopBuyQuotes( - quoteSourceFilters.isAllowed(ERC20BridgeSource.MultiHop) ? quoteSourceFilters.sources : [], - makerToken, - takerToken, - makerAmount, - ), - this._sampler.isAddressContract(txOrigin), - ); - - // Refresh the cached pools asynchronously if required - void this._refreshPoolCacheIfRequiredAsync(takerToken, makerToken); - - const [ - [ - blockNumber, - tokenDecimals, - orderFillableMakerAmounts, - ethToMakerAssetRate, - ethToTakerAssetRate, - dexQuotes, - rawTwoHopQuotes, - isTxOriginContract, - ], - ] = await Promise.all([samplerPromise]); - - // Filter out any invalid two hop quotes where we couldn't find a route - const twoHopQuotes = rawTwoHopQuotes.filter( - q => q && q.fillData && q.fillData.firstHopSource && q.fillData.secondHopSource, - ); - - const [makerTokenDecimals, takerTokenDecimals] = tokenDecimals; - const isRfqSupported = !isTxOriginContract; - - const limitOrdersWithFillableAmounts = nativeOrders.map((order, i) => ({ - ...order, - ...getNativeAdjustedFillableAmountsFromMakerAmount(order, orderFillableMakerAmounts[i]), - })); - - return { - side: MarketOperation.Buy, - inputAmount: makerAmount, - inputToken: makerToken, - outputToken: takerToken, - outputAmountPerEth: ethToTakerAssetRate, - inputAmountPerEth: ethToMakerAssetRate, - quoteSourceFilters, - makerTokenDecimals: makerTokenDecimals.toNumber(), - takerTokenDecimals: takerTokenDecimals.toNumber(), - quotes: { - nativeOrders: limitOrdersWithFillableAmounts, - rfqtIndicativeQuotes: [], - twoHopQuotes, - dexQuotes, - }, - isRfqSupported, - blockNumber: blockNumber.toNumber(), - }; - } - - /** - * gets the orders required for a batch of market buy operations by (potentially) merging native orders with - * generated bridge orders. - * - * NOTE: Currently `getBatchMarketBuyOrdersAsync()` does not support external liquidity providers. - * - * @param batchNativeOrders Batch of Native orders. Assumes LimitOrders not RfqOrders - * @param makerAmounts Array amount of maker asset to buy for each batch. - * @param opts Options object. - * @return orders. - */ - public async getBatchMarketBuyOrdersAsync( - batchNativeOrders: SignedNativeOrder[][], - makerAmounts: BigNumber[], - opts: Partial & { gasPrice: BigNumber }, - ): Promise> { - if (batchNativeOrders.length === 0) { - throw new Error(AggregationError.EmptyOrders); - } - const _opts: GetMarketOrdersOpts = { ...DEFAULT_GET_MARKET_ORDERS_OPTS, ...opts }; - - const requestFilters = new SourceFilters().exclude(_opts.excludedSources).include(_opts.includedSources); - const quoteSourceFilters = this._buySources.merge(requestFilters); - - const feeSourceFilters = this._feeSources.exclude(_opts.excludedFeeSources); - - const ops = [ - this._sampler.getBlockNumber(), - ...batchNativeOrders.map(orders => - this._sampler.getLimitOrderFillableMakerAmounts(orders, this.contractAddresses.exchangeProxy), - ), - ...batchNativeOrders.map(orders => - this._sampler.getBestNativeTokenSellRate( - feeSourceFilters.sources, - orders[0].order.takerToken, - this._nativeFeeToken, - this._nativeFeeTokenAmount, - _opts.feeSchedule, - ), - ), - ...batchNativeOrders.map((orders, i) => - this._sampler.getBuyQuotes( - quoteSourceFilters.sources, - orders[0].order.makerToken, - orders[0].order.takerToken, - [makerAmounts[i]], - ), - ), - ...batchNativeOrders.map(orders => - this._sampler.getTokenDecimals([orders[0].order.makerToken, orders[0].order.takerToken]), - ), - ]; - - const [blockNumberRaw, ...executeResults] = await this._sampler.executeBatchAsync(ops); - const batchOrderFillableMakerAmounts = executeResults.splice(0, batchNativeOrders.length) as BigNumber[][]; - const batchEthToTakerAssetRate = executeResults.splice(0, batchNativeOrders.length) as BigNumber[]; - const batchDexQuotes = executeResults.splice(0, batchNativeOrders.length) as DexSample[][][]; - const batchTokenDecimals = executeResults.splice(0, batchNativeOrders.length) as number[][]; - const inputAmountPerEth = ZERO_AMOUNT; - - const blockNumber: number = (blockNumberRaw as BigNumber).toNumber(); - - return Promise.all( - batchNativeOrders.map(async (nativeOrders, i) => { - if (nativeOrders.length === 0) { - throw new Error(AggregationError.EmptyOrders); - } - const { makerToken, takerToken } = nativeOrders[0].order; - const orderFillableMakerAmounts = batchOrderFillableMakerAmounts[i]; - const outputAmountPerEth = batchEthToTakerAssetRate[i]; - const dexQuotes = batchDexQuotes[i]; - const makerAmount = makerAmounts[i]; - try { - const optimizerResult = await this._generateOptimizedOrdersAsync( - { - side: MarketOperation.Buy, - inputToken: makerToken, - outputToken: takerToken, - inputAmount: makerAmount, - outputAmountPerEth, - inputAmountPerEth, - quoteSourceFilters, - makerTokenDecimals: batchTokenDecimals[i][0], - takerTokenDecimals: batchTokenDecimals[i][1], - quotes: { - nativeOrders: nativeOrders.map((o, k) => ({ - ...o, - ...getNativeAdjustedFillableAmountsFromMakerAmount(o, orderFillableMakerAmounts[k]), - })), - dexQuotes, - rfqtIndicativeQuotes: [], - twoHopQuotes: [], - }, - isRfqSupported: false, - blockNumber, - }, - { - bridgeSlippage: _opts.bridgeSlippage, - maxFallbackSlippage: _opts.maxFallbackSlippage, - excludedSources: _opts.excludedSources, - feeSchedule: _opts.feeSchedule, - allowFallback: _opts.allowFallback, - gasPrice: _opts.gasPrice, - neonRouterNumSamples: _opts.neonRouterNumSamples, - fillAdjustor: _opts.fillAdjustor, - }, - ); - return optimizerResult; - } catch (e) { - // It's possible for one of the pairs to have no path - // rather than throw NO_OPTIMAL_PATH we return undefined - return undefined; - } - }), - ); - } - - public async _generateOptimizedOrdersAsync( - marketSideLiquidity: MarketSideLiquidity, - opts: GenerateOptimizedOrdersOpts, - ): Promise { - const { - inputToken, - outputToken, - side, - inputAmount, - quotes, - outputAmountPerEth, - inputAmountPerEth, - } = marketSideLiquidity; - const { nativeOrders, rfqtIndicativeQuotes, dexQuotes } = quotes; - - const orderOpts = { - side, - inputToken, - outputToken, - orderDomain: this._orderDomain, - contractAddresses: this.contractAddresses, - bridgeSlippage: opts.bridgeSlippage || 0, - }; - - const augmentedRfqtIndicativeQuotes: NativeOrderWithFillableAmounts[] = rfqtIndicativeQuotes.map( - q => - // tslint:disable-next-line: no-object-literal-type-assertion - ({ - order: { ...new RfqOrder({ ...q }) }, - signature: INVALID_SIGNATURE, - fillableMakerAmount: new BigNumber(q.makerAmount), - fillableTakerAmount: new BigNumber(q.takerAmount), - fillableTakerFeeAmount: ZERO_AMOUNT, - type: FillQuoteTransformerOrderType.Rfq, - } as NativeOrderWithFillableAmounts), - ); - - // Find the optimal path. - const penaltyOpts: PathPenaltyOpts = { - outputAmountPerEth, - inputAmountPerEth, - exchangeProxyOverhead: opts.exchangeProxyOverhead || (() => ZERO_AMOUNT), - gasPrice: opts.gasPrice, - }; - - // NOTE: For sell quotes input is the taker asset and for buy quotes input is the maker asset - const takerAmountPerEth = side === MarketOperation.Sell ? inputAmountPerEth : outputAmountPerEth; - const makerAmountPerEth = side === MarketOperation.Sell ? outputAmountPerEth : inputAmountPerEth; - - // Find the optimal path using Rust router if enabled, otherwise fallback to JS Router - let optimalPath: Path | undefined; - optimalPath = findOptimalPathFromSamples( - side, - dexQuotes, - [...nativeOrders, ...augmentedRfqtIndicativeQuotes], - inputAmount, - penaltyOpts, - opts.feeSchedule, - this._sampler.chainId, - opts.neonRouterNumSamples, - opts.fillAdjustor, - opts.samplerMetrics, - ); - - const optimalPathAdjustedRate = optimalPath ? optimalPath.adjustedRate() : ZERO_AMOUNT; - - const { adjustedRate: bestTwoHopAdjustedRate, quote: bestTwoHopQuote } = getBestTwoHopQuote( - marketSideLiquidity, - opts.feeSchedule, - opts.exchangeProxyOverhead, - opts.fillAdjustor, - ); - - if (bestTwoHopQuote && bestTwoHopAdjustedRate.isGreaterThan(optimalPathAdjustedRate)) { - const twoHopOrders = createOrdersFromTwoHopSample(bestTwoHopQuote, orderOpts); - return { - optimizedOrders: twoHopOrders, - liquidityDelivered: bestTwoHopQuote, - sourceFlags: SOURCE_FLAGS[ERC20BridgeSource.MultiHop], - marketSideLiquidity, - adjustedRate: bestTwoHopAdjustedRate, - takerAmountPerEth, - makerAmountPerEth, - }; - } - - // If there is no optimal path AND we didn't return a MultiHop quote, then throw - if (optimalPath === undefined) { - throw new Error(AggregationError.NoOptimalPath); - } - - const finalizedPath = optimalPath.finalize(orderOpts); - - return { - optimizedOrders: finalizedPath.orders, - liquidityDelivered: finalizedPath.fills, - sourceFlags: finalizedPath.sourceFlags, - marketSideLiquidity, - adjustedRate: optimalPathAdjustedRate, - takerAmountPerEth, - makerAmountPerEth, - }; - } - - /** - * @param nativeOrders: Assumes LimitOrders not RfqOrders - */ - public async getOptimizerResultAsync( - nativeOrders: SignedNativeOrder[], - amount: BigNumber, - side: MarketOperation, - opts: Partial & { gasPrice: BigNumber }, - ): Promise { - const _opts: GetMarketOrdersOpts = { ...DEFAULT_GET_MARKET_ORDERS_OPTS, ...opts }; - const optimizerOpts: GenerateOptimizedOrdersOpts = { - bridgeSlippage: _opts.bridgeSlippage, - maxFallbackSlippage: _opts.maxFallbackSlippage, - excludedSources: _opts.excludedSources, - feeSchedule: _opts.feeSchedule, - allowFallback: _opts.allowFallback, - exchangeProxyOverhead: _opts.exchangeProxyOverhead, - gasPrice: _opts.gasPrice, - neonRouterNumSamples: _opts.neonRouterNumSamples, - samplerMetrics: _opts.samplerMetrics, - fillAdjustor: _opts.fillAdjustor, - }; - - if (nativeOrders.length === 0) { - throw new Error(AggregationError.EmptyOrders); - } - - // Compute an optimized path for on-chain DEX and open-orderbook. This should not include RFQ liquidity. - const marketLiquidityFnAsync = - side === MarketOperation.Sell - ? this.getMarketSellLiquidityAsync.bind(this) - : this.getMarketBuyLiquidityAsync.bind(this); - const marketSideLiquidity: MarketSideLiquidity = await marketLiquidityFnAsync(nativeOrders, amount, _opts); - - // Phase 1 Routing - // We find an optimized path for ALL the DEX and open-orderbook liquidity - let optimizerResult: OptimizerResult | undefined; - try { - optimizerResult = await this._generateOptimizedOrdersAsync(marketSideLiquidity, { - ...optimizerOpts, - fillAdjustor: new IdentityFillAdjustor(), - }); - } catch (e) { - // If no on-chain or off-chain Open Orderbook orders are present, a `NoOptimalPath` will be thrown. - // If this happens at this stage, there is still a chance that an RFQ order is fillable, therefore - // we catch the error and continue. - if (e.message !== AggregationError.NoOptimalPath) { - throw e; - } - } - - // Calculate a suggested price. For now, this is simply the overall price of the aggregation. - // We can use this as a comparison price for RFQ - let wholeOrderPrice: BigNumber | undefined; - if (optimizerResult) { - wholeOrderPrice = getComparisonPrices( - optimizerResult.adjustedRate, - amount, - marketSideLiquidity, - _opts.feeSchedule, - _opts.exchangeProxyOverhead, - ).wholeOrder; - } - - // If RFQ liquidity is enabled, make a request to check RFQ liquidity against the first optimizer result - - // Phase 2 Routing - // Mix in any off-chain RFQ quotes - // Apply any fill adjustments i - const phaseTwoOptimizerOpts = { - ...optimizerOpts, - // Pass in the FillAdjustor for Phase 2 adjustment, in the future we may perform this adjustment - // in Phase 1. - fillAdjustor: _opts.fillAdjustor, - }; - - const { rfqt } = _opts; - if ( - marketSideLiquidity.isRfqSupported && - rfqt && - rfqt.quoteRequestor && - marketSideLiquidity.quoteSourceFilters.isAllowed(ERC20BridgeSource.Native) - ) { - // Timing of RFQT lifecycle - const timeStart = new Date().getTime(); - const { makerToken, takerToken } = nativeOrders[0].order; - - // Filter Alt Rfq Maker Asset Offerings to the current pair - const filteredOfferings: AltRfqMakerAssetOfferings = {}; - if (rfqt.altRfqAssetOfferings) { - const endpoints = Object.keys(rfqt.altRfqAssetOfferings); - for (const endpoint of endpoints) { - // Get the current pair if being offered - const offering = getAltMarketInfo(rfqt.altRfqAssetOfferings[endpoint], makerToken, takerToken); - if (offering) { - filteredOfferings[endpoint] = [offering]; - } - } - } - - if (rfqt.isIndicative) { - // An indicative quote is being requested, and indicative quotes price-aware enabled - // Make the RFQT request and then re-run the sampler if new orders come back. - - const indicativeQuotes = - rfqt.rfqClient !== undefined - ? (( - await rfqt.rfqClient.getV1PricesAsync({ - altRfqAssetOfferings: filteredOfferings, - assetFillAmount: amount, - chainId: this._sampler.chainId, - comparisonPrice: wholeOrderPrice, - integratorId: rfqt.integrator.integratorId, - intentOnFilling: rfqt.intentOnFilling, - makerToken, - marketOperation: side, - takerAddress: rfqt.takerAddress, - takerToken, - txOrigin: rfqt.txOrigin, - }) - ).prices as V4RFQIndicativeQuoteMM[]) - : await rfqt.quoteRequestor.requestRfqtIndicativeQuotesAsync( - makerToken, - takerToken, - amount, - side, - wholeOrderPrice, - rfqt, - ); - const deltaTime = new Date().getTime() - timeStart; - DEFAULT_INFO_LOGGER({ - rfqQuoteType: 'indicative', - deltaTime, - }); - // Re-run optimizer with the new indicative quote - if (indicativeQuotes.length > 0) { - // Attach the indicative quotes to the market side liquidity - marketSideLiquidity.quotes.rfqtIndicativeQuotes = indicativeQuotes; - - // Phase 2 Routing - const phase1OptimalSources = optimizerResult - ? optimizerResult.optimizedOrders.map(o => o.source) - : []; - const phase2MarketSideLiquidity: MarketSideLiquidity = { - ...marketSideLiquidity, - quotes: { - ...marketSideLiquidity.quotes, - // Select only the quotes that were chosen in Phase 1 - dexQuotes: marketSideLiquidity.quotes.dexQuotes.filter( - q => q.length > 0 && phase1OptimalSources.includes(q[0].source), - ), - }, - }; - - optimizerResult = await this._generateOptimizedOrdersAsync( - phase2MarketSideLiquidity, - phaseTwoOptimizerOpts, - ); - } - } else { - // A firm quote is being requested, and firm quotes price-aware enabled. - // Ensure that `intentOnFilling` is enabled and make the request. - const firmQuotes = - rfqt.rfqClient !== undefined - ? ( - await rfqt.rfqClient.getV1QuotesAsync({ - altRfqAssetOfferings: filteredOfferings, - assetFillAmount: amount, - chainId: this._sampler.chainId, - comparisonPrice: wholeOrderPrice, - integratorId: rfqt.integrator.integratorId, - intentOnFilling: rfqt.intentOnFilling, - makerToken, - marketOperation: side, - takerAddress: rfqt.takerAddress, - takerToken, - txOrigin: rfqt.txOrigin, - }) - ).quotes.map(toSignedNativeOrder) - : await rfqt.quoteRequestor.requestRfqtFirmQuotesAsync( - makerToken, - takerToken, - amount, - side, - wholeOrderPrice, - rfqt, - ); - const deltaTime = new Date().getTime() - timeStart; - DEFAULT_INFO_LOGGER({ - rfqQuoteType: 'firm', - deltaTime, - }); - if (firmQuotes.length > 0) { - // Compute the RFQ order fillable amounts. This is done by performing a "soft" order - // validation and by checking order balances that are monitored by our worker. - // If a firm quote validator does not exist, then we assume that all orders are valid. - const rfqTakerFillableAmounts = - rfqt.firmQuoteValidator === undefined - ? firmQuotes.map(signedOrder => signedOrder.order.takerAmount) - : await rfqt.firmQuoteValidator.getRfqtTakerFillableAmountsAsync( - firmQuotes.map(q => new RfqOrder(q.order)), - ); - - const quotesWithOrderFillableAmounts: NativeOrderWithFillableAmounts[] = firmQuotes.map( - (order, i) => ({ - ...order, - fillableTakerAmount: rfqTakerFillableAmounts[i], - // Adjust the maker amount by the available taker fill amount - fillableMakerAmount: getNativeAdjustedMakerFillAmount( - order.order, - rfqTakerFillableAmounts[i], - ), - fillableTakerFeeAmount: ZERO_AMOUNT, - }), - ); - - // Attach the firm RFQt quotes to the market side liquidity - marketSideLiquidity.quotes.nativeOrders = [ - ...quotesWithOrderFillableAmounts, - ...marketSideLiquidity.quotes.nativeOrders, - ]; - - // Re-run optimizer with the new firm quote. This is the second and last time - // we run the optimized in a block of code. In this case, we don't catch a potential `NoOptimalPath` exception - // and we let it bubble up if it happens. - - // Phase 2 Routing - // Optimization: Filter by what is already currently in the Phase1 output as it doesn't - // seem possible that inclusion of RFQT could impact the sources chosen from Phase 1. - const phase1OptimalSources = optimizerResult - ? optimizerResult.optimizedOrders.map(o => o.source) - : []; - const phase2MarketSideLiquidity: MarketSideLiquidity = { - ...marketSideLiquidity, - quotes: { - ...marketSideLiquidity.quotes, - // Select only the quotes that were chosen in Phase 1 - dexQuotes: marketSideLiquidity.quotes.dexQuotes.filter( - q => q.length > 0 && phase1OptimalSources.includes(q[0].source), - ), - }, - }; - optimizerResult = await this._generateOptimizedOrdersAsync( - phase2MarketSideLiquidity, - phaseTwoOptimizerOpts, - ); - } - } - } - - // At this point we should have at least one valid optimizer result, therefore we manually raise - // `NoOptimalPath` if no optimizer result was ever set. - if (optimizerResult === undefined) { - throw new Error(AggregationError.NoOptimalPath); - } - - // Compute Quote Report and return the results. - let quoteReport: QuoteReport | undefined; - if (_opts.shouldGenerateQuoteReport) { - quoteReport = MarketOperationUtils._computeQuoteReport( - _opts.rfqt ? _opts.rfqt.quoteRequestor : undefined, - marketSideLiquidity, - optimizerResult, - wholeOrderPrice, - ); - } - - // Always compute the Extended Quote Report - let extendedQuoteReportSources: ExtendedQuoteReportSources | undefined; - extendedQuoteReportSources = MarketOperationUtils._computeExtendedQuoteReportSources( - _opts.rfqt ? _opts.rfqt.quoteRequestor : undefined, - marketSideLiquidity, - amount, - optimizerResult, - wholeOrderPrice, - ); - - let priceComparisonsReport: PriceComparisonsReport | undefined; - if (_opts.shouldIncludePriceComparisonsReport) { - priceComparisonsReport = MarketOperationUtils._computePriceComparisonsReport( - _opts.rfqt ? _opts.rfqt.quoteRequestor : undefined, - marketSideLiquidity, - wholeOrderPrice, - ); - } - return { ...optimizerResult, quoteReport, extendedQuoteReportSources, priceComparisonsReport }; - } - - private async _refreshPoolCacheIfRequiredAsync(takerToken: string, makerToken: string): Promise { - _.values(this._sampler.poolsCaches) - .filter(cache => cache !== undefined && !cache.isFresh(takerToken, makerToken)) - .forEach(cache => cache?.getFreshPoolsForPairAsync(takerToken, makerToken)); - } -} - -// tslint:disable: max-file-line-count diff --git a/packages/asset-swapper/src/utils/market_operation_utils/liquidity_provider_utils.ts b/packages/asset-swapper/src/utils/market_operation_utils/liquidity_provider_utils.ts deleted file mode 100644 index 17f2807254..0000000000 --- a/packages/asset-swapper/src/utils/market_operation_utils/liquidity_provider_utils.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { LiquidityProviderRegistry } from './types'; - -// tslint:disable completed-docs -export function getLiquidityProvidersForPair( - registry: LiquidityProviderRegistry, - takerToken: string, - makerToken: string, -): Array<{ providerAddress: string; gasCost: number }> { - return Object.entries(registry) - .filter(([, plp]) => [makerToken, takerToken].every(t => plp.tokens.includes(t))) - .map(([providerAddress]) => { - let gasCost: number; - if (typeof registry[providerAddress].gasCost === 'number') { - gasCost = registry[providerAddress].gasCost as number; - } else { - gasCost = (registry[providerAddress].gasCost as (takerToken: string, makerToken: string) => number)( - takerToken, - makerToken, - ); - } - return { - providerAddress, - gasCost, - }; - }); -} diff --git a/packages/asset-swapper/src/utils/market_operation_utils/multihop_utils.ts b/packages/asset-swapper/src/utils/market_operation_utils/multihop_utils.ts deleted file mode 100644 index 6bf3ca61bf..0000000000 --- a/packages/asset-swapper/src/utils/market_operation_utils/multihop_utils.ts +++ /dev/null @@ -1,71 +0,0 @@ -import { BigNumber } from '@0x/utils'; -import * as _ from 'lodash'; - -import { Omit } from '../../types'; - -import { ZERO_AMOUNT } from './constants'; -import { getTwoHopAdjustedRate } from './rate_utils'; -import { - DexSample, - ExchangeProxyOverhead, - FeeSchedule, - FillAdjustor, - MarketSideLiquidity, - MultiHopFillData, -} from './types'; - -/** - * Returns the best two-hop quote and the fee-adjusted rate of that quote. - */ -export function getBestTwoHopQuote( - marketSideLiquidity: Omit, - feeSchedule?: FeeSchedule, - exchangeProxyOverhead?: ExchangeProxyOverhead, - fillAdjustor?: FillAdjustor, -): { quote: DexSample | undefined; adjustedRate: BigNumber } { - const { side, inputAmount, outputAmountPerEth, quotes } = marketSideLiquidity; - const { twoHopQuotes } = quotes; - // Ensure the expected data we require exists. In the case where all hops reverted - // or there were no sources included that allowed for multi hop, - // we can end up with empty, but not undefined, fill data - const filteredQuotes = twoHopQuotes.filter( - quote => - quote && - quote.fillData && - quote.fillData.firstHopSource && - quote.fillData.secondHopSource && - quote.output.isGreaterThan(ZERO_AMOUNT), - ); - if (filteredQuotes.length === 0) { - return { quote: undefined, adjustedRate: ZERO_AMOUNT }; - } - const best = filteredQuotes - .map(quote => - getTwoHopAdjustedRate( - side, - quote, - inputAmount, - outputAmountPerEth, - feeSchedule, - exchangeProxyOverhead, - fillAdjustor, - ), - ) - .reduce( - (prev, curr, i) => - curr.isGreaterThan(prev.adjustedRate) ? { adjustedRate: curr, quote: filteredQuotes[i] } : prev, - { - adjustedRate: getTwoHopAdjustedRate( - side, - filteredQuotes[0], - inputAmount, - outputAmountPerEth, - feeSchedule, - exchangeProxyOverhead, - fillAdjustor, - ), - quote: filteredQuotes[0], - }, - ); - return best; -} diff --git a/packages/asset-swapper/src/utils/market_operation_utils/orders.ts b/packages/asset-swapper/src/utils/market_operation_utils/orders.ts deleted file mode 100644 index 9cd5cf71dd..0000000000 --- a/packages/asset-swapper/src/utils/market_operation_utils/orders.ts +++ /dev/null @@ -1,628 +0,0 @@ -import { BridgeProtocol, encodeBridgeSourceId, FillQuoteTransformerOrderType } from '@0x/protocol-utils'; -import { AbiEncoder, BigNumber } from '@0x/utils'; -import _ = require('lodash'); - -import { AssetSwapperContractAddresses, MarketOperation } from '../../types'; - -import { MAX_UINT256, ZERO_AMOUNT } from './constants'; -import { - AaveV2FillData, - AggregationError, - BalancerV2BatchSwapFillData, - BalancerV2FillData, - BancorFillData, - CompoundFillData, - CurveFillData, - DexSample, - DODOFillData, - ERC20BridgeSource, - Fill, - FillData, - FinalUniswapV3FillData, - GeistFillData, - GenericRouterFillData, - GMXFillData, - KyberDmmFillData, - LidoFillData, - LiquidityProviderFillData, - MakerPsmFillData, - MooniswapFillData, - MultiHopFillData, - NativeFillData, - NativeLimitOrderFillData, - NativeRfqOrderFillData, - OptimizedMarketBridgeOrder, - OptimizedMarketOrder, - OptimizedMarketOrderBase, - OrderDomain, - PlatypusFillData, - ShellFillData, - SynthetixFillData, - UniswapV2FillData, - UniswapV3FillData, - UniswapV3PathAmount, - VelodromeFillData, - WOOFiFillData, -} from './types'; - -// tslint:disable completed-docs - -export interface CreateOrderFromPathOpts { - side: MarketOperation; - inputToken: string; - outputToken: string; - orderDomain: OrderDomain; - contractAddresses: AssetSwapperContractAddresses; - bridgeSlippage: number; -} - -export function createOrdersFromTwoHopSample( - sample: DexSample, - opts: CreateOrderFromPathOpts, -): OptimizedMarketOrder[] { - const [makerToken, takerToken] = getMakerTakerTokens(opts); - const { firstHopSource, secondHopSource, intermediateToken } = sample.fillData; - const firstHopFill: Fill = { - sourcePathId: '', - source: firstHopSource.source, - type: FillQuoteTransformerOrderType.Bridge, - input: opts.side === MarketOperation.Sell ? sample.input : ZERO_AMOUNT, - output: opts.side === MarketOperation.Sell ? ZERO_AMOUNT : sample.output, - adjustedOutput: opts.side === MarketOperation.Sell ? ZERO_AMOUNT : sample.output, - fillData: firstHopSource.fillData, - flags: BigInt(0), - gas: 1, - }; - const secondHopFill: Fill = { - sourcePathId: '', - source: secondHopSource.source, - type: FillQuoteTransformerOrderType.Bridge, - input: opts.side === MarketOperation.Sell ? MAX_UINT256 : sample.input, - output: opts.side === MarketOperation.Sell ? sample.output : MAX_UINT256, - adjustedOutput: opts.side === MarketOperation.Sell ? sample.output : MAX_UINT256, - fillData: secondHopSource.fillData, - flags: BigInt(0), - gas: 1, - }; - return [ - createBridgeOrder(firstHopFill, intermediateToken, takerToken, opts.side), - createBridgeOrder(secondHopFill, makerToken, intermediateToken, opts.side), - ]; -} - -export function getErc20BridgeSourceToBridgeSource(source: ERC20BridgeSource): string { - switch (source) { - case ERC20BridgeSource.Balancer: - return encodeBridgeSourceId(BridgeProtocol.Balancer, 'Balancer'); - case ERC20BridgeSource.BalancerV2: - return encodeBridgeSourceId(BridgeProtocol.BalancerV2Batch, 'BalancerV2'); - case ERC20BridgeSource.Bancor: - return encodeBridgeSourceId(BridgeProtocol.Bancor, 'Bancor'); - case ERC20BridgeSource.Curve: - return encodeBridgeSourceId(BridgeProtocol.Curve, 'Curve'); - case ERC20BridgeSource.CryptoCom: - return encodeBridgeSourceId(BridgeProtocol.CryptoCom, 'CryptoCom'); - case ERC20BridgeSource.Dodo: - return encodeBridgeSourceId(BridgeProtocol.Dodo, 'Dodo'); - case ERC20BridgeSource.LiquidityProvider: - // "LiquidityProvider" is too long to encode (17 characters). - return encodeBridgeSourceId(BridgeProtocol.Unknown, 'LP'); - case ERC20BridgeSource.MakerPsm: - return encodeBridgeSourceId(BridgeProtocol.MakerPsm, 'MakerPsm'); - case ERC20BridgeSource.Mooniswap: - return encodeBridgeSourceId(BridgeProtocol.Mooniswap, 'Mooniswap'); - case ERC20BridgeSource.MStable: - return encodeBridgeSourceId(BridgeProtocol.MStable, 'MStable'); - case ERC20BridgeSource.Shell: - return encodeBridgeSourceId(BridgeProtocol.Shell, 'Shell'); - case ERC20BridgeSource.SushiSwap: - return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'SushiSwap'); - case ERC20BridgeSource.Uniswap: - return encodeBridgeSourceId(BridgeProtocol.Uniswap, 'Uniswap'); - case ERC20BridgeSource.UniswapV2: - return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'UniswapV2'); - case ERC20BridgeSource.DodoV2: - return encodeBridgeSourceId(BridgeProtocol.DodoV2, 'DodoV2'); - case ERC20BridgeSource.PancakeSwap: - return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'PancakeSwap'); - case ERC20BridgeSource.PancakeSwapV2: - return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'PancakeSwapV2'); - case ERC20BridgeSource.BakerySwap: - return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'BakerySwap'); - case ERC20BridgeSource.Nerve: - return encodeBridgeSourceId(BridgeProtocol.Nerve, 'Nerve'); - case ERC20BridgeSource.Synapse: - return encodeBridgeSourceId(BridgeProtocol.Nerve, 'Synapse'); - case ERC20BridgeSource.Belt: - return encodeBridgeSourceId(BridgeProtocol.Curve, 'Belt'); - case ERC20BridgeSource.Ellipsis: - return encodeBridgeSourceId(BridgeProtocol.Curve, 'Ellipsis'); - case ERC20BridgeSource.Component: - return encodeBridgeSourceId(BridgeProtocol.Shell, 'Component'); - case ERC20BridgeSource.Saddle: - return encodeBridgeSourceId(BridgeProtocol.Nerve, 'Saddle'); - case ERC20BridgeSource.XSigma: - return encodeBridgeSourceId(BridgeProtocol.Curve, 'xSigma'); - case ERC20BridgeSource.ApeSwap: - return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'ApeSwap'); - case ERC20BridgeSource.CheeseSwap: - return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'CheeseSwap'); - case ERC20BridgeSource.UniswapV3: - return encodeBridgeSourceId(BridgeProtocol.UniswapV3, 'UniswapV3'); - case ERC20BridgeSource.KyberDmm: - return encodeBridgeSourceId(BridgeProtocol.KyberDmm, 'KyberDmm'); - case ERC20BridgeSource.QuickSwap: - return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'QuickSwap'); - case ERC20BridgeSource.Dfyn: - return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'Dfyn'); - case ERC20BridgeSource.CurveV2: - return encodeBridgeSourceId(BridgeProtocol.CurveV2, 'CurveV2'); - case ERC20BridgeSource.WaultSwap: - return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'WaultSwap'); - case ERC20BridgeSource.FirebirdOneSwap: - return encodeBridgeSourceId(BridgeProtocol.Nerve, 'FirebirdOneSwap'); - case ERC20BridgeSource.Lido: - return encodeBridgeSourceId(BridgeProtocol.Lido, 'Lido'); - case ERC20BridgeSource.ShibaSwap: - return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'ShibaSwap'); - case ERC20BridgeSource.IronSwap: - return encodeBridgeSourceId(BridgeProtocol.Nerve, 'IronSwap'); - case ERC20BridgeSource.ACryptos: - return encodeBridgeSourceId(BridgeProtocol.Curve, 'ACryptoS'); - case ERC20BridgeSource.Pangolin: - return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'Pangolin'); - case ERC20BridgeSource.TraderJoe: - return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'TraderJoe'); - case ERC20BridgeSource.UbeSwap: - return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'UbeSwap'); - case ERC20BridgeSource.Beethovenx: - return encodeBridgeSourceId(BridgeProtocol.BalancerV2, 'Beethovenx'); - case ERC20BridgeSource.SpiritSwap: - return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'SpiritSwap'); - case ERC20BridgeSource.SpookySwap: - return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'SpookySwap'); - case ERC20BridgeSource.MorpheusSwap: - return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'MorpheusSwap'); - case ERC20BridgeSource.Yoshi: - return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'Yoshi'); - case ERC20BridgeSource.AaveV2: - return encodeBridgeSourceId(BridgeProtocol.AaveV2, 'AaveV2'); - case ERC20BridgeSource.Compound: - return encodeBridgeSourceId(BridgeProtocol.Compound, 'Compound'); - case ERC20BridgeSource.Geist: - return encodeBridgeSourceId(BridgeProtocol.AaveV2, 'Geist'); - case ERC20BridgeSource.MobiusMoney: - return encodeBridgeSourceId(BridgeProtocol.Nerve, 'MobiusMoney'); - case ERC20BridgeSource.BiSwap: - return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'BiSwap'); - case ERC20BridgeSource.MDex: - return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'MDex'); - case ERC20BridgeSource.KnightSwap: - return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'KnightSwap'); - case ERC20BridgeSource.GMX: - return encodeBridgeSourceId(BridgeProtocol.GMX, 'GMX'); - case ERC20BridgeSource.Platypus: - return encodeBridgeSourceId(BridgeProtocol.Platypus, 'Platypus'); - case ERC20BridgeSource.MeshSwap: - return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'MeshSwap'); - case ERC20BridgeSource.BancorV3: - return encodeBridgeSourceId(BridgeProtocol.BancorV3, 'BancorV3'); - case ERC20BridgeSource.Velodrome: - return encodeBridgeSourceId(BridgeProtocol.Velodrome, 'Velodrome'); - case ERC20BridgeSource.Synthetix: - return encodeBridgeSourceId(BridgeProtocol.Synthetix, 'Synthetix'); - case ERC20BridgeSource.WOOFi: - return encodeBridgeSourceId(BridgeProtocol.WOOFi, 'WOOFi'); - default: - throw new Error(AggregationError.NoBridgeForSource); - } -} - -export function createBridgeDataForBridgeOrder(order: OptimizedMarketBridgeOrder): string { - let bridgeData: string; - if ( - order.source === ERC20BridgeSource.MultiHop || - order.source === ERC20BridgeSource.MultiBridge || - order.source === ERC20BridgeSource.Native - ) { - throw new Error('Invalid order to encode for Bridge Data'); - } - const encoder = BRIDGE_ENCODERS[order.source]; - - if (!encoder) { - throw new Error(AggregationError.NoBridgeForSource); - } - - switch (order.source) { - case ERC20BridgeSource.Curve: - case ERC20BridgeSource.CurveV2: - case ERC20BridgeSource.Nerve: - case ERC20BridgeSource.Synapse: - case ERC20BridgeSource.Belt: - case ERC20BridgeSource.Ellipsis: - case ERC20BridgeSource.Saddle: - case ERC20BridgeSource.XSigma: - case ERC20BridgeSource.FirebirdOneSwap: - case ERC20BridgeSource.IronSwap: - case ERC20BridgeSource.ACryptos: - case ERC20BridgeSource.MobiusMoney: - const curveFillData = (order as OptimizedMarketBridgeOrder).fillData; - bridgeData = encoder.encode([ - curveFillData.pool.poolAddress, - curveFillData.pool.exchangeFunctionSelector, - curveFillData.fromTokenIdx, - curveFillData.toTokenIdx, - ]); - break; - case ERC20BridgeSource.Balancer: - case ERC20BridgeSource.BalancerV2: - { - const balancerV2FillData = (order as OptimizedMarketBridgeOrder).fillData; - bridgeData = encoder.encode([ - balancerV2FillData.vault, - balancerV2FillData.swapSteps, - balancerV2FillData.assets, - ]); - } - break; - case ERC20BridgeSource.Beethovenx: - const beethovenFillData = (order as OptimizedMarketBridgeOrder).fillData; - const { vault, poolId } = beethovenFillData; - bridgeData = encoder.encode([vault, poolId]); - break; - case ERC20BridgeSource.Bancor: - const bancorFillData = (order as OptimizedMarketBridgeOrder).fillData; - bridgeData = encoder.encode([bancorFillData.networkAddress, bancorFillData.path]); - break; - case ERC20BridgeSource.UniswapV2: - case ERC20BridgeSource.SushiSwap: - case ERC20BridgeSource.CryptoCom: - case ERC20BridgeSource.PancakeSwap: - case ERC20BridgeSource.PancakeSwapV2: - case ERC20BridgeSource.BakerySwap: - case ERC20BridgeSource.ApeSwap: - case ERC20BridgeSource.CheeseSwap: - case ERC20BridgeSource.QuickSwap: - case ERC20BridgeSource.Dfyn: - case ERC20BridgeSource.WaultSwap: - case ERC20BridgeSource.ShibaSwap: - case ERC20BridgeSource.Pangolin: - case ERC20BridgeSource.TraderJoe: - case ERC20BridgeSource.UbeSwap: - case ERC20BridgeSource.SpiritSwap: - case ERC20BridgeSource.SpookySwap: - case ERC20BridgeSource.MorpheusSwap: - case ERC20BridgeSource.BiSwap: - case ERC20BridgeSource.MDex: - case ERC20BridgeSource.KnightSwap: - case ERC20BridgeSource.Yoshi: - case ERC20BridgeSource.MeshSwap: - const uniswapV2FillData = (order as OptimizedMarketBridgeOrder).fillData; - bridgeData = encoder.encode([uniswapV2FillData.router, uniswapV2FillData.tokenAddressPath]); - break; - case ERC20BridgeSource.Mooniswap: - const mooniswapFillData = (order as OptimizedMarketBridgeOrder).fillData; - bridgeData = encoder.encode([mooniswapFillData.poolAddress]); - break; - case ERC20BridgeSource.Dodo: - const dodoFillData = (order as OptimizedMarketBridgeOrder).fillData; - bridgeData = encoder.encode([ - dodoFillData.helperAddress, - dodoFillData.poolAddress, - dodoFillData.isSellBase, - ]); - break; - case ERC20BridgeSource.DodoV2: - const dodoV2FillData = (order as OptimizedMarketBridgeOrder).fillData; - bridgeData = encoder.encode([dodoV2FillData.poolAddress, dodoV2FillData.isSellBase]); - break; - case ERC20BridgeSource.Shell: - case ERC20BridgeSource.Component: - const shellFillData = (order as OptimizedMarketBridgeOrder).fillData; - bridgeData = encoder.encode([shellFillData.poolAddress]); - break; - case ERC20BridgeSource.LiquidityProvider: - const lpFillData = (order as OptimizedMarketBridgeOrder).fillData; - bridgeData = encoder.encode([lpFillData.poolAddress, tokenAddressEncoder.encode([order.takerToken])]); - break; - case ERC20BridgeSource.Uniswap: - const uniFillData = (order as OptimizedMarketBridgeOrder).fillData; - bridgeData = encoder.encode([uniFillData.router]); - break; - case ERC20BridgeSource.MStable: - const mStableFillData = (order as OptimizedMarketBridgeOrder).fillData; - bridgeData = encoder.encode([mStableFillData.router]); - break; - case ERC20BridgeSource.MakerPsm: - const psmFillData = (order as OptimizedMarketBridgeOrder).fillData; - bridgeData = encoder.encode([psmFillData.psmAddress, psmFillData.gemTokenAddress]); - break; - case ERC20BridgeSource.UniswapV3: - const uniswapV3FillData = (order as OptimizedMarketBridgeOrder).fillData; - bridgeData = encoder.encode([uniswapV3FillData.router, uniswapV3FillData.uniswapPath]); - break; - case ERC20BridgeSource.KyberDmm: - const kyberDmmFillData = (order as OptimizedMarketBridgeOrder).fillData; - bridgeData = encoder.encode([ - kyberDmmFillData.router, - kyberDmmFillData.poolsPath, - kyberDmmFillData.tokenAddressPath, - ]); - break; - case ERC20BridgeSource.Lido: - const lidoFillData = (order as OptimizedMarketBridgeOrder).fillData; - bridgeData = encoder.encode([lidoFillData.stEthTokenAddress, lidoFillData.wstEthTokenAddress]); - break; - case ERC20BridgeSource.AaveV2: - const aaveFillData = (order as OptimizedMarketBridgeOrder).fillData; - bridgeData = encoder.encode([aaveFillData.lendingPool, aaveFillData.aToken]); - break; - case ERC20BridgeSource.Compound: - const compoundFillData = (order as OptimizedMarketBridgeOrder).fillData; - bridgeData = encoder.encode([compoundFillData.cToken]); - break; - case ERC20BridgeSource.Geist: - const geistFillData = (order as OptimizedMarketBridgeOrder).fillData; - bridgeData = encoder.encode([geistFillData.lendingPool, geistFillData.gToken]); - break; - case ERC20BridgeSource.GMX: - const gmxFillData = (order as OptimizedMarketBridgeOrder).fillData; - bridgeData = encoder.encode([ - gmxFillData.router, - gmxFillData.reader, - gmxFillData.vault, - gmxFillData.tokenAddressPath, - ]); - break; - case ERC20BridgeSource.Platypus: - const platypusFillData = (order as OptimizedMarketBridgeOrder).fillData; - bridgeData = encoder.encode([ - platypusFillData.router, - platypusFillData.pool, - platypusFillData.tokenAddressPath, - ]); - break; - case ERC20BridgeSource.BancorV3: - const bancorV3FillData = (order as OptimizedMarketBridgeOrder).fillData; - bridgeData = encoder.encode([bancorV3FillData.networkAddress, bancorV3FillData.path]); - break; - case ERC20BridgeSource.Velodrome: - const velodromeFillData = (order as OptimizedMarketBridgeOrder).fillData; - bridgeData = encoder.encode([velodromeFillData.router, velodromeFillData.stable]); - break; - case ERC20BridgeSource.Synthetix: - const fillData = (order as OptimizedMarketBridgeOrder).fillData; - bridgeData = encoder.encode([ - fillData.synthetix, - fillData.takerTokenSymbolBytes32, - fillData.makerTokenSymbolBytes32, - ]); - break; - case ERC20BridgeSource.WOOFi: - const woofiFillData = (order as OptimizedMarketBridgeOrder).fillData; - bridgeData = encoder.encode([woofiFillData.poolAddress]); - break; - default: - throw new Error(AggregationError.NoBridgeForSource); - } - return bridgeData; -} - -export const poolEncoder = AbiEncoder.create([{ name: 'poolAddress', type: 'address' }]); -const curveEncoder = AbiEncoder.create([ - { name: 'curveAddress', type: 'address' }, - { name: 'exchangeFunctionSelector', type: 'bytes4' }, - { name: 'fromTokenIdx', type: 'int128' }, - { name: 'toTokenIdx', type: 'int128' }, -]); -const makerPsmEncoder = AbiEncoder.create([ - { name: 'psmAddress', type: 'address' }, - { name: 'gemTokenAddress', type: 'address' }, -]); -const balancerV2Encoder = AbiEncoder.create([ - { name: 'vault', type: 'address' }, - { name: 'poolId', type: 'bytes32' }, -]); -const routerAddressPathEncoder = AbiEncoder.create('(address,address[])'); -const tokenAddressEncoder = AbiEncoder.create([{ name: 'tokenAddress', type: 'address' }]); -const gmxAddressPathEncoder = AbiEncoder.create('(address,address,address,address[])'); -const platypusAddressPathEncoder = AbiEncoder.create('(address,address[],address[])'); - -export const BRIDGE_ENCODERS: { - [key in Exclude< - ERC20BridgeSource, - ERC20BridgeSource.Native | ERC20BridgeSource.MultiHop | ERC20BridgeSource.MultiBridge - >]: AbiEncoder.DataType; -} = { - [ERC20BridgeSource.LiquidityProvider]: AbiEncoder.create([ - { name: 'provider', type: 'address' }, - { name: 'data', type: 'bytes' }, - ]), - [ERC20BridgeSource.Dodo]: AbiEncoder.create([ - { name: 'helper', type: 'address' }, - { name: 'poolAddress', type: 'address' }, - { name: 'isSellBase', type: 'bool' }, - ]), - [ERC20BridgeSource.DodoV2]: AbiEncoder.create([ - { name: 'poolAddress', type: 'address' }, - { name: 'isSellBase', type: 'bool' }, - ]), - // Curve like - [ERC20BridgeSource.Curve]: curveEncoder, - [ERC20BridgeSource.CurveV2]: curveEncoder, - [ERC20BridgeSource.Nerve]: curveEncoder, - [ERC20BridgeSource.Synapse]: curveEncoder, - [ERC20BridgeSource.Belt]: curveEncoder, - [ERC20BridgeSource.Ellipsis]: curveEncoder, - [ERC20BridgeSource.Saddle]: curveEncoder, - [ERC20BridgeSource.XSigma]: curveEncoder, - [ERC20BridgeSource.FirebirdOneSwap]: curveEncoder, - [ERC20BridgeSource.IronSwap]: curveEncoder, - [ERC20BridgeSource.ACryptos]: curveEncoder, - [ERC20BridgeSource.MobiusMoney]: curveEncoder, - // UniswapV2 like, (router, address[]) - [ERC20BridgeSource.Bancor]: routerAddressPathEncoder, - [ERC20BridgeSource.BancorV3]: routerAddressPathEncoder, - [ERC20BridgeSource.UniswapV2]: routerAddressPathEncoder, - [ERC20BridgeSource.SushiSwap]: routerAddressPathEncoder, - [ERC20BridgeSource.CryptoCom]: routerAddressPathEncoder, - [ERC20BridgeSource.ShibaSwap]: routerAddressPathEncoder, - [ERC20BridgeSource.Pangolin]: routerAddressPathEncoder, - [ERC20BridgeSource.TraderJoe]: routerAddressPathEncoder, - [ERC20BridgeSource.SpiritSwap]: routerAddressPathEncoder, - [ERC20BridgeSource.SpookySwap]: routerAddressPathEncoder, - [ERC20BridgeSource.MorpheusSwap]: routerAddressPathEncoder, - [ERC20BridgeSource.BiSwap]: routerAddressPathEncoder, - [ERC20BridgeSource.MDex]: routerAddressPathEncoder, - [ERC20BridgeSource.KnightSwap]: routerAddressPathEncoder, - [ERC20BridgeSource.Yoshi]: routerAddressPathEncoder, - [ERC20BridgeSource.MeshSwap]: routerAddressPathEncoder, - // Avalanche - [ERC20BridgeSource.GMX]: gmxAddressPathEncoder, - [ERC20BridgeSource.Platypus]: platypusAddressPathEncoder, - // Celo - [ERC20BridgeSource.UbeSwap]: routerAddressPathEncoder, - // BSC - [ERC20BridgeSource.PancakeSwap]: routerAddressPathEncoder, - [ERC20BridgeSource.PancakeSwapV2]: routerAddressPathEncoder, - [ERC20BridgeSource.BakerySwap]: routerAddressPathEncoder, - [ERC20BridgeSource.ApeSwap]: routerAddressPathEncoder, - [ERC20BridgeSource.CheeseSwap]: routerAddressPathEncoder, - [ERC20BridgeSource.WaultSwap]: routerAddressPathEncoder, - // Polygon - [ERC20BridgeSource.QuickSwap]: routerAddressPathEncoder, - [ERC20BridgeSource.Dfyn]: routerAddressPathEncoder, - // Generic pools - [ERC20BridgeSource.Shell]: poolEncoder, - [ERC20BridgeSource.Component]: poolEncoder, - [ERC20BridgeSource.Mooniswap]: poolEncoder, - [ERC20BridgeSource.MStable]: poolEncoder, - [ERC20BridgeSource.Balancer]: poolEncoder, - [ERC20BridgeSource.Uniswap]: poolEncoder, - // Custom integrations - [ERC20BridgeSource.MakerPsm]: makerPsmEncoder, - [ERC20BridgeSource.BalancerV2]: AbiEncoder.create([ - { name: 'vault', type: 'address' }, - { - name: 'swapSteps', - type: 'tuple[]', - components: [ - { name: 'poolId', type: 'bytes32' }, - { name: 'assetInIndex', type: 'uint256' }, - { name: 'assetOutIndex', type: 'uint256' }, - { name: 'amount', type: 'uint256' }, - { name: 'userData', type: 'bytes' }, - ], - }, - { name: 'assets', type: 'address[]' }, - ]), - [ERC20BridgeSource.Beethovenx]: balancerV2Encoder, - [ERC20BridgeSource.UniswapV3]: AbiEncoder.create([ - { name: 'router', type: 'address' }, - { name: 'path', type: 'bytes' }, - ]), - [ERC20BridgeSource.KyberDmm]: AbiEncoder.create('(address,address[],address[])'), - [ERC20BridgeSource.Lido]: AbiEncoder.create('(address,address)'), - [ERC20BridgeSource.AaveV2]: AbiEncoder.create('(address,address)'), - [ERC20BridgeSource.Compound]: AbiEncoder.create('(address)'), - [ERC20BridgeSource.Geist]: AbiEncoder.create('(address,address)'), - [ERC20BridgeSource.Velodrome]: AbiEncoder.create('(address,bool)'), - [ERC20BridgeSource.Synthetix]: AbiEncoder.create('(address,bytes32,bytes32)'), - [ERC20BridgeSource.WOOFi]: AbiEncoder.create('(address)'), -}; - -function getFillTokenAmounts(fill: Fill, side: MarketOperation): [BigNumber, BigNumber] { - return [ - // Maker asset amount. - side === MarketOperation.Sell ? fill.output.integerValue(BigNumber.ROUND_DOWN) : fill.input, - // Taker asset amount. - side === MarketOperation.Sell ? fill.input : fill.output.integerValue(BigNumber.ROUND_UP), - ]; -} - -export function createNativeOptimizedOrder( - fill: Fill, - side: MarketOperation, -): OptimizedMarketOrderBase | OptimizedMarketOrderBase { - const fillData = fill.fillData; - const [makerAmount, takerAmount] = getFillTokenAmounts(fill, side); - const base = { - type: fill.type, - source: ERC20BridgeSource.Native, - makerToken: fillData.order.makerToken, - takerToken: fillData.order.takerToken, - makerAmount, - takerAmount, - fillData, - fill: cleanFillForExport(fill), - }; - return fill.type === FillQuoteTransformerOrderType.Rfq - ? { ...base, type: FillQuoteTransformerOrderType.Rfq, fillData: fillData as NativeRfqOrderFillData } - : { ...base, type: FillQuoteTransformerOrderType.Limit, fillData: fillData as NativeLimitOrderFillData }; -} - -export function createBridgeOrder( - fill: Fill, - makerToken: string, - takerToken: string, - side: MarketOperation, -): OptimizedMarketBridgeOrder { - const [makerAmount, takerAmount] = getFillTokenAmounts(fill, side); - return { - type: FillQuoteTransformerOrderType.Bridge, - source: fill.source, - makerToken, - takerToken, - makerAmount, - takerAmount, - fillData: createFinalBridgeOrderFillDataFromCollapsedFill(fill), - fill: cleanFillForExport(fill), - sourcePathId: fill.sourcePathId, - }; -} - -function cleanFillForExport(fill: Fill): Fill { - return _.omit(fill, ['flags', 'fillData', 'sourcePathId', 'source', 'type']) as Fill; -} - -function createFinalBridgeOrderFillDataFromCollapsedFill(fill: Fill): FillData { - switch (fill.source) { - case ERC20BridgeSource.UniswapV3: { - const fd = fill.fillData as UniswapV3FillData; - const { uniswapPath, gasUsed } = getBestUniswapV3PathAmountForInputAmount(fd, fill.input); - const finalFillData: FinalUniswapV3FillData = { - router: fd.router, - tokenAddressPath: fd.tokenAddressPath, - uniswapPath, - gasUsed, - }; - return finalFillData; - } - default: - break; - } - return fill.fillData; -} - -function getBestUniswapV3PathAmountForInputAmount( - fillData: UniswapV3FillData, - inputAmount: BigNumber, -): UniswapV3PathAmount { - if (fillData.pathAmounts.length === 0) { - throw new Error(`No Uniswap V3 paths`); - } - // Find the best path that can satisfy `inputAmount`. - // Assumes `fillData.pathAmounts` is sorted ascending. - for (const pathAmount of fillData.pathAmounts) { - if (pathAmount.inputAmount.gte(inputAmount)) { - return pathAmount; - } - } - return fillData.pathAmounts[fillData.pathAmounts.length - 1]; -} - -export function getMakerTakerTokens(opts: CreateOrderFromPathOpts): [string, string] { - const makerToken = opts.side === MarketOperation.Sell ? opts.outputToken : opts.inputToken; - const takerToken = opts.side === MarketOperation.Sell ? opts.inputToken : opts.outputToken; - return [makerToken, takerToken]; -} diff --git a/packages/asset-swapper/src/utils/market_operation_utils/path.ts b/packages/asset-swapper/src/utils/market_operation_utils/path.ts deleted file mode 100644 index aaa94646f8..0000000000 --- a/packages/asset-swapper/src/utils/market_operation_utils/path.ts +++ /dev/null @@ -1,165 +0,0 @@ -import { BigNumber } from '@0x/utils'; -import _ = require('lodash'); - -import { MarketOperation } from '../../types'; - -import { POSITIVE_INF, ZERO_AMOUNT } from './constants'; -import { ethToOutputAmount } from './fills'; -import { createBridgeOrder, createNativeOptimizedOrder, CreateOrderFromPathOpts, getMakerTakerTokens } from './orders'; -import { getCompleteRate, getRate } from './rate_utils'; -import { ERC20BridgeSource, ExchangeProxyOverhead, Fill, NativeFillData, OptimizedMarketOrder } from './types'; - -// tslint:disable: prefer-for-of no-bitwise completed-docs - -export interface PathSize { - input: BigNumber; - output: BigNumber; -} - -export interface PathPenaltyOpts { - outputAmountPerEth: BigNumber; - inputAmountPerEth: BigNumber; - exchangeProxyOverhead: ExchangeProxyOverhead; - gasPrice: BigNumber; -} - -export const DEFAULT_PATH_PENALTY_OPTS: PathPenaltyOpts = { - outputAmountPerEth: ZERO_AMOUNT, - inputAmountPerEth: ZERO_AMOUNT, - exchangeProxyOverhead: () => ZERO_AMOUNT, - gasPrice: ZERO_AMOUNT, -}; - -export class Path { - public orders?: OptimizedMarketOrder[]; - public sourceFlags: bigint = BigInt(0); - protected _size: PathSize = { input: ZERO_AMOUNT, output: ZERO_AMOUNT }; - protected _adjustedSize: PathSize = { input: ZERO_AMOUNT, output: ZERO_AMOUNT }; - - public static create( - side: MarketOperation, - fills: ReadonlyArray, - targetInput: BigNumber = POSITIVE_INF, - pathPenaltyOpts: PathPenaltyOpts = DEFAULT_PATH_PENALTY_OPTS, - ): Path { - const path = new Path(side, fills, targetInput, pathPenaltyOpts); - fills.forEach(fill => { - path.sourceFlags |= fill.flags; - path._addFillSize(fill); - }); - return path; - } - - protected constructor( - protected readonly side: MarketOperation, - public fills: ReadonlyArray, - protected readonly targetInput: BigNumber, - public readonly pathPenaltyOpts: PathPenaltyOpts, - ) {} - - /** - * Finalizes this path, creating fillable orders with the information required - * for settlement - */ - public finalize(opts: CreateOrderFromPathOpts): FinalizedPath { - const [makerToken, takerToken] = getMakerTakerTokens(opts); - this.orders = []; - for (const fill of this.fills) { - // internal BigInt flag field is not supported JSON and is tricky - // to remove upstream. Since it's not needed in a FinalizedPath we just drop it. - const normalizedFill = _.omit(fill, 'flags') as Fill; - if (fill.source === ERC20BridgeSource.Native) { - this.orders.push(createNativeOptimizedOrder(normalizedFill as Fill, opts.side)); - } else { - this.orders.push(createBridgeOrder(normalizedFill, makerToken, takerToken, opts.side)); - } - } - return this as FinalizedPath; - } - - public adjustedSize(): PathSize { - // Adjusted input/output has been adjusted by the cost of the DEX, but not by any - // overhead added by the exchange proxy. - const { input, output } = this._adjustedSize; - const { exchangeProxyOverhead, outputAmountPerEth, inputAmountPerEth } = this.pathPenaltyOpts; - // Calculate the additional penalty from the ways this path can be filled - // by the exchange proxy, e.g VIPs (small) or FillQuoteTransformer (large) - const gasOverhead = exchangeProxyOverhead(this.sourceFlags); - const pathPenalty = ethToOutputAmount({ - input, - output, - inputAmountPerEth, - outputAmountPerEth, - ethAmount: gasOverhead, - }); - return { - input, - output: this.side === MarketOperation.Sell ? output.minus(pathPenalty) : output.plus(pathPenalty), - }; - } - - public adjustedCompleteRate(): BigNumber { - const { input, output } = this.adjustedSize(); - return getCompleteRate(this.side, input, output, this.targetInput); - } - - /** - * Calculates the rate of this path, where the output has been - * adjusted for penalties (e.g cost) - */ - public adjustedRate(): BigNumber { - const { input, output } = this.adjustedSize(); - return getRate(this.side, input, output); - } - - /** - * Returns the best possible rate this path can offer, given the fills. - */ - public bestRate(): BigNumber { - const best = this.fills.reduce((prevRate, curr) => { - const currRate = getRate(this.side, curr.input, curr.output); - return prevRate.isLessThan(currRate) ? currRate : prevRate; - }, new BigNumber(0)); - return best; - } - - /** - * Compares two paths returning if this adjusted path - * is better than the other adjusted path - */ - public isAdjustedBetterThan(other: Path): boolean { - if (!this.targetInput.isEqualTo(other.targetInput)) { - throw new Error(`Target input mismatch: ${this.targetInput} !== ${other.targetInput}`); - } - const { targetInput } = this; - const { input } = this._size; - const { input: otherInput } = other._size; - if (input.isLessThan(targetInput) || otherInput.isLessThan(targetInput)) { - return input.isGreaterThan(otherInput); - } else { - return this.adjustedCompleteRate().isGreaterThan(other.adjustedCompleteRate()); - } - } - - private _addFillSize(fill: Fill): void { - if (this._size.input.plus(fill.input).isGreaterThan(this.targetInput)) { - const remainingInput = this.targetInput.minus(this._size.input); - const scaledFillOutput = fill.output.times(remainingInput.div(fill.input)); - this._size.input = this.targetInput; - this._size.output = this._size.output.plus(scaledFillOutput); - // Penalty does not get interpolated. - const penalty = fill.adjustedOutput.minus(fill.output); - this._adjustedSize.input = this.targetInput; - this._adjustedSize.output = this._adjustedSize.output.plus(scaledFillOutput).plus(penalty); - } else { - this._size.input = this._size.input.plus(fill.input); - this._size.output = this._size.output.plus(fill.output); - this._adjustedSize.input = this._adjustedSize.input.plus(fill.input); - this._adjustedSize.output = this._adjustedSize.output.plus(fill.adjustedOutput); - } - } -} - -export interface FinalizedPath extends Path { - readonly orders: OptimizedMarketOrder[]; -} diff --git a/packages/asset-swapper/src/utils/market_operation_utils/path_optimizer.ts b/packages/asset-swapper/src/utils/market_operation_utils/path_optimizer.ts deleted file mode 100644 index bcf41dadf5..0000000000 --- a/packages/asset-swapper/src/utils/market_operation_utils/path_optimizer.ts +++ /dev/null @@ -1,444 +0,0 @@ -import { assert } from '@0x/assert'; -import { ChainId } from '@0x/contract-addresses'; -import { OptimizerCapture, route, SerializedPath } from '@0x/neon-router'; -import { FillQuoteTransformerOrderType } from '@0x/protocol-utils'; -import { BigNumber, hexUtils } from '@0x/utils'; -import * as _ from 'lodash'; -import { performance } from 'perf_hooks'; - -import { DEFAULT_WARNING_LOGGER } from '../../constants'; -import { MarketOperation, NativeOrderWithFillableAmounts } from '../../types'; - -import { VIP_ERC20_BRIDGE_SOURCES_BY_CHAIN_ID, ZERO_AMOUNT } from './constants'; -import { dexSampleToFill, ethToOutputAmount, nativeOrderToFill } from './fills'; -import { Path, PathPenaltyOpts } from './path'; -import { DexSample, ERC20BridgeSource, FeeSchedule, Fill, FillAdjustor, FillData, SamplerMetrics } from './types'; - -// tslint:disable: prefer-for-of completed-docs no-bitwise - -// NOTE: The Rust router will panic with less than 3 samples -const MIN_NUM_SAMPLE_INPUTS = 3; - -const isDexSample = (obj: DexSample | NativeOrderWithFillableAmounts): obj is DexSample => !!(obj as DexSample).source; - -const ONE_BASE_UNIT = new BigNumber(1); - -function nativeOrderToNormalizedAmounts( - side: MarketOperation, - nativeOrder: NativeOrderWithFillableAmounts, -): { input: BigNumber; output: BigNumber } { - const { fillableTakerAmount, fillableTakerFeeAmount, fillableMakerAmount } = nativeOrder; - const makerAmount = fillableMakerAmount; - const takerAmount = fillableTakerAmount.plus(fillableTakerFeeAmount); - const input = side === MarketOperation.Sell ? takerAmount : makerAmount; - const output = side === MarketOperation.Sell ? makerAmount : takerAmount; - - return { input, output }; -} - -function calculateOuputFee( - side: MarketOperation, - sampleOrNativeOrder: DexSample | NativeOrderWithFillableAmounts, - outputAmountPerEth: BigNumber, - inputAmountPerEth: BigNumber, - fees: FeeSchedule, -): BigNumber { - if (isDexSample(sampleOrNativeOrder)) { - const { input, output, source, fillData } = sampleOrNativeOrder; - const fee = fees[source]?.(fillData).fee || ZERO_AMOUNT; - const outputFee = ethToOutputAmount({ - input, - output, - inputAmountPerEth, - outputAmountPerEth, - ethAmount: fee, - }); - return outputFee; - } else { - const { input, output } = nativeOrderToNormalizedAmounts(side, sampleOrNativeOrder); - const fee = fees[ERC20BridgeSource.Native]?.(sampleOrNativeOrder).fee || ZERO_AMOUNT; - const outputFee = ethToOutputAmount({ - input, - output, - inputAmountPerEth, - outputAmountPerEth, - ethAmount: fee, - }); - return outputFee; - } -} - -function findRoutesAndCreateOptimalPath( - side: MarketOperation, - samples: DexSample[][], - nativeOrders: NativeOrderWithFillableAmounts[], - input: BigNumber, - opts: PathPenaltyOpts, - fees: FeeSchedule, - neonRouterNumSamples: number, - vipSourcesSet: Set, - fillAdjustor: FillAdjustor, -): { allSourcesPath: Path | undefined; vipSourcesPath: Path | undefined } | undefined { - // Currently the rust router is unable to handle 1 base unit sized quotes and will error out - // To avoid flooding the logs with these errors we just return an insufficient liquidity error - // which is how the JS router handles these quotes today - if (input.isLessThanOrEqualTo(ONE_BASE_UNIT)) { - return undefined; - } - - // Create a `Fill` from a dex sample and adjust it with any passed in - // adjustor - const createFillFromDexSample = (sample: DexSample): Fill => { - const fill = dexSampleToFill(side, sample, opts.outputAmountPerEth, opts.inputAmountPerEth, fees); - const adjustedFills = fillAdjustor.adjustFills(side, [fill], input); - return adjustedFills[0]; - }; - - const createPathFromStrategy = (optimalRouteInputs: Float64Array, optimalRouteOutputs: Float64Array) => { - /** - * inputs are the amounts to fill at each source index - * e.g fill 2076 at index 4 - * [ 0, 0, 0, 0, 2076, 464, 230, - * 230, 0, 0, 0 ] - * the sum represents the total input amount - * - * outputs are the amounts we expect out at each source index - * [ 0, 0, 0, 0, 42216, 9359, 4677, - * 4674, 0, 0, 0 ] - * the sum represents the total expected output amount - */ - - const routesAndSamplesAndOutputs = _.zip( - optimalRouteInputs, - optimalRouteOutputs, - samplesAndNativeOrdersWithResults, - sampleSourcePathIds, - ); - const adjustedFills: Fill[] = []; - const totalRoutedAmount = BigNumber.sum(...optimalRouteInputs); - - // Due to precision errors we can end up with a totalRoutedAmount that is not exactly equal to the input - const precisionErrorScalar = input.dividedBy(totalRoutedAmount); - - for (const [ - routeInput, - outputAmount, - routeSamplesAndNativeOrders, - sourcePathId, - ] of routesAndSamplesAndOutputs) { - if (!Number.isFinite(outputAmount)) { - DEFAULT_WARNING_LOGGER(rustArgs, `neon-router: invalid route outputAmount ${outputAmount}`); - return undefined; - } - if (!routeInput || !routeSamplesAndNativeOrders || !outputAmount) { - continue; - } - // TODO: [TKR-241] amounts are sometimes clipped in the router due to precision loss for number/f64 - // we can work around it by scaling it and rounding up. However now we end up with a total amount of a couple base units too much - const routeInputCorrected = BigNumber.min( - precisionErrorScalar.multipliedBy(routeInput).integerValue(BigNumber.ROUND_CEIL), - input, - ); - - const current = routeSamplesAndNativeOrders[routeSamplesAndNativeOrders.length - 1]; - // If it is a native single order we only have one Input/output - // we want to convert this to an array of samples - if (!isDexSample(current)) { - const nativeFill = nativeOrderToFill( - side, - current, - routeInputCorrected, - opts.outputAmountPerEth, - opts.inputAmountPerEth, - fees, - false, - ); - // Note: If the order has an adjusted rate of less than or equal to 0 it will be undefined - if (nativeFill) { - // NOTE: For Limit/RFQ orders we are done here. No need to scale output - adjustedFills.push({ ...nativeFill, sourcePathId: sourcePathId ?? hexUtils.random() }); - } - continue; - } - - // NOTE: For DexSamples only - let fill = createFillFromDexSample(current); - if (!fill) { - continue; - } - const routeSamples = routeSamplesAndNativeOrders as Array>; - - // From the output of the router, find the closest Sample in terms of input. - // The Router may have chosen an amount to fill that we do not have a measured sample of - // Choosing this accurately is required in some sources where the `FillData` may change depending - // on the size of the trade. For example, UniswapV3 has variable gas cost - // which increases with input. - assert.assert(routeSamples.length >= 1, 'Found no sample to use for source'); - for (let k = routeSamples.length - 1; k >= 0; k--) { - // If we're at the last remaining sample that's all we have left to use - if (k === 0) { - fill = createFillFromDexSample(routeSamples[0]) ?? fill; - } - if (routeInputCorrected.isGreaterThan(routeSamples[k].input)) { - const left = routeSamples[k]; - const right = routeSamples[k + 1]; - if (left && right) { - fill = - createFillFromDexSample({ - ...right, // default to the greater (for gas used) - input: routeInputCorrected, - output: new BigNumber(outputAmount).integerValue(), - }) ?? fill; - } else { - assert.assert(Boolean(left || right), 'No valid sample to use'); - fill = createFillFromDexSample(left || right) ?? fill; - } - break; - } - } - - // TODO: remove once we have solved the rounding/precision loss issues in the Rust router - const maxSampledOutput = BigNumber.max(...routeSamples.map(s => s.output)).integerValue(); - // Scale output by scale factor but never go above the largest sample in sell quotes (unknown liquidity) or below 1 base unit (unfillable) - const scaleOutput = (output: BigNumber) => { - const capped = BigNumber.min(output.integerValue(), maxSampledOutput); - return BigNumber.max(capped, 1); - }; - - adjustedFills.push({ - ...fill, - input: routeInputCorrected, - output: scaleOutput(fill.output), - adjustedOutput: scaleOutput(fill.adjustedOutput), - sourcePathId: sourcePathId ?? hexUtils.random(), - }); - } - - if (adjustedFills.length === 0) { - return undefined; - } - - const pathFromRustInputs = Path.create(side, adjustedFills, input, opts); - - return pathFromRustInputs; - }; - - const samplesAndNativeOrdersWithResults: Array = []; - const serializedPaths: SerializedPath[] = []; - const sampleSourcePathIds: string[] = []; - for (const singleSourceSamples of samples) { - if (singleSourceSamples.length === 0) { - continue; - } - - const singleSourceSamplesWithOutput = [...singleSourceSamples]; - for (let i = singleSourceSamples.length - 1; i >= 0; i--) { - const currentOutput = singleSourceSamples[i].output; - if (currentOutput.isZero() || !currentOutput.isFinite()) { - // Remove trailing 0/invalid output samples - singleSourceSamplesWithOutput.pop(); - } else { - break; - } - } - - if (singleSourceSamplesWithOutput.length < MIN_NUM_SAMPLE_INPUTS) { - continue; - } - - // TODO: Do we need to handle 0 entries, from eg Kyber? - const serializedPath = singleSourceSamplesWithOutput.reduce( - (memo, sample, sampleIdx) => { - // Use the fill from createFillFromDexSample to apply - // any user supplied adjustments - const f = createFillFromDexSample(sample); - memo.ids.push(`${f.source}-${serializedPaths.length}-${sampleIdx}`); - memo.inputs.push(f.input.integerValue().toNumber()); - memo.outputs.push(f.output.integerValue().toNumber()); - // Calculate the penalty of this sample as the diff between the - // output and the adjusted output - const outputFee = f.output - .minus(f.adjustedOutput) - .absoluteValue() - .integerValue() - .toNumber(); - memo.outputFees.push(outputFee); - - return memo; - }, - { - ids: [], - inputs: [], - outputs: [], - outputFees: [], - isVip: vipSourcesSet.has(singleSourceSamplesWithOutput[0]?.source), - }, - ); - - samplesAndNativeOrdersWithResults.push(singleSourceSamplesWithOutput); - serializedPaths.push(serializedPath); - - const sourcePathId = hexUtils.random(); - sampleSourcePathIds.push(sourcePathId); - } - - const nativeOrdersourcePathId = hexUtils.random(); - for (const [idx, nativeOrder] of nativeOrders.entries()) { - const { input: normalizedOrderInput, output: normalizedOrderOutput } = nativeOrderToNormalizedAmounts( - side, - nativeOrder, - ); - // NOTE: skip dummy order created in swap_quoter - // TODO: remove dummy order and this logic once we don't need the JS router - if (normalizedOrderInput.isLessThanOrEqualTo(0) || normalizedOrderOutput.isLessThanOrEqualTo(0)) { - continue; - } - const fee = calculateOuputFee(side, nativeOrder, opts.outputAmountPerEth, opts.inputAmountPerEth, fees) - .integerValue() - .toNumber(); - - // HACK: due to an issue with the Rust router interpolation we need to create exactly 13 samples from the native order - const ids = []; - const inputs = []; - const outputs = []; - const outputFees = []; - - // NOTE: Limit orders can be both larger or smaller than the input amount - // If the order is larger than the input we can scale the order to the size of - // the quote input (order pricing is constant) and then create 13 "samples" up to - // and including the full quote input amount. - // If the order is smaller we don't need to scale anything, we will just end up - // with trailing duplicate samples for the order input as we cannot go higher - const scaleToInput = BigNumber.min(input.dividedBy(normalizedOrderInput), 1); - for (let i = 1; i <= 13; i++) { - const fraction = i / 13; - const currentInput = BigNumber.min( - normalizedOrderInput.times(scaleToInput).times(fraction), - normalizedOrderInput, - ); - const currentOutput = BigNumber.min( - normalizedOrderOutput.times(scaleToInput).times(fraction), - normalizedOrderOutput, - ); - const id = `${ERC20BridgeSource.Native}-${nativeOrder.type}-${serializedPaths.length}-${idx}-${i}`; - inputs.push(currentInput.integerValue().toNumber()); - outputs.push(currentOutput.integerValue().toNumber()); - outputFees.push(fee); - ids.push(id); - } - - // We have a VIP for the Rfq order type, Limit order currently goes through FQT - const isVip = nativeOrder.type !== FillQuoteTransformerOrderType.Limit; - - const serializedPath: SerializedPath = { - ids, - inputs, - outputs, - outputFees, - isVip, - }; - - samplesAndNativeOrdersWithResults.push([nativeOrder]); - serializedPaths.push(serializedPath); - sampleSourcePathIds.push(nativeOrdersourcePathId); - } - - if (serializedPaths.length === 0) { - return undefined; - } - - const rustArgs: OptimizerCapture = { - side, - targetInput: input.toNumber(), - pathsIn: serializedPaths, - }; - - const allSourcesRustRoute = new Float64Array(rustArgs.pathsIn.length); - const allSourcesOutputAmounts = new Float64Array(rustArgs.pathsIn.length); - const vipSourcesRustRoute = new Float64Array(rustArgs.pathsIn.length); - const vipSourcesOutputAmounts = new Float64Array(rustArgs.pathsIn.length); - - route( - rustArgs, - allSourcesRustRoute, - allSourcesOutputAmounts, - vipSourcesRustRoute, - vipSourcesOutputAmounts, - neonRouterNumSamples, - ); - assert.assert( - rustArgs.pathsIn.length === allSourcesRustRoute.length, - 'different number of sources in the Router output than the input', - ); - assert.assert( - rustArgs.pathsIn.length === allSourcesOutputAmounts.length, - 'different number of sources in the Router output amounts results than the input', - ); - assert.assert( - rustArgs.pathsIn.length === vipSourcesRustRoute.length, - 'different number of sources in the Router output than the input', - ); - assert.assert( - rustArgs.pathsIn.length === vipSourcesOutputAmounts.length, - 'different number of sources in the Router output amounts results than the input', - ); - - const allSourcesPath = createPathFromStrategy(allSourcesRustRoute, allSourcesOutputAmounts); - const vipSourcesPath = createPathFromStrategy(vipSourcesRustRoute, vipSourcesOutputAmounts); - - return { - allSourcesPath, - vipSourcesPath, - }; -} - -export function findOptimalPathFromSamples( - side: MarketOperation, - samples: DexSample[][], - nativeOrders: NativeOrderWithFillableAmounts[], - input: BigNumber, - opts: PathPenaltyOpts, - fees: FeeSchedule, - chainId: ChainId, - neonRouterNumSamples: number, - fillAdjustor: FillAdjustor, - samplerMetrics?: SamplerMetrics, -): Path | undefined { - const beforeTimeMs = performance.now(); - const sendMetrics = () => { - // tslint:disable-next-line: no-unused-expression - samplerMetrics && - samplerMetrics.logRouterDetails({ - router: 'neon-router', - type: 'total', - timingMs: performance.now() - beforeTimeMs, - }); - }; - const vipSourcesSet = new Set(VIP_ERC20_BRIDGE_SOURCES_BY_CHAIN_ID[chainId]); - const paths = findRoutesAndCreateOptimalPath( - side, - samples, - nativeOrders, - input, - opts, - fees, - neonRouterNumSamples, - vipSourcesSet, - fillAdjustor, - ); - - if (!paths) { - sendMetrics(); - return undefined; - } - - const { allSourcesPath, vipSourcesPath } = paths; - - if (!allSourcesPath || vipSourcesPath?.isAdjustedBetterThan(allSourcesPath)) { - sendMetrics(); - return vipSourcesPath; - } - - sendMetrics(); - return allSourcesPath; -} diff --git a/packages/asset-swapper/src/utils/market_operation_utils/pools_cache/balancer_pools_cache.ts b/packages/asset-swapper/src/utils/market_operation_utils/pools_cache/balancer_pools_cache.ts deleted file mode 100644 index 533322ccc6..0000000000 --- a/packages/asset-swapper/src/utils/market_operation_utils/pools_cache/balancer_pools_cache.ts +++ /dev/null @@ -1,128 +0,0 @@ -import { ChainId } from '@0x/contract-addresses'; -import { getPoolsWithTokens, parsePoolData } from 'balancer-labs-sor-v1'; -import { Pool } from 'balancer-labs-sor-v1/dist/types'; -import { gql, request } from 'graphql-request'; - -import { DEFAULT_WARNING_LOGGER } from '../../../constants'; -import { LogFunction } from '../../../types'; -import { BALANCER_MAX_POOLS_FETCHED, BALANCER_TOP_POOLS_FETCHED } from '../constants'; - -import { NoOpPoolsCache } from './no_op_pools_cache'; -import { AbstractPoolsCache, CacheValue, PoolsCache } from './pools_cache'; - -const ONE_DAY_MS = 24 * 60 * 60 * 1000; -// tslint:disable: member-ordering - -const BALANCER_SUBGRAPH_URL = 'https://api.thegraph.com/subgraphs/name/balancer-labs/balancer'; - -interface BalancerPoolResponse { - id: string; - swapFee: string; - tokens: Array<{ address: string; decimals: number; balance: string }>; - tokensList: string[]; - totalWeight: string; -} - -export class BalancerPoolsCache extends AbstractPoolsCache { - public static create(chainId: ChainId): PoolsCache { - if (chainId !== ChainId.Mainnet) { - return new NoOpPoolsCache(); - } - - return new BalancerPoolsCache(); - } - - private constructor( - private readonly _subgraphUrl: string = BALANCER_SUBGRAPH_URL, - cache: Map = new Map(), - private readonly maxPoolsFetched: number = BALANCER_MAX_POOLS_FETCHED, - private readonly _topPoolsFetched: number = BALANCER_TOP_POOLS_FETCHED, - private readonly _warningLogger: LogFunction = DEFAULT_WARNING_LOGGER, - ) { - super(cache); - void this._loadTopPoolsAsync(); - // Reload the top pools every 12 hours - setInterval(async () => void this._loadTopPoolsAsync(), ONE_DAY_MS / 2); - } - - protected async _fetchPoolsForPairAsync(takerToken: string, makerToken: string): Promise { - try { - const poolData = (await getPoolsWithTokens(takerToken, makerToken)).pools; - // Sort by maker token balance (descending) - const pools = parsePoolData(poolData, takerToken, makerToken).sort((a, b) => - b.balanceOut.minus(a.balanceOut).toNumber(), - ); - return pools.length > this.maxPoolsFetched ? pools.slice(0, this.maxPoolsFetched) : pools; - } catch (err) { - return []; - } - } - - protected async _loadTopPoolsAsync(): Promise { - const fromToPools: { - [from: string]: { [to: string]: Pool[] }; - } = {}; - - let pools: BalancerPoolResponse[]; - try { - pools = await this._fetchTopPoolsAsync(); - } catch (err) { - this._warningLogger(err, 'Failed to fetch top pools for Balancer V1'); - return; - } - - for (const pool of pools) { - const { tokensList } = pool; - for (const from of tokensList) { - for (const to of tokensList.filter(t => t.toLowerCase() !== from.toLowerCase())) { - fromToPools[from] = fromToPools[from] || {}; - fromToPools[from][to] = fromToPools[from][to] || []; - - try { - // The list of pools must be relevant to `from` and `to` for `parsePoolData` - const poolData = parsePoolData([pool], from, to); - fromToPools[from][to].push(poolData[0]); - // Cache this as we progress through - const expiresAt = Date.now() + this._cacheTimeMs; - this._cachePoolsForPair(from, to, fromToPools[from][to], expiresAt); - } catch { - // soldier on - } - } - } - } - } - - protected async _fetchTopPoolsAsync(): Promise { - const query = gql` - query fetchTopPools($topPoolsFetched: Int!) { - pools( - first: $topPoolsFetched - where: { publicSwap: true, liquidity_gt: 0 } - orderBy: swapsCount - orderDirection: desc - ) { - id - publicSwap - swapFee - totalWeight - tokensList - tokens { - id - address - balance - decimals - symbol - denormWeight - } - } - } - `; - try { - const { pools } = await request(this._subgraphUrl, query, { topPoolsFetched: this._topPoolsFetched }); - return pools; - } catch (err) { - return []; - } - } -} diff --git a/packages/asset-swapper/src/utils/market_operation_utils/pools_cache/balancer_sor_v2.ts b/packages/asset-swapper/src/utils/market_operation_utils/pools_cache/balancer_sor_v2.ts deleted file mode 100644 index f81471b581..0000000000 --- a/packages/asset-swapper/src/utils/market_operation_utils/pools_cache/balancer_sor_v2.ts +++ /dev/null @@ -1,149 +0,0 @@ -import { BigNumber } from '@0x/utils'; -/** - * This has been copied from https://github.com/balancer-labs/balancer-sor/blob/john/rc2/src/helpers.ts. - * Still awaiting V2 support for @balancer-labs/sor, once full V2 support is shipped we can upgrade sor and delete this file - */ -export const parsePoolData = ( - directPools: SubGraphPoolDictionary, - tokenIn: string, - tokenOut: string, - mostLiquidPoolsFirstHop: SubGraphPool[] = [], - mostLiquidPoolsSecondHop: SubGraphPool[] = [], - hopTokens: string[] = [], -): [SubGraphPoolDictionary, Path[]] => { - const pathDataList: Path[] = []; - const pools: SubGraphPoolDictionary = {}; - - // First add direct pair paths - // tslint:disable-next-line:forin - for (const idKey in directPools) { - const p: SubGraphPool = directPools[idKey]; - // Add pool to the set with all pools (only adds if it's still not present in dict) - pools[idKey] = p; - - const swap: Swap = { - pool: p.id, - tokenIn, - tokenOut, - tokenInDecimals: 18, // Placeholder for actual decimals - tokenOutDecimals: 18, - }; - - const path: Path = { - id: p.id, - swaps: [swap], - }; - pathDataList.push(path); - } - - // Now add multi-hop paths. - // mostLiquidPoolsFirstHop and mostLiquidPoolsSecondHop always has the same - // lengh of hopTokens - for (let i = 0; i < hopTokens.length; i++) { - // Add pools to the set with all pools (only adds if it's still not present in dict) - pools[mostLiquidPoolsFirstHop[i].id] = mostLiquidPoolsFirstHop[i]; - pools[mostLiquidPoolsSecondHop[i].id] = mostLiquidPoolsSecondHop[i]; - - const swap1: Swap = { - pool: mostLiquidPoolsFirstHop[i].id, - tokenIn, - tokenOut: hopTokens[i], - tokenInDecimals: 18, // Placeholder for actual decimals - tokenOutDecimals: 18, - }; - - const swap2: Swap = { - pool: mostLiquidPoolsSecondHop[i].id, - tokenIn: hopTokens[i], - tokenOut, - tokenInDecimals: 18, // Placeholder for actual decimals - tokenOutDecimals: 18, - }; - - const path: Path = { - id: mostLiquidPoolsFirstHop[i].id + mostLiquidPoolsSecondHop[i].id, // Path id is the concatenation of the ids of poolFirstHop and poolSecondHop - swaps: [swap1, swap2], - }; - pathDataList.push(path); - } - return [pools, pathDataList]; -}; - -interface SubGraphPool { - id: string; - swapFee: string; - totalWeight: string; - totalShares: string; - tokens: SubGraphToken[]; - tokensList: string[]; - poolType?: string; - - // Only for stable pools - amp: string; - - // Only for element pools - lpShares?: BigNumber; - time?: BigNumber; - principalToken?: string; - baseToken?: string; -} - -interface SubGraphPoolDictionary { - [poolId: string]: SubGraphPool; -} - -interface SubGraphToken { - address: string; - balance: string; - decimals: string | number; - // Stable & Element field - weight?: string; -} -interface Path { - id: string; // pool address if direct path, contactenation of pool addresses if multihop - swaps: Swap[]; - poolPairData?: PoolPairData[]; - limitAmount?: BigNumber; - filterEffectivePrice?: BigNumber; // TODO: This is just used for filtering, maybe there is a better way to filter? -} - -interface Swap { - pool: string; - tokenIn: string; - tokenOut: string; - swapAmount?: string; - limitReturnAmount?: string; - maxPrice?: string; - tokenInDecimals: number; - tokenOutDecimals: number; -} - -export interface PoolPairData { - id: string; - poolType?: string; // Todo: make this a mandatory field? - pairType?: string; // Todo: make this a mandatory field? - tokenIn: string; - tokenOut: string; - balanceIn?: BigNumber; - balanceOut?: BigNumber; - decimalsIn: number; - decimalsOut: number; - swapFee: BigNumber; - - // For weighted & element pools - weightIn?: BigNumber; - weightOut?: BigNumber; - - // Only for stable pools - allBalances: BigNumber[]; - invariant?: BigNumber; - amp?: BigNumber; - tokenIndexIn?: number; - tokenIndexOut?: number; - - // Only for element pools - lpShares?: BigNumber; - time?: BigNumber; - principalToken?: string; - baseToken?: string; -} diff --git a/packages/asset-swapper/src/utils/market_operation_utils/pools_cache/balancer_v2_pools_cache.ts b/packages/asset-swapper/src/utils/market_operation_utils/pools_cache/balancer_v2_pools_cache.ts deleted file mode 100644 index 5471da2b70..0000000000 --- a/packages/asset-swapper/src/utils/market_operation_utils/pools_cache/balancer_v2_pools_cache.ts +++ /dev/null @@ -1,182 +0,0 @@ -import { ChainId } from '@0x/contract-addresses'; -import { BigNumber } from '@0x/utils'; -// import { parsePoolData } from '@balancer-labs'; // TODO - upgrade to v2 -import { Pool } from 'balancer-labs-sor-v1/dist/types'; -import { gql, request } from 'graphql-request'; - -import { DEFAULT_WARNING_LOGGER } from '../../../constants'; -import { LogFunction } from '../../../types'; -import { BALANCER_MAX_POOLS_FETCHED, BALANCER_TOP_POOLS_FETCHED } from '../constants'; - -import { parsePoolData } from './balancer_sor_v2'; -import { NoOpPoolsCache } from './no_op_pools_cache'; -import { AbstractPoolsCache, CacheValue, PoolsCache } from './pools_cache'; - -// tslint:disable: member-ordering - -const BEETHOVEN_X_SUBGRAPH_URL_BY_CHAIN = new Map([ - [ChainId.Fantom, 'https://api.thegraph.com/subgraphs/name/beethovenxfi/beethovenx'], -]); - -const ONE_DAY_MS = 24 * 60 * 60 * 1000; - -interface BalancerPoolResponse { - id: string; - swapFee: string; - tokens: Array<{ address: string; decimals: number; balance: string; weight: string; symbol: string }>; - tokensList: string[]; - totalWeight: string; - totalShares: string; - amp: string | null; -} - -export class BalancerV2PoolsCache extends AbstractPoolsCache { - public static createBeethovenXPoolCache(chainId: ChainId): PoolsCache { - const subgraphUrl = BEETHOVEN_X_SUBGRAPH_URL_BY_CHAIN.get(chainId); - if (subgraphUrl === undefined) { - return new NoOpPoolsCache(); - } - - return new BalancerV2PoolsCache(subgraphUrl); - } - - private static _parseSubgraphPoolData(pool: any, takerToken: string, makerToken: string): Pool { - const tToken = pool.tokens.find((t: any) => t.address === takerToken); - const mToken = pool.tokens.find((t: any) => t.address === makerToken); - const swap = pool.swaps && pool.swaps[0]; - const tokenAmountOut = swap ? swap.tokenAmountOut : undefined; - const tokenAmountIn = swap ? swap.tokenAmountIn : undefined; - const spotPrice = - tokenAmountOut && tokenAmountIn ? new BigNumber(tokenAmountOut).div(tokenAmountIn) : undefined; // TODO: xianny check - - return { - id: pool.id, - balanceIn: new BigNumber(tToken.balance), - balanceOut: new BigNumber(mToken.balance), - weightIn: new BigNumber(tToken.weight), - weightOut: new BigNumber(mToken.weight), - swapFee: new BigNumber(pool.swapFee), - spotPrice, - }; - } - - private constructor( - private readonly subgraphUrl: string, - private readonly maxPoolsFetched: number = BALANCER_MAX_POOLS_FETCHED, - private readonly _topPoolsFetched: number = BALANCER_TOP_POOLS_FETCHED, - private readonly _warningLogger: LogFunction = DEFAULT_WARNING_LOGGER, - cache: Map = new Map(), - ) { - super(cache); - void this._loadTopPoolsAsync(); - // Reload the top pools every 12 hours - setInterval(async () => void this._loadTopPoolsAsync(), ONE_DAY_MS / 2); - } - - protected async _fetchTopPoolsAsync(): Promise { - const query = gql` - query fetchTopPools($topPoolsFetched: Int!) { - pools( - first: $topPoolsFetched - where: { totalLiquidity_gt: 0 } - orderBy: swapsCount - orderDirection: desc - ) { - id - swapFee - totalWeight - tokensList - amp - totalShares - tokens { - id - address - balance - decimals - symbol - weight - } - } - } - `; - - const { pools } = await request<{ pools: BalancerPoolResponse[] }>(this.subgraphUrl, query, { - topPoolsFetched: this._topPoolsFetched, - }); - - return pools; - } - protected async _loadTopPoolsAsync(): Promise { - const fromToPools: { - [from: string]: { [to: string]: Pool[] }; - } = {}; - - let pools: BalancerPoolResponse[]; - try { - pools = await this._fetchTopPoolsAsync(); - } catch (err) { - this._warningLogger(err, 'Failed to fetch top pools for Balancer V2'); - return; - } - - for (const pool of pools) { - const { tokensList } = pool; - for (const from of tokensList) { - for (const to of tokensList.filter(t => t.toLowerCase() !== from.toLowerCase())) { - fromToPools[from] = fromToPools[from] || {}; - fromToPools[from][to] = fromToPools[from][to] || []; - - try { - // The list of pools must be relevant to `from` and `to` for `parsePoolData` - const [poolData] = parsePoolData({ [pool.id]: pool as any }, from, to); - fromToPools[from][to].push( - BalancerV2PoolsCache._parseSubgraphPoolData(poolData[pool.id], from, to), - ); - // Cache this as we progress through - const expiresAt = Date.now() + this._cacheTimeMs; - this._cachePoolsForPair(from, to, fromToPools[from][to], expiresAt); - } catch (err) { - this._warningLogger(err, `Failed to load Balancer V2 top pools`); - // soldier on - } - } - } - } - } - protected async _fetchPoolsForPairAsync(takerToken: string, makerToken: string): Promise { - const query = gql` - query getPools { - pools( - first: ${this.maxPoolsFetched}, - where: { - tokensList_contains: ["${takerToken}", "${makerToken}"] - } - ) { - id - tokens { - address - balance - weight - } - swapFee - swaps( - orderBy: timestamp, orderDirection: desc, first: 1, - where:{ - tokenIn: "${takerToken}", - tokenOut: "${makerToken}" - } - ) { - tokenAmountIn - tokenAmountOut - } - } - } - `; - try { - const { pools } = await request(this.subgraphUrl, query); - return pools.map((pool: any) => BalancerV2PoolsCache._parseSubgraphPoolData(pool, takerToken, makerToken)); - } catch (e) { - return []; - } - } -} diff --git a/packages/asset-swapper/src/utils/market_operation_utils/pools_cache/balancer_v2_swap_info_cache.ts b/packages/asset-swapper/src/utils/market_operation_utils/pools_cache/balancer_v2_swap_info_cache.ts deleted file mode 100644 index 918445e594..0000000000 --- a/packages/asset-swapper/src/utils/market_operation_utils/pools_cache/balancer_v2_swap_info_cache.ts +++ /dev/null @@ -1,189 +0,0 @@ -import { ChainId } from '@0x/contract-addresses'; -import { BigNumber } from '@0x/utils'; -import { - BalancerSDK, - BalancerSdkConfig, - formatSequence, - getTokenAddressesForSwap, - NewPath, - parseToPoolsDict, - PoolDictionary, - RouteProposer, - SwapTypes, -} from '@balancer-labs/sdk'; - -import { DEFAULT_WARNING_LOGGER } from '../../../constants'; -import { LogFunction } from '../../../types'; -import { BALANCER_V2_SUBGRAPH_URL_BY_CHAIN, ONE_SECOND_MS } from '../constants'; -import { BalancerSwapInfo, BalancerSwaps } from '../types'; - -import { CacheValue, EMPTY_BALANCER_SWAPS, SwapInfoCache } from './pair_swaps_cache'; -import { SubgraphPoolDataService } from './sgPoolDataService'; - -const ONE_DAY_MS = 24 * 60 * 60 * ONE_SECOND_MS; - -export interface BalancerPoolResponse { - poolType: string; - id: string; - tokens: Array<{ address: string }>; - tokensList: string[]; -} - -export class BalancerV2SwapInfoCache extends SwapInfoCache { - private static readonly _MAX_POOLS_PER_PATH = 4; - private static readonly _MAX_CANDIDATE_PATHS_PER_PAIR = 2; - private readonly _routeProposer: RouteProposer; - private readonly _poolDataService: SubgraphPoolDataService; - - constructor( - chainId: ChainId, - subgraphUrl: string | null = BALANCER_V2_SUBGRAPH_URL_BY_CHAIN[chainId], - private readonly _warningLogger: LogFunction = DEFAULT_WARNING_LOGGER, - cache: { [key: string]: CacheValue } = {}, - ) { - super(cache); - const config: BalancerSdkConfig = { - network: chainId as number, // wtf TS - rpcUrl: '', // Not actually used by SDK for this. - }; - const balancerSdk = new BalancerSDK(config); - // The RouteProposer finds paths between a token pair using direct/multihop/linearPool routes - this._routeProposer = balancerSdk.sor.routeProposer; - // Uses Subgraph to retrieve up to date pool data required for routeProposer - this._poolDataService = new SubgraphPoolDataService({ - chainId, - subgraphUrl, - }); - void this._loadTopPoolsAsync(); - // Reload the top pools every 12 hours - setInterval(async () => void this._loadTopPoolsAsync(), ONE_DAY_MS / 2); - } - - protected async _loadTopPoolsAsync(): Promise { - const fromToSwapInfo: { - [from: string]: { [to: string]: BalancerSwaps }; - } = {}; - - // Retrieve pool data from Subgraph - const pools = await this._poolDataService.getPools(); - // timestamp is used for Element pools - const timestamp = Math.floor(Date.now() / ONE_SECOND_MS); - const poolsDict = parseToPoolsDict(pools, timestamp); - - for (const pool of pools) { - const { tokensList } = pool; - // tslint:disable-next-line: await-promise - await null; // This loop can be CPU heavy so yield to event loop. - for (const from of tokensList) { - for (const to of tokensList.filter(t => t.toLowerCase() !== from.toLowerCase())) { - fromToSwapInfo[from] = fromToSwapInfo[from] || {}; - // If a record for pair already exists skip as all paths alreay found - if (fromToSwapInfo[from][to]) { - continue; - } else { - try { - const expiresAt = Date.now() + this._cacheTimeMs; - // Retrieve swap steps and assets for a token pair - // This only needs to be called once per pair as all paths will be created from single call - const pairSwapInfo = this._getPoolPairSwapInfo(poolsDict, from, to); - fromToSwapInfo[from][to] = pairSwapInfo; - this._cacheSwapInfoForPair(from, to, fromToSwapInfo[from][to], expiresAt); - } catch (err) { - this._warningLogger(err, `Failed to load Balancer V2 top pools`); - // soldier on - } - } - } - } - } - } - - /** - * Will retrieve fresh pair and path data from Subgraph and return and array of swap info for pair.. - * @param takerToken Address of takerToken. - * @param makerToken Address of makerToken. - * @returns Swap data for pair consisting of assets and swap steps for ExactIn and ExactOut swap types. - */ - protected async _fetchSwapInfoForPairAsync(takerToken: string, makerToken: string): Promise { - try { - // retrieve up to date pools from SG - const pools = await this._poolDataService.getPools(); - - // timestamp is used for Element pools - const timestamp = Math.floor(Date.now() / ONE_SECOND_MS); - const poolDictionary = parseToPoolsDict(pools, timestamp); - return this._getPoolPairSwapInfo(poolDictionary, takerToken, makerToken); - } catch (e) { - return EMPTY_BALANCER_SWAPS; - } - } - - /** - * Uses pool data from provided dictionary to find top swap paths for token pair. - * @param pools Dictionary of pool data. - * @param takerToken Address of taker token. - * @param makerToken Address of maker token. - * @returns Swap data for pair consisting of assets and swap steps for ExactIn and ExactOut swap types. - */ - private _getPoolPairSwapInfo(pools: PoolDictionary, takerToken: string, makerToken: string): BalancerSwaps { - /* - Uses Balancer SDK to construct available paths for pair. - Paths can be direct, i.e. both tokens are in same pool or multihop. - Will also create paths for the new Balancer Linear pools. - These are returned in order of available liquidity which is useful for filtering. - */ - const paths = this._routeProposer.getCandidatePathsFromDict( - takerToken, - makerToken, - SwapTypes.SwapExactIn, - pools, - BalancerV2SwapInfoCache._MAX_POOLS_PER_PATH, - ); - - if (paths.length === 0) { - return EMPTY_BALANCER_SWAPS; - } - - // Convert paths data to swap information suitable for queryBatchSwap. Only use top 2 liquid paths - return formatSwaps(paths.slice(0, BalancerV2SwapInfoCache._MAX_CANDIDATE_PATHS_PER_PAIR)); - } -} - -/** - * Given an array of Balancer paths, returns swap information that can be passed to queryBatchSwap. - * @param paths Array of Balancer paths. - * @returns Formatted swap data consisting of assets and swap steps for ExactIn and ExactOut swap types. - */ -function formatSwaps(paths: NewPath[]): BalancerSwaps { - const formattedSwapsExactIn: BalancerSwapInfo[] = []; - const formattedSwapsExactOut: BalancerSwapInfo[] = []; - let assets: string[]; - paths.forEach(path => { - // Add a swap amount for each swap so we can use formatSequence. (This will be overwritten with actual amount during query) - path.swaps.forEach(s => (s.swapAmount = '0')); - const tokenAddresses = getTokenAddressesForSwap(path.swaps); - // Formats for both ExactIn and ExactOut swap types - const swapsExactIn = formatSequence(SwapTypes.SwapExactIn, path.swaps, tokenAddresses); - const swapsExactOut = formatSequence(SwapTypes.SwapExactOut, path.swaps, tokenAddresses); - assets = tokenAddresses; - formattedSwapsExactIn.push({ - assets, - swapSteps: swapsExactIn.map(s => ({ - ...s, - amount: new BigNumber(s.amount), - })), - }); - formattedSwapsExactOut.push({ - assets, - swapSteps: swapsExactOut.map(s => ({ - ...s, - amount: new BigNumber(s.amount), - })), - }); - }); - const formattedSwaps: BalancerSwaps = { - swapInfoExactIn: formattedSwapsExactIn, - swapInfoExactOut: formattedSwapsExactOut, - }; - return formattedSwaps; -} diff --git a/packages/asset-swapper/src/utils/market_operation_utils/pools_cache/index.ts b/packages/asset-swapper/src/utils/market_operation_utils/pools_cache/index.ts deleted file mode 100644 index f18f96fb7f..0000000000 --- a/packages/asset-swapper/src/utils/market_operation_utils/pools_cache/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export { BalancerPoolsCache } from './balancer_pools_cache'; -export { BalancerV2PoolsCache } from './balancer_v2_pools_cache'; -export { AbstractPoolsCache, PoolsCache } from './pools_cache'; diff --git a/packages/asset-swapper/src/utils/market_operation_utils/pools_cache/no_op_pools_cache.ts b/packages/asset-swapper/src/utils/market_operation_utils/pools_cache/no_op_pools_cache.ts deleted file mode 100644 index a0edc94f14..0000000000 --- a/packages/asset-swapper/src/utils/market_operation_utils/pools_cache/no_op_pools_cache.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { Pool, PoolsCache } from './pools_cache'; - -// tslint:disable:prefer-function-over-method - -export class NoOpPoolsCache implements PoolsCache { - public async getFreshPoolsForPairAsync( - _takerToken: string, - _makerToken: string, - _timeoutMs?: number | undefined, - ): Promise { - return []; - } - - public getPoolAddressesForPair(_takerToken: string, _makerToken: string): string[] { - return []; - } - - public isFresh(_takerToken: string, _makerToken: string): boolean { - return true; - } -} diff --git a/packages/asset-swapper/src/utils/market_operation_utils/pools_cache/pair_swaps_cache.ts b/packages/asset-swapper/src/utils/market_operation_utils/pools_cache/pair_swaps_cache.ts deleted file mode 100644 index f79fee1b2f..0000000000 --- a/packages/asset-swapper/src/utils/market_operation_utils/pools_cache/pair_swaps_cache.ts +++ /dev/null @@ -1,88 +0,0 @@ -import { ONE_HOUR_IN_SECONDS, ONE_SECOND_MS } from '../constants'; -import { BalancerSwaps } from '../types'; - -export interface CacheValue { - expiresAt: number; - balancerSwaps: BalancerSwaps; -} - -// Cache results for 30mins -const DEFAULT_CACHE_TIME_MS = (ONE_HOUR_IN_SECONDS / 2) * ONE_SECOND_MS; -const DEFAULT_TIMEOUT_MS = ONE_SECOND_MS; -export const EMPTY_BALANCER_SWAPS = { swapInfoExactIn: [], swapInfoExactOut: [] }; - -/** - * Caches SwapInfo for a pair of tokens. - * SwapInfo includes swap steps and asset information for those swap steps. - */ -export abstract class SwapInfoCache { - protected static _isExpired(value: CacheValue): boolean { - return Date.now() >= value.expiresAt; - } - constructor( - protected readonly _cache: { [key: string]: CacheValue }, - protected readonly _cacheTimeMs: number = DEFAULT_CACHE_TIME_MS, - ) {} - - public async getFreshPoolsForPairAsync( - takerToken: string, - makerToken: string, - timeoutMs: number = DEFAULT_TIMEOUT_MS, - ): Promise { - const timeout = new Promise(resolve => setTimeout(resolve, timeoutMs, [])); - return Promise.race([this._getAndSaveFreshSwapInfoForPairAsync(takerToken, makerToken), timeout]); - } - - public getCachedSwapInfoForPair( - takerToken: string, - makerToken: string, - ignoreExpired: boolean = true, - ): BalancerSwaps | undefined { - const key = JSON.stringify([takerToken, makerToken]); - const value = this._cache[key]; - if (ignoreExpired) { - return value === undefined ? EMPTY_BALANCER_SWAPS : value.balancerSwaps; - } - if (!value) { - return undefined; - } - if (SwapInfoCache._isExpired(value)) { - return undefined; - } - return value.balancerSwaps; - } - - public isFresh(takerToken: string, makerToken: string): boolean { - const cached = this.getCachedSwapInfoForPair(takerToken, makerToken, false); - return cached !== undefined; - } - - protected async _getAndSaveFreshSwapInfoForPairAsync( - takerToken: string, - makerToken: string, - ): Promise { - const key = JSON.stringify([takerToken, makerToken]); - const value = this._cache[key]; - if (value === undefined || value.expiresAt >= Date.now()) { - const swapInfo = await this._fetchSwapInfoForPairAsync(takerToken, makerToken); - const expiresAt = Date.now() + this._cacheTimeMs; - this._cacheSwapInfoForPair(takerToken, makerToken, swapInfo, expiresAt); - } - return this._cache[key].balancerSwaps; - } - - protected _cacheSwapInfoForPair( - takerToken: string, - makerToken: string, - swapInfo: BalancerSwaps, - expiresAt: number, - ): void { - const key = JSON.stringify([takerToken, makerToken]); - this._cache[key] = { - expiresAt, - balancerSwaps: swapInfo, - }; - } - - protected abstract _fetchSwapInfoForPairAsync(takerToken: string, makerToken: string): Promise; -} diff --git a/packages/asset-swapper/src/utils/market_operation_utils/pools_cache/pools_cache.ts b/packages/asset-swapper/src/utils/market_operation_utils/pools_cache/pools_cache.ts deleted file mode 100644 index 52329f0df8..0000000000 --- a/packages/asset-swapper/src/utils/market_operation_utils/pools_cache/pools_cache.ts +++ /dev/null @@ -1,85 +0,0 @@ -import { Pool } from 'balancer-labs-sor-v1/dist/types'; - -import { ONE_HOUR_IN_SECONDS, ONE_SECOND_MS } from '../constants'; -export { Pool }; -export interface CacheValue { - expiresAt: number; - pools: Pool[]; -} - -// Cache results for 30mins -const DEFAULT_CACHE_TIME_MS = (ONE_HOUR_IN_SECONDS / 2) * ONE_SECOND_MS; -const DEFAULT_TIMEOUT_MS = 3000; - -export interface PoolsCache { - getFreshPoolsForPairAsync(takerToken: string, makerToken: string, timeoutMs?: number): Promise; - getPoolAddressesForPair(takerToken: string, makerToken: string): string[]; - isFresh(takerToken: string, makerToken: string): boolean; -} - -export abstract class AbstractPoolsCache implements PoolsCache { - protected static _getKey(takerToken: string, makerToken: string): string { - return `${takerToken}-${makerToken}`; - } - - protected static _isExpired(value: CacheValue | undefined): boolean { - if (value === undefined) { - return true; - } - return Date.now() >= value.expiresAt; - } - - constructor( - protected readonly _cache: Map, - protected readonly _cacheTimeMs: number = DEFAULT_CACHE_TIME_MS, - ) {} - - public async getFreshPoolsForPairAsync( - takerToken: string, - makerToken: string, - timeoutMs: number = DEFAULT_TIMEOUT_MS, - ): Promise { - const timeout = new Promise(resolve => setTimeout(resolve, timeoutMs, [])); - return Promise.race([this._getAndSaveFreshPoolsForPairAsync(takerToken, makerToken), timeout]); - } - - /** - * Returns pool addresses (can be stale) for a pair. - * - * An empty array will be returned if cache does not exist. - */ - public getPoolAddressesForPair(takerToken: string, makerToken: string): string[] { - const value = this._getValue(takerToken, makerToken); - return value === undefined ? [] : value.pools.map(pool => pool.id); - } - - public isFresh(takerToken: string, makerToken: string): boolean { - const value = this._getValue(takerToken, makerToken); - return !AbstractPoolsCache._isExpired(value); - } - - protected _getValue(takerToken: string, makerToken: string): CacheValue | undefined { - const key = AbstractPoolsCache._getKey(takerToken, makerToken); - return this._cache.get(key); - } - - protected async _getAndSaveFreshPoolsForPairAsync(takerToken: string, makerToken: string): Promise { - const key = AbstractPoolsCache._getKey(takerToken, makerToken); - const value = this._cache.get(key); - if (!AbstractPoolsCache._isExpired(value)) { - return value!.pools; - } - - const pools = await this._fetchPoolsForPairAsync(takerToken, makerToken); - const expiresAt = Date.now() + this._cacheTimeMs; - this._cachePoolsForPair(takerToken, makerToken, pools, expiresAt); - return pools; - } - - protected _cachePoolsForPair(takerToken: string, makerToken: string, pools: Pool[], expiresAt: number): void { - const key = AbstractPoolsCache._getKey(takerToken, makerToken); - this._cache.set(key, { pools, expiresAt }); - } - - protected abstract _fetchPoolsForPairAsync(takerToken: string, makerToken: string): Promise; -} diff --git a/packages/asset-swapper/src/utils/market_operation_utils/pools_cache/sgPoolDataService.ts b/packages/asset-swapper/src/utils/market_operation_utils/pools_cache/sgPoolDataService.ts deleted file mode 100644 index c035e84848..0000000000 --- a/packages/asset-swapper/src/utils/market_operation_utils/pools_cache/sgPoolDataService.ts +++ /dev/null @@ -1,114 +0,0 @@ -import { ChainId } from '@0x/contract-addresses'; -import { logUtils } from '@0x/utils'; -import { PoolDataService, SubgraphPoolBase } from '@balancer-labs/sdk'; -import { gql, request } from 'graphql-request'; - -const queryWithLinear = gql` - query fetchTopPoolsWithLinear($maxPoolsFetched: Int!) { - pools: pools( - first: $maxPoolsFetched - where: { swapEnabled: true } - orderBy: totalLiquidity - orderDirection: desc - ) { - id - address - poolType - swapFee - totalShares - tokens { - address - balance - decimals - weight - priceRate - } - tokensList - totalWeight - amp - expiryTime - unitSeconds - principalToken - baseToken - swapEnabled - wrappedIndex - mainIndex - lowerTarget - upperTarget - } - } -`; - -const queryWithOutLinear = gql` - query fetchTopPoolsWithoutLinear($maxPoolsFetched: Int!) { - pools: pools( - first: $maxPoolsFetched - where: { swapEnabled: true } - orderBy: totalLiquidity - orderDirection: desc - ) { - id - address - poolType - swapFee - totalShares - tokens { - address - balance - decimals - weight - priceRate - } - tokensList - totalWeight - amp - expiryTime - unitSeconds - principalToken - baseToken - swapEnabled - } - } -`; - -const QUERY_BY_CHAIN_ID: { [chainId: number]: string } = { - [ChainId.Mainnet]: queryWithLinear, - [ChainId.Polygon]: queryWithOutLinear, -}; - -const DEFAULT_MAX_POOLS_FETCHED = 96; - -/** - * Simple service to query required info from Subgraph for Balancer Pools. - * Because Balancer Subgraphs have slightly different schema depending on network the queries are adjusted as needed. - */ -export class SubgraphPoolDataService implements PoolDataService { - private readonly _gqlQuery: string | undefined; - - constructor( - private readonly _config: { - chainId: number; - subgraphUrl: string | null; - maxPoolsFetched?: number; - }, - ) { - this._config.maxPoolsFetched = this._config.maxPoolsFetched || DEFAULT_MAX_POOLS_FETCHED; - this._gqlQuery = QUERY_BY_CHAIN_ID[this._config.chainId]; - } - - // tslint:disable-next-line: async-suffix - public async getPools(): Promise { - if (!this._gqlQuery || !this._config.subgraphUrl) { - return []; - } - try { - const { pools } = await request<{ pools: SubgraphPoolBase[] }>(this._config.subgraphUrl, this._gqlQuery, { - maxPoolsFetched: this._config.maxPoolsFetched, - }); - return pools; - } catch (err) { - logUtils.warn(`Failed to fetch BalancerV2 subgraph pools: ${err.message}`); - return []; - } - } -} diff --git a/packages/asset-swapper/src/utils/market_operation_utils/rate_utils.ts b/packages/asset-swapper/src/utils/market_operation_utils/rate_utils.ts deleted file mode 100644 index dc7072caa4..0000000000 --- a/packages/asset-swapper/src/utils/market_operation_utils/rate_utils.ts +++ /dev/null @@ -1,116 +0,0 @@ -import { FillQuoteTransformerOrderType } from '@0x/protocol-utils'; -import { BigNumber } from '@0x/utils'; - -import { MarketOperation } from '../../types'; - -import { SOURCE_FLAGS, ZERO_AMOUNT } from './constants'; -import { adjustOutput } from './fills'; -import { IdentityFillAdjustor } from './identity_fill_adjustor'; -import { - DexSample, - ERC20BridgeSource, - ExchangeProxyOverhead, - FeeSchedule, - Fill, - FillAdjustor, - MultiHopFillData, -} from './types'; - -// tslint:disable:no-bitwise - -/** - * Returns the fee-adjusted rate of a two-hop quote. Returns zero if the - * quote falls short of the target input. - */ -export function getTwoHopAdjustedRate( - side: MarketOperation, - twoHopQuote: DexSample, - targetInput: BigNumber, - outputAmountPerEth: BigNumber, - fees: FeeSchedule = {}, - exchangeProxyOverhead: ExchangeProxyOverhead = () => ZERO_AMOUNT, - fillAdjustor: FillAdjustor = new IdentityFillAdjustor(), -): BigNumber { - const { output, input, fillData } = twoHopQuote; - if (input.isLessThan(targetInput) || output.isZero()) { - return ZERO_AMOUNT; - } - - // Flags to indicate which sources are used - const flags = - SOURCE_FLAGS.MultiHop | - SOURCE_FLAGS[fillData.firstHopSource.source] | - SOURCE_FLAGS[fillData.secondHopSource.source]; - - // Penalty of going to those sources in terms of output - const sourcePenalty = outputAmountPerEth.times(fees[ERC20BridgeSource.MultiHop]!(fillData).fee).integerValue(); - - // Create a Fill so it can be adjusted by the `FillAdjustor` - const fill: Fill = { - ...twoHopQuote, - flags, - type: FillQuoteTransformerOrderType.Bridge, - adjustedOutput: adjustOutput(side, twoHopQuote.output, sourcePenalty), - sourcePathId: `${ERC20BridgeSource.MultiHop}-${fillData.firstHopSource.source}-${fillData.secondHopSource.source}`, - // We don't have this information at this stage - gas: 0, - }; - // Adjust the individual Fill - // HACK: Chose the worst of slippage between the two sources in multihop - const adjustedOutputLeft = fillAdjustor.adjustFills( - side, - [{ ...fill, source: fillData.firstHopSource.source }], - targetInput, - )[0].adjustedOutput; - const adjustedOutputRight = fillAdjustor.adjustFills( - side, - [{ ...fill, source: fillData.secondHopSource.source }], - targetInput, - )[0].adjustedOutput; - // In Sells, output smaller is worse (you're getting less out) - // In Buys, output larger is worse (it's costing you more) - const fillAdjustedOutput = - side === MarketOperation.Sell - ? BigNumber.min(adjustedOutputLeft, adjustedOutputRight) - : BigNumber.max(adjustedOutputLeft, adjustedOutputRight); - - const pathPenalty = outputAmountPerEth.times(exchangeProxyOverhead(flags)).integerValue(); - const pathAdjustedOutput = adjustOutput(side, fillAdjustedOutput, pathPenalty); - - return getRate(side, input, pathAdjustedOutput); -} - -/** - * Computes the "complete" rate given the input/output of a path. - * This value penalizes the path if it falls short of the target input. - */ -export function getCompleteRate( - side: MarketOperation, - input: BigNumber, - output: BigNumber, - targetInput: BigNumber, -): BigNumber { - if (input.eq(0) || output.eq(0) || targetInput.eq(0)) { - return ZERO_AMOUNT; - } - // Penalize paths that fall short of the entire input amount by a factor of - // input / targetInput => (i / t) - if (side === MarketOperation.Sell) { - // (o / i) * (i / t) => (o / t) - return output.div(targetInput); - } - // (i / o) * (i / t) - return input.div(output).times(input.div(targetInput)); -} - -/** - * Computes the rate given the input/output of a path. - * - * If it is a sell, output/input. If it is a buy, input/output. - */ -export function getRate(side: MarketOperation, input: BigNumber, output: BigNumber): BigNumber { - if (input.eq(0) || output.eq(0)) { - return ZERO_AMOUNT; - } - return side === MarketOperation.Sell ? output.div(input) : input.div(output); -} diff --git a/packages/asset-swapper/src/utils/market_operation_utils/sampler.ts b/packages/asset-swapper/src/utils/market_operation_utils/sampler.ts deleted file mode 100644 index 472a7c5eec..0000000000 --- a/packages/asset-swapper/src/utils/market_operation_utils/sampler.ts +++ /dev/null @@ -1,199 +0,0 @@ -import { ChainId } from '@0x/contract-addresses'; -import { BigNumber, NULL_BYTES } from '@0x/utils'; - -import { SamplerOverrides } from '../../types'; -import { ERC20BridgeSamplerContract } from '../../wrappers'; -import { TokenAdjacencyGraph } from '../token_adjacency_graph'; - -import { BancorService } from './bancor_service'; -import { PoolsCacheMap, SamplerOperations } from './sampler_operations'; -import { BatchedOperation, LiquidityProviderRegistry } from './types'; - -/** - * Generate sample amounts up to `maxFillAmount`. - */ -export function getSampleAmounts(maxFillAmount: BigNumber, numSamples: number, expBase: number = 1): BigNumber[] { - const distribution = [...Array(numSamples)].map((_v, i) => new BigNumber(expBase).pow(i)); - const distributionSum = BigNumber.sum(...distribution); - const stepSizes = distribution.map(d => d.div(distributionSum)); - const amounts = stepSizes.map((_s, i) => { - if (i === numSamples - 1) { - return maxFillAmount; - } - return maxFillAmount - .times(BigNumber.sum(...[0, ...stepSizes.slice(0, i + 1)])) - .integerValue(BigNumber.ROUND_UP); - }); - return amounts; -} - -type BatchedOperationResult = T extends BatchedOperation ? TResult : never; - -/** - * Encapsulates interactions with the `ERC20BridgeSampler` contract. - */ -export class DexOrderSampler extends SamplerOperations { - constructor( - public readonly chainId: ChainId, - _samplerContract: ERC20BridgeSamplerContract, - private readonly _samplerOverrides?: SamplerOverrides, - poolsCaches?: PoolsCacheMap, - tokenAdjacencyGraph?: TokenAdjacencyGraph, - liquidityProviderRegistry?: LiquidityProviderRegistry, - bancorServiceFn: () => Promise = async () => undefined, - ) { - super(chainId, _samplerContract, poolsCaches, tokenAdjacencyGraph, liquidityProviderRegistry, bancorServiceFn); - } - - /* Type overloads for `executeAsync()`. Could skip this if we would upgrade TS. */ - - // prettier-ignore - public async executeAsync< - T1 - >(...ops: [T1]): Promise<[ - BatchedOperationResult - ]>; - - // prettier-ignore - public async executeAsync< - T1, T2 - >(...ops: [T1, T2]): Promise<[ - BatchedOperationResult, - BatchedOperationResult - ]>; - - // prettier-ignore - public async executeAsync< - T1, T2, T3 - >(...ops: [T1, T2, T3]): Promise<[ - BatchedOperationResult, - BatchedOperationResult, - BatchedOperationResult - ]>; - - // prettier-ignore - public async executeAsync< - T1, T2, T3, T4 - >(...ops: [T1, T2, T3, T4]): Promise<[ - BatchedOperationResult, - BatchedOperationResult, - BatchedOperationResult, - BatchedOperationResult - ]>; - - // prettier-ignore - public async executeAsync< - T1, T2, T3, T4, T5 - >(...ops: [T1, T2, T3, T4, T5]): Promise<[ - BatchedOperationResult, - BatchedOperationResult, - BatchedOperationResult, - BatchedOperationResult, - BatchedOperationResult - ]>; - - // prettier-ignore - public async executeAsync< - T1, T2, T3, T4, T5, T6 - >(...ops: [T1, T2, T3, T4, T5, T6]): Promise<[ - BatchedOperationResult, - BatchedOperationResult, - BatchedOperationResult, - BatchedOperationResult, - BatchedOperationResult, - BatchedOperationResult - ]>; - - // prettier-ignore - public async executeAsync< - T1, T2, T3, T4, T5, T6, T7 - >(...ops: [T1, T2, T3, T4, T5, T6, T7]): Promise<[ - BatchedOperationResult, - BatchedOperationResult, - BatchedOperationResult, - BatchedOperationResult, - BatchedOperationResult, - BatchedOperationResult, - BatchedOperationResult - ]>; - - // prettier-ignore - public async executeAsync< - T1, T2, T3, T4, T5, T6, T7, T8 - >(...ops: [T1, T2, T3, T4, T5, T6, T7, T8]): Promise<[ - BatchedOperationResult, - BatchedOperationResult, - BatchedOperationResult, - BatchedOperationResult, - BatchedOperationResult, - BatchedOperationResult, - BatchedOperationResult, - BatchedOperationResult - ]>; - - // prettier-ignore - public async executeAsync< - T1, T2, T3, T4, T5, T6, T7, T8, T9 - >(...ops: [T1, T2, T3, T4, T5, T6, T7, T8, T9]): Promise<[ - BatchedOperationResult, - BatchedOperationResult, - BatchedOperationResult, - BatchedOperationResult, - BatchedOperationResult, - BatchedOperationResult, - BatchedOperationResult, - BatchedOperationResult, - BatchedOperationResult - ]>; - - // prettier-ignore - public async executeAsync< - T1, T2, T3, T4, T5, T6, T7, T8, T9, T10 - >(...ops: [T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]): Promise<[ - BatchedOperationResult, - BatchedOperationResult, - BatchedOperationResult, - BatchedOperationResult, - BatchedOperationResult, - BatchedOperationResult, - BatchedOperationResult, - BatchedOperationResult, - BatchedOperationResult, - BatchedOperationResult, - ]>; - - /** - * Run a series of operations from `DexOrderSampler.ops` in a single transaction. - */ - public async executeAsync(...ops: any[]): Promise { - return this.executeBatchAsync(ops); - } - - /** - * Run a series of operations from `DexOrderSampler.ops` in a single transaction. - * Takes an arbitrary length array, but is not typesafe. - */ - public async executeBatchAsync>>(ops: T): Promise { - const callDatas = ops.map(o => o.encodeCall()); - const { overrides, block } = this._samplerOverrides - ? this._samplerOverrides - : { overrides: undefined, block: undefined }; - - // All operations are NOOPs - if (callDatas.every(cd => cd === NULL_BYTES)) { - return callDatas.map((_callData, i) => ops[i].handleCallResults(NULL_BYTES)); - } - // Execute all non-empty calldatas. - const rawCallResults = await this._samplerContract - .batchCall(callDatas.filter(cd => cd !== NULL_BYTES)) - .callAsync({ overrides }, block); - // Return the parsed results. - let rawCallResultsIdx = 0; - return callDatas.map((callData, i) => { - // tslint:disable-next-line:boolean-naming - const { data, success } = - callData !== NULL_BYTES ? rawCallResults[rawCallResultsIdx++] : { success: true, data: NULL_BYTES }; - return success ? ops[i].handleCallResults(data) : ops[i].handleRevert(data); - }); - } -} diff --git a/packages/asset-swapper/src/utils/market_operation_utils/sampler_contract_operation.ts b/packages/asset-swapper/src/utils/market_operation_utils/sampler_contract_operation.ts deleted file mode 100644 index 54468643ae..0000000000 --- a/packages/asset-swapper/src/utils/market_operation_utils/sampler_contract_operation.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { ContractFunctionObj } from '@0x/base-contract'; -import { BigNumber, decodeBytesAsRevertError, logUtils } from '@0x/utils'; - -import { ERC20BridgeSamplerContract } from '../../wrappers'; - -import { ERC20BridgeSource, FillData, SourceQuoteOperation } from './types'; - -export type Parameters = T extends (...args: infer TArgs) => any ? TArgs : never; - -export interface SamplerContractCall< - TFunc extends (...args: any[]) => ContractFunctionObj, - TFillData extends FillData = FillData -> { - contract: ERC20BridgeSamplerContract; - function: TFunc; - params: Parameters; - callback?: (callResults: string, fillData: TFillData) => BigNumber[]; -} - -export class SamplerContractOperation< - TFunc extends (...args: any[]) => ContractFunctionObj, - TFillData extends FillData = FillData -> implements SourceQuoteOperation { - public readonly source: ERC20BridgeSource; - public fillData: TFillData; - private readonly _samplerContract: ERC20BridgeSamplerContract; - private readonly _samplerFunction: TFunc; - private readonly _params: Parameters; - private readonly _callback?: (callResults: string, fillData: TFillData) => BigNumber[]; - - constructor(opts: { source: ERC20BridgeSource; fillData?: TFillData } & SamplerContractCall) { - this.source = opts.source; - this.fillData = opts.fillData || ({} as TFillData); // tslint:disable-line:no-object-literal-type-assertion - this._samplerContract = opts.contract; - this._samplerFunction = opts.function; - this._params = opts.params; - this._callback = opts.callback; - } - - public encodeCall(): string { - return this._samplerFunction - .bind(this._samplerContract)(...this._params) - .getABIEncodedTransactionData(); - } - public handleCallResults(callResults: string): BigNumber[] { - if (this._callback !== undefined) { - return this._callback(callResults, this.fillData); - } else { - return this._samplerContract.getABIDecodedReturnData(this._samplerFunction.name, callResults); - } - } - public handleRevert(callResults: string): BigNumber[] { - let msg = callResults; - try { - msg = decodeBytesAsRevertError(callResults).toString(); - } catch (e) { - // do nothing - } - logUtils.warn(`SamplerContractOperation: ${this.source}.${this._samplerFunction.name} reverted ${msg}`); - return []; - } -} diff --git a/packages/asset-swapper/src/utils/market_operation_utils/sampler_no_operation.ts b/packages/asset-swapper/src/utils/market_operation_utils/sampler_no_operation.ts deleted file mode 100644 index 8c4abd132a..0000000000 --- a/packages/asset-swapper/src/utils/market_operation_utils/sampler_no_operation.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { BigNumber, logUtils, NULL_BYTES } from '@0x/utils'; - -import { ERC20BridgeSource, FillData, SourceQuoteOperation } from './types'; - -interface SamplerNoOperationCall { - callback: () => BigNumber[]; -} - -/** - * SamplerNoOperation can be used for sources where we already have all the necessary information - * required to perform the sample operations, without needing access to any on-chain data. Using a noop sample - * you can skip the eth_call, and just calculate the results directly in typescript land. - */ -export class SamplerNoOperation implements SourceQuoteOperation { - public readonly source: ERC20BridgeSource; - public fillData: TFillData; - private readonly _callback: () => BigNumber[]; - - constructor(opts: { source: ERC20BridgeSource; fillData?: TFillData } & SamplerNoOperationCall) { - this.source = opts.source; - this.fillData = opts.fillData || ({} as TFillData); // tslint:disable-line:no-object-literal-type-assertion - this._callback = opts.callback; - } - - // tslint:disable-next-line:prefer-function-over-method - public encodeCall(): string { - return NULL_BYTES; - } - public handleCallResults(_callResults: string): BigNumber[] { - return this._callback(); - } - public handleRevert(_callResults: string): BigNumber[] { - logUtils.warn(`SamplerNoOperation: ${this.source} reverted`); - return []; - } -} diff --git a/packages/asset-swapper/src/utils/market_operation_utils/sampler_operations.ts b/packages/asset-swapper/src/utils/market_operation_utils/sampler_operations.ts deleted file mode 100644 index 0403893dd8..0000000000 --- a/packages/asset-swapper/src/utils/market_operation_utils/sampler_operations.ts +++ /dev/null @@ -1,2212 +0,0 @@ -import { ChainId } from '@0x/contract-addresses'; -import { LimitOrderFields } from '@0x/protocol-utils'; -import { BigNumber, logUtils } from '@0x/utils'; -import { formatBytes32String } from '@ethersproject/strings'; -import * as _ from 'lodash'; - -import { AaveV2Sampler } from '../../noop_samplers/AaveV2Sampler'; -import { GeistSampler } from '../../noop_samplers/GeistSampler'; -import { SamplerCallResult, SignedNativeOrder } from '../../types'; -import { ERC20BridgeSamplerContract } from '../../wrappers'; -import { TokenAdjacencyGraph } from '../token_adjacency_graph'; - -import { AaveV2ReservesCache } from './aave_reserves_cache'; -import { BancorService } from './bancor_service'; -import { - getCurveLikeInfosForPair, - getDodoV2Offsets, - getPlatypusInfoForPair, - getShellLikeInfosForPair, - isBadTokenForSource, - isValidAddress, - uniswapV2LikeRouterAddress, -} from './bridge_source_utils'; -import { CompoundCTokenCache } from './compound_ctoken_cache'; -import { - AAVE_V2_SUBGRAPH_URL_BY_CHAIN_ID, - AVALANCHE_TOKENS, - BALANCER_V2_VAULT_ADDRESS_BY_CHAIN, - BANCORV3_NETWORK_BY_CHAIN_ID, - BANCORV3_NETWORK_INFO_BY_CHAIN_ID, - BANCOR_REGISTRY_BY_CHAIN_ID, - BEETHOVEN_X_VAULT_ADDRESS_BY_CHAIN, - COMPOUND_API_URL_BY_CHAIN_ID, - DODOV1_CONFIG_BY_CHAIN_ID, - DODOV2_FACTORIES_BY_CHAIN_ID, - GMX_READER_BY_CHAIN_ID, - GMX_ROUTER_BY_CHAIN_ID, - GMX_VAULT_BY_CHAIN_ID, - KYBER_DMM_ROUTER_BY_CHAIN_ID, - LIDO_INFO_BY_CHAIN, - LIQUIDITY_PROVIDER_REGISTRY_BY_CHAIN_ID, - MAINNET_TOKENS, - MAKER_PSM_INFO_BY_CHAIN_ID, - MAX_UINT256, - MOONISWAP_REGISTRIES_BY_CHAIN_ID, - NATIVE_FEE_TOKEN_BY_CHAIN_ID, - NULL_ADDRESS, - PLATYPUS_ROUTER_BY_CHAIN_ID, - SELL_SOURCE_FILTER_BY_CHAIN_ID, - SYNTHETIX_CURRENCY_KEYS_BY_CHAIN_ID, - SYNTHETIX_READ_PROXY_BY_CHAIN_ID, - UNISWAPV1_ROUTER_BY_CHAIN_ID, - UNISWAPV3_CONFIG_BY_CHAIN_ID, - VELODROME_ROUTER_BY_CHAIN_ID, - WOOFI_POOL_BY_CHAIN_ID, - WOOFI_SUPPORTED_TOKENS, - ZERO_AMOUNT, -} from './constants'; -import { getGeistInfoForPair } from './geist_utils'; -import { getLiquidityProvidersForPair } from './liquidity_provider_utils'; -import { BalancerPoolsCache, BalancerV2PoolsCache, PoolsCache } from './pools_cache'; -import { BalancerV2SwapInfoCache } from './pools_cache/balancer_v2_swap_info_cache'; -import { SamplerContractOperation } from './sampler_contract_operation'; -import { SamplerNoOperation } from './sampler_no_operation'; -import { SourceFilters } from './source_filters'; -import { - AaveV2FillData, - AaveV2Info, - BalancerFillData, - BalancerSwapInfo, - BalancerV2BatchSwapFillData, - BalancerV2FillData, - BalancerV2PoolInfo, - BancorFillData, - BatchedOperation, - CompoundFillData, - CurveFillData, - CurveInfo, - DexSample, - DODOFillData, - ERC20BridgeSource, - FeeSchedule, - GeistFillData, - GeistInfo, - GenericRouterFillData, - GMXFillData, - HopInfo, - KyberDmmFillData, - LidoFillData, - LidoInfo, - LiquidityProviderFillData, - LiquidityProviderRegistry, - MakerPsmFillData, - MooniswapFillData, - MultiHopFillData, - PlatypusFillData, - PsmInfo, - ShellFillData, - SourceQuoteOperation, - SynthetixFillData, - UniswapV2FillData, - UniswapV3FillData, - VelodromeFillData, - WOOFiFillData, -} from './types'; - -/** - * Source filters for `getTwoHopBuyQuotes()` and `getTwoHopSellQuotes()`. - */ -export const TWO_HOP_SOURCE_FILTERS = SourceFilters.all().exclude([ - ERC20BridgeSource.MultiHop, - ERC20BridgeSource.Native, -]); -/** - * Source filters for `getSellQuotes()` and `getBuyQuotes()`. - */ -export const BATCH_SOURCE_FILTERS = SourceFilters.all().exclude([ERC20BridgeSource.MultiHop, ERC20BridgeSource.Native]); - -export interface PoolsCacheMap { - [ERC20BridgeSource.Balancer]: PoolsCache; - [ERC20BridgeSource.BalancerV2]: BalancerV2SwapInfoCache | undefined; - [ERC20BridgeSource.Beethovenx]: PoolsCache; -} - -// tslint:disable:no-inferred-empty-object-type no-unbound-method - -/** - * Composable operations that can be batched in a single transaction, - * for use with `DexOrderSampler.executeAsync()`. - */ -export class SamplerOperations { - public readonly liquidityProviderRegistry: LiquidityProviderRegistry; - public readonly poolsCaches: PoolsCacheMap; - public readonly aaveReservesCache: AaveV2ReservesCache | undefined; - public readonly compoundCTokenCache: CompoundCTokenCache | undefined; - protected _bancorService?: BancorService; - public static constant(result: T): BatchedOperation { - return { - encodeCall: () => '0x', - handleCallResults: _callResults => result, - handleRevert: _callResults => result, - }; - } - - constructor( - public readonly chainId: ChainId, - protected readonly _samplerContract: ERC20BridgeSamplerContract, - poolsCaches?: PoolsCacheMap, - protected readonly tokenAdjacencyGraph: TokenAdjacencyGraph = TokenAdjacencyGraph.getEmptyGraph(), - liquidityProviderRegistry: LiquidityProviderRegistry = {}, - bancorServiceFn: () => Promise = async () => undefined, - ) { - this.liquidityProviderRegistry = { - ...LIQUIDITY_PROVIDER_REGISTRY_BY_CHAIN_ID[chainId], - ...liquidityProviderRegistry, - }; - this.poolsCaches = poolsCaches - ? poolsCaches - : { - [ERC20BridgeSource.Beethovenx]: BalancerV2PoolsCache.createBeethovenXPoolCache(chainId), - [ERC20BridgeSource.Balancer]: BalancerPoolsCache.create(chainId), - [ERC20BridgeSource.BalancerV2]: - BALANCER_V2_VAULT_ADDRESS_BY_CHAIN[chainId] === NULL_ADDRESS - ? undefined - : new BalancerV2SwapInfoCache(chainId), - }; - - const aaveSubgraphUrl = AAVE_V2_SUBGRAPH_URL_BY_CHAIN_ID[chainId]; - if (aaveSubgraphUrl) { - this.aaveReservesCache = new AaveV2ReservesCache(aaveSubgraphUrl); - } - - const compoundApiUrl = COMPOUND_API_URL_BY_CHAIN_ID[chainId]; - if (compoundApiUrl) { - this.compoundCTokenCache = new CompoundCTokenCache( - compoundApiUrl, - NATIVE_FEE_TOKEN_BY_CHAIN_ID[this.chainId], - ); - } - // Initialize the Bancor service, fetching paths in the background - bancorServiceFn() - .then(service => (this._bancorService = service)) - .catch(/* do nothing */); - } - - public getTokenDecimals(tokens: string[]): BatchedOperation { - return new SamplerContractOperation({ - source: ERC20BridgeSource.Native, - contract: this._samplerContract, - function: this._samplerContract.getTokenDecimals, - params: [tokens], - }); - } - - public isAddressContract(address: string): BatchedOperation { - return { - encodeCall: () => this._samplerContract.isContract(address).getABIEncodedTransactionData(), - handleCallResults: (callResults: string) => - this._samplerContract.getABIDecodedReturnData('isContract', callResults), - handleRevert: () => { - /* should never happen */ - throw new Error('Invalid address for isAddressContract'); - }, - }; - } - - public getGasLeft(): BatchedOperation { - return { - encodeCall: () => this._samplerContract.getGasLeft().getABIEncodedTransactionData(), - handleCallResults: (callResults: string) => - this._samplerContract.getABIDecodedReturnData('getGasLeft', callResults), - handleRevert: () => { - /* should never happen */ - throw new Error('Invalid result for getGasLeft'); - }, - }; - } - - public getBlockNumber(): BatchedOperation { - return { - encodeCall: () => this._samplerContract.getBlockNumber().getABIEncodedTransactionData(), - handleCallResults: (callResults: string) => - this._samplerContract.getABIDecodedReturnData('getBlockNumber', callResults), - handleRevert: () => { - /* should never happen */ - throw new Error('Invalid result for getBlockNumber'); - }, - }; - } - - public getLimitOrderFillableTakerAmounts( - orders: SignedNativeOrder[], - exchangeAddress: string, - ): BatchedOperation { - // Skip checking empty or invalid orders on-chain, returning a constant - if (orders.length === 0) { - return SamplerOperations.constant([]); - } - if (orders.length === 1 && orders[0].order.maker === NULL_ADDRESS) { - return SamplerOperations.constant([ZERO_AMOUNT]); - } - return new SamplerContractOperation({ - source: ERC20BridgeSource.Native, - contract: this._samplerContract, - function: this._samplerContract.getLimitOrderFillableTakerAssetAmounts, - // tslint:disable-next-line:no-unnecessary-type-assertion - params: [orders.map(o => o.order as LimitOrderFields), orders.map(o => o.signature), exchangeAddress], - }); - } - - public getLimitOrderFillableMakerAmounts( - orders: SignedNativeOrder[], - exchangeAddress: string, - ): BatchedOperation { - // Skip checking empty or invalid orders on-chain, returning a constant - if (orders.length === 0) { - return SamplerOperations.constant([]); - } - if (orders.length === 1 && orders[0].order.maker === NULL_ADDRESS) { - return SamplerOperations.constant([ZERO_AMOUNT]); - } - return new SamplerContractOperation({ - source: ERC20BridgeSource.Native, - contract: this._samplerContract, - function: this._samplerContract.getLimitOrderFillableMakerAssetAmounts, - // tslint:disable-next-line:no-unnecessary-type-assertion - params: [orders.map(o => o.order as LimitOrderFields), orders.map(o => o.signature), exchangeAddress], - }); - } - - public getKyberDmmSellQuotes( - router: string, - tokenAddressPath: string[], - takerFillAmounts: BigNumber[], - ): SourceQuoteOperation { - return new SamplerContractOperation({ - source: ERC20BridgeSource.KyberDmm, - contract: this._samplerContract, - function: this._samplerContract.sampleSellsFromKyberDmm, - params: [router, tokenAddressPath, takerFillAmounts], - callback: (callResults: string, fillData: KyberDmmFillData): BigNumber[] => { - const [pools, samples] = this._samplerContract.getABIDecodedReturnData<[string[], BigNumber[]]>( - 'sampleSellsFromKyberDmm', - callResults, - ); - fillData.poolsPath = pools; - fillData.router = router; - fillData.tokenAddressPath = tokenAddressPath; - return samples; - }, - }); - } - - public getKyberDmmBuyQuotes( - router: string, - tokenAddressPath: string[], - makerFillAmounts: BigNumber[], - ): SourceQuoteOperation { - return new SamplerContractOperation({ - source: ERC20BridgeSource.KyberDmm, - contract: this._samplerContract, - function: this._samplerContract.sampleBuysFromKyberDmm, - params: [router, tokenAddressPath, makerFillAmounts], - callback: (callResults: string, fillData: KyberDmmFillData): BigNumber[] => { - const [pools, samples] = this._samplerContract.getABIDecodedReturnData<[string[], BigNumber[]]>( - 'sampleBuysFromKyberDmm', - callResults, - ); - fillData.poolsPath = pools; - fillData.router = router; - fillData.tokenAddressPath = tokenAddressPath; - return samples; - }, - }); - } - - public getUniswapSellQuotes( - router: string, - makerToken: string, - takerToken: string, - takerFillAmounts: BigNumber[], - ): SourceQuoteOperation { - // Uniswap uses ETH instead of WETH, represented by address(0) - const uniswapTakerToken = takerToken === NATIVE_FEE_TOKEN_BY_CHAIN_ID[this.chainId] ? NULL_ADDRESS : takerToken; - const uniswapMakerToken = makerToken === NATIVE_FEE_TOKEN_BY_CHAIN_ID[this.chainId] ? NULL_ADDRESS : makerToken; - return new SamplerContractOperation({ - source: ERC20BridgeSource.Uniswap, - fillData: { router }, - contract: this._samplerContract, - function: this._samplerContract.sampleSellsFromUniswap, - params: [router, uniswapTakerToken, uniswapMakerToken, takerFillAmounts], - }); - } - - public getUniswapBuyQuotes( - router: string, - makerToken: string, - takerToken: string, - makerFillAmounts: BigNumber[], - ): SourceQuoteOperation { - // Uniswap uses ETH instead of WETH, represented by address(0) - const uniswapTakerToken = takerToken === NATIVE_FEE_TOKEN_BY_CHAIN_ID[this.chainId] ? NULL_ADDRESS : takerToken; - const uniswapMakerToken = makerToken === NATIVE_FEE_TOKEN_BY_CHAIN_ID[this.chainId] ? NULL_ADDRESS : makerToken; - return new SamplerContractOperation({ - source: ERC20BridgeSource.Uniswap, - fillData: { router }, - contract: this._samplerContract, - function: this._samplerContract.sampleBuysFromUniswap, - params: [router, uniswapTakerToken, uniswapMakerToken, makerFillAmounts], - }); - } - - public getUniswapV2SellQuotes( - router: string, - tokenAddressPath: string[], - takerFillAmounts: BigNumber[], - source: ERC20BridgeSource = ERC20BridgeSource.UniswapV2, - ): SourceQuoteOperation { - return new SamplerContractOperation({ - source, - fillData: { tokenAddressPath, router }, - contract: this._samplerContract, - function: this._samplerContract.sampleSellsFromUniswapV2, - params: [router, tokenAddressPath, takerFillAmounts], - }); - } - - public getUniswapV2BuyQuotes( - router: string, - tokenAddressPath: string[], - makerFillAmounts: BigNumber[], - source: ERC20BridgeSource = ERC20BridgeSource.UniswapV2, - ): SourceQuoteOperation { - return new SamplerContractOperation({ - source, - fillData: { tokenAddressPath, router }, - contract: this._samplerContract, - function: this._samplerContract.sampleBuysFromUniswapV2, - params: [router, tokenAddressPath, makerFillAmounts], - }); - } - - public getLiquidityProviderSellQuotes( - providerAddress: string, - makerToken: string, - takerToken: string, - takerFillAmounts: BigNumber[], - gasCost: number, - source: ERC20BridgeSource = ERC20BridgeSource.LiquidityProvider, - ): SourceQuoteOperation { - return new SamplerContractOperation({ - source, - fillData: { - poolAddress: providerAddress, - gasCost, - }, - contract: this._samplerContract, - function: this._samplerContract.sampleSellsFromLiquidityProvider, - params: [providerAddress, takerToken, makerToken, takerFillAmounts], - }); - } - - public getLiquidityProviderBuyQuotes( - providerAddress: string, - makerToken: string, - takerToken: string, - makerFillAmounts: BigNumber[], - gasCost: number, - source: ERC20BridgeSource = ERC20BridgeSource.LiquidityProvider, - ): SourceQuoteOperation { - return new SamplerContractOperation({ - source, - fillData: { - poolAddress: providerAddress, - gasCost, - }, - contract: this._samplerContract, - function: this._samplerContract.sampleBuysFromLiquidityProvider, - params: [providerAddress, takerToken, makerToken, makerFillAmounts], - }); - } - - public getCurveSellQuotes( - pool: CurveInfo, - fromTokenIdx: number, - toTokenIdx: number, - takerFillAmounts: BigNumber[], - source: ERC20BridgeSource = ERC20BridgeSource.Curve, - ): SourceQuoteOperation { - return new SamplerContractOperation({ - source, - fillData: { - pool, - fromTokenIdx, - toTokenIdx, - }, - contract: this._samplerContract, - function: this._samplerContract.sampleSellsFromCurve, - params: [ - { - poolAddress: pool.poolAddress, - sellQuoteFunctionSelector: pool.sellQuoteFunctionSelector, - buyQuoteFunctionSelector: pool.buyQuoteFunctionSelector, - }, - new BigNumber(fromTokenIdx), - new BigNumber(toTokenIdx), - takerFillAmounts, - ], - }); - } - - public getCurveBuyQuotes( - pool: CurveInfo, - fromTokenIdx: number, - toTokenIdx: number, - makerFillAmounts: BigNumber[], - source: ERC20BridgeSource = ERC20BridgeSource.Curve, - ): SourceQuoteOperation { - return new SamplerContractOperation({ - source, - fillData: { - pool, - fromTokenIdx, - toTokenIdx, - }, - contract: this._samplerContract, - function: this._samplerContract.sampleBuysFromCurve, - params: [ - { - poolAddress: pool.poolAddress, - sellQuoteFunctionSelector: pool.sellQuoteFunctionSelector, - buyQuoteFunctionSelector: pool.buyQuoteFunctionSelector, - }, - new BigNumber(fromTokenIdx), - new BigNumber(toTokenIdx), - makerFillAmounts, - ], - }); - } - - public getBalancerV2MultihopSellQuotes( - vault: string, - quoteSwaps: BalancerSwapInfo, // Should always be sell swap steps. - fillSwaps: BalancerSwapInfo, // Should always be sell swap steps. - takerFillAmounts: BigNumber[], - source: ERC20BridgeSource, - ): SourceQuoteOperation { - const quoteSwapSteps = quoteSwaps.swapSteps.map(s => ({ - ...s, - assetInIndex: new BigNumber(s.assetInIndex), - assetOutIndex: new BigNumber(s.assetOutIndex), - })); - return new SamplerContractOperation({ - source, - fillData: { vault, swapSteps: fillSwaps.swapSteps, assets: fillSwaps.assets }, - contract: this._samplerContract, - function: this._samplerContract.sampleMultihopSellsFromBalancerV2, - params: [vault, quoteSwapSteps, quoteSwaps.assets, takerFillAmounts], - }); - } - - public getBalancerV2MultihopBuyQuotes( - vault: string, - quoteSwaps: BalancerSwapInfo, // Should always be buy swap steps. - fillSwaps: BalancerSwapInfo, // Should always be a sell quote. - makerFillAmounts: BigNumber[], - source: ERC20BridgeSource, - ): SourceQuoteOperation { - const quoteSwapSteps = quoteSwaps.swapSteps.map(s => ({ - ...s, - assetInIndex: new BigNumber(s.assetInIndex), - assetOutIndex: new BigNumber(s.assetOutIndex), - })); - return new SamplerContractOperation({ - source, - // NOTE: fillData is set up for sells but quote function is set up for buys. - fillData: { vault, swapSteps: fillSwaps.swapSteps, assets: fillSwaps.assets }, - contract: this._samplerContract, - function: this._samplerContract.sampleMultihopBuysFromBalancerV2, - params: [vault, quoteSwapSteps, quoteSwaps.assets, makerFillAmounts], - }); - } - - public getBalancerV2SellQuotes( - poolInfo: BalancerV2PoolInfo, - makerToken: string, - takerToken: string, - takerFillAmounts: BigNumber[], - source: ERC20BridgeSource, - ): SourceQuoteOperation { - return new SamplerContractOperation({ - source, - fillData: poolInfo, - contract: this._samplerContract, - function: this._samplerContract.sampleSellsFromBalancerV2, - params: [poolInfo, takerToken, makerToken, takerFillAmounts], - }); - } - - public getBalancerV2BuyQuotes( - poolInfo: BalancerV2PoolInfo, - makerToken: string, - takerToken: string, - makerFillAmounts: BigNumber[], - source: ERC20BridgeSource, - ): SourceQuoteOperation { - return new SamplerContractOperation({ - source, - fillData: poolInfo, - contract: this._samplerContract, - function: this._samplerContract.sampleBuysFromBalancerV2, - params: [poolInfo, takerToken, makerToken, makerFillAmounts], - }); - } - - public getBalancerSellQuotes( - poolAddress: string, - makerToken: string, - takerToken: string, - takerFillAmounts: BigNumber[], - source: ERC20BridgeSource, - ): SourceQuoteOperation { - return new SamplerContractOperation({ - source, - fillData: { poolAddress }, - contract: this._samplerContract, - function: this._samplerContract.sampleSellsFromBalancer, - params: [poolAddress, takerToken, makerToken, takerFillAmounts], - }); - } - - public getBalancerBuyQuotes( - poolAddress: string, - makerToken: string, - takerToken: string, - makerFillAmounts: BigNumber[], - source: ERC20BridgeSource, - ): SourceQuoteOperation { - return new SamplerContractOperation({ - source, - fillData: { poolAddress }, - contract: this._samplerContract, - function: this._samplerContract.sampleBuysFromBalancer, - params: [poolAddress, takerToken, makerToken, makerFillAmounts], - }); - } - - public getMStableSellQuotes( - router: string, - makerToken: string, - takerToken: string, - takerFillAmounts: BigNumber[], - ): SourceQuoteOperation { - return new SamplerContractOperation({ - source: ERC20BridgeSource.MStable, - fillData: { router }, - contract: this._samplerContract, - function: this._samplerContract.sampleSellsFromMStable, - params: [router, takerToken, makerToken, takerFillAmounts], - }); - } - - public getMStableBuyQuotes( - router: string, - makerToken: string, - takerToken: string, - makerFillAmounts: BigNumber[], - ): SourceQuoteOperation { - return new SamplerContractOperation({ - source: ERC20BridgeSource.MStable, - fillData: { router }, - contract: this._samplerContract, - function: this._samplerContract.sampleBuysFromMStable, - params: [router, takerToken, makerToken, makerFillAmounts], - }); - } - - public getBancorSellQuotes( - registry: string, - makerToken: string, - takerToken: string, - takerFillAmounts: BigNumber[], - ): SourceQuoteOperation { - const paths = this._bancorService ? this._bancorService.getPaths(takerToken, makerToken) : []; - return new SamplerContractOperation({ - source: ERC20BridgeSource.Bancor, - contract: this._samplerContract, - function: this._samplerContract.sampleSellsFromBancor, - params: [{ registry, paths }, takerToken, makerToken, takerFillAmounts], - callback: (callResults: string, fillData: BancorFillData): BigNumber[] => { - const [networkAddress, path, samples] = this._samplerContract.getABIDecodedReturnData< - [string, string[], BigNumber[]] - >('sampleSellsFromBancor', callResults); - fillData.networkAddress = networkAddress; - fillData.path = path; - return samples; - }, - }); - } - - // Unimplemented - public getBancorBuyQuotes( - registry: string, - makerToken: string, - takerToken: string, - makerFillAmounts: BigNumber[], - ): SourceQuoteOperation { - return new SamplerContractOperation({ - source: ERC20BridgeSource.Bancor, - contract: this._samplerContract, - function: this._samplerContract.sampleBuysFromBancor, - params: [{ registry, paths: [] }, takerToken, makerToken, makerFillAmounts], - callback: (callResults: string, fillData: BancorFillData): BigNumber[] => { - const [networkAddress, path, samples] = this._samplerContract.getABIDecodedReturnData< - [string, string[], BigNumber[]] - >('sampleBuysFromBancor', callResults); - fillData.networkAddress = networkAddress; - fillData.path = path; - return samples; - }, - }); - } - - public getBancorV3SellQuotes( - networkAddress: string, - networkInfoAddress: string, - path: string[], - takerFillAmounts: BigNumber[], - ): SourceQuoteOperation { - return new SamplerContractOperation({ - source: ERC20BridgeSource.BancorV3, - fillData: { networkAddress, path }, - contract: this._samplerContract, - function: this._samplerContract.sampleSellsFromBancorV3, - params: [MAINNET_TOKENS.WETH, networkInfoAddress, path, takerFillAmounts], - }); - } - - public getBancorV3BuyQuotes( - networkAddress: string, - networkInfoAddress: string, - path: string[], - makerFillAmounts: BigNumber[], - ): SourceQuoteOperation { - return new SamplerContractOperation({ - source: ERC20BridgeSource.BancorV3, - fillData: { networkAddress, path }, - contract: this._samplerContract, - function: this._samplerContract.sampleBuysFromBancorV3, - params: [MAINNET_TOKENS.WETH, networkInfoAddress, path, makerFillAmounts], - }); - } - - public getMooniswapSellQuotes( - registry: string, - makerToken: string, - takerToken: string, - takerFillAmounts: BigNumber[], - ): SourceQuoteOperation { - // Mooniswap uses ETH instead of WETH, represented by address(0) - const mooniswapTakerToken = - takerToken === NATIVE_FEE_TOKEN_BY_CHAIN_ID[this.chainId] ? NULL_ADDRESS : takerToken; - const mooniswapMakerToken = - makerToken === NATIVE_FEE_TOKEN_BY_CHAIN_ID[this.chainId] ? NULL_ADDRESS : makerToken; - return new SamplerContractOperation({ - source: ERC20BridgeSource.Mooniswap, - contract: this._samplerContract, - function: this._samplerContract.sampleSellsFromMooniswap, - params: [registry, mooniswapTakerToken, mooniswapMakerToken, takerFillAmounts], - callback: (callResults: string, fillData: MooniswapFillData): BigNumber[] => { - const [poolAddress, samples] = this._samplerContract.getABIDecodedReturnData<[string, BigNumber[]]>( - 'sampleSellsFromMooniswap', - callResults, - ); - fillData.poolAddress = poolAddress; - return samples; - }, - }); - } - - public getMooniswapBuyQuotes( - registry: string, - makerToken: string, - takerToken: string, - makerFillAmounts: BigNumber[], - ): SourceQuoteOperation { - // Mooniswap uses ETH instead of WETH, represented by address(0) - const mooniswapTakerToken = - takerToken === NATIVE_FEE_TOKEN_BY_CHAIN_ID[this.chainId] ? NULL_ADDRESS : takerToken; - const mooniswapMakerToken = - makerToken === NATIVE_FEE_TOKEN_BY_CHAIN_ID[this.chainId] ? NULL_ADDRESS : makerToken; - return new SamplerContractOperation({ - source: ERC20BridgeSource.Mooniswap, - contract: this._samplerContract, - function: this._samplerContract.sampleBuysFromMooniswap, - params: [registry, mooniswapTakerToken, mooniswapMakerToken, makerFillAmounts], - callback: (callResults: string, fillData: MooniswapFillData): BigNumber[] => { - const [poolAddress, samples] = this._samplerContract.getABIDecodedReturnData<[string, BigNumber[]]>( - 'sampleBuysFromMooniswap', - callResults, - ); - fillData.poolAddress = poolAddress; - return samples; - }, - }); - } - - public getUniswapV3SellQuotes( - router: string, - quoter: string, - tokenAddressPath: string[], - takerFillAmounts: BigNumber[], - source: ERC20BridgeSource = ERC20BridgeSource.UniswapV3, - ): SourceQuoteOperation { - return new SamplerContractOperation({ - source, - contract: this._samplerContract, - function: this._samplerContract.sampleSellsFromUniswapV3, - params: [quoter, tokenAddressPath, takerFillAmounts], - callback: (callResults: string, fillData: UniswapV3FillData): BigNumber[] => { - const [paths, gasUsed, samples] = this._samplerContract.getABIDecodedReturnData< - [string[], BigNumber[], BigNumber[]] - >('sampleSellsFromUniswapV3', callResults); - fillData.router = router; - fillData.tokenAddressPath = tokenAddressPath; - fillData.pathAmounts = paths.map((uniswapPath, i) => ({ - uniswapPath, - inputAmount: takerFillAmounts[i], - gasUsed: gasUsed[i].toNumber(), - })); - - return samples; - }, - }); - } - - public getUniswapV3BuyQuotes( - router: string, - quoter: string, - tokenAddressPath: string[], - makerFillAmounts: BigNumber[], - source: ERC20BridgeSource = ERC20BridgeSource.UniswapV3, - ): SourceQuoteOperation { - return new SamplerContractOperation({ - source, - contract: this._samplerContract, - function: this._samplerContract.sampleBuysFromUniswapV3, - params: [quoter, tokenAddressPath, makerFillAmounts], - callback: (callResults: string, fillData: UniswapV3FillData): BigNumber[] => { - const [paths, gasUsed, samples] = this._samplerContract.getABIDecodedReturnData< - [string[], BigNumber[], BigNumber[]] - >('sampleBuysFromUniswapV3', callResults); - fillData.router = router; - fillData.tokenAddressPath = tokenAddressPath; - fillData.pathAmounts = paths.map((uniswapPath, i) => ({ - uniswapPath, - inputAmount: makerFillAmounts[i], - gasUsed: gasUsed[i].toNumber(), - })); - return samples; - }, - }); - } - - public getTwoHopSellQuotes( - sources: ERC20BridgeSource[], - makerToken: string, - takerToken: string, - sellAmount: BigNumber, - ): BatchedOperation>> { - const _sources = TWO_HOP_SOURCE_FILTERS.getAllowed(sources); - if (_sources.length === 0) { - return SamplerOperations.constant([]); - } - const intermediateTokens = this.tokenAdjacencyGraph.getIntermediateTokens(makerToken, takerToken); - const subOps = intermediateTokens.map(intermediateToken => { - const firstHopOps = this._getSellQuoteOperations(_sources, intermediateToken, takerToken, [ZERO_AMOUNT]); - const secondHopOps = this._getSellQuoteOperations(_sources, makerToken, intermediateToken, [ZERO_AMOUNT]); - return new SamplerContractOperation({ - contract: this._samplerContract, - source: ERC20BridgeSource.MultiHop, - function: this._samplerContract.sampleTwoHopSell, - params: [firstHopOps.map(op => op.encodeCall()), secondHopOps.map(op => op.encodeCall()), sellAmount], - fillData: { intermediateToken } as MultiHopFillData, // tslint:disable-line:no-object-literal-type-assertion - callback: (callResults: string, fillData: MultiHopFillData): BigNumber[] => { - const [firstHop, secondHop, buyAmount] = this._samplerContract.getABIDecodedReturnData< - [HopInfo, HopInfo, BigNumber] - >('sampleTwoHopSell', callResults); - // Ensure the hop sources are set even when the buy amount is zero - fillData.firstHopSource = firstHopOps[firstHop.sourceIndex.toNumber()]; - fillData.secondHopSource = secondHopOps[secondHop.sourceIndex.toNumber()]; - if (buyAmount.isZero()) { - return [ZERO_AMOUNT]; - } - fillData.firstHopSource.handleCallResults(firstHop.returnData); - fillData.secondHopSource.handleCallResults(secondHop.returnData); - return [buyAmount]; - }, - }); - }); - return this._createBatch( - subOps, - (samples: BigNumber[][]) => { - return subOps.map((op, i) => { - return { - source: op.source, - output: samples[i][0], - input: sellAmount, - fillData: op.fillData, - }; - }); - }, - () => { - logUtils.warn('SamplerContractOperation: Two hop sampler reverted'); - return []; - }, - ); - } - - public getTwoHopBuyQuotes( - sources: ERC20BridgeSource[], - makerToken: string, - takerToken: string, - buyAmount: BigNumber, - ): BatchedOperation>> { - const _sources = TWO_HOP_SOURCE_FILTERS.getAllowed(sources); - if (_sources.length === 0) { - return SamplerOperations.constant([]); - } - const intermediateTokens = this.tokenAdjacencyGraph.getIntermediateTokens(makerToken, takerToken); - const subOps = intermediateTokens.map(intermediateToken => { - const firstHopOps = this._getBuyQuoteOperations(_sources, intermediateToken, takerToken, [ - new BigNumber(0), - ]); - const secondHopOps = this._getBuyQuoteOperations(_sources, makerToken, intermediateToken, [ - new BigNumber(0), - ]); - return new SamplerContractOperation({ - contract: this._samplerContract, - source: ERC20BridgeSource.MultiHop, - function: this._samplerContract.sampleTwoHopBuy, - params: [firstHopOps.map(op => op.encodeCall()), secondHopOps.map(op => op.encodeCall()), buyAmount], - fillData: { intermediateToken } as MultiHopFillData, // tslint:disable-line:no-object-literal-type-assertion - callback: (callResults: string, fillData: MultiHopFillData): BigNumber[] => { - const [firstHop, secondHop, sellAmount] = this._samplerContract.getABIDecodedReturnData< - [HopInfo, HopInfo, BigNumber] - >('sampleTwoHopBuy', callResults); - if (sellAmount.isEqualTo(MAX_UINT256)) { - return [sellAmount]; - } - fillData.firstHopSource = firstHopOps[firstHop.sourceIndex.toNumber()]; - fillData.secondHopSource = secondHopOps[secondHop.sourceIndex.toNumber()]; - fillData.firstHopSource.handleCallResults(firstHop.returnData); - fillData.secondHopSource.handleCallResults(secondHop.returnData); - return [sellAmount]; - }, - }); - }); - return this._createBatch( - subOps, - (samples: BigNumber[][]) => { - return subOps.map((op, i) => { - return { - source: op.source, - output: samples[i][0], - input: buyAmount, - fillData: op.fillData, - }; - }); - }, - () => { - logUtils.warn('SamplerContractOperation: Two hop sampler reverted'); - return []; - }, - ); - } - - public getShellSellQuotes( - poolAddress: string, - makerToken: string, - takerToken: string, - takerFillAmounts: BigNumber[], - source: ERC20BridgeSource = ERC20BridgeSource.Shell, - ): SourceQuoteOperation { - return new SamplerContractOperation({ - source, - fillData: { poolAddress }, - contract: this._samplerContract, - function: this._samplerContract.sampleSellsFromShell, - params: [poolAddress, takerToken, makerToken, takerFillAmounts], - }); - } - - public getShellBuyQuotes( - poolAddress: string, - makerToken: string, - takerToken: string, - makerFillAmounts: BigNumber[], - source: ERC20BridgeSource = ERC20BridgeSource.Shell, - ): SourceQuoteOperation { - return new SamplerContractOperation({ - source, - fillData: { poolAddress }, - contract: this._samplerContract, - function: this._samplerContract.sampleBuysFromShell, - params: [poolAddress, takerToken, makerToken, makerFillAmounts], - }); - } - - public getDODOSellQuotes( - opts: { registry: string; helper: string }, - makerToken: string, - takerToken: string, - takerFillAmounts: BigNumber[], - ): SourceQuoteOperation { - return new SamplerContractOperation({ - source: ERC20BridgeSource.Dodo, - contract: this._samplerContract, - function: this._samplerContract.sampleSellsFromDODO, - params: [opts, takerToken, makerToken, takerFillAmounts], - callback: (callResults: string, fillData: DODOFillData): BigNumber[] => { - const [isSellBase, pool, samples] = this._samplerContract.getABIDecodedReturnData< - [boolean, string, BigNumber[]] - >('sampleSellsFromDODO', callResults); - fillData.isSellBase = isSellBase; - fillData.poolAddress = pool; - fillData.helperAddress = opts.helper; - return samples; - }, - }); - } - - public getDODOBuyQuotes( - opts: { registry: string; helper: string }, - makerToken: string, - takerToken: string, - makerFillAmounts: BigNumber[], - ): SourceQuoteOperation { - return new SamplerContractOperation({ - source: ERC20BridgeSource.Dodo, - contract: this._samplerContract, - function: this._samplerContract.sampleBuysFromDODO, - params: [opts, takerToken, makerToken, makerFillAmounts], - callback: (callResults: string, fillData: DODOFillData): BigNumber[] => { - const [isSellBase, pool, samples] = this._samplerContract.getABIDecodedReturnData< - [boolean, string, BigNumber[]] - >('sampleBuysFromDODO', callResults); - fillData.isSellBase = isSellBase; - fillData.poolAddress = pool; - fillData.helperAddress = opts.helper; - return samples; - }, - }); - } - - public getDODOV2SellQuotes( - registry: string, - offset: BigNumber, - makerToken: string, - takerToken: string, - takerFillAmounts: BigNumber[], - ): SourceQuoteOperation { - return new SamplerContractOperation({ - source: ERC20BridgeSource.DodoV2, - contract: this._samplerContract, - function: this._samplerContract.sampleSellsFromDODOV2, - params: [registry, offset, takerToken, makerToken, takerFillAmounts], - callback: (callResults: string, fillData: DODOFillData): BigNumber[] => { - const [isSellBase, pool, samples] = this._samplerContract.getABIDecodedReturnData< - [boolean, string, BigNumber[]] - >('sampleSellsFromDODOV2', callResults); - fillData.isSellBase = isSellBase; - fillData.poolAddress = pool; - return samples; - }, - }); - } - - public getDODOV2BuyQuotes( - registry: string, - offset: BigNumber, - makerToken: string, - takerToken: string, - makerFillAmounts: BigNumber[], - ): SourceQuoteOperation { - return new SamplerContractOperation({ - source: ERC20BridgeSource.DodoV2, - contract: this._samplerContract, - function: this._samplerContract.sampleBuysFromDODOV2, - params: [registry, offset, takerToken, makerToken, makerFillAmounts], - callback: (callResults: string, fillData: DODOFillData): BigNumber[] => { - const [isSellBase, pool, samples] = this._samplerContract.getABIDecodedReturnData< - [boolean, string, BigNumber[]] - >('sampleSellsFromDODOV2', callResults); - fillData.isSellBase = isSellBase; - fillData.poolAddress = pool; - return samples; - }, - }); - } - - public getMakerPsmSellQuotes( - psmInfo: PsmInfo, - makerToken: string, - takerToken: string, - takerFillAmounts: BigNumber[], - ): SourceQuoteOperation { - return new SamplerContractOperation({ - source: ERC20BridgeSource.MakerPsm, - fillData: { - isSellOperation: true, - takerToken, - makerToken, - ...psmInfo, - }, - contract: this._samplerContract, - function: this._samplerContract.sampleSellsFromMakerPsm, - params: [psmInfo, takerToken, makerToken, takerFillAmounts], - }); - } - - public getMakerPsmBuyQuotes( - psmInfo: PsmInfo, - makerToken: string, - takerToken: string, - makerFillAmounts: BigNumber[], - ): SourceQuoteOperation { - return new SamplerContractOperation({ - source: ERC20BridgeSource.MakerPsm, - fillData: { - isSellOperation: false, - takerToken, - makerToken, - ...psmInfo, - }, - contract: this._samplerContract, - function: this._samplerContract.sampleBuysFromMakerPsm, - params: [psmInfo, takerToken, makerToken, makerFillAmounts], - }); - } - - public getLidoSellQuotes( - lidoInfo: LidoInfo, - makerToken: string, - takerToken: string, - takerFillAmounts: BigNumber[], - ): SourceQuoteOperation { - return new SamplerContractOperation({ - source: ERC20BridgeSource.Lido, - fillData: { - makerToken, - takerToken, - stEthTokenAddress: lidoInfo.stEthToken, - wstEthTokenAddress: lidoInfo.wstEthToken, - }, - contract: this._samplerContract, - function: this._samplerContract.sampleSellsFromLido, - params: [lidoInfo, takerToken, makerToken, takerFillAmounts], - }); - } - - public getLidoBuyQuotes( - lidoInfo: LidoInfo, - makerToken: string, - takerToken: string, - makerFillAmounts: BigNumber[], - ): SourceQuoteOperation { - return new SamplerContractOperation({ - source: ERC20BridgeSource.Lido, - fillData: { - makerToken, - takerToken, - stEthTokenAddress: lidoInfo.stEthToken, - wstEthTokenAddress: lidoInfo.wstEthToken, - }, - contract: this._samplerContract, - function: this._samplerContract.sampleBuysFromLido, - params: [lidoInfo, takerToken, makerToken, makerFillAmounts], - }); - } - - // tslint:disable-next-line:prefer-function-over-method - public getAaveV2SellQuotes( - aaveInfo: AaveV2Info, - makerToken: string, - takerToken: string, - takerFillAmounts: BigNumber[], - ): SourceQuoteOperation { - return new SamplerNoOperation({ - source: ERC20BridgeSource.AaveV2, - fillData: { ...aaveInfo, takerToken }, - callback: () => AaveV2Sampler.sampleSellsFromAaveV2(aaveInfo, takerToken, makerToken, takerFillAmounts), - }); - } - - // tslint:disable-next-line:prefer-function-over-method - public getAaveV2BuyQuotes( - aaveInfo: AaveV2Info, - makerToken: string, - takerToken: string, - makerFillAmounts: BigNumber[], - ): SourceQuoteOperation { - return new SamplerNoOperation({ - source: ERC20BridgeSource.AaveV2, - fillData: { ...aaveInfo, takerToken }, - callback: () => AaveV2Sampler.sampleBuysFromAaveV2(aaveInfo, takerToken, makerToken, makerFillAmounts), - }); - } - - // tslint:disable-next-line:prefer-function-over-method - public getGeistSellQuotes( - geistInfo: GeistInfo, - makerToken: string, - takerToken: string, - takerFillAmounts: BigNumber[], - ): SourceQuoteOperation { - return new SamplerNoOperation({ - source: ERC20BridgeSource.Geist, - fillData: { ...geistInfo, takerToken }, - callback: () => GeistSampler.sampleSellsFromGeist(geistInfo, takerToken, makerToken, takerFillAmounts), - }); - } - - // tslint:disable-next-line:prefer-function-over-method - public getGeistBuyQuotes( - geistInfo: GeistInfo, - makerToken: string, - takerToken: string, - makerFillAmounts: BigNumber[], - ): SourceQuoteOperation { - return new SamplerNoOperation({ - source: ERC20BridgeSource.Geist, - fillData: { ...geistInfo, takerToken }, - callback: () => GeistSampler.sampleBuysFromGeist(geistInfo, takerToken, makerToken, makerFillAmounts), - }); - } - - public getCompoundSellQuotes( - cToken: string, - makerToken: string, - takerToken: string, - takerFillAmounts: BigNumber[], - ): SourceQuoteOperation { - return new SamplerContractOperation({ - source: ERC20BridgeSource.Compound, - fillData: { cToken, takerToken, makerToken }, - contract: this._samplerContract, - function: this._samplerContract.sampleSellsFromCompound, - params: [cToken, takerToken, makerToken, takerFillAmounts], - }); - } - - public getCompoundBuyQuotes( - cToken: string, - makerToken: string, - takerToken: string, - makerFillAmounts: BigNumber[], - ): SourceQuoteOperation { - return new SamplerContractOperation({ - source: ERC20BridgeSource.Compound, - fillData: { cToken, takerToken, makerToken }, - contract: this._samplerContract, - function: this._samplerContract.sampleBuysFromCompound, - params: [cToken, takerToken, makerToken, makerFillAmounts], - }); - } - - public getGMXSellQuotes( - router: string, - reader: string, - vault: string, - tokenAddressPath: string[], - takerFillAmounts: BigNumber[], - ): SourceQuoteOperation { - return new SamplerContractOperation({ - source: ERC20BridgeSource.GMX, - fillData: { router, reader, vault, tokenAddressPath }, - contract: this._samplerContract, - function: this._samplerContract.sampleSellsFromGMX, - params: [reader, vault, tokenAddressPath, takerFillAmounts], - }); - } - public getGMXBuyQuotes( - router: string, - reader: string, - vault: string, - tokenAddressPath: string[], - makerFillAmounts: BigNumber[], - ): SourceQuoteOperation { - return new SamplerContractOperation({ - source: ERC20BridgeSource.GMX, - fillData: { router, reader, vault, tokenAddressPath }, - contract: this._samplerContract, - function: this._samplerContract.sampleBuysFromGMX, - params: [reader, vault, tokenAddressPath, makerFillAmounts], - }); - } - - public getPlatypusSellQuotes( - router: string, - pool: string[], - tokenAddressPath: string[], - takerFillAmounts: BigNumber[], - ): SourceQuoteOperation { - return new SamplerContractOperation({ - source: ERC20BridgeSource.Platypus, - fillData: { router, pool, tokenAddressPath }, - contract: this._samplerContract, - function: this._samplerContract.sampleSellsFromPlatypus, - params: [pool[0], tokenAddressPath, takerFillAmounts], - }); - } - - public getPlatypusBuyQuotes( - router: string, - pool: string[], - tokenAddressPath: string[], - makerFillAmounts: BigNumber[], - ): SourceQuoteOperation { - return new SamplerContractOperation({ - source: ERC20BridgeSource.Platypus, - fillData: { router, pool, tokenAddressPath }, - contract: this._samplerContract, - function: this._samplerContract.sampleBuysFromPlatypus, - params: [pool[0], tokenAddressPath, makerFillAmounts], - }); - } - - public getVelodromeSellQuotes( - router: string, - takerToken: string, - makerToken: string, - takerFillAmounts: BigNumber[], - ): SourceQuoteOperation { - return new SamplerContractOperation({ - source: ERC20BridgeSource.Velodrome, - contract: this._samplerContract, - function: this._samplerContract.sampleSellsFromVelodrome, - params: [router, takerToken, makerToken, takerFillAmounts], - callback: (callResults: string, fillData: VelodromeFillData): BigNumber[] => { - const [isStable, samples] = this._samplerContract.getABIDecodedReturnData<[boolean, BigNumber[]]>( - 'sampleSellsFromVelodrome', - callResults, - ); - fillData.router = router; - fillData.stable = isStable; - return samples; - }, - }); - } - - public getVelodromeBuyQuotes( - router: string, - takerToken: string, - makerToken: string, - makerFillAmounts: BigNumber[], - ): SourceQuoteOperation { - return new SamplerContractOperation({ - source: ERC20BridgeSource.Velodrome, - contract: this._samplerContract, - function: this._samplerContract.sampleBuysFromVelodrome, - params: [router, takerToken, makerToken, makerFillAmounts], - callback: (callResults: string, fillData: VelodromeFillData): BigNumber[] => { - const [isStable, samples] = this._samplerContract.getABIDecodedReturnData<[boolean, BigNumber[]]>( - 'sampleBuysFromVelodrome', - callResults, - ); - fillData.router = router; - fillData.stable = isStable; - return samples; - }, - }); - } - - public getSynthetixSellQuotes( - readProxy: string, - takerTokenSymbol: string, - makerTokenSymbol: string, - takerFillAmounts: BigNumber[], - ): SourceQuoteOperation { - const takerTokenSymbolBytes32 = formatBytes32String(takerTokenSymbol); - const makerTokenSymbolBytes32 = formatBytes32String(makerTokenSymbol); - return new SamplerContractOperation({ - source: ERC20BridgeSource.Synthetix, - contract: this._samplerContract, - function: this._samplerContract.sampleSellsFromSynthetix, - params: [readProxy, takerTokenSymbolBytes32, makerTokenSymbolBytes32, takerFillAmounts], - callback: (callResults: string, fillData: SynthetixFillData): BigNumber[] => { - const [synthetix, samples] = this._samplerContract.getABIDecodedReturnData<[string, BigNumber[]]>( - 'sampleSellsFromSynthetix', - callResults, - ); - fillData.synthetix = synthetix; - fillData.takerTokenSymbolBytes32 = takerTokenSymbolBytes32; - fillData.makerTokenSymbolBytes32 = makerTokenSymbolBytes32; - fillData.chainId = this.chainId; - return samples; - }, - }); - } - - public getSynthetixBuyQuotes( - readProxy: string, - takerTokenSymbol: string, - makerTokenSymbol: string, - makerFillAmounts: BigNumber[], - ): SourceQuoteOperation { - const takerTokenSymbolBytes32 = formatBytes32String(takerTokenSymbol); - const makerTokenSymbolBytes32 = formatBytes32String(makerTokenSymbol); - return new SamplerContractOperation({ - source: ERC20BridgeSource.Synthetix, - contract: this._samplerContract, - function: this._samplerContract.sampleBuysFromSynthetix, - params: [readProxy, takerTokenSymbolBytes32, makerTokenSymbolBytes32, makerFillAmounts], - callback: (callResults: string, fillData: SynthetixFillData): BigNumber[] => { - const [synthetix, samples] = this._samplerContract.getABIDecodedReturnData<[string, BigNumber[]]>( - 'sampleBuysFromSynthetix', - callResults, - ); - fillData.synthetix = synthetix; - fillData.takerTokenSymbolBytes32 = takerTokenSymbolBytes32; - fillData.makerTokenSymbolBytes32 = makerTokenSymbolBytes32; - fillData.chainId = this.chainId; - return samples; - }, - }); - } - public getWOOFiSellQuotes( - poolAddress: string, - takerToken: string, - makerToken: string, - makerFillAmounts: BigNumber[], - ): SourceQuoteOperation { - const chainId = this.chainId; - return new SamplerContractOperation({ - fillData: { poolAddress, takerToken, makerToken, chainId }, - source: ERC20BridgeSource.WOOFi, - contract: this._samplerContract, - function: this._samplerContract.sampleSellsFromWooPP, - params: [poolAddress, takerToken, makerToken, makerFillAmounts], - }); - } - - public getWOOFiBuyQuotes( - poolAddress: string, - takerToken: string, - makerToken: string, - makerFillAmounts: BigNumber[], - ): SourceQuoteOperation { - const chainId = this.chainId; - return new SamplerContractOperation({ - fillData: { poolAddress, takerToken, makerToken, chainId }, - source: ERC20BridgeSource.WOOFi, - contract: this._samplerContract, - function: this._samplerContract.sampleBuysFromWooPP, - params: [poolAddress, takerToken, makerToken, makerFillAmounts], - }); - } - - /** - * Returns the best price for the native token - * Best is calculated according to the fee schedule, so the price of the - * best source, fee adjusted, will be returned. - */ - public getBestNativeTokenSellRate( - sources: ERC20BridgeSource[], - makerToken: string, - nativeToken: string, - nativeFillAmount: BigNumber, - feeSchedule: FeeSchedule, - ): BatchedOperation { - if (makerToken.toLowerCase() === nativeToken.toLowerCase()) { - return SamplerOperations.constant(new BigNumber(1)); - } - const subOps = this._getSellQuoteOperations( - sources, - makerToken, - nativeToken, - [nativeFillAmount], - TokenAdjacencyGraph.getEmptyGraph(), - ); - return this._createBatch( - subOps, - (samples: BigNumber[][]) => { - if (samples.length === 0) { - return ZERO_AMOUNT; - } - - const adjustedPrices = subOps.map((s, i) => { - // If the source gave us nothing, skip it and return a default - if (samples[i].length === 0 || samples[i][0].isZero()) { - return { adjustedPrice: ZERO_AMOUNT, source: s.source, price: ZERO_AMOUNT }; - } - const v = samples[i][0]; - const price = v.dividedBy(nativeFillAmount); - // Create an adjusted price to avoid selecting the following: - // * a source that is too expensive to arbitrage given the gas environment - // * when a number of sources are poorly priced or liquidity is low - - // Fee is already gas * gasPrice - const fee = feeSchedule[subOps[i].source] - ? feeSchedule[subOps[i].source]!(subOps[i].fillData).fee - : ZERO_AMOUNT; - const adjustedNativeAmount = nativeFillAmount.plus(fee); - const adjustedPrice = v.div(adjustedNativeAmount); - return { - adjustedPrice, - source: subOps[i].source, - price, - }; - }); - - const sortedPrices = adjustedPrices.sort((a, b) => a.adjustedPrice.comparedTo(b.adjustedPrice)); - const selectedPrice = sortedPrices[sortedPrices.length - 1].price; - - return selectedPrice; - }, - () => ZERO_AMOUNT, - ); - } - - public getSellQuotes( - sources: ERC20BridgeSource[], - makerToken: string, - takerToken: string, - takerFillAmounts: BigNumber[], - ): BatchedOperation { - const subOps = this._getSellQuoteOperations(sources, makerToken, takerToken, takerFillAmounts); - return this._createBatch( - subOps, - (samples: BigNumber[][]) => { - return subOps.map((op, i) => { - return samples[i].map((output, j) => ({ - source: op.source, - output, - input: takerFillAmounts[j], - fillData: op.fillData, - })); - }); - }, - () => [], - ); - } - - public getBuyQuotes( - sources: ERC20BridgeSource[], - makerToken: string, - takerToken: string, - makerFillAmounts: BigNumber[], - ): BatchedOperation { - const subOps = this._getBuyQuoteOperations(sources, makerToken, takerToken, makerFillAmounts); - return this._createBatch( - subOps, - (samples: BigNumber[][]) => { - return subOps.map((op, i) => { - return samples[i].map((output, j) => ({ - source: op.source, - output, - input: makerFillAmounts[j], - fillData: op.fillData, - })); - }); - }, - () => [], - ); - } - - private _getSellQuoteOperations( - sources: ERC20BridgeSource[], - makerToken: string, - takerToken: string, - takerFillAmounts: BigNumber[], - tokenAdjacencyGraph: TokenAdjacencyGraph = this.tokenAdjacencyGraph, - ): SourceQuoteOperation[] { - // Find the adjacent tokens in the provided token adjacency graph, - // e.g if this is DAI->USDC we may check for DAI->WETH->USDC - const intermediateTokens = tokenAdjacencyGraph.getIntermediateTokens(makerToken, takerToken); - // Drop out MultiHop and Native as we do not query those here. - const _sources = SELL_SOURCE_FILTER_BY_CHAIN_ID[this.chainId] - .exclude([ERC20BridgeSource.MultiHop, ERC20BridgeSource.Native]) - .getAllowed(sources); - const allOps = _.flatten( - _sources.map((source): SourceQuoteOperation | SourceQuoteOperation[] => { - if (isBadTokenForSource(makerToken, source) || isBadTokenForSource(takerToken, source)) { - return []; - } - switch (source) { - case ERC20BridgeSource.Uniswap: - return isValidAddress(UNISWAPV1_ROUTER_BY_CHAIN_ID[this.chainId]) - ? this.getUniswapSellQuotes( - UNISWAPV1_ROUTER_BY_CHAIN_ID[this.chainId], - makerToken, - takerToken, - takerFillAmounts, - ) - : []; - case ERC20BridgeSource.UniswapV2: - case ERC20BridgeSource.SushiSwap: - case ERC20BridgeSource.CryptoCom: - case ERC20BridgeSource.PancakeSwap: - case ERC20BridgeSource.PancakeSwapV2: - case ERC20BridgeSource.BakerySwap: - case ERC20BridgeSource.ApeSwap: - case ERC20BridgeSource.CheeseSwap: - case ERC20BridgeSource.QuickSwap: - case ERC20BridgeSource.Dfyn: - case ERC20BridgeSource.WaultSwap: - case ERC20BridgeSource.ShibaSwap: - case ERC20BridgeSource.Pangolin: - case ERC20BridgeSource.TraderJoe: - case ERC20BridgeSource.UbeSwap: - case ERC20BridgeSource.SpiritSwap: - case ERC20BridgeSource.SpookySwap: - case ERC20BridgeSource.Yoshi: - case ERC20BridgeSource.MorpheusSwap: - case ERC20BridgeSource.BiSwap: - case ERC20BridgeSource.MDex: - case ERC20BridgeSource.KnightSwap: - case ERC20BridgeSource.MeshSwap: - const uniLikeRouter = uniswapV2LikeRouterAddress(this.chainId, source); - if (!isValidAddress(uniLikeRouter)) { - return []; - } - return [ - [takerToken, makerToken], - ...intermediateTokens.map(t => [takerToken, t, makerToken]), - ].map(path => this.getUniswapV2SellQuotes(uniLikeRouter, path, takerFillAmounts, source)); - case ERC20BridgeSource.KyberDmm: - const kyberDmmRouter = KYBER_DMM_ROUTER_BY_CHAIN_ID[this.chainId]; - if (!isValidAddress(kyberDmmRouter)) { - return []; - } - return this.getKyberDmmSellQuotes(kyberDmmRouter, [takerToken, makerToken], takerFillAmounts); - case ERC20BridgeSource.Curve: - case ERC20BridgeSource.CurveV2: - case ERC20BridgeSource.Nerve: - case ERC20BridgeSource.Synapse: - case ERC20BridgeSource.Belt: - case ERC20BridgeSource.Ellipsis: - case ERC20BridgeSource.Saddle: - case ERC20BridgeSource.XSigma: - case ERC20BridgeSource.FirebirdOneSwap: - case ERC20BridgeSource.IronSwap: - case ERC20BridgeSource.ACryptos: - case ERC20BridgeSource.MobiusMoney: - return getCurveLikeInfosForPair(this.chainId, takerToken, makerToken, source).map(pool => - this.getCurveSellQuotes( - pool, - pool.takerTokenIdx, - pool.makerTokenIdx, - takerFillAmounts, - source, - ), - ); - case ERC20BridgeSource.Shell: - case ERC20BridgeSource.Component: - return getShellLikeInfosForPair(this.chainId, takerToken, makerToken, source).map(pool => - this.getShellSellQuotes(pool, makerToken, takerToken, takerFillAmounts, source), - ); - case ERC20BridgeSource.LiquidityProvider: - return getLiquidityProvidersForPair( - this.liquidityProviderRegistry, - takerToken, - makerToken, - ).map(({ providerAddress, gasCost }) => - this.getLiquidityProviderSellQuotes( - providerAddress, - makerToken, - takerToken, - takerFillAmounts, - gasCost, - ), - ); - case ERC20BridgeSource.MStable: - return getShellLikeInfosForPair(this.chainId, takerToken, makerToken, source).map(pool => - this.getMStableSellQuotes(pool, makerToken, takerToken, takerFillAmounts), - ); - case ERC20BridgeSource.Mooniswap: - return [ - ...MOONISWAP_REGISTRIES_BY_CHAIN_ID[this.chainId] - .filter(r => isValidAddress(r)) - .map(registry => - this.getMooniswapSellQuotes(registry, makerToken, takerToken, takerFillAmounts), - ), - ]; - case ERC20BridgeSource.Balancer: - return this.poolsCaches[ERC20BridgeSource.Balancer] - .getPoolAddressesForPair(takerToken, makerToken) - .map(balancerPool => - this.getBalancerSellQuotes( - balancerPool, - makerToken, - takerToken, - takerFillAmounts, - ERC20BridgeSource.Balancer, - ), - ); - case ERC20BridgeSource.BalancerV2: { - const cache = this.poolsCaches[source]; - if (!cache) { - return []; - } - - const swaps = cache.getCachedSwapInfoForPair(takerToken, makerToken); - const vault = BALANCER_V2_VAULT_ADDRESS_BY_CHAIN[this.chainId]; - if (!swaps || vault === NULL_ADDRESS) { - return []; - } - // Changed to retrieve queryBatchSwap for swap steps > 1 of length - return swaps.swapInfoExactIn.map(swapInfo => - this.getBalancerV2MultihopSellQuotes(vault, swapInfo, swapInfo, takerFillAmounts, source), - ); - } - case ERC20BridgeSource.Beethovenx: { - const cache = this.poolsCaches[source]; - const poolAddresses = cache.getPoolAddressesForPair(takerToken, makerToken); - const vault = BEETHOVEN_X_VAULT_ADDRESS_BY_CHAIN[this.chainId]; - if (vault === NULL_ADDRESS) { - return []; - } - return poolAddresses.map(poolAddress => - this.getBalancerV2SellQuotes( - { poolId: poolAddress, vault }, - makerToken, - takerToken, - takerFillAmounts, - source, - ), - ); - } - case ERC20BridgeSource.Dodo: - if (!isValidAddress(DODOV1_CONFIG_BY_CHAIN_ID[this.chainId].registry)) { - return []; - } - return this.getDODOSellQuotes( - DODOV1_CONFIG_BY_CHAIN_ID[this.chainId], - makerToken, - takerToken, - takerFillAmounts, - ); - case ERC20BridgeSource.DodoV2: - return _.flatten( - DODOV2_FACTORIES_BY_CHAIN_ID[this.chainId] - .filter(factory => isValidAddress(factory)) - .map(factory => - getDodoV2Offsets().map(offset => - this.getDODOV2SellQuotes( - factory, - offset, - makerToken, - takerToken, - takerFillAmounts, - ), - ), - ), - ); - case ERC20BridgeSource.Bancor: - if (!isValidAddress(BANCOR_REGISTRY_BY_CHAIN_ID[this.chainId])) { - return []; - } - return this.getBancorSellQuotes( - BANCOR_REGISTRY_BY_CHAIN_ID[this.chainId], - makerToken, - takerToken, - takerFillAmounts, - ); - case ERC20BridgeSource.MakerPsm: - const psmInfo = MAKER_PSM_INFO_BY_CHAIN_ID[this.chainId]; - if (!isValidAddress(psmInfo.psmAddress)) { - return []; - } - return this.getMakerPsmSellQuotes(psmInfo, makerToken, takerToken, takerFillAmounts); - case ERC20BridgeSource.UniswapV3: { - const { quoter, router } = UNISWAPV3_CONFIG_BY_CHAIN_ID[this.chainId]; - if (!isValidAddress(router) || !isValidAddress(quoter)) { - return []; - } - return [ - [takerToken, makerToken], - ...intermediateTokens.map(t => [takerToken, t, makerToken]), - ].map(path => this.getUniswapV3SellQuotes(router, quoter, path, takerFillAmounts)); - } - case ERC20BridgeSource.Lido: { - if (!this._isLidoSupported(takerToken, makerToken)) { - return []; - } - const lidoInfo = LIDO_INFO_BY_CHAIN[this.chainId]; - return this.getLidoSellQuotes(lidoInfo, makerToken, takerToken, takerFillAmounts); - } - case ERC20BridgeSource.AaveV2: { - if (!this.aaveReservesCache) { - return []; - } - const reserve = this.aaveReservesCache.get(takerToken, makerToken); - if (!reserve) { - return []; - } - - const info: AaveV2Info = { - lendingPool: reserve.pool.lendingPool, - aToken: reserve.aToken.id, - underlyingToken: reserve.underlyingAsset, - }; - return this.getAaveV2SellQuotes(info, makerToken, takerToken, takerFillAmounts); - } - case ERC20BridgeSource.Geist: { - const info: GeistInfo | undefined = getGeistInfoForPair(takerToken, makerToken); - if (!info) { - return []; - } - return this.getGeistSellQuotes(info, makerToken, takerToken, takerFillAmounts); - } - case ERC20BridgeSource.Compound: { - if (!this.compoundCTokenCache) { - return []; - } - - const cToken = this.compoundCTokenCache.get(takerToken, makerToken); - if (!cToken) { - return []; - } - return this.getCompoundSellQuotes( - cToken.tokenAddress, - makerToken, - takerToken, - takerFillAmounts, - ); - } - case ERC20BridgeSource.GMX: { - // MIM has no liquidity. - if (takerToken === AVALANCHE_TOKENS.MIM || makerToken === AVALANCHE_TOKENS.MIM) { - return []; - } - return this.getGMXSellQuotes( - GMX_ROUTER_BY_CHAIN_ID[this.chainId], - GMX_READER_BY_CHAIN_ID[this.chainId], - GMX_VAULT_BY_CHAIN_ID[this.chainId], - [takerToken, makerToken], - takerFillAmounts, - ); - } - case ERC20BridgeSource.Platypus: { - return getPlatypusInfoForPair(this.chainId, takerToken, makerToken).map(pool => - this.getPlatypusSellQuotes( - PLATYPUS_ROUTER_BY_CHAIN_ID[this.chainId], - [pool.poolAddress], - [takerToken, makerToken], - takerFillAmounts, - ), - ); - } - case ERC20BridgeSource.BancorV3: { - return this.getBancorV3SellQuotes( - BANCORV3_NETWORK_BY_CHAIN_ID[this.chainId], - BANCORV3_NETWORK_INFO_BY_CHAIN_ID[this.chainId], - [takerToken, makerToken], - takerFillAmounts, - ); - } - case ERC20BridgeSource.Velodrome: { - return this.getVelodromeSellQuotes( - VELODROME_ROUTER_BY_CHAIN_ID[this.chainId], - takerToken, - makerToken, - takerFillAmounts, - ); - } - case ERC20BridgeSource.Synthetix: { - const readProxy = SYNTHETIX_READ_PROXY_BY_CHAIN_ID[this.chainId]; - const currencyKeyMap = SYNTHETIX_CURRENCY_KEYS_BY_CHAIN_ID[this.chainId]; - const takerTokenSymbol = currencyKeyMap.get(takerToken.toLowerCase()); - const makerTokenSymbol = currencyKeyMap.get(makerToken.toLowerCase()); - if (takerTokenSymbol === undefined || makerTokenSymbol === undefined) { - return []; - } - return this.getSynthetixSellQuotes( - readProxy, - takerTokenSymbol, - makerTokenSymbol, - takerFillAmounts, - ); - } - case ERC20BridgeSource.WOOFi: { - if (!(WOOFI_SUPPORTED_TOKENS.has(takerToken) && WOOFI_SUPPORTED_TOKENS.has(makerToken))) { - return []; - } - return this.getWOOFiSellQuotes( - WOOFI_POOL_BY_CHAIN_ID[this.chainId], - takerToken, - makerToken, - takerFillAmounts, - ); - } - default: - throw new Error(`Unsupported sell sample source: ${source}`); - } - }), - ); - return allOps; - } - - private _isLidoSupported(takerTokenAddress: string, makerTokenAddress: string): boolean { - const lidoInfo = LIDO_INFO_BY_CHAIN[this.chainId]; - if (lidoInfo.wethToken === NULL_ADDRESS) { - return false; - } - const takerToken = takerTokenAddress.toLowerCase(); - const makerToken = makerTokenAddress.toLowerCase(); - const wethToken = lidoInfo.wethToken.toLowerCase(); - const stEthToken = lidoInfo.stEthToken.toLowerCase(); - const wstEthToken = lidoInfo.wstEthToken.toLowerCase(); - - if (takerToken === wethToken && makerToken === stEthToken) { - return true; - } - - return _.difference([stEthToken, wstEthToken], [takerToken, makerToken]).length === 0; - } - - private _getBuyQuoteOperations( - sources: ERC20BridgeSource[], - makerToken: string, - takerToken: string, - makerFillAmounts: BigNumber[], - ): SourceQuoteOperation[] { - // Find the adjacent tokens in the provided token adjacency graph, - // e.g if this is DAI->USDC we may check for DAI->WETH->USDC - const intermediateTokens = this.tokenAdjacencyGraph.getIntermediateTokens(makerToken, takerToken); - const _sources = BATCH_SOURCE_FILTERS.getAllowed(sources); - return _.flatten( - _sources.map((source): SourceQuoteOperation | SourceQuoteOperation[] => { - switch (source) { - case ERC20BridgeSource.Uniswap: - return isValidAddress(UNISWAPV1_ROUTER_BY_CHAIN_ID[this.chainId]) - ? this.getUniswapBuyQuotes( - UNISWAPV1_ROUTER_BY_CHAIN_ID[this.chainId], - makerToken, - takerToken, - makerFillAmounts, - ) - : []; - case ERC20BridgeSource.UniswapV2: - case ERC20BridgeSource.SushiSwap: - case ERC20BridgeSource.CryptoCom: - case ERC20BridgeSource.PancakeSwap: - case ERC20BridgeSource.PancakeSwapV2: - case ERC20BridgeSource.BakerySwap: - case ERC20BridgeSource.ApeSwap: - case ERC20BridgeSource.CheeseSwap: - case ERC20BridgeSource.QuickSwap: - case ERC20BridgeSource.Dfyn: - case ERC20BridgeSource.WaultSwap: - case ERC20BridgeSource.ShibaSwap: - case ERC20BridgeSource.Pangolin: - case ERC20BridgeSource.TraderJoe: - case ERC20BridgeSource.UbeSwap: - case ERC20BridgeSource.SpiritSwap: - case ERC20BridgeSource.SpookySwap: - case ERC20BridgeSource.Yoshi: - case ERC20BridgeSource.MorpheusSwap: - case ERC20BridgeSource.BiSwap: - case ERC20BridgeSource.MDex: - case ERC20BridgeSource.KnightSwap: - case ERC20BridgeSource.MeshSwap: - const uniLikeRouter = uniswapV2LikeRouterAddress(this.chainId, source); - if (!isValidAddress(uniLikeRouter)) { - return []; - } - return [ - [takerToken, makerToken], - ...intermediateTokens.map(t => [takerToken, t, makerToken]), - ].map(path => this.getUniswapV2BuyQuotes(uniLikeRouter, path, makerFillAmounts, source)); - case ERC20BridgeSource.KyberDmm: - const kyberDmmRouter = KYBER_DMM_ROUTER_BY_CHAIN_ID[this.chainId]; - if (!isValidAddress(kyberDmmRouter)) { - return []; - } - return this.getKyberDmmBuyQuotes(kyberDmmRouter, [takerToken, makerToken], makerFillAmounts); - case ERC20BridgeSource.Curve: - case ERC20BridgeSource.CurveV2: - case ERC20BridgeSource.Nerve: - case ERC20BridgeSource.Synapse: - case ERC20BridgeSource.Belt: - case ERC20BridgeSource.Ellipsis: - case ERC20BridgeSource.Saddle: - case ERC20BridgeSource.XSigma: - case ERC20BridgeSource.FirebirdOneSwap: - case ERC20BridgeSource.IronSwap: - case ERC20BridgeSource.ACryptos: - case ERC20BridgeSource.MobiusMoney: - return getCurveLikeInfosForPair(this.chainId, takerToken, makerToken, source).map(pool => - this.getCurveBuyQuotes( - pool, - pool.takerTokenIdx, - pool.makerTokenIdx, - makerFillAmounts, - source, - ), - ); - case ERC20BridgeSource.Shell: - case ERC20BridgeSource.Component: - return getShellLikeInfosForPair(this.chainId, takerToken, makerToken, source).map(pool => - this.getShellBuyQuotes(pool, makerToken, takerToken, makerFillAmounts, source), - ); - case ERC20BridgeSource.LiquidityProvider: - return getLiquidityProvidersForPair( - this.liquidityProviderRegistry, - takerToken, - makerToken, - ).map(({ providerAddress, gasCost }) => - this.getLiquidityProviderBuyQuotes( - providerAddress, - makerToken, - takerToken, - makerFillAmounts, - gasCost, - ), - ); - case ERC20BridgeSource.MStable: - return getShellLikeInfosForPair(this.chainId, takerToken, makerToken, source).map(pool => - this.getMStableBuyQuotes(pool, makerToken, takerToken, makerFillAmounts), - ); - case ERC20BridgeSource.Mooniswap: - return [ - ...MOONISWAP_REGISTRIES_BY_CHAIN_ID[this.chainId] - .filter(r => isValidAddress(r)) - .map(registry => - this.getMooniswapBuyQuotes(registry, makerToken, takerToken, makerFillAmounts), - ), - ]; - case ERC20BridgeSource.Balancer: - return this.poolsCaches[ERC20BridgeSource.Balancer] - .getPoolAddressesForPair(takerToken, makerToken) - .map(poolAddress => - this.getBalancerBuyQuotes( - poolAddress, - makerToken, - takerToken, - makerFillAmounts, - ERC20BridgeSource.Balancer, - ), - ); - case ERC20BridgeSource.BalancerV2: { - const cache = this.poolsCaches[source]; - if (!cache) { - return []; - } - - const swaps = cache.getCachedSwapInfoForPair(takerToken, makerToken); - const vault = BALANCER_V2_VAULT_ADDRESS_BY_CHAIN[this.chainId]; - if (!swaps || vault === NULL_ADDRESS) { - return []; - } - // Changed to retrieve queryBatchSwap for swap steps > 1 of length - return swaps.swapInfoExactOut.map((quoteSwapInfo, i) => - this.getBalancerV2MultihopBuyQuotes( - vault, - quoteSwapInfo, - swaps.swapInfoExactIn[i], - makerFillAmounts, - source, - ), - ); - } - case ERC20BridgeSource.Beethovenx: { - const cache = this.poolsCaches[source]; - const poolIds = cache.getPoolAddressesForPair(takerToken, makerToken) || []; - const vault = BEETHOVEN_X_VAULT_ADDRESS_BY_CHAIN[this.chainId]; - if (vault === NULL_ADDRESS) { - return []; - } - return poolIds.map(poolId => - this.getBalancerV2BuyQuotes( - { poolId, vault }, - makerToken, - takerToken, - makerFillAmounts, - source, - ), - ); - } - case ERC20BridgeSource.Dodo: - if (!isValidAddress(DODOV1_CONFIG_BY_CHAIN_ID[this.chainId].registry)) { - return []; - } - return this.getDODOBuyQuotes( - DODOV1_CONFIG_BY_CHAIN_ID[this.chainId], - makerToken, - takerToken, - makerFillAmounts, - ); - case ERC20BridgeSource.DodoV2: - return _.flatten( - DODOV2_FACTORIES_BY_CHAIN_ID[this.chainId] - .filter(factory => isValidAddress(factory)) - .map(factory => - getDodoV2Offsets().map(offset => - this.getDODOV2BuyQuotes( - factory, - offset, - makerToken, - takerToken, - makerFillAmounts, - ), - ), - ), - ); - case ERC20BridgeSource.Bancor: - // Unimplemented - // return this.getBancorBuyQuotes(makerToken, takerToken, makerFillAmounts); - return []; - case ERC20BridgeSource.MakerPsm: - const psmInfo = MAKER_PSM_INFO_BY_CHAIN_ID[this.chainId]; - if (!isValidAddress(psmInfo.psmAddress)) { - return []; - } - return this.getMakerPsmBuyQuotes(psmInfo, makerToken, takerToken, makerFillAmounts); - case ERC20BridgeSource.UniswapV3: { - const { quoter, router } = UNISWAPV3_CONFIG_BY_CHAIN_ID[this.chainId]; - if (!isValidAddress(router) || !isValidAddress(quoter)) { - return []; - } - return [ - [takerToken, makerToken], - ...intermediateTokens.map(t => [takerToken, t, makerToken]), - ].map(path => this.getUniswapV3BuyQuotes(router, quoter, path, makerFillAmounts)); - } - case ERC20BridgeSource.Lido: { - if (!this._isLidoSupported(takerToken, makerToken)) { - return []; - } - const lidoInfo = LIDO_INFO_BY_CHAIN[this.chainId]; - return this.getLidoBuyQuotes(lidoInfo, makerToken, takerToken, makerFillAmounts); - } - case ERC20BridgeSource.AaveV2: { - if (!this.aaveReservesCache) { - return []; - } - const reserve = this.aaveReservesCache.get(takerToken, makerToken); - if (!reserve) { - return []; - } - const info: AaveV2Info = { - lendingPool: reserve.pool.lendingPool, - aToken: reserve.aToken.id, - underlyingToken: reserve.underlyingAsset, - }; - return this.getAaveV2BuyQuotes(info, makerToken, takerToken, makerFillAmounts); - } - case ERC20BridgeSource.Geist: { - const info: GeistInfo | undefined = getGeistInfoForPair(takerToken, makerToken); - if (!info) { - return []; - } - return this.getGeistBuyQuotes(info, makerToken, takerToken, makerFillAmounts); - } - case ERC20BridgeSource.Compound: { - if (!this.compoundCTokenCache) { - return []; - } - - const cToken = this.compoundCTokenCache.get(takerToken, makerToken); - if (!cToken) { - return []; - } - return this.getCompoundBuyQuotes(cToken.tokenAddress, makerToken, takerToken, makerFillAmounts); - } - case ERC20BridgeSource.GMX: { - // MIM has no liquidity. - if (takerToken === AVALANCHE_TOKENS.MIM || makerToken === AVALANCHE_TOKENS.MIM) { - return []; - } - return this.getGMXBuyQuotes( - GMX_ROUTER_BY_CHAIN_ID[this.chainId], - GMX_READER_BY_CHAIN_ID[this.chainId], - GMX_VAULT_BY_CHAIN_ID[this.chainId], - [takerToken, makerToken], - makerFillAmounts, - ); - } - case ERC20BridgeSource.Platypus: { - return getPlatypusInfoForPair(this.chainId, takerToken, makerToken).map(pool => - this.getPlatypusBuyQuotes( - PLATYPUS_ROUTER_BY_CHAIN_ID[this.chainId], - [pool.poolAddress], - [takerToken, makerToken], - makerFillAmounts, - ), - ); - } - case ERC20BridgeSource.BancorV3: { - return this.getBancorV3BuyQuotes( - BANCORV3_NETWORK_BY_CHAIN_ID[this.chainId], - BANCORV3_NETWORK_INFO_BY_CHAIN_ID[this.chainId], - [takerToken, makerToken], - makerFillAmounts, - ); - } - case ERC20BridgeSource.Velodrome: { - return this.getVelodromeBuyQuotes( - VELODROME_ROUTER_BY_CHAIN_ID[this.chainId], - takerToken, - makerToken, - makerFillAmounts, - ); - } - case ERC20BridgeSource.Synthetix: { - const readProxy = SYNTHETIX_READ_PROXY_BY_CHAIN_ID[this.chainId]; - const currencyKeyMap = SYNTHETIX_CURRENCY_KEYS_BY_CHAIN_ID[this.chainId]; - const takerTokenSymbol = currencyKeyMap.get(takerToken.toLowerCase()); - const makerTokenSymbol = currencyKeyMap.get(makerToken.toLowerCase()); - if (takerTokenSymbol === undefined || makerTokenSymbol === undefined) { - return []; - } - return this.getSynthetixBuyQuotes( - readProxy, - takerTokenSymbol, - makerTokenSymbol, - makerFillAmounts, - ); - } - case ERC20BridgeSource.WOOFi: { - if (!(WOOFI_SUPPORTED_TOKENS.has(takerToken) && WOOFI_SUPPORTED_TOKENS.has(makerToken))) { - return []; - } - return this.getWOOFiBuyQuotes( - WOOFI_POOL_BY_CHAIN_ID[this.chainId], - takerToken, - makerToken, - makerFillAmounts, - ); - } - default: - throw new Error(`Unsupported buy sample source: ${source}`); - } - }), - ); - } - - /** - * Wraps `subOps` operations into a batch call to the sampler - * @param subOps An array of Sampler operations - * @param resultHandler The handler of the parsed batch results - * @param revertHandler The handle for when the batch operation reverts. The result data is provided as an argument - */ - private _createBatch( - subOps: Array>, - resultHandler: (results: TResult[]) => T, - revertHandler: (result: string) => T, - ): BatchedOperation { - return { - encodeCall: () => { - const subCalls = subOps.map(op => op.encodeCall()); - return this._samplerContract.batchCall(subCalls).getABIEncodedTransactionData(); - }, - handleCallResults: callResults => { - const rawSubCallResults = this._samplerContract.getABIDecodedReturnData( - 'batchCall', - callResults, - ); - const results = subOps.map((op, i) => - rawSubCallResults[i].success - ? op.handleCallResults(rawSubCallResults[i].data) - : op.handleRevert(rawSubCallResults[i].data), - ); - return resultHandler(results); - }, - handleRevert: revertHandler, - }; - } -} -// tslint:disable max-file-line-count diff --git a/packages/asset-swapper/src/utils/market_operation_utils/source_filters.ts b/packages/asset-swapper/src/utils/market_operation_utils/source_filters.ts deleted file mode 100644 index 407673c02a..0000000000 --- a/packages/asset-swapper/src/utils/market_operation_utils/source_filters.ts +++ /dev/null @@ -1,95 +0,0 @@ -import * as _ from 'lodash'; - -import { ERC20BridgeSource } from './types'; - -export class SourceFilters { - // All valid sources. - private readonly _validSources: ERC20BridgeSource[]; - // Sources in `_validSources` that are not allowed. - private readonly _excludedSources: ERC20BridgeSource[]; - // Sources in `_validSources` that are only allowed. - private readonly _includedSources: ERC20BridgeSource[]; - - public static all(): SourceFilters { - return new SourceFilters(Object.values(ERC20BridgeSource)); - } - - constructor( - validSources: ERC20BridgeSource[] = [], - excludedSources: ERC20BridgeSource[] = [], - includedSources: ERC20BridgeSource[] = [], - ) { - this._validSources = _.uniq(validSources); - this._excludedSources = _.uniq(excludedSources); - this._includedSources = _.uniq(includedSources); - } - - public isAllowed(source: ERC20BridgeSource): boolean { - // Must be in list of valid sources. - if (this._validSources.length > 0 && !this._validSources.includes(source)) { - return false; - } - // Must not be excluded. - if (this._excludedSources.includes(source)) { - return false; - } - // If we have an inclusion list, it must be in that list. - if (this._includedSources.length > 0 && !this._includedSources.includes(source)) { - return false; - } - return true; - } - - public areAnyAllowed(sources: ERC20BridgeSource[]): boolean { - return sources.some(s => this.isAllowed(s)); - } - - public areAllAllowed(sources: ERC20BridgeSource[]): boolean { - return sources.every(s => this.isAllowed(s)); - } - - public getAllowed(sources: ERC20BridgeSource[] = []): ERC20BridgeSource[] { - return sources.filter(s => this.isAllowed(s)); - } - - public get sources(): ERC20BridgeSource[] { - return this._validSources.filter(s => this.isAllowed(s)); - } - - public exclude(sources: ERC20BridgeSource | ERC20BridgeSource[]): SourceFilters { - return new SourceFilters( - this._validSources, - [...this._excludedSources, ...(Array.isArray(sources) ? sources : [sources])], - this._includedSources, - ); - } - - public validate(sources: ERC20BridgeSource | ERC20BridgeSource[]): SourceFilters { - return new SourceFilters( - [...this._validSources, ...(Array.isArray(sources) ? sources : [sources])], - this._excludedSources, - this._includedSources, - ); - } - - public include(sources: ERC20BridgeSource | ERC20BridgeSource[]): SourceFilters { - return new SourceFilters(this._validSources, this._excludedSources, [ - ...this._includedSources, - ...(Array.isArray(sources) ? sources : [sources]), - ]); - } - - public merge(other: SourceFilters): SourceFilters { - let validSources = this._validSources; - if (validSources.length === 0) { - validSources = other._validSources; - } else if (other._validSources.length !== 0) { - validSources = validSources.filter(s => other._validSources.includes(s)); - } - return new SourceFilters( - validSources, - [...this._excludedSources, ...other._excludedSources], - [...this._includedSources, ...other._includedSources], - ); - } -} diff --git a/packages/asset-swapper/src/utils/market_operation_utils/types.ts b/packages/asset-swapper/src/utils/market_operation_utils/types.ts deleted file mode 100644 index 44c5ac16bf..0000000000 --- a/packages/asset-swapper/src/utils/market_operation_utils/types.ts +++ /dev/null @@ -1,678 +0,0 @@ -import { ChainId } from '@0x/contract-addresses'; -import { - FillQuoteTransformerLimitOrderInfo, - FillQuoteTransformerOrderType, - FillQuoteTransformerRfqOrderInfo, -} from '@0x/protocol-utils'; -import { MarketOperation } from '@0x/types'; -import { BigNumber } from '@0x/utils'; - -import { NativeOrderWithFillableAmounts, RfqFirmQuoteValidator, RfqRequestOpts } from '../../types'; -import { QuoteRequestor, V4RFQIndicativeQuoteMM } from '../../utils/quote_requestor'; -import { IRfqClient } from '../irfq_client'; -import { ExtendedQuoteReportSources, PriceComparisonsReport, QuoteReport } from '../quote_report_generator'; -import { TokenAdjacencyGraph } from '../token_adjacency_graph'; - -import { SourceFilters } from './source_filters'; - -/** - * Order domain keys: chainId and exchange - */ -export interface OrderDomain { - chainId: number; - exchangeAddress: string; -} - -/** - * Common exception messages thrown by aggregation logic. - */ -export enum AggregationError { - NoOptimalPath = 'NO_OPTIMAL_PATH', - EmptyOrders = 'EMPTY_ORDERS', - NotERC20AssetData = 'NOT_ERC20ASSET_DATA', - NoBridgeForSource = 'NO_BRIDGE_FOR_SOURCE', -} - -/** - * DEX sources to aggregate. - */ -export enum ERC20BridgeSource { - Native = 'Native', - Uniswap = 'Uniswap', - UniswapV2 = 'Uniswap_V2', - Curve = 'Curve', - LiquidityProvider = 'LiquidityProvider', - MultiBridge = 'MultiBridge', - Balancer = 'Balancer', - BalancerV2 = 'Balancer_V2', - Bancor = 'Bancor', - MakerPsm = 'MakerPsm', - MStable = 'mStable', - Mooniswap = 'Mooniswap', - MultiHop = 'MultiHop', - Shell = 'Shell', - SushiSwap = 'SushiSwap', - Dodo = 'DODO', - DodoV2 = 'DODO_V2', - CryptoCom = 'CryptoCom', - KyberDmm = 'KyberDMM', - Component = 'Component', - Saddle = 'Saddle', - XSigma = 'xSigma', - UniswapV3 = 'Uniswap_V3', - CurveV2 = 'Curve_V2', - Lido = 'Lido', - ShibaSwap = 'ShibaSwap', - AaveV2 = 'Aave_V2', - Compound = 'Compound', - Synapse = 'Synapse', - BancorV3 = 'BancorV3', - Synthetix = 'Synthetix', - WOOFi = 'WOOFi', - // BSC only - PancakeSwap = 'PancakeSwap', - PancakeSwapV2 = 'PancakeSwap_V2', - BiSwap = 'BiSwap', - MDex = 'MDex', - KnightSwap = 'KnightSwap', - BakerySwap = 'BakerySwap', - Nerve = 'Nerve', - Belt = 'Belt', - Ellipsis = 'Ellipsis', - ApeSwap = 'ApeSwap', - CheeseSwap = 'CheeseSwap', - ACryptos = 'ACryptoS', - // Polygon only - QuickSwap = 'QuickSwap', - Dfyn = 'Dfyn', - WaultSwap = 'WaultSwap', - FirebirdOneSwap = 'FirebirdOneSwap', - IronSwap = 'IronSwap', - MeshSwap = 'MeshSwap', - // Avalanche - Pangolin = 'Pangolin', - TraderJoe = 'TraderJoe', - Platypus = 'Platypus', - // tslint:disable: enum-naming - GMX = 'GMX', - // Celo only - UbeSwap = 'UbeSwap', - MobiusMoney = 'MobiusMoney', - // Fantom - SpiritSwap = 'SpiritSwap', - SpookySwap = 'SpookySwap', - Beethovenx = 'Beethovenx', - MorpheusSwap = 'MorpheusSwap', - Yoshi = 'Yoshi', - Geist = 'Geist', - // Optimism - Velodrome = 'Velodrome', -} - -// tslint:disable: enum-naming -/** - * Curve contract function selectors. - */ -export enum CurveFunctionSelectors { - None = '0x00000000', - exchange = '0x3df02124', - exchange_underlying = '0xa6417ed6', // exchange_underlying(int128 i, int128 j, uint256 dx, uint256 min_dy) - get_dy_underlying = '0x07211ef7', - get_dx_underlying = '0x0e71d1b9', - get_dy = '0x5e0d443f', // get_dy(int128,int128,uint256) - get_dx = '0x67df02ca', - get_dy_uint256 = '0x556d6e9f', // get_dy(uint256,uint256,uint256) - exchange_underlying_uint256 = '0x65b2489b', // exchange_underlying(uint256,uint256,uint256,uint256) - // Curve V2 - exchange_v2 = '0x5b41b908', - exchange_underlying_v2 = '0x65b2489b', - get_dy_v2 = '0x556d6e9f', - get_dy_underlying_v2 = '0x85f11d1e', - // Smoothy(deprecated) - swap_uint256 = '0x5673b02d', // swap(uint256,uint256,uint256,uint256) - get_swap_amount = '0x45cf2ef6', // getSwapAmount(uint256,uint256,uint256) - // Nerve BSC, Saddle Mainnet, Synapse - swap = '0x91695586', // swap(uint8,uint8,uint256,uint256,uint256) - calculateSwap = '0xa95b089f', // calculateSwap(uint8,uint8,uint256) -} -// tslint:enable: enum-naming - -/** - * Configuration info on a Curve pool. - */ -export interface CurveInfo { - exchangeFunctionSelector: CurveFunctionSelectors; - sellQuoteFunctionSelector: CurveFunctionSelectors; - buyQuoteFunctionSelector: CurveFunctionSelectors; - poolAddress: string; - tokens: string[]; - metaTokens: string[] | undefined; - gasSchedule: number; -} - -/** - * Configuration for a specific PSM vault - */ -export interface PsmInfo { - psmAddress: string; - ilkIdentifier: string; - gemTokenAddress: string; -} - -/** - * Configuration for a Lido deployment - */ -export interface LidoInfo { - stEthToken: string; - wethToken: string; - wstEthToken: string; -} - -/** - * Configuration info for a Balancer V2 pool. - */ -export interface BalancerV2PoolInfo { - poolId: string; - vault: string; -} - -export interface AaveV2Info { - lendingPool: string; - aToken: string; - underlyingToken: string; -} - -export interface GeistInfo { - lendingPool: string; - gToken: string; - underlyingToken: string; -} - -// Internal `fillData` field for `Fill` objects. -export interface FillData {} - -// `FillData` for native fills. Represents a single native order -export type NativeRfqOrderFillData = FillQuoteTransformerRfqOrderInfo; -export type NativeLimitOrderFillData = FillQuoteTransformerLimitOrderInfo; -export type NativeFillData = NativeRfqOrderFillData | NativeLimitOrderFillData; - -// Represents an individual DEX sample from the sampler contract -export interface DexSample { - source: ERC20BridgeSource; - fillData: TFillData; - input: BigNumber; - output: BigNumber; -} -export interface CurveFillData extends FillData { - fromTokenIdx: number; - toTokenIdx: number; - pool: CurveInfo; -} - -export interface BalancerBatchSwapStep { - poolId: string; - assetInIndex: number; - assetOutIndex: number; - amount: BigNumber; - userData: string; -} - -export interface BalancerSwaps { - swapInfoExactIn: BalancerSwapInfo[]; - swapInfoExactOut: BalancerSwapInfo[]; -} -export interface BalancerSwapInfo { - assets: string[]; - swapSteps: BalancerBatchSwapStep[]; -} - -export interface BalancerFillData extends FillData { - poolAddress: string; -} - -export interface BalancerV2FillData extends FillData { - vault: string; - poolId: string; -} - -export interface BalancerV2BatchSwapFillData extends FillData { - vault: string; - swapSteps: BalancerBatchSwapStep[]; - assets: string[]; -} - -export interface UniswapV2FillData extends FillData { - tokenAddressPath: string[]; - router: string; -} - -export interface ShellFillData extends FillData { - poolAddress: string; -} - -export interface LiquidityProviderFillData extends FillData { - poolAddress: string; - gasCost: number; -} - -export interface BancorFillData extends FillData { - path: string[]; - networkAddress: string; -} - -export interface BancorV3FillData extends FillData { - path: string[]; - networkAddress: string; -} - -export interface MooniswapFillData extends FillData { - poolAddress: string; -} - -export interface DODOFillData extends FillData { - poolAddress: string; - isSellBase: boolean; - helperAddress: string; -} - -export interface GenericRouterFillData extends FillData { - router: string; -} - -export interface MultiHopFillData extends FillData { - firstHopSource: SourceQuoteOperation; - secondHopSource: SourceQuoteOperation; - intermediateToken: string; -} - -export interface MakerPsmExtendedData { - isSellOperation: boolean; - takerToken: string; -} - -export type MakerPsmFillData = FillData & MakerPsmExtendedData & PsmInfo; - -export interface HopInfo { - sourceIndex: BigNumber; - returnData: string; -} - -export interface UniswapV3PathAmount { - uniswapPath: string; - inputAmount: BigNumber; - gasUsed: number; -} -export interface UniswapV3FillData extends FillData { - tokenAddressPath: string[]; - router: string; - pathAmounts: UniswapV3PathAmount[]; -} - -export interface KyberDmmFillData extends UniswapV2FillData { - poolsPath: string[]; -} - -/** - * Determines whether FillData is UniswapV3FillData or FinalUniswapV3FillData - */ -export function isFinalUniswapV3FillData( - data: UniswapV3FillData | FinalUniswapV3FillData, -): data is FinalUniswapV3FillData { - return !!(data as FinalUniswapV3FillData).uniswapPath; -} - -export interface FinalUniswapV3FillData extends Omit { - // The uniswap-encoded path that can fll the maximum input amount. - uniswapPath: string; - gasUsed: number; -} - -export interface LidoFillData extends FillData { - stEthTokenAddress: string; - wstEthTokenAddress: string; - takerToken: string; - makerToken: string; -} - -export interface AaveV2FillData extends FillData { - lendingPool: string; - aToken: string; - underlyingToken: string; - takerToken: string; -} - -export interface CompoundFillData extends FillData { - cToken: string; - takerToken: string; - makerToken: string; -} - -export interface GeistFillData extends FillData { - lendingPool: string; - gToken: string; - underlyingToken: string; - takerToken: string; -} - -export interface PlatypusInfo { - poolAddress: string; - tokens: string[]; - gasSchedule: number; -} - -export interface GMXFillData extends FillData { - router: string; - reader: string; - vault: string; - tokenAddressPath: string[]; -} - -export interface PlatypusFillData extends FillData { - router: string; - pool: string[]; - tokenAddressPath: string[]; -} - -export interface WOOFiFillData extends FillData { - poolAddress: string; - takerToken: string; - makerToken: string; - // Only needed for gas estimation - chainId: ChainId; -} - -export interface VelodromeFillData extends FillData { - router: string; - stable: boolean; -} - -export interface SynthetixFillData extends FillData { - synthetix: string; - takerTokenSymbolBytes32: string; - makerTokenSymbolBytes32: string; - // Only needed for gas estimation. - chainId: ChainId; -} - -/** - * Represents a node on a fill path. - */ -export interface Fill { - // basic data for every fill - source: ERC20BridgeSource; - // TODO jacob people seem to agree that orderType here is more readable - type: FillQuoteTransformerOrderType; // should correspond with TFillData - fillData: TFillData; - // Unique ID of the original source path this fill belongs to. - // This is generated when the path is generated and is useful to distinguish - // paths that have the same `source` IDs but are distinct (e.g., Curves). - sourcePathId: string; - // See `SOURCE_FLAGS`. - flags: bigint; - // Input fill amount (taker asset amount in a sell, maker asset amount in a buy). - input: BigNumber; - // Output fill amount (maker asset amount in a sell, taker asset amount in a buy). - output: BigNumber; - // The output fill amount, adjusted by fees. - adjustedOutput: BigNumber; - // The expected gas cost of this fill - gas: number; -} - -export interface OptimizedMarketOrderBase { - source: ERC20BridgeSource; - fillData: TFillData; - type: FillQuoteTransformerOrderType; // should correspond with TFillData - makerToken: string; - takerToken: string; - makerAmount: BigNumber; // The amount we wish to buy from this order, e.g inclusive of any previous partial fill - takerAmount: BigNumber; // The amount we wish to fill this for, e.g inclusive of any previous partial fill - fill: Omit; // Remove duplicates which have been brought into the OrderBase interface -} - -export interface OptimizedMarketBridgeOrder - extends OptimizedMarketOrderBase { - type: FillQuoteTransformerOrderType.Bridge; - sourcePathId: string; -} - -export interface OptimizedLimitOrder extends OptimizedMarketOrderBase { - type: FillQuoteTransformerOrderType.Limit; -} - -export interface OptimizedRfqOrder extends OptimizedMarketOrderBase { - type: FillQuoteTransformerOrderType.Rfq; -} - -/** - * Optimized orders to fill. - */ -export type OptimizedMarketOrder = - | OptimizedMarketBridgeOrder - | OptimizedMarketOrderBase - | OptimizedMarketOrderBase; - -export interface GetMarketOrdersRfqOpts extends RfqRequestOpts { - rfqClient?: IRfqClient; - quoteRequestor?: QuoteRequestor; - firmQuoteValidator?: RfqFirmQuoteValidator; -} - -export type FeeEstimate = (fillData: FillData) => { gas: number; fee: BigNumber }; -export type FeeSchedule = Partial<{ [key in ERC20BridgeSource]: FeeEstimate }>; - -export type GasEstimate = (fillData: FillData) => number; -export type GasSchedule = Partial<{ [key in ERC20BridgeSource]: GasEstimate }>; - -export type ExchangeProxyOverhead = (sourceFlags: bigint) => BigNumber; - -/** - * Options for `getMarketSellOrdersAsync()` and `getMarketBuyOrdersAsync()`. - */ -export interface GetMarketOrdersOpts { - /** - * Liquidity sources to exclude. Default is none. - */ - excludedSources: ERC20BridgeSource[]; - /** - * Liquidity sources to exclude when used to calculate the cost of the route. - * Default is none. - */ - excludedFeeSources: ERC20BridgeSource[]; - /** - * Liquidity sources to include. Default is none, which allows all supported - * sources that aren't excluded by `excludedSources`. - */ - includedSources: ERC20BridgeSource[]; - /** - * Complexity limit on the search algorithm, i.e., maximum number of - * nodes to visit. Default is 1024. - */ - runLimit: number; - /** - * When generating bridge orders, we use - * sampled rate * (1 - bridgeSlippage) - * as the rate for calculating maker/taker asset amounts. - * This should be a small positive number (e.g., 0.0005) to make up for - * small discrepancies between samples and truth. - * Default is 0.0005 (5 basis points). - */ - bridgeSlippage: number; - /** - * The maximum price slippage allowed in the fallback quote. If the slippage - * between the optimal quote and the fallback quote is greater than this - * percentage, no fallback quote will be provided. - */ - maxFallbackSlippage: number; - /** - * Number of samples to take for each DEX quote. - */ - numSamples: number; - /** - * The exponential sampling distribution base. - * A value of 1 will result in evenly spaced samples. - * > 1 will result in more samples at lower sizes. - * < 1 will result in more samples at higher sizes. - * Default: 1.25. - */ - sampleDistributionBase: number; - /** - * Number of samples to use when creating fill curves with neon-router - */ - neonRouterNumSamples: number; - /** - * Fees for each liquidity source, expressed in gas. - */ - feeSchedule: FeeSchedule; - /** - * Estimated gas consumed by each liquidity source. - */ - gasSchedule: GasSchedule; - exchangeProxyOverhead: ExchangeProxyOverhead; - /** - * Whether to pad the quote with a redundant fallback quote using different - * sources. Defaults to `true`. - */ - allowFallback: boolean; - /** - * Options for RFQT such as takerAddress, intent on filling - */ - rfqt?: GetMarketOrdersRfqOpts; - /** - * Whether to generate a quote report - */ - shouldGenerateQuoteReport: boolean; - - /** - * Whether to include price comparison data in the quote - */ - shouldIncludePriceComparisonsReport: boolean; - /** - * Token addresses with a list of adjacent intermediary tokens to consider - * hopping to. E.g DAI->USDC via an adjacent token WETH - */ - tokenAdjacencyGraph: TokenAdjacencyGraph; - - /** - * Gas price to use for quote - */ - gasPrice: BigNumber; - - /** - * Sampler metrics for recording data on the sampler service and operations - */ - samplerMetrics?: SamplerMetrics; - - /** - * Adjusts fills individual fills based on caller supplied criteria - */ - fillAdjustor: FillAdjustor; -} - -export interface SamplerMetrics { - /** - * Logs the gas information performed during a sampler call. - * - * @param data.gasBefore The gas remaining measured before any operations have been performed - * @param data.gasAfter The gas remaining measured after all operations have been performed - */ - logGasDetails(data: { gasBefore: BigNumber; gasAfter: BigNumber }): void; - - /** - * Logs the block number - * - * @param blockNumber block number of the sampler call - */ - logBlockNumber(blockNumber: BigNumber): void; - - /** - * Logs the routing timings - * - * @param data.router The router type (neon-router or js) - * @param data.type The type of timing being recorded (e.g total timing, all sources timing or vip timing) - * @param data.timingMs The timing in milliseconds - */ - logRouterDetails(data: { router: 'neon-router' | 'js'; type: 'all' | 'vip' | 'total'; timingMs: number }): void; -} - -/** - * A composable operation the be run in `DexOrderSampler.executeAsync()`. - */ -export interface BatchedOperation { - encodeCall(): string; - handleCallResults(callResults: string): TResult; - handleRevert(callResults: string): TResult; -} - -export interface SourceQuoteOperation extends BatchedOperation { - readonly source: ERC20BridgeSource; - fillData: TFillData; -} - -export interface OptimizerResult { - optimizedOrders: OptimizedMarketOrder[]; - sourceFlags: bigint; - liquidityDelivered: Readonly>; - marketSideLiquidity: MarketSideLiquidity; - adjustedRate: BigNumber; - takerAmountPerEth: BigNumber; - makerAmountPerEth: BigNumber; -} - -export interface OptimizerResultWithReport extends OptimizerResult { - quoteReport?: QuoteReport; - extendedQuoteReportSources?: ExtendedQuoteReportSources; - priceComparisonsReport?: PriceComparisonsReport; -} - -export interface MarketSideLiquidity { - side: MarketOperation; - inputAmount: BigNumber; - inputToken: string; - outputToken: string; - outputAmountPerEth: BigNumber; - inputAmountPerEth: BigNumber; - quoteSourceFilters: SourceFilters; - makerTokenDecimals: number; - takerTokenDecimals: number; - quotes: RawQuotes; - isRfqSupported: boolean; - blockNumber: number; -} - -export interface RawQuotes { - nativeOrders: NativeOrderWithFillableAmounts[]; - rfqtIndicativeQuotes: V4RFQIndicativeQuoteMM[]; - twoHopQuotes: Array>; - dexQuotes: Array>>; -} - -export interface LiquidityProviderRegistry { - [address: string]: { - tokens: string[]; - gasCost: number | ((takerToken: string, makerToken: string) => number); - }; -} - -export interface GenerateOptimizedOrdersOpts { - runLimit?: number; - bridgeSlippage?: number; - maxFallbackSlippage?: number; - excludedSources?: ERC20BridgeSource[]; - feeSchedule: FeeSchedule; - exchangeProxyOverhead?: ExchangeProxyOverhead; - allowFallback?: boolean; - shouldBatchBridgeOrders?: boolean; - gasPrice: BigNumber; - neonRouterNumSamples: number; - samplerMetrics?: SamplerMetrics; - fillAdjustor: FillAdjustor; -} - -export interface ComparisonPrice { - wholeOrder: BigNumber | undefined; -} - -export interface FillAdjustor { - adjustFills: (side: MarketOperation, fills: Fill[], amount: BigNumber) => Fill[]; -} diff --git a/packages/asset-swapper/src/utils/protocol_fee_utils.ts b/packages/asset-swapper/src/utils/protocol_fee_utils.ts deleted file mode 100644 index 01e857bc7a..0000000000 --- a/packages/asset-swapper/src/utils/protocol_fee_utils.ts +++ /dev/null @@ -1,93 +0,0 @@ -import { BigNumber } from '@0x/utils'; -import * as heartbeats from 'heartbeats'; - -import { constants } from '../constants'; -import { SwapQuoterError } from '../types'; - -const MAX_ERROR_COUNT = 5; - -interface GasOracleResponse { - result: { - // gas price in wei - fast: number; - }; -} - -export class ProtocolFeeUtils { - private static _instance: ProtocolFeeUtils; - private readonly _zeroExGasApiUrl: string; - private readonly _gasPriceHeart: any; - private _gasPriceEstimation: BigNumber = constants.ZERO_AMOUNT; - private _errorCount: number = 0; - - public static getInstance( - gasPricePollingIntervalInMs: number, - zeroExGasApiUrl: string = constants.ZERO_EX_GAS_API_URL, - initialGasPrice: BigNumber = constants.ZERO_AMOUNT, - ): ProtocolFeeUtils { - if (!ProtocolFeeUtils._instance) { - ProtocolFeeUtils._instance = new ProtocolFeeUtils( - gasPricePollingIntervalInMs, - zeroExGasApiUrl, - initialGasPrice, - ); - } - return ProtocolFeeUtils._instance; - } - - /** @returns gas price (in wei) */ - public async getGasPriceEstimationOrThrowAsync(shouldHardRefresh?: boolean): Promise { - if (this._gasPriceEstimation.eq(constants.ZERO_AMOUNT)) { - return this._getGasPriceFromGasStationOrThrowAsync(); - } - if (shouldHardRefresh) { - return this._getGasPriceFromGasStationOrThrowAsync(); - } else { - return this._gasPriceEstimation; - } - } - - /** - * Destroys any subscriptions or connections. - */ - public async destroyAsync(): Promise { - this._gasPriceHeart.kill(); - } - - private constructor( - gasPricePollingIntervalInMs: number, - zeroExGasApiUrl: string = constants.ZERO_EX_GAS_API_URL, - initialGasPrice: BigNumber = constants.ZERO_AMOUNT, - ) { - this._gasPriceHeart = heartbeats.createHeart(gasPricePollingIntervalInMs); - this._gasPriceEstimation = initialGasPrice; - this._zeroExGasApiUrl = zeroExGasApiUrl; - this._initializeHeartBeat(); - } - - // tslint:disable-next-line: prefer-function-over-method - private async _getGasPriceFromGasStationOrThrowAsync(): Promise { - try { - const res = await fetch(this._zeroExGasApiUrl); - const gasInfo: GasOracleResponse = await res.json(); - const gasPriceWei = new BigNumber(gasInfo.result.fast); - // Reset the error count to 0 once we have a successful response - this._errorCount = 0; - return gasPriceWei; - } catch (e) { - this._errorCount++; - // If we've reached our max error count then throw - if (this._errorCount > MAX_ERROR_COUNT || this._gasPriceEstimation.isZero()) { - this._errorCount = 0; - throw new Error(SwapQuoterError.NoGasPriceProvidedOrEstimated); - } - return this._gasPriceEstimation; - } - } - - private _initializeHeartBeat(): void { - this._gasPriceHeart.createEvent(1, async () => { - this._gasPriceEstimation = await this._getGasPriceFromGasStationOrThrowAsync(); - }); - } -} diff --git a/packages/asset-swapper/src/utils/quote_report_generator.ts b/packages/asset-swapper/src/utils/quote_report_generator.ts deleted file mode 100644 index 1fe8f74d62..0000000000 --- a/packages/asset-swapper/src/utils/quote_report_generator.ts +++ /dev/null @@ -1,434 +0,0 @@ -import { FillQuoteTransformerOrderType, RfqOrderFields, Signature } from '@0x/protocol-utils'; -import { BigNumber } from '@0x/utils'; -import _ = require('lodash'); - -import { MarketOperation, NativeOrderWithFillableAmounts } from '../types'; - -import { - DexSample, - ERC20BridgeSource, - Fill, - FillData, - MultiHopFillData, - NativeFillData, - NativeLimitOrderFillData, - NativeRfqOrderFillData, - RawQuotes, -} from './market_operation_utils/types'; -import { QuoteRequestor, V4RFQIndicativeQuoteMM } from './quote_requestor'; - -export interface QuoteReportEntryBase { - liquiditySource: ERC20BridgeSource; - makerAmount: BigNumber; - takerAmount: BigNumber; - fillData: FillData; -} -export interface BridgeQuoteReportEntry extends QuoteReportEntryBase { - liquiditySource: Exclude; -} - -export interface MultiHopQuoteReportEntry extends QuoteReportEntryBase { - liquiditySource: ERC20BridgeSource.MultiHop; - hopSources: ERC20BridgeSource[]; -} - -export interface NativeLimitOrderQuoteReportEntry extends QuoteReportEntryBase { - liquiditySource: ERC20BridgeSource.Native; - fillData: NativeFillData; - fillableTakerAmount: BigNumber; - isRFQ: false; -} - -export interface NativeRfqOrderQuoteReportEntry extends QuoteReportEntryBase { - liquiditySource: ERC20BridgeSource.Native; - fillData: NativeFillData; - fillableTakerAmount: BigNumber; - isRFQ: true; - nativeOrder: RfqOrderFields; - makerUri: string; - comparisonPrice?: number; -} - -export interface IndicativeRfqOrderQuoteReportEntry extends QuoteReportEntryBase { - liquiditySource: ERC20BridgeSource.Native; - fillableTakerAmount: BigNumber; - isRFQ: true; - makerUri?: string; - comparisonPrice?: number; -} - -export type QuoteReportEntry = - | BridgeQuoteReportEntry - | MultiHopQuoteReportEntry - | NativeLimitOrderQuoteReportEntry - | NativeRfqOrderQuoteReportEntry; - -export type ExtendedQuoteReportEntry = - | BridgeQuoteReportEntry - | MultiHopQuoteReportEntry - | NativeLimitOrderQuoteReportEntry - | NativeRfqOrderQuoteReportEntry - | IndicativeRfqOrderQuoteReportEntry; - -export type ExtendedQuoteReportIndexedEntry = ExtendedQuoteReportEntry & { - quoteEntryIndex: number; - isDelivered: boolean; -}; - -export type ExtendedQuoteReportIndexedEntryOutbound = Omit & { - fillData?: string; -}; - -export interface QuoteReport { - sourcesConsidered: QuoteReportEntry[]; - sourcesDelivered: QuoteReportEntry[]; -} - -export interface ExtendedQuoteReportSources { - sourcesConsidered: ExtendedQuoteReportIndexedEntry[]; - sourcesDelivered: ExtendedQuoteReportIndexedEntry[] | undefined; -} - -export interface ExtendedQuoteReport { - quoteId?: string; - taker?: string; - timestamp: number; - firmQuoteReport: boolean; - submissionBy: 'taker' | 'metaTxn' | 'rfqm'; - buyAmount?: string; - sellAmount?: string; - buyTokenAddress: string; - sellTokenAddress: string; - integratorId?: string; - slippageBips?: number; - zeroExTransactionHash?: string; - decodedUniqueId?: string; - sourcesConsidered: ExtendedQuoteReportIndexedEntryOutbound[]; - sourcesDelivered: ExtendedQuoteReportIndexedEntryOutbound[] | undefined; - blockNumber: number | undefined; - estimatedGas: string; -} - -export interface PriceComparisonsReport { - dexSources: BridgeQuoteReportEntry[]; - multiHopSources: MultiHopQuoteReportEntry[]; - nativeSources: Array; -} - -/** - * Generates a report of sources considered while computing the optimized - * swap quote, and the sources ultimately included in the computed quote. - */ -export function generateQuoteReport( - marketOperation: MarketOperation, - nativeOrders: NativeOrderWithFillableAmounts[], - liquidityDelivered: ReadonlyArray | DexSample, - comparisonPrice?: BigNumber | undefined, - quoteRequestor?: QuoteRequestor, -): QuoteReport { - const nativeOrderSourcesConsidered = nativeOrders.map(order => - nativeOrderToReportEntry(order.type, order as any, order.fillableTakerAmount, comparisonPrice, quoteRequestor), - ); - const sourcesConsidered = [...nativeOrderSourcesConsidered.filter(order => order.isRFQ)]; - - let sourcesDelivered; - if (Array.isArray(liquidityDelivered)) { - // create easy way to look up fillable amounts - const nativeOrderSignaturesToFillableAmounts = _.fromPairs( - nativeOrders.map(o => { - return [_nativeDataToId(o), o.fillableTakerAmount]; - }), - ); - // map sources delivered - sourcesDelivered = liquidityDelivered.map(collapsedFill => { - if (_isNativeOrderFromCollapsedFill(collapsedFill)) { - return nativeOrderToReportEntry( - collapsedFill.type, - collapsedFill.fillData, - nativeOrderSignaturesToFillableAmounts[_nativeDataToId(collapsedFill.fillData)], - comparisonPrice, - quoteRequestor, - ); - } else { - return dexSampleToReportSource(collapsedFill, marketOperation); - } - }); - } else { - sourcesDelivered = [ - // tslint:disable-next-line: no-unnecessary-type-assertion - multiHopSampleToReportSource(liquidityDelivered as DexSample, marketOperation), - ]; - } - return { - sourcesConsidered, - sourcesDelivered, - }; -} - -/** - * Generates a report of sources considered while computing the optimized - * swap quote, the sources ultimately included in the computed quote. This - * extende version incudes all considered quotes, not only native liquidity. - */ -export function generateExtendedQuoteReportSources( - marketOperation: MarketOperation, - quotes: RawQuotes, - liquidityDelivered: ReadonlyArray | DexSample, - amount: BigNumber, - comparisonPrice?: BigNumber | undefined, - quoteRequestor?: QuoteRequestor, -): ExtendedQuoteReportSources { - const sourcesConsidered: ExtendedQuoteReportEntry[] = []; - - // NativeOrders - sourcesConsidered.push( - ...quotes.nativeOrders.map(order => - nativeOrderToReportEntry( - order.type, - order as any, - order.fillableTakerAmount, - comparisonPrice, - quoteRequestor, - ), - ), - ); - - // IndicativeQuotes - sourcesConsidered.push( - ...quotes.rfqtIndicativeQuotes.map(order => indicativeQuoteToReportEntry(order, comparisonPrice)), - ); - - // MultiHop - sourcesConsidered.push(...quotes.twoHopQuotes.map(quote => multiHopSampleToReportSource(quote, marketOperation))); - - // Dex Quotes - sourcesConsidered.push( - ..._.flatten( - quotes.dexQuotes.map(dex => - dex - .filter(quote => isDexSampleFilter(quote, amount)) - .map(quote => dexSampleToReportSource(quote, marketOperation)), - ), - ), - ); - const sourcesConsideredIndexed = sourcesConsidered.map( - (quote, index): ExtendedQuoteReportIndexedEntry => { - return { - ...quote, - quoteEntryIndex: index, - isDelivered: false, - }; - }, - ); - let sourcesDelivered; - if (Array.isArray(liquidityDelivered)) { - // create easy way to look up fillable amounts - const nativeOrderSignaturesToFillableAmounts = _.fromPairs( - quotes.nativeOrders.map(o => { - return [_nativeDataToId(o), o.fillableTakerAmount]; - }), - ); - // map sources delivered - sourcesDelivered = liquidityDelivered.map(collapsedFill => { - if (_isNativeOrderFromCollapsedFill(collapsedFill)) { - return nativeOrderToReportEntry( - collapsedFill.type, - collapsedFill.fillData, - nativeOrderSignaturesToFillableAmounts[_nativeDataToId(collapsedFill.fillData)], - comparisonPrice, - quoteRequestor, - ); - } else { - return dexSampleToReportSource(collapsedFill, marketOperation); - } - }); - } else { - sourcesDelivered = [ - // tslint:disable-next-line: no-unnecessary-type-assertion - multiHopSampleToReportSource(liquidityDelivered as DexSample, marketOperation), - ]; - } - const sourcesDeliveredIndexed = sourcesDelivered.map( - (quote, index): ExtendedQuoteReportIndexedEntry => { - return { - ...quote, - quoteEntryIndex: index, - isDelivered: false, - }; - }, - ); - - return { - sourcesConsidered: sourcesConsideredIndexed, - sourcesDelivered: sourcesDeliveredIndexed, - }; -} - -function _nativeDataToId(data: { signature: Signature }): string { - const { v, r, s } = data.signature; - return `${v}${r}${s}`; -} - -/** - * Generates a report sample for a DEX source - * NOTE: this is used for the QuoteReport and quote price comparison data - */ -export function dexSampleToReportSource(ds: DexSample, marketOperation: MarketOperation): BridgeQuoteReportEntry { - const liquiditySource = ds.source; - - if (liquiditySource === ERC20BridgeSource.Native) { - throw new Error(`Unexpected liquidity source Native`); - } - - // input and output map to different values - // based on the market operation - if (marketOperation === MarketOperation.Buy) { - return { - makerAmount: ds.input, - takerAmount: ds.output, - liquiditySource, - fillData: ds.fillData, - }; - } else if (marketOperation === MarketOperation.Sell) { - return { - makerAmount: ds.output, - takerAmount: ds.input, - liquiditySource, - fillData: ds.fillData, - }; - } else { - throw new Error(`Unexpected marketOperation ${marketOperation}`); - } -} - -/** - * Checks if a DEX sample is the one that represents the whole amount requested by taker - * NOTE: this is used for the QuoteReport to filter samples - */ -function isDexSampleFilter(ds: DexSample, amount: BigNumber): boolean { - // The entry is for the total amont, not a sampler entry && there was liquidity in the source - return ds.input.eq(amount) && ds.output.isGreaterThan(0); -} - -/** - * Generates a report sample for a MultiHop source - * NOTE: this is used for the QuoteReport and quote price comparison data - */ -export function multiHopSampleToReportSource( - ds: DexSample, - marketOperation: MarketOperation, -): MultiHopQuoteReportEntry { - const { firstHopSource: firstHop, secondHopSource: secondHop } = ds.fillData; - // input and output map to different values - // based on the market operation - if (marketOperation === MarketOperation.Buy) { - return { - liquiditySource: ERC20BridgeSource.MultiHop, - makerAmount: ds.input, - takerAmount: ds.output, - fillData: ds.fillData, - hopSources: [firstHop.source, secondHop.source], - }; - } else if (marketOperation === MarketOperation.Sell) { - return { - liquiditySource: ERC20BridgeSource.MultiHop, - makerAmount: ds.output, - takerAmount: ds.input, - fillData: ds.fillData, - hopSources: [firstHop.source, secondHop.source], - }; - } else { - throw new Error(`Unexpected marketOperation ${marketOperation}`); - } -} - -function _isNativeOrderFromCollapsedFill(cf: Fill): cf is Fill { - const { type } = cf; - return type === FillQuoteTransformerOrderType.Limit || type === FillQuoteTransformerOrderType.Rfq; -} - -/** - * Generates a report entry for a native order - * NOTE: this is used for the QuoteReport and quote price comparison data - */ -export function nativeOrderToReportEntry( - type: FillQuoteTransformerOrderType, - fillData: NativeLimitOrderFillData | NativeRfqOrderFillData, - fillableAmount: BigNumber, - comparisonPrice?: BigNumber | undefined, - quoteRequestor?: QuoteRequestor, -): NativeRfqOrderQuoteReportEntry | NativeLimitOrderQuoteReportEntry { - const nativeOrderBase = { - makerAmount: fillData.order.makerAmount, - takerAmount: fillData.order.takerAmount, - fillableTakerAmount: fillableAmount, - }; - - // if we find this is an rfqt order, label it as such and associate makerUri - const isRFQ = type === FillQuoteTransformerOrderType.Rfq; - const rfqtMakerUri = - isRFQ && quoteRequestor ? quoteRequestor.getMakerUriForSignature(fillData.signature) : undefined; - - if (isRFQ) { - const nativeOrder = fillData.order as RfqOrderFields; - // tslint:disable-next-line: no-object-literal-type-assertion - return { - liquiditySource: ERC20BridgeSource.Native, - ...nativeOrderBase, - isRFQ: true, - makerUri: rfqtMakerUri || '', - ...(comparisonPrice ? { comparisonPrice: comparisonPrice.toNumber() } : {}), - nativeOrder, - fillData, - }; - } else { - // tslint:disable-next-line: no-object-literal-type-assertion - return { - liquiditySource: ERC20BridgeSource.Native, - ...nativeOrderBase, - isRFQ: false, - fillData, - }; - } -} - -/** - * Generates a report entry for an indicative RFQ Quote - * NOTE: this is used for the QuoteReport and quote price comparison data - */ -export function indicativeQuoteToReportEntry( - order: V4RFQIndicativeQuoteMM, - comparisonPrice?: BigNumber | undefined, -): IndicativeRfqOrderQuoteReportEntry { - const nativeOrderBase = { - makerAmount: order.makerAmount, - takerAmount: order.takerAmount, - fillableTakerAmount: order.takerAmount, - }; - - // tslint:disable-next-line: no-object-literal-type-assertion - return { - liquiditySource: ERC20BridgeSource.Native, - ...nativeOrderBase, - isRFQ: true, - makerUri: order.makerUri, - fillData: {}, - ...(comparisonPrice ? { comparisonPrice: comparisonPrice.toNumber() } : {}), - }; -} - -/** - * For the extended quote report, we output the filldata as JSON - */ -export function jsonifyFillData(source: ExtendedQuoteReportIndexedEntry): ExtendedQuoteReportIndexedEntryOutbound { - return { - ...source, - fillData: JSON.stringify(source.fillData, (key: string, value: any) => { - if (key === '_samplerContract') { - return {}; - } else { - return value; - } - }), - }; -} diff --git a/packages/asset-swapper/src/utils/quote_requestor.ts b/packages/asset-swapper/src/utils/quote_requestor.ts deleted file mode 100644 index 332b1d9afb..0000000000 --- a/packages/asset-swapper/src/utils/quote_requestor.ts +++ /dev/null @@ -1,800 +0,0 @@ -import { schemas, SchemaValidator } from '@0x/json-schemas'; -import { FillQuoteTransformerOrderType, Signature } from '@0x/protocol-utils'; -import { - TakerRequestQueryParamsUnnested, - V4RFQFirmQuote, - V4RFQIndicativeQuote, - V4SignedRfqOrder, -} from '@0x/quote-server'; -import { Fee } from '@0x/quote-server/lib/src/types'; -import { BigNumber, NULL_ADDRESS } from '@0x/utils'; -import axios, { AxiosInstance } from 'axios'; - -import { constants } from '../constants'; -import { - AltQuoteModel, - AltRfqMakerAssetOfferings, - Integrator, - LogFunction, - MarketOperation, - RfqMakerAssetOfferings, - RfqmRequestOptions, - RfqPairType, - RfqRequestOpts, - SignedNativeOrder, - TypedMakerUrl, -} from '../types'; - -import { returnQuoteFromAltMMAsync } from './alt_mm_implementation_utils'; -import { ONE_SECOND_MS } from './market_operation_utils/constants'; -import { RfqMakerBlacklist } from './rfq_maker_blacklist'; - -const MAKER_TIMEOUT_STREAK_LENGTH = 10; -const MAKER_TIMEOUT_BLACKLIST_DURATION_MINUTES = 10; -const FILL_RATIO_WARNING_LEVEL = 0.99; -const rfqMakerBlacklist = new RfqMakerBlacklist(MAKER_TIMEOUT_STREAK_LENGTH, MAKER_TIMEOUT_BLACKLIST_DURATION_MINUTES); - -interface RfqQuote { - response: T; - makerUri: string; -} - -export interface V4RFQIndicativeQuoteMM extends V4RFQIndicativeQuote { - makerUri: string; -} - -export interface MetricsProxy { - /** - * Increments a counter that is tracking valid Firm Quotes that are dropped due to low expiration. - * @param isLastLook mark if call is coming from RFQM - * @param maker the maker address - */ - incrementExpirationToSoonCounter(isLastLook: boolean, maker: string): void; - - /** - * Keeps track of summary statistics for expiration on Firm Quotes. - * @param isLastLook mark if call is coming from RFQM - * @param maker the maker address - * @param expirationTimeSeconds the expiration time in seconds - */ - measureExpirationForValidOrder(isLastLook: boolean, maker: string, expirationTimeSeconds: BigNumber): void; - - /** - * Increments a counter that tracks when an order is not fully fillable. - * @param isLastLook mark if call is coming from RFQM - * @param maker the maker address - * @param expirationTimeSeconds the expiration time in seconds - */ - incrementFillRatioWarningCounter(isLastLook: boolean, maker: string): void; - - /** - * Logs the outcome of a network (HTTP) interaction with a market maker. - * - * @param interaction.isLastLook true if the request is RFQM - * @param interaction.integrator the integrator that is requesting the RFQ quote - * @param interaction.url the URL of the market maker - * @param interaction.quoteType indicative or firm quote - * @param interaction.statusCode the statusCode returned by a market maker - * @param interaction.latencyMs the latency of the HTTP request (in ms) - * @param interaction.included if a firm quote that was returned got included in the next step of processing. - * NOTE: this does not mean that the request returned a valid fillable order. It just - * means that the network response was successful. - */ - logRfqMakerNetworkInteraction(interaction: { - isLastLook: boolean; - integrator: Integrator; - url: string; - quoteType: 'firm' | 'indicative'; - statusCode: number | undefined; - latencyMs: number; - included: boolean; - sellTokenAddress: string; - buyTokenAddress: string; - }): void; -} - -/** - * Request quotes from RFQ-T providers - */ - -function hasExpectedAddresses(comparisons: Array<[string, string]>): boolean { - return comparisons.every(c => c[0].toLowerCase() === c[1].toLowerCase()); -} - -function convertIfAxiosError(error: any): Error | object /* axios' .d.ts has AxiosError.toJSON() returning object */ { - if (error.hasOwnProperty('isAxiosError') && error.isAxiosError) { - const { message, name, config } = error; - const { headers, timeout, httpsAgent } = config; - const { keepAlive, keepAliveMsecs, sockets } = httpsAgent; - - const socketCounts: { [key: string]: number } = {}; - for (const socket of Object.keys(sockets)) { - socketCounts[socket] = sockets[socket].length; - } - - return { - message, - name, - config: { - headers, - timeout, - httpsAgent: { - keepAlive, - keepAliveMsecs, - socketCounts, - }, - }, - }; - } else { - return error; - } -} - -function nativeDataToId(data: { signature: Signature }): string { - const { v, r, s } = data.signature; - return `${v}${r}${s}`; -} - -export class QuoteRequestor { - private readonly _schemaValidator: SchemaValidator = new SchemaValidator(); - private readonly _orderSignatureToMakerUri: { [signature: string]: string } = {}; - - public static makeQueryParameters( - txOrigin: string, - takerAddress: string, - marketOperation: MarketOperation, - buyTokenAddress: string, // maker token - sellTokenAddress: string, // taker token - assetFillAmount: BigNumber, - comparisonPrice?: BigNumber, - isLastLook?: boolean | undefined, - fee?: Fee | undefined, - ): TakerRequestQueryParamsUnnested { - const { buyAmountBaseUnits, sellAmountBaseUnits } = - marketOperation === MarketOperation.Buy - ? { - buyAmountBaseUnits: assetFillAmount, - sellAmountBaseUnits: undefined, - } - : { - sellAmountBaseUnits: assetFillAmount, - buyAmountBaseUnits: undefined, - }; - - const requestParamsWithBigNumbers: Pick< - TakerRequestQueryParamsUnnested, - | 'txOrigin' - | 'takerAddress' - | 'buyTokenAddress' - | 'sellTokenAddress' - | 'comparisonPrice' - | 'isLastLook' - | 'protocolVersion' - | 'feeAmount' - | 'feeToken' - | 'feeType' - > = { - txOrigin, - takerAddress, - buyTokenAddress, - sellTokenAddress, - comparisonPrice: comparisonPrice === undefined ? undefined : comparisonPrice.toString(), - protocolVersion: '4', - }; - if (isLastLook) { - if (fee === undefined) { - throw new Error(`isLastLook cannot be passed without a fee parameter`); - } - requestParamsWithBigNumbers.isLastLook = isLastLook.toString(); - requestParamsWithBigNumbers.feeAmount = fee.amount.toString(); - requestParamsWithBigNumbers.feeToken = fee.token; - requestParamsWithBigNumbers.feeType = fee.type; - } - - // convert BigNumbers to strings - // so they are digestible by axios - if (sellAmountBaseUnits) { - return { - ...requestParamsWithBigNumbers, - sellAmountBaseUnits: sellAmountBaseUnits.toString(), - }; - } else if (buyAmountBaseUnits) { - return { - ...requestParamsWithBigNumbers, - buyAmountBaseUnits: buyAmountBaseUnits.toString(), - }; - } else { - throw new Error('Neither "buyAmountBaseUnits" or "sellAmountBaseUnits" were defined'); - } - } - - /** - * Gets both standard RFQ makers and "alternative" RFQ makers and combines them together - * in a single configuration map. If an integration key whitelist is present, it will be used - * to filter a specific makers. - * - * @param options the RfqmRequestOptions passed in - * @param assetOfferings the RFQM or RFQT maker offerings - * @returns a list of TypedMakerUrl instances - */ - public static getTypedMakerUrlsAndWhitelist( - options: Pick, - assetOfferings: RfqMakerAssetOfferings, - ): TypedMakerUrl[] { - const standardUrls = Object.keys(assetOfferings).map( - (mm: string): TypedMakerUrl => { - return { pairType: RfqPairType.Standard, url: mm }; - }, - ); - const altUrls = options.altRfqAssetOfferings - ? Object.keys(options.altRfqAssetOfferings).map( - (mm: string): TypedMakerUrl => { - return { pairType: RfqPairType.Alt, url: mm }; - }, - ) - : []; - - let typedMakerUrls = standardUrls.concat(altUrls); - - // If there is a whitelist, only allow approved maker URLs - if (options.integrator.whitelistIntegratorUrls !== undefined) { - const whitelist = new Set(options.integrator.whitelistIntegratorUrls.map(key => key.toLowerCase())); - typedMakerUrls = typedMakerUrls.filter(makerUrl => whitelist.has(makerUrl.url.toLowerCase())); - } - return typedMakerUrls; - } - - public static getDurationUntilExpirationMs(expirationTimeSeconds: BigNumber): BigNumber { - const expirationTimeMs = expirationTimeSeconds.times(constants.ONE_SECOND_MS); - const currentTimeMs = new BigNumber(Date.now()); - return BigNumber.max(expirationTimeMs.minus(currentTimeMs), 0); - } - - private static _makerSupportsPair( - typedMakerUrl: TypedMakerUrl, - makerToken: string, - takerToken: string, - altMakerAssetOfferings: AltRfqMakerAssetOfferings | undefined, - assetOfferings: RfqMakerAssetOfferings | undefined, - ): boolean { - if (typedMakerUrl.pairType === RfqPairType.Standard && assetOfferings) { - for (const assetPair of assetOfferings[typedMakerUrl.url]) { - if ( - (assetPair[0] === makerToken && assetPair[1] === takerToken) || - (assetPair[0] === takerToken && assetPair[1] === makerToken) - ) { - return true; - } - } - } else if (typedMakerUrl.pairType === RfqPairType.Alt && altMakerAssetOfferings) { - for (const altAssetPair of altMakerAssetOfferings[typedMakerUrl.url]) { - if ( - (altAssetPair.baseAsset === makerToken && altAssetPair.quoteAsset === takerToken) || - (altAssetPair.baseAsset === takerToken && altAssetPair.quoteAsset === makerToken) - ) { - return true; - } - } - } - return false; - } - - constructor( - private readonly _rfqtAssetOfferings: RfqMakerAssetOfferings, - private readonly _rfqmAssetOfferings: RfqMakerAssetOfferings, - private readonly _quoteRequestorHttpClient: AxiosInstance, - private readonly _altRfqCreds?: { altRfqApiKey: string; altRfqProfile: string }, - private readonly _warningLogger: LogFunction = constants.DEFAULT_WARNING_LOGGER, - private readonly _infoLogger: LogFunction = constants.DEFAULT_INFO_LOGGER, - private readonly _expiryBufferMs: number = constants.DEFAULT_SWAP_QUOTER_OPTS.expiryBufferMs, - private readonly _metrics?: MetricsProxy, - ) { - rfqMakerBlacklist.infoLogger = this._infoLogger; - } - - public async requestRfqmFirmQuotesAsync( - makerToken: string, // maker token - takerToken: string, // taker token - assetFillAmount: BigNumber, - marketOperation: MarketOperation, - comparisonPrice: BigNumber | undefined, - options: RfqmRequestOptions, - ): Promise { - const _opts: RfqRequestOpts = { - ...constants.DEFAULT_RFQT_REQUEST_OPTS, - ...options, - }; - - return this._fetchAndValidateFirmQuotesAsync( - makerToken, - takerToken, - assetFillAmount, - marketOperation, - comparisonPrice, - _opts, - this._rfqmAssetOfferings, - ); - } - - public async requestRfqtFirmQuotesAsync( - makerToken: string, // maker token - takerToken: string, // taker token - assetFillAmount: BigNumber, - marketOperation: MarketOperation, - comparisonPrice: BigNumber | undefined, - options: RfqRequestOpts, - ): Promise { - const _opts: RfqRequestOpts = { ...constants.DEFAULT_RFQT_REQUEST_OPTS, ...options }; - if (!_opts.txOrigin || [undefined, '', '0x', NULL_ADDRESS].includes(_opts.txOrigin)) { - throw new Error('RFQ-T firm quotes require the presence of a tx origin'); - } - - return this._fetchAndValidateFirmQuotesAsync( - makerToken, - takerToken, - assetFillAmount, - marketOperation, - comparisonPrice, - _opts, - this._rfqtAssetOfferings, - ); - } - - public async requestRfqmIndicativeQuotesAsync( - makerToken: string, - takerToken: string, - assetFillAmount: BigNumber, - marketOperation: MarketOperation, - comparisonPrice: BigNumber | undefined, - options: RfqmRequestOptions, - ): Promise { - const _opts: RfqRequestOpts = { - ...constants.DEFAULT_RFQT_REQUEST_OPTS, - ...options, - }; - - return this._fetchAndValidateIndicativeQuotesAsync( - makerToken, - takerToken, - assetFillAmount, - marketOperation, - comparisonPrice, - _opts, - this._rfqmAssetOfferings, - ); - } - - public async requestRfqtIndicativeQuotesAsync( - makerToken: string, - takerToken: string, - assetFillAmount: BigNumber, - marketOperation: MarketOperation, - comparisonPrice: BigNumber | undefined, - options: RfqRequestOpts, - ): Promise { - const _opts: RfqRequestOpts = { ...constants.DEFAULT_RFQT_REQUEST_OPTS, ...options }; - // Originally a takerAddress was required for indicative quotes, but - // now we've eliminated that requirement. @0x/quote-server, however, - // is still coded to expect a takerAddress. So if the client didn't - // send one, just use the null address to satisfy the quote server's - // expectations. - if (!_opts.takerAddress) { - _opts.takerAddress = constants.NULL_ADDRESS; - } - if (!_opts.txOrigin) { - _opts.txOrigin = constants.NULL_ADDRESS; - } - return this._fetchAndValidateIndicativeQuotesAsync( - makerToken, - takerToken, - assetFillAmount, - marketOperation, - comparisonPrice, - _opts, - this._rfqtAssetOfferings, - ); - } - - /** - * Given an order signature, returns the makerUri that the order originated from - */ - public getMakerUriForSignature(signature: Signature): string | undefined { - return this._orderSignatureToMakerUri[nativeDataToId({ signature })]; - } - - private _isValidRfqtIndicativeQuoteResponse(response: V4RFQIndicativeQuoteMM): boolean { - const requiredKeys: Array = [ - 'makerAmount', - 'takerAmount', - 'makerToken', - 'takerToken', - 'expiry', - ]; - - for (const k of requiredKeys) { - if (response[k] === undefined) { - return false; - } - } - // TODO (jacob): I have a feeling checking 5 schemas is slower then checking one - const hasValidMakerAssetAmount = this._schemaValidator.isValid(response.makerAmount, schemas.wholeNumberSchema); - const hasValidTakerAssetAmount = this._schemaValidator.isValid(response.takerAmount, schemas.wholeNumberSchema); - const hasValidMakerToken = this._schemaValidator.isValid(response.makerToken, schemas.hexSchema); - const hasValidTakerToken = this._schemaValidator.isValid(response.takerToken, schemas.hexSchema); - const hasValidExpirationTimeSeconds = this._schemaValidator.isValid(response.expiry, schemas.wholeNumberSchema); - if ( - !hasValidMakerAssetAmount || - !hasValidTakerAssetAmount || - !hasValidMakerToken || - !hasValidTakerToken || - !hasValidExpirationTimeSeconds - ) { - return false; - } - return true; - } - - private async _getQuotesAsync( - makerToken: string, - takerToken: string, - assetFillAmount: BigNumber, - marketOperation: MarketOperation, - comparisonPrice: BigNumber | undefined, - options: RfqRequestOpts, - quoteType: 'firm' | 'indicative', - assetOfferings: RfqMakerAssetOfferings, - ): Promise>> { - const requestParams = QuoteRequestor.makeQueryParameters( - options.txOrigin, - options.takerAddress, - marketOperation, - makerToken, - takerToken, - assetFillAmount, - comparisonPrice, - options.isLastLook, - options.fee, - ); - - const quotePath = (() => { - switch (quoteType) { - case 'firm': - return 'quote'; - case 'indicative': - return 'price'; - default: - throw new Error(`Unexpected quote type ${quoteType}`); - } - })(); - - const timeoutMs = - options.makerEndpointMaxResponseTimeMs || - constants.DEFAULT_RFQT_REQUEST_OPTS.makerEndpointMaxResponseTimeMs!; - const bufferMs = 20; - - // Set Timeout on CancelToken - const cancelTokenSource = axios.CancelToken.source(); - setTimeout(() => { - cancelTokenSource.cancel('timeout via cancel token'); - }, timeoutMs + bufferMs); - - const typedMakerUrls = QuoteRequestor.getTypedMakerUrlsAndWhitelist(options, assetOfferings); - const quotePromises = typedMakerUrls.map(async typedMakerUrl => { - // filter out requests to skip - const isBlacklisted = rfqMakerBlacklist.isMakerBlacklisted(typedMakerUrl.url); - const partialLogEntry = { url: typedMakerUrl.url, quoteType, requestParams, isBlacklisted }; - const { isLastLook, integrator } = options; - const { sellTokenAddress, buyTokenAddress } = requestParams; - if (isBlacklisted) { - this._metrics?.logRfqMakerNetworkInteraction({ - isLastLook: false, - url: typedMakerUrl.url, - quoteType, - statusCode: undefined, - sellTokenAddress, - buyTokenAddress, - latencyMs: 0, - included: false, - integrator, - }); - this._infoLogger({ rfqtMakerInteraction: { ...partialLogEntry } }); - return; - } else if ( - !QuoteRequestor._makerSupportsPair( - typedMakerUrl, - makerToken, - takerToken, - options.altRfqAssetOfferings, - assetOfferings, - ) - ) { - return; - } else { - // make request to MM - const timeBeforeAwait = Date.now(); - try { - if (typedMakerUrl.pairType === RfqPairType.Standard) { - const response = await this._quoteRequestorHttpClient.get(`${typedMakerUrl.url}/${quotePath}`, { - headers: { - '0x-api-key': options.integrator.integratorId, - '0x-integrator-id': options.integrator.integratorId, - }, - params: requestParams, - timeout: timeoutMs, - cancelToken: cancelTokenSource.token, - }); - const latencyMs = Date.now() - timeBeforeAwait; - this._metrics?.logRfqMakerNetworkInteraction({ - isLastLook: isLastLook || false, - url: typedMakerUrl.url, - quoteType, - statusCode: response.status, - sellTokenAddress, - buyTokenAddress, - latencyMs, - included: true, - integrator, - }); - this._infoLogger({ - rfqtMakerInteraction: { - ...partialLogEntry, - response: { - included: true, - apiKey: options.integrator.integratorId, - takerAddress: requestParams.takerAddress, - txOrigin: requestParams.txOrigin, - statusCode: response.status, - latencyMs, - }, - }, - }); - rfqMakerBlacklist.logTimeoutOrLackThereof(typedMakerUrl.url, latencyMs >= timeoutMs); - return { - response: { ...response.data, makerUri: typedMakerUrl.url }, - makerUri: typedMakerUrl.url, - }; - } else { - if (this._altRfqCreds === undefined) { - throw new Error(`don't have credentials for alt MM`); - } - const quote = await returnQuoteFromAltMMAsync( - typedMakerUrl.url, - this._altRfqCreds.altRfqApiKey, - this._altRfqCreds.altRfqProfile, - options.integrator.integratorId, - quoteType === 'firm' ? AltQuoteModel.Firm : AltQuoteModel.Indicative, - makerToken, - takerToken, - timeoutMs, - options.altRfqAssetOfferings || {}, - requestParams, - this._quoteRequestorHttpClient, - this._warningLogger, - cancelTokenSource.token, - ); - - const latencyMs = Date.now() - timeBeforeAwait; - this._metrics?.logRfqMakerNetworkInteraction({ - isLastLook: isLastLook || false, - url: typedMakerUrl.url, - quoteType, - statusCode: quote.status, - sellTokenAddress, - buyTokenAddress, - latencyMs, - included: true, - integrator, - }); - this._infoLogger({ - rfqtMakerInteraction: { - ...partialLogEntry, - response: { - included: true, - apiKey: options.integrator.integratorId, - takerAddress: requestParams.takerAddress, - txOrigin: requestParams.txOrigin, - statusCode: quote.status, - latencyMs, - }, - }, - }); - rfqMakerBlacklist.logTimeoutOrLackThereof(typedMakerUrl.url, latencyMs >= timeoutMs); - return { response: quote.data, makerUri: typedMakerUrl.url }; - } - } catch (err) { - // log error if any - const latencyMs = Date.now() - timeBeforeAwait; - this._metrics?.logRfqMakerNetworkInteraction({ - isLastLook: isLastLook || false, - url: typedMakerUrl.url, - quoteType, - statusCode: err.response?.status, - sellTokenAddress, - buyTokenAddress, - latencyMs, - included: false, - integrator, - }); - this._infoLogger({ - rfqtMakerInteraction: { - ...partialLogEntry, - response: { - included: false, - apiKey: options.integrator.integratorId, - takerAddress: requestParams.takerAddress, - txOrigin: requestParams.txOrigin, - statusCode: err.response ? err.response.status : undefined, - latencyMs, - }, - }, - }); - rfqMakerBlacklist.logTimeoutOrLackThereof(typedMakerUrl.url, latencyMs >= timeoutMs); - this._warningLogger( - convertIfAxiosError(err), - `Failed to get RFQ-T ${quoteType} quote from market maker endpoint ${typedMakerUrl.url} for integrator ${options.integrator.integratorId} (${options.integrator.label}) for taker address ${options.takerAddress} and tx origin ${options.txOrigin}`, - ); - return; - } - } - }); - - const results = (await Promise.all(quotePromises)).filter(x => x !== undefined); - return results as Array>; - } - private async _fetchAndValidateFirmQuotesAsync( - makerToken: string, - takerToken: string, - assetFillAmount: BigNumber, - marketOperation: MarketOperation, - comparisonPrice: BigNumber | undefined, - options: RfqRequestOpts, - assetOfferings: RfqMakerAssetOfferings, - ): Promise { - const quotesRaw = await this._getQuotesAsync( - makerToken, - takerToken, - assetFillAmount, - marketOperation, - comparisonPrice, - options, - 'firm', - assetOfferings, - ); - const quotes = quotesRaw.map(result => ({ ...result, response: result.response.signedOrder })); - - // validate - const validationFunction = (o: V4SignedRfqOrder) => { - try { - // Handle the validate throwing, i.e if it isn't an object or json response - return this._schemaValidator.isValid(o, schemas.v4RfqSignedOrderSchema); - } catch (e) { - return false; - } - }; - const validQuotes = quotes.filter(result => { - const order = result.response; - if (!validationFunction(order)) { - this._warningLogger(result, 'Invalid RFQ-T firm quote received, filtering out'); - return false; - } - if ( - !hasExpectedAddresses([ - [makerToken, order.makerToken], - [takerToken, order.takerToken], - [options.takerAddress, order.taker], - [options.txOrigin, order.txOrigin], - ]) - ) { - this._warningLogger( - order, - 'Unexpected token, tx origin or taker address in RFQ-T order, filtering out', - ); - return false; - } - const isLastLook = Boolean(options.isLastLook); - const msRemainingUntilExpiration = QuoteRequestor.getDurationUntilExpirationMs(new BigNumber(order.expiry)); - const isExpirationTooSoon = msRemainingUntilExpiration.lt(this._expiryBufferMs); - if (isExpirationTooSoon) { - this._warningLogger(order, 'Expiry too soon in RFQ-T firm quote, filtering out'); - this._metrics?.incrementExpirationToSoonCounter(isLastLook, order.maker); - return false; - } else { - const secondsRemaining = msRemainingUntilExpiration.div(ONE_SECOND_MS); - this._metrics?.measureExpirationForValidOrder(isLastLook, order.maker, secondsRemaining); - const takerAmount = new BigNumber(order.takerAmount); - const fillRatio = takerAmount.div(assetFillAmount); - if (fillRatio.lt(1) && fillRatio.gte(FILL_RATIO_WARNING_LEVEL)) { - this._warningLogger( - { - makerUri: result.makerUri, - fillRatio, - assetFillAmount, - takerToken, - makerToken, - takerAmount: order.takerAmount, - makerAmount: order.makerAmount, - }, - 'Fill ratio in warning range', - ); - this._metrics?.incrementFillRatioWarningCounter(isLastLook, order.maker); - } - return true; - } - }); - - // Save the maker URI for later and return just the order - const rfqQuotes = validQuotes.map(result => { - const { signature, ...rest } = result.response; - const order: SignedNativeOrder = { - order: { - ...rest, - makerAmount: new BigNumber(result.response.makerAmount), - takerAmount: new BigNumber(result.response.takerAmount), - expiry: new BigNumber(result.response.expiry), - salt: new BigNumber(result.response.salt), - }, - type: FillQuoteTransformerOrderType.Rfq, - signature, - }; - this._orderSignatureToMakerUri[nativeDataToId(result.response)] = result.makerUri; - return order; - }); - return rfqQuotes; - } - - private async _fetchAndValidateIndicativeQuotesAsync( - makerToken: string, - takerToken: string, - assetFillAmount: BigNumber, - marketOperation: MarketOperation, - comparisonPrice: BigNumber | undefined, - options: RfqRequestOpts, - assetOfferings: RfqMakerAssetOfferings, - ): Promise { - // fetch quotes - const rawQuotes = await this._getQuotesAsync( - makerToken, - takerToken, - assetFillAmount, - marketOperation, - comparisonPrice, - options, - 'indicative', - assetOfferings, - ); - - // validate - const validationFunction = (o: V4RFQIndicativeQuoteMM) => this._isValidRfqtIndicativeQuoteResponse(o); - const validQuotes = rawQuotes.filter(result => { - const order = result.response; - if (!validationFunction(order)) { - this._warningLogger(result, 'Invalid RFQ indicative quote received, filtering out'); - return false; - } - if ( - !hasExpectedAddresses([ - [makerToken, order.makerToken], - [takerToken, order.takerToken], - ]) - ) { - this._warningLogger(order, 'Unexpected token or taker address in RFQ order, filtering out'); - return false; - } - const msRemainingUntilExpiration = QuoteRequestor.getDurationUntilExpirationMs(new BigNumber(order.expiry)); - const isExpirationTooSoon = msRemainingUntilExpiration.lt(this._expiryBufferMs); - if (isExpirationTooSoon) { - this._warningLogger(order, 'Expiry too soon in RFQ indicative quote, filtering out'); - return false; - } else { - return true; - } - }); - const quotes = validQuotes.map(r => r.response); - quotes.forEach(q => { - q.makerAmount = new BigNumber(q.makerAmount); - q.takerAmount = new BigNumber(q.takerAmount); - q.expiry = new BigNumber(q.expiry); - }); - return quotes; - } -} diff --git a/packages/asset-swapper/src/utils/quote_simulation.ts b/packages/asset-swapper/src/utils/quote_simulation.ts deleted file mode 100644 index d8ef361fe5..0000000000 --- a/packages/asset-swapper/src/utils/quote_simulation.ts +++ /dev/null @@ -1,312 +0,0 @@ -import { FillQuoteTransformerOrderType } from '@0x/protocol-utils'; -import { BigNumber } from '@0x/utils'; - -import { constants } from '../constants'; -import { MarketOperation } from '../types'; - -import { GasSchedule, NativeLimitOrderFillData, OptimizedMarketOrder } from './market_operation_utils/types'; -import { getNativeAdjustedTakerFeeAmount } from './utils'; - -const { PROTOCOL_FEE_MULTIPLIER, ZERO_AMOUNT } = constants; -const { ROUND_DOWN, ROUND_UP } = BigNumber; - -// tslint:disable completed-docs - -export interface QuoteFillResult { - // Maker asset bought. - makerAssetAmount: BigNumber; - // Taker asset sold. - takerAssetAmount: BigNumber; - // Taker fees that can be paid with the maker asset. - takerFeeMakerAssetAmount: BigNumber; - // Taker fees that can be paid with the taker asset. - takerFeeTakerAssetAmount: BigNumber; - // Total maker asset amount bought (including fees). - totalMakerAssetAmount: BigNumber; - // Total taker asset amount sold (including fees). - totalTakerAssetAmount: BigNumber; - // Protocol fees paid. - protocolFeeAmount: BigNumber; - // (Estimated) gas used. - gas: number; - // Fill amounts by source. - // For sells, this is the taker assets sold. - // For buys, this is the maker assets bought. - fillAmountBySource: { [source: string]: BigNumber }; -} - -interface IntermediateQuoteFillResult { - // Input tokens filled. Taker asset for sells, maker asset for buys. - input: BigNumber; - // Output tokens filled. Maker asset for sells, taker asset for buys. - output: BigNumber; - // Taker fees that can be paid with the input token. - // Positive for sells, negative for buys. - inputFee: BigNumber; - // Taker fees that can be paid with the output token. - // Negative for sells, positive for buys. - outputFee: BigNumber; - // Protocol fees paid. - protocolFee: BigNumber; - // (Estimated) gas used. - gas: number; - // Input amounts filled by sources. - inputBySource: { [source: string]: BigNumber }; -} - -const EMPTY_QUOTE_INTERMEDIATE_FILL_RESULT = { - input: ZERO_AMOUNT, - output: ZERO_AMOUNT, - outputFee: ZERO_AMOUNT, - inputFee: ZERO_AMOUNT, - protocolFee: ZERO_AMOUNT, - gas: 0, -}; - -export interface QuoteFillInfo { - orders: OptimizedMarketOrder[]; - fillAmount: BigNumber; - gasPrice: BigNumber; - side: MarketOperation; - opts: Partial; -} - -export interface QuoteFillInfoOpts { - gasSchedule: GasSchedule; - protocolFeeMultiplier: BigNumber; - slippage: number; -} - -const DEFAULT_SIMULATED_FILL_QUOTE_INFO_OPTS: QuoteFillInfoOpts = { - gasSchedule: {}, - protocolFeeMultiplier: PROTOCOL_FEE_MULTIPLIER, - slippage: 0, -}; - -export interface QuoteFillOrderCall { - order: OptimizedMarketOrder; - // Total input amount defined in the order. - totalOrderInput: BigNumber; - // Total output amount defined in the order. - totalOrderOutput: BigNumber; - // Total fees payable with input token, defined in the order. - // Positive for sells, negative for buys. - totalOrderInputFee: BigNumber; - // Total fees payable with output token, defined in the order. - // Negative for sells, positive for buys. - totalOrderOutputFee: BigNumber; -} - -// Simulates filling a quote in the best case. -export function simulateBestCaseFill(quoteInfo: QuoteFillInfo): QuoteFillResult { - const opts = { - ...DEFAULT_SIMULATED_FILL_QUOTE_INFO_OPTS, - ...quoteInfo.opts, - }; - const protocolFeePerFillOrder = quoteInfo.gasPrice.times(opts.protocolFeeMultiplier); - const result = fillQuoteOrders( - createBestCaseFillOrderCalls(quoteInfo), - quoteInfo.fillAmount, - protocolFeePerFillOrder, - opts.gasSchedule, - ); - return fromIntermediateQuoteFillResult(result, quoteInfo); -} - -// Simulates filling a quote in the worst case. -export function simulateWorstCaseFill(quoteInfo: QuoteFillInfo): QuoteFillResult { - const opts = { - ...DEFAULT_SIMULATED_FILL_QUOTE_INFO_OPTS, - ...quoteInfo.opts, - }; - const protocolFeePerFillOrder = quoteInfo.gasPrice.times(opts.protocolFeeMultiplier); - const bestCase = createBestCaseFillOrderCalls(quoteInfo); - const result = { - ...fillQuoteOrders(bestCase, quoteInfo.fillAmount, protocolFeePerFillOrder, opts.gasSchedule), - // Worst case gas and protocol fee is hitting all orders. - gas: getTotalGasUsedByFills(quoteInfo.orders, opts.gasSchedule), - protocolFee: protocolFeePerFillOrder.times(quoteInfo.orders.filter(o => hasProtocolFee(o)).length), - }; - // Adjust the output by 1-slippage for the worst case if it is a sell - // Adjust the output by 1+slippage for the worst case if it is a buy - result.output = - quoteInfo.side === MarketOperation.Sell - ? result.output.times(1 - opts.slippage).integerValue(BigNumber.ROUND_DOWN) - : result.output.times(1 + opts.slippage).integerValue(BigNumber.ROUND_UP); - return fromIntermediateQuoteFillResult(result, quoteInfo); -} - -export function fillQuoteOrders( - fillOrders: QuoteFillOrderCall[], - inputAmount: BigNumber, - protocolFeePerFillOrder: BigNumber, - gasSchedule: GasSchedule, -): IntermediateQuoteFillResult { - const result: IntermediateQuoteFillResult = { - ...EMPTY_QUOTE_INTERMEDIATE_FILL_RESULT, - inputBySource: {}, - }; - let remainingInput = inputAmount; - for (const fo of fillOrders) { - if (remainingInput.lte(0)) { - break; - } - const { source, fillData } = fo.order; - const gas = gasSchedule[source] === undefined ? 0 : gasSchedule[source]!(fillData); - result.gas += new BigNumber(gas).toNumber(); - result.inputBySource[source] = result.inputBySource[source] || ZERO_AMOUNT; - - const filledInput = solveForInputFillAmount( - remainingInput, - fo.order.fill.input, - fo.totalOrderInput, - fo.totalOrderInputFee, - ); - const filledOutput = fo.order.fill.output.times(filledInput.div(fo.order.fill.input)); - const filledInputFee = filledInput.div(fo.totalOrderInput).times(fo.totalOrderInputFee); - const filledOutputFee = filledOutput.div(fo.totalOrderOutput).times(fo.totalOrderOutputFee); - - result.inputBySource[source] = result.inputBySource[source].plus(filledInput); - result.input = result.input.plus(filledInput); - result.output = result.output.plus(filledOutput); - result.inputFee = result.inputFee.plus(filledInputFee); - result.outputFee = result.outputFee.plus(filledOutputFee); - remainingInput = remainingInput.minus(filledInput.plus(filledInputFee)); - // NOTE: V4 Limit orders have Protocol fees - const protocolFee = hasProtocolFee(fo.order) ? protocolFeePerFillOrder : ZERO_AMOUNT; - result.protocolFee = result.protocolFee.plus(protocolFee); - } - return result; -} - -function hasProtocolFee(o: OptimizedMarketOrder): boolean { - return o.type === FillQuoteTransformerOrderType.Limit; -} - -function solveForInputFillAmount( - remainingInput: BigNumber, - fillableInput: BigNumber, - totalOrderInput: BigNumber, - totalOrderInputFee: BigNumber, -): BigNumber { - // When accounting for input token taker fees, the effective input amount is - // given by: - // i' = i + f * i / o - // where: - // i' - The effective input amount, including fees - // i - An input amount - // f - totalOrderInputFee - // o - totalOrderInput - // Solving for i we get: - // i = (i' * o) / (f + o) - const denom = totalOrderInput.plus(totalOrderInputFee); - if (denom.eq(0)) { - // A zero denominator would imply an order whose fees are >= the input - // token amount. - // For sells, takerFeeAmount >= takerAssetAmount (technically OK but really undesirable). - // For buys, takerFeeAmount >= makerAssetAmount (losing all your returns to fees). - return fillableInput; - } - return BigNumber.min( - fillableInput, - // let i' = remainingInput - remainingInput.times(totalOrderInput).div(denom), - ); -} - -function createBestCaseFillOrderCalls(quoteInfo: QuoteFillInfo): QuoteFillOrderCall[] { - const { orders, side } = quoteInfo; - return orders.map(o => ({ - order: o, - ...(side === MarketOperation.Sell - ? { - totalOrderInput: o.takerAmount, - totalOrderOutput: o.makerAmount, - totalOrderInputFee: - o.type === FillQuoteTransformerOrderType.Limit - ? getNativeAdjustedTakerFeeAmount( - (o.fillData as NativeLimitOrderFillData).order, - o.takerAmount, - ) - : ZERO_AMOUNT, - totalOrderOutputFee: ZERO_AMOUNT, // makerToken fees are not supported in v4 (sell output) - } - : // Buy - { - totalOrderInput: o.makerAmount, - totalOrderOutput: o.takerAmount, - totalOrderInputFee: ZERO_AMOUNT, // makerToken fees are not supported in v4 (buy input) - totalOrderOutputFee: - o.type === FillQuoteTransformerOrderType.Limit - ? getNativeAdjustedTakerFeeAmount( - (o.fillData as NativeLimitOrderFillData).order, - o.takerAmount, - ) - : ZERO_AMOUNT, - }), - })); -} - -function roundInputAmount(amount: BigNumber, side: MarketOperation): BigNumber { - return amount.integerValue(side === MarketOperation.Sell ? ROUND_UP : ROUND_DOWN); -} - -function roundOutputAmount(amount: BigNumber, side: MarketOperation): BigNumber { - return amount.integerValue(side === MarketOperation.Sell ? ROUND_DOWN : ROUND_UP); -} - -function roundIntermediateFillResult( - ir: IntermediateQuoteFillResult, - side: MarketOperation, -): IntermediateQuoteFillResult { - return { - input: roundInputAmount(ir.input, side), - output: roundOutputAmount(ir.output, side), - inputFee: roundInputAmount(ir.inputFee, side), - outputFee: roundOutputAmount(ir.outputFee, side), - protocolFee: ir.protocolFee.integerValue(ROUND_UP), - gas: Math.ceil(ir.gas), - inputBySource: Object.assign( - {}, - ...Object.entries(ir.inputBySource).map(([k, v]) => ({ [k]: roundInputAmount(v, side) })), - ), - }; -} - -function fromIntermediateQuoteFillResult(ir: IntermediateQuoteFillResult, quoteInfo: QuoteFillInfo): QuoteFillResult { - const { side } = quoteInfo; - const _ir = roundIntermediateFillResult(ir, side); - return { - ...(side === MarketOperation.Sell - ? // Sell - { - makerAssetAmount: _ir.output, - takerAssetAmount: _ir.input, - takerFeeMakerAssetAmount: _ir.outputFee, - takerFeeTakerAssetAmount: _ir.inputFee, - totalMakerAssetAmount: _ir.output.plus(_ir.outputFee), - totalTakerAssetAmount: _ir.input.plus(_ir.inputFee), - } - : // Buy - { - makerAssetAmount: _ir.input, - takerAssetAmount: _ir.output, - takerFeeMakerAssetAmount: _ir.inputFee, - takerFeeTakerAssetAmount: _ir.outputFee, - totalMakerAssetAmount: _ir.input.plus(_ir.inputFee), - totalTakerAssetAmount: _ir.output.plus(_ir.outputFee), - }), - protocolFeeAmount: _ir.protocolFee, - gas: _ir.gas, - fillAmountBySource: _ir.inputBySource, - }; -} - -function getTotalGasUsedByFills(fills: OptimizedMarketOrder[], gasSchedule: GasSchedule): number { - let gasUsed = 0; - for (const f of fills) { - const fee = gasSchedule[f.source] === undefined ? 0 : gasSchedule[f.source]!(f.fillData); - gasUsed += new BigNumber(fee).toNumber(); - } - return gasUsed; -} diff --git a/packages/asset-swapper/src/utils/rfq_client_mappers.ts b/packages/asset-swapper/src/utils/rfq_client_mappers.ts deleted file mode 100644 index 6d2936c3b6..0000000000 --- a/packages/asset-swapper/src/utils/rfq_client_mappers.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { FillQuoteTransformerOrderType } from '@0x/protocol-utils'; - -import { SignedNativeOrder } from '../types'; - -import { RfqClientV1Quote } from './irfq_client'; - -/** - * Converts a RfqClientRfqOrderFirmQuote to a SignedNativeOrder - */ -export const toSignedNativeOrder = (quote: RfqClientV1Quote): SignedNativeOrder => { - return { - type: FillQuoteTransformerOrderType.Rfq, - order: quote.order, - signature: quote.signature, - }; -}; diff --git a/packages/asset-swapper/src/utils/rfq_maker_blacklist.ts b/packages/asset-swapper/src/utils/rfq_maker_blacklist.ts deleted file mode 100644 index 8cfdf4c2b6..0000000000 --- a/packages/asset-swapper/src/utils/rfq_maker_blacklist.ts +++ /dev/null @@ -1,43 +0,0 @@ -/** - * Tracks a maker's history of timely responses, and manages whether a given - * maker should be avoided for being too latent. - */ - -import { constants } from '../constants'; -import { LogFunction } from '../types'; - -export class RfqMakerBlacklist { - private readonly _makerTimeoutStreakLength: { [makerUrl: string]: number } = {}; - private readonly _makerBlacklistedUntilDate: { [makerUrl: string]: number } = {}; - constructor( - private readonly _blacklistDurationMinutes: number, - private readonly _timeoutStreakThreshold: number, - public infoLogger: LogFunction = constants.DEFAULT_INFO_LOGGER, - ) {} - public logTimeoutOrLackThereof(makerUrl: string, didTimeout: boolean): void { - if (!this._makerTimeoutStreakLength.hasOwnProperty(makerUrl)) { - this._makerTimeoutStreakLength[makerUrl] = 0; - } - if (didTimeout) { - this._makerTimeoutStreakLength[makerUrl] += 1; - if (this._makerTimeoutStreakLength[makerUrl] === this._timeoutStreakThreshold) { - const blacklistEnd = Date.now() + this._blacklistDurationMinutes * constants.ONE_MINUTE_MS; - this._makerBlacklistedUntilDate[makerUrl] = blacklistEnd; - this.infoLogger( - { makerUrl, blacklistedUntil: new Date(blacklistEnd).toISOString() }, - 'maker blacklisted', - ); - } - } else { - this._makerTimeoutStreakLength[makerUrl] = 0; - } - } - public isMakerBlacklisted(makerUrl: string): boolean { - const now = Date.now(); - if (now > this._makerBlacklistedUntilDate[makerUrl]) { - delete this._makerBlacklistedUntilDate[makerUrl]; - this.infoLogger({ makerUrl }, 'maker unblacklisted'); - } - return this._makerBlacklistedUntilDate[makerUrl] > now; - } -} diff --git a/packages/asset-swapper/src/utils/rfqt_mocker.ts b/packages/asset-swapper/src/utils/rfqt_mocker.ts deleted file mode 100644 index 8b4ffdf3d4..0000000000 --- a/packages/asset-swapper/src/utils/rfqt_mocker.ts +++ /dev/null @@ -1,47 +0,0 @@ -import axios, { AxiosInstance } from 'axios'; -import AxiosMockAdapter from 'axios-mock-adapter'; - -import { MockedRfqQuoteResponse } from '../types'; - -export enum RfqtQuoteEndpoint { - Indicative = 'price', - Firm = 'quote', -} - -/** - * A helper utility for testing which mocks out - * requests to RFQ-t providers - */ -export const rfqtMocker = { - /** - * A helper utility for testing which mocks out - * requests to RFQ-t providers - */ - withMockedRfqtQuotes: async ( - mockedResponses: MockedRfqQuoteResponse[], - quoteType: RfqtQuoteEndpoint, - afterResponseCallback: () => Promise, - axiosClient: AxiosInstance = axios, - ): Promise => { - const mockedAxios = new AxiosMockAdapter(axiosClient); - try { - // Mock out RFQT responses - for (const mockedResponse of mockedResponses) { - const { endpoint, requestApiKey, requestParams, responseData, responseCode } = mockedResponse; - const requestHeaders = { - Accept: 'application/json, text/plain, */*', - '0x-api-key': requestApiKey, - '0x-integrator-id': requestApiKey, - }; - mockedAxios - .onGet(`${endpoint}/${quoteType}`, { params: requestParams }, requestHeaders) - .replyOnce(responseCode, responseData); - } - // Perform the callback function, e.g. a test validation - await afterResponseCallback(); - } finally { - // Ensure we always restore axios afterwards - mockedAxios.restore(); - } - }, -}; diff --git a/packages/asset-swapper/src/utils/token_adjacency_graph.ts b/packages/asset-swapper/src/utils/token_adjacency_graph.ts deleted file mode 100644 index 97dc7470ea..0000000000 --- a/packages/asset-swapper/src/utils/token_adjacency_graph.ts +++ /dev/null @@ -1,84 +0,0 @@ -import * as _ from 'lodash'; - -import { Address } from '../types'; - -export class TokenAdjacencyGraph { - private readonly _graph: Map; - private readonly _defaultTokens: readonly Address[]; - - public static getEmptyGraph(): TokenAdjacencyGraph { - return new TokenAdjacencyGraphBuilder().build(); - } - - /** Prefer using {@link TokenAdjacencyGraphBuilder}. */ - constructor(graph: Map, defaultTokens: readonly Address[]) { - this._graph = graph; - this._defaultTokens = defaultTokens; - } - - public getAdjacentTokens(fromToken: Address): readonly Address[] { - return this._graph.get(fromToken.toLowerCase()) || this._defaultTokens; - } - - /** Given a token pair, returns the intermediate tokens to consider for two-hop routes. */ - public getIntermediateTokens(takerToken: Address, makerToken: Address): Address[] { - // NOTE: it seems it should be a union of `to` tokens of `takerToken` and `from` tokens of `makerToken`, - // leaving it as same as the initial implementation for now. - return _.union(this.getAdjacentTokens(takerToken), this.getAdjacentTokens(makerToken)).filter( - token => token !== takerToken.toLowerCase() && token !== makerToken.toLowerCase(), - ); - } -} - -// tslint:disable-next-line: max-classes-per-file -export class TokenAdjacencyGraphBuilder { - private readonly _graph: Map; - private readonly _defaultTokens: readonly Address[]; - - constructor(defaultTokens: readonly string[] = []) { - this._graph = new Map(); - this._defaultTokens = defaultTokens.map(addr => addr.toLowerCase()); - } - - public add(fromToken: Address, toToken: Address): TokenAdjacencyGraphBuilder { - const fromLower = fromToken.toLowerCase(); - const toLower = toToken.toLowerCase(); - - if (fromLower === toLower) { - throw new Error(`from token (${fromToken}) must be different from to token (${toToken})`); - } - - if (!this._graph.has(fromLower)) { - this._graph.set(fromLower, [...this._defaultTokens]); - } - - const toTokens = this._graph.get(fromLower)!; - if (!toTokens.includes(toLower)) { - toTokens.push(toLower); - } - - return this; - } - - public addBidirectional(tokenA: Address, tokenB: Address): TokenAdjacencyGraphBuilder { - return this.add(tokenA, tokenB).add(tokenB, tokenA); - } - - public addCompleteSubgraph(tokens: Address[]): TokenAdjacencyGraphBuilder { - for (let i = 0; i < tokens.length; i++) { - for (let j = i + 1; j < tokens.length; j++) { - this.addBidirectional(tokens[i], tokens[j]); - } - } - return this; - } - - public tap(cb: (graph: TokenAdjacencyGraphBuilder) => void): TokenAdjacencyGraphBuilder { - cb(this); - return this; - } - - public build(): TokenAdjacencyGraph { - return new TokenAdjacencyGraph(this._graph, this._defaultTokens); - } -} diff --git a/packages/asset-swapper/src/utils/utils.ts b/packages/asset-swapper/src/utils/utils.ts deleted file mode 100644 index be7a7570ff..0000000000 --- a/packages/asset-swapper/src/utils/utils.ts +++ /dev/null @@ -1,100 +0,0 @@ -import { CommonOrderFields, FillQuoteTransformerOrderType, LimitOrderFields } from '@0x/protocol-utils'; -import { BigNumber } from '@0x/utils'; -import { Web3Wrapper } from '@0x/web3-wrapper'; - -import { constants } from '../constants'; -import { NativeOrderFillableAmountFields, SignedNativeOrder } from '../types'; - -import { ZERO_AMOUNT } from './market_operation_utils/constants'; - -// tslint:disable: no-unnecessary-type-assertion completed-docs - -export function numberPercentageToEtherTokenAmountPercentage(percentage: number): BigNumber { - return Web3Wrapper.toBaseUnitAmount(constants.ONE_AMOUNT, constants.ETHER_TOKEN_DECIMALS).multipliedBy(percentage); -} - -export function getAdjustedTakerAmountFromFees(order: T): BigNumber { - return order.takerAmount.plus(order.takerTokenFeeAmount); -} - -/** - * Given an amount of taker asset, calculate the the amount of maker asset - * @param order The order - * @param makerFillAmount the amount of taker asset - */ -export function getNativeAdjustedMakerFillAmount(order: CommonOrderFields, takerFillAmount: BigNumber): BigNumber { - // Round down because exchange rate favors Maker - const makerFillAmount = takerFillAmount - .multipliedBy(order.makerAmount) - .div(order.takerAmount) - .integerValue(BigNumber.ROUND_FLOOR); - return makerFillAmount; -} -/** - * Given an amount of maker asset, calculate the equivalent amount in taker asset - * @param order The order - * @param makerFillAmount the amount of maker asset - */ -export function getNativeAdjustedTakerFillAmount(order: CommonOrderFields, makerFillAmount: BigNumber): BigNumber { - // Round up because exchange rate favors Maker - const takerFillAmount = makerFillAmount - .multipliedBy(order.takerAmount) - .div(order.makerAmount) - .integerValue(BigNumber.ROUND_CEIL); - return takerFillAmount; -} - -/** - * Given an amount of taker asset, calculate the fee amount required for the taker - * @param order The order - * @param takerFillAmount the amount of taker asset - */ -export function getNativeAdjustedTakerFeeAmount(order: LimitOrderFields, takerFillAmount: BigNumber): BigNumber { - // Round down because Taker fee rate favors Taker - const takerFeeAmount = takerFillAmount - .multipliedBy(order.takerTokenFeeAmount) - .div(order.takerAmount) - .integerValue(BigNumber.ROUND_FLOOR); - return takerFeeAmount; -} - -const EMPTY_FILLABLE_AMOUNTS: NativeOrderFillableAmountFields = { - fillableMakerAmount: ZERO_AMOUNT, - fillableTakerAmount: ZERO_AMOUNT, - fillableTakerFeeAmount: ZERO_AMOUNT, -}; - -export function getNativeAdjustedFillableAmountsFromTakerAmount( - order: SignedNativeOrder, - takerFillableAmount: BigNumber, -): NativeOrderFillableAmountFields { - if (takerFillableAmount.isZero()) { - return EMPTY_FILLABLE_AMOUNTS; - } - return { - fillableTakerAmount: takerFillableAmount, - fillableMakerAmount: getNativeAdjustedMakerFillAmount(order.order, takerFillableAmount), - fillableTakerFeeAmount: - order.type === FillQuoteTransformerOrderType.Limit - ? getNativeAdjustedTakerFeeAmount(order.order as LimitOrderFields, takerFillableAmount) - : ZERO_AMOUNT, - }; -} - -export function getNativeAdjustedFillableAmountsFromMakerAmount( - order: SignedNativeOrder, - makerFillableAmount: BigNumber, -): NativeOrderFillableAmountFields { - if (makerFillableAmount.isZero()) { - return EMPTY_FILLABLE_AMOUNTS; - } - const takerFillableAmount = getNativeAdjustedTakerFillAmount(order.order, makerFillableAmount); - return { - fillableMakerAmount: makerFillableAmount, - fillableTakerAmount: takerFillableAmount, - fillableTakerFeeAmount: - order.type === FillQuoteTransformerOrderType.Limit - ? getNativeAdjustedTakerFeeAmount(order.order as LimitOrderFields, takerFillableAmount) - : ZERO_AMOUNT, - }; -} diff --git a/packages/asset-swapper/src/wrappers.ts b/packages/asset-swapper/src/wrappers.ts deleted file mode 100644 index 27b17d67ac..0000000000 --- a/packages/asset-swapper/src/wrappers.ts +++ /dev/null @@ -1,8 +0,0 @@ -/* - * ----------------------------------------------------------------------------- - * Warning: This file is auto-generated by contracts-gen. Don't edit manually. - * ----------------------------------------------------------------------------- - */ -export * from '../generated-wrappers/balance_checker'; -export * from '../generated-wrappers/erc20_bridge_sampler'; -export * from '../generated-wrappers/fake_taker'; diff --git a/packages/asset-swapper/test/artifacts.ts b/packages/asset-swapper/test/artifacts.ts deleted file mode 100644 index 4ebf5bba93..0000000000 --- a/packages/asset-swapper/test/artifacts.ts +++ /dev/null @@ -1,103 +0,0 @@ -/* - * ----------------------------------------------------------------------------- - * Warning: This file is auto-generated by contracts-gen. Don't edit manually. - * ----------------------------------------------------------------------------- - */ -import { ContractArtifact } from 'ethereum-types'; - -import * as ApproximateBuys from '../test/generated-artifacts/ApproximateBuys.json'; -import * as BalanceChecker from '../test/generated-artifacts/BalanceChecker.json'; -import * as BalancerSampler from '../test/generated-artifacts/BalancerSampler.json'; -import * as BalancerV2BatchSampler from '../test/generated-artifacts/BalancerV2BatchSampler.json'; -import * as BalancerV2Common from '../test/generated-artifacts/BalancerV2Common.json'; -import * as BalancerV2Sampler from '../test/generated-artifacts/BalancerV2Sampler.json'; -import * as BancorSampler from '../test/generated-artifacts/BancorSampler.json'; -import * as BancorV3Sampler from '../test/generated-artifacts/BancorV3Sampler.json'; -import * as CompoundSampler from '../test/generated-artifacts/CompoundSampler.json'; -import * as CurveSampler from '../test/generated-artifacts/CurveSampler.json'; -import * as DODOSampler from '../test/generated-artifacts/DODOSampler.json'; -import * as DODOV2Sampler from '../test/generated-artifacts/DODOV2Sampler.json'; -import * as ERC20BridgeSampler from '../test/generated-artifacts/ERC20BridgeSampler.json'; -import * as FakeTaker from '../test/generated-artifacts/FakeTaker.json'; -import * as GMXSampler from '../test/generated-artifacts/GMXSampler.json'; -import * as IBalancer from '../test/generated-artifacts/IBalancer.json'; -import * as IBalancerV2Vault from '../test/generated-artifacts/IBalancerV2Vault.json'; -import * as IBancor from '../test/generated-artifacts/IBancor.json'; -import * as IBancorV3 from '../test/generated-artifacts/IBancorV3.json'; -import * as ICurve from '../test/generated-artifacts/ICurve.json'; -import * as IGMX from '../test/generated-artifacts/IGMX.json'; -import * as IMooniswap from '../test/generated-artifacts/IMooniswap.json'; -import * as IMStable from '../test/generated-artifacts/IMStable.json'; -import * as IMultiBridge from '../test/generated-artifacts/IMultiBridge.json'; -import * as IPlatypus from '../test/generated-artifacts/IPlatypus.json'; -import * as IShell from '../test/generated-artifacts/IShell.json'; -import * as IUniswapExchangeQuotes from '../test/generated-artifacts/IUniswapExchangeQuotes.json'; -import * as IUniswapV2Router01 from '../test/generated-artifacts/IUniswapV2Router01.json'; -import * as KyberDmmSampler from '../test/generated-artifacts/KyberDmmSampler.json'; -import * as LidoSampler from '../test/generated-artifacts/LidoSampler.json'; -import * as LiquidityProviderSampler from '../test/generated-artifacts/LiquidityProviderSampler.json'; -import * as MakerPSMSampler from '../test/generated-artifacts/MakerPSMSampler.json'; -import * as MooniswapSampler from '../test/generated-artifacts/MooniswapSampler.json'; -import * as MStableSampler from '../test/generated-artifacts/MStableSampler.json'; -import * as NativeOrderSampler from '../test/generated-artifacts/NativeOrderSampler.json'; -import * as PlatypusSampler from '../test/generated-artifacts/PlatypusSampler.json'; -import * as SamplerUtils from '../test/generated-artifacts/SamplerUtils.json'; -import * as ShellSampler from '../test/generated-artifacts/ShellSampler.json'; -import * as SynthetixSampler from '../test/generated-artifacts/SynthetixSampler.json'; -import * as TestNativeOrderSampler from '../test/generated-artifacts/TestNativeOrderSampler.json'; -import * as TwoHopSampler from '../test/generated-artifacts/TwoHopSampler.json'; -import * as UniswapSampler from '../test/generated-artifacts/UniswapSampler.json'; -import * as UniswapV2Sampler from '../test/generated-artifacts/UniswapV2Sampler.json'; -import * as UniswapV3Sampler from '../test/generated-artifacts/UniswapV3Sampler.json'; -import * as UtilitySampler from '../test/generated-artifacts/UtilitySampler.json'; -import * as VelodromeSampler from '../test/generated-artifacts/VelodromeSampler.json'; -import * as WooPPSampler from '../test/generated-artifacts/WooPPSampler.json'; -export const artifacts = { - ApproximateBuys: ApproximateBuys as ContractArtifact, - BalanceChecker: BalanceChecker as ContractArtifact, - BalancerSampler: BalancerSampler as ContractArtifact, - BalancerV2BatchSampler: BalancerV2BatchSampler as ContractArtifact, - BalancerV2Common: BalancerV2Common as ContractArtifact, - BalancerV2Sampler: BalancerV2Sampler as ContractArtifact, - BancorSampler: BancorSampler as ContractArtifact, - BancorV3Sampler: BancorV3Sampler as ContractArtifact, - CompoundSampler: CompoundSampler as ContractArtifact, - CurveSampler: CurveSampler as ContractArtifact, - DODOSampler: DODOSampler as ContractArtifact, - DODOV2Sampler: DODOV2Sampler as ContractArtifact, - ERC20BridgeSampler: ERC20BridgeSampler as ContractArtifact, - FakeTaker: FakeTaker as ContractArtifact, - GMXSampler: GMXSampler as ContractArtifact, - KyberDmmSampler: KyberDmmSampler as ContractArtifact, - LidoSampler: LidoSampler as ContractArtifact, - LiquidityProviderSampler: LiquidityProviderSampler as ContractArtifact, - MStableSampler: MStableSampler as ContractArtifact, - MakerPSMSampler: MakerPSMSampler as ContractArtifact, - MooniswapSampler: MooniswapSampler as ContractArtifact, - NativeOrderSampler: NativeOrderSampler as ContractArtifact, - PlatypusSampler: PlatypusSampler as ContractArtifact, - SamplerUtils: SamplerUtils as ContractArtifact, - ShellSampler: ShellSampler as ContractArtifact, - SynthetixSampler: SynthetixSampler as ContractArtifact, - TwoHopSampler: TwoHopSampler as ContractArtifact, - UniswapSampler: UniswapSampler as ContractArtifact, - UniswapV2Sampler: UniswapV2Sampler as ContractArtifact, - UniswapV3Sampler: UniswapV3Sampler as ContractArtifact, - UtilitySampler: UtilitySampler as ContractArtifact, - VelodromeSampler: VelodromeSampler as ContractArtifact, - WooPPSampler: WooPPSampler as ContractArtifact, - IBalancer: IBalancer as ContractArtifact, - IBalancerV2Vault: IBalancerV2Vault as ContractArtifact, - IBancor: IBancor as ContractArtifact, - IBancorV3: IBancorV3 as ContractArtifact, - ICurve: ICurve as ContractArtifact, - IGMX: IGMX as ContractArtifact, - IMStable: IMStable as ContractArtifact, - IMooniswap: IMooniswap as ContractArtifact, - IMultiBridge: IMultiBridge as ContractArtifact, - IPlatypus: IPlatypus as ContractArtifact, - IShell: IShell as ContractArtifact, - IUniswapExchangeQuotes: IUniswapExchangeQuotes as ContractArtifact, - IUniswapV2Router01: IUniswapV2Router01 as ContractArtifact, - TestNativeOrderSampler: TestNativeOrderSampler as ContractArtifact, -}; diff --git a/packages/asset-swapper/test/comparison_price_test.ts b/packages/asset-swapper/test/comparison_price_test.ts deleted file mode 100644 index 3f2a9e286d..0000000000 --- a/packages/asset-swapper/test/comparison_price_test.ts +++ /dev/null @@ -1,158 +0,0 @@ -// tslint:disable:custom-no-magic-numbers -import { BigNumber } from '@0x/utils'; -import * as chai from 'chai'; -import * as _ from 'lodash'; -import 'mocha'; - -import { SOURCE_FLAGS } from '../src'; -import { MarketOperation } from '../src/types'; -import { getComparisonPrices } from '../src/utils/market_operation_utils/comparison_price'; -import { SourceFilters } from '../src/utils/market_operation_utils/source_filters'; -import { DexSample, ERC20BridgeSource, MarketSideLiquidity } from '../src/utils/market_operation_utils/types'; - -import { chaiSetup } from './utils/chai_setup'; - -chaiSetup.configure(); -const expect = chai.expect; - -const DAI_TOKEN = '0x6b175474e89094c44da98b954eedeac495271d0f'; -const ETH_TOKEN = '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee'; -const GAS_PRICE = new BigNumber(50e9); // 50 gwei -const NATIVE_ORDER_GAS = 220e3; // 220K gas - -// DEX samples to fill in MarketSideLiquidity -const curveSample: DexSample = { - source: ERC20BridgeSource.Curve, - input: new BigNumber(10000), - output: new BigNumber(10001), - fillData: {}, -}; -const uniswapSample1: DexSample = { - source: ERC20BridgeSource.UniswapV2, - input: new BigNumber(10003), - output: new BigNumber(10004), - fillData: {}, -}; -const dexQuotes: DexSample[] = [curveSample, uniswapSample1]; - -const feeSchedule = { - [ERC20BridgeSource.Native]: _.constant({ - gas: NATIVE_ORDER_GAS, - fee: GAS_PRICE.times(NATIVE_ORDER_GAS), - }), -}; - -const exchangeProxyOverhead = (sourceFlags: bigint) => { - if ([SOURCE_FLAGS.RfqOrder].includes(sourceFlags)) { - return new BigNumber(20e3).times(GAS_PRICE); - } else { - return new BigNumber(200e3).times(GAS_PRICE); - } -}; - -const buyMarketSideLiquidity: MarketSideLiquidity = { - // needed params - outputAmountPerEth: new BigNumber(500), - inputAmountPerEth: new BigNumber(1), - side: MarketOperation.Buy, - makerTokenDecimals: 18, - takerTokenDecimals: 18, - // extra - inputAmount: new BigNumber(0), - inputToken: ETH_TOKEN, - outputToken: DAI_TOKEN, - quotes: { - twoHopQuotes: [], - rfqtIndicativeQuotes: [], - dexQuotes: [dexQuotes], - nativeOrders: [], - }, - quoteSourceFilters: new SourceFilters(), - isRfqSupported: false, - blockNumber: 1337420, -}; - -const sellMarketSideLiquidity: MarketSideLiquidity = { - // needed params - outputAmountPerEth: new BigNumber(500), - inputAmountPerEth: new BigNumber(1), - side: MarketOperation.Sell, - makerTokenDecimals: 18, - takerTokenDecimals: 18, - // extra - inputAmount: new BigNumber(0), - inputToken: ETH_TOKEN, - outputToken: DAI_TOKEN, - quotes: { - dexQuotes: [dexQuotes], - nativeOrders: [], - twoHopQuotes: [], - rfqtIndicativeQuotes: [], - }, - quoteSourceFilters: new SourceFilters(), - isRfqSupported: false, - blockNumber: 1337420, -}; - -describe('getComparisonPrices', async () => { - it('should create a proper comparison price for Sells', () => { - // test selling 10 ETH for DAI - // here, ETH is the input token - // and DAI is the output token - const AMOUNT = new BigNumber(10 * 1e18); - // raw maker over taker rate, let's say is 500 flat - const adjustedRate = new BigNumber(500); - - const comparisonPrices = getComparisonPrices( - adjustedRate, - AMOUNT, - sellMarketSideLiquidity, - feeSchedule, - exchangeProxyOverhead, - ); - - // expected outcome - const EXPECTED_PRICE = new BigNumber('500.6'); - - expect(comparisonPrices.wholeOrder).to.deep.eq(EXPECTED_PRICE); - }); - it('should create a proper comparison price for Buys', () => { - // test buying 10 ETH with DAI - // here, ETH is the input token - // and DAI is the output token (now from the maker's perspective) - const AMOUNT = new BigNumber(10 * 1e18); - - // raw maker over taker rate, let's say is ETH/DAI rate is 500 flat - const adjustedRate = new BigNumber(1).dividedBy(new BigNumber(500)); - - const comparisonPrices = getComparisonPrices( - adjustedRate, - AMOUNT, - buyMarketSideLiquidity, - feeSchedule, - exchangeProxyOverhead, - ); - - // expected outcome - const EXPECTED_PRICE = new BigNumber('0.0020024029'); - - expect(comparisonPrices.wholeOrder).to.deep.eq(EXPECTED_PRICE); - }); - it('should not return a price if takerAmount is < 0', () => { - // test selling 0.00001 ETH for DAI - // this will result in a negative comparison price, but here we should return undefined - const AMOUNT = new BigNumber(0.00001 * 1e18); - // raw maker over taker rate, let's say is 500 flat - const adjustedRate = new BigNumber(500); - - const comparisonPrices = getComparisonPrices( - adjustedRate, - AMOUNT, - sellMarketSideLiquidity, - feeSchedule, - exchangeProxyOverhead, - ); - - expect(comparisonPrices.wholeOrder === undefined); - }); -}); diff --git a/packages/asset-swapper/test/contracts/balance_checker_test.ts b/packages/asset-swapper/test/contracts/balance_checker_test.ts deleted file mode 100644 index 6d7af42b21..0000000000 --- a/packages/asset-swapper/test/contracts/balance_checker_test.ts +++ /dev/null @@ -1,132 +0,0 @@ -import { artifacts as erc20Artifacts, DummyERC20TokenContract } from '@0x/contracts-erc20'; -import { blockchainTests, constants, expect, web3Wrapper } from '@0x/contracts-test-utils'; -import { BigNumber } from '@0x/utils'; -import * as _ from 'lodash'; - -import { artifacts } from '../artifacts'; -import { BalanceCheckerContract } from '../wrappers'; - -// tslint:disable: custom-no-magic-numbers - -const ETH_ADDRESS = '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee'; - -blockchainTests.resets('BalanceChecker contract', env => { - let contract: BalanceCheckerContract; - - before(async () => { - contract = await BalanceCheckerContract.deployFrom0xArtifactAsync( - artifacts.BalanceChecker, - env.provider, - env.txDefaults, - {}, - ); - }); - - describe('getBalances', () => { - it('returns the correct array for a successful call', async () => { - const makerToken = await DummyERC20TokenContract.deployFrom0xArtifactAsync( - erc20Artifacts.DummyERC20Token, - env.provider, - env.txDefaults, - artifacts, - constants.DUMMY_TOKEN_NAME, - constants.DUMMY_TOKEN_SYMBOL, - new BigNumber(18), - constants.DUMMY_TOKEN_TOTAL_SUPPLY, - ); - - const accounts = await web3Wrapper.getAvailableAddressesAsync(); - const owner = accounts[0]; - const owner2 = accounts[1]; - - await makerToken.mint(new BigNumber(100)).awaitTransactionSuccessAsync({ from: owner }); - - const testResults = await contract.balances([owner, owner2], [makerToken.address, ETH_ADDRESS]).callAsync(); - - expect(testResults).to.eql([new BigNumber(100), new BigNumber(1000000000000000000000)]); - }); - it('it throws an error if the input arrays of different lengths', async () => { - const accounts = await web3Wrapper.getAvailableAddressesAsync(); - const owner = accounts[0]; - - try { - await contract.balances([owner], [ETH_ADDRESS, ETH_ADDRESS]).callAsync(); - expect.fail(); - } catch (error) { - expect(error.message).to.eql('users array is a different length than the tokens array'); - } - }); - }); - describe('getMinOfBalancesOrAllowances', () => { - it('returns the balance if the allowance can cover it', async () => { - const makerToken = await DummyERC20TokenContract.deployFrom0xArtifactAsync( - erc20Artifacts.DummyERC20Token, - env.provider, - env.txDefaults, - artifacts, - constants.DUMMY_TOKEN_NAME, - constants.DUMMY_TOKEN_SYMBOL, - new BigNumber(18), - constants.DUMMY_TOKEN_TOTAL_SUPPLY, - ); - - const accounts = await web3Wrapper.getAvailableAddressesAsync(); - const owner = accounts[0]; - const owner2 = accounts[1]; - - const allowanceTarget = '0xdef1c0ded9bec7f1a1670819833240f027b25eff'; - - await makerToken.mint(new BigNumber(100)).awaitTransactionSuccessAsync({ from: owner }); - await makerToken.approve(allowanceTarget, new BigNumber(150)).awaitTransactionSuccessAsync({ from: owner }); - - await makerToken.mint(new BigNumber(150)).awaitTransactionSuccessAsync({ from: owner2 }); - await makerToken - .approve(allowanceTarget, new BigNumber(200)) - .awaitTransactionSuccessAsync({ from: owner2 }); - - const testResults = await contract - .getMinOfBalancesOrAllowances( - [owner, owner2], - [makerToken.address, makerToken.address], - allowanceTarget, - ) - .callAsync(); - - expect(testResults).to.eql([new BigNumber(100), new BigNumber(150)]); - }); - it('returns the allowance if the allowance < balance', async () => { - const makerToken = await DummyERC20TokenContract.deployFrom0xArtifactAsync( - erc20Artifacts.DummyERC20Token, - env.provider, - env.txDefaults, - artifacts, - constants.DUMMY_TOKEN_NAME, - constants.DUMMY_TOKEN_SYMBOL, - new BigNumber(18), - constants.DUMMY_TOKEN_TOTAL_SUPPLY, - ); - - const accounts = await web3Wrapper.getAvailableAddressesAsync(); - const owner = accounts[0]; - const owner2 = accounts[1]; - - const allowanceTarget = '0xdef1c0ded9bec7f1a1670819833240f027b25eff'; - - await makerToken.mint(new BigNumber(100)).awaitTransactionSuccessAsync({ from: owner }); - await makerToken.approve(allowanceTarget, new BigNumber(50)).awaitTransactionSuccessAsync({ from: owner }); - - await makerToken.mint(new BigNumber(100)).awaitTransactionSuccessAsync({ from: owner2 }); - await makerToken.approve(allowanceTarget, new BigNumber(75)).awaitTransactionSuccessAsync({ from: owner2 }); - - const testResults = await contract - .getMinOfBalancesOrAllowances( - [owner, owner2], - [makerToken.address, makerToken.address], - allowanceTarget, - ) - .callAsync(); - - expect(testResults).to.eql([new BigNumber(50), new BigNumber(75)]); - }); - }); -}); diff --git a/packages/asset-swapper/test/contracts/bridge_adapter_test.ts b/packages/asset-swapper/test/contracts/bridge_adapter_test.ts deleted file mode 100644 index ae5288eb59..0000000000 --- a/packages/asset-swapper/test/contracts/bridge_adapter_test.ts +++ /dev/null @@ -1,298 +0,0 @@ -import { ChainId } from '@0x/contract-addresses'; -import { blockchainTests, constants, describe, expect } from '@0x/contracts-test-utils'; -import { - artifacts as zeroExArtifacts, - AvalancheBridgeAdapterContract, - BSCBridgeAdapterContract, - CeloBridgeAdapterContract, - EthereumBridgeAdapterContract, - FantomBridgeAdapterContract, - OptimismBridgeAdapterContract, - PolygonBridgeAdapterContract, -} from '@0x/contracts-zero-ex'; - -import { BUY_SOURCE_FILTER_BY_CHAIN_ID, ERC20BridgeSource, SELL_SOURCE_FILTER_BY_CHAIN_ID } from '../../src'; -import { getErc20BridgeSourceToBridgeSource } from '../../src/utils/market_operation_utils/orders'; - -blockchainTests('Bridge adapter source compatibility tests', env => { - describe('Avalanche', () => { - let adapter: AvalancheBridgeAdapterContract; - before(async () => { - adapter = await AvalancheBridgeAdapterContract.deployFrom0xArtifactAsync( - zeroExArtifacts.AvalancheBridgeAdapter, - env.provider, - env.txDefaults, - zeroExArtifacts, - constants.NULL_ADDRESS, - ); - }); - it('sell sources', async () => { - const sellSources = SELL_SOURCE_FILTER_BY_CHAIN_ID[ChainId.Avalanche].exclude([ - ERC20BridgeSource.Native, - ERC20BridgeSource.MultiHop, - ]).sources; - return Promise.all( - sellSources.map(async source => { - const isSupported = await adapter - .isSupportedSource(getErc20BridgeSourceToBridgeSource(source)) - .callAsync(); - expect(isSupported, `${source} is not supported`).to.be.true(); - }), - ); - }); - it('buy sources', async () => { - const buySources = BUY_SOURCE_FILTER_BY_CHAIN_ID[ChainId.Avalanche].exclude([ - ERC20BridgeSource.Native, - ERC20BridgeSource.MultiHop, - ]).sources; - return Promise.all( - buySources.map(async source => { - const isSupported = await adapter - .isSupportedSource(getErc20BridgeSourceToBridgeSource(source)) - .callAsync(); - expect(isSupported, `${source} is not supported`).to.be.true(); - }), - ); - }); - }); - describe('BSC', () => { - let adapter: BSCBridgeAdapterContract; - before(async () => { - adapter = await BSCBridgeAdapterContract.deployFrom0xArtifactAsync( - zeroExArtifacts.BSCBridgeAdapter, - env.provider, - env.txDefaults, - zeroExArtifacts, - constants.NULL_ADDRESS, - ); - }); - it('sell sources', async () => { - const sellSources = SELL_SOURCE_FILTER_BY_CHAIN_ID[ChainId.BSC].exclude([ - ERC20BridgeSource.Native, - ERC20BridgeSource.MultiHop, - ]).sources; - return Promise.all( - sellSources.map(async source => { - const isSupported = await adapter - .isSupportedSource(getErc20BridgeSourceToBridgeSource(source)) - .callAsync(); - expect(isSupported, `${source} is not supported`).to.be.true(); - }), - ); - }); - it('buy sources', async () => { - const buySources = BUY_SOURCE_FILTER_BY_CHAIN_ID[ChainId.BSC].exclude([ - ERC20BridgeSource.Native, - ERC20BridgeSource.MultiHop, - ]).sources; - return Promise.all( - buySources.map(async source => { - const isSupported = await adapter - .isSupportedSource(getErc20BridgeSourceToBridgeSource(source)) - .callAsync(); - expect(isSupported, `${source} is not supported`).to.be.true(); - }), - ); - }); - }); - describe('Celo', () => { - let adapter: CeloBridgeAdapterContract; - before(async () => { - adapter = await CeloBridgeAdapterContract.deployFrom0xArtifactAsync( - zeroExArtifacts.CeloBridgeAdapter, - env.provider, - env.txDefaults, - zeroExArtifacts, - constants.NULL_ADDRESS, - ); - }); - it('sell sources', async () => { - const sellSources = SELL_SOURCE_FILTER_BY_CHAIN_ID[ChainId.Celo].exclude([ - ERC20BridgeSource.Native, - ERC20BridgeSource.MultiHop, - ]).sources; - return Promise.all( - sellSources.map(async source => { - const isSupported = await adapter - .isSupportedSource(getErc20BridgeSourceToBridgeSource(source)) - .callAsync(); - expect(isSupported, `${source} is not supported`).to.be.true(); - }), - ); - }); - it('buy sources', async () => { - const buySources = BUY_SOURCE_FILTER_BY_CHAIN_ID[ChainId.Celo].exclude([ - ERC20BridgeSource.Native, - ERC20BridgeSource.MultiHop, - ]).sources; - return Promise.all( - buySources.map(async source => { - const isSupported = await adapter - .isSupportedSource(getErc20BridgeSourceToBridgeSource(source)) - .callAsync(); - expect(isSupported, `${source} is not supported`).to.be.true(); - }), - ); - }); - }); - describe('Ethereum', () => { - let adapter: EthereumBridgeAdapterContract; - before(async () => { - adapter = await EthereumBridgeAdapterContract.deployFrom0xArtifactAsync( - zeroExArtifacts.EthereumBridgeAdapter, - env.provider, - env.txDefaults, - zeroExArtifacts, - constants.NULL_ADDRESS, - ); - }); - it('sell sources', async () => { - const sellSources = SELL_SOURCE_FILTER_BY_CHAIN_ID[ChainId.Mainnet].exclude([ - ERC20BridgeSource.Native, - ERC20BridgeSource.MultiHop, - ]).sources; - return Promise.all( - sellSources.map(async source => { - const isSupported = await adapter - .isSupportedSource(getErc20BridgeSourceToBridgeSource(source)) - .callAsync(); - expect(isSupported, `${source} is not supported`).to.be.true(); - }), - ); - }); - it('buy sources', async () => { - const buySources = BUY_SOURCE_FILTER_BY_CHAIN_ID[ChainId.Mainnet].exclude([ - ERC20BridgeSource.Native, - ERC20BridgeSource.MultiHop, - ]).sources; - return Promise.all( - buySources.map(async source => { - const isSupported = await adapter - .isSupportedSource(getErc20BridgeSourceToBridgeSource(source)) - .callAsync(); - expect(isSupported, `${source} is not supported`).to.be.true(); - }), - ); - }); - }); - describe('Fantom', () => { - let adapter: FantomBridgeAdapterContract; - before(async () => { - adapter = await FantomBridgeAdapterContract.deployFrom0xArtifactAsync( - zeroExArtifacts.FantomBridgeAdapter, - env.provider, - env.txDefaults, - zeroExArtifacts, - constants.NULL_ADDRESS, - ); - }); - it('sell sources', async () => { - const sellSources = SELL_SOURCE_FILTER_BY_CHAIN_ID[ChainId.Fantom].exclude([ - ERC20BridgeSource.Native, - ERC20BridgeSource.MultiHop, - ]).sources; - return Promise.all( - sellSources.map(async source => { - const isSupported = await adapter - .isSupportedSource(getErc20BridgeSourceToBridgeSource(source)) - .callAsync(); - expect(isSupported, `${source} is not supported`).to.be.true(); - }), - ); - }); - it('buy sources', async () => { - const buySources = BUY_SOURCE_FILTER_BY_CHAIN_ID[ChainId.Fantom].exclude([ - ERC20BridgeSource.Native, - ERC20BridgeSource.MultiHop, - ]).sources; - return Promise.all( - buySources.map(async source => { - const isSupported = await adapter - .isSupportedSource(getErc20BridgeSourceToBridgeSource(source)) - .callAsync(); - expect(isSupported, `${source} is not supported`).to.be.true(); - }), - ); - }); - }); - describe('Optimism', () => { - let adapter: OptimismBridgeAdapterContract; - before(async () => { - adapter = await OptimismBridgeAdapterContract.deployFrom0xArtifactAsync( - zeroExArtifacts.OptimismBridgeAdapter, - env.provider, - env.txDefaults, - zeroExArtifacts, - constants.NULL_ADDRESS, - ); - }); - it('sell sources', async () => { - const sellSources = SELL_SOURCE_FILTER_BY_CHAIN_ID[ChainId.Optimism].exclude([ - ERC20BridgeSource.Native, - ERC20BridgeSource.MultiHop, - ]).sources; - return Promise.all( - sellSources.map(async source => { - const isSupported = await adapter - .isSupportedSource(getErc20BridgeSourceToBridgeSource(source)) - .callAsync(); - expect(isSupported, `${source} is not supported`).to.be.true(); - }), - ); - }); - it('buy sources', async () => { - const buySources = BUY_SOURCE_FILTER_BY_CHAIN_ID[ChainId.Optimism].exclude([ - ERC20BridgeSource.Native, - ERC20BridgeSource.MultiHop, - ]).sources; - return Promise.all( - buySources.map(async source => { - const isSupported = await adapter - .isSupportedSource(getErc20BridgeSourceToBridgeSource(source)) - .callAsync(); - expect(isSupported, `${source} is not supported`).to.be.true(); - }), - ); - }); - }); - describe('Polygon', () => { - let adapter: PolygonBridgeAdapterContract; - before(async () => { - adapter = await PolygonBridgeAdapterContract.deployFrom0xArtifactAsync( - zeroExArtifacts.PolygonBridgeAdapter, - env.provider, - env.txDefaults, - zeroExArtifacts, - constants.NULL_ADDRESS, - ); - }); - it('sell sources', async () => { - const sellSources = SELL_SOURCE_FILTER_BY_CHAIN_ID[ChainId.Polygon].exclude([ - ERC20BridgeSource.Native, - ERC20BridgeSource.MultiHop, - ]).sources; - return Promise.all( - sellSources.map(async source => { - const isSupported = await adapter - .isSupportedSource(getErc20BridgeSourceToBridgeSource(source)) - .callAsync(); - expect(isSupported, `${source} is not supported`).to.be.true(); - }), - ); - }); - it('buy sources', async () => { - const buySources = BUY_SOURCE_FILTER_BY_CHAIN_ID[ChainId.Polygon].exclude([ - ERC20BridgeSource.Native, - ERC20BridgeSource.MultiHop, - ]).sources; - return Promise.all( - buySources.map(async source => { - const isSupported = await adapter - .isSupportedSource(getErc20BridgeSourceToBridgeSource(source)) - .callAsync(); - expect(isSupported, `${source} is not supported`).to.be.true(); - }), - ); - }); - }); -}); diff --git a/packages/asset-swapper/test/contracts/bridge_sampler_mainnet_test.ts b/packages/asset-swapper/test/contracts/bridge_sampler_mainnet_test.ts deleted file mode 100644 index 777ee8277c..0000000000 --- a/packages/asset-swapper/test/contracts/bridge_sampler_mainnet_test.ts +++ /dev/null @@ -1,80 +0,0 @@ -import { blockchainTests, describe, expect, toBaseUnitAmount, Web3ProviderEngine } from '@0x/contracts-test-utils'; -import { RPCSubprovider } from '@0x/subproviders'; -import { BigNumber, providerUtils } from '@0x/utils'; - -import { artifacts } from '../artifacts'; -import { ERC20BridgeSamplerContract } from '../wrappers'; - -export const VB = '0x6cc5f688a315f3dc28a7781717a9a798a59fda7b'; - -// tslint:disable: custom-no-magic-numbers - -blockchainTests.skip('Mainnet Sampler Tests', env => { - let testContract: ERC20BridgeSamplerContract; - const fakeSamplerAddress = '0x1111111111111111111111111111111111111111'; - const overrides = { - [fakeSamplerAddress]: { - code: artifacts.ERC20BridgeSampler.compilerOutput.evm.deployedBytecode.object, - }, - }; - before(async () => { - const provider = new Web3ProviderEngine(); - // tslint:disable-next-line:no-non-null-assertion - provider.addProvider(new RPCSubprovider(process.env.RPC_URL!)); - providerUtils.startProviderEngine(provider); - testContract = new ERC20BridgeSamplerContract(fakeSamplerAddress, provider, { - ...env.txDefaults, - from: VB, - }); - }); - describe('Curve', () => { - const CURVE_ADDRESS = '0x45f783cce6b7ff23b2ab2d70e416cdb7d6055f51'; - const DAI_TOKEN_INDEX = new BigNumber(0); - const USDC_TOKEN_INDEX = new BigNumber(1); - const CURVE_INFO = { - poolAddress: CURVE_ADDRESS, - sellQuoteFunctionSelector: '0x07211ef7', - buyQuoteFunctionSelector: '0x0e71d1b9', - }; - - describe('sampleSellsFromCurve()', () => { - it('samples sells from Curve DAI->USDC', async () => { - const samples = await testContract - .sampleSellsFromCurve(CURVE_INFO, DAI_TOKEN_INDEX, USDC_TOKEN_INDEX, [toBaseUnitAmount(1)]) - .callAsync({ overrides }); - expect(samples.length).to.be.bignumber.greaterThan(0); - expect(samples[0]).to.be.bignumber.greaterThan(0); - }); - - it('samples sells from Curve USDC->DAI', async () => { - const samples = await testContract - .sampleSellsFromCurve(CURVE_INFO, USDC_TOKEN_INDEX, DAI_TOKEN_INDEX, [toBaseUnitAmount(1, 6)]) - .callAsync({ overrides }); - expect(samples.length).to.be.bignumber.greaterThan(0); - expect(samples[0]).to.be.bignumber.greaterThan(0); - }); - }); - - describe('sampleBuysFromCurve()', () => { - it('samples buys from Curve DAI->USDC', async () => { - // From DAI to USDC - // I want to buy 1 USDC - const samples = await testContract - .sampleBuysFromCurve(CURVE_INFO, DAI_TOKEN_INDEX, USDC_TOKEN_INDEX, [toBaseUnitAmount(1, 6)]) - .callAsync({ overrides }); - expect(samples.length).to.be.bignumber.greaterThan(0); - expect(samples[0]).to.be.bignumber.greaterThan(0); - }); - - it('samples buys from Curve USDC->DAI', async () => { - // From USDC to DAI - // I want to buy 1 DAI - const samples = await testContract - .sampleBuysFromCurve(CURVE_INFO, USDC_TOKEN_INDEX, DAI_TOKEN_INDEX, [toBaseUnitAmount(1)]) - .callAsync({ overrides }); - expect(samples.length).to.be.bignumber.greaterThan(0); - expect(samples[0]).to.be.bignumber.greaterThan(0); - }); - }); - }); -}); diff --git a/packages/asset-swapper/test/contracts/erc20_bridge_sampler_test.ts b/packages/asset-swapper/test/contracts/erc20_bridge_sampler_test.ts deleted file mode 100644 index 8836e923e1..0000000000 --- a/packages/asset-swapper/test/contracts/erc20_bridge_sampler_test.ts +++ /dev/null @@ -1,983 +0,0 @@ -// import { -// blockchainTests, -// constants, -// expect, -// getRandomInteger, -// getRandomPortion, -// randomAddress, -// } from '@0x/contracts-test-utils'; -// import { SignatureType } from '@0x/protocol-utils'; -// import { BigNumber, hexUtils, NULL_BYTES } from '@0x/utils'; -// import * as _ from 'lodash'; -// -// import { FillQuoteTransformerOrderType, LimitOrderFields } from '../../src'; -// import { SamplerCallResult, SignedNativeOrder } from '../../src/types'; -// import { artifacts } from '../artifacts'; -// import { DummyLiquidityProviderContract, TestERC20BridgeSamplerContract } from '../wrappers'; -// -// // tslint:disable: custom-no-magic-numbers -// -// const { NULL_ADDRESS } = constants; -// // HACK(dorothy-zbornak): Disabled because these tests are flakey and all this logic is moving to -// // the sampler service anyway. -// blockchainTests.skip('erc20-bridge-sampler', env => { -// let testContract: TestERC20BridgeSamplerContract; -// const RATE_DENOMINATOR = constants.ONE_ETHER; -// const MIN_RATE = new BigNumber('0.01'); -// const MAX_RATE = new BigNumber('100'); -// const MIN_DECIMALS = 4; -// const MAX_DECIMALS = 20; -// const WETH_ADDRESS = '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2'; -// const KYBER_SALT = '0x0ff3ca9d46195c39f9a12afb74207b4970349fb3cfb1e459bbf170298d326bc7'; -// const UNISWAP_BASE_SALT = '0x1d6a6a0506b0b4a554b907a4c29d9f4674e461989d9c1921feb17b26716385ab'; -// const UNISWAP_V2_SALT = '0xadc7fcb33c735913b8635927e66896b356a53a912ab2ceff929e60a04b53b3c1'; -// const INVALID_TOKEN_PAIR_ERROR = 'ERC20BridgeSampler/INVALID_TOKEN_PAIR'; -// const MAKER_TOKEN = randomAddress(); -// const TAKER_TOKEN = randomAddress(); -// const INTERMEDIATE_TOKEN = randomAddress(); -// const KYBER_RESERVE_OFFSET = new BigNumber(0); -// let KYBER_ADDRESS = ''; -// let UNISWAP_ADDRESS = ''; -// let UNISWAP_V2_ROUTER = ''; -// -// before(async () => { -// testContract = await TestERC20BridgeSamplerContract.deployFrom0xArtifactAsync( -// artifacts.TestERC20BridgeSampler, -// env.provider, -// { ...env.txDefaults, gas: 100e6 }, -// {}, -// ); -// UNISWAP_V2_ROUTER = await testContract.uniswapV2Router().callAsync(); -// KYBER_ADDRESS = await testContract.kyber().callAsync(); -// UNISWAP_ADDRESS = await testContract.uniswap().callAsync(); -// }); -// -// function getPackedHash(...args: string[]): string { -// return hexUtils.hash(hexUtils.concat(...args.map(a => hexUtils.toHex(a)))); -// } -// -// function getUniswapExchangeSalt(tokenAddress: string): string { -// return getPackedHash(UNISWAP_BASE_SALT, tokenAddress); -// } -// -// function getDeterministicRate(salt: string, sellToken: string, buyToken: string): BigNumber { -// const hash = getPackedHash(salt, sellToken, buyToken); -// const _minRate = RATE_DENOMINATOR.times(MIN_RATE); -// const _maxRate = RATE_DENOMINATOR.times(MAX_RATE); -// return new BigNumber(hash) -// .mod(_maxRate.minus(_minRate)) -// .plus(_minRate) -// .div(RATE_DENOMINATOR); -// } -// -// function getDeterministicTokenDecimals(token: string): number { -// if (token === WETH_ADDRESS) { -// return 18; -// } -// // HACK(dorothy-zbornak): Linter will complain about the addition not being -// // between two numbers, even though they are. -// // tslint:disable-next-line restrict-plus-operands -// return new BigNumber(getPackedHash(token)).mod(MAX_DECIMALS - MIN_DECIMALS).toNumber() + MIN_DECIMALS; -// } -// -// function getDeterministicSellQuote( -// salt: string, -// sellToken: string, -// buyToken: string, -// sellAmount: BigNumber, -// ): BigNumber { -// const sellBase = new BigNumber(10).pow(getDeterministicTokenDecimals(sellToken)); -// const buyBase = new BigNumber(10).pow(getDeterministicTokenDecimals(buyToken)); -// const rate = getDeterministicRate(salt, sellToken, buyToken); -// return sellAmount -// .times(rate) -// .times(buyBase) -// .dividedToIntegerBy(sellBase); -// } -// -// function getDeterministicBuyQuote( -// salt: string, -// sellToken: string, -// buyToken: string, -// buyAmount: BigNumber, -// ): BigNumber { -// const sellBase = new BigNumber(10).pow(getDeterministicTokenDecimals(sellToken)); -// const buyBase = new BigNumber(10).pow(getDeterministicTokenDecimals(buyToken)); -// const rate = getDeterministicRate(salt, sellToken, buyToken); -// return buyAmount -// .times(sellBase) -// .dividedToIntegerBy(rate) -// .dividedToIntegerBy(buyBase); -// } -// -// function areAddressesEqual(a: string, b: string): boolean { -// return a.toLowerCase() === b.toLowerCase(); -// } -// -// function getDeterministicUniswapSellQuote(sellToken: string, buyToken: string, sellAmount: BigNumber): BigNumber { -// if (areAddressesEqual(buyToken, WETH_ADDRESS)) { -// return getDeterministicSellQuote(getUniswapExchangeSalt(sellToken), sellToken, WETH_ADDRESS, sellAmount); -// } -// if (areAddressesEqual(sellToken, WETH_ADDRESS)) { -// return getDeterministicSellQuote(getUniswapExchangeSalt(buyToken), buyToken, WETH_ADDRESS, sellAmount); -// } -// const ethBought = getDeterministicSellQuote( -// getUniswapExchangeSalt(sellToken), -// sellToken, -// WETH_ADDRESS, -// sellAmount, -// ); -// return getDeterministicSellQuote(getUniswapExchangeSalt(buyToken), buyToken, WETH_ADDRESS, ethBought); -// } -// -// function getDeterministicUniswapBuyQuote(sellToken: string, buyToken: string, buyAmount: BigNumber): BigNumber { -// if (areAddressesEqual(buyToken, WETH_ADDRESS)) { -// return getDeterministicBuyQuote(getUniswapExchangeSalt(sellToken), WETH_ADDRESS, sellToken, buyAmount); -// } -// if (areAddressesEqual(sellToken, WETH_ADDRESS)) { -// return getDeterministicBuyQuote(getUniswapExchangeSalt(buyToken), WETH_ADDRESS, buyToken, buyAmount); -// } -// const ethSold = getDeterministicBuyQuote(getUniswapExchangeSalt(buyToken), WETH_ADDRESS, buyToken, buyAmount); -// return getDeterministicBuyQuote(getUniswapExchangeSalt(sellToken), WETH_ADDRESS, sellToken, ethSold); -// } -// -// function getDeterministicSellQuotes( -// sellToken: string, -// buyToken: string, -// sources: string[], -// sampleAmounts: BigNumber[], -// ): BigNumber[][] { -// const quotes: BigNumber[][] = []; -// for (const source of sources) { -// const sampleOutputs = []; -// for (const amount of sampleAmounts) { -// if (source === 'Kyber') { -// sampleOutputs.push(getDeterministicSellQuote(KYBER_SALT, sellToken, buyToken, amount)); -// } else if (source === 'Uniswap') { -// sampleOutputs.push(getDeterministicUniswapSellQuote(sellToken, buyToken, amount)); -// } -// } -// quotes.push(sampleOutputs); -// } -// return quotes; -// } -// -// function getDeterministicBuyQuotes( -// sellToken: string, -// buyToken: string, -// sources: string[], -// sampleAmounts: BigNumber[], -// ): BigNumber[][] { -// const quotes: BigNumber[][] = []; -// for (const source of sources) { -// const sampleOutputs = []; -// for (const amount of sampleAmounts) { -// if (source === 'Kyber') { -// sampleOutputs.push(getDeterministicBuyQuote(KYBER_SALT, sellToken, buyToken, amount)); -// } else if (source === 'Uniswap') { -// sampleOutputs.push(getDeterministicUniswapBuyQuote(sellToken, buyToken, amount)); -// } -// } -// quotes.push(sampleOutputs); -// } -// return quotes; -// } -// -// function getDeterministicUniswapV2SellQuote(path: string[], sellAmount: BigNumber): BigNumber { -// let bought = sellAmount; -// for (let i = 0; i < path.length - 1; ++i) { -// bought = getDeterministicSellQuote(UNISWAP_V2_SALT, path[i], path[i + 1], bought); -// } -// return bought; -// } -// -// function getDeterministicUniswapV2BuyQuote(path: string[], buyAmount: BigNumber): BigNumber { -// let sold = buyAmount; -// for (let i = path.length - 1; i > 0; --i) { -// sold = getDeterministicBuyQuote(UNISWAP_V2_SALT, path[i - 1], path[i], sold); -// } -// return sold; -// } -// -// function getDeterministicFillableTakerAssetAmount(order: SignedNativeOrder): BigNumber { -// const hash = getPackedHash(hexUtils.leftPad(order.order.salt)); -// return new BigNumber(hash).mod(order.order.takerAmount); -// } -// -// function getDeterministicFillableMakerAssetAmount(order: SignedNativeOrder): BigNumber { -// const takerAmount = getDeterministicFillableTakerAssetAmount(order); -// return order.order.makerAmount -// .times(takerAmount) -// .div(order.order.takerAmount) -// .integerValue(BigNumber.ROUND_UP); -// } -// -// function getSampleAmounts(tokenAddress: string, count?: number): BigNumber[] { -// const tokenDecimals = getDeterministicTokenDecimals(tokenAddress); -// const _upperLimit = getRandomPortion(getRandomInteger(1000, 50000).times(10 ** tokenDecimals)); -// const _count = count || _.random(1, 16); -// const d = _upperLimit.div(_count); -// return _.times(_count, i => d.times((i + 1) / _count).integerValue()); -// } -// -// function createOrder(makerToken: string, takerToken: string): SignedNativeOrder { -// return { -// order: { -// chainId: 1337, -// verifyingContract: randomAddress(), -// maker: randomAddress(), -// taker: randomAddress(), -// pool: NULL_BYTES, -// sender: NULL_ADDRESS, -// feeRecipient: randomAddress(), -// makerAmount: getRandomInteger(1, 1e18), -// takerAmount: getRandomInteger(1, 1e18), -// takerTokenFeeAmount: getRandomInteger(1, 1e18), -// makerToken, -// takerToken, -// salt: new BigNumber(hexUtils.random()), -// expiry: getRandomInteger(0, 2 ** 32), -// }, -// signature: { v: 1, r: NULL_BYTES, s: NULL_BYTES, signatureType: SignatureType.EthSign }, -// type: FillQuoteTransformerOrderType.Limit, -// }; -// } -// -// function createOrders(makerToken: string, takerToken: string, count?: number): SignedNativeOrder[] { -// return _.times(count || _.random(1, 16), () => createOrder(makerToken, takerToken)); -// } -// -// async function enableFailTriggerAsync(): Promise { -// await testContract.enableFailTrigger().awaitTransactionSuccessAsync({ value: 1 }); -// } -// -// function expectQuotesWithinRange( -// quotes: BigNumber[], -// expectedQuotes: BigNumber[], -// maxSlippage: BigNumber | number, -// ): void { -// quotes.forEach((_q, i) => { -// // If we're within 1 base unit of a low decimal token -// // then that's as good as we're going to get (and slippage is "high") -// if ( -// expectedQuotes[i].isZero() || -// BigNumber.max(expectedQuotes[i], quotes[i]) -// .minus(BigNumber.min(expectedQuotes[i], quotes[i])) -// .eq(1) -// ) { -// return; -// } -// const slippage = quotes[i] -// .dividedBy(expectedQuotes[i]) -// .minus(1) -// .decimalPlaces(4); -// expect(slippage, `quote[${i}]: ${slippage} ${quotes[i]} ${expectedQuotes[i]}`).to.be.bignumber.gte(0); -// expect(slippage, `quote[${i}] ${slippage} ${quotes[i]} ${expectedQuotes[i]}`).to.be.bignumber.lte( -// new BigNumber(maxSlippage), -// ); -// }); -// } -// -// describe('getOrderFillableTakerAssetAmounts()', () => { -// it('returns the expected amount for each order', async () => { -// const orders = createOrders(MAKER_TOKEN, TAKER_TOKEN); -// const expected = orders.map(getDeterministicFillableTakerAssetAmount); -// const actual = await testContract -// .getLimitOrderFillableTakerAssetAmounts( -// // tslint:disable-next-line:no-unnecessary-type-assertion -// orders.map(o => o.order as LimitOrderFields), -// orders.map(o => o.signature), -// NULL_ADDRESS, -// ) -// .callAsync(); -// expect(actual).to.deep.eq(expected); -// }); -// -// it('returns empty for no orders', async () => { -// const actual = await testContract.getLimitOrderFillableTakerAssetAmounts([], [], NULL_ADDRESS).callAsync(); -// expect(actual).to.deep.eq([]); -// }); -// }); -// -// describe('getOrderFillableMakerAssetAmounts()', () => { -// it('returns the expected amount for each order', async () => { -// const orders = createOrders(MAKER_TOKEN, TAKER_TOKEN); -// const expected = orders.map(getDeterministicFillableMakerAssetAmount); -// const actual = await testContract -// .getLimitOrderFillableMakerAssetAmounts( -// // tslint:disable-next-line:no-unnecessary-type-assertion -// orders.map(o => o.order as LimitOrderFields), -// orders.map(o => o.signature), -// NULL_ADDRESS, -// ) -// .callAsync(); -// expect(actual).to.deep.eq(expected); -// }); -// -// it('returns empty for no orders', async () => { -// const actual = await testContract.getLimitOrderFillableMakerAssetAmounts([], [], NULL_ADDRESS).callAsync(); -// expect(actual).to.deep.eq([]); -// }); -// }); -// -// blockchainTests.resets('sampleSellsFromKyberNetwork()', () => { -// let kyberOpts = { -// hintHandler: NULL_ADDRESS, -// networkProxy: NULL_ADDRESS, -// weth: WETH_ADDRESS, -// reserveOffset: KYBER_RESERVE_OFFSET, -// hint: NULL_BYTES, -// }; -// before(async () => { -// await testContract.createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN]).awaitTransactionSuccessAsync(); -// kyberOpts = { -// ...kyberOpts, -// hintHandler: KYBER_ADDRESS, -// networkProxy: KYBER_ADDRESS, -// }; -// }); -// -// it('throws if tokens are the same', async () => { -// const tx = testContract.sampleSellsFromKyberNetwork(kyberOpts, MAKER_TOKEN, MAKER_TOKEN, []).callAsync(); -// return expect(tx).to.revertWith(INVALID_TOKEN_PAIR_ERROR); -// }); -// -// it('can return no quotes', async () => { -// const [, , quotes] = await testContract -// .sampleSellsFromKyberNetwork(kyberOpts, TAKER_TOKEN, MAKER_TOKEN, []) -// .callAsync(); -// expect(quotes).to.deep.eq([]); -// }); -// -// it('returns zero if token -> token fails', async () => { -// const sampleAmounts = getSampleAmounts(TAKER_TOKEN); -// const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT); -// await enableFailTriggerAsync(); -// const [, , quotes] = await testContract -// .sampleSellsFromKyberNetwork(kyberOpts, TAKER_TOKEN, MAKER_TOKEN, sampleAmounts) -// .callAsync(); -// expect(quotes).to.deep.eq(expectedQuotes); -// }); -// -// it('can quote token -> ETH', async () => { -// const sampleAmounts = getSampleAmounts(TAKER_TOKEN); -// const [expectedQuotes] = getDeterministicSellQuotes(TAKER_TOKEN, WETH_ADDRESS, ['Kyber'], sampleAmounts); -// const [, , quotes] = await testContract -// .sampleSellsFromKyberNetwork(kyberOpts, TAKER_TOKEN, WETH_ADDRESS, sampleAmounts) -// .callAsync(); -// expect(quotes).to.deep.eq(expectedQuotes); -// }); -// -// it('can quote token -> token', async () => { -// const sampleAmounts = getSampleAmounts(TAKER_TOKEN); -// const [expectedQuotes] = getDeterministicSellQuotes(TAKER_TOKEN, MAKER_TOKEN, ['Kyber'], sampleAmounts); -// const [, , quotes] = await testContract -// .sampleSellsFromKyberNetwork(kyberOpts, TAKER_TOKEN, MAKER_TOKEN, sampleAmounts) -// .callAsync(); -// expect(quotes).to.deep.eq(expectedQuotes); -// }); -// -// it('returns zero if token -> ETH fails', async () => { -// const sampleAmounts = getSampleAmounts(TAKER_TOKEN); -// const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT); -// await enableFailTriggerAsync(); -// const [, , quotes] = await testContract -// .sampleSellsFromKyberNetwork(kyberOpts, TAKER_TOKEN, WETH_ADDRESS, sampleAmounts) -// .callAsync(); -// expect(quotes).to.deep.eq(expectedQuotes); -// }); -// -// it('can quote ETH -> token', async () => { -// const sampleAmounts = getSampleAmounts(TAKER_TOKEN); -// const [expectedQuotes] = getDeterministicSellQuotes(WETH_ADDRESS, TAKER_TOKEN, ['Kyber'], sampleAmounts); -// const [, , quotes] = await testContract -// .sampleSellsFromKyberNetwork(kyberOpts, WETH_ADDRESS, TAKER_TOKEN, sampleAmounts) -// .callAsync(); -// expect(quotes).to.deep.eq(expectedQuotes); -// }); -// -// it('returns zero if ETH -> token fails', async () => { -// const sampleAmounts = getSampleAmounts(TAKER_TOKEN); -// const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT); -// await enableFailTriggerAsync(); -// const [, , quotes] = await testContract -// .sampleSellsFromKyberNetwork(kyberOpts, WETH_ADDRESS, TAKER_TOKEN, sampleAmounts) -// .callAsync(); -// expect(quotes).to.deep.eq(expectedQuotes); -// }); -// }); -// -// blockchainTests.resets('sampleBuysFromKyberNetwork()', () => { -// let kyberOpts = { -// hintHandler: NULL_ADDRESS, -// networkProxy: NULL_ADDRESS, -// weth: WETH_ADDRESS, -// reserveOffset: KYBER_RESERVE_OFFSET, -// hint: NULL_BYTES, -// }; -// const ACCEPTABLE_SLIPPAGE = 0.0005; -// before(async () => { -// await testContract.createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN]).awaitTransactionSuccessAsync(); -// kyberOpts = { -// ...kyberOpts, -// hintHandler: KYBER_ADDRESS, -// networkProxy: KYBER_ADDRESS, -// }; -// }); -// -// it('throws if tokens are the same', async () => { -// const tx = testContract.sampleBuysFromKyberNetwork(kyberOpts, MAKER_TOKEN, MAKER_TOKEN, []).callAsync(); -// return expect(tx).to.revertWith(INVALID_TOKEN_PAIR_ERROR); -// }); -// -// it('can return no quotes', async () => { -// const [, , quotes] = await testContract -// .sampleBuysFromKyberNetwork(kyberOpts, TAKER_TOKEN, MAKER_TOKEN, []) -// .callAsync(); -// expect(quotes).to.deep.eq([]); -// }); -// -// it('can quote token -> token', async () => { -// const sampleAmounts = getSampleAmounts(TAKER_TOKEN); -// const [expectedQuotes] = getDeterministicBuyQuotes(TAKER_TOKEN, MAKER_TOKEN, ['Kyber'], sampleAmounts); -// const [, , quotes] = await testContract -// .sampleBuysFromKyberNetwork(kyberOpts, TAKER_TOKEN, MAKER_TOKEN, sampleAmounts) -// .callAsync(); -// expectQuotesWithinRange(quotes, expectedQuotes, ACCEPTABLE_SLIPPAGE); -// }); -// -// it('returns zero if token -> token fails', async () => { -// const sampleAmounts = getSampleAmounts(TAKER_TOKEN); -// const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT); -// await enableFailTriggerAsync(); -// const [, , quotes] = await testContract -// .sampleBuysFromKyberNetwork(kyberOpts, TAKER_TOKEN, MAKER_TOKEN, sampleAmounts) -// .callAsync(); -// expect(quotes).to.deep.eq(expectedQuotes); -// }); -// -// it('can quote token -> ETH', async () => { -// const sampleAmounts = getSampleAmounts(TAKER_TOKEN); -// const [expectedQuotes] = getDeterministicBuyQuotes(TAKER_TOKEN, WETH_ADDRESS, ['Kyber'], sampleAmounts); -// const [, , quotes] = await testContract -// .sampleBuysFromKyberNetwork(kyberOpts, TAKER_TOKEN, WETH_ADDRESS, sampleAmounts) -// .callAsync(); -// expectQuotesWithinRange(quotes, expectedQuotes, ACCEPTABLE_SLIPPAGE); -// }); -// -// it('returns zero if token -> ETH fails', async () => { -// const sampleAmounts = getSampleAmounts(TAKER_TOKEN); -// const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT); -// await enableFailTriggerAsync(); -// const [, , quotes] = await testContract -// .sampleBuysFromKyberNetwork(kyberOpts, TAKER_TOKEN, WETH_ADDRESS, sampleAmounts) -// .callAsync(); -// expect(quotes).to.deep.eq(expectedQuotes); -// }); -// -// it('can quote ETH -> token', async () => { -// const sampleAmounts = getSampleAmounts(TAKER_TOKEN); -// const [expectedQuotes] = getDeterministicBuyQuotes(WETH_ADDRESS, TAKER_TOKEN, ['Kyber'], sampleAmounts); -// const [, , quotes] = await testContract -// .sampleBuysFromKyberNetwork(kyberOpts, WETH_ADDRESS, TAKER_TOKEN, sampleAmounts) -// .callAsync(); -// expectQuotesWithinRange(quotes, expectedQuotes, ACCEPTABLE_SLIPPAGE); -// }); -// -// it('returns zero if ETH -> token fails', async () => { -// const sampleAmounts = getSampleAmounts(TAKER_TOKEN); -// const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT); -// await enableFailTriggerAsync(); -// const [, , quotes] = await testContract -// .sampleBuysFromKyberNetwork(kyberOpts, WETH_ADDRESS, TAKER_TOKEN, sampleAmounts) -// .callAsync(); -// expect(quotes).to.deep.eq(expectedQuotes); -// }); -// }); -// -// blockchainTests.resets('sampleSellsFromUniswap()', () => { -// const UNISWAP_ETH_ADDRESS = NULL_ADDRESS; -// before(async () => { -// await testContract.createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN]).awaitTransactionSuccessAsync(); -// }); -// -// it('throws if tokens are the same', async () => { -// const tx = testContract.sampleSellsFromUniswap(UNISWAP_ADDRESS, MAKER_TOKEN, MAKER_TOKEN, []).callAsync(); -// return expect(tx).to.revertWith(INVALID_TOKEN_PAIR_ERROR); -// }); -// -// it('can return no quotes', async () => { -// const quotes = await testContract -// .sampleSellsFromUniswap(UNISWAP_ADDRESS, TAKER_TOKEN, MAKER_TOKEN, []) -// .callAsync(); -// expect(quotes).to.deep.eq([]); -// }); -// -// it('can quote token -> token', async () => { -// const sampleAmounts = getSampleAmounts(TAKER_TOKEN); -// const [expectedQuotes] = getDeterministicSellQuotes(TAKER_TOKEN, MAKER_TOKEN, ['Uniswap'], sampleAmounts); -// const quotes = await testContract -// .sampleSellsFromUniswap(UNISWAP_ADDRESS, TAKER_TOKEN, MAKER_TOKEN, sampleAmounts) -// .callAsync(); -// expect(quotes).to.deep.eq(expectedQuotes); -// }); -// -// it('returns zero if token -> token fails', async () => { -// const sampleAmounts = getSampleAmounts(TAKER_TOKEN); -// const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT); -// await enableFailTriggerAsync(); -// const quotes = await testContract -// .sampleSellsFromUniswap(UNISWAP_ADDRESS, TAKER_TOKEN, MAKER_TOKEN, sampleAmounts) -// .callAsync(); -// expect(quotes).to.deep.eq(expectedQuotes); -// }); -// -// it('can quote token -> ETH', async () => { -// const sampleAmounts = getSampleAmounts(TAKER_TOKEN); -// const [expectedQuotes] = getDeterministicSellQuotes(TAKER_TOKEN, WETH_ADDRESS, ['Uniswap'], sampleAmounts); -// const quotes = await testContract -// .sampleSellsFromUniswap(UNISWAP_ADDRESS, TAKER_TOKEN, UNISWAP_ETH_ADDRESS, sampleAmounts) -// .callAsync(); -// expect(quotes).to.deep.eq(expectedQuotes); -// }); -// -// it('returns zero if token -> ETH fails', async () => { -// const sampleAmounts = getSampleAmounts(TAKER_TOKEN); -// const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT); -// await enableFailTriggerAsync(); -// const quotes = await testContract -// .sampleSellsFromUniswap(UNISWAP_ADDRESS, TAKER_TOKEN, UNISWAP_ETH_ADDRESS, sampleAmounts) -// .callAsync(); -// expect(quotes).to.deep.eq(expectedQuotes); -// }); -// -// it('can quote ETH -> token', async () => { -// const sampleAmounts = getSampleAmounts(TAKER_TOKEN); -// const [expectedQuotes] = getDeterministicSellQuotes(WETH_ADDRESS, TAKER_TOKEN, ['Uniswap'], sampleAmounts); -// const quotes = await testContract -// .sampleSellsFromUniswap(UNISWAP_ADDRESS, UNISWAP_ETH_ADDRESS, TAKER_TOKEN, sampleAmounts) -// .callAsync(); -// expect(quotes).to.deep.eq(expectedQuotes); -// }); -// -// it('returns zero if ETH -> token fails', async () => { -// const sampleAmounts = getSampleAmounts(TAKER_TOKEN); -// const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT); -// await enableFailTriggerAsync(); -// const quotes = await testContract -// .sampleSellsFromUniswap(UNISWAP_ADDRESS, UNISWAP_ETH_ADDRESS, TAKER_TOKEN, sampleAmounts) -// .callAsync(); -// expect(quotes).to.deep.eq(expectedQuotes); -// }); -// -// it('returns zero if no exchange exists for the maker token', async () => { -// const nonExistantToken = randomAddress(); -// const sampleAmounts = getSampleAmounts(TAKER_TOKEN); -// const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT); -// const quotes = await testContract -// .sampleSellsFromUniswap(UNISWAP_ADDRESS, TAKER_TOKEN, nonExistantToken, sampleAmounts) -// .callAsync(); -// expect(quotes).to.deep.eq(expectedQuotes); -// }); -// -// it('returns zero if no exchange exists for the taker token', async () => { -// const nonExistantToken = randomAddress(); -// const sampleAmounts = getSampleAmounts(nonExistantToken); -// const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT); -// const quotes = await testContract -// .sampleSellsFromUniswap(UNISWAP_ADDRESS, nonExistantToken, MAKER_TOKEN, sampleAmounts) -// .callAsync(); -// expect(quotes).to.deep.eq(expectedQuotes); -// }); -// }); -// -// blockchainTests.resets('sampleBuysFromUniswap()', () => { -// const UNISWAP_ETH_ADDRESS = NULL_ADDRESS; -// before(async () => { -// await testContract.createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN]).awaitTransactionSuccessAsync(); -// }); -// -// it('throws if tokens are the same', async () => { -// const tx = testContract.sampleBuysFromUniswap(UNISWAP_ADDRESS, MAKER_TOKEN, MAKER_TOKEN, []).callAsync(); -// return expect(tx).to.revertWith(INVALID_TOKEN_PAIR_ERROR); -// }); -// -// it('can return no quotes', async () => { -// const quotes = await testContract -// .sampleBuysFromUniswap(UNISWAP_ADDRESS, TAKER_TOKEN, MAKER_TOKEN, []) -// .callAsync(); -// expect(quotes).to.deep.eq([]); -// }); -// -// it('can quote token -> token', async () => { -// const sampleAmounts = getSampleAmounts(MAKER_TOKEN); -// const [expectedQuotes] = getDeterministicBuyQuotes(TAKER_TOKEN, MAKER_TOKEN, ['Uniswap'], sampleAmounts); -// const quotes = await testContract -// .sampleBuysFromUniswap(UNISWAP_ADDRESS, TAKER_TOKEN, MAKER_TOKEN, sampleAmounts) -// .callAsync(); -// expect(quotes).to.deep.eq(expectedQuotes); -// }); -// -// it('returns zero if token -> token fails', async () => { -// const sampleAmounts = getSampleAmounts(MAKER_TOKEN); -// const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT); -// await enableFailTriggerAsync(); -// const quotes = await testContract -// .sampleBuysFromUniswap(UNISWAP_ADDRESS, TAKER_TOKEN, MAKER_TOKEN, sampleAmounts) -// .callAsync(); -// expect(quotes).to.deep.eq(expectedQuotes); -// }); -// -// it('can quote token -> ETH', async () => { -// const sampleAmounts = getSampleAmounts(MAKER_TOKEN); -// const [expectedQuotes] = getDeterministicBuyQuotes(TAKER_TOKEN, WETH_ADDRESS, ['Uniswap'], sampleAmounts); -// const quotes = await testContract -// .sampleBuysFromUniswap(UNISWAP_ADDRESS, TAKER_TOKEN, UNISWAP_ETH_ADDRESS, sampleAmounts) -// .callAsync(); -// expect(quotes).to.deep.eq(expectedQuotes); -// }); -// -// it('returns zero if token -> ETH fails', async () => { -// const sampleAmounts = getSampleAmounts(MAKER_TOKEN); -// const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT); -// await enableFailTriggerAsync(); -// const quotes = await testContract -// .sampleBuysFromUniswap(UNISWAP_ADDRESS, TAKER_TOKEN, UNISWAP_ETH_ADDRESS, sampleAmounts) -// .callAsync(); -// expect(quotes).to.deep.eq(expectedQuotes); -// }); -// -// it('can quote ETH -> token', async () => { -// const sampleAmounts = getSampleAmounts(MAKER_TOKEN); -// const [expectedQuotes] = getDeterministicBuyQuotes(WETH_ADDRESS, TAKER_TOKEN, ['Uniswap'], sampleAmounts); -// const quotes = await testContract -// .sampleBuysFromUniswap(UNISWAP_ADDRESS, UNISWAP_ETH_ADDRESS, TAKER_TOKEN, sampleAmounts) -// .callAsync(); -// expect(quotes).to.deep.eq(expectedQuotes); -// }); -// -// it('returns zero if ETH -> token fails', async () => { -// const sampleAmounts = getSampleAmounts(MAKER_TOKEN); -// const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT); -// await enableFailTriggerAsync(); -// const quotes = await testContract -// .sampleBuysFromUniswap(UNISWAP_ADDRESS, UNISWAP_ETH_ADDRESS, TAKER_TOKEN, sampleAmounts) -// .callAsync(); -// expect(quotes).to.deep.eq(expectedQuotes); -// }); -// -// it('returns zero if no exchange exists for the maker token', async () => { -// const nonExistantToken = randomAddress(); -// const sampleAmounts = getSampleAmounts(nonExistantToken); -// const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT); -// const quotes = await testContract -// .sampleBuysFromUniswap(UNISWAP_ADDRESS, TAKER_TOKEN, nonExistantToken, sampleAmounts) -// .callAsync(); -// expect(quotes).to.deep.eq(expectedQuotes); -// }); -// -// it('returns zero if no exchange exists for the taker token', async () => { -// const nonExistantToken = randomAddress(); -// const sampleAmounts = getSampleAmounts(MAKER_TOKEN); -// const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT); -// const quotes = await testContract -// .sampleBuysFromUniswap(UNISWAP_ADDRESS, nonExistantToken, MAKER_TOKEN, sampleAmounts) -// .callAsync(); -// expect(quotes).to.deep.eq(expectedQuotes); -// }); -// }); -// -// describe('liquidity provider', () => { -// const xAsset = randomAddress(); -// const yAsset = randomAddress(); -// const sampleAmounts = getSampleAmounts(yAsset); -// let liquidityProvider: DummyLiquidityProviderContract; -// -// before(async () => { -// liquidityProvider = await DummyLiquidityProviderContract.deployFrom0xArtifactAsync( -// artifacts.DummyLiquidityProvider, -// env.provider, -// env.txDefaults, -// {}, -// ); -// }); -// -// it('should be able to query sells from the liquidity provider', async () => { -// const quotes = await testContract -// .sampleSellsFromLiquidityProvider(liquidityProvider.address, yAsset, xAsset, sampleAmounts) -// .callAsync(); -// quotes.forEach((value, idx) => { -// expect(value).is.bignumber.eql(sampleAmounts[idx].minus(1)); -// }); -// }); -// -// it('should be able to query buys from the liquidity provider', async () => { -// const quotes = await testContract -// .sampleBuysFromLiquidityProvider(liquidityProvider.address, yAsset, xAsset, sampleAmounts) -// .callAsync(); -// quotes.forEach((value, idx) => { -// expect(value).is.bignumber.eql(sampleAmounts[idx].plus(1)); -// }); -// }); -// -// it('should just return zeros if the liquidity provider does not exist', async () => { -// const quotes = await testContract -// .sampleBuysFromLiquidityProvider(randomAddress(), yAsset, xAsset, sampleAmounts) -// .callAsync(); -// quotes.forEach(value => { -// expect(value).is.bignumber.eql(constants.ZERO_AMOUNT); -// }); -// }); -// }); -// -// blockchainTests.resets('sampleSellsFromUniswapV2()', () => { -// function predictSellQuotes(path: string[], sellAmounts: BigNumber[]): BigNumber[] { -// return sellAmounts.map(a => getDeterministicUniswapV2SellQuote(path, a)); -// } -// -// it('can return no quotes', async () => { -// const quotes = await testContract -// .sampleSellsFromUniswapV2(UNISWAP_V2_ROUTER, [TAKER_TOKEN, MAKER_TOKEN], []) -// .callAsync(); -// expect(quotes).to.deep.eq([]); -// }); -// -// it('can quote token -> token', async () => { -// const sampleAmounts = getSampleAmounts(TAKER_TOKEN); -// const expectedQuotes = predictSellQuotes([TAKER_TOKEN, MAKER_TOKEN], sampleAmounts); -// const quotes = await testContract -// .sampleSellsFromUniswapV2(UNISWAP_V2_ROUTER, [TAKER_TOKEN, MAKER_TOKEN], sampleAmounts) -// .callAsync(); -// expect(quotes).to.deep.eq(expectedQuotes); -// }); -// -// it('returns zero if token -> token fails', async () => { -// const sampleAmounts = getSampleAmounts(TAKER_TOKEN); -// const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT); -// await enableFailTriggerAsync(); -// const quotes = await testContract -// .sampleSellsFromUniswapV2(UNISWAP_V2_ROUTER, [TAKER_TOKEN, MAKER_TOKEN], sampleAmounts) -// .callAsync(); -// expect(quotes).to.deep.eq(expectedQuotes); -// }); -// -// it('can quote token -> token -> token', async () => { -// const intermediateToken = randomAddress(); -// const sampleAmounts = getSampleAmounts(TAKER_TOKEN); -// const expectedQuotes = predictSellQuotes([TAKER_TOKEN, intermediateToken, MAKER_TOKEN], sampleAmounts); -// const quotes = await testContract -// .sampleSellsFromUniswapV2( -// UNISWAP_V2_ROUTER, -// [TAKER_TOKEN, intermediateToken, MAKER_TOKEN], -// sampleAmounts, -// ) -// .callAsync(); -// expect(quotes).to.deep.eq(expectedQuotes); -// }); -// }); -// -// blockchainTests.resets('sampleBuysFromUniswapV2()', () => { -// function predictBuyQuotes(path: string[], buyAmounts: BigNumber[]): BigNumber[] { -// return buyAmounts.map(a => getDeterministicUniswapV2BuyQuote(path, a)); -// } -// -// it('can return no quotes', async () => { -// const quotes = await testContract -// .sampleBuysFromUniswapV2(UNISWAP_V2_ROUTER, [TAKER_TOKEN, MAKER_TOKEN], []) -// .callAsync(); -// expect(quotes).to.deep.eq([]); -// }); -// -// it('can quote token -> token', async () => { -// const sampleAmounts = getSampleAmounts(MAKER_TOKEN); -// const expectedQuotes = predictBuyQuotes([TAKER_TOKEN, MAKER_TOKEN], sampleAmounts); -// const quotes = await testContract -// .sampleBuysFromUniswapV2(UNISWAP_V2_ROUTER, [TAKER_TOKEN, MAKER_TOKEN], sampleAmounts) -// .callAsync(); -// expect(quotes).to.deep.eq(expectedQuotes); -// }); -// -// it('returns zero if token -> token fails', async () => { -// const sampleAmounts = getSampleAmounts(MAKER_TOKEN); -// const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT); -// await enableFailTriggerAsync(); -// const quotes = await testContract -// .sampleBuysFromUniswapV2(UNISWAP_V2_ROUTER, [TAKER_TOKEN, MAKER_TOKEN], sampleAmounts) -// .callAsync(); -// expect(quotes).to.deep.eq(expectedQuotes); -// }); -// -// it('can quote token -> token -> token', async () => { -// const intermediateToken = randomAddress(); -// const sampleAmounts = getSampleAmounts(MAKER_TOKEN); -// const expectedQuotes = predictBuyQuotes([TAKER_TOKEN, intermediateToken, MAKER_TOKEN], sampleAmounts); -// const quotes = await testContract -// .sampleBuysFromUniswapV2( -// UNISWAP_V2_ROUTER, -// [TAKER_TOKEN, intermediateToken, MAKER_TOKEN], -// sampleAmounts, -// ) -// .callAsync(); -// expect(quotes).to.deep.eq(expectedQuotes); -// }); -// }); -// -// describe('batchCall()', () => { -// it('can call one function', async () => { -// const orders = createOrders(MAKER_TOKEN, TAKER_TOKEN); -// const expected = orders.map(getDeterministicFillableTakerAssetAmount); -// const calls = [ -// testContract -// .getLimitOrderFillableTakerAssetAmounts( -// // tslint:disable-next-line:no-unnecessary-type-assertion -// orders.map(o => o.order as LimitOrderFields), -// orders.map(o => o.signature), -// NULL_ADDRESS, -// ) -// .getABIEncodedTransactionData(), -// ]; -// const r = await testContract.batchCall(calls).callAsync(); -// expect(r).to.be.length(1); -// const actual = testContract.getABIDecodedReturnData( -// 'getLimitOrderFillableTakerAssetAmounts', -// r[0].data, -// ); -// expect(actual).to.deep.eq(expected); -// }); -// -// it('can call two functions', async () => { -// const numOrders = _.random(1, 10); -// const orders = _.times(2, () => createOrders(MAKER_TOKEN, TAKER_TOKEN, numOrders)); -// const expecteds = [ -// orders[0].map(getDeterministicFillableTakerAssetAmount), -// orders[1].map(getDeterministicFillableMakerAssetAmount), -// ]; -// const calls = [ -// testContract -// .getLimitOrderFillableTakerAssetAmounts( -// // tslint:disable-next-line:no-unnecessary-type-assertion -// orders[0].map(o => o.order as LimitOrderFields), -// orders[0].map(o => o.signature), -// NULL_ADDRESS, -// ) -// .getABIEncodedTransactionData(), -// testContract -// .getLimitOrderFillableMakerAssetAmounts( -// // tslint:disable-next-line:no-unnecessary-type-assertion -// orders[1].map(o => o.order as LimitOrderFields), -// orders[1].map(o => o.signature), -// NULL_ADDRESS, -// ) -// .getABIEncodedTransactionData(), -// ]; -// const r = await testContract.batchCall(calls).callAsync(); -// expect(r).to.be.length(2); -// expect( -// testContract.getABIDecodedReturnData('getLimitOrderFillableTakerAssetAmounts', r[0].data), -// ).to.deep.eq(expecteds[0]); -// expect( -// testContract.getABIDecodedReturnData('getLimitOrderFillableMakerAssetAmounts', r[1].data), -// ).to.deep.eq(expecteds[1]); -// }); -// -// it('can make recursive calls', async () => { -// const numOrders = _.random(1, 10); -// const orders = createOrders(MAKER_TOKEN, TAKER_TOKEN, numOrders); -// const expected = orders.map(getDeterministicFillableTakerAssetAmount); -// let r = await testContract -// .batchCall([ -// testContract -// .batchCall([ -// testContract -// .getLimitOrderFillableTakerAssetAmounts( -// // tslint:disable-next-line:no-unnecessary-type-assertion -// orders.map(o => o.order as LimitOrderFields), -// orders.map(o => o.signature), -// NULL_ADDRESS, -// ) -// .getABIEncodedTransactionData(), -// ]) -// .getABIEncodedTransactionData(), -// ]) -// .callAsync(); -// expect(r).to.be.length(1); -// r = testContract.getABIDecodedReturnData('batchCall', r[0].data); -// expect(r).to.be.length(1); -// expect( -// testContract.getABIDecodedReturnData('getLimitOrderFillableTakerAssetAmounts', r[0].data), -// ).to.deep.eq(expected); -// }); -// }); -// -// blockchainTests.resets('TwoHopSampler', () => { -// before(async () => { -// await testContract -// .createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN, INTERMEDIATE_TOKEN]) -// .awaitTransactionSuccessAsync(); -// }); -// -// it('sampleTwoHopSell', async () => { -// // tslint:disable-next-line no-unnecessary-type-assertion -// const sellAmount = _.last(getSampleAmounts(TAKER_TOKEN))!; -// const uniswapV2FirstHopPath = [TAKER_TOKEN, INTERMEDIATE_TOKEN]; -// const uniswapV2FirstHop = testContract -// .sampleSellsFromUniswapV2(UNISWAP_V2_ROUTER, uniswapV2FirstHopPath, [constants.ZERO_AMOUNT]) -// .getABIEncodedTransactionData(); -// -// const uniswapV2SecondHopPath = [INTERMEDIATE_TOKEN, randomAddress(), MAKER_TOKEN]; -// const uniswapV2SecondHop = testContract -// .sampleSellsFromUniswapV2(UNISWAP_V2_ROUTER, uniswapV2SecondHopPath, [constants.ZERO_AMOUNT]) -// .getABIEncodedTransactionData(); -// -// const firstHopQuotes = [getDeterministicUniswapV2SellQuote(uniswapV2FirstHopPath, sellAmount)]; -// const expectedIntermediateAssetAmount = BigNumber.max(...firstHopQuotes); -// const secondHopQuotes = [ -// getDeterministicUniswapV2SellQuote(uniswapV2SecondHopPath, expectedIntermediateAssetAmount), -// ]; -// const expectedBuyAmount = BigNumber.max(...secondHopQuotes); -// -// const [firstHop, secondHop, buyAmount] = await testContract -// .sampleTwoHopSell([uniswapV2FirstHop], [uniswapV2SecondHop], sellAmount) -// .callAsync(); -// expect(firstHop.sourceIndex, 'First hop source index').to.bignumber.equal( -// firstHopQuotes.findIndex(quote => quote.isEqualTo(expectedIntermediateAssetAmount)), -// ); -// expect(secondHop.sourceIndex, 'Second hop source index').to.bignumber.equal( -// secondHopQuotes.findIndex(quote => quote.isEqualTo(expectedBuyAmount)), -// ); -// expect(buyAmount, 'Two hop buy amount').to.bignumber.equal(expectedBuyAmount); -// }); -// it('sampleTwoHopBuy', async () => { -// // tslint:disable-next-line no-unnecessary-type-assertion -// const buyAmount = _.last(getSampleAmounts(MAKER_TOKEN))!; -// const uniswapV2FirstHopPath = [TAKER_TOKEN, INTERMEDIATE_TOKEN]; -// const uniswapV2FirstHop = testContract -// .sampleBuysFromUniswapV2(UNISWAP_V2_ROUTER, uniswapV2FirstHopPath, [constants.ZERO_AMOUNT]) -// .getABIEncodedTransactionData(); -// -// const uniswapV2SecondHopPath = [INTERMEDIATE_TOKEN, randomAddress(), MAKER_TOKEN]; -// const uniswapV2SecondHop = testContract -// .sampleBuysFromUniswapV2(UNISWAP_V2_ROUTER, uniswapV2SecondHopPath, [constants.ZERO_AMOUNT]) -// .getABIEncodedTransactionData(); -// -// const secondHopQuotes = [getDeterministicUniswapV2BuyQuote(uniswapV2SecondHopPath, buyAmount)]; -// const expectedIntermediateAssetAmount = BigNumber.min(...secondHopQuotes); -// -// const firstHopQuotes = [ -// getDeterministicUniswapV2BuyQuote(uniswapV2FirstHopPath, expectedIntermediateAssetAmount), -// ]; -// const expectedSellAmount = BigNumber.min(...firstHopQuotes); -// -// const [firstHop, secondHop, sellAmount] = await testContract -// .sampleTwoHopBuy([uniswapV2FirstHop], [uniswapV2SecondHop], buyAmount) -// .callAsync(); -// expect(firstHop.sourceIndex, 'First hop source index').to.bignumber.equal( -// firstHopQuotes.findIndex(quote => quote.isEqualTo(expectedSellAmount)), -// ); -// expect(secondHop.sourceIndex, 'Second hop source index').to.bignumber.equal( -// secondHopQuotes.findIndex(quote => quote.isEqualTo(expectedIntermediateAssetAmount)), -// ); -// expect(sellAmount, 'Two hop sell amount').to.bignumber.equal(expectedSellAmount); -// }); -// }); -// }); diff --git a/packages/asset-swapper/test/contracts/native_order_sampler_test.ts b/packages/asset-swapper/test/contracts/native_order_sampler_test.ts deleted file mode 100644 index 1724edd118..0000000000 --- a/packages/asset-swapper/test/contracts/native_order_sampler_test.ts +++ /dev/null @@ -1,233 +0,0 @@ -import { artifacts as erc20Artifacts, DummyERC20TokenContract } from '@0x/contracts-erc20'; -import { - assertIntegerRoughlyEquals, - blockchainTests, - constants, - expect, - getRandomInteger, - randomAddress, -} from '@0x/contracts-test-utils'; -import { Web3Wrapper } from '@0x/dev-utils'; -import { SignatureType } from '@0x/protocol-utils'; -import { BigNumber, hexUtils } from '@0x/utils'; -import * as _ from 'lodash'; - -import { LimitOrderFields } from '../../src'; -import { NULL_ADDRESS } from '../../src/utils/market_operation_utils/constants'; -import { artifacts } from '../artifacts'; -import { TestNativeOrderSamplerContract } from '../wrappers'; - -const { NULL_BYTES, ZERO_AMOUNT } = constants; - -// tslint:disable: custom-no-magic-numbers - -blockchainTests.resets('NativeOrderSampler contract', env => { - let testContract: TestNativeOrderSamplerContract; - let makerToken: string; - let takerToken: string; - let makerAddress: string; - const VALID_SIGNATURE = { v: 1, r: '0x01', s: '0x01', signatureType: SignatureType.EthSign }; - - before(async () => { - testContract = await TestNativeOrderSamplerContract.deployFrom0xArtifactAsync( - artifacts.TestNativeOrderSampler, - env.provider, - env.txDefaults, - {}, - ); - const NUM_TOKENS = new BigNumber(3); - [makerToken, takerToken] = await testContract.createTokens(NUM_TOKENS).callAsync(); - [makerAddress] = await new Web3Wrapper(env.provider).getAvailableAddressesAsync(); - await testContract.createTokens(NUM_TOKENS).awaitTransactionSuccessAsync(); - }); - - function getPackedHash(...args: string[]): string { - return hexUtils.hash(hexUtils.concat(...args.map(a => hexUtils.toHex(a)))); - } - - interface OrderInfo { - orderHash: string; - orderStatus: number; - orderTakerAssetFilledAmount: BigNumber; - } - - function getOrderInfo(order: LimitOrderFields): OrderInfo { - const hash = getPackedHash(hexUtils.leftPad(order.salt)); - const orderStatus = order.salt.mod(255).eq(0) ? 3 : 5; - const filledAmount = order.expiry; - return { - orderStatus, - orderHash: hash, - orderTakerAssetFilledAmount: filledAmount, - }; - } - - function createFillableOrderSalt(): BigNumber { - return new BigNumber(hexUtils.concat(hexUtils.slice(hexUtils.random(), 0, -1), '0x01')); - } - - function createUnfillableOrderSalt(): BigNumber { - return new BigNumber(hexUtils.concat(hexUtils.slice(hexUtils.random(), 0, -1), '0xff')); - } - - function getLimitOrderFillableTakerAmount(order: LimitOrderFields): BigNumber { - return order.takerAmount.minus(getOrderInfo(order).orderTakerAssetFilledAmount); - } - - function createOrder( - fields: Partial = {}, - filledTakerAssetAmount: BigNumber = ZERO_AMOUNT, - ): LimitOrderFields { - return { - chainId: 1337, - verifyingContract: randomAddress(), - maker: makerAddress, - taker: NULL_ADDRESS, - pool: NULL_BYTES, - sender: NULL_ADDRESS, - feeRecipient: randomAddress(), - makerAmount: getRandomInteger(1, 1e18), - takerAmount: getRandomInteger(1, 1e18), - takerTokenFeeAmount: getRandomInteger(1, 1e18), - makerToken, - takerToken, - salt: createFillableOrderSalt(), - expiry: filledTakerAssetAmount, - ...fields, - }; - } - - async function fundMakerAsync( - order: LimitOrderFields, - balanceScaling: number = 1, - allowanceScaling: number = 1, - ): Promise { - const token = makerToken; - let amount = order.makerAmount; - amount = amount.times(getLimitOrderFillableTakerAmount(order).div(BigNumber.max(1, order.takerAmount))); - await testContract - .setTokenBalanceAndAllowance( - token, - order.maker, - testContract.address, - amount.times(balanceScaling).integerValue(), - amount.times(allowanceScaling).integerValue(), - ) - .awaitTransactionSuccessAsync(); - } - - describe('getTokenDecimals()', () => { - it('correctly returns the token decimals', async () => { - const newMakerToken = await DummyERC20TokenContract.deployFrom0xArtifactAsync( - erc20Artifacts.DummyERC20Token, - env.provider, - env.txDefaults, - artifacts, - constants.DUMMY_TOKEN_NAME, - constants.DUMMY_TOKEN_SYMBOL, - new BigNumber(18), - constants.DUMMY_TOKEN_TOTAL_SUPPLY, - ); - const newTakerToken = await DummyERC20TokenContract.deployFrom0xArtifactAsync( - erc20Artifacts.DummyERC20Token, - env.provider, - env.txDefaults, - artifacts, - constants.DUMMY_TOKEN_NAME, - constants.DUMMY_TOKEN_SYMBOL, - new BigNumber(6), - constants.DUMMY_TOKEN_TOTAL_SUPPLY, - ); - const [makerDecimals, takerDecimals] = await testContract - .getTokenDecimals([newMakerToken.address, newTakerToken.address]) - .callAsync(); - expect(makerDecimals.toString()).to.eql('18'); - expect(takerDecimals.toString()).to.eql('6'); - }); - }); - - describe('getLimitOrderFillableTakerAmount()', () => { - it('returns the full amount for a fully funded order', async () => { - const order = createOrder(); - await fundMakerAsync(order); - const expected = getLimitOrderFillableTakerAmount(order); - const actual = await testContract - .getLimitOrderFillableTakerAmount(order, VALID_SIGNATURE, testContract.address) - .callAsync(); - expect(actual).to.bignumber.eq(expected); - }); - - it('returns partial amount with insufficient maker asset balance', async () => { - const order = createOrder(); - const expected = getLimitOrderFillableTakerAmount(order) - .times(0.5) - .integerValue(BigNumber.ROUND_DOWN); - await fundMakerAsync(order, 0.5); - const actual = await testContract - .getLimitOrderFillableTakerAmount(order, VALID_SIGNATURE, testContract.address) - .callAsync(); - assertIntegerRoughlyEquals(actual, expected, 100); - }); - - it('returns partial amount with insufficient maker asset allowance', async () => { - const order = createOrder(); - const expected = getLimitOrderFillableTakerAmount(order) - .times(0.5) - .integerValue(BigNumber.ROUND_DOWN); - await fundMakerAsync(order, 1, 0.5); - const actual = await testContract - .getLimitOrderFillableTakerAmount(order, VALID_SIGNATURE, testContract.address) - .callAsync(); - assertIntegerRoughlyEquals(actual, expected, 100); - }); - - it('returns zero for an that is not fillable', async () => { - const order = { - ...createOrder(), - salt: createUnfillableOrderSalt(), - }; - await fundMakerAsync(order); - const fillableTakerAmount = await testContract - .getLimitOrderFillableTakerAmount(order, VALID_SIGNATURE, testContract.address) - .callAsync(); - expect(fillableTakerAmount).to.bignumber.eq(ZERO_AMOUNT); - }); - - it('returns zero for an order with zero maker asset amount', async () => { - const order = { - ...createOrder(), - makerAmount: ZERO_AMOUNT, - }; - await fundMakerAsync(order); - const fillableTakerAmount = await testContract - .getLimitOrderFillableTakerAmount(order, VALID_SIGNATURE, testContract.address) - .callAsync(); - expect(fillableTakerAmount).to.bignumber.eq(ZERO_AMOUNT); - }); - - it('returns zero for an order with zero taker asset amount', async () => { - const order = { - ...createOrder(), - takerAmount: ZERO_AMOUNT, - }; - await fundMakerAsync(order); - const fillableTakerAmount = await testContract - .getLimitOrderFillableTakerAmount(order, VALID_SIGNATURE, testContract.address) - .callAsync(); - expect(fillableTakerAmount).to.bignumber.eq(ZERO_AMOUNT); - }); - - it('returns zero for an order with an empty signature', async () => { - const order = createOrder(); - await fundMakerAsync(order); - const fillableTakerAmount = await testContract - .getLimitOrderFillableTakerAmount( - order, - { ...VALID_SIGNATURE, r: NULL_BYTES, s: NULL_BYTES }, - testContract.address, - ) - .callAsync(); - expect(fillableTakerAmount).to.bignumber.eq(ZERO_AMOUNT); - }); - }); -}); diff --git a/packages/asset-swapper/test/dex_sampler_test.ts b/packages/asset-swapper/test/dex_sampler_test.ts deleted file mode 100644 index f4adb7673f..0000000000 --- a/packages/asset-swapper/test/dex_sampler_test.ts +++ /dev/null @@ -1,533 +0,0 @@ -import { ChainId, getContractAddressesForChainOrThrow } from '@0x/contract-addresses'; -import { - constants, - expect, - getRandomFloat, - getRandomInteger, - randomAddress, - toBaseUnitAmount, -} from '@0x/contracts-test-utils'; -import { FillQuoteTransformerOrderType, LimitOrderFields, SignatureType } from '@0x/protocol-utils'; -import { BigNumber, hexUtils, NULL_ADDRESS } from '@0x/utils'; -import * as _ from 'lodash'; - -import { SignedOrder } from '../src/types'; -import { DexOrderSampler, getSampleAmounts } from '../src/utils/market_operation_utils/sampler'; -import { ERC20BridgeSource } from '../src/utils/market_operation_utils/types'; -import { TokenAdjacencyGraphBuilder } from '../src/utils/token_adjacency_graph'; - -import { MockSamplerContract } from './utils/mock_sampler_contract'; -import { generatePseudoRandomSalt } from './utils/utils'; - -const CHAIN_ID = 1; -const EMPTY_BYTES32 = '0x0000000000000000000000000000000000000000000000000000000000000000'; -// tslint:disable: custom-no-magic-numbers -describe('DexSampler tests', () => { - const MAKER_TOKEN = randomAddress(); - const TAKER_TOKEN = randomAddress(); - const chainId = ChainId.Mainnet; - - const wethAddress = getContractAddressesForChainOrThrow(CHAIN_ID).etherToken; - const exchangeProxyAddress = getContractAddressesForChainOrThrow(CHAIN_ID).exchangeProxy; - - const tokenAdjacencyGraph = new TokenAdjacencyGraphBuilder([wethAddress]).build(); - - describe('getSampleAmounts()', () => { - const FILL_AMOUNT = getRandomInteger(1, 1e18); - const NUM_SAMPLES = 16; - - it('generates the correct number of amounts', () => { - const amounts = getSampleAmounts(FILL_AMOUNT, NUM_SAMPLES); - expect(amounts).to.be.length(NUM_SAMPLES); - }); - - it('first amount is nonzero', () => { - const amounts = getSampleAmounts(FILL_AMOUNT, NUM_SAMPLES); - expect(amounts[0]).to.not.bignumber.eq(0); - }); - - it('last amount is the fill amount', () => { - const amounts = getSampleAmounts(FILL_AMOUNT, NUM_SAMPLES); - expect(amounts[NUM_SAMPLES - 1]).to.bignumber.eq(FILL_AMOUNT); - }); - - it('can generate a single amount', () => { - const amounts = getSampleAmounts(FILL_AMOUNT, 1); - expect(amounts).to.be.length(1); - expect(amounts[0]).to.bignumber.eq(FILL_AMOUNT); - }); - - it('generates ascending amounts', () => { - const amounts = getSampleAmounts(FILL_AMOUNT, NUM_SAMPLES); - for (const i of _.times(NUM_SAMPLES).slice(1)) { - const prev = amounts[i - 1]; - const amount = amounts[i]; - expect(prev).to.bignumber.lt(amount); - } - }); - }); - - function createOrder(overrides?: Partial): SignedOrder { - const o: SignedOrder = { - order: { - salt: generatePseudoRandomSalt(), - expiry: getRandomInteger(0, 2 ** 64), - makerToken: MAKER_TOKEN, - takerToken: TAKER_TOKEN, - makerAmount: getRandomInteger(1, 1e18), - takerAmount: getRandomInteger(1, 1e18), - takerTokenFeeAmount: constants.ZERO_AMOUNT, - chainId: CHAIN_ID, - pool: EMPTY_BYTES32, - feeRecipient: NULL_ADDRESS, - sender: NULL_ADDRESS, - maker: NULL_ADDRESS, - taker: NULL_ADDRESS, - verifyingContract: exchangeProxyAddress, - ...overrides, - }, - signature: { v: 1, r: hexUtils.random(), s: hexUtils.random(), signatureType: SignatureType.EthSign }, - type: FillQuoteTransformerOrderType.Limit, - }; - return o; - } - const ORDERS = _.times(4, () => createOrder()); - const SIMPLE_ORDERS = ORDERS.map(o => _.omit(o.order, ['chainId', 'verifyingContract'])); - - describe('operations', () => { - it('getLimitOrderFillableMakerAssetAmounts()', async () => { - const expectedFillableAmounts = ORDERS.map(() => getRandomInteger(0, 100e18)); - const sampler = new MockSamplerContract({ - getLimitOrderFillableMakerAssetAmounts: (orders, signatures) => { - expect(orders).to.deep.eq(SIMPLE_ORDERS); - expect(signatures).to.deep.eq(ORDERS.map(o => o.signature)); - return expectedFillableAmounts; - }, - }); - const dexOrderSampler = new DexOrderSampler( - chainId, - sampler, - undefined, - undefined, - undefined, - undefined, - async () => undefined, - ); - const [fillableAmounts] = await dexOrderSampler.executeAsync( - dexOrderSampler.getLimitOrderFillableMakerAmounts(ORDERS, exchangeProxyAddress), - ); - expect(fillableAmounts).to.deep.eq(expectedFillableAmounts); - }); - - it('getLimitOrderFillableTakerAssetAmounts()', async () => { - const expectedFillableAmounts = ORDERS.map(() => getRandomInteger(0, 100e18)); - const sampler = new MockSamplerContract({ - getLimitOrderFillableTakerAssetAmounts: (orders, signatures) => { - expect(orders).to.deep.eq(SIMPLE_ORDERS); - expect(signatures).to.deep.eq(ORDERS.map(o => o.signature)); - return expectedFillableAmounts; - }, - }); - const dexOrderSampler = new DexOrderSampler( - chainId, - sampler, - undefined, - undefined, - undefined, - undefined, - async () => undefined, - ); - const [fillableAmounts] = await dexOrderSampler.executeAsync( - dexOrderSampler.getLimitOrderFillableTakerAmounts(ORDERS, exchangeProxyAddress), - ); - expect(fillableAmounts).to.deep.eq(expectedFillableAmounts); - }); - - it('getLiquidityProviderSellQuotes()', async () => { - const expectedMakerToken = randomAddress(); - const expectedTakerToken = randomAddress(); - const poolAddress = randomAddress(); - const gasCost = 123; - const sampler = new MockSamplerContract({ - sampleSellsFromLiquidityProvider: (providerAddress, takerToken, makerToken, _fillAmounts) => { - expect(providerAddress).to.eq(poolAddress); - expect(takerToken).to.eq(expectedTakerToken); - expect(makerToken).to.eq(expectedMakerToken); - return [toBaseUnitAmount(1001)]; - }, - }); - const dexOrderSampler = new DexOrderSampler( - chainId, - sampler, - undefined, - undefined, - undefined, - { - [poolAddress]: { tokens: [expectedMakerToken, expectedTakerToken], gasCost }, - }, - async () => undefined, - ); - const [result] = await dexOrderSampler.executeAsync( - dexOrderSampler.getSellQuotes( - [ERC20BridgeSource.LiquidityProvider], - expectedMakerToken, - expectedTakerToken, - [toBaseUnitAmount(1000)], - ), - ); - expect(result).to.deep.equal([ - [ - { - source: 'LiquidityProvider', - output: toBaseUnitAmount(1001), - input: toBaseUnitAmount(1000), - fillData: { poolAddress, gasCost }, - }, - ], - ]); - }); - - it('getLiquidityProviderBuyQuotes()', async () => { - const expectedMakerToken = randomAddress(); - const expectedTakerToken = randomAddress(); - const poolAddress = randomAddress(); - const gasCost = 321; - const sampler = new MockSamplerContract({ - sampleBuysFromLiquidityProvider: (providerAddress, takerToken, makerToken, _fillAmounts) => { - expect(providerAddress).to.eq(poolAddress); - expect(takerToken).to.eq(expectedTakerToken); - expect(makerToken).to.eq(expectedMakerToken); - return [toBaseUnitAmount(999)]; - }, - }); - const dexOrderSampler = new DexOrderSampler( - chainId, - sampler, - undefined, - undefined, - undefined, - { - [poolAddress]: { tokens: [expectedMakerToken, expectedTakerToken], gasCost }, - }, - async () => undefined, - ); - const [result] = await dexOrderSampler.executeAsync( - dexOrderSampler.getBuyQuotes( - [ERC20BridgeSource.LiquidityProvider], - expectedMakerToken, - expectedTakerToken, - [toBaseUnitAmount(1000)], - ), - ); - expect(result).to.deep.equal([ - [ - { - source: 'LiquidityProvider', - output: toBaseUnitAmount(999), - input: toBaseUnitAmount(1000), - fillData: { poolAddress, gasCost }, - }, - ], - ]); - }); - - it('getUniswapSellQuotes()', async () => { - const expectedTakerToken = randomAddress(); - const expectedMakerToken = randomAddress(); - const expectedTakerFillAmounts = getSampleAmounts(new BigNumber(100e18), 10); - const expectedMakerFillAmounts = getSampleAmounts(new BigNumber(100e18), 10); - const sampler = new MockSamplerContract({ - sampleSellsFromUniswap: (_router, takerToken, makerToken, fillAmounts) => { - expect(takerToken).to.eq(expectedTakerToken); - expect(makerToken).to.eq(expectedMakerToken); - expect(fillAmounts).to.deep.eq(expectedTakerFillAmounts); - return expectedMakerFillAmounts; - }, - }); - const dexOrderSampler = new DexOrderSampler( - chainId, - sampler, - undefined, - undefined, - undefined, - undefined, - async () => undefined, - ); - const [fillableAmounts] = await dexOrderSampler.executeAsync( - dexOrderSampler.getUniswapSellQuotes( - randomAddress(), - expectedMakerToken, - expectedTakerToken, - expectedTakerFillAmounts, - ), - ); - expect(fillableAmounts).to.deep.eq(expectedMakerFillAmounts); - }); - - it('getUniswapV2SellQuotes()', async () => { - const expectedTakerToken = randomAddress(); - const expectedMakerToken = randomAddress(); - const expectedTakerFillAmounts = getSampleAmounts(new BigNumber(100e18), 10); - const expectedMakerFillAmounts = getSampleAmounts(new BigNumber(100e18), 10); - const sampler = new MockSamplerContract({ - sampleSellsFromUniswapV2: (_router, path, fillAmounts) => { - expect(path).to.deep.eq([expectedMakerToken, expectedTakerToken]); - expect(fillAmounts).to.deep.eq(expectedTakerFillAmounts); - return expectedMakerFillAmounts; - }, - }); - const dexOrderSampler = new DexOrderSampler( - chainId, - sampler, - undefined, - undefined, - undefined, - undefined, - async () => undefined, - ); - const [fillableAmounts] = await dexOrderSampler.executeAsync( - dexOrderSampler.getUniswapV2SellQuotes( - NULL_ADDRESS, - [expectedMakerToken, expectedTakerToken], - expectedTakerFillAmounts, - ), - ); - expect(fillableAmounts).to.deep.eq(expectedMakerFillAmounts); - }); - - it('getUniswapBuyQuotes()', async () => { - const expectedTakerToken = randomAddress(); - const expectedMakerToken = randomAddress(); - const expectedTakerFillAmounts = getSampleAmounts(new BigNumber(100e18), 10); - const expectedMakerFillAmounts = getSampleAmounts(new BigNumber(100e18), 10); - const sampler = new MockSamplerContract({ - sampleBuysFromUniswap: (_router, takerToken, makerToken, fillAmounts) => { - expect(takerToken).to.eq(expectedTakerToken); - expect(makerToken).to.eq(expectedMakerToken); - expect(fillAmounts).to.deep.eq(expectedMakerFillAmounts); - return expectedTakerFillAmounts; - }, - }); - const dexOrderSampler = new DexOrderSampler( - chainId, - sampler, - undefined, - undefined, - undefined, - undefined, - async () => undefined, - ); - const [fillableAmounts] = await dexOrderSampler.executeAsync( - dexOrderSampler.getUniswapBuyQuotes( - randomAddress(), - expectedMakerToken, - expectedTakerToken, - expectedMakerFillAmounts, - ), - ); - expect(fillableAmounts).to.deep.eq(expectedTakerFillAmounts); - }); - - interface RatesBySource { - [src: string]: BigNumber; - } - - it('getSellQuotes()', async () => { - const expectedTakerToken = randomAddress(); - const expectedMakerToken = randomAddress(); - const sources = [ERC20BridgeSource.Uniswap, ERC20BridgeSource.UniswapV2]; - const ratesBySource: RatesBySource = { - [ERC20BridgeSource.Uniswap]: getRandomFloat(0, 100), - [ERC20BridgeSource.UniswapV2]: getRandomFloat(0, 100), - }; - const expectedTakerFillAmounts = getSampleAmounts(new BigNumber(100e18), 3); - let uniswapRouter: string; - let uniswapV2Router: string; - const sampler = new MockSamplerContract({ - sampleSellsFromUniswap: (router, takerToken, makerToken, fillAmounts) => { - uniswapRouter = router; - expect(takerToken).to.eq(expectedTakerToken); - expect(makerToken).to.eq(expectedMakerToken); - expect(fillAmounts).to.deep.eq(expectedTakerFillAmounts); - return fillAmounts.map(a => a.times(ratesBySource[ERC20BridgeSource.Uniswap]).integerValue()); - }, - sampleSellsFromUniswapV2: (router, path, fillAmounts) => { - uniswapV2Router = router; - if (path.length === 2) { - expect(path).to.deep.eq([expectedTakerToken, expectedMakerToken]); - } else if (path.length === 3) { - expect(path).to.deep.eq([expectedTakerToken, wethAddress, expectedMakerToken]); - } else { - expect(path).to.have.lengthOf.within(2, 3); - } - expect(fillAmounts).to.deep.eq(expectedTakerFillAmounts); - return fillAmounts.map(a => a.times(ratesBySource[ERC20BridgeSource.UniswapV2]).integerValue()); - }, - }); - const dexOrderSampler = new DexOrderSampler( - chainId, - sampler, - undefined, - undefined, - tokenAdjacencyGraph, - undefined, - async () => undefined, - ); - const [quotes] = await dexOrderSampler.executeAsync( - dexOrderSampler.getSellQuotes( - sources, - expectedMakerToken, - expectedTakerToken, - expectedTakerFillAmounts, - ), - ); - const expectedQuotes = sources.map(s => - expectedTakerFillAmounts.map(a => ({ - source: s, - input: a, - output: a.times(ratesBySource[s]).integerValue(), - fillData: (() => { - if (s === ERC20BridgeSource.UniswapV2) { - return { - router: uniswapV2Router, - tokenAddressPath: [expectedTakerToken, expectedMakerToken], - }; - } - // TODO jacob pass through - if (s === ERC20BridgeSource.Uniswap) { - return { router: uniswapRouter }; - } - return {}; - })(), - })), - ); - const uniswapV2ETHQuotes = [ - expectedTakerFillAmounts.map(a => ({ - source: ERC20BridgeSource.UniswapV2, - input: a, - output: a.times(ratesBySource[ERC20BridgeSource.UniswapV2]).integerValue(), - fillData: { - router: uniswapV2Router, - tokenAddressPath: [expectedTakerToken, wethAddress, expectedMakerToken], - }, - })), - ]; - // extra quote for Uniswap V2, which provides a direct quote (tokenA -> tokenB) AND an ETH quote (tokenA -> ETH -> tokenB) - const additionalSourceCount = 1; - expect(quotes).to.have.lengthOf(sources.length + additionalSourceCount); - expect(quotes).to.deep.eq(expectedQuotes.concat(uniswapV2ETHQuotes)); - }); - it('getBuyQuotes()', async () => { - const expectedTakerToken = randomAddress(); - const expectedMakerToken = randomAddress(); - const sources = [ERC20BridgeSource.Uniswap, ERC20BridgeSource.UniswapV2]; - const ratesBySource: RatesBySource = { - [ERC20BridgeSource.Uniswap]: getRandomFloat(0, 100), - [ERC20BridgeSource.UniswapV2]: getRandomFloat(0, 100), - }; - const expectedMakerFillAmounts = getSampleAmounts(new BigNumber(100e18), 3); - let uniswapRouter: string; - let uniswapV2Router: string; - const sampler = new MockSamplerContract({ - sampleBuysFromUniswap: (router, takerToken, makerToken, fillAmounts) => { - uniswapRouter = router; - expect(takerToken).to.eq(expectedTakerToken); - expect(makerToken).to.eq(expectedMakerToken); - expect(fillAmounts).to.deep.eq(expectedMakerFillAmounts); - return fillAmounts.map(a => a.times(ratesBySource[ERC20BridgeSource.Uniswap]).integerValue()); - }, - sampleBuysFromUniswapV2: (router, path, fillAmounts) => { - uniswapV2Router = router; - if (path.length === 2) { - expect(path).to.deep.eq([expectedTakerToken, expectedMakerToken]); - } else if (path.length === 3) { - expect(path).to.deep.eq([expectedTakerToken, wethAddress, expectedMakerToken]); - } else { - expect(path).to.have.lengthOf.within(2, 3); - } - expect(fillAmounts).to.deep.eq(expectedMakerFillAmounts); - return fillAmounts.map(a => a.times(ratesBySource[ERC20BridgeSource.UniswapV2]).integerValue()); - }, - }); - const dexOrderSampler = new DexOrderSampler( - chainId, - sampler, - undefined, - undefined, - tokenAdjacencyGraph, - undefined, - async () => undefined, - ); - const [quotes] = await dexOrderSampler.executeAsync( - dexOrderSampler.getBuyQuotes(sources, expectedMakerToken, expectedTakerToken, expectedMakerFillAmounts), - ); - const expectedQuotes = sources.map(s => - expectedMakerFillAmounts.map(a => ({ - source: s, - input: a, - output: a.times(ratesBySource[s]).integerValue(), - fillData: (() => { - if (s === ERC20BridgeSource.UniswapV2) { - return { - router: uniswapV2Router, - tokenAddressPath: [expectedTakerToken, expectedMakerToken], - }; - } - if (s === ERC20BridgeSource.Uniswap) { - return { router: uniswapRouter }; - } - return {}; - })(), - })), - ); - const uniswapV2ETHQuotes = [ - expectedMakerFillAmounts.map(a => ({ - source: ERC20BridgeSource.UniswapV2, - input: a, - output: a.times(ratesBySource[ERC20BridgeSource.UniswapV2]).integerValue(), - fillData: { - router: uniswapV2Router, - tokenAddressPath: [expectedTakerToken, wethAddress, expectedMakerToken], - }, - })), - ]; - // extra quote for Uniswap V2, which provides a direct quote (tokenA -> tokenB) AND an ETH quote (tokenA -> ETH -> tokenB) - expect(quotes).to.have.lengthOf(sources.length + 1); - expect(quotes).to.deep.eq(expectedQuotes.concat(uniswapV2ETHQuotes)); - }); - describe('batched operations', () => { - it('getLimitOrderFillableMakerAssetAmounts(), getLimitOrderFillableTakerAssetAmounts()', async () => { - const expectedFillableTakerAmounts = ORDERS.map(() => getRandomInteger(0, 100e18)); - const expectedFillableMakerAmounts = ORDERS.map(() => getRandomInteger(0, 100e18)); - const sampler = new MockSamplerContract({ - getLimitOrderFillableMakerAssetAmounts: (orders, signatures) => { - expect(orders).to.deep.eq(SIMPLE_ORDERS); - expect(signatures).to.deep.eq(ORDERS.map(o => o.signature)); - return expectedFillableMakerAmounts; - }, - getLimitOrderFillableTakerAssetAmounts: (orders, signatures) => { - expect(orders).to.deep.eq(SIMPLE_ORDERS); - expect(signatures).to.deep.eq(ORDERS.map(o => o.signature)); - return expectedFillableTakerAmounts; - }, - }); - const dexOrderSampler = new DexOrderSampler( - chainId, - sampler, - undefined, - undefined, - undefined, - undefined, - async () => undefined, - ); - const [fillableMakerAmounts, fillableTakerAmounts] = await dexOrderSampler.executeAsync( - dexOrderSampler.getLimitOrderFillableMakerAmounts(ORDERS, exchangeProxyAddress), - dexOrderSampler.getLimitOrderFillableTakerAmounts(ORDERS, exchangeProxyAddress), - ); - expect(fillableMakerAmounts).to.deep.eq(expectedFillableMakerAmounts); - expect(fillableTakerAmounts).to.deep.eq(expectedFillableTakerAmounts); - }); - }); - }); -}); -// tslint:disable-next-line: max-file-line-count diff --git a/packages/asset-swapper/test/exchange_proxy_swap_quote_consumer_test.ts b/packages/asset-swapper/test/exchange_proxy_swap_quote_consumer_test.ts deleted file mode 100644 index a0241166e7..0000000000 --- a/packages/asset-swapper/test/exchange_proxy_swap_quote_consumer_test.ts +++ /dev/null @@ -1,511 +0,0 @@ -import { getContractAddressesForChainOrThrow } from '@0x/contract-addresses'; -import { constants as contractConstants, getRandomInteger, randomAddress } from '@0x/contracts-test-utils'; -import { - decodeAffiliateFeeTransformerData, - decodeFillQuoteTransformerData, - decodePayTakerTransformerData, - decodePositiveSlippageFeeTransformerData, - decodeWethTransformerData, - ETH_TOKEN_ADDRESS, - FillQuoteTransformerLimitOrderInfo, - FillQuoteTransformerOrderType, - FillQuoteTransformerSide, - getTransformerAddress, - LimitOrderFields, -} from '@0x/protocol-utils'; -import { AbiEncoder, BigNumber, hexUtils } from '@0x/utils'; -import * as chai from 'chai'; -import * as _ from 'lodash'; -import 'mocha'; - -import { constants, POSITIVE_SLIPPAGE_FEE_TRANSFORMER_GAS } from '../src/constants'; -import { ExchangeProxySwapQuoteConsumer } from '../src/quote_consumers/exchange_proxy_swap_quote_consumer'; -import { AffiliateFeeType, MarketBuySwapQuote, MarketOperation, MarketSellSwapQuote } from '../src/types'; -import { - ERC20BridgeSource, - Fill, - NativeFillData, - OptimizedLimitOrder, - OptimizedMarketOrder, -} from '../src/utils/market_operation_utils/types'; - -import { chaiSetup } from './utils/chai_setup'; -import { getRandomAmount, getRandomSignature } from './utils/utils'; - -chaiSetup.configure(); -const expect = chai.expect; - -const { NULL_ADDRESS } = constants; -const { MAX_UINT256, ZERO_AMOUNT } = contractConstants; - -// tslint:disable: custom-no-magic-numbers - -describe('ExchangeProxySwapQuoteConsumer', () => { - const CHAIN_ID = 1; - const TAKER_TOKEN = randomAddress(); - const MAKER_TOKEN = randomAddress(); - const INTERMEDIATE_TOKEN = randomAddress(); - const TRANSFORMER_DEPLOYER = randomAddress(); - const contractAddresses = { - ...getContractAddressesForChainOrThrow(CHAIN_ID), - exchangeProxy: randomAddress(), - exchangeProxyAllowanceTarget: randomAddress(), - exchangeProxyTransformerDeployer: TRANSFORMER_DEPLOYER, - transformers: { - wethTransformer: getTransformerAddress(TRANSFORMER_DEPLOYER, 1), - payTakerTransformer: getTransformerAddress(TRANSFORMER_DEPLOYER, 2), - fillQuoteTransformer: getTransformerAddress(TRANSFORMER_DEPLOYER, 3), - affiliateFeeTransformer: getTransformerAddress(TRANSFORMER_DEPLOYER, 4), - positiveSlippageFeeTransformer: getTransformerAddress(TRANSFORMER_DEPLOYER, 5), - }, - }; - let consumer: ExchangeProxySwapQuoteConsumer; - - before(async () => { - consumer = new ExchangeProxySwapQuoteConsumer(contractAddresses, { chainId: CHAIN_ID }); - }); - - function getRandomOrder(orderFields?: Partial): LimitOrderFields { - return { - chainId: CHAIN_ID, - verifyingContract: contractAddresses.exchangeProxy, - expiry: getRandomInteger(1, 2e9), - feeRecipient: randomAddress(), - sender: randomAddress(), - pool: hexUtils.random(32), - maker: randomAddress(), - makerAmount: getRandomAmount(), - takerAmount: getRandomAmount(), - takerTokenFeeAmount: getRandomAmount(), - salt: getRandomAmount(2e9), - taker: NULL_ADDRESS, - makerToken: MAKER_TOKEN, - takerToken: TAKER_TOKEN, - ...orderFields, - }; - } - - function getRandomOptimizedMarketOrder( - optimizerFields?: Partial, - orderFields?: Partial, - ): OptimizedLimitOrder { - const order = getRandomOrder(orderFields); - return { - source: ERC20BridgeSource.Native, - fillData: { - order, - signature: getRandomSignature(), - maxTakerTokenFillAmount: order.takerAmount, - }, - type: FillQuoteTransformerOrderType.Limit, - makerToken: order.makerToken, - takerToken: order.takerToken, - makerAmount: order.makerAmount, - takerAmount: order.takerAmount, - // tslint:disable-next-line:no-object-literal-type-assertion - fill: {} as Fill, - ...optimizerFields, - }; - } - - function getRandomQuote(side: MarketOperation): MarketBuySwapQuote | MarketSellSwapQuote { - const order = getRandomOptimizedMarketOrder(); - const makerTokenFillAmount = order.makerAmount; - const takerTokenFillAmount = order.takerAmount; - return { - gasPrice: getRandomInteger(1, 1e9), - makerToken: MAKER_TOKEN, - takerToken: TAKER_TOKEN, - orders: [order], - makerTokenDecimals: 18, - takerTokenDecimals: 18, - sourceBreakdown: {} as any, - isTwoHop: false, - bestCaseQuoteInfo: { - makerAmount: makerTokenFillAmount, - takerAmount: takerTokenFillAmount, - totalTakerAmount: takerTokenFillAmount, - gas: Math.floor(Math.random() * 8e6), - protocolFeeInWeiAmount: getRandomAmount(), - feeTakerTokenAmount: getRandomAmount(), - slippage: 0, - }, - worstCaseQuoteInfo: { - makerAmount: makerTokenFillAmount, - takerAmount: takerTokenFillAmount, - totalTakerAmount: takerTokenFillAmount, - gas: Math.floor(Math.random() * 8e6), - protocolFeeInWeiAmount: getRandomAmount(), - feeTakerTokenAmount: getRandomAmount(), - slippage: 0, - }, - makerAmountPerEth: getRandomInteger(1, 1e9), - takerAmountPerEth: getRandomInteger(1, 1e9), - ...(side === MarketOperation.Buy - ? { type: MarketOperation.Buy, makerTokenFillAmount } - : { type: MarketOperation.Sell, takerTokenFillAmount }), - blockNumber: 1337420, - }; - } - - function getRandomTwoHopQuote(side: MarketOperation): MarketBuySwapQuote | MarketSellSwapQuote { - return { - ...getRandomQuote(side), - orders: [ - getRandomOptimizedMarketOrder({ makerToken: INTERMEDIATE_TOKEN }, { makerToken: INTERMEDIATE_TOKEN }), - getRandomOptimizedMarketOrder({ takerToken: INTERMEDIATE_TOKEN }, { takerToken: INTERMEDIATE_TOKEN }), - ], - isTwoHop: true, - } as any; - } - - function getRandomSellQuote(): MarketSellSwapQuote { - return getRandomQuote(MarketOperation.Sell) as MarketSellSwapQuote; - } - - function getRandomBuyQuote(): MarketBuySwapQuote { - return getRandomQuote(MarketOperation.Buy) as MarketBuySwapQuote; - } - - type PlainOrder = Exclude; - - function cleanOrders(orders: OptimizedMarketOrder[]): PlainOrder[] { - return orders.map( - o => - _.omit( - { - ...o.fillData, - order: _.omit((o.fillData as FillQuoteTransformerLimitOrderInfo).order, [ - 'chainId', - 'verifyingContract', - ]) as any, - }, - [ - 'fillableMakerAssetAmount', - 'fillableTakerAssetAmount', - 'fillableTakerFeeAmount', - 'fills', - 'chainId', - 'verifyingContract', - ], - ) as PlainOrder, - ); - } - - const transformERC20Encoder = AbiEncoder.createMethod('transformERC20', [ - { type: 'address', name: 'inputToken' }, - { type: 'address', name: 'outputToken' }, - { type: 'uint256', name: 'inputTokenAmount' }, - { type: 'uint256', name: 'minOutputTokenAmount' }, - { - type: 'tuple[]', - name: 'transformations', - components: [ - { type: 'uint32', name: 'deploymentNonce' }, - { type: 'bytes', name: 'data' }, - ], - }, - ]); - - interface TransformERC20Args { - inputToken: string; - outputToken: string; - inputTokenAmount: BigNumber; - minOutputTokenAmount: BigNumber; - transformations: Array<{ - deploymentNonce: BigNumber; - data: string; - }>; - } - - // const liquidityProviderEncoder = AbiEncoder.createMethod('sellToLiquidityProvider', [ - // { type: 'address', name: 'inputToken' }, - // { type: 'address', name: 'outputToken' }, - // { type: 'address', name: 'target' }, - // { type: 'address', name: 'recipient' }, - // { type: 'uint256', name: 'sellAmount' }, - // { type: 'uint256', name: 'minBuyAmount' }, - // { type: 'bytes', name: 'auxiliaryData' }, - // ]); - - // interface LiquidityProviderArgs { - // inputToken: string; - // outputToken: string; - // target: string; - // recipient: string; - // sellAmount: BigNumber; - // minBuyAmount: BigNumber; - // auxiliaryData: string; - // } - - describe('getCalldataOrThrow()', () => { - it('can produce a sell quote', async () => { - const quote = getRandomSellQuote(); - const callInfo = await consumer.getCalldataOrThrowAsync(quote); - const callArgs = transformERC20Encoder.decode(callInfo.calldataHexString) as TransformERC20Args; - expect(callArgs.inputToken).to.eq(TAKER_TOKEN); - expect(callArgs.outputToken).to.eq(MAKER_TOKEN); - expect(callArgs.inputTokenAmount).to.bignumber.eq(quote.worstCaseQuoteInfo.totalTakerAmount); - expect(callArgs.minOutputTokenAmount).to.bignumber.eq(quote.worstCaseQuoteInfo.makerAmount); - expect(callArgs.transformations).to.be.length(2); - expect( - callArgs.transformations[0].deploymentNonce.toNumber() === - consumer.transformerNonces.fillQuoteTransformer, - ); - expect( - callArgs.transformations[1].deploymentNonce.toNumber() === - consumer.transformerNonces.payTakerTransformer, - ); - const fillQuoteTransformerData = decodeFillQuoteTransformerData(callArgs.transformations[0].data); - expect(fillQuoteTransformerData.side).to.eq(FillQuoteTransformerSide.Sell); - expect(fillQuoteTransformerData.fillAmount).to.bignumber.eq(quote.takerTokenFillAmount); - expect(fillQuoteTransformerData.limitOrders).to.deep.eq(cleanOrders(quote.orders)); - expect(fillQuoteTransformerData.limitOrders.map(o => o.signature)).to.deep.eq( - (quote.orders as OptimizedLimitOrder[]).map(o => o.fillData.signature), - ); - expect(fillQuoteTransformerData.sellToken).to.eq(TAKER_TOKEN); - expect(fillQuoteTransformerData.buyToken).to.eq(MAKER_TOKEN); - const payTakerTransformerData = decodePayTakerTransformerData(callArgs.transformations[1].data); - expect(payTakerTransformerData.amounts).to.deep.eq([]); - expect(payTakerTransformerData.tokens).to.deep.eq([TAKER_TOKEN, ETH_TOKEN_ADDRESS]); - }); - - it('can produce a buy quote', async () => { - const quote = getRandomBuyQuote(); - const callInfo = await consumer.getCalldataOrThrowAsync(quote); - const callArgs = transformERC20Encoder.decode(callInfo.calldataHexString) as TransformERC20Args; - expect(callArgs.inputToken).to.eq(TAKER_TOKEN); - expect(callArgs.outputToken).to.eq(MAKER_TOKEN); - expect(callArgs.inputTokenAmount).to.bignumber.eq(quote.worstCaseQuoteInfo.totalTakerAmount); - expect(callArgs.minOutputTokenAmount).to.bignumber.eq(quote.worstCaseQuoteInfo.makerAmount); - expect(callArgs.transformations).to.be.length(2); - expect( - callArgs.transformations[0].deploymentNonce.toNumber() === - consumer.transformerNonces.fillQuoteTransformer, - ); - expect( - callArgs.transformations[1].deploymentNonce.toNumber() === - consumer.transformerNonces.payTakerTransformer, - ); - const fillQuoteTransformerData = decodeFillQuoteTransformerData(callArgs.transformations[0].data); - expect(fillQuoteTransformerData.side).to.eq(FillQuoteTransformerSide.Buy); - expect(fillQuoteTransformerData.fillAmount).to.bignumber.eq(quote.makerTokenFillAmount); - expect(fillQuoteTransformerData.limitOrders).to.deep.eq(cleanOrders(quote.orders)); - expect(fillQuoteTransformerData.limitOrders.map(o => o.signature)).to.deep.eq( - (quote.orders as OptimizedLimitOrder[]).map(o => o.fillData.signature), - ); - expect(fillQuoteTransformerData.sellToken).to.eq(TAKER_TOKEN); - expect(fillQuoteTransformerData.buyToken).to.eq(MAKER_TOKEN); - const payTakerTransformerData = decodePayTakerTransformerData(callArgs.transformations[1].data); - expect(payTakerTransformerData.amounts).to.deep.eq([]); - expect(payTakerTransformerData.tokens).to.deep.eq([TAKER_TOKEN, ETH_TOKEN_ADDRESS]); - }); - - it('ERC20 -> ERC20 does not have a WETH transformer', async () => { - const quote = getRandomSellQuote(); - const callInfo = await consumer.getCalldataOrThrowAsync(quote); - const callArgs = transformERC20Encoder.decode(callInfo.calldataHexString) as TransformERC20Args; - const nonces = callArgs.transformations.map(t => t.deploymentNonce); - expect(nonces).to.not.include(consumer.transformerNonces.wethTransformer); - }); - - it('ETH -> ERC20 has a WETH transformer before the fill', async () => { - const quote = getRandomSellQuote(); - const callInfo = await consumer.getCalldataOrThrowAsync(quote, { - extensionContractOpts: { isFromETH: true }, - }); - const callArgs = transformERC20Encoder.decode(callInfo.calldataHexString) as TransformERC20Args; - expect(callArgs.transformations[0].deploymentNonce.toNumber()).to.eq( - consumer.transformerNonces.wethTransformer, - ); - const wethTransformerData = decodeWethTransformerData(callArgs.transformations[0].data); - expect(wethTransformerData.amount).to.bignumber.eq(quote.worstCaseQuoteInfo.totalTakerAmount); - expect(wethTransformerData.token).to.eq(ETH_TOKEN_ADDRESS); - }); - - it('ERC20 -> ETH has a WETH transformer after the fill', async () => { - const quote = getRandomSellQuote(); - const callInfo = await consumer.getCalldataOrThrowAsync(quote, { - extensionContractOpts: { isToETH: true }, - }); - const callArgs = transformERC20Encoder.decode(callInfo.calldataHexString) as TransformERC20Args; - expect(callArgs.transformations[1].deploymentNonce.toNumber()).to.eq( - consumer.transformerNonces.wethTransformer, - ); - const wethTransformerData = decodeWethTransformerData(callArgs.transformations[1].data); - expect(wethTransformerData.amount).to.bignumber.eq(MAX_UINT256); - expect(wethTransformerData.token).to.eq(contractAddresses.etherToken); - }); - it('Appends an affiliate fee transformer after the fill if a buy token affiliate fee is provided', async () => { - const quote = getRandomSellQuote(); - const affiliateFee = { - recipient: randomAddress(), - buyTokenFeeAmount: getRandomAmount(), - sellTokenFeeAmount: ZERO_AMOUNT, - feeType: AffiliateFeeType.PercentageFee, - }; - const callInfo = await consumer.getCalldataOrThrowAsync(quote, { - extensionContractOpts: { affiliateFee }, - }); - const callArgs = transformERC20Encoder.decode(callInfo.calldataHexString) as TransformERC20Args; - expect(callArgs.transformations[1].deploymentNonce.toNumber()).to.eq( - consumer.transformerNonces.affiliateFeeTransformer, - ); - const affiliateFeeTransformerData = decodeAffiliateFeeTransformerData(callArgs.transformations[1].data); - expect(affiliateFeeTransformerData.fees).to.deep.equal([ - { token: MAKER_TOKEN, amount: affiliateFee.buyTokenFeeAmount, recipient: affiliateFee.recipient }, - ]); - }); - it('Appends a positive slippage affiliate fee transformer after the fill if the positive slippage fee feeType is specified', async () => { - const quote = getRandomSellQuote(); - const affiliateFee = { - recipient: randomAddress(), - buyTokenFeeAmount: ZERO_AMOUNT, - sellTokenFeeAmount: ZERO_AMOUNT, - feeType: AffiliateFeeType.PositiveSlippageFee, - }; - const callInfo = await consumer.getCalldataOrThrowAsync(quote, { - extensionContractOpts: { affiliateFee }, - }); - const callArgs = transformERC20Encoder.decode(callInfo.calldataHexString) as TransformERC20Args; - expect(callArgs.transformations[1].deploymentNonce.toNumber()).to.eq( - consumer.transformerNonces.positiveSlippageFeeTransformer, - ); - const positiveSlippageFeeTransformerData = decodePositiveSlippageFeeTransformerData( - callArgs.transformations[1].data, - ); - const bestCaseAmount = quote.bestCaseQuoteInfo.makerAmount.plus( - POSITIVE_SLIPPAGE_FEE_TRANSFORMER_GAS.multipliedBy(quote.gasPrice).multipliedBy( - quote.makerAmountPerEth, - ), - ); - expect(positiveSlippageFeeTransformerData).to.deep.equal({ - token: MAKER_TOKEN, - bestCaseAmount, - recipient: affiliateFee.recipient, - }); - }); - it('Throws if a sell token affiliate fee is provided', async () => { - const quote = getRandomSellQuote(); - const affiliateFee = { - recipient: randomAddress(), - buyTokenFeeAmount: ZERO_AMOUNT, - sellTokenFeeAmount: getRandomAmount(), - feeType: AffiliateFeeType.PercentageFee, - }; - expect( - consumer.getCalldataOrThrowAsync(quote, { - extensionContractOpts: { affiliateFee }, - }), - ).to.eventually.be.rejectedWith('Affiliate fees denominated in sell token are not yet supported'); - }); - it('Uses two `FillQuoteTransformer`s if given two-hop sell quote', async () => { - const quote = getRandomTwoHopQuote(MarketOperation.Sell) as MarketSellSwapQuote; - const callInfo = await consumer.getCalldataOrThrowAsync(quote, { - extensionContractOpts: { isTwoHop: true }, - }); - const callArgs = transformERC20Encoder.decode(callInfo.calldataHexString) as TransformERC20Args; - expect(callArgs.inputToken).to.eq(TAKER_TOKEN); - expect(callArgs.outputToken).to.eq(MAKER_TOKEN); - expect(callArgs.inputTokenAmount).to.bignumber.eq(quote.worstCaseQuoteInfo.totalTakerAmount); - expect(callArgs.minOutputTokenAmount).to.bignumber.eq(quote.worstCaseQuoteInfo.makerAmount); - expect(callArgs.transformations).to.be.length(3); - expect( - callArgs.transformations[0].deploymentNonce.toNumber() === - consumer.transformerNonces.fillQuoteTransformer, - ); - expect( - callArgs.transformations[1].deploymentNonce.toNumber() === - consumer.transformerNonces.fillQuoteTransformer, - ); - expect( - callArgs.transformations[2].deploymentNonce.toNumber() === - consumer.transformerNonces.payTakerTransformer, - ); - const [firstHopOrder, secondHopOrder] = quote.orders; - const firstHopFillQuoteTransformerData = decodeFillQuoteTransformerData(callArgs.transformations[0].data); - expect(firstHopFillQuoteTransformerData.side).to.eq(FillQuoteTransformerSide.Sell); - expect(firstHopFillQuoteTransformerData.fillAmount).to.bignumber.eq(firstHopOrder.takerAmount); - expect(firstHopFillQuoteTransformerData.limitOrders).to.deep.eq(cleanOrders([firstHopOrder])); - expect(firstHopFillQuoteTransformerData.limitOrders.map(o => o.signature)).to.deep.eq([ - (firstHopOrder as OptimizedLimitOrder).fillData.signature, - ]); - expect(firstHopFillQuoteTransformerData.sellToken).to.eq(TAKER_TOKEN); - expect(firstHopFillQuoteTransformerData.buyToken).to.eq(INTERMEDIATE_TOKEN); - const secondHopFillQuoteTransformerData = decodeFillQuoteTransformerData(callArgs.transformations[1].data); - expect(secondHopFillQuoteTransformerData.side).to.eq(FillQuoteTransformerSide.Sell); - expect(secondHopFillQuoteTransformerData.fillAmount).to.bignumber.eq(contractConstants.MAX_UINT256); - expect(secondHopFillQuoteTransformerData.limitOrders).to.deep.eq(cleanOrders([secondHopOrder])); - expect(secondHopFillQuoteTransformerData.limitOrders.map(o => o.signature)).to.deep.eq([ - (secondHopOrder as OptimizedLimitOrder).fillData.signature, - ]); - expect(secondHopFillQuoteTransformerData.sellToken).to.eq(INTERMEDIATE_TOKEN); - expect(secondHopFillQuoteTransformerData.buyToken).to.eq(MAKER_TOKEN); - const payTakerTransformerData = decodePayTakerTransformerData(callArgs.transformations[2].data); - expect(payTakerTransformerData.amounts).to.deep.eq([]); - expect(payTakerTransformerData.tokens).to.deep.eq([TAKER_TOKEN, INTERMEDIATE_TOKEN, ETH_TOKEN_ADDRESS]); - }); - // it.skip('Uses the `LiquidityProviderFeature` if given a single LiquidityProvider order', async () => { - // const quote = { - // ...getRandomSellQuote(), - // orders: [ - // { - // ...getRandomOrder(), - // fills: [ - // { - // source: ERC20BridgeSource.LiquidityProvider, - // sourcePathId: '', - // input: constants.ZERO_AMOUNT, - // output: constants.ZERO_AMOUNT, - // subFills: [], - // }, - // ], - // }, - // ], - // }; - // const callInfo = await consumer.getCalldataOrThrowAsync(quote); - // const callArgs = liquidityProviderEncoder.decode(callInfo.calldataHexString) as LiquidityProviderArgs; - // expect(callArgs).to.deep.equal({ - // inputToken: TAKER_TOKEN, - // outputToken: MAKER_TOKEN, - // target: quote.orders[0].makerAddress, - // recipient: constants.NULL_ADDRESS, - // sellAmount: quote.worstCaseQuoteInfo.feeTakerTokenAmount, - // minBuyAmount: getSwapMinBuyAmount(quote), - // auxiliaryData: constants.NULL_BYTES, - // }); - // }); - it('allows selling the entire balance for CFL', async () => { - const quote = getRandomSellQuote(); - const callInfo = await consumer.getCalldataOrThrowAsync(quote, { - extensionContractOpts: { shouldSellEntireBalance: true }, - }); - const callArgs = transformERC20Encoder.decode(callInfo.calldataHexString) as TransformERC20Args; - expect(callArgs.inputToken).to.eq(TAKER_TOKEN); - expect(callArgs.outputToken).to.eq(MAKER_TOKEN); - expect(callArgs.inputTokenAmount).to.bignumber.eq(MAX_UINT256); - expect(callArgs.minOutputTokenAmount).to.bignumber.eq(quote.worstCaseQuoteInfo.makerAmount); - expect(callArgs.transformations).to.be.length(2); - expect( - callArgs.transformations[0].deploymentNonce.toNumber() === - consumer.transformerNonces.fillQuoteTransformer, - ); - expect( - callArgs.transformations[1].deploymentNonce.toNumber() === - consumer.transformerNonces.payTakerTransformer, - ); - const fillQuoteTransformerData = decodeFillQuoteTransformerData(callArgs.transformations[0].data); - expect(fillQuoteTransformerData.side).to.eq(FillQuoteTransformerSide.Sell); - expect(fillQuoteTransformerData.fillAmount).to.bignumber.eq(MAX_UINT256); - expect(fillQuoteTransformerData.limitOrders).to.deep.eq(cleanOrders(quote.orders)); - expect(fillQuoteTransformerData.limitOrders.map(o => o.signature)).to.deep.eq( - (quote.orders as OptimizedLimitOrder[]).map(o => o.fillData.signature), - ); - expect(fillQuoteTransformerData.sellToken).to.eq(TAKER_TOKEN); - expect(fillQuoteTransformerData.buyToken).to.eq(MAKER_TOKEN); - const payTakerTransformerData = decodePayTakerTransformerData(callArgs.transformations[1].data); - expect(payTakerTransformerData.amounts).to.deep.eq([]); - expect(payTakerTransformerData.tokens).to.deep.eq([TAKER_TOKEN, ETH_TOKEN_ADDRESS]); - }); - }); -}); diff --git a/packages/asset-swapper/test/global_hooks.ts b/packages/asset-swapper/test/global_hooks.ts deleted file mode 100644 index 26c37158f7..0000000000 --- a/packages/asset-swapper/test/global_hooks.ts +++ /dev/null @@ -1,6 +0,0 @@ -before('set up mocha', async function(): Promise { - // HACK: Since the migrations take longer then our global mocha timeout limit - // we manually increase it for this before hook. - const mochaTestTimeoutMs = 25000; - this.timeout(mochaTestTimeoutMs); // tslint:disable-line:no-invalid-this -}); diff --git a/packages/asset-swapper/test/market_operation_utils_test.ts b/packages/asset-swapper/test/market_operation_utils_test.ts deleted file mode 100644 index d895bfef71..0000000000 --- a/packages/asset-swapper/test/market_operation_utils_test.ts +++ /dev/null @@ -1,1640 +0,0 @@ -// tslint:disable: no-unbound-method -import { ChainId, getContractAddressesForChainOrThrow } from '@0x/contract-addresses'; -import { - assertRoughlyEquals, - constants, - expect, - getRandomFloat, - Numberish, - randomAddress, -} from '@0x/contracts-test-utils'; -import { FillQuoteTransformerOrderType, LimitOrder, RfqOrder, SignatureType } from '@0x/protocol-utils'; -import { BigNumber, hexUtils, NULL_BYTES } from '@0x/utils'; -import { Web3Wrapper } from '@0x/web3-wrapper'; -import { Pool } from 'balancer-labs-sor-v1/dist/types'; -import * as _ from 'lodash'; -import * as TypeMoq from 'typemoq'; - -import { MarketOperation, QuoteRequestor, RfqRequestOpts, SignedNativeOrder, TokenAdjacencyGraph } from '../src'; -import { Integrator } from '../src/types'; -import { MarketOperationUtils } from '../src/utils/market_operation_utils/'; -import { - BUY_SOURCE_FILTER_BY_CHAIN_ID, - SELL_SOURCE_FILTER_BY_CHAIN_ID, - SOURCE_FLAGS, - ZERO_AMOUNT, -} from '../src/utils/market_operation_utils/constants'; -import { AbstractPoolsCache } from '../src/utils/market_operation_utils/pools_cache'; -import { DexOrderSampler } from '../src/utils/market_operation_utils/sampler'; -import { BATCH_SOURCE_FILTERS } from '../src/utils/market_operation_utils/sampler_operations'; -import { SourceFilters } from '../src/utils/market_operation_utils/source_filters'; -import { - AggregationError, - DexSample, - ERC20BridgeSource, - FillData, - GenerateOptimizedOrdersOpts, - GetMarketOrdersOpts, - LiquidityProviderFillData, - MarketSideLiquidity, - OptimizedMarketBridgeOrder, - OptimizerResultWithReport, -} from '../src/utils/market_operation_utils/types'; - -const MAKER_TOKEN = randomAddress(); -const TAKER_TOKEN = randomAddress(); - -const DEFAULT_INCLUDED = [ - ERC20BridgeSource.SushiSwap, - ERC20BridgeSource.Native, - ERC20BridgeSource.Uniswap, - ERC20BridgeSource.Curve, -]; - -const DEFAULT_EXCLUDED = SELL_SOURCE_FILTER_BY_CHAIN_ID[ChainId.Mainnet].sources.filter( - s => !DEFAULT_INCLUDED.includes(s), -); -const BUY_SOURCES = BUY_SOURCE_FILTER_BY_CHAIN_ID[ChainId.Mainnet].sources; -const SELL_SOURCES = SELL_SOURCE_FILTER_BY_CHAIN_ID[ChainId.Mainnet].sources; -const TOKEN_ADJACENCY_GRAPH = TokenAdjacencyGraph.getEmptyGraph(); - -const SIGNATURE = { v: 1, r: NULL_BYTES, s: NULL_BYTES, signatureType: SignatureType.EthSign }; -const FOO_INTEGRATOR: Integrator = { - integratorId: 'foo', - label: 'foo', -}; - -/** - * gets the orders required for a market sell operation by (potentially) merging native orders with - * generated bridge orders. - * @param nativeOrders Native orders. Assumes LimitOrders not RfqOrders - * @param takerAmount Amount of taker asset to sell. - * @param opts Options object. - * @return object with optimized orders and a QuoteReport - */ -async function getMarketSellOrdersAsync( - utils: MarketOperationUtils, - nativeOrders: SignedNativeOrder[], - takerAmount: BigNumber, - opts: Partial & { gasPrice: BigNumber }, -): Promise { - return utils.getOptimizerResultAsync(nativeOrders, takerAmount, MarketOperation.Sell, opts); -} - -/** - * gets the orders required for a market buy operation by (potentially) merging native orders with - * generated bridge orders. - * @param nativeOrders Native orders. Assumes LimitOrders not RfqOrders - * @param makerAmount Amount of maker asset to buy. - * @param opts Options object. - * @return object with optimized orders and a QuoteReport - */ -async function getMarketBuyOrdersAsync( - utils: MarketOperationUtils, - nativeOrders: SignedNativeOrder[], - makerAmount: BigNumber, - opts: Partial & { gasPrice: BigNumber }, -): Promise { - return utils.getOptimizerResultAsync(nativeOrders, makerAmount, MarketOperation.Buy, opts); -} - -class MockPoolsCache extends AbstractPoolsCache { - constructor(private readonly _handler: (takerToken: string, makerToken: string) => Pool[]) { - super(new Map()); - } - protected async _fetchPoolsForPairAsync(takerToken: string, makerToken: string): Promise { - return this._handler(takerToken, makerToken); - } -} - -// Return some pool so that sampling functions are called for Balancer and BalancerV2 -// tslint:disable:custom-no-magic-numbers -const mockPoolsCache = new MockPoolsCache((_takerToken: string, _makerToken: string) => { - return [ - { - id: '0xe4b2554b622cc342ac7d6dc19b594553577941df000200000000000000000003', - balanceIn: new BigNumber('13655.491506618973154788'), - balanceOut: new BigNumber('8217005.926472'), - weightIn: new BigNumber('0.5'), - weightOut: new BigNumber('0.5'), - swapFee: new BigNumber('0.008'), - spotPrice: new BigNumber(596.92685), - }, - ]; -}); -// tslint:enable:custom-no-magic-numbers - -// tslint:disable: custom-no-magic-numbers promise-function-async -describe('MarketOperationUtils tests', () => { - const CHAIN_ID = ChainId.Mainnet; - const contractAddresses = { - ...getContractAddressesForChainOrThrow(CHAIN_ID), - }; - - function getMockedQuoteRequestor( - type: 'indicative' | 'firm', - results: SignedNativeOrder[], - verifiable: TypeMoq.Times, - ): TypeMoq.IMock { - const args: [any, any, any, any, any, any] = [ - TypeMoq.It.isAny(), - TypeMoq.It.isAny(), - TypeMoq.It.isAny(), - TypeMoq.It.isAny(), - TypeMoq.It.isAny(), - TypeMoq.It.isAny(), - ]; - const requestor = TypeMoq.Mock.ofType(QuoteRequestor, TypeMoq.MockBehavior.Loose, true); - requestor - .setup(mqr => mqr.getMakerUriForSignature(TypeMoq.It.isValue(SIGNATURE))) - .returns(() => 'https://foo.bar'); - if (type === 'firm') { - requestor - .setup(r => r.requestRfqtFirmQuotesAsync(...args)) - .returns(async () => results) - .verifiable(verifiable); - } else { - requestor - .setup(r => r.requestRfqtIndicativeQuotesAsync(...args)) - .returns(async () => - results.map(r => { - return { ...r.order, makerUri: 'https://foo.bar/' }; - }), - ) - .verifiable(verifiable); - } - return requestor; - } - - function createOrdersFromSellRates(takerAmount: BigNumber, rates: Numberish[]): SignedNativeOrder[] { - const singleTakerAmount = takerAmount.div(rates.length).integerValue(BigNumber.ROUND_UP); - return rates.map(r => { - const o: SignedNativeOrder = { - order: { - ...new LimitOrder({ - makerAmount: singleTakerAmount.times(r).integerValue(), - takerAmount: singleTakerAmount, - }), - }, - signature: SIGNATURE, - type: FillQuoteTransformerOrderType.Limit, - }; - return o; - }); - } - - function createOrdersFromBuyRates(makerAmount: BigNumber, rates: Numberish[]): SignedNativeOrder[] { - const singleMakerAmount = makerAmount.div(rates.length).integerValue(BigNumber.ROUND_UP); - return rates.map(r => { - const o: SignedNativeOrder = { - order: { - ...new LimitOrder({ - makerAmount: singleMakerAmount, - takerAmount: singleMakerAmount.div(r).integerValue(), - }), - }, - signature: SIGNATURE, - type: FillQuoteTransformerOrderType.Limit, - }; - return o; - }); - } - - const ORDER_DOMAIN = { - exchangeAddress: contractAddresses.exchange, - chainId: CHAIN_ID, - }; - - function createSamplesFromRates( - source: ERC20BridgeSource, - inputs: Numberish[], - rates: Numberish[], - fillData?: FillData, - ): DexSample[] { - const samples: DexSample[] = []; - inputs.forEach((input, i) => { - const rate = rates[i]; - samples.push({ - source, - fillData: fillData || DEFAULT_FILL_DATA[source], - input: new BigNumber(input), - output: new BigNumber(input) - .minus(i === 0 ? 0 : samples[i - 1].input) - .times(rate) - .plus(i === 0 ? 0 : samples[i - 1].output) - .integerValue(), - }); - }); - return samples; - } - - type GetMultipleQuotesOperation = ( - sources: ERC20BridgeSource[], - makerToken: string, - takerToken: string, - fillAmounts: BigNumber[], - wethAddress: string, - tokenAdjacencyGraph: TokenAdjacencyGraph, - liquidityProviderAddress?: string, - ) => DexSample[][]; - - function createGetMultipleSellQuotesOperationFromRates(rates: RatesBySource): GetMultipleQuotesOperation { - return ( - sources: ERC20BridgeSource[], - _makerToken: string, - _takerToken: string, - fillAmounts: BigNumber[], - _wethAddress: string, - ) => { - return BATCH_SOURCE_FILTERS.getAllowed(sources).map(s => createSamplesFromRates(s, fillAmounts, rates[s])); - }; - } - - function createGetMultipleBuyQuotesOperationFromRates(rates: RatesBySource): GetMultipleQuotesOperation { - return ( - sources: ERC20BridgeSource[], - _makerToken: string, - _takerToken: string, - fillAmounts: BigNumber[], - _wethAddress: string, - ) => { - return BATCH_SOURCE_FILTERS.getAllowed(sources).map(s => - createSamplesFromRates( - s, - fillAmounts, - rates[s].map(r => new BigNumber(1).div(r)), - ), - ); - }; - } - - type GetBestNativeTokenSellRateOperation = ( - sources: ERC20BridgeSource[], - makerToken: string, - takerToken: string, - fillAmounts: BigNumber[], - wethAddress: string, - liquidityProviderAddress?: string, - ) => BigNumber; - - function createGetBestNativeSellRate(rate: Numberish): GetBestNativeTokenSellRateOperation { - return ( - _sources: ERC20BridgeSource[], - _makerToken: string, - _takerToken: string, - _fillAmounts: BigNumber[], - _wethAddress: string, - ) => { - return new BigNumber(rate); - }; - } - - function createDecreasingRates(count: number): BigNumber[] { - const rates: BigNumber[] = []; - const initialRate = getRandomFloat(1e-3, 1e2); - _.times(count, () => getRandomFloat(0.95, 1)).forEach((r, i) => { - const prevRate = i === 0 ? initialRate : rates[i - 1]; - rates.push(prevRate.times(r)); - }); - return rates; - } - - const NUM_SAMPLES = 3; - - interface RatesBySource { - [source: string]: Numberish[]; - } - - const ZERO_RATES: RatesBySource = Object.assign( - {}, - ...Object.values(ERC20BridgeSource).map(source => ({ - [source]: _.times(NUM_SAMPLES, () => 0), - })), - ); - - const DEFAULT_RATES: RatesBySource = { - ...ZERO_RATES, - [ERC20BridgeSource.Native]: createDecreasingRates(NUM_SAMPLES), - [ERC20BridgeSource.SushiSwap]: createDecreasingRates(NUM_SAMPLES), - [ERC20BridgeSource.Uniswap]: createDecreasingRates(NUM_SAMPLES), - [ERC20BridgeSource.Curve]: createDecreasingRates(NUM_SAMPLES), - }; - - interface FillDataBySource { - [source: string]: FillData; - } - - const DEFAULT_FILL_DATA: FillDataBySource = { - [ERC20BridgeSource.UniswapV2]: { tokenAddressPath: [] }, - [ERC20BridgeSource.Balancer]: { poolAddress: randomAddress() }, - [ERC20BridgeSource.BalancerV2]: { - vault: randomAddress(), - poolId: randomAddress(), - deadline: Math.floor(Date.now() / 1000) + 300, - }, - [ERC20BridgeSource.Bancor]: { path: [], networkAddress: randomAddress() }, - [ERC20BridgeSource.Curve]: { - pool: { - poolAddress: randomAddress(), - tokens: [TAKER_TOKEN, MAKER_TOKEN], - exchangeFunctionSelector: hexUtils.random(4), - sellQuoteFunctionSelector: hexUtils.random(4), - buyQuoteFunctionSelector: hexUtils.random(4), - }, - fromTokenIdx: 0, - toTokenIdx: 1, - }, - [ERC20BridgeSource.Saddle]: { - pool: { - poolAddress: randomAddress(), - tokens: [TAKER_TOKEN, MAKER_TOKEN], - exchangeFunctionSelector: hexUtils.random(4), - sellQuoteFunctionSelector: hexUtils.random(4), - buyQuoteFunctionSelector: hexUtils.random(4), - }, - fromTokenIdx: 0, - toTokenIdx: 1, - }, - [ERC20BridgeSource.LiquidityProvider]: { poolAddress: randomAddress() }, - [ERC20BridgeSource.SushiSwap]: { tokenAddressPath: [] }, - [ERC20BridgeSource.Mooniswap]: { poolAddress: randomAddress() }, - [ERC20BridgeSource.Native]: { order: new LimitOrder() }, - [ERC20BridgeSource.MultiHop]: {}, - [ERC20BridgeSource.Shell]: { poolAddress: randomAddress() }, - [ERC20BridgeSource.Component]: { poolAddress: randomAddress() }, - [ERC20BridgeSource.Dodo]: {}, - [ERC20BridgeSource.DodoV2]: {}, - [ERC20BridgeSource.CryptoCom]: { tokenAddressPath: [] }, - [ERC20BridgeSource.Uniswap]: { router: randomAddress() }, - [ERC20BridgeSource.MakerPsm]: {}, - [ERC20BridgeSource.KyberDmm]: { tokenAddressPath: [], router: randomAddress(), poolsPath: [] }, - }; - - const DEFAULT_OPS = { - getTokenDecimals(_makerAddress: string, _takerAddress: string): BigNumber[] { - const result = new BigNumber(18); - return [result, result]; - }, - getLimitOrderFillableTakerAmounts(orders: SignedNativeOrder[]): BigNumber[] { - return orders.map(o => o.order.takerAmount); - }, - getLimitOrderFillableMakerAmounts(orders: SignedNativeOrder[]): BigNumber[] { - return orders.map(o => o.order.makerAmount); - }, - getSellQuotes: createGetMultipleSellQuotesOperationFromRates(DEFAULT_RATES), - getBuyQuotes: createGetMultipleBuyQuotesOperationFromRates(DEFAULT_RATES), - getBestNativeTokenSellRate: createGetBestNativeSellRate(1), - getTwoHopSellQuotes: (..._params: any[]) => [], - getTwoHopBuyQuotes: (..._params: any[]) => [], - isAddressContract: (..._params: any[]) => false, - getGasLeft: () => ZERO_AMOUNT, - getBlockNumber: () => ZERO_AMOUNT, - }; - - const MOCK_SAMPLER = ({ - async executeAsync(...ops: any[]): Promise { - return MOCK_SAMPLER.executeBatchAsync(ops); - }, - async executeBatchAsync(ops: any[]): Promise { - return ops; - }, - poolsCaches: { - [ERC20BridgeSource.BalancerV2]: mockPoolsCache, - [ERC20BridgeSource.Balancer]: mockPoolsCache, - }, - liquidityProviderRegistry: {}, - chainId: CHAIN_ID, - } as any) as DexOrderSampler; - - function replaceSamplerOps(ops: Partial = {}): void { - Object.assign(MOCK_SAMPLER, DEFAULT_OPS); - Object.assign(MOCK_SAMPLER, ops); - } - - describe('MarketOperationUtils', () => { - let marketOperationUtils: MarketOperationUtils; - - before(async () => { - marketOperationUtils = new MarketOperationUtils(MOCK_SAMPLER, contractAddresses, ORDER_DOMAIN); - }); - - describe('getMarketSellOrdersAsync()', () => { - const FILL_AMOUNT = new BigNumber('100e18'); - const ORDERS = createOrdersFromSellRates( - FILL_AMOUNT, - _.times(NUM_SAMPLES, i => DEFAULT_RATES[ERC20BridgeSource.Native][i]), - ); - const DEFAULT_OPTS: Partial & { gasPrice: BigNumber } = { - numSamples: NUM_SAMPLES, - sampleDistributionBase: 1, - bridgeSlippage: 0, - maxFallbackSlippage: 100, - excludedSources: DEFAULT_EXCLUDED, - allowFallback: false, - gasSchedule: {}, - feeSchedule: {}, - gasPrice: new BigNumber(30e9), - }; - - beforeEach(() => { - replaceSamplerOps(); - }); - - it('queries `numSamples` samples', async () => { - // neon-router requires at least 3 samples - const numSamples = _.random(3, NUM_SAMPLES); - let actualNumSamples = 0; - replaceSamplerOps({ - getSellQuotes: (sources, makerToken, takerToken, amounts, wethAddress) => { - actualNumSamples = amounts.length; - return DEFAULT_OPS.getSellQuotes( - sources, - makerToken, - takerToken, - amounts, - wethAddress, - TOKEN_ADJACENCY_GRAPH, - ); - }, - }); - await getMarketSellOrdersAsync(marketOperationUtils, ORDERS, FILL_AMOUNT, { - ...DEFAULT_OPTS, - numSamples, - neonRouterNumSamples: numSamples, - }); - expect(actualNumSamples).eq(numSamples); - }); - - it('polls all DEXes if `excludedSources` is empty', async () => { - let sourcesPolled: ERC20BridgeSource[] = []; - replaceSamplerOps({ - getSellQuotes: (sources, makerToken, takerToken, amounts, wethAddress) => { - sourcesPolled = sourcesPolled.concat(sources.slice()); - return DEFAULT_OPS.getSellQuotes( - sources, - makerToken, - takerToken, - amounts, - wethAddress, - TOKEN_ADJACENCY_GRAPH, - ); - }, - getTwoHopSellQuotes: (...args: any[]) => { - sourcesPolled.push(ERC20BridgeSource.MultiHop); - return DEFAULT_OPS.getTwoHopSellQuotes(...args); - }, - }); - await getMarketSellOrdersAsync(marketOperationUtils, ORDERS, FILL_AMOUNT, { - ...DEFAULT_OPTS, - excludedSources: [], - }); - expect(_.uniq(sourcesPolled).sort()).to.deep.equals(SELL_SOURCES.slice().sort()); - }); - - it('does not poll DEXes in `excludedSources`', async () => { - const excludedSources = [ERC20BridgeSource.Uniswap, ERC20BridgeSource.SushiSwap]; - let sourcesPolled: ERC20BridgeSource[] = []; - replaceSamplerOps({ - getSellQuotes: (sources, makerToken, takerToken, amounts, wethAddress) => { - sourcesPolled = sourcesPolled.concat(sources.slice()); - return DEFAULT_OPS.getSellQuotes( - sources, - makerToken, - takerToken, - amounts, - wethAddress, - TOKEN_ADJACENCY_GRAPH, - ); - }, - getTwoHopSellQuotes: (sources: ERC20BridgeSource[], ...args: any[]) => { - if (sources.length !== 0) { - sourcesPolled.push(ERC20BridgeSource.MultiHop); - sourcesPolled.push(...sources); - } - return DEFAULT_OPS.getTwoHopSellQuotes(...args); - }, - }); - await getMarketSellOrdersAsync(marketOperationUtils, ORDERS, FILL_AMOUNT, { - ...DEFAULT_OPTS, - excludedSources, - }); - expect(_.uniq(sourcesPolled).sort()).to.deep.equals(_.without(SELL_SOURCES, ...excludedSources).sort()); - }); - - it('only polls DEXes in `includedSources`', async () => { - const includedSources = [ERC20BridgeSource.Uniswap, ERC20BridgeSource.SushiSwap]; - let sourcesPolled: ERC20BridgeSource[] = []; - replaceSamplerOps({ - getSellQuotes: (sources, makerToken, takerToken, amounts, wethAddress) => { - sourcesPolled = sourcesPolled.concat(sources.slice()); - return DEFAULT_OPS.getSellQuotes( - sources, - makerToken, - takerToken, - amounts, - wethAddress, - TOKEN_ADJACENCY_GRAPH, - ); - }, - getTwoHopSellQuotes: (sources: ERC20BridgeSource[], ...args: any[]) => { - if (sources.length !== 0) { - sourcesPolled.push(ERC20BridgeSource.MultiHop); - sourcesPolled.push(...sources); - } - return DEFAULT_OPS.getTwoHopSellQuotes(sources, ...args); - }, - }); - await getMarketSellOrdersAsync(marketOperationUtils, ORDERS, FILL_AMOUNT, { - ...DEFAULT_OPTS, - excludedSources: [], - includedSources, - }); - expect(_.uniq(sourcesPolled).sort()).to.deep.equals(includedSources.sort()); - }); - - // // TODO (xianny): v4 will have a new way of representing bridge data - // it('generates bridge orders with correct asset data', async () => { - // const improvedOrdersResponse = await getMarketSellOrdersAsync( - // marketOperationUtils, - // // Pass in empty orders to prevent native orders from being used. - // ORDERS.map(o => ({ ...o, makerAmount: constants.ZERO_AMOUNT })), - // FILL_AMOUNT, - // DEFAULT_OPTS, - // ); - // const improvedOrders = improvedOrdersResponse.optimizedOrders; - // expect(improvedOrders).to.not.be.length(0); - // for (const order of improvedOrders) { - // expect(getSourceFromAssetData(order.makerAssetData)).to.exist(''); - // const makerAssetDataPrefix = hexUtils.slice( - // assetDataUtils.encodeERC20BridgeAssetData( - // MAKER_TOKEN, - // constants.NULL_ADDRESS, - // constants.NULL_BYTES, - // ), - // 0, - // 36, - // ); - // assertSamePrefix(order.makerAssetData, makerAssetDataPrefix); - // expect(order.takerAssetData).to.eq(TAKER_ASSET_DATA); - // } - // }); - - it('getMarketSellOrdersAsync() optimizer will be called once only if price-aware RFQ is disabled', async () => { - const mockedMarketOpUtils = TypeMoq.Mock.ofType( - MarketOperationUtils, - TypeMoq.MockBehavior.Loose, - false, - MOCK_SAMPLER, - contractAddresses, - ORDER_DOMAIN, - ); - mockedMarketOpUtils.callBase = true; - - // Ensure that `_generateOptimizedOrdersAsync` is only called once - mockedMarketOpUtils - .setup(m => m._generateOptimizedOrdersAsync(TypeMoq.It.isAny(), TypeMoq.It.isAny())) - .returns(async (a, b) => mockedMarketOpUtils.target._generateOptimizedOrdersAsync(a, b)) - .verifiable(TypeMoq.Times.once()); - - const totalAssetAmount = ORDERS.map(o => o.order.takerAmount).reduce((a, b) => a.plus(b)); - await mockedMarketOpUtils.object.getOptimizerResultAsync( - ORDERS, - totalAssetAmount, - MarketOperation.Sell, - DEFAULT_OPTS, - ); - mockedMarketOpUtils.verifyAll(); - }); - - it('optimizer will send in a comparison price to RFQ providers', async () => { - // Set up mocked quote requestor, will return an order that is better - // than the best of the orders. - const mockedQuoteRequestor = TypeMoq.Mock.ofType(QuoteRequestor, TypeMoq.MockBehavior.Loose, false, {}); - - let requestedComparisonPrice: BigNumber | undefined; - - // to get a comparisonPrice, you need a feeschedule for a native order - const feeSchedule = { - [ERC20BridgeSource.Native]: _.constant({ gas: 1, fee: new BigNumber(1) }), - }; - mockedQuoteRequestor - .setup(mqr => mqr.getMakerUriForSignature(TypeMoq.It.isValue(SIGNATURE))) - .returns(() => 'https://foo.bar'); - mockedQuoteRequestor - .setup(mqr => - mqr.requestRfqtFirmQuotesAsync( - TypeMoq.It.isAny(), - TypeMoq.It.isAny(), - TypeMoq.It.isAny(), - TypeMoq.It.isAny(), - TypeMoq.It.isAny(), - TypeMoq.It.isAny(), - ), - ) - .callback( - ( - _makerToken: string, - _takerToken: string, - _assetFillAmount: BigNumber, - _marketOperation: MarketOperation, - comparisonPrice: BigNumber | undefined, - _options: RfqRequestOpts, - ) => { - requestedComparisonPrice = comparisonPrice; - }, - ) - .returns(async () => { - return [ - { - order: { - ...new RfqOrder({ - makerToken: MAKER_TOKEN, - takerToken: TAKER_TOKEN, - makerAmount: Web3Wrapper.toBaseUnitAmount(321, 6), - takerAmount: Web3Wrapper.toBaseUnitAmount(1, 18), - }), - }, - signature: SIGNATURE, - type: FillQuoteTransformerOrderType.Rfq, - }, - ]; - }); - - // Set up sampler, will only return 1 on-chain order - const mockedMarketOpUtils = TypeMoq.Mock.ofType( - MarketOperationUtils, - TypeMoq.MockBehavior.Loose, - false, - MOCK_SAMPLER, - contractAddresses, - ORDER_DOMAIN, - ); - mockedMarketOpUtils.callBase = true; - mockedMarketOpUtils - .setup(mou => - mou.getMarketSellLiquidityAsync(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()), - ) - .returns(async () => { - return { - side: MarketOperation.Sell, - inputAmount: Web3Wrapper.toBaseUnitAmount(1, 18), - inputToken: MAKER_TOKEN, - outputToken: TAKER_TOKEN, - inputAmountPerEth: Web3Wrapper.toBaseUnitAmount(1, 18), - outputAmountPerEth: Web3Wrapper.toBaseUnitAmount(1, 6), - quoteSourceFilters: new SourceFilters(), - makerTokenDecimals: 6, - takerTokenDecimals: 18, - quotes: { - dexQuotes: [], - rfqtIndicativeQuotes: [], - twoHopQuotes: [], - nativeOrders: [ - { - order: new LimitOrder({ - makerToken: MAKER_TOKEN, - takerToken: TAKER_TOKEN, - makerAmount: Web3Wrapper.toBaseUnitAmount(320, 6), - takerAmount: Web3Wrapper.toBaseUnitAmount(1, 18), - }), - fillableTakerAmount: Web3Wrapper.toBaseUnitAmount(1, 18), - fillableMakerAmount: Web3Wrapper.toBaseUnitAmount(320, 6), - fillableTakerFeeAmount: new BigNumber(0), - type: FillQuoteTransformerOrderType.Limit, - signature: SIGNATURE, - }, - ], - }, - isRfqSupported: true, - blockNumber: 1337420, - }; - }); - const result = await mockedMarketOpUtils.object.getOptimizerResultAsync( - ORDERS, - Web3Wrapper.toBaseUnitAmount(1, 18), - MarketOperation.Sell, - { - ...DEFAULT_OPTS, - feeSchedule, - rfqt: { - isIndicative: false, - integrator: FOO_INTEGRATOR, - takerAddress: randomAddress(), - txOrigin: randomAddress(), - intentOnFilling: true, - quoteRequestor: { - requestRfqtFirmQuotesAsync: mockedQuoteRequestor.object.requestRfqtFirmQuotesAsync, - getMakerUriForSignature: mockedQuoteRequestor.object.getMakerUriForSignature, - } as any, - }, - }, - ); - expect(result.optimizedOrders.length).to.eql(1); - // tslint:disable-next-line:no-unnecessary-type-assertion - expect(requestedComparisonPrice!.toString()).to.eql('320'); - expect(result.optimizedOrders[0].makerAmount.toString()).to.eql('321000000'); - expect(result.optimizedOrders[0].takerAmount.toString()).to.eql('1000000000000000000'); - }); - - it('getMarketSellOrdersAsync() will not rerun the optimizer if no orders are returned', async () => { - // Ensure that `_generateOptimizedOrdersAsync` is only called once - const mockedMarketOpUtils = TypeMoq.Mock.ofType( - MarketOperationUtils, - TypeMoq.MockBehavior.Loose, - false, - MOCK_SAMPLER, - contractAddresses, - ORDER_DOMAIN, - ); - mockedMarketOpUtils.callBase = true; - mockedMarketOpUtils - .setup(m => m._generateOptimizedOrdersAsync(TypeMoq.It.isAny(), TypeMoq.It.isAny())) - .returns(async (a, b) => mockedMarketOpUtils.target._generateOptimizedOrdersAsync(a, b)) - .verifiable(TypeMoq.Times.once()); - - const requestor = getMockedQuoteRequestor('firm', [], TypeMoq.Times.once()); - - const totalAssetAmount = ORDERS.map(o => o.order.takerAmount).reduce((a, b) => a.plus(b)); - await mockedMarketOpUtils.object.getOptimizerResultAsync( - ORDERS, - totalAssetAmount, - MarketOperation.Sell, - { - ...DEFAULT_OPTS, - rfqt: { - isIndicative: false, - integrator: FOO_INTEGRATOR, - takerAddress: randomAddress(), - intentOnFilling: true, - txOrigin: randomAddress(), - quoteRequestor: { - requestRfqtFirmQuotesAsync: requestor.object.requestRfqtFirmQuotesAsync, - } as any, - }, - }, - ); - mockedMarketOpUtils.verifyAll(); - requestor.verifyAll(); - }); - - it('getMarketSellOrdersAsync() will rerun the optimizer if one or more indicative are returned', async () => { - const requestor = getMockedQuoteRequestor('indicative', [ORDERS[0], ORDERS[1]], TypeMoq.Times.once()); - - const numOrdersInCall: number[] = []; - const numIndicativeQuotesInCall: number[] = []; - - const mockedMarketOpUtils = TypeMoq.Mock.ofType( - MarketOperationUtils, - TypeMoq.MockBehavior.Loose, - false, - MOCK_SAMPLER, - contractAddresses, - ORDER_DOMAIN, - ); - mockedMarketOpUtils.callBase = true; - mockedMarketOpUtils - .setup(m => m._generateOptimizedOrdersAsync(TypeMoq.It.isAny(), TypeMoq.It.isAny())) - .callback(async (msl: MarketSideLiquidity, _opts: GenerateOptimizedOrdersOpts) => { - numOrdersInCall.push(msl.quotes.nativeOrders.length); - numIndicativeQuotesInCall.push(msl.quotes.rfqtIndicativeQuotes.length); - }) - .returns(async (a, b) => mockedMarketOpUtils.target._generateOptimizedOrdersAsync(a, b)) - .verifiable(TypeMoq.Times.exactly(2)); - - const totalAssetAmount = ORDERS.map(o => o.order.takerAmount).reduce((a, b) => a.plus(b)); - await mockedMarketOpUtils.object.getOptimizerResultAsync( - ORDERS.slice(2, ORDERS.length), - totalAssetAmount, - MarketOperation.Sell, - { - ...DEFAULT_OPTS, - rfqt: { - isIndicative: true, - integrator: FOO_INTEGRATOR, - takerAddress: randomAddress(), - txOrigin: randomAddress(), - intentOnFilling: true, - quoteRequestor: { - requestRfqtIndicativeQuotesAsync: requestor.object.requestRfqtIndicativeQuotesAsync, - getMakerUriForSignature: requestor.object.getMakerUriForSignature, - } as any, - }, - }, - ); - mockedMarketOpUtils.verifyAll(); - requestor.verifyAll(); - - // The first and second optimizer call contains same number of RFQ orders. - expect(numOrdersInCall.length).to.eql(2); - expect(numOrdersInCall[0]).to.eql(1); - expect(numOrdersInCall[1]).to.eql(1); - - // The first call to optimizer will have no RFQ indicative quotes. The second call will have - // two indicative quotes. - expect(numIndicativeQuotesInCall.length).to.eql(2); - expect(numIndicativeQuotesInCall[0]).to.eql(0); - expect(numIndicativeQuotesInCall[1]).to.eql(2); - }); - - it('getMarketSellOrdersAsync() will rerun the optimizer if one or more RFQ orders are returned', async () => { - const requestor = getMockedQuoteRequestor('firm', [ORDERS[0]], TypeMoq.Times.once()); - - // Ensure that `_generateOptimizedOrdersAsync` is only called once - - // TODO: Ensure fillable amounts increase too - const numOrdersInCall: number[] = []; - const mockedMarketOpUtils = TypeMoq.Mock.ofType( - MarketOperationUtils, - TypeMoq.MockBehavior.Loose, - false, - MOCK_SAMPLER, - contractAddresses, - ORDER_DOMAIN, - ); - mockedMarketOpUtils.callBase = true; - mockedMarketOpUtils - .setup(m => m._generateOptimizedOrdersAsync(TypeMoq.It.isAny(), TypeMoq.It.isAny())) - .callback(async (msl: MarketSideLiquidity, _opts: GenerateOptimizedOrdersOpts) => { - numOrdersInCall.push(msl.quotes.nativeOrders.length); - }) - .returns(async (a, b) => mockedMarketOpUtils.target._generateOptimizedOrdersAsync(a, b)) - .verifiable(TypeMoq.Times.exactly(2)); - - const totalAssetAmount = ORDERS.map(o => o.order.takerAmount).reduce((a, b) => a.plus(b)); - await mockedMarketOpUtils.object.getOptimizerResultAsync( - ORDERS.slice(1, ORDERS.length), - totalAssetAmount, - MarketOperation.Sell, - { - ...DEFAULT_OPTS, - rfqt: { - isIndicative: false, - integrator: { - integratorId: 'foo', - label: 'foo', - }, - takerAddress: randomAddress(), - intentOnFilling: true, - txOrigin: randomAddress(), - quoteRequestor: { - requestRfqtFirmQuotesAsync: requestor.object.requestRfqtFirmQuotesAsync, - } as any, - }, - }, - ); - mockedMarketOpUtils.verifyAll(); - requestor.verifyAll(); - expect(numOrdersInCall.length).to.eql(2); - - // The first call to optimizer was without an RFQ order. - // The first call to optimizer was with an extra RFQ order. - expect(numOrdersInCall[0]).to.eql(2); - expect(numOrdersInCall[1]).to.eql(3); - }); - - it('getMarketSellOrdersAsync() will not raise a NoOptimalPath error if no initial path was found during on-chain DEX optimization, but a path was found after RFQ optimization', async () => { - let hasFirstOptimizationRun = false; - let hasSecondOptimizationRun = false; - const requestor = getMockedQuoteRequestor('firm', [ORDERS[0], ORDERS[1]], TypeMoq.Times.once()); - - const mockedMarketOpUtils = TypeMoq.Mock.ofType( - MarketOperationUtils, - TypeMoq.MockBehavior.Loose, - false, - MOCK_SAMPLER, - contractAddresses, - ORDER_DOMAIN, - ); - mockedMarketOpUtils.callBase = true; - mockedMarketOpUtils - .setup(m => m._generateOptimizedOrdersAsync(TypeMoq.It.isAny(), TypeMoq.It.isAny())) - .returns(async (msl: MarketSideLiquidity, _opts: GenerateOptimizedOrdersOpts) => { - if (msl.quotes.nativeOrders.length === 1) { - hasFirstOptimizationRun = true; - throw new Error(AggregationError.NoOptimalPath); - } else if (msl.quotes.nativeOrders.length === 3) { - hasSecondOptimizationRun = true; - return mockedMarketOpUtils.target._generateOptimizedOrdersAsync(msl, _opts); - } else { - throw new Error('Invalid path. this error message should never appear'); - } - }) - .verifiable(TypeMoq.Times.exactly(2)); - - const totalAssetAmount = ORDERS.map(o => o.order.takerAmount).reduce((a, b) => a.plus(b)); - await mockedMarketOpUtils.object.getOptimizerResultAsync( - ORDERS.slice(2, ORDERS.length), - totalAssetAmount, - MarketOperation.Sell, - { - ...DEFAULT_OPTS, - rfqt: { - isIndicative: false, - integrator: FOO_INTEGRATOR, - takerAddress: randomAddress(), - txOrigin: randomAddress(), - intentOnFilling: true, - quoteRequestor: { - requestRfqtFirmQuotesAsync: requestor.object.requestRfqtFirmQuotesAsync, - } as any, - }, - }, - ); - mockedMarketOpUtils.verifyAll(); - requestor.verifyAll(); - - expect(hasFirstOptimizationRun).to.eql(true); - expect(hasSecondOptimizationRun).to.eql(true); - }); - - it('getMarketSellOrdersAsync() will raise a NoOptimalPath error if no path was found during on-chain DEX optimization and RFQ optimization', async () => { - const mockedMarketOpUtils = TypeMoq.Mock.ofType( - MarketOperationUtils, - TypeMoq.MockBehavior.Loose, - false, - MOCK_SAMPLER, - contractAddresses, - ORDER_DOMAIN, - ); - mockedMarketOpUtils.callBase = true; - mockedMarketOpUtils - .setup(m => m._generateOptimizedOrdersAsync(TypeMoq.It.isAny(), TypeMoq.It.isAny())) - .returns(async (msl: MarketSideLiquidity, _opts: GenerateOptimizedOrdersOpts) => { - throw new Error(AggregationError.NoOptimalPath); - }) - .verifiable(TypeMoq.Times.exactly(1)); - - try { - await mockedMarketOpUtils.object.getOptimizerResultAsync( - ORDERS.slice(2, ORDERS.length), - ORDERS[0].order.takerAmount, - MarketOperation.Sell, - DEFAULT_OPTS, - ); - expect.fail(`Call should have thrown "${AggregationError.NoOptimalPath}" but instead succeded`); - } catch (e) { - if (e.message !== AggregationError.NoOptimalPath) { - expect.fail(e); - } - } - mockedMarketOpUtils.verifyAll(); - }); - - it('generates bridge orders with correct taker amount', async () => { - const improvedOrdersResponse = await getMarketSellOrdersAsync( - marketOperationUtils, - // Pass in empty orders to prevent native orders from being used. - ORDERS.map(o => ({ ...o, makerAmount: constants.ZERO_AMOUNT })), - FILL_AMOUNT, - DEFAULT_OPTS, - ); - const improvedOrders = improvedOrdersResponse.optimizedOrders; - const totaltakerAmount = BigNumber.sum(...improvedOrders.map(o => o.takerAmount)); - expect(totaltakerAmount).to.bignumber.gte(FILL_AMOUNT); - }); - - it('generates bridge orders with max slippage of `bridgeSlippage`', async () => { - const bridgeSlippage = _.random(0.1, true); - const improvedOrdersResponse = await getMarketSellOrdersAsync( - marketOperationUtils, - // Pass in empty orders to prevent native orders from being used. - ORDERS.map(o => ({ ...o, makerAmount: constants.ZERO_AMOUNT })), - FILL_AMOUNT, - { ...DEFAULT_OPTS, bridgeSlippage }, - ); - const improvedOrders = improvedOrdersResponse.optimizedOrders; - expect(improvedOrders).to.not.be.length(0); - for (const order of improvedOrders) { - const expectedMakerAmount = order.fill.output; - const slippage = new BigNumber(1).minus(order.makerAmount.div(expectedMakerAmount.plus(1))); - assertRoughlyEquals(slippage, bridgeSlippage, 1); - } - }); - - it('can mix convex sources', async () => { - const rates: RatesBySource = { ...DEFAULT_RATES }; - rates[ERC20BridgeSource.Native] = [0.4, 0.3, 0.2, 0.1]; - rates[ERC20BridgeSource.Uniswap] = [0.5, 0.05, 0.05, 0.05]; - rates[ERC20BridgeSource.SushiSwap] = [0.6, 0.05, 0.05, 0.05]; - rates[ERC20BridgeSource.Curve] = [0, 0, 0, 0]; // unused - replaceSamplerOps({ - getSellQuotes: createGetMultipleSellQuotesOperationFromRates(rates), - }); - const improvedOrdersResponse = await getMarketSellOrdersAsync( - marketOperationUtils, - createOrdersFromSellRates(FILL_AMOUNT, rates[ERC20BridgeSource.Native]), - FILL_AMOUNT, - { ...DEFAULT_OPTS, numSamples: 4 }, - ); - const improvedOrders = improvedOrdersResponse.optimizedOrders; - const orderSources = improvedOrders.map(o => o.source); - const expectedSources = [ - ERC20BridgeSource.SushiSwap, - ERC20BridgeSource.Uniswap, - ERC20BridgeSource.Native, - ERC20BridgeSource.Native, - ]; - expect(orderSources.sort()).to.deep.eq(expectedSources.sort()); - }); - - const ETH_TO_MAKER_RATE = 1.5; - - // TODO: disabled as this is not supported by neon-router - it.skip('factors in fees for native orders', async () => { - // Native orders will have the best rates but have fees, - // dropping their effective rates. - const nativeFeeRate = 0.06; - const rates: RatesBySource = { - [ERC20BridgeSource.Native]: [1, 0.99, 0.98, 0.97], // Effectively [0.94, 0.93, 0.92, 0.91] - [ERC20BridgeSource.Uniswap]: [0.96, 0.1, 0.1, 0.1], - [ERC20BridgeSource.SushiSwap]: [0.95, 0.1, 0.1, 0.1], - }; - const feeSchedule = { - [ERC20BridgeSource.Native]: _.constant({ - gas: 1, - fee: FILL_AMOUNT.div(4) - .times(nativeFeeRate) - .dividedToIntegerBy(ETH_TO_MAKER_RATE), - }), - }; - replaceSamplerOps({ - getSellQuotes: createGetMultipleSellQuotesOperationFromRates(rates), - getBestNativeTokenSellRate: createGetBestNativeSellRate(ETH_TO_MAKER_RATE), - }); - const improvedOrdersResponse = await getMarketSellOrdersAsync( - marketOperationUtils, - createOrdersFromSellRates(FILL_AMOUNT, rates[ERC20BridgeSource.Native]), - FILL_AMOUNT, - { ...DEFAULT_OPTS, numSamples: 4, feeSchedule }, - ); - const improvedOrders = improvedOrdersResponse.optimizedOrders; - const orderSources = improvedOrders.map(o => o.source); - const expectedSources = [ - ERC20BridgeSource.Native, - ERC20BridgeSource.Uniswap, - ERC20BridgeSource.SushiSwap, - ERC20BridgeSource.Native, - ]; - expect(orderSources.sort()).to.deep.eq(expectedSources.sort()); - }); - - it('factors in fees for dexes', async () => { - const uniswapFeeRate = 0.2; - const rates: RatesBySource = { - [ERC20BridgeSource.Native]: [0.95, 0.1, 0.1, 0.1], - [ERC20BridgeSource.Curve]: [0.1, 0.1, 0.1, 0.1], - [ERC20BridgeSource.SushiSwap]: [0.92, 0.1, 0.1, 0.1], - // Effectively [0.8, ~0.5, ~0, ~0] - [ERC20BridgeSource.Uniswap]: [1, 0.7, 0.2, 0.2], - }; - const feeSchedule = { - [ERC20BridgeSource.Uniswap]: _.constant({ - gas: 1, - fee: FILL_AMOUNT.div(4) - .times(uniswapFeeRate) - .dividedToIntegerBy(ETH_TO_MAKER_RATE), - }), - }; - replaceSamplerOps({ - getSellQuotes: createGetMultipleSellQuotesOperationFromRates(rates), - getBestNativeTokenSellRate: createGetBestNativeSellRate(ETH_TO_MAKER_RATE), - }); - const improvedOrdersResponse = await getMarketSellOrdersAsync( - marketOperationUtils, - createOrdersFromSellRates(FILL_AMOUNT, rates[ERC20BridgeSource.Native]), - FILL_AMOUNT, - { ...DEFAULT_OPTS, numSamples: 4, feeSchedule }, - ); - const improvedOrders = improvedOrdersResponse.optimizedOrders; - const orderSources = improvedOrders.map(o => o.source); - const expectedSources = [ - ERC20BridgeSource.Native, - ERC20BridgeSource.SushiSwap, - ERC20BridgeSource.Uniswap, - ]; - expect(orderSources.sort()).to.deep.eq(expectedSources.sort()); - }); - - it('can mix one concave source', async () => { - const rates: RatesBySource = { - [ERC20BridgeSource.Curve]: [0, 0, 0, 0], // Won't use - [ERC20BridgeSource.SushiSwap]: [0.5, 0.85, 0.75, 0.75], // Concave - [ERC20BridgeSource.Uniswap]: [0.96, 0.2, 0.1, 0.1], - [ERC20BridgeSource.Native]: [0.95, 0.2, 0.2, 0.1], - }; - replaceSamplerOps({ - getSellQuotes: createGetMultipleSellQuotesOperationFromRates(rates), - getBestNativeTokenSellRate: createGetBestNativeSellRate(ETH_TO_MAKER_RATE), - }); - const improvedOrdersResponse = await getMarketSellOrdersAsync( - marketOperationUtils, - createOrdersFromSellRates(FILL_AMOUNT, rates[ERC20BridgeSource.Native]), - FILL_AMOUNT, - { ...DEFAULT_OPTS, numSamples: 4 }, - ); - const improvedOrders = improvedOrdersResponse.optimizedOrders; - const orderSources = improvedOrders.map(o => o.source); - const expectedSources = [ - ERC20BridgeSource.SushiSwap, - ERC20BridgeSource.Uniswap, - ERC20BridgeSource.Native, - ]; - expect(orderSources.sort()).to.deep.eq(expectedSources.sort()); - }); - - // NOTE: Currently fallbacks for native orders are disabled - // TODO: remove this if we remove fallbacks completely - it.skip('does not create a fallback if below maxFallbackSlippage', async () => { - const rates: RatesBySource = {}; - rates[ERC20BridgeSource.Native] = [1, 1, 0.01, 0.01]; - rates[ERC20BridgeSource.Uniswap] = [1, 1, 0.01, 0.01]; - rates[ERC20BridgeSource.SushiSwap] = [0.49, 0.49, 0.49, 0.49]; - rates[ERC20BridgeSource.Curve] = [0.35, 0.2, 0.01, 0.01]; - replaceSamplerOps({ - getSellQuotes: createGetMultipleSellQuotesOperationFromRates(rates), - }); - const improvedOrdersResponse = await getMarketSellOrdersAsync( - marketOperationUtils, - createOrdersFromSellRates(FILL_AMOUNT, rates[ERC20BridgeSource.Native]), - FILL_AMOUNT, - { ...DEFAULT_OPTS, numSamples: 4, allowFallback: true, maxFallbackSlippage: 0.25 }, - ); - const improvedOrders = improvedOrdersResponse.optimizedOrders; - const orderSources = improvedOrders.map(o => o.source); - const firstSources = [ERC20BridgeSource.Native, ERC20BridgeSource.Native, ERC20BridgeSource.Uniswap]; - const secondSources: ERC20BridgeSource[] = []; - expect(orderSources.slice(0, firstSources.length).sort()).to.deep.eq(firstSources.sort()); - expect(orderSources.slice(firstSources.length).sort()).to.deep.eq(secondSources.sort()); - }); - - it('is able to create a order from LiquidityProvider', async () => { - const liquidityProviderAddress = (DEFAULT_FILL_DATA[ERC20BridgeSource.LiquidityProvider] as any) - .poolAddress; - const rates: RatesBySource = {}; - rates[ERC20BridgeSource.LiquidityProvider] = [1, 1, 1, 1]; - MOCK_SAMPLER.liquidityProviderRegistry[liquidityProviderAddress] = { - tokens: [MAKER_TOKEN, TAKER_TOKEN], - gasCost: 0, - }; - replaceSamplerOps({ - getLimitOrderFillableTakerAmounts: () => [constants.ZERO_AMOUNT], - getSellQuotes: createGetMultipleSellQuotesOperationFromRates(rates), - }); - - const sampler = new MarketOperationUtils(MOCK_SAMPLER, contractAddresses, ORDER_DOMAIN); - const ordersAndReport = await sampler.getOptimizerResultAsync( - [ - { - order: new LimitOrder({ - makerToken: MAKER_TOKEN, - takerToken: TAKER_TOKEN, - }), - type: FillQuoteTransformerOrderType.Limit, - signature: {} as any, - }, - ], - FILL_AMOUNT, - MarketOperation.Sell, - { - includedSources: [ERC20BridgeSource.LiquidityProvider], - excludedSources: [], - numSamples: 4, - bridgeSlippage: 0, - gasPrice: new BigNumber(30e9), - }, - ); - const result = ordersAndReport.optimizedOrders; - expect(result.length).to.eql(1); - expect( - (result[0] as OptimizedMarketBridgeOrder).fillData.poolAddress, - ).to.eql(liquidityProviderAddress); - - // // TODO (xianny): decode bridge data in v4 format - // // tslint:disable-next-line:no-unnecessary-type-assertion - // const decodedAssetData = assetDataUtils.decodeAssetDataOrThrow( - // result[0].makerAssetData, - // ) as ERC20BridgeAssetData; - // expect(decodedAssetData.assetProxyId).to.eql(AssetProxyId.ERC20Bridge); - // expect(decodedAssetData.bridgeAddress).to.eql(liquidityProviderAddress); - // expect(result[0].takerAmount).to.bignumber.eql(FILL_AMOUNT); - }); - - it('factors in exchange proxy gas overhead', async () => { - // Uniswap has a slightly better rate than LiquidityProvider, - // but LiquidityProvider is better accounting for the EP gas overhead. - const rates: RatesBySource = { - [ERC20BridgeSource.Native]: [0.01, 0.01, 0.01, 0.01], - [ERC20BridgeSource.Uniswap]: [1, 1, 1, 1], - [ERC20BridgeSource.LiquidityProvider]: [0.9999, 0.9999, 0.9999, 0.9999], - }; - MOCK_SAMPLER.liquidityProviderRegistry[randomAddress()] = { - tokens: [MAKER_TOKEN, TAKER_TOKEN], - gasCost: 0, - }; - replaceSamplerOps({ - getSellQuotes: createGetMultipleSellQuotesOperationFromRates(rates), - getBestNativeTokenSellRate: createGetBestNativeSellRate(ETH_TO_MAKER_RATE), - }); - const optimizer = new MarketOperationUtils(MOCK_SAMPLER, contractAddresses, ORDER_DOMAIN); - const gasPrice = 100e9; // 100 gwei - const exchangeProxyOverhead = (sourceFlags: bigint) => - sourceFlags === SOURCE_FLAGS.LiquidityProvider - ? constants.ZERO_AMOUNT - : new BigNumber(1.3e5).times(gasPrice); - const improvedOrdersResponse = await optimizer.getOptimizerResultAsync( - createOrdersFromSellRates(FILL_AMOUNT, rates[ERC20BridgeSource.Native]), - FILL_AMOUNT, - MarketOperation.Sell, - { - ...DEFAULT_OPTS, - numSamples: 4, - includedSources: [ - ERC20BridgeSource.Native, - ERC20BridgeSource.Uniswap, - ERC20BridgeSource.LiquidityProvider, - ], - excludedSources: [], - exchangeProxyOverhead, - }, - ); - const improvedOrders = improvedOrdersResponse.optimizedOrders; - const orderSources = improvedOrders.map(o => o.source); - const expectedSources = [ERC20BridgeSource.LiquidityProvider]; - expect(orderSources).to.deep.eq(expectedSources); - }); - }); - - describe('getMarketBuyOrdersAsync()', () => { - const FILL_AMOUNT = new BigNumber('100e18'); - const ORDERS = createOrdersFromBuyRates( - FILL_AMOUNT, - _.times(NUM_SAMPLES, () => DEFAULT_RATES[ERC20BridgeSource.Native][0]), - ); - const GAS_PRICE = new BigNumber(100e9); // 100 gwei - const DEFAULT_OPTS: Partial & { gasPrice: BigNumber } = { - numSamples: NUM_SAMPLES, - sampleDistributionBase: 1, - bridgeSlippage: 0, - maxFallbackSlippage: 100, - excludedSources: DEFAULT_EXCLUDED, - allowFallback: false, - gasSchedule: {}, - feeSchedule: {}, - gasPrice: GAS_PRICE, - }; - - beforeEach(() => { - replaceSamplerOps(); - }); - - it('queries `numSamples` samples', async () => { - // neon-router requires at least 3 samples - const numSamples = _.random(3, 16); - let actualNumSamples = 0; - replaceSamplerOps({ - getBuyQuotes: (sources, makerToken, takerToken, amounts, wethAddress) => { - actualNumSamples = amounts.length; - return DEFAULT_OPS.getBuyQuotes( - sources, - makerToken, - takerToken, - amounts, - wethAddress, - TOKEN_ADJACENCY_GRAPH, - ); - }, - }); - await getMarketBuyOrdersAsync(marketOperationUtils, ORDERS, FILL_AMOUNT, { - ...DEFAULT_OPTS, - numSamples, - // Make sure to use same number of samples in neon-router for compatibility - neonRouterNumSamples: numSamples, - }); - expect(actualNumSamples).eq(numSamples); - }); - - it('polls all DEXes if `excludedSources` is empty', async () => { - let sourcesPolled: ERC20BridgeSource[] = []; - replaceSamplerOps({ - getBuyQuotes: (sources, makerToken, takerToken, amounts, wethAddress) => { - sourcesPolled = sourcesPolled.concat(sources.slice()); - return DEFAULT_OPS.getBuyQuotes( - sources, - makerToken, - takerToken, - amounts, - wethAddress, - TOKEN_ADJACENCY_GRAPH, - ); - }, - getTwoHopBuyQuotes: (sources: ERC20BridgeSource[], ..._args: any[]) => { - if (sources.length !== 0) { - sourcesPolled.push(ERC20BridgeSource.MultiHop); - sourcesPolled.push(...sources); - } - return DEFAULT_OPS.getTwoHopBuyQuotes(..._args); - }, - }); - await getMarketBuyOrdersAsync(marketOperationUtils, ORDERS, FILL_AMOUNT, { - ...DEFAULT_OPTS, - excludedSources: [], - }); - expect(_.uniq(sourcesPolled).sort()).to.deep.equals(BUY_SOURCES.sort()); - }); - - it('does not poll DEXes in `excludedSources`', async () => { - const excludedSources = [ERC20BridgeSource.Uniswap, ERC20BridgeSource.SushiSwap]; - let sourcesPolled: ERC20BridgeSource[] = []; - replaceSamplerOps({ - getBuyQuotes: (sources, makerToken, takerToken, amounts, wethAddress) => { - sourcesPolled = sourcesPolled.concat(sources.slice()); - return DEFAULT_OPS.getBuyQuotes( - sources, - makerToken, - takerToken, - amounts, - wethAddress, - TOKEN_ADJACENCY_GRAPH, - ); - }, - getTwoHopBuyQuotes: (sources: ERC20BridgeSource[], ..._args: any[]) => { - if (sources.length !== 0) { - sourcesPolled.push(ERC20BridgeSource.MultiHop); - sourcesPolled.push(...sources); - } - return DEFAULT_OPS.getTwoHopBuyQuotes(..._args); - }, - }); - await getMarketBuyOrdersAsync(marketOperationUtils, ORDERS, FILL_AMOUNT, { - ...DEFAULT_OPTS, - excludedSources, - }); - expect(_.uniq(sourcesPolled).sort()).to.deep.eq(_.without(BUY_SOURCES, ...excludedSources).sort()); - }); - - it('only polls DEXes in `includedSources`', async () => { - const includedSources = [ERC20BridgeSource.Uniswap, ERC20BridgeSource.SushiSwap]; - let sourcesPolled: ERC20BridgeSource[] = []; - replaceSamplerOps({ - getBuyQuotes: (sources, makerToken, takerToken, amounts, wethAddress) => { - sourcesPolled = sourcesPolled.concat(sources.slice()); - return DEFAULT_OPS.getBuyQuotes( - sources, - makerToken, - takerToken, - amounts, - wethAddress, - TOKEN_ADJACENCY_GRAPH, - ); - }, - getTwoHopBuyQuotes: (sources: ERC20BridgeSource[], ..._args: any[]) => { - if (sources.length !== 0) { - sourcesPolled.push(ERC20BridgeSource.MultiHop); - sourcesPolled.push(...sources); - } - return DEFAULT_OPS.getTwoHopBuyQuotes(..._args); - }, - }); - await getMarketBuyOrdersAsync(marketOperationUtils, ORDERS, FILL_AMOUNT, { - ...DEFAULT_OPTS, - excludedSources: [], - includedSources, - }); - expect(_.uniq(sourcesPolled).sort()).to.deep.eq(includedSources.sort()); - }); - - // it('generates bridge orders with correct asset data', async () => { - // const improvedOrdersResponse = await getMarketBuyOrdersAsync( - // marketOperationUtils, - // // Pass in empty orders to prevent native orders from being used. - // ORDERS.map(o => ({ ...o, makerAmount: constants.ZERO_AMOUNT })), - // FILL_AMOUNT, - // DEFAULT_OPTS, - // ); - // const improvedOrders = improvedOrdersResponse.optimizedOrders; - // expect(improvedOrders).to.not.be.length(0); - // for (const order of improvedOrders) { - // expect(getSourceFromAssetData(order.makerAssetData)).to.exist(''); - // const makerAssetDataPrefix = hexUtils.slice( - // assetDataUtils.encodeERC20BridgeAssetData( - // MAKER_TOKEN, - // constants.NULL_ADDRESS, - // constants.NULL_BYTES, - // ), - // 0, - // 36, - // ); - // assertSamePrefix(order.makerAssetData, makerAssetDataPrefix); - // expect(order.takerAssetData).to.eq(TAKER_ASSET_DATA); - // } - // }); - - it('generates bridge orders with correct maker amount', async () => { - const improvedOrdersResponse = await getMarketBuyOrdersAsync( - marketOperationUtils, - // Pass in empty orders to prevent native orders from being used. - ORDERS.map(o => ({ ...o, makerAmount: constants.ZERO_AMOUNT })), - FILL_AMOUNT, - DEFAULT_OPTS, - ); - const improvedOrders = improvedOrdersResponse.optimizedOrders; - const totalmakerAmount = BigNumber.sum(...improvedOrders.map(o => o.makerAmount)); - expect(totalmakerAmount).to.bignumber.gte(FILL_AMOUNT); - }); - - it('generates bridge orders with max slippage of `bridgeSlippage`', async () => { - const bridgeSlippage = _.random(0.1, true); - const improvedOrdersResponse = await getMarketBuyOrdersAsync( - marketOperationUtils, - // Pass in empty orders to prevent native orders from being used. - ORDERS.map(o => ({ ...o, makerAmount: constants.ZERO_AMOUNT })), - FILL_AMOUNT, - { ...DEFAULT_OPTS, bridgeSlippage }, - ); - const improvedOrders = improvedOrdersResponse.optimizedOrders; - expect(improvedOrders).to.not.be.length(0); - for (const order of improvedOrders) { - const expectedTakerAmount = order.fill.output; - const slippage = order.takerAmount.div(expectedTakerAmount.plus(1)).minus(1); - assertRoughlyEquals(slippage, bridgeSlippage, 1); - } - }); - - // TODO: disabled as this is not supported by neon-router - it.skip('can mix convex sources', async () => { - const rates: RatesBySource = { ...ZERO_RATES }; - rates[ERC20BridgeSource.Native] = [0.4, 0.3, 0.2, 0.1]; - rates[ERC20BridgeSource.Uniswap] = [0.5, 0.05, 0.05, 0.05]; - rates[ERC20BridgeSource.SushiSwap] = [0.6, 0.05, 0.05, 0.05]; - replaceSamplerOps({ - getBuyQuotes: createGetMultipleBuyQuotesOperationFromRates(rates), - }); - const improvedOrdersResponse = await getMarketBuyOrdersAsync( - marketOperationUtils, - createOrdersFromBuyRates(FILL_AMOUNT, rates[ERC20BridgeSource.Native]), - FILL_AMOUNT, - { ...DEFAULT_OPTS, numSamples: 4 }, - ); - const improvedOrders = improvedOrdersResponse.optimizedOrders; - const orderSources = improvedOrders.map(o => o.source); - const expectedSources = [ - ERC20BridgeSource.SushiSwap, - ERC20BridgeSource.Uniswap, - ERC20BridgeSource.Native, - ERC20BridgeSource.Native, - ]; - expect(orderSources.sort()).to.deep.eq(expectedSources.sort()); - }); - - const ETH_TO_TAKER_RATE = 1.5; - - // TODO: disabled as this is not supported by neon-router - it.skip('factors in fees for native orders', async () => { - // Native orders will have the best rates but have fees, - // dropping their effective rates. - const nativeFeeRate = 0.06; - const rates: RatesBySource = { - ...ZERO_RATES, - [ERC20BridgeSource.Native]: [1, 0.99, 0.98, 0.97], // Effectively [0.94, ~0.93, ~0.92, ~0.91] - [ERC20BridgeSource.Uniswap]: [0.96, 0.1, 0.1, 0.1], - [ERC20BridgeSource.SushiSwap]: [0.95, 0.1, 0.1, 0.1], - [ERC20BridgeSource.Curve]: [0.1, 0.1, 0.1, 0.1], - }; - const feeSchedule = { - [ERC20BridgeSource.Native]: _.constant({ - gas: 1, - fee: FILL_AMOUNT.div(4) - .times(nativeFeeRate) - .dividedToIntegerBy(ETH_TO_TAKER_RATE), - }), - }; - replaceSamplerOps({ - getBuyQuotes: createGetMultipleBuyQuotesOperationFromRates(rates), - getBestNativeTokenSellRate: createGetBestNativeSellRate(ETH_TO_TAKER_RATE), - }); - const improvedOrdersResponse = await getMarketBuyOrdersAsync( - marketOperationUtils, - createOrdersFromBuyRates(FILL_AMOUNT, rates[ERC20BridgeSource.Native]), - FILL_AMOUNT, - { ...DEFAULT_OPTS, numSamples: 4, feeSchedule }, - ); - const improvedOrders = improvedOrdersResponse.optimizedOrders; - const orderSources = improvedOrders.map(o => o.source); - const expectedSources = [ - ERC20BridgeSource.Uniswap, - ERC20BridgeSource.SushiSwap, - ERC20BridgeSource.Native, - ERC20BridgeSource.Native, - ]; - expect(orderSources.sort()).to.deep.eq(expectedSources.sort()); - }); - - it('factors in fees for dexes', async () => { - // Uniswap will have the best rates but will have fees, - // dropping its effective rates. - const uniswapFeeRate = 0.2; - const rates: RatesBySource = { - ...ZERO_RATES, - [ERC20BridgeSource.Native]: [0.95, 0.1, 0.1, 0.1], - // Effectively [0.8, ~0.5, ~0, ~0] - [ERC20BridgeSource.Uniswap]: [1, 0.7, 0.2, 0.2], - [ERC20BridgeSource.SushiSwap]: [0.92, 0.1, 0.1, 0.1], - }; - const feeSchedule = { - [ERC20BridgeSource.Uniswap]: _.constant({ - gas: 1, - fee: FILL_AMOUNT.div(4) - .times(uniswapFeeRate) - .dividedToIntegerBy(ETH_TO_TAKER_RATE), - }), - }; - replaceSamplerOps({ - getBuyQuotes: createGetMultipleBuyQuotesOperationFromRates(rates), - getBestNativeTokenSellRate: createGetBestNativeSellRate(ETH_TO_TAKER_RATE), - }); - const improvedOrdersResponse = await getMarketBuyOrdersAsync( - marketOperationUtils, - createOrdersFromBuyRates(FILL_AMOUNT, rates[ERC20BridgeSource.Native]), - FILL_AMOUNT, - { ...DEFAULT_OPTS, numSamples: 4, feeSchedule }, - ); - const improvedOrders = improvedOrdersResponse.optimizedOrders; - const orderSources = improvedOrders.map(o => o.source); - const expectedSources = [ - ERC20BridgeSource.Native, - ERC20BridgeSource.SushiSwap, - ERC20BridgeSource.Uniswap, - ]; - expect(orderSources.sort()).to.deep.eq(expectedSources.sort()); - }); - - // NOTE: Currently fallbacks for native orders are disabled - // TODO: remove this if we remove fallbacks completely - it.skip('does not create a fallback if below maxFallbackSlippage', async () => { - const rates: RatesBySource = { ...ZERO_RATES }; - rates[ERC20BridgeSource.Native] = [1, 1, 0.01, 0.01]; - rates[ERC20BridgeSource.Uniswap] = [1, 1, 0.01, 0.01]; - rates[ERC20BridgeSource.SushiSwap] = [0.49, 0.49, 0.49, 0.49]; - replaceSamplerOps({ - getBuyQuotes: createGetMultipleBuyQuotesOperationFromRates(rates), - }); - const improvedOrdersResponse = await getMarketBuyOrdersAsync( - marketOperationUtils, - createOrdersFromBuyRates(FILL_AMOUNT, rates[ERC20BridgeSource.Native]), - FILL_AMOUNT, - { ...DEFAULT_OPTS, numSamples: 4, allowFallback: true, maxFallbackSlippage: 0.25 }, - ); - const improvedOrders = improvedOrdersResponse.optimizedOrders; - const orderSources = improvedOrders.map(o => o.source); - const firstSources = [ERC20BridgeSource.Native, ERC20BridgeSource.Native, ERC20BridgeSource.Uniswap]; - const secondSources: ERC20BridgeSource[] = []; - expect(orderSources.slice(0, firstSources.length).sort()).to.deep.eq(firstSources.sort()); - expect(orderSources.slice(firstSources.length).sort()).to.deep.eq(secondSources.sort()); - }); - - it('factors in exchange proxy gas overhead', async () => { - // Uniswap has a slightly better rate than LiquidityProvider, - // but LiquidityProvider is better accounting for the EP gas overhead. - const rates: RatesBySource = { - [ERC20BridgeSource.Native]: [0.01, 0.01, 0.01, 0.01], - [ERC20BridgeSource.Uniswap]: [1, 1, 1, 1], - [ERC20BridgeSource.LiquidityProvider]: [0.9999, 0.9999, 0.9999, 0.9999], - }; - MOCK_SAMPLER.liquidityProviderRegistry[randomAddress()] = { - tokens: [MAKER_TOKEN, TAKER_TOKEN], - gasCost: 0, - }; - replaceSamplerOps({ - getBuyQuotes: createGetMultipleBuyQuotesOperationFromRates(rates), - getBestNativeTokenSellRate: createGetBestNativeSellRate(ETH_TO_TAKER_RATE), - }); - const optimizer = new MarketOperationUtils(MOCK_SAMPLER, contractAddresses, ORDER_DOMAIN); - const exchangeProxyOverhead = (sourceFlags: bigint) => - sourceFlags === SOURCE_FLAGS.LiquidityProvider - ? constants.ZERO_AMOUNT - : new BigNumber(1.3e5).times(GAS_PRICE); - const improvedOrdersResponse = await optimizer.getOptimizerResultAsync( - createOrdersFromSellRates(FILL_AMOUNT, rates[ERC20BridgeSource.Native]), - FILL_AMOUNT, - MarketOperation.Buy, - { - ...DEFAULT_OPTS, - numSamples: 4, - includedSources: [ - ERC20BridgeSource.Native, - ERC20BridgeSource.Uniswap, - ERC20BridgeSource.LiquidityProvider, - ], - excludedSources: [], - exchangeProxyOverhead, - }, - ); - const improvedOrders = improvedOrdersResponse.optimizedOrders; - const orderSources = improvedOrders.map(o => o.source); - const expectedSources = [ERC20BridgeSource.LiquidityProvider]; - expect(orderSources).to.deep.eq(expectedSources); - }); - }); - }); -}); -// tslint:disable-next-line: max-file-line-count diff --git a/packages/asset-swapper/test/pools_cache_test.ts b/packages/asset-swapper/test/pools_cache_test.ts deleted file mode 100644 index 3ac2adc639..0000000000 --- a/packages/asset-swapper/test/pools_cache_test.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { ChainId } from '@0x/contract-addresses'; -import * as chai from 'chai'; -import 'mocha'; - -import { BalancerPoolsCache, BalancerV2PoolsCache, PoolsCache } from '../src/utils/market_operation_utils/pools_cache'; - -import { chaiSetup } from './utils/chai_setup'; - -chaiSetup.configure(); -const expect = chai.expect; - -const usdcAddress = '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48'; -const daiAddress = '0x6b175474e89094c44da98b954eedeac495271d0f'; -const wethAddress = '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2'; - -const timeoutMs = 5000; -const poolKeys: string[] = ['id', 'balanceIn', 'balanceOut', 'weightIn', 'weightOut', 'swapFee']; - -describe('Pools Caches for Balancer-based sampling', () => { - async function fetchAndAssertPoolsAsync(cache: PoolsCache, takerToken: string, makerToken: string): Promise { - const pools = await cache.getFreshPoolsForPairAsync(takerToken, makerToken, timeoutMs); - expect(pools.length).greaterThan(0, `Failed to find any pools for ${takerToken} and ${makerToken}`); - expect(pools[0]).not.undefined(); - expect(Object.keys(pools[0])).to.include.members(poolKeys); - const cachedPoolIds = cache.getPoolAddressesForPair(takerToken, makerToken); - expect(cachedPoolIds).to.deep.equal(pools.map(p => p.id)); - } - - describe('BalancerPoolsCache', () => { - const cache = BalancerPoolsCache.create(ChainId.Mainnet); - it('fetches pools', async () => { - const pairs = [ - [usdcAddress, daiAddress], - [usdcAddress, wethAddress], - [daiAddress, wethAddress], - ]; - await Promise.all( - pairs.map(async ([takerToken, makerToken]) => fetchAndAssertPoolsAsync(cache, takerToken, makerToken)), - ); - }); - }); - - describe('BalancerV2PoolsCache', () => { - it('fetches pools (Beethoven X - Fantom)', async () => { - const cache = BalancerV2PoolsCache.createBeethovenXPoolCache(ChainId.Fantom); - const wftmAddress = '0x21be370d5312f44cb42ce377bc9b8a0cef1a4c83'; - const beetsAddress = '0xf24bcf4d1e507740041c9cfd2dddb29585adce1e'; - const fantomWethAddress = '0x74b23882a30290451a17c44f4f05243b6b58c76d'; - - const pairs = [ - [wftmAddress, beetsAddress], - [wftmAddress, fantomWethAddress], - ]; - - await Promise.all( - pairs.map(async ([takerToken, makerToken]) => fetchAndAssertPoolsAsync(cache, takerToken, makerToken)), - ); - }); - }); -}); diff --git a/packages/asset-swapper/test/protocol_fee_utils_test.ts b/packages/asset-swapper/test/protocol_fee_utils_test.ts deleted file mode 100644 index 1998a9c7fc..0000000000 --- a/packages/asset-swapper/test/protocol_fee_utils_test.ts +++ /dev/null @@ -1,45 +0,0 @@ -import * as chai from 'chai'; -import 'mocha'; -import { rest } from 'msw'; -import { setupServer } from 'msw/node'; - -import { ProtocolFeeUtils } from '../src'; - -import { chaiSetup } from './utils/chai_setup'; -chaiSetup.configure(); -const expect = chai.expect; - -const server = setupServer( - rest.get('https://mock-0x-gas-api.org/median', (_req, res, ctx) => { - return res( - ctx.json({ - result: { - source: 'MEDIAN', - timestamp: 1659386474, - instant: 22000000000, - fast: 18848500000, - standard: 14765010000, - low: 13265000000, - }, - }), - ); - }), -); - -describe('ProtocolFeeUtils', () => { - describe('getGasPriceEstimationOrThrowAsync', () => { - beforeEach(() => { - server.listen(); - }); - - afterEach(() => { - server.close(); - }); - - it('parses fast gas price response correctly', async () => { - const utils = ProtocolFeeUtils.getInstance(420000, 'https://mock-0x-gas-api.org/median'); - const gasPrice = await utils.getGasPriceEstimationOrThrowAsync(); - expect(gasPrice.toNumber()).to.eq(18848500000); - }); - }); -}); diff --git a/packages/asset-swapper/test/quote_report_generator_test.ts b/packages/asset-swapper/test/quote_report_generator_test.ts deleted file mode 100644 index 3f29dd22d6..0000000000 --- a/packages/asset-swapper/test/quote_report_generator_test.ts +++ /dev/null @@ -1,376 +0,0 @@ -// tslint:disable:custom-no-magic-numbers -// tslint:disable:no-object-literal-type-assertion -import { FillQuoteTransformerOrderType, LimitOrder, LimitOrderFields, RfqOrder } from '@0x/protocol-utils'; -import { BigNumber, hexUtils } from '@0x/utils'; -import * as chai from 'chai'; -import * as _ from 'lodash'; -import 'mocha'; -import * as TypeMoq from 'typemoq'; - -import { MarketOperation, NativeOrderWithFillableAmounts } from '../src/types'; -import { - DexSample, - ERC20BridgeSource, - Fill, - MultiHopFillData, - NativeFillData, - NativeLimitOrderFillData, - NativeRfqOrderFillData, -} from '../src/utils/market_operation_utils/types'; -import { QuoteRequestor } from '../src/utils/quote_requestor'; - -import { - BridgeQuoteReportEntry, - generateQuoteReport, - MultiHopQuoteReportEntry, - NativeLimitOrderQuoteReportEntry, - NativeRfqOrderQuoteReportEntry, - QuoteReportEntry, -} from './../src/utils/quote_report_generator'; -import { chaiSetup } from './utils/chai_setup'; -import { getRandomAmount, getRandomSignature } from './utils/utils'; - -chaiSetup.configure(); -const expect = chai.expect; - -function fillFromNativeOrder(order: NativeOrderWithFillableAmounts): Fill { - const fillData = { - order: order.order, - signature: order.signature, - maxTakerTokenFillAmount: order.fillableTakerAmount, - }; - return { - sourcePathId: hexUtils.random(), - source: ERC20BridgeSource.Native, - type: order.type, - input: order.order.takerAmount, - output: order.order.makerAmount, - fillData: - order.type === FillQuoteTransformerOrderType.Limit - ? (fillData as NativeLimitOrderFillData) - : (fillData as NativeRfqOrderFillData), - adjustedOutput: order.order.makerAmount, - flags: BigInt(0), - gas: 1, - }; -} - -describe('generateQuoteReport', async () => { - it('should generate report properly for sell', () => { - const marketOperation: MarketOperation = MarketOperation.Sell; - - const balancerSample2: DexSample = { - source: ERC20BridgeSource.BalancerV2, - input: new BigNumber(10003), - output: new BigNumber(10004), - fillData: {}, - }; - const uniswapSample2: DexSample = { - source: ERC20BridgeSource.UniswapV2, - input: new BigNumber(10005), - output: new BigNumber(10006), - fillData: {}, - }; - const orderbookOrder1: NativeOrderWithFillableAmounts = { - order: new LimitOrder({ takerAmount: new BigNumber(1000) }), - type: FillQuoteTransformerOrderType.Limit, - fillableTakerAmount: new BigNumber(1000), - fillableMakerAmount: getRandomAmount(), - fillableTakerFeeAmount: getRandomAmount(), - signature: getRandomSignature(), - }; - const orderbookOrder2: NativeOrderWithFillableAmounts = { - order: new LimitOrder({ takerAmount: new BigNumber(198) }), - type: FillQuoteTransformerOrderType.Limit, - fillableTakerAmount: new BigNumber(99), // takerAmount minus 99 - fillableMakerAmount: getRandomAmount(), - fillableTakerFeeAmount: getRandomAmount(), - signature: getRandomSignature(), - }; - const rfqtOrder1: NativeOrderWithFillableAmounts = { - order: new RfqOrder({ takerAmount: new BigNumber(100) }), - type: FillQuoteTransformerOrderType.Rfq, - fillableTakerAmount: new BigNumber(100), - fillableMakerAmount: getRandomAmount(), - fillableTakerFeeAmount: getRandomAmount(), - signature: getRandomSignature(), - }; - const rfqtOrder2: NativeOrderWithFillableAmounts = { - order: new RfqOrder({ takerAmount: new BigNumber(1101) }), - type: FillQuoteTransformerOrderType.Rfq, - fillableTakerAmount: new BigNumber(1001), - fillableMakerAmount: getRandomAmount(), - fillableTakerFeeAmount: getRandomAmount(), - signature: getRandomSignature(), - }; - - const nativeOrders: NativeOrderWithFillableAmounts[] = [ - orderbookOrder1, - rfqtOrder1, - rfqtOrder2, - orderbookOrder2, - ]; - - // generate path - const uniswap2Fill: Fill = { - ...uniswapSample2, - sourcePathId: hexUtils.random(), - type: FillQuoteTransformerOrderType.Bridge, - adjustedOutput: uniswapSample2.output, - flags: BigInt(0), - gas: 1, - }; - const balancer2Fill: Fill = { - ...balancerSample2, - sourcePathId: hexUtils.random(), - type: FillQuoteTransformerOrderType.Bridge, - adjustedOutput: balancerSample2.output, - flags: BigInt(0), - gas: 1, - }; - const orderbookOrder2Fill: Fill = fillFromNativeOrder(orderbookOrder2); - const rfqtOrder2Fill: Fill = fillFromNativeOrder(rfqtOrder2); - const pathGenerated: Fill[] = [rfqtOrder2Fill, orderbookOrder2Fill, uniswap2Fill, balancer2Fill]; - - // quote generator mock - const quoteRequestor = TypeMoq.Mock.ofType(); - quoteRequestor - .setup(qr => qr.getMakerUriForSignature(rfqtOrder1.signature)) - .returns(() => { - return 'https://rfqt1.provider.club'; - }) - .verifiable(TypeMoq.Times.atLeastOnce()); - quoteRequestor - .setup(qr => qr.getMakerUriForSignature(rfqtOrder2.signature)) - .returns(() => { - return 'https://rfqt2.provider.club'; - }) - .verifiable(TypeMoq.Times.atLeastOnce()); - - const orderReport = generateQuoteReport( - marketOperation, - nativeOrders, - pathGenerated, - undefined, - quoteRequestor.object, - ); - - const rfqtOrder1Source: NativeRfqOrderQuoteReportEntry = { - liquiditySource: ERC20BridgeSource.Native, - makerAmount: rfqtOrder1.order.makerAmount, - takerAmount: rfqtOrder1.order.takerAmount, - fillableTakerAmount: rfqtOrder1.fillableTakerAmount, - isRFQ: true, - makerUri: 'https://rfqt1.provider.club', - nativeOrder: rfqtOrder1.order, - fillData: { - order: rfqtOrder1.order, - } as NativeRfqOrderFillData, - }; - const rfqtOrder2Source: NativeRfqOrderQuoteReportEntry = { - liquiditySource: ERC20BridgeSource.Native, - makerAmount: rfqtOrder2.order.makerAmount, - takerAmount: rfqtOrder2.order.takerAmount, - fillableTakerAmount: rfqtOrder2.fillableTakerAmount, - isRFQ: true, - makerUri: 'https://rfqt2.provider.club', - nativeOrder: rfqtOrder2.order, - fillData: { - order: rfqtOrder2.order, - } as NativeRfqOrderFillData, - }; - const orderbookOrder2Source: NativeLimitOrderQuoteReportEntry = { - liquiditySource: ERC20BridgeSource.Native, - makerAmount: orderbookOrder2.order.makerAmount, - takerAmount: orderbookOrder2.order.takerAmount, - fillableTakerAmount: orderbookOrder2.fillableTakerAmount, - isRFQ: false, - fillData: { - order: orderbookOrder2.order, - } as NativeLimitOrderFillData, - }; - const uniswap2Source: BridgeQuoteReportEntry = { - liquiditySource: ERC20BridgeSource.UniswapV2, - makerAmount: uniswapSample2.output, - takerAmount: uniswapSample2.input, - fillData: {}, - }; - const balancer2Source: BridgeQuoteReportEntry = { - liquiditySource: ERC20BridgeSource.BalancerV2, - makerAmount: balancerSample2.output, - takerAmount: balancerSample2.input, - fillData: {}, - }; - - const expectedSourcesConsidered: QuoteReportEntry[] = [rfqtOrder1Source, rfqtOrder2Source]; - const expectedSourcesDelivered: QuoteReportEntry[] = [ - rfqtOrder2Source, - orderbookOrder2Source, - uniswap2Source, - balancer2Source, - ]; - expectEqualQuoteReportEntries(orderReport.sourcesConsidered, expectedSourcesConsidered, `sourcesConsidered`); - expectEqualQuoteReportEntries(orderReport.sourcesDelivered, expectedSourcesDelivered, `sourcesDelivered`); - quoteRequestor.verifyAll(); - }); - it('should handle properly for buy without quoteRequestor', () => { - const marketOperation: MarketOperation = MarketOperation.Buy; - const balancerSample1: DexSample = { - source: ERC20BridgeSource.BalancerV2, - input: new BigNumber(10000), - output: new BigNumber(10001), - fillData: {}, - }; - const uniswapSample1: DexSample = { - source: ERC20BridgeSource.UniswapV2, - input: new BigNumber(10003), - output: new BigNumber(10004), - fillData: {}, - }; - const orderbookOrder1: NativeOrderWithFillableAmounts = { - order: new LimitOrder({ takerAmount: new BigNumber(1101) }), - type: FillQuoteTransformerOrderType.Limit, - fillableTakerAmount: new BigNumber(1000), - fillableMakerAmount: getRandomAmount(), - fillableTakerFeeAmount: getRandomAmount(), - signature: getRandomSignature(), - }; - const orderbookOrder2: NativeOrderWithFillableAmounts = { - order: new LimitOrder({ takerAmount: new BigNumber(5101) }), - type: FillQuoteTransformerOrderType.Limit, - fillableTakerAmount: new BigNumber(5000), // takerAmount minus 99 - fillableMakerAmount: getRandomAmount(), - fillableTakerFeeAmount: getRandomAmount(), - signature: getRandomSignature(), - }; - const nativeOrders = [orderbookOrder1, orderbookOrder2]; - - // generate path - const orderbookOrder1Fill: Fill = fillFromNativeOrder(orderbookOrder1); - const uniswap1Fill: Fill = { - ...uniswapSample1, - sourcePathId: hexUtils.random(), - type: FillQuoteTransformerOrderType.Bridge, - adjustedOutput: uniswapSample1.output, - flags: BigInt(0), - gas: 1, - }; - const balancer1Fill: Fill = { - ...balancerSample1, - sourcePathId: hexUtils.random(), - type: FillQuoteTransformerOrderType.Bridge, - adjustedOutput: balancerSample1.output, - flags: BigInt(0), - gas: 1, - }; - const pathGenerated: Fill[] = [orderbookOrder1Fill, uniswap1Fill, balancer1Fill]; - - const orderReport = generateQuoteReport(marketOperation, nativeOrders, pathGenerated); - - const orderbookOrder1Source: NativeLimitOrderQuoteReportEntry = { - liquiditySource: ERC20BridgeSource.Native, - makerAmount: orderbookOrder1.order.makerAmount, - takerAmount: orderbookOrder1.order.takerAmount, - fillableTakerAmount: orderbookOrder1.fillableTakerAmount, - isRFQ: false, - fillData: { - order: orderbookOrder1.order, - } as NativeLimitOrderFillData, - }; - const uniswap1Source: BridgeQuoteReportEntry = { - liquiditySource: ERC20BridgeSource.UniswapV2, - makerAmount: uniswapSample1.input, - takerAmount: uniswapSample1.output, - fillData: {}, - }; - const balancer1Source: BridgeQuoteReportEntry = { - liquiditySource: ERC20BridgeSource.BalancerV2, - makerAmount: balancerSample1.input, - takerAmount: balancerSample1.output, - fillData: {}, - }; - - // No order is considered here because only Native RFQ orders are considered. - const expectedSourcesConsidered: QuoteReportEntry[] = []; - const expectedSourcesDelivered: QuoteReportEntry[] = [orderbookOrder1Source, uniswap1Source, balancer1Source]; - expectEqualQuoteReportEntries(orderReport.sourcesConsidered, expectedSourcesConsidered, `sourcesConsidered`); - expectEqualQuoteReportEntries(orderReport.sourcesDelivered, expectedSourcesDelivered, `sourcesDelivered`); - }); - it('should correctly generate report for a two-hop quote', () => { - const marketOperation: MarketOperation = MarketOperation.Sell; - const orderbookOrder1: NativeOrderWithFillableAmounts = { - order: new LimitOrder({ takerAmount: new BigNumber(1101) }), - type: FillQuoteTransformerOrderType.Limit, - fillableTakerAmount: new BigNumber(1000), - fillableMakerAmount: getRandomAmount(), - fillableTakerFeeAmount: getRandomAmount(), - signature: getRandomSignature(), - }; - const twoHopFillData: MultiHopFillData = { - intermediateToken: hexUtils.random(20), - firstHopSource: { - source: ERC20BridgeSource.Balancer, - fillData: {}, - encodeCall: () => '', - handleCallResults: _callResults => [new BigNumber(1337)], - handleRevert: _c => [], - }, - secondHopSource: { - source: ERC20BridgeSource.Curve, - fillData: {}, - encodeCall: () => '', - handleCallResults: _callResults => [new BigNumber(1337)], - handleRevert: _c => [], - }, - }; - const twoHopSample: DexSample = { - source: ERC20BridgeSource.MultiHop, - input: new BigNumber(3005), - output: new BigNumber(3006), - fillData: twoHopFillData, - }; - - const orderReport = generateQuoteReport(marketOperation, [orderbookOrder1], twoHopSample); - const twoHopSource: MultiHopQuoteReportEntry = { - liquiditySource: ERC20BridgeSource.MultiHop, - makerAmount: twoHopSample.output, - takerAmount: twoHopSample.input, - hopSources: [ERC20BridgeSource.Balancer, ERC20BridgeSource.Curve], - fillData: twoHopFillData, - }; - - // No entry is present in considered because No RFQ orders were reported. - const expectedSourcesConsidered: QuoteReportEntry[] = []; - expectEqualQuoteReportEntries(orderReport.sourcesConsidered, expectedSourcesConsidered, `sourcesConsidered`); - expect(orderReport.sourcesDelivered.length).to.eql(1); - expect(orderReport.sourcesDelivered[0]).to.deep.equal(twoHopSource); - }); -}); - -function expectEqualQuoteReportEntries( - actual: QuoteReportEntry[], - expected: QuoteReportEntry[], - variableName: string = 'quote report entries', -): void { - expect(actual.length).to.eql(expected.length); - actual.forEach((actualEntry, idx) => { - const expectedEntry = expected[idx]; - // remove fillable values - if (actualEntry.liquiditySource === ERC20BridgeSource.Native) { - actualEntry.fillData.order = _.omit(actualEntry.fillData.order, [ - 'fillableMakerAmount', - 'fillableTakerAmount', - 'fillableTakerFeeAmount', - ]) as LimitOrderFields; - expect(actualEntry.fillData.order).to.eql( - // tslint:disable-next-line:no-unnecessary-type-assertion - (expectedEntry.fillData as NativeFillData).order, - `${variableName} incorrect at index ${idx}`, - ); - } - expect(_.omit(actualEntry, 'fillData')).to.eql( - _.omit(expectedEntry, 'fillData'), - `${variableName} incorrect at index ${idx}`, - ); - }); -} diff --git a/packages/asset-swapper/test/quote_requestor_test.ts b/packages/asset-swapper/test/quote_requestor_test.ts deleted file mode 100644 index a285da8ae7..0000000000 --- a/packages/asset-swapper/test/quote_requestor_test.ts +++ /dev/null @@ -1,1148 +0,0 @@ -import { tokenUtils } from '@0x/dev-utils'; -import { ETH_TOKEN_ADDRESS, FillQuoteTransformerOrderType, SignatureType } from '@0x/protocol-utils'; -import { TakerRequestQueryParamsUnnested, V4RFQIndicativeQuote } from '@0x/quote-server'; -import { StatusCodes } from '@0x/types'; -import { BigNumber, logUtils } from '@0x/utils'; -import Axios from 'axios'; -import * as chai from 'chai'; -import { Agent as HttpAgent } from 'http'; -import { Agent as HttpsAgent } from 'https'; -import _ = require('lodash'); -import 'mocha'; - -import { constants, KEEP_ALIVE_TTL } from '../src/constants'; -import { - AltMockedRfqQuoteResponse, - AltQuoteModel, - AltQuoteRequestData, - AltQuoteSide, - AltRfqMakerAssetOfferings, - MarketOperation, - MockedRfqQuoteResponse, -} from '../src/types'; -import { NULL_ADDRESS } from '../src/utils/market_operation_utils/constants'; -import { QuoteRequestor } from '../src/utils/quote_requestor'; - -import { chaiSetup } from './utils/chai_setup'; -import { RfqQuoteEndpoint, testHelpers } from './utils/test_helpers'; - -const quoteRequestorHttpClient = Axios.create({ - httpAgent: new HttpAgent({ keepAlive: true, timeout: KEEP_ALIVE_TTL }), - httpsAgent: new HttpsAgent({ keepAlive: true, timeout: KEEP_ALIVE_TTL }), -}); - -chaiSetup.configure(); -const expect = chai.expect; -const ALT_MM_API_KEY = 'averysecurekey'; -const ALT_PROFILE = 'acoolprofile'; -const ALT_RFQ_CREDS = { - altRfqApiKey: ALT_MM_API_KEY, - altRfqProfile: ALT_PROFILE, -}; - -const CREATED_STATUS_CODE = 201; - -function makeThreeMinuteExpiry(): BigNumber { - const expiry = new Date(Date.now()); - expiry.setMinutes(expiry.getMinutes() + 3); - return new BigNumber(Math.round(expiry.valueOf() / constants.ONE_SECOND_MS)); -} - -describe('QuoteRequestor', async () => { - const [makerToken, takerToken, otherToken1] = tokenUtils.getDummyERC20TokenAddresses(); - const validSignature = { v: 28, r: '0x', s: '0x', signatureType: SignatureType.EthSign }; - - const altRfqAssetOfferings: AltRfqMakerAssetOfferings = { - 'https://132.0.0.1': [ - { - id: 'XYZ-123', - baseAsset: makerToken, - quoteAsset: takerToken, - baseAssetDecimals: 2, - quoteAssetDecimals: 3, - }, - ], - }; - - describe('requestRfqmFirmQuotesAsync for firm quotes', async () => { - it('should return successful RFQM requests', async () => { - const takerAddress = '0xd209925defc99488e3afff1174e48b4fa628302a'; - const txOrigin = takerAddress; - const apiKey = 'my-ko0l-api-key'; - - // Set up RFQM responses - // tslint:disable-next-line:array-type - const mockedRequests: MockedRfqQuoteResponse[] = []; - const altMockedRequests: AltMockedRfqQuoteResponse[] = []; - - const expectedParams: TakerRequestQueryParamsUnnested = { - sellTokenAddress: takerToken, - buyTokenAddress: makerToken, - sellAmountBaseUnits: '10000', - comparisonPrice: undefined, - takerAddress, - txOrigin, - isLastLook: 'true', // the major difference between RFQ-T and RFQ-M - protocolVersion: '4', - feeAmount: '1000000000', - feeToken: ETH_TOKEN_ADDRESS, - feeType: 'fixed', - }; - const mockedDefaults = { - requestApiKey: apiKey, - requestParams: expectedParams, - responseCode: StatusCodes.Success, - }; - const validSignedOrder = { - makerToken, - takerToken, - makerAmount: new BigNumber('1000'), - takerAmount: new BigNumber('1000'), - maker: takerAddress, - taker: takerAddress, - pool: '0x', - salt: '0', - chainId: 1, - verifyingContract: takerAddress, - txOrigin, - expiry: makeThreeMinuteExpiry(), - signature: validSignature, - }; - // request is to sell 10000 units of the base token - // 10 units at 3 decimals - const altFirmRequestData = { - market: 'XYZ-123', - model: AltQuoteModel.Firm, - profile: ALT_PROFILE, - side: AltQuoteSide.Sell, - meta: { - txOrigin, - taker: takerAddress, - client: apiKey, - }, - value: '10', - }; - const altFirmResponse = { - ...altFirmRequestData, - id: 'random_id', - // tslint:disable-next-line:custom-no-magic-numbers - price: new BigNumber(10 / 100).toString(), - status: 'active', - data: { - '0xv4order': validSignedOrder, - }, - }; - - // [GOOD] Successful response - mockedRequests.push({ - ...mockedDefaults, - endpoint: 'https://1337.0.0.1', - responseData: { - signedOrder: validSignedOrder, - }, - }); - // [GOOD] Another Successful response - mockedRequests.push({ - ...mockedDefaults, - endpoint: 'https://37.0.0.1', - responseData: { signedOrder: validSignedOrder }, - }); - // [BAD] Test out a bad response code, ensure it doesnt cause throw - mockedRequests.push({ - ...mockedDefaults, - endpoint: 'https://420.0.0.1', - responseData: { error: 'bad request' }, - responseCode: StatusCodes.InternalError, - }); - // [BAD] Test out a successful response code but a partial order - mockedRequests.push({ - ...mockedDefaults, - endpoint: 'https://421.0.0.1', - responseData: { signedOrder: { makerToken: '123' } }, - }); - // [BAD] A successful response code and invalid response data (encoding) - mockedRequests.push({ - ...mockedDefaults, - endpoint: 'https://421.1.0.1', - responseData: 'this is not JSON!', - }); - // [BAD] A successful response code and valid order, but for wrong maker asset data - mockedRequests.push({ - ...mockedDefaults, - endpoint: 'https://422.0.0.1', - responseData: { signedOrder: { ...validSignedOrder, makerToken: '0x1234' } }, - }); - // [BAD] A successful response code and valid order, but for wrong taker asset data - mockedRequests.push({ - ...mockedDefaults, - endpoint: 'https://423.0.0.1', - responseData: { signedOrder: { ...validSignedOrder, takerToken: '0x1234' } }, - }); - // [BAD] A successful response code and good order but its unsigned - mockedRequests.push({ - ...mockedDefaults, - endpoint: 'https://424.0.0.1', - responseData: { signedOrder: _.omit(validSignedOrder, ['signature']) }, - }); - // [BAD] A successful response code and good order but for the wrong txOrigin - mockedRequests.push({ - ...mockedDefaults, - endpoint: 'https://425.0.0.1', - responseData: { signedOrder: { ...validSignedOrder, txOrigin: NULL_ADDRESS } }, - }); - // [GOOD] A successful response code and order from an alt RFQ implementation - altMockedRequests.push({ - endpoint: 'https://132.0.0.1', - mmApiKey: ALT_MM_API_KEY, - responseCode: CREATED_STATUS_CODE, - requestData: altFirmRequestData, - responseData: altFirmResponse, - }); - - const normalizedSuccessfulOrder = { - order: { - ..._.omit(validSignedOrder, ['signature']), - makerAmount: new BigNumber(validSignedOrder.makerAmount), - takerAmount: new BigNumber(validSignedOrder.takerAmount), - expiry: new BigNumber(validSignedOrder.expiry), - salt: new BigNumber(validSignedOrder.salt), - }, - signature: validSignedOrder.signature, - type: FillQuoteTransformerOrderType.Rfq, - }; - - return testHelpers.withMockedRfqQuotes( - mockedRequests, - altMockedRequests, - RfqQuoteEndpoint.Firm, - async () => { - const qr = new QuoteRequestor( - {}, // No RFQ-T asset offerings - { - 'https://1337.0.0.1': [[makerToken, takerToken]], - 'https://420.0.0.1': [[makerToken, takerToken]], - 'https://421.0.0.1': [[makerToken, takerToken]], - 'https://421.1.0.1': [[makerToken, takerToken]], - 'https://422.0.0.1': [[makerToken, takerToken]], - 'https://423.0.0.1': [[makerToken, takerToken]], - 'https://424.0.0.1': [[makerToken, takerToken]], - 'https://425.0.0.1': [[makerToken, takerToken]], - 'https://426.0.0.1': [] /* Shouldn't ping an RFQ provider when they don't support the requested asset pair. */, - 'https://37.0.0.1': [[makerToken, takerToken]], - }, - quoteRequestorHttpClient, - ALT_RFQ_CREDS, - ); - const resp = await qr.requestRfqmFirmQuotesAsync( - makerToken, - takerToken, - new BigNumber(10000), - MarketOperation.Sell, - undefined, - { - integrator: { - integratorId: apiKey, - label: 'foo', - }, - takerAddress, - txOrigin: takerAddress, - intentOnFilling: true, - altRfqAssetOfferings, - isLastLook: true, - fee: { - amount: new BigNumber('1000000000'), - token: ETH_TOKEN_ADDRESS, - type: 'fixed', - }, - }, - ); - expect(resp).to.deep.eq([ - normalizedSuccessfulOrder, - normalizedSuccessfulOrder, - normalizedSuccessfulOrder, - ]); - }, - quoteRequestorHttpClient, - ); - }); - }); - describe('requestRfqtFirmQuotesAsync for firm quotes', async () => { - it('should return successful RFQT requests', async () => { - const takerAddress = '0xd209925defc99488e3afff1174e48b4fa628302a'; - const txOrigin = takerAddress; - const apiKey = 'my-ko0l-api-key'; - - // Set up RFQT responses - // tslint:disable-next-line:array-type - const mockedRequests: MockedRfqQuoteResponse[] = []; - const altMockedRequests: AltMockedRfqQuoteResponse[] = []; - - const expectedParams: TakerRequestQueryParamsUnnested = { - sellTokenAddress: takerToken, - buyTokenAddress: makerToken, - sellAmountBaseUnits: '10000', - comparisonPrice: undefined, - takerAddress, - txOrigin, - protocolVersion: '4', - }; - const mockedDefaults = { - requestApiKey: apiKey, - requestParams: expectedParams, - responseCode: StatusCodes.Success, - }; - const validSignedOrder = { - makerToken, - takerToken, - makerAmount: new BigNumber('1000'), - takerAmount: new BigNumber('1000'), - maker: takerAddress, - taker: takerAddress, - pool: '0x', - salt: '0', - chainId: 1, - verifyingContract: takerAddress, - txOrigin, - expiry: makeThreeMinuteExpiry(), - signature: validSignature, - }; - // request is to sell 10000 units of the base token - // 10 units at 3 decimals - const altFirmRequestData = { - market: 'XYZ-123', - model: AltQuoteModel.Firm, - profile: ALT_PROFILE, - side: AltQuoteSide.Sell, - meta: { - txOrigin, - taker: takerAddress, - client: apiKey, - }, - value: '10', - }; - const altFirmResponse = { - ...altFirmRequestData, - id: 'random_id', - // tslint:disable-next-line:custom-no-magic-numbers - price: new BigNumber(10 / 100).toString(), - status: 'active', - data: { - '0xv4order': validSignedOrder, - }, - }; - - // Successful response - mockedRequests.push({ - ...mockedDefaults, - endpoint: 'https://1337.0.0.1', - responseData: { - signedOrder: validSignedOrder, - }, - }); - // Another Successful response - mockedRequests.push({ - ...mockedDefaults, - endpoint: 'https://37.0.0.1', - responseData: { signedOrder: validSignedOrder }, - }); - // Test out a bad response code, ensure it doesnt cause throw - mockedRequests.push({ - ...mockedDefaults, - endpoint: 'https://420.0.0.1', - responseData: { error: 'bad request' }, - responseCode: StatusCodes.InternalError, - }); - // Test out a successful response code but a partial order - mockedRequests.push({ - ...mockedDefaults, - endpoint: 'https://421.0.0.1', - responseData: { signedOrder: { makerToken: '123' } }, - }); - // A successful response code and invalid response data (encoding) - mockedRequests.push({ - ...mockedDefaults, - endpoint: 'https://421.1.0.1', - responseData: 'this is not JSON!', - }); - // A successful response code and valid order, but for wrong maker asset data - mockedRequests.push({ - ...mockedDefaults, - endpoint: 'https://422.0.0.1', - responseData: { signedOrder: { ...validSignedOrder, makerToken: '0x1234' } }, - }); - // A successful response code and valid order, but for wrong taker asset data - mockedRequests.push({ - ...mockedDefaults, - endpoint: 'https://423.0.0.1', - responseData: { signedOrder: { ...validSignedOrder, takerToken: '0x1234' } }, - }); - // A successful response code and good order but its unsigned - mockedRequests.push({ - ...mockedDefaults, - endpoint: 'https://424.0.0.1', - responseData: { signedOrder: _.omit(validSignedOrder, ['signature']) }, - }); - // A successful response code and good order but for the wrong txOrigin - mockedRequests.push({ - ...mockedDefaults, - endpoint: 'https://425.0.0.1', - responseData: { signedOrder: { ...validSignedOrder, txOrigin: NULL_ADDRESS } }, - }); - // A successful response code and order from an alt RFQ implementation - altMockedRequests.push({ - endpoint: 'https://132.0.0.1', - mmApiKey: ALT_MM_API_KEY, - responseCode: CREATED_STATUS_CODE, - requestData: altFirmRequestData, - responseData: altFirmResponse, - }); - - const normalizedSuccessfulOrder = { - order: { - ..._.omit(validSignedOrder, ['signature']), - makerAmount: new BigNumber(validSignedOrder.makerAmount), - takerAmount: new BigNumber(validSignedOrder.takerAmount), - expiry: new BigNumber(validSignedOrder.expiry), - salt: new BigNumber(validSignedOrder.salt), - }, - signature: validSignedOrder.signature, - type: FillQuoteTransformerOrderType.Rfq, - }; - - return testHelpers.withMockedRfqQuotes( - mockedRequests, - altMockedRequests, - RfqQuoteEndpoint.Firm, - async () => { - const qr = new QuoteRequestor( - { - 'https://1337.0.0.1': [[makerToken, takerToken]], - 'https://420.0.0.1': [[makerToken, takerToken]], - 'https://421.0.0.1': [[makerToken, takerToken]], - 'https://421.1.0.1': [[makerToken, takerToken]], - 'https://422.0.0.1': [[makerToken, takerToken]], - 'https://423.0.0.1': [[makerToken, takerToken]], - 'https://424.0.0.1': [[makerToken, takerToken]], - 'https://425.0.0.1': [[makerToken, takerToken]], - 'https://426.0.0.1': [] /* Shouldn't ping an RFQ-T provider when they don't support the requested asset pair. */, - 'https://37.0.0.1': [[makerToken, takerToken]], - }, - {}, - quoteRequestorHttpClient, - ALT_RFQ_CREDS, - ); - const resp = await qr.requestRfqtFirmQuotesAsync( - makerToken, - takerToken, - new BigNumber(10000), - MarketOperation.Sell, - undefined, - { - integrator: { - integratorId: apiKey, - label: 'foo', - }, - takerAddress, - txOrigin: takerAddress, - intentOnFilling: true, - altRfqAssetOfferings, - }, - ); - expect(resp).to.deep.eq([ - normalizedSuccessfulOrder, - normalizedSuccessfulOrder, - normalizedSuccessfulOrder, - ]); - }, - quoteRequestorHttpClient, - ); - }); - }); - describe('requestRfqmIndicativeQuotesAsync for Indicative quotes', async () => { - it('should return successful RFQM requests', async () => { - const takerAddress = '0xd209925defc99488e3afff1174e48b4fa628302a'; - const apiKey = 'my-ko0l-api-key'; - - // Set up RFQ responses - // tslint:disable-next-line:array-type - const mockedRequests: MockedRfqQuoteResponse[] = []; - const expectedParams: TakerRequestQueryParamsUnnested = { - sellTokenAddress: takerToken, - buyTokenAddress: makerToken, - sellAmountBaseUnits: '10000', - comparisonPrice: undefined, - takerAddress, - txOrigin: takerAddress, - isLastLook: 'true', // the major difference between RFQ-T and RFQ-M - protocolVersion: '4', - feeAmount: '1000000000', - feeToken: ETH_TOKEN_ADDRESS, - feeType: 'fixed', - }; - const mockedDefaults = { - requestApiKey: apiKey, - requestParams: expectedParams, - responseCode: StatusCodes.Success, - }; - - // [GOOD] Successful response - const successfulQuote1 = { - makerToken, - takerToken, - makerAmount: new BigNumber(expectedParams.sellAmountBaseUnits), - takerAmount: new BigNumber(expectedParams.sellAmountBaseUnits), - expiry: makeThreeMinuteExpiry(), - }; - - const goodMMUri1 = 'https://1337.0.0.1'; - const goodMMUri2 = 'https://37.0.0.1'; - - mockedRequests.push({ - ...mockedDefaults, - endpoint: goodMMUri1, - responseData: successfulQuote1, - }); - // [GOOD] Another Successful response - mockedRequests.push({ - ...mockedDefaults, - endpoint: goodMMUri2, - responseData: successfulQuote1, - }); - - // [BAD] Test out a bad response code, ensure it doesnt cause throw - mockedRequests.push({ - ...mockedDefaults, - endpoint: 'https://420.0.0.1', - responseData: { error: 'bad request' }, - responseCode: StatusCodes.InternalError, - }); - // [BAD] Test out a successful response code but an invalid order - mockedRequests.push({ - ...mockedDefaults, - endpoint: 'https://421.0.0.1', - responseData: { makerToken: '123' }, - }); - // [BAD] A successful response code and valid response data, but for wrong maker asset data - mockedRequests.push({ - ...mockedDefaults, - endpoint: 'https://422.0.0.1', - responseData: { ...successfulQuote1, makerToken: otherToken1 }, - }); - // [BAD] A successful response code and valid response data, but for wrong taker asset data - mockedRequests.push({ - ...mockedDefaults, - endpoint: 'https://423.0.0.1', - responseData: { ...successfulQuote1, takerToken: otherToken1 }, - }); - - const assetOfferings: { [k: string]: [[string, string]] } = { - 'https://420.0.0.1': [[makerToken, takerToken]], - 'https://421.0.0.1': [[makerToken, takerToken]], - 'https://422.0.0.1': [[makerToken, takerToken]], - 'https://423.0.0.1': [[makerToken, takerToken]], - 'https://424.0.0.1': [[makerToken, takerToken]], - }; - assetOfferings[goodMMUri1] = [[makerToken, takerToken]]; - assetOfferings[goodMMUri2] = [[makerToken, takerToken]]; - - return testHelpers.withMockedRfqQuotes( - mockedRequests, - [], - RfqQuoteEndpoint.Indicative, - async () => { - const qr = new QuoteRequestor( - {}, // No RFQ-T asset offerings - assetOfferings, - quoteRequestorHttpClient, - ); - const resp = await qr.requestRfqmIndicativeQuotesAsync( - makerToken, - takerToken, - new BigNumber(10000), - MarketOperation.Sell, - undefined, - { - integrator: { - integratorId: apiKey, - label: 'foo', - }, - takerAddress, - txOrigin: takerAddress, - intentOnFilling: true, - isLastLook: true, - fee: { - type: 'fixed', - token: ETH_TOKEN_ADDRESS, - amount: new BigNumber('1000000000'), - }, - }, - ); - expect(resp.sort()).to.eql( - [ - { ...successfulQuote1, makerUri: goodMMUri1 }, - { ...successfulQuote1, makerUri: goodMMUri2 }, - ].sort(), - ); - }, - quoteRequestorHttpClient, - ); - }); - }); - describe('requestRfqtIndicativeQuotesAsync for Indicative quotes', async () => { - it('should optionally accept a "comparisonPrice" parameter', async () => { - const response = QuoteRequestor.makeQueryParameters( - otherToken1, // tx origin - otherToken1, // taker - MarketOperation.Sell, - makerToken, - takerToken, - new BigNumber(1000), - new BigNumber(300.2), - ); - expect(response.comparisonPrice).to.eql('300.2'); - }); - it('should return successful RFQT requests', async () => { - const takerAddress = '0xd209925defc99488e3afff1174e48b4fa628302a'; - const apiKey = 'my-ko0l-api-key'; - - // Set up RFQT responses - // tslint:disable-next-line:array-type - const mockedRequests: MockedRfqQuoteResponse[] = []; - const expectedParams: TakerRequestQueryParamsUnnested = { - sellTokenAddress: takerToken, - buyTokenAddress: makerToken, - sellAmountBaseUnits: '10000', - comparisonPrice: undefined, - takerAddress, - txOrigin: takerAddress, - protocolVersion: '4', - }; - const mockedDefaults = { - requestApiKey: apiKey, - requestParams: expectedParams, - responseCode: StatusCodes.Success, - }; - - // Successful response - const successfulQuote1 = { - makerToken, - takerToken, - makerAmount: new BigNumber(expectedParams.sellAmountBaseUnits), - takerAmount: new BigNumber(expectedParams.sellAmountBaseUnits), - expiry: makeThreeMinuteExpiry(), - }; - - const goodMMUri1 = 'https://1337.0.0.1'; - const goodMMUri2 = 'https://37.0.0.1'; - - mockedRequests.push({ - ...mockedDefaults, - endpoint: goodMMUri1, - responseData: successfulQuote1, - }); - // Test out a bad response code, ensure it doesnt cause throw - mockedRequests.push({ - ...mockedDefaults, - endpoint: 'https://420.0.0.1', - responseData: { error: 'bad request' }, - responseCode: StatusCodes.InternalError, - }); - // Test out a successful response code but an invalid order - mockedRequests.push({ - ...mockedDefaults, - endpoint: 'https://421.0.0.1', - responseData: { makerToken: '123' }, - }); - // A successful response code and valid response data, but for wrong maker asset data - mockedRequests.push({ - ...mockedDefaults, - endpoint: 'https://422.0.0.1', - responseData: { ...successfulQuote1, makerToken: otherToken1 }, - }); - // A successful response code and valid response data, but for wrong taker asset data - mockedRequests.push({ - ...mockedDefaults, - endpoint: 'https://423.0.0.1', - responseData: { ...successfulQuote1, takerToken: otherToken1 }, - }); - // Another Successful response - mockedRequests.push({ - ...mockedDefaults, - endpoint: goodMMUri2, - responseData: successfulQuote1, - }); - - const assetOfferings: { [k: string]: [[string, string]] } = { - 'https://420.0.0.1': [[makerToken, takerToken]], - 'https://421.0.0.1': [[makerToken, takerToken]], - 'https://422.0.0.1': [[makerToken, takerToken]], - 'https://423.0.0.1': [[makerToken, takerToken]], - 'https://424.0.0.1': [[makerToken, takerToken]], - }; - assetOfferings[goodMMUri1] = [[makerToken, takerToken]]; - assetOfferings[goodMMUri2] = [[makerToken, takerToken]]; - - return testHelpers.withMockedRfqQuotes( - mockedRequests, - [], - RfqQuoteEndpoint.Indicative, - async () => { - const qr = new QuoteRequestor(assetOfferings, {}, quoteRequestorHttpClient); - const resp = await qr.requestRfqtIndicativeQuotesAsync( - makerToken, - takerToken, - new BigNumber(10000), - MarketOperation.Sell, - undefined, - { - integrator: { - integratorId: apiKey, - label: 'foo', - }, - takerAddress, - txOrigin: takerAddress, - intentOnFilling: true, - }, - ); - expect(resp.sort()).to.eql( - [ - { ...successfulQuote1, makerUri: goodMMUri1 }, - { ...successfulQuote1, makerUri: goodMMUri2 }, - ].sort(), - ); - }, - quoteRequestorHttpClient, - ); - }); - it('should only return RFQT requests that meet the timeout', async () => { - const takerAddress = '0xd209925defc99488e3afff1174e48b4fa628302a'; - const apiKey = 'my-ko0l-api-key'; - const maxTimeoutMs = 10; - // tslint:disable-next-line:custom-no-magic-numbers - const exceedTimeoutMs = maxTimeoutMs + 50; - - // Set up RFQT responses - // tslint:disable-next-line:array-type - const mockedRequests: MockedRfqQuoteResponse[] = []; - const expectedParams: TakerRequestQueryParamsUnnested = { - sellTokenAddress: takerToken, - buyTokenAddress: makerToken, - sellAmountBaseUnits: '10000', - comparisonPrice: undefined, - takerAddress, - txOrigin: takerAddress, - protocolVersion: '4', - }; - const mockedDefaults = { - requestApiKey: apiKey, - requestParams: expectedParams, - responseCode: StatusCodes.Success, - }; - - // Successful response - const successfulQuote1 = { - makerToken, - takerToken, - makerAmount: new BigNumber(expectedParams.sellAmountBaseUnits), - takerAmount: new BigNumber(expectedParams.sellAmountBaseUnits), - expiry: makeThreeMinuteExpiry(), - }; - - // One good request - mockedRequests.push({ - ...mockedDefaults, - endpoint: 'https://1337.0.0.1', - responseData: successfulQuote1, - }); - - // One request that will timeout - mockedRequests.push({ - ...mockedDefaults, - endpoint: 'https://420.0.0.1', - responseData: successfulQuote1, - callback: async () => { - // tslint:disable-next-line:no-inferred-empty-object-type - return new Promise((resolve, reject) => { - setTimeout(() => { - resolve([StatusCodes.Success, successfulQuote1]); - }, exceedTimeoutMs); - }); - }, - }); - - return testHelpers.withMockedRfqQuotes( - mockedRequests, - [], - RfqQuoteEndpoint.Indicative, - async () => { - const qr = new QuoteRequestor( - { - 'https://1337.0.0.1': [[makerToken, takerToken]], - 'https://420.0.0.1': [[makerToken, takerToken]], - }, - {}, - quoteRequestorHttpClient, - ); - const resp = await qr.requestRfqtIndicativeQuotesAsync( - makerToken, - takerToken, - new BigNumber(10000), - MarketOperation.Sell, - undefined, - { - integrator: { - integratorId: apiKey, - label: 'foo', - }, - takerAddress, - txOrigin: takerAddress, - intentOnFilling: true, - makerEndpointMaxResponseTimeMs: maxTimeoutMs, - }, - ); - expect(resp.sort()).to.eql([{ ...successfulQuote1, makerUri: 'https://1337.0.0.1' }].sort()); // notice only one result, despite two requests made - }, - quoteRequestorHttpClient, - ); - }); - it('should return successful RFQT indicative quote requests (Buy)', async () => { - const takerAddress = '0xd209925defc99488e3afff1174e48b4fa628302a'; - const apiKey = 'my-ko0l-api-key'; - - // Set up RFQT responses - // tslint:disable-next-line:array-type - const mockedRequests: MockedRfqQuoteResponse[] = []; - const expectedParams: TakerRequestQueryParamsUnnested = { - sellTokenAddress: takerToken, - buyTokenAddress: makerToken, - buyAmountBaseUnits: '10000', - comparisonPrice: undefined, - takerAddress, - txOrigin: takerAddress, - protocolVersion: '4', - }; - // Successful response - const successfulQuote1 = { - makerToken, - takerToken, - makerAmount: new BigNumber(expectedParams.buyAmountBaseUnits), - takerAmount: new BigNumber(expectedParams.buyAmountBaseUnits), - expiry: makeThreeMinuteExpiry(), - }; - mockedRequests.push({ - endpoint: 'https://1337.0.0.1', - requestApiKey: apiKey, - requestParams: expectedParams, - responseData: successfulQuote1, - responseCode: StatusCodes.Success, - }); - - return testHelpers.withMockedRfqQuotes( - mockedRequests, - [], - RfqQuoteEndpoint.Indicative, - async () => { - const qr = new QuoteRequestor( - { 'https://1337.0.0.1': [[makerToken, takerToken]] }, - {}, - quoteRequestorHttpClient, - ); - const resp = await qr.requestRfqtIndicativeQuotesAsync( - makerToken, - takerToken, - new BigNumber(10000), - MarketOperation.Buy, - undefined, - { - integrator: { - integratorId: apiKey, - label: 'foo', - }, - takerAddress, - txOrigin: takerAddress, - intentOnFilling: true, - }, - ); - expect(resp.sort()).to.eql([{ ...successfulQuote1, makerUri: 'https://1337.0.0.1' }].sort()); - }, - quoteRequestorHttpClient, - ); - }); - it('should be able to handle and filter RFQ offerings', () => { - const tests: Array<[string[] | undefined, string[]]> = [ - [['https://top.maker'], []], - [undefined, ['https://foo.bar/', 'https://lorem.ipsum/']], - [['https://lorem.ipsum/'], ['https://lorem.ipsum/']], - ]; - for (const test of tests) { - const [apiKeyWhitelist, results] = test; - const response = QuoteRequestor.getTypedMakerUrlsAndWhitelist( - { - integrator: { - integratorId: 'foo', - label: 'bar', - whitelistIntegratorUrls: apiKeyWhitelist, - }, - altRfqAssetOfferings: {}, - }, - { - 'https://foo.bar/': [ - [ - '0xA6cD4cb8c62aCDe44739E3Ed0F1d13E0e31f2d94', - '0xF45107c0200a04A8aB9C600cc52A3C89AE5D0489', - ], - ], - 'https://lorem.ipsum/': [ - [ - '0xA6cD4cb8c62aCDe44739E3Ed0F1d13E0e31f2d94', - '0xF45107c0200a04A8aB9C600cc52A3C89AE5D0489', - ], - ], - }, - ); - const typedUrls = response.map(typed => typed.url); - expect(typedUrls).to.eql(results); - } - }); - - it('should return successful alt indicative quotes', async () => { - const takerAddress = '0xd209925defc99488e3afff1174e48b4fa628302a'; - const txOrigin = '0xf209925defc99488e3afff1174e48b4fa628302a'; - const apiKey = 'my-ko0l-api-key'; - - // base token has 2 decimals - // quote token has 3 decimals - const baseToken = makerToken; - const quoteToken = takerToken; - - // Set up RFQT responses - const altMockedRequests: AltMockedRfqQuoteResponse[] = []; - const altScenarios: Array<{ - successfulQuote: V4RFQIndicativeQuote; - requestedMakerToken: string; - requestedTakerToken: string; - requestedAmount: BigNumber; - requestedOperation: MarketOperation; - }> = []; - - // SCENARIO 1 - // buy, base asset specified - // requesting to buy 100 units (10000 base units) of the base token - // returning a price of 0.01, which should mean 10000 maker, 1000 taker amount - const buyAmountAltRequest: AltQuoteRequestData = { - market: 'XYZ-123', - model: AltQuoteModel.Indicative, - profile: ALT_PROFILE, - side: AltQuoteSide.Sell, - meta: { - txOrigin, - taker: takerAddress, - client: apiKey, - }, - amount: '100', - }; - // Successful response - const buyAmountAltResponse = { - ...buyAmountAltRequest, - id: 'random_id', - // tslint:disable-next-line:custom-no-magic-numbers - price: new BigNumber(0.01).toString(), - status: 'live', - }; - const successfulBuyAmountQuote: V4RFQIndicativeQuote = { - makerToken: baseToken, - takerToken: quoteToken, - makerAmount: new BigNumber(10000), - takerAmount: new BigNumber(1000), - expiry: new BigNumber(0), - }; - altMockedRequests.push({ - endpoint: 'https://132.0.0.1', - mmApiKey: ALT_MM_API_KEY, - responseCode: CREATED_STATUS_CODE, - requestData: buyAmountAltRequest, - responseData: buyAmountAltResponse, - }); - altScenarios.push({ - successfulQuote: successfulBuyAmountQuote, - requestedMakerToken: baseToken, - requestedTakerToken: quoteToken, - requestedAmount: new BigNumber(10000), - requestedOperation: MarketOperation.Buy, - }); - - // SCENARIO 2 - // alt buy, quote asset specified - // user is requesting to sell 1 unit of the quote token, or 1000 base units - // returning a price of 0.01, which should mean 10000 maker amount, 1000 taker amount - const buyValueAltRequest: AltQuoteRequestData = { - market: 'XYZ-123', - model: AltQuoteModel.Indicative, - profile: ALT_PROFILE, - side: AltQuoteSide.Sell, - meta: { - txOrigin, - taker: takerAddress, - client: apiKey, - }, - value: '1', - }; - // Successful response - const buyValueAltResponse = { - ...buyValueAltRequest, - id: 'random_id', - // tslint:disable-next-line:custom-no-magic-numbers - price: new BigNumber(0.01).toString(), - status: 'live', - }; - const successfulBuyValueQuote: V4RFQIndicativeQuote = { - makerToken: baseToken, - takerToken: quoteToken, - makerAmount: new BigNumber(10000), - takerAmount: new BigNumber(1000), - expiry: new BigNumber(0), - }; - altMockedRequests.push({ - endpoint: 'https://132.0.0.1', - mmApiKey: ALT_MM_API_KEY, - responseCode: CREATED_STATUS_CODE, - requestData: buyValueAltRequest, - responseData: buyValueAltResponse, - }); - altScenarios.push({ - successfulQuote: successfulBuyValueQuote, - requestedMakerToken: baseToken, - requestedTakerToken: quoteToken, - requestedAmount: new BigNumber(1000), - requestedOperation: MarketOperation.Sell, - }); - - // SCENARIO 3 - // alt sell, base asset specified - // user is requesting to sell 100 units (10000 base units) of the base token - // returning a price of 0.01, which should mean 10000 taker amount, 1000 maker amount - const sellAmountAltRequest: AltQuoteRequestData = { - market: 'XYZ-123', - model: AltQuoteModel.Indicative, - profile: ALT_PROFILE, - side: AltQuoteSide.Buy, - meta: { - txOrigin, - taker: takerAddress, - client: apiKey, - }, - amount: '100', - }; - // Successful response - const sellAmountAltResponse = { - ...sellAmountAltRequest, - id: 'random_id', - // tslint:disable-next-line:custom-no-magic-numbers - price: new BigNumber(0.01).toString(), - status: 'live', - }; - const successfulSellAmountQuote: V4RFQIndicativeQuote = { - makerToken: quoteToken, - takerToken: baseToken, - makerAmount: new BigNumber(1000), - takerAmount: new BigNumber(10000), - expiry: new BigNumber(0), - }; - altMockedRequests.push({ - endpoint: 'https://132.0.0.1', - mmApiKey: ALT_MM_API_KEY, - responseCode: CREATED_STATUS_CODE, - requestData: sellAmountAltRequest, - responseData: sellAmountAltResponse, - }); - altScenarios.push({ - successfulQuote: successfulSellAmountQuote, - requestedMakerToken: quoteToken, - requestedTakerToken: baseToken, - requestedAmount: new BigNumber(10000), - requestedOperation: MarketOperation.Sell, - }); - - // SCENARIO 4 - // alt sell, quote asset specified - // user is requesting to buy 1 unit (1000 base units) of the quote token - // returning a price of 0.01, which should mean 10000 taker amount, 1000 maker amount - const sellValueAltRequest: AltQuoteRequestData = { - market: 'XYZ-123', - model: AltQuoteModel.Indicative, - profile: ALT_PROFILE, - side: AltQuoteSide.Buy, - meta: { - txOrigin, - taker: takerAddress, - client: apiKey, - }, - value: '1', - }; - // Successful response - const sellValueAltResponse = { - ...sellValueAltRequest, - id: 'random_id', - // tslint:disable-next-line:custom-no-magic-numbers - price: new BigNumber(0.01).toString(), - status: 'live', - }; - const successfulSellValueQuote: V4RFQIndicativeQuote = { - makerToken: quoteToken, - takerToken: baseToken, - makerAmount: new BigNumber(1000), - takerAmount: new BigNumber(10000), - expiry: new BigNumber(0), - }; - altMockedRequests.push({ - endpoint: 'https://132.0.0.1', - mmApiKey: ALT_MM_API_KEY, - responseCode: CREATED_STATUS_CODE, - requestData: sellValueAltRequest, - responseData: sellValueAltResponse, - }); - altScenarios.push({ - successfulQuote: successfulSellValueQuote, - requestedMakerToken: quoteToken, - requestedTakerToken: baseToken, - requestedAmount: new BigNumber(1000), - requestedOperation: MarketOperation.Buy, - }); - - let scenarioCounter = 1; - for (const altScenario of altScenarios) { - logUtils.log(`Alt MM indicative scenario ${scenarioCounter}`); - scenarioCounter += 1; - await testHelpers.withMockedRfqQuotes( - [], - altMockedRequests, - RfqQuoteEndpoint.Indicative, - async () => { - const qr = new QuoteRequestor({}, {}, quoteRequestorHttpClient, ALT_RFQ_CREDS); - const resp = await qr.requestRfqtIndicativeQuotesAsync( - altScenario.requestedMakerToken, - altScenario.requestedTakerToken, - altScenario.requestedAmount, - altScenario.requestedOperation, - undefined, - { - integrator: { - integratorId: apiKey, - label: 'foo', - }, - takerAddress, - txOrigin, - intentOnFilling: true, - altRfqAssetOfferings, - }, - ); - // hack to get the expiry right, since it's dependent on the current timestamp - const expected = { ...altScenario.successfulQuote, expiry: resp[0].expiry }; - expect(resp.sort()).to.eql([expected].sort()); - }, - quoteRequestorHttpClient, - ); - } - }); - }); -}); diff --git a/packages/asset-swapper/test/quote_simulation_test.ts b/packages/asset-swapper/test/quote_simulation_test.ts deleted file mode 100644 index 1b476bbf37..0000000000 --- a/packages/asset-swapper/test/quote_simulation_test.ts +++ /dev/null @@ -1,927 +0,0 @@ -import { constants, expect, getRandomInteger, randomAddress } from '@0x/contracts-test-utils'; -import { FillQuoteTransformerOrderType, SignatureType } from '@0x/protocol-utils'; -import { BigNumber, hexUtils, NULL_BYTES } from '@0x/utils'; -import * as _ from 'lodash'; - -import { MarketOperation } from '../src/types'; -import { - ERC20BridgeSource, - Fill, - NativeLimitOrderFillData, - OptimizedMarketOrder, - OptimizedMarketOrderBase, -} from '../src/utils/market_operation_utils/types'; -import { - fillQuoteOrders, - QuoteFillOrderCall, - simulateBestCaseFill, - simulateWorstCaseFill, -} from '../src/utils/quote_simulation'; - -// tslint:disable: custom-no-magic-numbers - -describe('quote_simulation tests', async () => { - const { NULL_ADDRESS } = constants; - const ZERO = new BigNumber(0); - const ONE = new BigNumber(1); - const MAKER_TOKEN = randomAddress(); - const TAKER_TOKEN = randomAddress(); - const GAS_SCHEDULE = { [ERC20BridgeSource.Uniswap]: _.constant(1), [ERC20BridgeSource.Native]: _.constant(1) }; - - // Check if two numbers are within `maxError` error rate within each other. - function assertRoughlyEquals(n1: BigNumber, n2: BigNumber, maxError: BigNumber | number = 1e-10): void { - // |n2-n1| / max(|n1|, |n2|) - const err = n2 - .minus(n1) - .abs() - .div(BigNumber.max(n1.abs(), n2.abs())); - expect(err).to.bignumber.lt(maxError); - } - - function createQuoteFillOrders( - opts: Partial<{ - fillableInput: BigNumber; - fillableOutput: BigNumber; - inputFeeRate: number; - outputFeeRate: number; - count: number; - side: MarketOperation; - type?: FillQuoteTransformerOrderType; - }> = {}, - ): QuoteFillOrderCall[] { - const { fillableInput, fillableOutput, inputFeeRate, outputFeeRate, count, side, type } = { - fillableInput: getRandomOrderSize(), - fillableOutput: getRandomOrderSize(), - inputFeeRate: 0, - outputFeeRate: 0, - count: 3, - side: MarketOperation.Sell, - ...opts, - }; - const _inputFeeRate = side === MarketOperation.Sell ? inputFeeRate : -inputFeeRate; - const _outputFeeRate = side === MarketOperation.Sell ? -outputFeeRate : outputFeeRate; - - const fillableInputs = subdivideAmount(fillableInput, count); - const fillableOutputs = subdivideAmount(fillableOutput, count); - const filledInputs = subdivideAmount(fillableInput.times(0.5), count); - const filledOutputs: BigNumber[] = []; - const totalInputs: BigNumber[] = []; - const totalOutputs: BigNumber[] = []; - const inputFees: BigNumber[] = []; - const outputFees: BigNumber[] = []; - _.times(count).forEach(i => { - const f = filledInputs[i].div(fillableInputs[i]); - filledOutputs.push(fillableOutputs[i].times(f).integerValue(BigNumber.ROUND_DOWN)); - totalInputs.push(fillableInputs[i].plus(filledInputs[i])); - totalOutputs.push(fillableOutputs[i].plus(filledOutputs[i])); - inputFees.push(totalInputs[i].times(_inputFeeRate).integerValue()); - outputFees.push(totalOutputs[i].times(_outputFeeRate).integerValue()); - }); - return _.times(count, i => { - return { - order: createQuoteFillOrderOrder(totalInputs[i], totalOutputs[i], { - side, - filledInput: filledInputs[i], - takerInputFee: inputFees[i].abs(), - takerOutputFee: outputFees[i].abs(), - type, - }), - totalOrderInput: totalInputs[i], - totalOrderOutput: totalOutputs[i], - totalOrderInputFee: inputFees[i], - totalOrderOutputFee: outputFees[i], - }; - }); - } - - function createQuoteFillOrderOrder( - input: BigNumber, - output: BigNumber, - opts: Partial<{ - filledInput: BigNumber; - side: MarketOperation; - takerInputFee: BigNumber; - takerOutputFee: BigNumber; - type: FillQuoteTransformerOrderType; - }> = {}, - ): OptimizedMarketOrderBase { - const { filledInput, side, takerInputFee, takerOutputFee, type } = _.merge( - {}, - { - side: MarketOperation.Sell, - filledInput: ZERO, - takerInputFee: ZERO, - takerOutputFee: ZERO, - type: FillQuoteTransformerOrderType.Limit, - }, - opts, - ); - const filledOutput = filledInput - .div(input) - .times(output) - .integerValue(BigNumber.ROUND_DOWN); - const fillableInput = input.minus(filledInput); - const fillableOutput = output.minus(filledOutput); - const makerAmount = side === MarketOperation.Sell ? output : input; - const takerAmount = side === MarketOperation.Sell ? input : output; - const fillableMakerAmount = side === MarketOperation.Sell ? fillableOutput : fillableInput; - const fillableTakerAmount = side === MarketOperation.Sell ? fillableInput : fillableOutput; - const takerFee = BigNumber.max(takerInputFee, takerOutputFee); - - const order: OptimizedMarketOrderBase = { - source: ERC20BridgeSource.Native, - makerToken: MAKER_TOKEN, - takerToken: TAKER_TOKEN, - makerAmount: fillableMakerAmount, - takerAmount: fillableTakerAmount, - fillData: { - order: { - makerToken: MAKER_TOKEN, - makerAmount, - takerToken: TAKER_TOKEN, - takerAmount, - maker: NULL_ADDRESS, - taker: NULL_ADDRESS, - sender: NULL_ADDRESS, - salt: ZERO, - chainId: 1, - pool: NULL_BYTES, - verifyingContract: NULL_ADDRESS, - expiry: ZERO, - feeRecipient: NULL_ADDRESS, - takerTokenFeeAmount: takerFee, - }, - signature: { v: 1, r: NULL_BYTES, s: NULL_BYTES, signatureType: SignatureType.EthSign }, - maxTakerTokenFillAmount: fillableTakerAmount, - }, - type, - fill: createOrderFill(fillableInput, fillableOutput), - }; - return order; - } - const nativeSourcePathId = hexUtils.random(); - function createOrderFill(input: BigNumber, output: BigNumber): Fill { - return { - type: FillQuoteTransformerOrderType.Bridge, - sourcePathId: nativeSourcePathId, - source: ERC20BridgeSource.Uniswap, - fillData: {}, - input, - output, - flags: BigInt(0), - adjustedOutput: output, - gas: 1, - }; - } - - function randomSide(): MarketOperation { - return _.sampleSize(Object.values(MarketOperation), 1)[0]; - } - - function getRandomOrderSize(): BigNumber { - return getRandomInteger('100e18', '1000e18'); - } - - function getRandomFeeRate(): number { - return _.random(0.01, 0.25, true); - } - - function assertEqualRates(actual: number | BigNumber, expected: number | BigNumber): void { - expect(new BigNumber(actual).times(1e4).integerValue()).to.bignumber.eq( - new BigNumber(expected).times(1e4).integerValue(), - ); - } - - function subdivideAmount(amount: BigNumber, count: number): BigNumber[] { - const amounts = []; - for (let i = 0; i < count; ++i) { - const remaining = amount.minus(BigNumber.sum(0, ...amounts)); - if (i !== count - 1) { - amounts.push(remaining.times(Math.random()).integerValue()); - } else { - amounts.push(remaining.integerValue()); - } - } - return amounts; - } - - describe('fillQuoteOrders()', () => { - describe('single order', () => { - it('can exactly fill one order', () => { - const side = randomSide(); - const fillableInput = getRandomOrderSize(); - const fillableOutput = getRandomOrderSize(); - const fillOrders = createQuoteFillOrders({ - fillableInput, - fillableOutput, - side, - count: 1, - }); - const result = fillQuoteOrders(fillOrders, fillableInput, ONE, GAS_SCHEDULE); - const totalFilledInput = result.input.plus(result.inputFee); - const totalFilledOutput = result.output.plus(result.outputFee); - expect(totalFilledInput).to.bignumber.eq(fillableInput); - assertRoughlyEquals(totalFilledOutput, fillableOutput); - expect(result.protocolFee).to.bignumber.eq(1); - }); - - it('can partially fill one simple order', () => { - const side = randomSide(); - const fillableInput = getRandomOrderSize(); - const fillableOutput = getRandomOrderSize(); - const fillOrders = createQuoteFillOrders({ - fillableInput, - fillableOutput, - side, - count: 1, - }); - const inputFillAmount = fillableInput.times(2 / 3).integerValue(); - const result = fillQuoteOrders(fillOrders, inputFillAmount, ONE, GAS_SCHEDULE); - const totalFilledInput = result.input.plus(result.inputFee); - const totalFilledOutput = result.output.plus(result.outputFee); - expect(totalFilledInput).to.bignumber.eq(inputFillAmount); - const expectedOutputFilledAmount = inputFillAmount - .div(fillableInput) - .times(fillableOutput) - .integerValue(); - assertRoughlyEquals(totalFilledOutput, expectedOutputFilledAmount); - expect(result.protocolFee).to.bignumber.eq(1); - }); - - it('can partially fill one batched order', () => { - const side = randomSide(); - const fillableInput = getRandomOrderSize(); - const fillableOutput = getRandomOrderSize(); - const fillOrders = createQuoteFillOrders({ - fillableInput, - fillableOutput, - side, - count: 1, - }); - const inputFillAmount = fillableInput.times(2 / 3).integerValue(); - const result = fillQuoteOrders(fillOrders, inputFillAmount, ONE, GAS_SCHEDULE); - const totalFilledInput = result.input.plus(result.inputFee); - const totalFilledOutput = result.output.plus(result.outputFee); - expect(totalFilledInput).to.bignumber.eq(inputFillAmount); - expect(totalFilledOutput).to.bignumber.lt(fillableOutput); - expect(result.protocolFee).to.bignumber.eq(1); - }); - - it('does not over fill one order', () => { - const side = randomSide(); - const fillableInput = getRandomOrderSize(); - const fillableOutput = getRandomOrderSize(); - const fillOrders = createQuoteFillOrders({ - fillableInput, - fillableOutput, - side, - count: 1, - }); - const inputFillAmount = fillableInput.times(3 / 2).integerValue(); - const result = fillQuoteOrders(fillOrders, inputFillAmount, ONE, GAS_SCHEDULE); - const totalFilledInput = result.input.plus(result.inputFee); - const totalFilledOutput = result.output.plus(result.outputFee); - expect(totalFilledInput).to.bignumber.eq(fillableInput); - assertRoughlyEquals(totalFilledOutput, fillableOutput); - expect(result.protocolFee).to.bignumber.eq(1); - }); - - it('can exactly fill one order with input fees', () => { - const side = randomSide(); - const fillableInput = getRandomOrderSize(); - const fillableOutput = getRandomOrderSize(); - const inputFeeRate = getRandomFeeRate(); - const fillOrders = createQuoteFillOrders({ - fillableInput, - fillableOutput, - inputFeeRate, - side, - count: 1, - }); - const signedInputFeeRate = side === MarketOperation.Sell ? inputFeeRate : -inputFeeRate; - const totalFillableInput = fillableInput.times(signedInputFeeRate + 1).integerValue(); - const result = fillQuoteOrders(fillOrders, totalFillableInput, ONE, GAS_SCHEDULE); - const totalFilledInput = result.input.plus(result.inputFee); - const totalFilledOutput = result.output.plus(result.outputFee); - assertRoughlyEquals(totalFilledInput, totalFillableInput); - assertRoughlyEquals(totalFilledOutput, fillableOutput); - assertEqualRates(result.inputFee.div(result.input), signedInputFeeRate); - expect(result.protocolFee).to.bignumber.eq(1); - }); - - it('can partially fill one order with input fees', () => { - const side = randomSide(); - const fillableInput = getRandomOrderSize(); - const fillableOutput = getRandomOrderSize(); - const inputFeeRate = getRandomFeeRate(); - const fillOrders = createQuoteFillOrders({ - fillableInput, - fillableOutput, - inputFeeRate, - side, - count: 1, - }); - const signedInputFeeRate = side === MarketOperation.Sell ? inputFeeRate : -inputFeeRate; - const totalFillableInput = fillableInput.times(signedInputFeeRate + 1).integerValue(); - const inputFillAmount = totalFillableInput.times(2 / 3).integerValue(); - const result = fillQuoteOrders(fillOrders, inputFillAmount, ONE, GAS_SCHEDULE); - const totalFilledInput = result.input.plus(result.inputFee); - const totalFilledOutput = result.output.plus(result.outputFee); - assertRoughlyEquals(totalFilledInput, inputFillAmount); - expect(totalFilledOutput).to.bignumber.lt(fillableOutput); - assertEqualRates(result.inputFee.div(result.input), signedInputFeeRate); - expect(result.protocolFee).to.bignumber.eq(1); - }); - - it('does not over fill one order with input fees', () => { - const side = randomSide(); - const fillableInput = getRandomOrderSize(); - const fillableOutput = getRandomOrderSize(); - const inputFeeRate = getRandomFeeRate(); - const fillOrders = createQuoteFillOrders({ - fillableInput, - fillableOutput, - inputFeeRate, - side, - count: 1, - }); - const signedInputFeeRate = side === MarketOperation.Sell ? inputFeeRate : -inputFeeRate; - const totalFillableInput = fillableInput.times(signedInputFeeRate + 1).integerValue(); - const inputFillAmount = totalFillableInput.times(3 / 2).integerValue(); - const result = fillQuoteOrders(fillOrders, inputFillAmount, ONE, GAS_SCHEDULE); - const totalFilledInput = result.input.plus(result.inputFee); - const totalFilledOutput = result.output.plus(result.outputFee); - assertRoughlyEquals(totalFilledInput, totalFillableInput); - assertRoughlyEquals(totalFilledOutput, fillableOutput); - assertEqualRates(result.inputFee.div(result.input), signedInputFeeRate); - expect(result.protocolFee).to.bignumber.eq(1); - }); - - it('can exactly fill one order with output fees', () => { - const side = randomSide(); - const fillableInput = getRandomOrderSize(); - const fillableOutput = getRandomOrderSize(); - const outputFeeRate = getRandomFeeRate(); - const fillOrders = createQuoteFillOrders({ - fillableInput, - fillableOutput, - outputFeeRate, - side, - count: 1, - }); - const signedOutputFeeRate = side === MarketOperation.Sell ? -outputFeeRate : outputFeeRate; - const totalFillableOutput = fillableOutput.times(signedOutputFeeRate + 1).integerValue(); - const result = fillQuoteOrders(fillOrders, fillableInput, ONE, GAS_SCHEDULE); - const totalFilledInput = result.input.plus(result.inputFee); - const totalFilledOutput = result.output.plus(result.outputFee); - assertRoughlyEquals(totalFilledInput, fillableInput); - assertRoughlyEquals(totalFilledOutput, totalFillableOutput); - assertEqualRates(result.outputFee.div(result.output), signedOutputFeeRate); - expect(result.protocolFee).to.bignumber.eq(1); - }); - - it('can partial fill one order with output fees', () => { - const side = randomSide(); - const fillableInput = getRandomOrderSize(); - const fillableOutput = getRandomOrderSize(); - const outputFeeRate = getRandomFeeRate(); - const fillOrders = createQuoteFillOrders({ - fillableInput, - fillableOutput, - outputFeeRate, - side, - count: 1, - }); - const signedOutputFeeRate = side === MarketOperation.Sell ? -outputFeeRate : outputFeeRate; - const totalFillableOutput = fillableOutput.times(signedOutputFeeRate + 1).integerValue(); - const inputFillAmount = fillableInput.times(2 / 3).integerValue(); - const result = fillQuoteOrders(fillOrders, inputFillAmount, ONE, GAS_SCHEDULE); - const totalFilledInput = result.input.plus(result.inputFee); - const totalFilledOutput = result.output.plus(result.outputFee); - assertRoughlyEquals(totalFilledInput, inputFillAmount); - expect(totalFilledOutput).to.bignumber.lt(totalFillableOutput); - assertEqualRates(result.outputFee.div(result.output), signedOutputFeeRate); - expect(result.protocolFee).to.bignumber.eq(1); - }); - - it('does not over fill one order with output fees', () => { - const side = randomSide(); - const fillableInput = getRandomOrderSize(); - const fillableOutput = getRandomOrderSize(); - const outputFeeRate = getRandomFeeRate(); - const fillOrders = createQuoteFillOrders({ - fillableInput, - fillableOutput, - outputFeeRate, - side, - count: 1, - }); - const signedOutputFeeRate = side === MarketOperation.Sell ? -outputFeeRate : outputFeeRate; - const totalFillableOutput = fillableOutput.times(signedOutputFeeRate + 1).integerValue(); - const inputFillAmount = fillableInput.times(3 / 2).integerValue(); - const result = fillQuoteOrders(fillOrders, inputFillAmount, ONE, GAS_SCHEDULE); - const totalFilledInput = result.input.plus(result.inputFee); - const totalFilledOutput = result.output.plus(result.outputFee); - assertRoughlyEquals(totalFilledInput, fillableInput); - assertRoughlyEquals(totalFilledOutput, totalFillableOutput); - assertEqualRates(result.outputFee.div(result.output), signedOutputFeeRate); - expect(result.protocolFee).to.bignumber.eq(1); - }); - - it('does not charge a protocol fee for rfq orders', () => { - const side = randomSide(); - const fillableInput = getRandomOrderSize(); - const fillableOutput = getRandomOrderSize(); - const fillOrders = createQuoteFillOrders({ - fillableInput, - fillableOutput, - side, - count: 1, - type: FillQuoteTransformerOrderType.Rfq, - }); - const result = fillQuoteOrders(fillOrders, fillableInput, ONE, GAS_SCHEDULE); - const totalFilledInput = result.input.plus(result.inputFee); - const totalFilledOutput = result.output.plus(result.outputFee); - expect(totalFilledInput).to.bignumber.eq(fillableInput); - assertRoughlyEquals(totalFilledOutput, fillableOutput); - expect(result.protocolFee).to.bignumber.eq(0); - }); - }); - - describe('multiple orders', () => { - it('can exactly fill orders', () => { - const side = randomSide(); - const fillableInput = getRandomOrderSize(); - const fillableOutput = getRandomOrderSize(); - const fillOrders = createQuoteFillOrders({ fillableInput, fillableOutput, side }); - const result = fillQuoteOrders(fillOrders, fillableInput, ONE, GAS_SCHEDULE); - const totalFilledInput = result.input.plus(result.inputFee); - const totalFilledOutput = result.output.plus(result.outputFee); - expect(totalFilledInput).to.bignumber.eq(fillableInput); - expect(totalFilledOutput).to.bignumber.eq(fillableOutput); - expect(result.protocolFee).to.bignumber.eq(fillOrders.length); - }); - - it('can partial fill orders', () => { - const side = randomSide(); - const fillableInput = getRandomOrderSize(); - const fillableOutput = getRandomOrderSize(); - const inputFillAmount = fillableInput.times(2 / 3).integerValue(); - const fillOrders = createQuoteFillOrders({ fillableInput, fillableOutput, side }); - const result = fillQuoteOrders(fillOrders, inputFillAmount, ONE, GAS_SCHEDULE); - const totalFilledInput = result.input.plus(result.inputFee); - const totalFilledOutput = result.output.plus(result.outputFee); - expect(totalFilledInput).to.bignumber.eq(inputFillAmount); - expect(totalFilledOutput).to.bignumber.lt(fillableOutput); - expect(result.protocolFee).to.bignumber.gte(1); - }); - - it('does not over fill orders', () => { - const side = randomSide(); - const fillableInput = getRandomOrderSize(); - const fillableOutput = getRandomOrderSize(); - const inputFillAmount = fillableInput.times(3 / 2).integerValue(); - const fillOrders = createQuoteFillOrders({ fillableInput, fillableOutput, side }); - const result = fillQuoteOrders(fillOrders, inputFillAmount, ONE, GAS_SCHEDULE); - const totalFilledInput = result.input.plus(result.inputFee); - const totalFilledOutput = result.output.plus(result.outputFee); - expect(totalFilledInput).to.bignumber.eq(fillableInput); - expect(totalFilledOutput).to.bignumber.eq(fillableOutput); - expect(result.protocolFee).to.bignumber.eq(fillOrders.length); - }); - - it('can exactly fill orders with input fees', () => { - const side = randomSide(); - const fillableInput = getRandomOrderSize(); - const fillableOutput = getRandomOrderSize(); - const inputFeeRate = getRandomFeeRate(); - const fillOrders = createQuoteFillOrders({ - fillableInput, - fillableOutput, - inputFeeRate, - side, - }); - const signedInputFeeRate = side === MarketOperation.Sell ? inputFeeRate : -inputFeeRate; - const totalFillableInput = fillableInput.times(signedInputFeeRate + 1).integerValue(); - const result = fillQuoteOrders(fillOrders, totalFillableInput, ONE, GAS_SCHEDULE); - const totalFilledInput = result.input.plus(result.inputFee); - const totalFilledOutput = result.output.plus(result.outputFee); - assertRoughlyEquals(totalFilledInput, totalFillableInput); - assertRoughlyEquals(totalFilledOutput, fillableOutput); - assertEqualRates(result.inputFee.div(result.input), signedInputFeeRate); - expect(result.protocolFee).to.bignumber.eq(fillOrders.length); - }); - - it('can partial fill orders with input fees', () => { - const side = randomSide(); - const fillableInput = getRandomOrderSize(); - const fillableOutput = getRandomOrderSize(); - const inputFeeRate = getRandomFeeRate(); - const fillOrders = createQuoteFillOrders({ - fillableInput, - fillableOutput, - inputFeeRate, - side, - }); - const signedInputFeeRate = side === MarketOperation.Sell ? inputFeeRate : -inputFeeRate; - const totalFillableInput = fillableInput.times(signedInputFeeRate + 1).integerValue(); - const inputFillAmount = totalFillableInput.times(2 / 3).integerValue(); - const result = fillQuoteOrders(fillOrders, inputFillAmount, ONE, GAS_SCHEDULE); - const totalFilledInput = result.input.plus(result.inputFee); - const totalFilledOutput = result.output.plus(result.outputFee); - assertRoughlyEquals(totalFilledInput, inputFillAmount); - expect(totalFilledOutput).to.bignumber.lt(fillableOutput); - assertEqualRates(result.inputFee.div(result.input), signedInputFeeRate); - expect(result.protocolFee).to.bignumber.lte(fillOrders.length); - }); - - it('does not over fill orders with input fees', () => { - const side = randomSide(); - const fillableInput = getRandomOrderSize(); - const fillableOutput = getRandomOrderSize(); - const inputFeeRate = getRandomFeeRate(); - const fillOrders = createQuoteFillOrders({ - fillableInput, - fillableOutput, - inputFeeRate, - side, - }); - const signedInputFeeRate = side === MarketOperation.Sell ? inputFeeRate : -inputFeeRate; - const totalFillableInput = fillableInput.times(signedInputFeeRate + 1).integerValue(); - const inputFillAmount = totalFillableInput.times(3 / 2).integerValue(); - const result = fillQuoteOrders(fillOrders, inputFillAmount, ONE, GAS_SCHEDULE); - const totalFilledInput = result.input.plus(result.inputFee); - const totalFilledOutput = result.output.plus(result.outputFee); - assertRoughlyEquals(totalFilledInput, totalFillableInput); - assertRoughlyEquals(totalFilledOutput, fillableOutput); - assertEqualRates(result.inputFee.div(result.input), signedInputFeeRate); - expect(result.protocolFee).to.bignumber.eq(fillOrders.length); - }); - - it('can exactly fill orders with output fees', () => { - const side = randomSide(); - const fillableInput = getRandomOrderSize(); - const fillableOutput = getRandomOrderSize(); - const outputFeeRate = getRandomFeeRate(); - const fillOrders = createQuoteFillOrders({ - fillableInput, - fillableOutput, - outputFeeRate, - side, - }); - const signedOutputFeeRate = side === MarketOperation.Sell ? -outputFeeRate : outputFeeRate; - const totalFillableOutput = fillableOutput.times(signedOutputFeeRate + 1).integerValue(); - const result = fillQuoteOrders(fillOrders, fillableInput, ONE, GAS_SCHEDULE); - const totalFilledInput = result.input.plus(result.inputFee); - const totalFilledOutput = result.output.plus(result.outputFee); - assertRoughlyEquals(totalFilledInput, fillableInput); - assertRoughlyEquals(totalFilledOutput, totalFillableOutput); - assertEqualRates(result.outputFee.div(result.output), signedOutputFeeRate); - expect(result.protocolFee).to.bignumber.eq(fillOrders.length); - }); - - it('can partial fill orders with output fees', () => { - const side = randomSide(); - const fillableInput = getRandomOrderSize(); - const fillableOutput = getRandomOrderSize(); - const outputFeeRate = getRandomFeeRate(); - const fillOrders = createQuoteFillOrders({ - fillableInput, - fillableOutput, - outputFeeRate, - side, - }); - const signedOutputFeeRate = side === MarketOperation.Sell ? -outputFeeRate : outputFeeRate; - const totalFillableOutput = fillableOutput.times(signedOutputFeeRate + 1).integerValue(); - const inputFillAmount = fillableInput.times(2 / 3).integerValue(); - const result = fillQuoteOrders(fillOrders, inputFillAmount, ONE, GAS_SCHEDULE); - const totalFilledInput = result.input.plus(result.inputFee); - const totalFilledOutput = result.output.plus(result.outputFee); - assertRoughlyEquals(totalFilledInput, inputFillAmount); - expect(totalFilledOutput).to.bignumber.lt(totalFillableOutput); - assertEqualRates(result.outputFee.div(result.output), signedOutputFeeRate); - expect(result.protocolFee).to.bignumber.lte(fillOrders.length); - }); - - it('does not over fill orders with output fees', () => { - const side = randomSide(); - const fillableInput = getRandomOrderSize(); - const fillableOutput = getRandomOrderSize(); - const outputFeeRate = getRandomFeeRate(); - const fillOrders = createQuoteFillOrders({ - fillableInput, - fillableOutput, - outputFeeRate, - side, - }); - const signedOutputFeeRate = side === MarketOperation.Sell ? -outputFeeRate : outputFeeRate; - const totalFillableOutput = fillableOutput.times(signedOutputFeeRate + 1).integerValue(); - const inputFillAmount = fillableInput.times(3 / 2).integerValue(); - const result = fillQuoteOrders(fillOrders, inputFillAmount, ONE, GAS_SCHEDULE); - const totalFilledInput = result.input.plus(result.inputFee); - const totalFilledOutput = result.output.plus(result.outputFee); - assertRoughlyEquals(totalFilledInput, fillableInput); - assertRoughlyEquals(totalFilledOutput, totalFillableOutput); - assertEqualRates(result.outputFee.div(result.output), signedOutputFeeRate); - expect(result.protocolFee).to.bignumber.eq(fillOrders.length); - }); - }); - }); - - function slipOrder( - order: OptimizedMarketOrderBase, - orderSlippage: number, - side: MarketOperation, - ): OptimizedMarketOrder { - const makerScaling = side === MarketOperation.Sell ? 1 - orderSlippage : 1; - const takerScaling = side === MarketOperation.Sell ? 1 : orderSlippage + 1; - - // tslint:disable:next-line no-unnecessary-type-assertion - const nativeFillData = order.fillData!; - const slippedFillData = { - order: { - ...nativeFillData.order, - takerAmount: nativeFillData.order.takerAmount.times(takerScaling), - makerAmount: nativeFillData.order.makerAmount.times(makerScaling), - }, - signature: nativeFillData.signature, - maxTakerTokenFillAmount: nativeFillData.maxTakerTokenFillAmount.times(takerScaling), - }; - return { - ...order, - makerAmount: order.makerAmount.times(makerScaling), - takerAmount: order.takerAmount.times(takerScaling), - fillData: slippedFillData, - }; - } - - describe('simulateBestCaseFill()', () => { - it('ignores order slippage', async () => { - const side = randomSide(); - const fillableInput = getRandomOrderSize(); - const fillableOutput = getRandomOrderSize(); - const orderSlippage = getRandomFeeRate(); - const fillOrders = createQuoteFillOrders({ - fillableInput, - fillableOutput, - side, - }); - const orders = fillOrders.map(fo => - slipOrder(fo.order as OptimizedMarketOrderBase, orderSlippage, side), - ); - const result = simulateBestCaseFill({ - orders, - side, - fillAmount: fillableInput, - gasPrice: ONE, - opts: { gasSchedule: GAS_SCHEDULE, protocolFeeMultiplier: ONE }, - }); - if (side === MarketOperation.Sell) { - expect(result.totalMakerAssetAmount).to.be.bignumber.eq(fillableOutput); - expect(result.totalTakerAssetAmount).to.be.bignumber.eq(fillableInput); - } else { - expect(result.totalMakerAssetAmount).to.be.bignumber.eq(fillableInput); - expect(result.totalTakerAssetAmount).to.be.bignumber.eq(fillableOutput); - } - }); - - it('can fully fill orders', async () => { - const side = randomSide(); - const fillableInput = getRandomOrderSize(); - const fillableOutput = getRandomOrderSize(); - const orders = createQuoteFillOrders({ - fillableInput, - fillableOutput, - side, - }).map(fo => fo.order); - const result = simulateBestCaseFill({ - orders, - side, - fillAmount: fillableInput, - gasPrice: ONE, - opts: { gasSchedule: GAS_SCHEDULE, protocolFeeMultiplier: ONE }, - }); - expect(result.protocolFeeAmount).to.bignumber.eq(orders.length); - expect(result.takerFeeTakerAssetAmount).to.bignumber.eq(0); - expect(result.takerFeeMakerAssetAmount).to.bignumber.eq(0); - expect(result.makerAssetAmount).to.bignumber.eq(result.totalMakerAssetAmount); - expect(result.takerAssetAmount).to.bignumber.eq(result.totalTakerAssetAmount); - if (side === MarketOperation.Sell) { - expect(result.totalMakerAssetAmount).to.be.bignumber.eq(fillableOutput); - expect(result.totalTakerAssetAmount).to.be.bignumber.eq(fillableInput); - } else { - expect(result.totalMakerAssetAmount).to.be.bignumber.eq(fillableInput); - expect(result.totalTakerAssetAmount).to.be.bignumber.eq(fillableOutput); - } - }); - - it('can partial fill orders', async () => { - const side = randomSide(); - const fillableInput = getRandomOrderSize(); - const fillableOutput = getRandomOrderSize(); - const orders = createQuoteFillOrders({ - fillableInput, - fillableOutput, - side, - }).map(fo => fo.order); - const inputFillAmount = fillableInput.times(Math.random()).integerValue(); - const result = simulateBestCaseFill({ - orders, - side, - fillAmount: inputFillAmount, - gasPrice: ONE, - opts: { gasSchedule: GAS_SCHEDULE, protocolFeeMultiplier: ONE }, - }); - expect(result.gas).to.gt(0); - expect(result.protocolFeeAmount).to.bignumber.gt(0); - expect(result.takerFeeTakerAssetAmount).to.bignumber.eq(0); - expect(result.takerFeeMakerAssetAmount).to.bignumber.eq(0); - expect(result.makerAssetAmount).to.bignumber.eq(result.totalMakerAssetAmount); - expect(result.takerAssetAmount).to.bignumber.eq(result.totalTakerAssetAmount); - if (side === MarketOperation.Sell) { - expect(result.totalMakerAssetAmount).to.be.bignumber.lt(fillableOutput); - expect(result.totalTakerAssetAmount).to.be.bignumber.eq(inputFillAmount); - } else { - expect(result.totalMakerAssetAmount).to.be.bignumber.eq(inputFillAmount); - expect(result.totalTakerAssetAmount).to.be.bignumber.lt(fillableOutput); - } - }); - - it('can fully fill sell orders with "input" fees', async () => { - const side = MarketOperation.Sell; - const fillableInput = getRandomOrderSize(); - const fillableOutput = getRandomOrderSize(); - const inputFeeRate = getRandomFeeRate(); - const orders = createQuoteFillOrders({ - fillableInput, - fillableOutput, - inputFeeRate, - }).map(fo => fo.order); - const signedInputFeeRate = inputFeeRate; - const totalFillableInput = fillableInput.times(signedInputFeeRate + 1).integerValue(); - const result = simulateBestCaseFill({ - orders, - side, - fillAmount: totalFillableInput, - gasPrice: ONE, - opts: { gasSchedule: GAS_SCHEDULE, protocolFeeMultiplier: ONE }, - }); - - assertRoughlyEquals(result.takerAssetAmount, fillableInput); - assertRoughlyEquals(result.totalTakerAssetAmount, totalFillableInput); - assertRoughlyEquals(result.makerAssetAmount, fillableOutput); - assertRoughlyEquals(result.totalMakerAssetAmount, fillableOutput); - expect(result.makerAssetAmount).to.bignumber.eq(result.totalMakerAssetAmount); - expect(result.takerFeeMakerAssetAmount).to.bignumber.eq(0); - }); - - it('can partially fill sell orders with "input" fees', async () => { - const side = MarketOperation.Sell; - const fillableInput = getRandomOrderSize(); - const fillableOutput = getRandomOrderSize(); - const inputFeeRate = getRandomFeeRate(); - const orders = createQuoteFillOrders({ - fillableInput, - fillableOutput, - inputFeeRate, - side, - }).map(fo => fo.order); - const signedInputFeeRate = inputFeeRate; - const totalFillableInput = fillableInput.times(signedInputFeeRate + 1).integerValue(); - const inputFillAmount = totalFillableInput.times(2 / 3).integerValue(); - const result = simulateBestCaseFill({ - orders, - side, - fillAmount: inputFillAmount, - gasPrice: ONE, - opts: { gasSchedule: GAS_SCHEDULE, protocolFeeMultiplier: ONE }, - }); - expect(result.gas).to.gt(0); - expect(result.protocolFeeAmount).to.bignumber.gt(0); - assertRoughlyEquals(result.totalTakerAssetAmount, inputFillAmount); - expect(result.makerAssetAmount).to.bignumber.lt(fillableOutput); - expect(result.makerAssetAmount).to.bignumber.eq(result.totalMakerAssetAmount); - expect(result.takerFeeMakerAssetAmount).to.bignumber.eq(0); - }); - - it('can fully fill buy orders with "output" fees', async () => { - const side = MarketOperation.Buy; - const fillableInput = getRandomOrderSize(); - const fillableOutput = getRandomOrderSize(); - const outputFeeRate = getRandomFeeRate(); - const orders = createQuoteFillOrders({ - fillableInput, - fillableOutput, - outputFeeRate, - side, - }).map(fo => fo.order); - const signedOutputFeeRate = outputFeeRate; - const totalFillableOutput = fillableOutput.times(signedOutputFeeRate + 1).integerValue(); - const result = simulateBestCaseFill({ - orders, - side, - fillAmount: fillableInput, - gasPrice: ONE, - opts: { gasSchedule: GAS_SCHEDULE, protocolFeeMultiplier: ONE }, - }); - expect(result.protocolFeeAmount).to.bignumber.eq(orders.length); - - assertRoughlyEquals(result.makerAssetAmount, fillableInput); - assertRoughlyEquals(result.totalMakerAssetAmount, fillableInput); - assertRoughlyEquals(result.takerAssetAmount, fillableOutput); - assertRoughlyEquals(result.totalTakerAssetAmount, totalFillableOutput); - expect(result.makerAssetAmount).to.bignumber.eq(result.totalMakerAssetAmount); - expect(result.takerFeeMakerAssetAmount).to.bignumber.eq(0); - }); - - it('can partially fill buy orders with "output" fees', async () => { - const side = MarketOperation.Buy; - const fillableInput = getRandomOrderSize(); - const fillableOutput = getRandomOrderSize(); - const outputFeeRate = getRandomFeeRate(); - const orders = createQuoteFillOrders({ - fillableInput, - fillableOutput, - outputFeeRate, - side, - }).map(fo => fo.order); - const inputFillAmount = fillableInput.times(2 / 3).integerValue(); - const result = simulateBestCaseFill({ - orders, - side, - fillAmount: inputFillAmount, - gasPrice: ONE, - opts: { gasSchedule: GAS_SCHEDULE, protocolFeeMultiplier: ONE }, - }); - expect(result.gas).to.gt(0); - expect(result.protocolFeeAmount).to.bignumber.gt(0); - assertRoughlyEquals(result.totalMakerAssetAmount, inputFillAmount); - expect(result.takerAssetAmount).to.bignumber.lt(fillableOutput); - expect(result.makerAssetAmount).to.bignumber.eq(result.totalMakerAssetAmount); - expect(result.takerFeeMakerAssetAmount).to.bignumber.eq(0); - }); - }); - - describe('simulateWorstCaseFill()', () => { - it('includes order slippage', async () => { - const side = randomSide(); - const fillableInput = getRandomOrderSize(); - const fillableOutput = getRandomOrderSize(); - const slippage = getRandomFeeRate(); - const orders = createQuoteFillOrders({ - fillableInput, - fillableOutput, - side, - }).map(fo => fo.order); - - const result = simulateWorstCaseFill({ - orders, - side, - fillAmount: fillableInput, - gasPrice: ONE, - opts: { gasSchedule: GAS_SCHEDULE, protocolFeeMultiplier: ONE, slippage }, - }); - if (side === MarketOperation.Sell) { - const slippedOutput = fillableOutput.times(1 - slippage).integerValue(); - assertRoughlyEquals(result.totalMakerAssetAmount, slippedOutput); - assertRoughlyEquals(result.totalTakerAssetAmount, fillableInput); - } else { - const slippedOutput = fillableOutput.times(slippage + 1).integerValue(); - assertRoughlyEquals(result.totalMakerAssetAmount, fillableInput); - assertRoughlyEquals(result.totalTakerAssetAmount, slippedOutput); - } - }); - - it('expects worse price than the best case, even if orders are unsorted', async () => { - const side = randomSide(); - const fillableInput = getRandomOrderSize(); - const fillableOutput = getRandomOrderSize(); - const orderSlippage = getRandomFeeRate(); - let orders = createQuoteFillOrders({ - fillableInput, - fillableOutput, - side, - }).map(fo => - slipOrder(fo.order as OptimizedMarketOrderBase, orderSlippage, side), - ); - orders = [...orders.slice(1), orders[0]]; - const bestCase = simulateBestCaseFill({ - orders, - side, - fillAmount: fillableInput, - gasPrice: ONE, - opts: { gasSchedule: GAS_SCHEDULE, protocolFeeMultiplier: ONE }, - }); - const worstCase = simulateWorstCaseFill({ - orders, - side, - fillAmount: fillableInput, - gasPrice: ONE, - opts: { gasSchedule: GAS_SCHEDULE, slippage: orderSlippage }, - }); - const bestPrice = bestCase.makerAssetAmount.div(bestCase.totalTakerAssetAmount); - const worstPrice = worstCase.makerAssetAmount.div(worstCase.totalTakerAssetAmount); - expect(worstPrice).to.be.bignumber.lt(bestPrice); - }); - }); -}); // tslint:disable: max-file-line-count diff --git a/packages/asset-swapper/test/rfq_maker_blacklist_test.ts b/packages/asset-swapper/test/rfq_maker_blacklist_test.ts deleted file mode 100644 index 50a1f5d01d..0000000000 --- a/packages/asset-swapper/test/rfq_maker_blacklist_test.ts +++ /dev/null @@ -1,40 +0,0 @@ -import * as chai from 'chai'; -import 'mocha'; - -import { constants } from '../src/constants'; -import { RfqMakerBlacklist } from '../src/utils/rfq_maker_blacklist'; - -import { chaiSetup } from './utils/chai_setup'; - -chaiSetup.configure(); -const expect = chai.expect; - -describe('RfqMakerBlacklist', () => { - it('does blacklist', async () => { - const blacklistDurationMinutes = 1; - const timeoutStreakThreshold = 3; - const blacklist = new RfqMakerBlacklist(blacklistDurationMinutes, timeoutStreakThreshold); - blacklist.logTimeoutOrLackThereof('makerA', true); - blacklist.logTimeoutOrLackThereof('makerA', true); - expect(blacklist.isMakerBlacklisted('makerA')).to.be.false(); - blacklist.logTimeoutOrLackThereof('makerA', true); - const sleepTimeMs = 10; - await new Promise(r => { - setTimeout(r, sleepTimeMs); - }); - expect(blacklist.isMakerBlacklisted('makerA')).to.be.true(); - }); - it('does unblacklist', async () => { - const blacklistDurationMinutes = 0.1; - const timeoutStreakThreshold = 3; - const blacklist = new RfqMakerBlacklist(blacklistDurationMinutes, timeoutStreakThreshold); - blacklist.logTimeoutOrLackThereof('makerA', true); - blacklist.logTimeoutOrLackThereof('makerA', true); - blacklist.logTimeoutOrLackThereof('makerA', true); - expect(blacklist.isMakerBlacklisted('makerA')).to.be.true(); - await new Promise(r => { - setTimeout(r, blacklistDurationMinutes * constants.ONE_MINUTE_MS); - }); - expect(blacklist.isMakerBlacklisted('makerA')).to.be.false(); - }); -}); diff --git a/packages/asset-swapper/test/token_adjacency_graph_test.ts b/packages/asset-swapper/test/token_adjacency_graph_test.ts deleted file mode 100644 index cfaf9ecfb2..0000000000 --- a/packages/asset-swapper/test/token_adjacency_graph_test.ts +++ /dev/null @@ -1,108 +0,0 @@ -import * as chai from 'chai'; -import 'mocha'; - -import { TokenAdjacencyGraphBuilder } from '../src/utils/token_adjacency_graph'; - -import { chaiSetup } from './utils/chai_setup'; - -chaiSetup.configure(); -const expect = chai.expect; - -describe('TokenAdjacencyGraphBuilder and TokenAdjacencyGraph', () => { - describe('constructor', () => { - it('sanitizes passed default tokens to lower case', async () => { - const graph = new TokenAdjacencyGraphBuilder(['DEFAULT_1', 'DEFAULT_2']).build(); - - expect(graph.getAdjacentTokens('random_token')).to.deep.eq(['default_1', 'default_2']); - }); - }); - - describe('add', () => { - it('adds a new token path to the graph', async () => { - const graph = new TokenAdjacencyGraphBuilder(['default_1', 'default_2']).add('token_a', 'token_b').build(); - - expect(graph.getAdjacentTokens('token_a')).to.deep.eq(['default_1', 'default_2', 'token_b']); - }); - - it('adds lower-cased token path to the graph', async () => { - const graph = new TokenAdjacencyGraphBuilder(['default_1', 'default_2']).add('TOKEN_A', 'TOKEN_B').build(); - - expect(graph.getAdjacentTokens('token_a')).to.deep.eq(['default_1', 'default_2', 'token_b']); - }); - - it('ignores an existing to token', async () => { - const graph = new TokenAdjacencyGraphBuilder() - .add('token_a', 'token_b') - .add('token_a', 'token_b') - .build(); - - expect(graph.getAdjacentTokens('token_a')).to.deep.eq(['token_b']); - }); - }); - - describe('addBidirectional', () => { - it('adds a bidirectional path to the graph', async () => { - const graph = new TokenAdjacencyGraphBuilder(['default_1']).addBidirectional('token_a', 'token_b').build(); - - expect(graph.getAdjacentTokens('token_a')).to.deep.eq(['default_1', 'token_b']); - expect(graph.getAdjacentTokens('token_b')).to.deep.eq(['default_1', 'token_a']); - }); - }); - - describe('addCompleteSubgraph', () => { - it('adds a complete subgraph to the graph', async () => { - const graph = new TokenAdjacencyGraphBuilder(['default_1']) - .addCompleteSubgraph(['token_a', 'token_b', 'token_c', 'token_d']) - .build(); - - expect(graph.getAdjacentTokens('token_a')).to.deep.eq(['default_1', 'token_b', 'token_c', 'token_d']); - expect(graph.getAdjacentTokens('token_b')).to.deep.eq(['default_1', 'token_a', 'token_c', 'token_d']); - expect(graph.getAdjacentTokens('token_c')).to.deep.eq(['default_1', 'token_a', 'token_b', 'token_d']); - expect(graph.getAdjacentTokens('token_d')).to.deep.eq(['default_1', 'token_a', 'token_b', 'token_c']); - }); - }); - - describe('tap', () => { - it('applies callback correctly', async () => { - const graph = new TokenAdjacencyGraphBuilder(['default_1']) - .tap(g => { - g.add('token_a', 'token_b'); - g.add('token_c', 'token_d'); - }) - .build(); - - expect(graph.getAdjacentTokens('token_a')).to.deep.eq(['default_1', 'token_b']); - expect(graph.getAdjacentTokens('token_c')).to.deep.eq(['default_1', 'token_d']); - }); - }); - - describe('getIntermediateTokens', () => { - it('returns intermediate tokens without a duplicate ', async () => { - const graph = new TokenAdjacencyGraphBuilder(['default_1']) - .add('token_a', 'token_b') - .add('token_c', 'token_b') - .build(); - - expect(graph.getIntermediateTokens('token_a', 'token_c')).to.deep.eq(['default_1', 'token_b']); - }); - - it('returns intermediate tokens after lower-casing taker and maker tokens', async () => { - const graph = new TokenAdjacencyGraphBuilder(['default_1']) - .add('token_a', 'token_b') - .add('token_c', 'token_d') - .build(); - - expect(graph.getIntermediateTokens('TOKEN_a', 'token_C')).to.deep.eq(['default_1', 'token_b', 'token_d']); - }); - - it('returns intermediate tokens excluding taker token or maker token ', async () => { - const graph = new TokenAdjacencyGraphBuilder(['default_1']) - .addBidirectional('token_a', 'token_b') - .addBidirectional('token_b', 'token_c') - .addBidirectional('token_c', 'token_a') - .build(); - - expect(graph.getIntermediateTokens('token_a', 'token_c')).to.deep.eq(['default_1', 'token_b']); - }); - }); -}); diff --git a/packages/asset-swapper/test/utils/chai_setup.ts b/packages/asset-swapper/test/utils/chai_setup.ts deleted file mode 100644 index 1a87330932..0000000000 --- a/packages/asset-swapper/test/utils/chai_setup.ts +++ /dev/null @@ -1,13 +0,0 @@ -import * as chai from 'chai'; -import chaiAsPromised = require('chai-as-promised'); -import ChaiBigNumber = require('chai-bignumber'); -import * as dirtyChai from 'dirty-chai'; - -export const chaiSetup = { - configure(): void { - chai.config.includeStack = true; - chai.use(ChaiBigNumber()); - chai.use(dirtyChai); - chai.use(chaiAsPromised); - }, -}; diff --git a/packages/asset-swapper/test/utils/mock_sampler_contract.ts b/packages/asset-swapper/test/utils/mock_sampler_contract.ts deleted file mode 100644 index 23df160747..0000000000 --- a/packages/asset-swapper/test/utils/mock_sampler_contract.ts +++ /dev/null @@ -1,233 +0,0 @@ -import { ContractTxFunctionObj } from '@0x/base-contract'; -import { constants } from '@0x/contracts-test-utils'; -import { LimitOrderFields, Signature } from '@0x/protocol-utils'; -import { BigNumber, hexUtils } from '@0x/utils'; - -import { SamplerCallResult } from '../../src/types'; -import { ERC20BridgeSamplerContract } from '../../src/wrappers'; - -export type GetOrderFillableAssetAmountResult = BigNumber[]; -export type GetOrderFillableAssetAmountHandler = ( - orders: LimitOrderFields[], - signatures: Signature[], - devUtilsAddress: string, -) => GetOrderFillableAssetAmountResult; - -export type SampleResults = BigNumber[]; -export type SampleSellsUniswapHandler = ( - router: string, - takerToken: string, - makerToken: string, - takerTokenAmounts: BigNumber[], -) => SampleResults; -export type SampleBuysUniswapHandler = ( - router: string, - takerToken: string, - makerToken: string, - makerTokenAmounts: BigNumber[], -) => SampleResults; -export type SampleSellsEth2DaiHandler = ( - router: string, - takerToken: string, - makerToken: string, - takerTokenAmounts: BigNumber[], -) => SampleResults; -export type SampleBuysEth2DaiHandler = ( - router: string, - takerToken: string, - makerToken: string, - makerTokenAmounts: BigNumber[], -) => SampleResults; -export type SampleUniswapV2Handler = (router: string, path: string[], assetAmounts: BigNumber[]) => SampleResults; -export type SampleBuysMultihopHandler = (path: string[], takerTokenAmounts: BigNumber[]) => SampleResults; -export type SampleSellsLPHandler = ( - providerAddress: string, - takerToken: string, - makerToken: string, - takerTokenAmounts: BigNumber[], -) => SampleResults; -export type SampleSellsMultihopHandler = (path: string[], takerTokenAmounts: BigNumber[]) => SampleResults; - -const DUMMY_PROVIDER = { - sendAsync: (..._args: any[]): any => { - /* no-op */ - }, -}; - -interface Handlers { - getLimitOrderFillableMakerAssetAmounts: GetOrderFillableAssetAmountHandler; - getLimitOrderFillableTakerAssetAmounts: GetOrderFillableAssetAmountHandler; - sampleSellsFromLiquidityProvider: SampleSellsLPHandler; - sampleSellsFromUniswap: SampleSellsUniswapHandler; - sampleSellsFromUniswapV2: SampleUniswapV2Handler; - sampleBuysFromUniswap: SampleBuysUniswapHandler; - sampleBuysFromUniswapV2: SampleUniswapV2Handler; - sampleBuysFromLiquidityProvider: SampleSellsLPHandler; -} - -// tslint:disable: no-unbound-method - -export class MockSamplerContract extends ERC20BridgeSamplerContract { - private readonly _handlers: Partial = {}; - - public constructor(handlers: Partial = {}) { - super(constants.NULL_ADDRESS, DUMMY_PROVIDER); - this._handlers = handlers; - } - - public batchCall(callDatas: string[]): ContractTxFunctionObj { - return { - ...super.batchCall(callDatas), - callAsync: async (..._callArgs: any[]) => - callDatas.map(callData => ({ success: true, data: this._callEncodedFunction(callData) })), - }; - } - - public getLimitOrderFillableMakerAssetAmounts( - orders: LimitOrderFields[], - signatures: Signature[], - ): ContractTxFunctionObj { - return this._wrapCall( - super.getLimitOrderFillableMakerAssetAmounts, - this._handlers.getLimitOrderFillableMakerAssetAmounts, - orders, - signatures, - constants.NULL_ADDRESS, - ); - } - - public getLimitOrderFillableTakerAssetAmounts( - orders: LimitOrderFields[], - signatures: Signature[], - ): ContractTxFunctionObj { - return this._wrapCall( - super.getLimitOrderFillableTakerAssetAmounts, - this._handlers.getLimitOrderFillableTakerAssetAmounts, - orders, - signatures, - constants.NULL_ADDRESS, - ); - } - - public sampleSellsFromUniswap( - router: string, - takerToken: string, - makerToken: string, - takerAssetAmounts: BigNumber[], - ): ContractTxFunctionObj { - return this._wrapCall( - super.sampleSellsFromUniswap, - this._handlers.sampleSellsFromUniswap, - router, - takerToken, - makerToken, - takerAssetAmounts, - ); - } - - public sampleSellsFromUniswapV2( - router: string, - path: string[], - takerAssetAmounts: BigNumber[], - ): ContractTxFunctionObj { - return this._wrapCall( - super.sampleSellsFromUniswapV2, - this._handlers.sampleSellsFromUniswapV2, - router, - path, - takerAssetAmounts, - ); - } - - public sampleSellsFromLiquidityProvider( - providerAddress: string, - takerToken: string, - makerToken: string, - takerAssetAmounts: BigNumber[], - ): ContractTxFunctionObj { - return this._wrapCall( - super.sampleSellsFromLiquidityProvider, - this._handlers.sampleSellsFromLiquidityProvider, - providerAddress, - takerToken, - makerToken, - takerAssetAmounts, - ); - } - - public sampleBuysFromUniswap( - router: string, - takerToken: string, - makerToken: string, - makerAssetAmounts: BigNumber[], - ): ContractTxFunctionObj { - return this._wrapCall( - super.sampleBuysFromUniswap, - this._handlers.sampleBuysFromUniswap, - router, - takerToken, - makerToken, - makerAssetAmounts, - ); - } - - public sampleBuysFromUniswapV2( - router: string, - path: string[], - makerAssetAmounts: BigNumber[], - ): ContractTxFunctionObj { - return this._wrapCall( - super.sampleBuysFromUniswapV2, - this._handlers.sampleBuysFromUniswapV2, - router, - path, - makerAssetAmounts, - ); - } - - private _callEncodedFunction(callData: string): string { - if (callData === '0x') { - return callData; - } - // tslint:disable-next-line: custom-no-magic-numbers - const selector = hexUtils.slice(callData, 0, 4); - for (const [name, handler] of Object.entries(this._handlers)) { - if (handler && this.getSelector(name) === selector) { - const args = this.getABIDecodedTransactionData(name, callData); - const result = (handler as any)(...args); - const encoder = this._lookupAbiEncoder(this.getFunctionSignature(name)); - if (encoder.getReturnValueDataItem().components!.length === 1) { - return encoder.encodeReturnValues([result]); - } else { - return encoder.encodeReturnValues(result); - } - } - } - if (selector === this.getSelector('batchCall')) { - const calls = this.getABIDecodedTransactionData('batchCall', callData); - const results: SamplerCallResult[] = calls.map(cd => ({ - success: true, - data: this._callEncodedFunction(cd), - })); - return this._lookupAbiEncoder(this.getFunctionSignature('batchCall')).encodeReturnValues([results]); - } - throw new Error(`Unkown selector: ${selector}`); - } - - private _wrapCall( - superFn: (this: MockSamplerContract, ...args: TArgs) => ContractTxFunctionObj, - handler?: (this: MockSamplerContract, ...args: TArgs) => TResult, - // tslint:disable-next-line: trailing-comma - ...args: TArgs - ): ContractTxFunctionObj { - return { - ...superFn.call(this, ...args), - callAsync: async (..._callArgs: any[]): Promise => { - if (!handler) { - throw new Error(`${superFn.name} handler undefined`); - } - return handler.call(this, ...args); - }, - }; - } -} diff --git a/packages/asset-swapper/test/utils/swap_quote.ts b/packages/asset-swapper/test/utils/swap_quote.ts deleted file mode 100644 index 11115206e7..0000000000 --- a/packages/asset-swapper/test/utils/swap_quote.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { BigNumber } from '@0x/utils'; - -import { ERC20BridgeSource, OptimizedMarketOrder } from '../../src'; -import { constants } from '../../src/constants'; -import { MarketOperation, SwapQuote, SwapQuoteBase } from '../../src/types'; - -/** - * Creates a swap quote given orders. - */ -export async function getFullyFillableSwapQuoteWithNoFeesAsync( - makerToken: string, - takerToken: string, - orders: OptimizedMarketOrder[], - operation: MarketOperation, - gasPrice: BigNumber, -): Promise { - const makerAmount = BigNumber.sum(...[0, ...orders.map(o => o.makerAmount)]); - const takerAmount = BigNumber.sum(...[0, ...orders.map(o => o.takerAmount)]); - const protocolFeePerOrder = constants.PROTOCOL_FEE_MULTIPLIER.times(gasPrice); - const quoteInfo = { - makerAmount, - feeTakerTokenAmount: constants.ZERO_AMOUNT, - takerAmount, - totalTakerAmount: takerAmount, - protocolFeeInWeiAmount: protocolFeePerOrder.times(orders.length), - gas: 200e3, - slippage: 0, - }; - - const breakdown = { - [ERC20BridgeSource.Native]: new BigNumber(1), - }; - - const quoteBase: SwapQuoteBase = { - makerToken, - takerToken, - orders: orders.map(order => ({ ...order, fills: [] })), - gasPrice, - bestCaseQuoteInfo: quoteInfo, - worstCaseQuoteInfo: quoteInfo, - sourceBreakdown: breakdown, - isTwoHop: false, - takerAmountPerEth: constants.ZERO_AMOUNT, - makerAmountPerEth: constants.ZERO_AMOUNT, - makerTokenDecimals: 18, - takerTokenDecimals: 18, - blockNumber: 1337420, - }; - - if (operation === MarketOperation.Buy) { - return { - ...quoteBase, - type: MarketOperation.Buy, - makerTokenFillAmount: makerAmount, - }; - } else { - return { - ...quoteBase, - type: MarketOperation.Sell, - takerTokenFillAmount: takerAmount, - }; - } -} diff --git a/packages/asset-swapper/test/utils/test_helpers.ts b/packages/asset-swapper/test/utils/test_helpers.ts deleted file mode 100644 index ea116cf7a6..0000000000 --- a/packages/asset-swapper/test/utils/test_helpers.ts +++ /dev/null @@ -1,95 +0,0 @@ -import { BigNumber } from '@0x/utils'; -import axios, { AxiosInstance } from 'axios'; -import AxiosMockAdapter from 'axios-mock-adapter'; -import * as _ from 'lodash'; - -import { InsufficientAssetLiquidityError } from '../../src/errors'; -import { AltMockedRfqQuoteResponse, MockedRfqQuoteResponse } from '../../src/types'; - -export enum RfqQuoteEndpoint { - Indicative = 'price', - Firm = 'quote', -} - -export const testHelpers = { - expectInsufficientLiquidityErrorAsync: async ( - expect: Chai.ExpectStatic, - functionWhichTriggersErrorAsync: () => Promise, - expectedAmountAvailableToFill: BigNumber, - ): Promise => { - let wasErrorThrown = false; - try { - await functionWhichTriggersErrorAsync(); - } catch (e) { - wasErrorThrown = true; - expect(e).to.be.instanceOf(InsufficientAssetLiquidityError); - if (expectedAmountAvailableToFill) { - expect(e.amountAvailableToFill).to.be.bignumber.equal(expectedAmountAvailableToFill); - } else { - expect(e.amountAvailableToFill).to.be.undefined(); - } - } - - expect(wasErrorThrown).to.be.true(); - }, - /** - * A helper utility for testing which mocks out - * requests to RFQ-T/M providers - */ - withMockedRfqQuotes: async ( - standardMockedResponses: MockedRfqQuoteResponse[], - altMockedResponses: AltMockedRfqQuoteResponse[], - quoteType: RfqQuoteEndpoint, - afterResponseCallback: () => Promise, - axiosClient: AxiosInstance = axios, - ): Promise => { - const mockedAxios = new AxiosMockAdapter(axiosClient, { onNoMatch: 'throwException' } as any); - try { - // Mock out Standard RFQ-T/M responses - for (const mockedResponse of standardMockedResponses) { - const { endpoint, requestApiKey, requestParams, responseData, responseCode } = mockedResponse; - const requestHeaders = { - Accept: 'application/json, text/plain, */*', - '0x-api-key': requestApiKey, - '0x-integrator-id': requestApiKey, - }; - if (mockedResponse.callback !== undefined) { - mockedAxios - .onGet(`${endpoint}/${quoteType}`, { params: requestParams }, requestHeaders) - .reply(mockedResponse.callback); - } else { - mockedAxios - .onGet(`${endpoint}/${quoteType}`, { params: requestParams }, requestHeaders) - .replyOnce(responseCode, responseData); - } - } - // Mock out Alt RFQ-T/M responses - for (const mockedResponse of altMockedResponses) { - const { endpoint, mmApiKey, requestData, responseData, responseCode } = mockedResponse; - const requestHeaders = { - Accept: 'application/json, text/plain, */*', - 'Content-Type': 'application/json', - Authorization: `Bearer ${mmApiKey}`, - }; - mockedAxios - .onPost( - `${endpoint}/quotes`, - // hack to get AxiosMockAdapter to recognize the match - // b/t the mock data and the request data - { - asymmetricMatch: (x: any) => { - return _.isEqual(requestData, x); - }, - }, - requestHeaders, - ) - .replyOnce(responseCode, responseData); - } - // Perform the callback function, e.g. a test validation - await afterResponseCallback(); - } finally { - // Ensure we always restore axios afterwards - mockedAxios.restore(); - } - }, -}; diff --git a/packages/asset-swapper/test/utils/utils.ts b/packages/asset-swapper/test/utils/utils.ts deleted file mode 100644 index cc0e14c825..0000000000 --- a/packages/asset-swapper/test/utils/utils.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { getRandomInteger } from '@0x/contracts-test-utils'; -import { Signature, SignatureType } from '@0x/protocol-utils'; -import { BigNumber, generatePseudoRandom256BitNumber, hexUtils, Numberish } from '@0x/utils'; -import { Web3Wrapper } from '@0x/web3-wrapper'; - -const TOKEN_DECIMALS = 18; - -// tslint:disable:custom-no-magic-numbers -export const baseUnitAmount = (unitAmount: number, decimals = TOKEN_DECIMALS): BigNumber => { - return Web3Wrapper.toBaseUnitAmount(new BigNumber(unitAmount), decimals); -}; - -// tslint:disable:completed-docs -export function generatePseudoRandomSalt(): BigNumber { - const salt = generatePseudoRandom256BitNumber(); - return salt; -} - -export function getRandomAmount(maxAmount: Numberish = '1e18'): BigNumber { - return getRandomInteger(1, maxAmount); -} - -export function getRandomSignature(): Signature { - return { - v: 1, - r: hexUtils.random(32), - s: hexUtils.random(32), - signatureType: SignatureType.Invalid, - }; -} diff --git a/packages/asset-swapper/test/utils/web3_wrapper.ts b/packages/asset-swapper/test/utils/web3_wrapper.ts deleted file mode 100644 index 32f8543267..0000000000 --- a/packages/asset-swapper/test/utils/web3_wrapper.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { web3Factory } from '@0x/dev-utils'; -import { Web3ProviderEngine } from '@0x/subproviders'; -import { Web3Wrapper } from '@0x/web3-wrapper'; - -const provider: Web3ProviderEngine = web3Factory.getRpcProvider({ shouldUseInProcessGanache: true }); -const web3Wrapper = new Web3Wrapper(provider); - -export { provider, web3Wrapper }; diff --git a/packages/asset-swapper/test/wrappers.ts b/packages/asset-swapper/test/wrappers.ts deleted file mode 100644 index 3850f01359..0000000000 --- a/packages/asset-swapper/test/wrappers.ts +++ /dev/null @@ -1,52 +0,0 @@ -/* - * ----------------------------------------------------------------------------- - * Warning: This file is auto-generated by contracts-gen. Don't edit manually. - * ----------------------------------------------------------------------------- - */ -export * from '../test/generated-wrappers/approximate_buys'; -export * from '../test/generated-wrappers/balance_checker'; -export * from '../test/generated-wrappers/balancer_sampler'; -export * from '../test/generated-wrappers/balancer_v2_batch_sampler'; -export * from '../test/generated-wrappers/balancer_v2_common'; -export * from '../test/generated-wrappers/balancer_v2_sampler'; -export * from '../test/generated-wrappers/bancor_sampler'; -export * from '../test/generated-wrappers/bancor_v3_sampler'; -export * from '../test/generated-wrappers/compound_sampler'; -export * from '../test/generated-wrappers/curve_sampler'; -export * from '../test/generated-wrappers/d_o_d_o_sampler'; -export * from '../test/generated-wrappers/d_o_d_o_v2_sampler'; -export * from '../test/generated-wrappers/erc20_bridge_sampler'; -export * from '../test/generated-wrappers/fake_taker'; -export * from '../test/generated-wrappers/g_m_x_sampler'; -export * from '../test/generated-wrappers/i_balancer'; -export * from '../test/generated-wrappers/i_balancer_v2_vault'; -export * from '../test/generated-wrappers/i_bancor'; -export * from '../test/generated-wrappers/i_bancor_v3'; -export * from '../test/generated-wrappers/i_curve'; -export * from '../test/generated-wrappers/i_m_stable'; -export * from '../test/generated-wrappers/i_mooniswap'; -export * from '../test/generated-wrappers/i_multi_bridge'; -export * from '../test/generated-wrappers/i_platypus'; -export * from '../test/generated-wrappers/i_shell'; -export * from '../test/generated-wrappers/i_uniswap_exchange_quotes'; -export * from '../test/generated-wrappers/i_uniswap_v2_router01'; -export * from '../test/generated-wrappers/igmx'; -export * from '../test/generated-wrappers/kyber_dmm_sampler'; -export * from '../test/generated-wrappers/lido_sampler'; -export * from '../test/generated-wrappers/liquidity_provider_sampler'; -export * from '../test/generated-wrappers/m_stable_sampler'; -export * from '../test/generated-wrappers/maker_p_s_m_sampler'; -export * from '../test/generated-wrappers/mooniswap_sampler'; -export * from '../test/generated-wrappers/native_order_sampler'; -export * from '../test/generated-wrappers/platypus_sampler'; -export * from '../test/generated-wrappers/sampler_utils'; -export * from '../test/generated-wrappers/shell_sampler'; -export * from '../test/generated-wrappers/synthetix_sampler'; -export * from '../test/generated-wrappers/test_native_order_sampler'; -export * from '../test/generated-wrappers/two_hop_sampler'; -export * from '../test/generated-wrappers/uniswap_sampler'; -export * from '../test/generated-wrappers/uniswap_v2_sampler'; -export * from '../test/generated-wrappers/uniswap_v3_sampler'; -export * from '../test/generated-wrappers/utility_sampler'; -export * from '../test/generated-wrappers/velodrome_sampler'; -export * from '../test/generated-wrappers/woo_p_p_sampler'; diff --git a/packages/asset-swapper/tsconfig.json b/packages/asset-swapper/tsconfig.json deleted file mode 100644 index b656cc4425..0000000000 --- a/packages/asset-swapper/tsconfig.json +++ /dev/null @@ -1,57 +0,0 @@ -{ - "extends": "../../tsconfig", - "compilerOptions": { "outDir": "lib", "rootDir": ".", "resolveJsonModule": true }, - "include": ["./src/**/*", "./test/**/*", "./generated-wrappers/**/*"], - "files": [ - "generated-artifacts/BalanceChecker.json", - "generated-artifacts/ERC20BridgeSampler.json", - "generated-artifacts/FakeTaker.json", - "test/generated-artifacts/ApproximateBuys.json", - "test/generated-artifacts/BalanceChecker.json", - "test/generated-artifacts/BalancerSampler.json", - "test/generated-artifacts/BalancerV2BatchSampler.json", - "test/generated-artifacts/BalancerV2Common.json", - "test/generated-artifacts/BalancerV2Sampler.json", - "test/generated-artifacts/BancorSampler.json", - "test/generated-artifacts/BancorV3Sampler.json", - "test/generated-artifacts/CompoundSampler.json", - "test/generated-artifacts/CurveSampler.json", - "test/generated-artifacts/DODOSampler.json", - "test/generated-artifacts/DODOV2Sampler.json", - "test/generated-artifacts/ERC20BridgeSampler.json", - "test/generated-artifacts/FakeTaker.json", - "test/generated-artifacts/GMXSampler.json", - "test/generated-artifacts/IBalancer.json", - "test/generated-artifacts/IBalancerV2Vault.json", - "test/generated-artifacts/IBancor.json", - "test/generated-artifacts/IBancorV3.json", - "test/generated-artifacts/ICurve.json", - "test/generated-artifacts/IGMX.json", - "test/generated-artifacts/IMStable.json", - "test/generated-artifacts/IMooniswap.json", - "test/generated-artifacts/IMultiBridge.json", - "test/generated-artifacts/IPlatypus.json", - "test/generated-artifacts/IShell.json", - "test/generated-artifacts/IUniswapExchangeQuotes.json", - "test/generated-artifacts/IUniswapV2Router01.json", - "test/generated-artifacts/KyberDmmSampler.json", - "test/generated-artifacts/LidoSampler.json", - "test/generated-artifacts/LiquidityProviderSampler.json", - "test/generated-artifacts/MStableSampler.json", - "test/generated-artifacts/MakerPSMSampler.json", - "test/generated-artifacts/MooniswapSampler.json", - "test/generated-artifacts/NativeOrderSampler.json", - "test/generated-artifacts/PlatypusSampler.json", - "test/generated-artifacts/SamplerUtils.json", - "test/generated-artifacts/ShellSampler.json", - "test/generated-artifacts/SynthetixSampler.json", - "test/generated-artifacts/TestNativeOrderSampler.json", - "test/generated-artifacts/TwoHopSampler.json", - "test/generated-artifacts/UniswapSampler.json", - "test/generated-artifacts/UniswapV2Sampler.json", - "test/generated-artifacts/UniswapV3Sampler.json", - "test/generated-artifacts/UtilitySampler.json", - "test/generated-artifacts/VelodromeSampler.json", - "test/generated-artifacts/WooPPSampler.json" - ] -} diff --git a/packages/asset-swapper/tslint.json b/packages/asset-swapper/tslint.json deleted file mode 100644 index ddc50eb3c7..0000000000 --- a/packages/asset-swapper/tslint.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "extends": ["@0x/tslint-config"], - "rules": { - "array-type": false, - "custom-no-magic-numbers": false, - "max-file-line-count": false, - "binary-expression-operand-order": false - }, - "linterOptions": { - "exclude": ["src/artifacts.ts", "test/artifacts.ts"] - } -} diff --git a/packages/asset-swapper/typedoc-tsconfig.json b/packages/asset-swapper/typedoc-tsconfig.json deleted file mode 100644 index c9b0af1ae6..0000000000 --- a/packages/asset-swapper/typedoc-tsconfig.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "extends": "../../typedoc-tsconfig", - "compilerOptions": { - "outDir": "lib" - }, - "include": ["./src/**/*", "./test/**/*"] -} diff --git a/packages/asset-swapper/webpack.config.js b/packages/asset-swapper/webpack.config.js deleted file mode 100644 index c9420f3931..0000000000 --- a/packages/asset-swapper/webpack.config.js +++ /dev/null @@ -1,66 +0,0 @@ -/** - * This is to generate the umd bundle only - */ -const _ = require('lodash'); -const TerserPlugin = require('terser-webpack-plugin'); -const path = require('path'); -const production = process.env.NODE_ENV === 'production'; - -let entry = { - index: './src/index.ts', -}; -if (production) { - entry = _.assign({}, entry, { 'index.min': './src/index.ts' }); -} - -module.exports = { - entry, - mode: 'production', - output: { - path: path.resolve(__dirname, '_bundles'), - filename: '[name].js', - libraryTarget: 'umd', - library: 'AssetSwapper', - umdNamedDefine: true, - }, - resolve: { - extensions: ['.ts', '.js', '.json'], - }, - devtool: 'source-map', - optimization: { - minimizer: [ - new TerserPlugin({ - sourceMap: true, - terserOptions: { - mangle: { - reserved: ['BigNumber'], - }, - }, - }), - ], - }, - externals: { - fs: true, - }, - module: { - rules: [ - { - test: /\.ts$/, - use: [ - { - loader: 'awesome-typescript-loader', - // tsconfig.json contains some options required for - // project references which do not work with webback. - // We override those options here. - query: { - declaration: false, - declarationMap: false, - composite: false, - }, - }, - ], - exclude: /node_modules/, - }, - ], - }, -};