mirror of
https://github.com/MetaFam/TheGame.git
synced 2026-04-24 03:00:09 -04:00
377 lines
10 KiB
TypeScript
377 lines
10 KiB
TypeScript
import {
|
|
Box,
|
|
Button,
|
|
ButtonGroup,
|
|
Flex,
|
|
Image,
|
|
Link,
|
|
Stat,
|
|
StatArrow,
|
|
StatGroup,
|
|
StatHelpText,
|
|
StatLabel,
|
|
StatNumber,
|
|
Text,
|
|
VStack,
|
|
} from '@metafam/ds';
|
|
import { animated, useSpring } from '@react-spring/web';
|
|
import React, { FC, ReactElement, ReactNode, useEffect, useState } from 'react';
|
|
import { FaChartBar } from 'react-icons/fa';
|
|
import {
|
|
AreaSeries,
|
|
FlexibleXYPlot,
|
|
GradientDefs,
|
|
LineSeries,
|
|
} from 'react-vis';
|
|
|
|
import CoinGeckoLogo from '#assets/attribution/coingecko-logo-text.webp';
|
|
import {
|
|
findHighLowPrice,
|
|
HighLowType,
|
|
priceIncreased,
|
|
ticker,
|
|
volIncreased,
|
|
volumeChange,
|
|
} from '#utils/dashboardHelpers';
|
|
import { errorHandler } from '#utils/errorHandler';
|
|
|
|
const SEED_TOKEN_ID = 'metagame';
|
|
export const COINGECKO_API_URL = 'https://api.coingecko.com/api/v3/coins/';
|
|
export const TOKEN_QUERY = '?localization=false&tickers=true&market_data=true';
|
|
const CHART_QUERY = '/market_chart?vs_currency=usd&days=30&interval=daily';
|
|
|
|
/**
|
|
* Temporary link to the Seed Pool on Balancer
|
|
* Ref: #1407 https://github.com/MetaFam/TheGame/issues/1407
|
|
* On the Dashboard we provide a link to Seed Pool Info so Players/Patrons can check their investments in the Seed Pool
|
|
* The link is supposed to be provided by the CoinGecko API (`ticker.token_info_url`)
|
|
* As at 2022/11/26 the link provided by the API is to the (outdated) Mainnet Seed Pool instead of the (new, awesome) Polygon Seed Pool
|
|
* (UPDATE: As at 2022/11/26 17:47 the API isn't providing any of the ticker info)
|
|
* Until the CoinGecko API is updated, we're doing to hard-code the link to the Polygon Seed Pool.
|
|
* - TEMP_SEED_POOL_LINK_FIX_LATER, see below
|
|
* After the CoinGecko API is updated, we are going to remember to revert this change. (Previous code has been kept but commented out)
|
|
*/
|
|
|
|
const TEMP_SEED_POOL_LINK_FIX_LATER =
|
|
'https://app.balancer.fi/#/polygon/pool/0x8a8fcd351ed553fc75aecbc566a32f94471f302e000100000000000000000081';
|
|
|
|
type TokenDataProps = {
|
|
market: MarketDataProps;
|
|
poolTicker: TickerProps;
|
|
priceUp: boolean;
|
|
volumeUp: boolean;
|
|
volumePercent: string;
|
|
highLow7d: HighLowType;
|
|
prices: Array<Array<number>>;
|
|
};
|
|
|
|
type MarketDataProps = {
|
|
price_change_percentage_24h: number | null;
|
|
total_volume: Record<string, number>;
|
|
current_price: Record<string, number>;
|
|
};
|
|
|
|
type TickerProps = {
|
|
market: MarketProps;
|
|
token_info_url: string;
|
|
};
|
|
|
|
type MarketProps = {
|
|
identifier: string;
|
|
};
|
|
|
|
export const Seed = (): ReactElement => {
|
|
const [token, setToken] = useState<TokenDataProps | null>(null);
|
|
|
|
useEffect(() => {
|
|
if (token !== null) return;
|
|
(async () => {
|
|
try {
|
|
const tokenResponse = await fetch(
|
|
`${COINGECKO_API_URL}${SEED_TOKEN_ID}${TOKEN_QUERY}`,
|
|
);
|
|
const tokenJson = await tokenResponse.json();
|
|
const chartResponse = await fetch(
|
|
`${COINGECKO_API_URL}${SEED_TOKEN_ID}${CHART_QUERY}`,
|
|
);
|
|
const chartJson = await chartResponse.json();
|
|
const { market_data: marketData, tickers } = tokenJson;
|
|
const { prices, total_volumes: totalVolumes } = chartJson;
|
|
const market = marketData;
|
|
const poolTicker = ticker(tickers, 'balancer_v1');
|
|
const priceUp =
|
|
priceIncreased(marketData.price_change_percentage_24h) > 0;
|
|
const volumeUp =
|
|
volIncreased(totalVolumes, marketData.total_volume) > 0;
|
|
const volumePercent = volumeChange(
|
|
totalVolumes,
|
|
marketData.total_volume,
|
|
).toFixed(2);
|
|
const highLow7d = findHighLowPrice(prices, 7);
|
|
const tokenData: TokenDataProps = {
|
|
market,
|
|
poolTicker,
|
|
priceUp,
|
|
volumeUp,
|
|
volumePercent,
|
|
highLow7d,
|
|
prices,
|
|
};
|
|
|
|
return setToken(tokenData);
|
|
} catch (error) {
|
|
console.error('error fetching tokenData', error);
|
|
errorHandler(error as Error);
|
|
return null;
|
|
}
|
|
})();
|
|
}, [token]);
|
|
|
|
return (
|
|
<Flex direction="column" p={6} w="100%">
|
|
<Text fontSize="lg" fontWeight="bold" textTransform="uppercase">
|
|
Seed
|
|
</Text>
|
|
<VStack spacing={2} align="stretch">
|
|
<Box position="relative" zIndex="20" h="15rem">
|
|
<StatGroup position="relative" my={5} zIndex={10}>
|
|
<Stat mb={3}>
|
|
<StatLabel>Market Price</StatLabel>
|
|
<StatNumber>${token?.market.current_price.usd}</StatNumber>
|
|
<StatHelpText>
|
|
<StatArrow type={token?.priceUp ? 'increase' : 'decrease'} />
|
|
{token?.market.price_change_percentage_24h
|
|
? `${token?.market.price_change_percentage_24h?.toFixed(2)}%`
|
|
: `No data 😞`}
|
|
</StatHelpText>
|
|
</Stat>
|
|
|
|
<Stat mb={3}>
|
|
<StatLabel>24h Trading Volume</StatLabel>
|
|
<StatNumber>${token?.market.total_volume.usd}</StatNumber>
|
|
<StatHelpText>
|
|
<StatArrow type={token?.volumeUp ? 'increase' : 'decrease'} />
|
|
{token?.volumePercent}%
|
|
</StatHelpText>
|
|
</Stat>
|
|
|
|
<Stat alignSelf="flex-start" flex="0 0 100%">
|
|
<StatLabel>7d Low / High</StatLabel>
|
|
<StatNumber>
|
|
${token?.highLow7d.low} / ${token?.highLow7d.high}
|
|
</StatNumber>
|
|
</Stat>
|
|
</StatGroup>
|
|
</Box>
|
|
<Box
|
|
position="absolute"
|
|
width="100%"
|
|
height="100%"
|
|
bottom={0}
|
|
left={0}
|
|
zIndex={0}
|
|
>
|
|
{token?.prices ? (
|
|
<Chart data={token.prices} />
|
|
) : (
|
|
<Box
|
|
position="absolute"
|
|
bottom={5}
|
|
right={5}
|
|
opacity={0.5}
|
|
fontSize="lg"
|
|
>
|
|
Loading chart...
|
|
</Box>
|
|
)}
|
|
</Box>
|
|
{/**
|
|
* Delete this component after the CoinGecko API is updated with the Polygon Seed Pool info
|
|
* (and uncomment the following bit to start using the API again for the link)
|
|
*/}
|
|
<Link
|
|
position="absolute"
|
|
bottom={6}
|
|
left={6}
|
|
href={TEMP_SEED_POOL_LINK_FIX_LATER}
|
|
isExternal
|
|
zIndex={20}
|
|
>
|
|
Pool Info
|
|
</Link>
|
|
|
|
{/**
|
|
* Uncomment this after the CoinGecko API is updated with the Polygon Seed Pool info
|
|
|
|
{token?.poolTicker && (
|
|
<Link
|
|
position="absolute"
|
|
bottom={6}
|
|
left={6}
|
|
href={token?.poolTicker.token_info_url}
|
|
isExternal
|
|
zIndex={20}
|
|
>
|
|
Pool Info
|
|
</Link>
|
|
)}
|
|
|
|
*/}
|
|
</VStack>
|
|
</Flex>
|
|
);
|
|
};
|
|
|
|
type ChartType = {
|
|
children?: ReactNode;
|
|
data: Array<Array<number>>;
|
|
};
|
|
|
|
const ChartRange = ({ value = {} }): React.ReactElement => (
|
|
<Box
|
|
opacity={1}
|
|
color={'white'}
|
|
fontSize="md"
|
|
fontWeight={700}
|
|
transition="all 0.1s ease-in-out"
|
|
>{`${value}d`}</Box>
|
|
);
|
|
const AnimatedChartRange = animated(ChartRange);
|
|
|
|
export const Chart: FC<ChartType> = ({ data }) => {
|
|
const [scale, setScale] = useState<number>(30);
|
|
const props = useSpring({
|
|
config: {
|
|
mass: 10,
|
|
tension: 280,
|
|
friction: 100,
|
|
},
|
|
range: scale,
|
|
});
|
|
|
|
const makePlots = (days: Array<Array<number>>) =>
|
|
days.slice(scale === 30 ? 0 : -7).map((d, i) => ({
|
|
x: i + 1,
|
|
y: d[1],
|
|
y0: 0,
|
|
}));
|
|
|
|
const plots = makePlots(data);
|
|
|
|
return (
|
|
<Box>
|
|
<ButtonGroup
|
|
isAttached
|
|
position="absolute"
|
|
top={6}
|
|
right={6}
|
|
zIndex={250}
|
|
>
|
|
{[7, 30].map((s) => (
|
|
<Button
|
|
key={s}
|
|
aria-label="Toggle chart scale"
|
|
leftIcon={<FaChartBar />}
|
|
borderColor="pinkShadeOne"
|
|
background="rgba(17, 17, 17, 0.9)"
|
|
color="pinkShadeOne"
|
|
_hover={{ color: 'white', borderColor: 'white' }}
|
|
variant="outline"
|
|
onClick={() => setScale(s)}
|
|
size="xs"
|
|
sx={{
|
|
'&:focus, &:hover': {
|
|
background: 'transparent',
|
|
},
|
|
}}
|
|
>
|
|
{s}d
|
|
</Button>
|
|
))}
|
|
</ButtonGroup>
|
|
<FlexibleXYPlot
|
|
style={{
|
|
position: 'absolute',
|
|
bottom: 0,
|
|
left: 0,
|
|
right: 0,
|
|
maxW: '100%',
|
|
}}
|
|
height={175}
|
|
margin={{
|
|
right: -2,
|
|
left: -2,
|
|
bottom: -2,
|
|
}}
|
|
>
|
|
<LineSeries
|
|
curve="linear"
|
|
strokeStyle="solid"
|
|
animation={{ damping: 80, stiffness: 800 }}
|
|
stroke="#A426A4"
|
|
opacity={0.5}
|
|
data={plots}
|
|
style={{
|
|
bottom: 0,
|
|
strokeWidth: 2,
|
|
fillOpacity: 0,
|
|
}}
|
|
/>
|
|
<GradientDefs>
|
|
<linearGradient id="MetaGradient" x1="0" x2="0" y1="0" y2="1">
|
|
<stop offset="0%" stopColor="white" stopOpacity={0.9} />
|
|
<stop offset="100%" stopColor="black" stopOpacity={1} />
|
|
</linearGradient>
|
|
</GradientDefs>
|
|
<AreaSeries
|
|
curve="linear"
|
|
color="url(#MetaGradient)"
|
|
animation={{ damping: 80, stiffness: 800 }}
|
|
opacity={0.2}
|
|
data={plots}
|
|
style={{
|
|
bottom: 0,
|
|
fillOpacity: 0.5,
|
|
strokeWidth: 0,
|
|
}}
|
|
/>
|
|
</FlexibleXYPlot>
|
|
<Box
|
|
aria-label="Seed Graph scale"
|
|
position="absolute"
|
|
bottom={6}
|
|
right={6}
|
|
>
|
|
<AnimatedChartRange value={props.range.to((x) => x.toFixed(0))} />
|
|
</Box>
|
|
<Box
|
|
aria-label="Seed Data powered by CoinGecko"
|
|
position="absolute"
|
|
bottom={6}
|
|
right="auto"
|
|
fontSize="sm"
|
|
width="100%"
|
|
textAlign="center"
|
|
>
|
|
<Link
|
|
display="inline-flex"
|
|
href={`https://www.coingecko.com/en/coins/${SEED_TOKEN_ID}`}
|
|
isExternal
|
|
width="25%"
|
|
// title="CoinGecko"
|
|
sx={{
|
|
opacity: 0.08,
|
|
transition: 'all 1s ease-in-out',
|
|
_hover: {
|
|
opacity: 0.3,
|
|
// transform: 'scale(1.1)'
|
|
},
|
|
}}
|
|
>
|
|
<Image src={CoinGeckoLogo.src} height="auto" width="100%" mx="auto" />
|
|
</Link>
|
|
</Box>
|
|
</Box>
|
|
);
|
|
};
|