# === this part of the schema comes from giant squid ===
# https://github.com/subsquid-labs/giant-squid-explorer/blob/main/schema.graphql
# Block / Extrinsic / Call / Event

type Block @entity {
  "BlockHeight-blockHash - e.g. 0001812319-0001c"
  id: ID!
  height: Int! @index
  hash: Bytes! @index
  parentHash: Bytes!
  stateRoot: Bytes!
  extrinsicsicRoot: Bytes!
  specName: String!
  specVersion: Int! @index
  implName: String!
  implVersion: Int!
  timestamp: DateTime! @index
  validator: Bytes @index
  extrinsicsCount: Int!
  callsCount: Int!
  eventsCount: Int!
  extrinsics: [Extrinsic]! @derivedFrom(field: "block") @cardinality(value: 1000)
  calls: [Call]! @derivedFrom(field: "block") @cardinality(value: 1000)
  events: [Event]! @derivedFrom(field: "block") @cardinality(value: 1000)
}

type ExtrinsicSignature {
  address: JSON
  signature: JSON
  signedExtensions: JSON
}

type Extrinsic @entity {
  id: ID!

  block: Block!
  call: Call!

  index: Int!
  version: Int!
  signature: ExtrinsicSignature
  tip: BigInt
  fee: BigInt
  success: Boolean @index
  error: JSON
  hash: Bytes! @index

  calls: [Call]! @derivedFrom(field: "extrinsic")
  events: [Event]! @derivedFrom(field: "extrinsic")
}

type Call @entity @index(fields: ["id", "pallet", "name"]) {
  id: ID!

  block: Block!
  extrinsic: Extrinsic
  parent: Call

  address: [Int!]!
  success: Boolean! @index
  error: JSON

  pallet: String! @index
  name: String! @index

  args: JSON
  argsStr: [String]

  subcalls: [Call]! @derivedFrom(field: "parent")
  events: [Event]! @derivedFrom(field: "call")
}

type Event @entity @index(fields: ["id", "pallet", "name"]) {
  "Event id - e.g. 0000000001-000000-272d6"
  id: ID!

  block: Block!
  extrinsic: Extrinsic
  call: Call

  index: Int!
  phase: String!

  pallet: String! @index
  name: String! @index

  args: JSON
  argsStr: [String]
}

enum CounterLevel {
  Global
  Pallet
  Item
}

enum ItemType {
  Extrinsics
  Calls
  Events
}

type ItemsCounter @entity {
  id: ID!
  type: ItemType! @index
  level: CounterLevel! @index
  total: Int! @index
}

# === this part of the schema is for substrate pallets ===
# Balances /

"Account table identified by its ss58 address"
type Account @entity {
  "Account address is SS58 format"
  id: ID!
  "Block number of account creation"
  # if account is created multiple times, this is the first creation block
  createdOn: Int!
  "current account for the identity"
  identity: Identity @derivedFrom(field: "account")
  "removed identities on this account"
  # they are handled apart to avoid dropping the @unique constraint of account
  removedIdentities: [Identity] @derivedFrom(field: "accountRemoved")
  "was once account of the identity"
  wasIdentity: [ChangeOwnerKey!] @derivedFrom(field: "previous") # there should be at most one
  "linked to the identity"
  linkedIdentity: Identity
  "transfers issued by this account"
  transfersIssued: [Transfer!] @derivedFrom(field: "from")
  "transfers received by this account"
  transfersReceived: [Transfer!] @derivedFrom(field: "to")
  "comments issued"
  commentsIssued: [TxComment!] @derivedFrom(field: "author")
  "is currently active"
  isActive: Boolean!
}

"Since identities can change owner key, validator table helps track which smith is forging the block"
# this could be removed if we fix the following issues
# https://git.duniter.org/nodes/rust/duniter-v2s/-/issues/197
# https://git.duniter.org/nodes/rust/duniter-v2s/-/issues/245
type Validator @entity {
  "SS58 of pubkey used at least once to compute a block"
  id: ID!
  "Identity index of Smith who owned this pubkey"
  index: Int! @index
}

