promise
AppRepository
  • Overview
  • Links
  • How to use
    • Exploring promises
    • Creating a promise
    • Interacting with promises
    • Verifying a Twitter account
    • Indexing an IPFS directory
  • Chainlink - External Adapters
    • Introduction
    • Twitter account verification
    • IPFS & Arweave verification
  • IPFS & Arweave
    • Sending to the IPFS network
    • Sending to Arweave with Bundlr
  • The Graph
    • Catching events from promises
Powered by GitBook
On this page
  • Manifest
  • Schema
  • Mapping
  • Resources
  1. The Graph

Catching events from promises

How does the frontend listen for contract events?

PreviousSending to Arweave with Bundlr

Last updated 2 years ago

When reloading, or after a custom interaction from the user, the application performs queries to The Graph, and retrieves data from active node operators listening and indexing our selected contract events .

The subgraph, deployed on The Graph Network, is available for queries on the Hosted Service. It contains:

  • subgraph.yaml: a manifest that describes the data it's interested in ;

  • schema.graphql: a schema that defines the data entities, and how the queries should be performed ;

  • promise-factory.ts: a mapping that handles the custom actions, by translating the data it receives into understandable entities we defined in the schema.

Manifest

subgraph.yaml
specVersion: 0.0.4
schema:
  file: ./schema.graphql
dataSources:
  - kind: ethereum
    name: PromiseFactory
    network: mumbai
    source:
      # The current PromiseFactory address
      address: '0xa288Da44e534Ebed813D7ea8aEc7A86A50a878B9'
      abi: PromiseFactory
      # The block it should start indexing at
      startBlock: 29217396
    mapping:
      kind: ethereum/events
      apiVersion: 0.0.6
      language: wasm/assemblyscript
      entities:
        - ActivePromise
        - PromiseContractCreated
        - ParticipantAdded
        - TwitterVerifiedUser
        - TwitterAddVerifiedSuccessful
      abis:
        - name: PromiseFactory
          file: ./abis/PromiseFactory.json
      eventHandlers:
        # A new promise creation event
        - event: PromiseContractCreated(indexed address,indexed
            address,string,string,string,string,string[],string[],address[])
          # How to handle it in the mapping
          handler: handlePromiseContractCreated
          # A new participant added to a promise event
        - event: ParticipantAdded(indexed address,string,string,address)
          handler: handleParticipantAdded
          # A successful Twitter verification event
        - event: TwitterAddVerifiedSuccessful(indexed address,string)
          handler: handleTwitterAddVerifiedSuccessful
      file: ./src/promise-factory.ts

Schema

schema.graphql
# Updated at PromiseContractCreated and ParticipantAdded
type ActivePromise @entity {
  # A unique ID created in the mapping
  id: ID!
  # The creator of the promise
  owner: Bytes!
  # The address of the promise
  contractAddress: Bytes!
  # The name, IPFS CID and Arweave ID of the promise
  promiseName: String!
  ipfsCid: String!
  arweaveId: String!
  # The participants informations
  partyNames: [String!]!
  partyTwitterHandles: [String!]!
  partyAddresses: [Bytes!]!
  # The date of the promise creation - base on the block timestamp
  # of PromiseContractCreated
  createdAt: BigInt
  # The date of the last modification - base on the block timestamp
  # of ParticipantAdded
  updatedAt: BigInt
}

# Fired when a promise is created
type PromiseContractCreated @entity {
  id: ID!
  owner: Bytes!
  contractAddress: Bytes!
  promiseName: String!
  ipfsCid: String!
  arweaveId: String!
  partyNames: [String!]!
  partyTwitterHandles: [String!]!
  partyAddresses: [Bytes!]!
  blockTimestamp: BigInt
}

# Fired when a participant is added to a promise
type ParticipantAdded @entity {
  id: ID!
  contractAddress: Bytes! # address
  participantName: String! # string
  participantTwitterHandle: String! # string
  participantAddress: Bytes! # address
}

# Updated at TwitterAddVerifiedSuccessful
type TwitterVerifiedUser @entity {
  id: ID!
  address: Bytes!
  # All unique handles verified for this address
  twitterHandles: [String!]!
  # The timestamp of the verification
  verifiedAt: BigInt
}

# Fired when a participant gets a Twitter account verified
type TwitterAddVerifiedSuccessful @entity {
  id: ID!
  address: Bytes!
  twitterHandle: String!
}

Mapping

Handling PromiseContractCreated

