Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Batch upgrade for all contracts #242

Merged
merged 4 commits into from
Jun 27, 2024
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
4,918 changes: 4,918 additions & 0 deletions .openzeppelin/mainnet.json

Large diffs are not rendered by default.

32 changes: 32 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,38 @@ forge test --gas-report
forge coverage
```

# Upgrades via Multisig tx
## Single Contract Upgrade

To upgrade a single contract, replace `contractName` with the desired contract name [here](https://github.com/stader-labs/ethx/blob/main/scripts/safe-scripts/upgrade.ts#L7):

```bash
npx hardhat run scripts/safe-scripts/upgrade.ts --network <network>
```

## Individual Proposals for All Contracts

To upgrade all contracts in individual proposals:

```bash
npx hardhat run scripts/safe-scripts/upgradeAll.ts --network <network>
```
## Single Proposal for All Contracts

To upgrade all contracts in a single proposal:

```bash
npx hardhat run scripts/safe-scripts/upgradeAllBatch.ts --network <network>
```

`NOTE:`

1. **Lost Network Files**:
If network files are lost under `.openzeppelin`, uncomment force import in the scripts.

2. **Upgrade Checks**:
Both `UpgradeAll` and `UpgradeAllBatch` scripts check the on-chain and local code before preparing the upgrade. If the deployed bytecode and compiled bytecode match exactly, the upgrade will be skipped for that particular contract.

# Integration

Check the Integration guide [here](https://github.com/stader-labs/ethx/blob/mainnet_V0/INTEGRATION.md)
Expand Down
24 changes: 23 additions & 1 deletion scripts/safe-scripts/address.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,29 @@
"safeAddress": "0x8AD81487E553123A5f54fA688892a159AA6Ce53C"
},
"mainnet": {
"contracts": [{ "name": "ETHx", "address": "0xA35b1B31Ce002FBF2058D22F30f95D405200A15b" }],
"contracts": [
{ "name": "ETHx", "address": "0xA35b1B31Ce002FBF2058D22F30f95D405200A15b" },
{ "name": "StaderConfig", "address": "0x4ABEF2263d5A5ED582FC9A9789a41D85b68d69DB" },
{ "name": "VaultFactory", "address": "0x03ABEEC03BF39ac5A5C8886cF3496326d8164E1E" },
{ "name": "Auction", "address": "0x85A22763f94D703d2ee39E9374616ae4C1612569" },
{ "name": "OperatorRewardsCollector", "address": "0x84ffDC9De310144D889540A49052F6d1AdB2C335" },
{ "name": "Penalty", "address": "0x84645f1B80475992Df2C65c28bE6688d15dc6ED6" },
{ "name": "PermissionedNodeRegistry", "address": "0xaf42d795A6D279e9DCc19DC0eE1cE3ecd4ecf5dD" },
{ "name": "PermissionedPool", "address": "0x09134C643A6B95D342BdAf081Fa473338F066572" },
{ "name": "PermissionlessNodeRegistry", "address": "0x4f4Bfa0861F62309934a5551E0B2541Ee82fdcF1" },
{ "name": "PermissionlessPool", "address": "0xd1a72Bd052e0d65B7c26D3dd97A98B74AcbBb6c5" },
{ "name": "PoolSelector", "address": "0x62e0b431990Ea128fe685E764FB04e7d604603B0" },
{ "name": "PoolUtils", "address": "0xeDA89ed8F89D786D816F8E14CF8d2F90c6BF763f" },
{ "name": "SDCollateral", "address": "0x7Af4730cc8EbAd1a050dcad5c03c33D2793EE91f" },
{ "name": "SocializingPool", "address": "0x9d4C3166c59412CEdBe7d901f5fDe41903a1d6Fc" },
{ "name": "SocializingPool", "address": "0x1DE458031bFbe5689deD5A8b9ed57e1E79EaB2A4" },
{ "name": "StaderInsuranceFund", "address": "0xbe3781CE437Cc3fC8c8167913B4d462347D11F20" },
{ "name": "StaderOracle", "address": "0xF64bAe65f6f2a5277571143A24FaaFDFC0C2a737" },
{ "name": "StaderStakePoolsManager", "address": "0xcf5EA1b38380f6aF39068375516Daf40Ed70D299" },
{ "name": "UserWithdrawalManager", "address": "0x9F0491B32DBce587c50c4C43AB303b06478193A7" },
{ "name": "SDUtilityPool", "address": "0xED6EE5049f643289ad52411E9aDeC698D04a9602" },
{ "name": "SDIncentiveController", "address": "0xe225825bcf20F39E2F2e2170412a3247D83492D0" }
],
"safeAddress": "0x45B977CeCB9Dfaa17dFcBa88826ef684b8489fF6"
},
"arbitrum": {
Expand Down
11 changes: 8 additions & 3 deletions scripts/safe-scripts/helpers/upgrade.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import hre from "hardhat";
import proposeTransaction from "./proposeTransaction";

async function main(contractAddress: string, contractName: string) {
const { ethers, upgrades } = hre;
const network = await ethers.provider.getNetwork();
Expand All @@ -27,12 +25,19 @@ async function main(contractAddress: string, contractName: string) {
contractAddress,
newImplementationAddress,
]);
await proposeTransaction(await proxyAdminContract.getAddress(), "0", encodedFunctionCall);

console.log(
`Upgrade transaction proposed for ${contractName} at ${contractAddress} to new implementation at ${newImplementationAddress}`,
);
console.log(`When finished run to verify:`);
console.log(`npx hardhat verify ${newImplementationAddress} --network ${network.name}`);
console.log("\n");


const to = await proxyAdminContract.getAddress();
const value ="0"
const data = encodedFunctionCall;

return{to, value, data};
}
export default main;
18 changes: 18 additions & 0 deletions scripts/safe-scripts/helpers/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
const { ethers, upgrades } = require("hardhat");
import { artifacts } from "hardhat";

export async function getDeployedBytecode(address: string, provider: any) {
const contractImpl = await upgrades.erc1967.getImplementationAddress(address);
const response = await provider.getCode(contractImpl);
return response;
}

export async function getArtifact(name: string) {
const artifact = await artifacts.readArtifact(name);
return artifact.deployedBytecode;
}

export async function forceImportDeployedProxies(contractAddress: string, contractName: string) {
const contractArtifact = await ethers.getContractFactory(contractName);
await upgrades.forceImport(contractAddress, contractArtifact, { kind: "transparent" });
}
4 changes: 3 additions & 1 deletion scripts/safe-scripts/upgrade.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import hre from "hardhat";
import upgrade from "./helpers/upgrade";
import proposeTransaction from "./helpers/proposeTransaction";

import addressJson from "./address.json";

Expand All @@ -21,7 +22,8 @@ async function main() {
if (contract === undefined) {
throw new Error(`Contract ${contractName} not found`);
}
await upgrade(contract?.address, contractName);
const {to, value, data} = await upgrade(contract?.address, contractName);
await proposeTransaction(to, data, value);
} catch (error) {
console.error("An error occurred:", error);
}
Expand Down
24 changes: 5 additions & 19 deletions scripts/safe-scripts/upgradeAll.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
const { ethers, upgrades } = require("hardhat");
const { ethers } = require("hardhat");
import upgradeHelper from "./helpers/upgrade";
import networkAddresses from "./address.json";
import { artifacts } from "hardhat";
import proposeTransaction from "./helpers/proposeTransaction";
import {getArtifact, getDeployedBytecode, forceImportDeployedProxies} from "./helpers/utils";

async function main(networks: { [networkName: string]: { contracts: { name: string; address: string }[] } }) {
const provider = ethers.provider;
Expand All @@ -26,7 +27,8 @@ async function main(networks: { [networkName: string]: { contracts: { name: stri
if (deployedBytecode !== compiledBytecode) {
console.warn(` Contract "${name}" is out of date!`);
console.log(` Upgrading to latest version...`);
await upgradeHelper(address, name);
const {to, value, data} = await upgradeHelper(address, name);
await proposeTransaction(to, data, value);
} else {
console.log(` "${name}" is already up to date on network "${networkName}".`);
}
Expand All @@ -36,22 +38,6 @@ async function main(networks: { [networkName: string]: { contracts: { name: stri
}
}

async function getDeployedBytecode(address: string, provider: any) {
const contractImpl = await upgrades.erc1967.getImplementationAddress(address);
const response = await provider.getCode(contractImpl);
return response;
}

async function getArtifact(name: string) {
const artifact = await artifacts.readArtifact(name);
return artifact.deployedBytecode;
}

async function forceImportDeployedProxies(contractAddress: string, contractName: string) {
const contractArtifact = await ethers.getContractFactory(contractName);
await upgrades.forceImport(contractAddress, contractArtifact, { kind: "transparent" });
}

main(networkAddresses)
.then(() => process.exit(0))
.catch((error) => {
Expand Down
68 changes: 68 additions & 0 deletions scripts/safe-scripts/upgradeAllBatch.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import upgradeHelper from "./helpers/upgrade";
import networkAddresses from "./address.json";
import proposeTransactions from "./helpers/proposeTransactions";
const { ethers } = require("hardhat");
import { MetaTransactionData } from "@safe-global/safe-core-sdk-types";
import { getArtifact, getDeployedBytecode, forceImportDeployedProxies } from "./helpers/utils";

async function main(networks: { [networkName: string]: { contracts: { name: string; address: string }[] } }) {
const provider = ethers.provider;
const networkName = await hre.network.name;
const networkContracts = networks[networkName].contracts;

console.log(`Checking contracts on network "${networkName}":`, "\n");
const targets = [];
const values = [];
const params = [];

for (let { name, address } of networkContracts) {
console.log(`Checking contract "${name}" at address ${address}`);

const deployedBytecode = await getDeployedBytecode(address, provider);
if (!deployedBytecode) {
console.error(`Failed to retrieve deployed bytecode for "${name}". Skipping.`);
continue;
}
try {
// Uncomment below line if network files are lost and need to be force import.
// await forceImportDeployedProxies(address, name);
const compiledBytecode = await getArtifact(name);

if (deployedBytecode !== compiledBytecode) {
console.warn(`Contract "${name}" is out of date!`);
console.log(`Upgrading to latest version...`);
const { to, value, data } = await upgradeHelper(address, name);
targets.push(to);
values.push(value);
params.push(data);
} else {
console.log(`"${name}" is already up to date on network "${networkName}".`);
}
} catch (error) {
console.error(`Error checking or upgrading "${name}" on network "${networkName}":`, error);
}
}
const multisigData = await buildMultiSigTx(targets, values, params);
await proposeTransactions(multisigData);
}

async function buildMultiSigTx(targets: string[], values: string[], params: string[]): Promise<MetaTransactionData[]> {
const safeTransactionData: MetaTransactionData[] = [];
for (let i = 0; i < targets.length; ++i) {
const safeTxData: MetaTransactionData = {
to: targets[i],
data: params[i],
value: values[i],
operation: 0,
};
safeTransactionData.push(safeTxData);
}
return safeTransactionData;
}

main(networkAddresses)
.then(() => process.exit(0))
.catch((error) => {
console.error(error);
process.exit(1);
});
Loading