Skip to content

Commit

Permalink
Merge branch 'swap-endpoint' into fix/swap-fees-calculation
Browse files Browse the repository at this point in the history
  • Loading branch information
gsteenkamp89 committed Dec 19, 2024
2 parents fe0a840 + 28b6648 commit c891687
Show file tree
Hide file tree
Showing 13 changed files with 476 additions and 250 deletions.
113 changes: 62 additions & 51 deletions api/_dexes/cross-swap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import {
isOutputTokenBridgeable,
getBridgeQuoteForMinOutput,
getSpokePool,
Profiler,
} from "../_utils";
import {
getBestUniswapCrossSwapQuotesForOutputA2A,
Expand All @@ -21,6 +20,7 @@ import { CrossSwap, CrossSwapQuotes } from "./types";
import {
buildExactOutputBridgeTokenMessage,
buildMinOutputBridgeTokenMessage,
getUniversalSwapAndBridge,
} from "./utils";
import { getSpokePoolPeriphery } from "../_spoke-pool-periphery";
import { tagIntegratorId } from "../_integrator-id";
Expand All @@ -32,8 +32,6 @@ export type CrossSwapType =

export type AmountType = (typeof AMOUNT_TYPE)[keyof typeof AMOUNT_TYPE];

export type LeftoverType = (typeof LEFTOVER_TYPE)[keyof typeof LEFTOVER_TYPE];

export const AMOUNT_TYPE = {
EXACT_INPUT: "exactInput",
EXACT_OUTPUT: "exactOutput",
Expand All @@ -47,15 +45,11 @@ export const CROSS_SWAP_TYPE = {
ANY_TO_ANY: "anyToAny",
} as const;

export const LEFTOVER_TYPE = {
OUTPUT_TOKEN: "outputToken",
BRIDGEABLE_TOKEN: "bridgeableToken",
} as const;

export const PREFERRED_BRIDGE_TOKENS = ["WETH"];

const defaultQuoteFetchStrategy: UniswapQuoteFetchStrategy =
getSwapRouter02Strategy();
// This will be our default strategy until the periphery contract is audited
getSwapRouter02Strategy("UniversalSwapAndBridge");
const strategyOverrides = {
[CHAIN_IDs.BLAST]: defaultQuoteFetchStrategy,
};
Expand All @@ -79,47 +73,28 @@ export async function getCrossSwapQuotes(
}

export async function getCrossSwapQuotesForOutput(crossSwap: CrossSwap) {
const profiler = new Profiler({
at: "api/cross-swap#getCrossSwapQuotesForOutput",
logger: console,
});
const crossSwapType = getCrossSwapType({
inputToken: crossSwap.inputToken.address,
originChainId: crossSwap.inputToken.chainId,
outputToken: crossSwap.outputToken.address,
destinationChainId: crossSwap.outputToken.chainId,
isInputNative: Boolean(crossSwap.isInputNative),
});

if (crossSwapType === CROSS_SWAP_TYPE.BRIDGEABLE_TO_BRIDGEABLE) {
return profiler.measureAsync(
getCrossSwapQuotesForOutputB2B(crossSwap),
"getCrossSwapQuotesForOutputB2B",
crossSwap
);
return getCrossSwapQuotesForOutputB2B(crossSwap);
}

if (crossSwapType === CROSS_SWAP_TYPE.BRIDGEABLE_TO_ANY) {
return profiler.measureAsync(
getCrossSwapQuotesForOutputB2A(crossSwap),
"getCrossSwapQuotesForOutputB2A",
crossSwap
);
return getCrossSwapQuotesForOutputB2A(crossSwap);
}

if (crossSwapType === CROSS_SWAP_TYPE.ANY_TO_BRIDGEABLE) {
return profiler.measureAsync(
getCrossSwapQuotesForOutputA2B(crossSwap),
"getCrossSwapQuotesForOutputA2B",
crossSwap
);
return getCrossSwapQuotesForOutputA2B(crossSwap);
}

if (crossSwapType === CROSS_SWAP_TYPE.ANY_TO_ANY) {
return profiler.measureAsync(
getCrossSwapQuotesForOutputA2A(crossSwap),
"getCrossSwapQuotesForOutputA2A",
crossSwap
);
return getCrossSwapQuotesForOutputA2A(crossSwap);
}

throw new Error("Invalid cross swap type");
Expand Down Expand Up @@ -194,6 +169,7 @@ export function getCrossSwapType(params: {
originChainId: number;
outputToken: string;
destinationChainId: number;
isInputNative: boolean;
}): CrossSwapType {
if (
isRouteEnabled(
Expand All @@ -206,6 +182,20 @@ export function getCrossSwapType(params: {
return CROSS_SWAP_TYPE.BRIDGEABLE_TO_BRIDGEABLE;
}

// Prefer destination swap if input token is native because legacy
// `UniversalSwapAndBridge` does not support native tokens as input.
if (params.isInputNative) {
if (isInputTokenBridgeable(params.inputToken, params.originChainId)) {
return CROSS_SWAP_TYPE.BRIDGEABLE_TO_ANY;
}
// We can't bridge native tokens that are not ETH, e.g. MATIC or AZERO. Therefore
// throw until we have periphery contract audited so that it can accept native
// tokens and do an origin swap.
throw new Error(
"Unsupported swap: Input token is native but not bridgeable"
);
}

if (isOutputTokenBridgeable(params.outputToken, params.destinationChainId)) {
return CROSS_SWAP_TYPE.ANY_TO_BRIDGEABLE;
}
Expand All @@ -230,24 +220,45 @@ export async function buildCrossSwapTxForAllowanceHolder(
let toAddress: string;

if (crossSwapQuotes.originSwapQuote) {
const spokePoolPeriphery = getSpokePoolPeriphery(
crossSwapQuotes.originSwapQuote.peripheryAddress,
originChainId
);
tx = await spokePoolPeriphery.populateTransaction.swapAndBridge(
crossSwapQuotes.originSwapQuote.tokenIn.address,
crossSwapQuotes.originSwapQuote.tokenOut.address,
crossSwapQuotes.originSwapQuote.swapTx.data,
crossSwapQuotes.originSwapQuote.maximumAmountIn,
crossSwapQuotes.originSwapQuote.minAmountOut,
deposit,
{
value: crossSwapQuotes.crossSwap.isInputNative
? crossSwapQuotes.originSwapQuote.maximumAmountIn
: 0,
}
);
toAddress = spokePoolPeriphery.address;
const { entryPointContract } = crossSwapQuotes.originSwapQuote;
if (entryPointContract.name === "SpokePoolPeriphery") {
const spokePoolPeriphery = getSpokePoolPeriphery(
entryPointContract.address,
originChainId
);
tx = await spokePoolPeriphery.populateTransaction.swapAndBridge(
crossSwapQuotes.originSwapQuote.tokenIn.address,
crossSwapQuotes.originSwapQuote.tokenOut.address,
crossSwapQuotes.originSwapQuote.swapTx.data,
crossSwapQuotes.originSwapQuote.maximumAmountIn,
crossSwapQuotes.originSwapQuote.minAmountOut,
deposit,
{
value: crossSwapQuotes.crossSwap.isInputNative
? crossSwapQuotes.originSwapQuote.maximumAmountIn
: 0,
}
);
toAddress = spokePoolPeriphery.address;
} else if (entryPointContract.name === "UniversalSwapAndBridge") {
const universalSwapAndBridge = getUniversalSwapAndBridge(
entryPointContract.dex,
originChainId
);
tx = await universalSwapAndBridge.populateTransaction.swapAndBridge(
crossSwapQuotes.originSwapQuote.tokenIn.address,
crossSwapQuotes.originSwapQuote.tokenOut.address,
crossSwapQuotes.originSwapQuote.swapTx.data,
crossSwapQuotes.originSwapQuote.maximumAmountIn,
crossSwapQuotes.originSwapQuote.minAmountOut,
deposit
);
toAddress = universalSwapAndBridge.address;
} else {
throw new Error(
`Could not build cross swap tx for unknown entry point contract`
);
}
} else {
tx = await spokePool.populateTransaction.depositV3(
deposit.depositor,
Expand Down
16 changes: 13 additions & 3 deletions api/_dexes/types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { BigNumber } from "ethers";
import { getSuggestedFees } from "../_utils";
import { AmountType, CrossSwapType, LeftoverType } from "./cross-swap";
import { AmountType, CrossSwapType } from "./cross-swap";

export type { AmountType, CrossSwapType };

Expand Down Expand Up @@ -31,7 +31,6 @@ export type CrossSwap = {
recipient: string;
slippageTolerance: number;
type: AmountType;
leftoverType?: LeftoverType;
refundOnOrigin: boolean;
refundAddress?: string;
isInputNative?: boolean;
Expand Down Expand Up @@ -77,10 +76,21 @@ export type CrossSwapQuotes = {
};
destinationSwapQuote?: SwapQuote;
originSwapQuote?: SwapQuote & {
peripheryAddress: string;
entryPointContract: OriginSwapEntryPointContract;
};
};

export type OriginSwapEntryPointContract =
| {
name: "SpokePoolPeriphery";
address: string;
}
| {
name: "UniversalSwapAndBridge";
address: string;
dex: SupportedDex;
};

export type CrossSwapQuotesWithFees = CrossSwapQuotes & {
fees: CrossSwapFees;
};
Expand Down
Loading

0 comments on commit c891687

Please sign in to comment.