Skip to content

Commit

Permalink
Notify token 2022 (#319)
Browse files Browse the repository at this point in the history
* improved display on main page and display token 2022 where needed

* linting fixes

* formatting fix
  • Loading branch information
TovarishFin authored Dec 14, 2024
1 parent 1fb9b33 commit 70c3a23
Show file tree
Hide file tree
Showing 7 changed files with 147 additions and 52 deletions.
52 changes: 50 additions & 2 deletions debug-ui/app/components/AppWalletProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,14 @@ import {
getClusterFromConnection,
} from '@cks-systems/manifest-sdk/utils/solana';
import NavBar from './NavBar';
import { ActiveByAddr, LabelsByAddr, VolumeByAddr } from '@/lib/types';
import {
ActiveByAddr,
LabelsByAddr,
VolumeByAddr,
HasToken22ByAddr,
} from '@/lib/types';
import { fetchAndSetMfxAddrLabels } from '@/lib/address-labels';
import { checkForToken22 } from '@/lib/util';

require('react-toastify/dist/ReactToastify.css');
require('@solana/wallet-adapter-react-ui/styles.css');
Expand All @@ -44,11 +50,14 @@ interface AppStateContextValue {
network: WalletAdapterNetwork | null;
marketAddrs: string[];
labelsByAddr: LabelsByAddr;
infoByAddr: LabelsByAddr;
activeByAddr: ActiveByAddr;
marketVolumes: VolumeByAddr;
dailyVolumes: VolumeByAddr;
hasToken22ByAddr: HasToken22ByAddr;
setMarketAddrs: Dispatch<SetStateAction<string[]>>;
setLabelsByAddr: Dispatch<SetStateAction<LabelsByAddr>>;
setInfoByAddr: Dispatch<SetStateAction<LabelsByAddr>>;
setActiveByAddr: Dispatch<SetStateAction<ActiveByAddr>>;
setMarketVolumes: Dispatch<SetStateAction<VolumeByAddr>>;
}
Expand All @@ -75,8 +84,10 @@ const AppWalletProvider = ({
const [marketVolumes, setMarketVolumes] = useState<VolumeByAddr>({});
const [dailyVolumes, setDailyVolumes] = useState<VolumeByAddr>({});
const [labelsByAddr, setLabelsByAddr] = useState<LabelsByAddr>({});
const [infoByAddr, setInfoByAddr] = useState<LabelsByAddr>({});
const [activeByAddr, setActiveByAddr] = useState<ActiveByAddr>({});
const [loading, setLoading] = useState<boolean>(false);
const [has22ByAddr, setHas22ByAddr] = useState<HasToken22ByAddr>({});
const setupRun = useRef(false);

const rpcUrl = process.env.NEXT_PUBLIC_RPC_URL;
Expand Down Expand Up @@ -157,6 +168,7 @@ const AppWalletProvider = ({

// Fine to do an N^2 search until the number of markets gets too big.
const activeByAddr: ActiveByAddr = {};
const marketsByAddr: { [key: string]: Market } = {};
marketProgramAccounts.forEach(
(
acct1: Readonly<{
Expand All @@ -168,6 +180,7 @@ const AppWalletProvider = ({
address: acct1.pubkey,
buffer: acct1.account.data,
});
marketsByAddr[acct1.pubkey.toBase58()] = market;
let foundBigger: boolean = false;

marketProgramAccounts.forEach(
Expand Down Expand Up @@ -198,7 +211,39 @@ const AppWalletProvider = ({
);
setActiveByAddr(activeByAddr);

fetchAndSetMfxAddrLabels(conn, marketProgramAccounts, setLabelsByAddr);
const activeAddrs = Object.entries(activeByAddr)
.filter(([_, active]) => active)
.map(([addr]) => addr);
const res: [string, boolean][] = await Promise.all(
activeAddrs.map(async (addr) => {
const market = marketsByAddr[addr];
if (!market) {
throw new Error(
'missing market in mapping. this should never happen',
);
}
const [quoteIs22, baseIs22] = await Promise.all([
checkForToken22(conn, market.quoteMint()),
checkForToken22(conn, market.baseMint()),
]);
const has22 = quoteIs22 || baseIs22;

return [addr, has22];
}),
);
const hasToken22ByAddr: HasToken22ByAddr = res.reduce((acc, curr) => {
const [addr, has22] = curr;
acc[addr] = has22;
return acc;
}, {} as HasToken22ByAddr);
setHas22ByAddr(hasToken22ByAddr);

fetchAndSetMfxAddrLabels(
conn,
marketProgramAccounts,
setLabelsByAddr,
setInfoByAddr,
);

const tickers = await fetch(
'https://mfx-stats-mainnet.fly.dev/tickers',
Expand Down Expand Up @@ -241,11 +286,14 @@ const AppWalletProvider = ({
marketVolumes,
activeByAddr,
labelsByAddr,
infoByAddr,
setLabelsByAddr,
setInfoByAddr,
setMarketAddrs,
setMarketVolumes,
setActiveByAddr,
dailyVolumes,
hasToken22ByAddr: has22ByAddr,
loading,
}}
>
Expand Down
13 changes: 7 additions & 6 deletions debug-ui/app/components/Chart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,16 @@ import { useConnection } from '@solana/wallet-adapter-react';
import { PublicKey } from '@solana/web3.js';
import { toast } from 'react-toastify';
import { useAppState } from './AppWalletProvider';
import { addrToLabel } from '@/lib/address-labels';

const Chart = ({ marketAddress }: { marketAddress: string }): ReactElement => {
const chartContainerRef = useRef<HTMLDivElement | null>(null);
const chartRef = useRef<IChartApi | null>(null);
const candlestickSeriesRef = useRef<ISeriesApi<'Candlestick'> | null>(null);
const marketRef = useRef<Market | null>(null); // To track the latest market value

const { labelsByAddr } = useAppState();
const { labelsByAddr, hasToken22ByAddr } = useAppState();

const [chartEntries, setChartEntries] = useState<CandlestickData[]>([]);
const [marketName, setMarketName] = useState<string>(marketAddress);

const { connection: conn } = useConnection();

Expand All @@ -38,8 +36,6 @@ const Chart = ({ marketAddress }: { marketAddress: string }): ReactElement => {
console.log('got market', m);
marketRef.current = m;
});

setMarketName(addrToLabel(marketAddress, labelsByAddr));
}, [conn, marketAddress, labelsByAddr]);

useEffect(() => {
Expand Down Expand Up @@ -190,7 +186,12 @@ const Chart = ({ marketAddress }: { marketAddress: string }): ReactElement => {
return (
<div className="bg-gray-800 p-4 rounded-lg w-full">
<h2 className="text-xl font-semibold text-gray-200 mb-4 text-center">
{marketName}
{labelsByAddr[marketAddress]}
{hasToken22ByAddr[marketAddress] && (
<span className="ml-3 inline-block bg-blue-500 text-white text-xs font-semibold px-2 py-1 rounded">
TOKEN_2022
</span>
)}
</h2>

<div ref={chartContainerRef} className="w-full h-96" />
Expand Down
8 changes: 4 additions & 4 deletions debug-ui/app/components/Orderbook.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,10 @@ const Orderbook = ({
},
);

return () => {
return (): void => {
conn.removeAccountChangeListener(accountChangeListenerId);
};
}, [marketAddress]);
}, [conn, marketAddress]);

useEffect(() => {
try {
Expand Down Expand Up @@ -80,10 +80,10 @@ const Orderbook = ({
}
});

return () => {
return (): void => {
conn.removeSlotUpdateListener(slotUpdateListenerId);
};
}, []);
}, [conn]);

const formatOrder = (restingOrder: RestingOrder, i: number): ReactElement => {
const pk = wallet?.adapter?.publicKey;
Expand Down
99 changes: 61 additions & 38 deletions debug-ui/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,79 +13,102 @@ const Home = (): ReactElement => {
marketAddrs,
loading,
labelsByAddr,
infoByAddr,
marketVolumes,
activeByAddr,
dailyVolumes,
hasToken22ByAddr,
} = useAppState();
const [showAll, setShowAll] = useState<boolean>(false);

function handleShowAllChange(event: { target: { checked: any } }) {
function handleShowAllChange(event: { target: { checked: boolean } }): void {
setShowAll(event.target.checked);
}

return (
<main className="flex min-h-screen flex-col items-center justify-center bg-gray-900 text-gray-200 p-8">
<div className="bg-gray-800 p-8 rounded-lg shadow-lg w-full max-w-xl">
<p className="text-sm text-gray-400 mb-6">
Disclaimer: By accessing and using Manifest, you acknowledge and agree
that you do so at your own risk. This platform is intended for
developers ONLY and may not be actively supported or maintained. The
developers, contributors, and associated parties are not liable for
any losses, damages, or claims arising from your use of this platform.
This platform is provided &quot;as is&quot; without any warranties or
<div className="bg-gray-800 p-8 rounded-lg shadow-lg w-full max-w-2xl">
<p className="text-sm text-gray-400 mb-6 p-4 bg-gray-700 rounded-lg leading-relaxed">
<strong className="block mb-2 font-semibold text-gray-300">
Disclaimer
</strong>
By accessing and using Manifest, you acknowledge and agree that you do
so at your own risk. This platform is intended for developers ONLY and
may not be actively supported or maintained. The developers,
contributors, and associated parties are not liable for any losses,
damages, or claims arising from your use of this platform. This
platform is provided &quot;as is&quot; without any warranties or
guarantees. Users are responsible for complying with all applicable
laws and regulations in their jurisdiction. Please exercise caution.
</p>

{loading ? (
<p className="text-center">Loading markets...</p>
<p className="text-center text-lg font-medium">Loading markets...</p>
) : marketAddrs.length > 0 ? (
<>
<h2 className="text-xl font-semibold mb-4 text-center">
Existing Markets
</h2>
<ul className="space-y-4 bg-gray-700 p-4 rounded-lg">
<div className="flex items-center justify-between mb-4">
<h2 className="text-2xl font-semibold text-gray-100">
Existing Markets
</h2>
<div className="flex items-center gap-2">
<Toggle
defaultChecked={false}
icons={false}
onChange={handleShowAllChange}
/>
<span className="text-sm text-gray-300">Show All</span>
</div>
</div>

<ul className="space-y-4 bg-gray-700 p-4 rounded-lg shadow-inner">
{marketAddrs.map(
(market, index) =>
(showAll || activeByAddr[market]) && (
<li
key={index}
className="bg-gray-600 p-2 rounded-lg hover:bg-gray-500 transition-colors"
className="bg-gray-600 p-4 rounded-lg hover:bg-gray-500 transition-all duration-200 hover:shadow-md"
>
<Link
href={`/${readOnly ? 'market' : 'interact'}/${market}`}
className="text-blue-400 underline hover:text-blue-500 transition-colors"
className="text-blue-400 underline hover:text-blue-300 text-lg font-medium"
title={infoByAddr[market] || 'unknown'}
>
{addrToLabel(market, labelsByAddr)}
</Link>
{marketVolumes[market] != 0
? ' Total: $' +
marketVolumes[market]?.toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})
: ''}
{dailyVolumes[market] != 0 &&
dailyVolumes[market] !== undefined
? ' | 24 Hour: $' +
dailyVolumes[market]?.toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})
: ''}

{hasToken22ByAddr[market] && (
<span className="ml-3 inline-block bg-blue-500 text-white text-xs font-semibold px-2 py-1 rounded">
TOKEN_2022
</span>
)}
<div className="mt-2 text-sm text-gray-200">
{marketVolumes[market] !== 0 && (
<>
Total: $
{marketVolumes[market]?.toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}
</>
)}
{dailyVolumes[market] !== 0 &&
dailyVolumes[market] !== undefined && (
<>
{' | 24 Hour: $'}
{dailyVolumes[market]?.toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}
</>
)}
</div>
</li>
),
)}
</ul>
<Toggle
defaultChecked={false}
icons={false}
onChange={handleShowAllChange}
/>
<span>Show All</span>
</>
) : (
<p className="text-center">No markets found.</p>
<p className="text-center text-lg font-medium">No markets found.</p>
)}
</div>
</main>
Expand Down
10 changes: 8 additions & 2 deletions debug-ui/lib/address-labels.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ export const fetchAndSetMfxAddrLabels = async (
conn: Connection,
marketProgramAccounts: GetProgramAccountsResponse,
setLabelsByAddr: Dispatch<SetStateAction<LabelsByAddr>>,
setInfoByAddr: Dispatch<SetStateAction<LabelsByAddr>>,
): Promise<Set<string>> => {
const mints = new Set<string>();
const markets: Market[] = [];
Expand Down Expand Up @@ -75,12 +76,17 @@ export const fetchAndSetMfxAddrLabels = async (
);

const marketLabels: LabelsByAddr = {};
const infoByAddr: LabelsByAddr = {};
for (const m of markets) {
marketLabels[m.address.toBase58()] =
`MFX-${pubkeyToLabel(m.baseMint(), mintLabels)}/${pubkeyToLabel(m.quoteMint(), mintLabels)}-${shortenPub(m.address)}`;
const marketAddr = m.address.toBase58();
marketLabels[marketAddr] =
`${pubkeyToLabel(m.baseMint(), mintLabels)}/${pubkeyToLabel(m.quoteMint(), mintLabels)}`;
infoByAddr[marketAddr] =
`base: ${m.baseMint().toBase58()} quote: ${m.quoteMint().toBase58()} market: ${marketAddr}`;
}

setLabelsByAddr({ ...mintLabels, ...marketLabels });
setInfoByAddr({ ...infoByAddr });

return mints;
};
Expand Down
4 changes: 4 additions & 0 deletions debug-ui/lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ export interface VolumeByAddr {
[addr: string]: number;
}

export interface HasToken22ByAddr {
[addr: string]: boolean;
}

export type FillResultUi = {
market: string;
maker: string;
Expand Down
13 changes: 13 additions & 0 deletions debug-ui/lib/util.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { ManifestClient } from '@cks-systems/manifest-sdk';
import { TOKEN_PROGRAM_ID } from '@solana/spl-token';
import {
SendTransactionOptions,
WalletAdapterNetwork,
Expand Down Expand Up @@ -92,3 +93,15 @@ export const shortenAddress = (address: string): string => {
export const shortenSig = (address: string): string => {
return `${address.slice(0, 6)}...${address.slice(-6)}`;
};

export const checkForToken22 = async (
conn: Connection,
mint: PublicKey,
): Promise<boolean> => {
const acc = await conn.getAccountInfo(mint);
if (!acc) {
throw new Error('checkForToken22: account does not exist');
}

return acc.owner.toBase58() !== TOKEN_PROGRAM_ID.toBase58();
};

0 comments on commit 70c3a23

Please sign in to comment.