Skip to content

Commit

Permalink
dashboard: move CollapsibleSection into component
Browse files Browse the repository at this point in the history
cloud_functions: use bignumber to handle bigint

Signed-off-by: bingyuyap <bingyu.yap.21@gmail.com>
  • Loading branch information
bingyuyap authored and evan-gray committed Apr 29, 2024
1 parent e373ce1 commit b235c0f
Show file tree
Hide file tree
Showing 11 changed files with 170 additions and 132 deletions.
25 changes: 16 additions & 9 deletions cloud_functions/src/computeNTTRateLimits.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,35 +65,42 @@ async function computeNTTRateLimits_(
tokenDecimals = await getEvmTokenDecimals(rpcEndpoint, managerContract);
}

const outboundCapacity = await ntt.getCurrentOutboundCapacity();
const normalizedOutboundCapacity = outboundCapacity / BigInt(10 ** tokenDecimals);

const inboundChains = NTT_SUPPORTED_CHAINS(network, token).filter(
(inboundChain) => inboundChain !== chain
);

let totalInboundCapacity = BigInt(0);
let totalInboundCapacity = 0n;
const inboundRateLimits = await Promise.all(
inboundChains.map(async (inboundChain): Promise<NTTRateLimit> => {
const inboundCapacity = await ntt.getCurrentInboundCapacity(inboundChain);
const normalizedInboundCapacity = inboundCapacity / BigInt(10 ** tokenDecimals);
totalInboundCapacity += normalizedInboundCapacity;
totalInboundCapacity += inboundCapacity;

return {
tokenName: token,
srcChain: chainToChainId(inboundChain),
destChain: chainToChainId(chain),
amount: normalizedInboundCapacity.toString(),
amount: {
amount: inboundCapacity.toString(),
decimals: tokenDecimals,
},
};
})
);

const outboundCapacity = await ntt.getCurrentOutboundCapacity();

return {
tokenName: token,
srcChain: chainToChainId(chain),
amount: normalizedOutboundCapacity.toString(),
amount: {
amount: outboundCapacity.toString(),
decimals: tokenDecimals,
},
inboundCapacity: inboundRateLimits,
totalInboundCapacity: totalInboundCapacity.toString(),
totalInboundCapacity: {
amount: totalInboundCapacity.toString(),
decimals: tokenDecimals,
},
};
}

