首页 > 数据库 >同步合约数据到数据库经典案例1

同步合约数据到数据库经典案例1

时间:2023-04-07 18:32:42浏览次数:56  
标签:同步 logs process 数据库 案例 values address date let

之前在《数字藏品发行平台的架构》里讲过,有一种架构希望以区块链的数据为核心。这样就需要将合约保存在区块链上的数据同步到数据库里,方便后续中间件接口的开发。

本次我们以同步ConfirmSale事件日志为例,介绍数据同步程序开发的相关知识点。

一、先上全部代码

```javascript

const { ethers } = require("hardhat");
var mysql = require("mysql");
let dotenv = require('dotenv')
dotenv.config({ path: "./.env" })

var connection = mysql.createConnection({
  host: process.env.MYSQL_HOST,
  port: process.env.MYSQL_PORT,
  user: process.env.MYSQL_USER,
  password: process.env.MYSQL_PASSWORD,
  database: process.env.MYSQL_DATABASE,
  charset: process.env.MYSQL_CHARSET
});

function DbQuery(sqlstr, values) {
  return new Promise((resolve, reject) => {
    connection.query(sqlstr, [values], (error, results) => {
      if (error) {
        reject(error);
      } else {
        resolve(results);
      }
    });
  });
}

async function main() {
  let addr = "salePlainAddress"; // 此处使用交易合约地址
  let blockNum = await ethers.provider.getBlockNumber();
  let topic = ethers.utils.id("ConfirmSale(address,uint256,address,address,address,uint256)");
  let iface = new ethers.utils.Interface([
    "event ConfirmSale(address indexed _contract, uint _tokenId, address indexed solder, address indexed buyer, address token, uint _price)"
  ]);

  let values = [];
  let filter = {
    address: addr,
    fromBlock: 11040000,
    toBlock: blockNum,
    topics: [topic]
  }
  let logs = await ethers.provider.getLogs(filter);
  if (logs.length > 0) {
    for (i in logs) {
      let blockinfo = await ethers.provider.getBlock(logs[i].blockNumber);
      let date = new Date(blockinfo.timestamp * 1000);
      let dt = date.getFullYear() + "-" + (date.getMonth() < 9 ? '0' + (date.getMonth()+1) : (date.getMonth()+1)) + "-" + (date.getDate() < 10 ? '0' + date.getDate() : date.getDate());
      let h = date.getHours();
      let saleInfo = iface.decodeEventLog("ConfirmSale", logs[i].data, logs[i].topics);
      values.push([saleInfo._contract, saleInfo._tokenId, saleInfo.solder, saleInfo.buyer, saleInfo.token, saleInfo._price, logs[i].blockNumber, logs[i].transactionHash, blockinfo.timestamp, dt, h]);
    }
    let sqlstr = "insert into meta_order (`contract`,`tokenID`,`seller`,`buyer`,`token`,`price`,`blocknum`,`txhash`,`timestamp`,`dt`,`h`) values ? on duplicate key update contract=values(contract), tokenID=values(tokenID), seller=values(seller), buyer=values(buyer), token=values(token), price=values(price), blocknum=values(blocknum), timestamp=values(timestamp), dt=values(dt), h=values(h)";
    let s = await DbQuery(sqlstr, values);
    console.log(s);
  } else {
    console.log("have no eventlogs");
  }
}

main()
  .then(() => {
    connection.end();
    process.exit(0);
  })
  .catch(error => {
    console.error(error);
    process.exit(1);
  })

```

 二、相关知识点介绍

  1. event logs

在Solidity代码中,使用event关键字来定义一个事件,如:event EventName(address bidder, uint amount);

这个用法和定义函数式一样的,并且事件在合约中同样可以被继承。触发一个事件使用emit(说明,之前的版本里并不需要使用emit),如:emit EventName(msg.sender, msg.value);

触发事件可以在任何函数中调用,如:

```javascript

function testEvent() public {
    emit EventName(msg.sender, msg.value);
}

```

在我们的实例程序中,数字藏品交易合约定义了5种event log,分别是:商品上架、更新商品信息、取消上架、订单确认和拍卖出价。

```javascript

interface ISales{
    //events
    event CreateSale(address indexed _contract, uint _tokenId, address seller, address token, uint _price, uint _due);
    event UpdateSale(address indexed _contract, uint _tokenId, address seller, address token, uint _price, uint _due);
    event CancelSale(address indexed _contract, uint _tokenId);
    event ConfirmSale(address indexed _contract, uint _tokenId, address indexed solder, address indexed buyer, address token, uint _price);
    event NewBid(address indexed _contract, uint _tokenId, address indexed bidder, address token, uint _price);
    
    //other code
}

```

