Skip to content

Commit

Permalink
Add hatom liquidation event webhook
Browse files Browse the repository at this point in the history
  • Loading branch information
MogageNicolae committed Sep 18, 2024
1 parent 532918a commit e95b574
Show file tree
Hide file tree
Showing 11 changed files with 79 additions and 23 deletions.
1 change: 1 addition & 0 deletions .env.devnet
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ NETWORK=mainnet
API_URL=https://api.multiversx.com
DATA_API_CEX_URL=https://data-api.multiversx.com/v1/quotes/cex
DATA_API_XEXCHANGE_URL=https://data-api.multiversx.com/v1/quotes/xexchange
DATA_API_HATOM_URL=https://data-api.multiversx.com/v1/quotes/hatom
# DUNE_API_URL=http://localhost:3001/api/v1/table
DUNE_API_URL=https://api.dune.com/api/v1/table
DUNE_NAMESPACE=stefanmvx
Expand Down
1 change: 1 addition & 0 deletions .env.mainnet
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ NETWORK=mainnet
API_URL=https://api.multiversx.com
DATA_API_CEX_URL=https://data-api.multiversx.com/v1/quotes/cex
DATA_API_XEXCHANGE_URL=https://data-api.multiversx.com/v1/quotes/xexchange
DATA_API_HATOM_URL=https://data-api.multiversx.com/v1/quotes/hatom
DUNE_API_URL=http://localhost:3001/api/v1/table
# DUNE_API_URL=https://api.dune.com/api/v1/table
DUNE_NAMESPACE=stefanmvx
Expand Down
1 change: 1 addition & 0 deletions .env.testnet
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ NETWORK=mainnet
API_URL=https://api.multiversx.com
DATA_API_CEX_URL=https://data-api.multiversx.com/v1/quotes/cex
DATA_API_XEXCHANGE_URL=https://data-api.multiversx.com/v1/quotes/xexchange
DATA_API_HATOM_URL=https://data-api.multiversx.com/v1/quotes/hatom
# DUNE_API_URL=http://localhost:3001/api/v1/table
DUNE_API_URL=https://api.dune.com/api/v1/table
DUNE_NAMESPACE=stefanmvx
Expand Down
1 change: 1 addition & 0 deletions .multiversx/config/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ libs:
api: ${API_URL}
dataApiCex: ${DATA_API_CEX_URL}
dataApiXexchange: ${DATA_API_XEXCHANGE_URL}
dataApiHatom: ${DATA_API_HATOM_URL}
duneApi: ${DUNE_API_URL}
database:
host: 'localhost'
Expand Down
4 changes: 4 additions & 0 deletions apps/api/src/config/app-config.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ export class AppConfigService {
return configuration().libs.common.urls.dataApiXexchange ?? "";
}

getDataApiHatomUrl(): string {
return configuration().libs.common.urls.dataApiHatom ?? "";
}