"Transfers"
type Transfer @entity {
  "Block number of the transfer"
  blockNumber: Int! @index
  "Timestamp of the transfer (duplicate of block timestamp)"
  timestamp: DateTime! @index
  "Transfer issuer"
  from: Account!
  "Transfer receiver"
  to: Account!
  "Integer amount of the transfer"
  amount: BigInt! @index
  "Event the transfer was created in" # allows to find the call and block
  event: Event!
  "Optional comment linked to the transfer"
  comment: TxComment
}

type Smith @entity {
  "Identity index"
  index: Int! @index @unique
  identity: Identity! @unique
  "Smith certifications issued"
  smithCertIssued: [SmithCert!] @derivedFrom(field: "issuer")
  "Smith certifications received"
  smithCertReceived: [SmithCert!] @derivedFrom(field: "receiver")
  "Smith status of the identity"
  smithStatus: SmithStatus
  "history of the smith changes events"
  smithHistory: [SmithEvent] @derivedFrom(field: "smith")
  "Last status change"
  lastChanged: Int
  "Number of forged blocks"
  forged: Int!
  "Last forged block"
  lastForged: Int
}

# === this part of the schema is for Duniter pallets ===
#

"Identity"
type Identity @entity {
  "Identity index"
  index: Int! @index @unique
  "Current account"
  # should be null for a removed identity and only in this case
  account: Account @unique
  "Let track the account in case identity was removed"
  # should be non null for a removed identity and only in this case
  accountRemoved: Account
  "Name"
  name: String! @index
  "Status of the identity"
  # mixes identity pallet status (Unconfirmed, Unvalidated, Revoked)
  # and membership pallet status (Member, WasMember)
  # and a special status "Removed" specific to the indexer
  status: IdentityStatus! @index
  "Block number of identity creation event"
  createdOn: Int!
  "Event corresponding of identity creation event"
  createdIn: Event!
  "Block number of last identity, changeOwnerKey and membership active event"
  # - creation block
  # - confirmation block
  # - validation block
  # - membership renewal block
  # - manual revocation
  # but not
  # - membership expiry
  # - auto-revocation
  # - duniter deletion
  lastChangeOn: Int!
  "Certifications issued"
  certIssued: [Cert!] @derivedFrom(field: "issuer")
  "Certifications received"
  certReceived: [Cert!] @derivedFrom(field: "receiver")
  "True if the identity is a member"
  isMember: Boolean!
  "the current expireOn value"
  # Unconfirmed → block of expected confirmation (future)
  # Unvalidated → block of expected validation (future)
  # Member → block of membership expiry (future)
  # NotMember → block of auto-revocation (future)
  # Revoked → block of revocation (past)
  # Removed → still block of revocation (past) because only Duniter "garbage collection"
  expireOn: Int!
  "history of the membership changes events"
  membershipHistory: [MembershipEvent] @derivedFrom(field: "identity")
  "Owner key changes"
  ownerKeyChange: [ChangeOwnerKey!] @derivedFrom(field: "identity")
  "linked accounts"
  linkedAccount: [Account!] @derivedFrom(field: "linkedIdentity")
  "Smith information"
  smith: Smith @derivedFrom(field: "identity")
  "Universal Dividend history"
  udHistory: [UdHistory!] @derivedFrom(field: "identity")
}

"identity status directly linked to Duniter IdtyStatus"
enum IdentityStatus {
  Unconfirmed
  Unvalidated
  Member
  NotMember
  Revoked
  # this status is added because the indexer keeps track of the history
  # and does not remove an identity event when it is remove from Duniter
  Removed
}

"smith status directly linked to Duniter SmithStatus" # used as a nullable field because most identities are not smith as well
enum SmithStatus {
  Invited
  Pending
  Smith
  Excluded
}

