diff --git a/ecosystem/ecosystem.minter.service.ts b/ecosystem/ecosystem.minter.service.ts index 2291b4a..edbf357 100644 --- a/ecosystem/ecosystem.minter.service.ts +++ b/ecosystem/ecosystem.minter.service.ts @@ -1,8 +1,9 @@ +import { gql } from '@apollo/client/core'; import { Injectable, Logger } from '@nestjs/common'; +import { Address } from 'viem'; import { PONDER_CLIENT } from '../api.apollo.config'; -import { gql } from '@apollo/client/core'; import { ApiMinterListing, ApiMinterMapping, MinterQuery, MinterQueryObjectArray } from './ecosystem.minter.types'; -import { Address } from 'viem'; +import { EcosystemMintQueryItem } from './ecosystem.stablecoin.types'; @Injectable() export class EcosystemMinterService { @@ -84,4 +85,32 @@ export class EcosystemMinterService { return list; } + + async getRecentMints(timestamp: Date, minValue: bigint): Promise { + const checkTimestamp = Math.trunc(timestamp.getTime() / 1000); + + const mintsFetched = await PONDER_CLIENT.query({ + fetchPolicy: 'no-cache', + query: gql` + query { + mints( + orderBy: "timestamp", orderDirection: "desc" + where: { + timestamp_gt: "${checkTimestamp}" + value_gte: "${minValue}" + } + ) { + items { + txHash + blockheight + to + value + timestamp + } + } + } + `, + }); + return mintsFetched?.data?.mints?.items ?? []; + } } diff --git a/ecosystem/ecosystem.stablecoin.types.ts b/ecosystem/ecosystem.stablecoin.types.ts index 73e9b6d..175993c 100644 --- a/ecosystem/ecosystem.stablecoin.types.ts +++ b/ecosystem/ecosystem.stablecoin.types.ts @@ -1,5 +1,5 @@ -import { PriceQueryCurrencies } from '../prices/prices.types'; import { Address } from 'viem'; +import { PriceQueryCurrencies } from '../prices/prices.types'; // -------------------------------------------------------------------------- // Ponder return types @@ -10,7 +10,7 @@ export type EcosystemQueryItem = { }; export type EcosystemMintQueryItem = { - id: string; + txHash: string; to: string; value: bigint; blockheight: bigint; diff --git a/socialmedia/socialmedia.service.ts b/socialmedia/socialmedia.service.ts index c451eb2..61adead 100644 --- a/socialmedia/socialmedia.service.ts +++ b/socialmedia/socialmedia.service.ts @@ -3,6 +3,8 @@ import { StablecoinEnum } from 'bridge/bridge.enum'; import { BridgeService } from 'bridge/bridge.service'; import { StablecoinBridgeQuery } from 'bridge/bridge.types'; import { EcosystemDepsService } from 'ecosystem/ecosystem.deps.service'; +import { EcosystemMinterService } from 'ecosystem/ecosystem.minter.service'; +import { EcosystemMintQueryItem } from 'ecosystem/ecosystem.stablecoin.types'; import { FrontendCodeService } from 'frontendcode/frontendcode.service'; import { FrontendCodeRegisteredQuery, FrontendCodeSavingsQuery } from 'frontendcode/frontendcode.types'; import { TradesService } from 'trades/trade.service'; @@ -15,10 +17,12 @@ export interface SocialMediaFct { doSendFrontendCodeUpdates(frontendCodeRegistered: FrontendCodeRegisteredQuery): Promise; doSendTradeUpdates(trade: TradeQuery, depsMarketCap: number, totalShares: bigint): Promise; doSendBridgeUpdates(bridge: StablecoinBridgeQuery, stablecoin: string): Promise; + doSendMintUpdates(mint: EcosystemMintQueryItem): Promise; } const MIN_SAVING_AMOUNT = 1000; const MIN_BRIDGE_AMOUNT = 5000; +const MIN_MINTING_AMOUNT = 1000; @Injectable() export class SocialMediaService { @@ -32,7 +36,8 @@ export class SocialMediaService { private readonly frontendCode: FrontendCodeService, private readonly deps: EcosystemDepsService, private readonly trades: TradesService, - private readonly bridges: BridgeService + private readonly bridges: BridgeService, + private readonly mints: EcosystemMinterService ) { this.socialMediaRegister = new Map(); @@ -43,6 +48,7 @@ export class SocialMediaService { frontendCodeUpdates: time, tradeUpdates: time, bridgeUpdates: time, + mintUpdates: time, }; } @@ -59,6 +65,7 @@ export class SocialMediaService { await this.sendFrontendCodeUpdates(); await this.sendTradeUpdates(); await this.sendBridgeUpdates(); + await this.sendMintUpdates(); } private async sendUpdates(): Promise { @@ -156,4 +163,25 @@ export class SocialMediaService { this.logger.error('Error while sending bridge updates:', e); } } + + private async sendMintUpdates(): Promise { + try { + const checkDate = new Date(this.socialMediaState.mintUpdates); + const minAmount = BigInt(MIN_MINTING_AMOUNT * 10 ** 18); + + const requestedMints = await this.mints.getRecentMints(checkDate, minAmount); + + if (requestedMints.length > 0) { + this.socialMediaState.mintUpdates = Date.now(); + + for (const mint of requestedMints) { + for (const value of this.socialMediaRegister.values()) { + await value.doSendMintUpdates(mint); + } + } + } + } catch (e) { + this.logger.error('Error while sending mint updates:', e); + } + } } diff --git a/socialmedia/socialmedia.types.ts b/socialmedia/socialmedia.types.ts index 4b9088b..1dd1d1f 100644 --- a/socialmedia/socialmedia.types.ts +++ b/socialmedia/socialmedia.types.ts @@ -3,4 +3,5 @@ export type SocialMediaState = { frontendCodeUpdates: number; tradeUpdates: number; bridgeUpdates: number; + mintUpdates: number; }; diff --git a/socialmedia/telegram/messages/MintingUpdate.message.ts b/socialmedia/telegram/messages/MintingUpdate.message.ts index fb3b512..9a18339 100644 --- a/socialmedia/telegram/messages/MintingUpdate.message.ts +++ b/socialmedia/telegram/messages/MintingUpdate.message.ts @@ -1,50 +1,17 @@ -import { MintingUpdateQuery } from 'positions/positions.types'; -import { PriceQuery, PriceQueryObjectArray } from 'prices/prices.types'; +import { CONFIG } from 'api.config'; +import { EcosystemMintQueryItem } from 'ecosystem/ecosystem.stablecoin.types'; import { formatCurrency } from 'utils/format'; import { formatUnits } from 'viem'; -import { AppUrl, ExplorerAddressUrl, ExplorerTxUrl } from 'utils/func-helper'; -export function MintingUpdateMessage(minting: MintingUpdateQuery, prices: PriceQueryObjectArray): string { - const marketPrice = (prices[minting.collateral.toLowerCase()] as PriceQuery).price?.eur || 1; +export function MintingUpdateMessage(mint: EcosystemMintQueryItem): string[] { + const message = ` +*New dEURO Mint!* - const liqPrice = parseFloat(formatUnits(BigInt(minting.price), 36 - minting.collateralDecimals)); - // const liqPriceAdjusted = parseFloat(formatUnits(BigInt(minting.priceAdjusted), 36 - minting.collateralDecimals)); - - const ratio = marketPrice / liqPrice; - // const ratioAdjusted = marketPrice / liqPriceAdjusted; - - const minted = parseFloat(formatUnits(BigInt(minting.minted), 18)); - const mintedAdjusted = parseFloat(formatUnits(BigInt(minting.mintedAdjusted), 18)); - - const timefram = Math.round(minting.feeTimeframe / 60 / 60 / 24); - - const absStr = (n: number) => (n >= 0 ? '+' : '-'); - - return ` -*New Minting Update* - -Position: ${minting.position} -Owner: ${minting.owner} -[App Position](${AppUrl(`/monitoring/${minting.position}`)}) - -Collateral: ${minting.collateralName} (${minting.collateralSymbol}) -Market Price: ${formatCurrency(marketPrice, 2)} dEURO -Liq. Price: ${formatCurrency(liqPrice, 2)} dEURO -*Ratio: ${formatCurrency(ratio * 100, 2)}%* - -Minted: ${formatCurrency(minted, 2)} dEURO -*Changed: ${absStr(mintedAdjusted)}${formatCurrency(mintedAdjusted, 2)} dEURO* - -Annual Interest: ${formatCurrency(minting.annualInterestPPM / 10000, 2)}% -Reserve: ${formatCurrency(minting.reserveContribution / 10000, 2)}% +🏦 Lending Amount: *${formatCurrency(formatUnits(BigInt(mint.value), 18))}* +👤 [Lendner](https://etherscan.io/address/${mint.to}) / [TX](https://etherscan.io/tx/${mint.txHash}) +`; -FeeTimeframe: ${timefram} days -FeePct: ${formatCurrency(minting.feePPM / 10000, 2)}% -*FeePaid: ${formatCurrency(formatUnits(BigInt(minting.feePaid), 18), 2)} dEURO* + const image = `${CONFIG.telegram.imagesDir}/Lending.mp4`; -[Explorer Position](${ExplorerAddressUrl(minting.position)}) -[Explorer Owner](${ExplorerAddressUrl(minting.owner)}) -[Explorer Collateral](${ExplorerAddressUrl(minting.collateral)}) -[Explorer Transaction](${ExplorerTxUrl(minting.txHash)}) -`; + return [message, image]; } diff --git a/socialmedia/telegram/telegram.service.ts b/socialmedia/telegram/telegram.service.ts index ebfc29b..6b22c2b 100644 --- a/socialmedia/telegram/telegram.service.ts +++ b/socialmedia/telegram/telegram.service.ts @@ -3,10 +3,10 @@ import { CONFIG } from 'api.config'; import { StablecoinBridgeQuery } from 'bridge/bridge.types'; import { ChallengesService } from 'challenges/challenges.service'; import { EcosystemMinterService } from 'ecosystem/ecosystem.minter.service'; +import { EcosystemMintQueryItem } from 'ecosystem/ecosystem.stablecoin.types'; import { FrontendCodeRegisteredQuery, FrontendCodeSavingsQuery } from 'frontendcode/frontendcode.types'; import TelegramBot from 'node-telegram-bot-api'; import { PositionsService } from 'positions/positions.service'; -import { PricesService } from 'prices/prices.service'; import { SavingsLeadrateService } from 'savings/savings.leadrate.service'; import { SocialMediaFct, SocialMediaService } from 'socialmedia/socialmedia.service'; import { StorageService } from 'storage/storage.service'; @@ -41,7 +41,6 @@ export class TelegramService implements OnModuleInit, SocialMediaFct { private readonly minter: EcosystemMinterService, private readonly leadrate: SavingsLeadrateService, private readonly position: PositionsService, - private readonly prices: PricesService, private readonly challenge: ChallengesService ) { const time: number = Date.now() + 365 * 24 * 60 * 60 * 1000; @@ -54,7 +53,6 @@ export class TelegramService implements OnModuleInit, SocialMediaFct { positions: time, challenges: time, bids: time, - mintingUpdates: time, }; this.telegramGroupState = { @@ -194,20 +192,6 @@ export class TelegramService implements OnModuleInit, SocialMediaFct { this.sendMessageAll(BidTakenMessage(position, challenge, b)); } } - - // Minting updates - const requestedMintingUpdates = this.position - .getMintingUpdatesList() - .list.filter((m) => m.created * 1000 > this.telegramState.mintingUpdates && BigInt(m.mintedAdjusted) > 0n); - - if (requestedMintingUpdates.length > 0) { - this.telegramState.mintingUpdates = Date.now(); - - for (const mintingUpdate of requestedMintingUpdates) { - const prices = this.prices.getPricesMapping(); - this.sendMessageAll(MintingUpdateMessage(mintingUpdate, prices)); - } - } } async doSendSavingUpdates(savingSaved: FrontendCodeSavingsQuery): Promise { @@ -233,6 +217,12 @@ export class TelegramService implements OnModuleInit, SocialMediaFct { this.sendMessageAll(messageInfo[0], messageInfo[1]); } + async doSendMintUpdates(mint: EcosystemMintQueryItem): Promise { + if (BigInt(mint.value) === 0n) return; + const messageInfo = MintingUpdateMessage(mint); + this.sendMessageAll(messageInfo[0], messageInfo[1]); + } + private async sendMessageAll(message: string, video?: string) { if (this.telegramGroupState.groups.length == 0) return; for (const group of this.telegramGroupState.groups) { diff --git a/socialmedia/telegram/telegram.types.ts b/socialmedia/telegram/telegram.types.ts index 68e392d..814f058 100644 --- a/socialmedia/telegram/telegram.types.ts +++ b/socialmedia/telegram/telegram.types.ts @@ -7,7 +7,6 @@ export type TelegramState = { positions: number; challenges: number; bids: number; - mintingUpdates: number; }; export type TelegramSubscriptionState = { diff --git a/socialmedia/twitter/messages/MintingUpdate.message.ts b/socialmedia/twitter/messages/MintingUpdate.message.ts new file mode 100644 index 0000000..9f56e86 --- /dev/null +++ b/socialmedia/twitter/messages/MintingUpdate.message.ts @@ -0,0 +1,17 @@ +import { CONFIG } from 'api.config'; +import { EcosystemMintQueryItem } from 'ecosystem/ecosystem.stablecoin.types'; +import { formatCurrency } from 'utils/format'; +import { formatUnits } from 'viem'; + +export function MintingUpdateMessage(mint: EcosystemMintQueryItem): string[] { + const message = ` +New dEURO Mint! + +🏦 Lending Amount: ${formatCurrency(formatUnits(BigInt(mint.value), 18))} +🔗 Verifiable on the blockchain + `; + + const image = `${CONFIG.twitter.imagesDir}/Lending.png`; + + return [message, image]; +} diff --git a/socialmedia/twitter/twitter.service.ts b/socialmedia/twitter/twitter.service.ts index e3a3edd..07efa95 100644 --- a/socialmedia/twitter/twitter.service.ts +++ b/socialmedia/twitter/twitter.service.ts @@ -1,6 +1,7 @@ import { Injectable, Logger, OnModuleInit } from '@nestjs/common'; import { CONFIG } from 'api.config'; import { StablecoinBridgeQuery } from 'bridge/bridge.types'; +import { EcosystemMintQueryItem } from 'ecosystem/ecosystem.stablecoin.types'; import { FrontendCodeRegisteredQuery, FrontendCodeSavingsQuery } from 'frontendcode/frontendcode.types'; import { readFileSync } from 'fs'; import { SocialMediaFct, SocialMediaService } from 'socialmedia/socialmedia.service'; @@ -8,6 +9,7 @@ import { TradeQuery } from 'trades/trade.types'; import { SendTweetV2Params, TwitterApi } from 'twitter-api-v2'; import { TwitterAccessToken } from './dtos/twitter.dto'; import { FrontendCodeRegisteredMessage } from './messages/FrontendCodeRegistered.message'; +import { MintingUpdateMessage } from './messages/MintingUpdate.message'; import { SavingUpdateMessage } from './messages/SavingUpdate.message'; import { StablecoinBridgeMessage } from './messages/StablecoinBridgeUpdate.message'; import { TradeMessage } from './messages/Trade.message'; @@ -63,6 +65,12 @@ export class TwitterService implements OnModuleInit, SocialMediaFct { this.sendPost(messageInfo[0], messageInfo[1]); } + async doSendMintUpdates(mint: EcosystemMintQueryItem): Promise { + if (BigInt(mint.value) === 0n) return; + const messageInfo = MintingUpdateMessage(mint); + await this.sendPost(messageInfo[0], messageInfo[1]); + } + private async sendPost(message: string, media?: string): Promise { try { const tweetParams: Partial = {