主要学习了解如何写一个合约,如何在前端进行合约交互,以一段最简单的合约代码学习合约交互的流程
我们知道以太坊合约代码是用solidity
语言来写的,我们使用hardhat
初始化一个编写合约开发环境文档参考[1],以下是一篇学习笔记,希望看完有所帮助
npx hardhat init
我们在contracts
文件夹下新建一个Count.sol
合约代码
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
// 定义了一个Hello合约
contract Hello {
// 申明公有变量
string public name = "Hello World";
uint public count = 0;
function increment() public {
count++;
}
function desc() public {
count--;
}
function get() public view returns (uint) {
return count;
}
}
当我们写好合约后,我们需要编译合约,执行npx hardhat compile
,当我们执行命令后,会生成一个artifacts
存储合约编译后的文件,这个abi
数据映射的就是合约代码,在前端调用需要这个abi
数据
{
"_format": "hh-sol-artifact-1",
"contractName": "Hello",
"sourceName": "contracts/Count.sol",
"abi": [
{
"inputs": [],
"name": "count",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "desc",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "get",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "increment",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "name",
"outputs": [
{
"internalType": "string",
"name": "",
"type": "string"
}
],
"stateMutability": "view",
"type": "function"
}
],
...
}
当我们编译好本地合约,我们需要部署合约,因此我们开启本地
npx hardhat node
Started HTTP and WebSocket JSON-RPC server at http://127.0.0.1:8545/
Accounts
========
WARNING: These accounts, and their private keys, are publicly known.
Any funds sent to them on Mainnet or any other live network WILL BE LOST.
Account #0: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 (10000 ETH)
Private Key: 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
我们在script
目录下新建一个deploy.js
// scripts/deploy.js
const hre = require("hardhat");
asyncfunction main() {
const Hello = await hre.ethers.getContractFactory("Hello");
const hello = await Hello.deploy();
console.log("Hello deployed to:", hello.target);
}
main().catch((error) => {
console.error(error);
process.exitCode = 1;
});
部署合约,执行hardhat run scripts/deploy.js --network localhost
,会生成一个部署的合约地址contractAddress
work localhost
Hello deployed to: 0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512
我们已经编译并部署了合约,如何在前端调用呢
首先我们需要把本地rpc
添加到metaMask
中,就是我们之前运行的本地rpc
然后我们将npx hardhat node
本地运行生成的测试账号,倒入一个私钥到钱包里就行
基础的准备工作已经完成
在前端里,我们需要读取合约,以及调用合约方法
<div className="app">
<h1>Hello Contract</h1>
<p onClick={handleWalletConnect}>Connect Wallet</p>
<button onClick={handleDiscontent}>discontent wallet</button>
<p>address:{address}</p>
<p>Count: <span id="count">{count}</span></p>
<button onClick={getCount}>Get</button>
<button onClick={increment}>Increment</button>
<button onClick={desc}>Decrement</button>
</div>
import {ethers, BrowserProvider, Contract} from "ethers";
...
const handleWalletConnect = async () => {
await window.ethereum.request({ method: "eth_requestAccounts" });
const provider = new BrowserProvider(window.ethereum);
const signer = await provider.getSigner();
const address = await signer.getAddress();
console.log(address, "===address");
setAddress(address);
}
获取Contract
provider
signer
new Contract(contractAddress, abi, signer)
import {ethers, BrowserProvider, Contract} from "ethers";
const provider = new BrowserProvider(window.ethereum);
const signer = await provider.getSigner();
const contract = 以下是部分调用合约代码 Contract(contractAddress, abi, signer);
以下是部分调用合约带哪
import {ethers, BrowserProvider, Contract} from"ethers";
...
const [count, setCount] = useState(0);
const contractRef = useRef(null);
const [address, setAddress] = useState("")
const isWalletAuthorized = async () => {
const accounts = awaitwindow.ethereum.request({ method: "eth_accounts" });
return accounts.length > 0;
};
const checkIfWalletConnected = async () => {
try {
if (isWalletAuthorized()) {
const provider = new BrowserProvider(window.ethereum);
const signer = await provider.getSigner();
const address = await signer.getAddress();
setAddress(address);
const contract = new Contract(contractAddress, abi, signer);
contractRef.current = contract;
await getCount();
}
} catch (err) {
console.warn("检查钱包失败", err);
}
};
const getCount = async () => {
const count = await contractRef.current.get();
console.log(count);
setCount(count);
}
useEffect(() => {
checkIfWalletConnected();
}, [])
当我们遇到这种错误时,因为其他插件影响,关闭okx
插件钱包,只留metaMask
。这种错误是由于okx插件修改了window.ethereum
冲突造成的。
VM66 inpage.js:1 w[0].read is not a function RPC Stack: -> wallet_requestUnlockUI -> wallet_requestPermissions - in wallet_requestUnlockUI [ServerError -32000]
1. {code: -32000, message: 'w[0].read is not a function\nRPC Stack:\n-> wallet_r…- in wallet_requestUnlockUI [ServerError -32000]\n'}
当我们查询合约的count
,调用就是合约的get
方法
const getCount = async () => {
const count = await contractRef.current.get();
console.log(count);
setCount(count);
}
当我们调用increment
...
const increment = async () => {
const tx = await contractRef.current.increment();
await tx.wait();
await getCount();
}
时就会打开metaMask钱包发送交易请求,我们看到会产生交易请求increment
时就会打开metaMask钱包发送交易请求,我们会看到会产生交易请求
至此与链上合约交户就完成了
hardhat
官方文档写一个简单的测试合约代码编译
并部署
本地合约参考资料
[1]
文档参考: https://hardhat.org/hardhat-runner/docs/getting-started#overview
[2]
code example: https://github.com/maicFir/SolidityLesson/tree/master/17-test