Compare commits

..

23 Commits

Author SHA1 Message Date
phil-ociraptor
aa96e53271 Updated config.yml 2022-08-04 13:35:22 -05:00
phil-ociraptor
4090a001f2 Updated config.yml 2022-08-04 13:33:36 -05:00
phil-ociraptor
736cf8026e Updated config.yml 2022-08-04 13:20:38 -05:00
phil-ociraptor
882f2bb829 Add .circleci/config.yml 2022-08-04 13:18:12 -05:00
Phil Liao
337d483a3e add a comment in the README 2022-08-03 12:28:18 -05:00
Phil Liao
51a581136b ignore node_modules contracts too 2022-08-03 09:47:58 -05:00
Phil Liao
c1835e7423 try ignoring some files 2022-08-03 09:36:55 -05:00
Phil Liao
ee54177ca2 Merge branch 'development' into phil/foundry 2022-08-02 13:36:56 -05:00
Phil Liao
5acc01a4e1 reorder dependency installation dir 2022-08-02 13:25:49 -05:00
Phil Liao
30fafaa672 sync foundry.toml w/ feat/protocol-academy 2022-08-02 13:13:24 -05:00
Kyu
d3d4a08f91 Remove references to custom-no-magic-numbers ts lint rule [TKR-484] (#533)
* Remove references to `custom-no-magic-numbers ts` lint rule

* Run prettier
2022-08-01 17:50:40 -07:00
Github Actions
9ce090c8cd Publish
- @0x/asset-swapper@16.65.0
2022-08-01 22:10:47 +00:00
Github Actions
980d60deb8 Updated CHANGELOGS & MD docs 2022-08-01 22:10:42 +00:00
Kyu
d6d79e51e7 Use 0x gas api instead of eth gas station api [TKR-502] (#532)
* Use 0x gas api instead of eth gas station api

* Add integration test for `ProtocolFeeUtils`

* Update CHANGELOG.json
2022-08-01 14:42:45 -07:00
Kyu
3ef5de93bb Remove getBidAskLiquidityForMakerTakerAssetPairAsync (#528) 2022-07-28 09:26:07 -07:00
Kyu
ab7dc33ca4 Refactor PoolsCache (part 2) [TKR-500] (#526)
* Introduce NoOpPoolsCache and use it in unsupported chains for BeethovenX

* Use `NoOpPoolsCache` for `CreamPoolsCache` and `BalancerPoolsCache` on unsupported chains
2022-07-28 09:12:02 -07:00
Kyu
14dcee5bb6 Refactor PoolsCache (part 1) [TKR-500] (#525)
* Make _refreshPoolCacheIfRequiredAsync type-safe and remove Promise.all

* Factor out PoolsCache key logic into a function

* Use Map instead of object in PoolsCache and increase the default timeout

* Clean up PoolsCache and simplify its public interface
2022-07-28 09:04:42 -07:00
Pavel
9856e78609 Update reference.mdx (#531)
Align with `DEFAULT_QUOTE_SLIPPAGE_PERCENTAGE` value from c74e31c219/src/constants.ts (L26)
2022-07-27 12:35:16 -07:00
Michael Zhu
5756d7c563 Pull git submodules in CI 2022-06-01 18:39:07 -07:00
Michael Zhu
2577caaf5a forge install: ../contracts/test/foundry/deps/ds-test 2022-05-19 09:00:05 -07:00
Michael Zhu
603813191f Foundry test skeleton 2022-05-18 15:54:17 -07:00
Michael Zhu
0484519e4d Add typechain to zero-ex 2022-05-18 13:39:22 -07:00
Michael Zhu
275542b6b2 Add foundry.toml to zero-ex directory 2022-05-18 13:36:29 -07:00
40 changed files with 1554 additions and 284 deletions

View File

@@ -10,6 +10,7 @@ jobs:
working_directory: ~/repo
steps:
- checkout
- run: git submodule update --init --recursive
- run: echo 'export PATH=$HOME/CIRCLE_PROJECT_REPONAME/node_modules/.bin:$PATH' >> $BASH_ENV
- run:
name: install-yarn
@@ -27,6 +28,17 @@ jobs:
path: ~/repo/packages/abi-gen/test-cli/output
- store_artifacts:
path: ~/repo/packages/contract-wrappers/generated_docs
test-foundry:
resource_class: medium+
docker:
- image: ghcr.io/foundry-rs/foundry:latest
working_directory: ~/repo
steps:
- restore_cache:
keys:
- repo-{{ .Environment.CIRCLE_SHA1 }}
- run: forge install
- run: forge test
test-exchange-ganache:
resource_class: medium+
docker:
@@ -181,6 +193,9 @@ workflows:
# - test-contracts-extra-ganache:
# requires:
# - build
- test-foundry:
requires:
- build
- test-contracts-rest-ganache:
requires:
- build

9
.gitignore vendored
View File

@@ -173,6 +173,15 @@ contracts/zero-ex/test/generated-wrappers/
contracts/treasury/generated-wrappers/
contracts/treasury/test/generated-wrappers/
# foundry artifacts
contracts/zero-ex/foundry-artifacts/
# foundry cache
contracts/zero-ex/foundry-cache/
# typechain wrappers
contracts/zero-ex/typechain-wrappers/
# Doc README copy
packages/*/docs/README.md

3
.gitmodules vendored Normal file
View File

@@ -0,0 +1,3 @@
[submodule "contracts/zero-ex/contracts/deps/forge-std"]
path = contracts/zero-ex/contracts/deps/forge-std
url = https://github.com/foundry-rs/forge-std

View File

@@ -2,6 +2,10 @@
This package contains contracts for the ZeroEx extensible contract architecture.
> **_NOTE:_** This repo is undergoing a tooling change. If adding a contract, you will need to
> add it to `compiler.json`. You can generate the entire list by running the following:
> `find . -type f -name "*.sol" | grep -v foundry | grep -v "contracts/dep" | grep -v "node_modules"`
## Installation
**Install**

View File

@@ -1,6 +1,212 @@
{
"artifactsDir": "./test/generated-artifacts",
"contractsDir": "./contracts",
"contracts": [
"./contracts/test/TestFixinProtocolFees.sol",
"./contracts/test/TestTransformerHost.sol",
"./contracts/test/TestFillQuoteTransformerHost.sol",
"./contracts/test/TestTransformerDeployerTransformer.sol",
"./contracts/test/TestUniswapV3Feature.sol",
"./contracts/test/TestBridge.sol",
"./contracts/test/TestCallTarget.sol",
"./contracts/test/TestNoEthRecipient.sol",
"./contracts/test/TestFullMigration.sol",
"./contracts/test/TestLibSignature.sol",
"./contracts/test/integration/TestUniswapV3Pool.sol",
"./contracts/test/integration/TestMooniswap.sol",
"./contracts/test/integration/TestUniswapV3Factory.sol",
"./contracts/test/integration/TestCurve.sol",
"./contracts/test/integration/TestUniswapV2Pool.sol",
"./contracts/test/integration/TestUniswapV2Factory.sol",
"./contracts/test/integration/TestLiquidityProvider.sol",
"./contracts/test/TestMetaTransactionsNativeOrdersFeature.sol",
"./contracts/test/TestFeeCollectorController.sol",
"./contracts/test/TestNFTOrderPresigner.sol",
"./contracts/test/TestMetaTransactionsTransformERC20Feature.sol",
"./contracts/test/TestTransformERC20.sol",
"./contracts/test/TestFeeRecipient.sol",
"./contracts/test/TestPermissionlessTransformerDeployerTransformer.sol",
"./contracts/test/TestMigrator.sol",
"./contracts/test/TestFixinTokenSpender.sol",
"./contracts/test/TestDelegateCaller.sol",
"./contracts/test/TestTransformerBase.sol",
"./contracts/test/TestZeroExFeature.sol",
"./contracts/test/TestLibNativeOrder.sol",
"./contracts/test/TestWethTransformerHost.sol",
"./contracts/test/TestPropertyValidator.sol",
"./contracts/test/TestMintTokenERC20Transformer.sol",
"./contracts/test/TestFillQuoteTransformerBridge.sol",
"./contracts/test/TestOrderSignerRegistryWithContractWallet.sol",
"./contracts/test/TestNativeOrdersFeature.sol",
"./contracts/test/TestRfqOriginRegistration.sol",
"./contracts/test/TestStaking.sol",
"./contracts/test/TestFillQuoteTransformerExchange.sol",
"./contracts/test/TestInitialMigration.sol",
"./contracts/test/tokens/TestMintableERC721Token.sol",
"./contracts/test/tokens/TestTokenSpenderERC20Token.sol",
"./contracts/test/tokens/TestWeth.sol",
"./contracts/test/tokens/TestMintableERC1155Token.sol",
"./contracts/test/tokens/TestMintableERC20Token.sol",
"./contracts/test/TestSimpleFunctionRegistryFeatureImpl1.sol",
"./contracts/test/ITestSimpleFunctionRegistryFeature.sol",
"./contracts/test/TestPermissionlessTransformerDeployerSuicidal.sol",
"./contracts/test/TestSimpleFunctionRegistryFeatureImpl2.sol",
"./contracts/src/migrations/InitialMigration.sol",
"./contracts/src/migrations/LibMigrate.sol",
"./contracts/src/migrations/LibBootstrap.sol",
"./contracts/src/migrations/FullMigration.sol",
"./contracts/src/ZeroExOptimized.sol",
"./contracts/src/fixins/FixinERC721Spender.sol",
"./contracts/src/fixins/FixinTokenSpender.sol",
"./contracts/src/fixins/FixinERC1155Spender.sol",
"./contracts/src/fixins/FixinReentrancyGuard.sol",
"./contracts/src/fixins/FixinProtocolFees.sol",
"./contracts/src/fixins/FixinEIP712.sol",
"./contracts/src/fixins/FixinCommon.sol",
"./contracts/src/features/BatchFillNativeOrdersFeature.sol",
"./contracts/src/features/LiquidityProviderFeature.sol",
"./contracts/src/features/ERC165Feature.sol",
"./contracts/src/features/MetaTransactionsFeature.sol",
"./contracts/src/features/nft_orders/NFTOrders.sol",
"./contracts/src/features/nft_orders/ERC1155OrdersFeature.sol",
"./contracts/src/features/nft_orders/ERC721OrdersFeature.sol",
"./contracts/src/features/TransformERC20Feature.sol",
"./contracts/src/features/OwnableFeature.sol",
"./contracts/src/features/libs/LibSignature.sol",
"./contracts/src/features/libs/LibNativeOrder.sol",
"./contracts/src/features/libs/LibNFTOrder.sol",
"./contracts/src/features/native_orders/NativeOrdersSettlement.sol",
"./contracts/src/features/native_orders/NativeOrdersCancellation.sol",
"./contracts/src/features/native_orders/NativeOrdersProtocolFees.sol",
"./contracts/src/features/native_orders/NativeOrdersInfo.sol",
"./contracts/src/features/UniswapV3Feature.sol",
"./contracts/src/features/NativeOrdersFeature.sol",
"./contracts/src/features/UniswapFeature.sol",
"./contracts/src/features/BootstrapFeature.sol",
"./contracts/src/features/multiplex/MultiplexTransformERC20.sol",
"./contracts/src/features/multiplex/MultiplexRfq.sol",
"./contracts/src/features/multiplex/MultiplexFeature.sol",
"./contracts/src/features/multiplex/MultiplexLiquidityProvider.sol",
"./contracts/src/features/multiplex/MultiplexOtc.sol",
"./contracts/src/features/multiplex/MultiplexUniswapV3.sol",
"./contracts/src/features/multiplex/MultiplexUniswapV2.sol",
"./contracts/src/features/OtcOrdersFeature.sol",
"./contracts/src/features/FundRecoveryFeature.sol",
"./contracts/src/features/SimpleFunctionRegistryFeature.sol",
"./contracts/src/features/PancakeSwapFeature.sol",
"./contracts/src/features/interfaces/IMultiplexFeature.sol",
"./contracts/src/features/interfaces/IPancakeSwapFeature.sol",
"./contracts/src/features/interfaces/IUniswapV3Feature.sol",
"./contracts/src/features/interfaces/INativeOrdersFeature.sol",
"./contracts/src/features/interfaces/IOwnableFeature.sol",
"./contracts/src/features/interfaces/IERC165Feature.sol",
"./contracts/src/features/interfaces/IFundRecoveryFeature.sol",
"./contracts/src/features/interfaces/IERC721OrdersFeature.sol",
"./contracts/src/features/interfaces/ILiquidityProviderFeature.sol",
"./contracts/src/features/interfaces/IFeature.sol",
"./contracts/src/features/interfaces/IMetaTransactionsFeature.sol",
"./contracts/src/features/interfaces/IBootstrapFeature.sol",
"./contracts/src/features/interfaces/IOtcOrdersFeature.sol",
"./contracts/src/features/interfaces/IERC1155OrdersFeature.sol",
"./contracts/src/features/interfaces/IUniswapFeature.sol",
"./contracts/src/features/interfaces/ITokenSpenderFeature.sol",
"./contracts/src/features/interfaces/ISimpleFunctionRegistryFeature.sol",
"./contracts/src/features/interfaces/ITransformERC20Feature.sol",
"./contracts/src/features/interfaces/INativeOrdersEvents.sol",
"./contracts/src/features/interfaces/IBatchFillNativeOrdersFeature.sol",
"./contracts/src/IZeroEx.sol",
"./contracts/src/ZeroEx.sol",
"./contracts/src/storage/LibNativeOrdersStorage.sol",
"./contracts/src/storage/LibTransformERC20Storage.sol",
"./contracts/src/storage/LibOtcOrdersStorage.sol",
"./contracts/src/storage/LibSimpleFunctionRegistryStorage.sol",
"./contracts/src/storage/LibStorage.sol",
"./contracts/src/storage/LibERC721OrdersStorage.sol",
"./contracts/src/storage/LibReentrancyGuardStorage.sol",
"./contracts/src/storage/LibMetaTransactionsStorage.sol",
"./contracts/src/storage/LibProxyStorage.sol",
"./contracts/src/storage/LibOwnableStorage.sol",
"./contracts/src/storage/LibERC1155OrdersStorage.sol",
"./contracts/src/transformers/FillQuoteTransformer.sol",
"./contracts/src/transformers/AffiliateFeeTransformer.sol",
"./contracts/src/transformers/PositiveSlippageFeeTransformer.sol",
"./contracts/src/transformers/Transformer.sol",
"./contracts/src/transformers/LibERC20Transformer.sol",
"./contracts/src/transformers/bridges/mixins/MixinShell.sol",
"./contracts/src/transformers/bridges/mixins/MixinMakerPSM.sol",
"./contracts/src/transformers/bridges/mixins/MixinDodoV2.sol",
"./contracts/src/transformers/bridges/mixins/MixinMStable.sol",
"./contracts/src/transformers/bridges/mixins/MixinBalancerV2.sol",
"./contracts/src/transformers/bridges/mixins/MixinCurveV2.sol",
"./contracts/src/transformers/bridges/mixins/MixinPlatypus.sol",
"./contracts/src/transformers/bridges/mixins/MixinNerve.sol",
"./contracts/src/transformers/bridges/mixins/MixinBancor.sol",
"./contracts/src/transformers/bridges/mixins/MixinUniswap.sol",
"./contracts/src/transformers/bridges/mixins/MixinBalancer.sol",
"./contracts/src/transformers/bridges/mixins/MixinCompound.sol",
"./contracts/src/transformers/bridges/mixins/MixinBalancerV2Batch.sol",
"./contracts/src/transformers/bridges/mixins/MixinLido.sol",
"./contracts/src/transformers/bridges/mixins/MixinUniswapV2.sol",
"./contracts/src/transformers/bridges/mixins/MixinZeroExBridge.sol",
"./contracts/src/transformers/bridges/mixins/MixinUniswapV3.sol",
"./contracts/src/transformers/bridges/mixins/MixinBancorV3.sol",
"./contracts/src/transformers/bridges/mixins/MixinKyberDmm.sol",
"./contracts/src/transformers/bridges/mixins/MixinGMX.sol",
"./contracts/src/transformers/bridges/mixins/MixinCurve.sol",
"./contracts/src/transformers/bridges/mixins/MixinMooniswap.sol",
"./contracts/src/transformers/bridges/mixins/MixinDodo.sol",
"./contracts/src/transformers/bridges/mixins/MixinCryptoCom.sol",
"./contracts/src/transformers/bridges/mixins/MixinSynthetix.sol",
"./contracts/src/transformers/bridges/mixins/MixinAaveV2.sol",
"./contracts/src/transformers/bridges/mixins/MixinVelodrome.sol",
"./contracts/src/transformers/bridges/CeloBridgeAdapter.sol",
"./contracts/src/transformers/bridges/FantomBridgeAdapter.sol",
"./contracts/src/transformers/bridges/OptimismBridgeAdapter.sol",
"./contracts/src/transformers/bridges/AbstractBridgeAdapter.sol",
"./contracts/src/transformers/bridges/EthereumBridgeAdapter.sol",
"./contracts/src/transformers/bridges/PolygonBridgeAdapter.sol",
"./contracts/src/transformers/bridges/AvalancheBridgeAdapter.sol",
"./contracts/src/transformers/bridges/BridgeProtocols.sol",
"./contracts/src/transformers/bridges/IBridgeAdapter.sol",
"./contracts/src/transformers/bridges/BSCBridgeAdapter.sol",
"./contracts/src/transformers/LogMetadataTransformer.sol",
"./contracts/src/transformers/IERC20Transformer.sol",
"./contracts/src/transformers/PayTakerTransformer.sol",
"./contracts/src/transformers/WethTransformer.sol",
"./contracts/src/external/PermissionlessTransformerDeployer.sol",
"./contracts/src/external/FlashWallet.sol",
"./contracts/src/external/FeeCollector.sol",
"./contracts/src/external/IFlashWallet.sol",
"./contracts/src/external/LiquidityProviderSandbox.sol",
"./contracts/src/external/TransformerDeployer.sol",
"./contracts/src/external/LibFeeCollector.sol",
"./contracts/src/external/FeeCollectorController.sol",
"./contracts/src/external/ILiquidityProviderSandbox.sol",
"./contracts/src/errors/LibLiquidityProviderRichErrors.sol",
"./contracts/src/errors/LibSignatureRichErrors.sol",
"./contracts/src/errors/LibWalletRichErrors.sol",
"./contracts/src/errors/LibTransformERC20RichErrors.sol",
"./contracts/src/errors/LibNativeOrdersRichErrors.sol",
"./contracts/src/errors/LibNFTOrdersRichErrors.sol",
"./contracts/src/errors/LibMetaTransactionsRichErrors.sol",
"./contracts/src/errors/LibCommonRichErrors.sol",
"./contracts/src/errors/LibOwnableRichErrors.sol",
"./contracts/src/errors/LibProxyRichErrors.sol",
"./contracts/src/errors/LibSimpleFunctionRegistryRichErrors.sol",
"./contracts/src/liquidity-providers/MooniswapLiquidityProvider.sol",
"./contracts/src/liquidity-providers/CurveLiquidityProvider.sol",
"./contracts/src/vendor/IMooniswapPool.sol",
"./contracts/src/vendor/IFeeRecipient.sol",
"./contracts/src/vendor/IERC721Token.sol",
"./contracts/src/vendor/IERC1155Token.sol",
"./contracts/src/vendor/IPropertyValidator.sol",
"./contracts/src/vendor/ITakerCallback.sol",
"./contracts/src/vendor/IUniswapV3Pool.sol",
"./contracts/src/vendor/v3/IStaking.sol",
"./contracts/src/vendor/v3/IERC20Bridge.sol",
"./contracts/src/vendor/IUniswapV2Pair.sol",
"./contracts/src/vendor/ILiquidityProvider.sol"
],
"useDockerisedSolc": false,
"isOfflineMode": false,
"shouldSaveStandardInput": true,

View File

@@ -0,0 +1,30 @@
// 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;
import "forge-std/Test.sol";
contract ContractTest is Test {
function setUp() public {}
function testExample() public {
assertTrue(true);
}
}

View File

@@ -0,0 +1,8 @@
[default]
src = 'contracts/src'
out = 'foundry-artifacts'
test = 'contracts/test/foundry'
libs = ["contracts/deps/", "../utils/contracts/src/"]
remappings = ['@0x/contracts-utils/=../utils/', '@0x/contracts-erc20/=../erc20/', 'src/=./contracts/src']
cache_path = 'foundry-cache'
optimizer_runs = 1000000

View File

@@ -38,7 +38,8 @@
"docs:md": "ts-doc-gen --sourceDir='$PROJECT_FILES' --output=$MD_FILE_DIR --fileExtension=mdx --tsconfig=./typedoc-tsconfig.json",
"docs:json": "typedoc --excludePrivate --excludeExternals --excludeProtected --ignoreCompilerErrors --target ES5 --tsconfig typedoc-tsconfig.json --json $JSON_FILE_PATH $PROJECT_FILES",
"publish:private": "yarn build && gitpkg publish",
"rollback": "node ./lib/scripts/rollback.js"
"rollback": "node ./lib/scripts/rollback.js",
"typechain": "typechain --target=ethers-v5 --out-dir='typechain-wrappers' './foundry-artifacts/**/*.json'"
},
"config": {
"publicInterfaceContracts": "IZeroEx,ZeroEx,FullMigration,InitialMigration,IFlashWallet,IERC20Transformer,IOwnableFeature,ISimpleFunctionRegistryFeature,ITransformERC20Feature,FillQuoteTransformer,PayTakerTransformer,PositiveSlippageFeeTransformer,WethTransformer,OwnableFeature,SimpleFunctionRegistryFeature,TransformERC20Feature,AffiliateFeeTransformer,MetaTransactionsFeature,LogMetadataTransformer,LiquidityProviderFeature,ILiquidityProviderFeature,NativeOrdersFeature,INativeOrdersFeature,FeeCollectorController,FeeCollector,CurveLiquidityProvider,BatchFillNativeOrdersFeature,IBatchFillNativeOrdersFeature,MultiplexFeature,IMultiplexFeature,OtcOrdersFeature,IOtcOrdersFeature,AvalancheBridgeAdapter,BSCBridgeAdapter,CeloBridgeAdapter,EthereumBridgeAdapter,FantomBridgeAdapter,OptimismBridgeAdapter,PolygonBridgeAdapter",
@@ -65,6 +66,7 @@
"@0x/sol-compiler": "^4.8.1",
"@0x/ts-doc-gen": "^0.0.28",
"@0x/tslint-config": "^4.1.4",
"@typechain/ethers-v5": "^10.0.0",
"@types/isomorphic-fetch": "^0.0.35",
"@types/lodash": "4.14.104",
"@types/mocha": "^5.2.7",
@@ -78,6 +80,7 @@
"solhint": "^1.4.1",
"truffle": "^5.0.32",
"tslint": "5.11.0",
"typechain": "^8.0.0",
"typedoc": "~0.16.11",
"typescript": "4.6.3"
},

View File

@@ -1,4 +1,14 @@
[
{
"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": [

View File

@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## 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)

View File

@@ -1897,7 +1897,7 @@ ___
# Interface: SwapQuoteRequestOpts
slippagePercentage: The percentage buffer to add to account for slippage. Affects max ETH price estimates. Defaults to 0.2 (20%).
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

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/asset-swapper",
"version": "16.64.0",
"version": "16.65.0",
"engines": {
"node": ">=6.12"
},
@@ -109,6 +109,7 @@
"dirty-chai": "^2.0.1",
"gitpkg": "https://github.com/0xProject/gitpkg.git",
"mocha": "^6.2.0",
"msw": "^0.44.2",
"npm-run-all": "^4.1.2",
"nyc": "^11.0.1",
"shx": "^0.2.2",

View File

@@ -19,7 +19,7 @@ import {
DEFAULT_TOKEN_ADJACENCY_GRAPH_BY_CHAIN_ID,
} from './utils/market_operation_utils/constants';
const ETH_GAS_STATION_API_URL = 'https://ethgasstation.info/api/ethgasAPI.json';
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';
@@ -48,7 +48,7 @@ const DEFAULT_SWAP_QUOTER_OPTS: SwapQuoterOpts = {
orderRefreshIntervalMs: 10000, // 10 seconds
...DEFAULT_ORDER_PRUNER_OPTS,
samplerGasLimit: 500e6,
ethGasStationUrl: ETH_GAS_STATION_API_URL,
zeroExGasApiUrl: ZERO_EX_GAS_API_URL,
rfqt: {
integratorsWhitelist: [],
makerAssetOfferings: {},
@@ -95,11 +95,10 @@ export { DEFAULT_FEE_SCHEDULE, DEFAULT_GAS_SCHEDULE } from './utils/market_opera
export const POSITIVE_SLIPPAGE_FEE_TRANSFORMER_GAS = new BigNumber(30000);
// tslint:disable-next-line: custom-no-magic-numbers
export const KEEP_ALIVE_TTL = 5 * 60 * ONE_SECOND_MS;
export const constants = {
ETH_GAS_STATION_API_URL,
ZERO_EX_GAS_API_URL,
PROTOCOL_FEE_MULTIPLIER,
POSITIVE_SLIPPAGE_FEE_TRANSFORMER_GAS,
NULL_BYTES,

View File

@@ -157,8 +157,6 @@ export {
GetMarketOrdersRfqOpts,
LiquidityProviderFillData,
LiquidityProviderRegistry,
MarketDepth,
MarketDepthSide,
MooniswapFillData,
MultiHopFillData,
NativeRfqOrderFillData,

View File

@@ -61,7 +61,6 @@ import {
requiresTransformERC20,
} from './quote_consumer_utils';
// tslint:disable-next-line:custom-no-magic-numbers
const MAX_UINT256 = new BigNumber(2).pow(256).minus(1);
const { NULL_ADDRESS, NULL_BYTES, ZERO_AMOUNT } = constants;

View File

@@ -36,9 +36,6 @@ import {
FillData,
GasSchedule,
GetMarketOrdersOpts,
MarketDepth,
MarketDepthSide,
MarketSideLiquidity,
OptimizedMarketOrder,
OptimizerResultWithReport,
} from './utils/market_operation_utils/types';
@@ -112,7 +109,7 @@ export class SwapQuoter {
};
this._protocolFeeUtils = ProtocolFeeUtils.getInstance(
constants.PROTOCOL_FEE_UTILS_POLLING_INTERVAL_IN_MS,
options.ethGasStationUrl,
options.zeroExGasApiUrl,
);
// Allow the sampler bytecode to be overwritten using geths override functionality
const samplerBytecode = _.get(artifacts.ERC20BridgeSampler, 'compilerOutput.evm.deployedBytecode.object');
@@ -228,67 +225,6 @@ export class SwapQuoter {
return batchSwapQuotes.filter(x => x !== undefined) as MarketBuySwapQuote[];
}
/**
* Returns the bids and asks liquidity for the entire market.
* For certain sources (like AMM's) it is recommended to provide a practical maximum takerAssetAmount.
* @param makerTokenAddress The address of the maker asset
* @param takerTokenAddress The address of the taker asset
* @param takerAssetAmount The amount to sell and buy for the bids and asks.
*
* @return An object that conforms to MarketDepth that contains all of the samples and liquidity
* information for the source.
*/
public async getBidAskLiquidityForMakerTakerAssetPairAsync(
makerToken: string,
takerToken: string,
takerAssetAmount: BigNumber,
options: Partial<SwapQuoteRequestOpts> = {},
): Promise<MarketDepth> {
assert.isString('makerToken', makerToken);
assert.isString('takerToken', takerToken);
const sourceFilters = new SourceFilters([], options.excludedSources, options.includedSources);
let [sellOrders, buyOrders] = !sourceFilters.isAllowed(ERC20BridgeSource.Native)
? [[], []]
: await Promise.all([
this.orderbook.getOrdersAsync(makerToken, takerToken),
this.orderbook.getOrdersAsync(takerToken, makerToken),
]);
if (!sellOrders || sellOrders.length === 0) {
sellOrders = [createDummyOrder(makerToken, takerToken)];
}
if (!buyOrders || buyOrders.length === 0) {
buyOrders = [createDummyOrder(takerToken, makerToken)];
}
const getMarketDepthSide = (marketSideLiquidity: MarketSideLiquidity): MarketDepthSide => {
const { dexQuotes, nativeOrders } = marketSideLiquidity.quotes;
const { side } = marketSideLiquidity;
return [
...dexQuotes,
nativeOrders.map(o => {
return {
input: side === MarketOperation.Sell ? o.fillableTakerAmount : o.fillableMakerAmount,
output: side === MarketOperation.Sell ? o.fillableMakerAmount : o.fillableTakerAmount,
fillData: o,
source: ERC20BridgeSource.Native,
};
}),
];
};
const [bids, asks] = await Promise.all([
this._marketOperationUtils.getMarketBuyLiquidityAsync(buyOrders, takerAssetAmount, options),
this._marketOperationUtils.getMarketSellLiquidityAsync(sellOrders, takerAssetAmount, options),
]);
return {
bids: getMarketDepthSide(bids),
asks: getMarketDepthSide(asks),
makerTokenDecimals: asks.makerTokenDecimals,
takerTokenDecimals: asks.takerTokenDecimals,
};
}
/**
* Returns the recommended gas price for a fast transaction
*/

View File

@@ -337,7 +337,7 @@ export interface SwapQuoterOpts extends OrderPrunerOpts {
contractAddresses?: AssetSwapperContractAddresses;
samplerGasLimit?: number;
multiBridgeAddress?: string;
ethGasStationUrl?: string;
zeroExGasApiUrl?: string;
rfqt?: SwapQuoterRfqOpts;
samplerOverrides?: SamplerOverrides;
tokenAdjacencyGraph?: TokenAdjacencyGraph;

View File

@@ -104,11 +104,9 @@ function parseIndicativeQuoteResponseFromAltMM(
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:
// tslint:disable-next-line:custom-no-magic-numbers
new BigNumber(Date.now() / 1000)
.integerValue(BigNumber.ROUND_DOWN)
.plus(constants.ALT_MM_IMPUTED_INDICATIVE_EXPIRY_SECONDS),
expiry: new BigNumber(Date.now() / 1000)
.integerValue(BigNumber.ROUND_DOWN)
.plus(constants.ALT_MM_IMPUTED_INDICATIVE_EXPIRY_SECONDS),
};
}
@@ -243,7 +241,6 @@ export async function returnQuoteFromAltMMAsync<ResponseT>(
// empty response will get filtered out in validation
const emptyResponse = {};
// tslint:disable-next-line:custom-no-magic-numbers
if (response.status !== SUCCESS_CODE) {
const rejectedRequestInfo = {
status: response.status,

View File

@@ -40,7 +40,6 @@ interface Cache {
[key: string]: AaveReserve[];
}
// tslint:disable-next-line:custom-no-magic-numbers
const RESERVES_REFRESH_INTERVAL_MS = 30 * constants.ONE_MINUTE_MS;
/**

View File

@@ -19,7 +19,6 @@ interface Cache {
[key: string]: CToken;
}
// tslint:disable-next-line:custom-no-magic-numbers
const CTOKEN_REFRESH_INTERVAL_MS = 30 * constants.ONE_MINUTE_MS;
/**

View File

@@ -37,7 +37,7 @@ import {
UniswapV3FillData,
} from './types';
// tslint:disable: custom-no-magic-numbers no-bitwise
// tslint:disable: no-bitwise
export const ERC20_PROXY_ID = '0xf47261b0';
export const WALLET_SIGNATURE = '0x04';
@@ -2176,7 +2176,6 @@ export const LIDO_INFO_BY_CHAIN = valueByChainId<LidoInfo>(
},
);
export const BALANCER_SUBGRAPH_URL = 'https://api.thegraph.com/subgraphs/name/balancer-labs/balancer';
export const BALANCER_TOP_POOLS_FETCHED = 250;
export const BALANCER_MAX_POOLS_FETCHED = 3;
@@ -2473,7 +2472,6 @@ const uniswapV2CloneGasSchedule = (fillData?: FillData) => {
* I.e remove the overhead cost of ExchangeProxy (130k) and
* the ethereum transaction cost (21k)
*/
// tslint:disable:custom-no-magic-numbers
export const DEFAULT_GAS_SCHEDULE: Required<GasSchedule> = {
[ERC20BridgeSource.Native]: fillData => {
// TODO jacob re-order imports so there is no circular rependency with SignedNativeOrder
@@ -2698,10 +2696,7 @@ export const POSITIVE_SLIPPAGE_FEE_TRANSFORMER_GAS = new BigNumber(20000);
export const DEFAULT_FEE_ESTIMATE = { gas: 0, fee: ZERO_AMOUNT };
// tslint:enable:custom-no-magic-numbers
export const DEFAULT_GET_MARKET_ORDERS_OPTS: Omit<GetMarketOrdersOpts, 'gasPrice'> = {
// tslint:disable-next-line: custom-no-magic-numbers
runLimit: 2 ** 15,
excludedSources: [],
excludedFeeSources: [],

View File

@@ -862,14 +862,9 @@ export class MarketOperationUtils {
}
private async _refreshPoolCacheIfRequiredAsync(takerToken: string, makerToken: string): Promise<void> {
void Promise.all(
Object.values(this._sampler.poolsCaches).map(async cache => {
if (!cache || cache.isFresh(takerToken, makerToken)) {
return Promise.resolve([]);
}
return cache.getFreshPoolsForPairAsync(takerToken, makerToken);
}),
);
_.values(this._sampler.poolsCaches)
.filter(cache => cache !== undefined && !cache.isFresh(takerToken, makerToken))
.forEach(cache => cache?.getFreshPoolsForPairAsync(takerToken, makerToken));
}
}

View File

@@ -14,7 +14,7 @@ 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 custom-no-magic-numbers completed-docs no-bitwise
// 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;

View File

@@ -1,16 +1,19 @@
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_SUBGRAPH_URL, BALANCER_TOP_POOLS_FETCHED } from '../constants';
import { BALANCER_MAX_POOLS_FETCHED, BALANCER_TOP_POOLS_FETCHED } from '../constants';
import { CacheValue, PoolsCache } from './pools_cache';
import { NoOpPoolsCache } from './no_op_pools_cache';
import { AbstractPoolsCache, CacheValue, PoolsCache } from './pools_cache';
// tslint:disable:custom-no-magic-numbers
const ONE_DAY_MS = 24 * 60 * 60 * 1000;
// tslint:enable:custom-no-magic-numbers
// tslint:disable: member-ordering
const BALANCER_SUBGRAPH_URL = 'https://api.thegraph.com/subgraphs/name/balancer-labs/balancer';
interface BalancerPoolResponse {
id: string;
@@ -20,10 +23,18 @@ interface BalancerPoolResponse {
totalWeight: string;
}
export class BalancerPoolsCache extends PoolsCache {
constructor(
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: { [key: string]: CacheValue } = {},
cache: Map<string, CacheValue> = 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,

View File

@@ -9,13 +9,15 @@ import { LogFunction } from '../../../types';
import { BALANCER_MAX_POOLS_FETCHED, BALANCER_TOP_POOLS_FETCHED } from '../constants';
import { parsePoolData } from './balancer_sor_v2';
import { CacheValue, PoolsCache } from './pools_cache';
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, string>([
[ChainId.Fantom, 'https://api.thegraph.com/subgraphs/name/beethovenxfi/beethovenx'],
]);
// tslint:disable-next-line:custom-no-magic-numbers
const ONE_DAY_MS = 24 * 60 * 60 * 1000;
interface BalancerPoolResponse {
@@ -28,11 +30,11 @@ interface BalancerPoolResponse {
amp: string | null;
}
export class BalancerV2PoolsCache extends PoolsCache {
public static createBeethovenXPoolCache(chainId: ChainId): BalancerV2PoolsCache | undefined {
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 undefined;
return new NoOpPoolsCache();
}
return new BalancerV2PoolsCache(subgraphUrl);
@@ -58,12 +60,12 @@ export class BalancerV2PoolsCache extends PoolsCache {
};
}
constructor(
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: { [key: string]: CacheValue } = {},
cache: Map<string, CacheValue> = new Map(),
) {
super(cache);
void this._loadTopPoolsAsync();

View File

@@ -20,7 +20,6 @@ import { BalancerSwapInfo, BalancerSwaps } from '../types';
import { CacheValue, EMPTY_BALANCER_SWAPS, SwapInfoCache } from './pair_swaps_cache';
import { SubgraphPoolDataService } from './sgPoolDataService';
// tslint:disable-next-line:custom-no-magic-numbers
const ONE_DAY_MS = 24 * 60 * 60 * ONE_SECOND_MS;
export interface BalancerPoolResponse {

View File

@@ -1,18 +1,20 @@
import { ChainId } from '@0x/contract-addresses';
import { Pool } from 'balancer-labs-sor-v1/dist/types';
import { getPoolsWithTokens, parsePoolData } from 'cream-sor';
import { BALANCER_MAX_POOLS_FETCHED } from '../constants';
import { CacheValue, PoolsCache } from './pools_cache';
import { NoOpPoolsCache } from './no_op_pools_cache';
import { AbstractPoolsCache, CacheValue, PoolsCache } from './pools_cache';
export class CreamPoolsCache extends PoolsCache {
constructor(
_cache: { [key: string]: CacheValue } = {},
private readonly maxPoolsFetched: number = BALANCER_MAX_POOLS_FETCHED,
) {
super(_cache);
export class CreamPoolsCache extends AbstractPoolsCache {
public static create(chainId: ChainId): PoolsCache {
if (chainId !== ChainId.Mainnet) {
return new NoOpPoolsCache();
}
return new CreamPoolsCache();
}
protected async _fetchPoolsForPairAsync(takerToken: string, makerToken: string): Promise<Pool[]> {
try {
const poolData = (await getPoolsWithTokens(takerToken, makerToken)).pools;
@@ -25,4 +27,10 @@ export class CreamPoolsCache extends PoolsCache {
return [];
}
}
private constructor(
_cache: Map<string, CacheValue> = new Map(),
private readonly maxPoolsFetched: number = BALANCER_MAX_POOLS_FETCHED,
) {
super(_cache);
}
}

View File

@@ -1,4 +1,4 @@
export { BalancerPoolsCache } from './balancer_utils';
export { BalancerV2PoolsCache } from './balancer_v2_utils';
export { CreamPoolsCache } from './cream_utils';
export { PoolsCache } from './pools_cache';
export { AbstractPoolsCache, PoolsCache } from './pools_cache';

View File

@@ -0,0 +1,21 @@
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<Pool[]> {
return [];
}
public getPoolAddressesForPair(_takerToken: string, _makerToken: string): string[] {
return [];
}
public isFresh(_takerToken: string, _makerToken: string): boolean {
return true;
}
}

View File

@@ -7,12 +7,10 @@ export interface CacheValue {
balancerSwaps: BalancerSwaps;
}
// tslint:disable:custom-no-magic-numbers
// 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: [] };
// tslint:enable:custom-no-magic-numbers
/**
* Caches SwapInfo for a pair of tokens.

View File

@@ -7,18 +7,30 @@ export interface CacheValue {
pools: Pool[];
}
// tslint:disable:custom-no-magic-numbers
// Cache results for 30mins
const DEFAULT_CACHE_TIME_MS = (ONE_HOUR_IN_SECONDS / 2) * ONE_SECOND_MS;
const DEFAULT_TIMEOUT_MS = 1000;
// tslint:enable:custom-no-magic-numbers
const DEFAULT_TIMEOUT_MS = 3000;
export abstract class PoolsCache {
protected static _isExpired(value: CacheValue): boolean {
export interface PoolsCache {
getFreshPoolsForPairAsync(takerToken: string, makerToken: string, timeoutMs?: number): Promise<Pool[]>;
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: { [key: string]: CacheValue },
protected readonly _cache: Map<string, CacheValue>,
protected readonly _cacheTimeMs: number = DEFAULT_CACHE_TIME_MS,
) {}
@@ -31,47 +43,42 @@ export abstract class PoolsCache {
return Promise.race([this._getAndSaveFreshPoolsForPairAsync(takerToken, makerToken), timeout]);
}
public getCachedPoolAddressesForPair(
takerToken: string,
makerToken: string,
ignoreExpired: boolean = true,
): string[] | undefined {
const key = JSON.stringify([takerToken, makerToken]);
const value = this._cache[key];
if (ignoreExpired) {
return value === undefined ? [] : value.pools.map(pool => pool.id);
}
if (!value) {
return undefined;
}
if (PoolsCache._isExpired(value)) {
return undefined;
}
return (value || []).pools.map(pool => pool.id);
/**
* 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 cached = this.getCachedPoolAddressesForPair(takerToken, makerToken, false);
return cached !== undefined;
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<Pool[]> {
const key = JSON.stringify([takerToken, makerToken]);
const value = this._cache[key];
if (value === undefined || value.expiresAt >= Date.now()) {
const pools = await this._fetchPoolsForPairAsync(takerToken, makerToken);
const expiresAt = Date.now() + this._cacheTimeMs;
this._cachePoolsForPair(takerToken, makerToken, pools, expiresAt);
const key = AbstractPoolsCache._getKey(takerToken, makerToken);
const value = this._cache.get(key);
if (!AbstractPoolsCache._isExpired(value)) {
return value!.pools;
}
return this._cache[key].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 = JSON.stringify([takerToken, makerToken]);
this._cache[key] = {
pools,
expiresAt,
};
const key = AbstractPoolsCache._getKey(takerToken, makerToken);
this._cache.set(key, { pools, expiresAt });
}
protected abstract _fetchPoolsForPairAsync(takerToken: string, makerToken: string): Promise<Pool[]>;

View File

@@ -56,7 +56,7 @@ import {
} from './constants';
import { getGeistInfoForPair } from './geist_utils';
import { getLiquidityProvidersForPair } from './liquidity_provider_utils';
import { BalancerPoolsCache, BalancerV2PoolsCache, CreamPoolsCache } from './pools_cache';
import { BalancerPoolsCache, BalancerV2PoolsCache, CreamPoolsCache, PoolsCache } from './pools_cache';
import { BalancerV2SwapInfoCache } from './pools_cache/balancer_v2_utils_new';
import { SamplerContractOperation } from './sampler_contract_operation';
import { SamplerNoOperation } from './sampler_no_operation';
@@ -114,10 +114,10 @@ export const TWO_HOP_SOURCE_FILTERS = SourceFilters.all().exclude([
export const BATCH_SOURCE_FILTERS = SourceFilters.all().exclude([ERC20BridgeSource.MultiHop, ERC20BridgeSource.Native]);
export interface PoolsCacheMap {
[ERC20BridgeSource.Balancer]: BalancerPoolsCache;
[ERC20BridgeSource.Balancer]: PoolsCache;
[ERC20BridgeSource.BalancerV2]: BalancerV2SwapInfoCache | undefined;
[ERC20BridgeSource.Beethovenx]: BalancerV2PoolsCache | undefined;
[ERC20BridgeSource.Cream]: CreamPoolsCache;
[ERC20BridgeSource.Beethovenx]: PoolsCache;
[ERC20BridgeSource.Cream]: PoolsCache;
}
// tslint:disable:no-inferred-empty-object-type no-unbound-method
@@ -156,8 +156,8 @@ export class SamplerOperations {
? poolsCaches
: {
[ERC20BridgeSource.Beethovenx]: BalancerV2PoolsCache.createBeethovenXPoolCache(chainId),
[ERC20BridgeSource.Balancer]: new BalancerPoolsCache(),
[ERC20BridgeSource.Cream]: new CreamPoolsCache(),
[ERC20BridgeSource.Balancer]: BalancerPoolsCache.create(chainId),
[ERC20BridgeSource.Cream]: CreamPoolsCache.create(chainId),
[ERC20BridgeSource.BalancerV2]:
BALANCER_V2_VAULT_ADDRESS_BY_CHAIN[chainId] === NULL_ADDRESS
? undefined
@@ -1592,20 +1592,17 @@ export class SamplerOperations {
),
];
case ERC20BridgeSource.Balancer:
return (
this.poolsCaches[ERC20BridgeSource.Balancer].getCachedPoolAddressesForPair(
takerToken,
makerToken,
) || []
).map(balancerPool =>
this.getBalancerSellQuotes(
balancerPool,
makerToken,
takerToken,
takerFillAmounts,
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) {
@@ -1624,18 +1621,14 @@ export class SamplerOperations {
}
case ERC20BridgeSource.Beethovenx: {
const cache = this.poolsCaches[source];
if (cache === undefined) {
return [];
}
const poolIds = cache.getCachedPoolAddressesForPair(takerToken, makerToken) || [];
const poolAddresses = cache.getPoolAddressesForPair(takerToken, makerToken);
const vault = BEETHOVEN_X_VAULT_ADDRESS_BY_CHAIN[this.chainId];
if (vault === NULL_ADDRESS) {
return [];
}
return poolIds.map(poolId =>
return poolAddresses.map(poolAddress =>
this.getBalancerV2SellQuotes(
{ poolId, vault },
{ poolId: poolAddress, vault },
makerToken,
takerToken,
takerFillAmounts,
@@ -1644,20 +1637,17 @@ export class SamplerOperations {
);
}
case ERC20BridgeSource.Cream:
return (
this.poolsCaches[ERC20BridgeSource.Cream].getCachedPoolAddressesForPair(
takerToken,
makerToken,
) || []
).map(creamPool =>
this.getBalancerSellQuotes(
creamPool,
makerToken,
takerToken,
takerFillAmounts,
ERC20BridgeSource.Cream,
),
);
return this.poolsCaches[ERC20BridgeSource.Cream]
.getPoolAddressesForPair(takerToken, makerToken)
.map(creamPool =>
this.getBalancerSellQuotes(
creamPool,
makerToken,
takerToken,
takerFillAmounts,
ERC20BridgeSource.Cream,
),
);
case ERC20BridgeSource.Dodo:
if (!isValidAddress(DODOV1_CONFIG_BY_CHAIN_ID[this.chainId].registry)) {
return [];
@@ -1948,20 +1938,17 @@ export class SamplerOperations {
),
];
case ERC20BridgeSource.Balancer:
return (
this.poolsCaches[ERC20BridgeSource.Balancer].getCachedPoolAddressesForPair(
takerToken,
makerToken,
) || []
).map(poolAddress =>
this.getBalancerBuyQuotes(
poolAddress,
makerToken,
takerToken,
makerFillAmounts,
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) {
@@ -1986,11 +1973,7 @@ export class SamplerOperations {
}
case ERC20BridgeSource.Beethovenx: {
const cache = this.poolsCaches[source];
if (cache === undefined) {
return [];
}
const poolIds = cache.getCachedPoolAddressesForPair(takerToken, makerToken) || [];
const poolIds = cache.getPoolAddressesForPair(takerToken, makerToken) || [];
const vault = BEETHOVEN_X_VAULT_ADDRESS_BY_CHAIN[this.chainId];
if (vault === NULL_ADDRESS) {
return [];
@@ -2006,20 +1989,17 @@ export class SamplerOperations {
);
}
case ERC20BridgeSource.Cream:
return (
this.poolsCaches[ERC20BridgeSource.Cream].getCachedPoolAddressesForPair(
takerToken,
makerToken,
) || []
).map(poolAddress =>
this.getBalancerBuyQuotes(
poolAddress,
makerToken,
takerToken,
makerFillAmounts,
ERC20BridgeSource.Cream,
),
);
return this.poolsCaches[ERC20BridgeSource.Cream]
.getPoolAddressesForPair(takerToken, makerToken)
.map(poolAddress =>
this.getBalancerBuyQuotes(
poolAddress,
makerToken,
takerToken,
makerFillAmounts,
ERC20BridgeSource.Cream,
),
);
case ERC20BridgeSource.Dodo:
if (!isValidAddress(DODOV1_CONFIG_BY_CHAIN_ID[this.chainId].registry)) {
return [];

View File

@@ -617,15 +617,6 @@ export interface OptimizerResultWithReport extends OptimizerResult {
priceComparisonsReport?: PriceComparisonsReport;
}
export type MarketDepthSide = Array<Array<DexSample<FillData>>>;
export interface MarketDepth {
bids: MarketDepthSide;
asks: MarketDepthSide;
makerTokenDecimals: number;
takerTokenDecimals: number;
}
export interface MarketSideLiquidity {
side: MarketOperation;
inputAmount: BigNumber;

View File

@@ -6,28 +6,36 @@ 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 _ethGasStationUrl!: string;
private readonly _zeroExGasApiUrl: string;
private readonly _gasPriceHeart: any;
private _gasPriceEstimation: BigNumber = constants.ZERO_AMOUNT;
private _errorCount: number = 0;
public static getInstance(
gasPricePollingIntervalInMs: number,
ethGasStationUrl: string = constants.ETH_GAS_STATION_API_URL,
zeroExGasApiUrl: string = constants.ZERO_EX_GAS_API_URL,
initialGasPrice: BigNumber = constants.ZERO_AMOUNT,
): ProtocolFeeUtils {
if (!ProtocolFeeUtils._instance) {
ProtocolFeeUtils._instance = new ProtocolFeeUtils(
gasPricePollingIntervalInMs,
ethGasStationUrl,
zeroExGasApiUrl,
initialGasPrice,
);
}
return ProtocolFeeUtils._instance;
}
/** @returns gas price (in wei) */
public async getGasPriceEstimationOrThrowAsync(shouldHardRefresh?: boolean): Promise<BigNumber> {
if (this._gasPriceEstimation.eq(constants.ZERO_AMOUNT)) {
return this._getGasPriceFromGasStationOrThrowAsync();
@@ -48,27 +56,21 @@ export class ProtocolFeeUtils {
private constructor(
gasPricePollingIntervalInMs: number,
ethGasStationUrl: string = constants.ETH_GAS_STATION_API_URL,
zeroExGasApiUrl: string = constants.ZERO_EX_GAS_API_URL,
initialGasPrice: BigNumber = constants.ZERO_AMOUNT,
) {
this._gasPriceHeart = heartbeats.createHeart(gasPricePollingIntervalInMs);
this._gasPriceEstimation = initialGasPrice;
this._ethGasStationUrl = ethGasStationUrl;
this._zeroExGasApiUrl = zeroExGasApiUrl;
this._initializeHeartBeat();
}
// tslint:disable-next-line: prefer-function-over-method
private async _getGasPriceFromGasStationOrThrowAsync(): Promise<BigNumber> {
try {
const res = await fetch(this._ethGasStationUrl);
const gasInfo = await res.json();
// Eth Gas Station result is gwei * 10
// tslint:disable-next-line:custom-no-magic-numbers
const BASE_TEN = 10;
const gasPriceGwei = new BigNumber(gasInfo.fast / BASE_TEN);
// tslint:disable-next-line:custom-no-magic-numbers
const unit = new BigNumber(BASE_TEN).pow(9);
const gasPriceWei = unit.times(gasPriceGwei);
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;

View File

@@ -24,7 +24,7 @@ import {
SOURCE_FLAGS,
ZERO_AMOUNT,
} from '../src/utils/market_operation_utils/constants';
import { PoolsCache } from '../src/utils/market_operation_utils/pools_cache';
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';
@@ -98,9 +98,9 @@ async function getMarketBuyOrdersAsync(
return utils.getOptimizerResultAsync(nativeOrders, makerAmount, MarketOperation.Buy, opts);
}
class MockPoolsCache extends PoolsCache {
class MockPoolsCache extends AbstractPoolsCache {
constructor(private readonly _handler: (takerToken: string, makerToken: string) => Pool[]) {
super({});
super(new Map());
}
protected async _fetchPoolsForPairAsync(takerToken: string, makerToken: string): Promise<Pool[]> {
return this._handler(takerToken, makerToken);

View File

@@ -28,12 +28,12 @@ describe('Pools Caches for Balancer-based sampling', () => {
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.getCachedPoolAddressesForPair(takerToken, makerToken);
const cachedPoolIds = cache.getPoolAddressesForPair(takerToken, makerToken);
expect(cachedPoolIds).to.deep.equal(pools.map(p => p.id));
}
describe('BalancerPoolsCache', () => {
const cache = new BalancerPoolsCache();
const cache = BalancerPoolsCache.create(ChainId.Mainnet);
it('fetches pools', async () => {
const pairs = [
[usdcAddress, daiAddress],
@@ -58,15 +58,14 @@ describe('Pools Caches for Balancer-based sampling', () => {
[wftmAddress, fantomWethAddress],
];
expect(cache).not.null();
await Promise.all(
pairs.map(async ([takerToken, makerToken]) => fetchAndAssertPoolsAsync(cache!, takerToken, makerToken)),
pairs.map(async ([takerToken, makerToken]) => fetchAndAssertPoolsAsync(cache, takerToken, makerToken)),
);
});
});
describe('CreamPoolsCache', () => {
const cache = new CreamPoolsCache();
const cache = CreamPoolsCache.create(ChainId.Mainnet);
it('fetches pools', async () => {
const pairs = [
[usdcAddress, creamAddress],

View File

@@ -0,0 +1,45 @@
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);
});
});
});

View File

@@ -1,6 +1,7 @@
{
"extends": ["@0x/tslint-config"],
"rules": {
"custom-no-magic-numbers": false,
"max-file-line-count": false,
"binary-expression-operand-order": false
},

1029
yarn.lock

File diff suppressed because it is too large Load Diff