ethers.js与合约交互及事件机制 / 学习智能合约#66

in STEEM CN/中文8 months ago

上次测试了ethers.js与链上的交互功能,这次测试下与合约的交互以及查看事件的机制。这次也是准备打通链上与AI·Joe的功能:充值与NFT。预备着过几天上线新功能。

ethers.jpg

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

与合约交互

交互基础
ethers.js分析

//只读方法 (like view and pure)
import { ethers } from 'ethers'
//const url = "http://127.0.0.1:8545"
// const url = "https://polygon-rpc.com"
const url = "https://rpc-mumbai.maticvigil.com"
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签名 metamask
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)

//合约的set方法, 需要连上钱包来实现
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)

//另一种写法 在创建合约实例时导入钱包
const privateKey = 'bde95xxxxx'
const wallet = new ethers.Wallet(privateKey, provider)
console.log(56, "wallet:", wallet)
const _Contract = new ethers.Contract(contractAddr, ContractAbi, wallet)

const main = async () => {
    //显示原本的 owner
    let ownerVal = await _Contract.getOwner()
    console.log("原本的 owner:", ownerVal)
    //调用 setOwner 更改 owner
    await _Contract.setOwner("0x503063dD8f114059B09FD5bC953E71fc14a1d672")
    //更改后的 owner
    ownerVal = await _Contract.getOwner()
    console.log("更改后的 owner:", ownerVal)
}
main()

友好的abi

在上一点中,我们使用的 abi 并不是非常友好,咱们编写 abi 还有一种比较简单的 函数签名的方式 编写abi:

const ContractAbi = [
    "function getOwner()view public returns(address)",
]
其实简单点来说你就把函数签名进行复制过来就ok了,由于我们只是一个只读合约,复制一个只读方法即可,这样也可以完成上一点的内容。
eg:
const abi = [
    "event Transfer(address indexed from, address indexed to, uint value)",
    "function balanceOf(address account) external view returns (uint256)",
    "function transfer(address recipient, uint256 amount) external returns (bool)"
  ]

读取合约历史事件

查询合约的历史数据通常涉及到查看合约发出的事件(events),queryFilter来查询

import { ethers } from "ethers"

const url = "https://rpc-mumbai.maticvigil.com"
const provider = new ethers.JsonRpcProvider(url)
const abi = [
  "event Transfer(address indexed from, address indexed to, uint value)"
]
const address = '0xaA61b68301278Da4e001d2cF7F8b2202aed6347c'
const contract = new ethers.Contract(address, abi, provider)

// Query the last 1000 blocks for any transfer
let filter = contract.filters.Transfer
let events = await contract.queryFilter(filter, -1000) 
//也可以这样写
// const height = await provider.getBlockNumber()
// const events = await contract.queryFilter('Transfer', height - 1000, height)
console.log(899, "events", events)
//
[
  EventLog {
    provider: JsonRpcProvider {},
    transactionHash: '0x3ca891c7df6ce814e7a418bf1b6985ff88ec2087f540665c752a1916e0982e86',
    blockHash: '0xd8b47357bfcf1ae0185e458d393a1a8ba9a19999aa7c16c719edaadc30ad0419',
    blockNumber: 42621522,
    removed: false,
    address: '0xaA61b68301278Da4e001d2cF7F8b2202aed6347c',
    data: '0x00000000000000000000000000000000000000000000000ae2a8e81db2700000',
    topics: [
      '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef',
      '0x000000000000000000000000654efb5135e61f3db91dd69d239c2e2812d73604',
      '0x000000000000000000000000469d135bfdde122a198b978d1a85f05100217246'
    ],
    index: 83,
    transactionIndex: 36,
    interface: Interface {
      fragments: [Array],
      deploy: [ConstructorFragment],
      fallback: null,
      receive: false
    },
    fragment: EventFragment {
      type: 'event',
      inputs: [Array],
      name: 'Transfer',
      anonymous: false
    },
    args: Result(3) [
      '0x654Efb5135E61f3Db91dd69d239c2E2812D73604',
      '0x469D135bFdDe122a198B978d1A85f05100217246',
      200800000000000000000n
    ]
  }
]

获取历史日志

getLogs方法获取历史日志

let contractEnsName = '0xaA61b68301278Da4e001d2cF7F8b2202aed6347c'

let topic = ethers.id("Transfer(address,address,uint256)")
console.log(123, topic)
let filter = {
  address: contractEnsName,
  fromBlock: -600,
  toBlock: 'latest',
  topics: [ topic ]
}

let result = await provider.getLogs(filter)
console.log(6663, result)
//
[  Log {
    provider: JsonRpcProvider {},
    transactionHash: '0x8736cd74cb13488df6b49899cd8833206e003c37f02560a92c0ac9495c7155c5',
    blockHash: '0x5e163f0a62cdc8c858a4aaf2ba6cf471c9abf074fc887d611db64a5005fa8a18',
    blockNumber: 42625293,
    removed: false,
    address: '0xaA61b68301278Da4e001d2cF7F8b2202aed6347c',
    data: '0x00000000000000000000000000000000000000000000000029a2241af62c0000',
    topics: [
      '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef',
      '0x0000000000000000000000003b2dbd900e9b23e94270ade010616b9a28293a87',
      '0x000000000000000000000000032f93fae3866af7e86b8022c8ec33b60965de82'
    ],
    index: 21,
    transactionIndex: 4
  }
]

请注意,查询历史事件可能需要你的提供者支持历史状态查询,这在某些免费提供者中可能是受限的。
如果你需要查询很早之前的事件,可能需要一个归档节点或者使用专门的区块链索引服务,
如 The Graph。此外,查询大量的历史数据可能会非常耗时,并且可能会受到节点服务的速率限制。
在实际应用中,你可能需要考虑分批查询或使用其他策略来优化性能。

事件监听

监听合约事件

async function main(){
    const abi = [
         "event Transfer(address indexed from, address indexed to, uint value)"
        ]

    const address = '0xaA61b68301278Da4e001d2cF7F8b2202aed6347c'
        const contract = new ethers.Contract(address, abi, provider)

    // Begin listening for any Transfer event
    contract.on("Transfer", (from, to, _amount, event) => {
      const amount = ethers.formatEther(_amount)
      console.log(666, `${ from } => ${ to }: ${ amount }`);

      // The `event.log` has the entire EventLog

      // Optionally, stop listening
      event.removeListener()
    })

    /*
    // Same as above
    contract.on(contract.filters.Transfer, (from, to, amount, event) => {
      // See above
    })

    // Listen for any Transfer to "ethers.eth"
    filter = contract.filters.Transfer("ethers.eth")
    contract.on(filter, (from, to, amount, event) => {
      // `to` will always be equal to the address of "ethers.eth"
    })

    // Listen for any event, whether it is present in the ABI
    // or not. Since unknown events can be picked up, the
    // parameters are not destructed.
    contract.on("*", (event) => {
      // The `event.log` has the entire EventLog
    })
    */

}
main()

Coin Marketplace

STEEM 0.19
TRX 0.13
JST 0.030
BTC 63537.51
ETH 3405.29
USDT 1.00
SBD 2.55