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

Flip the sign of deltas #481

Merged
merged 31 commits into from
Mar 13, 2024
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
d93b3c9
Remove access lock
hensha256 Dec 11, 2023
13642d8
remove current hook
hensha256 Dec 11, 2023
cd92291
Remove nested locking
hensha256 Dec 11, 2023
1f4961e
Locker library tests
hensha256 Dec 12, 2023
c0e2722
Rename empty lock test
hensha256 Dec 12, 2023
2237a48
Test skeleton
hensha256 Dec 12, 2023
df58a45
Test for a nested swap
hensha256 Dec 13, 2023
99243a1
Tests for nested function calls
hensha256 Dec 14, 2023
514726d
Merge branch 'main' into remove-nested-locks
hensha256 Dec 15, 2023
9c8f549
Merge branch 'main' into remove-nested-locks
hensha256 Dec 15, 2023
250d74d
Merge branch 'main' into remove-nested-locks
hensha256 Dec 18, 2023
56f6044
snapshots
hensha256 Dec 18, 2023
6ce20c4
Separate libs, rename error
hensha256 Jan 31, 2024
96975e7
Merge branch 'main' into remove-nested-locks
hensha256 Feb 12, 2024
115341e
Merge branch 'main' into remove-nested-locks
hensha256 Feb 12, 2024
c802859
Remove lock caller
hensha256 Feb 12, 2024
67f79e8
Merge main into remove-nested-locks
hensha256 Feb 14, 2024
dff0d82
merge errors
hensha256 Feb 14, 2024
47bea1a
Flipping the sign of deltas
hensha256 Feb 14, 2024
ba1494d
update comments
hensha256 Feb 14, 2024
0f19c28
Merge branch 'main' into flip-the-deltas
hensha256 Feb 29, 2024
7a5d41d
Merge branch 'main' into flip-the-deltas
hensha256 Mar 5, 2024
b746376
Merge branch 'main' into flip-the-deltas
hensha256 Mar 6, 2024
d8ebb60
remove unnecessary read of result
hensha256 Mar 6, 2024
f45c75a
move delta flip to sqrtprice lib
hensha256 Mar 6, 2024
0043fa4
remove duplicate test
hensha256 Mar 6, 2024
1a76db2
amountSpecified matches deltas (#491)
ewilz Mar 6, 2024
4a1ccfa
PR nits
hensha256 Mar 8, 2024
9fcf92f
Merge branch 'main' into flip-the-deltas
hensha256 Mar 8, 2024
b02bda8
Merge branch 'main' into flip-the-deltas
hensha256 Mar 13, 2024
8ffcc42
Merge branch 'main' into flip-the-deltas
hensha256 Mar 13, 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
2 changes: 1 addition & 1 deletion .forge-snapshots/addLiquidity with empty hook.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
312661
312838
2 changes: 1 addition & 1 deletion .forge-snapshots/addLiquidity with native token.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
192843
193017
2 changes: 1 addition & 1 deletion .forge-snapshots/addLiquidity.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
192821
192998
Original file line number Diff line number Diff line change
@@ -1 +1 @@
184735
185335
2 changes: 1 addition & 1 deletion .forge-snapshots/cached dynamic fee, no hooks.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
138762
139362
2 changes: 1 addition & 1 deletion .forge-snapshots/donate gas with 1 token.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
131854
132225
2 changes: 1 addition & 1 deletion .forge-snapshots/donate gas with 2 tokens.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
176958
177402
1 change: 1 addition & 0 deletions .forge-snapshots/mintWithEmptyHookEOAInitiated.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
247213
1 change: 1 addition & 0 deletions .forge-snapshots/modify position with noop.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
54090
2 changes: 1 addition & 1 deletion .forge-snapshots/poolManager bytecode size.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
23381
23459
2 changes: 1 addition & 1 deletion .forge-snapshots/removeLiquidity with empty hook.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
99632
99591
2 changes: 1 addition & 1 deletion .forge-snapshots/removeLiquidity with native token.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
201005
200964
2 changes: 1 addition & 1 deletion .forge-snapshots/removeLiquidity.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
197282
197241
2 changes: 1 addition & 1 deletion .forge-snapshots/simple swap with native.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
187335
187932
2 changes: 1 addition & 1 deletion .forge-snapshots/simple swap.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
195872
196472
1 change: 1 addition & 0 deletions .forge-snapshots/simpleSwapEOAInitiated.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
167083
1 change: 1 addition & 0 deletions .forge-snapshots/simpleSwapNativeEOAInitiated.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
165611
Original file line number Diff line number Diff line change
@@ -1 +1 @@
117998
118595
2 changes: 1 addition & 1 deletion .forge-snapshots/swap against liquidity.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
105462
106062
2 changes: 1 addition & 1 deletion .forge-snapshots/swap burn 6909 for input.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
125651
126218
2 changes: 1 addition & 1 deletion .forge-snapshots/swap burn native 6909 for input.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
121604
122171
2 changes: 1 addition & 1 deletion .forge-snapshots/swap mint native output as 6909.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
189861
190494
2 changes: 1 addition & 1 deletion .forge-snapshots/swap mint output as 6909.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
206666
207299
2 changes: 1 addition & 1 deletion .forge-snapshots/swap with dynamic fee.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
183836
184436
2 changes: 1 addition & 1 deletion .forge-snapshots/swap with hooks.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
105440
106040
2 changes: 1 addition & 1 deletion .forge-snapshots/update dynamic fee in before swap.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
190540
191140
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ A more detailed description of Uniswap v4 Core can be found in the draft of the
- `settle`
- `mint`

Only the net balances owed to the pool (positive) or to the user (negative) are tracked throughout the duration of a lock. This is the `delta` field held in the lock state. Any number of actions can be run on the pools, as long as the deltas accumulated during the lock reach 0 by the lock’s release. This lock and call style architecture gives callers maximum flexibility in integrating with the core code.
Only the net balances owed to the user (positive) or to the pool (negative) are tracked throughout the duration of a lock. This is the `delta` field held in the lock state. Any number of actions can be run on the pools, as long as the deltas accumulated during the lock reach 0 by the lock’s release. This lock and call style architecture gives callers maximum flexibility in integrating with the core code.

Additionally, a pool may be initialized with a hook contract, that can implement any of the following callbacks in the lifecycle of pool actions:

Expand Down
15 changes: 8 additions & 7 deletions src/PoolManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -222,16 +222,16 @@ contract PoolManager is IPoolManager, Fees, NoDelegateCall, ERC6909Claims {
);

_accountPoolBalanceDelta(key, delta);
// the fee is on the input currency

// the fee is on the input currency
unchecked {
if (feeForProtocol > 0) {
protocolFeesAccrued[params.zeroForOne ? key.currency0 : key.currency1] += feeForProtocol;
}
}

emit Swap(
id, msg.sender, delta.amount0(), delta.amount1(), state.sqrtPriceX96, state.liquidity, state.tick, swapFee
id, msg.sender, -delta.amount0(), -delta.amount1(), state.sqrtPriceX96, state.liquidity, state.tick, swapFee
hensha256 marked this conversation as resolved.
Show resolved Hide resolved
);

key.hooks.afterSwap(key, params, delta, hookData);
Expand Down Expand Up @@ -259,7 +259,8 @@ contract PoolManager is IPoolManager, Fees, NoDelegateCall, ERC6909Claims {

/// @inheritdoc IPoolManager
function take(Currency currency, address to, uint256 amount) external override noDelegateCall isLocked {
_accountDelta(currency, amount.toInt128());
// flipping a postive number in an int is always safe
hensha256 marked this conversation as resolved.
Show resolved Hide resolved
_accountDelta(currency, -(amount.toInt128()));
reservesOf[currency] -= amount;
currency.transfer(to, amount);
}
Expand All @@ -269,19 +270,19 @@ contract PoolManager is IPoolManager, Fees, NoDelegateCall, ERC6909Claims {
uint256 reservesBefore = reservesOf[currency];
reservesOf[currency] = currency.balanceOfSelf();
paid = reservesOf[currency] - reservesBefore;
// subtraction must be safe
_accountDelta(currency, -(paid.toInt128()));
_accountDelta(currency, paid.toInt128());
}

/// @inheritdoc IPoolManager
function mint(address to, uint256 id, uint256 amount) external override noDelegateCall isLocked {
_accountDelta(CurrencyLibrary.fromId(id), amount.toInt128());
// flipping a postive number in an int is always safe
hensha256 marked this conversation as resolved.
Show resolved Hide resolved
_accountDelta(CurrencyLibrary.fromId(id), -(amount.toInt128()));
_mint(to, id, amount);
}

/// @inheritdoc IPoolManager
function burn(address from, uint256 id, uint256 amount) external override noDelegateCall isLocked {
_accountDelta(CurrencyLibrary.fromId(id), -(amount.toInt128()));
_accountDelta(CurrencyLibrary.fromId(id), amount.toInt128());
_burnFrom(from, id, amount);
}

Expand Down
19 changes: 10 additions & 9 deletions src/libraries/Pool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ library Pool {
// current tick is below the passed range; liquidity can only become in range by crossing from left to
// right, when we'll need _more_ currency0 (it's becoming more valuable) so user must provide it
result = result
hensha256 marked this conversation as resolved.
Show resolved Hide resolved
+ toBalanceDelta(
- toBalanceDelta(
SqrtPriceMath.getAmount0Delta(
TickMath.getSqrtRatioAtTick(params.tickLower),
TickMath.getSqrtRatioAtTick(params.tickUpper),
Expand All @@ -220,7 +220,7 @@ library Pool {
);
} else if (self.slot0.tick < params.tickUpper) {
result = result
+ toBalanceDelta(
- toBalanceDelta(
SqrtPriceMath.getAmount0Delta(
self.slot0.sqrtPriceX96, TickMath.getSqrtRatioAtTick(params.tickUpper), params.liquidityDelta
).toInt128(),
Expand All @@ -236,7 +236,7 @@ library Pool {
// current tick is above the passed range; liquidity can only become in range by crossing from right to
// left, when we'll need _more_ currency1 (it's becoming more valuable) so user must provide it
result = result
+ toBalanceDelta(
- toBalanceDelta(
0,
SqrtPriceMath.getAmount1Delta(
TickMath.getSqrtRatioAtTick(params.tickLower),
Expand All @@ -248,7 +248,7 @@ library Pool {
}

// Fees earned from LPing are removed from the pool balance.
hensha256 marked this conversation as resolved.
Show resolved Hide resolved
result = result - toBalanceDelta(feesOwed0.toInt128(), feesOwed1.toInt128());
result = result + toBalanceDelta(feesOwed0.toInt128(), feesOwed1.toInt128());
}

struct SwapCache {
Expand Down Expand Up @@ -444,16 +444,17 @@ library Pool {
self.feeGrowthGlobal1X128 = state.feeGrowthGlobalX128;
}

// in an unchecked block so sign flipping is not safe without a helper function
hensha256 marked this conversation as resolved.
Show resolved Hide resolved
unchecked {
if (params.zeroForOne == exactInput) {
result = toBalanceDelta(
(params.amountSpecified - state.amountSpecifiedRemaining).toInt128(),
state.amountCalculated.toInt128()
(params.amountSpecified - state.amountSpecifiedRemaining).toInt128().flipSign(),
hensha256 marked this conversation as resolved.
Show resolved Hide resolved
state.amountCalculated.toInt128().flipSign()
);
} else {
result = toBalanceDelta(
state.amountCalculated.toInt128(),
(params.amountSpecified - state.amountSpecifiedRemaining).toInt128()
state.amountCalculated.toInt128().flipSign(),
(params.amountSpecified - state.amountSpecifiedRemaining).toInt128().flipSign()
);
}
}
Expand All @@ -462,7 +463,7 @@ library Pool {
/// @notice Donates the given amount of currency0 and currency1 to the pool
function donate(State storage state, uint256 amount0, uint256 amount1) internal returns (BalanceDelta delta) {
if (state.liquidity == 0) revert NoLiquidityToReceiveFees();
delta = toBalanceDelta(amount0.toInt128(), amount1.toInt128());
delta = toBalanceDelta(-(amount0.toInt128()), -(amount1.toInt128()));
unchecked {
if (amount0 > 0) {
state.feeGrowthGlobal0X128 += FullMath.mulDiv(amount0, FixedPoint128.Q128, state.liquidity);
Expand Down
8 changes: 8 additions & 0 deletions src/libraries/SafeCast.sol
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,12 @@ library SafeCast {
if (y > uint128(type(int128).max)) revert SafeCastOverflow();
z = int128(int256(y));
}

/// @notice Flip the sign of an int128, revert on overflow
/// @param y The int128 to flip the sign of
/// @return The flipped integer, still an int128
function flipSign(int128 y) internal pure returns (int128) {
// this is not unchecked, so it will revert if y==type(int128).min
return -y;
}
}
2 changes: 1 addition & 1 deletion src/test/PoolClaimsTest.sol
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ contract PoolClaimsTest is PoolTestBase {

if (data.deposit) {
manager.mint(data.user, data.currency.toId(), uint128(data.amount));
_settle(data.currency, data.user, data.amount.toInt128(), true);
_settle(data.currency, data.user, -data.amount.toInt128(), true);
} else {
manager.burn(data.user, data.currency.toId(), uint128(data.amount));
_take(data.currency, data.user, data.amount.toInt128(), true);
Expand Down
16 changes: 7 additions & 9 deletions src/test/PoolDonateTest.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,10 @@ import {IPoolManager} from "../interfaces/IPoolManager.sol";
import {PoolKey} from "../types/PoolKey.sol";
import {BalanceDelta, BalanceDeltaLibrary} from "../types/BalanceDelta.sol";
import {PoolTestBase} from "./PoolTestBase.sol";
import {Test} from "forge-std/Test.sol";
import {IHooks} from "../interfaces/IHooks.sol";
import {Hooks} from "../libraries/Hooks.sol";

contract PoolDonateTest is PoolTestBase, Test {
contract PoolDonateTest is PoolTestBase {
using CurrencyLibrary for Currency;
using Hooks for IHooks;

Expand Down Expand Up @@ -59,14 +58,13 @@ contract PoolDonateTest is PoolTestBase, Test {

assertEq(reserveBefore0, reserveAfter0);
assertEq(reserveBefore1, reserveAfter1);
assertEq(deltaAfter0, -int256(data.amount0));
assertEq(deltaAfter1, -int256(data.amount1));

assertEq(deltaAfter0, int256(data.amount0));
assertEq(deltaAfter1, int256(data.amount1));

if (deltaAfter0 > 0) _settle(data.key.currency0, data.sender, int128(deltaAfter0), true);
if (deltaAfter1 > 0) _settle(data.key.currency1, data.sender, int128(deltaAfter1), true);
if (deltaAfter0 < 0) _take(data.key.currency0, data.sender, int128(deltaAfter0), true);
if (deltaAfter1 < 0) _take(data.key.currency1, data.sender, int128(deltaAfter1), true);
if (deltaAfter0 < 0) _settle(data.key.currency0, data.sender, int128(deltaAfter0), true);
if (deltaAfter1 < 0) _settle(data.key.currency1, data.sender, int128(deltaAfter1), true);
if (deltaAfter0 > 0) _take(data.key.currency0, data.sender, int128(deltaAfter0), true);
if (deltaAfter1 > 0) _take(data.key.currency1, data.sender, int128(deltaAfter1), true);

return abi.encode(delta);
}
Expand Down
12 changes: 6 additions & 6 deletions src/test/PoolModifyLiquidityTest.sol
Original file line number Diff line number Diff line change
Expand Up @@ -65,18 +65,18 @@ contract PoolModifyLiquidityTest is Test, PoolTestBase {
(,,, int256 delta0) = _fetchBalances(data.key.currency0, data.sender, address(this));
(,,, int256 delta1) = _fetchBalances(data.key.currency1, data.sender, address(this));

if (data.params.liquidityDelta > 0) {
if (data.params.liquidityDelta < 0) {
assert(delta0 > 0 || delta1 > 0);
assert(!(delta0 < 0 || delta1 < 0));
} else if (data.params.liquidityDelta < 0) {
} else if (data.params.liquidityDelta > 0) {
assert(delta0 < 0 || delta1 < 0);
assert(!(delta0 > 0 || delta1 > 0));
}

if (delta0 > 0) _settle(data.key.currency0, data.sender, int128(delta0), data.settleUsingTransfer);
if (delta1 > 0) _settle(data.key.currency1, data.sender, int128(delta1), data.settleUsingTransfer);
if (delta0 < 0) _take(data.key.currency0, data.sender, int128(delta0), data.withdrawTokens);
if (delta1 < 0) _take(data.key.currency1, data.sender, int128(delta1), data.withdrawTokens);
if (delta0 < 0) _settle(data.key.currency0, data.sender, int128(delta0), data.settleUsingTransfer);
if (delta1 < 0) _settle(data.key.currency1, data.sender, int128(delta1), data.settleUsingTransfer);
if (delta0 > 0) _take(data.key.currency0, data.sender, int128(delta0), data.withdrawTokens);
if (delta1 > 0) _take(data.key.currency1, data.sender, int128(delta1), data.withdrawTokens);

return abi.encode(delta);
}
Expand Down
12 changes: 6 additions & 6 deletions src/test/PoolNestedActionsTest.sol
Original file line number Diff line number Diff line change
Expand Up @@ -124,8 +124,8 @@ contract NestedActionExecutor is Test, PoolTestBase {

assertEq(deltaLockerBefore0, deltaLockerAfter0, "Locker delta 0");
assertEq(deltaLockerBefore1, deltaLockerAfter1, "Locker delta 1");
assertEq(deltaThisBefore0 + SWAP_PARAMS.amountSpecified, deltaThisAfter0, "Executor delta 0");
assertEq(deltaThisBefore1 - 98, deltaThisAfter1, "Executor delta 1");
assertEq(deltaThisBefore0 - SWAP_PARAMS.amountSpecified, deltaThisAfter0, "Executor delta 0");
assertEq(deltaThisBefore1 + 98, deltaThisAfter1, "Executor delta 1");
assertEq(delta.amount0(), deltaThisAfter0, "Swap delta 0");
assertEq(delta.amount1(), deltaThisAfter1, "Swap delta 1");

Expand Down Expand Up @@ -202,10 +202,10 @@ contract NestedActionExecutor is Test, PoolTestBase {

assertEq(deltaLockerBefore0, deltaLockerAfter0, "Locker delta 0");
assertEq(deltaLockerBefore1, deltaLockerAfter1, "Locker delta 1");
assertEq(deltaThisBefore0 + int256(DONATE_AMOUNT0), deltaThisAfter0, "Executor delta 0");
assertEq(deltaThisBefore1 + int256(DONATE_AMOUNT1), deltaThisAfter1, "Executor delta 1");
assertEq(delta.amount0(), int256(DONATE_AMOUNT0), "Donate delta 0");
assertEq(delta.amount1(), int256(DONATE_AMOUNT1), "Donate delta 1");
assertEq(deltaThisBefore0 - int256(DONATE_AMOUNT0), deltaThisAfter0, "Executor delta 0");
assertEq(deltaThisBefore1 - int256(DONATE_AMOUNT1), deltaThisAfter1, "Executor delta 1");
assertEq(-delta.amount0(), int256(DONATE_AMOUNT0), "Donate delta 0");
assertEq(-delta.amount1(), int256(DONATE_AMOUNT1), "Donate delta 1");

_settle(key.currency0, user, int128(deltaThisAfter0), true);
_settle(key.currency1, user, int128(deltaThisAfter1), true);
Expand Down
Loading
Loading