get vips working

This commit is contained in:
Lawrence Forman
2021-11-29 15:03:57 -05:00
parent 2f60eb1c79
commit 22ec626870
15 changed files with 409 additions and 273 deletions

View File

@@ -134,7 +134,6 @@ export {
LiquidityProviderRegistry,
MarketDepth,
MarketDepthSide,
NativeCollapsedFill,
TokenAdjacencyGraph,
} from './utils/market_operation_utils/types';
export { ProtocolFeeUtils } from './utils/protocol_fee_utils';

View File

@@ -12,12 +12,14 @@ import {
FillQuoteTransformerSide,
findTransformerNonce,
} from '@0x/protocol-utils';
import { BigNumber } from '@0x/utils';
import { BigNumber, hexUtils } from '@0x/utils';
import * as _ from 'lodash';
import { constants, POSITIVE_SLIPPAGE_FEE_TRANSFORMER_GAS } from '../constants';
import {
Address,
AffiliateFeeType,
Bytes,
CalldataInfo,
ExchangeProxyContractOpts,
MarketBuySwapQuote,
@@ -28,8 +30,14 @@ import {
SwapQuoteConsumerOpts,
SwapQuoteExecutionOpts,
SwapQuoteGetOutputOpts,
SwapQuoteLiquidityProviderBridgeOrder,
SwapQuoteUniswapV2BridgeOrder,
SwapQuoteUniswapV3BridgeOrder,
SwapQuoteCurveBridgeOrder,
SwapQuoteMooniswapBridgeOrder,
} from '../types';
import { assert } from '../utils/assert';
import { valueByChainId } from '../utils/utils';
import {
NATIVE_FEE_TOKEN_BY_CHAIN_ID,
} from '../utils/market_operation_utils/constants';
@@ -67,12 +75,30 @@ const PANCAKE_SWAP_FORKS = [
ERC20BridgeSource.CheeseSwap,
ERC20BridgeSource.JulSwap,
];
const FAKE_PROVIDER: any = {
sendAsync(): void {
return;
},
};
const CURVE_LIQUIDITY_PROVIDER_BY_CHAIN_ID = valueByChainId<string>(
{
[ChainId.Mainnet]: '0x561b94454b65614ae3db0897b74303f4acf7cc75',
[ChainId.Ropsten]: '0xae241c6fc7f28f6dc0cb58b4112ba7f63fcaf5e2',
},
NULL_ADDRESS,
);
const MOONISWAP_LIQUIDITY_PROVIDER_BY_CHAIN_ID = valueByChainId<string>(
{
[ChainId.Mainnet]: '0xa2033d6ba88756ce6a87584d69dc87bda9a4f889',
[ChainId.Ropsten]: '0x87e0393aee0fb8c10b8653c6507c182264fe5a34',
},
NULL_ADDRESS,
);
export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
public readonly chainId: ChainId;
public readonly transformerNonces: {
@@ -141,184 +167,185 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
}
// VIP routes.
// if (
// this.chainId === ChainId.Mainnet &&
// isDirectSwapCompatible(quote, optsWithDefaults, [ERC20BridgeSource.UniswapV2, ERC20BridgeSource.SushiSwap])
// ) {
// const source = slippedOrders[0].source;
// const fillData = (slippedOrders[0] as OptimizedMarketBridgeOrder<UniswapV2FillData>).fillData;
// return {
// calldataHexString: this._exchangeProxy
// .sellToUniswap(
// fillData.tokenAddressPath.map((a, i) => {
// if (i === 0 && isFromETH) {
// return ETH_TOKEN_ADDRESS;
// }
// if (i === fillData.tokenAddressPath.length - 1 && isToETH) {
// return ETH_TOKEN_ADDRESS;
// }
// return a;
// }),
// sellAmount,
// minBuyAmount,
// source === ERC20BridgeSource.SushiSwap,
// )
// .getABIEncodedTransactionData(),
// ethAmount: isFromETH ? sellAmount : ZERO_AMOUNT,
// toAddress: this._exchangeProxy.address,
// allowanceTarget: this._exchangeProxy.address,
// gasOverhead: ZERO_AMOUNT,
// };
// }
//
// if (
// this.chainId === ChainId.Mainnet &&
// isDirectSwapCompatible(quote, optsWithDefaults, [ERC20BridgeSource.UniswapV3])
// ) {
// const fillData = (slippedOrders[0] as OptimizedMarketBridgeOrder<FinalUniswapV3FillData>).fillData;
// let _calldataHexString;
// if (isFromETH) {
// _calldataHexString = this._exchangeProxy
// .sellEthForTokenToUniswapV3(fillData.uniswapPath, minBuyAmount, NULL_ADDRESS)
// .getABIEncodedTransactionData();
// } else if (isToETH) {
// _calldataHexString = this._exchangeProxy
// .sellTokenForEthToUniswapV3(fillData.uniswapPath, sellAmount, minBuyAmount, NULL_ADDRESS)
// .getABIEncodedTransactionData();
// } else {
// _calldataHexString = this._exchangeProxy
// .sellTokenForTokenToUniswapV3(fillData.uniswapPath, sellAmount, minBuyAmount, NULL_ADDRESS)
// .getABIEncodedTransactionData();
// }
// return {
// calldataHexString: _calldataHexString,
// ethAmount: isFromETH ? sellAmount : ZERO_AMOUNT,
// toAddress: this._exchangeProxy.address,
// allowanceTarget: this._exchangeProxy.address,
// gasOverhead: ZERO_AMOUNT,
// };
// }
//
// if (
// this.chainId === ChainId.BSC &&
// isDirectSwapCompatible(quote, optsWithDefaults, [
// ERC20BridgeSource.PancakeSwap,
// ERC20BridgeSource.PancakeSwapV2,
// ERC20BridgeSource.BakerySwap,
// ERC20BridgeSource.SushiSwap,
// ERC20BridgeSource.ApeSwap,
// ERC20BridgeSource.CafeSwap,
// ERC20BridgeSource.CheeseSwap,
// ERC20BridgeSource.JulSwap,
// ])
// ) {
// const source = slippedOrders[0].source;
// const fillData = (slippedOrders[0] as OptimizedMarketBridgeOrder<UniswapV2FillData>).fillData;
// return {
// calldataHexString: this._exchangeProxy
// .sellToPancakeSwap(
// fillData.tokenAddressPath.map((a, i) => {
// if (i === 0 && isFromETH) {
// return ETH_TOKEN_ADDRESS;
// }
// if (i === fillData.tokenAddressPath.length - 1 && isToETH) {
// return ETH_TOKEN_ADDRESS;
// }
// return a;
// }),
// sellAmount,
// minBuyAmount,
// PANCAKE_SWAP_FORKS.indexOf(source),
// )
// .getABIEncodedTransactionData(),
// ethAmount: isFromETH ? sellAmount : ZERO_AMOUNT,
// toAddress: this._exchangeProxy.address,
// allowanceTarget: this._exchangeProxy.address,
// gasOverhead: ZERO_AMOUNT,
// };
// }
//
// if (
// [ChainId.Mainnet, ChainId.BSC].includes(this.chainId) &&
// isDirectSwapCompatible(quote, optsWithDefaults, [ERC20BridgeSource.LiquidityProvider])
// ) {
// const fillData = (slippedOrders[0] as OptimizedMarketBridgeOrder<LiquidityProviderFillData>).fillData;
// const target = fillData.poolAddress;
// return {
// calldataHexString: this._exchangeProxy
// .sellToLiquidityProvider(
// isFromETH ? ETH_TOKEN_ADDRESS : sellToken,
// isToETH ? ETH_TOKEN_ADDRESS : buyToken,
// target,
// NULL_ADDRESS,
// sellAmount,
// minBuyAmount,
// NULL_BYTES,
// )
// .getABIEncodedTransactionData(),
// ethAmount: isFromETH ? sellAmount : ZERO_AMOUNT,
// toAddress: this._exchangeProxy.address,
// allowanceTarget: this._exchangeProxy.address,
// gasOverhead: ZERO_AMOUNT,
// };
// }
if (
this.chainId === ChainId.Mainnet &&
isDirectSwapCompatible(quote, optsWithDefaults, [ERC20BridgeSource.UniswapV2, ERC20BridgeSource.SushiSwap])
) {
const order = quote.hops[0].orders[0] as SwapQuoteUniswapV2BridgeOrder;
const { source } = order;
const { fillData } = order;
return {
calldataHexString: this._exchangeProxy
.sellToUniswap(
fillData.tokenAddressPath.map((a, i) => {
if (i === 0 && isFromETH) {
return ETH_TOKEN_ADDRESS;
}
if (i === fillData.tokenAddressPath.length - 1 && isToETH) {
return ETH_TOKEN_ADDRESS;
}
return a;
}),
sellAmount,
minBuyAmount,
source === ERC20BridgeSource.SushiSwap,
)
.getABIEncodedTransactionData(),
ethAmount: isFromETH ? sellAmount : ZERO_AMOUNT,
toAddress: this._exchangeProxy.address,
allowanceTarget: this._exchangeProxy.address,
gasOverhead: ZERO_AMOUNT,
};
}
// if (
// this.chainId === ChainId.Mainnet &&
// isDirectSwapCompatible(quote, optsWithDefaults, [ERC20BridgeSource.Curve, ERC20BridgeSource.Swerve]) &&
// // Curve VIP cannot currently support WETH buy/sell as the functionality needs to WITHDRAW or DEPOSIT
// // into WETH prior/post the trade.
// // ETH buy/sell is supported
// ![sellToken, buyToken].includes(NATIVE_FEE_TOKEN_BY_CHAIN_ID[ChainId.Mainnet])
// ) {
// const fillData = slippedOrders[0].fills[0].fillData as CurveFillData;
// return {
// calldataHexString: this._exchangeProxy
// .sellToLiquidityProvider(
// isFromETH ? ETH_TOKEN_ADDRESS : sellToken,
// isToETH ? ETH_TOKEN_ADDRESS : buyToken,
// CURVE_LIQUIDITY_PROVIDER_BY_CHAIN_ID[this.chainId],
// NULL_ADDRESS,
// sellAmount,
// minBuyAmount,
// encodeCurveLiquidityProviderData({
// curveAddress: fillData.pool.poolAddress,
// exchangeFunctionSelector: fillData.pool.exchangeFunctionSelector,
// fromCoinIdx: new BigNumber(fillData.fromTokenIdx),
// toCoinIdx: new BigNumber(fillData.toTokenIdx),
// }),
// )
// .getABIEncodedTransactionData(),
// ethAmount: isFromETH ? sellAmount : ZERO_AMOUNT,
// toAddress: this._exchangeProxy.address,
// allowanceTarget: this._exchangeProxy.address,
// gasOverhead: ZERO_AMOUNT,
// };
// }
if (
this.chainId === ChainId.Mainnet &&
isDirectSwapCompatible(quote, optsWithDefaults, [ERC20BridgeSource.UniswapV3])
) {
const order = quote.hops[0].orders[0] as SwapQuoteUniswapV3BridgeOrder;
const { fillData } = order;
let _calldataHexString;
if (isFromETH) {
_calldataHexString = this._exchangeProxy
.sellEthForTokenToUniswapV3(fillData.encodedPath, minBuyAmount, NULL_ADDRESS)
.getABIEncodedTransactionData();
} else if (isToETH) {
_calldataHexString = this._exchangeProxy
.sellTokenForEthToUniswapV3(fillData.encodedPath, sellAmount, minBuyAmount, NULL_ADDRESS)
.getABIEncodedTransactionData();
} else {
_calldataHexString = this._exchangeProxy
.sellTokenForTokenToUniswapV3(fillData.encodedPath, sellAmount, minBuyAmount, NULL_ADDRESS)
.getABIEncodedTransactionData();
}
return {
calldataHexString: _calldataHexString,
ethAmount: isFromETH ? sellAmount : ZERO_AMOUNT,
toAddress: this._exchangeProxy.address,
allowanceTarget: this._exchangeProxy.address,
gasOverhead: ZERO_AMOUNT,
};
}
// if (
// this.chainId === ChainId.Mainnet &&
// isDirectSwapCompatible(quote, optsWithDefaults, [ERC20BridgeSource.Mooniswap])
// ) {
// const fillData = slippedOrders[0].fills[0].fillData as MooniswapFillData;
// return {
// calldataHexString: this._exchangeProxy
// .sellToLiquidityProvider(
// isFromETH ? ETH_TOKEN_ADDRESS : sellToken,
// isToETH ? ETH_TOKEN_ADDRESS : buyToken,
// MOONISWAP_LIQUIDITY_PROVIDER_BY_CHAIN_ID[this.chainId],
// NULL_ADDRESS,
// sellAmount,
// minBuyAmount,
// poolEncoder.encode([fillData.poolAddress]),
// )
// .getABIEncodedTransactionData(),
// ethAmount: isFromETH ? sellAmount : ZERO_AMOUNT,
// toAddress: this._exchangeProxy.address,
// allowanceTarget: this.contractAddresses.exchangeProxy,
// gasOverhead: ZERO_AMOUNT,
// };
// }
if (
this.chainId === ChainId.BSC &&
isDirectSwapCompatible(quote, optsWithDefaults, [
ERC20BridgeSource.PancakeSwap,
ERC20BridgeSource.PancakeSwapV2,
ERC20BridgeSource.BakerySwap,
ERC20BridgeSource.SushiSwap,
ERC20BridgeSource.ApeSwap,
ERC20BridgeSource.CafeSwap,
ERC20BridgeSource.CheeseSwap,
ERC20BridgeSource.JulSwap,
])
) {
const order = quote.hops[0].orders[0] as SwapQuoteUniswapV2BridgeOrder;
const { source, fillData } = order;
return {
calldataHexString: this._exchangeProxy
.sellToPancakeSwap(
fillData.tokenAddressPath.map((a, i) => {
if (i === 0 && isFromETH) {
return ETH_TOKEN_ADDRESS;
}
if (i === fillData.tokenAddressPath.length - 1 && isToETH) {
return ETH_TOKEN_ADDRESS;
}
return a;
}),
sellAmount,
minBuyAmount,
PANCAKE_SWAP_FORKS.indexOf(source),
)
.getABIEncodedTransactionData(),
ethAmount: isFromETH ? sellAmount : ZERO_AMOUNT,
toAddress: this._exchangeProxy.address,
allowanceTarget: this._exchangeProxy.address,
gasOverhead: ZERO_AMOUNT,
};
}
if (
[ChainId.Mainnet, ChainId.BSC].includes(this.chainId) &&
isDirectSwapCompatible(quote, optsWithDefaults, [ERC20BridgeSource.LiquidityProvider])
) {
const { fillData } = quote.hops[0].orders[0] as SwapQuoteLiquidityProviderBridgeOrder;
return {
calldataHexString: this._exchangeProxy
.sellToLiquidityProvider(
isFromETH ? ETH_TOKEN_ADDRESS : sellToken,
isToETH ? ETH_TOKEN_ADDRESS : buyToken,
fillData.poolAddress,
NULL_ADDRESS,
sellAmount,
minBuyAmount,
NULL_BYTES,
)
.getABIEncodedTransactionData(),
ethAmount: isFromETH ? sellAmount : ZERO_AMOUNT,
toAddress: this._exchangeProxy.address,
allowanceTarget: this._exchangeProxy.address,
gasOverhead: ZERO_AMOUNT,
};
}
if (
this.chainId === ChainId.Mainnet &&
isDirectSwapCompatible(quote, optsWithDefaults, [ERC20BridgeSource.Curve, ERC20BridgeSource.Swerve]) &&
// Curve VIP cannot currently support WETH buy/sell as the functionality needs to WITHDRAW or DEPOSIT
// into WETH prior/post the trade.
// ETH buy/sell is supported
![sellToken, buyToken].includes(NATIVE_FEE_TOKEN_BY_CHAIN_ID[ChainId.Mainnet])
) {
const { fillData } = quote.hops[0].orders[0] as SwapQuoteCurveBridgeOrder;
return {
calldataHexString: this._exchangeProxy
.sellToLiquidityProvider(
isFromETH ? ETH_TOKEN_ADDRESS : sellToken,
isToETH ? ETH_TOKEN_ADDRESS : buyToken,
CURVE_LIQUIDITY_PROVIDER_BY_CHAIN_ID[this.chainId],
NULL_ADDRESS,
sellAmount,
minBuyAmount,
encodeCurveLiquidityProviderData({
curveAddress: fillData.poolAddress,
exchangeFunctionSelector: fillData.exchangeFunctionSelector,
fromCoinIdx: new BigNumber(fillData.fromTokenIdx),
toCoinIdx: new BigNumber(fillData.toTokenIdx),
}),
)
.getABIEncodedTransactionData(),
ethAmount: isFromETH ? sellAmount : ZERO_AMOUNT,
toAddress: this._exchangeProxy.address,
allowanceTarget: this._exchangeProxy.address,
gasOverhead: ZERO_AMOUNT,
};
}
if (
this.chainId === ChainId.Mainnet &&
isDirectSwapCompatible(quote, optsWithDefaults, [ERC20BridgeSource.Mooniswap])
) {
const { fillData } = quote.hops[0].orders[0] as SwapQuoteMooniswapBridgeOrder;
return {
calldataHexString: this._exchangeProxy
.sellToLiquidityProvider(
isFromETH ? ETH_TOKEN_ADDRESS : sellToken,
isToETH ? ETH_TOKEN_ADDRESS : buyToken,
MOONISWAP_LIQUIDITY_PROVIDER_BY_CHAIN_ID[this.chainId],
NULL_ADDRESS,
sellAmount,
minBuyAmount,
encodeAddress(fillData.poolAddress),
)
.getABIEncodedTransactionData(),
ethAmount: isFromETH ? sellAmount : ZERO_AMOUNT,
toAddress: this._exchangeProxy.address,
allowanceTarget: this.contractAddresses.exchangeProxy,
gasOverhead: ZERO_AMOUNT,
};
}
// if (this.chainId === ChainId.Mainnet && isMultiplexBatchFillCompatible(quote, optsWithDefaults)) {
// return {
@@ -650,3 +677,7 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
// }
// }
}
function encodeAddress(address: Address): Bytes {
return hexUtils.leftPad(hexUtils.slice(address, 0, 20));
}

