-
Notifications
You must be signed in to change notification settings - Fork 70
/
Copy pathquorum.ts
103 lines (91 loc) · 2.43 KB
/
quorum.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
import type { Chain } from "@wormhole-foundation/sdk";
import { api, toChain, wormhole } from "@wormhole-foundation/sdk";
import algorand from "@wormhole-foundation/sdk/algorand";
import cosmwasm from "@wormhole-foundation/sdk/cosmwasm";
import evm from "@wormhole-foundation/sdk/evm";
import solana from "@wormhole-foundation/sdk/solana";
type Stats = {
max: bigint;
min: bigint;
quorum: bigint;
mean: bigint;
delta: bigint;
};
type Status = {
address: string;
chainId: number;
height: bigint;
};
type HeightsByChain = Record<string, Record<string, bigint>>;
const skipChains = [
"Pythnet",
"Evmos",
"Osmosis",
"Kujira",
"Kaia",
"Wormchain",
"Near",
"Sui",
"Xpla",
"Aptos",
"Cosmoshub",
];
(async function () {
const wh = await wormhole("Mainnet", [evm, solana, algorand, cosmwasm]);
const hbc = await getHeartbeats(wh.config.api);
for (const [chain, heights] of Object.entries(hbc)) {
if (skipChains.includes(chain)) continue;
try {
const ctx = wh.getChain(chain as Chain);
// ..
await ctx.getRpc();
const chainLatest = await ctx.getLatestBlock();
const stats = getStats(Object.values(heights));
console.log(chain, BigInt(chainLatest) - stats.quorum);
} catch (e) {
console.error(chain, e);
}
}
})();
async function getHeartbeats(apiUrl: string): Promise<HeightsByChain> {
const hbs = await api.getGuardianHeartbeats(apiUrl);
const nets = hbs!
.map((hb) => {
return hb.rawHeartbeat.networks
.map((n) => {
return {
address: hb.verifiedGuardianAddr,
chainId: n.id,
height: BigInt(n.height),
} as Status;
})
.flat();
})
.flat();
const byChain: HeightsByChain = {};
for (const status of nets) {
// Jump
if (status.address === "0x58CC3AE5C097b213cE3c81979e1B9f9570746AA5") continue;
let chain;
try {
chain = toChain(status.chainId);
} catch {
continue;
}
if (!(chain in byChain)) byChain[chain] = {};
byChain[chain]![status.address] = status.height;
}
return byChain;
}
function getStats(vals: bigint[]): Stats {
vals.sort();
const max = vals[vals.length - 1]!;
const min = vals[0]!;
let sum = 0n;
for (const v of vals) {
sum += v;
}
const mean = sum / BigInt(vals.length);
const quorum = vals[Math.floor(vals.length / 3) * 2]!;
return { max: max!, min: min!, quorum, mean, delta: max! - min! };
}