mirror of
https://github.com/cerc-io/watcher-ts
synced 2025-07-30 19:52:07 +00:00
Use entity column type map to create entity in store get API (#72)
* Use typeof to distinguish between BigInt and BigDecimal in store get API * Use entity column type map to create entity in store get API * Add entity column type map in eden-watcher
This commit is contained in:
parent
5f03ad5029
commit
3e0c84b333
@ -133,6 +133,7 @@ export class Indexer implements IndexerInterface {
|
||||
|
||||
_ipfsClient: IPFSClient
|
||||
|
||||
_entityTypesMap: Map<string, { [key: string]: string }>
|
||||
_relationsMap: Map<any, { [key: string]: any }>
|
||||
|
||||
constructor (serverConfig: ServerConfig, db: Database, ethClient: EthClient, postgraphileClient: EthClient, ethProvider: BaseProvider, jobQueue: JobQueue, graphWatcher: GraphWatcher) {
|
||||
@ -179,6 +180,9 @@ export class Indexer implements IndexerInterface {
|
||||
|
||||
this._ipfsClient = new IPFSClient(this._serverConfig.ipfsApiAddr);
|
||||
|
||||
this._entityTypesMap = new Map();
|
||||
this._populateEntityTypesMap();
|
||||
|
||||
this._relationsMap = new Map();
|
||||
this._populateRelationsMap();
|
||||
}
|
||||
@ -1139,6 +1143,223 @@ export class Indexer implements IndexerInterface {
|
||||
return this._baseIndexer.getAncestorAtDepth(blockHash, depth);
|
||||
}
|
||||
|
||||
getEntityTypesMap (): Map<string, { [key: string]: string }> {
|
||||
return this._entityTypesMap;
|
||||
}
|
||||
|
||||
_populateEntityTypesMap (): void {
|
||||
this._entityTypesMap.set(
|
||||
'Producer',
|
||||
{
|
||||
id: 'ID',
|
||||
active: 'Boolean',
|
||||
rewardCollector: 'Bytes',
|
||||
rewards: 'BigInt',
|
||||
confirmedBlocks: 'BigInt',
|
||||
pendingEpochBlocks: 'BigInt'
|
||||
}
|
||||
);
|
||||
|
||||
this._entityTypesMap.set(
|
||||
'ProducerSet',
|
||||
{
|
||||
id: 'ID',
|
||||
producers: 'Producer'
|
||||
}
|
||||
);
|
||||
|
||||
this._entityTypesMap.set(
|
||||
'ProducerSetChange',
|
||||
{
|
||||
id: 'ID',
|
||||
blockNumber: 'BigInt',
|
||||
producer: 'Bytes',
|
||||
changeType: 'ProducerSetChangeType'
|
||||
}
|
||||
);
|
||||
|
||||
this._entityTypesMap.set(
|
||||
'ProducerRewardCollectorChange',
|
||||
{
|
||||
id: 'ID',
|
||||
blockNumber: 'BigInt',
|
||||
producer: 'Bytes',
|
||||
rewardCollector: 'Bytes'
|
||||
}
|
||||
);
|
||||
|
||||
this._entityTypesMap.set(
|
||||
'RewardScheduleEntry',
|
||||
{
|
||||
id: 'ID',
|
||||
startTime: 'BigInt',
|
||||
epochDuration: 'BigInt',
|
||||
rewardsPerEpoch: 'BigInt'
|
||||
}
|
||||
);
|
||||
|
||||
this._entityTypesMap.set(
|
||||
'RewardSchedule',
|
||||
{
|
||||
id: 'ID',
|
||||
rewardScheduleEntries: 'RewardScheduleEntry',
|
||||
lastEpoch: 'Epoch',
|
||||
pendingEpoch: 'Epoch',
|
||||
activeRewardScheduleEntry: 'RewardScheduleEntry'
|
||||
}
|
||||
);
|
||||
|
||||
this._entityTypesMap.set(
|
||||
'Block',
|
||||
{
|
||||
id: 'ID',
|
||||
fromActiveProducer: 'Boolean',
|
||||
hash: 'Bytes',
|
||||
parentHash: 'Bytes',
|
||||
unclesHash: 'Bytes',
|
||||
author: 'Bytes',
|
||||
stateRoot: 'Bytes',
|
||||
transactionsRoot: 'Bytes',
|
||||
receiptsRoot: 'Bytes',
|
||||
number: 'BigInt',
|
||||
gasUsed: 'BigInt',
|
||||
gasLimit: 'BigInt',
|
||||
timestamp: 'BigInt',
|
||||
difficulty: 'BigInt',
|
||||
totalDifficulty: 'BigInt',
|
||||
size: 'BigInt'
|
||||
}
|
||||
);
|
||||
|
||||
this._entityTypesMap.set(
|
||||
'Epoch',
|
||||
{
|
||||
id: 'ID',
|
||||
finalized: 'Boolean',
|
||||
epochNumber: 'BigInt',
|
||||
startBlock: 'Block',
|
||||
endBlock: 'Block',
|
||||
producerBlocks: 'BigInt',
|
||||
allBlocks: 'BigInt',
|
||||
producerBlocksRatio: 'BigDecimal'
|
||||
}
|
||||
);
|
||||
|
||||
this._entityTypesMap.set(
|
||||
'ProducerEpoch',
|
||||
{
|
||||
id: 'ID',
|
||||
address: 'Bytes',
|
||||
epoch: 'Epoch',
|
||||
totalRewards: 'BigInt',
|
||||
blocksProduced: 'BigInt',
|
||||
blocksProducedRatio: 'BigDecimal'
|
||||
}
|
||||
);
|
||||
|
||||
this._entityTypesMap.set(
|
||||
'SlotClaim',
|
||||
{
|
||||
id: 'ID',
|
||||
slot: 'Slot',
|
||||
owner: 'Bytes',
|
||||
winningBid: 'BigInt',
|
||||
oldBid: 'BigInt',
|
||||
startTime: 'BigInt',
|
||||
expirationTime: 'BigInt',
|
||||
taxRatePerDay: 'BigDecimal'
|
||||
}
|
||||
);
|
||||
|
||||
this._entityTypesMap.set(
|
||||
'Slot',
|
||||
{
|
||||
id: 'ID',
|
||||
owner: 'Bytes',
|
||||
delegate: 'Bytes',
|
||||
winningBid: 'BigInt',
|
||||
oldBid: 'BigInt',
|
||||
startTime: 'BigInt',
|
||||
expirationTime: 'BigInt',
|
||||
taxRatePerDay: 'BigDecimal'
|
||||
}
|
||||
);
|
||||
|
||||
this._entityTypesMap.set(
|
||||
'Staker',
|
||||
{
|
||||
id: 'ID',
|
||||
staked: 'BigInt',
|
||||
rank: 'BigInt'
|
||||
}
|
||||
);
|
||||
|
||||
this._entityTypesMap.set(
|
||||
'Network',
|
||||
{
|
||||
id: 'ID',
|
||||
slot0: 'Slot',
|
||||
slot1: 'Slot',
|
||||
slot2: 'Slot',
|
||||
stakers: 'Staker',
|
||||
numStakers: 'BigInt',
|
||||
totalStaked: 'BigInt',
|
||||
stakedPercentiles: 'BigInt'
|
||||
}
|
||||
);
|
||||
|
||||
this._entityTypesMap.set(
|
||||
'Distributor',
|
||||
{
|
||||
id: 'ID',
|
||||
currentDistribution: 'Distribution'
|
||||
}
|
||||
);
|
||||
|
||||
this._entityTypesMap.set(
|
||||
'Distribution',
|
||||
{
|
||||
id: 'ID',
|
||||
distributor: 'Distributor',
|
||||
timestamp: 'BigInt',
|
||||
distributionNumber: 'BigInt',
|
||||
merkleRoot: 'Bytes',
|
||||
metadataURI: 'String'
|
||||
}
|
||||
);
|
||||
|
||||
this._entityTypesMap.set(
|
||||
'Claim',
|
||||
{
|
||||
id: 'ID',
|
||||
timestamp: 'BigInt',
|
||||
index: 'BigInt',
|
||||
account: 'Account',
|
||||
totalEarned: 'BigInt',
|
||||
claimed: 'BigInt'
|
||||
}
|
||||
);
|
||||
|
||||
this._entityTypesMap.set(
|
||||
'Account',
|
||||
{
|
||||
id: 'ID',
|
||||
totalClaimed: 'BigInt',
|
||||
totalSlashed: 'BigInt'
|
||||
}
|
||||
);
|
||||
|
||||
this._entityTypesMap.set(
|
||||
'Slash',
|
||||
{
|
||||
id: 'ID',
|
||||
timestamp: 'BigInt',
|
||||
account: 'Account',
|
||||
slashed: 'BigInt'
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
_populateRelationsMap (): void {
|
||||
// Needs to be generated by codegen.
|
||||
this._relationsMap.set(ProducerSet, {
|
||||
|
@ -296,13 +296,13 @@ type RewardSchedule {
|
||||
type Block {
|
||||
id: ID!
|
||||
fromActiveProducer: Boolean!
|
||||
hash: String!
|
||||
parentHash: String!
|
||||
unclesHash: String!
|
||||
author: String!
|
||||
stateRoot: String!
|
||||
transactionsRoot: String!
|
||||
receiptsRoot: String!
|
||||
hash: Bytes!
|
||||
parentHash: Bytes!
|
||||
unclesHash: Bytes!
|
||||
author: Bytes!
|
||||
stateRoot: Bytes!
|
||||
transactionsRoot: Bytes!
|
||||
receiptsRoot: Bytes!
|
||||
number: BigInt!
|
||||
gasUsed: BigInt!
|
||||
gasLimit: BigInt!
|
||||
|
@ -189,7 +189,7 @@ export class Database {
|
||||
await repo.save(dbEntity);
|
||||
}
|
||||
|
||||
async toGraphEntity (instanceExports: any, entity: string, data: any): Promise<any> {
|
||||
async toGraphEntity (instanceExports: any, entity: string, data: any, entityTypes: { [key: string]: string }): Promise<any> {
|
||||
// TODO: Cache schema/columns.
|
||||
const repo = this._conn.getRepository(entity);
|
||||
const entityFields = repo.metadata.columns;
|
||||
@ -210,11 +210,11 @@ export class Database {
|
||||
// Fill _blockNumber as blockNumber and _blockHash as blockHash in the entityInstance (wasm).
|
||||
if (['_blockNumber', '_blockHash'].includes(field.propertyName)) {
|
||||
field.propertyName = field.propertyName.slice(1);
|
||||
|
||||
return toEntityValue(instanceExports, entityInstance, data, field);
|
||||
}
|
||||
|
||||
return toEntityValue(instanceExports, entityInstance, data, field);
|
||||
const gqlType = entityTypes[field.propertyName];
|
||||
|
||||
return toEntityValue(instanceExports, entityInstance, data, field, gqlType);
|
||||
}, {});
|
||||
|
||||
await Promise.all(entityValuePromises);
|
||||
|
@ -77,7 +77,13 @@ export const instantiate = async (
|
||||
return null;
|
||||
}
|
||||
|
||||
return database.toGraphEntity(instanceExports, entityName, entityData);
|
||||
assert(indexer.getEntityTypesMap);
|
||||
const entityTypesMap = indexer.getEntityTypesMap();
|
||||
|
||||
const entityTypes = entityTypesMap.get(entityName);
|
||||
assert(entityTypes);
|
||||
|
||||
return database.toGraphEntity(instanceExports, entityName, entityData, entityTypes);
|
||||
},
|
||||
'store.set': async (entity: number, id: number, data: number) => {
|
||||
const entityName = __getString(entity);
|
||||
|
@ -3,7 +3,6 @@ import path from 'path';
|
||||
import fs from 'fs-extra';
|
||||
import debug from 'debug';
|
||||
import yaml from 'js-yaml';
|
||||
import { ColumnType } from 'typeorm';
|
||||
import { ColumnMetadata } from 'typeorm/metadata/ColumnMetadata';
|
||||
|
||||
import { GraphDecimal } from '@vulcanize/util';
|
||||
@ -382,9 +381,9 @@ export const getSubgraphConfig = async (subgraphPath: string): Promise<any> => {
|
||||
return config;
|
||||
};
|
||||
|
||||
export const toEntityValue = async (instanceExports: any, entityInstance: any, data: any, field: ColumnMetadata) => {
|
||||
export const toEntityValue = async (instanceExports: any, entityInstance: any, data: any, field: ColumnMetadata, type: string) => {
|
||||
const { __newString, Value } = instanceExports;
|
||||
const { type, isArray, propertyName } = field;
|
||||
const { isArray, propertyName } = field;
|
||||
|
||||
const entityKey = await __newString(propertyName);
|
||||
const entityValuePtr = await entityInstance.get(entityKey);
|
||||
@ -475,11 +474,10 @@ const parseEntityValue = async (instanceExports: any, valuePtr: number) => {
|
||||
}
|
||||
};
|
||||
|
||||
const formatEntityValue = async (instanceExports: any, subgraphValue: any, type: ColumnType, value: any, isArray: boolean): Promise<any> => {
|
||||
const formatEntityValue = async (instanceExports: any, subgraphValue: any, type: string, value: any, isArray: boolean): Promise<any> => {
|
||||
const { __newString, __newArray, BigInt: ExportBigInt, Value, ByteArray, Bytes, BigDecimal, id_of_type: getIdOfType } = instanceExports;
|
||||
|
||||
if (isArray) {
|
||||
// TODO: Implement handling array of Bytes type field.
|
||||
const dataArrayPromises = value.map((el: any) => formatEntityValue(instanceExports, subgraphValue, type, el, false));
|
||||
const dataArray = await Promise.all(dataArrayPromises);
|
||||
const arrayStoreValueId = await getIdOfType(TypeId.ArrayStoreValue);
|
||||
@ -489,54 +487,49 @@ const formatEntityValue = async (instanceExports: any, subgraphValue: any, type:
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case 'varchar': {
|
||||
case 'ID':
|
||||
case 'String': {
|
||||
const entityValue = await __newString(value);
|
||||
const kind = await subgraphValue.kind;
|
||||
|
||||
switch (kind) {
|
||||
case ValueKind.BYTES: {
|
||||
const byteArray = await ByteArray.fromHexString(entityValue);
|
||||
const bytes = await Bytes.fromByteArray(byteArray);
|
||||
|
||||
return Value.fromBytes(bytes);
|
||||
}
|
||||
|
||||
default:
|
||||
return Value.fromString(entityValue);
|
||||
}
|
||||
return Value.fromString(entityValue);
|
||||
}
|
||||
|
||||
case 'integer': {
|
||||
case 'Boolean': {
|
||||
return Value.fromBoolean(value ? 1 : 0);
|
||||
}
|
||||
|
||||
case 'Int': {
|
||||
return Value.fromI32(value);
|
||||
}
|
||||
|
||||
case 'bigint': {
|
||||
case 'BigInt': {
|
||||
const valueStringPtr = await __newString(value.toString());
|
||||
const bigInt = await ExportBigInt.fromString(valueStringPtr);
|
||||
|
||||
return Value.fromBigInt(bigInt);
|
||||
}
|
||||
|
||||
case 'boolean': {
|
||||
return Value.fromBoolean(value ? 1 : 0);
|
||||
}
|
||||
|
||||
case 'enum': {
|
||||
const entityValue = await __newString(value);
|
||||
|
||||
return Value.fromString(entityValue);
|
||||
}
|
||||
|
||||
case 'numeric': {
|
||||
case 'BigDecimal': {
|
||||
const valueStringPtr = await __newString(value.toString());
|
||||
const bigDecimal = await BigDecimal.fromString(valueStringPtr);
|
||||
|
||||
return Value.fromBigDecimal(bigDecimal);
|
||||
}
|
||||
|
||||
// TODO: Support more types.
|
||||
default:
|
||||
throw new Error(`Unsupported type: ${type}`);
|
||||
case 'Bytes': {
|
||||
const entityValue = await __newString(value);
|
||||
const byteArray = await ByteArray.fromHexString(entityValue);
|
||||
const bytes = await Bytes.fromByteArray(byteArray);
|
||||
|
||||
return Value.fromBytes(bytes);
|
||||
}
|
||||
|
||||
// Return default as string for enum or custom type.
|
||||
default: {
|
||||
const entityValue = await __newString(value);
|
||||
|
||||
return Value.fromString(entityValue);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -100,6 +100,10 @@ export class Indexer implements IndexerInterface {
|
||||
assert(blockHash);
|
||||
assert(data);
|
||||
}
|
||||
|
||||
getEntityTypesMap (): Map<string, { [key: string]: string; }> {
|
||||
return new Map();
|
||||
}
|
||||
}
|
||||
|
||||
class SyncStatus implements SyncStatusInterface {
|
||||
|
@ -90,6 +90,7 @@ export class Indexer implements IndexerInterface {
|
||||
|
||||
_ipfsClient: IPFSClient
|
||||
|
||||
_entityTypesMap: Map<string, { [key: string]: string }>
|
||||
_relationsMap: Map<any, { [key: string]: any }>
|
||||
|
||||
constructor (serverConfig: ServerConfig, db: Database, ethClient: EthClient, postgraphileClient: EthClient, ethProvider: BaseProvider, jobQueue: JobQueue, graphWatcher: GraphWatcher) {
|
||||
@ -117,6 +118,9 @@ export class Indexer implements IndexerInterface {
|
||||
|
||||
this._ipfsClient = new IPFSClient(this._serverConfig.ipfsApiAddr);
|
||||
|
||||
this._entityTypesMap = new Map();
|
||||
this._populateEntityTypesMap();
|
||||
|
||||
this._relationsMap = new Map();
|
||||
this._populateRelationsMap();
|
||||
}
|
||||
@ -738,6 +742,46 @@ export class Indexer implements IndexerInterface {
|
||||
return this._baseIndexer.getAncestorAtDepth(blockHash, depth);
|
||||
}
|
||||
|
||||
getEntityTypesMap (): Map<string, { [key: string]: string }> {
|
||||
return this._entityTypesMap;
|
||||
}
|
||||
|
||||
_populateEntityTypesMap (): void {
|
||||
this._entityTypesMap.set(
|
||||
'Author',
|
||||
{
|
||||
id: 'ID',
|
||||
blogCount: 'BigInt',
|
||||
name: 'String',
|
||||
rating: 'BigDecimal',
|
||||
paramInt: 'Int',
|
||||
paramBigInt: 'BigInt',
|
||||
paramBytes: 'Bytes'
|
||||
}
|
||||
);
|
||||
|
||||
this._entityTypesMap.set(
|
||||
'Blog',
|
||||
{
|
||||
id: 'ID',
|
||||
kind: 'BlogKind',
|
||||
isActive: 'Boolean',
|
||||
reviews: 'BigInt',
|
||||
author: 'Author',
|
||||
categories: 'Category'
|
||||
}
|
||||
);
|
||||
|
||||
this._entityTypesMap.set(
|
||||
'Category',
|
||||
{
|
||||
id: 'ID',
|
||||
name: 'String',
|
||||
count: 'BigInt'
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
_populateRelationsMap (): void {
|
||||
// Needs to be generated by codegen.
|
||||
this._relationsMap.set(Author, {
|
||||
|
@ -74,6 +74,7 @@ export interface IndexerInterface {
|
||||
cacheContract?: (contract: ContractInterface) => void;
|
||||
createDiffStaged?: (contractAddress: string, blockHash: string, data: any) => Promise<void>
|
||||
watchContract?: (address: string, kind: string, checkpoint: boolean, startingBlock: number) => Promise<void>
|
||||
getEntityTypesMap?: () => Map<string, { [key: string]: string }>
|
||||
}
|
||||
|
||||
export interface EventWatcherInterface {
|
||||
|
Loading…
Reference in New Issue
Block a user