手把手教你区块链java开发智能合约nft(第四篇)-如何动态获取gasPrice和gasLimit?

手把手教你区块链java开发智能合约nft(第三篇)-如何动态获取gasPrice和gasLimit?

初学区块链,那真叫一个痛苦并无助。如果没有人带你的话

今天写的这篇是在前面文章基础上写的,初学区块链的朋友建议先看我前面写的文章
手把手教你区块链java开发智能合约nft-第一篇
手把手教你区块链java开发智能合约nft-第二篇(部署第一个NFT智能合约)

什么是gas?什么是gasPrice?什么是gasLimit?

关于这几个概念,可以点击参考官方的说明

开发中怎么给入gasPrice和gasLimit?

对于这个问题,刚入门的我就非常吐槽了,在区块链上任何操作EVM的,都会耗用一定的gas,给少了矿工就罢工了,直接报错

刚入门的我真不了解这些,利用web3j 依赖进行java开发,在部署智能合约时,采用默认的方式获取gasPrice和gasLimit,demo如下

 		Web3j web3j = Web3j.build(new HttpService("http://192.168.159.101:9545/"));
        BigInteger chainId = web3j.ethChainId().send().getChainId();
        RawTransactionManager transactionManager = new RawTransactionManager(web3j, getCredentials(),chainId.longValue());
        NFT721 nft721 = NFT721.deploy(web3j,transactionManager,new DefaultGasProvider()),

然后就是使劲的报错,报错内容拿去网上搜索,还真搜索不到什么内容,简直就是一脸的懵逼,问了有一定经验的大佬,也不鸟我,最最无助的就是使劲的报错,却哪里也查不出问题

