ethers.js分析

ethers.js 合约

ethers.js使用TypeScript写的框架, 用来访问eth网络。

连接provider

支持多种方式连接eth node JSON-RPC, INFURA, Etherscan, Alchemy, Ankr or MetaMask

// JsonRpcProvider实现了Provider接口, Provider中包含一些read方法 e.g. getBalance, getBlockNumber, getBlock, 也包含call方法调用
// Provider继承自ContractRunner、EventEmitterable、NameResolver
// ContractRunner是合约运行的基础, 包含了estimateGas估算gas、call、sendTransaction, 注意provider未实现sendTransaction该方法需要私钥签名才能执行
const provider = new ethers.JsonRpcProvider("http://127.0.0.1:8545");

// 获取区块高度
const blockNum = await provider.getBlockNumber()

合约操作

创建合约实例

    const abiERC20 = [
        "function name() view returns (string)",
        "function symbol() view returns (string)",
        "function totalSupply() view returns (uint256)",
        "function balanceOf(address) view returns (uint)",
    ];
    const provider = new ethers.JsonRpcProvider("https://goerli.infura.io/v3/52e90a56caa54252808c7d6c3add8a0f");
    const baseWallet = ethers.HDNodeWallet.createRandom();
    // 绑定wallet和provider(可以发送交易)
    const wallet = baseWallet.connect(provider);
    // 合约地址
    const addressDAI = '0x6B175474E89094C44Da98b954EedeAC495271d0F';
    // 创建合约实例
    const contractDAI = new ethers.Contract(addressDAI, abiERC20, wallet);
    // 调用合约方法
    const totalSupply = await contractDAI.totalSupply();

interface编码

可以使用Interface加载abi结构, 然后编码function, topics, eventlog等

    const abiERC20 = [
        "function name() view returns (string)",
        "function symbol() view returns (string)",
        "function totalSupply() view returns (uint256)",
        "function balanceOf(address) view returns (uint)",
        "event Transfer(address indexed from, address indexed to, uint256 value)",
    ];
    const abi = new ethers.Interface(abiERC20);
    const functionData = abi.encodeFunctionData('balanceOf',['0x6B175474E89094C44Da98b954EedeAC495271d0F']);
    const filterTopics = abi.encodeFilterTopics('Transfer',['0x6B175474E89094C44Da98b954EedeAC495271d0F','0x6B175474E89094C44Da98b954EedeAC495271d0F'])
    const eventLog = abi.encodeEventLog('Transfer',['0x6B175474E89094C44Da98b954EedeAC495271d0F','0x6B175474E89094C44Da98b954EedeAC495271d0F', 123])

发送tx

    const provider = new ethers.JsonRpcProvider("https://goerli.infura.io/v3/52e90a56caa54252808c7d6c3add8a0f");
    const baseWallet = ethers.HDNodeWallet.createRandom();
    // 绑定wallet和provider(可以发送交易)
    const wallet = baseWallet.connect(provider);
    const tx = {
        to: xxx,
        data: functionData  // 上面编码出来的data
    }
    const response = await wallet.sendTransaction(tx);

发送call

    const provider = new ethers.JsonRpcProvider("https://goerli.infura.io/v3/52e90a56caa54252808c7d6c3add8a0f");
    const baseWallet = ethers.HDNodeWallet.createRandom();
    // 绑定wallet和provider(可以发送交易)
    const wallet = baseWallet.connect(provider);
    const tx = {
        to: xxx,
        data: functionData  // 上面编码出来的data
    }
    // 如果不想真实发送交易到网络中可以使用call调用, 只在目标机器执行并返回结果
    const response = await wallet.call(tx);

wallet操作

SigningKey

使用SigningKey进行私钥签名、验证签名、生成公钥、地址. 数据转换过程privateKey -> publicKey -> address.

const { ethers,SigningKey } = require("ethers");

// 生成私钥
const privateKey = "0x0123456789012345678901234567890123456789012345678901234567890123";

// 创建一个 ethers.js 的签名器对象
const signingKey = new ethers.SigningKey(privateKey);

// 将消息进行哈希运算
const message = "Hello, world!";
const messageHash = ethers.keccak256(ethers.toUtf8Bytes(message));

// 生成 r s v
const signature = signingKey.sign(messageHash);
const { r, s, v } = ethers.Signature.from(signature);
console.log("r: " + r);
console.log("s: " + s);
console.log("v: " + v);

// public key验证
const recoverPublicKey = SigningKey.recoverPublicKey(messageHash, signature);
console.log(`public key 验证:${recoverPublicKey === signingKey.publicKey}`);

// address验证
const address = ethers.computeAddress(signingKey.publicKey);
const recoveredAddress = ethers.recoverAddress(messageHash, signature);
console.log(`address 验证:${address === recoveredAddress}`);

上述代码中的SigningKey主要是管理私钥、公钥、以及数字签名的. Wallet包含SigningKey 除了上述功能还管理账户地址(通过publicKey生成), 查看余额, 发送交易等功能.

使用Mnemonic助记词生成HDWallet过程

1.生成Mnemonic
1).生成随机种子Entropy
2).根据种子+wordList生成助记词(种子相当于index wordList相当于array), 可选password 在用Mnemonic生成seed时会用到.
3).最后组装成Mnemonic

2.生成HDNodeWallet
1).使用Mnemonic生成seed Mnemonic.computedSeed(使用PBKDF2函数和HMAC-SHA512算法,把助记词转换成一个种子seed)
2).使用种子生成HDWallet确定性钱包, 在这个钱包中可以根据路径派生出多个地址(每个地址都是独立的私钥)
使用ethers.js v6版本代码例子如下:

const { ethers,wordlists } = require("ethers");

const mnemonic = ethers.Mnemonic.fromEntropy("0xd4e409e44611be1a7e93a0c2ade7a131","123", wordlists.zh_cn)
const seed = mnemonic.computeSeed();
// path:'m'
let hdNodeWallet = ethers.HDNodeWallet.fromSeed(seed)

let basePath = "m/44'/60'/0'/0";
let wallet = hdNodeWallet.derivePath(basePath+'/0');
console.log(wallet)

wallet = hdNodeWallet.derivePath(basePath+'/1');
console.log(wallet)

wallet = hdNodeWallet.derivePath(basePath+'/2');
console.log(wallet)

查询区块数据

可以查询合约中的event事件,

过滤事件

使用provider过滤

    const logs = await provider.getLogs({
        fromBlock: 8876717,
        toBlock: 8876813,
        address: '0x39c72916169b0d9eb22610a90f612d09302aa234',  // 合约地址
        topics: ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef']  // topics编码信息
    });

使用合约过滤查询

    const events = await erc20Contract.queryFilter('Transfer', fromBlock, toBlock);

监听器

使用provider监听 block, pending, transaction, event, orphan等

     case "block": case "pending": case "debug": case "network": {return { type: _event, tag: _event }
    { type: "transaction", tag: getTag("tx", { hash }), hash }
    { type: "orphan", tag: getTag("orphan", event), filter: copy(event) }
    { filter, tag: getTag("event", filter), type: "event" }

代码

    provider.on('pending',async (txHash)=>{

    });

使用contract监听事件

    await erc20Contract.on('Transfer', (from, to, amount) => {
        console.log(`from:${from} to:${to} amount:${amount}`);
    });
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
打野工程师
打野工程师
江湖只有他的大名,没有他的介绍。