Skip to content

Commit

Permalink
Explorer: add token market prices from Coingecko (solana-labs#17289)
Browse files Browse the repository at this point in the history
* feat: add coingecko prices to tokens with associated coingeckoId

* feat: useCoingecko util
  • Loading branch information
oJshua authored May 17, 2021
1 parent 9d6837c commit 8a574ba
Show file tree
Hide file tree
Showing 3 changed files with 116 additions and 85 deletions.
27 changes: 26 additions & 1 deletion explorer/src/components/account/TokenAccountSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 = "";
Expand Down Expand Up @@ -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(
Expand All @@ -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">
Expand Down Expand Up @@ -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>
Expand Down Expand Up @@ -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>
);
}
Expand Down
85 changes: 1 addition & 84 deletions explorer/src/pages/ClusterStatsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand Down Expand Up @@ -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;
}
89 changes: 89 additions & 0 deletions explorer/src/utils/coingecko.tsx
Original file line number Diff line number Diff line change
@@ -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;
}

0 comments on commit 8a574ba

Please sign in to comment.