Synchronize contract data to database classic case 2

In the previous article, we synchronized data from the event log. In this article, we try to directly call the contract to obtain data.

In our sample contract, a struct is defined to store the listing information of digital collections.

```javascript

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

At the same time, a function getSales() is defined to return all product list information.

Originally, such a product list can support users to trade digital collections. But we found that the collection list is too simple and does not support sorting. It neither supports sorting by product price, let alone supports sorting by shelf time.

It's time for our data synchronization program and middleware interface to shine. Code first: 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);
  })
  
```

How to use dotenv to read environment variables, how to connect to MySQL, and the relevant knowledge of the async type main method have already been mentioned last time, so this article will skip it.

Connecting to the blockchain is still mainly achieved through the ethers library.

The ethers.provider.getLogs() method is used to read event logs from the blockchain, while reading the contract is more direct. Just connect to the contract instance and call the contract method directly.

That is, if the contract has a getSales method, the contract instance meta is used in js to directly call the meta.getSales() method, as in step 3 of the code explanation. The code is explained as follows:

```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);

```

So far, we have stored all the product information on all current shelves in the database. And the list is sorted by price, which is already easy to implement. A copy of the data structure of the market table is attached, as follows:

```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


```

Careful netizens have already noticed that the database does not record the time when the product is on the shelves at this time. That is to say, we are not yet able to sort the list according to the time when the products are on the shelf.

As we mentioned in the previous article, the example contract has 5 event types. Among them, ConfirmSale corresponds to the order table, and CreateSale can correspond to the market table.

```javascript

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

```

It seems that through the CreateSale log, we can also get the market list, why did we choose to read directly from the contract? The burden is left here, and it will be dug out later.

Next, let's define a new table to save all event logs of the example contract

```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

```

How to synchronize the event log to the database, we have already talked about it in the previous article, and we will skip it this time.

Through the activity table, we can find out the latest launch time of each digital collection, even if the item has been on the shelf many times. It is also because some collections have been put on the shelves multiple times that we did not choose to obtain the market list through event logs in the first place.

```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)

```

This contains knowledge points, the use of the over() function. Through the over() function, we grouped by the contract address and tokenID of the data table. At the same time, they are sorted in reverse order according to the block number of the CreateSale event log.

The serial number is added to each record in the group by the row_number() function. And by selecting again, find out the latest release time of each group (that is, each digital collection).

We save this statement as the view meta_view_create_latest.

When the middleware interface returns the market list, it can join the table query to obtain the shelf time of the collection. To improve query performance, the results can also be directly stored in the market table.

So far, we have been able to sort and return the list according to the time when the collection was put on the shelf.

Guess you like

Origin blog.csdn.net/2301_76642277/article/details/130112613