diff --git a/cashtab/extension/public/manifest.json b/cashtab/extension/public/manifest.json --- a/cashtab/extension/public/manifest.json +++ b/cashtab/extension/public/manifest.json @@ -3,7 +3,7 @@ "name": "Cashtab", "description": "A browser-integrated eCash wallet from Bitcoin ABC", - "version": "3.17.0", + "version": "3.18.0", "content_scripts": [ { "matches": ["file://*/*", "http://*/*", "https://*/*"], diff --git a/cashtab/package-lock.json b/cashtab/package-lock.json --- a/cashtab/package-lock.json +++ b/cashtab/package-lock.json @@ -1,12 +1,12 @@ { "name": "cashtab", - "version": "2.17.0", + "version": "2.18.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "cashtab", - "version": "2.17.0", + "version": "2.18.0", "dependencies": { "@ant-design/icons": "^5.3.0", "@bitgo/utxo-lib": "^9.33.0", diff --git a/cashtab/package.json b/cashtab/package.json --- a/cashtab/package.json +++ b/cashtab/package.json @@ -1,6 +1,6 @@ { "name": "cashtab", - "version": "2.17.0", + "version": "2.18.0", "private": true, "scripts": { "start": "node scripts/start.js", diff --git a/cashtab/src/components/Send/SendToken.js b/cashtab/src/components/Send/SendToken.js --- a/cashtab/src/components/Send/SendToken.js +++ b/cashtab/src/components/Send/SendToken.js @@ -37,7 +37,7 @@ getMaxMintAmount, } from 'slpv1'; import { sendXec } from 'transactions'; -import { hasEnoughToken } from 'wallet'; +import { hasEnoughToken, decimalizeTokenAmount } from 'wallet'; import Modal from 'components/Common/Modal'; import { toast } from 'react-toastify'; import { @@ -158,11 +158,11 @@ tokenTicker: 'UNCACHED', decimals: 0, }, - genesisSupply: 0, + genesisSupply: '0', genesisMintBatons: 0, }; - const { genesisInfo, genesisSupply, genesisMintBatons } = cachedInfo; + const { genesisInfo, genesisSupply } = cachedInfo; const { tokenName, tokenTicker, url, decimals } = genesisInfo; const [sendTokenAddressError, setSendTokenAddressError] = useState(false); @@ -179,6 +179,14 @@ const [showAirdrop, setShowAirdrop] = useState(false); const [showMint, setShowMint] = useState(false); const [showLargeIconModal, setShowLargeIconModal] = useState(false); + const defaultUncachedTokenInfo = { + circulatingSupply: null, + mintBatons: null, + }; + const [uncachedTokenInfo, setUncachedTokenInfo] = useState( + defaultUncachedTokenInfo, + ); + const [uncachedTokenInfoError, setUncachedTokenInfoError] = useState(false); // Check if the user has mint batons for this token // If they don't, disable the mint switch and label why @@ -200,6 +208,37 @@ const userLocale = getUserLocale(navigator); + const getUncachedTokenInfo = async () => { + let tokenUtxos; + try { + tokenUtxos = await chronik.tokenId(tokenId).utxos(); + let undecimalizedBigIntCirculatingSupply = 0n; + let mintBatons = 0; + for (const utxo of tokenUtxos.utxos) { + const { token } = utxo; + const { amount, isMintBaton } = token; + undecimalizedBigIntCirculatingSupply += BigInt(amount); + if (isMintBaton) { + mintBatons += 1; + } + } + const circulatingSupply = decimalizeTokenAmount( + undecimalizedBigIntCirculatingSupply.toString(), + decimals, + ); + + setUncachedTokenInfo({ circulatingSupply, mintBatons }); + } catch (err) { + console.error(`Error in chronik.tokenId(${tokenId}).utxos()`, err); + setUncachedTokenInfoError(true); + } + }; + + useEffect(() => { + // Get token info that is not practical to cache as it is subject to change + getUncachedTokenInfo(); + }, []); + useEffect(() => { if ( loading === false && @@ -745,9 +784,20 @@ Supply: - {genesisMintBatons === 0 - ? 'Fixed' - : 'Variable'} + {typeof uncachedTokenInfo.circulatingSupply === + 'string' + ? `${decimalizedTokenQtyToLocaleFormat( + uncachedTokenInfo.circulatingSupply, + userLocale, + )}${ + uncachedTokenInfo.mintBatons === + 0 + ? ' (fixed)' + : ' (var.)' + }` + : uncachedTokenInfoError + ? 'Error fetching supply' + : 'Loading...'}