Skip to content

Commit a433d2f

Browse files
committed
feat: script to compare the storage layout against an already deployed contract
1 parent 63dc875 commit a433d2f

File tree

2 files changed

+81
-0
lines changed

2 files changed

+81
-0
lines changed

contracts/hardhat.config.ts

+7
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,24 @@ require("./scripts/populatePolicyRegistry");
1717
require("./scripts/populateCourts");
1818
require("./scripts/changeGovernor");
1919
require("./scripts/getDisputeTemplate");
20+
require("./scripts/compareStorageLayout");
2021

2122
dotenv.config();
2223

2324
const config: HardhatUserConfig = {
2425
solidity: {
2526
version: "0.8.24",
2627
settings: {
28+
// viaIR: true,
2729
optimizer: {
2830
enabled: true,
2931
runs: 100,
3032
},
33+
outputSelection: {
34+
"*": {
35+
"*": ["storageLayout"],
36+
},
37+
},
3138
},
3239
},
3340
paths: {
+74
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import { task } from "hardhat/config";
2+
import { readFileSync } from "fs";
3+
import path from "path";
4+
import { print } from "gluegun";
5+
import { HardhatRuntimeEnvironment } from "hardhat/types";
6+
import { getStorageUpgradeReport, ValidationOptions } from "@openzeppelin/upgrades-core";
7+
8+
const { bold } = print.colors;
9+
10+
task("compare-storage", "Compare storage layout between deployed and modified contracts")
11+
.addParam("contract", "The name of the contract to compare against")
12+
.addOptionalParam("deployedArtifact", "The name of the deployed contract artifact in deployments")
13+
.setAction(async ({ deployedArtifact, contract }, hre: HardhatRuntimeEnvironment) => {
14+
try {
15+
// Get storage layout of the deployed contract implementation
16+
const artifactName = deployedArtifact || `${contract}_Implementation`;
17+
const deployedArtifactPath = path.resolve(`deployments/${hre.network.name}/${artifactName}.json`);
18+
const deployedJson = JSON.parse(readFileSync(deployedArtifactPath, "utf8"));
19+
const originalLayout = deployedJson.storageLayout;
20+
21+
// Get storage layout of the modified contract
22+
const buildInfos = await hre.artifacts.getBuildInfoPaths();
23+
const buildInfo = buildInfos.find((buildInfo) => {
24+
const content = JSON.parse(readFileSync(buildInfo, "utf8"));
25+
return Object.keys(content.output.contracts).some((file) =>
26+
Object.keys(content.output.contracts[file]).includes(contract)
27+
);
28+
});
29+
30+
if (!buildInfo) {
31+
throw new Error(`Could not find build info for contract ${contract}`);
32+
}
33+
34+
const buildInfoContent = JSON.parse(readFileSync(buildInfo, "utf8"));
35+
const contractFile = Object.keys(buildInfoContent.output.contracts).find((file) =>
36+
Object.keys(buildInfoContent.output.contracts[file]).includes(contract)
37+
);
38+
39+
if (!contractFile) {
40+
throw new Error(`Could not find contract ${contract} in build info`);
41+
}
42+
43+
const contractData = buildInfoContent.output.contracts[contractFile][contract];
44+
const updatedLayout = contractData.storageLayout;
45+
46+
// Compare layouts using OpenZeppelin's report
47+
const validationOptions: Required<ValidationOptions> = {
48+
unsafeAllowCustomTypes: true,
49+
unsafeAllowRenames: false,
50+
unsafeAllow: [],
51+
unsafeSkipStorageCheck: false,
52+
unsafeAllowLinkedLibraries: false,
53+
kind: "uups",
54+
};
55+
56+
const report = getStorageUpgradeReport(originalLayout, updatedLayout, validationOptions);
57+
58+
print.info(`\n${bold("Storage Layout Analysis:")}`);
59+
print.info("=".repeat(100));
60+
61+
if (report.ok) {
62+
print.success("✓ No storage layout incompatibilities found");
63+
return;
64+
}
65+
66+
const explanation = report.explain();
67+
print.error(`✗ ${explanation}`);
68+
print.info("-".repeat(100));
69+
} catch (error) {
70+
print.error("\nError comparing storage layouts:");
71+
print.error(error);
72+
process.exit(1);
73+
}
74+
});

0 commit comments

Comments
 (0)