ethers.js链上交互指南 / 学习智能合约#65

in STEEM CN/中文9 months ago

这些天想升级Dapp的时候却发现web3.js包用不了!web3.js一直以来是又大又难装,这下彻底完犊了,用不了了。这时只能启用备用方案:ethers.js

ethers.jpg

https://docs.ethers.org/v6/

ethers.js更新的挺快,这就6.8的版本啰!

简单对比了下,ethers.js大约是18M, web3.js却有近57M!ethers.js很是小巧灵活。测试了一些写法,感觉还是很丝滑的。而且功能也很全面,爱了!

下载与资源

ethers |
ethers-github |
中文文档 |

安装

npm install ethers --save 
// "ethers": "^6.8.0"   18.3M
// cnpm install ethers --save 
// yarn add ethers

Gas

一经创建,每笔交易都收取一定数量的 gas ,目的是限制执行交易所需要的工作量和为交易支付手续费。EVM 执行交易时,gas 将按特定规则逐渐耗尽。

gas price 是交易发送者设置的一个值,发送者账户需要预付的手续费= gasPrice * gas 。如果交易执行后还有剩余, gas 会原路返还。

创建providers

ethers是一套和以太坊节点进行通信的API,是对JSON-RPC的封装。如果我们需要基于以太坊来开发去中心化应用,就需要通过ethers来获取节点状态,获取账号信息,调用合约、监听合约事件等等。

智能合约是运行在节点提供的虚拟机上,因此调用智能合约也需要像节点发送请求。

import { ethers } from 'ethers'
// const ethers = require('ethers')

//使用本地节点或是远程节点
const url = "http://127.0.0.1:8545"
//"http://localhost:8545" 会报错
// const url = "https://rpc-mumbai.maticvigil.com"
// const url = "https://rpc.ftm.tools/"
const provider = new ethers.JsonRpcProvider(url)
//交易签名
const signer = await provider.getSigner() //默认地址,一般是第一个
//const signer = await provider.getSigner("0x025C4AB61A50Ce391E9F3cd88a858E5C5ADABaF1") //可以这样指定

//metamask中获取
provider = new ethers.BrowserProvider(window.ethereum)

基本使用

// Look up the current block number
await provider.getBlockNumber()
// 16383845

//获取一个地址或ENS的余额
let addr = "0xf9eFdD67e2C46a8086aed666ea8e294986AB285C"
let balance = await provider.getBalance(addr)
// 100000000000000000000n

//eth转成wei
ethers.parseEther("1.2")
//1200000000000000000n

//wei转成 eth
ethers.formatEther('23')
// 0.000000000000000023


//发送交易
const option = {
    to:toAccount,
    value: 1500000000,  //wei
    gas: gas,
    gasPrice: gasPrice
}
eg:
const tx = await signer.sendTransaction({
    to: "0xf33B812142b8a008cD98ED889605869B31393849",
    value: ethers.parseEther("1.0")
  })
const receipt = await tx.wait()
console.log(899, receipt) 
/*
TransactionReceipt {
  provider: JsonRpcProvider {},
  to: '0xf33B812142b8a008cD98ED889605869B31393849',
  from: '0xf9eFdD67e2C46a8086aed666ea8e294986AB285C',
  contractAddress: null,
  hash: '0xafac68a6a871424bf2aa4da66b7aed38c379bd335e56f8bfecdea680febac5ed',
  index: 0,
  blockHash: '0xb5e876eae0264cdfbbb7e8d0e3676ed31abc6121d78fc9080498fea3bdafa3d2',
  blockNumber: 1,
  logsBloom: '0x0000000....00',
  gasUsed: 21000n,
  cumulativeGasUsed: 21000n,
  gasPrice: 20000000000n,
  type: 0,
  status: 1,
  root: undefined
}
*/

常用方法

1. 单位换算
//eth转成wei
ethers.parseEther("1.2")
//1200000000000000000n
//wei转成 eth
ethers.formatEther('23')
// 0.000000000000000023

//另外一种转换方法
ethers.parseUnits("1.3", 18) // 乘以10**18
//1300000000000000000n
ethers.formatUnits("1300000000000000000", 18) // 除以10**18
//1.3

2. gasLimit 
signer.estimateGas(tx)
通过执行一个消息调用来得到交易的 gas 用量, 在此基础上加30000gas

3. GasPrice
await provider.getFeeData() //获取当前gas价格
/*
FeeData {
  gasPrice: 20000000000n,
  maxFeePerGas: null,
  maxPriorityFeePerGas: null
}*/

4. 查找最新区块号
await provider.getBlockNumber()
//2655567

钱包功能

手册
新帐户

