Skip to content

Commit

Permalink
merge main
Browse files Browse the repository at this point in the history
  • Loading branch information
hensha256 committed Sep 4, 2024
2 parents 2961983 + 9c94b1a commit c716901
Show file tree
Hide file tree
Showing 28 changed files with 136 additions and 54 deletions.
Original file line number Diff line number Diff line change
@@ -1 +1 @@
144929
144627
2 changes: 1 addition & 1 deletion .forge-snapshots/addLiquidity CA fee.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
171218
170916
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 @@
274496
274194
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 @@
135271
135120
Original file line number Diff line number Diff line change
@@ -1 +1 @@
293121
292819
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 @@
106472
106321
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 @@
146038
145736
2 changes: 1 addition & 1 deletion .forge-snapshots/poolManager bytecode size.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
24119
24044
Original file line number Diff line number Diff line change
@@ -1 +1 @@
98989
98838
2 changes: 1 addition & 1 deletion .forge-snapshots/simple addLiquidity.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
161534
161383
2 changes: 1 addition & 1 deletion .forge-snapshots/simple swap.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
123337
123186
2 changes: 1 addition & 1 deletion .forge-snapshots/swap CA custom curve + swap noop.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
127123
126821
2 changes: 1 addition & 1 deletion .forge-snapshots/swap CA fee on unspecified.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
154782
154631
2 changes: 1 addition & 1 deletion .forge-snapshots/swap against liquidity.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
116709
116558
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 @@
139754
139603
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 @@
155178
155027
Original file line number Diff line number Diff line change
@@ -1 +1 @@
206434
206132
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 @@
139358
139207
2 changes: 1 addition & 1 deletion .forge-snapshots/swap with hooks.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
132346
132195
2 changes: 1 addition & 1 deletion .forge-snapshots/swap with lp fee and protocol fee.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
169549
169398
2 changes: 1 addition & 1 deletion .forge-snapshots/swap with return dynamic fee.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
145685
145534
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 @@
147965
147814
17 changes: 11 additions & 6 deletions src/PoolManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -276,11 +276,14 @@ contract PoolManager is IPoolManager, ProtocolFees, NoDelegateCall, ERC6909Claim

/// @inheritdoc IPoolManager
function sync(Currency currency) external onlyWhenUnlocked {
CurrencyReserves.requireNotSynced();
// address(0) is used for the native currency
if (currency.isAddressZero()) return;
uint256 balance = currency.balanceOfSelf();
CurrencyReserves.syncCurrencyAndReserves(currency, balance);
if (currency.isAddressZero()) {
// The reserves balance is not used for native settling, so we only need to reset the currency.
CurrencyReserves.resetCurrency();
} else {
uint256 balance = currency.balanceOfSelf();
CurrencyReserves.syncCurrencyAndReserves(currency, balance);
}
}

/// @inheritdoc IPoolManager
Expand Down Expand Up @@ -343,17 +346,19 @@ contract PoolManager is IPoolManager, ProtocolFees, NoDelegateCall, ERC6909Claim

function _settle(address recipient) internal returns (uint256 paid) {
Currency currency = CurrencyReserves.getSyncedCurrency();
// if not previously synced, expects native currency to be settled

// if not previously synced, or the syncedCurrency slot has been reset, expects native currency to be settled
if (currency.isAddressZero()) {
paid = msg.value;
} else {
if (msg.value > 0) NonzeroNativeValue.selector.revertWith();
// Reserves are guaranteed to be set, because currency and reserves are always set together
// Reserves are guaranteed to be set because currency and reserves are always set together
uint256 reservesBefore = CurrencyReserves.getSyncedReserves();
uint256 reservesNow = currency.balanceOfSelf();
paid = reservesNow - reservesBefore;
CurrencyReserves.resetCurrency();
}

_accountDelta(currency, paid.toInt128(), recipient);
}

