Skip to content

Commit

Permalink
pg: show token prices (#247)
Browse files Browse the repository at this point in the history
  • Loading branch information
yurushao authored Oct 14, 2024
1 parent 5e00093 commit 08573df
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 6 deletions.
4 changes: 2 additions & 2 deletions anchor/src/client/assets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ export const ASSETS_MAINNET: Map<string, AssetMeta> = new Map([
"mSoLzYCxHdYgdzU16g5QSh3i5K3z3KZK7ytfqcJm7So",
{
pricingAccount: new PublicKey(
"8szGkuLTAux9XMgZ2vtY39jVSowEcpBfFfD8hXSEqdGC" // state
"E4v1BBgoso9s64TQvmyownAVJbhbEPGyzA3qn4n46qj9" // pyth
),
},
],
Expand Down Expand Up @@ -383,7 +383,7 @@ export const ASSETS_MAINNET: Map<string, AssetMeta> = new Map([
"J1toso1uCk3RLmjorhTtrVwY9HJ7X8V9yYac6Y7kGCPn",
{
pricingAccount: new PublicKey(
"Jito4APyf642JPZPx3hGc6WWJ8zPKtRbRs4P815Awbb" // state
"7yyaeuJ1GGtVBLT2z2xub5ZWYKaNhF28mj1RdV4VDFVk" // pyth
),
},
],
Expand Down
63 changes: 63 additions & 0 deletions anchor/src/react/glam.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ import type { FundModel } from "../models";
import { GlamClient } from "../client";
import { useAtomValue, useSetAtom } from "jotai/react";
import { PublicKey } from "@solana/web3.js";
import { ASSETS_MAINNET } from "../client/assets";
import base58 from "bs58";

interface JupTokenListItem {
address: string;
Expand All @@ -31,6 +33,11 @@ interface JupTokenListItem {
logoURI: string;
}

interface PythPrice {
mint: string;
price: number; // USD
}

interface GlamProviderContext {
glamClient: GlamClient;
wallet?: PublicKey;
Expand All @@ -43,6 +50,7 @@ interface GlamProviderContext {
walletBalances: any;
walletBalancesQueryKey: any[];
refresh?: () => void;
prices: PythPrice[];
setActiveFund: any;
jupTokenList?: JupTokenListItem[];
}
Expand Down Expand Up @@ -137,6 +145,7 @@ export function GlamProvider({
);
const [allFunds, setAllFunds] = useState([] as FundModel[]);
const [jupTokenList, setJupTokenList] = useState([] as JupTokenListItem[]);
const [pythPrices, setPythPrices] = useState([] as PythPrice[]);

const activeFund = deserializeFundCache(useAtomValue(fundAtom)) as FundCache;

Expand Down Expand Up @@ -203,6 +212,59 @@ export function GlamProvider({
fetchData();
}, [allFundsData, activeFund, wallet]);

//
// Fetch prices from pyth
//

const { data: pythData } = useQuery({
queryKey: ["/prices1"],
enabled: !!activeFund?.treasury?.tokenAccounts,
refetchInterval: 30 * 1000,
queryFn: () => {
const pythFeedIds = [] as string[];
activeFund?.treasury.tokenAccounts.forEach((ta: TokenAccount) => {
const hex = Buffer.from(
ASSETS_MAINNET.get(ta.mint)?.pricingAccount.toBytes()!
).toString("hex");

pythFeedIds.push(hex);
});

const params = pythFeedIds.map((hex) => `ids[]=${hex}`).join("&");

return fetch(
`https://hermes.pyth.network/v2/updates/price/latest?${params}`
).then((res) => res.json());
},
});
useEffect(() => {
if (pythData) {
// Build a lookup table for price account -> mint account
const priceToMint = new Map<string, string>([]);
for (let [mint, asset] of ASSETS_MAINNET) {
priceToMint.set(asset.pricingAccount.toBase58(), mint);
}

if (process.env.NODE_ENV === "development") {
console.log("Pyth data:", pythData.parsed);
console.log("Price account to mint account:", priceToMint);
}
const prices = pythData.parsed.map((p: any) => {
const priceAccount = base58.encode(Buffer.from(p.id, "hex")).toString();

const price =
Number.parseFloat(p.price.price) *
10 ** Number.parseInt(p.price.expo);

return {
mint: priceToMint.get(priceAccount),
price,
} as PythPrice;
});
setPythPrices(prices);
}
}, [pythData]);

//
// Balance and token accounts of the connected wallet
//
Expand Down Expand Up @@ -250,6 +312,7 @@ export function GlamProvider({
walletBalances,
walletBalancesQueryKey,
jupTokenList: jupTokenList,
prices: pythPrices,
setActiveFund,
};

Expand Down
11 changes: 7 additions & 4 deletions playground/src/app/holdings/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { Holding } from "./data/holdingSchema"; // Make sure to import the Holdi
const SKELETON_ROW_COUNT = 5;

export default function Holdings() {
const { activeFund, jupTokenList } = useGlam();
const { activeFund, jupTokenList, prices } = useGlam();

const { treasury } = activeFund || {};

Expand All @@ -36,13 +36,15 @@ export default function Holdings() {
const solBalance = Number(treasury?.balanceLamports) / LAMPORTS_PER_SOL;
const tokenAccounts: Holding[] = [];
if (solBalance) {
const mint = "So11111111111111111111111111111111111111112";
const price = prices?.find((p) => p.mint === mint)?.price || 0;
tokenAccounts.push({
name: "Solana",
symbol: "SOL",
mint: "",
ata: "",
balance: solBalance,
notional: 1234.56,
notional: solBalance * price || 0, // FIXME: NaN not supported by zod schema
location: "vault",
logoURI:
"https://raw.githubusercontent.com/solana-labs/token-list/main/assets/mainnet/So11111111111111111111111111111111111111112/logo.png",
Expand All @@ -61,13 +63,14 @@ export default function Holdings() {
const symbol =
jupTokenList?.find((t: any) => t.address === ta.mint)?.symbol ||
ta.mint;
const price = prices?.find((p) => p.mint === ta.mint)?.price || 0;
return {
name,
symbol: symbol === "SOL" ? "wSOL" : symbol,
mint: ta.mint,
ata: ta.address,
balance: Number(ta.uiAmount),
notional: 1234.56,
notional: Number(ta.uiAmount) * price || 0, // FIXME: NaN not supported by zod schema
logoURI: logoURI,
location: "vault",
};
Expand All @@ -77,7 +80,7 @@ export default function Holdings() {

// Sort the tokenAccounts by balance in descending order
return tokenAccounts.sort((a, b) => b.balance - a.balance);
}, [treasury, jupTokenList, isLoading]);
}, [treasury, jupTokenList, isLoading, prices]);

return (
<PageContentWrapper>
Expand Down

0 comments on commit 08573df

Please sign in to comment.