import { ethers } from 'ethers'
import fs from 'fs'

//直接生成私钥和地址
let lbcWallet = ethers.Wallet.createRandom()
lbcWallet.address
// "0x863e27dbD608649d08c69F83ccA51b045721f318"
lbcWallet.privateKey
// "0x8bc29xxxxxxx"

//随机数生成钱包
let random = ethers.randomBytes(32) 
console.log(563, random)
let privateKey = Buffer.from(random).toString('hex')   //转成16进制
console.log(112, privateKey) 
let wallet = new ethers.Wallet(privateKey)
console.log("账号地址: " + wallet.address, wallet)

//生成随机助记词并创建钱包
let mnemonic = ethers.Mnemonic.fromEntropy(ethers.randomBytes(16))
// let mnemonic = ethers.Mnemonic.fromEntropy(ethers.randomBytes(16), "jxxxx")  第二个参数是密码
console.log(1323, mnemonic)
var path = "m/44'/60'/0'/0/0"
// 通过助记词创建钱包
let wallet = ethers.HDNodeWallet.fromMnemonic(mnemonic, path) 
console.log("账号地址: " + wallet.address, wallet)

//根据助记词找回钱包信息
let monic = "dinosaur tape orbit chronic private ....."
let mnemonic = ethers.Wallet.fromPhrase(monic)
console.log(566, mnemonic)
let privateKey = mnemonic.privateKey
console.log("钱包私钥:",privateKey)

//根据私钥找回钱包地址
let wallet = new ethers.Wallet(privateKey)
//钱包地址
let address = wallet.address
console.log(156, address)

//生成json钱包文件
let main = async function (){
  let random = ethers.randomBytes(32) 
  let privateKey = Buffer.from(random).toString('hex')  //转成16进制
  let wallet = new ethers.Wallet(privateKey)
  let password = "xxxxx"
  let res = await wallet.encrypt(password)
  console.log(156, res)
  let d = new Date()
  let time = d.getTime()
  let path = './keystore/' + time + '-' + wallet.address
  fs.writeFileSync(path, res)
}

//json文件找回钱包信息
let main2 = async function (){
  let res = fs.readFileSync('./keystore/16983xxxxxxxxxxxB2c6', "utf8")
  let password = "xxxxxx"
  let wallet = await ethers.Wallet.fromEncryptedJson(res, password)
  console.log("Address: " + wallet.address, wallet,"privateKey:", wallet.privateKey)
}

与合约交互

//只读方法 (like view and pure)
import { ethers } from 'ethers'
const url = "http://127.0.0.1:8545"
const provider = new ethers.JsonRpcProvider(url)
let abi = [
    "function str() view returns (string)",
    "function set(uint x)",
    "function get()view returns (uint)"
  ]
//这里的abi相当于接口
// 也可以使用合约的编译abi: import Storage from '@/static/Storage.json' -> Storage.abi
let contractAddr = "0x600a00aE84b896edd9F2732565cf2195d032315E"
let contract = new ethers.Contract(contractAddr, abi, provider)
let str = await contract.get()
console.log(256, str)

//合约的所有方法, signer签名
const url = "http://127.0.0.1:8545"
const contractAddr = "0x600a00aE84b896edd9F2732565cf2195d032315E"
const provider = new ethers.JsonRpcProvider(url)
const signer = await provider.getSigner()
const contract = new ethers.Contract( contractAddr,  Storage.abi,  signer)
console.log(56, "signer:", signer)
const option = { 
}
const tx = await contract.set(996, option)
await tx.wait()
console.log(669, "singen res:", tx)

//合约的所有方法, 也可以连上钱包来实现
import Storage from '@/static/Storage.json'
const url = "http://127.0.0.1:8545"
const contractAddr = "0x600a00aE84b896edd9F2732565cf2195d032315E"
const provider = new ethers.JsonRpcProvider(url)
const contract = new ethers.Contract(contractAddr, Storage.abi, provider)
const PRIVATE_KEY = "0x182xxxxxxxxx"
const wallet = new ethers.Wallet(PRIVATE_KEY, provider)
console.log(56, "wallet:", wallet)
const nonce = await provider.getTransactionCount(wallet.address)
console.log(58, "nonce:", nonce)
const StorageConnected = contract.connect(wallet)
const option = {
    nonce
}
const tx = await StorageConnected.set(86394, option)
await tx.wait()
console.log(669, "res:", tx)

ethers的体验相当不错,功能齐全,它的转正也是情理之中啰。

Coin Marketplace

STEEM 0.19
TRX 0.13
JST 0.030
BTC 63630.78
ETH 3406.29
USDT 1.00
SBD 2.55