在上一篇文章中,我们从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表中。
至此我们已经可以实现按藏品上架的时间进行排序返回列表了。
标签:COMMENT,同步,utf8mb4,DEFAULT,数据库,案例,meta,let,NULL From: https://blog.51cto.com/u_16028817/6186018