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(universal-router-sdk): DRAFT ETH/WETH output split route swaps #290

Draft
wants to merge 1 commit into
base: split-route-eth-weth-ur-sdk
Choose a base branch
from
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 20 additions & 39 deletions sdks/universal-router-sdk/src/entities/actions/uniswap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,24 +96,11 @@ export class UniswapTrade implements Command {
}

get outputRequiresWrap(): boolean {
if (this.isAllV4) {
return (
!this.trade.outputAmount.currency.isNative &&
(this.trade.swaps[0].route as unknown as V4Route<Currency, Currency>).pathOutput.isNative
)
}
return false
return this.numberOfSplitOutputsRequireWrap > 0
}

get outputRequiresUnwrap(): boolean {
if (this.isAllV4) {
return (
this.trade.outputAmount.currency.isNative &&
!(this.trade.swaps[0].route as unknown as V4Route<Currency, Currency>).pathOutput.isNative
)
} else {
return this.trade.outputAmount.currency.isNative
}
return this.numberOfSplitOutputsRequireUnwrap > 0
}

get outputRequiresTransition(): boolean {
Expand Down Expand Up @@ -149,7 +136,6 @@ export class UniswapTrade implements Command {
// 1. when there are >2 exact input trades. this is only a heuristic,
// as it's still more gas-expensive even in this case, but has benefits
// in that the reversion probability is lower
// TODO there are more cases now
const performAggregatedSlippageCheck =
this.trade.tradeType === TradeType.EXACT_INPUT && this.trade.routes.length > 2
const routerMustCustody =
Expand Down Expand Up @@ -184,18 +170,24 @@ export class UniswapTrade implements Command {
let minimumAmountOut: BigNumber = BigNumber.from(
this.trade.minimumAmountOut(this.options.slippageTolerance).quotient.toString()
)
// The router custodies for 3 reasons: to unwrap, to take a fee, and/or to do a slippage check

// The router custodies for 3 reasons: to unwrap/wrap, to take a fee, and/or to do a slippage check
if (routerMustCustody) {
const pools = this.trade.swaps[0].route.pools
const pathOutputCurrencyAddress = getCurrencyAddress(
getPathCurrency(this.trade.outputAmount.currency, pools[pools.length - 1])
)
let outputCurrencyAddr = getCurrencyAddress(this.trade.outputAmount.currency)

// If there is a fee, that percentage is sent to the fee recipient
// In the case where ETH is the output currency, the fee is taken in WETH (for gas reasons)
// 1. If some/all of the output needs to be wrapped or unwrapped we do that first to get all the output into the same currency
if (this.outputRequiresUnwrap) {
planner.addCommand(CommandType.UNWRAP_WETH, [ROUTER_AS_RECIPIENT, 0])
} else if (this.outputRequiresWrap) {
planner.addCommand(CommandType.WRAP_ETH, [ROUTER_AS_RECIPIENT, CONTRACT_BALANCE])
}

// 2. Take a fee, in the output currency of the trade

// If there is a fee, that percentage is sent to the fee recipient.
if (!!this.options.fee) {
const feeBips = encodeFeeBips(this.options.fee.fee)
planner.addCommand(CommandType.PAY_PORTION, [pathOutputCurrencyAddress, this.options.fee.recipient, feeBips])
planner.addCommand(CommandType.PAY_PORTION, [outputCurrencyAddr, this.options.fee.recipient, feeBips])

// If the trade is exact output, and a fee was taken, we must adjust the amount out to be the amount after the fee
// Otherwise we continue as expected with the trade's normal expected output
Expand All @@ -205,12 +197,11 @@ export class UniswapTrade implements Command {
}

// If there is a flat fee, that absolute amount is sent to the fee recipient
// In the case where ETH is the output currency, the fee is taken in WETH (for gas reasons)
if (!!this.options.flatFee) {
const feeAmount = this.options.flatFee.amount
if (minimumAmountOut.lt(feeAmount)) throw new Error('Flat fee amount greater than minimumAmountOut')

planner.addCommand(CommandType.TRANSFER, [pathOutputCurrencyAddress, this.options.flatFee.recipient, feeAmount])
planner.addCommand(CommandType.TRANSFER, [outputCurrencyAddr, this.options.flatFee.recipient, feeAmount])

// If the trade is exact output, and a fee was taken, we must adjust the amount out to be the amount after the fee
// Otherwise we continue as expected with the trade's normal expected output
Expand All @@ -219,19 +210,9 @@ export class UniswapTrade implements Command {
}
}

// The remaining tokens that need to be sent to the user after the fee is taken will be caught
// by this if-else clause.
if (this.outputRequiresUnwrap) {
planner.addCommand(CommandType.UNWRAP_WETH, [this.options.recipient, minimumAmountOut])
} else if (this.outputRequiresWrap) {
planner.addCommand(CommandType.WRAP_ETH, [this.options.recipient, CONTRACT_BALANCE])
} else {
planner.addCommand(CommandType.SWEEP, [
getCurrencyAddress(this.trade.outputAmount.currency),
this.options.recipient,
minimumAmountOut,
])
}
// 3. Slippage check, for aggregate slippage, or post-fee slippage
// Some cases will end up with a slippage check that don't need one, but its harmless
planner.addCommand(CommandType.SWEEP, [outputCurrencyAddr, this.options.recipient, minimumAmountOut])
}

// for exactOutput swaps with native input or that perform an inputToken transition (wrap or unwrap)
Expand Down
Loading