refactor: make context option parameters as optional criterias

closes #411
This commit is contained in:
Jeeiii
2024-03-18 15:37:07 +01:00
parent 275d4a1ab8
commit 289e7679ab
17 changed files with 210 additions and 118 deletions

View File

@@ -22,8 +22,7 @@ export class CredentialsController {
dto.oAuthState,
dto.oAuthCode,
dto.address,
dto.network,
dto.blockNumber
dto.network
)
}
}

View File

@@ -329,8 +329,7 @@ describe("CredentialsService", () => {
_stateId,
undefined,
"0x1",
"sepolia",
"1234"
"sepolia"
)
expect(clientRedirectUri).toBeUndefined()

View File

@@ -86,8 +86,7 @@ export class CredentialsService {
oAuthState: string,
oAuthCode?: string,
address?: string,
network?: string,
blockNumber?: string
network?: string
): Promise<string> {
if (!this.oAuthState.has(oAuthState)) {
throw new BadRequestException(`OAuth state does not exist`)
@@ -118,14 +117,9 @@ export class CredentialsService {
provider as BlockchainProvider
).getJsonRpcProvider(web3providerRpcURL)
const BlockNumberNumber = blockNumber
? Number(blockNumber)
: undefined
context = {
address,
jsonRpcProvider,
blockNumber: BlockNumberNumber
jsonRpcProvider
}
// Check if the same account has already joined the group.

View File

@@ -2,6 +2,7 @@ import { validators } from "@bandada/credentials"
import {
Box,
Button,
Checkbox,
HStack,
Icon,
Input,
@@ -157,15 +158,21 @@ export default function AccessModeStep({
{_credentials &&
_credentials.criteria &&
Object.entries(validators[_validator].criteriaABI).map(
(parameter) => (
(parameter: any) => (
<VStack
align="left"
pt="20px"
key={parameter[0]}
>
<Text>{capitalize(parameter[0])}</Text>
<Text>
{capitalize(
`${parameter[0]}${
parameter[1].optional ? "" : "*"
}`
)}
</Text>
{parameter[1] === "number" ? (
{parameter[1].type === "number" && (
<NumberInput
size="lg"
value={
@@ -190,7 +197,8 @@ export default function AccessModeStep({
<NumberDecrementStepper />
</NumberInputStepper>
</NumberInput>
) : (
)}
{parameter[1].type === "string" && (
<Input
size="lg"
value={
@@ -215,6 +223,25 @@ export default function AccessModeStep({
}
/>
)}
{parameter[1].type === "boolean" && (
<Checkbox
isChecked={
_credentials.criteria[
parameter[0]
]
}
onChange={(e) =>
setCredentials({
..._credentials,
criteria: {
..._credentials.criteria,
[parameter[0]]:
e.target.checked
}
})
}
/>
)}
</VStack>
)
)}
@@ -244,12 +271,14 @@ export default function AccessModeStep({
(_accessMode === "credentials" &&
(!_credentials ||
!_credentials.criteria ||
Object.keys(_credentials.criteria).length !==
Object.keys(
validators[_validator].criteriaABI
).length ||
Object.values(_credentials.criteria).some(
(c) => c === undefined
Object.entries(
validators[_validator].criteriaABI
).some(
([key, { optional }]: any) =>
!optional &&
(_credentials.criteria[key] ===
undefined ||
!_credentials.criteria[key])
)))
}
variant="solid"

View File

@@ -7,7 +7,9 @@
export default function checkCriteria(criteria: any, criteriaABI: any) {
for (const parameter in criteriaABI) {
if (Object.prototype.hasOwnProperty.call(criteriaABI, parameter)) {
if (criteria[parameter] === undefined) {
const isOptional = criteriaABI[parameter].optional
if (!isOptional && criteria[parameter] === undefined) {
throw Error(`Parameter '${parameter}' has not been defined`)
}
}
@@ -28,10 +30,10 @@ export default function checkCriteria(criteria: any, criteriaABI: any) {
value !== null &&
!Array.isArray(value)
) {
checkCriteria(value, criteriaABI[parameter])
} else if (typeof value !== criteriaABI[parameter]) {
checkCriteria(value, criteriaABI[parameter].type)
} else if (typeof value !== criteriaABI[parameter].type) {
throw TypeError(
`Parameter '${parameter}' is not a ${criteriaABI[parameter]}`
`Parameter '${parameter}' is not a ${criteriaABI[parameter].type}`
)
}
}

View File

@@ -24,17 +24,10 @@ export type Web2Context = {
export type BlockchainContext = {
address: BigNumberish
jsonRpcProvider: any
blockNumber?: number
}
export type EASContext = {
recipient: BigNumberish
queryGraph: (query: string) => Promise<any>
attester?: BigNumberish
schemaId?: BigNumberish
revocable?: boolean
revoked?: boolean
isOffchain?: boolean
}
export type Context = Web2Context | BlockchainContext | EASContext

View File

@@ -33,13 +33,13 @@ describe("BlockchainBalance", () => {
{
id: blockchainBalance.id,
criteria: {
minBalance: "10"
minBalance: "10",
blockNumber: 4749638
}
},
{
address: "0x",
jsonRpcProvider: jsonRpcProviderMocked,
blockNumber: 4749638
jsonRpcProvider: jsonRpcProviderMocked
}
)

View File

@@ -3,13 +3,21 @@ import { BlockchainContext, Validator } from "../.."
export type Criteria = {
minBalance: string
blockNumber?: number
}
const validator: Validator = {
id: "BLOCKCHAIN_BALANCE",
criteriaABI: {
minBalance: "string"
minBalance: {
type: "string",
optional: false
},
blockNumber: {
type: "number",
optional: true
}
},
/**
@@ -20,11 +28,16 @@ const validator: Validator = {
*/
async validate(criteria: Criteria, context) {
if ("address" in context) {
const blockNumber =
criteria.blockNumber !== undefined
? criteria.blockNumber
: undefined
const balance = await (
context as BlockchainContext
).jsonRpcProvider.getBalance(
(context as BlockchainContext).address,
(context as BlockchainContext).blockNumber
blockNumber
)
return balance >= BigNumber.from(criteria.minBalance)
}

View File

@@ -32,13 +32,13 @@ describe("BlockchainTransactions", () => {
{
id: blockchainTransactions.id,
criteria: {
minTransactions: 10
minTransactions: 10,
blockNumber: 4749638
}
},
{
address: "0x",
jsonRpcProvider: jsonRpcProviderMocked,
blockNumber: 4749638
jsonRpcProvider: jsonRpcProviderMocked
}
)

View File

@@ -2,13 +2,21 @@ import { BlockchainContext, Validator } from "../.."
export type Criteria = {
minTransactions: number
blockNumber: number
}
const validator: Validator = {
id: "BLOCKCHAIN_TRANSACTIONS",
criteriaABI: {
minTransactions: "number"
minTransactions: {
type: "number",
optional: false
},
blockNumber: {
type: "number",
optional: true
}
},
/**
@@ -19,11 +27,16 @@ const validator: Validator = {
*/
async validate(criteria: Criteria, context) {
if ("address" in context) {
const blockNumber =
criteria.blockNumber !== undefined
? criteria.blockNumber
: undefined
const transactionCount = await (
context as BlockchainContext
).jsonRpcProvider.getTransactionCount(
(context as BlockchainContext).address,
(context as BlockchainContext).blockNumber
blockNumber
)
return transactionCount >= criteria.minTransactions
}

View File

@@ -54,11 +54,11 @@ describe("EASAttestations", () => {
{
id: easAttestations.id,
criteria: {
minAttestations: 3
minAttestations: 3,
recipient: "0x9aB3971e1b065701C72C5f3cAFbF33118dC51ae8"
}
},
{
recipient: "0x9aB3971e1b065701C72C5f3cAFbF33118dC51ae8",
queryGraph: queryGraphMocked.queryGraph
}
)
@@ -71,18 +71,18 @@ describe("EASAttestations", () => {
{
id: easAttestations.id,
criteria: {
minAttestations: 1
minAttestations: 1,
recipient: "0x9aB3971e1b065701C72C5f3cAFbF33118dC51ae8",
attester: "0x63A35A52c0ac206108EBbf559E4C7109dAd281d3",
schemaId:
"0xe2636f31239f7948afdd9a9c477048b7fc2a089c347af60e3aa1251e5bf63e5c",
revocable: true,
revoked: false,
isOffchain: false
}
},
{
recipient: "0x9aB3971e1b065701C72C5f3cAFbF33118dC51ae8",
queryGraph: queryGraphMocked.queryGraph,
attester: "0x63A35A52c0ac206108EBbf559E4C7109dAd281d3",
schemaId:
"0xe2636f31239f7948afdd9a9c477048b7fc2a089c347af60e3aa1251e5bf63e5c",
revocable: true,
revoked: false,
isOffchain: false
queryGraph: queryGraphMocked.queryGraph
}
)
@@ -94,18 +94,18 @@ describe("EASAttestations", () => {
{
id: easAttestations.id,
criteria: {
minAttestations: 1
minAttestations: 1,
recipient: "0x9aB3971e1b065701C72C5f3cAFbF33118dC51ae8",
attester: "0x63A35A52c0ac206108EBbf559E4C7109dAd281d4",
schemaId:
"0xe2636f31239f7948afdd9a9c477048b7fc2a089c347af60e3aa1251e5bf63e5c",
revocable: true,
revoked: false,
isOffchain: false
}
},
{
recipient: "0x9aB3971e1b065701C72C5f3cAFbF33118dC51ae8",
queryGraph: queryGraphMocked.queryGraph,
attester: "0x63A35A52c0ac206108EBbf559E4C7109dAd281d4",
schemaId:
"0xe2636f31239f7948afdd9a9c477048b7fc2a089c347af60e3aa1251e5bf63e5c",
revocable: true,
revoked: false,
isOffchain: false
queryGraph: queryGraphMocked.queryGraph
}
)
@@ -117,18 +117,18 @@ describe("EASAttestations", () => {
{
id: easAttestations.id,
criteria: {
minAttestations: 1
minAttestations: 1,
recipient: "0x9aB3971e1b065701C72C5f3cAFbF33118dC51ae8",
attester: "0x63A35A52c0ac206108EBbf559E4C7109dAd281d3",
schemaId:
"0xe2636f31239f7948afdd9a9c477048b7fc2a089c347af60e3aa1251e5bf63e5d",
revocable: true,
revoked: false,
isOffchain: false
}
},
{
recipient: "0x9aB3971e1b065701C72C5f3cAFbF33118dC51ae8",
queryGraph: queryGraphMocked.queryGraph,
attester: "0x63A35A52c0ac206108EBbf559E4C7109dAd281d3",
schemaId:
"0xe2636f31239f7948afdd9a9c477048b7fc2a089c347af60e3aa1251e5bf63e5d",
revocable: true,
revoked: false,
isOffchain: false
queryGraph: queryGraphMocked.queryGraph
}
)
@@ -140,18 +140,18 @@ describe("EASAttestations", () => {
{
id: easAttestations.id,
criteria: {
minAttestations: 1
minAttestations: 1,
recipient: "0x9aB3971e1b065701C72C5f3cAFbF33118dC51ae8",
attester: "0x63A35A52c0ac206108EBbf559E4C7109dAd281d3",
schemaId:
"0xe2636f31239f7948afdd9a9c477048b7fc2a089c347af60e3aa1251e5bf63e5c",
revocable: false,
revoked: false,
isOffchain: false
}
},
{
recipient: "0x9aB3971e1b065701C72C5f3cAFbF33118dC51ae8",
queryGraph: queryGraphMocked.queryGraph,
attester: "0x63A35A52c0ac206108EBbf559E4C7109dAd281d3",
schemaId:
"0xe2636f31239f7948afdd9a9c477048b7fc2a089c347af60e3aa1251e5bf63e5c",
revocable: false,
revoked: false,
isOffchain: false
queryGraph: queryGraphMocked.queryGraph
}
)
@@ -163,18 +163,18 @@ describe("EASAttestations", () => {
{
id: easAttestations.id,
criteria: {
minAttestations: 1
minAttestations: 1,
recipient: "0x9aB3971e1b065701C72C5f3cAFbF33118dC51ae8",
attester: "0x63A35A52c0ac206108EBbf559E4C7109dAd281d3",
schemaId:
"0xe2636f31239f7948afdd9a9c477048b7fc2a089c347af60e3aa1251e5bf63e5c",
revocable: true,
revoked: true,
isOffchain: false
}
},
{
recipient: "0x9aB3971e1b065701C72C5f3cAFbF33118dC51ae8",
queryGraph: queryGraphMocked.queryGraph,
attester: "0x63A35A52c0ac206108EBbf559E4C7109dAd281d3",
schemaId:
"0xe2636f31239f7948afdd9a9c477048b7fc2a089c347af60e3aa1251e5bf63e5c",
revocable: true,
revoked: true,
isOffchain: false
queryGraph: queryGraphMocked.queryGraph
}
)
@@ -186,18 +186,18 @@ describe("EASAttestations", () => {
{
id: easAttestations.id,
criteria: {
minAttestations: 1
minAttestations: 1,
recipient: "0x9aB3971e1b065701C72C5f3cAFbF33118dC51ae8",
attester: "0x63A35A52c0ac206108EBbf559E4C7109dAd281d3",
schemaId:
"0xe2636f31239f7948afdd9a9c477048b7fc2a089c347af60e3aa1251e5bf63e5c",
revocable: true,
revoked: false,
isOffchain: true
}
},
{
recipient: "0x9aB3971e1b065701C72C5f3cAFbF33118dC51ae8",
queryGraph: queryGraphMocked.queryGraph,
attester: "0x63A35A52c0ac206108EBbf559E4C7109dAd281d3",
schemaId:
"0xe2636f31239f7948afdd9a9c477048b7fc2a089c347af60e3aa1251e5bf63e5c",
revocable: true,
revoked: false,
isOffchain: true
queryGraph: queryGraphMocked.queryGraph
}
)
@@ -209,11 +209,11 @@ describe("EASAttestations", () => {
{
id: easAttestations.id,
criteria: {
minAttestations: 3
minAttestations: 3,
recipient: "0x9aB3971e1b065701C72C5f3cAFbF33118dC51ae9"
}
},
{
recipient: "0x9aB3971e1b065701C72C5f3cAFbF33118dC51ae9",
queryGraph: queryGraphMocked.queryGraph
}
)
@@ -221,7 +221,7 @@ describe("EASAttestations", () => {
expect(result).toBeFalsy()
})
it("Should throw an error if a criteria parameter is missing", async () => {
it("Should throw an error if a mandatory criteria parameter is missing", async () => {
const fun = () =>
validateCredentials(
{
@@ -229,7 +229,6 @@ describe("EASAttestations", () => {
criteria: {}
},
{
recipient: "0x9aB3971e1b065701C72C5f3cAFbF33118dC51ae9",
queryGraph: queryGraphMocked.queryGraph
}
)
@@ -246,11 +245,11 @@ describe("EASAttestations", () => {
id: easAttestations.id,
criteria: {
minAttestations: 1,
recipient: "0x9aB3971e1b065701C72C5f3cAFbF33118dC51ae9",
test: 123
}
},
{
recipient: "0x9aB3971e1b065701C72C5f3cAFbF33118dC51ae9",
queryGraph: queryGraphMocked.queryGraph
}
)
@@ -266,11 +265,11 @@ describe("EASAttestations", () => {
{
id: easAttestations.id,
criteria: {
minAttestations: "1"
minAttestations: "1",
recipient: "0x9aB3971e1b065701C72C5f3cAFbF33118dC51ae9"
}
},
{
recipient: "0x9aB3971e1b065701C72C5f3cAFbF33118dC51ae9",
queryGraph: queryGraphMocked.queryGraph
}
)

View File

@@ -2,13 +2,46 @@ import { Context, EASContext, Validator } from "../.."
export type Criteria = {
minAttestations: number
recipient: string
attester?: string
schemaId?: string
revocable?: boolean
revoked?: boolean
isOffchain?: boolean
}
const validator: Validator = {
id: "EAS_ATTESTATIONS",
criteriaABI: {
minAttestations: "number"
minAttestations: {
type: "number",
optional: false
},
recipient: {
type: "string",
optional: false
},
attester: {
type: "string",
optional: true
},
schemaId: {
type: "string",
optional: true
},
revocable: {
type: "boolean",
optional: true
},
revoked: {
type: "boolean",
optional: true
},
isOffchain: {
type: "boolean",
optional: true
}
},
/**
@@ -18,7 +51,9 @@ const validator: Validator = {
* @returns True if the user meets the criteria.
*/
async validate(criteria: Criteria, context: Context) {
if ("recipient" in context) {
if ("queryGraph" in context) {
const getAttestations = (context as EASContext).queryGraph
const {
recipient,
attester,
@@ -26,8 +61,7 @@ const validator: Validator = {
revocable,
revoked,
isOffchain
} = context as EASContext
const getAttestations = (context as EASContext).queryGraph
} = criteria
const attestations = await getAttestations(`
query {
@@ -44,10 +78,9 @@ const validator: Validator = {
const filteredAttestations = attestations.filter(
(attestation: any) => {
// Mandatory recipient check.
// Criteria checks.
if (attestation.recipient !== recipient) return false
// Optional criteria checks.
if (
attester !== undefined &&
attestation.attester !== attester

View File

@@ -8,7 +8,10 @@ const validator: Validator = {
id: "GITHUB_FOLLOWERS",
criteriaABI: {
minFollowers: "number"
minFollowers: {
type: "number",
optional: false
}
},
/**

View File

@@ -8,7 +8,10 @@ const validator: Validator = {
id: "GITHUB_PERSONAL_STARS",
criteriaABI: {
minStars: "number"
minStars: {
type: "number",
optional: false
}
},
/**

View File

@@ -9,8 +9,14 @@ const validator: Validator = {
id: "GITHUB_REPOSITORY_COMMITS",
criteriaABI: {
repository: "string",
minCommits: "number"
minCommits: {
type: "number",
optional: false
},
repository: {
type: "string",
optional: false
}
},
/**

View File

@@ -8,7 +8,10 @@ const validator: Validator = {
id: "TWITTER_FOLLOWERS",
criteriaABI: {
minFollowers: "number"
minFollowers: {
type: "number",
optional: false
}
},
/**

View File

@@ -8,7 +8,10 @@ const validator: Validator = {
id: "TWITTER_FOLLOWING_USER",
criteriaABI: {
username: "string"
username: {
type: "string",
optional: false
}
},
/**