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

WIP: PositionManager: execute() #132

Closed
wants to merge 11 commits into from
2 changes: 1 addition & 1 deletion .forge-snapshots/autocompound_exactUnclaimedFees.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
258477
261480
Original file line number Diff line number Diff line change
@@ -1 +1 @@
190850
193853
2 changes: 1 addition & 1 deletion .forge-snapshots/autocompound_excessFeesCredit.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
279016
282019
2 changes: 1 addition & 1 deletion .forge-snapshots/decreaseLiquidity_erc20.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
190026
191804
2 changes: 1 addition & 1 deletion .forge-snapshots/decreaseLiquidity_erc6909.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
168894
170671
2 changes: 1 addition & 1 deletion .forge-snapshots/increaseLiquidity_erc20.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
171241
174244
2 changes: 1 addition & 1 deletion .forge-snapshots/increaseLiquidity_erc6909.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
146823
149826
2 changes: 1 addition & 1 deletion .forge-snapshots/mintWithLiquidity.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
466530
469640
58 changes: 52 additions & 6 deletions contracts/NonfungiblePositionManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {BalanceDelta, toBalanceDelta} from "@uniswap/v4-core/src/types/BalanceDe
import {LiquidityAmounts} from "./libraries/LiquidityAmounts.sol";
import {TickMath} from "@uniswap/v4-core/src/libraries/TickMath.sol";
import {StateLibrary} from "@uniswap/v4-core/src/libraries/StateLibrary.sol";
import {TransientStateLibrary} from "@uniswap/v4-core/src/libraries/TransientStateLibrary.sol";
import {SafeCast} from "@uniswap/v4-core/src/libraries/SafeCast.sol";