Expand Down
3 changes: 2 additions & 1 deletion src/interfaces/IPoolManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,8 @@ interface IPoolManager is IProtocolFees, IERC6909Claims, IExtsload, IExttload {
/// This is used to checkpoint balances for the manager and derive deltas for the caller.
/// @dev This MUST be called before any ERC20 tokens are sent into the contract, but can be skipped
/// for native tokens because the amount to settle is determined by the sent value.
/// @param currency The currency whose balance to sync
/// However, if an ERC20 token has been synced and not settled, and the caller instead wants to settle
/// native funds, this function can be called with the native currency to then be able to settle the native currency
function sync(Currency currency) external;

/// @notice Called by the user to net out some value owed to the user
Expand Down
9 changes: 0 additions & 9 deletions src/libraries/CurrencyReserves.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,11 @@ import {CustomRevert} from "./CustomRevert.sol";
library CurrencyReserves {
using CustomRevert for bytes4;

/// @notice Thrown when a user has already synced a currency, but not yet settled
error AlreadySynced();

/// bytes32(uint256(keccak256("ReservesOf")) - 1)
bytes32 constant RESERVES_OF_SLOT = 0x1e0745a7db1623981f0b2a5d4232364c00787266eb75ad546f190e6cebe9bd95;
/// bytes32(uint256(keccak256("Currency")) - 1)
bytes32 constant CURRENCY_SLOT = 0x27e098c505d44ec3574004bca052aabf76bd35004c182099d8c575fb238593b9;

function requireNotSynced() internal view {
if (!getSyncedCurrency().isAddressZero()) {
AlreadySynced.selector.revertWith();
}
}

function getSyncedCurrency() internal view returns (Currency currency) {
assembly ("memory-safe") {
currency := tload(CURRENCY_SLOT)
Expand Down
12 changes: 8 additions & 4 deletions src/test/ProxyPoolManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -141,10 +141,14 @@ contract ProxyPoolManager is IPoolManager, ProtocolFees, NoDelegateCall, ERC6909

/// @inheritdoc IPoolManager
function sync(Currency currency) public {
CurrencyReserves.requireNotSynced();
if (currency.isAddressZero()) return;
uint256 balance = currency.balanceOfSelf();
CurrencyReserves.syncCurrencyAndReserves(currency, balance);
// address(0) is used for the native currency
if (currency.isAddressZero()) {
// The reserves balance is not used for native settling, so we only need to reset the currency.
CurrencyReserves.resetCurrency();
} else {
uint256 balance = currency.balanceOfSelf();
CurrencyReserves.syncCurrencyAndReserves(currency, balance);
}
}

/// @inheritdoc IPoolManager
Expand Down
10 changes: 5 additions & 5 deletions test/PoolManager.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -1135,16 +1135,16 @@ contract PoolManagerTest is Test, Deployers, GasSnapshot {
emptyUnlockRouter.unlock();
}

Action[] actions;
Action[] _actions;

function test_unlock_cannotBeCalledTwiceByCaller() public {
actions = [Action.NESTED_SELF_UNLOCK];
nestedActionRouter.unlock(abi.encode(actions));
_actions = [Action.NESTED_SELF_UNLOCK];
nestedActionRouter.unlock(abi.encode(_actions));
}

function test_unlock_cannotBeCalledTwiceByDifferentCallers() public {
actions = [Action.NESTED_EXECUTOR_UNLOCK];
nestedActionRouter.unlock(abi.encode(actions));
_actions = [Action.NESTED_EXECUTOR_UNLOCK];
nestedActionRouter.unlock(abi.encode(_actions));
}

// function testExtsloadForPoolPrice() public {
Expand Down
95 changes: 88 additions & 7 deletions test/Sync.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {StateLibrary} from "../src/libraries/StateLibrary.sol";
import {TransientStateLibrary} from "../src/libraries/TransientStateLibrary.sol";
import {NativeERC20} from "../src/test/NativeERC20.sol";
import {IPoolManager} from "../src/interfaces/IPoolManager.sol";
import {CurrencyLibrary} from "../src/types/Currency.sol";

contract SyncTest is Test, Deployers, GasSnapshot {
using StateLibrary for IPoolManager;
Expand All @@ -40,8 +41,8 @@ contract SyncTest is Test, Deployers, GasSnapshot {
function test_sync_balanceIsZero() public {
assertEq(currency2.balanceOf(address(manager)), uint256(0));

Actions[] memory actions = new Actions[](4);
bytes[] memory params = new bytes[](4);
Actions[] memory actions = new Actions[](2);
bytes[] memory params = new bytes[](2);

actions[0] = Actions.SYNC;
params[0] = abi.encode(currency2);
Expand Down Expand Up @@ -255,15 +256,19 @@ contract SyncTest is Test, Deployers, GasSnapshot {
vm.deal(address(manager), value);
NativeERC20 nativeERC20 = new NativeERC20();

Actions[] memory actions = new Actions[](2);
bytes[] memory params = new bytes[](2);
uint256 nativeERC20Balance = nativeERC20.balanceOf(address(manager));

Actions[] memory actions = new Actions[](3);
bytes[] memory params = new bytes[](3);

actions[0] = Actions.SYNC;
params[0] = abi.encode(nativeERC20);

// Revert with NonzeroNativeValue
actions[1] = Actions.SETTLE_NATIVE;
params[1] = abi.encode(value);
actions[1] = Actions.ASSERT_RESERVES_EQUALS;
params[1] = abi.encode(nativeERC20Balance);

actions[2] = Actions.SETTLE_NATIVE;
params[2] = abi.encode(value);

vm.expectRevert(IPoolManager.NonzeroNativeValue.selector);
actionsRouter.executeActions{value: value}(actions, params);
Expand All @@ -289,4 +294,80 @@ contract SyncTest is Test, Deployers, GasSnapshot {
// uint256 balanceAfter = address(this).balance;
// assertEq(balanceAfter - balanceBefore, value);
}

function test_settle_native_afterERC20Sync_succeeds(uint256 currency2Balance, uint256 ethBalance) public {
currency2Balance = bound(currency2Balance, 1, uint256(int256(type(int128).max / 2)));
ethBalance = bound(ethBalance, 1, uint256(int256(type(int128).max / 2)));

vm.deal(address(this), ethBalance);
// ensure the reserves balance is non 0
currency2.transfer(address(manager), currency2Balance);

Actions[] memory actions = new Actions[](8);
bytes[] memory params = new bytes[](8);

actions[0] = Actions.ASSERT_RESERVES_EQUALS;
params[0] = abi.encode(0);

actions[1] = Actions.SYNC;
params[1] = abi.encode(currency2);

actions[2] = Actions.ASSERT_RESERVES_EQUALS;
params[2] = abi.encode(currency2Balance);

actions[3] = Actions.SYNC;
params[3] = abi.encode(CurrencyLibrary.ADDRESS_ZERO);

// Under the hood this is non-zero but our transient state library overrides the value if the currency is address(0)
actions[4] = Actions.ASSERT_RESERVES_EQUALS;
params[4] = abi.encode(0);

// This calls settle with a value, of ethBalance. Since the synedCurrency slot is address(0), the call should successfully apply a positive delta on the native currency.
actions[5] = Actions.SETTLE_NATIVE;
params[5] = abi.encode(ethBalance);

actions[6] = Actions.ASSERT_DELTA_EQUALS;
params[6] = abi.encode(CurrencyLibrary.ADDRESS_ZERO, address(actionsRouter), ethBalance);

// take the eth to close the deltas
actions[7] = Actions.TAKE;
params[7] = abi.encode(CurrencyLibrary.ADDRESS_ZERO, address(this), ethBalance);

actionsRouter.executeActions{value: ethBalance}(actions, params);
}

function test_settle_twice_doesNotApplyDelta(uint256 value) public {
value = bound(value, 1, uint256(int256(type(int128).max / 2)));
currency2.transfer(address(manager), value);

Actions[] memory actions = new Actions[](8);
bytes[] memory params = new bytes[](8);

actions[0] = Actions.SYNC;
params[0] = abi.encode(currency2);

actions[1] = Actions.ASSERT_RESERVES_EQUALS;
params[1] = abi.encode(value);

actions[2] = Actions.TRANSFER_FROM;
params[2] = abi.encode(currency2, address(this), address(manager), value);

// This settles the syncedCurrency, currency2.
actions[3] = Actions.SETTLE;

actions[4] = Actions.ASSERT_DELTA_EQUALS;
params[4] = abi.encode(currency2, address(actionsRouter), value);

actions[5] = Actions.TAKE;
params[5] = abi.encode(currency2, address(this), value);

// This settles the syncedCurrency, which has been cleared to address(0).
actions[6] = Actions.SETTLE;

// Calling settle on address(0) does not apply a delta when called with no value.
actions[7] = Actions.ASSERT_DELTA_EQUALS;
params[7] = abi.encode(CurrencyLibrary.ADDRESS_ZERO, address(actionsRouter), 0);

actionsRouter.executeActions(actions, params);
}
}

0 comments on commit c716901

Please sign in to comment.