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: Add support for mixed routes in the interface #4181

Merged
merged 23 commits into from
Aug 11, 2022

Conversation

zhongeric
Copy link
Contributor

@zhongeric zhongeric commented Jul 25, 2022

The new versions of the smart order router (SOR) and routing-api introduce support for a new kind of route called MixedRoutes. MixedRoutes are routes which have both V2 and V3 pools in them, ex:

USDC -[V3]-> ETH -[V2]-> USDT

We only support returning these routes through the AutoRouter / routingAPI, and so we can pass in a new protocols queryParam of [Protocol.V2, Protocol.V3, Protocol.MIXED] to direct the auto router to also look for and consider mixedRoutes. Client side, we should still only query for V3 routes using the client side SOR and/or the QuoterV2 fallback.

We also need to edit the Trade object we create here to include the new mixed route, in order to generate the correct calldata to submit the swap with.

We also show a new badge label of V3 + V2 to denote the new mixed routes in the RoutingDiagram

@vercel
Copy link

vercel bot commented Jul 25, 2022

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Updated
interface ✅ Ready (Inspect) Visit Preview Aug 11, 2022 at 6:54PM (UTC)

Comment on lines +77 to +79
const MixedProtocolBadge = styled(ProtocolBadge)`
width: 60px;
`
Copy link
Contributor Author

@zhongeric zhongeric Aug 5, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems to work to prevent the badge text from overflowing and line breaking

src/state/routing/slice.ts Outdated Show resolved Hide resolved
@zhongeric zhongeric marked this pull request as ready for review August 8, 2022 15:59
@vm
Copy link
Contributor

vm commented Aug 10, 2022

@zhongeric can you rebase/merge main?

Comment on lines 57 to 60
} else {
// TODO: Update with better estimates once we have interleaved routes.
// fallback general gas estimation
gas += V3_SWAP_BASE_GAS_ESTIMATE + route.pools.length * V3_SWAP_HOP_GAS_ESTIMATE
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Chose to keep an else case as a fallback, not really needed tho

@zhongeric
Copy link
Contributor Author

Do we want to query for mixedRoutes in the getClientSideQuote function as well? See: https://github.com/Uniswap/interface/pull/4181/files#diff-c9fd69a3658e7e36f7ab6b62fd18589fed617dad15c3184bc7335b9e6ad7ee02R68

@@ -28,7 +28,6 @@ const protocols: Protocol[] = [Protocol.V2, Protocol.V3, Protocol.MIXED]

const DEFAULT_QUERY_PARAMS = {
protocols: protocols.map((p) => p.toLowerCase()).join(','),
forceMixedRoutes: true,
Copy link
Contributor

@vm vm Aug 11, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this part should be done in a separate PR (turning on the mixed routes at the API level)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that way we know what we can minimally revert if issues arise

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

makes sense, will cut

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think all that PR needs to be is forceMixedRoutes: true?

@@ -43,8 +45,19 @@ function guesstimateGas(trade: Trade<Currency, Currency, TradeType> | undefined)
// V3 gas costs scale on initialized ticks being crossed, but we don't have that data here.
// We bake in some tick crossings into the base 100k cost.
gas += V3_SWAP_BASE_GAS_ESTIMATE + route.pools.length * V3_SWAP_HOP_GAS_ESTIMATE
} else if (route.protocol === Protocol.MIXED) {
const sections = partitionMixedRouteByProtocol(route as MixedRoute<Currency, Currency>)
let acc = 0
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

acc is unnecssary - you should instead just have L53 and L55 use gas += to be more explicit about what you're adding up.

You should also use reduce to make it more clear what is happening in the code:

gas += sections.reduce((gas, section) => {
  if (section.every((pool) => pool instanceof Pool)) {
    return gas + V3_SWAP_BASE_GAS_ESTIMATE + section.length * V3_SWAP_HOP_GAS_ESTIMATE
  } else if (section.every((pool) => pool instanceof Pair)) {
    return gas + V2_SWAP_BASE_GAS_ESTIMATE + (section.length - 1) * V2_SWAP_HOP_GAS_ESTIMATE
  } else {
    console.warn('Invalid section')
    return gas
  }
})

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the .reduce

}

function isMixedRoute(route: (V3PoolInRoute | V2PoolInRoute)[]): route is (V3PoolInRoute | V2PoolInRoute)[] {
/// Must have at least one V3 pool and one V2 pool
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't comprehensive. Should you also check that every route is either V3 or V2?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be resolved by new logic in getRouteProtocol

@@ -71,6 +79,13 @@ export function transformRoutesToTrade<TTradeType extends TradeType>(
route
?.filter((r): r is typeof route[0] & { routev3: NonNullable<typeof route[0]['routev3']> } => r.routev3 !== null)
.map(({ routev3, inputAmount, outputAmount }) => ({ routev3, inputAmount, outputAmount })) ?? [],
mixedRoutes:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where is this used?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

used to build the InterfaceTrade which is returned by useRoutingAPITrade() and then in useBestTrade()

@@ -41,8 +42,15 @@ export function computeRoutes(
}

return {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This requires a lot of looping that isn't necessary. Could you improve it by doing something like this?

getRouteProtocol(route: (V3PoolInRoute | V2PoolInRoute)[]): Protocol {
  if (route.every((pool) => pool.type === 'v2-pool')) return Protocol.V2
  if (route.every((pool) => pool.type === 'v3-pool')) return Protocol.V3
  return Protocol.MIXED
}

You could optimize it more but this seems sufficient. The old isV2Route-style functions were type guards, but the genericPoolPairParser makes it so that you no longer need to cast to a type before parsing the route.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah this is a lot more elegant. This also solves the comment above about the mixed routes not exactly being comprehensive

@@ -41,16 +41,21 @@ export function computeRoutes(
throw new Error('Expected both amountIn and amountOut to be present')
}

const routeProtocol: Protocol = getRouteProtocol(route)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rm : Protocol, the typing is unneeded here. It can be inferred from the return value of getRouteProtocol.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants