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

Using wrappedToken in CompositeLiquidityRouter in ERC4626Pool operations #1201

Open
wants to merge 32 commits into
base: main
Choose a base branch
from

Conversation

elshan-eth
Copy link
Contributor

@elshan-eth elshan-eth commented Dec 24, 2024

Description

The current CLR implementation does not support adding mixed assets. For example, if my pool consists of two wrapped tokens, and I already have one of them and want to use it, this is not possible with the current CLR. The reason is that it treats all input tokens as underlying assets. This PR resolves the issue by introducing a new flag in the interface, allowing us to specify which type of token to use.

Type of change

  • Bug fix
  • New feature
  • Breaking change
  • Dependency changes
  • Code refactor / cleanup
  • Optimization: [ ] gas / [ ] bytecode
  • Documentation or wording changes
  • Other

Checklist:

  • The diff is legible and has no extraneous changes
  • Complex code has been commented, including external interfaces
  • Tests have 100% code coverage
  • The base branch is either main, or there's a description of how to merge

Issue Resolution

Closes #1192

Copy link
Contributor

@jubeira jubeira left a comment

Choose a reason for hiding this comment

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

First pass done. I didn't get to the tests yet, but I think there are a few things that need to be adjusted before proceeding.

Overall the idea is correct; great job. It's just that some of the edge cases should behave differently now that we're not automatically wrapping / unwrapping.

pkg/vault/contracts/CompositeLiquidityRouter.sol Outdated Show resolved Hide resolved
pkg/vault/contracts/CompositeLiquidityRouter.sol Outdated Show resolved Hide resolved
pkg/vault/contracts/CompositeLiquidityRouter.sol Outdated Show resolved Hide resolved
pkg/vault/contracts/CompositeLiquidityRouter.sol Outdated Show resolved Hide resolved
pkg/vault/contracts/CompositeLiquidityRouter.sol Outdated Show resolved Hide resolved
pkg/vault/contracts/CompositeLiquidityRouter.sol Outdated Show resolved Hide resolved
@elshan-eth elshan-eth requested a review from jubeira January 7, 2025 18:07
Copy link
Contributor

@jubeira jubeira left a comment

Choose a reason for hiding this comment

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

Another pass done.

Changes are looking good for the most part. Just a few minor comments around names / docs / style; otherwise seems to be correct. I'll finish covering the tests in one more pass. Great job!

pkg/vault/contracts/CompositeLiquidityRouter.sol Outdated Show resolved Hide resolved
pkg/vault/contracts/CompositeLiquidityRouter.sol Outdated Show resolved Hide resolved
pkg/vault/contracts/CompositeLiquidityRouter.sol Outdated Show resolved Hide resolved
pkg/vault/contracts/CompositeLiquidityRouter.sol Outdated Show resolved Hide resolved
@elshan-eth elshan-eth changed the title Using wrappedToken in CompositeLiquidityRouter in ERC4626Pool operations [IN PROGRESS] Using wrappedToken in CompositeLiquidityRouter in ERC4626Pool operations Jan 9, 2025
@elshan-eth elshan-eth changed the title [IN PROGRESS] Using wrappedToken in CompositeLiquidityRouter in ERC4626Pool operations Using wrappedToken in CompositeLiquidityRouter in ERC4626Pool operations Jan 10, 2025
@elshan-eth elshan-eth requested a review from jubeira January 10, 2025 13:34
Copy link
Collaborator

@EndymionJkb EndymionJkb left a comment

Choose a reason for hiding this comment

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

Looks like a pretty elegant solution (especially to the limits problem). I have a conceptual issue with the useWrappedTokens name, and lots of little details.

I also think these PRs need a lot more in the description. Why do we need it? What problem does it address? What options did we consider and reject (e.g., in this case, the limits hack, replaced by splitting the wrap/unwrap functions)?

Otherwise we're not going to remember all this context when we go back and look at this in 6 months or a year.

pkg/vault/contracts/CompositeLiquidityRouter.sol Outdated Show resolved Hide resolved
elshan-eth and others added 14 commits January 13, 2025 18:10
Copy link
Contributor

@joaobrunoah joaobrunoah left a comment

