Explorer: add token market prices from Coingecko (#17289)
* feat: add coingecko prices to tokens with associated coingeckoId * feat: useCoingecko util
This commit is contained in:
parent
9d6837c904
commit
8a574baae2
|
@ -17,6 +17,8 @@ import { reportError } from "utils/sentry";
|
|||
import { useTokenRegistry } from "providers/mints/token-registry";
|
||||
import { BigNumber } from "bignumber.js";
|
||||
import { Copyable } from "components/common/Copyable";
|
||||
import { CoingeckoStatus, useCoinGecko } from "utils/coingecko";
|
||||
import { displayTimestampWithoutDate } from "utils/date";
|
||||
|
||||
const getEthAddress = (link?: string) => {
|
||||
let address = "";
|
||||
|
@ -76,7 +78,6 @@ function MintAccountCard({
|
|||
const mintAddress = account.pubkey.toBase58();
|
||||
const fetchInfo = useFetchAccountInfo();
|
||||
const refresh = () => fetchInfo(account.pubkey);
|
||||
|
||||
const tokenInfo = tokenRegistry.get(mintAddress);
|
||||
|
||||
const bridgeContractAddress = getEthAddress(
|
||||
|
@ -86,6 +87,13 @@ function MintAccountCard({
|
|||
tokenInfo?.extensions?.assetContract
|
||||
);
|
||||
|
||||
const coinInfo = useCoinGecko(tokenInfo?.extensions?.coingeckoId);
|
||||
|
||||
let tokenPriceInfo;
|
||||
if (coinInfo?.status === CoingeckoStatus.Success) {
|
||||
tokenPriceInfo = coinInfo.coinInfo;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="card">
|
||||
<div className="card-header">
|
||||
|
@ -115,6 +123,17 @@ function MintAccountCard({
|
|||
)}
|
||||
</td>
|
||||
</tr>
|
||||
{tokenPriceInfo?.price && (
|
||||
<tr>
|
||||
<td>Current Price</td>
|
||||
<td className="text-lg-right">
|
||||
$
|
||||
{tokenPriceInfo.price.toLocaleString("en-US", {
|
||||
minimumFractionDigits: 2,
|
||||
})}
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
{tokenInfo?.extensions?.website && (
|
||||
<tr>
|
||||
<td>Website</td>
|
||||
|
@ -189,6 +208,12 @@ function MintAccountCard({
|
|||
</tr>
|
||||
)}
|
||||
</TableCardBody>
|
||||
{tokenPriceInfo && (
|
||||
<p className="updated-time text-muted mr-4">
|
||||
Price updated at{" "}
|
||||
{displayTimestampWithoutDate(tokenPriceInfo.last_updated.getTime())}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -15,18 +15,9 @@ import { Status, useFetchSupply, useSupply } from "providers/supply";
|
|||
import { ErrorCard } from "components/common/ErrorCard";
|
||||
import { LoadingCard } from "components/common/LoadingCard";
|
||||
import { useVoteAccounts } from "providers/accounts/vote-accounts";
|
||||
// @ts-ignore
|
||||
import * as CoinGecko from "coingecko-api";
|
||||
|
||||
enum CoingeckoStatus {
|
||||
Success,
|
||||
FetchFailed,
|
||||
}
|
||||
|
||||
const CoinGeckoClient = new CoinGecko();
|
||||
import { CoingeckoStatus, useCoinGecko } from "utils/coingecko";
|
||||
|
||||
const CLUSTER_STATS_TIMEOUT = 5000;
|
||||
const PRICE_REFRESH = 10000;
|
||||
|
||||
export function ClusterStatsPage() {
|
||||
return (
|
||||
|
@ -332,77 +323,3 @@ export function StatsNotReady({ error }: { error: boolean }) {
|
|||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
interface CoinInfo {
|
||||
price: number;
|
||||
volume_24: number;
|
||||
market_cap: number;
|
||||
price_change_percentage_24h: number;
|
||||
market_cap_rank: number;
|
||||
last_updated: Date;
|
||||
}
|
||||
|
||||
interface CoinInfoResult {
|
||||
data: {
|
||||
market_data: {
|
||||
current_price: {
|
||||
usd: number;
|
||||
};
|
||||
total_volume: {
|
||||
usd: number;
|
||||
};
|
||||
market_cap: {
|
||||
usd: number;
|
||||
};
|
||||
price_change_percentage_24h: number;
|
||||
market_cap_rank: number;
|
||||
};
|
||||
last_updated: string;
|
||||
};
|
||||
}
|
||||
|
||||
type CoinGeckoResult = {
|
||||
coinInfo?: CoinInfo;
|
||||
status: CoingeckoStatus;
|
||||
};
|
||||
|
||||
function useCoinGecko(coinId: string): CoinGeckoResult | undefined {
|
||||
const [coinInfo, setCoinInfo] = React.useState<CoinGeckoResult>();
|
||||
|
||||
React.useEffect(() => {
|
||||
const getCoinInfo = () => {
|
||||
CoinGeckoClient.coins
|
||||
.fetch("solana")
|
||||
.then((info: CoinInfoResult) => {
|
||||
setCoinInfo({
|
||||
coinInfo: {
|
||||
price: info.data.market_data.current_price.usd,
|
||||
volume_24: info.data.market_data.total_volume.usd,
|
||||
market_cap: info.data.market_data.market_cap.usd,
|
||||
market_cap_rank: info.data.market_data.market_cap_rank,
|
||||
price_change_percentage_24h:
|
||||
info.data.market_data.price_change_percentage_24h,
|
||||
last_updated: new Date(info.data.last_updated),
|
||||
},
|
||||
status: CoingeckoStatus.Success,
|
||||
});
|
||||
})
|
||||
.catch((error: any) => {
|
||||
setCoinInfo({
|
||||
status: CoingeckoStatus.FetchFailed,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
getCoinInfo();
|
||||
const interval = setInterval(() => {
|
||||
getCoinInfo();
|
||||
}, PRICE_REFRESH);
|
||||
|
||||
return () => {
|
||||
clearInterval(interval);
|
||||
};
|
||||
}, [setCoinInfo]);
|
||||
|
||||
return coinInfo;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,89 @@
|
|||
import React from "react";
|
||||
// @ts-ignore
|
||||
import * as CoinGecko from "coingecko-api";
|
||||
|
||||
const PRICE_REFRESH = 10000;
|
||||
|
||||
const CoinGeckoClient = new CoinGecko();
|
||||
|
||||
export enum CoingeckoStatus {
|
||||
Success,
|
||||
FetchFailed,
|
||||
}
|
||||
|
||||
export interface CoinInfo {
|
||||
price: number;
|
||||
volume_24: number;
|
||||
market_cap: number;
|
||||
price_change_percentage_24h: number;
|
||||
market_cap_rank: number;
|
||||
last_updated: Date;
|
||||
}
|
||||
|
||||
export interface CoinInfoResult {
|
||||
data: {
|
||||
market_data: {
|
||||
current_price: {
|
||||
usd: number;
|
||||
};
|
||||
total_volume: {
|
||||
usd: number;
|
||||
};
|
||||
market_cap: {
|
||||
usd: number;
|
||||
};
|
||||
price_change_percentage_24h: number;
|
||||
market_cap_rank: number;
|
||||
};
|
||||
last_updated: string;
|
||||
};
|
||||
}
|
||||
|
||||
export type CoinGeckoResult = {
|
||||
coinInfo?: CoinInfo;
|
||||
status: CoingeckoStatus;
|
||||
};
|
||||
|
||||
export function useCoinGecko(coinId?: string): CoinGeckoResult | undefined {
|
||||
const [coinInfo, setCoinInfo] = React.useState<CoinGeckoResult>();
|
||||
React.useEffect(() => {
|
||||
let interval: NodeJS.Timeout | undefined;
|
||||
if (coinId) {
|
||||
const getCoinInfo = () => {
|
||||
CoinGeckoClient.coins
|
||||
.fetch(coinId)
|
||||
.then((info: CoinInfoResult) => {
|
||||
setCoinInfo({
|
||||
coinInfo: {
|
||||
price: info.data.market_data.current_price.usd,
|
||||
volume_24: info.data.market_data.total_volume.usd,
|
||||
market_cap: info.data.market_data.market_cap.usd,
|
||||
market_cap_rank: info.data.market_data.market_cap_rank,
|
||||
price_change_percentage_24h:
|
||||
info.data.market_data.price_change_percentage_24h,
|
||||
last_updated: new Date(info.data.last_updated),
|
||||
},
|
||||
status: CoingeckoStatus.Success,
|
||||
});
|
||||
})
|
||||
.catch((error: any) => {
|
||||
setCoinInfo({
|
||||
status: CoingeckoStatus.FetchFailed,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
getCoinInfo();
|
||||
interval = setInterval(() => {
|
||||
getCoinInfo();
|
||||
}, PRICE_REFRESH);
|
||||
}
|
||||
return () => {
|
||||
if (interval) {
|
||||
clearInterval(interval);
|
||||
}
|
||||
};
|
||||
}, [setCoinInfo, coinId]);
|
||||
|
||||
return coinInfo;
|
||||
}
|
Loading…
Reference in New Issue