上次测试了ethers.js与链上的交互功能,这次测试下与合约的交互以及查看事件的机制。这次也是准备打通链上与AI·Joe的功能:充值与NFT。预备着过几天上线新功能。
与合约交互
//只读方法 (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()