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

Add transfer support #218

Merged
merged 30 commits into from
Jul 31, 2024
Merged
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
d59846b
move to a base actions router
snreynolds Jul 27, 2024
46f47b2
use one Planner file
snreynolds Jul 27, 2024
b198c30
msgSender
snreynolds Jul 27, 2024
3482b23
add transfer
snreynolds Jul 27, 2024
2cee496
add transfer test
snreynolds Jul 27, 2024
bf369e6
merge main
snreynolds Jul 29, 2024
494f7d6
use delta saving hook to get deltas
snreynolds Jul 29, 2024
0449cac
remove return values
snreynolds Jul 29, 2024
47bbc6d
add burn comment
snreynolds Jul 29, 2024
7c911cb
make gas snapshots more accurate, remove hook
snreynolds Jul 29, 2024
5f26936
merge main
snreynolds Jul 29, 2024
03cf453
move to 1 planner, fix merge conf
snreynolds Jul 29, 2024
2aba16d
sweep currency, pr comments
snreynolds Jul 30, 2024
486617e
merge updates from use-actions-router
snreynolds Jul 30, 2024
b3aa874
rename, add liquidityDelta return param
snreynolds Jul 30, 2024
6885931
rename
snreynolds Jul 30, 2024
a60f16d
comment
snreynolds Jul 30, 2024
7ac7043
gas check
snreynolds Jul 30, 2024
64897c3
add gas test, using uint256
snreynolds Jul 30, 2024
b0b1b73
gas check, using 0
snreynolds Jul 30, 2024
bdfc04d
comments
snreynolds Jul 30, 2024
767605d
Merge branch 'main' into use-actions-router
snreynolds Jul 30, 2024
68e3a66
remove SafeCallback
snreynolds Jul 30, 2024
9d403c3
merge actions router
snreynolds Jul 30, 2024
0778130
remove FULL_DELTA
snreynolds Jul 30, 2024
9087916
merge main
snreynolds Jul 30, 2024
03e978f
move helpers to delta resolver
snreynolds Jul 30, 2024
dedf310
remove import
snreynolds Jul 30, 2024
428db01
increase liq with sttle with balance test
hensha256 Jul 31, 2024
858707e
Merge branch 'main' into add-transfer-support
hensha256 Jul 31, 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
Original file line number Diff line number Diff line change
@@ -1 +1 @@
152726
152736
Original file line number Diff line number Diff line change
@@ -1 +1 @@
134609
134536
2 changes: 1 addition & 1 deletion .forge-snapshots/PositionManager_mint.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
372827
372837
2 changes: 1 addition & 1 deletion .forge-snapshots/PositionManager_mint_native.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
337610
337537
2 changes: 1 addition & 1 deletion .forge-snapshots/PositionManager_mint_nativeWithSweep.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
344548
346399
2 changes: 1 addition & 1 deletion .forge-snapshots/PositionManager_mint_onSameTickLower.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
315509
315519
2 changes: 1 addition & 1 deletion .forge-snapshots/PositionManager_mint_onSameTickUpper.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
316151
316161
2 changes: 1 addition & 1 deletion .forge-snapshots/PositionManager_mint_sameRange.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
241733
241743
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
371451
Original file line number Diff line number Diff line change
@@ -1 +1 @@
321527
321537
Original file line number Diff line number Diff line change
@@ -1 +1 @@
417163
417173
44 changes: 33 additions & 11 deletions src/PositionManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,10 @@ contract PositionManager is
} else if (action == Actions.BURN_POSITION) {
// Will automatically decrease liquidity to 0 if the position is not already empty.
_burn(params);
} else if (action == Actions.SETTLE_WITH_BALANCE) {
_settleWithBalance(params);
} else if (action == Actions.SWEEP) {
_sweep(params);
} else {
revert UnsupportedAction(action);
}
Expand Down Expand Up @@ -146,14 +150,20 @@ contract PositionManager is
address caller = _msgSender();
if (currencyDelta < 0) {
_settle(currency, caller, uint256(-currencyDelta));

// if there are native tokens left over after settling, return to locker
if (currency.isNative()) _sweepNativeToken(caller);
hensha256 marked this conversation as resolved.
Show resolved Hide resolved
} else if (currencyDelta > 0) {
_take(currency, caller, uint256(currencyDelta));
}
}

/// @param params is an encoding of Currency
/// @dev uses this addresses balance to settle a negative delta
function _settleWithBalance(bytes memory params) internal {
Currency currency = abi.decode(params, (Currency));

// set the payer to this address, performs a transfer.
_settle(currency, address(this), _getFullSettleAmount(currency));
}

/// @param params is an encoding of uint256 tokenId, PositionConfig memory config, bytes hookData
/// @dev this is overloaded with ERC721Permit._burn
function _burn(bytes memory params) internal {
Expand Down Expand Up @@ -202,16 +212,28 @@ contract PositionManager is
liquidity = poolManager.getPositionLiquidity(config.poolKey.toId(), positionId);
}

