379 lines
10 KiB
TypeScript
379 lines
10 KiB
TypeScript
import {
|
|
BigNumber,
|
|
ERC20BridgeSource,
|
|
FillData,
|
|
FillQuoteTransformerOrderType,
|
|
UniswapV2FillData,
|
|
} from '../../../src/asset-swapper';
|
|
import {
|
|
AffiliateFeeAmount,
|
|
AffiliateFeeType,
|
|
FillBase,
|
|
IPath,
|
|
MarketBuySwapQuote,
|
|
MarketOperation,
|
|
MarketSellSwapQuote,
|
|
OptimizedMarketBridgeOrder,
|
|
OptimizedOrdersByType,
|
|
SwapQuote,
|
|
TwoHopOrder,
|
|
} from '../../../src/asset-swapper/types';
|
|
import { MAX_UINT256 } from '../../../src/asset-swapper/utils/market_operation_utils/constants';
|
|
import { DODOFillData, VelodromeFillData } from '../../../src/asset-swapper/utils/market_operation_utils/types';
|
|
import { NULL_ADDRESS } from '../../constants';
|
|
import { randomAddress } from '../../utils/random';
|
|
|
|
export const ONE_ETHER = new BigNumber(1e18);
|
|
|
|
export const NO_AFFILIATE_FEE: AffiliateFeeAmount = {
|
|
feeType: AffiliateFeeType.None,
|
|
recipient: NULL_ADDRESS,
|
|
buyTokenFeeAmount: new BigNumber(0),
|
|
sellTokenFeeAmount: new BigNumber(0),
|
|
};
|
|
|
|
const testConstants = {
|
|
UNISWAP_V2_ROUTER: randomAddress(),
|
|
SUSHI_SWAP_ROUTER: randomAddress(),
|
|
VELODROME_ROUTER: randomAddress(),
|
|
DODO_POOL_ADDRESS: randomAddress(),
|
|
DODO_HELPER_ADDRESS: randomAddress(),
|
|
};
|
|
|
|
function createFillData({
|
|
source,
|
|
takerToken,
|
|
makerToken,
|
|
}: {
|
|
source: ERC20BridgeSource;
|
|
takerToken: string;
|
|
makerToken: string;
|
|
}): FillData {
|
|
switch (source) {
|
|
case ERC20BridgeSource.UniswapV2:
|
|
case ERC20BridgeSource.SushiSwap: {
|
|
const fillData: UniswapV2FillData = {
|
|
tokenAddressPath: [takerToken, makerToken],
|
|
router:
|
|
source === ERC20BridgeSource.UniswapV2
|
|
? testConstants.UNISWAP_V2_ROUTER
|
|
: testConstants.SUSHI_SWAP_ROUTER,
|
|
};
|
|
return fillData;
|
|
}
|
|
case ERC20BridgeSource.Dodo: {
|
|
const fillData: DODOFillData = {
|
|
poolAddress: testConstants.DODO_POOL_ADDRESS,
|
|
isSellBase: false,
|
|
helperAddress: testConstants.DODO_HELPER_ADDRESS,
|
|
};
|
|
return fillData;
|
|
}
|
|
case ERC20BridgeSource.Velodrome: {
|
|
const fillData: VelodromeFillData = {
|
|
router: testConstants.VELODROME_ROUTER,
|
|
stable: false,
|
|
};
|
|
return fillData;
|
|
}
|
|
|
|
default:
|
|
throw new Error(`createFillData: unimplemented source: ${source}`);
|
|
}
|
|
}
|
|
|
|
interface BridgeOrderParams {
|
|
source: ERC20BridgeSource;
|
|
takerToken: string;
|
|
makerToken: string;
|
|
takerAmount: BigNumber;
|
|
makerAmount: BigNumber;
|
|
}
|
|
|
|
function createBridgeOrder({
|
|
source,
|
|
takerToken,
|
|
makerToken,
|
|
takerAmount,
|
|
makerAmount,
|
|
}: BridgeOrderParams): OptimizedMarketBridgeOrder {
|
|
return {
|
|
source,
|
|
takerToken,
|
|
makerToken,
|
|
takerAmount,
|
|
makerAmount,
|
|
type: FillQuoteTransformerOrderType.Bridge,
|
|
fillData: createFillData({ source, takerToken, makerToken }),
|
|
// Currently unused by the tests that depends.
|
|
fill: undefined as unknown as FillBase,
|
|
};
|
|
}
|
|
|
|
interface TwoHopOrderParams {
|
|
takerToken: string;
|
|
intermediateToken: string;
|
|
makerToken: string;
|
|
firstHopSource: ERC20BridgeSource;
|
|
secondHopSource: ERC20BridgeSource;
|
|
takerAmount: BigNumber;
|
|
makerAmount: BigNumber;
|
|
}
|
|
function createTwoHopOrder({
|
|
takerToken,
|
|
intermediateToken,
|
|
makerAmount,
|
|
firstHopSource,
|
|
secondHopSource,
|
|
takerAmount,
|
|
makerToken,
|
|
}: TwoHopOrderParams): TwoHopOrder {
|
|
const firstHopOrder = createBridgeOrder({
|
|
source: firstHopSource,
|
|
takerToken,
|
|
makerToken: intermediateToken,
|
|
takerAmount,
|
|
makerAmount: new BigNumber(0),
|
|
});
|
|
|
|
const secondHopOrder = createBridgeOrder({
|
|
source: secondHopSource,
|
|
takerToken: intermediateToken,
|
|
makerToken,
|
|
takerAmount: MAX_UINT256,
|
|
makerAmount,
|
|
});
|
|
return { firstHopOrder, secondHopOrder };
|
|
}
|
|
|
|
function createPathFromOrders(partialOrdersByType: Partial<OptimizedOrdersByType>): IPath {
|
|
const ordersByType = {
|
|
bridgeOrders: [],
|
|
nativeOrders: [],
|
|
twoHopOrders: [],
|
|
...partialOrdersByType,
|
|
};
|
|
const { bridgeOrders, nativeOrders, twoHopOrders } = ordersByType;
|
|
const twoHopIndividualOrders = twoHopOrders.flatMap(({ firstHopOrder, secondHopOrder }) => [
|
|
firstHopOrder,
|
|
secondHopOrder,
|
|
]);
|
|
0;
|
|
const orders = [...bridgeOrders, ...nativeOrders, ...twoHopIndividualOrders];
|
|
return {
|
|
getOrders: () => orders,
|
|
getSlippedOrders: () => orders,
|
|
getOrdersByType: () => ordersByType,
|
|
getSlippedOrdersByType: () => ordersByType,
|
|
hasTwoHop: () => ordersByType.twoHopOrders.length > 0,
|
|
};
|
|
}
|
|
|
|
interface CreatePathParams {
|
|
bridgeOrderParams?: BridgeOrderParams[];
|
|
twoHopOrderParams?: TwoHopOrderParams[];
|
|
// TODO: support native
|
|
}
|
|
|
|
function createPath(params: CreatePathParams): IPath {
|
|
const bridgeOrderParams = params.bridgeOrderParams || [];
|
|
const twoHopOrderParams = params.twoHopOrderParams || [];
|
|
|
|
return createPathFromOrders({
|
|
bridgeOrders: bridgeOrderParams.map(createBridgeOrder),
|
|
twoHopOrders: twoHopOrderParams.map(createTwoHopOrder),
|
|
});
|
|
}
|
|
|
|
export function createSwapQuote({
|
|
side,
|
|
takerToken,
|
|
makerToken,
|
|
takerAmount,
|
|
makerAmount,
|
|
createPathParams,
|
|
slippage,
|
|
takerAmountPerEth,
|
|
makerAmountPerEth,
|
|
gasPrice,
|
|
}: {
|
|
side: MarketOperation;
|
|
takerToken: string;
|
|
makerToken: string;
|
|
takerAmount: BigNumber;
|
|
makerAmount: BigNumber;
|
|
createPathParams: CreatePathParams;
|
|
slippage?: number;
|
|
takerAmountPerEth?: BigNumber;
|
|
makerAmountPerEth?: BigNumber;
|
|
gasPrice?: number;
|
|
}): SwapQuote {
|
|
// NOTES: consider using `slippage` to generate different amounts for `worstCaseQuoteInfo` and
|
|
// slipped orders of `path`.
|
|
return {
|
|
...(side === MarketOperation.Buy
|
|
? { type: MarketOperation.Buy, makerTokenFillAmount: makerAmount }
|
|
: { type: MarketOperation.Sell, takerTokenFillAmount: takerAmount }),
|
|
takerToken,
|
|
makerToken,
|
|
gasPrice: new BigNumber(gasPrice || 0),
|
|
path: createPath(createPathParams),
|
|
bestCaseQuoteInfo: {
|
|
takerAmount,
|
|
makerAmount,
|
|
totalTakerAmount: takerAmount,
|
|
protocolFeeInWeiAmount: new BigNumber(0),
|
|
gas: 42,
|
|
slippage: 0,
|
|
},
|
|
worstCaseQuoteInfo: {
|
|
takerAmount,
|
|
makerAmount,
|
|
totalTakerAmount: takerAmount,
|
|
protocolFeeInWeiAmount: new BigNumber(0),
|
|
gas: 42,
|
|
slippage: slippage || 0,
|
|
},
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
sourceBreakdown: {} as any,
|
|
makerTokenDecimals: 18,
|
|
takerTokenDecimals: 18,
|
|
takerAmountPerEth: takerAmountPerEth || new BigNumber(0),
|
|
makerAmountPerEth: makerAmountPerEth || new BigNumber(0),
|
|
blockNumber: 424242,
|
|
samplerGasUsage: 1_000_000,
|
|
};
|
|
}
|
|
|
|
function createSimpleSwapQuoteWithBridgeOrder({
|
|
side,
|
|
source,
|
|
takerToken,
|
|
makerToken,
|
|
takerAmount,
|
|
makerAmount,
|
|
slippage,
|
|
takerAmountPerEth,
|
|
makerAmountPerEth,
|
|
gasPrice,
|
|
}: {
|
|
side: MarketOperation;
|
|
source: ERC20BridgeSource;
|
|
takerToken: string;
|
|
makerToken: string;
|
|
takerAmount: BigNumber;
|
|
makerAmount: BigNumber;
|
|
slippage: number;
|
|
takerAmountPerEth?: BigNumber;
|
|
makerAmountPerEth?: BigNumber;
|
|
gasPrice?: number;
|
|
}): SwapQuote {
|
|
return createSwapQuote({
|
|
side,
|
|
takerToken,
|
|
makerToken,
|
|
takerAmount,
|
|
makerAmount,
|
|
createPathParams: {
|
|
bridgeOrderParams: [
|
|
{
|
|
takerToken,
|
|
makerToken,
|
|
takerAmount,
|
|
makerAmount,
|
|
source,
|
|
},
|
|
],
|
|
},
|
|
slippage,
|
|
takerAmountPerEth,
|
|
makerAmountPerEth,
|
|
gasPrice,
|
|
});
|
|
}
|
|
|
|
export function createSimpleSellSwapQuoteWithBridgeOrder(params: {
|
|
source: ERC20BridgeSource;
|
|
takerToken: string;
|
|
makerToken: string;
|
|
takerAmount: BigNumber;
|
|
makerAmount: BigNumber;
|
|
slippage: number;
|
|
takerAmountPerEth?: BigNumber;
|
|
makerAmountPerEth?: BigNumber;
|
|
gasPrice?: number;
|
|
}): MarketSellSwapQuote {
|
|
return createSimpleSwapQuoteWithBridgeOrder({
|
|
side: MarketOperation.Sell,
|
|
...params,
|
|
}) as MarketSellSwapQuote;
|
|
}
|
|
|
|
export function createSimpleBuySwapQuoteWithBridgeOrder(params: {
|
|
source: ERC20BridgeSource;
|
|
takerToken: string;
|
|
makerToken: string;
|
|
takerAmount: BigNumber;
|
|
makerAmount: BigNumber;
|
|
slippage: number;
|
|
takerAmountPerEth?: BigNumber;
|
|
makerAmountPerEth?: BigNumber;
|
|
gasPrice?: number;
|
|
}): MarketBuySwapQuote {
|
|
return createSimpleSwapQuoteWithBridgeOrder({
|
|
side: MarketOperation.Buy,
|
|
...params,
|
|
}) as MarketBuySwapQuote;
|
|
}
|
|
|
|
export function createTwoHopSellQuote({
|
|
takerToken,
|
|
intermediateToken,
|
|
makerToken,
|
|
firstHopSource,
|
|
secondHopSource,
|
|
takerAmount,
|
|
makerAmount,
|
|
slippage,
|
|
takerAmountPerEth,
|
|
makerAmountPerEth,
|
|
gasPrice,
|
|
}: {
|
|
takerToken: string;
|
|
intermediateToken: string;
|
|
makerToken: string;
|
|
firstHopSource: ERC20BridgeSource;
|
|
secondHopSource: ERC20BridgeSource;
|
|
takerAmount: BigNumber;
|
|
makerAmount: BigNumber;
|
|
slippage?: number;
|
|
takerAmountPerEth?: BigNumber;
|
|
makerAmountPerEth?: BigNumber;
|
|
gasPrice?: number;
|
|
}): MarketSellSwapQuote {
|
|
return createSwapQuote({
|
|
side: MarketOperation.Sell,
|
|
takerToken,
|
|
makerToken,
|
|
takerAmount,
|
|
makerAmount,
|
|
createPathParams: {
|
|
twoHopOrderParams: [
|
|
{
|
|
takerToken,
|
|
intermediateToken,
|
|
makerToken,
|
|
firstHopSource,
|
|
secondHopSource,
|
|
takerAmount,
|
|
makerAmount,
|
|
},
|
|
],
|
|
},
|
|
slippage,
|
|
takerAmountPerEth,
|
|
makerAmountPerEth,
|
|
gasPrice,
|
|
}) as MarketSellSwapQuote;
|
|
}
|