Skip to content

Commit

Permalink
feat: As an Issuer, I want to decode off-chain attestations through t…
Browse files Browse the repository at this point in the history
…he SDK (#433)

Co-authored-by: Satyajeet Kolhapure <kolhapure.satyajeet@gmail.com>
Co-authored-by: Alain Nicolas <alainncls@users.noreply.github.com>
  • Loading branch information
3 people authored Nov 29, 2023
1 parent 16668d0 commit 69d5b71
Show file tree
Hide file tree
Showing 5 changed files with 57 additions and 16 deletions.
13 changes: 4 additions & 9 deletions sdk/examples/attestation/attestationExamples.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Address } from "viem";
import { VeraxSdk } from "../../src/VeraxSdk";
import { Attestation_filter } from "../../.graphclient";

export default class AttestationExamples {
private veraxSdk: VeraxSdk;
Expand All @@ -16,15 +17,9 @@ export default class AttestationExamples {
}

if (methodName.toLowerCase() == "findBy".toLowerCase() || methodName == "") {
console.log(
await this.veraxSdk.attestation.findBy(
2,
0,
{ attester_not: "0x809e815596AbEB3764aBf81BE2DC39fBBAcc9949" },
"attestedDate",
"desc",
),
);
const filter: Attestation_filter | undefined =
argv !== "" ? JSON.parse(argv) : { attester_not: "0x809e815596AbEB3764aBf81BE2DC39fBBAcc9949" };
console.log(await this.veraxSdk.attestation.findBy(2, 0, filter, "attestedDate", "desc"));
}

if (methodName.toLowerCase() == "getRelatedAttestations".toLowerCase() || methodName == "") {
Expand Down
47 changes: 40 additions & 7 deletions sdk/src/dataMapper/AttestationDataMapper.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import BaseDataMapper from "./BaseDataMapper";
import { abiAttestationRegistry } from "../abi/AttestationRegistry";
import { Attestation, AttestationPayload, AttestationWithDecodeObject, Schema } from "../types";
import { Attestation, AttestationPayload, AttestationWithDecodeObject, OffchainData, Schema } from "../types";
import { Attestation_filter, Attestation_orderBy, OrderDirection } from "../../.graphclient";
import { Constants } from "../utils/constants";
import { handleSimulationError } from "../utils/simulationErrorHandler";
import { Address } from "viem";
import { decodeWithRetry, encode } from "../utils/abiCoder";
import { executeTransaction } from "../utils/transactionSender";
import { getIPFSContent } from "../utils/ipfsClient";

export default class AttestationDataMapper extends BaseDataMapper<
Attestation,
Expand Down Expand Up @@ -34,8 +35,7 @@ export default class AttestationDataMapper extends BaseDataMapper<
override async findOneById(id: string) {
const attestation = await super.findOneById(id);
if (attestation !== undefined) {
const schema = (await this.veraxSdk.schema.getSchema(attestation.schemaId)) as Schema;
attestation.decodedPayload = decodeWithRetry(schema.schema, attestation.attestationData as `0x${string}`);
await this.enrichAttestation(attestation);
}
return attestation;
}
Expand All @@ -48,14 +48,47 @@ export default class AttestationDataMapper extends BaseDataMapper<
orderDirection?: OrderDirection,
) {
const attestations = await super.findBy(first, skip, where, orderBy, orderDirection);
attestations.forEach(async (attestation) => {
const schema = (await this.veraxSdk.schema.getSchema(attestation.schemaId)) as Schema;
attestation.decodedPayload = decodeWithRetry(schema.schema, attestation.attestationData as `0x${string}`);
});
await Promise.all(
attestations.map(async (attestation) => {
await this.enrichAttestation(attestation);
}),
);

return attestations;
}

private async enrichAttestation(attestation: Attestation) {
const schema = (await this.veraxSdk.schema.getSchema(attestation.schemaId)) as Schema;
attestation.decodedPayload = decodeWithRetry(schema.schema, attestation.attestationData as `0x${string}`);
// Check if data is stored offchain
if (attestation.schemaId === Constants.OFFCHAIN_DATA_SCHEMA_ID) {
attestation.offchainData = {
schemaId: (attestation.decodedPayload as OffchainData[])[0].schemaId,
uri: (attestation.decodedPayload as OffchainData[])[0].uri,
};
attestation.decodedPayload = {};
if (attestation.offchainData.uri.startsWith("ipfs://")) {
try {
const ipfsHash = attestation.offchainData.uri.split("//")[1];
const response = await getIPFSContent(ipfsHash);
if (response.toString().startsWith("0x")) {
const offchainDataSchema = (await this.veraxSdk.schema.getSchema(
attestation.offchainData.schemaId,
)) as Schema;
attestation.decodedPayload = decodeWithRetry(
offchainDataSchema.schema,
attestation.attestationData as `0x${string}`,
);
} else {
attestation.decodedPayload = response as unknown as object;
}
} catch (error) {
attestation.offchainData.error = (error as Error).message;
}
}
}
}

async getRelatedAttestations(id: string) {
return this.findBy(
undefined,
Expand Down
3 changes: 3 additions & 0 deletions sdk/src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,11 @@ export type Attestation = OnChainAttestation & {
schemaString: string;
decodedData: string[];
decodedPayload: object;
offchainData?: OffchainData;
};

export type OffchainData = { schemaId: string; uri: string; error?: string };

export type OnChainAttestation = {
attestationId: string; // The unique identifier of the attestation.
schemaId: string; // The identifier of the schema this attestation adheres to.
Expand Down
1 change: 1 addition & 0 deletions sdk/src/utils/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ export class Constants {
static readonly RELATIONSHIP_SCHEMA_ID = "0x89bd76e17fd84df8e1e448fa1b46dd8d97f7e8e806552b003f8386a5aebcb9f0";
static readonly NAMED_GRAPH_RELATIONSHIP_SCHEMA_ID =
"0x5003a7832fa2734780a5bf6a1f3940b84c0c66a398e62dd4e7f183fdbc7da6ee";
static readonly OFFCHAIN_DATA_SCHEMA_ID = "0xa288e257097a4bed4166c002cb6911713edacc88e30b6cb2b0104df9c365327d";
}

export enum SDKMode {
Expand Down
9 changes: 9 additions & 0 deletions sdk/src/utils/ipfsClient.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import axios from "axios";

export const getIPFSContent = async (ipfsHash: string): Promise<string> => {
// Use the Cloudflare IPFS gateway URL
const ipfsGatewayUrl = `https://cloudflare-ipfs.com/ipfs/${ipfsHash}`;
// Make a request to the IPFS gateway
const response = await axios.get(ipfsGatewayUrl);
return response.data;
};

0 comments on commit 69d5b71

Please sign in to comment.