From e59b4b6aba8746bd41e399a2b49762262f906d7a Mon Sep 17 00:00:00 2001 From: Alain Nicolas Date: Tue, 26 Sep 2023 15:23:42 +0200 Subject: [PATCH] feat: As a user, I want to get decoded Attestation data via the subgraph --- subgraph/schema.graphql | 2 + subgraph/src/attestation-registry.ts | 81 ++++++++++++++++++++++++++-- 2 files changed, 79 insertions(+), 4 deletions(-) diff --git a/subgraph/schema.graphql b/subgraph/schema.graphql index ba4e6e18..9ceb0218 100644 --- a/subgraph/schema.graphql +++ b/subgraph/schema.graphql @@ -11,6 +11,8 @@ type Attestation @entity { revoked: Boolean! subject: Bytes! attestationData: Bytes! + schemaString: String + decodedData:[String!] } type Module @entity { diff --git a/subgraph/src/attestation-registry.ts b/subgraph/src/attestation-registry.ts index 028ba275..3d2c1b4c 100644 --- a/subgraph/src/attestation-registry.ts +++ b/subgraph/src/attestation-registry.ts @@ -2,12 +2,12 @@ import { AttestationRegistered as AttestationRegisteredEvent, AttestationRegistry, } from "../generated/AttestationRegistry/AttestationRegistry"; -import { Attestation } from "../generated/schema"; -import { BigInt } from "@graphprotocol/graph-ts"; +import { Attestation, Schema } from "../generated/schema"; +import { BigInt, ByteArray, Bytes, ethereum } from "@graphprotocol/graph-ts"; export function handleAttestationRegistered(event: AttestationRegisteredEvent): void { - const contract = AttestationRegistry.bind(event.address); - const attestationData = contract.getAttestation(event.params.attestationId); + const attestationRegistryContract = AttestationRegistry.bind(event.address); + const attestationData = attestationRegistryContract.getAttestation(event.params.attestationId); const attestation = new Attestation(event.params.attestationId.toHex()); attestation.schemaId = attestationData.schemaId; @@ -22,5 +22,78 @@ export function handleAttestationRegistered(event: AttestationRegisteredEvent): attestation.subject = attestationData.subject; attestation.attestationData = attestationData.attestationData; + // Get matching Schema + const schema = Schema.load(attestationData.schemaId.toHex()); + + if (schema) { + // Split Schema into a "type field name" array + const splitSchema = schema.schema.split(","); + + // Keep only the Schema's types + const schemaTypes = splitSchema.map((item) => item.trim().split(" ")[0]); + + // Join the types in a single coma-separated string + const schemaString = schemaTypes.toString(); + + // Add this Schema string to the Attestation Entity + attestation.schemaString = schemaString; + + // Change attestation encoded data into an encoded Tuple + const encodedData = attestationData.attestationData; + const tuplePrefix = ByteArray.fromHexString("0x0000000000000000000000000000000000000000000000000000000000000020"); + const encodedDataAsTuple = new Uint8Array(tuplePrefix.length + encodedData.length); + encodedDataAsTuple.set(tuplePrefix, 0); + encodedDataAsTuple.set(encodedData, tuplePrefix.length); + + // Decode the tuple + const decoded = ethereum.decode("(" + schemaString + ")", Bytes.fromUint8Array(encodedDataAsTuple)); + + // Initiate the decoded data in case it's not decoded at all + attestation.decodedData = ["NOT DECODED"]; + + // If the decode function went through, save it as an Array of Strings + if (decoded) { + const tempStringArray: string[] = []; + + // Make the decoded data into a Tuple + const tupleValue = decoded.toTuple(); + + // Convert every field of the Tuple into a String + for (let i = 0; i < tupleValue.length; i++) { + tempStringArray.push(valueToString(tupleValue[i])); + } + + // Add this decoded Array to the Attestation Entity + attestation.decodedData = tempStringArray; + } + } + attestation.save(); } + +function valueToString(value: ethereum.Value): string { + switch (value.kind) { + case ethereum.ValueKind.ADDRESS: + return "ADDRESS"; + case ethereum.ValueKind.FIXED_BYTES: + return "FIXED_BYTES"; + case ethereum.ValueKind.BYTES: + return "BYTES"; + case ethereum.ValueKind.INT: + return "INT"; + case ethereum.ValueKind.UINT: + return "UINT"; + case ethereum.ValueKind.BOOL: + return "BOOL"; + case ethereum.ValueKind.STRING: + return "STRING"; + case ethereum.ValueKind.FIXED_ARRAY: + return "FIXED_ARRAY"; + case ethereum.ValueKind.ARRAY: + return "ARRAY"; + case ethereum.ValueKind.TUPLE: + return "TUPLE"; + default: + return "UNKNOWN"; + } +}