Choose a reason for hiding this comment

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

I'd recommend changing isWrappedToken to useWrappedToken, it's probably clearer. Also, since the amounts out are mixing wrapped and underlying, I'd return a token array, for UX purposes. Not sure if gas is a constraint in this case.

@joaobrunoah
Copy link
Contributor

I'd recommend changing isWrappedToken to useWrappedToken, it's probably clearer. Also, since the amounts out are mixing wrapped and underlying, I'd return a token array, for UX purposes. Not sure if gas is a constraint in this case.

Ohh, I just noticed it was useWrappedToken before. In my opinion, useWrappedToken is more intuitive than isWrappedToken. Unless you use something like returnInWrappedTokens (remove liquidity) and chargeInWrappedTokens (add liquidity)

@EndymionJkb
Copy link
Collaborator

EndymionJkb commented Jan 13, 2025

I'd recommend changing isWrappedToken to useWrappedToken, it's probably clearer. Also, since the amounts out are mixing wrapped and underlying, I'd return a token array, for UX purposes. Not sure if gas is a constraint in this case.

Ohh, I just noticed it was useWrappedToken before. In my opinion, useWrappedToken is more intuitive than isWrappedToken. Unless you use something like returnInWrappedTokens (remove liquidity) and chargeInWrappedTokens (add liquidity)

I didn't have an issue with the name (also prefer useWrappedToken) - it just seemed like the logic was backwards to me. If the flag is true, it's treated like a standard token, whereas to me, "using" a wrapped token means wrapping/unwrapping with it.

Maybe reverse the sense of the name to make it clearer, like isStandardToken. If true, it ignores that it's a wrapped token. Also, that way it would default to false, which should be the usual case. Most of these will have wrapped tokens that we want the Vault to wrap/unwrap; the special case we're handling is when we don't want to do that.

Copy link
Collaborator

@EndymionJkb EndymionJkb left a comment

Choose a reason for hiding this comment

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

I think we need to address the boolean array name (see my suggestion to use isStandardToken or useAsStandardToken), and returning the token addresses, which would resolve all the "sorted in ... order" comment issues.

@elshan-eth
Copy link
Contributor Author

My eyes are already lost in the description, I double checked several times, but if you can double check, I would be very grateful

Copy link
Collaborator

@EndymionJkb EndymionJkb left a comment

Choose a reason for hiding this comment

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

Looking much better! Note the suggested language for NatSpec, which I believe is much clearer.

pkg/vault/contracts/CompositeLiquidityRouter.sol Outdated Show resolved Hide resolved
pkg/vault/contracts/CompositeLiquidityRouter.sol Outdated Show resolved Hide resolved
@jubeira
Copy link
Contributor

jubeira commented Jan 15, 2025

Sorry for the late comment, but I feel we're making up new terms here 😅

We're already dealing with wrapped and underlying, and that should be enough. I can't know which of them is standard in that context without reading the docs, which is not ideal.

The argument is consumed by a function called _wrapTokensExactInIfRequired or _wrapTokensExactOutIfRequired. Then, a more appropriate name for the array of flags would be wrapUnderlying. It's pretty clear that if an element is true, you wrap the given underlying amount; if not, you don't.

@EndymionJkb
Copy link
Collaborator

The argument is consumed by a function called _wrapTokensExactInIfRequired or _wrapTokensExactOutIfRequired. Then, a > more appropriate name for the array of flags would be wrapUnderlying. It's pretty clear that if an element is true, you wrap > the given underlying amount; if not, you don't.

Makes sense - especially because there isn't an unwrap, so there's only one name for the set of flags. The only drawback is the "default" value would be to set everything to true (kind of annoying), but I agree that reusing standard terms is more important.

Copy link
Contributor

@jubeira jubeira left a comment

Choose a reason for hiding this comment

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

This is looking great.

Just a few more comments; otherwise LGTM. I like the new structure much better; it reuses less code, but it's more clear and goes straight to the point.

pkg/vault/contracts/CompositeLiquidityRouter.sol Outdated Show resolved Hide resolved
pkg/vault/contracts/CompositeLiquidityRouter.sol Outdated Show resolved Hide resolved
pkg/vault/contracts/CompositeLiquidityRouter.sol Outdated Show resolved Hide resolved
Copy link
Collaborator

