diff --git a/Implementations/Subgraph/daostar/build/EIP4824Index/EIP4824Index.wasm b/Implementations/Subgraph/daostar/build/EIP4824Index/EIP4824Index.wasm index 8ebf4ee9..b67bd38f 100644 Binary files a/Implementations/Subgraph/daostar/build/EIP4824Index/EIP4824Index.wasm and b/Implementations/Subgraph/daostar/build/EIP4824Index/EIP4824Index.wasm differ diff --git a/Implementations/Subgraph/daostar/build/schema.graphql b/Implementations/Subgraph/daostar/build/schema.graphql index 8d15e2a7..6bf8239f 100644 --- a/Implementations/Subgraph/daostar/build/schema.graphql +++ b/Implementations/Subgraph/daostar/build/schema.graphql @@ -1,22 +1,26 @@ type RegistrationInstance @entity { - id: ID! - registrationAddress: Bytes! - daoAddress: Bytes! - daoURI: String! # string - daoName: String - registrationNetwork: RegistrationNetwork! - daoDescription: String - membersURI: String - issuersURI: String - proposalsURI: String - governanceURI: String - activityLogURI: String - managerAddress: String - contractsRegistryURI: String + id: ID! + daoAddress: Bytes! + registrationNetwork: RegistrationNetwork! @relation + registrationAddress: Bytes! + daoURI: String! +} + +type DAOMetadata @entity { + id: ID! + daoName: String! + daoDescription: String! + membersURI: String! + issuersURI: String! + proposalsURI: String! + governanceURI: String! + activityLogURI: String! + contractsRegistryURI: String! + managerAddress: String! } type RegistrationNetwork @entity { - id: ID! - registrations: [RegistrationInstance!] @derivedFrom(field: "registrationNetwork") - chainId: String! + id: ID! + registrations: [RegistrationInstance!] @derivedFrom(field: "registrationNetwork") + chainId: String! } diff --git a/Implementations/Subgraph/daostar/build/subgraph.yaml b/Implementations/Subgraph/daostar/build/subgraph.yaml index bcf8ed8a..4889f7aa 100644 --- a/Implementations/Subgraph/daostar/build/subgraph.yaml +++ b/Implementations/Subgraph/daostar/build/subgraph.yaml @@ -11,7 +11,7 @@ templates: abi: EIP4824Registration mapping: kind: ethereum/events - apiVersion: 0.0.6 + apiVersion: 0.0.7 language: wasm/assemblyscript file: EIP4824Index/EIP4824Index.wasm entities: @@ -22,6 +22,16 @@ templates: eventHandlers: - event: DAOURIUpdate(address,string) handler: handleNewURI + - name: DAOMetadataTemplate + kind: file/ipfs + mapping: + apiVersion: 0.0.7 + language: wasm/assemblyscript + file: EIP4824Index/EIP4824Index.wasm + handler: handleDAOMetadata + entities: + - DAOMetadata + abis: [] dataSources: - kind: ethereum name: EIP4824Index @@ -32,7 +42,7 @@ dataSources: startBlock: 171329384 mapping: kind: ethereum/events - apiVersion: 0.0.6 + apiVersion: 0.0.7 language: wasm/assemblyscript entities: - NewRegistration diff --git a/Implementations/Subgraph/daostar/generated/schema.ts b/Implementations/Subgraph/daostar/generated/schema.ts index 4c42c9c5..aa510c22 100644 --- a/Implementations/Subgraph/daostar/generated/schema.ts +++ b/Implementations/Subgraph/daostar/generated/schema.ts @@ -54,8 +54,8 @@ export class RegistrationInstance extends Entity { this.set("id", Value.fromString(value)); } - get registrationAddress(): Bytes { - let value = this.get("registrationAddress"); + get daoAddress(): Bytes { + let value = this.get("daoAddress"); if (!value || value.kind == ValueKind.NULL) { throw new Error("Cannot return null for a required field."); } else { @@ -63,12 +63,25 @@ export class RegistrationInstance extends Entity { } } - set registrationAddress(value: Bytes) { - this.set("registrationAddress", Value.fromBytes(value)); + set daoAddress(value: Bytes) { + this.set("daoAddress", Value.fromBytes(value)); } - get daoAddress(): Bytes { - let value = this.get("daoAddress"); + get registrationNetwork(): string { + let value = this.get("registrationNetwork"); + if (!value || value.kind == ValueKind.NULL) { + throw new Error("Cannot return null for a required field."); + } else { + return value.toString(); + } + } + + set registrationNetwork(value: string) { + this.set("registrationNetwork", Value.fromString(value)); + } + + get registrationAddress(): Bytes { + let value = this.get("registrationAddress"); if (!value || value.kind == ValueKind.NULL) { throw new Error("Cannot return null for a required field."); } else { @@ -76,8 +89,8 @@ export class RegistrationInstance extends Entity { } } - set daoAddress(value: Bytes) { - this.set("daoAddress", Value.fromBytes(value)); + set registrationAddress(value: Bytes) { + this.set("registrationAddress", Value.fromBytes(value)); } get daoURI(): string { @@ -92,26 +105,51 @@ export class RegistrationInstance extends Entity { set daoURI(value: string) { this.set("daoURI", Value.fromString(value)); } +} - get daoName(): string | null { - let value = this.get("daoName"); +export class DAOMetadata extends Entity { + constructor(id: string) { + super(); + this.set("id", Value.fromString(id)); + } + + save(): void { + let id = this.get("id"); + assert(id != null, "Cannot save DAOMetadata entity without an ID"); + if (id) { + assert( + id.kind == ValueKind.STRING, + `Entities of type DAOMetadata must have an ID of type String but the id '${id.displayData()}' is of type ${id.displayKind()}`, + ); + store.set("DAOMetadata", id.toString(), this); + } + } + + static loadInBlock(id: string): DAOMetadata | null { + return changetype( + store.get_in_block("DAOMetadata", id), + ); + } + + static load(id: string): DAOMetadata | null { + return changetype(store.get("DAOMetadata", id)); + } + + get id(): string { + let value = this.get("id"); if (!value || value.kind == ValueKind.NULL) { - return null; + throw new Error("Cannot return null for a required field."); } else { return value.toString(); } } - set daoName(value: string | null) { - if (!value) { - this.unset("daoName"); - } else { - this.set("daoName", Value.fromString(value)); - } + set id(value: string) { + this.set("id", Value.fromString(value)); } - get registrationNetwork(): string { - let value = this.get("registrationNetwork"); + get daoName(): string { + let value = this.get("daoName"); if (!value || value.kind == ValueKind.NULL) { throw new Error("Cannot return null for a required field."); } else { @@ -119,144 +157,112 @@ export class RegistrationInstance extends Entity { } } - set registrationNetwork(value: string) { - this.set("registrationNetwork", Value.fromString(value)); + set daoName(value: string) { + this.set("daoName", Value.fromString(value)); } - get daoDescription(): string | null { + get daoDescription(): string { let value = this.get("daoDescription"); if (!value || value.kind == ValueKind.NULL) { - return null; + throw new Error("Cannot return null for a required field."); } else { return value.toString(); } } - set daoDescription(value: string | null) { - if (!value) { - this.unset("daoDescription"); - } else { - this.set("daoDescription", Value.fromString(value)); - } + set daoDescription(value: string) { + this.set("daoDescription", Value.fromString(value)); } - get membersURI(): string | null { + get membersURI(): string { let value = this.get("membersURI"); if (!value || value.kind == ValueKind.NULL) { - return null; + throw new Error("Cannot return null for a required field."); } else { return value.toString(); } } - set membersURI(value: string | null) { - if (!value) { - this.unset("membersURI"); - } else { - this.set("membersURI", Value.fromString(value)); - } + set membersURI(value: string) { + this.set("membersURI", Value.fromString(value)); } - get issuersURI(): string | null { + get issuersURI(): string { let value = this.get("issuersURI"); if (!value || value.kind == ValueKind.NULL) { - return null; + throw new Error("Cannot return null for a required field."); } else { return value.toString(); } } - set issuersURI(value: string | null) { - if (!value) { - this.unset("issuersURI"); - } else { - this.set("issuersURI", Value.fromString(value)); - } + set issuersURI(value: string) { + this.set("issuersURI", Value.fromString(value)); } - get proposalsURI(): string | null { + get proposalsURI(): string { let value = this.get("proposalsURI"); if (!value || value.kind == ValueKind.NULL) { - return null; + throw new Error("Cannot return null for a required field."); } else { return value.toString(); } } - set proposalsURI(value: string | null) { - if (!value) { - this.unset("proposalsURI"); - } else { - this.set("proposalsURI", Value.fromString(value)); - } + set proposalsURI(value: string) { + this.set("proposalsURI", Value.fromString(value)); } - get governanceURI(): string | null { + get governanceURI(): string { let value = this.get("governanceURI"); if (!value || value.kind == ValueKind.NULL) { - return null; + throw new Error("Cannot return null for a required field."); } else { return value.toString(); } } - set governanceURI(value: string | null) { - if (!value) { - this.unset("governanceURI"); - } else { - this.set("governanceURI", Value.fromString(value)); - } + set governanceURI(value: string) { + this.set("governanceURI", Value.fromString(value)); } - get activityLogURI(): string | null { + get activityLogURI(): string { let value = this.get("activityLogURI"); if (!value || value.kind == ValueKind.NULL) { - return null; + throw new Error("Cannot return null for a required field."); } else { return value.toString(); } } - set activityLogURI(value: string | null) { - if (!value) { - this.unset("activityLogURI"); - } else { - this.set("activityLogURI", Value.fromString(value)); - } + set activityLogURI(value: string) { + this.set("activityLogURI", Value.fromString(value)); } - get managerAddress(): string | null { - let value = this.get("managerAddress"); + get contractsRegistryURI(): string { + let value = this.get("contractsRegistryURI"); if (!value || value.kind == ValueKind.NULL) { - return null; + throw new Error("Cannot return null for a required field."); } else { return value.toString(); } } - set managerAddress(value: string | null) { - if (!value) { - this.unset("managerAddress"); - } else { - this.set("managerAddress", Value.fromString(value)); - } + set contractsRegistryURI(value: string) { + this.set("contractsRegistryURI", Value.fromString(value)); } - get contractsRegistryURI(): string | null { - let value = this.get("contractsRegistryURI"); + get managerAddress(): string { + let value = this.get("managerAddress"); if (!value || value.kind == ValueKind.NULL) { - return null; + throw new Error("Cannot return null for a required field."); } else { return value.toString(); } } - set contractsRegistryURI(value: string | null) { - if (!value) { - this.unset("contractsRegistryURI"); - } else { - this.set("contractsRegistryURI", Value.fromString(value)); - } + set managerAddress(value: string) { + this.set("managerAddress", Value.fromString(value)); } } diff --git a/Implementations/Subgraph/daostar/generated/templates.ts b/Implementations/Subgraph/daostar/generated/templates.ts index cc553fed..c4bfe447 100644 --- a/Implementations/Subgraph/daostar/generated/templates.ts +++ b/Implementations/Subgraph/daostar/generated/templates.ts @@ -19,3 +19,13 @@ export class EIP4824Registration extends DataSourceTemplate { ); } } + +export class DAOMetadataTemplate extends DataSourceTemplate { + static create(cid: string): void { + DataSourceTemplate.create("DAOMetadataTemplate", [cid]); + } + + static createWithContext(cid: string, context: DataSourceContext): void { + DataSourceTemplate.createWithContext("DAOMetadataTemplate", [cid], context); + } +} diff --git a/Implementations/Subgraph/daostar/schema.graphql b/Implementations/Subgraph/daostar/schema.graphql index 8d15e2a7..6bf8239f 100644 --- a/Implementations/Subgraph/daostar/schema.graphql +++ b/Implementations/Subgraph/daostar/schema.graphql @@ -1,22 +1,26 @@ type RegistrationInstance @entity { - id: ID! - registrationAddress: Bytes! - daoAddress: Bytes! - daoURI: String! # string - daoName: String - registrationNetwork: RegistrationNetwork! - daoDescription: String - membersURI: String - issuersURI: String - proposalsURI: String - governanceURI: String - activityLogURI: String - managerAddress: String - contractsRegistryURI: String + id: ID! + daoAddress: Bytes! + registrationNetwork: RegistrationNetwork! @relation + registrationAddress: Bytes! + daoURI: String! +} + +type DAOMetadata @entity { + id: ID! + daoName: String! + daoDescription: String! + membersURI: String! + issuersURI: String! + proposalsURI: String! + governanceURI: String! + activityLogURI: String! + contractsRegistryURI: String! + managerAddress: String! } type RegistrationNetwork @entity { - id: ID! - registrations: [RegistrationInstance!] @derivedFrom(field: "registrationNetwork") - chainId: String! + id: ID! + registrations: [RegistrationInstance!] @derivedFrom(field: "registrationNetwork") + chainId: String! } diff --git a/Implementations/Subgraph/daostar/src/mapping.ts b/Implementations/Subgraph/daostar/src/mapping.ts index 77885cc5..a8ed4b37 100644 --- a/Implementations/Subgraph/daostar/src/mapping.ts +++ b/Implementations/Subgraph/daostar/src/mapping.ts @@ -1,8 +1,8 @@ -import { BigInt, ipfs, json, JSONValueKind, log, dataSource } from '@graphprotocol/graph-ts' +import { BigInt, log, dataSource, json, Bytes, JSONValueKind } from '@graphprotocol/graph-ts' import { DAOURIRegistered } from '../generated/EIP4824Index/EIP4824Index' import { DAOURIUpdate } from '../generated/templates/EIP4824Registration/EIP4824Registration' -import { RegistrationInstance, RegistrationNetwork } from '../generated/schema' -import { EIP4824Registration } from '../generated/templates' +import { RegistrationInstance, RegistrationNetwork, DAOMetadata } from '../generated/schema' +import { EIP4824Registration, DAOMetadataTemplate } from '../generated/templates' import { getChainId } from './getChainId' export function handleNewRegistration(event: DAOURIRegistered): void { @@ -11,7 +11,7 @@ export function handleNewRegistration(event: DAOURIRegistered): void { let registrationNetwork = RegistrationNetwork.load(chainName) if (!registrationNetwork) { registrationNetwork = new RegistrationNetwork(chainName) - registrationNetwork.chainId = getChainId().toString(); + registrationNetwork.chainId = getChainId().toString() registrationNetwork.save() } @@ -23,7 +23,7 @@ export function handleNewRegistration(event: DAOURIRegistered): void { EIP4824Registration.create(event.params.daoAddress) let newAddress = event.params.daoAddress.toHex() registrationInstance = new RegistrationInstance(newAddress) - registrationInstance.registrationNetwork = chainName + registrationInstance.registrationNetwork = registrationNetwork.id // Set the relationship correctly registrationInstance.registrationAddress = event.params.daoAddress registrationInstance.daoAddress = event.params.daoAddress registrationInstance.daoURI = 'placeholder' @@ -32,53 +32,92 @@ export function handleNewRegistration(event: DAOURIRegistered): void { } export function handleNewURI(event: DAOURIUpdate): void { - let registrationAddress = event.address.toHex() let registrationInstance = RegistrationInstance.load(registrationAddress) - if (!registrationInstance) log.warning('Invalid Registration: {}', [registrationAddress]) - else { + if (!registrationInstance) { + log.warning('Invalid Registration: {}', [registrationAddress]) + } else { registrationInstance.daoAddress = event.params.daoAddress if (event.params.daoURI) { registrationInstance.daoURI = event.params.daoURI const ipfsHash = event.params.daoURI.substring(event.params.daoURI.length - 46) log.info('Fetching ipfs data for: {}', [ipfsHash]) - let ipfsData = ipfs.cat(ipfsHash) - if (ipfsData) { - log.debug('IPFS data found for : {}', [ipfsHash]) - let daoMetadata = json.fromBytes(ipfsData).toObject() - - const daoName = daoMetadata.get('name') - log.info('My name is: {}', [daoName ? daoName.toString() : 'unknown']) - const daoDescription = daoMetadata.get('description') - log.info('My description is: {}', [ - daoDescription && daoDescription.kind == JSONValueKind.STRING ? daoDescription.toString() : 'unknown', - ]) - const membersURI = daoMetadata.get('membersURI') - const issuersURI = daoMetadata.get('issuersURI') - const proposalsURI = daoMetadata.get('proposalsURI') - const governanceURI = daoMetadata.get('governanceURI') - const activityLogURI = daoMetadata.get('activityLogURI') - const managerAddress = daoMetadata.get('managerAddress'); - const contractsRegistryURI = daoMetadata.get('contractsRegistryURI'); - - registrationInstance.daoName = daoName && daoName.kind == JSONValueKind.STRING ? daoName.toString() : '' - registrationInstance.daoDescription = daoDescription && daoDescription.kind == JSONValueKind.STRING ? daoDescription.toString() : '' - registrationInstance.membersURI = membersURI && membersURI.kind == JSONValueKind.STRING ? membersURI.toString() : '' - registrationInstance.issuersURI = issuersURI && issuersURI.kind == JSONValueKind.STRING ? issuersURI.toString() : '' - registrationInstance.proposalsURI = proposalsURI && proposalsURI.kind == JSONValueKind.STRING ? proposalsURI.toString() : '' - registrationInstance.governanceURI = governanceURI && governanceURI.kind == JSONValueKind.STRING ? governanceURI.toString() : '' - registrationInstance.activityLogURI = activityLogURI && activityLogURI.kind == JSONValueKind.STRING ? activityLogURI.toString() : '' - registrationInstance.contractsRegistryURI = contractsRegistryURI && contractsRegistryURI.kind == JSONValueKind.STRING ? contractsRegistryURI.toString() : '' - registrationInstance.managerAddress = managerAddress && managerAddress.kind == JSONValueKind.STRING ? managerAddress.toString() : '' - registrationInstance.save() // For some reason this does not work without this additional save - } else { - log.warning('IPFS data missing for : {}', [ipfsHash]) - } + DAOMetadataTemplate.create(ipfsHash) // Spawning the file data source template } - registrationInstance.daoURI = event.params.daoURI - // TODO resolve IPFS here registrationInstance.save() } +} + +// Handler to process the fetched file from IPFS +export function handleDAOMetadata(content: Bytes): void { + let metadata = new DAOMetadata(dataSource.stringParam()) + const value = json.fromBytes(content).toObject() + + if (value) { + let daoName = value.get('name') + if (daoName !== null && daoName.kind === JSONValueKind.STRING) { + metadata.daoName = daoName.toString() + } else { + metadata.daoName = '' + } + + let daoDescription = value.get('description') + if (daoDescription !== null && daoDescription.kind === JSONValueKind.STRING) { + metadata.daoDescription = daoDescription.toString() + } else { + metadata.daoDescription = '' + } + + let membersURI = value.get('membersURI') + if (membersURI !== null && membersURI.kind === JSONValueKind.STRING) { + metadata.membersURI = membersURI.toString() + } else { + metadata.membersURI = '' + } + + let issuersURI = value.get('issuersURI') + if (issuersURI !== null && issuersURI.kind === JSONValueKind.STRING) { + metadata.issuersURI = issuersURI.toString() + } else { + metadata.issuersURI = '' + } -} \ No newline at end of file + let proposalsURI = value.get('proposalsURI') + if (proposalsURI !== null && proposalsURI.kind === JSONValueKind.STRING) { + metadata.proposalsURI = proposalsURI.toString() + } else { + metadata.proposalsURI = '' + } + + let governanceURI = value.get('governanceURI') + if (governanceURI !== null && governanceURI.kind === JSONValueKind.STRING) { + metadata.governanceURI = governanceURI.toString() + } else { + metadata.governanceURI = '' + } + + let activityLogURI = value.get('activityLogURI') + if (activityLogURI !== null && activityLogURI.kind === JSONValueKind.STRING) { + metadata.activityLogURI = activityLogURI.toString() + } else { + metadata.activityLogURI = '' + } + + let contractsRegistryURI = value.get('contractsRegistryURI') + if (contractsRegistryURI !== null && contractsRegistryURI.kind === JSONValueKind.STRING) { + metadata.contractsRegistryURI = contractsRegistryURI.toString() + } else { + metadata.contractsRegistryURI = '' + } + + let managerAddress = value.get('managerAddress') + if (managerAddress !== null && managerAddress.kind === JSONValueKind.STRING) { + metadata.managerAddress = managerAddress.toString() + } else { + metadata.managerAddress = '' + } + + metadata.save() + } +} diff --git a/Implementations/Subgraph/daostar/subgraph.yaml b/Implementations/Subgraph/daostar/subgraph.yaml index 183d3ee6..5255302f 100644 --- a/Implementations/Subgraph/daostar/subgraph.yaml +++ b/Implementations/Subgraph/daostar/subgraph.yaml @@ -3,6 +3,7 @@ schema: file: ./schema.graphql features: - ipfsOnEthereumContracts + templates: - name: EIP4824Registration kind: ethereum/contract @@ -11,7 +12,7 @@ templates: abi: EIP4824Registration mapping: kind: ethereum/events - apiVersion: 0.0.6 + apiVersion: 0.0.7 language: wasm/assemblyscript file: ./src/mapping.ts entities: @@ -22,6 +23,18 @@ templates: eventHandlers: - event: DAOURIUpdate(address,string) handler: handleNewURI + + - name: DAOMetadataTemplate + kind: file/ipfs + mapping: + apiVersion: 0.0.7 + language: wasm/assemblyscript + file: ./src/mapping.ts + handler: handleDAOMetadata + entities: + - DAOMetadata + abis: [] # No ABIs required for file data sources + dataSources: - kind: ethereum name: EIP4824Index @@ -32,7 +45,7 @@ dataSources: startBlock: 171329384 mapping: kind: ethereum/events - apiVersion: 0.0.6 + apiVersion: 0.0.7 language: wasm/assemblyscript entities: - NewRegistration