Skip to content

Commit 748afc2

Browse files
authored
feat(core): update exporter metrics (#4617)
* feat(core): update exporter metrics * fix: avoid multiple queries to db for the same account * fix: logger level for getRealAssetsVersusLiabilities * fix: real assets vs liabilities calc
1 parent 47a816e commit 748afc2

File tree

2 files changed

+150
-14
lines changed

2 files changed

+150
-14
lines changed

core/api/src/servers/exporter.ts

Lines changed: 128 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import {
99
SECS_PER_5_MINS,
1010
} from "@/config"
1111

12-
import { Lightning, OnChain } from "@/app"
12+
import { Lightning, OnChain, Prices } from "@/app"
1313

1414
import { toSeconds } from "@/domain/primitives"
1515

@@ -32,6 +32,7 @@ import { activateLndHealthCheck } from "@/services/lnd/health"
3232
import { ledgerAdmin, setupMongoConnection } from "@/services/mongodb"
3333

3434
import { timeoutWithCancel } from "@/utils"
35+
import { displayAmountFromNumber, UsdDisplayCurrency } from "@/domain/fiat"
3536

3637
const TIMEOUT_WALLET_BALANCE = 30000
3738

@@ -40,12 +41,35 @@ const logger = baseLogger.child({ module: "exporter" })
4041
const prefix = "galoy"
4142

4243
const main = async () => {
43-
const { getLiabilitiesBalance, getLndBalance, getBitcoindBalance, getOnChainBalance } =
44-
ledgerAdmin
44+
const { getLndBalance, getBitcoindBalance, getOnChainBalance } = ledgerAdmin
45+
46+
createGauge({
47+
name: "assets",
48+
description: "how much money (BTC) is on books",
49+
collect: async () => {
50+
const getDealerBalance = async (getId: () => Promise<WalletId>) => {
51+
const walletId = await getId()
52+
return getWalletBalance(walletId)
53+
}
54+
55+
const btcAssets = await ledgerAdmin.getAssetsBalance()
56+
const dealerBtcLiabilities = await getDealerBalance(getDealerBtcWalletId)
57+
58+
// Dealer BTC liabilities must be deducted from assets because
59+
// Stablesats deposits and withdrawals are processed directly through Bria.
60+
return Math.abs(btcAssets) - dealerBtcLiabilities
61+
},
62+
})
63+
4564
createGauge({
4665
name: "liabilities",
47-
description: "how much money customers has",
48-
collect: getLiabilitiesBalance,
66+
description: "how much money (BTC) customers has",
67+
collect: async () => {
68+
const liabilities = await getUserLiabilities()
69+
if (liabilities instanceof Error) return 0
70+
71+
return liabilities
72+
},
4973
})
5074

5175
createGauge({
@@ -109,6 +133,12 @@ const main = async () => {
109133
},
110134
})
111135

136+
createGauge({
137+
name: "realAssetsVsLiabilities",
138+
description: "do we have enough Bitcoin to cover users' liabilities",
139+
collect: getRealAssetsVersusLiabilities,
140+
})
141+
112142
createGauge({
113143
name: "assetsEqLiabilities",
114144
description: "do we have a balanced book",
@@ -271,14 +301,31 @@ const createWalletGauge = ({
271301
})
272302
}
273303

304+
const inProgressBalanceQueries = new Map<string, Promise<number>>()
305+
274306
const getWalletBalance = async (walletId: WalletId): Promise<number> => {
275-
const walletBalance = await LedgerService().getWalletBalance(walletId)
276-
if (walletBalance instanceof Error) {
277-
logger.warn({ walletId, walletBalance }, "impossible to get balance")
278-
return 0
307+
const inProgressKey = `wallet-${walletId}`
308+
309+
const inProgress = inProgressBalanceQueries.get(inProgressKey)
310+
if (inProgress) {
311+
return inProgress
279312
}
280313

281-
return walletBalance
314+
const balancePromise = (async () => {
315+
try {
316+
const walletBalance = await LedgerService().getWalletBalance(walletId)
317+
if (walletBalance instanceof Error) {
318+
logger.warn({ walletId, walletBalance }, "impossible to get balance")
319+
return 0
320+
}
321+
return walletBalance
322+
} finally {
323+
inProgressBalanceQueries.delete(inProgressKey)
324+
}
325+
})()
326+
327+
inProgressBalanceQueries.set(inProgressKey, balancePromise)
328+
return balancePromise
282329
}
283330

284331
const createColdStorageWalletGauge = () => {
@@ -311,6 +358,77 @@ const getAssetsLiabilitiesDifference = async () => {
311358
return assets + liabilities
312359
}
313360

361+
const getUserLiabilities = async () => {
362+
const getDealerBalance = async (getId: () => Promise<WalletId>) => {
363+
const walletId = await getId()
364+
return getWalletBalance(walletId)
365+
}
366+
367+
const btcLiabilities = await ledgerAdmin.getLiabilitiesBalance()
368+
const dealerBtcLiabilities = await getDealerBalance(getDealerBtcWalletId)
369+
370+
// Dealer BTC liabilities must be deducted from liabilities because
371+
// Stablesats deposits and withdrawals are processed directly through Bria.
372+
const customerBtcLiabilities = btcLiabilities - dealerBtcLiabilities
373+
374+
const dealerUsdLiabilities = await getDealerBalance(getDealerUsdWalletId)
375+
logger.info(
376+
{
377+
btcLiabilities,
378+
dealerBtcLiabilities,
379+
customerBtcLiabilities,
380+
dealerUsdLiabilities,
381+
},
382+
"getUserLiabilities balances",
383+
)
384+
const dealerUsdLiabilitiesDisplay = displayAmountFromNumber({
385+
amount: Math.abs(dealerUsdLiabilities),
386+
currency: UsdDisplayCurrency,
387+
})
388+
if (dealerUsdLiabilitiesDisplay instanceof Error) return dealerUsdLiabilitiesDisplay
389+
390+
const dealerUsdLiabilitiesInSatsAmount = await Prices.estimateWalletsAmounts({
391+
amount: Number(dealerUsdLiabilitiesDisplay.displayInMajor),
392+
currency: UsdDisplayCurrency,
393+
})
394+
logger.info(
395+
{
396+
mayor: dealerUsdLiabilitiesDisplay.displayInMajor,
397+
currency: UsdDisplayCurrency,
398+
dealerUsdLiabilitiesInSatsAmount,
399+
},
400+
"getUserLiabilities usd balances",
401+
)
402+
if (dealerUsdLiabilitiesInSatsAmount instanceof Error)
403+
return dealerUsdLiabilitiesInSatsAmount
404+
405+
return (
406+
customerBtcLiabilities + Number(dealerUsdLiabilitiesInSatsAmount.btcSatAmount.amount)
407+
)
408+
}
409+
410+
const getRealAssetsVersusLiabilities = async () => {
411+
const [liabilitiesBalance, lndBalance, coldStorage, hotBalance] = await Promise.all([
412+
getUserLiabilities(),
413+
Lightning.getTotalBalance(),
414+
OnChain.getColdBalance(),
415+
OnChain.getHotBalance(),
416+
])
417+
418+
const liabilities = liabilitiesBalance instanceof Error ? 0 : liabilitiesBalance
419+
const lnd = lndBalance instanceof Error ? 0 : lndBalance
420+
const briaHot = hotBalance instanceof Error ? 0 : Number(hotBalance.amount)
421+
const briaCold = coldStorage instanceof Error ? 0 : Number(coldStorage.amount)
422+
423+
logger.info(
424+
{ liabilities, lnd, briaHot, briaCold },
425+
"getRealAssetsVersusLiabilities balances",
426+
)
427+
428+
// if it is a negative value then it must match with exchange stablesats balance
429+
return lnd + briaCold + briaHot - liabilities
430+
}
431+
314432
export const getBookingVersusRealWorldAssets = async () => {
315433
const [lightning, bitcoin, onChain, lndBalance, coldStorage, hotBalance] =
316434
await Promise.all([

core/api/src/services/ledger/admin-legacy.ts

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,30 @@ import {
1616
UnknownLedgerError,
1717
} from "@/domain/ledger"
1818

19+
const inProgressQueries = new Map<string, Promise<number>>()
20+
1921
const getWalletBalance = async (account: string, query = {}) => {
2022
const params = { account, currency: "BTC", ...query }
21-
const { balance } = await MainBookAdmin.balance(params, {
22-
readPreference: "secondaryPreferred",
23-
})
24-
return balance
23+
const inProgressKey = `${account}-${JSON.stringify(params)}`
24+
25+
const inProgress = inProgressQueries.get(inProgressKey)
26+
if (inProgress) {
27+
return inProgress
28+
}
29+
30+
const balancePromise = (async () => {
31+
try {
32+
const { balance } = await MainBookAdmin.balance(params, {
33+
readPreference: "secondaryPreferred",
34+
})
35+
return balance
36+
} finally {
37+
inProgressQueries.delete(inProgressKey)
38+
}
39+
})()
40+
41+
inProgressQueries.set(inProgressKey, balancePromise)
42+
return balancePromise
2543
}
2644

2745
export const getAssetsBalance = (endDate?: Date) =>

0 commit comments

Comments
 (0)