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:
Josh 2021-05-17 12:43:18 -07:00 committed by GitHub
parent 9d6837c904
commit 8a574baae2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 116 additions and 85 deletions

View File

@ -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>
);
}

View File

@ -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;
}

View File

@ -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;
}