diff --git a/.github/workflows/main-pipeline.yml b/.github/workflows/main-pipeline.yml index 502a39c15..8e6ba5d7a 100644 --- a/.github/workflows/main-pipeline.yml +++ b/.github/workflows/main-pipeline.yml @@ -119,6 +119,7 @@ jobs: SOLANA_DEVNET_NODE_RPC_URL: ${{ secrets.SOLANA_DEVNET_NODE_RPC_URL }} SOLANA_MAINNET_NODE_RPC_URL: ${{ secrets.SOLANA_MAINNET_NODE_RPC_URL }} MPETH_GRAPHQL_PRICES_URL: ${{ secrets.MPETH_GRAPHQL_PRICES_URL }} + PRIVADO_VERIFIER_NETWORK_ID: ${{ secrets.PRIVADO_VERIFIER_NETWORK_ID }} publish: needs: test @@ -161,4 +162,4 @@ jobs: git pull docker compose -f docker-compose-production.yml pull docker compose -f docker-compose-production.yml up -d - docker image prune -a --force \ No newline at end of file + docker image prune -a --force diff --git a/.github/workflows/staging-pipeline.yml b/.github/workflows/staging-pipeline.yml index d71ea2243..b767bbaaf 100644 --- a/.github/workflows/staging-pipeline.yml +++ b/.github/workflows/staging-pipeline.yml @@ -119,6 +119,7 @@ jobs: SOLANA_DEVNET_NODE_RPC_URL: ${{ secrets.SOLANA_DEVNET_NODE_RPC_URL }} SOLANA_MAINNET_NODE_RPC_URL: ${{ secrets.SOLANA_MAINNET_NODE_RPC_URL }} MPETH_GRAPHQL_PRICES_URL: ${{ secrets.MPETH_GRAPHQL_PRICES_URL }} + PRIVADO_VERIFIER_NETWORK_ID: ${{ secrets.PRIVADO_VERIFIER_NETWORK_ID }} publish: needs: test @@ -161,4 +162,4 @@ jobs: git pull docker compose -f docker-compose-staging.yml pull docker compose -f docker-compose-staging.yml up -d - docker image prune -a --force \ No newline at end of file + docker image prune -a --force diff --git a/config/example.env b/config/example.env index f05b3bd2b..624225332 100644 --- a/config/example.env +++ b/config/example.env @@ -283,7 +283,6 @@ QACC_DONATION_TOKEN_NAME= QACC_EARLY_ACCESS_ROUND_FINISH_TIMESTAMP= ABC_LAUNCHER_ADAPTER= -PRIVADO_VERIFIER_ADAPTER= PRIVADO_VERIFIER_NETWORK_ID= PRIVADO_VERIFIER_CONTRACT_ADDRESS= PRIVADO_REQUEST_ID= diff --git a/src/adapters/adaptersFactory.ts b/src/adapters/adaptersFactory.ts index 8725e95db..8de660bd8 100644 --- a/src/adapters/adaptersFactory.ts +++ b/src/adapters/adaptersFactory.ts @@ -20,7 +20,6 @@ import { SuperFluidAdapterInterface } from './superFluid/superFluidAdapterInterf import { AbcLauncherAdapter } from './abcLauncher/abcLauncherAdapter'; import { AbcLauncherMockAdapter } from './abcLauncher/abcLauncherMockAdapter'; import { PrivadoAdapter } from './privado/privadoAdapter'; -import { IPrivadoAdapter } from './privado/privadoAdapterInterface'; const discordAdapter = new DiscordAdapter(); const googleAdapter = new GoogleAdapter(); @@ -129,4 +128,4 @@ export const getAbcLauncherAdapter = () => { } }; -export const privadoAdapter: IPrivadoAdapter = new PrivadoAdapter(); +export const privadoAdapter = new PrivadoAdapter(); diff --git a/src/adapters/privado/privadoAdapter.ts b/src/adapters/privado/privadoAdapter.ts index 07d6f8208..414ab7596 100644 --- a/src/adapters/privado/privadoAdapter.ts +++ b/src/adapters/privado/privadoAdapter.ts @@ -1,8 +1,9 @@ import { ethers } from 'ethers'; import config from '../../config'; import { getProvider } from '../../provider'; -import { IPrivadoAdapter } from './privadoAdapterInterface'; import { findUserById } from '../../repositories/userRepository'; +import { User } from '../../entities/user'; +import { logger } from '../../utils/logger'; const PRIVADO_VERIFIER_NETWORK_ID = +config.get( 'PRIVADO_VERIFIER_NETWORK_ID', ) as number; @@ -10,9 +11,14 @@ const PRIVADO_VERIFIER_CONTRACT_ADDRESS = config.get( 'PRIVADO_VERIFIER_CONTRACT_ADDRESS', ) as string; const PRIVADO_REQUEST_ID = +config.get('PRIVADO_REQUEST_ID') as number; -export class PrivadoAdapter implements IPrivadoAdapter { +export class PrivadoAdapter { + private provider; + + constructor() { + this.provider = getProvider(PRIVADO_VERIFIER_NETWORK_ID); + } + private async checkVerificationOnchain(address: string): Promise { - const provider = getProvider(PRIVADO_VERIFIER_NETWORK_ID); const abi = [ { inputs: [ @@ -29,26 +35,27 @@ export class PrivadoAdapter implements IPrivadoAdapter { const contract = new ethers.Contract( PRIVADO_VERIFIER_CONTRACT_ADDRESS, abi, - provider, + this.provider, ); - return contract.isProofVerified(address, this.privadoRequestId()); + return contract.isProofVerified(address, PrivadoAdapter.privadoRequestId()); } async checkUserVerified(userId: number): Promise { + logger.debug('Checking Privado verification for user', { userId }); + const user = await findUserById(userId); - const requestId = this.privadoRequestId(); if (!user || !user.walletAddress) { throw new Error('No user or wallet address'); } - if (user.privadoVerifiedRequestIds.includes(requestId)) { + if (PrivadoAdapter.isUserVerified(user)) { return true; } const response = await this.checkVerificationOnchain(user.walletAddress); if (response) { user.privadoVerifiedRequestIds = [ - requestId, + PrivadoAdapter.privadoRequestId(), ...user.privadoVerifiedRequestIds, ]; await user.save(); @@ -56,13 +63,14 @@ export class PrivadoAdapter implements IPrivadoAdapter { return response; } - async isUserVerified(userId: number): Promise { - const user = await findUserById(userId); + static isUserVerified(user: User): boolean { return ( - user?.privadoVerifiedRequestIds.includes(this.privadoRequestId()) || false + user?.privadoVerifiedRequestIds.includes( + PrivadoAdapter.privadoRequestId(), + ) || false ); } - privadoRequestId(): number { + static privadoRequestId(): number { return PRIVADO_REQUEST_ID; } } diff --git a/src/adapters/privado/privadoAdapterInterface.ts b/src/adapters/privado/privadoAdapterInterface.ts deleted file mode 100644 index 38e8377ab..000000000 --- a/src/adapters/privado/privadoAdapterInterface.ts +++ /dev/null @@ -1,5 +0,0 @@ -export interface IPrivadoAdapter { - isUserVerified(userId: number): Promise; - checkUserVerified(userId: number): Promise; - privadoRequestId(): number; -} diff --git a/src/entities/user.ts b/src/entities/user.ts index 7297a0767..6bcb82b46 100644 --- a/src/entities/user.ts +++ b/src/entities/user.ts @@ -18,6 +18,7 @@ import { ProjectStatusHistory } from './projectStatusHistory'; import { ProjectVerificationForm } from './projectVerificationForm'; import { ReferredEvent } from './referredEvent'; import { NOTIFICATIONS_EVENT_NAMES } from '../analytics/analytics'; +import { PrivadoAdapter } from '../adapters/privado/privadoAdapter'; export const publicSelectionFields = [ 'user.id', @@ -207,6 +208,13 @@ export class User extends BaseEntity { @Column('integer', { array: true, default: [] }) privadoVerifiedRequestIds: number[]; + @Field(_type => Boolean, { nullable: true }) + privadoVerified(): boolean { + return this.privadoVerifiedRequestIds.includes( + PrivadoAdapter.privadoRequestId(), + ); + } + @Field(_type => Int, { nullable: true }) async donationsCount() { return await Donation.createQueryBuilder('donation') diff --git a/src/resolvers/userResolver.test.ts b/src/resolvers/userResolver.test.ts index 8efa19a2e..14fd91e77 100644 --- a/src/resolvers/userResolver.test.ts +++ b/src/resolvers/userResolver.test.ts @@ -17,7 +17,6 @@ import { } from '../../test/testUtils'; import { checkUserPrivadoVerifiedState, - isUserPrivadoVerified, refreshUserScores, updateUser, userByAddress, @@ -30,6 +29,7 @@ import { getGitcoinAdapter, privadoAdapter } from '../adapters/adaptersFactory'; import { updateUserTotalDonated } from '../services/userService'; import { getUserEmailConfirmationFields } from '../repositories/userRepository'; import { UserEmailVerification } from '../entities/userEmailVerification'; +import { PrivadoAdapter } from '../adapters/privado/privadoAdapter'; describe('updateUser() test cases', updateUserTestCases); describe('userByAddress() test cases', userByAddressTestCases); @@ -905,12 +905,15 @@ function checkUserPrivadoVerfiedStateTestCases() { await user.save(); const accessToken = await generateTestAccessToken(user.id); - sinon.stub(privadoAdapter, 'privadoRequestId').returns(2); + sinon.stub(PrivadoAdapter, 'privadoRequestId').returns(2); const result = await axios.post( graphqlUrl, { - query: isUserPrivadoVerified, + query: userByAddress, + variables: { + address: user.walletAddress, + }, }, { headers: { @@ -919,7 +922,8 @@ function checkUserPrivadoVerfiedStateTestCases() { }, ); - assert.isTrue(result.data.data.isUserPrivadoVerified); + assert.isOk(result.data.data.userByAddress); + assert.isTrue(result.data.data.userByAddress.privadoVerified); }); it('should return false if the user does not has request privadoVerifiedRequestIds', async () => { @@ -928,12 +932,15 @@ function checkUserPrivadoVerfiedStateTestCases() { await user.save(); const accessToken = await generateTestAccessToken(user.id); - sinon.stub(privadoAdapter, 'privadoRequestId').returns(4); + sinon.stub(PrivadoAdapter, 'privadoRequestId').returns(4); const result = await axios.post( graphqlUrl, { - query: isUserPrivadoVerified, + query: userByAddress, + variables: { + address: user.walletAddress, + }, }, { headers: { @@ -942,7 +949,8 @@ function checkUserPrivadoVerfiedStateTestCases() { }, ); - assert.isFalse(result.data.data.isUserPrivadoVerified); + assert.isOk(result.data.data.userByAddress); + assert.isFalse(result.data.data.userByAddress.privadoVerified); }); it('should add request ID to privadoVerifiedRequestIds if user is verified', async () => { @@ -952,7 +960,7 @@ function checkUserPrivadoVerfiedStateTestCases() { const accessToken = await generateTestAccessToken(user.id); sinon.stub(privadoAdapter, 'checkVerificationOnchain').returns(true); - sinon.stub(privadoAdapter, 'privadoRequestId').returns(4); + sinon.stub(PrivadoAdapter, 'privadoRequestId').returns(4); const result = await axios.post( graphqlUrl, @@ -984,7 +992,7 @@ function checkUserPrivadoVerfiedStateTestCases() { const accessToken = await generateTestAccessToken(user.id); sinon.stub(privadoAdapter, 'checkVerificationOnchain').returns(false); - sinon.stub(privadoAdapter, 'privadoRequestId').returns(4); + sinon.stub(PrivadoAdapter, 'privadoRequestId').returns(4); const result = await axios.post( graphqlUrl, @@ -1017,7 +1025,7 @@ function checkUserPrivadoVerfiedStateTestCases() { const accessToken = await generateTestAccessToken(user.id); sinon.stub(privadoAdapter, 'checkVerificationOnchain').returns(true); - sinon.stub(privadoAdapter, 'privadoRequestId').returns(2); + sinon.stub(PrivadoAdapter, 'privadoRequestId').returns(2); const result = await axios.post( graphqlUrl, diff --git a/src/resolvers/userResolver.ts b/src/resolvers/userResolver.ts index d9b34a648..48ecb64f2 100644 --- a/src/resolvers/userResolver.ts +++ b/src/resolvers/userResolver.ts @@ -366,16 +366,6 @@ export class UserResolver { } } - @Query(_return => Boolean) - async isUserPrivadoVerified( - @Ctx() { req: { user } }: ApolloContext, - ): Promise { - if (!user) - throw new Error( - i18n.__(translationErrorMessagesKeys.AUTHENTICATION_REQUIRED), - ); - return await privadoAdapter.isUserVerified(user.userId); - } @Mutation(_returns => Boolean) async checkUserPrivadoVerifiedState( @Ctx() { req: { user } }: ApolloContext, diff --git a/test/graphqlQueries.ts b/test/graphqlQueries.ts index 773729faa..e8d04bc28 100644 --- a/test/graphqlQueries.ts +++ b/test/graphqlQueries.ts @@ -1185,6 +1185,7 @@ export const userByAddress = ` projectsCount passportScore passportStamps + privadoVerified } } `; @@ -2062,11 +2063,6 @@ export const fetchActiveEarlyAccessRoundQuery = ` } `; -export const isUserPrivadoVerified = ` - query { - isUserPrivadoVerified - } -`; export const checkUserPrivadoVerifiedState = ` mutation { checkUserPrivadoVerifiedState