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 reverse mapping for pool key, store tL, tU #310

Merged
merged 29 commits into from
Sep 5, 2024

Conversation

snreynolds
Copy link
Member

@snreynolds snreynolds commented Aug 28, 2024

motivation was to have better interface standardization for ERC721s
(functions dont require PositionConfig to be passed in)

  • & you get an onchain lookup of poolKey, tickLower, tickUpper
  • & position descriptor can read from config

I personally was against this for gas reasons (mint is 40-50K more expensive) but that should go down with mints on the same pool, and overhead for other ops is around 4-5K.

@hensha256
Copy link
Contributor

(mint is 40-50K more expensive

so its like 8k more for mints on the same pool right? which is the "normal" case?

src/libraries/PositionConfig.sol Outdated Show resolved Hide resolved
src/interfaces/ISubscriber.sol Outdated Show resolved Hide resolved
src/PositionManager.sol Outdated Show resolved Hide resolved
src/PositionManager.sol Outdated Show resolved Hide resolved
src/PositionManager.sol Outdated Show resolved Hide resolved
@snreynolds
Copy link
Member Author

(mint is 40-50K more expensive

so its like 8k more for mints on the same pool right? which is the "normal" case?

not sure I can add a test but sounds about right it definitely wont be 40-50K

Copy link
Collaborator

@saucepoint saucepoint left a comment

Choose a reason for hiding this comment

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

despite the massive diff, i quite like this

something about not checking subscriber on mint feels nice, same with maybe making burn only call unsubscribe

src/PositionManager.sol Outdated Show resolved Hide resolved
src/PositionManager.sol Outdated Show resolved Hide resolved
src/libraries/PositionInfoLibrary.sol Outdated Show resolved Hide resolved
test/erc721Permit/ERC721Permit.permit.t.sol Outdated Show resolved Hide resolved
uint256 liquidity,
uint128 amount0Max,
uint128 amount1Max,
bytes calldata hookData
) internal onlyIfApproved(msgSender(), tokenId) onlyValidConfig(tokenId, config) {
) internal onlyIfApproved(msgSender(), tokenId) {
(PositionInfo memory info, PoolKey memory poolKey) = getPoolPositionInfo(tokenId);
Copy link
Collaborator

Choose a reason for hiding this comment

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

are there gas savings if we dont load the entirety of PositionInfo? we're not using the truncated poolId obviously, and the ticks are just repacked to IPoolManager.ModifyLiquidityParams

something like this could be quite cool

(
    PoolKey memory key,
    IPoolManager.ModifyLiquidityParams memory liquidityParams,
    bool hasSubscriber
) = veryAwesomeFunction(tokenId);

liquidityParams.liquidityDelta = liquidityChange;

theres also ways to pack it further since PoolKey is 528 bits, so we can fit tL/tU/hasSubscriber in the remaining bits of the 3rd word

Copy link
Member Author

Choose a reason for hiding this comment

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

yeah was thinking the same thing.. I think I'm actually happy with it now that I have a version where the PositionInfo is packed (uint256).. since its the minimum we can basically return

@@ -3,33 +3,9 @@ pragma solidity ^0.8.24;

import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol";

// A PositionConfig is the input for creating and modifying a Position in core, whose truncated hash is set per tokenId
// A PositionConfig is the input for creating and modifying a Position in core
Copy link
Member Author

Choose a reason for hiding this comment

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

Keeping this for now to reduce merge conflicts

later can move to test utils

Copy link
Contributor

Choose a reason for hiding this comment

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

woudl rather delete now that we arent using it

src/base/Notifier.sol Outdated Show resolved Hide resolved
src/base/Notifier.sol Outdated Show resolved Hide resolved
src/base/Notifier.sol Show resolved Hide resolved
src/PositionManager.sol Outdated Show resolved Hide resolved
function _positionConfigs(uint256 tokenId) internal view override returns (PositionConfigId storage) {
return positionConfigs[tokenId];
}
mapping(uint256 tokenId => PackedPositionInfo info) public positionInfo;
Copy link
Contributor

Choose a reason for hiding this comment

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

now that its packed maybe worth having this internal and exposing a getter that returns the elements individually?

Copy link
Member Author

Choose a reason for hiding this comment

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

hm I dont feel strongly about it. For offchain, a simple sdk helper can dissect it. and onchain someone can just import the PositionInfoLibrary?

But if you prefer I can add

src/libraries/PoolKeyChecker.sol Outdated Show resolved Hide resolved
src/PositionManager.sol Outdated Show resolved Hide resolved
src/PositionManager.sol Outdated Show resolved Hide resolved
src/PositionManager.sol Outdated Show resolved Hide resolved
src/libraries/PositionInfoLibrary.sol Outdated Show resolved Hide resolved
src/base/Notifier.sol Outdated Show resolved Hide resolved
src/base/Notifier.sol Show resolved Hide resolved
src/libraries/PositionInfoLibrary.sol Outdated Show resolved Hide resolved
src/interfaces/IPositionManager.sol Outdated Show resolved Hide resolved
src/PositionManager.sol Outdated Show resolved Hide resolved
src/PositionManager.sol Outdated Show resolved Hide resolved
@snreynolds snreynolds changed the title wip add reserve mapping for pool key, store tL, tU wip add reverse mapping for pool key, store tL, tU Aug 29, 2024
@snreynolds snreynolds changed the title wip add reverse mapping for pool key, store tL, tU feat: add reverse mapping for pool key, store tL, tU Aug 29, 2024
@snreynolds snreynolds marked this pull request as ready for review August 29, 2024 20:50
assertEq(amount0Min, _amount0Min);
assertEq(amount1Min, _amount1Min);
}

// TODO: fix stack too deep here
Copy link
Contributor

Choose a reason for hiding this comment

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

is it fixed now?

Copy link
Member Author

Choose a reason for hiding this comment

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

yes i just removed that comment! thanks

saucepoint
saucepoint previously approved these changes Aug 29, 2024
Copy link
Collaborator

@saucepoint saucepoint left a comment

Choose a reason for hiding this comment

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

very clean work per usual 😌

src/PositionManager.sol Outdated Show resolved Hide resolved
test/mocks/MockCalldataDecoder.sol Show resolved Hide resolved
src/PositionManager.sol Outdated Show resolved Hide resolved
src/libraries/PositionInfoLibrary.sol Show resolved Hide resolved
@saucepoint
Copy link
Collaborator

reminder: consider SB-86, hasSubscriber should revert if the token does not exist

stance: probably ugly to enforce, and adds gas. BUT should be natspec'd in hasSubscriber function

Copy link
Collaborator

@saucepoint saucepoint left a comment

Choose a reason for hiding this comment

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

Comment on lines 288 to 293
bytes25 poolId = info.poolId();
// Store the poolKey if it is not already stored.
// On UniswapV4, the minimum tick spacing is 1, which means that if the tick spacing is 0, the pool key has not been set.
if (poolKeys[poolId].tickSpacing == 0) {
poolKeys[poolId] = poolKey;
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

let's do this write prior to _modifyLiquidity for CEI anyway (the hook still has access to tick range and poolKey regardless of CEI)

Copy link
Member Author

Choose a reason for hiding this comment

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

ah yeah that makes sense

Comment on lines 332 to 315
delete positionConfigs[tokenId];
positionInfo[tokenId] = PositionInfoLibrary.EMPTY_POSITION_INFO;
// Burn the token.
_burn(tokenId);
Copy link
Collaborator

Choose a reason for hiding this comment

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

thinking this logic should run prior to _modifyLiquidity for CEI

Copy link
Collaborator

Choose a reason for hiding this comment

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

solmate _burn might be save to leave it, but positionInfo may want to happen before?

Comment on lines +27 to +31
info = info.setSubscribe();
assertEq(info.hasSubscriber(), true);
assertEq(info.tickLower(), tickLower);
assertEq(info.tickUpper(), tickUpper);
assertEq(info.poolId(), bytes25(PoolId.unwrap(poolKey.toId())));
Copy link
Collaborator

Choose a reason for hiding this comment

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

library itself looks good, but as an insane person we should assert that the tickLower/tickUpper/poolId is NOT changed by .setSubscribe

i.e. asserting that the info data is the same before/after .setSubscribe

// fee delta can be ignored as this is a new position
(BalanceDelta liquidityDelta,) =
_modifyLiquidity(info, poolKey, liquidity.toInt256(), bytes32(tokenId), hookData);
(liquidityDelta).validateMaxIn(amount0Max, amount1Max);
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
(liquidityDelta).validateMaxIn(amount0Max, amount1Max);
liquidityDelta.validateMaxIn(amount0Max, amount1Max);

liquidityDelta: liquidityChange,
salt: salt
}),
hookData
);

if (positionConfigs[uint256(salt)].hasSubscriber()) {
_notifyModifyLiquidity(uint256(salt), config, liquidityChange, feesAccrued);
// TODO: Audit issue for burn, decide if we want to keep this and also unsubscribe.
Copy link
Contributor

Choose a reason for hiding this comment

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

remove

hensha256
hensha256 previously approved these changes Sep 5, 2024
hensha256
hensha256 previously approved these changes Sep 5, 2024
@snreynolds snreynolds merged commit 2b63bd7 into main Sep 5, 2024
3 checks passed
@snreynolds snreynolds deleted the reverse-mapping branch September 5, 2024 22:34
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.

5 participants