From ed5654a523e63dd39cfeb0b4475cf7e248deb5bf Mon Sep 17 00:00:00 2001 From: Alain Nicolas Date: Thu, 21 Dec 2023 10:51:47 +0100 Subject: [PATCH] feat: As a user, I want the indexed objects to contain some "audit" data fix: All indexed objects should have lowercase IDs --- .prettierignore | 1 + subgraph/.env.example | 14 ++- subgraph/README.md | 9 +- subgraph/abis/PortalRegistry.json | 26 +++++ subgraph/networks.json | 2 +- subgraph/package.json | 14 ++- subgraph/schema.graphql | 29 ++++- subgraph/src/attestation-registry.ts | 113 +++++++++++++++++--- subgraph/src/module-registry.ts | 24 ++++- subgraph/src/portal-registry.ts | 70 +++++++++++- subgraph/src/schema-registry.ts | 43 +++++++- subgraph/subgraph.arbitrum-goerli.yaml | 12 +++ subgraph/subgraph.arbitrum-mainnet.yaml | 12 +++ subgraph/subgraph.linea-goerli.yaml | 2 + subgraph/subgraph.linea-mainnet.yaml | 10 +- subgraph/tests/attestation-registry.test.ts | 20 ++-- subgraph/tests/module-registry.test.ts | 7 +- subgraph/tests/portal-registry.test.ts | 18 +++- subgraph/tests/schema-registry.test.ts | 7 +- 19 files changed, 379 insertions(+), 54 deletions(-) diff --git a/.prettierignore b/.prettierignore index f2b415b6..8bcabc73 100644 --- a/.prettierignore +++ b/.prettierignore @@ -21,3 +21,4 @@ dist .pnp.* lcov.info pnpm-lock.yaml +.latest.json diff --git a/subgraph/.env.example b/subgraph/.env.example index c6b631c1..afdf2a7a 100644 --- a/subgraph/.env.example +++ b/subgraph/.env.example @@ -1,8 +1,16 @@ -DEPLOY_ENDPOINT_LINEA_GOERLI= -DEPLOY_ENDPOINT_LINEA_MAINNET= DEPLOY_ENDPOINT_ARBITRUM_GOERLI= DEPLOY_ENDPOINT_ARBITRUM_MAINNET= +DEPLOY_ENDPOINT_ARBITRUM_NOVA= +DEPLOY_ENDPOINT_ARBITRUM_SEPOLIA= +DEPLOY_ENDPOINT_LINEA_GOERLI= +DEPLOY_ENDPOINT_LINEA_MAINNET= + IPFS_ENDPOINT= IPFS_IDENTIFIERS= -SUBGRAPH_NAME_ARBITRUM= + SUBGRAPH_NAME_ARBITRUM_GOERLI= +SUBGRAPH_NAME_ARBITRUM_MAINNET= +SUBGRAPH_NAME_ARBITRUM_NOVA= +SUBGRAPH_NAME_ARBITRUM_SEPOLIA= +SUBGRAPH_NAME_LINEA_GOERLI= +SUBGRAPH_NAME_LINEA_MAINNET= diff --git a/subgraph/README.md b/subgraph/README.md index 38ed4d88..7bfd5135 100644 --- a/subgraph/README.md +++ b/subgraph/README.md @@ -7,17 +7,20 @@ This subgraph aims to index the data generated by the smart contracts of Verax. Our subgraph is deployable on all the networks where Verax is deployed. You can replace `XXX` by the name of the networks you want to work on in the commands below: +- `arbitrum-goerli` +- `arbitrum-mainnet` +- `arbitrum-nova` +- `arbitrum-sepolia` - `linea-goerli` - `linea-mainnet` -- `arbitrum-goerli` -- `arbitrum-one` ### 1. Add secrets 1. Copy the .env.example file to a .env file 2. Fill `DEPLOY_ENDPOINT_XXX` with the endpoint(s) dedicated to network(s) you want to work on 3. Fill `IPFS_ENDPOINT` with your IPFS endpoint (you can get one for free via Infura) -4. Fill `IPFS_IDENTIFIERS` with your IPFS identifiers (you can get them for free via Infura). +4. Fill `IPFS_IDENTIFIERS` with your IPFS identifiers (you can get them for free via Infura) +5. Fill `SUBGRAPH_NAME_XXX` with the name you want to give to the subgraph Note: You need to encode your identifier and secret key to Base64, following this format: `IDENTIFIER:SECRET`. diff --git a/subgraph/abis/PortalRegistry.json b/subgraph/abis/PortalRegistry.json index 783a051a..eca94eda 100644 --- a/subgraph/abis/PortalRegistry.json +++ b/subgraph/abis/PortalRegistry.json @@ -132,6 +132,19 @@ "name": "PortalRegistered", "type": "event" }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "portalAddress", + "type": "address" + } + ], + "name": "PortalRevoked", + "type": "event" + }, { "inputs": [ { @@ -345,6 +358,19 @@ "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [ + { + "internalType": "address", + "name": "id", + "type": "address" + } + ], + "name": "revoke", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, { "inputs": [], "name": "router", diff --git a/subgraph/networks.json b/subgraph/networks.json index 7427d013..e2b8c0a7 100644 --- a/subgraph/networks.json +++ b/subgraph/networks.json @@ -17,7 +17,7 @@ "startBlock": 1454671 } }, - "linea-mainnet": { + "linea": { "AttestationRegistry": { "address": "0x3de3893aa4Cdea029e84e75223a152FD08315138", "startBlock": 346695 diff --git a/subgraph/package.json b/subgraph/package.json index d305ec98..1f6d4ade 100644 --- a/subgraph/package.json +++ b/subgraph/package.json @@ -15,7 +15,7 @@ "license": "MIT", "author": "Consensys", "scripts": { - "build:linea-mainnet": "cp subgraph.linea-mainnet.yaml subgraph.yaml && pnpm run codegen:linea-mainnet && graph build --network linea-mainnet", + "build:linea-mainnet": "cp subgraph.linea-mainnet.yaml subgraph.yaml && pnpm run codegen:linea-mainnet && graph build --network linea", "build:linea-goerli": "cp subgraph.linea-goerli.yaml subgraph.yaml && pnpm run codegen:linea-goerli && graph build --network linea-goerli", "build:arbitrum-goerli": "cp subgraph.arbitrum-goerli.yaml subgraph.yaml && pnpm run codegen:arbitrum-goerli && graph build --network arbitrum-goerli", "build:arbitrum-sepolia": "cp subgraph.arbitrum-sepolia.yaml subgraph.yaml && pnpm run codegen:arbitrum-sepolia && graph build --network arbitrum-sepolia", @@ -27,19 +27,17 @@ "codegen:arbitrum-sepolia": "cp subgraph.arbitrum-sepolia.yaml subgraph.yaml && graph codegen", "codegen:arbitrum-mainnet": "cp subgraph.arbitrum-mainnet.yaml subgraph.yaml && graph codegen", "codegen:arbitrum-nova": "cp subgraph.arbitrum-nova.yaml subgraph.yaml && graph codegen", - "create:linea-mainnet": "source .env && graph create --node $DEPLOY_ENDPOINT_LINEA_MAINNET Consensys/linea-attestation-registry", - "create:linea-goerli": "source .env && graph create --node $DEPLOY_ENDPOINT_LINEA_GOERLI Consensys/linea-attestation-registry", + "create:linea-mainnet": "source .env && graph create --node $DEPLOY_ENDPOINT_LINEA_MAINNET $SUBGRAPH_NAME_LINEA_MAINNET", + "create:linea-goerli": "source .env && graph create --node $DEPLOY_ENDPOINT_LINEA_GOERLI $SUBGRAPH_NAME_LINEA_GOERLI", "create:arbitrum-goerli": "source .env && graph create --node $DEPLOY_ENDPOINT_ARBITRUM_GOERLI Consensys/verax-arbitrum-goerli", "create:arbitrum-sepolia": "source .env && graph create --node $DEPLOY_ENDPOINT_ARBITRUM_SEPOLIA $SUBGRAPH_NAME_ARBITRUM_SEPOLIA", "create:arbitrum-one": "source .env && graph create --node $DEPLOY_ENDPOINT_ARBITRUM_GOERLI Consensys/verax-arbitrum", - "deploy:linea-mainnet": "source .env && cp subgraph.linea-mainnet.yaml subgraph.yaml && pnpm run build:linea-mainnet && graph deploy --network linea-mainnet --node $DEPLOY_ENDPOINT_LINEA_MAINNET --headers \"{\\\"Authorization\\\": \\\"Basic $IPFS_IDENTIFIERS\\\"}\" --ipfs $IPFS_ENDPOINT --version-label v0.0.1 Consensys/linea-attestation-registry", - "deploy:linea-goerli": "source .env && cp subgraph.linea-goerli.yaml subgraph.yaml && pnpm run build:linea-goerli && graph deploy --network linea-goerli --node $DEPLOY_ENDPOINT_LINEA_GOERLI --headers \"{\\\"Authorization\\\": \\\"Basic $IPFS_IDENTIFIERS\\\"}\" --ipfs $IPFS_ENDPOINT --version-label v0.0.5 Consensys/linea-attestation-registry", - "deploy:arbitrum-goerli": "source .env && cp subgraph.arbitrum-goerli.yaml subgraph.yaml && pnpm run build:arbitrum-goerli && graph deploy --network arbitrum-goerli --node $DEPLOY_ENDPOINT_ARBITRUM_GOERLI --headers \"{\\\"Authorization\\\": \\\"Basic $IPFS_IDENTIFIERS\\\"}\" --ipfs $IPFS_ENDPOINT --version-label v0.0.5 Consensys/verax-arbitrum-goerli", + "deploy:linea-mainnet": "source .env && cp subgraph.linea-mainnet.yaml subgraph.yaml && graph deploy --studio $SUBGRAPH_NAME_LINEA_MAINNET", + "deploy:linea-goerli": "source .env && cp subgraph.linea-goerli.yaml subgraph.yaml && graph deploy --studio $SUBGRAPH_NAME_LINEA_GOERLI", + "deploy:arbitrum-goerli": "source .env && cp subgraph.arbitrum-goerli.yaml subgraph.yaml && graph deploy --network arbitrum-goerli --node $DEPLOY_ENDPOINT_ARBITRUM_GOERLI --headers \"{\\\"Authorization\\\": \\\"Basic $IPFS_IDENTIFIERS\\\"}\" --ipfs $IPFS_ENDPOINT --version-label v0.0.5 Consensys/verax-arbitrum-goerli", "deploy:arbitrum-sepolia": "source .env && cp subgraph.arbitrum-sepolia.yaml subgraph.yaml && pnpm run build:arbitrum-sepolia && graph deploy --studio $SUBGRAPH_NAME_ARBITRUM_SEPOLIA", "deploy:arbitrum-mainnet": "source .env && cp subgraph.arbitrum-mainnet.yaml subgraph.yaml && pnpm run build:arbitrum-mainnet && graph deploy --network arbitrum-mainnet --node $DEPLOY_ENDPOINT_ARBITRUM_MAINNET --headers \"{\\\"Authorization\\\": \\\"Basic $IPFS_IDENTIFIERS\\\"}\" --ipfs $IPFS_ENDPOINT --version-label v0.0.5 Consensys/verax-arbitrum", "deploy:arbitrum-nova": "source .env && cp subgraph.arbitrum-nova.yaml subgraph.yaml && pnpm run build:arbitrum-nova && goldsky subgraph deploy 'verax-arbitrum-nova/0.0.5'", - "remove:linea-mainnet": "source .env && graph remove --node $DEPLOY_ENDPOINT_LINEA_MAINNET Consensys/linea-attestation-registry", - "remove:linea-goerli": "source .env && graph remove --node $DEPLOY_ENDPOINT_LINEA_GOERLI Consensys/linea-attestation-registry", "test": "pnpm run codegen:linea-goerli && graph test", "test:coverage": "graph test -c", "test:docker": "graph test -d" diff --git a/subgraph/schema.graphql b/subgraph/schema.graphql index 3f2f98f5..c7c47963 100644 --- a/subgraph/schema.graphql +++ b/subgraph/schema.graphql @@ -1,9 +1,9 @@ type Attestation @entity { id: ID! - schemaId: Bytes! + schema: Schema! replacedBy: Bytes! attester: Bytes! - portal: Bytes! + portal: Portal! attestedDate: BigInt! expirationDate: BigInt! revocationDate: BigInt! @@ -12,8 +12,8 @@ type Attestation @entity { subject: Bytes! encodedSubject: Bytes! attestationData: Bytes! - schemaString: String decodedData: [String!] + auditInformation: AuditInformation! } type Module @entity { @@ -21,6 +21,7 @@ type Module @entity { moduleAddress: Bytes! name: String! description: String! + auditInformation: AuditInformation! } type Portal @entity { @@ -32,6 +33,7 @@ type Portal @entity { description: String! ownerName: String! attestationCounter: Int + auditInformation: AuditInformation! } type Schema @entity { @@ -41,6 +43,7 @@ type Schema @entity { context: String! schema: String! attestationCounter: Int + auditInformation: AuditInformation! } type Counter @entity { @@ -53,10 +56,30 @@ type Counter @entity { type Issuer @entity { id: ID! + auditInformation: AuditInformation! } type RegistryVersion @entity { id: ID! versionNumber: Int timestamp: BigInt + auditInformation: AuditInformation! +} + +type AuditInformation @entity { + id: ID! + creation: Audit! + lastModification: Audit! + modifications: [Audit!]! +} + +type Audit @entity { + id: ID! + blockNumber: BigInt! + transactionHash: Bytes! + transactionTimestamp: BigInt! + fromAddress: Bytes! + toAddress: Bytes + valueTransferred: BigInt + gasPrice: BigInt } diff --git a/subgraph/src/attestation-registry.ts b/subgraph/src/attestation-registry.ts index 56c07b44..9efa75d3 100644 --- a/subgraph/src/attestation-registry.ts +++ b/subgraph/src/attestation-registry.ts @@ -5,20 +5,38 @@ import { AttestationRevoked, VersionUpdated, } from "../generated/AttestationRegistry/AttestationRegistry"; -import { Attestation, Counter, Portal, RegistryVersion, Schema } from "../generated/schema"; +import { Attestation, Audit, AuditInformation, Counter, Portal, RegistryVersion, Schema } from "../generated/schema"; import { BigInt, ByteArray, Bytes, ethereum } from "@graphprotocol/graph-ts"; export function handleAttestationRegistered(event: AttestationRegisteredEvent): void { const attestationRegistryContract = AttestationRegistry.bind(event.address); const attestationData = attestationRegistryContract.getAttestation(event.params.attestationId); - const attestation = new Attestation(event.params.attestationId.toHex()); + const attestation = new Attestation(event.params.attestationId.toHexString().toLowerCase()); - incrementAttestationCount(attestationData.portal.toHexString(), attestationData.schemaId.toHex()); + const audit = new Audit(event.transaction.hash.toHexString().toLowerCase()); + audit.blockNumber = event.block.number; + audit.transactionHash = event.transaction.hash; + audit.transactionTimestamp = event.block.timestamp; + audit.fromAddress = event.transaction.from; + audit.toAddress = event.transaction.to; + audit.valueTransferred = event.transaction.value; + audit.gasPrice = event.transaction.gasPrice; + + audit.save(); + + const auditInformation = new AuditInformation(attestation.id); + auditInformation.creation = audit.id.toLowerCase(); + auditInformation.lastModification = audit.id.toLowerCase(); + auditInformation.modifications = [audit.id.toLowerCase()]; + + auditInformation.save(); + + attestation.auditInformation = auditInformation.id.toLowerCase(); + + incrementAttestationCount(attestationData.portal.toHexString(), attestationData.schemaId.toHexString()); - attestation.schemaId = attestationData.schemaId; attestation.replacedBy = attestationData.replacedBy; attestation.attester = attestationData.attester; - attestation.portal = attestationData.portal; attestation.attestedDate = attestationData.attestedDate; attestation.expirationDate = attestationData.expirationDate; attestation.revocationDate = attestationData.revocationDate; @@ -26,13 +44,15 @@ export function handleAttestationRegistered(event: AttestationRegisteredEvent): attestation.revoked = attestationData.revoked; attestation.encodedSubject = attestationData.subject; attestation.attestationData = attestationData.attestationData; + attestation.schema = attestationData.schemaId.toHexString().toLowerCase(); + attestation.portal = attestationData.portal.toHexString().toLowerCase(); // If the subject looks like an encoded address, decode it to an address const tempSubject = ethereum.decode("address", attestationData.subject); attestation.subject = tempSubject ? tempSubject.toAddress() : attestationData.subject; - // Get matching Schema - const schema = Schema.load(attestationData.schemaId.toHex()); + // Get matching Schemax + const schema = Schema.load(attestation.schema); if (schema) { // Split Schema into a "type fieldName" array @@ -44,9 +64,6 @@ export function handleAttestationRegistered(event: AttestationRegisteredEvent): // Join the types in a single coma-separated string const schemaString = schemaTypes.toString(); - // Add this Schema string to the Attestation Entity - attestation.schemaString = schemaString; - const encodedData = attestationData.attestationData; // Initiate the decoded data in case it's not decoded at all @@ -93,20 +110,64 @@ export function handleAttestationRegistered(event: AttestationRegisteredEvent): export function handleAttestationRevoked(event: AttestationRevoked): void { const attestationRegistryContract = AttestationRegistry.bind(event.address); const attestationData = attestationRegistryContract.getAttestation(event.params.attestationId); - const attestation = Attestation.load(event.params.attestationId.toHex()); + const attestation = Attestation.load(event.params.attestationId.toHexString().toLowerCase()); if (attestation) { attestation.revoked = true; attestation.revocationDate = attestationData.revocationDate; + + const audit = new Audit(event.transaction.hash.toHexString().toLowerCase()); + audit.blockNumber = event.block.number; + audit.transactionHash = event.transaction.hash; + audit.transactionTimestamp = event.block.timestamp; + audit.fromAddress = event.transaction.from; + audit.toAddress = event.transaction.to; + audit.valueTransferred = event.transaction.value; + audit.gasPrice = event.transaction.gasPrice; + + audit.save(); + + const auditInformation = AuditInformation.load(attestation.id); + if (auditInformation !== null) { + auditInformation.lastModification = audit.id.toLowerCase(); + auditInformation.modifications.push(audit.id.toLowerCase()); + + auditInformation.save(); + + attestation.auditInformation = auditInformation.id.toLowerCase(); + } + attestation.save(); } } export function handleAttestationReplaced(event: AttestationReplaced): void { - const attestation = Attestation.load(event.params.attestationId.toHex()); + const attestation = Attestation.load(event.params.attestationId.toHexString().toLowerCase()); if (attestation) { attestation.replacedBy = event.params.replacedBy; + + const audit = new Audit(event.transaction.hash.toHexString().toLowerCase()); + audit.blockNumber = event.block.number; + audit.transactionHash = event.transaction.hash; + audit.transactionTimestamp = event.block.timestamp; + audit.fromAddress = event.transaction.from; + audit.toAddress = event.transaction.to; + audit.valueTransferred = event.transaction.value; + audit.gasPrice = event.transaction.gasPrice; + + audit.save(); + + const auditInformation = AuditInformation.load(attestation.id); + if (auditInformation !== null) { + auditInformation.lastModification = audit.id.toLowerCase(); + auditInformation.modifications.push(audit.id.toLowerCase()); + + auditInformation.save(); + + attestation.auditInformation = auditInformation.id.toLowerCase(); + } + attestation.save(); } } @@ -118,6 +179,32 @@ export function handleVersionUpdated(event: VersionUpdated): void { registryVersion = new RegistryVersion("registry-version"); } + const audit = new Audit(event.transaction.hash.toHexString().toLowerCase()); + audit.blockNumber = event.block.number; + audit.transactionHash = event.transaction.hash; + audit.transactionTimestamp = event.block.timestamp; + audit.fromAddress = event.transaction.from; + audit.toAddress = event.transaction.to; + audit.valueTransferred = event.transaction.value; + audit.gasPrice = event.transaction.gasPrice; + + audit.save(); + + let auditInformation = AuditInformation.load(registryVersion.id); + if (auditInformation === null) { + auditInformation = new AuditInformation(registryVersion.id); + auditInformation.creation = audit.id.toLowerCase(); + auditInformation.lastModification = audit.id.toLowerCase(); + auditInformation.modifications = [audit.id.toLowerCase()]; + } else { + auditInformation.lastModification = audit.id.toLowerCase(); + auditInformation.modifications.push(audit.id.toLowerCase()); + } + + auditInformation.save(); + + registryVersion.auditInformation = auditInformation.id.toLowerCase(); + registryVersion.versionNumber = event.params.version; registryVersion.timestamp = event.block.timestamp; @@ -129,7 +216,7 @@ function valueToString(value: ethereum.Value): string { case ethereum.ValueKind.ADDRESS: return value.toAddress().toHexString(); case ethereum.ValueKind.FIXED_BYTES: - return value.toBytes().toHex(); + return value.toBytes().toHexString().toLowerCase(); case ethereum.ValueKind.BYTES: return value.toString(); case ethereum.ValueKind.INT: diff --git a/subgraph/src/module-registry.ts b/subgraph/src/module-registry.ts index 612dd9f6..fe97f9b9 100644 --- a/subgraph/src/module-registry.ts +++ b/subgraph/src/module-registry.ts @@ -1,8 +1,28 @@ import { ModuleRegistered as ModuleRegisteredEvent } from "../generated/ModuleRegistry/ModuleRegistry"; -import { Counter, Module } from "../generated/schema"; +import { Audit, AuditInformation, Counter, Module } from "../generated/schema"; export function handleModuleRegistered(event: ModuleRegisteredEvent): void { - const module = new Module(event.params.moduleAddress.toHexString()); + const module = new Module(event.params.moduleAddress.toHexString().toLowerCase()); + + const audit = new Audit(event.transaction.hash.toHexString().toLowerCase()); + audit.blockNumber = event.block.number; + audit.transactionHash = event.transaction.hash; + audit.transactionTimestamp = event.block.timestamp; + audit.fromAddress = event.transaction.from; + audit.toAddress = event.transaction.to; + audit.valueTransferred = event.transaction.value; + audit.gasPrice = event.transaction.gasPrice; + + audit.save(); + + const auditInformation = new AuditInformation(module.id); + auditInformation.creation = audit.id.toLowerCase(); + auditInformation.lastModification = audit.id.toLowerCase(); + auditInformation.modifications = [audit.id.toLowerCase()]; + + auditInformation.save(); + + module.auditInformation = auditInformation.id.toLowerCase(); incrementModulesCount(); diff --git a/subgraph/src/portal-registry.ts b/subgraph/src/portal-registry.ts index 445bbd72..45e162b0 100644 --- a/subgraph/src/portal-registry.ts +++ b/subgraph/src/portal-registry.ts @@ -4,13 +4,34 @@ import { IssuerRemoved, PortalRegistered as PortalRegisteredEvent, PortalRegistry, + PortalRevoked as PortalRevokedEvent, } from "../generated/PortalRegistry/PortalRegistry"; -import { Counter, Issuer, Portal } from "../generated/schema"; +import { Audit, AuditInformation, Counter, Issuer, Portal } from "../generated/schema"; export function handlePortalRegistered(event: PortalRegisteredEvent): void { const contract = PortalRegistry.bind(event.address); const portalData = contract.getPortalByAddress(event.params.portalAddress); - const portal = new Portal(event.params.portalAddress.toHexString()); + const portal = new Portal(event.params.portalAddress.toHexString().toLowerCase()); + + const audit = new Audit(event.transaction.hash.toHexString().toLowerCase()); + audit.blockNumber = event.block.number; + audit.transactionHash = event.transaction.hash; + audit.transactionTimestamp = event.block.timestamp; + audit.fromAddress = event.transaction.from; + audit.toAddress = event.transaction.to; + audit.valueTransferred = event.transaction.value; + audit.gasPrice = event.transaction.gasPrice; + + audit.save(); + + const auditInformation = new AuditInformation(portal.id); + auditInformation.creation = audit.id.toLowerCase(); + auditInformation.lastModification = audit.id.toLowerCase(); + auditInformation.modifications = [audit.id.toLowerCase()]; + + auditInformation.save(); + + portal.auditInformation = auditInformation.id.toLowerCase(); incrementPortalsCount(); @@ -25,8 +46,46 @@ export function handlePortalRegistered(event: PortalRegisteredEvent): void { portal.save(); } +export function handlePortalRevoked(event: PortalRevokedEvent): void { + const portalId = event.params.portalAddress.toHexString(); + const portal = Portal.load(portalId); + + if (portal != null) { + // Delete the Portal entity + store.remove("Portal", portalId); + } + + const auditInformation = AuditInformation.load(portalId); + + if (auditInformation != null) { + // Delete the AuditInformation entity + store.remove("AuditInformation", portalId); + } +} + export function handleIssuerAdded(event: IssuerAdded): void { const issuer = new Issuer(event.params.issuerAddress.toHexString()); + + const audit = new Audit(event.transaction.hash.toHexString().toLowerCase()); + audit.blockNumber = event.block.number; + audit.transactionHash = event.transaction.hash; + audit.transactionTimestamp = event.block.timestamp; + audit.fromAddress = event.transaction.from; + audit.toAddress = event.transaction.to; + audit.valueTransferred = event.transaction.value; + audit.gasPrice = event.transaction.gasPrice; + + audit.save(); + + const auditInformation = new AuditInformation(issuer.id); + auditInformation.creation = audit.id.toLowerCase(); + auditInformation.lastModification = audit.id.toLowerCase(); + auditInformation.modifications = [audit.id.toLowerCase()]; + + auditInformation.save(); + + issuer.auditInformation = auditInformation.id.toLowerCase(); + issuer.save(); } @@ -38,6 +97,13 @@ export function handleIssuerRemoved(event: IssuerRemoved): void { // Delete the Issuer entity store.remove("Issuer", issuerId); } + + const auditInformation = AuditInformation.load(issuerId); + + if (auditInformation != null) { + // Delete the AuditInformation entity + store.remove("AuditInformation", issuerId); + } } function incrementPortalsCount(): void { diff --git a/subgraph/src/schema-registry.ts b/subgraph/src/schema-registry.ts index 6b93c4b4..0fc27cdd 100644 --- a/subgraph/src/schema-registry.ts +++ b/subgraph/src/schema-registry.ts @@ -3,11 +3,31 @@ import { SchemaCreated as SchemaCreatedEvent, SchemaRegistry, } from "../generated/SchemaRegistry/SchemaRegistry"; -import { Counter, Schema } from "../generated/schema"; +import { Audit, AuditInformation, Counter, Schema } from "../generated/schema"; export function handleSchemaCreated(event: SchemaCreatedEvent): void { const schema = new Schema(event.params.id.toHexString()); + const audit = new Audit(event.transaction.hash.toHexString().toLowerCase()); + audit.blockNumber = event.block.number; + audit.transactionHash = event.transaction.hash; + audit.transactionTimestamp = event.block.timestamp; + audit.fromAddress = event.transaction.from; + audit.toAddress = event.transaction.to; + audit.valueTransferred = event.transaction.value; + audit.gasPrice = event.transaction.gasPrice; + + audit.save(); + + const auditInformation = new AuditInformation(schema.id); + auditInformation.creation = audit.id.toLowerCase(); + auditInformation.lastModification = audit.id.toLowerCase(); + auditInformation.modifications = [audit.id.toLowerCase()]; + + auditInformation.save(); + + schema.auditInformation = auditInformation.id.toLowerCase(); + incrementSchemasCount(); schema.name = event.params.name; @@ -26,6 +46,27 @@ export function handleSchemaContextUpdated(event: SchemaContextUpdated): void { const schema = Schema.load(event.params.id.toHexString()); if (schema !== null) { + const audit = new Audit(event.transaction.hash.toHexString().toLowerCase()); + audit.blockNumber = event.block.number; + audit.transactionHash = event.transaction.hash; + audit.transactionTimestamp = event.block.timestamp; + audit.fromAddress = event.transaction.from; + audit.toAddress = event.transaction.to; + audit.valueTransferred = event.transaction.value; + audit.gasPrice = event.transaction.gasPrice; + + audit.save(); + + const auditInformation = AuditInformation.load(schema.id); + if (auditInformation !== null) { + auditInformation.lastModification = audit.id.toLowerCase(); + auditInformation.modifications.push(audit.id.toLowerCase()); + + auditInformation.save(); + + schema.auditInformation = auditInformation.id.toLowerCase(); + } + schema.context = newContext; schema.save(); } diff --git a/subgraph/subgraph.arbitrum-goerli.yaml b/subgraph/subgraph.arbitrum-goerli.yaml index ea22df0c..5cb4c8cf 100644 --- a/subgraph/subgraph.arbitrum-goerli.yaml +++ b/subgraph/subgraph.arbitrum-goerli.yaml @@ -21,6 +21,12 @@ dataSources: eventHandlers: - event: AttestationRegistered(indexed bytes32) handler: handleAttestationRegistered + - event: AttestationRevoked(bytes32) + handler: handleAttestationRevoked + - event: AttestationReplaced(bytes32,bytes32) + handler: handleAttestationReplaced + - event: VersionUpdated(uint16) + handler: handleVersionUpdated file: ./src/attestation-registry.ts - kind: ethereum name: ModuleRegistry @@ -61,6 +67,12 @@ dataSources: eventHandlers: - event: PortalRegistered(string,string,address) handler: handlePortalRegistered + - event: PortalRevoked(address) + handler: handlePortalRevoked + - event: IssuerAdded(address) + handler: handleIssuerAdded + - event: IssuerRemoved(address) + handler: handleIssuerRemoved file: ./src/portal-registry.ts - kind: ethereum name: SchemaRegistry diff --git a/subgraph/subgraph.arbitrum-mainnet.yaml b/subgraph/subgraph.arbitrum-mainnet.yaml index 03e8bbaa..3a24c675 100644 --- a/subgraph/subgraph.arbitrum-mainnet.yaml +++ b/subgraph/subgraph.arbitrum-mainnet.yaml @@ -21,6 +21,12 @@ dataSources: eventHandlers: - event: AttestationRegistered(indexed bytes32) handler: handleAttestationRegistered + - event: AttestationRevoked(bytes32) + handler: handleAttestationRevoked + - event: AttestationReplaced(bytes32,bytes32) + handler: handleAttestationReplaced + - event: VersionUpdated(uint16) + handler: handleVersionUpdated file: ./src/attestation-registry.ts - kind: ethereum name: ModuleRegistry @@ -61,6 +67,12 @@ dataSources: eventHandlers: - event: PortalRegistered(string,string,address) handler: handlePortalRegistered + - event: PortalRevoked(address) + handler: handlePortalRevoked + - event: IssuerAdded(address) + handler: handleIssuerAdded + - event: IssuerRemoved(address) + handler: handleIssuerRemoved file: ./src/portal-registry.ts - kind: ethereum name: SchemaRegistry diff --git a/subgraph/subgraph.linea-goerli.yaml b/subgraph/subgraph.linea-goerli.yaml index 81b5c355..a2ced337 100644 --- a/subgraph/subgraph.linea-goerli.yaml +++ b/subgraph/subgraph.linea-goerli.yaml @@ -67,6 +67,8 @@ dataSources: eventHandlers: - event: PortalRegistered(string,string,address) handler: handlePortalRegistered + - event: PortalRevoked(address) + handler: handlePortalRevoked - event: IssuerAdded(address) handler: handleIssuerAdded - event: IssuerRemoved(address) diff --git a/subgraph/subgraph.linea-mainnet.yaml b/subgraph/subgraph.linea-mainnet.yaml index 2daa94c5..80b66adc 100644 --- a/subgraph/subgraph.linea-mainnet.yaml +++ b/subgraph/subgraph.linea-mainnet.yaml @@ -4,7 +4,7 @@ schema: dataSources: - kind: ethereum name: AttestationRegistry - network: linea-mainnet + network: linea source: abi: AttestationRegistry address: "0x3de3893aa4Cdea029e84e75223a152FD08315138" @@ -30,7 +30,7 @@ dataSources: file: ./src/attestation-registry.ts - kind: ethereum name: ModuleRegistry - network: linea-mainnet + network: linea source: abi: ModuleRegistry address: "0xf851513A732996F22542226341748f3C9978438f" @@ -50,7 +50,7 @@ dataSources: file: ./src/module-registry.ts - kind: ethereum name: PortalRegistry - network: linea-mainnet + network: linea source: abi: PortalRegistry address: "0xd5d61e4ECDf6d46A63BfdC262af92544DFc19083" @@ -67,6 +67,8 @@ dataSources: eventHandlers: - event: PortalRegistered(string,string,address) handler: handlePortalRegistered + - event: PortalRevoked(address) + handler: handlePortalRevoked - event: IssuerAdded(address) handler: handleIssuerAdded - event: IssuerRemoved(address) @@ -74,7 +76,7 @@ dataSources: file: ./src/portal-registry.ts - kind: ethereum name: SchemaRegistry - network: linea-mainnet + network: linea source: abi: SchemaRegistry address: "0x0f95dCec4c7a93F2637eb13b655F2223ea036B59" diff --git a/subgraph/tests/attestation-registry.test.ts b/subgraph/tests/attestation-registry.test.ts index 68252db9..73bb7a6e 100644 --- a/subgraph/tests/attestation-registry.test.ts +++ b/subgraph/tests/attestation-registry.test.ts @@ -59,7 +59,7 @@ describe("AttestationRegistry", () => { assert.bytesEquals(result.attestationData, attestationData); }); - test("Should create a new Attestation entity", () => { + test("Should create a new Attestation entity and audit data", () => { assert.entityCount("Attestation", 0); const attestationRegisteredEvent = createAttestationRegisteredEvent(attestationId); @@ -69,7 +69,7 @@ describe("AttestationRegistry", () => { assert.entityCount("Attestation", 1); assert.fieldEquals("Attestation", attestationId.toHexString(), "id", attestationId.toHexString()); - assert.fieldEquals("Attestation", attestationId.toHexString(), "schemaId", schemaId.toHexString()); + assert.fieldEquals("Attestation", attestationId.toHexString(), "schema", schemaId.toHexString()); assert.fieldEquals("Attestation", attestationId.toHexString(), "replacedBy", replacedBy.toHexString()); assert.fieldEquals("Attestation", attestationId.toHexString(), "attester", attester.toHexString()); assert.fieldEquals("Attestation", attestationId.toHexString(), "portal", portal.toHexString()); @@ -90,6 +90,11 @@ describe("AttestationRegistry", () => { assert.fieldEquals("Attestation", attestationId.toHexString(), "revoked", revoked.toString()); assert.fieldEquals("Attestation", attestationId.toHexString(), "subject", subject.toHexString()); assert.fieldEquals("Attestation", attestationId.toHexString(), "attestationData", attestationData.toHexString()); + + assert.entityCount("AuditInformation", 1); + assert.fieldEquals("AuditInformation", attestationId.toHexString(), "id", attestationId.toHexString()); + + assert.entityCount("Audit", 1); }); test("Should increment the attestations Counter", () => { @@ -116,6 +121,7 @@ describe("AttestationRegistry", () => { portalEntity.ownerName = "ownerName"; portalEntity.ownerAddress = Address.fromString("e75be6f9418710fd516fa82afb3aad07e11a0f1b"); portalEntity.attestationCounter = 0; + portalEntity.auditInformation = portal.toHexString(); portalEntity.save(); assert.entityCount("Portal", 1); @@ -139,6 +145,7 @@ describe("AttestationRegistry", () => { schemaEntity.context = "context"; schemaEntity.schema = "schemaString"; schemaEntity.attestationCounter = 0; + schemaEntity.auditInformation = schemaId.toHexString(); schemaEntity.save(); assert.entityCount("Schema", 1); @@ -159,6 +166,7 @@ describe("AttestationRegistry", () => { schema.context = "context"; schema.schema = "uint256[] providers, bytes32[] hashes, uint64[] issuanceDates, uint64[] expirationDates, uint16 providerMapVersion"; + schema.auditInformation = schemaId.toHexString(); schema.save(); const attestationRegisteredEvent = createAttestationRegisteredEvent(attestationId); @@ -168,7 +176,7 @@ describe("AttestationRegistry", () => { assert.entityCount("Attestation", 1); assert.fieldEquals("Attestation", attestationId.toHexString(), "id", attestationId.toHexString()); - assert.fieldEquals("Attestation", attestationId.toHexString(), "schemaId", schemaId.toHexString()); + assert.fieldEquals("Attestation", attestationId.toHexString(), "schema", schemaId.toHexString()); assert.fieldEquals("Attestation", attestationId.toHexString(), "replacedBy", replacedBy.toHexString()); assert.fieldEquals("Attestation", attestationId.toHexString(), "attester", attester.toHexString()); assert.fieldEquals("Attestation", attestationId.toHexString(), "portal", portal.toHexString()); @@ -189,12 +197,6 @@ describe("AttestationRegistry", () => { assert.fieldEquals("Attestation", attestationId.toHexString(), "revoked", revoked.toString()); assert.fieldEquals("Attestation", attestationId.toHexString(), "subject", subject.toHexString()); assert.fieldEquals("Attestation", attestationId.toHexString(), "attestationData", attestationData.toHexString()); - assert.fieldEquals( - "Attestation", - attestationId.toHexString(), - "schemaString", - "uint256[],bytes32[],uint64[],uint64[],uint16", - ); assert.fieldEquals( "Attestation", attestationId.toHexString(), diff --git a/subgraph/tests/module-registry.test.ts b/subgraph/tests/module-registry.test.ts index 3ca7796a..c7c3ab6c 100644 --- a/subgraph/tests/module-registry.test.ts +++ b/subgraph/tests/module-registry.test.ts @@ -16,7 +16,7 @@ describe("handleModuleRegistered()", () => { clearStore(); }); - test("Should create a new Module entity", () => { + test("Should create a new Module entity and audit data", () => { assert.entityCount("Module", 0); const moduleRegisteredEvent = createModuleRegisteredEvent(moduleAddress, moduleName, moduleDescription); @@ -29,6 +29,11 @@ describe("handleModuleRegistered()", () => { assert.fieldEquals("Module", "0x" + moduleAddress, "name", moduleName); assert.fieldEquals("Module", "0x" + moduleAddress, "description", moduleDescription); assert.fieldEquals("Module", "0x" + moduleAddress, "moduleAddress", "0x" + moduleAddress); + + assert.entityCount("AuditInformation", 1); + assert.fieldEquals("AuditInformation", "0x" + moduleAddress, "id", "0x" + moduleAddress); + + assert.entityCount("Audit", 1); }); test("Should increment the modules Counter", () => { diff --git a/subgraph/tests/portal-registry.test.ts b/subgraph/tests/portal-registry.test.ts index 088faa2a..41515860 100644 --- a/subgraph/tests/portal-registry.test.ts +++ b/subgraph/tests/portal-registry.test.ts @@ -1,9 +1,9 @@ import { afterEach, assert, beforeAll, clearStore, createMockedFunction, describe, test } from "matchstick-as"; import { Address, ethereum } from "@graphprotocol/graph-ts"; import { - PortalRegistered as PortalRegisteredEvent, IssuerAdded as IssuerAddedEvent, IssuerRemoved as IssuerRemovedEvent, + PortalRegistered as PortalRegisteredEvent, PortalRegistry, } from "../generated/PortalRegistry/PortalRegistry"; import { newTypedMockEvent } from "matchstick-as/assembly/defaults"; @@ -63,7 +63,7 @@ describe("handlePortalRegistered()", () => { assert.stringEquals(result.ownerName, ownerName); }); - test("Should create a new Portal entity", () => { + test("Should create a new Portal entity and audit data", () => { assert.entityCount("Portal", 0); const portalRegisteredEvent = createPortalRegisteredEvent(portalAddress, name, description); @@ -76,8 +76,15 @@ describe("handlePortalRegistered()", () => { assert.fieldEquals("Portal", portalAddress.toHexString(), "name", name); assert.fieldEquals("Portal", portalAddress.toHexString(), "description", description); assert.fieldEquals("Portal", portalAddress.toHexString(), "attestationCounter", "0"); + + assert.entityCount("AuditInformation", 1); + assert.fieldEquals("AuditInformation", portalAddress.toHexString(), "id", portalAddress.toHexString()); + + assert.entityCount("Audit", 1); }); + // TODO: test `handlePortalRevoked` + test("Should increment the portals Counter", () => { assert.entityCount("Portal", 0); assert.entityCount("Counter", 0); @@ -96,7 +103,7 @@ describe("handleIssuerAdded()", () => { clearStore(); }); - test("Should create a new Issuer entity", () => { + test("Should create a new Issuer entity and audit data", () => { assert.entityCount("Issuer", 0); const issuerAddedEvent = newTypedMockEvent(); issuerAddedEvent.address = portalRegistryAddress; @@ -108,6 +115,11 @@ describe("handleIssuerAdded()", () => { assert.entityCount("Issuer", 1); assert.fieldEquals("Issuer", portalAddress.toHexString(), "id", "0x" + issuerAddress); + + assert.entityCount("AuditInformation", 1); + assert.fieldEquals("AuditInformation", portalAddress.toHexString(), "id", portalAddress.toHexString()); + + assert.entityCount("Audit", 1); }); }); diff --git a/subgraph/tests/schema-registry.test.ts b/subgraph/tests/schema-registry.test.ts index 1d99623b..5536843d 100644 --- a/subgraph/tests/schema-registry.test.ts +++ b/subgraph/tests/schema-registry.test.ts @@ -15,7 +15,7 @@ describe("handleSchemaCreated()", () => { clearStore(); }); - test("Should create a new Schema entity", () => { + test("Should create a new Schema entity and audit data", () => { assert.entityCount("Schema", 0); const schemaCreatedEvent = createSchemaCreatedEvent( @@ -36,6 +36,11 @@ describe("handleSchemaCreated()", () => { assert.fieldEquals("Schema", schemaId, "context", schemaContext); assert.fieldEquals("Schema", schemaId, "schema", schemaString); assert.fieldEquals("Schema", schemaId, "attestationCounter", "0"); + + assert.entityCount("AuditInformation", 1); + assert.fieldEquals("AuditInformation", schemaId, "id", schemaId); + + assert.entityCount("Audit", 1); }); test("Should increment the schemas Counter", () => {