From 800ad79baf9c9eb8c28f830d8f19d8f0f95cf1f7 Mon Sep 17 00:00:00 2001 From: nikugogoi <95nikass@gmail.com> Date: Wed, 17 Nov 2021 17:01:09 +0530 Subject: [PATCH] Handle subgraph schema entity array type and relation fields (#49) * Handle relation entities in subgraph * Modify eden-watcher entities to handle subgraph schema data types * Modify schema gql to match eden subgraph gql shape * Handle array type fields in subgraph schema * Fix store get api for array type fields in subgraph entities * Handle array type in eden-watcher and format await used in params --- packages/eden-watcher/package.json | 3 +- packages/eden-watcher/src/entity/Claim.ts | 8 +- .../eden-watcher/src/entity/Distribution.ts | 8 +- .../eden-watcher/src/entity/Distributor.ts | 7 +- packages/eden-watcher/src/entity/Epoch.ts | 17 +- packages/eden-watcher/src/entity/Network.ts | 25 +- .../eden-watcher/src/entity/ProducerEpoch.ts | 15 +- .../eden-watcher/src/entity/ProducerSet.ts | 7 +- .../src/entity/ProducerSetChange.ts | 5 +- .../eden-watcher/src/entity/RewardSchedule.ts | 20 +- packages/eden-watcher/src/entity/Slash.ts | 8 +- packages/eden-watcher/src/entity/Slot.ts | 9 +- packages/eden-watcher/src/entity/SlotClaim.ts | 15 +- packages/eden-watcher/src/schema.gql | 30 +- packages/graph-node/src/database.ts | 16 +- packages/graph-node/src/loader.ts | 78 +++-- packages/graph-node/src/utils.ts | 289 ++++++++++++------ .../subgraph/example1/generated/schema.ts | 74 +++++ .../test/subgraph/example1/schema.graphql | 8 + .../test/subgraph/example1/src/mapping.ts | 25 +- .../environments/local.toml | 2 +- .../src/entity/ExampleEntity.ts | 3 + .../src/entity/RelatedEntity.ts | 28 ++ packages/graph-test-watcher/src/schema.gql | 14 +- packages/util/src/misc.ts | 34 +++ 25 files changed, 527 insertions(+), 221 deletions(-) create mode 100644 packages/graph-test-watcher/src/entity/RelatedEntity.ts diff --git a/packages/eden-watcher/package.json b/packages/eden-watcher/package.json index 87780fb2..569517a7 100644 --- a/packages/eden-watcher/package.json +++ b/packages/eden-watcher/package.json @@ -48,7 +48,8 @@ "multiformats": "^9.4.8", "reflect-metadata": "^0.1.13", "typeorm": "^0.2.32", - "yargs": "^17.0.1" + "yargs": "^17.0.1", + "decimal.js": "^10.3.1" }, "devDependencies": { "@ethersproject/abi": "^5.3.0", diff --git a/packages/eden-watcher/src/entity/Claim.ts b/packages/eden-watcher/src/entity/Claim.ts index 53ab3a5a..e0e26698 100644 --- a/packages/eden-watcher/src/entity/Claim.ts +++ b/packages/eden-watcher/src/entity/Claim.ts @@ -2,8 +2,8 @@ // Copyright 2021 Vulcanize, Inc. // -import { Entity, PrimaryColumn, Column, ManyToOne } from 'typeorm'; -import { Account } from './Account'; +import { Entity, PrimaryColumn, Column } from 'typeorm'; + import { bigintTransformer } from '@vulcanize/util'; @Entity() @@ -23,8 +23,8 @@ export class Claim { @Column('bigint', { transformer: bigintTransformer }) index!: bigint; - @ManyToOne(() => Account) - account!: Account; + @Column('varchar') + account!: string; @Column('bigint', { transformer: bigintTransformer }) totalEarned!: bigint; diff --git a/packages/eden-watcher/src/entity/Distribution.ts b/packages/eden-watcher/src/entity/Distribution.ts index f817e39d..1617652b 100644 --- a/packages/eden-watcher/src/entity/Distribution.ts +++ b/packages/eden-watcher/src/entity/Distribution.ts @@ -2,8 +2,8 @@ // Copyright 2021 Vulcanize, Inc. // -import { Entity, PrimaryColumn, Column, ManyToOne } from 'typeorm'; -import { Distributor } from './Distributor'; +import { Entity, PrimaryColumn, Column } from 'typeorm'; + import { bigintTransformer } from '@vulcanize/util'; @Entity() @@ -17,8 +17,8 @@ export class Distribution { @Column('integer') blockNumber!: number; - @ManyToOne(() => Distributor) - distributor!: Distributor; + @Column('varchar') + distributor!: string; @Column('bigint', { transformer: bigintTransformer }) timestamp!: bigint; diff --git a/packages/eden-watcher/src/entity/Distributor.ts b/packages/eden-watcher/src/entity/Distributor.ts index f243f556..b02669d5 100644 --- a/packages/eden-watcher/src/entity/Distributor.ts +++ b/packages/eden-watcher/src/entity/Distributor.ts @@ -2,8 +2,7 @@ // Copyright 2021 Vulcanize, Inc. // -import { Entity, PrimaryColumn, Column, ManyToOne } from 'typeorm'; -import { Distribution } from './Distribution'; +import { Entity, PrimaryColumn, Column } from 'typeorm'; @Entity() export class Distributor { @@ -16,6 +15,6 @@ export class Distributor { @Column('integer') blockNumber!: number; - @ManyToOne(() => Distribution, { nullable: true }) - currentDistribution!: Distribution; + @Column('varchar', { nullable: true }) + currentDistribution!: string; } diff --git a/packages/eden-watcher/src/entity/Epoch.ts b/packages/eden-watcher/src/entity/Epoch.ts index decf58de..37c351b7 100644 --- a/packages/eden-watcher/src/entity/Epoch.ts +++ b/packages/eden-watcher/src/entity/Epoch.ts @@ -3,9 +3,10 @@ // import { Entity, PrimaryColumn, Column, ManyToOne } from 'typeorm'; -import { Block } from './Block'; +import Decimal from 'decimal.js'; + import { ProducerEpoch } from './ProducerEpoch'; -import { bigintTransformer } from '@vulcanize/util'; +import { bigintTransformer, decimalTransformer } from '@vulcanize/util'; @Entity() export class Epoch { @@ -24,11 +25,11 @@ export class Epoch { @Column('bigint', { transformer: bigintTransformer }) epochNumber!: bigint; - @ManyToOne(() => Block, { nullable: true }) - startBlock!: Block; + @Column('varchar', { nullable: true }) + startBlock!: string; - @ManyToOne(() => Block, { nullable: true }) - endBlock!: Block; + @Column('varchar', { nullable: true }) + endBlock!: string; @Column('bigint', { transformer: bigintTransformer }) producerBlocks!: bigint; @@ -36,8 +37,8 @@ export class Epoch { @Column('bigint', { transformer: bigintTransformer }) allBlocks!: bigint; - @Column('varchar') - producerBlocksRatio!: string; + @Column('numeric', { default: 0, transformer: decimalTransformer }) + producerBlocksRatio!: Decimal; @ManyToOne(() => ProducerEpoch) producerRewards!: ProducerEpoch; diff --git a/packages/eden-watcher/src/entity/Network.ts b/packages/eden-watcher/src/entity/Network.ts index 7964de6c..066c78fe 100644 --- a/packages/eden-watcher/src/entity/Network.ts +++ b/packages/eden-watcher/src/entity/Network.ts @@ -2,10 +2,9 @@ // Copyright 2021 Vulcanize, Inc. // -import { Entity, PrimaryColumn, Column, ManyToOne } from 'typeorm'; -import { Slot } from './Slot'; -import { Staker } from './Staker'; -import { bigintTransformer } from '@vulcanize/util'; +import { Entity, PrimaryColumn, Column } from 'typeorm'; + +import { bigintArrayTransformer, bigintTransformer } from '@vulcanize/util'; @Entity() export class Network { @@ -18,17 +17,17 @@ export class Network { @Column('integer') blockNumber!: number; - @ManyToOne(() => Slot, { nullable: true }) - slot0!: Slot; + @Column('varchar', { nullable: true }) + slot0!: string; - @ManyToOne(() => Slot, { nullable: true }) - slot1!: Slot; + @Column('varchar', { nullable: true }) + slot1!: string; - @ManyToOne(() => Slot, { nullable: true }) - slot2!: Slot; + @Column('varchar', { nullable: true }) + slot2!: string; - @ManyToOne(() => Staker) - stakers!: Staker; + @Column('varchar', { array: true }) + stakers!: string[]; @Column('bigint', { nullable: true, transformer: bigintTransformer }) numStakers!: bigint; @@ -36,6 +35,6 @@ export class Network { @Column('bigint', { transformer: bigintTransformer }) totalStaked!: bigint; - @Column('bigint', { array: true }) + @Column('bigint', { transformer: bigintArrayTransformer, array: true }) stakedPercentiles!: bigint[]; } diff --git a/packages/eden-watcher/src/entity/ProducerEpoch.ts b/packages/eden-watcher/src/entity/ProducerEpoch.ts index 1dfe983a..bf86124f 100644 --- a/packages/eden-watcher/src/entity/ProducerEpoch.ts +++ b/packages/eden-watcher/src/entity/ProducerEpoch.ts @@ -2,9 +2,10 @@ // Copyright 2021 Vulcanize, Inc. // -import { Entity, PrimaryColumn, Column, ManyToOne } from 'typeorm'; -import { Epoch } from './Epoch'; -import { bigintTransformer } from '@vulcanize/util'; +import { Entity, PrimaryColumn, Column } from 'typeorm'; +import Decimal from 'decimal.js'; + +import { bigintTransformer, decimalTransformer } from '@vulcanize/util'; @Entity() export class ProducerEpoch { @@ -20,8 +21,8 @@ export class ProducerEpoch { @Column('varchar') address!: string; - @ManyToOne(() => Epoch) - epoch!: Epoch; + @Column('varchar') + epoch!: string; @Column('bigint', { transformer: bigintTransformer }) totalRewards!: bigint; @@ -29,6 +30,6 @@ export class ProducerEpoch { @Column('bigint', { transformer: bigintTransformer }) blocksProduced!: bigint; - @Column('varchar') - blocksProducedRatio!: string; + @Column('numeric', { default: 0, transformer: decimalTransformer }) + blocksProducedRatio!: Decimal; } diff --git a/packages/eden-watcher/src/entity/ProducerSet.ts b/packages/eden-watcher/src/entity/ProducerSet.ts index 481aa006..dba58e37 100644 --- a/packages/eden-watcher/src/entity/ProducerSet.ts +++ b/packages/eden-watcher/src/entity/ProducerSet.ts @@ -2,8 +2,7 @@ // Copyright 2021 Vulcanize, Inc. // -import { Entity, PrimaryColumn, Column, ManyToOne } from 'typeorm'; -import { Producer } from './Producer'; +import { Entity, PrimaryColumn, Column } from 'typeorm'; @Entity() export class ProducerSet { @@ -16,6 +15,6 @@ export class ProducerSet { @Column('integer') blockNumber!: number; - @ManyToOne(() => Producer) - producers!: Producer; + @Column('varchar', { array: true }) + producers!: string[]; } diff --git a/packages/eden-watcher/src/entity/ProducerSetChange.ts b/packages/eden-watcher/src/entity/ProducerSetChange.ts index a690e99b..21a77b51 100644 --- a/packages/eden-watcher/src/entity/ProducerSetChange.ts +++ b/packages/eden-watcher/src/entity/ProducerSetChange.ts @@ -27,6 +27,9 @@ export class ProducerSetChange { @Column('varchar') producer!: string; - @Column('integer') + @Column({ + type: 'enum', + enum: ProducerSetChangeType + }) changeType!: ProducerSetChangeType; } diff --git a/packages/eden-watcher/src/entity/RewardSchedule.ts b/packages/eden-watcher/src/entity/RewardSchedule.ts index 135f2b84..6e6b7c28 100644 --- a/packages/eden-watcher/src/entity/RewardSchedule.ts +++ b/packages/eden-watcher/src/entity/RewardSchedule.ts @@ -2,9 +2,7 @@ // Copyright 2021 Vulcanize, Inc. // -import { Entity, PrimaryColumn, Column, ManyToOne } from 'typeorm'; -import { RewardScheduleEntry } from './RewardScheduleEntry'; -import { Epoch } from './Epoch'; +import { Entity, PrimaryColumn, Column } from 'typeorm'; @Entity() export class RewardSchedule { @@ -17,15 +15,15 @@ export class RewardSchedule { @Column('integer') blockNumber!: number; - @ManyToOne(() => RewardScheduleEntry) - rewardScheduleEntries!: RewardScheduleEntry; + @Column('varchar', { array: true }) + rewardScheduleEntries!: string[]; - @ManyToOne(() => Epoch, { nullable: true }) - lastEpoch!: Epoch; + @Column('varchar', { nullable: true }) + lastEpoch!: string; - @ManyToOne(() => Epoch, { nullable: true }) - pendingEpoch!: Epoch; + @Column('varchar', { nullable: true }) + pendingEpoch!: string; - @ManyToOne(() => RewardScheduleEntry, { nullable: true }) - activeRewardScheduleEntry!: RewardScheduleEntry; + @Column('varchar', { nullable: true }) + activeRewardScheduleEntry!: string; } diff --git a/packages/eden-watcher/src/entity/Slash.ts b/packages/eden-watcher/src/entity/Slash.ts index 5589409b..7865abcf 100644 --- a/packages/eden-watcher/src/entity/Slash.ts +++ b/packages/eden-watcher/src/entity/Slash.ts @@ -2,8 +2,8 @@ // Copyright 2021 Vulcanize, Inc. // -import { Entity, PrimaryColumn, Column, ManyToOne } from 'typeorm'; -import { Account } from './Account'; +import { Entity, PrimaryColumn, Column } from 'typeorm'; + import { bigintTransformer } from '@vulcanize/util'; @Entity() @@ -20,8 +20,8 @@ export class Slash { @Column('bigint', { transformer: bigintTransformer }) timestamp!: bigint; - @ManyToOne(() => Account) - account!: Account; + @Column('varchar') + account!: string; @Column('bigint', { transformer: bigintTransformer }) slashed!: bigint; diff --git a/packages/eden-watcher/src/entity/Slot.ts b/packages/eden-watcher/src/entity/Slot.ts index 862add69..000e7bfd 100644 --- a/packages/eden-watcher/src/entity/Slot.ts +++ b/packages/eden-watcher/src/entity/Slot.ts @@ -3,8 +3,11 @@ // import { Entity, PrimaryColumn, Column, ManyToOne } from 'typeorm'; +import Decimal from 'decimal.js'; + +import { bigintTransformer, decimalTransformer } from '@vulcanize/util'; + import { SlotClaim } from './SlotClaim'; -import { bigintTransformer } from '@vulcanize/util'; @Entity() export class Slot { @@ -35,8 +38,8 @@ export class Slot { @Column('bigint', { transformer: bigintTransformer }) expirationTime!: bigint; - @Column('varchar') - taxRatePerDay!: string; + @Column('numeric', { default: 0, transformer: decimalTransformer }) + taxRatePerDay!: Decimal; @ManyToOne(() => SlotClaim) claims!: SlotClaim; diff --git a/packages/eden-watcher/src/entity/SlotClaim.ts b/packages/eden-watcher/src/entity/SlotClaim.ts index c8804b03..4893679b 100644 --- a/packages/eden-watcher/src/entity/SlotClaim.ts +++ b/packages/eden-watcher/src/entity/SlotClaim.ts @@ -2,9 +2,10 @@ // Copyright 2021 Vulcanize, Inc. // -import { Entity, PrimaryColumn, Column, ManyToOne } from 'typeorm'; -import { Slot } from './Slot'; -import { bigintTransformer } from '@vulcanize/util'; +import { Entity, PrimaryColumn, Column } from 'typeorm'; +import Decimal from 'decimal.js'; + +import { bigintTransformer, decimalTransformer } from '@vulcanize/util'; @Entity() export class SlotClaim { @@ -17,8 +18,8 @@ export class SlotClaim { @Column('integer') blockNumber!: number; - @ManyToOne(() => Slot) - slot!: Slot; + @Column('varchar') + slot!: string; @Column('varchar') owner!: string; @@ -35,6 +36,6 @@ export class SlotClaim { @Column('bigint', { transformer: bigintTransformer }) expirationTime!: bigint; - @Column('varchar') - taxRatePerDay!: string; + @Column('numeric', { default: 0, transformer: decimalTransformer }) + taxRatePerDay!: Decimal; } diff --git a/packages/eden-watcher/src/schema.gql b/packages/eden-watcher/src/schema.gql index ca706ff7..3d26b4c5 100644 --- a/packages/eden-watcher/src/schema.gql +++ b/packages/eden-watcher/src/schema.gql @@ -1,5 +1,9 @@ scalar BigInt +scalar Bytes + +scalar BigDecimal + type Proof { data: String! } @@ -239,7 +243,7 @@ type Query { type Producer { id: ID! active: Boolean! - rewardCollector: String + rewardCollector: Bytes rewards: BigInt! confirmedBlocks: BigInt! pendingEpochBlocks: BigInt! @@ -253,7 +257,7 @@ type ProducerSet { type ProducerSetChange { id: ID! blockNumber: BigInt! - producer: String! + producer: Bytes! changeType: ProducerSetChangeType! } @@ -265,8 +269,8 @@ enum ProducerSetChangeType { type ProducerRewardCollectorChange { id: ID! blockNumber: BigInt! - producer: String! - rewardCollector: String! + producer: Bytes! + rewardCollector: Bytes! } type RewardScheduleEntry { @@ -311,39 +315,39 @@ type Epoch { endBlock: Block producerBlocks: BigInt! allBlocks: BigInt! - producerBlocksRatio: String! + producerBlocksRatio: BigDecimal! producerRewards: [ProducerEpoch!]! } type ProducerEpoch { id: ID! - address: String! + address: Bytes! epoch: Epoch! totalRewards: BigInt! blocksProduced: BigInt! - blocksProducedRatio: String! + blocksProducedRatio: BigDecimal! } type SlotClaim { id: ID! slot: Slot! - owner: String! + owner: Bytes! winningBid: BigInt! oldBid: BigInt! startTime: BigInt! expirationTime: BigInt! - taxRatePerDay: String! + taxRatePerDay: BigDecimal! } type Slot { id: ID! - owner: String! - delegate: String! + owner: Bytes! + delegate: Bytes! winningBid: BigInt! oldBid: BigInt! startTime: BigInt! expirationTime: BigInt! - taxRatePerDay: String! + taxRatePerDay: BigDecimal! claims: [SlotClaim!]! } @@ -374,7 +378,7 @@ type Distribution { distributor: Distributor! timestamp: BigInt! distributionNumber: BigInt! - merkleRoot: String! + merkleRoot: Bytes! metadataURI: String! } diff --git a/packages/graph-node/src/database.ts b/packages/graph-node/src/database.ts index 66b7823f..f2c2191a 100644 --- a/packages/graph-node/src/database.ts +++ b/packages/graph-node/src/database.ts @@ -90,14 +90,14 @@ export class Database { return true; }).map(async (field) => { - const { type, propertyName } = field; - // Fill _blockNumber as blockNumber and _blockHash as blockHash in the entityInstance (wasm). - if (['_blockNumber', '_blockHash'].includes(propertyName)) { - return toEntityValue(instanceExports, entityInstance, data, type.toString(), propertyName.slice(1)); + if (['_blockNumber', '_blockHash'].includes(field.propertyName)) { + field.propertyName = field.propertyName.slice(1); + + return toEntityValue(instanceExports, entityInstance, data, field); } - return toEntityValue(instanceExports, entityInstance, data, type.toString(), propertyName); + return toEntityValue(instanceExports, entityInstance, data, field); }, {}); await Promise.all(entityValuePromises); @@ -115,7 +115,7 @@ export class Database { async getEntityValues (instanceExports: any, block: Block, entityInstance: any, entityFields: any): Promise<{ [key: string]: any } > { const entityValuePromises = entityFields.map(async (field: any) => { - const { type, propertyName } = field; + const { propertyName } = field; // Get blockHash property for db entry from block instance. if (propertyName === 'blockHash') { @@ -129,10 +129,10 @@ export class Database { // Get blockNumber as _blockNumber and blockHash as _blockHash from the entityInstance (wasm). if (['_blockNumber', '_blockHash'].includes(propertyName)) { - return fromEntityValue(instanceExports, entityInstance, type.toString(), propertyName.slice(1)); + return fromEntityValue(instanceExports, entityInstance, propertyName); } - return fromEntityValue(instanceExports, entityInstance, type.toString(), propertyName); + return fromEntityValue(instanceExports, entityInstance, propertyName); }, {}); const entityValues = await Promise.all(entityValuePromises); diff --git a/packages/graph-node/src/loader.ts b/packages/graph-node/src/loader.ts index 0b00e78c..8f289957 100644 --- a/packages/graph-node/src/loader.ts +++ b/packages/graph-node/src/loader.ts @@ -105,17 +105,27 @@ export const instantiate = async (database: Database, indexer: IndexerInterface, 'ethereum.call': async (call: number) => { const smartContractCall = await ethereum.SmartContractCall.wrap(call); - const contractAddress = await Address.wrap(await smartContractCall.contractAddress); - const contractName = __getString(await smartContractCall.contractName); - const functionName = __getString(await smartContractCall.functionName); - const functionSignature = __getString(await smartContractCall.functionSignature); - let functionParams = __getArray(await smartContractCall.functionParams); + const contractAddressPtr = await smartContractCall.contractAddress; + const contractAddress = await Address.wrap(contractAddressPtr); + + const contractNamePtr = await smartContractCall.contractName; + const contractName = __getString(contractNamePtr); + + const functionNamePtr = await smartContractCall.functionName; + const functionName = __getString(functionNamePtr); + + const functionSignaturePtr = await smartContractCall.functionSignature; + const functionSignature = __getString(functionSignaturePtr); + + const functionParamsPtr = await smartContractCall.functionParams; + let functionParams = __getArray(functionParamsPtr); console.log('ethereum.call params'); console.log('functionSignature:', functionSignature); const abi = abis[contractName]; - const contract = new Contract(__getString(await contractAddress.toHexString()), abi, provider); + const contractAddressStringPtr = await contractAddress.toHexString(); + const contract = new Contract(__getString(contractAddressStringPtr), abi, provider); try { const functionParamsPromise = functionParams.map(async param => { @@ -142,7 +152,8 @@ export const instantiate = async (database: Database, indexer: IndexerInterface, }); const resultPtrArray: any[] = await Promise.all(resultPtrArrayPromise); - const res = await __newArray(await getIdOfType(TypeId.ArrayEthereumValue), resultPtrArray); + const arrayEthereumValueId = await getIdOfType(TypeId.ArrayEthereumValue); + const res = await __newArray(arrayEthereumValueId, resultPtrArray); return res; } catch (err) { @@ -194,10 +205,12 @@ export const instantiate = async (database: Database, indexer: IndexerInterface, const bigDecimaly = BigDecimal.wrap(y); - const yDigitsBigIntArray = __getArray(await bigDecimaly.digits); + const digitsPtr = await bigDecimaly.digits; + const yDigitsBigIntArray = __getArray(digitsPtr); const yDigits = BigNumber.from(yDigitsBigIntArray); - const yExpBigIntArray = __getArray(await bigDecimaly.exp); + const expPtr = await bigDecimaly.exp; + const yExpBigIntArray = __getArray(expPtr); const yExp = BigNumber.from(yExpBigIntArray); console.log('y digits and exp', yDigits, yExp); @@ -205,11 +218,17 @@ export const instantiate = async (database: Database, indexer: IndexerInterface, 'bigDecimal.toString': async (bigDecimal: number) => { const bigDecimalInstance = BigDecimal.wrap(bigDecimal); - const digitsBigInt = BigInt.wrap(await bigDecimalInstance.digits); - const expBigInt = BigInt.wrap(await bigDecimalInstance.exp); + const digitsPtr = await bigDecimalInstance.digits; + const digitsBigInt = BigInt.wrap(digitsPtr); - const digits = __getString(await digitsBigInt.toString()); - const exp = __getString(await expBigInt.toString()); + const expPtr = await bigDecimalInstance.exp; + const expBigInt = BigInt.wrap(expPtr); + + const digitsStringPtr = await digitsBigInt.toString(); + const digits = __getString(digitsStringPtr); + + const expStringPtr = await expBigInt.toString(); + const exp = __getString(expStringPtr); const decimal = new Decimal(`${digits}e${exp}`); const ptr = __newString(decimal.toFixed()); @@ -224,11 +243,13 @@ export const instantiate = async (database: Database, indexer: IndexerInterface, const digits = decimal.d.join(''); const digitsBigNumber = BigNumber.from(digits); const signBigNumber = BigNumber.from(decimal.s); - const digitsBigInt = await BigInt.fromString(await __newString(digitsBigNumber.mul(signBigNumber).toString())); + const digitsStringPtr = await __newString(digitsBigNumber.mul(signBigNumber).toString()); + const digitsBigInt = await BigInt.fromString(digitsStringPtr); // Calculate exp after converting digits to BigInt above. const exp = decimal.e - digits.length + 1; - const expBigInt = await BigInt.fromString(await __newString(exp.toString())); + const expStringPtr = await __newString(exp.toString()); + const expBigInt = await BigInt.fromString(expStringPtr); const bigDecimal = await BigDecimal.__new(digitsBigInt); bigDecimal.exp = expBigInt; @@ -259,10 +280,12 @@ export const instantiate = async (database: Database, indexer: IndexerInterface, }, 'bigInt.plus': async (x: number, y: number) => { const xBigInt = await BigInt.wrap(x); - const xBigNumber = BigNumber.from(__getString(await xBigInt.toString())); + const xStringPtr = await xBigInt.toString(); + const xBigNumber = BigNumber.from(__getString(xStringPtr)); const yBigInt = await BigInt.wrap(y); - const yBigNumber = BigNumber.from(__getString(await yBigInt.toString())); + const yStringPtr = await yBigInt.toString(); + const yBigNumber = BigNumber.from(__getString(yStringPtr)); const sum = xBigNumber.add(yBigNumber); const ptr = await __newString(sum.toString()); @@ -272,10 +295,12 @@ export const instantiate = async (database: Database, indexer: IndexerInterface, }, 'bigInt.minus': async (x: number, y: number) => { const xBigInt = await BigInt.wrap(x); - const xBigNumber = BigNumber.from(__getString(await xBigInt.toString())); + const xStringPtr = await xBigInt.toString(); + const xBigNumber = BigNumber.from(__getString(xStringPtr)); const yBigInt = await BigInt.wrap(y); - const yBigNumber = BigNumber.from(__getString(await yBigInt.toString())); + const yStringPtr = await yBigInt.toString(); + const yBigNumber = BigNumber.from(__getString(yStringPtr)); const diff = xBigNumber.sub(yBigNumber); const ptr = await __newString(diff.toString()); @@ -285,10 +310,12 @@ export const instantiate = async (database: Database, indexer: IndexerInterface, }, 'bigInt.times': async (x: number, y: number) => { const xBigInt = await BigInt.wrap(x); - const xBigNumber = BigNumber.from(__getString(await xBigInt.toString())); + const xStringPtr = await xBigInt.toString(); + const xBigNumber = BigNumber.from(__getString(xStringPtr)); const yBigInt = await BigInt.wrap(y); - const yBigNumber = BigNumber.from(__getString(await yBigInt.toString())); + const yStringPtr = await yBigInt.toString(); + const yBigNumber = BigNumber.from(__getString(yStringPtr)); const product = xBigNumber.mul(yBigNumber); const ptr = await __newString(product.toString()); @@ -298,10 +325,12 @@ export const instantiate = async (database: Database, indexer: IndexerInterface, }, 'bigInt.dividedBy': async (x: number, y: number) => { const xBigInt = await BigInt.wrap(x); - const xBigNumber = BigNumber.from(__getString(await xBigInt.toString())); + const xStringPtr = await xBigInt.toString(); + const xBigNumber = BigNumber.from(__getString(xStringPtr)); const yBigInt = await BigInt.wrap(y); - const yBigNumber = BigNumber.from(__getString(await yBigInt.toString())); + const yStringPtr = await yBigInt.toString(); + const yBigNumber = BigNumber.from(__getString(yStringPtr)); const quotient = xBigNumber.div(yBigNumber); const ptr = await __newString(quotient.toString()); @@ -334,7 +363,8 @@ export const instantiate = async (database: Database, indexer: IndexerInterface, datasource: { 'dataSource.address': async () => { assert(dataSource); - return Address.fromString(await __newString(dataSource.address)); + const addressStringPtr = await __newString(dataSource.address); + return Address.fromString(addressStringPtr); } } }; diff --git a/packages/graph-node/src/utils.ts b/packages/graph-node/src/utils.ts index 9c72be08..d8101813 100644 --- a/packages/graph-node/src/utils.ts +++ b/packages/graph-node/src/utils.ts @@ -3,9 +3,11 @@ import path from 'path'; import fs from 'fs-extra'; import debug from 'debug'; import yaml from 'js-yaml'; +import Decimal from 'decimal.js'; +import { ColumnType } from 'typeorm'; +import { ColumnMetadata } from 'typeorm/metadata/ColumnMetadata'; import { TypeId, EthereumValueKind, ValueKind } from './types'; -import Decimal from 'decimal.js'; const log = debug('vulcanize:utils'); @@ -57,7 +59,8 @@ export const fromEthereumValue = async (instanceExports: any, value: any): Promi switch (kind) { case EthereumValueKind.ADDRESS: { - const address = Address.wrap(await value.toAddress()); + const addressPtr = await value.toAddress(); + const address = Address.wrap(addressPtr); const addressStringPtr = await address.toHexString(); return __getString(addressStringPtr); } @@ -76,7 +79,8 @@ export const fromEthereumValue = async (instanceExports: any, value: any): Promi case EthereumValueKind.INT: case EthereumValueKind.UINT: { - const bigInt = BigInt.wrap(await value.toBigInt()); + const bigIntPtr = await value.toBigInt(); + const bigInt = BigInt.wrap(bigIntPtr); const bigIntStringPtr = await bigInt.toString(); const bigIntString = __getString(bigIntStringPtr); return BigNumber.from(bigIntString); @@ -113,8 +117,8 @@ export const toEthereumValue = async (instanceExports: any, value: any, type: st // For uint/int type or enum type. if (isIntegerOrEnum) { - const valueString = await __newString(value.toString()); - const bigInt = await BigInt.fromString(valueString); + const valueStringPtr = await __newString(value.toString()); + const bigInt = await BigInt.fromString(valueStringPtr); let ethereumValue = await ethereum.Value.fromUnsignedBigInt(bigInt); if (Boolean(isInteger) && !isUnsigned) { @@ -125,18 +129,23 @@ export const toEthereumValue = async (instanceExports: any, value: any, type: st } if (type.startsWith('address')) { - return ethereum.Value.fromAddress(await Address.fromString(await __newString(value))); + const valueStringPtr = await __newString(value); + const addressPtr = await Address.fromString(valueStringPtr); + + return ethereum.Value.fromAddress(addressPtr); } // TODO: Check between fixed bytes and dynamic bytes. if (type.startsWith('bytes')) { - const byteArray = await ByteArray.fromHexString(await __newString(value)); + const valueStringPtr = await __newString(value); + const byteArray = await ByteArray.fromHexString(valueStringPtr); const bytes = await Bytes.fromByteArray(byteArray); return ethereum.Value.fromBytes(bytes); } // For string type. - return ethereum.Value.fromString(await __newString(value)); + const valueStringPtr = await __newString(value); + return ethereum.Value.fromString(valueStringPtr); }; /** @@ -168,14 +177,22 @@ export const createEvent = async (instanceExports: any, contractAddress: string, const block = await createBlock(instanceExports, blockData); // Fill transaction data. - const txHashByteArray = await ByteArray.fromHexString(await __newString(tx.hash)); + const txHashStringPtr = await __newString(tx.hash); + const txHashByteArray = await ByteArray.fromHexString(txHashStringPtr); const txHash = await Bytes.fromByteArray(txHashByteArray); const txIndex = await BigInt.fromI32(tx.index); - const txFrom = await Address.fromString(await __newString(tx.from)); + const txFromStringPtr = await __newString(tx.from); + const txFrom = await Address.fromString(txFromStringPtr); - const txTo = tx.to && await Address.fromString(await __newString(tx.to)); + const txToStringPtr = await __newString(tx.to); + const txTo = tx.to && await Address.fromString(txToStringPtr); + + const txValuePtr = await BigInt.fromI32(0); + const txGasLimitPtr = await BigInt.fromI32(0); + const txGasPricePtr = await BigInt.fromI32(0); + const txinputPtr = await Bytes.empty(); // Missing fields from watcher in transaction data: // value @@ -187,33 +204,39 @@ export const createEvent = async (instanceExports: any, contractAddress: string, txIndex, txFrom, txTo, - await BigInt.fromI32(0), - await BigInt.fromI32(0), - await BigInt.fromI32(0), - await Bytes.empty() + txValuePtr, + txGasLimitPtr, + txGasPricePtr, + txinputPtr ); const eventParamArrayPromise = eventParamsData.map(async data => { const { name, value, kind } = data; const ethValue = await toEthereumValue(instanceExports, value, kind); + const namePtr = await __newString(name); return ethereum.EventParam.__new( - await __newString(name), + namePtr, ethValue ); }); const eventParamArray = await Promise.all(eventParamArrayPromise); - const eventParams = await __newArray(await idOfType(TypeId.ArrayEventParam), eventParamArray); + const arrayEventParamId = await idOfType(TypeId.ArrayEventParam); + const eventParams = await __newArray(arrayEventParamId, eventParamArray); const addStrPtr = await __newString(contractAddress); + const eventAddressPtr = await Address.fromString(addStrPtr); + + const eventIndexPtr = await BigInt.fromI32(eventIndex); + const transactionLogIndexPtr = await BigInt.fromI32(0); // Create event to be passed to handler. return ethereum.Event.__new( - await Address.fromString(addStrPtr), - await BigInt.fromI32(eventIndex), - await BigInt.fromI32(0), + eventAddressPtr, + eventIndexPtr, + transactionLogIndexPtr, null, block, transaction, @@ -232,26 +255,40 @@ export const createBlock = async (instanceExports: any, blockData: Block): Promi } = instanceExports; // Fill block data. - const blockHashByteArray = await ByteArray.fromHexString(await __newString(blockData.blockHash)); + const blockHashStringPtr = await __newString(blockData.blockHash); + const blockHashByteArray = await ByteArray.fromHexString(blockHashStringPtr); const blockHash = await Bytes.fromByteArray(blockHashByteArray); - const parentHashByteArray = await ByteArray.fromHexString(await __newString(blockData.parentHash)); + const parentHashStringPtr = await __newString(blockData.parentHash); + const parentHashByteArray = await ByteArray.fromHexString(parentHashStringPtr); const parentHash = await Bytes.fromByteArray(parentHashByteArray); - const blockNumber = await BigInt.fromString(await __newString(blockData.blockNumber)); + const blockNumberStringPtr = await __newString(blockData.blockNumber); + const blockNumber = await BigInt.fromString(blockNumberStringPtr); - const blockTimestamp = await BigInt.fromString(await __newString(blockData.timestamp)); + const timestampStringPtr = await __newString(blockData.timestamp); + const blockTimestamp = await BigInt.fromString(timestampStringPtr); - const stateRootByteArray = await ByteArray.fromHexString(await __newString(blockData.stateRoot)); + const stateRootStringPtr = await __newString(blockData.stateRoot); + const stateRootByteArray = await ByteArray.fromHexString(stateRootStringPtr); const stateRoot = await Bytes.fromByteArray(stateRootByteArray); - const transactionsRootByteArray = await ByteArray.fromHexString(await __newString(blockData.txRoot)); + const txRootStringPtr = await __newString(blockData.txRoot); + const transactionsRootByteArray = await ByteArray.fromHexString(txRootStringPtr); const transactionsRoot = await Bytes.fromByteArray(transactionsRootByteArray); - const receiptsRootByteArray = await ByteArray.fromHexString(await __newString(blockData.receiptRoot)); + const receiptRootStringPtr = await __newString(blockData.receiptRoot); + const receiptsRootByteArray = await ByteArray.fromHexString(receiptRootStringPtr); const receiptsRoot = await Bytes.fromByteArray(receiptsRootByteArray); - const totalDifficulty = await BigInt.fromString(await __newString(blockData.td)); + const tdStringPtr = await __newString(blockData.td); + const totalDifficulty = await BigInt.fromString(tdStringPtr); + + const unclesHashPtr = await Bytes.empty(); + const authorPtr = await Address.zero(); + const gasUsedPtr = await BigInt.fromI32(0); + const gasLimitPtr = await BigInt.fromI32(0); + const difficultyPtr = await BigInt.fromI32(0); // Missing fields from watcher in block data: // unclesHash @@ -263,16 +300,16 @@ export const createBlock = async (instanceExports: any, blockData: Block): Promi return await ethereum.Block.__new( blockHash, parentHash, - await Bytes.empty(), - await Address.zero(), + unclesHashPtr, + authorPtr, stateRoot, transactionsRoot, receiptsRoot, blockNumber, - await BigInt.fromI32(0), - await BigInt.fromI32(0), + gasUsedPtr, + gasLimitPtr, blockTimestamp, - await BigInt.fromI32(0), + difficultyPtr, totalDifficulty, null ); @@ -286,109 +323,163 @@ export const getSubgraphConfig = async (subgraphPath: string): Promise => { throw new Error(`Config file not found: ${configFilePath}`); } - const config = yaml.load(await fs.readFile(configFilePath, 'utf8')); + const configFile = await fs.readFile(configFilePath, 'utf8'); + const config = yaml.load(configFile); log('config', JSON.stringify(config, null, 2)); return config; }; -export const toEntityValue = async (instanceExports: any, entityInstance: any, data: any, type: string, key: string) => { - const { __newString, BigInt: ExportBigInt, Value, ByteArray, Bytes, BigDecimal } = instanceExports; +export const toEntityValue = async (instanceExports: any, entityInstance: any, data: any, field: ColumnMetadata) => { + const { __newString, Value } = instanceExports; + const { type, isArray, propertyName } = field; + + const entityKey = await __newString(propertyName); + const entityValuePtr = await entityInstance.get(entityKey); + const subgraphValue = Value.wrap(entityValuePtr); + const value = data[propertyName]; + + const entityValue = await formatEntityValue(instanceExports, subgraphValue, type, value, isArray); + + return entityInstance.set(entityKey, entityValue); +}; + +export const fromEntityValue = async (instanceExports: any, entityInstance: any, key: string): Promise => { + const { __newString } = instanceExports; const entityKey = await __newString(key); - const value = data[key]; + const entityValuePtr = await entityInstance.get(entityKey); + + return parseEntityValue(instanceExports, entityValuePtr); +}; + +const parseEntityValue = async (instanceExports: any, valuePtr: number) => { + const { + __getString, + __getArray, + BigInt: ExportBigInt, + Bytes, + BigDecimal, + Value + } = instanceExports; + + const value = Value.wrap(valuePtr); + const kind = await value.kind; + + switch (kind) { + case ValueKind.STRING: { + const stringValue = await value.toString(); + return __getString(stringValue); + } + + case ValueKind.BYTES: { + const bytesPtr = await value.toBytes(); + const bytes = await Bytes.wrap(bytesPtr); + const bytesStringPtr = await bytes.toHexString(); + + return __getString(bytesStringPtr); + } + + case ValueKind.BOOL: { + const bool = await value.toBoolean(); + + return Boolean(bool); + } + + case ValueKind.INT: { + return value.toI32(); + } + + case ValueKind.BIGINT: { + const bigIntPtr = await value.toBigInt(); + const bigInt = ExportBigInt.wrap(bigIntPtr); + const bigIntStringPtr = await bigInt.toString(); + const bigIntString = __getString(bigIntStringPtr); + + return BigInt(bigIntString); + } + + case ValueKind.BIGDECIMAL: { + const bigDecimalPtr = await value.toBigDecimal(); + const bigDecimal = BigDecimal.wrap(bigDecimalPtr); + const bigDecimalStringPtr = await bigDecimal.toString(); + + return new Decimal(__getString(bigDecimalStringPtr)); + } + + case ValueKind.ARRAY: { + const arrayPtr = await value.toArray(); + const arr = await __getArray(arrayPtr); + const arrDataPromises = arr.map((arrValuePtr: any) => parseEntityValue(instanceExports, arrValuePtr)); + + return Promise.all(arrDataPromises); + } + + case ValueKind.NULL: { + return null; + } + + default: + throw new Error(`Unsupported value kind: ${kind}`); + } +}; + +const formatEntityValue = async (instanceExports: any, subgraphValue: any, type: ColumnType, value: any, isArray: boolean): Promise => { + 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); + const valueArray = await __newArray(arrayStoreValueId, dataArray); + + return Value.fromArray(valueArray); + } switch (type) { case 'varchar': { const entityValue = await __newString(value); - - const graphValue = Value.wrap(await entityInstance.get(entityKey)); - - const kind = await graphValue.kind; + const kind = await subgraphValue.kind; switch (kind) { case ValueKind.BYTES: { const byteArray = await ByteArray.fromHexString(entityValue); const bytes = await Bytes.fromByteArray(byteArray); - return entityInstance.setBytes(entityKey, bytes); + + return Value.fromBytes(bytes); } default: - return entityInstance.setString(entityKey, entityValue); + return Value.fromString(entityValue); } } case 'integer': { - return entityInstance.setI32(entityKey, value); + return Value.fromI32(value); } case 'bigint': { - const bigInt = await ExportBigInt.fromString(await __newString(value.toString())); + const valueStringPtr = await __newString(value.toString()); + const bigInt = await ExportBigInt.fromString(valueStringPtr); - return entityInstance.setBigInt(entityKey, bigInt); + return Value.fromBigInt(bigInt); } case 'boolean': { - return entityInstance.setBoolean(entityKey, value ? 1 : 0); + return Value.fromBoolean(value ? 1 : 0); } case 'enum': { const entityValue = await __newString(value); - return entityInstance.setString(entityKey, entityValue); + + return Value.fromString(entityValue); } case 'numeric': { - const bigDecimal = await BigDecimal.fromString(await __newString(value.toString())); - return entityInstance.setBigDecimal(entityKey, bigDecimal); - } + const valueStringPtr = await __newString(value.toString()); + const bigDecimal = await BigDecimal.fromString(valueStringPtr); - // TODO: Support more types. - default: - throw new Error(`Unsupported type: ${type}`); - } -}; - -export const fromEntityValue = async (instanceExports: any, entityInstance: any, type: string, key: string): Promise => { - const { __newString, __getString, BigInt: ExportBigInt, Value, BigDecimal, Bytes } = instanceExports; - const entityKey = await __newString(key); - - switch (type) { - case 'varchar': { - const value = Value.wrap(await entityInstance.get(entityKey)); - - const kind = await value.kind; - - switch (kind) { - case ValueKind.BYTES: { - const bytes = await Bytes.wrap(await value.toBytes()); - const bytesStringPtr = await bytes.toHexString(); - return __getString(bytesStringPtr); - } - - default: - return __getString(await entityInstance.getString(entityKey)); - } - } - - case 'integer': { - return entityInstance.getI32(entityKey); - } - - case 'bigint': { - const bigInt = ExportBigInt.wrap(await entityInstance.getBigInt(entityKey)); - return BigInt(__getString(await bigInt.toString())); - } - - case 'boolean': { - return Boolean(await entityInstance.getBoolean(entityKey)); - } - - case 'enum': { - return __getString(await entityInstance.getString(entityKey)); - } - - case 'numeric': { - const bigDecimal = BigDecimal.wrap(await entityInstance.getBigDecimal(entityKey)); - return new Decimal(__getString(await bigDecimal.toString())); + return Value.fromBigDecimal(bigDecimal); } // TODO: Support more types. diff --git a/packages/graph-node/test/subgraph/example1/generated/schema.ts b/packages/graph-node/test/subgraph/example1/generated/schema.ts index 7c910300..d4b490cf 100644 --- a/packages/graph-node/test/subgraph/example1/generated/schema.ts +++ b/packages/graph-node/test/subgraph/example1/generated/schema.ts @@ -12,6 +12,70 @@ import { BigDecimal } from "@graphprotocol/graph-ts"; +export class RelatedEntity extends Entity { + constructor(id: string) { + super(); + this.set("id", Value.fromString(id)); + + this.set("paramBigInt", Value.fromBigInt(BigInt.zero())); + this.set("examples", Value.fromStringArray(new Array(0))); + this.set("bigIntArray", Value.fromBigIntArray(new Array(0))); + } + + save(): void { + let id = this.get("id"); + assert(id != null, "Cannot save RelatedEntity entity without an ID"); + if (id) { + assert( + id.kind == ValueKind.STRING, + "Cannot save RelatedEntity entity with non-string ID. " + + 'Considering using .toHex() to convert the "id" to a string.' + ); + store.set("RelatedEntity", id.toString(), this); + } + } + + static load(id: string): RelatedEntity | null { + return changetype(store.get("RelatedEntity", id)); + } + + get id(): string { + let value = this.get("id"); + return value!.toString(); + } + + set id(value: string) { + this.set("id", Value.fromString(value)); + } + + get paramBigInt(): BigInt { + let value = this.get("paramBigInt"); + return value!.toBigInt(); + } + + set paramBigInt(value: BigInt) { + this.set("paramBigInt", Value.fromBigInt(value)); + } + + get examples(): Array { + let value = this.get("examples"); + return value!.toStringArray(); + } + + set examples(value: Array) { + this.set("examples", Value.fromStringArray(value)); + } + + get bigIntArray(): Array { + let value = this.get("bigIntArray"); + return value!.toBigIntArray(); + } + + set bigIntArray(value: Array) { + this.set("bigIntArray", Value.fromBigIntArray(value)); + } +} + export class ExampleEntity extends Entity { constructor(id: string) { super(); @@ -24,6 +88,7 @@ export class ExampleEntity extends Entity { this.set("paramBytes", Value.fromBytes(Bytes.empty())); this.set("paramEnum", Value.fromString("")); this.set("paramBigDecimal", Value.fromBigDecimal(BigDecimal.zero())); + this.set("related", Value.fromString("")); } save(): void { @@ -114,4 +179,13 @@ export class ExampleEntity extends Entity { set paramBigDecimal(value: BigDecimal) { this.set("paramBigDecimal", Value.fromBigDecimal(value)); } + + get related(): string { + let value = this.get("related"); + return value!.toString(); + } + + set related(value: string) { + this.set("related", Value.fromString(value)); + } } diff --git a/packages/graph-node/test/subgraph/example1/schema.graphql b/packages/graph-node/test/subgraph/example1/schema.graphql index 2b3a9b17..dd3a30a8 100644 --- a/packages/graph-node/test/subgraph/example1/schema.graphql +++ b/packages/graph-node/test/subgraph/example1/schema.graphql @@ -3,6 +3,13 @@ enum EnumType { choice2 } +type RelatedEntity @entity { + id: ID! + paramBigInt: BigInt! + examples: [ExampleEntity!]! + bigIntArray: [BigInt!]! +} + type ExampleEntity @entity { id: ID! count: BigInt! @@ -12,4 +19,5 @@ type ExampleEntity @entity { paramBytes: Bytes! paramEnum: EnumType! paramBigDecimal: BigDecimal! + related: RelatedEntity! } diff --git a/packages/graph-node/test/subgraph/example1/src/mapping.ts b/packages/graph-node/test/subgraph/example1/src/mapping.ts index da503e17..64a96776 100644 --- a/packages/graph-node/test/subgraph/example1/src/mapping.ts +++ b/packages/graph-node/test/subgraph/example1/src/mapping.ts @@ -4,7 +4,7 @@ import { Example1, Test } from '../generated/Example1/Example1'; -import { ExampleEntity } from '../generated/schema'; +import { ExampleEntity, RelatedEntity } from '../generated/schema'; export function handleTest (event: Test): void { log.debug('event.address: {}', [event.address.toHexString()]); @@ -15,12 +15,12 @@ export function handleTest (event: Test): void { // Entities can be loaded from the store using a string ID; this ID // needs to be unique across all entities of the same type - let entity = ExampleEntity.load(event.transaction.from.toHex()); + let entity = ExampleEntity.load(event.transaction.hash.toHexString()); // Entities only exist after they have been saved to the store; // `null` checks allow to create entities on demand if (!entity) { - entity = new ExampleEntity(event.transaction.from.toHex()); + entity = new ExampleEntity(event.transaction.hash.toHexString()); // Entity fields can be set using simple assignments entity.count = BigInt.fromString('0'); @@ -37,6 +37,25 @@ export function handleTest (event: Test): void { entity.paramEnum = 'choice1'; entity.paramBigDecimal = BigDecimal.fromString('123'); + let relatedEntity = RelatedEntity.load(event.transaction.from.toHex()); + + if (!relatedEntity) { + relatedEntity = new RelatedEntity(event.transaction.from.toHex()); + relatedEntity.paramBigInt = BigInt.fromString('123'); + } + + const bigIntArray = relatedEntity.bigIntArray; + bigIntArray.push(entity.count); + relatedEntity.bigIntArray = bigIntArray; + + const examples = relatedEntity.examples; + examples.push(entity.id); + relatedEntity.examples = examples; + + relatedEntity.save(); + + entity.related = relatedEntity.id; + // Entities can be written to the store with `.save()` entity.save(); diff --git a/packages/graph-test-watcher/environments/local.toml b/packages/graph-test-watcher/environments/local.toml index 276befa0..42d3a600 100644 --- a/packages/graph-test-watcher/environments/local.toml +++ b/packages/graph-test-watcher/environments/local.toml @@ -11,7 +11,7 @@ # IPFS API address (can be taken from the output on running the IPFS daemon). ipfsApiAddr = "/ip4/127.0.0.1/tcp/5001" - + subgraphPath = "../graph-node/test/subgraph/example1/build" [database] diff --git a/packages/graph-test-watcher/src/entity/ExampleEntity.ts b/packages/graph-test-watcher/src/entity/ExampleEntity.ts index 9e21c09d..4e873cc9 100644 --- a/packages/graph-test-watcher/src/entity/ExampleEntity.ts +++ b/packages/graph-test-watcher/src/entity/ExampleEntity.ts @@ -47,4 +47,7 @@ export class ExampleEntity { @Column('numeric', { default: 0, transformer: decimalTransformer }) paramBigDecimal!: Decimal + + @Column('varchar') + related!: string; } diff --git a/packages/graph-test-watcher/src/entity/RelatedEntity.ts b/packages/graph-test-watcher/src/entity/RelatedEntity.ts new file mode 100644 index 00000000..65511e02 --- /dev/null +++ b/packages/graph-test-watcher/src/entity/RelatedEntity.ts @@ -0,0 +1,28 @@ +// +// Copyright 2021 Vulcanize, Inc. +// + +import { Entity, PrimaryColumn, Column } from 'typeorm'; + +import { bigintTransformer, bigintArrayTransformer } from '@vulcanize/util'; + +@Entity() +export class RelatedEntity { + @PrimaryColumn('varchar') + id!: string; + + @PrimaryColumn('varchar', { length: 66 }) + blockHash!: string; + + @Column('integer') + blockNumber!: number; + + @Column('bigint', { transformer: bigintTransformer }) + paramBigInt!: bigint; + + @Column('varchar', { array: true }) + examples!: string[]; + + @Column('bigint', { transformer: bigintArrayTransformer, array: true }) + bigIntArray!: bigint[]; +} diff --git a/packages/graph-test-watcher/src/schema.gql b/packages/graph-test-watcher/src/schema.gql index 3f4a5d9d..ece2fa65 100644 --- a/packages/graph-test-watcher/src/schema.gql +++ b/packages/graph-test-watcher/src/schema.gql @@ -77,11 +77,21 @@ type Query { getState(blockHash: String!, contractAddress: String!, kind: String): ResultIPLDBlock } +enum EnumType { + choice1 + choice2 +} + type ExampleEntity { id: ID! count: BigInt! - param1: String! - param2: Int! + paramString: String! + paramInt: Int! + paramBoolean: Boolean! + paramBytes: Bytes! + paramEnum: EnumType! + paramBigDecimal: BigDecimal! + related: String! } type Mutation { diff --git a/packages/util/src/misc.ts b/packages/util/src/misc.ts index 57ff8b48..94754c94 100644 --- a/packages/util/src/misc.ts +++ b/packages/util/src/misc.ts @@ -80,6 +80,40 @@ export const bigintTransformer: ValueTransformer = { } }; +export const bigintArrayTransformer: ValueTransformer = { + to: (valueArray?: bigint[]) => { + if (valueArray) { + return valueArray.map(value => bigintTransformer.to(value)); + } + + return valueArray; + }, + from: (valueArray?: string[]) => { + if (valueArray) { + return valueArray.map(value => bigintTransformer.from(value)); + } + + return valueArray; + } +}; + +export const decimalArrayTransformer: ValueTransformer = { + to: (valueArray?: Decimal[]) => { + if (valueArray) { + return valueArray.map(value => decimalTransformer.to(value)); + } + + return valueArray; + }, + from: (valueArray?: string[]) => { + if (valueArray) { + return valueArray.map(value => decimalTransformer.from(value)); + } + + return valueArray; + } +}; + export const resetJobs = async (config: Config): Promise => { const { jobQueue: jobQueueConfig } = config;