Skip to content

Commit d9de497

Browse files
committed
fix: build
1 parent e570cb0 commit d9de497

File tree

11 files changed

+210
-134
lines changed

11 files changed

+210
-134
lines changed

foundry.toml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,7 @@ compilation_restrictions = [
2121
{ paths = "src/PositionManager.sol", optimizer_runs = 30000 },
2222
{ paths = "src/PositionDescriptor.sol", optimizer_runs = 1 },
2323
{ paths = "src/hooks/V2OnV4/**", via_ir = true },
24-
{ paths = "test/hooks/V2OnV4/**", via_ir = true },
25-
{ paths = "^test/(?!hooks/V2OnV4/).*$", via_ir = false },
24+
{ paths = "test/**", via_ir = false }
2625
]
2726

2827
[profile.debug]

snapshots/StateViewTest.json

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
{
2-
"StateView_extsload_getFeeGrowthGlobals": "8703",
3-
"StateView_extsload_getFeeGrowthInside": "24375",
4-
"StateView_extsload_getLiquidity": "5939",
5-
"StateView_extsload_getPositionInfo": "11261",
6-
"StateView_extsload_getPositionLiquidity": "6191",
7-
"StateView_extsload_getSlot0": "5986",
8-
"StateView_extsload_getTickBitmap": "5932",
9-
"StateView_extsload_getTickFeeGrowthOutside": "8990",
10-
"StateView_extsload_getTickInfo": "11193",
11-
"StateView_extsload_getTickLiquidity": "6186"
2+
"StateView_extsload_getFeeGrowthGlobals": "2203",
3+
"StateView_extsload_getFeeGrowthInside": "7875",
4+
"StateView_extsload_getLiquidity": "1439",
5+
"StateView_extsload_getPositionInfo": "2761",
6+
"StateView_extsload_getPositionLiquidity": "1691",
7+
"StateView_extsload_getSlot0": "1486",
8+
"StateView_extsload_getTickBitmap": "1432",
9+
"StateView_extsload_getTickFeeGrowthOutside": "2490",
10+
"StateView_extsload_getTickInfo": "2693",
11+
"StateView_extsload_getTickLiquidity": "1686"
1212
}
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
{
2-
"V2OnV4Hook_exactInput": "154039",
3-
"V2OnV4Hook_initialize": "3659711"
2+
"V2OnV4Hook_exactInput": "155838",
3+
"V2OnV4Hook_initialize": "3817093"
44
}

snapshots/V2OnV4PairTest.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
2-
"V2OnV4Hook_AddLiquidity": "256421",
3-
"V2OnV4Hook_AddLiquidityClaim": "134487",
4-
"V2OnV4Hook_Swap": "99792",
5-
"V2OnV4Hook_SwapClaim": "71287"
2+
"V2OnV4Hook_AddLiquidity": "257591",
3+
"V2OnV4Hook_AddLiquidityClaim": "134868",
4+
"V2OnV4Hook_Swap": "100831",
5+
"V2OnV4Hook_SwapClaim": "71979"
66
}

src/hooks/V2OnV4/V2OnV4FactoryHook.sol

Lines changed: 14 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,9 @@
22
pragma solidity 0.8.26;
33

