Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions old-builds/build-info-v1/b216d03a13a7aeb5.json

Large diffs are not rendered by default.

146 changes: 145 additions & 1 deletion script/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ REQUESTER_WHITELIST="0x1234567890123456789012345678901234567890"
# CUSTOM_CURRENCY="" # Optional, defaults to none or USDC.e on Polygon
# MINIMUM_BOND_AMOUNT="100000000" # Optional, defaults to 100 USDC.e on Polygon
# MAXIMUM_BOND_AMOUNT="100000000000" # Optional, defaults to 100,000 USDC.e on Polygon

# ManagedOptimisticOracleV2 Upgrade-specific variables
PROXY_ADDRESS="0x1234567890123456789012345678901234567890"
REFERENCE_BUILD_VERSION="1" # Required, integer version to derive reference paths (e.g., 1 for build-info-v1)

```

## AddressWhitelist Deployment
Expand Down Expand Up @@ -212,4 +217,143 @@ The `ManagedOptimisticOracleV2` contract:
- Supports role-based access control with config and upgrade admins
- Allows request managers to set custom bonds, liveness, and proposer whitelists
- Enforces maximum bonds and minimum liveness set by admins
- Requires whitelisted requesters and proposers
- Requires whitelisted requesters and proposers

## ManagedOptimisticOracleV2 Upgrade

The `UpgradeManagedOptimisticOracleV2.s.sol` script upgrades the `ManagedOptimisticOracleV2` contract implementation using OpenZeppelin Upgrades library.

### Prerequisites

The old build info should normally be created and committed at the time of deployment. If this was missed, you need to generate it before running the upgrade script:

```bash
# 1. Checkout the exact commit corresponding to the previous version deployment
git checkout <commit-hash-of-previous-version>

# 2. Create a new branch for the previous version build info (e.g., for v1)
git checkout -b add-build-info-v1

# 3. Generate build info for the previous version
forge clean && forge build --build-info --build-info-path old-builds/build-info-v1

# 4. Add and commit the build info to the repository
git add old-builds/build-info-v1/
git commit -s -m "fix: add build info for version v1"

# 5. Push the branch and create a PR
git push origin add-build-info-v1
# Create PR on GitHub, get review, and merge

# 6. Switch back to the latest branch for the upgrade
git checkout master # or your latest development branch
git pull origin master # ensure you have the latest changes

