如何同步BNB智能合约logs
我们可以直接使用下面代码引入redis的Bean
package com.example.demo;import org.springframework.beans.factory.DisposableBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;@Component
@Lazy(false)
public class SpringContextHolder implements ApplicationContextAware, DisposableBean {private static ApplicationContext applicationContext = null;/*** 取得存储在静态变量中的ApplicationContext.*/public static ApplicationContext getApplicationContext() {return applicationContext;}/*** 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型.*/@SuppressWarnings("unchecked")public static <T> T getBean(String name) {return (T) applicationContext.getBean(name);}/*** 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型.*/public static <T> T getBean(Class<T> requiredType) {return applicationContext.getBean(requiredType);}public static <T> T getBean(String name, Class<T> clazz) {return getApplicationContext().getBean(name, clazz);}/*** 清除SpringContextHolder中的ApplicationContext为Null.*/public static void clearHolder() {applicationContext = null;}/*** 实现ApplicationContextAware接口, 注入Context到静态变量中.*/@Overridepublic void setApplicationContext(ApplicationContext appContext) {applicationContext = appContext;}/*** 实现DisposableBean接口, 在Context关闭时清理静态变量.*/@Overridepublic void destroy() {SpringContextHolder.clearHolder();}
}
Maven配置如下: <dependency><groupId>org.web3j</groupId><artifactId>core</artifactId><version>3.4.0</version> </dependency>
com.odcchina:# 区块链节点地址bscChainUrl: 'https://data-seed-prebsc-1-s1.binance.org:8545/'contracts:# 合约地址bscContractUrl: '0x165fa14332d0ac163513d65D415aD2D692296B4d'
下面的每一段代码写的非常详细,我们需要注意的是我们合约日志如果加了indexed的话请使用: logObject.getTopics();
如果没有加indexed的话可以使用下面代码获取,需要注意的是没有加indexed的日志他是将你那一条所有没有加indexed的参数拼接成字符串的,所以我们需要进行拆分,代码里面也写的非常详细了:
logObject.getData().substring(2, logObject.getData().length());
package com.example.demo.web3jLog;import com.example.demo.util.RedisUtil; import io.micrometer.core.instrument.util.StringUtils; import org.springframework.beans.factory.annotation.Value; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; import org.web3j.abi.EventEncoder; import org.web3j.protocol.Web3j; import org.web3j.protocol.core.DefaultBlockParameter; import org.web3j.protocol.core.methods.request.EthFilter; import org.web3j.protocol.core.methods.response.EthLog; import org.web3j.protocol.http.HttpService;import java.io.IOException; import java.math.BigDecimal; import java.math.BigInteger; import java.util.ArrayList; import java.util.Arrays; import java.util.List;@Component public class Web3jTest {//第一步操作,完成Redis的配置。//第二步操作,完成web3j的maven配置/*** <dependency>* <groupId>org.web3j</groupId>* <artifactId>core</artifactId>* <version>3.4.0</version>* </dependency>*///第三步,配置好数据库的定时任务/*** 启动类上面加上下面注解** @EnableCaching // 启用缓存功能* @EnableScheduling // 开启定时任务功能*///定时任务类加上 @Component注解,方法上面加上 @Scheduled(cron = "*/15 * * * * ?"),里面的表达式自定义设置/*** 需要同步的合约地址*/@Value("${com.odcchina.contracts.bscContractUrl}")private String bscContractUrl;/*** 完成web3的初始化 下面的地址引入区块链节点地址 BNB或者ETH的*/private Web3j bscWeb3j = Web3j.build(new HttpService("https://data-seed-prebsc-1-s1.binance.org:8545/"));/*** 每分钟同步一次,获取区块链最新区块号** @throws IOException*/@Scheduled(initialDelay = 1 * 1000, fixedDelay = 1 * 1000)public void latestBlockTask() throws IOException {//同步最新区块BigInteger latestBlock = bscWeb3j.ethBlockNumber().send().getBlockNumber();RedisUtil.set("end-wen3jLogs", latestBlock.toString());//第一次执行将退20000块开始扫描String start = RedisUtil.get("start-wen3jLogs");if (start == null || start.isEmpty()) {RedisUtil.set("start-wen3jLogs", latestBlock.subtract(BigInteger.valueOf(20000)).toString());}}/*** 开始扫描日志*/@Scheduled(initialDelay = 1 * 1000, fixedDelay = 1 * 1000)public void bscScanTask() {String start = RedisUtil.get("start-wen3jLogs");if (StringUtils.isEmpty(start)) {try {//如果没有块的话将获取块在进行扫描日志latestBlockTask();} catch (IOException e) {return;}}BigInteger scannedBlock = new BigInteger(start);//最新区块BigInteger latestBlock = new BigInteger(RedisUtil.get("end-wen3jLogs"));//一次性同步多少块BigInteger offset;int times = 0;//如果有1块的话就执行 并且 这里考虑性能一次性任务只跑60次 大于2是因为开始块要加1,比如上次同步到了区块5,那么这次要从第6块开始同步。while (latestBlock.subtract(scannedBlock).longValue() > 2 && times++ < 60) {if (latestBlock.longValue() - scannedBlock.longValue() > 10000) {//如果大于1万块的话每次同步500块数据offset = BigInteger.valueOf(500);} else if (latestBlock.longValue() - scannedBlock.longValue() > 1000) {//如果大于1000块的话每次同步200块数据offset = BigInteger.valueOf(200);} else if (latestBlock.longValue() - scannedBlock.longValue() > 100) {//如果大于100块的话每次同步80块数据offset = BigInteger.valueOf(80);} else if (latestBlock.longValue() - scannedBlock.longValue() > 10) {//如果大于10块的话每次同步8块数据offset = BigInteger.valueOf(8);} else if (latestBlock.longValue() - scannedBlock.longValue() > 4) {offset = BigInteger.valueOf(3);} else {//最后一条一条数据同步offset = BigInteger.valueOf(1);}if (latestBlock.subtract(scannedBlock).longValue() < offset.longValue()) {break;}//根据区块值查询日志 如果原来同步到区块5的话 那么现在就从区块6开始同步List<EthLog.LogResult> resp = scanBlock(scannedBlock.add(BigInteger.ONE), scannedBlock.add(offset));for (EthLog.LogResult logItem : resp) {//循环同步下来的地址,筛选我们需要获取的日志handleLogEvent((EthLog.LogObject) logItem.get());}//日志同步完毕,将本次同步的区块数量加到初始区块号上面进行缓存scannedBlock = scannedBlock.add(offset);RedisUtil.set("start-wen3jLogs", scannedBlock.toString());}}/*** 根据区块号查询日志** @param startBlock* @param endBlock* @return*/private List<EthLog.LogResult> scanBlock(BigInteger startBlock, BigInteger endBlock) {EthFilter filter = new EthFilter(DefaultBlockParameter.valueOf(startBlock),DefaultBlockParameter.valueOf(endBlock),Arrays.asList(bscContractUrl));EthLog send = null;try {send = bscWeb3j.ethGetLogs(filter).send();} catch (IOException e) {e.printStackTrace();throw new RuntimeException(e.getMessage());}return send.getLogs();}@Transactionalvoid handleLogEvent(EthLog.LogObject logObject) {{//这段代码会将日志没有加indexed的打印出来,并且组成数组//event TestWeb3jToken2(address indexed userAddress, uint256 indexed order,address userAddress1, uint256 order1);//比如上面这段合约就会将第三个地址userAddress1和第四个order1取出来,我们可以处理一些业务逻辑String data = logObject.getData().substring(2);List<String> dataList = new ArrayList<>(data.length() / 64);int beginIndex = 0;while (beginIndex < data.length()) {dataList.add(data.substring(beginIndex, beginIndex + 64));beginIndex += 64;}}//这就是获取出来的数据,数据是日志加了indexed参数的List<String> topList = logObject.getTopics();String code = handle(logObject);//对TestWeb3jToken1的日志开始处理if (code != null && code.equals("TestWeb3jToken2")) {//下面将进行数据的解析和存储//第0个参数一般是固定 hash直接取String topHash = topList.get(0);//我们第1个参数为地址,需要截取取出 数据状态格式为 0x0000000000000000000000009175f9ecbbddc078e40c5e037ad31f4abf36628aString addsser = topList.get(1);addsser = "0x" + addsser.substring(26, addsser.length());//我们日志里面的第二个参数为订单号,我们截取出来为16进制,需要我们转换成10进制//一般数据格式为 0x00000000000000000000000000000000000000000000000004380663b7c5ec3cString order = topList.get(2);order = order.substring(2, order.length());order = getBin16By10(order) + "" ;{//下面这段代码也是可以处理没有加indexed参数的日志//event TestWeb3jToken2(address indexed userAddress, uint256 indexed order,address userAddress1, uint256 order1);String date = logObject.getData().substring(2, logObject.getData().length());String address = "0x" + date.substring(0, 64).substring(26, addsser.length());String order1 = date.substring(64, date.length());BigInteger typePas = getBin16By10(order1);//上面的参数我们可以进行存储和业务逻辑处理}}//记录事件/*ChainEventLogRecord eventRecord = dslContext.newRecord(CHAIN_EVENT_LOG);eventRecord.setContract(logObject.getAddress());eventRecord.setTxHash(logObject.getTransactionHash());eventRecord.setBlockHash(logObject.getBlockHash());eventRecord.setBlockNum(logObject.getBlockNumber().longValue());eventRecord.setEvent("ignore");eventRecord.setToppics(String.join(",", logObject.getTopics()));eventRecord.setData(logObject.getData());eventRecord.setMd5(Md5Util.md5(eventRecord.getTxHash() + eventRecord.getToppics() + eventRecord.getData()));RedisUtil.lock("MTXM-UMTP:txLock:" + eventRecord.getMd5(), 5);int count = dslContext.selectCount().from(CHAIN_EVENT_LOG).where(CHAIN_EVENT_LOG.MD5.eq(eventRecord.getMd5()).and(CHAIN_EVENT_LOG.TX_HASH.eq(eventRecord.getTxHash()))).fetchOneInto(Integer.class);//下面是为了防止存入重复日志if (count > 0) {log.info("tx_hash = {} 已录入,跳过", eventRecord.getTxHash());RedisUtil.del("MTXM-UMTP:txLock:" + eventRecord.getMd5());return;}if (logObject.getAddress().equalsIgnoreCase(smtAddress)) {smTokenService.handle(logObject, dataList, eventRecord);}eventRecord.store();*/}/*** 需要获取的合约日志* 合约日志代码* TODO TestWeb3jToken(address,uint256) 中间一定不能留空格* event TestWeb3jToken(address indexed userAddress, uint256 indexed order);* event TestWeb3jToken1(address userAddress, uint256 order);* event TestWeb3jToken2(address indexed userAddress, uint256 indexed order,address userAddress1, uint256 order1);*/final String TESTWEB3JTOKEN = EventEncoder.buildEventSignature("TestWeb3jToken(address,uint256)");final String TestWeb3jToken1 = EventEncoder.buildEventSignature("TestWeb3jToken1(address,uint256)");final String TESTWEB3JTOKEN2 = EventEncoder.buildEventSignature("TestWeb3jToken2(address,uint256,address,uint256)");/*** 如果该日志是这个方法打印出来的将该方法返回** @param logObject* @return*/public String handle(EthLog.LogObject logObject) {if (logObject.getTopics().get(0).equalsIgnoreCase(TESTWEB3JTOKEN)) {return "TestWeb3jToken" ;} else if (logObject.getTopics().get(0).equalsIgnoreCase(TestWeb3jToken1)) {return "TestWeb3jToken1" ;} else if (logObject.getTopics().get(0).equalsIgnoreCase(TESTWEB3JTOKEN2)) {return "TestWeb3jToken2" ;}return null;}/*** 16进制转10进制** @param code* @return*/public static BigInteger getBin16By10(String code) {return new BigInteger(code, 16);}}
下面为大家准备了两份合约代码,可以直接运行的,配置上面Java代码可以直接呈现和测试,第一份合约就是一个普通的方法,里面打印了3条不同的日志,第二个合约是日志的定义。
pragma solidity ^0.5.1;import "./web3j_tokenBasic.sol";contract business_token is web3j_tokenBasic{function orderPayBnb() public returns (bool success) {//不做任何业务处理,直接打印3种不同的日志address tsc = 0xa7B049d3A19796B195B0e976ec43EB7a12f07Bf9;emit TestWeb3jToken(msg.sender, 2555);emit TestWeb3jToken1(msg.sender, 3000);emit TestWeb3jToken2(msg.sender, 7000,tsc, 8000);return true;}}
pragma solidity ^0.5.1;contract web3j_tokenBasic {event TestWeb3jToken(address indexed userAddress, uint256 indexed order);event TestWeb3jToken1(address userAddress, uint256 order);event TestWeb3jToken2(address indexed userAddress, uint256 indexed order,address userAddress1, uint256 order1);
}
如何同步BNB智能合约logs相关推荐
- 不同步节点在线使用Remix开发以太坊Dapp及solidity学习入门 ( 一 ):智能合约HelloWorld
有问题可以点击–>加群互相学习 本人本来想自己写公链,结果发现任重道远: 遂,开始写Dapp,顺便写的时候搞个教程吧... 通过系列教程学习将会: 1.基本使用solidity 语言开发智能合约 ...
- 以太坊开发实战:通过truffle-contract与智能合约交互
以太坊开发实战:通过truffle-contract与智能合约交互 与以太坊的智能合约交互,除了使用web3.js,还可以使用另外一个Javascript库,就是truffle-contract.tr ...
- Foundry教程:ERC-20代币智能合约从编写到部署全流程开发
概述 如果你想获得更好的阅读体验,请前往我的博客 本博客的内容主要分为以下四部分: 一是Foundry的介绍与安装,主要介绍为什么选择Foundry进行智能合约开发和安装过程中的各种官方文档中未提及的 ...
- 区块链100讲:Truffle——一个更简单的部署智能合约的方法
本期<区块链100讲>我们将介绍一个更简单的部署智能合约的方法:Truffle. 1 什么是Truffle ? Truffle是针对基于以太坊的Solidity语言的一套开发框架.本身基于 ...
- Truffle - 2 利用Truffle编写、测试智能合约并将其部署到不同的测试网络
2 利用Truffle编写.测试智能合约并将其部署到不同的测试网络 2.1创建项目 建一个文件夹 mkdir truffle-project truffle init //初始化qinjianquan ...
- 使用ganache-cli和truffle构建以太坊智能合约,以实现“基于哈希锁定的跨链技术”为例
目录 配置以太坊开发环境 部署智能合约 调用智能合约 配置以太坊开发环境 系统与工具的版本:Ubuntu 21.04 npm 7.5.2 Ganache CLI v6.12.2 (ganache-co ...
- 一起学:以太坊智能合约开发
课程介绍 无论在科技圈还是金融圈,"区块链"俨然成了最热的词汇.2016年,区块链写入了国家的十三五规划中:2017年,央行基于区块链技术的数字票据交易平台测试成功:同年,工信部发 ...
- solidity开发智能合约
文章目录 1 Solidity与智能合约 2 智能合约概述 3 以太坊简介 4 以太坊交互工具 5 开发环境搭建 5.1 remix在线编译器 5.2 搭建本地网络 5.2.1 安装本地remix-i ...
- 深度解读波卡智能合约平台Gear:通往并行架构公链之路
本篇节选自:深度解读波卡智能合约平台Gear:通往并行架构公链之路 摘要 2021 年 11 月,随着波卡主网正式开启平行链插槽拍卖,波卡生态顿时成为一股耀眼的新势力.其创始人 Gavin Wood ...
最新文章
- 构建一个给爬虫使用的代理IP池
- windows下安装nodejs及框架express
- spring中的IOC和AOP
- USACO2.4のP1522-牛的旅行(Cow Tours)【最短路Flody】
- 洛谷P2347 砝码称重 某一年noip提高组原题
- 【Prince2科普】P2七大主题之变更
- GPU并行计算OpenCL(1)——helloworld
- AV-TEST最新Windows 10平台最佳杀毒软件测试结果
- win7计算机桌面文件位置更改,Win7系统怎么更改桌面文件路径_win7修改桌面文件保存路径的方法...
- 追光的人对Echo,SkyReach的Beta产品测试报告
- C# WPF设备监控软件(经典)-上篇
- Echarts的世界、中国、省份地图
- java web: 上午 org.apache.catalina.core log 信息: 将servlet[***]标记为不可用/或者XXX资源不可用
- MATLAB随机森林回归模型
- 2022年搭载国产芯片的手机推荐 这3款性能就不错
- 多态 在游戏程序实例
- python场景动画_昨夜星辰多媒体情景动画
- SLAM导航机器人零基础实战系列:(四)差分底盘设计——5.底盘PID控制参数整定
- Android学习--RecyclerView的使用
- “0x00000014”内存。该内存不能为“Written”(或“Read”)的解决办法。
热门文章
- 对称加密、非对称加密、公钥、私钥究竟是个啥?
- html显示毒经,谁能肩负剑三PVE王者之名?哪怕职业再强,这点恐怕都比不上毒经...
- OKX和UniSat联手革新比特币区块链上的BRC-20
- python 模拟微信浏览器请求_用chrome模拟微信浏览器访问需要OAuth2.0网页授权的页面...
- 什么是除权(除息)基准价 ?
- Cad二次开发绘图1
- 艾司博讯:拼多多开店选择那些类目好
- 从Preact中了解React组件和hooks基本原理
- python ndarray find_在列表中查找numpy数组的索引(Find index of numpy array in list)
- Linux忘记登陆密码之破解Linux密码