44
import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol";
5-
import {V2OnV4Pair} from "./V2OnV4Pair.sol";
65
import {
76
toBeforeSwapDelta, BeforeSwapDelta, BeforeSwapDeltaLibrary
87
} from "@uniswap/v4-core/src/types/BeforeSwapDelta.sol";
9-
import {IUniswapV2Factory} from "briefcase/protocols/v2-core/interfaces/IUniswapV2Factory.sol";
108
import {SafeCast} from "@uniswap/v4-core/src/libraries/SafeCast.sol";
119
import {ModifyLiquidityParams, SwapParams} from "@uniswap/v4-core/src/types/PoolOperation.sol";
1210
import {IUniswapV2Pair} from "briefcase/protocols/v2-core/interfaces/IUniswapV2Pair.sol";
@@ -17,13 +15,15 @@ import {IHooks} from "@uniswap/v4-core/src/interfaces/IHooks.sol";
1715
import {Hooks} from "@uniswap/v4-core/src/libraries/Hooks.sol";
1816
import {UniswapV2Library} from "./UniswapV2Library.sol";
1917
import {V2OnV4PairDeployer} from "./V2OnV4PairDeployer.sol";
18+
import {IV2OnV4Pair} from "../../interfaces/IV2OnV4Pair.sol";
19+
import {IV2OnV4Factory} from "../../interfaces/IV2OnV4Factory.sol";
2020
import {BaseHook} from "../../utils/BaseHook.sol";
2121

