赞
踩
java也有对web3的支持,其依赖叫做web3j。
web3j对于智能合约的调用与交互,是通过将其智能合约的sol文件编译成对应的java文件来实现的。
web3j对于sol文件的编译有两种,
一:在IDE项目中通过Maven/Gradle插件编译
maven配置如下:
<build> <plugins> <!-- web3j插件。该插件会将.sol格式的solidity语法代码生成为对应语法的java文件。 --> <!-- 参考:https://cloud.tencent.com/developer/article/1373951 --> <plugin> <groupId>org.web3j</groupId> <artifactId>web3j-maven-plugin</artifactId> <version>4.8.7</version> <configuration> <!-- 指定.sol文件所在的资源目录,插件将会扫描该目录。 --> <soliditySourceFiles> <directory>src/main/resources/solidity</directory> <includes> <!-- 指定扫描以.sol结尾的文件格式 --> <include>**/*.sol</include> </includes> </soliditySourceFiles> <!-- 指定将.sol文件编译为.java文件后的输出路径 --> <outputDirectory> <java>src/main/java/com/nb/common/contract/generated/java</java> <bin>src/main/java/com/nb/common/contract/generated/bin</bin> <abi>src/main/java/com/nb/common/contract/generated/abi</abi> </outputDirectory> </configuration> </plugin> </plugins> </build>
二:使用独立的Web3j CLI工具编译。
一开始,我是使用第一种方式,也就是maven插件来编译sol文件的,对于结构简单的智能合约sol文件来讲,是比较顺利的。
但是对于博饼路由器(PancakeRouter)这类一个代码文件中具有多个合约/接口并存在继承关系的sol合约文件来讲,web3j的maven编译插件并不能很好的支持它,很多时候要么是内部各个接口的solidity版本不同导致编译不了,要么就是报note that nightly builds are considered to be strictly less than the released version这类错误。
那么,需要做的,就是能够顺利编译这类合约文件。
后来我又查看了一遍web3j的官方文档,发现官方有提供一个叫Web3j CLI工具,经过尝试,发现使用这个工具可以完美解决问题,成功的将博饼路由器(PancakeRouter)合约编译为java文件。
Web3j CLI的工具最好在centos系统下使用,但需要配备以下二个环境
第一个是solc环境,通过npm安装,
第二个是jdk环境。
我的Web3j CLI是安装在docker的nojdes镜像容器实例,因为这样可以直接使用npm。
1,进入nodejs容器实例
bash-4.2# docker exec -it --user root nodejs bash
2,更新yum
bash-4.2# yum update
3,全局安装指定为0.6.6版本的solc
0.6.6版本不仅为solc版本,也代表所支持的solidity智能合约版本
bash-4.2# sudo npm install -g solc@0.6.6
4,安装jdk环境
参考 https://blog.csdn.net/chinatopno1/article/details/104935100
5,安装Web3j CLI
bash-4.2# curl -L get.web3j.io | sh && source ~/.web3j/source.sh
6,查看版本,以确认是否安装成功
bash-4.2# web3j -v
_ _____ _
| | |____ (_)
__ _____| |__ / /_
\ \ /\ / / _ \ '_ \ \ \ |
\ V V / __/ |_) |.___/ / |
\_/\_/ \___|_.__/ \____/| |
_/ |
|__/
by Web3Labs
Version: 1.4.1
Build timestamp: 2021-02-16 20:28:33.742 UTC
7,先编译.sol文件为.bin和.abi文件
# PancakeRouter.sol为文件名 pancakerouter-builded/为输出目录 bash-4.2# solcjs PancakeRouter.sol --bin --abi --optimize -o pancakerouter-builded/ # 查看编译结果 bash-4.2# ls PancakeRouter_sol_IERC20.abi PancakeRouter_sol_IWETH.abi PancakeRouter_sol_IERC20.bin PancakeRouter_sol_IWETH.bin PancakeRouter_sol_IPancakeFactory.abi PancakeRouter_sol_PancakeLibrary.abi PancakeRouter_sol_IPancakeFactory.bin PancakeRouter_sol_PancakeLibrary.bin PancakeRouter_sol_IPancakePair.abi PancakeRouter_sol_PancakeRouter.abi PancakeRouter_sol_IPancakePair.bin PancakeRouter_sol_PancakeRouter.bin PancakeRouter_sol_IPancakeRouter01.abi PancakeRouter_sol_SafeMath.abi PancakeRouter_sol_IPancakeRouter01.bin PancakeRouter_sol_SafeMath.bin PancakeRouter_sol_IPancakeRouter02.abi PancakeRouter_sol_TransferHelper.abi PancakeRouter_sol_IPancakeRouter02.bin PancakeRouter_sol_TransferHelper.bin
8,根据生成后的.abi和.bin文件,编译为.java文件
# 格式:web3j generate solidity -b <文件名>.bin -a <文件名>.abi -o <输出目录> -p <包路径> bash-4.2# web3j generate solidity -b pancakerouter-builded/PancakeRouter_sol_PancakeRouter.bin -a pancakerouter-builded/PancakeRouter_sol_PancakeRouter.abi -o pancakerouter-builded/ -p com.nb.common.contract.generated.java.org.web3j.model _ _____ _ | | |____ (_) __ _____| |__ / /_ \ \ /\ / / _ \ '_ \ \ \ | \ V V / __/ |_) |.___/ / | \_/\_/ \___|_.__/ \____/| | _/ | |__/ by Web3Labs Generating com.nb.common.contract.generated.java.org.web3j.model.PancakeRouter_sol_PancakeRouter ... File written to pancakerouter-builded
接着到你指定的输出目录就能看到编译后的java文件了。
9.这个时候我们把编译为java文件的智能合约代码添加项目工程中,通过web3j可以直接来实现交互了。
比如我这里要同pancakerouter合约交互,将其一些需要交互的过程封装成一个类,方便后续的开发。
package com.nb.common.contract.datainteraction; import com.nb.common.contract.generated.java.org.web3j.model.PancakeRouterSolPancakeRouter; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.web3j.crypto.Credentials; import org.web3j.protocol.Web3j; import org.web3j.protocol.core.DefaultBlockParameterName; import org.web3j.protocol.core.RemoteFunctionCall; import org.web3j.protocol.core.methods.response.EthBlock; import org.web3j.protocol.core.methods.response.TransactionReceipt; import org.web3j.tx.gas.DefaultGasProvider; import org.web3j.tx.gas.StaticGasProvider; import org.web3j.utils.Convert; import java.math.BigDecimal; import java.math.BigInteger; import java.util.ArrayList; import java.util.List; public class PancakeSwapTransaction { private final static Logger logger = LoggerFactory.getLogger(PancakeSwapTransaction.class); private Web3j web3j; private Credentials walletCredentials; //private String pancakeRouterAddress; private static final String defaultPancakeRouterAddress = "0x10ED43C718714eb63d5aA57B78B54704E256024E"; //private String netTokenAddress; private static final String defaultNetTokenAddress = "0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c"; private static final String usdtAddress = "0x55d398326f99059fF775485246999027B3197955"; private static final String defaultGasPriceTheBuy = "5000000000"; private static final String defaultGasLimitTheBuy = "269380"; private static final String defaultGasPriceTheSell = "5000000000"; private static final String defaultGasLimitTheSell = "516087"; private PancakeRouterSolPancakeRouter pancakeRouter; @Deprecated public PancakeSwapTransaction() { } public PancakeSwapTransaction(Web3j web3j, Credentials walletCredentials) { this.web3j = web3j; this.walletCredentials = walletCredentials; this.pancakeRouter = PancakeRouterSolPancakeRouter.load(defaultPancakeRouterAddress, web3j, walletCredentials, new DefaultGasProvider()); } public PancakeRouterSolPancakeRouter getPancakeRouterInstance(){ return this.pancakeRouter; } public TransactionReceipt buy(String contractAddress,String usdtAmount) throws Exception { return buy(contractAddress, usdtAmount, null,null ); } public TransactionReceipt buy(String contractAddress,String usdtAmount,String gasPrice,String gasLimit) throws Exception { BigDecimal amountInput = Convert.toWei(usdtAmount, Convert.Unit.ETHER); BigDecimal amountOutput = new BigDecimal("1"); List<String> path = new ArrayList<>(); path.add(usdtAddress); path.add(defaultNetTokenAddress); path.add(contractAddress); EthBlock.Block block = this.web3j.ethGetBlockByNumber(DefaultBlockParameterName.LATEST, false).send().getBlock(); logger.info("current number:"+block.getNumber()); BigInteger timestampUnix = block.getTimestamp(); logger.info("time add Before:"+timestampUnix); timestampUnix = timestampUnix.add(new BigInteger("1000")); logger.info("time add After:"+timestampUnix); StaticGasProvider staticGasProvider = new StaticGasProvider(StringUtils.isEmpty(gasPrice) ? new BigInteger(defaultGasPriceTheBuy) : new BigInteger(gasPrice) , StringUtils.isEmpty(gasLimit) ? new BigInteger(defaultGasLimitTheBuy) : new BigInteger(gasLimit)); this.pancakeRouter.setGasProvider(staticGasProvider); RemoteFunctionCall<TransactionReceipt> transactionReceiptRemoteFunctionCall = this.pancakeRouter.swapExactTokensForTokens(amountInput.toBigInteger(), amountOutput.toBigInteger(), path, walletCredentials.getAddress(), timestampUnix); TransactionReceipt buyResult = transactionReceiptRemoteFunctionCall.send(); return buyResult; } public TransactionReceipt sell(String contractAddress,String tokenAmount) throws Exception { return sell(contractAddress, tokenAmount, null,null ); } public TransactionReceipt sell(String contractAddress,String tokenAmount,String gasPrice,String gasLimit) throws Exception{ BigDecimal amountInput = new BigDecimal(tokenAmount); BigDecimal amountOutput = new BigDecimal("1"); List<String> path = new ArrayList<>(); path.add(contractAddress); path.add(defaultNetTokenAddress); path.add(usdtAddress); EthBlock.Block block = this.web3j.ethGetBlockByNumber(DefaultBlockParameterName.LATEST, false).send().getBlock(); logger.info("current number:"+block.getNumber()); BigInteger timestampUnix = block.getTimestamp(); logger.info("time add Before:"+timestampUnix); timestampUnix = timestampUnix.add(new BigInteger("1000")); logger.info("time add After:"+timestampUnix); StaticGasProvider staticGasProvider = new StaticGasProvider(StringUtils.isEmpty(gasPrice) ? new BigInteger(defaultGasPriceTheSell) : new BigInteger(gasPrice) , StringUtils.isEmpty(gasLimit) ? new BigInteger(defaultGasLimitTheSell) : new BigInteger(gasLimit)); this.pancakeRouter.setGasProvider(staticGasProvider); TransactionReceipt sellResult = this.pancakeRouter.swapExactTokensForTokensSupportingFeeOnTransferTokens(amountInput.toBigInteger(), amountOutput.toBigInteger(), path, walletCredentials.getAddress(), timestampUnix).send(); return sellResult; } }
@Test
public void PancakeTransactionTest() throws Exception{
Web3j web3jObj = Web3j.build(new HttpService("http://链上地址/"));
Credentials wallet = WalletUtils.loadJsonCredentials("自己的密码", "json文");
PancakeSwapTransaction pancakeSwapTransaction = new PancakeSwapTransaction(web3jObj, wallet);
TransactionReceipt buy = pancakeSwapTransaction.buy("0x地址", "2");
System.out.println(buy);
String tokenBalance = balanceOf("0x地址", wallet.getAddress());
TransactionReceipt sell = pancakeSwapTransaction.sell("0x地址", tokenBalance);
System.out.println(sell);
}
今天尝试将uniswapv2Router2合约编译为java文件,但是中间使用solc转换为abi和bin文件时报错"solcjs ParserError: Source not found:xxxx File import callback not supporte"
尝试了一些解决办法,发现还是不太容易通过solc来转换,但是其中一篇文章中(点我查看)则是介绍,可以用网页版IDE remix 来将其转换为abi/和bin文件的,随后再通过web3j文件将其abi和bin转换为java文件。本人通过此办法解决。
参考:https://docs.web3j.io/4.8.7/quickstart/
https://help.aliyun.com/document_detail/102372.html
http://www.blogjava.net/waterjava/archive/2019/07/08/434107.html
https://blog.csdn.net/chinatopno1/article/details/104935100
https://blog.csdn.net/cangyuemis/article/details/111928457
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。