Show token tickers on pool page
This commit is contained in:
parent
3f21f90f77
commit
cc6d2707ef
|
@ -0,0 +1,42 @@
|
|||
import React from 'react';
|
||||
import { PublicKey } from '@solana/web3.js';
|
||||
import { abbreviateAddress } from '../utils/utils';
|
||||
import { useMintToTickers } from '../utils/tokens';
|
||||
import { Popover } from 'antd';
|
||||
import LinkAddress from './LinkAddress';
|
||||
import { InfoCircleOutlined } from '@ant-design/icons';
|
||||
|
||||
export function MintName({
|
||||
mint,
|
||||
showAddress = false,
|
||||
}: {
|
||||
mint: string | PublicKey | null | undefined;
|
||||
showAddress?: boolean;
|
||||
}) {
|
||||
const mintToTickers = useMintToTickers();
|
||||
if (!mint) {
|
||||
return null;
|
||||
}
|
||||
const mintKey = typeof mint === 'string' ? new PublicKey(mint) : mint;
|
||||
const mintAddress = typeof mint === 'string' ? mint : mint.toBase58();
|
||||
const ticker = mintToTickers[mintAddress] ?? abbreviateAddress(mintKey);
|
||||
|
||||
return (
|
||||
<>
|
||||
{ticker}
|
||||
{showAddress ? (
|
||||
<>
|
||||
{' '}
|
||||
<Popover
|
||||
content={<LinkAddress address={mintAddress} />}
|
||||
placement="bottomRight"
|
||||
title="Token mint"
|
||||
trigger="hover"
|
||||
>
|
||||
<InfoCircleOutlined style={{ color: '#2abdd2' }} />
|
||||
</Popover>
|
||||
</>
|
||||
) : null}
|
||||
</>
|
||||
);
|
||||
}
|
|
@ -11,16 +11,17 @@ import {
|
|||
import { sendTransaction } from '../../../utils/send';
|
||||
import { notify } from '../../../utils/notifications';
|
||||
import { PublicKey, Transaction } from '@solana/web3.js';
|
||||
import { Button, Input, Tabs } from 'antd';
|
||||
import { Button, Input, Select, Tabs } from 'antd';
|
||||
import {
|
||||
createAssociatedTokenAccount,
|
||||
getAssociatedTokenAddress,
|
||||
} from '@project-serum/associated-token';
|
||||
import { parseTokenMintData } from '../../../utils/tokens';
|
||||
import { parseTokenMintData, useMintToTickers } from '../../../utils/tokens';
|
||||
import BN from 'bn.js';
|
||||
import { refreshAllCaches } from '../../../utils/fetch-loop';
|
||||
|
||||
const { TabPane } = Tabs;
|
||||
const { Option } = Select;
|
||||
|
||||
export function PoolAdminPanel({ poolInfo }: { poolInfo: PoolInfo }) {
|
||||
return (
|
||||
|
@ -41,7 +42,7 @@ export function PoolAdminPanel({ poolInfo }: { poolInfo: PoolInfo }) {
|
|||
<TabPane tab="Withdraw" key="withdraw">
|
||||
<WithdrawTab poolInfo={poolInfo} />
|
||||
</TabPane>
|
||||
<TabPane tab="Change Fee" key="updateFee">
|
||||
<TabPane tab="Modify Fee" key="updateFee">
|
||||
<UpdateFeeTab poolInfo={poolInfo} />
|
||||
</TabPane>
|
||||
</Tabs>
|
||||
|
@ -177,10 +178,11 @@ function RemoveAssetTab({ poolInfo }: TabParams) {
|
|||
|
||||
return (
|
||||
<form onSubmit={onSubmit}>
|
||||
<Input
|
||||
addonBefore={<>Token Mint Address</>}
|
||||
<MintInPoolSelector
|
||||
poolInfo={poolInfo}
|
||||
label="Token Mint Address"
|
||||
value={address}
|
||||
onChange={(e) => setAddress(e.target.value.trim())}
|
||||
onChange={(value) => setAddress(value)}
|
||||
style={{ marginBottom: 24 }}
|
||||
/>
|
||||
<SubmitButton canSubmit={canSubmit} submitting={submitting} />
|
||||
|
@ -242,10 +244,11 @@ function DepositTab({ poolInfo }: TabParams) {
|
|||
|
||||
return (
|
||||
<form onSubmit={onSubmit}>
|
||||
<Input
|
||||
addonBefore={<>Token Mint Address</>}
|
||||
<MintInPoolSelector
|
||||
poolInfo={poolInfo}
|
||||
label="Token Mint Address"
|
||||
value={address}
|
||||
onChange={(e) => setAddress(e.target.value.trim())}
|
||||
onChange={(value) => setAddress(value)}
|
||||
style={{ marginBottom: 24 }}
|
||||
/>
|
||||
<Input
|
||||
|
@ -318,10 +321,11 @@ function WithdrawTab({ poolInfo }: TabParams) {
|
|||
|
||||
return (
|
||||
<form onSubmit={onSubmit}>
|
||||
<Input
|
||||
addonBefore={<>Token Mint Address</>}
|
||||
<MintInPoolSelector
|
||||
poolInfo={poolInfo}
|
||||
label="Token Mint Address"
|
||||
value={address}
|
||||
onChange={(e) => setAddress(e.target.value.trim())}
|
||||
onChange={(value) => setAddress(value)}
|
||||
style={{ marginBottom: 24 }}
|
||||
/>
|
||||
<Input
|
||||
|
@ -422,3 +426,37 @@ function SubmitButton({ canSubmit, submitting }) {
|
|||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
function MintInPoolSelector({
|
||||
poolInfo,
|
||||
label,
|
||||
value,
|
||||
onChange,
|
||||
style,
|
||||
}: {
|
||||
poolInfo: PoolInfo;
|
||||
label: string;
|
||||
value: string;
|
||||
onChange: (string) => void;
|
||||
style: any;
|
||||
}) {
|
||||
const mintToTickers = useMintToTickers();
|
||||
return (
|
||||
<Input.Group style={style}>
|
||||
<span className="ant-input-group-addon">{label}</span>
|
||||
<Select onChange={onChange} value={value} style={{ width: '100%' }}>
|
||||
{poolInfo.state.assets.map((asset) => (
|
||||
<Option value={asset.mint.toBase58()} key={asset.mint.toBase58()}>
|
||||
{mintToTickers[asset.mint.toBase58()] ? (
|
||||
<>
|
||||
{mintToTickers[asset.mint.toBase58()]} ({asset.mint.toBase58()})
|
||||
</>
|
||||
) : (
|
||||
asset.mint.toBase58()
|
||||
)}
|
||||
</Option>
|
||||
))}
|
||||
</Select>
|
||||
</Input.Group>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -6,9 +6,11 @@ import {
|
|||
parseTokenAccountData,
|
||||
parseTokenMintData,
|
||||
} from '../../../utils/tokens';
|
||||
import { Spin, Tabs } from 'antd';
|
||||
import { Button, Spin, Tabs } from 'antd';
|
||||
import FloatingElement from '../../../components/layout/FloatingElement';
|
||||
import { useTokenAccounts } from '../../../utils/markets';
|
||||
import { MintName } from '../../../components/MintName';
|
||||
import { LinkOutlined } from '@ant-design/icons';
|
||||
|
||||
const { TabPane } = Tabs;
|
||||
|
||||
|
@ -90,7 +92,14 @@ function BalanceItem({ mint, publicKey }: BalanceItemProps) {
|
|||
|
||||
return (
|
||||
<li>
|
||||
{quantityDisplay} {mint.toBase58()} {publicKey.toBase58()}
|
||||
{quantityDisplay} <MintName mint={mint} />{' '}
|
||||
<Button
|
||||
type="link"
|
||||
icon={<LinkOutlined />}
|
||||
href={'https://explorer.solana.com/address/' + publicKey.toBase58()}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
/>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -5,10 +5,11 @@ import { PublicKey } from '@solana/web3.js';
|
|||
import { useAccountInfo } from '../../../utils/connection';
|
||||
import { parseTokenMintData } from '../../../utils/tokens';
|
||||
import { Spin } from 'antd';
|
||||
import { MintName } from '../../../components/MintName';
|
||||
|
||||
interface BasketDisplayProps {
|
||||
poolInfo: PoolInfo;
|
||||
basket: Basket;
|
||||
basket?: Basket | null | undefined;
|
||||
}
|
||||
|
||||
export default function PoolBasketDisplay({
|
||||
|
@ -21,7 +22,7 @@ export default function PoolBasketDisplay({
|
|||
<BasketItem
|
||||
key={index}
|
||||
mint={asset.mint}
|
||||
quantity={basket.quantities[index]}
|
||||
quantity={basket?.quantities[index]}
|
||||
/>
|
||||
))}
|
||||
</ul>
|
||||
|
@ -30,20 +31,20 @@ export default function PoolBasketDisplay({
|
|||
|
||||
interface BasketItemProps {
|
||||
mint: PublicKey;
|
||||
quantity: BN;
|
||||
quantity?: BN;
|
||||
}
|
||||
|
||||
function BasketItem({ mint, quantity }: BasketItemProps) {
|
||||
const [mintAccountInfo] = useAccountInfo(mint);
|
||||
let quantityDisplay = <Spin size="small" />;
|
||||
if (mintAccountInfo) {
|
||||
if (mintAccountInfo && quantity) {
|
||||
const mintInfo = parseTokenMintData(mintAccountInfo.data);
|
||||
quantityDisplay = <>{quantity.toNumber() / 10 ** mintInfo.decimals}</>;
|
||||
}
|
||||
|
||||
return (
|
||||
<li>
|
||||
{quantityDisplay} {mint.toBase58()}
|
||||
{quantityDisplay} <MintName mint={mint} showAddress />
|
||||
</li>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { getPoolBasket, PoolInfo } from '@project-serum/pool';
|
||||
import React from 'react';
|
||||
import FloatingElement from '../../../components/layout/FloatingElement';
|
||||
import { Spin, Typography } from 'antd';
|
||||
import { Typography } from 'antd';
|
||||
import { MintInfo } from '../../../utils/tokens';
|
||||
import { useAsyncData } from '../../../utils/fetch-loop';
|
||||
import { useConnection } from '../../../utils/connection';
|
||||
|
@ -52,11 +52,7 @@ export default function PoolInfoPanel({ poolInfo, mintInfo }: PoolInfoProps) {
|
|||
</Paragraph>
|
||||
<Text>Total assets:</Text>
|
||||
<div>
|
||||
{!totalBasket ? (
|
||||
<Spin />
|
||||
) : (
|
||||
<PoolBasketDisplay poolInfo={poolInfo} basket={totalBasket} />
|
||||
)}
|
||||
<PoolBasketDisplay poolInfo={poolInfo} basket={totalBasket} />
|
||||
</div>
|
||||
</FloatingElement>
|
||||
);
|
||||
|
|
|
@ -10,6 +10,7 @@ import { useConnection } from './connection';
|
|||
import { useAsyncData } from './fetch-loop';
|
||||
import tuple from 'immutable-tuple';
|
||||
import BN from 'bn.js';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
export const ACCOUNT_LAYOUT = BufferLayout.struct([
|
||||
BufferLayout.blob(32, 'mint'),
|
||||
|
@ -147,28 +148,31 @@ export async function getTokenAccountInfo(
|
|||
export function useMintToTickers(): { [mint: string]: string } {
|
||||
const { customMarkets } = useMarket();
|
||||
const [markets] = useAllMarkets(customMarkets);
|
||||
const mintsToTickers = Object.fromEntries(
|
||||
TOKEN_MINTS.map((mint) => [mint.address.toBase58(), mint.name]),
|
||||
);
|
||||
for (let market of markets || []) {
|
||||
const customMarketInfo = customMarkets.find(
|
||||
(customMarket) =>
|
||||
customMarket.address === market.market.address.toBase58(),
|
||||
return useMemo(() => {
|
||||
const mintsToTickers = Object.fromEntries(
|
||||
TOKEN_MINTS.map((mint) => [mint.address.toBase58(), mint.name]),
|
||||
);
|
||||
if (!(market.market.baseMintAddress.toBase58() in mintsToTickers)) {
|
||||
if (customMarketInfo) {
|
||||
mintsToTickers[market.market.baseMintAddress.toBase58()] =
|
||||
customMarketInfo.baseLabel || `${customMarketInfo.name}_BASE`;
|
||||
for (let market of markets || []) {
|
||||
const customMarketInfo = customMarkets.find(
|
||||
(customMarket) =>
|
||||
customMarket.address === market.market.address.toBase58(),
|
||||
);
|
||||
if (!(market.market.baseMintAddress.toBase58() in mintsToTickers)) {
|
||||
if (customMarketInfo) {
|
||||
mintsToTickers[market.market.baseMintAddress.toBase58()] =
|
||||
customMarketInfo.baseLabel || `${customMarketInfo.name}_BASE`;
|
||||
}
|
||||
}
|
||||
if (!(market.market.quoteMintAddress.toBase58() in mintsToTickers)) {
|
||||
if (customMarketInfo) {
|
||||
mintsToTickers[market.market.quoteMintAddress.toBase58()] =
|
||||
customMarketInfo.quoteLabel || `${customMarketInfo.name}_QUOTE`;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!(market.market.quoteMintAddress.toBase58() in mintsToTickers)) {
|
||||
if (customMarketInfo) {
|
||||
mintsToTickers[market.market.quoteMintAddress.toBase58()] =
|
||||
customMarketInfo.quoteLabel || `${customMarketInfo.name}_QUOTE`;
|
||||
}
|
||||
}
|
||||
}
|
||||
return mintsToTickers;
|
||||
return mintsToTickers;
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [markets?.length, customMarkets.length]);
|
||||
}
|
||||
|
||||
const _VERY_SLOW_REFRESH_INTERVAL = 5000 * 1000;
|
||||
|
|
|
@ -132,7 +132,7 @@ export function useListener(emitter, eventName) {
|
|||
}, [emitter, eventName]);
|
||||
}
|
||||
|
||||
export function abbreviateAddress(address, size = 4) {
|
||||
export function abbreviateAddress(address: PublicKey, size = 4) {
|
||||
const base58 = address.toBase58();
|
||||
return base58.slice(0, size) + '…' + base58.slice(-size);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue