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

add position manager [wip] #141

Merged
merged 112 commits into from
Jul 23, 2024
Merged
Show file tree
Hide file tree
Changes from 93 commits
Commits
Show all changes
112 commits
Select commit Hold shift + click to select a range
6c63e1f
initial thoughts lock and batch
snreynolds Dec 5, 2023
ad39d19
update safecallback with constructor
snreynolds Dec 5, 2023
64fc40a
simple batch under lock
snreynolds Dec 6, 2023
a707c20
oops
snreynolds Dec 6, 2023
1d0e566
misc version bump; will conflict but can resolve later
saucepoint Feb 23, 2024
4f8bbd2
defining types and different levels of abstractions
saucepoint Feb 23, 2024
c4c9dcd
merge in main; resolve conflicts
saucepoint Mar 1, 2024
187fc3d
merge in main?
saucepoint Mar 1, 2024
ee27f4f
wip
saucepoint Mar 2, 2024
30ac060
merge in main
saucepoint Mar 2, 2024
d1c5897
misc fixes with main:latest
saucepoint Mar 2, 2024
7a134a5
basic mint
saucepoint Mar 3, 2024
7bd2996
begin moving tests to fuzz
saucepoint Mar 4, 2024
307f4bb
test for slippage
saucepoint Mar 5, 2024
109caf4
burning
saucepoint Mar 6, 2024
1bf080f
decrease liquidity
saucepoint Mar 6, 2024
40f042c
mint transfer burn, liquidityOf accounting
saucepoint Mar 7, 2024
6b1c7cb
wip
saucepoint Mar 8, 2024
fa511d0
refactor to use CurrencySettleTake
saucepoint Mar 12, 2024
a0e0a44
basic fee collection
saucepoint Mar 12, 2024
0d936d4
wip
saucepoint Mar 16, 2024
4be3c2a
misc fix
saucepoint Mar 19, 2024
7fa4c54
fee collection for independent same-range parties
saucepoint Mar 19, 2024
aae9697
LiquidityPosition -> LiquidityRange
saucepoint Mar 19, 2024
5dec534
erc20 fee collection
saucepoint Mar 19, 2024
1196c6a
decrease liquidity with fee collection
saucepoint Mar 25, 2024
3d317e8
wip test decrease liquidity on same range
saucepoint Mar 26, 2024
31a70cb
reworked fuzzers; more testing on fee claims for liquidity decreasing
saucepoint Mar 26, 2024
f844687
merge in main; update flipped deltas; update lockAcquired signature
saucepoint Mar 27, 2024
666faf8
forge fmt
saucepoint Mar 27, 2024
714913e
Merge remote-tracking branch 'parent/lock-and-batch-call' into positi…
saucepoint Mar 27, 2024
ddf5771
Merge branch 'position-manager' of github.com:saucepoint/v4-periphery…
saucepoint Mar 27, 2024
3c56d48
test fixes for flipped deltas
saucepoint Mar 27, 2024
f4275cc
wip
saucepoint Apr 3, 2024
245cc3e
test coverage for increase liquidity cases
saucepoint Apr 5, 2024
f971b3d
preliminary gas benchmarks
saucepoint Apr 5, 2024
0f9936e
merge in main; fix breaking changes
saucepoint Jun 3, 2024
41619f9
merge in changes
saucepoint Jun 3, 2024
0165be5
Position manager refactor (#2)
saucepoint Jun 7, 2024
d86a8c2
fix conflicts
saucepoint Jun 9, 2024
52b304e
cleanup: TODOs and imports
saucepoint Jun 12, 2024
af67661
Position manager Consolidate (#3)
saucepoint Jun 12, 2024
48f38c4
use currency settler syntax
saucepoint Jun 12, 2024
c8ce67b
use v4-core's gas snapshot
saucepoint Jun 13, 2024
da91136
use snapLastCall and isolate for posm benchmarks
saucepoint Jun 13, 2024
18600bd
Update contracts/libraries/CurrencySettleTake.sol
saucepoint Jun 14, 2024
f52adcf
use v4-core's solmate its more recent
saucepoint Jun 14, 2024
07cc628
use v4-core's openzeppelin-contracts
saucepoint Jun 17, 2024
240c8e1
add ERC721Permit
saucepoint Jun 17, 2024
1cb1948
feedback: memory hookData
saucepoint Jun 17, 2024
227683b
initial refactor. stack too deep
saucepoint Jun 19, 2024
a19636f
passing tests
saucepoint Jun 19, 2024
ea56ec2
merge in main
saucepoint Jun 19, 2024
fc04651
gutted LockAndBatchCall
saucepoint Jun 19, 2024
e1d55f8
cleanup diff
saucepoint Jun 19, 2024
b73a240
renaming vanilla functions
saucepoint Jun 19, 2024
2227265
sanitize
saucepoint Jun 20, 2024
0cff6ef
change add liq accounting (#126)
snreynolds Jun 26, 2024
e21e847
create compatibility with arbitrary execute handler
saucepoint Jun 26, 2024
1e52f82
being supporting batched ops on vanilla functions
saucepoint Jun 26, 2024
97080cb
some initial tests to drive TDD
saucepoint Jun 26, 2024
80b5185
gas with isolate
saucepoint Jun 26, 2024
93f012e
mint to recipient
saucepoint Jun 27, 2024
cc031aa
refactor for external call and code reuse (#134)
saucepoint Jun 27, 2024
1995c9e
updated interface with unlockAndExecute
saucepoint Jun 28, 2024
0d6ab0b
update decrease (#133)
snreynolds Jun 28, 2024
635ebe3
merge in posm-batch and fix
saucepoint Jun 28, 2024
406506f
fix bubbling different return types because of recursive calls
saucepoint Jun 28, 2024
fae83dc
all operations only return a BalanceDelta type (#136)
saucepoint Jun 29, 2024
7db4e14
temp-dev-update (#135)
saucepoint Jul 9, 2024
a565e0f
using internal calls, first pass (#143)
snreynolds Jul 12, 2024
4cec450
dumb fix to test ci (#146)
snreynolds Jul 12, 2024
f4793e0
some more gas snapshots (#150)
saucepoint Jul 17, 2024
77ff368
feat: posm, use salts for all positions & update permit (#148)
snreynolds Jul 17, 2024
77527f6
merge main
snreynolds Jul 17, 2024
07a3e63
Multicall & initialize (#154)
saucepoint Jul 18, 2024
cae6ed6
prep shared actions (#158)
snreynolds Jul 18, 2024
08f15e7
update main (#162)
snreynolds Jul 18, 2024
7ff81b1
Merge branch 'main' of github.com:Uniswap/periphery-next into dev-posm
snreynolds Jul 18, 2024
85bab02
ERC721Permit cleanup (#161)
saucepoint Jul 18, 2024
7c87344
remove old files, imports
snreynolds Jul 18, 2024
55978ac
Update src/NonfungiblePositionManager.sol
snreynolds Jul 19, 2024
4a3d64a
pr comments
snreynolds Jul 19, 2024
8f1be9f
pr comments
snreynolds Jul 19, 2024
7eb01a6
Merge branch 'main' into dev-posm
snreynolds Jul 19, 2024
a35b9ea
refactor test helpers per feedback
saucepoint Jul 19, 2024
bc1ee23
fix gas
snreynolds Jul 19, 2024
026c1b0
remove permit
snreynolds Jul 19, 2024
4470dba
fix compiler warnings
snreynolds Jul 19, 2024
af62834
rename to PositionManager
snreynolds Jul 19, 2024
2761404
cache length
snreynolds Jul 19, 2024
bbacc75
skip take for 0
snreynolds Jul 19, 2024
2adafa9
fix tests
snreynolds Jul 19, 2024
f04a857
Update src/interfaces/IPositionManager.sol
saucepoint Jul 22, 2024
983ffc8
update multicall tests per feedback
saucepoint Jul 22, 2024
c1cab0c
remove unused imports
saucepoint Jul 22, 2024
75b3543
more unused imports
saucepoint Jul 22, 2024
71b1a4f
improve assertion
saucepoint Jul 22, 2024
5c96bed
assert mint recipient is the payer and not the recipient
saucepoint Jul 22, 2024
a769478
pr feedback
saucepoint Jul 22, 2024
49a02ce
assert pool creation
saucepoint Jul 22, 2024
bbaf355
use poolManager
snreynolds Jul 22, 2024
538a0fb
remove liquidityrange imports
snreynolds Jul 22, 2024
b215934
remove version string
snreynolds Jul 22, 2024
6093c4b
pr comments, use base test setup
snreynolds Jul 22, 2024
84f9629
fuzz sqrtPrice
snreynolds Jul 22, 2024
7dbc6b2
use fuzz, assert eq
snreynolds Jul 22, 2024
decc745
other final rand pr comment fixes
snreynolds Jul 22, 2024
7dfdbeb
fix off by 1
snreynolds Jul 22, 2024
d7c358c
use bound
snreynolds Jul 22, 2024
c579a64
merge main
snreynolds Jul 22, 2024
04660de
use nextTokenId
snreynolds Jul 23, 2024
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
1 change: 0 additions & 1 deletion .forge-snapshots/FullOracleObserve0After5Seconds.snap

This file was deleted.

1 change: 0 additions & 1 deletion .forge-snapshots/FullOracleObserve200By13.snap

This file was deleted.

1 change: 0 additions & 1 deletion .forge-snapshots/FullOracleObserve200By13Plus5.snap

This file was deleted.

1 change: 0 additions & 1 deletion .forge-snapshots/FullOracleObserve5After5Seconds.snap

This file was deleted.

1 change: 0 additions & 1 deletion .forge-snapshots/FullOracleObserveOldest.snap

This file was deleted.

1 change: 0 additions & 1 deletion .forge-snapshots/FullOracleObserveOldestAfter5Seconds.snap

This file was deleted.

1 change: 0 additions & 1 deletion .forge-snapshots/FullOracleObserveZero.snap

This file was deleted.

1 change: 0 additions & 1 deletion .forge-snapshots/FullRangeAddInitialLiquidity.snap

This file was deleted.

1 change: 0 additions & 1 deletion .forge-snapshots/FullRangeAddLiquidity.snap

This file was deleted.

1 change: 0 additions & 1 deletion .forge-snapshots/FullRangeFirstSwap.snap

This file was deleted.

1 change: 0 additions & 1 deletion .forge-snapshots/FullRangeInitialize.snap

This file was deleted.

1 change: 0 additions & 1 deletion .forge-snapshots/FullRangeRemoveLiquidity.snap

This file was deleted.

1 change: 0 additions & 1 deletion .forge-snapshots/FullRangeRemoveLiquidityAndRebalance.snap

This file was deleted.

1 change: 0 additions & 1 deletion .forge-snapshots/FullRangeSecondSwap.snap

This file was deleted.

1 change: 0 additions & 1 deletion .forge-snapshots/FullRangeSwap.snap

This file was deleted.

1 change: 0 additions & 1 deletion .forge-snapshots/OracleGrow10Slots.snap

This file was deleted.

1 change: 0 additions & 1 deletion .forge-snapshots/OracleGrow10SlotsCardinalityGreater.snap

This file was deleted.

1 change: 0 additions & 1 deletion .forge-snapshots/OracleGrow1Slot.snap

This file was deleted.

1 change: 0 additions & 1 deletion .forge-snapshots/OracleGrow1SlotCardinalityGreater.snap

This file was deleted.

1 change: 0 additions & 1 deletion .forge-snapshots/OracleInitialize.snap

This file was deleted.

This file was deleted.

1 change: 0 additions & 1 deletion .forge-snapshots/OracleObserveCurrentTime.snap

This file was deleted.

This file was deleted.

1 change: 0 additions & 1 deletion .forge-snapshots/OracleObserveLast20Seconds.snap

This file was deleted.

1 change: 0 additions & 1 deletion .forge-snapshots/OracleObserveLatestEqual.snap

This file was deleted.

1 change: 0 additions & 1 deletion .forge-snapshots/OracleObserveLatestTransform.snap

This file was deleted.

1 change: 0 additions & 1 deletion .forge-snapshots/OracleObserveMiddle.snap

This file was deleted.

1 change: 0 additions & 1 deletion .forge-snapshots/OracleObserveOldest.snap

This file was deleted.

1 change: 0 additions & 1 deletion .forge-snapshots/OracleObserveSinceMostRecent.snap

This file was deleted.

1 change: 1 addition & 0 deletions .forge-snapshots/PositionManager_collect.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
162408
1 change: 1 addition & 0 deletions .forge-snapshots/PositionManager_collect_sameRange.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
162408
1 change: 1 addition & 0 deletions .forge-snapshots/PositionManager_decreaseLiquidity.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
127786
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
140667
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
157204
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
148714
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
184978
1 change: 1 addition & 0 deletions .forge-snapshots/PositionManager_mint.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
384014
1 change: 1 addition & 0 deletions .forge-snapshots/PositionManager_mint_onSameTickLower.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
343796
1 change: 1 addition & 0 deletions .forge-snapshots/PositionManager_mint_onSameTickUpper.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
344438
1 change: 1 addition & 0 deletions .forge-snapshots/PositionManager_mint_sameRange.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
287120
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
349814
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
427889
1 change: 0 additions & 1 deletion .forge-snapshots/TWAMMSubmitOrder.snap

This file was deleted.

2 changes: 1 addition & 1 deletion .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,5 @@ jobs:
with:
version: nightly

- name: Run tests
- name: Check format
run: forge fmt --check
12 changes: 0 additions & 12 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -1,15 +1,3 @@
[submodule "lib/forge-std"]
path = lib/forge-std
url = https://github.com/foundry-rs/forge-std
[submodule "lib/openzeppelin-contracts"]
path = lib/openzeppelin-contracts
url = https://github.com/OpenZeppelin/openzeppelin-contracts
[submodule "lib/forge-gas-snapshot"]
path = lib/forge-gas-snapshot
url = https://github.com/marktoda/forge-gas-snapshot
[submodule "lib/v4-core"]
path = lib/v4-core
url = https://github.com/Uniswap/v4-core
[submodule "lib/solmate"]
path = lib/solmate
url = https://github.com/transmissions11/solmate
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ contract CoolHook is BaseHook {
address,
IPoolManager.PoolKey calldata key,
IPoolManager.ModifyLiquidityParams calldata params
) external override poolManagerOnly returns (bytes4) {
) external override onlyByManager returns (bytes4) {
// hook logic
return BaseHook.beforeAddLiquidity.selector;
}
Expand Down
1 change: 0 additions & 1 deletion lib/forge-gas-snapshot
Submodule forge-gas-snapshot deleted from 9161f7
1 change: 0 additions & 1 deletion lib/forge-std
Submodule forge-std deleted from 3d8086
1 change: 0 additions & 1 deletion lib/openzeppelin-contracts
Submodule openzeppelin-contracts deleted from 5ae630
1 change: 0 additions & 1 deletion lib/solmate
Submodule solmate deleted from bfc9c2
4 changes: 1 addition & 3 deletions remappings.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,2 @@
@uniswap/v4-core/=lib/v4-core/
solmate/=lib/solmate/src/
forge-std/=lib/forge-std/src/
@openzeppelin/=lib/openzeppelin-contracts/
@openzeppelin/=lib/v4-core/lib/openzeppelin-contracts/
223 changes: 223 additions & 0 deletions src/PositionManager.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,223 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.24;

import {IPoolManager} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol";
import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol";
import {PoolIdLibrary} from "@uniswap/v4-core/src/types/PoolId.sol";
import {Currency, CurrencyLibrary} from "@uniswap/v4-core/src/types/Currency.sol";
import {BalanceDelta} from "@uniswap/v4-core/src/types/BalanceDelta.sol";
import {SafeCast} from "@uniswap/v4-core/src/libraries/SafeCast.sol";
import {StateLibrary} from "@uniswap/v4-core/src/libraries/StateLibrary.sol";
import {TransientStateLibrary} from "@uniswap/v4-core/src/libraries/TransientStateLibrary.sol";

import {ERC721Permit} from "./base/ERC721Permit.sol";
import {IPositionManager, Actions} from "./interfaces/IPositionManager.sol";
import {SafeCallback} from "./base/SafeCallback.sol";
import {ImmutableState} from "./base/ImmutableState.sol";
import {Multicall} from "./base/Multicall.sol";
import {PoolInitializer} from "./base/PoolInitializer.sol";
import {CurrencySettleTake} from "./libraries/CurrencySettleTake.sol";
import {LiquidityRange, LiquidityRangeId, LiquidityRangeIdLibrary} from "./types/LiquidityRange.sol";
snreynolds marked this conversation as resolved.
Show resolved Hide resolved

contract PositionManager is IPositionManager, ERC721Permit, PoolInitializer, Multicall, SafeCallback {
using CurrencyLibrary for Currency;
using CurrencySettleTake for Currency;
using PoolIdLibrary for PoolKey;
using LiquidityRangeIdLibrary for LiquidityRange;
using StateLibrary for IPoolManager;
using TransientStateLibrary for IPoolManager;
using SafeCast for uint256;

/// @dev The ID of the next token that will be minted. Skips 0
uint256 public nextTokenId = 1;

// maps the ERC721 tokenId to its Range (poolKey, tick range)
mapping(uint256 tokenId => LiquidityRange range) public tokenRange;

constructor(IPoolManager _manager)
ImmutableState(_manager)
ERC721Permit("Uniswap V4 Positions NFT-V1", "UNI-V4-POS", "1")
snreynolds marked this conversation as resolved.
Show resolved Hide resolved
{}

modifier checkDeadline(uint256 deadline) {
if (block.timestamp > deadline) revert DeadlinePassed();
_;
}

/// @param unlockData is an encoding of actions, params, and currencies
/// @return returnData is the endocing of each actions return information
function modifyLiquidities(bytes calldata unlockData, uint256 deadline)
external
checkDeadline(deadline)
returns (bytes[] memory)
{
// TODO: Edit the encoding/decoding.
return abi.decode(manager.unlock(abi.encode(unlockData, msg.sender)), (bytes[]));
}

function _unlockCallback(bytes calldata payload) internal override returns (bytes memory) {
// TODO: Fix double encode/decode
snreynolds marked this conversation as resolved.
Show resolved Hide resolved
(bytes memory unlockData, address sender) = abi.decode(payload, (bytes, address));

(Actions[] memory actions, bytes[] memory params) = abi.decode(unlockData, (Actions[], bytes[]));

bytes[] memory returnData = _dispatch(actions, params, sender);

return abi.encode(returnData);
}

function _dispatch(Actions[] memory actions, bytes[] memory params, address sender)
internal
returns (bytes[] memory returnData)
{
uint256 length = actions.length;
if (length != params.length) revert MismatchedLengths();
returnData = new bytes[](length);
for (uint256 i; i < length; i++) {
if (actions[i] == Actions.INCREASE) {
returnData[i] = _increase(params[i], sender);
} else if (actions[i] == Actions.DECREASE) {
returnData[i] = _decrease(params[i], sender);
} else if (actions[i] == Actions.MINT) {
// TODO: Mint will be coupled with increase.
returnData[i] = _mint(params[i]);
} else if (actions[i] == Actions.CLOSE_CURRENCY) {
returnData[i] = _close(params[i], sender);
} else if (actions[i] == Actions.BURN) {
// TODO: Burn will be coupled with decrease.
(uint256 tokenId) = abi.decode(params[i], (uint256));
burn(tokenId, sender);
} else {
revert UnsupportedAction();
}
}
}

/// @param param is an encoding of uint256 tokenId, uint256 liquidity, bytes hookData
/// @param sender the msg.sender, set by the `modifyLiquidities` function before the `unlockCallback`. Using msg.sender directly inside
/// the _unlockCallback will be the pool manager.
/// @return returns an encoding of the BalanceDelta applied by this increase call, including credited fees.
/// @dev Calling increase with 0 liquidity will credit the caller with any underlying fees of the position
function _increase(bytes memory param, address sender) internal returns (bytes memory) {
(uint256 tokenId, uint256 liquidity, bytes memory hookData) = abi.decode(param, (uint256, uint256, bytes));

_requireApprovedOrOwner(tokenId, sender);

// Note: The tokenId is used as the salt for this position, so every minted liquidity has unique storage in the pool manager.
(BalanceDelta delta,) = _modifyLiquidity(tokenRange[tokenId], liquidity.toInt256(), bytes32(tokenId), hookData);
snreynolds marked this conversation as resolved.
Show resolved Hide resolved
return abi.encode(delta);
}

/// @param params is an encoding of uint256 tokenId, uint256 liquidity, bytes hookData
/// @param sender the msg.sender, set by the `modifyLiquidities` function before the `unlockCallback`. Using msg.sender directly inside
/// the _unlockCallback will be the pool manager.
/// @return returns an encoding of the BalanceDelta applied by this increase call, including credited fees.
/// @dev Calling decrease with 0 liquidity will credit the caller with any underlying fees of the position
function _decrease(bytes memory params, address sender) internal returns (bytes memory) {
(uint256 tokenId, uint256 liquidity, bytes memory hookData) = abi.decode(params, (uint256, uint256, bytes));

_requireApprovedOrOwner(tokenId, sender);

// Note: the tokenId is used as the salt.
(BalanceDelta delta,) =
_modifyLiquidity(tokenRange[tokenId], -(liquidity.toInt256()), bytes32(tokenId), hookData);
return abi.encode(delta);
}

/// @param param is an encoding of LiquidityRange memory range, uint256 liquidity, address recipient, bytes hookData where recipient is the receiver / owner of the ERC721
/// @return returns an encoding of the BalanceDelta from the initial increase
function _mint(bytes memory param) internal returns (bytes memory) {
(LiquidityRange memory range, uint256 liquidity, address owner, bytes memory hookData) =
abi.decode(param, (LiquidityRange, uint256, address, bytes));

// mint receipt token
uint256 tokenId;
// tokenId is assigned to current nextTokenId before incrementing it
unchecked {
tokenId = nextTokenId++;
snreynolds marked this conversation as resolved.
Show resolved Hide resolved
}
_mint(owner, tokenId);

(BalanceDelta delta,) = _modifyLiquidity(range, liquidity.toInt256(), bytes32(tokenId), hookData);

tokenRange[tokenId] = range;

return abi.encode(delta);
}

/// @param params is an encoding of the Currency to close
/// @param sender is the msg.sender encoded by the `modifyLiquidities` function before the `unlockCallback`.
/// @return int256 the balance of the currency being settled by this call
snreynolds marked this conversation as resolved.
Show resolved Hide resolved
function _close(bytes memory params, address sender) internal returns (bytes memory) {
(Currency currency) = abi.decode(params, (Currency));
// this address has applied all deltas on behalf of the user/owner
// it is safe to close this entire delta because of slippage checks throughout the batched calls.
int256 currencyDelta = manager.currencyDelta(address(this), currency);

// the sender is the payer or receiver
if (currencyDelta < 0) {
snreynolds marked this conversation as resolved.
Show resolved Hide resolved
currency.settle(manager, sender, uint256(-int256(currencyDelta)), false);
} else if (currencyDelta > 0) {
currency.take(manager, sender, uint256(int256(currencyDelta)), false);
}

return abi.encode(currencyDelta);
}

function burn(uint256 tokenId, address sender) internal {
_requireApprovedOrOwner(tokenId, sender);

// Checks that the full position's liquidity has been removed and all tokens have been collected from tokensOwed.
_validateBurn(tokenId);

delete tokenRange[tokenId];
// Burn the token.
_burn(tokenId);
}

function _modifyLiquidity(LiquidityRange memory range, int256 liquidityChange, bytes32 salt, bytes memory hookData)
internal
returns (BalanceDelta liquidityDelta, BalanceDelta totalFeesAccrued)
{
(liquidityDelta, totalFeesAccrued) = manager.modifyLiquidity(
range.poolKey,
IPoolManager.ModifyLiquidityParams({
tickLower: range.tickLower,
tickUpper: range.tickUpper,
liquidityDelta: liquidityChange,
salt: salt
}),
hookData
);
}

// ensures liquidity of the position is empty before burning the token.
function _validateBurn(uint256 tokenId) internal view {
snreynolds marked this conversation as resolved.
Show resolved Hide resolved
bytes32 positionId = getPositionIdFromTokenId(tokenId);
uint128 liquidity = manager.getPositionLiquidity(tokenRange[tokenId].poolKey.toId(), positionId);
if (liquidity > 0) revert PositionMustBeEmpty();
snreynolds marked this conversation as resolved.
Show resolved Hide resolved
}

// TODO: Move this to a posm state-view library.
function getPositionIdFromTokenId(uint256 tokenId) public view returns (bytes32 positionId) {
LiquidityRange memory range = tokenRange[tokenId];
bytes32 salt = bytes32(tokenId);
int24 tickLower = range.tickLower;
int24 tickUpper = range.tickUpper;
address owner = address(this);

// positionId = keccak256(abi.encodePacked(owner, tickLower, tickUpper, salt))
assembly {
mstore(0x26, salt) // [0x26, 0x46)
mstore(0x06, tickUpper) // [0x23, 0x26)
mstore(0x03, tickLower) // [0x20, 0x23)
mstore(0, owner) // [0x0c, 0x20)
positionId := keccak256(0x0c, 0x3a) // len is 58 bytes
mstore(0x26, 0) // rewrite 0x26 to 0
}
}

function _requireApprovedOrOwner(uint256 tokenId, address sender) internal view {
if (!_isApprovedOrOwner(sender, tokenId)) revert NotApproved(sender);
}
}
Loading
Loading