Skip to content

Commit 64955d6

Browse files
committed
[DEV-4476] Citrea monitoring
1 parent 538fb2b commit 64955d6

File tree

10 files changed

+120
-44
lines changed

10 files changed

+120
-44
lines changed
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
const { MigrationInterface, QueryRunner } = require("typeorm");
2+
3+
module.exports = class addCitreaMonitoring1763453659323 {
4+
name = 'addCitreaMonitoring1763453659323'
5+
6+
async up(queryRunner) {
7+
await queryRunner.query(`ALTER TABLE "monitoring_balance" ADD "citreaBalance" float NOT NULL CONSTRAINT "DF_98f1eabfa79a178eacdc47fe777" DEFAULT 0`);
8+
}
9+
10+
async down(queryRunner) {
11+
await queryRunner.query(`ALTER TABLE "monitoring_balance" DROP CONSTRAINT "DF_98f1eabfa79a178eacdc47fe777"`);
12+
await queryRunner.query(`ALTER TABLE "monitoring_balance" DROP COLUMN "citreaBalance"`);
13+
}
14+
}

src/config/config.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,11 @@ export class Configuration {
154154
chainId: +(process.env.ROOTSTOCK_CHAIN_ID ?? -1),
155155
walletSeed: process.env.ROOTSTOCK_WALLET_SEED ?? '',
156156
},
157+
citrea: {
158+
gatewayUrl: process.env.CITREA_GATEWAY_URL ?? '',
159+
chainId: +(process.env.CITREA_CHAIN_ID ?? -1),
160+
walletAddress: process.env.CITREA_WALLET_ADDRESS ?? '',
161+
},
157162
};
158163

