Skip to content

Commit

Permalink
add support for multiple solc versions (as is required to support opt…
Browse files Browse the repository at this point in the history
…imism contracts deployments)
  • Loading branch information
RnkSngh committed Oct 28, 2024
1 parent 0b3d480 commit af47770
Show file tree
Hide file tree
Showing 7 changed files with 78 additions and 34 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@open-ibc/vibc-core-smart-contracts",
"version": "4.0.5",
"version": "4.0.6",
"main": "dist/index.js",
"bin": {
"verify-vibc-core-smart-contracts": "./dist/scripts/verify-contract-script.js",
Expand Down
30 changes: 24 additions & 6 deletions src/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,21 @@ export async function updateNoncesForSender(
return nonces;
}

// Converts a factory to a name. If a solc version is specified (which needs to happen for multiple solc versions in a foundry/hardhat project, then it will return the file with the constructed name)
export const getFactoryFileName = (
factoryName: string,
solcVersion: string | undefined
) => {
if (!solcVersion) return `${factoryName}__factory`;

// Filter version string to remove periods e.g. 0.8.15 -> 0815
const versionStr = solcVersion
.split("")
.filter((c) => c !== "." && c !== "v")
.join("");
return `${factoryName}${versionStr}__factory`;
};

/**
* Return deployment libraries, factory, factory constructor,
* and rendered arguments for a contract deployment
Expand All @@ -48,11 +63,12 @@ const getDeployData = (
env: StringToStringMap,
libraries: any[] = [],
init: { args: any[]; signature: string } | undefined,
contractFactories: Record<string, any>
contractFactories: Record<string, any>,
solcVersion: string | undefined
) => {
const contractFactoryFileName = getFactoryFileName(factoryName, solcVersion);
// @ts-ignore
const contractFactoryConstructor =
contractFactories[`${factoryName}__factory`];
const contractFactoryConstructor = contractFactories[contractFactoryFileName];
assert(
contractFactoryConstructor,
`cannot find contract factory constructor for contract: ${factoryName}`
Expand All @@ -67,7 +83,7 @@ const getDeployData = (
const factory = new contractFactoryConstructor(...libs);
if (!factory) {
throw new Error(
`cannot load contract factory for contract: ${factoryName} with factory name: ${factoryName}__factory`
`cannot load contract factory for contract: ${factoryName} from factory file: ${contractFactoryFileName}`
);
}

Expand Down Expand Up @@ -107,15 +123,16 @@ export const deployContract = async (
env,
contract.libraries,
contract.init,
contractFactories
contractFactories,
contract.solcVersion
);

logger.info(
`[${chain.chainName}-${chain.deploymentEnvironment}]: deploying ${
contract.name
} with args: [${constructorData.args}] with libraries: ${JSON.stringify(
constructorData.libraries
)}`
)} `
);
let deployedAddr = `new.${contract.name}.address`;
const deployer = accountRegistry.mustGet(
Expand Down Expand Up @@ -164,6 +181,7 @@ export const deployContract = async (
name: contract.name,
args: constructorData.args,
libraries: constructorData.libraries,
solcVersion: contract.solcVersion,
};
writeDeployedContractToFile(chain, contractObject);
}
Expand Down
1 change: 1 addition & 0 deletions src/evm/schemas/contract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export const ContractItemSchema = z
})
),
abi: z.optional(z.any()),
solcVersion: z.optional(z.string()),
})
.strict();

Expand Down
10 changes: 7 additions & 3 deletions src/scripts/update-contracts-script.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { UPDATE_SPECS_PATH } from "../utils/constants";
import { parseArgsFromCLI } from "../utils/io";

async function main() {
const { chain, accounts, args, extraBindingsPath, extraArtifactsPath } =
const { chain, accounts, args, extraBindingsPath, externalContractsPath } =
await parseArgsFromCLI();
const updateSpecs = (args.UPDATE_SPECS_PATH as string) || UPDATE_SPECS_PATH;

Expand All @@ -27,17 +27,21 @@ async function main() {
}
}

const existingContracts = externalContractsPath
? ContractRegistryLoader.loadSingle(externalContractsPath)
: ContractRegistryLoader.emptySingle();

updateContractsForChain(
chain,
accounts.mustGet(chain.chainName),
ContractRegistryLoader.emptySingle(),
existingContracts,
contractUpdates,
getOutputLogger(),
{
dryRun: false,
forceDeployNewContracts: false,
writeContracts: true,
extraContractFactories: extraContractFactories ?? {},
extraContractFactories: extraContractFactories ?? undefined,
}
);
}
Expand Down
12 changes: 6 additions & 6 deletions src/scripts/verify-contract-script.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,19 @@ import { $, cd } from "zx";
import { MODULE_ROOT_PATH } from "../utils/constants";
import { Logger } from "winston";
import { getMainLogger } from "../utils/cli";
import { ContractItemSchema } from "../evm/schemas/contract";
import { ContractItem, ContractItemSchema } from "../evm/schemas/contract";
import { loadContractUpdateRegistry } from "../evm/schemas/contractUpdate";

const verifyContract = async (
deploymentName: string,
deployedContract: ContractItem,
chainFolder: ChainFolder,
etherscanApiKey: string,
verifierUrl: string,
logger: Logger
) => {
// Read deployment file, so that we can find path to artifact
const deployment = await readFromDeploymentFile(deploymentName, chainFolder);
const metadata = await readMetadata(deployment.factory);
const deployment = await readFromDeploymentFile(deployedContract.name, chainFolder);
const metadata = await readMetadata(deployment.factory, deployedContract.solcVersion);
const compilationTarget = JSON.parse(metadata).settings.compilationTarget;
const contractFile = Object.keys(compilationTarget)[0];
const contractPath = `${contractFile}:${compilationTarget[contractFile]}`;
Expand All @@ -44,7 +44,7 @@ const verifyContract = async (
}

logger.info(
`verifying ${deploymentName}'s deployment with ${deployment.factory} ${
`verifying ${deployedContract.name}'s deployment with ${deployment.factory} ${
libraries ? `and libraries ${libraries}` : ``
}`
);
Expand Down Expand Up @@ -83,7 +83,7 @@ async function main() {
// Only try to verify contractName if it matches the deploymentName
try {
await verifyContract(
parsed.data.name,
parsed.data,
chainFolder,
etherscanApiKey,
verifierUrl,
Expand Down
2 changes: 2 additions & 0 deletions src/utils/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,5 @@ export const EXTRA_BINDINGS_PATH = process.env.EXTRA_BINDINGS_PATH;
// The path where we access external artifacts for already deployed contracts
export const EXTRA_ARTIFACTS_PATH = process.env.EXTRA_ARTIFACTS_PATH;

// Path where we can load an existing contract registry from
export const EXTERNAL_CONTRACTS_PATH= process.env.EXTERNAL_CONTRACTS_PATH;
55 changes: 37 additions & 18 deletions src/utils/io.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
UPDATE_SPECS_PATH,
EXTRA_BINDINGS_PATH,
EXTRA_ARTIFACTS_PATH,
EXTERNAL_CONTRACTS_PATH,
} from './constants';
import yargs from 'yargs/yargs';
import { hideBin } from 'yargs/helpers';
Expand Down Expand Up @@ -51,6 +52,7 @@ export type DeployedContractObject = {
metadata?: string;
name: string;
libraries: LibraryMetadata[];
solcVersion: string | undefined;
};

// readYamlFile reads a yaml file and returns the parsed object.
Expand Down Expand Up @@ -137,10 +139,10 @@ export function parseZodSchema<T>(
`parsing ${className} failed. ${zErr.issues
.map((i) => i.path)
.join(', ')}: ${zErr.message}\nconfig obj:\n${JSON.stringify(
config,
null,
2
)}`
config,
null,
2
)}`
);
} else {
throw e;
Expand Down Expand Up @@ -179,38 +181,49 @@ export function toEnvVarName(e: string) {
}

/** Reads a foundry build file given */
export async function readArtifactFile(artifactName: string) {
export async function readArtifactFile(
artifactName: string,
solcVersion: string | undefined
) {
const basePaths = [ARTIFACTS_PATH];
if (EXTRA_ARTIFACTS_PATH) {
basePaths.push(EXTRA_ARTIFACTS_PATH);
}

const paths = basePaths.map((basePath) =>
path.join(basePath, `${artifactName}.sol`, `${artifactName}.json`)
);
const paths = basePaths.map((basePath) => {
return path.join(
basePath,
`${artifactName}.sol`,
`${artifactName}${solcVersion ? `.${solcVersion}` : ''}.json`
);
});
for (const path of paths) {
try {
return await fsAsync.readFile(path, 'utf8');
} catch (e) {
console.error(`error reading from file in extra file ${path}: \n`, e);
console.log(`no file found in ${path}: \n`, e);
}
}

console.error(`error reading from file in extra file ${path}: \n`, );
return '';
}


/** Reads a deployment metadata rom a foundry build file. used to generate bytecode for deployment files*/
export async function readMetadata(factoryName: string) {
const data = await readArtifactFile(factoryName);
export async function readMetadata(
factoryName: string,
solcVersion: string | undefined
) {
const data = await readArtifactFile(factoryName, solcVersion);
return JSON.stringify(JSON.parse(data).metadata);
}


export async function readFactoryAbi(factoryName: string, contractFactories: Record<string, any>) {
export async function readFactoryAbi(
factoryName: string,
contractFactories: Record<string, any>
) {
const contractFactoryConstructor =
contractFactories[`${factoryName}__factory`]
contractFactories[`${factoryName}__factory`];

// const data = await readArtifactFile(factoryName);
return contractFactoryConstructor.abi;
}

Expand All @@ -237,7 +250,10 @@ export async function writeDeployedContractToFile(
ensureDir(deploymentFolder);

// get metadata from contract from forge build output
const metadata = await readMetadata(deployedContract.factory);
const metadata = await readMetadata(
deployedContract.factory,
deployedContract.solcVersion
);
const outData = JSON.stringify({
...deployedContract,
metadata,
Expand Down Expand Up @@ -381,6 +397,8 @@ export async function parseArgsFromCLI() {
(argv1.EXTRA_BINDINGS_PATH as string) || EXTRA_BINDINGS_PATH; // Any directory of extra typechain bindings.
const extraArtifactsPath =
(argv1.EXTRA_ARTIFACTS_PATH as string) || EXTRA_ARTIFACTS_PATH; // Any directory of extra foundry artifacts
const externalContractsPath =
(argv1.EXTERNAL_CONTRACTS_PATH as string) || EXTERNAL_CONTRACTS_PATH;

const chainParse = ChainConfigSchema.safeParse({
rpc: rpcUrl,
Expand Down Expand Up @@ -410,6 +428,7 @@ export async function parseArgsFromCLI() {
anvilPort,
extraBindingsPath,
extraArtifactsPath,
externalContractsPath,
};
}

Expand Down

0 comments on commit af47770

Please sign in to comment.