fix: validate bridged ABI and malformed logs

This commit is contained in:
golem
2026-08-21 03:19:22 -06:00
parent ba393bbbc3
commit 9ce8843279
4 changed files with 79 additions and 18 deletions
+38 -10
View File
@@ -3,11 +3,21 @@ import { dirname, resolve } from "node:path";
import { fileURLToPath, pathToFileURL } from "node:url";
const repositoryRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..");
const bankRequirements = {
functions: ["contractVersion", "paused", "asset", "owner", "totalLiabilities", "balanceOf"],
events: ["Deposited", "Withdrawn", "Paused", "Unpaused", "OwnershipTransferred", "Upgraded"],
};
const tokenRequirements = { functions: ["balanceOf"], events: [] };
const bankRequirements = [
functionSignature("contractVersion", [], ["uint256"], "pure"),
functionSignature("paused", [], ["bool"], "view"),
functionSignature("asset", [], ["address"], "view"),
functionSignature("owner", [], ["address"], "view"),
functionSignature("totalLiabilities", [], ["uint256"], "view"),
functionSignature("balanceOf", [parameter("account", "address")], ["uint256"], "view"),
eventSignature("Deposited", [parameter("account", "address", true), parameter("amount", "uint256", false)]),
eventSignature("Withdrawn", [parameter("account", "address", true), parameter("amount", "uint256", false)]),
eventSignature("Paused", [parameter("account", "address", false)]),
eventSignature("Unpaused", [parameter("account", "address", false)]),
eventSignature("OwnershipTransferred", [parameter("previousOwner", "address", true), parameter("newOwner", "address", true)]),
eventSignature("Upgraded", [parameter("implementation", "address", true)]),
];
const tokenRequirements = [functionSignature("balanceOf", [parameter("account", "address")], ["uint256"], "view")];
export function extractAbi(artifact, contractName) {
if (!artifact || typeof artifact !== "object" || !Array.isArray(artifact.abi)) {
@@ -15,16 +25,34 @@ export function extractAbi(artifact, contractName) {
}
const requirements = contractName === "BankV1" ? bankRequirements : contractName === "MockUSDC" ? tokenRequirements : null;
if (!requirements) throw new Error(`unsupported contract artifact ${contractName}`);
for (const [type, names] of Object.entries(requirements)) {
for (const name of names) {
if (!artifact.abi.some((entry) => entry && entry.type === type.slice(0, -1) && entry.name === name)) {
throw new Error(`${contractName} ABI is missing required ${type.slice(0, -1)} ${name}`);
}
for (const requirement of requirements) {
const entries = artifact.abi.filter((entry) => entry && entry.type === requirement.type && entry.name === requirement.name);
if (entries.length === 0) throw new Error(`${contractName} ABI is missing required ${requirement.type} ${requirement.name}`);
if (!entries.some((entry) => matchesSignature(entry, requirement))) {
throw new Error(`${contractName} ABI has an invalid signature for ${requirement.name}`);
}
}
return artifact.abi;
}
function parameter(name, type, indexed) { return { name, type, ...(indexed === undefined ? {} : { indexed }) }; }
function functionSignature(name, inputs, outputs, stateMutability) {
return { type: "function", name, inputs, outputs: outputs.map((type) => parameter("", type)), stateMutability };
}
function eventSignature(name, inputs) { return { type: "event", name, inputs, anonymous: false }; }
function matchesSignature(entry, requirement) {
return entry.stateMutability === requirement.stateMutability
&& entry.anonymous === requirement.anonymous
&& matchesParameters(entry.inputs, requirement.inputs)
&& (requirement.outputs === undefined || matchesParameters(entry.outputs, requirement.outputs));
}
function matchesParameters(actual, expected) {
return Array.isArray(actual) && actual.length === expected.length && actual.every((parameter, index) => {
const required = expected[index];
return parameter && parameter.name === required.name && parameter.type === required.type && parameter.indexed === required.indexed;
});
}
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`;
}