前もって言う
Web3 は新興のネットワーク概念です。いくつかの理由により、アクセスできる関連技術知識は非常に限られています。技術的なボトルネックに遭遇したとき、常に十分な情報を見つけることができません。これは、この知識のギャップを埋めるというアイデアも私に与えました。衝動。コラム「こんにちは Web3」では、私が習得した Web3 の知識を皆さんと共有できるよう全力を尽くします。共有された知識が皆さんのお役に立てれば、著者をフォローしたり、いいねをしたり、サポートしていただければ幸いです。Web3j ツールをgithub にリリースしました。ぜひ使用してスターを付けてください。
Java はスマート コントラクト (Web3j) と対話します。
私がスマート コントラクトと対話するために Java を使用することを選択した理由は、完全に私が Java しか知らないからであり、Java は世界で最高の言語であるからです。
何ができますか
- 契約のステータスを監視し、契約の主要なパラメータを読み取り、バックグラウンド データ ソースとして使用できます。
- 転送や承認などの基本的なやり取り。
- スナップ、ピックアップ、購入などの複雑なインタラクションを実現します。
コードシェア
- 依存関係を導入する
<dependency>
<groupId>org.web3j</groupId>
<artifactId>core</artifactId>
<version>5.0.0</version>
</dependency>
- 新しいWeb3jオブジェクトを作成する
Web3j web3 = Web3j.build(new HttpService(rpcUrl));
- rpcUrl 変数は、ブロックチェーン ネットワーク ノードの URL リンクです。これらのノードは、この URL を通じて呼び出すための多くの標準 API メソッドを提供します。Web3j モジュールは、この API にカプセル化されています。
- さまざまなネットワークの rpcUrl は、対応するブロックチェーン ブラウザ API ドキュメントで見つけることができ、Baidu キーワードも簡単に取得できます。
- 現在のネットワークのガス価格を問い合わせる
// 获取gasPrice方法
BigInteger gasPrice = web3.ethGasPrice().send().getGasPrice();
/**
* 获取当期的gasPrice,如果超过最大的限制,取最大限制
*
* @return 在区间内的gasPrice
* @throws IOException 与节点交互出现异常
*/
public BigInteger getGasPriceWithLimit() throws IOException {
BigInteger gasPrice = web3.ethGasPrice().send().getGasPrice();
log.info("Gas price: {} Gwei, Min gas price: {} Gwei, Max gas price: {} Gwei",
Convert.fromWei(String.valueOf(gasPrice), Convert.Unit.GWEI),
Convert.fromWei(String.valueOf(minGasPrice), Convert.Unit.GWEI),
Convert.fromWei(String.valueOf(maxGasPrice), Convert.Unit.GWEI));
// 超过最大限制返回最大的gasPrice
if (maxGasPrice.compareTo(gasPrice) < 0) {
return maxGasPrice;
}
// 小于最小的限制返回最小的gasPrice
if (minGasPrice.compareTo(gasPrice) > 0) {
return minGasPrice;
}
return gasPrice;
}
- このメソッドは最近のブロックの平均 GasPrice を取得しますが、これは高すぎる可能性があることに注意してください。
- 作者の getGasPriceWithLimit メソッドを参照してください。パニック買いなどの緊急の必要がない場合は、このメソッドの戻り値を直接 GasPrice として使用しないことをお勧めします。
- 現在のアカウントのトランザクション数をクエリし、それをトランザクションのノンスとして使用します
/**
* 获取交易数量
*
* @return 账户交易次数
* @throws IOException 与节点交互失败
*/
public BigInteger getTransactionCount() throws IOException {
EthGetTransactionCount ethGetTransactionCount = web3.ethGetTransactionCount(
ownerAddress, DefaultBlockParameterName.LATEST).send();
return ethGetTransactionCount.getTransactionCount();
}
- Ethereum 仮想マシンは、この nonce フィールドをシーケンスとして使用して、複数のトランザクションを処理します。デフォルトでは、一般的な nonce はアカウント内のトランザクションの数を取得します。
- このトランザクションのナンスが以前に成功したトランザクションよりも小さい場合、トランザクションは失敗します。
- 現在のブロックチェーン ネットワークのチェーンのchainIdをクエリします。
long chainId = web3.ethChainId().send().getChainId().longValue();
- イーサリアムクラシックがイーサリアムからフォークされた後、二重支払い攻撃を防ぐために、EIP155でチェーンIDが導入されました。
- 署名情報にチェーン ID を追加することで、署名後にトランザクションが異なるチェーンで繰り返し送信されるのを防ぎます。
- 秘密キーをインポートします (コード内の秘密キーを github パブリック ウェアハウスに渡してはなりません!!!)
Credentials credentials = Credentials.create(privateKey);
// 私钥对应的地址
String address = credentials.getAddress();
- 見積、署名、取引データの送信
/**
* 与合约交互
*
* @param contractAddress 交互合约地址
* @param functionName 交互函数名称
* @param value 携带的eth数量(单位Ether)
* @param input 输入参数 eg:Arrays.asList(new Address("0x6dF655480F465DC36347a5616E875D155804F0c5"), new Uint256(10000000));
* @param output 输出参数类型 eg: Arrays.asList(new TypeReference<Bool>(){});
* 类型映射关系
* boolean - bool
* BigInteger - uint/int
* byte[] - bytes
* String - string and address types
* List - dynamic/static array
* T - struct/tuple types
* @return 交易hash
* @throws Exception 与节点交互出现异常
*/
public String writeContract(String contractAddress, String functionName, String value, List<Type> input, List<TypeReference<?>> output) throws Exception {
// 转换value的单位
BigInteger valueWei = Convert.toWei(value, Convert.Unit.ETHER).toBigInteger();
// 生成需要调用函数的data
Function function = new Function(functionName, input, output);
String data = FunctionEncoder.encode(function);
// 估算gasLimit
BigInteger gasLimit = estimateGasLimit(contractAddress, data, valueWei);
// 获取gasPrice
BigInteger gasPrice = getGasPriceWithLimit();
// 获取chainId
long chainId = web3.ethChainId().send().getChainId().longValue();
// 正式请求
RawTransaction rawTransaction = RawTransaction.createTransaction(getNonce(), gasPrice, gasLimit, contractAddress, valueWei, data);
// 签名数据
byte[] signedMessage = TransactionEncoder.signMessage(rawTransaction, chainId, credentials);
String hexValue = Numeric.toHexString(signedMessage);
// 发送数据
EthSendTransaction response = web3.ethSendRawTransaction(hexValue).send();
// 查看是否有错误
if (response.hasError()) {
throw new Exception("trade hash: " + response.getTransactionHash() +
"\nerror: " + response.getError().getMessage());
}
log.info("function: {} data: {}", functionName, data);
log.info("Gas fee: {} ETH", Convert.fromWei(String.valueOf(gasLimit.multiply(gasPrice)), Convert.Unit.ETHER));
log.info("Trade Hash: {}", response.getTransactionHash());
return response.getTransactionHash();
}
/**
* 估算GasLimit
*
* @param to 发送的地址
* @param data 发送的数据
* @param value 携带的eth数量(单位wei)
* @return GasLimit
* @throws Exception 与节点交互失败
*/
public BigInteger estimateGasLimit(String to, String data, BigInteger value) throws Exception {
if (gasLimit.intValue() != 0) {
return gasLimit;
}
Transaction testTransaction = Transaction.createFunctionCallTransaction(ownerAddress, null, null, null, to, value, data);
EthEstimateGas response = web3.ethEstimateGas(testTransaction).send();
// 查看是否有错误
if (response.hasError()) {
throw new Exception("error: " + response.getError().getMessage());
}
return response.getAmountUsed();
}
- writeContract関数の関連パラメータの説明については、前回の記事「read関数」を参照してください。
- estimateGasLimit 関数は、トランザクションが今回消費する必要がある Gas の量を計算するために使用されます。実際、トランザクション データをチェーンに転送して実行関数をシミュレートしますが、結果は送信せず、トランザクションに費やされた Gas を返します。リクエスト結果としてシミュレーションを実行します。
- トランザクションに必要なデータをすべて取得したら、実行する前に秘密キーを使用してデータに署名する必要があります
- write 関数を呼び出して転送操作を実行します。
/**
* 转账操作
*
* @param contractAddress 交互合约地址
* @param recipient 接收转账地址
* @param amount 转账数量
* @return 交易hash
* @throws Exception 与节点交互失败
*/
public String transfer(String contractAddress, String recipient, String amount) throws Exception {
List input = Arrays.asList(new Address(recipient)
, new Uint256(new BigInteger(amount, 10)));
List output = Arrays.asList(new TypeReference<Bool>() {
});
return writeContract(contractAddress, "transfer", input, output);
}
これらの知識ポイントを習得する最善の方法は、自分でコードを実行し、チェーンにアクセスして必要な情報を取得することです。
このコラムに注目してください、著者はすべてを知っています~