Skip to content

Commit

Permalink
feat: PLA-1017, PLA-1033, PLA-1047 (#529)
Browse files Browse the repository at this point in the history
* Mytokens

* fix
  • Loading branch information
justraman authored Jul 31, 2023
1 parent 1af8da8 commit 9663451
Show file tree
Hide file tree
Showing 5 changed files with 209 additions and 11 deletions.
9 changes: 3 additions & 6 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
"axios": "^1.4.0",
"bull": "^4.10.4",
"cacheable-request": "^8.3.1",
"class-validator": "^0.14.0",
"dotenv": "^16.3.1",
"lodash": "^4.17.21",
"node-cache": "^5.1.2",
Expand Down
2 changes: 2 additions & 0 deletions src/server-extension/resolvers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ import { RefreshAccountResolver } from './refresh_account'
import { TokenSalesHistoryResolver } from './token_sales_history'
import { VerifyMessageResolver } from './verify_message'
import { TopCollectionResolver } from './top_collections'
import { MyTokensResolver } from './my_tokens'

export {
TokenSalesHistoryResolver,
VerifyMessageResolver,
RefreshMetadataResolver,
RefreshAccountResolver,
TopCollectionResolver,
MyTokensResolver,
}
198 changes: 198 additions & 0 deletions src/server-extension/resolvers/my_tokens.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
/* eslint-disable max-classes-per-file */

import { Field, ObjectType, Query, Resolver, ID, Int, registerEnumType, ArgsType, Args } from 'type-graphql'
import { Json, BigInteger } from '@subsquid/graphql-server'
import 'reflect-metadata'
import type { EntityManager } from 'typeorm'
// eslint-disable-next-line import/no-extraneous-dependencies
import { Validate, ValidatorConstraint, ValidatorConstraintInterface } from 'class-validator'
import { Collection, FreezeState, Listing, Token, TokenAccount } from '../../model'
import { isValidAddress } from '../../common/tools'

enum OrderBy {
COLLECTION_NAME = "collection.metadata->>'name'",
TOKEN_NAME = "token.metadata->>'name'",
FLOOR_PRICE = 'listing.highestPrice',
DATE = 'token.createdAt',
}

enum Order {
ASC = 'ASC',
DESC = 'DESC',
}

registerEnumType(OrderBy, {
name: 'MyTokensOrderBy',
})

registerEnumType(Order, {
name: 'MyTokensOrder',
})

registerEnumType(FreezeState, {
name: 'MyTokensFreezeState',
})

@ValidatorConstraint({ name: 'PublicKey', async: false })
export class IsPublicKey implements ValidatorConstraintInterface {
validate(value: string) {
return isValidAddress(value)
}

defaultMessage() {
return 'Invalid public key!'
}
}

@ArgsType()
class MyTokenArgs {
@Field(() => ID)
@Validate(IsPublicKey)
accountId!: string

@Field(() => OrderBy)
orderBy!: OrderBy

@Field(() => Order)
order!: Order

@Field(() => Int, { defaultValue: 0 })
offset: number = 0

@Field(() => Int, { defaultValue: 10 })
limit: number = 10

@Field(() => String, { nullable: true })
query?: string
}

@ObjectType()
class MyTokensCollection {
@Field(() => ID)
id!: string

@Field(() => BigInteger)
collectionId!: typeof BigInteger

@Field(() => Json)
metadata!: typeof Json

@Field(() => Json)
stats!: typeof Json
}

@ObjectType()
class MyTokensOwner {
@Field(() => ID)
id!: string

@Field(() => BigInteger)
balance!: typeof BigInteger

@Field(() => Boolean)
isFrozen!: boolean
}

@ObjectType()
class MyTokensBestListing {
@Field(() => ID)
id!: string

@Field(() => BigInteger)
highestPrice!: typeof BigInteger

@Field(() => Json)
state!: typeof Json

@Field(() => Json)
data!: typeof Json
}

@ObjectType()
export class MyTokensToken {
@Field(() => ID)
id!: string

@Field(() => BigInteger)
tokenId!: typeof BigInteger

@Field(() => BigInteger)
supply!: typeof BigInteger

@Field(() => Boolean)
isFrozen!: boolean

@Field({ nullable: true })
freezeState!: FreezeState

@Field(() => Json, { nullable: true })
metadata!: typeof Json

@Field(() => Boolean)
nonFungible!: boolean

@Field()
createdAt!: Date

@Field(() => MyTokensCollection)
collection!: MyTokensCollection

@Field(() => MyTokensBestListing, { nullable: true })
bestListing!: MyTokensBestListing

@Field(() => MyTokensOwner, { nullable: false })
owner!: MyTokensOwner

constructor(props: Partial<MyTokensResponse>) {
Object.assign(this, props)
}
}

@ObjectType()
export class MyTokensResponse {
@Field(() => [MyTokensToken])
data!: MyTokensToken[]

@Field(() => Int)
count!: number

constructor(props: Partial<MyTokensResponse>) {
Object.assign(this, props)
}
}

@Resolver()
export class MyTokensResolver {
constructor(private tx: () => Promise<EntityManager>) {}

@Query(() => MyTokensResponse)
async myTokens(@Args() { accountId, limit, offset, order, orderBy, query }: MyTokenArgs): Promise<MyTokensResponse> {
const manager = await this.tx()

const builder = manager
.getRepository(Token)
.createQueryBuilder('token')
.innerJoinAndMapOne('token.collection', Collection, 'collection', 'token.collection = collection.id')
.leftJoinAndMapOne('token.bestListing', Listing, 'listing', 'listing.makeAssetId = token.id')
.innerJoinAndMapOne(
'token.owner',
TokenAccount,
'token_account',
'token_account.token = token.id AND token_account.account = :accountId',
{ accountId }
)
.orderBy(orderBy, order, 'NULLS LAST')
.skip(offset)
.limit(limit)

if (query) {
builder.where("collection.metadata->>'name' ILIKE :query OR token.metadata->>'name' ILIKE :query", {
query: `%${query}%`,
})
}

const [data, count] = (await builder.getManyAndCount()) as any[]

return { data, count }
}
}
10 changes: 5 additions & 5 deletions src/server-extension/resolvers/top_collections.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ const timeFrameMap = {
ALL: { c: '0', p: '0' },
}

enum OrderBy {
enum TopCollectionOrderBy {
VOLUME = 'volume',
SALES = 'sales',
VOLUME_CHANGE = 'volume_change',
Expand All @@ -42,12 +42,12 @@ registerEnumType(Timeframe, {
name: 'Timeframe',
})

registerEnumType(OrderBy, {
name: 'OrderBy',
registerEnumType(TopCollectionOrderBy, {
name: 'TopCollectionOrderBy',
})

registerEnumType(Order, {
name: 'Order',
name: 'TopCollectionOrder',
})

@ObjectType()
Expand Down Expand Up @@ -85,7 +85,7 @@ export class TopCollectionResolver {
@Query(() => [CollectionRow])
async topCollection(
@Arg('timeFrame', () => Timeframe) timeFrame: Timeframe,
@Arg('orderBy', () => OrderBy) orderBy: OrderBy,
@Arg('orderBy', () => TopCollectionOrderBy) orderBy: TopCollectionOrderBy,
@Arg('order', () => Order) order: Order,
@Arg('offset', () => Int) offset: number = 0,
@Arg('limit', () => Int) limit: number = 10
Expand Down

0 comments on commit 9663451

Please sign in to comment.