137 lines
4.9 KiB
TypeScript
137 lines
4.9 KiB
TypeScript
/**
|
|
* DON'T MODIFY OR DELETE THIS SCRIPT (unless you know what you're doing)
|
|
*
|
|
* This script generates the file containing the contracts Abi definitions.
|
|
* These definitions are used to derive the types needed in the custom scaffold-eth hooks, for example.
|
|
* This script should run as the last deploy script.
|
|
*/
|
|
|
|
import * as fs from "fs";
|
|
import prettier from "prettier";
|
|
import { DeployFunction } from "hardhat-deploy/types";
|
|
|
|
const generatedContractComment = `
|
|
/**
|
|
* This file is autogenerated by Scaffold-ETH.
|
|
* You should not edit it manually or your changes might be overwritten.
|
|
*/
|
|
`;
|
|
|
|
const DEPLOYMENTS_DIR = "./deployments";
|
|
const ARTIFACTS_DIR = "./artifacts";
|
|
|
|
function getDirectories(path: string) {
|
|
return fs
|
|
.readdirSync(path, { withFileTypes: true })
|
|
.filter(dirent => dirent.isDirectory())
|
|
.map(dirent => dirent.name);
|
|
}
|
|
|
|
function getContractNames(path: string) {
|
|
return fs
|
|
.readdirSync(path, { withFileTypes: true })
|
|
.filter(dirent => dirent.isFile() && dirent.name.endsWith(".json"))
|
|
.map(dirent => dirent.name.split(".")[0]);
|
|
}
|
|
|
|
function getActualSourcesForContract(sources: Record<string, any>, contractName: string) {
|
|
for (const sourcePath of Object.keys(sources)) {
|
|
const sourceName = sourcePath.split("/").pop()?.split(".sol")[0];
|
|
if (sourceName === contractName) {
|
|
const contractContent = sources[sourcePath].content as string;
|
|
const regex = /contract\s+(\w+)\s+is\s+([^{}]+)\{/;
|
|
const match = contractContent.match(regex);
|
|
|
|
if (match) {
|
|
const inheritancePart = match[2];
|
|
// Split the inherited contracts by commas to get the list of inherited contracts
|
|
const inheritedContracts = inheritancePart.split(",").map(contract => `${contract.trim()}.sol`);
|
|
|
|
return inheritedContracts;
|
|
}
|
|
return [];
|
|
}
|
|
}
|
|
return [];
|
|
}
|
|
|
|
function getInheritedFunctions(sources: Record<string, any>, contractName: string) {
|
|
const actualSources = getActualSourcesForContract(sources, contractName);
|
|
const inheritedFunctions = {} as Record<string, any>;
|
|
|
|
for (const sourceContractName of actualSources) {
|
|
const sourcePath = Object.keys(sources).find(key => key.includes(`/${sourceContractName}`));
|
|
if (sourcePath) {
|
|
const sourceName = sourcePath?.split("/").pop()?.split(".sol")[0];
|
|
const { abi } = JSON.parse(fs.readFileSync(`${ARTIFACTS_DIR}/${sourcePath}/${sourceName}.json`).toString());
|
|
for (const functionAbi of abi) {
|
|
if (functionAbi.type === "function") {
|
|
inheritedFunctions[functionAbi.name] = sourcePath;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return inheritedFunctions;
|
|
}
|
|
|
|
function getContractDataFromDeployments() {
|
|
if (!fs.existsSync(DEPLOYMENTS_DIR)) {
|
|
throw Error("At least one other deployment script should exist to generate an actual contract.");
|
|
}
|
|
const output = {} as Record<string, any>;
|
|
const chainDirectories = getDirectories(DEPLOYMENTS_DIR);
|
|
for (const chainName of chainDirectories) {
|
|
let chainId;
|
|
try {
|
|
chainId = fs.readFileSync(`${DEPLOYMENTS_DIR}/${chainName}/.chainId`).toString();
|
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
} catch (error) {
|
|
console.log(`No chainId file found for ${chainName}`);
|
|
continue;
|
|
}
|
|
|
|
const contracts = {} as Record<string, any>;
|
|
for (const contractName of getContractNames(`${DEPLOYMENTS_DIR}/${chainName}`)) {
|
|
const { abi, address, metadata, receipt } = JSON.parse(
|
|
fs.readFileSync(`${DEPLOYMENTS_DIR}/${chainName}/${contractName}.json`).toString(),
|
|
);
|
|
const inheritedFunctions = metadata ? getInheritedFunctions(JSON.parse(metadata).sources, contractName) : {};
|
|
contracts[contractName] = { address, abi, inheritedFunctions, deployedOnBlock: receipt?.blockNumber };
|
|
}
|
|
output[chainId] = contracts;
|
|
}
|
|
return output;
|
|
}
|
|
|
|
/**
|
|
* Generates the TypeScript contract definition file based on the json output of the contract deployment scripts
|
|
* This script should be run last.
|
|
*/
|
|
const generateTsAbis: DeployFunction = async function () {
|
|
const TARGET_DIR = "../nextjs/contracts/";
|
|
const allContractsData = getContractDataFromDeployments();
|
|
|
|
const fileContent = Object.entries(allContractsData).reduce((content, [chainId, chainConfig]) => {
|
|
return `${content}${parseInt(chainId).toFixed(0)}:${JSON.stringify(chainConfig, null, 2)},`;
|
|
}, "");
|
|
|
|
if (!fs.existsSync(TARGET_DIR)) {
|
|
fs.mkdirSync(TARGET_DIR);
|
|
}
|
|
fs.writeFileSync(
|
|
`${TARGET_DIR}deployedContracts.ts`,
|
|
await prettier.format(
|
|
`${generatedContractComment} import { GenericContractsDeclaration } from "~~/utils/scaffold-eth/contract"; \n\n
|
|
const deployedContracts = {${fileContent}} as const; \n\n export default deployedContracts satisfies GenericContractsDeclaration`,
|
|
{
|
|
parser: "typescript",
|
|
},
|
|
),
|
|
);
|
|
|
|
console.log(`📝 Updated TypeScript contract definition file on ${TARGET_DIR}deployedContracts.ts`);
|
|
};
|
|
|
|
export default generateTsAbis;
|