feat: Swerve Finance and SushiSwap (#2698)
* feat: Swerve Finance * export SwerveFillData * test and CHANGELOG * feat: Sushiswap (#2700) * feat: SushiSwap * Changelog * fix tests * Deployed SushiSwap bridge * Fix test * IGNORED_EXCESSIVE_TYPES
This commit is contained in:
parent
08ae43aad3
commit
630108ccb6
136
contracts/asset-proxy/contracts/src/bridges/SushiSwapBridge.sol
Normal file
136
contracts/asset-proxy/contracts/src/bridges/SushiSwapBridge.sol
Normal file
@ -0,0 +1,136 @@
|
||||
/*
|
||||
|
||||
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.5.9;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol";
|
||||
import "@0x/contracts-erc20/contracts/src/interfaces/IEtherToken.sol";
|
||||
import "@0x/contracts-erc20/contracts/src/LibERC20Token.sol";
|
||||
import "@0x/contracts-exchange-libs/contracts/src/IWallet.sol";
|
||||
import "@0x/contracts-utils/contracts/src/LibAddressArray.sol";
|
||||
import "@0x/contracts-utils/contracts/src/DeploymentConstants.sol";
|
||||
import "../interfaces/IUniswapV2Router01.sol";
|
||||
import "../interfaces/IERC20Bridge.sol";
|
||||
|
||||
|
||||
// solhint-disable space-after-comma
|
||||
// solhint-disable not-rely-on-time
|
||||
contract SushiSwapBridge is
|
||||
IERC20Bridge,
|
||||
IWallet,
|
||||
DeploymentConstants
|
||||
{
|
||||
struct TransferState {
|
||||
address[] path;
|
||||
address router;
|
||||
uint256 fromTokenBalance;
|
||||
}
|
||||
|
||||
/// @dev Callback for `IERC20Bridge`. Tries to buy `amount` of
|
||||
/// `toTokenAddress` tokens by selling the entirety of the `fromTokenAddress`
|
||||
/// token encoded in the bridge data.
|
||||
/// @param toTokenAddress The token to buy and transfer to `to`.
|
||||
/// @param from The maker (this contract).
|
||||
/// @param to The recipient of the bought tokens.
|
||||
/// @param amount Minimum amount of `toTokenAddress` tokens to buy.
|
||||
/// @param bridgeData The abi-encoded path of token addresses. Last element must be toTokenAddress
|
||||
/// @return success The magic bytes if successful.
|
||||
function bridgeTransferFrom(
|
||||
address toTokenAddress,
|
||||
address from,
|
||||
address to,
|
||||
uint256 amount,
|
||||
bytes calldata bridgeData
|
||||
)
|
||||
external
|
||||
returns (bytes4 success)
|
||||
{
|
||||
// hold variables to get around stack depth limitations
|
||||
TransferState memory state;
|
||||
|
||||
// Decode the bridge data to get the `fromTokenAddress`.
|
||||
// solhint-disable indent
|
||||
(state.path, state.router) = abi.decode(bridgeData, (address[], address));
|
||||
// solhint-enable indent
|
||||
|
||||
require(state.path.length >= 2, "SushiSwapBridge/PATH_LENGTH_MUST_BE_AT_LEAST_TWO");
|
||||
require(state.path[state.path.length - 1] == toTokenAddress, "SushiSwapBridge/LAST_ELEMENT_OF_PATH_MUST_MATCH_OUTPUT_TOKEN");
|
||||
|
||||
// Just transfer the tokens if they're the same.
|
||||
if (state.path[0] == toTokenAddress) {
|
||||
LibERC20Token.transfer(state.path[0], to, amount);
|
||||
return BRIDGE_SUCCESS;
|
||||
}
|
||||
|
||||
// Get our balance of `fromTokenAddress` token.
|
||||
state.fromTokenBalance = IERC20Token(state.path[0]).balanceOf(address(this));
|
||||
|
||||
// Grant the SushiSwap router an allowance.
|
||||
LibERC20Token.approveIfBelow(
|
||||
state.path[0],
|
||||
state.router,
|
||||
state.fromTokenBalance
|
||||
);
|
||||
|
||||
// Buy as much `toTokenAddress` token with `fromTokenAddress` token
|
||||
// and transfer it to `to`.
|
||||
IUniswapV2Router01 router = IUniswapV2Router01(state.router);
|
||||
uint[] memory amounts = router.swapExactTokensForTokens(
|
||||
// Sell all tokens we hold.
|
||||
state.fromTokenBalance,
|
||||
// Minimum buy amount.
|
||||
amount,
|
||||
// Convert `fromTokenAddress` to `toTokenAddress`.
|
||||
state.path,
|
||||
// Recipient is `to`.
|
||||
to,
|
||||
// Expires after this block.
|
||||
block.timestamp
|
||||
);
|
||||
|
||||
emit ERC20BridgeTransfer(
|
||||
// input token
|
||||
state.path[0],
|
||||
// output token
|
||||
toTokenAddress,
|
||||
// input token amount
|
||||
state.fromTokenBalance,
|
||||
// output token amount
|
||||
amounts[amounts.length - 1],
|
||||
from,
|
||||
to
|
||||
);
|
||||
|
||||
return BRIDGE_SUCCESS;
|
||||
}
|
||||
|
||||
/// @dev `SignatureType.Wallet` callback, so that this bridge can be the maker
|
||||
/// and sign for itself in orders. Always succeeds.
|
||||
/// @return magicValue Success bytes, always.
|
||||
function isValidSignature(
|
||||
bytes32,
|
||||
bytes calldata
|
||||
)
|
||||
external
|
||||
view
|
||||
returns (bytes4 magicValue)
|
||||
{
|
||||
return LEGACY_WALLET_MAGIC_VALUE;
|
||||
}
|
||||
}
|
@ -38,7 +38,7 @@
|
||||
"docs:json": "typedoc --excludePrivate --excludeExternals --excludeProtected --ignoreCompilerErrors --target ES5 --tsconfig typedoc-tsconfig.json --json $JSON_FILE_PATH $PROJECT_FILES"
|
||||
},
|
||||
"config": {
|
||||
"abis": "./test/generated-artifacts/@(BalancerBridge|BancorBridge|ChaiBridge|CurveBridge|DexForwarderBridge|DydxBridge|ERC1155Proxy|ERC20BridgeProxy|ERC20Proxy|ERC721Proxy|Eth2DaiBridge|IAssetData|IAssetProxy|IAssetProxyDispatcher|IAuthorizable|IBalancerPool|IBancorNetwork|IChai|ICurve|IDydx|IDydxBridge|IERC20Bridge|IEth2Dai|IGasToken|IKyberNetworkProxy|IMStable|IMooniswap|IUniswapExchange|IUniswapExchangeFactory|IUniswapV2Router01|KyberBridge|MStableBridge|MixinAssetProxyDispatcher|MixinAuthorizable|MixinGasToken|MooniswapBridge|MultiAssetProxy|Ownable|StaticCallProxy|TestBancorBridge|TestChaiBridge|TestDexForwarderBridge|TestDydxBridge|TestERC20Bridge|TestEth2DaiBridge|TestKyberBridge|TestStaticCallTarget|TestUniswapBridge|TestUniswapV2Bridge|UniswapBridge|UniswapV2Bridge).json",
|
||||
"abis": "./test/generated-artifacts/@(BalancerBridge|BancorBridge|ChaiBridge|CurveBridge|DexForwarderBridge|DydxBridge|ERC1155Proxy|ERC20BridgeProxy|ERC20Proxy|ERC721Proxy|Eth2DaiBridge|IAssetData|IAssetProxy|IAssetProxyDispatcher|IAuthorizable|IBalancerPool|IBancorNetwork|IChai|ICurve|IDydx|IDydxBridge|IERC20Bridge|IEth2Dai|IGasToken|IKyberNetworkProxy|IMStable|IMooniswap|IUniswapExchange|IUniswapExchangeFactory|IUniswapV2Router01|KyberBridge|MStableBridge|MixinAssetProxyDispatcher|MixinAuthorizable|MixinGasToken|MooniswapBridge|MultiAssetProxy|Ownable|StaticCallProxy|SushiSwapBridge|TestBancorBridge|TestChaiBridge|TestDexForwarderBridge|TestDydxBridge|TestERC20Bridge|TestEth2DaiBridge|TestKyberBridge|TestStaticCallTarget|TestUniswapBridge|TestUniswapV2Bridge|UniswapBridge|UniswapV2Bridge).json",
|
||||
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually."
|
||||
},
|
||||
"repository": {
|
||||
|
@ -44,6 +44,7 @@ import * as MStableBridge from '../generated-artifacts/MStableBridge.json';
|
||||
import * as MultiAssetProxy from '../generated-artifacts/MultiAssetProxy.json';
|
||||
import * as Ownable from '../generated-artifacts/Ownable.json';
|
||||
import * as StaticCallProxy from '../generated-artifacts/StaticCallProxy.json';
|
||||
import * as SushiSwapBridge from '../generated-artifacts/SushiSwapBridge.json';
|
||||
import * as TestBancorBridge from '../generated-artifacts/TestBancorBridge.json';
|
||||
import * as TestChaiBridge from '../generated-artifacts/TestChaiBridge.json';
|
||||
import * as TestDexForwarderBridge from '../generated-artifacts/TestDexForwarderBridge.json';
|
||||
@ -77,6 +78,7 @@ export const artifacts = {
|
||||
MStableBridge: MStableBridge as ContractArtifact,
|
||||
MixinGasToken: MixinGasToken as ContractArtifact,
|
||||
MooniswapBridge: MooniswapBridge as ContractArtifact,
|
||||
SushiSwapBridge: SushiSwapBridge as ContractArtifact,
|
||||
UniswapBridge: UniswapBridge as ContractArtifact,
|
||||
UniswapV2Bridge: UniswapV2Bridge as ContractArtifact,
|
||||
IAssetData: IAssetData as ContractArtifact,
|
||||
|
@ -42,6 +42,7 @@ export * from '../generated-wrappers/mooniswap_bridge';
|
||||
export * from '../generated-wrappers/multi_asset_proxy';
|
||||
export * from '../generated-wrappers/ownable';
|
||||
export * from '../generated-wrappers/static_call_proxy';
|
||||
export * from '../generated-wrappers/sushi_swap_bridge';
|
||||
export * from '../generated-wrappers/test_bancor_bridge';
|
||||
export * from '../generated-wrappers/test_chai_bridge';
|
||||
export * from '../generated-wrappers/test_dex_forwarder_bridge';
|
||||
|
@ -44,6 +44,7 @@ import * as MStableBridge from '../test/generated-artifacts/MStableBridge.json';
|
||||
import * as MultiAssetProxy from '../test/generated-artifacts/MultiAssetProxy.json';
|
||||
import * as Ownable from '../test/generated-artifacts/Ownable.json';
|
||||
import * as StaticCallProxy from '../test/generated-artifacts/StaticCallProxy.json';
|
||||
import * as SushiSwapBridge from '../test/generated-artifacts/SushiSwapBridge.json';
|
||||
import * as TestBancorBridge from '../test/generated-artifacts/TestBancorBridge.json';
|
||||
import * as TestChaiBridge from '../test/generated-artifacts/TestChaiBridge.json';
|
||||
import * as TestDexForwarderBridge from '../test/generated-artifacts/TestDexForwarderBridge.json';
|
||||
@ -77,6 +78,7 @@ export const artifacts = {
|
||||
MStableBridge: MStableBridge as ContractArtifact,
|
||||
MixinGasToken: MixinGasToken as ContractArtifact,
|
||||
MooniswapBridge: MooniswapBridge as ContractArtifact,
|
||||
SushiSwapBridge: SushiSwapBridge as ContractArtifact,
|
||||
UniswapBridge: UniswapBridge as ContractArtifact,
|
||||
UniswapV2Bridge: UniswapV2Bridge as ContractArtifact,
|
||||
IAssetData: IAssetData as ContractArtifact,
|
||||
|
@ -42,6 +42,7 @@ export * from '../test/generated-wrappers/mooniswap_bridge';
|
||||
export * from '../test/generated-wrappers/multi_asset_proxy';
|
||||
export * from '../test/generated-wrappers/ownable';
|
||||
export * from '../test/generated-wrappers/static_call_proxy';
|
||||
export * from '../test/generated-wrappers/sushi_swap_bridge';
|
||||
export * from '../test/generated-wrappers/test_bancor_bridge';
|
||||
export * from '../test/generated-wrappers/test_chai_bridge';
|
||||
export * from '../test/generated-wrappers/test_dex_forwarder_bridge';
|
||||
|
@ -42,6 +42,7 @@
|
||||
"generated-artifacts/MultiAssetProxy.json",
|
||||
"generated-artifacts/Ownable.json",
|
||||
"generated-artifacts/StaticCallProxy.json",
|
||||
"generated-artifacts/SushiSwapBridge.json",
|
||||
"generated-artifacts/TestBancorBridge.json",
|
||||
"generated-artifacts/TestChaiBridge.json",
|
||||
"generated-artifacts/TestDexForwarderBridge.json",
|
||||
@ -93,6 +94,7 @@
|
||||
"test/generated-artifacts/MultiAssetProxy.json",
|
||||
"test/generated-artifacts/Ownable.json",
|
||||
"test/generated-artifacts/StaticCallProxy.json",
|
||||
"test/generated-artifacts/SushiSwapBridge.json",
|
||||
"test/generated-artifacts/TestBancorBridge.json",
|
||||
"test/generated-artifacts/TestChaiBridge.json",
|
||||
"test/generated-artifacts/TestDexForwarderBridge.json",
|
||||
|
@ -288,11 +288,11 @@ contract DeploymentConstants {
|
||||
}
|
||||
|
||||
/// @dev An overridable way to retrieve the Mooniswap registry address.
|
||||
/// @return musd The Mooniswap registry address.
|
||||
/// @return registry The Mooniswap registry address.
|
||||
function _getMooniswapAddress()
|
||||
internal
|
||||
view
|
||||
returns (address registry)
|
||||
returns (address)
|
||||
{
|
||||
return MOONISWAP_REGISTRY;
|
||||
}
|
||||
|
@ -101,6 +101,14 @@
|
||||
{
|
||||
"note": "Return Mooniswap pool in sampler and encode it in bridge data",
|
||||
"pr": 2692
|
||||
},
|
||||
{
|
||||
"note": "Added `Swerve`",
|
||||
"pr": 2698
|
||||
},
|
||||
{
|
||||
"note": "Added `SushiSwap`",
|
||||
"pr": 2698
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -28,9 +28,10 @@ import "./MultiBridgeSampler.sol";
|
||||
import "./MStableSampler.sol";
|
||||
import "./MooniswapSampler.sol";
|
||||
import "./NativeOrderSampler.sol";
|
||||
import "./SushiSwapSampler.sol";
|
||||
import "./TwoHopSampler.sol";
|
||||
import "./UniswapSampler.sol";
|
||||
import "./UniswapV2Sampler.sol";
|
||||
import "./TwoHopSampler.sol";
|
||||
|
||||
|
||||
contract ERC20BridgeSampler is
|
||||
@ -43,6 +44,7 @@ contract ERC20BridgeSampler is
|
||||
MooniswapSampler,
|
||||
MultiBridgeSampler,
|
||||
NativeOrderSampler,
|
||||
SushiSwapSampler,
|
||||
TwoHopSampler,
|
||||
UniswapSampler,
|
||||
UniswapV2Sampler
|
||||
|
103
packages/asset-swapper/contracts/src/SushiSwapSampler.sol
Normal file
103
packages/asset-swapper/contracts/src/SushiSwapSampler.sol
Normal file
@ -0,0 +1,103 @@
|
||||
/*
|
||||
|
||||
Copyright 2019 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.9;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "@0x/contracts-utils/contracts/src/DeploymentConstants.sol";
|
||||
import "./interfaces/IUniswapV2Router01.sol";
|
||||
|
||||
|
||||
contract SushiSwapSampler is
|
||||
DeploymentConstants
|
||||
{
|
||||
/// @dev Gas limit for SushiSwap calls.
|
||||
uint256 constant private SUSHISWAP_CALL_GAS = 150e3; // 150k
|
||||
|
||||
/// @dev Sample sell quotes from SushiSwap.
|
||||
/// @param router Router to look up tokens and amounts
|
||||
/// @param path Token route. Should be takerToken -> makerToken
|
||||
/// @param takerTokenAmounts Taker token sell amount for each sample.
|
||||
/// @return makerTokenAmounts Maker amounts bought at each taker token
|
||||
/// amount.
|
||||
function sampleSellsFromSushiSwap(
|
||||
address router,
|
||||
address[] memory path,
|
||||
uint256[] memory takerTokenAmounts
|
||||
)
|
||||
public
|
||||
view
|
||||
returns (uint256[] memory makerTokenAmounts)
|
||||
{
|
||||
uint256 numSamples = takerTokenAmounts.length;
|
||||
makerTokenAmounts = new uint256[](numSamples);
|
||||
for (uint256 i = 0; i < numSamples; i++) {
|
||||
(bool didSucceed, bytes memory resultData) =
|
||||
router.staticcall.gas(SUSHISWAP_CALL_GAS)(
|
||||
abi.encodeWithSelector(
|
||||
IUniswapV2Router01(0).getAmountsOut.selector,
|
||||
takerTokenAmounts[i],
|
||||
path
|
||||
));
|
||||
uint256 buyAmount = 0;
|
||||
if (didSucceed) {
|
||||
// solhint-disable-next-line indent
|
||||
buyAmount = abi.decode(resultData, (uint256[]))[path.length - 1];
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
makerTokenAmounts[i] = buyAmount;
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Sample buy quotes from SushiSwap
|
||||
/// @param router Router to look up tokens and amounts
|
||||
/// @param path Token route. Should be takerToken -> makerToken.
|
||||
/// @param makerTokenAmounts Maker token buy amount for each sample.
|
||||
/// @return takerTokenAmounts Taker amounts sold at each maker token
|
||||
/// amount.
|
||||
function sampleBuysFromSushiSwap(
|
||||
address router,
|
||||
address[] memory path,
|
||||
uint256[] memory makerTokenAmounts
|
||||
)
|
||||
public
|
||||
view
|
||||
returns (uint256[] memory takerTokenAmounts)
|
||||
{
|
||||
uint256 numSamples = makerTokenAmounts.length;
|
||||
takerTokenAmounts = new uint256[](numSamples);
|
||||
for (uint256 i = 0; i < numSamples; i++) {
|
||||
(bool didSucceed, bytes memory resultData) =
|
||||
router.staticcall.gas(SUSHISWAP_CALL_GAS)(
|
||||
abi.encodeWithSelector(
|
||||
IUniswapV2Router01(0).getAmountsIn.selector,
|
||||
makerTokenAmounts[i],
|
||||
path
|
||||
));
|
||||
uint256 sellAmount = 0;
|
||||
if (didSucceed) {
|
||||
// solhint-disable-next-line indent
|
||||
sellAmount = abi.decode(resultData, (uint256[]))[0];
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
takerTokenAmounts[i] = sellAmount;
|
||||
}
|
||||
}
|
||||
}
|
@ -38,7 +38,7 @@
|
||||
"config": {
|
||||
"publicInterfaceContracts": "ERC20BridgeSampler,ILiquidityProvider,ILiquidityProviderRegistry,DummyLiquidityProviderRegistry,DummyLiquidityProvider",
|
||||
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually.",
|
||||
"abis": "./test/generated-artifacts/@(ApproximateBuys|BalancerSampler|CurveSampler|DummyLiquidityProvider|DummyLiquidityProviderRegistry|ERC20BridgeSampler|Eth2DaiSampler|IBalancer|ICurve|IEth2Dai|IKyberNetwork|ILiquidityProvider|ILiquidityProviderRegistry|IMStable|IMooniswap|IMultiBridge|IUniswapExchangeQuotes|IUniswapV2Router01|KyberSampler|LiquidityProviderSampler|MStableSampler|MooniswapSampler|MultiBridgeSampler|NativeOrderSampler|SamplerUtils|TestERC20BridgeSampler|TestNativeOrderSampler|TwoHopSampler|UniswapSampler|UniswapV2Sampler).json",
|
||||
"abis": "./test/generated-artifacts/@(ApproximateBuys|BalancerSampler|CurveSampler|DummyLiquidityProvider|DummyLiquidityProviderRegistry|ERC20BridgeSampler|Eth2DaiSampler|IBalancer|ICurve|IEth2Dai|IKyberNetwork|ILiquidityProvider|ILiquidityProviderRegistry|IMStable|IMooniswap|IMultiBridge|IUniswapExchangeQuotes|IUniswapV2Router01|KyberSampler|LiquidityProviderSampler|MStableSampler|MooniswapSampler|MultiBridgeSampler|NativeOrderSampler|SamplerUtils|SushiSwapSampler|TestERC20BridgeSampler|TestNativeOrderSampler|TwoHopSampler|UniswapSampler|UniswapV2Sampler).json",
|
||||
"postpublish": {
|
||||
"assets": []
|
||||
}
|
||||
|
@ -99,6 +99,7 @@ export {
|
||||
MarketOperation,
|
||||
MarketSellSwapQuote,
|
||||
MockedRfqtFirmQuoteResponse,
|
||||
OrderPrunerPermittedFeeTypes,
|
||||
RfqtMakerAssetOfferings,
|
||||
RfqtRequestOpts,
|
||||
SamplerOverrides,
|
||||
@ -148,6 +149,9 @@ export {
|
||||
OptimizedMarketOrder,
|
||||
SourceInfo,
|
||||
SourceQuoteOperation,
|
||||
SushiSwapFillData,
|
||||
SwerveFillData,
|
||||
SwerveInfo,
|
||||
TokenAdjacencyGraph,
|
||||
UniswapV2FillData,
|
||||
} from './utils/market_operation_utils/types';
|
||||
|
@ -17,6 +17,8 @@ export const SELL_SOURCES = [
|
||||
// ERC20BridgeSource.Bancor, // FIXME: Disabled until Bancor SDK supports batch requests
|
||||
ERC20BridgeSource.MStable,
|
||||
ERC20BridgeSource.Mooniswap,
|
||||
ERC20BridgeSource.Swerve,
|
||||
ERC20BridgeSource.SushiSwap,
|
||||
];
|
||||
|
||||
/**
|
||||
@ -32,6 +34,8 @@ export const BUY_SOURCES = [
|
||||
// ERC20BridgeSource.Bancor, // FIXME: Disabled until Bancor SDK supports buy quotes
|
||||
ERC20BridgeSource.MStable,
|
||||
ERC20BridgeSource.Mooniswap,
|
||||
ERC20BridgeSource.Swerve,
|
||||
ERC20BridgeSource.SushiSwap,
|
||||
];
|
||||
|
||||
export const DEFAULT_GET_MARKET_ORDERS_OPTS: GetMarketOrdersOpts = {
|
||||
@ -132,6 +136,20 @@ export const MAINNET_CURVE_INFOS: { [name: string]: CurveInfo } = {
|
||||
],
|
||||
},
|
||||
};
|
||||
export const MAINNET_SWERVE_INFOS: { [name: string]: CurveInfo } = {
|
||||
swUSD: {
|
||||
exchangeFunctionSelector: CurveFunctionSelectors.exchange,
|
||||
sellQuoteFunctionSelector: CurveFunctionSelectors.get_dy_underlying,
|
||||
buyQuoteFunctionSelector: CurveFunctionSelectors.get_dx_underlying,
|
||||
poolAddress: '0x329239599afB305DA0A2eC69c58F8a6697F9F88d',
|
||||
tokens: [
|
||||
'0x6b175474e89094c44da98b954eedeac495271d0f',
|
||||
'0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48',
|
||||
'0xdac17f958d2ee523a2206206994597c13d831ec7',
|
||||
'0x0000000000085d4780b73119b644ae5ecd22b376',
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
export const MAINNET_KYBER_RESERVE_IDS: { [name: string]: string } = {
|
||||
Reserve1: '0xff4b796265722046707200000000000000000000000000000000000000000000',
|
||||
@ -169,6 +187,8 @@ export const MAINNET_KYBER_TOKEN_RESERVE_IDS: { [token: string]: string } = {
|
||||
'0xaa42414e44000000000000000000000000000000000000000000000000000000',
|
||||
};
|
||||
|
||||
export const MAINNET_SUSHI_SWAP_ROUTER = '0xd9e1cE17f2641f24aE83637ab66a2cca9C378B9F';
|
||||
|
||||
export const ERC20_PROXY_ID = '0xf47261b0';
|
||||
export const WALLET_SIGNATURE = '0x04';
|
||||
export const ONE_ETHER = new BigNumber(1e18);
|
||||
|
@ -1,7 +1,11 @@
|
||||
import { MAINNET_CURVE_INFOS } from './constants';
|
||||
import { CurveInfo } from './types';
|
||||
import { MAINNET_CURVE_INFOS, MAINNET_SWERVE_INFOS } from './constants';
|
||||
import { CurveInfo, SwerveInfo } from './types';
|
||||
|
||||
// tslint:disable completed-docs
|
||||
export function getCurveInfosForPair(takerToken: string, makerToken: string): CurveInfo[] {
|
||||
return Object.values(MAINNET_CURVE_INFOS).filter(c => [makerToken, takerToken].every(t => c.tokens.includes(t)));
|
||||
}
|
||||
|
||||
export function getSwerveInfosForPair(takerToken: string, makerToken: string): SwerveInfo[] {
|
||||
return Object.values(MAINNET_SWERVE_INFOS).filter(c => [makerToken, takerToken].every(t => c.tokens.includes(t)));
|
||||
}
|
||||
|
@ -35,6 +35,8 @@ import {
|
||||
NativeCollapsedFill,
|
||||
OptimizedMarketOrder,
|
||||
OrderDomain,
|
||||
SushiSwapFillData,
|
||||
SwerveFillData,
|
||||
UniswapV2FillData,
|
||||
} from './types';
|
||||
|
||||
@ -222,8 +224,12 @@ function getBridgeAddressFromFill(fill: CollapsedFill, opts: CreateOrderFromPath
|
||||
return opts.contractAddresses.uniswapBridge;
|
||||
case ERC20BridgeSource.UniswapV2:
|
||||
return opts.contractAddresses.uniswapV2Bridge;
|
||||
case ERC20BridgeSource.SushiSwap:
|
||||
return opts.contractAddresses.sushiswapBridge;
|
||||
case ERC20BridgeSource.Curve:
|
||||
return opts.contractAddresses.curveBridge;
|
||||
case ERC20BridgeSource.Swerve:
|
||||
return opts.contractAddresses.curveBridge;
|
||||
case ERC20BridgeSource.Bancor:
|
||||
return opts.contractAddresses.bancorBridge;
|
||||
case ERC20BridgeSource.Balancer:
|
||||
@ -266,6 +272,20 @@ function createBridgeOrder(
|
||||
),
|
||||
);
|
||||
break;
|
||||
case ERC20BridgeSource.Swerve:
|
||||
const swerveFillData = (fill as CollapsedFill<SwerveFillData>).fillData!; // tslint:disable-line:no-non-null-assertion
|
||||
makerAssetData = assetDataUtils.encodeERC20BridgeAssetData(
|
||||
makerToken,
|
||||
bridgeAddress,
|
||||
createCurveBridgeData(
|
||||
swerveFillData.pool.poolAddress,
|
||||
swerveFillData.pool.exchangeFunctionSelector,
|
||||
takerToken,
|
||||
swerveFillData.fromTokenIdx,
|
||||
swerveFillData.toTokenIdx,
|
||||
),
|
||||
);
|
||||
break;
|
||||
case ERC20BridgeSource.Balancer:
|
||||
const balancerFillData = (fill as CollapsedFill<BalancerFillData>).fillData!; // tslint:disable-line:no-non-null-assertion
|
||||
makerAssetData = assetDataUtils.encodeERC20BridgeAssetData(
|
||||
@ -290,6 +310,14 @@ function createBridgeOrder(
|
||||
createUniswapV2BridgeData(uniswapV2FillData.tokenAddressPath),
|
||||
);
|
||||
break;
|
||||
case ERC20BridgeSource.SushiSwap:
|
||||
const sushiSwapFillData = (fill as CollapsedFill<SushiSwapFillData>).fillData!; // tslint:disable-line:no-non-null-assertion
|
||||
makerAssetData = assetDataUtils.encodeERC20BridgeAssetData(
|
||||
makerToken,
|
||||
bridgeAddress,
|
||||
createSushiSwapBridgeData(sushiSwapFillData.tokenAddressPath, sushiSwapFillData.router),
|
||||
);
|
||||
break;
|
||||
case ERC20BridgeSource.MultiBridge:
|
||||
makerAssetData = assetDataUtils.encodeERC20BridgeAssetData(
|
||||
makerToken,
|
||||
@ -431,25 +459,24 @@ function createCurveBridgeData(
|
||||
fromTokenIdx: number,
|
||||
toTokenIdx: number,
|
||||
): string {
|
||||
const curveBridgeDataEncoder = AbiEncoder.create([
|
||||
const encoder = AbiEncoder.create([
|
||||
{ name: 'curveAddress', type: 'address' },
|
||||
{ name: 'exchangeFunctionSelector', type: 'bytes4' },
|
||||
{ name: 'fromTokenAddress', type: 'address' },
|
||||
{ name: 'fromTokenIdx', type: 'int128' },
|
||||
{ name: 'toTokenIdx', type: 'int128' },
|
||||
]);
|
||||
return curveBridgeDataEncoder.encode([
|
||||
curveAddress,
|
||||
exchangeFunctionSelector,
|
||||
takerToken,
|
||||
fromTokenIdx,
|
||||
toTokenIdx,
|
||||
]);
|
||||
return encoder.encode([curveAddress, exchangeFunctionSelector, takerToken, fromTokenIdx, toTokenIdx]);
|
||||
}
|
||||
|
||||
function createUniswapV2BridgeData(tokenAddressPath: string[]): string {
|
||||
const uniswapV2BridgeDataEncoder = AbiEncoder.create('(address[])');
|
||||
return uniswapV2BridgeDataEncoder.encode([tokenAddressPath]);
|
||||
const encoder = AbiEncoder.create('(address[])');
|
||||
return encoder.encode([tokenAddressPath]);
|
||||
}
|
||||
|
||||
function createSushiSwapBridgeData(tokenAddressPath: string[], router: string): string {
|
||||
const encoder = AbiEncoder.create('(address[],address)');
|
||||
return encoder.encode([tokenAddressPath, router]);
|
||||
}
|
||||
|
||||
function getSlippedBridgeAssetAmounts(fill: CollapsedFill, opts: CreateOrderFromPathOpts): [BigNumber, BigNumber] {
|
||||
|
@ -6,8 +6,8 @@ import { ERC20BridgeSamplerContract } from '../../wrappers';
|
||||
|
||||
import { BalancerPoolsCache, computeBalancerBuyQuote, computeBalancerSellQuote } from './balancer_utils';
|
||||
import { BancorService } from './bancor_service';
|
||||
import { MAX_UINT256, NULL_BYTES, ZERO_AMOUNT } from './constants';
|
||||
import { getCurveInfosForPair } from './curve_utils';
|
||||
import { MAINNET_SUSHI_SWAP_ROUTER, MAX_UINT256, NULL_BYTES, ZERO_AMOUNT } from './constants';
|
||||
import { getCurveInfosForPair, getSwerveInfosForPair } from './curve_utils';
|
||||
import { getKyberReserveIdsForPair } from './kyber_utils';
|
||||
import { getMultiBridgeIntermediateToken } from './multibridge_utils';
|
||||
import { getIntermediateTokens } from './multihop_utils';
|
||||
@ -27,6 +27,9 @@ import {
|
||||
MultiBridgeFillData,
|
||||
MultiHopFillData,
|
||||
SourceQuoteOperation,
|
||||
SushiSwapFillData,
|
||||
SwerveFillData,
|
||||
SwerveInfo,
|
||||
TokenAdjacencyGraph,
|
||||
UniswapV2FillData,
|
||||
} from './types';
|
||||
@ -315,6 +318,62 @@ export class SamplerOperations {
|
||||
});
|
||||
}
|
||||
|
||||
public getSwerveSellQuotes(
|
||||
pool: SwerveInfo,
|
||||
fromTokenIdx: number,
|
||||
toTokenIdx: number,
|
||||
takerFillAmounts: BigNumber[],
|
||||
): SourceQuoteOperation<SwerveFillData> {
|
||||
return new SamplerContractOperation({
|
||||
source: ERC20BridgeSource.Swerve,
|
||||
fillData: {
|
||||
pool,
|
||||
fromTokenIdx,
|
||||
toTokenIdx,
|
||||
},
|
||||
contract: this._samplerContract,
|
||||
function: this._samplerContract.sampleSellsFromCurve,
|
||||
params: [
|
||||
{
|
||||
poolAddress: pool.poolAddress,
|
||||
sellQuoteFunctionSelector: pool.sellQuoteFunctionSelector,
|
||||
buyQuoteFunctionSelector: pool.buyQuoteFunctionSelector,
|
||||
},
|
||||
new BigNumber(fromTokenIdx),
|
||||
new BigNumber(toTokenIdx),
|
||||
takerFillAmounts,
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
public getSwerveBuyQuotes(
|
||||
pool: SwerveInfo,
|
||||
fromTokenIdx: number,
|
||||
toTokenIdx: number,
|
||||
makerFillAmounts: BigNumber[],
|
||||
): SourceQuoteOperation<SwerveFillData> {
|
||||
return new SamplerContractOperation({
|
||||
source: ERC20BridgeSource.Swerve,
|
||||
fillData: {
|
||||
pool,
|
||||
fromTokenIdx,
|
||||
toTokenIdx,
|
||||
},
|
||||
contract: this._samplerContract,
|
||||
function: this._samplerContract.sampleBuysFromCurve,
|
||||
params: [
|
||||
{
|
||||
poolAddress: pool.poolAddress,
|
||||
sellQuoteFunctionSelector: pool.sellQuoteFunctionSelector,
|
||||
buyQuoteFunctionSelector: pool.buyQuoteFunctionSelector,
|
||||
},
|
||||
new BigNumber(fromTokenIdx),
|
||||
new BigNumber(toTokenIdx),
|
||||
makerFillAmounts,
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
public getBalancerSellQuotes(
|
||||
poolAddress: string,
|
||||
makerToken: string,
|
||||
@ -621,6 +680,32 @@ export class SamplerOperations {
|
||||
};
|
||||
}
|
||||
|
||||
public getSushiSwapSellQuotes(
|
||||
tokenAddressPath: string[],
|
||||
takerFillAmounts: BigNumber[],
|
||||
): SourceQuoteOperation<SushiSwapFillData> {
|
||||
return new SamplerContractOperation({
|
||||
source: ERC20BridgeSource.SushiSwap,
|
||||
fillData: { tokenAddressPath, router: MAINNET_SUSHI_SWAP_ROUTER },
|
||||
contract: this._samplerContract,
|
||||
function: this._samplerContract.sampleSellsFromSushiSwap,
|
||||
params: [MAINNET_SUSHI_SWAP_ROUTER, tokenAddressPath, takerFillAmounts],
|
||||
});
|
||||
}
|
||||
|
||||
public getSushiSwapBuyQuotes(
|
||||
tokenAddressPath: string[],
|
||||
makerFillAmounts: BigNumber[],
|
||||
): SourceQuoteOperation<SushiSwapFillData> {
|
||||
return new SamplerContractOperation({
|
||||
source: ERC20BridgeSource.SushiSwap,
|
||||
fillData: { tokenAddressPath, router: MAINNET_SUSHI_SWAP_ROUTER },
|
||||
contract: this._samplerContract,
|
||||
function: this._samplerContract.sampleBuysFromSushiSwap,
|
||||
params: [MAINNET_SUSHI_SWAP_ROUTER, tokenAddressPath, makerFillAmounts],
|
||||
});
|
||||
}
|
||||
|
||||
public getMedianSellRate(
|
||||
sources: ERC20BridgeSource[],
|
||||
makerToken: string,
|
||||
@ -784,6 +869,17 @@ export class SamplerOperations {
|
||||
);
|
||||
}
|
||||
return ops;
|
||||
case ERC20BridgeSource.SushiSwap:
|
||||
const sushiOps = [this.getSushiSwapSellQuotes([takerToken, makerToken], takerFillAmounts)];
|
||||
if (takerToken !== wethAddress && makerToken !== wethAddress) {
|
||||
sushiOps.push(
|
||||
this.getSushiSwapSellQuotes(
|
||||
[takerToken, wethAddress, makerToken],
|
||||
takerFillAmounts,
|
||||
),
|
||||
);
|
||||
}
|
||||
return sushiOps;
|
||||
case ERC20BridgeSource.Kyber:
|
||||
return getKyberReserveIdsForPair(takerToken, makerToken).map(reserveId =>
|
||||
this.getKyberSellQuotes(reserveId, makerToken, takerToken, takerFillAmounts),
|
||||
@ -797,6 +893,15 @@ export class SamplerOperations {
|
||||
takerFillAmounts,
|
||||
),
|
||||
);
|
||||
case ERC20BridgeSource.Swerve:
|
||||
return getSwerveInfosForPair(takerToken, makerToken).map(pool =>
|
||||
this.getSwerveSellQuotes(
|
||||
pool,
|
||||
pool.tokens.indexOf(takerToken),
|
||||
pool.tokens.indexOf(makerToken),
|
||||
takerFillAmounts,
|
||||
),
|
||||
);
|
||||
case ERC20BridgeSource.LiquidityProvider:
|
||||
if (liquidityProviderRegistryAddress === undefined) {
|
||||
throw new Error(
|
||||
@ -865,6 +970,14 @@ export class SamplerOperations {
|
||||
);
|
||||
}
|
||||
return ops;
|
||||
case ERC20BridgeSource.SushiSwap:
|
||||
const sushiOps = [this.getSushiSwapBuyQuotes([takerToken, makerToken], makerFillAmounts)];
|
||||
if (takerToken !== wethAddress && makerToken !== wethAddress) {
|
||||
sushiOps.push(
|
||||
this.getSushiSwapBuyQuotes([takerToken, wethAddress, makerToken], makerFillAmounts),
|
||||
);
|
||||
}
|
||||
return sushiOps;
|
||||
case ERC20BridgeSource.Kyber:
|
||||
return getKyberReserveIdsForPair(takerToken, makerToken).map(reserveId =>
|
||||
this.getKyberBuyQuotes(reserveId, makerToken, takerToken, makerFillAmounts),
|
||||
@ -878,6 +991,15 @@ export class SamplerOperations {
|
||||
makerFillAmounts,
|
||||
),
|
||||
);
|
||||
case ERC20BridgeSource.Swerve:
|
||||
return getSwerveInfosForPair(takerToken, makerToken).map(pool =>
|
||||
this.getSwerveBuyQuotes(
|
||||
pool,
|
||||
pool.tokens.indexOf(takerToken),
|
||||
pool.tokens.indexOf(makerToken),
|
||||
makerFillAmounts,
|
||||
),
|
||||
);
|
||||
case ERC20BridgeSource.LiquidityProvider:
|
||||
if (liquidityProviderRegistryAddress === undefined) {
|
||||
throw new Error(
|
||||
|
@ -41,6 +41,8 @@ export enum ERC20BridgeSource {
|
||||
MStable = 'mStable',
|
||||
Mooniswap = 'Mooniswap',
|
||||
MultiHop = 'MultiHop',
|
||||
Swerve = 'Swerve',
|
||||
SushiSwap = 'SushiSwap',
|
||||
}
|
||||
|
||||
// tslint:disable: enum-naming
|
||||
@ -69,6 +71,8 @@ export interface CurveInfo {
|
||||
tokens: string[];
|
||||
}
|
||||
|
||||
export interface SwerveInfo extends CurveInfo {}
|
||||
|
||||
// Internal `fillData` field for `Fill` objects.
|
||||
export interface FillData {}
|
||||
|
||||
@ -88,6 +92,12 @@ export interface CurveFillData extends FillData {
|
||||
curve: CurveInfo;
|
||||
}
|
||||
|
||||
export interface SwerveFillData extends FillData {
|
||||
fromTokenIdx: number;
|
||||
toTokenIdx: number;
|
||||
pool: SwerveInfo;
|
||||
}
|
||||
|
||||
export interface BalancerFillData extends FillData {
|
||||
poolAddress: string;
|
||||
}
|
||||
@ -96,6 +106,10 @@ export interface UniswapV2FillData extends FillData {
|
||||
tokenAddressPath: string[];
|
||||
}
|
||||
|
||||
export interface SushiSwapFillData extends UniswapV2FillData {
|
||||
router: string;
|
||||
}
|
||||
|
||||
export interface LiquidityProviderFillData extends FillData {
|
||||
poolAddress: string;
|
||||
}
|
||||
@ -103,6 +117,7 @@ export interface LiquidityProviderFillData extends FillData {
|
||||
export interface MultiBridgeFillData extends FillData {
|
||||
poolAddress: string;
|
||||
}
|
||||
|
||||
export interface BancorFillData extends FillData {
|
||||
path: string[];
|
||||
networkAddress: string;
|
||||
|
@ -30,6 +30,7 @@ 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 SamplerUtils from '../test/generated-artifacts/SamplerUtils.json';
|
||||
import * as SushiSwapSampler from '../test/generated-artifacts/SushiSwapSampler.json';
|
||||
import * as TestERC20BridgeSampler from '../test/generated-artifacts/TestERC20BridgeSampler.json';
|
||||
import * as TestNativeOrderSampler from '../test/generated-artifacts/TestNativeOrderSampler.json';
|
||||
import * as TwoHopSampler from '../test/generated-artifacts/TwoHopSampler.json';
|
||||
@ -49,6 +50,7 @@ export const artifacts = {
|
||||
MultiBridgeSampler: MultiBridgeSampler as ContractArtifact,
|
||||
NativeOrderSampler: NativeOrderSampler as ContractArtifact,
|
||||
SamplerUtils: SamplerUtils as ContractArtifact,
|
||||
SushiSwapSampler: SushiSwapSampler as ContractArtifact,
|
||||
TwoHopSampler: TwoHopSampler as ContractArtifact,
|
||||
UniswapSampler: UniswapSampler as ContractArtifact,
|
||||
UniswapV2Sampler: UniswapV2Sampler as ContractArtifact,
|
||||
|
@ -27,6 +27,16 @@ const MAKER_TOKEN = randomAddress();
|
||||
const TAKER_TOKEN = randomAddress();
|
||||
const MAKER_ASSET_DATA = assetDataUtils.encodeERC20AssetData(MAKER_TOKEN);
|
||||
const TAKER_ASSET_DATA = assetDataUtils.encodeERC20AssetData(TAKER_TOKEN);
|
||||
const DEFAULT_EXCLUDED = [
|
||||
ERC20BridgeSource.UniswapV2,
|
||||
ERC20BridgeSource.Curve,
|
||||
ERC20BridgeSource.Balancer,
|
||||
ERC20BridgeSource.MStable,
|
||||
ERC20BridgeSource.Mooniswap,
|
||||
ERC20BridgeSource.Bancor,
|
||||
ERC20BridgeSource.Swerve,
|
||||
ERC20BridgeSource.SushiSwap,
|
||||
];
|
||||
|
||||
// tslint:disable: custom-no-magic-numbers promise-function-async
|
||||
describe('MarketOperationUtils tests', () => {
|
||||
@ -80,6 +90,8 @@ describe('MarketOperationUtils tests', () => {
|
||||
return ERC20BridgeSource.MStable;
|
||||
case contractAddresses.mooniswapBridge.toLowerCase():
|
||||
return ERC20BridgeSource.Mooniswap;
|
||||
case contractAddresses.sushiswapBridge.toLowerCase():
|
||||
return ERC20BridgeSource.SushiSwap;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -238,11 +250,11 @@ describe('MarketOperationUtils tests', () => {
|
||||
[source: string]: Numberish[];
|
||||
}
|
||||
|
||||
const DEFAULT_RATES: RatesBySource = {
|
||||
[ERC20BridgeSource.Native]: createDecreasingRates(NUM_SAMPLES),
|
||||
[ERC20BridgeSource.Eth2Dai]: createDecreasingRates(NUM_SAMPLES),
|
||||
[ERC20BridgeSource.Kyber]: createDecreasingRates(NUM_SAMPLES),
|
||||
[ERC20BridgeSource.Uniswap]: createDecreasingRates(NUM_SAMPLES),
|
||||
const ZERO_RATES: RatesBySource = {
|
||||
[ERC20BridgeSource.Native]: _.times(NUM_SAMPLES, () => 0),
|
||||
[ERC20BridgeSource.Eth2Dai]: _.times(NUM_SAMPLES, () => 0),
|
||||
[ERC20BridgeSource.Uniswap]: _.times(NUM_SAMPLES, () => 0),
|
||||
[ERC20BridgeSource.Kyber]: _.times(NUM_SAMPLES, () => 0),
|
||||
[ERC20BridgeSource.UniswapV2]: _.times(NUM_SAMPLES, () => 0),
|
||||
[ERC20BridgeSource.Balancer]: _.times(NUM_SAMPLES, () => 0),
|
||||
[ERC20BridgeSource.Bancor]: _.times(NUM_SAMPLES, () => 0),
|
||||
@ -251,6 +263,15 @@ describe('MarketOperationUtils tests', () => {
|
||||
[ERC20BridgeSource.MultiBridge]: _.times(NUM_SAMPLES, () => 0),
|
||||
[ERC20BridgeSource.MStable]: _.times(NUM_SAMPLES, () => 0),
|
||||
[ERC20BridgeSource.Mooniswap]: _.times(NUM_SAMPLES, () => 0),
|
||||
[ERC20BridgeSource.Swerve]: _.times(NUM_SAMPLES, () => 0),
|
||||
[ERC20BridgeSource.SushiSwap]: _.times(NUM_SAMPLES, () => 0),
|
||||
};
|
||||
|
||||
const DEFAULT_RATES: RatesBySource = {
|
||||
...ZERO_RATES,
|
||||
[ERC20BridgeSource.Native]: createDecreasingRates(NUM_SAMPLES),
|
||||
[ERC20BridgeSource.Eth2Dai]: createDecreasingRates(NUM_SAMPLES),
|
||||
[ERC20BridgeSource.Uniswap]: createDecreasingRates(NUM_SAMPLES),
|
||||
};
|
||||
|
||||
interface FillDataBySource {
|
||||
@ -273,7 +294,19 @@ describe('MarketOperationUtils tests', () => {
|
||||
fromTokenIdx: 0,
|
||||
toTokenIdx: 1,
|
||||
},
|
||||
[ERC20BridgeSource.Swerve]: {
|
||||
pool: {
|
||||
poolAddress: randomAddress(),
|
||||
tokens: [TAKER_TOKEN, MAKER_TOKEN],
|
||||
exchangeFunctionSelector: hexUtils.random(4),
|
||||
sellQuoteFunctionSelector: hexUtils.random(4),
|
||||
buyQuoteFunctionSelector: hexUtils.random(4),
|
||||
},
|
||||
fromTokenIdx: 0,
|
||||
toTokenIdx: 1,
|
||||
},
|
||||
[ERC20BridgeSource.LiquidityProvider]: { poolAddress: randomAddress() },
|
||||
[ERC20BridgeSource.SushiSwap]: { tokenAddressPath: [] },
|
||||
};
|
||||
|
||||
const DEFAULT_OPS = {
|
||||
@ -416,14 +449,7 @@ describe('MarketOperationUtils tests', () => {
|
||||
sampleDistributionBase: 1,
|
||||
bridgeSlippage: 0,
|
||||
maxFallbackSlippage: 100,
|
||||
excludedSources: [
|
||||
ERC20BridgeSource.UniswapV2,
|
||||
ERC20BridgeSource.Curve,
|
||||
ERC20BridgeSource.Balancer,
|
||||
ERC20BridgeSource.MStable,
|
||||
ERC20BridgeSource.Mooniswap,
|
||||
ERC20BridgeSource.Bancor,
|
||||
],
|
||||
excludedSources: DEFAULT_EXCLUDED,
|
||||
allowFallback: false,
|
||||
shouldBatchBridgeOrders: false,
|
||||
};
|
||||
@ -583,7 +609,7 @@ describe('MarketOperationUtils tests', () => {
|
||||
});
|
||||
|
||||
it('can mix convex sources', async () => {
|
||||
const rates: RatesBySource = {};
|
||||
const rates: RatesBySource = { ...DEFAULT_RATES };
|
||||
rates[ERC20BridgeSource.Native] = [0.4, 0.3, 0.2, 0.1];
|
||||
rates[ERC20BridgeSource.Uniswap] = [0.5, 0.05, 0.05, 0.05];
|
||||
rates[ERC20BridgeSource.Eth2Dai] = [0.6, 0.05, 0.05, 0.05];
|
||||
@ -852,14 +878,7 @@ describe('MarketOperationUtils tests', () => {
|
||||
sampleDistributionBase: 1,
|
||||
bridgeSlippage: 0,
|
||||
maxFallbackSlippage: 100,
|
||||
excludedSources: [
|
||||
ERC20BridgeSource.Kyber,
|
||||
ERC20BridgeSource.UniswapV2,
|
||||
ERC20BridgeSource.Curve,
|
||||
ERC20BridgeSource.Balancer,
|
||||
ERC20BridgeSource.MStable,
|
||||
ERC20BridgeSource.Mooniswap,
|
||||
],
|
||||
excludedSources: DEFAULT_EXCLUDED,
|
||||
allowFallback: false,
|
||||
shouldBatchBridgeOrders: false,
|
||||
};
|
||||
@ -1019,7 +1038,7 @@ describe('MarketOperationUtils tests', () => {
|
||||
});
|
||||
|
||||
it('can mix convex sources', async () => {
|
||||
const rates: RatesBySource = {};
|
||||
const rates: RatesBySource = { ...ZERO_RATES };
|
||||
rates[ERC20BridgeSource.Native] = [0.4, 0.3, 0.2, 0.1];
|
||||
rates[ERC20BridgeSource.Uniswap] = [0.5, 0.05, 0.05, 0.05];
|
||||
rates[ERC20BridgeSource.Eth2Dai] = [0.6, 0.05, 0.05, 0.05];
|
||||
@ -1049,6 +1068,7 @@ describe('MarketOperationUtils tests', () => {
|
||||
// dropping their effective rates.
|
||||
const nativeFeeRate = 0.06;
|
||||
const rates: RatesBySource = {
|
||||
...ZERO_RATES,
|
||||
[ERC20BridgeSource.Native]: [1, 0.99, 0.98, 0.97], // Effectively [0.94, ~0.93, ~0.92, ~0.91]
|
||||
[ERC20BridgeSource.Uniswap]: [0.96, 0.1, 0.1, 0.1],
|
||||
[ERC20BridgeSource.Eth2Dai]: [0.95, 0.1, 0.1, 0.1],
|
||||
@ -1086,6 +1106,7 @@ describe('MarketOperationUtils tests', () => {
|
||||
// dropping its effective rates.
|
||||
const uniswapFeeRate = 0.2;
|
||||
const rates: RatesBySource = {
|
||||
...ZERO_RATES,
|
||||
[ERC20BridgeSource.Native]: [0.95, 0.1, 0.1, 0.1],
|
||||
// Effectively [0.8, ~0.5, ~0, ~0]
|
||||
[ERC20BridgeSource.Uniswap]: [1, 0.7, 0.2, 0.2],
|
||||
@ -1118,7 +1139,7 @@ describe('MarketOperationUtils tests', () => {
|
||||
});
|
||||
|
||||
it('fallback orders use different sources', async () => {
|
||||
const rates: RatesBySource = {};
|
||||
const rates: RatesBySource = { ...ZERO_RATES };
|
||||
rates[ERC20BridgeSource.Native] = [0.9, 0.8, 0.5, 0.5];
|
||||
rates[ERC20BridgeSource.Uniswap] = [0.6, 0.05, 0.01, 0.01];
|
||||
rates[ERC20BridgeSource.Eth2Dai] = [0.4, 0.3, 0.01, 0.01];
|
||||
@ -1138,7 +1159,7 @@ describe('MarketOperationUtils tests', () => {
|
||||
});
|
||||
|
||||
it('does not create a fallback if below maxFallbackSlippage', async () => {
|
||||
const rates: RatesBySource = {};
|
||||
const rates: RatesBySource = { ...ZERO_RATES };
|
||||
rates[ERC20BridgeSource.Native] = [1, 1, 0.01, 0.01];
|
||||
rates[ERC20BridgeSource.Uniswap] = [1, 1, 0.01, 0.01];
|
||||
rates[ERC20BridgeSource.Eth2Dai] = [0.49, 0.49, 0.49, 0.49];
|
||||
@ -1159,7 +1180,7 @@ describe('MarketOperationUtils tests', () => {
|
||||
});
|
||||
|
||||
it('batches contiguous bridge sources', async () => {
|
||||
const rates: RatesBySource = {};
|
||||
const rates: RatesBySource = { ...ZERO_RATES };
|
||||
rates[ERC20BridgeSource.Native] = [0.5, 0.01, 0.01, 0.01];
|
||||
rates[ERC20BridgeSource.Eth2Dai] = [0.49, 0.02, 0.01, 0.01];
|
||||
rates[ERC20BridgeSource.Uniswap] = [0.48, 0.01, 0.01, 0.01];
|
||||
|
@ -28,6 +28,7 @@ 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/sampler_utils';
|
||||
export * from '../test/generated-wrappers/sushi_swap_sampler';
|
||||
export * from '../test/generated-wrappers/test_erc20_bridge_sampler';
|
||||
export * from '../test/generated-wrappers/test_native_order_sampler';
|
||||
export * from '../test/generated-wrappers/two_hop_sampler';
|
||||
|
@ -33,6 +33,7 @@
|
||||
"test/generated-artifacts/MultiBridgeSampler.json",
|
||||
"test/generated-artifacts/NativeOrderSampler.json",
|
||||
"test/generated-artifacts/SamplerUtils.json",
|
||||
"test/generated-artifacts/SushiSwapSampler.json",
|
||||
"test/generated-artifacts/TestERC20BridgeSampler.json",
|
||||
"test/generated-artifacts/TestNativeOrderSampler.json",
|
||||
"test/generated-artifacts/TwoHopSampler.json",
|
||||
|
@ -42,6 +42,7 @@
|
||||
"exchangeProxyFlashWallet": "0x22f9dcf4647084d6c31b2765f6910cd85c178c18",
|
||||
"mStableBridge": "0x2bf04fcea05f0989a14d9afa37aa376baca6b2b3",
|
||||
"mooniswapBridge": "0x02b7eca484ad960fca3f7709e0b2ac81eec3069c",
|
||||
"sushiswapBridge": "0x47ed0262a0b688dcb836d254c6a2e96b6c48a9f5",
|
||||
"transformers": {
|
||||
"wethTransformer": "0x68c0bb685099dc7cb5c5ce2b26185945b357383e",
|
||||
"payTakerTransformer": "0x49b9df2c58491764cf40cb052dd4243df63622c7",
|
||||
@ -92,6 +93,7 @@
|
||||
"exchangeProxyFlashWallet": "0x22f9dcf4647084d6c31b2765f6910cd85c178c18",
|
||||
"mStableBridge": "0x0000000000000000000000000000000000000000",
|
||||
"mooniswapBridge": "0x0000000000000000000000000000000000000000",
|
||||
"sushiswapBridge": "0x0000000000000000000000000000000000000000",
|
||||
"transformers": {
|
||||
"wethTransformer": "0x8d822fe2b42f60531203e288f5f357fa79474437",
|
||||
"payTakerTransformer": "0x150652244723102faeaefa4c79597d097ffa26c6",
|
||||
@ -142,6 +144,7 @@
|
||||
"exchangeProxyFlashWallet": "0x22f9dcf4647084d6c31b2765f6910cd85c178c18",
|
||||
"mStableBridge": "0x0000000000000000000000000000000000000000",
|
||||
"mooniswapBridge": "0x0000000000000000000000000000000000000000",
|
||||
"sushiswapBridge": "0x0000000000000000000000000000000000000000",
|
||||
"transformers": {
|
||||
"wethTransformer": "0x8d822fe2b42f60531203e288f5f357fa79474437",
|
||||
"payTakerTransformer": "0x150652244723102faeaefa4c79597d097ffa26c6",
|
||||
@ -192,6 +195,7 @@
|
||||
"exchangeProxyFlashWallet": "0x22f9dcf4647084d6c31b2765f6910cd85c178c18",
|
||||
"mStableBridge": "0x0000000000000000000000000000000000000000",
|
||||
"mooniswapBridge": "0x0000000000000000000000000000000000000000",
|
||||
"sushiswapBridge": "0x0000000000000000000000000000000000000000",
|
||||
"transformers": {
|
||||
"wethTransformer": "0x9ce35b5ee9e710535e3988e3f8731d9ca9dba17d",
|
||||
"payTakerTransformer": "0x5a53e7b02a83aa9f60ccf4e424f0442c255bc977",
|
||||
@ -242,6 +246,7 @@
|
||||
"exchangeProxyFlashWallet": "0xb9682a8e7920b431f1d412b8510f0077410c8faa",
|
||||
"mStableBridge": "0x0000000000000000000000000000000000000000",
|
||||
"mooniswapBridge": "0x0000000000000000000000000000000000000000",
|
||||
"sushiswapBridge": "0x0000000000000000000000000000000000000000",
|
||||
"transformers": {
|
||||
"wethTransformer": "0xc6b0d3c45a6b5092808196cb00df5c357d55e1d5",
|
||||
"payTakerTransformer": "0x7209185959d7227fb77274e1e88151d7c4c368d3",
|
||||
|
@ -43,6 +43,7 @@ export interface ContractAddresses {
|
||||
exchangeProxyFlashWallet: string;
|
||||
mStableBridge: string;
|
||||
mooniswapBridge: string;
|
||||
sushiswapBridge: string;
|
||||
transformers: {
|
||||
wethTransformer: string;
|
||||
payTakerTransformer: string;
|
||||
|
@ -400,6 +400,7 @@ export async function runMigrationsAsync(
|
||||
exchangeProxyGovernor: NULL_ADDRESS,
|
||||
mStableBridge: NULL_ADDRESS,
|
||||
mooniswapBridge: NULL_ADDRESS,
|
||||
sushiswapBridge: NULL_ADDRESS,
|
||||
exchangeProxy: exchangeProxy.address,
|
||||
exchangeProxyAllowanceTarget: exchangeProxyAllowanceTargetAddress,
|
||||
exchangeProxyTransformerDeployer: txDefaults.from,
|
||||
|
@ -70,6 +70,7 @@ export const docGenConfigs: DocGenConfigs = {
|
||||
'MultiAssetData',
|
||||
'StaticCallAssetData',
|
||||
'MultiAssetDataWithRecursiveDecoding',
|
||||
'OrderPrunerPermittedFeeTypes',
|
||||
],
|
||||
// Some libraries only export types. In those cases, we cannot check if the exported types are part of the
|
||||
// "exported public interface". Thus we add them here and skip those checks.
|
||||
|
Loading…
x
Reference in New Issue
Block a user