Expand Down
56 changes: 28 additions & 28 deletions cloud_functions/src/computeTotalSupplyAndLocked.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
derivePda,
getEvmTokenDecimals,
getEvmTotalSupply,
normalizeToDecimals,
} from '@wormhole-foundation/wormhole-monitor-common';
import { PublicKey } from '@solana/web3.js';
import {
Expand All @@ -25,23 +26,6 @@ const cacheBucket = storage.bucket(bucketName);
const cacheFileName = 'ntt-total-supply-and-locked.json';
const cloudStorageCache = cacheBucket.file(cacheFileName);

async function getEvmNormalizedTotalSupply(
network: Network,
token: string,
chain: Chain
): Promise<number> {
const tokenDecimals = await getEvmTokenDecimals(
rpc.rpcAddress(network, chain),
NTT_MANAGER_CONTRACT[network][token][chain]!
);
const tokenSupply = await getEvmTotalSupply(
rpc.rpcAddress(network, chain),
NTT_TOKENS[network][token][chain]!
);

return tokenSupply / 10 ** tokenDecimals;
}

async function fetchTotalSupplyAndLocked(network: Network): Promise<NTTTotalSupplyAndLockedData[]> {
const tokens = NTT_MANAGER_CONTRACT[network];
const totalSupplyVsLocked: NTTTotalSupplyAndLockedData[] = [];
Expand All @@ -51,32 +35,49 @@ async function fetchTotalSupplyAndLocked(network: Network): Promise<NTTTotalSupp
const programId = new PublicKey(NTT_MANAGER_CONTRACT[network][token].Solana!);
const pda = derivePda('config', programId);
const custody = await getCustody(rpc.rpcAddress(network, 'Solana'), pda.toBase58());
const locked = await getCustodyAmount(rpc.rpcAddress(network, 'Solana'), custody);
const custodyAmount = await getCustodyAmount(rpc.rpcAddress(network, 'Solana'), custody);

const evmTotalSupply: NTTTotalSupplyAndLockedData[] = [];
let totalSupply = 0;
let cumulativeEvmSupply = 0n;
for (const [supportedChain] of Object.entries(NTT_TOKENS[network][token])) {
if (supportedChain === 'Solana') continue;
const tokenSupplyNormalized = await getEvmNormalizedTotalSupply(
network,
token,
supportedChain as Chain
const tokenSupply = await getEvmTotalSupply(
rpc.rpcAddress(network, supportedChain as Chain),
NTT_TOKENS[network][token][supportedChain as Chain]!
);

const tokenDecimals = await getEvmTokenDecimals(
rpc.rpcAddress(network, supportedChain as Chain),
NTT_MANAGER_CONTRACT[network][token][supportedChain as Chain]!
);

evmTotalSupply.push({
tokenName: token,
chain: chainToChainId(supportedChain as Chain),
totalSupply: tokenSupplyNormalized,
totalSupply: {
amount: tokenSupply.toString(),
decimals: tokenDecimals,
},
});

totalSupply += tokenSupplyNormalized;
// Normalize to 18 decimals so prevent potential different decimals from affecting the total supply
cumulativeEvmSupply += normalizeToDecimals(
{
amount: tokenSupply.toString(),
decimals: tokenDecimals,
},
18
);
}

totalSupplyVsLocked.push({
chain: chainToChainId('Solana'),
tokenName: token,
amountLocked: locked,
totalSupply,
amountLocked: custodyAmount,
totalSupply: {
amount: cumulativeEvmSupply.toString(),
decimals: 18,
},
evmTotalSupply,
});
}
Expand All @@ -98,7 +99,6 @@ export async function computeTotalSupplyAndLocked(req: any, res: any) {
const network = assertEnvironmentVariable('NETWORK') as Network;
const totalSupplyAndLocked = await fetchTotalSupplyAndLocked(network);
await cloudStorageCache.save(JSON.stringify(totalSupplyAndLocked));

res.status(200).send('Total supply and locked saved');
} catch (e) {
console.error(e);
Expand Down
7 changes: 5 additions & 2 deletions cloud_functions/src/getNTTRateLimits.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { chainIdToChain } from '@wormhole-foundation/sdk-base';
import {
NTTRateLimit,
assertEnvironmentVariable,
normalizeToDecimals,
} from '@wormhole-foundation/wormhole-monitor-common';
import { Gauge, register } from 'prom-client';

Expand Down Expand Up @@ -58,7 +59,9 @@ async function handlePrometheusMetrics(rateLimits: NTTRateLimit[]) {
network,
product: PRODUCT,
},
BigInt(rateLimit.amount)
// Normalize the amount to 18 decimals, Grafana should hold high precision
// It should also be able to handle the normalization to 0 decimals
normalizeToDecimals(rateLimit.amount, 18)
);

if (!rateLimit.inboundCapacity) return;
Expand All @@ -81,7 +84,7 @@ async function handlePrometheusMetrics(rateLimits: NTTRateLimit[]) {
network,
product: PRODUCT,
},
BigInt(inboundRateLimit.amount)
normalizeToDecimals(inboundRateLimit.amount, 18)
);
});
});
Expand Down
4 changes: 2 additions & 2 deletions common/src/evm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,8 @@ export async function getEvmTokenDecimals(rpc: string, contractAddress: string):
return Number(result);
}

export async function getEvmTotalSupply(rpc: string, contractAddress: string): Promise<number> {
export async function getEvmTotalSupply(rpc: string, contractAddress: string): Promise<bigint> {
const methodId = getMethodId('totalSupply()');
const result = await callContractMethod(rpc, contractAddress, methodId);
return Number(result);
return BigInt(result);
}
15 changes: 11 additions & 4 deletions common/src/solana.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@ import {
MessageV0,
PublicKeyInitData,
PublicKey,
} from '@solana/web3.js'; // NOTE: types only for bundling size
import { decode } from 'bs58';
} from '@solana/web3.js';
import axios from 'axios';
import { decode } from 'bs58';
import { encoding } from '@wormhole-foundation/sdk-base';
import { TokenAmount } from './types';

export const isLegacyMessage = (message: Message | MessageV0): message is Message => {
return message.version === 'legacy';
Expand Down Expand Up @@ -72,12 +73,18 @@ export async function getCustody(rpcUrl: string, programAddress: string): Promis
return pubkey.toString();
}

