> 技术文档 > 基于 ethers.js 的区块链事件处理与钱包管理_ethers.js钱包

基于 ethers.js 的区块链事件处理与钱包管理_ethers.js钱包

币圈工具箱 bqbot.cn 月访问量达90w https://bqbot.cn/jms.html (在线版地址

Event事件

检索事件

const { ethers } = require(\"hardhat\");async function SearchEvent() { try { const provider = new ethers.JsonRpcProvider(\"http://127.0.0.1:8545\"); const signer = await provider.getSigner(); const TokenAddress = \"0xxxxx\";//合约地址 const TokenABI =[]//合约的abi; const TokenContract = new ethers.Contract(TokenAddress, TokenABI, signer);//创建合约 //读取合约 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(); console.log(\"Total Supply:\", totalSupply.toString()); //合约转eth const arr1=\"0xxxxxxxx\" await TokenContract.transfer(arr1,10);//给arr1转10; const block = await provider.getBlockNumber()//得到当前block const transferEvents = await TokenContract.queryFilter(\'Transfer\', block - x, block);//检索合约Transfer,从block - x,到block之间的解析事件 console.log(`Transfer事件数量: ${transferEvents.length}`); //transferEvents是个数组,我们可以解析他的参数 console.log(...transferEvents[0].args);//返回form,to ,value }catch (error) { console.error(\"Error:\", error); } }

监听事件

//以上同上TokenContract.on(\"Transfer\", (from, to, value, event) => { console.log(`Transfer事件触发:`); console.log(`From: ${from}`); console.log(`To: ${to}`); console.log(`Value: ${value.toString()}`); console.log(` 从 ${from}=> 到 ${to} = ${value.toString()}`); console.log(`Event Details:`, event);  });

过滤事件

设置过滤规则:contract.filters.EVENT_NAME( ...args )说明:EVENT_NAME:过滤事件,...args:过滤规则

基础规则汇总

规则 含义 示例 null 该位置不限制,匹配任意值 contract.filters.Transfer(null, addr) 单个值 必须完全匹配 contract.filters.Transfer(addr) 数组 至少匹配数组中任意一个值 contract.filters.Transfer(null, [addr1, addr2])
以上代码如上//设置规则# 规则1let addr1=\"0xf39Fd6e51aad88F6F4ce6axxxxxxx\"let addr2=\"0x70997970C51812dc3A010C7xxxxxx\"let addr3=\"0xb0997970C51812dcxxxxxxxxxxxxx\"let rule1 = TokenContract.filters.Transfer(addr1);//过滤来自`addr1`地址的`Transfer`事件let rule2 = TokenContract.filters.Transfer(null,addr2);//过滤所有发给 addr2`地址的`Transfer`事件let rule3 = TokenContract.filters.Transfer(addr1,addr2);//过滤所有从 `addr1`发给`addr2`的`Transfer`事件let rule3 = TokenContract.filters.Transfer(addr1,addr2);//过滤所有从 `addr1`发给`addr2`的`Transfer`事件let rule4 = TokenContract.filters.Transfer(null,[addr2,addr3]);//过滤所有发给 addr2`地址的或者addr3`的Transfer`事件# 其他就是各种组合使用了# 过滤使用TokenContract.on(rule1, (res) => { console.log(\'---------监听开始过滤--------\'); console.log( `${res.args[0]} -> ${res.args[1]} ${res.args[2]}` )})# 其他同上 把过滤规则给监听事件即可

批量生成HD钱包

BIP汇总
BIP编号 主要用途 典型格式示例 BIP-32 HD 钱包路径 m/44\'/0\'/0\'/0/0 BIP-39 助记词生成种子 12/24 个单词 BIP-44 多币种路径 m/44\'/60\'/0\'/0/0 BIP-49 隔离见证兼容地址 m/49\'/0\'/0\'/0/0 BIP-84 原生隔离见证地址 m/84\'/0\'/0\'/0/0 BIP-173 Bech32 地址编码 bc1q... BIP-350 Taproot 地址编码 bc1p...
以BIP-44为例代码实践
  • 助记词生成
 const mnemonic = ethers.Mnemonic.entropyToPhrase(ethers.randomBytes(32))
  • 创建HD基钱包
    BIP-44

    基路格式:\"m / purpose\' / coin_type\' / account\' / change\"
    参数说明

    • m:主密钥(Master Key)
    • purpose\':固定为 44\'(表示遵循 BIP-44 多账户标准)
    • coin_type\':币种标识(如 0\' = BTC,60\' = ETH,501\' = SOL)详细可查看SLIP-44
    • account\':账户编号(从 0\' 开始)
    • change:比特币专用(0 = 外部地址,1 = 找零地址);其他链通常为 0
    • address_index:地址索引(从 0 开始)
 # BIP-44 // 基路径: const basePath = \"44\'/60\'/0\'/0\" # 生成第一对外的链接 const baseWallet = ethers.HDNodeWallet.fromPhrase(mnemonic, basePath)
  • 批量生成
const WalletNumber = 10;//钱包数 for (let i = 0; i  wallet.address));
  • 加密JSON保存
async function saveWalletJson() { const wallet = ethers.Wallet.fromPhrase(mnemonic);//助记词 console.log(\"通过助记词创建钱包:\") console.log(wallet) // 加密json用的密码,可以更改成别的 const pwd = \"XXXX\"; const json = await wallet.encrypt(pwd) console.log(\"钱包的加密json:\") console.log(json) require(\"fs\").writeFileSync(\"keystoreBatch.json\", json);//在当前文件夹下生成一个 keystoreBatch.json文件 } saveWalletJson();
  • 通过加密json读取钱包信息
async function ReadWalletJson() {console.log(\"开始读取json文件\");const json=require(\"fs\").readFileSync(\"keystoreBatch.json\", \"utf8\");const walletJson =await ethers.Wallet.fromEncryptedJson(json, \"xxx\");//生成json时设置的密码console.log(\"Wallet from JSON:\",walletJson);console.log(\"Address:\", walletJson.address);console.log(\"Private Key:\", walletJson.privateKey);console.log(\"Mnemonic:\", walletJson.mnemonic.phrase);}ReadWalletJson();

staticCall和callStatic:

名称 所属模块 作用 返回值 适用场景 staticCall ethers.Contract 实例方法 以 只读方式 调用合约函数,不修改状态 函数返回值 任何函数(读/写) callStatic ethers.Contract 实例方法(v6 新增) 以 只读方式 调用合约函数,不修改状态 函数返回值 任何函数(读/写)
# 代码实例# staticCallconst from=\"0xf39xxx\"const to=\"0x70xxx\"const result = await TokenContract.transfer.staticCall(to,10,{ // 可选 overrides  from: from, // 指定调用者(模拟不同账户) }); console.log(\'模拟结果:\', result);# callStaticconst result = await TokenContract.transfer.staticCall(to,10,{  // 可选 overrides  from: from, // 指定调用者(模拟不同账户) }); console.log(\'模拟结果:\', result);

callData

  • 接口abi:infce=new ethers.Interface(abi);//两者是一样的功能

  • callData:infce=TokenContract.interface;//两者是一样的功能

const provider = new ethers.JsonRpcProvider(\"http://127.0.0.1:8545\");const signer = await provider.getSigner();const TokenAddress = \"0xxxx\";//合约地址const TokenABI =[];//abiconst TokenContract = new ethers.Contract(TokenAddress, TokenABI, signer);const param = TokenContract.interface.encodeFunctionData( \"balanceOf\", [\"0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266\"] ); console.log(\"param:\", param); const tx = { to: TokenAddress, data: param}// 发起交易,可读操作(view/pure)可以用 provider.call(tx)const balanceWETH = await provider.call(tx)console.log(`存款前WETH持仓: ${ethers.formatEther(balanceWETH)}\\n`)

encodeFunctionData

const provider = new ethers.JsonRpcProvider(\"http://127.0.0.1:8545\");const signer = await provider.getSigner();const TokenAddress = \"0xxxxxxx\";//合约地址const TokenContract = new ethers.Contract(TokenAddress, TokenABI, signer);//构造合约# 使用合约的transfer 向0x70997970C51812dc3A010C7d01b50e0d17dc79C8 转10nconst calldata = TokenContract.interface.encodeFunctionData(\'transfer\', [ \'0x70997970C51812dc3A010C7d01b50e0d17dc79C8\', // 收款地址 10n  // 转账数量 (BigInt)]);console.log(calldata)//生成callDataconst wallet = new ethers.Wallet(\"钱包的私钥\", provider);const tx = await wallet.sendTransaction({ to: \"0x5Fxxxxxxx\",//合约地址 data: calldata,});await tx.wait();console.log(\"交易成功生成的txHash:\", tx.hash);//通过交易hash //交易的详细信息const hash = await provider.getTransaction(tx.hash);//交易收据const receipt = await provider.getTransactionReceipt(tx.hash);

识别ERC20、ERC721、ERC115标准合约

识别关键说明:所有现代标准(ERC721、ERC1155)都实现了 ERC165,通过 supportsInterface(bytes4 interfaceId) 函数声明支持的接口,ERC20 不支持 ERC165

  • ERC20

    说明:识别关键ERC20不是基于ERC165,但是ERC20包含totalSupply,识别关键通过totalSupply

    const provider = new ethers.JsonRpcProvider(\"http://127.0.0.1:8545\");const signer = await provider.getSigner();//const TokenAddress = \"0x5Fbxxxxx\";//合约地址const TokenABI = []//abiconst TokenContract = new ethers.Contract(TokenAddress, TokenABI, signer);//创建合约const totalSupplyValue=await TokenContract.totalSupply(); console.log(totalSupplyValue)//说明是ERC20
  • ERC721

    说明:识别关键是ERC721基于ERC165,ERC165标准包含supportsInterface(bytes4 interfaceId)

     创建合约如上 const isERC721 = await contract.supportsInterface(\"0x80ac58cd\"); console.log(isERC721); // true 或 false
  • ERC1155

    说明:识别关键是ERC721基于ERC165,ERC165标准包含supportsInterface(bytes4 interfaceId)

     创建合约如上 const isERC721 = await contract.supportsInterface(\"0xd9b67a26\"); console.log(isERC721); // true 或 false
  • 总结

    调用函数/方法 返回值 识别结果 备注 supportsInterface(0x80ac58cd) true ERC721 NFT 标准接口标识符 supportsInterface(0xd9b67a26) true ERC1155 多代币标准接口标识符 totalSupply() 等函数调用成功 成功 ERC20 同质化代币标准(无 ERC165)