getDuneApiUrl(): string {
return configuration().libs.common.urls.duneApi ?? "";
}
Expand Down
1 change: 1 addition & 0 deletions config/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ libs:
api: ${API_URL}
dataApiCex: ${DATA_API_CEX_URL}
dataApiXexchange: ${DATA_API_XEXCHANGE_URL}
dataApiHatom: ${DATA_API_HATOM_URL}
duneApi: ${DUNE_API_URL}
database:
host: 'localhost'
Expand Down
1 change: 1 addition & 0 deletions config/schema.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ libs:
api: string
dataApiCex: string
dataApiXexchange: string
dataApiHatom: string
duneApi: string
database:
host: string
Expand Down
1 change: 1 addition & 0 deletions libs/common/src/entities/config.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export interface Config {
api: string;
dataApiCex: string;
dataApiXexchange: string;
dataApiHatom: string;
duneApi: string;
};
database: {
Expand Down
18 changes: 15 additions & 3 deletions libs/services/src/data/data.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,28 @@ export class DataService {
private readonly appConfigService: AppConfigService,
) { }

async getTokenPrice(tokenId: string, date: moment.Moment): Promise<BigNumber> {
async getTokenPrice(tokenId: string, date: moment.Moment, market?: string): Promise<BigNumber> {
return await this.cachingService.getOrSet(
CacheInfo.TokenPrice(tokenId, date).key,
async () => await this.getTokenPriceRaw(tokenId, date),
async () => await this.getTokenPriceRaw(tokenId, date, market),
CacheInfo.TokenPrice(tokenId, date).ttl
);
}

async getTokenPriceRaw(tokenId: string, date: moment.Moment): Promise<BigNumber> {
async getTokenPriceRaw(tokenId: string, date: moment.Moment, market?: string): Promise<BigNumber> {
try {
if (market) {
switch (market) {
case 'hatom':
return (await axios.get<TokenPrice>(`${this.appConfigService.getDataApiHatomUrl()}/${tokenId}?date=${date.format('YYYY-MM-DD')}`)).data.price;
case 'xexchange':
return (await axios.get<TokenPrice>(`${this.appConfigService.getDataApiXexchangeUrl()}/${tokenId}?date=${date.format('YYYY-MM-DD')}`)).data.price;
case 'cex':
return (await axios.get<TokenPrice>(`${this.appConfigService.getDataApiCexUrl()}/${tokenId}?date=${date.format('YYYY-MM-DD')}`)).data.price;
default:
throw Error('Invalid market !');
}
}
if (tokenId.startsWith('USD')) {
return (await axios.get<TokenPrice>(`${this.appConfigService.getDataApiCexUrl()}/${tokenId}?date=${date.format('YYYY-MM-DD')}`)).data.price;
}
Expand Down
3 changes: 1 addition & 2 deletions libs/services/src/events/hatom.borrow.events.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@ import { CsvRecordsService } from "../records";
import moment from "moment";
import { DataService } from "../data";
import { TableSchema } from "apps/dune-simulator/src/endpoints/dune-simulator/entities";
import { decodeTopics, HatomEvent, joinCsvAttributes } from "libs/services/utils";
import { borrowEvent } from "../../utils/hex-constants";
import { borrowEvent, decodeTopics, HatomEvent, joinCsvAttributes } from "libs/services/utils";

interface BorrowEvent extends HatomEvent {
eventName: string;
Expand Down
70 changes: 52 additions & 18 deletions libs/services/src/events/hatom.liquidation.service.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,35 @@
import { Injectable } from "@nestjs/common";
import { TableSchema } from "apps/dune-simulator/src/endpoints/dune-simulator/entities";
import { EventLog } from "apps/api/src/endpoints/events/entities";
import { liquidationBorrowEvent } from "../../utils/hex-constants";
import { CsvRecordsService } from "../records";
import { decodeTopics, HatomEvent, joinCsvAttributes } from "libs/services/utils";
import { liquidationBorrowEvent, decodeTopics, getTokenIdByMoneyMarket, HatomEvent, joinCsvAttributes } from "libs/services/utils";
import BigNumber from "bignumber.js";
import moment from "moment";
import { DataService } from "../data";

interface LiquidationEvent extends HatomEvent {
'liquidator': string,
'borrower': string,
'amount': BigNumber,
'collateral_mma': string,
'tokens': BigNumber,
liquidator: string,
borrower: string,
amount: BigNumber,
collateral_mma: string,
tokens: BigNumber,
}

@Injectable()
export class HatomLiquidationService {
private readonly headers: TableSchema[] = [];
private readonly headers: TableSchema[] = [
{ name: "timestamp", type: "varchar" },
{ name: 'liquidator', type: 'varchar' },
{ name: 'account_liquidated', type: 'varchar' },
{ name: 'token', type: 'varchar' },
{ name: 'amount', type: 'double' },
{ name: 'amount_in_egld', type: 'double' },
{ name: 'amount_in_usd', type: 'double' },
];

constructor(
private readonly csvRecordsService: CsvRecordsService,
private readonly dataService: DataService,
) {}

public async hatomLiquidationWebhook(eventsLog: EventLog[]): Promise<void> {
Expand All @@ -31,17 +40,42 @@ export class HatomLiquidationService {
const currentEvent: LiquidationEvent = decodeTopics(properties, eventLog.topics.slice(1), types) as LiquidationEvent;
const eventDate = moment.unix(eventLog.timestamp);

await this.csvRecordsService.pushRecord("hatom_liquidation_events", [
joinCsvAttributes(
currentEvent.liquidator,
currentEvent.borrower,
eventDate.format('YYYY-MM-DD HH:mm:ss.SSS'),
currentEvent.amount.shiftedBy(-18).decimalPlaces(4),
currentEvent.collateral_mma,
currentEvent.tokens.shiftedBy(-18).decimalPlaces(4),
),
], this.headers);
const tokenId: string = getTokenIdByMoneyMarket(currentEvent.collateral_mma);

if (tokenId === "Not Found") {
console.log("Token ID not found for money market:", currentEvent.collateral_mma);
continue;
}

const tokenPrecision = await this.dataService.getTokenPrecision(tokenId);
const [liquidatedAmountInEGLD, liquidatedAmountInUSD] = await this.convertTokenValue(currentEvent.tokens, tokenId, eventDate);

await this.csvRecordsService.pushRecord(
"hatom_liquidation_events",
[
joinCsvAttributes(
eventDate.format('YYYY-MM-DD HH:mm:ss.SSS'),
currentEvent.liquidator,
currentEvent.borrower,
tokenId,
currentEvent.tokens.shiftedBy(-tokenPrecision).decimalPlaces(4),
liquidatedAmountInEGLD.shiftedBy(-tokenPrecision).decimalPlaces(4),
liquidatedAmountInUSD.shiftedBy(-tokenPrecision).decimalPlaces(4),
),
],
this.headers
);
}
}
}

async convertTokenValue(amount: BigNumber, tokenID: string, date: moment.Moment): Promise<[BigNumber, BigNumber]> {
const egldPrice = await this.dataService.getTokenPrice('WEGLD-bd4d79', date);
const tokenPrice = await this.dataService.getTokenPrice(tokenID, date, 'hatom');

const valueInUsd = amount.multipliedBy(tokenPrice);
const valueInEgld = valueInUsd.dividedBy(egldPrice);

return [valueInEgld, valueInUsd];
}
}

0 comments on commit e95b574

Please sign in to comment.