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 (Learn more).
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.4schema:file:./schema.graphqldataSources: - kind:ethereumname:PromiseFactorynetwork:mumbaisource:# The current PromiseFactory addressaddress:'0xa288Da44e534Ebed813D7ea8aEc7A86A50a878B9'abi:PromiseFactory# The block it should start indexing atstartBlock:29217396mapping:kind:ethereum/eventsapiVersion:0.0.6language:wasm/assemblyscriptentities: - ActivePromise - PromiseContractCreated - ParticipantAdded - TwitterVerifiedUser - TwitterAddVerifiedSuccessfulabis: - name:PromiseFactoryfile:./abis/PromiseFactory.jsoneventHandlers:# A new promise creation event - event:PromiseContractCreated(indexed address,indexedaddress,string,string,string,string,string[],string[],address[])# How to handle it in the mappinghandler: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:handleTwitterAddVerifiedSuccessfulfile:./src/promise-factory.ts
Schema
schema.graphql
# Updated at PromiseContractCreated and ParticipantAddedtypeActivePromise@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 createdtypePromiseContractCreated@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 promisetypeParticipantAdded@entity { id: ID! contractAddress: Bytes! # address participantName: String! # string participantTwitterHandle: String! # string participantAddress: Bytes! # address}# Updated at TwitterAddVerifiedSuccessfultypeTwitterVerifiedUser@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 verifiedtypeTwitterAddVerifiedSuccessful@entity { id: ID! address: Bytes! twitterHandle: String!}
Mapping
Handling PromiseContractCreated
promise-factory.ts
exportfunctionhandlePromiseContractCreated( 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 anywaylet activePromise =ActivePromise.load(getIdFromEventParams(event.params._contractAddress), );// If this ActivePromise doesn't exist, create itif (!activePromise) { activePromise =newActivePromise(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 entityactivePromise.save();}
Handling ParticipantAdded
promise-factory.ts
exportfunctionhandleParticipantAdded(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 existlet 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 parameterconstnewNamesArray= activePromise!.partyNames.concat([event.params._participantName, ]);constnewTwitterHandlesArray= activePromise!.partyTwitterHandles.concat([event.params._participantTwitterHandle, ]);constnewAddressesArray= 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
exportfunctionhandleTwitterAddVerifiedSuccessful( event:TwitterAddVerifiedSuccessfulEvent,):void {// Load the user entity, if they already have verified Twitter accountslet twitterVerifiedUser =TwitterVerifiedUser.load(getIdFromEventParams(event.params._owner), );// We prefer not interacting directly with twitterHandles that could be null// Create a new arraylet twitterHandlesArray:string[] = [];// If the user has no verified account (so no entity) yet...if (!twitterVerifiedUser) {// Create an entity... twitterVerifiedUser =newTwitterVerifiedUser(getIdFromEventParams(event.params._owner), );// ... and just add the handle to a new array twitterHandlesArray =newArray<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 entitytwitterVerifiedUser.twitterHandles = twitterHandlesArray;twitterVerifiedUser.verifiedAt =event.block.timestamp;twitterVerifiedUser.save();}