@@ -13,6 +13,7 @@ import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol";
1313import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol " ;
1414import {Currency} from "@uniswap/v4-core/src/types/Currency.sol " ;
1515import {TransferHelper} from "./libraries/TransferHelper.sol " ;
16+ import {ActionConstants} from "@uniswap/v4-periphery/src/libraries/ActionConstants.sol " ;
1617
1718contract ProxyUniswapV4 is Ownable {
1819 using StateLibrary for IPoolManager;
@@ -21,20 +22,19 @@ contract ProxyUniswapV4 is Ownable {
2122 IPoolManager public immutable poolManager;
2223 IPermit2 public immutable permit2;
2324
24- uint256 public feePercent = 50 ;
25+ uint256 public feePercent = 5_000 ;
2526
26- uint256 public feeBase = 100 ;
27+ uint256 public constant FEE_DENOMINATOR = 10_000 ;
2728
2829 constructor (address _router , address _permit2 , address _initialOwner ) Ownable (_initialOwner) {
2930 router = IUniversalRouter (_router);
3031 permit2 = IPermit2 (_permit2);
3132 // poolManager = IPoolManager(_poolManager);
3233 }
3334
34- function setFeePercent (uint256 _percent , uint256 _base ) external onlyOwner {
35- require (_percent <= _base , "Fee cannot exceed feeBase% " );
35+ function setFeePercent (uint256 _percent ) external onlyOwner {
36+ require (_percent <= FEE_DENOMINATOR , "Fee cannot exceed feeBase% " );
3637 feePercent = _percent;
37- feeBase = _base;
3838 }
3939
4040 function swapExactInputSingle (
@@ -44,6 +44,17 @@ contract ProxyUniswapV4 is Ownable {
4444 uint128 amountOutMin ,
4545 address recipient
4646 ) external payable returns (uint256 amountOut ) {
47+ (Currency currencyIn , Currency currencyOut ) =
48+ zeroForOne ? (key.currency0, key.currency1) : (key.currency1, key.currency0);
49+
50+ // Transfer the input token to Proxy Router
51+ if (! currencyIn.isAddressZero ()) {
52+ address tokenIn = Currency.unwrap (currencyIn);
53+ TransferHelper.safeTransferFrom (tokenIn, msg .sender , address (this ), amountIn);
54+ TransferHelper.safeApprove (tokenIn, address (permit2), amountIn);
55+ permit2.approve (tokenIn, address (router), amountIn, uint48 (block .timestamp + 10 ));
56+ }
57+
4758 // Encode the Universal Router command
4859 bytes memory commands = abi.encodePacked (uint8 (Commands.V4_SWAP));
4960 bytes [] memory inputs = new bytes [](1 );
@@ -63,22 +74,13 @@ contract ProxyUniswapV4 is Ownable {
6374 hookData: bytes ("" )
6475 })
6576 );
66- (Currency currencyIn , Currency currencyOut ) =
67- zeroForOne ? (key.currency0, key.currency1) : (key.currency1, key.currency0);
77+
6878 params[1 ] = abi.encode (currencyIn, amountIn);
6979 params[2 ] = abi.encode (currencyOut, amountOutMin);
7080
7181 // Combine actions and params into inputs
7282 inputs[0 ] = abi.encode (actions, params);
7383
74- // Transfer the input token to Proxy Router
75- if (! currencyIn.isAddressZero ()) {
76- address tokenIn = Currency.unwrap (currencyIn);
77- TransferHelper.safeTransferFrom (tokenIn, msg .sender , address (this ), amountIn);
78- TransferHelper.safeApprove (tokenIn, address (permit2), amountIn);
79- permit2.approve (tokenIn, address (router), amountIn, uint48 (block .timestamp + 10 ));
80- }
81-
8284 uint256 amountOut_before = currencyOut.balanceOf (address (this ));
8385
8486 // Execute the swap
@@ -87,7 +89,7 @@ contract ProxyUniswapV4 is Ownable {
8789
8890 // Verify and return the output amount
8991 amountOut = currencyOut.balanceOf (address (this )) - amountOut_before;
90- uint256 feeAmount = (amountOut * feePercent) / feeBase ;
92+ uint256 feeAmount = (amountOut * feePercent) / FEE_DENOMINATOR ;
9193 uint256 userAmount = amountOut - feeAmount;
9294 require (userAmount >= amountOutMin, "AmountOutMin not met " );
9395
@@ -97,5 +99,68 @@ contract ProxyUniswapV4 is Ownable {
9799 return userAmount;
98100 }
99101
102+ function swapExactInputSingleOfficial (
103+ PoolKey calldata key ,
104+ bool zeroForOne ,
105+ uint128 amountIn ,
106+ uint128 amountOutMin ,
107+ address recipient
108+ ) external payable returns (uint256 amountOut ) {
109+ (Currency currencyIn , Currency currencyOut ) =
110+ zeroForOne ? (key.currency0, key.currency1) : (key.currency1, key.currency0);
111+
112+ // Transfer the input token to Proxy Router
113+ if (! currencyIn.isAddressZero ()) {
114+ address tokenIn = Currency.unwrap (currencyIn);
115+ TransferHelper.safeTransferFrom (tokenIn, msg .sender , address (this ), amountIn);
116+ TransferHelper.safeApprove (tokenIn, address (permit2), amountIn);
117+ permit2.approve (tokenIn, address (router), amountIn, uint48 (block .timestamp + 10 ));
118+ }
119+
120+ // Encode the Universal Router command
121+ bytes memory commands =
122+ abi.encodePacked (uint8 (Commands.V4_SWAP), uint8 (Commands.PAY_PORTION), uint8 (Commands.SWEEP));
123+ bytes [] memory inputs = new bytes [](3 );
124+
125+ // Commands.V4_SWAP
126+ bytes memory actions =
127+ abi.encodePacked (uint8 (Actions.SWAP_EXACT_IN_SINGLE), uint8 (Actions.SETTLE), uint8 (Actions.TAKE));
128+
129+ // Prepare parameters for each action
130+ bytes [] memory params = new bytes [](3 );
131+
132+ // SWAP_EXACT_IN_SINGLE calldata
133+ params[0 ] = abi.encode (
134+ IV4Router.ExactInputSingleParams ({
135+ poolKey: key,
136+ zeroForOne: zeroForOne,
137+ amountIn: amountIn,
138+ amountOutMinimum: amountOutMin,
139+ hookData: bytes ("" )
140+ })
141+ );
142+
143+ // SETTLE (Currency currency, uint256 amount, bool payerIsUser)
144+ params[1 ] = abi.encode (currencyIn, ActionConstants.OPEN_DELTA, true );
145+
146+ // TAKE (Currency currency, address recipient, uint256 amount)
147+ params[2 ] = abi.encode (currencyOut, ActionConstants.ADDRESS_THIS, ActionConstants.OPEN_DELTA);
148+
149+ // TODO:Commands.V4_SWAP
150+ inputs[0 ] = abi.encode (actions, params);
151+
152+ // TODO:Commands.PAY_PORTION encode(token, recipient, bips)
153+ inputs[1 ] = abi.encode (currencyOut, owner (), feePercent);
154+
155+ // TODO:Commands.SWEEP encode(token, recipient, amountMinimum)
156+ inputs[2 ] = abi.encode (currencyOut, recipient, amountOutMin);
157+
158+ uint256 amountOut_before = currencyOut.balanceOf (recipient);
159+
160+ router.execute {value: amountIn}(commands, inputs, block .timestamp + 20 );
161+
162+ amountOut = currencyOut.balanceOf (recipient) - amountOut_before;
163+ }
164+
100165 receive () external payable {}
101166}
0 commit comments