Skip to content

Commit

Permalink
checkpointing
Browse files Browse the repository at this point in the history
  • Loading branch information
saucepoint committed Jun 28, 2024
1 parent 406506f commit 8ce4ed6
Show file tree
Hide file tree
Showing 9 changed files with 30 additions and 14 deletions.
2 changes: 1 addition & 1 deletion .forge-snapshots/autocompound_exactUnclaimedFees.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
262501
264058
Original file line number Diff line number Diff line change
@@ -1 +1 @@
194874
196431
2 changes: 1 addition & 1 deletion .forge-snapshots/autocompound_excessFeesCredit.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
283040
284597
2 changes: 1 addition & 1 deletion .forge-snapshots/increaseLiquidity_erc20.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
175258
176815
2 changes: 1 addition & 1 deletion .forge-snapshots/increaseLiquidity_erc6909.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
150847
152404
2 changes: 1 addition & 1 deletion .forge-snapshots/mintWithLiquidity.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
472846
475856
8 changes: 6 additions & 2 deletions contracts/NonfungiblePositionManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ 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";
import {TransientLiquidityDelta} from "./libraries/TransientLiquidityDelta.sol";

contract NonfungiblePositionManager is INonfungiblePositionManager, BaseLiquidityManagement, ERC721Permit {
using CurrencyLibrary for Currency;
Expand All @@ -27,6 +28,7 @@ contract NonfungiblePositionManager is INonfungiblePositionManager, BaseLiquidit
using StateLibrary for IPoolManager;
using TransientStateLibrary for IPoolManager;
using SafeCast for uint256;
using TransientLiquidityDelta for address;

/// @dev The ID of the next token that will be minted. Skips 0
uint256 private _nextId = 1;
Expand Down Expand Up @@ -69,10 +71,12 @@ contract NonfungiblePositionManager is INonfungiblePositionManager, BaseLiquidit
) public payable returns (uint256 tokenId, BalanceDelta delta) {
// 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);
_increaseLiquidity(recipient, range, liquidity, hookData);

// TODO: should be triggered by zeroOut in _execute...
delta = recipient.getBalanceDelta(range.poolKey.currency0, range.poolKey.currency1);
BalanceDelta thisDelta = address(this).getBalanceDelta(range.poolKey.currency0, range.poolKey.currency1);

_closeCallerDeltas(delta, range.poolKey.currency0, range.poolKey.currency1, recipient, false);
_closeThisDeltas(thisDelta, range.poolKey.currency0, range.poolKey.currency1);

Expand Down
5 changes: 5 additions & 0 deletions contracts/base/BaseLiquidityManagement.sol
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {IBaseLiquidityManagement} from "../interfaces/IBaseLiquidityManagement.s
import {PositionLibrary} from "../libraries/Position.sol";
import {BalanceDeltaExtensionLibrary} from "../libraries/BalanceDeltaExtensionLibrary.sol";
import {LiquidityDeltaAccounting} from "../libraries/LiquidityDeltaAccounting.sol";
import {TransientLiquidityDelta} from "../libraries/TransientLiquidityDelta.sol";

import "forge-std/console2.sol";

Expand All @@ -40,6 +41,8 @@ abstract contract BaseLiquidityManagement is IBaseLiquidityManagement, SafeCallb
using PositionLibrary for IBaseLiquidityManagement.Position;
using BalanceDeltaExtensionLibrary for BalanceDelta;
using LiquidityDeltaAccounting for BalanceDelta;
using TransientLiquidityDelta for BalanceDelta;
using TransientLiquidityDelta for Currency;

mapping(address owner => mapping(LiquidityRangeId rangeId => Position)) public positions;

Expand Down Expand Up @@ -101,6 +104,8 @@ abstract contract BaseLiquidityManagement is IBaseLiquidityManagement, SafeCallb
// Calculate the portion of the liquidityDelta that is attributable to the caller.
// We must account for fees that might be owed to other users on the same range.
(callerDelta, thisDelta) = liquidityDelta.split(callerFeesAccrued, totalFeesAccrued);
callerDelta.flush(owner, range.poolKey.currency0, range.poolKey.currency1);
thisDelta.flush(address(this), range.poolKey.currency0, range.poolKey.currency1);

// Update position storage, flushing the callerDelta value to tokensOwed first if necessary.
// If callerDelta > 0, then even after investing callerFeesAccrued, the caller still has some amount to collect that were not added into the position so they are accounted to tokensOwed and removed from the final callerDelta returned.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.24;

import {BalanceDelta} from "@uniswap/v4-core/src/types/BalanceDelta.sol";
import {BalanceDelta, toBalanceDelta} 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 {
Expand All @@ -18,8 +19,8 @@ library TransientLiquidityDelta {

/// @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());
addDelta(currency0, holder, delta.amount0());
addDelta(currency1, holder, delta.amount1());
}

function addDelta(Currency currency, address caller, int128 delta) internal {
Expand All @@ -31,7 +32,7 @@ library TransientLiquidityDelta {
}
}

function subDelta(Currency currency, address caller, int128 delta) internal {
function subtractDelta(Currency currency, address caller, int128 delta) internal {
bytes32 hashSlot = _computeSlot(caller, currency);
assembly {
let oldValue := tload(hashSlot)
Expand All @@ -40,8 +41,13 @@ library TransientLiquidityDelta {
}
}

function getBalanceDelta(address holder, Currency currency0, Currency currency1) internal view returns (BalanceDelta delta) {
delta = toBalanceDelta(getDelta(currency0, holder), getDelta(currency1, holder));
}

/// Copied from v4-core/src/libraries/CurrencyDelta.sol:
/// @notice sets a new currency delta for a given caller and currency
function setDelta(Currency currency, address caller, int256 delta) internal {
function setDelta(Currency currency, address caller, int128 delta) internal {
bytes32 hashSlot = _computeSlot(caller, currency);

assembly {
Expand All @@ -50,7 +56,8 @@ library TransientLiquidityDelta {
}

/// @notice gets a new currency delta for a given caller and currency
function getDelta(Currency currency, address caller) internal view returns (int256 delta) {
// TODO: is returning 128 bits safe?
function getDelta(Currency currency, address caller) internal view returns (int128 delta) {
bytes32 hashSlot = _computeSlot(caller, currency);

assembly {
Expand Down

0 comments on commit 8ce4ed6

Please sign in to comment.