import Input from '@components/forms/Input' import Label from '@components/forms/Label' import Button, { IconButton } from '@components/shared/Button' import { ChangeEvent, useCallback, useEffect, useMemo, useState } from 'react' import mangoStore, { CLUSTER } from '@store/mangoStore' import { Token } from 'types/jupiter' import { handleGetRoutes } from '@components/swap/useQuoteRoutes' import { JUPITER_PRICE_API_MAINNET, USDC_MINT } from 'utils/constants' import { Connection, PublicKey, SYSVAR_RENT_PUBKEY, Transaction, } from '@solana/web3.js' import { useWallet } from '@solana/wallet-adapter-react' import { OPENBOOK_PROGRAM_ID, toNative, toUiDecimals, } from '@blockworks-foundation/mango-v4' import { MANGO_DAO_FAST_LISTING_GOVERNANCE, MANGO_DAO_FAST_LISTING_WALLET, MANGO_DAO_WALLET, MANGO_DAO_WALLET_GOVERNANCE, MANGO_MINT_DECIMALS, } from 'utils/governance/constants' import { ArrowLeftIcon, ChevronDownIcon, ExclamationCircleIcon, } from '@heroicons/react/20/solid' import { createProposal } from 'utils/governance/instructions/createProposal' import GovernanceStore from '@store/governanceStore' import { notify } from 'utils/notifications' import { useTranslation } from 'next-i18next' import { emptyPk } from 'utils/governance/accounts/vsrAccounts' import Loading from '@components/shared/Loading' import ListingSuccess from '../ListingSuccess' import InlineNotification from '@components/shared/InlineNotification' import { Disclosure } from '@headlessui/react' import { abbreviateAddress } from 'utils/formatting' import { formatNumericValue } from 'utils/numbers' import useMangoGroup from 'hooks/useMangoGroup' import { getBestMarket, getOracle } from 'utils/governance/listingTools' import { fmtTokenAmount, tryGetPubKey } from 'utils/governance/tools' import OnBoarding from '../OnBoarding' import CreateOpenbookMarketModal from '@components/modals/CreateOpenbookMarketModal' import useJupiterMints from 'hooks/useJupiterMints' import CreateSwitchboardOracleModal from '@components/modals/CreateSwitchboardOracleModal' import { LISTING_PRESETS, calculateMarketTradingParams, LISTING_PRESET, getPresetWithAdjustedDepositLimit, LISTING_PRESETS_KEY, } from '@blockworks-foundation/mango-v4-settings/lib/helpers/listingTools' import Checkbox from '@components/forms/Checkbox' import { BN } from '@coral-xyz/anchor' import Select from '@components/forms/Select' import { WRAPPED_SOL_MINT } from '@metaplex-foundation/js' import { struct, u8, publicKey, u64, option } from '@raydium-io/raydium-sdk' import * as toml from '@iarna/toml' const feeFields = [u64('denominator'), u64('numerator')] const StakePoolLayout = struct([ u8('accountType'), publicKey('manager'), publicKey('staker'), publicKey('stakeDepositAuthority'), u8('stakeWithdrawBumpSeed'), publicKey('validatorList'), publicKey('reserveStake'), publicKey('poolMint'), publicKey('managerFeeAccount'), publicKey('tokenProgramId'), u64('totalLamports'), u64('poolTokenSupply'), u64('lastUpdateEpoch'), struct( [u64('unixTimestamp'), u64('epoch'), publicKey('custodian')], 'lockup', ), struct(feeFields, 'epochFee'), option(struct(feeFields), 'nextEpochFee'), option(publicKey(), 'preferredDepositValidatorVoteAddress'), option(publicKey(), 'preferredWithdrawValidatorVoteAddress'), struct(feeFields, 'stakeDepositFee'), struct(feeFields, 'stakeWithdrawalFee'), option(struct(feeFields), 'nextStakeWithdrawalFee'), u8('stakeReferralFee'), option(publicKey(), 'solDepositAuthority'), struct(feeFields, 'solDepositFee'), u8('solReferralFee'), option(publicKey(), 'solWithdrawAuthority'), struct(feeFields, 'solWithdrawalFee'), option(struct(feeFields), 'nextSolWithdrawalFee'), u64('lastEpochPoolTokenSupply'), u64('lastEpochTotalLamports'), ]) type FormErrors = Partial> type TokenListForm = { mintPk: string oraclePk: string name: string tokenIndex: number openBookMarketExternalPk: string baseBankPk: string quoteBankPk: string marketIndex: number openBookProgram: string marketName: string proposalTitle: string proposalDescription: string listForSwapOnly: boolean fastListing: boolean } const defaultTokenListFormValues: TokenListForm = { mintPk: '', oraclePk: '', name: '', tokenIndex: 0, openBookMarketExternalPk: '', baseBankPk: '', quoteBankPk: '', marketIndex: 0, openBookProgram: '', marketName: '', proposalTitle: '', proposalDescription: '', listForSwapOnly: false, fastListing: false, } const TWENTY_K_USDC_BASE = '20000000000' const ListToken = ({ goBack }: { goBack: () => void }) => { //do not deconstruct wallet is used for anchor to sign const wallet = useWallet() const { jupiterTokens } = useJupiterMints() const connection = mangoStore((s) => s.connection) const client = mangoStore((s) => s.client) const { group } = useMangoGroup() const voter = GovernanceStore((s) => s.voter) const vsrClient = GovernanceStore((s) => s.vsrClient) const governances = GovernanceStore((s) => s.governances) const loadingRealm = GovernanceStore((s) => s.loadingRealm) const fee = mangoStore((s) => s.priorityFee) const loadingVoter = GovernanceStore((s) => s.loadingVoter) const proposals = GovernanceStore((s) => s.proposals) const refetchProposals = GovernanceStore((s) => s.refetchProposals) const getCurrentVotingPower = GovernanceStore((s) => s.getCurrentVotingPower) const connectionContext = GovernanceStore((s) => s.connectionContext) const { t } = useTranslation(['governance']) const [advForm, setAdvForm] = useState({ ...defaultTokenListFormValues, }) const [loadingListingParams, setLoadingListingParams] = useState(false) const [formErrors, setFormErrors] = useState({}) const [priceImpact, setPriceImpact] = useState(0) const [currentTokenInfo, setCurrentTokenInfo] = useState< Token | null | undefined >(null) const [baseTokenPrice, setBaseTokenPrice] = useState(0) const [proposalPk, setProposalPk] = useState(null) const [mint, setMint] = useState('') const [creatingProposal, setCreatingProposal] = useState(false) const [createOpenbookMarketModal, setCreateOpenbookMarket] = useState(false) const [orcaPoolAddress, setOrcaPoolAddress] = useState('') const [raydiumPoolAddress, setRaydiumPoolAddress] = useState('') const [oracleModalOpen, setOracleModalOpen] = useState(false) const [isSolPool, setIsSolPool] = useState(false) const presets = LISTING_PRESETS const [proposedPresetTargetAmount, setProposedProposedTargetAmount] = useState(0) const [preset, setPreset] = useState(presets.UNTRUSTED) const [isLST, setIsLST] = useState(false) const [lstStakePoolAddress, setLstStakePoolAddress] = useState('') const QUOTE_MINT = isLST ? WRAPPED_SOL_MINT.toBase58() : USDC_MINT const proposedPreset = getPresetWithAdjustedDepositLimit( preset, baseTokenPrice, currentTokenInfo?.decimals || 0, ) const suggestedPreset = Object.values(presets).find( (x) => x.preset_target_amount <= proposedPresetTargetAmount, ) || presets.UNTRUSTED useEffect(() => { if (advForm.fastListing) { setAdvForm((prevState) => ({ ...prevState, fastListing: false, })) } }, [preset]) useEffect(() => { const handleOracleUpdate = async () => { if (currentTokenInfo) { setLoadingListingParams(true) const { oraclePk } = await getOracle({ baseSymbol: currentTokenInfo.symbol, quoteSymbol: 'usd', connection, targetAmount: proposedPreset.preset_target_amount, }) setAdvForm((prevState) => ({ ...prevState, oraclePk: oraclePk || '', })) setLoadingListingParams(false) } } handleOracleUpdate() }, [proposedPreset.preset_name]) const quoteBank = group?.getFirstBankByMint(new PublicKey(QUOTE_MINT)) const minVoterWeight = useMemo( () => governances ? governances[MANGO_DAO_WALLET_GOVERNANCE.toBase58()].account.config .minCommunityTokensToCreateProposal : new BN(0), [governances], ) as BN const mintVoterWeightNumber = governances ? fmtTokenAmount(minVoterWeight, MANGO_MINT_DECIMALS) : 0 const tradingParams = useMemo(() => { if (quoteBank && currentTokenInfo) { return calculateMarketTradingParams( baseTokenPrice, quoteBank.uiPrice, currentTokenInfo.decimals, quoteBank.mintDecimals, ) } return { baseLots: 0, quoteLots: 0, minOrderValue: 0, baseLotExponent: 0, quoteLotExponent: 0, minOrderSize: 0, priceIncrement: 0, priceIncrementRelative: 0, } }, [quoteBank, currentTokenInfo, baseTokenPrice]) const handleSetAdvForm = ( propertyName: string, value: string | number | boolean, ) => { setFormErrors({}) setAdvForm({ ...advForm, [propertyName]: value }) } const getListingParams = useCallback( async ( tokenInfo: Token, quoteMint: string, targetAmount: number, isLST: boolean, ) => { setLoadingListingParams(true) const [{ oraclePk }, marketPk] = await Promise.all([ getOracle({ baseSymbol: tokenInfo.symbol, quoteSymbol: 'usd', connection, targetAmount: targetAmount, }), getBestMarket({ baseMint: mint, quoteMint: quoteMint, cluster: CLUSTER, connection, }), ]) const index = proposals ? Object.values(proposals).length : 0 const bankNum = 0 const [baseBank] = PublicKey.findProgramAddressSync( [ Buffer.from('Bank'), group!.publicKey.toBuffer(), new BN(index).toArrayLike(Buffer, 'le', 2), new BN(bankNum).toArrayLike(Buffer, 'le', 4), ], client.programId, ) setAdvForm({ ...advForm, oraclePk: oraclePk || '', mintPk: mint, name: tokenInfo.symbol, tokenIndex: index, openBookProgram: OPENBOOK_PROGRAM_ID[CLUSTER].toBase58(), marketName: `${tokenInfo.symbol}/${isLST ? 'SOL' : 'USDC'}`, baseBankPk: baseBank.toBase58(), quoteBankPk: group! .getFirstBankByMint(new PublicKey(quoteMint)) .publicKey.toBase58(), marketIndex: index, openBookMarketExternalPk: marketPk?.toBase58() || '', proposalTitle: `List ${tokenInfo.symbol} on Mango-v4`, listForSwapOnly: false, fastListing: false, }) setLoadingListingParams(false) }, [connection, mint, proposals, group, client.programId, advForm, quoteBank], ) const handleGetRoutesWithFixedArgs = useCallback( ( amount: number, tokenMint: PublicKey, mode: 'ExactIn' | 'ExactOut', onlyDirect = false, ) => { const SLIPPAGE_BPS = 50 const walletForCheck = wallet.publicKey ? wallet.publicKey?.toBase58() : emptyPk return handleGetRoutes( USDC_MINT, tokenMint.toBase58(), toNative(amount, 6).toNumber(), SLIPPAGE_BPS, mode, walletForCheck, undefined, // mangoAccount onlyDirect ? 'JUPITER_DIRECT' : 'JUPITER', connection, undefined, ) }, [wallet.publicKey, connection], ) const handleLiquidityCheck = useCallback( async (tokenMint: PublicKey, isLST: boolean) => { if (isLST) { const targetAmount = 250000 setProposedProposedTargetAmount(targetAmount) setPreset(presets.asset_250) setPriceImpact(0.9) return targetAmount } try { const swaps = await Promise.all([ handleGetRoutesWithFixedArgs(5000000, tokenMint, 'ExactIn'), handleGetRoutesWithFixedArgs(250000, tokenMint, 'ExactIn'), handleGetRoutesWithFixedArgs(100000, tokenMint, 'ExactIn'), handleGetRoutesWithFixedArgs(20000, tokenMint, 'ExactIn'), handleGetRoutesWithFixedArgs(10000, tokenMint, 'ExactIn'), handleGetRoutesWithFixedArgs(5000, tokenMint, 'ExactIn'), handleGetRoutesWithFixedArgs(3000, tokenMint, 'ExactIn'), handleGetRoutesWithFixedArgs(1000, tokenMint, 'ExactIn'), handleGetRoutesWithFixedArgs(5000000, tokenMint, 'ExactOut'), handleGetRoutesWithFixedArgs(250000, tokenMint, 'ExactOut'), handleGetRoutesWithFixedArgs(100000, tokenMint, 'ExactOut'), handleGetRoutesWithFixedArgs(20000, tokenMint, 'ExactOut'), handleGetRoutesWithFixedArgs(10000, tokenMint, 'ExactOut'), handleGetRoutesWithFixedArgs(5000, tokenMint, 'ExactOut'), handleGetRoutesWithFixedArgs(3000, tokenMint, 'ExactOut'), handleGetRoutesWithFixedArgs(1000, tokenMint, 'ExactOut'), ]) const bestRoutesSwaps = swaps .filter((x) => x.bestRoute) .map((x) => x.bestRoute!) const averageSwaps = bestRoutesSwaps.reduce( (acc: { amount: string; priceImpactPct: number }[], val) => { if (val.swapMode === 'ExactIn') { const exactOutRoute = bestRoutesSwaps.find( (x) => x.outAmount === val.inAmount && x.swapMode === 'ExactOut', ) acc.push({ amount: val.inAmount.toString(), priceImpactPct: exactOutRoute?.priceImpactPct ? (Number(val.priceImpactPct) + Number(exactOutRoute.priceImpactPct)) / 2 : Number(val.priceImpactPct), }) } return acc }, [], ) const midTierCheck = averageSwaps.find( (x) => x.amount === TWENTY_K_USDC_BASE, ) const indexForTargetAmount = averageSwaps.findIndex( (x) => x?.priceImpactPct && x?.priceImpactPct * 100 < 1, ) const targetAmount = indexForTargetAmount > -1 ? toUiDecimals(new BN(averageSwaps[indexForTargetAmount].amount), 6) : 0 setProposedProposedTargetAmount(targetAmount) setPriceImpact(midTierCheck ? midTierCheck.priceImpactPct * 100 : 100) handleGetPoolParams(targetAmount, tokenMint) return targetAmount } catch (e) { notify({ title: t('liquidity-check-error'), description: `${e}`, type: 'error', }) return 0 } }, [t, handleGetRoutesWithFixedArgs], ) const handleGetPoolParams = async ( targetAmount: number, tokenMint: PublicKey, ) => { const swaps = await handleGetRoutesWithFixedArgs( targetAmount ? targetAmount : 100, tokenMint, 'ExactIn', true, ) const swapInfos = swaps?.bestRoute?.routePlan?.map((x) => x.swapInfo) const orcaPool = swapInfos?.find( (x) => x.label?.toLowerCase().includes('orca') || x.label?.toLowerCase().includes('whirlpool'), ) const raydiumPool = swapInfos?.find( (x) => x.label?.toLowerCase().includes('raydium'), ) if (!orcaPool?.ammKey && !raydiumPool?.ammKey) { try { const dex = await fetch( `https://api.dexscreener.com/latest/dex/search?q=${tokenMint.toBase58()}`, ) const resp = await dex.json() if (!resp?.pairs?.length) { return } const bestSolPool = resp.pairs.find( // eslint-disable-next-line @typescript-eslint/no-explicit-any (x: any) => x.quoteToken.address === WRAPPED_SOL_MINT.toBase58() || x.baseToken.address === WRAPPED_SOL_MINT.toBase58(), ) if (bestSolPool?.dexId.includes('raydium')) { setRaydiumPoolAddress(bestSolPool.pairAddress) } if (bestSolPool?.dexId.includes('orca')) { setOrcaPoolAddress(bestSolPool.pairAddress) } setIsSolPool(true) return } catch (e) { console.log(e) } } setOrcaPoolAddress(orcaPool?.ammKey || '') setRaydiumPoolAddress(raydiumPool?.ammKey || '') } const handleTokenFind = useCallback(async () => { cancel() if (!tryGetPubKey(mint)) { notify({ title: t('enter-valid-token-mint'), type: 'error', }) return } const tokenInfo = jupiterTokens.find((x) => x.address === mint) const priceInfo = await ( await fetch(`${JUPITER_PRICE_API_MAINNET}price?ids=${mint}`) ).json() //Note: if listing asset that don't have price on jupiter remember to edit this 0 to real price //in case of using 0 openbook market can be wrongly configured ignore if openbook market is existing setBaseTokenPrice(priceInfo.data[mint]?.price || 0) setCurrentTokenInfo(tokenInfo) if (tokenInfo) { const lstPool = await getLstStakePool(connection, mint) const targetAmount = await handleLiquidityCheck( new PublicKey(mint), !!lstPool, ) getListingParams( tokenInfo, lstPool ? WRAPPED_SOL_MINT.toBase58() : USDC_MINT, lstPool ? targetAmount : 0, !!lstPool, ) setIsLST(!!lstPool) setLstStakePoolAddress(lstPool) } }, [getListingParams, handleLiquidityCheck, jupiterTokens, mint, t]) const cancel = () => { setCurrentTokenInfo(null) setPriceImpact(0) setAdvForm({ ...defaultTokenListFormValues }) setProposalPk(null) setOrcaPoolAddress('') setRaydiumPoolAddress('') setProposedProposedTargetAmount(0) setBaseTokenPrice(0) setIsLST(false) } const isFormValid = useCallback( (advForm: TokenListForm) => { const invalidFields: FormErrors = {} setFormErrors({}) const pubkeyFields: (keyof TokenListForm)[] = [ 'openBookProgram', 'quoteBankPk', 'baseBankPk', 'openBookMarketExternalPk', 'oraclePk', ] const numberFields: (keyof TokenListForm)[] = ['tokenIndex'] const textFields: (keyof TokenListForm)[] = [ 'marketName', 'proposalTitle', ] for (const key of pubkeyFields) { if (!tryGetPubKey(advForm[key] as string)) { if (advForm.listForSwapOnly && key === 'openBookMarketExternalPk') { continue } invalidFields[key] = t('invalid-pk') } } for (const key of numberFields) { if (isNaN(advForm[key] as number) || advForm[key] === '') { invalidFields[key] = t('invalid-num') } } for (const key of textFields) { if (!advForm[key]) { invalidFields[key] = t('field-req') } } if (Object.keys(invalidFields).length) { setFormErrors(invalidFields) } return invalidFields }, [t], ) const propose = useCallback(async () => { const invalidFields = isFormValid(advForm) if (Object.keys(invalidFields).length) { return } if (!wallet.publicKey || !vsrClient || !connectionContext) return await getCurrentVotingPower(wallet.publicKey, vsrClient, connectionContext) if (voter.voteWeight.cmp(minVoterWeight) === -1) { notify({ title: `${t('on-boarding-description', { amount: formatNumericValue(mintVoterWeightNumber), })} ${t('mango-governance')}`, type: 'error', }) return } const newProposals = await refetchProposals() const index = proposals ? Object.values(newProposals).length : 0 const mint = new PublicKey(advForm.mintPk) const proposalTx = [] if (Object.keys(proposedPreset).length && !advForm.fastListing) { const registerTokenIx = await client!.program.methods .tokenRegister( Number(index), advForm.name, { confFilter: Number(proposedPreset.oracleConfFilter), maxStalenessSlots: proposedPreset.maxStalenessSlots === -1 ? null : proposedPreset.maxStalenessSlots, }, { adjustmentFactor: Number(proposedPreset.adjustmentFactor), util0: Number(proposedPreset.util0), rate0: Number(proposedPreset.rate0), util1: Number(proposedPreset.util1), rate1: Number(proposedPreset.rate1), maxRate: Number(proposedPreset.maxRate), }, Number(proposedPreset.loanFeeRate), Number(proposedPreset.loanOriginationFeeRate), Number(proposedPreset.maintAssetWeight), Number(proposedPreset.initAssetWeight), Number(proposedPreset.maintLiabWeight), Number(proposedPreset.initLiabWeight), Number(proposedPreset.liquidationFee), Number(proposedPreset.stablePriceDelayIntervalSeconds), Number(proposedPreset.stablePriceDelayGrowthLimit), Number(proposedPreset.stablePriceGrowthLimit), Number(proposedPreset.minVaultToDepositsRatio), new BN(proposedPreset.netBorrowLimitWindowSizeTs), new BN(proposedPreset.netBorrowLimitPerWindowQuote), Number(proposedPreset.borrowWeightScaleStartQuote), Number(proposedPreset.depositWeightScaleStartQuote), Number(proposedPreset.reduceOnly), Number(proposedPreset.tokenConditionalSwapTakerFeeRate), Number(proposedPreset.tokenConditionalSwapMakerFeeRate), Number(proposedPreset.flashLoanSwapFeeRate), Number(proposedPreset.interestCurveScaling), Number(proposedPreset.interestTargetUtilization), proposedPreset.groupInsuranceFund, new BN(proposedPreset.depositLimit.toString()), Number(proposedPreset.zeroUtilRate), Number(proposedPreset.platformLiquidationFee), proposedPreset.disableAssetLiquidation, Number( isLST ? 0.000027396999939810485 : proposedPreset.collateralFeePerDay, ), ) .accounts({ fallbackOracle: PublicKey.default, admin: MANGO_DAO_WALLET, group: group!.publicKey, mint: mint, oracle: new PublicKey(advForm.oraclePk), payer: MANGO_DAO_WALLET, rent: SYSVAR_RENT_PUBKEY, }) .instruction() proposalTx.push(registerTokenIx) } else { const trustlessIx = await client!.program.methods .tokenRegisterTrustless(Number(index), advForm.name) .accounts({ fallbackOracle: PublicKey.default, mint: mint, payer: MANGO_DAO_FAST_LISTING_WALLET, rent: SYSVAR_RENT_PUBKEY, oracle: new PublicKey(advForm.oraclePk), admin: MANGO_DAO_FAST_LISTING_WALLET, group: group!.publicKey, }) .instruction() proposalTx.push(trustlessIx) } if (!advForm.listForSwapOnly) { const registerMarketix = await client!.program.methods .serum3RegisterMarket( Number(index), advForm.marketName, proposedPreset.oraclePriceBand, ) .accounts({ group: group!.publicKey, admin: advForm.fastListing ? MANGO_DAO_FAST_LISTING_WALLET : MANGO_DAO_WALLET, serumProgram: new PublicKey(advForm.openBookProgram), serumMarketExternal: new PublicKey(advForm.openBookMarketExternalPk), baseBank: new PublicKey(advForm.baseBankPk), quoteBank: new PublicKey(advForm.quoteBankPk), payer: advForm.fastListing ? MANGO_DAO_FAST_LISTING_WALLET : MANGO_DAO_WALLET, }) .instruction() proposalTx.push(registerMarketix) } const walletSigner = wallet as never setCreatingProposal(true) try { const simTransaction = new Transaction({ feePayer: wallet.publicKey }) simTransaction.add(...proposalTx) const simulation = await connection.simulateTransaction(simTransaction) if (!simulation.value.err) { const proposalAddress = await createProposal( connection, client, walletSigner, advForm.fastListing ? MANGO_DAO_FAST_LISTING_GOVERNANCE : MANGO_DAO_WALLET_GOVERNANCE, voter.tokenOwnerRecord!, advForm.proposalTitle, advForm.proposalDescription, index, proposalTx, vsrClient, fee, ) setProposalPk(proposalAddress.toBase58()) } else { throw simulation.value.logs } } catch (e) { console.log(e) notify({ title: t('error-proposal-creation'), description: `${e}`, type: 'error', }) } setCreatingProposal(false) }, [ isFormValid, advForm, wallet, vsrClient, connectionContext, getCurrentVotingPower, voter.voteWeight, voter.tokenOwnerRecord, minVoterWeight, refetchProposals, proposals, proposedPreset, t, mintVoterWeightNumber, client, group, connection, fee, isLST, ]) const closeCreateOpenBookMarketModal = () => { setCreateOpenbookMarket(false) if (currentTokenInfo && proposedPresetTargetAmount) { getListingParams( currentTokenInfo, QUOTE_MINT, isLST ? proposedPresetTargetAmount : 0, isLST, ) } } const closeCreateOracleModal = (oraclePk?: PublicKey) => { setOracleModalOpen(false) if (oraclePk) { handleSetAdvForm('oraclePk', oraclePk.toBase58()) } } return (

{t('list-token')}

{!currentTokenInfo ? ( <>
) : ( <> {proposalPk ? ( ) : ( <>

{t('token-details')}

{t('name')}

{currentTokenInfo?.name}

{t('symbol')}

{currentTokenInfo?.symbol}

{isLST && (

LST detected

Quote token SOL

)}

{t('mint')}

{abbreviateAddress( new PublicKey(currentTokenInfo?.address), )}

{priceImpact > 2 && (
)}
{({ open }) => ( <>
{t('adv-fields')}

{t('tier')} (suggested:{' '} {suggestedPreset.preset_name})

{!advForm.listForSwapOnly && (
)}
{proposedPreset.preset_key === 'UNTRUSTED' && (
)}
)}
    {!advForm.openBookMarketExternalPk && !advForm.listForSwapOnly && !loadingListingParams ? (
  1. } type="error" />
{createOpenbookMarketModal ? ( ) : null} ) : null} {!advForm.oraclePk && !loadingListingParams && proposedPreset.oracle !== 0 ? (
  • setOracleModalOpen(true)} className="cursor-pointer underline" > {t('cant-list-oracle-not-found-switch')} } type="error" />
  • ) : null}
    {wallet.connected ? ( ) : ( )}
    )} )} ) } export default ListToken const getLstStakePool = async (connection: Connection, mint: string) => { try { let poolAddress = '' //backup intial list let addresses = [ 'GUAMR8ciiaijraJeLDEDrFVaueLm9YzWWY9R7CBPL9rA', 'F8h46pYkaqPJNP2MRkUUUtRkf8efCkpoqehn9g1bTTm7', '5oc4nmbNTda9fx8Tw57ShLD132aqDK65vuHH4RU1K4LZ', 'GutG5bcmEZw15WmPHNVMWHU77c6t8CEinUEdPLYz3doa', '9Z8yimuc3bQCWLDyMhe6jfWqNk9EggyJZUo8TLnYsqhN', 'GM7TwD34n8HmDP9XcT6bD3JJuNniKJkrKQinHqmqHarz', '8VpRhuxa7sUUepdY3kQiTmX9rS5vx4WgaXiAnXq4KCtr', '4mBwcXKJN2vz6MJikNTgVBSY5vYnyjZk7txd8j3K46Ei', 'phasejkG1akKgqkLvfWzWY17evnH6mSWznnUspmpyeG', '4fdMvFuyNboQ5Kr93X16f1tFcTeEkvfNwNAeSrzY3afb', 'EVXQHaLSJyUNrnBGfXUnvEi4DvVz4UJ3GnoKGVQVxrjr', '5FYTvZgc7QEGZSDmbJn5hrtjtRtyFZo5vR7gL1jJYanE', 'DfiQgSvpW3Dy4gKfhtdHnWGHwFUrE8exvaxqjtMtAVxk', '4dZDUL3BFJUFeqS3Y3cwkc84Rs6mgVHRYGt1LJvhooW4', 'AZGSr2fUyKkPLMhAW6WUEKEsQiRMAFKf8Fjnt4MFFaGv', 'ArAQfbzsdotoKB5jJcZa3ajQrrPcWr2YQoDAEAiFxJAC', '9mhGNSPArRMHpLDMSmxAvuoizBqtBGqYdT8WGuqgxNdn', 'AwDeTcW6BovNYR34Df1TPm4bFwswa4CJY4YPye2LXtPS', '8Dv3hNYcEWEaa4qVx9BTN1Wfvtha1z8cWDUXb7KVACVe', '6e2LpgytfG3RqMdYuPr3dnedv6bmHQUk9hH9h2fzVk9o', 'ECRqn7gaNASuvTyC5xfCUjehWZCSowMXstZiM5DNweyB', 'GZDX5JYXDzCEDL3kybhjN7PSixL4ams3M2G4CvWmMmm5', 'LW3qEdGWdVrxNgxSXW8vZri7Jifg4HuKEQ1UABLxs3C', '2jjK1MsLgsPgVjnp97HUJeovNj3jp4XgyQ3nuiWMwiS8', '8WHCJsUduwDBhPL9uVADQSdWkUi2LPZNFAMyX1n2HGMD', '3wK2g8ZdzAH8FJ7PKr2RcvGh7V9VYson5hrVsJM5Lmws', 'CgntPoLka5pD5fesJYhGmUCF8KU1QS1ZmZiuAuMZr2az', '2qyEeSAWKfU18AFthrF7JA8z8ZCi1yt76Tqs917vwQTV', 'Fu9BYC6tWBo1KMKaP3CFoKfRhqv9akmy3DuYwnCyWiyC', 'stk9ApL5HeVAwPLr3TLhDXdZS8ptVu7zp6ov8HFDuMi', '7ge2xKsZXmqPxa3YmXxXmzCp9Hc2ezrTxh6PECaxCwrL', 'Jito4APyf642JPZPx3hGc6WWJ8zPKtRbRs4P815Awbb', 'CtMyWsrUtAwXWiGr9WjHT5fC3p3fgV8cyGpLTo2LJzG1', 'DqhH94PjkZsjAqEze2BEkWhFQJ6EyU6MdtMphMgnXqeK', 'DxRFpqBQBC2nKcvh14gD1eizCj9Xi7ruMR3nCR3Hvw8f', 'edgejNWAqkePLpi5sHRxT9vHi7u3kSHP9cocABPKiWZ', 'G9WdMBxWSo1X3fKxbuyGrv1nGXrVqGg5zBKAkBFkb37g', 'CWM1VcNPd2A5WF2x2mmEUCgA1PGSKNZCGAH5GsoQw7h8', '2RUTyfN8iq7Hsd2s9rLgrRT9VhHLuqkx2mGNgbuzbhTc', '7qJ34Vq7nGZvk5YExkJsDZB6to6vz9RpcPmNEK84HjrV', '4qYufFsPQETukkXd5z9fxDsdwm8AEaSqzYpuzmZzCJxR', '9pffpv2w65TSeZpD988hAjvvzUiF1KZN1Swx5j2zPCdy', 'BmEgS5XpWJJDqT3FVfB6ZmoELQrWkJxDXo3cNoJVsNFK', '5bzgfi7nidWWrp3DCwPwLzepw7PGgawRmMH9tqqXMZRj', 'Fvy5L7f3rduuYfRf9GR9fDqEgmJkYagDPh3Ddkp5jcoP', '9dP2MvpoFuVgW31NbwyRJzybcjH2gMZS5YkSWEC7NDhD', 'GrrASJmjz19gHDsUUGv9y3gtRAwYJcdrtFESCRAosd44', 'EYwMHf8Ajnpvy3PqMMkq1MPkTyhCsBEesXFgnK9BZfmu', '6LXCxeyQZqdAL4yLCtgATFYF6dcayWvsiwjtBFYVfb1N', '4gT1GaFtJK5pnX3CnjnSYwy8VUV9UdmozoQV9GCNk9RQ', 'GEGRQNw17Y5s44dRH69sk8bvhyj3i6VwgqGmN1MBHKHp', '9j2mFdABTCCnWnzLtpMjp86AEcm4e3XistVeuujds7Au', 'DYuSikgwzHidFo2b8jqrViW1psAb7hpawJnszBothRzp', 'pjwKqvtt4ij6VJW4HxNxSaufSrkWHRc6iCTHoC4gFs4', '5ocnV1qiCgaQR8Jb8xWnVbApfaygJ8tNoZfgPwsgx9kx', ] try { const tomlFile = await fetch( `https://raw.githubusercontent.com/${'igneous-labs'}/${'sanctum-lst-list'}/master/sanctum-lst-list.toml`, ) const tomlText = await tomlFile.text() const tomlData = toml.parse(tomlText) as unknown as { sanctum_lst_list: { pool: { pool: string } }[] } addresses = [ ...tomlData.sanctum_lst_list .map((x) => tryGetPubKey(x.pool.pool)?.toBase58()) .filter((x) => x), ] as string[] } catch (e) { console.log(e) } //remove duplicates const possibleStakePoolsAddresses = [...new Set(addresses)].map( (x) => new PublicKey(x), ) const accounts = await connection.getMultipleAccountsInfo( possibleStakePoolsAddresses, ) for (const idx in accounts) { try { const acc = accounts[idx] const stakeAddressPk = possibleStakePoolsAddresses[idx] if (acc?.data) { const decoded = StakePoolLayout.decode(acc?.data) if (decoded.poolMint.toBase58() === mint && stakeAddressPk) { poolAddress = stakeAddressPk?.toBase58() break } } // eslint-disable-next-line no-empty } catch (e) {} } return poolAddress } catch (e) { console.log(e) return '' } }