"owner key change"
type ChangeOwnerKey @entity {
  identity: Identity!
  previous: Account!
  next: Account!
  blockNumber: Int!
}

"Certification"
type Cert @entity {
  "certification source"
  issuer: Identity! @index
  "certification target"
  receiver: Identity! @index
  "whether the certification is currently active or not"
  isActive: Boolean!
  "the first block number of the certification creation"
  # (helper field to avoid looking for all CertCreation and choose the first)
  createdOn: Int!
  "the event corresponding to the first certification creation"
  createdIn: Event!
  "the last block number of the certification renewal"
  updatedOn: Int!
  "the event corresponding to the last certification renewal"
  updatedIn: Event!
  "the current expireOn value"
  # usually this is updatedOn + certDuration *but*:
  # - certs can be removed prematurely for unconfirmed or unvalidated identity
  # - update can be before genesis, in this case we still need expire block after genesis
  expireOn: Int!
  "list all events of this cert"
  certHistory: [CertEvent!] @derivedFrom(field: "cert")
}

"Certification event"
type CertEvent @entity {
  cert: Cert! @index
  event: Event!
  blockNumber: Int! @index
  eventType: EventType!
}

"Smith certification"
type SmithCert @entity {
  issuer: Smith!
  receiver: Smith!
  createdOn: Int!
}

type SmithEvent @entity {
  smith: Smith! @index
  eventType: SmithEventType!
  event: Event!
  blockNumber: Int! @index
}

enum SmithEventType {
  Invited
  Accepted
  Promoted
  Excluded
}

type MembershipEvent @entity {
  identity: Identity! @index
  eventType: EventType!
  event: Event!
  blockNumber: Int! @index
}

"event type used for certification and membership"
enum EventType {
  "creation event"
  Creation
  "renewal event"
  Renewal
  "removal event"
  Removal
}

"Each Universal Dividend created since the beginning of the blockchain"
type UniversalDividend @entity {
  blockNumber: Int!
  event: Event!
  timestamp: DateTime!
  amount: BigInt!
  monetaryMass: BigInt!
  membersCount: Int!
}

"List of reevaluation of Universal Dividend based on changes in monetary mass and number of members. Every 6 months in Ğ1"
type UdReeval @entity {
  blockNumber: Int!
  event: Event!
  timestamp: DateTime!
  newUdAmount: BigInt!
  monetaryMass: BigInt!
  membersCount: Int!
}

"History of Universal Dividend received by an member identity."
type UdHistory @entity {
  identity: Identity! @index
  amount: BigInt!
  blockNumber: Int!
  timestamp: DateTime!
}

"transaction comment"
type TxComment @entity {
  "Block number of the comment" # allows easy sorting
  blockNumber: Int!
  "Author of the comment"
  author: Account!
  "Event where the comment comes from"
  event: Event!
  "Raw remark as present on the blockchain"
  remarkBytes: Bytes!
  "Remark decoded as string"
  remark: String!
  "Blake two 256 hash published by the blockchain in the remark event"
  # not used at the moment, will be changed in Duniter
  hash: String!
  "Type of the comment"
  # now classified by the indexer, can later appear in Duniter
  type: CommentType!
}

"type of the tx comment"
# by order of preference
enum CommentType {
  "bytes look like a CID"
  Cid
  "bytes can be decoded to printable ascii"
  Ascii
  "bytes can be decoded to printable utf8"
  Unicode
  "no known type, raw bytes"
  Raw
}

"History of the blockchain population."
type PopulationHistory @entity {
  "The count of smiths at this point in the history."
  smithCount: Int!
  "The count of members at this point in the history."
  memberCount: Int!
  "The count of active accounts at this point in the history."
  activeAccountCount: Int!
  "The block number at which this history record was created."
  blockNumber: Int! @index @unique
}