Browse Source

query node - council & referndum input schema + generic event mappings

ondratra 3 years ago
parent
commit
5a7ecda7b2

+ 74 - 0
query-node/manifest.yml

@@ -110,6 +110,31 @@ typegen:
     - data_directory.ContentAccepted
     - data_directory.ContentRejected
     - data_directory.ContentUploadingStatusUpdated
+    # Council
+    - council.AnnouncingPeriodStarted
+    - council.NotEnoughCandidates
+    - council.VotingPeriodStarted
+    - council.NewCandidate
+    - council.NewCouncilElected
+    - council.NewCouncilNotElected
+    - council.CandidacyStakeRelease
+    - council.CandidacyWithdraw
+    - council.CandidacyNoteSet
+    - council.RewardPayment
+    - council.BudgetBalanceSet
+    - council.BudgetRefill
+    - council.BudgetRefillPlanned
+    - council.BudgetIncrementUpdated
+    - council.CouncilorRewardUpdated
+    - council.RequestFunded
+    # Referendum
+    - referendum.ReferendumStarted
+    - referendum.ReferendumStartedForcefully
+    - referendum.RevealingStageStarted
+    - referendum.ReferendumFinished
+    - referendum.VoteCast
+    - referendum.VoteRevealed
+    - referendum.StakeReleased
   calls:
     # Memberships
     - members.updateProfile
@@ -573,6 +598,55 @@ mappings:
     # not handled at the moment
     #- event: dataDirectory.ContentUploadingStatusUpdated
     #  handler: data_directory_ContentUploadingStatusUpdated
+
+    # Council
+    - event: council.AnnouncingPeriodStarted
+      handler: council_AnnouncingPeriodStarted
+    - event: council.NotEnoughCandidates
+      handler: council_NotEnoughCandidates
+    - event: council.VotingPeriodStarted
+      handler: council_VotingPeriodStarted
+    - event: council.NewCandidate
+      handler: council_NewCandidate
+    - event: council.NewCouncilElected
+      handler: council_NewCouncilElected
+    - event: council.NewCouncilNotElected
+      handler: council_NewCouncilNotElected
+    - event: council.CandidacyStakeRelease
+      handler: council_CandidacyStakeRelease
+    - event: council.CandidacyWithdraw
+      handler: council_CandidacyWithdraw
+    - event: council.CandidacyNoteSet
+      handler: council_CandidacyNoteSet
+    - event: council.RewardPayment
+      handler: council_RewardPayment
+    - event: council.BudgetBalanceSet
+      handler: council_BudgetBalanceSet
+    - event: council.BudgetRefill
+      handler: council_BudgetRefill
+    - event: council.BudgetRefillPlanned
+      handler: council_BudgetRefillPlanned
+    - event: council.BudgetIncrementUpdated
+      handler: council_BudgetIncrementUpdated
+    - event: council.CouncilorRewardUpdated
+      handler: council_CouncilorRewardUpdated
+    - event: council.RequestFunded
+      handler: council_RequestFunded
+    # Referendum
+    - event: referendum.ReferendumStarted
+      handler: referendum_ReferendumStarted
+    - event: referendum.ReferendumStartedForcefully
+      handler: referendum_ReferendumStartedForcefully
+    - event: referendum.RevealingStageStarted
+      handler: referendum_RevealingStageStarted
+    - event: referendum.ReferendumFinished
+      handler: referendum_ReferendumFinished
+    - event: referendum.VoteCast
+      handler: referendum_VoteCast
+    - event: referendum.VoteRevealed
+      handler: referendum_VoteRevealed
+    - event: referendum.StakeReleased
+      handler: referendum_StakeReleased
   extrinsicHandlers:
     # infer defaults here
     #- extrinsic: Balances.Transfer

+ 318 - 0
query-node/mappings/council.ts

