日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

Springboot+web3j(4.7)+实战+填坑

發(fā)布時(shí)間:2024/1/1 javascript 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Springboot+web3j(4.7)+实战+填坑 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

實(shí)現(xiàn)功能:獲取合約event數(shù)據(jù)(相當(dāng)于日志)。


中文文檔
目前我找的比較好的文檔是 匯智網(wǎng) 的,java以太坊庫web3j文檔


搭建項(xiàng)目
Springboot版本

<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.3.4.RELEASE</version><relativePath/> <!-- lookup parent from repository --></parent>

web3j依賴

<!--Java 操作智能合約 開始--><!--web3j-spring-boot-starter使用的web3j版本為3.x。本項(xiàng)目使用web3j的4.x版本--><!-- <dependency>--><!-- <groupId>org.web3j</groupId>--><!-- <artifactId>web3j-spring-boot-starter</artifactId>--><!-- <version>1.6.0</version>--><!-- </dependency>--><!-- https://mvnrepository.com/artifact/org.jetbrains.kotlin/kotlin-stdlib --><!--springboot 2.3.4 不需要,2.1.7 需要 --><!-- <dependency>--><!-- <groupId>org.jetbrains.kotlin</groupId>--><!-- <artifactId>kotlin-stdlib</artifactId>--><!-- <version>1.3.70</version>--><!-- </dependency>--><!--okhttp與logging-interceptor可根據(jù)項(xiàng)目實(shí)際情況選擇是否添加--><dependency><groupId>com.squareup.okhttp3</groupId><artifactId>okhttp</artifactId><version>4.3.1</version></dependency><dependency><groupId>com.squareup.okhttp3</groupId><artifactId>logging-interceptor</artifactId><version>4.3.1</version></dependency><dependency><groupId>org.web3j</groupId><artifactId>core</artifactId><version>4.7.0</version></dependency><!--Java 操作智能合約 結(jié)束-->

fasterxml依賴

<!--這個(gè)依賴可根據(jù)項(xiàng)目實(shí)際情況選擇是否添加--><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-core</artifactId><version>2.11.2</version></dependency><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-annotations</artifactId><version>2.11.2</version></dependency><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId><version>2.11.2</version></dependency>

web3j maven plugin
我們把合約文件(.sol)放在resources目錄下,運(yùn)行插件,即可生成合約對(duì)應(yīng)的Java類。這個(gè)插件會(huì)根據(jù)你的合約版本自動(dòng)下載對(duì)應(yīng)的solidity編譯器,真正實(shí)現(xiàn)一鍵生成合約java類,非常好用,老外就是牛皮。

