web3j 查询ERC20 代币余额

5,698 阅读4分钟

 

一.通过web3j 来查询

这里使用的是ropsten 测试网络

1.连接以太网网络

web3j 连接以太网的第一步是要有一个以太网客户端,(例如本地客户端,或云客户端),web3j推荐的使用infura 的云客户端

例如: Web3j web3j  = Web3j.build(new HttpService("ropsten.infura.io/mew")); 这里token是mew,是MyEtherWallet 在infura 的token

我这里使用的是infura的云客户端,可以去infura 注册自己的token,然后就可以连上以太坊网络了

2.获得 ERC20 合约的java 实现版本,这里 web3j 是通过web3j 自带的命令行工具实现的(需要合约的二进制文件 和 abi 接口)

/*TODO通过编译器 获得 bin 有问题,验证的时候,无法验证通过

比如我在remix.ethereum.org 这个在线编译器上编写了一个合约(这个时候可以在remix 的右边compiler 工具框中选择 Details 可以查看合约的二进制代码和 abi 接口)*/

实际上发布了合约之后,应该在区块链浏览器(tetherscan.io)中,验证合约代码,验证通过后,会看到合约的创建的二进制代码,在abi 文件的下面,这里的二进制代码才是完整的,然后生成的合约对象,通过 合约对象.isValid(),返回true ,可以验证通过

在本地启动命令行工具,工具会一闪而过,web3j.bat 不是双击运行,进入文件位置,直接web3j.bat回车 ,出现web3j.io 图标说明成功,然后直接输入web3j 命令.

例如: 需要通过web3j 命令行工具生成 合约的java 对象
*  需要获得合约的二进制代码 保存: 名称.bin
*  需要获得合约的abi数据       保存为: 名称.abi

-o 是输出的文件夹名称

-p 后面是保存的包名
*  命令实例: web3j solidity generate TokenDemo.bin TokenDemo.abi -o /java -p com.your.organisation.name

  1. 获得查询对象余额
package test;

import java.io.IOException;
import java.math.BigDecimal;
import java.math.BigInteger;

import org.web3j.crypto.Credentials;
import org.web3j.protocol.Web3j;
import org.web3j.protocol.admin.Admin;
import org.web3j.protocol.core.DefaultBlockParameterName;
import org.web3j.protocol.core.RemoteCall;
import org.web3j.protocol.http.HttpService;
import org.web3j.tx.Contract;

import contract.MttContract;

/**
 * 获取用户钱包地址 余额,包括 智能合约地址余额
 */
public class TestGetBalance {
	
	
	
	/**
	 * 查询以太币
	 * @param args
	 * @throws Exception 
	 */
	public static void main(String[] args) throws Exception {
		Web3j web3j = getWeb3j(StaticValue.ropstenService);
		getEthBanlance(web3j,StaticValue.account2);
		//获取指定钱包的指定币种余额

		Contract erc20Contract = getERC20Contract(web3j, StaticValue.MttContractAddress, StaticValue.account2Pri);
		getERC20Balance(web3j,erc20Contract,StaticValue.account2,8);
		close(web3j);
	}
	
	/**
	 * 连接网络
	 * @param netUrl
	 * @return
	 */
	public static Web3j getWeb3j(String netUrl){
		Admin web3j = Admin.build(new HttpService(netUrl));
		return web3j;
	}
	
	/**
	 * 关闭网络
	 * @param web3j
	 */
	public static void close(Web3j web3j){
		web3j.shutdown();
	}

	/**
	 * 查询以太币账户余额
	 * @throws IOException
	 */
	public static String getEthBanlance(Web3j web3j,String userAddress) throws IOException {
		//获取指定钱包的以太币余额
		BigInteger integer=web3j.ethGetBalance(userAddress,DefaultBlockParameterName.LATEST).send().getBalance();
		
		
		//eth默认会部18个0这里处理比较随意
		String decimal = toDecimal(18,integer);
		System.out.println(decimal);

		
		//因为是按18位来算的,所有在倒数18位前面加 小数点
		/*String str = integer.toString();
		String decimal = toDecimal(18,str);
		System.out.println(decimal);*/
		return decimal;
	}
	
	
	/**
	 * 获得 erc20 合约 实例
	 * @return
	 */
	public static Contract getERC20Contract(Web3j web3j,String contractAddress,String userPri){
		BigInteger GAS_PRICE = BigInteger.valueOf(22_000_000_000L);
        BigInteger GAS_LIMIT = BigInteger.valueOf(4_300_000);
        
        Credentials credentials = Credentials.create(userPri);//获得信任凭证
		
		MttContract contract = MttContract.load(
				contractAddress, web3j, credentials, GAS_PRICE, GAS_LIMIT);
		return contract;
	}
	
