diff --git a/package.json b/package.json index 16adc98c..8db17fd7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@kodadot1/stick", - "version": "0.0.2", + "version": "0.0.3", "description": "GraphQL service for Uniques and Assets on Statemine", "private": true, "engines": { diff --git a/speck.yaml b/speck.yaml index 31c18851..175beee5 100644 --- a/speck.yaml +++ b/speck.yaml @@ -1,6 +1,6 @@ manifestVersion: subsquid.io/v0.1 name: speck -version: 9 +version: 10 description: 'SubSquid indexer for Uniques and Assets on Statemint' build: deploy: diff --git a/squid.yaml b/squid.yaml index 7a8477b3..2243e09c 100644 --- a/squid.yaml +++ b/squid.yaml @@ -1,6 +1,6 @@ manifestVersion: subsquid.io/v0.1 name: stick -version: 8 +version: 9 description: 'SubSquid indexer for Uniques and Assets on Statemine' build: deploy: diff --git a/src/mappings/nfts/burn.ts b/src/mappings/nfts/burn.ts index d9f97686..f156d442 100644 --- a/src/mappings/nfts/burn.ts +++ b/src/mappings/nfts/burn.ts @@ -1,10 +1,10 @@ -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 { calculateCollectionFloor, calculateCollectionOwnerCountAndDistribution } from '../utils/helper' import { burnHandler } from '../shared/token' import { getBurnTokenEvent } from './getters' @@ -18,6 +18,7 @@ export async function handleTokenBurn(context: Context): Promise { const id = createTokenId(event.collectionId, event.sn) const entity = await getWith(context.store, NE, id, { collection: true }) + const { floor } = await calculateCollectionFloor(context.store, entity.collection.id, id) const { ownerCount, distribution } = await calculateCollectionOwnerCountAndDistribution( context.store, entity.collection.id, @@ -27,15 +28,17 @@ export async function handleTokenBurn(context: Context): Promise { entity.burned = true entity.updatedAt = event.timestamp - entity.collection.updatedAt = event.timestamp entity.collection.supply -= 1 + entity.collection.floor = floor entity.collection.ownerCount = ownerCount entity.collection.distribution = distribution + entity.collection.updatedAt = event.timestamp await burnHandler(context, entity) success(OPERATION, `${id} by ${event.caller}`) await context.store.save(entity) + await context.store.save(entity.collection) const meta = entity.metadata ?? '' await createEvent(entity, OPERATION, event, meta, context.store) } diff --git a/src/mappings/nfts/buy.ts b/src/mappings/nfts/buy.ts index 9d25b883..088e390e 100644 --- a/src/mappings/nfts/buy.ts +++ b/src/mappings/nfts/buy.ts @@ -1,10 +1,10 @@ -import { getOrFail } from '@kodadot1/metasquid/entity' -import { CollectionEntity as CE, NFTEntity as NE } from '../../model' +import { getWith } from '@kodadot1/metasquid/entity' +import { NFTEntity as NE } from '../../model' import { createEvent } from '../shared/event' import { unwrap } from '../utils/extract' import { debug, pending, success } from '../utils/logger' import { Action, Context, createTokenId } from '../utils/types' -import { calculateCollectionOwnerCountAndDistribution } from '../utils/helper' +import { calculateCollectionFloor, calculateCollectionOwnerCountAndDistribution } from '../utils/helper' import { getBuyTokenEvent } from './getters' const OPERATION = Action.BUY @@ -15,8 +15,7 @@ export async function handleTokenBuy(context: Context): Promise { debug(OPERATION, event, true) const id = createTokenId(event.collectionId, event.sn) - const entity = await getOrFail(context.store, NE, id) - const collection = await getOrFail(context.store, CE, event.collectionId) + const entity = await getWith(context.store, NE, id, { collection: true }) const originalPrice = event.price const originalOwner = entity.currentOwner ?? undefined @@ -24,25 +23,29 @@ export async function handleTokenBuy(context: Context): Promise { entity.price = BigInt(0) entity.currentOwner = event.caller entity.updatedAt = event.timestamp - + if (originalPrice) { - collection.volume += originalPrice - if (originalPrice > collection.highestSale) { - collection.highestSale = originalPrice + entity.collection.volume += originalPrice + if (originalPrice > entity.collection.highestSale) { + entity.collection.highestSale = originalPrice } } + + const { floor } = await calculateCollectionFloor(context.store, entity.collection.id, id) const { ownerCount, distribution } = await calculateCollectionOwnerCountAndDistribution( context.store, - collection.id, + entity.collection.id, entity.currentOwner, originalOwner ) - collection.ownerCount = ownerCount - collection.distribution = distribution + entity.collection.floor = floor + entity.collection.ownerCount = ownerCount + entity.collection.distribution = distribution + entity.collection.updatedAt = event.timestamp success(OPERATION, `${id} by ${event.caller} for ${String(event.price)}`) await context.store.save(entity) - await context.store.save(collection) + await context.store.save(entity.collection) const meta = String(event.price || '') await createEvent(entity, OPERATION, event, meta, context.store, event.currentOwner) } diff --git a/src/mappings/nfts/list.ts b/src/mappings/nfts/list.ts index 8fb8d82e..39b7ff98 100644 --- a/src/mappings/nfts/list.ts +++ b/src/mappings/nfts/list.ts @@ -1,5 +1,5 @@ -import { getOrFail } from '@kodadot1/metasquid/entity' -import { CollectionEntity as CE, NFTEntity as NE } from '../../model' +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' @@ -15,18 +15,17 @@ export async function handleTokenList(context: Context): Promise { debug(OPERATION, event, true) const id = createTokenId(event.collectionId, event.sn) - const entity = await getOrFail(context.store, NE, id) - const collection = await getOrFail(context.store, CE, event.collectionId) + const entity = await getWith(context.store, NE, id, { collection: true }) entity.price = event.price - if (event.price && (collection.floor === 0n || event.price < collection.floor)) { - collection.floor = event.price + if (event.price && (entity.collection.floor === 0n || event.price < entity.collection.floor)) { + entity.collection.floor = event.price } - success(OPERATION, `${id} by ${event.caller}} for ${String(event.price)}`) + success(OPERATION, `${id} by ${event.caller} for ${String(event.price)}`) await context.store.save(entity) - await context.store.save(collection) + await context.store.save(entity.collection) const meta = String(event.price || '') const interaction = event.price ? OPERATION : UNLIST await createEvent(entity, interaction, event, meta, context.store) diff --git a/src/mappings/nfts/setAttribute.ts b/src/mappings/nfts/setAttribute.ts index 4c343fc8..41ba3531 100644 --- a/src/mappings/nfts/setAttribute.ts +++ b/src/mappings/nfts/setAttribute.ts @@ -2,9 +2,9 @@ import { getOrFail as get } from '@kodadot1/metasquid/entity' import { CollectionEntity, NFTEntity } from '../../model' import { unwrap } from '../utils/extract' import { Context, isNFT } from '../utils/types' -import { addressOf } from '../utils/helper' +// import { addressOf } from '../utils/helper' import { getAttributeEvent } from './getters' -import { attributeFrom, tokenIdOf } from './types' +import { tokenIdOf } from './types' export async function handleAttributeSet(context: Context): Promise { const event = unwrap(context, getAttributeEvent) @@ -17,18 +17,18 @@ export async function handleAttributeSet(context: Context): Promise { final.attributes = [] } - if ('royalty' in final && event.trait === 'royalty') { - final.royalty = final.royalty ?? Number.parseFloat(event.value as string) - } + // if ('royalty' in final && event.trait === 'royalty') { + // final.royalty = final.royalty ?? Number.parseFloat(event.value as string) + // } - if ('recipient' in final && event.trait === 'recipient') { - try { - final.recipient = final.recipient ?? addressOf(event.value as string) - } catch (error) { - console.log(error) - final.recipient = final.recipient ?? (event.value as string) - } - } + // if ('recipient' in final && event.trait === 'recipient') { + // try { + // final.recipient = final.recipient ?? addressOf(event.value as string) + // } catch (error) { + // console.log(error) + // final.recipient = final.recipient ?? (event.value as string) + // } + // } if (event.value === null) { final.attributes = final.attributes?.filter((attr) => attr.trait !== event.trait) @@ -36,10 +36,11 @@ export async function handleAttributeSet(context: Context): Promise { const attribute = final.attributes?.find((attr) => attr.trait === event.trait) if (attribute) { attribute.value = String(event.value) - } else if (event.trait !== 'royalty' && event.trait !== 'recipient') { - const newAttribute = attributeFrom({ trait_type: event.trait, value: String(event.value) }) - final.attributes?.push(newAttribute) - } + } + // else if (event.trait !== 'royalty' && event.trait !== 'recipient') { + // const newAttribute = attributeFrom({ trait_type: event.trait, value: String(event.value) }) + // final.attributes?.push(newAttribute) + // } } await context.store.save(final) diff --git a/src/mappings/nfts/setMetadata.ts b/src/mappings/nfts/setMetadata.ts index 1d33df16..72dfeff3 100644 --- a/src/mappings/nfts/setMetadata.ts +++ b/src/mappings/nfts/setMetadata.ts @@ -46,6 +46,8 @@ export async function handleMetadataSet(context: Context): Promise { final.image = metadata?.image final.media = metadata?.animationUrl + await context.store.save(final) + if (eventIsOnNFT) { const collection = await getOptional(context.store, CollectionEntity, event.collectionId) @@ -58,8 +60,6 @@ export async function handleMetadataSet(context: Context): Promise { } } - await context.store.save(final) - if (!event.sn && final.metadata) { await updateItemMetadataByCollection(context.store, event.collectionId) } diff --git a/src/mappings/nfts/transfer.ts b/src/mappings/nfts/transfer.ts index 9e16e5c5..3f4e0cfd 100644 --- a/src/mappings/nfts/transfer.ts +++ b/src/mappings/nfts/transfer.ts @@ -33,5 +33,6 @@ export async function handleTokenTransfer(context: Context): Promise { success(OPERATION, `${id} from ${event.caller} to ${event.to}`) await context.store.save(entity) + await context.store.save(entity.collection) await createEvent(entity, OPERATION, event, event.to, context.store, oldOwner) } diff --git a/src/mappings/uniques/burn.ts b/src/mappings/uniques/burn.ts index 03c1bd48..f156d442 100644 --- a/src/mappings/uniques/burn.ts +++ b/src/mappings/uniques/burn.ts @@ -4,7 +4,7 @@ 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 { calculateCollectionFloor, calculateCollectionOwnerCountAndDistribution } from '../utils/helper' import { burnHandler } from '../shared/token' import { getBurnTokenEvent } from './getters' @@ -18,6 +18,7 @@ export async function handleTokenBurn(context: Context): Promise { const id = createTokenId(event.collectionId, event.sn) const entity = await getWith(context.store, NE, id, { collection: true }) + const { floor } = await calculateCollectionFloor(context.store, entity.collection.id, id) const { ownerCount, distribution } = await calculateCollectionOwnerCountAndDistribution( context.store, entity.collection.id, @@ -27,15 +28,17 @@ export async function handleTokenBurn(context: Context): Promise { entity.burned = true entity.updatedAt = event.timestamp - entity.collection.updatedAt = event.timestamp entity.collection.supply -= 1 + entity.collection.floor = floor entity.collection.ownerCount = ownerCount entity.collection.distribution = distribution + entity.collection.updatedAt = event.timestamp await burnHandler(context, entity) success(OPERATION, `${id} by ${event.caller}`) await context.store.save(entity) + await context.store.save(entity.collection) const meta = entity.metadata ?? '' await createEvent(entity, OPERATION, event, meta, context.store) } diff --git a/src/mappings/uniques/buy.ts b/src/mappings/uniques/buy.ts index 2f3752b4..088e390e 100644 --- a/src/mappings/uniques/buy.ts +++ b/src/mappings/uniques/buy.ts @@ -4,7 +4,7 @@ import { createEvent } from '../shared/event' import { unwrap } from '../utils/extract' import { debug, pending, success } from '../utils/logger' import { Action, Context, createTokenId } from '../utils/types' -import { calculateCollectionOwnerCountAndDistribution } from '../utils/helper' +import { calculateCollectionFloor, calculateCollectionOwnerCountAndDistribution } from '../utils/helper' import { getBuyTokenEvent } from './getters' const OPERATION = Action.BUY @@ -17,30 +17,35 @@ export async function handleTokenBuy(context: Context): Promise { const id = createTokenId(event.collectionId, event.sn) const entity = await getWith(context.store, NE, id, { collection: true }) - const originalPrice = entity.price + const originalPrice = event.price const originalOwner = entity.currentOwner ?? undefined entity.price = BigInt(0) entity.currentOwner = event.caller entity.updatedAt = event.timestamp + if (originalPrice) { entity.collection.volume += originalPrice if (originalPrice > entity.collection.highestSale) { entity.collection.highestSale = originalPrice } } + + const { floor } = await calculateCollectionFloor(context.store, entity.collection.id, id) const { ownerCount, distribution } = await calculateCollectionOwnerCountAndDistribution( context.store, entity.collection.id, entity.currentOwner, originalOwner ) + entity.collection.floor = floor entity.collection.ownerCount = ownerCount entity.collection.distribution = distribution - + entity.collection.updatedAt = event.timestamp success(OPERATION, `${id} by ${event.caller} for ${String(event.price)}`) await context.store.save(entity) + await context.store.save(entity.collection) const meta = String(event.price || '') await createEvent(entity, OPERATION, event, meta, context.store, event.currentOwner) } diff --git a/src/mappings/uniques/list.ts b/src/mappings/uniques/list.ts index d722e5a0..39b7ff98 100644 --- a/src/mappings/uniques/list.ts +++ b/src/mappings/uniques/list.ts @@ -19,12 +19,13 @@ export async function handleTokenList(context: Context): Promise { entity.price = event.price - if (entity.price && (entity.collection.floor === 0n || entity.price < entity.collection.floor)) { - entity.collection.floor = entity.price + if (event.price && (entity.collection.floor === 0n || event.price < entity.collection.floor)) { + entity.collection.floor = event.price } - success(OPERATION, `${id} by ${event.caller}} for ${String(event.price)}`) + success(OPERATION, `${id} by ${event.caller} for ${String(event.price)}`) await context.store.save(entity) + await context.store.save(entity.collection) const meta = String(event.price || '') const interaction = event.price ? OPERATION : UNLIST await createEvent(entity, interaction, event, meta, context.store) diff --git a/src/mappings/uniques/setMetadata.ts b/src/mappings/uniques/setMetadata.ts index 765d7504..aa4103ac 100644 --- a/src/mappings/uniques/setMetadata.ts +++ b/src/mappings/uniques/setMetadata.ts @@ -45,6 +45,8 @@ export async function handleMetadataSet(context: Context): Promise { final.image = metadata?.image final.media = metadata?.animationUrl + await context.store.save(final) + if (eventIsOnNFT) { const collection = await getOptional(context.store, CollectionEntity, event.collectionId) @@ -57,8 +59,6 @@ export async function handleMetadataSet(context: Context): Promise { } } - await context.store.save(final) - if (!event.sn && final.metadata) { await updateItemMetadataByCollection(context.store, event.collectionId) } diff --git a/src/mappings/uniques/transfer.ts b/src/mappings/uniques/transfer.ts index 9e16e5c5..3f4e0cfd 100644 --- a/src/mappings/uniques/transfer.ts +++ b/src/mappings/uniques/transfer.ts @@ -33,5 +33,6 @@ export async function handleTokenTransfer(context: Context): Promise { success(OPERATION, `${id} from ${event.caller} to ${event.to}`) await context.store.save(entity) + await context.store.save(entity.collection) await createEvent(entity, OPERATION, event, event.to, context.store, oldOwner) } diff --git a/src/mappings/utils/helper.ts b/src/mappings/utils/helper.ts index 09aba666..85c12728 100644 --- a/src/mappings/utils/helper.ts +++ b/src/mappings/utils/helper.ts @@ -113,3 +113,21 @@ export async function calculateCollectionOwnerCountAndDistribution( return adjustedResults } + +export async function calculateCollectionFloor( + store: Store, + collectionId: string, + nftId: string +): Promise<{ floor: bigint }> { + const query: string = ` + SELECT MIN(NULLIF(nft_entity.price, 0)) as floor + FROM nft_entity + WHERE collection_id = '${collectionId}' + AND nft_entity.id <> '${nftId}' + ` + const [result]: { floor: bigint; }[] = await store.query(query) + + return { + floor: result.floor ?? BigInt(0) + } +} diff --git a/src/mappings/utils/metadata.ts b/src/mappings/utils/metadata.ts index 8be47bb1..463338d9 100644 --- a/src/mappings/utils/metadata.ts +++ b/src/mappings/utils/metadata.ts @@ -13,7 +13,7 @@ export const fetchMetadata = async (metadata: string): Promise => { } return await $obtain(metadata, ['rmrk', 'infura_kodadot1'], true) } catch (e) { - logger.error(`[MINIPFS] ${e}}`) + logger.error(`[MINIPFS] ${e}`) } return ensure({})