diff --git a/libs/credentials/src/types/index.ts b/libs/credentials/src/types/index.ts index b6943e65..6bc1d5b4 100644 --- a/libs/credentials/src/types/index.ts +++ b/libs/credentials/src/types/index.ts @@ -32,6 +32,9 @@ export type EASContext = { queryGraph: (query: string) => Promise attester?: BigNumberish schemaId?: BigNumberish + revocable?: boolean + revoked?: boolean + isOffchain?: boolean } export type Context = Web2Context | BlockchainContext | EASContext diff --git a/libs/credentials/src/validators/easAttestations/index.test.ts b/libs/credentials/src/validators/easAttestations/index.test.ts index 8e409df0..f81d50ce 100644 --- a/libs/credentials/src/validators/easAttestations/index.test.ts +++ b/libs/credentials/src/validators/easAttestations/index.test.ts @@ -6,62 +6,205 @@ describe("EASAttestations", () => { queryGraph: jest.fn() } + queryGraphMocked.queryGraph.mockReturnValue([ + { + id: "0x52561c95029d9f2335839ddc96a69ee9737a18e2a781e64659b7bd645ccb8efc", + recipient: "0x9aB3971e1b065701C72C5f3cAFbF33118dC51ae8", + attester: "0x63A35A52c0ac206108EBbf559E4C7109dAd281d3", + revocable: true, + revoked: false, + schemaId: + "0xe2636f31239f7948afdd9a9c477048b7fc2a089c347af60e3aa1251e5bf63e5c", + isOffchain: false + }, + { + id: "0xee06a022c7d55f67bac213d6b2cd384a899ef79a57f1f5f148e45c313b4fdebe", + recipient: "0x9aB3971e1b065701C72C5f3cAFbF33118dC51ae8", + attester: "0x63A35A52c0ac206108EBbf559E4C7109dAd281d3", + revocable: true, + revoked: false, + schemaId: + "0xe2636f31239f7948afdd9a9c477048b7fc2a089c347af60e3aa1251e5bf63e5c", + isOffchain: false + }, + { + id: "0xfbc0f1aac4379c18fa9a5b6493825234a8ca82a2a296148465d150c2e64c6202", + recipient: "0x0000000000000000000000000000000000000000", + attester: "0x63A35A52c0ac206108EBbf559E4C7109dAd281d3", + revocable: true, + revoked: false, + schemaId: + "0xe2636f31239f7948afdd9a9c477048b7fc2a089c347af60e3aa1251e5bf63e5c", + isOffchain: false + }, + { + id: "0x227510204bcfe7b543388b82c6e02aafe7b0d0a20e4f159794e8121611aa601b", + recipient: "0x9aB3971e1b065701C72C5f3cAFbF33118dC51ae8", + attester: "0x63A35A52c0ac206108EBbf559E4C7109dAd281d3", + revocable: true, + revoked: false, + schemaId: + "0xe2636f31239f7948afdd9a9c477048b7fc2a089c347af60e3aa1251e5bf63e5c", + isOffchain: false + } + ]) + it("Should return true if an account has greater than or equal to 3 attestations", async () => { - queryGraphMocked.queryGraph.mockReturnValue([ + const result = await validateCredentials( { - id: "0x52561c95029d9f2335839ddc96a69ee9737a18e2a781e64659b7bd645ccb8efc", - recipient: "0x9aB3971e1b065701C72C5f3cAFbF33118dC51ae8" + id: easAttestations.id, + criteria: { + minAttestations: 3 + } }, { - id: "0xee06a022c7d55f67bac213d6b2cd384a899ef79a57f1f5f148e45c313b4fdebe", - recipient: "0x9aB3971e1b065701C72C5f3cAFbF33118dC51ae8" + recipient: "0x9aB3971e1b065701C72C5f3cAFbF33118dC51ae8", + queryGraph: queryGraphMocked.queryGraph + } + ) + + expect(result).toBeTruthy() + }) + + it("Should return true if the given optional criterias are satisfied", async () => { + const result = await validateCredentials( + { + id: easAttestations.id, + criteria: { + minAttestations: 1 + } }, { - id: "0xfbc0f1aac4379c18fa9a5b6493825234a8ca82a2a296148465d150c2e64c6202", - recipient: "0x0000000000000000000000000000000000000000" + recipient: "0x9aB3971e1b065701C72C5f3cAFbF33118dC51ae8", + queryGraph: queryGraphMocked.queryGraph, + attester: "0x63A35A52c0ac206108EBbf559E4C7109dAd281d3", + schemaId: + "0xe2636f31239f7948afdd9a9c477048b7fc2a089c347af60e3aa1251e5bf63e5c", + revocable: true, + revoked: false, + isOffchain: false + } + ) + + expect(result).toBeTruthy() + }) + + it("Should return false if the attester optional criteria doesn't match", async () => { + const result = await validateCredentials( + { + id: easAttestations.id, + criteria: { + minAttestations: 1 + } }, { - id: "0x227510204bcfe7b543388b82c6e02aafe7b0d0a20e4f159794e8121611aa601b", - recipient: "0x9aB3971e1b065701C72C5f3cAFbF33118dC51ae8" + recipient: "0x9aB3971e1b065701C72C5f3cAFbF33118dC51ae8", + queryGraph: queryGraphMocked.queryGraph, + attester: "0x63A35A52c0ac206108EBbf559E4C7109dAd281d4", + schemaId: + "0xe2636f31239f7948afdd9a9c477048b7fc2a089c347af60e3aa1251e5bf63e5c", + revocable: true, + revoked: false, + isOffchain: false } - ]) + ) + expect(result).toBeFalsy() + }) + + it("Should return false if the schemaId optional criteria doesn't match", async () => { const result = await validateCredentials( { id: easAttestations.id, criteria: { - minAttestations: 3 + minAttestations: 1 } }, { recipient: "0x9aB3971e1b065701C72C5f3cAFbF33118dC51ae8", - queryGraph: queryGraphMocked.queryGraph + queryGraph: queryGraphMocked.queryGraph, + attester: "0x63A35A52c0ac206108EBbf559E4C7109dAd281d3", + schemaId: + "0xe2636f31239f7948afdd9a9c477048b7fc2a089c347af60e3aa1251e5bf63e5d", + revocable: true, + revoked: false, + isOffchain: false } ) - expect(result).toBeTruthy() + expect(result).toBeFalsy() }) - it("Should return false if an account has less than 3 attestations", async () => { - queryGraphMocked.queryGraph.mockReturnValue([ + it("Should return false if the revocable optional criteria doesn't match", async () => { + const result = await validateCredentials( { - id: "0x52561c95029d9f2335839ddc96a69ee9737a18e2a781e64659b7bd645ccb8efc", - recipient: "0x0000000000000000000000000000000000000000" + id: easAttestations.id, + criteria: { + minAttestations: 1 + } }, { - id: "0xee06a022c7d55f67bac213d6b2cd384a899ef79a57f1f5f148e45c313b4fdebe", - recipient: "0x0000000000000000000000000000000000000000" + recipient: "0x9aB3971e1b065701C72C5f3cAFbF33118dC51ae8", + queryGraph: queryGraphMocked.queryGraph, + attester: "0x63A35A52c0ac206108EBbf559E4C7109dAd281d3", + schemaId: + "0xe2636f31239f7948afdd9a9c477048b7fc2a089c347af60e3aa1251e5bf63e5c", + revocable: false, + revoked: false, + isOffchain: false + } + ) + + expect(result).toBeFalsy() + }) + + it("Should return false if the revoked optional criteria doesn't match", async () => { + const result = await validateCredentials( + { + id: easAttestations.id, + criteria: { + minAttestations: 1 + } }, { - id: "0xfbc0f1aac4379c18fa9a5b6493825234a8ca82a2a296148465d150c2e64c6202", - recipient: "0x0000000000000000000000000000000000000000" + recipient: "0x9aB3971e1b065701C72C5f3cAFbF33118dC51ae8", + queryGraph: queryGraphMocked.queryGraph, + attester: "0x63A35A52c0ac206108EBbf559E4C7109dAd281d3", + schemaId: + "0xe2636f31239f7948afdd9a9c477048b7fc2a089c347af60e3aa1251e5bf63e5c", + revocable: true, + revoked: true, + isOffchain: false + } + ) + + expect(result).toBeFalsy() + }) + + it("Should return false if the isOffchain optional criteria doesn't match", async () => { + const result = await validateCredentials( + { + id: easAttestations.id, + criteria: { + minAttestations: 1 + } }, { - id: "0x227510204bcfe7b543388b82c6e02aafe7b0d0a20e4f159794e8121611aa601b", - recipient: "0x9aB3971e1b065701C72C5f3cAFbF33118dC51ae8" + recipient: "0x9aB3971e1b065701C72C5f3cAFbF33118dC51ae8", + queryGraph: queryGraphMocked.queryGraph, + attester: "0x63A35A52c0ac206108EBbf559E4C7109dAd281d3", + schemaId: + "0xe2636f31239f7948afdd9a9c477048b7fc2a089c347af60e3aa1251e5bf63e5c", + revocable: true, + revoked: false, + isOffchain: true } - ]) + ) + expect(result).toBeFalsy() + }) + + it("Should return false if an account has less than 3 attestations", async () => { const result = await validateCredentials( { id: easAttestations.id, @@ -70,7 +213,7 @@ describe("EASAttestations", () => { } }, { - recipient: "0x9aB3971e1b065701C72C5f3cAFbF33118dC51ae8", + recipient: "0x9aB3971e1b065701C72C5f3cAFbF33118dC51ae9", queryGraph: queryGraphMocked.queryGraph } ) diff --git a/libs/credentials/src/validators/easAttestations/index.ts b/libs/credentials/src/validators/easAttestations/index.ts index c13c9cb6..765b5902 100644 --- a/libs/credentials/src/validators/easAttestations/index.ts +++ b/libs/credentials/src/validators/easAttestations/index.ts @@ -19,24 +19,66 @@ const validator: Validator = { */ async validate(criteria: Criteria, context: Context) { if ("recipient" in context) { - const { recipient } = context as EASContext - + const { + recipient, + attester, + schemaId, + revocable, + revoked, + isOffchain + } = context as EASContext const getAttestations = (context as EASContext).queryGraph const attestations = await getAttestations(` query { attestations { - id recipient + attester + revocable + revoked + schemaId + isOffchain } } `) - const recipientAttestations = attestations.filter( - (attestation: any) => attestation.recipient === recipient + const filteredAttestations = attestations.filter( + (attestation: any) => { + // Mandatory recipient check. + if (attestation.recipient !== recipient) return false + + // Optional criteria checks. + if ( + attester !== undefined && + attestation.attester !== attester + ) + return false + if ( + schemaId !== undefined && + attestation.schemaId !== schemaId + ) + return false + if ( + revocable !== undefined && + attestation.revocable !== revocable + ) + return false + if ( + revoked !== undefined && + attestation.revoked !== revoked + ) + return false + if ( + isOffchain !== undefined && + attestation.isOffchain !== isOffchain + ) + return false + + return true + } ) - return recipientAttestations.length >= criteria.minAttestations + return filteredAttestations.length >= criteria.minAttestations } throw new Error("No recipient value found")