Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TokenEntity/fix count calculation #109

Merged
merged 6 commits into from
Oct 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions db/migrations/1689623692588-Data.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module.exports = class Data1689623692588 {

async up(db) {
await db.query(`CREATE TABLE "token_entity" ("id" character varying NOT NULL, "block_number" numeric, "hash" text NOT NULL, "image" text, "media" text, "name" text, "updated_at" TIMESTAMP WITH TIME ZONE NOT NULL, "created_at" TIMESTAMP WITH TIME ZONE NOT NULL, "count" integer NOT NULL, "collection_id" character varying, CONSTRAINT "PK_687443f2a51af49b5472e2c5ddc" PRIMARY KEY ("id"))`)
await db.query(`ALTER TABLE "token_entity" ADD "total_count" integer NOT NULL`)
await db.query(`CREATE INDEX "IDX_0eb2ed7929c3e81941fa1b51b3" ON "token_entity" ("collection_id") `)
await db.query(`CREATE INDEX "IDX_40d6049fd30532dada71922792" ON "token_entity" ("hash") `)
await db.query(`CREATE INDEX "IDX_47b385945a425667b9e690bc02" ON "token_entity" ("name") `)
Expand Down
1 change: 1 addition & 0 deletions schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ type TokenEntity @entity {
updatedAt: DateTime!
createdAt: DateTime!
count: Int!
totalCount: Int!
Comment on lines 40 to +41
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please use same wording as collection has

count for total
supply for available

Screenshot 2023-10-06 at 12 04 10

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok
so supply is only not burned
count is all

yes?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes?

YES

}

type NFTEntity @entity {
Expand Down
5 changes: 4 additions & 1 deletion src/mappings/nfts/burn.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { getWith } from '@kodadot1/metasquid/entity'
import { getWith } from '@kodadot1/metasquid/entity'
import { NFTEntity as NE } from '../../model'
import { unwrap } from '../utils/extract'
import { debug, pending, success } from '../utils/logger'
import { Action, Context, createTokenId } from '../utils/types'
import { createEvent } from '../shared/event'
import { calculateCollectionOwnerCountAndDistribution } from '../utils/helper'
import { eventHandlers } from '../shared/handleTokenEntity'
import { getBurnTokenEvent } from './getters'

const OPERATION = Action.BURN
Expand All @@ -31,6 +32,8 @@ export async function handleTokenBurn(context: Context): Promise<void> {
entity.collection.ownerCount = ownerCount
entity.collection.distribution = distribution

await eventHandlers.burnHandler(context, entity)

success(OPERATION, `${id} by ${event.caller}}`)
await context.store.save(entity)
const meta = entity.metadata ?? ''
Expand Down
3 changes: 3 additions & 0 deletions src/mappings/nfts/mint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { unwrap } from '../utils/extract'
import { debug, pending, success } from '../utils/logger'
import { Action, Context, createTokenId } from '../utils/types'
import { calculateCollectionOwnerCountAndDistribution, versionOf } from '../utils/helper'
import { eventHandlers } from '../shared/handleTokenEntity'
import { getCreateTokenEvent } from './getters'

const OPERATION = Action.MINT
Expand Down Expand Up @@ -60,6 +61,8 @@ export async function handleTokenCreate(context: Context): Promise<void> {
final.name = metadata?.name
final.image = metadata?.image
final.media = metadata?.animationUrl

await eventHandlers.mintHandler(context, collection, final)
}

success(OPERATION, `${final.id}`)
Expand Down
18 changes: 8 additions & 10 deletions src/mappings/nfts/setMetadata.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { get, getOptional } from '@kodadot1/metasquid/entity'
import { get, getOptional, getWith } from '@kodadot1/metasquid/entity'
import { isFetchable } from '@kodadot1/minipfs'
import { unwrap } from '../utils/extract'
import { Context, isNFT } from '../utils/types'
import { CollectionEntity, NFTEntity } from '../../model'
import { handleMetadata } from '../shared/metadata'
import { debug, warn } from '../utils/logger'
import { updateItemMetadataByCollection } from '../utils/cache'
import { handleTokenEntity } from '../shared/handleTokenEntity'
import { eventHandlers } from '../shared/handleTokenEntity'
import { tokenIdOf } from './types'
import { getMetadataEvent } from './getters'

Expand Down Expand Up @@ -51,17 +51,15 @@ export async function handleMetadataSet(context: Context): Promise<void> {
warn(OPERATION, `collection ${event.collectionId} not found`)
return
}
const nft = final as NFTEntity
const token = await handleTokenEntity(context, collection, nft)
if (token) {
nft.token = token
if (final instanceof NFTEntity) {
await eventHandlers.setMetadataHandler(context, collection, final)
}
}
}

await context.store.save(final)
await context.store.save(final)

if (!event.sn && final.metadata) {
await updateItemMetadataByCollection(context.store, event.collectionId)
if (!event.sn && final.metadata) {
await updateItemMetadataByCollection(context.store, event.collectionId)
}
}
}
142 changes: 116 additions & 26 deletions src/mappings/shared/handleTokenEntity.ts
Original file line number Diff line number Diff line change
@@ -1,44 +1,134 @@
import { create, getOptional } from '@kodadot1/metasquid/entity'
import { create, getOptional, getWith } from '@kodadot1/metasquid/entity'
import md5 from 'md5'
import { CollectionEntity as CE, NFTEntity as NE, TokenEntity as TE } from '../../model'
import { warn } from '../utils/logger'
import { debug, warn } from '../utils/logger'
import { Context } from '../utils/types'

const OPERATION = 'TokenEntity' as any

export async function handleTokenEntity(context: Context, collection: CE, nft: NE): Promise<TE | undefined> {
const nftMedia = nft.image || nft.media
function generateTokenId(collectionId: string, nftMedia: string): string {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use utils file

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sure

return `${collectionId}-${md5(nftMedia)}`
}

async function createToken(context: Context, collection: CE, nft: NE): Promise<TE | undefined> {
const nftMedia = nft.image ?? nft.media
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as mentioned in #110

if (!nftMedia || nftMedia === '') {
warn(OPERATION, `MISSING NFT MEDIA ${nft.id}`)
return
}
const tokenId = generateTokenId(collection.id, nftMedia)
debug(OPERATION, { createToken: `Create TOKEN ${tokenId} for NFT ${nft.id}` })
const tokenName = typeof nft.name === 'string' ? nft.name?.replace(/([#_]\d+$)/g, '').trim() : ''
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as mentioned in #110


const tokenId = `${collection.id}-${md5(nftMedia)}`
let token = await getOptional<TE>(context.store, TE, tokenId)
const token = create(TE, tokenId, {
createdAt: nft.createdAt,
collection,
name: tokenName,
count: 1,
totalCount: 1,
hash: md5(tokenId),
image: nft.image,
media: nft.media,
blockNumber: nft.blockNumber,
updatedAt: nft.updatedAt,
id: tokenId,
})

if (!token) {
const tokenName = (typeof nft.name === 'string' ? nft.name?.replace(/([#_]\d+$)/g, '').trim() : '')

token = create(TE, tokenId, {
createdAt: nft.createdAt,
collection,
name: tokenName,
count: 1,
hash: md5(tokenId),
image: nft.image,
media: nft.media,
blockNumber: nft.blockNumber,
updatedAt: nft.updatedAt,
id: tokenId,
})
} else {
token.count += 1
}
nft.token = token
await context.store.save(token)
await context.store.save(nft)

token.updatedAt = nft.updatedAt
token.blockNumber = nft.blockNumber
return token
}

async function addNftToToken(context: Context, nft: NE, token: TE): Promise<TE> {
debug(OPERATION, { updateToken: `Add NFT ${nft.id} to TOKEN ${token.id} for ` })
token.count += 1
token.totalCount += 1
token.updatedAt = nft.updatedAt
nft.token = token
await context.store.save(token)
await context.store.save(nft)

return token
}

async function removeNftFromToken(context: Context, nft: NE, token: TE): Promise<void> {
if (!token) {
return
}
debug(OPERATION, { removeNftFromToken: `Unlink NFT ${nft.id} from TOKEN ${token.id}` })

await context.store.update(NE, nft.id, { token: null })
const updatedTotalCount = token.totalCount - 1
await context.store.update(TE, token.id, { count: token.count - 1, totalCount: updatedTotalCount , updatedAt: nft.updatedAt })

if (updatedTotalCount === 0) {
debug(OPERATION, { deleteEmptyToken: `delete empty token ${token.id}` })
await context.store.delete(TE, token.id)
}
}

async function mintHandler(context: Context, collection: CE, nft: NE): Promise<TE | undefined> {
const nftMedia = nft.image ?? nft.media
debug(OPERATION, { mintHandler: `Handle mint for NFT ${nft.id}` })

if (!nftMedia || nftMedia === '') {
warn(OPERATION, `MISSING NFT MEDIA ${nft.id}`)
return
}

const existingToken = await getOptional<TE>(context.store, TE, generateTokenId(collection.id, nftMedia))
return await (existingToken ? addNftToToken(context, nft, existingToken) : createToken(context, collection, nft))
}

async function handleMetadataSet(context: Context, collection: CE, nft: NE): Promise<TE | undefined> {
debug(OPERATION, { handleMetadataSet: `Handle set metadata for NFT ${nft.id}` })
const nftMedia = nft.image ?? nft.media

if (!nftMedia || nftMedia === '') {
warn(OPERATION, `MISSING NFT MEDIA ${nft.id}`)
return
}

let nftWithToken, existingToken
try {
;[nftWithToken, existingToken] = await Promise.all([
getWith(context.store, NE, nft.id, { token: true }),
getOptional<TE>(context.store, TE, generateTokenId(collection.id, nftMedia)),
])
} catch (error) {
warn(OPERATION, `ERROR ${error}`)
return
}
if (nftWithToken.token) {
await removeNftFromToken(context, nft, nftWithToken.token)
}
return await (existingToken ? addNftToToken(context, nft, existingToken) : createToken(context, collection, nft))
}

async function handleBurn(context: Context, nft: NE): Promise<void> {
debug(OPERATION, { handleBurn: `Handle Burn for NFT ${nft.id}` })
const nftMedia = nft.image ?? nft.media

if (!nftMedia || nftMedia === '') {
warn(OPERATION, `MISSING NFT MEDIA ${nft.id}`)
return
}

const token = await getOptional<TE>(context.store, TE, generateTokenId(nft.collection.id, nftMedia))

if (!token) {
return
}

debug(OPERATION, { BURN: `decrement Token's ${token.id} count` })

await context.store.update(TE, token.id, { count: token.count - 1, updatedAt: nft.updatedAt })
}

export const eventHandlers = {
setMetadataHandler: handleMetadataSet,
mintHandler,
burnHandler: handleBurn,
}
3 changes: 3 additions & 0 deletions src/mappings/uniques/burn.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { debug, pending, success } from '../utils/logger'
import { Action, Context, createTokenId } from '../utils/types'
import { createEvent } from '../shared/event'
import { calculateCollectionOwnerCountAndDistribution } from '../utils/helper'
import { eventHandlers } from '../shared/handleTokenEntity'
import { getBurnTokenEvent } from './getters'

const OPERATION = Action.BURN
Expand All @@ -31,6 +32,8 @@ export async function handleTokenBurn(context: Context): Promise<void> {
entity.collection.ownerCount = ownerCount
entity.collection.distribution = distribution

await eventHandlers.burnHandler(context, entity)

success(OPERATION, `${id} by ${event.caller}}`)
await context.store.save(entity)
const meta = entity.metadata ?? ''
Expand Down
8 changes: 3 additions & 5 deletions src/mappings/uniques/mint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { unwrap } from '../utils/extract'
import { debug, pending, success } from '../utils/logger'
import { Action, Context, createTokenId } from '../utils/types'
import { versionOf , calculateCollectionOwnerCountAndDistribution } from '../utils/helper'
import { handleTokenEntity } from '../shared/handleTokenEntity'
import { eventHandlers } from '../shared/handleTokenEntity'
import { getCreateTokenEvent } from './getters'

const OPERATION = Action.MINT
Expand Down Expand Up @@ -63,10 +63,8 @@ export async function handleTokenCreate(context: Context): Promise<void> {
final.media = metadata?.animationUrl
}

const token = await handleTokenEntity(context, collection, final)
if (token) {
final.token = token
}
await eventHandlers.mintHandler(context, collection, final)


success(OPERATION, `${final.id}`)
await context.store.save(final)
Expand Down
11 changes: 4 additions & 7 deletions src/mappings/uniques/setMetadata.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { get, getOptional } from '@kodadot1/metasquid/entity'
import { get, getOptional, getWith } from '@kodadot1/metasquid/entity'
import { isFetchable } from '@kodadot1/minipfs'
import { unwrap } from '../utils/extract'
import { Context, isNFT } from '../utils/types'
import { CollectionEntity, NFTEntity } from '../../model'
import { handleMetadata } from '../shared/metadata'
import { debug, warn } from '../utils/logger'
import { updateItemMetadataByCollection } from '../utils/cache'
import { handleTokenEntity } from '../shared/handleTokenEntity'
import { eventHandlers } from '../shared/handleTokenEntity'
import { tokenIdOf } from './types'
import { getMetadataEvent } from './getters'

Expand Down Expand Up @@ -52,11 +52,8 @@ export async function handleMetadataSet(context: Context): Promise<void> {
warn(OPERATION, `collection ${event.collectionId} not found`)
return
}
const nft = final as NFTEntity
const token = await handleTokenEntity(context, collection, nft)
if (token) {
nft.token = token
}

await eventHandlers.setMetadataHandler(context, collection, final as NFTEntity)
}
}

Expand Down
3 changes: 3 additions & 0 deletions src/model/generated/tokenEntity.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,7 @@ export class TokenEntity {

@Column_("int4", {nullable: false})
count!: number

@Column_("int4", {nullable: false})
totalCount!: number
}