# 7. Clean and rebuild for the upgrade script
forge clean && forge build
```

**Note**: The `REFERENCE_BUILD_VERSION` environment variable should match the version number in the build info path (e.g., `REFERENCE_BUILD_VERSION=1` for `old-builds/build-info-v1`).

### Environment Variables

| Variable | Required | Description |
|----------|----------|-------------|
| `MNEMONIC` | Yes | The mnemonic phrase for the upgrade admin wallet (uses 0 index address) |
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question: here "upgrade admin" is the “default admin”?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, we made an alias UPGRADE_ADMIN_ROLE = DEFAULT_ADMIN_ROLE that is used in onlyUpgradeAdmin check

| `PROXY_ADDRESS` | Yes | Address of the existing proxy contract to upgrade |
| `REFERENCE_BUILD_VERSION` | Yes | Integer version number to derive reference contract and build info dir (e.g., 1 for "build-info-v1:ManagedOptimisticOracleV2" and "old-builds/build-info-v1") |

### Usage Examples

```bash
forge script script/UpgradeManagedOptimisticOracleV2.s.sol --rpc-url "YOUR_RPC_URL" --broadcast
```

**Note**: The script automatically detects whether the MNEMONIC corresponds to the actual upgrade admin and adjusts its behavior accordingly.

### Features

- **UUPS upgradeable**: Uses UUPS (Universal Upgradeable Proxy Standard) pattern
- **Mandatory reference validation**: Always uses reference contracts for upgrade safety validation
- **Dual execution modes**: Supports both direct execution and multisig transaction data generation
- **Upgrade simulation**: In multisig mode, simulates the upgrade transaction to verify it would succeed
- **Automatic admin detection**: Automatically fetches the upgrade admin from the proxy contract
- **Detailed logging**: Provides comprehensive upgrade information and status updates

### Important Notes

1. **Upgrade Admin**: The script automatically fetches the actual upgrade admin from the proxy contract using the `owner()` function. It compares this with the address derived from the mnemonic to determine the execution mode.

2. **Multisig Support**: If the MNEMONIC doesn't correspond to the actual upgrade admin (e.g., if it's a multisig wallet), the script will:
- Deploy the new implementation using the mnemonic's private key
- Simulate the upgrade transaction to verify it would succeed
- Generate transaction data for manual multisig execution

3. **Upgrade Validation**: The script automatically derives reference contract and build info directory from the `REFERENCE_BUILD_VERSION` environment variable. The upgrade validation uses the automatically derived reference paths based on `REFERENCE_BUILD_VERSION`.

4. **Upgrade Simulation**: In multisig mode, the script simulates the upgrade transaction using `vm.startPrank()` to impersonate the actual upgrade admin, ensuring the transaction would succeed before generating the multisig data.

5. **Testing**: Always test upgrades on a forked mainnet or testnet before executing on mainnet.

### Etherscan Verification

After upgrading, verify the new implementation contract on Etherscan:

```bash
forge verify-contract <NEW_IMPLEMENTATION_ADDRESS> src/optimistic-oracle-v2/implementation/ManagedOptimisticOracleV2.sol:ManagedOptimisticOracleV2 --chain-id <CHAIN_ID> --etherscan-api-key <YOUR_ETHERSCAN_API_KEY>
```

**Replace:**
- `<NEW_IMPLEMENTATION_ADDRESS>` with the new implementation contract address (printed in the upgrade summary)
- `<CHAIN_ID>` with the network chain ID (1 for Ethereum mainnet, 11155111 for Sepolia, etc.)
- `<YOUR_ETHERSCAN_API_KEY>` with your Etherscan API key

### Example Upgrade Process

#### Direct Execution (Single Signer)
1. **Prepare the upgrade**:
```bash
# Set environment variables
export MNEMONIC="your mnemonic phrase here"
export PROXY_ADDRESS="0x2c0367a9db231ddebd88a94b4f6461a6e47c58b1"
export REFERENCE_BUILD_VERSION="1"
```

2. **Run the upgrade**:
```bash
forge script script/UpgradeManagedOptimisticOracleV2.s.sol --rpc-url "YOUR_RPC_URL" --broadcast
```

3. **Verify the new implementation**:
```bash
# Use the new implementation address from the upgrade summary
forge verify-contract 0xNEW_IMPLEMENTATION_ADDRESS src/optimistic-oracle-v2/implementation/ManagedOptimisticOracleV2.sol:ManagedOptimisticOracleV2 --chain-id 137 --etherscan-api-key <YOUR_ETHERSCAN_API_KEY>
```

#### Multisig Execution
1. **Prepare the upgrade**:
```bash
# Set environment variables
export MNEMONIC="your mnemonic phrase here"
export PROXY_ADDRESS="0x2c0367a9db231ddebd88a94b4f6461a6e47c58b1"
export REFERENCE_BUILD_VERSION="1"
```

2. **Generate transaction data and deploy implementation**:
```bash
forge script script/UpgradeManagedOptimisticOracleV2.s.sol --rpc-url "YOUR_RPC_URL" --broadcast
```

The script will:
- Deploy the new implementation using your mnemonic's private key
- Simulate the upgrade transaction to verify it would succeed
- Generate transaction data for multisig execution
- Display the new implementation address and transaction data

3. **Execute via multisig**: Use the generated transaction data in your multisig wallet to execute the upgrade.

4. **Verify the new implementation**:
```bash
# Use the new implementation address from the script output
forge verify-contract 0xNEW_IMPLEMENTATION_ADDRESS src/optimistic-oracle-v2/implementation/ManagedOptimisticOracleV2.sol:ManagedOptimisticOracleV2 --chain-id 137 --etherscan-api-key <YOUR_ETHERSCAN_API_KEY>
```
124 changes: 124 additions & 0 deletions script/UpgradeManagedOptimisticOracleV2.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.0;

import {Script} from "forge-std/Script.sol";
import {console} from "forge-std/console.sol";
import {Upgrades} from "@openzeppelin/foundry-upgrades/Upgrades.sol";
import {Options} from "@openzeppelin/foundry-upgrades/Options.sol";

import {ManagedOptimisticOracleV2} from "../src/optimistic-oracle-v2/implementation/ManagedOptimisticOracleV2.sol";

/**
* @title Upgrade script for ManagedOptimisticOracleV2
* @notice Upgrades the ManagedOptimisticOracleV2 contract implementation using OZ Upgrades
*
* Environment variables:
* - MNEMONIC: Required. The mnemonic phrase for the upgrade admin wallet
* - PROXY_ADDRESS: Required. Address of the existing proxy contract to upgrade
* - REFERENCE_BUILD_VERSION: Required. Integer version number to derive reference contract and build info dir (e.g., 1 for "build-info-v1:ManagedOptimisticOracleV2" and "old-builds/build-info-v1")
*/
contract UpgradeManagedOptimisticOracleV2 is Script {
function run() external {
uint256 deployerPrivateKey = _getDeployerPrivateKey();
address deployerAddress = vm.addr(deployerPrivateKey);

// Get the proxy address to upgrade
address proxyAddress = vm.envAddress("PROXY_ADDRESS");

// Fetch the upgrade admin from the contract
address upgradeAdmin = ManagedOptimisticOracleV2(proxyAddress).owner();

// Required reference build version for upgrade validation
uint256 referenceBuildVersion = vm.envUint("REFERENCE_BUILD_VERSION");

// Build upgrade validation options
Options memory opts;
opts.referenceContract =
string.concat("build-info-v", vm.toString(referenceBuildVersion), ":ManagedOptimisticOracleV2");
opts.referenceBuildInfoDir = string.concat("old-builds/build-info-v", vm.toString(referenceBuildVersion));

// Log initial setup
console.log("Deployer Address:", deployerAddress);
console.log("Upgrade Admin:", upgradeAdmin);
console.log("Proxy Address:", proxyAddress);
console.log("Reference Contract:", opts.referenceContract);
console.log("Reference Build Info Dir:", opts.referenceBuildInfoDir);

// Check if we need to impersonate or can execute directly
bool shouldImpersonate = upgradeAdmin != deployerAddress;

if (shouldImpersonate) {
// Multisig mode - deploy implementation and generate transaction data
console.log("\n=== IMPERSONATION MODE ===");
console.log("MNEMONIC does not correspond to upgrade admin");
console.log("Deploying new implementation and generating upgrade transaction data");

// Deploy the new implementation
console.log("\n=== DEPLOYING NEW IMPLEMENTATION ===");
vm.startBroadcast(deployerPrivateKey);
address newImplementationAddress =
Upgrades.prepareUpgrade("ManagedOptimisticOracleV2.sol:ManagedOptimisticOracleV2", opts);
vm.stopBroadcast();
console.log("New Implementation Address:", newImplementationAddress);

// Generate upgrade transaction data
bytes memory upgradeData =
abi.encodeWithSignature("upgradeToAndCall(address,bytes)", newImplementationAddress, bytes(""));

// Simulate the upgrade transaction to verify it would succeed
console.log("\n=== SIMULATING UPGRADE TRANSACTION ===");
vm.startPrank(upgradeAdmin);
(bool success, bytes memory result) = proxyAddress.call(upgradeData);
vm.stopPrank();

if (success) {
console.log("Upgrade simulation successful!");
} else {
console.log("Upgrade simulation failed!");
console.log("Error:", vm.toString(result));
revert("Upgrade simulation failed - check the error above");
}

// Log transaction data for multisig
console.log("\n=== MULTISIG UPGRADE TRANSACTION DATA ===");
console.log("Target Contract:", proxyAddress);
console.log("Transaction Data:", vm.toString(upgradeData));
console.log("Upgrade Admin:", upgradeAdmin);
console.log("Chain ID:", block.chainid);
console.log("\nUse this transaction data in your multisig wallet to execute the upgrade.");
console.log("The new implementation has been deployed at:", newImplementationAddress);
} else {
// Direct mode - execute upgrade directly
console.log("\n=== DIRECT EXECUTION MODE ===");
console.log("MNEMONIC corresponds to upgrade admin");
console.log("Executing upgrade directly");

// Start broadcasting transactions with the derived private key
vm.startBroadcast(deployerPrivateKey);

// Upgrade the proxy
Upgrades.upgradeProxy(
proxyAddress, "ManagedOptimisticOracleV2.sol:ManagedOptimisticOracleV2", bytes(""), opts
);

vm.stopBroadcast();

// Output upgrade summary
console.log("\n=== Upgrade Summary ===");
console.log("Proxy Address:", proxyAddress);
console.log("New Implementation Address:", Upgrades.getImplementationAddress(proxyAddress));
console.log("Chain ID:", block.chainid);
console.log("Upgrade Admin:", upgradeAdmin);
}
}

/**
* @notice Derives the deployer's private key from the mnemonic
* @return deployerPrivateKey The derived private key for the deployer
*/
function _getDeployerPrivateKey() internal view returns (uint256) {
string memory mnemonic = vm.envString("MNEMONIC");
// Derive the 0 index address from mnemonic
return vm.deriveKey(mnemonic, 0);
}
}