Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 31 additions & 2 deletions ecosystem/ecosystem.minter.service.ts
Original file line number Diff line number Diff line change
@@ -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 {
Expand Down Expand Up @@ -84,4 +85,32 @@ export class EcosystemMinterService {

return list;
}

async getRecentMints(timestamp: Date, minValue: bigint): Promise<EcosystemMintQueryItem[]> {
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 ?? [];
}
}
4 changes: 2 additions & 2 deletions ecosystem/ecosystem.stablecoin.types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { PriceQueryCurrencies } from '../prices/prices.types';
import { Address } from 'viem';
import { PriceQueryCurrencies } from '../prices/prices.types';

// --------------------------------------------------------------------------
// Ponder return types
Expand All @@ -10,7 +10,7 @@ export type EcosystemQueryItem = {
};

export type EcosystemMintQueryItem = {
id: string;
txHash: string;
to: string;
value: bigint;
blockheight: bigint;
Expand Down
30 changes: 29 additions & 1 deletion socialmedia/socialmedia.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -15,10 +17,12 @@ export interface SocialMediaFct {
doSendFrontendCodeUpdates(frontendCodeRegistered: FrontendCodeRegisteredQuery): Promise<void>;
doSendTradeUpdates(trade: TradeQuery, depsMarketCap: number, totalShares: bigint): Promise<void>;
doSendBridgeUpdates(bridge: StablecoinBridgeQuery, stablecoin: string): Promise<void>;
doSendMintUpdates(mint: EcosystemMintQueryItem): Promise<void>;
}

const MIN_SAVING_AMOUNT = 1000;
const MIN_BRIDGE_AMOUNT = 5000;
const MIN_MINTING_AMOUNT = 1000;

@Injectable()
export class SocialMediaService {
Expand All @@ -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();

Expand All @@ -43,6 +48,7 @@ export class SocialMediaService {
frontendCodeUpdates: time,
tradeUpdates: time,
bridgeUpdates: time,
mintUpdates: time,
};
}

Expand All @@ -59,6 +65,7 @@ export class SocialMediaService {
await this.sendFrontendCodeUpdates();
await this.sendTradeUpdates();
await this.sendBridgeUpdates();
await this.sendMintUpdates();
}

private async sendUpdates(): Promise<void> {
Expand Down Expand Up @@ -156,4 +163,25 @@ export class SocialMediaService {
this.logger.error('Error while sending bridge updates:', e);
}
}

private async sendMintUpdates(): Promise<void> {
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);
}
}
}
1 change: 1 addition & 0 deletions socialmedia/socialmedia.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ export type SocialMediaState = {
frontendCodeUpdates: number;
tradeUpdates: number;
bridgeUpdates: number;
mintUpdates: number;
};
53 changes: 10 additions & 43 deletions socialmedia/telegram/messages/MintingUpdate.message.ts
Original file line number Diff line number Diff line change
@@ -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];
}
24 changes: 7 additions & 17 deletions socialmedia/telegram/telegram.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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;
Expand All @@ -54,7 +53,6 @@ export class TelegramService implements OnModuleInit, SocialMediaFct {
positions: time,
challenges: time,
bids: time,
mintingUpdates: time,
};

this.telegramGroupState = {
Expand Down Expand Up @@ -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<void> {
Expand All @@ -233,6 +217,12 @@ export class TelegramService implements OnModuleInit, SocialMediaFct {
this.sendMessageAll(messageInfo[0], messageInfo[1]);
}

async doSendMintUpdates(mint: EcosystemMintQueryItem): Promise<void> {
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) {
Expand Down
1 change: 0 additions & 1 deletion socialmedia/telegram/telegram.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ export type TelegramState = {
positions: number;
challenges: number;
bids: number;
mintingUpdates: number;
};

export type TelegramSubscriptionState = {
Expand Down
17 changes: 17 additions & 0 deletions socialmedia/twitter/messages/MintingUpdate.message.ts
Original file line number Diff line number Diff line change
@@ -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];
}
8 changes: 8 additions & 0 deletions socialmedia/twitter/twitter.service.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
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';
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';
Expand Down Expand Up @@ -63,6 +65,12 @@ export class TwitterService implements OnModuleInit, SocialMediaFct {
this.sendPost(messageInfo[0], messageInfo[1]);
}

async doSendMintUpdates(mint: EcosystemMintQueryItem): Promise<void> {
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<string> {
try {
const tweetParams: Partial<SendTweetV2Params> = {
Expand Down