@@ -3,68 +3,91 @@ pragma solidity ^0.8.24;
3
3
4
4
import {CurrencyLibrary, Currency} from "@uniswap/v4-core/src/types/Currency.sol " ;
5
5
import {IPoolManager} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol " ;
6
- import {BalanceDelta} from "@uniswap/v4-core/src/types/BalanceDelta.sol " ;
6
+ import {BalanceDelta, toBalanceDelta } from "@uniswap/v4-core/src/types/BalanceDelta.sol " ;
7
7
import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol " ;
8
8
import {IHooks} from "@uniswap/v4-core/src/interfaces/IHooks.sol " ;
9
9
import {Hooks} from "@uniswap/v4-core/src/libraries/Hooks.sol " ;
10
10
import {CurrencySettler} from "@uniswap/v4-core/test/utils/CurrencySettler.sol " ;
11
11
import {SafeCallback} from "v4-periphery/src/base/SafeCallback.sol " ;
12
12
import {TickMath} from "@uniswap/v4-core/src/libraries/TickMath.sol " ;
13
+ import {TransientStateLibrary} from "@uniswap/v4-core/src/libraries/TransientStateLibrary.sol " ;
13
14
14
15
contract MinimalRouter is SafeCallback {
16
+ using TransientStateLibrary for IPoolManager;
15
17
using CurrencySettler for Currency;
16
18
17
19
uint160 public constant MIN_PRICE_LIMIT = TickMath.MIN_SQRT_PRICE + 1 ;
18
20
uint160 public constant MAX_PRICE_LIMIT = TickMath.MAX_SQRT_PRICE - 1 ;
19
21
20
22
constructor (IPoolManager _manager ) SafeCallback (_manager) {}
21
23
22
- function swap (PoolKey memory key , bool zeroForOne , bool exactInput , uint256 amount , bytes memory hookData )
24
+ /// @dev an unsafe swap function that does not check for slippage
25
+ /// @param key The pool key
26
+ /// @param zeroForOne The direction of the swap
27
+ /// @param amountIn The amount of input token, should be provided (as an estimate) for exact output swaps
28
+ /// @param amountOut The amount of output token can be provided as 0, for exact input swaps
29
+ /// @param hookData The data to pass to the hook
30
+ function swap (PoolKey memory key , bool zeroForOne , uint256 amountIn , uint256 amountOut , bytes memory hookData )
23
31
external
24
32
payable
25
33
returns (BalanceDelta delta )
26
34
{
27
35
delta = abi.decode (
28
- poolManager.unlock (abi.encode (msg .sender , key, zeroForOne, exactInput, amount , hookData)), (BalanceDelta)
36
+ poolManager.unlock (abi.encode (msg .sender , key, zeroForOne, amountIn, amountOut , hookData)), (BalanceDelta)
29
37
);
30
38
31
39
uint256 ethBalance = address (this ).balance;
32
40
if (ethBalance > 0 ) CurrencyLibrary.ADDRESS_ZERO.transfer (msg .sender , ethBalance);
33
41
}
34
42
35
43
function _unlockCallback (bytes calldata data ) internal override returns (bytes memory ) {
36
- (address sender , PoolKey memory key , bool zeroForOne , bool exactInput , uint256 amount , bytes memory hookData ) =
37
- abi.decode (data, (address , PoolKey, bool , bool , uint256 , bytes ));
44
+ (
45
+ address sender ,
46
+ PoolKey memory key ,
47
+ bool zeroForOne ,
48
+ uint256 amountIn ,
49
+ uint256 amountOut ,
50
+ bytes memory hookData
51
+ ) = abi.decode (data, (address , PoolKey, bool , uint256 , uint256 , bytes ));
38
52
39
- // for exact input swaps, send the input first to avoid PoolManager token balance issues
40
- if (exactInput) {
41
- zeroForOne
42
- ? key.currency0.settle (poolManager, sender, amount, false )
43
- : key.currency1.settle (poolManager, sender, amount, false );
44
- }
53
+ // send the input first to avoid PoolManager token balance issues
54
+ zeroForOne
55
+ ? key.currency0.settle (poolManager, sender, amountIn, false )
56
+ : key.currency1.settle (poolManager, sender, amountIn, false );
45
57
46
- BalanceDelta delta = poolManager.swap (
58
+ poolManager.swap (
47
59
key,
48
60
IPoolManager.SwapParams ({
49
61
zeroForOne: zeroForOne,
50
- amountSpecified: exactInput ? - int256 (amount ) : int256 (amount ),
62
+ amountSpecified: amountOut != 0 ? int256 (amountOut ) : - int256 (amountIn ),
51
63
sqrtPriceLimitX96: zeroForOne ? MIN_PRICE_LIMIT : MAX_PRICE_LIMIT
52
64
}),
53
65
hookData
54
66
);
55
67
56
- if (! exactInput && delta.amount0 () < 0 ) {
57
- key.currency0.settle (poolManager, sender, uint256 (int256 (- delta.amount0 ())), false );
58
- } else if (delta.amount0 () > 0 ) {
59
- key.currency0.take (poolManager, sender, uint256 (int256 (delta.amount0 ())), false );
68
+ // observe deltas
69
+ int256 delta0 = poolManager.currencyDelta (address (this ), key.currency0);
70
+ int256 delta1 = poolManager.currencyDelta (address (this ), key.currency1);
71
+
72
+ // primarily take the output token, and excess amounts for exact output swaps
73
+ if (delta0 < 0 ) {
74
+ key.currency0.settle (poolManager, sender, uint256 (- delta0), false );
75
+ } else if (delta0 > 0 ) {
76
+ key.currency0.take (poolManager, sender, uint256 (delta0), false );
60
77
}
61
78
62
- if (! exactInput && delta. amount1 () < 0 ) {
63
- key.currency1.settle (poolManager, sender, uint256 (int256 ( - delta. amount1 ()) ), false );
64
- } else if (delta. amount1 () > 0 ) {
65
- key.currency1.take (poolManager, sender, uint256 (int256 (delta. amount1 ()) ), false );
79
+ if (delta1 < 0 ) {
80
+ key.currency1.settle (poolManager, sender, uint256 (- delta1 ), false );
81
+ } else if (delta1 > 0 ) {
82
+ key.currency1.take (poolManager, sender, uint256 (delta1 ), false );
66
83
}
67
84
68
- return abi.encode (delta);
85
+ // account for prepaid input against the observed deltas
86
+ BalanceDelta returnDelta = toBalanceDelta (int128 (delta0), int128 (delta1))
87
+ + toBalanceDelta (
88
+ zeroForOne ? - int128 (int256 (amountIn)) : int128 (0 ), zeroForOne ? int128 (0 ) : - int128 (int256 (amountIn))
89
+ );
90
+
91
+ return abi.encode (returnDelta);
69
92
}
70
93
}
0 commit comments