-
Notifications
You must be signed in to change notification settings - Fork 42
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: build cross swap tx #1274
Changes from all commits
57b5d6d
3f23d87
4ed90e3
cb41cab
fb882e5
188ccfd
3a3187a
589dc98
8ac1e95
8958fad
30c38f5
484314e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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 { 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", | ||
|
@@ -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( | ||
|
@@ -73,7 +88,7 @@ | |
} | ||
|
||
// @TODO: Implement the following function | ||
export async function getCrossSwapQuotesForExactInput(crossSwap: CrossSwap) { | ||
throw new Error("Not implemented yet"); | ||
} | ||
|
||
|
@@ -139,4 +154,92 @@ | |
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.depositor, | ||
recipient: crossSwapQuotes.destinationSwapQuote | ||
? getMultiCallHandlerAddress(destinationChainId) | ||
: crossSwapQuotes.crossSwap.recipient, | ||
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 | ||
); | ||
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 | ||
); | ||
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); | ||
} | ||
Comment on lines
+236
to
+245
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This also exists in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yea, we are currently in a weird state with API and FE source code... We can not share unfortunately because our current folder structure is not optimized for sharing utilities between API and FE without blowing up the bundle size. So upstreaming to the SDK would prob make sense |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the case of outputToken bridged USDC on a CCTP chain, would we short-circuit the destination swap?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yep, we do the check here https://github.com/across-protocol/frontend-v2/blob/8ac1e952dee670245ffd52501e469f6b6b5e2c49/api/_dexes/cross-swap.ts#L129
and if it's a normal bridge route we don't populate a destination swap quote