From 6c572cdd0c7ea71314d8fbe1dcd93519e75ee752 Mon Sep 17 00:00:00 2001 From: 0xEthan Date: Mon, 9 Feb 2026 18:02:38 +0800 Subject: [PATCH] feat: use multicall3 to Batch Query Wallet Balance --- src/lang/en.js | 2 +- src/lang/zh.js | 2 +- src/views/BulkQuery.vue | 114 ++++++++++++++++++++++++++++++---------- 3 files changed, 87 insertions(+), 31 deletions(-) diff --git a/src/lang/en.js b/src/lang/en.js index b8b863d..0b7dd38 100644 --- a/src/lang/en.js +++ b/src/lang/en.js @@ -102,7 +102,7 @@ export default { enterWalletAddress: "Wallet", enterWalletAddressPrompt: "One address per line", inquireResult: "Result", - list: ["Wallet", "Token", "Balance"], + list: ["Wallet", "Token", "Balance","Decimals"], bntCheckBalance: "Get Balance", btnExportExcel: "Download Excel", exportExcelPrompt: "No balance data", diff --git a/src/lang/zh.js b/src/lang/zh.js index 5780049..7dce408 100644 --- a/src/lang/zh.js +++ b/src/lang/zh.js @@ -102,7 +102,7 @@ export default { enterWalletAddress: "钱包地址", enterWalletAddressPrompt: "一行输入一个地址", inquireResult: "查询结果", - list: ["钱包地址", "代币", "余额"], + list: ["钱包地址", "代币", "余额","精度"], bntCheckBalance: "查询余额", btnExportExcel: "下载 Excel", exportExcelPrompt: "你当前还没有查询余额,请查询余额后再进行下载表格", diff --git a/src/views/BulkQuery.vue b/src/views/BulkQuery.vue index 303143a..f0631c3 100644 --- a/src/views/BulkQuery.vue +++ b/src/views/BulkQuery.vue @@ -16,11 +16,13 @@
{{$t("bulkQuery.inquireResult")}}
- + - + - + + +
@@ -31,6 +33,7 @@
{{$t('bulkQuery.list[1]')}}: {{item.token}}
{{$t('bulkQuery.list[2]')}}: {{item.balance}}
+
{{$t('bulkQuery.list[3]')}}: {{item.decimal}}
@@ -143,6 +146,7 @@ export default { this.searchResult = [] let walletBalance = [] let token = [] + let decimals = [] let walletAddress = this.walletAddress.split('\n') let provider = new ethers.providers.InfuraProvider('mainnet') if (this.walletAddress == '') { @@ -150,17 +154,46 @@ export default { return } this.isloading = true + + let multicall3Addr = "0xcA11bde05977b3631167028862bE2a173976CA11" + let abi = [ + 'function aggregate(tuple(address target, bytes callData)[] calls) payable returns (uint256 blockNumber, bytes[] returnData)', + 'function getEthBalance(address addr) view returns (uint256 balance)', + 'function balanceOf(address) view returns (uint256)', + 'function symbol() view returns (string)', + 'function decimals() view returns (uint8)', + ] + const iface = new ethers.utils.Interface(abi); + let multicall3 = new ethers.Contract( + multicall3Addr, + abi, + provider + ) if (this.tokenAdress == '') { + let calls = [] for (let i in walletAddress) { - try { - walletBalance[i] = await ( - await provider.getBalance(walletAddress[i]) - )._hex - token[i] = 'ETH' - } catch (error) { - walletBalance[i] = '查询失败' - } + calls.push({ + target: multicall3Addr, + callData: iface.encodeFunctionData("getEthBalance", [ + walletAddress[i], + ]), + }) } + try { + const result = await multicall3.callStatic.aggregate(calls); + const returnData = result.returnData; + for (let i = 0; i < returnData.length; i++) { + const balanceHex = returnData[i]; + const balance = ethers.BigNumber.from(balanceHex).toString(); + walletBalance.push(balance) + token.push('ETH') + decimals.push(18) + } + } catch (error) { + for (let i in walletAddress) { + walletBalance.push('查询失败') + } + } } else { try { var source = await provider.getCode(this.tokenAdress) @@ -171,33 +204,56 @@ export default { this.$message(this.$t('bulkQuery.checkBalancePrompt[1]')) return } - let abi = [ - 'function balanceOf(address) view returns (uint256)', - 'function symbol() view returns (string)', - ] - let contract = new ethers.Contract( - this.tokenAdress, - abi, - provider - ) + + let calls = [] for (let i in walletAddress) { - try { - let balance = await contract.balanceOf(walletAddress[i]) - walletBalance.push(balance._hex) - let tokenAddress = await contract.symbol() - token[i] = tokenAddress - } catch (error) { + calls.push({ + target: this.tokenAdress, + callData: iface.encodeFunctionData("balanceOf", [ + walletAddress[i], + ]), + }) + calls.push({ + target: this.tokenAdress, + callData: iface.encodeFunctionData("symbol", []), + }) + calls.push({ + target: this.tokenAdress, + callData: iface.encodeFunctionData("decimals", []), + }) + } + + try { + const result = await multicall3.callStatic.aggregate(calls); + const returnData = result.returnData; + for (let i = 0; i < returnData.length; i += 3) { + const balanceHex = returnData[i]; + const symbolBytes = returnData[i + 1]; + const decimalHex = returnData[i + 2]; + + const balance = ethers.BigNumber.from(balanceHex).toString(); + const symbol = ethers.utils.toUtf8String(symbolBytes); + const decimal = ethers.BigNumber.from(decimalHex).toString(); + + walletBalance.push(balance); + token.push(symbol); + decimals.push(decimal); + } + } catch (error) { + for (let i in walletAddress) { walletBalance.push('查询失败') } } + } + for (let i in walletAddress) { - walletBalance[i] = parseInt(walletBalance[i], 16) + walletBalance[i] = ethers.utils.formatUnits(walletBalance[i], decimals[i]) let data = { walletAddress: walletAddress[i], balance: walletBalance[i], - // token:token[i] - token: 'ETH', + token:token[i], + decimal: decimals[i], } this.searchResult.push(data) }