You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Restores Factory to the upstream Uniswap V3 shape — plain constructor,
NoDelegateCall on createPool, no Initializable / __gap / initialize.
NPM stays upgradeable behind TransparentUpgradeableProxy.
Knock-on changes:
- script/Deploy.s.sol: factory deploys directly; setOwner called only if
OWNER != msg.sender. Factory impl + proxy + init data removed.
- test/periphery/FullFlowTest.t.sol: setUp constructs factory directly.
- foundry.toml: factory drops out of compilation_restrictions; the
reverted (smaller) code fits at the default optimizer_runs.
- README.md: top-line and Contracts section describe factory as
non-upgradeable; deployment outline shortened. The License &
on-chain bytecode reuse disclaimer is removed since the
NoDelegateCall deviation it explained no longer applies.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: README.md
+13-19Lines changed: 13 additions & 19 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,33 +1,33 @@
1
1
# Lista V3
2
2
3
-
A Uniswap V3 fork renamed to Lista V3, with the factory and NFT position manager deployed behind upgradeable proxies.
3
+
A Uniswap V3 fork renamed to Lista V3, with the NFT position manager deployed behind an upgradeable proxy. Factory, pool, and router behavior tracks upstream Uniswap V3.
4
4
5
5
## Contracts
6
6
7
7
### Core (`src/core/`)
8
8
9
-
-`ListaV3Factory` — canonical factory. **Upgradeable** via `TransparentUpgradeableProxy`; initializer replaces the constructor and seeds the 500 / 3000 / 10000 fee tiers.
10
-
-`ListaV3Pool` — AMM pool, CREATE2-deployed by the factory, **not upgradeable**.
9
+
-`ListaV3Factory` — canonical factory. Plain (non-upgradeable) deploy; constructor sets the deployer as owner and seeds the 500 / 3000 / 10000 fee tiers. Keeps `NoDelegateCall` as upstream.
10
+
-`ListaV3Pool` — AMM pool, CREATE2-deployed by the factory, not upgradeable.
11
11
-`ListaV3PoolDeployer` — base of the factory; writes transient parameters so the pool constructor can read them back, keeping `POOL_INIT_CODE_HASH` constant for off-chain address derivation.
12
12
13
13
### Periphery (`src/periphery/`)
14
14
15
-
-`NonfungiblePositionManager` — wraps positions as ERC-721 NFTs. **Hybrid upgradeable**: the ERC-721 / permit stack uses `ERC721PermitUpgradeable` (storage-backed, behind the proxy), while `factory` and `WETH9` remain constructor-set `immutable`s in the implementation bytecode. Every impl upgrade must re-pass the original `(factory, WETH9)` to the new impl's constructor.
15
+
-`NonfungiblePositionManager` — wraps positions as ERC-721 NFTs. **Hybrid upgradeable**: the ERC-721 / permit stack uses `ERC721PermitUpgradeable` (storage-backed, behind a `TransparentUpgradeableProxy`), while `factory` and `WETH9` remain constructor-set `immutable`s in the implementation bytecode. Every impl upgrade must re-pass the original `(factory, WETH9)` to the new impl's constructor.
1. Deploy `ListaV3Factory` impl (no constructor args).
21
-
2. Deploy a shared `ProxyAdmin`.
22
-
3. Deploy `TransparentUpgradeableProxy(factoryImpl, proxyAdmin, abi.encodeWithSelector(ListaV3Factory.initialize.selector, owner))`. Treat the proxy address as *the* factory from here on.
23
-
4. Deploy `NonfungiblePositionManager` impl with `(factoryProxy, WETH9)`.
5. Deploy `SwapRouter`, `V3Migrator`, etc. against the factory address.
26
25
27
26
Operational notes:
28
27
29
-
- The Factory/NPM implementations should have their initializers consumed post-deploy (e.g. `initialize(0xdead)` / `initialize(0xdead, 0xdead)`) to close the Parity-style impl-takeover window.
30
-
- Pool addresses are derived from the factory proxy via `PoolAddress.computeAddress`. If `ListaV3Pool` bytecode is ever changed, `PoolAddress.POOL_INIT_CODE_HASH` must be recomputed — the value in `src/periphery/libraries/PoolAddress.sol` is only valid for the currently-checked-in pool source and build settings.
28
+
- The NPM implementation should have its initializer consumed post-deploy (e.g. `npmImpl.initialize(0xdead)`) to close the Parity-style impl-takeover window.
29
+
- Every NPM impl upgrade must re-pass the same `(factory, WETH9)` to the new impl's constructor — the values are baked into impl bytecode as immutables. A deploy script that reads `npm.factory()` / `npm.WETH9()` from the existing proxy and forwards them to the new impl's constructor is the safest pattern.
30
+
- Pool addresses are derived from the factory via `PoolAddress.computeAddress`. If `ListaV3Pool` bytecode is ever changed, `PoolAddress.POOL_INIT_CODE_HASH` must be recomputed — the value in `src/periphery/libraries/PoolAddress.sol` is only valid for the currently-checked-in pool source and build settings.
31
31
- Re-verify `POOL_INIT_CODE_HASH` before deploying to a new chain. The value is machine-deterministic given `bytecode_hash = "none"` in `foundry.toml`, but a different toolchain version, optimizer setting, or solc patch can still shift it. Run `testInitCodeHash` in `test/periphery/FullFlowTest.t.sol` against your build environment as a pre-deploy gate; if it fails, update the constant before deploying or off-chain pool address derivation will silently point at the wrong addresses.
32
32
33
33
## Build & test
@@ -41,16 +41,10 @@ forge test
41
41
42
42
The end-to-end flow (pool creation, mint, swap, increase / decrease liquidity, collect, transfer, burn) runs in `test/periphery/FullFlowTest.t.sol`, which also exercises the proxy wiring and asserts NPM initializer state / ERC-165 registrations.
43
43
44
-
## License & on-chain bytecode reuse
44
+
## License
45
45
46
46
Source is GPL-2.0-or-later (see `NOTICE` for derivation and attribution).
47
47
48
-
`NoDelegateCall` has been removed from `ListaV3Factory` — its `address(this) == original` invariant is incompatible with the proxy pattern, since every legitimate call from `TransparentUpgradeableProxy` is itself a `delegatecall`. `ListaV3Pool` is unproxied and keeps the modifier.
49
-
50
-
Upstream Uniswap V3 added `NoDelegateCall` as a license-agnostic deterrent against on-chain bytecode reuse — the original PR comment frames the intent as ["Prevents circumventing the license, GPL or otherwise"](https://github.com/Uniswap/v3-core/pull/327#issuecomment-813462722). Without it, a third party can point their own proxy at our deployed factory implementation and run a parallel AMM on Lista's compiled logic. Pools they create that way are state-isolated — CREATE2 addresses derive from their proxy, not Lista's — and cannot interact with Lista pools.
51
-
52
-
This does not change GPL obligations: source modification and redistribution remain governed by GPL-2.0-or-later. The legal status of `delegatecall`-into-licensed-bytecode is itself unsettled; `NoDelegateCall` was a technical deterrent, not a settled legal interpretation. We accept the tradeoff as the cost of TUP-fronted upgradeability.
0 commit comments