{"content":{"title":"Foundry 工程中的合约部署和验证教程","body":"## 目录\r\n\r\n1. [导入钱包私钥到 keystore](#1-导入钱包私钥到-keystore) \r\n2. [编写合约和部署脚本](#2-编写合约和部署脚本) \r\n3. [配置环境变量](#3-配置环境变量) \r\n4. [编写 Bash 部署脚本](#4-编写-bash-部署脚本) \r\n5. [运行部署脚本](#5-运行部署脚本) \r\n6. [验证合约](#6-验证合约) \r\n7. [总结](#7-总结)\r\n\r\n\r\n### 1-导入钱包私钥到-keystore ###\r\n\r\n使用cast wallet import把你的钱包私钥导入到keystore\r\n\r\n```bash\r\n$ cast wallet import Metamask -i\r\nEnter private key:\r\nEnter password: \r\n`Metamask` keystore was saved successfully. Address: 0xe844f618f2c47aa2bab373a0b46ce6a2ce427ed9\r\n\r\n$ cast wallet list\r\nMetamask (Local)\r\n\r\n$ cat ~/.foundry/keystores/Metamask \r\n{\"crypto\":{\"cipher\":\"aes-128-ctr\",\"cipherparams\":{\"iv\":\"5e0e2865628deaeb4536cb1baa841c95\"},\"ciphertext\":\"afaa88a316b2dcfeeee87c3a701729c2f5aa98ef259e6c72c0582644b8539ef4\",\"kdf\":\"scrypt\",\"kdfparams\":{\"dklen\":32,\"n\":8192,\"p\":1,\"r\":8,\"salt\":\"8370cd9bf48c7e1f3efd535abcf81b974235532bb8d652bada982e3c73a31bdd\"},\"mac\":\"77bda41cac19695c29c604c645882df20659a3df48b9dfee33488aa876553c70\"},\"id\":\"33f5ac31-1837-41f7-a15c-98d03436cc98\",\"version\":3}%\r\n```\r\n\r\n可以看到上面已经生成了一个wallet存到的keystore里, 内容都是被加密过的。\r\n\r\n### 2-编写合约和部署脚本 ###\r\nfoundry工程中编写合约和script\r\n```\r\n// SPDX-License-Identifier: MIT\r\npragma solidity 0.8.25;\r\n\r\nimport \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\r\nimport \"forge-std/Script.sol\";\r\n\r\ncontract MyToken is ERC20 {\r\n    constructor(\r\n        string memory name_,\r\n        string memory symbol_\r\n    ) ERC20(name_, symbol_) {\r\n        _mint(msg.sender, 1e10 * 1e18);\r\n    }\r\n}\r\n\r\ncontract DeployMyToken is Script {\r\n    function run() external {\r\n        vm.startBroadcast();\r\n        \r\n        // deploy contract\r\n        MyToken myToken = new MyToken(\"MyToken\", \"MTK\");\r\n\r\n        vm.stopBroadcast();\r\n    }\r\n}\r\n```\r\n\r\n### 3-配置环境变量 ###\r\n在foundry工程中准备.env来配置必要的部署参数,请自行填写对应的参数\r\n```\r\nMAINNET_FORK_URL=\r\nOPT_FORK_URL=\r\nSEPOLIA_RPC_URL=<YOUR_ETHERSCAN_API_KEY>\r\nDEV_PRIVATE_KEY=0x123\r\nETHERSCAN_API_KEY=<YOUR_ETHERSCAN_API_KEY>\r\nCHAIN_ID=11155111\r\n```\r\n\r\n### 4-编写-bash-部署脚本 ###\r\n在foundry工程中准备编写bash部署脚本\r\n\r\n```bash\r\n#!/bin/bash\r\n\r\n# effect the env vars\r\nsource .env\r\n\r\n# get bash arg\r\nwhile [[ \"$#\" -gt 0 ]]; do\r\n  case $1 in\r\n    --file) SCRIPT_FILE=\"$2\"; shift ;;\r\n    --account) ACCOUNT=\"$2\"; shift ;;\r\n    *) echo \"unknown arg: $1\" ; exit 1 ;;\r\n  esac\r\n  shift\r\ndone\r\n\r\n# make sure the script file was provided\r\nif [[ -z \"$SCRIPT_FILE\" ]]; then\r\n  echo \"Please specify --file <your_script_path>\"\r\n  exit 1\r\nfi\r\n\r\nif [[ -z \"$ACCOUNT\" ]]; then\r\n  echo \"Please specify --account <your_cast_wallet_account>\"\r\n  exit 1\r\nfi\r\n\r\n# check if the env vars was defined preceedly\r\nif [[ -z \"$SEPOLIA_RPC_URL\" || -z \"$ETHERSCAN_API_KEY\" || -z \"$CHAIN_ID\" ]]; then\r\n  echo \"Please ensure .env defines the vars：SEPOLIA_RPC_URL, ETHERSCAN_API_KEY, CHAIN_ID\"\r\n  exit 1\r\nfi\r\n\r\nif [[ -z \"$PRIVATE_KEY\" ]]; then\r\n  echo \"Can not load the private key from keystore...\"\r\n  exit 1\r\nfi\r\n\r\n# deploy\r\nforge script \"$SCRIPT_FILE\" \\\r\n  --rpc-url \"$SEPOLIA_RPC_URL\" \\\r\n  --broadcast \\\r\n  --verify \\\r\n  --etherscan-api-key \"$ETHERSCAN_API_KEY\" \\\r\n  --account $ACCOUNT \\\r\n  -vvvv\r\n```\r\n\r\n### 5-运行部署脚本 ###\r\n使用编写好的bash脚本 \r\n- --file 参数指定要部署的文件 例如 -file script/DeployMyToken.s.sol:DeployMyToken \r\n- --account 参数指定你用cast wallet import 的 account\r\n- **在部署的过程中会问询你输入创建account时的密码**\r\n\r\n```bash\r\nbash ./script/deploy.sh --file script/DeployMyToken.s.sol:DeployMyToken --account Metamask\r\n\r\nPlease input cast wallet account Metamask keystore password：\r\n\r\n[⠒] Compiling...\r\n[⠒] Compiling 1 files with Solc 0.8.25\r\n[⠢] Solc 0.8.25 finished in 1.97s\r\nCompiler run successful with warnings:\r\nWarning (2072): Unused local variable.\r\n  --> script/DeployMyToken.s.sol:24:9:\r\n   |\r\n24 |         MyToken myToken = new MyToken(\"MyToken\", \"MTK\");\r\n   |         ^^^^^^^^^^^^^^^\r\n\r\nEnter keystore password:\r\nTraces:\r\n  [494159] DeployMyToken::run()\r\n    ├─ [0] VM::startBroadcast()\r\n    │   └─ ← [Return] \r\n    ├─ [458012] → new MyToken@0x69C7b902d29bE3B516F42Fba4E919f4C17A310fD\r\n    │   ├─ emit Transfer(from: 0x0000000000000000000000000000000000000000, to: 0xE844F618f2c47aA2BAb373a0b46ce6a2ce427eD9, value: 10000000000000000000000000000 [1e28])\r\n    │   └─ ← [Return] 1825 bytes of code\r\n    ├─ [0] VM::stopBroadcast()\r\n    │   └─ ← [Return] \r\n    └─ ← [Stop] \r\n\r\n\r\nScript ran successfully.\r\n\r\n## Setting up 1 EVM.\r\n==========================\r\nSimulated On-chain Traces:\r\n\r\n  [458012] → new MyToken@0x69C7b902d29bE3B516F42Fba4E919f4C17A310fD\r\n    ├─ emit Transfer(from: 0x0000000000000000000000000000000000000000, to: 0xE844F618f2c47aA2BAb373a0b46ce6a2ce427eD9, value: 10000000000000000000000000000 [1e28])\r\n    └─ ← [Return] 1825 bytes of code\r\n\r\n\r\n==========================\r\n\r\nChain 11155111\r\n\r\nEstimated gas price: 13.158315272 gwei\r\n\r\nEstimated total gas used for script: 725597\r\n\r\nEstimated amount required: 0.009547634086417384 ETH\r\n\r\n==========================\r\n##\r\nSending transactions [0 - 0].\r\n⠁ [00:00:00] [###############################################################################################] 1/1 txes (0.0s)##\r\nWaiting for receipts.\r\n⠉ [00:00:21] [###########################################################################################] 1/1 receipts (0.0s)\r\n##### sepolia\r\n✅  [Success]Hash: 0xb814b28778d277f3c8b81a4f491c7dfcd33a96eaaff4f35ec13665d684eee79e\r\nContract Address: 0x69C7b902d29bE3B516F42Fba4E919f4C17A310fD\r\nBlock: 6297189\r\nPaid: 0.00390782089756305 ETH (558350 gas * 6.998873283 gwei)\r\n\r\n\r\n\r\n==========================\r\n\r\nONCHAIN EXECUTION COMPLETE & SUCCESSFUL.\r\nTotal Paid: 0.00390782089756305 ETH (558350 gas * avg 6.998873283 gwei)\r\n##\r\nStart verification for (1) contracts\r\nStart verifying contract `0x69C7b902d29bE3B516F42Fba4E919f4C17A310fD` deployed on sepolia\r\n\r\nSubmitting verification for [script/DeployMyToken.s.sol:MyToken] 0x69C7b902d29bE3B516F42Fba4E919f4C17A310fD.\r\n\r\nSubmitting verification for [script/DeployMyToken.s.sol:MyToken] 0x69C7b902d29bE3B516F42Fba4E919f4C17A310fD.\r\n\r\nSubmitting verification for [script/DeployMyToken.s.sol:MyToken] 0x69C7b902d29bE3B516F42Fba4E919f4C17A310fD.\r\nSubmitted contract for verification:\r\n        Response: `OK`\r\n        GUID: `f8vuzmbhgyek3dzixgv4uzj2675ajdzikyprg8swkkfxbcz7cr`\r\n        URL: https://sepolia.etherscan.io/address/0x69c7b902d29be3b516f42fba4e919f4c17a310fd\r\nContract verification status:\r\nResponse: `NOTOK`\r\nDetails: `Pending in queue`\r\nContract verification status:\r\nResponse: `OK`\r\nDetails: `Pass - Verified`\r\nContract successfully verified\r\nAll (1) contracts were verified!\r\n\r\nTransactions saved to: /Users/taylor/Documents/Developer/web3-src/hello_foundry/broadcast/DeployMyToken.s.sol/11155111/run-latest.json\r\n\r\nSensitive values saved to: /Users/taylor/Documents/Developer/web3-src/hello_foundry/cache/DeployMyToken.s.sol/11155111/run-latest.json\r\n```\r\n### 6-验证合约 ###\r\n![111.png](https://img.learnblockchain.cn/attachments/2024/07/mfPAiz8d669153736b3df.png)\r\n-----\r\n本次教程成功部署并验证的合约地址：\r\nhttps://sepolia.etherscan.io/address/0x69c7b902d29be3b516f42fba4e919f4c17a310fd\r\n\r\n### 7-总结 ###\r\n本次教程所有代码记录在我个人github的repo中：https://github.com/TaylorDurden/hello_foundry/pull/2"},"author":{"user":"https://learnblockchain.cn/people/20024","address":null},"history":"bafkreigv55evyggwhdhzc3yuwieikmexs2xtp6bcgc3mumm3tqraw4422a","timestamp":1720801663,"version":1}