惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

M
MIT News - Artificial intelligence
罗磊的独立博客
Hugging Face - Blog
Hugging Face - Blog
Apple Machine Learning Research
Apple Machine Learning Research
Last Week in AI
Last Week in AI
S
SegmentFault 最新的问题
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
美团技术团队
人人都是产品经理
人人都是产品经理
WordPress大学
WordPress大学
The Cloudflare Blog
IT之家
IT之家
雷峰网
雷峰网
小众软件
小众软件
博客园 - 叶小钗
博客园 - 聂微东
爱范儿
爱范儿
博客园 - 司徒正美
博客园 - 三生石上(FineUI控件)
V
Visual Studio Blog
博客园 - 【当耐特】
V
V2EX
博客园_首页
T
Tailwind CSS Blog

pseudoyu

周报 #106 - OpenClaw 实践、搬家与生活的能量 周报 #105 - 打破无聊的社会 2025 年末回顾 - Anything different is good 周报 #104 - 被遗忘的生活一隅 周报 #103 - 工作随想与被吞噬的生活 周报 #102 - 我是如何使用 AI 的 周报 #101 - 消失了三个月,我的生活发生了什么变化 周报 #100 - 过去这十年 周报 #99 - 离开 RSS3,迷茫与重启人生 周报 #98 - 生活之书与 Agentic Coding 周报 #97 - 日本旅行小记 28 岁的自白:去找寻自己 周报 #96 - 基于 Roo Code/Cursor 的 iOS 开发工作流 周报 #95 - All AI 与 No AI 周报 #94 - 生活的界限与真实感 周报 #93 - 婚礼纪录片与失落的电影院 周报 #92 - 数码产品 Porn 与断舍离 周报 #91 - 一席、消失的时代与自由意志 周报 #90 - OVH 服务器、Coolify 与对 AI Coding 的思考 周报 #89 - 博客改版与 TheWan App 发布 周报 #88 - 方大同、《回留》与人生清单 周报 #87 - 我们主持了自己的宝可梦婚礼,还为它办了一个展 周报 #86 - 用影像打败时间 周报 #85 - 京华烟云 Moment in Peking 周报 #84 - 想起他们 Echos of Reverie 周报 #83 - 年初收纳(房间、设备、服务、软件) 周报 #82 - Brand New Year 与写日记 2024 年末回顾 - 不完美的日子 周报 #81 - Engaged -> Married 与时间的尺度 周报 #80 - 关于求婚、爱情与婚姻
Solidity 智能合约开发 - 玩转 ethers.js
pseudoyu · 2022-06-07 · via pseudoyu

前言

在之前的《Solidity 智能合约开发 - 基础》中,我们学习了 Solidity 的基本语法,并且了解了可以通过 BrownieHardHat 等框架进行调试。而另一篇《Solidity 智能合约开发 - 玩转 Web3.py》中我们也通过 Web3.py 直接与我们本地的 Ganache 节点进行交互了。

原本因为之前比较熟悉 Python 的使用,所以想使用 Brownie 框架进行后续开发。然而经过了一番调研,业界还是使用 HardHat 框架居多,也有更多拓展,且我关注的 Solidity 教程也更新了 Javascript 版本,于是还是打算学习一下。

为了更好了解其原理,也为我们后续更好使用框架打好基础,我们这次通过 ethers.js 来与我们部署在 Alchemy 平台上的 Rinkeby 测试网络进行交互。实现了基础的合约编译、部署至 Rinkeby 网络、与合约交互等功能。

可以点击这里访问本测试 Demo 代码仓库。

ethers.js

ethers.js 是 Javascript 的一个开源库,可以与以太坊网络进行交互,其 GitHub 地址为 ethers.io/ethers.js,可以访问其官方文档进行使用。

安装

我们可以通过 yarn 安装 ethers.js,如下:

yarn add ethers

yarn_add_ethers

使用

使用 require 导入库即可使用

const ethers = require('ethers')

Solidity 合约编译

合约源码

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.7;

contract SimpleStorage {
    uint256 favoriteNumber;
    bool favoriteBool;

    struct People {
        uint256 favoriteNumber;
        string name;
    }

    People public person = People({favoriteNumber: 2, name: "Arthur"});

    People[] public people;

    mapping(string => uint256) public nameToFavoriteNumber;

    function store(uint256 _favoriteNumber) public returns (uint256) {
        favoriteNumber = _favoriteNumber;
        return favoriteNumber;
    }

    function retrieve() public view returns (uint256) {
        return favoriteNumber;
    }

    function addPerson(string memory _name, uint256 _favoriteNumber) public {
        people.push(People({favoriteNumber: _favoriteNumber, name: _name}));
        nameToFavoriteNumber[_name] = _favoriteNumber;
    }
}

这是一个简单的存储合约,通过一个 People 结构体对象来存储人名和他喜欢数字,通过一个数组来存储多个人的信息,并提供了添加、查找方法。

读取合约源文件

当我们通过 VSCode 或其他编辑器完成 Solidity 合约编写与语法检查后,需要编译合约为 abi 文件与 bytecode。

