下载 & 安装
使用线上remix的缺点:
1、只能处理智能合约,不能真正的与项目的其他部分进行集成
2、对测试和定制部署的支持有限,需要保持联网
一种更为专业的智能合约开发者配置:
hardhat,智能合约开发框架,基于javaScript的编译环境,部署,测试,bug调试
如何使用ether.js ?
它是一个基于js的库,用于编写智能合约,hardhat的底层使用了很多ether.js的东西
下载Visual Studio Code node.js
使用工具让Windows环境可以向Linux那样工作,因为Linux是大多数开发环境的标准
wsl表示Windows的Linux子系统,可以让Linux程序在Windows环境的本机下运行
安装wsl:https://learn.microsoft.com/en-us/windows/wsl/install
扩展:Remote Development
下载
ctrl + shift + p
,输入wsl,点击connect to wsl,然后在打开终端
使用Code .
打开文件夹或者文件
安装node.js
z-jiawei@DESKTOP-84O604F:~/folder$ curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0 curl: (7) Failed to connect to 127.0.0.1 port 7890 after 0 ms: Connection refused DNS污染 199.232.68.133 raw.githubusercontent.com 199.232.68.133 user-images.githubusercontent.com 199.232.68.133 avatars2.githubusercontent.com 199.232.68.133 avatars1.githubusercontent.com 也不能解决的话,可以根据这个安装 https://blog.csdn.net/weixin_44786530/article/details/134263497 ===> 很方便
添加扩展,提供语法高亮:
设置VS Code在我们保存的时候自动格式化
(1)view - command open User settings(JSON)
// 表示任何时候我们格式化代码的时候,都会使用hardhat-solidity默认的格式化程序 "[solidity]": { "editor.defaultFormatter": "NomicFoundation.hardhat-solidity" }
(2)添加在保存时格式化:
open settings(UI)
打钩 format on save
使用js部署合约
(1)如何使用JavaScript和Node.js运行一个脚本
deploy.js中写入console.log("hi!")
,使用node.js执行JavaScript
JavaScript会自动从文件的顶部的代码开始执行
当把操作封装进函数后,node deploy.js
是不会有任何响应的
//可以通过这样执行 function main() { console.log("hi!"); let variable = 5; console.log(variable);//javaScript允许选择是否带上分号 } main();
异步函数(代码同时执行):
async function main() {//await console.log("hi!"); let variable = 5; console.log(variable);//javaScript允许选择是否带上分号 } main();
在remix中我们首先要做的就是编译代码,为了编译我们的合约,我们使用solc-js工具,能编译通过相对路径导入其他合约的合约
yarn package manager
,安装yarn作为总的包管理工具
官网:yarnpkg.com/getting-started
使用corepack enable
接下来就可以通过yarn
来安装所有项目了,而不是npm
package.json
:是一个描述我们的项目以及它的依赖关系的文件
yarn.lock
:告诉我们项目所使用的不同包的确切版本,自动生成的文件,不要直接编译
node_moudles
:我们下载的所有代码
我们使用的solidity的版本是:
pragma solidity ^0.8.19;
在package.json
中可以看到:
solc
的依赖部分变成了0.8.19
,yarn
既可以安装依赖库,也可以运行脚本
接下来就可以编译合约了
yarn solcjs --bin --abi --include-path node_modules/ --base-path . -o . SimpleStorage.sol
--bin --abi
:想要的是二进制文件和ABI
include-path node_moudles/
:想包含node_modules中的所有智能合约或文件
--base-path .
:表示其实路径是当前这个文件夹
-o .
:表示编译出来的二进制文件和ABI也输出到这个文件夹
简化编译:
在package.json
中添加script
部分,缩短运行脚本时的命令长度
"scripts": { "compile": "yarn solcjs --bin --abi --include-path node_modules/ --base-path . -o . SimpleStorage.sol" } //使用yarn compile 进行编译
Ganache:
未来将使用hardhat运行时环境作为我们的JavaScript虚拟机
trufflesuite.com/ganache/
快速启动个人以太坊区块链,您可以使用它来运行测试、执行命令和检查状态,同时控制链的运行方式
下载:
github加速:https://github.ur1.fun/
https://github.com/trufflesuite/ganache-ui/releases/download/v2.7.1/Ganache-2.7.1-win-x64.appx
点击quick start 就可以快速的启动一个假的区块链网路
在代码中,我们首先要做的就是连接到区块链上,在MetaMask的network中的RPC URL使得我们能够连接到测试网络
http://127.0.0.1:7545
,有了这个端点后,就可以对这个端点进行API调用了
提示:http://127.0.0.1:7545
需要在软件中更改:
点击switch -- -- server,更改成wsl
https://ethereum.org/en/developers/docs/apis/json-rpc
ethers.js介绍
最流行的JavaScript工具包之一,能够让我们与不同的区块链进行交互,有许多封装函数可以进行API调用,可以在一切兼容EVM的区块链山使用
还有一个非常受欢迎的包web3.js,我们使用ether.js是因为它是支撑hardhat环境的主要工具
yarn add ethers
把ethers导入脚本deploy.js
const ethers = require("ethers");
//在调用main函数之前就把包放入脚本中 //Require 负责导入ethers包 const fs = require("fs-extra"); const ethers = require("ethers"); async function main() { const provider = new ethers.JsonRpcProvider("http://172.28.128.1:7545"); //获取钱包 const wallet = new ethers.Wallet( "0xb9e9c69de4f038566a57220c7b27a4b5f95f725ab272f989b1affa22cd7a5d3b", provider ); const abi = fs.readFileSync("./SimpleStorage_sol_SimpleStorage.abi", "utf8"); const binary = fs.readFileSync("./SimpleStorage_sol_SimpleStorage.bin", "utf8"); //创建合约工厂,只是一个用来部署合约的对象 const contractFactory = new ethers.ContractFactory(abi, binary, wallet); //abi 我们的代码知道如何和合约进行交互 //bin 编译后的代码 //wallet 提供了一个私钥使得我们能用它来签名并部署这个合约 console.log("Deploy...... wait"); const contract = await contractFactory.deploy(); // 要等待合约部署完成 console.log(contract); } main() .then(() => process.exit(0)) .catch((error) => { console.error(error); process.exit(1); });
结果:
其他区块详细信息可以在ganache
中查看
await
等待contractFactory
退出pending
状态,并返回promise
在pending
后返回的任何东西
如果想在await contractFactory.deploy
中指定gas价格,可以在deploy函数中添加一些overrides
const contract = await contractFactory.deploy({gasPrice: 1000000000});
交易回执:
//更改如下 console.log("Deploy...... wait"); const contract = await contractFactory.deploy({ gasPrice: 1000000000 }); // 要在这里厅下载,等待合约部署完成 const deploymentReceipt = await contract.deploymentTransaction().wait(1);//想要等待确认的确切数量 console.log(deploymentReceipt); //会返回交易的精确信息
to
:null,因为我们正在部署合约
from
:表示我们使用的私钥对应的ganache地址
contractAddress
:我们创建的这个合约的合约地址
区分开deploymentReceipt
和contract.deployTransaction
只有在等待区块确认完成时才会获得交易回执(receipt
),否则只能得到合约对象及其包含的部署交易(deploymentTransaction)信息
const deploymentReceipt = await contract.deploymentTransaction().wait(1);
通过ether.js发送_raw_
交易:
自己创建一个交易,并通过制定交易信息自己创建一个合约
要把networkID更改成1337
//在调用main函数之前就把包放入脚本中 //Require 负责导入ethers包 const fs = require("fs-extra"); const ethers = require("ethers"); async function main() { const provider = new ethers.JsonRpcProvider("http://172.28.128.1:7545"); //获取钱包 const wallet = new ethers.Wallet( "0xb9e9c69de4f038566a57220c7b27a4b5f95f725ab272f989b1affa22cd7a5d3b", provider ); const abi = fs.readFileSync("./SimpleStorage_sol_SimpleStorage.abi", "utf8"); const binary = fs.readFileSync("./SimpleStorage_sol_SimpleStorage.bin", "utf8"); //创建合约工厂,只是一个用来部署合约的对象 // const contractFactory = new ethers.ContractFactory(abi, binary, wallet); //abi 我们的代码知道如何和合约进行交互 //bin 编译后的代码 //wallet 提供了一个私钥使得我们能用它来签名并部署这个合约 // console.log("Deploy...... wait"); // const contract = await contractFactory.deploy({ gasPrice: 1000000000 }); // 要在这里厅下载,等待合约部署完成 // const deploymentReceipt = await contract.deploymentTransaction().wait(1);//想要等待确认的确切数量 console.log("Let's deploy with only transaction data !"); //仅使用交易数据发送交易 const nonce = await wallet.getNonce(); console.log("nonce的值是多少 ?", nonce) const tx = { //nonce也用于钱包中给交易签名 nonce: nonce, gasPrice: 20000000000, gasLimit: 1000000, to: null, value: 0, //将存储一个很大的二进制对象,二进制代码告诉区块链如何部署这个智能合约 data: "0x",//就是合约的二进制文件,前面加上0x chainId: 1337, };//交易未签名,给交易签名,然后发送到区块链上 // const signedTxResponse = await wallet.signTransaction(tx); // console.log(signedTxResponse); const sendTxResponse = await wallet.sendTransaction(tx); await sendTxResponse.wait(1); console.log(sendTxResponse); } main() .then(() => process.exit(0)) .catch((error) => { console.error(error); process.exit(1); });
在sendTransaction
中我们已经完成可对交易的签名操作:
1、首先会做一些检查提供者的事情
2、在发送交易之前,进行签名交易
3、然后发送交易
通过ether.js与合约进行交互
ABI不易阅读,可以将后缀改为json,搜索format document
格式化代码
const fs = require("fs-extra"); const ethers = require("ethers"); async function main() { const provider = new ethers.JsonRpcProvider("http://172.28.128.1:7545"); const wallet = new ethers.Wallet( "0xb9e9c69de4f038566a57220c7b27a4b5f95f725ab272f989b1affa22cd7a5d3b", provider ); const abi = fs.readFileSync("./SimpleStorage_sol_SimpleStorage.abi", "utf8"); const binary = fs.readFileSync("./SimpleStorage_sol_SimpleStorage.bin", "utf8"); const contractFactory = new ethers.ContractFactory(abi, binary, wallet); console.log("Deploy...... wait"); const contract = await contractFactory.deploy(); const deploymentReceipt = await contract.deploymentTransaction().wait(1); const currentFavoriteNumber = await contract.retrieve(); console.log(currentFavoriteNumber.toString()); const transactionResponse = await contract.store("7"); const transactionReceipt = transactionResponse.wait(1); const updateFavoriteNumber = await contract.retrieve(); console.log(updateFavoriteNumber.toString()); } main() .then(() => process.exit(0)) .catch((error) => { console.error(error); process.exit(1); });
加上toString( );
console.log('current Favorite Number : ${currentFavoriteNumber.toString()}');
使用反引号
环境变量:
不能将私钥硬编码到我们的代码中
创建一个.env
文件,输出环境变量使用echo $CAT
PRIVATE_KEY=0xb9e9c69de4f038566a57220c7b27a4b5f95f725ab272f989b1affa22cd7a5d3b
想要获取环境变量,并粘贴到脚本中,需要一个dotenv
的工具
yarn add dotenv
npmjs.com/package/dotenv
存在以下问题:
info There appears to be trouble with your network connection. Retrying..
问题的解决:
https://blog.csdn.net/weixin_42132439/article/details/127861199
代码:
require("dotenv").config(); const wallet = new ethers.Wallet(process.env.PRIVATE_KEY, provider);
创建.gitignore
文件,上传到github时不上传以下两个:
.env node_modules
私钥管理:
可以在命令行中这样写,不写在.env
中
可以把私钥本地加密,然后把加密后的密钥放在本地,创建一个encryptKey.js
,用来加密密钥
const fs = require("fs-extra"); const ethers = require("ethers"); require("dotenv").config(); async function main() { const wallet = new ethers.Wallet(process.env.PRIVATE_KEY); // 密钥密码和密钥 const encryptedJsonKey = await wallet.encryptSync( process.env.PRIVATE_KEY_PASSWORD, process.env.PRIVATE_KEY ); console.log(encryptedJsonKey); // 将加密后的私钥放在.encryptedKey.json中,添加在.ignore中 fs.writeFileSync("./.encryptedKey.json", encryptedJsonKey); } main() .then(() => process.exit(0)) .catch((error) => { console.error(error); process.exit(1); });
这样就可以在env中删掉私钥还有我们的私钥密码
const provider = new ethers.JsonRpcProvider(process.env.RPC_URL); //获取钱包 // const wallet = new ethers.Wallet(process.env.PRIVATE_KEY, provider); const encryptedJson = fs.readFileSync("./.encryptedKey.json", "utf8");//利用加密的密钥创建一个钱包 console.log(encryptedJson); // new 加上报错 let wallet = ethers.Wallet.fromEncryptedJsonSync( encryptedJson, process.env.PRIVATE_KEY_PASSWORD ); // 将钱包和privider连接起来,当我们的合约工厂进行交易的时候,确保wallet知道privider wallet = await wallet.connect(provider); const abi = fs.readFileSync("./SimpleStorage_sol_SimpleStorage.abi", "utf8"); const binary = fs.readFileSync("./SimpleStorage_sol_SimpleStorage.bin", "utf8");
histoy
可以查看我们的历史记录,可以使用history -C
清空历史记录
在测试网和主网上部署合约
只需要RPC_URL和一个私钥就可以在区块链网络上开始交易
alchemy.com
Alchemy具有节点即服务的功能,允许我们连接到他们支持的任何区块链,其他的备选方案有QuickNode、INfura、Morales
google账号注册参考:
https://kerrynotes.com/proxy/
https://kerrynotes.com/register-google-account/
1、Alchemy
使用Google账号登录的话,会有一层辅助验证,向账号注册绑定的手机号发送验证码,我一直收不到
2、正常注册的话,会一直卡在vertify to continue
不出来验证信息
视频推荐用的是Alchemy
,注册不上,改用quicknode.com
,存在绑定银行卡的操作
最后选择了INFURA
根据官网的操作创建API密钥:
https://docs.infura.io/getting-started?_ga=2.41864032.394649363.1705835805-1850825774.1705835795
复制Sepolia中的HTTP到我们项目的.env
文件中替换掉RPC _URL
在MetaMask中导出Sepolia网络中账户的私钥
如果不足的话,facuts.chain.link
搞一些测试用
https://sepolia.etherscan.io/
区块浏览器
合约地址:0xe203397303545E98Beafd9ec9A8EB920e969A573
参考:https://www.jianshu.com/p/1e688582ef44
https://docs.ethers.org/v6/
代码:
const fs = require("fs-extra"); const ethers = require("ethers"); require("dotenv").config(); async function main() { const provider = new ethers.JsonRpcProvider(process.env.RPC_URL); //获取钱包 const wallet = new ethers.Wallet(process.env.PRIVATE_KEY, provider); // const encryptedJson = fs.readFileSync("./.encryptedKey.json", "utf8");//利用加密的密钥创建一个钱包 const abi = fs.readFileSync("./SimpleStorage_sol_SimpleStorage.abi", "utf8"); const binary = fs.readFileSync("./SimpleStorage_sol_SimpleStorage.bin", "utf8"); //创建合约工厂,只是一个用来部署合约的对象 const contractFactory = new ethers.ContractFactory(abi, binary, wallet); //abi 我们的代码知道如何和合约进行交互 //bin 编译后的代码 //wallet 提供了一个私钥使得我们能用它来签名并部署这个合约 console.log("Deploy...... wait"); const contract = await contractFactory.deploy(); // 要在这里厅下载,等待合约部署完成 const deploymentReceipt = await contract.deploymentTransaction().wait(1);//想要等待确认的确切数量 console.log(`合约地址:${contract.address}`)//不行,一直是undefine,但是可以复制公钥在区块浏览器中看到所有的交易记录 const currentFavoriteNumber = await contract.retrieve(); console.log(currentFavoriteNumber.toString()); const transactionResponse = await contract.store("7"); await transactionResponse.wait(1); const updateFavoriteNumber = await contract.retrieve(); console.log(updateFavoriteNumber.toString()); }
结果:
交易记录:
通过浏览器验证合约
如果有人想在链上查看我们的合约,它们只能看见字节码,通过反编码器尝试将字节码反编译成solidity代码
点击verify and public
,选择编译器、solidity的版本和开源许可证,点击continue
将solidity的代码复制粘贴放到页面上,成功后,就可以在contract Source Code
看到solidity代码了
如果将合约地址放入搜索框中,搜索后,点击contract,就可以看到合约代码,读合约和写合约
仪表盘和内存池
每个节点除了有区块链节点的副本外,还有一个本地交易的内存,就是内存池
等待被打包的交易,可以认为他们在交易池中