ethers.js 全栈开发实战:从 Provider 到 Utils 的 6 大核心模块深度解析

  • 木西
  • 发布于 8小时前
  • 阅读 40

前言在以太坊生态中,ethers.js是开发者与区块链交互的核心工具包。本文系统梳理了其六大核心模块(Provider、Contract、Wallet、Utils、部署工具及高级功能),通过代码示例+关键差异对比+安全实践,帮助开发者快速掌握从环境搭建到链上交互的全流程。无论是浏览器端

前言

在以太坊生态中,ethers.js 是开发者与区块链交互的核心工具包。本文系统梳理了其六大核心模块(Provider、Contract、Wallet、Utils、部署工具及高级功能),通过代码示例 + 关键差异对比 + 安全实践,帮助开发者快速掌握从环境搭建到链上交互的全流程。无论是浏览器端轻量集成,还是 Node.js 服务端深度开发,本文均提供可直接落地的解决方案

安装

  • 链接方式

    # 在页面上使用链接使用
    <script type="module">
    import { ethers } from "https://cdnjs.cloudflare.com/ajax/libs/ethers/6.7.0/ethers.min.js";
    </script>
  • 安装包方式

    # 项目中引入包
    npm install ethers
    import {ethers} from "ethers";
    import { BrowserProvider, parseUnits } from "ethers"; 
    import { HDNodeWallet } from "ethers/wallet";

    Provider提供者类

    通过Provider类,读取链上的信息;

  • ethers.BrowserProvider
    #和钱包插件交 链接钱包插件读取钱包账号的信息
    import { ethers } from "https://cdnjs.cloudflare.com/ajax/libs/ethers/6.7.0/ethers.min.js";
    const provider = new ethers.BrowserProvider(window.ethereum);
  • ethers.JsonRpcProvider
    import { ethers } from "https://cdnjs.cloudflare.com/ajax/libs/ethers/6.7.0/ethers.min.js";
    # 注册infura 创意一个项目获取your-infura-project-id ,读取sepolia链上的信息
    const ALCHEMY_MAINNET_URL="https://sepolia.infura.io/v3/{your-infura-project-id}"
    const provider=new ethers.JsonRpcProvider(ALCHEMY_MAINNET_URL);
    or
    # 启动ganach 
    # 读取本地ganach
    const provider = new ethers.JsonRpcProvider("http://127.0.0.1:7545");

    区别

    特性 ethers.BrowserProvider ethers.JsonRpcProvider
    用途 连接到用户的钱包(如 MetaMask) 连接到远程以太坊节点(如 Infura、Alchemy)
    依赖 依赖 window.ethereum 依赖远程节点的 JSON-RPC 接口
    用户友好 高(用户直接使用钱包) 低(需要配置远程节点)
    安全性 高(私钥存储在本地钱包) 低(需要信任远程节点)
    功能 有限(依赖钱包功能) 强大(访问节点全部功能)
    适用场景 浏览器环境,用户交互 服务器端或无钱包环境

    Provider常见属性和方法

    属性

  • blockNumber:返回 Provider 已经知晓的最新区块号(块高),如果没有同步到区块,则为 null
  • polling:可变属性,表示 Provider 是否正在轮询。轮询可以设置为临时启用/禁用或永久禁用以允许节点进程退出。
  • pollingInterval:可变属性,表示 Provider 的轮询频率(以毫秒为单位)。默认时间间隔为 4 秒

    方法

    getNetwork() :获取当前连接的网络信息

    const ALCHEMY_MAINNET_URL="https://sepolia.infura.io/v3/{your-infura-project-id}"
    const provider=new ethers.JsonRpcProvider(ALCHEMY_MAINNET_URL);
    const network = await provider.getNetwork();
    # 网络名
    console.log("Network name:", network.name);//sepolia
    # 网络id
    console.log("Chain ID:", network.chainId);//11155111

getBalance(address) :获取指定地址的余额