<!--mvn web3j:generate-sources--><plugin><groupId>org.web3j</groupId><artifactId>web3j-maven-plugin</artifactId><version>4.6.5</version><configuration><packageName>com.contract</packageName><sourceDestination>src/main/java/generated</sourceDestination><nativeJavaType>true</nativeJavaType><outputFormat>java,bin</outputFormat><soliditySourceFiles><directory>src/main/resources</directory><includes><include>**/*.sol</include></includes></soliditySourceFiles><outputDirectory><java>src/java/generated</java><bin>src/bin/generated</bin><abi>src/abi/generated</abi></outputDirectory><contract><!--<includes>--><!-- <include>Chip</include>--><!--</includes>--><!--<excludes>--><!-- <exclude>Hello</exclude>--><!-- <exclude>Chip</exclude>--><!--</excludes>--></contract><pathPrefixes><pathPrefix>dep=../dependencies</pathPrefix></pathPrefixes></configuration></plugin>

創(chuàng)建event過濾器

package com.fc.task.contract.config;import com.fc.task.contract.contract.Chip; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Scope; import org.web3j.crypto.Credentials; import org.web3j.protocol.Web3j; import org.web3j.protocol.core.DefaultBlockParameterName; import org.web3j.protocol.core.methods.request.EthFilter; import org.web3j.protocol.http.HttpService; import org.web3j.tx.gas.DefaultGasProvider;import java.io.IOException;/*** @author ydw* @version 1.0* @date 2020/11/6 10:26*/ @Configuration public class ContractConfigDemo {/*** 為Spring容器注入一個(gè)web3j實(shí)例。* 方便我們后續(xù)用 @Autowired 調(diào)用web3j* <p>* web3j Infura 模塊提供了一個(gè)Infura Http 客戶端(InfuraHttpService),* 它為Infura特定的Infura-Ethereum-Preferred-Client提供支持。* 你可以指定geth或Parity客戶端響應(yīng)你的請(qǐng)求,* 并且可以像普通的HTTPClient一樣創(chuàng)建客戶端。* <p>* 首先,你需要在 infura.io 注冊(cè)并創(chuàng)建一個(gè)project,* 得到你的 https://mainnet.infura.io/v3/xxxxxxxxxxxxxxxxxxxx (主網(wǎng))* or https://rinkeby.infura.io/v3/xxxxxxxxxxxxxxxxxxx (測試網(wǎng))** @return*/@Beanpublic Web3j web3j() {String ip = "https://mainnet.infura.io/v3/xxxxxxxxxxxxxxxxxxxx";Web3j web3j = Web3j.build(new HttpService(ip));return web3j;}/*** @param chip 智能合約java類* @param web3j web3j客戶端* @return*/@Bean(name = "inviteFilter") // 如果你有多個(gè)過濾器,你需要指定每個(gè)過濾器的名字@Scope("prototype") // 你可能要同時(shí)監(jiān)聽多個(gè)事件,那么就不能使用同一個(gè)實(shí)例,因此這里需要每次都生成一個(gè)新的對(duì)象public EthFilter ethFilter(Chip chip, Web3j web3j) throws IOException {// 兩種方式生成過濾器// 方式一:通過智能合約java類// // 獲取當(dāng)前區(qū)塊鏈的區(qū)塊 // BigInteger startBlockNum = web3j.ethBlockNumber().send().getBlockNumber(); // return new EthFilter( // // 開始區(qū)塊 // DefaultBlockParameter.valueOf(startBlockNum), // // 直接設(shè)置開始區(qū)塊為初始區(qū)塊。當(dāng)這樣設(shè)置時(shí),我們?cè)诤罄m(xù)監(jiān)聽event(智能合約的概念,相當(dāng)于日志)事件時(shí),會(huì)得到初始區(qū)塊到最新區(qū)塊之間的所有數(shù)據(jù)。 // // DefaultBlockParameterName.EARLIEST, // // // 結(jié)束區(qū)塊。這里直接監(jiān)聽最后一個(gè)區(qū)塊,即最新的區(qū)塊 // DefaultBlockParameterName.LATEST, // // // 智能合約地址 // // 如果監(jiān)聽不到,這里的地址可以把 0x 去掉 // chip.getContractAddress() // );// 方式二:通過智能合約地址。這種方式不需要我們把智能合約轉(zhuǎn)為java類,更加靈活。String contractAddress = "";String eventTopics = "";return new EthFilter(DefaultBlockParameterName.EARLIEST,DefaultBlockParameterName.LATEST,contractAddress)// 這里我在創(chuàng)建過濾器時(shí),就直接指定eventTopics即指定我要監(jiān)聽的event,// 也可以先不指定,例如方式一,在后續(xù)使用時(shí)再指定。.addSingleTopic(eventTopics);}/*** 往Spring容器注入智能合約Java類** @param web3j* @return* @throws IOException*/@Beanpublic Chip chip(Web3j web3j) throws IOException {// 加載智能合約,線上智能合約與本地java類映射Chip chip;// 鏈上智能合約地址(部署智能合約后得到的地址)String chipAddress = ""; // // 方式一:通過 org.web3j.tx.transactionManager // String yourMetaMaskAddress = ""; // TransactionManager transactionManager = new ClientTransactionManager(web3j, yourMetaMaskAddress); // Chip chip = Chip.load(chipAddress, web3j, transactionManager, new DefaultGasProvider());// 方式二:通過 org.web3j.crypto.Credentials// MetaMask(小狐貍,谷歌瀏覽器的一個(gè)插件,需要翻墻安裝,方便進(jìn)行智能合約交互) 私鑰或其他類似于Metamask 產(chǎn)品的私鑰String privateKey = "";Credentials credentials = Credentials.create(privateKey);chip = Chip.load(chipAddress, web3j, credentials, new DefaultGasProvider());return chip;} }

創(chuàng)建監(jiān)聽器

package com.fc.task.contract.listener;import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.ApplicationArguments; import org.springframework.boot.ApplicationRunner; import org.springframework.stereotype.Component; import org.web3j.protocol.Web3j; import org.web3j.protocol.core.methods.request.EthFilter;/*** @author ydw* @version 1.0* @date 2020/11/6 11:15*/ @Component public class ContractListenerDemo implements ApplicationRunner {@AutowiredWeb3j web3j;@Autowired@Qualifier("inviteFilter") // 你自己創(chuàng)建的過濾器EthFilter inviteFilter;@Overridepublic void run(ApplicationArguments args) throws Exception {this.inviteFilterHandle();}private void inviteFilterHandle() {// 當(dāng)你的過濾器沒有指定event topic時(shí)// 方式一:使用智能合約Java類// inviteFilter.addSingleTopic(EventEncoder.encode(Chip.ADDREWARDCHIP_EVENT));// 方式二:從 etherscan.io 中獲取/*** 合約被部署到以太坊后,可通過合約地址查詢合約的信息,* 其中就能看到event,而event中就有你想要的topic* */// String eventTopic = "";// inviteFilter.addSingleTopic(eventTopic);// 因?yàn)槲业?inviteFilter 在創(chuàng)建時(shí)就已經(jīng)指定了topic,所以我在這里就再指定。web3j.ethLogFlowable(inviteFilter).subscribe(log->{System.out.println("收到事件inviteFilter");// 在合約中,當(dāng)event的emit函數(shù)的參數(shù)被index修飾,這里表現(xiàn)為log中的topics,// 否則它們將會(huì)出現(xiàn)在data中。 });} }

過濾器和監(jiān)聽器結(jié)合使即可完成監(jiān)聽合約event功能。


定時(shí)任務(wù)獲取event數(shù)據(jù)
當(dāng)我們使用監(jiān)聽器獲取數(shù)據(jù)時(shí),很有可能會(huì)漏數(shù)據(jù),這時(shí)我們需要補(bǔ)救措施,我這里使用的是定時(shí)器5秒獲取一次數(shù)據(jù)。
這里我們需要用到 https://etherscan.io/ 的api。首先你要去這個(gè)網(wǎng)站注冊(cè)賬號(hào),得到自己的apikey。主網(wǎng)API說明點(diǎn)這里

package com.fc.service.mananger;import com.alibaba.fastjson.JSONObject; import org.apache.http.HttpEntity; import org.apache.http.NameValuePair; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.utils.URIBuilder; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.message.BasicNameValuePair; import org.apache.http.util.EntityUtils; import org.springframework.stereotype.Component;import java.io.IOException; import java.net.URI; import java.util.*;/*** @author ydw* @version 1.0* @date 2020/11/6 11:40*/ @Component public class EtherscanApiDemo {public void getUserRelationship() {String startBlockNumber = "";// 開始區(qū)塊String inviteContractAddress = "";// 合約地址String apiKey = "";// etherscan.io 中你自己的apikeyString inviteContractTopic = ""; // 合約 event 的 topicString url = "";// url: "https://api-cn.etherscan.com/api"; 主網(wǎng)// url: "https://api-rinkeby.etherscan.io/api" 測試網(wǎng)Map<String, Object> parameters = new HashMap<>(7);parameters.put("module", "logs");parameters.put("action", "getLogs");parameters.put("fromBlock", startBlockNumber);parameters.put("toBlock", "latest");parameters.put("address", inviteContractAddress);parameters.put("topic0", inviteContractTopic);parameters.put("apikey", apiKey);String response = HttpUtil.doGet(url, parameters);TransactionsResponse tr = JSONObject.parseObject(response, TransactionsResponse.class);} }import org.apache.http.HttpEntity; import org.apache.http.NameValuePair; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.utils.URIBuilder; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.message.BasicNameValuePair; import org.apache.http.util.EntityUtils;import java.io.IOException; import java.net.URI; import java.util.ArrayList; import java.util.List; import java.util.Map;/*** httpClient工具類** @author heyi*/ class HttpUtil {private final static int NORMAL_STATUS = 200;/*** get請(qǐng)求處理** @param url* @param args* @return*/public static String doGet(String url, Map<String, Object>... args) {//創(chuàng)建默認(rèn)的httpClient實(shí)例CloseableHttpClient httpClient = HttpClients.createDefault();//httpResponse響應(yīng)對(duì)象CloseableHttpResponse response = null;//響應(yīng)返回結(jié)果String resultString = "";try {//創(chuàng)建uriURIBuilder builder = new URIBuilder(url);if (args.length > 0) {args[0].forEach((k, v) -> builder.addParameter(k, String.valueOf(v)));}URI uri = builder.build();// 創(chuàng)建http GET請(qǐng)求HttpGet httpGet = new HttpGet(uri);// 執(zhí)行請(qǐng)求response = httpClient.execute(httpGet);// 判斷返回狀態(tài)是否為200if (response != null && response.getStatusLine().getStatusCode() == NORMAL_STATUS) {// 獲取響應(yīng)實(shí)體HttpEntity httpEntity = response.getEntity();resultString = EntityUtils.toString(httpEntity, "UTF-8");}} catch (Exception e) {e.printStackTrace();} finally {try {if (response != null) {response.close();}httpClient.close();} catch (IOException e) {e.printStackTrace();}}return resultString;}/*** post請(qǐng)求處理** @param url* @param args args[0] formdata args[1] header* @return*/public static String doPost(String url, Map<String, Object>... args) {//創(chuàng)建默認(rèn)的httpClient實(shí)例CloseableHttpClient httpClient = HttpClients.createDefault();//httpResponse響應(yīng)對(duì)象CloseableHttpResponse response = null;//響應(yīng)返回結(jié)果String resultString = "";try {// 創(chuàng)建Http Post請(qǐng)求HttpPost httpPost = new HttpPost(url);if (args.length > 1) {args[1].forEach((k, v) -> httpPost.setHeader(k, String.valueOf(v)));}// 創(chuàng)建參數(shù)列表if (args.length > 0) {List<NameValuePair> formParams = new ArrayList<NameValuePair>();args[0].forEach((k, v) -> formParams.add(new BasicNameValuePair(k, String.valueOf(v))));// 模擬表單UrlEncodedFormEntity entity = new UrlEncodedFormEntity(formParams, "UTF-8");httpPost.setEntity(entity);}//執(zhí)行http請(qǐng)求response = httpClient.execute(httpPost);//獲取響應(yīng)結(jié)果// 判斷返回狀態(tài)是否為200if (response != null && response.getStatusLine().getStatusCode() == NORMAL_STATUS) {resultString = EntityUtils.toString(response.getEntity(), "UTF-8");}} catch (Exception e) {e.printStackTrace();} finally {try {if (response != null) {response.close();}httpClient.close();} catch (IOException e) {e.printStackTrace();}}return resultString;}/*** post請(qǐng)求處理** @param url* @return*/public static String doPostJson(String url, String json) {//創(chuàng)建默認(rèn)的httpClient實(shí)例CloseableHttpClient httpClient = HttpClients.createDefault();//httpResponse響應(yīng)對(duì)象CloseableHttpResponse response = null;//響應(yīng)返回結(jié)果String resultString = "";try {// 創(chuàng)建Http Post請(qǐng)求HttpPost httpPost = new HttpPost(url);// 創(chuàng)建參數(shù)列表httpPost.setHeader("Content-Type", "application/json;charset=UTF-8");StringEntity se = new StringEntity(json, "UTF-8");se.setContentType("application/json");httpPost.setEntity(se);//執(zhí)行http請(qǐng)求response = httpClient.execute(httpPost);//獲取響應(yīng)結(jié)果// 判斷返回狀態(tài)是否為200if (response != null && response.getStatusLine().getStatusCode() == NORMAL_STATUS) {resultString = EntityUtils.toString(response.getEntity(), "UTF-8");}} catch (Exception e) {e.printStackTrace();} finally {try {if (response != null) {response.close();}httpClient.close();} catch (IOException e) {e.printStackTrace();}}return resultString;} }class TransactionsResponse {private String status;private String message;private List<Transactions> result = new ArrayList<Transactions>();public TransactionsResponse() {}public String getStatus() {return status;}public void setStatus(String status) {this.status = status;}public String getMessage() {return message;}public void setMessage(String message) {this.message = message;}public List<Transactions> getResult() {return result;}public void setResult(List<Transactions> result) {this.result = result;}@Overridepublic String toString() {return "TransactionsResponse [status=" + status + ", message=" + message + ", result=" + result + "]";}}class Transactions {private String blockNumber;private String timeStamp;private String hash;private String nonce;private String blockHash;private String transactionIndex;private String from;private String to;private String value;private String gas;private String gasPrice;private String isError;private String txreceipt_status;private String input;private String contractAddress;private String cumulativeGasUsed;private String gasUsed;private String confirmations;private String address;private String[] topics;private String data;private String logIndex;private String transactionHash;public String getAddress() {return address;}public void setAddress(String address) {this.address = address;}public String[] getTopics() {return topics;}public void setTopics(String[] topics) {this.topics = topics;}public String getData() {return data;}public void setData(String data) {this.data = data;}public String getLogIndex() {return logIndex;}public void setLogIndex(String logIndex) {this.logIndex = logIndex;}public String getTransactionHash() {return transactionHash;}public void setTransactionHash(String transactionHash) {this.transactionHash = transactionHash;}public Transactions() {}public String getBlockNumber() {return blockNumber;}public void setBlockNumber(String blockNumber) {this.blockNumber = blockNumber;}public String getTimeStamp() {return timeStamp;}public void setTimeStamp(String timeStamp) {this.timeStamp = timeStamp;}public String getHash() {return hash;}public void setHash(String hash) {this.hash = hash;}public String getNonce() {return nonce;}public void setNonce(String nonce) {this.nonce = nonce;}public String getBlockHash() {return blockHash;}public void setBlockHash(String blockHash) {this.blockHash = blockHash;}public String getTransactionIndex() {return transactionIndex;}public void setTransactionIndex(String transactionIndex) {this.transactionIndex = transactionIndex;}public String getFrom() {return from;}public void setFrom(String from) {this.from = from;}public String getTo() {return to;}public void setTo(String to) {this.to = to;}public String getValue() {return value;}public void setValue(String value) {this.value = value;}public String getGas() {return gas;}public void setGas(String gas) {this.gas = gas;}public String getGasPrice() {return gasPrice;}public void setGasPrice(String gasPrice) {this.gasPrice = gasPrice;}public String getIsError() {return isError;}public void setIsError(String isError) {this.isError = isError;}public String getTxreceipt_status() {return txreceipt_status;}public void setTxreceipt_status(String txreceipt_status) {this.txreceipt_status = txreceipt_status;}public String getInput() {return input;}public void setInput(String input) {this.input = input;}public String getContractAddress() {return contractAddress;}public void setContractAddress(String contractAddress) {this.contractAddress = contractAddress;}public String getCumulativeGasUsed() {return cumulativeGasUsed;}public void setCumulativeGasUsed(String cumulativeGasUsed) {this.cumulativeGasUsed = cumulativeGasUsed;}public String getGasUsed() {return gasUsed;}public void setGasUsed(String gasUsed) {this.gasUsed = gasUsed;}public String getConfirmations() {return confirmations;}public void setConfirmations(String confirmations) {this.confirmations = confirmations;}@Overridepublic String toString() {return "Transactions{" +"blockNumber='" + blockNumber + '\'' +", timeStamp='" + timeStamp + '\'' +", hash='" + hash + '\'' +", nonce='" + nonce + '\'' +", blockHash='" + blockHash + '\'' +", transactionIndex='" + transactionIndex + '\'' +", from='" + from + '\'' +", to='" + to + '\'' +", value='" + value + '\'' +", gas='" + gas + '\'' +", gasPrice='" + gasPrice + '\'' +", isError='" + isError + '\'' +", txreceipt_status='" + txreceipt_status + '\'' +", input='" + input + '\'' +", contractAddress='" + contractAddress + '\'' +", cumulativeGasUsed='" + cumulativeGasUsed + '\'' +", gasUsed='" + gasUsed + '\'' +", confirmations='" + confirmations + '\'' +", address='" + address + '\'' +", topics=" + Arrays.toString(topics) +", data='" + data + '\'' +", logIndex='" + logIndex + '\'' +", transactionHash='" + transactionHash + '\'' +'}';} }

本文有引用這篇文章 web3j 的 Infura Http 客戶端

總結(jié)

以上是生活随笔為你收集整理的Springboot+web3j(4.7)+实战+填坑的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。