326 lines
9.5 KiB
TypeScript
326 lines
9.5 KiB
TypeScript
import { ChainId } from "@certusone/wormhole-sdk";
|
|
import {
|
|
Checkbox,
|
|
CircularProgress,
|
|
FormControl,
|
|
ListItemText,
|
|
makeStyles,
|
|
MenuItem,
|
|
Paper,
|
|
Select,
|
|
TextField,
|
|
Tooltip,
|
|
Typography,
|
|
withStyles,
|
|
} from "@material-ui/core";
|
|
import { InfoOutlined } from "@material-ui/icons";
|
|
import { ToggleButton, ToggleButtonGroup } from "@material-ui/lab";
|
|
import { useCallback, useMemo, useState } from "react";
|
|
import useNotionalTransferred from "../../hooks/useNotionalTransferred";
|
|
import { COLORS } from "../../muiTheme";
|
|
import { CHAINS_BY_ID } from "../../utils/consts";
|
|
import { TIME_FRAMES } from "./Charts/TimeFrame";
|
|
import {
|
|
createCumulativeTransferChartData,
|
|
createCumulativeTransactionData,
|
|
formatTransactionCount,
|
|
} from "./Charts/utils";
|
|
import VolumeAreaChart from "./Charts/VolumeAreaChart";
|
|
import VolumeStackedBarChart from "./Charts/VolumeStackedBarChart";
|
|
import VolumeLineChart from "./Charts/VolumeLineChart";
|
|
import TransactionsAreaChart from "./Charts/TransactionsAreaChart";
|
|
import TransactionsLineChart from "./Charts/TransactionsLineChart";
|
|
import useTransactionTotals from "../../hooks/useTransactionTotals";
|
|
|
|
const DISPLAY_BY_VALUES = ["Dollar", "Percent", "Transactions"];
|
|
|
|
const useStyles = makeStyles((theme) => ({
|
|
description: {
|
|
display: "flex",
|
|
alignItems: "center",
|
|
justifyContent: "space-between",
|
|
marginBottom: "16px",
|
|
[theme.breakpoints.down("xs")]: {
|
|
flexDirection: "column",
|
|
},
|
|
},
|
|
displayBy: {
|
|
display: "flex",
|
|
alignItems: "center",
|
|
justifyContent: "space-between",
|
|
flexWrap: "wrap",
|
|
marginBottom: "16px",
|
|
[theme.breakpoints.down("xs")]: {
|
|
justifyContent: "center",
|
|
columnGap: 8,
|
|
rowGap: 8,
|
|
},
|
|
},
|
|
mainPaper: {
|
|
display: "flex",
|
|
flexDirection: "column",
|
|
backgroundColor: COLORS.whiteWithTransparency,
|
|
padding: "2rem",
|
|
marginBottom: theme.spacing(8),
|
|
borderRadius: 8,
|
|
},
|
|
toggleButton: {
|
|
textTransform: "none",
|
|
},
|
|
tooltip: {
|
|
margin: 8,
|
|
},
|
|
alignCenter: {
|
|
margin: "0 auto",
|
|
display: "block",
|
|
},
|
|
}));
|
|
|
|
const tooltipStyles = {
|
|
tooltip: {
|
|
minWidth: "max-content",
|
|
borderRadius: "4px",
|
|
backgroundColor: "#5EA1EC",
|
|
color: "#0F0C48",
|
|
fontSize: "14px",
|
|
},
|
|
};
|
|
|
|
const StyledTooltip = withStyles(tooltipStyles)(Tooltip);
|
|
|
|
const VolumeStats = () => {
|
|
const classes = useStyles();
|
|
|
|
const [displayBy, setDisplayBy] = useState(DISPLAY_BY_VALUES[0]);
|
|
const [timeFrame, setTimeFrame] = useState("All time");
|
|
|
|
const [selectedChains, setSelectedChains] = useState<ChainId[]>([]);
|
|
|
|
const notionalTransferred = useNotionalTransferred();
|
|
|
|
const transferData = useMemo(() => {
|
|
return notionalTransferred.data
|
|
? createCumulativeTransferChartData(
|
|
notionalTransferred.data,
|
|
TIME_FRAMES[timeFrame]
|
|
)
|
|
: [];
|
|
}, [notionalTransferred, timeFrame]);
|
|
|
|
const transferredAllTime = useMemo(() => {
|
|
return notionalTransferred.data
|
|
? new Intl.NumberFormat("en-US", {
|
|
style: "currency",
|
|
currency: "USD",
|
|
maximumFractionDigits: 0,
|
|
}).format(notionalTransferred.data.Total || 0)
|
|
: "";
|
|
}, [notionalTransferred]);
|
|
|
|
const transactionTotals = useTransactionTotals();
|
|
|
|
const transactionData = useMemo(() => {
|
|
return transactionTotals.data
|
|
? createCumulativeTransactionData(
|
|
transactionTotals.data,
|
|
TIME_FRAMES[timeFrame]
|
|
)
|
|
: [];
|
|
}, [transactionTotals, timeFrame]);
|
|
|
|
const transactionsAllTime = useMemo(() => {
|
|
const totalTransactions =
|
|
transactionData[transactionData.length - 1]?.totalTransactions;
|
|
return totalTransactions !== undefined
|
|
? formatTransactionCount(totalTransactions)
|
|
: "";
|
|
}, [transactionData]);
|
|
|
|
const availableChains = useMemo(() => {
|
|
const chainIds = notionalTransferred.data
|
|
? Object.keys(
|
|
Object.values(notionalTransferred.data.Daily)[0] || {}
|
|
).reduce<ChainId[]>((chainIds, key) => {
|
|
if (key !== "*") {
|
|
const chainId = parseInt(key) as ChainId;
|
|
if (CHAINS_BY_ID[chainId] !== undefined) {
|
|
chainIds.push(chainId);
|
|
}
|
|
}
|
|
return chainIds;
|
|
}, [])
|
|
: [];
|
|
setSelectedChains(chainIds);
|
|
return chainIds;
|
|
}, [notionalTransferred]);
|
|
|
|
const handleDisplayByChange = useCallback((event, nextValue) => {
|
|
if (nextValue !== null) {
|
|
setDisplayBy(nextValue);
|
|
}
|
|
}, []);
|
|
|
|
const handleTimeFrameChange = useCallback(
|
|
(event) => setTimeFrame(event.target.value),
|
|
[]
|
|
);
|
|
|
|
const handleSelectedChainsChange = useCallback(
|
|
(event) => {
|
|
const value = event.target.value;
|
|
if (value[value.length - 1] === "all") {
|
|
setSelectedChains((prevValue) =>
|
|
prevValue.length === availableChains.length ? [] : availableChains
|
|
);
|
|
} else {
|
|
setSelectedChains(value);
|
|
}
|
|
},
|
|
[availableChains]
|
|
);
|
|
|
|
const allChainsSelected = selectedChains.length === availableChains.length;
|
|
|
|
return (
|
|
<>
|
|
<div className={classes.description}>
|
|
<Typography variant="h3">
|
|
{displayBy === "Transactions"
|
|
? "Transaction Count"
|
|
: "Outbound Volume"}
|
|
<StyledTooltip
|
|
title={
|
|
displayBy === "Transactions"
|
|
? "Total number of transactions the Token Bridge has processed"
|
|
: "Amount of assets bridged through Portal in the outbound direction"
|
|
}
|
|
className={classes.tooltip}
|
|
>
|
|
<InfoOutlined />
|
|
</StyledTooltip>
|
|
</Typography>
|
|
<Typography variant="h3">
|
|
{displayBy === "Transactions"
|
|
? transactionsAllTime
|
|
: transferredAllTime}
|
|
</Typography>
|
|
</div>
|
|
<div className={classes.displayBy}>
|
|
<div>
|
|
<Typography display="inline" style={{ marginRight: "8px" }}>
|
|
Display by
|
|
</Typography>
|
|
<ToggleButtonGroup
|
|
value={displayBy}
|
|
exclusive
|
|
onChange={handleDisplayByChange}
|
|
>
|
|
{DISPLAY_BY_VALUES.map((value) => (
|
|
<ToggleButton
|
|
key={value}
|
|
value={value}
|
|
className={classes.toggleButton}
|
|
>
|
|
{value}
|
|
</ToggleButton>
|
|
))}
|
|
</ToggleButtonGroup>
|
|
</div>
|
|
<div>
|
|
<FormControl>
|
|
<Select
|
|
multiple
|
|
variant="outlined"
|
|
value={selectedChains}
|
|
onChange={handleSelectedChainsChange}
|
|
renderValue={(selected: any) =>
|
|
selected.length === availableChains.length
|
|
? "All chains"
|
|
: selected.length > 1
|
|
? `${selected.length} chains`
|
|
: //@ts-ignore
|
|
CHAINS_BY_ID[selected[0]]?.name
|
|
}
|
|
MenuProps={{ getContentAnchorEl: null }} // hack to prevent popup menu from moving
|
|
style={{ minWidth: 128 }}
|
|
>
|
|
<MenuItem value="all">
|
|
<Checkbox
|
|
checked={availableChains.length > 0 && allChainsSelected}
|
|
indeterminate={
|
|
selectedChains.length > 0 &&
|
|
selectedChains.length < availableChains.length
|
|
}
|
|
/>
|
|
<ListItemText primary="All chains" />
|
|
</MenuItem>
|
|
{availableChains.map((option) => (
|
|
<MenuItem key={option} value={option}>
|
|
<Checkbox checked={selectedChains.indexOf(option) > -1} />
|
|
<ListItemText primary={CHAINS_BY_ID[option]?.name} />
|
|
</MenuItem>
|
|
))}
|
|
</Select>
|
|
</FormControl>
|
|
<TextField
|
|
select
|
|
variant="outlined"
|
|
value={timeFrame}
|
|
onChange={handleTimeFrameChange}
|
|
style={{ marginLeft: 8 }}
|
|
>
|
|
{Object.keys(TIME_FRAMES).map((timeFrame) => (
|
|
<MenuItem key={timeFrame} value={timeFrame}>
|
|
{timeFrame}
|
|
</MenuItem>
|
|
))}
|
|
</TextField>
|
|
</div>
|
|
</div>
|
|
<Paper className={classes.mainPaper}>
|
|
{displayBy === "Dollar" ? (
|
|
notionalTransferred.data ? (
|
|
allChainsSelected ? (
|
|
<VolumeAreaChart
|
|
transferData={transferData}
|
|
timeFrame={TIME_FRAMES[timeFrame]}
|
|
/>
|
|
) : (
|
|
<VolumeLineChart
|
|
transferData={transferData}
|
|
timeFrame={TIME_FRAMES[timeFrame]}
|
|
chains={selectedChains}
|
|
/>
|
|
)
|
|
) : (
|
|
<CircularProgress className={classes.alignCenter} />
|
|
)
|
|
) : displayBy === "Percent" ? (
|
|
<VolumeStackedBarChart
|
|
transferData={transferData}
|
|
timeFrame={TIME_FRAMES[timeFrame]}
|
|
selectedChains={selectedChains}
|
|
/>
|
|
) : transactionTotals.data ? (
|
|
allChainsSelected ? (
|
|
<TransactionsAreaChart
|
|
transactionData={transactionData}
|
|
timeFrame={TIME_FRAMES[timeFrame]}
|
|
/>
|
|
) : (
|
|
<TransactionsLineChart
|
|
transactionData={transactionData}
|
|
timeFrame={TIME_FRAMES[timeFrame]}
|
|
chains={selectedChains}
|
|
/>
|
|
)
|
|
) : (
|
|
<CircularProgress className={classes.alignCenter} />
|
|
)}
|
|
</Paper>
|
|
</>
|
|
);
|
|
};
|
|
|
|
export default VolumeStats;
|