159164
alchemy = {

src/integration/blockchain/blockchain.module.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { Module } from '@nestjs/common';
22
import { ArbitrumModule } from './arbitrum/arbitrum.module';
33
import { BaseModule } from './base/base.module';
44
import { BitcoinModule } from './bitcoin/bitcoin.module';
5+
import { CitreaModule } from './citrea/citrea.module';
56
import { EthereumModule } from './ethereum/ethereum.module';
67
import { LightningModule } from './lightning/lightning.module';
78
import { OptimismModule } from './optimism/optimism.module';
@@ -21,6 +22,7 @@ import { UmaModule } from './uma/uma.module';
2122
PolygonModule,
2223
BaseModule,
2324
RootstockModule,
25+
CitreaModule,
2426
],
2527
controllers: [],
2628
providers: [CryptoService],
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { Config } from 'src/config/config';
2+
import { EvmUtil } from 'src/subdomains/evm/evm.util';
3+
import { AssetTransferEntity } from 'src/subdomains/master-data/asset/entities/asset-transfer.entity';
4+
import { LightningHelper } from '../lightning/lightning-helper';
5+
import { EvmTokenBalance } from '../shared/evm/dto/evm-token-balance.dto';
6+
import { EvmClient, EvmClientParams } from '../shared/evm/evm-client';
7+
8+
export class CitreaClient extends EvmClient {
9+
constructor(private readonly params: EvmClientParams) {
10+
super(params);
11+
}
12+
13+
async getNativeCoinBalance(): Promise<number> {
14+
const balance = await this.provider.getBalance(Config.blockchain.citrea.walletAddress);
15+
return LightningHelper.btcToSat(EvmUtil.fromWeiAmount(balance.toString()));
16+
}
17+
18+
async getTokenBalance(_asset: AssetTransferEntity): Promise<number> {
19+
throw new Error('Method not implemented.');
20+
}
21+
22+
async getTokenBalances(_assets: AssetTransferEntity[]): Promise<EvmTokenBalance[]> {
23+
throw new Error('Method not implemented.');
24+
}
25+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { Module } from '@nestjs/common';
2+
import { SharedModule } from 'src/shared/shared.module';
3+
import { AlchemyModule } from 'src/subdomains/alchemy/alchemy.module';
4+
import { EvmRegistryModule } from '../shared/evm/registry/evm-registry.module';
5+
import { CitreaService } from './citrea.service';
6+
7+
@Module({
8+
imports: [SharedModule, EvmRegistryModule, AlchemyModule],
9+
providers: [CitreaService],
10+
exports: [],
11+
})
12+
export class CitreaModule {}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { Injectable } from '@nestjs/common';
2+
import { GetConfig } from 'src/config/config';
3+
import { Blockchain } from 'src/shared/enums/blockchain.enum';
4+
import { HttpService } from 'src/shared/services/http.service';
5+
import { AlchemyService } from 'src/subdomains/alchemy/services/alchemy.service';
6+
import { EvmService } from '../shared/evm/evm.service';
7+
import { CitreaClient } from './citrea-client';
8+
9+
@Injectable()
10+
export class CitreaService extends EvmService {
11+
constructor(http: HttpService, alchemyService: AlchemyService) {
12+
const { gatewayUrl, chainId } = GetConfig().blockchain.citrea;
13+
14+
super(CitreaClient, {
15+
http: http,
16+
alchemyService,
17+
gatewayUrl,
18+
apiKey: '',
19+
chainId,
20+
});
21+
}
22+
23+
get blockchain(): Blockchain {
24+
return Blockchain.CITREA;
25+
}
26+
}

src/integration/blockchain/shared/evm/evm-client.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ export abstract class EvmClient {
2121
private readonly alchemyService: AlchemyService;
2222
private readonly chainId: number;
2323

24-
private readonly provider: ethers.providers.JsonRpcProvider;
24+
protected readonly provider: ethers.providers.JsonRpcProvider;
2525
private readonly tokens = new AsyncCache<Token>();
2626

2727
constructor(params: EvmClientParams) {

src/shared/enums/blockchain.enum.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,5 @@ export enum Blockchain {
77
POLYGON = 'polygon',
88
BASE = 'base',
99
ROOTSTOCK = 'rootstock',
10+
CITREA = 'citrea',
1011
}

src/subdomains/monitoring/entities/monitoring-balance.entity.ts

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,14 @@ import { Price } from 'src/subdomains/support/dto/price.dto';
55
import { LightningWalletTotalBalanceDto } from 'src/subdomains/user/application/dto/lightning-wallet.dto';
66
import { Column, Entity, ManyToOne } from 'typeorm';
77

8+
export interface MonitoringBlockchainBalance {
9+
onchainBalance: number;
10+
lndOnchainBalance: number;
11+
lightningBalance: number;
12+
rootstockBalance: number;
13+
citreaBalance: number;
14+
}
15+
816
@Entity('monitoring_balance')
917
export class MonitoringBalanceEntity extends IEntity {
1018
@ManyToOne(() => AssetAccountEntity, { eager: true })
@@ -22,6 +30,9 @@ export class MonitoringBalanceEntity extends IEntity {
2230
@Column({ type: 'float', default: 0 })
2331
rootstockBalance: number;
2432

33+
@Column({ type: 'float', default: 0 })
34+
citreaBalance: number;
35+
2536
@Column({ type: 'float', default: 0 })
2637
customerBalance: number;
2738

@@ -37,28 +48,27 @@ export class MonitoringBalanceEntity extends IEntity {
3748
// --- FACTORY METHODS --- //
3849

3950
static createAsBtcEntity(
40-
onchainBalance: number,
41-
lndOnchainBalance: number,
42-
lightningBalance: number,
43-
rootstockBalance: number,
51+
blockchainBalance: MonitoringBlockchainBalance,
4452
internalBalance: LightningWalletTotalBalanceDto,
4553
customerBalance: LightningWalletTotalBalanceDto,
4654
chfPrice: Price,
4755
): MonitoringBalanceEntity {
4856
const entity = new MonitoringBalanceEntity();
4957

5058
entity.asset = { id: customerBalance.assetId } as AssetAccountEntity;
51-
entity.onchainBalance = onchainBalance;
52-
entity.lndOnchainBalance = lndOnchainBalance;
53-
entity.lightningBalance = lightningBalance;
54-
entity.rootstockBalance = rootstockBalance;
59+
entity.onchainBalance = blockchainBalance.onchainBalance;
60+
entity.lndOnchainBalance = blockchainBalance.lndOnchainBalance;
61+
entity.lightningBalance = blockchainBalance.lightningBalance;
62+
entity.rootstockBalance = blockchainBalance.rootstockBalance;
63+
entity.citreaBalance = blockchainBalance.citreaBalance;
5564
entity.customerBalance = customerBalance.totalBalance;
5665

5766
entity.ldsBalance =
5867
entity.onchainBalance +
5968
entity.lndOnchainBalance +
6069
entity.lightningBalance +
6170
entity.rootstockBalance +
71+
entity.citreaBalance +
6272
internalBalance.totalBalance -
6373
entity.customerBalance;
6474

src/subdomains/monitoring/services/monitoring.service.ts

Lines changed: 16 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { Injectable, InternalServerErrorException, OnModuleInit } from '@nestjs/common';
22
import { BitcoinClient } from 'src/integration/blockchain/bitcoin/bitcoin-client';
33
import { BitcoinService } from 'src/integration/blockchain/bitcoin/bitcoin.service';
4+
import { CitreaClient } from 'src/integration/blockchain/citrea/citrea-client';
45
import { LndChannelDto } from 'src/integration/blockchain/lightning/dto/lnd.dto';
56
import { LightningClient } from 'src/integration/blockchain/lightning/lightning-client';
67
import { LightningService } from 'src/integration/blockchain/lightning/services/lightning.service';
@@ -12,7 +13,7 @@ import { QueueHandler } from 'src/shared/utils/queue-handler';
1213
import { AssetService } from 'src/subdomains/master-data/asset/services/asset.service';
1314
import { CoinGeckoService } from 'src/subdomains/pricing/services/coingecko.service';
1415
import { LightningWalletTotalBalanceDto } from 'src/subdomains/user/application/dto/lightning-wallet.dto';
15-
import { MonitoringBalanceEntity } from '../entities/monitoring-balance.entity';
16+
import { MonitoringBalanceEntity, MonitoringBlockchainBalance } from '../entities/monitoring-balance.entity';
1617
import { MonitoringBalanceRepository } from '../repositories/monitoring-balance.repository';
1718
import { MonitoringRepository } from '../repositories/monitoring.repository';
1819

@@ -23,6 +24,7 @@ export class MonitoringService implements OnModuleInit {
2324
private readonly bitcoinClient: BitcoinClient;
2425
private readonly lightningClient: LightningClient;
2526
private rootstockClient: RootstockClient;
27+
private citreaClient: CitreaClient;
2628

2729
private readonly processBalancesQueue: QueueHandler;
2830

@@ -43,6 +45,7 @@ export class MonitoringService implements OnModuleInit {
4345

4446
onModuleInit() {
4547
this.rootstockClient = this.evmRegistryService.getClient(Blockchain.ROOTSTOCK) as RootstockClient;
48+
this.citreaClient = this.evmRegistryService.getClient(Blockchain.CITREA) as CitreaClient;
4649
}
4750

4851
// --- LIGHTNING --- //
@@ -66,10 +69,7 @@ export class MonitoringService implements OnModuleInit {
6669
customerBalances: LightningWalletTotalBalanceDto[],
6770
): Promise<void> {
6871
try {
69-
const onchainBalance = await this.getOnchainBalance();
70-
const lndOnchainBalance = await this.getLndOnchainBalance();
71-
const lightningBalance = await this.getLightningBalance();
72-
const rootstockBalance = await this.getRootstockBalance();
72+
const blockchainBalance = await this.getBlockchainBalances();
7373

7474
const btcAccountAsset = await this.assetService.getBtcAccountAssetOrThrow();
7575
const btcAccountAssetId = btcAccountAsset.id;
@@ -86,14 +86,7 @@ export class MonitoringService implements OnModuleInit {
8686

8787
const customerFiatBalances = customerBalances.filter((b) => b.assetId !== btcAccountAssetId);
8888

89-
await this.processBtcBalance(
90-
onchainBalance,
91-
lndOnchainBalance,
92-
lightningBalance,
93-
rootstockBalance,
94-
internalBtcBalance,
95-
customerBtcBalance,
96-
);
89+
await this.processBtcBalance(blockchainBalance, internalBtcBalance, customerBtcBalance);
9790
await this.processFiatBalances(customerFiatBalances);
9891
} catch (e) {
9992
this.logger.error('Error while processing balances', e);
@@ -119,21 +112,15 @@ export class MonitoringService implements OnModuleInit {
119112
}
120113

121114
private async processBtcBalance(
122-
onchainBalance: number,
123-
lndOnchainBalance: number,
124-
lightningBalance: number,
125-
rootstockBalance: number,
115+
blockchainBalance: MonitoringBlockchainBalance,
126116
internalBtcBalance: LightningWalletTotalBalanceDto,
127117
customerBtcBalance: LightningWalletTotalBalanceDto,
128118
) {
129119
const chfPrice = await this.coinGeckoService.getPrice('BTC', 'CHF');
130120
if (!chfPrice.isValid) throw new InternalServerErrorException(`Invalid price from BTC to CHF`);
131121

132122
const btcMonitoringEntity = MonitoringBalanceEntity.createAsBtcEntity(
133-
onchainBalance,
134-
lndOnchainBalance,
135-
lightningBalance,
136-
rootstockBalance,
123+
blockchainBalance,
137124
internalBtcBalance,
138125
customerBtcBalance,
139126
chfPrice,
@@ -169,20 +156,14 @@ export class MonitoringService implements OnModuleInit {
169156
return balance;
170157
}
171158

172-
private async getOnchainBalance(): Promise<number> {
173-
return this.bitcoinClient.getWalletBalance();
174-
}
175-
176-
private async getLndOnchainBalance(): Promise<number> {
177-
return this.lightningClient.getLndConfirmedWalletBalance();
178-
}
179-
180-
private async getLightningBalance(): Promise<number> {
181-
return this.lightningClient.getLndLightningBalance();
182-
}
183-
184-
private async getRootstockBalance(): Promise<number> {
185-
return this.rootstockClient.getNativeCoinBalance();
159+
private async getBlockchainBalances(): Promise<MonitoringBlockchainBalance> {
160+
return {
161+
onchainBalance: await this.bitcoinClient.getWalletBalance(),
162+
lndOnchainBalance: await this.lightningClient.getLndConfirmedWalletBalance(),
163+
lightningBalance: await this.lightningClient.getLndLightningBalance(),
164+
rootstockBalance: await this.rootstockClient.getNativeCoinBalance(),
165+
citreaBalance: await this.citreaClient.getNativeCoinBalance(),
166+
};
186167
}
187168

188169
private async getChannels(): Promise<LndChannelDto[]> {

0 commit comments

Comments
 (0)