feat: deploy interest tokens (#321)
* feat: Aave aToken deposit/withdrawal [TKR-111] (#293) * feat: AaveV2 deposit/withdrawal integration WIP * feat: add basic Aave Reserves cache with data from subgraphs WIP * feat: hook up Aave Reserves integration * fix: set allowance before trade & use ERC20 token interface * refactor: pass aToken to mixin to avoid lookup * fix: migrate from swap/revert to normal sampling * fix: Aave gas estimate & refactor to clean up code * feat: Create a sampler no operation type and make AaveV2Sampler a no-op * fix: Clipper merge conflict resolution issues * fix: don't fetch unnecessary Aave pool data & clean up code * chore: Add changelog entries * feat: cToken deposit/withdrawal [TKR-222] (#294) * feat: first stab at a CompoundSampler implementation * feat: MixinCompound implementation WIP * feat: Compound integration with cache WIP * fix: decimals scaling in CompoundSampler * feat: handle minting and redeeming of cETH * fix: adjust Compound gas schedule * refactor: clean up code and add comments in cToken cache * fix: MixinCompound check allowance on WETH withdrawal & fix indentation * fix: address review comments and clean up code * chore: add changelog entries * feat: enable AaveV2 on Avalanche * chore: add freshly deployed FQT on Polygon, Avalanche * fix: temporarily disable on Ethereum mainnet until we redeploy EP * fix: address PR comments and update changelogs * fix: correct contract-addresses changelog note
This commit is contained in:
parent
880a9c3da0
commit
9615570dc6
@ -1,4 +1,13 @@
|
|||||||
[
|
[
|
||||||
|
{
|
||||||
|
"version": "0.30.0",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Add `AaveV2` and `Compound` deposit/withdrawal liquidity source",
|
||||||
|
"pr": 321
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"timestamp": 1637102971,
|
"timestamp": 1637102971,
|
||||||
"version": "0.29.5",
|
"version": "0.29.5",
|
||||||
|
@ -22,10 +22,12 @@ pragma experimental ABIEncoderV2;
|
|||||||
|
|
||||||
import "./IBridgeAdapter.sol";
|
import "./IBridgeAdapter.sol";
|
||||||
import "./BridgeProtocols.sol";
|
import "./BridgeProtocols.sol";
|
||||||
|
import "./mixins/MixinAaveV2.sol";
|
||||||
import "./mixins/MixinBalancer.sol";
|
import "./mixins/MixinBalancer.sol";
|
||||||
import "./mixins/MixinBalancerV2.sol";
|
import "./mixins/MixinBalancerV2.sol";
|
||||||
import "./mixins/MixinBancor.sol";
|
import "./mixins/MixinBancor.sol";
|
||||||
import "./mixins/MixinCoFiX.sol";
|
import "./mixins/MixinCoFiX.sol";
|
||||||
|
import "./mixins/MixinCompound.sol";
|
||||||
import "./mixins/MixinCurve.sol";
|
import "./mixins/MixinCurve.sol";
|
||||||
import "./mixins/MixinCurveV2.sol";
|
import "./mixins/MixinCurveV2.sol";
|
||||||
import "./mixins/MixinCryptoCom.sol";
|
import "./mixins/MixinCryptoCom.sol";
|
||||||
@ -47,10 +49,12 @@ import "./mixins/MixinZeroExBridge.sol";
|
|||||||
|
|
||||||
contract BridgeAdapter is
|
contract BridgeAdapter is
|
||||||
IBridgeAdapter,
|
IBridgeAdapter,
|
||||||
|
MixinAaveV2,
|
||||||
MixinBalancer,
|
MixinBalancer,
|
||||||
MixinBalancerV2,
|
MixinBalancerV2,
|
||||||
MixinBancor,
|
MixinBancor,
|
||||||
MixinCoFiX,
|
MixinCoFiX,
|
||||||
|
MixinCompound,
|
||||||
MixinCurve,
|
MixinCurve,
|
||||||
MixinCurveV2,
|
MixinCurveV2,
|
||||||
MixinCryptoCom,
|
MixinCryptoCom,
|
||||||
@ -72,10 +76,12 @@ contract BridgeAdapter is
|
|||||||
{
|
{
|
||||||
constructor(IEtherTokenV06 weth)
|
constructor(IEtherTokenV06 weth)
|
||||||
public
|
public
|
||||||
|
MixinAaveV2()
|
||||||
MixinBalancer()
|
MixinBalancer()
|
||||||
MixinBalancerV2()
|
MixinBalancerV2()
|
||||||
MixinBancor(weth)
|
MixinBancor(weth)
|
||||||
MixinCoFiX()
|
MixinCoFiX()
|
||||||
|
MixinCompound(weth)
|
||||||
MixinCurve(weth)
|
MixinCurve(weth)
|
||||||
MixinCurveV2()
|
MixinCurveV2()
|
||||||
MixinCryptoCom()
|
MixinCryptoCom()
|
||||||
@ -245,6 +251,20 @@ contract BridgeAdapter is
|
|||||||
sellAmount,
|
sellAmount,
|
||||||
order.bridgeData
|
order.bridgeData
|
||||||
);
|
);
|
||||||
|
} else if (protocolId == BridgeProtocols.AAVEV2) {
|
||||||
|
boughtAmount = _tradeAaveV2(
|
||||||
|
sellToken,
|
||||||
|
buyToken,
|
||||||
|
sellAmount,
|
||||||
|
order.bridgeData
|
||||||
|
);
|
||||||
|
} else if (protocolId == BridgeProtocols.COMPOUND) {
|
||||||
|
boughtAmount = _tradeCompound(
|
||||||
|
sellToken,
|
||||||
|
buyToken,
|
||||||
|
sellAmount,
|
||||||
|
order.bridgeData
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
boughtAmount = _tradeZeroExBridge(
|
boughtAmount = _tradeZeroExBridge(
|
||||||
sellToken,
|
sellToken,
|
||||||
|
@ -50,4 +50,6 @@ library BridgeProtocols {
|
|||||||
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 COMPOUND = 24;
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,93 @@
|
|||||||
|
// 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.5;
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
import "@0x/contracts-erc20/contracts/src/v06/LibERC20TokenV06.sol";
|
||||||
|
import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
|
||||||
|
|
||||||
|
// Minimal Aave V2 LendingPool interface
|
||||||
|
interface ILendingPool {
|
||||||
|
/**
|
||||||
|
* @dev Deposits an `amount` of underlying asset into the reserve, receiving in return overlying aTokens.
|
||||||
|
* - E.g. User deposits 100 USDC and gets in return 100 aUSDC
|
||||||
|
* @param asset The address of the underlying asset to deposit
|
||||||
|
* @param amount The amount to be deposited
|
||||||
|
* @param onBehalfOf The address that will receive the aTokens, same as msg.sender if the user
|
||||||
|
* wants to receive them on his own wallet, or a different address if the beneficiary of aTokens
|
||||||
|
* is a different wallet
|
||||||
|
* @param referralCode Code used to register the integrator originating the operation, for potential rewards.
|
||||||
|
* 0 if the action is executed directly by the user, without any middle-man
|
||||||
|
**/
|
||||||
|
function deposit(
|
||||||
|
address asset,
|
||||||
|
uint256 amount,
|
||||||
|
address onBehalfOf,
|
||||||
|
uint16 referralCode
|
||||||
|
) external;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Withdraws an `amount` of underlying asset from the reserve, burning the equivalent aTokens owned
|
||||||
|
* E.g. User has 100 aUSDC, calls withdraw() and receives 100 USDC, burning the 100 aUSDC
|
||||||
|
* @param asset The address of the underlying asset to withdraw
|
||||||
|
* @param amount The underlying amount to be withdrawn
|
||||||
|
* - Send the value type(uint256).max in order to withdraw the whole aToken balance
|
||||||
|
* @param to Address that will receive the underlying, same as msg.sender if the user
|
||||||
|
* wants to receive it on his own wallet, or a different address if the beneficiary is a
|
||||||
|
* different wallet
|
||||||
|
* @return The final amount withdrawn
|
||||||
|
**/
|
||||||
|
function withdraw(
|
||||||
|
address asset,
|
||||||
|
uint256 amount,
|
||||||
|
address to
|
||||||
|
) external returns (uint256);
|
||||||
|
}
|
||||||
|
|
||||||
|
contract MixinAaveV2 {
|
||||||
|
using LibERC20TokenV06 for IERC20TokenV06;
|
||||||
|
|
||||||
|
function _tradeAaveV2(
|
||||||
|
IERC20TokenV06 sellToken,
|
||||||
|
IERC20TokenV06 buyToken,
|
||||||
|
uint256 sellAmount,
|
||||||
|
bytes memory bridgeData
|
||||||
|
)
|
||||||
|
internal
|
||||||
|
returns (uint256)
|
||||||
|
{
|
||||||
|
(ILendingPool lendingPool, address aToken) = abi.decode(bridgeData, (ILendingPool, address));
|
||||||
|
|
||||||
|
sellToken.approveIfBelow(
|
||||||
|
address(lendingPool),
|
||||||
|
sellAmount
|
||||||
|
);
|
||||||
|
|
||||||
|
if (address(buyToken) == aToken) {
|
||||||
|
lendingPool.deposit(address(sellToken), sellAmount, address(this), 0);
|
||||||
|
// 1:1 mapping token -> aToken and have the same number of decimals as the underlying token
|
||||||
|
return sellAmount;
|
||||||
|
} else if (address(sellToken) == aToken) {
|
||||||
|
return lendingPool.withdraw(address(buyToken), sellAmount, address(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
revert("MixinAaveV2/UNSUPPORTED_TOKEN_PAIR");
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,110 @@
|
|||||||
|
// 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.5;
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
import "@0x/contracts-erc20/contracts/src/v06/LibERC20TokenV06.sol";
|
||||||
|
import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
|
||||||
|
import "@0x/contracts-erc20/contracts/src/v06/IEtherTokenV06.sol";
|
||||||
|
import "@0x/contracts-utils/contracts/src/v06/LibSafeMathV06.sol";
|
||||||
|
|
||||||
|
|
||||||
|
/// @dev Minimal CToken interface
|
||||||
|
interface ICToken {
|
||||||
|
/// @dev deposits specified amount underlying tokens and mints cToken for the sender
|
||||||
|
/// @param mintAmountInUnderlying amount of underlying tokens to deposit to mint cTokens
|
||||||
|
/// @return status code of whether the mint was successful or not
|
||||||
|
function mint(uint256 mintAmountInUnderlying) external returns (uint256);
|
||||||
|
/// @dev redeems specified amount of cTokens and returns the underlying token to the sender
|
||||||
|
/// @param redeemTokensInCtokens amount of cTokens to redeem for underlying collateral
|
||||||
|
/// @return status code of whether the redemption was successful or not
|
||||||
|
function redeem(uint256 redeemTokensInCtokens) external returns (uint256);
|
||||||
|
}
|
||||||
|
/// @dev Minimal CEther interface
|
||||||
|
interface ICEther {
|
||||||
|
/// @dev deposits the amount of Ether sent as value and return mints cEther for the sender
|
||||||
|
function mint() payable external;
|
||||||
|
/// @dev redeems specified amount of cETH and returns the underlying ether to the sender
|
||||||
|
/// @dev redeemTokensInCEther amount of cETH to redeem for underlying ether
|
||||||
|
/// @return status code of whether the redemption was successful or not
|
||||||
|
function redeem(uint256 redeemTokensInCEther) external returns (uint256);
|
||||||
|
}
|
||||||
|
|
||||||
|
contract MixinCompound {
|
||||||
|
using LibERC20TokenV06 for IERC20TokenV06;
|
||||||
|
using LibSafeMathV06 for uint256;
|
||||||
|
|
||||||
|
IEtherTokenV06 private immutable WETH;
|
||||||
|
|
||||||
|
constructor(IEtherTokenV06 weth)
|
||||||
|
public
|
||||||
|
{
|
||||||
|
WETH = weth;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint256 constant private COMPOUND_SUCCESS_CODE = 0;
|
||||||
|
|
||||||
|
function _tradeCompound(
|
||||||
|
IERC20TokenV06 sellToken,
|
||||||
|
IERC20TokenV06 buyToken,
|
||||||
|
uint256 sellAmount,
|
||||||
|
bytes memory bridgeData
|
||||||
|
)
|
||||||
|
internal
|
||||||
|
returns (uint256)
|
||||||
|
{
|
||||||
|
(address cTokenAddress) = abi.decode(bridgeData, (address));
|
||||||
|
uint256 beforeBalance = buyToken.balanceOf(address(this));
|
||||||
|
|
||||||
|
if (address(buyToken) == cTokenAddress) {
|
||||||
|
if (address(sellToken) == address(WETH)) {
|
||||||
|
// ETH/WETH -> cETH
|
||||||
|
ICEther cETH = ICEther(cTokenAddress);
|
||||||
|
// Compound expects ETH to be sent with mint call
|
||||||
|
WETH.withdraw(sellAmount);
|
||||||
|
// NOTE: cETH mint will revert on failure instead of returning a status code
|
||||||
|
cETH.mint{value: sellAmount}();
|
||||||
|
} else {
|
||||||
|
sellToken.approveIfBelow(
|
||||||
|
cTokenAddress,
|
||||||
|
sellAmount
|
||||||
|
);
|
||||||
|
// Token -> cToken
|
||||||
|
ICToken cToken = ICToken(cTokenAddress);
|
||||||
|
require(cToken.mint(sellAmount) == COMPOUND_SUCCESS_CODE, "MixinCompound/FAILED_TO_MINT_CTOKEN");
|
||||||
|
}
|
||||||
|
} else if (address(sellToken) == cTokenAddress) {
|
||||||
|
if (address(buyToken) == address(WETH)) {
|
||||||
|
// cETH -> ETH/WETH
|
||||||
|
uint256 etherBalanceBefore = address(this).balance;
|
||||||
|
ICEther cETH = ICEther(cTokenAddress);
|
||||||
|
require(cETH.redeem(sellAmount) == COMPOUND_SUCCESS_CODE, "MixinCompound/FAILED_TO_REDEEM_CETHER");
|
||||||
|
uint256 etherBalanceAfter = address(this).balance;
|
||||||
|
uint256 receivedEtherBalance = etherBalanceAfter.safeSub(etherBalanceBefore);
|
||||||
|
WETH.deposit{value: receivedEtherBalance}();
|
||||||
|
} else {
|
||||||
|
ICToken cToken = ICToken(cTokenAddress);
|
||||||
|
require(cToken.redeem(sellAmount) == COMPOUND_SUCCESS_CODE, "MixinCompound/FAILED_TO_REDEEM_CTOKEN");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return buyToken.balanceOf(address(this)).safeSub(beforeBalance);
|
||||||
|
}
|
||||||
|
}
|
@ -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|FeeCollector|FeeCollectorController|FillQuoteTransformer|FixinCommon|FixinEIP712|FixinProtocolFees|FixinReentrancyGuard|FixinTokenSpender|FlashWallet|FullMigration|FundRecoveryFeature|IBatchFillNativeOrdersFeature|IBootstrapFeature|IBridgeAdapter|IERC20Bridge|IERC20Transformer|IFeature|IFlashWallet|IFundRecoveryFeature|ILiquidityProvider|ILiquidityProviderFeature|ILiquidityProviderSandbox|IMetaTransactionsFeature|IMooniswapPool|IMultiplexFeature|INativeOrdersEvents|INativeOrdersFeature|IOtcOrdersFeature|IOwnableFeature|IPancakeSwapFeature|ISimpleFunctionRegistryFeature|IStaking|ITestSimpleFunctionRegistryFeature|ITokenSpenderFeature|ITransformERC20Feature|IUniswapFeature|IUniswapV2Pair|IUniswapV3Feature|IUniswapV3Pool|IZeroEx|InitialMigration|LibBootstrap|LibCommonRichErrors|LibERC20Transformer|LibFeeCollector|LibLiquidityProviderRichErrors|LibMetaTransactionsRichErrors|LibMetaTransactionsStorage|LibMigrate|LibNativeOrder|LibNativeOrdersRichErrors|LibNativeOrdersStorage|LibOtcOrdersStorage|LibOwnableRichErrors|LibOwnableStorage|LibProxyRichErrors|LibProxyStorage|LibReentrancyGuardStorage|LibSignature|LibSignatureRichErrors|LibSimpleFunctionRegistryRichErrors|LibSimpleFunctionRegistryStorage|LibStorage|LibTransformERC20RichErrors|LibTransformERC20Storage|LibWalletRichErrors|LiquidityProviderFeature|LiquidityProviderSandbox|LogMetadataTransformer|MetaTransactionsFeature|MixinBalancer|MixinBalancerV2|MixinBancor|MixinCoFiX|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|NativeOrdersCancellation|NativeOrdersFeature|NativeOrdersInfo|NativeOrdersProtocolFees|NativeOrdersSettlement|OtcOrdersFeature|OwnableFeature|PancakeSwapFeature|PayTakerTransformer|PermissionlessTransformerDeployer|PositiveSlippageFeeTransformer|SimpleFunctionRegistryFeature|TestBridge|TestCallTarget|TestCurve|TestDelegateCaller|TestFeeCollectorController|TestFillQuoteTransformerBridge|TestFillQuoteTransformerExchange|TestFillQuoteTransformerHost|TestFixinProtocolFees|TestFixinTokenSpender|TestFullMigration|TestInitialMigration|TestLibNativeOrder|TestLibSignature|TestLiquidityProvider|TestMetaTransactionsNativeOrdersFeature|TestMetaTransactionsTransformERC20Feature|TestMigrator|TestMintTokenERC20Transformer|TestMintableERC20Token|TestMooniswap|TestNativeOrdersFeature|TestNoEthRecipient|TestOrderSignerRegistryWithContractWallet|TestPermissionlessTransformerDeployerSuicidal|TestPermissionlessTransformerDeployerTransformer|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|FeeCollector|FeeCollectorController|FillQuoteTransformer|FixinCommon|FixinEIP712|FixinProtocolFees|FixinReentrancyGuard|FixinTokenSpender|FlashWallet|FullMigration|FundRecoveryFeature|IBatchFillNativeOrdersFeature|IBootstrapFeature|IBridgeAdapter|IERC20Bridge|IERC20Transformer|IFeature|IFlashWallet|IFundRecoveryFeature|ILiquidityProvider|ILiquidityProviderFeature|ILiquidityProviderSandbox|IMetaTransactionsFeature|IMooniswapPool|IMultiplexFeature|INativeOrdersEvents|INativeOrdersFeature|IOtcOrdersFeature|IOwnableFeature|IPancakeSwapFeature|ISimpleFunctionRegistryFeature|IStaking|ITestSimpleFunctionRegistryFeature|ITokenSpenderFeature|ITransformERC20Feature|IUniswapFeature|IUniswapV2Pair|IUniswapV3Feature|IUniswapV3Pool|IZeroEx|InitialMigration|LibBootstrap|LibCommonRichErrors|LibERC20Transformer|LibFeeCollector|LibLiquidityProviderRichErrors|LibMetaTransactionsRichErrors|LibMetaTransactionsStorage|LibMigrate|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|NativeOrdersCancellation|NativeOrdersFeature|NativeOrdersInfo|NativeOrdersProtocolFees|NativeOrdersSettlement|OtcOrdersFeature|OwnableFeature|PancakeSwapFeature|PayTakerTransformer|PermissionlessTransformerDeployer|PositiveSlippageFeeTransformer|SimpleFunctionRegistryFeature|TestBridge|TestCallTarget|TestCurve|TestDelegateCaller|TestFeeCollectorController|TestFillQuoteTransformerBridge|TestFillQuoteTransformerExchange|TestFillQuoteTransformerHost|TestFixinProtocolFees|TestFixinTokenSpender|TestFullMigration|TestInitialMigration|TestLibNativeOrder|TestLibSignature|TestLiquidityProvider|TestMetaTransactionsNativeOrdersFeature|TestMetaTransactionsTransformERC20Feature|TestMigrator|TestMintTokenERC20Transformer|TestMintableERC20Token|TestMooniswap|TestNativeOrdersFeature|TestNoEthRecipient|TestOrderSignerRegistryWithContractWallet|TestPermissionlessTransformerDeployerSuicidal|TestPermissionlessTransformerDeployerTransformer|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",
|
||||||
|
@ -81,10 +81,12 @@ import * as LiquidityProviderFeature from '../test/generated-artifacts/Liquidity
|
|||||||
import * as LiquidityProviderSandbox from '../test/generated-artifacts/LiquidityProviderSandbox.json';
|
import * as LiquidityProviderSandbox from '../test/generated-artifacts/LiquidityProviderSandbox.json';
|
||||||
import * as LogMetadataTransformer from '../test/generated-artifacts/LogMetadataTransformer.json';
|
import * as LogMetadataTransformer from '../test/generated-artifacts/LogMetadataTransformer.json';
|
||||||
import * as MetaTransactionsFeature from '../test/generated-artifacts/MetaTransactionsFeature.json';
|
import * as MetaTransactionsFeature from '../test/generated-artifacts/MetaTransactionsFeature.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 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 MixinCryptoCom from '../test/generated-artifacts/MixinCryptoCom.json';
|
import * as MixinCryptoCom from '../test/generated-artifacts/MixinCryptoCom.json';
|
||||||
import * as MixinCurve from '../test/generated-artifacts/MixinCurve.json';
|
import * as MixinCurve from '../test/generated-artifacts/MixinCurve.json';
|
||||||
import * as MixinCurveV2 from '../test/generated-artifacts/MixinCurveV2.json';
|
import * as MixinCurveV2 from '../test/generated-artifacts/MixinCurveV2.json';
|
||||||
@ -272,10 +274,12 @@ export const artifacts = {
|
|||||||
BridgeAdapter: BridgeAdapter as ContractArtifact,
|
BridgeAdapter: BridgeAdapter as ContractArtifact,
|
||||||
BridgeProtocols: BridgeProtocols as ContractArtifact,
|
BridgeProtocols: BridgeProtocols as ContractArtifact,
|
||||||
IBridgeAdapter: IBridgeAdapter as ContractArtifact,
|
IBridgeAdapter: IBridgeAdapter as ContractArtifact,
|
||||||
|
MixinAaveV2: MixinAaveV2 as ContractArtifact,
|
||||||
MixinBalancer: MixinBalancer as ContractArtifact,
|
MixinBalancer: MixinBalancer as ContractArtifact,
|
||||||
MixinBalancerV2: MixinBalancerV2 as ContractArtifact,
|
MixinBalancerV2: MixinBalancerV2 as ContractArtifact,
|
||||||
MixinBancor: MixinBancor as ContractArtifact,
|
MixinBancor: MixinBancor as ContractArtifact,
|
||||||
MixinCoFiX: MixinCoFiX as ContractArtifact,
|
MixinCoFiX: MixinCoFiX as ContractArtifact,
|
||||||
|
MixinCompound: MixinCompound as ContractArtifact,
|
||||||
MixinCryptoCom: MixinCryptoCom as ContractArtifact,
|
MixinCryptoCom: MixinCryptoCom as ContractArtifact,
|
||||||
MixinCurve: MixinCurve as ContractArtifact,
|
MixinCurve: MixinCurve as ContractArtifact,
|
||||||
MixinCurveV2: MixinCurveV2 as ContractArtifact,
|
MixinCurveV2: MixinCurveV2 as ContractArtifact,
|
||||||
|
@ -79,10 +79,12 @@ export * from '../test/generated-wrappers/liquidity_provider_feature';
|
|||||||
export * from '../test/generated-wrappers/liquidity_provider_sandbox';
|
export * from '../test/generated-wrappers/liquidity_provider_sandbox';
|
||||||
export * from '../test/generated-wrappers/log_metadata_transformer';
|
export * from '../test/generated-wrappers/log_metadata_transformer';
|
||||||
export * from '../test/generated-wrappers/meta_transactions_feature';
|
export * from '../test/generated-wrappers/meta_transactions_feature';
|
||||||
|
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_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_crypto_com';
|
export * from '../test/generated-wrappers/mixin_crypto_com';
|
||||||
export * from '../test/generated-wrappers/mixin_curve';
|
export * from '../test/generated-wrappers/mixin_curve';
|
||||||
export * from '../test/generated-wrappers/mixin_curve_v2';
|
export * from '../test/generated-wrappers/mixin_curve_v2';
|
||||||
|
@ -112,10 +112,12 @@
|
|||||||
"test/generated-artifacts/LiquidityProviderSandbox.json",
|
"test/generated-artifacts/LiquidityProviderSandbox.json",
|
||||||
"test/generated-artifacts/LogMetadataTransformer.json",
|
"test/generated-artifacts/LogMetadataTransformer.json",
|
||||||
"test/generated-artifacts/MetaTransactionsFeature.json",
|
"test/generated-artifacts/MetaTransactionsFeature.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/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/MixinCryptoCom.json",
|
"test/generated-artifacts/MixinCryptoCom.json",
|
||||||
"test/generated-artifacts/MixinCurve.json",
|
"test/generated-artifacts/MixinCurve.json",
|
||||||
"test/generated-artifacts/MixinCurveV2.json",
|
"test/generated-artifacts/MixinCurveV2.json",
|
||||||
|
@ -1,4 +1,13 @@
|
|||||||
[
|
[
|
||||||
|
{
|
||||||
|
"version": "16.40.0",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Add `AaveV2` and `Compound` deposit/withdrawal liquidity source",
|
||||||
|
"pr": 321
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"version": "16.39.0",
|
"version": "16.39.0",
|
||||||
"changes": [
|
"changes": [
|
||||||
|
96
packages/asset-swapper/contracts/src/CompoundSampler.sol
Normal file
96
packages/asset-swapper/contracts/src/CompoundSampler.sol
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
/*
|
||||||
|
|
||||||
|
Copyright 2021 ZeroEx Intl.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
pragma solidity ^0.6;
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
import "./SamplerUtils.sol";
|
||||||
|
import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
|
||||||
|
|
||||||
|
// Minimal CToken interface
|
||||||
|
interface ICToken {
|
||||||
|
function mint(uint mintAmount) external returns (uint);
|
||||||
|
function redeem(uint redeemTokens) external returns (uint);
|
||||||
|
function redeemUnderlying(uint redeemAmount) external returns (uint);
|
||||||
|
function exchangeRateStored() external view returns (uint);
|
||||||
|
function decimals() external view returns (uint8);
|
||||||
|
}
|
||||||
|
|
||||||
|
contract CompoundSampler is SamplerUtils {
|
||||||
|
uint256 constant private EXCHANGE_RATE_SCALE = 1e10;
|
||||||
|
|
||||||
|
function sampleSellsFromCompound(
|
||||||
|
ICToken cToken,
|
||||||
|
IERC20TokenV06 takerToken,
|
||||||
|
IERC20TokenV06 makerToken,
|
||||||
|
uint256[] memory takerTokenAmounts
|
||||||
|
)
|
||||||
|
public
|
||||||
|
view
|
||||||
|
returns (uint256[] memory makerTokenAmounts)
|
||||||
|
{
|
||||||
|
uint256 numSamples = takerTokenAmounts.length;
|
||||||
|
makerTokenAmounts = new uint256[](numSamples);
|
||||||
|
// Exchange rate is scaled by 1 * 10^(18 - 8 + Underlying Token Decimals
|
||||||
|
uint256 exchangeRate = cToken.exchangeRateStored();
|
||||||
|
uint256 cTokenDecimals = uint256(cToken.decimals());
|
||||||
|
|
||||||
|
if (address(makerToken) == address(cToken)) {
|
||||||
|
// mint
|
||||||
|
for (uint256 i = 0; i < numSamples; i++) {
|
||||||
|
makerTokenAmounts[i] = (takerTokenAmounts[i] * EXCHANGE_RATE_SCALE * 10 ** cTokenDecimals) / exchangeRate;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (address(takerToken) == address(cToken)) {
|
||||||
|
// redeem
|
||||||
|
for (uint256 i = 0; i < numSamples; i++) {
|
||||||
|
makerTokenAmounts[i] = (takerTokenAmounts[i] * exchangeRate) / (EXCHANGE_RATE_SCALE * 10 ** cTokenDecimals);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function sampleBuysFromCompound(
|
||||||
|
ICToken cToken,
|
||||||
|
IERC20TokenV06 takerToken,
|
||||||
|
IERC20TokenV06 makerToken,
|
||||||
|
uint256[] memory makerTokenAmounts
|
||||||
|
)
|
||||||
|
public
|
||||||
|
view
|
||||||
|
returns (uint256[] memory takerTokenAmounts)
|
||||||
|
{
|
||||||
|
uint256 numSamples = makerTokenAmounts.length;
|
||||||
|
takerTokenAmounts = new uint256[](numSamples);
|
||||||
|
// Exchange rate is scaled by 1 * 10^(18 - 8 + Underlying Token Decimals
|
||||||
|
uint256 exchangeRate = cToken.exchangeRateStored();
|
||||||
|
uint256 cTokenDecimals = uint256(cToken.decimals());
|
||||||
|
|
||||||
|
if (address(makerToken) == address(cToken)) {
|
||||||
|
// mint
|
||||||
|
for (uint256 i = 0; i < numSamples; i++) {
|
||||||
|
takerTokenAmounts[i] = makerTokenAmounts[i] * exchangeRate / (EXCHANGE_RATE_SCALE * 10 ** cTokenDecimals);
|
||||||
|
}
|
||||||
|
} else if (address(takerToken) == address(cToken)) {
|
||||||
|
// redeem
|
||||||
|
for (uint256 i = 0; i < numSamples; i++) {
|
||||||
|
takerTokenAmounts[i] = (makerTokenAmounts[i] * EXCHANGE_RATE_SCALE * 10 ** cTokenDecimals)/exchangeRate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -23,6 +23,7 @@ pragma experimental ABIEncoderV2;
|
|||||||
import "./BalancerSampler.sol";
|
import "./BalancerSampler.sol";
|
||||||
import "./BalancerV2Sampler.sol";
|
import "./BalancerV2Sampler.sol";
|
||||||
import "./BancorSampler.sol";
|
import "./BancorSampler.sol";
|
||||||
|
import "./CompoundSampler.sol";
|
||||||
import "./CurveSampler.sol";
|
import "./CurveSampler.sol";
|
||||||
import "./DODOSampler.sol";
|
import "./DODOSampler.sol";
|
||||||
import "./DODOV2Sampler.sol";
|
import "./DODOV2Sampler.sol";
|
||||||
@ -48,6 +49,7 @@ contract ERC20BridgeSampler is
|
|||||||
BalancerSampler,
|
BalancerSampler,
|
||||||
BalancerV2Sampler,
|
BalancerV2Sampler,
|
||||||
BancorSampler,
|
BancorSampler,
|
||||||
|
CompoundSampler,
|
||||||
CurveSampler,
|
CurveSampler,
|
||||||
DODOSampler,
|
DODOSampler,
|
||||||
DODOV2Sampler,
|
DODOV2Sampler,
|
||||||
|
@ -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|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|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",
|
||||||
"postpublish": {
|
"postpublish": {
|
||||||
"assets": []
|
"assets": []
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,6 @@ const ONE_SECOND_MS = 1000;
|
|||||||
const ONE_MINUTE_SECS = 60;
|
const ONE_MINUTE_SECS = 60;
|
||||||
const ONE_MINUTE_MS = ONE_SECOND_MS * ONE_MINUTE_SECS;
|
const ONE_MINUTE_MS = ONE_SECOND_MS * ONE_MINUTE_SECS;
|
||||||
const DEFAULT_PER_PAGE = 1000;
|
const DEFAULT_PER_PAGE = 1000;
|
||||||
const ZERO_AMOUNT = new BigNumber(0);
|
|
||||||
const ALT_MM_IMPUTED_INDICATIVE_EXPIRY_SECONDS = 180;
|
const ALT_MM_IMPUTED_INDICATIVE_EXPIRY_SECONDS = 180;
|
||||||
|
|
||||||
const DEFAULT_ORDER_PRUNER_OPTS: OrderPrunerOpts = {
|
const DEFAULT_ORDER_PRUNER_OPTS: OrderPrunerOpts = {
|
||||||
@ -43,6 +42,7 @@ const PROTOCOL_FEE_MULTIPLIER = new BigNumber(0);
|
|||||||
// default 50% buffer for selecting native orders to be aggregated with other sources
|
// default 50% buffer for selecting native orders to be aggregated with other sources
|
||||||
const MARKET_UTILS_AMOUNT_BUFFER_PERCENTAGE = 0.5;
|
const MARKET_UTILS_AMOUNT_BUFFER_PERCENTAGE = 0.5;
|
||||||
|
|
||||||
|
export const ZERO_AMOUNT = new BigNumber(0);
|
||||||
const DEFAULT_SWAP_QUOTER_OPTS: SwapQuoterOpts = {
|
const DEFAULT_SWAP_QUOTER_OPTS: SwapQuoterOpts = {
|
||||||
chainId: ChainId.Mainnet,
|
chainId: ChainId.Mainnet,
|
||||||
orderRefreshIntervalMs: 10000, // 10 seconds
|
orderRefreshIntervalMs: 10000, // 10 seconds
|
||||||
|
57
packages/asset-swapper/src/noop_samplers/AaveV2Sampler.ts
Normal file
57
packages/asset-swapper/src/noop_samplers/AaveV2Sampler.ts
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
import { BigNumber } from '@0x/utils';
|
||||||
|
|
||||||
|
import { ZERO_AMOUNT } from '../constants';
|
||||||
|
export interface AaveInfo {
|
||||||
|
lendingPool: string;
|
||||||
|
aToken: string;
|
||||||
|
underlyingToken: string;
|
||||||
|
}
|
||||||
|
// tslint:disable-next-line:no-unnecessary-class
|
||||||
|
export class AaveV2Sampler {
|
||||||
|
public static sampleSellsFromAaveV2(
|
||||||
|
aaveInfo: AaveInfo,
|
||||||
|
takerToken: string,
|
||||||
|
makerToken: string,
|
||||||
|
takerTokenAmounts: BigNumber[],
|
||||||
|
): BigNumber[] {
|
||||||
|
// Deposit/Withdrawal underlying <-> aToken is always 1:1
|
||||||
|
if (
|
||||||
|
(takerToken.toLowerCase() === aaveInfo.aToken.toLowerCase() &&
|
||||||
|
makerToken.toLowerCase() === aaveInfo.underlyingToken.toLowerCase()) ||
|
||||||
|
(takerToken.toLowerCase() === aaveInfo.underlyingToken.toLowerCase() &&
|
||||||
|
makerToken.toLowerCase() === aaveInfo.aToken.toLowerCase())
|
||||||
|
) {
|
||||||
|
return takerTokenAmounts;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Not matching the reserve return 0 results
|
||||||
|
const numSamples = takerTokenAmounts.length;
|
||||||
|
|
||||||
|
const makerTokenAmounts = new Array(numSamples);
|
||||||
|
makerTokenAmounts.fill(ZERO_AMOUNT);
|
||||||
|
return makerTokenAmounts;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static sampleBuysFromAaveV2(
|
||||||
|
aaveInfo: AaveInfo,
|
||||||
|
takerToken: string,
|
||||||
|
makerToken: string,
|
||||||
|
makerTokenAmounts: BigNumber[],
|
||||||
|
): BigNumber[] {
|
||||||
|
// Deposit/Withdrawal underlying <-> aToken is always 1:1
|
||||||
|
if (
|
||||||
|
(takerToken.toLowerCase() === aaveInfo.aToken.toLowerCase() &&
|
||||||
|
makerToken.toLowerCase() === aaveInfo.underlyingToken.toLowerCase()) ||
|
||||||
|
(takerToken.toLowerCase() === aaveInfo.underlyingToken.toLowerCase() &&
|
||||||
|
makerToken.toLowerCase() === aaveInfo.aToken.toLowerCase())
|
||||||
|
) {
|
||||||
|
return makerTokenAmounts;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Not matching the reserve return 0 results
|
||||||
|
const numSamples = makerTokenAmounts.length;
|
||||||
|
const takerTokenAmounts = new Array(numSamples);
|
||||||
|
takerTokenAmounts.fill(ZERO_AMOUNT);
|
||||||
|
return takerTokenAmounts;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,106 @@
|
|||||||
|
import { logUtils } from '@0x/utils';
|
||||||
|
import { gql, request } from 'graphql-request';
|
||||||
|
|
||||||
|
import { constants } from '../../constants';
|
||||||
|
|
||||||
|
const RESERVES_GQL_QUERY = gql`
|
||||||
|
{
|
||||||
|
reserves(
|
||||||
|
first: 300
|
||||||
|
where: { isActive: true, isFrozen: false }
|
||||||
|
orderBy: totalLiquidity
|
||||||
|
orderDirection: desc
|
||||||
|
) {
|
||||||
|
id
|
||||||
|
underlyingAsset
|
||||||
|
aToken {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
pool {
|
||||||
|
id
|
||||||
|
lendingPool
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export interface AaveReserve {
|
||||||
|
id: string;
|
||||||
|
underlyingAsset: string;
|
||||||
|
aToken: {
|
||||||
|
id: string;
|
||||||
|
};
|
||||||
|
pool: {
|
||||||
|
id: string;
|
||||||
|
lendingPool: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Cache {
|
||||||
|
[key: string]: AaveReserve[];
|
||||||
|
}
|
||||||
|
|
||||||
|
// tslint:disable-next-line:custom-no-magic-numbers
|
||||||
|
const RESERVES_REFRESH_INTERVAL_MS = 30 * constants.ONE_MINUTE_MS;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches Aave V2 reserve information from the official subgraph(s).
|
||||||
|
* The reserve information is updated every 30 minutes and cached
|
||||||
|
* so that it can be accessed with the underlying token's address
|
||||||
|
*/
|
||||||
|
export class AaveV2ReservesCache {
|
||||||
|
private _cache: Cache = {};
|
||||||
|
constructor(private readonly _subgraphUrl: string) {
|
||||||
|
const resfreshReserves = async () => this.fetchAndUpdateReservesAsync();
|
||||||
|
// tslint:disable-next-line:no-floating-promises
|
||||||
|
resfreshReserves();
|
||||||
|
setInterval(resfreshReserves, RESERVES_REFRESH_INTERVAL_MS);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Fetches Aave V2 reserves from the subgraph and updates the cache
|
||||||
|
*/
|
||||||
|
public async fetchAndUpdateReservesAsync(): Promise<void> {
|
||||||
|
try {
|
||||||
|
const { reserves } = await request<{ reserves: AaveReserve[] }>(this._subgraphUrl, RESERVES_GQL_QUERY);
|
||||||
|
const newCache = reserves.reduce<Cache>((memo, reserve) => {
|
||||||
|
const underlyingAsset = reserve.underlyingAsset.toLowerCase();
|
||||||
|
if (!memo[underlyingAsset]) {
|
||||||
|
memo[underlyingAsset] = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
memo[underlyingAsset].push(reserve);
|
||||||
|
return memo;
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
this._cache = newCache;
|
||||||
|
} catch (err) {
|
||||||
|
logUtils.warn(`Failed to update Aave V2 reserves cache: ${err.message}`);
|
||||||
|
// Empty cache just to be safe
|
||||||
|
this._cache = {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public get(takerToken: string, makerToken: string): AaveReserve | undefined {
|
||||||
|
// Deposit takerToken into reserve
|
||||||
|
if (this._cache[takerToken.toLowerCase()]) {
|
||||||
|
const matchingReserve = this._cache[takerToken.toLowerCase()].find(
|
||||||
|
r => r.aToken.id === makerToken.toLowerCase(),
|
||||||
|
);
|
||||||
|
if (matchingReserve) {
|
||||||
|
return matchingReserve;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Withdraw makerToken from reserve
|
||||||
|
if (this._cache[makerToken.toLowerCase()]) {
|
||||||
|
const matchingReserve = this._cache[makerToken.toLowerCase()].find(
|
||||||
|
r => r.aToken.id === takerToken.toLowerCase(),
|
||||||
|
);
|
||||||
|
if (matchingReserve) {
|
||||||
|
return matchingReserve;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// No match
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,78 @@
|
|||||||
|
import { logUtils } from '@0x/utils';
|
||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
|
import { constants } from '../../constants';
|
||||||
|
|
||||||
|
export interface CToken {
|
||||||
|
tokenAddress: string;
|
||||||
|
underlyingAddress: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface CTokenApiResponse {
|
||||||
|
cToken: Array<{
|
||||||
|
token_address: string;
|
||||||
|
underlying_address: string;
|
||||||
|
}>;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Cache {
|
||||||
|
[key: string]: CToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
// tslint:disable-next-line:custom-no-magic-numbers
|
||||||
|
const CTOKEN_REFRESH_INTERVAL_MS = 30 * constants.ONE_MINUTE_MS;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches a list of CTokens from Compound's official API.
|
||||||
|
* The token information is updated every 30 minutes and cached
|
||||||
|
* so that it can be accessed with the underlying token's address.
|
||||||
|
*/
|
||||||
|
export class CompoundCTokenCache {
|
||||||
|
private _cache: Cache = {};
|
||||||
|
constructor(private readonly _apiUrl: string, private readonly _wethAddress: string) {
|
||||||
|
const refreshCTokenCache = async () => this.fetchAndUpdateCTokensAsync();
|
||||||
|
// tslint:disable-next-line:no-floating-promises
|
||||||
|
refreshCTokenCache();
|
||||||
|
setInterval(refreshCTokenCache, CTOKEN_REFRESH_INTERVAL_MS);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async fetchAndUpdateCTokensAsync(): Promise<void> {
|
||||||
|
try {
|
||||||
|
const { data } = await axios.get<CTokenApiResponse>(`${this._apiUrl}/ctoken`);
|
||||||
|
const newCache = data?.cToken.reduce<Cache>((memo, cToken) => {
|
||||||
|
// NOTE: Re-map cETH with null underlying token address to WETH address (we only handle WETH internally)
|
||||||
|
const underlyingAddressClean = cToken.underlying_address
|
||||||
|
? cToken.underlying_address.toLowerCase()
|
||||||
|
: this._wethAddress;
|
||||||
|
|
||||||
|
const tokenData: CToken = {
|
||||||
|
tokenAddress: cToken.token_address.toLowerCase(),
|
||||||
|
underlyingAddress: underlyingAddressClean,
|
||||||
|
};
|
||||||
|
memo[underlyingAddressClean] = tokenData;
|
||||||
|
return memo;
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
this._cache = newCache;
|
||||||
|
} catch (err) {
|
||||||
|
logUtils.warn(`Failed to update Compound cToken cache: ${err.message}`);
|
||||||
|
// NOTE: Safe to keep already cached data as tokens should only be added to the list
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public get(takerToken: string, makerToken: string): CToken | undefined {
|
||||||
|
// mint cToken
|
||||||
|
let cToken = this._cache[takerToken.toLowerCase()];
|
||||||
|
if (cToken && makerToken.toLowerCase() === cToken.tokenAddress.toLowerCase()) {
|
||||||
|
return cToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
// redeem cToken
|
||||||
|
cToken = this._cache[makerToken.toLowerCase()];
|
||||||
|
if (cToken && takerToken.toLowerCase() === cToken.tokenAddress.toLowerCase()) {
|
||||||
|
return cToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
// No match
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
@ -7,7 +7,9 @@ import { TokenAdjacencyGraphBuilder } from '../token_adjacency_graph_builder';
|
|||||||
|
|
||||||
import { SourceFilters } from './source_filters';
|
import { SourceFilters } from './source_filters';
|
||||||
import {
|
import {
|
||||||
|
AaveV2FillData,
|
||||||
BancorFillData,
|
BancorFillData,
|
||||||
|
CompoundFillData,
|
||||||
CurveFillData,
|
CurveFillData,
|
||||||
CurveFunctionSelectors,
|
CurveFunctionSelectors,
|
||||||
CurveInfo,
|
CurveInfo,
|
||||||
@ -101,6 +103,9 @@ export const SELL_SOURCE_FILTER_BY_CHAIN_ID = valueByChainId<SourceFilters>(
|
|||||||
ERC20BridgeSource.UniswapV3,
|
ERC20BridgeSource.UniswapV3,
|
||||||
ERC20BridgeSource.CurveV2,
|
ERC20BridgeSource.CurveV2,
|
||||||
ERC20BridgeSource.ShibaSwap,
|
ERC20BridgeSource.ShibaSwap,
|
||||||
|
// TODO: enable after FQT has been redeployed on Ethereum mainnet
|
||||||
|
// ERC20BridgeSource.AaveV2,
|
||||||
|
// ERC20BridgeSource.Compound,
|
||||||
]),
|
]),
|
||||||
[ChainId.Ropsten]: new SourceFilters([
|
[ChainId.Ropsten]: new SourceFilters([
|
||||||
ERC20BridgeSource.Kyber,
|
ERC20BridgeSource.Kyber,
|
||||||
@ -159,6 +164,7 @@ export const SELL_SOURCE_FILTER_BY_CHAIN_ID = valueByChainId<SourceFilters>(
|
|||||||
ERC20BridgeSource.MultiHop,
|
ERC20BridgeSource.MultiHop,
|
||||||
ERC20BridgeSource.JetSwap,
|
ERC20BridgeSource.JetSwap,
|
||||||
ERC20BridgeSource.IronSwap,
|
ERC20BridgeSource.IronSwap,
|
||||||
|
ERC20BridgeSource.AaveV2,
|
||||||
]),
|
]),
|
||||||
[ChainId.Avalanche]: new SourceFilters([
|
[ChainId.Avalanche]: new SourceFilters([
|
||||||
ERC20BridgeSource.MultiHop,
|
ERC20BridgeSource.MultiHop,
|
||||||
@ -168,6 +174,7 @@ export const SELL_SOURCE_FILTER_BY_CHAIN_ID = valueByChainId<SourceFilters>(
|
|||||||
ERC20BridgeSource.Curve,
|
ERC20BridgeSource.Curve,
|
||||||
ERC20BridgeSource.CurveV2,
|
ERC20BridgeSource.CurveV2,
|
||||||
ERC20BridgeSource.KyberDmm,
|
ERC20BridgeSource.KyberDmm,
|
||||||
|
ERC20BridgeSource.AaveV2,
|
||||||
]),
|
]),
|
||||||
[ChainId.Fantom]: new SourceFilters([
|
[ChainId.Fantom]: new SourceFilters([
|
||||||
ERC20BridgeSource.MultiHop,
|
ERC20BridgeSource.MultiHop,
|
||||||
@ -227,6 +234,9 @@ export const BUY_SOURCE_FILTER_BY_CHAIN_ID = valueByChainId<SourceFilters>(
|
|||||||
ERC20BridgeSource.UniswapV3,
|
ERC20BridgeSource.UniswapV3,
|
||||||
ERC20BridgeSource.CurveV2,
|
ERC20BridgeSource.CurveV2,
|
||||||
ERC20BridgeSource.ShibaSwap,
|
ERC20BridgeSource.ShibaSwap,
|
||||||
|
// TODO: enable after FQT has been redeployed on Ethereum mainnet
|
||||||
|
// ERC20BridgeSource.AaveV2,
|
||||||
|
// ERC20BridgeSource.Compound,
|
||||||
]),
|
]),
|
||||||
[ChainId.Ropsten]: new SourceFilters([
|
[ChainId.Ropsten]: new SourceFilters([
|
||||||
ERC20BridgeSource.Kyber,
|
ERC20BridgeSource.Kyber,
|
||||||
@ -285,6 +295,7 @@ export const BUY_SOURCE_FILTER_BY_CHAIN_ID = valueByChainId<SourceFilters>(
|
|||||||
ERC20BridgeSource.MultiHop,
|
ERC20BridgeSource.MultiHop,
|
||||||
ERC20BridgeSource.JetSwap,
|
ERC20BridgeSource.JetSwap,
|
||||||
ERC20BridgeSource.IronSwap,
|
ERC20BridgeSource.IronSwap,
|
||||||
|
ERC20BridgeSource.AaveV2,
|
||||||
]),
|
]),
|
||||||
[ChainId.Avalanche]: new SourceFilters([
|
[ChainId.Avalanche]: new SourceFilters([
|
||||||
ERC20BridgeSource.MultiHop,
|
ERC20BridgeSource.MultiHop,
|
||||||
@ -294,6 +305,7 @@ export const BUY_SOURCE_FILTER_BY_CHAIN_ID = valueByChainId<SourceFilters>(
|
|||||||
ERC20BridgeSource.Curve,
|
ERC20BridgeSource.Curve,
|
||||||
ERC20BridgeSource.CurveV2,
|
ERC20BridgeSource.CurveV2,
|
||||||
ERC20BridgeSource.KyberDmm,
|
ERC20BridgeSource.KyberDmm,
|
||||||
|
ERC20BridgeSource.AaveV2,
|
||||||
]),
|
]),
|
||||||
[ChainId.Fantom]: new SourceFilters([
|
[ChainId.Fantom]: new SourceFilters([
|
||||||
ERC20BridgeSource.MultiHop,
|
ERC20BridgeSource.MultiHop,
|
||||||
@ -1700,6 +1712,24 @@ export const UNISWAPV3_CONFIG_BY_CHAIN_ID = valueByChainId(
|
|||||||
{ quoter: NULL_ADDRESS, router: NULL_ADDRESS },
|
{ quoter: NULL_ADDRESS, router: NULL_ADDRESS },
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const AAVE_V2_SUBGRAPH_URL_BY_CHAIN_ID = valueByChainId(
|
||||||
|
{
|
||||||
|
// TODO: enable after FQT has been redeployed on Ethereum mainnet
|
||||||
|
// [ChainId.Mainnet]: 'https://api.thegraph.com/subgraphs/name/aave/protocol-v2',
|
||||||
|
[ChainId.Polygon]: 'https://api.thegraph.com/subgraphs/name/aave/aave-v2-matic',
|
||||||
|
[ChainId.Avalanche]: 'https://api.thegraph.com/subgraphs/name/aave/protocol-v2-avalanche',
|
||||||
|
},
|
||||||
|
null,
|
||||||
|
);
|
||||||
|
|
||||||
|
export const COMPOUND_API_URL_BY_CHAIN_ID = valueByChainId(
|
||||||
|
{
|
||||||
|
// TODO: enable after FQT has been redeployed on Ethereum mainnet
|
||||||
|
// [ChainId.Mainnet]: 'https://api.compound.finance/api/v2',
|
||||||
|
},
|
||||||
|
null,
|
||||||
|
);
|
||||||
|
|
||||||
//
|
//
|
||||||
// BSC
|
// BSC
|
||||||
//
|
//
|
||||||
@ -1965,6 +1995,21 @@ export const DEFAULT_GAS_SCHEDULE: Required<FeeSchedule> = {
|
|||||||
return gas;
|
return gas;
|
||||||
},
|
},
|
||||||
[ERC20BridgeSource.Lido]: () => 226e3,
|
[ERC20BridgeSource.Lido]: () => 226e3,
|
||||||
|
[ERC20BridgeSource.AaveV2]: (fillData?: FillData) => {
|
||||||
|
const aaveFillData = fillData as AaveV2FillData;
|
||||||
|
// NOTE: The Aave deposit method is more expensive than the withdraw
|
||||||
|
return aaveFillData.takerToken === aaveFillData.underlyingToken ? 400e3 : 300e3;
|
||||||
|
},
|
||||||
|
[ERC20BridgeSource.Compound]: (fillData?: FillData) => {
|
||||||
|
// NOTE: cETH is handled differently than other cTokens
|
||||||
|
const wethAddress = NATIVE_FEE_TOKEN_BY_CHAIN_ID[ChainId.Mainnet];
|
||||||
|
const compoundFillData = fillData as CompoundFillData;
|
||||||
|
if (compoundFillData.takerToken === compoundFillData.cToken) {
|
||||||
|
return compoundFillData.makerToken === wethAddress ? 120e3 : 150e3;
|
||||||
|
} else {
|
||||||
|
return compoundFillData.takerToken === wethAddress ? 210e3 : 250e3;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
//
|
//
|
||||||
// BSC
|
// BSC
|
||||||
|
@ -5,11 +5,13 @@ import { AssetSwapperContractAddresses, MarketOperation } from '../../types';
|
|||||||
|
|
||||||
import { MAX_UINT256, ZERO_AMOUNT } from './constants';
|
import { MAX_UINT256, ZERO_AMOUNT } from './constants';
|
||||||
import {
|
import {
|
||||||
|
AaveV2FillData,
|
||||||
AggregationError,
|
AggregationError,
|
||||||
BalancerFillData,
|
BalancerFillData,
|
||||||
BalancerV2FillData,
|
BalancerV2FillData,
|
||||||
BancorFillData,
|
BancorFillData,
|
||||||
CollapsedFill,
|
CollapsedFill,
|
||||||
|
CompoundFillData,
|
||||||
CurveFillData,
|
CurveFillData,
|
||||||
DexSample,
|
DexSample,
|
||||||
DODOFillData,
|
DODOFillData,
|
||||||
@ -194,6 +196,10 @@ export function getErc20BridgeSourceToBridgeSource(source: ERC20BridgeSource): s
|
|||||||
return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'SpookySwap');
|
return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'SpookySwap');
|
||||||
case ERC20BridgeSource.MorpheusSwap:
|
case ERC20BridgeSource.MorpheusSwap:
|
||||||
return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'MorpheusSwap');
|
return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'MorpheusSwap');
|
||||||
|
case ERC20BridgeSource.AaveV2:
|
||||||
|
return encodeBridgeSourceId(BridgeProtocol.AaveV2, 'AaveV2');
|
||||||
|
case ERC20BridgeSource.Compound:
|
||||||
|
return encodeBridgeSourceId(BridgeProtocol.Compound, 'Compound');
|
||||||
default:
|
default:
|
||||||
throw new Error(AggregationError.NoBridgeForSource);
|
throw new Error(AggregationError.NoBridgeForSource);
|
||||||
}
|
}
|
||||||
@ -339,6 +345,15 @@ export function createBridgeDataForBridgeOrder(order: OptimizedMarketBridgeOrder
|
|||||||
const lidoFillData = (order as OptimizedMarketBridgeOrder<LidoFillData>).fillData;
|
const lidoFillData = (order as OptimizedMarketBridgeOrder<LidoFillData>).fillData;
|
||||||
bridgeData = encoder.encode([lidoFillData.stEthTokenAddress]);
|
bridgeData = encoder.encode([lidoFillData.stEthTokenAddress]);
|
||||||
break;
|
break;
|
||||||
|
case ERC20BridgeSource.AaveV2:
|
||||||
|
const aaveFillData = (order as OptimizedMarketBridgeOrder<AaveV2FillData>).fillData;
|
||||||
|
bridgeData = encoder.encode([aaveFillData.lendingPool, aaveFillData.aToken]);
|
||||||
|
break;
|
||||||
|
case ERC20BridgeSource.Compound:
|
||||||
|
const compoundFillData = (order as OptimizedMarketBridgeOrder<CompoundFillData>).fillData;
|
||||||
|
bridgeData = encoder.encode([compoundFillData.cToken]);
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new Error(AggregationError.NoBridgeForSource);
|
throw new Error(AggregationError.NoBridgeForSource);
|
||||||
}
|
}
|
||||||
@ -504,6 +519,8 @@ export const BRIDGE_ENCODERS: {
|
|||||||
]),
|
]),
|
||||||
[ERC20BridgeSource.KyberDmm]: AbiEncoder.create('(address,address[],address[])'),
|
[ERC20BridgeSource.KyberDmm]: AbiEncoder.create('(address,address[],address[])'),
|
||||||
[ERC20BridgeSource.Lido]: AbiEncoder.create('(address)'),
|
[ERC20BridgeSource.Lido]: AbiEncoder.create('(address)'),
|
||||||
|
[ERC20BridgeSource.AaveV2]: AbiEncoder.create('(address,address)'),
|
||||||
|
[ERC20BridgeSource.Compound]: AbiEncoder.create('(address)'),
|
||||||
};
|
};
|
||||||
|
|
||||||
function getFillTokenAmounts(fill: CollapsedFill, side: MarketOperation): [BigNumber, BigNumber] {
|
function getFillTokenAmounts(fill: CollapsedFill, side: MarketOperation): [BigNumber, BigNumber] {
|
||||||
|
@ -0,0 +1,36 @@
|
|||||||
|
import { BigNumber, logUtils, NULL_BYTES } from '@0x/utils';
|
||||||
|
|
||||||
|
import { ERC20BridgeSource, FillData, SourceQuoteOperation } from './types';
|
||||||
|
|
||||||
|
interface SamplerNoOperationCall {
|
||||||
|
callback: () => BigNumber[];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SamplerNoOperation can be used for sources where we already have all the necessary information
|
||||||
|
* required to perform the sample operations, without needing access to any on-chain data. Using a noop sample
|
||||||
|
* you can skip the eth_call, and just calculate the results directly in typescript land.
|
||||||
|
*/
|
||||||
|
export class SamplerNoOperation<TFillData extends FillData = FillData> implements SourceQuoteOperation<TFillData> {
|
||||||
|
public readonly source: ERC20BridgeSource;
|
||||||
|
public fillData: TFillData;
|
||||||
|
private readonly _callback: () => BigNumber[];
|
||||||
|
|
||||||
|
constructor(opts: { source: ERC20BridgeSource; fillData?: TFillData } & SamplerNoOperationCall) {
|
||||||
|
this.source = opts.source;
|
||||||
|
this.fillData = opts.fillData || ({} as TFillData); // tslint:disable-line:no-object-literal-type-assertion
|
||||||
|
this._callback = opts.callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
// tslint:disable-next-line:prefer-function-over-method
|
||||||
|
public encodeCall(): string {
|
||||||
|
return NULL_BYTES;
|
||||||
|
}
|
||||||
|
public handleCallResults(_callResults: string): BigNumber[] {
|
||||||
|
return this._callback();
|
||||||
|
}
|
||||||
|
public handleRevert(_callResults: string): BigNumber[] {
|
||||||
|
logUtils.warn(`SamplerNoOperation: ${this.source} reverted`);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
@ -3,9 +3,11 @@ import { LimitOrderFields } from '@0x/protocol-utils';
|
|||||||
import { BigNumber, logUtils } from '@0x/utils';
|
import { BigNumber, logUtils } from '@0x/utils';
|
||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
|
|
||||||
|
import { AaveV2Sampler } from '../../noop_samplers/AaveV2Sampler';
|
||||||
import { SamplerCallResult, SignedNativeOrder } from '../../types';
|
import { SamplerCallResult, SignedNativeOrder } from '../../types';
|
||||||
import { ERC20BridgeSamplerContract } from '../../wrappers';
|
import { ERC20BridgeSamplerContract } from '../../wrappers';
|
||||||
|
|
||||||
|
import { AaveV2ReservesCache } from './aave_reserves_cache';
|
||||||
import { BancorService } from './bancor_service';
|
import { BancorService } from './bancor_service';
|
||||||
import {
|
import {
|
||||||
getCurveLikeInfosForPair,
|
getCurveLikeInfosForPair,
|
||||||
@ -17,11 +19,14 @@ import {
|
|||||||
isValidAddress,
|
isValidAddress,
|
||||||
uniswapV2LikeRouterAddress,
|
uniswapV2LikeRouterAddress,
|
||||||
} from './bridge_source_utils';
|
} from './bridge_source_utils';
|
||||||
|
import { CompoundCTokenCache } from './compound_ctoken_cache';
|
||||||
import {
|
import {
|
||||||
|
AAVE_V2_SUBGRAPH_URL_BY_CHAIN_ID,
|
||||||
BALANCER_V2_VAULT_ADDRESS_BY_CHAIN,
|
BALANCER_V2_VAULT_ADDRESS_BY_CHAIN,
|
||||||
BANCOR_REGISTRY_BY_CHAIN_ID,
|
BANCOR_REGISTRY_BY_CHAIN_ID,
|
||||||
BEETHOVEN_X_SUBGRAPH_URL_BY_CHAIN,
|
BEETHOVEN_X_SUBGRAPH_URL_BY_CHAIN,
|
||||||
BEETHOVEN_X_VAULT_ADDRESS_BY_CHAIN,
|
BEETHOVEN_X_VAULT_ADDRESS_BY_CHAIN,
|
||||||
|
COMPOUND_API_URL_BY_CHAIN_ID,
|
||||||
DODOV1_CONFIG_BY_CHAIN_ID,
|
DODOV1_CONFIG_BY_CHAIN_ID,
|
||||||
DODOV2_FACTORIES_BY_CHAIN_ID,
|
DODOV2_FACTORIES_BY_CHAIN_ID,
|
||||||
KYBER_CONFIG_BY_CHAIN_ID,
|
KYBER_CONFIG_BY_CHAIN_ID,
|
||||||
@ -45,13 +50,17 @@ 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 { SamplerContractOperation } from './sampler_contract_operation';
|
import { SamplerContractOperation } from './sampler_contract_operation';
|
||||||
|
import { SamplerNoOperation } from './sampler_no_operation';
|
||||||
import { SourceFilters } from './source_filters';
|
import { SourceFilters } from './source_filters';
|
||||||
import {
|
import {
|
||||||
|
AaveV2FillData,
|
||||||
|
AaveV2Info,
|
||||||
BalancerFillData,
|
BalancerFillData,
|
||||||
BalancerV2FillData,
|
BalancerV2FillData,
|
||||||
BalancerV2PoolInfo,
|
BalancerV2PoolInfo,
|
||||||
BancorFillData,
|
BancorFillData,
|
||||||
BatchedOperation,
|
BatchedOperation,
|
||||||
|
CompoundFillData,
|
||||||
CurveFillData,
|
CurveFillData,
|
||||||
CurveInfo,
|
CurveInfo,
|
||||||
DexSample,
|
DexSample,
|
||||||
@ -99,6 +108,8 @@ 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: { [key in SourcesWithPoolsCache]: PoolsCache };
|
||||||
|
public readonly aaveReservesCache: AaveV2ReservesCache | undefined;
|
||||||
|
public readonly compoundCTokenCache: CompoundCTokenCache | undefined;
|
||||||
protected _bancorService?: BancorService;
|
protected _bancorService?: BancorService;
|
||||||
public static constant<T>(result: T): BatchedOperation<T> {
|
public static constant<T>(result: T): BatchedOperation<T> {
|
||||||
return {
|
return {
|
||||||
@ -131,6 +142,19 @@ export class SamplerOperations {
|
|||||||
[ERC20BridgeSource.Balancer]: new BalancerPoolsCache(),
|
[ERC20BridgeSource.Balancer]: new BalancerPoolsCache(),
|
||||||
[ERC20BridgeSource.Cream]: new CreamPoolsCache(),
|
[ERC20BridgeSource.Cream]: new CreamPoolsCache(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const aaveSubgraphUrl = AAVE_V2_SUBGRAPH_URL_BY_CHAIN_ID[chainId];
|
||||||
|
if (aaveSubgraphUrl) {
|
||||||
|
this.aaveReservesCache = new AaveV2ReservesCache(aaveSubgraphUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
const compoundApiUrl = COMPOUND_API_URL_BY_CHAIN_ID[chainId];
|
||||||
|
if (compoundApiUrl) {
|
||||||
|
this.compoundCTokenCache = new CompoundCTokenCache(
|
||||||
|
compoundApiUrl,
|
||||||
|
NATIVE_FEE_TOKEN_BY_CHAIN_ID[this.chainId],
|
||||||
|
);
|
||||||
|
}
|
||||||
// Initialize the Bancor service, fetching paths in the background
|
// Initialize the Bancor service, fetching paths in the background
|
||||||
bancorServiceFn()
|
bancorServiceFn()
|
||||||
.then(service => (this._bancorService = service))
|
.then(service => (this._bancorService = service))
|
||||||
@ -1099,6 +1123,64 @@ export class SamplerOperations {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// tslint:disable-next-line:prefer-function-over-method
|
||||||
|
public getAaveV2SellQuotes(
|
||||||
|
aaveInfo: AaveV2Info,
|
||||||
|
makerToken: string,
|
||||||
|
takerToken: string,
|
||||||
|
takerFillAmounts: BigNumber[],
|
||||||
|
): SourceQuoteOperation<AaveV2FillData> {
|
||||||
|
return new SamplerNoOperation({
|
||||||
|
source: ERC20BridgeSource.AaveV2,
|
||||||
|
fillData: { ...aaveInfo, takerToken },
|
||||||
|
callback: () => AaveV2Sampler.sampleSellsFromAaveV2(aaveInfo, takerToken, makerToken, takerFillAmounts),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// tslint:disable-next-line:prefer-function-over-method
|
||||||
|
public getAaveV2BuyQuotes(
|
||||||
|
aaveInfo: AaveV2Info,
|
||||||
|
makerToken: string,
|
||||||
|
takerToken: string,
|
||||||
|
makerFillAmounts: BigNumber[],
|
||||||
|
): SourceQuoteOperation<AaveV2FillData> {
|
||||||
|
return new SamplerNoOperation({
|
||||||
|
source: ERC20BridgeSource.AaveV2,
|
||||||
|
fillData: { ...aaveInfo, takerToken },
|
||||||
|
callback: () => AaveV2Sampler.sampleBuysFromAaveV2(aaveInfo, takerToken, makerToken, makerFillAmounts),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public getCompoundSellQuotes(
|
||||||
|
cToken: string,
|
||||||
|
makerToken: string,
|
||||||
|
takerToken: string,
|
||||||
|
takerFillAmounts: BigNumber[],
|
||||||
|
): SourceQuoteOperation<CompoundFillData> {
|
||||||
|
return new SamplerContractOperation({
|
||||||
|
source: ERC20BridgeSource.Compound,
|
||||||
|
fillData: { cToken, takerToken, makerToken },
|
||||||
|
contract: this._samplerContract,
|
||||||
|
function: this._samplerContract.sampleSellsFromCompound,
|
||||||
|
params: [cToken, takerToken, makerToken, takerFillAmounts],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public getCompoundBuyQuotes(
|
||||||
|
cToken: string,
|
||||||
|
makerToken: string,
|
||||||
|
takerToken: string,
|
||||||
|
makerFillAmounts: BigNumber[],
|
||||||
|
): SourceQuoteOperation<CompoundFillData> {
|
||||||
|
return new SamplerContractOperation({
|
||||||
|
source: ERC20BridgeSource.Compound,
|
||||||
|
fillData: { cToken, takerToken, makerToken },
|
||||||
|
contract: this._samplerContract,
|
||||||
|
function: this._samplerContract.sampleBuysFromCompound,
|
||||||
|
params: [cToken, takerToken, makerToken, makerFillAmounts],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public getMedianSellRate(
|
public getMedianSellRate(
|
||||||
sources: ERC20BridgeSource[],
|
sources: ERC20BridgeSource[],
|
||||||
makerToken: string,
|
makerToken: string,
|
||||||
@ -1449,6 +1531,38 @@ export class SamplerOperations {
|
|||||||
|
|
||||||
return this.getLidoSellQuotes(lidoInfo, makerToken, takerToken, takerFillAmounts);
|
return this.getLidoSellQuotes(lidoInfo, makerToken, takerToken, takerFillAmounts);
|
||||||
}
|
}
|
||||||
|
case ERC20BridgeSource.AaveV2: {
|
||||||
|
if (!this.aaveReservesCache) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
const reserve = this.aaveReservesCache.get(takerToken, makerToken);
|
||||||
|
if (!reserve) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const info: AaveV2Info = {
|
||||||
|
lendingPool: reserve.pool.lendingPool,
|
||||||
|
aToken: reserve.aToken.id,
|
||||||
|
underlyingToken: reserve.underlyingAsset,
|
||||||
|
};
|
||||||
|
return this.getAaveV2SellQuotes(info, makerToken, takerToken, takerFillAmounts);
|
||||||
|
}
|
||||||
|
case ERC20BridgeSource.Compound: {
|
||||||
|
if (!this.compoundCTokenCache) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const cToken = this.compoundCTokenCache.get(takerToken, makerToken);
|
||||||
|
if (!cToken) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
return this.getCompoundSellQuotes(
|
||||||
|
cToken.tokenAddress,
|
||||||
|
makerToken,
|
||||||
|
takerToken,
|
||||||
|
takerFillAmounts,
|
||||||
|
);
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
throw new Error(`Unsupported sell sample source: ${source}`);
|
throw new Error(`Unsupported sell sample source: ${source}`);
|
||||||
}
|
}
|
||||||
@ -1718,6 +1832,32 @@ export class SamplerOperations {
|
|||||||
|
|
||||||
return this.getLidoBuyQuotes(lidoInfo, makerToken, takerToken, makerFillAmounts);
|
return this.getLidoBuyQuotes(lidoInfo, makerToken, takerToken, makerFillAmounts);
|
||||||
}
|
}
|
||||||
|
case ERC20BridgeSource.AaveV2: {
|
||||||
|
if (!this.aaveReservesCache) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
const reserve = this.aaveReservesCache.get(takerToken, makerToken);
|
||||||
|
if (!reserve) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
const info: AaveV2Info = {
|
||||||
|
lendingPool: reserve.pool.lendingPool,
|
||||||
|
aToken: reserve.aToken.id,
|
||||||
|
underlyingToken: reserve.underlyingAsset,
|
||||||
|
};
|
||||||
|
return this.getAaveV2BuyQuotes(info, makerToken, takerToken, makerFillAmounts);
|
||||||
|
}
|
||||||
|
case ERC20BridgeSource.Compound: {
|
||||||
|
if (!this.compoundCTokenCache) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const cToken = this.compoundCTokenCache.get(takerToken, makerToken);
|
||||||
|
if (!cToken) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
return this.getCompoundBuyQuotes(cToken.tokenAddress, makerToken, takerToken, makerFillAmounts);
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
throw new Error(`Unsupported buy sample source: ${source}`);
|
throw new Error(`Unsupported buy sample source: ${source}`);
|
||||||
}
|
}
|
||||||
|
@ -68,6 +68,8 @@ export enum ERC20BridgeSource {
|
|||||||
CurveV2 = 'Curve_V2',
|
CurveV2 = 'Curve_V2',
|
||||||
Lido = 'Lido',
|
Lido = 'Lido',
|
||||||
ShibaSwap = 'ShibaSwap',
|
ShibaSwap = 'ShibaSwap',
|
||||||
|
AaveV2 = 'Aave_V2',
|
||||||
|
Compound = 'Compound',
|
||||||
// BSC only
|
// BSC only
|
||||||
PancakeSwap = 'PancakeSwap',
|
PancakeSwap = 'PancakeSwap',
|
||||||
PancakeSwapV2 = 'PancakeSwap_V2',
|
PancakeSwapV2 = 'PancakeSwap_V2',
|
||||||
@ -172,6 +174,12 @@ export interface BalancerV2PoolInfo {
|
|||||||
vault: string;
|
vault: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface AaveV2Info {
|
||||||
|
lendingPool: string;
|
||||||
|
aToken: string;
|
||||||
|
underlyingToken: string;
|
||||||
|
}
|
||||||
|
|
||||||
// Internal `fillData` field for `Fill` objects.
|
// Internal `fillData` field for `Fill` objects.
|
||||||
export interface FillData {}
|
export interface FillData {}
|
||||||
|
|
||||||
@ -279,6 +287,19 @@ export interface LidoFillData extends FillData {
|
|||||||
takerToken: string;
|
takerToken: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface AaveV2FillData extends FillData {
|
||||||
|
lendingPool: string;
|
||||||
|
aToken: string;
|
||||||
|
underlyingToken: string;
|
||||||
|
takerToken: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CompoundFillData extends FillData {
|
||||||
|
cToken: string;
|
||||||
|
takerToken: string;
|
||||||
|
makerToken: string;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a node on a fill path.
|
* Represents a node on a fill path.
|
||||||
*/
|
*/
|
||||||
|
@ -10,6 +10,7 @@ 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 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 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';
|
||||||
@ -52,6 +53,7 @@ export const artifacts = {
|
|||||||
BalancerSampler: BalancerSampler as ContractArtifact,
|
BalancerSampler: BalancerSampler as ContractArtifact,
|
||||||
BalancerV2Sampler: BalancerV2Sampler as ContractArtifact,
|
BalancerV2Sampler: BalancerV2Sampler as ContractArtifact,
|
||||||
BancorSampler: BancorSampler as ContractArtifact,
|
BancorSampler: BancorSampler as ContractArtifact,
|
||||||
|
CompoundSampler: CompoundSampler as ContractArtifact,
|
||||||
CurveSampler: CurveSampler as ContractArtifact,
|
CurveSampler: CurveSampler as ContractArtifact,
|
||||||
DODOSampler: DODOSampler as ContractArtifact,
|
DODOSampler: DODOSampler as ContractArtifact,
|
||||||
DODOV2Sampler: DODOV2Sampler as ContractArtifact,
|
DODOV2Sampler: DODOV2Sampler as ContractArtifact,
|
||||||
|
@ -8,6 +8,7 @@ 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_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/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';
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
"test/generated-artifacts/BalancerSampler.json",
|
"test/generated-artifacts/BalancerSampler.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/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",
|
||||||
|
@ -1,4 +1,13 @@
|
|||||||
[
|
[
|
||||||
|
{
|
||||||
|
"version": "6.10.0",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Add Aave supported FQT addresses for Polygon, Avalanche",
|
||||||
|
"pr": 321
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"version": "6.9.0",
|
"version": "6.9.0",
|
||||||
"changes": [
|
"changes": [
|
||||||
|
@ -289,7 +289,7 @@
|
|||||||
"wethTransformer": "0xe309d011cc6f189a3e8dcba85922715a019fed38",
|
"wethTransformer": "0xe309d011cc6f189a3e8dcba85922715a019fed38",
|
||||||
"payTakerTransformer": "0x5ba7b9be86cda01cfbf56e0fb97184783be9dda1",
|
"payTakerTransformer": "0x5ba7b9be86cda01cfbf56e0fb97184783be9dda1",
|
||||||
"affiliateFeeTransformer": "0xbed27284b42e5684e987169cf1da09c5d6c49fa8",
|
"affiliateFeeTransformer": "0xbed27284b42e5684e987169cf1da09c5d6c49fa8",
|
||||||
"fillQuoteTransformer": "0xf708d512b8a82e2862543a630403327174410baf",
|
"fillQuoteTransformer": "0xd3afdf4a8ea9183e76c9c2306cda03ea4afffea5",
|
||||||
"positiveSlippageFeeTransformer": "0x4cd8f1c0df4d40fcc1e073845d5f6f4ed5cc8dab"
|
"positiveSlippageFeeTransformer": "0x4cd8f1c0df4d40fcc1e073845d5f6f4ed5cc8dab"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -373,7 +373,7 @@
|
|||||||
"wethTransformer": "0x9b8b52391071d71cd4ad1e61d7f273268fa34c6c",
|
"wethTransformer": "0x9b8b52391071d71cd4ad1e61d7f273268fa34c6c",
|
||||||
"payTakerTransformer": "0x898c6fde239d646c73f0a57e3570b6f86a3d62a3",
|
"payTakerTransformer": "0x898c6fde239d646c73f0a57e3570b6f86a3d62a3",
|
||||||
"affiliateFeeTransformer": "0x34617b855411e52fbc05899435f44cbd0503022c",
|
"affiliateFeeTransformer": "0x34617b855411e52fbc05899435f44cbd0503022c",
|
||||||
"fillQuoteTransformer": "0x8a5417dd7ffde61ec61e11b45797e16686e1d6b9",
|
"fillQuoteTransformer": "0xd421f50b3ae27f223aa35a04944236d257235412",
|
||||||
"positiveSlippageFeeTransformer": "0x470ba89da18a6db6e8a0567b3c9214b960861857"
|
"positiveSlippageFeeTransformer": "0x470ba89da18a6db6e8a0567b3c9214b960861857"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -1,4 +1,13 @@
|
|||||||
[
|
[
|
||||||
|
{
|
||||||
|
"version": "1.10.0",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Add `AaveV2` and `Compound` deposit/withdrawal liquidity source",
|
||||||
|
"pr": 321
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"timestamp": 1637102971,
|
"timestamp": 1637102971,
|
||||||
"version": "1.9.5",
|
"version": "1.9.5",
|
||||||
|
@ -132,6 +132,8 @@ export enum BridgeProtocol {
|
|||||||
CurveV2,
|
CurveV2,
|
||||||
Lido,
|
Lido,
|
||||||
Clipper, // Not used: Clipper is now using PLP interface
|
Clipper, // Not used: Clipper is now using PLP interface
|
||||||
|
AaveV2,
|
||||||
|
Compound,
|
||||||
}
|
}
|
||||||
// tslint:enable: enum-naming
|
// tslint:enable: enum-naming
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user