diff --git a/templates/solidity-frameworks/foundry/packages/foundry/.env.example b/templates/solidity-frameworks/foundry/packages/foundry/.env.example index 7fe93e381..952d0d8c7 100644 --- a/templates/solidity-frameworks/foundry/packages/foundry/.env.example +++ b/templates/solidity-frameworks/foundry/packages/foundry/.env.example @@ -1,3 +1,21 @@ -DEPLOYER_PRIVATE_KEY= -ETHERSCAN_API_KEY= -ALCHEMY_API_KEY= \ No newline at end of file +# Template for foundry environment variables. + +# For local development, copy this file, rename it to .env, and fill in the values. + +# We provide default values so developers can start prototyping out of the box, +# but we recommend getting your own API Keys for Production Apps. + +# DEPLOYER_PRIVATE_KEY is used while deploying contract. +# On anvil chain the value of it can be empty since we use the prefunded account +# which comes with anvil chain to deploy contract. +# NOTE: You don't need to manually change the value of DEPLOYER_PRIVATE_KEY, it should +# be auto filled when run `yarn generate`. +# Although `.env` is ignored by git, it's still important that you don't paste your +# actual account private key and use the generated one. +# Alchemy rpc URL is used while deploying the contracts to some testnets/mainnets, checkout `foundry.toml` for it's use. +ALCHEMY_API_KEY=oKxs-03sij-U_N0iOlrSsZFr29-IqbuF + +# Etherscan API key is used to verify the contract on etherscan. +ETHERSCAN_API_KEY=DNXJA8RX2Q3VZ4URQIWP7Z68CJXQZSC6AW +# Default account for localhost / use "scaffold-eth-custom" if you wish to use a generated account or imported account +ETH_KEYSTORE_ACCOUNT=scaffold-eth-default diff --git a/templates/solidity-frameworks/foundry/packages/foundry/.env.template.mjs b/templates/solidity-frameworks/foundry/packages/foundry/.env.template.mjs index 62fd71dd8..26359667c 100644 --- a/templates/solidity-frameworks/foundry/packages/foundry/.env.template.mjs +++ b/templates/solidity-frameworks/foundry/packages/foundry/.env.template.mjs @@ -1,7 +1,24 @@ const contents = () => -`DEPLOYER_PRIVATE_KEY= + `# Template for foundry environment variables. + +# For local development, copy this file, rename it to .env, and fill in the values. + +# We provide default values so developers can start prototyping out of the box, +# but we recommend getting your own API Keys for Production Apps. + +# DEPLOYER_PRIVATE_KEY is used while deploying contract. +# On anvil chain the value of it can be empty since we use the prefunded account +# which comes with anvil chain to deploy contract. +# NOTE: You don't need to manually change the value of DEPLOYER_PRIVATE_KEY, it should +# be auto filled when run \`yarn generate\`. +# Although \`.env\` is ignored by git, it's still important that you don't paste your +# actual account private key and use the generated one. +# Alchemy rpc URL is used while deploying the contracts to some testnets/mainnets, checkout \`foundry.toml\` for it's use. ALCHEMY_API_KEY=oKxs-03sij-U_N0iOlrSsZFr29-IqbuF + +# Etherscan API key is used to verify the contract on etherscan. ETHERSCAN_API_KEY=DNXJA8RX2Q3VZ4URQIWP7Z68CJXQZSC6AW -` +# Default account for localhost / use "scaffold-eth-custom" if you wish to use a generated account or imported account +ETH_KEYSTORE_ACCOUNT=scaffold-eth-default`; -export default contents +export default contents; diff --git a/templates/solidity-frameworks/foundry/packages/foundry/Makefile b/templates/solidity-frameworks/foundry/packages/foundry/Makefile new file mode 100644 index 000000000..00144c4d3 --- /dev/null +++ b/templates/solidity-frameworks/foundry/packages/foundry/Makefile @@ -0,0 +1,89 @@ +.PHONY: build deploy generate-abis verify-keystore account chain compile deploy-verify flatten fork format lint test verify + +# setup wallet for anvil +setup-anvil-wallet: + shx rm ~/.foundry/keystores/scaffold-eth-default 2>/dev/null; \ + cast wallet import --private-key 0x2a871d0798f97d79848a013d4936a73bf4cc922c825d33c1cf7073dff6d409c6 --unsafe-password 'localhost' scaffold-eth-default + +# Start local chain +chain: setup-anvil-wallet + anvil + +# Start a fork +fork: setup-anvil-wallet + anvil --fork-url ${FORK_URL} --chain-id 31337 + +# Build the project +build: + forge build --build-info --build-info-path out/build-info/ + +# Deploy the project +deploy: + @if [ "$(RPC_URL)" = "localhost" ]; then \ + forge script script/Deploy.s.sol --rpc-url localhost --password localhost --broadcast --legacy --ffi; \ + else \ + forge script script/Deploy.s.sol --rpc-url $(RPC_URL) --broadcast --legacy --ffi; \ + fi + +# Build and deploy target +build-and-deploy: build deploy generate-abis + +# Generate TypeScript ABIs +generate-abis: + node scripts-js/generateTsAbis.js + +verify-keystore: + if grep -q "scaffold-eth-default" .env; then \ + cast wallet address --password localhost; \ + else \ + cast wallet address; \ + fi + +# List account +account: + @node scripts-js/ListAccount.js $$(make verify-keystore) + +# Generate a new account +account-generate: + @cast wallet import $(ACCOUNT_NAME) --private-key $$(cast wallet new | grep 'Private key:' | awk '{print $$3}') + @echo "Please update .env file with ETH_KEYSTORE_ACCOUNT=$(ACCOUNT_NAME)" + +# Import an existing account +account-import: + @cast wallet import ${ACCOUNT_NAME} --interactive + +# Compile contracts +compile: + forge compile + +# Deploy and verify +deploy-verify: + @if [ "$(RPC_URL)" = "localhost" ]; then \ + forge script script/Deploy.s.sol --rpc-url localhost --password localhost --broadcast --legacy --ffi --verify; \ + else \ + forge script script/Deploy.s.sol --rpc-url $(RPC_URL) --broadcast --legacy --ffi --verify; \ + fi + node scripts-js/generateTsAbis.js + +# Flatten contracts +flatten: + forge flatten + +# Format code +format: + forge fmt && prettier --write ./scripts-js/**/*.js + +# Lint code +lint: + forge fmt --check && prettier --check ./script/**/*.js + +# Run tests +test: + forge test + +# Verify contracts +verify: + forge script script/VerifyAll.s.sol --ffi --rpc-url $(RPC_URL) + +build-and-verify: build verify + diff --git a/templates/solidity-frameworks/foundry/packages/foundry/package.json b/templates/solidity-frameworks/foundry/packages/foundry/package.json index 81dddfad6..960366dc0 100644 --- a/templates/solidity-frameworks/foundry/packages/foundry/package.json +++ b/templates/solidity-frameworks/foundry/packages/foundry/package.json @@ -1,23 +1,23 @@ { "name": "@se-2/foundry", "version": "0.0.1", + "type": "module", "scripts": { - "account": "node script/ListAccount.js", - "chain": "anvil --config-out localhost.json", - "fork": "anvil --fork-url ${0:-mainnet} --chain-id 31337 --config-out localhost.json", - "compile": "forge compile", - "generate": "node script/generateAccount.js", - "flatten": "forge flatten", - "deploy": "forge build --build-info --build-info-path out/build-info/ && forge script script/Deploy.s.sol --rpc-url ${1:-default_network} --broadcast --legacy && node script/generateTsAbis.js", - "deploy:verify": "forge build --build-info --build-info-path out/build-info/ && forge script script/Deploy.s.sol --rpc-url ${1:-default_network} --broadcast --legacy --verify ; node script/generateTsAbis.js", - "verify": "forge build --build-info --build-info-path out/build-info/ && forge script script/VerifyAll.s.sol --ffi --rpc-url ${1:-default_network}", - "lint": "forge fmt --check && prettier --check ./script/**/*.js", - "format": "forge fmt && prettier --write ./script/**/*.js", - "test": "forge test" - }, - "devDependencies": { - "@types/prettier": "2", - "@types/qrcode": "1" + "verify-keystore": "make verify-keystore", + "account": "make account", + "account:generate": "make account-generate ACCOUNT_NAME=${1:-scaffold-eth-custom}", + "account:import": "make account-import ACCOUNT_NAME=${1:-scaffold-eth-custom}", + "chain": "make chain", + "compile": "make compile", + "deploy": "make build-and-deploy RPC_URL=${1:-localhost}", + "deploy:verify": "make deploy-verify RPC_URL=${1:-localhost}", + "flatten": "make flatten", + "fork": "make fork FORK_URL=${1:-mainnet}", + "format": "make format", + "lint": "make lint", + "test": "make test", + "verify": "make build-and-verify RPC_URL=${1:-localhost}", + "postinstall": "shx cp -n .env.example .env" }, "dependencies": { "dotenv": "~16.3.1", @@ -26,5 +26,8 @@ "prettier": "~2.8.8", "qrcode": "~1.5.3", "toml": "~3.0.0" + }, + "devDependencies": { + "shx": "~0.3.4" } } diff --git a/templates/solidity-frameworks/foundry/packages/foundry/script/Deploy.s.sol.template.mjs b/templates/solidity-frameworks/foundry/packages/foundry/script/Deploy.s.sol.template.mjs index 9089bdad8..f9f05e2ff 100644 --- a/templates/solidity-frameworks/foundry/packages/foundry/script/Deploy.s.sol.template.mjs +++ b/templates/solidity-frameworks/foundry/packages/foundry/script/Deploy.s.sol.template.mjs @@ -3,45 +3,21 @@ import { withDefaults } from "../../../../../utils.js"; const content = ({ deploymentsScriptsImports, deploymentsLogic }) => `//SPDX-License-Identifier: MIT pragma solidity ^0.8.19; -import "../contracts/YourContract.sol"; import "./DeployHelpers.s.sol"; +import { DeployYourContract } from "./DeployYourContract.s.sol"; ${deploymentsScriptsImports.filter(Boolean).join("\n")} contract DeployScript is ScaffoldETHDeploy { - error InvalidPrivateKey(string); + function run() external { + DeployYourContract deployYourContract = new DeployYourContract(); + deployYourContract.run(); - function run() external { - uint256 deployerPrivateKey = setupLocalhostEnv(); - if (deployerPrivateKey == 0) { - revert InvalidPrivateKey( - "You don't have a deployer account. Make sure you have set DEPLOYER_PRIVATE_KEY in .env or use \`yarn generate\` to generate a new random account" - ); - } - vm.startBroadcast(deployerPrivateKey); + ${deploymentsLogic.filter(Boolean).join("\n")} - YourContract yourContract = new YourContract( - vm.addr(deployerPrivateKey) - ); - console.logString( - string.concat( - "YourContract deployed at: ", - vm.toString(address(yourContract)) - ) - ); - - vm.stopBroadcast(); - - ${deploymentsLogic.filter(Boolean).join("\n")} - - /** - * This function 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 function should be called last. - */ - exportDeployments(); - } - - function test() public {} + // deploy more contracts here + // DeployMyContract deployMyContract = new DeployMyContract(); + // deployMyContract.run(); + } }`; export default withDefaults(content, { diff --git a/templates/solidity-frameworks/foundry/packages/foundry/script/DeployHelpers.s.sol b/templates/solidity-frameworks/foundry/packages/foundry/script/DeployHelpers.s.sol index 7338ac1af..d8bacf668 100644 --- a/templates/solidity-frameworks/foundry/packages/foundry/script/DeployHelpers.s.sol +++ b/templates/solidity-frameworks/foundry/packages/foundry/script/DeployHelpers.s.sol @@ -1,11 +1,16 @@ //SPDX-License-Identifier: MIT pragma solidity ^0.8.19; -import "forge-std/Script.sol"; -import "forge-std/Vm.sol"; +import { Script, console } from "forge-std/Script.sol"; +import { Vm } from "forge-std/Vm.sol"; contract ScaffoldETHDeploy is Script { error InvalidChain(); + error DeployerHasNoBalance(); + error InvalidPrivateKey(string); + + event AnvilSetBalance(address account, uint256 amount); + event FailedAnvilRequest(); struct Deployment { string name; @@ -15,18 +20,38 @@ contract ScaffoldETHDeploy is Script { string root; string path; Deployment[] public deployments; + uint256 constant ANVIL_BASE_BALANCE = 10000 ether; + + /// @notice The deployer address for every run + address deployer; - function setupLocalhostEnv() internal returns (uint256 localhostPrivateKey) { - if (block.chainid == 31337) { - root = vm.projectRoot(); - path = string.concat(root, "/localhost.json"); - string memory json = vm.readFile(path); - bytes memory mnemonicBytes = vm.parseJson(json, ".wallet.mnemonic"); - string memory mnemonic = abi.decode(mnemonicBytes, (string)); - return vm.deriveKey(mnemonic, 0); - } else { - return vm.envUint("DEPLOYER_PRIVATE_KEY"); + /// @notice Use this modifier on your run() function on your deploy scripts + modifier ScaffoldEthDeployerRunner() { + deployer = _startBroadcast(); + if (deployer == address(0)) { + revert InvalidPrivateKey("Invalid private key"); + } + _; + _stopBroadcast(); + exportDeployments(); + } + + function _startBroadcast() internal returns (address) { + vm.startBroadcast(); + (, address _deployer,) = vm.readCallers(); + + if (block.chainid == 31337 && _deployer.balance == 0) { + try this.anvil_setBalance(_deployer, ANVIL_BASE_BALANCE) { + emit AnvilSetBalance(_deployer, ANVIL_BASE_BALANCE); + } catch { + emit FailedAnvilRequest(); + } } + return _deployer; + } + + function _stopBroadcast() internal { + vm.stopBroadcast(); } function exportDeployments() internal { @@ -61,6 +86,30 @@ contract ScaffoldETHDeploy is Script { return getChain(block.chainid); } + function anvil_setBalance(address addr, uint256 amount) public { + string memory addressString = vm.toString(addr); + string memory amountString = vm.toString(amount); + string memory requestPayload = string.concat( + '{"method":"anvil_setBalance","params":["', + addressString, + '","', + amountString, + '"],"id":1,"jsonrpc":"2.0"}' + ); + + string[] memory inputs = new string[](8); + inputs[0] = "curl"; + inputs[1] = "-X"; + inputs[2] = "POST"; + inputs[3] = "http://localhost:8545"; + inputs[4] = "-H"; + inputs[5] = "Content-Type: application/json"; + inputs[6] = "--data"; + inputs[7] = requestPayload; + + vm.ffi(inputs); + } + function findChainName() public returns (string memory) { uint256 thisChainId = block.chainid; string[2][] memory allRpcUrls = vm.rpcUrls(); diff --git a/templates/solidity-frameworks/foundry/packages/foundry/script/DeployYourContract.s.sol b/templates/solidity-frameworks/foundry/packages/foundry/script/DeployYourContract.s.sol new file mode 100644 index 000000000..166e2df57 --- /dev/null +++ b/templates/solidity-frameworks/foundry/packages/foundry/script/DeployYourContract.s.sol @@ -0,0 +1,13 @@ +//SPDX-License-Identifier: MIT +pragma solidity ^0.8.19; + +import "../contracts/YourContract.sol"; +import "./DeployHelpers.s.sol"; + +contract DeployYourContract is ScaffoldETHDeploy { + // use `deployer` from `ScaffoldETHDeploy` + function run() external ScaffoldEthDeployerRunner { + YourContract yourContract = new YourContract(deployer); + console.logString(string.concat("YourContract deployed at: ", vm.toString(address(yourContract)))); + } +} diff --git a/templates/solidity-frameworks/foundry/packages/foundry/script/ListAccount.js b/templates/solidity-frameworks/foundry/packages/foundry/script/ListAccount.js deleted file mode 100644 index 20493b3a8..000000000 --- a/templates/solidity-frameworks/foundry/packages/foundry/script/ListAccount.js +++ /dev/null @@ -1,77 +0,0 @@ -const dotenv = require("dotenv"); -dotenv.config(); -const path = require("path"); -const { ethers, Wallet } = require("ethers"); -const QRCode = require("qrcode"); -const fs = require("fs"); -const toml = require("toml"); - -const ALCHEMY_API_KEY = - process.env.ALCHEMY_API_KEY || "oKxs-03sij-U_N0iOlrSsZFr29-IqbuF"; - -async function getBalanceForEachNetwork(address) { - try { - // Read the foundry.toml file - const foundryTomlPath = path.join(__dirname, "..", "foundry.toml"); - const tomlString = fs.readFileSync(foundryTomlPath, "utf-8"); - - // Parse the tomlString to get the JS object representation - const parsedToml = toml.parse(tomlString); - - // Extract rpc_endpoints from parsedToml - const rpcEndpoints = parsedToml.rpc_endpoints; - - // Replace placeholders in the rpc_endpoints section - function replaceENVAlchemyKey(input) { - return input.replace("${ALCHEMY_API_KEY}", ALCHEMY_API_KEY); - } - - for (const networkName in rpcEndpoints) { - if (networkName === "localhost" || networkName === "default_network") - continue; - - const networkUrl = replaceENVAlchemyKey(rpcEndpoints[networkName]); - - try { - const provider = new ethers.providers.JsonRpcProvider(networkUrl); - const balance = await provider.getBalance(address); - console.log("--", networkName, "-- šŸ“”"); - console.log(" balance:", +ethers.utils.formatEther(balance)); - console.log( - " nonce:", - +(await provider.getTransactionCount(address)) - ); - } catch (e) { - console.log("Can't connect to network", networkName); - console.log(); - } - } - } catch (error) { - console.error("Error reading foundry.toml:", error); - } -} -async function main() { - const privateKey = process.env.DEPLOYER_PRIVATE_KEY; - - if (!privateKey) { - console.log( - "šŸš«ļø You don't have a deployer account. Run `yarn generate` first" - ); - return; - } - - // Get account from private key. - const wallet = new Wallet(privateKey); - const address = wallet.address; - console.log( - await QRCode.toString(address, { type: "terminal", small: true }) - ); - console.log("Public address:", address, "\n"); - - await getBalanceForEachNetwork(address); -} - -main().catch((error) => { - console.error(error); - process.exitCode = 1; -}); diff --git a/templates/solidity-frameworks/foundry/packages/foundry/script/VerifyAll.s.sol b/templates/solidity-frameworks/foundry/packages/foundry/script/VerifyAll.s.sol index 275f49b97..2b307a04d 100644 --- a/templates/solidity-frameworks/foundry/packages/foundry/script/VerifyAll.s.sol +++ b/templates/solidity-frameworks/foundry/packages/foundry/script/VerifyAll.s.sol @@ -49,32 +49,28 @@ contract VerifyAll is Script { } } - function _verifyContract(string memory content) internal { - string memory contractName = abi.decode( - vm.parseJson(content, searchStr(currTransactionIdx, "contractName")), - (string) - ); - address contractAddr = abi.decode( - vm.parseJson( - content, searchStr(currTransactionIdx, "contractAddress") - ), - (address) - ); - bytes memory deployedBytecode = abi.decode( - vm.parseJson( - content, searchStr(currTransactionIdx, "transaction.input") - ), - (bytes) - ); - bytes memory compiledBytecode = abi.decode( - vm.parseJson(_getCompiledBytecode(contractName), ".bytecode.object"), - (bytes) - ); - bytes memory constructorArgs = BytesLib.slice( - deployedBytecode, - compiledBytecode.length, - deployedBytecode.length - compiledBytecode.length - ); + function _verifyContract(string memory content) internal { + string memory contractName = abi.decode( + vm.parseJson(content, searchStr(currTransactionIdx, "contractName")), + (string) + ); + address contractAddr = abi.decode( + vm.parseJson(content, searchStr(currTransactionIdx, "contractAddress")), + (address) + ); + bytes memory deployedBytecode = abi.decode( + vm.parseJson(content, searchStr(currTransactionIdx, "transaction.input")), + (bytes) + ); + bytes memory compiledBytecode = abi.decode( + vm.parseJson(_getCompiledBytecode(contractName), ".bytecode.object"), + (bytes) + ); + bytes memory constructorArgs = BytesLib.slice( + deployedBytecode, + compiledBytecode.length, + deployedBytecode.length - compiledBytecode.length + ); string[] memory inputs = new string[](9); inputs[0] = "forge"; @@ -110,11 +106,9 @@ contract VerifyAll is Script { } } - function _getCompiledBytecode(string memory contractName) - internal - view - returns (string memory compiledBytecode) - { + function _getCompiledBytecode( + string memory contractName + ) internal view returns (string memory compiledBytecode) { string memory root = vm.projectRoot(); string memory path = string.concat(root, "/out/", contractName, ".sol/", contractName, ".json"); diff --git a/templates/solidity-frameworks/foundry/packages/foundry/script/generateAccount.js b/templates/solidity-frameworks/foundry/packages/foundry/script/generateAccount.js deleted file mode 100644 index d6b2be33b..000000000 --- a/templates/solidity-frameworks/foundry/packages/foundry/script/generateAccount.js +++ /dev/null @@ -1,49 +0,0 @@ -const ethers = require("ethers"); -const { parse, stringify } = require("envfile"); -const fs = require("fs"); - -const envFilePath = "./.env"; - -/** - * Generate a new random private key and write it to the .env file - * @param existingEnvConfig - */ -const setNewEnvConfig = (existingEnvConfig = {}) => { - console.log("šŸ‘› Generating new Wallet"); - const randomWallet = ethers.Wallet.createRandom(); - - const newEnvConfig = { - ...existingEnvConfig, - DEPLOYER_PRIVATE_KEY: randomWallet.privateKey, - }; - - // Store in .env - fs.writeFileSync(envFilePath, stringify(newEnvConfig)); - console.log("šŸ“„ Private Key saved to packages/foundry/.env file"); - console.log("šŸŖ„ Generated wallet address:", randomWallet.address); -}; - -async function main() { - if (!fs.existsSync(envFilePath)) { - console.log("entered here"); - // No .env file yet. - setNewEnvConfig(); - return; - } - - // .env file exists - const existingEnvConfig = parse(fs.readFileSync(envFilePath).toString()); - if (existingEnvConfig.DEPLOYER_PRIVATE_KEY) { - console.log( - "āš ļø You already have a deployer account. Check the packages/foundry/.env file" - ); - return; - } - - setNewEnvConfig(existingEnvConfig); -} - -main().catch((error) => { - console.error(error); - process.exitCode = 1; -}); diff --git a/templates/solidity-frameworks/foundry/packages/foundry/scripts-js/ListAccount.js b/templates/solidity-frameworks/foundry/packages/foundry/scripts-js/ListAccount.js new file mode 100644 index 000000000..a2c31fd72 --- /dev/null +++ b/templates/solidity-frameworks/foundry/packages/foundry/scripts-js/ListAccount.js @@ -0,0 +1,110 @@ +import { config } from "dotenv"; +config(); +import { join, dirname } from "path"; +import { ethers, Wallet } from "ethers"; +import { toString } from "qrcode"; +import { readFileSync } from "fs"; +import { parse } from "toml"; +import { fileURLToPath } from "url"; + +const __dirname = dirname(fileURLToPath(import.meta.url)); + +const ALCHEMY_API_KEY = + process.env.ALCHEMY_API_KEY || "oKxs-03sij-U_N0iOlrSsZFr29-IqbuF"; + +async function getBalanceForEachNetwork(address) { + try { + // Read the foundry.toml file + const foundryTomlPath = join(__dirname, "..", "foundry.toml"); + const tomlString = readFileSync(foundryTomlPath, "utf-8"); + + // Parse the tomlString to get the JS object representation + const parsedToml = parse(tomlString); + + // Extract rpc_endpoints from parsedToml + const rpcEndpoints = parsedToml.rpc_endpoints; + + // Replace placeholders in the rpc_endpoints section + function replaceENVAlchemyKey(input) { + return input.replace("${ALCHEMY_API_KEY}", ALCHEMY_API_KEY); + } + + for (const networkName in rpcEndpoints) { + if (networkName === "localhost") continue; + + const networkUrl = replaceENVAlchemyKey(rpcEndpoints[networkName]); + + try { + const provider = new ethers.providers.JsonRpcProvider(networkUrl); + const balance = await provider.getBalance(address); + console.log("--", networkName, "-- šŸ“”"); + console.log(" balance:", +ethers.utils.formatEther(balance)); + console.log( + " nonce:", + +(await provider.getTransactionCount(address)) + ); + } catch (e) { + console.log("Can't connect to network", networkName); + console.log(); + } + } + } catch (error) { + console.error("Error reading foundry.toml:", error); + } +} + +function verifyAddressFormat(address) { + try { + ethers.utils.getAddress(address); + return true; + } catch (e) { + return false; + } +} + +function findAddressFromArgs(args) { + return args.find((arg) => arg.startsWith("0x") && arg.length === 42); +} + +const DEFAULT_KEYSTORE_ACCOUNT = "scaffold-eth-default"; +async function main() { + const address = findAddressFromArgs(process.argv); + + const isValidAddress = verifyAddressFormat(address); + const isDefaultAccount = + process.env.ETH_KEYSTORE_ACCOUNT === DEFAULT_KEYSTORE_ACCOUNT; + + if (!isValidAddress) { + console.log( + `\nšŸš«ļø Unable to access keystore account ${process.env.ETH_KEYSTORE_ACCOUNT}` + ); + + if (isDefaultAccount) { + console.log( + "\nšŸ  It seems you are trying to access the localhost account. Did you forget to update ETH_KEYSTORE_ACCOUNT=scaffold-eth-custom in the .env file?\n" + ); + } + + console.log( + "\nšŸ’” If you haven't generated a deployer keystore account yet, please run `yarn account:generate`. Then update the `.env` file with `ETH_KEYSTORE_ACCOUNT=scaffold-eth-custom`" + ); + return; + } + + if (isValidAddress && isDefaultAccount) { + console.log("\nāš ļø Displaying balance for default account"); + console.log( + "\nā“ Did you forget to update ETH_KEYSTORE_ACCOUNT=scaffold-eth-custom in the .env file?\n" + ); + } + + console.log(await toString(address, { type: "terminal", small: true })); + console.log("Public address:", address, "\n"); + + await getBalanceForEachNetwork(address); +} + +main().catch((error) => { + console.error(error); + process.exitCode = 1; +}); diff --git a/templates/solidity-frameworks/foundry/packages/foundry/script/generateTsAbis.js b/templates/solidity-frameworks/foundry/packages/foundry/scripts-js/generateTsAbis.js similarity index 77% rename from templates/solidity-frameworks/foundry/packages/foundry/script/generateTsAbis.js rename to templates/solidity-frameworks/foundry/packages/foundry/scripts-js/generateTsAbis.js index 699c914cd..274f12cb3 100644 --- a/templates/solidity-frameworks/foundry/packages/foundry/script/generateTsAbis.js +++ b/templates/solidity-frameworks/foundry/packages/foundry/scripts-js/generateTsAbis.js @@ -1,8 +1,16 @@ -const fs = require("fs"); -const path = require("path"); -//@ts-expect-error This script runs after `forge deploy` therefore its deterministic that it will present -// const deployments = require("../deployments.json"); -const prettier = require("prettier"); +import { + readdirSync, + statSync, + readFileSync, + existsSync, + mkdirSync, + writeFileSync, +} from "fs"; +import { join, dirname } from "path"; +import { fileURLToPath } from "url"; +import { format } from "prettier"; + +const __dirname = dirname(fileURLToPath(import.meta.url)); const generatedContractComment = ` /** @@ -12,23 +20,23 @@ const generatedContractComment = ` `; function getDirectories(path) { - return fs.readdirSync(path).filter(function (file) { - return fs.statSync(path + "/" + file).isDirectory(); + return readdirSync(path).filter(function (file) { + return statSync(path + "/" + file).isDirectory(); }); } function getFiles(path) { - return fs.readdirSync(path).filter(function (file) { - return fs.statSync(path + "/" + file).isFile(); + return readdirSync(path).filter(function (file) { + return statSync(path + "/" + file).isFile(); }); } function getArtifactOfContract(contractName) { - const current_path_to_artifacts = path.join( + const current_path_to_artifacts = join( __dirname, "..", `out/${contractName}.sol` ); const artifactJson = JSON.parse( - fs.readFileSync(`${current_path_to_artifacts}/${contractName}.json`) + readFileSync(`${current_path_to_artifacts}/${contractName}.json`) ); return artifactJson; @@ -68,12 +76,12 @@ function getInheritedFunctions(mainArtifact) { } function main() { - const current_path_to_broadcast = path.join( + const current_path_to_broadcast = join( __dirname, "..", "broadcast/Deploy.s.sol" ); - const current_path_to_deployments = path.join(__dirname, "..", "deployments"); + const current_path_to_deployments = join(__dirname, "..", "deployments"); const chains = getDirectories(current_path_to_broadcast); const Deploymentchains = getFiles(current_path_to_deployments); @@ -84,7 +92,7 @@ function main() { if (!chain.endsWith(".json")) return; chain = chain.slice(0, -5); var deploymentObject = JSON.parse( - fs.readFileSync(`${current_path_to_deployments}/${chain}.json`) + readFileSync(`${current_path_to_deployments}/${chain}.json`) ); deployments[chain] = deploymentObject; }); @@ -94,7 +102,7 @@ function main() { chains.forEach((chain) => { allGeneratedContracts[chain] = {}; const broadCastObject = JSON.parse( - fs.readFileSync(`${current_path_to_broadcast}/${chain}/run-latest.json`) + readFileSync(`${current_path_to_broadcast}/${chain}/run-latest.json`) ); const transactionsCreate = broadCastObject.transactions.filter( (transaction) => transaction.transactionType == "CREATE" @@ -125,12 +133,12 @@ function main() { "" ); - if (!fs.existsSync(TARGET_DIR)) { - fs.mkdirSync(TARGET_DIR); + if (!existsSync(TARGET_DIR)) { + mkdirSync(TARGET_DIR); } - fs.writeFileSync( + writeFileSync( `${TARGET_DIR}deployedContracts.ts`, - prettier.format( + format( `${generatedContractComment} import { GenericContractsDeclaration } from "~~/utils/scaffold-eth/contract"; \n\n const deployedContracts = {${fileContent}} as const; \n\n export default deployedContracts satisfies GenericContractsDeclaration`, {