	/**
	 * 查询erc 20 代币价格
	 * @throws Exception 
	 */
	public static String getERC20Balance(Web3j web3j,Contract contract,String userAddress,int decimal) throws Exception{
		
//		Web3ClientVersion web3ClientVersion = web3j.web3ClientVersion().send();//send 是同步请求 
//		Web3ClientVersion web3ClientVersion = web3.web3ClientVersion().sendAsync().get(); //sendAsync(), 使用CompletableFuture(Android上的Future)发送异步请求:
//		String clientVersion = web3ClientVersion.getWeb3ClientVersion();
//		System.out.println(clientVersion);//查看版本信息
		//或者 RxJava Observable:
		/*Web3j web3 = Web3j.build(new HttpService());  // defaults to http://localhost:8545/
		web3.web3ClientVersion().observable().subscribe(x -> {
		    String clientVersion = x.getWeb3ClientVersion();
		    ...
		});*/
		
		
		RemoteCall<BigInteger> balanceOf = ((MttContract) contract).balanceOf(userAddress);
		BigInteger send2 = balanceOf.send();
		String result = toDecimal(8, send2);
		System.out.println(result);
		return result;
	}
	
	
	
	
	
	
	/**
	 * 转换成符合 decimal 的数值
	 * @param decimal
	 * @param str
	 * @return
	 */
	public static String toDecimal(int decimal,BigInteger integer){
//		String substring = str.substring(str.length() - decimal);
		StringBuffer sbf = new StringBuffer("1");
		for (int i = 0; i < decimal; i++) {
			sbf.append("0");
		}
		String balance = new BigDecimal(integer).divide(new BigDecimal(sbf.toString()), 18, BigDecimal.ROUND_DOWN).toPlainString();
		return balance;
	}
	

}

StaticValue.class

package test;

public class StaticValue {

	public static final String ropstenService = "https://ropsten.infura.io/mew";//模仿 myEtherWallet 钱包请求 infura 网络
	
	public static final String account1 = "我的测试钱包地址";
	public static final String account2 = "我的测试钱包地址2";
	
	public static final String account1Pri = "我的测试钱包地址1私钥";//私钥
	public static final String account2Pri = "我的测试钱包地址2私钥";//私钥
	
	public static final String MttContractAddress = "部署的erc20 的合约地址";
	
}

二.通过etherscan.io 的接口来查询ERC20 代币余额

decimal 是合约创建时指定的精度,这里url 是指etherscan连接的网络,例如ropsten测试网络 : api-ropsten.etherscan.io/api

apikey 是指在etherscan 获得token,contractAddress 是合约地址,address 账户地址,

这里请求使用的okhttp3,TestOKHttp 类是自己封装过,对结果的json 进行处理使用的是jackson 2.8.5

/**
	 * https://api.etherscan.io/api?module=account&action=tokenbalance&contractaddress=0x57d90b64a1a57749b0f932f1a3395792e12e7055&address=0xe04f27eb70e025b78871a2ad7eabe85e61212761&tag=latest&apikey=YourApiKeyToken
	 * 查询 账户 ERC20 代币余额
	 * @param url
	 * @param contractAddress
	 * @param address
	 * @param decimal
	 * @return
	 */
	public static String getERC20Balance(String url,String contractAddress,String address,int decimal){
		TreeMap<String,String> treeMap = new TreeMap<>();
		treeMap.put("module", "account");
		treeMap.put("action", "tokenbalance");
		treeMap.put("contractaddress", contractAddress);
		treeMap.put("address", address);
		treeMap.put("tag", "latest");
		treeMap.put("apikey", StaticValue.etherscanToken);
		StringBuffer sbf = new StringBuffer();
		for (String str : treeMap.keySet()) {
			sbf.append(str).append("=").append(treeMap.get(str)).append("&");
		}
		url = url.concat("?").concat(sbf.substring(0, sbf.length() - 1));
		 Object send = send(url);
		 return TestUtil.toDecimal(decimal,new BigInteger(String.valueOf(send)));
	}
	
	/**
	 * 获得以太币价格
	 * @param url
	 * @return
	 */
	public static String getEthPrice(String url){
		TreeMap<String,String> treeMap = new TreeMap<>();
		treeMap.put("module", "stats");
		treeMap.put("action", "ethprice");
		treeMap.put("apikey", StaticValue.etherscanToken);
		StringBuffer sbf = new StringBuffer();
		for (String str : treeMap.keySet()) {
			sbf.append(str).append("=").append(treeMap.get(str)).append("&");
		}
		url = url.concat("?").concat(sbf.substring(0, sbf.length() - 1));
		 Object send = send(url);
		 return String.valueOf(send);
	}
	
	public static Object send(String url){
		String responseMessageGet = TestOKHttp.getResponseMessageGet(url);
		 ObjectMapper mapper = new ObjectMapper();
		 Map<String,String> reMap = null;
		 try {
			reMap = mapper.readValue(responseMessageGet, new TypeReference<Map<String,Object>>(){});
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		 
		 return reMap.get("result");
	}

 decimal 方法是转换精度的,以太坊默认是18,但其他代币就不一定,

/**
	 * 转换成符合 decimal 的数值
	 * @param decimal
	 * @param str
	 * @return
	 */
	public static String toDecimal(int decimal,BigInteger integer){
//		String substring = str.substring(str.length() - decimal);
		StringBuffer sbf = new StringBuffer("1");
		for (int i = 0; i < decimal; i++) {
			sbf.append("0");
		}
		String balance = new BigDecimal(integer).divide(new BigDecimal(sbf.toString()), 18, BigDecimal.ROUND_DOWN).toPlainString();
		return balance;
	}

TestOKHttp 类

/**
	 * okhttp get 请求
	 */
	public static ResponseBody testGetClient(String url){
		OkHttpClient okClient = new OkHttpClient();
		
		Request request = new Request.Builder()
		  .get().url(url).build();
		ResponseBody resBody = null;
		try {
			
			Response response = okClient.newCall(request).execute();
			resBody = response.body();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return resBody;
	}

	
	public static String getResponseMessageGet(String url) {
		ResponseBody body = null;String string = null;
		body = testGetClient(url);
		if(checkEmpty(body)) return null;
		try {
			string = body.string();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return string;
		
	}
	
	public static boolean checkEmpty(Object obj){
		if(null!=obj && !obj.toString().trim().equals("")){
			return false;
		}
		return true;
		
	}

转载请注明来处