@@ -0,0 +1,318 @@
+import { EventContext, StoreContext, DatabaseManager } from '@dzlzv/hydra-common'
+import { bytesToString, genericEventFields } from './common'
+
+import {
+  // Council events
+  AnnouncingPeriodStartedEvent,
+  NotEnoughCandidatesEvent,
+  VotingPeriodStartedEvent,
+  NewCandidateEvent,
+  NewCouncilElectedEvent,
+  NewCouncilNotElectedEvent,
+  CandidacyStakeReleaseEvent,
+  CandidacyWithdrawEvent,
+  CandidacyNoteSetEvent,
+  RewardPaymentEvent,
+  BudgetBalanceSetEvent,
+  BudgetRefillEvent,
+  BudgetRefillPlannedEvent,
+  BudgetIncrementUpdatedEvent,
+  CouncilorRewardUpdatedEvent,
+  RequestFundedEvent,
+
+  // Referendum events
+  ReferendumStartedEvent,
+  ReferendumStartedForcefullyEvent,
+  RevealingStageStartedEvent,
+  ReferendumFinishedEvent,
+  VoteCastEvent,
+  VoteRevealedEvent,
+  StakeReleasedEvent,
+
+  // Council & referendum structures
+  ReferendumStageRevealingOptionResult,
+
+  // Misc
+  Membership,
+} from 'query-node/dist/model'
+import { Council, Referendum } from './generated/types'
+
+/////////////////// Council events /////////////////////////////////////////////
+
+export async function council_AnnouncingPeriodStartedEvent({
+  event,
+  store,
+}: EventContext & StoreContext): Promise<void> {
+  const [] = new Council.AnnouncingPeriodStartedEvent(event).params
+
+  const announcingPeriodStartedEvent = new AnnouncingPeriodStartedEvent({
+    ...genericEventFields(event),
+  })
+
+  await store.save<AnnouncingPeriodStartedEvent>(announcingPeriodStartedEvent)
+}
+export async function council_NotEnoughCandidatesEvent({ event, store }: EventContext & StoreContext): Promise<void> {
+  const [] = new Council.NotEnoughCandidatesEvent(event).params
+
+  const notEnoughCandidatesEvent = new NotEnoughCandidatesEvent({
+    ...genericEventFields(event),
+  })
+
+  await store.save<NotEnoughCandidatesEvent>(notEnoughCandidatesEvent)
+}
+export async function council_VotingPeriodStartedEvent({ event, store }: EventContext & StoreContext): Promise<void> {
+  const [numOfCandidates] = new Council.VotingPeriodStartedEvent(event).params
+
+  const votingPeriodStartedEvent = new VotingPeriodStartedEvent({
+    ...genericEventFields(event),
+    numOfCandidates,
+  })
+
+  await store.save<VotingPeriodStartedEvent>(votingPeriodStartedEvent)
+}
+export async function council_NewCandidateEvent({ event, store }: EventContext & StoreContext): Promise<void> {
+  const [memberId, stakingAccount, rewardAccount, balance] = new Council.NewCandidateEvent(event).params
+  const member = new Membership({ id: memberId.toString() })
+
+  const newCandidateEvent = new NewCandidateEvent({
+    ...genericEventFields(event),
+    member,
+    stakingAccount: stakingAccount.toString(),
+    rewardAccount: rewardAccount.toString(),
+    balance,
+  })
+
+  await store.save<NewCandidateEvent>(newCandidateEvent)
+}
+export async function council_NewCouncilElectedEvent({ event, store }: EventContext & StoreContext): Promise<void> {
+  const [memberIds] = new Council.NewCouncilElectedEvent(event).params
+  const members = await store.getMany(Membership, { where: { id_in: memberIds.map((item) => item.toString()) } })
+
+  const newCouncilElectedEvent = new NewCouncilElectedEvent({
+    ...genericEventFields(event),
+    electedMembers: members,
+  })
+
+  await store.save<NewCouncilElectedEvent>(newCouncilElectedEvent)
+}
+export async function council_NewCouncilNotElectedEvent({ event, store }: EventContext & StoreContext): Promise<void> {
+  const [] = new Council.NewCouncilNotElectedEvent(event).params
+
+  const newCouncilNotElectedEvent = new NewCouncilNotElectedEvent({
+    ...genericEventFields(event),
+  })
+
+  await store.save<NewCouncilNotElectedEvent>(newCouncilNotElectedEvent)
+}
+export async function council_CandidacyStakeReleaseEvent({ event, store }: EventContext & StoreContext): Promise<void> {
+  const [memberId] = new Council.CandidacyStakeReleaseEvent(event).params
+  const member = new Membership({ id: memberId.toString() })
+
+  const candidacyStakeReleaseEvent = new CandidacyStakeReleaseEvent({
+    ...genericEventFields(event),
+    member,
+  })
+
+  await store.save<CandidacyStakeReleaseEvent>(candidacyStakeReleaseEvent)
+}
+export async function council_CandidacyWithdrawEvent({ event, store }: EventContext & StoreContext): Promise<void> {
+  const [memberId] = new Council.CandidacyWithdrawEvent(event).params
+  const member = new Membership({ id: memberId.toString() })
+
+  const candidacyWithdrawEvent = new CandidacyWithdrawEvent({
+    ...genericEventFields(event),
+    member,
+  })
+
+  await store.save<CandidacyWithdrawEvent>(candidacyWithdrawEvent)
+}
+export async function council_CandidacyNoteSetEvent({ event, store }: EventContext & StoreContext): Promise<void> {
+  const [memberId, note] = new Council.CandidacyNoteSetEvent(event).params
+  const member = new Membership({ id: memberId.toString() })
+
+  const candidacyNoteSetEvent = new CandidacyNoteSetEvent({
+    ...genericEventFields(event),
+    member,
+    note: bytesToString(note),
+  })
+
+  await store.save<CandidacyNoteSetEvent>(candidacyNoteSetEvent)
+}
+export async function council_RewardPaymentEvent({ event, store }: EventContext & StoreContext): Promise<void> {
+  const [memberId, rewardAccount, paidBalance, missingBalance] = new Council.RewardPaymentEvent(event).params
+  const member = new Membership({ id: memberId.toString() })
+
+  const rewardPaymentEvent = new RewardPaymentEvent({
+    ...genericEventFields(event),
+    member,
+    rewardAccount: rewardAccount.toString(),
+    paidBalance,
+    missingBalance,
+  })
+
+  await store.save<RewardPaymentEvent>(rewardPaymentEvent)
+}
+export async function council_BudgetBalanceSetEvent({ event, store }: EventContext & StoreContext): Promise<void> {
+  const [balance] = new Council.BudgetBalanceSetEvent(event).params
+
+  const budgetBalanceSetEvent = new BudgetBalanceSetEvent({
+    ...genericEventFields(event),
+    balance,
+  })
+
+  await store.save<BudgetBalanceSetEvent>(budgetBalanceSetEvent)
+}
+export async function council_BudgetRefillEvent({ event, store }: EventContext & StoreContext): Promise<void> {
+  const [balance] = new Council.BudgetRefillEvent(event).params
+
+  const budgetRefillEvent = new BudgetRefillEvent({
+    ...genericEventFields(event),
+    balance,
+  })
+
+  await store.save<BudgetRefillEvent>(budgetRefillEvent)
+}
+export async function council_BudgetRefillPlannedEvent({ event, store }: EventContext & StoreContext): Promise<void> {
+  const [] = new Council.BudgetRefillPlannedEvent(event).params
+
+  const budgetRefillPlannedEvent = new BudgetRefillPlannedEvent({
+    ...genericEventFields(event),
+  })
+
+  await store.save<BudgetRefillPlannedEvent>(budgetRefillPlannedEvent)
+}
+export async function council_BudgetIncrementUpdatedEvent({
+  event,
+  store,
+}: EventContext & StoreContext): Promise<void> {
+  const [amount] = new Council.BudgetIncrementUpdatedEvent(event).params
+
+  const budgetIncrementUpdatedEvent = new BudgetIncrementUpdatedEvent({
+    ...genericEventFields(event),
+    amount,
+  })
+
+  await store.save<BudgetIncrementUpdatedEvent>(budgetIncrementUpdatedEvent)
+}
+export async function council_CouncilorRewardUpdatedEvent({
+  event,
+  store,
+}: EventContext & StoreContext): Promise<void> {
+  const [rewardAmount] = new Council.CouncilorRewardUpdatedEvent(event).params
+
+  const councilorRewardUpdatedEvent = new CouncilorRewardUpdatedEvent({
+    ...genericEventFields(event),
+    rewardAmount,
+  })
+
+  await store.save<CouncilorRewardUpdatedEvent>(councilorRewardUpdatedEvent)
+}
+export async function council_RequestFundedEvent({ event, store }: EventContext & StoreContext): Promise<void> {
+  const [account, amount] = new Council.RequestFundedEvent(event).params
+
+  const requestFundedEvent = new RequestFundedEvent({
+    ...genericEventFields(event),
+    account: account.toString(),
+    amount,
+  })
+
+  await store.save<RequestFundedEvent>(requestFundedEvent)
+}
+
+/////////////////// Referendum events //////////////////////////////////////////
+
+export async function referendum_ReferendumStartedEvent({ event, store }: EventContext & StoreContext): Promise<void> {
+  const [] = new Referendum.ReferendumStartedEvent(event).params
+
+  const referendumStartedEvent = new ReferendumStartedEvent({
+    ...genericEventFields(event),
+  })
+
+  await store.save<ReferendumStartedEvent>(referendumStartedEvent)
+}
+
+export async function referendum_ReferendumStartedForcefullyEvent({
+  event,
+  store,
+}: EventContext & StoreContext): Promise<void> {
+  const [winningTargetCount] = new Referendum.ReferendumStartedForcefullyEvent(event).params
+
+  const referendumStartedForcefullyEvent = new ReferendumStartedForcefullyEvent({
+    ...genericEventFields(event),
+    winningTargetCount,
+  })
+
+  await store.save<ReferendumStartedForcefullyEvent>(referendumStartedForcefullyEvent)
+}
+
+export async function referendum_RevealingStageStartedEvent({
+  event,
+  store,
+}: EventContext & StoreContext): Promise<void> {
+  const [] = new Referendum.RevealingStageStartedEvent(event).params
+
+  const revealingStageStartedEvent = new RevealingStageStartedEvent({
+    ...genericEventFields(event),
+  })
+
+  await store.save<RevealingStageStartedEvent>(revealingStageStartedEvent)
+}
+
+export async function referendum_ReferendumFinishedEvent({ event, store }: EventContext & StoreContext): Promise<void> {
+  const [optionResultsRaw] = new Referendum.ReferendumFinishedEvent(event).params
+
+  const members = await store.getMany(Membership, {
+    where: { id_in: optionResultsRaw.map((item) => item.option_id.toString()) },
+  })
+
+  const referendumFinishedEvent = new ReferendumFinishedEvent({
+    ...genericEventFields(event),
+    optionResults: optionResultsRaw.map(
+      (item, index) =>
+        new ReferendumStageRevealingOptionResult({
+          votePower: item.vote_power,
+          optionId: members[index],
+        })
+    ),
+  })
+
+  await store.save<ReferendumFinishedEvent>(referendumFinishedEvent)
+}
+
+export async function referendum_VoteCastEvent({ event, store }: EventContext & StoreContext): Promise<void> {
+  const [account, hash, votePower] = new Referendum.VoteCastEvent(event).params
+
+  const voteCastEvent = new VoteCastEvent({
+    ...genericEventFields(event),
+    account: account.toString(),
+    hash: bytesToString(hash),
+    votePower,
+  })
+
+  await store.save<VoteCastEvent>(voteCastEvent)
+}
+
+export async function referendum_VoteRevealedEvent({ event, store }: EventContext & StoreContext): Promise<void> {
+  const [account, memberId, salt] = new Referendum.VoteRevealedEvent(event).params
+  const member = new Membership({ id: memberId.toString() })
+
+  const voteRevealedEvent = new VoteRevealedEvent({
+    ...genericEventFields(event),
+    account: account.toString(),
+    member: member,
+    salt: bytesToString(salt),
+  })
+
+  await store.save<VoteRevealedEvent>(voteRevealedEvent)
+}
+
+export async function referendum_StakeReleasedEvent({ event, store }: EventContext & StoreContext): Promise<void> {
+  const [stakingAccount] = new Referendum.StakeReleasedEvent(event).params
+
+  const stakeReleasedEvent = new StakeReleasedEvent({
+    ...genericEventFields(event),
+    stakingAccount: stakingAccount.toString(),
+  })
+
+  await store.save<StakeReleasedEvent>(stakeReleasedEvent)
+}

