Skip to content

Commit

Permalink
Add transfer support (#218)
Browse files Browse the repository at this point in the history
* move to a base actions router

* use one Planner file

* msgSender

* add transfer

* add transfer test

* use delta saving hook to get deltas

* remove return values

* add burn comment

* make gas snapshots more accurate, remove hook

* move to 1 planner, fix merge conf

* sweep currency, pr comments

* rename, add liquidityDelta return param

* rename

* comment

* gas check

* add gas test, using uint256

* gas check, using 0

* comments

* remove SafeCallback

* remove FULL_DELTA

* move helpers to delta resolver

* remove import

* increase liq with sttle with balance test

---------

Co-authored-by: Alice Henshaw <henshawalice@gmail.com>
Co-authored-by: Alice <34962750+hensha256@users.noreply.github.com>
  • Loading branch information
3 people authored Jul 31, 2024
1 parent f37734c commit b065dcd
Show file tree
Hide file tree
Showing 42 changed files with 243 additions and 75 deletions.
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
2 changes: 1 addition & 1 deletion .forge-snapshots/V4Router_Bytecode.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
6595
6628
2 changes: 1 addition & 1 deletion .forge-snapshots/V4Router_ExactIn1Hop_nativeIn.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
120991
121090
2 changes: 1 addition & 1 deletion .forge-snapshots/V4Router_ExactIn1Hop_nativeOut.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
120158
120257
2 changes: 1 addition & 1 deletion .forge-snapshots/V4Router_ExactIn1Hop_oneForZero.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
129030
129129
2 changes: 1 addition & 1 deletion .forge-snapshots/V4Router_ExactIn1Hop_zeroForOne.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
135860
135959
2 changes: 1 addition & 1 deletion .forge-snapshots/V4Router_ExactIn2Hops.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
187212
187311
2 changes: 1 addition & 1 deletion .forge-snapshots/V4Router_ExactIn2Hops_nativeIn.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
179175
179274
2 changes: 1 addition & 1 deletion .forge-snapshots/V4Router_ExactIn3Hops.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
238593
238692
2 changes: 1 addition & 1 deletion .forge-snapshots/V4Router_ExactIn3Hops_nativeIn.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
230580
230679
2 changes: 1 addition & 1 deletion .forge-snapshots/V4Router_ExactInputSingle.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
134618
134717
2 changes: 1 addition & 1 deletion .forge-snapshots/V4Router_ExactInputSingle_nativeIn.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
119749
119848
2 changes: 1 addition & 1 deletion .forge-snapshots/V4Router_ExactInputSingle_nativeOut.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
118894
118993
Original file line number Diff line number Diff line change
@@ -1 +1 @@
126794
126893
2 changes: 1 addition & 1 deletion .forge-snapshots/V4Router_ExactOut1Hop_nativeOut.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
120999
121098
2 changes: 1 addition & 1 deletion .forge-snapshots/V4Router_ExactOut1Hop_oneForZero.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
129871
129970
2 changes: 1 addition & 1 deletion .forge-snapshots/V4Router_ExactOut1Hop_zeroForOne.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
134672
134771
2 changes: 1 addition & 1 deletion .forge-snapshots/V4Router_ExactOut2Hops.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
186633
186732
2 changes: 1 addition & 1 deletion .forge-snapshots/V4Router_ExactOut2Hops_nativeIn.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
183556
183655
2 changes: 1 addition & 1 deletion .forge-snapshots/V4Router_ExactOut3Hops.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
238638
238737
2 changes: 1 addition & 1 deletion .forge-snapshots/V4Router_ExactOut3Hops_nativeIn.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
235585
235684
2 changes: 1 addition & 1 deletion .forge-snapshots/V4Router_ExactOut3Hops_nativeOut.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
229790
229889
2 changes: 1 addition & 1 deletion .forge-snapshots/V4Router_ExactOutputSingle.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
133152
133251
Original file line number Diff line number Diff line change
@@ -1 +1 @@
125274
125373
2 changes: 1 addition & 1 deletion .forge-snapshots/V4Router_ExactOutputSingle_nativeOut.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
119537
119636
37 changes: 26 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);
} 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,21 @@ 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));
}
}
}
12 changes: 4 additions & 8 deletions src/V4Router.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import {BalanceDelta} from "@uniswap/v4-core/src/types/BalanceDelta.sol";
import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol";
import {Currency, CurrencyLibrary} from "@uniswap/v4-core/src/types/Currency.sol";
import {TickMath} from "@uniswap/v4-core/src/libraries/TickMath.sol";
import {TransientStateLibrary} from "@uniswap/v4-core/src/libraries/TransientStateLibrary.sol";

