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

Routing #58

Merged
merged 64 commits into from
Jul 29, 2024
Merged

Routing #58

merged 64 commits into from
Jul 29, 2024

Conversation

ewilz
Copy link
Member

@ewilz ewilz commented Sep 1, 2023

Probably of interest to note that foundry gas snapshots are not accounting for storage clear refunds, so before we get transient storage in here, gas is incredibly exaggerated.

Could cut down on bytecode by removing Single options

@ewilz ewilz marked this pull request as ready for review September 5, 2023 21:01
@ewilz ewilz changed the title [WIP] Routing Routing Sep 5, 2023
contracts/V4Router.sol Outdated Show resolved Hide resolved
contracts/V4Router.sol Outdated Show resolved Hide resolved
contracts/V4Router.sol Outdated Show resolved Hide resolved

function _swapExactOutput(ExactOutputParams memory params, address msgSender) private {
unchecked {
for (uint256 i = params.path.length; i > 0; i--) {
Copy link
Contributor

Choose a reason for hiding this comment

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

ugh i dislike that this is from n to 1 not n-1 to 0... but I see the issue.
I guess it could be
for (uint256 i = params.path.length-1; i < params.path.length; i--) {
but thats also ugly so i guess theres no good way 😂

Copy link
Member Author

Choose a reason for hiding this comment

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

dude yah, I actually searched all Uniswap for i-- to see how ppl deal with this type of thing. And I found ANOTHER POC v4 router that Pote wrote ages ago and he did the same thing baha

Copy link
Member

Choose a reason for hiding this comment

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

I feel like this is typical for for loops iterating backwards through the path though.. like doesn't seem too weird to me. Is there a reason we force it to be backwards though? I guess compatibility with how other routers work but there is no reason we couldn't just encode the path the other way also

contracts/V4Router.sol Outdated Show resolved Hide resolved
test/shared/implementation/V4RouterImplementation.sol Outdated Show resolved Hide resolved
contracts/interfaces/IV4Router.sol Outdated Show resolved Hide resolved
contracts/V4Router.sol Outdated Show resolved Hide resolved

function _swapExactOutput(ExactOutputParams memory params, address msgSender) private {
unchecked {
for (uint256 i = params.path.length; i > 0; i--) {
Copy link
Member

Choose a reason for hiding this comment

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

I feel like this is typical for for loops iterating backwards through the path though.. like doesn't seem too weird to me. Is there a reason we force it to be backwards though? I guess compatibility with how other routers work but there is no reason we couldn't just encode the path the other way also

res.offset := offset
}
}

/// @notice Performs the equivalent of `abi.decode(data, (uint256[], bytes[]))` in calldata
/// @param _bytes The input bytes string to extract input arrays from
/// @return actions The uint256 calldata array of actions
Copy link
Member

@snreynolds snreynolds Jul 25, 2024

Choose a reason for hiding this comment

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

I think decodeInCalldata is not memory-safe?

Copy link
Contributor

Choose a reason for hiding this comment

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

What about it isnt memory safe?

src/libraries/Actions.sol Show resolved Hide resolved
src/V4Router.sol Outdated Show resolved Hide resolved
src/V4Router.sol Outdated Show resolved Hide resolved
src/V4Router.sol Show resolved Hide resolved
if (delta > 0) revert InvalidDeltaForAction();

// TODO support address(this) paying too
// TODO should it have a maxAmountOut added slippage protection?
Copy link
Member

Choose a reason for hiding this comment

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

my thought is that slippage protection should be with the swap commands? why do we also need slippage protection with settle commands?

i think we can make settle just take an amount, and if you're ok with the full settle amount then you pass in a constant/sentinel

Copy link
Contributor

Choose a reason for hiding this comment

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

Its to support split route trades. Lets say youre trading 1000 DAI for USDC, where you want at least 998 USDC

  • 500 DAI -> USDT -> USDC
  • 500 DAI -> USDC

Then you would do a command thats like "take all USDC, and check we're receiving at least 998".

Copy link
Contributor

Choose a reason for hiding this comment

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

We implement the same style of logic in UR using the sweep command e.g. here

Copy link
Contributor

Choose a reason for hiding this comment

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

For v3 split routes it trades both routes, with the recipient of the UR, and then calls SWEEP(USDC, 998) which will revert the trade if 998 weren't received

Copy link
Contributor

Choose a reason for hiding this comment

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

And we do it with the unwrapETH command to do the same check for ETH output trades e.g. here

Copy link
Member

Choose a reason for hiding this comment

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

but surely you're also checking slippage from USDT to USDC and from DAI to USDC? or are you ignoring those checks until the end? My concern is that we're doing lots of slippage checks and the ones in the middle then dont even matter?

Copy link
Member

Choose a reason for hiding this comment

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

My other thought process then also is just if posm & routers take/settle commands looks different now bc I was not planning on doing extra slippage checks on take/settle. (IK this is a future TODO but just adding my thoughts in this thread. Not blocking on getting this PR merged)

Copy link
Contributor

Choose a reason for hiding this comment

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

but surely you're also checking slippage from USDT to USDC and from DAI to USDC? or are you ignoring those checks until the end? My concern is that we're doing lots of slippage checks and the ones in the middle then dont even matter?

yeah the ones in the middle dont matter. the user doesnt care if one half of the trade gets hit with more slippage than the other - theres just an output amount that they want from the trade in total and thats it

Copy link
Contributor

Choose a reason for hiding this comment

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

for split route trades we pass in 0 as minAmountOut at each stage - and then do aggregated slippage checks on total output

Copy link
Contributor

Choose a reason for hiding this comment

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

My other thought process then also is just if posm & routers take/settle commands looks different now

yeah i could do it as a different cmmand or something depending how the other commands end up

src/V4Router.sol Show resolved Hide resolved
@@ -7,6 +7,9 @@ import {ImmutableState} from "./ImmutableState.sol";
/// @notice Abstract contract used to sync, send, and settle funds to the pool manager
/// @dev Note that sync() is called before any erc-20 transfer in `settle`.
abstract contract DeltaResolver is ImmutableState {
/// @notice Emitted trying to settle a positive delta, or take a negative delta
Copy link
Member

Choose a reason for hiding this comment

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

should we make this type(uint256).max so its super explicit? Also matches other patterns like approving with type(uint256).max to mean my entire balance

Copy link
Member

Choose a reason for hiding this comment

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

Oh weird.. on codespaces the code I was reviewing had the changes where you kept the flag for checking to use the contract balance.. I guess we can do that in a different PR!

// equivalent: abi.decode(params, (Currency, address))
Currency currency;
address recipient;
assembly ("memory-safe") {
Copy link
Member

Choose a reason for hiding this comment

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

#199 just tagging that 199 should also put these in a lib so we can use in both contracts

src/V4Router.sol Outdated
for (uint256 i = pathLength; i > 0; i--) {
pathKey = params.path[i - 1];
(PoolKey memory poolKey, bool oneForZero) = pathKey.getPoolAndSwapDirection(currencyOut);
amountIn = uint128(-_swap(poolKey, !oneForZero, int256(uint256(amountOut)), 0, pathKey.hookData));
Copy link
Member

Choose a reason for hiding this comment

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

I feel like I commented at some point about documenting/adding a little comment about the return signs but can't find it anywhere.. Anyways I do think it would be nice explaining that the output is guaranteed to be negative for exactOutput in the reciprocal token.

Copy link
Contributor

Choose a reason for hiding this comment

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

have actually added a safecast on this - i think with custom accounting hooks on low liquidity pools its technically not guaranteed

Copy link
Member

@snreynolds snreynolds left a comment

Choose a reason for hiding this comment

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

ok looks good! I do think we should be adding some more tests (like split routes) but we can followup?

@@ -7,6 +7,9 @@ import {ImmutableState} from "./ImmutableState.sol";
/// @notice Abstract contract used to sync, send, and settle funds to the pool manager
/// @dev Note that sync() is called before any erc-20 transfer in `settle`.
abstract contract DeltaResolver is ImmutableState {
/// @notice Emitted trying to settle a positive delta, or take a negative delta
Copy link
Member

Choose a reason for hiding this comment

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

Oh weird.. on codespaces the code I was reviewing had the changes where you kept the flag for checking to use the contract balance.. I guess we can do that in a different PR!

test/shared/RoutingTestHelpers.sol Outdated Show resolved Hide resolved
test/shared/RoutingTestHelpers.sol Outdated Show resolved Hide resolved

function test_swapExactInputSingle_oneForZero() public {
uint256 amountIn = 1 ether;
uint256 expectedAmountOut = 992054607780215625;
Copy link
Member

Choose a reason for hiding this comment

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

where is this number from? I think you could use deltas instead of hardcoding?

Copy link
Contributor

Choose a reason for hiding this comment

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

gonna do this in the next PR - #221


/// @title Safe casting methods
/// @notice Contains methods for safely casting between types
library SafeCast {
Copy link
Member

Choose a reason for hiding this comment

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

why not just import SafeCast from core?

Copy link
Contributor

Choose a reason for hiding this comment

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

this function doesnt exist in core rip

Copy link
Member

@snreynolds snreynolds left a comment

Choose a reason for hiding this comment

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

approving, but will need to move that func to core! (alice opening issue)

@snreynolds snreynolds merged commit de79c94 into main Jul 29, 2024
3 checks passed
@snreynolds snreynolds deleted the routing branch July 29, 2024 22:09
@saucepoint saucepoint mentioned this pull request Sep 4, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants