MultiBridge support in AssetSwapper
This commit is contained in:
parent
0fbbabe208
commit
44262bf747
@ -21,6 +21,10 @@
|
||||
{
|
||||
"note": "Add UniswapV2",
|
||||
"pr": 2595
|
||||
},
|
||||
{
|
||||
"note": "Sample from MultiBridge",
|
||||
"pr": 2593
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -35,6 +35,7 @@ import "./ICurve.sol";
|
||||
import "./ILiquidityProvider.sol";
|
||||
import "./ILiquidityProviderRegistry.sol";
|
||||
import "./IUniswapV2Router01.sol";
|
||||
import "./IMultiBridge.sol";
|
||||
|
||||
|
||||
contract ERC20BridgeSampler is
|
||||
@ -55,7 +56,7 @@ contract ERC20BridgeSampler is
|
||||
/// So a reasonable ceil is 150k per token. Biggest Curve has 4 tokens.
|
||||
uint256 constant internal CURVE_CALL_GAS = 600e3; // 600k
|
||||
/// @dev Default gas limit for liquidity provider calls.
|
||||
uint256 constant internal DEFAULT_CALL_GAS = 200e3; // 200k
|
||||
uint256 constant internal DEFAULT_CALL_GAS = 400e3; // 400k
|
||||
/// @dev The Kyber Uniswap Reserve address
|
||||
address constant internal KYBER_UNIWAP_RESERVE = 0x31E085Afd48a1d6e51Cc193153d625e8f0514C7F;
|
||||
/// @dev The Kyber Eth2Dai Reserve address
|
||||
@ -593,6 +594,56 @@ contract ERC20BridgeSampler is
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Sample sell quotes from MultiBridge.
|
||||
/// @param multibridge Address of the MultiBridge contract.
|
||||
/// @param takerToken Address of the taker token (what to sell).
|
||||
/// @param intermediateToken The address of the intermediate token to
|
||||
/// use in an indirect route.
|
||||
/// @param makerToken Address of the maker token (what to buy).
|
||||
/// @param takerTokenAmounts Taker token sell amount for each sample.
|
||||
/// @return makerTokenAmounts Maker amounts bought at each taker token
|
||||
/// amount.
|
||||
function sampleSellsFromMultiBridge(
|
||||
address multibridge,
|
||||
address takerToken,
|
||||
address intermediateToken,
|
||||
address makerToken,
|
||||
uint256[] memory takerTokenAmounts
|
||||
)
|
||||
public
|
||||
view
|
||||
returns (uint256[] memory makerTokenAmounts)
|
||||
{
|
||||
// Initialize array of maker token amounts.
|
||||
uint256 numSamples = takerTokenAmounts.length;
|
||||
makerTokenAmounts = new uint256[](numSamples);
|
||||
|
||||
// If no address provided, return all zeros.
|
||||
if (multibridge == address(0)) {
|
||||
return makerTokenAmounts;
|
||||
}
|
||||
|
||||
for (uint256 i = 0; i < numSamples; i++) {
|
||||
(bool didSucceed, bytes memory resultData) =
|
||||
multibridge.staticcall.gas(DEFAULT_CALL_GAS)(
|
||||
abi.encodeWithSelector(
|
||||
IMultiBridge(0).getSellQuote.selector,
|
||||
takerToken,
|
||||
intermediateToken,
|
||||
makerToken,
|
||||
takerTokenAmounts[i]
|
||||
));
|
||||
uint256 buyAmount = 0;
|
||||
if (didSucceed) {
|
||||
buyAmount = abi.decode(resultData, (uint256));
|
||||
} else {
|
||||
// Exit early if the amount is too high for the liquidity provider to serve
|
||||
break;
|
||||
}
|
||||
makerTokenAmounts[i] = buyAmount;
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Sample buy quotes from an arbitrary on-chain liquidity provider.
|
||||
/// @param registryAddress Address of the liquidity provider registry contract.
|
||||
/// @param takerToken Address of the taker token (what to sell).
|
||||
|
@ -206,6 +206,26 @@ interface IERC20BridgeSampler {
|
||||
view
|
||||
returns (uint256[] memory makerTokenAmounts);
|
||||
|
||||
/// @dev Sample sell quotes from MultiBridge.
|
||||
/// @param multibridge Address of the MultiBridge contract.
|
||||
/// @param takerToken Address of the taker token (what to sell).
|
||||
/// @param intermediateToken The address of the intermediate token to
|
||||
/// use in an indirect route.
|
||||
/// @param makerToken Address of the maker token (what to buy).
|
||||
/// @param takerTokenAmounts Taker token sell amount for each sample.
|
||||
/// @return makerTokenAmounts Maker amounts bought at each taker token
|
||||
/// amount.
|
||||
function sampleSellsFromMultiBridge(
|
||||
address multibridge,
|
||||
address takerToken,
|
||||
address intermediateToken,
|
||||
address makerToken,
|
||||
uint256[] calldata takerTokenAmounts
|
||||
)
|
||||
external
|
||||
view
|
||||
returns (uint256[] memory makerTokenAmounts);
|
||||
|
||||
/// @dev Sample buy quotes from an arbitrary on-chain liquidity provider.
|
||||
/// @param registryAddress Address of the liquidity provider registry contract.
|
||||
/// @param takerToken Address of the taker token (what to sell).
|
||||
|
@ -0,0 +1,58 @@
|
||||
/*
|
||||
|
||||
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;
|
||||
|
||||
|
||||
interface IMultiBridge {
|
||||
|
||||
/// @dev Transfers `amount` of the ERC20 `tokenAddress` from `from` to `to`.
|
||||
/// @param tokenAddress The address of the ERC20 token to transfer.
|
||||
/// @param from Address to transfer asset from.
|
||||
/// @param to Address to transfer asset to.
|
||||
/// @param amount Amount of asset to transfer.
|
||||
/// @param bridgeData Arbitrary asset data needed by the bridge contract.
|
||||
/// @return success The magic bytes `0xdc1600f3` if successful.
|
||||
function bridgeTransferFrom(
|
||||
address tokenAddress,
|
||||
address from,
|
||||
address to,
|
||||
uint256 amount,
|
||||
bytes calldata bridgeData
|
||||
)
|
||||
external
|
||||
returns (bytes4 success);
|
||||
|
||||
/// @dev Quotes the amount of `makerToken` that would be obtained by
|
||||
/// selling `sellAmount` of `takerToken`.
|
||||
/// @param takerToken Address of the taker token (what to sell).
|
||||
/// @param intermediateToken The address of the intermediate token to
|
||||
/// use in an indirect route.
|
||||
/// @param makerToken Address of the maker token (what to buy).
|
||||
/// @param sellAmount Amount of `takerToken` to sell.
|
||||
/// @return makerTokenAmount Amount of `makerToken` that would be obtained.
|
||||
function getSellQuote(
|
||||
address takerToken,
|
||||
address intermediateToken,
|
||||
address makerToken,
|
||||
uint256 sellAmount
|
||||
)
|
||||
external
|
||||
view
|
||||
returns (uint256 makerTokenAmount);
|
||||
}
|
@ -38,7 +38,7 @@
|
||||
"config": {
|
||||
"publicInterfaceContracts": "ERC20BridgeSampler,IERC20BridgeSampler,ILiquidityProvider,ILiquidityProviderRegistry,DummyLiquidityProviderRegistry,DummyLiquidityProvider",
|
||||
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually.",
|
||||
"abis": "./test/generated-artifacts/@(DummyLiquidityProvider|DummyLiquidityProviderRegistry|ERC20BridgeSampler|ICurve|IDevUtils|IERC20BridgeSampler|IEth2Dai|IKyberNetwork|IKyberNetworkProxy|ILiquidityProvider|ILiquidityProviderRegistry|IUniswapExchangeQuotes|IUniswapV2Router01|TestERC20BridgeSampler).json"
|
||||
"abis": "./test/generated-artifacts/@(DummyLiquidityProvider|DummyLiquidityProviderRegistry|ERC20BridgeSampler|ICurve|IDevUtils|IERC20BridgeSampler|IEth2Dai|IKyberNetwork|IKyberNetworkProxy|ILiquidityProvider|ILiquidityProviderRegistry|IMultiBridge|IUniswapExchangeQuotes|IUniswapV2Router01|TestERC20BridgeSampler).json"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
@ -16,6 +16,7 @@ import * as IKyberNetwork from '../test/generated-artifacts/IKyberNetwork.json';
|
||||
import * as IKyberNetworkProxy from '../test/generated-artifacts/IKyberNetworkProxy.json';
|
||||
import * as ILiquidityProvider from '../test/generated-artifacts/ILiquidityProvider.json';
|
||||
import * as ILiquidityProviderRegistry from '../test/generated-artifacts/ILiquidityProviderRegistry.json';
|
||||
import * as IMultiBridge from '../test/generated-artifacts/IMultiBridge.json';
|
||||
import * as IUniswapExchangeQuotes from '../test/generated-artifacts/IUniswapExchangeQuotes.json';
|
||||
import * as IUniswapV2Router01 from '../test/generated-artifacts/IUniswapV2Router01.json';
|
||||
import * as TestERC20BridgeSampler from '../test/generated-artifacts/TestERC20BridgeSampler.json';
|
||||
@ -31,6 +32,7 @@ export const artifacts = {
|
||||
IKyberNetworkProxy: IKyberNetworkProxy as ContractArtifact,
|
||||
ILiquidityProvider: ILiquidityProvider as ContractArtifact,
|
||||
ILiquidityProviderRegistry: ILiquidityProviderRegistry as ContractArtifact,
|
||||
IMultiBridge: IMultiBridge as ContractArtifact,
|
||||
IUniswapExchangeQuotes: IUniswapExchangeQuotes as ContractArtifact,
|
||||
IUniswapV2Router01: IUniswapV2Router01 as ContractArtifact,
|
||||
TestERC20BridgeSampler: TestERC20BridgeSampler as ContractArtifact,
|
||||
|
@ -14,6 +14,7 @@ export * from '../test/generated-wrappers/i_kyber_network';
|
||||
export * from '../test/generated-wrappers/i_kyber_network_proxy';
|
||||
export * from '../test/generated-wrappers/i_liquidity_provider';
|
||||
export * from '../test/generated-wrappers/i_liquidity_provider_registry';
|
||||
export * from '../test/generated-wrappers/i_multi_bridge';
|
||||
export * from '../test/generated-wrappers/i_uniswap_exchange_quotes';
|
||||
export * from '../test/generated-wrappers/i_uniswap_v2_router01';
|
||||
export * from '../test/generated-wrappers/test_erc20_bridge_sampler';
|
||||
|
@ -20,6 +20,7 @@
|
||||
"test/generated-artifacts/IKyberNetworkProxy.json",
|
||||
"test/generated-artifacts/ILiquidityProvider.json",
|
||||
"test/generated-artifacts/ILiquidityProviderRegistry.json",
|
||||
"test/generated-artifacts/IMultiBridge.json",
|
||||
"test/generated-artifacts/IUniswapExchangeQuotes.json",
|
||||
"test/generated-artifacts/IUniswapV2Router01.json",
|
||||
"test/generated-artifacts/TestERC20BridgeSampler.json"
|
||||
|
@ -81,6 +81,10 @@
|
||||
{
|
||||
"note": "Add support for Uniswap V2",
|
||||
"pr": 2599
|
||||
},
|
||||
{
|
||||
"note": "Add support for MultiBridge",
|
||||
"pr": 2593
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -233,6 +233,7 @@ export interface SwapQuoterOpts extends OrderPrunerOpts {
|
||||
contractAddresses?: ContractAddresses;
|
||||
samplerGasLimit?: number;
|
||||
liquidityProviderRegistryAddress?: string;
|
||||
multiBridgeAddress?: string;
|
||||
rfqt?: {
|
||||
takerApiKeyWhitelist: string[];
|
||||
makerAssetOfferings: RfqtMakerAssetOfferings;
|
||||
|
@ -158,10 +158,16 @@ function dexQuotesToPaths(
|
||||
}
|
||||
|
||||
function sourceToFillFlags(source: ERC20BridgeSource): number {
|
||||
if (source === ERC20BridgeSource.Kyber) {
|
||||
return FillFlags.Kyber;
|
||||
switch (source) {
|
||||
case ERC20BridgeSource.Uniswap:
|
||||
return FillFlags.ConflictsWithMultiBridge;
|
||||
case ERC20BridgeSource.LiquidityProvider:
|
||||
return FillFlags.ConflictsWithMultiBridge;
|
||||
case ERC20BridgeSource.MultiBridge:
|
||||
return FillFlags.MultiBridge;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
export function getPathSize(path: Fill[], targetInput: BigNumber = POSITIVE_INF): [BigNumber, BigNumber] {
|
||||
@ -217,8 +223,8 @@ export function isValidPath(path: Fill[], skipDuplicateCheck: boolean = false):
|
||||
}
|
||||
flags |= path[i].flags;
|
||||
}
|
||||
const conflictFlags = FillFlags.Kyber | FillFlags.ConflictsWithKyber;
|
||||
return (flags & conflictFlags) !== conflictFlags;
|
||||
const multiBridgeConflict = FillFlags.MultiBridge | FillFlags.ConflictsWithMultiBridge;
|
||||
return (flags & multiBridgeConflict) !== multiBridgeConflict;
|
||||
}
|
||||
|
||||
export function clipPathToInput(path: Fill[], targetInput: BigNumber = POSITIVE_INF): Fill[] {
|
||||
|
@ -47,6 +47,7 @@ async function getRfqtIndicativeQuotesAsync(
|
||||
|
||||
export class MarketOperationUtils {
|
||||
private readonly _wethAddress: string;
|
||||
private readonly _multiBridge: string;
|
||||
|
||||
constructor(
|
||||
private readonly _sampler: DexOrderSampler,
|
||||
@ -55,6 +56,7 @@ export class MarketOperationUtils {
|
||||
private readonly _liquidityProviderRegistry: string = NULL_ADDRESS,
|
||||
) {
|
||||
this._wethAddress = contractAddresses.etherToken.toLowerCase();
|
||||
this._multiBridge = contractAddresses.multiBridge.toLowerCase();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -75,6 +77,11 @@ export class MarketOperationUtils {
|
||||
}
|
||||
const _opts = { ...DEFAULT_GET_MARKET_ORDERS_OPTS, ...opts };
|
||||
const [makerToken, takerToken] = getNativeOrderTokens(nativeOrders[0]);
|
||||
const optionalSources = (this._liquidityProviderRegistry !== NULL_ADDRESS
|
||||
? [ERC20BridgeSource.LiquidityProvider]
|
||||
: []
|
||||
).concat(this._multiBridge !== NULL_ADDRESS ? [ERC20BridgeSource.MultiBridge] : []);
|
||||
|
||||
// Call the sampler contract.
|
||||
const samplerPromise = this._sampler.executeAsync(
|
||||
// Get native order fillable amounts.
|
||||
@ -87,25 +94,23 @@ export class MarketOperationUtils {
|
||||
),
|
||||
// Get ETH -> maker token price.
|
||||
DexOrderSampler.ops.getMedianSellRate(
|
||||
difference(FEE_QUOTE_SOURCES, _opts.excludedSources).concat(
|
||||
this._liquidityProviderSourceIfAvailable(_opts.excludedSources),
|
||||
),
|
||||
difference(FEE_QUOTE_SOURCES.concat(optionalSources), _opts.excludedSources),
|
||||
makerToken,
|
||||
this._wethAddress,
|
||||
ONE_ETHER,
|
||||
this._wethAddress,
|
||||
this._liquidityProviderRegistry,
|
||||
this._multiBridge,
|
||||
),
|
||||
// Get sell quotes for taker -> maker.
|
||||
DexOrderSampler.ops.getSellQuotes(
|
||||
difference(SELL_SOURCES, _opts.excludedSources).concat(
|
||||
this._liquidityProviderSourceIfAvailable(_opts.excludedSources),
|
||||
),
|
||||
difference(SELL_SOURCES.concat(optionalSources), _opts.excludedSources),
|
||||
makerToken,
|
||||
takerToken,
|
||||
getSampleAmounts(takerAmount, _opts.numSamples, _opts.sampleDistributionBase),
|
||||
this._wethAddress,
|
||||
this._liquidityProviderRegistry,
|
||||
this._multiBridge,
|
||||
),
|
||||
);
|
||||
const rfqtPromise = getRfqtIndicativeQuotesAsync(
|
||||
@ -125,6 +130,7 @@ export class MarketOperationUtils {
|
||||
dexQuotes,
|
||||
rfqtIndicativeQuotes,
|
||||
liquidityProviderAddress,
|
||||
multiBridgeAddress: this._multiBridge,
|
||||
inputToken: takerToken,
|
||||
outputToken: makerToken,
|
||||
side: MarketOperation.Sell,
|
||||
@ -157,6 +163,10 @@ export class MarketOperationUtils {
|
||||
}
|
||||
const _opts = { ...DEFAULT_GET_MARKET_ORDERS_OPTS, ...opts };
|
||||
const [makerToken, takerToken] = getNativeOrderTokens(nativeOrders[0]);
|
||||
const optionalSources = (this._liquidityProviderRegistry !== NULL_ADDRESS
|
||||
? [ERC20BridgeSource.LiquidityProvider]
|
||||
: []
|
||||
).concat(this._multiBridge !== NULL_ADDRESS ? [ERC20BridgeSource.MultiBridge] : []);
|
||||
// Call the sampler contract.
|
||||
const samplerPromise = this._sampler.executeAsync(
|
||||
// Get native order fillable amounts.
|
||||
@ -169,19 +179,21 @@ export class MarketOperationUtils {
|
||||
),
|
||||
// Get ETH -> taker token price.
|
||||
DexOrderSampler.ops.getMedianSellRate(
|
||||
difference(FEE_QUOTE_SOURCES, _opts.excludedSources).concat(
|
||||
this._liquidityProviderSourceIfAvailable(_opts.excludedSources),
|
||||
),
|
||||
difference(FEE_QUOTE_SOURCES.concat(optionalSources), _opts.excludedSources),
|
||||
takerToken,
|
||||
this._wethAddress,
|
||||
ONE_ETHER,
|
||||
this._wethAddress,
|
||||
this._liquidityProviderRegistry,
|
||||
this._multiBridge,
|
||||
),
|
||||
// Get buy quotes for taker -> maker.
|
||||
DexOrderSampler.ops.getBuyQuotes(
|
||||
difference(BUY_SOURCES, _opts.excludedSources).concat(
|
||||
this._liquidityProviderSourceIfAvailable(_opts.excludedSources),
|
||||
difference(
|
||||
BUY_SOURCES.concat(
|
||||
this._liquidityProviderRegistry !== NULL_ADDRESS ? [ERC20BridgeSource.LiquidityProvider] : [],
|
||||
),
|
||||
_opts.excludedSources,
|
||||
),
|
||||
makerToken,
|
||||
takerToken,
|
||||
@ -208,6 +220,7 @@ export class MarketOperationUtils {
|
||||
dexQuotes,
|
||||
rfqtIndicativeQuotes,
|
||||
liquidityProviderAddress,
|
||||
multiBridgeAddress: this._multiBridge,
|
||||
inputToken: makerToken,
|
||||
outputToken: takerToken,
|
||||
side: MarketOperation.Buy,
|
||||
@ -324,6 +337,7 @@ export class MarketOperationUtils {
|
||||
allowFallback?: boolean;
|
||||
shouldBatchBridgeOrders?: boolean;
|
||||
liquidityProviderAddress?: string;
|
||||
multiBridgeAddress?: string;
|
||||
}): OptimizedMarketOrder[] {
|
||||
const { inputToken, outputToken, side, inputAmount } = opts;
|
||||
const maxFallbackSlippage = opts.maxFallbackSlippage || 0;
|
||||
@ -385,16 +399,10 @@ export class MarketOperationUtils {
|
||||
contractAddresses: this.contractAddresses,
|
||||
bridgeSlippage: opts.bridgeSlippage || 0,
|
||||
liquidityProviderAddress: opts.liquidityProviderAddress,
|
||||
multiBridgeAddress: opts.multiBridgeAddress,
|
||||
shouldBatchBridgeOrders: !!opts.shouldBatchBridgeOrders,
|
||||
});
|
||||
}
|
||||
|
||||
private _liquidityProviderSourceIfAvailable(excludedSources: ERC20BridgeSource[]): ERC20BridgeSource[] {
|
||||
return this._liquidityProviderRegistry !== NULL_ADDRESS &&
|
||||
!excludedSources.includes(ERC20BridgeSource.LiquidityProvider)
|
||||
? [ERC20BridgeSource.LiquidityProvider]
|
||||
: [];
|
||||
}
|
||||
}
|
||||
|
||||
// tslint:disable: max-file-line-count
|
||||
|
@ -0,0 +1,20 @@
|
||||
import { NULL_ADDRESS } from './constants';
|
||||
|
||||
// tslint:disable completed-docs
|
||||
|
||||
export const TOKENS = {
|
||||
WETH: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2',
|
||||
DAI: '0x6b175474e89094c44da98b954eedeac495271d0f',
|
||||
USDC: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48',
|
||||
MKR: '0x9f8f72aa9304c8b593d555f12ef6589cc3a579a2',
|
||||
};
|
||||
|
||||
export function getMultiBridgeIntermediateToken(takerToken: string, makerToken: string): string {
|
||||
let intermediateToken = NULL_ADDRESS;
|
||||
if (takerToken !== TOKENS.WETH && makerToken !== TOKENS.WETH) {
|
||||
intermediateToken = TOKENS.WETH;
|
||||
} else if (takerToken === TOKENS.USDC || makerToken === TOKENS.USDC) {
|
||||
intermediateToken = TOKENS.DAI;
|
||||
}
|
||||
return intermediateToken;
|
||||
}
|
@ -17,6 +17,7 @@ import {
|
||||
ZERO_AMOUNT,
|
||||
} from './constants';
|
||||
import { collapsePath } from './fills';
|
||||
import { getMultiBridgeIntermediateToken } from './multibridge_utils';
|
||||
import {
|
||||
AggregationError,
|
||||
CollapsedFill,
|
||||
@ -141,6 +142,7 @@ export interface CreateOrderFromPathOpts {
|
||||
bridgeSlippage: number;
|
||||
shouldBatchBridgeOrders: boolean;
|
||||
liquidityProviderAddress?: string;
|
||||
multiBridgeAddress?: string;
|
||||
}
|
||||
|
||||
// Convert sell fills into orders.
|
||||
@ -195,6 +197,11 @@ function getBridgeAddressFromSource(source: ERC20BridgeSource, opts: CreateOrder
|
||||
throw new Error('Cannot create a LiquidityProvider order without a LiquidityProvider pool address.');
|
||||
}
|
||||
return opts.liquidityProviderAddress;
|
||||
case ERC20BridgeSource.MultiBridge:
|
||||
if (opts.multiBridgeAddress === undefined) {
|
||||
throw new Error('Cannot create a MultiBridge order without a MultiBridge address.');
|
||||
}
|
||||
return opts.multiBridgeAddress;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -242,6 +249,13 @@ function createBridgeOrder(fill: CollapsedFill, opts: CreateOrderFromPathOpts):
|
||||
createUniswapV2BridgeData([makerToken, opts.contractAddresses.etherToken, takerToken]),
|
||||
);
|
||||
break;
|
||||
case ERC20BridgeSource.MultiBridge:
|
||||
makerAssetData = assetDataUtils.encodeERC20BridgeAssetData(
|
||||
makerToken,
|
||||
bridgeAddress,
|
||||
createMultiBridgeData(takerToken, makerToken),
|
||||
);
|
||||
break;
|
||||
default:
|
||||
makerAssetData = assetDataUtils.encodeERC20BridgeAssetData(
|
||||
makerToken,
|
||||
@ -315,6 +329,15 @@ function createBridgeData(tokenAddress: string): string {
|
||||
return encoder.encode({ tokenAddress });
|
||||
}
|
||||
|
||||
function createMultiBridgeData(takerToken: string, makerToken: string): string {
|
||||
const intermediateToken = getMultiBridgeIntermediateToken(takerToken, makerToken);
|
||||
const encoder = AbiEncoder.create([
|
||||
{ name: 'takerToken', type: 'address' },
|
||||
{ name: 'intermediateToken', type: 'address' },
|
||||
]);
|
||||
return encoder.encode({ takerToken, intermediateToken });
|
||||
}
|
||||
|
||||
function createCurveBridgeData(
|
||||
curveAddress: string,
|
||||
fromTokenIdx: number,
|
||||
|
@ -2,6 +2,7 @@ import { BigNumber, ERC20BridgeSource, SignedOrder } from '../..';
|
||||
import { getCurveInfo, isCurveSource } from '../source_utils';
|
||||
|
||||
import { DEFAULT_FAKE_BUY_OPTS } from './constants';
|
||||
import { getMultiBridgeIntermediateToken } from './multibridge_utils';
|
||||
import { BatchedOperation, DexSample, FakeBuyOpts } from './types';
|
||||
|
||||
/**
|
||||
@ -123,7 +124,7 @@ export const samplerOperations = {
|
||||
};
|
||||
},
|
||||
getLiquidityProviderSellQuotes(
|
||||
liquidityProviderRegistryAddress: string,
|
||||
registryAddress: string,
|
||||
makerToken: string,
|
||||
takerToken: string,
|
||||
takerFillAmounts: BigNumber[],
|
||||
@ -131,9 +132,31 @@ export const samplerOperations = {
|
||||
return {
|
||||
encodeCall: contract => {
|
||||
return contract
|
||||
.sampleSellsFromLiquidityProviderRegistry(
|
||||
liquidityProviderRegistryAddress,
|
||||
.sampleSellsFromLiquidityProviderRegistry(registryAddress, takerToken, makerToken, takerFillAmounts)
|
||||
.getABIEncodedTransactionData();
|
||||
},
|
||||
handleCallResultsAsync: async (contract, callResults) => {
|
||||
return contract.getABIDecodedReturnData<BigNumber[]>(
|
||||
'sampleSellsFromLiquidityProviderRegistry',
|
||||
callResults,
|
||||
);
|
||||
},
|
||||
};
|
||||
},
|
||||
getMultiBridgeSellQuotes(
|
||||
multiBridgeAddress: string,
|
||||
makerToken: string,
|
||||
intermediateToken: string,
|
||||
takerToken: string,
|
||||
takerFillAmounts: BigNumber[],
|
||||
): BatchedOperation<BigNumber[]> {
|
||||
return {
|
||||
encodeCall: contract => {
|
||||
return contract
|
||||
.sampleSellsFromMultiBridge(
|
||||
multiBridgeAddress,
|
||||
takerToken,
|
||||
intermediateToken,
|
||||
makerToken,
|
||||
takerFillAmounts,
|
||||
)
|
||||
@ -148,7 +171,7 @@ export const samplerOperations = {
|
||||
};
|
||||
},
|
||||
getLiquidityProviderBuyQuotes(
|
||||
liquidityProviderRegistryAddress: string,
|
||||
registryAddress: string,
|
||||
makerToken: string,
|
||||
takerToken: string,
|
||||
makerFillAmounts: BigNumber[],
|
||||
@ -158,7 +181,7 @@ export const samplerOperations = {
|
||||
encodeCall: contract => {
|
||||
return contract
|
||||
.sampleBuysFromLiquidityProviderRegistry(
|
||||
liquidityProviderRegistryAddress,
|
||||
registryAddress,
|
||||
takerToken,
|
||||
makerToken,
|
||||
makerFillAmounts,
|
||||
@ -256,7 +279,8 @@ export const samplerOperations = {
|
||||
takerToken: string,
|
||||
takerFillAmount: BigNumber,
|
||||
wethAddress: string,
|
||||
liquidityProviderRegistryAddress?: string | undefined,
|
||||
liquidityProviderRegistryAddress?: string,
|
||||
multiBridgeAddress?: string,
|
||||
): BatchedOperation<BigNumber> {
|
||||
if (makerToken.toLowerCase() === takerToken.toLowerCase()) {
|
||||
return samplerOperations.constant(new BigNumber(1));
|
||||
@ -268,6 +292,7 @@ export const samplerOperations = {
|
||||
[takerFillAmount],
|
||||
wethAddress,
|
||||
liquidityProviderRegistryAddress,
|
||||
multiBridgeAddress,
|
||||
);
|
||||
return {
|
||||
encodeCall: contract => {
|
||||
@ -324,7 +349,8 @@ export const samplerOperations = {
|
||||
takerToken: string,
|
||||
takerFillAmounts: BigNumber[],
|
||||
wethAddress: string,
|
||||
liquidityProviderRegistryAddress?: string | undefined,
|
||||
liquidityProviderRegistryAddress?: string,
|
||||
multiBridgeAddress?: string,
|
||||
): BatchedOperation<DexSample[][]> {
|
||||
const subOps = sources
|
||||
.map(source => {
|
||||
@ -367,6 +393,18 @@ export const samplerOperations = {
|
||||
takerToken,
|
||||
takerFillAmounts,
|
||||
);
|
||||
} else if (source === ERC20BridgeSource.MultiBridge) {
|
||||
if (multiBridgeAddress === undefined) {
|
||||
throw new Error('Cannot sample liquidity from MultiBridge if an address is not provided.');
|
||||
}
|
||||
const intermediateToken = getMultiBridgeIntermediateToken(takerToken, makerToken);
|
||||
batchedOperation = samplerOperations.getMultiBridgeSellQuotes(
|
||||
multiBridgeAddress,
|
||||
makerToken,
|
||||
intermediateToken,
|
||||
takerToken,
|
||||
takerFillAmounts,
|
||||
);
|
||||
} else {
|
||||
throw new Error(`Unsupported sell sample source: ${source}`);
|
||||
}
|
||||
@ -404,7 +442,7 @@ export const samplerOperations = {
|
||||
takerToken: string,
|
||||
makerFillAmounts: BigNumber[],
|
||||
wethAddress: string,
|
||||
liquidityProviderRegistryAddress?: string | undefined,
|
||||
liquidityProviderRegistryAddress?: string,
|
||||
fakeBuyOpts: FakeBuyOpts = DEFAULT_FAKE_BUY_OPTS,
|
||||
): BatchedOperation<DexSample[][]> {
|
||||
const subOps = sources
|
||||
|
@ -38,6 +38,7 @@ export enum ERC20BridgeSource {
|
||||
CurveUsdcDaiUsdtBusd = 'Curve_USDC_DAI_USDT_BUSD',
|
||||
CurveUsdcDaiUsdtSusd = 'Curve_USDC_DAI_USDT_SUSD',
|
||||
LiquidityProvider = 'LiquidityProvider',
|
||||
MultiBridge = 'MultiBridge',
|
||||
}
|
||||
|
||||
// Internal `fillData` field for `Fill` objects.
|
||||
@ -67,6 +68,8 @@ export interface DexSample {
|
||||
export enum FillFlags {
|
||||
ConflictsWithKyber = 0x1,
|
||||
Kyber = 0x2,
|
||||
ConflictsWithMultiBridge = 0x4,
|
||||
MultiBridge = 0x8,
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -212,6 +212,48 @@ describe('DexSampler tests', () => {
|
||||
]);
|
||||
});
|
||||
|
||||
it('getMultiBridgeSellQuotes()', async () => {
|
||||
const expectedTakerToken = randomAddress();
|
||||
const expectedMakerToken = randomAddress();
|
||||
const multiBridge = randomAddress();
|
||||
|
||||
const sampler = new MockSamplerContract({
|
||||
sampleSellsFromMultiBridge: (
|
||||
multiBridgeAddress,
|
||||
takerToken,
|
||||
_intermediateToken,
|
||||
makerToken,
|
||||
_fillAmounts,
|
||||
) => {
|
||||
expect(multiBridgeAddress).to.eq(multiBridge);
|
||||
expect(takerToken).to.eq(expectedTakerToken);
|
||||
expect(makerToken).to.eq(expectedMakerToken);
|
||||
return [toBaseUnitAmount(1001)];
|
||||
},
|
||||
});
|
||||
const dexOrderSampler = new DexOrderSampler(sampler);
|
||||
const [result] = await dexOrderSampler.executeAsync(
|
||||
DexOrderSampler.ops.getSellQuotes(
|
||||
[ERC20BridgeSource.MultiBridge],
|
||||
expectedMakerToken,
|
||||
expectedTakerToken,
|
||||
[toBaseUnitAmount(1000)],
|
||||
randomAddress(),
|
||||
randomAddress(),
|
||||
multiBridge,
|
||||
),
|
||||
);
|
||||
expect(result).to.deep.equal([
|
||||
[
|
||||
{
|
||||
source: 'MultiBridge',
|
||||
output: toBaseUnitAmount(1001),
|
||||
input: toBaseUnitAmount(1000),
|
||||
},
|
||||
],
|
||||
]);
|
||||
});
|
||||
|
||||
it('getEth2DaiSellQuotes()', async () => {
|
||||
const expectedTakerToken = randomAddress();
|
||||
const expectedMakerToken = randomAddress();
|
||||
|
@ -30,7 +30,7 @@ import { DexSample, ERC20BridgeSource, NativeFillData } from '../src/utils/marke
|
||||
// tslint:disable: custom-no-magic-numbers
|
||||
describe('MarketOperationUtils tests', () => {
|
||||
const CHAIN_ID = 1;
|
||||
const contractAddresses = getContractAddressesForChainOrThrow(CHAIN_ID);
|
||||
const contractAddresses = { ...getContractAddressesForChainOrThrow(CHAIN_ID), multiBridge: NULL_ADDRESS };
|
||||
const ETH2DAI_BRIDGE_ADDRESS = contractAddresses.eth2DaiBridge;
|
||||
const KYBER_BRIDGE_ADDRESS = contractAddresses.kyberBridge;
|
||||
const UNISWAP_BRIDGE_ADDRESS = contractAddresses.uniswapBridge;
|
||||
@ -259,7 +259,9 @@ describe('MarketOperationUtils tests', () => {
|
||||
const fn = (registryAddress: string, takerToken: string, makerToken: string): string => {
|
||||
callArgs.makerToken = makerToken;
|
||||
callArgs.takerToken = takerToken;
|
||||
callArgs.registryAddress = registryAddress;
|
||||
if (registryAddress !== constants.NULL_ADDRESS) {
|
||||
callArgs.registryAddress = registryAddress;
|
||||
}
|
||||
return liquidityProviderAddress;
|
||||
};
|
||||
return [callArgs, fn];
|
||||
@ -294,6 +296,7 @@ describe('MarketOperationUtils tests', () => {
|
||||
[ERC20BridgeSource.CurveUsdcDaiUsdtBusd]: _.times(NUM_SAMPLES, () => 0),
|
||||
[ERC20BridgeSource.CurveUsdcDaiUsdtSusd]: _.times(NUM_SAMPLES, () => 0),
|
||||
[ERC20BridgeSource.LiquidityProvider]: _.times(NUM_SAMPLES, () => 0),
|
||||
[ERC20BridgeSource.MultiBridge]: _.times(NUM_SAMPLES, () => 0),
|
||||
};
|
||||
|
||||
const DEFAULT_OPS = {
|
||||
|
@ -29,6 +29,13 @@ export type SampleSellsLPHandler = (
|
||||
takerTokenAmounts: BigNumber[],
|
||||
) => SampleResults;
|
||||
export type SampleSellsMultihopHandler = (path: string[], takerTokenAmounts: BigNumber[]) => SampleResults;
|
||||
export type SampleSellsMBHandler = (
|
||||
multiBridgeAddress: string,
|
||||
takerToken: string,
|
||||
intermediateToken: string,
|
||||
makerToken: string,
|
||||
takerTokenAmounts: BigNumber[],
|
||||
) => SampleResults;
|
||||
|
||||
const DUMMY_PROVIDER = {
|
||||
sendAsync: (...args: any[]): any => {
|
||||
@ -41,6 +48,7 @@ interface Handlers {
|
||||
getOrderFillableTakerAssetAmounts: GetOrderFillableAssetAmountHandler;
|
||||
sampleSellsFromKyberNetwork: SampleSellsHandler;
|
||||
sampleSellsFromLiquidityProviderRegistry: SampleSellsLPHandler;
|
||||
sampleSellsFromMultiBridge: SampleSellsMBHandler;
|
||||
sampleSellsFromEth2Dai: SampleSellsHandler;
|
||||
sampleSellsFromUniswap: SampleSellsHandler;
|
||||
sampleSellsFromUniswapV2: SampleSellsMultihopHandler;
|
||||
@ -159,6 +167,24 @@ export class MockSamplerContract extends IERC20BridgeSamplerContract {
|
||||
);
|
||||
}
|
||||
|
||||
public sampleSellsFromMultiBridge(
|
||||
multiBridgeAddress: string,
|
||||
takerToken: string,
|
||||
intermediateToken: string,
|
||||
makerToken: string,
|
||||
takerAssetAmounts: BigNumber[],
|
||||
): ContractFunctionObj<GetOrderFillableAssetAmountResult> {
|
||||
return this._wrapCall(
|
||||
super.sampleSellsFromMultiBridge,
|
||||
this._handlers.sampleSellsFromMultiBridge,
|
||||
multiBridgeAddress,
|
||||
takerToken,
|
||||
intermediateToken,
|
||||
makerToken,
|
||||
takerAssetAmounts,
|
||||
);
|
||||
}
|
||||
|
||||
public sampleBuysFromEth2Dai(
|
||||
takerToken: string,
|
||||
makerToken: string,
|
||||
|
@ -31,7 +31,8 @@
|
||||
"chainlinkStopLimit": "0xeb27220f95f364e1d9531992c48613f231839f53",
|
||||
"curveBridge": "0x6dc7950423ada9f56fb2c93a23edb787f1e29088",
|
||||
"maximumGasPrice": "0xe2bfd35306495d11e3c9db0d8de390cda24563cf",
|
||||
"dexForwarderBridge": "0x5591360f8c7640fea5771c9682d6b5ecb776e1f8"
|
||||
"dexForwarderBridge": "0x5591360f8c7640fea5771c9682d6b5ecb776e1f8",
|
||||
"multiBridge": "0xc03117a8c9bde203f70aa911cb64a7a0df5ba1e1"
|
||||
},
|
||||
"3": {
|
||||
"erc20Proxy": "0xb1408f4c245a23c31b98d2c626777d4c0d766caa",
|
||||
@ -65,7 +66,8 @@
|
||||
"chainlinkStopLimit": "0x67a094cf028221ffdd93fc658f963151d05e2a74",
|
||||
"curveBridge": "0x0000000000000000000000000000000000000000",
|
||||
"maximumGasPrice": "0x407b4128e9ecad8769b2332312a9f655cb9f5f3a",
|
||||
"dexForwarderBridge": "0x3be8e59038d8c4e8d8776ca40ef2f024bad95ad1"
|
||||
"dexForwarderBridge": "0x3be8e59038d8c4e8d8776ca40ef2f024bad95ad1",
|
||||
"multiBridge": "0x0000000000000000000000000000000000000000"
|
||||
},
|
||||
"4": {
|
||||
"exchangeV2": "0xbff9493f92a3df4b0429b6d00743b3cfb4c85831",
|
||||
@ -99,7 +101,8 @@
|
||||
"chainlinkStopLimit": "0x407b4128e9ecad8769b2332312a9f655cb9f5f3a",
|
||||
"curveBridge": "0x0000000000000000000000000000000000000000",
|
||||
"maximumGasPrice": "0x47697b44bd89051e93b4d5857ba8e024800a74ac",
|
||||
"dexForwarderBridge": "0x0000000000000000000000000000000000000000"
|
||||
"dexForwarderBridge": "0x0000000000000000000000000000000000000000",
|
||||
"multiBridge": "0x0000000000000000000000000000000000000000"
|
||||
},
|
||||
"42": {
|
||||
"erc20Proxy": "0xf1ec01d6236d3cd881a0bf0130ea25fe4234003e",
|
||||
@ -133,7 +136,8 @@
|
||||
"chainlinkStopLimit": "0x0000000000000000000000000000000000000000",
|
||||
"curveBridge": "0x90c62c91a9f655f4f739e6cee85c84f9ccf47323",
|
||||
"maximumGasPrice": "0x67a094cf028221ffdd93fc658f963151d05e2a74",
|
||||
"dexForwarderBridge": "0x6cce442a48ab07635462a40594054f34f44195ff"
|
||||
"dexForwarderBridge": "0x6cce442a48ab07635462a40594054f34f44195ff",
|
||||
"multiBridge": "0x0000000000000000000000000000000000000000"
|
||||
},
|
||||
"1337": {
|
||||
"erc20Proxy": "0x1dc4c1cefef38a777b15aa20260a54e584b16c48",
|
||||
@ -167,6 +171,7 @@
|
||||
"chainlinkStopLimit": "0x0000000000000000000000000000000000000000",
|
||||
"curveBridge": "0x0000000000000000000000000000000000000000",
|
||||
"maximumGasPrice": "0x0000000000000000000000000000000000000000",
|
||||
"dexForwarderBridge": "0x0000000000000000000000000000000000000000"
|
||||
"dexForwarderBridge": "0x0000000000000000000000000000000000000000",
|
||||
"multiBridge": "0x0000000000000000000000000000000000000000"
|
||||
}
|
||||
}
|
||||
|
@ -33,6 +33,7 @@ export interface ContractAddresses {
|
||||
chainlinkStopLimit: string;
|
||||
maximumGasPrice: string;
|
||||
dexForwarderBridge: string;
|
||||
multiBridge: string;
|
||||
}
|
||||
|
||||
export enum ChainId {
|
||||
|
@ -323,6 +323,7 @@ export async function runMigrationsAsync(
|
||||
chainlinkStopLimit: constants.NULL_ADDRESS,
|
||||
maximumGasPrice: constants.NULL_ADDRESS,
|
||||
dexForwarderBridge: constants.NULL_ADDRESS,
|
||||
multiBridge: constants.NULL_ADDRESS,
|
||||
};
|
||||
return contractAddresses;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user