Initial commit with 🏗️ Scaffold-ETH 2 @ 1.0.5
This commit is contained in:
58
packages/hardhat/scripts/generateAccount.ts
Normal file
58
packages/hardhat/scripts/generateAccount.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
import { ethers } from "ethers";
|
||||
import { parse, stringify } from "envfile";
|
||||
import * as fs from "fs";
|
||||
import password from "@inquirer/password";
|
||||
|
||||
const envFilePath = "./.env";
|
||||
|
||||
const getValidatedPassword = async () => {
|
||||
while (true) {
|
||||
const pass = await password({ message: "Enter a password to encrypt your private key:" });
|
||||
const confirmation = await password({ message: "Confirm password:" });
|
||||
|
||||
if (pass === confirmation) {
|
||||
return pass;
|
||||
}
|
||||
console.log("❌ Passwords don't match. Please try again.");
|
||||
}
|
||||
};
|
||||
|
||||
const setNewEnvConfig = async (existingEnvConfig = {}) => {
|
||||
console.log("👛 Generating new Wallet\n");
|
||||
const randomWallet = ethers.Wallet.createRandom();
|
||||
|
||||
const pass = await getValidatedPassword();
|
||||
const encryptedJson = await randomWallet.encrypt(pass);
|
||||
|
||||
const newEnvConfig = {
|
||||
...existingEnvConfig,
|
||||
DEPLOYER_PRIVATE_KEY_ENCRYPTED: encryptedJson,
|
||||
};
|
||||
|
||||
// Store in .env
|
||||
fs.writeFileSync(envFilePath, stringify(newEnvConfig));
|
||||
console.log("\n📄 Encrypted Private Key saved to packages/hardhat/.env file");
|
||||
console.log("🪄 Generated wallet address:", randomWallet.address, "\n");
|
||||
console.log("⚠️ Make sure to remember your password! You'll need it to decrypt the private key.");
|
||||
};
|
||||
|
||||
async function main() {
|
||||
if (!fs.existsSync(envFilePath)) {
|
||||
// No .env file yet.
|
||||
await setNewEnvConfig();
|
||||
return;
|
||||
}
|
||||
|
||||
const existingEnvConfig = parse(fs.readFileSync(envFilePath).toString());
|
||||
if (existingEnvConfig.DEPLOYER_PRIVATE_KEY_ENCRYPTED) {
|
||||
console.log("⚠️ You already have a deployer account. Check the packages/hardhat/.env file");
|
||||
return;
|
||||
}
|
||||
|
||||
await setNewEnvConfig(existingEnvConfig);
|
||||
}
|
||||
|
||||
main().catch(error => {
|
||||
console.error(error);
|
||||
process.exitCode = 1;
|
||||
});
|
||||
136
packages/hardhat/scripts/generateTsAbis.ts
Normal file
136
packages/hardhat/scripts/generateTsAbis.ts
Normal file
@@ -0,0 +1,136 @@
|
||||
/**
|
||||
* 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;
|
||||
72
packages/hardhat/scripts/importAccount.ts
Normal file
72
packages/hardhat/scripts/importAccount.ts
Normal file
@@ -0,0 +1,72 @@
|
||||
import { ethers } from "ethers";
|
||||
import { parse, stringify } from "envfile";
|
||||
import * as fs from "fs";
|
||||
import password from "@inquirer/password";
|
||||
|
||||
const envFilePath = "./.env";
|
||||
|
||||
const getValidatedPassword = async () => {
|
||||
while (true) {
|
||||
const pass = await password({ message: "Enter a password to encrypt your private key:" });
|
||||
const confirmation = await password({ message: "Confirm password:" });
|
||||
|
||||
if (pass === confirmation) {
|
||||
return pass;
|
||||
}
|
||||
console.log("❌ Passwords don't match. Please try again.");
|
||||
}
|
||||
};
|
||||
|
||||
const getWalletFromPrivateKey = async () => {
|
||||
while (true) {
|
||||
const privateKey = await password({ message: "Paste your private key:" });
|
||||
try {
|
||||
const wallet = new ethers.Wallet(privateKey);
|
||||
return wallet;
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
} catch (e) {
|
||||
console.log("❌ Invalid private key format. Please try again.");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const setNewEnvConfig = async (existingEnvConfig = {}) => {
|
||||
console.log("👛 Importing Wallet\n");
|
||||
|
||||
const wallet = await getWalletFromPrivateKey();
|
||||
|
||||
const pass = await getValidatedPassword();
|
||||
const encryptedJson = await wallet.encrypt(pass);
|
||||
|
||||
const newEnvConfig = {
|
||||
...existingEnvConfig,
|
||||
DEPLOYER_PRIVATE_KEY_ENCRYPTED: encryptedJson,
|
||||
};
|
||||
|
||||
// Store in .env
|
||||
fs.writeFileSync(envFilePath, stringify(newEnvConfig));
|
||||
console.log("\n📄 Encrypted Private Key saved to packages/hardhat/.env file");
|
||||
console.log("🪄 Imported wallet address:", wallet.address, "\n");
|
||||
console.log("⚠️ Make sure to remember your password! You'll need it to decrypt the private key.");
|
||||
};
|
||||
|
||||
async function main() {
|
||||
if (!fs.existsSync(envFilePath)) {
|
||||
// No .env file yet.
|
||||
await setNewEnvConfig();
|
||||
return;
|
||||
}
|
||||
|
||||
const existingEnvConfig = parse(fs.readFileSync(envFilePath).toString());
|
||||
if (existingEnvConfig.DEPLOYER_PRIVATE_KEY_ENCRYPTED) {
|
||||
console.log("⚠️ You already have a deployer account. Check the packages/hardhat/.env file");
|
||||
return;
|
||||
}
|
||||
|
||||
await setNewEnvConfig(existingEnvConfig);
|
||||
}
|
||||
|
||||
main().catch(error => {
|
||||
console.error(error);
|
||||
process.exitCode = 1;
|
||||
});
|
||||
52
packages/hardhat/scripts/listAccount.ts
Normal file
52
packages/hardhat/scripts/listAccount.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
import * as dotenv from "dotenv";
|
||||
dotenv.config();
|
||||
import { ethers, Wallet } from "ethers";
|
||||
import QRCode from "qrcode";
|
||||
import { config } from "hardhat";
|
||||
import password from "@inquirer/password";
|
||||
|
||||
async function main() {
|
||||
const encryptedKey = process.env.DEPLOYER_PRIVATE_KEY_ENCRYPTED;
|
||||
|
||||
if (!encryptedKey) {
|
||||
console.log("🚫️ You don't have a deployer account. Run `yarn generate` or `yarn account:import` first");
|
||||
return;
|
||||
}
|
||||
|
||||
const pass = await password({ message: "Enter your password to decrypt the private key:" });
|
||||
let wallet: Wallet;
|
||||
try {
|
||||
wallet = (await Wallet.fromEncryptedJson(encryptedKey, pass)) as Wallet;
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
} catch (e) {
|
||||
console.log("❌ Failed to decrypt private key. Wrong password?");
|
||||
return;
|
||||
}
|
||||
|
||||
const address = wallet.address;
|
||||
console.log(await QRCode.toString(address, { type: "terminal", small: true }));
|
||||
console.log("Public address:", address, "\n");
|
||||
|
||||
// Balance on each network
|
||||
const availableNetworks = config.networks;
|
||||
for (const networkName in availableNetworks) {
|
||||
try {
|
||||
const network = availableNetworks[networkName];
|
||||
if (!("url" in network)) continue;
|
||||
const provider = new ethers.JsonRpcProvider(network.url);
|
||||
await provider._detectNetwork();
|
||||
const balance = await provider.getBalance(address);
|
||||
console.log("--", networkName, "-- 📡");
|
||||
console.log(" balance:", +ethers.formatEther(balance));
|
||||
console.log(" nonce:", +(await provider.getTransactionCount(address)));
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
} catch (e) {
|
||||
console.log("Can't connect to network", networkName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
main().catch(error => {
|
||||
console.error(error);
|
||||
process.exitCode = 1;
|
||||
});
|
||||
31
packages/hardhat/scripts/revealPK.ts
Normal file
31
packages/hardhat/scripts/revealPK.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import * as dotenv from "dotenv";
|
||||
dotenv.config();
|
||||
import { Wallet } from "ethers";
|
||||
import password from "@inquirer/password";
|
||||
|
||||
async function main() {
|
||||
const encryptedKey = process.env.DEPLOYER_PRIVATE_KEY_ENCRYPTED;
|
||||
|
||||
if (!encryptedKey) {
|
||||
console.log("🚫️ You don't have a deployer account. Run `yarn generate` or `yarn account:import` first");
|
||||
return;
|
||||
}
|
||||
|
||||
console.log("👀 This will reveal your private key on the console.\n");
|
||||
|
||||
const pass = await password({ message: "Enter your password to decrypt the private key:" });
|
||||
let wallet: Wallet;
|
||||
try {
|
||||
wallet = (await Wallet.fromEncryptedJson(encryptedKey, pass)) as Wallet;
|
||||
} catch {
|
||||
console.log("❌ Failed to decrypt private key. Wrong password?");
|
||||
return;
|
||||
}
|
||||
|
||||
console.log("\n🔑 Private key:", wallet.privateKey);
|
||||
}
|
||||
|
||||
main().catch(error => {
|
||||
console.error(error);
|
||||
process.exitCode = 1;
|
||||
});
|
||||
58
packages/hardhat/scripts/runHardhatDeployWithPK.ts
Normal file
58
packages/hardhat/scripts/runHardhatDeployWithPK.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
import * as dotenv from "dotenv";
|
||||
dotenv.config();
|
||||
import { Wallet } from "ethers";
|
||||
import password from "@inquirer/password";
|
||||
import { spawn } from "child_process";
|
||||
import { config } from "hardhat";
|
||||
|
||||
/**
|
||||
* Unencrypts the private key and runs the hardhat deploy command
|
||||
*/
|
||||
async function main() {
|
||||
const networkIndex = process.argv.indexOf("--network");
|
||||
const networkName = networkIndex !== -1 ? process.argv[networkIndex + 1] : config.defaultNetwork;
|
||||
|
||||
if (networkName === "localhost" || networkName === "hardhat") {
|
||||
// Deploy command on the localhost network
|
||||
const hardhat = spawn("hardhat", ["deploy", ...process.argv.slice(2)], {
|
||||
stdio: "inherit",
|
||||
env: process.env,
|
||||
shell: process.platform === "win32",
|
||||
});
|
||||
|
||||
hardhat.on("exit", code => {
|
||||
process.exit(code || 0);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const encryptedKey = process.env.DEPLOYER_PRIVATE_KEY_ENCRYPTED;
|
||||
|
||||
if (!encryptedKey) {
|
||||
console.log("🚫️ You don't have a deployer account. Run `yarn generate` or `yarn account:import` first");
|
||||
return;
|
||||
}
|
||||
|
||||
const pass = await password({ message: "Enter password to decrypt private key:" });
|
||||
|
||||
try {
|
||||
const wallet = await Wallet.fromEncryptedJson(encryptedKey, pass);
|
||||
process.env.__RUNTIME_DEPLOYER_PRIVATE_KEY = wallet.privateKey;
|
||||
|
||||
const hardhat = spawn("hardhat", ["deploy", ...process.argv.slice(2)], {
|
||||
stdio: "inherit",
|
||||
env: process.env,
|
||||
shell: process.platform === "win32",
|
||||
});
|
||||
|
||||
hardhat.on("exit", code => {
|
||||
process.exit(code || 0);
|
||||
});
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
} catch (e) {
|
||||
console.error("Failed to decrypt private key. Wrong password?");
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
main().catch(console.error);
|
||||
Reference in New Issue
Block a user