AS: Balancer V2 batchSwap (#462)

* Draft. PoC pseudo code showing general idea for resuing SOR path creation logic and adding multihop support.

* Add actual Balancer SDK function calls.

* Update to handle buys.

* Correct taker>maker for buy.

* Draft. PoC pseudo code showing general idea for resuing SOR path creation logic and adding multihop support.

* make it build

* rebase

* add BalancerV2Batch protocol

* add BalancerV2Batch protocol

* get balancer v2 multihop working

* fix BalancerV2Batch for sells (buys still iffy)

* fix buys, appease linter and prettier

* remove unused RPC URL from balancer sdk construction

* update changelogs

* clean up comments
add event loop yield in `BalancerV2SwapInfoCache.loadTopPools()`

* add negative result check on balancerv2batch swap output

* compiler hack

* reintroduce CompilerHack

* delete unused multibridge sampler

* remove compilerhack

* reintroduce compilerhack

* try to fix CI compile errors

* plz work

* plz work

* pretty plz work

* yay it works, also address feedback

* appease linter

* deploy new FQTs

Co-authored-by: johngrantuk <johngrantuk@googlemail.com>
Co-authored-by: Lawrence Forman <me@merklejerk.com>
This commit is contained in:
Lawrence Forman 2022-04-22 02:43:41 -04:00 committed by GitHub
parent 7c51412e2f
commit 470e9a4697
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
39 changed files with 2000 additions and 1695 deletions

View File

@ -19,7 +19,6 @@ jobs:
command: yarn --frozen-lockfile --ignore-engines install || yarn --frozen-lockfile --ignore-engines install command: yarn --frozen-lockfile --ignore-engines install || yarn --frozen-lockfile --ignore-engines install
- setup_remote_docker - setup_remote_docker
- run: yarn build:ci || yarn build:ci || yarn build:ci || yarn build:ci || yarn build:ci || yarn build:ci - run: yarn build:ci || yarn build:ci || yarn build:ci || yarn build:ci || yarn build:ci || yarn build:ci
- run: yarn build:ts || yarn build:ts || yarn build:ts || yarn build:ts || yarn build:ts || yarn build:ts
- save_cache: - save_cache:
key: repo-{{ .Environment.CIRCLE_SHA1 }} key: repo-{{ .Environment.CIRCLE_SHA1 }}
paths: paths:

View File

@ -1,4 +1,13 @@
[ [
{
"version": "0.32.0",
"changes": [
{
"note": "Add support for `BalancerV2Batch` fills in FQT",
"pr": 462
}
]
},
{ {
"timestamp": 1648739346, "timestamp": 1648739346,
"version": "0.31.2", "version": "0.31.2",

View File

@ -25,6 +25,7 @@ import "./BridgeProtocols.sol";
import "./mixins/MixinAaveV2.sol"; import "./mixins/MixinAaveV2.sol";
import "./mixins/MixinBalancer.sol"; import "./mixins/MixinBalancer.sol";
import "./mixins/MixinBalancerV2.sol"; import "./mixins/MixinBalancerV2.sol";
import "./mixins/MixinBalancerV2Batch.sol";
import "./mixins/MixinBancor.sol"; import "./mixins/MixinBancor.sol";
import "./mixins/MixinCoFiX.sol"; import "./mixins/MixinCoFiX.sol";
import "./mixins/MixinCompound.sol"; import "./mixins/MixinCompound.sol";
@ -52,6 +53,7 @@ contract BridgeAdapter is
MixinAaveV2, MixinAaveV2,
MixinBalancer, MixinBalancer,
MixinBalancerV2, MixinBalancerV2,
MixinBalancerV2Batch,
MixinBancor, MixinBancor,
MixinCoFiX, MixinCoFiX,
MixinCompound, MixinCompound,
@ -159,6 +161,11 @@ contract BridgeAdapter is
sellAmount, sellAmount,
order.bridgeData order.bridgeData
); );
} else if (protocolId == BridgeProtocols.BALANCERV2BATCH) {
boughtAmount = _tradeBalancerV2Batch(
sellAmount,
order.bridgeData
);
} else if (protocolId == BridgeProtocols.KYBER) { } else if (protocolId == BridgeProtocols.KYBER) {
boughtAmount = _tradeKyber( boughtAmount = _tradeKyber(
sellToken, sellToken,

View File

@ -27,29 +27,30 @@ library BridgeProtocols {
// A incrementally increasing, append-only list of protocol IDs. // A incrementally increasing, append-only list of protocol IDs.
// We don't use an enum so solidity doesn't throw when we pass in a // We don't use an enum so solidity doesn't throw when we pass in a
// new protocol ID that hasn't been rolled up yet. // new protocol ID that hasn't been rolled up yet.
uint128 internal constant UNKNOWN = 0; uint128 internal constant UNKNOWN = 0;
uint128 internal constant CURVE = 1; uint128 internal constant CURVE = 1;
uint128 internal constant UNISWAPV2 = 2; uint128 internal constant UNISWAPV2 = 2;
uint128 internal constant UNISWAP = 3; uint128 internal constant UNISWAP = 3;
uint128 internal constant BALANCER = 4; uint128 internal constant BALANCER = 4;
uint128 internal constant KYBER = 5; uint128 internal constant KYBER = 5;
uint128 internal constant MOONISWAP = 6; uint128 internal constant MOONISWAP = 6;
uint128 internal constant MSTABLE = 7; uint128 internal constant MSTABLE = 7;
uint128 internal constant OASIS = 8; uint128 internal constant OASIS = 8;
uint128 internal constant SHELL = 9; uint128 internal constant SHELL = 9;
uint128 internal constant DODO = 10; uint128 internal constant DODO = 10;
uint128 internal constant DODOV2 = 11; uint128 internal constant DODOV2 = 11;
uint128 internal constant CRYPTOCOM = 12; uint128 internal constant CRYPTOCOM = 12;
uint128 internal constant BANCOR = 13; uint128 internal constant BANCOR = 13;
uint128 internal constant COFIX = 14; uint128 internal constant COFIX = 14;
uint128 internal constant NERVE = 15; uint128 internal constant NERVE = 15;
uint128 internal constant MAKERPSM = 16; uint128 internal constant MAKERPSM = 16;
uint128 internal constant BALANCERV2 = 17; uint128 internal constant BALANCERV2 = 17;
uint128 internal constant UNISWAPV3 = 18; uint128 internal constant UNISWAPV3 = 18;
uint128 internal constant KYBERDMM = 19; uint128 internal constant KYBERDMM = 19;
uint128 internal constant CURVEV2 = 20; uint128 internal constant CURVEV2 = 20;
uint128 internal constant LIDO = 21; uint128 internal constant LIDO = 21;
uint128 internal constant CLIPPER = 22; // Not used: Clipper is now using PLP interface uint128 internal constant CLIPPER = 22; // Not used: Clipper is now using PLP interface
uint128 internal constant AAVEV2 = 23; uint128 internal constant AAVEV2 = 23;
uint128 internal constant COMPOUND = 24; uint128 internal constant COMPOUND = 24;
uint128 internal constant BALANCERV2BATCH = 25;
} }

View File

@ -0,0 +1,107 @@
// 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.5;
pragma experimental ABIEncoderV2;
import "@0x/contracts-erc20/contracts/src/v06/LibERC20TokenV06.sol";
import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
interface IBalancerV2BatchSwapVault {
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;
}
function batchSwap(
SwapKind kind,
BatchSwapStep[] calldata swaps,
IERC20TokenV06[] calldata assets,
FundManagement calldata funds,
int256[] calldata limits,
uint256 deadline
) external returns (int256[] memory amounts);
}
contract MixinBalancerV2Batch {
using LibERC20TokenV06 for IERC20TokenV06;
struct BalancerV2BatchBridgeData {
IBalancerV2BatchSwapVault vault;
IBalancerV2BatchSwapVault.BatchSwapStep[] swapSteps;
IERC20TokenV06[] assets;
}
function _tradeBalancerV2Batch(
uint256 sellAmount,
bytes memory bridgeData
)
internal
returns (uint256 boughtAmount)
{
// Decode the bridge data.
(
IBalancerV2BatchSwapVault vault,
IBalancerV2BatchSwapVault.BatchSwapStep[] memory swapSteps,
address[] memory assets_
) = abi.decode(bridgeData, (IBalancerV2BatchSwapVault, IBalancerV2BatchSwapVault.BatchSwapStep[], address[]));
IERC20TokenV06[] memory assets;
assembly { assets := assets_ }
// Grant an allowance to the exchange to spend `fromTokenAddress` token.
assets[0].approveIfBelow(address(vault), sellAmount);
swapSteps[0].amount = sellAmount;
int256[] memory limits = new int256[](assets.length);
for (uint256 i = 0; i < limits.length; ++i) {
limits[i] = type(int256).max;
}
int256[] memory amounts = vault.batchSwap(
IBalancerV2BatchSwapVault.SwapKind.GIVEN_IN,
swapSteps,
assets,
IBalancerV2BatchSwapVault.FundManagement({
sender: address(this),
fromInternalBalance: false,
recipient: payable(address(this)),
toInternalBalance: false
}),
limits,
block.timestamp + 1
);
require(amounts[amounts.length - 1] <= 0, 'Unexpected BalancerV2Batch output');
return uint256(amounts[amounts.length - 1] * -1);
}
}

View File

@ -43,7 +43,7 @@
"config": { "config": {
"publicInterfaceContracts": "IZeroEx,ZeroEx,FullMigration,InitialMigration,IFlashWallet,IERC20Transformer,IOwnableFeature,ISimpleFunctionRegistryFeature,ITransformERC20Feature,FillQuoteTransformer,PayTakerTransformer,PositiveSlippageFeeTransformer,WethTransformer,OwnableFeature,SimpleFunctionRegistryFeature,TransformERC20Feature,AffiliateFeeTransformer,MetaTransactionsFeature,LogMetadataTransformer,BridgeAdapter,LiquidityProviderFeature,ILiquidityProviderFeature,NativeOrdersFeature,INativeOrdersFeature,FeeCollectorController,FeeCollector,CurveLiquidityProvider,BatchFillNativeOrdersFeature,IBatchFillNativeOrdersFeature,MultiplexFeature,IMultiplexFeature,OtcOrdersFeature,IOtcOrdersFeature", "publicInterfaceContracts": "IZeroEx,ZeroEx,FullMigration,InitialMigration,IFlashWallet,IERC20Transformer,IOwnableFeature,ISimpleFunctionRegistryFeature,ITransformERC20Feature,FillQuoteTransformer,PayTakerTransformer,PositiveSlippageFeeTransformer,WethTransformer,OwnableFeature,SimpleFunctionRegistryFeature,TransformERC20Feature,AffiliateFeeTransformer,MetaTransactionsFeature,LogMetadataTransformer,BridgeAdapter,LiquidityProviderFeature,ILiquidityProviderFeature,NativeOrdersFeature,INativeOrdersFeature,FeeCollectorController,FeeCollector,CurveLiquidityProvider,BatchFillNativeOrdersFeature,IBatchFillNativeOrdersFeature,MultiplexFeature,IMultiplexFeature,OtcOrdersFeature,IOtcOrdersFeature",
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually.", "abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually.",
"abis": "./test/generated-artifacts/@(AffiliateFeeTransformer|BatchFillNativeOrdersFeature|BootstrapFeature|BridgeAdapter|BridgeProtocols|CurveLiquidityProvider|ERC1155OrdersFeature|ERC165Feature|ERC721OrdersFeature|FeeCollector|FeeCollectorController|FillQuoteTransformer|FixinCommon|FixinEIP712|FixinERC1155Spender|FixinERC721Spender|FixinProtocolFees|FixinReentrancyGuard|FixinTokenSpender|FlashWallet|FullMigration|FundRecoveryFeature|IBatchFillNativeOrdersFeature|IBootstrapFeature|IBridgeAdapter|IERC1155OrdersFeature|IERC1155Token|IERC165Feature|IERC20Bridge|IERC20Transformer|IERC721OrdersFeature|IERC721Token|IFeature|IFeeRecipient|IFlashWallet|IFundRecoveryFeature|ILiquidityProvider|ILiquidityProviderFeature|ILiquidityProviderSandbox|IMetaTransactionsFeature|IMooniswapPool|IMultiplexFeature|INativeOrdersEvents|INativeOrdersFeature|IOtcOrdersFeature|IOwnableFeature|IPancakeSwapFeature|IPropertyValidator|ISimpleFunctionRegistryFeature|IStaking|ITakerCallback|ITestSimpleFunctionRegistryFeature|ITokenSpenderFeature|ITransformERC20Feature|IUniswapFeature|IUniswapV2Pair|IUniswapV3Feature|IUniswapV3Pool|IZeroEx|InitialMigration|LibBootstrap|LibCommonRichErrors|LibERC1155OrdersStorage|LibERC20Transformer|LibERC721OrdersStorage|LibFeeCollector|LibLiquidityProviderRichErrors|LibMetaTransactionsRichErrors|LibMetaTransactionsStorage|LibMigrate|LibNFTOrder|LibNFTOrdersRichErrors|LibNativeOrder|LibNativeOrdersRichErrors|LibNativeOrdersStorage|LibOtcOrdersStorage|LibOwnableRichErrors|LibOwnableStorage|LibProxyRichErrors|LibProxyStorage|LibReentrancyGuardStorage|LibSignature|LibSignatureRichErrors|LibSimpleFunctionRegistryRichErrors|LibSimpleFunctionRegistryStorage|LibStorage|LibTransformERC20RichErrors|LibTransformERC20Storage|LibWalletRichErrors|LiquidityProviderFeature|LiquidityProviderSandbox|LogMetadataTransformer|MetaTransactionsFeature|MixinAaveV2|MixinBalancer|MixinBalancerV2|MixinBancor|MixinCoFiX|MixinCompound|MixinCryptoCom|MixinCurve|MixinCurveV2|MixinDodo|MixinDodoV2|MixinKyber|MixinKyberDmm|MixinLido|MixinMStable|MixinMakerPSM|MixinMooniswap|MixinNerve|MixinOasis|MixinShell|MixinUniswap|MixinUniswapV2|MixinUniswapV3|MixinZeroExBridge|MooniswapLiquidityProvider|MultiplexFeature|MultiplexLiquidityProvider|MultiplexOtc|MultiplexRfq|MultiplexTransformERC20|MultiplexUniswapV2|MultiplexUniswapV3|NFTOrders|NativeOrdersCancellation|NativeOrdersFeature|NativeOrdersInfo|NativeOrdersProtocolFees|NativeOrdersSettlement|OtcOrdersFeature|OwnableFeature|PancakeSwapFeature|PayTakerTransformer|PermissionlessTransformerDeployer|PositiveSlippageFeeTransformer|SimpleFunctionRegistryFeature|TestBridge|TestCallTarget|TestCurve|TestDelegateCaller|TestFeeCollectorController|TestFeeRecipient|TestFillQuoteTransformerBridge|TestFillQuoteTransformerExchange|TestFillQuoteTransformerHost|TestFixinProtocolFees|TestFixinTokenSpender|TestFullMigration|TestInitialMigration|TestLibNativeOrder|TestLibSignature|TestLiquidityProvider|TestMetaTransactionsNativeOrdersFeature|TestMetaTransactionsTransformERC20Feature|TestMigrator|TestMintTokenERC20Transformer|TestMintableERC1155Token|TestMintableERC20Token|TestMintableERC721Token|TestMooniswap|TestNFTOrderPresigner|TestNativeOrdersFeature|TestNoEthRecipient|TestOrderSignerRegistryWithContractWallet|TestPermissionlessTransformerDeployerSuicidal|TestPermissionlessTransformerDeployerTransformer|TestPropertyValidator|TestRfqOriginRegistration|TestSimpleFunctionRegistryFeatureImpl1|TestSimpleFunctionRegistryFeatureImpl2|TestStaking|TestTokenSpenderERC20Token|TestTransformERC20|TestTransformerBase|TestTransformerDeployerTransformer|TestTransformerHost|TestUniswapV2Factory|TestUniswapV2Pool|TestUniswapV3Factory|TestUniswapV3Feature|TestUniswapV3Pool|TestWeth|TestWethTransformerHost|TestZeroExFeature|TransformERC20Feature|Transformer|TransformerDeployer|UniswapFeature|UniswapV3Feature|WethTransformer|ZeroEx|ZeroExOptimized).json" "abis": "./test/generated-artifacts/@(AffiliateFeeTransformer|BatchFillNativeOrdersFeature|BootstrapFeature|BridgeAdapter|BridgeProtocols|CurveLiquidityProvider|ERC1155OrdersFeature|ERC165Feature|ERC721OrdersFeature|FeeCollector|FeeCollectorController|FillQuoteTransformer|FixinCommon|FixinEIP712|FixinERC1155Spender|FixinERC721Spender|FixinProtocolFees|FixinReentrancyGuard|FixinTokenSpender|FlashWallet|FullMigration|FundRecoveryFeature|IBatchFillNativeOrdersFeature|IBootstrapFeature|IBridgeAdapter|IERC1155OrdersFeature|IERC1155Token|IERC165Feature|IERC20Bridge|IERC20Transformer|IERC721OrdersFeature|IERC721Token|IFeature|IFeeRecipient|IFlashWallet|IFundRecoveryFeature|ILiquidityProvider|ILiquidityProviderFeature|ILiquidityProviderSandbox|IMetaTransactionsFeature|IMooniswapPool|IMultiplexFeature|INativeOrdersEvents|INativeOrdersFeature|IOtcOrdersFeature|IOwnableFeature|IPancakeSwapFeature|IPropertyValidator|ISimpleFunctionRegistryFeature|IStaking|ITakerCallback|ITestSimpleFunctionRegistryFeature|ITokenSpenderFeature|ITransformERC20Feature|IUniswapFeature|IUniswapV2Pair|IUniswapV3Feature|IUniswapV3Pool|IZeroEx|InitialMigration|LibBootstrap|LibCommonRichErrors|LibERC1155OrdersStorage|LibERC20Transformer|LibERC721OrdersStorage|LibFeeCollector|LibLiquidityProviderRichErrors|LibMetaTransactionsRichErrors|LibMetaTransactionsStorage|LibMigrate|LibNFTOrder|LibNFTOrdersRichErrors|LibNativeOrder|LibNativeOrdersRichErrors|LibNativeOrdersStorage|LibOtcOrdersStorage|LibOwnableRichErrors|LibOwnableStorage|LibProxyRichErrors|LibProxyStorage|LibReentrancyGuardStorage|LibSignature|LibSignatureRichErrors|LibSimpleFunctionRegistryRichErrors|LibSimpleFunctionRegistryStorage|LibStorage|LibTransformERC20RichErrors|LibTransformERC20Storage|LibWalletRichErrors|LiquidityProviderFeature|LiquidityProviderSandbox|LogMetadataTransformer|MetaTransactionsFeature|MixinAaveV2|MixinBalancer|MixinBalancerV2|MixinBalancerV2Batch|MixinBancor|MixinCoFiX|MixinCompound|MixinCryptoCom|MixinCurve|MixinCurveV2|MixinDodo|MixinDodoV2|MixinKyber|MixinKyberDmm|MixinLido|MixinMStable|MixinMakerPSM|MixinMooniswap|MixinNerve|MixinOasis|MixinShell|MixinUniswap|MixinUniswapV2|MixinUniswapV3|MixinZeroExBridge|MooniswapLiquidityProvider|MultiplexFeature|MultiplexLiquidityProvider|MultiplexOtc|MultiplexRfq|MultiplexTransformERC20|MultiplexUniswapV2|MultiplexUniswapV3|NFTOrders|NativeOrdersCancellation|NativeOrdersFeature|NativeOrdersInfo|NativeOrdersProtocolFees|NativeOrdersSettlement|OtcOrdersFeature|OwnableFeature|PancakeSwapFeature|PayTakerTransformer|PermissionlessTransformerDeployer|PositiveSlippageFeeTransformer|SimpleFunctionRegistryFeature|TestBridge|TestCallTarget|TestCurve|TestDelegateCaller|TestFeeCollectorController|TestFeeRecipient|TestFillQuoteTransformerBridge|TestFillQuoteTransformerExchange|TestFillQuoteTransformerHost|TestFixinProtocolFees|TestFixinTokenSpender|TestFullMigration|TestInitialMigration|TestLibNativeOrder|TestLibSignature|TestLiquidityProvider|TestMetaTransactionsNativeOrdersFeature|TestMetaTransactionsTransformERC20Feature|TestMigrator|TestMintTokenERC20Transformer|TestMintableERC1155Token|TestMintableERC20Token|TestMintableERC721Token|TestMooniswap|TestNFTOrderPresigner|TestNativeOrdersFeature|TestNoEthRecipient|TestOrderSignerRegistryWithContractWallet|TestPermissionlessTransformerDeployerSuicidal|TestPermissionlessTransformerDeployerTransformer|TestPropertyValidator|TestRfqOriginRegistration|TestSimpleFunctionRegistryFeatureImpl1|TestSimpleFunctionRegistryFeatureImpl2|TestStaking|TestTokenSpenderERC20Token|TestTransformERC20|TestTransformerBase|TestTransformerDeployerTransformer|TestTransformerHost|TestUniswapV2Factory|TestUniswapV2Pool|TestUniswapV3Factory|TestUniswapV3Feature|TestUniswapV3Pool|TestWeth|TestWethTransformerHost|TestZeroExFeature|TransformERC20Feature|Transformer|TransformerDeployer|UniswapFeature|UniswapV3Feature|WethTransformer|ZeroEx|ZeroExOptimized).json"
}, },
"repository": { "repository": {
"type": "git", "type": "git",

View File

@ -101,6 +101,7 @@ import * as MetaTransactionsFeature from '../test/generated-artifacts/MetaTransa
import * as MixinAaveV2 from '../test/generated-artifacts/MixinAaveV2.json'; import * as MixinAaveV2 from '../test/generated-artifacts/MixinAaveV2.json';
import * as MixinBalancer from '../test/generated-artifacts/MixinBalancer.json'; import * as MixinBalancer from '../test/generated-artifacts/MixinBalancer.json';
import * as MixinBalancerV2 from '../test/generated-artifacts/MixinBalancerV2.json'; import * as MixinBalancerV2 from '../test/generated-artifacts/MixinBalancerV2.json';
import * as MixinBalancerV2Batch from '../test/generated-artifacts/MixinBalancerV2Batch.json';
import * as MixinBancor from '../test/generated-artifacts/MixinBancor.json'; import * as MixinBancor from '../test/generated-artifacts/MixinBancor.json';
import * as MixinCoFiX from '../test/generated-artifacts/MixinCoFiX.json'; import * as MixinCoFiX from '../test/generated-artifacts/MixinCoFiX.json';
import * as MixinCompound from '../test/generated-artifacts/MixinCompound.json'; import * as MixinCompound from '../test/generated-artifacts/MixinCompound.json';
@ -313,6 +314,7 @@ export const artifacts = {
MixinAaveV2: MixinAaveV2 as ContractArtifact, MixinAaveV2: MixinAaveV2 as ContractArtifact,
MixinBalancer: MixinBalancer as ContractArtifact, MixinBalancer: MixinBalancer as ContractArtifact,
MixinBalancerV2: MixinBalancerV2 as ContractArtifact, MixinBalancerV2: MixinBalancerV2 as ContractArtifact,
MixinBalancerV2Batch: MixinBalancerV2Batch as ContractArtifact,
MixinBancor: MixinBancor as ContractArtifact, MixinBancor: MixinBancor as ContractArtifact,
MixinCoFiX: MixinCoFiX as ContractArtifact, MixinCoFiX: MixinCoFiX as ContractArtifact,
MixinCompound: MixinCompound as ContractArtifact, MixinCompound: MixinCompound as ContractArtifact,

View File

@ -99,6 +99,7 @@ export * from '../test/generated-wrappers/meta_transactions_feature';
export * from '../test/generated-wrappers/mixin_aave_v2'; export * from '../test/generated-wrappers/mixin_aave_v2';
export * from '../test/generated-wrappers/mixin_balancer'; export * from '../test/generated-wrappers/mixin_balancer';
export * from '../test/generated-wrappers/mixin_balancer_v2'; export * from '../test/generated-wrappers/mixin_balancer_v2';
export * from '../test/generated-wrappers/mixin_balancer_v2_batch';
export * from '../test/generated-wrappers/mixin_bancor'; export * from '../test/generated-wrappers/mixin_bancor';
export * from '../test/generated-wrappers/mixin_co_fi_x'; export * from '../test/generated-wrappers/mixin_co_fi_x';
export * from '../test/generated-wrappers/mixin_compound'; export * from '../test/generated-wrappers/mixin_compound';

View File

@ -132,6 +132,7 @@
"test/generated-artifacts/MixinAaveV2.json", "test/generated-artifacts/MixinAaveV2.json",
"test/generated-artifacts/MixinBalancer.json", "test/generated-artifacts/MixinBalancer.json",
"test/generated-artifacts/MixinBalancerV2.json", "test/generated-artifacts/MixinBalancerV2.json",
"test/generated-artifacts/MixinBalancerV2Batch.json",
"test/generated-artifacts/MixinBancor.json", "test/generated-artifacts/MixinBancor.json",
"test/generated-artifacts/MixinCoFiX.json", "test/generated-artifacts/MixinCoFiX.json",
"test/generated-artifacts/MixinCompound.json", "test/generated-artifacts/MixinCompound.json",

View File

@ -1,4 +1,13 @@
[ [
{
"version": "16.57.0",
"changes": [
{
"note": "Add BalancerV2 batch swap support",
"pr": 462
}
]
},
{ {
"version": "16.56.0", "version": "16.56.0",
"changes": [ "changes": [

View File

@ -6,7 +6,7 @@
"shouldSaveStandardInput": true, "shouldSaveStandardInput": true,
"compilerSettings": { "compilerSettings": {
"evmVersion": "istanbul", "evmVersion": "istanbul",
"optimizer": { "enabled": true, "runs": 200, "details": { "yul": true, "deduplicate": true } }, "optimizer": { "enabled": true, "runs": 200, "details": { "yul": false, "deduplicate": true } },
"outputSelection": { "outputSelection": {
"*": { "*": {
"*": [ "*": [

View File

@ -0,0 +1,105 @@
// 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;
}
}
}
}

View File

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

View File

@ -21,44 +21,11 @@ pragma solidity ^0.6;
pragma experimental ABIEncoderV2; pragma experimental ABIEncoderV2;
import "./SamplerUtils.sol"; import "./SamplerUtils.sol";
import "./interfaces/IBalancerV2Vault.sol";
import "./BalancerV2Common.sol";
/// @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 { contract BalancerV2Sampler is SamplerUtils, BalancerV2Common {
bytes32 poolId;
uint256 assetInIndex;
uint256 assetOutIndex;
uint256 amount;
bytes userData;
}
struct FundManagement {
address sender;
bool fromInternalBalance;
address payable recipient;
bool toInternalBalance;
}
function queryBatchSwap(
SwapKind kind,
BatchSwapStep[] calldata swaps,
IAsset[] calldata assets,
FundManagement calldata funds
) external returns (int256[] memory assetDeltas);
}
interface IAsset {
// solhint-disable-previous-line no-empty-blocks
}
contract BalancerV2Sampler is SamplerUtils {
struct BalancerV2PoolInfo {
bytes32 poolId;
address vault;
}
/// @dev Sample sell quotes from Balancer V2. /// @dev Sample sell quotes from Balancer V2.
/// @param poolInfo Struct with pool related data /// @param poolInfo Struct with pool related data
@ -68,7 +35,7 @@ contract BalancerV2Sampler is SamplerUtils {
/// @return makerTokenAmounts Maker amounts bought at each taker token /// @return makerTokenAmounts Maker amounts bought at each taker token
/// amount. /// amount.
function sampleSellsFromBalancerV2( function sampleSellsFromBalancerV2(
BalancerV2PoolInfo memory poolInfo, IBalancerV2Vault.BalancerV2PoolInfo memory poolInfo,
address takerToken, address takerToken,
address makerToken, address makerToken,
uint256[] memory takerTokenAmounts uint256[] memory takerTokenAmounts
@ -78,9 +45,9 @@ contract BalancerV2Sampler is SamplerUtils {
{ {
_assertValidPair(makerToken, takerToken); _assertValidPair(makerToken, takerToken);
IBalancerV2Vault vault = IBalancerV2Vault(poolInfo.vault); IBalancerV2Vault vault = IBalancerV2Vault(poolInfo.vault);
IAsset[] memory swapAssets = new IAsset[](2); address[] memory swapAssets = new address[](2);
swapAssets[0] = IAsset(takerToken); swapAssets[0] = takerToken;
swapAssets[1] = IAsset(makerToken); swapAssets[1] = makerToken;
uint256 numSamples = takerTokenAmounts.length; uint256 numSamples = takerTokenAmounts.length;
makerTokenAmounts = new uint256[](numSamples); makerTokenAmounts = new uint256[](numSamples);
@ -97,7 +64,7 @@ contract BalancerV2Sampler is SamplerUtils {
// amounts represent pool balance deltas from the swap (incoming balance, outgoing balance) // amounts represent pool balance deltas from the swap (incoming balance, outgoing balance)
returns (int256[] memory amounts) { returns (int256[] memory amounts) {
// Outgoing balance is negative so we need to flip the sign // Outgoing balance is negative so we need to flip the sign
int256 amountOutFromPool = amounts[1] * -1; int256 amountOutFromPool = amounts[amounts.length - 1] * -1;
if (amountOutFromPool <= 0) { if (amountOutFromPool <= 0) {
break; break;
} }
@ -117,7 +84,7 @@ contract BalancerV2Sampler is SamplerUtils {
/// @return takerTokenAmounts Taker amounts sold at each maker token /// @return takerTokenAmounts Taker amounts sold at each maker token
/// amount. /// amount.
function sampleBuysFromBalancerV2( function sampleBuysFromBalancerV2(
BalancerV2PoolInfo memory poolInfo, IBalancerV2Vault.BalancerV2PoolInfo memory poolInfo,
address takerToken, address takerToken,
address makerToken, address makerToken,
uint256[] memory makerTokenAmounts uint256[] memory makerTokenAmounts
@ -127,9 +94,9 @@ contract BalancerV2Sampler is SamplerUtils {
{ {
_assertValidPair(makerToken, takerToken); _assertValidPair(makerToken, takerToken);
IBalancerV2Vault vault = IBalancerV2Vault(poolInfo.vault); IBalancerV2Vault vault = IBalancerV2Vault(poolInfo.vault);
IAsset[] memory swapAssets = new IAsset[](2); address[] memory swapAssets = new address[](2);
swapAssets[0] = IAsset(takerToken); swapAssets[0] = takerToken;
swapAssets[1] = IAsset(makerToken); swapAssets[1] = makerToken;
uint256 numSamples = makerTokenAmounts.length; uint256 numSamples = makerTokenAmounts.length;
takerTokenAmounts = new uint256[](numSamples); takerTokenAmounts = new uint256[](numSamples);
@ -157,7 +124,7 @@ contract BalancerV2Sampler is SamplerUtils {
} }
function _createSwapSteps( function _createSwapSteps(
BalancerV2PoolInfo memory poolInfo, IBalancerV2Vault.BalancerV2PoolInfo memory poolInfo,
uint256 amount uint256 amount
) private pure returns (IBalancerV2Vault.BatchSwapStep[] memory) { ) private pure returns (IBalancerV2Vault.BatchSwapStep[] memory) {
IBalancerV2Vault.BatchSwapStep[] memory swapSteps = IBalancerV2Vault.BatchSwapStep[] memory swapSteps =
@ -172,18 +139,4 @@ contract BalancerV2Sampler is SamplerUtils {
return swapSteps; return swapSteps;
} }
function _createSwapFunds()
private
view
returns (IBalancerV2Vault.FundManagement memory)
{
return
IBalancerV2Vault.FundManagement({
sender: address(this),
fromInternalBalance: false,
recipient: payable(address(this)),
toInternalBalance: false
});
}
} }

View File

@ -22,9 +22,8 @@ pragma experimental ABIEncoderV2;
import "./interfaces/IBancor.sol"; import "./interfaces/IBancor.sol";
contract CompilerHack {}
contract BancorSampler is CompilerHack { contract BancorSampler {
/// @dev Base gas limit for Bancor calls. /// @dev Base gas limit for Bancor calls.
uint256 constant private BANCOR_CALL_GAS = 300e3; // 300k uint256 constant private BANCOR_CALL_GAS = 300e3; // 300k

View File

@ -22,6 +22,7 @@ pragma experimental ABIEncoderV2;
import "./BalancerSampler.sol"; import "./BalancerSampler.sol";
import "./BalancerV2Sampler.sol"; import "./BalancerV2Sampler.sol";
import "./BalancerV2BatchSampler.sol";
import "./BancorSampler.sol"; import "./BancorSampler.sol";
import "./CompoundSampler.sol"; import "./CompoundSampler.sol";
import "./CurveSampler.sol"; import "./CurveSampler.sol";
@ -32,7 +33,6 @@ import "./KyberDmmSampler.sol";
import "./LidoSampler.sol"; import "./LidoSampler.sol";
import "./LiquidityProviderSampler.sol"; import "./LiquidityProviderSampler.sol";
import "./MakerPSMSampler.sol"; import "./MakerPSMSampler.sol";
import "./MultiBridgeSampler.sol";
import "./MStableSampler.sol"; import "./MStableSampler.sol";
import "./MooniswapSampler.sol"; import "./MooniswapSampler.sol";
import "./NativeOrderSampler.sol"; import "./NativeOrderSampler.sol";
@ -48,6 +48,7 @@ import "./UtilitySampler.sol";
contract ERC20BridgeSampler is contract ERC20BridgeSampler is
BalancerSampler, BalancerSampler,
BalancerV2Sampler, BalancerV2Sampler,
BalancerV2BatchSampler,
BancorSampler, BancorSampler,
CompoundSampler, CompoundSampler,
CurveSampler, CurveSampler,
@ -60,7 +61,6 @@ contract ERC20BridgeSampler is
MakerPSMSampler, MakerPSMSampler,
MStableSampler, MStableSampler,
MooniswapSampler, MooniswapSampler,
MultiBridgeSampler,
NativeOrderSampler, NativeOrderSampler,
ShellSampler, ShellSampler,
SmoothySampler, SmoothySampler,
@ -92,4 +92,6 @@ contract ERC20BridgeSampler is
(callResults[i].success, callResults[i].data) = address(this).call(callDatas[i]); (callResults[i].success, callResults[i].data) = address(this).call(callDatas[i]);
} }
} }
receive() external payable {}
} }

View File

@ -1,82 +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/IMultiBridge.sol";
contract MultiBridgeSampler {
/// @dev Default gas limit for multibridge calls.
uint256 constant private DEFAULT_CALL_GAS = 400e3; // 400k
/// @dev Sample sell quotes from MultiBridge.
/// @param multibridge Address of the MultiBridge contract.
/// @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 takerTokenAmounts Taker token sell amount for each sample.
/// @return makerTokenAmounts Maker amounts bought at each taker token
/// amount.
function sampleSellsFromMultiBridge(
address multibridge,
address takerToken,
address intermediateToken,
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);
// If no address provided, return all zeros.
if (multibridge == address(0)) {
return makerTokenAmounts;
}
for (uint256 i = 0; i < numSamples; i++) {
(bool didSucceed, bytes memory resultData) =
multibridge.staticcall.gas(DEFAULT_CALL_GAS)(
abi.encodeWithSelector(
IMultiBridge(0).getSellQuote.selector,
takerToken,
intermediateToken,
makerToken,
takerTokenAmounts[i]
));
uint256 buyAmount = 0;
if (didSucceed) {
buyAmount = abi.decode(resultData, (uint256));
}
// Exit early if the amount is too high for the source to serve
if (buyAmount == 0) {
break;
}
makerTokenAmounts[i] = buyAmount;
}
}
}

View File

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

View File

@ -1,39 +0,0 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.6;
pragma experimental ABIEncoderV2;
contract DummyLiquidityProvider
{
/// @dev Quotes the amount of `makerToken` that would be obtained by
/// selling `sellAmount` of `takerToken`.
/// @param sellAmount Amount of `takerToken` to sell.
/// @return makerTokenAmount Amount of `makerToken` that would be obtained.
function getSellQuote(
address, /* takerToken */
address, /* makerToken */
uint256 sellAmount
)
external
view
returns (uint256 makerTokenAmount)
{
makerTokenAmount = sellAmount - 1;
}
/// @dev Quotes the amount of `takerToken` that would need to be sold in
/// order to obtain `buyAmount` of `makerToken`.
/// @param buyAmount Amount of `makerToken` to buy.
/// @return takerTokenAmount Amount of `takerToken` that would need to be sold.
function getBuyQuote(
address, /* takerToken */
address, /* makerToken */
uint256 buyAmount
)
external
view
returns (uint256 takerTokenAmount)
{
takerTokenAmount = buyAmount + 1;
}
}

View File

@ -1,455 +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 "../src/ERC20BridgeSampler.sol";
import "../src/interfaces/IKyberNetwork.sol";
import "../src/interfaces/IUniswapV2Router01.sol";
library LibDeterministicQuotes {
address private constant WETH_ADDRESS = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
uint256 private constant RATE_DENOMINATOR = 1 ether;
uint256 private constant MIN_RATE = RATE_DENOMINATOR / 100;
uint256 private constant MAX_RATE = 100 * RATE_DENOMINATOR;
uint8 private constant MIN_DECIMALS = 4;
uint8 private constant MAX_DECIMALS = 20;
function getDeterministicSellQuote(
bytes32 salt,
address sellToken,
address buyToken,
uint256 sellAmount
)
internal
pure
returns (uint256 buyAmount)
{
uint256 sellBase = uint256(10) ** getDeterministicTokenDecimals(sellToken);
uint256 buyBase = uint256(10) ** getDeterministicTokenDecimals(buyToken);
uint256 rate = getDeterministicRate(salt, sellToken, buyToken);
return sellAmount * rate * buyBase / sellBase / RATE_DENOMINATOR;
}
function getDeterministicBuyQuote(
bytes32 salt,
address sellToken,
address buyToken,
uint256 buyAmount
)
internal
pure
returns (uint256 sellAmount)
{
uint256 sellBase = uint256(10) ** getDeterministicTokenDecimals(sellToken);
uint256 buyBase = uint256(10) ** getDeterministicTokenDecimals(buyToken);
uint256 rate = getDeterministicRate(salt, sellToken, buyToken);
return buyAmount * RATE_DENOMINATOR * sellBase / rate / buyBase;
}
function getDeterministicTokenDecimals(address token)
internal
pure
returns (uint8 decimals)
{
if (token == WETH_ADDRESS) {
return 18;
}
bytes32 seed = keccak256(abi.encodePacked(token));
return uint8(uint256(seed) % (MAX_DECIMALS - MIN_DECIMALS)) + MIN_DECIMALS;
}
function getDeterministicRate(bytes32 salt, address sellToken, address buyToken)
internal
pure
returns (uint256 rate)
{
bytes32 seed = keccak256(abi.encodePacked(salt, sellToken, buyToken));
return uint256(seed) % (MAX_RATE - MIN_RATE) + MIN_RATE;
}
}
contract TestDeploymentConstants {
// solhint-disable separate-by-one-line-in-contract
// Mainnet addresses ///////////////////////////////////////////////////////
/// @dev Mainnet address of the WETH contract.
address constant private WETH_ADDRESS = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
/// @dev Overridable way to get the WETH address.
/// @return wethAddress The WETH address.
function _getWethAddress()
internal
view
returns (address wethAddress)
{
return WETH_ADDRESS;
}
}
contract FailTrigger {
// Give this address a balance to force operations to fail.
address payable constant public FAILURE_ADDRESS = 0xe9dB8717BC5DFB20aaf538b4a5a02B7791FF430C;
// Funds `FAILURE_ADDRESS`.
function enableFailTrigger() external payable {
FAILURE_ADDRESS.transfer(msg.value);
}
function _revertIfShouldFail() internal view {
if (FAILURE_ADDRESS.balance != 0) {
revert("FAIL_TRIGGERED");
}
}
}
contract TestERC20BridgeSamplerUniswapExchange is
IUniswapExchangeQuotes,
TestDeploymentConstants,
FailTrigger
{
bytes32 constant private BASE_SALT = 0x1d6a6a0506b0b4a554b907a4c29d9f4674e461989d9c1921feb17b26716385ab;
address public tokenAddress;
bytes32 public salt;
constructor(address _tokenAddress) public {
tokenAddress = _tokenAddress;
salt = keccak256(abi.encodePacked(BASE_SALT, _tokenAddress));
}
// Deterministic `IUniswapExchangeQuotes.getEthToTokenInputPrice()`.
function getEthToTokenInputPrice(
uint256 ethSold
)
override
external
view
returns (uint256 tokensBought)
{
_revertIfShouldFail();
return LibDeterministicQuotes.getDeterministicSellQuote(
salt,
tokenAddress,
_getWethAddress(),
ethSold
);
}
// Deterministic `IUniswapExchangeQuotes.getEthToTokenOutputPrice()`.
function getEthToTokenOutputPrice(
uint256 tokensBought
)
override
external
view
returns (uint256 ethSold)
{
_revertIfShouldFail();
return LibDeterministicQuotes.getDeterministicBuyQuote(
salt,
_getWethAddress(),
tokenAddress,
tokensBought
);
}
// Deterministic `IUniswapExchangeQuotes.getTokenToEthInputPrice()`.
function getTokenToEthInputPrice(
uint256 tokensSold
)
override
external
view
returns (uint256 ethBought)
{
_revertIfShouldFail();
return LibDeterministicQuotes.getDeterministicSellQuote(
salt,
tokenAddress,
_getWethAddress(),
tokensSold
);
}
// Deterministic `IUniswapExchangeQuotes.getTokenToEthOutputPrice()`.
function getTokenToEthOutputPrice(
uint256 ethBought
)
override
external
view
returns (uint256 tokensSold)
{
_revertIfShouldFail();
return LibDeterministicQuotes.getDeterministicBuyQuote(
salt,
_getWethAddress(),
tokenAddress,
ethBought
);
}
}
contract TestERC20BridgeSamplerUniswapV2Router01 is
IUniswapV2Router01,
TestDeploymentConstants,
FailTrigger
{
bytes32 constant private SALT = 0xadc7fcb33c735913b8635927e66896b356a53a912ab2ceff929e60a04b53b3c1;
// Deterministic `IUniswapV2Router01.getAmountsOut()`.
function getAmountsOut(uint256 amountIn, address[] calldata path)
override
external
view
returns (uint256[] memory amounts)
{
require(path.length >= 2, "PATH_TOO_SHORT");
_revertIfShouldFail();
amounts = new uint256[](path.length);
amounts[0] = amountIn;
for (uint256 i = 0; i < path.length - 1; ++i) {
amounts[i + 1] = LibDeterministicQuotes.getDeterministicSellQuote(
SALT,
path[i],
path[i + 1],
amounts[i]
);
}
}
// Deterministic `IUniswapV2Router01.getAmountsInt()`.
function getAmountsIn(uint256 amountOut, address[] calldata path)
override
external
view
returns (uint256[] memory amounts)
{
require(path.length >= 2, "PATH_TOO_SHORT");
_revertIfShouldFail();
amounts = new uint256[](path.length);
amounts[path.length - 1] = amountOut;
for (uint256 i = path.length - 1; i > 0; --i) {
amounts[i - 1] = LibDeterministicQuotes.getDeterministicBuyQuote(
SALT,
path[i - 1],
path[i],
amounts[i]
);
}
}
}
// solhint-disable space-after-comma
contract TestERC20BridgeSamplerKyberNetwork is
TestDeploymentConstants,
FailTrigger
{
bytes32 constant private SALT = 0x0ff3ca9d46195c39f9a12afb74207b4970349fb3cfb1e459bbf170298d326bc7;
address constant public ETH_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
enum TradeType {BestOfAll, MaskIn, MaskOut, Split}
enum ProcessWithRate {NotRequired, Required}
// IKyberHintHandler
function buildTokenToEthHint(
address tokenSrc,
TradeType /* tokenToEthType */,
bytes32[] calldata /* tokenToEthReserveIds */,
uint256[] calldata /* tokenToEthSplits */
) external view returns (bytes memory hint)
{
return abi.encode(tokenSrc);
}
function buildEthToTokenHint(
address tokenDest,
TradeType /* ethToTokenType */,
bytes32[] calldata /* ethToTokenReserveIds */,
uint256[] calldata /* ethToTokenSplits */
) external view returns (bytes memory hint)
{
return abi.encode(tokenDest);
}
// IKyberHintHandler
function buildTokenToTokenHint(
address tokenSrc,
TradeType /* tokenToEthType */,
bytes32[] calldata /* tokenToEthReserveIds */,
uint256[] calldata /* tokenToEthSplits */,
address /* tokenDest */,
TradeType /* EthToTokenType */,
bytes32[] calldata /* EthToTokenReserveIds */,
uint256[] calldata /* EthToTokenSplits */
) external view returns (bytes memory hint)
{
return abi.encode(tokenSrc);
}
// IKyberHintHandler
function getTradingReserves(
address tokenSrc,
address tokenDest,
bool isTokenToToken,
bytes calldata hint
)
external
view
returns (
bytes32[] memory reserveIds,
uint256[] memory splitValuesBps,
ProcessWithRate processWithRate
)
{
reserveIds = new bytes32[](1);
reserveIds[0] = bytes32(uint256(1));
splitValuesBps = new uint256[](0);
processWithRate = ProcessWithRate.NotRequired;
}
// Deterministic `IKyberNetworkProxy.getExpectedRateAfterFee()`.
function getExpectedRateAfterFee(
address fromToken,
address toToken,
uint256 /* srcQty */,
uint256 /* fee */,
bytes calldata /* hint */
)
external
view
returns
(uint256 expectedRate)
{
_revertIfShouldFail();
fromToken = fromToken == ETH_ADDRESS ? _getWethAddress() : fromToken;
toToken = toToken == ETH_ADDRESS ? _getWethAddress() : toToken;
expectedRate = LibDeterministicQuotes.getDeterministicRate(
SALT,
fromToken,
toToken
);
}
// Deterministic `IKyberNetworkProxy.getExpectedRate()`.
function getExpectedRate(
address fromToken,
address toToken,
uint256
)
external
view
returns (uint256 expectedRate, uint256)
{
_revertIfShouldFail();
fromToken = fromToken == ETH_ADDRESS ? _getWethAddress() : fromToken;
toToken = toToken == ETH_ADDRESS ? _getWethAddress() : toToken;
expectedRate = LibDeterministicQuotes.getDeterministicRate(
SALT,
fromToken,
toToken
);
}
}
contract TestERC20BridgeSamplerUniswapExchangeFactory is
IUniswapExchangeFactory
{
mapping (address => IUniswapExchangeQuotes) private _exchangesByToken;
// Creates Uniswap exchange contracts for tokens.
function createTokenExchanges(address[] calldata tokenAddresses)
external
{
for (uint256 i = 0; i < tokenAddresses.length; i++) {
address tokenAddress = tokenAddresses[i];
_exchangesByToken[tokenAddress] =
new TestERC20BridgeSamplerUniswapExchange(tokenAddress);
}
}
// `IUniswapExchangeFactory.getExchange()`.
function getExchange(address tokenAddress)
override
external
view
returns (address)
{
return address(_exchangesByToken[tokenAddress]);
}
}
contract TestERC20BridgeSampler is
ERC20BridgeSampler,
FailTrigger
{
TestERC20BridgeSamplerUniswapExchangeFactory public uniswap;
TestERC20BridgeSamplerUniswapV2Router01 public uniswapV2Router;
TestERC20BridgeSamplerKyberNetwork public kyber;
uint8 private constant MAX_ORDER_STATUS = uint8(IExchange.OrderStatus.CANCELLED) + 1;
constructor() public ERC20BridgeSampler() {
uniswap = new TestERC20BridgeSamplerUniswapExchangeFactory();
uniswapV2Router = new TestERC20BridgeSamplerUniswapV2Router01();
kyber = new TestERC20BridgeSamplerKyberNetwork();
}
// Creates Uniswap exchange contracts for tokens.
function createTokenExchanges(address[] calldata tokenAddresses)
external
{
uniswap.createTokenExchanges(tokenAddresses);
}
// Overridden to return deterministic states.
function getLimitOrderFillableTakerAmount(
IExchange.LimitOrder memory order,
IExchange.Signature memory,
IExchange
)
override
public
view
returns (uint256 fillableTakerAmount)
{
return uint256(keccak256(abi.encode(order.salt))) % order.takerAmount;
}
// Overriden to return deterministic decimals.
function _getTokenDecimals(address tokenAddress)
override
internal
view
returns (uint8 decimals)
{
return LibDeterministicQuotes.getDeterministicTokenDecimals(tokenAddress);
}
}

View File

@ -39,7 +39,7 @@
"config": { "config": {
"publicInterfaceContracts": "ERC20BridgeSampler,BalanceChecker,FakeTaker", "publicInterfaceContracts": "ERC20BridgeSampler,BalanceChecker,FakeTaker",
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually.", "abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually.",
"abis": "./test/generated-artifacts/@(ApproximateBuys|BalanceChecker|BalancerSampler|BalancerV2Sampler|BancorSampler|CompoundSampler|CurveSampler|DODOSampler|DODOV2Sampler|DummyLiquidityProvider|ERC20BridgeSampler|FakeTaker|IBalancer|IBancor|ICurve|IKyberNetwork|IMStable|IMooniswap|IMultiBridge|IShell|ISmoothy|IUniswapExchangeQuotes|IUniswapV2Router01|KyberDmmSampler|KyberSampler|LidoSampler|LiquidityProviderSampler|MStableSampler|MakerPSMSampler|MooniswapSampler|MultiBridgeSampler|NativeOrderSampler|SamplerUtils|ShellSampler|SmoothySampler|TestERC20BridgeSampler|TestNativeOrderSampler|TwoHopSampler|UniswapSampler|UniswapV2Sampler|UniswapV3Sampler|UtilitySampler).json", "abis": "./test/generated-artifacts/@(ApproximateBuys|BalanceChecker|BalancerSampler|BalancerV2BatchSampler|BalancerV2Common|BalancerV2Sampler|BancorSampler|CompoundSampler|CurveSampler|DODOSampler|DODOV2Sampler|ERC20BridgeSampler|FakeTaker|IBalancer|IBalancerV2Vault|IBancor|ICurve|IKyberNetwork|IMStable|IMooniswap|IMultiBridge|IShell|ISmoothy|IUniswapExchangeQuotes|IUniswapV2Router01|KyberDmmSampler|KyberSampler|LidoSampler|LiquidityProviderSampler|MStableSampler|MakerPSMSampler|MooniswapSampler|NativeOrderSampler|SamplerUtils|ShellSampler|SmoothySampler|TestNativeOrderSampler|TwoHopSampler|UniswapSampler|UniswapV2Sampler|UniswapV3Sampler|UtilitySampler).json",
"postpublish": { "postpublish": {
"assets": [] "assets": []
} }
@ -73,6 +73,7 @@
"@0x/typescript-typings": "^5.3.1", "@0x/typescript-typings": "^5.3.1",
"@0x/utils": "^6.5.3", "@0x/utils": "^6.5.3",
"@0x/web3-wrapper": "^7.6.5", "@0x/web3-wrapper": "^7.6.5",
"@balancer-labs/sdk": "^0.1.6",
"@balancer-labs/sor": "0.3.2", "@balancer-labs/sor": "0.3.2",
"@bancor/sdk": "0.2.9", "@bancor/sdk": "0.2.9",
"@ethersproject/abi": "^5.0.1", "@ethersproject/abi": "^5.0.1",
@ -90,7 +91,8 @@
"graphql": "^15.4.0", "graphql": "^15.4.0",
"graphql-request": "^3.4.0", "graphql-request": "^3.4.0",
"heartbeats": "^5.0.1", "heartbeats": "^5.0.1",
"lodash": "^4.17.11" "lodash": "^4.17.11",
"sorV2": "npm:@balancer-labs/sor"
}, },
"devDependencies": { "devDependencies": {
"@0x/abi-gen": "^5.8.0", "@0x/abi-gen": "^5.8.0",

View File

@ -8,6 +8,7 @@ import { TokenAdjacencyGraphBuilder } from '../token_adjacency_graph_builder';
import { SourceFilters } from './source_filters'; import { SourceFilters } from './source_filters';
import { import {
AaveV2FillData, AaveV2FillData,
BalancerV2BatchSwapFillData,
BancorFillData, BancorFillData,
CompoundFillData, CompoundFillData,
CurveFillData, CurveFillData,
@ -2156,11 +2157,12 @@ export const BALANCER_SUBGRAPH_URL = 'https://api.thegraph.com/subgraphs/name/ba
export const BALANCER_TOP_POOLS_FETCHED = 250; export const BALANCER_TOP_POOLS_FETCHED = 250;
export const BALANCER_MAX_POOLS_FETCHED = 3; export const BALANCER_MAX_POOLS_FETCHED = 3;
export const BALANCER_V2_SUBGRAPH_URL_BY_CHAIN = valueByChainId<string>( 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', [ChainId.Polygon]: 'https://api.thegraph.com/subgraphs/name/balancer-labs/balancer-polygon-v2',
}, },
'https://api.thegraph.com/subgraphs/name/balancer-labs/balancer-v2', null,
); );
export const BEETHOVEN_X_SUBGRAPH_URL_BY_CHAIN = valueByChainId<string>( export const BEETHOVEN_X_SUBGRAPH_URL_BY_CHAIN = valueByChainId<string>(
@ -2433,7 +2435,9 @@ export const DEFAULT_GAS_SCHEDULE: Required<FeeSchedule> = {
[ERC20BridgeSource.Linkswap]: uniswapV2CloneGasSchedule, [ERC20BridgeSource.Linkswap]: uniswapV2CloneGasSchedule,
[ERC20BridgeSource.ShibaSwap]: uniswapV2CloneGasSchedule, [ERC20BridgeSource.ShibaSwap]: uniswapV2CloneGasSchedule,
[ERC20BridgeSource.Balancer]: () => 120e3, [ERC20BridgeSource.Balancer]: () => 120e3,
[ERC20BridgeSource.BalancerV2]: () => 100e3, [ERC20BridgeSource.BalancerV2]: (fillData?: FillData) => {
return 100e3 + ((fillData as BalancerV2BatchSwapFillData).swapSteps.length - 1) * 50e3;
},
[ERC20BridgeSource.Cream]: () => 120e3, [ERC20BridgeSource.Cream]: () => 120e3,
[ERC20BridgeSource.MStable]: () => 200e3, [ERC20BridgeSource.MStable]: () => 200e3,
[ERC20BridgeSource.MakerPsm]: (fillData?: FillData) => { [ERC20BridgeSource.MakerPsm]: (fillData?: FillData) => {

View File

@ -8,6 +8,7 @@ import {
AaveV2FillData, AaveV2FillData,
AggregationError, AggregationError,
BalancerFillData, BalancerFillData,
BalancerV2BatchSwapFillData,
BalancerV2FillData, BalancerV2FillData,
BancorFillData, BancorFillData,
CollapsedFill, CollapsedFill,
@ -86,7 +87,7 @@ export function getErc20BridgeSourceToBridgeSource(source: ERC20BridgeSource): s
case ERC20BridgeSource.Balancer: case ERC20BridgeSource.Balancer:
return encodeBridgeSourceId(BridgeProtocol.Balancer, 'Balancer'); return encodeBridgeSourceId(BridgeProtocol.Balancer, 'Balancer');
case ERC20BridgeSource.BalancerV2: case ERC20BridgeSource.BalancerV2:
return encodeBridgeSourceId(BridgeProtocol.BalancerV2, 'BalancerV2'); return encodeBridgeSourceId(BridgeProtocol.BalancerV2Batch, 'BalancerV2');
case ERC20BridgeSource.Bancor: case ERC20BridgeSource.Bancor:
return encodeBridgeSourceId(BridgeProtocol.Bancor, 'Bancor'); return encodeBridgeSourceId(BridgeProtocol.Bancor, 'Bancor');
// case ERC20BridgeSource.CoFiX: // case ERC20BridgeSource.CoFiX:
@ -258,9 +259,18 @@ export function createBridgeDataForBridgeOrder(order: OptimizedMarketBridgeOrder
bridgeData = encoder.encode([balancerFillData.poolAddress]); bridgeData = encoder.encode([balancerFillData.poolAddress]);
break; break;
case ERC20BridgeSource.BalancerV2: case ERC20BridgeSource.BalancerV2:
{
const balancerV2FillData = (order as OptimizedMarketBridgeOrder<BalancerV2BatchSwapFillData>).fillData;
bridgeData = encoder.encode([
balancerV2FillData.vault,
balancerV2FillData.swapSteps,
balancerV2FillData.assets,
]);
}
break;
case ERC20BridgeSource.Beethovenx: case ERC20BridgeSource.Beethovenx:
const balancerV2FillData = (order as OptimizedMarketBridgeOrder<BalancerV2FillData>).fillData; const beethovenFillData = (order as OptimizedMarketBridgeOrder<BalancerV2FillData>).fillData;
const { vault, poolId } = balancerV2FillData; const { vault, poolId } = beethovenFillData;
bridgeData = encoder.encode([vault, poolId]); bridgeData = encoder.encode([vault, poolId]);
break; break;
case ERC20BridgeSource.Bancor: case ERC20BridgeSource.Bancor:
@ -533,7 +543,21 @@ export const BRIDGE_ENCODERS: {
[ERC20BridgeSource.Uniswap]: poolEncoder, [ERC20BridgeSource.Uniswap]: poolEncoder,
// Custom integrations // Custom integrations
[ERC20BridgeSource.MakerPsm]: makerPsmEncoder, [ERC20BridgeSource.MakerPsm]: makerPsmEncoder,
[ERC20BridgeSource.BalancerV2]: balancerV2Encoder, [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.Beethovenx]: balancerV2Encoder,
[ERC20BridgeSource.UniswapV3]: AbiEncoder.create([ [ERC20BridgeSource.UniswapV3]: AbiEncoder.create([
{ name: 'router', type: 'address' }, { name: 'router', type: 'address' },

View File

@ -51,7 +51,7 @@ export class BalancerV2PoolsCache extends PoolsCache {
constructor( constructor(
chainId: ChainId, chainId: ChainId,
private readonly subgraphUrl: string = BALANCER_V2_SUBGRAPH_URL_BY_CHAIN[chainId], private readonly subgraphUrl: string = BALANCER_V2_SUBGRAPH_URL_BY_CHAIN[chainId]!,
private readonly maxPoolsFetched: number = BALANCER_MAX_POOLS_FETCHED, private readonly maxPoolsFetched: number = BALANCER_MAX_POOLS_FETCHED,
private readonly _topPoolsFetched: number = BALANCER_TOP_POOLS_FETCHED, private readonly _topPoolsFetched: number = BALANCER_TOP_POOLS_FETCHED,
private readonly _warningLogger: LogFunction = DEFAULT_WARNING_LOGGER, private readonly _warningLogger: LogFunction = DEFAULT_WARNING_LOGGER,

View File

@ -0,0 +1,190 @@
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';
// tslint:disable-next-line:custom-no-magic-numbers
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<void> {
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<BalancerSwaps> {
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;
}

View File

@ -0,0 +1,91 @@
import { BalancerSwaps } from '../types';
import { ONE_HOUR_IN_SECONDS, ONE_SECOND_MS } from '../constants';
export interface CacheValue {
expiresAt: number;
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.
* 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<BalancerSwaps> {
const timeout = new Promise<BalancerSwaps>(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<BalancerSwaps> {
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<BalancerSwaps>;
}

View File

@ -0,0 +1,114 @@
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<SubgraphPoolBase[]> {
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 [];
}
}
}

View File

@ -5,9 +5,8 @@ import { SamplerOverrides } from '../../types';
import { ERC20BridgeSamplerContract } from '../../wrappers'; import { ERC20BridgeSamplerContract } from '../../wrappers';
import { BancorService } from './bancor_service'; import { BancorService } from './bancor_service';
import { PoolsCache } from './pools_cache'; import { PoolsCacheMap, SamplerOperations } from './sampler_operations';
import { SamplerOperations } from './sampler_operations'; import { BatchedOperation, LiquidityProviderRegistry, TokenAdjacencyGraph } from './types';
import { BatchedOperation, ERC20BridgeSource, LiquidityProviderRegistry, TokenAdjacencyGraph } from './types';
/** /**
* Generate sample amounts up to `maxFillAmount`. * Generate sample amounts up to `maxFillAmount`.
@ -37,7 +36,7 @@ export class DexOrderSampler extends SamplerOperations {
public readonly chainId: ChainId, public readonly chainId: ChainId,
_samplerContract: ERC20BridgeSamplerContract, _samplerContract: ERC20BridgeSamplerContract,
private readonly _samplerOverrides?: SamplerOverrides, private readonly _samplerOverrides?: SamplerOverrides,
poolsCaches?: { [key in ERC20BridgeSource]: PoolsCache }, poolsCaches?: PoolsCacheMap,
tokenAdjacencyGraph?: TokenAdjacencyGraph, tokenAdjacencyGraph?: TokenAdjacencyGraph,
liquidityProviderRegistry?: LiquidityProviderRegistry, liquidityProviderRegistry?: LiquidityProviderRegistry,
bancorServiceFn: () => Promise<BancorService | undefined> = async () => undefined, bancorServiceFn: () => Promise<BancorService | undefined> = async () => undefined,

View File

@ -51,6 +51,7 @@ import { getGeistInfoForPair } from './geist_utils';
import { getLiquidityProvidersForPair } from './liquidity_provider_utils'; import { getLiquidityProvidersForPair } from './liquidity_provider_utils';
import { getIntermediateTokens } from './multihop_utils'; import { getIntermediateTokens } from './multihop_utils';
import { BalancerPoolsCache, BalancerV2PoolsCache, CreamPoolsCache, PoolsCache } 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 { SamplerContractOperation } from './sampler_contract_operation';
import { SamplerNoOperation } from './sampler_no_operation'; import { SamplerNoOperation } from './sampler_no_operation';
import { SourceFilters } from './source_filters'; import { SourceFilters } from './source_filters';
@ -58,6 +59,8 @@ import {
AaveV2FillData, AaveV2FillData,
AaveV2Info, AaveV2Info,
BalancerFillData, BalancerFillData,
BalancerSwapInfo,
BalancerV2BatchSwapFillData,
BalancerV2FillData, BalancerV2FillData,
BalancerV2PoolInfo, BalancerV2PoolInfo,
BancorFillData, BancorFillData,
@ -103,6 +106,10 @@ export const TWO_HOP_SOURCE_FILTERS = SourceFilters.all().exclude([
*/ */
export const BATCH_SOURCE_FILTERS = SourceFilters.all().exclude([ERC20BridgeSource.MultiHop, ERC20BridgeSource.Native]); export const BATCH_SOURCE_FILTERS = SourceFilters.all().exclude([ERC20BridgeSource.MultiHop, ERC20BridgeSource.Native]);
export type PoolsCacheMap = { [key in Exclude<SourcesWithPoolsCache, ERC20BridgeSource.BalancerV2>]: PoolsCache } & {
[ERC20BridgeSource.BalancerV2]: BalancerV2SwapInfoCache;
};
// tslint:disable:no-inferred-empty-object-type no-unbound-method // tslint:disable:no-inferred-empty-object-type no-unbound-method
/** /**
@ -111,7 +118,7 @@ export const BATCH_SOURCE_FILTERS = SourceFilters.all().exclude([ERC20BridgeSour
*/ */
export class SamplerOperations { export class SamplerOperations {
public readonly liquidityProviderRegistry: LiquidityProviderRegistry; public readonly liquidityProviderRegistry: LiquidityProviderRegistry;
public readonly poolsCaches: { [key in SourcesWithPoolsCache]: PoolsCache }; public readonly poolsCaches: PoolsCacheMap;
public readonly aaveReservesCache: AaveV2ReservesCache | undefined; public readonly aaveReservesCache: AaveV2ReservesCache | undefined;
public readonly compoundCTokenCache: CompoundCTokenCache | undefined; public readonly compoundCTokenCache: CompoundCTokenCache | undefined;
protected _bancorService?: BancorService; protected _bancorService?: BancorService;
@ -126,7 +133,7 @@ export class SamplerOperations {
constructor( constructor(
public readonly chainId: ChainId, public readonly chainId: ChainId,
protected readonly _samplerContract: ERC20BridgeSamplerContract, protected readonly _samplerContract: ERC20BridgeSamplerContract,
poolsCaches?: { [key in SourcesWithPoolsCache]: PoolsCache }, poolsCaches?: PoolsCacheMap,
protected readonly tokenAdjacencyGraph: TokenAdjacencyGraph = { default: [] }, protected readonly tokenAdjacencyGraph: TokenAdjacencyGraph = { default: [] },
liquidityProviderRegistry: LiquidityProviderRegistry = {}, liquidityProviderRegistry: LiquidityProviderRegistry = {},
bancorServiceFn: () => Promise<BancorService | undefined> = async () => undefined, bancorServiceFn: () => Promise<BancorService | undefined> = async () => undefined,
@ -138,13 +145,13 @@ export class SamplerOperations {
this.poolsCaches = poolsCaches this.poolsCaches = poolsCaches
? poolsCaches ? poolsCaches
: { : {
[ERC20BridgeSource.BalancerV2]: new BalancerV2PoolsCache(chainId),
[ERC20BridgeSource.Beethovenx]: new BalancerV2PoolsCache( [ERC20BridgeSource.Beethovenx]: new BalancerV2PoolsCache(
chainId, chainId,
BEETHOVEN_X_SUBGRAPH_URL_BY_CHAIN[chainId], BEETHOVEN_X_SUBGRAPH_URL_BY_CHAIN[chainId],
), ),
[ERC20BridgeSource.Balancer]: new BalancerPoolsCache(), [ERC20BridgeSource.Balancer]: new BalancerPoolsCache(),
[ERC20BridgeSource.Cream]: new CreamPoolsCache(), [ERC20BridgeSource.Cream]: new CreamPoolsCache(),
[ERC20BridgeSource.BalancerV2]: new BalancerV2SwapInfoCache(chainId),
}; };
const aaveSubgraphUrl = AAVE_V2_SUBGRAPH_URL_BY_CHAIN_ID[chainId]; const aaveSubgraphUrl = AAVE_V2_SUBGRAPH_URL_BY_CHAIN_ID[chainId];
@ -564,6 +571,49 @@ export class SamplerOperations {
}); });
} }
public getBalancerV2MulthopSellQuotes(
vault: string,
quoteSwaps: BalancerSwapInfo, // Should always be sell swap steps.
fillSwaps: BalancerSwapInfo, // Should always be sell swap steps.
takerFillAmounts: BigNumber[],
source: ERC20BridgeSource,
): SourceQuoteOperation<BalancerV2BatchSwapFillData> {
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 getBalancerV2MulthopBuyQuotes(
vault: string,
quoteSwaps: BalancerSwapInfo, // Should always be buy swap steps.
fillSwaps: BalancerSwapInfo, // Should always be a sell quote.
makerFillAmounts: BigNumber[],
source: ERC20BridgeSource,
): SourceQuoteOperation<BalancerV2BatchSwapFillData> {
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( public getBalancerV2SellQuotes(
poolInfo: BalancerV2PoolInfo, poolInfo: BalancerV2PoolInfo,
makerToken: string, makerToken: string,
@ -1448,15 +1498,23 @@ export class SamplerOperations {
ERC20BridgeSource.Balancer, ERC20BridgeSource.Balancer,
), ),
); );
case ERC20BridgeSource.BalancerV2: case ERC20BridgeSource.BalancerV2: {
case ERC20BridgeSource.Beethovenx: const swaps = this.poolsCaches[source].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.getBalancerV2MulthopSellQuotes(vault, swapInfo, swapInfo, takerFillAmounts, source),
);
}
case ERC20BridgeSource.Beethovenx: {
const poolIds = const poolIds =
this.poolsCaches[source].getCachedPoolAddressesForPair(takerToken, makerToken) || []; this.poolsCaches[source].getCachedPoolAddressesForPair(takerToken, makerToken) || [];
const vault = const vault = BEETHOVEN_X_VAULT_ADDRESS_BY_CHAIN[this.chainId];
source === ERC20BridgeSource.BalancerV2
? BALANCER_V2_VAULT_ADDRESS_BY_CHAIN[this.chainId]
: BEETHOVEN_X_VAULT_ADDRESS_BY_CHAIN[this.chainId];
if (vault === NULL_ADDRESS) { if (vault === NULL_ADDRESS) {
return []; return [];
} }
@ -1469,6 +1527,7 @@ export class SamplerOperations {
source, source,
), ),
); );
}
case ERC20BridgeSource.Cream: case ERC20BridgeSource.Cream:
return ( return (
this.poolsCaches[ERC20BridgeSource.Cream].getCachedPoolAddressesForPair( this.poolsCaches[ERC20BridgeSource.Cream].getCachedPoolAddressesForPair(
@ -1762,15 +1821,29 @@ export class SamplerOperations {
ERC20BridgeSource.Balancer, ERC20BridgeSource.Balancer,
), ),
); );
case ERC20BridgeSource.BalancerV2: case ERC20BridgeSource.BalancerV2: {
case ERC20BridgeSource.Beethovenx: const swaps = this.poolsCaches[source].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.getBalancerV2MulthopBuyQuotes(
vault,
quoteSwapInfo,
swaps.swapInfoExactIn[i],
makerFillAmounts,
source,
),
);
}
case ERC20BridgeSource.Beethovenx: {
const poolIds = const poolIds =
this.poolsCaches[source].getCachedPoolAddressesForPair(takerToken, makerToken) || []; this.poolsCaches[source].getCachedPoolAddressesForPair(takerToken, makerToken) || [];
const vault = const vault = BEETHOVEN_X_VAULT_ADDRESS_BY_CHAIN[this.chainId];
source === ERC20BridgeSource.BalancerV2
? BALANCER_V2_VAULT_ADDRESS_BY_CHAIN[this.chainId]
: BEETHOVEN_X_VAULT_ADDRESS_BY_CHAIN[this.chainId];
if (vault === NULL_ADDRESS) { if (vault === NULL_ADDRESS) {
return []; return [];
} }
@ -1783,6 +1856,7 @@ export class SamplerOperations {
source, source,
), ),
); );
}
case ERC20BridgeSource.Cream: case ERC20BridgeSource.Cream:
return ( return (
this.poolsCaches[ERC20BridgeSource.Cream].getCachedPoolAddressesForPair( this.poolsCaches[ERC20BridgeSource.Cream].getCachedPoolAddressesForPair(

View File

@ -209,6 +209,23 @@ export interface CurveFillData extends FillData {
pool: CurveInfo; 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 { export interface BalancerFillData extends FillData {
poolAddress: string; poolAddress: string;
} }
@ -218,6 +235,12 @@ export interface BalancerV2FillData extends FillData {
poolId: string; poolId: string;
} }
export interface BalancerV2BatchSwapFillData extends FillData {
vault: string;
swapSteps: BalancerBatchSwapStep[];
assets: string[];
}
export interface UniswapV2FillData extends FillData { export interface UniswapV2FillData extends FillData {
tokenAddressPath: string[]; tokenAddressPath: string[];
router: string; router: string;

View File

@ -8,16 +8,18 @@ import { ContractArtifact } from 'ethereum-types';
import * as ApproximateBuys from '../test/generated-artifacts/ApproximateBuys.json'; import * as ApproximateBuys from '../test/generated-artifacts/ApproximateBuys.json';
import * as BalanceChecker from '../test/generated-artifacts/BalanceChecker.json'; import * as BalanceChecker from '../test/generated-artifacts/BalanceChecker.json';
import * as BalancerSampler from '../test/generated-artifacts/BalancerSampler.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 BalancerV2Sampler from '../test/generated-artifacts/BalancerV2Sampler.json';
import * as BancorSampler from '../test/generated-artifacts/BancorSampler.json'; import * as BancorSampler from '../test/generated-artifacts/BancorSampler.json';
import * as CompoundSampler from '../test/generated-artifacts/CompoundSampler.json'; import * as CompoundSampler from '../test/generated-artifacts/CompoundSampler.json';
import * as CurveSampler from '../test/generated-artifacts/CurveSampler.json'; import * as CurveSampler from '../test/generated-artifacts/CurveSampler.json';
import * as DODOSampler from '../test/generated-artifacts/DODOSampler.json'; import * as DODOSampler from '../test/generated-artifacts/DODOSampler.json';
import * as DODOV2Sampler from '../test/generated-artifacts/DODOV2Sampler.json'; import * as DODOV2Sampler from '../test/generated-artifacts/DODOV2Sampler.json';
import * as DummyLiquidityProvider from '../test/generated-artifacts/DummyLiquidityProvider.json';
import * as ERC20BridgeSampler from '../test/generated-artifacts/ERC20BridgeSampler.json'; import * as ERC20BridgeSampler from '../test/generated-artifacts/ERC20BridgeSampler.json';
import * as FakeTaker from '../test/generated-artifacts/FakeTaker.json'; import * as FakeTaker from '../test/generated-artifacts/FakeTaker.json';
import * as IBalancer from '../test/generated-artifacts/IBalancer.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 IBancor from '../test/generated-artifacts/IBancor.json';
import * as ICurve from '../test/generated-artifacts/ICurve.json'; import * as ICurve from '../test/generated-artifacts/ICurve.json';
import * as IKyberNetwork from '../test/generated-artifacts/IKyberNetwork.json'; import * as IKyberNetwork from '../test/generated-artifacts/IKyberNetwork.json';
@ -35,12 +37,10 @@ import * as LiquidityProviderSampler from '../test/generated-artifacts/Liquidity
import * as MakerPSMSampler from '../test/generated-artifacts/MakerPSMSampler.json'; import * as MakerPSMSampler from '../test/generated-artifacts/MakerPSMSampler.json';
import * as MooniswapSampler from '../test/generated-artifacts/MooniswapSampler.json'; import * as MooniswapSampler from '../test/generated-artifacts/MooniswapSampler.json';
import * as MStableSampler from '../test/generated-artifacts/MStableSampler.json'; import * as MStableSampler from '../test/generated-artifacts/MStableSampler.json';
import * as MultiBridgeSampler from '../test/generated-artifacts/MultiBridgeSampler.json';
import * as NativeOrderSampler from '../test/generated-artifacts/NativeOrderSampler.json'; import * as NativeOrderSampler from '../test/generated-artifacts/NativeOrderSampler.json';
import * as SamplerUtils from '../test/generated-artifacts/SamplerUtils.json'; import * as SamplerUtils from '../test/generated-artifacts/SamplerUtils.json';
import * as ShellSampler from '../test/generated-artifacts/ShellSampler.json'; import * as ShellSampler from '../test/generated-artifacts/ShellSampler.json';
import * as SmoothySampler from '../test/generated-artifacts/SmoothySampler.json'; import * as SmoothySampler from '../test/generated-artifacts/SmoothySampler.json';
import * as TestERC20BridgeSampler from '../test/generated-artifacts/TestERC20BridgeSampler.json';
import * as TestNativeOrderSampler from '../test/generated-artifacts/TestNativeOrderSampler.json'; import * as TestNativeOrderSampler from '../test/generated-artifacts/TestNativeOrderSampler.json';
import * as TwoHopSampler from '../test/generated-artifacts/TwoHopSampler.json'; import * as TwoHopSampler from '../test/generated-artifacts/TwoHopSampler.json';
import * as UniswapSampler from '../test/generated-artifacts/UniswapSampler.json'; import * as UniswapSampler from '../test/generated-artifacts/UniswapSampler.json';
@ -51,6 +51,8 @@ export const artifacts = {
ApproximateBuys: ApproximateBuys as ContractArtifact, ApproximateBuys: ApproximateBuys as ContractArtifact,
BalanceChecker: BalanceChecker as ContractArtifact, BalanceChecker: BalanceChecker as ContractArtifact,
BalancerSampler: BalancerSampler as ContractArtifact, BalancerSampler: BalancerSampler as ContractArtifact,
BalancerV2BatchSampler: BalancerV2BatchSampler as ContractArtifact,
BalancerV2Common: BalancerV2Common as ContractArtifact,
BalancerV2Sampler: BalancerV2Sampler as ContractArtifact, BalancerV2Sampler: BalancerV2Sampler as ContractArtifact,
BancorSampler: BancorSampler as ContractArtifact, BancorSampler: BancorSampler as ContractArtifact,
CompoundSampler: CompoundSampler as ContractArtifact, CompoundSampler: CompoundSampler as ContractArtifact,
@ -66,7 +68,6 @@ export const artifacts = {
MStableSampler: MStableSampler as ContractArtifact, MStableSampler: MStableSampler as ContractArtifact,
MakerPSMSampler: MakerPSMSampler as ContractArtifact, MakerPSMSampler: MakerPSMSampler as ContractArtifact,
MooniswapSampler: MooniswapSampler as ContractArtifact, MooniswapSampler: MooniswapSampler as ContractArtifact,
MultiBridgeSampler: MultiBridgeSampler as ContractArtifact,
NativeOrderSampler: NativeOrderSampler as ContractArtifact, NativeOrderSampler: NativeOrderSampler as ContractArtifact,
SamplerUtils: SamplerUtils as ContractArtifact, SamplerUtils: SamplerUtils as ContractArtifact,
ShellSampler: ShellSampler as ContractArtifact, ShellSampler: ShellSampler as ContractArtifact,
@ -77,6 +78,7 @@ export const artifacts = {
UniswapV3Sampler: UniswapV3Sampler as ContractArtifact, UniswapV3Sampler: UniswapV3Sampler as ContractArtifact,
UtilitySampler: UtilitySampler as ContractArtifact, UtilitySampler: UtilitySampler as ContractArtifact,
IBalancer: IBalancer as ContractArtifact, IBalancer: IBalancer as ContractArtifact,
IBalancerV2Vault: IBalancerV2Vault as ContractArtifact,
IBancor: IBancor as ContractArtifact, IBancor: IBancor as ContractArtifact,
ICurve: ICurve as ContractArtifact, ICurve: ICurve as ContractArtifact,
IKyberNetwork: IKyberNetwork as ContractArtifact, IKyberNetwork: IKyberNetwork as ContractArtifact,
@ -87,7 +89,5 @@ export const artifacts = {
ISmoothy: ISmoothy as ContractArtifact, ISmoothy: ISmoothy as ContractArtifact,
IUniswapExchangeQuotes: IUniswapExchangeQuotes as ContractArtifact, IUniswapExchangeQuotes: IUniswapExchangeQuotes as ContractArtifact,
IUniswapV2Router01: IUniswapV2Router01 as ContractArtifact, IUniswapV2Router01: IUniswapV2Router01 as ContractArtifact,
DummyLiquidityProvider: DummyLiquidityProvider as ContractArtifact,
TestERC20BridgeSampler: TestERC20BridgeSampler as ContractArtifact,
TestNativeOrderSampler: TestNativeOrderSampler as ContractArtifact, TestNativeOrderSampler: TestNativeOrderSampler as ContractArtifact,
}; };

View File

@ -6,16 +6,18 @@
export * from '../test/generated-wrappers/approximate_buys'; export * from '../test/generated-wrappers/approximate_buys';
export * from '../test/generated-wrappers/balance_checker'; export * from '../test/generated-wrappers/balance_checker';
export * from '../test/generated-wrappers/balancer_sampler'; 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/balancer_v2_sampler';
export * from '../test/generated-wrappers/bancor_sampler'; export * from '../test/generated-wrappers/bancor_sampler';
export * from '../test/generated-wrappers/compound_sampler'; export * from '../test/generated-wrappers/compound_sampler';
export * from '../test/generated-wrappers/curve_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_sampler';
export * from '../test/generated-wrappers/d_o_d_o_v2_sampler'; export * from '../test/generated-wrappers/d_o_d_o_v2_sampler';
export * from '../test/generated-wrappers/dummy_liquidity_provider';
export * from '../test/generated-wrappers/erc20_bridge_sampler'; export * from '../test/generated-wrappers/erc20_bridge_sampler';
export * from '../test/generated-wrappers/fake_taker'; export * from '../test/generated-wrappers/fake_taker';
export * from '../test/generated-wrappers/i_balancer'; 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';
export * from '../test/generated-wrappers/i_curve'; export * from '../test/generated-wrappers/i_curve';
export * from '../test/generated-wrappers/i_kyber_network'; export * from '../test/generated-wrappers/i_kyber_network';
@ -33,12 +35,10 @@ export * from '../test/generated-wrappers/liquidity_provider_sampler';
export * from '../test/generated-wrappers/m_stable_sampler'; export * from '../test/generated-wrappers/m_stable_sampler';
export * from '../test/generated-wrappers/maker_p_s_m_sampler'; export * from '../test/generated-wrappers/maker_p_s_m_sampler';
export * from '../test/generated-wrappers/mooniswap_sampler'; export * from '../test/generated-wrappers/mooniswap_sampler';
export * from '../test/generated-wrappers/multi_bridge_sampler';
export * from '../test/generated-wrappers/native_order_sampler'; export * from '../test/generated-wrappers/native_order_sampler';
export * from '../test/generated-wrappers/sampler_utils'; export * from '../test/generated-wrappers/sampler_utils';
export * from '../test/generated-wrappers/shell_sampler'; export * from '../test/generated-wrappers/shell_sampler';
export * from '../test/generated-wrappers/smoothy_sampler'; export * from '../test/generated-wrappers/smoothy_sampler';
export * from '../test/generated-wrappers/test_erc20_bridge_sampler';
export * from '../test/generated-wrappers/test_native_order_sampler'; export * from '../test/generated-wrappers/test_native_order_sampler';
export * from '../test/generated-wrappers/two_hop_sampler'; export * from '../test/generated-wrappers/two_hop_sampler';
export * from '../test/generated-wrappers/uniswap_sampler'; export * from '../test/generated-wrappers/uniswap_sampler';

View File

@ -9,16 +9,18 @@
"test/generated-artifacts/ApproximateBuys.json", "test/generated-artifacts/ApproximateBuys.json",
"test/generated-artifacts/BalanceChecker.json", "test/generated-artifacts/BalanceChecker.json",
"test/generated-artifacts/BalancerSampler.json", "test/generated-artifacts/BalancerSampler.json",
"test/generated-artifacts/BalancerV2BatchSampler.json",
"test/generated-artifacts/BalancerV2Common.json",
"test/generated-artifacts/BalancerV2Sampler.json", "test/generated-artifacts/BalancerV2Sampler.json",
"test/generated-artifacts/BancorSampler.json", "test/generated-artifacts/BancorSampler.json",
"test/generated-artifacts/CompoundSampler.json", "test/generated-artifacts/CompoundSampler.json",
"test/generated-artifacts/CurveSampler.json", "test/generated-artifacts/CurveSampler.json",
"test/generated-artifacts/DODOSampler.json", "test/generated-artifacts/DODOSampler.json",
"test/generated-artifacts/DODOV2Sampler.json", "test/generated-artifacts/DODOV2Sampler.json",
"test/generated-artifacts/DummyLiquidityProvider.json",
"test/generated-artifacts/ERC20BridgeSampler.json", "test/generated-artifacts/ERC20BridgeSampler.json",
"test/generated-artifacts/FakeTaker.json", "test/generated-artifacts/FakeTaker.json",
"test/generated-artifacts/IBalancer.json", "test/generated-artifacts/IBalancer.json",
"test/generated-artifacts/IBalancerV2Vault.json",
"test/generated-artifacts/IBancor.json", "test/generated-artifacts/IBancor.json",
"test/generated-artifacts/ICurve.json", "test/generated-artifacts/ICurve.json",
"test/generated-artifacts/IKyberNetwork.json", "test/generated-artifacts/IKyberNetwork.json",
@ -36,12 +38,10 @@
"test/generated-artifacts/MStableSampler.json", "test/generated-artifacts/MStableSampler.json",
"test/generated-artifacts/MakerPSMSampler.json", "test/generated-artifacts/MakerPSMSampler.json",
"test/generated-artifacts/MooniswapSampler.json", "test/generated-artifacts/MooniswapSampler.json",
"test/generated-artifacts/MultiBridgeSampler.json",
"test/generated-artifacts/NativeOrderSampler.json", "test/generated-artifacts/NativeOrderSampler.json",
"test/generated-artifacts/SamplerUtils.json", "test/generated-artifacts/SamplerUtils.json",
"test/generated-artifacts/ShellSampler.json", "test/generated-artifacts/ShellSampler.json",
"test/generated-artifacts/SmoothySampler.json", "test/generated-artifacts/SmoothySampler.json",
"test/generated-artifacts/TestERC20BridgeSampler.json",
"test/generated-artifacts/TestNativeOrderSampler.json", "test/generated-artifacts/TestNativeOrderSampler.json",
"test/generated-artifacts/TwoHopSampler.json", "test/generated-artifacts/TwoHopSampler.json",
"test/generated-artifacts/UniswapSampler.json", "test/generated-artifacts/UniswapSampler.json",

View File

@ -1,4 +1,13 @@
[ [
{
"version": "6.13.0",
"changes": [
{
"note": "Redeploy FQT on mainnet and polygon",
"pr": 462
}
]
},
{ {
"timestamp": 1648739346, "timestamp": 1648739346,
"version": "6.12.1", "version": "6.12.1",

View File

@ -37,7 +37,7 @@
"wethTransformer": "0xb2bc06a4efb20fc6553a69dbfa49b7be938034a7", "wethTransformer": "0xb2bc06a4efb20fc6553a69dbfa49b7be938034a7",
"payTakerTransformer": "0x4638a7ebe75b911b995d0ec73a81e4f85f41f24e", "payTakerTransformer": "0x4638a7ebe75b911b995d0ec73a81e4f85f41f24e",
"affiliateFeeTransformer": "0xda6d9fc5998f550a094585cf9171f0e8ee3ac59f", "affiliateFeeTransformer": "0xda6d9fc5998f550a094585cf9171f0e8ee3ac59f",
"fillQuoteTransformer": "0xb4fa284689c9784a60d840eb136bb16c5246191f", "fillQuoteTransformer": "0xadbe39f2988a8be1c1120f05e28cc888b150c8a6",
"positiveSlippageFeeTransformer": "0xa9416ce1dbde8d331210c07b5c253d94ee4cc3fd" "positiveSlippageFeeTransformer": "0xa9416ce1dbde8d331210c07b5c253d94ee4cc3fd"
} }
}, },
@ -289,7 +289,7 @@
"wethTransformer": "0xe309d011cc6f189a3e8dcba85922715a019fed38", "wethTransformer": "0xe309d011cc6f189a3e8dcba85922715a019fed38",
"payTakerTransformer": "0x5ba7b9be86cda01cfbf56e0fb97184783be9dda1", "payTakerTransformer": "0x5ba7b9be86cda01cfbf56e0fb97184783be9dda1",
"affiliateFeeTransformer": "0xbed27284b42e5684e987169cf1da09c5d6c49fa8", "affiliateFeeTransformer": "0xbed27284b42e5684e987169cf1da09c5d6c49fa8",
"fillQuoteTransformer": "0xd3afdf4a8ea9183e76c9c2306cda03ea4afffea5", "fillQuoteTransformer": "0xd4a518760030dae1adbde9496f8a3b478e83932a",
"positiveSlippageFeeTransformer": "0x4cd8f1c0df4d40fcc1e073845d5f6f4ed5cc8dab" "positiveSlippageFeeTransformer": "0x4cd8f1c0df4d40fcc1e073845d5f6f4ed5cc8dab"
} }
}, },

View File

@ -1,4 +1,13 @@
[ [
{
"version": "11.12.0",
"changes": [
{
"note": "Add `BalancerV2Batch` to `BridgeProtocol` enum",
"pr": 462
}
]
},
{ {
"timestamp": 1648739346, "timestamp": 1648739346,
"version": "1.11.2", "version": "1.11.2",

View File

@ -134,6 +134,7 @@ export enum BridgeProtocol {
Clipper, // Not used: Clipper is now using PLP interface Clipper, // Not used: Clipper is now using PLP interface
AaveV2, AaveV2,
Compound, Compound,
BalancerV2Batch,
} }
// tslint:enable: enum-naming // tslint:enable: enum-naming

View File

@ -1384,6 +1384,17 @@
dependencies: dependencies:
regenerator-runtime "^0.13.4" regenerator-runtime "^0.13.4"
"@balancer-labs/sdk@^0.1.6":
version "0.1.6"
resolved "https://registry.yarnpkg.com/@balancer-labs/sdk/-/sdk-0.1.6.tgz#1a6f0aacfada7b0afbdf02259ef40ed37d3ecbcb"
integrity sha512-r9s7Y2XJks+8V53kqwaqHDAETipgFSEQxI7TFHYigoOtWp/sUaZnlu0kDMv3NuDvya0+t9gp5a0VxbztLwcn+g==
dependencies:
"@balancer-labs/sor" "^4.0.0-beta.2"
axios "^0.24.0"
graphql "^15.6.1"
graphql-request "^3.5.0"
lodash "^4.17.21"
"@balancer-labs/sor@0.3.2": "@balancer-labs/sor@0.3.2":
version "0.3.2" version "0.3.2"
resolved "https://registry.yarnpkg.com/@balancer-labs/sor/-/sor-0.3.2.tgz#b05c63a07031c2ea13ed0d2670f5105cfaa40523" resolved "https://registry.yarnpkg.com/@balancer-labs/sor/-/sor-0.3.2.tgz#b05c63a07031c2ea13ed0d2670f5105cfaa40523"
@ -1393,6 +1404,13 @@
isomorphic-fetch "^2.2.1" isomorphic-fetch "^2.2.1"
typescript "^3.8.3" typescript "^3.8.3"
"@balancer-labs/sor@^4.0.0-beta.2":
version "4.0.0-beta.2"
resolved "https://registry.yarnpkg.com/@balancer-labs/sor/-/sor-4.0.0-beta.2.tgz#17ee901c7434f9a5702653488b1a3ec6e1f926f1"
integrity sha512-ylDxsMDKpoynOQIH4PhucYc7bkXddjL6GRFCF2BwnQ4Yoy7vBOT7S0zJvIkKuUG6MSUdoTBaAtWckxXBJiNxyA==
dependencies:
isomorphic-fetch "^2.2.1"
"@bancor/sdk@0.2.9": "@bancor/sdk@0.2.9":
version "0.2.9" version "0.2.9"
resolved "https://registry.yarnpkg.com/@bancor/sdk/-/sdk-0.2.9.tgz#2e4c168dc9d667709e3ed85eac3b15362c5676d8" resolved "https://registry.yarnpkg.com/@bancor/sdk/-/sdk-0.2.9.tgz#2e4c168dc9d667709e3ed85eac3b15362c5676d8"
@ -3252,6 +3270,13 @@ axios@^0.21.1:
dependencies: dependencies:
follow-redirects "^1.10.0" follow-redirects "^1.10.0"
axios@^0.24.0:
version "0.24.0"
resolved "https://registry.yarnpkg.com/axios/-/axios-0.24.0.tgz#804e6fa1e4b9c5288501dd9dff56a7a0940d20d6"
integrity sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA==
dependencies:
follow-redirects "^1.14.4"
babel-code-frame@^6.22.0, babel-code-frame@^6.26.0: babel-code-frame@^6.22.0, babel-code-frame@^6.26.0:
version "6.26.0" version "6.26.0"
resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b" resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b"
@ -6515,7 +6540,7 @@ follow-redirects@^1.10.0:
version "1.13.3" version "1.13.3"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.13.3.tgz#e5598ad50174c1bc4e872301e82ac2cd97f90267" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.13.3.tgz#e5598ad50174c1bc4e872301e82ac2cd97f90267"
follow-redirects@^1.12.1: follow-redirects@^1.12.1, follow-redirects@^1.14.4:
version "1.14.9" version "1.14.9"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.9.tgz#dd4ea157de7bfaf9ea9b3fbd85aa16951f78d8d7" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.9.tgz#dd4ea157de7bfaf9ea9b3fbd85aa16951f78d8d7"
integrity sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w== integrity sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w==
@ -7031,10 +7056,24 @@ graphql-request@^3.4.0:
extract-files "^9.0.0" extract-files "^9.0.0"
form-data "^3.0.0" form-data "^3.0.0"
graphql-request@^3.5.0:
version "3.7.0"
resolved "https://registry.yarnpkg.com/graphql-request/-/graphql-request-3.7.0.tgz#c7406e537084f8b9788541e3e6704340ca13055b"
integrity sha512-dw5PxHCgBneN2DDNqpWu8QkbbJ07oOziy8z+bK/TAXufsOLaETuVO4GkXrbs0WjhdKhBMN3BkpN/RIvUHkmNUQ==
dependencies:
cross-fetch "^3.0.6"
extract-files "^9.0.0"
form-data "^3.0.0"
graphql@^15.4.0: graphql@^15.4.0:
version "15.5.0" version "15.5.0"
resolved "https://registry.yarnpkg.com/graphql/-/graphql-15.5.0.tgz#39d19494dbe69d1ea719915b578bf920344a69d5" resolved "https://registry.yarnpkg.com/graphql/-/graphql-15.5.0.tgz#39d19494dbe69d1ea719915b578bf920344a69d5"
graphql@^15.6.1:
version "15.8.0"
resolved "https://registry.yarnpkg.com/graphql/-/graphql-15.8.0.tgz#33410e96b012fa3bdb1091cc99a94769db212b38"
integrity sha512-5gghUc24tP9HRznNpV2+FIoq3xKkj5dTQqf4v0CpdPbFVwFkWoxOM+o+2OC9ZSvjEMTjfmG9QT+gcvggTwW1zw==
growl@1.10.5: growl@1.10.5:
version "1.10.5" version "1.10.5"
resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e" resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e"
@ -8393,6 +8432,11 @@ lodash@4.17.20, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.14, lodash@^4.17.
version "4.17.20" version "4.17.20"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52"
lodash@^4.17.21:
version "4.17.21"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
log-driver@^1.2.7: log-driver@^1.2.7:
version "1.2.7" version "1.2.7"
resolved "https://registry.yarnpkg.com/log-driver/-/log-driver-1.2.7.tgz#63b95021f0702fedfa2c9bb0a24e7797d71871d8" resolved "https://registry.yarnpkg.com/log-driver/-/log-driver-1.2.7.tgz#63b95021f0702fedfa2c9bb0a24e7797d71871d8"
@ -11223,6 +11267,13 @@ solidity-parser-antlr@^0.4.2:
version "0.4.11" version "0.4.11"
resolved "https://registry.yarnpkg.com/solidity-parser-antlr/-/solidity-parser-antlr-0.4.11.tgz#af43e1f13b3b88309a875455f5d6e565b05ee5f1" resolved "https://registry.yarnpkg.com/solidity-parser-antlr/-/solidity-parser-antlr-0.4.11.tgz#af43e1f13b3b88309a875455f5d6e565b05ee5f1"
"sorV2@npm:@balancer-labs/sor":
version "4.0.0-beta.1"
resolved "https://registry.yarnpkg.com/@balancer-labs/sor/-/sor-4.0.0-beta.1.tgz#fb8b3f2d9bb5cec5c79446e0062aab7cdfcabccb"
integrity sha512-L3eMBRA51egMNKHkLktOr3sNJuqgoz24AfJkpzU4w1I66m9HlOPY/E3FgYKWO+1cXJ2sQZWDH3pEjnWMRnNbNg==
dependencies:
isomorphic-fetch "^2.2.1"
sort-keys@^2.0.0: sort-keys@^2.0.0:
version "2.0.0" version "2.0.0"
resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-2.0.0.tgz#658535584861ec97d730d6cf41822e1f56684128" resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-2.0.0.tgz#658535584861ec97d730d6cf41822e1f56684128"