+ 224 - 0
query-node/schemas/council.graphql

@@ -0,0 +1,224 @@
+# TODO:
+# - do we need some fulltext search for council/election?
+
+# workaround for https://github.com/Joystream/hydra/issues/434
+type VariantNone @variant {
+  _phantom: Int
+}
+
+################### Council ####################################################
+
+type CouncilStageUpdate @entity {
+  "The new stage council got into."
+  stage: CouncilStage!
+
+  "Block number at which change happened."
+  changedAt: BigInt!
+
+  "Council elected in this stage update if any."
+  electedCouncil: ElectedCouncil
+}
+
+type CouncilStageAnnouncing @variant {
+  "Number of candidates aspiring to be elected as council members."
+  candidatesCount: BigInt!
+}
+
+type CouncilStageElection @variant {
+  "Number of candidates aspiring to be elected as council members."
+  candidatesCount: BigInt!
+}
+
+type CouncilStageIdle @variant {
+  # no properties
+
+  # TODO: remove me - variant needs to have at least 1 property now
+  dummy: Int
+}
+
+union CouncilStage = CouncilStageAnnouncing | CouncilStageElection | CouncilStageIdle | VariantNone
+
+type Candidate @entity {
+  "Account used for staking currency needed for the candidacy."
+  stakingAccountId: String!
+
+  "Account that will receive rewards if candidate's elected to the council."
+  rewardAccountId: String!
+
+  "Election cycle"
+  cycleId: ElectionRound!
+  stake: BigInt!
+  votePower: BigInt!
+  note: String!
+}
+
+type CouncilMember @entity {
+  "Runtime council member id"
+  id: ID!
+
+  "Account used for staking currency for council membership."
+  stakingAccountId: String!
+
+  "Account that will receive used for reward currency for council membership."
+  rewardAccountId: String!
+
+  "Council member's membership."
+  member: Membership!
+
+  "Stake used for the council membership."
+  stake: BigInt!
+
+  "Block number in which council member recieved the last reward payment."
+  lastPaymentBlock: BigInt!
+
+  "Reward amount that should have been paid but couldn't be paid off due to insufficient budget."
+  unpaidReward: BigInt!
+
+  electedInCouncil: ElectedCouncil!
+}
+
+################### Referendum #################################################
+
+type ReferendumStageInactive @variant {
+  # no properties
+
+  # TODO: remove me - variant needs to have at least 1 property now
+  dummy: Int
+}
+
+type ReferendumStageVoting @variant {
+  "Block in which referendum started."
+  started: BigInt!
+
+  "Target number of winners."
+  winningTargetCount: BigInt!
+
+  "Index of current election"
+  cycleId: ElectionRound!
+}
+
+type ReferendumStageRevealing @variant {
+  "Block in which referendum started"
+  started: BigInt!
+
+  "Target number of winners"
+  winningTargetCount: BigInt!
+
+  "Intermediate winning options"
+  intermediateWinners: [ReferendumStageRevealingOptionResult!]
+
+  "Index of current election"
+  cycleId: ElectionRound!
+}
+
+type ReferendumStageRevealingOptionResult @entity {
+  "Member that received votes."
+  optionId: Membership!
+
+  "Sum of votes' power received."
+  votePower: BigInt!
+
+  # TODO: reference variant (how?)
+  #referendumRevealingStages: [ReferendumStageRevealing!] @derivedFrom(field: "intermediateWinners")
+
+  "Election round."
+  cycleId: ElectionRound!
+
+  "Event that concluded the referendum."
+  referendumFinishedEvent: ReferendumFinishedEvent!
+}
+
+# TODO: this maybe needs to be @entity - so it's saved in db
+union ReferendumStage = ReferendumStageInactive | ReferendumStageVoting | ReferendumStageRevealing | VariantNone
+
+type CastVote @entity {
+  "Hashed vote that was casted before being revealed."
+  commitment: String!
+
+  "Election round."
+  cycleId: ElectionRound!
+
+  "Stake used to back up the vote."
+  stake: BigInt!
+
+  "Account that cast the vote."
+  castBy: String!
+
+  "Member receiving the vote."
+  voteFor: Membership
+}
+
+################### Derived ####################################################
+
+type ElectedCouncil @entity {
+  "Members that were elected to the council."
+  councilMembers: [CouncilMember!] @derivedFrom(field: "electedInCouncil")
+
+  "Changes to council status that were made during it's reign."
+  updates: [CouncilStageUpdate!] @derivedFrom(field: "electedCouncil")
+
+  "Block number at which the council was elected."
+  electedAtBlock: Int!
+
+  "Block number at which the council reign ended and a new council was elected."
+  endedAtBlock: Int
+
+  "Elections held before the council was rightfully elected."
+  councilElections: [ElectionRound!] @derivedFrom(field: "electedCouncil")
+
+  "Elections held before the next council was or will be rightfully elected."
+  nextCouncilElections: [ElectionRound!] @derivedFrom(field: "nextElectedCouncil")
+
+  # TODO: make this nullable to represent that council is already resigned https://github.com/Joystream/hydra/issues/434
+  stage: ReferendumStage!
+}
+
+type ElectionRound @entity {
+  "Election cycle ID."
+  cycleId: Int!
+
+  "Sign if election has already finished."
+  isFinished: Boolean!
+
+  "Vote cast in the election round."
+  castVotes: [CastVote!] @derivedFrom(field: "cycleId")
+
+  # TODO: reference variant (how?)
+  #referendumStageVoting: ReferendumStage @derivedFrom(field: "cycleId")
+  #referendumStageRevealing: ReferendumStage @derivedFrom(field: "cycleId")
+
+  "Council that is ruling during the election."
+  electedCouncil: ElectedCouncil!
+
+  "Council that was elected in this election round."
+  nextElectedCouncil: ElectedCouncil
+
+  "Candidates in this election round."
+  candidates: [Candidate!] @derivedFrom(field: "cycleId")
+}
+
+# TODO:
+# - is it useful as defined below?
+# - do we need to add records for BudgetRefill, BudgetIncrement, etc. or is it ok to read this info from councilEvents?
+
+type Budget @entity {
+  "Block number at which the next rewards will be paid."
+  nextRewardPaymentsAt: BigInt!
+}
+
+type BudgetPayment @entity {
+  "Block number at which the payment was done."
+  paidAtBlock: Int!
+
+  "Member that was paid."
+  member: Membership!
+
+  "Account that received the payment"
+  account: String!
+
+  "Amount that was paid."
+  amount: BigInt!
+
+  "Amount that couldn't be paid due to insufficient council budget's balance."
+  unpaidAmount: BigInt!
+}

