Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support native eth as input/output #1279

Merged
merged 18 commits into from
Nov 21, 2024
Merged
118 changes: 115 additions & 3 deletions api/_dexes/cross-swap.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,31 @@
import { SpokePool } from "@across-protocol/contracts/dist/typechain";

import {
isRouteEnabled,
isInputTokenBridgeable,
isOutputTokenBridgeable,
getBridgeQuoteForMinOutput,
getSpokePool,
latestGasPriceCache,
} from "../_utils";
import {
getUniswapCrossSwapQuotesForMinOutputB2A,
getUniswapCrossSwapQuotesForMinOutputA2B,
getBestUniswapCrossSwapQuotesForMinOutputA2A,
} from "./uniswap";
import { CrossSwap, CrossSwapQuotes } from "./types";
import { buildExactOutputBridgeTokenMessage, getSwapAndBridge } from "./utils";
import { tagIntegratorId } from "../_integrator-id";
import { PopulatedTransaction } from "ethers";
import { getMultiCallHandlerAddress } from "../_multicall-handler";

export type CrossSwapType =
(typeof CROSS_SWAP_TYPE)[keyof typeof CROSS_SWAP_TYPE];

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",
MIN_OUTPUT: "minOutput",
Expand All @@ -28,6 +38,11 @@
ANY_TO_ANY: "anyToAny",
} as const;

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

export const PREFERRED_BRIDGE_TOKENS = ["WETH", "USDC"];

export async function getCrossSwapQuotes(
Expand Down Expand Up @@ -73,7 +88,7 @@
}

// @TODO: Implement the following function
export async function getCrossSwapQuotesForExactInput(crossSwap: CrossSwap) {

Check warning on line 91 in api/_dexes/cross-swap.ts

View workflow job for this annotation

GitHub Actions / format-and-lint

'crossSwap' is defined but never used. Allowed unused args must match /^_/u
throw new Error("Not implemented yet");
}

Expand All @@ -82,8 +97,8 @@
inputToken: crossSwap.inputToken,
outputToken: crossSwap.outputToken,
minOutputAmount: crossSwap.amount,
// @TODO: handle ETH/WETH message generation
message: "0x",
recipient: getMultiCallHandlerAddress(crossSwap.outputToken.chainId),
message: buildExactOutputBridgeTokenMessage(crossSwap),
});
return {
crossSwap,
Expand Down Expand Up @@ -139,4 +154,101 @@
return CROSS_SWAP_TYPE.ANY_TO_ANY;
}

export function calcFees() {}
export async function buildCrossSwapTx(
crossSwapQuotes: CrossSwapQuotes,
integratorId?: string
) {
const originChainId = crossSwapQuotes.crossSwap.inputToken.chainId;
const destinationChainId = crossSwapQuotes.crossSwap.outputToken.chainId;
const spokePool = getSpokePool(originChainId);
const deposit = {
depositor: crossSwapQuotes.crossSwap.recipient,
recipient: getMultiCallHandlerAddress(destinationChainId),
inputToken: crossSwapQuotes.bridgeQuote.inputToken.address,
outputToken: crossSwapQuotes.bridgeQuote.outputToken.address,
inputAmount: crossSwapQuotes.bridgeQuote.inputAmount,
outputAmount: crossSwapQuotes.bridgeQuote.outputAmount,
destinationChainid: crossSwapQuotes.bridgeQuote.outputToken.chainId,
exclusiveRelayer:
crossSwapQuotes.bridgeQuote.suggestedFees.exclusiveRelayer,
quoteTimestamp: crossSwapQuotes.bridgeQuote.suggestedFees.timestamp,
fillDeadline: await getFillDeadline(spokePool),
exclusivityDeadline:
crossSwapQuotes.bridgeQuote.suggestedFees.exclusivityDeadline,
message: crossSwapQuotes.bridgeQuote?.message || "0x",
};

let tx: PopulatedTransaction;
let toAddress: string;

if (crossSwapQuotes.originSwapQuote) {
const swapAndBridge = getSwapAndBridge("uniswap", originChainId);
tx = await swapAndBridge.populateTransaction.swapAndBridge(
crossSwapQuotes.originSwapQuote.tokenIn.address,
crossSwapQuotes.originSwapQuote.tokenOut.address,
crossSwapQuotes.originSwapQuote.swapTx.data,
crossSwapQuotes.originSwapQuote.maximumAmountIn,
crossSwapQuotes.originSwapQuote.minAmountOut,
deposit
// @FIXME: This needs a redeployed version of the contract
// {
// value: crossSwapQuotes.crossSwap.isInputNative
// ? deposit.inputAmount
// : 0,
// }
);
toAddress = swapAndBridge.address;
} else {
const spokePool = getSpokePool(
crossSwapQuotes.crossSwap.inputToken.chainId
);
tx = await spokePool.populateTransaction.depositV3(
deposit.depositor,
deposit.recipient,
deposit.inputToken,
deposit.outputToken,
deposit.inputAmount,
deposit.outputAmount,
deposit.destinationChainid,
deposit.exclusiveRelayer,
deposit.quoteTimestamp,
deposit.fillDeadline,
deposit.exclusivityDeadline,
deposit.message,
{
value: crossSwapQuotes.crossSwap.isInputNative
? deposit.inputAmount
dohaki marked this conversation as resolved.
Show resolved Hide resolved
: 0,
}
);
toAddress = spokePool.address;
}

const [gas, gasPrice] = await Promise.all([
spokePool.provider.estimateGas({
from: crossSwapQuotes.crossSwap.depositor,
...tx,
}),
latestGasPriceCache(originChainId).get(),
]);

return {
from: crossSwapQuotes.crossSwap.depositor,
to: toAddress,
data: integratorId ? tagIntegratorId(integratorId, tx.data!) : tx.data,
gas,
gasPrice,
value: tx.value,
};
}

async function getFillDeadline(spokePool: SpokePool): Promise<number> {
const calls = [
spokePool.interface.encodeFunctionData("getCurrentTime"),
spokePool.interface.encodeFunctionData("fillDeadlineBuffer"),
];

const [currentTime, fillDeadlineBuffer] =
await spokePool.callStatic.multicall(calls);
return Number(currentTime) + Number(fillDeadlineBuffer);
}
9 changes: 8 additions & 1 deletion 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 } from "./cross-swap";
import { AmountType, CrossSwapType, LeftoverType } from "./cross-swap";

export type { AmountType, CrossSwapType };

Expand All @@ -19,17 +19,24 @@ export type Swap = {
recipient: string;
slippageTolerance: number;
type: AmountType;
leftoverType?: LeftoverType;
isInputNative?: boolean;
isOutputNative?: boolean;
};

export type CrossSwap = {
amount: BigNumber;
inputToken: Token;
outputToken: Token;
depositor: string;
recipient: string;
slippageTolerance: number;
type: AmountType;
leftoverType?: LeftoverType;
refundOnOrigin: boolean;
refundAddress?: string;
isInputNative?: boolean;
isOutputNative?: boolean;
};

export type SupportedDex = "1inch" | "uniswap";
Expand Down
Loading
Loading