Skip to content

Commit

Permalink
feat: update to PubKey Protocol v1 sdk
Browse files Browse the repository at this point in the history
  • Loading branch information
beeman committed Oct 23, 2024
1 parent ce3cd82 commit fc7912a
Show file tree
Hide file tree
Showing 21 changed files with 168 additions and 110 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
getRequestDetails,
slugifyId,
} from '@pubkey-network/api-core-data-access'
import { PubKeyProfile } from '@pubkey-program-library/anchor'
import { PubKeyProfile } from '@pubkey-protocol/sdk'
import { ApiIdentitySolanaService } from './api-identity-solana.service'
import { IdentityRequestChallengeInput } from './dto/identity-request-challenge-input'
import { IdentityVerifyChallengeInput } from './dto/identity-verify-challenge-input'
Expand Down
105 changes: 59 additions & 46 deletions libs/api/profile/data-access/src/lib/api-profile.service.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,34 @@
import { Injectable, NotFoundException } from '@nestjs/common'
import { Identity, IdentityProvider } from '@prisma/client'
import { Identity, IdentityProvider as PrismaIdentityProvider } from '@prisma/client'
import { ApiCoreService } from '@pubkey-network/api-core-data-access'
import { ApiSolanaService } from '@pubkey-network/api-solana-data-access'
import { PUBKEY_PROFILE_PROGRAM_ID, PubKeyIdentityProvider, PubKeyProfile } from '@pubkey-program-library/anchor'
import { PubKeyProfileSdk } from '@pubkey-program-library/sdk'
import {
IdentityProvider as IdentityProvider,
PUBKEY_PROTOCOL_PROGRAM_ID,
PubKeyProfile,
PubKeyProtocolSdk,
} from '@pubkey-protocol/sdk'
import { Keypair, PublicKey } from '@solana/web3.js'

