feat: demonstrate state-preserving V2 upgrade
This commit is contained in:
@@ -22,6 +22,11 @@ const bankAbi = [
|
||||
{ type: "event", name: "Upgraded", inputs: [{ name: "implementation", type: "address", indexed: true }], anonymous: false },
|
||||
];
|
||||
const tokenAbi = [{ type: "function", name: "balanceOf", inputs: [{ name: "account", type: "address" }], outputs: [{ name: "", type: "uint256" }], stateMutability: "view" }];
|
||||
const bankV2Abi = [
|
||||
...bankAbi,
|
||||
{ type: "function", name: "transferBalance", inputs: [{ name: "recipient", type: "address" }, { name: "amount", type: "uint256" }], outputs: [], stateMutability: "nonpayable" },
|
||||
{ type: "event", name: "BalanceTransferred", inputs: [{ name: "from", type: "address", indexed: true }, { name: "to", type: "address", indexed: true }, { name: "amount", type: "uint256", indexed: false }], anonymous: false },
|
||||
];
|
||||
const manifest = {
|
||||
schemaVersion: 1, network: "anvil", chainId: 31337, deploymentBlock: 3,
|
||||
rpcUrl: "http://127.0.0.1:8545", token: address, proxy: address,
|
||||
@@ -40,13 +45,18 @@ async function expectRejects(action, pattern) {
|
||||
|
||||
await withFixture(async (root) => {
|
||||
const bank = join(root, "BankV1.json");
|
||||
const bankV2 = join(root, "BankV2.json");
|
||||
const token = join(root, "MockUSDC.json");
|
||||
const output = join(root, "contracts.ts");
|
||||
|
||||
// Catches a production bridge that silently produces an ABI module from incomplete artifacts.
|
||||
await expectRejects(() => syncArtifacts({ bankArtifactPath: bank, tokenArtifactPath: token, outputPath: output }), /BankV1 artifact/i);
|
||||
await expectRejects(() => syncArtifacts({ bankArtifactPath: bank, bankV2ArtifactPath: bankV2, tokenArtifactPath: token, outputPath: output }), /BankV1 artifact/i);
|
||||
await writeFile(bank, JSON.stringify({ abi: bankAbi }));
|
||||
await expectRejects(() => syncArtifacts({ bankArtifactPath: bank, tokenArtifactPath: token, outputPath: output }), /MockUSDC artifact/i);
|
||||
await writeFile(token, JSON.stringify({ abi: tokenAbi }));
|
||||
await expectRejects(() => syncArtifacts({ bankArtifactPath: bank, bankV2ArtifactPath: bankV2, tokenArtifactPath: token, outputPath: output }), /BankV2 artifact/i);
|
||||
await writeFile(bankV2, JSON.stringify({ abi: bankV2Abi }));
|
||||
await rm(token);
|
||||
await expectRejects(() => syncArtifacts({ bankArtifactPath: bank, bankV2ArtifactPath: bankV2, tokenArtifactPath: token, outputPath: output }), /MockUSDC artifact/i);
|
||||
await writeFile(token, JSON.stringify({ abi: tokenAbi }));
|
||||
|
||||
// Catches a bridge that exports an ABI missing a V1 contract function or event.
|
||||
@@ -58,16 +68,20 @@ await withFixture(async (root) => {
|
||||
const wrongEvent = structuredClone(bankAbi);
|
||||
wrongEvent.find((entry) => entry.name === "Deposited").inputs[0].indexed = false;
|
||||
assert.throws(() => extractAbi({ abi: wrongEvent }, "BankV1"), /signature.*Deposited/i);
|
||||
const rendered = renderContractsModule(extractAbi({ abi: bankAbi }, "BankV1"), extractAbi({ abi: tokenAbi }, "MockUSDC"));
|
||||
assert.throws(() => extractAbi({ abi: bankV2Abi.filter((entry) => entry.name !== "transferBalance") }, "BankV2"), /transferBalance/);
|
||||
assert.throws(() => extractAbi({ abi: bankV2Abi.filter((entry) => entry.name !== "BalanceTransferred") }, "BankV2"), /BalanceTransferred/);
|
||||
const rendered = renderContractsModule(extractAbi({ abi: bankAbi }, "BankV1"), extractAbi({ abi: bankV2Abi }, "BankV2"), extractAbi({ abi: tokenAbi }, "MockUSDC"));
|
||||
assert.match(rendered, /export const bankV1Abi = .* as const;/s);
|
||||
assert.match(rendered, /export const bankV2Abi = .* as const;/s);
|
||||
assert.match(rendered, /export const mockUsdcAbi = .* as const;/s);
|
||||
assert.doesNotMatch(rendered, /bankV2Abi|BankV2/);
|
||||
assert.match(rendered, /transferBalance/);
|
||||
assert.match(rendered, /BalanceTransferred/);
|
||||
|
||||
// Catches a bridge that requires an active manifest or reads V2 as part of ABI generation.
|
||||
await syncArtifacts({ bankArtifactPath: bank, tokenArtifactPath: token, outputPath: output });
|
||||
await syncArtifacts({ bankArtifactPath: bank, bankV2ArtifactPath: bankV2, tokenArtifactPath: token, outputPath: output });
|
||||
assert.equal(await readFile(output, "utf8"), rendered);
|
||||
await writeFile(output, "stale\n");
|
||||
await expectRejects(() => syncArtifacts({ bankArtifactPath: bank, tokenArtifactPath: token, outputPath: output, check: true }), /stale/i);
|
||||
await expectRejects(() => syncArtifacts({ bankArtifactPath: bank, bankV2ArtifactPath: bankV2, tokenArtifactPath: token, outputPath: output, check: true }), /stale/i);
|
||||
});
|
||||
|
||||
await withFixture(async (root) => {
|
||||
|
||||
Reference in New Issue
Block a user