+ 576 - 0
query-node/schemas/councilEvents.graphql

@@ -0,0 +1,576 @@
+################### Council ####################################################
+
+type AnnouncingPeriodStartedEvent @entity {
+  ### GENERIC DATA ###
+
+  "(network}-{blockNumber}-{indexInBlock}"
+  id: ID!
+
+  "Hash of the extrinsic which caused the event to be emitted"
+  inExtrinsic: String
+
+  "Blocknumber of the block in which the event was emitted."
+  inBlock: Int!
+
+  "Network the block was produced in"
+  network: Network!
+
+  "Index of event in block from which it was emitted."
+  indexInBlock: Int!
+
+  ### SPECIFIC DATA ###
+}
+
+type NotEnoughCandidatesEvent @entity {
+  ### GENERIC DATA ###
+
+  "(network}-{blockNumber}-{indexInBlock}"
+  id: ID!
+
+  "Hash of the extrinsic which caused the event to be emitted"
+  inExtrinsic: String
+
+  "Blocknumber of the block in which the event was emitted."
+  inBlock: Int!
+
+  "Network the block was produced in"
+  network: Network!
+
+  "Index of event in block from which it was emitted."
+  indexInBlock: Int!
+
+  ### SPECIFIC DATA ###
+}
+
+type VotingPeriodStartedEvent @entity {
+  ### GENERIC DATA ###
+
+  "(network}-{blockNumber}-{indexInBlock}"
+  id: ID!
+
+  "Hash of the extrinsic which caused the event to be emitted"
+  inExtrinsic: String
+
+  "Blocknumber of the block in which the event was emitted."
+  inBlock: Int!
+
+  "Network the block was produced in"
+  network: Network!
+
+  "Index of event in block from which it was emitted."
+  indexInBlock: Int!
+
+  ### SPECIFIC DATA ###
+
+  "Number of candidates in the election."
+  numOfCandidates: BigInt!
+}
+
+type NewCandidateEvent @entity {
+  ### GENERIC DATA ###
+
+  "(network}-{blockNumber}-{indexInBlock}"
+  id: ID!
+
+  "Hash of the extrinsic which caused the event to be emitted"
+  inExtrinsic: String
+
+  "Blocknumber of the block in which the event was emitted."
+  inBlock: Int!
+
+  "Network the block was produced in"
+  network: Network!
+
+  "Index of event in block from which it was emitted."
+  indexInBlock: Int!
+
+  ### SPECIFIC DATA ###
+
+  "Candidate's membership."
+  member: Membership!
+
+  "Candidate's account used to stake currency."
+  stakingAccount: String!
+
+  "Candidate's account that will be recieving rewards if candidate's elected."
+  rewardAccount: String!
+
+  "Amount of currency to be staked for the candidacy."
+  balance: BigInt!
+}
+
+type NewCouncilElectedEvent @entity {
+  ### GENERIC DATA ###
+
+  "(network}-{blockNumber}-{indexInBlock}"
+  id: ID!
+
+  "Hash of the extrinsic which caused the event to be emitted"
+  inExtrinsic: String
+
+  "Blocknumber of the block in which the event was emitted."
+  inBlock: Int!
+
+  "Network the block was produced in"
+  network: Network!
+
+  "Index of event in block from which it was emitted."
+  indexInBlock: Int!
+
+  ### SPECIFIC DATA ###
+
+  "Candidates' memberships."
+  electedMembers: [Membership!]
+}
+
+type NewCouncilNotElectedEvent @entity {
+  ### GENERIC DATA ###
+
+  "(network}-{blockNumber}-{indexInBlock}"
+  id: ID!
+
+  "Hash of the extrinsic which caused the event to be emitted"
+  inExtrinsic: String
+
+  "Blocknumber of the block in which the event was emitted."
+  inBlock: Int!
+
+  "Network the block was produced in"
+  network: Network!
+
+  "Index of event in block from which it was emitted."
+  indexInBlock: Int!
+
+  ### SPECIFIC DATA ###
+}
+
+type CandidacyStakeReleaseEvent @entity {
+  ### GENERIC DATA ###
+
+  "(network}-{blockNumber}-{indexInBlock}"
+  id: ID!
+
+  "Hash of the extrinsic which caused the event to be emitted"
+  inExtrinsic: String
+
+  "Blocknumber of the block in which the event was emitted."
+  inBlock: Int!
+
+  "Network the block was produced in"
+  network: Network!
+
+  "Index of event in block from which it was emitted."
+  indexInBlock: Int!
+
+  ### SPECIFIC DATA ###
+
+  "Candidate's membership."
+  member: Membership!
+}
+
+type CandidacyWithdrawEvent @entity {
+  ### GENERIC DATA ###
+
+  "(network}-{blockNumber}-{indexInBlock}"
+  id: ID!
+
+  "Hash of the extrinsic which caused the event to be emitted"
+  inExtrinsic: String
+
+  "Blocknumber of the block in which the event was emitted."
+  inBlock: Int!
+
+  "Network the block was produced in"
+  network: Network!
+
+  "Index of event in block from which it was emitted."
+  indexInBlock: Int!
+
+  ### SPECIFIC DATA ###
+
+  "Candidate's membership."
+  member: Membership!
+}
+
+type CandidacyNoteSetEvent @entity {
+  ### GENERIC DATA ###
+
+  "(network}-{blockNumber}-{indexInBlock}"
+  id: ID!
+
+  "Hash of the extrinsic which caused the event to be emitted"
+  inExtrinsic: String
+
+  "Blocknumber of the block in which the event was emitted."
+  inBlock: Int!
+
+  "Network the block was produced in"
+  network: Network!
+
+  "Index of event in block from which it was emitted."
+  indexInBlock: Int!
+
+  ### SPECIFIC DATA ###
+
+  "Candidate's membership."
+  member: Membership!
+
+  "The note that has been set."
+  note: String!
+}
+
+type RewardPaymentEvent @entity {
+  ### GENERIC DATA ###
+
+  "(network}-{blockNumber}-{indexInBlock}"
+  id: ID!
+
+  "Hash of the extrinsic which caused the event to be emitted"
+  inExtrinsic: String
+
+  "Blocknumber of the block in which the event was emitted."
+  inBlock: Int!
+
+  "Network the block was produced in"
+  network: Network!
+
+  "Index of event in block from which it was emitted."
+  indexInBlock: Int!
+
+  ### SPECIFIC DATA ###
+
+  "Candidate's membership."
+  member: Membership!
+
+  "Candidate's account that will be recieving rewards if candidate's elected."
+  rewardAccount: String!
+
+  "Amount paid to the council member"
+  paidBalance: BigInt!
+
+  "Amount that couldn't be paid and will be paid the next time."
+  missingBalance: BigInt!
+}
+
+type BudgetBalanceSetEvent @entity {
+  ### GENERIC DATA ###
+
+  "(network}-{blockNumber}-{indexInBlock}"
+  id: ID!
+
+  "Hash of the extrinsic which caused the event to be emitted"
+  inExtrinsic: String
+
+  "Blocknumber of the block in which the event was emitted."
+  inBlock: Int!
+
+  "Network the block was produced in"
+  network: Network!
+
+  "Index of event in block from which it was emitted."
+  indexInBlock: Int!
+
+  ### SPECIFIC DATA ###
+
+  "Budget balance that has been set."
+  balance: BigInt!
+}
+
+type BudgetRefillEvent @entity {
+  ### GENERIC DATA ###
+
+  "(network}-{blockNumber}-{indexInBlock}"
+  id: ID!
+
+  "Hash of the extrinsic which caused the event to be emitted"
+  inExtrinsic: String
+
+  "Blocknumber of the block in which the event was emitted."
+  inBlock: Int!
+
+  "Network the block was produced in"
+  network: Network!
+
+  "Index of event in block from which it was emitted."
+  indexInBlock: Int!
+
+  ### SPECIFIC DATA ###
+
+  "Balance that has been refilled."
+  balance: BigInt!
+}
+
+type BudgetRefillPlannedEvent @entity {
+  ### GENERIC DATA ###
+
+  "(network}-{blockNumber}-{indexInBlock}"
+  id: ID!
+
+  "Hash of the extrinsic which caused the event to be emitted"
+  inExtrinsic: String
+
+  "Blocknumber of the block in which the event was emitted."
+  inBlock: Int!
+
+  "Network the block was produced in"
+  network: Network!
+
+  "Index of event in block from which it was emitted."
+  indexInBlock: Int!
+
+  ### SPECIFIC DATA ###
+}
+
+type BudgetIncrementUpdatedEvent @entity {
+  ### GENERIC DATA ###
+
+  "(network}-{blockNumber}-{indexInBlock}"
+  id: ID!
+
+  "Hash of the extrinsic which caused the event to be emitted"
+  inExtrinsic: String
+
+  "Blocknumber of the block in which the event was emitted."
+  inBlock: Int!
+
+  "Network the block was produced in"
+  network: Network!
+
+  "Index of event in block from which it was emitted."
+  indexInBlock: Int!
+
+  ### SPECIFIC DATA ###
+
+  "Amount that is added to the budget each time it's refilled."
+  amount: BigInt!
+}
+
+type CouncilorRewardUpdatedEvent @entity {
+  ### GENERIC DATA ###
+
+  "(network}-{blockNumber}-{indexInBlock}"
+  id: ID!
+
+  "Hash of the extrinsic which caused the event to be emitted"
+  inExtrinsic: String
+
+  "Blocknumber of the block in which the event was emitted."
+  inBlock: Int!
+
+  "Network the block was produced in"
+  network: Network!
+
+  "Index of event in block from which it was emitted."
+  indexInBlock: Int!
+
+  ### SPECIFIC DATA ###
+
+  "New reward amount paid each reward period."
+  rewardAmount: BigInt!
+}
+
+type RequestFundedEvent @entity {
+  ### GENERIC DATA ###
+
+  "(network}-{blockNumber}-{indexInBlock}"
+  id: ID!
+
+  "Hash of the extrinsic which caused the event to be emitted"
+  inExtrinsic: String
+
+  "Blocknumber of the block in which the event was emitted."
+  inBlock: Int!
+
+  "Network the block was produced in"
+  network: Network!
+
+  "Index of event in block from which it was emitted."
+  indexInBlock: Int!
+
+  ### SPECIFIC DATA ###
+
+  "Funding account."
+  account: String!
+
+  "Funding amount."
+  amount: BigInt!
+}
+
+################### Referendum #################################################
+
+type ReferendumStartedEvent @entity {
+  ### GENERIC DATA ###
+
+  "(network}-{blockNumber}-{indexInBlock}"
+  id: ID!
+
+  "Hash of the extrinsic which caused the event to be emitted"
+  inExtrinsic: String
+
+  "Blocknumber of the block in which the event was emitted."
+  inBlock: Int!
+
+  "Network the block was produced in"
+  network: Network!
+
+  "Index of event in block from which it was emitted."
+  indexInBlock: Int!
+
+  ### SPECIFIC DATA ###
+
+  "Amount of winning referendum options."
+  winningTargetCount: BigInt!
+}
+
+type ReferendumStartedForcefullyEvent @entity {
+  ### GENERIC DATA ###
+
+  "(network}-{blockNumber}-{indexInBlock}"
+  id: ID!
+
+  "Hash of the extrinsic which caused the event to be emitted"
+  inExtrinsic: String
+
+  "Blocknumber of the block in which the event was emitted."
+  inBlock: Int!
+
+  "Network the block was produced in"
+  network: Network!
+
+  "Index of event in block from which it was emitted."
+  indexInBlock: Int!
+
+  ### SPECIFIC DATA ###
+
+  "Amount of winning referendum options."
+  winningTargetCount: BigInt!
+}
+
+type RevealingStageStartedEvent @entity {
+  ### GENERIC DATA ###
+
+  "(network}-{blockNumber}-{indexInBlock}"
+  id: ID!
+
+  "Hash of the extrinsic which caused the event to be emitted"
+  inExtrinsic: String
+
+  "Blocknumber of the block in which the event was emitted."
+  inBlock: Int!
+
+  "Network the block was produced in"
+  network: Network!
+
+  "Index of event in block from which it was emitted."
+  indexInBlock: Int!
+
+  ### SPECIFIC DATA ###
+}
+
+type ReferendumFinishedEvent @entity {
+  ### GENERIC DATA ###
+
+  "(network}-{blockNumber}-{indexInBlock}"
+  id: ID!
+
+  "Hash of the extrinsic which caused the event to be emitted"
+  inExtrinsic: String
+
+  "Blocknumber of the block in which the event was emitted."
+  inBlock: Int!
+
+  "Network the block was produced in"
+  network: Network!
+
+  "Index of event in block from which it was emitted."
+  indexInBlock: Int!
+
+  ### SPECIFIC DATA ###
+
+  "Referendum results."
+  optionResults: [ReferendumStageRevealingOptionResult!] @derivedFrom(field: "referendumFinishedEvent")
+}
+
+type VoteCastEvent @entity {
+  ### GENERIC DATA ###
+
+  "(network}-{blockNumber}-{indexInBlock}"
+  id: ID!
+
+  "Hash of the extrinsic which caused the event to be emitted"
+  inExtrinsic: String
+
+  "Blocknumber of the block in which the event was emitted."
+  inBlock: Int!
+
+  "Network the block was produced in"
+  network: Network!
+
+  "Index of event in block from which it was emitted."
+  indexInBlock: Int!
+
+  ### SPECIFIC DATA ###
+
+  "Account that cast the vote."
+  account: String!
+
+  "Hash of sealed vote."
+  hash: String!
+
+  "Vote power decided by amount staked."
+  votePower: BigInt!
+}
+
+type VoteRevealedEvent @entity {
+  ### GENERIC DATA ###
+
+  "(network}-{blockNumber}-{indexInBlock}"
+  id: ID!
+
+  "Hash of the extrinsic which caused the event to be emitted"
+  inExtrinsic: String
+
+  "Blocknumber of the block in which the event was emitted."
+  inBlock: Int!
+
+  "Network the block was produced in"
+  network: Network!
+
+  "Index of event in block from which it was emitted."
+  indexInBlock: Int!
+
+  ### SPECIFIC DATA ###
+
+  "Account that cast a vote."
+  account: String!
+
+  "Membership of user that cast the vote."
+  member: Membership!
+
+  "Salt that has been used for the vote's hash creation."
+  salt: String!
+}
+
+type StakeReleasedEvent @entity {
+  ### GENERIC DATA ###
+
+  "(network}-{blockNumber}-{indexInBlock}"
+  id: ID!
+
+  "Hash of the extrinsic which caused the event to be emitted"
+  inExtrinsic: String
+
+  "Blocknumber of the block in which the event was emitted."
+  inBlock: Int!
+
+  "Network the block was produced in"
+  network: Network!
+
+  "Index of event in block from which it was emitted."
+  indexInBlock: Int!
+
+  ### SPECIFIC DATA ###
+
+  "Account used to stake the value."
+  stakingAccount: String!
+}

+ 8 - 0
query-node/schemas/membership.graphql

@@ -80,6 +80,14 @@ type Membership @entity {
 
   "Content channels the member owns"
   channels: [Channel!] @derivedFrom(field: "ownerMember")
+
+  # Council & Referendum relations
+
+  #budgetPayments: [BudgetPayment!] @derivedFrom(field: "member")
+  councilMembers: [CouncilMember!] @derivedFrom(field: "member")
+  referendumStageRevealingOptionResults: [ReferendumStageRevealingOptionResult!] @derivedFrom(field: "optionId")
+  votesRecieved: [CastVote!] @derivedFrom(field: "voteFor")
+  electedCouncilEvents: [NewCouncilElectedEvent!] @derivedFrom(field: "electedMembers")
 }
 
 type MembershipSystemSnapshot @entity {