import {PathKey, PathKeyLib} from "./libraries/PathKey.sol";
import {CalldataDecoder} from "./libraries/CalldataDecoder.sol";
Expand All @@ -24,7 +23,6 @@ abstract contract V4Router is IV4Router, BaseActionsRouter, DeltaResolver {
using SafeCastTemp for *;
using PathKeyLib for PathKey;
using CalldataDecoder for bytes;
using TransientStateLibrary for IPoolManager;

constructor(IPoolManager _poolManager) BaseActionsRouter(_poolManager) {}

Expand All @@ -51,12 +49,11 @@ abstract contract V4Router is IV4Router, BaseActionsRouter, DeltaResolver {
currency := calldataload(params.offset)
}

int256 delta = poolManager.currencyDelta(address(this), currency);
if (delta > 0) revert InvalidDeltaForAction();
uint256 amount = _getFullSettleAmount(currency);

// TODO support address(this) paying too
// TODO should it have a maxAmountOut added slippage protection?
_settle(currency, _msgSender(), uint256(-delta));
_settle(currency, _msgSender(), amount);
} else if (action == Actions.TAKE_ALL) {
// equivalent: abi.decode(params, (Currency, address))
Currency currency;
Expand All @@ -66,12 +63,11 @@ abstract contract V4Router is IV4Router, BaseActionsRouter, DeltaResolver {
recipient := calldataload(add(params.offset, 0x20))
}

int256 delta = poolManager.currencyDelta(address(this), currency);
if (delta < 0) revert InvalidDeltaForAction();
uint256 amount = _getFullTakeAmount(currency);

// TODO should _take have a minAmountOut added slippage check?
// TODO recipient mapping
_take(currency, recipient, uint256(delta));
_take(currency, recipient, amount);
} else {
revert UnsupportedAction(action);
}
Expand Down
24 changes: 22 additions & 2 deletions src/base/DeltaResolver.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,19 @@
pragma solidity ^0.8.24;

import {Currency} from "@uniswap/v4-core/src/types/Currency.sol";
import {TransientStateLibrary} from "@uniswap/v4-core/src/libraries/TransientStateLibrary.sol";
import {IPoolManager} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol";
import {ImmutableState} from "./ImmutableState.sol";

/// @notice Abstract contract used to sync, send, and settle funds to the pool manager
/// @dev Note that sync() is called before any erc-20 transfer in `settle`.
abstract contract DeltaResolver is ImmutableState {
/// @notice Emitted trying to settle a positive delta, or take a negative delta
error InvalidDeltaForAction();
using TransientStateLibrary for IPoolManager;

/// @notice Emitted trying to settle a positive delta.
error IncorrectUseOfSettle();
/// @notice Emitted trying to take a negative delta.
error IncorrectUseOfTake();

/// @notice Take an amount of currency out of the PoolManager
/// @param currency Currency to take
Expand Down Expand Up @@ -39,4 +45,18 @@ abstract contract DeltaResolver is ImmutableState {
/// @param payer The address who should pay tokens
/// @param amount The number of tokens to send
function _pay(Currency token, address payer, uint256 amount) internal virtual;

function _getFullSettleAmount(Currency currency) internal view returns (uint256 amount) {
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);
}

function _getFullTakeAmount(Currency currency) internal view returns (uint256 amount) {
int256 _amount = poolManager.currencyDelta(address(this), currency);
// If the amount is negative, it should be settled not taken.
if (_amount < 0) revert IncorrectUseOfTake();
amount = uint256(_amount);
}
}
36 changes: 20 additions & 16 deletions src/libraries/Actions.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,31 +5,35 @@ pragma solidity ^0.8.24;
/// @dev These are suggested common commands, however additional commands should be defined as required
library Actions {
// pool actions
// liquidity actions
uint256 constant INCREASE_LIQUIDITY = 0x00;
uint256 constant DECREASE_LIQUIDITY = 0x01;
uint256 constant SWAP_EXACT_IN_SINGLE = 0x02;
uint256 constant SWAP_EXACT_IN = 0x03;
uint256 constant SWAP_EXACT_OUT_SINGLE = 0x04;
uint256 constant SWAP_EXACT_OUT = 0x05;
uint256 constant DONATE = 0x06;
uint256 constant MINT_POSITION = 0x02;
uint256 constant BURN_POSITION = 0x03;
// swapping
uint256 constant SWAP_EXACT_IN_SINGLE = 0x04;
uint256 constant SWAP_EXACT_IN = 0x05;
uint256 constant SWAP_EXACT_OUT_SINGLE = 0x06;
uint256 constant SWAP_EXACT_OUT = 0x07;
// donate
uint256 constant DONATE = 0x08;

// closing deltas on the pool manager
// settling
uint256 constant SETTLE = 0x10;
uint256 constant SETTLE_ALL = 0x11;
uint256 constant SETTLE_WITH_BALANCE = 0x12;
// taking
uint256 constant TAKE = 0x13;
uint256 constant TAKE_ALL = 0x14;
uint256 constant TAKE_PORTION = 0x15;

uint256 constant TAKE = 0x12;
uint256 constant TAKE_ALL = 0x13;
uint256 constant TAKE_PORTION = 0x14;

uint256 constant CLOSE_CURRENCY = 0x15;
uint256 constant CLOSE_PAIR = 0x16;
uint256 constant CLEAR = 0x17;
uint256 constant CLOSE_CURRENCY = 0x16;
uint256 constant CLOSE_PAIR = 0x17;
uint256 constant CLEAR = 0x18;
uint256 constant SWEEP = 0x19;

// minting/burning 6909s to close deltas
uint256 constant MINT_6909 = 0x20;
uint256 constant BURN_6909 = 0x21;

// mint + burn ERC721 position
uint256 constant MINT_POSITION = 0x22;
uint256 constant BURN_POSITION = 0x23;
}
Loading

0 comments on commit b065dcd

Please sign in to comment.