/// @dev Send excess native tokens back to the recipient (locker)
/// @param recipient the receiver of the excess native tokens. Should be the caller, the one that sent the native tokens
function _sweepNativeToken(address recipient) internal {
uint256 nativeBalance = address(this).balance;
if (nativeBalance > 0) recipient.safeTransferETH(nativeBalance);
/// @notice Sweeps the entire contract balance of specified currency to the recipient
/// @param params an encoding of Currency, address
function _sweep(bytes calldata params) internal {
(Currency currency, address to) = abi.decode(params, (Currency, address));
uint256 balance = currency.balanceOfSelf();
if (balance > 0) currency.transfer(to, balance);
}

// implementation of abstract function DeltaResolver._pay
function _pay(Currency token, address payer, uint256 amount) internal override {
// TODO: Should we also support direct transfer?
permit2.transferFrom(payer, address(poolManager), uint160(amount), Currency.unwrap(token));
function _pay(Currency currency, address payer, uint256 amount) internal override {
if (payer == address(this)) {
// TODO: currency is guaranteed to not be eth so the native check in transfer is not optimal.
currency.transfer(address(poolManager), amount);
} else {
permit2.transferFrom(payer, address(poolManager), uint160(amount), Currency.unwrap(currency));
}
}

function _getFullSettleAmount(Currency currency) private view returns (uint256 amount) {
hensha256 marked this conversation as resolved.
Show resolved Hide resolved
int256 _amount = poolManager.currencyDelta(address(this), currency);
// If the amount is positive, it should be taken not settled for.
if (_amount > 0) revert IncorrectUseOfSettle();
amount = uint256(-_amount);
}
}
1 change: 1 addition & 0 deletions src/interfaces/IPositionManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol";
interface IPositionManager {
error NotApproved(address caller);
error DeadlinePassed();
error IncorrectUseOfSettle();
error IncorrectPositionConfigForTokenId(uint256 tokenId);

/// @notice Maps the ERC721 tokenId to a configId, which is a keccak256 hash of the position's pool key, and range (tickLower, tickUpper)
Expand Down
4 changes: 4 additions & 0 deletions src/libraries/Actions.sol
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,8 @@ library Actions {
// mint + burn ERC721 position
uint256 constant MINT_POSITION = 0x22;
hensha256 marked this conversation as resolved.
Show resolved Hide resolved
uint256 constant BURN_POSITION = 0x23;

// settle using the balance contract
uint256 constant SETTLE_WITH_BALANCE = 0x24;
uint256 constant SWEEP = 0x25;
}
38 changes: 38 additions & 0 deletions test/position-managers/IncreaseLiquidity.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -338,4 +338,42 @@ contract IncreaseLiquidityTest is Test, PosmTestSetup, Fuzzers {
);
}
}

