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
This commit is contained in:
nikugogoi 2021-11-17 17:01:09 +05:30 committed by nabarun
parent 158c3928c9
commit 800ad79baf
25 changed files with 527 additions and 221 deletions

View File

@ -48,7 +48,8 @@
"multiformats": "^9.4.8", "multiformats": "^9.4.8",
"reflect-metadata": "^0.1.13", "reflect-metadata": "^0.1.13",
"typeorm": "^0.2.32", "typeorm": "^0.2.32",
"yargs": "^17.0.1" "yargs": "^17.0.1",
"decimal.js": "^10.3.1"
}, },
"devDependencies": { "devDependencies": {
"@ethersproject/abi": "^5.3.0", "@ethersproject/abi": "^5.3.0",

View File

@ -2,8 +2,8 @@
// Copyright 2021 Vulcanize, Inc. // Copyright 2021 Vulcanize, Inc.
// //
import { Entity, PrimaryColumn, Column, ManyToOne } from 'typeorm'; import { Entity, PrimaryColumn, Column } from 'typeorm';
import { Account } from './Account';
import { bigintTransformer } from '@vulcanize/util'; import { bigintTransformer } from '@vulcanize/util';
@Entity() @Entity()
@ -23,8 +23,8 @@ export class Claim {
@Column('bigint', { transformer: bigintTransformer }) @Column('bigint', { transformer: bigintTransformer })
index!: bigint; index!: bigint;
@ManyToOne(() => Account) @Column('varchar')
account!: Account; account!: string;
@Column('bigint', { transformer: bigintTransformer }) @Column('bigint', { transformer: bigintTransformer })
totalEarned!: bigint; totalEarned!: bigint;

View File

@ -2,8 +2,8 @@
// Copyright 2021 Vulcanize, Inc. // Copyright 2021 Vulcanize, Inc.
// //
import { Entity, PrimaryColumn, Column, ManyToOne } from 'typeorm'; import { Entity, PrimaryColumn, Column } from 'typeorm';
import { Distributor } from './Distributor';
import { bigintTransformer } from '@vulcanize/util'; import { bigintTransformer } from '@vulcanize/util';
@Entity() @Entity()
@ -17,8 +17,8 @@ export class Distribution {
@Column('integer') @Column('integer')
blockNumber!: number; blockNumber!: number;
@ManyToOne(() => Distributor) @Column('varchar')
distributor!: Distributor; distributor!: string;
@Column('bigint', { transformer: bigintTransformer }) @Column('bigint', { transformer: bigintTransformer })
timestamp!: bigint; timestamp!: bigint;

View File

@ -2,8 +2,7 @@
// Copyright 2021 Vulcanize, Inc. // Copyright 2021 Vulcanize, Inc.
// //
import { Entity, PrimaryColumn, Column, ManyToOne } from 'typeorm'; import { Entity, PrimaryColumn, Column } from 'typeorm';
import { Distribution } from './Distribution';
@Entity() @Entity()
export class Distributor { export class Distributor {
@ -16,6 +15,6 @@ export class Distributor {
@Column('integer') @Column('integer')
blockNumber!: number; blockNumber!: number;
@ManyToOne(() => Distribution, { nullable: true }) @Column('varchar', { nullable: true })
currentDistribution!: Distribution; currentDistribution!: string;
} }

View File

@ -3,9 +3,10 @@
// //
import { Entity, PrimaryColumn, Column, ManyToOne } from 'typeorm'; import { Entity, PrimaryColumn, Column, ManyToOne } from 'typeorm';
import { Block } from './Block'; import Decimal from 'decimal.js';
import { ProducerEpoch } from './ProducerEpoch'; import { ProducerEpoch } from './ProducerEpoch';
import { bigintTransformer } from '@vulcanize/util'; import { bigintTransformer, decimalTransformer } from '@vulcanize/util';
@Entity() @Entity()
export class Epoch { export class Epoch {
@ -24,11 +25,11 @@ export class Epoch {
@Column('bigint', { transformer: bigintTransformer }) @Column('bigint', { transformer: bigintTransformer })
epochNumber!: bigint; epochNumber!: bigint;
@ManyToOne(() => Block, { nullable: true }) @Column('varchar', { nullable: true })
startBlock!: Block; startBlock!: string;
@ManyToOne(() => Block, { nullable: true }) @Column('varchar', { nullable: true })
endBlock!: Block; endBlock!: string;
@Column('bigint', { transformer: bigintTransformer }) @Column('bigint', { transformer: bigintTransformer })
producerBlocks!: bigint; producerBlocks!: bigint;
@ -36,8 +37,8 @@ export class Epoch {
@Column('bigint', { transformer: bigintTransformer }) @Column('bigint', { transformer: bigintTransformer })
allBlocks!: bigint; allBlocks!: bigint;
@Column('varchar') @Column('numeric', { default: 0, transformer: decimalTransformer })
producerBlocksRatio!: string; producerBlocksRatio!: Decimal;
@ManyToOne(() => ProducerEpoch) @ManyToOne(() => ProducerEpoch)
producerRewards!: ProducerEpoch; producerRewards!: ProducerEpoch;

View File

@ -2,10 +2,9 @@
// Copyright 2021 Vulcanize, Inc. // Copyright 2021 Vulcanize, Inc.
// //
import { Entity, PrimaryColumn, Column, ManyToOne } from 'typeorm'; import { Entity, PrimaryColumn, Column } from 'typeorm';
import { Slot } from './Slot';
import { Staker } from './Staker'; import { bigintArrayTransformer, bigintTransformer } from '@vulcanize/util';
import { bigintTransformer } from '@vulcanize/util';
@Entity() @Entity()
export class Network { export class Network {
@ -18,17 +17,17 @@ export class Network {
@Column('integer') @Column('integer')
blockNumber!: number; blockNumber!: number;
@ManyToOne(() => Slot, { nullable: true }) @Column('varchar', { nullable: true })
slot0!: Slot; slot0!: string;
@ManyToOne(() => Slot, { nullable: true }) @Column('varchar', { nullable: true })
slot1!: Slot; slot1!: string;
@ManyToOne(() => Slot, { nullable: true }) @Column('varchar', { nullable: true })
slot2!: Slot; slot2!: string;
@ManyToOne(() => Staker) @Column('varchar', { array: true })
stakers!: Staker; stakers!: string[];
@Column('bigint', { nullable: true, transformer: bigintTransformer }) @Column('bigint', { nullable: true, transformer: bigintTransformer })
numStakers!: bigint; numStakers!: bigint;
@ -36,6 +35,6 @@ export class Network {
@Column('bigint', { transformer: bigintTransformer }) @Column('bigint', { transformer: bigintTransformer })
totalStaked!: bigint; totalStaked!: bigint;
@Column('bigint', { array: true }) @Column('bigint', { transformer: bigintArrayTransformer, array: true })
stakedPercentiles!: bigint[]; stakedPercentiles!: bigint[];
} }

View File

@ -2,9 +2,10 @@
// Copyright 2021 Vulcanize, Inc. // Copyright 2021 Vulcanize, Inc.
// //
import { Entity, PrimaryColumn, Column, ManyToOne } from 'typeorm'; import { Entity, PrimaryColumn, Column } from 'typeorm';
import { Epoch } from './Epoch'; import Decimal from 'decimal.js';
import { bigintTransformer } from '@vulcanize/util';
import { bigintTransformer, decimalTransformer } from '@vulcanize/util';
@Entity() @Entity()
export class ProducerEpoch { export class ProducerEpoch {
@ -20,8 +21,8 @@ export class ProducerEpoch {
@Column('varchar') @Column('varchar')
address!: string; address!: string;
@ManyToOne(() => Epoch) @Column('varchar')
epoch!: Epoch; epoch!: string;
@Column('bigint', { transformer: bigintTransformer }) @Column('bigint', { transformer: bigintTransformer })
totalRewards!: bigint; totalRewards!: bigint;
@ -29,6 +30,6 @@ export class ProducerEpoch {
@Column('bigint', { transformer: bigintTransformer }) @Column('bigint', { transformer: bigintTransformer })
blocksProduced!: bigint; blocksProduced!: bigint;
@Column('varchar') @Column('numeric', { default: 0, transformer: decimalTransformer })
blocksProducedRatio!: string; blocksProducedRatio!: Decimal;
} }

View File

@ -2,8 +2,7 @@
// Copyright 2021 Vulcanize, Inc. // Copyright 2021 Vulcanize, Inc.
// //
import { Entity, PrimaryColumn, Column, ManyToOne } from 'typeorm'; import { Entity, PrimaryColumn, Column } from 'typeorm';
import { Producer } from './Producer';
@Entity() @Entity()
export class ProducerSet { export class ProducerSet {
@ -16,6 +15,6 @@ export class ProducerSet {
@Column('integer') @Column('integer')
blockNumber!: number; blockNumber!: number;
@ManyToOne(() => Producer) @Column('varchar', { array: true })
producers!: Producer; producers!: string[];
} }

View File

@ -27,6 +27,9 @@ export class ProducerSetChange {
@Column('varchar') @Column('varchar')
producer!: string; producer!: string;
@Column('integer') @Column({
type: 'enum',
enum: ProducerSetChangeType
})
changeType!: ProducerSetChangeType; changeType!: ProducerSetChangeType;
} }

View File

@ -2,9 +2,7 @@
// Copyright 2021 Vulcanize, Inc. // Copyright 2021 Vulcanize, Inc.
// //
import { Entity, PrimaryColumn, Column, ManyToOne } from 'typeorm'; import { Entity, PrimaryColumn, Column } from 'typeorm';
import { RewardScheduleEntry } from './RewardScheduleEntry';
import { Epoch } from './Epoch';
@Entity() @Entity()
export class RewardSchedule { export class RewardSchedule {
@ -17,15 +15,15 @@ export class RewardSchedule {
@Column('integer') @Column('integer')
blockNumber!: number; blockNumber!: number;
@ManyToOne(() => RewardScheduleEntry) @Column('varchar', { array: true })
rewardScheduleEntries!: RewardScheduleEntry; rewardScheduleEntries!: string[];
@ManyToOne(() => Epoch, { nullable: true }) @Column('varchar', { nullable: true })
lastEpoch!: Epoch; lastEpoch!: string;
@ManyToOne(() => Epoch, { nullable: true }) @Column('varchar', { nullable: true })
pendingEpoch!: Epoch; pendingEpoch!: string;
@ManyToOne(() => RewardScheduleEntry, { nullable: true }) @Column('varchar', { nullable: true })
activeRewardScheduleEntry!: RewardScheduleEntry; activeRewardScheduleEntry!: string;
} }

View File

@ -2,8 +2,8 @@
// Copyright 2021 Vulcanize, Inc. // Copyright 2021 Vulcanize, Inc.
// //
import { Entity, PrimaryColumn, Column, ManyToOne } from 'typeorm'; import { Entity, PrimaryColumn, Column } from 'typeorm';
import { Account } from './Account';
import { bigintTransformer } from '@vulcanize/util'; import { bigintTransformer } from '@vulcanize/util';
@Entity() @Entity()
@ -20,8 +20,8 @@ export class Slash {
@Column('bigint', { transformer: bigintTransformer }) @Column('bigint', { transformer: bigintTransformer })
timestamp!: bigint; timestamp!: bigint;
@ManyToOne(() => Account) @Column('varchar')
account!: Account; account!: string;
@Column('bigint', { transformer: bigintTransformer }) @Column('bigint', { transformer: bigintTransformer })
slashed!: bigint; slashed!: bigint;

View File

@ -3,8 +3,11 @@
// //
import { Entity, PrimaryColumn, Column, ManyToOne } from 'typeorm'; import { Entity, PrimaryColumn, Column, ManyToOne } from 'typeorm';
import Decimal from 'decimal.js';
import { bigintTransformer, decimalTransformer } from '@vulcanize/util';
import { SlotClaim } from './SlotClaim'; import { SlotClaim } from './SlotClaim';
import { bigintTransformer } from '@vulcanize/util';
@Entity() @Entity()
export class Slot { export class Slot {
@ -35,8 +38,8 @@ export class Slot {
@Column('bigint', { transformer: bigintTransformer }) @Column('bigint', { transformer: bigintTransformer })
expirationTime!: bigint; expirationTime!: bigint;
@Column('varchar') @Column('numeric', { default: 0, transformer: decimalTransformer })
taxRatePerDay!: string; taxRatePerDay!: Decimal;
@ManyToOne(() => SlotClaim) @ManyToOne(() => SlotClaim)
claims!: SlotClaim; claims!: SlotClaim;

View File

@ -2,9 +2,10 @@
// Copyright 2021 Vulcanize, Inc. // Copyright 2021 Vulcanize, Inc.
// //
import { Entity, PrimaryColumn, Column, ManyToOne } from 'typeorm'; import { Entity, PrimaryColumn, Column } from 'typeorm';
import { Slot } from './Slot'; import Decimal from 'decimal.js';
import { bigintTransformer } from '@vulcanize/util';
import { bigintTransformer, decimalTransformer } from '@vulcanize/util';
@Entity() @Entity()
export class SlotClaim { export class SlotClaim {
@ -17,8 +18,8 @@ export class SlotClaim {
@Column('integer') @Column('integer')
blockNumber!: number; blockNumber!: number;
@ManyToOne(() => Slot) @Column('varchar')
slot!: Slot; slot!: string;
@Column('varchar') @Column('varchar')
owner!: string; owner!: string;
@ -35,6 +36,6 @@ export class SlotClaim {
@Column('bigint', { transformer: bigintTransformer }) @Column('bigint', { transformer: bigintTransformer })
expirationTime!: bigint; expirationTime!: bigint;
@Column('varchar') @Column('numeric', { default: 0, transformer: decimalTransformer })
taxRatePerDay!: string; taxRatePerDay!: Decimal;
} }

View File

@ -1,5 +1,9 @@
scalar BigInt scalar BigInt
scalar Bytes
scalar BigDecimal
type Proof { type Proof {
data: String! data: String!
} }
@ -239,7 +243,7 @@ type Query {
type Producer { type Producer {
id: ID! id: ID!
active: Boolean! active: Boolean!
rewardCollector: String rewardCollector: Bytes
rewards: BigInt! rewards: BigInt!
confirmedBlocks: BigInt! confirmedBlocks: BigInt!
pendingEpochBlocks: BigInt! pendingEpochBlocks: BigInt!
@ -253,7 +257,7 @@ type ProducerSet {
type ProducerSetChange { type ProducerSetChange {
id: ID! id: ID!
blockNumber: BigInt! blockNumber: BigInt!
producer: String! producer: Bytes!
changeType: ProducerSetChangeType! changeType: ProducerSetChangeType!
} }
@ -265,8 +269,8 @@ enum ProducerSetChangeType {
type ProducerRewardCollectorChange { type ProducerRewardCollectorChange {
id: ID! id: ID!
blockNumber: BigInt! blockNumber: BigInt!
producer: String! producer: Bytes!
rewardCollector: String! rewardCollector: Bytes!
} }
type RewardScheduleEntry { type RewardScheduleEntry {
@ -311,39 +315,39 @@ type Epoch {
endBlock: Block endBlock: Block
producerBlocks: BigInt! producerBlocks: BigInt!
allBlocks: BigInt! allBlocks: BigInt!
producerBlocksRatio: String! producerBlocksRatio: BigDecimal!
producerRewards: [ProducerEpoch!]! producerRewards: [ProducerEpoch!]!
} }
type ProducerEpoch { type ProducerEpoch {
id: ID! id: ID!
address: String! address: Bytes!
epoch: Epoch! epoch: Epoch!
totalRewards: BigInt! totalRewards: BigInt!
blocksProduced: BigInt! blocksProduced: BigInt!
blocksProducedRatio: String! blocksProducedRatio: BigDecimal!
} }
type SlotClaim { type SlotClaim {
id: ID! id: ID!
slot: Slot! slot: Slot!
owner: String! owner: Bytes!
winningBid: BigInt! winningBid: BigInt!
oldBid: BigInt! oldBid: BigInt!
startTime: BigInt! startTime: BigInt!
expirationTime: BigInt! expirationTime: BigInt!
taxRatePerDay: String! taxRatePerDay: BigDecimal!
} }
type Slot { type Slot {
id: ID! id: ID!
owner: String! owner: Bytes!
delegate: String! delegate: Bytes!
winningBid: BigInt! winningBid: BigInt!
oldBid: BigInt! oldBid: BigInt!
startTime: BigInt! startTime: BigInt!
expirationTime: BigInt! expirationTime: BigInt!
taxRatePerDay: String! taxRatePerDay: BigDecimal!
claims: [SlotClaim!]! claims: [SlotClaim!]!
} }
@ -374,7 +378,7 @@ type Distribution {
distributor: Distributor! distributor: Distributor!
timestamp: BigInt! timestamp: BigInt!
distributionNumber: BigInt! distributionNumber: BigInt!
merkleRoot: String! merkleRoot: Bytes!
metadataURI: String! metadataURI: String!
} }

View File

@ -90,14 +90,14 @@ export class Database {
return true; return true;
}).map(async (field) => { }).map(async (field) => {
const { type, propertyName } = field;
// Fill _blockNumber as blockNumber and _blockHash as blockHash in the entityInstance (wasm). // Fill _blockNumber as blockNumber and _blockHash as blockHash in the entityInstance (wasm).
if (['_blockNumber', '_blockHash'].includes(propertyName)) { if (['_blockNumber', '_blockHash'].includes(field.propertyName)) {
return toEntityValue(instanceExports, entityInstance, data, type.toString(), propertyName.slice(1)); 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); 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 } > { async getEntityValues (instanceExports: any, block: Block, entityInstance: any, entityFields: any): Promise<{ [key: string]: any } > {
const entityValuePromises = entityFields.map(async (field: any) => { const entityValuePromises = entityFields.map(async (field: any) => {
const { type, propertyName } = field; const { propertyName } = field;
// Get blockHash property for db entry from block instance. // Get blockHash property for db entry from block instance.
if (propertyName === 'blockHash') { if (propertyName === 'blockHash') {
@ -129,10 +129,10 @@ export class Database {
// Get blockNumber as _blockNumber and blockHash as _blockHash from the entityInstance (wasm). // Get blockNumber as _blockNumber and blockHash as _blockHash from the entityInstance (wasm).
if (['_blockNumber', '_blockHash'].includes(propertyName)) { 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); const entityValues = await Promise.all(entityValuePromises);

View File

@ -105,17 +105,27 @@ export const instantiate = async (database: Database, indexer: IndexerInterface,
'ethereum.call': async (call: number) => { 'ethereum.call': async (call: number) => {
const smartContractCall = await ethereum.SmartContractCall.wrap(call); const smartContractCall = await ethereum.SmartContractCall.wrap(call);
const contractAddress = await Address.wrap(await smartContractCall.contractAddress); const contractAddressPtr = await smartContractCall.contractAddress;
const contractName = __getString(await smartContractCall.contractName); const contractAddress = await Address.wrap(contractAddressPtr);
const functionName = __getString(await smartContractCall.functionName);
const functionSignature = __getString(await smartContractCall.functionSignature); const contractNamePtr = await smartContractCall.contractName;
let functionParams = __getArray(await smartContractCall.functionParams); 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('ethereum.call params');
console.log('functionSignature:', functionSignature); console.log('functionSignature:', functionSignature);
const abi = abis[contractName]; 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 { try {
const functionParamsPromise = functionParams.map(async param => { 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 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; return res;
} catch (err) { } catch (err) {
@ -194,10 +205,12 @@ export const instantiate = async (database: Database, indexer: IndexerInterface,
const bigDecimaly = BigDecimal.wrap(y); 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 yDigits = BigNumber.from(yDigitsBigIntArray);
const yExpBigIntArray = __getArray(await bigDecimaly.exp); const expPtr = await bigDecimaly.exp;
const yExpBigIntArray = __getArray(expPtr);
const yExp = BigNumber.from(yExpBigIntArray); const yExp = BigNumber.from(yExpBigIntArray);
console.log('y digits and exp', yDigits, yExp); 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) => { 'bigDecimal.toString': async (bigDecimal: number) => {
const bigDecimalInstance = BigDecimal.wrap(bigDecimal); const bigDecimalInstance = BigDecimal.wrap(bigDecimal);
const digitsBigInt = BigInt.wrap(await bigDecimalInstance.digits); const digitsPtr = await bigDecimalInstance.digits;
const expBigInt = BigInt.wrap(await bigDecimalInstance.exp); const digitsBigInt = BigInt.wrap(digitsPtr);
const digits = __getString(await digitsBigInt.toString()); const expPtr = await bigDecimalInstance.exp;
const exp = __getString(await expBigInt.toString()); 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 decimal = new Decimal(`${digits}e${exp}`);
const ptr = __newString(decimal.toFixed()); const ptr = __newString(decimal.toFixed());
@ -224,11 +243,13 @@ export const instantiate = async (database: Database, indexer: IndexerInterface,
const digits = decimal.d.join(''); const digits = decimal.d.join('');
const digitsBigNumber = BigNumber.from(digits); const digitsBigNumber = BigNumber.from(digits);
const signBigNumber = BigNumber.from(decimal.s); 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. // Calculate exp after converting digits to BigInt above.
const exp = decimal.e - digits.length + 1; 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); const bigDecimal = await BigDecimal.__new(digitsBigInt);
bigDecimal.exp = expBigInt; bigDecimal.exp = expBigInt;
@ -259,10 +280,12 @@ export const instantiate = async (database: Database, indexer: IndexerInterface,
}, },
'bigInt.plus': async (x: number, y: number) => { 'bigInt.plus': async (x: number, y: number) => {
const xBigInt = await BigInt.wrap(x); 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 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 sum = xBigNumber.add(yBigNumber);
const ptr = await __newString(sum.toString()); 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) => { 'bigInt.minus': async (x: number, y: number) => {
const xBigInt = await BigInt.wrap(x); 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 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 diff = xBigNumber.sub(yBigNumber);
const ptr = await __newString(diff.toString()); 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) => { 'bigInt.times': async (x: number, y: number) => {
const xBigInt = await BigInt.wrap(x); 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 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 product = xBigNumber.mul(yBigNumber);
const ptr = await __newString(product.toString()); 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) => { 'bigInt.dividedBy': async (x: number, y: number) => {
const xBigInt = await BigInt.wrap(x); 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 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 quotient = xBigNumber.div(yBigNumber);
const ptr = await __newString(quotient.toString()); const ptr = await __newString(quotient.toString());
@ -334,7 +363,8 @@ export const instantiate = async (database: Database, indexer: IndexerInterface,
datasource: { datasource: {
'dataSource.address': async () => { 'dataSource.address': async () => {
assert(dataSource); assert(dataSource);
return Address.fromString(await __newString(dataSource.address)); const addressStringPtr = await __newString(dataSource.address);
return Address.fromString(addressStringPtr);
} }
} }
}; };

View File

@ -3,9 +3,11 @@ import path from 'path';
import fs from 'fs-extra'; import fs from 'fs-extra';
import debug from 'debug'; import debug from 'debug';
import yaml from 'js-yaml'; 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 { TypeId, EthereumValueKind, ValueKind } from './types';
import Decimal from 'decimal.js';
const log = debug('vulcanize:utils'); const log = debug('vulcanize:utils');
@ -57,7 +59,8 @@ export const fromEthereumValue = async (instanceExports: any, value: any): Promi
switch (kind) { switch (kind) {
case EthereumValueKind.ADDRESS: { 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(); const addressStringPtr = await address.toHexString();
return __getString(addressStringPtr); return __getString(addressStringPtr);
} }
@ -76,7 +79,8 @@ export const fromEthereumValue = async (instanceExports: any, value: any): Promi
case EthereumValueKind.INT: case EthereumValueKind.INT:
case EthereumValueKind.UINT: { 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 bigIntStringPtr = await bigInt.toString();
const bigIntString = __getString(bigIntStringPtr); const bigIntString = __getString(bigIntStringPtr);
return BigNumber.from(bigIntString); 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. // For uint/int type or enum type.
if (isIntegerOrEnum) { if (isIntegerOrEnum) {
const valueString = await __newString(value.toString()); const valueStringPtr = await __newString(value.toString());
const bigInt = await BigInt.fromString(valueString); const bigInt = await BigInt.fromString(valueStringPtr);
let ethereumValue = await ethereum.Value.fromUnsignedBigInt(bigInt); let ethereumValue = await ethereum.Value.fromUnsignedBigInt(bigInt);
if (Boolean(isInteger) && !isUnsigned) { if (Boolean(isInteger) && !isUnsigned) {
@ -125,18 +129,23 @@ export const toEthereumValue = async (instanceExports: any, value: any, type: st
} }
if (type.startsWith('address')) { 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. // TODO: Check between fixed bytes and dynamic bytes.
if (type.startsWith('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); const bytes = await Bytes.fromByteArray(byteArray);
return ethereum.Value.fromBytes(bytes); return ethereum.Value.fromBytes(bytes);
} }
// For string type. // 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); const block = await createBlock(instanceExports, blockData);
// Fill transaction data. // 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 txHash = await Bytes.fromByteArray(txHashByteArray);
const txIndex = await BigInt.fromI32(tx.index); 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: // Missing fields from watcher in transaction data:
// value // value
@ -187,33 +204,39 @@ export const createEvent = async (instanceExports: any, contractAddress: string,
txIndex, txIndex,
txFrom, txFrom,
txTo, txTo,
await BigInt.fromI32(0), txValuePtr,
await BigInt.fromI32(0), txGasLimitPtr,
await BigInt.fromI32(0), txGasPricePtr,
await Bytes.empty() txinputPtr
); );
const eventParamArrayPromise = eventParamsData.map(async data => { const eventParamArrayPromise = eventParamsData.map(async data => {
const { name, value, kind } = data; const { name, value, kind } = data;
const ethValue = await toEthereumValue(instanceExports, value, kind); const ethValue = await toEthereumValue(instanceExports, value, kind);
const namePtr = await __newString(name);
return ethereum.EventParam.__new( return ethereum.EventParam.__new(
await __newString(name), namePtr,
ethValue ethValue
); );
}); });
const eventParamArray = await Promise.all(eventParamArrayPromise); 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 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. // Create event to be passed to handler.
return ethereum.Event.__new( return ethereum.Event.__new(
await Address.fromString(addStrPtr), eventAddressPtr,
await BigInt.fromI32(eventIndex), eventIndexPtr,
await BigInt.fromI32(0), transactionLogIndexPtr,
null, null,
block, block,
transaction, transaction,
@ -232,26 +255,40 @@ export const createBlock = async (instanceExports: any, blockData: Block): Promi
} = instanceExports; } = instanceExports;
// Fill block data. // 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 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 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 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 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 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: // Missing fields from watcher in block data:
// unclesHash // unclesHash
@ -263,16 +300,16 @@ export const createBlock = async (instanceExports: any, blockData: Block): Promi
return await ethereum.Block.__new( return await ethereum.Block.__new(
blockHash, blockHash,
parentHash, parentHash,
await Bytes.empty(), unclesHashPtr,
await Address.zero(), authorPtr,
stateRoot, stateRoot,
transactionsRoot, transactionsRoot,
receiptsRoot, receiptsRoot,
blockNumber, blockNumber,
await BigInt.fromI32(0), gasUsedPtr,
await BigInt.fromI32(0), gasLimitPtr,
blockTimestamp, blockTimestamp,
await BigInt.fromI32(0), difficultyPtr,
totalDifficulty, totalDifficulty,
null null
); );
@ -286,109 +323,163 @@ export const getSubgraphConfig = async (subgraphPath: string): Promise<any> => {
throw new Error(`Config file not found: ${configFilePath}`); 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)); log('config', JSON.stringify(config, null, 2));
return config; return config;
}; };
export const toEntityValue = async (instanceExports: any, entityInstance: any, data: any, type: string, key: string) => { export const toEntityValue = async (instanceExports: any, entityInstance: any, data: any, field: ColumnMetadata) => {
const { __newString, BigInt: ExportBigInt, Value, ByteArray, Bytes, BigDecimal } = instanceExports; 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<any> => {
const { __newString } = instanceExports;
const entityKey = await __newString(key); 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<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);
const valueArray = await __newArray(arrayStoreValueId, dataArray);
return Value.fromArray(valueArray);
}
switch (type) { switch (type) {
case 'varchar': { case 'varchar': {
const entityValue = await __newString(value); const entityValue = await __newString(value);
const kind = await subgraphValue.kind;
const graphValue = Value.wrap(await entityInstance.get(entityKey));
const kind = await graphValue.kind;
switch (kind) { switch (kind) {
case ValueKind.BYTES: { case ValueKind.BYTES: {
const byteArray = await ByteArray.fromHexString(entityValue); const byteArray = await ByteArray.fromHexString(entityValue);
const bytes = await Bytes.fromByteArray(byteArray); const bytes = await Bytes.fromByteArray(byteArray);
return entityInstance.setBytes(entityKey, bytes);
return Value.fromBytes(bytes);
} }
default: default:
return entityInstance.setString(entityKey, entityValue); return Value.fromString(entityValue);
} }
} }
case 'integer': { case 'integer': {
return entityInstance.setI32(entityKey, value); return Value.fromI32(value);
} }
case 'bigint': { 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': { case 'boolean': {
return entityInstance.setBoolean(entityKey, value ? 1 : 0); return Value.fromBoolean(value ? 1 : 0);
} }
case 'enum': { case 'enum': {
const entityValue = await __newString(value); const entityValue = await __newString(value);
return entityInstance.setString(entityKey, entityValue);
return Value.fromString(entityValue);
} }
case 'numeric': { case 'numeric': {
const bigDecimal = await BigDecimal.fromString(await __newString(value.toString())); const valueStringPtr = await __newString(value.toString());
return entityInstance.setBigDecimal(entityKey, bigDecimal); const bigDecimal = await BigDecimal.fromString(valueStringPtr);
}
// TODO: Support more types. return Value.fromBigDecimal(bigDecimal);
default:
throw new Error(`Unsupported type: ${type}`);
}
};
export const fromEntityValue = async (instanceExports: any, entityInstance: any, type: string, key: string): Promise<any> => {
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()));
} }
// TODO: Support more types. // TODO: Support more types.

View File

@ -12,6 +12,70 @@ import {
BigDecimal BigDecimal
} from "@graphprotocol/graph-ts"; } 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<RelatedEntity | null>(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<string> {
let value = this.get("examples");
return value!.toStringArray();
}
set examples(value: Array<string>) {
this.set("examples", Value.fromStringArray(value));
}
get bigIntArray(): Array<BigInt> {
let value = this.get("bigIntArray");
return value!.toBigIntArray();
}
set bigIntArray(value: Array<BigInt>) {
this.set("bigIntArray", Value.fromBigIntArray(value));
}
}
export class ExampleEntity extends Entity { export class ExampleEntity extends Entity {
constructor(id: string) { constructor(id: string) {
super(); super();
@ -24,6 +88,7 @@ export class ExampleEntity extends Entity {
this.set("paramBytes", Value.fromBytes(Bytes.empty())); this.set("paramBytes", Value.fromBytes(Bytes.empty()));
this.set("paramEnum", Value.fromString("")); this.set("paramEnum", Value.fromString(""));
this.set("paramBigDecimal", Value.fromBigDecimal(BigDecimal.zero())); this.set("paramBigDecimal", Value.fromBigDecimal(BigDecimal.zero()));
this.set("related", Value.fromString(""));
} }
save(): void { save(): void {
@ -114,4 +179,13 @@ export class ExampleEntity extends Entity {
set paramBigDecimal(value: BigDecimal) { set paramBigDecimal(value: BigDecimal) {
this.set("paramBigDecimal", Value.fromBigDecimal(value)); 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));
}
} }

View File

@ -3,6 +3,13 @@ enum EnumType {
choice2 choice2
} }
type RelatedEntity @entity {
id: ID!
paramBigInt: BigInt!
examples: [ExampleEntity!]!
bigIntArray: [BigInt!]!
}
type ExampleEntity @entity { type ExampleEntity @entity {
id: ID! id: ID!
count: BigInt! count: BigInt!
@ -12,4 +19,5 @@ type ExampleEntity @entity {
paramBytes: Bytes! paramBytes: Bytes!
paramEnum: EnumType! paramEnum: EnumType!
paramBigDecimal: BigDecimal! paramBigDecimal: BigDecimal!
related: RelatedEntity!
} }

View File

@ -4,7 +4,7 @@ import {
Example1, Example1,
Test Test
} from '../generated/Example1/Example1'; } from '../generated/Example1/Example1';
import { ExampleEntity } from '../generated/schema'; import { ExampleEntity, RelatedEntity } from '../generated/schema';
export function handleTest (event: Test): void { export function handleTest (event: Test): void {
log.debug('event.address: {}', [event.address.toHexString()]); 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 // Entities can be loaded from the store using a string ID; this ID
// needs to be unique across all entities of the same type // 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; // Entities only exist after they have been saved to the store;
// `null` checks allow to create entities on demand // `null` checks allow to create entities on demand
if (!entity) { 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 fields can be set using simple assignments
entity.count = BigInt.fromString('0'); entity.count = BigInt.fromString('0');
@ -37,6 +37,25 @@ export function handleTest (event: Test): void {
entity.paramEnum = 'choice1'; entity.paramEnum = 'choice1';
entity.paramBigDecimal = BigDecimal.fromString('123'); 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()` // Entities can be written to the store with `.save()`
entity.save(); entity.save();

View File

@ -11,7 +11,7 @@
# IPFS API address (can be taken from the output on running the IPFS daemon). # IPFS API address (can be taken from the output on running the IPFS daemon).
ipfsApiAddr = "/ip4/127.0.0.1/tcp/5001" ipfsApiAddr = "/ip4/127.0.0.1/tcp/5001"
subgraphPath = "../graph-node/test/subgraph/example1/build" subgraphPath = "../graph-node/test/subgraph/example1/build"
[database] [database]

View File

@ -47,4 +47,7 @@ export class ExampleEntity {
@Column('numeric', { default: 0, transformer: decimalTransformer }) @Column('numeric', { default: 0, transformer: decimalTransformer })
paramBigDecimal!: Decimal paramBigDecimal!: Decimal
@Column('varchar')
related!: string;
} }

View File

@ -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[];
}

View File

@ -77,11 +77,21 @@ type Query {
getState(blockHash: String!, contractAddress: String!, kind: String): ResultIPLDBlock getState(blockHash: String!, contractAddress: String!, kind: String): ResultIPLDBlock
} }
enum EnumType {
choice1
choice2
}
type ExampleEntity { type ExampleEntity {
id: ID! id: ID!
count: BigInt! count: BigInt!
param1: String! paramString: String!
param2: Int! paramInt: Int!
paramBoolean: Boolean!
paramBytes: Bytes!
paramEnum: EnumType!
paramBigDecimal: BigDecimal!
related: String!
} }
type Mutation { type Mutation {

View File

@ -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<void> => { export const resetJobs = async (config: Config): Promise<void> => {
const { jobQueue: jobQueueConfig } = config; const { jobQueue: jobQueueConfig } = config;