Skip to content
This repository has been archived by the owner on Oct 1, 2024. It is now read-only.

Commit

Permalink
Merge pull request #81 from metaDAOproject/feat/use-base-token-for-price
Browse files Browse the repository at this point in the history
feat: adds a fetch for hard coded spot price of future token
  • Loading branch information
swaggymarie authored Apr 17, 2024
2 parents 29a4bed + 714bbfa commit 9f147b5
Show file tree
Hide file tree
Showing 5 changed files with 96 additions and 68 deletions.
71 changes: 10 additions & 61 deletions components/Layout/Layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import {
useMantineColorScheme,
useMantineTheme,
} from '@mantine/core';
import { useDisclosure, useFavicon, useMediaQuery, useHeadroom } from '@mantine/hooks';
import { useDisclosure, useFavicon, useMediaQuery } from '@mantine/hooks';
import '@mantine/notifications/styles.css';
import { LAMPORTS_PER_SOL } from '@solana/web3.js';
import numeral from 'numeral';
Expand All @@ -31,13 +31,11 @@ import {
IconBrandTwitter,
IconSun,
IconMoonStars,
IconExternalLink,
IconPlugConnected,
} from '@tabler/icons-react';
import Image from 'next/image';
import Link from 'next/link';
import React, { useEffect, useRef, useState } from 'react';
import useSWR from 'swr';
import { Networks, useNetworkConfiguration } from '../../hooks/useNetworkConfiguration';
import { shortKey } from '@/lib/utils';
import icon from '@/public/meta.png';
Expand All @@ -48,6 +46,7 @@ import { usePriorityFee } from '../../hooks/usePriorityFee';
import { NUMERAL_FORMAT } from '../../lib/constants';
import { NavigationLinks } from './NavigationLinks';
import { DialectNotificationComponent } from '@/components/Plugins/DialectNotification';
import { TokenPrice } from './TokenPrice';