# 节点公钥
例如:0xEb9e88cE633B2a8F400xxxxxxxxx
const balance = await provider.getBalance("0xEb9e88cE633B2a8F400xxxxxxxxx");
console.log("Balance:", ethers.formatEther(balance)); // 把wei格式化为 ETH

getTransaction(transactionHash) :获取指定交易的详细信息。

    const ALCHEMY_MAINNET_URL="https://sepolia.infura.io/v3/{your-infura-project-id}"
    const provider=new ethers.JsonRpcProvider(ALCHEMY_MAINNET_URL);
    //通过交易hash:在sepolia.etherscan浏览器器上找一个交易hash:例如:0xb1fd8ca5493460bb7d9d1ae7d0a96fcd7a77c3eea53954f9ab6ae6225019972b验证
    const transaction = await provider.getTransaction("0xYourTransactionHashHere");
    console.log("Transaction from:", transaction.from);
    console.log("Transaction to:", transaction.to);
    console.log("Transaction value:", ethers.formatEther(transaction.value));

getTransactionReceipt(transactionHash) :获取指定交易的收据信息。

 const ALCHEMY_MAINNET_URL="https://sepolia.infura.io/v3/{your-infura-project-id}"
    const provider=new ethers.JsonRpcProvider(ALCHEMY_MAINNET_URL);
    //通过交易hash:在sepolia.etherscan浏览器器上找一个交易hash:例如:0xb1fd8ca5493460bb7d9d1ae7d0a96fcd7a77c3eea53954f9ab6ae6225019972b验证
    const receipt = await provider.getTransactionReceipt("0xYourTransactionHashHere");
    console.log("Transaction status:", receipt.status);
    console.log("Transaction logs:", receipt.logs);

sendTransaction(signedTransaction) :发送一个已签名的交易。

    const txHash = await provider.sendTransaction(signedTransaction);
    console.log("Transaction hash:", txHash); 

getSigner(address) :获取与指定地址关联的 Signer 对象。

    const signer = provider.getSigner("0xYourAddressHere");
    console.log("Signer address:", await signer.getAddress());

estimateGas(transaction) :估算执行指定交易所需的 gas。

    const gasEstimate = await provider.estimateGas({
      to: "0xRecipientAddressHere",
      from: "0xSenderAddressHere",
      value: ethers.parseEther("1.0"),
    });
    console.log("Estimated gas:", gasEstimate.toString());

call(transaction, blockTag) :调用一个智能合约的函数,不发送交易。

    const result = await provider.call({
      to: "0xContractAddressHere",
      data: "0xYourEncodedFunctionCallHere",
    });
    console.log("Call result:", result);

getBlock(blockTag) :获取指定区块的信息

        const block = await provider.getBlock("latest");
        console.log("Block number:", block.number);
        console.log("Block timestamp:", block.timestamp);

getBlockTransactionCount(blockTag) :获取指定区块中的交易数量。

    const transactionCount = await provider.getBlockTransactionCount("latest");
    console.log("Transaction count in block:", transactionCount);

resolveName(ensName) :获取 ensName 对应地址的 Promise,如果没有则为 null

    provider.resolveName("registrar.firefly.eth").then(function(address) {
      console.log("Address: " + address);
      // "0x6fC21092DA55B392b045eD78F4732bff3C580e2c"
    });

lookupAddress(address) :获取 address 对应的 ENS 名称的 Promise,如果没有则为 null

let address = "0x6fC21092DA55B392b045eD78F4732bff3C580e2c";
provider.lookupAddress(address).then(function(name) {
  console.log("Name: " + name);
  // "registrar.firefly.eth"
});

Contract合约类

