Skip to content

Commit

Permalink
Merge pull request #112 from kodadot/main
Browse files Browse the repository at this point in the history
🔖  Release Speck v5
  • Loading branch information
vikiival authored Oct 12, 2023
2 parents 6888f0c + 5d97bc8 commit d03b9bc
Show file tree
Hide file tree
Showing 27 changed files with 611 additions and 80 deletions.
25 changes: 25 additions & 0 deletions db/migrations/1696751796584-Data.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
module.exports = class Data1696751796584 {
name = 'Data1696751796584'

async up(db) {
await db.query(`ALTER TABLE "token_entity" ADD "metadata" text`)
await db.query(`ALTER TABLE "token_entity" ADD "supply" integer NOT NULL`)
await db.query(`ALTER TABLE "token_entity" ADD "cheapest_id" character varying`)
await db.query(`ALTER TABLE "token_entity" ADD "meta_id" character varying`)
await db.query(`CREATE INDEX "IDX_637db5c040f1d9f935817ae1e8" ON "token_entity" ("cheapest_id") `)
await db.query(`CREATE INDEX "IDX_ae4ff3b28e3fec72aa14124d1e" ON "token_entity" ("meta_id") `)
await db.query(`ALTER TABLE "token_entity" ADD CONSTRAINT "FK_637db5c040f1d9f935817ae1e8a" FOREIGN KEY ("cheapest_id") REFERENCES "nft_entity"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`)
await db.query(`ALTER TABLE "token_entity" ADD CONSTRAINT "FK_ae4ff3b28e3fec72aa14124d1e1" FOREIGN KEY ("meta_id") REFERENCES "metadata_entity"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`)
}

async down(db) {
await db.query(`ALTER TABLE "token_entity" DROP COLUMN "metadata"`)
await db.query(`ALTER TABLE "token_entity" DROP COLUMN "supply"`)
await db.query(`ALTER TABLE "token_entity" DROP COLUMN "cheapest_id"`)
await db.query(`ALTER TABLE "token_entity" DROP COLUMN "meta_id"`)
await db.query(`DROP INDEX "public"."IDX_637db5c040f1d9f935817ae1e8"`)
await db.query(`DROP INDEX "public"."IDX_ae4ff3b28e3fec72aa14124d1e"`)
await db.query(`ALTER TABLE "token_entity" DROP CONSTRAINT "FK_637db5c040f1d9f935817ae1e8a"`)
await db.query(`ALTER TABLE "token_entity" DROP CONSTRAINT "FK_ae4ff3b28e3fec72aa14124d1e1"`)
}
}
3 changes: 3 additions & 0 deletions schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,12 @@ type TokenEntity @entity {
hash: String! @index
image: String
media: String
meta: MetadataEntity
metadata: String
name: String @index
updatedAt: DateTime!
createdAt: DateTime!
supply: Int!
count: Int!
}

Expand Down
5 changes: 3 additions & 2 deletions speck.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
manifestVersion: subsquid.io/v0.1
name: speck
version: 4
version: 5
description: 'SubSquid indexer for Uniques and Assets on Statemint'
build:
deploy:
Expand Down Expand Up @@ -33,4 +33,5 @@ scale:
dedicated: true
addons:
postgres:
storage: 5G
storage: 5G
profile: medium
5 changes: 3 additions & 2 deletions squid.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
manifestVersion: subsquid.io/v0.1
name: stick
version: 4
version: 5
description: 'SubSquid indexer for Uniques and Assets on Statemine'
build:
deploy:
Expand Down Expand Up @@ -33,4 +33,5 @@ scale:
dedicated: true
addons:
postgres:
storage: 5G
storage: 5G
profile: medium
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 { burnHandler } from '../shared/token'
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 burnHandler(context, entity)

success(OPERATION, `${id} by ${event.caller}}`)
await context.store.save(entity)
const meta = entity.metadata ?? ''
Expand Down
1 change: 0 additions & 1 deletion src/mappings/nfts/list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ export async function handleTokenList(context: Context): Promise<void> {
if (entity.price && (entity.collection.floor === 0n || entity.price < entity.collection.floor)) {
entity.collection.floor = entity.price
}

success(OPERATION, `${id} by ${event.caller}} for ${String(event.price)}`)
await context.store.save(entity)
const meta = String(event.price || '')
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 { mintHandler } from '../shared/token'
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 mintHandler(context, collection, final)
}

success(OPERATION, `${final.id}`)
Expand Down
16 changes: 7 additions & 9 deletions src/mappings/nfts/setMetadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ 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 { setMetadataHandler } from '../shared/token'
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 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)
}
}
}
44 changes: 0 additions & 44 deletions src/mappings/shared/handleTokenEntity.ts

This file was deleted.

24 changes: 24 additions & 0 deletions src/mappings/shared/token/burn.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { getOptional } from '@kodadot1/metasquid/entity'
import { Context } from '../../utils/types'
import { NFTEntity as NE, TokenEntity as TE } from '../../../model'
import { debug } from '../../utils/logger'
import { OPERATION, generateTokenId, mediaOf } from './utils'

