Skip to content

Commit

Permalink
feat: add forge for project bootstrapping (#135)
Browse files Browse the repository at this point in the history
  • Loading branch information
dutterbutter authored May 14, 2024
1 parent e4eef4c commit dba8f0e
Show file tree
Hide file tree
Showing 2 changed files with 146 additions and 31 deletions.
94 changes: 78 additions & 16 deletions src/commands/create/groups/quickstart.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@ import { packageManagers } from "../../../utils/packageManager.js";
import { isPrivateKey } from "../../../utils/validators.js";
import { askForTemplate, setupTemplate, askForPackageManager, successfulMessage, getUniqueValues } from "../utils.js";

import type { PackageManagerType } from "../../../utils/packageManager.js";
import type { GenericTemplate } from "../index.js";

type Template = GenericTemplate & {
framework: "Hardhat";
ethereumFramework: "Ethers v5" | "Ethers v6";
export type Template = GenericTemplate & {
framework: "Hardhat" | "Foundry";
ethereumFramework: "Ethers v5" | "Ethers v6" | "Solidity";
language: "Solidity" | "Vyper";
};

Expand Down Expand Up @@ -60,8 +61,69 @@ export const templates: Template[] = [
path: "templates/quickstart/hardhat/paymaster",
git: "https://github.com/matter-labs/zksync-contract-templates/",
},
{
name: "Quickstart - Foundry",
value: "qs-fs-hello-zksync",
framework: "Foundry",
ethereumFramework: "Solidity",
language: "Solidity",
path: "templates/quickstart/foundry/hello-zksync",
git: "https://github.com/matter-labs/zksync-contract-templates/",
},
{
name: "Quickstart - Foundry",
value: "qs-fs-factories",
framework: "Foundry",
ethereumFramework: "Solidity",
language: "Solidity",
path: "templates/quickstart/foundry/factory",
git: "https://github.com/matter-labs/zksync-contract-templates/",
},
{
name: "Quickstart - Foundry",
value: "qs-fs-testing",
framework: "Foundry",
ethereumFramework: "Solidity",
language: "Solidity",
path: "templates/quickstart/foundry/testing",
git: "https://github.com/matter-labs/zksync-contract-templates/",
},
];

const logFoundryInfo = () => {
const contractsDir = "/src";
const deploymentScriptsDir = "/script";
const tipMessage =
"- Tip: You can use the " + chalk.blueBright("--rpc-url") + " option to specify the network to deploy to.";
const deployCommand = `- Deploy your contract: ${chalk.blueBright("forge script [OPTIONS] <PATH> [ARGS] --zksync")}`;
const directoryOverview = `${chalk.magentaBright("Directory Overview:")}
- Contracts: ${contractsDir}
- Deployment Scripts: ${deploymentScriptsDir}`;
const commandsOverview = `${chalk.magentaBright("Commands:")}
- Compile your contracts: ${chalk.blueBright("forge build --zksync")}
${deployCommand}
${tipMessage}`;

Logger.info(`${directoryOverview}\n\n${commandsOverview}`);
};

const logHardhatInfo = (packageManager: PackageManagerType) => {
const contractsDir = "/contracts";
const deploymentScriptsDir = "/deploy";
const tipMessage =
"- Tip: You can use the " + chalk.blueBright("--network") + " option to specify the network to deploy to.";
const deployCommand = `- Deploy your contract: ${chalk.blueBright(packageManagers[packageManager].run("deploy"))}`;
const directoryOverview = `${chalk.magentaBright("Directory Overview:")}
- Contracts: ${contractsDir}
- Deployment Scripts: ${deploymentScriptsDir}`;
const commandsOverview = `${chalk.magentaBright("Commands:")}
- Compile your contracts: ${chalk.blueBright(packageManagers[packageManager].run("compile"))}
${deployCommand}
${tipMessage}`;

Logger.info(`${directoryOverview}\n\n${commandsOverview}`);
};

export default async (folderLocation: string, folderRelativePath: string, templateKey?: string) => {
let env: Record<string, string> = {};
let template: Template;
Expand Down Expand Up @@ -99,17 +161,17 @@ export default async (folderLocation: string, folderRelativePath: string, templa
...env,
WALLET_PRIVATE_KEY: privateKey,
};
const packageManager = await askForPackageManager();
await setupTemplate(template, folderLocation, env, packageManager);

successfulMessage.start(folderRelativePath);
Logger.info(`${chalk.magentaBright("Directory Overview:")}
- Contracts: /contracts
- Deployment Scripts: /deploy
${chalk.magentaBright("Commands:")}
- Compile your contracts: ${chalk.blueBright(packageManagers[packageManager].run("compile"))}
- Deploy your contract: ${chalk.blueBright(packageManagers[packageManager].run("deploy"))}
- Tip: You can use the ${chalk.blueBright("--network")} option to specify the network to deploy to.`);
successfulMessage.end(folderRelativePath);
let packageManager: PackageManagerType | undefined;
if (template.framework === "Foundry") {
await setupTemplate(template, folderLocation, env);
successfulMessage.start(folderRelativePath);
logFoundryInfo();
successfulMessage.end(folderRelativePath);
} else {
packageManager = await askForPackageManager();
await setupTemplate(template, folderLocation, env, packageManager);
successfulMessage.start(folderRelativePath);
logHardhatInfo(packageManager);
successfulMessage.end(folderRelativePath);
}
};
83 changes: 68 additions & 15 deletions src/commands/create/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { executeCommand } from "../../utils/helpers.js";
import Logger from "../../utils/logger.js";
import { packageManagers } from "../../utils/packageManager.js";

import type { Template } from "./groups/quickstart.js";
import type { GenericTemplate } from "./index.js";
import type { PackageManagerType } from "../../utils/packageManager.js";

Expand Down Expand Up @@ -96,9 +97,61 @@ export const setupTemplate = async (
template: GenericTemplate,
folderLocation: string,
env: Record<string, string>,
packageManager: PackageManagerType
packageManager?: PackageManagerType
) => {
Logger.info(`\nSetting up template in ${chalk.magentaBright(folderLocation)}...`);
const typedTemplate = template as Template;
if (typedTemplate.framework === "Foundry") {
await setupFoundryProject(template, folderLocation);
return;
}

await setupHardhatProject(template, folderLocation);

if (Object.keys(env).length > 0) {
await setupEnvironmentVariables(folderLocation, env);
}
if (packageManager) {
await installDependencies(packageManager, folderLocation);
} else {
throw new Error("Package manager is required to install dependencies.");
}
};
// Sets up a foundry project by initializing it with the specified template.
// Primarily only used for foundry quickstart templates.
const setupFoundryProject = async (template: GenericTemplate, folderLocation: string) => {
const spinner = ora("Initializing foundry project...").start();

try {
const isInstalled = await executeCommand("forge --version", { silent: true });
// TODO: https://github.com/matter-labs/zksync-cli/issues/137
if (!isInstalled) {
spinner.fail(
"Forge is not installed from the `foundry-zksync` repository. Please visit the official installation guide at https://github.com/matter-labs/foundry-zksync and follow the instructions to install it. Once installed, try running the command again."
);
return;
}

const cloneTempPath = path.join(folderLocation, "___temp");
await executeCommand(`forge init --template ${template.git} ${cloneTempPath}`, { silent: true });

const templatePath = path.join(cloneTempPath, template.path || "");
if (!fileOrDirExists(templatePath)) {
throw new Error(`The specified template path does not exist: ${templatePath}`);
}

copyRecursiveSync(templatePath, folderLocation);
fs.rmSync(cloneTempPath, { recursive: true, force: true });

spinner.succeed("Foundry project initialized successfully.");
} catch (error) {
spinner.fail("Failed to initialize foundry project");
throw error;
}
};

// Sets up a Hardhat project by cloning the specified template and copying it to the specified folder location.
const setupHardhatProject = async (template: GenericTemplate, folderLocation: string) => {
if (!template.path) {
const spinner = ora("Cloning template...").start();
try {
Expand Down Expand Up @@ -128,14 +181,11 @@ export const setupTemplate = async (
const cloneTempPath = path.join(folderLocation, "___temp");
const spinner = ora("Cloning template...").start();
try {
await cloneRepo(template.git, path.join(folderLocation, "___temp"), { silent: true });

await cloneRepo(template.git, cloneTempPath, { silent: true });
const templatePath = path.join(cloneTempPath, template.path);
if (fileOrDirExists(templatePath)) {
try {
// Copy the template to the folder location
copyRecursiveSync(templatePath, folderLocation);
// Remove the temp folder after copying
fs.rmSync(cloneTempPath, { recursive: true, force: true });
} catch (err) {
throw new Error("An error occurred while copying the template");
Expand All @@ -149,17 +199,20 @@ export const setupTemplate = async (
throw error;
}
}
if (Object.keys(env).length > 0) {
const spinner = ora("Setting up environment variables...").start();
try {
setupEnv(folderLocation, env);
} catch (error) {
spinner.fail("Failed to set up environment variables");
throw error;
}
spinner.succeed("Environment variables set up");
};
// Sets up environment variables in the specified folder location.
const setupEnvironmentVariables = async (folderLocation: string, env: Record<string, string>) => {
const spinner = ora("Setting up environment variables...").start();
try {
setupEnv(folderLocation, env);
} catch (error) {
spinner.fail("Failed to set up environment variables");
throw error;
}

spinner.succeed("Environment variables set up");
};
// Install dependencies with the specified package manager in the specified folder location.
const installDependencies = async (packageManager: PackageManagerType, folderLocation: string) => {
const spinner = ora(
`Installing dependencies with ${chalk.bold(packageManager)}... This may take a couple minutes.`
).start();
Expand Down

0 comments on commit dba8f0e

Please sign in to comment.