我们需要将这5种事件从区块链同步下来,存入activity表中,作为基础数据,方便我们进行数据处理。

同时我们将event ConfirmSale单独拿出来,因为该事件数据作可以存入order为订单表,表示交易完成。

  1. 使用环境变量

dotenv是一个零依赖的模块,它将环境变量从.env文件加载到process.env中。这使得我们可以隐藏数据库密码细节到.env文件,同时通过git进行代码版本管理。

```javascript

let dotenv = require('dotenv')
dotenv.config({ path: "./.env" })

```
  1. 使用mysql数据库
```javascript

var mysql = require("mysql");

var connection = mysql.createConnection({
  host: process.env.MYSQL_HOST,
  port: process.env.MYSQL_PORT,
  user: process.env.MYSQL_USER,
  password: process.env.MYSQL_PASSWORD,
  database: process.env.MYSQL_DATABASE,
  charset: process.env.MYSQL_CHARSET
});

function DbQuery(sqlstr, values) {
  return new Promise((resolve, reject) => {
    connection.query(sqlstr, [values], (error, results) => {
      if (error) {
        reject(error);
      } else {
        resolve(results);
      }
    });
  });
}

```

此处我们将query包装在DbQuery方法的Promise中,目的是为了在程序中同步运行并等待。防止程序停止后,数据来不及存入数据库丢失。

  1. 代码整体架构


```javascript

async function main() {
  // some code here
  
}

main()
  .then(() => {
    connection.end();
    process.exit(0);
  })
  .catch(error => {
    console.error(error);
    process.exit(1);
  })

```

使用异步方式方便使用wait语法

  1. 使用hardhat同步数据
```javascript

// 导入hardhat库
const { ethers } = require("hardhat");

async function main() {
  let addr = "salePlainAddress"; // salePlainAddress
  let blockNum = await ethers.provider.getBlockNumber(); // 获取最新区块号
  let topic = ethers.utils.id("ConfirmSale(address,uint256,address,address,address,uint256)"); // 此处与合约event定义相对应,但只使用数据类型,不需要变量名
  
  // iface会用户日志分析
  let iface = new ethers.utils.Interface([
    "event ConfirmSale(address indexed _contract, uint _tokenId, address indexed solder, address indexed buyer, address token, uint _price)"
  ]);

  let values = [];
  let filter = {
    address: addr,
    fromBlock: 11040000,
    toBlock: blockNum,
    topics: [topic]
  }
  let logs = await ethers.provider.getLogs(filter); //从链上获取数据
  if (logs.length > 0) {
    for (i in logs) {
      let blockinfo = await ethers.provider.getBlock(logs[i].blockNumber);
      let date = new Date(blockinfo.timestamp * 1000);
      let dt = date.getFullYear() + "-" + (date.getMonth() < 9 ? '0' + (date.getMonth()+1) : (date.getMonth()+1)) + "-" + (date.getDate() < 10 ? '0' + date.getDate() : date.getDate());
      let h = date.getHours();
      let saleInfo = iface.decodeEventLog("ConfirmSale", logs[i].data, logs[i].topics);
      values.push([saleInfo._contract, saleInfo._tokenId, saleInfo.solder, saleInfo.buyer, saleInfo.token, saleInfo._price, logs[i].blockNumber, logs[i].transactionHash, blockinfo.timestamp, dt, h]);
    }
    
    将数据存入数据库
    let sqlstr = "insert into meta_order (`contract`,`tokenID`,`seller`,`buyer`,`token`,`price`,`blocknum`,`txhash`,`timestamp`,`dt`,`h`) values ? on duplicate key update contract=values(contract), tokenID=values(tokenID), seller=values(seller), buyer=values(buyer), token=values(token), price=values(price), blocknum=values(blocknum), timestamp=values(timestamp), dt=values(dt), h=values(h)";
    let s = await DbQuery(sqlstr, values);
    console.log(s);
  } else {
    console.log("have no eventlogs");
  }
}

```

此处mysql语法使用“on duplicate key update”,为防止重复录入数据库。

6.附加order表数据结构

```sql

CREATE TABLE `meta_order` (
  `txType` smallint NOT NULL DEFAULT '1' COMMENT '交易类型:1.一口价;2.拍卖;',
  `contract` char(42) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT 'nft合约地址',
  `tokenID` int NOT NULL COMMENT 'tokenID',
  `seller` char(42) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '卖家',
  `buyer` char(42) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '买家',
  `token` char(42) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '代币合约地址',
  `price` decimal(65,0) NOT NULL COMMENT '价格',
  `blocknum` int NOT NULL COMMENT '交易所在区块号',
  `txhash` char(66) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '交易哈希',
  `timestamp` int NOT NULL COMMENT '交易区块对应的时间戳',
  `dt` char(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '""' COMMENT '交易日期',
  `h` int NOT NULL COMMENT '交易小时数',
  PRIMARY KEY (`txhash`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC

```

