Browse Source

Block as relation

Leszek Wiesner 3 years ago
parent
commit
d30054b61c

+ 36 - 4
query-node/mappings/common.ts

@@ -1,16 +1,28 @@
 import { SubstrateEvent } from '@dzlzv/hydra-common'
-import { EventType } from 'query-node/dist/src/modules/enums/enums'
+import { EventType, Network } from 'query-node/dist/src/modules/enums/enums'
 import { Event } from 'query-node/dist/src/modules/event/event.model'
 import { Bytes } from '@polkadot/types'
+import { Block } from 'query-node/dist/model'
+import { DatabaseManager } from '@dzlzv/hydra-db-utils'
 
-export function createEvent({ blockNumber, extrinsic, index }: SubstrateEvent, type: EventType): Event {
-  return new Event({
+export const CURRENT_NETWORK = Network.OLYMPIA
+
+export async function createEvent(
+  db: DatabaseManager,
+  substrateEvent: SubstrateEvent,
+  type: EventType
+): Promise<Event> {
+  const { blockNumber, index, extrinsic } = substrateEvent
+  const event = new Event({
     id: `${blockNumber}-${index}`,
-    inBlock: blockNumber,
+    inBlock: await getOrCreateBlock(db, substrateEvent),
     inExtrinsic: extrinsic?.hash,
     indexInBlock: index,
     type,
   })
+  await db.save<Event>(event)
+
+  return event
 }
 
 type MetadataClass<T> = {
@@ -26,3 +38,23 @@ export function deserializeMetadata<T>(metadataType: MetadataClass<T>, metadataB
     return null
   }
 }
+
+export async function getOrCreateBlock(
+  db: DatabaseManager,
+  { blockNumber, blockTimestamp }: SubstrateEvent
+): Promise<Block> {
+  const block = await db.get(Block, { where: { number: blockNumber } })
+  if (!block) {
+    const newBlock = new Block({
+      id: `${CURRENT_NETWORK}-${blockNumber}`,
+      number: blockNumber,
+      timestamp: blockTimestamp,
+      network: CURRENT_NETWORK,
+    })
+    await db.save<Block>(newBlock)
+
+    return newBlock
+  }
+
+  return block
+}

+ 11 - 2
query-node/mappings/initializeDb.ts

@@ -1,21 +1,30 @@
 import { ApiPromise } from '@polkadot/api'
 import { BalanceOf } from '@polkadot/types/interfaces'
 import { DatabaseManager } from '@dzlzv/hydra-db-utils'
-import { MembershipSystemSnapshot, WorkingGroup } from 'query-node/dist/model'
+import { Block, MembershipSystemSnapshot, WorkingGroup } from 'query-node/dist/model'
+import { CURRENT_NETWORK } from './common'
+import BN from 'bn.js'
 
 async function initMembershipSystem(api: ApiPromise, db: DatabaseManager) {
   const initialInvitationCount = await api.query.members.initialInvitationCount.at(api.genesisHash)
   const initialInvitationBalance = await api.query.members.initialInvitationBalance.at(api.genesisHash)
   const referralCut = await api.query.members.referralCut.at(api.genesisHash)
   const membershipPrice = await api.query.members.membershipPrice.at(api.genesisHash)
+  const genesisBlock = new Block({
+    id: `${CURRENT_NETWORK}-0`,
+    network: CURRENT_NETWORK,
+    number: 0,
+    timestamp: new BN(0),
+  })
   const membershipSystem = new MembershipSystemSnapshot({
-    snapshotBlock: 0,
+    snapshotBlock: genesisBlock,
     snapshotTime: new Date(0),
     defaultInviteCount: initialInvitationCount.toNumber(),
     membershipPrice,
     referralCut: referralCut.toNumber(),
     invitedInitialBalance: initialInvitationBalance,
   })
+  await db.save<Block>(genesisBlock)
   await db.save<MembershipSystemSnapshot>(membershipSystem)
 }
 

+ 23 - 34
query-node/mappings/membership.ts

@@ -8,7 +8,7 @@ import BN from 'bn.js'
 import { Bytes } from '@polkadot/types'
 import { MemberId, BuyMembershipParameters, InviteMembershipParameters } from '@joystream/types/augment/all'
 import { MembershipMetadata } from '@joystream/metadata-protobuf'
-import { createEvent } from './common'
+import { createEvent, getOrCreateBlock } from './common'
 import {
   Membership,
   EventType,
@@ -19,7 +19,6 @@ import {
   MemberProfileUpdatedEvent,
   MemberAccountsUpdatedEvent,
   MemberInvitedEvent,
-  Event,
   MemberVerificationStatusUpdatedEvent,
   InvitesTransferredEvent,
   StakingAccountConfirmedEvent,
@@ -41,7 +40,10 @@ async function getMemberById(db: DatabaseManager, id: MemberId): Promise<Members
 }
 
 async function getLatestMembershipSystemSnapshot(db: DatabaseManager): Promise<MembershipSystemSnapshot> {
-  const membershipSystem = await db.get(MembershipSystemSnapshot, { order: { snapshotBlock: 'DESC' } })
+  const membershipSystem = await db.get(MembershipSystemSnapshot, {
+    order: { snapshotBlock: 'DESC' },
+    relations: ['snapshotBlock'],
+  })
   if (!membershipSystem) {
     throw new Error(`Membership system snapshot not found! Forgot to run "yarn workspace query-node-root db:init"?`)
   }
@@ -50,12 +52,12 @@ async function getLatestMembershipSystemSnapshot(db: DatabaseManager): Promise<M
 
 async function getOrCreateMembershipSnapshot(db: DatabaseManager, event_: SubstrateEvent) {
   const latestSnapshot = await getLatestMembershipSystemSnapshot(db)
-  return latestSnapshot.snapshotBlock === event_.blockNumber
+  return latestSnapshot.snapshotBlock.number === event_.blockNumber
     ? latestSnapshot
     : new MembershipSystemSnapshot({
         ...latestSnapshot,
         id: undefined,
-        snapshotBlock: event_.blockNumber,
+        snapshotBlock: await getOrCreateBlock(db, event_),
         snapshotTime: new Date(new BN(event_.blockTimestamp).toNumber()),
       })
 }
@@ -97,7 +99,7 @@ async function newMembershipFromParams(
     controllerAccount: controllerAccount.toString(),
     handle: handle.unwrap().toString(),
     metadata: metadataEntity,
-    registeredAtBlock: event_.blockNumber,
+    registeredAtBlock: await getOrCreateBlock(db, event_),
     registeredAtTime: new Date(event_.blockTimestamp.toNumber()),
     entry: entryMethod,
     referredBy:
@@ -131,8 +133,9 @@ export async function members_MembershipBought(db: DatabaseManager, event_: Subs
     MembershipEntryMethod.PAID,
     buyMembershipParameters
   )
+
   const membershipBoughtEvent = new MembershipBoughtEvent({
-    event: createEvent(event_, EventType.MembershipBought),
+    event: await createEvent(db, event_, EventType.MembershipBought),
     newMember: member,
     controllerAccount: member.controllerAccount,
     rootAccount: member.rootAccount,
@@ -144,7 +147,6 @@ export async function members_MembershipBought(db: DatabaseManager, event_: Subs
     referrer: member.referredBy,
   })
 
-  await db.save<Event>(membershipBoughtEvent.event)
   await db.save<MemberMetadata>(membershipBoughtEvent.metadata)
   await db.save<MembershipBoughtEvent>(membershipBoughtEvent)
 }
@@ -169,7 +171,7 @@ export async function members_MemberProfileUpdated(db: DatabaseManager, event_:
   await db.save<Membership>(member)
 
   const memberProfileUpdatedEvent = new MemberProfileUpdatedEvent({
-    event: createEvent(event_, EventType.MemberProfileUpdated),
+    event: await createEvent(db, event_, EventType.MemberProfileUpdated),
     member: member,
     newHandle: member.handle,
     newMetadata: new MemberMetadata({
@@ -178,7 +180,6 @@ export async function members_MemberProfileUpdated(db: DatabaseManager, event_:
     }),
   })
 
-  await db.save<Event>(memberProfileUpdatedEvent.event)
   await db.save<MemberMetadata>(memberProfileUpdatedEvent.newMetadata)
   await db.save<MemberProfileUpdatedEvent>(memberProfileUpdatedEvent)
 }
@@ -197,13 +198,12 @@ export async function members_MemberAccountsUpdated(db: DatabaseManager, event_:
   await db.save<Membership>(member)
 
   const memberAccountsUpdatedEvent = new MemberAccountsUpdatedEvent({
-    event: createEvent(event_, EventType.MemberAccountsUpdated),
+    event: await createEvent(db, event_, EventType.MemberAccountsUpdated),
     member: member,
     newRootAccount: member.rootAccount,
     newControllerAccount: member.controllerAccount,
   })
 
-  await db.save<Event>(memberAccountsUpdatedEvent.event)
   await db.save<MemberAccountsUpdatedEvent>(memberAccountsUpdatedEvent)
 }
 
@@ -218,12 +218,11 @@ export async function members_MemberVerificationStatusUpdated(
   await db.save<Membership>(member)
 
   const memberVerificationStatusUpdatedEvent = new MemberVerificationStatusUpdatedEvent({
-    event: createEvent(event_, EventType.MemberVerificationStatusUpdated),
+    event: await createEvent(db, event_, EventType.MemberVerificationStatusUpdated),
     member: member,
     isVerified: member.isVerified,
   })
 
-  await db.save<Event>(memberVerificationStatusUpdatedEvent.event)
   await db.save<MemberVerificationStatusUpdatedEvent>(memberVerificationStatusUpdatedEvent)
 }
 
@@ -241,13 +240,12 @@ export async function members_InvitesTransferred(db: DatabaseManager, event_: Su
   await db.save<Membership>(targetMember)
 
   const invitesTransferredEvent = new InvitesTransferredEvent({
-    event: createEvent(event_, EventType.InvitesTransferred),
+    event: await createEvent(db, event_, EventType.InvitesTransferred),
     sourceMember,
     targetMember,
     numberOfInvites: numberOfInvites.toNumber(),
   })
 
-  await db.save<Event>(invitesTransferredEvent.event)
   await db.save<InvitesTransferredEvent>(invitesTransferredEvent)
 }
 
@@ -267,7 +265,7 @@ export async function members_MemberInvited(db: DatabaseManager, event_: Substra
   await db.save<Membership>(invitingMember)
 
   const memberInvitedEvent = new MemberInvitedEvent({
-    event: createEvent(event_, EventType.MemberInvited),
+    event: await createEvent(db, event_, EventType.MemberInvited),
     invitingMember,
     newMember: invitedMember,
     handle: invitedMember.handle,
@@ -279,7 +277,6 @@ export async function members_MemberInvited(db: DatabaseManager, event_: Substra
     }),
   })
 
-  await db.save<Event>(memberInvitedEvent.event)
   await db.save<MemberMetadata>(memberInvitedEvent.metadata)
   await db.save<MemberInvitedEvent>(memberInvitedEvent)
 }
@@ -288,12 +285,11 @@ export async function members_StakingAccountAdded(db: DatabaseManager, event_: S
   const { memberId, accountId } = new Members.StakingAccountAddedEvent(event_).data
 
   const stakingAccountAddedEvent = new StakingAccountAddedEvent({
-    event: createEvent(event_, EventType.StakingAccountAddedEvent),
+    event: await createEvent(db, event_, EventType.StakingAccountAddedEvent),
     member: new Membership({ id: memberId.toString() }),
     account: accountId.toString(),
   })
 
-  await db.save<Event>(stakingAccountAddedEvent.event)
   await db.save<StakingAccountAddedEvent>(stakingAccountAddedEvent)
 }
 
@@ -305,12 +301,11 @@ export async function members_StakingAccountConfirmed(db: DatabaseManager, event
   await db.save<Membership>(member)
 
   const stakingAccountConfirmedEvent = new StakingAccountConfirmedEvent({
-    event: createEvent(event_, EventType.StakingAccountConfirmed),
+    event: await createEvent(db, event_, EventType.StakingAccountConfirmed),
     member,
     account: accountId.toString(),
   })
 
-  await db.save<Event>(stakingAccountConfirmedEvent.event)
   await db.save<StakingAccountConfirmedEvent>(stakingAccountConfirmedEvent)
 }
 
@@ -325,12 +320,11 @@ export async function members_StakingAccountRemoved(db: DatabaseManager, event_:
   await db.save<Membership>(member)
 
   const stakingAccountRemovedEvent = new StakingAccountRemovedEvent({
-    event: createEvent(event_, EventType.StakingAccountRemoved),
+    event: await createEvent(db, event_, EventType.StakingAccountRemoved),
     member,
     account: accountId.toString(),
   })
 
-  await db.save<Event>(stakingAccountRemovedEvent.event)
   await db.save<StakingAccountRemovedEvent>(stakingAccountRemovedEvent)
 }
 
@@ -345,11 +339,10 @@ export async function members_InitialInvitationCountUpdated(
   await db.save<MembershipSystemSnapshot>(membershipSystemSnapshot)
 
   const initialInvitationCountUpdatedEvent = new InitialInvitationCountUpdatedEvent({
-    event: createEvent(event_, EventType.InitialInvitationCountUpdated),
+    event: await createEvent(db, event_, EventType.InitialInvitationCountUpdated),
     newInitialInvitationCount: newDefaultInviteCount.toNumber(),
   })
 
-  await db.save<Event>(initialInvitationCountUpdatedEvent.event)
   await db.save<InitialInvitationCountUpdatedEvent>(initialInvitationCountUpdatedEvent)
 }
 
@@ -361,11 +354,10 @@ export async function members_MembershipPriceUpdated(db: DatabaseManager, event_
   await db.save<MembershipSystemSnapshot>(membershipSystemSnapshot)
 
   const membershipPriceUpdatedEvent = new MembershipPriceUpdatedEvent({
-    event: createEvent(event_, EventType.MembershipPriceUpdated),
+    event: await createEvent(db, event_, EventType.MembershipPriceUpdated),
     newPrice: newMembershipPrice,
   })
 
-  await db.save<Event>(membershipPriceUpdatedEvent.event)
   await db.save<MembershipPriceUpdatedEvent>(membershipPriceUpdatedEvent)
 }
 
@@ -377,11 +369,10 @@ export async function members_ReferralCutUpdated(db: DatabaseManager, event_: Su
   await db.save<MembershipSystemSnapshot>(membershipSystemSnapshot)
 
   const referralCutUpdatedEvent = new ReferralCutUpdatedEvent({
-    event: createEvent(event_, EventType.ReferralCutUpdated),
+    event: await createEvent(db, event_, EventType.ReferralCutUpdated),
     newValue: newReferralCut.toNumber(),
   })
 
-  await db.save<Event>(referralCutUpdatedEvent.event)
   await db.save<ReferralCutUpdatedEvent>(referralCutUpdatedEvent)
 }
 
@@ -396,11 +387,10 @@ export async function members_InitialInvitationBalanceUpdated(
   await db.save<MembershipSystemSnapshot>(membershipSystemSnapshot)
 
   const initialInvitationBalanceUpdatedEvent = new InitialInvitationBalanceUpdatedEvent({
-    event: createEvent(event_, EventType.InitialInvitationBalanceUpdated),
+    event: await createEvent(db, event_, EventType.InitialInvitationBalanceUpdated),
     newInitialBalance: newInvitedInitialBalance,
   })
 
-  await db.save<Event>(initialInvitationBalanceUpdatedEvent.event)
   await db.save<InitialInvitationBalanceUpdatedEvent>(initialInvitationBalanceUpdatedEvent)
 }
 
@@ -408,10 +398,9 @@ export async function members_LeaderInvitationQuotaUpdated(db: DatabaseManager,
   const { u32: newQuota } = new Members.LeaderInvitationQuotaUpdatedEvent(event_).data
 
   const leaderInvitationQuotaUpdatedEvent = new LeaderInvitationQuotaUpdatedEvent({
-    event: createEvent(event_, EventType.LeaderInvitationQuotaUpdated),
+    event: await createEvent(db, event_, EventType.LeaderInvitationQuotaUpdated),
     newInvitationQuota: newQuota.toNumber(),
   })
 
-  await db.save<Event>(leaderInvitationQuotaUpdatedEvent.event)
   await db.save<LeaderInvitationQuotaUpdatedEvent>(leaderInvitationQuotaUpdatedEvent)
 }

+ 9 - 15
query-node/mappings/workingGroups.ts

@@ -6,7 +6,7 @@ import { DatabaseManager } from '@dzlzv/hydra-db-utils'
 import { StorageWorkingGroup as WorkingGroups } from './generated/types'
 import { ApplicationMetadata, OpeningMetadata } from '@joystream/metadata-protobuf'
 import { Bytes } from '@polkadot/types'
-import { createEvent, deserializeMetadata } from './common'
+import { createEvent, deserializeMetadata, getOrCreateBlock } from './common'
 import BN from 'bn.js'
 import {
   WorkingGroupOpening,
@@ -18,7 +18,6 @@ import {
   OpeningStatusOpen,
   WorkingGroupOpeningType,
   EventType,
-  Event,
   WorkingGroupApplication,
   ApplicationFormQuestionAnswer,
   AppliedOnOpeningEvent,
@@ -216,7 +215,7 @@ export async function workingGroups_OpeningAdded(db: DatabaseManager, event_: Su
   const opening = new WorkingGroupOpening({
     createdAt: eventTime,
     updatedAt: eventTime,
-    createdAtBlock: event_.blockNumber,
+    createdAtBlock: await getOrCreateBlock(db, event_),
     id: `${group.name}-${openingRuntimeId.toString()}`,
     runtimeId: openingRuntimeId.toNumber(),
     applications: [],
@@ -233,7 +232,7 @@ export async function workingGroups_OpeningAdded(db: DatabaseManager, event_: Su
 
   await db.save<WorkingGroupOpening>(opening)
 
-  const event = await createEvent(event_, EventType.OpeningAdded)
+  const event = await createEvent(db, event_, EventType.OpeningAdded)
   const openingAddedEvent = new OpeningAddedEvent({
     createdAt: eventTime,
     updatedAt: eventTime,
@@ -242,7 +241,6 @@ export async function workingGroups_OpeningAdded(db: DatabaseManager, event_: Su
     opening,
   })
 
-  await db.save<Event>(event)
   await db.save<OpeningAddedEvent>(openingAddedEvent)
 }
 
@@ -267,7 +265,7 @@ export async function workingGroups_AppliedOnOpening(db: DatabaseManager, event_
   const application = new WorkingGroupApplication({
     createdAt: eventTime,
     updatedAt: eventTime,
-    createdAtBlock: event_.blockNumber,
+    createdAtBlock: await getOrCreateBlock(db, event_),
     id: `${group.name}-${applicationRuntimeId.toString()}`,
     runtimeId: applicationRuntimeId.toNumber(),
     opening: new WorkingGroupOpening({ id: openingDbId }),
@@ -283,7 +281,7 @@ export async function workingGroups_AppliedOnOpening(db: DatabaseManager, event_
   await db.save<WorkingGroupApplication>(application)
   await createApplicationQuestionAnswers(db, application, metadataBytes)
 
-  const event = await createEvent(event_, EventType.AppliedOnOpening)
+  const event = await createEvent(db, event_, EventType.AppliedOnOpening)
   const appliedOnOpeningEvent = new AppliedOnOpeningEvent({
     createdAt: eventTime,
     updatedAt: eventTime,
@@ -293,7 +291,6 @@ export async function workingGroups_AppliedOnOpening(db: DatabaseManager, event_
     application,
   })
 
-  await db.save<Event>(event)
   await db.save<AppliedOnOpeningEvent>(appliedOnOpeningEvent)
 }
 
@@ -315,7 +312,7 @@ export async function workingGroups_OpeningFilled(db: DatabaseManager, event_: S
   const acceptedApplicationIds = createType('Vec<ApplicationId>', applicationIdsSet.toHex() as any)
 
   // Save the event
-  const event = await createEvent(event_, EventType.OpeningFilled)
+  const event = await createEvent(db, event_, EventType.OpeningFilled)
   const openingFilledEvent = new OpeningFilledEvent({
     createdAt: eventTime,
     updatedAt: eventTime,
@@ -324,7 +321,6 @@ export async function workingGroups_OpeningFilled(db: DatabaseManager, event_: S
     opening,
   })
 
-  await db.save<Event>(event)
   await db.save<OpeningFilledEvent>(openingFilledEvent)
 
   const hiredWorkers: Worker[] = []
@@ -356,7 +352,7 @@ export async function workingGroups_OpeningFilled(db: DatabaseManager, event_: S
             updatedAt: eventTime,
             id: `${group.name}-${workerRuntimeId.toString()}`,
             runtimeId: workerRuntimeId.toNumber(),
-            hiredAtBlock: event_.blockNumber,
+            hiredAtBlock: await getOrCreateBlock(db, event_),
             hiredAtTime: new Date(event_.blockTimestamp.toNumber()),
             application,
             group,
@@ -425,7 +421,7 @@ export async function workingGroups_OpeningCanceled(db: DatabaseManager, event_:
   const eventTime = new Date(event_.blockTimestamp.toNumber())
 
   // Create and save event
-  const event = createEvent(event_, EventType.OpeningCanceled)
+  const event = await createEvent(db, event_, EventType.OpeningCanceled)
   const openingCanceledEvent = new OpeningCanceledEvent({
     createdAt: eventTime,
     updatedAt: eventTime,
@@ -434,7 +430,6 @@ export async function workingGroups_OpeningCanceled(db: DatabaseManager, event_:
     opening,
   })
 
-  await db.save<Event>(event)
   await db.save<OpeningCanceledEvent>(openingCanceledEvent)
 
   // Set opening status
@@ -469,7 +464,7 @@ export async function workingGroups_ApplicationWithdrawn(db: DatabaseManager, ev
   const eventTime = new Date(event_.blockTimestamp.toNumber())
 
   // Create and save event
-  const event = createEvent(event_, EventType.ApplicationWithdrawn)
+  const event = await createEvent(db, event_, EventType.ApplicationWithdrawn)
   const applicationWithdrawnEvent = new ApplicationWithdrawnEvent({
     createdAt: eventTime,
     updatedAt: eventTime,
@@ -478,7 +473,6 @@ export async function workingGroups_ApplicationWithdrawn(db: DatabaseManager, ev
     application,
   })
 
-  await db.save<Event>(event)
   await db.save<ApplicationWithdrawnEvent>(applicationWithdrawnEvent)
 
   // Set application status

+ 16 - 2
query-node/schemas/common.graphql

@@ -46,6 +46,20 @@ enum EventType {
   NewMissedRewardLevelReached
 }
 
+type Block @entity {
+  "{network}-{blockNumber}"
+  id: ID!
+
+  "Block number (height)"
+  number: Int!
+
+  "Block timestamp"
+  timestamp: BigInt!
+
+  "Network the block was produced in"
+  network: Network!
+}
+
 type Event @entity {
   "{blockNumber}-{indexInBlock}"
   id: ID!
@@ -53,8 +67,8 @@ type Event @entity {
   "Hash of the extrinsic which caused the event to be emitted"
   inExtrinsic: String
 
-  "Blocknumber of a block in which the event was emitted."
-  inBlock: Int!
+  "Block in which the event was emitted."
+  inBlock: Block!
 
   "Index of event in block from which it was emitted."
   indexInBlock: Int!

+ 4 - 4
query-node/schemas/membership.graphql

@@ -32,8 +32,8 @@ type Membership @entity {
   "Member's root account id"
   rootAccount: String!
 
-  "Block number when member was registered"
-  registeredAtBlock: Int!
+  "Block at which the member was registered"
+  registeredAtBlock: Block!
 
   "Timestamp when member was registered"
   registeredAtTime: DateTime!
@@ -70,8 +70,8 @@ type Membership @entity {
 }
 
 type MembershipSystemSnapshot @entity {
-  "Block number of the snapshot block"
-  snapshotBlock: Int!
+  "The snapshot block"
+  snapshotBlock: Block!
 
   "Time of the snapshot (based on block timestamp)"
   snapshotTime: DateTime!

+ 8 - 9
query-node/schemas/workingGroups.graphql

@@ -56,9 +56,8 @@ type Worker @entity {
   "All related reward payouts"
   payouts: [RewardPaidEvent!] @derivedFrom(field: "worker")
 
-  # TODO: should we use createdAt for consistency / doesn't Hydra actually require us to override this field?
-  "Blocknumber of the block the worker was hired at"
-  hiredAtBlock: Int!
+  "Block the worker was hired at"
+  hiredAtBlock: Block!
 
   "Time the worker was hired at"
   hiredAtTime: DateTime!
@@ -86,8 +85,8 @@ type WorkingGroupMetadata @entity {
   "Working group description text"
   description: String
 
-  "Blocknumber of the block at which status was set"
-  setAtBlock: Int!
+  "Block the status was set at"
+  setAtBlock: Block!
 
   "The time at which status was set"
   setAtTime: DateTime!
@@ -191,8 +190,8 @@ type WorkingGroupOpening @entity {
   "Initial workers' reward per block"
   rewardPerBlock: BigInt!
 
-  "Blocknumber of opening creation block"
-  createdAtBlock: Int!
+  "Block the opening was created at"
+  createdAtBlock: Block!
 
   "Time of opening creation"
   createdAt: DateTime!
@@ -260,8 +259,8 @@ type WorkingGroupApplication @entity {
   "Current application status"
   status: WorkingGroupApplicationStatus!
 
-  "Blocknumber of application creation block"
-  createdAtBlock: Int!
+  "Block the application was created at"
+  createdAtBlock: Block!
 
   "Time of application creation"
   createdAt: DateTime!

+ 6 - 3
tests/integration-tests/src/Api.ts

@@ -266,14 +266,17 @@ export class Api {
       return
     }
 
-    const blockHash = status.asInBlock
-    const { number: blockNumber } = await this.api.rpc.chain.getHeader(blockHash)
+    const blockHash = status.asInBlock.toString()
+    const blockNumber = (await this.api.rpc.chain.getHeader(blockHash)).number.toNumber()
+    const blockTimestamp = (await this.api.query.timestamp.now.at(blockHash)).toNumber()
     const blockEvents = await this.api.query.system.events.at(blockHash)
     const indexInBlock = blockEvents.findIndex(({ event: blockEvent }) => blockEvent.hash.eq(record.event.hash))
 
     return {
       event: record.event,
-      blockNumber: blockNumber.toNumber(),
+      blockNumber,
+      blockHash,
+      blockTimestamp,
       indexInBlock,
     }
   }

+ 66 - 119
tests/integration-tests/src/QueryNodeApi.ts

@@ -17,6 +17,20 @@ import Debugger from 'debug'
 import { ApplicationId, OpeningId } from '@joystream/types/working-group'
 import { WorkingGroupModuleName } from './types'
 
+const EVENT_GENERIC_FIELDS = `
+  id
+  event {
+    inBlock {
+      number
+      timestamp
+      network
+    }
+    inExtrinsic
+    indexInBlock
+    type
+  }
+`
+
 export class QueryNodeApi {
   private readonly queryNodeProvider: ApolloClient<NormalizedCacheObject>
   private readonly debug: Debugger.Debugger
@@ -85,7 +99,11 @@ export class QueryNodeApi {
           }
           controllerAccount
           rootAccount
-          registeredAtBlock
+          registeredAtBlock {
+            number
+            timestamp
+            network
+          }
           registeredAtTime
           entry
           isVerified
@@ -112,12 +130,7 @@ export class QueryNodeApi {
     const MEMBERTSHIP_BOUGHT_BY_MEMBER_ID = gql`
       query($memberId: ID!) {
         membershipBoughtEvents(where: { newMemberId_eq: $memberId }) {
-          event {
-            inBlock
-            inExtrinsic
-            indexInBlock
-            type
-          }
+          ${EVENT_GENERIC_FIELDS}
           newMember {
             id
           }
@@ -149,12 +162,7 @@ export class QueryNodeApi {
     const MEMBER_PROFILE_UPDATED_BY_MEMBER_ID = gql`
       query($memberId: ID!) {
         memberProfileUpdatedEvents(where: { memberId_eq: $memberId }) {
-          event {
-            inBlock
-            inExtrinsic
-            indexInBlock
-            type
-          }
+          ${EVENT_GENERIC_FIELDS}
           member {
             id
           }
@@ -181,12 +189,7 @@ export class QueryNodeApi {
     const MEMBER_ACCOUNTS_UPDATED_BY_MEMBER_ID = gql`
       query($memberId: ID!) {
         memberAccountsUpdatedEvents(where: { memberId_eq: $memberId }) {
-          event {
-            inBlock
-            inExtrinsic
-            indexInBlock
-            type
-          }
+          ${EVENT_GENERIC_FIELDS}
           member {
             id
           }
@@ -210,12 +213,7 @@ export class QueryNodeApi {
     const MEMBER_INVITED_BY_MEMBER_ID = gql`
       query($memberId: ID!) {
         memberInvitedEvents(where: { newMemberId_eq: $memberId }) {
-          event {
-            inBlock
-            inExtrinsic
-            indexInBlock
-            type
-          }
+          ${EVENT_GENERIC_FIELDS}
           invitingMember {
             id
           }
@@ -247,12 +245,7 @@ export class QueryNodeApi {
     const INVITES_TRANSFERRED_BY_MEMBER_ID = gql`
       query($from: ID!) {
         invitesTransferredEvents(where: { sourceMemberId_eq: $from }) {
-          event {
-            inBlock
-            inExtrinsic
-            indexInBlock
-            type
-          }
+          ${EVENT_GENERIC_FIELDS}
           sourceMember {
             id
           }
@@ -278,12 +271,7 @@ export class QueryNodeApi {
     const STAKING_ACCOUNT_ADDED_BY_MEMBER_ID = gql`
       query($memberId: ID!) {
         stakingAccountAddedEvents(where: { memberId_eq: $memberId }) {
-          event {
-            inBlock
-            inExtrinsic
-            indexInBlock
-            type
-          }
+          ${EVENT_GENERIC_FIELDS}
           member {
             id
           }
@@ -306,12 +294,7 @@ export class QueryNodeApi {
     const STAKING_ACCOUNT_CONFIRMED_BY_MEMBER_ID = gql`
       query($memberId: ID!) {
         stakingAccountConfirmedEvents(where: { memberId_eq: $memberId }) {
-          event {
-            inBlock
-            inExtrinsic
-            indexInBlock
-            type
-          }
+          ${EVENT_GENERIC_FIELDS}
           member {
             id
           }
@@ -334,12 +317,7 @@ export class QueryNodeApi {
     const STAKING_ACCOUNT_REMOVED_BY_MEMBER_ID = gql`
       query($memberId: ID!) {
         stakingAccountRemovedEvents(where: { memberId_eq: $memberId }) {
-          event {
-            inBlock
-            inExtrinsic
-            indexInBlock
-            type
-          }
+          ${EVENT_GENERIC_FIELDS}
           member {
             id
           }
@@ -356,29 +334,34 @@ export class QueryNodeApi {
     })
   }
 
+  // FIXME: Cross-filtering is not enabled yet, so we have to use timestamp workaround
   public async getMembershipSystemSnapshot(
-    blockNumber: number,
+    timestamp: number,
     matchType: 'eq' | 'lt' | 'lte' | 'gt' | 'gte' = 'eq'
   ): Promise<MembershipSystemSnapshot | undefined> {
     const MEMBERSHIP_SYSTEM_SNAPSHOT_QUERY = gql`
-      query($blockNumber: Int!) {
-        membershipSystemSnapshots(where: { snapshotBlock_${matchType}: $blockNumber }, orderBy: snapshotBlock_DESC, limit: 1) {
-          snapshotBlock,
-          snapshotTime,
-          referralCut,
-          invitedInitialBalance,
-          defaultInviteCount,
+      query($time: DateTime!) {
+        membershipSystemSnapshots(where: { snapshotTime_${matchType}: $time }, orderBy: snapshotTime_DESC, limit: 1) {
+          snapshotBlock {
+            timestamp
+            network
+            number
+          }
+          snapshotTime
+          referralCut
+          invitedInitialBalance
+          defaultInviteCount
           membershipPrice
         }
       }
     `
 
-    this.queryDebug(`Executing getMembershipSystemSnapshot(${matchType} ${blockNumber})`)
+    this.queryDebug(`Executing getMembershipSystemSnapshot(${matchType} ${timestamp})`)
 
     return (
       await this.queryNodeProvider.query<Pick<Query, 'membershipSystemSnapshots'>>({
         query: MEMBERSHIP_SYSTEM_SNAPSHOT_QUERY,
-        variables: { blockNumber },
+        variables: { time: new Date(timestamp) },
       })
     ).data.membershipSystemSnapshots[0]
   }
@@ -390,12 +373,7 @@ export class QueryNodeApi {
     const REFERRAL_CUT_UPDATED_BY_ID = gql`
       query($eventId: ID!) {
         referralCutUpdatedEvents(where: { eventId_eq: $eventId }) {
-          event {
-            inBlock
-            inExtrinsic
-            indexInBlock
-            type
-          }
+          ${EVENT_GENERIC_FIELDS}
           newValue
         }
       }
@@ -419,12 +397,7 @@ export class QueryNodeApi {
     const MEMBERSHIP_PRICE_UPDATED_BY_ID = gql`
       query($eventId: ID!) {
         membershipPriceUpdatedEvents(where: { eventId_eq: $eventId }) {
-          event {
-            inBlock
-            inExtrinsic
-            indexInBlock
-            type
-          }
+          ${EVENT_GENERIC_FIELDS}
           newPrice
         }
       }
@@ -448,12 +421,7 @@ export class QueryNodeApi {
     const INITIAL_INVITATION_BALANCE_UPDATED_BY_ID = gql`
       query($eventId: ID!) {
         initialInvitationBalanceUpdatedEvents(where: { eventId_eq: $eventId }) {
-          event {
-            inBlock
-            inExtrinsic
-            indexInBlock
-            type
-          }
+          ${EVENT_GENERIC_FIELDS}
           newInitialBalance
         }
       }
@@ -477,12 +445,7 @@ export class QueryNodeApi {
     const INITIAL_INVITATION_COUNT_UPDATED_BY_ID = gql`
       query($eventId: ID!) {
         initialInvitationCountUpdatedEvents(where: { eventId_eq: $eventId }) {
-          event {
-            inBlock
-            inExtrinsic
-            indexInBlock
-            type
-          }
+          ${EVENT_GENERIC_FIELDS}
           newInitialInvitationCount
         }
       }
@@ -558,7 +521,11 @@ export class QueryNodeApi {
           stakeAmount
           unstakingPeriod
           rewardPerBlock
-          createdAtBlock
+          createdAtBlock {
+            number
+            timestamp
+            network
+          }
           createdAt
         }
       }
@@ -582,7 +549,11 @@ export class QueryNodeApi {
         workingGroupApplicationByUniqueInput(where: { id: $applicationId }) {
           id
           runtimeId
-          createdAtBlock
+          createdAtBlock {
+            number
+            timestamp
+            network
+          }
           createdAt
           opening {
             id
@@ -636,12 +607,7 @@ export class QueryNodeApi {
     const APPLIED_ON_OPENING_BY_ID = gql`
       query($eventId: ID!) {
         appliedOnOpeningEvents(where: { eventId_eq: $eventId }) {
-          event {
-            inBlock
-            inExtrinsic
-            indexInBlock
-            type
-          }
+          ${EVENT_GENERIC_FIELDS}
           group {
             name
           }
@@ -672,12 +638,7 @@ export class QueryNodeApi {
     const OPENING_ADDED_BY_ID = gql`
       query($eventId: ID!) {
         openingAddedEvents(where: { eventId_eq: $eventId }) {
-          event {
-            inBlock
-            inExtrinsic
-            indexInBlock
-            type
-          }
+          ${EVENT_GENERIC_FIELDS}
           group {
             name
           }
@@ -707,13 +668,7 @@ export class QueryNodeApi {
     const OPENING_FILLED_BY_ID = gql`
       query($eventId: ID!) {
         openingFilledEvents(where: { eventId_eq: $eventId }) {
-          id
-          event {
-            inBlock
-            inExtrinsic
-            indexInBlock
-            type
-          }
+          ${EVENT_GENERIC_FIELDS}
           group {
             name
           }
@@ -741,7 +696,11 @@ export class QueryNodeApi {
             payouts {
               id
             }
-            hiredAtBlock
+            hiredAtBlock {
+              number
+              timestamp
+              network
+            }
             hiredAtTime
             application {
               id
@@ -771,13 +730,7 @@ export class QueryNodeApi {
     const APPLICATION_WITHDRAWN_BY_ID = gql`
       query($eventId: ID!) {
         applicationWithdrawnEvents(where: { eventId_eq: $eventId }) {
-          id
-          event {
-            inBlock
-            inExtrinsic
-            indexInBlock
-            type
-          }
+          ${EVENT_GENERIC_FIELDS}
           group {
             name
           }
@@ -807,13 +760,7 @@ export class QueryNodeApi {
     const OPENING_CANCELLED_BY_ID = gql`
       query($eventId: ID!) {
         openingCanceledEvents(where: { eventId_eq: $eventId }) {
-          id
-          event {
-            inBlock
-            inExtrinsic
-            indexInBlock
-            type
-          }
+          ${EVENT_GENERIC_FIELDS}
           group {
             name
           }

+ 208 - 88
tests/integration-tests/src/QueryNodeApiSchema.generated.ts

@@ -734,6 +734,116 @@ export type BaseWhereInput = {
   deletedById_eq?: Maybe<Scalars['String']>
 }
 
+export type Block = BaseGraphQlObject & {
+  __typename?: 'Block'
+  id: Scalars['ID']
+  createdAt: Scalars['DateTime']
+  createdById: Scalars['String']
+  updatedAt?: Maybe<Scalars['DateTime']>
+  updatedById?: Maybe<Scalars['String']>
+  deletedAt?: Maybe<Scalars['DateTime']>
+  deletedById?: Maybe<Scalars['String']>
+  version: Scalars['Int']
+  /** Block number (height) */
+  number: Scalars['Int']
+  /** Block timestamp */
+  timestamp: Scalars['BigInt']
+  /** Network the block was produced in */
+  network: Network
+  eventinBlock?: Maybe<Array<Event>>
+  membershipregisteredAtBlock?: Maybe<Array<Membership>>
+  membershipsystemsnapshotsnapshotBlock?: Maybe<Array<MembershipSystemSnapshot>>
+  workerhiredAtBlock?: Maybe<Array<Worker>>
+  workinggroupapplicationcreatedAtBlock?: Maybe<Array<WorkingGroupApplication>>
+  workinggroupmetadatasetAtBlock?: Maybe<Array<WorkingGroupMetadata>>
+  workinggroupopeningcreatedAtBlock?: Maybe<Array<WorkingGroupOpening>>
+}
+
+export type BlockConnection = {
+  __typename?: 'BlockConnection'
+  totalCount: Scalars['Int']
+  edges: Array<BlockEdge>
+  pageInfo: PageInfo
+}
+
+export type BlockCreateInput = {
+  number: Scalars['Float']
+  timestamp: Scalars['BigInt']
+  network: Network
+}
+
+export type BlockEdge = {
+  __typename?: 'BlockEdge'
+  node: Block
+  cursor: Scalars['String']
+}
+
+export enum BlockOrderByInput {
+  CreatedAtAsc = 'createdAt_ASC',
+  CreatedAtDesc = 'createdAt_DESC',
+  UpdatedAtAsc = 'updatedAt_ASC',
+  UpdatedAtDesc = 'updatedAt_DESC',
+  DeletedAtAsc = 'deletedAt_ASC',
+  DeletedAtDesc = 'deletedAt_DESC',
+  NumberAsc = 'number_ASC',
+  NumberDesc = 'number_DESC',
+  TimestampAsc = 'timestamp_ASC',
+  TimestampDesc = 'timestamp_DESC',
+  NetworkAsc = 'network_ASC',
+  NetworkDesc = 'network_DESC',
+}
+
+export type BlockUpdateInput = {
+  number?: Maybe<Scalars['Float']>
+  timestamp?: Maybe<Scalars['BigInt']>
+  network?: Maybe<Network>
+}
+
+export type BlockWhereInput = {
+  id_eq?: Maybe<Scalars['ID']>
+  id_in?: Maybe<Array<Scalars['ID']>>
+  createdAt_eq?: Maybe<Scalars['DateTime']>
+  createdAt_lt?: Maybe<Scalars['DateTime']>
+  createdAt_lte?: Maybe<Scalars['DateTime']>
+  createdAt_gt?: Maybe<Scalars['DateTime']>
+  createdAt_gte?: Maybe<Scalars['DateTime']>
+  createdById_eq?: Maybe<Scalars['ID']>
+  createdById_in?: Maybe<Array<Scalars['ID']>>
+  updatedAt_eq?: Maybe<Scalars['DateTime']>
+  updatedAt_lt?: Maybe<Scalars['DateTime']>
+  updatedAt_lte?: Maybe<Scalars['DateTime']>
+  updatedAt_gt?: Maybe<Scalars['DateTime']>
+  updatedAt_gte?: Maybe<Scalars['DateTime']>
+  updatedById_eq?: Maybe<Scalars['ID']>
+  updatedById_in?: Maybe<Array<Scalars['ID']>>
+  deletedAt_all?: Maybe<Scalars['Boolean']>
+  deletedAt_eq?: Maybe<Scalars['DateTime']>
+  deletedAt_lt?: Maybe<Scalars['DateTime']>
+  deletedAt_lte?: Maybe<Scalars['DateTime']>
+  deletedAt_gt?: Maybe<Scalars['DateTime']>
+  deletedAt_gte?: Maybe<Scalars['DateTime']>
+  deletedById_eq?: Maybe<Scalars['ID']>
+  deletedById_in?: Maybe<Array<Scalars['ID']>>
+  number_eq?: Maybe<Scalars['Int']>
+  number_gt?: Maybe<Scalars['Int']>
+  number_gte?: Maybe<Scalars['Int']>
+  number_lt?: Maybe<Scalars['Int']>
+  number_lte?: Maybe<Scalars['Int']>
+  number_in?: Maybe<Array<Scalars['Int']>>
+  timestamp_eq?: Maybe<Scalars['BigInt']>
+  timestamp_gt?: Maybe<Scalars['BigInt']>
+  timestamp_gte?: Maybe<Scalars['BigInt']>
+  timestamp_lt?: Maybe<Scalars['BigInt']>
+  timestamp_lte?: Maybe<Scalars['BigInt']>
+  timestamp_in?: Maybe<Array<Scalars['BigInt']>>
+  network_eq?: Maybe<Network>
+  network_in?: Maybe<Array<Network>>
+}
+
+export type BlockWhereUniqueInput = {
+  id: Scalars['ID']
+}
+
 export type BudgetSetEvent = BaseGraphQlObject & {
   __typename?: 'BudgetSetEvent'
   id: Scalars['ID']
@@ -1408,8 +1518,8 @@ export type Event = BaseGraphQlObject & {
   version: Scalars['Int']
   /** Hash of the extrinsic which caused the event to be emitted */
   inExtrinsic?: Maybe<Scalars['String']>
-  /** Blocknumber of a block in which the event was emitted. */
-  inBlock: Scalars['Int']
+  inBlock: Block
+  inBlockId: Scalars['String']
   /** Index of event in block from which it was emitted. */
   indexInBlock: Scalars['Int']
   /** Type of the event */
@@ -1460,7 +1570,7 @@ export type EventConnection = {
 
 export type EventCreateInput = {
   inExtrinsic?: Maybe<Scalars['String']>
-  inBlock: Scalars['Float']
+  inBlockId: Scalars['ID']
   indexInBlock: Scalars['Float']
   type: EventType
 }
@@ -1480,8 +1590,8 @@ export enum EventOrderByInput {
   DeletedAtDesc = 'deletedAt_DESC',
   InExtrinsicAsc = 'inExtrinsic_ASC',
   InExtrinsicDesc = 'inExtrinsic_DESC',
-  InBlockAsc = 'inBlock_ASC',
-  InBlockDesc = 'inBlock_DESC',
+  InBlockIdAsc = 'inBlockId_ASC',
+  InBlockIdDesc = 'inBlockId_DESC',
   IndexInBlockAsc = 'indexInBlock_ASC',
   IndexInBlockDesc = 'indexInBlock_DESC',
   TypeAsc = 'type_ASC',
@@ -1529,7 +1639,7 @@ export enum EventType {
 
 export type EventUpdateInput = {
   inExtrinsic?: Maybe<Scalars['String']>
-  inBlock?: Maybe<Scalars['Float']>
+  inBlockId?: Maybe<Scalars['ID']>
   indexInBlock?: Maybe<Scalars['Float']>
   type?: Maybe<EventType>
 }
@@ -1564,12 +1674,8 @@ export type EventWhereInput = {
   inExtrinsic_startsWith?: Maybe<Scalars['String']>
   inExtrinsic_endsWith?: Maybe<Scalars['String']>
   inExtrinsic_in?: Maybe<Array<Scalars['String']>>
-  inBlock_eq?: Maybe<Scalars['Int']>
-  inBlock_gt?: Maybe<Scalars['Int']>
-  inBlock_gte?: Maybe<Scalars['Int']>
-  inBlock_lt?: Maybe<Scalars['Int']>
-  inBlock_lte?: Maybe<Scalars['Int']>
-  inBlock_in?: Maybe<Array<Scalars['Int']>>
+  inBlockId_eq?: Maybe<Scalars['ID']>
+  inBlockId_in?: Maybe<Array<Scalars['ID']>>
   indexInBlock_eq?: Maybe<Scalars['Int']>
   indexInBlock_gt?: Maybe<Scalars['Int']>
   indexInBlock_gte?: Maybe<Scalars['Int']>
@@ -2637,8 +2743,8 @@ export type Membership = BaseGraphQlObject & {
   controllerAccount: Scalars['String']
   /** Member's root account id */
   rootAccount: Scalars['String']
-  /** Block number when member was registered */
-  registeredAtBlock: Scalars['Int']
+  registeredAtBlock: Block
+  registeredAtBlockId: Scalars['String']
   /** Timestamp when member was registered */
   registeredAtTime: Scalars['DateTime']
   /** How the member was registered */
@@ -2821,7 +2927,7 @@ export type MembershipCreateInput = {
   metadataId: Scalars['ID']
   controllerAccount: Scalars['String']
   rootAccount: Scalars['String']
-  registeredAtBlock: Scalars['Float']
+  registeredAtBlockId: Scalars['ID']
   registeredAtTime: Scalars['DateTime']
   entry: MembershipEntryMethod
   isVerified: Scalars['Boolean']
@@ -2859,8 +2965,8 @@ export enum MembershipOrderByInput {
   ControllerAccountDesc = 'controllerAccount_DESC',
   RootAccountAsc = 'rootAccount_ASC',
   RootAccountDesc = 'rootAccount_DESC',
-  RegisteredAtBlockAsc = 'registeredAtBlock_ASC',
-  RegisteredAtBlockDesc = 'registeredAtBlock_DESC',
+  RegisteredAtBlockIdAsc = 'registeredAtBlockId_ASC',
+  RegisteredAtBlockIdDesc = 'registeredAtBlockId_DESC',
   RegisteredAtTimeAsc = 'registeredAtTime_ASC',
   RegisteredAtTimeDesc = 'registeredAtTime_DESC',
   EntryAsc = 'entry_ASC',
@@ -2978,8 +3084,8 @@ export type MembershipSystemSnapshot = BaseGraphQlObject & {
   deletedAt?: Maybe<Scalars['DateTime']>
   deletedById?: Maybe<Scalars['String']>
   version: Scalars['Int']
-  /** Block number of the snapshot block */
-  snapshotBlock: Scalars['Int']
+  snapshotBlock: Block
+  snapshotBlockId: Scalars['String']
   /** Time of the snapshot (based on block timestamp) */
   snapshotTime: Scalars['DateTime']
   /** Initial invitation count of a new member. */
@@ -3000,7 +3106,7 @@ export type MembershipSystemSnapshotConnection = {
 }
 
 export type MembershipSystemSnapshotCreateInput = {
-  snapshotBlock: Scalars['Float']
+  snapshotBlockId: Scalars['ID']
   snapshotTime: Scalars['DateTime']
   defaultInviteCount: Scalars['Float']
   membershipPrice: Scalars['BigInt']
@@ -3021,8 +3127,8 @@ export enum MembershipSystemSnapshotOrderByInput {
   UpdatedAtDesc = 'updatedAt_DESC',
   DeletedAtAsc = 'deletedAt_ASC',
   DeletedAtDesc = 'deletedAt_DESC',
-  SnapshotBlockAsc = 'snapshotBlock_ASC',
-  SnapshotBlockDesc = 'snapshotBlock_DESC',
+  SnapshotBlockIdAsc = 'snapshotBlockId_ASC',
+  SnapshotBlockIdDesc = 'snapshotBlockId_DESC',
   SnapshotTimeAsc = 'snapshotTime_ASC',
   SnapshotTimeDesc = 'snapshotTime_DESC',
   DefaultInviteCountAsc = 'defaultInviteCount_ASC',
@@ -3036,7 +3142,7 @@ export enum MembershipSystemSnapshotOrderByInput {
 }
 
 export type MembershipSystemSnapshotUpdateInput = {
-  snapshotBlock?: Maybe<Scalars['Float']>
+  snapshotBlockId?: Maybe<Scalars['ID']>
   snapshotTime?: Maybe<Scalars['DateTime']>
   defaultInviteCount?: Maybe<Scalars['Float']>
   membershipPrice?: Maybe<Scalars['BigInt']>
@@ -3069,12 +3175,8 @@ export type MembershipSystemSnapshotWhereInput = {
   deletedAt_gte?: Maybe<Scalars['DateTime']>
   deletedById_eq?: Maybe<Scalars['ID']>
   deletedById_in?: Maybe<Array<Scalars['ID']>>
-  snapshotBlock_eq?: Maybe<Scalars['Int']>
-  snapshotBlock_gt?: Maybe<Scalars['Int']>
-  snapshotBlock_gte?: Maybe<Scalars['Int']>
-  snapshotBlock_lt?: Maybe<Scalars['Int']>
-  snapshotBlock_lte?: Maybe<Scalars['Int']>
-  snapshotBlock_in?: Maybe<Array<Scalars['Int']>>
+  snapshotBlockId_eq?: Maybe<Scalars['ID']>
+  snapshotBlockId_in?: Maybe<Array<Scalars['ID']>>
   snapshotTime_eq?: Maybe<Scalars['DateTime']>
   snapshotTime_lt?: Maybe<Scalars['DateTime']>
   snapshotTime_lte?: Maybe<Scalars['DateTime']>
@@ -3115,7 +3217,7 @@ export type MembershipUpdateInput = {
   metadataId?: Maybe<Scalars['ID']>
   controllerAccount?: Maybe<Scalars['String']>
   rootAccount?: Maybe<Scalars['String']>
-  registeredAtBlock?: Maybe<Scalars['Float']>
+  registeredAtBlockId?: Maybe<Scalars['ID']>
   registeredAtTime?: Maybe<Scalars['DateTime']>
   entry?: Maybe<MembershipEntryMethod>
   isVerified?: Maybe<Scalars['Boolean']>
@@ -3168,12 +3270,8 @@ export type MembershipWhereInput = {
   rootAccount_startsWith?: Maybe<Scalars['String']>
   rootAccount_endsWith?: Maybe<Scalars['String']>
   rootAccount_in?: Maybe<Array<Scalars['String']>>
-  registeredAtBlock_eq?: Maybe<Scalars['Int']>
-  registeredAtBlock_gt?: Maybe<Scalars['Int']>
-  registeredAtBlock_gte?: Maybe<Scalars['Int']>
-  registeredAtBlock_lt?: Maybe<Scalars['Int']>
-  registeredAtBlock_lte?: Maybe<Scalars['Int']>
-  registeredAtBlock_in?: Maybe<Array<Scalars['Int']>>
+  registeredAtBlockId_eq?: Maybe<Scalars['ID']>
+  registeredAtBlockId_in?: Maybe<Array<Scalars['ID']>>
   registeredAtTime_eq?: Maybe<Scalars['DateTime']>
   registeredAtTime_lt?: Maybe<Scalars['DateTime']>
   registeredAtTime_lte?: Maybe<Scalars['DateTime']>
@@ -3305,6 +3403,13 @@ export type MemberVerificationStatusUpdatedEventWhereUniqueInput = {
   id: Scalars['ID']
 }
 
+export enum Network {
+  Babylon = 'BABYLON',
+  Alexandria = 'ALEXANDRIA',
+  Rome = 'ROME',
+  Olympia = 'OLYMPIA',
+}
+
 export type OpeningAddedEvent = BaseGraphQlObject & {
   __typename?: 'OpeningAddedEvent'
   id: Scalars['ID']
@@ -3769,6 +3874,9 @@ export type Query = {
   appliedOnOpeningEvents: Array<AppliedOnOpeningEvent>
   appliedOnOpeningEventByUniqueInput?: Maybe<AppliedOnOpeningEvent>
   appliedOnOpeningEventsConnection: AppliedOnOpeningEventConnection
+  blocks: Array<Block>
+  blockByUniqueInput?: Maybe<Block>
+  blocksConnection: BlockConnection
   budgetSetEvents: Array<BudgetSetEvent>
   budgetSetEventByUniqueInput?: Maybe<BudgetSetEvent>
   budgetSetEventsConnection: BudgetSetEventConnection
@@ -3984,6 +4092,26 @@ export type QueryAppliedOnOpeningEventsConnectionArgs = {
   orderBy?: Maybe<AppliedOnOpeningEventOrderByInput>
 }
 
+export type QueryBlocksArgs = {
+  offset?: Maybe<Scalars['Int']>
+  limit?: Maybe<Scalars['Int']>
+  where?: Maybe<BlockWhereInput>
+  orderBy?: Maybe<BlockOrderByInput>
+}
+
+export type QueryBlockByUniqueInputArgs = {
+  where: BlockWhereUniqueInput
+}
+
+export type QueryBlocksConnectionArgs = {
+  first?: Maybe<Scalars['Int']>
+  after?: Maybe<Scalars['String']>
+  last?: Maybe<Scalars['Int']>
+  before?: Maybe<Scalars['String']>
+  where?: Maybe<BlockWhereInput>
+  orderBy?: Maybe<BlockOrderByInput>
+}
+
 export type QueryBudgetSetEventsArgs = {
   offset?: Maybe<Scalars['Int']>
   limit?: Maybe<Scalars['Int']>
@@ -6088,8 +6216,8 @@ export type Worker = BaseGraphQlObject & {
   /** Current role stake (in JOY) */
   stake: Scalars['BigInt']
   payouts: Array<RewardPaidEvent>
-  /** Blocknumber of the block the worker was hired at */
-  hiredAtBlock: Scalars['Int']
+  hiredAtBlock: Block
+  hiredAtBlockId: Scalars['String']
   /** Time the worker was hired at */
   hiredAtTime: Scalars['DateTime']
   entry: OpeningFilledEvent
@@ -6130,7 +6258,7 @@ export type WorkerCreateInput = {
   status: Scalars['JSONObject']
   isLead: Scalars['Boolean']
   stake: Scalars['BigInt']
-  hiredAtBlock: Scalars['Float']
+  hiredAtBlockId: Scalars['ID']
   hiredAtTime: Scalars['DateTime']
   entryId: Scalars['ID']
   applicationId: Scalars['ID']
@@ -6261,8 +6389,8 @@ export enum WorkerOrderByInput {
   IsLeadDesc = 'isLead_DESC',
   StakeAsc = 'stake_ASC',
   StakeDesc = 'stake_DESC',
-  HiredAtBlockAsc = 'hiredAtBlock_ASC',
-  HiredAtBlockDesc = 'hiredAtBlock_DESC',
+  HiredAtBlockIdAsc = 'hiredAtBlockId_ASC',
+  HiredAtBlockIdDesc = 'hiredAtBlockId_DESC',
   HiredAtTimeAsc = 'hiredAtTime_ASC',
   HiredAtTimeDesc = 'hiredAtTime_DESC',
   EntryIdAsc = 'entryId_ASC',
@@ -6874,7 +7002,7 @@ export type WorkerUpdateInput = {
   status?: Maybe<Scalars['JSONObject']>
   isLead?: Maybe<Scalars['Boolean']>
   stake?: Maybe<Scalars['BigInt']>
-  hiredAtBlock?: Maybe<Scalars['Float']>
+  hiredAtBlockId?: Maybe<Scalars['ID']>
   hiredAtTime?: Maybe<Scalars['DateTime']>
   entryId?: Maybe<Scalars['ID']>
   applicationId?: Maybe<Scalars['ID']>
@@ -6940,12 +7068,8 @@ export type WorkerWhereInput = {
   stake_lt?: Maybe<Scalars['BigInt']>
   stake_lte?: Maybe<Scalars['BigInt']>
   stake_in?: Maybe<Array<Scalars['BigInt']>>
-  hiredAtBlock_eq?: Maybe<Scalars['Int']>
-  hiredAtBlock_gt?: Maybe<Scalars['Int']>
-  hiredAtBlock_gte?: Maybe<Scalars['Int']>
-  hiredAtBlock_lt?: Maybe<Scalars['Int']>
-  hiredAtBlock_lte?: Maybe<Scalars['Int']>
-  hiredAtBlock_in?: Maybe<Array<Scalars['Int']>>
+  hiredAtBlockId_eq?: Maybe<Scalars['ID']>
+  hiredAtBlockId_in?: Maybe<Array<Scalars['ID']>>
   hiredAtTime_eq?: Maybe<Scalars['DateTime']>
   hiredAtTime_lt?: Maybe<Scalars['DateTime']>
   hiredAtTime_lte?: Maybe<Scalars['DateTime']>
@@ -7037,8 +7161,8 @@ export type WorkingGroupApplication = BaseGraphQlObject & {
   answers: Array<ApplicationFormQuestionAnswer>
   /** Current application status */
   status: WorkingGroupApplicationStatus
-  /** Blocknumber of application creation block */
-  createdAtBlock: Scalars['Int']
+  createdAtBlock: Block
+  createdAtBlockId: Scalars['String']
   applicationwithdrawneventapplication?: Maybe<Array<ApplicationWithdrawnEvent>>
   appliedonopeningeventapplication?: Maybe<Array<AppliedOnOpeningEvent>>
   workerapplication?: Maybe<Array<Worker>>
@@ -7061,7 +7185,7 @@ export type WorkingGroupApplicationCreateInput = {
   rewardAccount: Scalars['String']
   stakingAccount: Scalars['String']
   status: Scalars['JSONObject']
-  createdAtBlock: Scalars['Float']
+  createdAtBlockId: Scalars['ID']
 }
 
 export type WorkingGroupApplicationEdge = {
@@ -7091,8 +7215,8 @@ export enum WorkingGroupApplicationOrderByInput {
   RewardAccountDesc = 'rewardAccount_DESC',
   StakingAccountAsc = 'stakingAccount_ASC',
   StakingAccountDesc = 'stakingAccount_DESC',
-  CreatedAtBlockAsc = 'createdAtBlock_ASC',
-  CreatedAtBlockDesc = 'createdAtBlock_DESC',
+  CreatedAtBlockIdAsc = 'createdAtBlockId_ASC',
+  CreatedAtBlockIdDesc = 'createdAtBlockId_DESC',
 }
 
 export type WorkingGroupApplicationStatus =
@@ -7112,7 +7236,7 @@ export type WorkingGroupApplicationUpdateInput = {
   rewardAccount?: Maybe<Scalars['String']>
   stakingAccount?: Maybe<Scalars['String']>
   status?: Maybe<Scalars['JSONObject']>
-  createdAtBlock?: Maybe<Scalars['Float']>
+  createdAtBlockId?: Maybe<Scalars['ID']>
 }
 
 export type WorkingGroupApplicationWhereInput = {
@@ -7172,12 +7296,8 @@ export type WorkingGroupApplicationWhereInput = {
   stakingAccount_endsWith?: Maybe<Scalars['String']>
   stakingAccount_in?: Maybe<Array<Scalars['String']>>
   status_json?: Maybe<Scalars['JSONObject']>
-  createdAtBlock_eq?: Maybe<Scalars['Int']>
-  createdAtBlock_gt?: Maybe<Scalars['Int']>
-  createdAtBlock_gte?: Maybe<Scalars['Int']>
-  createdAtBlock_lt?: Maybe<Scalars['Int']>
-  createdAtBlock_lte?: Maybe<Scalars['Int']>
-  createdAtBlock_in?: Maybe<Array<Scalars['Int']>>
+  createdAtBlockId_eq?: Maybe<Scalars['ID']>
+  createdAtBlockId_in?: Maybe<Array<Scalars['ID']>>
 }
 
 export type WorkingGroupApplicationWhereUniqueInput = {
@@ -7222,8 +7342,8 @@ export type WorkingGroupMetadata = BaseGraphQlObject & {
   about?: Maybe<Scalars['String']>
   /** Working group description text */
   description?: Maybe<Scalars['String']>
-  /** Blocknumber of the block at which status was set */
-  setAtBlock: Scalars['Int']
+  setAtBlock: Block
+  setAtBlockId: Scalars['String']
   /** The time at which status was set */
   setAtTime: Scalars['DateTime']
   statustextchangedeventmetadata?: Maybe<Array<StatusTextChangedEvent>>
@@ -7242,7 +7362,7 @@ export type WorkingGroupMetadataCreateInput = {
   message?: Maybe<Scalars['String']>
   about?: Maybe<Scalars['String']>
   description?: Maybe<Scalars['String']>
-  setAtBlock: Scalars['Float']
+  setAtBlockId: Scalars['ID']
   setAtTime: Scalars['DateTime']
 }
 
@@ -7267,8 +7387,8 @@ export enum WorkingGroupMetadataOrderByInput {
   AboutDesc = 'about_DESC',
   DescriptionAsc = 'description_ASC',
   DescriptionDesc = 'description_DESC',
-  SetAtBlockAsc = 'setAtBlock_ASC',
-  SetAtBlockDesc = 'setAtBlock_DESC',
+  SetAtBlockIdAsc = 'setAtBlockId_ASC',
+  SetAtBlockIdDesc = 'setAtBlockId_DESC',
   SetAtTimeAsc = 'setAtTime_ASC',
   SetAtTimeDesc = 'setAtTime_DESC',
 }
@@ -7278,7 +7398,7 @@ export type WorkingGroupMetadataUpdateInput = {
   message?: Maybe<Scalars['String']>
   about?: Maybe<Scalars['String']>
   description?: Maybe<Scalars['String']>
-  setAtBlock?: Maybe<Scalars['Float']>
+  setAtBlockId?: Maybe<Scalars['ID']>
   setAtTime?: Maybe<Scalars['DateTime']>
 }
 
@@ -7327,12 +7447,8 @@ export type WorkingGroupMetadataWhereInput = {
   description_startsWith?: Maybe<Scalars['String']>
   description_endsWith?: Maybe<Scalars['String']>
   description_in?: Maybe<Array<Scalars['String']>>
-  setAtBlock_eq?: Maybe<Scalars['Int']>
-  setAtBlock_gt?: Maybe<Scalars['Int']>
-  setAtBlock_gte?: Maybe<Scalars['Int']>
-  setAtBlock_lt?: Maybe<Scalars['Int']>
-  setAtBlock_lte?: Maybe<Scalars['Int']>
-  setAtBlock_in?: Maybe<Array<Scalars['Int']>>
+  setAtBlockId_eq?: Maybe<Scalars['ID']>
+  setAtBlockId_in?: Maybe<Array<Scalars['ID']>>
   setAtTime_eq?: Maybe<Scalars['DateTime']>
   setAtTime_lt?: Maybe<Scalars['DateTime']>
   setAtTime_lte?: Maybe<Scalars['DateTime']>
@@ -7372,8 +7488,8 @@ export type WorkingGroupOpening = BaseGraphQlObject & {
   unstakingPeriod: Scalars['Int']
   /** Initial workers' reward per block */
   rewardPerBlock: Scalars['BigInt']
-  /** Blocknumber of opening creation block */
-  createdAtBlock: Scalars['Int']
+  createdAtBlock: Block
+  createdAtBlockId: Scalars['String']
   appliedonopeningeventopening?: Maybe<Array<AppliedOnOpeningEvent>>
   openingaddedeventopening?: Maybe<Array<OpeningAddedEvent>>
   openingcanceledeventopening?: Maybe<Array<OpeningCanceledEvent>>
@@ -7397,7 +7513,7 @@ export type WorkingGroupOpeningCreateInput = {
   stakeAmount: Scalars['BigInt']
   unstakingPeriod: Scalars['Float']
   rewardPerBlock: Scalars['BigInt']
-  createdAtBlock: Scalars['Float']
+  createdAtBlockId: Scalars['ID']
 }
 
 export type WorkingGroupOpeningEdge = {
@@ -7416,14 +7532,16 @@ export type WorkingGroupOpeningMetadata = BaseGraphQlObject & {
   deletedAt?: Maybe<Scalars['DateTime']>
   deletedById?: Maybe<Scalars['String']>
   version: Scalars['Int']
+  /** Whether the originally provided metadata was valid */
+  originallyValid: Scalars['Boolean']
   /** Opening short description */
   shortDescription: Scalars['String']
   /** Opening description (md-formatted) */
   description: Scalars['String']
   /** Expected max. number of applicants that will be hired */
-  hiringLimit: Scalars['Int']
+  hiringLimit?: Maybe<Scalars['Int']>
   /** Expected time when the opening will close */
-  expectedEnding: Scalars['DateTime']
+  expectedEnding?: Maybe<Scalars['DateTime']>
   /** Md-formatted text explaining the application process */
   applicationDetails: Scalars['String']
   applicationFormQuestions: Array<ApplicationFormQuestion>
@@ -7438,10 +7556,11 @@ export type WorkingGroupOpeningMetadataConnection = {
 }
 
 export type WorkingGroupOpeningMetadataCreateInput = {
+  originallyValid: Scalars['Boolean']
   shortDescription: Scalars['String']
   description: Scalars['String']
-  hiringLimit: Scalars['Float']
-  expectedEnding: Scalars['DateTime']
+  hiringLimit?: Maybe<Scalars['Float']>
+  expectedEnding?: Maybe<Scalars['DateTime']>
   applicationDetails: Scalars['String']
 }
 
@@ -7458,6 +7577,8 @@ export enum WorkingGroupOpeningMetadataOrderByInput {
   UpdatedAtDesc = 'updatedAt_DESC',
   DeletedAtAsc = 'deletedAt_ASC',
   DeletedAtDesc = 'deletedAt_DESC',
+  OriginallyValidAsc = 'originallyValid_ASC',
+  OriginallyValidDesc = 'originallyValid_DESC',
   ShortDescriptionAsc = 'shortDescription_ASC',
   ShortDescriptionDesc = 'shortDescription_DESC',
   DescriptionAsc = 'description_ASC',
@@ -7471,6 +7592,7 @@ export enum WorkingGroupOpeningMetadataOrderByInput {
 }
 
 export type WorkingGroupOpeningMetadataUpdateInput = {
+  originallyValid?: Maybe<Scalars['Boolean']>
   shortDescription?: Maybe<Scalars['String']>
   description?: Maybe<Scalars['String']>
   hiringLimit?: Maybe<Scalars['Float']>
@@ -7503,6 +7625,8 @@ export type WorkingGroupOpeningMetadataWhereInput = {
   deletedAt_gte?: Maybe<Scalars['DateTime']>
   deletedById_eq?: Maybe<Scalars['ID']>
   deletedById_in?: Maybe<Array<Scalars['ID']>>
+  originallyValid_eq?: Maybe<Scalars['Boolean']>
+  originallyValid_in?: Maybe<Array<Scalars['Boolean']>>
   shortDescription_eq?: Maybe<Scalars['String']>
   shortDescription_contains?: Maybe<Scalars['String']>
   shortDescription_startsWith?: Maybe<Scalars['String']>
@@ -7556,8 +7680,8 @@ export enum WorkingGroupOpeningOrderByInput {
   UnstakingPeriodDesc = 'unstakingPeriod_DESC',
   RewardPerBlockAsc = 'rewardPerBlock_ASC',
   RewardPerBlockDesc = 'rewardPerBlock_DESC',
-  CreatedAtBlockAsc = 'createdAtBlock_ASC',
-  CreatedAtBlockDesc = 'createdAtBlock_DESC',
+  CreatedAtBlockIdAsc = 'createdAtBlockId_ASC',
+  CreatedAtBlockIdDesc = 'createdAtBlockId_DESC',
 }
 
 export type WorkingGroupOpeningStatus = OpeningStatusOpen | OpeningStatusFilled | OpeningStatusCancelled
@@ -7577,7 +7701,7 @@ export type WorkingGroupOpeningUpdateInput = {
   stakeAmount?: Maybe<Scalars['BigInt']>
   unstakingPeriod?: Maybe<Scalars['Float']>
   rewardPerBlock?: Maybe<Scalars['BigInt']>
-  createdAtBlock?: Maybe<Scalars['Float']>
+  createdAtBlockId?: Maybe<Scalars['ID']>
 }
 
 export type WorkingGroupOpeningWhereInput = {
@@ -7636,12 +7760,8 @@ export type WorkingGroupOpeningWhereInput = {
   rewardPerBlock_lt?: Maybe<Scalars['BigInt']>
   rewardPerBlock_lte?: Maybe<Scalars['BigInt']>
   rewardPerBlock_in?: Maybe<Array<Scalars['BigInt']>>
-  createdAtBlock_eq?: Maybe<Scalars['Int']>
-  createdAtBlock_gt?: Maybe<Scalars['Int']>
-  createdAtBlock_gte?: Maybe<Scalars['Int']>
-  createdAtBlock_lt?: Maybe<Scalars['Int']>
-  createdAtBlock_lte?: Maybe<Scalars['Int']>
-  createdAtBlock_in?: Maybe<Array<Scalars['Int']>>
+  createdAtBlockId_eq?: Maybe<Scalars['ID']>
+  createdAtBlockId_in?: Maybe<Array<Scalars['ID']>>
 }
 
 export type WorkingGroupOpeningWhereUniqueInput = {

+ 16 - 10
tests/integration-tests/src/fixtures/membershipModule.ts

@@ -64,7 +64,9 @@ abstract class MembershipFixture extends BaseFixture {
 
   findMatchingQueryNodeEvent<T extends AnyQueryNodeEvent>(eventToFind: EventDetails, queryNodeEvents: T[]) {
     const { blockNumber, indexInBlock } = eventToFind
-    const qEvent = queryNodeEvents.find((e) => e.event.inBlock === blockNumber && e.event.indexInBlock === indexInBlock)
+    const qEvent = queryNodeEvents.find(
+      (e) => e.event.inBlock.number === blockNumber && e.event.indexInBlock === indexInBlock
+    )
     if (!qEvent) {
       throw new Error(`Could not find matching query-node event (expected ${blockNumber}:${indexInBlock})!`)
     }
@@ -126,7 +128,7 @@ export class BuyMembershipHappyCaseFixture extends MembershipFixture implements
     const [qEvent] = qEvents
     const txParams = this.generateParamsFromAccountId(account)
     const metadata = MembershipMetadata.deserializeBinary(txParams.metadata.toU8a(true))
-    assert.equal(qEvent.event.inBlock, eventDetails.blockNumber)
+    assert.equal(qEvent.event.inBlock.number, eventDetails.blockNumber)
     assert.equal(qEvent.event.inExtrinsic, txHash)
     assert.equal(qEvent.event.indexInBlock, eventDetails.indexInBlock)
     assert.equal(qEvent.event.type, EventType.MembershipBought)
@@ -435,7 +437,7 @@ export class InviteMembersHappyCaseFixture extends MembershipFixture {
     const [qEvent] = qEvents
     const txParams = this.generateParamsFromAccountId(account)
     const metadata = MembershipMetadata.deserializeBinary(txParams.metadata.toU8a(true))
-    assert.equal(qEvent.event.inBlock, eventDetails.blockNumber)
+    assert.equal(qEvent.event.inBlock.number, eventDetails.blockNumber)
     assert.equal(qEvent.event.inExtrinsic, txHash)
     assert.equal(qEvent.event.indexInBlock, eventDetails.indexInBlock)
     assert.equal(qEvent.event.type, EventType.MemberInvited)
@@ -805,8 +807,8 @@ export class SudoUpdateMembershipSystem extends MembershipFixture {
   }
 
   private async assertBeforeSnapshotIsValid(beforeSnapshot: MembershipSystemSnapshot) {
-    assert.isNumber(beforeSnapshot.snapshotBlock)
-    const chainValues = await this.getMembershipSystemValuesAt(beforeSnapshot.snapshotBlock)
+    assert.isNumber(beforeSnapshot.snapshotBlock.number)
+    const chainValues = await this.getMembershipSystemValuesAt(beforeSnapshot.snapshotBlock.number)
     assert.equal(beforeSnapshot.referralCut, chainValues.referralCut)
     assert.equal(beforeSnapshot.invitedInitialBalance, chainValues.invitedInitialBalance.toString())
     assert.equal(beforeSnapshot.membershipPrice, chainValues.membershipPrice.toString())
@@ -871,19 +873,23 @@ export class SudoUpdateMembershipSystem extends MembershipFixture {
   async runQueryNodeChecks(): Promise<void> {
     await super.runQueryNodeChecks()
     const { events, extrinsics, eventNames } = this
-    const beforeSnapshotMaxBlockNumber = Math.min(...events.map((e) => e.blockNumber)) - 1
-    const afterSnapshotBlockNumber = Math.max(...events.map((e) => e.blockNumber))
+    const beforeSnapshotMaxBlockTimestamp = (
+      await this.api.query.timestamp.now.at(
+        await this.api.getBlockHash(Math.min(...events.map((e) => e.blockNumber)) - 1)
+      )
+    ).toNumber()
+    const afterSnapshotBlockTimestamp = Math.max(...events.map((e) => e.blockTimestamp))
 
     // Fetch "afterSnapshot" first to make sure query node has progressed enough
     const afterSnapshot = (await this.query.tryQueryWithTimeout(
-      () => this.query.getMembershipSystemSnapshot(afterSnapshotBlockNumber),
+      () => this.query.getMembershipSystemSnapshot(afterSnapshotBlockTimestamp),
       (snapshot) => assert.isOk(snapshot)
     )) as MembershipSystemSnapshot
 
-    const beforeSnapshot = await this.query.getMembershipSystemSnapshot(beforeSnapshotMaxBlockNumber, 'lte')
+    const beforeSnapshot = await this.query.getMembershipSystemSnapshot(beforeSnapshotMaxBlockTimestamp, 'lte')
 
     if (!beforeSnapshot) {
-      throw new Error(`MembershipSystemSnapshot before block ${beforeSnapshotMaxBlockNumber} not found!`)
+      throw new Error(`MembershipSystemSnapshot before timestamp ${beforeSnapshotMaxBlockTimestamp} not found!`)
     }
 
     // Validate snapshots

+ 3 - 3
tests/integration-tests/src/fixtures/workingGroupsModule.ts

@@ -133,7 +133,7 @@ export class CreateOpeningFixture extends BaseFixture {
       throw new Error('Query node: Opening not found')
     }
     assert.equal(qOpening.runtimeId, eventDetails.openingId.toNumber())
-    assert.equal(qOpening.createdAtBlock, eventDetails.blockNumber)
+    assert.equal(qOpening.createdAtBlock.number, eventDetails.blockNumber)
     assert.equal(qOpening.group.name, this.group)
     assert.equal(qOpening.rewardPerBlock, this.openingParams.reward.toString())
     assert.equal(qOpening.type, this.asSudo ? WorkingGroupOpeningType.Leader : WorkingGroupOpeningType.Regular)
@@ -268,7 +268,7 @@ export class ApplyOnOpeningHappyCaseFixture extends BaseFixture {
       throw new Error('Application not found')
     }
     assert.equal(qApplication.runtimeId, eventDetails.applicationId.toNumber())
-    assert.equal(qApplication.createdAtBlock, eventDetails.blockNumber)
+    assert.equal(qApplication.createdAtBlock.number, eventDetails.blockNumber)
     assert.equal(qApplication.opening.runtimeId, this.openingId.toNumber())
     assert.equal(qApplication.applicant.id, this.applicant.memberId.toString())
     assert.equal(qApplication.roleAccount, this.applicant.account)
@@ -443,7 +443,7 @@ export class SudoFillLeadOpeningFixture extends BaseFixture {
     assert.equal(qWorker.status.__typename, 'WorkerStatusActive')
     assert.equal(qWorker.isLead, true)
     assert.equal(qWorker.stake, applicationStake.toString())
-    assert.equal(qWorker.hiredAtBlock, eventDetails.blockNumber)
+    assert.equal(qWorker.hiredAtBlock.number, eventDetails.blockNumber)
     assert.equal(qWorker.application.runtimeId, applicationId.toNumber())
   }
 

+ 2 - 0
tests/integration-tests/src/types.ts

@@ -14,6 +14,8 @@ export type AnyQueryNodeEvent = { event: GenericEventData }
 export interface EventDetails {
   event: Event
   blockNumber: number
+  blockTimestamp: number
+  blockHash: string
   indexInBlock: number
 }