diff --git a/src/modules/assets/models/AuctionEvent.enum.ts b/src/modules/assets/models/AuctionEvent.enum.ts index 3a02986f3..d2aa392ec 100644 --- a/src/modules/assets/models/AuctionEvent.enum.ts +++ b/src/modules/assets/models/AuctionEvent.enum.ts @@ -14,6 +14,7 @@ export enum ExternalAuctionEventEnum { BulkBuy = 'bulkBuy', ChangePrice = 'changePrice', UpdatePrice = 'updatePrice', + UpdateListing = 'changeListing', AcceptOffer = 'acceptOffer', UpdateOffer = 'update_offer_event', AcceptGlobalOffer = 'acceptGlobalOffer', diff --git a/src/modules/auctions/auctions-setter.service.ts b/src/modules/auctions/auctions-setter.service.ts index 8203031eb..f411df25a 100644 --- a/src/modules/auctions/auctions-setter.service.ts +++ b/src/modules/auctions/auctions-setter.service.ts @@ -197,6 +197,7 @@ export class AuctionsSetterService { const decimals = paymentToken?.decimals ?? mxConfig.decimals; return await this.persistenceService.updateAuction({ ...auction, + endDate: auction.endDate ?? 0, maxBidDenominated: BigNumberUtils.denominateAmount( auction.maxBid, decimals, @@ -208,7 +209,7 @@ export class AuctionsSetterService { }); } catch (error) { this.logger.error('An error occurred while updating auction', { - path: 'AuctionsService.updateAuction', + path: `${AuctionsSetterService.name}.${this.updateAuction.name}`, id: auction.id, exception: error, }); diff --git a/src/modules/rabbitmq/blockchain-events/handlers/updateListing-event.handler.ts b/src/modules/rabbitmq/blockchain-events/handlers/updateListing-event.handler.ts new file mode 100644 index 000000000..bdf9595e9 --- /dev/null +++ b/src/modules/rabbitmq/blockchain-events/handlers/updateListing-event.handler.ts @@ -0,0 +1,90 @@ +import { BinaryUtils } from '@elrondnetwork/erdnest'; +import { Injectable, Logger } from '@nestjs/common'; +import { Token } from 'src/common/services/mx-communication/models/Token.model'; +import { AuctionEntity } from 'src/db/auctions'; +import { ExternalAuctionEventEnum } from 'src/modules/assets/models'; +import { + AuctionsGetterService, + AuctionsSetterService, +} from 'src/modules/auctions'; +import { MarketplacesService } from 'src/modules/marketplaces/marketplaces.service'; +import { MarketplaceTypeEnum } from 'src/modules/marketplaces/models/MarketplaceType.enum'; +import { UsdPriceService } from 'src/modules/usdPrice/usd-price.service'; +import { BigNumberUtils } from 'src/utils/bigNumber-utils'; +import { UpdateListingEvent } from '../../entities/auction/updateListing.event'; + +@Injectable() +export class UpdateListingEventHandler { + private readonly logger = new Logger(UpdateListingEventHandler.name); + constructor( + private auctionsGetterService: AuctionsGetterService, + private auctionsService: AuctionsSetterService, + private readonly marketplaceService: MarketplacesService, + private usdPriceService: UsdPriceService, + ) {} + + async handle(event: any, hash: string, marketplaceType: MarketplaceTypeEnum) { + const updateListingEvent = new UpdateListingEvent(event); + const topics = updateListingEvent.getTopics(); + const marketplace = await this.marketplaceService.getMarketplaceByType( + updateListingEvent.getAddress(), + marketplaceType, + topics.collection, + ); + this.logger.log( + `Update listing event detected for hash '${hash}' and marketplace '${marketplace?.name}'`, + ); + let auction = await this.auctionsGetterService.getAuctionByIdAndMarketplace( + parseInt(topics.auctionId, 16), + marketplace.key, + ); + + if (auction && marketplace) { + const paymentToken = await this.usdPriceService.getToken( + auction.paymentToken, + ); + + this.updateAuctionListing( + auction, + updateListingEvent, + paymentToken, + hash, + ); + + this.auctionsService.updateAuction( + auction, + ExternalAuctionEventEnum.UpdateListing, + ); + } + } + + private updateAuctionListing( + auction: AuctionEntity, + event: UpdateListingEvent, + paymentToken: Token, + hash: string, + ) { + const eventTopics = event.getTopics(); + + if (eventTopics.newBid) { + auction.minBid = eventTopics.newBid; + auction.minBidDenominated = BigNumberUtils.denominateAmount( + eventTopics.newBid, + paymentToken.decimals, + ); + } + + if (eventTopics.deadline) { + auction.endDate = eventTopics.deadline; + } + + if (eventTopics.paymentToken) { + auction.paymentToken = eventTopics.paymentToken; + auction.paymentNonce = BinaryUtils.hexToNumber( + eventTopics.paymentTokenNonce, + ); + } + + auction.blockHash = hash; + } +} diff --git a/src/modules/rabbitmq/blockchain-events/marketplace-events.service.ts b/src/modules/rabbitmq/blockchain-events/marketplace-events.service.ts index bb2a245ba..191b11e96 100644 --- a/src/modules/rabbitmq/blockchain-events/marketplace-events.service.ts +++ b/src/modules/rabbitmq/blockchain-events/marketplace-events.service.ts @@ -16,6 +16,7 @@ import { AcceptOfferEventHandler } from './handlers/acceptOffer-event.handler'; import { AcceptGlobalOfferEventHandler } from './handlers/acceptGlobalOffer-event.handler'; import { SwapUpdateEventHandler } from './handlers/swapUpdate-event.handler'; import { SlackReportService } from 'src/common/services/mx-communication/slack-report.service'; +import { UpdateListingEventHandler } from './handlers/updateListing-event.handler'; @Injectable() export class MarketplaceEventsService { @@ -31,6 +32,7 @@ export class MarketplaceEventsService { private acceptOfferEventHandler: AcceptOfferEventHandler, private acceptGlobalOfferEventHandler: AcceptGlobalOfferEventHandler, private swapUpdateEventHandler: SwapUpdateEventHandler, + private updateListingEventHandler: UpdateListingEventHandler, private readonly slackReportService: SlackReportService, ) {} @@ -106,7 +108,14 @@ export class MarketplaceEventsService { marketplaceType, ); break; - + case ExternalAuctionEventEnum.UpdateListing: { + await this.updateListingEventHandler.handle( + event, + hash, + marketplaceType, + ); + break; + } case ExternalAuctionEventEnum.AcceptOffer: await this.acceptOfferEventHandler.handle( event, diff --git a/src/modules/rabbitmq/blockchain-events/nft-events.module.ts b/src/modules/rabbitmq/blockchain-events/nft-events.module.ts index a792c1718..84f803a43 100644 --- a/src/modules/rabbitmq/blockchain-events/nft-events.module.ts +++ b/src/modules/rabbitmq/blockchain-events/nft-events.module.ts @@ -38,6 +38,7 @@ import { UpdatePriceEventHandler } from './handlers/updatePrice-event.handler'; import { WithdrawAuctionEventHandler } from './handlers/withdrawAuction-event.handler'; import { MarketplaceEventsService } from './marketplace-events.service'; import { SwapUpdateEventHandler } from './handlers/swapUpdate-event.handler'; +import { UpdateListingEventHandler } from './handlers/updateListing-event.handler'; @Module({ imports: [ @@ -64,6 +65,7 @@ import { SwapUpdateEventHandler } from './handlers/swapUpdate-event.handler'; AcceptGlobalOfferEventHandler, AcceptOfferEventHandler, UpdatePriceEventHandler, + UpdateListingEventHandler, SwapUpdateEventHandler, NftEventsConsumer, NftEventsService, diff --git a/src/modules/rabbitmq/entities/auction/updateListing.event.topics.ts b/src/modules/rabbitmq/entities/auction/updateListing.event.topics.ts new file mode 100644 index 000000000..813f3aa98 --- /dev/null +++ b/src/modules/rabbitmq/entities/auction/updateListing.event.topics.ts @@ -0,0 +1,47 @@ +import { Address } from '@elrondnetwork/erdjs/out'; +import { BinaryUtils } from '@elrondnetwork/erdnest'; +export class UpdateListingEventsTopics { + private collection: string; + private nonce: string; + private auctionId: string; + private ownerAddress: Address; + private oldPrice: string; + private newBid: string; + private paymentToken: string; + private paymentTokenNonce: string; + private deadline: number = 0; + + constructor(rawTopics: string[]) { + this.collection = Buffer.from(rawTopics[1], 'base64').toString(); + this.nonce = Buffer.from(rawTopics[2], 'base64').toString('hex'); + this.auctionId = Buffer.from(rawTopics[3], 'base64').toString('hex'); + this.ownerAddress = new Address(Buffer.from(rawTopics[4], 'base64')); + this.oldPrice = Buffer.from(rawTopics[5], 'base64') + .toString('hex') + .hexBigNumberToString(); + this.newBid = Buffer.from(rawTopics[6], 'base64') + .toString('hex') + .hexBigNumberToString(); + this.paymentToken = BinaryUtils.base64Decode(rawTopics[7]); + this.paymentTokenNonce = + BinaryUtils.tryBase64ToBigInt(rawTopics[8])?.toString() ?? '0'; + this.deadline = parseInt( + Buffer.from(rawTopics[9] ?? '0', 'base64').toString('hex'), + 16, + ); + } + + toPlainObject() { + return { + collection: this.collection, + nonce: this.nonce, + auctionId: this.auctionId, + ownerAddress: this.ownerAddress, + oldPrice: this.oldPrice, + newBid: this.newBid, + paymentToken: this.paymentToken, + paymentTokenNonce: this.paymentTokenNonce, + deadline: this.deadline, + }; + } +} diff --git a/src/modules/rabbitmq/entities/auction/updateListing.event.ts b/src/modules/rabbitmq/entities/auction/updateListing.event.ts new file mode 100644 index 000000000..d4ef778be --- /dev/null +++ b/src/modules/rabbitmq/entities/auction/updateListing.event.ts @@ -0,0 +1,15 @@ +import { GenericEvent } from '../generic.event'; +import { UpdateListingEventsTopics } from './updateListing.event.topics'; + +export class UpdateListingEvent extends GenericEvent { + private decodedTopics: UpdateListingEventsTopics; + + constructor(init?: Partial) { + super(init); + this.decodedTopics = new UpdateListingEventsTopics(this.topics); + } + + getTopics() { + return this.decodedTopics.toPlainObject(); + } +}