2222
/// @title V2OnV4FactoryHook
2323
/// @author Uniswap Labs
2424
/// @notice Factory contract that enables Uniswap V2-style AMM pools to run on Uniswap V4 infrastructure
2525
/// @dev Implements the IUniswapV2Factory interface while leveraging V4's hook system for pool management
26-
contract V2OnV4FactoryHook is BaseHook, V2OnV4PairDeployer, IUniswapV2Factory {
26+
contract V2OnV4FactoryHook is BaseHook, V2OnV4PairDeployer, IV2OnV4Factory {
2727
using CurrencyLibrary for Currency;
2828
using SafeCast for int256;
2929
using SafeCast for uint256;
@@ -32,27 +32,17 @@ contract V2OnV4FactoryHook is BaseHook, V2OnV4PairDeployer, IUniswapV2Factory {
3232
address public feeTo;
3333

3434
/// @notice Fixed swap fee of 0.3% (3000 basis points) matching V2's fee structure
35-
uint24 public constant SWAP_FEE = 3000;
35+
uint24 public constant override SWAP_FEE = 3000;
3636

3737
/// @notice Minimum tick spacing for V4 pools (1 tick = finest granularity)
38-
int24 public constant TICK_SPACING = 1;
38+
int24 public constant override TICK_SPACING = 1;
3939

4040
/// @notice Returns the address of the pair for tokenA and tokenB, if it exists
4141
/// @dev getPair[tokenA][tokenB] and getPair[tokenB][tokenA] return the same pair address
42-
mapping(address => mapping(address => address)) public getPair;
42+
mapping(address => mapping(address => address)) public override getPair;
4343

4444
/// @notice Array of all created pair addresses for enumeration
45-
address[] public allPairs;
46-
47-
error InvalidFee();
48-
error LiquidityNotAllowed();
49-
error InvalidTickSpacing();
50-
error InvalidToken();
51-
error IdenticalAddresses();
52-
error ZeroAddress();
53-
error PairExists();
54-
error Forbidden();
55-
error FeeToSetterLocked();
45+
address[] public override allPairs;
5646

5747
/// @notice Deploys the V2OnV4 factory hook
5848
/// @param _manager The Uniswap V4 pool manager contract
@@ -80,7 +70,7 @@ contract V2OnV4FactoryHook is BaseHook, V2OnV4PairDeployer, IUniswapV2Factory {
8070

8171
/// @notice Returns the total number of pairs created
8272
/// @return The length of the allPairs array
83-
function allPairsLength() external view returns (uint256) {
73+
function allPairsLength() external view override returns (uint256) {
8474
return allPairs.length;
8575
}
8676

@@ -89,7 +79,7 @@ contract V2OnV4FactoryHook is BaseHook, V2OnV4PairDeployer, IUniswapV2Factory {
8979
/// @param tokenB Address of the second token
9080
/// @return pair Address of the newly created pair contract
9181
/// @dev Tokens are sorted, and the pair is deployed deterministically
92-
function createPair(address tokenA, address tokenB) public returns (address pair) {
82+
function createPair(address tokenA, address tokenB) public override returns (address pair) {
9383
require(tokenA != tokenB, IdenticalAddresses());
9484
(address token0, address token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA);
9585
require(token0 != address(0), ZeroAddress());
@@ -104,20 +94,20 @@ contract V2OnV4FactoryHook is BaseHook, V2OnV4PairDeployer, IUniswapV2Factory {
10494
/// @notice Returns the address authorized to set the fee recipient
10595
/// @return The protocol fee controller from the V4 pool manager
10696
/// @dev Delegates fee setter authority to V4's protocol fee controller
107-
function feeToSetter() public view returns (address) {
97+
function feeToSetter() public view override returns (address) {
10898
return poolManager.protocolFeeController();
10999
}
110100

111101
/// @notice Prevents changing the fee setter (locked to V4's protocol controller)
112102
/// @dev Always reverts as fee setter is managed by V4 pool manager
113-
function setFeeToSetter(address) external pure {
103+
function setFeeToSetter(address) external pure override {
114104
revert FeeToSetterLocked();
115105
}
116106

117107
/// @notice Sets the address that receives protocol fees
118108
/// @param _feeTo The address to receive protocol fees (or address(0) to disable)
119109
/// @dev Only callable by the current fee setter
120-
function setFeeTo(address _feeTo) external {
110+
function setFeeTo(address _feeTo) external override {
121111
require(msg.sender == feeToSetter(), Forbidden());
122112
feeTo = _feeTo;
123113
}
@@ -162,7 +152,7 @@ contract V2OnV4FactoryHook is BaseHook, V2OnV4PairDeployer, IUniswapV2Factory {
162152
override
163153
returns (bytes4, BeforeSwapDelta swapDelta, uint24)
164154
{
165-
V2OnV4Pair pair = V2OnV4Pair(getPair[Currency.unwrap(poolKey.currency0)][Currency.unwrap(poolKey.currency1)]);
155+
IV2OnV4Pair pair = IV2OnV4Pair(getPair[Currency.unwrap(poolKey.currency0)][Currency.unwrap(poolKey.currency1)]);
166156

167157
(Currency tokenIn, Currency tokenOut, uint256 reserveIn, uint256 reserveOut, uint256 amountSpecified) =
168158
_parseSwap(poolKey, params, pair);
@@ -200,7 +190,7 @@ contract V2OnV4FactoryHook is BaseHook, V2OnV4PairDeployer, IUniswapV2Factory {
200190
/// @return reserveIn Input token reserves
201191
/// @return reserveOut Output token reserves
202192
/// @return amountSpecified Absolute value of the specified swap amount
203-
function _parseSwap(PoolKey calldata poolKey, SwapParams calldata params, V2OnV4Pair pair)
193+
function _parseSwap(PoolKey calldata poolKey, SwapParams calldata params, IV2OnV4Pair pair)
204194
private
205195
view
206196
returns (Currency tokenIn, Currency tokenOut, uint256 reserveIn, uint256 reserveOut, uint256 amountSpecified)

src/hooks/V2OnV4/V2OnV4Pair.sol

Lines changed: 29 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {Hooks} from "@uniswap/v4-core/src/libraries/Hooks.sol";
1515
import {BaseHook} from "../../utils/BaseHook.sol";
1616
import {V2OnV4PairDeployer} from "./V2OnV4PairDeployer.sol";
1717
import {UQ112x112} from "./UQ112x112.sol";
18+
import {IV2OnV4Pair} from "../../interfaces/IV2OnV4Pair.sol";
1819

1920
enum UnlockCallbackAction {
2021
MINT,
@@ -32,7 +33,7 @@ struct UnlockCallback {
3233
/// @author Uniswap Labs
3334
/// @notice A V2-style AMM pair contract that operates on Uniswap V4 infrastructure
3435
/// @dev Implements constant product (x*y=k) AMM logic while leveraging V4's singleton pool manager
35-
contract V2OnV4Pair is ERC20, ReentrancyGuardTransient {
36+
contract V2OnV4Pair is IV2OnV4Pair, ERC20, ReentrancyGuardTransient {
3637
using UQ112x112 for uint224;
3738
using SafeTransferLib for ERC20;
3839
using CurrencyLibrary for Currency;
@@ -79,29 +80,6 @@ contract V2OnV4Pair is ERC20, ReentrancyGuardTransient {
7980
_;
8081
}
8182

82-
error K();
83-
error Forbidden();
84-
error NotPoolManager();
85-
error InvalidUnlockCallbackData();
86-
error InsufficientLiquidityBurned();
87-
error InsufficientLiquidityMinted();
88-
error InsufficientOutputAmount();
89-
error InsufficientInputAmount();
90-
error InsufficientLiquidity();
91-
error InvalidTo();
92-
93-
event Mint(address indexed sender, uint256 amount0, uint256 amount1);
94-
event Burn(address indexed sender, uint256 amount0, uint256 amount1, address indexed to);
95-
event Swap(
96-
address indexed sender,
97-
uint256 amount0In,
98-
uint256 amount1In,
99-
uint256 amount0Out,
100-
uint256 amount1Out,
101-
address indexed to
102-
);
103-
event Sync(uint112 reserve0, uint112 reserve1);
104-
10583
/// @notice Deploys a new V2 pair on V4
10684
/// @dev Called by the factory with deployment parameters
10785
constructor() ERC20("Uniswap V2", "UNI-V2", 18) {
@@ -118,8 +96,13 @@ contract V2OnV4Pair is ERC20, ReentrancyGuardTransient {
11896
/// @dev Low-level function that should be called through a router with proper safety checks
11997
/// @param to Address to receive the minted LP tokens
12098
/// @return liquidity Amount of LP tokens minted
121-
function mint(address to) external nonReentrant returns (uint256 liquidity) {
122-
poolManager.unlock(abi.encode(UnlockCallback({action: UnlockCallbackAction.MINT, to: to, data: new bytes(0)})));
99+
function mint(address to) external override nonReentrant returns (uint256 liquidity) {
100+
(liquidity) = abi.decode(
101+
poolManager.unlock(
102+
abi.encode(UnlockCallback({action: UnlockCallbackAction.MINT, to: to, data: new bytes(0)}))
103+
),
104+
(uint256)
105+
);
123106
}
124107

125108
/// @notice Internal mint function that handles ERC20 token deposits and liquidity creation
@@ -128,18 +111,23 @@ contract V2OnV4Pair is ERC20, ReentrancyGuardTransient {
128111
/// @return liquidity Amount of LP tokens minted
129112
function _mint(address to) internal returns (uint256 liquidity) {
130113
// transform ERC20 tokens into claims
131-
(uint256 amount0, uint256 amount1) = _slurp();
114+
_slurp();
132115
// Then mint liquidity as normal with those claims
133-
_mintClaims(to);
116+
liquidity = _mintClaims(to);
134117
}
135118

136119
/// @notice Burns liquidity tokens and returns underlying assets
137120
/// @dev Low-level function that should be called through a router with proper safety checks
138121
/// @param to Address to receive the underlying tokens
139122
/// @return amount0 Amount of token0 returned
140123
/// @return amount1 Amount of token1 returned
141-
function burn(address to) external nonReentrant returns (uint256 amount0, uint256 amount1) {
142-
poolManager.unlock(abi.encode(UnlockCallback({action: UnlockCallbackAction.BURN, to: to, data: new bytes(0)})));
124+
function burn(address to) external override nonReentrant returns (uint256 amount0, uint256 amount1) {
125+
(amount0, amount1) = abi.decode(
126+
poolManager.unlock(
127+
abi.encode(UnlockCallback({action: UnlockCallbackAction.BURN, to: to, data: new bytes(0)}))
128+
),
129+
(uint256, uint256)
130+
);
143131
}
144132

145133
/// @notice Internal burn function that redeems LP tokens for underlying assets
@@ -158,7 +146,11 @@ contract V2OnV4Pair is ERC20, ReentrancyGuardTransient {
158146
/// @param amount1Out Amount of token1 to send
159147
/// @param to Address to receive output tokens
160148
/// @param data Callback data for flash swaps
161-
function swap(uint256 amount0Out, uint256 amount1Out, address to, bytes calldata data) external nonReentrant {
149+
function swap(uint256 amount0Out, uint256 amount1Out, address to, bytes calldata data)
150+
external
151+
override
152+
nonReentrant
153+
{
162154
poolManager.unlock(
163155
abi.encode(
164156
UnlockCallback({
@@ -207,7 +199,7 @@ contract V2OnV4Pair is ERC20, ReentrancyGuardTransient {
207199
/// @param to Address to receive the minted LP tokens
208200
/// @return liquidity Amount of LP tokens minted
209201
function mintClaims(address to) external nonReentrant returns (uint256 liquidity) {
210-
_mintClaims(to);
202+
return _mintClaims(to);
211203
}
212204

213205
/// @notice Burns liquidity and returns claims directly
@@ -216,7 +208,7 @@ contract V2OnV4Pair is ERC20, ReentrancyGuardTransient {
216208
/// @return amount0 Amount of token0 claims returned
217209
/// @return amount1 Amount of token1 claims returned
218210
function burnClaims(address to) external nonReentrant returns (uint256 amount0, uint256 amount1) {
219-
_burnClaims(to, true);
211+
return _burnClaims(to, true);
220212
}
221213

222214
/// @notice Executes a swap using V4 claims directly
@@ -239,13 +231,15 @@ contract V2OnV4Pair is ERC20, ReentrancyGuardTransient {
239231
function unlockCallback(bytes calldata data) external onlyPoolManager returns (bytes memory) {
240232
UnlockCallback memory callbackData = abi.decode(data, (UnlockCallback));
241233
if (callbackData.action == UnlockCallbackAction.MINT) {
242-
_mint(callbackData.to);
234+
return abi.encode(_mint(callbackData.to));
243235
} else if (callbackData.action == UnlockCallbackAction.BURN) {
244-
_burn(callbackData.to);
236+
(uint256 amount0, uint256 amount1) = _burn(callbackData.to);
237+
return abi.encode(amount0, amount1);
245238
} else if (callbackData.action == UnlockCallbackAction.SWAP) {
246239
(uint256 amount0Out, uint256 amount1Out, bytes memory swapData) =
247240
abi.decode(callbackData.data, (uint256, uint256, bytes));
248241
_swap(amount0Out, amount1Out, callbackData.to, swapData);
242+
return new bytes(0); // no return data needed for swap
249243
} else {
250244
revert InvalidUnlockCallbackData();
251245
}

src/interfaces/IV2OnV4Factory.sol

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity 0.8.26;
3+
4+
import {Currency} from "@uniswap/v4-core/src/types/Currency.sol";
5+
import {IPoolManager} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol";
6+
import {IUniswapV2Factory} from "briefcase/protocols/v2-core/interfaces/IUniswapV2Factory.sol";
7+
import {IERC20} from "forge-std/interfaces/IERC20.sol";
8+
9+
/// @title IV2OnV4Factory
10+
/// @notice Interface for V2-style AMM pair contracts operating on Uniswap V4 infrastructure
11+
interface IV2OnV4Factory is IUniswapV2Factory {
12+
function SWAP_FEE() external pure returns (uint24);
13+
function TICK_SPACING() external pure returns (int24);
14+
15+
error InvalidFee();
16+
error LiquidityNotAllowed();
17+
error InvalidTickSpacing();
18+
error InvalidToken();
19+
error IdenticalAddresses();
20+
error ZeroAddress();
21+
error PairExists();
22+
error Forbidden();
23+
error FeeToSetterLocked();
24+
}

0 commit comments

Comments
 (0)