mirror of
https://github.com/selfxyz/self.git
synced 2026-01-13 00:28:17 -05:00
* refactor: remove namespace imports * refactor: use named fs imports * refactor(app): replace path and fs namespace imports * format * format
186 lines
5.3 KiB
TypeScript
186 lines
5.3 KiB
TypeScript
import { readdirSync, statSync, readFileSync, writeFileSync } from "fs";
|
|
import path from "path";
|
|
import { keccak256 } from "ethers";
|
|
|
|
interface CustomError {
|
|
name: string;
|
|
signature: string;
|
|
selector: string;
|
|
file: string;
|
|
line: number;
|
|
}
|
|
|
|
/**
|
|
* Recursively find all .sol files in a directory
|
|
*/
|
|
function findSolidityFiles(dir: string): string[] {
|
|
const files: string[] = [];
|
|
|
|
function traverse(currentDir: string) {
|
|
const items = readdirSync(currentDir);
|
|
|
|
for (const item of items) {
|
|
const fullPath = path.join(currentDir, item);
|
|
const stat = statSync(fullPath);
|
|
|
|
if (stat.isDirectory()) {
|
|
// Skip node_modules, .git, and other common directories
|
|
if (!["node_modules", ".git", "dist", "build", "cache"].includes(item)) {
|
|
traverse(fullPath);
|
|
}
|
|
} else if (item.endsWith(".sol")) {
|
|
files.push(fullPath);
|
|
}
|
|
}
|
|
}
|
|
|
|
traverse(dir);
|
|
return files;
|
|
}
|
|
|
|
/**
|
|
* Extract custom errors from Solidity file content
|
|
*/
|
|
function extractCustomErrors(filePath: string): CustomError[] {
|
|
const content = readFileSync(filePath, "utf8");
|
|
const lines = content.split("\n");
|
|
const errors: CustomError[] = [];
|
|
|
|
// Regex to match custom error declarations
|
|
// Matches: error ErrorName(type1 param1, type2 param2, ...);
|
|
const errorRegex = /^\s*error\s+([A-Za-z_][A-Za-z0-9_]*)\s*\(([^)]*)\)\s*;/;
|
|
|
|
for (let i = 0; i < lines.length; i++) {
|
|
const line = lines[i];
|
|
const match = line.match(errorRegex);
|
|
|
|
if (match) {
|
|
const errorName = match[1];
|
|
const params = match[2].trim();
|
|
|
|
// Clean up parameters - remove parameter names, keep only types
|
|
const paramTypes = params
|
|
.split(",")
|
|
.map((param) => param.trim())
|
|
.filter((param) => param.length > 0)
|
|
.map((param) => {
|
|
// Extract type from "type name" or "type"
|
|
const parts = param.split(/\s+/);
|
|
return parts[0]; // First part is the type
|
|
})
|
|
.join(",");
|
|
|
|
const signature = `${errorName}(${paramTypes})`;
|
|
const hash = keccak256(Buffer.from(signature, "utf8"));
|
|
const selector = hash.slice(0, 10); // First 4 bytes (8 hex chars + 0x)
|
|
|
|
errors.push({
|
|
name: errorName,
|
|
signature,
|
|
selector,
|
|
file: path.relative(process.cwd(), filePath),
|
|
line: i + 1,
|
|
});
|
|
}
|
|
}
|
|
|
|
return errors;
|
|
}
|
|
|
|
/**
|
|
* Main function to scan all contracts and find error selectors
|
|
*/
|
|
async function findAllErrorSelectors(targetSelector?: string) {
|
|
console.log("🔍 Scanning Solidity files for custom errors...\n");
|
|
|
|
const contractsDir = path.join(process.cwd(), "contracts");
|
|
const solidityFiles = findSolidityFiles(contractsDir);
|
|
|
|
console.log(`Found ${solidityFiles.length} Solidity files\n`);
|
|
|
|
const allErrors: CustomError[] = [];
|
|
let foundTarget = false;
|
|
|
|
for (const file of solidityFiles) {
|
|
const errors = extractCustomErrors(file);
|
|
allErrors.push(...errors);
|
|
|
|
// Check if we found the target selector
|
|
if (targetSelector) {
|
|
const match = errors.find((error) => error.selector.toLowerCase() === targetSelector.toLowerCase());
|
|
if (match) {
|
|
console.log(`🎯 FOUND TARGET ERROR: ${targetSelector}`);
|
|
console.log(` Error: ${match.name}`);
|
|
console.log(` Signature: ${match.signature}`);
|
|
console.log(` File: ${match.file}:${match.line}`);
|
|
console.log(` Selector: ${match.selector}\n`);
|
|
foundTarget = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Sort errors by selector for easy lookup
|
|
allErrors.sort((a, b) => a.selector.localeCompare(b.selector));
|
|
|
|
console.log(`📊 Found ${allErrors.length} custom errors total\n`);
|
|
|
|
if (targetSelector && !foundTarget) {
|
|
console.log(`❌ Target selector ${targetSelector} not found in custom errors`);
|
|
console.log(`💡 This might be a built-in error or from an imported contract\n`);
|
|
}
|
|
|
|
// Group errors by file for better organization
|
|
const errorsByFile = allErrors.reduce(
|
|
(acc, error) => {
|
|
if (!acc[error.file]) {
|
|
acc[error.file] = [];
|
|
}
|
|
acc[error.file].push(error);
|
|
return acc;
|
|
},
|
|
{} as Record<string, CustomError[]>,
|
|
);
|
|
|
|
console.log("📋 All Custom Errors by File:");
|
|
console.log("================================\n");
|
|
|
|
for (const [file, errors] of Object.entries(errorsByFile)) {
|
|
console.log(`📄 ${file}:`);
|
|
for (const error of errors) {
|
|
console.log(` ${error.selector} → ${error.signature} (line ${error.line})`);
|
|
}
|
|
console.log("");
|
|
}
|
|
|
|
// Generate a quick lookup table
|
|
console.log("🔗 Quick Selector Lookup:");
|
|
console.log("=========================\n");
|
|
|
|
for (const error of allErrors) {
|
|
console.log(`${error.selector} → ${error.name} (${error.file}:${error.line})`);
|
|
}
|
|
|
|
// Save results to JSON file for future reference
|
|
const outputFile = "error-selectors.json";
|
|
writeFileSync(outputFile, JSON.stringify(allErrors, null, 2));
|
|
console.log(`\n💾 Results saved to ${outputFile}`);
|
|
|
|
return allErrors;
|
|
}
|
|
|
|
// CLI interface
|
|
const targetSelector = process.argv[2];
|
|
|
|
if (targetSelector) {
|
|
console.log(`🎯 Looking for specific error selector: ${targetSelector}\n`);
|
|
}
|
|
|
|
findAllErrorSelectors(targetSelector)
|
|
.then(() => {
|
|
console.log("\n✅ Scan complete!");
|
|
})
|
|
.catch((error) => {
|
|
console.error("❌ Error during scan:", error);
|
|
process.exit(1);
|
|
});
|