Skip to content

Commit

Permalink
feat: add basic Wallet features
Browse files Browse the repository at this point in the history
  • Loading branch information
beeman committed Apr 4, 2024
1 parent e3a30e1 commit 75aee0a
Show file tree
Hide file tree
Showing 30 changed files with 571 additions and 39 deletions.
4 changes: 4 additions & 0 deletions api-schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,9 @@ type Query {
anonRequestIdentityChallenge(input: RequestIdentityChallengeInput!): IdentityChallenge
appConfig: AppConfig!
me: User
solanaGetBalance(account: String!): String
solanaGetTokenAccounts(account: String!): JSON
solanaGetTransactions(account: String!): JSON
uptime: Float!
userFindManyCommunity(input: UserFindManyCommunityInput!): CommunityPaging!
userFindManyCommunityMember(input: UserFindManyCommunityMemberInput!): CommunityMemberPaging!
Expand Down Expand Up @@ -507,6 +510,7 @@ input VerifyIdentityChallengeInput {
}

type Wallet {
communityId: String
createdAt: DateTime
id: String!
name: String!
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export class ApiCommunityDataAdminService {

async findManyCommunity(input: AdminFindManyCommunityInput): Promise<CommunityPaging> {
return this.data.findMany({
orderBy: { createdAt: 'desc' },
orderBy: { name: 'asc' },
where: getCommunityWhereAdminInput(input),
limit: input.limit,
page: input.page,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export class ApiCommunityDataAnonService {

async findManyCommunity(input: AnonFindManyCommunityInput): Promise<CommunityPaging> {
return this.data.findMany({
orderBy: { createdAt: 'desc' },
orderBy: { name: 'asc' },
where: getCommunityWhereAnonInput(input),
limit: input.limit,
page: input.page,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export class ApiCommunityDataUserService {

async findManyCommunity(input: UserFindManyCommunityInput): Promise<CommunityPaging> {
return this.data.findMany({
orderBy: { createdAt: 'desc' },
orderBy: { name: 'asc' },
where: getCommunityWhereUserInput(input),
limit: input.limit,
page: input.page,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,28 @@
import { Injectable } from '@nestjs/common'
import { CommunityMemberRole, Prisma } from '@prisma/client'
import { Keypair } from '@solana/web3.js'
import { ApiCoreService, PagingInputFields, slugifyId } from '@tokengator-mint/api-core-data-access'
import { CommunityPaging } from './entity/community.entity'

@Injectable()
export class ApiCommunityDataService {
constructor(private readonly core: ApiCoreService) {}
async create(data: Omit<Prisma.CommunityUncheckedCreateInput, 'slug'>, userId?: string) {
const kp = Keypair.generate()
return this.core.data.community.create({
data: {
...data,
slug: slugifyId(data.name).toLowerCase(),
members: userId ? { create: [{ userId, role: CommunityMemberRole.Admin }] } : data.members,
wallets: data.wallets
? data.wallets
: {
create: {
name: 'Fee Payer',
publicKey: kp.publicKey.toBase58(),
secretKey: JSON.stringify(Array.from(kp.secretKey)),
},
},
},
})
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,21 @@ export const provisionCommunities: ProvisionCommunityInput[] = [
],
},
},
{
name: 'COLOSSEUM',
description: `Powering online Solana hackathons, accelerating winners & investing in breakout crypto startups.🏟️`,
imageUrl: 'https://pbs.twimg.com/profile_images/1684566200662233092/BZzDPr5q_400x400.jpg',
wallets: {
create: [
{
name: 'Fee Payer',
publicKey: 'CLSMpWGGvmjh9qFoLtANu2gts3frcrNr2J4XEPoFvQi2',
secretKey:
'[172,236,240,133,3,153,43,108,116,65,138,10,92,241,146,193,31,113,94,213,116,167,89,137,204,233,119,92,232,8,216,9,168,107,67,216,45,228,124,94,213,148,190,81,30,168,9,180,158,112,154,224,189,227,6,7,19,25,18,103,56,132,85,111]',
},
],
},
},
]

@Injectable()
Expand All @@ -146,7 +161,7 @@ export class ApiCommunityProvisionService {
}

private async provisionCommunities() {
await Promise.all(provisionCommunities.map((user) => this.provisionCommunity(user)))
await Promise.all(provisionCommunities.map((item) => this.provisionCommunity(item)))
}

private async provisionCommunity(input: ProvisionCommunityInput) {
Expand Down
18 changes: 17 additions & 1 deletion libs/api/solana/data-access/src/lib/api-solana.service.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Injectable, Logger } from '@nestjs/common'
import { OnEvent } from '@nestjs/event-emitter'
import { Cron, CronExpression } from '@nestjs/schedule'
import { getMint, TOKEN_2022_PROGRAM_ID } from '@solana/spl-token'
import { getMint, TOKEN_2022_PROGRAM_ID, TOKEN_PROGRAM_ID } from '@solana/spl-token'
import { TokenMetadata } from '@solana/spl-token-metadata'
import { AccountInfo, Connection, LAMPORTS_PER_SOL, ParsedAccountData, PublicKey } from '@solana/web3.js'
import { ApiCoreService, CORE_APP_STARTED } from '@tokengator-mint/api-core-data-access'
Expand Down Expand Up @@ -59,4 +59,20 @@ export class ApiSolanaService {

return metadata.state
}

async getBalance(account: string) {
return this.connection.getBalance(new PublicKey(account))
}

async getTokenAccounts(account: string) {
const [tokenAccounts, token2022Accounts] = await Promise.all([
this.connection.getParsedTokenAccountsByOwner(new PublicKey(account), { programId: TOKEN_PROGRAM_ID }),
this.connection.getParsedTokenAccountsByOwner(new PublicKey(account), { programId: TOKEN_2022_PROGRAM_ID }),
])
return [...tokenAccounts.value, ...token2022Accounts.value]
}

async getTransactions(account: string) {
return this.connection.getConfirmedSignaturesForAddress2(new PublicKey(account), { limit: 50 })
}
}
21 changes: 20 additions & 1 deletion libs/api/solana/feature/src/lib/api-solana.resolver.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,26 @@
import { Resolver } from '@nestjs/graphql'
import { UseGuards } from '@nestjs/common'
import { Args, Query, Resolver } from '@nestjs/graphql'
import { ApiAuthGraphQLUserGuard } from '@tokengator-mint/api-auth-data-access'
import { ApiSolanaService } from '@tokengator-mint/api-solana-data-access'
import { GraphQLJSON } from 'graphql-scalars'

@Resolver()
@UseGuards(ApiAuthGraphQLUserGuard)
export class ApiSolanaResolver {
constructor(private readonly service: ApiSolanaService) {}

@Query(() => String, { nullable: true })
solanaGetBalance(@Args('account') account: string) {
return this.service.getBalance(account)
}

@Query(() => GraphQLJSON, { nullable: true })
solanaGetTokenAccounts(@Args('account') account: string) {
return this.service.getTokenAccounts(account)
}

@Query(() => GraphQLJSON, { nullable: true })
solanaGetTransactions(@Args('account') account: string) {
return this.service.getTransactions(account)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export class ApiWalletDataService {
secretKey: JSON.stringify(Array.from(kp.secretKey)),
name,
publicKey,
communityId: input.communityId,
},
})
}
Expand Down
2 changes: 2 additions & 0 deletions libs/api/wallet/data-access/src/lib/entity/wallet.entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ export class Wallet {
name!: string
@Field()
publicKey!: string
@Field({ nullable: true })
communityId?: string | null
}

@ObjectType()
Expand Down
Loading

0 comments on commit 75aee0a

Please sign in to comment.