export async function getCustodyAmount(rpcUrl: string, programAddress: string): Promise<number> {
export async function getCustodyAmount(
rpcUrl: string,
programAddress: string
): Promise<TokenAmount> {
const accountInfo = await makeRpcCall(rpcUrl, 'getAccountInfo', [programAddress], 'jsonParsed');
if (!accountInfo.value?.data?.parsed?.info?.tokenAmount?.uiAmount) {
throw new Error('Custody amount not found or missing data');
}
return Number(accountInfo.value.data.parsed.info.tokenAmount.uiAmount);
return {
amount: accountInfo.value.data.parsed.info.tokenAmount.amount,
decimals: accountInfo.value.data.parsed.info.tokenAmount.decimals,
};
}

// Helper function to make JSON-RPC requests
Expand Down
34 changes: 30 additions & 4 deletions common/src/types.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,44 @@
import { ChainId } from '@wormhole-foundation/sdk-base';

export type TokenAmount = {
amount: string;
decimals: number;
};

export function normalizeToDecimals(tokenAmount: TokenAmount, targetDecimals: number): bigint {
const { amount, decimals } = tokenAmount;
const bigIntAmount = BigInt(amount);
let normalizedAmount: bigint;

if (decimals < targetDecimals) {
// If less decimals, multiply to shift the decimal point to the right
const factor = BigInt(10 ** (targetDecimals - decimals));
normalizedAmount = bigIntAmount * factor;
} else if (decimals > targetDecimals) {
// If more decimals, divide to shift the decimal point to the left
const factor = BigInt(10 ** (decimals - targetDecimals));
normalizedAmount = bigIntAmount / factor;
} else {
normalizedAmount = bigIntAmount;
}

return normalizedAmount;
}

export type NTTTotalSupplyAndLockedData = {
tokenName: string;
chain: ChainId;
amountLocked?: Number;
totalSupply: Number;
// this is bigint but for the sake of precision we are using string
amountLocked?: TokenAmount;
totalSupply?: TokenAmount;
evmTotalSupply?: NTTTotalSupplyAndLockedData[];
};

export type NTTRateLimit = {
tokenName: string;
srcChain?: ChainId;
destChain?: ChainId;
amount?: string;
totalInboundCapacity?: string;
amount?: TokenAmount;
totalInboundCapacity?: TokenAmount;
inboundCapacity?: NTTRateLimit[];
};
71 changes: 5 additions & 66 deletions dashboard/src/components/NTTMetrics.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { Box, Divider } from '@mui/material';
import { Divider } from '@mui/material';
import { useNetworkContext } from '../contexts/NetworkContext';
import CollapsibleSection from './CollapsibleSection';
import { LookerDashboard } from './LookerDashboard';
import { NTTRateLimits } from './NTTRateLimits';
import { NTTTotalSupplyAndLocked } from './NTTTotalSupplyAndLocked';
Expand All @@ -16,39 +15,9 @@ function NTTMetrics() {
hasTabs
/>
<Divider />
<CollapsibleSection
defaultExpanded={false}
header={
<Box
sx={{
display: 'flex',
alignItems: 'center',
paddingRight: 1,
}}
>
<Box>Rate Limit Capacity</Box>
</Box>
}
>
<NTTRateLimits />
</CollapsibleSection>
<NTTRateLimits />
<Divider />
<CollapsibleSection
defaultExpanded={false}
header={
<Box
sx={{
display: 'flex',
alignItems: 'center',
paddingRight: 1,
}}
>
<Box>Total Supply and Locked</Box>
</Box>
}
>
<NTTTotalSupplyAndLocked />
</CollapsibleSection>
<NTTTotalSupplyAndLocked />
</>
);
}
Expand All @@ -60,39 +29,9 @@ function NTTMetrics() {
src="https://lookerstudio.google.com/embed/reporting/a47057a8-15a0-4cc7-8086-eb00f5d09d2a/page/SPpuD"
/>
<Divider />
<CollapsibleSection
defaultExpanded={false}
header={
<Box
sx={{
display: 'flex',
alignItems: 'center',
paddingRight: 1,
}}
>
<Box>Rate Limit Capacity</Box>
</Box>
}
>
<NTTRateLimits />
</CollapsibleSection>
<NTTRateLimits />
<Divider />
<CollapsibleSection
defaultExpanded={false}
header={
<Box
sx={{
display: 'flex',
alignItems: 'center',
paddingRight: 1,
}}
>
<Box>Total Supply and Locked</Box>
</Box>
}
>
<NTTTotalSupplyAndLocked />
</CollapsibleSection>
<NTTTotalSupplyAndLocked />
</>
);
}
Expand Down
Loading

0 comments on commit b235c0f

Please sign in to comment.