Changeset View
Changeset View
Standalone View
Standalone View
cashtab/src/components/Etokens/Token/index.tsx
| // Copyright (c) 2024 The Bitcoin developers | // Copyright (c) 2024 The Bitcoin developers | ||||
| // Distributed under the MIT software license, see the accompanying | // Distributed under the MIT software license, see the accompanying | ||||
| // file COPYING or http://www.opensource.org/licenses/mit-license.php. | // file COPYING or http://www.opensource.org/licenses/mit-license.php. | ||||
| import React, { useState, useEffect, useContext } from 'react'; | import React, { useState, useEffect, useContext, useRef } from 'react'; | ||||
| import { Link, useParams } from 'react-router'; | import { Link, useParams } from 'react-router'; | ||||
| import { WalletContext, isWalletContextLoaded } from 'wallet/context'; | import { WalletContext, isWalletContextLoaded } from 'wallet/context'; | ||||
| import PrimaryButton, { | import PrimaryButton, { | ||||
| SecondaryButton, | SecondaryButton, | ||||
| IconButton, | IconButton, | ||||
| CopyIconButton, | CopyIconButton, | ||||
| } from 'components/Common/Buttons'; | } from 'components/Common/Buttons'; | ||||
| import { SwitchLabel, Info, Alert } from 'components/Common/Atoms'; | import { Info, Alert } from 'components/Common/Atoms'; | ||||
| import Spinner from 'components/Common/Spinner'; | import Spinner from 'components/Common/Spinner'; | ||||
| import { Event } from 'components/Common/GoogleAnalytics'; | import { Event } from 'components/Common/GoogleAnalytics'; | ||||
| import ApiError from 'components/Common/ApiError'; | import ApiError from 'components/Common/ApiError'; | ||||
| import { | import { | ||||
| isValidTokenSendOrBurnAmount, | isValidTokenSendOrBurnAmount, | ||||
| parseAddressInput, | parseAddressInput, | ||||
| isValidTokenMintAmount, | isValidTokenMintAmount, | ||||
| getXecListPriceError, | getXecListPriceError, | ||||
| Show All 36 Lines | import { | ||||
| ModalInput, | ModalInput, | ||||
| InputFlex, | InputFlex, | ||||
| ListPriceInput, | ListPriceInput, | ||||
| Slider, | Slider, | ||||
| LabelAndInputFlex, | LabelAndInputFlex, | ||||
| SliderLabel, | SliderLabel, | ||||
| } from 'components/Common/Inputs'; | } from 'components/Common/Inputs'; | ||||
| import { QuestionIcon } from 'components/Common/CustomIcons'; | import { QuestionIcon } from 'components/Common/CustomIcons'; | ||||
| import Switch from 'components/Common/Switch'; | import { ReactComponent as DownArrow } from 'assets/drop-down-arrow.svg'; | ||||
| import { | import { | ||||
| DataAndQuestionButton, | DataAndQuestionButton, | ||||
| TokenIconExpandButton, | TokenIconExpandButton, | ||||
| SendTokenForm, | SendTokenForm, | ||||
| SendTokenFormRow, | SendTokenFormRow, | ||||
| InputRow, | InputRow, | ||||
| SectionCtn, | |||||
| SectionLabel, | |||||
| TokenStatsTable, | TokenStatsTable, | ||||
| TokenStatsRow, | TokenStatsRow, | ||||
| TokenStatsCol, | TokenStatsCol, | ||||
| TokenUrlCol, | TokenUrlCol, | ||||
| TokenStatsTableRow, | TokenStatsTableRow, | ||||
| SwitchHolder, | TokenInfoRow, | ||||
| NftTitle, | NftTitle, | ||||
| NftTable, | NftTable, | ||||
| NftRow, | NftRow, | ||||
| NftCol, | NftCol, | ||||
| NftTokenIdAndCopyIcon, | NftTokenIdAndCopyIcon, | ||||
| NftNameTitle, | NftNameTitle, | ||||
| NftCollectionTitle, | NftCollectionTitle, | ||||
| ListPricePreview, | ListPricePreview, | ||||
| AgoraPreviewParagraph, | AgoraPreviewParagraph, | ||||
| AgoraPreviewTable, | AgoraPreviewTable, | ||||
| AgoraPreviewRow, | AgoraPreviewRow, | ||||
| AgoraPreviewLabel, | AgoraPreviewLabel, | ||||
| AgoraPreviewCol, | AgoraPreviewCol, | ||||
| TokenScreenWrapper, | TokenScreenWrapper, | ||||
| NftOfferWrapper, | NftOfferWrapper, | ||||
| OuterCtn, | OuterCtn, | ||||
| TokenStatsRowCtn, | TokenStatsRowCtn, | ||||
| TokenActionBar, | |||||
| TokenActionBtn, | |||||
| TokenActionMoreWrap, | |||||
| TokenActionDropdown, | |||||
| TokenActionDropdownItem, | |||||
| } from 'components/Etokens/Token/styled'; | } from 'components/Etokens/Token/styled'; | ||||
| import CreateTokenForm from 'components/Etokens/CreateTokenForm'; | import CreateTokenForm from 'components/Etokens/CreateTokenForm'; | ||||
| import { | import { | ||||
| getAllTxHistoryByTokenId, | getAllTxHistoryByTokenId, | ||||
| getChildNftsFromParent, | getChildNftsFromParent, | ||||
| getTokenGenesisInfo, | getTokenGenesisInfo, | ||||
| } from 'chronik'; | } from 'chronik'; | ||||
| import { GenesisInfo } from 'chronik-client'; | import { GenesisInfo } from 'chronik-client'; | ||||
| ▲ Show 20 Lines • Show All 213 Lines • ▼ Show 20 Lines | const [ | ||||
| setPreviewedAgoraPartialUnacceptable, | setPreviewedAgoraPartialUnacceptable, | ||||
| ] = useState<boolean>(false); | ] = useState<boolean>(false); | ||||
| const [nftActiveOffer, setNftActiveOffer] = useState<null | OneshotOffer[]>( | const [nftActiveOffer, setNftActiveOffer] = useState<null | OneshotOffer[]>( | ||||
| null, | null, | ||||
| ); | ); | ||||
| const [nftOfferAgoraQueryError, setNftOfferAgoraQueryError] = | const [nftOfferAgoraQueryError, setNftOfferAgoraQueryError] = | ||||
| useState<boolean>(false); | useState<boolean>(false); | ||||
| const [firmaBidPrice, setFirmaBidPrice] = useState<null | number>(null); | const [firmaBidPrice, setFirmaBidPrice] = useState<null | number>(null); | ||||
| const [tokenDetailsExpanded, setTokenDetailsExpanded] = | |||||
| useState<boolean>(false); | |||||
| // By default, we load the app with all switches disabled | // By default, we load the app with all switches disabled | ||||
| // For SLP v1 tokens, we want showSend to be enabled by default | // For SLP v1 tokens, we want showSend to be enabled by default | ||||
| // But we may not want this to be default for other token types in the future | // But we may not want this to be default for other token types in the future | ||||
| interface TokenScreenSwitches { | interface TokenScreenSwitches { | ||||
| showRedeemXecx: boolean; | showRedeemXecx: boolean; | ||||
| showRedeemFirma: boolean; | showRedeemFirma: boolean; | ||||
| showSend: boolean; | showSend: boolean; | ||||
| Show All 11 Lines | const switchesOff: TokenScreenSwitches = { | ||||
| showAirdrop: false, | showAirdrop: false, | ||||
| showBurn: false, | showBurn: false, | ||||
| showMint: false, | showMint: false, | ||||
| showMintNft: false, | showMintNft: false, | ||||
| showSellNft: false, | showSellNft: false, | ||||
| showSellSlp: false, | showSellSlp: false, | ||||
| }; | }; | ||||
| const [switches, setSwitches] = useState<TokenScreenSwitches>(switchesOff); | const [switches, setSwitches] = useState<TokenScreenSwitches>(switchesOff); | ||||
| type TokenActionType = | |||||
| | 'buy' | |||||
| | 'sell' | |||||
| | 'send' | |||||
| | 'airdrop' | |||||
| | 'burn' | |||||
| | 'redeemXecx' | |||||
| | 'redeemFirma' | |||||
| | 'sellNft' | |||||
| | 'sellSlp' | |||||
| | 'mintNft' | |||||
| | 'mint'; | |||||
| const [activeTokenAction, setActiveTokenAction] = | |||||
| useState<TokenActionType>('buy'); | |||||
| const [moreDropdownOpen, setMoreDropdownOpen] = useState(false); | |||||
| const moreDropdownRef = useRef<HTMLDivElement>(null); | |||||
| const setAction = (action: TokenActionType) => { | |||||
| setActiveTokenAction(action); | |||||
| setMoreDropdownOpen(false); | |||||
| if (action === 'buy') { | |||||
| setSwitches(switchesOff); | |||||
| return; | |||||
| } | |||||
| setSwitches({ | |||||
| ...switchesOff, | |||||
| ...(action === 'redeemXecx' && { showRedeemXecx: true }), | |||||
| ...(action === 'redeemFirma' && { showRedeemFirma: true }), | |||||
| ...(action === 'send' && { showSend: true }), | |||||
| ...(action === 'airdrop' && { showAirdrop: true }), | |||||
| ...(action === 'burn' && { showBurn: true }), | |||||
| ...(action === 'sellNft' && { showSellNft: true }), | |||||
| ...(action === 'sellSlp' && { showSellSlp: true }), | |||||
| ...(action === 'mintNft' && { showMintNft: true }), | |||||
| ...(action === 'mint' && { showMint: true }), | |||||
| }); | |||||
| }; | |||||
| const [showLargeIconModal, setShowLargeIconModal] = | const [showLargeIconModal, setShowLargeIconModal] = | ||||
| useState<boolean>(false); | useState<boolean>(false); | ||||
| const [showLargeNftIcon, setShowLargeNftIcon] = useState<string>(''); | const [showLargeNftIcon, setShowLargeNftIcon] = useState<string>(''); | ||||
| interface UncachedTokenInfo { | interface UncachedTokenInfo { | ||||
| circulatingSupply: null | string; | circulatingSupply: null | string; | ||||
| mintBatons: null | number; | mintBatons: null | number; | ||||
| } | } | ||||
| const defaultUncachedTokenInfo: UncachedTokenInfo = { | const defaultUncachedTokenInfo: UncachedTokenInfo = { | ||||
| ▲ Show 20 Lines • Show All 432 Lines • ▼ Show 20 Lines | const getNftOffer = async () => { | ||||
| `Error querying agora.activeOffersByTokenId(${tokenId})`, | `Error querying agora.activeOffersByTokenId(${tokenId})`, | ||||
| ); | ); | ||||
| setNftOfferAgoraQueryError(true); | setNftOfferAgoraQueryError(true); | ||||
| } | } | ||||
| }; | }; | ||||
| useEffect(() => { | useEffect(() => { | ||||
| if (!isSupportedToken || typeof tokenType === 'undefined') { | if (!isSupportedToken || typeof tokenType === 'undefined') { | ||||
| // Do nothing for unsupported tokens | |||||
| // Do nothing if we haven't loaded the cached info yet | |||||
| return; | return; | ||||
| } | } | ||||
| // This useEffect block works as a de-facto "on load" block, | // Load NFT offer data when viewing an NFT child token (for sell UI) | ||||
| // for after we have the tokenId from the url params of this page | if (isNftChild) { | ||||
| if (!isNftParent) { | |||||
| if (tokenId === appConfig.vipTokens.xecx.tokenId) { | |||||
| // If this is the XECX token page, default option is redeeming XECX | |||||
| // i.e. selling XECX for XEC, 1:1 | |||||
| setSwitches({ ...switchesOff, showRedeemXecx: true }); | |||||
| } else if (tokenId === FIRMA.tokenId) { | |||||
| // If this is the Firma token page, default option is redeeming Firma | |||||
| // i.e. selling Firma for XEC at the Firma bid price | |||||
| setSwitches({ ...switchesOff, showRedeemFirma: true }); | |||||
| } else if (isNftChild) { | |||||
| // Default action is list | |||||
| setSwitches({ ...switchesOff, showSellNft: true }); | |||||
| // Check if it is listed | |||||
| getNftOffer(); | getNftOffer(); | ||||
| } else if ( | |||||
| tokenType.type === 'SLP_TOKEN_TYPE_FUNGIBLE' || | |||||
| tokenType.type === 'SLP_TOKEN_TYPE_MINT_VAULT' || | |||||
| isAlp | |||||
| ) { | |||||
| // Default action is List for non-NFT tokens | |||||
| setSwitches({ ...switchesOff, showSellSlp: true }); | |||||
| } else { | |||||
| // Default action is send | |||||
| setSwitches({ ...switchesOff, showSend: true }); | |||||
| } | |||||
| } | } | ||||
| }, [isSupportedToken, isNftParent, isNftChild]); | }, [isSupportedToken, isNftParent, isNftChild]); | ||||
| useEffect(() => { | useEffect(() => { | ||||
| const handleClickOutside = (e: MouseEvent) => { | |||||
| if ( | |||||
| moreDropdownRef.current && | |||||
| !moreDropdownRef.current.contains(e.target as Node) | |||||
| ) { | |||||
| setMoreDropdownOpen(false); | |||||
| } | |||||
| }; | |||||
| document.addEventListener('mousedown', handleClickOutside); | |||||
| return () => | |||||
| document.removeEventListener('mousedown', handleClickOutside); | |||||
| }, []); | |||||
| useEffect(() => { | |||||
| if (switches.showRedeemXecx) { | if (switches.showRedeemXecx) { | ||||
| // If the user is redeeming XECX | // If the user is redeeming XECX | ||||
| // Set selected currency to XEC | // Set selected currency to XEC | ||||
| setSelectedCurrency(appConfig.ticker); | setSelectedCurrency(appConfig.ticker); | ||||
| // Set the listing price to 1 XEC | // Set the listing price to 1 XEC | ||||
| setFormData({ | setFormData({ | ||||
| ▲ Show 20 Lines • Show All 1,696 Lines • ▼ Show 20 Lines | return ( | ||||
| <TokenStatsTable title="Token Stats"> | <TokenStatsTable title="Token Stats"> | ||||
| <TokenStatsCol> | <TokenStatsCol> | ||||
| <TokenIconExpandButton | <TokenIconExpandButton | ||||
| onClick={() => setShowLargeIconModal(true)} | onClick={() => setShowLargeIconModal(true)} | ||||
| > | > | ||||
| <TokenIcon size={128} tokenId={tokenId} /> | <TokenIcon size={128} tokenId={tokenId} /> | ||||
| </TokenIconExpandButton> | </TokenIconExpandButton> | ||||
| {renderedTokenType !== 'NFT' && ( | {renderedTokenType !== 'NFT' && ( | ||||
| <> | <div> | ||||
| {tokenName !== undefined && ( | {tokenName !== undefined && ( | ||||
| <h2>{tokenName}</h2> | <h2>{tokenName}</h2> | ||||
| )} | )} | ||||
| {tokenTicker !== undefined && ( | {tokenTicker !== undefined && ( | ||||
| <span>{tokenTicker}</span> | <span>{tokenTicker}</span> | ||||
| )} | )} | ||||
| </> | </div> | ||||
| )} | )} | ||||
| </TokenStatsCol> | </TokenStatsCol> | ||||
| <TokenStatsRowCtn> | <TokenStatsRowCtn> | ||||
| {typeof tokenBalance === 'string' && ( | <TokenInfoRow> | ||||
| <TokenStatsTableRow balance> | |||||
| <label>Your Balance</label> | <label>Your Balance</label> | ||||
| <div> | <div> | ||||
| {decimalizedTokenQtyToLocaleFormat( | {decimalizedTokenQtyToLocaleFormat( | ||||
| tokenBalance, | typeof tokenBalance === 'string' | ||||
| ? tokenBalance | |||||
| : '0', | |||||
| userLocale, | userLocale, | ||||
| )} | )} | ||||
| {tokenTicker !== undefined && | {tokenTicker !== undefined && | ||||
| tokenTicker !== '' && | tokenTicker !== '' && | ||||
| ` ${tokenTicker}`} | ` ${tokenTicker}`} | ||||
| </div> | </div> | ||||
| </TokenStatsTableRow> | </TokenInfoRow> | ||||
| )} | <TokenInfoRow | ||||
| <TokenStatsTableRow> | expand | ||||
| role="button" | |||||
| onClick={() => | |||||
| setTokenDetailsExpanded(prev => !prev) | |||||
| } | |||||
| aria-expanded={tokenDetailsExpanded} | |||||
| > | |||||
| <label>Token Details</label> | |||||
| <div | |||||
| style={{ | |||||
| display: 'flex', | |||||
| alignItems: 'center', | |||||
| justifyContent: 'center', | |||||
| transform: tokenDetailsExpanded | |||||
| ? 'rotate(180deg)' | |||||
| : 'none', | |||||
| }} | |||||
| > | |||||
| <DownArrow /> | |||||
| </div> | |||||
| </TokenInfoRow> | |||||
| {tokenDetailsExpanded && ( | |||||
| <> | |||||
| <TokenStatsTableRow | |||||
| style={{ marginTop: '10px' }} | |||||
| > | |||||
| <label>Type</label> | <label>Type</label> | ||||
| <div> | <div> | ||||
| <DataAndQuestionButton> | <DataAndQuestionButton> | ||||
| {renderedTokenType}{' '} | {renderedTokenType}{' '} | ||||
| <IconButton | <IconButton | ||||
| name={`Click for more info about this token type`} | name={`Click for more info about this token type`} | ||||
| icon={<QuestionIcon />} | icon={<QuestionIcon />} | ||||
| onClick={() => | onClick={() => | ||||
| setShowTokenTypeInfo(true) | setShowTokenTypeInfo( | ||||
| true, | |||||
| ) | |||||
| } | } | ||||
| /> | /> | ||||
| </DataAndQuestionButton> | </DataAndQuestionButton> | ||||
| </div> | </div> | ||||
| </TokenStatsTableRow> | </TokenStatsTableRow> | ||||
| <TokenStatsTableRow> | <TokenStatsTableRow> | ||||
| <label>Token Id</label> | <label>Token Id</label> | ||||
| <div> | <div> | ||||
| <a | <a | ||||
| href={`${explorer.blockExplorerUrl}/tx/${tokenId}`} | href={`${explorer.blockExplorerUrl}/tx/${tokenId}`} | ||||
| target="_blank" | target="_blank" | ||||
| rel="noopener noreferrer" | rel="noopener noreferrer" | ||||
| > | > | ||||
| {(tokenId as string).slice(0, 3)}... | {(tokenId as string).slice( | ||||
| 0, | |||||
| 3, | |||||
| )} | |||||
| ... | |||||
| {(tokenId as string).slice(-3)} | {(tokenId as string).slice(-3)} | ||||
| </a> | </a> | ||||
| <CopyIconButton | <CopyIconButton | ||||
| name={`Copy Token ID`} | name={`Copy Token ID`} | ||||
| data={tokenId as string} | data={tokenId as string} | ||||
| showToast | showToast | ||||
| customMsg={`Token ID "${tokenId}" copied to clipboard`} | customMsg={`Token ID "${tokenId}" copied to clipboard`} | ||||
| /> | /> | ||||
| </div> | </div> | ||||
| </TokenStatsTableRow> | </TokenStatsTableRow> | ||||
| {renderedTokenType !== 'NFT' && | {renderedTokenType !== 'NFT' && | ||||
| renderedTokenType !== 'NFT Collection' && ( | renderedTokenType !== | ||||
| 'NFT Collection' && ( | |||||
| <TokenStatsTableRow> | <TokenStatsTableRow> | ||||
| <label>Decimals</label> | <label>Decimals</label> | ||||
| <div>{decimals}</div> | <div>{decimals}</div> | ||||
| </TokenStatsTableRow> | </TokenStatsTableRow> | ||||
| )} | )} | ||||
| {url !== '' && ( | {url !== '' && ( | ||||
| <TokenStatsTableRow> | <TokenStatsTableRow> | ||||
| <label>URL</label> | <label>URL</label> | ||||
| <TokenUrlCol> | <TokenUrlCol> | ||||
| <UncontrolledLink | <UncontrolledLink | ||||
| url={ | url={ | ||||
| url?.startsWith('https://') || | url?.startsWith( | ||||
| url?.startsWith('http://') | 'https://', | ||||
| ) || | |||||
| url?.startsWith( | |||||
| 'http://', | |||||
| ) | |||||
| ? url | ? url | ||||
| : `https://${url}` | : `https://${url}` | ||||
| } | } | ||||
| /> | /> | ||||
| </TokenUrlCol> | </TokenUrlCol> | ||||
| </TokenStatsTableRow> | </TokenStatsTableRow> | ||||
| )} | )} | ||||
| <TokenStatsTableRow> | <TokenStatsTableRow> | ||||
| <label>Created</label> | <label>Created</label> | ||||
| <div> | <div> | ||||
| {typeof cachedInfo?.block !== 'undefined' | {typeof cachedInfo?.block !== | ||||
| 'undefined' | |||||
| ? formatDate( | ? formatDate( | ||||
| cachedInfo.block.timestamp.toString(), | cachedInfo.block.timestamp.toString(), | ||||
| navigator.language, | navigator.language, | ||||
| ) | ) | ||||
| : formatDate( | : formatDate( | ||||
| ( | ( | ||||
| cachedInfo?.timeFirstSeen as number | cachedInfo?.timeFirstSeen as number | ||||
| ).toString(), | ).toString(), | ||||
| navigator.language, | navigator.language, | ||||
| )} | )} | ||||
| </div> | </div> | ||||
| </TokenStatsTableRow> | </TokenStatsTableRow> | ||||
| {renderedTokenType !== 'NFT' && ( | {renderedTokenType !== 'NFT' && ( | ||||
| <TokenStatsTableRow> | <TokenStatsTableRow> | ||||
| <label>Genesis Qty</label> | <label>Genesis Qty</label> | ||||
| <div> | <div> | ||||
| {typeof genesisSupply === 'string' ? ( | {typeof genesisSupply === | ||||
| 'string' ? ( | |||||
| decimalizedTokenQtyToLocaleFormat( | decimalizedTokenQtyToLocaleFormat( | ||||
| genesisSupply, | genesisSupply, | ||||
| userLocale, | userLocale, | ||||
| ) | ) | ||||
| ) : ( | ) : ( | ||||
| <InlineLoader /> | <InlineLoader /> | ||||
| )} | )} | ||||
| </div> | </div> | ||||
| </TokenStatsTableRow> | </TokenStatsTableRow> | ||||
| )} | )} | ||||
| {renderedTokenType !== 'NFT' && ( | {renderedTokenType !== 'NFT' && ( | ||||
| <TokenStatsTableRow> | <TokenStatsTableRow> | ||||
| <label>Supply</label> | <label>Supply</label> | ||||
| <div> | <div> | ||||
| {typeof uncachedTokenInfo.circulatingSupply === | {typeof uncachedTokenInfo.circulatingSupply === | ||||
| 'string' ? ( | 'string' ? ( | ||||
| `${decimalizedTokenQtyToLocaleFormat( | `${decimalizedTokenQtyToLocaleFormat( | ||||
| uncachedTokenInfo.circulatingSupply, | uncachedTokenInfo.circulatingSupply, | ||||
| userLocale, | userLocale, | ||||
| )}${ | )}${ | ||||
| uncachedTokenInfo.mintBatons === | uncachedTokenInfo.mintBatons === | ||||
| 0 && | 0 && | ||||
| tokenType!.type !== | tokenType!.type !== | ||||
| 'SLP_TOKEN_TYPE_MINT_VAULT' | 'SLP_TOKEN_TYPE_MINT_VAULT' | ||||
| ? ' (fixed)' | ? ' (fixed)' | ||||
| : ' (var.)' | : ' (var.)' | ||||
| }` | }` | ||||
| ) : uncachedTokenInfoError ? ( | ) : uncachedTokenInfoError ? ( | ||||
| 'Error fetching supply' | 'Error fetching supply' | ||||
| ) : ( | ) : ( | ||||
| <InlineLoader /> | <InlineLoader /> | ||||
| )} | )} | ||||
| </div> | </div> | ||||
| </TokenStatsTableRow> | </TokenStatsTableRow> | ||||
| )} | )} | ||||
| </> | |||||
| )} | |||||
| {typeof hash !== 'undefined' && hash !== '' && ( | {typeof hash !== 'undefined' && hash !== '' && ( | ||||
| <TokenStatsTableRow> | <TokenStatsTableRow> | ||||
| <label>Hash</label> | <label>Hash</label> | ||||
| <div> | <div> | ||||
| {hash.slice(0, 3)}... | {hash.slice(0, 3)}... | ||||
| {hash.slice(-3)} | {hash.slice(-3)} | ||||
| <CopyIconButton | <CopyIconButton | ||||
| name={`Copy Token ID`} | name={`Copy Token ID`} | ||||
| ▲ Show 20 Lines • Show All 41 Lines • ▼ Show 20 Lines | return ( | ||||
| setOffers={setNftActiveOffer} | setOffers={setNftActiveOffer} | ||||
| /> | /> | ||||
| </NftOfferWrapper> | </NftOfferWrapper> | ||||
| )} | )} | ||||
| </> | </> | ||||
| )} | )} | ||||
| {isSupportedToken && | {isSupportedToken && | ||||
| isBlacklisted !== null && | isBlacklisted !== null && | ||||
| !isBlacklisted && | !isBlacklisted && ( | ||||
| <> | |||||
| <TokenActionBar> | |||||
| <TokenActionBtn | |||||
| $active={activeTokenAction === 'buy'} | |||||
| disabled={isNftParent || isNftChild} | |||||
| onClick={() => setAction('buy')} | |||||
| > | |||||
| + Buy | |||||
| </TokenActionBtn> | |||||
| <TokenActionBtn | |||||
| $active={ | |||||
| tokenId === | |||||
| appConfig.vipTokens.xecx | |||||
| .tokenId || | |||||
| tokenId === FIRMA.tokenId | |||||
| ? activeTokenAction === | |||||
| 'redeemXecx' || | |||||
| activeTokenAction === | |||||
| 'redeemFirma' | |||||
| : [ | |||||
| 'sellSlp', | |||||
| 'sellNft', | |||||
| ].includes(activeTokenAction) | |||||
| } | |||||
| disabled={ | |||||
| typeof tokenBalance === 'undefined' | |||||
| } | |||||
| onClick={() => { | |||||
| if ( | |||||
| typeof tokenBalance === | |||||
| 'undefined' | |||||
| ) | |||||
| return; | |||||
| if ( | |||||
| tokenId === | |||||
| appConfig.vipTokens.xecx.tokenId | |||||
| ) | |||||
| setAction('redeemXecx'); | |||||
| else if (tokenId === FIRMA.tokenId) | |||||
| setAction('redeemFirma'); | |||||
| else if (isNftChild) | |||||
| setAction('sellNft'); | |||||
| else if ( | |||||
| tokenType?.type === | |||||
| 'SLP_TOKEN_TYPE_FUNGIBLE' || | |||||
| tokenType?.type === | |||||
| 'SLP_TOKEN_TYPE_MINT_VAULT' || | |||||
| isAlp | |||||
| ) | |||||
| setAction('sellSlp'); | |||||
| }} | |||||
| > | |||||
| {tokenId === | |||||
| appConfig.vipTokens.xecx.tokenId || | |||||
| tokenId === FIRMA.tokenId | |||||
| ? '− Redeem' | |||||
| : '− Sell'} | |||||
| </TokenActionBtn> | |||||
| <TokenActionMoreWrap ref={moreDropdownRef}> | |||||
| <TokenActionBtn | |||||
| $active={[ | |||||
| 'send', | |||||
| 'airdrop', | |||||
| 'burn', | |||||
| 'mint', | |||||
| 'mintNft', | |||||
| ].includes(activeTokenAction)} | |||||
| onClick={() => | |||||
| setMoreDropdownOpen(o => !o) | |||||
| } | |||||
| > | |||||
| ⋯ | |||||
| </TokenActionBtn> | |||||
| <TokenActionDropdown | |||||
| $open={moreDropdownOpen} | |||||
| > | |||||
| {!isNftParent && ( | |||||
| <TokenActionDropdownItem | |||||
| $disabled={ | |||||
| typeof tokenBalance === | |||||
| 'undefined' | |||||
| } | |||||
| onClick={() => | |||||
| setAction('send') | |||||
| } | |||||
| > | |||||
| Send | |||||
| </TokenActionDropdownItem> | |||||
| )} | |||||
| {isNftParent && ( | |||||
| <TokenActionDropdownItem | |||||
| onClick={() => | |||||
| setAction('mintNft') | |||||
| } | |||||
| > | |||||
| Mint NFT | |||||
| </TokenActionDropdownItem> | |||||
| )} | |||||
| {!isNftChild && ( | |||||
| <TokenActionDropdownItem | |||||
| $disabled={ | |||||
| typeof tokenBalance === | |||||
| 'undefined' | |||||
| } | |||||
| onClick={() => | |||||
| setAction('airdrop') | |||||
| } | |||||
| > | |||||
| Airdrop | |||||
| </TokenActionDropdownItem> | |||||
| )} | |||||
| {!isNftParent && !isNftChild && ( | |||||
| <TokenActionDropdownItem | |||||
| $disabled={ | |||||
| typeof tokenBalance === | |||||
| 'undefined' | |||||
| } | |||||
| onClick={() => | |||||
| setAction('burn') | |||||
| } | |||||
| > | |||||
| Burn | |||||
| </TokenActionDropdownItem> | |||||
| )} | |||||
| {mintBatons.length > 0 && ( | |||||
| <TokenActionDropdownItem | |||||
| $disabled={ | |||||
| typeof tokenBalance === | |||||
| 'undefined' | |||||
| } | |||||
| onClick={() => | |||||
| setAction('mint') | |||||
| } | |||||
| > | |||||
| Mint | |||||
| </TokenActionDropdownItem> | |||||
| )} | |||||
| {isNftChild && ( | |||||
| <TokenActionDropdownItem | |||||
| $disabled={ | |||||
| typeof tokenBalance === | |||||
| 'undefined' | |||||
| } | |||||
| onClick={() => | |||||
| setAction('sellNft') | |||||
| } | |||||
| > | |||||
| Sell NFT | |||||
| </TokenActionDropdownItem> | |||||
| )} | |||||
| {(tokenId === | |||||
| appConfig.vipTokens.xecx | |||||
| .tokenId || | |||||
| tokenId === FIRMA.tokenId) && ( | |||||
| <TokenActionDropdownItem | |||||
| $disabled={ | |||||
| typeof tokenBalance === | |||||
| 'undefined' | |||||
| } | |||||
| onClick={() => | |||||
| setAction('sellSlp') | |||||
| } | |||||
| > | |||||
| List token | |||||
| </TokenActionDropdownItem> | |||||
| )} | |||||
| </TokenActionDropdown> | |||||
| </TokenActionMoreWrap> | |||||
| </TokenActionBar> | |||||
| {activeTokenAction === 'buy' && | |||||
| !isNftParent && | !isNftParent && | ||||
| !isNftChild && ( | !isNftChild && ( | ||||
| <OrderBook | <OrderBook | ||||
| tokenId={tokenId as string} | tokenId={tokenId as string} | ||||
| noIcon | noIcon | ||||
| userLocale={userLocale} | userLocale={userLocale} | ||||
| priceInFiat={tokenId === FIRMA.tokenId} | priceInFiat={ | ||||
| tokenId === FIRMA.tokenId | |||||
| } | |||||
| /> | /> | ||||
| )} | )} | ||||
| </> | |||||
| )} | |||||
| {isNftParent && nftTokenIds.length > 0 && ( | {isNftParent && nftTokenIds.length > 0 && ( | ||||
| <> | <> | ||||
| <NftTitle>NFTs in this Collection</NftTitle> | <NftTitle>NFTs in this Collection</NftTitle> | ||||
| <NftTable> | <NftTable> | ||||
| {nftTokenIds.map(nftTokenId => { | {nftTokenIds.map(nftTokenId => { | ||||
| const cachedNftInfo = | const cachedNftInfo = | ||||
| cashtabCache.tokens.get(nftTokenId); | cashtabCache.tokens.get(nftTokenId); | ||||
| return ( | return ( | ||||
| ▲ Show 20 Lines • Show All 69 Lines • ▼ Show 20 Lines | |||||
| fiatPrice={fiatPrice} | fiatPrice={fiatPrice} | ||||
| userLocale={userLocale} | userLocale={userLocale} | ||||
| ecashWallet={ecashWallet} | ecashWallet={ecashWallet} | ||||
| noCollectionInfo | noCollectionInfo | ||||
| /> | /> | ||||
| </> | </> | ||||
| )} | )} | ||||
| {apiError && <ApiError />} | {apiError && <ApiError />} | ||||
| {typeof tokenBalance === 'undefined' ? ( | {typeof tokenBalance === 'undefined' && ( | ||||
| <Info>You do not hold this token.</Info> | <Info>You do not hold this token.</Info> | ||||
| ) : ( | )} | ||||
| <> | {isSupportedToken && activeTokenAction !== 'buy' && ( | ||||
| {isSupportedToken && ( | <SendTokenForm> | ||||
| <SendTokenForm title="Token Actions"> | {tokenId === appConfig.vipTokens.xecx.tokenId && ( | ||||
| {tokenId === | |||||
| appConfig.vipTokens.xecx.tokenId && ( | |||||
| <> | <> | ||||
| <SwitchHolder> | |||||
| <Switch | |||||
| name="Toggle Redeem XECX" | |||||
| on="🤳" | |||||
| off="🤳" | |||||
| checked={ | |||||
| switches.showRedeemXecx | |||||
| } | |||||
| handleToggle={() => { | |||||
| // We turn everything else off, whether we are turning this one on or off | |||||
| setSwitches({ | |||||
| ...switchesOff, | |||||
| showRedeemXecx: | |||||
| !switches.showRedeemXecx, | |||||
| }); | |||||
| }} | |||||
| /> | |||||
| <SwitchLabel> | |||||
| Redeem {tokenName} ( | |||||
| {tokenTicker}) 1:1 for XEC | |||||
| </SwitchLabel> | |||||
| </SwitchHolder> | |||||
| {switches.showRedeemXecx && ( | {switches.showRedeemXecx && ( | ||||
| <> | <> | ||||
| <SectionCtn> | |||||
| <SectionLabel> | |||||
| Quantity | |||||
| </SectionLabel> | |||||
| <SendTokenFormRow> | <SendTokenFormRow> | ||||
| <InputRow> | <InputRow> | ||||
| <Slider | <Slider | ||||
| name={ | name={ | ||||
| 'agoraPartialTokenQty' | 'agoraPartialTokenQty' | ||||
| } | } | ||||
| label={`Offered qty`} | |||||
| value={ | value={ | ||||
| agoraPartialTokenQty | agoraPartialTokenQty | ||||
| } | } | ||||
| handleSlide={ | handleSlide={ | ||||
| handleTokenOfferedSlide | handleTokenOfferedSlide | ||||
| } | } | ||||
| error={ | error={ | ||||
| agoraPartialTokenQtyError | agoraPartialTokenQtyError | ||||
| } | } | ||||
| min={0} | min={0} | ||||
| max={ | max={tokenBalance} | ||||
| tokenBalance | |||||
| } | |||||
| step={parseFloat( | step={parseFloat( | ||||
| `1e-${decimals}`, | `1e-${decimals}`, | ||||
| )} | )} | ||||
| allowTypedInput | allowTypedInput | ||||
| /> | /> | ||||
| </InputRow> | </InputRow> | ||||
| </SendTokenFormRow> | </SendTokenFormRow> | ||||
| {!tokenListPriceError && | {!tokenListPriceError && | ||||
| formData.tokenListPrice !== | formData.tokenListPrice !== | ||||
| '' && | '' && | ||||
| formData.tokenListPrice !== | formData.tokenListPrice !== | ||||
| null && | null && | ||||
| fiatPrice !== null && ( | fiatPrice !== null && ( | ||||
| <ListPricePreview title="Token List Price"> | <ListPricePreview title="Token List Price"> | ||||
| {getAgoraPartialPricePreview()} | {getAgoraPartialPricePreview()} | ||||
| </ListPricePreview> | </ListPricePreview> | ||||
| )} | )} | ||||
| <SendTokenFormRow> | <SendTokenFormRow> | ||||
| <PrimaryButton | <PrimaryButton | ||||
| style={{ | style={{ | ||||
| marginTop: | marginTop: '12px', | ||||
| '12px', | |||||
| }} | }} | ||||
| disabled={ | disabled={ | ||||
| apiError || | apiError || | ||||
| agoraPartialTokenQtyError !== | agoraPartialTokenQtyError !== | ||||
| false || | false || | ||||
| agoraPartialMinError !== | agoraPartialMinError !== | ||||
| false || | false || | ||||
| tokenListPriceError !== | tokenListPriceError !== | ||||
| false || | false || | ||||
| formData.tokenListPrice === | formData.tokenListPrice === | ||||
| '' || | '' || | ||||
| formData.tokenListPrice === | formData.tokenListPrice === | ||||
| null || | null || | ||||
| agoraPartialTokenQty === | agoraPartialTokenQty === | ||||
| '' || | '' || | ||||
| agoraPartialMin === | agoraPartialMin === | ||||
| '' | '' | ||||
| } | } | ||||
| onClick={ | onClick={ | ||||
| previewAgoraPartial | previewAgoraPartial | ||||
| } | } | ||||
| > | > | ||||
| Redeem XECX for XEC | Redeem XECX for XEC | ||||
| </PrimaryButton> | </PrimaryButton> | ||||
| </SendTokenFormRow> | </SendTokenFormRow> | ||||
| </SectionCtn> | |||||
| </> | </> | ||||
| )} | )} | ||||
| </> | </> | ||||
| )} | )} | ||||
| {tokenId === FIRMA.tokenId && ( | {tokenId === FIRMA.tokenId && ( | ||||
| <> | <> | ||||
| <SwitchHolder> | |||||
| <Switch | |||||
| name="Toggle Redeem FIRMA" | |||||
| on="🤳" | |||||
| off="🤳" | |||||
| checked={ | |||||
| switches.showRedeemFirma | |||||
| } | |||||
| handleToggle={() => { | |||||
| // We turn everything else off, whether we are turning this one on or off | |||||
| setSwitches({ | |||||
| ...switchesOff, | |||||
| showRedeemFirma: | |||||
| !switches.showRedeemFirma, | |||||
| }); | |||||
| }} | |||||
| /> | |||||
| <SwitchLabel> | |||||
| Redeem {tokenName} | |||||
| </SwitchLabel> | |||||
| </SwitchHolder> | |||||
| {switches.showRedeemFirma && ( | {switches.showRedeemFirma && ( | ||||
| <> | <> | ||||
| <SectionCtn> | |||||
| <SectionLabel> | |||||
| Quantity | |||||
| </SectionLabel> | |||||
| <SendTokenFormRow> | <SendTokenFormRow> | ||||
| <InputRow> | <InputRow> | ||||
| <Slider | <Slider | ||||
| name={ | name={ | ||||
| 'agoraPartialTokenQty' | 'agoraPartialTokenQty' | ||||
| } | } | ||||
| label={`Offered qty`} | |||||
| value={ | value={ | ||||
| agoraPartialTokenQty | agoraPartialTokenQty | ||||
| } | } | ||||
| handleSlide={ | handleSlide={ | ||||
| handleTokenOfferedSlide | handleTokenOfferedSlide | ||||
| } | } | ||||
| error={ | error={ | ||||
| agoraPartialTokenQtyError | agoraPartialTokenQtyError | ||||
| } | } | ||||
| min={0} | min={0} | ||||
| max={ | max={tokenBalance} | ||||
| tokenBalance | |||||
| } | |||||
| step={parseFloat( | step={parseFloat( | ||||
| `1e-${decimals}`, | `1e-${decimals}`, | ||||
| )} | )} | ||||
| allowTypedInput | allowTypedInput | ||||
| /> | /> | ||||
| </InputRow> | </InputRow> | ||||
| </SendTokenFormRow> | </SendTokenFormRow> | ||||
| {!tokenListPriceError && | {!tokenListPriceError && | ||||
| formData.tokenListPrice !== | formData.tokenListPrice !== | ||||
| '' && | '' && | ||||
| formData.tokenListPrice !== | formData.tokenListPrice !== | ||||
| null && | null && | ||||
| fiatPrice !== null && ( | fiatPrice !== null && ( | ||||
| <ListPricePreview title="Token List Price"> | <ListPricePreview title="Token List Price"> | ||||
| {getAgoraPartialPricePreview()} | {getAgoraPartialPricePreview()} | ||||
| </ListPricePreview> | </ListPricePreview> | ||||
| )} | )} | ||||
| <SendTokenFormRow> | <SendTokenFormRow> | ||||
| <PrimaryButton | <PrimaryButton | ||||
| style={{ | style={{ | ||||
| marginTop: | marginTop: '12px', | ||||
| '12px', | |||||
| }} | }} | ||||
| disabled={ | disabled={ | ||||
| apiError || | apiError || | ||||
| agoraPartialTokenQtyError !== | agoraPartialTokenQtyError !== | ||||
| false || | false || | ||||
| agoraPartialTokenQty === | agoraPartialTokenQty === | ||||
| '0' || | '0' || | ||||
| agoraPartialTokenQty === | agoraPartialTokenQty === | ||||
| '' || | '' || | ||||
| isCalculatingRedeemFirma | isCalculatingRedeemFirma | ||||
| } | } | ||||
| onClick={ | onClick={ | ||||
| previewFirmaPartial | previewFirmaPartial | ||||
| } | } | ||||
| > | > | ||||
| {isCalculatingRedeemFirma ? ( | {isCalculatingRedeemFirma ? ( | ||||
| <InlineLoader /> | <InlineLoader /> | ||||
| ) : ( | ) : ( | ||||
| `Redeem FIRMA for XEC` | `Redeem FIRMA for XEC` | ||||
| )} | )} | ||||
| </PrimaryButton> | </PrimaryButton> | ||||
| </SendTokenFormRow> | </SendTokenFormRow> | ||||
| </SectionCtn> | |||||
| </> | </> | ||||
| )} | )} | ||||
| </> | </> | ||||
| )} | )} | ||||
| {isNftChild ? ( | {isNftChild ? ( | ||||
| <> | <> | ||||
| <SwitchHolder> | |||||
| <Switch | |||||
| name="Toggle Sell NFT" | |||||
| on="💰" | |||||
| off="💰" | |||||
| checked={ | |||||
| switches.showSellNft | |||||
| } | |||||
| handleToggle={() => { | |||||
| // We turn everything else off, whether we are turning this one on or off | |||||
| setSwitches({ | |||||
| ...switchesOff, | |||||
| showSellNft: | |||||
| !switches.showSellNft, | |||||
| }); | |||||
| }} | |||||
| /> | |||||
| <SwitchLabel> | |||||
| Sell {tokenName} ( | |||||
| {tokenTicker}) | |||||
| </SwitchLabel> | |||||
| </SwitchHolder> | |||||
| {switches.showSellNft && ( | {switches.showSellNft && ( | ||||
| <> | <SectionCtn> | ||||
| <SendTokenFormRow> | <SendTokenFormRow> | ||||
| <InputRow> | <InputRow> | ||||
| <ListPriceInput | <ListPriceInput | ||||
| name="nftListPrice" | name="nftListPrice" | ||||
| placeholder="Enter NFT list price" | placeholder="Enter NFT list price" | ||||
| value={ | value={ | ||||
| formData.nftListPrice | formData.nftListPrice | ||||
| } | } | ||||
| selectValue={ | selectValue={ | ||||
| selectedCurrency | selectedCurrency | ||||
| } | } | ||||
| selectDisabled={ | selectDisabled={ | ||||
| fiatPrice === | fiatPrice === null | ||||
| null | |||||
| } | } | ||||
| fiatCode={settings.fiatCurrency.toUpperCase()} | fiatCode={settings.fiatCurrency.toUpperCase()} | ||||
| error={ | error={ | ||||
| nftListPriceError | nftListPriceError | ||||
| } | } | ||||
| handleInput={ | handleInput={ | ||||
| handleNftListPriceChange | handleNftListPriceChange | ||||
| } | } | ||||
| handleSelect={ | handleSelect={ | ||||
| handleSelectedCurrencyChange | handleSelectedCurrencyChange | ||||
| } | } | ||||
| ></ListPriceInput> | ></ListPriceInput> | ||||
| </InputRow> | </InputRow> | ||||
| </SendTokenFormRow> | </SendTokenFormRow> | ||||
| {!nftListPriceError && | {!nftListPriceError && | ||||
| formData.nftListPrice !== | formData.nftListPrice !== '' && | ||||
| '' && | |||||
| formData.nftListPrice !== | formData.nftListPrice !== | ||||
| null && | null && | ||||
| fiatPrice !== null && ( | fiatPrice !== null && ( | ||||
| <ListPricePreview title="NFT List Price"> | <ListPricePreview title="NFT List Price"> | ||||
| {selectedCurrency === | {selectedCurrency === | ||||
| appConfig.ticker | appConfig.ticker | ||||
| ? `${parseFloat( | ? `${parseFloat( | ||||
| formData.nftListPrice, | formData.nftListPrice, | ||||
| ).toLocaleString( | ).toLocaleString( | ||||
| userLocale, | userLocale, | ||||
| )} | )} | ||||
| XEC = ${ | XEC = ${ | ||||
| settings | settings | ||||
| ? `${ | ? `${ | ||||
| supportedFiatCurrencies[ | supportedFiatCurrencies[ | ||||
| settings | settings | ||||
| .fiatCurrency | .fiatCurrency | ||||
| ].symbol | ].symbol | ||||
| } ` | } ` | ||||
| Show All 11 Lines | |||||
| appConfig.cashDecimals, | appConfig.cashDecimals, | ||||
| }, | }, | ||||
| )} ${ | )} ${ | ||||
| settings && | settings && | ||||
| settings.fiatCurrency | settings.fiatCurrency | ||||
| ? settings.fiatCurrency.toUpperCase() | ? settings.fiatCurrency.toUpperCase() | ||||
| : 'USD' | : 'USD' | ||||
| }` | }` | ||||
| : `${ | : `${ | ||||
| settings | settings | ||||
| ? `${ | ? `${ | ||||
| supportedFiatCurrencies[ | supportedFiatCurrencies[ | ||||
| settings | settings | ||||
| .fiatCurrency | .fiatCurrency | ||||
| ] | ] | ||||
| .symbol | .symbol | ||||
| } ` | } ` | ||||
| : '$ ' | : '$ ' | ||||
| }${parseFloat( | }${parseFloat( | ||||
| formData.nftListPrice, | formData.nftListPrice, | ||||
| ).toLocaleString( | ).toLocaleString( | ||||
| userLocale, | userLocale, | ||||
| )} ${ | )} ${ | ||||
| settings && | settings && | ||||
| settings.fiatCurrency | settings.fiatCurrency | ||||
| ? settings.fiatCurrency.toUpperCase() | ? settings.fiatCurrency.toUpperCase() | ||||
| : 'USD' | : 'USD' | ||||
| } = ${( | } = ${( | ||||
| parseFloat( | parseFloat( | ||||
| formData.nftListPrice, | formData.nftListPrice, | ||||
| ) / | ) / fiatPrice | ||||
| fiatPrice | |||||
| ).toLocaleString( | ).toLocaleString( | ||||
| userLocale, | userLocale, | ||||
| { | { | ||||
| minimumFractionDigits: | minimumFractionDigits: | ||||
| appConfig.cashDecimals, | appConfig.cashDecimals, | ||||
| maximumFractionDigits: | maximumFractionDigits: | ||||
| appConfig.cashDecimals, | appConfig.cashDecimals, | ||||
| }, | }, | ||||
| )} | )} | ||||
| XEC`} | XEC`} | ||||
| </ListPricePreview> | </ListPricePreview> | ||||
| )} | )} | ||||
| <SendTokenFormRow> | <SendTokenFormRow> | ||||
| <PrimaryButton | <PrimaryButton | ||||
| style={{ | style={{ | ||||
| marginTop: | marginTop: '12px', | ||||
| '12px', | |||||
| }} | }} | ||||
| disabled={ | disabled={ | ||||
| apiError || | apiError || | ||||
| nftListPriceError !== | nftListPriceError !== | ||||
| false || | false || | ||||
| formData.nftListPrice === | formData.nftListPrice === | ||||
| '' | '' | ||||
| } | } | ||||
| onClick={() => | onClick={() => | ||||
| setShowConfirmListNft( | setShowConfirmListNft( | ||||
| true, | true, | ||||
| ) | ) | ||||
| } | } | ||||
| > | > | ||||
| List {tokenName} | List {tokenName} | ||||
| </PrimaryButton> | </PrimaryButton> | ||||
| </SendTokenFormRow> | </SendTokenFormRow> | ||||
| </> | </SectionCtn> | ||||
| )} | )} | ||||
| </> | </> | ||||
| ) : ( | ) : ( | ||||
| (tokenType?.type === | (tokenType?.type === | ||||
| 'SLP_TOKEN_TYPE_FUNGIBLE' || | 'SLP_TOKEN_TYPE_FUNGIBLE' || | ||||
| tokenType?.type === | tokenType?.type === | ||||
| 'SLP_TOKEN_TYPE_MINT_VAULT' || | 'SLP_TOKEN_TYPE_MINT_VAULT' || | ||||
| isAlp) && ( | isAlp) && ( | ||||
| <> | <> | ||||
| <SwitchHolder> | |||||
| <Switch | |||||
| name="Toggle Sell Token" | |||||
| on="💰" | |||||
| off="💰" | |||||
| checked={ | |||||
| switches.showSellSlp | |||||
| } | |||||
| handleToggle={() => { | |||||
| // We turn everything else off, whether we are turning this one on or off | |||||
| setSwitches({ | |||||
| ...switchesOff, | |||||
| showSellSlp: | |||||
| !switches.showSellSlp, | |||||
| }); | |||||
| }} | |||||
| /> | |||||
| <SwitchLabel> | |||||
| Sell {tokenName} ( | |||||
| {tokenTicker}) | |||||
| </SwitchLabel> | |||||
| <IconButton | |||||
| name={`Click for more info about agora partial sales`} | |||||
| icon={<QuestionIcon />} | |||||
| onClick={() => | |||||
| setShowAgoraPartialInfo( | |||||
| true, | |||||
| ) | |||||
| } | |||||
| /> | |||||
| </SwitchHolder> | |||||
| {switches.showSellSlp && ( | {switches.showSellSlp && ( | ||||
| <> | <SectionCtn> | ||||
| <SectionLabel> | |||||
| Quantity | |||||
| </SectionLabel> | |||||
| <SendTokenFormRow> | <SendTokenFormRow> | ||||
| <InputRow> | <InputRow> | ||||
| <Slider | <Slider | ||||
| name={ | name={ | ||||
| 'agoraPartialTokenQty' | 'agoraPartialTokenQty' | ||||
| } | } | ||||
| label={`Offered qty`} | |||||
| value={ | value={ | ||||
| agoraPartialTokenQty | agoraPartialTokenQty | ||||
| } | } | ||||
| handleSlide={ | handleSlide={ | ||||
| handleTokenOfferedSlide | handleTokenOfferedSlide | ||||
| } | } | ||||
| error={ | error={ | ||||
| agoraPartialTokenQtyError | agoraPartialTokenQtyError | ||||
| } | } | ||||
| min={0} | min={0} | ||||
| max={ | max={tokenBalance} | ||||
| tokenBalance | |||||
| } | |||||
| // Step is 1 smallets supported decimal point of the given token | // Step is 1 smallets supported decimal point of the given token | ||||
| step={parseFloat( | step={parseFloat( | ||||
| `1e-${decimals}`, | `1e-${decimals}`, | ||||
| )} | )} | ||||
| allowTypedInput | allowTypedInput | ||||
| /> | /> | ||||
| </InputRow> | </InputRow> | ||||
| </SendTokenFormRow> | </SendTokenFormRow> | ||||
| <SendTokenFormRow> | <SendTokenFormRow> | ||||
| <InputRow> | <InputRow> | ||||
| <LabelAndInputFlex> | <LabelAndInputFlex> | ||||
| <SliderLabel> | <SliderLabel> | ||||
| Price: | Price: | ||||
| </SliderLabel> | </SliderLabel> | ||||
| <ListPriceInput | <ListPriceInput | ||||
| name="tokenListPrice" | name="tokenListPrice" | ||||
| placeholder="Enter list price (per token)" | placeholder="Enter list price (per token)" | ||||
| inputDisabled={ | inputDisabled={ | ||||
| agoraPartialTokenQty === | agoraPartialTokenQty === | ||||
| '' | '' | ||||
| } | } | ||||
| value={ | value={ | ||||
| formData.tokenListPrice | formData.tokenListPrice | ||||
| } | } | ||||
| selectValue={ | selectValue={ | ||||
| selectedCurrency | selectedCurrency | ||||
| } | } | ||||
| selectDisabled={ | selectDisabled={ | ||||
| fiatPrice === | fiatPrice === | ||||
| null | null | ||||
| } | } | ||||
| fiatCode={settings.fiatCurrency.toUpperCase()} | fiatCode={settings.fiatCurrency.toUpperCase()} | ||||
| error={ | error={ | ||||
| tokenListPriceError | tokenListPriceError | ||||
| } | } | ||||
| handleInput={ | handleInput={ | ||||
| handleTokenListPriceChange | handleTokenListPriceChange | ||||
| } | } | ||||
| handleSelect={ | handleSelect={ | ||||
| handleSelectedCurrencyChange | handleSelectedCurrencyChange | ||||
| } | } | ||||
| ></ListPriceInput> | ></ListPriceInput> | ||||
| </LabelAndInputFlex> | </LabelAndInputFlex> | ||||
| </InputRow> | </InputRow> | ||||
| </SendTokenFormRow> | </SendTokenFormRow> | ||||
| <SendTokenFormRow> | <SendTokenFormRow> | ||||
| <InputRow> | <InputRow> | ||||
| <Slider | <Slider | ||||
| name={ | name={ | ||||
| 'agoraPartialMin' | 'agoraPartialMin' | ||||
| } | } | ||||
| disabled={ | disabled={ | ||||
| formData.tokenListPrice === | formData.tokenListPrice === | ||||
| '' || | '' || | ||||
| tokenListPriceError !== | tokenListPriceError !== | ||||
| false | false | ||||
| } | } | ||||
| label={`Min qty`} | label="Min qty" | ||||
| value={ | value={ | ||||
| agoraPartialMin | agoraPartialMin | ||||
| } | } | ||||
| handleSlide={ | handleSlide={ | ||||
| handleTokenMinSlide | handleTokenMinSlide | ||||
| } | } | ||||
| error={ | error={ | ||||
| agoraPartialMinError | agoraPartialMinError | ||||
| } | } | ||||
| min={0} | min={0} | ||||
| max={ | max={ | ||||
| agoraPartialTokenQty | agoraPartialTokenQty | ||||
| } | } | ||||
| // Step is 1 smallets supported decimal point of the given token | // Step is 1 smallets supported decimal point of the given token | ||||
| step={parseFloat( | step={parseFloat( | ||||
| `1e-${decimals}`, | `1e-${decimals}`, | ||||
| )} | )} | ||||
| allowTypedInput | allowTypedInput | ||||
| /> | /> | ||||
| </InputRow> | </InputRow> | ||||
| </SendTokenFormRow> | </SendTokenFormRow> | ||||
| {!tokenListPriceError && | {!tokenListPriceError && | ||||
| formData.tokenListPrice !== | formData.tokenListPrice !== | ||||
| '' && | '' && | ||||
| formData.tokenListPrice !== | formData.tokenListPrice !== | ||||
| null && | null && | ||||
| fiatPrice !== | fiatPrice !== null && ( | ||||
| null && ( | |||||
| <ListPricePreview title="Token List Price"> | <ListPricePreview title="Token List Price"> | ||||
| {getAgoraPartialPricePreview()} | {getAgoraPartialPricePreview()} | ||||
| </ListPricePreview> | </ListPricePreview> | ||||
| )} | )} | ||||
| <SendTokenFormRow> | <SendTokenFormRow> | ||||
| <PrimaryButton | <PrimaryButton | ||||
| style={{ | style={{ | ||||
| marginTop: | marginTop: '12px', | ||||
| '12px', | |||||
| }} | }} | ||||
| disabled={ | disabled={ | ||||
| apiError || | apiError || | ||||
| agoraPartialTokenQtyError !== | agoraPartialTokenQtyError !== | ||||
| false || | false || | ||||
| agoraPartialMinError !== | agoraPartialMinError !== | ||||
| false || | false || | ||||
| tokenListPriceError !== | tokenListPriceError !== | ||||
| false || | false || | ||||
| formData.tokenListPrice === | formData.tokenListPrice === | ||||
| '' || | '' || | ||||
| agoraPartialTokenQty === | agoraPartialTokenQty === | ||||
| '' || | '' || | ||||
| agoraPartialMin === | agoraPartialMin === | ||||
| '' | '' | ||||
| } | } | ||||
| onClick={ | onClick={ | ||||
| previewAgoraPartial | previewAgoraPartial | ||||
| } | } | ||||
| > | > | ||||
| List {tokenName} | List {tokenName} | ||||
| </PrimaryButton> | </PrimaryButton> | ||||
| </SendTokenFormRow> | </SendTokenFormRow> | ||||
| </> | </SectionCtn> | ||||
| )} | )} | ||||
| </> | </> | ||||
| ) | ) | ||||
| )} | )} | ||||
| {!isNftParent && ( | {!isNftParent && ( | ||||
| <> | <> | ||||
| <SwitchHolder> | |||||
| <Switch | |||||
| name="Toggle Send" | |||||
| on="➡️" | |||||
| off="➡️" | |||||
| checked={switches.showSend} | |||||
| handleToggle={() => { | |||||
| // We turn everything else off, whether we are turning this one on or off | |||||
| setSwitches({ | |||||
| ...switchesOff, | |||||
| showSend: | |||||
| !switches.showSend, | |||||
| }); | |||||
| }} | |||||
| /> | |||||
| <SwitchLabel> | |||||
| Send {tokenName} ( | |||||
| {tokenTicker}) | |||||
| </SwitchLabel> | |||||
| </SwitchHolder> | |||||
| {switches.showSend && ( | {switches.showSend && ( | ||||
| <> | <> | ||||
| <SectionCtn> | |||||
| <SendTokenFormRow> | <SendTokenFormRow> | ||||
| <InputRow> | <InputRow> | ||||
| <InputWithScanner | <InputWithScanner | ||||
| label="Address" | |||||
| placeholder={`Address`} | placeholder={`Address`} | ||||
| name="address" | name="address" | ||||
| value={ | value={ | ||||
| formData.address | formData.address | ||||
| } | } | ||||
| handleInput={ | handleInput={ | ||||
| handleTokenAddressChange | handleTokenAddressChange | ||||
| } | } | ||||
| error={ | error={ | ||||
| sendTokenAddressError | sendTokenAddressError | ||||
| } | } | ||||
| /> | /> | ||||
| </InputRow> | </InputRow> | ||||
| </SendTokenFormRow> | </SendTokenFormRow> | ||||
| {!isNftChild && ( | {!isNftChild && ( | ||||
| <SendTokenFormRow> | <SendTokenFormRow> | ||||
| <SendTokenInput | <SendTokenInput | ||||
| label="Amount" | |||||
| name="amount" | name="amount" | ||||
| value={ | value={ | ||||
| formData.amount | formData.amount | ||||
| } | } | ||||
| error={ | error={ | ||||
| sendTokenAmountError | sendTokenAmountError | ||||
| } | } | ||||
| placeholder="Amount" | placeholder="Amount" | ||||
| handleInput={ | handleInput={ | ||||
| handleTokenAmountChange | handleTokenAmountChange | ||||
| } | } | ||||
| handleOnMax={ | handleOnMax={onMax} | ||||
| onMax | |||||
| } | |||||
| /> | /> | ||||
| </SendTokenFormRow> | </SendTokenFormRow> | ||||
| )} | )} | ||||
| <SendTokenFormRow> | <SendTokenFormRow> | ||||
| <PrimaryButton | <PrimaryButton | ||||
| style={{ | style={{ | ||||
| marginTop: | marginTop: '12px', | ||||
| '12px', | |||||
| }} | }} | ||||
| disabled={ | disabled={ | ||||
| apiError || | apiError || | ||||
| sendTokenAmountError !== | sendTokenAmountError !== | ||||
| false || | false || | ||||
| sendTokenAddressError !== | sendTokenAddressError !== | ||||
| false || | false || | ||||
| formData.address === | formData.address === | ||||
| '' || | '' || | ||||
| (!isNftChild && | (!isNftChild && | ||||
| formData.amount === | formData.amount === | ||||
| '') | '') | ||||
| } | } | ||||
| onClick={() => | onClick={() => | ||||
| checkForConfirmationBeforeSendEtoken() | checkForConfirmationBeforeSendEtoken() | ||||
| } | } | ||||
| > | > | ||||
| Send {tokenTicker} | Send {tokenTicker} | ||||
| </PrimaryButton> | </PrimaryButton> | ||||
| </SendTokenFormRow> | </SendTokenFormRow> | ||||
| </SectionCtn> | |||||
| </> | </> | ||||
| )} | )} | ||||
| </> | </> | ||||
| )} | )} | ||||
| {isNftParent && ( | {isNftParent && switches.showMintNft && ( | ||||
| <> | <SectionCtn> | ||||
| <SwitchHolder> | <CreateTokenForm groupTokenId={tokenId} /> | ||||
| <Switch | </SectionCtn> | ||||
| name="Toggle Mint NFT" | |||||
| checked={ | |||||
| switches.showMintNft | |||||
| } | |||||
| handleToggle={() => | |||||
| // We turn everything else off, whether we are turning this one on or off | |||||
| setSwitches({ | |||||
| ...switchesOff, | |||||
| showMintNft: | |||||
| !switches.showMintNft, | |||||
| }) | |||||
| } | |||||
| /> | |||||
| <SwitchLabel> | |||||
| Mint NFT | |||||
| </SwitchLabel> | |||||
| </SwitchHolder> | |||||
| {switches.showMintNft && ( | |||||
| <CreateTokenForm | |||||
| groupTokenId={tokenId} | |||||
| /> | |||||
| )} | )} | ||||
| </> | {!isNftChild && switches.showAirdrop && ( | ||||
| )} | <SectionCtn> | ||||
| {!isNftChild && ( | |||||
| <> | |||||
| <SwitchHolder> | |||||
| <Switch | |||||
| name="Toggle Airdrop" | |||||
| on="🪂" | |||||
| off="🪂" | |||||
| checked={ | |||||
| switches.showAirdrop | |||||
| } | |||||
| handleToggle={() => | |||||
| // We turn everything else off, whether we are turning this one on or off | |||||
| setSwitches({ | |||||
| ...switchesOff, | |||||
| showAirdrop: | |||||
| !switches.showAirdrop, | |||||
| }) | |||||
| } | |||||
| /> | |||||
| <SwitchLabel> | |||||
| Airdrop XEC to {tokenTicker}{' '} | |||||
| holders | |||||
| </SwitchLabel> | |||||
| </SwitchHolder> | |||||
| {switches.showAirdrop && ( | |||||
| <TokenStatsRow> | <TokenStatsRow> | ||||
| <Link | <Link | ||||
| style={{ | style={{ | ||||
| width: '100%', | width: '100%', | ||||
| }} | }} | ||||
| to="/airdrop" | to="/airdrop" | ||||
| state={{ | state={{ | ||||
| airdropEtokenId: | airdropEtokenId: tokenId, | ||||
| tokenId, | |||||
| }} | }} | ||||
| > | > | ||||
| <SecondaryButton | <SecondaryButton | ||||
| style={{ | style={{ | ||||
| marginTop: | marginTop: '12px', | ||||
| '12px', | |||||
| }} | }} | ||||
| > | > | ||||
| Airdrop Calculator | Airdrop Calculator | ||||
| </SecondaryButton> | </SecondaryButton> | ||||
| </Link> | </Link> | ||||
| </TokenStatsRow> | </TokenStatsRow> | ||||
| </SectionCtn> | |||||
| )} | )} | ||||
| </> | {!isNftParent && | ||||
| )} | !isNftChild && | ||||
| {!isNftParent && !isNftChild && ( | switches.showBurn && ( | ||||
| <> | <SectionCtn> | ||||
| <SwitchHolder> | |||||
| <Switch | |||||
| name="Toggle Burn" | |||||
| on="🔥" | |||||
| off="🔥" | |||||
| checked={switches.showBurn} | |||||
| handleToggle={() => | |||||
| // We turn everything else off, whether we are turning this one on or off | |||||
| setSwitches({ | |||||
| ...switchesOff, | |||||
| showBurn: | |||||
| !switches.showBurn, | |||||
| }) | |||||
| } | |||||
| /> | |||||
| <SwitchLabel> | |||||
| Burn {tokenTicker} | |||||
| </SwitchLabel> | |||||
| </SwitchHolder> | |||||
| {switches.showBurn && ( | |||||
| <TokenStatsRow> | <TokenStatsRow> | ||||
| <InputFlex> | <InputFlex> | ||||
| <SendTokenInput | <SendTokenInput | ||||
| label="Amount" | |||||
| name="burnAmount" | name="burnAmount" | ||||
| value={ | value={formData.burnAmount} | ||||
| formData.burnAmount | error={burnTokenAmountError} | ||||
| } | |||||
| error={ | |||||
| burnTokenAmountError | |||||
| } | |||||
| placeholder="Burn Amount" | placeholder="Burn Amount" | ||||
| handleInput={ | handleInput={ | ||||
| handleEtokenBurnAmountChange | handleEtokenBurnAmountChange | ||||
| } | } | ||||
| handleOnMax={ | handleOnMax={onMaxBurn} | ||||
| onMaxBurn | |||||
| } | |||||
| /> | /> | ||||
| <SecondaryButton | <SecondaryButton | ||||
| onClick={ | onClick={ | ||||
| handleBurnAmountInput | handleBurnAmountInput | ||||
| } | } | ||||
| disabled={ | disabled={ | ||||
| burnTokenAmountError !== | burnTokenAmountError !== | ||||
| false || | false || | ||||
| formData.burnAmount === | formData.burnAmount === | ||||
| '' | '' | ||||
| } | } | ||||
| > | > | ||||
| Burn {tokenTicker} | Burn {tokenTicker} | ||||
| </SecondaryButton> | </SecondaryButton> | ||||
| </InputFlex> | </InputFlex> | ||||
| </TokenStatsRow> | </TokenStatsRow> | ||||
| </SectionCtn> | |||||
| )} | )} | ||||
| </> | {mintBatons.length > 0 && switches.showMint && ( | ||||
| )} | <SectionCtn> | ||||
| {mintBatons.length > 0 && ( | |||||
| <SwitchHolder> | |||||
| <Switch | |||||
| name="Toggle Mint" | |||||
| on="⚗️" | |||||
| off="⚗️" | |||||
| checked={switches.showMint} | |||||
| handleToggle={() => | |||||
| // We turn everything else off, whether we are turning this one on or off | |||||
| setSwitches({ | |||||
| ...switchesOff, | |||||
| showMint: | |||||
| !switches.showMint, | |||||
| }) | |||||
| } | |||||
| /> | |||||
| <SwitchLabel>Mint</SwitchLabel> | |||||
| </SwitchHolder> | |||||
| )} | |||||
| {switches.showMint && ( | |||||
| <TokenStatsRow> | <TokenStatsRow> | ||||
| <InputFlex> | <InputFlex> | ||||
| <SendTokenInput | <SendTokenInput | ||||
| label="Amount" | |||||
| name="mintAmount" | name="mintAmount" | ||||
| value={formData.mintAmount} | value={formData.mintAmount} | ||||
| error={mintAmountError} | error={mintAmountError} | ||||
| placeholder="Mint Amount" | placeholder="Mint Amount" | ||||
| handleInput={ | handleInput={ | ||||
| handleMintAmountChange | handleMintAmountChange | ||||
| } | } | ||||
| handleOnMax={onMaxMint} | handleOnMax={onMaxMint} | ||||
| /> | /> | ||||
| <SecondaryButton | <SecondaryButton | ||||
| onClick={ | onClick={ | ||||
| mintOrShowConfirmationModal | mintOrShowConfirmationModal | ||||
| } | } | ||||
| disabled={ | disabled={ | ||||
| mintAmountError !== | mintAmountError !== false || | ||||
| false || | formData.mintAmount === '' | ||||
| formData.mintAmount === | |||||
| '' | |||||
| } | } | ||||
| > | > | ||||
| Mint {tokenTicker} | Mint {tokenTicker} | ||||
| </SecondaryButton> | </SecondaryButton> | ||||
| </InputFlex> | </InputFlex> | ||||
| </TokenStatsRow> | </TokenStatsRow> | ||||
| </SectionCtn> | |||||
| )} | )} | ||||
| </SendTokenForm> | </SendTokenForm> | ||||
| )} | )} | ||||
| </> | </> | ||||
| )} | )} | ||||
| </> | |||||
| )} | |||||
| </OuterCtn> | </OuterCtn> | ||||
| ); | ); | ||||
| }; | }; | ||||
| export default Token; | export default Token; | ||||