|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
| 2 | +pragma solidity =0.7.6; |
| 3 | +pragma abicoder v2; |
| 4 | + |
| 5 | +import 'forge-std/Script.sol'; |
| 6 | +import 'forge-std/console.sol'; |
| 7 | + |
| 8 | +import {ListaV3Factory} from '../src/core/ListaV3Factory.sol'; |
| 9 | +import {NonfungiblePositionManager} from '../src/periphery/NonfungiblePositionManager.sol'; |
| 10 | +import {SwapRouter} from '../src/periphery/SwapRouter.sol'; |
| 11 | + |
| 12 | +import {ProxyAdmin} from 'lib/openzeppelin-contracts/contracts/proxy/ProxyAdmin.sol'; |
| 13 | +import {TransparentUpgradeableProxy} from 'lib/openzeppelin-contracts/contracts/proxy/TransparentUpgradeableProxy.sol'; |
| 14 | + |
| 15 | +/// @title Deploy Lista V3 factory / NPM / SwapRouter |
| 16 | +/// @notice Multi-chain deploy. WETH9 is resolved by chain id with built-in values for |
| 17 | +/// common networks; on unknown chains the `WETH9` env var is required. Owner addresses |
| 18 | +/// come from env. Run per chain with `--rpc-url $RPC` + either `--private-key` |
| 19 | +/// or a `--ledger` / hardware signer. |
| 20 | +/// |
| 21 | +/// Required env: |
| 22 | +/// OWNER = factory owner (fee-tier admin) |
| 23 | +/// |
| 24 | +/// Optional env (with defaults): |
| 25 | +/// PROXY_ADMIN_OWNER = owner of ProxyAdmin (defaults to OWNER) |
| 26 | +/// TOKEN_DESCRIPTOR = NFT position descriptor address (defaults to 0x0, disables tokenURI) |
| 27 | +/// WETH9 = WETH9 address (required on unknown chains; overrides built-ins) |
| 28 | +/// |
| 29 | +/// Usage: |
| 30 | +/// forge script script/Deploy.s.sol:Deploy --rpc-url $RPC --broadcast --verify |
| 31 | +contract Deploy is Script { |
| 32 | + struct Deployment { |
| 33 | + address proxyAdmin; |
| 34 | + address factoryImpl; |
| 35 | + address factoryProxy; |
| 36 | + address npmImpl; |
| 37 | + address npmProxy; |
| 38 | + address swapRouter; |
| 39 | + } |
| 40 | + |
| 41 | + function run() external returns (Deployment memory out) { |
| 42 | + uint256 chainId; |
| 43 | + assembly { |
| 44 | + chainId := chainid() |
| 45 | + } |
| 46 | + |
| 47 | + address weth9 = _resolveWeth9(chainId); |
| 48 | + address owner = vm.envAddress('OWNER'); |
| 49 | + address proxyAdminOwner = vm.envOr('PROXY_ADMIN_OWNER', owner); |
| 50 | + address tokenDescriptor = vm.envOr('TOKEN_DESCRIPTOR', address(0)); |
| 51 | + |
| 52 | + require(owner != address(0), 'OWNER=0'); |
| 53 | + require(weth9 != address(0), 'WETH9=0'); |
| 54 | + |
| 55 | + console.log('--- Lista V3 deploy ---'); |
| 56 | + console.log('chainId:', chainId); |
| 57 | + console.log('network:', _chainLabel(chainId)); |
| 58 | + console.log('owner:', owner); |
| 59 | + console.log('proxyAdminOwner:', proxyAdminOwner); |
| 60 | + console.log('WETH9:', weth9); |
| 61 | + console.log('tokenDescriptor:', tokenDescriptor); |
| 62 | + |
| 63 | + vm.startBroadcast(); |
| 64 | + |
| 65 | + ProxyAdmin proxyAdmin = new ProxyAdmin(); |
| 66 | + if (proxyAdminOwner != address(this) && proxyAdminOwner != proxyAdmin.owner()) { |
| 67 | + proxyAdmin.transferOwnership(proxyAdminOwner); |
| 68 | + } |
| 69 | + |
| 70 | + // Factory: impl + proxy |
| 71 | + ListaV3Factory factoryImpl = new ListaV3Factory(); |
| 72 | + // Consume the impl's initializer so it can't be hijacked on-chain. |
| 73 | + factoryImpl.initialize(address(0xdead)); |
| 74 | + |
| 75 | + bytes memory factoryInit = abi.encodeWithSelector(ListaV3Factory.initialize.selector, owner); |
| 76 | + TransparentUpgradeableProxy factoryProxy = |
| 77 | + new TransparentUpgradeableProxy(address(factoryImpl), address(proxyAdmin), factoryInit); |
| 78 | + |
| 79 | + // NPM: impl + proxy. factory/WETH9 are constructor immutables on the impl; every |
| 80 | + // future upgrade MUST re-pass the exact same (factoryProxy, WETH9) to the new impl. |
| 81 | + NonfungiblePositionManager npmImpl = new NonfungiblePositionManager(address(factoryProxy), weth9); |
| 82 | + npmImpl.initialize(address(0xdead)); |
| 83 | + |
| 84 | + bytes memory npmInit = abi.encodeWithSelector(NonfungiblePositionManager.initialize.selector, tokenDescriptor); |
| 85 | + TransparentUpgradeableProxy npmProxy = |
| 86 | + new TransparentUpgradeableProxy(address(npmImpl), address(proxyAdmin), npmInit); |
| 87 | + |
| 88 | + // SwapRouter is not upgradeable; it's a plain deploy against the factory proxy. |
| 89 | + SwapRouter swapRouter = new SwapRouter(address(factoryProxy), weth9); |
| 90 | + |
| 91 | + vm.stopBroadcast(); |
| 92 | + |
| 93 | + out = Deployment({ |
| 94 | + proxyAdmin: address(proxyAdmin), |
| 95 | + factoryImpl: address(factoryImpl), |
| 96 | + factoryProxy: address(factoryProxy), |
| 97 | + npmImpl: address(npmImpl), |
| 98 | + npmProxy: address(npmProxy), |
| 99 | + swapRouter: address(swapRouter) |
| 100 | + }); |
| 101 | + |
| 102 | + console.log('--- deployed ---'); |
| 103 | + console.log('ProxyAdmin:', out.proxyAdmin); |
| 104 | + console.log('Factory impl:', out.factoryImpl); |
| 105 | + console.log('Factory proxy:', out.factoryProxy); |
| 106 | + console.log('NPM impl:', out.npmImpl); |
| 107 | + console.log('NPM proxy:', out.npmProxy); |
| 108 | + console.log('SwapRouter:', out.swapRouter); |
| 109 | + } |
| 110 | + |
| 111 | + /// @dev Built-in WETH9 addresses keyed by chain id. Any explicit `WETH9` env var |
| 112 | + /// overrides. Add entries here when onboarding a new chain. |
| 113 | + function _resolveWeth9(uint256 chainId) internal returns (address) { |
| 114 | + address override_ = vm.envOr('WETH9', address(0)); |
| 115 | + if (override_ != address(0)) return override_; |
| 116 | + |
| 117 | + if (chainId == 1) return 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; // Ethereum — WETH |
| 118 | + if (chainId == 11155111) return 0xfFf9976782d46CC05630D1f6eBAb18b2324d6B14; // Sepolia — WETH (Uniswap canonical) |
| 119 | + if (chainId == 56) return 0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c; // BNB Chain — WBNB |
| 120 | + if (chainId == 97) return 0xae13d989daC2f0dEbFf460aC112a837C89BAa7cd; // BNB Chain testnet — WBNB |
| 121 | + |
| 122 | + revert('WETH9 not set: pass WETH9=0x... or add chain id to _resolveWeth9'); |
| 123 | + } |
| 124 | + |
| 125 | + function _chainLabel(uint256 chainId) internal pure returns (string memory) { |
| 126 | + if (chainId == 1) return 'ethereum'; |
| 127 | + if (chainId == 11155111) return 'sepolia'; |
| 128 | + if (chainId == 56) return 'bnb'; |
| 129 | + if (chainId == 97) return 'bnb-testnet'; |
| 130 | + if (chainId == 31337) return 'anvil'; |
| 131 | + return 'unknown'; |
| 132 | + } |
| 133 | +} |
0 commit comments