diff --git a/web/cashtab/src/components/Common/Ticker.js b/web/cashtab/src/components/Common/Ticker.js index 5838d0a9f..1834522b9 100644 --- a/web/cashtab/src/components/Common/Ticker.js +++ b/web/cashtab/src/components/Common/Ticker.js @@ -1,1659 +1,1661 @@ import mainLogo from 'assets/logo_primary.png'; import tokenLogo from 'assets/logo_secondary.png'; import BigNumber from 'bignumber.js'; export const currency = { name: 'eCash', ticker: 'XEC', logo: mainLogo, legacyPrefix: 'bitcoincash', coingeckoId: 'ecash', defaultFee: 2.01, dustSats: 550, etokenSats: 546, cashDecimals: 2, chronikUrl: 'https://chronik.fabien.cash', blockExplorerUrl: 'https://explorer.e.cash', blockExplorerUrlTestnet: 'https://texplorer.bitcoinabc.org', pdfReceiptUrl: 'https://blockchair.com/ecash/transaction', tokenName: 'eToken', tokenTicker: 'eToken', tokenIconSubmitApi: 'https://icons.etokens.cash/new', tokenLogo: tokenLogo, tokenIconsUrl: 'https://etoken-icons.s3.us-west-2.amazonaws.com', tokenDbUrl: 'https://tokendb.kingbch.com', txHistoryCount: 10, xecApiBatchSize: 20, websocketDisconnectedRefreshInterval: 5000, // 1000 = 1s websocketConnectedRefreshInterval: 10000, defaultCashtabCache: { tokenInfoById: {}, }, defaultSettings: { fiatCurrency: 'usd', sendModal: false, autoCameraOn: true, hideMessagesFromUnknownSenders: false, }, notificationDurationShort: 3, notificationDurationLong: 5, localStorageMaxCharacters: 24, newTokenDefaultUrl: 'https://cashtab.com/', opReturn: { opReturnPrefixHex: '6a', opReturnPrefixDec: '106', opReturnAppPrefixLengthHex: '04', opPushDataOne: '4c', appPrefixesHex: { eToken: '534c5000', cashtab: '00746162', cashtabEncrypted: '65746162', airdrop: '64726f70', }, encryptedMsgCharLimit: 94, unencryptedMsgCharLimit: 145, }, settingsValidation: { fiatCurrency: [ 'usd', 'idr', 'krw', 'cny', 'zar', 'vnd', 'cad', 'nok', 'eur', 'gbp', 'jpy', 'try', 'rub', 'inr', 'brl', 'php', 'ils', 'clp', 'twd', 'hkd', 'bhd', 'sar', 'aud', 'nzd', 'chf', ], sendModal: [true, false], autoCameraOn: [true, false], hideMessagesFromUnknownSenders: [true, false], }, fiatCurrencies: { usd: { name: 'US Dollar', symbol: '$', slug: 'usd' }, aud: { name: 'Australian Dollar', symbol: '$', slug: 'aud' }, bhd: { name: 'Bahraini Dinar', symbol: 'BD', slug: 'bhd' }, brl: { name: 'Brazilian Real', symbol: 'R$', slug: 'brl' }, gbp: { name: 'British Pound', symbol: '£', slug: 'gbp' }, cad: { name: 'Canadian Dollar', symbol: '$', slug: 'cad' }, clp: { name: 'Chilean Peso', symbol: '$', slug: 'clp' }, cny: { name: 'Chinese Yuan', symbol: '元', slug: 'cny' }, eur: { name: 'Euro', symbol: '€', slug: 'eur' }, hkd: { name: 'Hong Kong Dollar', symbol: 'HK$', slug: 'hkd' }, inr: { name: 'Indian Rupee', symbol: '₹', slug: 'inr' }, idr: { name: 'Indonesian Rupiah', symbol: 'Rp', slug: 'idr' }, ils: { name: 'Israeli Shekel', symbol: '₪', slug: 'ils' }, jpy: { name: 'Japanese Yen', symbol: '¥', slug: 'jpy' }, krw: { name: 'Korean Won', symbol: '₩', slug: 'krw' }, nzd: { name: 'New Zealand Dollar', symbol: '$', slug: 'nzd' }, nok: { name: 'Norwegian Krone', symbol: 'kr', slug: 'nok' }, php: { name: 'Philippine Peso', symbol: '₱', slug: 'php' }, rub: { name: 'Russian Ruble', symbol: 'р.', slug: 'rub' }, twd: { name: 'New Taiwan Dollar', symbol: 'NT$', slug: 'twd' }, sar: { name: 'Saudi Riyal', symbol: 'SAR', slug: 'sar' }, zar: { name: 'South African Rand', symbol: 'R', slug: 'zar' }, chf: { name: 'Swiss Franc', symbol: 'Fr.', slug: 'chf' }, try: { name: 'Turkish Lira', symbol: '₺', slug: 'try' }, vnd: { name: 'Vietnamese đồng', symbol: 'đ', slug: 'vnd' }, }, - bannedTokenTickers: [ + coingeckoTop500Tickers: [ 'btc', 'eth', 'usdt', 'bnb', 'usdc', 'xrp', 'busd', 'ada', 'sol', 'doge', 'dot', 'matic', 'steth', 'shib', 'dai', 'trx', 'wbtc', 'avax', 'uni', 'okb', 'leo', 'ltc', 'atom', 'link', 'etc', 'ftt', 'xlm', 'cro', 'qnt', 'xmr', 'near', 'algo', 'bch', 'vet', 'lunc', 'flow', 'fil', 'hbar', 'ape', 'egld', 'wbt', 'frax', 'icp', 'apt', 'aave', 'xtz', 'xcn', 'ht', 'tkx', 'sand', 'mana', 'ldo', 'eos', 'theta', 'chz', 'cusdc', 'kcs', 'bsv', 'usdp', 'axs', 'tusd', 'mkr', 'evmos', 'btt', 'usdd', 'xec', 'miota', 'ethw', 'klay', 'zec', 'cdai', 'gt', 'cake', 'grt', 'btse', 'neo', 'osmo', 'ceth', 'hnt', 'snx', 'nexo', 'ftm', 'ar', 'xrd', 'paxg', 'cspr', 'crv', 'kava', 'bit', 'ens', 'twt', 'dash', 'rune', 'zil', 'rpl', 'xdc', 'enj', 'bat', 'xaut', 'stx', 'fxs', 'cel', 'mina', 'dcr', 'dfi', 'amp', 'luna', 'rvn', 'ustc', 'cusdt', 'cvx', '1inch', 'mex', 'comp', 'omi', 'hot', 'xem', 'gusd', 'celo', 'waves', 'imx', 'btg', 'lrc', 'ksm', 'rose', 'nxm', 'tfuel', 'gmx', 'gno', 'ohm', 'gmt', 'qtum', 'sushi', 'safemoon', 'okt', 'srm', 'iost', 'glm', 'jst', 'hbtc', 'gala', 'kda', 'cvxcrv', 'iotx', 'rsr', 'yfi', 'poly', 'elg', 'lpt', 'ankr', 'bal', 'omg', 'bone', 'msol', 'syn', 'zrx', 'mc', 'one', 'reth', 'eurt', 'dydx', 'mim', 'dag', 'flux', 'icx', 'glmr', 'woo', 'juno', 'rbn', 'ont', 'babydoge', 'op', 'husd', 'brise', 'lusd', 'nft', 'ewt', 'nu', 'chsb', 'ln', 'xcm', '10set', 'alusd', 'waxp', 'hive', 'vvs', 'azero', 'xch', 'deso', 'zen', 'mdx', 'sc', 'scrt', 'sxp', 'rndr', 'audio', 'uma', 'sgb', 'inj', 'dao', 'sfm', 'cet', 'lsk', 'pla', 'skl', 'dgb', 'elon', 'astr', 'slp', 'metis', 'fx', 'eurs', 'pundix', 'mxc', 'kiro', 'erg', 'gfarm2', 'stsol', 'vgx', 'ckb', 'reef', 'usdn', 'ren', 'lyxe', 'api3', 'coti', 'looks', 'dexe', 'snt', 'pltc', 'uqc', 'kub', 'ceek', 'usdx', 'scho', 'prom', 'tribe', 'veri', 'pyr', 'xsushi', 'win', 'med', 'mpl', 'pokt', 'boba', 'cvc', 'xno', 'sys', 'gns', 'steem', 'orbs', 'ardr', 'mbox', 'mvl', 'tryb', 'ever', 'wrx', 'kuji', 'nmr', 'pha', 'mx', 'ult', 'seth2', 'rad', 'bsw', 'aca', 'people', 'spell', 'knc', 'bico', 'alice', 'hxro', 'eul', 'multi', 'plt', 'c98', 'raca', 'exrd', 'celr', 'req', 'plex', 'powr', 'bnt', 'ctsi', 'ilv', 'ctc', 'temple', 'tel', 'krd', 'cfx', 'uos', 'dka', 'nrv', 'chr', 'xprt', 'dent', 'fun', 'asd', 'arrr', 'savax', 'magic', 'rlc', 'bdx', 'emaid', 'xyo', 'euroc', 'quack', 'strax', 'wcfg', 'cfg', 'aethc', 'tsuka', 'btc.b', 'qkc', 'renbtc', 'stpt', 'canto', 'vtho', 'gal', 'mlk', 'fidu', 'ton', 'trac', 'dock', 'ctk', 'ocean', 'ray', 'stg', 'shr', 'ogn', 'mnw', 'pcx', 'sx', 'susd', 'joe', 'nest', 'kilt', 'aurora', 'h2o', 'keep', 'flexusd', 'mtrg', 'xvs', 'bfc', 'ant', 'ccd', 'ssv', 'floki', 'sfund', 'cdt', 'mtl', 'ark', 'sure', 'xmon', 'tlm', 'btcst', 'stmx', 'itamcube', 'koin', 'lqty', 'rev', 'utk', 'storj', 'movr', 'elf', 'xido', 'hez', 'core', 'efi', 'aergo', 'stars', 'zz', 'wozx', 'fei', 'ach', 'fet', 'sun', 'agix', 'gog', 'sofi', 'ride', 'nkn', 'mtd', 'ads', 'bor', 'sweat', 'oxt', 'iq', 'badger', 'dodo', 'fold', 'xsgd', 'rly', 'xvg', 'rep', 'dawn', 'gbex', 'ygg', 'strk', 'beta', 'mimatic', 'alpha', 'manc', 'phb', 'koda', 'dpx', 'dero', 'ghst', 'cusd', 'meta', 'ufo', 'etn', 'divi', 'saitama', 'mask', 'dusk', 'santos', 'akt', 'nxs', 'bond', 'rif', 'mintme', 'yfii', 'wnxm', 'bitci', 'mft', 'ibeur', 'tlos', 'dc', 'ata', 'alpaca', 'fer', 'xcad', 'blid', 'band', 'wmt', 'gods', 'egc', 'axn', 'bake', 'pols', 'auction', 'kishu', 'ama', 'tt', 'usdk', 'flm', 'ageur', 'zig', 'poop', 'dome', 'vega', 'sfp', 'cbat', 'vra', 'ampl', 'mln', 'dola', 'ousd', 'kp3r', 'cocos', 'hydra', 'pond', 'tru', 'dei', 'ern', 'idex', 'nebl', 'time', 'loomold', 'qrdo', 'lcx', 'avinoc', 'perp', 'hunt', 'cweb', 'b2m', 'clv', 'klv', 'xki', 'lto', 'ava', 'ron', 'hc', 'opt2', 'astrafer', 'kunci', 'itheum', 'leash', 'tomo', 'cqt', 'volt', 'orn', 'regen', 'super', 'gxc', 'wan', 'forth', 'sb', ], - bannedTokenNames: [ + coingeckoTop500Names: [ 'Bitcoin', 'Ethereum', 'Tether', 'BNB', 'USD Coin', 'XRP', 'Binance USD', 'Cardano', 'Solana', 'Dogecoin', 'Polkadot', 'Polygon', 'Lido Staked Ether', 'Shiba Inu', 'Dai', 'TRON', 'Wrapped Bitcoin', 'Avalanche', 'Uniswap', 'OKB', 'LEO Token', 'Litecoin', 'Cosmos Hub', 'Chainlink', 'Ethereum Classic', 'FTX', 'Stellar', 'Cronos', 'Quant', 'Monero', 'NEAR Protocol', 'Algorand', 'Bitcoin Cash', 'VeChain', 'Terra Luna Classic', 'Flow', 'Filecoin', 'Hedera', 'ApeCoin', 'Elrond', 'WhiteBIT Token', 'Frax', 'Internet Computer', 'Aptos', 'Aave', 'Tezos', 'Chain', 'Huobi', 'Tokenize Xchange', 'The Sandbox', 'Decentraland', 'Lido DAO', 'EOS', 'Theta Network', 'Chiliz', 'cUSDC', 'KuCoin', 'Bitcoin SV', 'Pax Dollar', 'Axie Infinity', 'TrueUSD', 'Maker', 'Evmos', 'BitTorrent', 'USDD', 'eCash', 'Klaytn', 'IOTA', 'EthereumPoW', 'Zcash', 'cDAI', 'PancakeSwap', 'Gate', 'The Graph', 'NEO', 'BTSE Token', 'cETH', 'Osmosis', 'Helium', 'Synthetix Network', 'NEXO', 'Fantom', 'Arweave', 'Radix', 'Casper Network', 'PAX Gold', 'Curve DAO', 'Kava', 'BitDAO', 'Ethereum Name Service', 'Dash', 'Trust Wallet', 'Zilliqa', 'THORChain', 'Rocket Pool', 'XDC Network', 'Enjin Coin', 'Basic Attention', 'Tether Gold', 'Stacks', 'Frax Share', 'Celsius Network', 'Mina Protocol', 'Decred', 'DeFiChain', 'Amp', 'Terra', 'Ravencoin', 'cUSDT', 'TerraClassicUSD', 'Convex Finance', '1inch', 'Maiar DEX', 'Compound', 'ECOMI', 'Holo', 'NEM', 'Gemini Dollar', 'Celo', 'Waves', 'Bitcoin Gold', 'Immutable X', 'Loopring', 'Kusama', 'Oasis Network', 'Theta Fuel', 'Nexus Mutual', 'GMX', 'Gnosis', 'Olympus', 'STEPN', 'Qtum', 'Sushi', 'SafeMoon [OLD]', 'OKC', 'Serum', 'IOST', 'Golem', 'JUST', 'Gala', 'Huobi BTC', 'Kadena', 'IoTeX', 'Convex CRV', 'Reserve Rights', 'yearn.finance', 'Polymath', 'Ankr', 'Escoin', 'Livepeer', 'Balancer', 'Bone ShibaSwap', 'OMG Network', 'Marinade staked SOL', 'Merit Circle', '0x', 'Harmony', 'Synapse', 'Rocket Pool ETH', 'Euro Tether', 'dYdX', 'Magic Internet Money', 'Flux', 'ICON', 'Constellation', 'Moonbeam', 'WOO Network', 'JUNO', 'Optimism', 'Ontology', 'Ribbon Finance', 'Bitgert', 'HUSD', 'Baby Doge Coin', 'Energy Web', 'APENFT', 'SwissBorg', 'Liquity USD', 'NuCypher', 'Coinmetro', 'Tenset', 'LINK', 'Alchemix USD', 'WAX', 'Hive', 'VVS Finance', 'Decentralized Social', 'Aleph Zero', 'Chia', 'Horizen', 'Mdex', 'Siacoin', 'Secret', 'SXP', 'Render', 'UMA', 'Audius', 'Injective', 'DAO Maker', 'Songbird', 'SafeMoon', 'CoinEx', 'Lisk', 'PlayDapp', 'SKALE', 'DigiByte', 'Dogelon Mars', 'Astar', 'Smooth Love Potion', 'Metis', 'Function X', 'Pundi X', 'STASIS EURO', 'MXC', 'Kirobo', 'Ergo', 'Gains Farm', 'Lido Staked SOL', 'Voyager VGX', 'Nervos Network', 'Reef', 'Neutrino USD', 'REN', 'LUKSO', 'API3', 'COTI', 'LooksRare', 'DeXe', 'Status', 'PlatonCoin', 'Uquid Coin', 'Bitkub Coin', 'CEEK Smart VR', 'USDX', 'Scholarship Coin', 'Tribe', 'Prom', 'Veritaseum', 'xSUSHI', 'Vulcan Forged', 'WINkLink', 'Medibloc', 'Maple', 'Pocket Network', 'Civic', 'Nano', 'Boba Network', 'Syscoin', 'Gains Network', 'Velas', 'Steem', 'Orbs', 'Ardor', 'Mobox', 'MVL', 'BiLira', 'Everscale', 'WazirX', 'Kujira', 'Numeraire', 'Phala Network', 'MX', 'Shardus', 'sETH2', 'Radicle', 'Biswap', 'Acala', 'ConstitutionDAO', 'Spell', 'Kyber Network Crystal', 'Biconomy', 'My Neighbor Alice', 'Hxro', 'Euler', 'Multichain', 'Poollotto.finance', 'Coin98', 'Radio Caca', 'e-Radix', 'Celer Network', 'Request', 'PLEX', 'Power Ledger', 'Bancor Network', 'Cartesi', 'Illuvium', 'Creditcoin', 'Telcoin', 'TempleDAO', 'Krypton DAO', 'Conflux', 'Ultra', 'dKargo', 'Nerve Finance', 'Chromia', 'Persistence', 'Dent', 'FUN', 'AscendEx', 'Pirate Chain', 'BENQI Liquid Staked AVAX', 'iExec RLC', 'Magic', 'Beldex', 'MaidSafeCoin', 'XYO Network', 'Euro Coin', 'Rich Quack', 'Stratis', 'Wrapped Centrifuge', 'Centrifuge', 'Ankr Reward-Bearing Staked ETH', 'Bitcoin Avalanche Bridged (BTC.b)', 'QuarkChain', 'renBTC', 'STP', 'CANTO', 'VeThor', 'Galxe', 'MiL.k Alliance', 'Fidu', 'Dejitaru Tsuka', 'Tokamak Network', 'OriginTrail', 'Dock', 'Shentu', 'Ocean Protocol', 'Raydium', 'Stargate Finance', 'Share', 'Origin Protocol', 'Morpheus Network', 'ChainX', 'SX Network', 'sUSD', 'JOE', 'Nest Protocol', 'KILT Protocol', 'Aurora', 'H2O Dao', 'Keep Network', 'flexUSD', 'Meter Governance', 'Venus', 'Bifrost', 'Aragon', 'Concordium', 'SSV Network', 'FLOKI', 'Seedify.fund', 'Blox', 'Metal', 'Ark', 'inSure DeFi', 'XMON', 'Alien Worlds', 'BTC Standard Hashrate Token', 'StormX', 'CUBE', 'Koinos', 'Liquity', 'Revain', 'Utrust', 'Storj', 'Moonriver', 'aelf', 'Xido Finance', 'Hermez Network', 'cVault.finance', 'Efinity', 'Aergo', 'Stargaze', 'ZigZag', 'Efforce', 'Fei USD', 'Alchemy Pay', 'Fetch.ai', 'Sun Token', 'SingularityNET', 'Guild of Guardians', 'RAI Finance', 'holoride', 'NKN', 'Minted', 'Adshares', 'BoringDAO [OLD]', 'Sweatcoin - Sweat Economy', 'Orchid Protocol', 'IQ', 'Badger DAO', 'DODO', 'Manifold Finance', 'XSGD', 'Rally', 'Verge', 'Augur', 'Globiance Exchange', 'Yield Guild Games', 'Strike', 'Dawn Protocol', 'MAI', 'Beta Finance', 'Alpha Venture DAO', 'Mancium', 'Phoenix Global [OLD]', 'Koda Cryptocurrency', 'Dopex', 'Dero', 'Aavegotchi', 'UFO Gaming', 'Metadium', 'Celo Dollar', 'Electroneum', 'Divi', 'Saitama', 'Mask Network', 'DUSK Network', 'Santos FC Fan Token', 'Akash Network', 'Nexus', 'BarnBridge', 'RSK Infrastructure Framework', 'MintMe.com Coin', 'DFI.money', 'Wrapped NXM', 'Hifi Finance', 'Bitcicoin', 'Iron Bank EURO', 'Telos', 'Dogechain', 'XCAD Network', 'Automata', 'Alpaca Finance', 'Ferro', 'Bolide', 'World Mobile Token', 'Band Protocol', 'Gods Unchained', 'EverGrow Coin', 'Axion', 'BakerySwap', 'Polkastarter', 'Bounce', 'Kishu Inu', 'MrWeb Finance [OLD]', 'ThunderCore', 'USDK', 'Flamingo Finance', 'agEUR', 'Zignaly', 'Raresama', 'Everdome', 'Vega Protocol', 'SafePal', 'cBAT', 'Verasity', 'Ampleforth', 'Enzyme', 'Dola', 'Keep3rV1', 'Origin Dollar', 'COCOS BCX', 'Hydra', 'Marlin', 'TrueFi', 'DEI', 'Ethernity Chain', 'IDEX', 'Neblio', 'chrono.tech', 'Loom Network (OLD)', 'LCX', 'Qredo', 'AVINOC', 'Perpetual Protocol', 'Hunt', 'Coinweb', 'Bit2Me', 'Klever', 'Clover Finance', 'KI', 'LTO Network', 'Travala.com', 'Ronin', 'HyperCash', 'Optimus OPT2', 'Astrafer', 'Kunci Coin', 'Itheum', 'Doge Killer', 'TomoChain', 'Covalent', 'Volt Inu', 'Orion Protocol', 'Regen', 'SuperFarm', 'GXChain', 'Vite', 'Wanchain', 'Ampleforth Governance', ], - bannedTokenIds: [ + coingeckoTop500Ids: [ 'bitcoin', 'ethereum', 'tether', 'binancecoin', 'usd-coin', 'ripple', 'binance-usd', 'cardano', 'solana', 'dogecoin', 'polkadot', 'matic-network', 'staked-ether', 'shiba-inu', 'dai', 'tron', 'avalanche-2', 'wrapped-bitcoin', 'uniswap', 'okb', 'litecoin', 'leo-token', 'cosmos', 'chainlink', 'ethereum-classic', 'ftx-token', 'stellar', 'crypto-com-chain', 'quant-network', 'monero', 'near', 'algorand', 'bitcoin-cash', 'vechain', 'terra-luna', 'flow', 'filecoin', 'apecoin', 'hedera-hashgraph', 'tokenize-xchange', 'elrond-erd-2', 'whitebit', 'internet-computer', 'tezos', 'frax', 'aptos', 'chain-2', 'huobi-token', 'aave', 'the-sandbox', 'decentraland', 'lido-dao', 'eos', 'theta-token', 'chiliz', 'compound-usd-coin', 'kucoin-shares', 'axie-infinity', 'bitcoin-cash-sv', 'paxos-standard', 'true-usd', 'maker', 'evmos', 'bittorrent', 'usdd', 'ecash', 'iota', 'ethereum-pow-iou', 'zcash', 'gatechain-token', 'pancakeswap-token', 'cdai', 'klay-token', 'the-graph', 'neo', 'compound-ether', 'osmosis', 'btse-token', 'havven', 'helium', 'nexo', 'fantom', 'arweave', 'pax-gold', 'radix', 'curve-dao-token', 'casper-network', 'kava', 'bitdao', 'trust-wallet-token', 'dash', 'ethereum-name-service', 'zilliqa', 'thorchain', 'enjincoin', 'basic-attention-token', 'blockstack', 'rocket-pool', 'xdce-crowd-sale', 'tether-gold', 'frax-share', 'mina-protocol', 'celsius-degree-token', 'decred', 'defichain', 'amp-token', 'terra-luna-2', 'ravencoin', '1inch', 'convex-finance', 'terrausd', 'maiar-dex', 'compound-usdt', 'compound-governance-token', 'nem', 'holotoken', 'ecomi', 'gemini-dollar', 'celo', 'theta-fuel', 'loopring', 'waves', 'kusama', 'immutable-x', 'nxm', 'bitcoin-gold', 'oasis-network', 'gnosis', 'gmx', 'olympus', 'qtum', 'stepn', 'sushi', 'safemoon', 'serum', 'oec-token', 'iostoken', 'golem', 'gala', 'iotex', 'kadena', 'huobi-btc', 'yearn-finance', 'reserve-rights-token', 'just', 'convex-crv', 'polymath', 'balancer', 'ankr', 'livepeer', 'escoin-token', 'omisego', 'bone-shibaswap', 'msol', 'rocket-pool-eth', '0x', 'harmony', 'synapse-2', 'ribbon-finance', 'tether-eurt', 'merit-circle', 'energy-web-token', 'magic-internet-money', 'icon', 'dydx', 'moonbeam', 'zelcash', 'woo-network', 'constellation-labs', 'optimism', 'juno-network', 'ontology', 'baby-doge-coin', 'nucypher', 'husd', 'tenset', 'bitrise-token', 'link', 'liquity-usd', 'coinmetro', 'swissborg', 'apenft', 'wax', 'alchemix-usd', 'hive', 'vvs-finance', 'bitclout', 'aleph-zero', 'zencash', 'chia', 'siacoin', 'mdex', 'secret', 'songbird', 'swipe', 'render-token', 'audius', 'injective-protocol', 'uma', 'dao-maker', 'playdapp', 'safemoon-2', 'coinex-token', 'lisk', 'dogelon-mars', 'digibyte', 'skale', 'astar', 'smooth-love-potion', 'gains-farm', 'metis-token', 'pundi-x-2', 'lukso-token', 'mxc', 'fx-coin', 'stasis-eurs', 'lido-staked-sol', 'kirobo', 'ergo', 'republic-protocol', 'reef', 'ethos', 'nervos-network', 'coti', 'api3', 'neutrino', 'looksrare', 'status', 'dexe', 'platoncoin', 'bitkub-coin', 'ceek', 'gains-network', 'tribe-2', 'scholarship-coin', 'usdx', 'veritaseum', 'prometeus', 'medibloc', 'xsushi', 'syscoin', 'vulcan-forged', 'wink', 'civic', 'nano', 'boba-network', 'maple', 'ardor', 'pocket-network', 'uquid-coin', 'orbs', 'mass-vehicle-ledger', 'mobox', 'everscale', 'velas', 'krypton-dao', 'bilira', 'wazirx', 'steem', 'numeraire', 'kujira', 'seth2', 'constitutiondao', 'hxro', 'mx-token', 'my-neighbor-alice', 'kyber-network-crystal', 'radicle', 'spell-token', 'shardus', 'acala', 'multichain', 'biswap', 'pha', 'richquack', 'biconomy', 'coin98', 'euler', 'canto', 'celer-network', 'cartesi', 'request-network', 'illuvium', 'milk-alliance', 'radio-caca', 'telcoin', 'power-ledger', 'bancor', 'poollotto-finance', 'ultra', 'e-radix', 'creditcoin-2', 'conflux-token', 'dkargo', 'chromaway', 'temple', 'magic', 'plex', 'dent', 'benqi-liquid-staked-avax', 'pirate-chain', 'nerve-finance', 'funfair', 'persistence', 'beldex', 'iexec-rlc', 'maidsafecoin', 'euro-coin', 'ankreth', 'wrapped-centrifuge', 'centrifuge', 'stratis', 'stp-network', 'xyo-network', 'asd', 'quark-chain', 'aurora-near', 'renbtc', 'dejitaru-tsuka', 'bitcoin-avalanche-bridged-btc-b', 'project-galaxy', 'origintrail', 'fidu', 'meter', 'vethor-token', 'tokamak-network', 'raydium', 'certik', 'dock', 'sx-network', 'ocean-protocol', 'origin-protocol', 'sharering', 'kilt-protocol', 'morpheus-network', 'joe', 'chainx', 'insure', 'keep-network', 'stargate-finance', 'nusd', 'aragon', 'venus', 'flex-usd', 'bifrost', 'h2o-dao', 'ssv-network', 'nest', 'ark', 'concordium', 'metal', 'floki', 'koinos', 'alien-worlds', 'btc-standard-hashrate-token', 'storm', 'seedify-fund', 'utrust', 'liquity', 'blox', 'revain', 'storj', 'cube', 'moonriver', 'xmon', 'aelf', 'cvault-finance', 'hermez-network-token', 'efinity', 'wozx', 'guild-of-guardians', 'aergo', 'singularitynet', 'alchemy-pay', 'fei-usd', 'fetch-ai', 'zigzag-2', 'minted', 'everipedia', 'sun-token', 'stargaze', 'sweatcoin', 'rai-finance', 'nkn', 'adshares', 'orchid-protocol', 'augur', 'boringdao-[old]', 'holoride', 'badger-dao', 'dodo', 'dogechain', 'santos-fc-fan-token', 'rally-2', 'divi', 'xido-finance', 'dopex', 'strike', 'xsgd', 'yield-guild-games', 'dawn-protocol', 'mancium', 'verge', 'beta-finance', 'manifold-finance', 'alpha-finance', 'mimatic', 'dero', 'metadium', 'celo-dollar', 'red-pulse', 'dusk-network', 'koda-finance', 'aavegotchi', 'ufo-gaming', 'mask-network', 'saitama-inu', 'akash-network', 'electroneum', 'globiance-exchange', 'nexus', 'rif-token', 'barnbridge', 'wrapped-nxm', 'bitcicoin', 'yfii-finance', 'origin-dollar', 'xcad-network', 'automata', 'iron-bank-euro', 'mainframe', 'webchain', 'kishu-inu', 'world-mobile-token', 'telos', 'alpaca-finance', 'band-protocol', 'ferro', 'bolide', 'thunder-token', 'gods-unchained', 'bakerytoken', 'auction', 'axion', 'evergrowcoin', 'vega-protocol', 'polkastarter', 'flamingo-finance', 'mrweb-finance', 'ampleforth', 'everdome', 'usdk', 'ageur', 'compound-basic-attention-token', 'verasity', 'zignaly', 'safepal', 'keep3rv1', 'dola-usd', 'truefi', 'cocos-bcx', 'raresama', 'hydra', 'marlin', 'aurora-dao', 'dei-token', 'ethernity-chain', 'loom-network', 'chronobank', 'qredo', 'hunt-token', 'step-app-fitfi', 'lcx', 'avinoc', 'bit2me', 'neblio', 'melon', 'optimus-opt2', 'perpetual-protocol', 'ronin', 'klever', 'coinweb', 'volt-inu-2', 'astrafer', 'concierge-io', 'leash', 'tomochain', 'clover-finance', 'orion-protocol', 'lto-network', 'hshare', 'regen', 'itheum', 'ki', 'covalent', 'unibright', 'kunci-coin', 'ampleforth-governance-token', 'ellipsis', 'superfarm', ], + bannedTickers: ['ebtc'], + bannedNames: ['ebitcoin'], }; export function parseAddressForParams(addressString) { // Build return obj const addressInfo = { address: '', queryString: null, amount: null, }; // Parse address string for parameters const paramCheck = addressString.split('?'); let cleanAddress = paramCheck[0]; addressInfo.address = cleanAddress; // Check for parameters // only the amount param is currently supported let queryString = null; let amount = null; if (paramCheck.length > 1) { queryString = paramCheck[1]; addressInfo.queryString = queryString; const addrParams = new URLSearchParams(queryString); if (addrParams.has('amount')) { // Amount in XEC try { amount = new BigNumber( parseFloat(addrParams.get('amount')), ).toString(); } catch (err) { amount = null; } } } addressInfo.amount = amount; return addressInfo; } diff --git a/web/cashtab/src/utils/validation.js b/web/cashtab/src/utils/validation.js index 918e5a8ce..185f70704 100644 --- a/web/cashtab/src/utils/validation.js +++ b/web/cashtab/src/utils/validation.js @@ -1,522 +1,538 @@ import BigNumber from 'bignumber.js'; import { currency } from 'components/Common/Ticker.js'; import { fromSatoshisToXec } from 'utils/cashMethods'; import cashaddr from 'ecashaddrjs'; // Validate cash amount export const shouldRejectAmountInput = ( cashAmount, selectedCurrency, fiatPrice, totalCashBalance, ) => { // Take cashAmount as input, a string from form input let error = false; let testedAmount = new BigNumber(cashAmount); if (selectedCurrency !== currency.ticker) { // Ensure no more than currency.cashDecimals decimal places testedAmount = new BigNumber(fiatToCrypto(cashAmount, fiatPrice)); } // Validate value for > 0 if (isNaN(testedAmount)) { error = 'Amount must be a number'; } else if (testedAmount.lte(0)) { error = 'Amount must be greater than 0'; } else if (testedAmount.lt(fromSatoshisToXec(currency.dustSats))) { error = `Send amount must be at least ${fromSatoshisToXec( currency.dustSats, ).toString()} ${currency.ticker}`; } else if (testedAmount.gt(totalCashBalance)) { error = `Amount cannot exceed your ${currency.ticker} balance`; } else if (!isNaN(testedAmount) && testedAmount.toString().includes('.')) { if ( testedAmount.toString().split('.')[1].length > currency.cashDecimals ) { error = `${currency.ticker} transactions do not support more than ${currency.cashDecimals} decimal places`; } } // return false if no error, or string error msg if error return error; }; export const fiatToCrypto = ( fiatAmount, fiatPrice, cashDecimals = currency.cashDecimals, ) => { let cryptoAmount = new BigNumber(fiatAmount) .div(new BigNumber(fiatPrice)) .toFixed(cashDecimals); return cryptoAmount; }; export const isProbablyNotAScamTokenName = tokenName => { // convert to lower case, trim leading and trailing spaces // split, filter then join on ' ' for cases where user inputs multiple spaces const sanitizedTokenName = tokenName .toLowerCase() .trim() .split(' ') .filter(string => string) .join(' '); return ( - !currency.bannedTokenNames.includes(sanitizedTokenName) && + !currency.coingeckoTop500Names.includes(sanitizedTokenName) && // for cases where user adds spaces between e a c h letter - !currency.bannedTokenNames.includes( + !currency.coingeckoTop500Names.includes( sanitizedTokenName.split(' ').join(''), ) && - // cross reference with bannedTokenTickers - !currency.bannedTokenTickers.includes(sanitizedTokenName) && - !currency.bannedTokenTickers.includes( + // cross reference with coingeckoTop500Tickers + !currency.coingeckoTop500Tickers.includes(sanitizedTokenName) && + !currency.coingeckoTop500Tickers.includes( sanitizedTokenName.split(' ').join(''), ) && - //cross reference with bannedTokenIds - !currency.bannedTokenIds.includes(sanitizedTokenName) && - !currency.bannedTokenIds.includes( + //cross reference with coingeckoTop500Ids + !currency.coingeckoTop500Ids.includes(sanitizedTokenName) && + !currency.coingeckoTop500Ids.includes( sanitizedTokenName.split(' ').join(''), ) && //cross reference with bannedFiatCurrencies !currency.settingsValidation.fiatCurrency.includes( sanitizedTokenName, ) && !currency.settingsValidation.fiatCurrency.includes( sanitizedTokenName.split(' ').join(''), - ) + ) && + //cross reference with bannedTickers + !currency.bannedTickers.includes(sanitizedTokenName) && + !currency.bannedTickers.includes( + sanitizedTokenName.split(' ').join(''), + ) && + //cross reference with bannedNames + !currency.bannedNames.includes(sanitizedTokenName) && + !currency.bannedNames.includes(sanitizedTokenName.split(' ').join('')) ); }; export const isProbablyNotAScamTokenTicker = tokenTicker => { // convert to lower case, trim leading and trailing spaces // split, filter then join on ' ' for cases where user inputs multiple spaces const sanitizedTokenTicker = tokenTicker .toLowerCase() .trim() .split(' ') .filter(string => string) .join(''); return ( - !currency.bannedTokenTickers.includes(sanitizedTokenTicker) && + !currency.coingeckoTop500Tickers.includes(sanitizedTokenTicker) && // for cases where user adds spaces between e a c h letter - !currency.bannedTokenTickers.includes( + !currency.coingeckoTop500Tickers.includes( sanitizedTokenTicker.split(' ').join(''), ) && - //cross reference with bannedTokenNames - !currency.bannedTokenNames.includes(sanitizedTokenTicker) && - !currency.bannedTokenNames.includes( + //cross reference with coingeckoTop500Names + !currency.coingeckoTop500Names.includes(sanitizedTokenTicker) && + !currency.coingeckoTop500Names.includes( sanitizedTokenTicker.split(' ').join(''), ) && - //cross reference with bannedTokenIds - !currency.bannedTokenIds.includes(sanitizedTokenTicker) && - !currency.bannedTokenIds.includes( + //cross reference with coingeckoTop500Ids + !currency.coingeckoTop500Ids.includes(sanitizedTokenTicker) && + !currency.coingeckoTop500Ids.includes( sanitizedTokenTicker.split(' ').join(''), ) && //cross reference with bannedFiatCurrencies !currency.settingsValidation.fiatCurrency.includes( sanitizedTokenTicker, ) && !currency.settingsValidation.fiatCurrency.includes( sanitizedTokenTicker.split(' ').join(''), - ) + ) && + //cross reference with bannedTickers + !currency.bannedTickers.includes(sanitizedTokenTicker) && + !currency.bannedTickers.includes( + sanitizedTokenTicker.split(' ').join(''), + ) && + //cross reference with bannedNames + !currency.bannedNames.includes(sanitizedTokenTicker) && + !currency.bannedNames.includes(sanitizedTokenTicker.split(' ').join('')) ); }; export const isValidTokenName = tokenName => { return ( typeof tokenName === 'string' && tokenName.length > 0 && tokenName.length < 68 ); }; export const isValidTokenTicker = tokenTicker => { return ( typeof tokenTicker === 'string' && tokenTicker.length > 0 && tokenTicker.length < 13 ); }; export const isValidTokenDecimals = tokenDecimals => { return ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'].includes( tokenDecimals, ); }; export const isValidTokenInitialQty = (tokenInitialQty, tokenDecimals) => { const minimumQty = new BigNumber(1 / 10 ** tokenDecimals); const tokenIntialQtyBig = new BigNumber(tokenInitialQty); return ( tokenIntialQtyBig.gte(minimumQty) && tokenIntialQtyBig.lt(100000000000) && tokenIntialQtyBig.dp() <= tokenDecimals ); }; export const isValidTokenDocumentUrl = tokenDocumentUrl => { const urlPattern = new RegExp( '^(https?:\\/\\/)?' + // protocol '((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|' + // domain name '((\\d{1,3}\\.){3}\\d{1,3}))' + // OR ip (v4) address '(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*' + // port and path '(\\?[;&a-z\\d%_.~+=-]*)?' + // query string '(\\#[-a-z\\d_]*)?$', 'i', ); // fragment locator const urlTestResult = urlPattern.test(tokenDocumentUrl); return ( tokenDocumentUrl === '' || (typeof tokenDocumentUrl === 'string' && tokenDocumentUrl.length >= 0 && tokenDocumentUrl.length < 68 && urlTestResult) ); }; export const isValidCashtabSettings = settings => { try { let isValidSettingParams = true; for (let param in currency.defaultSettings) { if ( !Object.prototype.hasOwnProperty.call(settings, param) || !currency.settingsValidation[param].includes(settings[param]) ) { isValidSettingParams = false; break; } } const isValid = typeof settings === 'object' && isValidSettingParams; return isValid; } catch (err) { return false; } }; export const parseInvalidSettingsForMigration = invalidCashtabSettings => { // create a copy of the invalidCashtabSettings let migratedCashtabSettings = invalidCashtabSettings; // determine if settings are invalid because it is missing a parameter for (let param in currency.defaultSettings) { if ( !Object.prototype.hasOwnProperty.call(invalidCashtabSettings, param) ) { // adds the default setting for only that parameter migratedCashtabSettings[param] = currency.defaultSettings[param]; } } return migratedCashtabSettings; }; export const isValidContactList = contactList => { /* A valid contact list is an array of objects An empty contact list looks like [{}] Although a valid contact list does not contain duplicated addresses, this is not checked here. This is checked for when contacts are added. Duplicate addresses will not break the app if a user somehow sideloads a contact list with everything valid except some addresses are duplicated. */ if (!Array.isArray(contactList)) { return false; } for (let i = 0; i < contactList.length; i += 1) { const contactObj = contactList[i]; // Must have keys 'address' and 'name' if ( typeof contactObj === 'object' && 'address' in contactObj && 'name' in contactObj ) { // Address must be a valid XEC address, name must be a string if ( isValidXecAddress(contactObj.address) && typeof contactObj.name === 'string' ) { continue; } return false; } else { // Check for empty object in an array of length 1, the default blank contactList if ( contactObj && Object.keys(contactObj).length === 0 && Object.getPrototypeOf(contactObj) === Object.prototype && contactList.length === 1 ) { // [{}] is valid, default blank // But a list with random blanks is not valid return true; } return false; } } // If you get here, it's good return true; }; export const isValidCashtabCache = cashtabCache => { /* Object must contain all keys listed in currency.defaultCashtabCache The tokenInfoById object must have keys that are valid token IDs, and at each one an object like: { "tokenTicker": "ST", "tokenName": "ST", "tokenDocumentUrl": "developer.bitcoin.com", "tokenDocumentHash": "", "decimals": 0, "tokenId": "bf24d955f59351e738ecd905966606a6837e478e1982943d724eab10caad82fd" } i.e. an object that contains these keys 'tokenTicker' is a string 'tokenName' is a string 'tokenDocumentUrl' is a string 'tokenDocumentHash' is a string 'decimals' is a number 'tokenId' is a valid tokenId */ // Check that every key in currency.defaultCashtabCache is also in this cashtabCache const cashtabCacheKeys = Object.keys(currency.defaultCashtabCache); for (let i = 0; i < cashtabCacheKeys.length; i += 1) { const thisKey = cashtabCacheKeys[i]; if (thisKey in cashtabCache) { continue; } return false; } // Check that tokenInfoById is expected type and that tokenIds are valid const { tokenInfoById } = cashtabCache; const tokenIds = Object.keys(tokenInfoById); for (let i = 0; i < tokenIds.length; i += 1) { const thisTokenId = tokenIds[i]; if (!isValidTokenId(thisTokenId)) { return false; } const { tokenTicker, tokenName, tokenDocumentUrl, tokenDocumentHash, decimals, tokenId, } = tokenInfoById[thisTokenId]; if ( typeof tokenTicker !== 'string' || typeof tokenName !== 'string' || typeof tokenDocumentUrl !== 'string' || typeof tokenDocumentHash !== 'string' || typeof decimals !== 'number' || !isValidTokenId(tokenId) ) { return false; } } return true; }; export const isValidXecAddress = addr => { /* Returns true for a valid XEC address Valid XEC address: - May or may not have prefix `ecash:` - Checksum must validate for prefix `ecash:` An eToken address is not considered a valid XEC address */ if (!addr) { return false; } let isValidXecAddress; let isPrefixedXecAddress; // Check for possible prefix if (addr.includes(':')) { // Test for 'ecash:' prefix isPrefixedXecAddress = addr.slice(0, 6) === 'ecash:'; // Any address including ':' that doesn't start explicitly with 'ecash:' is invalid if (!isPrefixedXecAddress) { isValidXecAddress = false; return isValidXecAddress; } } else { isPrefixedXecAddress = false; } // If no prefix, assume it is checksummed for an ecash: prefix const testedXecAddr = isPrefixedXecAddress ? addr : `ecash:${addr}`; try { const decoded = cashaddr.decode(testedXecAddr); if (decoded.prefix === 'ecash') { isValidXecAddress = true; } } catch (err) { isValidXecAddress = false; } return isValidXecAddress; }; export const isValidEtokenAddress = addr => { /* Returns true for a valid eToken address Valid eToken address: - May or may not have prefix `etoken:` - Checksum must validate for prefix `etoken:` An XEC address is not considered a valid eToken address */ if (!addr) { return false; } let isValidEtokenAddress; let isPrefixedEtokenAddress; // Check for possible prefix if (addr.includes(':')) { // Test for 'etoken:' prefix isPrefixedEtokenAddress = addr.slice(0, 7) === 'etoken:'; // Any token address including ':' that doesn't start explicitly with 'etoken:' is invalid if (!isPrefixedEtokenAddress) { isValidEtokenAddress = false; return isValidEtokenAddress; } } else { isPrefixedEtokenAddress = false; } // If no prefix, assume it is checksummed for an etoken: prefix const testedEtokenAddr = isPrefixedEtokenAddress ? addr : `etoken:${addr}`; try { const decoded = cashaddr.decode(testedEtokenAddr); if (decoded.prefix === 'etoken') { isValidEtokenAddress = true; } } catch (err) { isValidEtokenAddress = false; } return isValidEtokenAddress; }; export const isValidXecSendAmount = xecSendAmount => { // A valid XEC send amount must be a number higher than the app dust limit return ( xecSendAmount !== null && typeof xecSendAmount !== 'undefined' && !isNaN(parseFloat(xecSendAmount)) && parseFloat(xecSendAmount) >= fromSatoshisToXec(currency.dustSats).toNumber() ); }; export const isValidEtokenBurnAmount = (tokenBurnAmount, maxAmount) => { // A valid eToken burn amount must be between 1 and the wallet's token balance return ( tokenBurnAmount !== null && maxAmount !== null && typeof tokenBurnAmount !== 'undefined' && typeof maxAmount !== 'undefined' && new BigNumber(tokenBurnAmount).gt(0) && new BigNumber(tokenBurnAmount).lte(maxAmount) ); }; // XEC airdrop field validations export const isValidTokenId = tokenId => { // disable no-useless-escape for regex //eslint-disable-next-line const format = /[ `!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?~]/; const specialCharCheck = format.test(tokenId); return ( typeof tokenId === 'string' && tokenId.length === 64 && tokenId.trim() != '' && !specialCharCheck ); }; export const isValidNewWalletNameLength = newWalletName => { return ( typeof newWalletName === 'string' && newWalletName.length > 0 && newWalletName.length <= currency.localStorageMaxCharacters && newWalletName.length !== '' ); }; export const isValidXecAirdrop = xecAirdrop => { return ( typeof xecAirdrop === 'string' && xecAirdrop.length > 0 && xecAirdrop.trim() != '' && new BigNumber(xecAirdrop).gt(0) ); }; export const isValidAirdropOutputsArray = airdropOutputsArray => { if (!airdropOutputsArray) { return false; } let isValid = true; // split by individual rows const addressStringArray = airdropOutputsArray.split('\n'); for (let i = 0; i < addressStringArray.length; i++) { const substring = addressStringArray[i].split(','); let valueString = substring[1]; // if the XEC being sent is less than dust sats or contains extra values per line if ( new BigNumber(valueString).lt( fromSatoshisToXec(currency.dustSats), ) || substring.length !== 2 ) { isValid = false; } } return isValid; }; export const isValidAirdropExclusionArray = airdropExclusionArray => { if (!airdropExclusionArray || airdropExclusionArray.length === 0) { return false; } let isValid = true; // split by comma as the delimiter const addressStringArray = airdropExclusionArray.split(','); // parse and validate each address in array for (let i = 0; i < addressStringArray.length; i++) { if (!isValidXecAddress(addressStringArray[i])) { return false; } } return isValid; };