Web3j中长十六进制字符串转BigInteger遇NumberFormatException求助
问题描述
我在使用Web3j调用ERC1155合约的balanceOf方法时,尝试将一个长十六进制字符串转换为BigInteger作为tokenId传入,但始终抛出NumberFormatException。以下是相关代码和异常信息:
触发异常的代码
public void getBalance1155(String walletAddress) throws ExecutionException, InterruptedException { //define constant values Web3j web3j=Web3j.build(new HttpService("https://mainnet.infura.io/v3/<apiKey>")); String contractAddress = "0xfaaFDc07907ff5120a76b34b731b278c38d6043C"; BigInteger tokenId=new BigInteger("0x1800000000001289000000000000000000000000000000000000000000000000",16); NoOpProcessor processor = new NoOpProcessor(web3j); Credentials credentials = Credentials.create("privatekey"); TransactionManager txManager = new FastRawTransactionManager(web3j, credentials, processor); //Query Blockchain to get balance of WALLETADDRESS from Contract for given TokenID ERC1155 token = ERC1155.load(contractAddress, web3j, txManager, DefaultGasProvider.GAS_PRICE, DefaultGasProvider.GAS_LIMIT); RemoteCall<BigInteger> sendCall = token.balanceOf(walletAddress, tokenId); BigInteger balance=sendCall.sendAsync().get(); log.info("balance >>>>>>>> " +balance); }
异常信息
java.lang.NumberFormatException: For input string: "0x1800000000001289000000000000000000000000000000000000000000000000"
at java.base/java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
at java.base/java.lang.Long.parseLong(Long.java:692)
at java.base/java.lang.Long.parseLong(Long.java:817)
问题分析
问题出在BigInteger的构造逻辑上:当我们指定第二个参数为16(表示按十六进制解析)时,BigInteger的构造器不识别0x前缀。此时字符串中的0x会被当作普通字符处理,但x并不是有效的十六进制字符,因此直接触发格式异常。
异常栈中出现Long.parseLong是因为Web3j内部在处理ERC1155的tokenId时存在类型转换逻辑,但根本原因还是初始的BigInteger构造错误。
解决方案
通过移除十六进制字符串的0x前缀后再解析,就能成功创建BigInteger对象。修复后的代码如下:
String tokenIdStr = "0x1800000000001289000000000000000000000000000000000000000000000000"; BigInteger tokenId; if (tokenIdStr.startsWith("0x")) { // 移除0x前缀,按十六进制解析为BigInteger tokenId = new BigInteger(tokenIdStr.substring(2), 16); } else { // 兼容不带前缀的十六进制字符串(可选) tokenId = new BigInteger(tokenIdStr, 16); }
将这段代码替换原代码中BigInteger tokenId=new BigInteger(...)的部分,再调用balanceOf方法就可以正常获取余额了。
内容的提问来源于stack exchange,提问作者Gladiator9120

