同步合约数据到数据库经典案例2
  iQN3saqQGbmw 2023年11月02日 85 0

在上一篇文章中,我们从event log中同步数据。本篇我们尝试直接调用合约来获取数据。

在我们的示例合约中,定义了一个struct用来保存数字藏品的上架信息。

```javascript

struct salesInfo {
   address sales;
   address collection;
   uint96 token_id;
   address token;
   uint128 price;
   uint64 duetime;
   address seller;
}
    
```

同时定义了一个function getSales()用来返回全部的商品列表信息。

原本这样一个商品列表就可以支持用户进行数字藏品交易了。但是我们发现,藏品列表过于简单,不支持排序功能。既不支持按商品价格排序,更别提要支持上架时间排序了。

轮到我们数据同步程序以及中间件接口发挥光芒的时刻了。先上代码:syncMarket.js

```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 = null) {
  return new Promise((resolve, reject) => {
    connection.query(sqlstr, [values], (error, results) => {
      if (error) {
        reject(error);
      } else {
        resolve(results);
      }
    });
  });
}

async function main() {
  let values = [];
  let [owner] = await ethers.getSigners();
  let addr = ""; // metaMasterAddress
  let meta = await ethers.getContractAt("metaMaster", addr, owner);
  let SalesCount = await meta.getSalesCount();
  let nftsInSale = await meta.getSales(SalesCount, 0);
  let nftsInSaleArr = Object.values(nftsInSale);

  nftsInSaleArr.forEach(element => {
    values.push([element.sales, element.collection, element.token_id, element.token, element.price, element.duetime, element.seller]);
  });
  if (values.length > 0) {
    let sqlstr = "insert into meta_market (`sales`,`collection`,`tokenID`,`token`,`price`,`duetime`,`seller`) values ? on duplicate key update sales=values(sales), token=values(token), price=values(price), duetime=values(duetime), seller=values(seller)";
    let s = await DbQuery(sqlstr, nftsInSale);
    console.log(s);
  }
}

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

如何使用dotenv读取环境变量,如何连接MySQL,以及async类型的main方法的相关知识,上次已经讲过了,本文略过不提。

连接区块链依然主要通过ethers库来实现。

从区块链读取event logs使用ethers.provider.getLogs()方法,而读取合约更加直接,连接上合约实例,直接调用合约方法即可。

即如果合约有getSales方法,js里就用合约实例meta直接调用meta.getSales()方法,如代码解释第3步。代码解释如下:

```javascript

// 1. 连接合约
let meta = await ethers.getContractAt("metaMaster", addr, owner);
// 2. 调用合约,获取当前商品数量
let SalesCount = await meta.getSalesCount();
// 3. 调用合约,获取当前商品列表
let nftsInSale = await meta.getSales(SalesCount, 0);
// 4. 将返回值从object转化为array
let nftsInSaleArr = Object.values(nftsInSale);

```

至此,我们将当前所有货架上的商品信息全部存入数据库。而列表按价格排序,已经很容易实现了。现附market表的数据结构一份,如下:

```sql

CREATE TABLE `meta_market` (
  `sales` char(42) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '0x' COMMENT '销售合约(一口价/拍卖)',
  `collection` char(42) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '0x' COMMENT 'nft合约地址',
  `tokenID` int unsigned NOT NULL DEFAULT '0' COMMENT 'tokenID',
  `token` char(42) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '0x' COMMENT '付款代币合约地址',
  `price` decimal(65,0) unsigned NOT NULL DEFAULT '0' COMMENT '报价',
  `duetime` int unsigned NOT NULL DEFAULT '0' COMMENT '到期时间',
  `seller` char(42) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '0x' COMMENT '卖家',
  `updateTime` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`collection`,`tokenID`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC


```

有细心的网友已经看出来了,数据库此时并没有记录商品的上架时间。也就是说我们暂时还不能够按照商品上架时间给列表进行排序。

在上一篇文章我们有提到过,示例合约有5种事件类型。其中ConfirmSale对应order表,而CreateSale就可以对应market表。

```javascript

event CreateSale(address indexed _contract, uint _tokenId, address seller, address token, uint _price, uint _due);

```

貌似通过CreateSale日志,我们也能够获得market列表,为什么我们选择了直接从合约读取呢?此处留下包袱,后续再翻出。

接下来我们先定义一张新表,用于保存示例合约的全部event logs

```sql

CREATE TABLE `meta_activity` (
  `event` varchar(11) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '事件名',
  `txType` smallint 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',
  `from` char(42) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '卖家',
  `to` 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 '价格',
  `duetime` int unsigned DEFAULT 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`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC

```

如何将event log同步到数据库,我们上一篇已经讲过,本次略过不提。

通过activity表,我们可以找出每一个数字藏品的最新上架时间,即便该商品曾经多次上架。也正式因为某些藏品存在多次上架的情况,我们才没有第一时间选取通过event logs来获取market列表。

```sql

SELECT 
  `b`.`contract` AS `contract`,
  `b`.`tokenID` AS `tokenID`,
  `b`.`blocknum` AS `blocknum`,
  `b`.`timestamp` AS `timestamp`,
  `b`.`rn` AS `rn` 
FROM
  (SELECT 
    `nft_mall`.`meta_activity`.`contract` AS `contract`,
    `nft_mall`.`meta_activity`.`tokenID` AS `tokenID`,
    `nft_mall`.`meta_activity`.`blocknum` AS `blocknum`,
    `nft_mall`.`meta_activity`.`timestamp` AS `timestamp`,
    row_number () OVER (
      PARTITION BY `nft_mall`.`meta_activity`.`contract`,
      `nft_mall`.`meta_activity`.`tokenID` 
  ORDER BY `nft_mall`.`meta_activity`.`blocknum` DESC
  ) AS `rn` 
  FROM
    `nft_mall`.`meta_activity` 
  WHERE (
      `nft_mall`.`meta_activity`.`event` = 'CreateSale'
    )) `b` 
WHERE (`b`.`rn` = 1)

```

此处蕴含知识点,over()函数的使用。通过over()函数,我们按数据表的合约地址和tokenID进行了分组。同时按照CreateSale事件日志的区块号进行了倒序排序。

通过row_number()函数给分组内的每一条记录增加了序号。而通过再次select选择,找出来每一组(即每一个数字藏品)的最新上架时间。

我们将该语句保存为视图 meta_view_create_latest。

中间件接口在返回market list时,可联表查询查询获得该藏品的上架时间。如为提高查询性能,也可将结果直接保存在market表中。

至此我们已经可以实现按藏品上架的时间进行排序返回列表了。

【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年11月08日 0

暂无评论

推荐阅读
  jJVxqmfuiDRw   2023年11月02日   59   0   0 sqli++System
  DJsdk34H4Gbu   2023年11月02日   111   0   0 javascript
iQN3saqQGbmw