export async function burnHandler(context: Context, nft: NE): Promise<void> {
debug(OPERATION, { handleBurn: `Handle Burn for NFT ${nft.id}` })

const nftMedia = mediaOf(nft)
if (!nftMedia) {
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} supply` })

await context.store.update(TE, token.id, { supply: token.supply - 1, updatedAt: nft.updatedAt })
}
3 changes: 3 additions & 0 deletions src/mappings/shared/token/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from './mint'
export * from './setMetadata'
export * from './burn'
20 changes: 20 additions & 0 deletions src/mappings/shared/token/mint.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { getOptional } from '@kodadot1/metasquid/entity'
import { Context } from '../../utils/types'
import { CollectionEntity as CE, NFTEntity as NE, TokenEntity as TE } from '../../../model'
import { debug } from '../../utils/logger'
import { OPERATION, generateTokenId, mediaOf } from './utils'
import { TokenAPI } from './tokenAPI'

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

const nftMedia = mediaOf(nft)
if (!nftMedia) {
return
}

const tokenApi = new TokenAPI(context.store)

const existingToken = await getOptional<TE>(context.store, TE, generateTokenId(collection.id, nftMedia))
return await (existingToken ? tokenApi.addNftToToken(nft, existingToken) : tokenApi.create(collection, nft))
}
32 changes: 32 additions & 0 deletions src/mappings/shared/token/setMetadata.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { getOptional, getWith } from '@kodadot1/metasquid/entity'
import { Context } from '../../utils/types'
import { CollectionEntity as CE, NFTEntity as NE, TokenEntity as TE } from '../../../model'
import { debug, warn } from '../../utils/logger'
import { OPERATION, generateTokenId, mediaOf } from './utils'
import { TokenAPI } from './tokenAPI'

export async function setMetadataHandler(context: Context, collection: CE, nft: NE): Promise<TE | undefined> {
debug(OPERATION, { handleMetadataSet: `Handle set metadata for NFT ${nft.id}` })
const nftMedia = mediaOf(nft)
if (!nftMedia) {
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
}

const tokenAPI = new TokenAPI(context.store)

if (nftWithToken.token) {
await tokenAPI.removeNftFromToken(nft, nftWithToken.token)
}
return await (existingToken ? tokenAPI.addNftToToken(nft, existingToken) : tokenAPI.create(collection, nft))
}
73 changes: 73 additions & 0 deletions src/mappings/shared/token/tokenAPI.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { create as createEntity } from '@kodadot1/metasquid/entity'
import md5 from 'md5'
import { Store } from '../../utils/types'
import { CollectionEntity as CE, NFTEntity as NE, TokenEntity as TE } from '../../../model'
import { debug } from '../../utils/logger'
import { OPERATION, generateTokenId, mediaOf, tokenName } from './utils'

export class TokenAPI {
constructor(private store: Store) {}

async create(collection: CE, nft: NE): Promise<TE | undefined> {
const nftMedia = mediaOf(nft)
if (!nftMedia) {
return
}
const tokenId = generateTokenId(collection.id, nftMedia)
debug(OPERATION, { createToken: `Create TOKEN ${tokenId} for NFT ${nft.id}` })

const token = createEntity(TE, tokenId, {
createdAt: nft.createdAt,
collection,
name: tokenName(nft.name),
count: 1,
supply: 1,
hash: md5(tokenId),
image: nft.image,
media: nft.media,
metadata: nft.metadata,
meta: nft.meta,
blockNumber: nft.blockNumber,
updatedAt: nft.updatedAt,
id: tokenId,
})

await this.store.save(token)
await this.store.update(NE, nft.id, { token })

return token
}

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

await this.store.update(NE, nft.id, { token: null })
const updatedCount = token.count - 1
await this.store.update(TE, token.id, {
supply: token.supply - 1,
count: updatedCount,
updatedAt: nft.updatedAt,
})

if (updatedCount === 0) {
debug(OPERATION, { deleteEmptyToken: `delete empty token ${token.id}` })

await this.store.delete(TE, token.id)
}
}

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

return token
}
}
23 changes: 23 additions & 0 deletions src/mappings/shared/token/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import md5 from 'md5'
import { NFTEntity as NE } from '../../../model'
import { warn } from '../../utils/logger'

export const OPERATION = 'TokenEntity' as any

export function generateTokenId(collectionId: string, nftMedia: string): string {
return `${collectionId}-${md5(nftMedia)}`
}

export const mediaOf = (nft: NE): string | undefined => {
const nftMedia = nft.image ?? nft.media

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

return nftMedia
}

export const tokenName = (nftName: string | undefined | null): string =>
typeof nftName === 'string' ? nftName?.replace(/([#_]\d+$)/g, '').trim() : ''
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 { burnHandler } from '../shared/token'
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 burnHandler(context, entity)

success(OPERATION, `${id} by ${event.caller}}`)
await context.store.save(entity)
const meta = entity.metadata ?? ''
Expand Down
Loading

0 comments on commit d03b9bc

Please sign in to comment.