说明:通过一个ERC20的代币合约,实现对合约的读写操作

  • 合约类型

    • 可读:最后参数:Provider
    • 可读、写:最后参数 Singer
    • 区别:创建合约的最后一个参数:是Provider合约只读,Singer可以读、写
  • 只读

    # 说明: 使用hardhat 部署成功后会在控制台返回合约地址 abi 在artifacts/contracts/中的json中取出abi数组
    # 引入包
    import { ethers } from 'https://cdnjs.cloudflare.com/ajax/libs/ethers/6.7.0/ethers.min.js';
    //const provider = new ethers.BrowserProvider(window.ethereum);
    const provider = new ethers.JsonRpcProvider("http://127.0.0.1:8545");
    # 创建合约
    const TokenAddress="0xxxxxxxxxx";
    const const TokenABI =[{
                        "inputs": [],
                        "name": "name",
                        "outputs": [{"type": "string"}],
                        "stateMutability": "view",
                        "type": "function"
                    },
                    {
                        "inputs": [],
                        "name": "symbol",
                        "outputs": [{"type": "string"}],
                        "stateMutability": "view",
                        "type": "function"
                    },
                    {
                        "inputs": [],
                        "name": "totalSupply",
                        "outputs": [{"type": "uint256"}],
                        "stateMutability": "view",
                        "type": "function"
                    }
                    //其他更多
                    ]
    const TokenContract = new ethers.Contract(TokenAddress, TokenABI, provider);
    # 代币名
    const name = await TokenContract.name();
    console.log("Contract Name:", name);
    # 代币符合
    const symbol = await TokenContract.symbol();
    console.log("Contract Symbol:", symbol);
    # 代币总额
    const totalSupply = await TokenContract.totalSupply();
  • 读、写

    说明:最主要的区别是创建合约的提供者不同(singer)

    import { ethers } from 'https://cdnjs.cloudflare.com/ajax/libs/ethers/6.7.0/ethers.min.js';
    //const provider = new ethers.BrowserProvider(window.ethereum);
    const provider = new ethers.JsonRpcProvider("http://127.0.0.1:8545");
    # 创建合约
    const TokenAddress="0xxxxxxxxxx";
    const const TokenABI =[];
    const signer = await provider.getSigner();
    const TokenContract = new ethers.Contract(TokenAddress, TokenABI, singer);
    # 交互操作 转账
    const addr1="0xxxxxxxxxx";
    # 给addr1转1LYTH
    const tx= await TokenContract.transfer(addr1,1);
    # 等待交易成功
    await tx.wait()
    console.log("查看账号addr1的余额",await TokenContract.balanceOf(addr1))
    console.log("查看账号signer的余额",await TokenContract.balanceOf(signer))
  • 部署

    说明通过合约工厂部署合约

    const { ethers } = require("hardhat");
    async function deployContract() {
        try {
            const [signer] = await ethers.getSigners();//签名者
            //通过 npx hardhat compile编译合约,在artifacts/contracts/xxx.sol/xxx.json中复制abi数组和bytecode码
            const abi=[]//
            const bytecode="";//
            const deployFactory = new ethers.ContractFactory(abi,bytecode,signer)//工厂合约
            const TokenContract = await deployFactory.deploy();//部署
            console.log("部署合约交易详情",TokenContract.deploymentTransaction())
            await TokenContract.waitForDeployment();//等待部署成功
            console.log("Token deployed at:", TokenContract.target);//合约地址
            # 调用合约的方法和交互
            console.log(`合约代号: ${await TokenContract.name()`)
            console.log(`合约代号: ${await TokenContract.symbol()}`)
            console.log(`合约代号: ${await TokenContract.totalSupply()}`)
            const addr1="0xxxxxxx"
            const tx= await TokenContract.transfer(addr1,1);//向addr1转1
            console.log("Transfer Transaction:", tx);
            const balance = await TokenContract.balanceOf(addr1);//查看addr1余额
            console.log("Balance:", balance.toString());
            console.log(await TokenContract.balanceOf(signer))//查看signer余额
    }catch (error) {
    console.log(error)
    }
    }

    Wallet钱包类

    创建钱包

    说明:Wallet是Singer的子类,Singer的抽象类不能直接实例化

  • 随机钱包

    const Wallet=ethers.Wallet.createRandom();
    # 公钥
    console.log("Address:", Wallet.address);
    # 私钥
    console.log("Private Key:", Wallet.privateKey);
    # 助记词
    console.log("Mnemonic:", Wallet.mnemonic.phrase);
  • 用私钥创建wallet对象

    const private=new ethers.JsonRpcProvider("http://127.0.1:8545");
    const privateKey = "acxxxxxxxxx";//私钥
    const wallet = new ethers.Wallet(privateKey,private);
    console.log("Wallet address:", wallet);//钱包公钥
  • 从助记词创建wallet对象

    # 助记词
    const Mnemonic="quote shy web universe book wheat turtle cabin degree permit beach capable";//12个不同的单词()
    const wallet3 = ethers.Wallet.fromPhrase(Mnemonic);
    console.log("Wallet3:", wallet3);
    console.log("Address:", wallet3.address);
    console.log("Private Key:", wallet3.privateKey);
    console.log("Mnemonic:", wallet3.mnemonic.phrase);
  • 通过JSON文件创建wallet对象

    如何生成Keystore V3 文件格式(以太坊钱包加密 JSON 文件)

    async function creactKeystoreV3() {
    const wallet = ethers.Wallet.createRandom();
    // 2. 加密为 Keystore V3 JSON
    const password="xxxxxx";//密码
    const json = await wallet.encrypt(password);
    // 3. 保存到文件
    require("fs").writeFileSync("keystore.json", json);
    console.log("JSON:", json);
    }
    creactKeystoreV3();//会在当前页面生成一个keystore.json文件
    

    Keystore V3创建wallet

    async function creatWalletJson() {
    console.log("开始读取json文件");
    const json=require("fs").readFileSync("keystore.json", "utf8");
    const password="xxxxxx"
    const walletJson =await ethers.Wallet.fromEncryptedJson(json, password);//json文件和密码
    console.log("Wallet from JSON:",walletJson);
    console.log("Address:", walletJson.address);//钱包地址
    console.log("Private Key:", walletJson.privateKey);//私钥
    console.log("Mnemonic:", walletJson.mnemonic.phrase);//助记词
    }
    creatWalletJson();

    转账

