feat: demonstrate state-preserving V2 upgrade

This commit is contained in:
golem
2026-08-21 15:31:40 -06:00
parent ca6a99b913
commit 94f2ad6e09
16 changed files with 1082 additions and 36 deletions
+18 -5
View File
@@ -18,12 +18,19 @@ const bankRequirements = [
eventSignature("Upgraded", [parameter("implementation", "address", true)]),
];
const tokenRequirements = [functionSignature("balanceOf", [parameter("account", "address")], ["uint256"], "view")];
const bankV2Requirements = [
...bankRequirements,
functionSignature("transferBalance", [parameter("recipient", "address"), parameter("amount", "uint256")], [], "nonpayable"),
eventSignature("BalanceTransferred", [parameter("from", "address", true), parameter("to", "address", true), parameter("amount", "uint256", false)]),
];
export function extractAbi(artifact, contractName) {
if (!artifact || typeof artifact !== "object" || !Array.isArray(artifact.abi)) {
throw new Error(`${contractName} artifact must contain an ABI`);
}
const requirements = contractName === "BankV1" ? bankRequirements : contractName === "MockUSDC" ? tokenRequirements : null;
const requirements = contractName === "BankV1" ? bankRequirements
: contractName === "BankV2" ? bankV2Requirements
: contractName === "MockUSDC" ? tokenRequirements : null;
if (!requirements) throw new Error(`unsupported contract artifact ${contractName}`);
for (const requirement of requirements) {
const entries = artifact.abi.filter((entry) => entry && entry.type === requirement.type && entry.name === requirement.name);
@@ -53,21 +60,27 @@ function matchesParameters(actual, expected) {
});
}
export function renderContractsModule(bankV1Abi, mockUsdcAbi) {
return `/* This file is generated by tools/sync-web-artifacts.mjs. Do not edit. */\n\nexport const bankV1Abi = ${JSON.stringify(bankV1Abi, null, 2)} as const;\n\nexport const mockUsdcAbi = ${JSON.stringify(mockUsdcAbi, null, 2)} as const;\n`;
export function renderContractsModule(bankV1Abi, bankV2Abi, mockUsdcAbi) {
return `/* This file is generated by tools/sync-web-artifacts.mjs. Do not edit. */\n\nexport const bankV1Abi = ${JSON.stringify(bankV1Abi, null, 2)} as const;\n\nexport const bankV2Abi = ${JSON.stringify(bankV2Abi, null, 2)} as const;\n\nexport const mockUsdcAbi = ${JSON.stringify(mockUsdcAbi, null, 2)} as const;\n`;
}
export async function syncArtifacts({
bankArtifactPath = resolve(repositoryRoot, "out/BankV1.sol/BankV1.json"),
bankV2ArtifactPath = resolve(repositoryRoot, "out/BankV2.sol/BankV2.json"),
tokenArtifactPath = resolve(repositoryRoot, "out/MockUSDC.sol/MockUSDC.json"),
outputPath = resolve(repositoryRoot, "web/src/generated/contracts.ts"),
check = false,
} = {}) {
const [bankArtifact, tokenArtifact] = await Promise.all([
const [bankArtifact, bankV2Artifact, tokenArtifact] = await Promise.all([
readArtifact(bankArtifactPath, "BankV1"),
readArtifact(bankV2ArtifactPath, "BankV2"),
readArtifact(tokenArtifactPath, "MockUSDC"),
]);
const contents = renderContractsModule(extractAbi(bankArtifact, "BankV1"), extractAbi(tokenArtifact, "MockUSDC"));
const contents = renderContractsModule(
extractAbi(bankArtifact, "BankV1"),
extractAbi(bankV2Artifact, "BankV2"),
extractAbi(tokenArtifact, "MockUSDC")
);
if (check) {
let existing;
try { existing = await readFile(outputPath, "utf8"); } catch { throw new Error("generated contracts module is stale or missing"); }