-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathmain.ts
168 lines (142 loc) · 5.17 KB
/
main.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
import { CosmWasmClient } from "npm:@cosmjs/cosmwasm-stargate";
import { Coin } from "npm:@cosmjs/stargate";
import { Decimal } from "npm:@cosmjs/math";
import { toUtf8 } from "npm:@cosmjs/encoding";
import { HttpBatchClient, Tendermint34Client } from "npm:@cosmjs/tendermint-rpc";
import * as promclient from "npm:prom-client";
import express from "npm:[email protected]";
import settings from "./settings.ts";
import { communityPoolFunds, getContractUsage, getIbcChannels, totalSupply } from "./queries.ts";
function printableCoin(coin: Coin): string {
if (coin.denom?.startsWith("u")) {
const ticker = coin.denom.slice(1).toUpperCase();
return Decimal.fromAtomics(coin.amount ?? "0", 6).toString() + " " + ticker;
} else {
return coin.amount + coin.denom;
}
}
function mapChannelToDescription(chainId: string, channelId: string): string {
// Default to a unknown if the channel is not found
return settings[chainId].mappingChannels[channelId] || "unknown-proxy";
}
export interface Account {
readonly name: string;
readonly address: string;
readonly denom: "unois";
}
function debugLog(msg: string) {
Deno.stderr.writeSync(toUtf8(`[${new Date().toISOString()}] ` + msg + "\n"));
}
function errorLog(msg: string) {
Deno.stderr.writeSync(toUtf8(`[${new Date().toISOString()}] ` + msg + "\n"));
}
if (import.meta.main) {
const app = express();
const balances = new Map<string, string>();
const balancesGauge = new promclient.Gauge({
name: "balances",
help: "Account balances in NOIS",
labelNames: ["account", "rpcEndpoint", "chainId"] as const,
});
const customerUsageGauge = new promclient.Gauge({
name: "customer_usage",
help: "Usage data per customer chain",
labelNames: [
"channel",
"rpcEndpoint",
"chainId",
"description",
"address",
] as const,
});
// Updates all gauges with the current balances
const gaugify = () => {
for (const [key, val] of balances.entries()) {
balancesGauge.set({ account: key, rpcEndpoint, chainId }, parseInt(val, 10) / 1_000_000);
}
};
// deno-lint-ignore no-explicit-any
app.get("/metrics", (_req: any, res: any) => {
gaugify();
updateContractUsage();
res.set("Content-Type", promclient.register.contentType);
promclient.register.metrics().then((metrics) => res.end(metrics));
});
const rpcEndpoint = Deno.env.get("RPC_ENDPOINT");
if (!rpcEndpoint) {
console.error('RPC_ENDPOINT environment variable is not defined');
Deno.exit(1); // Exit the process with a non-zero code to indicate failure
}
const httpBatch = new HttpBatchClient(rpcEndpoint);
const tmClient = await Tendermint34Client.create(httpBatch);
const client = await CosmWasmClient.create(tmClient);
const chainId = await client.getChainId();
debugLog(`Connected to ${chainId} via ${rpcEndpoint}`);
const accounts = settings[chainId].accounts;
const updateAccounts = () => {
// debugLog("Getting balances ...");
for (const account of accounts) {
client.getBalance(account.address, account.denom).then((balance) => {
debugLog(`${account.name}: ${printableCoin(balance)}`);
balances.set(account.name, balance.amount);
}, (err) => errorLog(err.toString()));
}
};
const updateTotalSupply = () => {
totalSupply(tmClient, "unois").then((balance) => {
const exportAccountName = "total supply";
debugLog(`${exportAccountName}: ${printableCoin(balance)}`);
balances.set(exportAccountName, balance.amount);
}, (err) => errorLog(err.toString()));
};
const updateCommunityPool = () => {
communityPoolFunds(tmClient, "unois").then(
(balance) => {
const exportAccountName = "community pool";
debugLog(`${exportAccountName}: ${printableCoin(balance)}`);
balances.set(exportAccountName, balance.amount);
},
(err) => errorLog(err.toString())
);
};
const updateContractUsage = async () => {
try {
const contractState = await getContractUsage(
tmClient,
settings[chainId].gatewayAddr
);
// get an array of channels infos
const channelsInfo = await getIbcChannels(tmClient);
// array of customer data
for (const customer of contractState.customers) {
const description = mapChannelToDescription(chainId, customer.channel_id);
const connection = channelsInfo.filter(channel => channel.channelId === customer.channel_id);
customerUsageGauge.set(
{
channel: customer.channel_id,
rpcEndpoint: rpcEndpoint,
chainId: chainId,
description: description,
address: connection[0].counterparty?.portId?.slice(5)
},
customer.requested_beacons
);
}
} catch (err) {
errorLog(err.toString());
}
};
// Initial call
updateAccounts();
updateTotalSupply();
updateCommunityPool();
updateContractUsage();
setInterval(updateAccounts, 10_000);
setInterval(updateTotalSupply, 45_000);
setInterval(updateCommunityPool, 100_000);
setInterval(updateContractUsage, 60_000);
const port = 3000;
app.listen(port, function () {
debugLog(`Listening on port ${port} ...`);
});
}