const links = [
{
Expand Down Expand Up @@ -75,49 +74,25 @@ const explorers = [
{ label: 'Solana Explorer', value: Explorers.Solana.toString() },
];

function getTokenPrice(data: any) {
const price = Math.round((Number(data.outAmount) / Number(data.inAmount)) * 1000000) / 1000;
return price;
}

function useTokenPrice() {
const url =
'https://quote-api.jup.ag/v6/quote?inputMint=METADDFL6wWMWEoKTFJwcThTbUmtarRJZjRpzUvkxhr&' +
'outputMint=EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v&' +
'amount=100000000&' +
'slippageBps=50&' +
'swapMode=ExactIn&' +
'onlyDirectRoutes=false&' +
'maxAccounts=64&' +
'experimentalDexes=Jupiter%20LO';
const tokenPriceFetcher = () =>
fetch(url)
.then((res) => res.json())
.then((data) => getTokenPrice(data));
const { data, error, isLoading } = useSWR('metaSpotPrice', tokenPriceFetcher);
export type LayoutProps = {
children: React.ReactNode;
};

return {
price: data,
isLoading,
isError: error,
};
}

export function Layout({ children }: { children: React.ReactNode }) {
export function Layout(props: LayoutProps) {
const { children } = props;
const wallet = useWallet();
const modal = useWalletModal();
const pinned = useHeadroom({ fixedAt: 200 });
const { network, endpoint, setNetwork, setCustomEndpoint } = useNetworkConfiguration();
const { explorer, setExplorer } = useExplorerConfiguration();
const colorScheme = useMantineColorScheme();
const theme = useMantineTheme();

const isTiny = useMediaQuery(`(max-width: ${theme.breakpoints.xs})`);
const logoRef = useRef(null);
const { priorityFee, setPriorityFee } = usePriorityFee();
const [solPrice, setSolPrice] = useState<number>();
const [mobileOpened, { toggle: toggleMobile }] = useDisclosure();
const [desktopOpened, { toggle: toggleDesktop }] = useDisclosure(false);
const [collapseFooter, setCollapseFooter] = useState(false);

useFavicon(_favicon.src);
useEffect(() => {
Expand Down Expand Up @@ -145,7 +120,6 @@ export function Layout({ children }: { children: React.ReactNode }) {
// Call fetchData immediately when component mounts
fetchData();
}, []); // Empty dependency array means this effect will only run once
const tokenPrice = useTokenPrice();

const feesCost = (((priorityFee / 100000) * 200000) / LAMPORTS_PER_SOL) * (solPrice || 0);

Expand All @@ -161,24 +135,13 @@ export function Layout({ children }: { children: React.ReactNode }) {
/>
);

const hasScrollbar = () => document.body.clientHeight > window.innerHeight;

useEffect(() => {
if (pinned && hasScrollbar()) {
setCollapseFooter(true);
}
if (!pinned && hasScrollbar()) {
setCollapseFooter(false);
}
}, [pinned]);

return (
<div>
<AppShell
header={{ height: 60 }}
navbar={{ breakpoint: 'md', width: 200, collapsed: { mobile: !mobileOpened, desktop: !desktopOpened } }}
padding="md"
footer={{ height: 100, collapsed: collapseFooter, offset: true }}
footer={{ height: 100, offset: true }}
>
<AppShell.Header withBorder>
<Flex justify="space-between" align="center" p="md" w="100%" h="100%">
Expand All @@ -193,21 +156,7 @@ export function Layout({ children }: { children: React.ReactNode }) {
</Link>
</Group>

<Group gap="0" justify="center" ta="center">
<div style={{ fontSize: 'small' }}>
{tokenPrice.isLoading
? 'loading...'
: !tokenPrice.isError
? `1 META ≈ $${tokenPrice.price}`
: ''}
<Link
target="_blank"
href="https://birdeye.so/token/METADDFL6wWMWEoKTFJwcThTbUmtarRJZjRpzUvkxhr?chain=solana"
>
<IconExternalLink height=".7rem" width="1rem" />
</Link>
</div>
</Group>
<TokenPrice />

<Group>
{wallet?.publicKey ? (
Expand Down
33 changes: 33 additions & 0 deletions components/Layout/TokenPrice.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { Group } from '@mantine/core';
import { IconExternalLink } from '@tabler/icons-react';
import Link from 'next/link';
import { useFetchSpotPrice } from '@/hooks/useFetchSpotPrice';

export function TokenPrice() {
const tokenPrice = useFetchSpotPrice();

const tokenPriceStatus = () => {
let tokenPriceString = '';
if (tokenPrice.isLoading) {
tokenPriceString = 'loading...';
}
if (!tokenPrice.isError) {
tokenPriceString = `1 ${tokenPrice.token} ≈ $${tokenPrice.price}`;
}
return tokenPriceString;
};

return (
<Group gap="0" justify="center" ta="center">
<div style={{ fontSize: 'small' }}>
{tokenPriceStatus()}
<Link
target="_blank"
href="https://birdeye.so/token/METADDFL6wWMWEoKTFJwcThTbUmtarRJZjRpzUvkxhr?chain=solana"
>
<IconExternalLink height=".7rem" width="1rem" />
</Link>
</div>
</Group>
);
}
13 changes: 6 additions & 7 deletions components/Proposals/ProposalDetailCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,12 +59,9 @@ export function ProposalDetailCard(props: ProposalProps) {
const { programKey, proposalNumber } = props;
const wallet = useWallet();
const { daoTreasuryKey, daoTokens, daoState, programVersion, setProgramVersion } = useAutocrat();
if (
!daoTokens || !daoState || !programVersion
|| (!programVersion?.programId.toString() && programKey)
) return <Loader />;

// NOTE: Added as we don't want to willy nilly just update stuff already set.
const isSameProgram = programVersion.programId.toString() === programKey;
const isSameProgram = programVersion?.programId.toString() === programKey;

const tokens = daoTokens;
const { redeemTokensTransactions } = useConditionalVault();
Expand Down Expand Up @@ -302,11 +299,13 @@ export function ProposalDetailCard(props: ProposalProps) {
const proposalId = pendingProposals?.filter((p) => p?.title === title)[0]?.account.number;

if (proposalId) {
router.replace(`/program/proposal?programKey=${programKey || programVersion.programId.toString()}&proposalNumber=${proposalId}`);
router.replace(`/program/proposal?programKey=${programKey || programVersion?.programId.toString()}&proposalNumber=${proposalId}`);
}
};

return !proposal || !markets || !programVersion ? (
return !daoTokens || !daoState || !proposal
|| !markets || !programVersion
|| (!programVersion?.programId.toString() && programKey) ? (
<Group justify="center">
<Loader />
</Group>
Expand Down
40 changes: 40 additions & 0 deletions hooks/useFetchSpotPrice.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import useSWR from 'swr';
import { useSearchParams } from 'next/navigation';
import { convertTokenPrice } from '@/lib/utils';

export function useFetchSpotPrice() {
const params = useSearchParams();
const programKey = params.get('programKey');
let inputMint = 'METADDFL6wWMWEoKTFJwcThTbUmtarRJZjRpzUvkxhr';
let tokenBase = 100_000_000;
let decimals = 3;
let tokenName = 'META';
if (programKey
&& programKey === 'fut5MzSUFcmxaEHMvo9qQThrAL4nAv5FQ52McqhniSt'
) {
inputMint = 'FUTURETnhzFApq2TiZiNbWLQDXMx4nWNpFtmvTf11pMy';
tokenBase = 100_000_000;
decimals = 5;
tokenName = 'FUTURE';
}
const url =
`https://quote-api.jup.ag/v6/quote?inputMint=${inputMint}&` +
'outputMint=EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v&' +
`amount=${tokenBase.toString()}&` +
'slippageBps=50&' +
'swapMode=ExactIn&' +
'onlyDirectRoutes=false&' +
'maxAccounts=64&' +
'experimentalDexes=Jupiter%20LO';
const tokenPriceFetcher = () =>
fetch(url)
.then((res) => res.json())
.then((data) => convertTokenPrice(data, decimals));
const { data, error, isLoading } = useSWR(`${tokenName}SpotPrice`, tokenPriceFetcher);
return {
token: tokenName,
price: data,
isLoading,
isError: error,
};
}
7 changes: 7 additions & 0 deletions lib/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,13 @@ export function debounce<T extends any[]>(
};
}

export const convertTokenPrice = (data: any, decimals: number) => {
const price = Math.round(
(Number(data.outAmount) / Number(data.inAmount)) * 1_000 * (10 ** decimals)
) / 10 ** decimals;
return price;
};

export const validateType = async (type: InstructionFieldTypes, value?: string) => {
switch (type) {
case InstructionFieldTypes.Key:
Expand Down

0 comments on commit 9f147b5

Please sign in to comment.