From 4d37d00d9831400130bd161fe7a73b2b7bb12e9e Mon Sep 17 00:00:00 2001 From: godlin Date: Sun, 28 Apr 2024 12:46:38 +0200 Subject: [PATCH] Humanode EVM --- Humanode/Humanode-starter/README.md | 132 ++++++++++- Humanode/Humanode-starter/erc20.abi.json | 222 ++++++++++++++++++ Humanode/Humanode-starter/package.json | 24 +- Humanode/Humanode-starter/project.ts | 40 +++- Humanode/Humanode-starter/schema.graphql | 20 +- Humanode/Humanode-starter/src/chaintypes.ts | 0 Humanode/Humanode-starter/src/index.ts | 2 +- .../src/mappings/mappingHandlers.ts | 64 ++++- 8 files changed, 473 insertions(+), 31 deletions(-) create mode 100644 Humanode/Humanode-starter/erc20.abi.json create mode 100644 Humanode/Humanode-starter/src/chaintypes.ts diff --git a/Humanode/Humanode-starter/README.md b/Humanode/Humanode-starter/README.md index 8d2e25e5..ffb47dac 100644 --- a/Humanode/Humanode-starter/README.md +++ b/Humanode/Humanode-starter/README.md @@ -2,7 +2,7 @@ [SubQuery](https://subquery.network) is a fast, flexible, and reliable open-source data indexer that provides you with custom APIs for your web3 project across all of our supported networks. To learn about how to get started with SubQuery, [visit our docs](https://academy.subquery.network). -**This SubQuery project indexes all transfers, bioauthentication events, and online validator nodes from Humanode chain** +This project can be used as a starting point for developing your SubQuery project. It indexes all transfers, bioauthentication events, and online validator nodes from Humanode chain. Moreover, it indexes the approvals and transfers of HMND token. ## Start @@ -41,20 +41,132 @@ For this project, you can try to query with the following GraphQL code to get a ```graphql { query { - starterEntities(first: 5) { + transactions(first: 1, orderBy: VALUE_DESC) { + totalCount nodes { id + transactionHash + blockHeight + from + to + value + contractAddress } } - bioAuths(first: 5) { - nodes { - id - } + } + approvals(first: 1) { + nodes { + id + owner + spender + value + contractAddress + } + } + bioauthNewAuthentications(first: 1) { + nodes { + id + validatorPublicKey + timestamp + blockNumber + } + } + imOnlineSomeOfflines(first: 1) { + nodes { + id + accountIds + timestamp + blockNumber } - _metadata { - lastProcessedHeight - lastProcessedTimestamp - targetHeight + } +} +``` + +Afterward, anticipate receiving a result resembling this: + +```json +{ + "data": { + "query": { + "transactions": { + "nodes": [ + { + "id": "7487952-0x030d4ab19a9fa75e093bcbc400f1a10a6e377e52f673bfd1cd490572e9874282-0", + "transactionHash": "0x030d4ab19a9fa75e093bcbc400f1a10a6e377e52f673bfd1cd490572e9874282", + "blockHeight": "7487952", + "from": "0xf591da380c911C7bE9Ba48bD14c451bF784886F7", + "to": "0xe48Cc340B8f1AC0a326577B3c2d1Fe5229ebb1c2", + "value": "9800000000000000000000", + "contractAddress": "0x0000000000000000000000000000000000000802" + } + ] + } + }, + "approvals": { + "nodes": [ + { + "id": "0x909a9c2d5eead83eeba78f8b4cfd789cf1b2fcfc543a33b63a792fe4a8a53b37", + "owner": "0xd5b2bceeaaaccb2bf152207f7e8c4abcaa7d5881", + "spender": "0x8C20a3F5C9A5926d4C83592c25317f602b385441", + "value": "100000000000000000000", + "contractAddress": "0x0000000000000000000000000000000000000802" + } + ] + }, + "bioauthNewAuthentications": { + "nodes": [ + { + "id": "7489527-1", + "validatorPublicKey": "hmsxpGvLUfSbUZxUYFZF9dNm3Jf9xUBdhN1p229qAr4xiktFP", + "timestamp": "2024-04-26T13:34:18", + "blockNumber": 7489527 + } + ] + }, + "imOnlineSomeOfflines": { + "nodes": [ + { + "id": "7495265-0", + "accountIds": [ + "hmrjcaAhyXhsZ37ndzTTv4BdUEVc8yw9xaNfp75oXzH3YGJgq", + "hmqKroHLbyP9QfREyGT6X9D5AbHwCBnQw5bsrg99EirM6uPpk", + "hmrrvsazEScvkVj1CJdewfxxP7bLz99fydDxSeLkKcHzYp6KY", + "hmpt1bKjZfJBUQ8ZiGLvwJuxv7Fg2cKJMNxojDh7EE3RANHAA", + "hmsbiRJYWozBFpHhzWyiHs99rDy88rMHCVRsN9mXxJBMVyKim", + "hmq7RB5cu3GsVLfD4iUDC8LQmqhL8gmxLLLCbNw1geZa1pMRr", + "hmqNahjGVaaNVZ3GsmyScgfQuWjsAJMyV5vRSBghVa1EkPKt2", + "hmq6hEjFh5wu3zFa2ZNC8nunRhquhpxvTK96ph4jxTjnn9kdF", + "hmpZ6medWcV9suYc4b98SRtVyYc4En7Mmx6kUSZBaaTH7SM9E", + "hmrHE9bXxtse94j8FoqJLdQjVTAwwNxCuietpi84V2WwwpDVq", + "hmpsTmnYR5qLik1FjCWVTkXH3NMu5qBmnb6BQaZjx6gETY29f", + "hmrxbpsg1rnd75vt3vCpD3fTVEgrv5bCMwGqdRYfGmYMKa7K8", + "hmqCBhZDc828ZtMkpDifdeNc1jSZMQBzPjQGpD7mekyKp7oh6", + "hmpALuSfpMpFjXQPpDV2Sn4AW5qCM2JfJwugWxK7AtwYcVSXR", + "hmp7QcaW8c1JCjqeFpX2aHE5146wtcVdca8WXeJx9Y5Vwx1uN", + "hmrhDq3PCCNmpMWQDYhS9JNFDEcKSZBR5RJJmCEYv8Abnw5by", + "hmokJ7UHP4in95tfqHMQpqL7rRL9hHAfHVFJrRxN987KmsEQY", + "hmncjXyHh4j8z6YtvGTCs2G3ByBACWutpW9q56cvcp3cA5Uad", + "hmrU6j8B5aUPLz78D5jzc9DhnWXb8ypYa5fBupascJAGk1LMw", + "hmsBuGEC7XfkXV8bVXu6tULnDd9qa3U1gDTUbPCXREK8Yhdna", + "hmsgV5aPxsZgschUS3owhB1RSvaq8PNw5pCUCdM3eCFNknuHq", + "hmpXQ7k6gFzASRT2DVcQ62XjPMnL8DGQLEHns4Gxhv6EiD2TP", + "hmnqeQT8J37tTp9L4ddSu35CgbaAgYf5oByHxjdTiopKmGt7X", + "hmsRKiowVG6eU5SKNShU7YJW4UN5c8jPih27invvBFGWtvoPT", + "hmoVBTcRevpKH8uZkrAhXLrn9RqAk59jhd7CHbGzoWWjMF2Fy", + "hmpxeu6SBsXHCHja3mQGLPehDJkaRMsNZA4DAgfLDgxRPm98i", + "hmoyMLtAh7HqUzJ2ZpKttAbDpGuUrAnH8N1kD4ZU9JukHGSap", + "hmpbWVGtyAhJM8aKzovbWFioHUdEiN4PoWcwJBSbGyoRDmoia", + "hmoLUKVnybkDEpEgAmmJLiAAxb8c1XV8AGtKoRHi9phd7gPhP", + "hmt3Ge343U6svqMDgNJAaZM5RwtNYYjQhEVDPxNEByeoBgzcS", + "hmqq7LR14c3U1hdwEigRZ2mV6VRtXVviBpjuqm9YUtiNdtfau", + "hmpKz8z31bq17BC72wQNrDVNc8XeSJjucYT1dpfS1ocw2FXkV", + "hmnrJ8M7XUZtGEVLnLbngi6XCwEwKgfUNfkRiyrqjP1iLpRj8", + "hmrQRgz8pU3gT8cEn4XgsRtLUW8nxTtz4SCzXRx6GxdDEtXVF" + ], + "timestamp": "2024-04-26T23:32:54", + "blockNumber": 7495265 + } + ] } } } diff --git a/Humanode/Humanode-starter/erc20.abi.json b/Humanode/Humanode-starter/erc20.abi.json new file mode 100644 index 00000000..405d6b36 --- /dev/null +++ b/Humanode/Humanode-starter/erc20.abi.json @@ -0,0 +1,222 @@ +[ + { + "constant": true, + "inputs": [], + "name": "name", + "outputs": [ + { + "name": "", + "type": "string" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_spender", + "type": "address" + }, + { + "name": "_value", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_from", + "type": "address" + }, + { + "name": "_to", + "type": "address" + }, + { + "name": "_value", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "decimals", + "outputs": [ + { + "name": "", + "type": "uint8" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "_owner", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "name": "balance", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "symbol", + "outputs": [ + { + "name": "", + "type": "string" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_to", + "type": "address" + }, + { + "name": "_value", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "_owner", + "type": "address" + }, + { + "name": "_spender", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "payable": true, + "stateMutability": "payable", + "type": "fallback" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "name": "value", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "from", + "type": "address" + }, + { + "indexed": true, + "name": "to", + "type": "address" + }, + { + "indexed": false, + "name": "value", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + } +] diff --git a/Humanode/Humanode-starter/package.json b/Humanode/Humanode-starter/package.json index b5901cec..54a69408 100644 --- a/Humanode/Humanode-starter/package.json +++ b/Humanode/Humanode-starter/package.json @@ -1,7 +1,7 @@ { - "name": "humanode-starter", + "name": "peaq-starter", "version": "1.0.0", - "description": "This project can be used as a starting point for developing your SubQuery project. It indexes all transfers, bioauthentication events, and online validator nodes from Humanode chain", + "description": "A basic Frontier EVM example project with an event and call handler. Read more about this at https://academy.subquery.network/create/frontier/. This project can be use as a starting point for developing your SubQuery project", "main": "dist/index.js", "scripts": { "build": "subql build", @@ -19,17 +19,21 @@ "project.yaml" ], "author": "SubQuery Team", - "license": "MIT", + "license": "Apache-2.0", + "resolutions": { + "ipfs-unixfs": "6.0.6" + }, "devDependencies": { "@polkadot/api": "^10", - "@subql/types": "latest", - "typescript": "^5.2.2", "@subql/cli": "latest", - "node-fetch": "2.6.7", + "@subql/frontier-evm-processor": "latest", + "@subql/node": "latest", "@subql/testing": "latest", - "@subql/node": "latest" + "@subql/types": "latest", + "@subql/types-ethereum": "latest", + "typescript": "^5.2.2" }, - "resolutions": { - "ipfs-unixfs": "6.0.6" + "dependencies": { + "assert": "^2.0.0" } -} +} \ No newline at end of file diff --git a/Humanode/Humanode-starter/project.ts b/Humanode/Humanode-starter/project.ts index 2ec0150b..3d9cdb62 100644 --- a/Humanode/Humanode-starter/project.ts +++ b/Humanode/Humanode-starter/project.ts @@ -4,13 +4,14 @@ import { SubstrateProject, } from "@subql/types"; +import { FrontierEvmDatasource } from "@subql/frontier-evm-processor"; + // Can expand the Datasource processor types via the genreic param -const project: SubstrateProject = { +const project: SubstrateProject = { specVersion: "1.0.0", version: "0.0.1", name: "humanode-starter", - description: - "This project can be used as a starting point for developing your SubQuery project. It indexes all transfers, bioauthentication events, and online validator nodes from Humanode chain", + description: `This project can be used as a starting point for developing your SubQuery project. It indexes all transfers, bioauthentication events, and online validator nodes from Humanode chain. Moreover, it indexes the approvals and transfers of HMND token.`, runner: { node: { name: "@subql/node", @@ -39,6 +40,39 @@ const project: SubstrateProject = { endpoint: ["wss://explorer-rpc-ws.mainnet.stages.humanode.io"], }, dataSources: [ + { + kind: "substrate/FrontierEvm", + startBlock: 1, + processor: { + file: "./node_modules/@subql/frontier-evm-processor/dist/bundle.js", + options: { + abi: "erc20", + address: "0x0000000000000000000000000000000000000802", // A specific contract to index + }, + }, + assets: new Map([["erc20", { file: "./erc20.abi.json" }]]), + mapping: { + file: "./dist/index.js", + handlers: [ + { + handler: "handleEvmEvent", + kind: "substrate/FrontierEvmEvent", + filter: { + topics: [ + "Transfer(address indexed from,address indexed to,uint256 value)", + ], + }, + }, + { + handler: "handleEvmCall", + kind: "substrate/FrontierEvmCall", + filter: { + function: "approve(address to,uint256 value)", + }, + }, + ], + }, + }, { kind: SubstrateDatasourceKind.Runtime, startBlock: 1, diff --git a/Humanode/Humanode-starter/schema.graphql b/Humanode/Humanode-starter/schema.graphql index 82965108..9d615020 100644 --- a/Humanode/Humanode-starter/schema.graphql +++ b/Humanode/Humanode-starter/schema.graphql @@ -1,6 +1,20 @@ -# To improve query performance, we strongly suggest adding indexes to any field that you plan to filter or sort by -# Add the `@index` or `@index(unique: true)` annotation after any non-key field -# https://academy.subquery.network/build/graphql.html#indexing-by-non-primary-key-field +type Transaction @entity { + id: ID! + transactionHash: String! + value: BigInt + to: String + from: String + blockHeight: String + contractAddress: String! +} + +type Approval @entity { + id: ID! + value: BigInt + owner: String! + spender: String! + contractAddress: String! +} type BioauthNewAuthentication @entity { id: ID! diff --git a/Humanode/Humanode-starter/src/chaintypes.ts b/Humanode/Humanode-starter/src/chaintypes.ts new file mode 100644 index 00000000..e69de29b diff --git a/Humanode/Humanode-starter/src/index.ts b/Humanode/Humanode-starter/src/index.ts index 50916157..89dc0c5f 100644 --- a/Humanode/Humanode-starter/src/index.ts +++ b/Humanode/Humanode-starter/src/index.ts @@ -1,3 +1,3 @@ //Exports all handler functions -export * from "./mappings/mappingHandlers"; import "@polkadot/api-augment"; +export * from "./mappings/mappingHandlers"; diff --git a/Humanode/Humanode-starter/src/mappings/mappingHandlers.ts b/Humanode/Humanode-starter/src/mappings/mappingHandlers.ts index 2b6151a3..9385d413 100644 --- a/Humanode/Humanode-starter/src/mappings/mappingHandlers.ts +++ b/Humanode/Humanode-starter/src/mappings/mappingHandlers.ts @@ -1,10 +1,66 @@ +import { + Approval, + Transaction, + BioauthNewAuthentication, + ImOnlineSomeOffline, +} from "../types"; +import { + FrontierEvmEvent, + FrontierEvmCall, +} from "@subql/frontier-evm-processor"; +import { BigNumber } from "ethers"; +import assert from "assert"; import { Codec } from "@polkadot/types/types"; import { Vec } from "@polkadot/types-codec"; import { SubstrateEvent } from "@subql/types"; -import { BioauthNewAuthentication, ImOnlineSomeOffline } from "../types"; + +type TransferEventArgs = [string, string, BigNumber] & { + from: string; + to: string; + value: BigNumber; +}; + +type ApproveCallArgs = [string, BigNumber] & { + _spender: string; + _value: BigNumber; +}; + +export async function handleEvmEvent( + event: FrontierEvmEvent +): Promise { + assert(event.transactionHash, "No transactionHash"); + + const transaction = Transaction.create({ + id: `${event.blockNumber}-${event.transactionHash}-${event.logIndex}`, + value: event.args?.value.toBigInt(), + from: event.args?.from, + to: event.args?.to, + blockHeight: event.blockNumber.toString(), + contractAddress: event.address, + transactionHash: event.transactionHash, + }); + await transaction.save(); +} + +export async function handleEvmCall( + event: FrontierEvmCall +): Promise { + assert(event.args, "No event.args"); + assert(event.to, "No event.to"); + + const approval = Approval.create({ + id: event.hash, + owner: event.from, + value: event.args._value.toBigInt(), + spender: event.args._spender, + contractAddress: event.to, + }); + + await approval.save(); +} export async function handleBioauthNewAuthenticationEvent( - substrateEvent: SubstrateEvent, + substrateEvent: SubstrateEvent ): Promise { const { event, block, idx } = substrateEvent; @@ -22,7 +78,7 @@ export async function handleBioauthNewAuthenticationEvent( } export async function handleImonlineSomeOfflineEvent( - substrateEvent: SubstrateEvent<[]>, + substrateEvent: SubstrateEvent<[]> ): Promise { const { event, block, idx } = substrateEvent; @@ -40,7 +96,7 @@ export async function handleImonlineSomeOfflineEvent( for (const identification of offline as Vec) { const [accountId, _fullIdentification] = identification as any as [ Codec, - Codec, + Codec ]; record.accountIds.push(accountId.toString()); }