一、demo简介
demo源码 html-fund-me-fcc,一个前端网站 连接到区块连上并调用 合约函数。
到这里,就基本掌握了web3 区块链全栈开发的基本技术!
钱包
MetaMask安装后会注入window.ethereum
类似 phantom钱包会注入 window.solana
节点
钱包在内部构造中,会与一个区块链节点相连接。
为了与区块链进行交互,我们始终都需要节点。
一般用 alchemy 、infura 租用节点。
需要创建一个provider或者说 一个节点,才能发送交易。
二、源码调试
(一)启动网页(推荐)
1)安装vscode插件:live server
安装完,vscode右下角会有个按钮 Go Live,
点击 启动server,然后该按钮会变成 port:5500,
点击 关闭server。
新建文件夹 .vscode,新建配置文件settings.json如下:
{
"liveServer.settings.port":5502
}
2)node httpServer
#安装
yarn add --dev http-server
#启动, 缺点:不能自动刷新
yarn http-server
(二)连接钱包
index.html 中 引入 index.js 时,type="module" 可以允许我们在代码中导入模块。
如:import { ethers } from "./ethers-5.6.esm.min.js"<script src="./index.js" type="module"></script>
此时按钮事件绑定要放到 index.js里进行。
index.js中,判断window.ethereum对象(钱包插件)存在,才去连接,并且打印账号。
参考MetaMask官方文档 https://docs.metamask.io/wallet/how-to/connect/access-accounts/
async function connect() {
if (typeof window.ethereum !== "undefined") {
try {
await ethereum.request({ method: "eth_requestAccounts" })
} catch (error) {
console.log(error)
}
connectButton.innerHTML = "Connected"
const accounts = await ethereum.request({ method: "eth_accounts" })
console.log(accounts)
} else {
connectButton.innerHTML = "Please install MetaMask"
}
}
点击connect按钮,钱包弹出如下:
同意连接后,网站九可以对MetaMask进行api调用。
控制台也打印出 账户地址。
(三)fund函数
注意: 这里前端js 导入 用import, nodejs 用require。
浏览器导入ethers https://docs.ethers.org/v5/getting-started/#importing
1)源码
async function fund() {
const ethAmount = document.getElementById("ethAmount").value
console.log(`Funding with ${ethAmount}...`)
if (typeof window.ethereum !== "undefined") {
//import { ethers } from "./ethers-5.6.esm.min.js"
//用于连接区块链
// "Web3Provider"是一个 ethers 中的对象,它允许我们附在类似 MetaMask 这样的东西上。
// Web3Provider 接收MetaMask中的RPC_URL http端点,并自动将其导入到ethers中。
// (类似 JsonRpcProvider: 我们放入端点的地方,比如 Alchemy端点)
//
const provider = new ethers.providers.Web3Provider(window.ethereum)
//用户/签署者/钱包,用于支付gas
//比如 account1
const signer = provider.getSigner()
//合约地址,合约abi,
const contract = new ethers.Contract(contractAddress, abi, signer)
try {
const transactionResponse = await contract.fund({
value: ethers.utils.parseEther("0.1"),
})
await listenForTransactionMine(transactionResponse, provider)
console.log("done")
} catch (error) {
// 如果用户 reject Metamask的交易弹窗,则走这里
console.log(error)
}
} else {
fundButton.innerHTML = "Please install MetaMask"
}
}
2)配置constants.js
合约地址,合约abi 都写在在 constants.js里。
1.abi:
从 之前 hardhat-fund-me项目中artifacts-contracts-FundMe.sol-FundMe.json 可以拿到abi信息,
放到constants.js
2.合约地址:
1.hardhat-fund 项目 启动节点: yarn hardhat node。
hardhat-deploy插件这个时候会自动执行 deploy'目录下的所有脚本,
完成chainlinkmock以及部署。
2.部署成功的信息里找到合约地址。
Deploying FundMe and waiting for confirmations...
deploying "FundMe" (tx: 0xdd379d5ecb28596d592ce69278734d72761c3c8d3187ae65f27e9a3408ba83d1)...: deployed at 0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512 with 1058635 gas
FundMe deployed at 0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512
3)MetaMask连接本地节点
metaMask默认设置里 网络只有 几个主网 和测试网,添加本地网络并切换连接到本地网络。
此时会发现账户在本地网络上没有钱。
4)MetaMask导入Hardhat账户
从上个步骤 复制 account0 的privateKey,进行导入。
导入完 MetaMask 多了个account3,余额 9999.9967ETH。
5)点击fund按钮
metamask弹出交易 申请窗口,同意即可完成。
(四)监听交易挖掘
function listenForTransactionMine(transactionResponse, provider) {
console.log(`Mining ${transactionResponse.hash}`)
return new Promise((resolve, reject) => {
try {
// 官方文档https://docs.ethers.org/v5/api/providers/provider/#Provider-once
provider.once(transactionResponse.hash, (transactionReceipt) => {
console.log(`Completed with ${transactionReceipt.confirmations} confirmations. `)
resolve()
})
} catch (error) {
reject(error)
}
})
}
provider.once:只触发一次事件。触发一次后,监听器移除。
provider.on:每次事件都触发。
执行fund,可以看到网页控制台如下输出:
Mining 0x8b2127d00e8ff5bafa0e7a239d6af36a19871c3cce2574e2983c7a561385deb0
Completed with 1 confirmations.
done
(五)ethers监听交易和事件
todo
(六)读取区块链数据
读取合约当前 金额。
async function getBalance() {
if (typeof window.ethereum !== "undefined") {
const provider = new ethers.providers.Web3Provider(window.ethereum)
try {
const balance = await provider.getBalance(contractAddress)
console.log(ethers.utils.formatEther(balance))
} catch (error) {
console.log(error)
}
} else {
balanceButton.innerHTML = "Please install MetaMask"
}
}
(七)withdraw函数
async function withdraw() {
console.log(`Withdrawing...`)
if (typeof window.ethereum !== "undefined") {
const provider = new ethers.providers.Web3Provider(window.ethereum)
await provider.send("eth_requestAccounts", [])
const signer = provider.getSigner()
const contract = new ethers.Contract(contractAddress, abi, signer)
try {
const transactionResponse = await contract.withdraw()
await listenForTransactionMine(transactionResponse, provider)
//等待一个区块进行写入
// await transactionResponse.wait(1)
} catch (error) {
console.log(error)
}
} else {
withdrawButton.innerHTML = "Please install MetaMask"
}
}
三、FAQ
(一)、hardhat node 重启后,hardhat控制台报错。
问题:
Received invalid block tag 4. Latest block number is 3
-----------------------------------------------------------
解决:
切换 Metamask网络到其它,再切回localhost,此时控制台不会再报错。
https://ethereum.stackexchange.com/questions/109625/received-invalid-block-tag-87-latest-block-number-is-0
但是会遇到下面第2点问题。
(二)、fund调用报错。
问题1: 网页控制台报错如下:
{code: -32603, message: 'Internal JSON-RPC error.'}
-----------------------------------------------------------------------
问题2:hardhat控制台报错如下:
eth_sendRawTransaction
Nonce too high. Expected nonce to be 3 but got 4. Note that transactions can't be queued when automining.
eth_call
Contract call: FundMe#<unrecognized-selector>
From: 0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266
To: 0xe7f1725e7734ce288f8367e1bb143e90bb3f0512
Error: Transaction reverted: function selector was not recognized and there's no fallback function
at FundMe.<unrecognized-selector> (contracts/FundMe.sol:16)
-----------------------------------------------------------------------
原因: hardhat重启后知道自己要刷新,要从0开始。
但是MetaMask却不知道。
-----------------------------------------------------------------------
解决:MetaMask重置账户:进入 MetaMask -> 设置->高级-> 清除活动和nonce数据,即可解决。