datahaven/contracts/script/deploy/DeployImplementation.s.sol

90 lines
4 KiB
Solidity
Raw Permalink Normal View History

feat: contracts upgrade command (#463) ## Contracts upgrade command with simple version tracking This PR aims to take the most minimal changes from #438 to make the upgrade command available. So it adds the `bun cli contracts upgrade` command for deploying a new `DataHavenServiceManager` implementation and upgrading the proxy, and includes a simple version tracking via a `contracts/VERSION` file. ### Contracts **`DataHavenServiceManager.sol`** - Added `_version` storage variable - Added `DATAHAVEN_VERSION()` view function, - Added `updateVersion(string)` function gated by `onlyProxyAdmin` - Added `VersionUpdated` event - The version is set at initialization and updated atomically with proxy upgrades via `upgradeAndCall`. ### CLI **`bun cli contracts upgrade`** works in two modes: _dry-run_ or _execute_. **Dry-run (default)** Deploys the new implementation on-chain (signed by the deployer key), then prints a ready-to-submit JSON payload for the multisig to execute the proxy upgrade. No AVS owner key required. ```bash # Uses version from contracts/VERSION (standard workflow) bun cli contracts upgrade --chain hoodi # Override version for this upgrade only (warns if it differs from contracts/VERSION) bun cli contracts upgrade --chain hoodi --target x.y.z ``` Example output: ```json { "to": "0xProxyAdmin...", "value": "0", "data": "0x...", "description": "Upgrade ServiceManager proxy to 0xNewImpl... and set version to 1.1.0" } ``` **Execute mode (`--execute`)** Deploys the implementation and broadcasts the proxy upgrade + version update in a single atomic `upgradeAndCall` transaction. Requires `AVS_OWNER_PRIVATE_KEY`. Used mostly for testing. ```bash bun cli contracts upgrade --chain anvil --execute ``` --- ### Expected flow - Bump mannually contracts/VERSION (e.g., 1.1.0) - Run bun cli contracts upgrade --chain anvil|hoodi|mainnet
2026-03-02 20:50:10 +00:00
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.27;
import {Script} from "forge-std/Script.sol";
import {console} from "forge-std/console.sol";
// DataHaven imports
import {DataHavenServiceManager} from "../../src/DataHavenServiceManager.sol";
// EigenLayer imports
import {RewardsCoordinator} from "eigenlayer-contracts/src/contracts/core/RewardsCoordinator.sol";
import {AllocationManager} from "eigenlayer-contracts/src/contracts/core/AllocationManager.sol";
// OpenZeppelin imports
import {ProxyAdmin} from "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol";
import {
ITransparentUpgradeableProxy
} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol";
/**
* @title DeployImplementation
* @notice Script for deploying implementation contracts and upgrading proxies
*/
contract DeployImplementation is Script {
function run() public {
// This script is designed to be called with specific function selectors
// Use forge script with --sig "functionName()" to call specific functions
}
/**
* @notice Deploy new ServiceManager implementation
*/
function deployServiceManagerImpl() public {
console.log("Deploying ServiceManager Implementation...");
// Get constructor parameters from environment variables
address rewardsCoordinator = vm.envAddress("REWARDS_COORDINATOR");
address allocationManager = vm.envAddress("ALLOCATION_MANAGER");
require(rewardsCoordinator != address(0), "REWARDS_COORDINATOR not set");
require(allocationManager != address(0), "ALLOCATION_MANAGER not set");
uint256 deployerPrivateKey = uint256(vm.envBytes32("PRIVATE_KEY"));
vm.broadcast(deployerPrivateKey);
DataHavenServiceManager serviceManagerImpl = new DataHavenServiceManager(
RewardsCoordinator(rewardsCoordinator), AllocationManager(allocationManager)
);
console.log("ServiceManager Implementation deployed at:", address(serviceManagerImpl));
}
/**
* @notice Update ServiceManager proxy and set version in one transaction.
* @dev Uses upgradeAndCall so the version update is atomically bundled with the upgrade.
* updateVersion is gated by onlyProxyAdmin, and upgradeAndCall executes the calldata
* with msg.sender = ProxyAdmin satisfying that check.
* The AVS owner owns the ProxyAdmin, so the trust chain is: AVS owner ProxyAdmin updateVersion.
*/
function updateServiceManagerProxyWithVersion() public {
console.log("Updating ServiceManager proxy with version...");
// Get addresses and version from environment variables
address serviceManager = vm.envAddress("SERVICE_MANAGER");
address newImplementation = vm.envAddress("SERVICE_MANAGER_IMPL");
address proxyAdmin = vm.envAddress("PROXY_ADMIN");
string memory newVersion = vm.envString("NEW_VERSION");
require(serviceManager != address(0), "SERVICE_MANAGER not set");
require(newImplementation != address(0), "SERVICE_MANAGER_IMPL not set");
require(newImplementation.code.length > 0, "SERVICE_MANAGER_IMPL is not a contract");
require(proxyAdmin != address(0), "PROXY_ADMIN not set");
require(bytes(newVersion).length > 0, "NEW_VERSION not set");
// Encode the updateVersion call — gated by onlyProxyAdmin, satisfied since
// upgradeAndCall executes this calldata with msg.sender = ProxyAdmin
bytes memory data = abi.encodeWithSignature("updateVersion(string)", newVersion);
// AVS owner owns the ProxyAdmin (transferred during deployment)
uint256 avsOwnerPrivateKey = uint256(vm.envBytes32("AVS_OWNER_PRIVATE_KEY"));
vm.broadcast(avsOwnerPrivateKey);
ProxyAdmin(proxyAdmin)
.upgradeAndCall(
ITransparentUpgradeableProxy(payable(serviceManager)), newImplementation, data
);
console.log("ServiceManager proxy updated to:", newImplementation);
console.log("Version updated to:", newVersion);
}
}