Java Web3J 使用指南

  • Post author:
  • Post category:小程序

Web3J 是一个轻量级、高度模块化、反应式、类型安全的 JavaAndroid 库,用于处理智能合约并与以太坊网络上的客户端(节点)集成。这使您可以使用以太坊区块链,而无需为平台编写自己的集成代码的额外开销。

提供的功能

  • 基于 HTTP 和 IPC 的以太坊 JSON-RPC 客户端 API 的完整实现
  • 以太坊钱包支持
  • 自动生成 Java 智能合约包装器,以从本机 Java 代码创建、部署、交易和调用智能合约(支持 Solidity 和 Truffle 定义格式)
  • 以太坊名称服务 (ENS) 支持
  • 支持 Alchemy 和 Infura,因此您不必自己运行以太坊客户端
  • 安卓兼容
  • 命令行工具

如何使用

<dependency>
  <groupId>org.web3j</groupId>
  <artifactId>core</artifactId>
  <version>4.8.7</version>
</dependency>
public class Main {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        Web3j client = Web3j.build(new HttpService(""https://ropsten.infura.io/v3/You Infura Project Id""));
        Web3ClientVersion clientVersion = client.web3ClientVersion().sendAsync().get();
        System.out.println(clientVersion.getWeb3ClientVersion());
        // => Geth/v1.10.15-omnibus-hotfix-f4decf48/linux-amd64/go1.17.6
    }
}
使用 Web3J 获取以太坊账户余额
public class Main {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        Web3j client = Web3j.build(new HttpService(""https://ropsten.infura.io/v3/You Infura Project Id""));
        EthGetBalance ethGetBalance = client.ethGetBalance(
                ""0x64f44b31ad0ed4537f94a5c084cfba8945463345"",
                DefaultBlockParameterName.fromString(DefaultBlockParameterName.LATEST.name())
        ).sendAsync().get();
        System.out.println(ethGetBalance.getBalance());
        // => 741270235881990866
    }
}
使用 Web3J 获取当前的 Gas 价格
public class Main {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        Web3j client = Web3j.build(new HttpService(""https://ropsten.infura.io/v3/You Infura Project Id""));
        EthGasPrice ethGasPrice = client.ethGasPrice().sendAsync().get();
        System.out.println(ethGasPrice.getGasPrice());
        // => 41493167936
    }
}
使用 Web3J 通过交易哈希获取交易详情
public class Main {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        Web3j client = Web3j.build(new HttpService(""https://ropsten.infura.io/v3/You Infura Project Id""));
        String transactionHash = ""0x9030edd43f8ae6c4ed49bcbc11dd7d6f6ce2798e8bb1c5ea4f1e130780fec74a"";
        EthGetTransactionReceipt ethGetTransactionReceipt = client.ethGetTransactionReceipt(transactionHash).sendAsync().get();
        TransactionReceipt transactionReceipt = ethGetTransactionReceipt.getTransactionReceipt().orElseThrow(RuntimeException::new);
        System.out.println(transactionReceipt);
        // => TransactionReceipt{transactionHash='0x9030edd43f8ae6c4ed49bcbc11dd7d6f6ce2798e8bb1c5ea4f1e130780fec74a', transactionIndex='0x27', blockHash='0xf0f28e6040d3440abb4abb1da59bf144fc2e210c4d321061d58da3fa3b011b0f', blockNumber='0xb84dee', cumulativeGasUsed='0x79d27a', gasUsed='0xc704', contractAddress='null', root='null', status='0x1', from='0x5ba191f4b0d70b778d9877e217dfb2c5daa353be', to='0xf80a32a835f79d7787e8a8ee5721d0feafd78108', logs=[Log{removed=false, logIndex='0x21', transactionIndex='0x27', transactionHash='0x9030edd43f8ae6c4ed49bcbc11dd7d6f6ce2798e8bb1c5ea4f1e130780fec74a', blockHash='0xf0f28e6040d3440abb4abb1da59bf144fc2e210c4d321061d58da3fa3b011b0f', blockNumber='0xb84dee', address='0xf80a32a835f79d7787e8a8ee5721d0feafd78108', data='0x00000000000000000000000000000000000000000000152d02c7e14af6800000', type='null', topics=[0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x0000000000000000000000000000000000000000000000000000000000000000, 0x0000000000000000000000005ba191f4b0d70b778d9877e217dfb2c5daa353be]}], logsBloom='0x80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000008000000000000000000000000000000000000000000000000020000000000000000000800000000000000000000000010000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000002000000000000000200000000000000000000000000000000000020000000000200000000000000000000000000000000000000000000000000000000', revertReason='null', type='0x2', effectiveGasPrice='0x15fb4c0517'}
    }
}
使用 Web3J 订阅新的区块
Web3J 的函数式编程的特性让我们设置观察者很容易,我们可以设置订阅者,监听链上发生的一些事情,如出块,交易,日志等。
public class Main {
    public static void main(String[] args) throws ExecutionException, InterruptedException, ConnectException {
        WebSocketService webSocketService = new WebSocketService(""wss://ropsten.infura.io/ws/v3/You Infura Project Id"", true);
        webSocketService.connect();
        Web3j client = Web3j.build(webSocketService);
        Disposable subscribe = client.replayPastBlocksFlowable(DefaultBlockParameterName.fromString(""earliest""), true).subscribe(ethBlock -> {
            System.out.println(ethBlock.getBlock());
            // => org.web3j.protocol.core.methods.response.EthBlock$Block@39fcf950
        });
    }
}
使用 Web3J 订阅新的交易
public class Main {
    public static void main(String[] args) throws ExecutionException, InterruptedException, ConnectException {
        WebSocketService webSocketService = new WebSocketService(""wss://ropsten.infura.io/ws/v3/You Infura Project Id"", true);
        webSocketService.connect();
        Web3j client = Web3j.build(webSocketService);
        Disposable subscribe = client.replayPastTransactionsFlowable(DefaultBlockParameterName.fromString(""earliest"")).subscribe(transaction -> {
            System.out.println(transaction);
            // => org.web3j.protocol.core.methods.response.EthBlock$TransactionObject@47b0a71a
        });
    }
}
使用 Web3J 订阅新的合约事件
public class Main {
    public static void main(String[] args) throws ExecutionException, InterruptedException, ConnectException {
        WebSocketService webSocketService = new WebSocketService(""wss://ropsten.infura.io/ws/v3/You Infura Project Id"", true);
        webSocketService.connect();
        Web3j client = Web3j.build(webSocketService);
        Disposable subscribe = client.ethLogFlowable(
                new EthFilter(
                        DefaultBlockParameterName.EARLIEST,
                        DefaultBlockParameterName.LATEST,
                        // 合约地址
                        ""0x7b52aae43e962ce6a9a1b7e79f549582ae8bcff9""
                )
        ).subscribe(event -> {
            System.out.println(event);
            // => Log{removed=false, logIndex='0x39', transactionIndex='0x13', transactionHash='0x9c3653c27946cb39c3a256229634cfedbca54f146a740f46b661b986590f8358', blockHash='0x1e674b9d111601519f977b75f9a1865ba359b474550ff5d0d87daec1f543d64d', blockNumber='0xb83ccd', address='0x7b52aae43e962ce6a9a1b7e79f549582ae8bcff9', data='0x', type='null', topics=[0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x0000000000000000000000000000000000000000000000000000000000000000, 0x0000000000000000000000006ab3d0bd875f8667026d2a9bb3060ec44380bcc2, 0x0000000000000000000000000000000000000000000000000000000000000017]}
        }, Throwable::printStackTrace);
    }
}
使用 Web3J 签名并发送交易
public class Main {
    public static void main(String[] args) throws ExecutionException, InterruptedException, ConnectException {
        Web3j client = Web3j.build(new HttpService(""https://ropsten.infura.io/v3/You Infura Project Id""));
    <span class=""hljs-comment"">// 获取 nonce 值</span>
    <span class=""hljs-type"">EthGetTransactionCount</span> <span class=""hljs-variable"">ethGetTransactionCount</span> <span class=""hljs-operator"">=</span> client
            .ethGetTransactionCount(<span class=""hljs-string"">""0x64f44b31ad0ed4537f94a5c084cfba8945463345""</span>, DefaultBlockParameterName.PENDING)
            .sendAsync().get();
    <span class=""hljs-type"">BigInteger</span> <span class=""hljs-variable"">nonce</span> <span class=""hljs-operator"">=</span> ethGetTransactionCount.getTransactionCount();
    System.out.println(nonce);

    <span class=""hljs-comment"">// 构建交易</span>
    <span class=""hljs-type"">RawTransaction</span> <span class=""hljs-variable"">etherTransaction</span> <span class=""hljs-operator"">=</span> RawTransaction.createEtherTransaction(
            nonce,
            client.ethGasPrice().sendAsync().get().getGasPrice(),
            DefaultGasProvider.GAS_LIMIT,
            <span class=""hljs-string"">""0x64f44b31ad0ed4537f94a5c084cfba8945463345""</span>,
            Convert.toWei(<span class=""hljs-string"">""0.001""</span>, Convert.Unit.ETHER).toBigInteger()
    );
    System.out.println(etherTransaction);

    <span class=""hljs-comment"">// 加载私钥</span>
    <span class=""hljs-type"">Credentials</span> <span class=""hljs-variable"">credentials</span> <span class=""hljs-operator"">=</span> Credentials.create(<span class=""hljs-string"">""You Private Key""</span>);

    <span class=""hljs-comment"">// 使用私钥签名交易并发送</span>
    <span class=""hljs-type"">byte</span>[] signature = TransactionEncoder.signMessage(etherTransaction, credentials);
    <span class=""hljs-type"">String</span> <span class=""hljs-variable"">signatureHexValue</span> <span class=""hljs-operator"">=</span> Numeric.toHexString(signature);
    <span class=""hljs-type"">EthSendTransaction</span> <span class=""hljs-variable"">ethSendTransaction</span> <span class=""hljs-operator"">=</span> client.ethSendRawTransaction(signatureHexValue).sendAsync().get();
    System.out.println(ethSendTransaction.getResult());
}
}
使用 Web3J 生成合约包装类
Web3J 可以生成智能合约的包装类,方便使用纯 Java 代码与合约进行交互。
$ web3j generate solidity -a ./contract.abi -o ./ -p com.contract.proxy
              _      _ 
             | |    | ()
_      | |      / /
\ \ /\ / / _ \ '_ \     \ \ |
 \ V  V /  / |) |./ / |
  _/_/ _|./ __/| |
                         / |
                        |_/
by Web3Labs
Generating com.contract.proxy.Contract …
Warning: Duplicate field(s) found: [FUNC_SAFETRANSFERFROM]. Please don't use names which will be the same in uppercase.
File written to .

$ tree com
com
└── contract
    └── proxy
        └── Contract.java # 包装类
2 directories, 1 file
使用 Web3J 与合约进行交互
public class Main {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        Web3j client = Web3j.build(new HttpService(""https://ropsten.infura.io/v3/You Infura Project Id""));
        Credentials credentials = Credentials.create(""You Private Key"");
        BigInteger currentGasPrice = client.ethGasPrice().sendAsync().get().getGasPrice();
        Contract foundersKeyContract = Contract.load(""0x7b52aae43e962ce6a9a1b7e79f549582ae8bcff9"", client, credentials, new DefaultGasProvider() {
            /**
             * 使用动态获取的 Gas Price
             * @return
             */
            @Override
            public BigInteger getGasPrice() {
                return currentGasPrice;
            }
        });
        BigInteger balance = foundersKeyContract.balanceOf(""0x64f44b31ad0ed4537f94a5c084cfba8945463345"").send();
        System.out.println(balance);
        // => 41493167936
    }
}