promise-factory.ts
export function handlePromiseContractCreated(
  event: PromiseContractCreatedEvent,
): void {
  // It should never happen that the same contract is created twice
  // But we can't ever be sure enough, so we check if the entity already exists anyway
  let activePromise = ActivePromise.load(
    getIdFromEventParams(event.params._contractAddress),
  );

  // If this ActivePromise doesn't exist, create it
  if (!activePromise) {
    activePromise = new ActivePromise(
      getIdFromEventParams(event.params._contractAddress),
    );
  }

  // Grab the data from the event parameters
  // and associate it to this entity
  // From the contract...
  activePromise.owner = event.params._owner;
  activePromise.contractAddress = event.params._contractAddress;
  activePromise.promiseName = event.params._promiseName;
  activePromise.ipfsCid = event.params._ipfsCid;
  activePromise.arweaveId = event.params._arweaveId;
  activePromise.partyNames = event.params._partyNames;
  activePromise.partyTwitterHandles = event.params._partyTwitterHandles;
  activePromise.partyAddresses = event.params._partyAddresses.map<Bytes>(
    (e: Bytes) => e,
  );
  // From the block...
  activePromise.createdAt = event.block.timestamp;
  activePromise.updatedAt = event.block.timestamp;

  // Save the entity
  activePromise.save();
}

Handling ParticipantAdded

promise-factory.ts
export function handleParticipantAdded(event: ParticipantAddedEvent): void {
  // Grab the entity (created when the promise was created)
  // We won't need to create it here, it should not be possible to add
  // a participant to a promise that doesn't exist
  let activePromise = ActivePromise.load(
    getIdFromEventParams(event.params._contractAddress),
  );

  // We can't use the .push method here because it's not supported by AssemblyScript
  // So we have to do it 'manually'
  // Create an new array from the old one along with the new parameter
  const newNamesArray = activePromise!.partyNames.concat([
    event.params._participantName,
  ]);
  const newTwitterHandlesArray = activePromise!.partyTwitterHandles.concat([
    event.params._participantTwitterHandle,
  ]);
  const newAddressesArray = activePromise!.partyAddresses.concat([
    event.params._participantAddress,
  ]);

  // Set the promise new parameter with the new array
  activePromise!.partyNames = newNamesArray;
  activePromise!.partyTwitterHandles = newTwitterHandlesArray;
  activePromise!.partyAddresses = newAddressesArray;
  activePromise!.updatedAt = event.block.timestamp;

  activePromise!.save();
}

Handling TwitterAddVerifiedSuccessful

promise-factory.ts
export function handleTwitterAddVerifiedSuccessful(
  event: TwitterAddVerifiedSuccessfulEvent,
): void {
  // Load the user entity, if they already have verified Twitter accounts
  let twitterVerifiedUser = TwitterVerifiedUser.load(
    getIdFromEventParams(event.params._owner),
  );
  // We prefer not interacting directly with twitterHandles that could be null
  // Create a new array
  let twitterHandlesArray: string[] = [];

  // If the user has no verified account (so no entity) yet...
  if (!twitterVerifiedUser) {
    // Create an entity...
    twitterVerifiedUser = new TwitterVerifiedUser(
      getIdFromEventParams(event.params._owner),
    );
    // ... and just add the handle to a new array
    twitterHandlesArray = new Array<string>().concat([
      event.params._twitterHandle,
    ]);
  } else {
    // If the user has been verified before, get the array from the entity
    twitterHandlesArray = twitterVerifiedUser.twitterHandles;
    // Add the new handle to the array
    twitterHandlesArray = twitterHandlesArray.concat([
      event.params._twitterHandle,
    ]);
    // Remove duplicates from the array (if the same handle has been verified)
    twitterHandlesArray = twitterHandlesArray.filter(
      (value, index, self) => self.indexOf(value) === index,
    );
  }

  twitterVerifiedUser.address = event.params._owner;
  // Set the twitterHandles without ever checking the content of the entity
  twitterVerifiedUser.twitterHandles = twitterHandlesArray;
  twitterVerifiedUser.verifiedAt = event.block.timestamp;

  twitterVerifiedUser.save();
}

Resources

Repository
External

Subgraph
Manifest (subgraph.yaml)
Schema (schema.graphql)
Mapping (promise-factory.ts)
Subgraph on the Hosted Service (Mumbai)
The Graph documentation - About The Graph
The Graph documentation - Creating a Subgraph
(Learn more)
promises-subgraph-mumbai-v1 Subgraphpromises-subgraph-mumbai-v1 Subgraph
Our subgraph on the Hosted Service.
The GraphiQL
The API to make queries to.
Logo