标签:同步,logs,process,数据库,案例,values,address,date,let
From: https://blog.51cto.com/u_16028817/6176476

相关文章

  • 查询mysql的数据库容量
    查看所有数据库容量大小SELECT table_schemaAS'数据库', sum(table_rows)AS'记录数', sum( TRUNCATE(data_length/1024/1024/1024,2))AS'数据容量(G)', sum( TRUNCATE(index_length/1024/1024/1024,2))AS'索引容量(G)'FRO......
  • 记一次达梦数据库虚拟表SQL优化记录分享
    前言:遇到问题不要怕,先看一看。语句看懂了,创建个索引,优化个处理方式,30S变0.3秒,速度提升90倍。 背景:达梦数据库、督办定制功能的一个查询列表慢(虚拟表)。语句:selectidasdbrw,hzrwnr,createdate,BB,whbh01,whbh02,whbh03,zkh,ykh,sfyrq,qtkckry,(selectcount(wfrb.requestid)from......
  • javascript把本地sql数据库表转换为对象
    在做项目的时候,需要读取本地数据库,并且在页面上显示出来,原始数据读取出来的原始数据如下:varr=sqliteDB.exec(document.getElementById('txtSQL').value); console.info(r);  使用系统的转换方式console.log('Hereisarow:'+JSON.stringify(r));  可以看到其......
  • 非关系数据库型--Redis
    RedisRedis安装1.yum/apt安装root@ubuntu:~#aptinforedisPackage:redisVersion:5:6.0.16-1ubuntu1Priority:optionalSection:universe/databaseOrigin:Ubuntu[root@localhost~]#yuminforedisAvailablePackagesName:redisVersion:5.0.3......
  • 动力节点王鹤SpringBoot3笔记——第四章 访问数据库
    视频:动力节点SpringBoot3从入门到项目实战第四章访问数据库SpringBoot框架为SQL数据库提供了广泛的支持,既有用JdbcTemplate直接访问JDBC,同时支持“objectrelationalmapping”技术(如Hibernate,MyBatis)。SpringData独立的项目提供对多种关系型和非关系型数据库的访问支持。......
  • JUC并发编程基础篇第三章之Synchronized八锁案例[理解锁的对象]
    目录1、总结2、Java8锁案例1打印的方法都有synchronized修饰,先调用email,后调用Sms;输出顺序?案例2如果在发送email的方法,加入了暂定3s中的操作,打印顺序?案例3增加一个普通的方法hello,此时b线程调用hello,先打印email还是hello?案例4有两部手机,先打印邮件还是短信案......
  • 虹科案例 | 丝芙兰xDomo:全球美妆巨头商业智能新玩法
    全球美妆行业的佼佼者丝芙兰,其走向成功绝非仅依靠品牌知名度和营销手段。身为数据驱动型企业,2018年以来,丝芙兰就率先在行业内采用虹科提供的Domo商业智能进行数据分析和决策,并首先享受了运营优化、效率提升所带来的商业价值。在DomoPalooza客户分享大会上,丝芙兰的战略与业务副......
  • 虹科案例 | 虹科Domo商业智能,助力保险公司逃离繁杂数据池!
    金融行业的发展充满着不确定性,一个具备强大承保能力和精算专业知识的资金池,对于身处该领域的公司和个人都是十分必要的。在全国城市联盟(NLC)的协助下成立的NCLMutual会员制互助保险公司,为各个地区城市提供了稳定的再保险答案。,然而,面对数字化转型这场已经打响的战斗,NCLMutual却......
  • MySQL数据库与Nacos搭建监控服务
    目录Nacos部署项目环境快速开始nacos2.2.0版本配置说明MySQL部署安装方式Linux平台(CentOS-Stream-9)部署MySQL调试防火墙管理工具MySQL用户权限MySQL导入mysql-schema脚本Springboot项目构建项目环境项目构建Nacos联动测试Nacos监控(控制台)基本功能演示验证数据库表我相信有不少小......
  • 虹科案例 | 虹科Domo商业智能,助力保险公司逃离繁杂数据池!
    金融行业的发展充满着不确定性,一个具备强大承保能力和精算专业知识的资金池,对于身处该领域的公司和个人都是十分必要的。在全国城市联盟(NLC)的协助下成立的NCLMutual会员制互助保险公司,为各个地区城市提供了稳定的再保险答案。,然而,面对数字化转型这场已经打响的战斗,NCLMutual却因......