Skip to content

Commit

Permalink
fix bubbling different return types because of recursive calls
Browse files Browse the repository at this point in the history
  • Loading branch information
saucepoint committed Jun 28, 2024
1 parent 635ebe3 commit 406506f
Show file tree
Hide file tree
Showing 11 changed files with 83 additions and 19 deletions.
2 changes: 1 addition & 1 deletion .forge-snapshots/autocompound_exactUnclaimedFees.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
262492
262501
Original file line number Diff line number Diff line change
@@ -1 +1 @@
194865
194874
2 changes: 1 addition & 1 deletion .forge-snapshots/autocompound_excessFeesCredit.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
283031
283040
2 changes: 1 addition & 1 deletion .forge-snapshots/decreaseLiquidity_erc20.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
180845
180891
2 changes: 1 addition & 1 deletion .forge-snapshots/decreaseLiquidity_erc6909.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
180857
180903
2 changes: 1 addition & 1 deletion .forge-snapshots/increaseLiquidity_erc20.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
175249
175258
2 changes: 1 addition & 1 deletion .forge-snapshots/increaseLiquidity_erc6909.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
150838
150847
2 changes: 1 addition & 1 deletion .forge-snapshots/mintWithLiquidity.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
600177
472846
24 changes: 14 additions & 10 deletions contracts/NonfungiblePositionManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ contract NonfungiblePositionManager is INonfungiblePositionManager, BaseLiquidit
ERC721Permit("Uniswap V4 Positions NFT-V1", "UNI-V4-POS", "1")
{}

function unlockAndExecute(bytes[] memory data) public returns (BalanceDelta delta) {
delta = abi.decode(manager.unlock(abi.encode(data)), (BalanceDelta));
function unlockAndExecute(bytes[] memory data) public returns (bytes memory) {
return manager.unlock(abi.encode(data));
}

function _unlockCallback(bytes calldata payload) internal override returns (bytes memory) {
Expand Down Expand Up @@ -75,15 +75,16 @@ contract NonfungiblePositionManager is INonfungiblePositionManager, BaseLiquidit
// 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);

// mint receipt token
_mint(recipient, (tokenId = _nextId++));
tokenPositions[tokenId] = TokenPosition({owner: recipient, range: range});
} else {
bytes[] memory data = new bytes[](1);
data[0] = abi.encodeWithSelector(this.mint.selector, range, liquidity, deadline, recipient, hookData);
delta = unlockAndExecute(data);
bytes memory result = unlockAndExecute(data);
(tokenId, delta) = abi.decode(result, (uint256, BalanceDelta));
}

// mint receipt token
_mint(recipient, (tokenId = _nextId++));
tokenPositions[tokenId] = TokenPosition({owner: recipient, range: range});
}

// NOTE: more expensive since LiquidityAmounts is used onchain
Expand Down Expand Up @@ -125,7 +126,8 @@ contract NonfungiblePositionManager is INonfungiblePositionManager, BaseLiquidit
} else {
bytes[] memory data = new bytes[](1);
data[0] = abi.encodeWithSelector(this.increaseLiquidity.selector, tokenId, liquidity, hookData, claims);
delta = unlockAndExecute(data);
bytes memory result = unlockAndExecute(data);
delta = abi.decode(result, (BalanceDelta));
}
}

Expand All @@ -145,7 +147,8 @@ contract NonfungiblePositionManager is INonfungiblePositionManager, BaseLiquidit
} else {
bytes[] memory data = new bytes[](1);
data[0] = abi.encodeWithSelector(this.decreaseLiquidity.selector, tokenId, liquidity, hookData, claims);
delta = unlockAndExecute(data);
bytes memory result = unlockAndExecute(data);
(delta, thisDelta) = abi.decode(result, (BalanceDelta, BalanceDelta));
}
}

Expand Down Expand Up @@ -189,7 +192,8 @@ contract NonfungiblePositionManager is INonfungiblePositionManager, BaseLiquidit
} else {
bytes[] memory data = new bytes[](1);
data[0] = abi.encodeWithSelector(this.collect.selector, tokenId, recipient, hookData, claims);
delta = unlockAndExecute(data);
bytes memory result = unlockAndExecute(data);
delta = abi.decode(result, (BalanceDelta));
}
}

Expand Down
2 changes: 1 addition & 1 deletion contracts/interfaces/INonfungiblePositionManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ interface INonfungiblePositionManager {
/// @notice Execute a batch of external calls by unlocking the PoolManager
/// @param data an array of abi.encodeWithSelector(<selector>, <args>) for each call
/// @return delta The final delta changes of the caller
function unlockAndExecute(bytes[] memory data) external returns (BalanceDelta delta);
function unlockAndExecute(bytes[] memory data) external returns (bytes memory);

/// @notice Returns the fees owed for a position. Includes unclaimed fees + custodied fees + claimable fees
/// @param tokenId The ID of the position
Expand Down
60 changes: 60 additions & 0 deletions contracts/libraries/TransientDemo.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.24;

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

/// @title a library to store callers' currency deltas in transient storage
/// @dev this library implements the equivalent of a mapping, as transient storage can only be accessed in assembly
library TransientLiquidityDelta {
/// @notice calculates which storage slot a delta should be stored in for a given caller and currency
function _computeSlot(address caller_, Currency currency) internal pure returns (bytes32 hashSlot) {
assembly {
mstore(0, caller_)
mstore(32, currency)
hashSlot := keccak256(0, 64)
}
}

/// @notice Flush a BalanceDelta into transient storage for a given holder
function flush(BalanceDelta delta, address holder, Currency currency0, Currency currency1) internal {
setDelta(currency0, holder, delta.amount0());
setDelta(currency1, holder, delta.amount1());
}

function addDelta(Currency currency, address caller, int128 delta) internal {
bytes32 hashSlot = _computeSlot(caller, currency);
assembly {
let oldValue := tload(hashSlot)
let newValue := add(oldValue, delta)
tstore(hashSlot, newValue)
}
}

function subDelta(Currency currency, address caller, int128 delta) internal {
bytes32 hashSlot = _computeSlot(caller, currency);
assembly {
let oldValue := tload(hashSlot)
let newValue := sub(oldValue, delta)
tstore(hashSlot, newValue)
}
}

/// @notice sets a new currency delta for a given caller and currency
function setDelta(Currency currency, address caller, int256 delta) internal {
bytes32 hashSlot = _computeSlot(caller, currency);

assembly {
tstore(hashSlot, delta)
}
}

/// @notice gets a new currency delta for a given caller and currency
function getDelta(Currency currency, address caller) internal view returns (int256 delta) {
bytes32 hashSlot = _computeSlot(caller, currency);

assembly {
delta := tload(hashSlot)
}
}
}

0 comments on commit 406506f

Please sign in to comment.