我们可以通过 yarn 安装 solc 命令行工具进行编辑,并且可以选择对应版本,命令如下:

yarn add [email protected]

安装完成后,,我们可以通过 solcjs 命令来进行编译,命令如下:

yarn solcjs --bin --abi --include-path node_modules/ --base-path . -o . SimpleStorage.sol

因为编译合约是一个高频操作,我们可以在 package.json 中配置 compile 脚本命令,如下:

{
  "scripts": {
    "compile": "yarn solcjs --bin --abi --include-path node_modules/ --base-path . -o . SimpleStorage.sol"
  }
}

之后仅需执行 yarn compile 即可生成合约编译文件。

获取编译结果

编译完成后会生成 abi 和 bytecode 文件,分别以 .bin.abi 为后缀。

获取 bytecode 与 abi

Solidity 合约的部署与交互需要 bytecode 与 abi 两个部分,我们可以通过通过以下代码将其写入对应变量供后续操作使用。

const fs = require('fs-extra')

const abi = fs.readFileSync('./SimpleStorage_sol_SimpleStorage.abi', 'utf-8')
const binary = fs.readFileSync('./SimpleStorage_sol_SimpleStorage.bin', 'utf-8')

创建 Rinkeby 测试网络环境(Alchemy)

智能合约的调试需要将合约部署到实际的链上,我们选择部署到 Alchemy 平台的 Rinkeby 测试网进行后续调试开发,

Alchemy 平台

首先我们访问 Alchemy 官网,注册并登录,会看到其 Dashboard,会展示所有已创建的应用。

alchemy_dashboard

安装完成后选择 Create App 即可快速创建一个 Rinkeby 测试网络节点。

alchemy_create_app

创建完成后,点击 View Details,可以看到我们刚创建的 App 详细信息,点击右上角 View Key,可以查询我们的节点信息,我们需要记录下 HTTP URL,供后续连接使用。

alchemy_app_detail

MetaMask

完成了 Rinkeby 测试网络环境的创建,我们需要通过 MetaMask 创建账户,获取一些测试 Token,并且将账户私钥记录下来,以便后续使用。

metamask_private_key

获取测试 Token

创建账户后,我们需要一些测试 Token 来进行后续开发调试,我们可以通过以下网址获取:

连接测试节点与钱包

连接节点

ethers.js 提供了库可以方便地连接到我们的测试节点,其中 process.env.ALCHEMY_RPC_URL 为我们在 Alchemy 平台创建 App 的 HTTP URL:

const ethers = require('ethers')

const provider = new ethers.providers.JsonRpcProvider(process.env.ALCHEMY_RPC_URL)

连接钱包

ethers.js 也提供了方法可以连接到我们的测试钱包,其中 process.env.RINKEBY_PRIVATE_KEY 为我们从 MetaMask 复制的私钥。

const ethers = require('ethers')

const wallet = new ethers.Wallet(
  process.env.RINKEBY_PRIVATE_KEY,
  provider
)

Solidity 合约部署

创建合约

我们可以通过 ethers.js 库创建合约。

const contractFactory = new ethers.ContractFactory(abi, binary, wallet)

部署合约

下面我们介绍一下如何通过 ethers.js 库部署合约,其中 SimpleStorage 合约的 ABI 和 BIN 文件已经在上面的代码中读取过了。

创建合约

const contractFactory = new ethers.ContractFactory(abi, binary, wallet)

部署合约

const contract = await contractFactory.deploy()
await contract.deployTransaction.wait(1)

与合约交互

我们也可以通过 ethers.js 来与合约进行交互。

retrieve()

const currentFavoriteNumber = await contract.retrieve()

store()

const transactionResponse = await contract.store('7')
const transactionReceipt = await transactionResponse.wait(1)

从 raw data 构造交易

除了直接调用部署合约方法等,我们也可以自己构造交易。

构造交易

const nonce = await wallet.getTransactionCount()
const tx = {
  nonce,
  gasPrice: 20000000000,
  gasLimit: 1000000,
  to: null,
  value: 0,
  data: `0x${binary}`,
  chainId: 1337,
}

签名交易

const signedTx = await wallet.signTransaction(tx)

发送交易

const sentTxResponse = await wallet.sendTransaction(tx)
await sentTxResponse.wait(1)

总结

以上就是我们通过 ethers.js 库与 Alchemy 的 Rinkeby 测试网络进行交互的步骤,在真正的生产项目开发中我们一般不会直接使用 ethers.js 这样的库,而是会使用 Brownie、HardHat 这样进一步封装的框架,但了解 Web3.pyethers.js 等库的使用方法也非常重要。后续我还会对 HardHat 框架的使用作进一步讲解。

参考资料

  1. Solidity 智能合约开发 - 基础
  2. Solidity 智能合约开发 - 玩转 Web3.py
  3. Solidity, Blockchain, and Smart Contract - Javascript 版本
  4. ethers.js 项目仓库
  5. ethers.js 官方文档
  6. Alchemy 官网