function test_mint_settleWithBalance() public {
Copy link
Contributor

Choose a reason for hiding this comment

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

can you also test test_increaseLiquidity_settleWithBalance()

uint256 liquidityAlice = 3_000e18;

Plan memory planner = Planner.init();
planner.add(Actions.MINT_POSITION, abi.encode(config, liquidityAlice, alice, ZERO_BYTES));
planner.add(Actions.SETTLE_WITH_BALANCE, abi.encode(currency0));
planner.add(Actions.SETTLE_WITH_BALANCE, abi.encode(currency1));
planner.add(Actions.SWEEP, abi.encode(currency0, address(this)));
planner.add(Actions.SWEEP, abi.encode(currency1, address(this)));

uint256 balanceBefore0 = currency0.balanceOf(address(this));
uint256 balanceBefore1 = currency1.balanceOf(address(this));

assertEq(currency0.balanceOf(address(lpm)), 0);
assertEq(currency0.balanceOf(address(lpm)), 0);

currency0.transfer(address(lpm), 100e18);
currency1.transfer(address(lpm), 100e18);

assertEq(currency0.balanceOf(address(lpm)), 100e18);
assertEq(currency0.balanceOf(address(lpm)), 100e18);

bytes memory calls = planner.encode();

vm.prank(alice);
lpm.modifyLiquidities(calls, _deadline);
BalanceDelta delta = getLastDelta();
uint256 amount0 = uint128(-delta.amount0());
uint256 amount1 = uint128(-delta.amount1());

// The balances were swept back to this address.
assertEq(IERC20(Currency.unwrap(currency0)).balanceOf(address(lpm)), 0);
assertEq(IERC20(Currency.unwrap(currency1)).balanceOf(address(lpm)), 0);

assertEq(currency0.balanceOf(address(this)), balanceBefore0 - amount0);
hensha256 marked this conversation as resolved.
Show resolved Hide resolved
assertEq(currency1.balanceOf(address(this)), balanceBefore1 - amount1);
}
}
19 changes: 17 additions & 2 deletions test/position-managers/NativeToken.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,15 @@ contract PositionManagerTest is Test, PosmTestSetup, LiquidityFuzzers {
uint256 balance1Before = currency1.balanceOfSelf();

uint256 tokenId = lpm.nextTokenId();
bytes memory calls = getMintEncoded(config, liquidityToAdd, address(this), ZERO_BYTES);

Plan memory planner = Planner.init();
planner.add(Actions.MINT_POSITION, abi.encode(config, liquidityToAdd, address(this), ZERO_BYTES));
planner.add(Actions.CLOSE_CURRENCY, abi.encode(nativeKey.currency0));
planner.add(Actions.CLOSE_CURRENCY, abi.encode(nativeKey.currency1));
// sweep the excess eth
planner.add(Actions.SWEEP, abi.encode(currency0, address(this)));

bytes memory calls = planner.encode();

(uint256 amount0,) = LiquidityAmounts.getAmountsForLiquidity(
SQRT_PRICE_1_1,
Expand Down Expand Up @@ -301,7 +309,14 @@ contract PositionManagerTest is Test, PosmTestSetup, LiquidityFuzzers {
uint128(liquidityToAdd)
);

bytes memory calls = getIncreaseEncoded(tokenId, config, liquidityToAdd, ZERO_BYTES); // double the liquidity
Plan memory planner = Planner.init();
planner.add(Actions.INCREASE_LIQUIDITY, abi.encode(tokenId, config, liquidityToAdd, ZERO_BYTES));
planner.add(Actions.CLOSE_CURRENCY, abi.encode(nativeKey.currency0));
planner.add(Actions.CLOSE_CURRENCY, abi.encode(nativeKey.currency1));
// sweep the excess eth
planner.add(Actions.SWEEP, abi.encode(currency0, address(this)));
bytes memory calls = planner.encode();

lpm.modifyLiquidities{value: amount0 * 2}(calls, _deadline); // overpay on increase liquidity
BalanceDelta delta = getLastDelta();

Expand Down
28 changes: 27 additions & 1 deletion test/position-managers/PositionManager.gas.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -376,7 +376,13 @@ contract PosMGasTest is Test, PosmTestSetup, GasSnapshot {

function test_gas_mint_native_excess() public {
uint256 liquidityToAdd = 10_000 ether;
bytes memory calls = getMintEncoded(configNative, liquidityToAdd, address(this), ZERO_BYTES);
Copy link
Contributor

Choose a reason for hiding this comment

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

if you wanted to keep using the liquidity operations helpers you could always have it check if one of the currencies is native and add a sweep automatically? like in finalizeML if its a mint/increase

Copy link
Contributor

Choose a reason for hiding this comment

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

but dont feel strongly about it if you dnt want to

Copy link
Member Author

Choose a reason for hiding this comment

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

eh its fine for now, can maybe add it later


Plan memory planner = Planner.init();
planner.add(Actions.MINT_POSITION, abi.encode(configNative, liquidityToAdd, address(this), ZERO_BYTES));
planner.add(Actions.CLOSE_CURRENCY, abi.encode(nativeKey.currency0));
planner.add(Actions.CLOSE_CURRENCY, abi.encode(nativeKey.currency1));
planner.add(Actions.SWEEP, abi.encode(CurrencyLibrary.NATIVE, address(this)));
bytes memory calls = planner.encode();

(uint256 amount0,) = LiquidityAmounts.getAmountsForLiquidity(
SQRT_PRICE_1_1,
Expand Down Expand Up @@ -466,4 +472,24 @@ contract PosMGasTest is Test, PosmTestSetup, GasSnapshot {
lpm.modifyLiquidities(calls, _deadline);
snapLastCall("PositionManager_decrease_burnEmpty_native");
}

function test_gas_mint_settleWithBalance_sweep() public {
uint256 liquidityAlice = 3_000e18;

Plan memory planner = Planner.init();
planner.add(Actions.MINT_POSITION, abi.encode(config, liquidityAlice, alice, ZERO_BYTES));
planner.add(Actions.SETTLE_WITH_BALANCE, abi.encode(currency0));
planner.add(Actions.SETTLE_WITH_BALANCE, abi.encode(currency1));
planner.add(Actions.SWEEP, abi.encode(currency0, address(this)));
planner.add(Actions.SWEEP, abi.encode(currency1, address(this)));

currency0.transfer(address(lpm), 100e18);
currency1.transfer(address(lpm), 100e18);

bytes memory calls = planner.encode();

vm.prank(alice);
lpm.modifyLiquidities(calls, _deadline);
snapLastCall("PositionManager_mint_settleWithBalance_sweep");
}
}
Loading