后面就超了官网上的某个地方,看到几个配置值,就随便配置了对应的值,竟然神奇的成功了

 NFT721 nft721 = NFT721.deploy(web3j,transactionManager,new StaticGasProvider(BigInteger.valueOf(22_000_000_000l),BigInteger.valueOf(6_700_000l)),

然后就一直用这个值进行开发测试,完事后向领导汇报情况,说当前这两个值配置死了,配置其他的都报错,后面领导说这个不能写死,要动态获取,于是,开始探索动态获取的方式

动态获取gasPrice

gasPrice动态获取还是比较简单的,因为可以直接调用web3j依赖的api,就能获取到

  private static BigInteger getGasPrice(Web3j web3j){
    
    
    BigInteger gasPrice=null;
    try {
    
    
      EthGasPrice send = web3j.ethGasPrice().send();
      gasPrice=send.getGasPrice();
    } catch (IOException e) {
    
    
      log.error("can't get gasPrice from private chain ,load default gasPrice:[{}],exception log:{}",gasPrice,e);
    }
    return gasPrice;
  }

这样就能获取到了动态gasPrice了,这个值是对应到节点上的gasPrice的值,如在geth attach上查看:

> eth.gasPrice
1000000000

动态获取gasLimit

获取gasLimit相对于gasPrice不同,不能直接获取到,需要计算即将要操作的方法,通过调用

      EthEstimateGas send = web3j.ethEstimateGas(transaction).send();
      gasLimit=send.getAmountUsed();

获取gasLimit值,难点主要是在transaction上

我在网上查阅了一番,发现资料少之又少,基本上没人给出怎么获取gasLimit预估值
于是就自己跟着前端发送的websocket去看,几乎所有的操作智能合约的,发送的数据获取gasLimit大概如下:

{
    
    
    "id": 141,
    "jsonrpc": "2.0",
    "method": "eth_estimateGas",
    "params": [
        {
    
    
            "data": "0x42966c68000000000000000000000000000000000000000000000000000000000000050a",
            "from": "0x2dd277910fca14b15e5e086dbac6732d6f397ef7",
            "to": "0x0b6a1b596a3d5817f3d34322a0ec013ca5439842"
        }
    ]
}

于是我就开始去研究transaction了,主要包含三个数据,from,to,data
from: 就是操作账户地址
to: 所要操作的智能合约的地址
data: 就是所要操作方法的参数加密串(加密串是我的理解,实际上是对方法进行加密)

那具体要怎么操作呢?

上代码:

  private static BigInteger getGasLimit(Web3j web3j,String from,String to,String data){
    
    
    BigInteger gasLimit=BigInteger.valueOf(GasConstant.GAS_LIMIT);;
    try {
    
    
      Transaction transaction = Transaction.createFunctionCallTransaction(from, null, null, null,to,data);
      EthEstimateGas send = web3j.ethEstimateGas(transaction).send();
      gasLimit=send.getAmountUsed();
      log.info("got gasLimit:[{}] from chain",gasLimit);
    } catch (IOException e) {
    
    
      log.error("can't get gasLimit from private chain,load default gasLimit:[{}],exception log :{}",gasLimit,e);
    }
    return gasLimit;
  }

这是封装了获取gasLimit的方法,根据传入参数就能获取到gasLimit

那怎么组装data?
我这里以智能合约Token 转账为例

    public static void transfer(String address,String recipient,String tokenAddress,String amount){
    
    
        Function transferFunction = Token.getTransferFunction(recipient, new BigInteger(amount));
        Token token = getToken(address, tokenAddress, FunctionEncoder.encode(transferFunction));

        try {
    
    
            token.transfer(transferFunction).sendAsync();
        } catch (Exception e) {
    
    
            log.error("transfer failure:",e);
            throw new BusinessException(40502,"transfer failure:"+e.getMessage());
        }
    }
      private static Token getToken(String address,String contractAddress,String data){
    
    
        Token load = Token.load(contractAddress, web3j, Web3Util.getTransactionManager(web3j,address), 		                    getContractGasProvider(web3j,address,contractAddress,data));
        return load;
    }
  public static ContractGasProvider getContractGasProvider(Web3j web3j,String from,String to,String data){
    
    
    BigInteger gasPrice = getGasPrice(web3j);
    BigInteger gasLimit = getGasLimit(web3j, from,to,data);
    log.info("ContractGasProvider gasPrice:{},gasLimit:{}",gasPrice,gasLimit);
    StaticGasProvider staticGasProvider = new StaticGasProvider(gasPrice,gasLimit );
    return staticGasProvider;
  }

其中,getTransferFunction方法和transfer 方法是我自己手动加入的方法,但实际上我是参考了Token转账的方法参数进行的组装
原转账方法如下:

    public RemoteFunctionCall<TransactionReceipt> transfer(String recipient, BigInteger amount) {
    
    
        final org.web3j.abi.datatypes.Function function = new org.web3j.abi.datatypes.Function(
                FUNC_TRANSFER, 
                Arrays.<Type>asList(new Address(recipient),
                new Uint256(amount)),
                Collections.<TypeReference<?>>emptyList());
        return executeRemoteCallTransaction(function);
    }

现在增加如下方法:

//这个是调用转账的方法
    public RemoteFunctionCall<TransactionReceipt> transfer(org.web3j.abi.datatypes.Function function ) {
    
    
        return executeRemoteCallTransaction(function);
    }

//获取即将调用操作的方法function,该方法会将参数和参数值组装为一个Function对象
    public static org.web3j.abi.datatypes.Function getTransferFunction(String recipient, BigInteger amount) {
    
    
        final org.web3j.abi.datatypes.Function function = new org.web3j.abi.datatypes.Function(
                FUNC_TRANSFER,
                Arrays.<Type>asList(new Address(recipient),
                        new Uint256(amount)),
                Collections.<TypeReference<?>>emptyList());
        return function;
    }

这样,就能预估出gasLimit的值,动态获取gasLimit了

如果操作的是其他的方法,自行修改对应的function方法和参数就可以了,当然,操作时也要记得改为自己对应的方法

猜你喜欢

转载自blog.csdn.net/huangxuanheng/article/details/125685416