@EndymionJkb EndymionJkb left a comment

Choose a reason for hiding this comment

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

Looks good now; have one last (very minor) rename suggestion, and some comments.

* @param useAsStandardToken An array indicating whether to use the token as standard or wrap it,
* sorted in token registration order of wrapped tokens in the pool
* @param wrapUnderlying Flags indicating whether the corresponding token should be wrapped or
* use as a standard ERC20
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
* use as a standard ERC20
* used as a standard ERC20

* @param useAsStandardToken An array indicating whether to use the token as standard or wrap it,
* sorted in token registration order of wrapped tokens in the pool
* @param wrapUnderlying Flags indicating whether the corresponding token should be wrapped or
* use as a standard ERC20
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
* use as a standard ERC20
* used as a standard ERC20

* @param useAsStandardToken An array indicating whether to use the token as standard or unwrap it,
* sorted in token registration order of wrapped tokens in the pool
* @param unwrapWrapper Flags indicating whether the corresponding token should be unwrapped or
* use as a standard ERC20
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
* use as a standard ERC20
* used as a standard ERC20

* @param wethIsEth If true, incoming ETH will be wrapped to WETH and outgoing WETH will be unwrapped to ETH
* @param userData Additional (optional) data required for removing liquidity
* @return tokensOut Actual tokens received
* @return amountsOut Actual amounts of tokens received
*/
function removeLiquidityProportionalFromERC4626Pool(
address pool,
bool[] memory useAsStandardToken,
bool[] memory unwrapWrapper,
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
bool[] memory unwrapWrapper,
bool[] memory unwrapWrapped,

If we're saying "wrapped" and "underlying," this should be unwrapWrapped

* @param useAsStandardToken An array indicating whether to use the token as standard or wrap it,
* sorted in token registration order of wrapped tokens in the pool
* @param wrapUnderlying Flags indicating whether the corresponding token should be wrapped or
* use as a standard ERC20
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
* use as a standard ERC20
* used as a standard ERC20

@@ -134,7 +134,7 @@ contract CompositeLiquidityRouter is ICompositeLiquidityRouter, BatchRouterCommo
wethIsEth: wethIsEth,
userData: userData
}),
useAsStandardToken
unwrapWrapper
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
unwrapWrapper
unwrapWrapped

@@ -303,13 +303,13 @@ contract CompositeLiquidityRouter is ICompositeLiquidityRouter, BatchRouterCommo

function removeLiquidityERC4626PoolProportionalHook(
RemoveLiquidityHookParams calldata params,
bool[] calldata useAsStandardToken
bool[] calldata unwrapWrapper
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
bool[] calldata unwrapWrapper
bool[] calldata unwrapWrapped

Comment on lines +311 to +312
// Revert if `poolTokens` length does not match `minAmountsOut` and `unwrapWrapper`.
InputHelpers.ensureInputLengthMatch(poolTokensLength, params.minAmountsOut.length, unwrapWrapper.length);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
// Revert if `poolTokens` length does not match `minAmountsOut` and `unwrapWrapper`.
InputHelpers.ensureInputLengthMatch(poolTokensLength, params.minAmountsOut.length, unwrapWrapper.length);
// Revert if `poolTokens` length does not match `minAmountsOut` and `unwrapWrapped`.
InputHelpers.ensureInputLengthMatch(poolTokensLength, params.minAmountsOut.length, unwrapWrapped.length);

_sendTokenOut(params.sender, erc4626PoolTokens[i], amountsOut[i], params.wethIsEth);
}
} else {
if (unwrapWrapper[i]) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
if (unwrapWrapper[i]) {
if (unwrapWrapped[i]) {

Comment on lines +213 to +216
bool[] memory wrapUnderlying = new bool[](exactUnderlyingAmountsIn.length);
for (uint256 i = 0; i < wrapUnderlying.length; i++) {
wrapUnderlying[i] = true;
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Might be able to refactor this section of code out

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.

Fix automatic wraps / unwraps in CLR
4 participants