From a8afd7d00324fe8554ba042dcffa23a6313c3766 Mon Sep 17 00:00:00 2001 From: Pilou <76021631+0xPilou@users.noreply.github.com> Date: Thu, 29 Jan 2026 17:04:09 +0100 Subject: [PATCH 1/9] chore: update solhint to accept console.log in scripts --- packages/contracts/.solhint.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/contracts/.solhint.json b/packages/contracts/.solhint.json index 31eb531..8cba538 100644 --- a/packages/contracts/.solhint.json +++ b/packages/contracts/.solhint.json @@ -6,6 +6,7 @@ "avoid-sha3": "warn", "compiler-version": ["error", "^0.8.23"], "func-visibility": ["warn", { "ignoreConstructors": true }], - "import-path-check": "off" + "import-path-check": "off", + "no-console": ["off", { "excludedFiles": ["script/**"] }] } } From d9be376918d1393109b0a704a7e6e2f767d9e826 Mon Sep 17 00:00:00 2001 From: Pilou <76021631+0xPilou@users.noreply.github.com> Date: Thu, 29 Jan 2026 17:09:21 +0100 Subject: [PATCH 2/9] chore: add abstract deployer --- packages/contracts/script/SupDeployer.s.sol | 28 +++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 packages/contracts/script/SupDeployer.s.sol diff --git a/packages/contracts/script/SupDeployer.s.sol b/packages/contracts/script/SupDeployer.s.sol new file mode 100644 index 0000000..5fa0022 --- /dev/null +++ b/packages/contracts/script/SupDeployer.s.sol @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.23; + +import { Script, console2 } from "forge-std/Script.sol"; + +abstract contract SupDeployer is Script { + function _startBroadcast() internal returns (address deployer) { + vm.startBroadcast(); + + // This is the way to get deployer address in foundry: + (, deployer,) = vm.readCallers(); + } + + function _stopBroadcast() internal { + vm.stopBroadcast(); + } + + function _showGitRevision() internal { + string[] memory inputs = new string[](2); + inputs[0] = "../tasks/show-git-rev.sh"; + inputs[1] = "forge_ffi_mode"; + try vm.ffi(inputs) returns (bytes memory res) { + console2.log("GIT REVISION : %s", string(res)); + } catch { + console2.log("!! _showGitRevision: FFI not enabled"); + } + } +} From 25fd65d4a33591a14ed53e47dc5a66c06a007f07 Mon Sep 17 00:00:00 2001 From: Pilou <76021631+0xPilou@users.noreply.github.com> Date: Thu, 29 Jan 2026 17:09:43 +0100 Subject: [PATCH 3/9] feat: add new AddressRegistry and Locker implementation deploy script --- .../script/config/AddressRegistry.sol | 75 ++++++++ .../upgrades/deploy-locker-upgrade.s.sol | 180 ++++++++++++++++++ 2 files changed, 255 insertions(+) create mode 100644 packages/contracts/script/config/AddressRegistry.sol create mode 100644 packages/contracts/script/upgrades/deploy-locker-upgrade.s.sol diff --git a/packages/contracts/script/config/AddressRegistry.sol b/packages/contracts/script/config/AddressRegistry.sol new file mode 100644 index 0000000..f1269ff --- /dev/null +++ b/packages/contracts/script/config/AddressRegistry.sol @@ -0,0 +1,75 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.23; + +library AddressRegistry { + struct LockerDeploymentParameters { + address lockerBeacon; + address sup; + address programManager; + address stakingRewardController; + address fontaineBeacon; + address uniswapNonFungiblePositionManager; + address uniswapSupEthxPool; + address uniswapSwapRouter; + address daoTreasury; + bool isUnlockAvailable; + } + + function getLockerDeploymentParameters(uint256 chainId) + internal + pure + returns (LockerDeploymentParameters memory addresses) + { + if (chainId == 8453) { + addresses = getBaseLockerDeploymentParameters(); + } else if (chainId == 84_532) { + addresses = getBaseSepoliaLockerDeploymentParameters(); + } else { + revert("Unsupported chainId"); + } + } + + /** + * @dev Get Base Mainnet address registry + */ + function getBaseLockerDeploymentParameters() internal pure returns (LockerDeploymentParameters memory addresses) { + return LockerDeploymentParameters({ + sup: 0xa69f80524381275A7fFdb3AE01c54150644c8792, + programManager: 0x1e32cf099992E9D3b17eDdDFFfeb2D07AED95C6a, + stakingRewardController: 0xb19Ae25A98d352B36CED60F93db926247535048b, + fontaineBeacon: 0xA26FbA47Da24F7DF11b3E4CF60Dcf7D1691Ae47d, + lockerBeacon: 0x664161f0974F5B17FB1fD3FDcE5D1679E829176c, + uniswapNonFungiblePositionManager: 0x03a520b32C04BF3bEEf7BEb72E919cf822Ed34f1, + uniswapSupEthxPool: 0xBa154BEAa14172fF9384B82499732c669527d85D, + uniswapSwapRouter: 0x2626664c2603336E57B271c5C0b26F421741e481, + daoTreasury: 0xac808840f02c47C05507f48165d2222FF28EF4e1, + isUnlockAvailable: true + }); + } + + function getLocalLockerDeploymentParameters() internal pure returns (LockerDeploymentParameters memory addresses) { + return getBaseLockerDeploymentParameters(); + } + + /** + * @dev Get Base Sepolia configuration + */ + function getBaseSepoliaLockerDeploymentParameters() + internal + pure + returns (LockerDeploymentParameters memory addresses) + { + return LockerDeploymentParameters({ + sup: 0xFd62b398DD8a233ad37156690631fb9515059d6A, + programManager: 0x71a1975A1009e48E0BF2f621B6835db5Ea1f7706, + stakingRewardController: 0x9FC0Bb109F3e733Bd84B30F8D89685b0304fC018, + fontaineBeacon: 0xeBfA246A0BAd08A2A3ffB137ed75601AA41867dE, + lockerBeacon: 0xf2880c6D68080393C1784f978417a96ab4f37c38, + uniswapNonFungiblePositionManager: 0x27F971cb582BF9E50F397e4d29a5C7A34f11faA2, + uniswapSupEthxPool: 0xCa2054E3E5A940473DD6dCC4a67ECdfdFa8c0b72, + uniswapSwapRouter: 0x94cC0AaC535CCDB3C01d6787D6413C739ae12bc4, + daoTreasury: 0xe7143e87661418DEA122941e01Fdb3f9Acfd02aB, + isUnlockAvailable: true + }); + } +} diff --git a/packages/contracts/script/upgrades/deploy-locker-upgrade.s.sol b/packages/contracts/script/upgrades/deploy-locker-upgrade.s.sol new file mode 100644 index 0000000..6113db8 --- /dev/null +++ b/packages/contracts/script/upgrades/deploy-locker-upgrade.s.sol @@ -0,0 +1,180 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.23; + +import { console2 } from "forge-std/console2.sol"; +import { SupDeployer } from "script/SupDeployer.s.sol"; +import { ISuperToken } from "@superfluid-finance/ethereum-contracts/contracts/interfaces/superfluid/ISuperToken.sol"; +import { FluidLocker } from "src/FluidLocker.sol"; +import { AddressRegistry } from "script/config/AddressRegistry.sol"; +import { UpgradeableBeacon } from "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol"; +import { IStakingRewardController } from "src/interfaces/IStakingRewardController.sol"; +import { IEPProgramManager } from "src/interfaces/IEPProgramManager.sol"; +import { IUniswapV3Pool } from "@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol"; +import { INonfungiblePositionManager } from "@uniswap/v3-periphery/contracts/interfaces/INonfungiblePositionManager.sol"; +import { IV3SwapRouter } from "@uniswap/swap-router-contracts/contracts/interfaces/IV3SwapRouter.sol"; + +// forge script script/upgrades/deploy-locker-upgrade.s.sol:DeployLockerUpgrade --ffi --via-ir --rpc-url $BASE_MAINNET_RPC_URL --account SUP_DEPLOYER +contract DeployLockerUpgrade is SupDeployer { + function run() public { + _showGitRevision(); + + uint256 chainId = block.chainid; + + // Get configuration + AddressRegistry.LockerDeploymentParameters memory lockerParams = + AddressRegistry.getLockerDeploymentParameters(chainId); + + // Get the current live Locker Implementation + UpgradeableBeacon lockerBeacon = UpgradeableBeacon(lockerParams.lockerBeacon); + FluidLocker currentLockerImplementation = FluidLocker(payable(lockerBeacon.implementation())); + + // Validate the deployment parameters; + _validateDeploymentParameters(currentLockerImplementation, lockerParams); + + // Start Deployment : + address deployer = _startBroadcast(); + + _logDeploymentParameters(deployer, lockerParams); + + address newFluidLockerImplementation = address( + new FluidLocker( + ISuperToken(lockerParams.sup), + IEPProgramManager(lockerParams.programManager), + IStakingRewardController(lockerParams.stakingRewardController), + lockerParams.fontaineBeacon, + lockerParams.isUnlockAvailable, + INonfungiblePositionManager(lockerParams.uniswapNonFungiblePositionManager), + IUniswapV3Pool(lockerParams.uniswapSupEthxPool), + IV3SwapRouter(lockerParams.uniswapSwapRouter), + lockerParams.daoTreasury + ) + ); + + _logDeploymentResults(newFluidLockerImplementation); + + _stopBroadcast(); + } + + /** + * @notice Returns whether or not a parameter should be checked against current implementation + * @dev if the `paramName` evaluates to true, the check is not necessary + * @param paramName the env var corresponding to a parameter change + * @return shouldCheck true if the param should be check (i.e. no update) false otherwise + */ + function _shouldCheckParam(string memory paramName) internal view returns (bool shouldCheck) { + shouldCheck = !vm.envOr(paramName, false); + } + + /** + * @notice Validates that the new deployment parameters match the current implementation + * @dev Each parameter check can be bypassed by setting the corresponding env var to true. + * This is useful when intentionally updating a specific immutable parameter. + * + * Env vars to bypass checks: + * - UPDATE_SUP: Skip SUP token address validation + * - UPDATE_PROGRAM_MANAGER: Skip EP Program Manager address validation + * - UPDATE_STAKING_REWARD_CONTROLLER: Skip Staking Reward Controller address validation + * - UPDATE_FONTAINE_BEACON: Skip Fontaine Beacon address validation + * - UPDATE_UNLOCK_AVAILABLE: Skip unlock availability flag validation + * - UPDATE_NONFUNGIBLE_POSITION_MANAGER: Skip Uniswap NFT Position Manager address validation + * - UPDATE_ETH_SUP_POOL: Skip ETH/SUP Uniswap V3 Pool address validation + * - UPDATE_SWAP_ROUTER: Skip Uniswap V3 Swap Router address validation + * - UPDATE_DAO_TREASURY: Skip DAO Treasury address validation + * + * @param lockerImpl The current FluidLocker implementation to validate against + * @param lockerParams The address lockerParams containing the new deployment parameters + */ + function _validateDeploymentParameters( + FluidLocker lockerImpl, + AddressRegistry.LockerDeploymentParameters memory lockerParams + ) internal view { + if (_shouldCheckParam("UPDATE_SUP")) { + require(address(lockerImpl.FLUID()) == lockerParams.sup, "SUP is not meant to be updated"); + } + + if (_shouldCheckParam("UPDATE_PROGRAM_MANAGER")) { + require( + address(lockerImpl.EP_PROGRAM_MANAGER()) == lockerParams.programManager, + "ProgramManager is not meant to be updated" + ); + } + + if (_shouldCheckParam("UPDATE_STAKING_REWARD_CONTROLLER")) { + require( + address(lockerImpl.STAKING_REWARD_CONTROLLER()) == lockerParams.stakingRewardController, + "StakingRewardController is not meant to be updated" + ); + } + + if (_shouldCheckParam("UPDATE_FONTAINE_BEACON")) { + require( + address(lockerImpl.FONTAINE_BEACON()) == lockerParams.fontaineBeacon, + "FontaineBeacon is not meant to be updated" + ); + } + + if (_shouldCheckParam("UPDATE_UNLOCK_AVAILABLE")) { + require( + lockerImpl.UNLOCK_AVAILABLE() == lockerParams.isUnlockAvailable, + "UnlockAvailable is not meant to be updated" + ); + } + + if (_shouldCheckParam("UPDATE_NONFUNGIBLE_POSITION_MANAGER")) { + require( + address(lockerImpl.NONFUNGIBLE_POSITION_MANAGER()) == lockerParams.uniswapNonFungiblePositionManager, + "NonfungiblePositionManager is not meant to be updated" + ); + } + + if (_shouldCheckParam("UPDATE_ETH_SUP_POOL")) { + require( + address(lockerImpl.ETH_SUP_POOL()) == lockerParams.uniswapSupEthxPool, + "EthSupPool is not meant to be updated" + ); + } + + if (_shouldCheckParam("UPDATE_SWAP_ROUTER")) { + require( + address(lockerImpl.SWAP_ROUTER()) == lockerParams.uniswapSwapRouter, + "SwapRouter is not meant to be updated" + ); + } + + if (_shouldCheckParam("UPDATE_DAO_TREASURY")) { + require(lockerImpl.DAO_TREASURY() == lockerParams.daoTreasury, "DaoTreasury is not meant to be updated"); + } + } + + function _logDeploymentParameters(address deployer, AddressRegistry.LockerDeploymentParameters memory lockerParams) + internal + pure + { + console2.log("DEPLOYING NEW `FluidLocker` IMPLEMENTATION CONTRACT .........."); + console2.log(""); + console2.log(""); + console2.log("*----------------------------------* DEPLOYMENT SETTINGS *---------------------------------*"); + console2.log("| "); + console2.log("| DEPLOYER : %s", deployer); + console2.log("| SUP_ADDRESS : %s", lockerParams.sup); + console2.log("| PROGRAM_MANAGER_ADDRESS : %s", lockerParams.programManager); + console2.log("| STAKING_REWARD_CONTROLLER_ADDRESS : %s", lockerParams.stakingRewardController); + console2.log("| FONTAINE_BEACON_ADDRESS : %s", lockerParams.fontaineBeacon); + console2.log("| IS_UNLOCK_AVAILABLE : %s", lockerParams.isUnlockAvailable); + console2.log("| DAO_TREASURY_ADDRESS : %s", lockerParams.daoTreasury); + console2.log("| NONFUNGIBLE_POSITION_MANAGER_ADDRESS : %s", lockerParams.uniswapNonFungiblePositionManager); + console2.log("| ETH_SUP_POOL_ADDRESS : %s", lockerParams.uniswapSupEthxPool); + console2.log("| SWAP_ROUTER_ADDRESS : %s", lockerParams.uniswapSwapRouter); + console2.log("| "); + console2.log("*------------------------------------------------------------------------------------------*"); + } + + function _logDeploymentResults(address newFluidLockerImplementation) internal pure { + console2.log(""); + console2.log("*----------------------------------* DEPLOYMENT SUMMARY *----------------------------------*"); + console2.log("| |"); + console2.log("| FluidLocker (Logic) : deployed at %s |", newFluidLockerImplementation); + console2.log("*------------------------------------------------------------------------------------------*"); + console2.log(""); + } +} From cd9ac643296552b5e9f1a7fd86e0cab9e429efca Mon Sep 17 00:00:00 2001 From: Pilou <76021631+0xPilou@users.noreply.github.com> Date: Thu, 29 Jan 2026 17:17:00 +0100 Subject: [PATCH 4/9] chore: added comments --- .../upgrades/deploy-locker-upgrade.s.sol | 47 +++++++++++++++++-- 1 file changed, 44 insertions(+), 3 deletions(-) diff --git a/packages/contracts/script/upgrades/deploy-locker-upgrade.s.sol b/packages/contracts/script/upgrades/deploy-locker-upgrade.s.sol index 6113db8..ab33778 100644 --- a/packages/contracts/script/upgrades/deploy-locker-upgrade.s.sol +++ b/packages/contracts/script/upgrades/deploy-locker-upgrade.s.sol @@ -13,7 +13,45 @@ import { IUniswapV3Pool } from "@uniswap/v3-core/contracts/interfaces/IUniswapV3 import { INonfungiblePositionManager } from "@uniswap/v3-periphery/contracts/interfaces/INonfungiblePositionManager.sol"; import { IV3SwapRouter } from "@uniswap/swap-router-contracts/contracts/interfaces/IV3SwapRouter.sol"; -// forge script script/upgrades/deploy-locker-upgrade.s.sol:DeployLockerUpgrade --ffi --via-ir --rpc-url $BASE_MAINNET_RPC_URL --account SUP_DEPLOYER +/** + * @title DeployLockerUpgrade + * @notice Deployment script for upgrading the FluidLocker implementation + * + * @dev This script deploys a new FluidLocker implementation contract with updated immutable parameters. + * It includes a safety mechanism to prevent accidental parameter changes. + * + * == Deployment Workflow == + * + * 1. Update the desired parameter values in `AddressRegistry.sol` for the target network + * + * 2. For each parameter you intend to change, set the corresponding environment variable to `true`: + * - UPDATE_SUP=true + * - UPDATE_PROGRAM_MANAGER=true + * - UPDATE_STAKING_REWARD_CONTROLLER=true + * - UPDATE_FONTAINE_BEACON=true + * - UPDATE_UNLOCK_AVAILABLE=true + * - UPDATE_NONFUNGIBLE_POSITION_MANAGER=true + * - UPDATE_ETH_SUP_POOL=true + * - UPDATE_SWAP_ROUTER=true + * - UPDATE_DAO_TREASURY=true + * + * 3. Run the deployment script (see command below) + * + * == Safety Mechanism == + * + * The script compares each parameter in `AddressRegistry` against the current live implementation. + * If a parameter differs and the corresponding env var is NOT set, the script reverts. + * This ensures that parameter changes are always intentional and explicitly acknowledged. + * + * == Example == + * + * To deploy an upgrade that only changes the DAO treasury address: + * ``` + * export UPDATE_DAO_TREASURY=true + * forge script script/upgrades/deploy-locker-upgrade.s.sol:DeployLockerUpgrade \ + * --ffi --via-ir --rpc-url $BASE_MAINNET_RPC_URL --account SUP_DEPLOYER + * ``` + */ contract DeployLockerUpgrade is SupDeployer { function run() public { _showGitRevision(); @@ -28,14 +66,16 @@ contract DeployLockerUpgrade is SupDeployer { UpgradeableBeacon lockerBeacon = UpgradeableBeacon(lockerParams.lockerBeacon); FluidLocker currentLockerImplementation = FluidLocker(payable(lockerBeacon.implementation())); - // Validate the deployment parameters; + // Validate the deployment parameters - will revert in case of inexplicit parameter change _validateDeploymentParameters(currentLockerImplementation, lockerParams); - // Start Deployment : + // Start Deployment address deployer = _startBroadcast(); + // Log parameters used for deployment _logDeploymentParameters(deployer, lockerParams); + // Deploy the new Locker Implementation address newFluidLockerImplementation = address( new FluidLocker( ISuperToken(lockerParams.sup), @@ -50,6 +90,7 @@ contract DeployLockerUpgrade is SupDeployer { ) ); + // Log deployment results _logDeploymentResults(newFluidLockerImplementation); _stopBroadcast(); From 02a62708d545c3ceba5de48dc4ee05fc3597ef81 Mon Sep 17 00:00:00 2001 From: Pilou <76021631+0xPilou@users.noreply.github.com> Date: Fri, 30 Jan 2026 09:53:45 +0100 Subject: [PATCH 5/9] feat: added script to deploy factory upgrade --- .../script/config/AddressRegistry.sol | 57 ++++++- .../upgrades/deploy-factory-upgrade.s.sol | 152 ++++++++++++++++++ 2 files changed, 207 insertions(+), 2 deletions(-) create mode 100644 packages/contracts/script/upgrades/deploy-factory-upgrade.s.sol diff --git a/packages/contracts/script/config/AddressRegistry.sol b/packages/contracts/script/config/AddressRegistry.sol index f1269ff..7579bba 100644 --- a/packages/contracts/script/config/AddressRegistry.sol +++ b/packages/contracts/script/config/AddressRegistry.sol @@ -2,6 +2,13 @@ pragma solidity ^0.8.23; library AddressRegistry { + struct FactoryDeploymentParameters { + address lockerFactory; + address lockerBeacon; + address stakingRewardController; + bool isPaused; + } + struct LockerDeploymentParameters { address lockerBeacon; address sup; @@ -29,6 +36,20 @@ library AddressRegistry { } } + function getFactoryDeploymentParameters(uint256 chainId) + internal + pure + returns (FactoryDeploymentParameters memory params) + { + if (chainId == 8453) { + params = getBaseFactoryDeploymentParameters(); + } else if (chainId == 84_532) { + params = getBaseSepoliaFactoryDeploymentParameters(); + } else { + revert("Unsupported chainId"); + } + } + /** * @dev Get Base Mainnet address registry */ @@ -47,8 +68,16 @@ library AddressRegistry { }); } - function getLocalLockerDeploymentParameters() internal pure returns (LockerDeploymentParameters memory addresses) { - return getBaseLockerDeploymentParameters(); + /** + * @dev Get Base Mainnet Factory deployment parameters + */ + function getBaseFactoryDeploymentParameters() internal pure returns (FactoryDeploymentParameters memory params) { + return FactoryDeploymentParameters({ + lockerFactory: 0xA6694cAB43713287F7735dADc940b555db9d39D9, + lockerBeacon: 0x664161f0974F5B17FB1fD3FDcE5D1679E829176c, + stakingRewardController: 0xb19Ae25A98d352B36CED60F93db926247535048b, + isPaused: false + }); } /** @@ -72,4 +101,28 @@ library AddressRegistry { isUnlockAvailable: true }); } + + /** + * @dev Get Base Sepolia Factory deployment parameters + */ + function getBaseSepoliaFactoryDeploymentParameters() + internal + pure + returns (FactoryDeploymentParameters memory params) + { + return FactoryDeploymentParameters({ + lockerFactory: 0x897D343D24Ac5b84838B976Cf37036EDEfe3E967, + lockerBeacon: 0xf2880c6D68080393C1784f978417a96ab4f37c38, + stakingRewardController: 0x9FC0Bb109F3e733Bd84B30F8D89685b0304fC018, + isPaused: false + }); + } + + function getLocalLockerDeploymentParameters() internal pure returns (LockerDeploymentParameters memory addresses) { + return getBaseLockerDeploymentParameters(); + } + + function getLocalFactoryDeploymentParameters() internal pure returns (FactoryDeploymentParameters memory params) { + return getBaseFactoryDeploymentParameters(); + } } diff --git a/packages/contracts/script/upgrades/deploy-factory-upgrade.s.sol b/packages/contracts/script/upgrades/deploy-factory-upgrade.s.sol new file mode 100644 index 0000000..369f880 --- /dev/null +++ b/packages/contracts/script/upgrades/deploy-factory-upgrade.s.sol @@ -0,0 +1,152 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.23; + +/** + * @title DeployLockerFactoryUpgrade + * @notice Deployment script for upgrading the FluidLockerFactory implementation + * + * @dev This script deploys a new FluidLockerFactory implementation contract with updated immutable parameters. + * It includes a safety mechanism to prevent accidental parameter changes. + * + * == Deployment Workflow == + * + * 1. Update the desired parameter values in `AddressRegistry.sol` for the target network + * + * 2. For each parameter you intend to change, set the corresponding environment variable to `true`: + * - UPDATE_LOCKER_BEACON=true + * - UPDATE_STAKING_REWARD_CONTROLLER=true + * - UPDATE_IS_PAUSED=true + * + * 3. Run the deployment script (see command below) + * + * == Safety Mechanism == + * + * The script compares each parameter in `AddressRegistry` against the current live implementation. + * If a parameter differs and the corresponding env var is NOT set, the script reverts. + * This ensures that parameter changes are always intentional and explicitly acknowledged. + * + * == Example == + * + * To deploy an upgrade that only changes the pause status: + * ``` + * export UPDATE_IS_PAUSED=true + * forge script script/upgrades/deploy-factory-upgrade.s.sol:DeployFactoryUpgrade \ + * --ffi --rpc-url $BASE_MAINNET_RPC_URL --account SUP_DEPLOYER + * ``` + */ +import { console2 } from "forge-std/console2.sol"; +import { SupDeployer } from "script/SupDeployer.s.sol"; +import { FluidLockerFactory } from "src/FluidLockerFactory.sol"; +import { AddressRegistry } from "script/config/AddressRegistry.sol"; +import { IStakingRewardController } from "src/interfaces/IStakingRewardController.sol"; + +contract DeployFactoryUpgrade is SupDeployer { + function run() public { + _showGitRevision(); + + uint256 chainId = block.chainid; + + // Get configuration + AddressRegistry.FactoryDeploymentParameters memory factoryParams = + AddressRegistry.getFactoryDeploymentParameters(chainId); + + // Get the current live Factory Implementation + FluidLockerFactory currentFactoryImplementation = FluidLockerFactory(factoryParams.lockerFactory); + + // Validate the deployment parameters - will revert in case of inexplicit parameter change + _validateDeploymentParameters(currentFactoryImplementation, factoryParams); + + // Start Deployment + address deployer = _startBroadcast(); + + // Log parameters used for deployment + _logDeploymentParameters(deployer, factoryParams); + + // Deploy the new Factory Implementation + address newFluidLockerFactoryImplementation = address( + new FluidLockerFactory( + factoryParams.lockerBeacon, + IStakingRewardController(factoryParams.stakingRewardController), + factoryParams.isPaused + ) + ); + + // Log deployment results + _logDeploymentResults(newFluidLockerFactoryImplementation); + + _stopBroadcast(); + } + + /** + * @notice Returns whether or not a parameter should be checked against current implementation + * @dev if the `paramName` evaluates to true, the check is not necessary + * @param paramName the env var corresponding to a parameter change + * @return shouldCheck true if the param should be check (i.e. no update) false otherwise + */ + function _shouldCheckParam(string memory paramName) internal view returns (bool shouldCheck) { + shouldCheck = !vm.envOr(paramName, false); + } + + /** + * @notice Validates that the new deployment parameters match the current implementation + * @dev Each parameter check can be bypassed by setting the corresponding env var to true. + * This is useful when intentionally updating a specific immutable parameter. + * + * Env vars to bypass checks: + * - UPDATE_LOCKER_BEACON: Skip Locker Beacon address validation + * - UPDATE_STAKING_REWARD_CONTROLLER: Skip Staking Reward Controller address validation + * - UPDATE_IS_PAUSED: Skip pause status validation + * + * @param factoryImpl The current FluidLockerFactory implementation to validate against + * @param factoryParams The deployment parameters containing the new values + */ + function _validateDeploymentParameters( + FluidLockerFactory factoryImpl, + AddressRegistry.FactoryDeploymentParameters memory factoryParams + ) internal view { + if (_shouldCheckParam("UPDATE_LOCKER_BEACON")) { + require( + address(factoryImpl.LOCKER_BEACON()) == factoryParams.lockerBeacon, + "LockerBeacon is not meant to be updated" + ); + } + + if (_shouldCheckParam("UPDATE_STAKING_REWARD_CONTROLLER")) { + require( + address(factoryImpl.STAKING_REWARD_CONTROLLER()) == factoryParams.stakingRewardController, + "StakingRewardController is not meant to be updated" + ); + } + + if (_shouldCheckParam("UPDATE_IS_PAUSED")) { + require(factoryImpl.IS_PAUSED() == factoryParams.isPaused, "IsPaused is not meant to be updated"); + } + } + + function _logDeploymentParameters( + address deployer, + AddressRegistry.FactoryDeploymentParameters memory factoryParams + ) internal pure { + console2.log("DEPLOYING NEW `FluidLockerFactory` IMPLEMENTATION CONTRACT .........."); + console2.log(""); + console2.log(""); + console2.log("*----------------------------------* DEPLOYMENT SETTINGS *---------------------------------*"); + console2.log("| "); + console2.log("| DEPLOYER : %s", deployer); + console2.log("| FACTORY_PROXY_ADDRESS : %s", factoryParams.lockerFactory); + console2.log("| LOCKER_BEACON_ADDRESS : %s", factoryParams.lockerBeacon); + console2.log("| STAKING_REWARD_CONTROLLER_ADDRESS : %s", factoryParams.stakingRewardController); + console2.log("| IS_PAUSED : %s", factoryParams.isPaused); + console2.log("| "); + console2.log("*------------------------------------------------------------------------------------------*"); + } + + function _logDeploymentResults(address newFluidLockerFactoryImplementation) internal pure { + console2.log(""); + console2.log("*----------------------------------* DEPLOYMENT SUMMARY *----------------------------------*"); + console2.log("| |"); + console2.log("| FluidLockerFactory (Logic) : deployed at %s |", newFluidLockerFactoryImplementation); + console2.log("*------------------------------------------------------------------------------------------*"); + console2.log(""); + } +} From 5e995dbb91af2fe99150a570ae9ca92d5e08d05d Mon Sep 17 00:00:00 2001 From: Pilou <76021631+0xPilou@users.noreply.github.com> Date: Fri, 30 Jan 2026 10:23:40 +0100 Subject: [PATCH 6/9] feat: added script to deploy StakingRewardController upgrade --- .../script/config/AddressRegistry.sol | 55 ++++++++ ...oy-staking-reward-controller-upgrade.s.sol | 129 ++++++++++++++++++ 2 files changed, 184 insertions(+) create mode 100644 packages/contracts/script/upgrades/deploy-staking-reward-controller-upgrade.s.sol diff --git a/packages/contracts/script/config/AddressRegistry.sol b/packages/contracts/script/config/AddressRegistry.sol index 7579bba..3f43c10 100644 --- a/packages/contracts/script/config/AddressRegistry.sol +++ b/packages/contracts/script/config/AddressRegistry.sol @@ -2,6 +2,11 @@ pragma solidity ^0.8.23; library AddressRegistry { + struct StakingRewardControllerDeploymentParameters { + address stakingRewardController; + address sup; + } + struct FactoryDeploymentParameters { address lockerFactory; address lockerBeacon; @@ -50,6 +55,20 @@ library AddressRegistry { } } + function getStakingRewardControllerDeploymentParameters(uint256 chainId) + internal + pure + returns (StakingRewardControllerDeploymentParameters memory params) + { + if (chainId == 8453) { + params = getBaseStakingRewardControllerDeploymentParameters(); + } else if (chainId == 84_532) { + params = getBaseSepoliaStakingRewardControllerDeploymentParameters(); + } else { + revert("Unsupported chainId"); + } + } + /** * @dev Get Base Mainnet address registry */ @@ -80,6 +99,20 @@ library AddressRegistry { }); } + /** + * @dev Get Base Mainnet StakingRewardController deployment parameters + */ + function getBaseStakingRewardControllerDeploymentParameters() + internal + pure + returns (StakingRewardControllerDeploymentParameters memory params) + { + return StakingRewardControllerDeploymentParameters({ + stakingRewardController: 0xb19Ae25A98d352B36CED60F93db926247535048b, + sup: 0xa69f80524381275A7fFdb3AE01c54150644c8792 + }); + } + /** * @dev Get Base Sepolia configuration */ @@ -118,6 +151,20 @@ library AddressRegistry { }); } + /** + * @dev Get Base Sepolia StakingRewardController deployment parameters + */ + function getBaseSepoliaStakingRewardControllerDeploymentParameters() + internal + pure + returns (StakingRewardControllerDeploymentParameters memory params) + { + return StakingRewardControllerDeploymentParameters({ + stakingRewardController: 0x9FC0Bb109F3e733Bd84B30F8D89685b0304fC018, + sup: 0xFd62b398DD8a233ad37156690631fb9515059d6A + }); + } + function getLocalLockerDeploymentParameters() internal pure returns (LockerDeploymentParameters memory addresses) { return getBaseLockerDeploymentParameters(); } @@ -125,4 +172,12 @@ library AddressRegistry { function getLocalFactoryDeploymentParameters() internal pure returns (FactoryDeploymentParameters memory params) { return getBaseFactoryDeploymentParameters(); } + + function getLocalStakingRewardControllerDeploymentParameters() + internal + pure + returns (StakingRewardControllerDeploymentParameters memory params) + { + return getBaseStakingRewardControllerDeploymentParameters(); + } } diff --git a/packages/contracts/script/upgrades/deploy-staking-reward-controller-upgrade.s.sol b/packages/contracts/script/upgrades/deploy-staking-reward-controller-upgrade.s.sol new file mode 100644 index 0000000..012f9c2 --- /dev/null +++ b/packages/contracts/script/upgrades/deploy-staking-reward-controller-upgrade.s.sol @@ -0,0 +1,129 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.23; + +/** + * @title DeployStakingRewardControllerUpgrade + * @notice Deployment script for upgrading the StakingRewardController implementation + * + * @dev This script deploys a new StakingRewardController implementation contract with updated immutable parameters. + * It includes a safety mechanism to prevent accidental parameter changes. + * + * == Deployment Workflow == + * + * 1. Update the desired parameter values in `AddressRegistry.sol` for the target network + * + * 2. For each parameter you intend to change, set the corresponding environment variable to `true`: + * - UPDATE_SUP=true + * + * 3. Run the deployment script (see command below) + * + * == Safety Mechanism == + * + * The script compares each parameter in `AddressRegistry` against the current live implementation. + * If a parameter differs and the corresponding env var is NOT set, the script reverts. + * This ensures that parameter changes are always intentional and explicitly acknowledged. + * + * == Example == + * + * To deploy an upgrade that changes the SUP token address: + * ``` + * export UPDATE_SUP=true + * forge script script/upgrades/deploy-staking-reward-controller-upgrade.s.sol:DeployStakingRewardControllerUpgrade \ + * --ffi --rpc-url $BASE_MAINNET_RPC_URL --account SUP_DEPLOYER + * ``` + */ +import { console2 } from "forge-std/console2.sol"; +import { SupDeployer } from "script/SupDeployer.s.sol"; +import { StakingRewardController } from "src/StakingRewardController.sol"; +import { AddressRegistry } from "script/config/AddressRegistry.sol"; +import { ISuperToken } from "@superfluid-finance/ethereum-contracts/contracts/interfaces/superfluid/ISuperToken.sol"; + +contract DeployStakingRewardControllerUpgrade is SupDeployer { + function run() public { + _showGitRevision(); + + uint256 chainId = block.chainid; + + // Get configuration + AddressRegistry.StakingRewardControllerDeploymentParameters memory srcParams = + AddressRegistry.getStakingRewardControllerDeploymentParameters(chainId); + + // Get the current live StakingRewardController Implementation + StakingRewardController currentSrcImplementation = StakingRewardController(srcParams.stakingRewardController); + + // Validate the deployment parameters - will revert in case of inexplicit parameter change + _validateDeploymentParameters(currentSrcImplementation, srcParams); + + // Start Deployment + address deployer = _startBroadcast(); + + // Log parameters used for deployment + _logDeploymentParameters(deployer, srcParams); + + // Deploy the new StakingRewardController Implementation + address newStakingRewardControllerImplementation = + address(new StakingRewardController(ISuperToken(srcParams.sup))); + + // Log deployment results + _logDeploymentResults(newStakingRewardControllerImplementation); + + _stopBroadcast(); + } + + /** + * @notice Returns whether or not a parameter should be checked against current implementation + * @dev if the `paramName` evaluates to true, the check is not necessary + * @param paramName the env var corresponding to a parameter change + * @return shouldCheck true if the param should be check (i.e. no update) false otherwise + */ + function _shouldCheckParam(string memory paramName) internal view returns (bool shouldCheck) { + shouldCheck = !vm.envOr(paramName, false); + } + + /** + * @notice Validates that the new deployment parameters match the current implementation + * @dev Each parameter check can be bypassed by setting the corresponding env var to true. + * This is useful when intentionally updating a specific immutable parameter. + * + * Env vars to bypass checks: + * - UPDATE_SUP: Skip SUP token address validation + * + * @param srcImpl The current StakingRewardController implementation to validate against + * @param srcParams The deployment parameters containing the new values + */ + function _validateDeploymentParameters( + StakingRewardController srcImpl, + AddressRegistry.StakingRewardControllerDeploymentParameters memory srcParams + ) internal view { + if (_shouldCheckParam("UPDATE_SUP")) { + require(address(srcImpl.FLUID()) == srcParams.sup, "SUP is not meant to be updated"); + } + } + + function _logDeploymentParameters( + address deployer, + AddressRegistry.StakingRewardControllerDeploymentParameters memory srcParams + ) internal pure { + console2.log("DEPLOYING NEW `StakingRewardController` IMPLEMENTATION CONTRACT .........."); + console2.log(""); + console2.log(""); + console2.log("*----------------------------------* DEPLOYMENT SETTINGS *---------------------------------*"); + console2.log("| "); + console2.log("| DEPLOYER : %s", deployer); + console2.log("| STAKING_REWARD_CONTROLLER_ADDRESS : %s", srcParams.stakingRewardController); + console2.log("| SUP_ADDRESS : %s", srcParams.sup); + console2.log("| "); + console2.log("*------------------------------------------------------------------------------------------*"); + } + + function _logDeploymentResults(address newStakingRewardControllerImplementation) internal pure { + console2.log(""); + console2.log("*----------------------------------* DEPLOYMENT SUMMARY *----------------------------------*"); + console2.log("| |"); + console2.log( + "| StakingRewardController (Logic) : deployed at %s |", newStakingRewardControllerImplementation + ); + console2.log("*------------------------------------------------------------------------------------------*"); + console2.log(""); + } +} From a279de27b769adb8eb62b69d234f4e0a2abf945e Mon Sep 17 00:00:00 2001 From: Pilou <76021631+0xPilou@users.noreply.github.com> Date: Fri, 30 Jan 2026 10:38:38 +0100 Subject: [PATCH 7/9] feat: added script to deploy StakingRewardController upgrade --- .../script/config/AddressRegistry.sol | 55 ++++++++ .../deploy-program-manager-upgrade.s.sol | 130 ++++++++++++++++++ 2 files changed, 185 insertions(+) create mode 100644 packages/contracts/script/upgrades/deploy-program-manager-upgrade.s.sol diff --git a/packages/contracts/script/config/AddressRegistry.sol b/packages/contracts/script/config/AddressRegistry.sol index 3f43c10..e94ea1e 100644 --- a/packages/contracts/script/config/AddressRegistry.sol +++ b/packages/contracts/script/config/AddressRegistry.sol @@ -2,6 +2,11 @@ pragma solidity ^0.8.23; library AddressRegistry { + struct ProgramManagerDeploymentParameters { + address programManager; + address taxDistributionPool; + } + struct StakingRewardControllerDeploymentParameters { address stakingRewardController; address sup; @@ -69,6 +74,20 @@ library AddressRegistry { } } + function getProgramManagerDeploymentParameters(uint256 chainId) + internal + pure + returns (ProgramManagerDeploymentParameters memory params) + { + if (chainId == 8453) { + params = getBaseProgramManagerDeploymentParameters(); + } else if (chainId == 84_532) { + params = getBaseSepoliaProgramManagerDeploymentParameters(); + } else { + revert("Unsupported chainId"); + } + } + /** * @dev Get Base Mainnet address registry */ @@ -113,6 +132,20 @@ library AddressRegistry { }); } + /** + * @dev Get Base Mainnet EPProgramManager deployment parameters + */ + function getBaseProgramManagerDeploymentParameters() + internal + pure + returns (ProgramManagerDeploymentParameters memory params) + { + return ProgramManagerDeploymentParameters({ + programManager: 0x1e32cf099992E9D3b17eDdDFFfeb2D07AED95C6a, + taxDistributionPool: 0xF0f494f4BD2C3A6bF8b49E6f798875301d944C0A + }); + } + /** * @dev Get Base Sepolia configuration */ @@ -165,6 +198,20 @@ library AddressRegistry { }); } + /** + * @dev Get Base Sepolia EPProgramManager deployment parameters + */ + function getBaseSepoliaProgramManagerDeploymentParameters() + internal + pure + returns (ProgramManagerDeploymentParameters memory params) + { + return ProgramManagerDeploymentParameters({ + programManager: 0x71a1975A1009e48E0BF2f621B6835db5Ea1f7706, + taxDistributionPool: 0xBed96F4cE618798C286eE8BF7586BD607d491Ce7 + }); + } + function getLocalLockerDeploymentParameters() internal pure returns (LockerDeploymentParameters memory addresses) { return getBaseLockerDeploymentParameters(); } @@ -180,4 +227,12 @@ library AddressRegistry { { return getBaseStakingRewardControllerDeploymentParameters(); } + + function getLocalProgramManagerDeploymentParameters() + internal + pure + returns (ProgramManagerDeploymentParameters memory params) + { + return getBaseProgramManagerDeploymentParameters(); + } } diff --git a/packages/contracts/script/upgrades/deploy-program-manager-upgrade.s.sol b/packages/contracts/script/upgrades/deploy-program-manager-upgrade.s.sol new file mode 100644 index 0000000..52c709a --- /dev/null +++ b/packages/contracts/script/upgrades/deploy-program-manager-upgrade.s.sol @@ -0,0 +1,130 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.23; + +/** + * @title DeployEPProgramManagerUpgrade + * @notice Deployment script for upgrading the FluidEPProgramManager implementation + * + * @dev This script deploys a new FluidEPProgramManager implementation contract with updated immutable parameters. + * It includes a safety mechanism to prevent accidental parameter changes. + * + * == Deployment Workflow == + * + * 1. Update the desired parameter values in `AddressRegistry.sol` for the target network + * + * 2. For each parameter you intend to change, set the corresponding environment variable to `true`: + * - UPDATE_TAX_DISTRIBUTION_POOL=true + * + * 3. Run the deployment script (see command below) + * + * == Safety Mechanism == + * + * The script compares each parameter in `AddressRegistry` against the current live implementation. + * If a parameter differs and the corresponding env var is NOT set, the script reverts. + * This ensures that parameter changes are always intentional and explicitly acknowledged. + * + * == Example == + * + * To deploy an upgrade that changes the tax distribution pool address: + * ``` + * export UPDATE_TAX_DISTRIBUTION_POOL=true + * forge script script/upgrades/deploy-program-manager-upgrade.s.sol:DeployProgramManagerUpgrade \ + * --ffi --rpc-url $BASE_MAINNET_RPC_URL --account SUP_DEPLOYER + * ``` + */ +import { console2 } from "forge-std/console2.sol"; +import { SupDeployer } from "script/SupDeployer.s.sol"; +import { FluidEPProgramManager } from "src/FluidEPProgramManager.sol"; +import { AddressRegistry } from "script/config/AddressRegistry.sol"; +import { ISuperfluidPool } from "@superfluid-finance/ethereum-contracts/contracts/interfaces/superfluid/ISuperfluid.sol"; + +contract DeployProgramManagerUpgrade is SupDeployer { + function run() public { + _showGitRevision(); + + uint256 chainId = block.chainid; + + // Get configuration + AddressRegistry.ProgramManagerDeploymentParameters memory pmParams = + AddressRegistry.getProgramManagerDeploymentParameters(chainId); + + // Get the current live EPProgramManager Implementation + FluidEPProgramManager currentPmImplementation = FluidEPProgramManager(pmParams.programManager); + + // Validate the deployment parameters - will revert in case of inexplicit parameter change + _validateDeploymentParameters(currentPmImplementation, pmParams); + + // Start Deployment + address deployer = _startBroadcast(); + + // Log parameters used for deployment + _logDeploymentParameters(deployer, pmParams); + + // Deploy the new EPProgramManager Implementation + address newEPProgramManagerImplementation = + address(new FluidEPProgramManager(ISuperfluidPool(pmParams.taxDistributionPool))); + + // Log deployment results + _logDeploymentResults(newEPProgramManagerImplementation); + + _stopBroadcast(); + } + + /** + * @notice Returns whether or not a parameter should be checked against current implementation + * @dev if the `paramName` evaluates to true, the check is not necessary + * @param paramName the env var corresponding to a parameter change + * @return shouldCheck true if the param should be check (i.e. no update) false otherwise + */ + function _shouldCheckParam(string memory paramName) internal view returns (bool shouldCheck) { + shouldCheck = !vm.envOr(paramName, false); + } + + /** + * @notice Validates that the new deployment parameters match the current implementation + * @dev Each parameter check can be bypassed by setting the corresponding env var to true. + * This is useful when intentionally updating a specific immutable parameter. + * + * Env vars to bypass checks: + * - UPDATE_TAX_DISTRIBUTION_POOL: Skip Tax Distribution Pool address validation + * + * @param epmImpl The current FluidEPProgramManager implementation to validate against + * @param pmParams The deployment parameters containing the new values + */ + function _validateDeploymentParameters( + FluidEPProgramManager epmImpl, + AddressRegistry.ProgramManagerDeploymentParameters memory pmParams + ) internal view { + if (_shouldCheckParam("UPDATE_TAX_DISTRIBUTION_POOL")) { + require( + address(epmImpl.TAX_DISTRIBUTION_POOL()) == pmParams.taxDistributionPool, + "TaxDistributionPool is not meant to be updated" + ); + } + } + + function _logDeploymentParameters( + address deployer, + AddressRegistry.ProgramManagerDeploymentParameters memory pmParams + ) internal pure { + console2.log("DEPLOYING NEW `FluidEPProgramManager` IMPLEMENTATION CONTRACT .........."); + console2.log(""); + console2.log(""); + console2.log("*----------------------------------* DEPLOYMENT SETTINGS *---------------------------------*"); + console2.log("| "); + console2.log("| DEPLOYER : %s", deployer); + console2.log("| PROGRAM_MANAGER_ADDRESS : %s", pmParams.programManager); + console2.log("| TAX_DISTRIBUTION_POOL_ADDRESS : %s", pmParams.taxDistributionPool); + console2.log("| "); + console2.log("*------------------------------------------------------------------------------------------*"); + } + + function _logDeploymentResults(address newEPProgramManagerImplementation) internal pure { + console2.log(""); + console2.log("*----------------------------------* DEPLOYMENT SUMMARY *----------------------------------*"); + console2.log("| |"); + console2.log("| FluidEPProgramManager (Logic) : deployed at %s |", newEPProgramManagerImplementation); + console2.log("*------------------------------------------------------------------------------------------*"); + console2.log(""); + } +} From d77b92daa4aebeeddddf6b9bea20e63d9243a8c7 Mon Sep 17 00:00:00 2001 From: Pilou <76021631+0xPilou@users.noreply.github.com> Date: Fri, 30 Jan 2026 10:44:21 +0100 Subject: [PATCH 8/9] feat: added script to deploy Fontaine upgrade --- .../script/config/AddressRegistry.sol | 56 ++++++++ .../upgrades/deploy-fontaine-upgrade.s.sol | 128 ++++++++++++++++++ 2 files changed, 184 insertions(+) create mode 100644 packages/contracts/script/upgrades/deploy-fontaine-upgrade.s.sol diff --git a/packages/contracts/script/config/AddressRegistry.sol b/packages/contracts/script/config/AddressRegistry.sol index e94ea1e..871e041 100644 --- a/packages/contracts/script/config/AddressRegistry.sol +++ b/packages/contracts/script/config/AddressRegistry.sol @@ -2,6 +2,11 @@ pragma solidity ^0.8.23; library AddressRegistry { + struct FontaineDeploymentParameters { + address fontaineBeacon; + address sup; + } + struct ProgramManagerDeploymentParameters { address programManager; address taxDistributionPool; @@ -88,6 +93,20 @@ library AddressRegistry { } } + function getFontaineDeploymentParameters(uint256 chainId) + internal + pure + returns (FontaineDeploymentParameters memory params) + { + if (chainId == 8453) { + params = getBaseFontaineDeploymentParameters(); + } else if (chainId == 84_532) { + params = getBaseSepoliaFontaineDeploymentParameters(); + } else { + revert("Unsupported chainId"); + } + } + /** * @dev Get Base Mainnet address registry */ @@ -145,6 +164,20 @@ library AddressRegistry { taxDistributionPool: 0xF0f494f4BD2C3A6bF8b49E6f798875301d944C0A }); } + + /** + * @dev Get Base Mainnet Fontaine deployment parameters + */ + function getBaseFontaineDeploymentParameters() + internal + pure + returns (FontaineDeploymentParameters memory params) + { + return FontaineDeploymentParameters({ + fontaineBeacon: 0xA26FbA47Da24F7DF11b3E4CF60Dcf7D1691Ae47d, + sup: 0xa69f80524381275A7fFdb3AE01c54150644c8792 + }); + } /** * @dev Get Base Sepolia configuration @@ -212,6 +245,21 @@ library AddressRegistry { }); } + + /** + * @dev Get Base Sepolia Fontaine deployment parameters + */ + function getBaseSepoliaFontaineDeploymentParameters() + internal + pure + returns (FontaineDeploymentParameters memory params) + { + return FontaineDeploymentParameters({ + fontaineBeacon: 0xeBfA246A0BAd08A2A3ffB137ed75601AA41867dE, + sup: 0xFd62b398DD8a233ad37156690631fb9515059d6A + }); + } + function getLocalLockerDeploymentParameters() internal pure returns (LockerDeploymentParameters memory addresses) { return getBaseLockerDeploymentParameters(); } @@ -235,4 +283,12 @@ library AddressRegistry { { return getBaseProgramManagerDeploymentParameters(); } + + function getLocalFontaineDeploymentParameters() + internal + pure + returns (FontaineDeploymentParameters memory params) + { + return getBaseFontaineDeploymentParameters(); + } } diff --git a/packages/contracts/script/upgrades/deploy-fontaine-upgrade.s.sol b/packages/contracts/script/upgrades/deploy-fontaine-upgrade.s.sol new file mode 100644 index 0000000..290c178 --- /dev/null +++ b/packages/contracts/script/upgrades/deploy-fontaine-upgrade.s.sol @@ -0,0 +1,128 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.23; + +/** + * @title DeployFontaineUpgrade + * @notice Deployment script for upgrading the Fontaine implementation + * + * @dev This script deploys a new Fontaine implementation contract with updated immutable parameters. + * It includes a safety mechanism to prevent accidental parameter changes. + * + * == Deployment Workflow == + * + * 1. Update the desired parameter values in `AddressRegistry.sol` for the target network + * + * 2. For each parameter you intend to change, set the corresponding environment variable to `true`: + * - UPDATE_SUP=true + * + * 3. Run the deployment script (see command below) + * + * == Safety Mechanism == + * + * The script compares each parameter in `AddressRegistry` against the current live implementation. + * If a parameter differs and the corresponding env var is NOT set, the script reverts. + * This ensures that parameter changes are always intentional and explicitly acknowledged. + * + * == Example == + * + * To deploy an upgrade that changes the SUP token address: + * ``` + * export UPDATE_SUP=true + * forge script script/upgrades/deploy-fontaine-upgrade.s.sol:DeployFontaineUpgrade \ + * --ffi --rpc-url $BASE_MAINNET_RPC_URL --account SUP_DEPLOYER + * ``` + */ +import { console2 } from "forge-std/console2.sol"; +import { SupDeployer } from "script/SupDeployer.s.sol"; +import { Fontaine } from "src/Fontaine.sol"; +import { AddressRegistry } from "script/config/AddressRegistry.sol"; +import { UpgradeableBeacon } from "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol"; +import { ISuperToken } from "@superfluid-finance/ethereum-contracts/contracts/interfaces/superfluid/ISuperToken.sol"; + +contract DeployFontaineUpgrade is SupDeployer { + function run() public { + _showGitRevision(); + + uint256 chainId = block.chainid; + + // Get configuration + AddressRegistry.FontaineDeploymentParameters memory fontaineParams = + AddressRegistry.getFontaineDeploymentParameters(chainId); + + // Get the current live Fontaine Implementation from the beacon + UpgradeableBeacon fontaineBeacon = UpgradeableBeacon(fontaineParams.fontaineBeacon); + Fontaine currentFontaineImplementation = Fontaine(fontaineBeacon.implementation()); + + // Validate the deployment parameters - will revert in case of inexplicit parameter change + _validateDeploymentParameters(currentFontaineImplementation, fontaineParams); + + // Start Deployment + address deployer = _startBroadcast(); + + // Log parameters used for deployment + _logDeploymentParameters(deployer, fontaineParams); + + // Deploy the new Fontaine Implementation + address newFontaineImplementation = address(new Fontaine(ISuperToken(fontaineParams.sup))); + + // Log deployment results + _logDeploymentResults(newFontaineImplementation); + + _stopBroadcast(); + } + + /** + * @notice Returns whether or not a parameter should be checked against current implementation + * @dev if the `paramName` evaluates to true, the check is not necessary + * @param paramName the env var corresponding to a parameter change + * @return shouldCheck true if the param should be check (i.e. no update) false otherwise + */ + function _shouldCheckParam(string memory paramName) internal view returns (bool shouldCheck) { + shouldCheck = !vm.envOr(paramName, false); + } + + /** + * @notice Validates that the new deployment parameters match the current implementation + * @dev Each parameter check can be bypassed by setting the corresponding env var to true. + * This is useful when intentionally updating a specific immutable parameter. + * + * Env vars to bypass checks: + * - UPDATE_SUP: Skip SUP token address validation + * + * @param fontaineImpl The current Fontaine implementation to validate against + * @param fontaineParams The deployment parameters containing the new values + */ + function _validateDeploymentParameters( + Fontaine fontaineImpl, + AddressRegistry.FontaineDeploymentParameters memory fontaineParams + ) internal view { + if (_shouldCheckParam("UPDATE_SUP")) { + require(address(fontaineImpl.FLUID()) == fontaineParams.sup, "SUP is not meant to be updated"); + } + } + + function _logDeploymentParameters( + address deployer, + AddressRegistry.FontaineDeploymentParameters memory fontaineParams + ) internal pure { + console2.log("DEPLOYING NEW `Fontaine` IMPLEMENTATION CONTRACT .........."); + console2.log(""); + console2.log(""); + console2.log("*----------------------------------* DEPLOYMENT SETTINGS *---------------------------------*"); + console2.log("| "); + console2.log("| DEPLOYER : %s", deployer); + console2.log("| FONTAINE_BEACON_ADDRESS : %s", fontaineParams.fontaineBeacon); + console2.log("| SUP_ADDRESS : %s", fontaineParams.sup); + console2.log("| "); + console2.log("*------------------------------------------------------------------------------------------*"); + } + + function _logDeploymentResults(address newFontaineImplementation) internal pure { + console2.log(""); + console2.log("*----------------------------------* DEPLOYMENT SUMMARY *----------------------------------*"); + console2.log("| |"); + console2.log("| Fontaine (Logic) : deployed at %s |", newFontaineImplementation); + console2.log("*------------------------------------------------------------------------------------------*"); + console2.log(""); + } +} From 1019e6dc97d2574db3cde39be579bb708716cef3 Mon Sep 17 00:00:00 2001 From: Pilou <76021631+0xPilou@users.noreply.github.com> Date: Fri, 30 Jan 2026 10:44:50 +0100 Subject: [PATCH 9/9] chore: archive old deploy script --- .../script/upgrades/{ => .archive}/deploy-factory-impl.s.sol | 0 .../contracts/script/upgrades/{ => .archive}/deploy-sip8.s.sol | 0 .../contracts/script/upgrades/{ => .archive}/deploy-tte.s.sol | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename packages/contracts/script/upgrades/{ => .archive}/deploy-factory-impl.s.sol (100%) rename packages/contracts/script/upgrades/{ => .archive}/deploy-sip8.s.sol (100%) rename packages/contracts/script/upgrades/{ => .archive}/deploy-tte.s.sol (100%) diff --git a/packages/contracts/script/upgrades/deploy-factory-impl.s.sol b/packages/contracts/script/upgrades/.archive/deploy-factory-impl.s.sol similarity index 100% rename from packages/contracts/script/upgrades/deploy-factory-impl.s.sol rename to packages/contracts/script/upgrades/.archive/deploy-factory-impl.s.sol diff --git a/packages/contracts/script/upgrades/deploy-sip8.s.sol b/packages/contracts/script/upgrades/.archive/deploy-sip8.s.sol similarity index 100% rename from packages/contracts/script/upgrades/deploy-sip8.s.sol rename to packages/contracts/script/upgrades/.archive/deploy-sip8.s.sol diff --git a/packages/contracts/script/upgrades/deploy-tte.s.sol b/packages/contracts/script/upgrades/.archive/deploy-tte.s.sol similarity index 100% rename from packages/contracts/script/upgrades/deploy-tte.s.sol rename to packages/contracts/script/upgrades/.archive/deploy-tte.s.sol