Merge branch 'development' of github.com:0xProject/0x-monorepo into feature/keepAlive
# Conflicts: # packages/asset-swapper/src/utils/quote_requestor.ts
This commit is contained in:
commit
ceb90989d0
@ -5,6 +5,10 @@
|
|||||||
{
|
{
|
||||||
"note": "Update `CurveBridge` to support more varied curves",
|
"note": "Update `CurveBridge` to support more varied curves",
|
||||||
"pr": 2633
|
"pr": 2633
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Export DexForwarderBridgeContract",
|
||||||
|
"pr": 2656
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -18,6 +18,7 @@ export {
|
|||||||
TestDydxBridgeContract,
|
TestDydxBridgeContract,
|
||||||
TestStaticCallTargetContract,
|
TestStaticCallTargetContract,
|
||||||
UniswapBridgeContract,
|
UniswapBridgeContract,
|
||||||
|
DexForwarderBridgeContract,
|
||||||
} from './wrappers';
|
} from './wrappers';
|
||||||
|
|
||||||
export { ERC20Wrapper } from './erc20_wrapper';
|
export { ERC20Wrapper } from './erc20_wrapper';
|
||||||
|
@ -1,4 +1,13 @@
|
|||||||
[
|
[
|
||||||
|
{
|
||||||
|
"version": "4.5.2",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Add Ropsten and Rinkeby addresses to `DeploymentConstants`",
|
||||||
|
"pr": 2656
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"version": "4.5.1",
|
"version": "4.5.1",
|
||||||
"changes": [
|
"changes": [
|
||||||
|
@ -51,33 +51,89 @@ contract DeploymentConstants {
|
|||||||
/// @dev Mainnet address of the GST Collector
|
/// @dev Mainnet address of the GST Collector
|
||||||
address constant private GST_COLLECTOR_ADDRESS = 0x000000D3b08566BE75A6DB803C03C85C0c1c5B96;
|
address constant private GST_COLLECTOR_ADDRESS = 0x000000D3b08566BE75A6DB803C03C85C0c1c5B96;
|
||||||
|
|
||||||
// Kovan addresses /////////////////////////////////////////////////////////
|
/* // Ropsten addresses ///////////////////////////////////////////////////////
|
||||||
// /// @dev Kovan address of the WETH contract.
|
/// @dev Mainnet address of the WETH contract.
|
||||||
// address constant private WETH_ADDRESS = 0xd0A1E359811322d97991E03f863a0C30C2cF029C;
|
address constant private WETH_ADDRESS = 0xc778417E063141139Fce010982780140Aa0cD5Ab;
|
||||||
// /// @dev Kovan address of the KyberNetworkProxy contract.
|
/// @dev Mainnet address of the KyberNetworkProxy contract.
|
||||||
// address constant private KYBER_NETWORK_PROXY_ADDRESS = 0x692f391bCc85cefCe8C237C01e1f636BbD70EA4D;
|
address constant private KYBER_NETWORK_PROXY_ADDRESS = address(0);
|
||||||
// /// @dev Kovan address of the `UniswapExchangeFactory` contract.
|
/// @dev Mainnet address of the `UniswapExchangeFactory` contract.
|
||||||
// address constant private UNISWAP_EXCHANGE_FACTORY_ADDRESS = 0xD3E51Ef092B2845f10401a0159B2B96e8B6c3D30;
|
address constant private UNISWAP_EXCHANGE_FACTORY_ADDRESS = address(0);
|
||||||
// /// @dev Kovan address of the `UniswapV2Router01` contract.
|
/// @dev Mainnet address of the `UniswapV2Router01` contract.
|
||||||
// address constant private UNISWAP_V2_ROUTER_01_ADDRESS = 0xf164fC0Ec4E93095b804a4795bBe1e041497b92a;
|
address constant private UNISWAP_V2_ROUTER_01_ADDRESS = address(0);
|
||||||
// /// @dev Kovan address of the Eth2Dai `MatchingMarket` contract.
|
/// @dev Mainnet address of the Eth2Dai `MatchingMarket` contract.
|
||||||
// address constant private ETH2DAI_ADDRESS = 0xe325acB9765b02b8b418199bf9650972299235F4;
|
address constant private ETH2DAI_ADDRESS = address(0);
|
||||||
// /// @dev Kovan address of the `ERC20BridgeProxy` contract
|
/// @dev Mainnet address of the `ERC20BridgeProxy` contract
|
||||||
// address constant private ERC20_BRIDGE_PROXY_ADDRESS = 0xFb2DD2A1366dE37f7241C83d47DA58fd503E2C64;
|
address constant private ERC20_BRIDGE_PROXY_ADDRESS = 0xb344afeD348de15eb4a9e180205A2B0739628339;
|
||||||
// /// @dev Kovan address of the `Chai` contract
|
///@dev Mainnet address of the `Dai` (multi-collateral) contract
|
||||||
// address constant private CHAI_ADDRESS = address(0);
|
address constant private DAI_ADDRESS = address(0);
|
||||||
// /// @dev Kovan address of the `Dai` (multi-collateral) contract
|
/// @dev Mainnet address of the `Chai` contract
|
||||||
// address constant private DAI_ADDRESS = 0x4F96Fe3b7A6Cf9725f59d353F723c1bDb64CA6Aa;
|
address constant private CHAI_ADDRESS = address(0);
|
||||||
// /// @dev Kovan address of the 0x DevUtils contract.
|
/// @dev Mainnet address of the 0x DevUtils contract.
|
||||||
// address constant private DEV_UTILS_ADDRESS = 0x9402639A828BdF4E9e4103ac3B69E1a6E522eB59;
|
address constant private DEV_UTILS_ADDRESS = 0xC812AF3f3fBC62F76ea4262576EC0f49dB8B7f1c;
|
||||||
// /// @dev Kyber ETH pseudo-address.
|
/// @dev Kyber ETH pseudo-address.
|
||||||
// address constant internal KYBER_ETH_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
|
address constant internal KYBER_ETH_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
|
||||||
// /// @dev Kovan address of the dYdX contract.
|
/// @dev Mainnet address of the dYdX contract.
|
||||||
// address constant private DYDX_ADDRESS = address(0);
|
address constant private DYDX_ADDRESS = address(0);
|
||||||
// /// @dev Kovan address of the GST2 contract
|
/// @dev Mainnet address of the GST2 contract
|
||||||
// address constant private GST_ADDRESS = address(0);
|
address constant private GST_ADDRESS = address(0);
|
||||||
// /// @dev Kovan address of the GST Collector
|
/// @dev Mainnet address of the GST Collector
|
||||||
// address constant private GST_COLLECTOR_ADDRESS = address(0);
|
address constant private GST_COLLECTOR_ADDRESS = address(0); */
|
||||||
|
|
||||||
|
/* // Rinkeby addresses ///////////////////////////////////////////////////////
|
||||||
|
/// @dev Mainnet address of the WETH contract.
|
||||||
|
address constant private WETH_ADDRESS = 0xc778417E063141139Fce010982780140Aa0cD5Ab;
|
||||||
|
/// @dev Mainnet address of the KyberNetworkProxy contract.
|
||||||
|
address constant private KYBER_NETWORK_PROXY_ADDRESS = address(0);
|
||||||
|
/// @dev Mainnet address of the `UniswapExchangeFactory` contract.
|
||||||
|
address constant private UNISWAP_EXCHANGE_FACTORY_ADDRESS = address(0);
|
||||||
|
/// @dev Mainnet address of the `UniswapV2Router01` contract.
|
||||||
|
address constant private UNISWAP_V2_ROUTER_01_ADDRESS = address(0);
|
||||||
|
/// @dev Mainnet address of the Eth2Dai `MatchingMarket` contract.
|
||||||
|
address constant private ETH2DAI_ADDRESS = address(0);
|
||||||
|
/// @dev Mainnet address of the `ERC20BridgeProxy` contract
|
||||||
|
address constant private ERC20_BRIDGE_PROXY_ADDRESS = 0xA2AA4bEFED748Fba27a3bE7Dfd2C4b2c6DB1F49B;
|
||||||
|
///@dev Mainnet address of the `Dai` (multi-collateral) contract
|
||||||
|
address constant private DAI_ADDRESS = address(0);
|
||||||
|
/// @dev Mainnet address of the `Chai` contract
|
||||||
|
address constant private CHAI_ADDRESS = address(0);
|
||||||
|
/// @dev Mainnet address of the 0x DevUtils contract.
|
||||||
|
address constant private DEV_UTILS_ADDRESS = 0x46B5BC959e8A754c0256FFF73bF34A52Ad5CdfA9;
|
||||||
|
/// @dev Kyber ETH pseudo-address.
|
||||||
|
address constant internal KYBER_ETH_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
|
||||||
|
/// @dev Mainnet address of the dYdX contract.
|
||||||
|
address constant private DYDX_ADDRESS = address(0);
|
||||||
|
/// @dev Mainnet address of the GST2 contract
|
||||||
|
address constant private GST_ADDRESS = address(0);
|
||||||
|
/// @dev Mainnet address of the GST Collector
|
||||||
|
address constant private GST_COLLECTOR_ADDRESS = address(0); */
|
||||||
|
|
||||||
|
/* // Kovan addresses /////////////////////////////////////////////////////////
|
||||||
|
/// @dev Kovan address of the WETH contract.
|
||||||
|
address constant private WETH_ADDRESS = 0xd0A1E359811322d97991E03f863a0C30C2cF029C;
|
||||||
|
/// @dev Kovan address of the KyberNetworkProxy contract.
|
||||||
|
address constant private KYBER_NETWORK_PROXY_ADDRESS = 0x692f391bCc85cefCe8C237C01e1f636BbD70EA4D;
|
||||||
|
/// @dev Kovan address of the `UniswapExchangeFactory` contract.
|
||||||
|
address constant private UNISWAP_EXCHANGE_FACTORY_ADDRESS = 0xD3E51Ef092B2845f10401a0159B2B96e8B6c3D30;
|
||||||
|
/// @dev Kovan address of the `UniswapV2Router01` contract.
|
||||||
|
address constant private UNISWAP_V2_ROUTER_01_ADDRESS = 0xf164fC0Ec4E93095b804a4795bBe1e041497b92a;
|
||||||
|
/// @dev Kovan address of the Eth2Dai `MatchingMarket` contract.
|
||||||
|
address constant private ETH2DAI_ADDRESS = 0xe325acB9765b02b8b418199bf9650972299235F4;
|
||||||
|
/// @dev Kovan address of the `ERC20BridgeProxy` contract
|
||||||
|
address constant private ERC20_BRIDGE_PROXY_ADDRESS = 0x3577552C1Fb7A44aD76BeEB7aB53251668A21F8D;
|
||||||
|
/// @dev Kovan address of the `Chai` contract
|
||||||
|
address constant private CHAI_ADDRESS = address(0);
|
||||||
|
/// @dev Kovan address of the `Dai` (multi-collateral) contract
|
||||||
|
address constant private DAI_ADDRESS = 0x4F96Fe3b7A6Cf9725f59d353F723c1bDb64CA6Aa;
|
||||||
|
/// @dev Kovan address of the 0x DevUtils contract.
|
||||||
|
address constant private DEV_UTILS_ADDRESS = 0x9402639A828BdF4E9e4103ac3B69E1a6E522eB59;
|
||||||
|
/// @dev Kyber ETH pseudo-address.
|
||||||
|
address constant internal KYBER_ETH_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
|
||||||
|
/// @dev Kovan address of the dYdX contract.
|
||||||
|
address constant private DYDX_ADDRESS = address(0);
|
||||||
|
/// @dev Kovan address of the GST2 contract
|
||||||
|
address constant private GST_ADDRESS = address(0);
|
||||||
|
/// @dev Kovan address of the GST Collector
|
||||||
|
address constant private GST_COLLECTOR_ADDRESS = address(0); */
|
||||||
|
|
||||||
/// @dev Overridable way to get the `KyberNetworkProxy` address.
|
/// @dev Overridable way to get the `KyberNetworkProxy` address.
|
||||||
/// @return kyberAddress The `IKyberNetworkProxy` address.
|
/// @return kyberAddress The `IKyberNetworkProxy` address.
|
||||||
|
@ -2,6 +2,10 @@
|
|||||||
{
|
{
|
||||||
"version": "4.7.0",
|
"version": "4.7.0",
|
||||||
"changes": [
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Return quoteReport from SwapQuoter functions",
|
||||||
|
"pr": 2627
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"note": "Allow an empty override for sampler overrides",
|
"note": "Allow an empty override for sampler overrides",
|
||||||
"pr": 2637
|
"pr": 2637
|
||||||
@ -13,6 +17,34 @@
|
|||||||
{
|
{
|
||||||
"note": "Support more varied curves",
|
"note": "Support more varied curves",
|
||||||
"pr": 2633
|
"pr": 2633
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Make path optimization go faster",
|
||||||
|
"pr": 2640
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Adds `getBidAskLiquidityForMakerTakerAssetPairAsync` to return more detailed sample information",
|
||||||
|
"pr": 2641
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Fix regression where a split on the same source was collapsed into a single fill",
|
||||||
|
"pr": 2654
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Add support for buy token affiliate fees",
|
||||||
|
"pr": 2658
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Fix optimization of buy paths",
|
||||||
|
"pr": 2655
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Fix depth buy scale",
|
||||||
|
"pr": 2659
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Adjust fill by ethToInputRate when ethToOutputRate is 0",
|
||||||
|
"pr": 2660
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { BigNumber } from '@0x/utils';
|
import { BigNumber } from '@0x/utils';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
ExchangeProxyContractOpts,
|
||||||
ExtensionContractType,
|
ExtensionContractType,
|
||||||
ForwarderExtensionContractOpts,
|
ForwarderExtensionContractOpts,
|
||||||
OrderPrunerOpts,
|
OrderPrunerOpts,
|
||||||
@ -20,6 +21,7 @@ const NULL_ADDRESS = '0x0000000000000000000000000000000000000000';
|
|||||||
const MAINNET_CHAIN_ID = 1;
|
const MAINNET_CHAIN_ID = 1;
|
||||||
const ONE_SECOND_MS = 1000;
|
const ONE_SECOND_MS = 1000;
|
||||||
const DEFAULT_PER_PAGE = 1000;
|
const DEFAULT_PER_PAGE = 1000;
|
||||||
|
const ZERO_AMOUNT = new BigNumber(0);
|
||||||
|
|
||||||
const DEFAULT_ORDER_PRUNER_OPTS: OrderPrunerOpts = {
|
const DEFAULT_ORDER_PRUNER_OPTS: OrderPrunerOpts = {
|
||||||
expiryBufferMs: 120000, // 2 minutes
|
expiryBufferMs: 120000, // 2 minutes
|
||||||
@ -60,8 +62,23 @@ const DEFAULT_FORWARDER_SWAP_QUOTE_GET_OPTS: SwapQuoteGetOutputOpts = {
|
|||||||
extensionContractOpts: DEFAULT_FORWARDER_EXTENSION_CONTRACT_OPTS,
|
extensionContractOpts: DEFAULT_FORWARDER_EXTENSION_CONTRACT_OPTS,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const DEFAULT_EXCHANGE_PROXY_EXTENSION_CONTRACT_OPTS: ExchangeProxyContractOpts = {
|
||||||
|
isFromETH: false,
|
||||||
|
isToETH: false,
|
||||||
|
affiliateFee: {
|
||||||
|
recipient: NULL_ADDRESS,
|
||||||
|
buyTokenFeeAmount: ZERO_AMOUNT,
|
||||||
|
sellTokenFeeAmount: ZERO_AMOUNT,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
const DEFAULT_FORWARDER_SWAP_QUOTE_EXECUTE_OPTS: SwapQuoteExecutionOpts = DEFAULT_FORWARDER_SWAP_QUOTE_GET_OPTS;
|
const DEFAULT_FORWARDER_SWAP_QUOTE_EXECUTE_OPTS: SwapQuoteExecutionOpts = DEFAULT_FORWARDER_SWAP_QUOTE_GET_OPTS;
|
||||||
|
|
||||||
|
const DEFAULT_EXCHANGE_PROXY_SWAP_QUOTE_GET_OPTS: SwapQuoteGetOutputOpts = {
|
||||||
|
useExtensionContract: ExtensionContractType.ExchangeProxy,
|
||||||
|
extensionContractOpts: DEFAULT_EXCHANGE_PROXY_EXTENSION_CONTRACT_OPTS,
|
||||||
|
};
|
||||||
|
|
||||||
const DEFAULT_SWAP_QUOTE_REQUEST_OPTS: SwapQuoteRequestOpts = {
|
const DEFAULT_SWAP_QUOTE_REQUEST_OPTS: SwapQuoteRequestOpts = {
|
||||||
...DEFAULT_GET_MARKET_ORDERS_OPTS,
|
...DEFAULT_GET_MARKET_ORDERS_OPTS,
|
||||||
};
|
};
|
||||||
@ -74,7 +91,7 @@ export const constants = {
|
|||||||
ETH_GAS_STATION_API_URL,
|
ETH_GAS_STATION_API_URL,
|
||||||
PROTOCOL_FEE_MULTIPLIER,
|
PROTOCOL_FEE_MULTIPLIER,
|
||||||
NULL_BYTES,
|
NULL_BYTES,
|
||||||
ZERO_AMOUNT: new BigNumber(0),
|
ZERO_AMOUNT,
|
||||||
NULL_ADDRESS,
|
NULL_ADDRESS,
|
||||||
MAINNET_CHAIN_ID,
|
MAINNET_CHAIN_ID,
|
||||||
DEFAULT_ORDER_PRUNER_OPTS,
|
DEFAULT_ORDER_PRUNER_OPTS,
|
||||||
@ -85,6 +102,8 @@ export const constants = {
|
|||||||
DEFAULT_FORWARDER_SWAP_QUOTE_GET_OPTS,
|
DEFAULT_FORWARDER_SWAP_QUOTE_GET_OPTS,
|
||||||
DEFAULT_FORWARDER_SWAP_QUOTE_EXECUTE_OPTS,
|
DEFAULT_FORWARDER_SWAP_QUOTE_EXECUTE_OPTS,
|
||||||
DEFAULT_SWAP_QUOTE_REQUEST_OPTS,
|
DEFAULT_SWAP_QUOTE_REQUEST_OPTS,
|
||||||
|
DEFAULT_EXCHANGE_PROXY_SWAP_QUOTE_GET_OPTS,
|
||||||
|
DEFAULT_EXCHANGE_PROXY_EXTENSION_CONTRACT_OPTS,
|
||||||
DEFAULT_PER_PAGE,
|
DEFAULT_PER_PAGE,
|
||||||
DEFAULT_RFQT_REQUEST_OPTS,
|
DEFAULT_RFQT_REQUEST_OPTS,
|
||||||
NULL_ERC20_ASSET_DATA,
|
NULL_ERC20_ASSET_DATA,
|
||||||
|
@ -40,6 +40,7 @@ export { InsufficientAssetLiquidityError } from './errors';
|
|||||||
export { SwapQuoteConsumer } from './quote_consumers/swap_quote_consumer';
|
export { SwapQuoteConsumer } from './quote_consumers/swap_quote_consumer';
|
||||||
export { SwapQuoter } from './swap_quoter';
|
export { SwapQuoter } from './swap_quoter';
|
||||||
export {
|
export {
|
||||||
|
AffiliateFee,
|
||||||
CalldataInfo,
|
CalldataInfo,
|
||||||
ExchangeProxyContractOpts,
|
ExchangeProxyContractOpts,
|
||||||
ExtensionContractType,
|
ExtensionContractType,
|
||||||
@ -66,23 +67,37 @@ export {
|
|||||||
SwapQuoteRequestOpts,
|
SwapQuoteRequestOpts,
|
||||||
SwapQuoterError,
|
SwapQuoterError,
|
||||||
SwapQuoterOpts,
|
SwapQuoterOpts,
|
||||||
|
SwapQuoterRfqtOpts,
|
||||||
} from './types';
|
} from './types';
|
||||||
export { affiliateFeeUtils } from './utils/affiliate_fee_utils';
|
export { affiliateFeeUtils } from './utils/affiliate_fee_utils';
|
||||||
export {
|
export {
|
||||||
BalancerFillData,
|
BalancerFillData,
|
||||||
CollapsedFill,
|
CollapsedFill,
|
||||||
CurveFillData,
|
CurveFillData,
|
||||||
|
CurveFunctionSelectors,
|
||||||
CurveInfo,
|
CurveInfo,
|
||||||
ERC20BridgeSource,
|
ERC20BridgeSource,
|
||||||
FeeSchedule,
|
FeeSchedule,
|
||||||
FillData,
|
FillData,
|
||||||
GetMarketOrdersRfqtOpts,
|
GetMarketOrdersRfqtOpts,
|
||||||
|
LiquidityProviderFillData,
|
||||||
|
MarketDepth,
|
||||||
|
MarketDepthSide,
|
||||||
|
MultiBridgeFillData,
|
||||||
NativeCollapsedFill,
|
NativeCollapsedFill,
|
||||||
NativeFillData,
|
NativeFillData,
|
||||||
OptimizedMarketOrder,
|
OptimizedMarketOrder,
|
||||||
UniswapV2FillData,
|
UniswapV2FillData,
|
||||||
CurveFunctionSelectors,
|
|
||||||
} from './utils/market_operation_utils/types';
|
} from './utils/market_operation_utils/types';
|
||||||
export { ProtocolFeeUtils } from './utils/protocol_fee_utils';
|
export { ProtocolFeeUtils } from './utils/protocol_fee_utils';
|
||||||
|
export {
|
||||||
|
BridgeReportSource,
|
||||||
|
NativeOrderbookReportSource,
|
||||||
|
NativeRFQTReportSource,
|
||||||
|
QuoteReport,
|
||||||
|
QuoteReportSource,
|
||||||
|
} from './utils/quote_report_generator';
|
||||||
export { QuoteRequestor } from './utils/quote_requestor';
|
export { QuoteRequestor } from './utils/quote_requestor';
|
||||||
export { rfqtMocker } from './utils/rfqt_mocker';
|
export { rfqtMocker } from './utils/rfqt_mocker';
|
||||||
|
import { ERC20BridgeSource } from './utils/market_operation_utils/types';
|
||||||
|
export type Native = ERC20BridgeSource.Native;
|
||||||
|
@ -2,6 +2,7 @@ import { ContractAddresses } from '@0x/contract-addresses';
|
|||||||
import { ITransformERC20Contract } from '@0x/contract-wrappers';
|
import { ITransformERC20Contract } from '@0x/contract-wrappers';
|
||||||
import {
|
import {
|
||||||
assetDataUtils,
|
assetDataUtils,
|
||||||
|
encodeAffiliateFeeTransformerData,
|
||||||
encodeFillQuoteTransformerData,
|
encodeFillQuoteTransformerData,
|
||||||
encodePayTakerTransformerData,
|
encodePayTakerTransformerData,
|
||||||
encodeWethTransformerData,
|
encodeWethTransformerData,
|
||||||
@ -18,6 +19,7 @@ import * as _ from 'lodash';
|
|||||||
import { constants } from '../constants';
|
import { constants } from '../constants';
|
||||||
import {
|
import {
|
||||||
CalldataInfo,
|
CalldataInfo,
|
||||||
|
ExchangeProxyContractOpts,
|
||||||
MarketBuySwapQuote,
|
MarketBuySwapQuote,
|
||||||
MarketOperation,
|
MarketOperation,
|
||||||
MarketSellSwapQuote,
|
MarketSellSwapQuote,
|
||||||
@ -41,6 +43,7 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
|
|||||||
wethTransformer: number;
|
wethTransformer: number;
|
||||||
payTakerTransformer: number;
|
payTakerTransformer: number;
|
||||||
fillQuoteTransformer: number;
|
fillQuoteTransformer: number;
|
||||||
|
affiliateFeeTransformer: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
private readonly _transformFeature: ITransformERC20Contract;
|
private readonly _transformFeature: ITransformERC20Contract;
|
||||||
@ -70,6 +73,10 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
|
|||||||
contractAddresses.transformers.fillQuoteTransformer,
|
contractAddresses.transformers.fillQuoteTransformer,
|
||||||
contractAddresses.exchangeProxyTransformerDeployer,
|
contractAddresses.exchangeProxyTransformerDeployer,
|
||||||
),
|
),
|
||||||
|
affiliateFeeTransformer: findTransformerNonce(
|
||||||
|
contractAddresses.transformers.affiliateFeeTransformer,
|
||||||
|
contractAddresses.exchangeProxyTransformerDeployer,
|
||||||
|
),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,14 +85,11 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
|
|||||||
opts: Partial<SwapQuoteGetOutputOpts> = {},
|
opts: Partial<SwapQuoteGetOutputOpts> = {},
|
||||||
): Promise<CalldataInfo> {
|
): Promise<CalldataInfo> {
|
||||||
assert.isValidSwapQuote('quote', quote);
|
assert.isValidSwapQuote('quote', quote);
|
||||||
const { isFromETH, isToETH } = {
|
// tslint:disable-next-line:no-object-literal-type-assertion
|
||||||
...constants.DEFAULT_FORWARDER_SWAP_QUOTE_GET_OPTS,
|
const { affiliateFee, isFromETH, isToETH } = {
|
||||||
extensionContractOpts: {
|
...constants.DEFAULT_EXCHANGE_PROXY_EXTENSION_CONTRACT_OPTS,
|
||||||
isFromETH: false,
|
...opts.extensionContractOpts,
|
||||||
isToETH: false,
|
} as ExchangeProxyContractOpts;
|
||||||
},
|
|
||||||
...opts,
|
|
||||||
}.extensionContractOpts;
|
|
||||||
|
|
||||||
const sellToken = getTokenFromAssetData(quote.takerAssetData);
|
const sellToken = getTokenFromAssetData(quote.takerAssetData);
|
||||||
const buyToken = getTokenFromAssetData(quote.makerAssetData);
|
const buyToken = getTokenFromAssetData(quote.makerAssetData);
|
||||||
@ -129,6 +133,28 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This transformer pays affiliate fees.
|
||||||
|
const { buyTokenFeeAmount, sellTokenFeeAmount, recipient: feeRecipient } = affiliateFee;
|
||||||
|
|
||||||
|
if (buyTokenFeeAmount.isGreaterThan(0) && feeRecipient !== NULL_ADDRESS) {
|
||||||
|
transforms.push({
|
||||||
|
deploymentNonce: this.transformerNonces.affiliateFeeTransformer,
|
||||||
|
data: encodeAffiliateFeeTransformerData({
|
||||||
|
fees: [
|
||||||
|
{
|
||||||
|
token: isToETH ? ETH_TOKEN_ADDRESS : buyToken,
|
||||||
|
amount: buyTokenFeeAmount,
|
||||||
|
recipient: feeRecipient,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sellTokenFeeAmount.isGreaterThan(0) && feeRecipient !== NULL_ADDRESS) {
|
||||||
|
throw new Error('Affiliate fees denominated in sell token are not yet supported');
|
||||||
|
}
|
||||||
|
|
||||||
// The final transformer will send all funds to the taker.
|
// The final transformer will send all funds to the taker.
|
||||||
transforms.push({
|
transforms.push({
|
||||||
deploymentNonce: this.transformerNonces.payTakerTransformer,
|
deploymentNonce: this.transformerNonces.payTakerTransformer,
|
||||||
@ -138,12 +164,13 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
|
|||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const minBuyAmount = BigNumber.max(0, quote.worstCaseQuoteInfo.makerAssetAmount.minus(buyTokenFeeAmount));
|
||||||
const calldataHexString = this._transformFeature
|
const calldataHexString = this._transformFeature
|
||||||
.transformERC20(
|
.transformERC20(
|
||||||
isFromETH ? ETH_TOKEN_ADDRESS : sellToken,
|
isFromETH ? ETH_TOKEN_ADDRESS : sellToken,
|
||||||
isToETH ? ETH_TOKEN_ADDRESS : buyToken,
|
isToETH ? ETH_TOKEN_ADDRESS : buyToken,
|
||||||
sellAmount,
|
sellAmount,
|
||||||
quote.worstCaseQuoteInfo.makerAssetAmount,
|
minBuyAmount,
|
||||||
transforms,
|
transforms,
|
||||||
)
|
)
|
||||||
.getABIEncodedTransactionData();
|
.getABIEncodedTransactionData();
|
||||||
|
@ -20,13 +20,19 @@ import {
|
|||||||
SwapQuote,
|
SwapQuote,
|
||||||
SwapQuoteRequestOpts,
|
SwapQuoteRequestOpts,
|
||||||
SwapQuoterOpts,
|
SwapQuoterOpts,
|
||||||
|
SwapQuoterRfqtOpts,
|
||||||
} from './types';
|
} from './types';
|
||||||
import { assert } from './utils/assert';
|
import { assert } from './utils/assert';
|
||||||
import { calculateLiquidity } from './utils/calculate_liquidity';
|
import { calculateLiquidity } from './utils/calculate_liquidity';
|
||||||
import { MarketOperationUtils } from './utils/market_operation_utils';
|
import { MarketOperationUtils } from './utils/market_operation_utils';
|
||||||
import { createDummyOrderForSampler } from './utils/market_operation_utils/orders';
|
import { createDummyOrderForSampler } from './utils/market_operation_utils/orders';
|
||||||
import { DexOrderSampler } from './utils/market_operation_utils/sampler';
|
import { DexOrderSampler } from './utils/market_operation_utils/sampler';
|
||||||
import { ERC20BridgeSource } from './utils/market_operation_utils/types';
|
import {
|
||||||
|
ERC20BridgeSource,
|
||||||
|
MarketDepth,
|
||||||
|
MarketDepthSide,
|
||||||
|
MarketSideLiquidity,
|
||||||
|
} from './utils/market_operation_utils/types';
|
||||||
import { orderPrunerUtils } from './utils/order_prune_utils';
|
import { orderPrunerUtils } from './utils/order_prune_utils';
|
||||||
import { OrderStateUtils } from './utils/order_state_utils';
|
import { OrderStateUtils } from './utils/order_state_utils';
|
||||||
import { ProtocolFeeUtils } from './utils/protocol_fee_utils';
|
import { ProtocolFeeUtils } from './utils/protocol_fee_utils';
|
||||||
@ -46,8 +52,7 @@ export class SwapQuoter {
|
|||||||
private readonly _devUtilsContract: DevUtilsContract;
|
private readonly _devUtilsContract: DevUtilsContract;
|
||||||
private readonly _marketOperationUtils: MarketOperationUtils;
|
private readonly _marketOperationUtils: MarketOperationUtils;
|
||||||
private readonly _orderStateUtils: OrderStateUtils;
|
private readonly _orderStateUtils: OrderStateUtils;
|
||||||
private readonly _quoteRequestor: QuoteRequestor;
|
private readonly _rfqtOptions?: SwapQuoterRfqtOpts;
|
||||||
private readonly _rfqtTakerApiKeyWhitelist: string[];
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Instantiates a new SwapQuoter instance given existing liquidity in the form of orders and feeOrders.
|
* Instantiates a new SwapQuoter instance given existing liquidity in the form of orders and feeOrders.
|
||||||
@ -168,7 +173,8 @@ export class SwapQuoter {
|
|||||||
this.orderbook = orderbook;
|
this.orderbook = orderbook;
|
||||||
this.expiryBufferMs = expiryBufferMs;
|
this.expiryBufferMs = expiryBufferMs;
|
||||||
this.permittedOrderFeeTypes = permittedOrderFeeTypes;
|
this.permittedOrderFeeTypes = permittedOrderFeeTypes;
|
||||||
this._rfqtTakerApiKeyWhitelist = rfqt ? rfqt.takerApiKeyWhitelist || [] : [];
|
|
||||||
|
this._rfqtOptions = rfqt;
|
||||||
this._contractAddresses = options.contractAddresses || getContractAddressesForChainOrThrow(chainId);
|
this._contractAddresses = options.contractAddresses || getContractAddressesForChainOrThrow(chainId);
|
||||||
this._devUtilsContract = new DevUtilsContract(this._contractAddresses.devUtils, provider);
|
this._devUtilsContract = new DevUtilsContract(this._contractAddresses.devUtils, provider);
|
||||||
this._protocolFeeUtils = ProtocolFeeUtils.getInstance(
|
this._protocolFeeUtils = ProtocolFeeUtils.getInstance(
|
||||||
@ -176,12 +182,6 @@ export class SwapQuoter {
|
|||||||
options.ethGasStationUrl,
|
options.ethGasStationUrl,
|
||||||
);
|
);
|
||||||
this._orderStateUtils = new OrderStateUtils(this._devUtilsContract);
|
this._orderStateUtils = new OrderStateUtils(this._devUtilsContract);
|
||||||
this._quoteRequestor = new QuoteRequestor(
|
|
||||||
rfqt ? rfqt.makerAssetOfferings || {} : {},
|
|
||||||
rfqt ? rfqt.warningLogger : undefined,
|
|
||||||
rfqt ? rfqt.infoLogger : undefined,
|
|
||||||
expiryBufferMs,
|
|
||||||
);
|
|
||||||
// Allow the sampler bytecode to be overwritten using geths override functionality
|
// Allow the sampler bytecode to be overwritten using geths override functionality
|
||||||
const samplerBytecode = _.get(ERC20BridgeSampler, 'compilerOutput.evm.deployedBytecode.object');
|
const samplerBytecode = _.get(ERC20BridgeSampler, 'compilerOutput.evm.deployedBytecode.object');
|
||||||
const defaultCodeOverrides = samplerBytecode
|
const defaultCodeOverrides = samplerBytecode
|
||||||
@ -398,6 +398,98 @@ export class SwapQuoter {
|
|||||||
return calculateLiquidity(ordersWithFillableAmounts);
|
return calculateLiquidity(ordersWithFillableAmounts);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the bids and asks liquidity for the entire market.
|
||||||
|
* For certain sources (like AMM's) it is recommended to provide a practical maximum takerAssetAmount.
|
||||||
|
* @param makerTokenAddress The address of the maker asset
|
||||||
|
* @param takerTokenAddress The address of the taker asset
|
||||||
|
* @param takerAssetAmount The amount to sell and buy for the bids and asks.
|
||||||
|
*
|
||||||
|
* @return An object that conforms to MarketDepth that contains all of the samples and liquidity
|
||||||
|
* information for the source.
|
||||||
|
*/
|
||||||
|
public async getBidAskLiquidityForMakerTakerAssetPairAsync(
|
||||||
|
makerTokenAddress: string,
|
||||||
|
takerTokenAddress: string,
|
||||||
|
takerAssetAmount: BigNumber,
|
||||||
|
options: Partial<SwapQuoteRequestOpts> = {},
|
||||||
|
): Promise<MarketDepth> {
|
||||||
|
assert.isString('makerTokenAddress', makerTokenAddress);
|
||||||
|
assert.isString('takerTokenAddress', takerTokenAddress);
|
||||||
|
const makerAssetData = assetDataUtils.encodeERC20AssetData(makerTokenAddress);
|
||||||
|
const takerAssetData = assetDataUtils.encodeERC20AssetData(takerTokenAddress);
|
||||||
|
let [sellOrders, buyOrders] =
|
||||||
|
options.excludedSources && options.excludedSources.includes(ERC20BridgeSource.Native)
|
||||||
|
? Promise.resolve([[], []])
|
||||||
|
: await Promise.all([
|
||||||
|
this.orderbook.getOrdersAsync(makerAssetData, takerAssetData),
|
||||||
|
this.orderbook.getOrdersAsync(takerAssetData, makerAssetData),
|
||||||
|
]);
|
||||||
|
if (!sellOrders || sellOrders.length === 0) {
|
||||||
|
sellOrders = [
|
||||||
|
{
|
||||||
|
metaData: {},
|
||||||
|
order: createDummyOrderForSampler(
|
||||||
|
makerAssetData,
|
||||||
|
takerAssetData,
|
||||||
|
this._contractAddresses.uniswapBridge,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
if (!buyOrders || buyOrders.length === 0) {
|
||||||
|
buyOrders = [
|
||||||
|
{
|
||||||
|
metaData: {},
|
||||||
|
order: createDummyOrderForSampler(
|
||||||
|
takerAssetData,
|
||||||
|
makerAssetData,
|
||||||
|
this._contractAddresses.uniswapBridge,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
const getMarketDepthSide = (marketSideLiquidity: MarketSideLiquidity): MarketDepthSide => {
|
||||||
|
const { dexQuotes, nativeOrders, orderFillableAmounts, side } = marketSideLiquidity;
|
||||||
|
return [
|
||||||
|
...dexQuotes,
|
||||||
|
nativeOrders.map((o, i) => {
|
||||||
|
// When sell order fillable amount is taker
|
||||||
|
// When buy order fillable amount is maker
|
||||||
|
const scaleFactor = orderFillableAmounts[i].div(
|
||||||
|
side === MarketOperation.Sell ? o.takerAssetAmount : o.makerAssetAmount,
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
input: (side === MarketOperation.Sell ? o.takerAssetAmount : o.makerAssetAmount)
|
||||||
|
.times(scaleFactor)
|
||||||
|
.integerValue(),
|
||||||
|
output: (side === MarketOperation.Sell ? o.makerAssetAmount : o.takerAssetAmount)
|
||||||
|
.times(scaleFactor)
|
||||||
|
.integerValue(),
|
||||||
|
fillData: o,
|
||||||
|
source: ERC20BridgeSource.Native,
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
};
|
||||||
|
const [bids, asks] = await Promise.all([
|
||||||
|
this._marketOperationUtils.getMarketBuyLiquidityAsync(
|
||||||
|
(buyOrders || []).map(o => o.order),
|
||||||
|
takerAssetAmount,
|
||||||
|
options,
|
||||||
|
),
|
||||||
|
this._marketOperationUtils.getMarketSellLiquidityAsync(
|
||||||
|
(sellOrders || []).map(o => o.order),
|
||||||
|
takerAssetAmount,
|
||||||
|
options,
|
||||||
|
),
|
||||||
|
]);
|
||||||
|
return {
|
||||||
|
bids: getMarketDepthSide(bids),
|
||||||
|
asks: getMarketDepthSide(asks),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the asset data of all assets that can be used to purchase makerAssetData in the order provider passed in at init.
|
* Get the asset data of all assets that can be used to purchase makerAssetData in the order provider passed in at init.
|
||||||
*
|
*
|
||||||
@ -569,24 +661,34 @@ export class SwapQuoter {
|
|||||||
|
|
||||||
// get batches of orders from different sources, awaiting sources in parallel
|
// get batches of orders from different sources, awaiting sources in parallel
|
||||||
const orderBatchPromises: Array<Promise<SignedOrder[]>> = [];
|
const orderBatchPromises: Array<Promise<SignedOrder[]>> = [];
|
||||||
orderBatchPromises.push(
|
|
||||||
// Don't fetch Open Orderbook orders from the DB if Native has been excluded, or if `nativeExclusivelyRFQT` has been set.
|
const skipOpenOrderbook =
|
||||||
opts.excludedSources.includes(ERC20BridgeSource.Native) ||
|
opts.excludedSources.includes(ERC20BridgeSource.Native) ||
|
||||||
(opts.rfqt && opts.rfqt.nativeExclusivelyRFQT === true)
|
(opts.rfqt && opts.rfqt.nativeExclusivelyRFQT === true);
|
||||||
? Promise.resolve([])
|
if (!skipOpenOrderbook) {
|
||||||
: this._getSignedOrdersAsync(makerAssetData, takerAssetData),
|
orderBatchPromises.push(this._getSignedOrdersAsync(makerAssetData, takerAssetData)); // order book
|
||||||
|
}
|
||||||
|
|
||||||
|
const rfqtOptions = this._rfqtOptions;
|
||||||
|
const quoteRequestor = new QuoteRequestor(
|
||||||
|
rfqtOptions ? rfqtOptions.makerAssetOfferings || {} : {},
|
||||||
|
rfqtOptions ? rfqtOptions.warningLogger : undefined,
|
||||||
|
rfqtOptions ? rfqtOptions.infoLogger : undefined,
|
||||||
|
this.expiryBufferMs,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (
|
if (
|
||||||
opts.rfqt && // This is an RFQT-enabled API request
|
opts.rfqt && // This is an RFQT-enabled API request
|
||||||
opts.rfqt.intentOnFilling && // The requestor is asking for a firm quote
|
opts.rfqt.intentOnFilling && // The requestor is asking for a firm quote
|
||||||
!opts.excludedSources.includes(ERC20BridgeSource.Native) && // Native liquidity is not excluded
|
opts.rfqt.apiKey &&
|
||||||
this._rfqtTakerApiKeyWhitelist.includes(opts.rfqt.apiKey) // A valid API key was provided
|
this._isApiKeyWhitelisted(opts.rfqt.apiKey) && // A valid API key was provided
|
||||||
|
!opts.excludedSources.includes(ERC20BridgeSource.Native) // Native liquidity is not excluded
|
||||||
) {
|
) {
|
||||||
if (!opts.rfqt.takerAddress || opts.rfqt.takerAddress === constants.NULL_ADDRESS) {
|
if (!opts.rfqt.takerAddress || opts.rfqt.takerAddress === constants.NULL_ADDRESS) {
|
||||||
throw new Error('RFQ-T requests must specify a taker address');
|
throw new Error('RFQ-T requests must specify a taker address');
|
||||||
}
|
}
|
||||||
orderBatchPromises.push(
|
orderBatchPromises.push(
|
||||||
this._quoteRequestor
|
quoteRequestor
|
||||||
.requestRfqtFirmQuotesAsync(
|
.requestRfqtFirmQuotesAsync(
|
||||||
makerAssetData,
|
makerAssetData,
|
||||||
takerAssetData,
|
takerAssetData,
|
||||||
@ -600,7 +702,7 @@ export class SwapQuoter {
|
|||||||
|
|
||||||
const orderBatches: SignedOrder[][] = await Promise.all(orderBatchPromises);
|
const orderBatches: SignedOrder[][] = await Promise.all(orderBatchPromises);
|
||||||
|
|
||||||
const unsortedOrders: SignedOrder[] = orderBatches.reduce((_orders, batch) => _orders.concat(...batch));
|
const unsortedOrders: SignedOrder[] = orderBatches.reduce((_orders, batch) => _orders.concat(...batch), []);
|
||||||
|
|
||||||
const orders = sortingUtils.sortOrders(unsortedOrders);
|
const orders = sortingUtils.sortOrders(unsortedOrders);
|
||||||
|
|
||||||
@ -615,8 +717,8 @@ export class SwapQuoter {
|
|||||||
|
|
||||||
const calcOpts: CalculateSwapQuoteOpts = opts;
|
const calcOpts: CalculateSwapQuoteOpts = opts;
|
||||||
|
|
||||||
if (calcOpts.rfqt !== undefined && this._shouldEnableIndicativeRfqt(calcOpts.rfqt, marketOperation)) {
|
if (calcOpts.rfqt !== undefined) {
|
||||||
calcOpts.rfqt.quoteRequestor = this._quoteRequestor;
|
calcOpts.rfqt.quoteRequestor = quoteRequestor;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (marketOperation === MarketOperation.Buy) {
|
if (marketOperation === MarketOperation.Buy) {
|
||||||
@ -637,13 +739,9 @@ export class SwapQuoter {
|
|||||||
|
|
||||||
return swapQuote;
|
return swapQuote;
|
||||||
}
|
}
|
||||||
private _shouldEnableIndicativeRfqt(opts: CalculateSwapQuoteOpts['rfqt'], op: MarketOperation): boolean {
|
private _isApiKeyWhitelisted(apiKey: string): boolean {
|
||||||
return (
|
const whitelistedApiKeys = this._rfqtOptions ? this._rfqtOptions.takerApiKeyWhitelist : [];
|
||||||
opts !== undefined &&
|
return whitelistedApiKeys.includes(apiKey);
|
||||||
opts.isIndicative !== undefined &&
|
|
||||||
opts.isIndicative &&
|
|
||||||
this._rfqtTakerApiKeyWhitelist.includes(opts.apiKey)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// tslint:disable-next-line: max-file-line-count
|
// tslint:disable-next-line: max-file-line-count
|
||||||
|
@ -3,6 +3,7 @@ import { SignedOrder } from '@0x/types';
|
|||||||
import { BigNumber } from '@0x/utils';
|
import { BigNumber } from '@0x/utils';
|
||||||
|
|
||||||
import { GetMarketOrdersOpts, OptimizedMarketOrder } from './utils/market_operation_utils/types';
|
import { GetMarketOrdersOpts, OptimizedMarketOrder } from './utils/market_operation_utils/types';
|
||||||
|
import { QuoteReport } from './utils/quote_report_generator';
|
||||||
import { LogFunction } from './utils/quote_requestor';
|
import { LogFunction } from './utils/quote_requestor';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -123,13 +124,21 @@ export interface ForwarderExtensionContractOpts {
|
|||||||
feeRecipient: string;
|
feeRecipient: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface AffiliateFee {
|
||||||
|
recipient: string;
|
||||||
|
buyTokenFeeAmount: BigNumber;
|
||||||
|
sellTokenFeeAmount: BigNumber;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param isFromETH Whether the input token is ETH.
|
* @param isFromETH Whether the input token is ETH.
|
||||||
* @param isToETH Whether the output token is ETH.
|
* @param isToETH Whether the output token is ETH.
|
||||||
|
* @param affiliateFee Fee denominated in taker or maker asset to send to specified recipient.
|
||||||
*/
|
*/
|
||||||
export interface ExchangeProxyContractOpts {
|
export interface ExchangeProxyContractOpts {
|
||||||
isFromETH: boolean;
|
isFromETH: boolean;
|
||||||
isToETH: boolean;
|
isToETH: boolean;
|
||||||
|
affiliateFee: AffiliateFee;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type SwapQuote = MarketBuySwapQuote | MarketSellSwapQuote;
|
export type SwapQuote = MarketBuySwapQuote | MarketSellSwapQuote;
|
||||||
@ -155,6 +164,7 @@ export interface SwapQuoteBase {
|
|||||||
bestCaseQuoteInfo: SwapQuoteInfo;
|
bestCaseQuoteInfo: SwapQuoteInfo;
|
||||||
worstCaseQuoteInfo: SwapQuoteInfo;
|
worstCaseQuoteInfo: SwapQuoteInfo;
|
||||||
sourceBreakdown: SwapQuoteOrdersBreakdown;
|
sourceBreakdown: SwapQuoteOrdersBreakdown;
|
||||||
|
quoteReport?: QuoteReport;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -236,6 +246,13 @@ export interface RfqtMakerAssetOfferings {
|
|||||||
|
|
||||||
export { LogFunction } from './utils/quote_requestor';
|
export { LogFunction } from './utils/quote_requestor';
|
||||||
|
|
||||||
|
export interface SwapQuoterRfqtOpts {
|
||||||
|
takerApiKeyWhitelist: string[];
|
||||||
|
makerAssetOfferings: RfqtMakerAssetOfferings;
|
||||||
|
warningLogger?: LogFunction;
|
||||||
|
infoLogger?: LogFunction;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* chainId: The ethereum chain id. Defaults to 1 (mainnet).
|
* chainId: The ethereum chain id. Defaults to 1 (mainnet).
|
||||||
* orderRefreshIntervalMs: The interval in ms that getBuyQuoteAsync should trigger an refresh of orders and order states. Defaults to 10000ms (10s).
|
* orderRefreshIntervalMs: The interval in ms that getBuyQuoteAsync should trigger an refresh of orders and order states. Defaults to 10000ms (10s).
|
||||||
@ -252,12 +269,7 @@ export interface SwapQuoterOpts extends OrderPrunerOpts {
|
|||||||
liquidityProviderRegistryAddress?: string;
|
liquidityProviderRegistryAddress?: string;
|
||||||
multiBridgeAddress?: string;
|
multiBridgeAddress?: string;
|
||||||
ethGasStationUrl?: string;
|
ethGasStationUrl?: string;
|
||||||
rfqt?: {
|
rfqt?: SwapQuoterRfqtOpts;
|
||||||
takerApiKeyWhitelist: string[];
|
|
||||||
makerAssetOfferings: RfqtMakerAssetOfferings;
|
|
||||||
warningLogger?: LogFunction;
|
|
||||||
infoLogger?: LogFunction;
|
|
||||||
};
|
|
||||||
samplerOverrides?: SamplerOverrides;
|
samplerOverrides?: SamplerOverrides;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { BigNumber } from '@0x/utils';
|
import { BigNumber, hexUtils } from '@0x/utils';
|
||||||
|
|
||||||
import { MarketOperation, SignedOrderWithFillableAmounts } from '../../types';
|
import { MarketOperation, SignedOrderWithFillableAmounts } from '../../types';
|
||||||
import { fillableAmountsUtils } from '../../utils/fillable_amounts_utils';
|
import { fillableAmountsUtils } from '../../utils/fillable_amounts_utils';
|
||||||
@ -17,6 +17,7 @@ export function createFillPaths(opts: {
|
|||||||
dexQuotes?: DexSample[][];
|
dexQuotes?: DexSample[][];
|
||||||
targetInput?: BigNumber;
|
targetInput?: BigNumber;
|
||||||
ethToOutputRate?: BigNumber;
|
ethToOutputRate?: BigNumber;
|
||||||
|
ethToInputRate?: BigNumber;
|
||||||
excludedSources?: ERC20BridgeSource[];
|
excludedSources?: ERC20BridgeSource[];
|
||||||
feeSchedule?: FeeSchedule;
|
feeSchedule?: FeeSchedule;
|
||||||
}): Fill[][] {
|
}): Fill[][] {
|
||||||
@ -26,8 +27,9 @@ export function createFillPaths(opts: {
|
|||||||
const orders = opts.orders || [];
|
const orders = opts.orders || [];
|
||||||
const dexQuotes = opts.dexQuotes || [];
|
const dexQuotes = opts.dexQuotes || [];
|
||||||
const ethToOutputRate = opts.ethToOutputRate || ZERO_AMOUNT;
|
const ethToOutputRate = opts.ethToOutputRate || ZERO_AMOUNT;
|
||||||
|
const ethToInputRate = opts.ethToInputRate || ZERO_AMOUNT;
|
||||||
// Create native fill paths.
|
// Create native fill paths.
|
||||||
const nativePath = nativeOrdersToPath(side, orders, opts.targetInput, ethToOutputRate, feeSchedule);
|
const nativePath = nativeOrdersToPath(side, orders, opts.targetInput, ethToOutputRate, ethToInputRate, feeSchedule);
|
||||||
// Create DEX fill paths.
|
// Create DEX fill paths.
|
||||||
const dexPaths = dexQuotesToPaths(side, dexQuotes, ethToOutputRate, feeSchedule);
|
const dexPaths = dexQuotesToPaths(side, dexQuotes, ethToOutputRate, feeSchedule);
|
||||||
return filterPaths([...dexPaths, nativePath].map(p => clipPathToInput(p, opts.targetInput)), excludedSources);
|
return filterPaths([...dexPaths, nativePath].map(p => clipPathToInput(p, opts.targetInput)), excludedSources);
|
||||||
@ -54,19 +56,21 @@ function nativeOrdersToPath(
|
|||||||
orders: SignedOrderWithFillableAmounts[],
|
orders: SignedOrderWithFillableAmounts[],
|
||||||
targetInput: BigNumber = POSITIVE_INF,
|
targetInput: BigNumber = POSITIVE_INF,
|
||||||
ethToOutputRate: BigNumber,
|
ethToOutputRate: BigNumber,
|
||||||
|
ethToInputRate: BigNumber,
|
||||||
fees: FeeSchedule,
|
fees: FeeSchedule,
|
||||||
): Fill[] {
|
): Fill[] {
|
||||||
|
const sourcePathId = hexUtils.random();
|
||||||
// Create a single path from all orders.
|
// Create a single path from all orders.
|
||||||
let path: Fill[] = [];
|
let path: Array<Fill & { adjustedRate: BigNumber }> = [];
|
||||||
for (const order of orders) {
|
for (const order of orders) {
|
||||||
const makerAmount = fillableAmountsUtils.getMakerAssetAmountSwappedAfterOrderFees(order);
|
const makerAmount = fillableAmountsUtils.getMakerAssetAmountSwappedAfterOrderFees(order);
|
||||||
const takerAmount = fillableAmountsUtils.getTakerAssetAmountSwappedAfterOrderFees(order);
|
const takerAmount = fillableAmountsUtils.getTakerAssetAmountSwappedAfterOrderFees(order);
|
||||||
const input = side === MarketOperation.Sell ? takerAmount : makerAmount;
|
const input = side === MarketOperation.Sell ? takerAmount : makerAmount;
|
||||||
const output = side === MarketOperation.Sell ? makerAmount : takerAmount;
|
const output = side === MarketOperation.Sell ? makerAmount : takerAmount;
|
||||||
const penalty = ethToOutputRate.times(
|
const fee = fees[ERC20BridgeSource.Native] === undefined ? 0 : fees[ERC20BridgeSource.Native]!();
|
||||||
fees[ERC20BridgeSource.Native] === undefined ? 0 : fees[ERC20BridgeSource.Native]!(),
|
const outputPenalty = !ethToOutputRate.isZero()
|
||||||
);
|
? ethToOutputRate.times(fee)
|
||||||
const rate = makerAmount.div(takerAmount);
|
: ethToInputRate.times(fee).times(output.dividedToIntegerBy(input));
|
||||||
// targetInput can be less than the order size
|
// targetInput can be less than the order size
|
||||||
// whilst the penalty is constant, it affects the adjusted output
|
// whilst the penalty is constant, it affects the adjusted output
|
||||||
// only up until the target has been exhausted.
|
// only up until the target has been exhausted.
|
||||||
@ -76,7 +80,7 @@ function nativeOrdersToPath(
|
|||||||
// scale the clipped output inline with the input
|
// scale the clipped output inline with the input
|
||||||
const clippedOutput = clippedInput.dividedBy(input).times(output);
|
const clippedOutput = clippedInput.dividedBy(input).times(output);
|
||||||
const adjustedOutput =
|
const adjustedOutput =
|
||||||
side === MarketOperation.Sell ? clippedOutput.minus(penalty) : clippedOutput.plus(penalty);
|
side === MarketOperation.Sell ? clippedOutput.minus(outputPenalty) : clippedOutput.plus(outputPenalty);
|
||||||
const adjustedRate =
|
const adjustedRate =
|
||||||
side === MarketOperation.Sell ? adjustedOutput.div(clippedInput) : clippedInput.div(adjustedOutput);
|
side === MarketOperation.Sell ? adjustedOutput.div(clippedInput) : clippedInput.div(adjustedOutput);
|
||||||
// Skip orders with rates that are <= 0.
|
// Skip orders with rates that are <= 0.
|
||||||
@ -84,11 +88,11 @@ function nativeOrdersToPath(
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
path.push({
|
path.push({
|
||||||
input: clippedInput,
|
sourcePathId,
|
||||||
output: clippedOutput,
|
|
||||||
rate,
|
|
||||||
adjustedRate,
|
adjustedRate,
|
||||||
adjustedOutput,
|
adjustedOutput,
|
||||||
|
input: clippedInput,
|
||||||
|
output: clippedOutput,
|
||||||
flags: 0,
|
flags: 0,
|
||||||
index: 0, // TBD
|
index: 0, // TBD
|
||||||
parent: undefined, // TBD
|
parent: undefined, // TBD
|
||||||
@ -114,6 +118,7 @@ function dexQuotesToPaths(
|
|||||||
): Fill[][] {
|
): Fill[][] {
|
||||||
const paths: Fill[][] = [];
|
const paths: Fill[][] = [];
|
||||||
for (let quote of dexQuotes) {
|
for (let quote of dexQuotes) {
|
||||||
|
const sourcePathId = hexUtils.random();
|
||||||
const path: Fill[] = [];
|
const path: Fill[] = [];
|
||||||
// Drop any non-zero entries. This can occur if the any fills on Kyber were UniswapReserves
|
// Drop any non-zero entries. This can occur if the any fills on Kyber were UniswapReserves
|
||||||
// We need not worry about Kyber fills going to UniswapReserve as the input amount
|
// We need not worry about Kyber fills going to UniswapReserve as the input amount
|
||||||
@ -132,14 +137,11 @@ function dexQuotesToPaths(
|
|||||||
? ethToOutputRate.times(fee)
|
? ethToOutputRate.times(fee)
|
||||||
: ZERO_AMOUNT;
|
: ZERO_AMOUNT;
|
||||||
const adjustedOutput = side === MarketOperation.Sell ? output.minus(penalty) : output.plus(penalty);
|
const adjustedOutput = side === MarketOperation.Sell ? output.minus(penalty) : output.plus(penalty);
|
||||||
const rate = side === MarketOperation.Sell ? output.div(input) : input.div(output);
|
|
||||||
const adjustedRate = side === MarketOperation.Sell ? adjustedOutput.div(input) : input.div(adjustedOutput);
|
|
||||||
|
|
||||||
path.push({
|
path.push({
|
||||||
|
sourcePathId,
|
||||||
input,
|
input,
|
||||||
output,
|
output,
|
||||||
rate,
|
|
||||||
adjustedRate,
|
|
||||||
adjustedOutput,
|
adjustedOutput,
|
||||||
source,
|
source,
|
||||||
fillData,
|
fillData,
|
||||||
@ -189,8 +191,12 @@ export function getPathAdjustedSize(path: Fill[], targetInput: BigNumber = POSIT
|
|||||||
for (const fill of path) {
|
for (const fill of path) {
|
||||||
if (input.plus(fill.input).gte(targetInput)) {
|
if (input.plus(fill.input).gte(targetInput)) {
|
||||||
const di = targetInput.minus(input);
|
const di = targetInput.minus(input);
|
||||||
|
if (di.gt(0)) {
|
||||||
input = input.plus(di);
|
input = input.plus(di);
|
||||||
output = output.plus(fill.adjustedOutput.times(di.div(fill.input)));
|
// Penalty does not get interpolated.
|
||||||
|
const penalty = fill.adjustedOutput.minus(fill.output);
|
||||||
|
output = output.plus(fill.output.times(di.div(fill.input)).plus(penalty));
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
input = input.plus(fill.input);
|
input = input.plus(fill.input);
|
||||||
@ -219,6 +225,10 @@ export function isValidPath(path: Fill[], skipDuplicateCheck: boolean = false):
|
|||||||
}
|
}
|
||||||
flags |= path[i].flags;
|
flags |= path[i].flags;
|
||||||
}
|
}
|
||||||
|
return arePathFlagsAllowed(flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function arePathFlagsAllowed(flags: number): boolean {
|
||||||
const multiBridgeConflict = FillFlags.MultiBridge | FillFlags.ConflictsWithMultiBridge;
|
const multiBridgeConflict = FillFlags.MultiBridge | FillFlags.ConflictsWithMultiBridge;
|
||||||
return (flags & multiBridgeConflict) !== multiBridgeConflict;
|
return (flags & multiBridgeConflict) !== multiBridgeConflict;
|
||||||
}
|
}
|
||||||
@ -243,7 +253,7 @@ export function collapsePath(path: Fill[]): CollapsedFill[] {
|
|||||||
if (collapsed.length !== 0 && source !== ERC20BridgeSource.Native) {
|
if (collapsed.length !== 0 && source !== ERC20BridgeSource.Native) {
|
||||||
const prevFill = collapsed[collapsed.length - 1];
|
const prevFill = collapsed[collapsed.length - 1];
|
||||||
// If the last fill is from the same source, merge them.
|
// If the last fill is from the same source, merge them.
|
||||||
if (prevFill.source === source) {
|
if (prevFill.sourcePathId === fill.sourcePathId) {
|
||||||
prevFill.input = prevFill.input.plus(fill.input);
|
prevFill.input = prevFill.input.plus(fill.input);
|
||||||
prevFill.output = prevFill.output.plus(fill.output);
|
prevFill.output = prevFill.output.plus(fill.output);
|
||||||
prevFill.subFills.push(fill);
|
prevFill.subFills.push(fill);
|
||||||
@ -251,6 +261,7 @@ export function collapsePath(path: Fill[]): CollapsedFill[] {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
collapsed.push({
|
collapsed.push({
|
||||||
|
sourcePathId: fill.sourcePathId,
|
||||||
source: fill.source,
|
source: fill.source,
|
||||||
fillData: fill.fillData,
|
fillData: fill.fillData,
|
||||||
input: fill.input,
|
input: fill.input,
|
||||||
@ -261,35 +272,14 @@ export function collapsePath(path: Fill[]): CollapsedFill[] {
|
|||||||
return collapsed;
|
return collapsed;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getFallbackSourcePaths(optimalPath: Fill[], allPaths: Fill[][]): Fill[][] {
|
export function getPathAdjustedCompleteRate(side: MarketOperation, path: Fill[], targetInput: BigNumber): BigNumber {
|
||||||
const optimalSources: ERC20BridgeSource[] = [];
|
const [input, output] = getPathAdjustedSize(path, targetInput);
|
||||||
for (const fill of optimalPath) {
|
return getCompleteRate(side, input, output, targetInput);
|
||||||
if (!optimalSources.includes(fill.source)) {
|
|
||||||
optimalSources.push(fill.source);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const fallbackPaths: Fill[][] = [];
|
|
||||||
for (const path of allPaths) {
|
|
||||||
if (optimalSources.includes(path[0].source)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// HACK(dorothy-zbornak): We *should* be filtering out paths that
|
|
||||||
// conflict with the optimal path (i.e., Kyber conflicts), but in
|
|
||||||
// practice we often end up not being able to find a fallback path
|
|
||||||
// because we've lost 2 major liquiduty sources. The end result is
|
|
||||||
// we end up with many more reverts than what would be actually caused
|
|
||||||
// by conflicts.
|
|
||||||
fallbackPaths.push(path);
|
|
||||||
}
|
|
||||||
return fallbackPaths;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getPathAdjustedRate(side: MarketOperation, path: Fill[], targetInput: BigNumber): BigNumber {
|
export function getPathAdjustedRate(side: MarketOperation, path: Fill[], targetInput: BigNumber): BigNumber {
|
||||||
const [input, output] = getPathAdjustedSize(path, targetInput);
|
const [input, output] = getPathAdjustedSize(path, targetInput);
|
||||||
if (input.eq(0) || output.eq(0)) {
|
return getRate(side, input, output);
|
||||||
return ZERO_AMOUNT;
|
|
||||||
}
|
|
||||||
return side === MarketOperation.Sell ? output.div(input) : input.div(output);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getPathAdjustedSlippage(
|
export function getPathAdjustedSlippage(
|
||||||
@ -305,3 +295,29 @@ export function getPathAdjustedSlippage(
|
|||||||
const rateChange = maxRate.minus(totalRate);
|
const rateChange = maxRate.minus(totalRate);
|
||||||
return rateChange.div(maxRate).toNumber();
|
return rateChange.div(maxRate).toNumber();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getCompleteRate(
|
||||||
|
side: MarketOperation,
|
||||||
|
input: BigNumber,
|
||||||
|
output: BigNumber,
|
||||||
|
targetInput: BigNumber,
|
||||||
|
): BigNumber {
|
||||||
|
if (input.eq(0) || output.eq(0) || targetInput.eq(0)) {
|
||||||
|
return ZERO_AMOUNT;
|
||||||
|
}
|
||||||
|
// Penalize paths that fall short of the entire input amount by a factor of
|
||||||
|
// input / targetInput => (i / t)
|
||||||
|
if (side === MarketOperation.Sell) {
|
||||||
|
// (o / i) * (i / t) => (o / t)
|
||||||
|
return output.div(targetInput);
|
||||||
|
}
|
||||||
|
// (i / o) * (i / t)
|
||||||
|
return input.div(output).times(input.div(targetInput));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getRate(side: MarketOperation, input: BigNumber, output: BigNumber): BigNumber {
|
||||||
|
if (input.eq(0) || output.eq(0)) {
|
||||||
|
return ZERO_AMOUNT;
|
||||||
|
}
|
||||||
|
return side === MarketOperation.Sell ? output.div(input) : input.div(output);
|
||||||
|
}
|
||||||
|
@ -1,11 +1,15 @@
|
|||||||
import { ContractAddresses } from '@0x/contract-addresses';
|
import { ContractAddresses } from '@0x/contract-addresses';
|
||||||
|
import { ZERO_AMOUNT } from '@0x/order-utils';
|
||||||
import { RFQTIndicativeQuote } from '@0x/quote-server';
|
import { RFQTIndicativeQuote } from '@0x/quote-server';
|
||||||
import { SignedOrder } from '@0x/types';
|
import { SignedOrder } from '@0x/types';
|
||||||
import { BigNumber, NULL_ADDRESS } from '@0x/utils';
|
import { BigNumber, NULL_ADDRESS } from '@0x/utils';
|
||||||
|
import * as _ from 'lodash';
|
||||||
|
|
||||||
import { MarketOperation } from '../../types';
|
import { MarketOperation } from '../../types';
|
||||||
|
import { QuoteRequestor } from '../quote_requestor';
|
||||||
import { difference } from '../utils';
|
import { difference } from '../utils';
|
||||||
|
|
||||||
|
import { QuoteReportGenerator } from './../quote_report_generator';
|
||||||
import { BUY_SOURCES, DEFAULT_GET_MARKET_ORDERS_OPTS, FEE_QUOTE_SOURCES, ONE_ETHER, SELL_SOURCES } from './constants';
|
import { BUY_SOURCES, DEFAULT_GET_MARKET_ORDERS_OPTS, FEE_QUOTE_SOURCES, ONE_ETHER, SELL_SOURCES } from './constants';
|
||||||
import { createFillPaths, getPathAdjustedRate, getPathAdjustedSlippage } from './fills';
|
import { createFillPaths, getPathAdjustedRate, getPathAdjustedSlippage } from './fills';
|
||||||
import {
|
import {
|
||||||
@ -22,7 +26,9 @@ import {
|
|||||||
ERC20BridgeSource,
|
ERC20BridgeSource,
|
||||||
FeeSchedule,
|
FeeSchedule,
|
||||||
GetMarketOrdersOpts,
|
GetMarketOrdersOpts,
|
||||||
|
MarketSideLiquidity,
|
||||||
OptimizedMarketOrder,
|
OptimizedMarketOrder,
|
||||||
|
OptimizedOrdersAndQuoteReport,
|
||||||
OrderDomain,
|
OrderDomain,
|
||||||
} from './types';
|
} from './types';
|
||||||
|
|
||||||
@ -70,18 +76,17 @@ export class MarketOperationUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* gets the orders required for a market sell operation by (potentially) merging native orders with
|
* Gets the liquidity available for a market sell operation
|
||||||
* generated bridge orders.
|
|
||||||
* @param nativeOrders Native orders.
|
* @param nativeOrders Native orders.
|
||||||
* @param takerAmount Amount of taker asset to sell.
|
* @param takerAmount Amount of taker asset to sell.
|
||||||
* @param opts Options object.
|
* @param opts Options object.
|
||||||
* @return orders.
|
* @return MarketSideLiquidity.
|
||||||
*/
|
*/
|
||||||
public async getMarketSellOrdersAsync(
|
public async getMarketSellLiquidityAsync(
|
||||||
nativeOrders: SignedOrder[],
|
nativeOrders: SignedOrder[],
|
||||||
takerAmount: BigNumber,
|
takerAmount: BigNumber,
|
||||||
opts?: Partial<GetMarketOrdersOpts>,
|
opts?: Partial<GetMarketOrdersOpts>,
|
||||||
): Promise<OptimizedMarketOrder[]> {
|
): Promise<MarketSideLiquidity> {
|
||||||
if (nativeOrders.length === 0) {
|
if (nativeOrders.length === 0) {
|
||||||
throw new Error(AggregationError.EmptyOrders);
|
throw new Error(AggregationError.EmptyOrders);
|
||||||
}
|
}
|
||||||
@ -110,6 +115,17 @@ export class MarketOperationUtils {
|
|||||||
this._liquidityProviderRegistry,
|
this._liquidityProviderRegistry,
|
||||||
this._multiBridge,
|
this._multiBridge,
|
||||||
),
|
),
|
||||||
|
// Get ETH -> taker token price.
|
||||||
|
await DexOrderSampler.ops.getMedianSellRateAsync(
|
||||||
|
difference(FEE_QUOTE_SOURCES.concat(this._optionalSources()), _opts.excludedSources),
|
||||||
|
takerToken,
|
||||||
|
this._wethAddress,
|
||||||
|
ONE_ETHER,
|
||||||
|
this._wethAddress,
|
||||||
|
this._sampler.balancerPoolsCache,
|
||||||
|
this._liquidityProviderRegistry,
|
||||||
|
this._multiBridge,
|
||||||
|
),
|
||||||
// Get sell quotes for taker -> maker.
|
// Get sell quotes for taker -> maker.
|
||||||
await DexOrderSampler.ops.getSellQuotesAsync(
|
await DexOrderSampler.ops.getSellQuotesAsync(
|
||||||
difference(
|
difference(
|
||||||
@ -148,44 +164,45 @@ export class MarketOperationUtils {
|
|||||||
.then(async r => this._sampler.executeAsync(r));
|
.then(async r => this._sampler.executeAsync(r));
|
||||||
|
|
||||||
const [
|
const [
|
||||||
[orderFillableAmounts, liquidityProviderAddress, ethToMakerAssetRate, dexQuotes],
|
[orderFillableAmounts, liquidityProviderAddress, ethToMakerAssetRate, ethToTakerAssetRate, dexQuotes],
|
||||||
rfqtIndicativeQuotes,
|
rfqtIndicativeQuotes,
|
||||||
[balancerQuotes],
|
[balancerQuotes],
|
||||||
] = await Promise.all([samplerPromise, rfqtPromise, balancerPromise]);
|
] = await Promise.all([samplerPromise, rfqtPromise, balancerPromise]);
|
||||||
return this._generateOptimizedOrdersAsync({
|
|
||||||
orderFillableAmounts,
|
// Attach the LiquidityProvider address to the sample fillData
|
||||||
nativeOrders,
|
(dexQuotes.find(quotes => quotes[0] && quotes[0].source === ERC20BridgeSource.LiquidityProvider) || []).forEach(
|
||||||
dexQuotes: dexQuotes.concat(balancerQuotes),
|
q => (q.fillData = { poolAddress: liquidityProviderAddress }),
|
||||||
rfqtIndicativeQuotes,
|
);
|
||||||
liquidityProviderAddress,
|
// Attach the MultiBridge address to the sample fillData
|
||||||
multiBridgeAddress: this._multiBridge,
|
(dexQuotes.find(quotes => quotes[0] && quotes[0].source === ERC20BridgeSource.MultiBridge) || []).forEach(
|
||||||
inputToken: takerToken,
|
q => (q.fillData = { poolAddress: this._multiBridge }),
|
||||||
outputToken: makerToken,
|
);
|
||||||
|
return {
|
||||||
side: MarketOperation.Sell,
|
side: MarketOperation.Sell,
|
||||||
inputAmount: takerAmount,
|
inputAmount: takerAmount,
|
||||||
|
inputToken: takerToken,
|
||||||
|
outputToken: makerToken,
|
||||||
|
dexQuotes: dexQuotes.concat(balancerQuotes),
|
||||||
|
nativeOrders,
|
||||||
|
orderFillableAmounts,
|
||||||
ethToOutputRate: ethToMakerAssetRate,
|
ethToOutputRate: ethToMakerAssetRate,
|
||||||
bridgeSlippage: _opts.bridgeSlippage,
|
ethToInputRate: ethToTakerAssetRate,
|
||||||
maxFallbackSlippage: _opts.maxFallbackSlippage,
|
rfqtIndicativeQuotes,
|
||||||
excludedSources: _opts.excludedSources,
|
};
|
||||||
feeSchedule: _opts.feeSchedule,
|
|
||||||
allowFallback: _opts.allowFallback,
|
|
||||||
shouldBatchBridgeOrders: _opts.shouldBatchBridgeOrders,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* gets the orders required for a market buy operation by (potentially) merging native orders with
|
* Gets the liquidity available for a market buy operation
|
||||||
* generated bridge orders.
|
|
||||||
* @param nativeOrders Native orders.
|
* @param nativeOrders Native orders.
|
||||||
* @param makerAmount Amount of maker asset to buy.
|
* @param makerAmount Amount of maker asset to buy.
|
||||||
* @param opts Options object.
|
* @param opts Options object.
|
||||||
* @return orders.
|
* @return MarketSideLiquidity.
|
||||||
*/
|
*/
|
||||||
public async getMarketBuyOrdersAsync(
|
public async getMarketBuyLiquidityAsync(
|
||||||
nativeOrders: SignedOrder[],
|
nativeOrders: SignedOrder[],
|
||||||
makerAmount: BigNumber,
|
makerAmount: BigNumber,
|
||||||
opts?: Partial<GetMarketOrdersOpts>,
|
opts?: Partial<GetMarketOrdersOpts>,
|
||||||
): Promise<OptimizedMarketOrder[]> {
|
): Promise<MarketSideLiquidity> {
|
||||||
if (nativeOrders.length === 0) {
|
if (nativeOrders.length === 0) {
|
||||||
throw new Error(AggregationError.EmptyOrders);
|
throw new Error(AggregationError.EmptyOrders);
|
||||||
}
|
}
|
||||||
@ -203,6 +220,17 @@ export class MarketOperationUtils {
|
|||||||
makerToken,
|
makerToken,
|
||||||
takerToken,
|
takerToken,
|
||||||
),
|
),
|
||||||
|
// Get ETH -> maker token price.
|
||||||
|
await DexOrderSampler.ops.getMedianSellRateAsync(
|
||||||
|
difference(FEE_QUOTE_SOURCES.concat(this._optionalSources()), _opts.excludedSources),
|
||||||
|
makerToken,
|
||||||
|
this._wethAddress,
|
||||||
|
ONE_ETHER,
|
||||||
|
this._wethAddress,
|
||||||
|
this._sampler.balancerPoolsCache,
|
||||||
|
this._liquidityProviderRegistry,
|
||||||
|
this._multiBridge,
|
||||||
|
),
|
||||||
// Get ETH -> taker token price.
|
// Get ETH -> taker token price.
|
||||||
await DexOrderSampler.ops.getMedianSellRateAsync(
|
await DexOrderSampler.ops.getMedianSellRateAsync(
|
||||||
difference(FEE_QUOTE_SOURCES.concat(this._optionalSources()), _opts.excludedSources),
|
difference(FEE_QUOTE_SOURCES.concat(this._optionalSources()), _opts.excludedSources),
|
||||||
@ -251,29 +279,81 @@ export class MarketOperationUtils {
|
|||||||
_opts,
|
_opts,
|
||||||
);
|
);
|
||||||
const [
|
const [
|
||||||
[orderFillableAmounts, liquidityProviderAddress, ethToTakerAssetRate, dexQuotes],
|
[orderFillableAmounts, liquidityProviderAddress, ethToMakerAssetRate, ethToTakerAssetRate, dexQuotes],
|
||||||
rfqtIndicativeQuotes,
|
rfqtIndicativeQuotes,
|
||||||
[balancerQuotes],
|
[balancerQuotes],
|
||||||
] = await Promise.all([samplerPromise, rfqtPromise, balancerPromise]);
|
] = await Promise.all([samplerPromise, rfqtPromise, balancerPromise]);
|
||||||
|
// Attach the LiquidityProvider address to the sample fillData
|
||||||
return this._generateOptimizedOrdersAsync({
|
(dexQuotes.find(quotes => quotes[0] && quotes[0].source === ERC20BridgeSource.LiquidityProvider) || []).forEach(
|
||||||
orderFillableAmounts,
|
q => (q.fillData = { poolAddress: liquidityProviderAddress }),
|
||||||
nativeOrders,
|
);
|
||||||
dexQuotes: dexQuotes.concat(balancerQuotes),
|
// Attach the MultiBridge address to the sample fillData
|
||||||
rfqtIndicativeQuotes,
|
(dexQuotes.find(quotes => quotes[0] && quotes[0].source === ERC20BridgeSource.MultiBridge) || []).forEach(
|
||||||
liquidityProviderAddress,
|
q => (q.fillData = { poolAddress: this._multiBridge }),
|
||||||
multiBridgeAddress: this._multiBridge,
|
);
|
||||||
inputToken: makerToken,
|
return {
|
||||||
outputToken: takerToken,
|
|
||||||
side: MarketOperation.Buy,
|
side: MarketOperation.Buy,
|
||||||
inputAmount: makerAmount,
|
inputAmount: makerAmount,
|
||||||
|
inputToken: makerToken,
|
||||||
|
outputToken: takerToken,
|
||||||
|
dexQuotes: dexQuotes.concat(balancerQuotes),
|
||||||
|
nativeOrders,
|
||||||
|
orderFillableAmounts,
|
||||||
ethToOutputRate: ethToTakerAssetRate,
|
ethToOutputRate: ethToTakerAssetRate,
|
||||||
|
ethToInputRate: ethToMakerAssetRate,
|
||||||
|
rfqtIndicativeQuotes,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gets the orders required for a market sell operation by (potentially) merging native orders with
|
||||||
|
* generated bridge orders.
|
||||||
|
* @param nativeOrders Native orders.
|
||||||
|
* @param takerAmount Amount of taker asset to sell.
|
||||||
|
* @param opts Options object.
|
||||||
|
* @return object with optimized orders and a QuoteReport
|
||||||
|
*/
|
||||||
|
public async getMarketSellOrdersAsync(
|
||||||
|
nativeOrders: SignedOrder[],
|
||||||
|
takerAmount: BigNumber,
|
||||||
|
opts?: Partial<GetMarketOrdersOpts>,
|
||||||
|
): Promise<OptimizedOrdersAndQuoteReport> {
|
||||||
|
const _opts = { ...DEFAULT_GET_MARKET_ORDERS_OPTS, ...opts };
|
||||||
|
const marketSideLiquidity = await this.getMarketSellLiquidityAsync(nativeOrders, takerAmount, _opts);
|
||||||
|
return this._generateOptimizedOrdersAsync(marketSideLiquidity, {
|
||||||
bridgeSlippage: _opts.bridgeSlippage,
|
bridgeSlippage: _opts.bridgeSlippage,
|
||||||
maxFallbackSlippage: _opts.maxFallbackSlippage,
|
maxFallbackSlippage: _opts.maxFallbackSlippage,
|
||||||
excludedSources: _opts.excludedSources,
|
excludedSources: _opts.excludedSources,
|
||||||
feeSchedule: _opts.feeSchedule,
|
feeSchedule: _opts.feeSchedule,
|
||||||
allowFallback: _opts.allowFallback,
|
allowFallback: _opts.allowFallback,
|
||||||
shouldBatchBridgeOrders: _opts.shouldBatchBridgeOrders,
|
shouldBatchBridgeOrders: _opts.shouldBatchBridgeOrders,
|
||||||
|
quoteRequestor: _opts.rfqt ? _opts.rfqt.quoteRequestor : undefined,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gets the orders required for a market buy operation by (potentially) merging native orders with
|
||||||
|
* generated bridge orders.
|
||||||
|
* @param nativeOrders Native orders.
|
||||||
|
* @param makerAmount Amount of maker asset to buy.
|
||||||
|
* @param opts Options object.
|
||||||
|
* @return object with optimized orders and a QuoteReport
|
||||||
|
*/
|
||||||
|
public async getMarketBuyOrdersAsync(
|
||||||
|
nativeOrders: SignedOrder[],
|
||||||
|
makerAmount: BigNumber,
|
||||||
|
opts?: Partial<GetMarketOrdersOpts>,
|
||||||
|
): Promise<OptimizedOrdersAndQuoteReport> {
|
||||||
|
const _opts = { ...DEFAULT_GET_MARKET_ORDERS_OPTS, ...opts };
|
||||||
|
const marketSideLiquidity = await this.getMarketBuyLiquidityAsync(nativeOrders, makerAmount, _opts);
|
||||||
|
return this._generateOptimizedOrdersAsync(marketSideLiquidity, {
|
||||||
|
bridgeSlippage: _opts.bridgeSlippage,
|
||||||
|
maxFallbackSlippage: _opts.maxFallbackSlippage,
|
||||||
|
excludedSources: _opts.excludedSources,
|
||||||
|
feeSchedule: _opts.feeSchedule,
|
||||||
|
allowFallback: _opts.allowFallback,
|
||||||
|
shouldBatchBridgeOrders: _opts.shouldBatchBridgeOrders,
|
||||||
|
quoteRequestor: _opts.rfqt ? _opts.rfqt.quoteRequestor : undefined,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -333,6 +413,7 @@ export class MarketOperationUtils {
|
|||||||
const batchOrderFillableAmounts = executeResults.splice(0, batchNativeOrders.length) as BigNumber[][];
|
const batchOrderFillableAmounts = executeResults.splice(0, batchNativeOrders.length) as BigNumber[][];
|
||||||
const batchEthToTakerAssetRate = executeResults.splice(0, batchNativeOrders.length) as BigNumber[];
|
const batchEthToTakerAssetRate = executeResults.splice(0, batchNativeOrders.length) as BigNumber[];
|
||||||
const batchDexQuotes = executeResults.splice(0, batchNativeOrders.length) as DexSample[][][];
|
const batchDexQuotes = executeResults.splice(0, batchNativeOrders.length) as DexSample[][][];
|
||||||
|
const ethToInputRate = ZERO_AMOUNT;
|
||||||
|
|
||||||
return Promise.all(
|
return Promise.all(
|
||||||
batchNativeOrders.map(async (nativeOrders, i) => {
|
batchNativeOrders.map(async (nativeOrders, i) => {
|
||||||
@ -345,23 +426,29 @@ export class MarketOperationUtils {
|
|||||||
const dexQuotes = batchDexQuotes[i];
|
const dexQuotes = batchDexQuotes[i];
|
||||||
const makerAmount = makerAmounts[i];
|
const makerAmount = makerAmounts[i];
|
||||||
try {
|
try {
|
||||||
return await this._generateOptimizedOrdersAsync({
|
const { optimizedOrders } = await this._generateOptimizedOrdersAsync(
|
||||||
orderFillableAmounts,
|
{
|
||||||
|
side: MarketOperation.Buy,
|
||||||
nativeOrders,
|
nativeOrders,
|
||||||
|
orderFillableAmounts,
|
||||||
dexQuotes,
|
dexQuotes,
|
||||||
|
inputAmount: makerAmount,
|
||||||
|
ethToOutputRate: ethToTakerAssetRate,
|
||||||
|
ethToInputRate,
|
||||||
rfqtIndicativeQuotes: [],
|
rfqtIndicativeQuotes: [],
|
||||||
inputToken: makerToken,
|
inputToken: makerToken,
|
||||||
outputToken: takerToken,
|
outputToken: takerToken,
|
||||||
side: MarketOperation.Buy,
|
},
|
||||||
inputAmount: makerAmount,
|
{
|
||||||
ethToOutputRate: ethToTakerAssetRate,
|
|
||||||
bridgeSlippage: _opts.bridgeSlippage,
|
bridgeSlippage: _opts.bridgeSlippage,
|
||||||
maxFallbackSlippage: _opts.maxFallbackSlippage,
|
maxFallbackSlippage: _opts.maxFallbackSlippage,
|
||||||
excludedSources: _opts.excludedSources,
|
excludedSources: _opts.excludedSources,
|
||||||
feeSchedule: _opts.feeSchedule,
|
feeSchedule: _opts.feeSchedule,
|
||||||
allowFallback: _opts.allowFallback,
|
allowFallback: _opts.allowFallback,
|
||||||
shouldBatchBridgeOrders: _opts.shouldBatchBridgeOrders,
|
shouldBatchBridgeOrders: _opts.shouldBatchBridgeOrders,
|
||||||
});
|
},
|
||||||
|
);
|
||||||
|
return optimizedOrders;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// It's possible for one of the pairs to have no path
|
// It's possible for one of the pairs to have no path
|
||||||
// rather than throw NO_OPTIMAL_PATH we return undefined
|
// rather than throw NO_OPTIMAL_PATH we return undefined
|
||||||
@ -371,39 +458,44 @@ export class MarketOperationUtils {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _generateOptimizedOrdersAsync(opts: {
|
private async _generateOptimizedOrdersAsync(
|
||||||
side: MarketOperation;
|
marketSideLiquidity: MarketSideLiquidity,
|
||||||
inputToken: string;
|
opts: {
|
||||||
outputToken: string;
|
|
||||||
inputAmount: BigNumber;
|
|
||||||
nativeOrders: SignedOrder[];
|
|
||||||
orderFillableAmounts: BigNumber[];
|
|
||||||
dexQuotes: DexSample[][];
|
|
||||||
rfqtIndicativeQuotes: RFQTIndicativeQuote[];
|
|
||||||
runLimit?: number;
|
runLimit?: number;
|
||||||
ethToOutputRate?: BigNumber;
|
|
||||||
bridgeSlippage?: number;
|
bridgeSlippage?: number;
|
||||||
maxFallbackSlippage?: number;
|
maxFallbackSlippage?: number;
|
||||||
excludedSources?: ERC20BridgeSource[];
|
excludedSources?: ERC20BridgeSource[];
|
||||||
feeSchedule?: FeeSchedule;
|
feeSchedule?: FeeSchedule;
|
||||||
allowFallback?: boolean;
|
allowFallback?: boolean;
|
||||||
shouldBatchBridgeOrders?: boolean;
|
shouldBatchBridgeOrders?: boolean;
|
||||||
liquidityProviderAddress?: string;
|
quoteRequestor?: QuoteRequestor;
|
||||||
multiBridgeAddress?: string;
|
},
|
||||||
}): Promise<OptimizedMarketOrder[]> {
|
): Promise<OptimizedOrdersAndQuoteReport> {
|
||||||
const { inputToken, outputToken, side, inputAmount } = opts;
|
const {
|
||||||
|
inputToken,
|
||||||
|
outputToken,
|
||||||
|
side,
|
||||||
|
inputAmount,
|
||||||
|
nativeOrders,
|
||||||
|
orderFillableAmounts,
|
||||||
|
rfqtIndicativeQuotes,
|
||||||
|
dexQuotes,
|
||||||
|
ethToOutputRate,
|
||||||
|
ethToInputRate,
|
||||||
|
} = marketSideLiquidity;
|
||||||
const maxFallbackSlippage = opts.maxFallbackSlippage || 0;
|
const maxFallbackSlippage = opts.maxFallbackSlippage || 0;
|
||||||
// Convert native orders and dex quotes into fill paths.
|
// Convert native orders and dex quotes into fill paths.
|
||||||
const paths = createFillPaths({
|
const paths = createFillPaths({
|
||||||
side,
|
side,
|
||||||
// Augment native orders with their fillable amounts.
|
// Augment native orders with their fillable amounts.
|
||||||
orders: [
|
orders: [
|
||||||
...createSignedOrdersWithFillableAmounts(side, opts.nativeOrders, opts.orderFillableAmounts),
|
...createSignedOrdersWithFillableAmounts(side, nativeOrders, orderFillableAmounts),
|
||||||
...createSignedOrdersFromRfqtIndicativeQuotes(opts.rfqtIndicativeQuotes),
|
...createSignedOrdersFromRfqtIndicativeQuotes(rfqtIndicativeQuotes),
|
||||||
],
|
],
|
||||||
dexQuotes: opts.dexQuotes,
|
dexQuotes,
|
||||||
targetInput: inputAmount,
|
targetInput: inputAmount,
|
||||||
ethToOutputRate: opts.ethToOutputRate,
|
ethToOutputRate,
|
||||||
|
ethToInputRate,
|
||||||
excludedSources: opts.excludedSources,
|
excludedSources: opts.excludedSources,
|
||||||
feeSchedule: opts.feeSchedule,
|
feeSchedule: opts.feeSchedule,
|
||||||
});
|
});
|
||||||
@ -444,17 +536,24 @@ export class MarketOperationUtils {
|
|||||||
optimalPath = [...nativeSubPath.filter(f => f !== lastNativeFillIfExists), ...nonNativeOptimalPath];
|
optimalPath = [...nativeSubPath.filter(f => f !== lastNativeFillIfExists), ...nonNativeOptimalPath];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return createOrdersFromPath(optimalPath, {
|
const optimizedOrders = createOrdersFromPath(optimalPath, {
|
||||||
side,
|
side,
|
||||||
inputToken,
|
inputToken,
|
||||||
outputToken,
|
outputToken,
|
||||||
orderDomain: this._orderDomain,
|
orderDomain: this._orderDomain,
|
||||||
contractAddresses: this.contractAddresses,
|
contractAddresses: this.contractAddresses,
|
||||||
bridgeSlippage: opts.bridgeSlippage || 0,
|
bridgeSlippage: opts.bridgeSlippage || 0,
|
||||||
liquidityProviderAddress: opts.liquidityProviderAddress,
|
|
||||||
multiBridgeAddress: opts.multiBridgeAddress,
|
|
||||||
shouldBatchBridgeOrders: !!opts.shouldBatchBridgeOrders,
|
shouldBatchBridgeOrders: !!opts.shouldBatchBridgeOrders,
|
||||||
});
|
});
|
||||||
|
const quoteReport = new QuoteReportGenerator(
|
||||||
|
side,
|
||||||
|
_.flatten(dexQuotes),
|
||||||
|
nativeOrders,
|
||||||
|
orderFillableAmounts,
|
||||||
|
_.flatten(optimizedOrders.map(o => o.fills)),
|
||||||
|
opts.quoteRequestor,
|
||||||
|
).generateReport();
|
||||||
|
return { optimizedOrders, quoteReport };
|
||||||
}
|
}
|
||||||
|
|
||||||
private _optionalSources(): ERC20BridgeSource[] {
|
private _optionalSources(): ERC20BridgeSource[] {
|
||||||
|
@ -24,6 +24,8 @@ import {
|
|||||||
CurveFillData,
|
CurveFillData,
|
||||||
ERC20BridgeSource,
|
ERC20BridgeSource,
|
||||||
Fill,
|
Fill,
|
||||||
|
LiquidityProviderFillData,
|
||||||
|
MultiBridgeFillData,
|
||||||
NativeCollapsedFill,
|
NativeCollapsedFill,
|
||||||
OptimizedMarketOrder,
|
OptimizedMarketOrder,
|
||||||
OrderDomain,
|
OrderDomain,
|
||||||
@ -143,8 +145,6 @@ export interface CreateOrderFromPathOpts {
|
|||||||
contractAddresses: ContractAddresses;
|
contractAddresses: ContractAddresses;
|
||||||
bridgeSlippage: number;
|
bridgeSlippage: number;
|
||||||
shouldBatchBridgeOrders: boolean;
|
shouldBatchBridgeOrders: boolean;
|
||||||
liquidityProviderAddress?: string;
|
|
||||||
multiBridgeAddress?: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert sell fills into orders.
|
// Convert sell fills into orders.
|
||||||
@ -177,7 +177,8 @@ export function createOrdersFromPath(path: Fill[], opts: CreateOrderFromPathOpts
|
|||||||
return orders;
|
return orders;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getBridgeAddressFromSource(source: ERC20BridgeSource, opts: CreateOrderFromPathOpts): string {
|
function getBridgeAddressFromFill(fill: CollapsedFill, opts: CreateOrderFromPathOpts): string {
|
||||||
|
const source = fill.source;
|
||||||
switch (source) {
|
switch (source) {
|
||||||
case ERC20BridgeSource.Eth2Dai:
|
case ERC20BridgeSource.Eth2Dai:
|
||||||
return opts.contractAddresses.eth2DaiBridge;
|
return opts.contractAddresses.eth2DaiBridge;
|
||||||
@ -192,15 +193,9 @@ function getBridgeAddressFromSource(source: ERC20BridgeSource, opts: CreateOrder
|
|||||||
case ERC20BridgeSource.Balancer:
|
case ERC20BridgeSource.Balancer:
|
||||||
return opts.contractAddresses.balancerBridge;
|
return opts.contractAddresses.balancerBridge;
|
||||||
case ERC20BridgeSource.LiquidityProvider:
|
case ERC20BridgeSource.LiquidityProvider:
|
||||||
if (opts.liquidityProviderAddress === undefined) {
|
return (fill.fillData as LiquidityProviderFillData).poolAddress;
|
||||||
throw new Error('Cannot create a LiquidityProvider order without a LiquidityProvider pool address.');
|
|
||||||
}
|
|
||||||
return opts.liquidityProviderAddress;
|
|
||||||
case ERC20BridgeSource.MultiBridge:
|
case ERC20BridgeSource.MultiBridge:
|
||||||
if (opts.multiBridgeAddress === undefined) {
|
return (fill.fillData as MultiBridgeFillData).poolAddress;
|
||||||
throw new Error('Cannot create a MultiBridge order without a MultiBridge address.');
|
|
||||||
}
|
|
||||||
return opts.multiBridgeAddress;
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -209,7 +204,7 @@ function getBridgeAddressFromSource(source: ERC20BridgeSource, opts: CreateOrder
|
|||||||
|
|
||||||
function createBridgeOrder(fill: CollapsedFill, opts: CreateOrderFromPathOpts): OptimizedMarketOrder {
|
function createBridgeOrder(fill: CollapsedFill, opts: CreateOrderFromPathOpts): OptimizedMarketOrder {
|
||||||
const [makerToken, takerToken] = getMakerTakerTokens(opts);
|
const [makerToken, takerToken] = getMakerTakerTokens(opts);
|
||||||
const bridgeAddress = getBridgeAddressFromSource(fill.source, opts);
|
const bridgeAddress = getBridgeAddressFromFill(fill, opts);
|
||||||
|
|
||||||
let makerAssetData;
|
let makerAssetData;
|
||||||
switch (fill.source) {
|
switch (fill.source) {
|
||||||
|
@ -3,16 +3,20 @@ import { BigNumber } from '@0x/utils';
|
|||||||
import { MarketOperation } from '../../types';
|
import { MarketOperation } from '../../types';
|
||||||
|
|
||||||
import { ZERO_AMOUNT } from './constants';
|
import { ZERO_AMOUNT } from './constants';
|
||||||
import { getPathAdjustedSize, getPathSize, isValidPath } from './fills';
|
import {
|
||||||
|
arePathFlagsAllowed,
|
||||||
|
getCompleteRate,
|
||||||
|
getPathAdjustedCompleteRate,
|
||||||
|
getPathAdjustedRate,
|
||||||
|
getPathAdjustedSize,
|
||||||
|
getPathSize,
|
||||||
|
isValidPath,
|
||||||
|
} from './fills';
|
||||||
import { Fill } from './types';
|
import { Fill } from './types';
|
||||||
|
|
||||||
// tslint:disable: prefer-for-of custom-no-magic-numbers completed-docs
|
// tslint:disable: prefer-for-of custom-no-magic-numbers completed-docs no-bitwise
|
||||||
|
|
||||||
const RUN_LIMIT_DECAY_FACTOR = 0.8;
|
const RUN_LIMIT_DECAY_FACTOR = 0.5;
|
||||||
// Used to yield the event loop when performing CPU intensive tasks
|
|
||||||
// tislint:disable-next-line:no-inferred-empty-object-type
|
|
||||||
const setImmediateAsync = async (delay: number = 0) =>
|
|
||||||
new Promise<void>(resolve => setImmediate(() => resolve(), delay));
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find the optimal mixture of paths that maximizes (for sells) or minimizes
|
* Find the optimal mixture of paths that maximizes (for sells) or minimizes
|
||||||
@ -22,16 +26,21 @@ export async function findOptimalPathAsync(
|
|||||||
side: MarketOperation,
|
side: MarketOperation,
|
||||||
paths: Fill[][],
|
paths: Fill[][],
|
||||||
targetInput: BigNumber,
|
targetInput: BigNumber,
|
||||||
runLimit: number = 2 ** 15,
|
runLimit: number = 2 ** 8,
|
||||||
): Promise<Fill[] | undefined> {
|
): Promise<Fill[] | undefined> {
|
||||||
// Sort paths in descending order by adjusted output amount.
|
// Sort paths by descending adjusted completed rate.
|
||||||
const sortedPaths = paths
|
const sortedPaths = paths
|
||||||
.slice(0)
|
.slice(0)
|
||||||
.sort((a, b) => getPathAdjustedSize(b, targetInput)[1].comparedTo(getPathAdjustedSize(a, targetInput)[1]));
|
.sort((a, b) =>
|
||||||
|
getPathAdjustedCompleteRate(side, b, targetInput).comparedTo(
|
||||||
|
getPathAdjustedCompleteRate(side, a, targetInput),
|
||||||
|
),
|
||||||
|
);
|
||||||
let optimalPath = sortedPaths[0] || [];
|
let optimalPath = sortedPaths[0] || [];
|
||||||
for (const [i, path] of sortedPaths.slice(1).entries()) {
|
for (const [i, path] of sortedPaths.slice(1).entries()) {
|
||||||
optimalPath = mixPaths(side, optimalPath, path, targetInput, runLimit * RUN_LIMIT_DECAY_FACTOR ** i);
|
optimalPath = mixPaths(side, optimalPath, path, targetInput, runLimit * RUN_LIMIT_DECAY_FACTOR ** i);
|
||||||
await setImmediateAsync();
|
// Yield to event loop.
|
||||||
|
await Promise.resolve();
|
||||||
}
|
}
|
||||||
return isPathComplete(optimalPath, targetInput) ? optimalPath : undefined;
|
return isPathComplete(optimalPath, targetInput) ? optimalPath : undefined;
|
||||||
}
|
}
|
||||||
@ -43,10 +52,12 @@ function mixPaths(
|
|||||||
targetInput: BigNumber,
|
targetInput: BigNumber,
|
||||||
maxSteps: number,
|
maxSteps: number,
|
||||||
): Fill[] {
|
): Fill[] {
|
||||||
let bestPath: Fill[] = [];
|
const _maxSteps = Math.max(maxSteps, 32);
|
||||||
let bestPathInput = ZERO_AMOUNT;
|
|
||||||
let bestPathRate = ZERO_AMOUNT;
|
|
||||||
let steps = 0;
|
let steps = 0;
|
||||||
|
// We assume pathA is the better of the two initially.
|
||||||
|
let bestPath: Fill[] = pathA;
|
||||||
|
let [bestPathInput, bestPathOutput] = getPathAdjustedSize(pathA, targetInput);
|
||||||
|
let bestPathRate = getCompleteRate(side, bestPathInput, bestPathOutput, targetInput);
|
||||||
const _isBetterPath = (input: BigNumber, rate: BigNumber) => {
|
const _isBetterPath = (input: BigNumber, rate: BigNumber) => {
|
||||||
if (bestPathInput.lt(targetInput)) {
|
if (bestPathInput.lt(targetInput)) {
|
||||||
return input.gt(bestPathInput);
|
return input.gt(bestPathInput);
|
||||||
@ -55,46 +66,77 @@ function mixPaths(
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
const _walk = (path: Fill[], input: BigNumber, output: BigNumber, allFills: Fill[]) => {
|
const _walk = (path: Fill[], input: BigNumber, output: BigNumber, flags: number, remainingFills: Fill[]) => {
|
||||||
steps += 1;
|
steps += 1;
|
||||||
const rate = getRate(side, input, output);
|
const rate = getCompleteRate(side, input, output, targetInput);
|
||||||
if (_isBetterPath(input, rate)) {
|
if (_isBetterPath(input, rate)) {
|
||||||
bestPath = path;
|
bestPath = path;
|
||||||
bestPathInput = input;
|
bestPathInput = input;
|
||||||
|
bestPathOutput = output;
|
||||||
bestPathRate = rate;
|
bestPathRate = rate;
|
||||||
}
|
}
|
||||||
const remainingInput = targetInput.minus(input);
|
const remainingInput = targetInput.minus(input);
|
||||||
if (remainingInput.gt(0)) {
|
if (remainingInput.gt(0)) {
|
||||||
for (let i = 0; i < allFills.length; ++i) {
|
for (let i = 0; i < remainingFills.length && steps < _maxSteps; ++i) {
|
||||||
const fill = allFills[i];
|
const fill = remainingFills[i];
|
||||||
if (steps + 1 >= maxSteps) {
|
// Only walk valid paths.
|
||||||
break;
|
if (!isValidNextPathFill(path, flags, fill)) {
|
||||||
}
|
|
||||||
const childPath = [...path, fill];
|
|
||||||
if (!isValidPath(childPath, true)) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// Remove this fill from the next list of candidate fills.
|
// Remove this fill from the next list of candidate fills.
|
||||||
const nextAllFills = allFills.slice();
|
const nextRemainingFills = remainingFills.slice();
|
||||||
nextAllFills.splice(i, 1);
|
nextRemainingFills.splice(i, 1);
|
||||||
// Recurse.
|
// Recurse.
|
||||||
_walk(
|
_walk(
|
||||||
childPath,
|
[...path, fill],
|
||||||
input.plus(BigNumber.min(remainingInput, fill.input)),
|
input.plus(BigNumber.min(remainingInput, fill.input)),
|
||||||
output.plus(
|
output.plus(
|
||||||
// Clip the output of the next fill to the remaining
|
// Clip the output of the next fill to the remaining
|
||||||
// input.
|
// input.
|
||||||
clipFillAdjustedOutput(fill, remainingInput),
|
clipFillAdjustedOutput(fill, remainingInput),
|
||||||
),
|
),
|
||||||
nextAllFills,
|
flags | fill.flags,
|
||||||
|
nextRemainingFills,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
_walk(bestPath, ZERO_AMOUNT, ZERO_AMOUNT, [...pathA, ...pathB].sort((a, b) => b.rate.comparedTo(a.rate)));
|
const allFills = [...pathA, ...pathB];
|
||||||
|
const sources = allFills.filter(f => f.index === 0).map(f => f.sourcePathId);
|
||||||
|
const rateBySource = Object.assign(
|
||||||
|
{},
|
||||||
|
...sources.map(s => ({
|
||||||
|
[s]: getPathAdjustedRate(side, allFills.filter(f => f.sourcePathId === s), targetInput),
|
||||||
|
})),
|
||||||
|
);
|
||||||
|
// Sort subpaths by rate and keep fills contiguous to improve our
|
||||||
|
// chances of walking ideal, valid paths first.
|
||||||
|
const sortedFills = allFills.sort((a, b) => {
|
||||||
|
if (a.sourcePathId !== b.sourcePathId) {
|
||||||
|
return rateBySource[b.sourcePathId].comparedTo(rateBySource[a.sourcePathId]);
|
||||||
|
}
|
||||||
|
return a.index - b.index;
|
||||||
|
});
|
||||||
|
_walk([], ZERO_AMOUNT, ZERO_AMOUNT, 0, sortedFills);
|
||||||
|
if (!isValidPath(bestPath)) {
|
||||||
|
throw new Error('nooope');
|
||||||
|
}
|
||||||
return bestPath;
|
return bestPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isValidNextPathFill(path: Fill[], pathFlags: number, fill: Fill): boolean {
|
||||||
|
if (path.length === 0) {
|
||||||
|
return !fill.parent;
|
||||||
|
}
|
||||||
|
if (path[path.length - 1] === fill.parent) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (fill.parent) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return arePathFlagsAllowed(pathFlags | fill.flags);
|
||||||
|
}
|
||||||
|
|
||||||
function isPathComplete(path: Fill[], targetInput: BigNumber): boolean {
|
function isPathComplete(path: Fill[], targetInput: BigNumber): boolean {
|
||||||
const [input] = getPathSize(path);
|
const [input] = getPathSize(path);
|
||||||
return input.gte(targetInput);
|
return input.gte(targetInput);
|
||||||
@ -104,16 +146,7 @@ function clipFillAdjustedOutput(fill: Fill, remainingInput: BigNumber): BigNumbe
|
|||||||
if (fill.input.lte(remainingInput)) {
|
if (fill.input.lte(remainingInput)) {
|
||||||
return fill.adjustedOutput;
|
return fill.adjustedOutput;
|
||||||
}
|
}
|
||||||
|
// Penalty does not get interpolated.
|
||||||
const penalty = fill.adjustedOutput.minus(fill.output);
|
const penalty = fill.adjustedOutput.minus(fill.output);
|
||||||
return remainingInput.times(fill.rate).plus(penalty);
|
return remainingInput.times(fill.output.div(fill.input)).plus(penalty);
|
||||||
}
|
|
||||||
|
|
||||||
function getRate(side: MarketOperation, input: BigNumber, output: BigNumber): BigNumber {
|
|
||||||
if (input.eq(0) || output.eq(0)) {
|
|
||||||
return ZERO_AMOUNT;
|
|
||||||
}
|
|
||||||
if (side === MarketOperation.Sell) {
|
|
||||||
return output.div(input);
|
|
||||||
}
|
|
||||||
return input.div(output);
|
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,11 @@
|
|||||||
import { ERC20BridgeSamplerContract } from '@0x/contract-wrappers';
|
import { ERC20BridgeSamplerContract } from '@0x/contract-wrappers';
|
||||||
|
import { RFQTIndicativeQuote } from '@0x/quote-server';
|
||||||
|
import { MarketOperation, SignedOrder } from '@0x/types';
|
||||||
import { BigNumber } from '@0x/utils';
|
import { BigNumber } from '@0x/utils';
|
||||||
|
|
||||||
import { RfqtRequestOpts, SignedOrderWithFillableAmounts } from '../../types';
|
import { RfqtRequestOpts, SignedOrderWithFillableAmounts } from '../../types';
|
||||||
import { QuoteRequestor } from '../../utils/quote_requestor';
|
import { QuoteRequestor } from '../../utils/quote_requestor';
|
||||||
|
import { QuoteReport } from '../quote_report_generator';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Order domain keys: chainId and exchange
|
* Order domain keys: chainId and exchange
|
||||||
@ -85,6 +88,14 @@ export interface UniswapV2FillData extends FillData {
|
|||||||
tokenAddressPath: string[];
|
tokenAddressPath: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface LiquidityProviderFillData extends FillData {
|
||||||
|
poolAddress: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface MultiBridgeFillData extends FillData {
|
||||||
|
poolAddress: string;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents an individual DEX sample from the sampler contract.
|
* Represents an individual DEX sample from the sampler contract.
|
||||||
*/
|
*/
|
||||||
@ -109,16 +120,16 @@ export enum FillFlags {
|
|||||||
* Represents a node on a fill path.
|
* Represents a node on a fill path.
|
||||||
*/
|
*/
|
||||||
export interface Fill<TFillData extends FillData = FillData> {
|
export interface Fill<TFillData extends FillData = FillData> {
|
||||||
|
// Unique ID of the original source path this fill belongs to.
|
||||||
|
// This is generated when the path is generated and is useful to distinguish
|
||||||
|
// paths that have the same `source` IDs but are distinct (e.g., Curves).
|
||||||
|
sourcePathId: string;
|
||||||
// See `FillFlags`.
|
// See `FillFlags`.
|
||||||
flags: FillFlags;
|
flags: FillFlags;
|
||||||
// Input fill amount (taker asset amount in a sell, maker asset amount in a buy).
|
// Input fill amount (taker asset amount in a sell, maker asset amount in a buy).
|
||||||
input: BigNumber;
|
input: BigNumber;
|
||||||
// Output fill amount (maker asset amount in a sell, taker asset amount in a buy).
|
// Output fill amount (maker asset amount in a sell, taker asset amount in a buy).
|
||||||
output: BigNumber;
|
output: BigNumber;
|
||||||
// The maker/taker rate.
|
|
||||||
rate: BigNumber;
|
|
||||||
// The maker/taker rate, adjusted by fees.
|
|
||||||
adjustedRate: BigNumber;
|
|
||||||
// The output fill amount, ajdusted by fees.
|
// The output fill amount, ajdusted by fees.
|
||||||
adjustedOutput: BigNumber;
|
adjustedOutput: BigNumber;
|
||||||
// Fill that must precede this one. This enforces certain fills to be contiguous.
|
// Fill that must precede this one. This enforces certain fills to be contiguous.
|
||||||
@ -136,6 +147,10 @@ export interface Fill<TFillData extends FillData = FillData> {
|
|||||||
* Represents continguous fills on a path that have been merged together.
|
* Represents continguous fills on a path that have been merged together.
|
||||||
*/
|
*/
|
||||||
export interface CollapsedFill<TFillData extends FillData = FillData> {
|
export interface CollapsedFill<TFillData extends FillData = FillData> {
|
||||||
|
// Unique ID of the original source path this fill belongs to.
|
||||||
|
// This is generated when the path is generated and is useful to distinguish
|
||||||
|
// paths that have the same `source` IDs but are distinct (e.g., Curves).
|
||||||
|
sourcePathId: string;
|
||||||
/**
|
/**
|
||||||
* The source DEX.
|
* The source DEX.
|
||||||
*/
|
*/
|
||||||
@ -254,3 +269,28 @@ export interface SourceQuoteOperation<TFillData extends FillData = FillData> ext
|
|||||||
source: ERC20BridgeSource;
|
source: ERC20BridgeSource;
|
||||||
fillData?: TFillData;
|
fillData?: TFillData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface OptimizedOrdersAndQuoteReport {
|
||||||
|
optimizedOrders: OptimizedMarketOrder[];
|
||||||
|
quoteReport: QuoteReport;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type MarketDepthSide = Array<Array<DexSample<FillData>>>;
|
||||||
|
|
||||||
|
export interface MarketDepth {
|
||||||
|
bids: MarketDepthSide;
|
||||||
|
asks: MarketDepthSide;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface MarketSideLiquidity {
|
||||||
|
side: MarketOperation;
|
||||||
|
inputAmount: BigNumber;
|
||||||
|
inputToken: string;
|
||||||
|
outputToken: string;
|
||||||
|
dexQuotes: Array<Array<DexSample<FillData>>>;
|
||||||
|
nativeOrders: SignedOrder[];
|
||||||
|
orderFillableAmounts: BigNumber[];
|
||||||
|
ethToOutputRate: BigNumber;
|
||||||
|
ethToInputRate: BigNumber;
|
||||||
|
rfqtIndicativeQuotes: RFQTIndicativeQuote[];
|
||||||
|
}
|
||||||
|
161
packages/asset-swapper/src/utils/quote_report_generator.ts
Normal file
161
packages/asset-swapper/src/utils/quote_report_generator.ts
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
import { orderHashUtils } from '@0x/order-utils';
|
||||||
|
import { BigNumber } from '@0x/utils';
|
||||||
|
import * as _ from 'lodash';
|
||||||
|
|
||||||
|
import { ERC20BridgeSource, SignedOrder } from '..';
|
||||||
|
import { MarketOperation } from '../types';
|
||||||
|
|
||||||
|
import { CollapsedFill, DexSample, NativeCollapsedFill } from './market_operation_utils/types';
|
||||||
|
import { QuoteRequestor } from './quote_requestor';
|
||||||
|
|
||||||
|
export interface BridgeReportSource {
|
||||||
|
liquiditySource: Exclude<ERC20BridgeSource, ERC20BridgeSource.Native>;
|
||||||
|
makerAmount: BigNumber;
|
||||||
|
takerAmount: BigNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface NativeReportSourceBase {
|
||||||
|
liquiditySource: ERC20BridgeSource.Native;
|
||||||
|
makerAmount: BigNumber;
|
||||||
|
takerAmount: BigNumber;
|
||||||
|
orderHash: string;
|
||||||
|
nativeOrder: SignedOrder;
|
||||||
|
fillableTakerAmount: BigNumber;
|
||||||
|
}
|
||||||
|
export interface NativeOrderbookReportSource extends NativeReportSourceBase {
|
||||||
|
isRfqt: false;
|
||||||
|
}
|
||||||
|
export interface NativeRFQTReportSource extends NativeReportSourceBase {
|
||||||
|
isRfqt: true;
|
||||||
|
makerUri: string;
|
||||||
|
}
|
||||||
|
export type QuoteReportSource = BridgeReportSource | NativeOrderbookReportSource | NativeRFQTReportSource;
|
||||||
|
|
||||||
|
export interface QuoteReport {
|
||||||
|
sourcesConsidered: QuoteReportSource[];
|
||||||
|
sourcesDelivered: QuoteReportSource[];
|
||||||
|
}
|
||||||
|
|
||||||
|
const nativeOrderFromCollapsedFill = (cf: CollapsedFill): SignedOrder | undefined => {
|
||||||
|
// Cast as NativeCollapsedFill and then check
|
||||||
|
// if it really is a NativeCollapsedFill
|
||||||
|
const possibleNativeCollapsedFill = cf as NativeCollapsedFill;
|
||||||
|
if (possibleNativeCollapsedFill.fillData && possibleNativeCollapsedFill.fillData.order) {
|
||||||
|
return possibleNativeCollapsedFill.fillData.order;
|
||||||
|
} else {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export class QuoteReportGenerator {
|
||||||
|
private readonly _dexQuotes: DexSample[];
|
||||||
|
private readonly _nativeOrders: SignedOrder[];
|
||||||
|
private readonly _orderHashesToFillableAmounts: { [orderHash: string]: BigNumber };
|
||||||
|
private readonly _marketOperation: MarketOperation;
|
||||||
|
private readonly _collapsedFills: CollapsedFill[];
|
||||||
|
private readonly _quoteRequestor?: QuoteRequestor;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
marketOperation: MarketOperation,
|
||||||
|
dexQuotes: DexSample[],
|
||||||
|
nativeOrders: SignedOrder[],
|
||||||
|
orderFillableAmounts: BigNumber[],
|
||||||
|
collapsedFills: CollapsedFill[],
|
||||||
|
quoteRequestor?: QuoteRequestor,
|
||||||
|
) {
|
||||||
|
this._dexQuotes = dexQuotes;
|
||||||
|
this._nativeOrders = nativeOrders;
|
||||||
|
this._marketOperation = marketOperation;
|
||||||
|
this._quoteRequestor = quoteRequestor;
|
||||||
|
this._collapsedFills = collapsedFills;
|
||||||
|
|
||||||
|
// convert order fillable amount array to easy to look up hash
|
||||||
|
if (orderFillableAmounts.length !== nativeOrders.length) {
|
||||||
|
// length mismatch, abort
|
||||||
|
this._orderHashesToFillableAmounts = {};
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const orderHashesToFillableAmounts: { [orderHash: string]: BigNumber } = {};
|
||||||
|
nativeOrders.forEach((nativeOrder, idx) => {
|
||||||
|
orderHashesToFillableAmounts[orderHashUtils.getOrderHash(nativeOrder)] = orderFillableAmounts[idx];
|
||||||
|
});
|
||||||
|
this._orderHashesToFillableAmounts = orderHashesToFillableAmounts;
|
||||||
|
}
|
||||||
|
|
||||||
|
public generateReport(): QuoteReport {
|
||||||
|
const dexReportSourcesConsidered = this._dexQuotes.map(dq => this._dexSampleToReportSource(dq));
|
||||||
|
const nativeOrderSourcesConsidered = this._nativeOrders.map(no => this._nativeOrderToReportSource(no));
|
||||||
|
|
||||||
|
const sourcesConsidered = [...dexReportSourcesConsidered, ...nativeOrderSourcesConsidered];
|
||||||
|
const sourcesDelivered = this._collapsedFills.map(collapsedFill => {
|
||||||
|
const foundNativeOrder = nativeOrderFromCollapsedFill(collapsedFill);
|
||||||
|
if (foundNativeOrder) {
|
||||||
|
return this._nativeOrderToReportSource(foundNativeOrder);
|
||||||
|
} else {
|
||||||
|
return this._dexSampleToReportSource(collapsedFill);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
sourcesConsidered,
|
||||||
|
sourcesDelivered,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private _dexSampleToReportSource(ds: DexSample): BridgeReportSource {
|
||||||
|
const liquiditySource = ds.source;
|
||||||
|
|
||||||
|
if (liquiditySource === ERC20BridgeSource.Native) {
|
||||||
|
throw new Error(`Unexpected liquidity source Native`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// input and output map to different values
|
||||||
|
// based on the market operation
|
||||||
|
if (this._marketOperation === MarketOperation.Buy) {
|
||||||
|
return {
|
||||||
|
makerAmount: ds.input,
|
||||||
|
takerAmount: ds.output,
|
||||||
|
liquiditySource,
|
||||||
|
};
|
||||||
|
} else if (this._marketOperation === MarketOperation.Sell) {
|
||||||
|
return {
|
||||||
|
makerAmount: ds.output,
|
||||||
|
takerAmount: ds.input,
|
||||||
|
liquiditySource,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
throw new Error(`Unexpected marketOperation ${this._marketOperation}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _nativeOrderToReportSource(nativeOrder: SignedOrder): NativeRFQTReportSource | NativeOrderbookReportSource {
|
||||||
|
const orderHash = orderHashUtils.getOrderHash(nativeOrder);
|
||||||
|
|
||||||
|
const nativeOrderBase: NativeReportSourceBase = {
|
||||||
|
liquiditySource: ERC20BridgeSource.Native,
|
||||||
|
makerAmount: nativeOrder.makerAssetAmount,
|
||||||
|
takerAmount: nativeOrder.takerAssetAmount,
|
||||||
|
fillableTakerAmount: this._orderHashesToFillableAmounts[orderHash],
|
||||||
|
nativeOrder,
|
||||||
|
orderHash,
|
||||||
|
};
|
||||||
|
|
||||||
|
// if we find this is an rfqt order, label it as such and associate makerUri
|
||||||
|
const foundRfqtMakerUri = this._quoteRequestor && this._quoteRequestor.getMakerUriForOrderHash(orderHash);
|
||||||
|
if (foundRfqtMakerUri) {
|
||||||
|
const rfqtSource: NativeRFQTReportSource = {
|
||||||
|
...nativeOrderBase,
|
||||||
|
isRfqt: true,
|
||||||
|
makerUri: foundRfqtMakerUri,
|
||||||
|
};
|
||||||
|
return rfqtSource;
|
||||||
|
} else {
|
||||||
|
// if it's not an rfqt order, treat as normal
|
||||||
|
const regularNativeOrder: NativeOrderbookReportSource = {
|
||||||
|
...nativeOrderBase,
|
||||||
|
isRfqt: false,
|
||||||
|
};
|
||||||
|
return regularNativeOrder;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,9 +1,9 @@
|
|||||||
import { schemas, SchemaValidator } from '@0x/json-schemas';
|
import { schemas, SchemaValidator } from '@0x/json-schemas';
|
||||||
import { assetDataUtils, orderCalculationUtils, SignedOrder } from '@0x/order-utils';
|
import { assetDataUtils, orderCalculationUtils, orderHashUtils, SignedOrder } from '@0x/order-utils';
|
||||||
import { RFQTFirmQuote, RFQTIndicativeQuote, TakerRequest } from '@0x/quote-server';
|
import { RFQTFirmQuote, RFQTIndicativeQuote, TakerRequest } from '@0x/quote-server';
|
||||||
import { ERC20AssetData } from '@0x/types';
|
import { ERC20AssetData } from '@0x/types';
|
||||||
import { BigNumber, logUtils } from '@0x/utils';
|
import { BigNumber, logUtils } from '@0x/utils';
|
||||||
import Axios, { AxiosInstance, AxiosResponse } from 'axios';
|
import Axios, { AxiosInstance } from 'axios';
|
||||||
import { Agent as HttpAgent } from 'http';
|
import { Agent as HttpAgent } from 'http';
|
||||||
import { Agent as HttpsAgent } from 'https';
|
import { Agent as HttpsAgent } from 'https';
|
||||||
|
|
||||||
@ -92,6 +92,7 @@ export type LogFunction = (obj: object, msg?: string, ...args: any[]) => void;
|
|||||||
|
|
||||||
export class QuoteRequestor {
|
export class QuoteRequestor {
|
||||||
private readonly _schemaValidator: SchemaValidator = new SchemaValidator();
|
private readonly _schemaValidator: SchemaValidator = new SchemaValidator();
|
||||||
|
private readonly _orderHashToMakerUri: { [orderHash: string]: string } = {};
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly _rfqtAssetOfferings: RfqtMakerAssetOfferings,
|
private readonly _rfqtAssetOfferings: RfqtMakerAssetOfferings,
|
||||||
@ -112,7 +113,7 @@ export class QuoteRequestor {
|
|||||||
const _opts: RfqtRequestOpts = { ...constants.DEFAULT_RFQT_REQUEST_OPTS, ...options };
|
const _opts: RfqtRequestOpts = { ...constants.DEFAULT_RFQT_REQUEST_OPTS, ...options };
|
||||||
assertTakerAddressOrThrow(_opts.takerAddress);
|
assertTakerAddressOrThrow(_opts.takerAddress);
|
||||||
|
|
||||||
const firmQuotes = await this._getQuotesAsync<RFQTFirmQuote>( // not yet BigNumber
|
const firmQuoteResponses = await this._getQuotesAsync<RFQTFirmQuote>( // not yet BigNumber
|
||||||
makerAssetData,
|
makerAssetData,
|
||||||
takerAssetData,
|
takerAssetData,
|
||||||
assetFillAmount,
|
assetFillAmount,
|
||||||
@ -121,41 +122,38 @@ export class QuoteRequestor {
|
|||||||
'firm',
|
'firm',
|
||||||
);
|
);
|
||||||
|
|
||||||
const ordersWithStringInts = firmQuotes.map(quote => quote.signedOrder);
|
const result: RFQTFirmQuote[] = [];
|
||||||
|
firmQuoteResponses.forEach(firmQuoteResponse => {
|
||||||
|
const orderWithStringInts = firmQuoteResponse.response.signedOrder;
|
||||||
|
|
||||||
const validatedOrdersWithStringInts = ordersWithStringInts.filter(order => {
|
|
||||||
try {
|
try {
|
||||||
const hasValidSchema = this._schemaValidator.isValid(order, schemas.signedOrderSchema);
|
const hasValidSchema = this._schemaValidator.isValid(orderWithStringInts, schemas.signedOrderSchema);
|
||||||
if (!hasValidSchema) {
|
if (!hasValidSchema) {
|
||||||
throw new Error('order not valid');
|
throw new Error('Order not valid');
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this._warningLogger(order, `Invalid RFQ-t order received, filtering out. ${err.message}`);
|
this._warningLogger(orderWithStringInts, `Invalid RFQ-t order received, filtering out. ${err.message}`);
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
!hasExpectedAssetData(
|
!hasExpectedAssetData(
|
||||||
makerAssetData,
|
makerAssetData,
|
||||||
takerAssetData,
|
takerAssetData,
|
||||||
order.makerAssetData.toLowerCase(),
|
orderWithStringInts.makerAssetData.toLowerCase(),
|
||||||
order.takerAssetData.toLowerCase(),
|
orderWithStringInts.takerAssetData.toLowerCase(),
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
this._warningLogger(order, 'Unexpected asset data in RFQ-T order, filtering out');
|
this._warningLogger(orderWithStringInts, 'Unexpected asset data in RFQ-T order, filtering out');
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (order.takerAddress.toLowerCase() !== _opts.takerAddress.toLowerCase()) {
|
if (orderWithStringInts.takerAddress.toLowerCase() !== _opts.takerAddress.toLowerCase()) {
|
||||||
this._warningLogger(order, 'Unexpected takerAddress in RFQ-T order, filtering out');
|
this._warningLogger(orderWithStringInts, 'Unexpected takerAddress in RFQ-T order, filtering out');
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
const orderWithBigNumberInts: SignedOrder = {
|
||||||
});
|
|
||||||
|
|
||||||
const validatedOrders: SignedOrder[] = validatedOrdersWithStringInts.map(orderWithStringInts => {
|
|
||||||
return {
|
|
||||||
...orderWithStringInts,
|
...orderWithStringInts,
|
||||||
makerAssetAmount: new BigNumber(orderWithStringInts.makerAssetAmount),
|
makerAssetAmount: new BigNumber(orderWithStringInts.makerAssetAmount),
|
||||||
takerAssetAmount: new BigNumber(orderWithStringInts.takerAssetAmount),
|
takerAssetAmount: new BigNumber(orderWithStringInts.takerAssetAmount),
|
||||||
@ -164,17 +162,25 @@ export class QuoteRequestor {
|
|||||||
expirationTimeSeconds: new BigNumber(orderWithStringInts.expirationTimeSeconds),
|
expirationTimeSeconds: new BigNumber(orderWithStringInts.expirationTimeSeconds),
|
||||||
salt: new BigNumber(orderWithStringInts.salt),
|
salt: new BigNumber(orderWithStringInts.salt),
|
||||||
};
|
};
|
||||||
});
|
|
||||||
|
|
||||||
const orders = validatedOrders.filter(order => {
|
if (
|
||||||
if (orderCalculationUtils.willOrderExpire(order, this._expiryBufferMs / constants.ONE_SECOND_MS)) {
|
orderCalculationUtils.willOrderExpire(
|
||||||
this._warningLogger(order, 'Expiry too soon in RFQ-T order, filtering out');
|
orderWithBigNumberInts,
|
||||||
return false;
|
this._expiryBufferMs / constants.ONE_SECOND_MS,
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
this._warningLogger(orderWithBigNumberInts, 'Expiry too soon in RFQ-T order, filtering out');
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
});
|
|
||||||
|
|
||||||
return orders.map(order => ({ signedOrder: order }));
|
// Store makerUri for looking up later
|
||||||
|
this._orderHashToMakerUri[orderHashUtils.getOrderHash(orderWithBigNumberInts)] = firmQuoteResponse.makerUri;
|
||||||
|
|
||||||
|
// Passed all validation, add it to result
|
||||||
|
result.push({ signedOrder: orderWithBigNumberInts });
|
||||||
|
return;
|
||||||
|
});
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async requestRfqtIndicativeQuotesAsync(
|
public async requestRfqtIndicativeQuotesAsync(
|
||||||
@ -196,7 +202,8 @@ export class QuoteRequestor {
|
|||||||
'indicative',
|
'indicative',
|
||||||
);
|
);
|
||||||
|
|
||||||
const validResponsesWithStringInts = responsesWithStringInts.filter(response => {
|
const validResponsesWithStringInts = responsesWithStringInts.filter(result => {
|
||||||
|
const response = result.response;
|
||||||
if (!this._isValidRfqtIndicativeQuoteResponse(response)) {
|
if (!this._isValidRfqtIndicativeQuoteResponse(response)) {
|
||||||
this._warningLogger(response, 'Invalid RFQ-T indicative quote received, filtering out');
|
this._warningLogger(response, 'Invalid RFQ-T indicative quote received, filtering out');
|
||||||
return false;
|
return false;
|
||||||
@ -210,7 +217,8 @@ export class QuoteRequestor {
|
|||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
const validResponses = validResponsesWithStringInts.map(response => {
|
const validResponses = validResponsesWithStringInts.map(result => {
|
||||||
|
const response = result.response;
|
||||||
return {
|
return {
|
||||||
...response,
|
...response,
|
||||||
makerAssetAmount: new BigNumber(response.makerAssetAmount),
|
makerAssetAmount: new BigNumber(response.makerAssetAmount),
|
||||||
@ -230,6 +238,13 @@ export class QuoteRequestor {
|
|||||||
return responses;
|
return responses;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given an order hash, returns the makerUri that the order originated from
|
||||||
|
*/
|
||||||
|
public getMakerUriForOrderHash(orderHash: string): string | undefined {
|
||||||
|
return this._orderHashToMakerUri[orderHash];
|
||||||
|
}
|
||||||
|
|
||||||
private _isValidRfqtIndicativeQuoteResponse(response: RFQTIndicativeQuote): boolean {
|
private _isValidRfqtIndicativeQuoteResponse(response: RFQTIndicativeQuote): boolean {
|
||||||
const hasValidMakerAssetAmount =
|
const hasValidMakerAssetAmount =
|
||||||
response.makerAssetAmount !== undefined &&
|
response.makerAssetAmount !== undefined &&
|
||||||
@ -285,10 +300,9 @@ export class QuoteRequestor {
|
|||||||
marketOperation: MarketOperation,
|
marketOperation: MarketOperation,
|
||||||
options: RfqtRequestOpts,
|
options: RfqtRequestOpts,
|
||||||
quoteType: 'firm' | 'indicative',
|
quoteType: 'firm' | 'indicative',
|
||||||
): Promise<ResponseT[]> {
|
): Promise<Array<{ response: ResponseT; makerUri: string }>> {
|
||||||
// create an array of promises for quote responses, using "undefined"
|
const result: Array<{ response: ResponseT; makerUri: string }> = [];
|
||||||
// as a placeholder for failed requests.
|
await Promise.all(
|
||||||
const responsesIfDefined: Array<undefined | AxiosResponse<ResponseT>> = await Promise.all(
|
|
||||||
Object.keys(this._rfqtAssetOfferings).map(async url => {
|
Object.keys(this._rfqtAssetOfferings).map(async url => {
|
||||||
if (this._makerSupportsPair(url, makerAssetData, takerAssetData)) {
|
if (this._makerSupportsPair(url, makerAssetData, takerAssetData)) {
|
||||||
const requestParamsWithBigNumbers = {
|
const requestParamsWithBigNumbers = {
|
||||||
@ -337,7 +351,7 @@ export class QuoteRequestor {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
return response;
|
result.push({ response: response.data, makerUri: url });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this._infoLogger({
|
this._infoLogger({
|
||||||
rfqtMakerInteraction: {
|
rfqtMakerInteraction: {
|
||||||
@ -354,17 +368,10 @@ export class QuoteRequestor {
|
|||||||
options.apiKey
|
options.apiKey
|
||||||
} for taker address ${options.takerAddress}`,
|
} for taker address ${options.takerAddress}`,
|
||||||
);
|
);
|
||||||
return undefined;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return undefined;
|
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
return result;
|
||||||
const responses = responsesIfDefined.filter(
|
|
||||||
(respIfDefd): respIfDefd is AxiosResponse<ResponseT> => respIfDefd !== undefined,
|
|
||||||
);
|
|
||||||
|
|
||||||
return responses.map(response => response.data);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,6 @@ import {
|
|||||||
MarketOperation,
|
MarketOperation,
|
||||||
MarketSellSwapQuote,
|
MarketSellSwapQuote,
|
||||||
SwapQuote,
|
SwapQuote,
|
||||||
SwapQuoteBase,
|
|
||||||
SwapQuoteInfo,
|
SwapQuoteInfo,
|
||||||
SwapQuoteOrdersBreakdown,
|
SwapQuoteOrdersBreakdown,
|
||||||
SwapQuoterError,
|
SwapQuoterError,
|
||||||
@ -20,6 +19,7 @@ import { convertNativeOrderToFullyFillableOptimizedOrders } from './market_opera
|
|||||||
import { FeeSchedule, FillData, GetMarketOrdersOpts, OptimizedMarketOrder } from './market_operation_utils/types';
|
import { FeeSchedule, FillData, GetMarketOrdersOpts, OptimizedMarketOrder } from './market_operation_utils/types';
|
||||||
import { isSupportedAssetDataInOrders } from './utils';
|
import { isSupportedAssetDataInOrders } from './utils';
|
||||||
|
|
||||||
|
import { QuoteReport } from './quote_report_generator';
|
||||||
import { QuoteFillResult, simulateBestCaseFill, simulateWorstCaseFill } from './quote_simulation';
|
import { QuoteFillResult, simulateBestCaseFill, simulateWorstCaseFill } from './quote_simulation';
|
||||||
|
|
||||||
// TODO(dave4506) How do we want to reintroduce InsufficientAssetLiquidityError?
|
// TODO(dave4506) How do we want to reintroduce InsufficientAssetLiquidityError?
|
||||||
@ -87,6 +87,7 @@ export class SwapQuoteCalculator {
|
|||||||
assetFillAmounts,
|
assetFillAmounts,
|
||||||
opts,
|
opts,
|
||||||
);
|
);
|
||||||
|
|
||||||
const batchSwapQuotes = await Promise.all(
|
const batchSwapQuotes = await Promise.all(
|
||||||
batchSignedOrders.map(async (orders, i) => {
|
batchSignedOrders.map(async (orders, i) => {
|
||||||
if (orders) {
|
if (orders) {
|
||||||
@ -120,7 +121,8 @@ export class SwapQuoteCalculator {
|
|||||||
}
|
}
|
||||||
// since prunedOrders do not have fillState, we will add a buffer of fillable orders to consider that some native are orders are partially filled
|
// since prunedOrders do not have fillState, we will add a buffer of fillable orders to consider that some native are orders are partially filled
|
||||||
|
|
||||||
let resultOrders: OptimizedMarketOrder[] = [];
|
let optimizedOrders: OptimizedMarketOrder[] | undefined;
|
||||||
|
let quoteReport: QuoteReport | undefined;
|
||||||
|
|
||||||
{
|
{
|
||||||
// Scale fees by gas price.
|
// Scale fees by gas price.
|
||||||
@ -137,20 +139,24 @@ export class SwapQuoteCalculator {
|
|||||||
|
|
||||||
if (firstOrderMakerAssetData.assetProxyId === AssetProxyId.ERC721) {
|
if (firstOrderMakerAssetData.assetProxyId === AssetProxyId.ERC721) {
|
||||||
// HACK: to conform ERC721 orders to the output of market operation utils, assumes complete fillable
|
// HACK: to conform ERC721 orders to the output of market operation utils, assumes complete fillable
|
||||||
resultOrders = prunedOrders.map(o => convertNativeOrderToFullyFillableOptimizedOrders(o));
|
optimizedOrders = prunedOrders.map(o => convertNativeOrderToFullyFillableOptimizedOrders(o));
|
||||||
} else {
|
} else {
|
||||||
if (operation === MarketOperation.Buy) {
|
if (operation === MarketOperation.Buy) {
|
||||||
resultOrders = await this._marketOperationUtils.getMarketBuyOrdersAsync(
|
const buyResult = await this._marketOperationUtils.getMarketBuyOrdersAsync(
|
||||||
prunedOrders,
|
prunedOrders,
|
||||||
assetFillAmount,
|
assetFillAmount,
|
||||||
_opts,
|
_opts,
|
||||||
);
|
);
|
||||||
|
optimizedOrders = buyResult.optimizedOrders;
|
||||||
|
quoteReport = buyResult.quoteReport;
|
||||||
} else {
|
} else {
|
||||||
resultOrders = await this._marketOperationUtils.getMarketSellOrdersAsync(
|
const sellResult = await this._marketOperationUtils.getMarketSellOrdersAsync(
|
||||||
prunedOrders,
|
prunedOrders,
|
||||||
assetFillAmount,
|
assetFillAmount,
|
||||||
_opts,
|
_opts,
|
||||||
);
|
);
|
||||||
|
optimizedOrders = sellResult.optimizedOrders;
|
||||||
|
quoteReport = sellResult.quoteReport;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -160,11 +166,12 @@ export class SwapQuoteCalculator {
|
|||||||
return createSwapQuote(
|
return createSwapQuote(
|
||||||
makerAssetData,
|
makerAssetData,
|
||||||
takerAssetData,
|
takerAssetData,
|
||||||
resultOrders,
|
optimizedOrders,
|
||||||
operation,
|
operation,
|
||||||
assetFillAmount,
|
assetFillAmount,
|
||||||
gasPrice,
|
gasPrice,
|
||||||
opts.gasSchedule,
|
opts.gasSchedule,
|
||||||
|
quoteReport,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -172,15 +179,16 @@ export class SwapQuoteCalculator {
|
|||||||
function createSwapQuote(
|
function createSwapQuote(
|
||||||
makerAssetData: string,
|
makerAssetData: string,
|
||||||
takerAssetData: string,
|
takerAssetData: string,
|
||||||
resultOrders: OptimizedMarketOrder[],
|
optimizedOrders: OptimizedMarketOrder[],
|
||||||
operation: MarketOperation,
|
operation: MarketOperation,
|
||||||
assetFillAmount: BigNumber,
|
assetFillAmount: BigNumber,
|
||||||
gasPrice: BigNumber,
|
gasPrice: BigNumber,
|
||||||
gasSchedule: FeeSchedule,
|
gasSchedule: FeeSchedule,
|
||||||
|
quoteReport?: QuoteReport,
|
||||||
): SwapQuote {
|
): SwapQuote {
|
||||||
const bestCaseFillResult = simulateBestCaseFill({
|
const bestCaseFillResult = simulateBestCaseFill({
|
||||||
gasPrice,
|
gasPrice,
|
||||||
orders: resultOrders,
|
orders: optimizedOrders,
|
||||||
side: operation,
|
side: operation,
|
||||||
fillAmount: assetFillAmount,
|
fillAmount: assetFillAmount,
|
||||||
opts: { gasSchedule },
|
opts: { gasSchedule },
|
||||||
@ -188,20 +196,21 @@ function createSwapQuote(
|
|||||||
|
|
||||||
const worstCaseFillResult = simulateWorstCaseFill({
|
const worstCaseFillResult = simulateWorstCaseFill({
|
||||||
gasPrice,
|
gasPrice,
|
||||||
orders: resultOrders,
|
orders: optimizedOrders,
|
||||||
side: operation,
|
side: operation,
|
||||||
fillAmount: assetFillAmount,
|
fillAmount: assetFillAmount,
|
||||||
opts: { gasSchedule },
|
opts: { gasSchedule },
|
||||||
});
|
});
|
||||||
|
|
||||||
const quoteBase: SwapQuoteBase = {
|
const quoteBase = {
|
||||||
takerAssetData,
|
takerAssetData,
|
||||||
makerAssetData,
|
makerAssetData,
|
||||||
gasPrice,
|
gasPrice,
|
||||||
bestCaseQuoteInfo: fillResultsToQuoteInfo(bestCaseFillResult),
|
bestCaseQuoteInfo: fillResultsToQuoteInfo(bestCaseFillResult),
|
||||||
worstCaseQuoteInfo: fillResultsToQuoteInfo(worstCaseFillResult),
|
worstCaseQuoteInfo: fillResultsToQuoteInfo(worstCaseFillResult),
|
||||||
sourceBreakdown: getSwapQuoteOrdersBreakdown(bestCaseFillResult.fillAmountBySource),
|
sourceBreakdown: getSwapQuoteOrdersBreakdown(bestCaseFillResult.fillAmountBySource),
|
||||||
orders: resultOrders,
|
orders: optimizedOrders,
|
||||||
|
quoteReport,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (operation === MarketOperation.Buy) {
|
if (operation === MarketOperation.Buy) {
|
||||||
@ -209,12 +218,14 @@ function createSwapQuote(
|
|||||||
...quoteBase,
|
...quoteBase,
|
||||||
type: MarketOperation.Buy,
|
type: MarketOperation.Buy,
|
||||||
makerAssetFillAmount: assetFillAmount,
|
makerAssetFillAmount: assetFillAmount,
|
||||||
|
quoteReport,
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
return {
|
return {
|
||||||
...quoteBase,
|
...quoteBase,
|
||||||
type: MarketOperation.Sell,
|
type: MarketOperation.Sell,
|
||||||
takerAssetFillAmount: assetFillAmount,
|
takerAssetFillAmount: assetFillAmount,
|
||||||
|
quoteReport,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ import { getContractAddressesForChainOrThrow } from '@0x/contract-addresses';
|
|||||||
import { constants as contractConstants, getRandomInteger, Numberish, randomAddress } from '@0x/contracts-test-utils';
|
import { constants as contractConstants, getRandomInteger, Numberish, randomAddress } from '@0x/contracts-test-utils';
|
||||||
import {
|
import {
|
||||||
assetDataUtils,
|
assetDataUtils,
|
||||||
|
decodeAffiliateFeeTransformerData,
|
||||||
decodeFillQuoteTransformerData,
|
decodeFillQuoteTransformerData,
|
||||||
decodePayTakerTransformerData,
|
decodePayTakerTransformerData,
|
||||||
decodeWethTransformerData,
|
decodeWethTransformerData,
|
||||||
@ -28,7 +29,7 @@ chaiSetup.configure();
|
|||||||
const expect = chai.expect;
|
const expect = chai.expect;
|
||||||
|
|
||||||
const { NULL_ADDRESS } = constants;
|
const { NULL_ADDRESS } = constants;
|
||||||
const { MAX_UINT256 } = contractConstants;
|
const { MAX_UINT256, ZERO_AMOUNT } = contractConstants;
|
||||||
|
|
||||||
// tslint:disable: custom-no-magic-numbers
|
// tslint:disable: custom-no-magic-numbers
|
||||||
|
|
||||||
@ -265,5 +266,37 @@ describe('ExchangeProxySwapQuoteConsumer', () => {
|
|||||||
expect(wethTransformerData.amount).to.bignumber.eq(MAX_UINT256);
|
expect(wethTransformerData.amount).to.bignumber.eq(MAX_UINT256);
|
||||||
expect(wethTransformerData.token).to.eq(contractAddresses.etherToken);
|
expect(wethTransformerData.token).to.eq(contractAddresses.etherToken);
|
||||||
});
|
});
|
||||||
|
it('Appends an affiliate fee transformer after the fill if a buy token affiliate fee is provided', async () => {
|
||||||
|
const quote = getRandomSellQuote();
|
||||||
|
const affiliateFee = {
|
||||||
|
recipient: randomAddress(),
|
||||||
|
buyTokenFeeAmount: getRandomAmount(),
|
||||||
|
sellTokenFeeAmount: ZERO_AMOUNT,
|
||||||
|
};
|
||||||
|
const callInfo = await consumer.getCalldataOrThrowAsync(quote, {
|
||||||
|
extensionContractOpts: { affiliateFee },
|
||||||
|
});
|
||||||
|
const callArgs = callDataEncoder.decode(callInfo.calldataHexString) as CallArgs;
|
||||||
|
expect(callArgs.transformations[1].deploymentNonce.toNumber()).to.eq(
|
||||||
|
consumer.transformerNonces.affiliateFeeTransformer,
|
||||||
|
);
|
||||||
|
const affiliateFeeTransformerData = decodeAffiliateFeeTransformerData(callArgs.transformations[1].data);
|
||||||
|
expect(affiliateFeeTransformerData.fees).to.deep.equal([
|
||||||
|
{ token: MAKER_TOKEN, amount: affiliateFee.buyTokenFeeAmount, recipient: affiliateFee.recipient },
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
it('Throws if a sell token affiliate fee is provided', async () => {
|
||||||
|
const quote = getRandomSellQuote();
|
||||||
|
const affiliateFee = {
|
||||||
|
recipient: randomAddress(),
|
||||||
|
buyTokenFeeAmount: ZERO_AMOUNT,
|
||||||
|
sellTokenFeeAmount: getRandomAmount(),
|
||||||
|
};
|
||||||
|
expect(
|
||||||
|
consumer.getCalldataOrThrowAsync(quote, {
|
||||||
|
extensionContractOpts: { affiliateFee },
|
||||||
|
}),
|
||||||
|
).to.eventually.be.rejectedWith('Affiliate fees denominated in sell token are not yet supported');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -507,12 +507,13 @@ describe('MarketOperationUtils tests', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('generates bridge orders with correct asset data', async () => {
|
it('generates bridge orders with correct asset data', async () => {
|
||||||
const improvedOrders = await marketOperationUtils.getMarketSellOrdersAsync(
|
const improvedOrdersResponse = await marketOperationUtils.getMarketSellOrdersAsync(
|
||||||
// Pass in empty orders to prevent native orders from being used.
|
// Pass in empty orders to prevent native orders from being used.
|
||||||
ORDERS.map(o => ({ ...o, makerAssetAmount: constants.ZERO_AMOUNT })),
|
ORDERS.map(o => ({ ...o, makerAssetAmount: constants.ZERO_AMOUNT })),
|
||||||
FILL_AMOUNT,
|
FILL_AMOUNT,
|
||||||
DEFAULT_OPTS,
|
DEFAULT_OPTS,
|
||||||
);
|
);
|
||||||
|
const improvedOrders = improvedOrdersResponse.optimizedOrders;
|
||||||
expect(improvedOrders).to.not.be.length(0);
|
expect(improvedOrders).to.not.be.length(0);
|
||||||
for (const order of improvedOrders) {
|
for (const order of improvedOrders) {
|
||||||
expect(getSourceFromAssetData(order.makerAssetData)).to.exist('');
|
expect(getSourceFromAssetData(order.makerAssetData)).to.exist('');
|
||||||
@ -531,24 +532,26 @@ describe('MarketOperationUtils tests', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('generates bridge orders with correct taker amount', async () => {
|
it('generates bridge orders with correct taker amount', async () => {
|
||||||
const improvedOrders = await marketOperationUtils.getMarketSellOrdersAsync(
|
const improvedOrdersResponse = await marketOperationUtils.getMarketSellOrdersAsync(
|
||||||
// Pass in empty orders to prevent native orders from being used.
|
// Pass in empty orders to prevent native orders from being used.
|
||||||
ORDERS.map(o => ({ ...o, makerAssetAmount: constants.ZERO_AMOUNT })),
|
ORDERS.map(o => ({ ...o, makerAssetAmount: constants.ZERO_AMOUNT })),
|
||||||
FILL_AMOUNT,
|
FILL_AMOUNT,
|
||||||
DEFAULT_OPTS,
|
DEFAULT_OPTS,
|
||||||
);
|
);
|
||||||
|
const improvedOrders = improvedOrdersResponse.optimizedOrders;
|
||||||
const totalTakerAssetAmount = BigNumber.sum(...improvedOrders.map(o => o.takerAssetAmount));
|
const totalTakerAssetAmount = BigNumber.sum(...improvedOrders.map(o => o.takerAssetAmount));
|
||||||
expect(totalTakerAssetAmount).to.bignumber.gte(FILL_AMOUNT);
|
expect(totalTakerAssetAmount).to.bignumber.gte(FILL_AMOUNT);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('generates bridge orders with max slippage of `bridgeSlippage`', async () => {
|
it('generates bridge orders with max slippage of `bridgeSlippage`', async () => {
|
||||||
const bridgeSlippage = _.random(0.1, true);
|
const bridgeSlippage = _.random(0.1, true);
|
||||||
const improvedOrders = await marketOperationUtils.getMarketSellOrdersAsync(
|
const improvedOrdersResponse = await marketOperationUtils.getMarketSellOrdersAsync(
|
||||||
// Pass in empty orders to prevent native orders from being used.
|
// Pass in empty orders to prevent native orders from being used.
|
||||||
ORDERS.map(o => ({ ...o, makerAssetAmount: constants.ZERO_AMOUNT })),
|
ORDERS.map(o => ({ ...o, makerAssetAmount: constants.ZERO_AMOUNT })),
|
||||||
FILL_AMOUNT,
|
FILL_AMOUNT,
|
||||||
{ ...DEFAULT_OPTS, bridgeSlippage },
|
{ ...DEFAULT_OPTS, bridgeSlippage },
|
||||||
);
|
);
|
||||||
|
const improvedOrders = improvedOrdersResponse.optimizedOrders;
|
||||||
expect(improvedOrders).to.not.be.length(0);
|
expect(improvedOrders).to.not.be.length(0);
|
||||||
for (const order of improvedOrders) {
|
for (const order of improvedOrders) {
|
||||||
const expectedMakerAmount = order.fills[0].output;
|
const expectedMakerAmount = order.fills[0].output;
|
||||||
@ -566,11 +569,12 @@ describe('MarketOperationUtils tests', () => {
|
|||||||
replaceSamplerOps({
|
replaceSamplerOps({
|
||||||
getSellQuotesAsync: createGetMultipleSellQuotesOperationFromRates(rates),
|
getSellQuotesAsync: createGetMultipleSellQuotesOperationFromRates(rates),
|
||||||
});
|
});
|
||||||
const improvedOrders = await marketOperationUtils.getMarketSellOrdersAsync(
|
const improvedOrdersResponse = await marketOperationUtils.getMarketSellOrdersAsync(
|
||||||
createOrdersFromSellRates(FILL_AMOUNT, rates[ERC20BridgeSource.Native]),
|
createOrdersFromSellRates(FILL_AMOUNT, rates[ERC20BridgeSource.Native]),
|
||||||
FILL_AMOUNT,
|
FILL_AMOUNT,
|
||||||
{ ...DEFAULT_OPTS, numSamples: 4 },
|
{ ...DEFAULT_OPTS, numSamples: 4 },
|
||||||
);
|
);
|
||||||
|
const improvedOrders = improvedOrdersResponse.optimizedOrders;
|
||||||
const orderSources = improvedOrders.map(o => o.fills[0].source);
|
const orderSources = improvedOrders.map(o => o.fills[0].source);
|
||||||
const expectedSources = [
|
const expectedSources = [
|
||||||
ERC20BridgeSource.Eth2Dai,
|
ERC20BridgeSource.Eth2Dai,
|
||||||
@ -604,11 +608,12 @@ describe('MarketOperationUtils tests', () => {
|
|||||||
getSellQuotesAsync: createGetMultipleSellQuotesOperationFromRates(rates),
|
getSellQuotesAsync: createGetMultipleSellQuotesOperationFromRates(rates),
|
||||||
getMedianSellRateAsync: createGetMedianSellRate(ETH_TO_MAKER_RATE),
|
getMedianSellRateAsync: createGetMedianSellRate(ETH_TO_MAKER_RATE),
|
||||||
});
|
});
|
||||||
const improvedOrders = await marketOperationUtils.getMarketSellOrdersAsync(
|
const improvedOrdersResponse = await marketOperationUtils.getMarketSellOrdersAsync(
|
||||||
createOrdersFromSellRates(FILL_AMOUNT, rates[ERC20BridgeSource.Native]),
|
createOrdersFromSellRates(FILL_AMOUNT, rates[ERC20BridgeSource.Native]),
|
||||||
FILL_AMOUNT,
|
FILL_AMOUNT,
|
||||||
{ ...DEFAULT_OPTS, numSamples: 4, feeSchedule },
|
{ ...DEFAULT_OPTS, numSamples: 4, feeSchedule },
|
||||||
);
|
);
|
||||||
|
const improvedOrders = improvedOrdersResponse.optimizedOrders;
|
||||||
const orderSources = improvedOrders.map(o => o.fills[0].source);
|
const orderSources = improvedOrders.map(o => o.fills[0].source);
|
||||||
const expectedSources = [
|
const expectedSources = [
|
||||||
ERC20BridgeSource.Native,
|
ERC20BridgeSource.Native,
|
||||||
@ -641,11 +646,12 @@ describe('MarketOperationUtils tests', () => {
|
|||||||
getSellQuotesAsync: createGetMultipleSellQuotesOperationFromRates(rates),
|
getSellQuotesAsync: createGetMultipleSellQuotesOperationFromRates(rates),
|
||||||
getMedianSellRateAsync: createGetMedianSellRate(ETH_TO_MAKER_RATE),
|
getMedianSellRateAsync: createGetMedianSellRate(ETH_TO_MAKER_RATE),
|
||||||
});
|
});
|
||||||
const improvedOrders = await marketOperationUtils.getMarketSellOrdersAsync(
|
const improvedOrdersResponse = await marketOperationUtils.getMarketSellOrdersAsync(
|
||||||
createOrdersFromSellRates(FILL_AMOUNT, rates[ERC20BridgeSource.Native]),
|
createOrdersFromSellRates(FILL_AMOUNT, rates[ERC20BridgeSource.Native]),
|
||||||
FILL_AMOUNT,
|
FILL_AMOUNT,
|
||||||
{ ...DEFAULT_OPTS, numSamples: 4, feeSchedule },
|
{ ...DEFAULT_OPTS, numSamples: 4, feeSchedule },
|
||||||
);
|
);
|
||||||
|
const improvedOrders = improvedOrdersResponse.optimizedOrders;
|
||||||
const orderSources = improvedOrders.map(o => o.fills[0].source);
|
const orderSources = improvedOrders.map(o => o.fills[0].source);
|
||||||
const expectedSources = [
|
const expectedSources = [
|
||||||
ERC20BridgeSource.Native,
|
ERC20BridgeSource.Native,
|
||||||
@ -666,11 +672,12 @@ describe('MarketOperationUtils tests', () => {
|
|||||||
getSellQuotesAsync: createGetMultipleSellQuotesOperationFromRates(rates),
|
getSellQuotesAsync: createGetMultipleSellQuotesOperationFromRates(rates),
|
||||||
getMedianSellRateAsync: createGetMedianSellRate(ETH_TO_MAKER_RATE),
|
getMedianSellRateAsync: createGetMedianSellRate(ETH_TO_MAKER_RATE),
|
||||||
});
|
});
|
||||||
const improvedOrders = await marketOperationUtils.getMarketSellOrdersAsync(
|
const improvedOrdersResponse = await marketOperationUtils.getMarketSellOrdersAsync(
|
||||||
createOrdersFromSellRates(FILL_AMOUNT, rates[ERC20BridgeSource.Native]),
|
createOrdersFromSellRates(FILL_AMOUNT, rates[ERC20BridgeSource.Native]),
|
||||||
FILL_AMOUNT,
|
FILL_AMOUNT,
|
||||||
{ ...DEFAULT_OPTS, numSamples: 4 },
|
{ ...DEFAULT_OPTS, numSamples: 4 },
|
||||||
);
|
);
|
||||||
|
const improvedOrders = improvedOrdersResponse.optimizedOrders;
|
||||||
const orderSources = improvedOrders.map(o => o.fills[0].source);
|
const orderSources = improvedOrders.map(o => o.fills[0].source);
|
||||||
const expectedSources = [
|
const expectedSources = [
|
||||||
ERC20BridgeSource.Eth2Dai,
|
ERC20BridgeSource.Eth2Dai,
|
||||||
@ -689,21 +696,16 @@ describe('MarketOperationUtils tests', () => {
|
|||||||
replaceSamplerOps({
|
replaceSamplerOps({
|
||||||
getSellQuotesAsync: createGetMultipleSellQuotesOperationFromRates(rates),
|
getSellQuotesAsync: createGetMultipleSellQuotesOperationFromRates(rates),
|
||||||
});
|
});
|
||||||
const improvedOrders = await marketOperationUtils.getMarketSellOrdersAsync(
|
const improvedOrdersResponse = await marketOperationUtils.getMarketSellOrdersAsync(
|
||||||
createOrdersFromSellRates(FILL_AMOUNT, rates[ERC20BridgeSource.Native]),
|
createOrdersFromSellRates(FILL_AMOUNT, rates[ERC20BridgeSource.Native]),
|
||||||
FILL_AMOUNT,
|
FILL_AMOUNT,
|
||||||
{ ...DEFAULT_OPTS, numSamples: 4, allowFallback: true },
|
{ ...DEFAULT_OPTS, numSamples: 4, allowFallback: true },
|
||||||
);
|
);
|
||||||
|
const improvedOrders = improvedOrdersResponse.optimizedOrders;
|
||||||
const orderSources = improvedOrders.map(o => o.fills[0].source);
|
const orderSources = improvedOrders.map(o => o.fills[0].source);
|
||||||
const firstSources = [
|
const firstSources = orderSources.slice(0, 4);
|
||||||
ERC20BridgeSource.Native,
|
const secondSources = orderSources.slice(4);
|
||||||
ERC20BridgeSource.Native,
|
expect(_.intersection(firstSources, secondSources)).to.be.length(0);
|
||||||
ERC20BridgeSource.Native,
|
|
||||||
ERC20BridgeSource.Uniswap,
|
|
||||||
];
|
|
||||||
const secondSources = [ERC20BridgeSource.Eth2Dai, ERC20BridgeSource.Kyber];
|
|
||||||
expect(orderSources.slice(0, firstSources.length).sort()).to.deep.eq(firstSources.sort());
|
|
||||||
expect(orderSources.slice(firstSources.length).sort()).to.deep.eq(secondSources.sort());
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not create a fallback if below maxFallbackSlippage', async () => {
|
it('does not create a fallback if below maxFallbackSlippage', async () => {
|
||||||
@ -715,11 +717,12 @@ describe('MarketOperationUtils tests', () => {
|
|||||||
replaceSamplerOps({
|
replaceSamplerOps({
|
||||||
getSellQuotesAsync: createGetMultipleSellQuotesOperationFromRates(rates),
|
getSellQuotesAsync: createGetMultipleSellQuotesOperationFromRates(rates),
|
||||||
});
|
});
|
||||||
const improvedOrders = await marketOperationUtils.getMarketSellOrdersAsync(
|
const improvedOrdersResponse = await marketOperationUtils.getMarketSellOrdersAsync(
|
||||||
createOrdersFromSellRates(FILL_AMOUNT, rates[ERC20BridgeSource.Native]),
|
createOrdersFromSellRates(FILL_AMOUNT, rates[ERC20BridgeSource.Native]),
|
||||||
FILL_AMOUNT,
|
FILL_AMOUNT,
|
||||||
{ ...DEFAULT_OPTS, numSamples: 4, allowFallback: true, maxFallbackSlippage: 0.25 },
|
{ ...DEFAULT_OPTS, numSamples: 4, allowFallback: true, maxFallbackSlippage: 0.25 },
|
||||||
);
|
);
|
||||||
|
const improvedOrders = improvedOrdersResponse.optimizedOrders;
|
||||||
const orderSources = improvedOrders.map(o => o.fills[0].source);
|
const orderSources = improvedOrders.map(o => o.fills[0].source);
|
||||||
const firstSources = [ERC20BridgeSource.Native, ERC20BridgeSource.Native, ERC20BridgeSource.Uniswap];
|
const firstSources = [ERC20BridgeSource.Native, ERC20BridgeSource.Native, ERC20BridgeSource.Uniswap];
|
||||||
const secondSources: ERC20BridgeSource[] = [];
|
const secondSources: ERC20BridgeSource[] = [];
|
||||||
@ -756,7 +759,7 @@ describe('MarketOperationUtils tests', () => {
|
|||||||
ORDER_DOMAIN,
|
ORDER_DOMAIN,
|
||||||
registryAddress,
|
registryAddress,
|
||||||
);
|
);
|
||||||
const result = await sampler.getMarketSellOrdersAsync(
|
const ordersAndReport = await sampler.getMarketSellOrdersAsync(
|
||||||
[
|
[
|
||||||
createOrder({
|
createOrder({
|
||||||
makerAssetData: assetDataUtils.encodeERC20AssetData(xAsset),
|
makerAssetData: assetDataUtils.encodeERC20AssetData(xAsset),
|
||||||
@ -766,6 +769,7 @@ describe('MarketOperationUtils tests', () => {
|
|||||||
Web3Wrapper.toBaseUnitAmount(10, 18),
|
Web3Wrapper.toBaseUnitAmount(10, 18),
|
||||||
{ excludedSources: SELL_SOURCES, numSamples: 4, bridgeSlippage: 0, shouldBatchBridgeOrders: false },
|
{ excludedSources: SELL_SOURCES, numSamples: 4, bridgeSlippage: 0, shouldBatchBridgeOrders: false },
|
||||||
);
|
);
|
||||||
|
const result = ordersAndReport.optimizedOrders;
|
||||||
expect(result.length).to.eql(1);
|
expect(result.length).to.eql(1);
|
||||||
expect(result[0].makerAddress).to.eql(liquidityProviderAddress);
|
expect(result[0].makerAddress).to.eql(liquidityProviderAddress);
|
||||||
|
|
||||||
@ -792,7 +796,7 @@ describe('MarketOperationUtils tests', () => {
|
|||||||
replaceSamplerOps({
|
replaceSamplerOps({
|
||||||
getSellQuotesAsync: createGetMultipleSellQuotesOperationFromRates(rates),
|
getSellQuotesAsync: createGetMultipleSellQuotesOperationFromRates(rates),
|
||||||
});
|
});
|
||||||
const improvedOrders = await marketOperationUtils.getMarketSellOrdersAsync(
|
const improvedOrdersResponse = await marketOperationUtils.getMarketSellOrdersAsync(
|
||||||
createOrdersFromSellRates(FILL_AMOUNT, rates[ERC20BridgeSource.Native]),
|
createOrdersFromSellRates(FILL_AMOUNT, rates[ERC20BridgeSource.Native]),
|
||||||
FILL_AMOUNT,
|
FILL_AMOUNT,
|
||||||
{
|
{
|
||||||
@ -805,6 +809,7 @@ describe('MarketOperationUtils tests', () => {
|
|||||||
shouldBatchBridgeOrders: true,
|
shouldBatchBridgeOrders: true,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
const improvedOrders = improvedOrdersResponse.optimizedOrders;
|
||||||
expect(improvedOrders).to.be.length(3);
|
expect(improvedOrders).to.be.length(3);
|
||||||
const orderFillSources = improvedOrders.map(o => o.fills.map(f => f.source));
|
const orderFillSources = improvedOrders.map(o => o.fills.map(f => f.source));
|
||||||
expect(orderFillSources).to.deep.eq([
|
expect(orderFillSources).to.deep.eq([
|
||||||
@ -913,12 +918,13 @@ describe('MarketOperationUtils tests', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('generates bridge orders with correct asset data', async () => {
|
it('generates bridge orders with correct asset data', async () => {
|
||||||
const improvedOrders = await marketOperationUtils.getMarketBuyOrdersAsync(
|
const improvedOrdersResponse = await marketOperationUtils.getMarketBuyOrdersAsync(
|
||||||
// Pass in empty orders to prevent native orders from being used.
|
// Pass in empty orders to prevent native orders from being used.
|
||||||
ORDERS.map(o => ({ ...o, makerAssetAmount: constants.ZERO_AMOUNT })),
|
ORDERS.map(o => ({ ...o, makerAssetAmount: constants.ZERO_AMOUNT })),
|
||||||
FILL_AMOUNT,
|
FILL_AMOUNT,
|
||||||
DEFAULT_OPTS,
|
DEFAULT_OPTS,
|
||||||
);
|
);
|
||||||
|
const improvedOrders = improvedOrdersResponse.optimizedOrders;
|
||||||
expect(improvedOrders).to.not.be.length(0);
|
expect(improvedOrders).to.not.be.length(0);
|
||||||
for (const order of improvedOrders) {
|
for (const order of improvedOrders) {
|
||||||
expect(getSourceFromAssetData(order.makerAssetData)).to.exist('');
|
expect(getSourceFromAssetData(order.makerAssetData)).to.exist('');
|
||||||
@ -937,24 +943,26 @@ describe('MarketOperationUtils tests', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('generates bridge orders with correct maker amount', async () => {
|
it('generates bridge orders with correct maker amount', async () => {
|
||||||
const improvedOrders = await marketOperationUtils.getMarketBuyOrdersAsync(
|
const improvedOrdersResponse = await marketOperationUtils.getMarketBuyOrdersAsync(
|
||||||
// Pass in empty orders to prevent native orders from being used.
|
// Pass in empty orders to prevent native orders from being used.
|
||||||
ORDERS.map(o => ({ ...o, makerAssetAmount: constants.ZERO_AMOUNT })),
|
ORDERS.map(o => ({ ...o, makerAssetAmount: constants.ZERO_AMOUNT })),
|
||||||
FILL_AMOUNT,
|
FILL_AMOUNT,
|
||||||
DEFAULT_OPTS,
|
DEFAULT_OPTS,
|
||||||
);
|
);
|
||||||
|
const improvedOrders = improvedOrdersResponse.optimizedOrders;
|
||||||
const totalMakerAssetAmount = BigNumber.sum(...improvedOrders.map(o => o.makerAssetAmount));
|
const totalMakerAssetAmount = BigNumber.sum(...improvedOrders.map(o => o.makerAssetAmount));
|
||||||
expect(totalMakerAssetAmount).to.bignumber.gte(FILL_AMOUNT);
|
expect(totalMakerAssetAmount).to.bignumber.gte(FILL_AMOUNT);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('generates bridge orders with max slippage of `bridgeSlippage`', async () => {
|
it('generates bridge orders with max slippage of `bridgeSlippage`', async () => {
|
||||||
const bridgeSlippage = _.random(0.1, true);
|
const bridgeSlippage = _.random(0.1, true);
|
||||||
const improvedOrders = await marketOperationUtils.getMarketBuyOrdersAsync(
|
const improvedOrdersResponse = await marketOperationUtils.getMarketBuyOrdersAsync(
|
||||||
// Pass in empty orders to prevent native orders from being used.
|
// Pass in empty orders to prevent native orders from being used.
|
||||||
ORDERS.map(o => ({ ...o, makerAssetAmount: constants.ZERO_AMOUNT })),
|
ORDERS.map(o => ({ ...o, makerAssetAmount: constants.ZERO_AMOUNT })),
|
||||||
FILL_AMOUNT,
|
FILL_AMOUNT,
|
||||||
{ ...DEFAULT_OPTS, bridgeSlippage },
|
{ ...DEFAULT_OPTS, bridgeSlippage },
|
||||||
);
|
);
|
||||||
|
const improvedOrders = improvedOrdersResponse.optimizedOrders;
|
||||||
expect(improvedOrders).to.not.be.length(0);
|
expect(improvedOrders).to.not.be.length(0);
|
||||||
for (const order of improvedOrders) {
|
for (const order of improvedOrders) {
|
||||||
const expectedTakerAmount = order.fills[0].output;
|
const expectedTakerAmount = order.fills[0].output;
|
||||||
@ -971,11 +979,12 @@ describe('MarketOperationUtils tests', () => {
|
|||||||
replaceSamplerOps({
|
replaceSamplerOps({
|
||||||
getBuyQuotesAsync: createGetMultipleBuyQuotesOperationFromRates(rates),
|
getBuyQuotesAsync: createGetMultipleBuyQuotesOperationFromRates(rates),
|
||||||
});
|
});
|
||||||
const improvedOrders = await marketOperationUtils.getMarketBuyOrdersAsync(
|
const improvedOrdersResponse = await marketOperationUtils.getMarketBuyOrdersAsync(
|
||||||
createOrdersFromBuyRates(FILL_AMOUNT, rates[ERC20BridgeSource.Native]),
|
createOrdersFromBuyRates(FILL_AMOUNT, rates[ERC20BridgeSource.Native]),
|
||||||
FILL_AMOUNT,
|
FILL_AMOUNT,
|
||||||
{ ...DEFAULT_OPTS, numSamples: 4 },
|
{ ...DEFAULT_OPTS, numSamples: 4 },
|
||||||
);
|
);
|
||||||
|
const improvedOrders = improvedOrdersResponse.optimizedOrders;
|
||||||
const orderSources = improvedOrders.map(o => o.fills[0].source);
|
const orderSources = improvedOrders.map(o => o.fills[0].source);
|
||||||
const expectedSources = [
|
const expectedSources = [
|
||||||
ERC20BridgeSource.Eth2Dai,
|
ERC20BridgeSource.Eth2Dai,
|
||||||
@ -1009,11 +1018,12 @@ describe('MarketOperationUtils tests', () => {
|
|||||||
getBuyQuotesAsync: createGetMultipleBuyQuotesOperationFromRates(rates),
|
getBuyQuotesAsync: createGetMultipleBuyQuotesOperationFromRates(rates),
|
||||||
getMedianSellRateAsync: createGetMedianSellRate(ETH_TO_TAKER_RATE),
|
getMedianSellRateAsync: createGetMedianSellRate(ETH_TO_TAKER_RATE),
|
||||||
});
|
});
|
||||||
const improvedOrders = await marketOperationUtils.getMarketBuyOrdersAsync(
|
const improvedOrdersResponse = await marketOperationUtils.getMarketBuyOrdersAsync(
|
||||||
createOrdersFromBuyRates(FILL_AMOUNT, rates[ERC20BridgeSource.Native]),
|
createOrdersFromBuyRates(FILL_AMOUNT, rates[ERC20BridgeSource.Native]),
|
||||||
FILL_AMOUNT,
|
FILL_AMOUNT,
|
||||||
{ ...DEFAULT_OPTS, numSamples: 4, feeSchedule },
|
{ ...DEFAULT_OPTS, numSamples: 4, feeSchedule },
|
||||||
);
|
);
|
||||||
|
const improvedOrders = improvedOrdersResponse.optimizedOrders;
|
||||||
const orderSources = improvedOrders.map(o => o.fills[0].source);
|
const orderSources = improvedOrders.map(o => o.fills[0].source);
|
||||||
const expectedSources = [
|
const expectedSources = [
|
||||||
ERC20BridgeSource.Uniswap,
|
ERC20BridgeSource.Uniswap,
|
||||||
@ -1045,11 +1055,12 @@ describe('MarketOperationUtils tests', () => {
|
|||||||
getBuyQuotesAsync: createGetMultipleBuyQuotesOperationFromRates(rates),
|
getBuyQuotesAsync: createGetMultipleBuyQuotesOperationFromRates(rates),
|
||||||
getMedianSellRateAsync: createGetMedianSellRate(ETH_TO_TAKER_RATE),
|
getMedianSellRateAsync: createGetMedianSellRate(ETH_TO_TAKER_RATE),
|
||||||
});
|
});
|
||||||
const improvedOrders = await marketOperationUtils.getMarketBuyOrdersAsync(
|
const improvedOrdersResponse = await marketOperationUtils.getMarketBuyOrdersAsync(
|
||||||
createOrdersFromBuyRates(FILL_AMOUNT, rates[ERC20BridgeSource.Native]),
|
createOrdersFromBuyRates(FILL_AMOUNT, rates[ERC20BridgeSource.Native]),
|
||||||
FILL_AMOUNT,
|
FILL_AMOUNT,
|
||||||
{ ...DEFAULT_OPTS, numSamples: 4, feeSchedule },
|
{ ...DEFAULT_OPTS, numSamples: 4, feeSchedule },
|
||||||
);
|
);
|
||||||
|
const improvedOrders = improvedOrdersResponse.optimizedOrders;
|
||||||
const orderSources = improvedOrders.map(o => o.fills[0].source);
|
const orderSources = improvedOrders.map(o => o.fills[0].source);
|
||||||
const expectedSources = [
|
const expectedSources = [
|
||||||
ERC20BridgeSource.Native,
|
ERC20BridgeSource.Native,
|
||||||
@ -1067,21 +1078,16 @@ describe('MarketOperationUtils tests', () => {
|
|||||||
replaceSamplerOps({
|
replaceSamplerOps({
|
||||||
getBuyQuotesAsync: createGetMultipleBuyQuotesOperationFromRates(rates),
|
getBuyQuotesAsync: createGetMultipleBuyQuotesOperationFromRates(rates),
|
||||||
});
|
});
|
||||||
const improvedOrders = await marketOperationUtils.getMarketBuyOrdersAsync(
|
const improvedOrdersResponse = await marketOperationUtils.getMarketBuyOrdersAsync(
|
||||||
createOrdersFromBuyRates(FILL_AMOUNT, rates[ERC20BridgeSource.Native]),
|
createOrdersFromBuyRates(FILL_AMOUNT, rates[ERC20BridgeSource.Native]),
|
||||||
FILL_AMOUNT,
|
FILL_AMOUNT,
|
||||||
{ ...DEFAULT_OPTS, numSamples: 4, allowFallback: true },
|
{ ...DEFAULT_OPTS, numSamples: 4, allowFallback: true },
|
||||||
);
|
);
|
||||||
|
const improvedOrders = improvedOrdersResponse.optimizedOrders;
|
||||||
const orderSources = improvedOrders.map(o => o.fills[0].source);
|
const orderSources = improvedOrders.map(o => o.fills[0].source);
|
||||||
const firstSources = [
|
const firstSources = orderSources.slice(0, 4);
|
||||||
ERC20BridgeSource.Native,
|
const secondSources = orderSources.slice(4);
|
||||||
ERC20BridgeSource.Native,
|
expect(_.intersection(firstSources, secondSources)).to.be.length(0);
|
||||||
ERC20BridgeSource.Native,
|
|
||||||
ERC20BridgeSource.Uniswap,
|
|
||||||
];
|
|
||||||
const secondSources = [ERC20BridgeSource.Eth2Dai];
|
|
||||||
expect(orderSources.slice(0, firstSources.length).sort()).to.deep.eq(firstSources.sort());
|
|
||||||
expect(orderSources.slice(firstSources.length).sort()).to.deep.eq(secondSources.sort());
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not create a fallback if below maxFallbackSlippage', async () => {
|
it('does not create a fallback if below maxFallbackSlippage', async () => {
|
||||||
@ -1092,11 +1098,12 @@ describe('MarketOperationUtils tests', () => {
|
|||||||
replaceSamplerOps({
|
replaceSamplerOps({
|
||||||
getBuyQuotesAsync: createGetMultipleBuyQuotesOperationFromRates(rates),
|
getBuyQuotesAsync: createGetMultipleBuyQuotesOperationFromRates(rates),
|
||||||
});
|
});
|
||||||
const improvedOrders = await marketOperationUtils.getMarketBuyOrdersAsync(
|
const improvedOrdersResponse = await marketOperationUtils.getMarketBuyOrdersAsync(
|
||||||
createOrdersFromBuyRates(FILL_AMOUNT, rates[ERC20BridgeSource.Native]),
|
createOrdersFromBuyRates(FILL_AMOUNT, rates[ERC20BridgeSource.Native]),
|
||||||
FILL_AMOUNT,
|
FILL_AMOUNT,
|
||||||
{ ...DEFAULT_OPTS, numSamples: 4, allowFallback: true, maxFallbackSlippage: 0.25 },
|
{ ...DEFAULT_OPTS, numSamples: 4, allowFallback: true, maxFallbackSlippage: 0.25 },
|
||||||
);
|
);
|
||||||
|
const improvedOrders = improvedOrdersResponse.optimizedOrders;
|
||||||
const orderSources = improvedOrders.map(o => o.fills[0].source);
|
const orderSources = improvedOrders.map(o => o.fills[0].source);
|
||||||
const firstSources = [ERC20BridgeSource.Native, ERC20BridgeSource.Native, ERC20BridgeSource.Uniswap];
|
const firstSources = [ERC20BridgeSource.Native, ERC20BridgeSource.Native, ERC20BridgeSource.Uniswap];
|
||||||
const secondSources: ERC20BridgeSource[] = [];
|
const secondSources: ERC20BridgeSource[] = [];
|
||||||
@ -1107,12 +1114,12 @@ describe('MarketOperationUtils tests', () => {
|
|||||||
it('batches contiguous bridge sources', async () => {
|
it('batches contiguous bridge sources', async () => {
|
||||||
const rates: RatesBySource = {};
|
const rates: RatesBySource = {};
|
||||||
rates[ERC20BridgeSource.Native] = [0.5, 0.01, 0.01, 0.01];
|
rates[ERC20BridgeSource.Native] = [0.5, 0.01, 0.01, 0.01];
|
||||||
rates[ERC20BridgeSource.Eth2Dai] = [0.49, 0.01, 0.01, 0.01];
|
rates[ERC20BridgeSource.Eth2Dai] = [0.49, 0.02, 0.01, 0.01];
|
||||||
rates[ERC20BridgeSource.Uniswap] = [0.48, 0.47, 0.01, 0.01];
|
rates[ERC20BridgeSource.Uniswap] = [0.48, 0.01, 0.01, 0.01];
|
||||||
replaceSamplerOps({
|
replaceSamplerOps({
|
||||||
getBuyQuotesAsync: createGetMultipleBuyQuotesOperationFromRates(rates),
|
getBuyQuotesAsync: createGetMultipleBuyQuotesOperationFromRates(rates),
|
||||||
});
|
});
|
||||||
const improvedOrders = await marketOperationUtils.getMarketBuyOrdersAsync(
|
const improvedOrdersResponse = await marketOperationUtils.getMarketBuyOrdersAsync(
|
||||||
createOrdersFromBuyRates(FILL_AMOUNT, rates[ERC20BridgeSource.Native]),
|
createOrdersFromBuyRates(FILL_AMOUNT, rates[ERC20BridgeSource.Native]),
|
||||||
FILL_AMOUNT,
|
FILL_AMOUNT,
|
||||||
{
|
{
|
||||||
@ -1121,6 +1128,7 @@ describe('MarketOperationUtils tests', () => {
|
|||||||
shouldBatchBridgeOrders: true,
|
shouldBatchBridgeOrders: true,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
const improvedOrders = improvedOrdersResponse.optimizedOrders;
|
||||||
expect(improvedOrders).to.be.length(2);
|
expect(improvedOrders).to.be.length(2);
|
||||||
const orderFillSources = improvedOrders.map(o => o.fills.map(f => f.source));
|
const orderFillSources = improvedOrders.map(o => o.fills.map(f => f.source));
|
||||||
expect(orderFillSources).to.deep.eq([
|
expect(orderFillSources).to.deep.eq([
|
||||||
|
355
packages/asset-swapper/test/quote_report_generator_test.ts
Normal file
355
packages/asset-swapper/test/quote_report_generator_test.ts
Normal file
@ -0,0 +1,355 @@
|
|||||||
|
// tslint:disable:custom-no-magic-numbers
|
||||||
|
import { orderHashUtils } from '@0x/order-utils';
|
||||||
|
import { SignedOrder } from '@0x/types';
|
||||||
|
import { BigNumber, hexUtils } from '@0x/utils';
|
||||||
|
import * as chai from 'chai';
|
||||||
|
import * as _ from 'lodash';
|
||||||
|
import 'mocha';
|
||||||
|
import * as TypeMoq from 'typemoq';
|
||||||
|
|
||||||
|
import { MarketOperation } from '../src/types';
|
||||||
|
import {
|
||||||
|
CollapsedFill,
|
||||||
|
DexSample,
|
||||||
|
ERC20BridgeSource,
|
||||||
|
NativeCollapsedFill,
|
||||||
|
} from '../src/utils/market_operation_utils/types';
|
||||||
|
import { QuoteRequestor } from '../src/utils/quote_requestor';
|
||||||
|
|
||||||
|
import {
|
||||||
|
BridgeReportSource,
|
||||||
|
NativeOrderbookReportSource,
|
||||||
|
NativeRFQTReportSource,
|
||||||
|
QuoteReportGenerator,
|
||||||
|
QuoteReportSource,
|
||||||
|
} from './../src/utils/quote_report_generator';
|
||||||
|
import { chaiSetup } from './utils/chai_setup';
|
||||||
|
import { testOrderFactory } from './utils/test_order_factory';
|
||||||
|
|
||||||
|
chaiSetup.configure();
|
||||||
|
const expect = chai.expect;
|
||||||
|
|
||||||
|
const collapsedFillFromNativeOrder = (order: SignedOrder): NativeCollapsedFill => {
|
||||||
|
return {
|
||||||
|
sourcePathId: hexUtils.random(),
|
||||||
|
source: ERC20BridgeSource.Native,
|
||||||
|
input: order.takerAssetAmount,
|
||||||
|
output: order.makerAssetAmount,
|
||||||
|
fillData: {
|
||||||
|
order: {
|
||||||
|
...order,
|
||||||
|
fillableMakerAssetAmount: new BigNumber(1),
|
||||||
|
fillableTakerAssetAmount: new BigNumber(1),
|
||||||
|
fillableTakerFeeAmount: new BigNumber(1),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
subFills: [],
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('QuoteReportGenerator', async () => {
|
||||||
|
describe('generateReport', async () => {
|
||||||
|
it('should generate report properly for sell', () => {
|
||||||
|
const marketOperation: MarketOperation = MarketOperation.Sell;
|
||||||
|
|
||||||
|
const kyberSample1: DexSample = {
|
||||||
|
source: ERC20BridgeSource.Kyber,
|
||||||
|
input: new BigNumber(10000),
|
||||||
|
output: new BigNumber(10001),
|
||||||
|
};
|
||||||
|
const kyberSample2: DexSample = {
|
||||||
|
source: ERC20BridgeSource.Kyber,
|
||||||
|
input: new BigNumber(10003),
|
||||||
|
output: new BigNumber(10004),
|
||||||
|
};
|
||||||
|
const uniswapSample1: DexSample = {
|
||||||
|
source: ERC20BridgeSource.UniswapV2,
|
||||||
|
input: new BigNumber(10003),
|
||||||
|
output: new BigNumber(10004),
|
||||||
|
};
|
||||||
|
const uniswapSample2: DexSample = {
|
||||||
|
source: ERC20BridgeSource.UniswapV2,
|
||||||
|
input: new BigNumber(10005),
|
||||||
|
output: new BigNumber(10006),
|
||||||
|
};
|
||||||
|
const dexQuotes: DexSample[] = [kyberSample1, kyberSample2, uniswapSample1, uniswapSample2];
|
||||||
|
|
||||||
|
const orderbookOrder1FillableAmount = new BigNumber(1000);
|
||||||
|
const orderbookOrder1 = testOrderFactory.generateTestSignedOrder({
|
||||||
|
signature: 'orderbookOrder1',
|
||||||
|
takerAssetAmount: orderbookOrder1FillableAmount,
|
||||||
|
});
|
||||||
|
const orderbookOrder2FillableAmount = new BigNumber(99);
|
||||||
|
const orderbookOrder2 = testOrderFactory.generateTestSignedOrder({
|
||||||
|
signature: 'orderbookOrder2',
|
||||||
|
takerAssetAmount: orderbookOrder2FillableAmount.plus(99),
|
||||||
|
});
|
||||||
|
const rfqtOrder1FillableAmount = new BigNumber(100);
|
||||||
|
const rfqtOrder1 = testOrderFactory.generateTestSignedOrder({
|
||||||
|
signature: 'rfqtOrder1',
|
||||||
|
takerAssetAmount: rfqtOrder1FillableAmount,
|
||||||
|
});
|
||||||
|
const rfqtOrder2FillableAmount = new BigNumber(1001);
|
||||||
|
const rfqtOrder2 = testOrderFactory.generateTestSignedOrder({
|
||||||
|
signature: 'rfqtOrder2',
|
||||||
|
takerAssetAmount: rfqtOrder2FillableAmount.plus(100),
|
||||||
|
});
|
||||||
|
const nativeOrders: SignedOrder[] = [orderbookOrder1, rfqtOrder1, rfqtOrder2, orderbookOrder2];
|
||||||
|
const orderFillableAmounts: BigNumber[] = [
|
||||||
|
orderbookOrder1FillableAmount,
|
||||||
|
rfqtOrder1FillableAmount,
|
||||||
|
rfqtOrder2FillableAmount,
|
||||||
|
orderbookOrder2FillableAmount,
|
||||||
|
];
|
||||||
|
|
||||||
|
// generate path
|
||||||
|
const uniswap2Fill: CollapsedFill = { ...uniswapSample2, subFills: [], sourcePathId: hexUtils.random() };
|
||||||
|
const kyber2Fill: CollapsedFill = { ...kyberSample2, subFills: [], sourcePathId: hexUtils.random() };
|
||||||
|
const orderbookOrder2Fill: CollapsedFill = collapsedFillFromNativeOrder(orderbookOrder2);
|
||||||
|
const rfqtOrder2Fill: CollapsedFill = collapsedFillFromNativeOrder(rfqtOrder2);
|
||||||
|
const pathGenerated: CollapsedFill[] = [rfqtOrder2Fill, orderbookOrder2Fill, uniswap2Fill, kyber2Fill];
|
||||||
|
|
||||||
|
// quote generator mock
|
||||||
|
const quoteRequestor = TypeMoq.Mock.ofType<QuoteRequestor>();
|
||||||
|
quoteRequestor
|
||||||
|
.setup(qr => qr.getMakerUriForOrderHash(orderHashUtils.getOrderHash(orderbookOrder2)))
|
||||||
|
.returns(() => {
|
||||||
|
return undefined;
|
||||||
|
})
|
||||||
|
.verifiable(TypeMoq.Times.atLeastOnce());
|
||||||
|
quoteRequestor
|
||||||
|
.setup(qr => qr.getMakerUriForOrderHash(orderHashUtils.getOrderHash(rfqtOrder1)))
|
||||||
|
.returns(() => {
|
||||||
|
return 'https://rfqt1.provider.club';
|
||||||
|
})
|
||||||
|
.verifiable(TypeMoq.Times.atLeastOnce());
|
||||||
|
quoteRequestor
|
||||||
|
.setup(qr => qr.getMakerUriForOrderHash(orderHashUtils.getOrderHash(rfqtOrder2)))
|
||||||
|
.returns(() => {
|
||||||
|
return 'https://rfqt2.provider.club';
|
||||||
|
})
|
||||||
|
.verifiable(TypeMoq.Times.atLeastOnce());
|
||||||
|
|
||||||
|
const orderReport = new QuoteReportGenerator(
|
||||||
|
marketOperation,
|
||||||
|
dexQuotes,
|
||||||
|
nativeOrders,
|
||||||
|
orderFillableAmounts,
|
||||||
|
pathGenerated,
|
||||||
|
quoteRequestor.object,
|
||||||
|
).generateReport();
|
||||||
|
|
||||||
|
const rfqtOrder1Source: NativeRFQTReportSource = {
|
||||||
|
liquiditySource: ERC20BridgeSource.Native,
|
||||||
|
makerAmount: rfqtOrder1.makerAssetAmount,
|
||||||
|
takerAmount: rfqtOrder1.takerAssetAmount,
|
||||||
|
orderHash: orderHashUtils.getOrderHash(rfqtOrder1),
|
||||||
|
nativeOrder: rfqtOrder1,
|
||||||
|
fillableTakerAmount: rfqtOrder1FillableAmount,
|
||||||
|
isRfqt: true,
|
||||||
|
makerUri: 'https://rfqt1.provider.club',
|
||||||
|
};
|
||||||
|
const rfqtOrder2Source: NativeRFQTReportSource = {
|
||||||
|
liquiditySource: ERC20BridgeSource.Native,
|
||||||
|
makerAmount: rfqtOrder2.makerAssetAmount,
|
||||||
|
takerAmount: rfqtOrder2.takerAssetAmount,
|
||||||
|
orderHash: orderHashUtils.getOrderHash(rfqtOrder2),
|
||||||
|
nativeOrder: rfqtOrder2,
|
||||||
|
fillableTakerAmount: rfqtOrder2FillableAmount,
|
||||||
|
isRfqt: true,
|
||||||
|
makerUri: 'https://rfqt2.provider.club',
|
||||||
|
};
|
||||||
|
const orderbookOrder1Source: NativeOrderbookReportSource = {
|
||||||
|
liquiditySource: ERC20BridgeSource.Native,
|
||||||
|
makerAmount: orderbookOrder1.makerAssetAmount,
|
||||||
|
takerAmount: orderbookOrder1.takerAssetAmount,
|
||||||
|
orderHash: orderHashUtils.getOrderHash(orderbookOrder1),
|
||||||
|
nativeOrder: orderbookOrder1,
|
||||||
|
fillableTakerAmount: orderbookOrder1FillableAmount,
|
||||||
|
isRfqt: false,
|
||||||
|
};
|
||||||
|
const orderbookOrder2Source: NativeOrderbookReportSource = {
|
||||||
|
liquiditySource: ERC20BridgeSource.Native,
|
||||||
|
makerAmount: orderbookOrder2.makerAssetAmount,
|
||||||
|
takerAmount: orderbookOrder2.takerAssetAmount,
|
||||||
|
orderHash: orderHashUtils.getOrderHash(orderbookOrder2),
|
||||||
|
nativeOrder: orderbookOrder2,
|
||||||
|
fillableTakerAmount: orderbookOrder2FillableAmount,
|
||||||
|
isRfqt: false,
|
||||||
|
};
|
||||||
|
const uniswap1Source: BridgeReportSource = {
|
||||||
|
liquiditySource: ERC20BridgeSource.UniswapV2,
|
||||||
|
makerAmount: uniswapSample1.output,
|
||||||
|
takerAmount: uniswapSample1.input,
|
||||||
|
};
|
||||||
|
const uniswap2Source: BridgeReportSource = {
|
||||||
|
liquiditySource: ERC20BridgeSource.UniswapV2,
|
||||||
|
makerAmount: uniswapSample2.output,
|
||||||
|
takerAmount: uniswapSample2.input,
|
||||||
|
};
|
||||||
|
const kyber1Source: BridgeReportSource = {
|
||||||
|
liquiditySource: ERC20BridgeSource.Kyber,
|
||||||
|
makerAmount: kyberSample1.output,
|
||||||
|
takerAmount: kyberSample1.input,
|
||||||
|
};
|
||||||
|
const kyber2Source: BridgeReportSource = {
|
||||||
|
liquiditySource: ERC20BridgeSource.Kyber,
|
||||||
|
makerAmount: kyberSample2.output,
|
||||||
|
takerAmount: kyberSample2.input,
|
||||||
|
};
|
||||||
|
|
||||||
|
const expectedSourcesConsidered: QuoteReportSource[] = [
|
||||||
|
kyber1Source,
|
||||||
|
kyber2Source,
|
||||||
|
uniswap1Source,
|
||||||
|
uniswap2Source,
|
||||||
|
orderbookOrder1Source,
|
||||||
|
rfqtOrder1Source,
|
||||||
|
rfqtOrder2Source,
|
||||||
|
orderbookOrder2Source,
|
||||||
|
];
|
||||||
|
|
||||||
|
expect(orderReport.sourcesConsidered.length).to.eql(expectedSourcesConsidered.length);
|
||||||
|
|
||||||
|
orderReport.sourcesConsidered.forEach((actualSourcesConsidered, idx) => {
|
||||||
|
const expectedSourceConsidered = expectedSourcesConsidered[idx];
|
||||||
|
expect(actualSourcesConsidered).to.eql(
|
||||||
|
expectedSourceConsidered,
|
||||||
|
`sourceConsidered incorrect at index ${idx}`,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
const expectedSourcesDelivered: QuoteReportSource[] = [
|
||||||
|
rfqtOrder2Source,
|
||||||
|
orderbookOrder2Source,
|
||||||
|
uniswap2Source,
|
||||||
|
kyber2Source,
|
||||||
|
];
|
||||||
|
expect(orderReport.sourcesDelivered.length).to.eql(expectedSourcesDelivered.length);
|
||||||
|
orderReport.sourcesDelivered.forEach((actualSourceDelivered, idx) => {
|
||||||
|
const expectedSourceDelivered = expectedSourcesDelivered[idx];
|
||||||
|
|
||||||
|
// remove fillable values
|
||||||
|
if (actualSourceDelivered.liquiditySource === ERC20BridgeSource.Native) {
|
||||||
|
actualSourceDelivered.nativeOrder = _.omit(actualSourceDelivered.nativeOrder, [
|
||||||
|
'fillableMakerAssetAmount',
|
||||||
|
'fillableTakerAssetAmount',
|
||||||
|
'fillableTakerFeeAmount',
|
||||||
|
]) as SignedOrder;
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(actualSourceDelivered).to.eql(
|
||||||
|
expectedSourceDelivered,
|
||||||
|
`sourceDelivered incorrect at index ${idx}`,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
quoteRequestor.verifyAll();
|
||||||
|
});
|
||||||
|
it('should handle properly for buy without quoteRequestor', () => {
|
||||||
|
const marketOperation: MarketOperation = MarketOperation.Buy;
|
||||||
|
const kyberSample1: DexSample = {
|
||||||
|
source: ERC20BridgeSource.Kyber,
|
||||||
|
input: new BigNumber(10000),
|
||||||
|
output: new BigNumber(10001),
|
||||||
|
};
|
||||||
|
const uniswapSample1: DexSample = {
|
||||||
|
source: ERC20BridgeSource.UniswapV2,
|
||||||
|
input: new BigNumber(10003),
|
||||||
|
output: new BigNumber(10004),
|
||||||
|
};
|
||||||
|
const dexQuotes: DexSample[] = [kyberSample1, uniswapSample1];
|
||||||
|
|
||||||
|
const orderbookOrder1FillableAmount = new BigNumber(1000);
|
||||||
|
const orderbookOrder1 = testOrderFactory.generateTestSignedOrder({
|
||||||
|
signature: 'orderbookOrder1',
|
||||||
|
takerAssetAmount: orderbookOrder1FillableAmount.plus(101),
|
||||||
|
});
|
||||||
|
const orderbookOrder2FillableAmount = new BigNumber(5000);
|
||||||
|
const orderbookOrder2 = testOrderFactory.generateTestSignedOrder({
|
||||||
|
signature: 'orderbookOrder2',
|
||||||
|
takerAssetAmount: orderbookOrder2FillableAmount.plus(101),
|
||||||
|
});
|
||||||
|
const nativeOrders: SignedOrder[] = [orderbookOrder1, orderbookOrder2];
|
||||||
|
const orderFillableAmounts: BigNumber[] = [orderbookOrder1FillableAmount, orderbookOrder2FillableAmount];
|
||||||
|
|
||||||
|
// generate path
|
||||||
|
const orderbookOrder1Fill: CollapsedFill = collapsedFillFromNativeOrder(orderbookOrder1);
|
||||||
|
const uniswap1Fill: CollapsedFill = { ...uniswapSample1, subFills: [], sourcePathId: hexUtils.random() };
|
||||||
|
const kyber1Fill: CollapsedFill = { ...kyberSample1, subFills: [], sourcePathId: hexUtils.random() };
|
||||||
|
const pathGenerated: CollapsedFill[] = [orderbookOrder1Fill, uniswap1Fill, kyber1Fill];
|
||||||
|
|
||||||
|
const orderReport = new QuoteReportGenerator(
|
||||||
|
marketOperation,
|
||||||
|
dexQuotes,
|
||||||
|
nativeOrders,
|
||||||
|
orderFillableAmounts,
|
||||||
|
pathGenerated,
|
||||||
|
).generateReport();
|
||||||
|
|
||||||
|
const orderbookOrder1Source: NativeOrderbookReportSource = {
|
||||||
|
liquiditySource: ERC20BridgeSource.Native,
|
||||||
|
makerAmount: orderbookOrder1.makerAssetAmount,
|
||||||
|
takerAmount: orderbookOrder1.takerAssetAmount,
|
||||||
|
orderHash: orderHashUtils.getOrderHash(orderbookOrder1),
|
||||||
|
nativeOrder: orderbookOrder1,
|
||||||
|
fillableTakerAmount: orderbookOrder1FillableAmount,
|
||||||
|
isRfqt: false,
|
||||||
|
};
|
||||||
|
const orderbookOrder2Source: NativeOrderbookReportSource = {
|
||||||
|
liquiditySource: ERC20BridgeSource.Native,
|
||||||
|
makerAmount: orderbookOrder2.makerAssetAmount,
|
||||||
|
takerAmount: orderbookOrder2.takerAssetAmount,
|
||||||
|
orderHash: orderHashUtils.getOrderHash(orderbookOrder2),
|
||||||
|
nativeOrder: orderbookOrder2,
|
||||||
|
fillableTakerAmount: orderbookOrder2FillableAmount,
|
||||||
|
isRfqt: false,
|
||||||
|
};
|
||||||
|
const uniswap1Source: BridgeReportSource = {
|
||||||
|
liquiditySource: ERC20BridgeSource.UniswapV2,
|
||||||
|
makerAmount: uniswapSample1.input,
|
||||||
|
takerAmount: uniswapSample1.output,
|
||||||
|
};
|
||||||
|
const kyber1Source: BridgeReportSource = {
|
||||||
|
liquiditySource: ERC20BridgeSource.Kyber,
|
||||||
|
makerAmount: kyberSample1.input,
|
||||||
|
takerAmount: kyberSample1.output,
|
||||||
|
};
|
||||||
|
|
||||||
|
const expectedSourcesConsidered: QuoteReportSource[] = [
|
||||||
|
kyber1Source,
|
||||||
|
uniswap1Source,
|
||||||
|
orderbookOrder1Source,
|
||||||
|
orderbookOrder2Source,
|
||||||
|
];
|
||||||
|
expect(orderReport.sourcesConsidered.length).to.eql(expectedSourcesConsidered.length);
|
||||||
|
orderReport.sourcesConsidered.forEach((actualSourcesConsidered, idx) => {
|
||||||
|
const expectedSourceConsidered = expectedSourcesConsidered[idx];
|
||||||
|
expect(actualSourcesConsidered).to.eql(
|
||||||
|
expectedSourceConsidered,
|
||||||
|
`sourceConsidered incorrect at index ${idx}`,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
const expectedSourcesDelivered: QuoteReportSource[] = [orderbookOrder1Source, uniswap1Source, kyber1Source];
|
||||||
|
expect(orderReport.sourcesDelivered.length).to.eql(expectedSourcesDelivered.length);
|
||||||
|
orderReport.sourcesDelivered.forEach((actualSourceDelivered, idx) => {
|
||||||
|
const expectedSourceDelivered = expectedSourcesDelivered[idx];
|
||||||
|
|
||||||
|
// remove fillable values
|
||||||
|
if (actualSourceDelivered.liquiditySource === ERC20BridgeSource.Native) {
|
||||||
|
actualSourceDelivered.nativeOrder = _.omit(actualSourceDelivered.nativeOrder, [
|
||||||
|
'fillableMakerAssetAmount',
|
||||||
|
'fillableTakerAssetAmount',
|
||||||
|
'fillableTakerFeeAmount',
|
||||||
|
]) as SignedOrder;
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(actualSourceDelivered).to.eql(
|
||||||
|
expectedSourceDelivered,
|
||||||
|
`sourceDelivered incorrect at index ${idx}`,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -1,6 +1,6 @@
|
|||||||
import { constants, expect, getRandomInteger, randomAddress } from '@0x/contracts-test-utils';
|
import { constants, expect, getRandomInteger, randomAddress } from '@0x/contracts-test-utils';
|
||||||
import { assetDataUtils } from '@0x/order-utils';
|
import { assetDataUtils } from '@0x/order-utils';
|
||||||
import { BigNumber } from '@0x/utils';
|
import { BigNumber, hexUtils } from '@0x/utils';
|
||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
|
|
||||||
import { MarketOperation } from '../src/types';
|
import { MarketOperation } from '../src/types';
|
||||||
@ -155,7 +155,7 @@ describe('quote_simulation tests', async () => {
|
|||||||
signature: '0x',
|
signature: '0x',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
const nativeSourcePathId = hexUtils.random();
|
||||||
function createOrderCollapsedFills(input: BigNumber, output: BigNumber, count: number): CollapsedFill[] {
|
function createOrderCollapsedFills(input: BigNumber, output: BigNumber, count: number): CollapsedFill[] {
|
||||||
const inputs = subdivideAmount(input, count);
|
const inputs = subdivideAmount(input, count);
|
||||||
const outputs = subdivideAmount(output, count);
|
const outputs = subdivideAmount(output, count);
|
||||||
@ -163,6 +163,7 @@ describe('quote_simulation tests', async () => {
|
|||||||
const subFillInputs = subdivideAmount(inputs[i], count);
|
const subFillInputs = subdivideAmount(inputs[i], count);
|
||||||
const subFillOutputs = subdivideAmount(outputs[i], count);
|
const subFillOutputs = subdivideAmount(outputs[i], count);
|
||||||
return {
|
return {
|
||||||
|
sourcePathId: nativeSourcePathId,
|
||||||
source: ERC20BridgeSource.Native,
|
source: ERC20BridgeSource.Native,
|
||||||
input: inputs[i],
|
input: inputs[i],
|
||||||
output: outputs[i],
|
output: outputs[i],
|
||||||
|
@ -121,6 +121,7 @@ describe('swapQuoteConsumerUtils', () => {
|
|||||||
|
|
||||||
swapQuoteConsumer = new SwapQuoteConsumer(provider, {
|
swapQuoteConsumer = new SwapQuoteConsumer(provider, {
|
||||||
chainId,
|
chainId,
|
||||||
|
contractAddresses,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
after(async () => {
|
after(async () => {
|
||||||
|
@ -5,6 +5,18 @@
|
|||||||
{
|
{
|
||||||
"note": "Update `CurveBridge` address on all networks",
|
"note": "Update `CurveBridge` address on all networks",
|
||||||
"pr": 2633
|
"pr": 2633
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Redeploy `ERC20BridgeSampler` on Kovan",
|
||||||
|
"pr": 2644
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Deploy `UniswapV2Bridge` on Kovan",
|
||||||
|
"pr": 2652
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Redeploy previously unverified contracts on testnets",
|
||||||
|
"pr": 2656
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -47,25 +47,25 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"3": {
|
"3": {
|
||||||
"erc20Proxy": "0xb1408f4c245a23c31b98d2c626777d4c0d766caa",
|
"erc20Proxy": "0xf1ec7d0ba42f15fb5c9e3adbe86431973e44764c",
|
||||||
"erc721Proxy": "0xe654aac058bfbf9f83fcaee7793311dd82f6ddb4",
|
"erc721Proxy": "0x070efeb7e5ffa3d1a59d03a219539551ae60ba43",
|
||||||
"zrxToken": "0xff67881f8d12f372d91baae9752eb3631ff0ed00",
|
"zrxToken": "0xff67881f8d12f372d91baae9752eb3631ff0ed00",
|
||||||
"etherToken": "0xc778417e063141139fce010982780140aa0cd5ab",
|
"etherToken": "0xc778417e063141139fce010982780140aa0cd5ab",
|
||||||
"exchangeV2": "0xbff9493f92a3df4b0429b6d00743b3cfb4c85831",
|
"exchangeV2": "0xbff9493f92a3df4b0429b6d00743b3cfb4c85831",
|
||||||
"exchange": "0xfb2dd2a1366de37f7241c83d47da58fd503e2c64",
|
"exchange": "0x5d8c9ba74607d2cbc4176882a42d4ace891c1c00",
|
||||||
"assetProxyOwner": "0x0000000000000000000000000000000000000000",
|
"assetProxyOwner": "0x0000000000000000000000000000000000000000",
|
||||||
"zeroExGovernor": "0x53993733d41a88ae86f77a18a024e5548ee26579",
|
"zeroExGovernor": "0x53993733d41a88ae86f77a18a024e5548ee26579",
|
||||||
"forwarder": "0x2127a60bedfba1c01857b09b8f24094049c48493",
|
"forwarder": "0xd5abddda4ba89c0120edc0ca8a95ed1ad0bf9fc3",
|
||||||
"coordinatorRegistry": "0x403cc23e88c17c4652fb904784d1af640a6722d9",
|
"coordinatorRegistry": "0xf8becacec90bfc361c0a2c720839e08405a72f6d",
|
||||||
"coordinator": "0x6ff734d96104965c9c1b0108f83abc46e6e501df",
|
"coordinator": "0xc2e2f8faf4bf649123b6f94103646cb4a0331006",
|
||||||
"multiAssetProxy": "0xab8fbd189c569ccdee3a4d929bb7f557be4028f6",
|
"multiAssetProxy": "0x7b70a148e20b348c320208df84fdd642aab49fd0",
|
||||||
"staticCallProxy": "0xe1b97e47aa3796276033a5341e884d2ba46b6ac1",
|
"staticCallProxy": "0xaa460127562482faa5df42f2c39a025cd4a1cc0a",
|
||||||
"erc1155Proxy": "0x19bb6caa3bc34d39e5a23cedfa3e6c7e7f3c931d",
|
"erc1155Proxy": "0x7f10d80f2659aaae790ab03da12be11c4e6008c3",
|
||||||
"devUtils": "0x88e746ad9ab158210266e7765adbe1756c73cf84",
|
"devUtils": "0xc812af3f3fbc62f76ea4262576ec0f49db8b7f1c",
|
||||||
"zrxVault": "0xffd161026865ad8b4ab28a76840474935eec4dfa",
|
"zrxVault": "0x38bbb9fb54a6b6d0376948bf3b2a7ed1e8aea6e8",
|
||||||
"staking": "0x986b588e472b712385579d172494fe2685669504",
|
"staking": "0x4af649ffde640ceb34b1afaba3e0bb8e9698cb01",
|
||||||
"stakingProxy": "0xfaabcee42ab6b9c649794ac6c133711071897ee9",
|
"stakingProxy": "0x6acab4c9c4e3a0c78435fdb5ad1719c95460a668",
|
||||||
"erc20BridgeProxy": "0x599b340b5045436a99b1f0c718d30f5a0c8519dd",
|
"erc20BridgeProxy": "0xb344afed348de15eb4a9e180205a2b0739628339",
|
||||||
"uniswapBridge": "0x0000000000000000000000000000000000000000",
|
"uniswapBridge": "0x0000000000000000000000000000000000000000",
|
||||||
"uniswapV2Bridge": "0x0000000000000000000000000000000000000000",
|
"uniswapV2Bridge": "0x0000000000000000000000000000000000000000",
|
||||||
"eth2DaiBridge": "0x0000000000000000000000000000000000000000",
|
"eth2DaiBridge": "0x0000000000000000000000000000000000000000",
|
||||||
@ -74,11 +74,11 @@
|
|||||||
"chaiBridge": "0x0000000000000000000000000000000000000000",
|
"chaiBridge": "0x0000000000000000000000000000000000000000",
|
||||||
"dydxBridge": "0x0000000000000000000000000000000000000000",
|
"dydxBridge": "0x0000000000000000000000000000000000000000",
|
||||||
"godsUnchainedValidator": "0xd4690a51044db77d91d7aa8f7a3a5ad5da331af0",
|
"godsUnchainedValidator": "0xd4690a51044db77d91d7aa8f7a3a5ad5da331af0",
|
||||||
"broker": "0x4aa817c6f383c8e8ae77301d18ce48efb16fd2be",
|
"broker": "0x4022e3982f326455f0905de3dbc4449999baf2dc",
|
||||||
"chainlinkStopLimit": "0x67a094cf028221ffdd93fc658f963151d05e2a74",
|
"chainlinkStopLimit": "0x67a094cf028221ffdd93fc658f963151d05e2a74",
|
||||||
"curveBridge": "0x1796cd592d19e3bcd744fbb025bb61a6d8cb2c09",
|
"curveBridge": "0x1796cd592d19e3bcd744fbb025bb61a6d8cb2c09",
|
||||||
"maximumGasPrice": "0x407b4128e9ecad8769b2332312a9f655cb9f5f3a",
|
"maximumGasPrice": "0x407b4128e9ecad8769b2332312a9f655cb9f5f3a",
|
||||||
"dexForwarderBridge": "0x3be8e59038d8c4e8d8776ca40ef2f024bad95ad1",
|
"dexForwarderBridge": "0x3261ea1411a1a840aed708896f779e1b837c917e",
|
||||||
"multiBridge": "0x0000000000000000000000000000000000000000",
|
"multiBridge": "0x0000000000000000000000000000000000000000",
|
||||||
"balancerBridge": "0x47697b44bd89051e93b4d5857ba8e024800a74ac",
|
"balancerBridge": "0x47697b44bd89051e93b4d5857ba8e024800a74ac",
|
||||||
"exchangeProxyGovernor": "0x618f9c67ce7bf1a50afa1e7e0238422601b0ff6e",
|
"exchangeProxyGovernor": "0x618f9c67ce7bf1a50afa1e7e0238422601b0ff6e",
|
||||||
@ -89,30 +89,30 @@
|
|||||||
"transformers": {
|
"transformers": {
|
||||||
"wethTransformer": "0x7bab5f7299e1ca123bb44eb71e6c89be7e558cc8",
|
"wethTransformer": "0x7bab5f7299e1ca123bb44eb71e6c89be7e558cc8",
|
||||||
"payTakerTransformer": "0xe8c07a119452b55eee2f999478aab97f3656d841",
|
"payTakerTransformer": "0xe8c07a119452b55eee2f999478aab97f3656d841",
|
||||||
"fillQuoteTransformer": "0x43bea7eaca21a14a411274fb365707080ff25f80",
|
"fillQuoteTransformer": "0xfadbeff43a07dedeb69eda5590be1b78be3d1044",
|
||||||
"affiliateFeeTransformer": "0x9d7174f55b50dad2e417bd567ad2da1ae4eef76d"
|
"affiliateFeeTransformer": "0x9d7174f55b50dad2e417bd567ad2da1ae4eef76d"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"4": {
|
"4": {
|
||||||
"exchangeV2": "0xbff9493f92a3df4b0429b6d00743b3cfb4c85831",
|
"exchangeV2": "0xbff9493f92a3df4b0429b6d00743b3cfb4c85831",
|
||||||
"exchange": "0x198805e9682fceec29413059b68550f92868c129",
|
"exchange": "0xf8becacec90bfc361c0a2c720839e08405a72f6d",
|
||||||
"erc20Proxy": "0x2f5ae4f6106e89b4147651688a92256885c5f410",
|
"erc20Proxy": "0x070efeb7e5ffa3d1a59d03a219539551ae60ba43",
|
||||||
"erc721Proxy": "0x7656d773e11ff7383a14dcf09a9c50990481cd10",
|
"erc721Proxy": "0x7f10d80f2659aaae790ab03da12be11c4e6008c3",
|
||||||
"zrxToken": "0x8080c7e4b81ecf23aa6f877cfbfd9b0c228c6ffa",
|
"zrxToken": "0x8080c7e4b81ecf23aa6f877cfbfd9b0c228c6ffa",
|
||||||
"etherToken": "0xc778417e063141139fce010982780140aa0cd5ab",
|
"etherToken": "0xc778417e063141139fce010982780140aa0cd5ab",
|
||||||
"assetProxyOwner": "0x0000000000000000000000000000000000000000",
|
"assetProxyOwner": "0x0000000000000000000000000000000000000000",
|
||||||
"zeroExGovernor": "0x3f46b98061a3e1e1f41dff296ec19402c298f8a9",
|
"zeroExGovernor": "0x3f46b98061a3e1e1f41dff296ec19402c298f8a9",
|
||||||
"forwarder": "0x18571835c95a6d79b2f5c45b676ccd16f5fa34a1",
|
"forwarder": "0xe30f6166fe1cd5f0048abeed3d20360feb4a1fd8",
|
||||||
"coordinatorRegistry": "0x1084b6a398e47907bae43fec3ff4b677db6e4fee",
|
"coordinatorRegistry": "0xc2e2f8faf4bf649123b6f94103646cb4a0331006",
|
||||||
"coordinator": "0x70c5385ee5ee4629ef72abd169e888c8b4a12238",
|
"coordinator": "0xf1ec7d0ba42f15fb5c9e3adbe86431973e44764c",
|
||||||
"multiAssetProxy": "0xb34cde0ad3a83d04abebc0b66e75196f22216621",
|
"multiAssetProxy": "0xb344afed348de15eb4a9e180205a2b0739628339",
|
||||||
"staticCallProxy": "0xe1b97e47aa3796276033a5341e884d2ba46b6ac1",
|
"staticCallProxy": "0x7b70a148e20b348c320208df84fdd642aab49fd0",
|
||||||
"erc1155Proxy": "0x19bb6caa3bc34d39e5a23cedfa3e6c7e7f3c931d",
|
"erc1155Proxy": "0xaa460127562482faa5df42f2c39a025cd4a1cc0a",
|
||||||
"devUtils": "0x9402639a828bdf4e9e4103ac3b69e1a6e522eb59",
|
"devUtils": "0x46b5bc959e8a754c0256fff73bf34a52ad5cdfa9",
|
||||||
"zrxVault": "0xa5bf6ac73bc40790fc6ffc9dbbbce76c9176e224",
|
"zrxVault": "0x4af649ffde640ceb34b1afaba3e0bb8e9698cb01",
|
||||||
"staking": "0x7cbe3c09cba24f26db24d9100ee915fa9fa21f5b",
|
"staking": "0x6acab4c9c4e3a0c78435fdb5ad1719c95460a668",
|
||||||
"stakingProxy": "0xc6ad5277ea225ac05e271eb14a7ebb480cd9dd9f",
|
"stakingProxy": "0x781ee6683595f823208be6540a279f940e6af196",
|
||||||
"erc20BridgeProxy": "0x31b8653642110f17bdb1f719901d7e7d49b08141",
|
"erc20BridgeProxy": "0xa2aa4befed748fba27a3be7dfd2c4b2c6db1f49b",
|
||||||
"uniswapBridge": "0x0000000000000000000000000000000000000000",
|
"uniswapBridge": "0x0000000000000000000000000000000000000000",
|
||||||
"uniswapV2Bridge": "0x0000000000000000000000000000000000000000",
|
"uniswapV2Bridge": "0x0000000000000000000000000000000000000000",
|
||||||
"eth2DaiBridge": "0x0000000000000000000000000000000000000000",
|
"eth2DaiBridge": "0x0000000000000000000000000000000000000000",
|
||||||
@ -121,7 +121,7 @@
|
|||||||
"chaiBridge": "0x0000000000000000000000000000000000000000",
|
"chaiBridge": "0x0000000000000000000000000000000000000000",
|
||||||
"dydxBridge": "0x0000000000000000000000000000000000000000",
|
"dydxBridge": "0x0000000000000000000000000000000000000000",
|
||||||
"godsUnchainedValidator": "0x0000000000000000000000000000000000000000",
|
"godsUnchainedValidator": "0x0000000000000000000000000000000000000000",
|
||||||
"broker": "0x0000000000000000000000000000000000000000",
|
"broker": "0x0dd2d6cabbd8ae7d2fe6840fa597a44b1a7e4747",
|
||||||
"chainlinkStopLimit": "0x407b4128e9ecad8769b2332312a9f655cb9f5f3a",
|
"chainlinkStopLimit": "0x407b4128e9ecad8769b2332312a9f655cb9f5f3a",
|
||||||
"curveBridge": "0x1796cd592d19e3bcd744fbb025bb61a6d8cb2c09",
|
"curveBridge": "0x1796cd592d19e3bcd744fbb025bb61a6d8cb2c09",
|
||||||
"maximumGasPrice": "0x47697b44bd89051e93b4d5857ba8e024800a74ac",
|
"maximumGasPrice": "0x47697b44bd89051e93b4d5857ba8e024800a74ac",
|
||||||
@ -136,43 +136,43 @@
|
|||||||
"transformers": {
|
"transformers": {
|
||||||
"wethTransformer": "0x7bab5f7299e1ca123bb44eb71e6c89be7e558cc8",
|
"wethTransformer": "0x7bab5f7299e1ca123bb44eb71e6c89be7e558cc8",
|
||||||
"payTakerTransformer": "0xe8c07a119452b55eee2f999478aab97f3656d841",
|
"payTakerTransformer": "0xe8c07a119452b55eee2f999478aab97f3656d841",
|
||||||
"fillQuoteTransformer": "0x2013735f6df965494a0fbc292f84dd44debaba3e",
|
"fillQuoteTransformer": "0x454cC891dc428Be81d1d6Fd3dd7026a752FBFBc9",
|
||||||
"affiliateFeeTransformer": "0x9d7174f55b50dad2e417bd567ad2da1ae4eef76d"
|
"affiliateFeeTransformer": "0x9d7174f55b50dad2e417bd567ad2da1ae4eef76d"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"42": {
|
"42": {
|
||||||
"erc20Proxy": "0xf1ec01d6236d3cd881a0bf0130ea25fe4234003e",
|
"erc20Proxy": "0xaa460127562482faa5df42f2c39a025cd4a1cc0a",
|
||||||
"erc721Proxy": "0x2a9127c745688a165106c11cd4d647d2220af821",
|
"erc721Proxy": "0x7b70a148e20b348c320208df84fdd642aab49fd0",
|
||||||
"zrxToken": "0x2002d3812f58e35f0ea1ffbf80a75a38c32175fa",
|
"zrxToken": "0x2002d3812f58e35f0ea1ffbf80a75a38c32175fa",
|
||||||
"etherToken": "0xd0a1e359811322d97991e03f863a0c30c2cf029c",
|
"etherToken": "0xd0a1e359811322d97991e03f863a0c30c2cf029c",
|
||||||
"exchangeV2": "0x30589010550762d2f0d06f650d8e8b6ade6dbf4b",
|
"exchangeV2": "0x30589010550762d2f0d06f650d8e8b6ade6dbf4b",
|
||||||
"exchange": "0x4eacd0af335451709e1e7b570b8ea68edec8bc97",
|
"exchange": "0xf1ec7d0ba42f15fb5c9e3adbe86431973e44764c",
|
||||||
"assetProxyOwner": "0x0000000000000000000000000000000000000000",
|
"assetProxyOwner": "0x0000000000000000000000000000000000000000",
|
||||||
"zeroExGovernor": "0x6ff734d96104965c9c1b0108f83abc46e6e501df",
|
"zeroExGovernor": "0x6ff734d96104965c9c1b0108f83abc46e6e501df",
|
||||||
"forwarder": "0x01c0ecf5d1a22de07a2de84c322bfa2b5435990e",
|
"forwarder": "0x0f64646a5154ae5e58b6dd87ede7b04f508d76f8",
|
||||||
"coordinatorRegistry": "0x09fb99968c016a3ff537bf58fb3d9fe55a7975d5",
|
"coordinatorRegistry": "0x070efeb7e5ffa3d1a59d03a219539551ae60ba43",
|
||||||
"coordinator": "0xd29e59e51e8ab5f94121efaeebd935ca4214e257",
|
"coordinator": "0x7f10d80f2659aaae790ab03da12be11c4e6008c3",
|
||||||
"multiAssetProxy": "0xf6313a772c222f51c28f2304c0703b8cf5428fd8",
|
"multiAssetProxy": "0x58a01e826e60731247e7de8b446ed4c8535a099c",
|
||||||
"staticCallProxy": "0x48e94bdb9033640d45ea7c721e25f380f8bffa43",
|
"staticCallProxy": "0xa2aa4befed748fba27a3be7dfd2c4b2c6db1f49b",
|
||||||
"erc1155Proxy": "0x64517fa2b480ba3678a2a3c0cf08ef7fd4fad36f",
|
"erc1155Proxy": "0xb344afed348de15eb4a9e180205a2b0739628339",
|
||||||
"devUtils": "0x9402639a828bdf4e9e4103ac3b69e1a6e522eb59",
|
"devUtils": "0xc67ae71928568a180b3aad1339dedcf3076876fe",
|
||||||
"zrxVault": "0xf36eabdfe986b35b62c8fd5a98a7f2aebb79b291",
|
"zrxVault": "0x781ee6683595f823208be6540a279f940e6af196",
|
||||||
"staking": "0x32b06d5611a03737a5f1834a24ccd641033fd89c",
|
"staking": "0x73ea24041e03a012c51a45c307e0ba376af0238c",
|
||||||
"stakingProxy": "0xbab9145f1d57cd4bb0c9aa2d1ece0a5b6e734d34",
|
"stakingProxy": "0xe94cb304b3f515be7c95fedcfa249a84995fd748",
|
||||||
"erc20BridgeProxy": "0xfb2dd2a1366de37f7241c83d47da58fd503e2c64",
|
"erc20BridgeProxy": "0x3577552c1fb7a44ad76beeb7ab53251668a21f8d",
|
||||||
"uniswapBridge": "0x0e85f89f29998df65402391478e5924700c0079d",
|
"uniswapBridge": "0x0e85f89f29998df65402391478e5924700c0079d",
|
||||||
"uniswapV2Bridge": "0x0000000000000000000000000000000000000000",
|
"uniswapV2Bridge": "0x7b3530a635d099de0534dc27e46cd7c57578c3c8",
|
||||||
"eth2DaiBridge": "0x2d47147429b474d2e4f83e658015858a1312ed5b",
|
"eth2DaiBridge": "0x2d47147429b474d2e4f83e658015858a1312ed5b",
|
||||||
"erc20BridgeSampler": "0xccc9769c1a58766e79423a34b2cc5052d65c1983",
|
"erc20BridgeSampler": "0xcf9e66851f274aa4721e54526117876d90d51aa1",
|
||||||
"kyberBridge": "0xaecfa25920f892b6eb496e1f6e84037f59da7f44",
|
"kyberBridge": "0xaecfa25920f892b6eb496e1f6e84037f59da7f44",
|
||||||
"chaiBridge": "0x0000000000000000000000000000000000000000",
|
"chaiBridge": "0x0000000000000000000000000000000000000000",
|
||||||
"dydxBridge": "0x3be8e59038d8c4e8d8776ca40ef2f024bad95ad1",
|
"dydxBridge": "0xc213707de0454008758071c2edc1365621b8a5c5",
|
||||||
"godsUnchainedValidator": "0x0000000000000000000000000000000000000000",
|
"godsUnchainedValidator": "0x0000000000000000000000000000000000000000",
|
||||||
"broker": "0x0000000000000000000000000000000000000000",
|
"broker": "0xcdeb6d90ee7c96b4c713f7bb4f8604981f7ebe9d",
|
||||||
"chainlinkStopLimit": "0x0000000000000000000000000000000000000000",
|
"chainlinkStopLimit": "0x0000000000000000000000000000000000000000",
|
||||||
"curveBridge": "0x81c0ab53a7352d2e97f682a37cba44e54647eefb",
|
"curveBridge": "0x81c0ab53a7352d2e97f682a37cba44e54647eefb",
|
||||||
"maximumGasPrice": "0x67a094cf028221ffdd93fc658f963151d05e2a74",
|
"maximumGasPrice": "0x67a094cf028221ffdd93fc658f963151d05e2a74",
|
||||||
"dexForwarderBridge": "0xf220eb0b29e18bbc8ebc964e915b7547c7b4de4f",
|
"dexForwarderBridge": "0x985d1a95c6a86a3bf85c4d425af984abceaf01de",
|
||||||
"multiBridge": "0x0000000000000000000000000000000000000000",
|
"multiBridge": "0x0000000000000000000000000000000000000000",
|
||||||
"balancerBridge": "0x407b4128e9ecad8769b2332312a9f655cb9f5f3a",
|
"balancerBridge": "0x407b4128e9ecad8769b2332312a9f655cb9f5f3a",
|
||||||
"exchangeProxyGovernor": "0x618f9c67ce7bf1a50afa1e7e0238422601b0ff6e",
|
"exchangeProxyGovernor": "0x618f9c67ce7bf1a50afa1e7e0238422601b0ff6e",
|
||||||
@ -183,7 +183,7 @@
|
|||||||
"transformers": {
|
"transformers": {
|
||||||
"wethTransformer": "0x7bab5f7299e1ca123bb44eb71e6c89be7e558cc8",
|
"wethTransformer": "0x7bab5f7299e1ca123bb44eb71e6c89be7e558cc8",
|
||||||
"payTakerTransformer": "0xe8c07a119452b55eee2f999478aab97f3656d841",
|
"payTakerTransformer": "0xe8c07a119452b55eee2f999478aab97f3656d841",
|
||||||
"fillQuoteTransformer": "0xbc33dd7a09da8ca943517a0fb786bcf0192f8be2",
|
"fillQuoteTransformer": "0xA8c8Cf29699F223766F47FE79e2B7eB1a90e08C8",
|
||||||
"affiliateFeeTransformer": "0x9d7174f55b50dad2e417bd567ad2da1ae4eef76d"
|
"affiliateFeeTransformer": "0x9d7174f55b50dad2e417bd567ad2da1ae4eef76d"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -9,6 +9,10 @@
|
|||||||
{
|
{
|
||||||
"note": "Update `ERC20BridgeSampler` wrapper",
|
"note": "Update `ERC20BridgeSampler` wrapper",
|
||||||
"pr": 2633
|
"pr": 2633
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Add `exchangeProxy` to `ContractWrappers` type.",
|
||||||
|
"pr": 2649
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -11,6 +11,7 @@ import { ERC20TokenContract } from './generated-wrappers/erc20_token';
|
|||||||
import { ERC721TokenContract } from './generated-wrappers/erc721_token';
|
import { ERC721TokenContract } from './generated-wrappers/erc721_token';
|
||||||
import { ExchangeContract } from './generated-wrappers/exchange';
|
import { ExchangeContract } from './generated-wrappers/exchange';
|
||||||
import { ForwarderContract } from './generated-wrappers/forwarder';
|
import { ForwarderContract } from './generated-wrappers/forwarder';
|
||||||
|
import { IZeroExContract } from './generated-wrappers/i_zero_ex';
|
||||||
import { StakingContract } from './generated-wrappers/staking';
|
import { StakingContract } from './generated-wrappers/staking';
|
||||||
import { WETH9Contract } from './generated-wrappers/weth9';
|
import { WETH9Contract } from './generated-wrappers/weth9';
|
||||||
import { ContractWrappersConfig } from './types';
|
import { ContractWrappersConfig } from './types';
|
||||||
@ -49,6 +50,10 @@ export class ContractWrappers {
|
|||||||
* An instance of the StakingContract class containing methods for interacting with the Staking contracts.
|
* An instance of the StakingContract class containing methods for interacting with the Staking contracts.
|
||||||
*/
|
*/
|
||||||
public staking: StakingContract;
|
public staking: StakingContract;
|
||||||
|
/**
|
||||||
|
* An instance of the IZeroExContract class containing methods for interacting with the Exchange Proxy.
|
||||||
|
*/
|
||||||
|
public exchangeProxy: IZeroExContract;
|
||||||
|
|
||||||
private readonly _web3Wrapper: Web3Wrapper;
|
private readonly _web3Wrapper: Web3Wrapper;
|
||||||
/**
|
/**
|
||||||
@ -73,6 +78,7 @@ export class ContractWrappers {
|
|||||||
ForwarderContract,
|
ForwarderContract,
|
||||||
StakingContract,
|
StakingContract,
|
||||||
WETH9Contract,
|
WETH9Contract,
|
||||||
|
IZeroExContract,
|
||||||
];
|
];
|
||||||
contractsArray.forEach(contract => {
|
contractsArray.forEach(contract => {
|
||||||
this._web3Wrapper.abiDecoder.addABI(contract.ABI(), contract.contractName);
|
this._web3Wrapper.abiDecoder.addABI(contract.ABI(), contract.contractName);
|
||||||
@ -87,6 +93,7 @@ export class ContractWrappers {
|
|||||||
this.staking = new StakingContract(contractAddresses.stakingProxy, this.getProvider());
|
this.staking = new StakingContract(contractAddresses.stakingProxy, this.getProvider());
|
||||||
this.devUtils = new DevUtilsContract(contractAddresses.devUtils, this.getProvider());
|
this.devUtils = new DevUtilsContract(contractAddresses.devUtils, this.getProvider());
|
||||||
this.coordinator = new CoordinatorContract(contractAddresses.coordinator, this.getProvider());
|
this.coordinator = new CoordinatorContract(contractAddresses.coordinator, this.getProvider());
|
||||||
|
this.exchangeProxy = new IZeroExContract(contractAddresses.exchangeProxy, this.getProvider());
|
||||||
this.contractAddresses = contractAddresses;
|
this.contractAddresses = contractAddresses;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
|
@ -1,4 +1,13 @@
|
|||||||
[
|
[
|
||||||
|
{
|
||||||
|
"version": "3.3.0",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Make `payable` field in `ConstructorAbi` optional",
|
||||||
|
"pr": 2648
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"version": "3.2.0",
|
"version": "3.2.0",
|
||||||
"changes": [
|
"changes": [
|
||||||
|
@ -101,7 +101,7 @@ export interface ConstructorAbi {
|
|||||||
// from JSON files, and this value has type `string` not type `'constructor'`
|
// from JSON files, and this value has type `string` not type `'constructor'`
|
||||||
type: string;
|
type: string;
|
||||||
inputs: DataItem[];
|
inputs: DataItem[];
|
||||||
payable: boolean;
|
payable?: boolean;
|
||||||
stateMutability: ConstructorStateMutability;
|
stateMutability: ConstructorStateMutability;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,6 +5,10 @@
|
|||||||
{
|
{
|
||||||
"note": "Change test protocol fee to 70000.",
|
"note": "Change test protocol fee to 70000.",
|
||||||
"pr": 2637
|
"pr": 2637
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Refactor `migration.ts` a little",
|
||||||
|
"pr": 2656
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -14,12 +14,9 @@ import {
|
|||||||
CoordinatorRegistryContract,
|
CoordinatorRegistryContract,
|
||||||
} from '@0x/contracts-coordinator';
|
} from '@0x/contracts-coordinator';
|
||||||
import { artifacts as devUtilsArtifacts, DevUtilsContract } from '@0x/contracts-dev-utils';
|
import { artifacts as devUtilsArtifacts, DevUtilsContract } from '@0x/contracts-dev-utils';
|
||||||
import { artifacts as erc1155Artifacts, ERC1155MintableContract } from '@0x/contracts-erc1155';
|
import { artifacts as erc1155Artifacts } from '@0x/contracts-erc1155';
|
||||||
import { artifacts as erc20Artifacts, DummyERC20TokenContract, WETH9Contract } from '@0x/contracts-erc20';
|
import { artifacts as erc20Artifacts, DummyERC20TokenContract, WETH9Contract } from '@0x/contracts-erc20';
|
||||||
import {
|
import { artifacts as erc20BridgeSamplerArtifacts } from '@0x/contracts-erc20-bridge-sampler';
|
||||||
artifacts as erc20BridgeSamplerArtifacts,
|
|
||||||
ERC20BridgeSamplerContract,
|
|
||||||
} from '@0x/contracts-erc20-bridge-sampler';
|
|
||||||
import { artifacts as erc721Artifacts, DummyERC721TokenContract } from '@0x/contracts-erc721';
|
import { artifacts as erc721Artifacts, DummyERC721TokenContract } from '@0x/contracts-erc721';
|
||||||
import { artifacts as exchangeArtifacts, ExchangeContract } from '@0x/contracts-exchange';
|
import { artifacts as exchangeArtifacts, ExchangeContract } from '@0x/contracts-exchange';
|
||||||
import { artifacts as forwarderArtifacts, ForwarderContract } from '@0x/contracts-exchange-forwarder';
|
import { artifacts as forwarderArtifacts, ForwarderContract } from '@0x/contracts-exchange-forwarder';
|
||||||
@ -38,8 +35,9 @@ import {
|
|||||||
ITransformERC20Contract,
|
ITransformERC20Contract,
|
||||||
PayTakerTransformerContract,
|
PayTakerTransformerContract,
|
||||||
WethTransformerContract,
|
WethTransformerContract,
|
||||||
|
ZeroExContract,
|
||||||
} from '@0x/contracts-zero-ex';
|
} from '@0x/contracts-zero-ex';
|
||||||
import { Web3ProviderEngine } from '@0x/subproviders';
|
import { Web3ProviderEngine, ZeroExProvider } from '@0x/subproviders';
|
||||||
import { BigNumber, providerUtils } from '@0x/utils';
|
import { BigNumber, providerUtils } from '@0x/utils';
|
||||||
import { SupportedProvider, TxData } from 'ethereum-types';
|
import { SupportedProvider, TxData } from 'ethereum-types';
|
||||||
|
|
||||||
@ -61,6 +59,53 @@ const allArtifacts = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const { NULL_ADDRESS } = constants;
|
const { NULL_ADDRESS } = constants;
|
||||||
|
const NULL_ADDRESSES = {
|
||||||
|
erc20Proxy: NULL_ADDRESS,
|
||||||
|
erc721Proxy: NULL_ADDRESS,
|
||||||
|
erc1155Proxy: NULL_ADDRESS,
|
||||||
|
zrxToken: NULL_ADDRESS,
|
||||||
|
etherToken: NULL_ADDRESS,
|
||||||
|
exchange: NULL_ADDRESS,
|
||||||
|
assetProxyOwner: NULL_ADDRESS,
|
||||||
|
erc20BridgeProxy: NULL_ADDRESS,
|
||||||
|
zeroExGovernor: NULL_ADDRESS,
|
||||||
|
forwarder: NULL_ADDRESS,
|
||||||
|
coordinatorRegistry: NULL_ADDRESS,
|
||||||
|
coordinator: NULL_ADDRESS,
|
||||||
|
multiAssetProxy: NULL_ADDRESS,
|
||||||
|
staticCallProxy: NULL_ADDRESS,
|
||||||
|
devUtils: NULL_ADDRESS,
|
||||||
|
exchangeV2: NULL_ADDRESS,
|
||||||
|
zrxVault: NULL_ADDRESS,
|
||||||
|
staking: NULL_ADDRESS,
|
||||||
|
stakingProxy: NULL_ADDRESS,
|
||||||
|
uniswapBridge: NULL_ADDRESS,
|
||||||
|
eth2DaiBridge: NULL_ADDRESS,
|
||||||
|
kyberBridge: NULL_ADDRESS,
|
||||||
|
erc20BridgeSampler: NULL_ADDRESS,
|
||||||
|
chaiBridge: NULL_ADDRESS,
|
||||||
|
dydxBridge: NULL_ADDRESS,
|
||||||
|
curveBridge: NULL_ADDRESS,
|
||||||
|
uniswapV2Bridge: NULL_ADDRESS,
|
||||||
|
godsUnchainedValidator: NULL_ADDRESS,
|
||||||
|
broker: NULL_ADDRESS,
|
||||||
|
chainlinkStopLimit: NULL_ADDRESS,
|
||||||
|
maximumGasPrice: NULL_ADDRESS,
|
||||||
|
dexForwarderBridge: NULL_ADDRESS,
|
||||||
|
multiBridge: NULL_ADDRESS,
|
||||||
|
balancerBridge: NULL_ADDRESS,
|
||||||
|
exchangeProxyGovernor: NULL_ADDRESS,
|
||||||
|
exchangeProxy: NULL_ADDRESS,
|
||||||
|
exchangeProxyAllowanceTarget: NULL_ADDRESS,
|
||||||
|
exchangeProxyTransformerDeployer: NULL_ADDRESS,
|
||||||
|
exchangeProxyFlashWallet: NULL_ADDRESS,
|
||||||
|
transformers: {
|
||||||
|
wethTransformer: NULL_ADDRESS,
|
||||||
|
payTakerTransformer: NULL_ADDRESS,
|
||||||
|
fillQuoteTransformer: NULL_ADDRESS,
|
||||||
|
affiliateFeeTransformer: NULL_ADDRESS,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates and deploys all the contracts that are required for the latest
|
* Creates and deploys all the contracts that are required for the latest
|
||||||
@ -75,22 +120,8 @@ export async function runMigrationsAsync(
|
|||||||
): Promise<ContractAddresses> {
|
): Promise<ContractAddresses> {
|
||||||
const provider = providerUtils.standardizeOrThrow(supportedProvider);
|
const provider = providerUtils.standardizeOrThrow(supportedProvider);
|
||||||
const chainId = new BigNumber(await providerUtils.getChainIdAsync(provider));
|
const chainId = new BigNumber(await providerUtils.getChainIdAsync(provider));
|
||||||
|
const { exchangeV2 } = getContractAddressesForChainOrThrow(chainId.toNumber());
|
||||||
|
|
||||||
// Proxies
|
|
||||||
const erc20Proxy = await ERC20ProxyContract.deployFrom0xArtifactAsync(
|
|
||||||
assetProxyArtifacts.ERC20Proxy,
|
|
||||||
provider,
|
|
||||||
txDefaults,
|
|
||||||
allArtifacts,
|
|
||||||
);
|
|
||||||
const erc721Proxy = await ERC721ProxyContract.deployFrom0xArtifactAsync(
|
|
||||||
assetProxyArtifacts.ERC721Proxy,
|
|
||||||
provider,
|
|
||||||
txDefaults,
|
|
||||||
allArtifacts,
|
|
||||||
);
|
|
||||||
|
|
||||||
// ZRX
|
|
||||||
const zrxToken = await DummyERC20TokenContract.deployFrom0xArtifactAsync(
|
const zrxToken = await DummyERC20TokenContract.deployFrom0xArtifactAsync(
|
||||||
erc20Artifacts.DummyERC20Token,
|
erc20Artifacts.DummyERC20Token,
|
||||||
provider,
|
provider,
|
||||||
@ -110,6 +141,8 @@ export async function runMigrationsAsync(
|
|||||||
allArtifacts,
|
allArtifacts,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
await _migrateDummyTokensAsync(provider, txDefaults);
|
||||||
|
|
||||||
// Exchange
|
// Exchange
|
||||||
const exchange = await ExchangeContract.deployFrom0xArtifactAsync(
|
const exchange = await ExchangeContract.deployFrom0xArtifactAsync(
|
||||||
exchangeArtifacts.Exchange,
|
exchangeArtifacts.Exchange,
|
||||||
@ -119,31 +152,135 @@ export async function runMigrationsAsync(
|
|||||||
chainId,
|
chainId,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Dummy ERC20 tokens
|
// CoordinatorRegistry
|
||||||
for (const token of erc20TokenInfo) {
|
const coordinatorRegistry = await CoordinatorRegistryContract.deployFrom0xArtifactAsync(
|
||||||
const totalSupply = new BigNumber(1000000000000000000000000000);
|
coordinatorArtifacts.CoordinatorRegistry,
|
||||||
// tslint:disable-next-line:no-unused-variable
|
|
||||||
const dummyErc20Token = await DummyERC20TokenContract.deployFrom0xArtifactAsync(
|
|
||||||
erc20Artifacts.DummyERC20Token,
|
|
||||||
provider,
|
provider,
|
||||||
txDefaults,
|
txDefaults,
|
||||||
allArtifacts,
|
allArtifacts,
|
||||||
token.name,
|
|
||||||
token.symbol,
|
|
||||||
token.decimals,
|
|
||||||
totalSupply,
|
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
// ERC721
|
// Coordinator
|
||||||
// tslint:disable-next-line:no-unused-variable
|
const coordinator = await CoordinatorContract.deployFrom0xArtifactAsync(
|
||||||
const cryptoKittieToken = await DummyERC721TokenContract.deployFrom0xArtifactAsync(
|
coordinatorArtifacts.Coordinator,
|
||||||
erc721Artifacts.DummyERC721Token,
|
provider,
|
||||||
|
txDefaults,
|
||||||
|
allArtifacts,
|
||||||
|
exchange.address,
|
||||||
|
chainId,
|
||||||
|
);
|
||||||
|
|
||||||
|
const [
|
||||||
|
erc20Proxy,
|
||||||
|
erc721Proxy,
|
||||||
|
erc1155Proxy,
|
||||||
|
staticCallProxy,
|
||||||
|
multiAssetProxy,
|
||||||
|
erc20BridgeProxy,
|
||||||
|
] = await _migrateAssetProxiesAsync(exchange, provider, txDefaults);
|
||||||
|
|
||||||
|
// Dev Utils
|
||||||
|
const devUtils = await DevUtilsContract.deployWithLibrariesFrom0xArtifactAsync(
|
||||||
|
devUtilsArtifacts.DevUtils,
|
||||||
|
devUtilsArtifacts,
|
||||||
|
provider,
|
||||||
|
txDefaults,
|
||||||
|
allArtifacts,
|
||||||
|
exchange.address,
|
||||||
|
NULL_ADDRESS,
|
||||||
|
NULL_ADDRESS,
|
||||||
|
);
|
||||||
|
|
||||||
|
const [zrxVault, stakingLogic, stakingProxy] = await _migrateStakingAsync(
|
||||||
|
exchange,
|
||||||
|
erc20Proxy,
|
||||||
|
zrxToken.address,
|
||||||
|
etherToken.address,
|
||||||
|
provider,
|
||||||
|
txDefaults,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Forwarder
|
||||||
|
// Deployed after Exchange and Staking is configured as it queries
|
||||||
|
// in the constructor
|
||||||
|
const forwarder = await ForwarderContract.deployFrom0xArtifactAsync(
|
||||||
|
forwarderArtifacts.Forwarder,
|
||||||
|
provider,
|
||||||
|
txDefaults,
|
||||||
|
allArtifacts,
|
||||||
|
exchange.address,
|
||||||
|
exchangeV2,
|
||||||
|
etherToken.address,
|
||||||
|
);
|
||||||
|
|
||||||
|
const [
|
||||||
|
exchangeProxy,
|
||||||
|
fillQuoteTransformer,
|
||||||
|
payTakerTransformer,
|
||||||
|
wethTransformer,
|
||||||
|
affiliateFeeTransformer,
|
||||||
|
exchangeProxyFlashWalletAddress,
|
||||||
|
exchangeProxyAllowanceTargetAddress,
|
||||||
|
] = await _migrateExchangeProxyAsync(exchange, etherToken.address, provider, txDefaults);
|
||||||
|
|
||||||
|
const contractAddresses = {
|
||||||
|
...NULL_ADDRESSES,
|
||||||
|
erc20Proxy: erc20Proxy.address,
|
||||||
|
erc721Proxy: erc721Proxy.address,
|
||||||
|
erc1155Proxy: erc1155Proxy.address,
|
||||||
|
zrxToken: zrxToken.address,
|
||||||
|
etherToken: etherToken.address,
|
||||||
|
exchange: exchange.address,
|
||||||
|
erc20BridgeProxy: erc20BridgeProxy.address,
|
||||||
|
forwarder: forwarder.address,
|
||||||
|
coordinatorRegistry: coordinatorRegistry.address,
|
||||||
|
coordinator: coordinator.address,
|
||||||
|
multiAssetProxy: multiAssetProxy.address,
|
||||||
|
staticCallProxy: staticCallProxy.address,
|
||||||
|
devUtils: devUtils.address,
|
||||||
|
zrxVault: zrxVault.address,
|
||||||
|
staking: stakingLogic.address,
|
||||||
|
stakingProxy: stakingProxy.address,
|
||||||
|
exchangeProxy: exchangeProxy.address,
|
||||||
|
exchangeProxyAllowanceTarget: exchangeProxyAllowanceTargetAddress,
|
||||||
|
exchangeProxyTransformerDeployer: txDefaults.from,
|
||||||
|
exchangeProxyFlashWallet: exchangeProxyFlashWalletAddress,
|
||||||
|
transformers: {
|
||||||
|
wethTransformer: wethTransformer.address,
|
||||||
|
payTakerTransformer: payTakerTransformer.address,
|
||||||
|
fillQuoteTransformer: fillQuoteTransformer.address,
|
||||||
|
affiliateFeeTransformer: affiliateFeeTransformer.address,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
return contractAddresses;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function _migrateAssetProxiesAsync(
|
||||||
|
exchange: ExchangeContract,
|
||||||
|
provider: ZeroExProvider,
|
||||||
|
txDefaults: TxData,
|
||||||
|
): Promise<
|
||||||
|
[
|
||||||
|
ERC20ProxyContract,
|
||||||
|
ERC721ProxyContract,
|
||||||
|
ERC1155ProxyContract,
|
||||||
|
StaticCallProxyContract,
|
||||||
|
MultiAssetProxyContract,
|
||||||
|
ERC20BridgeProxyContract
|
||||||
|
]
|
||||||
|
> {
|
||||||
|
// Proxies
|
||||||
|
const erc20Proxy = await ERC20ProxyContract.deployFrom0xArtifactAsync(
|
||||||
|
assetProxyArtifacts.ERC20Proxy,
|
||||||
|
provider,
|
||||||
|
txDefaults,
|
||||||
|
allArtifacts,
|
||||||
|
);
|
||||||
|
const erc721Proxy = await ERC721ProxyContract.deployFrom0xArtifactAsync(
|
||||||
|
assetProxyArtifacts.ERC721Proxy,
|
||||||
provider,
|
provider,
|
||||||
txDefaults,
|
txDefaults,
|
||||||
allArtifacts,
|
allArtifacts,
|
||||||
erc721TokenInfo[0].name,
|
|
||||||
erc721TokenInfo[0].symbol,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// 1155 Asset Proxy
|
// 1155 Asset Proxy
|
||||||
@ -168,19 +305,29 @@ export async function runMigrationsAsync(
|
|||||||
allArtifacts,
|
allArtifacts,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const erc20BridgeProxy = await ERC20BridgeProxyContract.deployFrom0xArtifactAsync(
|
||||||
|
assetProxyArtifacts.ERC20BridgeProxy,
|
||||||
|
provider,
|
||||||
|
txDefaults,
|
||||||
|
allArtifacts,
|
||||||
|
);
|
||||||
|
|
||||||
await erc20Proxy.addAuthorizedAddress(exchange.address).awaitTransactionSuccessAsync(txDefaults);
|
await erc20Proxy.addAuthorizedAddress(exchange.address).awaitTransactionSuccessAsync(txDefaults);
|
||||||
await erc721Proxy.addAuthorizedAddress(exchange.address).awaitTransactionSuccessAsync(txDefaults);
|
await erc721Proxy.addAuthorizedAddress(exchange.address).awaitTransactionSuccessAsync(txDefaults);
|
||||||
await erc1155Proxy.addAuthorizedAddress(exchange.address).awaitTransactionSuccessAsync(txDefaults);
|
await erc1155Proxy.addAuthorizedAddress(exchange.address).awaitTransactionSuccessAsync(txDefaults);
|
||||||
await multiAssetProxy.addAuthorizedAddress(exchange.address).awaitTransactionSuccessAsync(txDefaults);
|
await multiAssetProxy.addAuthorizedAddress(exchange.address).awaitTransactionSuccessAsync(txDefaults);
|
||||||
|
await erc20BridgeProxy.addAuthorizedAddress(exchange.address).awaitTransactionSuccessAsync(txDefaults);
|
||||||
|
|
||||||
// MultiAssetProxy
|
// MultiAssetProxy
|
||||||
await erc20Proxy.addAuthorizedAddress(multiAssetProxy.address).awaitTransactionSuccessAsync(txDefaults);
|
await erc20Proxy.addAuthorizedAddress(multiAssetProxy.address).awaitTransactionSuccessAsync(txDefaults);
|
||||||
await erc721Proxy.addAuthorizedAddress(multiAssetProxy.address).awaitTransactionSuccessAsync(txDefaults);
|
await erc721Proxy.addAuthorizedAddress(multiAssetProxy.address).awaitTransactionSuccessAsync(txDefaults);
|
||||||
await erc1155Proxy.addAuthorizedAddress(multiAssetProxy.address).awaitTransactionSuccessAsync(txDefaults);
|
await erc1155Proxy.addAuthorizedAddress(multiAssetProxy.address).awaitTransactionSuccessAsync(txDefaults);
|
||||||
|
await erc20BridgeProxy.addAuthorizedAddress(multiAssetProxy.address).awaitTransactionSuccessAsync(txDefaults);
|
||||||
await multiAssetProxy.registerAssetProxy(erc20Proxy.address).awaitTransactionSuccessAsync(txDefaults);
|
await multiAssetProxy.registerAssetProxy(erc20Proxy.address).awaitTransactionSuccessAsync(txDefaults);
|
||||||
await multiAssetProxy.registerAssetProxy(erc721Proxy.address).awaitTransactionSuccessAsync(txDefaults);
|
await multiAssetProxy.registerAssetProxy(erc721Proxy.address).awaitTransactionSuccessAsync(txDefaults);
|
||||||
await multiAssetProxy.registerAssetProxy(erc1155Proxy.address).awaitTransactionSuccessAsync(txDefaults);
|
await multiAssetProxy.registerAssetProxy(erc1155Proxy.address).awaitTransactionSuccessAsync(txDefaults);
|
||||||
await multiAssetProxy.registerAssetProxy(staticCallProxy.address).awaitTransactionSuccessAsync(txDefaults);
|
await multiAssetProxy.registerAssetProxy(staticCallProxy.address).awaitTransactionSuccessAsync(txDefaults);
|
||||||
|
await multiAssetProxy.registerAssetProxy(erc20BridgeProxy.address).awaitTransactionSuccessAsync(txDefaults);
|
||||||
|
|
||||||
// Register the Asset Proxies to the Exchange
|
// Register the Asset Proxies to the Exchange
|
||||||
await exchange.registerAssetProxy(erc20Proxy.address).awaitTransactionSuccessAsync(txDefaults);
|
await exchange.registerAssetProxy(erc20Proxy.address).awaitTransactionSuccessAsync(txDefaults);
|
||||||
@ -188,64 +335,26 @@ export async function runMigrationsAsync(
|
|||||||
await exchange.registerAssetProxy(erc1155Proxy.address).awaitTransactionSuccessAsync(txDefaults);
|
await exchange.registerAssetProxy(erc1155Proxy.address).awaitTransactionSuccessAsync(txDefaults);
|
||||||
await exchange.registerAssetProxy(multiAssetProxy.address).awaitTransactionSuccessAsync(txDefaults);
|
await exchange.registerAssetProxy(multiAssetProxy.address).awaitTransactionSuccessAsync(txDefaults);
|
||||||
await exchange.registerAssetProxy(staticCallProxy.address).awaitTransactionSuccessAsync(txDefaults);
|
await exchange.registerAssetProxy(staticCallProxy.address).awaitTransactionSuccessAsync(txDefaults);
|
||||||
|
|
||||||
// CoordinatorRegistry
|
|
||||||
const coordinatorRegistry = await CoordinatorRegistryContract.deployFrom0xArtifactAsync(
|
|
||||||
coordinatorArtifacts.CoordinatorRegistry,
|
|
||||||
provider,
|
|
||||||
txDefaults,
|
|
||||||
allArtifacts,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Coordinator
|
|
||||||
const coordinator = await CoordinatorContract.deployFrom0xArtifactAsync(
|
|
||||||
coordinatorArtifacts.Coordinator,
|
|
||||||
provider,
|
|
||||||
txDefaults,
|
|
||||||
allArtifacts,
|
|
||||||
exchange.address,
|
|
||||||
chainId,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Dev Utils
|
|
||||||
const devUtils = await DevUtilsContract.deployWithLibrariesFrom0xArtifactAsync(
|
|
||||||
devUtilsArtifacts.DevUtils,
|
|
||||||
devUtilsArtifacts,
|
|
||||||
provider,
|
|
||||||
txDefaults,
|
|
||||||
allArtifacts,
|
|
||||||
exchange.address,
|
|
||||||
NULL_ADDRESS,
|
|
||||||
NULL_ADDRESS,
|
|
||||||
);
|
|
||||||
|
|
||||||
// tslint:disable-next-line:no-unused-variable
|
|
||||||
const erc1155DummyToken = await ERC1155MintableContract.deployFrom0xArtifactAsync(
|
|
||||||
erc1155Artifacts.ERC1155Mintable,
|
|
||||||
provider,
|
|
||||||
txDefaults,
|
|
||||||
allArtifacts,
|
|
||||||
);
|
|
||||||
|
|
||||||
const erc20BridgeProxy = await ERC20BridgeProxyContract.deployFrom0xArtifactAsync(
|
|
||||||
assetProxyArtifacts.ERC20BridgeProxy,
|
|
||||||
provider,
|
|
||||||
txDefaults,
|
|
||||||
allArtifacts,
|
|
||||||
);
|
|
||||||
await exchange.registerAssetProxy(erc20BridgeProxy.address).awaitTransactionSuccessAsync(txDefaults);
|
await exchange.registerAssetProxy(erc20BridgeProxy.address).awaitTransactionSuccessAsync(txDefaults);
|
||||||
await erc20BridgeProxy.addAuthorizedAddress(exchange.address).awaitTransactionSuccessAsync(txDefaults);
|
|
||||||
await erc20BridgeProxy.addAuthorizedAddress(multiAssetProxy.address).awaitTransactionSuccessAsync(txDefaults);
|
|
||||||
await multiAssetProxy.registerAssetProxy(erc20BridgeProxy.address).awaitTransactionSuccessAsync(txDefaults);
|
|
||||||
|
|
||||||
const zrxProxy = erc20Proxy.address;
|
return [erc20Proxy, erc721Proxy, erc1155Proxy, staticCallProxy, multiAssetProxy, erc20BridgeProxy];
|
||||||
|
}
|
||||||
|
|
||||||
|
async function _migrateStakingAsync(
|
||||||
|
exchange: ExchangeContract,
|
||||||
|
erc20Proxy: ERC20ProxyContract,
|
||||||
|
zrxTokenAddress: string,
|
||||||
|
etherTokenAddress: string,
|
||||||
|
provider: ZeroExProvider,
|
||||||
|
txDefaults: TxData,
|
||||||
|
): Promise<[ZrxVaultContract, TestStakingContract, StakingProxyContract]> {
|
||||||
const zrxVault = await ZrxVaultContract.deployFrom0xArtifactAsync(
|
const zrxVault = await ZrxVaultContract.deployFrom0xArtifactAsync(
|
||||||
stakingArtifacts.ZrxVault,
|
stakingArtifacts.ZrxVault,
|
||||||
provider,
|
provider,
|
||||||
txDefaults,
|
txDefaults,
|
||||||
allArtifacts,
|
allArtifacts,
|
||||||
zrxProxy,
|
erc20Proxy.address,
|
||||||
zrxToken.address,
|
zrxTokenAddress,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Note we use TestStakingContract as the deployed bytecode of a StakingContract
|
// Note we use TestStakingContract as the deployed bytecode of a StakingContract
|
||||||
@ -255,7 +364,7 @@ export async function runMigrationsAsync(
|
|||||||
provider,
|
provider,
|
||||||
txDefaults,
|
txDefaults,
|
||||||
allArtifacts,
|
allArtifacts,
|
||||||
etherToken.address,
|
etherTokenAddress,
|
||||||
zrxVault.address,
|
zrxVault.address,
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -281,29 +390,25 @@ export async function runMigrationsAsync(
|
|||||||
await stakingLogic.addAuthorizedAddress(txDefaults.from).awaitTransactionSuccessAsync(txDefaults);
|
await stakingLogic.addAuthorizedAddress(txDefaults.from).awaitTransactionSuccessAsync(txDefaults);
|
||||||
await stakingLogic.addExchangeAddress(exchange.address).awaitTransactionSuccessAsync(txDefaults);
|
await stakingLogic.addExchangeAddress(exchange.address).awaitTransactionSuccessAsync(txDefaults);
|
||||||
|
|
||||||
// Forwarder
|
return [zrxVault, stakingLogic, stakingProxy];
|
||||||
// Deployed after Exchange and Staking is configured as it queries
|
}
|
||||||
// in the constructor
|
|
||||||
const { exchangeV2: exchangeV2Address } = getContractAddressesForChainOrThrow(chainId.toNumber());
|
|
||||||
const forwarder = await ForwarderContract.deployFrom0xArtifactAsync(
|
|
||||||
forwarderArtifacts.Forwarder,
|
|
||||||
provider,
|
|
||||||
txDefaults,
|
|
||||||
allArtifacts,
|
|
||||||
exchange.address,
|
|
||||||
exchangeV2Address || NULL_ADDRESS,
|
|
||||||
etherToken.address,
|
|
||||||
);
|
|
||||||
|
|
||||||
const erc20BridgeSampler = await ERC20BridgeSamplerContract.deployFrom0xArtifactAsync(
|
|
||||||
erc20BridgeSamplerArtifacts.ERC20BridgeSampler,
|
|
||||||
provider,
|
|
||||||
txDefaults,
|
|
||||||
allArtifacts,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Exchange Proxy //////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
|
async function _migrateExchangeProxyAsync(
|
||||||
|
exchange: ExchangeContract,
|
||||||
|
etherTokenAddress: string,
|
||||||
|
provider: ZeroExProvider,
|
||||||
|
txDefaults: TxData,
|
||||||
|
): Promise<
|
||||||
|
[
|
||||||
|
ZeroExContract,
|
||||||
|
FillQuoteTransformerContract,
|
||||||
|
PayTakerTransformerContract,
|
||||||
|
WethTransformerContract,
|
||||||
|
AffiliateFeeTransformerContract,
|
||||||
|
string,
|
||||||
|
string
|
||||||
|
]
|
||||||
|
> {
|
||||||
const exchangeProxy = await fullMigrateExchangeProxyAsync(txDefaults.from, provider, txDefaults);
|
const exchangeProxy = await fullMigrateExchangeProxyAsync(txDefaults.from, provider, txDefaults);
|
||||||
const exchangeProxyAllowanceTargetAddress = await new ITokenSpenderContract(
|
const exchangeProxyAllowanceTargetAddress = await new ITokenSpenderContract(
|
||||||
exchangeProxy.address,
|
exchangeProxy.address,
|
||||||
@ -335,7 +440,7 @@ export async function runMigrationsAsync(
|
|||||||
provider,
|
provider,
|
||||||
txDefaults,
|
txDefaults,
|
||||||
allArtifacts,
|
allArtifacts,
|
||||||
etherToken.address,
|
etherTokenAddress,
|
||||||
);
|
);
|
||||||
const affiliateFeeTransformer = await AffiliateFeeTransformerContract.deployFrom0xArtifactAsync(
|
const affiliateFeeTransformer = await AffiliateFeeTransformerContract.deployFrom0xArtifactAsync(
|
||||||
exchangeProxyArtifacts.AffiliateFeeTransformer,
|
exchangeProxyArtifacts.AffiliateFeeTransformer,
|
||||||
@ -344,54 +449,42 @@ export async function runMigrationsAsync(
|
|||||||
allArtifacts,
|
allArtifacts,
|
||||||
);
|
);
|
||||||
|
|
||||||
const contractAddresses = {
|
return [
|
||||||
erc20Proxy: erc20Proxy.address,
|
exchangeProxy,
|
||||||
erc721Proxy: erc721Proxy.address,
|
fillQuoteTransformer,
|
||||||
erc1155Proxy: erc1155Proxy.address,
|
payTakerTransformer,
|
||||||
zrxToken: zrxToken.address,
|
wethTransformer,
|
||||||
etherToken: etherToken.address,
|
affiliateFeeTransformer,
|
||||||
exchange: exchange.address,
|
exchangeProxyFlashWalletAddress,
|
||||||
assetProxyOwner: NULL_ADDRESS,
|
exchangeProxyAllowanceTargetAddress,
|
||||||
erc20BridgeProxy: erc20BridgeProxy.address,
|
];
|
||||||
zeroExGovernor: NULL_ADDRESS,
|
}
|
||||||
forwarder: forwarder.address,
|
|
||||||
coordinatorRegistry: coordinatorRegistry.address,
|
async function _migrateDummyTokensAsync(provider: ZeroExProvider, txDefaults: TxData): Promise<void> {
|
||||||
coordinator: coordinator.address,
|
// Dummy ERC20 tokens
|
||||||
multiAssetProxy: multiAssetProxy.address,
|
for (const token of erc20TokenInfo) {
|
||||||
staticCallProxy: staticCallProxy.address,
|
const totalSupply = new BigNumber(1000000000000000000000000000);
|
||||||
devUtils: devUtils.address,
|
await DummyERC20TokenContract.deployFrom0xArtifactAsync(
|
||||||
exchangeV2: exchangeV2Address || NULL_ADDRESS,
|
erc20Artifacts.DummyERC20Token,
|
||||||
zrxVault: zrxVault.address,
|
provider,
|
||||||
staking: stakingLogic.address,
|
txDefaults,
|
||||||
stakingProxy: stakingProxy.address,
|
allArtifacts,
|
||||||
uniswapBridge: NULL_ADDRESS,
|
token.name,
|
||||||
eth2DaiBridge: NULL_ADDRESS,
|
token.symbol,
|
||||||
kyberBridge: NULL_ADDRESS,
|
token.decimals,
|
||||||
erc20BridgeSampler: erc20BridgeSampler.address,
|
totalSupply,
|
||||||
chaiBridge: NULL_ADDRESS,
|
);
|
||||||
dydxBridge: NULL_ADDRESS,
|
}
|
||||||
curveBridge: NULL_ADDRESS,
|
|
||||||
uniswapV2Bridge: NULL_ADDRESS,
|
// Dummy ERC721 token
|
||||||
godsUnchainedValidator: NULL_ADDRESS,
|
await DummyERC721TokenContract.deployFrom0xArtifactAsync(
|
||||||
broker: NULL_ADDRESS,
|
erc721Artifacts.DummyERC721Token,
|
||||||
chainlinkStopLimit: NULL_ADDRESS,
|
provider,
|
||||||
maximumGasPrice: NULL_ADDRESS,
|
txDefaults,
|
||||||
dexForwarderBridge: NULL_ADDRESS,
|
allArtifacts,
|
||||||
multiBridge: NULL_ADDRESS,
|
erc721TokenInfo[0].name,
|
||||||
balancerBridge: NULL_ADDRESS,
|
erc721TokenInfo[0].symbol,
|
||||||
exchangeProxyGovernor: NULL_ADDRESS,
|
);
|
||||||
exchangeProxy: exchangeProxy.address,
|
|
||||||
exchangeProxyAllowanceTarget: exchangeProxyAllowanceTargetAddress,
|
|
||||||
exchangeProxyTransformerDeployer: txDefaults.from,
|
|
||||||
exchangeProxyFlashWallet: exchangeProxyFlashWalletAddress,
|
|
||||||
transformers: {
|
|
||||||
wethTransformer: wethTransformer.address,
|
|
||||||
payTakerTransformer: payTakerTransformer.address,
|
|
||||||
fillQuoteTransformer: fillQuoteTransformer.address,
|
|
||||||
affiliateFeeTransformer: affiliateFeeTransformer.address,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
return contractAddresses;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let _cachedContractAddresses: ContractAddresses;
|
let _cachedContractAddresses: ContractAddresses;
|
||||||
@ -414,3 +507,4 @@ export async function runMigrationsOnceAsync(
|
|||||||
_cachedContractAddresses = await runMigrationsAsync(provider, txDefaults);
|
_cachedContractAddresses = await runMigrationsAsync(provider, txDefaults);
|
||||||
return _cachedContractAddresses;
|
return _cachedContractAddresses;
|
||||||
}
|
}
|
||||||
|
// tslint:disable-next-line: max-file-line-count
|
||||||
|
@ -28,6 +28,7 @@ export const docGenConfigs: DocGenConfigs = {
|
|||||||
TFillData: true,
|
TFillData: true,
|
||||||
IterableIterator: true,
|
IterableIterator: true,
|
||||||
Set: true,
|
Set: true,
|
||||||
|
Exclude: true,
|
||||||
},
|
},
|
||||||
// Some types are not explicitly part of the public interface like params, return values, etc... But we still
|
// Some types are not explicitly part of the public interface like params, return values, etc... But we still
|
||||||
// want them exported. E.g error enum types that can be thrown by methods. These must be manually added to this
|
// want them exported. E.g error enum types that can be thrown by methods. These must be manually added to this
|
||||||
|
@ -1,4 +1,17 @@
|
|||||||
[
|
[
|
||||||
|
{
|
||||||
|
"version": "10.3.1",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Add gitpkg.",
|
||||||
|
"pr": 2649
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Fix `decodeAffiliateFeeTransformerData`",
|
||||||
|
"pr": 2658
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"version": "10.3.0",
|
"version": "10.3.0",
|
||||||
"changes": [
|
"changes": [
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "yarn tsc -b",
|
"build": "yarn tsc -b",
|
||||||
"build:ci": "yarn build",
|
"build:ci": "yarn build",
|
||||||
|
"publish:private": "yarn clean && yarn build && gitpkg publish",
|
||||||
"test": "yarn run_mocha",
|
"test": "yarn run_mocha",
|
||||||
"rebuild_and_test": "run-s build test",
|
"rebuild_and_test": "run-s build test",
|
||||||
"test:circleci": "yarn test:coverage",
|
"test:circleci": "yarn test:coverage",
|
||||||
@ -29,6 +30,9 @@
|
|||||||
"assets": []
|
"assets": []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"gitpkg": {
|
||||||
|
"registry": "git@github.com:0xProject/gitpkg-registry.git"
|
||||||
|
},
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
@ -52,6 +56,7 @@
|
|||||||
"@types/web3-provider-engine": "^14.0.0",
|
"@types/web3-provider-engine": "^14.0.0",
|
||||||
"chai": "^4.0.1",
|
"chai": "^4.0.1",
|
||||||
"ethereum-types": "^3.2.0",
|
"ethereum-types": "^3.2.0",
|
||||||
|
"gitpkg": "https://github.com/0xProject/gitpkg.git",
|
||||||
"make-promises-safe": "^1.1.0",
|
"make-promises-safe": "^1.1.0",
|
||||||
"mocha": "^6.2.0",
|
"mocha": "^6.2.0",
|
||||||
"npm-run-all": "^4.1.2",
|
"npm-run-all": "^4.1.2",
|
||||||
|
@ -183,5 +183,5 @@ export function encodeAffiliateFeeTransformerData(data: AffiliateFeeTransformerD
|
|||||||
* ABI-decode a `AffiliateFeeTransformer.TransformData` type.
|
* ABI-decode a `AffiliateFeeTransformer.TransformData` type.
|
||||||
*/
|
*/
|
||||||
export function decodeAffiliateFeeTransformerData(encoded: string): AffiliateFeeTransformerData {
|
export function decodeAffiliateFeeTransformerData(encoded: string): AffiliateFeeTransformerData {
|
||||||
return affiliateFeeTransformerDataEncoder.decode(encoded).data;
|
return affiliateFeeTransformerDataEncoder.decode(encoded);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user