async function sendTransactionFn() { const private=new ethers.JsonRpcProvider("http://127.0.1:8545"); const privateKey = "xxx";//私钥 const wallet = new ethers.Wallet(privateKey,private); const address1="0xxxxx";//账号1公钥 const tx={ to:address1, value:ethers.parseEther("100"),//转移100eth }// //执行交易 const txRes = await wallet2.sendTransaction(tx); const receipt = await txRes.wait() // 等待链上确认交易 console.log(receipt) // 打印交易的收据 } sendTransactionFn(); // 把公钥导入MetaMask插件中导入在账号可以查看账号余额的变化


# Utils工具类

| 工具方法                       | 典型用途                  | 示例代码                           |
| :------------------------- | :-------------------- | :----------------------------- |
| `parseUnits`/`formatUnits` | 处理代币单位转换(如 ETH ↔ Wei) | `ethers.parseUnits("1.5", 18)` |
| `keccak256`                | 生成哈希(如事件签名、地址校验)      | `ethers.keccak256("0x1234")`   |
| `toChecksumAddress`        | 校验并标准化地址格式            | `ethers.getAddress("0x...")`   |
| `hexlify`/`hexStripZeros`  | 处理十六进制字符串             | `ethers.hexlify([1, 2, 3])`    |
| `randomBytes`              | 生成安全随机数               | `ethers.randomBytes(32)`
# 总结
通过上述实践,我们系统梳理了 ethers.js 核心模块,在开发中的典型应用场景与最佳实践,为高效构建区块链交互提供了可复用的技术方案。
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
木西
木西
江湖只有他的大名,没有他的介绍。