@Injectable()
export class ApiProfileService {
private readonly sdk: PubKeyProfileSdk
private readonly sdk: PubKeyProtocolSdk
private readonly feePayer: Keypair
private readonly validProviders: PubKeyIdentityProvider[] = [
private readonly validProviders: PrismaIdentityProvider[] = [
// Add more providers here once the protocol supports them
PubKeyIdentityProvider.Discord,
PubKeyIdentityProvider.Github,
PubKeyIdentityProvider.Google,
PubKeyIdentityProvider.Solana,
PubKeyIdentityProvider.Twitter,
PrismaIdentityProvider.Discord,
// PrismaIdentityProvider.Github,
PrismaIdentityProvider.Google,
PrismaIdentityProvider.Solana,
// PrismaIdentityProvider.X,
]

constructor(private readonly core: ApiCoreService, private readonly solana: ApiSolanaService) {
this.feePayer = this.core.config.solanaFeePayer
this.sdk = new PubKeyProfileSdk({
this.sdk = new PubKeyProtocolSdk({
connection: this.solana.connection,
provider: this.solana.getAnchorProvider(this.feePayer),
programId: PUBKEY_PROFILE_PROGRAM_ID,
programId: PUBKEY_PROTOCOL_PROGRAM_ID,
})
}

Expand All @@ -35,11 +39,13 @@ export class ApiProfileService {
}
const authority = ensureAuthority(user.identities, publicKey)

const transaction = await this.sdk.createProfile({
const { tx: transaction } = await this.sdk.profileCreate({
username: user.username,
avatarUrl: user.avatarUrl ?? '',
feePayer: this.feePayer.publicKey,
authority,
community: PublicKey.unique(),
name: user.username,
})

return transaction.serialize()
Expand All @@ -49,8 +55,13 @@ export class ApiProfileService {
return `${this.core.config.apiUrl}${path}`
}

async profileIdentityAdd(userId: string, publicKey: string, identityProvider: IdentityProvider, providerId: string) {
const provider = convertToPubKeyIdentityProvider(identityProvider)
async profileIdentityAdd(
userId: string,
publicKey: string,
identityProvider: PrismaIdentityProvider,
providerId: string,
) {
const provider = convertToIdentityProvider(identityProvider)
this.ensureValidProvider(provider)
const { user, profile } = await this.ensureUserProfile(userId)

Expand All @@ -64,19 +75,20 @@ export class ApiProfileService {

const authority = ensureAuthority(user.identities, publicKey)

const nickname =
const name =
(identity.profile as { username?: string })?.username ??
(identity.profile as { name?: string })?.name ??
identity.name ??
identity.providerId

const transaction = await this.sdk.addIdentity({
const transaction = await this.sdk.profileIdentityAdd({
authority,
feePayer: this.feePayer.publicKey,
username: user.username,
provider,
providerId,
nickname,
name,
community: PublicKey.unique(),
})
transaction.sign([this.feePayer])

Expand All @@ -86,10 +98,10 @@ export class ApiProfileService {
async profileIdentityRemove(
userId: string,
publicKey: string,
identityProvider: IdentityProvider,
identityProvider: PrismaIdentityProvider,
providerId: string,
) {
const provider = convertToPubKeyIdentityProvider(identityProvider)
const provider = convertToIdentityProvider(identityProvider)
this.ensureValidProvider(provider)
const { user, profile } = await this.ensureUserProfile(userId)

Expand All @@ -108,12 +120,13 @@ export class ApiProfileService {

const authority = ensureAuthority(user.identities, publicKey)

const transaction = await this.sdk.removeIdentity({
const transaction = await this.sdk.profileIdentityRemove({
authority,
feePayer: this.feePayer.publicKey,
username: user.username,
provider,
providerId,
community: '',
})
transaction.sign([this.feePayer])

Expand Down Expand Up @@ -196,20 +209,20 @@ export class ApiProfileService {
throw new Error('User not found')
}

return this.sdk.getProfileByUsernameNullable({ username: user.username })
return this.sdk.profileGetByUsernameNullable({ username: user.username })
}

async getUserProfileByUsername(username: string): Promise<PubKeyProfile | null> {
this.ensureValidUsername(username)

try {
return await this.sdk.getProfileByUsernameNullable({ username })
return await this.sdk.profileGetByUsernameNullable({ username })
} catch (e) {
throw new NotFoundException(`User profile not found for username ${username}`)
}
}

async getUserProfileByProvider(provider: PubKeyIdentityProvider, providerId: string): Promise<PubKeyProfile | null> {
async getUserProfileByProvider(provider: IdentityProvider, providerId: string): Promise<PubKeyProfile | null> {
try {
this.ensureValidProvider(provider)
} catch (e) {
Expand All @@ -222,19 +235,19 @@ export class ApiProfileService {
throw new NotFoundException(`Invalid provider ID for provider ${provider}`)
}
try {
return await this.sdk.getProfileByProviderNullable({ provider, providerId })
return await this.sdk.profileGetByProviderNullable({ provider, providerId })
} catch (e) {
throw new NotFoundException(`User profile not found for provider ${provider} and providerId ${providerId}`)
}
}

async getUserProfileByProviderNullable(
provider: IdentityProvider,
provider: PrismaIdentityProvider,
providerId: string,
): Promise<PubKeyProfile | null> {
try {
return await this.sdk.getProfileByProviderNullable({
provider: convertToPubKeyIdentityProvider(provider),
return await this.sdk.profileGetByProviderNullable({
provider: convertToIdentityProvider(provider),
providerId,
})
} catch (e) {
Expand All @@ -243,7 +256,7 @@ export class ApiProfileService {
}

async getUserProfiles(): Promise<PubKeyProfile[]> {
return this.sdk.getProfiles().then((res) => res.sort((a, b) => a.username.localeCompare(b.username)))
return this.sdk.profileGetAll().then((res) => res.sort((a, b) => a.username.localeCompare(b.username)))
}

private async ensureUserProfile(userId: string) {
Expand All @@ -253,7 +266,7 @@ export class ApiProfileService {
}

const publicKeys: string[] = user.identities
.filter((i) => i.provider === IdentityProvider.Solana)
.filter((i) => i.provider === PrismaIdentityProvider.Solana)
.map((i) => i.providerId)
const profile = await this.findProfile({ username: user.username, publicKeys })
if (!profile) {
Expand All @@ -264,7 +277,7 @@ export class ApiProfileService {
}

private async findProfile({ username, publicKeys }: { username: string; publicKeys: string[] }) {
const profile = await this.sdk.getProfileByUsernameNullable({ username })
const profile = await this.sdk.profileGetByUsernameNullable({ username })

if (profile) {
console.log(`No profile found`)
Expand All @@ -274,8 +287,8 @@ export class ApiProfileService {
const profiles = await Promise.all(
publicKeys.map((providerId) => {
console.log(` -> Searching ${providerId}`)
return this.sdk.getProfileByProviderNullable({
provider: PubKeyIdentityProvider.Solana,
return this.sdk.profileGetByProviderNullable({
provider: IdentityProvider.Solana,
providerId,
})
}),
Expand All @@ -291,17 +304,17 @@ export class ApiProfileService {
throw new Error('User profile not found')
}

private ensureValidProvider(provider: PubKeyIdentityProvider) {
if (!this.validProviders.includes(provider)) {
private ensureValidProvider(provider: IdentityProvider) {
if (!this.validProviders.includes(provider as PrismaIdentityProvider)) {
throw new Error(`Invalid provider: ${provider}`)
}
}

private ensureValidProviderId(provider: PubKeyIdentityProvider, providerId: string) {
if (provider === PubKeyIdentityProvider.Solana && !isSolanaPublicKey(providerId)) {
private ensureValidProviderId(provider: IdentityProvider, providerId: string) {
if (provider === IdentityProvider.Solana && !isSolanaPublicKey(providerId)) {
throw new Error(`Invalid provider ID for ${provider}.`)
}
if (provider !== PubKeyIdentityProvider.Solana && !isNumericString(providerId)) {
if (provider !== IdentityProvider.Solana && !isNumericString(providerId)) {
throw new Error(`Invalid provider ID for ${provider}.`)
}
}
Expand Down Expand Up @@ -353,7 +366,7 @@ function ensureAuthority(identities: Identity[], publicKey: string) {
return authority
}

async function ensureUserIdentity(identities: Identity[], provider: IdentityProvider, providerId: string) {
async function ensureUserIdentity(identities: Identity[], provider: PrismaIdentityProvider, providerId: string) {
const identity = identities.find((i) => i.provider === provider && i.providerId === providerId)

if (!identity) {
Expand Down Expand Up @@ -382,18 +395,18 @@ function diffProfileDetails(
}
}

export function convertToPubKeyIdentityProvider(provider: IdentityProvider): PubKeyIdentityProvider {
export function convertToIdentityProvider(provider: PrismaIdentityProvider): IdentityProvider {
switch (provider.toString()) {
case 'Discord':
return PubKeyIdentityProvider.Discord
return IdentityProvider.Discord
case 'GitHub':
return PubKeyIdentityProvider.Github
return IdentityProvider.Github
case 'Google':
return PubKeyIdentityProvider.Google
return IdentityProvider.Google
case 'Solana':
return PubKeyIdentityProvider.Solana
case 'Twitter':
return PubKeyIdentityProvider.Twitter
return IdentityProvider.Solana
case 'X':
return IdentityProvider.X
default:
throw new Error(`Invalid provider: ${provider}`)
}
Expand Down
7 changes: 2 additions & 5 deletions libs/api/profile/feature/src/lib/api-profile.controller.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Controller, Get, Param } from '@nestjs/common'
import { ApiProfileService } from '@pubkey-network/api-profile-data-access'
import { PubKeyIdentityProvider } from '@pubkey-program-library/anchor'
import { IdentityProvider } from '@pubkey-protocol/sdk'

@Controller('profiles')
export class ApiProfileController {
Expand All @@ -27,10 +27,7 @@ export class ApiProfileController {
}

@Get('provider/:provider/:providerId')
async getProfileByProvider(
@Param('provider') provider: PubKeyIdentityProvider,
@Param('providerId') providerId: string,
) {
async getProfileByProvider(@Param('provider') provider: IdentityProvider, @Param('providerId') providerId: string) {
return this.service.getUserProfileByProvider(provider, providerId)
}

Expand Down
2 changes: 1 addition & 1 deletion libs/api/solana/data-access/src/lib/api-solana.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Injectable, Logger } from '@nestjs/common'
import { OnEvent } from '@nestjs/event-emitter'
import { Cron, CronExpression } from '@nestjs/schedule'
import { ApiCoreService, CORE_APP_STARTED } from '@pubkey-network/api-core-data-access'
import { AnchorKeypairWallet } from '@pubkey-program-library/sdk'
import { AnchorKeypairWallet } from '@pubkey-protocol/sdk'
import { getMint, TOKEN_2022_PROGRAM_ID, TOKEN_PROGRAM_ID } from '@solana/spl-token'
import { TokenMetadata } from '@solana/spl-token-metadata'
import {
Expand Down
6 changes: 3 additions & 3 deletions libs/sdk/src/lib/app-types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { PubKeyIdentity, PubKeyIdentityProvider } from '@pubkey-program-library/anchor'
import { Identity, IdentityProvider, PubkeyProfileIdentity } from '../generated/graphql-sdk'
import { IdentityProvider, PubKeyIdentity } from '@pubkey-protocol/sdk'
import { Identity, IdentityProvider as SdkIdentityProvider, PubkeyProfileIdentity } from '../generated/graphql-sdk'

export type AppIdentity = PubKeyIdentity | Identity | PubkeyProfileIdentity
export type AppIdentityProvider = PubKeyIdentityProvider | IdentityProvider
export type AppIdentityProvider = IdentityProvider | SdkIdentityProvider
2 changes: 1 addition & 1 deletion libs/web/profile/data-access/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,5 @@ export * from './lib/use-profile-identity-add'
export * from './lib/use-profile-identity-remove'
export * from './lib/use-pubkey-profile-program'
export * from './lib/use-pubkey-profile-program-account'
export * from './lib/use-pubkey-profile-sdk'
export * from './lib/use-pubkey-protocol-sdk'
export * from './lib/use-sync-user-profile'
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,14 @@ import {
import { toastError } from '@pubkey-ui/core'
import { PublicKey } from '@solana/web3.js'
import { useMutation, useQuery } from '@tanstack/react-query'
import { usePubkeyProfileSdk } from './use-pubkey-profile-sdk'
import { usePubKeyProtocolSdk } from './use-pubkey-protocol-sdk'

export function usePubkeyProfileProgramAccount({ profilePda }: { profilePda: PublicKey }) {
const { sdk, solanaEndpoint, getExplorerUrl } = usePubkeyProfileSdk()
const { sdk, solanaEndpoint, getExplorerUrl } = usePubKeyProtocolSdk()

const profileAccountQuery = useQuery({
queryKey: ['pubkey-profile', 'fetchProfile', { solanaEndpoint, profilePda }],
queryFn: () => sdk.getProfile({ profilePda }),
queryFn: () => sdk.profileGet({ profile: profilePda.toString() }),
})

// const pointerAccountQuery = useQuery({
Expand All @@ -26,7 +26,15 @@ export function usePubkeyProfileProgramAccount({ profilePda }: { profilePda: Pub

const updateAvatarUrl = useMutation({
mutationKey: ['pubkey-profile', 'updateAvatarUrl', { solanaEndpoint, profilePda }],
mutationFn: (options: UpdateAvatarUrlOptions) => sdk.updateAvatarUrl(options),
mutationFn: (options: UpdateAvatarUrlOptions) =>
sdk.profileUpdate({
avatarUrl: options.avatarUrl,
authority: '',
community: '',
feePayer: '',
name: '',
username: '',
}),
onSuccess: (tx) => {
uiToastLink({ label: 'View transaction', link: getExplorerUrl(`tx/${tx}`) })
return profileAccountQuery.refetch()
Expand All @@ -35,7 +43,7 @@ export function usePubkeyProfileProgramAccount({ profilePda }: { profilePda: Pub

const addAuthority = useMutation({
mutationKey: ['pubkey-profile', 'addAuthority', { solanaEndpoint, profilePda }],
mutationFn: (options: AddAuthorityOptions) => sdk.addAuthority(options),
mutationFn: (options: AddAuthorityOptions) => sdk.profileAuthorityAdd(options),
onError: (err) => toastError(`Error: ${err}`),
onSuccess: (tx) => {
uiToastLink({ label: 'View transaction', link: getExplorerUrl(`tx/${tx}`) })
Expand All @@ -45,7 +53,7 @@ export function usePubkeyProfileProgramAccount({ profilePda }: { profilePda: Pub

const removeAuthority = useMutation({
mutationKey: ['pubkey-profile', 'removeAuthority', { solanaEndpoint, profilePda }],
mutationFn: (options: RemoveAuthorityOptions) => sdk.removeAuthority(options),
mutationFn: (options: RemoveAuthorityOptions) => sdk.profileAuthorityRemove(options),
onSuccess: (tx) => {
uiToastLink({ label: 'View transaction', link: getExplorerUrl(`tx/${tx}`) })
return profileAccountQuery.refetch()
Expand All @@ -54,7 +62,7 @@ export function usePubkeyProfileProgramAccount({ profilePda }: { profilePda: Pub

const addIdentity = useMutation({
mutationKey: ['pubkey-profile', 'addIdentity', { solanaEndpoint, profilePda }],
mutationFn: (options: AddIdentityOptions) => sdk.addIdentity(options),
mutationFn: (options: AddIdentityOptions) => sdk.profileIdentityAdd(options),
onSuccess: (tx) =>
Promise.all([
//pointerAccountQuery.refetch(),
Expand All @@ -64,7 +72,7 @@ export function usePubkeyProfileProgramAccount({ profilePda }: { profilePda: Pub

const removeIdentity = useMutation({
mutationKey: ['pubkey-profile', 'removeIdentity', { solanaEndpoint, profilePda }],
mutationFn: (options: RemoveIdentityOptions) => sdk.removeIdentity(options),
mutationFn: (options: RemoveIdentityOptions) => sdk.profileIdentityRemove(options),
onSuccess: (tx) =>
Promise.all([
//pointerAccountQuery.refetch(),
Expand Down
Loading

0 comments on commit fc7912a

Please sign in to comment.