View File

@@ -8,7 +8,7 @@ import {
ERC20BridgeSource,
} from '../utils/market_operation_utils/types';
import {
SwapQuoteBridgeOrder,
SwapQuoteGenericBridgeOrder,
SwapQuoteOrder,
SwapQuoteLimitOrder,
SwapQuoteRfqOrder,
@@ -96,7 +96,7 @@ export function isBuyQuote(quote: SwapQuote): quote is MarketBuySwapQuote {
return quote.type === MarketOperation.Buy;
}
function isBridgeOrder(x: SwapQuoteOrder): x is SwapQuoteBridgeOrder {
function isBridgeOrder(x: SwapQuoteOrder): x is SwapQuoteGenericBridgeOrder {
return x.type === FillQuoteTransformerOrderType.Bridge;
}
@@ -125,7 +125,7 @@ export function getFQTTransformerDataFromOptimizedOrders(
for (const order of orders) {
if (isBridgeOrder(order)) {
fqtData.bridgeOrders.push({
bridgeData: order.encodedFillData,
bridgeData: order.fillData.encodedFillData,
makerTokenAmount: order.makerAmount,
takerTokenAmount: order.takerAmount,
source: getErc20BridgeSourceToBridgeSource(order.source),

View File

@@ -20,6 +20,8 @@ import {
SwapQuoteInfo,
SwapQuoteHop,
SwapQuoteOrder,
SwapQuoteGenericBridgeOrder,
SwapQuoteNativeOrder,
SwapQuoteOrdersBreakdown,
SwapQuoteRequestOpts,
SwapQuoterOpts,
@@ -41,6 +43,7 @@ import {
OptimizedBridgeOrder,
OptimizedLimitOrder,
OptimizedRfqOrder,
OptimizedGenericBridgeOrder,
OptimizerResultWithReport,
} from './utils/market_operation_utils/types';
import { ProtocolFeeUtils } from './utils/protocol_fee_utils';
@@ -550,7 +553,7 @@ function slipTakerAmount(side: MarketOperation, takerAmount: BigNumber, slippage
);
}
function toSwapQuoteOrder(order: OptimizedOrder, side: MarketOperation, slippage: number): SwapQuoteOrder {
function toSwapQuoteOrder(order: OptimizedOrder, side: MarketOperation, slippage: number): SwapQuoteGenericBridgeOrder | SwapQuoteNativeOrder {
const { inputToken, outputToken, inputAmount, outputAmount, ...rest } = order;
const common = {
...rest,
@@ -562,7 +565,6 @@ function toSwapQuoteOrder(order: OptimizedOrder, side: MarketOperation, slippage
if (isBridgeOrder(order)) {
return {
...common,
encodedFillData: order.encodedFillData,
minMakerAmount: slipMakerAmount(
side,
side === MarketOperation.Sell
@@ -579,13 +581,10 @@ function toSwapQuoteOrder(order: OptimizedOrder, side: MarketOperation, slippage
),
};
}
return {
...common,
orderInfo: order.orderInfo,
} as any; // y typescript
return common as SwapQuoteNativeOrder;
}
function isBridgeOrder(order: OptimizedOrder): order is OptimizedBridgeOrder {
function isBridgeOrder(order: OptimizedOrder): order is OptimizedGenericBridgeOrder {
return order.type === FillQuoteTransformerOrderType.Bridge;
}

View File

@@ -18,7 +18,14 @@ import {
ERC20BridgeSource,
GetMarketOrdersOpts,
LiquidityProviderRegistry,
LiquidityProviderFillData,
TokenAdjacencyGraph,
BridgeFillData,
CurveFillData,
UniswapV2FillData,
UniswapV3FillData,
NativeOrderFillData,
MooniswapFillData,
} from './utils/market_operation_utils/types';
import { PriceComparisonsReport, QuoteReport } from './utils/quote_report_generator';
import { MetricsProxy } from './utils/quote_requestor';
@@ -196,7 +203,7 @@ export interface SwapQuoteHop {
orders: SwapQuoteOrder[];
}
interface SwapQuoteOrderBase {
export interface SwapQuoteOrder {
type: FillQuoteTransformerOrderType; // should correspond with TFillData
source: ERC20BridgeSource;
makerToken: string;
@@ -205,26 +212,38 @@ interface SwapQuoteOrderBase {
makerAmount: BigNumber;
takerAmount: BigNumber;
isFallback: boolean;
fillData?: any;
}
export interface SwapQuoteBridgeOrder extends SwapQuoteOrderBase {
encodedFillData: Bytes;
export interface SwapQuoteBridgeOrder<TFillData extends BridgeFillData> extends SwapQuoteOrder {
fillData: TFillData;
minMakerAmount: BigNumber;
maxTakerAmount: BigNumber;
}
export interface SwapQuoteLimitOrder extends SwapQuoteOrderBase {
export interface SwapQuoteGenericBridgeOrder extends SwapQuoteBridgeOrder<BridgeFillData> {}
export interface SwapQuoteUniswapV2BridgeOrder extends SwapQuoteBridgeOrder<UniswapV2FillData> {}
export interface SwapQuoteUniswapV3BridgeOrder extends SwapQuoteBridgeOrder<UniswapV3FillData> {}
export interface SwapQuoteLiquidityProviderBridgeOrder extends SwapQuoteBridgeOrder<LiquidityProviderFillData> {}
export interface SwapQuoteMooniswapBridgeOrder extends SwapQuoteBridgeOrder<MooniswapFillData> {}
export interface SwapQuoteCurveBridgeOrder extends SwapQuoteBridgeOrder<CurveFillData> {}
export interface SwapQuoteLimitOrder extends SwapQuoteOrder {
type: FillQuoteTransformerOrderType.Limit;
orderInfo: FillQuoteTransformerLimitOrderInfo;
fillData: NativeOrderFillData;
}
export interface SwapQuoteRfqOrder extends SwapQuoteOrderBase {
export interface SwapQuoteRfqOrder extends SwapQuoteOrder {
type: FillQuoteTransformerOrderType.Rfq;
orderInfo: FillQuoteTransformerRfqOrderInfo;
fillData: NativeOrderFillData;
}
export type SwapQuoteOrder =
SwapQuoteBridgeOrder | SwapQuoteLimitOrder | SwapQuoteRfqOrder;
export type SwapQuoteNativeOrder = SwapQuoteLimitOrder | SwapQuoteRfqOrder;
/**
* takerAssetFillAmount: The amount of takerAsset sold for makerAsset.
@@ -460,8 +479,6 @@ export interface SamplerCallResult {
data: string;
}
export type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
export enum AltQuoteModel {
Firm = 'firm',
Indicative = 'indicative',

View File

@@ -2,6 +2,7 @@ import { ChainId, getContractAddressesForChainOrThrow } from '@0x/contract-addre
import { BigNumber } from '@0x/utils';
import { TokenAdjacencyGraphBuilder } from '../token_adjacency_graph_builder';
import { valueByChainId } from '../utils';
import { SourceFilters } from './source_filters';
import {
@@ -23,24 +24,6 @@ export const NULL_BYTES = '0x';
export const NULL_ADDRESS = '0x0000000000000000000000000000000000000000';
export const COMPARISON_PRICE_DECIMALS = 10;
// TODO(kimpers): Consolidate this implementation with the one in @0x/token-metadata
function valueByChainId<T>(rest: Partial<{ [key in ChainId]: T }>, defaultValue: T): { [key in ChainId]: T } {
// TODO I don't like this but iterating through enums is weird
return {
[ChainId.Mainnet]: defaultValue,
[ChainId.Ropsten]: defaultValue,
[ChainId.Rinkeby]: defaultValue,
[ChainId.Kovan]: defaultValue,
[ChainId.Ganache]: defaultValue,
[ChainId.BSC]: defaultValue,
[ChainId.Polygon]: defaultValue,
[ChainId.PolygonMumbai]: defaultValue,
[ChainId.Avalanche]: defaultValue,
[ChainId.Fantom]: defaultValue,
...(rest || {}),
};
}
/**
* Valid sources for market sell.
*/
@@ -289,22 +272,6 @@ export const FEE_QUOTE_SOURCES_BY_CHAIN_ID = valueByChainId<ERC20BridgeSource[]>
[],
);
export const CURVE_LIQUIDITY_PROVIDER_BY_CHAIN_ID = valueByChainId<string>(
{
[ChainId.Mainnet]: '0x561b94454b65614ae3db0897b74303f4acf7cc75',
[ChainId.Ropsten]: '0xae241c6fc7f28f6dc0cb58b4112ba7f63fcaf5e2',
},
NULL_ADDRESS,
);
export const MOONISWAP_LIQUIDITY_PROVIDER_BY_CHAIN_ID = valueByChainId<string>(
{
[ChainId.Mainnet]: '0xa2033d6ba88756ce6a87584d69dc87bda9a4f889',
[ChainId.Ropsten]: '0x87e0393aee0fb8c10b8653c6507c182264fe5a34',
},
NULL_ADDRESS,
);
// HACK(mzhu25): Limit and RFQ orders need to be treated as different sources
// when computing the exchange proxy gas overhead.
export const SOURCE_FLAGS: { [key in ERC20BridgeSource]: bigint } & {

View File

@@ -172,7 +172,7 @@ export function dexSamplesToFills(
for (let i = 0; i < nonzeroSamples.length; i++) {
const sample = nonzeroSamples[i];
const prevSample = i === 0 ? undefined : nonzeroSamples[i - 1];
const { source, encodedFillData } = sample;
const { source, encodedFillData, metadata } = sample;
const input = sample.input.minus(prevSample ? prevSample.input : 0);
const output = sample.output.minus(prevSample ? prevSample.output : 0);
const fee = gasPrice.times(sample.gasCost);
@@ -195,12 +195,15 @@ export function dexSamplesToFills(
output,
adjustedOutput,
source,
encodedFillData,
type: FillQuoteTransformerOrderType.Bridge,
gasCost: sample.gasCost,
index: i,
parent: i !== 0 ? fills[fills.length - 1] : undefined,
flags: SOURCE_FLAGS[source],
data: {
...metadata,
encodedFillData,
},
});
}
return fills;

View File

@@ -4,10 +4,10 @@ import { Address, MarketOperation } from '../../types';
import {
AggregationError,
CollapsedFill,
CollapsedGenericBridgeFill,
ERC20BridgeSource,
NativeCollapsedFill,
OptimizedBridgeOrder,
CollapsedNativeOrderFill,
OptimizedGenericBridgeOrder,
OptimizedLimitOrder,
OptimizedRfqOrder,
} from './types';
@@ -131,22 +131,23 @@ export function getErc20BridgeSourceToBridgeSource(source: ERC20BridgeSource): s
}
export function createBridgeOrder(
fill: CollapsedFill,
fill: CollapsedGenericBridgeFill,
inputToken: Address,
outputToken: Address,
): OptimizedBridgeOrder {
): OptimizedGenericBridgeOrder {
return {
inputToken,
outputToken,
inputAmount: fill.input,
outputAmount: fill.output,
encodedFillData: fill.encodedFillData,
fillData: fill.data,
source: fill.source,
sourcePathId: fill.sourcePathId,
type: FillQuoteTransformerOrderType.Bridge,
fills: [fill],
gasCost: fill.gasCost,
isFallback: fill.isFallback,
...((fill as any).metadata !== undefined ? { metadata: (fill as any).metadata } : {}),
};
}
@@ -157,7 +158,7 @@ export function getMakerTakerTokens(side: MarketOperation, inputToken: Address,
}
export function createNativeOptimizedOrder(
fill: NativeCollapsedFill,
fill: CollapsedNativeOrderFill,
side: MarketOperation,
): OptimizedLimitOrder | OptimizedRfqOrder {
throw new Error(`No implementado`);

View File

@@ -7,11 +7,13 @@ import { ethToOutputAmount } from './fills';
import { createBridgeOrder, createNativeOptimizedOrder } from './orders';
import { getCompleteRate, getRate } from './rate_utils';
import {
BridgeFill,
CollapsedGenericBridgeFill,
CollapsedFill,
CollapsedNativeOrderFill,
ERC20BridgeSource,
ExchangeProxyOverhead,
Fill,
NativeCollapsedFill,
OptimizedOrder,
} from './types';
@@ -119,11 +121,11 @@ export class Path {
this.orders = [];
for (let i = 0; i < collapsedFills.length; ++i) {
if (collapsedFills[i].source === ERC20BridgeSource.Native) {
this.orders.push(createNativeOptimizedOrder(collapsedFills[i] as NativeCollapsedFill, opts.side));
this.orders.push(createNativeOptimizedOrder(collapsedFills[i] as CollapsedNativeOrderFill, opts.side));
continue;
}
this.orders.push(createBridgeOrder(
collapsedFills[i],
collapsedFills[i] as CollapsedGenericBridgeFill,
opts.inputToken,
opts.outputToken,
));
@@ -251,7 +253,7 @@ export class Path {
if (prevFill.sourcePathId === fill.sourcePathId) {
prevFill.input = prevFill.input.plus(fill.input);
prevFill.output = prevFill.output.plus(fill.output);
prevFill.encodedFillData = fill.encodedFillData;
prevFill.data = fill.data;
prevFill.subFills.push(fill);
prevFill.gasCost;
continue;
@@ -261,7 +263,7 @@ export class Path {
sourcePathId: fill.sourcePathId,
source: fill.source,
type: fill.type,
encodedFillData: fill.encodedFillData,
data: fill.data,
input: fill.input,
output: fill.output,
subFills: [fill],

View File

@@ -78,6 +78,7 @@ export class SamplerClient implements Sampler {
input: pt.sellAmount,
output: pt.buyAmount,
encodedFillData: pt.encodedFillData,
metadata: pt.metadata,
gasCost: pt.gasCost,
source: liq.source,
}) as DexSample),

View File

@@ -9,12 +9,16 @@ export interface LiquidityCurvePoint {
sellAmount: BigNumber;
buyAmount: BigNumber;
encodedFillData: Bytes;
metadata: object;
gasCost: number;
}
type RpcLiquidityCurvePoint = Omit<Omit<LiquidityCurvePoint, 'sellAmount'>, 'buyAmount'> & {
interface RpcLiquidityCurvePoint {
sellAmount: DecimalString;
buyAmount: DecimalString;
encodedFillData: Bytes;
jsonMetadata: string;
gasCost: number;
}
export interface LiquidityRequest {
@@ -95,6 +99,7 @@ export class SamplerServiceRpcClient {
...c,
buyAmount: new BigNumber(c.buyAmount),
sellAmount: new BigNumber(c.sellAmount),
metadata: decodeMetadata(c.jsonMetadata),
}))),
}));
}
@@ -115,6 +120,7 @@ export class SamplerServiceRpcClient {
...c,
buyAmount: new BigNumber(c.buyAmount),
sellAmount: new BigNumber(c.sellAmount),
metadata: decodeMetadata(c.jsonMetadata),
}))),
}));
}
@@ -134,3 +140,29 @@ export class SamplerServiceRpcClient {
);
}
}
function decodeMetadata(jsonMetadata: string): any {
if (!jsonMetadata) {
return undefined;
}
return unmarshallMetadata(JSON.parse(jsonMetadata));
}
function unmarshallMetadata(v: any): any {
switch (typeof(v)) {
case 'string':
if (/^\d+n$/.test(v)) {
return new BigNumber(v.slice(0, -1));
}
return v;
case 'object':
if (Array.isArray(v)) {
return v.map(v => unmarshallMetadata(v));
}
return Object.assign(
{},
...Object.entries(v).map(([k, v]) => ({ [k]: v})),
);
}
return v;
}

View File

@@ -166,11 +166,44 @@ export interface BalancerV2PoolInfo {
export interface DexSample {
source: ERC20BridgeSource;
encodedFillData: Bytes;
metadata?: any;
input: BigNumber;
output: BigNumber;
gasCost: number;
}
export interface BridgeFillData {
encodedFillData: Bytes;
}
export interface UniswapV2FillData extends BridgeFillData {
tokenAddressPath: Address[];
}
export interface UniswapV3FillData extends BridgeFillData {
encodedPath: Bytes;
}
export interface LiquidityProviderFillData extends BridgeFillData {
poolAddress: Address;
}
export interface CurveFillData extends BridgeFillData {
poolAddress: Address;
exchangeFunctionSelector: Bytes;
fromTokenIdx: number;
toTokenIdx: number;
}
export interface MooniswapFillData extends BridgeFillData {
poolAddress: Address;
}
export interface NativeOrderFillData {
type: FillQuoteTransformerOrderType.Limit | FillQuoteTransformerOrderType.Rfq;
orderInfo: FillQuoteTransformerLimitOrderInfo;
}
/**
* Represents a node on a fill path.
*/
@@ -179,7 +212,7 @@ export interface Fill {
source: ERC20BridgeSource;
// TODO jacob people seem to agree that orderType here is more readable
type: FillQuoteTransformerOrderType; // should correspond with TFillData
encodedFillData: Bytes;
data?: any;
// 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).
@@ -200,13 +233,33 @@ export interface Fill {
gasCost: number;
}
export interface BridgeFill<TData extends BridgeFillData> extends Fill {
data: TData;
}
export interface GenericBridgeFill extends BridgeFill<BridgeFillData> {}
export interface UniswapV2BridgeFill extends BridgeFill<UniswapV2FillData> {}
export interface UniswapV3BridgeFill extends BridgeFill<UniswapV3FillData> {}
export interface LiquidityProviderBridgeFill extends BridgeFill<LiquidityProviderFillData> {}
export interface CurveBridgeFill extends BridgeFill<CurveFillData> {}
export interface MooniswapBridgeFill extends BridgeFill<MooniswapFillData> {}
export interface NativeOrderFill extends Fill {
data: NativeOrderFillData;
}
/**
* Represents continguous fills on a path that have been merged together.
*/
export interface CollapsedFill {
source: ERC20BridgeSource;
type: FillQuoteTransformerOrderType; // should correspond with TFillData
encodedFillData: Bytes;
data?: any;
// 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).
@@ -230,14 +283,29 @@ export interface CollapsedFill {
isFallback: boolean;
}
/**
* A `CollapsedFill` wrapping a native order.
*/
export interface NativeCollapsedFill extends CollapsedFill {}
export interface CollapsedBridgeFill<TData extends BridgeFillData> extends CollapsedFill {
data: TData;
}
export interface OptimizedOrderBase {
export interface CollapsedGenericBridgeFill extends CollapsedBridgeFill<BridgeFillData> {}
export interface CollapsedUniswapV2BridgeFill extends CollapsedBridgeFill<UniswapV2FillData> {}
export interface CollapsedUniswapV3BridgeFill extends CollapsedBridgeFill<UniswapV3FillData> {}
export interface CollapsedLiquidityProviderBridgeFill extends CollapsedBridgeFill<LiquidityProviderFillData> {}
export interface CollapsedCurveBridgeFill extends CollapsedBridgeFill<CurveFillData> {}
export interface CollapsedMooniswapBridgeFill extends CollapsedBridgeFill<MooniswapFillData> {}
export interface CollapsedNativeOrderFill extends CollapsedFill {
data: NativeOrderFillData;
}
export interface OptimizedOrder {
source: ERC20BridgeSource;
type: FillQuoteTransformerOrderType; // should correspond with TFillData
type: FillQuoteTransformerOrderType;
inputToken: string;
outputToken: string;
gasCost: number;
@@ -245,32 +313,29 @@ export interface OptimizedOrderBase {
outputAmount: BigNumber;
fills: CollapsedFill[];
isFallback: boolean;
fillData: any;
}
export interface OptimizedBridgeOrder extends OptimizedOrderBase {
export interface OptimizedBridgeOrder<TFillData extends BridgeFillData> extends OptimizedOrder {
type: FillQuoteTransformerOrderType.Bridge;
sourcePathId: string;
encodedFillData: Bytes;
fillData: TFillData;
}
export interface OptimizedLimitOrder extends OptimizedOrderBase {
export interface OptimizedGenericBridgeOrder extends OptimizedBridgeOrder<BridgeFillData> {}
export interface OptimizedUniswapV2BridgeOrder extends OptimizedBridgeOrder<UniswapV2FillData> {}
export interface OptimizedLimitOrder extends OptimizedOrder {
type: FillQuoteTransformerOrderType.Limit;
orderInfo: FillQuoteTransformerLimitOrderInfo;
fillData: Omit<NativeOrderFillData, 'type'>;
}
export interface OptimizedRfqOrder extends OptimizedOrderBase {
export interface OptimizedRfqOrder extends OptimizedOrder {
type: FillQuoteTransformerOrderType.Rfq;
orderInfo: FillQuoteTransformerRfqOrderInfo;
fillData: Omit<NativeOrderFillData, 'type'>;
}
/**
* Optimized orders to fill.
*/
export type OptimizedOrder =
| OptimizedBridgeOrder
| OptimizedRfqOrder
| OptimizedLimitOrder;
export interface GetMarketOrdersRfqOpts extends RfqRequestOpts {
quoteRequestor?: QuoteRequestor;
firmQuoteValidator?: RfqFirmQuoteValidator;

View File

@@ -8,7 +8,7 @@ import {
CollapsedFill,
DexSample,
ERC20BridgeSource,
NativeCollapsedFill,
CollapsedNativeOrderFill,
} from './market_operation_utils/types';
import { QuoteRequestor } from './quote_requestor';
@@ -183,7 +183,7 @@ export function multiHopSampleToReportSource(
// }
}
function _isNativeOrderFromCollapsedFill(cf: CollapsedFill): cf is NativeCollapsedFill {
function _isNativeOrderFromCollapsedFill(cf: CollapsedFill): cf is CollapsedNativeOrderFill {
const { type } = cf;
return type === FillQuoteTransformerOrderType.Limit || type === FillQuoteTransformerOrderType.Rfq;
}

View File

@@ -220,7 +220,7 @@ function createBestCaseFillOrderCalls(quoteInfo: QuoteFillInfo): QuoteFillOrderC
totalOrderInputFee:
o.type === FillQuoteTransformerOrderType.Limit
? getNativeAdjustedTakerFeeAmount(
(o as SwapQuoteLimitOrder).orderInfo.order,
(o as SwapQuoteLimitOrder).fillData.orderInfo.order,
o.takerAmount,
)
: ZERO_AMOUNT,

View File

@@ -1,3 +1,4 @@
import { ChainId } from '@0x/contract-addresses';
import { CommonOrderFields, FillQuoteTransformerOrderType, LimitOrderFields } from '@0x/protocol-utils';
import { BigNumber } from '@0x/utils';
import { Web3Wrapper } from '@0x/web3-wrapper';
@@ -98,3 +99,21 @@ export function getNativeAdjustedFillableAmountsFromMakerAmount(
: ZERO_AMOUNT,
};
}
// TODO(kimpers): Consolidate this implementation with the one in @0x/token-metadata
export function valueByChainId<T>(rest: Partial<{ [key in ChainId]: T }>, defaultValue: T): { [key in ChainId]: T } {
// TODO I don't like this but iterating through enums is weird
return {
[ChainId.Mainnet]: defaultValue,
[ChainId.Ropsten]: defaultValue,
[ChainId.Rinkeby]: defaultValue,
[ChainId.Kovan]: defaultValue,
[ChainId.Ganache]: defaultValue,
[ChainId.BSC]: defaultValue,
[ChainId.Polygon]: defaultValue,
[ChainId.PolygonMumbai]: defaultValue,
[ChainId.Avalanche]: defaultValue,
[ChainId.Fantom]: defaultValue,
...(rest || {}),
};
}