From 264d8d3f67cdedcebe66d91ea199833350f6c0c4 Mon Sep 17 00:00:00 2001 From: Viki Val Date: Thu, 27 Jun 2024 16:40:28 +0300 Subject: [PATCH 01/59] :zap: getters --- src/mappings/nfts/getters/index.ts | 12 ++++ src/mappings/nfts/getters/kusama.ts | 97 +++++++++++++++++++++++++++ src/mappings/nfts/getters/polkadot.ts | 97 +++++++++++++++++++++++++++ 3 files changed, 206 insertions(+) diff --git a/src/mappings/nfts/getters/index.ts b/src/mappings/nfts/getters/index.ts index 463d7872..ed050520 100644 --- a/src/mappings/nfts/getters/index.ts +++ b/src/mappings/nfts/getters/index.ts @@ -144,3 +144,15 @@ export function getUpdateMintCall(_ctx: Context): UpdateMintSettings { const ctx = _ctx.call return proc.getUpdateMintCall(ctx) } + +export function getSwapCreatedEvent(ctx: Context) { + return proc.getSwapCreatedEvent(ctx); +} + +export function getSwapCancelledEvent(ctx: Context) { + return proc.getSwapCancelledEvent(ctx); +} + +export function getSwapClaimedEvent(ctx: Context) { + return proc.getSwapClaimedEvent(ctx); +} \ No newline at end of file diff --git a/src/mappings/nfts/getters/kusama.ts b/src/mappings/nfts/getters/kusama.ts index 1e95d253..3028ef5c 100644 --- a/src/mappings/nfts/getters/kusama.ts +++ b/src/mappings/nfts/getters/kusama.ts @@ -370,4 +370,101 @@ export function getUpdateMintCall(ctx: Call): UpdateMintSettings { const { collection: classId, mintSettings: { mintType, startBlock, endBlock, price } } = call.v9420.decode(ctx) return { id: classId.toString(), type: mintType, startBlock, endBlock, price } +} + +export function getSwapCreatedEvent(ctx: Event) { + const event = events.swapCreated + + if (event.v9420.is(ctx)) { + const { offeredCollection, offeredItem, desiredCollection, desiredItem, price, deadline } = event.v9420.decode(ctx) + + return { + offeredCollection: offeredCollection.toString(), + offeredItem: offeredItem.toString(), + desiredCollection: desiredCollection.toString(), + desiredItem: desiredItem?.toString(), + price, + deadline + } + } + + const { offeredCollection, offeredItem, desiredCollection, desiredItem, price, deadline } = event.v9420.decode(ctx) + return { + offeredCollection: offeredCollection.toString(), + offeredItem: offeredItem.toString(), + desiredCollection: desiredCollection.toString(), + desiredItem: desiredItem?.toString(), + price, + deadline, + } +} + +export function getSwapCancelledEvent(ctx: Event) { + const event = events.swapCancelled + + if (event.v9420.is(ctx)) { + const { offeredCollection, offeredItem, desiredCollection, desiredItem, price, deadline } = event.v9420.decode(ctx) + + return { + offeredCollection: offeredCollection.toString(), + offeredItem: offeredItem.toString(), + desiredCollection: desiredCollection.toString(), + desiredItem: desiredItem?.toString(), + price, + deadline, + } + } + + const { offeredCollection, offeredItem, desiredCollection, desiredItem, price, deadline } = event.v9420.decode( + ctx + ) + return { + offeredCollection: offeredCollection.toString(), + offeredItem: offeredItem.toString(), + desiredCollection: desiredCollection.toString(), + desiredItem: desiredItem?.toString(), + price, + deadline, + } +} + +export function getSwapClaimedEvent(ctx: Event) { + const event = events.swapClaimed + + if (event.v9420.is(ctx)) { + const { sentCollection, sentItem, sentItemOwner, receivedCollection, receivedItem, receivedItemOwner, price, deadline } = event.v9420.decode(ctx) + + return { + sentCollection: sentCollection.toString(), + sentItem: sentItem.toString(), + sentItemOwner: sentItemOwner ? addressOf(sentItemOwner) : '', + receivedCollection: receivedCollection.toString(), + receivedItem: receivedItem.toString(), + receivedItemOwner: receivedItemOwner ? addressOf(receivedItemOwner) : '', + price, + deadline, + } + } + + const { + sentCollection, + sentItem, + sentItemOwner, + receivedCollection, + receivedItem, + receivedItemOwner, + price, + deadline, + } = event.v9420.decode(ctx) + + return { + sentCollection: sentCollection.toString(), + sentItem: sentItem.toString(), + sentItemOwner: sentItemOwner ? addressOf(sentItemOwner) : '', + receivedCollection: receivedCollection.toString(), + receivedItem: receivedItem.toString(), + receivedItemOwner: receivedItemOwner ? addressOf(receivedItemOwner) : '', + price, + deadline, + } } \ No newline at end of file diff --git a/src/mappings/nfts/getters/polkadot.ts b/src/mappings/nfts/getters/polkadot.ts index 9592ab38..fd6cdfdf 100644 --- a/src/mappings/nfts/getters/polkadot.ts +++ b/src/mappings/nfts/getters/polkadot.ts @@ -374,3 +374,100 @@ export function getUpdateMintCall(ctx: Call): UpdateMintSettings { const { collection: classId, mintSettings: { mintType, startBlock, endBlock, price } } = call.v9430.decode(ctx) return { id: classId.toString(), type: mintType, startBlock, endBlock, price } } + +export function getSwapCreatedEvent(ctx: Event) { + const event = events.swapCreated + + if (event.v9430.is(ctx)) { + const { offeredCollection, offeredItem, desiredCollection, desiredItem, price, deadline } = event.v9430.decode(ctx) + + return { + offeredCollection: offeredCollection.toString(), + offeredItem: offeredItem.toString(), + desiredCollection: desiredCollection.toString(), + desiredItem: desiredItem?.toString(), + price, + deadline + } + } + + const { offeredCollection, offeredItem, desiredCollection, desiredItem, price, deadline } = event.v9430.decode(ctx) + return { + offeredCollection: offeredCollection.toString(), + offeredItem: offeredItem.toString(), + desiredCollection: desiredCollection.toString(), + desiredItem: desiredItem?.toString(), + price, + deadline, + } +} + +export function getSwapCancelledEvent(ctx: Event) { + const event = events.swapCancelled + + if (event.v9430.is(ctx)) { + const { offeredCollection, offeredItem, desiredCollection, desiredItem, price, deadline } = event.v9430.decode(ctx) + + return { + offeredCollection: offeredCollection.toString(), + offeredItem: offeredItem.toString(), + desiredCollection: desiredCollection.toString(), + desiredItem: desiredItem?.toString(), + price, + deadline, + } + } + + const { offeredCollection, offeredItem, desiredCollection, desiredItem, price, deadline } = event.v9430.decode( + ctx + ) + return { + offeredCollection: offeredCollection.toString(), + offeredItem: offeredItem.toString(), + desiredCollection: desiredCollection.toString(), + desiredItem: desiredItem?.toString(), + price, + deadline, + } +} + +export function getSwapClaimedEvent(ctx: Event) { + const event = events.swapClaimed + + if (event.v9430.is(ctx)) { + const { sentCollection, sentItem, sentItemOwner, receivedCollection, receivedItem, receivedItemOwner, price, deadline } = event.v9430.decode(ctx) + + return { + sentCollection: sentCollection.toString(), + sentItem: sentItem.toString(), + sentItemOwner: sentItemOwner ? addressOf(sentItemOwner) : '', + receivedCollection: receivedCollection.toString(), + receivedItem: receivedItem.toString(), + receivedItemOwner: receivedItemOwner ? addressOf(receivedItemOwner) : '', + price, + deadline, + } + } + + const { + sentCollection, + sentItem, + sentItemOwner, + receivedCollection, + receivedItem, + receivedItemOwner, + price, + deadline, + } = event.v9430.decode(ctx) + + return { + sentCollection: sentCollection.toString(), + sentItem: sentItem.toString(), + sentItemOwner: sentItemOwner ? addressOf(sentItemOwner) : '', + receivedCollection: receivedCollection.toString(), + receivedItem: receivedItem.toString(), + receivedItemOwner: receivedItemOwner ? addressOf(receivedItemOwner) : '', + price, + deadline, + } +} From d3ffff74a6bb9b23d4c0584054de8ba797d102a1 Mon Sep 17 00:00:00 2001 From: Viki Val Date: Thu, 27 Jun 2024 16:42:48 +0300 Subject: [PATCH 02/59] :zap: add swaps to processor --- src/processor.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/processor.ts b/src/processor.ts index b5413e65..e1621f26 100644 --- a/src/processor.ts +++ b/src/processor.ts @@ -3,7 +3,7 @@ import { } from '@subsquid/substrate-processor' import { TypeormDatabase as Database } from '@subsquid/typeorm-store' import logger from './mappings/utils/logger' -import { Asset, NonFungible, NonFungibleCall, Unique } from './processable' +import { Asset, NewNonFungible, NonFungible, NonFungibleCall, Unique } from './processable' import { CHAIN, getArchiveUrl, getNodeUrl } from './environment' import { mainFrame } from './mappings' @@ -101,6 +101,11 @@ processor.addEvent({ name: [NonFungible.changeTeam], call: true, extrinsic: true // processor.addEvent({ name: [NonFungible.thaw, dummy); processor.addEvent({ name: [NonFungible.transfer], call: true, extrinsic: true }) // n.handleTokenTransfer) + +processor.addEvent({ name: [NewNonFungible.createSwap], call: true, extrinsic: true }) +processor.addEvent({ name: [NewNonFungible.cancelSwap], call: true, extrinsic: true }) +processor.addEvent({ name: [NewNonFungible.claimSwap], call: true, extrinsic: true }) + processor.addCall({ name: [NonFungibleCall.updateMintSettings], extrinsic: true }) processor.setFields(fieldSelection) From 3028b3149301392485ea80787e4877188b91487d Mon Sep 17 00:00:00 2001 From: Viki Val Date: Mon, 1 Jul 2024 11:38:40 +0200 Subject: [PATCH 03/59] chore: Add handleCreateSwap function to createSwap.ts file --- src/mappings/nfts/createSwap.ts | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 src/mappings/nfts/createSwap.ts diff --git a/src/mappings/nfts/createSwap.ts b/src/mappings/nfts/createSwap.ts new file mode 100644 index 00000000..5a3ab48e --- /dev/null +++ b/src/mappings/nfts/createSwap.ts @@ -0,0 +1,24 @@ +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 } from '../utils/types' +import { getSwapCreatedEvent } from './getters' + +const OPERATION = Action.SEND + +export async function handleCreateSwap(context: Context): Promise { + pending(OPERATION, `${context.block.height}`) + const event = unwrap(context, getSwapCreatedEvent) + debug(OPERATION, event) + + // SwapCreated { + // offered_collection: T::CollectionId, + // offered_item: T::ItemId, + // desired_collection: T::CollectionId, + // desired_item: Option, + // price: Option>>, + // deadline: BlockNumberFor, + // }, +} From 6556b1f57774fc2d29d7e54c16d8cfd8885f4ccc Mon Sep 17 00:00:00 2001 From: Viki Val Date: Mon, 1 Jul 2024 11:38:48 +0200 Subject: [PATCH 04/59] feat: Add handleClaimSwap function to claimSwap.ts file --- src/mappings/nfts/claimSwap.ts | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 src/mappings/nfts/claimSwap.ts diff --git a/src/mappings/nfts/claimSwap.ts b/src/mappings/nfts/claimSwap.ts new file mode 100644 index 00000000..2f5ed26b --- /dev/null +++ b/src/mappings/nfts/claimSwap.ts @@ -0,0 +1,26 @@ +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 } from '../utils/types' +import { getSwapClaimedEvent } from './getters' + +const OPERATION = Action.SEND + +export async function handleClaimSwap(context: Context): Promise { + pending(OPERATION, `${context.block.height}`) + const event = unwrap(context, getSwapClaimedEvent) + debug(OPERATION, event) + + // SwapClaimed { + // sent_collection: T::CollectionId, + // sent_item: T::ItemId, + // sent_item_owner: T::AccountId, + // received_collection: T::CollectionId, + // received_item: T::ItemId, + // received_item_owner: T::AccountId, + // price: Option>>, + // deadline: BlockNumberFor, + // }, +} From 4a9f5e3f43ad0e699ff1255d53a47705fc03625f Mon Sep 17 00:00:00 2001 From: Viki Val Date: Mon, 1 Jul 2024 11:39:09 +0200 Subject: [PATCH 05/59] feat: Add cancelSwap.ts file for handling swap cancellation --- src/mappings/nfts/cancelSwap.ts | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 src/mappings/nfts/cancelSwap.ts diff --git a/src/mappings/nfts/cancelSwap.ts b/src/mappings/nfts/cancelSwap.ts new file mode 100644 index 00000000..7cc6add7 --- /dev/null +++ b/src/mappings/nfts/cancelSwap.ts @@ -0,0 +1,24 @@ +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 } from '../utils/types' +import { getSwapCancelledEvent } from './getters' + +const OPERATION = Action.SEND + +export async function handleCancelSwap(context: Context): Promise { + pending(OPERATION, `${context.block.height}`) + const event = unwrap(context, getSwapCancelledEvent) + debug(OPERATION, event) + + // SwapCancelled { + // offered_collection: T::CollectionId, + // offered_item: T::ItemId, + // desired_collection: T::CollectionId, + // desired_item: Option, + // price: Option>>, + // deadline: BlockNumberFor, + // }, +} From 2324357fd3771d043b4bdf889c55d3874518dc04 Mon Sep 17 00:00:00 2001 From: Viki Val Date: Mon, 1 Jul 2024 11:39:53 +0200 Subject: [PATCH 06/59] :alien: Swap --- schema.graphql | 78 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/schema.graphql b/schema.graphql index fc17caca..fa405c74 100644 --- a/schema.graphql +++ b/schema.graphql @@ -155,6 +155,64 @@ type CollectionEvent implements EventType @entity { # version: Int! } +# type OfferEvent implements EventType @entity { +# id: ID! +# blockNumber: BigInt +# caller: String! +# currentOwner: String # currentOwner +# interaction: OfferInteraction! +# meta: String! +# offer: Offer! +# timestamp: DateTime! +# } + + +# interface OfferType { +# id: ID! # should be addr-id-amount? +# blockNumber: BigInt! +# nft: NFTEntity! +# consideration: Consideration +# caller: String! +# createdAt: DateTime! +# expiration: BigInt! +# status: OfferStatus! +# updatedAt: DateTime +# } + +# type Offer implements OfferType @entity { +# id: ID! # should be addr-id-amount? +# blockNumber: BigInt! +# caller: String! +# createdAt: DateTime! +# events: [OfferEvent!] @derivedFrom(field: "offer") +# expiration: BigInt! +# nft: NFTEntity! +# price: BigInt! +# status: OfferStatus! +# updatedAt: DateTime +# } + +type Consideration @entity { + id: ID! + collection: CollectionEntity! + nft: NFTEntity +} + +type Swap @entity { + id: ID! # should be addr-id-amount? + blockNumber: BigInt! + caller: String! + createdAt: DateTime! + # events: [OfferEvent!] @derivedFrom(field: "offer") + consideration: Consideration! + expiration: BigInt! + nft: NFTEntity! + price: BigInt! + status: OfferStatus! + surcharge: Surcharge! + updatedAt: DateTime +} + # Possible on-chain interactions that we listen for enum Interaction { BURN @@ -168,6 +226,8 @@ enum Interaction { LOCK CHANGEISSUER PAY_ROYALTY + OFFER + SWAP # ROYALTY } @@ -181,6 +241,24 @@ enum CollectionType { Public } +enum Surcharge { + Receive + Send +} + +enum OfferInteraction { + CREATE + ACCEPT + CANCEL +} + +enum OfferStatus { + ACTIVE + ACCEPTED + EXPIRED + WITHDRAWN +} + # Entity to represent a Fungible Asset # defined on chain as pub type Asset, I: 'static = ()> # https://github.com/paritytech/polkadot-sdk/blob/99234440f0f8b24f7e4d1d3a0102a9b19a408dd3/substrate/frame/assets/src/lib.rs#L325 From f0eca6ce8ae9780760068501e75d2ab804b6523b Mon Sep 17 00:00:00 2001 From: Viki Val Date: Mon, 1 Jul 2024 11:40:07 +0200 Subject: [PATCH 07/59] :squid: swap --- src/model/generated/_interaction.ts | 2 + src/model/generated/_offerStatus.ts | 6 +++ src/model/generated/_surcharge.ts | 4 ++ src/model/generated/consideration.model.ts | 21 ++++++++++ src/model/generated/index.ts | 4 ++ src/model/generated/swap.model.ts | 47 ++++++++++++++++++++++ 6 files changed, 84 insertions(+) create mode 100644 src/model/generated/_offerStatus.ts create mode 100644 src/model/generated/_surcharge.ts create mode 100644 src/model/generated/consideration.model.ts create mode 100644 src/model/generated/swap.model.ts diff --git a/src/model/generated/_interaction.ts b/src/model/generated/_interaction.ts index 5f1bba48..44debadb 100644 --- a/src/model/generated/_interaction.ts +++ b/src/model/generated/_interaction.ts @@ -10,4 +10,6 @@ export enum Interaction { LOCK = "LOCK", CHANGEISSUER = "CHANGEISSUER", PAY_ROYALTY = "PAY_ROYALTY", + OFFER = "OFFER", + SWAP = "SWAP", } diff --git a/src/model/generated/_offerStatus.ts b/src/model/generated/_offerStatus.ts new file mode 100644 index 00000000..441ccde0 --- /dev/null +++ b/src/model/generated/_offerStatus.ts @@ -0,0 +1,6 @@ +export enum OfferStatus { + ACTIVE = "ACTIVE", + ACCEPTED = "ACCEPTED", + EXPIRED = "EXPIRED", + WITHDRAWN = "WITHDRAWN", +} diff --git a/src/model/generated/_surcharge.ts b/src/model/generated/_surcharge.ts new file mode 100644 index 00000000..39988f5b --- /dev/null +++ b/src/model/generated/_surcharge.ts @@ -0,0 +1,4 @@ +export enum Surcharge { + Receive = "Receive", + Send = "Send", +} diff --git a/src/model/generated/consideration.model.ts b/src/model/generated/consideration.model.ts new file mode 100644 index 00000000..34792570 --- /dev/null +++ b/src/model/generated/consideration.model.ts @@ -0,0 +1,21 @@ +import {Entity as Entity_, Column as Column_, PrimaryColumn as PrimaryColumn_, ManyToOne as ManyToOne_, Index as Index_} from "@subsquid/typeorm-store" +import {CollectionEntity} from "./collectionEntity.model" +import {NFTEntity} from "./nftEntity.model" + +@Entity_() +export class Consideration { + constructor(props?: Partial) { + Object.assign(this, props) + } + + @PrimaryColumn_() + id!: string + + @Index_() + @ManyToOne_(() => CollectionEntity, {nullable: true}) + collection!: CollectionEntity + + @Index_() + @ManyToOne_(() => NFTEntity, {nullable: true}) + nft!: NFTEntity | undefined | null +} diff --git a/src/model/generated/index.ts b/src/model/generated/index.ts index fc5e7f8a..4d5d2030 100644 --- a/src/model/generated/index.ts +++ b/src/model/generated/index.ts @@ -8,5 +8,9 @@ export * from "./metadataEntity.model" export * from "./event.model" export * from "./_interaction" export * from "./collectionEvent.model" +export * from "./consideration.model" +export * from "./swap.model" +export * from "./_offerStatus" +export * from "./_surcharge" export * from "./assetEntity.model" export * from "./cacheStatus.model" diff --git a/src/model/generated/swap.model.ts b/src/model/generated/swap.model.ts new file mode 100644 index 00000000..b693fc24 --- /dev/null +++ b/src/model/generated/swap.model.ts @@ -0,0 +1,47 @@ +import {Entity as Entity_, Column as Column_, PrimaryColumn as PrimaryColumn_, BigIntColumn as BigIntColumn_, StringColumn as StringColumn_, DateTimeColumn as DateTimeColumn_, ManyToOne as ManyToOne_, Index as Index_} from "@subsquid/typeorm-store" +import {Consideration} from "./consideration.model" +import {NFTEntity} from "./nftEntity.model" +import {OfferStatus} from "./_offerStatus" +import {Surcharge} from "./_surcharge" + +@Entity_() +export class Swap { + constructor(props?: Partial) { + Object.assign(this, props) + } + + @PrimaryColumn_() + id!: string + + @BigIntColumn_({nullable: false}) + blockNumber!: bigint + + @StringColumn_({nullable: false}) + caller!: string + + @DateTimeColumn_({nullable: false}) + createdAt!: Date + + @Index_() + @ManyToOne_(() => Consideration, {nullable: true}) + consideration!: Consideration + + @BigIntColumn_({nullable: false}) + expiration!: bigint + + @Index_() + @ManyToOne_(() => NFTEntity, {nullable: true}) + nft!: NFTEntity + + @BigIntColumn_({nullable: false}) + price!: bigint + + @Column_("varchar", {length: 9, nullable: false}) + status!: OfferStatus + + @Column_("varchar", {length: 7, nullable: false}) + surcharge!: Surcharge + + @DateTimeColumn_({nullable: true}) + updatedAt!: Date | undefined | null +} From 5c21147971ba47f48912d1c376883545232ada12 Mon Sep 17 00:00:00 2001 From: Viki Val Date: Mon, 1 Jul 2024 12:52:34 +0200 Subject: [PATCH 08/59] feat: Add Surcharge to SwapData in types.ts --- src/mappings/nfts/types.ts | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/mappings/nfts/types.ts b/src/mappings/nfts/types.ts index a862f267..e5ca17f0 100644 --- a/src/mappings/nfts/types.ts +++ b/src/mappings/nfts/types.ts @@ -1,5 +1,5 @@ import { ArchiveCall, ArchiveCallWithOptionalValue, MetadataAttribute, Optional } from '@kodadot1/metasquid/types' -import { Attribute, CollectionSettings } from '../../model' +import { Attribute, CollectionSettings, Surcharge } from '../../model' import { createTokenId } from '../utils/types' export type WithId = { @@ -86,6 +86,25 @@ export type UpdateMintSettings = WithId & { price: Optional, } +type SwapData = { + price: Optional, + surcharge: Optional, + deadline: number, +} + +type BaseSwapEvent = BaseTokenEvent & SwapData + +export type CreateSwapEvent = BaseSwapEvent & { + consideration: { + collectionId: string + sn?: string + } +} + +export type ClaimSwapEvent = BaseSwapEvent & { + +} + export const tokenIdOf = (base: BaseTokenEvent): string => createTokenId(base.collectionId, base.sn) export function attributeFrom(attribute: MetadataAttribute): Attribute { From 391d9a61bda42a10a671d30dbf5749225d6d2adb Mon Sep 17 00:00:00 2001 From: Viki Val Date: Mon, 1 Jul 2024 12:57:14 +0200 Subject: [PATCH 09/59] refactor: Update Swap model associations and nullable fields --- src/model/generated/swap.model.ts | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/model/generated/swap.model.ts b/src/model/generated/swap.model.ts index b693fc24..a893f294 100644 --- a/src/model/generated/swap.model.ts +++ b/src/model/generated/swap.model.ts @@ -1,5 +1,5 @@ import {Entity as Entity_, Column as Column_, PrimaryColumn as PrimaryColumn_, BigIntColumn as BigIntColumn_, StringColumn as StringColumn_, DateTimeColumn as DateTimeColumn_, ManyToOne as ManyToOne_, Index as Index_} from "@subsquid/typeorm-store" -import {Consideration} from "./consideration.model" +import {CollectionEntity} from "./collectionEntity.model" import {NFTEntity} from "./nftEntity.model" import {OfferStatus} from "./_offerStatus" import {Surcharge} from "./_surcharge" @@ -23,8 +23,12 @@ export class Swap { createdAt!: Date @Index_() - @ManyToOne_(() => Consideration, {nullable: true}) - consideration!: Consideration + @ManyToOne_(() => CollectionEntity, {nullable: true}) + considered!: CollectionEntity + + @Index_() + @ManyToOne_(() => NFTEntity, {nullable: true}) + desired!: NFTEntity | undefined | null @BigIntColumn_({nullable: false}) expiration!: bigint @@ -33,14 +37,14 @@ export class Swap { @ManyToOne_(() => NFTEntity, {nullable: true}) nft!: NFTEntity - @BigIntColumn_({nullable: false}) - price!: bigint + @BigIntColumn_({nullable: true}) + price!: bigint | undefined | null @Column_("varchar", {length: 9, nullable: false}) status!: OfferStatus - @Column_("varchar", {length: 7, nullable: false}) - surcharge!: Surcharge + @Column_("varchar", {length: 7, nullable: true}) + surcharge!: Surcharge | undefined | null @DateTimeColumn_({nullable: true}) updatedAt!: Date | undefined | null From 3ea459a5cc79b2ff4c3f989d6c6a7f9a5694e076 Mon Sep 17 00:00:00 2001 From: Viki Val Date: Mon, 1 Jul 2024 12:58:09 +0200 Subject: [PATCH 10/59] refactor: Update Swap model associations and nullable fields --- src/mappings/nfts/getters/index.ts | 3 ++- src/mappings/nfts/getters/kusama.ts | 34 +++++++++++++++++---------- src/mappings/nfts/getters/polkadot.ts | 31 +++++++++++++++--------- 3 files changed, 43 insertions(+), 25 deletions(-) diff --git a/src/mappings/nfts/getters/index.ts b/src/mappings/nfts/getters/index.ts index ed050520..9ab1a972 100644 --- a/src/mappings/nfts/getters/index.ts +++ b/src/mappings/nfts/getters/index.ts @@ -6,6 +6,7 @@ import { ChangeCollectionOwnerEvent, ChangeCollectionTeam, CreateCollectionEvent, + CreateSwapEvent, CreateTokenEvent, DestroyCollectionEvent, ForceCreateCollectionEvent, @@ -145,7 +146,7 @@ export function getUpdateMintCall(_ctx: Context): UpdateMintSettings { return proc.getUpdateMintCall(ctx) } -export function getSwapCreatedEvent(ctx: Context) { +export function getSwapCreatedEvent(ctx: Context): CreateSwapEvent { return proc.getSwapCreatedEvent(ctx); } diff --git a/src/mappings/nfts/getters/kusama.ts b/src/mappings/nfts/getters/kusama.ts index 3028ef5c..f6dd4df7 100644 --- a/src/mappings/nfts/getters/kusama.ts +++ b/src/mappings/nfts/getters/kusama.ts @@ -1,14 +1,15 @@ import { NonFungible } from '../../../processable' import { nfts as events } from '../../../types/kusama/events' import { nfts as calls } from '../../../types/kusama/calls' -import { addressOf, onlyValue, unHex } from '../../utils/helper' -import { Event, Call } from '../../utils/types' +import { addressOf, onlyKind, unHex } from '../../utils/helper' +import { Event, Call, Optional } from '../../utils/types' import { BurnTokenEvent, BuyTokenEvent, ChangeCollectionOwnerEvent, ChangeCollectionTeam, CreateCollectionEvent, + CreateSwapEvent, CreateTokenEvent, DestroyCollectionEvent, ForceCreateCollectionEvent, @@ -19,6 +20,7 @@ import { TransferTokenEvent, UpdateMintSettings, } from '../types' +import { Surcharge } from '../../../model' export function getCreateCollectionEvent(ctx: Event): CreateCollectionEvent { const event = events.created @@ -372,29 +374,35 @@ export function getUpdateMintCall(ctx: Call): UpdateMintSettings { return { id: classId.toString(), type: mintType, startBlock, endBlock, price } } -export function getSwapCreatedEvent(ctx: Event) { +export function getSwapCreatedEvent(ctx: Event): CreateSwapEvent { const event = events.swapCreated if (event.v9420.is(ctx)) { const { offeredCollection, offeredItem, desiredCollection, desiredItem, price, deadline } = event.v9420.decode(ctx) return { - offeredCollection: offeredCollection.toString(), - offeredItem: offeredItem.toString(), - desiredCollection: desiredCollection.toString(), - desiredItem: desiredItem?.toString(), - price, + collectionId: offeredCollection.toString(), + sn: offeredItem.toString(), + consideration: { + collectionId: desiredCollection.toString(), + sn: desiredItem?.toString(), + }, + price: price?.amount, + surcharge: price?.direction.__kind as Optional, deadline } } const { offeredCollection, offeredItem, desiredCollection, desiredItem, price, deadline } = event.v9420.decode(ctx) return { - offeredCollection: offeredCollection.toString(), - offeredItem: offeredItem.toString(), - desiredCollection: desiredCollection.toString(), - desiredItem: desiredItem?.toString(), - price, + collectionId: offeredCollection.toString(), + sn: offeredItem.toString(), + consideration: { + collectionId: desiredCollection.toString(), + sn: desiredItem?.toString(), + }, + price: price?.amount, + surcharge: price?.direction.__kind as Optional, deadline, } } diff --git a/src/mappings/nfts/getters/polkadot.ts b/src/mappings/nfts/getters/polkadot.ts index fd6cdfdf..4322c861 100644 --- a/src/mappings/nfts/getters/polkadot.ts +++ b/src/mappings/nfts/getters/polkadot.ts @@ -10,6 +10,7 @@ import { ChangeCollectionOwnerEvent, ChangeCollectionTeam, CreateCollectionEvent, + CreateSwapEvent, CreateTokenEvent, DestroyCollectionEvent, ForceCreateCollectionEvent, @@ -21,6 +22,8 @@ import { UpdateMintSettings, } from '../types' import { debug } from '../../utils/logger' +import { Surcharge } from '../../../model' +import { Optional } from '@kodadot1/metasquid/types' export function getCreateCollectionEvent(ctx: Event): CreateCollectionEvent { const event = events.created @@ -375,29 +378,35 @@ export function getUpdateMintCall(ctx: Call): UpdateMintSettings { return { id: classId.toString(), type: mintType, startBlock, endBlock, price } } -export function getSwapCreatedEvent(ctx: Event) { +export function getSwapCreatedEvent(ctx: Event): CreateSwapEvent { const event = events.swapCreated if (event.v9430.is(ctx)) { const { offeredCollection, offeredItem, desiredCollection, desiredItem, price, deadline } = event.v9430.decode(ctx) return { - offeredCollection: offeredCollection.toString(), - offeredItem: offeredItem.toString(), - desiredCollection: desiredCollection.toString(), - desiredItem: desiredItem?.toString(), - price, + collectionId: offeredCollection.toString(), + sn: offeredItem.toString(), + consideration: { + collectionId: desiredCollection.toString(), + sn: desiredItem?.toString(), + }, + price: price?.amount, + surcharge: price?.direction.__kind as Optional, deadline } } const { offeredCollection, offeredItem, desiredCollection, desiredItem, price, deadline } = event.v9430.decode(ctx) return { - offeredCollection: offeredCollection.toString(), - offeredItem: offeredItem.toString(), - desiredCollection: desiredCollection.toString(), - desiredItem: desiredItem?.toString(), - price, + collectionId: offeredCollection.toString(), + sn: offeredItem.toString(), + consideration: { + collectionId: desiredCollection.toString(), + sn: desiredItem?.toString(), + }, + price: price?.amount, + surcharge: price?.direction.__kind as Optional, deadline, } } From 228dbf3b284da7ec85756702351fc49b7c7b771a Mon Sep 17 00:00:00 2001 From: Viki Val Date: Mon, 1 Jul 2024 13:00:36 +0200 Subject: [PATCH 11/59] :ambulance: magic code --- src/mappings/nfts/getters/index.ts | 115 ++++++++++++++-------------- src/mappings/nfts/getters/kusama.ts | 2 +- 2 files changed, 60 insertions(+), 57 deletions(-) diff --git a/src/mappings/nfts/getters/index.ts b/src/mappings/nfts/getters/index.ts index 9ab1a972..418eafc8 100644 --- a/src/mappings/nfts/getters/index.ts +++ b/src/mappings/nfts/getters/index.ts @@ -22,138 +22,141 @@ import { const proc = require(`./${CHAIN}`) export function getCreateCollectionEvent(_ctx: Context): CreateCollectionEvent { - const ctx = _ctx.event - return proc.getCreateCollectionEvent(ctx) + const ctx = _ctx.event + return proc.getCreateCollectionEvent(ctx) } export function getForceCreateCollectionEvent(_ctx: Context): ForceCreateCollectionEvent { - const ctx = _ctx.event - return proc.getForceCreateCollectionEvent(ctx) + const ctx = _ctx.event + return proc.getForceCreateCollectionEvent(ctx) } export function getCreateTokenEvent(_ctx: Context): CreateTokenEvent { - const ctx = _ctx.event - return proc.getCreateTokenEvent(ctx) + const ctx = _ctx.event + return proc.getCreateTokenEvent(ctx) } export function getTransferTokenEvent(_ctx: Context): TransferTokenEvent { - const ctx = _ctx.event - return proc.getTransferTokenEvent(ctx) + const ctx = _ctx.event + return proc.getTransferTokenEvent(ctx) } export function getTipSentEvent(_ctx: Context) { - const ctx = _ctx.event - return proc.getTipSentEvent(ctx) + const ctx = _ctx.event + return proc.getTipSentEvent(ctx) } export function getBurnTokenEvent(_ctx: Context): BurnTokenEvent { - const ctx = _ctx.event - return proc.getBurnTokenEvent(ctx) + const ctx = _ctx.event + return proc.getBurnTokenEvent(ctx) } export function getDestroyCollectionEvent(_ctx: Context): DestroyCollectionEvent { - const ctx = _ctx.event - return proc.getDestroyCollectionEvent(ctx) + const ctx = _ctx.event + return proc.getDestroyCollectionEvent(ctx) } export function getListTokenEvent(_ctx: Context): ListTokenEvent { - const ctx = _ctx.event - return proc.getListTokenEvent(ctx) + const ctx = _ctx.event + return proc.getListTokenEvent(ctx) } export function getUnListTokenEvent(_ctx: Context): ListTokenEvent { - const ctx = _ctx.event - return proc.getUnListTokenEvent(ctx) + const ctx = _ctx.event + return proc.getUnListTokenEvent(ctx) } export function getPriceTokenEvent(_ctx: Context): ListTokenEvent { - const ctx = _ctx.event - return proc.getPriceTokenEvent(ctx) + const ctx = _ctx.event + return proc.getPriceTokenEvent(ctx) } export function getBuyTokenEvent(_ctx: Context): BuyTokenEvent { - const ctx = _ctx.event - return proc.getBuyTokenEvent(ctx) + const ctx = _ctx.event + return proc.getBuyTokenEvent(ctx) } export function getLockCollectionEvent(_ctx: Context): LockCollectionEvent { - const ctx = _ctx.event - return proc.getLockCollectionEvent(ctx) + const ctx = _ctx.event + return proc.getLockCollectionEvent(ctx) } export function getChangeCollectionOwnerEvent(_ctx: Context): ChangeCollectionOwnerEvent { - const ctx = _ctx.event - return proc.getChangeCollectionOwnerEvent(ctx) + const ctx = _ctx.event + return proc.getChangeCollectionOwnerEvent(ctx) } export function getClearCollectionMetadataEvent(_ctx: Context): SetMetadata { - const ctx = _ctx.event - return proc.getClearCollectionMetadataEvent(ctx) + const ctx = _ctx.event + return proc.getClearCollectionMetadataEvent(ctx) } export function getCreateCollectionMetadataEvent(_ctx: Context): SetMetadata { - const ctx = _ctx.event - return proc.getCreateCollectionMetadataEvent(ctx) + const ctx = _ctx.event + return proc.getCreateCollectionMetadataEvent(ctx) } export function getClearClassMetadataEvent(_ctx: Context): SetMetadata { - const ctx = _ctx.event - return proc.getClearClassMetadataEvent(ctx) + const ctx = _ctx.event + return proc.getClearClassMetadataEvent(ctx) } export function getCreateClassMetadataEvent(_ctx: Context): SetMetadata { - const ctx = _ctx.event - return proc.getCreateClassMetadataEvent(ctx) + const ctx = _ctx.event + return proc.getCreateClassMetadataEvent(ctx) } export function getCreateMetadataEvent(_ctx: Context): SetMetadata { - const ctx = _ctx.event - return proc.getCreateMetadataEvent(ctx) + const ctx = _ctx.event + return proc.getCreateMetadataEvent(ctx) } export function getClearMetadataEvent(_ctx: Context): SetMetadata { - const ctx = _ctx.event - return proc.getClearMetadataEvent(ctx) + const ctx = _ctx.event + return proc.getClearMetadataEvent(ctx) } export function getMetadataEvent(_ctx: Context): SetMetadata { - const ctx = _ctx.event - return proc.getMetadataEvent(ctx) + const ctx = _ctx.event + return proc.getMetadataEvent(ctx) } export function getSetAttributeEvent(_ctx: Context): SetAttribute { - const ctx = _ctx.event - return proc.getSetAttributeEvent(ctx) + const ctx = _ctx.event + return proc.getSetAttributeEvent(ctx) } export function getClearAttributeEvent(_ctx: Context): SetAttribute { - const ctx = _ctx.event - return proc.getClearAttributeEvent(ctx) + const ctx = _ctx.event + return proc.getClearAttributeEvent(ctx) } export function getAttributeEvent(_ctx: Context): SetAttribute { - const ctx = _ctx.event - return proc.getAttributeEvent(ctx) + const ctx = _ctx.event + return proc.getAttributeEvent(ctx) } export function getChangeTeamEvent(_ctx: Context): ChangeCollectionTeam { - const ctx = _ctx.event - return proc.getChangeTeamEvent(ctx) + const ctx = _ctx.event + return proc.getChangeTeamEvent(ctx) } export function getUpdateMintCall(_ctx: Context): UpdateMintSettings { - const ctx = _ctx.call - return proc.getUpdateMintCall(ctx) + const ctx = _ctx.call + return proc.getUpdateMintCall(ctx) } -export function getSwapCreatedEvent(ctx: Context): CreateSwapEvent { - return proc.getSwapCreatedEvent(ctx); +export function getSwapCreatedEvent(_ctx: Context): CreateSwapEvent { + const ctx = _ctx.event + return proc.getSwapCreatedEvent(ctx) } -export function getSwapCancelledEvent(ctx: Context) { - return proc.getSwapCancelledEvent(ctx); +export function getSwapCancelledEvent(_ctx: Context) { + const ctx = _ctx.event + return proc.getSwapCancelledEvent(ctx) } -export function getSwapClaimedEvent(ctx: Context) { - return proc.getSwapClaimedEvent(ctx); +export function getSwapClaimedEvent(_ctx: Context) { + const ctx = _ctx.event + return proc.getSwapClaimedEvent(ctx) } \ No newline at end of file diff --git a/src/mappings/nfts/getters/kusama.ts b/src/mappings/nfts/getters/kusama.ts index f6dd4df7..ef157ebc 100644 --- a/src/mappings/nfts/getters/kusama.ts +++ b/src/mappings/nfts/getters/kusama.ts @@ -1,7 +1,7 @@ import { NonFungible } from '../../../processable' import { nfts as events } from '../../../types/kusama/events' import { nfts as calls } from '../../../types/kusama/calls' -import { addressOf, onlyKind, unHex } from '../../utils/helper' +import { addressOf, unHex } from '../../utils/helper' import { Event, Call, Optional } from '../../utils/types' import { BurnTokenEvent, From 595fa85ce23ca31e3d3d82fb4f052abfef522b44 Mon Sep 17 00:00:00 2001 From: Viki Val Date: Mon, 1 Jul 2024 13:00:57 +0200 Subject: [PATCH 12/59] feat: Update createSwap.ts to handle swap creation and saving --- src/mappings/nfts/createSwap.ts | 36 ++++++++++++++++++++++++++++----- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/src/mappings/nfts/createSwap.ts b/src/mappings/nfts/createSwap.ts index 5a3ab48e..ff1f590a 100644 --- a/src/mappings/nfts/createSwap.ts +++ b/src/mappings/nfts/createSwap.ts @@ -1,18 +1,44 @@ -import { getWith } from '@kodadot1/metasquid/entity' -import { NFTEntity as NE } from '../../model' +import { getOrFail as get, getOrCreate } from '@kodadot1/metasquid/entity' +import { CollectionEntity as CE, NFTEntity as NE, OfferStatus, Swap } from '../../model' import { createEvent } from '../shared/event' import { unwrap } from '../utils/extract' import { debug, pending, success } from '../utils/logger' -import { Action, Context } from '../utils/types' +import { Action, Context, createTokenId, isNFT } from '../utils/types' import { getSwapCreatedEvent } from './getters' +import { tokenIdOf } from './types' -const OPERATION = Action.SEND +const OPERATION = Action.SWAP export async function handleCreateSwap(context: Context): Promise { pending(OPERATION, `${context.block.height}`) const event = unwrap(context, getSwapCreatedEvent) - debug(OPERATION, event) + debug(OPERATION, event, true) + // TODO: SWAP CAN BE OVERWRITTEN! + const id = createTokenId(event.collectionId, event.sn) + const final = await getOrCreate(context.store, Swap, id, {}) + const deadline = BigInt(event.deadline) + // the nft that is being swapped + const nft = await get(context.store, NE, id) + const considered = await get(context.store, CE, event.consideration.collectionId) + const desired = isNFT(event.consideration) ? await get(context.store, NE, tokenIdOf(event as any)) : undefined + + final.blockNumber = BigInt(event.blockNumber) + final.createdAt = event.timestamp + final.caller = event.caller + final.nft = nft + final.considered = considered + final.desired = desired + final.price = event.price + final.surcharge = event.surcharge + final.status = final.blockNumber >= deadline ? OfferStatus.EXPIRED : OfferStatus.ACTIVE + final.updatedAt = event.timestamp + + // TODO: SAVE SOMEWHERE + + // success(OPERATION, `${id} by ${event.caller} for ${String(event.price)}`) + await context.store.save(final) + // SwapCreated { // offered_collection: T::CollectionId, // offered_item: T::ItemId, From 9a199c8db9dbdb11bfbc49ec67fe5c4d1a40298b Mon Sep 17 00:00:00 2001 From: Viki Val Date: Mon, 1 Jul 2024 13:01:14 +0200 Subject: [PATCH 13/59] :alien: schenma for swap :// --- schema.graphql | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/schema.graphql b/schema.graphql index fa405c74..b7165130 100644 --- a/schema.graphql +++ b/schema.graphql @@ -204,12 +204,14 @@ type Swap @entity { caller: String! createdAt: DateTime! # events: [OfferEvent!] @derivedFrom(field: "offer") - consideration: Consideration! + # consideration: Consideration! + considered: CollectionEntity! + desired: NFTEntity expiration: BigInt! nft: NFTEntity! - price: BigInt! + price: BigInt status: OfferStatus! - surcharge: Surcharge! + surcharge: Surcharge updatedAt: DateTime } From 1995fcb60b787c51be5ed10b027f24316aa5a91a Mon Sep 17 00:00:00 2001 From: Viki Val Date: Mon, 1 Jul 2024 13:09:57 +0200 Subject: [PATCH 14/59] feat: Update getSwapCancelledEvent to include surcharge in CreateSwapEvent --- src/mappings/nfts/getters/index.ts | 2 +- src/mappings/nfts/getters/kusama.ts | 28 ++++++++++++++++----------- src/mappings/nfts/getters/polkadot.ts | 28 ++++++++++++++++----------- 3 files changed, 35 insertions(+), 23 deletions(-) diff --git a/src/mappings/nfts/getters/index.ts b/src/mappings/nfts/getters/index.ts index 418eafc8..099d3d18 100644 --- a/src/mappings/nfts/getters/index.ts +++ b/src/mappings/nfts/getters/index.ts @@ -151,7 +151,7 @@ export function getSwapCreatedEvent(_ctx: Context): CreateSwapEvent { return proc.getSwapCreatedEvent(ctx) } -export function getSwapCancelledEvent(_ctx: Context) { +export function getSwapCancelledEvent(_ctx: Context): CreateSwapEvent { const ctx = _ctx.event return proc.getSwapCancelledEvent(ctx) } diff --git a/src/mappings/nfts/getters/kusama.ts b/src/mappings/nfts/getters/kusama.ts index ef157ebc..252d0afa 100644 --- a/src/mappings/nfts/getters/kusama.ts +++ b/src/mappings/nfts/getters/kusama.ts @@ -407,18 +407,21 @@ export function getSwapCreatedEvent(ctx: Event): CreateSwapEvent { } } -export function getSwapCancelledEvent(ctx: Event) { +export function getSwapCancelledEvent(ctx: Event): CreateSwapEvent { const event = events.swapCancelled if (event.v9420.is(ctx)) { const { offeredCollection, offeredItem, desiredCollection, desiredItem, price, deadline } = event.v9420.decode(ctx) return { - offeredCollection: offeredCollection.toString(), - offeredItem: offeredItem.toString(), - desiredCollection: desiredCollection.toString(), - desiredItem: desiredItem?.toString(), - price, + collectionId: offeredCollection.toString(), + sn: offeredItem.toString(), + consideration: { + collectionId: desiredCollection.toString(), + sn: desiredItem?.toString(), + }, + price: price?.amount, + surcharge: price?.direction.__kind as Optional, deadline, } } @@ -427,11 +430,14 @@ export function getSwapCancelledEvent(ctx: Event) { ctx ) return { - offeredCollection: offeredCollection.toString(), - offeredItem: offeredItem.toString(), - desiredCollection: desiredCollection.toString(), - desiredItem: desiredItem?.toString(), - price, + collectionId: offeredCollection.toString(), + sn: offeredItem.toString(), + consideration: { + collectionId: desiredCollection.toString(), + sn: desiredItem?.toString(), + }, + price: price?.amount, + surcharge: price?.direction.__kind as Optional, deadline, } } diff --git a/src/mappings/nfts/getters/polkadot.ts b/src/mappings/nfts/getters/polkadot.ts index 4322c861..441c9250 100644 --- a/src/mappings/nfts/getters/polkadot.ts +++ b/src/mappings/nfts/getters/polkadot.ts @@ -411,18 +411,21 @@ export function getSwapCreatedEvent(ctx: Event): CreateSwapEvent { } } -export function getSwapCancelledEvent(ctx: Event) { +export function getSwapCancelledEvent(ctx: Event): CreateSwapEvent { const event = events.swapCancelled if (event.v9430.is(ctx)) { const { offeredCollection, offeredItem, desiredCollection, desiredItem, price, deadline } = event.v9430.decode(ctx) return { - offeredCollection: offeredCollection.toString(), - offeredItem: offeredItem.toString(), - desiredCollection: desiredCollection.toString(), - desiredItem: desiredItem?.toString(), - price, + collectionId: offeredCollection.toString(), + sn: offeredItem.toString(), + consideration: { + collectionId: desiredCollection.toString(), + sn: desiredItem?.toString(), + }, + price: price?.amount, + surcharge: price?.direction.__kind as Optional, deadline, } } @@ -431,11 +434,14 @@ export function getSwapCancelledEvent(ctx: Event) { ctx ) return { - offeredCollection: offeredCollection.toString(), - offeredItem: offeredItem.toString(), - desiredCollection: desiredCollection.toString(), - desiredItem: desiredItem?.toString(), - price, + collectionId: offeredCollection.toString(), + sn: offeredItem.toString(), + consideration: { + collectionId: desiredCollection.toString(), + sn: desiredItem?.toString(), + }, + price: price?.amount, + surcharge: price?.direction.__kind as Optional, deadline, } } From cc4b25cbec869672ae6e06486c2be5ae3e2a34ea Mon Sep 17 00:00:00 2001 From: Viki Val Date: Mon, 1 Jul 2024 13:10:06 +0200 Subject: [PATCH 15/59] feat: Update cancelSwap.ts to handle swap cancellation and update status --- src/mappings/nfts/cancelSwap.ts | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/mappings/nfts/cancelSwap.ts b/src/mappings/nfts/cancelSwap.ts index 7cc6add7..57752d32 100644 --- a/src/mappings/nfts/cancelSwap.ts +++ b/src/mappings/nfts/cancelSwap.ts @@ -1,9 +1,9 @@ -import { getWith } from '@kodadot1/metasquid/entity' -import { NFTEntity as NE } from '../../model' +import { getOrFail as get } from '@kodadot1/metasquid/entity' +import { OfferStatus, Swap } from '../../model' import { createEvent } from '../shared/event' import { unwrap } from '../utils/extract' import { debug, pending, success } from '../utils/logger' -import { Action, Context } from '../utils/types' +import { Action, Context, createTokenId } from '../utils/types' import { getSwapCancelledEvent } from './getters' const OPERATION = Action.SEND @@ -13,6 +13,15 @@ export async function handleCancelSwap(context: Context): Promise { const event = unwrap(context, getSwapCancelledEvent) debug(OPERATION, event) + const id = createTokenId(event.collectionId, event.sn) + const entity = await get(context.store, Swap, id) + + entity.status = OfferStatus.WITHDRAWN + entity.updatedAt = event.timestamp + + success(OPERATION, `${id} by ${event.caller}`) + + await context.store.save(entity) // SwapCancelled { // offered_collection: T::CollectionId, // offered_item: T::ItemId, From f38bb972fc4eee1a7c96b9d20d83d578285d78c2 Mon Sep 17 00:00:00 2001 From: Viki Val Date: Mon, 1 Jul 2024 14:31:56 +0200 Subject: [PATCH 16/59] refactor: Update logger.ts to include OfferStatus in Action type --- src/mappings/utils/logger.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mappings/utils/logger.ts b/src/mappings/utils/logger.ts index 82802db9..ab6ca1c3 100644 --- a/src/mappings/utils/logger.ts +++ b/src/mappings/utils/logger.ts @@ -1,8 +1,8 @@ import { serializer } from '@kodadot1/metasquid' import { logger } from '@kodadot1/metasquid/logger' -import { Interaction } from '../../model' +import { Interaction, OfferStatus } from '../../model' -type Action = Interaction +type Action = Interaction | OfferStatus type ErrorCallback = (error: Error) => void From 6a21f83a61cb8993a223845fa622c6a1d1269018 Mon Sep 17 00:00:00 2001 From: Viki Val Date: Mon, 1 Jul 2024 14:32:03 +0200 Subject: [PATCH 17/59] feat: Include ClaimSwapEvent in getSwapClaimedEvent --- src/mappings/nfts/getters/index.ts | 3 ++- src/mappings/nfts/getters/kusama.ts | 37 ++++++++++++++++----------- src/mappings/nfts/getters/polkadot.ts | 37 ++++++++++++++++----------- 3 files changed, 46 insertions(+), 31 deletions(-) diff --git a/src/mappings/nfts/getters/index.ts b/src/mappings/nfts/getters/index.ts index 099d3d18..8afbbf8f 100644 --- a/src/mappings/nfts/getters/index.ts +++ b/src/mappings/nfts/getters/index.ts @@ -5,6 +5,7 @@ import { BuyTokenEvent, ChangeCollectionOwnerEvent, ChangeCollectionTeam, + ClaimSwapEvent, CreateCollectionEvent, CreateSwapEvent, CreateTokenEvent, @@ -156,7 +157,7 @@ export function getSwapCancelledEvent(_ctx: Context): CreateSwapEvent { return proc.getSwapCancelledEvent(ctx) } -export function getSwapClaimedEvent(_ctx: Context) { +export function getSwapClaimedEvent(_ctx: Context): ClaimSwapEvent { const ctx = _ctx.event return proc.getSwapClaimedEvent(ctx) } \ No newline at end of file diff --git a/src/mappings/nfts/getters/kusama.ts b/src/mappings/nfts/getters/kusama.ts index 252d0afa..b73cb69f 100644 --- a/src/mappings/nfts/getters/kusama.ts +++ b/src/mappings/nfts/getters/kusama.ts @@ -8,6 +8,7 @@ import { BuyTokenEvent, ChangeCollectionOwnerEvent, ChangeCollectionTeam, + ClaimSwapEvent, CreateCollectionEvent, CreateSwapEvent, CreateTokenEvent, @@ -442,20 +443,23 @@ export function getSwapCancelledEvent(ctx: Event): CreateSwapEvent { } } -export function getSwapClaimedEvent(ctx: Event) { +export function getSwapClaimedEvent(ctx: Event): ClaimSwapEvent { const event = events.swapClaimed if (event.v9420.is(ctx)) { const { sentCollection, sentItem, sentItemOwner, receivedCollection, receivedItem, receivedItemOwner, price, deadline } = event.v9420.decode(ctx) return { - sentCollection: sentCollection.toString(), - sentItem: sentItem.toString(), - sentItemOwner: sentItemOwner ? addressOf(sentItemOwner) : '', - receivedCollection: receivedCollection.toString(), - receivedItem: receivedItem.toString(), - receivedItemOwner: receivedItemOwner ? addressOf(receivedItemOwner) : '', - price, + collectionId: receivedCollection.toString(), + sn: receivedItem.toString(), + currentOwner: receivedItemOwner ? addressOf(receivedItemOwner) : '', + sent: { + collectionId: sentCollection.toString(), + sn: sentItem.toString(), + owner: sentItemOwner ? addressOf(sentItemOwner) : '', + }, + price: price?.amount, + surcharge: price?.direction.__kind as Optional, deadline, } } @@ -472,13 +476,16 @@ export function getSwapClaimedEvent(ctx: Event) { } = event.v9420.decode(ctx) return { - sentCollection: sentCollection.toString(), - sentItem: sentItem.toString(), - sentItemOwner: sentItemOwner ? addressOf(sentItemOwner) : '', - receivedCollection: receivedCollection.toString(), - receivedItem: receivedItem.toString(), - receivedItemOwner: receivedItemOwner ? addressOf(receivedItemOwner) : '', - price, + collectionId: receivedCollection.toString(), + sn: receivedItem.toString(), + currentOwner: receivedItemOwner ? addressOf(receivedItemOwner) : '', + sent: { + collectionId: sentCollection.toString(), + sn: sentItem.toString(), + owner: sentItemOwner ? addressOf(sentItemOwner) : '', + }, + price: price?.amount, + surcharge: price?.direction.__kind as Optional, deadline, } } \ No newline at end of file diff --git a/src/mappings/nfts/getters/polkadot.ts b/src/mappings/nfts/getters/polkadot.ts index 441c9250..daf0390d 100644 --- a/src/mappings/nfts/getters/polkadot.ts +++ b/src/mappings/nfts/getters/polkadot.ts @@ -9,6 +9,7 @@ import { BuyTokenEvent, ChangeCollectionOwnerEvent, ChangeCollectionTeam, + ClaimSwapEvent, CreateCollectionEvent, CreateSwapEvent, CreateTokenEvent, @@ -446,20 +447,23 @@ export function getSwapCancelledEvent(ctx: Event): CreateSwapEvent { } } -export function getSwapClaimedEvent(ctx: Event) { +export function getSwapClaimedEvent(ctx: Event): ClaimSwapEvent { const event = events.swapClaimed if (event.v9430.is(ctx)) { const { sentCollection, sentItem, sentItemOwner, receivedCollection, receivedItem, receivedItemOwner, price, deadline } = event.v9430.decode(ctx) return { - sentCollection: sentCollection.toString(), - sentItem: sentItem.toString(), - sentItemOwner: sentItemOwner ? addressOf(sentItemOwner) : '', - receivedCollection: receivedCollection.toString(), - receivedItem: receivedItem.toString(), - receivedItemOwner: receivedItemOwner ? addressOf(receivedItemOwner) : '', - price, + collectionId: receivedCollection.toString(), + sn: receivedItem.toString(), + currentOwner: receivedItemOwner ? addressOf(receivedItemOwner) : '', + sent: { + collectionId: sentCollection.toString(), + sn: sentItem.toString(), + owner: sentItemOwner ? addressOf(sentItemOwner) : '', + }, + price: price?.amount, + surcharge: price?.direction.__kind as Optional, deadline, } } @@ -476,13 +480,16 @@ export function getSwapClaimedEvent(ctx: Event) { } = event.v9430.decode(ctx) return { - sentCollection: sentCollection.toString(), - sentItem: sentItem.toString(), - sentItemOwner: sentItemOwner ? addressOf(sentItemOwner) : '', - receivedCollection: receivedCollection.toString(), - receivedItem: receivedItem.toString(), - receivedItemOwner: receivedItemOwner ? addressOf(receivedItemOwner) : '', - price, + collectionId: receivedCollection.toString(), + sn: receivedItem.toString(), + currentOwner: receivedItemOwner ? addressOf(receivedItemOwner) : '', + sent: { + collectionId: sentCollection.toString(), + sn: sentItem.toString(), + owner: sentItemOwner ? addressOf(sentItemOwner) : '', + }, + price: price?.amount, + surcharge: price?.direction.__kind as Optional, deadline, } } From 76f6c8424ad854b85608f9e3251b072851ffaa19 Mon Sep 17 00:00:00 2001 From: Viki Val Date: Mon, 1 Jul 2024 14:32:14 +0200 Subject: [PATCH 18/59] feat: Update claimSwap.ts to handle swap claim and update status --- src/mappings/nfts/claimSwap.ts | 18 ++++++++++++++---- src/mappings/nfts/types.ts | 7 ++++++- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/src/mappings/nfts/claimSwap.ts b/src/mappings/nfts/claimSwap.ts index 2f5ed26b..33c78bfd 100644 --- a/src/mappings/nfts/claimSwap.ts +++ b/src/mappings/nfts/claimSwap.ts @@ -1,18 +1,28 @@ -import { getWith } from '@kodadot1/metasquid/entity' -import { NFTEntity as NE } from '../../model' +import { getOrFail as get } from '@kodadot1/metasquid/entity' +import { NFTEntity as NE, OfferStatus, Swap } from '../../model' import { createEvent } from '../shared/event' import { unwrap } from '../utils/extract' import { debug, pending, success } from '../utils/logger' -import { Action, Context } from '../utils/types' +import { Action, Context, createTokenId } from '../utils/types' import { getSwapClaimedEvent } from './getters' -const OPERATION = Action.SEND +const OPERATION = OfferStatus.ACCEPTED export async function handleClaimSwap(context: Context): Promise { pending(OPERATION, `${context.block.height}`) const event = unwrap(context, getSwapClaimedEvent) debug(OPERATION, event) + const id = createTokenId(event.collectionId, event.sn) + const entity = await get(context.store, Swap, id) + + entity.status = OfferStatus.ACCEPTED + entity.updatedAt = event.timestamp + + success(OPERATION, `${id} by ${event.caller}`) + + await context.store.save(entity) + // SwapClaimed { // sent_collection: T::CollectionId, // sent_item: T::ItemId, diff --git a/src/mappings/nfts/types.ts b/src/mappings/nfts/types.ts index e5ca17f0..814320df 100644 --- a/src/mappings/nfts/types.ts +++ b/src/mappings/nfts/types.ts @@ -102,7 +102,12 @@ export type CreateSwapEvent = BaseSwapEvent & { } export type ClaimSwapEvent = BaseSwapEvent & { - + sent: { + collectionId: string + sn: string + owner: string + } + currentOwner: string } export const tokenIdOf = (base: BaseTokenEvent): string => createTokenId(base.collectionId, base.sn) From 9c92c59e378f4c35c8e44c155d2f86e5a17c76a9 Mon Sep 17 00:00:00 2001 From: Viki Val Date: Mon, 1 Jul 2024 14:32:20 +0200 Subject: [PATCH 19/59] refactor: Update cancelSwap.ts to use OfferStatus.WITHDRAWN for swap cancellation --- src/mappings/nfts/cancelSwap.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mappings/nfts/cancelSwap.ts b/src/mappings/nfts/cancelSwap.ts index 57752d32..8f4b08d3 100644 --- a/src/mappings/nfts/cancelSwap.ts +++ b/src/mappings/nfts/cancelSwap.ts @@ -6,7 +6,7 @@ import { debug, pending, success } from '../utils/logger' import { Action, Context, createTokenId } from '../utils/types' import { getSwapCancelledEvent } from './getters' -const OPERATION = Action.SEND +const OPERATION = OfferStatus.WITHDRAWN export async function handleCancelSwap(context: Context): Promise { pending(OPERATION, `${context.block.height}`) From 2aa6dcc2c99ef8944b6450aa25f5b4e1dfa35407 Mon Sep 17 00:00:00 2001 From: Viki Val Date: Mon, 1 Jul 2024 14:47:18 +0200 Subject: [PATCH 20/59] feat: Add swap cancellation and claim functionality --- src/mappings/index.ts | 9 +++++++++ src/mappings/nfts/index.ts | 3 +++ 2 files changed, 12 insertions(+) diff --git a/src/mappings/index.ts b/src/mappings/index.ts index 9d402d11..c724418b 100644 --- a/src/mappings/index.ts +++ b/src/mappings/index.ts @@ -151,6 +151,15 @@ export async function nfts(item: T, ctx: Context): Prom case NewNonFungible.sendTip: await n.handleTipSend(ctx) break + case NewNonFungible.createSwap: + await n.handleCreateSwap(ctx) + break + case NewNonFungible.claimSwap: + await n.handleClaimSwap(ctx) + break + case NewNonFungible.cancelSwap: + await n.handleCancelSwap(ctx) + break default: throw new Error(`Unknown event ${item.name}`) } diff --git a/src/mappings/nfts/index.ts b/src/mappings/nfts/index.ts index 958b9628..a349194f 100644 --- a/src/mappings/nfts/index.ts +++ b/src/mappings/nfts/index.ts @@ -1,8 +1,11 @@ export * from './burn' export * from './buy' +export * from './cancelSwap' export * from './change' export * from './changeTeam' +export * from './claimSwap' export * from './create' +export * from './createSwap' export * from './destroy' export * from './forceCreate' export * from './list' From 37c4470733e169626f4f8f6f34eae3d2625e97e6 Mon Sep 17 00:00:00 2001 From: Viki Val Date: Mon, 1 Jul 2024 15:15:45 +0200 Subject: [PATCH 21/59] :zap: works at this point --- src/mappings/nfts/cancelSwap.ts | 2 +- src/mappings/nfts/claimSwap.ts | 2 +- src/mappings/nfts/createSwap.ts | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/mappings/nfts/cancelSwap.ts b/src/mappings/nfts/cancelSwap.ts index 8f4b08d3..457586fa 100644 --- a/src/mappings/nfts/cancelSwap.ts +++ b/src/mappings/nfts/cancelSwap.ts @@ -11,7 +11,7 @@ const OPERATION = OfferStatus.WITHDRAWN export async function handleCancelSwap(context: Context): Promise { pending(OPERATION, `${context.block.height}`) const event = unwrap(context, getSwapCancelledEvent) - debug(OPERATION, event) + debug(OPERATION, event, true) const id = createTokenId(event.collectionId, event.sn) const entity = await get(context.store, Swap, id) diff --git a/src/mappings/nfts/claimSwap.ts b/src/mappings/nfts/claimSwap.ts index 33c78bfd..fc232361 100644 --- a/src/mappings/nfts/claimSwap.ts +++ b/src/mappings/nfts/claimSwap.ts @@ -11,7 +11,7 @@ const OPERATION = OfferStatus.ACCEPTED export async function handleClaimSwap(context: Context): Promise { pending(OPERATION, `${context.block.height}`) const event = unwrap(context, getSwapClaimedEvent) - debug(OPERATION, event) + debug(OPERATION, event, true) const id = createTokenId(event.collectionId, event.sn) const entity = await get(context.store, Swap, id) diff --git a/src/mappings/nfts/createSwap.ts b/src/mappings/nfts/createSwap.ts index ff1f590a..bd9f696a 100644 --- a/src/mappings/nfts/createSwap.ts +++ b/src/mappings/nfts/createSwap.ts @@ -29,6 +29,7 @@ export async function handleCreateSwap(context: Context): Promise { final.nft = nft final.considered = considered final.desired = desired + final.expiration = deadline final.price = event.price final.surcharge = event.surcharge final.status = final.blockNumber >= deadline ? OfferStatus.EXPIRED : OfferStatus.ACTIVE From 3314e03b34867b28fb06720abb7466c56c87e222 Mon Sep 17 00:00:00 2001 From: Viki Val Date: Tue, 2 Jul 2024 12:45:59 +0200 Subject: [PATCH 22/59] feat: Add name field to BaseCall in extract.ts --- src/mappings/utils/extract.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/mappings/utils/extract.ts b/src/mappings/utils/extract.ts index c11eab24..5dd8f2a1 100644 --- a/src/mappings/utils/extract.ts +++ b/src/mappings/utils/extract.ts @@ -19,8 +19,9 @@ function toBaseEvent(ctx: Context): BaseCall { const caller = addressOf(address) const blockNumber = ctx.block.height.toString() const timestamp = ctx.block.timestamp ? new Date(ctx.block.timestamp) : new Date() + const name = ctx.call?.name - return { caller, blockNumber, timestamp } + return { caller, blockNumber, timestamp, name } } /** From f16543a593b7d2eaa7f49daa2a0b5fb7f3ba638d Mon Sep 17 00:00:00 2001 From: Viki Val Date: Tue, 2 Jul 2024 12:46:29 +0200 Subject: [PATCH 23/59] refactor: Add skip function to logger.ts --- src/mappings/utils/logger.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/mappings/utils/logger.ts b/src/mappings/utils/logger.ts index ab6ca1c3..2c80fa28 100644 --- a/src/mappings/utils/logger.ts +++ b/src/mappings/utils/logger.ts @@ -34,6 +34,15 @@ export const pending = (action: Action, message: string) => { logger.info(`⏳ [${action}] ${message}`) } +/** + * Log a started action + * @param action - the action being performed + * @param message - the message to log +**/ +export const skip = (action: Action, message: string) => { + logger.info(`⏩ [${action}] ${message}`) +} + /** * Log a debug message * @param action - the action being performed From 0b3b9796ec64c958291933202c5ac34c640995d7 Mon Sep 17 00:00:00 2001 From: Viki Val Date: Tue, 2 Jul 2024 12:46:48 +0200 Subject: [PATCH 24/59] feat: Add transfer and buyItem options to NonFungibleCall enum --- src/processable.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/processable.ts b/src/processable.ts index 603aca00..d1f0a936 100644 --- a/src/processable.ts +++ b/src/processable.ts @@ -104,6 +104,8 @@ export enum NewNonFungible { */ export enum NonFungibleCall { updateMintSettings = 'Nfts.update_mint_settings', + transfer = 'Nfts.transfer', + buyItem = 'Nfts.buy_item', } /** From ba7eadd2d6012d9981fc0c2d458ecde624dbda6e Mon Sep 17 00:00:00 2001 From: Viki Val Date: Tue, 2 Jul 2024 12:48:06 +0200 Subject: [PATCH 25/59] refactor: Skip NonFungibleCall.buyItem event in handleTokenTransfer ``` --- src/mappings/nfts/transfer.ts | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/mappings/nfts/transfer.ts b/src/mappings/nfts/transfer.ts index 2590ee31..7d67af79 100644 --- a/src/mappings/nfts/transfer.ts +++ b/src/mappings/nfts/transfer.ts @@ -1,10 +1,11 @@ import { getWith } from '@kodadot1/metasquid/entity' import { NFTEntity as NE } from '../../model' +import { NonFungibleCall } from '../../processable' 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 { debug, pending, skip, success } from '../utils/logger' +import { Action, Context, createTokenId } from '../utils/types' import { getTransferTokenEvent } from './getters' const OPERATION = Action.SEND @@ -20,6 +21,12 @@ export async function handleTokenTransfer(context: Context): Promise { const event = unwrap(context, getTransferTokenEvent) debug(OPERATION, event) + // Check if event has a name and can be skipped in some canses + if (event.name && [NonFungibleCall.buyItem].includes(event.name as NonFungibleCall)) { + skip(OPERATION, `because it is **${event.name}**`) + return + } + const id = createTokenId(event.collectionId, event.sn) const entity = await getWith(context.store, NE, id, { collection: true }) From 012c57b85ffc27cb288525e5530d24542435a5d2 Mon Sep 17 00:00:00 2001 From: Viki Val Date: Tue, 2 Jul 2024 12:48:13 +0200 Subject: [PATCH 26/59] feat: Add name field to BaseCall in types.ts --- src/mappings/utils/types.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/mappings/utils/types.ts b/src/mappings/utils/types.ts index 71b8386b..1e1de473 100644 --- a/src/mappings/utils/types.ts +++ b/src/mappings/utils/types.ts @@ -22,6 +22,7 @@ export type BaseCall = { caller: string blockNumber: string timestamp: Date + name?: string } // In case of fire consult this repo: // https://github.com/subsquid-labs/squid-substrate-template/tree/main From 152b701f88129d2ed4516eaae8aa17c8f0fa2e95 Mon Sep 17 00:00:00 2001 From: Viki Val Date: Tue, 2 Jul 2024 13:03:46 +0200 Subject: [PATCH 27/59] feat: Add Nfts.claim_swap option to NonFungibleCall enum --- src/processable.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/processable.ts b/src/processable.ts index d1f0a936..dc610922 100644 --- a/src/processable.ts +++ b/src/processable.ts @@ -106,6 +106,7 @@ export enum NonFungibleCall { updateMintSettings = 'Nfts.update_mint_settings', transfer = 'Nfts.transfer', buyItem = 'Nfts.buy_item', + claimSwap = 'Nfts.claim_swap', } /** From 0dd36a07ce4f9ff1d64a3c107a449e9f4b31d627 Mon Sep 17 00:00:00 2001 From: Viki Val Date: Tue, 2 Jul 2024 15:07:31 +0200 Subject: [PATCH 28/59] refactor: Update handleTokenTransfer to handle NonFungibleCall.claimSwap event --- src/mappings/nfts/transfer.ts | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/mappings/nfts/transfer.ts b/src/mappings/nfts/transfer.ts index 7d67af79..801431e1 100644 --- a/src/mappings/nfts/transfer.ts +++ b/src/mappings/nfts/transfer.ts @@ -4,11 +4,11 @@ import { NonFungibleCall } from '../../processable' import { createEvent } from '../shared/event' import { unwrap } from '../utils/extract' import { calculateCollectionOwnerCountAndDistribution } from '../utils/helper' -import { debug, pending, skip, success } from '../utils/logger' +import { debug, pending, skip, success, warn } from '../utils/logger' import { Action, Context, createTokenId } from '../utils/types' import { getTransferTokenEvent } from './getters' -const OPERATION = Action.SEND +let OPERATION = Action.SEND /** * Handle the token transfer event (Nfts.Transferred) @@ -22,9 +22,16 @@ export async function handleTokenTransfer(context: Context): Promise { debug(OPERATION, event) // Check if event has a name and can be skipped in some canses - if (event.name && [NonFungibleCall.buyItem].includes(event.name as NonFungibleCall)) { - skip(OPERATION, `because it is **${event.name}**`) - return + switch (event.name) { + case NonFungibleCall.buyItem: + skip(OPERATION, `because it is **${event.name}**`) + return + case NonFungibleCall.claimSwap: + warn(OPERATION, `Will be treated as **${Action.SWAP}**`) + OPERATION = Action.SWAP + break + default: + break } const id = createTokenId(event.collectionId, event.sn) From ca363e60d731e7ba5088ee1c406f23ade7061f73 Mon Sep 17 00:00:00 2001 From: Viki Val Date: Wed, 3 Jul 2024 11:27:55 +0200 Subject: [PATCH 29/59] Update squid.yaml --- squid.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/squid.yaml b/squid.yaml index f0fe1265..1cf014c7 100644 --- a/squid.yaml +++ b/squid.yaml @@ -1,6 +1,6 @@ manifestVersion: subsquid.io/v0.1 name: stick -version: 13 +version: 12 description: 'SubSquid indexer for Uniques and Assets on Statemine' build: deploy: From b5a2aaeefe7ec33f0fa0e02762baf35d5cc7e1f8 Mon Sep 17 00:00:00 2001 From: Viki Val Date: Mon, 15 Jul 2024 16:44:33 +0200 Subject: [PATCH 30/59] feat: Add unique constraint to nft field in Swap entity --- schema.graphql | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/schema.graphql b/schema.graphql index b7165130..84e6dcc9 100644 --- a/schema.graphql +++ b/schema.graphql @@ -79,6 +79,7 @@ type NFTEntity @entity { recipient: String royalty: Float sn: BigInt! @index + # swap: Swap @derivedFrom(field: "nft") updatedAt: DateTime! @index version: Int! token: TokenEntity @@ -208,10 +209,10 @@ type Swap @entity { considered: CollectionEntity! desired: NFTEntity expiration: BigInt! - nft: NFTEntity! + nft: NFTEntity! # @unique price: BigInt - status: OfferStatus! surcharge: Surcharge + status: OfferStatus! updatedAt: DateTime } From 1bd348df56803f12682b660bf44645d614627e09 Mon Sep 17 00:00:00 2001 From: Viki Val Date: Mon, 15 Jul 2024 17:35:59 +0200 Subject: [PATCH 31/59] :bug: forgot GW --- src/processor.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/processor.ts b/src/processor.ts index e1621f26..7e3d3e4b 100644 --- a/src/processor.ts +++ b/src/processor.ts @@ -13,7 +13,7 @@ const database = new Database({ supportHotBlocks: false }) const processor = new SubstrateProcessor() const UNIQUE_STARTING_BLOCK = 323_750 // 618838; -// const _NFT_STARTING_BLOCK = 4_556_552 +const _NFT_STARTING_BLOCK = 4_556_552 const STARTING_BLOCK = UNIQUE_STARTING_BLOCK const ONLY_ARCHIVE = false @@ -27,12 +27,14 @@ processor.setBlockRange({ from: STARTING_BLOCK }) const archive = getArchiveUrl() const chain = getNodeUrl() -processor.setGateway(archive) + processor.setRpcEndpoint({ url: chain, rateLimit: 10 }) +processor.setGateway(archive); + // disables RPC ingestion and drastically reduce no of RPC calls processor.setRpcDataIngestionSettings({ disabled: ONLY_ARCHIVE }) From 1d4a3cbd0ee1c1941c81a75a3ef5844a218255b9 Mon Sep 17 00:00:00 2001 From: Viki Val Date: Sun, 21 Jul 2024 19:37:15 +0200 Subject: [PATCH 32/59] :bug: incorrect value for desired nft --- src/mappings/nfts/createSwap.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mappings/nfts/createSwap.ts b/src/mappings/nfts/createSwap.ts index bd9f696a..7d955888 100644 --- a/src/mappings/nfts/createSwap.ts +++ b/src/mappings/nfts/createSwap.ts @@ -21,7 +21,7 @@ export async function handleCreateSwap(context: Context): Promise { // the nft that is being swapped const nft = await get(context.store, NE, id) const considered = await get(context.store, CE, event.consideration.collectionId) - const desired = isNFT(event.consideration) ? await get(context.store, NE, tokenIdOf(event as any)) : undefined + const desired = isNFT(event.consideration) ? await get(context.store, NE, tokenIdOf(event.consideration as any)) : undefined final.blockNumber = BigInt(event.blockNumber) final.createdAt = event.timestamp From d00d535b88ea99b6816fcca89cf1b38ce1176ede Mon Sep 17 00:00:00 2001 From: Viki Val Date: Mon, 22 Jul 2024 10:40:55 +0200 Subject: [PATCH 33/59] :zap: Remove swap if it exists in transfer --- src/mappings/nfts/transfer.ts | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/mappings/nfts/transfer.ts b/src/mappings/nfts/transfer.ts index 801431e1..9d72e3be 100644 --- a/src/mappings/nfts/transfer.ts +++ b/src/mappings/nfts/transfer.ts @@ -1,5 +1,5 @@ -import { getWith } from '@kodadot1/metasquid/entity' -import { NFTEntity as NE } from '../../model' +import { getOptional, getWith } from '@kodadot1/metasquid/entity' +import { NFTEntity as NE, OfferStatus, Swap } from '../../model' import { NonFungibleCall } from '../../processable' import { createEvent } from '../shared/event' import { unwrap } from '../utils/extract' @@ -55,4 +55,13 @@ export async function handleTokenTransfer(context: Context): Promise { await context.store.save(entity) await context.store.save(entity.collection) await createEvent(entity, OPERATION, event, event.to, context.store, oldOwner) + + // remove swap if exists + // PendingSwapOf::::remove(&collection, &item); + const swap = await getOptional(context.store, Swap, id) + if (swap) { + swap.status = OfferStatus.WITHDRAWN + swap.updatedAt = event.timestamp + await context.store.save(swap) + } } From 6ef50fba13bdb47130bd7a202e137640dc20dbc9 Mon Sep 17 00:00:00 2001 From: Viki Val Date: Mon, 22 Jul 2024 10:42:23 +0200 Subject: [PATCH 34/59] :zap: withdraw swap if it exists in burn --- src/mappings/nfts/burn.ts | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/mappings/nfts/burn.ts b/src/mappings/nfts/burn.ts index 4bf93dcf..d9741f6e 100644 --- a/src/mappings/nfts/burn.ts +++ b/src/mappings/nfts/burn.ts @@ -1,5 +1,5 @@ -import { getWith } from '@kodadot1/metasquid/entity' -import { NFTEntity as NE } from '../../model' +import { getOptional, getWith } from '@kodadot1/metasquid/entity' +import { NFTEntity as NE, OfferStatus, Swap } from '../../model' import { unwrap } from '../utils/extract' import { debug, pending, success } from '../utils/logger' import { Action, Context, createTokenId } from '../utils/types' @@ -47,4 +47,11 @@ export async function handleTokenBurn(context: Context): Promise { await context.store.save(entity.collection) const meta = entity.metadata ?? '' await createEvent(entity, OPERATION, event, meta, context.store) + + const swap = await getOptional(context.store, Swap, id) + if (swap) { + swap.status = OfferStatus.WITHDRAWN + swap.updatedAt = event.timestamp + await context.store.save(swap) + } } From daef9293f6e5ccdc7a5d03e07b39388bb290003d Mon Sep 17 00:00:00 2001 From: Viki Val Date: Mon, 22 Jul 2024 10:59:09 +0200 Subject: [PATCH 35/59] :bug: incorrectly think something is a swap --- src/mappings/nfts/transfer.ts | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/mappings/nfts/transfer.ts b/src/mappings/nfts/transfer.ts index 9d72e3be..c9b7dd3d 100644 --- a/src/mappings/nfts/transfer.ts +++ b/src/mappings/nfts/transfer.ts @@ -8,7 +8,7 @@ import { debug, pending, skip, success, warn } from '../utils/logger' import { Action, Context, createTokenId } from '../utils/types' import { getTransferTokenEvent } from './getters' -let OPERATION = Action.SEND +const OPERATION = Action.SEND /** * Handle the token transfer event (Nfts.Transferred) @@ -17,6 +17,9 @@ let OPERATION = Action.SEND * @param context - the context for the event **/ export async function handleTokenTransfer(context: Context): Promise { + // Handling swaps and other operations + let TRUE_OPERATION = OPERATION; + pending(OPERATION, `${context.block.height}`) const event = unwrap(context, getTransferTokenEvent) debug(OPERATION, event) @@ -28,7 +31,7 @@ export async function handleTokenTransfer(context: Context): Promise { return case NonFungibleCall.claimSwap: warn(OPERATION, `Will be treated as **${Action.SWAP}**`) - OPERATION = Action.SWAP + TRUE_OPERATION = Action.SWAP break default: break @@ -51,10 +54,10 @@ export async function handleTokenTransfer(context: Context): Promise { entity.collection.ownerCount = ownerCount entity.collection.distribution = distribution - success(OPERATION, `${id} from ${event.caller} to ${event.to}`) + success(TRUE_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) + await createEvent(entity, TRUE_OPERATION, event, event.to, context.store, oldOwner) // remove swap if exists // PendingSwapOf::::remove(&collection, &item); From 8e77756277942de90727d724bd3c7b9e3965dc31 Mon Sep 17 00:00:00 2001 From: Viki Val Date: Mon, 22 Jul 2024 13:23:43 +0200 Subject: [PATCH 36/59] Offer Collections - https://github.com/kodadot/stick/discussions/295#discussioncomment-10113851 --- speck.yaml | 1 + squid.yaml | 1 + 2 files changed, 2 insertions(+) diff --git a/speck.yaml b/speck.yaml index 5b419957..218e3d79 100644 --- a/speck.yaml +++ b/speck.yaml @@ -15,6 +15,7 @@ deploy: - lib/processor env: CHAIN: polkadot + OFFER: 174 api: cmd: - npx diff --git a/squid.yaml b/squid.yaml index f0fe1265..2fa03e34 100644 --- a/squid.yaml +++ b/squid.yaml @@ -15,6 +15,7 @@ deploy: - lib/processor env: CHAIN: kusama + OFFER: 464 api: cmd: - npx From 74deb395ef9ea2df561c5229912c76de722b643f Mon Sep 17 00:00:00 2001 From: Viki Val Date: Mon, 22 Jul 2024 13:29:38 +0200 Subject: [PATCH 37/59] :zap: isOffer --- src/environment.ts | 2 ++ src/mappings/utils/types.ts | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/src/environment.ts b/src/environment.ts index df912be8..bb710b99 100644 --- a/src/environment.ts +++ b/src/environment.ts @@ -1,6 +1,7 @@ export type Chain = 'kusama' | 'rococo' | 'polkadot' export const CHAIN: Chain = process.env.CHAIN as Chain || 'kusama' +export const COLLECTION_OFFER: string = process.env.OFFER || '' const UNIQUE_STARTING_BLOCK = 323_750 // 618838; // const _NFT_STARTING_BLOCK = 4_556_552 @@ -14,6 +15,7 @@ export const isProd = CHAIN !== 'rococo' console.table({ CHAIN, ARCHIVE_URL, NODE_URL, STARTING_BLOCK, + COLLECTION_OFFER, disabledRPC: false, environment: isProd ? 'production' : 'development', }) diff --git a/src/mappings/utils/types.ts b/src/mappings/utils/types.ts index 1e1de473..316ead92 100644 --- a/src/mappings/utils/types.ts +++ b/src/mappings/utils/types.ts @@ -17,6 +17,7 @@ import { Attribute } from '../../model/generated/_attribute' import { Interaction } from '../../model' import { SetMetadata } from '../nfts/types' +import { COLLECTION_OFFER } from '../../environment' export type BaseCall = { caller: string @@ -70,6 +71,10 @@ export function isNFT(event: T) { return event.sn !== undefined } +export function isOffer(event: T): boolean { + return event.collectionId === COLLECTION_OFFER +} + export function eventFrom( interaction: T, { blockNumber, caller, timestamp }: BaseCall, From 630315f41aee52238a056cdbb9d35f0933988a7f Mon Sep 17 00:00:00 2001 From: Viki Val Date: Mon, 22 Jul 2024 14:54:03 +0200 Subject: [PATCH 38/59] :test_tube: misc test for offer --- tests/misc.test.ts | 53 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 tests/misc.test.ts diff --git a/tests/misc.test.ts b/tests/misc.test.ts new file mode 100644 index 00000000..f1f18e0f --- /dev/null +++ b/tests/misc.test.ts @@ -0,0 +1,53 @@ +import { Optional } from "@kodadot1/metasquid/types" +import { describe, expect, it } from 'vitest' + +export type SwapData = { + price: Optional, + surcharge: Optional, + deadline: number, +} + +export enum Surcharge { + Receive = "Receive", + Send = "Send", +} + +describe('Misc', () => { + function isOffer(event: SwapData): boolean { + return Boolean(event.price && event.price > 0n && event.surcharge === 'Send') + } + // let store: SquidStore; + + + describe('isOffer', () => { + it('should be valid offer', () => { + const swapdata: SwapData = { + price: BigInt(1e10), + surcharge: Surcharge.Send, + deadline: 0 + } + const value = isOffer(swapdata) + expect(value).toBe(true) + }) + + it('should be invalid offer (bad surchage)', () => { + const swapdata: SwapData = { + price: BigInt(1e10), + surcharge: Surcharge.Receive, + deadline: 0 + } + const value = isOffer(swapdata) + expect(value).toBe(false) + }) + + it('should be invalid offer (bad surchage)', () => { + const swapdata: SwapData = { + price: undefined, + surcharge: Surcharge.Send, + deadline: 0 + } + const value = isOffer(swapdata) + expect(value).toBe(false) + }) + }) +}) From 067f76d9338062565a6481247cef3a8208a2aad4 Mon Sep 17 00:00:00 2001 From: Viki Val Date: Mon, 22 Jul 2024 15:02:21 +0200 Subject: [PATCH 39/59] :squid: magick generation --- .../{_offerStatus.ts => _tradeStatus.ts} | 6 ++- src/model/generated/consideration.model.ts | 21 -------- src/model/generated/index.ts | 4 +- src/model/generated/offer.model.ts | 48 +++++++++++++++++++ src/model/generated/swap.model.ts | 17 +++---- 5 files changed, 63 insertions(+), 33 deletions(-) rename src/model/generated/{_offerStatus.ts => _tradeStatus.ts} (56%) delete mode 100644 src/model/generated/consideration.model.ts create mode 100644 src/model/generated/offer.model.ts diff --git a/src/model/generated/_offerStatus.ts b/src/model/generated/_tradeStatus.ts similarity index 56% rename from src/model/generated/_offerStatus.ts rename to src/model/generated/_tradeStatus.ts index 441ccde0..27c13b7a 100644 --- a/src/model/generated/_offerStatus.ts +++ b/src/model/generated/_tradeStatus.ts @@ -1,6 +1,8 @@ -export enum OfferStatus { - ACTIVE = "ACTIVE", +export enum TradeStatus { ACCEPTED = "ACCEPTED", + ACTIVE = "ACTIVE", + CANCELLED = "CANCELLED", EXPIRED = "EXPIRED", + INVALID = "INVALID", WITHDRAWN = "WITHDRAWN", } diff --git a/src/model/generated/consideration.model.ts b/src/model/generated/consideration.model.ts deleted file mode 100644 index 34792570..00000000 --- a/src/model/generated/consideration.model.ts +++ /dev/null @@ -1,21 +0,0 @@ -import {Entity as Entity_, Column as Column_, PrimaryColumn as PrimaryColumn_, ManyToOne as ManyToOne_, Index as Index_} from "@subsquid/typeorm-store" -import {CollectionEntity} from "./collectionEntity.model" -import {NFTEntity} from "./nftEntity.model" - -@Entity_() -export class Consideration { - constructor(props?: Partial) { - Object.assign(this, props) - } - - @PrimaryColumn_() - id!: string - - @Index_() - @ManyToOne_(() => CollectionEntity, {nullable: true}) - collection!: CollectionEntity - - @Index_() - @ManyToOne_(() => NFTEntity, {nullable: true}) - nft!: NFTEntity | undefined | null -} diff --git a/src/model/generated/index.ts b/src/model/generated/index.ts index 4d5d2030..2d4d4954 100644 --- a/src/model/generated/index.ts +++ b/src/model/generated/index.ts @@ -8,9 +8,9 @@ export * from "./metadataEntity.model" export * from "./event.model" export * from "./_interaction" export * from "./collectionEvent.model" -export * from "./consideration.model" +export * from "./offer.model" +export * from "./_tradeStatus" export * from "./swap.model" -export * from "./_offerStatus" export * from "./_surcharge" export * from "./assetEntity.model" export * from "./cacheStatus.model" diff --git a/src/model/generated/offer.model.ts b/src/model/generated/offer.model.ts new file mode 100644 index 00000000..cf2921c9 --- /dev/null +++ b/src/model/generated/offer.model.ts @@ -0,0 +1,48 @@ +import {Entity as Entity_, Column as Column_, PrimaryColumn as PrimaryColumn_, BigIntColumn as BigIntColumn_, StringColumn as StringColumn_, ManyToOne as ManyToOne_, Index as Index_, DateTimeColumn as DateTimeColumn_, OneToOne as OneToOne_, JoinColumn as JoinColumn_} from "@subsquid/typeorm-store" +import {CollectionEntity} from "./collectionEntity.model" +import {NFTEntity} from "./nftEntity.model" +import {TradeStatus} from "./_tradeStatus" + +@Entity_() +export class Offer { + constructor(props?: Partial) { + Object.assign(this, props) + } + + @PrimaryColumn_() + id!: string + + @BigIntColumn_({nullable: false}) + blockNumber!: bigint + + @StringColumn_({nullable: false}) + caller!: string + + @Index_() + @ManyToOne_(() => CollectionEntity, {nullable: true}) + considered!: CollectionEntity + + @DateTimeColumn_({nullable: false}) + createdAt!: Date + + @Index_() + @ManyToOne_(() => NFTEntity, {nullable: true}) + desired!: NFTEntity | undefined | null + + @BigIntColumn_({nullable: false}) + expiration!: bigint + + @Index_({unique: true}) + @OneToOne_(() => NFTEntity, {nullable: true}) + @JoinColumn_() + nft!: NFTEntity + + @BigIntColumn_({nullable: false}) + price!: bigint + + @Column_("varchar", {length: 9, nullable: false}) + status!: TradeStatus + + @DateTimeColumn_({nullable: true}) + updatedAt!: Date | undefined | null +} diff --git a/src/model/generated/swap.model.ts b/src/model/generated/swap.model.ts index a893f294..94af8ff4 100644 --- a/src/model/generated/swap.model.ts +++ b/src/model/generated/swap.model.ts @@ -1,7 +1,7 @@ -import {Entity as Entity_, Column as Column_, PrimaryColumn as PrimaryColumn_, BigIntColumn as BigIntColumn_, StringColumn as StringColumn_, DateTimeColumn as DateTimeColumn_, ManyToOne as ManyToOne_, Index as Index_} from "@subsquid/typeorm-store" +import {Entity as Entity_, Column as Column_, PrimaryColumn as PrimaryColumn_, BigIntColumn as BigIntColumn_, StringColumn as StringColumn_, ManyToOne as ManyToOne_, Index as Index_, DateTimeColumn as DateTimeColumn_, OneToOne as OneToOne_, JoinColumn as JoinColumn_} from "@subsquid/typeorm-store" import {CollectionEntity} from "./collectionEntity.model" import {NFTEntity} from "./nftEntity.model" -import {OfferStatus} from "./_offerStatus" +import {TradeStatus} from "./_tradeStatus" import {Surcharge} from "./_surcharge" @Entity_() @@ -19,13 +19,13 @@ export class Swap { @StringColumn_({nullable: false}) caller!: string - @DateTimeColumn_({nullable: false}) - createdAt!: Date - @Index_() @ManyToOne_(() => CollectionEntity, {nullable: true}) considered!: CollectionEntity + @DateTimeColumn_({nullable: false}) + createdAt!: Date + @Index_() @ManyToOne_(() => NFTEntity, {nullable: true}) desired!: NFTEntity | undefined | null @@ -33,15 +33,16 @@ export class Swap { @BigIntColumn_({nullable: false}) expiration!: bigint - @Index_() - @ManyToOne_(() => NFTEntity, {nullable: true}) + @Index_({unique: true}) + @OneToOne_(() => NFTEntity, {nullable: true}) + @JoinColumn_() nft!: NFTEntity @BigIntColumn_({nullable: true}) price!: bigint | undefined | null @Column_("varchar", {length: 9, nullable: false}) - status!: OfferStatus + status!: TradeStatus @Column_("varchar", {length: 7, nullable: true}) surcharge!: Surcharge | undefined | null From f2825ef930cb9f1c122bf286a3df2b4c1cd95ea9 Mon Sep 17 00:00:00 2001 From: Viki Val Date: Mon, 22 Jul 2024 15:03:58 +0200 Subject: [PATCH 40/59] :truck: Offerstatus -> TradeStatus --- src/mappings/nfts/burn.ts | 4 ++-- src/mappings/nfts/cancelSwap.ts | 6 +++--- src/mappings/nfts/claimSwap.ts | 6 +++--- src/mappings/nfts/transfer.ts | 4 ++-- src/mappings/utils/logger.ts | 4 ++-- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/mappings/nfts/burn.ts b/src/mappings/nfts/burn.ts index d9741f6e..8a7fcb57 100644 --- a/src/mappings/nfts/burn.ts +++ b/src/mappings/nfts/burn.ts @@ -1,5 +1,5 @@ import { getOptional, getWith } from '@kodadot1/metasquid/entity' -import { NFTEntity as NE, OfferStatus, Swap } from '../../model' +import { NFTEntity as NE, TradeStatus, Swap } from '../../model' import { unwrap } from '../utils/extract' import { debug, pending, success } from '../utils/logger' import { Action, Context, createTokenId } from '../utils/types' @@ -50,7 +50,7 @@ export async function handleTokenBurn(context: Context): Promise { const swap = await getOptional(context.store, Swap, id) if (swap) { - swap.status = OfferStatus.WITHDRAWN + swap.status = TradeStatus.WITHDRAWN swap.updatedAt = event.timestamp await context.store.save(swap) } diff --git a/src/mappings/nfts/cancelSwap.ts b/src/mappings/nfts/cancelSwap.ts index 457586fa..84d96105 100644 --- a/src/mappings/nfts/cancelSwap.ts +++ b/src/mappings/nfts/cancelSwap.ts @@ -1,12 +1,12 @@ import { getOrFail as get } from '@kodadot1/metasquid/entity' -import { OfferStatus, Swap } from '../../model' +import { TradeStatus, Swap } 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 { getSwapCancelledEvent } from './getters' -const OPERATION = OfferStatus.WITHDRAWN +const OPERATION = TradeStatus.WITHDRAWN export async function handleCancelSwap(context: Context): Promise { pending(OPERATION, `${context.block.height}`) @@ -16,7 +16,7 @@ export async function handleCancelSwap(context: Context): Promise { const id = createTokenId(event.collectionId, event.sn) const entity = await get(context.store, Swap, id) - entity.status = OfferStatus.WITHDRAWN + entity.status = TradeStatus.WITHDRAWN entity.updatedAt = event.timestamp success(OPERATION, `${id} by ${event.caller}`) diff --git a/src/mappings/nfts/claimSwap.ts b/src/mappings/nfts/claimSwap.ts index fc232361..f204d445 100644 --- a/src/mappings/nfts/claimSwap.ts +++ b/src/mappings/nfts/claimSwap.ts @@ -1,12 +1,12 @@ import { getOrFail as get } from '@kodadot1/metasquid/entity' -import { NFTEntity as NE, OfferStatus, Swap } from '../../model' +import { NFTEntity as NE, TradeStatus, Swap } 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 { getSwapClaimedEvent } from './getters' -const OPERATION = OfferStatus.ACCEPTED +const OPERATION = TradeStatus.ACCEPTED export async function handleClaimSwap(context: Context): Promise { pending(OPERATION, `${context.block.height}`) @@ -16,7 +16,7 @@ export async function handleClaimSwap(context: Context): Promise { const id = createTokenId(event.collectionId, event.sn) const entity = await get(context.store, Swap, id) - entity.status = OfferStatus.ACCEPTED + entity.status = TradeStatus.ACCEPTED entity.updatedAt = event.timestamp success(OPERATION, `${id} by ${event.caller}`) diff --git a/src/mappings/nfts/transfer.ts b/src/mappings/nfts/transfer.ts index c9b7dd3d..bf4ce128 100644 --- a/src/mappings/nfts/transfer.ts +++ b/src/mappings/nfts/transfer.ts @@ -1,5 +1,5 @@ import { getOptional, getWith } from '@kodadot1/metasquid/entity' -import { NFTEntity as NE, OfferStatus, Swap } from '../../model' +import { NFTEntity as NE, TradeStatus, Swap } from '../../model' import { NonFungibleCall } from '../../processable' import { createEvent } from '../shared/event' import { unwrap } from '../utils/extract' @@ -63,7 +63,7 @@ export async function handleTokenTransfer(context: Context): Promise { // PendingSwapOf::::remove(&collection, &item); const swap = await getOptional(context.store, Swap, id) if (swap) { - swap.status = OfferStatus.WITHDRAWN + swap.status = TradeStatus.WITHDRAWN swap.updatedAt = event.timestamp await context.store.save(swap) } diff --git a/src/mappings/utils/logger.ts b/src/mappings/utils/logger.ts index 2c80fa28..9b84b2c8 100644 --- a/src/mappings/utils/logger.ts +++ b/src/mappings/utils/logger.ts @@ -1,8 +1,8 @@ import { serializer } from '@kodadot1/metasquid' import { logger } from '@kodadot1/metasquid/logger' -import { Interaction, OfferStatus } from '../../model' +import { Interaction, TradeStatus } from '../../model' -type Action = Interaction | OfferStatus +type Action = Interaction | TradeStatus type ErrorCallback = (error: Error) => void From 064566770953f124c782c39c968bb4353e8c71e4 Mon Sep 17 00:00:00 2001 From: Viki Val Date: Mon, 22 Jul 2024 15:04:16 +0200 Subject: [PATCH 41/59] :alien: Offers --- schema.graphql | 80 +++++++++++++++++++++++--------------------------- 1 file changed, 37 insertions(+), 43 deletions(-) diff --git a/schema.graphql b/schema.graphql index 84e6dcc9..3450d4f7 100644 --- a/schema.graphql +++ b/schema.graphql @@ -1,5 +1,5 @@ # Entity to represent a collection -# defined on chain as pub type Collection, I: 'static = ()> +# defined on chain as pub type Collection, I: 'static = ()> # https://github.com/paritytech/polkadot-sdk/blob/b8ad0d1f565659f004165c5244acba78828d0bf7/substrate/frame/nfts/src/lib.rs#L217 type CollectionEntity @entity { attributes: [Attribute!] @@ -35,7 +35,7 @@ type CollectionEntity @entity { } # Entity to group NFTEntity by common metadata -# grouping is done either by NFTEntity.image or NFTEntity.media +# grouping is done either by NFTEntity.image or NFTEntity.media # https://github.com/paritytech/polkadot-sdk/blob/b8ad0d1f565659f004165c5244acba78828d0bf7/substrate/frame/nfts/src/lib.rs#L293 type TokenEntity @entity { id: ID! @@ -156,63 +156,55 @@ type CollectionEvent implements EventType @entity { # version: Int! } -# type OfferEvent implements EventType @entity { +# type TradeEvent implements EventType @entity { # id: ID! # blockNumber: BigInt # caller: String! # currentOwner: String # currentOwner # interaction: OfferInteraction! # meta: String! -# offer: Offer! +# trade: Swap! # timestamp: DateTime! # } +type Offer @entity { + id: ID! # collection-id // same as NFTEntity.id + # events: [TradeEvent!] @derivedFrom(field: "offer") + blockNumber: BigInt! + caller: String! + considered: CollectionEntity! + createdAt: DateTime! + desired: NFTEntity + expiration: BigInt! + nft: NFTEntity! @unique + price: BigInt! + status: TradeStatus! + updatedAt: DateTime +} -# interface OfferType { -# id: ID! # should be addr-id-amount? -# blockNumber: BigInt! -# nft: NFTEntity! -# consideration: Consideration -# caller: String! -# createdAt: DateTime! -# expiration: BigInt! -# status: OfferStatus! -# updatedAt: DateTime -# } - -# type Offer implements OfferType @entity { -# id: ID! # should be addr-id-amount? -# blockNumber: BigInt! -# caller: String! -# createdAt: DateTime! -# events: [OfferEvent!] @derivedFrom(field: "offer") -# expiration: BigInt! -# nft: NFTEntity! -# price: BigInt! -# status: OfferStatus! -# updatedAt: DateTime +# DEV: Consideration is not used +# type Consideration @entity { +# id: ID! +# collection: CollectionEntity! +# nft: NFTEntity # } -type Consideration @entity { - id: ID! - collection: CollectionEntity! - nft: NFTEntity -} - +# Entity to represent a Swap +# defined on chain as pub type PendingSwapOf, I: 'static = ()> +# https://github.com/paritytech/polkadot-sdk/blob/d0d8e29197a783f3ea300569afc50244a280cafa/substrate/frame/nfts/src/types.rs#L207 type Swap @entity { - id: ID! # should be addr-id-amount? + id: ID! # collection-id // same as NFTEntity.id + # events: [TradeEvent!] @derivedFrom(field: "offer") blockNumber: BigInt! caller: String! + considered: CollectionEntity! createdAt: DateTime! - # events: [OfferEvent!] @derivedFrom(field: "offer") - # consideration: Consideration! - considered: CollectionEntity! desired: NFTEntity expiration: BigInt! - nft: NFTEntity! # @unique + nft: NFTEntity! @unique price: BigInt + status: TradeStatus! surcharge: Surcharge - status: OfferStatus! updatedAt: DateTime } @@ -249,16 +241,18 @@ enum Surcharge { Send } -enum OfferInteraction { +enum TradeInteraction { CREATE ACCEPT CANCEL } -enum OfferStatus { - ACTIVE +enum TradeStatus { ACCEPTED + ACTIVE + CANCELLED EXPIRED + INVALID WITHDRAWN } @@ -276,4 +270,4 @@ type AssetEntity @entity { type CacheStatus @entity { id: ID! lastBlockTimestamp: DateTime! -} \ No newline at end of file +} From b0cddf4b834993a625c9d2dafccd1aeeee321559 Mon Sep 17 00:00:00 2001 From: Viki Val Date: Mon, 22 Jul 2024 15:10:22 +0200 Subject: [PATCH 42/59] refactor: Update createSwap.ts to handle offers and trade status --- src/mappings/nfts/createSwap.ts | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/src/mappings/nfts/createSwap.ts b/src/mappings/nfts/createSwap.ts index 7d955888..bad3abc5 100644 --- a/src/mappings/nfts/createSwap.ts +++ b/src/mappings/nfts/createSwap.ts @@ -1,22 +1,32 @@ import { getOrFail as get, getOrCreate } from '@kodadot1/metasquid/entity' -import { CollectionEntity as CE, NFTEntity as NE, OfferStatus, Swap } from '../../model' +import { CollectionEntity as CE, NFTEntity as NE, TradeStatus, Swap, Offer } from '../../model' import { createEvent } from '../shared/event' import { unwrap } from '../utils/extract' -import { debug, pending, success } from '../utils/logger' -import { Action, Context, createTokenId, isNFT } from '../utils/types' +import { debug, pending, success, warn } from '../utils/logger' +import { Action, Context, createTokenId, isNFT, isOffer } from '../utils/types' import { getSwapCreatedEvent } from './getters' import { tokenIdOf } from './types' const OPERATION = Action.SWAP export async function handleCreateSwap(context: Context): Promise { + let TRUE_OPERATION = OPERATION; + pending(OPERATION, `${context.block.height}`) const event = unwrap(context, getSwapCreatedEvent) debug(OPERATION, event, true) + let offer = false + if (isOffer(event)) { + // Validate offer + offer = Boolean(event.price && event.price > 0n && event.surcharge === 'Send') + warn(OPERATION, `Will be treated as **${Action.OFFER}**`) + TRUE_OPERATION = Action.OFFER + } + const Entity = offer ? Offer : Swap // TODO: SWAP CAN BE OVERWRITTEN! const id = createTokenId(event.collectionId, event.sn) - const final = await getOrCreate(context.store, Swap, id, {}) + const final = offer ? await getOrCreate(context.store, Offer, id, {}) : await getOrCreate(context.store, Swap, id, {}) const deadline = BigInt(event.deadline) // the nft that is being swapped const nft = await get(context.store, NE, id) @@ -31,14 +41,15 @@ export async function handleCreateSwap(context: Context): Promise { final.desired = desired final.expiration = deadline final.price = event.price - final.surcharge = event.surcharge - final.status = final.blockNumber >= deadline ? OfferStatus.EXPIRED : OfferStatus.ACTIVE + if ('surcharge' in final) { + final.surcharge = event.surcharge + } + final.status = final.blockNumber >= deadline ? TradeStatus.EXPIRED : TradeStatus.ACTIVE final.updatedAt = event.timestamp // TODO: SAVE SOMEWHERE - - // success(OPERATION, `${id} by ${event.caller} for ${String(event.price)}`) await context.store.save(final) + success(OPERATION, `${id} by ${event.caller} for ${String(event.price)}`) // SwapCreated { // offered_collection: T::CollectionId, From 0a4c292f2a49470db38f06b0b13e1959e428ac34 Mon Sep 17 00:00:00 2001 From: Viki Val Date: Mon, 22 Jul 2024 15:21:04 +0200 Subject: [PATCH 43/59] :loud_sound: debug log --- src/mappings/nfts/createSwap.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mappings/nfts/createSwap.ts b/src/mappings/nfts/createSwap.ts index bad3abc5..eae418e8 100644 --- a/src/mappings/nfts/createSwap.ts +++ b/src/mappings/nfts/createSwap.ts @@ -49,7 +49,7 @@ export async function handleCreateSwap(context: Context): Promise { // TODO: SAVE SOMEWHERE await context.store.save(final) - success(OPERATION, `${id} by ${event.caller} for ${String(event.price)}`) + success(OPERATION, `${id} by ${event.caller} by ${event.caller}`) // SwapCreated { // offered_collection: T::CollectionId, From 678d30c7a16b41fa31296baa70ecaf8f1d9d442e Mon Sep 17 00:00:00 2001 From: Viki Val Date: Mon, 22 Jul 2024 15:35:42 +0200 Subject: [PATCH 44/59] :bug: mark swap as cancelled when removed by chain --- src/mappings/nfts/burn.ts | 4 ++-- src/mappings/nfts/transfer.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/mappings/nfts/burn.ts b/src/mappings/nfts/burn.ts index 8a7fcb57..559763ad 100644 --- a/src/mappings/nfts/burn.ts +++ b/src/mappings/nfts/burn.ts @@ -49,8 +49,8 @@ export async function handleTokenBurn(context: Context): Promise { await createEvent(entity, OPERATION, event, meta, context.store) const swap = await getOptional(context.store, Swap, id) - if (swap) { - swap.status = TradeStatus.WITHDRAWN + if (swap && swap.status === TradeStatus.ACTIVE) { + swap.status = TradeStatus.CANCELLED swap.updatedAt = event.timestamp await context.store.save(swap) } diff --git a/src/mappings/nfts/transfer.ts b/src/mappings/nfts/transfer.ts index bf4ce128..d0dfe51b 100644 --- a/src/mappings/nfts/transfer.ts +++ b/src/mappings/nfts/transfer.ts @@ -62,8 +62,8 @@ export async function handleTokenTransfer(context: Context): Promise { // remove swap if exists // PendingSwapOf::::remove(&collection, &item); const swap = await getOptional(context.store, Swap, id) - if (swap) { - swap.status = TradeStatus.WITHDRAWN + if (swap && swap.status === TradeStatus.ACTIVE) { + swap.status = TradeStatus.CANCELLED swap.updatedAt = event.timestamp await context.store.save(swap) } From 2314760e5282a6e6f3af2e2d0a08adba09994b5e Mon Sep 17 00:00:00 2001 From: Viki Val Date: Mon, 22 Jul 2024 15:36:00 +0200 Subject: [PATCH 45/59] :memo: docs for ops --- src/mappings/nfts/cancelSwap.ts | 6 ++++++ src/mappings/nfts/claimSwap.ts | 11 ++++++++--- src/mappings/nfts/createSwap.ts | 13 +++++++++---- 3 files changed, 23 insertions(+), 7 deletions(-) diff --git a/src/mappings/nfts/cancelSwap.ts b/src/mappings/nfts/cancelSwap.ts index 84d96105..a633b949 100644 --- a/src/mappings/nfts/cancelSwap.ts +++ b/src/mappings/nfts/cancelSwap.ts @@ -8,6 +8,12 @@ import { getSwapCancelledEvent } from './getters' const OPERATION = TradeStatus.WITHDRAWN +/** + * Handle the atomic swap cancel event (Nfts.SwapCancelled) + * Marks the swap as withdrawn + * Logs Nothing + * @param context - the context for the event + **/ export async function handleCancelSwap(context: Context): Promise { pending(OPERATION, `${context.block.height}`) const event = unwrap(context, getSwapCancelledEvent) diff --git a/src/mappings/nfts/claimSwap.ts b/src/mappings/nfts/claimSwap.ts index f204d445..c3095a18 100644 --- a/src/mappings/nfts/claimSwap.ts +++ b/src/mappings/nfts/claimSwap.ts @@ -1,13 +1,18 @@ import { getOrFail as get } from '@kodadot1/metasquid/entity' -import { NFTEntity as NE, TradeStatus, Swap } from '../../model' -import { createEvent } from '../shared/event' +import { Swap, TradeStatus } from '../../model' import { unwrap } from '../utils/extract' import { debug, pending, success } from '../utils/logger' -import { Action, Context, createTokenId } from '../utils/types' +import { Context, createTokenId } from '../utils/types' import { getSwapClaimedEvent } from './getters' const OPERATION = TradeStatus.ACCEPTED +/** + * Handle the atomic swap claim event (Nfts.SwapClaimed) + * Marks the swap as accepted + * Logs Nothing + * @param context - the context for the event +**/ export async function handleClaimSwap(context: Context): Promise { pending(OPERATION, `${context.block.height}`) const event = unwrap(context, getSwapClaimedEvent) diff --git a/src/mappings/nfts/createSwap.ts b/src/mappings/nfts/createSwap.ts index eae418e8..cd556b57 100644 --- a/src/mappings/nfts/createSwap.ts +++ b/src/mappings/nfts/createSwap.ts @@ -1,6 +1,5 @@ import { getOrFail as get, getOrCreate } from '@kodadot1/metasquid/entity' -import { CollectionEntity as CE, NFTEntity as NE, TradeStatus, Swap, Offer } from '../../model' -import { createEvent } from '../shared/event' +import { CollectionEntity as CE, NFTEntity as NE, Offer, Swap, TradeStatus } from '../../model' import { unwrap } from '../utils/extract' import { debug, pending, success, warn } from '../utils/logger' import { Action, Context, createTokenId, isNFT, isOffer } from '../utils/types' @@ -9,8 +8,14 @@ import { tokenIdOf } from './types' const OPERATION = Action.SWAP +/** + * Handle the atomic swap create event (Nfts.SwapCreated) + * Marks the swap as active + * Logs Action.SWAP event + * @param context - the context for the event + **/ export async function handleCreateSwap(context: Context): Promise { - let TRUE_OPERATION = OPERATION; + // let TRUE_OPERATION = OPERATION; pending(OPERATION, `${context.block.height}`) const event = unwrap(context, getSwapCreatedEvent) @@ -21,7 +26,7 @@ export async function handleCreateSwap(context: Context): Promise { // Validate offer offer = Boolean(event.price && event.price > 0n && event.surcharge === 'Send') warn(OPERATION, `Will be treated as **${Action.OFFER}**`) - TRUE_OPERATION = Action.OFFER + // TRUE_OPERATION = Action.OFFER } const Entity = offer ? Offer : Swap // TODO: SWAP CAN BE OVERWRITTEN! From 017cbb7da297e5dbd4a81b0959bef437000b0a10 Mon Sep 17 00:00:00 2001 From: Viki Val Date: Mon, 22 Jul 2024 15:40:55 +0200 Subject: [PATCH 46/59] :bug: not proper offer handling --- src/mappings/nfts/cancelSwap.ts | 8 ++++---- src/mappings/nfts/claimSwap.ts | 7 ++++--- src/mappings/nfts/createSwap.ts | 1 - 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/mappings/nfts/cancelSwap.ts b/src/mappings/nfts/cancelSwap.ts index a633b949..fd126e23 100644 --- a/src/mappings/nfts/cancelSwap.ts +++ b/src/mappings/nfts/cancelSwap.ts @@ -1,9 +1,8 @@ import { getOrFail as get } from '@kodadot1/metasquid/entity' -import { TradeStatus, Swap } from '../../model' -import { createEvent } from '../shared/event' +import { Offer, Swap, TradeStatus } from '../../model' import { unwrap } from '../utils/extract' import { debug, pending, success } from '../utils/logger' -import { Action, Context, createTokenId } from '../utils/types' +import { Context, createTokenId, isOffer } from '../utils/types' import { getSwapCancelledEvent } from './getters' const OPERATION = TradeStatus.WITHDRAWN @@ -20,7 +19,8 @@ export async function handleCancelSwap(context: Context): Promise { debug(OPERATION, event, true) const id = createTokenId(event.collectionId, event.sn) - const entity = await get(context.store, Swap, id) + const offer = isOffer(event) + const entity = offer ? await get(context.store, Offer, id) : await get(context.store, Swap, id) entity.status = TradeStatus.WITHDRAWN entity.updatedAt = event.timestamp diff --git a/src/mappings/nfts/claimSwap.ts b/src/mappings/nfts/claimSwap.ts index c3095a18..42b6bb02 100644 --- a/src/mappings/nfts/claimSwap.ts +++ b/src/mappings/nfts/claimSwap.ts @@ -1,8 +1,8 @@ import { getOrFail as get } from '@kodadot1/metasquid/entity' -import { Swap, TradeStatus } from '../../model' +import { Offer, Swap, TradeStatus } from '../../model' import { unwrap } from '../utils/extract' import { debug, pending, success } from '../utils/logger' -import { Context, createTokenId } from '../utils/types' +import { Context, createTokenId, isOffer } from '../utils/types' import { getSwapClaimedEvent } from './getters' const OPERATION = TradeStatus.ACCEPTED @@ -19,7 +19,8 @@ export async function handleClaimSwap(context: Context): Promise { debug(OPERATION, event, true) const id = createTokenId(event.collectionId, event.sn) - const entity = await get(context.store, Swap, id) + const offer = isOffer(event) + const entity = offer ? await get(context.store, Offer, id) : await get(context.store, Swap, id) entity.status = TradeStatus.ACCEPTED entity.updatedAt = event.timestamp diff --git a/src/mappings/nfts/createSwap.ts b/src/mappings/nfts/createSwap.ts index cd556b57..070a91fd 100644 --- a/src/mappings/nfts/createSwap.ts +++ b/src/mappings/nfts/createSwap.ts @@ -28,7 +28,6 @@ export async function handleCreateSwap(context: Context): Promise { warn(OPERATION, `Will be treated as **${Action.OFFER}**`) // TRUE_OPERATION = Action.OFFER } - const Entity = offer ? Offer : Swap // TODO: SWAP CAN BE OVERWRITTEN! const id = createTokenId(event.collectionId, event.sn) const final = offer ? await getOrCreate(context.store, Offer, id, {}) : await getOrCreate(context.store, Swap, id, {}) From 26858e43d05d8e9952bd7ca950e1f4f6e4ad0cc8 Mon Sep 17 00:00:00 2001 From: Viki Val Date: Mon, 22 Jul 2024 16:34:27 +0200 Subject: [PATCH 47/59] :bug: does not alter name --- src/mappings/shared/token/utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mappings/shared/token/utils.ts b/src/mappings/shared/token/utils.ts index 615c2ac1..81081848 100644 --- a/src/mappings/shared/token/utils.ts +++ b/src/mappings/shared/token/utils.ts @@ -16,7 +16,7 @@ export function generateTokenId(collectionId: string, nft: NE): string | undefin } export const collectionsToKeepNameAsIs: Record = { - statemine: [ + kusama: [ '176', // chained - generative art ], } From 50544d9e49ff18a0c25bd0468c85ceb731c61062 Mon Sep 17 00:00:00 2001 From: Viki Val Date: Mon, 22 Jul 2024 16:34:52 +0200 Subject: [PATCH 48/59] :card_file_box: offers and swaps --- db/migrations/1721653971599-Data.js | 37 +++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 db/migrations/1721653971599-Data.js diff --git a/db/migrations/1721653971599-Data.js b/db/migrations/1721653971599-Data.js new file mode 100644 index 00000000..ed0c3561 --- /dev/null +++ b/db/migrations/1721653971599-Data.js @@ -0,0 +1,37 @@ +module.exports = class Data1721653971599 { + name = 'Data1721653971599' + + async up(db) { + await db.query(`CREATE TABLE "offer" ("id" character varying NOT NULL, "block_number" numeric NOT NULL, "caller" text NOT NULL, "created_at" TIMESTAMP WITH TIME ZONE NOT NULL, "expiration" numeric NOT NULL, "price" numeric NOT NULL, "status" character varying(9) NOT NULL, "updated_at" TIMESTAMP WITH TIME ZONE, "considered_id" character varying, "desired_id" character varying, "nft_id" character varying, CONSTRAINT "REL_71609884f4478ed41be6672a66" UNIQUE ("nft_id"), CONSTRAINT "PK_57c6ae1abe49201919ef68de900" PRIMARY KEY ("id"))`) + await db.query(`CREATE INDEX "IDX_004a20a1eed4189bc23b13efa0" ON "offer" ("considered_id") `) + await db.query(`CREATE INDEX "IDX_f8c1e3faf9cdba27703e0ea2c5" ON "offer" ("desired_id") `) + await db.query(`CREATE UNIQUE INDEX "IDX_71609884f4478ed41be6672a66" ON "offer" ("nft_id") `) + await db.query(`CREATE TABLE "swap" ("id" character varying NOT NULL, "block_number" numeric NOT NULL, "caller" text NOT NULL, "created_at" TIMESTAMP WITH TIME ZONE NOT NULL, "expiration" numeric NOT NULL, "price" numeric, "status" character varying(9) NOT NULL, "surcharge" character varying(7), "updated_at" TIMESTAMP WITH TIME ZONE, "considered_id" character varying, "desired_id" character varying, "nft_id" character varying, CONSTRAINT "REL_4a045cf15c5c5c44e6cf52e70c" UNIQUE ("nft_id"), CONSTRAINT "PK_4a10d0f359339acef77e7f986d9" PRIMARY KEY ("id"))`) + await db.query(`CREATE INDEX "IDX_ef7a3bc067c4f3dd314c90f79a" ON "swap" ("considered_id") `) + await db.query(`CREATE INDEX "IDX_ded173f5a5ff89483d9ffa4dce" ON "swap" ("desired_id") `) + await db.query(`CREATE UNIQUE INDEX "IDX_4a045cf15c5c5c44e6cf52e70c" ON "swap" ("nft_id") `) + await db.query(`ALTER TABLE "offer" ADD CONSTRAINT "FK_004a20a1eed4189bc23b13efa0d" FOREIGN KEY ("considered_id") REFERENCES "collection_entity"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`) + await db.query(`ALTER TABLE "offer" ADD CONSTRAINT "FK_f8c1e3faf9cdba27703e0ea2c54" FOREIGN KEY ("desired_id") REFERENCES "nft_entity"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`) + await db.query(`ALTER TABLE "offer" ADD CONSTRAINT "FK_71609884f4478ed41be6672a668" FOREIGN KEY ("nft_id") REFERENCES "nft_entity"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`) + await db.query(`ALTER TABLE "swap" ADD CONSTRAINT "FK_ef7a3bc067c4f3dd314c90f79a5" FOREIGN KEY ("considered_id") REFERENCES "collection_entity"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`) + await db.query(`ALTER TABLE "swap" ADD CONSTRAINT "FK_ded173f5a5ff89483d9ffa4dce6" FOREIGN KEY ("desired_id") REFERENCES "nft_entity"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`) + await db.query(`ALTER TABLE "swap" ADD CONSTRAINT "FK_4a045cf15c5c5c44e6cf52e70c2" FOREIGN KEY ("nft_id") REFERENCES "nft_entity"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`) + } + + async down(db) { + await db.query(`DROP TABLE "offer"`) + await db.query(`DROP INDEX "public"."IDX_004a20a1eed4189bc23b13efa0"`) + await db.query(`DROP INDEX "public"."IDX_f8c1e3faf9cdba27703e0ea2c5"`) + await db.query(`DROP INDEX "public"."IDX_71609884f4478ed41be6672a66"`) + await db.query(`DROP TABLE "swap"`) + await db.query(`DROP INDEX "public"."IDX_ef7a3bc067c4f3dd314c90f79a"`) + await db.query(`DROP INDEX "public"."IDX_ded173f5a5ff89483d9ffa4dce"`) + await db.query(`DROP INDEX "public"."IDX_4a045cf15c5c5c44e6cf52e70c"`) + await db.query(`ALTER TABLE "offer" DROP CONSTRAINT "FK_004a20a1eed4189bc23b13efa0d"`) + await db.query(`ALTER TABLE "offer" DROP CONSTRAINT "FK_f8c1e3faf9cdba27703e0ea2c54"`) + await db.query(`ALTER TABLE "offer" DROP CONSTRAINT "FK_71609884f4478ed41be6672a668"`) + await db.query(`ALTER TABLE "swap" DROP CONSTRAINT "FK_ef7a3bc067c4f3dd314c90f79a5"`) + await db.query(`ALTER TABLE "swap" DROP CONSTRAINT "FK_ded173f5a5ff89483d9ffa4dce6"`) + await db.query(`ALTER TABLE "swap" DROP CONSTRAINT "FK_4a045cf15c5c5c44e6cf52e70c2"`) + } +} From 4bb4f3c3d2e1333c6cbbe2e186bce21d8f65a9da Mon Sep 17 00:00:00 2001 From: Viki Val Date: Mon, 22 Jul 2024 16:47:05 +0200 Subject: [PATCH 49/59] :memo: updated memo --- README.md | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index bfd9d1f3..ae8863bb 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # stick -![](https://media.tenor.com/eK1dyB3TOLsAAAAC/anime-stick.gif) +![](https://media1.tenor.com/m/Eu0LNbU4hQMAAAAC/jeanne-darc-vanitas-no-carte.gif) [Squid](https://docs.subsquid.io) based data used to index, process, and query on top of AssetHub for [KodaDot](https://kodadot.xyz) NFT Marketplace. @@ -8,7 +8,7 @@ * Kusama AssetHub Processor (Statemine -> KSM): https://squid.subsquid.io/stick/graphql * Polkadot AssetHub Processor (Statemint -> DOT): https://squid.subsquid.io/speck/graphql -* Pasoe Testnet Processor: 🚧 Coming soon 🚧 +* Paseo Testnet Processor: 🚧 Coming soon 🚧 ## Project structure @@ -129,22 +129,29 @@ The architecture of this project is following: 1. fast generate event handlers -``` +```bash pbpaste | cut -d '=' -f 1 | tr -d ' ' | xargs -I_ echo "processor.addEventHandler(Event._, dummy);" ``` 2. enable debug logs (in .env) -``` +```bash SQD_DEBUG=squid:log ``` 3. generate metagetters from getters -``` +```bash pbpaste | grep 'export' | xargs -I_ echo "_ return proc. }" ``` +4. enable offers + +`Offers` support is a hack on top of the `Atomic Swap` to enable `Offers` set in `.env` file + +```bash +OFFER= +``` ## Funding From 8d0ef06377f5d6fff5e57e3613b36f0547d14160 Mon Sep 17 00:00:00 2001 From: Viki Val Date: Mon, 22 Jul 2024 16:47:59 +0200 Subject: [PATCH 50/59] :memo: correct link to image --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ae8863bb..08069f10 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # stick -![](https://media1.tenor.com/m/Eu0LNbU4hQMAAAAC/jeanne-darc-vanitas-no-carte.gif) +![](https://media.tenor.com/Eu0LNbU4hQMAAAAC/jeanne-darc-vanitas-no-carte.gif) [Squid](https://docs.subsquid.io) based data used to index, process, and query on top of AssetHub for [KodaDot](https://kodadot.xyz) NFT Marketplace. From 18c91550b9b5d944585c99d76b4624a8571e9335 Mon Sep 17 00:00:00 2001 From: Viki Val Date: Mon, 22 Jul 2024 16:50:05 +0200 Subject: [PATCH 51/59] :squid: tokenEntity --- schema.graphql | 10 ++++----- src/model/generated/tokenEntity.model.ts | 28 ++++++++++++------------ 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/schema.graphql b/schema.graphql index 3450d4f7..e34cf4cf 100644 --- a/schema.graphql +++ b/schema.graphql @@ -41,18 +41,18 @@ type TokenEntity @entity { id: ID! blockNumber: BigInt collection: CollectionEntity - nfts: [NFTEntity!] @derivedFrom(field: "token") + count: Int! + createdAt: DateTime! + deleted: Boolean! hash: String! @index image: String media: String meta: MetadataEntity metadata: String name: String @index - updatedAt: DateTime! - createdAt: DateTime! + nfts: [NFTEntity!] @derivedFrom(field: "token") supply: Int! - count: Int! - deleted: Boolean! + updatedAt: DateTime! } # Entity to represent a collection diff --git a/src/model/generated/tokenEntity.model.ts b/src/model/generated/tokenEntity.model.ts index 6ffd7341..5f0522ae 100644 --- a/src/model/generated/tokenEntity.model.ts +++ b/src/model/generated/tokenEntity.model.ts @@ -1,7 +1,7 @@ -import {Entity as Entity_, Column as Column_, PrimaryColumn as PrimaryColumn_, BigIntColumn as BigIntColumn_, ManyToOne as ManyToOne_, Index as Index_, OneToMany as OneToMany_, StringColumn as StringColumn_, DateTimeColumn as DateTimeColumn_, IntColumn as IntColumn_, BooleanColumn as BooleanColumn_} from "@subsquid/typeorm-store" +import {Entity as Entity_, Column as Column_, PrimaryColumn as PrimaryColumn_, BigIntColumn as BigIntColumn_, ManyToOne as ManyToOne_, Index as Index_, IntColumn as IntColumn_, DateTimeColumn as DateTimeColumn_, BooleanColumn as BooleanColumn_, StringColumn as StringColumn_, OneToMany as OneToMany_} from "@subsquid/typeorm-store" import {CollectionEntity} from "./collectionEntity.model" -import {NFTEntity} from "./nftEntity.model" import {MetadataEntity} from "./metadataEntity.model" +import {NFTEntity} from "./nftEntity.model" @Entity_() export class TokenEntity { @@ -19,8 +19,14 @@ export class TokenEntity { @ManyToOne_(() => CollectionEntity, {nullable: true}) collection!: CollectionEntity | undefined | null - @OneToMany_(() => NFTEntity, e => e.token) - nfts!: NFTEntity[] + @IntColumn_({nullable: false}) + count!: number + + @DateTimeColumn_({nullable: false}) + createdAt!: Date + + @BooleanColumn_({nullable: false}) + deleted!: boolean @Index_() @StringColumn_({nullable: false}) @@ -43,18 +49,12 @@ export class TokenEntity { @StringColumn_({nullable: true}) name!: string | undefined | null - @DateTimeColumn_({nullable: false}) - updatedAt!: Date - - @DateTimeColumn_({nullable: false}) - createdAt!: Date + @OneToMany_(() => NFTEntity, e => e.token) + nfts!: NFTEntity[] @IntColumn_({nullable: false}) supply!: number - @IntColumn_({nullable: false}) - count!: number - - @BooleanColumn_({nullable: false}) - deleted!: boolean + @DateTimeColumn_({nullable: false}) + updatedAt!: Date } From 136327f840947f682d8b5c151e96b006dfbce44c Mon Sep 17 00:00:00 2001 From: Viki Val Date: Mon, 22 Jul 2024 16:52:32 +0200 Subject: [PATCH 52/59] :memo: offer --- schema.graphql | 3 +++ 1 file changed, 3 insertions(+) diff --git a/schema.graphql b/schema.graphql index e34cf4cf..55304d99 100644 --- a/schema.graphql +++ b/schema.graphql @@ -167,6 +167,9 @@ type CollectionEvent implements EventType @entity { # timestamp: DateTime! # } +# Entity to represent a Offer +# defined on chain as pub type PendingSwapOf, I: 'static = ()> +# https://github.com/paritytech/polkadot-sdk/blob/d0d8e29197a783f3ea300569afc50244a280cafa/substrate/frame/nfts/src/types.rs#L207 type Offer @entity { id: ID! # collection-id // same as NFTEntity.id # events: [TradeEvent!] @derivedFrom(field: "offer") From 41e86d7e6fac71a6a287b17f98e11d6c33b5101a Mon Sep 17 00:00:00 2001 From: Viki Val Date: Wed, 24 Jul 2024 11:04:06 +0200 Subject: [PATCH 53/59] Refactor isNFT and isOffer functions for better readability and maintainability --- src/mappings/utils/types.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/mappings/utils/types.ts b/src/mappings/utils/types.ts index 316ead92..de3a6e79 100644 --- a/src/mappings/utils/types.ts +++ b/src/mappings/utils/types.ts @@ -67,10 +67,18 @@ export function collectionEventFrom( return eventFrom(interaction, basecall, meta) } +/** + * Check is current entity is NFT + * @param event - event should satisfy { collectionId: string, sn: string } interface +**/ export function isNFT(event: T) { return event.sn !== undefined } +/** + * Check is current entity is Offer + * @param event - event should satisfy { collectionId: string, sn: string } interface +**/ export function isOffer(event: T): boolean { return event.collectionId === COLLECTION_OFFER } From 479385304265ec9d535de7b05f73ac1be2c9dd9b Mon Sep 17 00:00:00 2001 From: Viki Val Date: Wed, 24 Jul 2024 11:05:50 +0200 Subject: [PATCH 54/59] Update README.md to enable different chain support --- README.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 08069f10..ceec7464 100644 --- a/README.md +++ b/README.md @@ -145,7 +145,16 @@ SQD_DEBUG=squid:log pbpaste | grep 'export' | xargs -I_ echo "_ return proc. }" ``` -4. enable offers +4. Enable different chain (currently only Kusama and Polkadot are supported) + +> [!NOTE] +> By default the chain is set to `kusama` + +```bash +CHAIN=polkadot # or kusama +``` + +5. enable offers `Offers` support is a hack on top of the `Atomic Swap` to enable `Offers` set in `.env` file From e8f46f4677b036b61a59cfa8bbdc90a788472a48 Mon Sep 17 00:00:00 2001 From: Viki Val Date: Wed, 24 Jul 2024 13:52:14 +0200 Subject: [PATCH 55/59] :broom: format --- src/mappings/shared/token/burn.ts | 6 ++++-- src/mappings/shared/token/mint.ts | 18 ++++++++++++---- src/mappings/shared/token/setMetadata.ts | 26 ++++++++++++++++++------ src/mappings/shared/token/tokenAPI.ts | 20 +++++++++++++----- src/mappings/shared/token/utils.ts | 11 +++++++--- 5 files changed, 61 insertions(+), 20 deletions(-) diff --git a/src/mappings/shared/token/burn.ts b/src/mappings/shared/token/burn.ts index 1c9eb3e1..513d7e0d 100644 --- a/src/mappings/shared/token/burn.ts +++ b/src/mappings/shared/token/burn.ts @@ -2,7 +2,7 @@ import { getWith } from '@kodadot1/metasquid/entity' import { Context } from '../../utils/types' import { NFTEntity as NE } from '../../../model' import { debug, warn } from '../../utils/logger' -import { OPERATION, generateTokenId } from './utils' +import { generateTokenId, OPERATION } from './utils' import { TokenAPI } from './tokenAPI' export async function burnHandler(context: Context, nft: NE): Promise { @@ -15,7 +15,9 @@ export async function burnHandler(context: Context, nft: NE): Promise { const tokenAPI = new TokenAPI(context.store) try { - const nftWithToken = await getWith(context.store, NE, nft.id, { token: true }) + const nftWithToken = await getWith(context.store, NE, nft.id, { + token: true, + }) if (nftWithToken?.token) { await tokenAPI.removeNftFromToken(nft, nftWithToken.token) } diff --git a/src/mappings/shared/token/mint.ts b/src/mappings/shared/token/mint.ts index 5c9144c2..0bd6d7bb 100644 --- a/src/mappings/shared/token/mint.ts +++ b/src/mappings/shared/token/mint.ts @@ -1,11 +1,19 @@ import { getOptional } from '@kodadot1/metasquid/entity' import { Context } from '../../utils/types' -import { CollectionEntity as CE, NFTEntity as NE, TokenEntity as TE } from '../../../model' +import { + CollectionEntity as CE, + NFTEntity as NE, + TokenEntity as TE, +} from '../../../model' import { debug } from '../../utils/logger' -import { OPERATION, generateTokenId } from './utils' +import { generateTokenId, OPERATION } from './utils' import { TokenAPI } from './tokenAPI' -export async function mintHandler(context: Context, collection: CE, nft: NE): Promise { +export async function mintHandler( + context: Context, + collection: CE, + nft: NE, +): Promise { debug(OPERATION, { mintHandler: `Handle mint for NFT ${nft.id}` }) const tokenId = generateTokenId(collection.id, nft) @@ -16,5 +24,7 @@ export async function mintHandler(context: Context, collection: CE, nft: NE): Pr const tokenApi = new TokenAPI(context.store) const existingToken = await getOptional(context.store, TE, tokenId) - return await (existingToken ? tokenApi.addNftToToken(nft, existingToken) : tokenApi.create(collection, nft)) + return await (existingToken + ? tokenApi.addNftToToken(nft, existingToken) + : tokenApi.create(collection, nft)) } diff --git a/src/mappings/shared/token/setMetadata.ts b/src/mappings/shared/token/setMetadata.ts index 44c52ed8..3670a58a 100644 --- a/src/mappings/shared/token/setMetadata.ts +++ b/src/mappings/shared/token/setMetadata.ts @@ -1,12 +1,22 @@ 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 { + CollectionEntity as CE, + NFTEntity as NE, + TokenEntity as TE, +} from '../../../model' import { debug, warn } from '../../utils/logger' -import { OPERATION, generateTokenId } from './utils' +import { generateTokenId, OPERATION } from './utils' import { TokenAPI } from './tokenAPI' -export async function setMetadataHandler(context: Context, collection: CE, nft: NE): Promise { - debug(OPERATION, { handleMetadataSet: `Handle set metadata for NFT ${nft.id}` }) +export async function setMetadataHandler( + context: Context, + collection: CE, + nft: NE, +): Promise { + debug(OPERATION, { + handleMetadataSet: `Handle set metadata for NFT ${nft.id}`, + }) const tokenId = generateTokenId(collection.id, nft) if (!tokenId) { @@ -16,7 +26,9 @@ export async function setMetadataHandler(context: Context, collection: CE, nft: const tokenAPI = new TokenAPI(context.store) try { - const nftWithToken = await getWith(context.store, NE, nft.id, { token: true }) + const nftWithToken = await getWith(context.store, NE, nft.id, { + token: true, + }) if (nftWithToken?.token) { await tokenAPI.removeNftFromToken(nft, nftWithToken.token) } @@ -26,5 +38,7 @@ export async function setMetadataHandler(context: Context, collection: CE, nft: } const existingToken = await getOptional(context.store, TE, tokenId) - return await (existingToken ? tokenAPI.addNftToToken(nft, existingToken) : tokenAPI.create(collection, nft)) + return await (existingToken + ? tokenAPI.addNftToToken(nft, existingToken) + : tokenAPI.create(collection, nft)) } diff --git a/src/mappings/shared/token/tokenAPI.ts b/src/mappings/shared/token/tokenAPI.ts index 8895a0b8..41646be6 100644 --- a/src/mappings/shared/token/tokenAPI.ts +++ b/src/mappings/shared/token/tokenAPI.ts @@ -1,9 +1,13 @@ import { create as createEntity, emOf } 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 { + CollectionEntity as CE, + NFTEntity as NE, + TokenEntity as TE, +} from '../../../model' import { debug } from '../../utils/logger' -import { OPERATION, generateTokenId, tokenName } from './utils' +import { generateTokenId, OPERATION, tokenName } from './utils' export class TokenAPI { constructor(private store: Store) {} @@ -13,7 +17,9 @@ export class TokenAPI { if (!tokenId) { return } - debug(OPERATION, { createToken: `Create TOKEN ${tokenId} for NFT ${nft.id}` }) + debug(OPERATION, { + createToken: `Create TOKEN ${tokenId} for NFT ${nft.id}`, + }) const token = createEntity(TE, tokenId, { createdAt: nft.createdAt, @@ -42,7 +48,9 @@ export class TokenAPI { if (!token) { return } - debug(OPERATION, { removeNftFromToken: `Unlink NFT ${nft.id} from TOKEN ${token.id}` }) + debug(OPERATION, { + removeNftFromToken: `Unlink NFT ${nft.id} from TOKEN ${token.id}`, + }) await emOf(this.store).update(NE, nft.id, { token: null }) const updatedCount = await emOf(this.store).countBy(NE, { @@ -80,7 +88,9 @@ export class TokenAPI { if (nft.token?.id === token.id) { return token } - debug(OPERATION, { updateToken: `Add NFT ${nft.id} to TOKEN ${token.id} for ` }) + debug(OPERATION, { + updateToken: `Add NFT ${nft.id} to TOKEN ${token.id} for `, + }) token.count += 1 token.supply += nft.burned ? 0 : 1 token.updatedAt = nft.updatedAt diff --git a/src/mappings/shared/token/utils.ts b/src/mappings/shared/token/utils.ts index 81081848..aeaf2b57 100644 --- a/src/mappings/shared/token/utils.ts +++ b/src/mappings/shared/token/utils.ts @@ -5,7 +5,10 @@ import { CHAIN } from '../../../environment' export const OPERATION = 'TokenEntity' as any -export function generateTokenId(collectionId: string, nft: NE): string | undefined { +export function generateTokenId( + collectionId: string, + nft: NE, +): string | undefined { if (!nft.image && !nft.media) { warn(OPERATION, `MISSING NFT MEDIA ${nft.id}`) return undefined @@ -21,7 +24,10 @@ export const collectionsToKeepNameAsIs: Record = { ], } -export const tokenName = (nftName: string | undefined | null, collectionId: string): string => { +export const tokenName = ( + nftName: string | undefined | null, + collectionId: string, +): string => { if (typeof nftName !== 'string') { return '' } @@ -30,4 +36,3 @@ export const tokenName = (nftName: string | undefined | null, collectionId: stri return doNotAlter ? nftName : nftName.replace(/([#_]\d+$)/g, '') } - From 2dbb3c5e7e0c098000d247aee269cf9d029525b3 Mon Sep 17 00:00:00 2001 From: Viki Val Date: Wed, 31 Jul 2024 11:19:46 +0200 Subject: [PATCH 56/59] :note: createSwap.ts --- src/mappings/nfts/createSwap.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mappings/nfts/createSwap.ts b/src/mappings/nfts/createSwap.ts index 070a91fd..8df84318 100644 --- a/src/mappings/nfts/createSwap.ts +++ b/src/mappings/nfts/createSwap.ts @@ -28,7 +28,7 @@ export async function handleCreateSwap(context: Context): Promise { warn(OPERATION, `Will be treated as **${Action.OFFER}**`) // TRUE_OPERATION = Action.OFFER } - // TODO: SWAP CAN BE OVERWRITTEN! + // DEV_NOT: SWAP CAN BE OVERWRITTEN! const id = createTokenId(event.collectionId, event.sn) const final = offer ? await getOrCreate(context.store, Offer, id, {}) : await getOrCreate(context.store, Swap, id, {}) const deadline = BigInt(event.deadline) From 8b73f7ab5215e31734250ba08d1775e9ff4d1686 Mon Sep 17 00:00:00 2001 From: Viki Val Date: Fri, 2 Aug 2024 14:20:19 +0200 Subject: [PATCH 57/59] :memo: :bug: typo in word case --- src/mappings/nfts/transfer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mappings/nfts/transfer.ts b/src/mappings/nfts/transfer.ts index d0dfe51b..6c3cbedf 100644 --- a/src/mappings/nfts/transfer.ts +++ b/src/mappings/nfts/transfer.ts @@ -24,7 +24,7 @@ export async function handleTokenTransfer(context: Context): Promise { const event = unwrap(context, getTransferTokenEvent) debug(OPERATION, event) - // Check if event has a name and can be skipped in some canses + // Check if event has a name and can be skipped in some cases switch (event.name) { case NonFungibleCall.buyItem: skip(OPERATION, `because it is **${event.name}**`) From 6fefe71f010baab331ef926fe7b50d89b49b7e88 Mon Sep 17 00:00:00 2001 From: Viki Val Date: Fri, 2 Aug 2024 14:21:16 +0200 Subject: [PATCH 58/59] :memo: notes on Swaps --- README.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/README.md b/README.md index ceec7464..cb4896eb 100644 --- a/README.md +++ b/README.md @@ -162,6 +162,19 @@ CHAIN=polkadot # or kusama OFFER= ``` +### Note on Swaps + +1. Swaps can be overwritten at any time + +Therefore if you have a swap, and will create a new one, the old one will be overwritten. This is mentioned in `createSwap.ts` Line 31. + +2. Swaps are autocancelled by few conditions + +- if you `burn` the NFT +- if you `transfer` the NFT + +in any other condition the swap will have to be cancelled manually. + ## Funding Project was funded as a common good by From f1bbe8759066a597bce95e5714266e414f5feb8c Mon Sep 17 00:00:00 2001 From: Viki Val Date: Fri, 2 Aug 2024 14:21:39 +0200 Subject: [PATCH 59/59] :zap: save swap in nft if it is enabled by schema --- src/mappings/nfts/createSwap.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/mappings/nfts/createSwap.ts b/src/mappings/nfts/createSwap.ts index 8df84318..991bbda1 100644 --- a/src/mappings/nfts/createSwap.ts +++ b/src/mappings/nfts/createSwap.ts @@ -51,8 +51,14 @@ export async function handleCreateSwap(context: Context): Promise { final.status = final.blockNumber >= deadline ? TradeStatus.EXPIRED : TradeStatus.ACTIVE final.updatedAt = event.timestamp - // TODO: SAVE SOMEWHERE await context.store.save(final) + + // DEV_NOTE: need to be first enabled by schema (line 82) + // if ('swap' in nft) { + // nft.swap = final + // await context.store.save(nft) + // } + success(OPERATION, `${id} by ${event.caller} by ${event.caller}`) // SwapCreated {