幣圈工具箱 bqbot.cn 月訪問量達90whttps://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());//合約轉ethconst arr1="0xxxxxxxx"await TokenContract.transfer(arr1,10);//給arr1轉10;const block = await provider.getBlockNumber()//得到當前blockconst 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]) |
以上代碼如上
//設置規則
# 規則1
let 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-44account'
:賬戶編號(從?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 < WalletNumber; i++) {let NewBaseWallet = baseWallet.derivePath(i.toString());console.log(`第${i+1}個錢包地址: ${baseWalletNew.address}`)wallets.push(baseWalletNew);//生成10個錢包}
console.log("錢包地址列表:", wallets.map(wallet => 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 新增) | 以?只讀方式?調用合約函數,不修改狀態 | 函數返回值 | 任何函數(讀/寫) |
# 代碼實例
# staticCall
const from="0xf39xxx"
const to="0x70xxx"
const result = await TokenContract.transfer.staticCall(to,10,{ // 可選 overridesfrom: from, // 指定調用者(模擬不同賬戶)});console.log('模擬結果:', result);
# callStatic
const result = await TokenContract.transfer.staticCall(to,10,{ // 可選 overridesfrom: 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 =[];//abi
const 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 轉10n
const calldata = TokenContract.interface.encodeFunctionData('transfer', ['0x70997970C51812dc3A010C7d01b50e0d17dc79C8', // 收款地址10n // 轉賬數量 (BigInt)
]);
console.log(calldata)//生成callData
const 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
,識別關鍵通過totalSupplyconst provider = new ethers.JsonRpcProvider("http://127.0.0.1:8545"); const signer = await provider.getSigner();// const TokenAddress = "0x5Fbxxxxx";//合約地址 const TokenABI = []//abi const 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)