contract NonfungiblePositionManager is INonfungiblePositionManager, BaseLiquidityManagement, ERC721Permit {
Expand All @@ -24,6 +25,7 @@ contract NonfungiblePositionManager is INonfungiblePositionManager, BaseLiquidit
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
Expand All @@ -37,6 +39,26 @@ contract NonfungiblePositionManager is INonfungiblePositionManager, BaseLiquidit
ERC721Permit("Uniswap V4 Positions NFT-V1", "UNI-V4-POS", "1")
{}

function unlockAndExecute(bytes[] calldata data) external {
// TODO: bubble up the return
manager.unlock(abi.encode(LiquidityOperation.EXECUTE, abi.encode(data)));
}

/// @param data bytes[] - array of abi.encodeWithSelector(<selector>, <arguments>)
function _execute(bytes[] memory data) internal override returns (bytes memory) {
bool success;
for (uint256 i; i < data.length; i++) {
// TODO: bubble up the return
(success,) = address(this).call(data[i]);
if (!success) revert("EXECUTE_FAILED");
}

// zeroOut();

// TODO: return something
return new bytes(0);
}

// NOTE: more gas efficient as LiquidityAmounts is used offchain
// TODO: deadline check
function mint(
Expand All @@ -46,8 +68,17 @@ contract NonfungiblePositionManager is INonfungiblePositionManager, BaseLiquidit
address recipient,
bytes calldata hookData
) public payable returns (uint256 tokenId, BalanceDelta delta) {
// delta = modifyLiquidity(range, liquidity.toInt256(), hookData, false);
delta = _lockAndIncreaseLiquidity(msg.sender, range, liquidity, hookData, false);
// TODO: optimization, read/write manager.isUnlocked to avoid repeated external calls for batched execution
if (manager.isUnlocked()) {
BalanceDelta thisDelta;
(delta, thisDelta) = _increaseLiquidity(recipient, range, liquidity, hookData);

// TODO: should be triggered by zeroOut in _execute...
_closeCallerDeltas(delta, range.poolKey.currency0, range.poolKey.currency1, recipient, false);
_closeThisDeltas(thisDelta, range.poolKey.currency0, range.poolKey.currency1);
} else {
delta = _unlockAndIncreaseLiquidity(msg.sender, range, liquidity, hookData, false);
}

// mint receipt token
_mint(recipient, (tokenId = _nextId++));
Expand Down Expand Up @@ -80,7 +111,19 @@ contract NonfungiblePositionManager is INonfungiblePositionManager, BaseLiquidit
returns (BalanceDelta delta)
{
TokenPosition memory tokenPos = tokenPositions[tokenId];
delta = _lockAndIncreaseLiquidity(tokenPos.owner, tokenPos.range, liquidity, hookData, claims);

if (manager.isUnlocked()) {
BalanceDelta thisDelta;
(delta, thisDelta) = _increaseLiquidity(tokenPos.owner, tokenPos.range, liquidity, hookData);

// TODO: should be triggered by zeroOut in _execute...
_closeCallerDeltas(
delta, tokenPos.range.poolKey.currency0, tokenPos.range.poolKey.currency1, tokenPos.owner, claims
);
_closeThisDeltas(thisDelta, tokenPos.range.poolKey.currency0, tokenPos.range.poolKey.currency1);
} else {
delta = _unlockAndIncreaseLiquidity(tokenPos.owner, tokenPos.range, liquidity, hookData, claims);
}
}

function decreaseLiquidity(uint256 tokenId, uint256 liquidity, bytes calldata hookData, bool claims)
Expand All @@ -89,7 +132,9 @@ contract NonfungiblePositionManager is INonfungiblePositionManager, BaseLiquidit
returns (BalanceDelta delta)
{
TokenPosition memory tokenPos = tokenPositions[tokenId];
delta = _lockAndDecreaseLiquidity(tokenPos.owner, tokenPos.range, liquidity, hookData, claims);

// TODO: @sauce update once _decreaseLiquidity returns callerDelta/thisDelta
delta = _unlockAndDecreaseLiquidity(tokenPos.owner, tokenPos.range, liquidity, hookData, claims);
}

function burn(uint256 tokenId, address recipient, bytes calldata hookData, bool claims)
Expand Down Expand Up @@ -118,7 +163,8 @@ contract NonfungiblePositionManager is INonfungiblePositionManager, BaseLiquidit
returns (BalanceDelta delta)
{
TokenPosition memory tokenPos = tokenPositions[tokenId];
delta = _lockAndCollect(tokenPos.owner, tokenPos.range, hookData, claims);
// TODO: @sauce update once _collect returns callerDelta/thisDel
delta = _unlockAndCollect(tokenPos.owner, tokenPos.range, hookData, claims);
}

function feesOwed(uint256 tokenId) external view returns (uint256 token0Owed, uint256 token1Owed) {
Expand Down Expand Up @@ -146,7 +192,7 @@ contract NonfungiblePositionManager is INonfungiblePositionManager, BaseLiquidit
}

modifier isAuthorizedForToken(uint256 tokenId) {
require(_isApprovedOrOwner(msg.sender, tokenId), "Not approved");
require(msg.sender == address(this) || _isApprovedOrOwner(msg.sender, tokenId), "Not approved");
_;
}
}
53 changes: 30 additions & 23 deletions contracts/base/BaseLiquidityManagement.sol
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import {BalanceDeltaExtensionLibrary} from "../libraries/BalanceDeltaExtensionLi

import "forge-std/console2.sol";

contract BaseLiquidityManagement is IBaseLiquidityManagement, SafeCallback {
abstract contract BaseLiquidityManagement is IBaseLiquidityManagement, SafeCallback {
using LiquidityRangeIdLibrary for LiquidityRange;
using CurrencyLibrary for Currency;
using CurrencySettleTake for Currency;
Expand Down Expand Up @@ -64,24 +64,26 @@ contract BaseLiquidityManagement is IBaseLiquidityManagement, SafeCallback {
else if (callerDelta1 > 0) currency1.send(manager, owner, uint128(callerDelta1), claims);
}

function _execute(bytes[] memory data) internal virtual returns (bytes memory);

function _unlockCallback(bytes calldata data) internal override returns (bytes memory) {
(
LiquidityOperation op,
address owner,
LiquidityRange memory range,
uint256 liquidityChange,
bytes memory hookData,
bool claims
) = abi.decode(data, (LiquidityOperation, address, LiquidityRange, uint256, bytes, bool));

if (op == LiquidityOperation.INCREASE) {
return abi.encode(_increaseLiquidityAndZeroOut(owner, range, liquidityChange, hookData, claims));
} else if (op == LiquidityOperation.DECREASE) {
return abi.encode(_decreaseLiquidityAndZeroOut(owner, range, liquidityChange, hookData, claims));
} else if (op == LiquidityOperation.COLLECT) {
return abi.encode(_collectAndZeroOut(owner, range, 0, hookData, claims));
(LiquidityOperation op, bytes memory args) = abi.decode(data, (LiquidityOperation, bytes));
if (op == LiquidityOperation.EXECUTE) {
(bytes[] memory payload) = abi.decode(args, (bytes[]));
return _execute(payload);
} else {
return new bytes(0);
(address owner, LiquidityRange memory range, uint256 liquidityChange, bytes memory hookData, bool claims) =
abi.decode(args, (address, LiquidityRange, uint256, bytes, bool));

if (op == LiquidityOperation.INCREASE) {
return abi.encode(_increaseLiquidityAndZeroOut(owner, range, liquidityChange, hookData, claims));
} else if (op == LiquidityOperation.DECREASE) {
return abi.encode(_decreaseLiquidityAndZeroOut(owner, range, liquidityChange, hookData, claims));
} else if (op == LiquidityOperation.COLLECT) {
return abi.encode(_collectAndZeroOut(owner, range, 0, hookData, claims));
} else {
return new bytes(0);
}
}
}

Expand Down Expand Up @@ -223,15 +225,17 @@ contract BaseLiquidityManagement is IBaseLiquidityManagement, SafeCallback {
return (tokensOwed, callerDelta, thisDelta);
}

function _lockAndIncreaseLiquidity(
function _unlockAndIncreaseLiquidity(
address owner,
LiquidityRange memory range,
uint256 liquidityToAdd,
bytes memory hookData,
bool claims
) internal returns (BalanceDelta) {
return abi.decode(
manager.unlock(abi.encode(LiquidityOperation.INCREASE, owner, range, liquidityToAdd, hookData, claims)),
manager.unlock(
abi.encode(LiquidityOperation.INCREASE, abi.encode(owner, range, liquidityToAdd, hookData, claims))
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

the double encode and double decode 😭

probably will get reworked...

),
(BalanceDelta)
);
}
Expand Down Expand Up @@ -285,15 +289,17 @@ contract BaseLiquidityManagement is IBaseLiquidityManagement, SafeCallback {
_closeAllDeltas(range.poolKey.currency0, range.poolKey.currency1);
}

function _lockAndDecreaseLiquidity(
function _unlockAndDecreaseLiquidity(
address owner,
LiquidityRange memory range,
uint256 liquidityToRemove,
bytes memory hookData,
bool claims
) internal returns (BalanceDelta) {
return abi.decode(
manager.unlock(abi.encode(LiquidityOperation.DECREASE, owner, range, liquidityToRemove, hookData, claims)),
manager.unlock(
abi.encode(LiquidityOperation.DECREASE, abi.encode(owner, range, liquidityToRemove, hookData, claims))
),
(BalanceDelta)
);
}
Expand Down Expand Up @@ -335,12 +341,13 @@ contract BaseLiquidityManagement is IBaseLiquidityManagement, SafeCallback {
_closeAllDeltas(range.poolKey.currency0, range.poolKey.currency1);
}

function _lockAndCollect(address owner, LiquidityRange memory range, bytes memory hookData, bool claims)
function _unlockAndCollect(address owner, LiquidityRange memory range, bytes memory hookData, bool claims)
internal
returns (BalanceDelta)
{
return abi.decode(
manager.unlock(abi.encode(LiquidityOperation.COLLECT, owner, range, 0, hookData, claims)), (BalanceDelta)
manager.unlock(abi.encode(LiquidityOperation.COLLECT, abi.encode(owner, range, 0, hookData, claims))),
(BalanceDelta)
);
}

Expand Down
3 changes: 2 additions & 1 deletion contracts/interfaces/IBaseLiquidityManagement.sol
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ interface IBaseLiquidityManagement {
enum LiquidityOperation {
INCREASE,
DECREASE,
COLLECT
COLLECT,
EXECUTE
}

/// @notice Fees owed for a given liquidity position. Includes materialized fees and uncollected fees.
Expand Down
Loading
Loading