wormhole/bridge_ui/src/components/Stats/Charts/utils.tsx

213 lines
6.0 KiB
TypeScript

import { NotionalTVLCumulative } from "../../../hooks/useCumulativeTVL";
import { NotionalTransferredFrom } from "../../../hooks/useNotionalTransferred";
import { TimeFrame } from "./TimeFrame";
import { DateTime } from "luxon";
import { Totals } from "../../../hooks/useTransactionTotals";
import {
ChainInfo,
CHAINS_BY_ID,
VAA_EMITTER_ADDRESSES,
} from "../../../utils/consts";
import { NotionalTVL } from "../../../hooks/useTVL";
import { ChainId } from "@certusone/wormhole-sdk";
export const formatTVL = (tvl: number) => {
const [divisor, unit, fractionDigits] =
tvl < 1e3
? [1, "", 0]
: tvl < 1e6
? [1e3, "K", 0]
: tvl < 1e9
? [1e6, "M", 0]
: [1e9, "B", 2];
return `$${(tvl / divisor).toFixed(fractionDigits)} ${unit}`;
};
export const formatDate = (date: Date) => {
return date.toLocaleString("en-US", {
day: "numeric",
month: "long",
year: "numeric",
timeZone: "UTC",
});
};
export const formatTickDay = (date: Date) => {
return date.toLocaleString("en-US", {
day: "numeric",
month: "short",
year: "numeric",
timeZone: "UTC",
});
};
export const formatTickMonth = (date: Date) => {
return date.toLocaleString("en-US", {
month: "short",
year: "numeric",
timeZone: "UTC",
});
};
export const formatTransactionCount = (transactionCount: number) => {
return transactionCount.toLocaleString("en-US");
};
export const renderLegendText = (value: any) => {
return <span style={{ color: "white", margin: "8px" }}>{value}</span>;
};
export const getStartDate = (timeFrame: TimeFrame) => {
return timeFrame.duration
? DateTime.now().toUTC().minus(timeFrame.duration).toJSDate()
: undefined;
};
export interface CumulativeTVLChartData {
date: Date;
totalTVL: number;
tvlByChain: {
[chainId: string]: number;
};
}
export const createCumulativeTVLChartData = (
cumulativeTVL: NotionalTVLCumulative,
timeFrame: TimeFrame
) => {
const startDate = getStartDate(timeFrame);
return Object.entries(cumulativeTVL.DailyLocked)
.reduce<CumulativeTVLChartData[]>(
(chartData, [dateString, chainsAssets]) => {
const date = new Date(dateString);
if (!startDate || date >= startDate) {
const data: CumulativeTVLChartData = {
date: date,
totalTVL: 0,
tvlByChain: {},
};
Object.entries(chainsAssets).forEach(([chainId, lockedAssets]) => {
const notional = lockedAssets["*"].Notional;
if (chainId === "*") {
data.totalTVL = notional;
} else {
data.tvlByChain[chainId] = notional;
}
});
chartData.push(data);
}
return chartData;
},
[]
)
.sort((a, z) => a.date.getTime() - z.date.getTime());
};
export interface TransferChartData {
date: Date;
totalTransferred: number;
transferredByChain: {
[chainId: string]: number;
};
}
export const createCumulativeTransferChartData = (
notionalTransferredFrom: NotionalTransferredFrom,
timeFrame: TimeFrame
) => {
const startDate = getStartDate(timeFrame);
return Object.keys(notionalTransferredFrom.Daily)
.sort()
.reduce<TransferChartData[]>((chartData, dateString) => {
const transferFromData = notionalTransferredFrom.Daily[dateString];
const data: TransferChartData = {
date: new Date(dateString),
totalTransferred: 0,
transferredByChain: {},
};
Object.entries(transferFromData).forEach(([chainId, amount]) => {
if (chainId === "*") {
data.totalTransferred =
(chartData[chartData.length - 1]?.totalTransferred || 0) + amount;
} else {
data.transferredByChain[chainId] =
(chartData[chartData.length - 1]?.transferredByChain[chainId] ||
0) + amount;
}
});
chartData.push(data);
return chartData;
}, [])
.filter((value) => !startDate || startDate <= value.date);
};
export interface TransactionData {
date: Date;
totalTransactions: number;
transactionsByChain: {
[chainId: string]: number;
};
}
export const createCumulativeTransactionData = (
totals: Totals,
timeFrame: TimeFrame
) => {
const startDate = getStartDate(timeFrame);
return Object.keys(totals.DailyTotals)
.sort()
.reduce<TransactionData[]>((chartData, dateString) => {
const groupByKeys = totals.DailyTotals[dateString];
const prevData = chartData[chartData.length - 1];
const data: TransactionData = {
date: new Date(dateString),
totalTransactions: prevData?.totalTransactions || 0,
transactionsByChain: {},
};
VAA_EMITTER_ADDRESSES.forEach((address) => {
const count = groupByKeys[address] || 0;
data.totalTransactions += count;
const chainId = address.slice(0, address.indexOf(":"));
if (data.transactionsByChain[chainId] === undefined) {
data.transactionsByChain[chainId] =
prevData?.transactionsByChain[chainId] || 0;
}
data.transactionsByChain[chainId] += count;
});
chartData.push(data);
return chartData;
}, [])
.filter((value) => !startDate || startDate <= value.date);
};
export interface ChainTVLChartData {
chainInfo: ChainInfo;
tvl: number;
tvlRatio: number;
}
export const createChainTVLChartData = (tvl: NotionalTVL) => {
let maxTVL = 0;
const chainTVLs = Object.entries(tvl.AllTime)
.reduce<ChainTVLChartData[]>((chartData, [chainId, assets]) => {
const chainInfo = CHAINS_BY_ID[+chainId as ChainId];
if (chainInfo !== undefined) {
const tvl = assets["*"].Notional;
chartData.push({
chainInfo: chainInfo,
tvl: tvl,
tvlRatio: 0,
});
maxTVL = Math.max(maxTVL, tvl);
}
return chartData;
}, [])
.sort((a, z) => z.tvl - a.tvl);
if (maxTVL > 0) {
chainTVLs.forEach((chainTVL) => {
chainTVL.tvlRatio = (chainTVL.tvl / maxTVL) * 100;
});
}
return chainTVLs;
};