diff --git a/package.json b/package.json index c4567ffe0f0b..886f798f5ffc 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "yarn": ">=999.0.0", "npm": ">=999.0.0" }, - "version": "2.9.0", + "version": "2.10.0", "private": true, "license": "AGPL-3.0-or-later", "scripts": { diff --git a/packages/dashboard/src/locales/en-US.json b/packages/dashboard/src/locales/en-US.json index 057814034962..38f9884ec87d 100644 --- a/packages/dashboard/src/locales/en-US.json +++ b/packages/dashboard/src/locales/en-US.json @@ -33,7 +33,7 @@ "about_dialog_touch": "Get in touch", "about_dialog_description": "Mask Network is the portal to the new, open internet. Mask allows you to send encrypted posts on social networks. We provide more functions such as sending encrypted lucky drops, purchasing cryptocurrencies, file service, etc.", "setup_page_title": "Welcome to Mask Network", - "setup_page_description": "Encrypt your posts & chats on social networks, allow only your friends to decrypt.", + "setup_page_description": "Encrypt your posts on social media and only your friends on Mask can decrypt them.", "setup_page_create_account_title": "Create an Identity", "setup_page_create_account_subtitle": "Create your digital identity system, explore Web 3.0", "setup_page_create_account_button": "Create", @@ -52,7 +52,7 @@ "create_account_connect_social_media_button": "Create", "create_account_connect_social_media": "Connect to {{type}}", "create_account_persona_title": "Welcome to Mask Network", - "create_account_persona_subtitle": "You can create personas and connect social accounts", + "create_account_persona_subtitle": "Connect to social media accounts with your personas.", "create_account_persona_successfully": "Persona created successfully.", "create_account_connect_social_media_title": "Connect Social Media", "create_account_failed": "Create Account Failed", @@ -197,7 +197,7 @@ "wallets_empty_history_tips": "No transaction history", "wallets_loading_token": "Loading Token", "personas_setup_connect_tips": "Please connect to your {{type}} account.", - "personas_setup_tip": "Please to create/restore persona.", + "personas_setup_tip": "Please create or restore a Mask identity.", "personas_setup_connect": "Connect", "personas_name_maximum_tips": "Maximum length is {{length}} characters long.", "personas_name_existed": "The persona name already exists", diff --git a/packages/dashboard/src/pages/Wallets/components/FungibleTokenTable/index.tsx b/packages/dashboard/src/pages/Wallets/components/FungibleTokenTable/index.tsx index 560feab939cd..7eb50a19d791 100644 --- a/packages/dashboard/src/pages/Wallets/components/FungibleTokenTable/index.tsx +++ b/packages/dashboard/src/pages/Wallets/components/FungibleTokenTable/index.tsx @@ -2,7 +2,6 @@ import { memo, useCallback, useMemo } from 'react' import { useNavigate } from 'react-router-dom' import BigNumber from 'bignumber.js' import { Table, TableBody, TableCell, TableContainer, TableHead, TableRow } from '@mui/material' -import { TrendingCoinType } from '@masknet/web3-providers' import { makeStyles, MaskColorVar } from '@masknet/theme' import { useDashboardI18N } from '../../../../locales' import { EmptyPlaceholder } from '../EmptyPlaceholder' @@ -10,7 +9,15 @@ import { LoadingPlaceholder } from '../../../../components/LoadingPlaceholder' import { FungibleTokenTableRow } from '../FungibleTokenTableRow' import { DashboardRoutes, EMPTY_LIST } from '@masknet/shared-base' import { useRemoteControlledDialog } from '@masknet/shared-base-ui' -import { CurrencyType, formatBalance, FungibleAsset, minus, NetworkPluginID, toZero } from '@masknet/web3-shared-base' +import { + CurrencyType, + formatBalance, + FungibleAsset, + minus, + NetworkPluginID, + TokenType, + toZero, +} from '@masknet/web3-shared-base' import { useCurrentWeb3NetworkPluginID, useFungibleAssets, @@ -94,7 +101,7 @@ export const FungibleTokenTable = memo(({ selectedChainId }) => id: token.id, name: token.name || '', symbol: token.symbol || '', - type: TrendingCoinType.Fungible, + type: TokenType.Fungible, contract_address: token.address, decimals: token.decimals, }, diff --git a/packages/dashboard/src/pages/Wallets/components/HistoryTableRow/index.tsx b/packages/dashboard/src/pages/Wallets/components/HistoryTableRow/index.tsx index 56604e38f0d6..3167b610b9a9 100644 --- a/packages/dashboard/src/pages/Wallets/components/HistoryTableRow/index.tsx +++ b/packages/dashboard/src/pages/Wallets/components/HistoryTableRow/index.tsx @@ -33,7 +33,7 @@ const useStyles = makeStyles()((theme) => ({ }, linkIcon: { // TODO: replace with theme color - fill: theme.palette.mode === 'dark' ? '#F5F5F5' : '#07101B', + color: theme.palette.mode === 'dark' ? '#F5F5F5' : '#07101B', fontSize: 16, marginLeft: 10, }, diff --git a/packages/dashboard/src/pages/Wallets/components/SelectTokenDialog/index.tsx b/packages/dashboard/src/pages/Wallets/components/SelectTokenDialog/index.tsx deleted file mode 100644 index 10528de822ea..000000000000 --- a/packages/dashboard/src/pages/Wallets/components/SelectTokenDialog/index.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import { memo } from 'react' -import { MaskDialog } from '@masknet/theme' -import { useDashboardI18N } from '../../../../locales' -import { FungibleTokenList } from '@masknet/shared' -import { DialogContent } from '@mui/material' -import type { FungibleToken } from '@masknet/web3-shared-base' -import type { ChainId, SchemaType } from '@masknet/web3-shared-evm' - -export interface SelectTokenDialogProps { - open: boolean - onClose: () => void - onSelect?(token: FungibleToken | null): void -} - -// todo use remote dialog for add token list dialog -export const SelectTokenDialog = memo(({ open, onClose, onSelect }) => { - const t = useDashboardI18N() - - return ( - - - - - - ) -}) diff --git a/packages/encryption/src/image-steganography/index.ts b/packages/encryption/src/image-steganography/index.ts index baf1a29d041d..1a17b02459e0 100644 --- a/packages/encryption/src/image-steganography/index.ts +++ b/packages/encryption/src/image-steganography/index.ts @@ -6,7 +6,7 @@ import { omit } from 'lodash-unified' import { getDimension } from './utils' export { GrayscaleAlgorithm } from '@dimensiondev/stego-js/cjs/grayscale.js' -export type ImageTemplateTypes = 'v2' | 'eth' +export type ImageTemplateTypes = 'v2' interface Dimension { width: number @@ -34,7 +34,6 @@ const dimensionPreset: Preset[] = [ { width: 1200, height: 680, - template: 'eth', mask: new URL('./masks/mask-transparent.png', import.meta.url).toString(), options: { cropEdgePixels: true, diff --git a/packages/icons/general/LinkOut.tsx b/packages/icons/general/LinkOut.tsx index 3dfd945cc774..7d42b718ffe5 100644 --- a/packages/icons/general/LinkOut.tsx +++ b/packages/icons/general/LinkOut.tsx @@ -7,7 +7,6 @@ export const LinkOutIcon: typeof SvgIcon = createIcon( , diff --git a/packages/icons/general/PopupLink.tsx b/packages/icons/general/PopupLink.tsx new file mode 100644 index 000000000000..d59676c01546 --- /dev/null +++ b/packages/icons/general/PopupLink.tsx @@ -0,0 +1,15 @@ +import { createIcon } from '../utils' +import type { SvgIcon } from '@mui/material' + +export const PopupLinkIcon: typeof SvgIcon = createIcon( + 'PopupLinkIcon', + + + , + '0 0 12 12', +) diff --git a/packages/icons/general/index.ts b/packages/icons/general/index.ts index 7437955acca7..580a192f706c 100644 --- a/packages/icons/general/index.ts +++ b/packages/icons/general/index.ts @@ -139,3 +139,4 @@ export * from './CheckCircle' export * from './SuccessForSnackBar' export * from './MaskAvatarIcon' export * from './ArrowDownward' +export * from './PopupLink' diff --git a/packages/injected-script/main/sceneChange/twitter.ts b/packages/injected-script/main/sceneChange/twitter.ts index b8497f2ab688..5ff16de8d4c8 100644 --- a/packages/injected-script/main/sceneChange/twitter.ts +++ b/packages/injected-script/main/sceneChange/twitter.ts @@ -15,7 +15,7 @@ export function setupWatcherForTwitter() { const update = () => { const newFirstSlug = getFirstSlug() // reset to void wrong value - if (!firstSlug || apply(includes, TWITTER_RESERVED_SLUGS, [firstSlug])) { + if (!newFirstSlug || apply(includes, TWITTER_RESERVED_SLUGS, [newFirstSlug])) { const event = new no_xray_CustomEvent('scenechange', { detail: { scene: 'unknown' }, }) as WindowEventMap['scenechange'] diff --git a/packages/mask/package.json b/packages/mask/package.json index bf8b312f4f1f..7116a3c103d4 100644 --- a/packages/mask/package.json +++ b/packages/mask/package.json @@ -97,6 +97,7 @@ "jsbi": "3.1.4", "json-stable-stringify": "^1.0.1", "json2csv": "^5.0.6", + "lru-cache": "^7.10.1", "millify": "^4.0.0", "next-tick": "^1.0.0", "opensea-js": "^1.2.7", @@ -143,6 +144,7 @@ "@nice-labs/emit-file-webpack-plugin": "^1.1.2", "@pmmmwh/react-refresh-webpack-plugin": "^0.5.7", "@types/color": "^3.0.3", + "@types/lru-cache": "^7.10.10", "binaryen": "^107.0.0", "copy-webpack-plugin": "^11.0.0", "html-webpack-plugin": "^5.5.0", diff --git a/packages/mask/shared-ui/locales/en-US.json b/packages/mask/shared-ui/locales/en-US.json index 901f2b8d58de..f17770319004 100644 --- a/packages/mask/shared-ui/locales/en-US.json +++ b/packages/mask/shared-ui/locales/en-US.json @@ -1,5 +1,5 @@ { - "promote": "🎭 🎭🎭 Tired of plaintext?Try to send encrypted message to you friends. Install Mask.io to send your first encrypted tweet.", + "promote": "🎭🎭🎭 Tired of plaintext?Try to send encrypted message to you friends. Install Mask.io to send your first encrypted tweet.", "promote_red_packet": "🧧🧧🧧 Try sending Lucky Drop to your friends with tokens or NFTs to share the joy now! Install Mask.io to send your first Lucky Drop.", "promote_ito": "Launch decentralized assets freely and participate in token launch directly on Twitter! Install Mask.io to participate in your first token launch activity.", "promote_ito_short": "Install Mask.io to launch dencentralized assests freely on Twitter!", @@ -210,9 +210,7 @@ "sharing": "Sharing", "transfer": "Transfer", "export": "Export", - "wallet_status_bar_pending": "Pending", "wallet_load_retry": "Failed to load {{symbol}}. Click to retry.", - "wallet_status_bar_pending": "Pending", "wallet_name": "Wallet Name", "wallet_rename": "Rename Wallet", "wallet_loading_nft_contract": "Loading NFT contract...", @@ -252,7 +250,8 @@ "wallet_balance": "Balance", "wallet_balance_eth": "Balance(ETH)", "wallet_new": "New Wallet", - "wallet_status_pending": "Pending{{plural}}", + "wallet_status_pending_one": "{{count}} Pending", + "wallet_status_pending_other": "{{count}} Pendings", "wallet_status_pending_clear_all": "Clear All", "wallet_status_pending_clear": "Clear", "wallet_status_button_change": "Change", @@ -340,7 +339,8 @@ "plugin_wallet_on_create": "Create Wallet", "plugin_wallet_on_connect": "Connect Wallet", "plugin_wallet_wrong_network": "Wrong Network", - "plugin_wallet_pending_transactions": "{{count}} Pending{{plural}}", + "plugin_wallet_pending_transactions_one": "{{count}} Pending", + "plugin_wallet_pending_transactions_other": "{{count}} Pendings", "plugin_wallet_import_wallet": "Import Wallet", "plugin_wallet_select_provider_dialog_title": "Connect Wallet", "plugin_wallet_qr_code_with_wallet_connect": "Scan QR code with a WalletConnect-compatible wallet", @@ -369,13 +369,15 @@ "plugin_wallet_cancel_sign": "Signature canceled.", "plugin_web3_profile_recommend_feature_description": "Choose and showcase your Web3 footprints on Twitter.", "plugin_nft_avatar_recommend_feature_description": "Set your NFT as profile picture with exclusive aura.", - "application_hint": "Socialize and show off your NFTs. People can bid,buy, view your valuable NFTs without leaving Twitter.", + "application_hint": "Socialize and show off your NFTs. People can bid, buy and view your valuable NFTs without leaving Twitter.", "plugin_goPlusSecurity_description": "Provide you with fast, reliable and convenient security services", "plugin_red_packet_create": "Create a Lucky Drop", "plugin_red_packet_claimed": "Claimed", "plugin_red_packet_claim": "Claim", + "plugin_red_packet_claim_notification": "Claim a Lucky Drop", "plugin_red_packet_create_with_token": "Create a Lucky Drop with {{amount}} {{symbol}}", "plugin_nft_red_packet_create": "Create an NFT Lucky Drop", + "plugin_nft_red_packet_claim": "Claim an NFT Lucky Drop", "plugin_red_packet_nft_account_name": "Wallet account", "plugin_gitcoin_select_a_token": "Select a token", "plugin_gitcoin_insufficient_balance": "Insufficient {{symbol}} balance", @@ -422,6 +424,8 @@ "plugin_savings_withdraw": "Withdraw", "plugin_savings_process_deposit": "Processing Deposit", "plugin_savings_process_withdraw": "Processing Withdrawal", + "plugin_findtruman_powered_by": "Powered By", + "plugin_findtruman_find_truman": "FindTruman", "plugin_trader_swap": "Swap", "plugin_trader_wrap": "Wrap", "plugin_trader_swap_from": "You sell", diff --git a/packages/mask/shared-ui/locales/qya-AA.json b/packages/mask/shared-ui/locales/qya-AA.json index 0c61af603c7c..e6cb343c82ed 100644 --- a/packages/mask/shared-ui/locales/qya-AA.json +++ b/packages/mask/shared-ui/locales/qya-AA.json @@ -205,7 +205,6 @@ "sharing": "crwdns4469:0crwdne4469:0", "transfer": "crwdns4471:0crwdne4471:0", "export": "crwdns9305:0crwdne9305:0", - "wallet_status_bar_pending": "crwdns17546:0crwdne17546:0", "wallet_load_retry": "crwdns10135:0{{symbol}}crwdne10135:0", "wallet_name": "crwdns4487:0crwdne4487:0", "wallet_rename": "crwdns4489:0crwdne4489:0", diff --git a/packages/mask/src/components/CompositionDialog/Composition.tsx b/packages/mask/src/components/CompositionDialog/Composition.tsx index 9098d7c87131..1dabce46e0c6 100644 --- a/packages/mask/src/components/CompositionDialog/Composition.tsx +++ b/packages/mask/src/components/CompositionDialog/Composition.tsx @@ -26,6 +26,9 @@ const useStyles = makeStyles()({ hideDialogRoot: { visibility: 'hidden', }, + dialogContent: { + padding: '20px 24px', + }, }) export interface PostDialogProps { type?: 'popup' | 'timeline' @@ -114,7 +117,7 @@ export function Composition({ type = 'timeline', requireClipboardPermission }: P open={open} onClose={onClose} title={t('post_dialog__title')}> - + { let isOpenedVerifyDialog = false let isOpenedFromButton = false -const verifyPersona = (personaIdentifier?: PersonaIdentifier, username?: string) => async () => { +export const verifyPersona = (personaIdentifier?: PersonaIdentifier, username?: string) => async () => { if (!personaIdentifier) return currentSetupGuideStatus[activatedSocialNetworkUI.networkIdentifier].value = stringify({ status: SetupGuideStep.VerifyOnNextID, @@ -34,7 +34,7 @@ const verifyPersona = (personaIdentifier?: PersonaIdentifier, username?: string) export const useNextIDBoundByPlatform = (platform?: NextIDPlatform, userId?: string) => { const res = useAsyncRetry(async () => { if (!platform || !userId) return EMPTY_LIST - return NextIDProof.queryExistedBindingByPlatform(platform, userId) + return NextIDProof.queryAllExistedBindingsByPlatform(platform, userId) }, [platform, userId]) useEffect(() => MaskMessages.events.ownProofChanged.on(res.retry), [res.retry]) return res @@ -59,7 +59,7 @@ export function useSetupGuideStatusState() { }, [lastState_]) } -export function useNextIDConnectStatus() { +export function useNextIDConnectStatus(disableInitialVerify = false) { const ui = activatedSocialNetworkUI const [enableNextID] = useState(ui.configuration.nextIDConfig?.enable) const personaConnectStatus = usePersonaConnectStatus() @@ -110,13 +110,20 @@ export function useNextIDConnectStatus() { ) if (isBound) return NextIDVerificationStatus.Verified - if (isOpenedFromButton) { + if (isOpenedFromButton && !disableInitialVerify) { verifyPersona(personaConnectStatus.currentConnectedPersona?.identifier)() } isOpenedVerifyDialog = true isOpenedFromButton = false return NextIDVerificationStatus.WaitingVerify - }, [username, enableNextID, isOpenedVerifyDialog, personaConnectStatus, currentPersonaIdentifier.value]) + }, [ + username, + enableNextID, + isOpenedVerifyDialog, + personaConnectStatus, + currentPersonaIdentifier.value, + disableInitialVerify, + ]) return { isVerified: VerificationStatus === NextIDVerificationStatus.Verified, diff --git a/packages/mask/src/components/InjectedComponents/DisabledPluginSuggestion.tsx b/packages/mask/src/components/InjectedComponents/DisabledPluginSuggestion.tsx index 68a9c33a14b4..01f7a6f0c4c8 100644 --- a/packages/mask/src/components/InjectedComponents/DisabledPluginSuggestion.tsx +++ b/packages/mask/src/components/InjectedComponents/DisabledPluginSuggestion.tsx @@ -80,7 +80,7 @@ export function PossiblePluginSuggestionUI(props: { plugins: Plugin.DeferredDefi
e.currentTarget.getElementsByTagName('input')[0].focus()} sx={{ width: '100%' }} placeholder={t('plugin_avatar_input_token_address')} onChange={(e) => onAddressChange(e.target.value)} @@ -154,6 +157,7 @@ export function AddNFT(props: AddNFTProps) {
e.currentTarget.getElementsByTagName('input')[0].focus()} sx={{ width: '100%' }} placeholder={t('plugin_avatar_input_token_id')} onChange={(e) => onTokenIdChange(e.target.value)} diff --git a/packages/mask/src/plugins/Avatar/hooks/useNFT.ts b/packages/mask/src/plugins/Avatar/hooks/useNFT.ts index 6df8705b0501..d5e4a1d60b94 100644 --- a/packages/mask/src/plugins/Avatar/hooks/useNFT.ts +++ b/packages/mask/src/plugins/Avatar/hooks/useNFT.ts @@ -22,9 +22,9 @@ export function useNFT( chainId, }) return { - amount: asset?.price?.[CurrencyType.USD] ?? '0', + amount: asset?.priceInToken?.amount ?? asset?.price?.[CurrencyType.USD] ?? '0', name: asset?.contract?.name ?? '', - symbol: asset?.paymentTokens?.[0].symbol ?? 'ETH', + symbol: asset?.priceInToken?.token.symbol ?? asset?.paymentTokens?.[0].symbol ?? 'ETH', image: asset?.metadata?.imageURL ?? '', owner: asset?.owner?.address ?? asset?.ownerId ?? '', slug: asset?.collection?.slug ?? '', diff --git a/packages/mask/src/plugins/Avatar/hooks/usePersonaNFTAvatar.ts b/packages/mask/src/plugins/Avatar/hooks/usePersonaNFTAvatar.ts index d9fc5c6ee05d..e6f10cf6cecb 100644 --- a/packages/mask/src/plugins/Avatar/hooks/usePersonaNFTAvatar.ts +++ b/packages/mask/src/plugins/Avatar/hooks/usePersonaNFTAvatar.ts @@ -3,23 +3,49 @@ import { NetworkPluginID } from '@masknet/web3-shared-base' import { useAsyncRetry } from 'react-use' import { activatedSocialNetworkUI } from '../../../social-network' import type { RSS3_KEY_SNS } from '../constants' +import type { AvatarMetaDB, NextIDAvatarMeta } from '../types' import { getNFTAvatarByUserId } from '../utils' import { useGetNFTAvatar } from './useGetNFTAvatar' +import LRU from 'lru-cache' + +const cache = new LRU>({ + max: 500, + ttl: 60 * 1000, +}) + +type GetNFTAvatar = ( + userId?: string, + network?: EnhanceableSite, + snsKey?: RSS3_KEY_SNS, +) => Promise export function usePersonaNFTAvatar(userId: string, avatarId: string, snsKey: RSS3_KEY_SNS) { const [, getNFTAvatar] = useGetNFTAvatar() + return useAsyncRetry(async () => { - const avatarMetaFromPersona = await getNFTAvatarByUserId(userId, avatarId) - if (avatarMetaFromPersona) return avatarMetaFromPersona - const avatarMeta = await getNFTAvatar( - userId, - activatedSocialNetworkUI.networkIdentifier as EnhanceableSite, - snsKey, - ) - if (!avatarMeta) return - if (avatarMeta.pluginId === NetworkPluginID.PLUGIN_SOLANA) { - return { imageUrl: '', nickname: '', ...avatarMeta, address: avatarMeta.tokenId } - } - return { imageUrl: '', nickname: '', ...avatarMeta } - }, [userId, getNFTAvatar, avatarId, snsKey]) + if (!userId) return + const key = `${userId}-${activatedSocialNetworkUI.networkIdentifier}` + if (!cache.has(key)) cache.set(key, getNFTAvatarForCache(userId, snsKey, avatarId, getNFTAvatar)) + const v = cache.get(key) + return v + }, [ + userId, + getNFTAvatar, + avatarId, + activatedSocialNetworkUI.networkIdentifier, + snsKey, + cache, + getNFTAvatarForCache, + ]) +} + +async function getNFTAvatarForCache(userId: string, snsKey: RSS3_KEY_SNS, avatarId: string, fn: GetNFTAvatar) { + const avatarMetaFromPersona = await getNFTAvatarByUserId(userId, avatarId) + if (avatarMetaFromPersona) return avatarMetaFromPersona + const avatarMeta = await fn(userId, activatedSocialNetworkUI.networkIdentifier as EnhanceableSite, snsKey) + if (!avatarMeta) return + if (avatarMeta.pluginId === NetworkPluginID.PLUGIN_SOLANA) { + return { imageUrl: '', nickname: '', ...avatarMeta, address: avatarMeta.tokenId } + } + return { imageUrl: '', nickname: '', ...avatarMeta } } diff --git a/packages/mask/src/plugins/Avatar/locales/en-US.json b/packages/mask/src/plugins/Avatar/locales/en-US.json index 52e185d34e2a..ea1a90515d65 100644 --- a/packages/mask/src/plugins/Avatar/locales/en-US.json +++ b/packages/mask/src/plugins/Avatar/locales/en-US.json @@ -22,7 +22,7 @@ "collectible_no_collectible": "No any collectible is available to preview.", "no_collectible_found": "No collectible found.", "retry": "Retry", - "application_hint": "Socialize and show off your NFTs. People can bid,buy, view your valuable NFTs without leaving Twitter.", + "application_hint": "Socialize and show off your NFTs. People can bid, buy and view your valuable NFTs without leaving Twitter.", "provider_by": "Provided by", "downloading_image": "Downloading image...", "saving": "Saving...", diff --git a/packages/mask/src/plugins/Collectible/SNSAdaptor/HistoryTab/index.tsx b/packages/mask/src/plugins/Collectible/SNSAdaptor/HistoryTab/index.tsx index 3833fa7786fd..18e99a08d994 100644 --- a/packages/mask/src/plugins/Collectible/SNSAdaptor/HistoryTab/index.tsx +++ b/packages/mask/src/plugins/Collectible/SNSAdaptor/HistoryTab/index.tsx @@ -58,7 +58,8 @@ export function HistoryTab(props: HistoryTabProps) {
) - if (events.length) + + if (!asset.value || asset.error || !events.length) return ( diff --git a/packages/mask/src/plugins/Collectible/hooks/useCollectibleState.ts b/packages/mask/src/plugins/Collectible/hooks/useCollectibleState.ts index d79262cac0f2..1d241a2dc17e 100644 --- a/packages/mask/src/plugins/Collectible/hooks/useCollectibleState.ts +++ b/packages/mask/src/plugins/Collectible/hooks/useCollectibleState.ts @@ -3,6 +3,7 @@ import { createContainer } from 'unstated-next' import { CollectibleTab, CollectibleToken } from '../types' import { useNonFungibleAsset } from '@masknet/plugin-infra/web3' import { NetworkPluginID, SourceType } from '@masknet/web3-shared-base' +import { ChainId } from '@masknet/web3-shared-evm' function useCollectibleState(token?: CollectibleToken) { const [tabIndex, setTabIndex] = useState(CollectibleTab.ARTICLE) @@ -10,6 +11,7 @@ function useCollectibleState(token?: CollectibleToken) { const asset = useNonFungibleAsset(NetworkPluginID.PLUGIN_EVM, token?.contractAddress ?? '', token?.tokenId ?? '', { sourceType: provider, + chainId: ChainId.Mainnet, }) return { diff --git a/packages/mask/src/plugins/CryptoartAI/SNSAdaptor/ActionBar.tsx b/packages/mask/src/plugins/CryptoartAI/SNSAdaptor/ActionBar.tsx index cc898a98a4f3..6051dbc657e2 100644 --- a/packages/mask/src/plugins/CryptoartAI/SNSAdaptor/ActionBar.tsx +++ b/packages/mask/src/plugins/CryptoartAI/SNSAdaptor/ActionBar.tsx @@ -59,6 +59,7 @@ export function ActionBar(props: ActionBarProps) { assetSource?.trade?.is_auction ? ( { onOpenOfferDialog() }}> @@ -70,7 +71,7 @@ export function ActionBar(props: ActionBarProps) { !assetSource?.is24Auction && assetSource?.priceInEth < 100000 && assetSource?.trade?.isCanBuy ? ( - + {t('plugin_collectible_buy_now')} ) : null} diff --git a/packages/mask/src/plugins/FindTruman/SNSAdaptor/FindTrumanDialog.tsx b/packages/mask/src/plugins/FindTruman/SNSAdaptor/FindTrumanDialog.tsx index 6fe99aa90272..6f2d69701e93 100644 --- a/packages/mask/src/plugins/FindTruman/SNSAdaptor/FindTrumanDialog.tsx +++ b/packages/mask/src/plugins/FindTruman/SNSAdaptor/FindTrumanDialog.tsx @@ -10,10 +10,9 @@ import { FindTrumanContext } from '../context' import { useAccount, useChainId } from '@masknet/plugin-infra/web3' import { useConst } from './hooks/useConst' import IntroductionPanel from './IntroductionPanel' -import { PluginWalletStatusBar } from '../../../utils' +import { PluginWalletStatusBar, useI18N } from '../../../utils' import { ChainBoundary } from '../../../web3/UI/ChainBoundary' import { NetworkPluginID } from '@masknet/web3-shared-base' -import { useI18N } from '../locales' const useStyles = makeStyles()((theme, props) => ({ wrapper: { @@ -77,7 +76,7 @@ interface FindTrumanDialogProps { } export function FindTrumanDialog(props: FindTrumanDialogProps) { - const i18N = useI18N() + const { t: i18N } = useI18N() const { open, onClose } = props const { classes } = useStyles() const account = useAccount() @@ -126,10 +125,10 @@ export function FindTrumanDialog(props: FindTrumanDialogProps) { color="textSecondary" fontSize={14} fontWeight={700}> - {i18N.powered_by()} + {i18N('plugin_findtruman_powered_by')} - FindTruman + {i18N('plugin_findtruman_find_truman')} - isSameAddress(investable.address, props.address) && - investable.chainId === currentChainId && - investable.category === props.category, + isSameAddress(investable.address, props.address) && investable.category === props.category, ) if (!investable) return ( - <> - - {t('plugin_furucombo_pool_not_found')} - - - - - - + + {t('plugin_furucombo_pool_not_found')} + ) return ( @@ -123,9 +110,6 @@ export function FurucomboView(props: PoolViewProps) { - - - ) } diff --git a/packages/mask/src/plugins/Game/SNSAdaptor/index.tsx b/packages/mask/src/plugins/Game/SNSAdaptor/index.tsx index 2019b5b5dbd8..0cf4b93930ab 100644 --- a/packages/mask/src/plugins/Game/SNSAdaptor/index.tsx +++ b/packages/mask/src/plugins/Game/SNSAdaptor/index.tsx @@ -22,10 +22,18 @@ const sns: Plugin.SNSAdaptor.Definition = { const name = return { ApplicationEntryID: base.ID, - RenderEntryComponent({ disabled }) { + RenderEntryComponent({ disabled, ...props }) { const { openDialog } = useRemoteControlledDialog(PluginGameMessages.events.gameDialogUpdated) - return + return ( + props.onClick?.(openDialog) : openDialog} + /> + ) }, appBoardSortingDefaultPriority: 11, marketListSortingPriority: 12, diff --git a/packages/mask/src/plugins/MaskBox/SNSAdaptor/components/DetailsTab.tsx b/packages/mask/src/plugins/MaskBox/SNSAdaptor/components/DetailsTab.tsx index 2424d5bbd90c..7ce607cb793f 100644 --- a/packages/mask/src/plugins/MaskBox/SNSAdaptor/components/DetailsTab.tsx +++ b/packages/mask/src/plugins/MaskBox/SNSAdaptor/components/DetailsTab.tsx @@ -26,11 +26,13 @@ const useStyles = makeStyles()((theme) => ({ fontWeight: 500, lineHeight: '24px', marginBottom: theme.spacing(4), + color: theme.palette.maskColor.dark, }, content: { fontSize: 14, lineHeight: '24px', whiteSpace: 'pre-line', + color: theme.palette.maskColor.dark, }, })) diff --git a/packages/mask/src/plugins/NextID/components/NextIdPage.tsx b/packages/mask/src/plugins/NextID/components/NextIdPage.tsx index 20ae7994a13f..8714ed63374d 100644 --- a/packages/mask/src/plugins/NextID/components/NextIdPage.tsx +++ b/packages/mask/src/plugins/NextID/components/NextIdPage.tsx @@ -94,20 +94,24 @@ const useStyles = makeStyles()((theme) => ({ marginTop: 2, }, item1: { - color: theme.palette.maskColor.second, + color: '#767f8d', fontSize: '14', fontWeight: 400, }, item2: { - color: theme.palette.maskColor.main, + color: '#07101B', fontSize: '14', fontWeight: 500, marginLeft: '2px', }, button: { borderRadius: '99px', - backgroundColor: theme.palette.maskColor.dark, + backgroundColor: '#07101b', color: '#fff', + ':hover': { + color: 'fff', + backgroundColor: '#07101b', + }, }, })) @@ -131,7 +135,11 @@ export function NextIdPage({ persona }: NextIdPageProps) { const personaActionButton = useMemo(() => { if (!personaConnectStatus.action) return null const button = personaConnectStatus.hasPersona ? t.connect_persona() : t.create_persona() - return + return ( + + ) }, [personaConnectStatus, t]) const { value: currentPersona, loading: loadingPersona } = useAsyncRetry(async () => { @@ -221,21 +229,6 @@ export function NextIdPage({ persona }: NextIdPageProps) { ) } - if (!isAccountVerified && isOwn) { - return ( - - - {t.verify_Twitter_ID_intro()} - {t.verify_Twitter_ID()} - - - - - - - ) - } - return ( <> diff --git a/packages/mask/src/plugins/Pets/SNSAdaptor/PetDialog.tsx b/packages/mask/src/plugins/Pets/SNSAdaptor/PetDialog.tsx index 77ca0256bbc0..bad57c29ff2c 100644 --- a/packages/mask/src/plugins/Pets/SNSAdaptor/PetDialog.tsx +++ b/packages/mask/src/plugins/Pets/SNSAdaptor/PetDialog.tsx @@ -42,7 +42,7 @@ export function PetDialog() { onClose={handleClose} title={step === PetFriendNFTStep.SetFriendNFT ? t.pets_dialog_title() : t.pets_dialog_title_share()} titleBarIconStyle="back"> - + {step === PetFriendNFTStep.SetFriendNFT ? ( ) : ( diff --git a/packages/mask/src/plugins/Pets/SNSAdaptor/PetSetDialog.tsx b/packages/mask/src/plugins/Pets/SNSAdaptor/PetSetDialog.tsx index 58a140552f9d..3e9a81a7f8b1 100644 --- a/packages/mask/src/plugins/Pets/SNSAdaptor/PetSetDialog.tsx +++ b/packages/mask/src/plugins/Pets/SNSAdaptor/PetSetDialog.tsx @@ -115,6 +115,10 @@ const useStyles = makeStyles()((theme) => ({ logo: { height: 21, }, + RSS3Icon: { + fontSize: 24, + fill: theme.palette.mode === 'light' ? '#000' : '#fff', + }, })) interface PetSetDialogProps { @@ -373,7 +377,7 @@ export function PetSetDialog({ configNFTs, onClose }: PetSetDialogProps) { RSS3 - + @@ -385,11 +389,6 @@ export function PetSetDialog({ configNFTs, onClose }: PetSetDialogProps) { noSwitchNetworkTip ActionButtonPromiseProps={{ fullWidth: true, - sx: { - height: 40, - padding: 0, - margin: 0, - }, }}> ({ root: { margin: theme.spacing(0, 2.5), + flex: 1, + display: 'flex', + flexDirection: 'column', + justifyContent: 'space-between', }, shareNotice: { color: theme.palette.maskColor.main, diff --git a/packages/mask/src/plugins/Pets/assets/rss3.tsx b/packages/mask/src/plugins/Pets/assets/rss3.tsx index e06bb99fef38..7778f2d9a0a7 100644 --- a/packages/mask/src/plugins/Pets/assets/rss3.tsx +++ b/packages/mask/src/plugins/Pets/assets/rss3.tsx @@ -2,22 +2,10 @@ import { createIcon } from '@masknet/icons' export const RSS3Icon = createIcon( 'RSS3Icon', - - - - + + + + , diff --git a/packages/mask/src/plugins/RedPacket/SNSAdaptor/RedPacketConfirmDialog.tsx b/packages/mask/src/plugins/RedPacket/SNSAdaptor/RedPacketConfirmDialog.tsx index e7feaca80ae2..b0bfe5d634e1 100644 --- a/packages/mask/src/plugins/RedPacket/SNSAdaptor/RedPacketConfirmDialog.tsx +++ b/packages/mask/src/plugins/RedPacket/SNSAdaptor/RedPacketConfirmDialog.tsx @@ -145,6 +145,10 @@ export function RedPacketConfirmDialog(props: ConfirmRedPacketFormProps) { token_address: string total: string } + + // the events log is not available + if (!events?.CreationSuccess.returnValues.id) return + payload.current.sender = { address: account, name: settings.name, diff --git a/packages/mask/src/plugins/RedPacket/SNSAdaptor/RedPacketCreateNew.tsx b/packages/mask/src/plugins/RedPacket/SNSAdaptor/RedPacketCreateNew.tsx deleted file mode 100644 index b2bfa7610b7d..000000000000 --- a/packages/mask/src/plugins/RedPacket/SNSAdaptor/RedPacketCreateNew.tsx +++ /dev/null @@ -1,90 +0,0 @@ -import { makeStyles } from '@masknet/theme' -import { ChainId } from '@masknet/web3-shared-evm' -import { NetworkPluginID } from '@masknet/web3-shared-base' -import { useChainId } from '@masknet/plugin-infra/web3' -import { RedPacketFormProps, RedPacketERC20Form } from './RedPacketERC20Form' -import { RedPacketERC721Form } from './RedPacketERC721Form' -import AbstractTab, { AbstractTabProps } from '../../../components/shared/AbstractTab' -import { useI18N } from '../locales' -import { activatedSocialNetworkUI } from '../../../social-network' -import { IconURLs } from './IconURL' -import { EnhanceableSite } from '@masknet/shared-base' - -interface StyleProps { - snsId: string -} - -const useStyles = makeStyles()((theme, { snsId }) => ({ - tab: { - height: 36, - minHeight: 36, - fontWeight: 300, - }, - tabs: { - height: 36, - minHeight: 36, - margin: `0 ${snsId === EnhanceableSite.Minds ? '12px' : 'auto'}`, - borderRadius: 4, - backgroundColor: theme.palette.background.default, - '& .Mui-selected': { - color: theme.palette.primary.contrastText, - backgroundColor: theme.palette.primary.main, - }, - }, - indicator: { - display: 'none', - }, - tabPanel: { - marginTop: theme.spacing(3), - }, - img: { - width: 20, - marginRight: 4, - }, - labelWrapper: { - display: 'flex', - }, - tabWrapper: { - padding: theme.spacing(0, 2, 2, 2), - }, -})) - -export function RedPacketCreateNew(props: RedPacketFormProps & { state: readonly [number, (next: number) => void] }) { - const { origin, onNext, onChange, onClose, state, setERC721DialogHeight } = props - const t = useI18N() - const { classes } = useStyles({ snsId: activatedSocialNetworkUI.networkIdentifier }) - const chainId = useChainId(NetworkPluginID.PLUGIN_EVM) - - const tabProps: AbstractTabProps = { - tabs: [ - { - label: ( -
- - {t.erc20_tab_title()} -
- ), - children: , - sx: { p: 0 }, - }, - { - label: ( -
- - {t.erc721_tab_title()} -
- ), - children: , - sx: { p: 0 }, - disabled: ![ChainId.Mainnet, ChainId.Matic, ChainId.BSC, ChainId.Conflux].includes(chainId), - }, - ], - state, - classes, - } - return ( -
- -
- ) -} diff --git a/packages/mask/src/plugins/RedPacket/SNSAdaptor/RedPacketDialog.tsx b/packages/mask/src/plugins/RedPacket/SNSAdaptor/RedPacketDialog.tsx index 3d77b47735ff..40c2e528fafc 100644 --- a/packages/mask/src/plugins/RedPacket/SNSAdaptor/RedPacketDialog.tsx +++ b/packages/mask/src/plugins/RedPacket/SNSAdaptor/RedPacketDialog.tsx @@ -33,6 +33,7 @@ const useStyles = makeStyles()((theme) => ({ borderBottom: `1px solid ${theme.palette.divider}`, }, dialogContent: { + minHeight: 305, padding: 0, '::-webkit-scrollbar': { display: 'none', @@ -40,6 +41,12 @@ const useStyles = makeStyles()((theme) => ({ overflowX: 'hidden', }, + nftDialogContent: { + height: 305, + }, + nftDialogContentLoaded: { + height: 620, + }, tabPaper: { position: 'sticky', top: 0, @@ -79,9 +86,10 @@ interface RedPacketDialogProps extends withClasses { export default function RedPacketDialog(props: RedPacketDialogProps) { const t = useI18N() - const { classes } = useStyles() + const { cx, classes } = useStyles() const { attachMetadata, dropMetadata } = useCompositionContext() const state = useState(DialogTabs.create) + const [isNFTRedPacketLoaded, setIsNFTRedPacketLoaded] = useState(false) const connection = useWeb3Connection(NetworkPluginID.PLUGIN_EVM) const chainId = useChainId(NetworkPluginID.PLUGIN_EVM) const account = useAccount(NetworkPluginID.PLUGIN_EVM) @@ -180,7 +188,15 @@ export default function RedPacketDialog(props: RedPacketDialogProps) { onClose={() => (showHistory ? setShowHistory(false) : onBack())} isOnBack={showHistory || step !== CreateRedPacketPageStep.NewRedPacketPage} disableTitleBorder> - + {step === CreateRedPacketPageStep.NewRedPacketPage ? ( !showHistory ? ( <> @@ -193,7 +209,10 @@ export default function RedPacketDialog(props: RedPacketDialogProps) { /> - + ) : ( diff --git a/packages/mask/src/plugins/RedPacket/SNSAdaptor/RedPacketERC20Form.tsx b/packages/mask/src/plugins/RedPacket/SNSAdaptor/RedPacketERC20Form.tsx index a0d597439df8..c908b0ef8e58 100644 --- a/packages/mask/src/plugins/RedPacket/SNSAdaptor/RedPacketERC20Form.tsx +++ b/packages/mask/src/plugins/RedPacket/SNSAdaptor/RedPacketERC20Form.tsx @@ -9,7 +9,7 @@ import { formatBalance, } from '@masknet/web3-shared-base' import { ChainId, SchemaType, useRedPacketConstants } from '@masknet/web3-shared-evm' -import { FormControl, InputLabel, MenuItem, Select, TextField } from '@mui/material' +import { FormControl, InputLabel, MenuItem, Select, TextField, Box } from '@mui/material' import BigNumber from 'bignumber.js' import { omit } from 'lodash-unified' import { ChangeEvent, useCallback, useEffect, useMemo, useRef, useState } from 'react' @@ -287,30 +287,32 @@ export function RedPacketERC20Form(props: RedPacketFormProps) { value={message} /> - - - - - - {validationMessage || t.next()} - - - - - + + + + + + + {validationMessage || t.next()} + + + + + + ) } diff --git a/packages/mask/src/plugins/RedPacket/SNSAdaptor/RedPacketERC721Form.tsx b/packages/mask/src/plugins/RedPacket/SNSAdaptor/RedPacketERC721Form.tsx index e1fab8948164..a9d13f269519 100644 --- a/packages/mask/src/plugins/RedPacket/SNSAdaptor/RedPacketERC721Form.tsx +++ b/packages/mask/src/plugins/RedPacket/SNSAdaptor/RedPacketERC721Form.tsx @@ -214,11 +214,11 @@ const useStyles = makeStyles()((theme) => { }) interface RedPacketERC721FormProps { onClose: () => void - setERC721DialogHeight?: (height: number) => void + setIsNFTRedPacketLoaded?: (x: boolean) => void } export function RedPacketERC721Form(props: RedPacketERC721FormProps) { const t = useI18N() - const { onClose, setERC721DialogHeight } = props + const { onClose, setIsNFTRedPacketLoaded } = props const { classes } = useStyles() const [open, setOpen] = useState(false) const [balance, setBalance] = useState(0) @@ -287,7 +287,7 @@ export function RedPacketERC721Form(props: RedPacketERC721FormProps) { return '' }, [tokenDetailedList.length, balance, t]) - setERC721DialogHeight?.(balance > 0 ? 690 : 350) + setIsNFTRedPacketLoaded?.(balance > 0) return ( <> diff --git a/packages/mask/src/plugins/RedPacket/SNSAdaptor/RedPacketInHistoryList.tsx b/packages/mask/src/plugins/RedPacket/SNSAdaptor/RedPacketInHistoryList.tsx index 6a8665b28fa0..d99cc5e477e4 100644 --- a/packages/mask/src/plugins/RedPacket/SNSAdaptor/RedPacketInHistoryList.tsx +++ b/packages/mask/src/plugins/RedPacket/SNSAdaptor/RedPacketInHistoryList.tsx @@ -12,7 +12,6 @@ import REDPACKET_ABI from '@masknet/web3-contracts/abis/HappyRedPacketV4.json' import intervalToDuration from 'date-fns/intervalToDuration' import nextDay from 'date-fns/nextDay' import ActionButton from '../../../extension/options-page/DashboardComponents/ActionButton' -import { useI18N as useBaseI18N } from '../../../utils' import { Translate, useI18N } from '../locales' import { dateTimeFormat } from '../../ITO/assets/formatDate' import { StyledLinearProgress } from '../../ITO/SNSAdaptor/StyledLinearProgress' @@ -181,7 +180,6 @@ export interface RedPacketInHistoryListProps { } export function RedPacketInHistoryList(props: RedPacketInHistoryListProps) { const { history, onSelect } = props - const i18n = useBaseI18N() const t = useI18N() const { classes } = useStyles() const account = useAccount(NetworkPluginID.PLUGIN_EVM) @@ -216,11 +214,12 @@ export function RedPacketInHistoryList(props: RedPacketInHistoryListProps) { const rpid = receipt?.rpid ?? '' const creation_time = receipt?.creation_time ?? 0 + history.rpid = rpid const { value: availability, computed: { canRefund, canSend, listOfStatus, isPasswordValid }, retry: revalidateAvailability, - } = useAvailabilityComputed(account, { ...history, rpid, creation_time }) + } = useAvailabilityComputed(account, { ...history, creation_time }) const claimerNumber = availability ? Number(availability.claimed) : 0 const total_remaining = availability?.balance diff --git a/packages/mask/src/plugins/RedPacket/SNSAdaptor/RedPacketNft.tsx b/packages/mask/src/plugins/RedPacket/SNSAdaptor/RedPacketNft.tsx index f1c3695a0283..30e9e5bfee45 100644 --- a/packages/mask/src/plugins/RedPacket/SNSAdaptor/RedPacketNft.tsx +++ b/packages/mask/src/plugins/RedPacket/SNSAdaptor/RedPacketNft.tsx @@ -417,6 +417,7 @@ export function RedPacketNft({ payload }: RedPacketNftProps) { - diff --git a/packages/mask/src/plugins/Savings/SNSAdaptor/SavingsForm.tsx b/packages/mask/src/plugins/Savings/SNSAdaptor/SavingsForm.tsx index 1b5da7267f83..555c8abe7ed7 100644 --- a/packages/mask/src/plugins/Savings/SNSAdaptor/SavingsForm.tsx +++ b/packages/mask/src/plugins/Savings/SNSAdaptor/SavingsForm.tsx @@ -1,8 +1,7 @@ -import { useState, useCallback, useMemo } from 'react' +import { useState, useMemo } from 'react' import { useAsync, useAsyncFn } from 'react-use' import type { AbiItem } from 'web3-utils' import BigNumber from 'bignumber.js' -import { unreachable } from '@dimensiondev/kit' import { isLessThan, rightShift, @@ -38,7 +37,6 @@ import { PluginWalletStatusBar, useI18N } from '../../../utils' import { WalletConnectedBoundary } from '../../../web3/UI/WalletConnectedBoundary' import { ChainBoundary } from '../../../web3/UI/ChainBoundary' import { PluginTraderMessages } from '../../Trader/messages' -import type { Coin } from '../../Trader/types' import { ProtocolType, SavingsProtocol, TabType } from '../types' import { useStyles } from './SavingsFormStyles' import { EthereumERC20TokenApprovedBoundary } from '../../../web3/UI/EthereumERC20TokenApprovedBoundary' @@ -76,22 +74,6 @@ export function SavingsFormDialog({ chainId, protocol, tab, onClose }: SavingsFo const { setDialog: openSwapDialog } = useRemoteControlledDialog(PluginTraderMessages.swapDialogUpdated) - const onConvertClick = useCallback(() => { - const token = protocol.stakeToken - openSwapDialog({ - open: true, - traderProps: { - defaultInputCoin: { - id: token.address, - name: token.name ?? '', - symbol: token.symbol ?? '', - contract_address: token.address, - decimals: token.decimals, - } as Coin, - }, - }) - }, [protocol, openSwapDialog]) - // #region form variables const { value: inputTokenBalance } = useFungibleTokenBalance( NetworkPluginID.PLUGIN_EVM, @@ -182,38 +164,19 @@ export function SavingsFormDialog({ chainId, protocol, tab, onClose }: SavingsFo }) const [, executor] = useAsyncFn(async () => { if (!web3) return - switch (tab) { - case TabType.Deposit: - const hash = await protocol.deposit(account, chainId, web3, tokenAmount) - if (typeof hash !== 'string') { - throw new Error('Failed to deposit token.') - } else { - await protocol.updateBalance(chainId, web3, account) - } - openShareTxDialog({ - hash, - onShare() { - activatedSocialNetworkUI.utils.share?.(shareText) - }, - }) - break - case TabType.Withdraw: - switch (protocol.type) { - case ProtocolType.Lido: - onClose?.() - onConvertClick() - return - default: - if (!(await protocol.withdraw(account, chainId, web3, tokenAmount))) { - throw new Error('Failed to withdraw token.') - } else { - await protocol.updateBalance(chainId, web3, account) - } - return - } - default: - unreachable(tab) + const methodName = tab === TabType.Deposit ? 'deposit' : 'withdraw' + const hash = await protocol[methodName](account, chainId, web3, tokenAmount) + if (typeof hash !== 'string') { + throw new Error('Failed to deposit token.') + } else { + await protocol.updateBalance(chainId, web3, account) } + openShareTxDialog({ + hash, + onShare() { + activatedSocialNetworkUI.utils.share?.(shareText) + }, + }) }, [tab, protocol, account, chainId, web3, tokenAmount, openShareTxDialog]) const buttonDom = useMemo(() => { diff --git a/packages/mask/src/plugins/Savings/SNSAdaptor/SavingsTable.tsx b/packages/mask/src/plugins/Savings/SNSAdaptor/SavingsTable.tsx index f94dbd776e62..3083c30d30db 100644 --- a/packages/mask/src/plugins/Savings/SNSAdaptor/SavingsTable.tsx +++ b/packages/mask/src/plugins/Savings/SNSAdaptor/SavingsTable.tsx @@ -3,12 +3,16 @@ import { makeStyles } from '@masknet/theme' import { Box, Button, Grid, Typography } from '@mui/material' import { FormattedBalance, TokenIcon } from '@masknet/shared' import { CircleLoadingIcon, DirectIcon } from '@masknet/icons' -import { isZero, rightShift, formatBalance, isSameAddress, NetworkPluginID } from '@masknet/web3-shared-base' +import { isZero, rightShift, formatBalance, isSameAddress, NetworkPluginID, TokenType } from '@masknet/web3-shared-base' import type { ChainId, Web3 } from '@masknet/web3-shared-evm' import { ProviderIconURLs } from './IconURL' import { useI18N } from '../../../utils' -import { SavingsProtocol, TabType } from '../types' +import { ProtocolType, SavingsProtocol, TabType } from '../types' import { useAccount, useWeb3, useFungibleAssets } from '@masknet/plugin-infra/web3' +import { useRemoteControlledDialog } from '@masknet/shared-base-ui' +import { useCallback } from 'react' +import { PluginTraderMessages } from '../../Trader/messages' +import { LDO_PAIRS } from '../constants' const useStyles = makeStyles()((theme, props) => ({ containerWrap: { @@ -105,7 +109,7 @@ export function SavingsTable({ chainId, tab, protocols, setTab, setSelectedProto const account = useAccount(NetworkPluginID.PLUGIN_EVM) const { value: assets, loading: getAssetsLoading } = useFungibleAssets(NetworkPluginID.PLUGIN_EVM) - + const { setDialog: openSwapDialog } = useRemoteControlledDialog(PluginTraderMessages.swapDialogUpdated) // Only fetch protocol APR and Balance on chainId change const { loading } = useAsync(async () => { await Promise.all( @@ -115,6 +119,32 @@ export function SavingsTable({ chainId, tab, protocols, setTab, setSelectedProto }), ) }, [chainId, web3, account, protocols]) + + const onConvertClick = useCallback(() => { + const ETH = LDO_PAIRS[0][0] + const sETH = LDO_PAIRS[0][1] + openSwapDialog({ + open: true, + traderProps: { + defaultInputCoin: { + id: sETH.address, + name: sETH.name ?? '', + symbol: sETH.symbol ?? '', + contract_address: sETH.address, + decimals: sETH.decimals, + type: TokenType.Fungible, + }, + defaultOutputCoin: { + id: ETH.address, + name: ETH.name ?? '', + symbol: ETH.symbol ?? '', + contract_address: ETH.address, + decimals: ETH.decimals, + type: TokenType.Fungible, + }, + }, + }) + }, [openSwapDialog]) return ( @@ -188,6 +218,10 @@ export function SavingsTable({ chainId, tab, protocols, setTab, setSelectedProto color="primary" disabled={tab === TabType.Withdraw ? isZero(protocol.balance) : false} onClick={() => { + if (tab === TabType.Withdraw && protocol.type === ProtocolType.Lido) { + onConvertClick() + return + } setTab(tab) setSelectedProtocol(protocol) }}> diff --git a/packages/mask/src/plugins/Savings/protocols/AAVEProtocol.ts b/packages/mask/src/plugins/Savings/protocols/AAVEProtocol.ts index 4c1ddaf1e699..9d55bb937ef0 100644 --- a/packages/mask/src/plugins/Savings/protocols/AAVEProtocol.ts +++ b/packages/mask/src/plugins/Savings/protocols/AAVEProtocol.ts @@ -239,13 +239,14 @@ export class AAVEProtocol implements SavingsProtocol { poolAddress || ZERO_ADDRESS, AaveLendingPoolABI as AbiItem[], ) - return new Promise((resolve) => + return new Promise((resolve, reject) => contract?.methods .withdraw(this.bareToken.address, new BigNumber(value).toFixed(), account) .send({ from: account, gas: gasEstimate.toNumber(), }) + .once(TransactionEventType.ERROR, reject) .once(TransactionEventType.CONFIRMATION, (_, receipt) => { resolve(receipt.transactionHash) }), diff --git a/packages/mask/src/plugins/Snapshot/SNSAdaptor/InformationCard.tsx b/packages/mask/src/plugins/Snapshot/SNSAdaptor/InformationCard.tsx index 9db85f13ced1..625c7e302429 100644 --- a/packages/mask/src/plugins/Snapshot/SNSAdaptor/InformationCard.tsx +++ b/packages/mask/src/plugins/Snapshot/SNSAdaptor/InformationCard.tsx @@ -1,10 +1,14 @@ -import { formatEthereumAddress, explorerResolver, resolveIPFSLink } from '@masknet/web3-shared-evm' +import { + formatEthereumAddress, + explorerResolver, + resolveIPFSLink, + resolveIPFSLinkFromURL, +} from '@masknet/web3-shared-evm' import { Avatar, Box, Link, Typography } from '@mui/material' import { makeStyles } from '@masknet/theme' import OpenInNew from '@mui/icons-material/OpenInNew' import formatDateTime from 'date-fns/format' import { useContext } from 'react' -import { TokenIcon } from '@masknet/shared' import { useI18N } from '../../../utils' import { EthereumBlockie } from '../../../web3/UI/EthereumBlockie' import { SnapshotContext } from '../context' @@ -80,7 +84,10 @@ export function InformationCard(props: InformationCardProps) { target="_blank" rel="noopener" href={explorerResolver.addressLink(chainId, strategy.params.address)}> - + ))} diff --git a/packages/mask/src/plugins/Snapshot/SNSAdaptor/LoadingFailCard.tsx b/packages/mask/src/plugins/Snapshot/SNSAdaptor/LoadingFailCard.tsx index 26b3473ea946..840ea0002b8c 100644 --- a/packages/mask/src/plugins/Snapshot/SNSAdaptor/LoadingFailCard.tsx +++ b/packages/mask/src/plugins/Snapshot/SNSAdaptor/LoadingFailCard.tsx @@ -36,15 +36,28 @@ export class LoadingFailCard extends Component< ) : ( - Loading fails due to Snapshot API service breakdown. - + + + Loading fails due to Snapshot API service breakdown. + + + ) } diff --git a/packages/mask/src/plugins/Snapshot/Worker/apis/index.ts b/packages/mask/src/plugins/Snapshot/Worker/apis/index.ts index 4b38b195362a..802950c5c34b 100644 --- a/packages/mask/src/plugins/Snapshot/Worker/apis/index.ts +++ b/packages/mask/src/plugins/Snapshot/Worker/apis/index.ts @@ -44,6 +44,7 @@ async function fetchProposalFromGraphql(id: string) { id name symbol + avatar } } votes(first: 10000, where: { proposal: $id }) { @@ -75,6 +76,7 @@ async function fetchProposalFromGraphql(id: string) { id: string name: string symbol: string + avatar: string } state: string title: string diff --git a/packages/mask/src/plugins/Snapshot/types.ts b/packages/mask/src/plugins/Snapshot/types.ts index d57fded9f0a8..985e33a782db 100644 --- a/packages/mask/src/plugins/Snapshot/types.ts +++ b/packages/mask/src/plugins/Snapshot/types.ts @@ -30,6 +30,7 @@ export interface Proposal { id: string name: string symbol: string + avatar: string } start: number snapshot: string diff --git a/packages/mask/src/plugins/Tips/SNSAdaptor/TipsEntranceDialog.tsx b/packages/mask/src/plugins/Tips/SNSAdaptor/TipsEntranceDialog.tsx index 84c84380b87a..8ccae4ef6c76 100644 --- a/packages/mask/src/plugins/Tips/SNSAdaptor/TipsEntranceDialog.tsx +++ b/packages/mask/src/plugins/Tips/SNSAdaptor/TipsEntranceDialog.tsx @@ -51,6 +51,7 @@ const useStyles = makeStyles()((theme) => ({ gap: theme.spacing(1.5), }, dialogContent: { + padding: '12px 16px', position: 'relative', boxSizing: 'border-box', }, diff --git a/packages/mask/src/plugins/Tips/components/NFTSection/NFTList.tsx b/packages/mask/src/plugins/Tips/components/NFTSection/NFTList.tsx index c3296c95f061..9a3db51223c5 100644 --- a/packages/mask/src/plugins/Tips/components/NFTSection/NFTList.tsx +++ b/packages/mask/src/plugins/Tips/components/NFTSection/NFTList.tsx @@ -2,9 +2,8 @@ import { useChainId, useCurrentWeb3NetworkPluginID, useWeb3State, Web3Helper } f import { ElementAnchor, NFTCardStyledAssetPlayer, RetryHint } from '@masknet/shared' import { LoadingBase, makeStyles } from '@masknet/theme' import { isSameAddress, NetworkPluginID, NonFungibleToken } from '@masknet/web3-shared-base' -import { Checkbox, Link, List, ListItem, Radio, Stack, Tooltip } from '@mui/material' +import { Checkbox, List, ListItem, Radio, Stack, Tooltip } from '@mui/material' import classnames from 'classnames' -import { noop } from 'lodash-unified' import { FC, useCallback } from 'react' import type { TipNFTKeyPair } from '../../types' @@ -153,13 +152,6 @@ export const NFTList: FC = ({ {tokens.map((token) => { const selected = includes(selectedPairs, [token.contract?.address!, token.tokenId]) const disabled = !isRadio && reachedLimit && !selected - const link = token.contract - ? Others?.explorerResolver?.nonFungibleTokenLink( - token.contract.chainId, - token.contract.address, - token.tokenId, - ) - : undefined const name = token.collection?.name || token.contract?.name const title = `${name} ${Others?.formatTokenId(token.tokenId, 2)}` return ( @@ -181,22 +173,19 @@ export const NFTList: FC = ({ [classes.disabled]: disabled, [classes.selected]: selected, [classes.unselected]: selectedPairs.length > 0 && !selected, - })}> - - - + })} + onClick={() => { + if (disabled) return + if (selected) { + toggleItem(null, '') + } else { + toggleItem(token.tokenId, token.contract?.address) + } + }}> + { - if (disabled) return - if (selected) { - toggleItem(null, '') - } else { - toggleItem(token.tokenId, token.contract?.address) - } - }} className={classes.checkbox} checked={selected} /> diff --git a/packages/mask/src/plugins/Trader/SNSAdaptor/trader/TraderDialog.tsx b/packages/mask/src/plugins/Trader/SNSAdaptor/trader/TraderDialog.tsx index 349fc3be830f..d76c6f43180e 100644 --- a/packages/mask/src/plugins/Trader/SNSAdaptor/trader/TraderDialog.tsx +++ b/packages/mask/src/plugins/Trader/SNSAdaptor/trader/TraderDialog.tsx @@ -11,7 +11,7 @@ import { TargetChainIdContext } from '@masknet/plugin-infra/web3-evm' import { PluginTraderMessages } from '../../messages' import { Trader, TraderRef, TraderProps } from './Trader' import { useI18N } from '../../../../utils' -import { makeStyles } from '@masknet/theme' +import { makeStyles, MaskColorVar } from '@masknet/theme' import { NetworkTab } from '../../../../components/shared/NetworkTab' import { useUpdateEffect } from 'react-use' import { NetworkPluginID } from '@masknet/web3-shared-base' @@ -28,6 +28,12 @@ const useStyles = makeStyles()((theme) => ({ position: 'sticky', top: 0, zIndex: 2, + + '& > div .MuiBox-root': isDashboard + ? { + background: MaskColorVar.mainBackground, + } + : {}, }, indicator: { display: 'none', diff --git a/packages/mask/src/plugins/Trader/SNSAdaptor/trader/components/ConfirmDialogUI.tsx b/packages/mask/src/plugins/Trader/SNSAdaptor/trader/components/ConfirmDialogUI.tsx index 3e4fa376cc4b..559890b48140 100644 --- a/packages/mask/src/plugins/Trader/SNSAdaptor/trader/components/ConfirmDialogUI.tsx +++ b/packages/mask/src/plugins/Trader/SNSAdaptor/trader/components/ConfirmDialogUI.tsx @@ -323,7 +323,7 @@ export const ConfirmDialogUI = memo( - + ( - + ()((theme, { isDashboard } height: 20, backgroundColor: !isDashboard ? theme.palette.maskColor?.primary : undefined, '&:hover': { - backgroundColor: !isDashboard ? lighten(theme.palette.maskColor?.primary, 0.1) : undefined, + backgroundColor: !isDashboard ? `${lighten(theme.palette.maskColor?.primary, 0.1)}!important` : undefined, }, }, chipLabel: { diff --git a/packages/mask/src/plugins/Trader/SNSAdaptor/trending/CoinMarketTable.tsx b/packages/mask/src/plugins/Trader/SNSAdaptor/trending/CoinMarketTable.tsx index 89fece8be549..9c2f7c2babec 100644 --- a/packages/mask/src/plugins/Trader/SNSAdaptor/trending/CoinMarketTable.tsx +++ b/packages/mask/src/plugins/Trader/SNSAdaptor/trending/CoinMarketTable.tsx @@ -2,10 +2,9 @@ import { Paper, Stack, Table, TableBody, TableCell, TableContainer, TableRow, Ty import { makeStyles } from '@masknet/theme' import { DataProvider } from '@masknet/public-api' import { FormattedCurrency } from '@masknet/shared' -import { formatCurrency, formatInteger, formatSupply } from '@masknet/web3-shared-base' +import { formatCurrency, formatInteger, formatSupply, TokenType } from '@masknet/web3-shared-base' import type { Trending } from '../../types' import { useI18N } from '../../../../utils' -import { TrendingCoinType } from '@masknet/web3-providers' const useStyles = makeStyles()((theme) => ({ root: { @@ -47,7 +46,7 @@ export function FungibleCoinMarketTable(props: CoinMarketTableProps) { return ( - + {t('plugin_trader_usdc_price_statistic')} @@ -56,19 +55,19 @@ export function FungibleCoinMarketTable(props: CoinMarketTableProps) { {DataProvider.UNISWAP_INFO !== dataProvider ? ( - + {t('plugin_trader_market_cap')} - + {market?.market_cap ? `$${formatSupply(market.market_cap)}` : '--'} ) : null} {DataProvider.UNISWAP_INFO !== dataProvider ? ( - + {t('plugin_trader_circulating_supply')} @@ -79,18 +78,18 @@ export function FungibleCoinMarketTable(props: CoinMarketTableProps) { ) : null} - + {t('plugin_trader_volume_24')} - + {market?.total_volume ? `$${formatSupply(market.total_volume)}` : '--'} {dataProvider !== DataProvider.UNISWAP_INFO ? ( - + {t('plugin_trader_total_supply')} @@ -117,7 +116,7 @@ export function NonFungibleCoinMarketTable(props: CoinMarketTableProps) { return ( - + {t('plugin_trader_market_statistic')} @@ -125,7 +124,7 @@ export function NonFungibleCoinMarketTable(props: CoinMarketTableProps) {
- + {t('plugin_trader_floor_price')} @@ -139,7 +138,7 @@ export function NonFungibleCoinMarketTable(props: CoinMarketTableProps) { - + {t('plugin_trader_volume_24')} @@ -153,7 +152,7 @@ export function NonFungibleCoinMarketTable(props: CoinMarketTableProps) { - + {t('plugin_trader_owners_count')} @@ -161,7 +160,7 @@ export function NonFungibleCoinMarketTable(props: CoinMarketTableProps) { {formatInteger(market?.owners_count, '--')} - + {t('plugin_trader_total_assets')} @@ -176,6 +175,6 @@ export function NonFungibleCoinMarketTable(props: CoinMarketTableProps) { } export function CoinMarketTable(props: CoinMarketTableProps) { - const isNFT = props.trending.coin.type === TrendingCoinType.NonFungible + const isNFT = props.trending.coin.type === TokenType.NonFungible return isNFT ? : } diff --git a/packages/mask/src/plugins/Trader/SNSAdaptor/trending/CoinMenu.tsx b/packages/mask/src/plugins/Trader/SNSAdaptor/trending/CoinMenu.tsx index c1044648a0ca..f7de1343b669 100644 --- a/packages/mask/src/plugins/Trader/SNSAdaptor/trending/CoinMenu.tsx +++ b/packages/mask/src/plugins/Trader/SNSAdaptor/trending/CoinMenu.tsx @@ -1,11 +1,11 @@ import { CheckCircleIcon } from '@masknet/icons' import { makeStyles, ShadowRootMenu } from '@masknet/theme' -import { TrendingCoinType } from '@masknet/web3-providers' +import { TokenType } from '@masknet/web3-shared-base' import RadioButtonUncheckedIcon from '@mui/icons-material/RadioButtonUnchecked' import { Divider, MenuItem, Stack, Typography } from '@mui/material' import { useTheme } from '@mui/system' -import { FC, PropsWithChildren, useCallback, useMemo } from 'react' import { groupBy, toPairs } from 'lodash-unified' +import { FC, PropsWithChildren, useCallback, useMemo } from 'react' import type { Coin } from '../../types' import { CoinIcon } from './components' @@ -16,6 +16,10 @@ const useStyles = makeStyles()((theme) => ({ '&::-webkit-scrollbar': { display: 'none', }, + '& > ul': { + paddingRight: '0 !important', + width: '100% !important', + }, }, groupName: { height: 18, @@ -26,7 +30,6 @@ const useStyles = makeStyles()((theme) => ({ menuItem: { overflow: 'hidden', alignItems: 'stretch', - paddingRight: 0, height: 36, }, itemText: { @@ -34,7 +37,6 @@ const useStyles = makeStyles()((theme) => ({ flexGrow: 1, justifyContent: 'space-around', gap: theme.spacing(1), - paddingRight: theme.spacing(1), alignItems: 'center', overflow: 'hidden', }, @@ -61,9 +63,9 @@ export interface CoinMenuOption { } interface TokenMenuListProps { options: CoinMenuOption[] - type?: TrendingCoinType + type?: TokenType value?: CoinMenuOption['value'] - onSelect(type: TrendingCoinType, value: CoinMenuOption['value']): void + onSelect(type: TokenType, value: CoinMenuOption['value']): void } const TokenMenuList: FC = ({ options, type, value, onSelect }) => { @@ -116,15 +118,15 @@ export interface CoinMenuProps { open: boolean anchorEl: HTMLElement | null options: CoinMenuOption[] - type?: TrendingCoinType + type?: TokenType value?: CoinMenuOption['value'] - onChange?: (type: TrendingCoinType, value: CoinMenuOption['value']) => void + onChange?: (type: TokenType, value: CoinMenuOption['value']) => void onClose?: () => void } -const menuGroupNameMap: Record = { - [TrendingCoinType.Fungible]: 'Token', - [TrendingCoinType.NonFungible]: 'NFT', +const menuGroupNameMap: Record = { + [TokenType.Fungible]: 'Token', + [TokenType.NonFungible]: 'NFT', } export const CoinMenu: FC> = ({ @@ -138,7 +140,7 @@ export const CoinMenu: FC> = ({ }) => { const { classes } = useStyles() const onSelect = useCallback( - (type: TrendingCoinType, value: CoinMenuOption['value']) => { + (type: TokenType, value: CoinMenuOption['value']) => { onChange?.(type, value) onClose?.() }, @@ -146,9 +148,9 @@ export const CoinMenu: FC> = ({ ) const menuItems = useMemo(() => { - const groups: Array<[type: TrendingCoinType, options: CoinMenuOption[]]> = toPairs( + const groups: Array<[type: TokenType, options: CoinMenuOption[]]> = toPairs( groupBy(options, (x) => x.coin.type), - ).map(([type, options]) => [Number.parseInt(type, 10), options]) + ).map(([type, options]) => [type as TokenType, options]) if (groups.length > 1) { return groups.map(([type, groupOptions]) => ( diff --git a/packages/mask/src/plugins/Trader/SNSAdaptor/trending/CoinMetadataTable.tsx b/packages/mask/src/plugins/Trader/SNSAdaptor/trending/CoinMetadataTable.tsx index 23e0423d895f..b21082e2eebc 100644 --- a/packages/mask/src/plugins/Trader/SNSAdaptor/trending/CoinMetadataTable.tsx +++ b/packages/mask/src/plugins/Trader/SNSAdaptor/trending/CoinMetadataTable.tsx @@ -95,7 +95,16 @@ export function CoinMetadataTable(props: CoinMetadataTableProps) { const metadataLinks = [['Website', trending.coin.home_urls]] as Array<[string, string[] | undefined]> - const contracts = trending.contracts ?? [] + const contracts = + trending.contracts ?? (trending.coin.chainId && trending.coin.contract_address) + ? [ + { + chainId: trending.coin.chainId!, + address: trending.coin.contract_address!, + iconURL: '', + }, + ] + : [] const [menu, openMenu] = useMenu( contracts.map((x) => ( diff --git a/packages/mask/src/plugins/Trader/SNSAdaptor/trending/CoinSafetyAlert.tsx b/packages/mask/src/plugins/Trader/SNSAdaptor/trending/CoinSafetyAlert.tsx index 7f2881433af1..40db6ad85fb7 100644 --- a/packages/mask/src/plugins/Trader/SNSAdaptor/trending/CoinSafetyAlert.tsx +++ b/packages/mask/src/plugins/Trader/SNSAdaptor/trending/CoinSafetyAlert.tsx @@ -8,7 +8,7 @@ import { ChainId, explorerResolver } from '@masknet/web3-shared-evm' const useStyles = makeStyles()((theme) => { return { root: { - padding: theme.spacing(0, 2, 2, 2), + padding: theme.spacing(2), }, approve: { marginLeft: theme.spacing(1), diff --git a/packages/mask/src/plugins/Trader/SNSAdaptor/trending/TickersTable.tsx b/packages/mask/src/plugins/Trader/SNSAdaptor/trending/TickersTable.tsx index 4293a566d492..507f36516655 100644 --- a/packages/mask/src/plugins/Trader/SNSAdaptor/trending/TickersTable.tsx +++ b/packages/mask/src/plugins/Trader/SNSAdaptor/trending/TickersTable.tsx @@ -12,14 +12,13 @@ import { import { makeStyles } from '@masknet/theme' import { FormattedCurrency } from '@masknet/shared' import { formatEthereumAddress } from '@masknet/web3-shared-evm' -import { formatCurrency } from '@masknet/web3-shared-base' +import { formatCurrency, TokenType } from '@masknet/web3-shared-base' import { useI18N } from '../../../../utils' import type { Ticker } from '../../types' import { DataProvider } from '@masknet/public-api' import { formatElapsed } from '../../../Wallet/formatter' import { ReactNode, useMemo } from 'react' import { compact, pick } from 'lodash-unified' -import { TrendingCoinType } from '@masknet/web3-providers' const useStyles = makeStyles()((theme) => ({ container: { @@ -57,7 +56,7 @@ const useStyles = makeStyles()((theme) => ({ export interface TickersTableProps { dataProvider: DataProvider tickers: Ticker[] - coinType: TrendingCoinType + coinType: TokenType } type FungibleTokenCells = 'exchange' | 'pair' | 'price' | 'volume' | 'updated' @@ -67,7 +66,7 @@ type Cells = FungibleTokenCells | NonFungibleTokenCells export function TickersTable({ dataProvider, tickers, coinType }: TickersTableProps) { const { t } = useI18N() const { classes } = useStyles() - const isNFT = coinType === TrendingCoinType.NonFungible + const isNFT = coinType === TokenType.NonFungible const isUniswap = dataProvider === DataProvider.UNISWAP_INFO const headCellMap: Record = { diff --git a/packages/mask/src/plugins/Trader/SNSAdaptor/trending/TraderView.tsx b/packages/mask/src/plugins/Trader/SNSAdaptor/trending/TraderView.tsx index 2045ab3f0fed..430da2c0cc9f 100644 --- a/packages/mask/src/plugins/Trader/SNSAdaptor/trending/TraderView.tsx +++ b/packages/mask/src/plugins/Trader/SNSAdaptor/trending/TraderView.tsx @@ -9,8 +9,7 @@ import { DataProvider } from '@masknet/public-api' import { NFTList } from '@masknet/shared' import { EMPTY_LIST } from '@masknet/shared-base' import { makeStyles, MaskTabList, useTabs } from '@masknet/theme' -import { TrendingCoinType } from '@masknet/web3-providers' -import { NetworkPluginID } from '@masknet/web3-shared-base' +import { NetworkPluginID, TokenType } from '@masknet/web3-shared-base' import { isNativeTokenSymbol } from '@masknet/web3-shared-evm' import { TabContext } from '@mui/lab' import { Link, Stack, Tab } from '@mui/material' @@ -169,7 +168,7 @@ export function TraderView(props: TraderViewProps) { }) // #endregion - const isNFT = trending?.coin.type === TrendingCoinType.NonFungible + const isNFT = trending?.coin.type === TokenType.NonFungible // #region if the coin is a native token or contract address exists const isSwappable = !isMinimalMode && @@ -238,8 +237,7 @@ export function TraderView(props: TraderViewProps) { props.onUpdate?.() }, [tabIndex, loadingTrending]) // #endregion - const collectionAddress = - trending?.coin.type === TrendingCoinType.NonFungible ? trending.coin.contract_address : undefined + const collectionAddress = trending?.coin.type === TokenType.NonFungible ? trending.coin.contract_address : undefined const { value: fetchedTokens = EMPTY_LIST, done, diff --git a/packages/mask/src/plugins/Trader/SNSAdaptor/trending/TrendingViewDeck.tsx b/packages/mask/src/plugins/Trader/SNSAdaptor/trending/TrendingViewDeck.tsx index 45179cb1a0bf..4d13767a1e7e 100644 --- a/packages/mask/src/plugins/Trader/SNSAdaptor/trending/TrendingViewDeck.tsx +++ b/packages/mask/src/plugins/Trader/SNSAdaptor/trending/TrendingViewDeck.tsx @@ -6,8 +6,8 @@ import { FormattedCurrency, Linking, TokenSecurityBar, useTokenSecurity } from ' import { EMPTY_LIST } from '@masknet/shared-base' import { useRemoteControlledDialog, useValueRef } from '@masknet/shared-base-ui' import { makeStyles, useStylesExtends } from '@masknet/theme' -import { TrendingAPI, TrendingCoinType } from '@masknet/web3-providers' -import { formatCurrency, NetworkPluginID } from '@masknet/web3-shared-base' +import type { TrendingAPI } from '@masknet/web3-providers' +import { formatCurrency, NetworkPluginID, TokenType } from '@masknet/web3-shared-base' import { ChainId } from '@masknet/web3-shared-evm' import { Avatar, Button, CardContent, IconButton, Paper, Stack, Typography, useTheme } from '@mui/material' import { Box } from '@mui/system' @@ -104,6 +104,12 @@ const useStyles = makeStyles()((theme) => { width: 24, height: 24, }, + buyButton: { + background: theme.palette.maskColor.dark, + color: theme.palette.maskColor.white, + marginLeft: 'auto', + marginBottom: theme.spacing(2), + }, } }) @@ -138,7 +144,7 @@ export function TrendingViewDeck(props: TrendingViewDeckProps) { const theme = useTheme() const classes = useStylesExtends(useStyles(), props) - const isNFT = coin.type === TrendingCoinType.NonFungible + const isNFT = coin.type === TokenType.NonFungible // #region buy const transakPluginEnabled = useActivatedPluginsSNSAdaptor('any').some((x) => x.ID === PluginId.Transak) @@ -152,19 +158,19 @@ export function TrendingViewDeck(props: TrendingViewDeckProps) { const isTokenSecurityEnable = !isNFT && !snsAdaptorMinimalPlugins.map((x) => x.ID).includes(PluginId.GoPlusSecurity) const { value: tokenSecurityInfo, error } = useTokenSecurity( - coin?.chainId ?? ChainId.Mainnet, + coin.chainId ?? ChainId.Mainnet, coin.contract_address?.trim(), isTokenSecurityEnable, ) + const isBuyable = !isNFT && transakPluginEnabled && !transakIsMinimalMode && coin.symbol && isAllowanceCoin const onBuyButtonClicked = useCallback(() => { setBuyDialog({ open: true, - // @ts-ignore code: coin.symbol, address: account, }) - }, [account, trending?.coin?.symbol]) + }, [account, coin.symbol]) // #endregion // #region sync with settings @@ -176,7 +182,7 @@ export function TrendingViewDeck(props: TrendingViewDeckProps) { // #region switch between coins with the same symbol const currentPreferredCoinIdSettings = useValueRef(getCurrentPreferredCoinIdSettings(dataProvider)) const onCoinMenuChange = useCallback( - (type: TrendingCoinType, value: string) => { + (type: TokenType, value: string) => { const settings = JSON.parse(currentPreferredCoinIdSettings) as Record const coin = coins.find((x) => x.id === value && x.type === type) if (!coin) return @@ -216,10 +222,10 @@ export function TrendingViewDeck(props: TrendingViewDeckProps) { @@ -230,9 +236,11 @@ export function TrendingViewDeck(props: TrendingViewDeckProps) { LinkProps={{ className: classes.name, title: coin.name.toUpperCase() }}> {coin.name.toUpperCase()} - - ({coin.symbol.toUpperCase()}) - + {coin.symbol ? ( + + ({coin.symbol.toUpperCase()}) + + ) : null} {coins.length > 1 ? ( <> @@ -253,6 +261,16 @@ export function TrendingViewDeck(props: TrendingViewDeckProps) { /> ) : null} + {isBuyable ? ( + + ) : null} @@ -292,21 +310,6 @@ export function TrendingViewDeck(props: TrendingViewDeckProps) { - - {transakPluginEnabled && !transakIsMinimalMode && trending.coin.symbol && isAllowanceCoin ? ( - - ) : null} - diff --git a/packages/mask/src/plugins/Trader/SNSAdaptor/trending/components/CoinIcon.tsx b/packages/mask/src/plugins/Trader/SNSAdaptor/trending/components/CoinIcon.tsx index 79792183527d..f39c47bc2b38 100644 --- a/packages/mask/src/plugins/Trader/SNSAdaptor/trending/components/CoinIcon.tsx +++ b/packages/mask/src/plugins/Trader/SNSAdaptor/trending/components/CoinIcon.tsx @@ -1,27 +1,28 @@ -import { TrendingCoinType } from '@masknet/web3-providers' -import { TokenIcon, TokenIconProps } from '@masknet/shared' +import { TokenIcon } from '@masknet/shared' +import { NetworkPluginID, TokenType } from '@masknet/web3-shared-base' import type { FC } from 'react' -import { NetworkPluginID } from '@masknet/web3-shared-base' interface CoinIconProps { - type: TrendingCoinType + type: TokenType address?: string logoUrl?: string name?: string size?: number } export const CoinIcon: FC = ({ type, address, logoUrl, name, size }) => { - const sharedProps: Partial = { - pluginID: NetworkPluginID.PLUGIN_EVM, - name, - AvatarProps: { - sx: { - height: size, - width: size, - }, - }, - } - if (address && type === TrendingCoinType.Fungible) return - if (type === TrendingCoinType.NonFungible) return - return null + return ( + + ) } diff --git a/packages/mask/src/plugins/Trader/apis/trending/index.ts b/packages/mask/src/plugins/Trader/apis/trending/index.ts index 668a4243d19a..58ad01bf30b3 100644 --- a/packages/mask/src/plugins/Trader/apis/trending/index.ts +++ b/packages/mask/src/plugins/Trader/apis/trending/index.ts @@ -170,11 +170,8 @@ export async function getAvailableDataProviders(chainId: ChainId, type?: TagType export async function getAvailableCoins(chainId: ChainId, keyword: string, type: TagType, dataProvider: DataProvider) { if (!(await checkAvailabilityOnDataProvider(chainId, keyword, type, dataProvider))) return [] const ids = coinNamespace.get(dataProvider)?.supportedSymbolIdsMap - return ( - ids - ?.get(resolveAlias(chainId, keyword, dataProvider).toLowerCase()) - ?.filter((x) => !isBlockedAddress(chainId, x.address || x.contract_address || '')) ?? [] - ) + const alias = resolveAlias(chainId, keyword, dataProvider).toLowerCase() + return ids?.get(alias)?.filter((x) => !isBlockedAddress(chainId, x.address || x.contract_address || '')) ?? [] } // #endregion diff --git a/packages/mask/src/plugins/Trader/trader/0x/useTradeCallback.ts b/packages/mask/src/plugins/Trader/trader/0x/useTradeCallback.ts index 7101f3f65922..c1f87753caf5 100644 --- a/packages/mask/src/plugins/Trader/trader/0x/useTradeCallback.ts +++ b/packages/mask/src/plugins/Trader/trader/0x/useTradeCallback.ts @@ -3,17 +3,17 @@ import { useAsyncFn } from 'react-use' import { pick } from 'lodash-unified' import stringify from 'json-stable-stringify' import type { TransactionConfig } from 'web3-core' -import { GasOptionConfig, TransactionEventType } from '@masknet/web3-shared-evm' +import type { GasOptionConfig } from '@masknet/web3-shared-evm' import type { SwapQuoteResponse, TradeComputed } from '../../types' import { TargetChainIdContext } from '@masknet/plugin-infra/web3-evm' import { SUPPORTED_CHAIN_ID_LIST } from './constants' -import { NetworkPluginID } from '@masknet/web3-shared-base' -import { useAccount, useWeb3 } from '@masknet/plugin-infra/web3' +import { NetworkPluginID, ZERO } from '@masknet/web3-shared-base' +import { useAccount, useWeb3Connection } from '@masknet/plugin-infra/web3' export function useTradeCallback(tradeComputed: TradeComputed | null, gasConfig?: GasOptionConfig) { const { targetChainId: chainId } = TargetChainIdContext.useContainer() const account = useAccount(NetworkPluginID.PLUGIN_EVM) - const web3 = useWeb3(NetworkPluginID.PLUGIN_EVM, { chainId }) + const connection = useWeb3Connection(NetworkPluginID.PLUGIN_EVM, { chainId }) // compose transaction config const config = useMemo(() => { @@ -27,28 +27,23 @@ export function useTradeCallback(tradeComputed: TradeComputed return useAsyncFn(async () => { // validate config - if (!web3 || !account || !config || !tradeComputed) { + if (!account || !config || !tradeComputed || !connection) { return } const config_ = { ...config, - gas: await web3.eth - .estimateGas({ + gas: + (await connection.estimateTransaction?.({ from: account, ...pick(tradeComputed.trade_, ['to', 'data', 'value']), - }) - .catch(() => 0), + })) ?? ZERO.toString(), } // send transaction and wait for hash - return new Promise((resolve, reject) => { - web3.eth - .sendTransaction(config_) - .on(TransactionEventType.CONFIRMATION, (_, receipt) => { - resolve(receipt.transactionHash) - }) - .on(TransactionEventType.ERROR, reject) - }) - }, [web3, account, chainId, stringify(config), gasConfig]) + const hash = await connection.sendTransaction(config_) + const receipt = await connection.getTransactionReceipt(hash) + + return receipt?.transactionHash + }, [connection, account, chainId, stringify(config), gasConfig]) } diff --git a/packages/mask/src/plugins/Trader/trader/balancer/useTradeCallback.ts b/packages/mask/src/plugins/Trader/trader/balancer/useTradeCallback.ts index da77ea446aac..fc3059a63ccc 100644 --- a/packages/mask/src/plugins/Trader/trader/balancer/useTradeCallback.ts +++ b/packages/mask/src/plugins/Trader/trader/balancer/useTradeCallback.ts @@ -1,11 +1,10 @@ import type { ExchangeProxy } from '@masknet/web3-contracts/types/ExchangeProxy' -import type { PayableTx } from '@masknet/web3-contracts/types/types' -import { SchemaType, GasOptionConfig, TransactionEventType, useTraderConstants } from '@masknet/web3-shared-evm' +import { SchemaType, GasOptionConfig, useTraderConstants, encodeContractTransaction } from '@masknet/web3-shared-evm' import { useAsyncFn } from 'react-use' import { SLIPPAGE_DEFAULT } from '../../constants' import { SwapResponse, TradeComputed, TradeStrategy } from '../../types' import { TargetChainIdContext } from '@masknet/plugin-infra/web3-evm' -import { useAccount } from '@masknet/plugin-infra/web3' +import { useAccount, useWeb3Connection } from '@masknet/plugin-infra/web3' import { NetworkPluginID } from '@masknet/web3-shared-base' import { useTradeAmount } from './useTradeAmount' @@ -18,7 +17,7 @@ export function useTradeCallback( const account = useAccount(NetworkPluginID.PLUGIN_EVM) const { targetChainId: chainId } = TargetChainIdContext.useContainer() const { BALANCER_ETH_ADDRESS } = useTraderConstants(chainId) - + const connection = useWeb3Connection() const tradeAmount = useTradeAmount(trade, allowedSlippage) return useAsyncFn(async () => { @@ -51,7 +50,22 @@ export function useTradeCallback( const outputTokenAddress = trade.outputToken.schema === SchemaType.Native ? BALANCER_ETH_ADDRESS : trade.outputToken.address - const tx = + // trade with the native token + let transactionValue = '0' + if (trade.strategy === TradeStrategy.ExactIn && trade.inputToken.schema === SchemaType.Native) + transactionValue = trade.inputAmount.toFixed() + else if (trade.strategy === TradeStrategy.ExactOut && trade.outputToken.schema === SchemaType.Native) + transactionValue = trade.outputAmount.toFixed() + + // send transaction and wait for hash + const config = { + from: account, + value: transactionValue, + ...gasConfig, + } + + const tx = await encodeContractTransaction( + exchangeProxyContract, trade.strategy === TradeStrategy.ExactIn ? exchangeProxyContract.methods.multihopBatchSwapExactIn( swap_, @@ -65,39 +79,14 @@ export function useTradeCallback( inputTokenAddress, outputTokenAddress, tradeAmount.toFixed(), - ) - - // trade with the native token - let transactionValue = '0' - if (trade.strategy === TradeStrategy.ExactIn && trade.inputToken.schema === SchemaType.Native) - transactionValue = trade.inputAmount.toFixed() - else if (trade.strategy === TradeStrategy.ExactOut && trade.outputToken.schema === SchemaType.Native) - transactionValue = trade.outputAmount.toFixed() + ), + config, + ) // send transaction and wait for hash - const config = { - from: account, - gas: await tx - .estimateGas({ - from: account, - value: transactionValue, - }) - .catch((error: Error) => { - throw error - }), - value: transactionValue, - ...gasConfig, - } + const hash = await connection.sendTransaction(tx) + const receipt = await connection.getTransactionReceipt(hash) - // send transaction and wait for hash - return new Promise((resolve, reject) => { - tx.send(config as PayableTx) - .on(TransactionEventType.CONFIRMATION, (_, receipt) => { - resolve(receipt.transactionHash) - }) - .on(TransactionEventType.ERROR, (error) => { - reject(error) - }) - }) - }, [chainId, trade, tradeAmount, exchangeProxyContract, BALANCER_ETH_ADDRESS]) + return receipt?.transactionHash + }, [chainId, trade, tradeAmount, exchangeProxyContract, BALANCER_ETH_ADDRESS, connection]) } diff --git a/packages/mask/src/plugins/Trader/trader/bancor/useTradeCallback.ts b/packages/mask/src/plugins/Trader/trader/bancor/useTradeCallback.ts index 3df5e15598d4..f7d91416aa61 100644 --- a/packages/mask/src/plugins/Trader/trader/bancor/useTradeCallback.ts +++ b/packages/mask/src/plugins/Trader/trader/bancor/useTradeCallback.ts @@ -2,16 +2,16 @@ import { pick } from 'lodash-unified' import { useMemo } from 'react' import { useAsyncFn } from 'react-use' import stringify from 'json-stable-stringify' -import { NetworkPluginID } from '@masknet/web3-shared-base' -import { useAccount, useWeb3 } from '@masknet/plugin-infra/web3' -import { GasOptionConfig, TransactionEventType } from '@masknet/web3-shared-evm' +import { NetworkPluginID, ZERO } from '@masknet/web3-shared-base' +import { useAccount, useWeb3Connection } from '@masknet/plugin-infra/web3' +import type { GasOptionConfig } from '@masknet/web3-shared-evm' import { PluginTraderRPC } from '../../messages' import type { SwapBancorRequest, TradeComputed } from '../../types' import { TargetChainIdContext } from '@masknet/plugin-infra/web3-evm' export function useTradeCallback(tradeComputed: TradeComputed | null, gasConfig?: GasOptionConfig) { const { targetChainId: chainId } = TargetChainIdContext.useContainer() - const web3 = useWeb3(NetworkPluginID.PLUGIN_EVM, { chainId }) + const connection = useWeb3Connection(NetworkPluginID.PLUGIN_EVM, { chainId }) const account = useAccount(NetworkPluginID.PLUGIN_EVM) const trade: SwapBancorRequest | null = useMemo(() => { @@ -20,14 +20,13 @@ export function useTradeCallback(tradeComputed: TradeComputed }, [account, tradeComputed]) return useAsyncFn(async () => { - if (!account || !trade || !web3) { + if (!account || !trade || !connection) { return } const [data, err] = await PluginTraderRPC.swapTransactionBancor(trade) if (err) { - const error = new Error(err.error.messages?.[0] || 'Unknown Error') - throw error + throw new Error(err.error.messages?.[0] || 'Unknown Error') } // Note that if approval is required, the API will also return the necessary approval transaction. @@ -36,20 +35,15 @@ export function useTradeCallback(tradeComputed: TradeComputed const config = pick(tradeTransaction.transaction, ['to', 'data', 'value', 'from']) const config_ = { ...config, - gas: await web3.eth.estimateGas(config).catch((error) => { - throw error - }), + gas: (await connection.estimateTransaction?.(config)) ?? ZERO.toString(), ...gasConfig, } // send transaction and wait for hash - return new Promise((resolve, reject) => { - web3.eth - .sendTransaction(config_) - .on(TransactionEventType.ERROR, reject) - .on(TransactionEventType.CONFIRMATION, (_, receipt) => { - resolve(receipt.transactionHash) - }) - }) - }, [web3, account, chainId, stringify(trade), gasConfig]) + + const hash = await connection.sendTransaction(config_) + const receipt = await connection.getTransactionReceipt(hash) + + return receipt?.transactionHash + }, [connection, account, chainId, stringify(trade), gasConfig]) } diff --git a/packages/mask/src/plugins/Trader/trader/dodo/useTradeCallback.ts b/packages/mask/src/plugins/Trader/trader/dodo/useTradeCallback.ts index da5fbc34b972..f446521c02af 100644 --- a/packages/mask/src/plugins/Trader/trader/dodo/useTradeCallback.ts +++ b/packages/mask/src/plugins/Trader/trader/dodo/useTradeCallback.ts @@ -1,4 +1,4 @@ -import { useAccount, useWeb3 } from '@masknet/plugin-infra/web3' +import { useAccount, useWeb3Connection } from '@masknet/plugin-infra/web3' import stringify from 'json-stable-stringify' import { pick } from 'lodash-unified' import { useMemo } from 'react' @@ -6,7 +6,7 @@ import { useAsyncFn } from 'react-use' import type { TransactionConfig } from 'web3-core' import type { SwapRouteSuccessResponse, TradeComputed } from '../../types' import { TargetChainIdContext } from '@masknet/plugin-infra/web3-evm' -import { NetworkPluginID } from '@masknet/web3-shared-base' +import { NetworkPluginID, ZERO } from '@masknet/web3-shared-base' import type { GasOptionConfig } from '@masknet/web3-shared-evm' export function useTradeCallback( @@ -15,7 +15,7 @@ export function useTradeCallback( ) { const { targetChainId: chainId } = TargetChainIdContext.useContainer() const account = useAccount(NetworkPluginID.PLUGIN_EVM) - const web3 = useWeb3(NetworkPluginID.PLUGIN_EVM, { chainId }) + const connection = useWeb3Connection(NetworkPluginID.PLUGIN_EVM, { chainId }) // compose transaction config const config = useMemo(() => { @@ -28,28 +28,22 @@ export function useTradeCallback( return useAsyncFn(async () => { // validate config - if (!account || !config || !web3) { + if (!account || !config || !connection) { return } // compose transaction config const config_ = { ...config, - gas: await web3.eth.estimateGas(config).catch((error) => { - throw error - }), + gas: (await connection.estimateTransaction?.(config)) ?? ZERO.toString(), ...gasConfig, } // send transaction and wait for hash - return new Promise((resolve, reject) => { - web3.eth.sendTransaction(config_, (error, hash) => { - if (error) { - reject(error) - } else { - resolve(hash) - } - }) - }) - }, [web3, account, chainId, stringify(config), gasConfig]) + + const hash = await connection.sendTransaction(config_) + const receipt = await connection.getTransactionReceipt(hash) + + return receipt?.transactionHash + }, [connection, account, chainId, stringify(config), gasConfig]) } diff --git a/packages/mask/src/plugins/Trader/trader/openocean/useTradeCallback.ts b/packages/mask/src/plugins/Trader/trader/openocean/useTradeCallback.ts index af16b1aa1588..3b081653bdec 100644 --- a/packages/mask/src/plugins/Trader/trader/openocean/useTradeCallback.ts +++ b/packages/mask/src/plugins/Trader/trader/openocean/useTradeCallback.ts @@ -5,14 +5,14 @@ import { useAsyncFn } from 'react-use' import type { TransactionConfig } from 'web3-core' import type { GasOptionConfig } from '@masknet/web3-shared-evm' import type { SwapOOSuccessResponse, TradeComputed } from '../../types' -import { NetworkPluginID } from '@masknet/web3-shared-base' -import { useAccount, useChainId, useWeb3 } from '@masknet/plugin-infra/web3' +import { NetworkPluginID, ZERO } from '@masknet/web3-shared-base' +import { useAccount, useChainId, useWeb3Connection } from '@masknet/plugin-infra/web3' export function useTradeCallback( tradeComputed: TradeComputed | null, gasConfig?: GasOptionConfig, ) { - const web3 = useWeb3(NetworkPluginID.PLUGIN_EVM) + const connection = useWeb3Connection(NetworkPluginID.PLUGIN_EVM) const account = useAccount(NetworkPluginID.PLUGIN_EVM) const chainId = useChainId(NetworkPluginID.PLUGIN_EVM) @@ -27,28 +27,21 @@ export function useTradeCallback( return useAsyncFn(async () => { // validate config - if (!account || !config || !web3) { + if (!account || !config || !connection) { return } // compose transaction config const config_ = { ...config, - gas: await web3.eth.estimateGas(config).catch((error) => { - throw error - }), + gas: (await connection.estimateTransaction?.(config)) ?? ZERO.toString(), ...gasConfig, } // send transaction and wait for hash - return new Promise((resolve, reject) => { - web3.eth.sendTransaction(config_, (error, hash) => { - if (error) { - reject(error) - } else { - resolve(hash) - } - }) - }) - }, [web3, account, chainId, stringify(config)]) + + const hash = await connection.sendTransaction(config_) + const receipt = await connection.getTransactionReceipt(hash) + return receipt?.transactionHash + }, [connection, account, chainId, stringify(config)]) } diff --git a/packages/mask/src/plugins/Trader/trader/uniswap/useTradeCallback.ts b/packages/mask/src/plugins/Trader/trader/uniswap/useTradeCallback.ts index 52edd6456174..5bdacb1555ef 100644 --- a/packages/mask/src/plugins/Trader/trader/uniswap/useTradeCallback.ts +++ b/packages/mask/src/plugins/Trader/trader/uniswap/useTradeCallback.ts @@ -2,13 +2,13 @@ import { useAsyncFn } from 'react-use' import BigNumber from 'bignumber.js' import type { TradeProvider } from '@masknet/public-api' import type { SwapParameters } from '@uniswap/v2-sdk' -import { GasOptionConfig, TransactionEventType } from '@masknet/web3-shared-evm' +import type { GasOptionConfig } from '@masknet/web3-shared-evm' import { useSwapParameters as useTradeParameters } from './useTradeParameters' import { swapErrorToUserReadableMessage } from '../../helpers' import type { SwapCall, Trade, TradeComputed } from '../../types' import { TargetChainIdContext } from '@masknet/plugin-infra/web3-evm' -import { useAccount, useWeb3 } from '@masknet/plugin-infra/web3' -import { NetworkPluginID } from '@masknet/web3-shared-base' +import { useAccount, useWeb3Connection } from '@masknet/plugin-infra/web3' +import { NetworkPluginID, ZERO } from '@masknet/web3-shared-base' interface FailedCall { parameters: SwapParameters @@ -36,15 +36,15 @@ export function useTradeCallback( allowedSlippage?: number, ) { const { targetChainId } = TargetChainIdContext.useContainer() - const web3 = useWeb3(NetworkPluginID.PLUGIN_EVM, { chainId: targetChainId }) + const connection = useWeb3Connection(NetworkPluginID.PLUGIN_EVM, { chainId: targetChainId }) const account = useAccount(NetworkPluginID.PLUGIN_EVM) const tradeParameters = useTradeParameters(trade, tradeProvider, allowedSlippage) return useAsyncFn(async () => { - if (!tradeParameters.length || !web3) { + if (!tradeParameters.length || !connection) { return } - + const web3 = await connection.getWeb3() // step 1: estimate each trade parameter const estimatedCalls: SwapCallEstimate[] = await Promise.all( tradeParameters.map(async (x) => { @@ -58,8 +58,14 @@ export function useTradeCallback( : { value: `0x${Number.parseInt(value, 16).toString(16)}` }), } - return web3.eth - .estimateGas(config) + if (!connection.estimateTransaction) { + return { + call: x, + gasEstimate: ZERO, + } + } + return connection + .estimateTransaction(config) .then((gasEstimate) => { return { call: x, @@ -104,39 +110,34 @@ export function useTradeCallback( bestCallOption = firstNoErrorCall } - return new Promise(async (resolve, reject) => { - if (!bestCallOption) { - return - } + if (!bestCallOption) { + return + } - const { - call: { address, calldata, value }, - } = bestCallOption + const { + call: { address, calldata, value }, + } = bestCallOption - web3.eth - .sendTransaction({ - from: account, - to: address, - data: calldata, - ...('gasEstimate' in bestCallOption ? { gas: bestCallOption.gasEstimate.toFixed() } : {}), - ...(!value || /^0x0*$/.test(value) ? {} : { value }), - ...gasConfig, - }) - .on(TransactionEventType.CONFIRMATION, (_, receipt) => { - resolve(receipt.transactionHash) - }) - .on(TransactionEventType.ERROR, (error) => { - if (!(error as any)?.code) { - reject(error) - return - } - const error_ = new Error( - error?.message === 'Unable to add more requests.' - ? 'Unable to add more requests.' - : 'Transaction rejected.', - ) - reject(error_) - }) - }) - }, [web3, account, tradeParameters, gasConfig]) + try { + const hash = await connection.sendTransaction({ + from: account, + to: address, + data: calldata, + ...('gasEstimate' in bestCallOption ? { gas: bestCallOption.gasEstimate.toFixed() } : {}), + ...(!value || /^0x0*$/.test(value) ? {} : { value }), + ...gasConfig, + }) + const receipt = await connection.getTransactionReceipt(hash) + return receipt?.transactionHash + } catch (error: any) { + if (!(error as any)?.code) { + throw error + } + throw new Error( + error?.message === 'Unable to add more requests.' + ? 'Unable to add more requests.' + : 'Transaction rejected.', + ) + } + }, [connection, account, tradeParameters, gasConfig]) } diff --git a/packages/mask/src/plugins/Trader/trending/useAvailableCoins.ts b/packages/mask/src/plugins/Trader/trending/useAvailableCoins.ts index 21aca17966e0..f5e050678ab8 100644 --- a/packages/mask/src/plugins/Trader/trending/useAvailableCoins.ts +++ b/packages/mask/src/plugins/Trader/trending/useAvailableCoins.ts @@ -1,16 +1,17 @@ +import { useChainId } from '@masknet/plugin-infra/web3' +import type { DataProvider } from '@masknet/public-api' +import { EMPTY_LIST } from '@masknet/shared-base' +import { NetworkPluginID } from '@masknet/web3-shared-base' import { useAsync } from 'react-use' import type { AsyncState } from 'react-use/lib/useAsyncFn' import { PluginTraderRPC } from '../messages' import type { Coin, TagType } from '../types' -import type { DataProvider } from '@masknet/public-api' -import { useChainId } from '@masknet/plugin-infra/web3' -import { NetworkPluginID } from '@masknet/web3-shared-base' export function useAvailableCoins(type: TagType, keyword: string, dataProvider: DataProvider): AsyncState { const chainId = useChainId(NetworkPluginID.PLUGIN_EVM) return useAsync(async () => { - if (!keyword) return [] + if (!keyword) return EMPTY_LIST return PluginTraderRPC.getAvailableCoins(chainId, keyword, type, dataProvider) }, [dataProvider, type, keyword, chainId]) } diff --git a/packages/mask/src/plugins/Trader/trending/useTrending.ts b/packages/mask/src/plugins/Trader/trending/useTrending.ts index 8cd6020b561d..02c2cf993400 100644 --- a/packages/mask/src/plugins/Trader/trending/useTrending.ts +++ b/packages/mask/src/plugins/Trader/trending/useTrending.ts @@ -22,8 +22,8 @@ export function useTrendingByKeyword(tagType: TagType, keyword: string, dataProv const coin = { ...trending?.coin, decimals: trending?.coin.decimals || detailedToken?.decimals || 0, - contract_address: trending?.contracts?.[0]?.address, - chainId: trending?.contracts?.[0]?.chainId, + contract_address: trending?.contracts?.[0]?.address ?? trending?.coin.contract_address, + chainId: trending?.contracts?.[0]?.chainId ?? trending?.coin.chainId, } as Coin return { value: { @@ -50,7 +50,7 @@ export function useTrendingById(id: string, dataProvider: DataProvider) { } = useAsync(async () => { if (!id) return null if (!currency) return null - return PluginTraderRPC.getCoinTrendingById(chainId, id, currency, dataProvider) + return PluginTraderRPC.getCoinTrendingById(chainId, id, currency, dataProvider).catch(() => null) }, [chainId, dataProvider, currency?.id, id]) const { value: detailedToken } = useFungibleToken(NetworkPluginID.PLUGIN_EVM, trending?.coin.contract_address) @@ -58,8 +58,8 @@ export function useTrendingById(id: string, dataProvider: DataProvider) { const coin = { ...trending?.coin, decimals: trending?.coin.decimals || detailedToken?.decimals || 0, - contract_address: trending?.contracts?.[0]?.address, - chainId: trending?.contracts?.[0]?.chainId, + contract_address: trending?.contracts?.[0]?.address ?? trending?.coin.contract_address, + chainId: trending?.contracts?.[0]?.chainId ?? trending?.coin.chainId, } as Coin return { diff --git a/packages/mask/src/plugins/Wallet/services/send.ts b/packages/mask/src/plugins/Wallet/services/send.ts index 30c627ddb7bd..6177c84dc303 100644 --- a/packages/mask/src/plugins/Wallet/services/send.ts +++ b/packages/mask/src/plugins/Wallet/services/send.ts @@ -1,11 +1,13 @@ import Web3 from 'web3' import type { HttpProvider } from 'web3-core' import type { JsonRpcPayload, JsonRpcResponse } from 'web3-core-helpers' +import { isNil } from 'lodash-unified' import { defer } from '@dimensiondev/kit' import { ChainId, + createWeb3, EthereumMethodType, - getPayloadConfig, + getSignablePayloadConfig, getPayloadId, getRPCConstants, isRiskMethod, @@ -13,7 +15,6 @@ import { import { openPopupWindow, removePopupWindow } from '../../../../background/services/helper' import { nativeAPI } from '../../../../shared/native-rpc' import { WalletRPC } from '../messages' -import { isNil } from 'lodash-unified' enum JSON_RPC_ERROR_CODE { INVALID_REQUEST = -32600, @@ -119,34 +120,34 @@ export async function send( options?: Options, ) { const provider = await createProvider(options?.chainId) - const computedPayload = getPayloadConfig(payload) + switch (payload.method) { case EthereumMethodType.ETH_SEND_TRANSACTION: case EthereumMethodType.MASK_REPLACE_TRANSACTION: + const computedPayload = getSignablePayloadConfig(payload) if (!computedPayload?.from || !computedPayload.to || !options?.chainId) return - const rawTransaction = await WalletRPC.signTransaction(computedPayload.from as string, { - ...computedPayload, - chainId: options.chainId, - }) - if (!rawTransaction) break + const privateKey = await WalletRPC.exportPrivateKey(computedPayload.from as string) + const web3 = createWeb3(provider) + const transactionSigned = await web3.eth.accounts.signTransaction(computedPayload, `0x${privateKey}`) + if (!transactionSigned.rawTransaction) break return provider.send( { ...payload, method: EthereumMethodType.ETH_SEND_RAW_TRANSACTION, - params: [rawTransaction], + params: [transactionSigned.rawTransaction], }, callback, ) case EthereumMethodType.ETH_SIGN_TYPED_DATA: const [address, dataToSign] = payload.params as [string, string] - const signed = await WalletRPC.signTypedData(address, dataToSign) + const dataSigned = await WalletRPC.signTypedData(address, dataToSign) try { callback(null, { jsonrpc: '2.0', id: payload.id as number, - result: signed, + result: dataSigned, }) } catch (error) { callback(getError(error, null, 'Failed to sign message.')) @@ -154,12 +155,12 @@ export async function send( break case EthereumMethodType.PERSONAL_SIGN: const [data, account] = payload.params as [string, string] - const personalSigned = await WalletRPC.signPersonalMessage(data, account) + const messageSigned = await WalletRPC.signPersonalMessage(data, account) try { callback(null, { jsonrpc: '2.0', id: payload.id as number, - result: personalSigned, + result: messageSigned, }) } catch (error) { callback(getError(error, null, 'Failed to sign message.')) @@ -219,7 +220,7 @@ export async function confirmRequest(payload: JsonRpcPayload, options?: Options) return } if (response?.error) { - reject(new Error(`Failed to send transaction: ${response.error}`)) + reject(new Error(`Failed to send transaction: ${response.error?.message ?? response.error}`)) return } WalletRPC.deleteUnconfirmedRequest(payload) diff --git a/packages/mask/src/resources/image-payload/index.ts b/packages/mask/src/resources/image-payload/index.ts index 5657f8a46107..ea5d95676b1a 100644 --- a/packages/mask/src/resources/image-payload/index.ts +++ b/packages/mask/src/resources/image-payload/index.ts @@ -2,5 +2,4 @@ import type { ImageTemplateTypes } from '@masknet/encryption' export const ImagePayloadURLs: Readonly> = { v2: new URL('./normal/payload-v2.png', import.meta.url).toString(), - eth: new URL('./wallet/payload-eth.png', import.meta.url).toString(), } diff --git a/packages/mask/src/resources/image-payload/wallet/payload-eth.png b/packages/mask/src/resources/image-payload/wallet/payload-eth.png deleted file mode 100644 index e9f6d6a2614e..000000000000 Binary files a/packages/mask/src/resources/image-payload/wallet/payload-eth.png and /dev/null differ diff --git a/packages/mask/src/social-network-adaptor/twitter.com/collecting/identity.ts b/packages/mask/src/social-network-adaptor/twitter.com/collecting/identity.ts index d394351bf86b..b0fabd0fb176 100644 --- a/packages/mask/src/social-network-adaptor/twitter.com/collecting/identity.ts +++ b/packages/mask/src/social-network-adaptor/twitter.com/collecting/identity.ts @@ -124,6 +124,7 @@ function resolveCurrentVisitingIdentityInner( ) { const update = async (twitterId: string) => { const user = await Twitter.getUserByScreenName(twitterId) + if (!user) return const bio = user.legacy.description const nickname = user.legacy.name diff --git a/packages/mask/src/utils/components/PluginWalletStatusBar.tsx b/packages/mask/src/utils/components/PluginWalletStatusBar.tsx index 4a420c5c9d2a..69a4eb73975c 100644 --- a/packages/mask/src/utils/components/PluginWalletStatusBar.tsx +++ b/packages/mask/src/utils/components/PluginWalletStatusBar.tsx @@ -14,13 +14,14 @@ import { import { WalletMessages } from '@masknet/plugin-wallet' import { ImageIcon, WalletIcon } from '@masknet/shared' import { useRemoteControlledDialog } from '@masknet/shared-base-ui' -import { makeStyles, parseColor } from '@masknet/theme' +import { makeStyles, MaskColorVar, parseColor } from '@masknet/theme' import { NetworkPluginID, TransactionStatusType, Wallet } from '@masknet/web3-shared-base' import { Box, Button, CircularProgress, Link, Typography } from '@mui/material' import { useI18N } from '../i18n-next-ui' import { LinkOutIcon, ArrowDropIcon, PluginWalletConnectIcon } from '@masknet/icons' import { useLayoutEffect, useRef, useState, PropsWithChildren } from 'react' import { ChainId, ProviderType } from '@masknet/web3-shared-evm' +import { isDashboardPage } from '@masknet/shared-base' interface WalletStatusBarProps extends PropsWithChildren<{}> { className?: string @@ -34,11 +35,16 @@ interface WalletStatusBarProps extends PropsWithChildren<{}> { expectedChainIdOrNetworkTypeOrID?: string | number } +const isDashboard = isDashboardPage() + const useStyles = makeStyles()((theme) => ({ root: { + boxSizing: 'content-box', display: 'flex', - backgroundColor: parseColor(theme.palette.maskColor?.bottom).setAlpha(0.8).toRgbString(), - boxShadow: `0 0 20px ${parseColor(theme.palette.maskColor?.highlight).setAlpha(0.05).toRgbString()}`, + backgroundColor: isDashboard + ? MaskColorVar.mainBackground + : parseColor(theme.palette.maskColor.bottom).setAlpha(0.8).toRgbString(), + boxShadow: `0 0 20px ${parseColor(theme.palette.maskColor.highlight).setAlpha(0.2).toRgbString()}`, backdropFilter: 'blur(16px)', padding: theme.spacing(2), borderRadius: '0 0 12px 12px', @@ -60,13 +66,13 @@ const useStyles = makeStyles()((theme) => ({ display: 'flex', alignItems: 'center', columnGap: 4, - color: theme.palette.maskColor?.main, + color: theme.palette.maskColor.main, fontWeight: 700, fontSize: 14, lineHeight: '18px', }, address: { - color: theme.palette.maskColor?.second, + color: theme.palette.maskColor.second, fontSize: 14, lineHeight: '18px', display: 'flex', @@ -79,19 +85,19 @@ const useStyles = makeStyles()((theme) => ({ gap: 2, borderRadius: 2, padding: '2px 4px', - backgroundColor: parseColor(theme.palette.maskColor?.warn).setAlpha(0.1).toRgbString(), - color: theme.palette.maskColor?.warn, + backgroundColor: parseColor(theme.palette.maskColor.warn).setAlpha(0.1).toRgbString(), + color: theme.palette.maskColor.warn, fontSize: 14, lineHeight: '18px', }, progress: { - color: theme.palette.maskColor?.warn, + color: theme.palette.maskColor.warn, }, linkIcon: { width: 14, height: 14, fontSize: 14, - fill: theme.palette.maskColor?.second, + color: theme.palette.maskColor.second, cursor: 'pointer', }, action: { @@ -211,7 +217,7 @@ export function PluginWalletStatusBar({ e.stopPropagation() openWalletStatusDialog() }}> - {t('wallet_status_bar_pending')} + {t('recent_transaction_pending')} ) : null} diff --git a/packages/mask/src/utils/hooks/useSceneChange.ts b/packages/mask/src/utils/hooks/useSceneChange.ts new file mode 100644 index 000000000000..fd912c98c6ac --- /dev/null +++ b/packages/mask/src/utils/hooks/useSceneChange.ts @@ -0,0 +1,8 @@ +import { useEffect } from 'react' + +export function useSceneChange(handler: (event: WindowEventMap['scenechange']) => void) { + useEffect(() => { + window.addEventListener('scenechange', handler) + return () => window.removeEventListener('scenechange', handler) + }, [handler]) +} diff --git a/packages/mask/src/utils/theme/useClassicMaskFullPageTheme.ts b/packages/mask/src/utils/theme/useClassicMaskFullPageTheme.ts index 8a0da1724299..3854232ac03b 100644 --- a/packages/mask/src/utils/theme/useClassicMaskFullPageTheme.ts +++ b/packages/mask/src/utils/theme/useClassicMaskFullPageTheme.ts @@ -1,10 +1,11 @@ // ! This file is used during SSR. DO NOT import new files that does not work in SSR import type { LanguageOptions } from '@masknet/public-api' -import { Appearance } from '@masknet/theme' +import { Appearance, MaskColors } from '@masknet/theme' import { PaletteMode, unstable_createMuiStrictModeTheme } from '@mui/material' import { MaskDarkTheme, MaskLightTheme } from './MaskTheme' import { useThemeLanguage } from './useThemeLanguage' +import produce, { setAutoFreeze } from 'immer' /** * @deprecated Should migrate to \@masknet/theme @@ -15,7 +16,13 @@ export function useClassicMaskFullPageTheme(userPreference: Appearance, language const finalPalette: PaletteMode = userPreference === Appearance.default ? systemPreference : userPreference const baseTheme = finalPalette === 'dark' ? MaskDarkTheme : MaskLightTheme - return unstable_createMuiStrictModeTheme(baseTheme, useThemeLanguage(language)) + setAutoFreeze(false) + const maskTheme = produce(baseTheme, (theme) => { + const colorSchema = MaskColors[theme.palette.mode] + theme.palette.maskColor = colorSchema.maskColor + }) + setAutoFreeze(true) + return unstable_createMuiStrictModeTheme(maskTheme, useThemeLanguage(language)) } /** diff --git a/packages/mask/src/web3/UI/ChainBoundary.tsx b/packages/mask/src/web3/UI/ChainBoundary.tsx index 0817df9a22bf..9b385174ecfb 100644 --- a/packages/mask/src/web3/UI/ChainBoundary.tsx +++ b/packages/mask/src/web3/UI/ChainBoundary.tsx @@ -12,8 +12,9 @@ import { useWeb3State, useWeb3Connection, useChainIdValid, + useProviderDescriptor, } from '@masknet/plugin-infra/web3' -import { ChainId, ProviderType } from '@masknet/web3-shared-evm' +import { ProviderType } from '@masknet/web3-shared-evm' import { useRemoteControlledDialog } from '@masknet/shared-base-ui' import { delay } from '@dimensiondev/kit' import ActionButton, { @@ -83,6 +84,7 @@ export function ChainBoundary(props: ChainBoundaryPro const { Others: actualOthers } = useWeb3State(actualPluginID) const actualChainId = useChainId(actualPluginID) const actualProviderType = useProviderType(actualPluginID) + const actualProviderDescriptor = useProviderDescriptor(actualPluginID) const actualChainName = actualOthers?.chainResolver.chainName(actualChainId) const account = useAccount(actualPluginID) @@ -135,15 +137,14 @@ export function ChainBoundary(props: ChainBoundaryPro openSelectProviderDialog, ]) - const fortmaticDisabled = useMemo(() => { - if (actualProviderType !== ProviderType.Fortmatic) return false - return !(expectedChainId === ChainId.Mainnet || expectedChainId === ChainId.BSC) - }, [actualProviderType, expectedChainId]) + const switchButtonDisabled = useMemo(() => { + return !(actualProviderDescriptor?.enableRequirements?.supportedChainIds?.includes(expectedChainId) ?? false) + }, [expectedChainId, actualProviderDescriptor]) const renderBox = (children?: React.ReactNode, tips?: string) => { return ( - + {children} @@ -251,7 +252,7 @@ export function ChainBoundary(props: ChainBoundaryPro size={18} /> } - disabled={actualProviderType === ProviderType.WalletConnect || fortmaticDisabled} + disabled={actualProviderType === ProviderType.WalletConnect || switchButtonDisabled} sx={props.ActionButtonPromiseProps?.sx} init={{t('plugin_wallet_switch_network', { network: expectedChainName })}} waiting={t('plugin_wallet_switch_network_under_going', { @@ -268,7 +269,7 @@ export function ChainBoundary(props: ChainBoundaryPro , actualProviderType === ProviderType.WalletConnect ? t('plugin_wallet_connect_tips') - : fortmaticDisabled + : switchButtonDisabled ? t('plugin_wallet_not_support_network') : '', ) diff --git a/packages/mask/src/web3/UI/EthereumERC20TokenApprovedBoundary.tsx b/packages/mask/src/web3/UI/EthereumERC20TokenApprovedBoundary.tsx index 5e33d87e5156..e146566acc85 100644 --- a/packages/mask/src/web3/UI/EthereumERC20TokenApprovedBoundary.tsx +++ b/packages/mask/src/web3/UI/EthereumERC20TokenApprovedBoundary.tsx @@ -117,6 +117,7 @@ export function EthereumERC20TokenApprovedBoundary(props: EthereumERC20TokenAppr address={token.address} chainId={token.chainId} name={token.name} + disableDefaultIcon classes={{ icon: classes.icon }} /> } diff --git a/packages/mask/src/web3/UI/EthereumERC721TokenApprovedBoundary.tsx b/packages/mask/src/web3/UI/EthereumERC721TokenApprovedBoundary.tsx index 261a0bcacc17..163dbb33cbf8 100644 --- a/packages/mask/src/web3/UI/EthereumERC721TokenApprovedBoundary.tsx +++ b/packages/mask/src/web3/UI/EthereumERC721TokenApprovedBoundary.tsx @@ -49,6 +49,7 @@ export function EthereumERC721TokenApprovedBoundary(props: EthereumERC712TokenAp contractDetailed?.address, operator, true, + retry, ) const validationMessage = useMemo(() => { @@ -65,7 +66,6 @@ export function EthereumERC721TokenApprovedBoundary(props: EthereumERC712TokenAp @@ -96,7 +95,6 @@ export function EthereumERC721TokenApprovedBoundary(props: EthereumERC712TokenAp @@ -126,7 +123,6 @@ export function EthereumERC721TokenApprovedBoundary(props: EthereumERC712TokenAp diff --git a/packages/plugin-infra/src/types.ts b/packages/plugin-infra/src/types.ts index 35c32492029d..de1241da56ee 100644 --- a/packages/plugin-infra/src/types.ts +++ b/packages/plugin-infra/src/types.ts @@ -547,6 +547,7 @@ export namespace Plugin.SNSAdaptor { recommendFeature?: { description: React.ReactNode backgroundGradient: string + isFirst?: boolean } } diff --git a/packages/plugin-infra/src/web3-state/Provider.ts b/packages/plugin-infra/src/web3-state/Provider.ts index 080adc4b39ef..d42bb5c9538b 100644 --- a/packages/plugin-infra/src/web3-state/Provider.ts +++ b/packages/plugin-infra/src/web3-state/Provider.ts @@ -109,7 +109,7 @@ export class ProviderState< const siteType = getSiteType() if (!siteType) return - this.storage.providerType.setValue(this.options.getDefaultProviderType()) + this.storage.providerType.setValue(this.options.getDefaultProviderType(siteType)) }) }) } diff --git a/packages/plugin-infra/src/web3/EVM/useGasConfig.ts b/packages/plugin-infra/src/web3/EVM/useGasConfig.ts index 8846b45668e8..29c2648ad867 100644 --- a/packages/plugin-infra/src/web3/EVM/useGasConfig.ts +++ b/packages/plugin-infra/src/web3/EVM/useGasConfig.ts @@ -3,10 +3,14 @@ import { useAsync } from 'react-use' import { GasOptionConfig, formatGweiToWei, ChainId } from '@masknet/web3-shared-evm' import { GasOptionType, NetworkPluginID } from '@masknet/web3-shared-base' import { useGasOptions } from '../useGasOptions' +import { useWeb3State } from '../useWeb3State' // TODO: support multiple chain export function useGasConfig(chainId: ChainId) { const [gasConfig, setGasConfig] = useState() + const { Others } = useWeb3State(NetworkPluginID.PLUGIN_EVM) + const isEIP1559 = Others?.chainResolver.isSupport(chainId, 'EIP1559') + const { value: gasOptions_ } = useGasOptions(NetworkPluginID.PLUGIN_EVM) const { value: gasPrice } = useAsync(async () => { try { @@ -17,10 +21,16 @@ export function useGasConfig(chainId: ChainId) { gasOptions_?.[GasOptionType.NORMAL]?.suggestedMaxPriorityFeePerGas ?? 0, ).toFixed(0) - setGasConfig({ - maxFeePerGas, - maxPriorityFeePerGas, - }) + setGasConfig( + isEIP1559 + ? { + maxFeePerGas, + maxPriorityFeePerGas, + } + : { + gasPrice: maxFeePerGas, + }, + ) return maxFeePerGas } catch (err) { setGasConfig(undefined) diff --git a/packages/plugin-infra/src/web3/useNonFungibleAsset.ts b/packages/plugin-infra/src/web3/useNonFungibleAsset.ts index dc2dcb44b9ff..045a3155b956 100644 --- a/packages/plugin-infra/src/web3/useNonFungibleAsset.ts +++ b/packages/plugin-infra/src/web3/useNonFungibleAsset.ts @@ -3,7 +3,6 @@ import type { NetworkPluginID } from '@masknet/web3-shared-base' import type { Web3Helper } from '../web3-helpers' import { useAccount } from '../entry-web3' import { useWeb3Hub } from './useWeb3Hub' -import { ChainId } from '@masknet/web3-shared-evm' export function useNonFungibleAsset( pluginID?: T, @@ -19,6 +18,6 @@ export function useNonFungibleAsset | undefined>(async () => { if (!address || !id || !hub) return - return hub.getNonFungibleAsset?.(address, id, { chainId: ChainId.Mainnet }) + return hub.getNonFungibleAsset?.(address, id) }, [address, id, hub]) } diff --git a/packages/plugins/CyberConnect/src/SNSAdaptor/ConnectButton.tsx b/packages/plugins/CyberConnect/src/SNSAdaptor/ConnectButton.tsx index 545e4a667a55..1cc4b27e21e7 100644 --- a/packages/plugins/CyberConnect/src/SNSAdaptor/ConnectButton.tsx +++ b/packages/plugins/CyberConnect/src/SNSAdaptor/ConnectButton.tsx @@ -92,7 +92,7 @@ export default function ConnectButton({ }) { const { classes, cx } = useStyles() const web3 = useWeb3(NetworkPluginID.PLUGIN_EVM) - const myAddress = useAccount(NetworkPluginID.PLUGIN_EVM) + const myAddress = useAccount() const [cc, setCC] = useState(null) const [isFollowing, setFollowing] = useState(false) const [isLoading, setLoading] = useState(false) @@ -134,9 +134,15 @@ export default function ConnectButton({ } }, [cc, myAddress, isFollowing]) + if (!myAddress) + return ( + + Please connect your wallet first + + ) if (blockChainNetwork !== NetworkPluginID.PLUGIN_EVM) { return ( - + Please switch to EVM-based wallet to follow ) diff --git a/packages/plugins/CyberConnect/src/SNSAdaptor/FollowTab.tsx b/packages/plugins/CyberConnect/src/SNSAdaptor/FollowTab.tsx index ea74e6be060d..7fe5a1371463 100644 --- a/packages/plugins/CyberConnect/src/SNSAdaptor/FollowTab.tsx +++ b/packages/plugins/CyberConnect/src/SNSAdaptor/FollowTab.tsx @@ -89,6 +89,7 @@ export default function FollowTab({ sx={{ color: 'black', width: '100%', + boxSizing: 'border-box', }}> {followingList.map((f: IFollowIdentity) => { return @@ -100,6 +101,7 @@ export default function FollowTab({ sx={{ color: 'black', width: '100%', + boxSizing: 'border-box', }}> {followerList.map((f: IFollowIdentity) => { return diff --git a/packages/plugins/Debugger/src/SNSAdaptor/components/ConsoleContent.tsx b/packages/plugins/Debugger/src/SNSAdaptor/components/ConsoleContent.tsx index 39fc5a22876d..c94174ec8b38 100644 --- a/packages/plugins/Debugger/src/SNSAdaptor/components/ConsoleContent.tsx +++ b/packages/plugins/Debugger/src/SNSAdaptor/components/ConsoleContent.tsx @@ -11,7 +11,7 @@ import { useWeb3State, Web3Helper, } from '@masknet/plugin-infra/web3' -import { makeStyles } from '@masknet/theme' +import { makeStyles, useCustomSnackbar } from '@masknet/theme' import { NetworkPluginID, SourceType } from '@masknet/web3-shared-base' import { ChainId, @@ -61,6 +61,7 @@ export function ConsoleContent(props: ConsoleContentProps) { const { value: balance = '0' } = useBalance() const { value: blockNumber = 0 } = useBlockNumber() const { value: blockTimestamp = 0 } = useBlockTimestamp() + const onTransferCallback = useCallback(() => { if (!NATIVE_TOKEN_ADDRESS) return return connection.transferFungibleToken( @@ -70,6 +71,26 @@ export function ConsoleContent(props: ConsoleContentProps) { ) }, [connection]) + const onApproveFungibleTokenCallback = useCallback(() => { + if (pluginID !== NetworkPluginID.PLUGIN_EVM) return + if (chainId !== ChainId.Mainnet) return + return connection.approveFungibleToken( + '0x6B175474E89094C44Da98b954EedeAC495271d0F', + '0x31f42841c2db5173425b5223809cf3a38fede360', + '1', + ) + }, [pluginID, connection]) + + const onApproveNonFungibleTokenCallback = useCallback(() => { + if (pluginID !== NetworkPluginID.PLUGIN_EVM) return + if (chainId !== ChainId.Mainnet) return + return connection.approveNonFungibleToken( + '0xd945f759d422ae30a6166838317b937de08380e3', + '0x31f42841c2db5173425b5223809cf3a38fede360', + '71050', + ) + }, [pluginID, connection]) + const onSignMessage = useCallback( async (type?: string) => { const message = 'Hello World' @@ -152,6 +173,8 @@ export function ConsoleContent(props: ConsoleContentProps) { const onSelectFungibleToken = useSelectFungibleToken() const onSelectGasSettings = useSelectAdvancedSettings(NetworkPluginID.PLUGIN_EVM) + const { showSnackbar, closeSnackbar } = useCustomSnackbar() + return (
@@ -208,6 +231,30 @@ export function ConsoleContent(props: ConsoleContentProps) { + + + + Approve Fungible Token + + + + + + + + + + Approve Non-Fungible Token + + + + + + @@ -266,9 +313,7 @@ export function ConsoleContent(props: ConsoleContentProps) { switch (pluginID) { case NetworkPluginID.PLUGIN_EVM: await onSwitchChain( - chainId === EVM_ChainId.Mainnet - ? EVM_ChainId.Matic - : EVM_ChainId.Mainnet, + chainId === EVM_ChainId.Mainnet ? EVM_ChainId.BSC : EVM_ChainId.Mainnet, ) break default: @@ -487,6 +532,21 @@ export function ConsoleContent(props: ConsoleContentProps) { + + Test Snackbar + + + +
diff --git a/packages/plugins/EVM/src/state/Connection/connection.ts b/packages/plugins/EVM/src/state/Connection/connection.ts index 0da7171ec6e0..56f6d443bbb8 100644 --- a/packages/plugins/EVM/src/state/Connection/connection.ts +++ b/packages/plugins/EVM/src/state/Connection/connection.ts @@ -60,8 +60,8 @@ import { getReceiptStatus } from './utils' import { Web3StateSettings } from '../../settings' import { getSubscriptionCurrentValue, PartialRequired } from '@masknet/shared-base' -const EMPTY_STRING = () => Promise.resolve('') -const ZERO = () => Promise.resolve(0) +const EMPTY_STRING = Promise.resolve('') +const ZERO = Promise.resolve(0) export function isReadOnlyMethod(method: EthereumMethodType) { return [ @@ -337,26 +337,30 @@ class Connection implements EVM_Connection { const ERC721_ENUMERABLE_INTERFACE_ID = '0x780e9d63' const ERC1155_ENUMERABLE_INTERFACE_ID = '0xd9b67a26' - const erc165Contract = await this.getWeb3Contract(address, ERC165ABI as AbiItem[], options) + try { + const erc165Contract = await this.getWeb3Contract(address, ERC165ABI as AbiItem[], options) - const isERC165 = await erc165Contract?.methods - .supportsInterface(ERC165_INTERFACE_ID) - .call({ from: options.account }) + const isERC165 = await erc165Contract?.methods + .supportsInterface(ERC165_INTERFACE_ID) + .call({ from: options.account }) - const isERC721 = await erc165Contract?.methods - .supportsInterface(ERC721_ENUMERABLE_INTERFACE_ID) - .call({ from: options.account }) - if (isERC165 && isERC721) return SchemaType.ERC721 + const isERC721 = await erc165Contract?.methods + .supportsInterface(ERC721_ENUMERABLE_INTERFACE_ID) + .call({ from: options.account }) + if (isERC165 && isERC721) return SchemaType.ERC721 - const isERC1155 = await erc165Contract?.methods - .supportsInterface(ERC1155_ENUMERABLE_INTERFACE_ID) - .call({ from: options.account }) - if (isERC165 && isERC1155) return SchemaType.ERC1155 + const isERC1155 = await erc165Contract?.methods + .supportsInterface(ERC1155_ENUMERABLE_INTERFACE_ID) + .call({ from: options.account }) + if (isERC165 && isERC1155) return SchemaType.ERC1155 - const isERC20 = (await this.getCode(address, options)) !== '0x' - if (isERC20) return SchemaType.ERC20 + const isERC20 = (await this.getCode(address, options)) !== '0x' + if (isERC20) return SchemaType.ERC20 - return + return + } catch { + return + } } async getNonFungibleToken( address: string, @@ -794,6 +798,7 @@ class Connection implements EVM_Connection { { from: options.account, ...transaction, + value: transaction.value ? toHex(transaction.value) : undefined, }, ], }, diff --git a/packages/plugins/EVM/src/state/Connection/middleware/Nonce.ts b/packages/plugins/EVM/src/state/Connection/middleware/Nonce.ts index 4dd6945f0d6a..b5bfae20b392 100644 --- a/packages/plugins/EVM/src/state/Connection/middleware/Nonce.ts +++ b/packages/plugins/EVM/src/state/Connection/middleware/Nonce.ts @@ -1,120 +1,38 @@ import { EthereumAddress } from 'wallet.ts' +import Web3 from 'web3' import { toHex } from 'web3-utils' -import { EthereumMethodType, ProviderType } from '@masknet/web3-shared-evm' -import type { Context, EVM_Connection, Middleware } from '../types' -import { SharedContextSettings } from '../../../settings' - -class NonceManager { - constructor(private address: string, private connection: EVM_Connection) {} - - private nonce = NonceManager.INITIAL_NONCE - private locked = false - private tasks: Array<() => void> = [] - - private lock() { - this.locked = true - } - private unlock() { - this.locked = false - } - private continue() { - if (!this.locked) this.tasks.shift()?.() - } - private async getRemoteNonce() { - const { chainId } = SharedContextSettings.value - - return new Promise(async (resolve, reject) => { - const callback = (error: Error | null, nonce = 0) => { - if (error) reject(error) - else resolve(nonce) - this.unlock() - this.continue() - } - const run = async () => { - try { - this.lock() - callback( - null, - await this.connection.getTransactionNonce(this.address, { - chainId: chainId.getCurrentValue(), - // Only mask wallets need to use Nonce - providerType: ProviderType.MaskWallet, - }), - ) - } catch (error: unknown) { - callback(error instanceof Error ? error : new Error('Failed to get remote nonce.')) - } - } - if (this.locked) this.tasks.push(run) - else run() - }) - } - - public async getNonce() { - const nonce = Math.max(await this.getRemoteNonce(), this.nonce) - await this.setNonce(nonce) - return nonce - } - - public async setNonce(nonce: number) { - return new Promise(async (resolve, reject) => { - const callback = (e: Error | null) => { - if (e) reject(e) - else resolve() - this.unlock() - this.continue() - } - const run = async () => { - this.lock() - this.nonce = nonce - callback(null) - } - if (this.locked) this.tasks.push(run) - else run() - }) - } - - public async resetNonce() { - const nonce = await this.getRemoteNonce() - await this.setNonce(nonce) - } - - static INITIAL_NONCE = -1 -} +import { first } from 'lodash-unified' +import { ChainId, EthereumMethodType, getRPCConstants, ProviderType } from '@masknet/web3-shared-evm' +import type { Context, Middleware } from '../types' export class Nonce implements Middleware { - private cache = new Map() - - constructor() { - // const { chainId } = SharedContextSettings.value - // // reset all nonce if the chain id of mask wallet was changed - // chainId.subscribe(() => { - // this.resetAllNonce() - // }) - } + static INITIAL_NONCE = -1 - private getManager(address: string, connection: EVM_Connection) { - const address_ = EthereumAddress.checksumAddress(address) - if (!this.cache.has(address_)) this.cache.set(address_, new NonceManager(address_, connection)) - return this.cache.get(address_)! - } + private nonces = new Map>() - private getNonce(address: string, connection: EVM_Connection) { - return this.getManager(address, connection).getNonce() - } + private getRemoteNonce(chainId: ChainId, address: string) { + const RPC_URL = first(getRPCConstants(chainId).RPC_URLS) + if (!RPC_URL) throw new Error('Failed to fetch nonce.') - private async commitNonce(address: string, connection: EVM_Connection) { - const manager = this.getManager(address, connection) - return manager.setNonce((await manager.getNonce()) + 1) + const web3 = new Web3(RPC_URL) + return web3.eth.getTransactionCount(address) } - private resetNonce(address: string, connection: EVM_Connection) { - const manager = this.getManager(address, connection) - return manager.resetNonce() - } - - private async resetAllNonce() { - await Promise.all(Array.from(this.cache.values()).map((m) => m.resetNonce())) + private async syncRemoteNonce(chainId: ChainId, address: string, commitment = 0) { + const address_ = EthereumAddress.checksumAddress(address) + const addressNonces = this.nonces.get(address_) ?? new Map() + addressNonces.set( + chainId, + commitment + + Math.max( + await this.getRemoteNonce(chainId, address), + addressNonces.get(chainId) ?? Nonce.INITIAL_NONCE, + ), + ) + + // set back into cache + this.nonces.set(address_, addressNonces) + return addressNonces.get(chainId)! } async fn(context: Context, next: () => Promise) { @@ -129,7 +47,7 @@ export class Nonce implements Middleware { params: [ { ...context.config, - nonce: toHex(await this.getNonce(context.account, context.connection)), + nonce: toHex(await this.syncRemoteNonce(context.chainId, context.account)), }, ], } @@ -145,10 +63,10 @@ export class Nonce implements Middleware { const isAuroraErrorNonce = message.includes('ERR_INCORRECT_NONCE') // if a transaction hash was received then commit the nonce - if (isGeneralErrorNonce || isAuroraErrorNonce) await this.resetNonce(context.account, context.connection) + if (isGeneralErrorNonce || isAuroraErrorNonce) await this.syncRemoteNonce(context.chainId, context.account) // else if a nonce error was occurred then reset the nonce else if (!context.error && typeof context.result === 'string') - await this.commitNonce(context.account, context.connection) + await this.syncRemoteNonce(context.chainId, context.account, 1) } catch { // to scan the context to determine how to update the local nonce, allow to fail silently } diff --git a/packages/plugins/EVM/src/state/Connection/providers/Fortmatic.ts b/packages/plugins/EVM/src/state/Connection/providers/Fortmatic.ts index 5db1b1dc093e..868d5ba3d1c0 100644 --- a/packages/plugins/EVM/src/state/Connection/providers/Fortmatic.ts +++ b/packages/plugins/EVM/src/state/Connection/providers/Fortmatic.ts @@ -1,8 +1,9 @@ import Fortmatic from 'fortmatic' +import { toHex } from 'web3-utils' import type { RequestArguments } from 'web3-core' import { first } from 'lodash-unified' import type { FmProvider } from 'fortmatic/dist/cjs/src/core/fm-provider' -import { ChainId, chainResolver, getRPCConstants } from '@masknet/web3-shared-evm' +import { ChainId, chainResolver, getRPCConstants, ProviderType } from '@masknet/web3-shared-evm' import { createLookupTableResolver } from '@masknet/web3-shared-base' import type { EVM_Provider } from '../types' import { BaseProvider } from './Base' @@ -50,16 +51,28 @@ export default class FortmaticProvider extends BaseProvider implements EVM_Provi private get chainId(): ChainIdFortmatic { const chainId = this.chainId_ if (!chainId) throw new Error('No connection.') - if (!isFortmaticSupported(chainId)) throw new Error(`Chain id ${chainId} is not supported.`) + if (!isFortmaticSupported(chainId)) throw new Error(`The chain id ${chainId} is not supported.`) return chainId } private set chainId(newChainId: ChainId) { const chainId = newChainId - if (!isFortmaticSupported(chainId)) throw new Error(`Chain id ${chainId} is not supported.`) + if (!isFortmaticSupported(chainId)) throw new Error(`The chain id ${chainId} is not supported.`) this.chainId_ = chainId } + protected onAccountsChanged(accounts: string[]) { + this.emitter.emit('accounts', accounts) + } + + protected onChainChanged(chainId: string) { + this.emitter.emit('chainId', chainId) + } + + protected onDisconnect() { + this.emitter.emit('disconnect', ProviderType.Fortmatic) + } + private createFortmatic(chainId: ChainIdFortmatic) { const rpcUrl = first(getRPCConstants(chainId).RPC_URLS) if (!rpcUrl) throw new Error('Failed to create provider.') @@ -85,11 +98,21 @@ export default class FortmaticProvider extends BaseProvider implements EVM_Provi return fm.user.logout() } + override async switchChain(chainId?: ChainId): Promise { + if (chainId) { + await this.connect(chainId) + return + } + throw new Error(`Failed to switch to ${chainResolver.chainFullName(chainId)}.`) + } + override async connect(chainId: ChainId) { try { this.chainId = chainId const accounts = await this.login() if (!accounts.length) throw new Error(`Failed to connect to ${chainResolver.chainFullName(this.chainId)}.`) + this.onAccountsChanged(accounts) + this.onChainChanged(toHex(chainId)) return { account: first(accounts)!, chainId, @@ -103,6 +126,7 @@ export default class FortmaticProvider extends BaseProvider implements EVM_Provi override async disconnect() { await this.logout() this.chainId_ = null + this.onDisconnect() } override request(requestArguments: RequestArguments) { diff --git a/packages/plugins/EVM/src/state/TransactionFormatter.ts b/packages/plugins/EVM/src/state/TransactionFormatter.ts index 8101f61a3f80..1d8b33eb99d4 100644 --- a/packages/plugins/EVM/src/state/TransactionFormatter.ts +++ b/packages/plugins/EVM/src/state/TransactionFormatter.ts @@ -106,7 +106,7 @@ export class TransactionFormatter extends TransactionFormatterState) { if (!context.methods?.length) return @@ -10,16 +19,31 @@ export class ERC721Descriptor implements TransactionDescriptor { const parameters = method.parameters switch (method.name) { - case 'setApprovalForAll': + case 'approve': { + if (parameters?.to === undefined || parameters?.tokenId === undefined) break + + const symbol = await this.getContractSymbol(context.chainId, context.to) + + return { + chainId: context.chainId, + title: `Unlock ${symbol ?? 'token'} contract`, + description: `Unlock ${symbol ?? 'token'} contract`, + successfulDescription: `${symbol ?? 'token'} is unlocked successfully.`, + } + } + case 'setApprovalForAll': { if (parameters?.operator === undefined || parameters?.approved === undefined) break + + const action = parameters?.approved === false ? 'Revoke' : 'Unlock' + const symbol = await this.getContractSymbol(context.chainId, context.to) + return { chainId: context.chainId, - title: parameters?.approved === false ? 'Revoke' : 'Unlock', - description: `${ - parameters?.approved === false ? 'Revoke the approval for' : 'Unlock' - } the token.`, - successfulDescription: 'Revoke the approval successfully.', + title: `${action} ${symbol ?? 'token'} contract`, + description: `${action} ${symbol ?? 'token'} contract`, + successfulDescription: `${action} the approval successfully.`, } + } default: return diff --git a/packages/plugins/EVM/src/state/TransactionFormatter/descriptors/RedPacket.ts b/packages/plugins/EVM/src/state/TransactionFormatter/descriptors/RedPacket.ts index 72a305dbf3ab..f85b02f8d31a 100644 --- a/packages/plugins/EVM/src/state/TransactionFormatter/descriptors/RedPacket.ts +++ b/packages/plugins/EVM/src/state/TransactionFormatter/descriptors/RedPacket.ts @@ -17,35 +17,52 @@ export class RedPacketDescriptor implements TransactionDescriptor { const { HAPPY_RED_PACKET_ADDRESS_V4 } = getRedPacketConstants(context.chainId) const { RED_PACKET_NFT_ADDRESS } = getNftRedPacketConstants(context.chainId) - const parameters = context.methods?.find((x) => x.name === 'create_red_packet')?.parameters - if (!parameters?._token_addr && parameters?._total_tokens) return + const method = context.methods?.find((x) => ['create_red_packet', 'claim'].includes(x.name ?? '')) - if (isSameAddress(context.to, HAPPY_RED_PACKET_ADDRESS_V4)) { + if ( + isSameAddress(context.to, HAPPY_RED_PACKET_ADDRESS_V4) && + method?.parameters?._token_addr && + method?.parameters?._total_tokens + ) { const connection = await Web3StateSettings.value.Connection?.getConnection?.({ chainId: context.chainId, account: context.from, }) - - const token = await connection?.getFungibleToken(parameters?._token_addr ?? '') + const token = await connection?.getFungibleToken(method?.parameters?._token_addr ?? '') const amount = formatBalance( - parameters?._total_tokens, + method.parameters?._total_tokens, token?.decimals, - isNativeTokenAddress(parameters?._token_addr) ? 6 : 0, + isNativeTokenAddress(method.parameters?._token_addr) ? 6 : 0, ) - - return { - chainId: context.chainId, - title: 'Create Lucky Drop', - description: i18NextInstance.t('plugin_red_packet_create_with_token', { - amount, - symbol: token?.symbol, - }), + if (method?.name === 'create_red_packet') { + return { + chainId: context.chainId, + title: 'Create Lucky Drop', + description: i18NextInstance.t('plugin_red_packet_create_with_token', { + amount, + symbol: token?.symbol, + }), + } + } else if (method?.name === 'claim') { + return { + chainId: context.chainId, + title: 'Claim Lucky Drop', + description: i18NextInstance.t('plugin_red_packet_claim_notification'), + } } } else if (isSameAddress(context.to, RED_PACKET_NFT_ADDRESS)) { - return { - chainId: context.chainId, - title: 'Create NFT Lucky Drop', - description: i18NextInstance.t('plugin_nft_red_packet_create'), + if (method?.name === 'create_red_packet') { + return { + chainId: context.chainId, + title: 'Create NFT Lucky Drop', + description: i18NextInstance.t('plugin_nft_red_packet_create'), + } + } else if (method?.name === 'claim') { + return { + chainId: context.chainId, + title: 'Claim NFT Lucky Drop', + description: i18NextInstance.t('plugin_nft_red_packet_claim'), + } } } return diff --git a/packages/plugins/GoPlusSecurity/src/SNSAdaptor/components/Common.tsx b/packages/plugins/GoPlusSecurity/src/SNSAdaptor/components/Common.tsx index f20033875d53..403cd516e158 100644 --- a/packages/plugins/GoPlusSecurity/src/SNSAdaptor/components/Common.tsx +++ b/packages/plugins/GoPlusSecurity/src/SNSAdaptor/components/Common.tsx @@ -34,13 +34,13 @@ export const DefineMapping: DefineMapping = { [SecurityMessageLevel.High]: { i18nKey: 'high_risk', titleColor: '#FF5F5F', - bgColor: 'rgba(255, 95, 95, 0.1)', + bgColor: 'rgba(255, 53, 69, 0.1)', icon: (size: number) => , }, [SecurityMessageLevel.Medium]: { i18nKey: 'medium_risk', - titleColor: '#FFB915', - bgColor: 'rgba(255, 177, 0, 0.2)', + titleColor: '#FFB100', + bgColor: 'rgba(255, 177, 0, 0.1)', // TODO: Merge duplicate icon in a another PR. icon: (size: number) => , }, diff --git a/packages/plugins/GoPlusSecurity/src/SNSAdaptor/components/RiskCard.tsx b/packages/plugins/GoPlusSecurity/src/SNSAdaptor/components/RiskCard.tsx index 971b3342e68b..a9efe262cbb2 100644 --- a/packages/plugins/GoPlusSecurity/src/SNSAdaptor/components/RiskCard.tsx +++ b/packages/plugins/GoPlusSecurity/src/SNSAdaptor/components/RiskCard.tsx @@ -22,11 +22,14 @@ const useStyles = makeStyles()((theme) => ({ alignItems: 'center', }, header: { - fontSize: 14, + fontSize: 16, + fontWeight: 700, lineHeight: '22px', }, description: { - fontSize: 12, + fontSize: 16, + fontWeight: 400, + color: theme.palette.maskColor.second, }, })) diff --git a/packages/plugins/GoPlusSecurity/src/SNSAdaptor/components/SecurityPanel.tsx b/packages/plugins/GoPlusSecurity/src/SNSAdaptor/components/SecurityPanel.tsx index 060233f8aa36..12a067f6c7d9 100644 --- a/packages/plugins/GoPlusSecurity/src/SNSAdaptor/components/SecurityPanel.tsx +++ b/packages/plugins/GoPlusSecurity/src/SNSAdaptor/components/SecurityPanel.tsx @@ -23,7 +23,7 @@ interface TokenCardProps { const useStyles = makeStyles()((theme) => ({ header: { - fontWeight: 500, + fontWeight: 700, fontSize: 18, }, root: { @@ -43,13 +43,14 @@ const useStyles = makeStyles()((theme) => ({ height: '48px', }, tokenName: { - fontSize: '16px', + fontSize: '18px', fontWeight: 700, + color: theme.palette.maskColor.main, }, tokenPrice: { - fontSize: '16px', + fontSize: '18px', fontWeight: 700, - color: theme.palette.text.secondary, + color: theme.palette.maskColor.main, }, itemTitle: { color: theme.palette.maskColor.second, @@ -160,7 +161,7 @@ export const SecurityPanel = memo(({ tokenSecurity, tokenInfo, t /> - + {t.more_details()} (({ tokenSecurity, tokenInfo, t href={resolveGoLabLink(tokenSecurity.chainId, tokenSecurity.contract)} target="_blank" rel="noopener noreferrer"> - + @@ -204,28 +207,18 @@ export const SecurityPanel = memo(({ tokenSecurity, tokenInfo, t )} - - - {makeMessageList.map((x, i) => ( - - ))} - {(!makeMessageList.length || securityMessageLevel === SecurityMessageLevel.Safe) && ( - - )} - - + + {makeMessageList.map((x, i) => ( + + ))} + {(!makeMessageList.length || securityMessageLevel === SecurityMessageLevel.Safe) && ( + + )} + ) diff --git a/packages/plugins/GoPlusSecurity/src/SNSAdaptor/components/TokenPanel.tsx b/packages/plugins/GoPlusSecurity/src/SNSAdaptor/components/TokenPanel.tsx index bf36eced894d..b7c57869d857 100644 --- a/packages/plugins/GoPlusSecurity/src/SNSAdaptor/components/TokenPanel.tsx +++ b/packages/plugins/GoPlusSecurity/src/SNSAdaptor/components/TokenPanel.tsx @@ -5,7 +5,7 @@ import React from 'react' import { useTheme } from '@mui/system' import { makeStyles, usePortalShadowRoot } from '@masknet/theme' import { explorerResolver, formatEthereumAddress } from '@masknet/web3-shared-evm' -import { formatCurrency } from '@masknet/web3-shared-base' +import { formatCurrency, formatSupply } from '@masknet/web3-shared-base' import { LinkOutIcon } from '@masknet/icons' const useStyles = makeStyles()((theme) => ({ @@ -51,16 +51,17 @@ export const TokenPanel = React.forwardRef(({ tokenSecurity, tokenMarketCap }: T const theme = useTheme() const totalSupply = usePortalShadowRoot((container) => { + const supply = tokenSecurity.total_supply ? formatSupply(tokenSecurity.total_supply) : DEFAULT_PLACEHOLDER return ( theme.palette.text.buttonText} className={classes.tooltip}> - {tokenSecurity.total_supply} + {supply} }> - {formatTotalSupply(tokenSecurity.total_supply)} + {supply} ) }) @@ -88,7 +89,9 @@ export const TokenPanel = React.forwardRef(({ tokenSecurity, tokenMarketCap }: T href={explorerResolver.fungibleTokenLink(tokenSecurity.chainId, tokenSecurity.contract)} target="_blank" rel="noopener noreferrer"> - {' '} + @@ -109,7 +112,9 @@ export const TokenPanel = React.forwardRef(({ tokenSecurity, tokenMarketCap }: T )} target="_blank" rel="noopener noreferrer"> - + )} @@ -140,7 +145,7 @@ export const TokenPanel = React.forwardRef(({ tokenSecurity, tokenMarketCap }: T {t.token_market_cap()} - {tokenMarketCap ? formatCurrency(tokenMarketCap) : DEFAULT_PLACEHOLDER} + {tokenMarketCap ? `$${formatSupply(tokenMarketCap)}` : DEFAULT_PLACEHOLDER} diff --git a/packages/plugins/GoPlusSecurity/src/SNSAdaptor/index.tsx b/packages/plugins/GoPlusSecurity/src/SNSAdaptor/index.tsx index 0194a6b1b098..eb29fee2a06a 100644 --- a/packages/plugins/GoPlusSecurity/src/SNSAdaptor/index.tsx +++ b/packages/plugins/GoPlusSecurity/src/SNSAdaptor/index.tsx @@ -33,7 +33,6 @@ const sns: Plugin.SNSAdaptor.Definition = { }, name, icon, - iconFilterColor, appBoardSortingDefaultPriority: 14, category: 'dapp', marketListSortingPriority: 16, diff --git a/packages/plugins/GoPlusSecurity/src/base.ts b/packages/plugins/GoPlusSecurity/src/base.ts index c19fe3023fe1..2c83e4ed8e50 100644 --- a/packages/plugins/GoPlusSecurity/src/base.ts +++ b/packages/plugins/GoPlusSecurity/src/base.ts @@ -4,7 +4,6 @@ import { languages } from './locales/languages' export const base: Plugin.Shared.Definition = { ID: PLUGIN_ID, - icon: '', name: { fallback: PLUGIN_NAME }, description: { fallback: PLUGIN_DESCRIPTION }, publisher: { name: { fallback: 'Mask Network' }, link: 'https://mask.io/' }, diff --git a/packages/plugins/RSS3/src/SNSAdaptor/TabCard.tsx b/packages/plugins/RSS3/src/SNSAdaptor/TabCard.tsx index 247dca396a53..888796c26d09 100644 --- a/packages/plugins/RSS3/src/SNSAdaptor/TabCard.tsx +++ b/packages/plugins/RSS3/src/SNSAdaptor/TabCard.tsx @@ -108,7 +108,10 @@ export function TabCard({ type, socialAddressList, persona }: TabCardProps) { className={classes.button}> {selectedAddress?.type === SocialAddressType.KV || selectedAddress?.type === SocialAddressType.ADDRESS ? ( - + ) : ( selectedAddress.label )} @@ -123,9 +126,8 @@ export function TabCard({ type, socialAddressList, persona }: TabCardProps) { {uniqBy(socialAddressList ?? [], (x) => x.address.toLowerCase()).map((x) => { return ( onSelect(x)}> - {selectedAddress?.type === SocialAddressType.KV || - selectedAddress?.type === SocialAddressType.ADDRESS ? ( - + {x?.type === SocialAddressType.KV || x?.type === SocialAddressType.ADDRESS ? ( + ) : ( x.label )} diff --git a/packages/plugins/RSS3/src/SNSAdaptor/index.tsx b/packages/plugins/RSS3/src/SNSAdaptor/index.tsx index 454adf7a5020..1d3e40b7a429 100644 --- a/packages/plugins/RSS3/src/SNSAdaptor/index.tsx +++ b/packages/plugins/RSS3/src/SNSAdaptor/index.tsx @@ -12,13 +12,7 @@ function sorter(a: SocialAddress, z: SocialAddress>) { - return ( - addressNames?.some( - (x) => - (x.type === SocialAddressType.RSS3 || x.type === SocialAddressType.KV) && - x.networkSupporterPluginID === NetworkPluginID.PLUGIN_EVM, - ) ?? false - ) + return !!addressNames?.some((x) => x.networkSupporterPluginID === NetworkPluginID.PLUGIN_EVM) } const sns: Plugin.SNSAdaptor.Definition = { diff --git a/packages/plugins/Web3Profile/src/SNSAdaptor/components/AddCollectibles.tsx b/packages/plugins/Web3Profile/src/SNSAdaptor/components/AddCollectibles.tsx index 2ef64203701d..a4b02a112376 100644 --- a/packages/plugins/Web3Profile/src/SNSAdaptor/components/AddCollectibles.tsx +++ b/packages/plugins/Web3Profile/src/SNSAdaptor/components/AddCollectibles.tsx @@ -145,6 +145,9 @@ export function AddNFT(props: AddNFTProps) {
e.currentTarget.getElementsByTagName('input')[0].focus()} sx={{ width: '100%' }} placeholder={t.plugin_avatar_input_token_address()} onChange={(e) => onAddressChange(e.target.value)} @@ -152,6 +155,7 @@ export function AddNFT(props: AddNFTProps) {
e.currentTarget.getElementsByTagName('input')[0].focus()} sx={{ width: '100%' }} placeholder={t.plugin_avatar_input_token_id()} onChange={(e) => onTokenIdChange(e.target.value)} diff --git a/packages/plugins/Web3Profile/src/SNSAdaptor/components/ImageManagement.tsx b/packages/plugins/Web3Profile/src/SNSAdaptor/components/ImageManagement.tsx index 69da446dd5e5..380c3f181f16 100644 --- a/packages/plugins/Web3Profile/src/SNSAdaptor/components/ImageManagement.tsx +++ b/packages/plugins/Web3Profile/src/SNSAdaptor/components/ImageManagement.tsx @@ -127,7 +127,7 @@ export function ImageManagement(props: ImageManagementProps) { )) ) : ( - + )} {!hasConnectedWallets && ( diff --git a/packages/plugins/Web3Profile/src/SNSAdaptor/components/Main.tsx b/packages/plugins/Web3Profile/src/SNSAdaptor/components/Main.tsx index bc5e7a4e03ad..528c8fd39ea9 100644 --- a/packages/plugins/Web3Profile/src/SNSAdaptor/components/Main.tsx +++ b/packages/plugins/Web3Profile/src/SNSAdaptor/components/Main.tsx @@ -3,6 +3,9 @@ import type { PersonaInformation } from '@masknet/shared-base' import type { IdentityResolved } from '@masknet/plugin-infra' import type { AccountType } from '../types' import type { CURRENT_STATUS } from '../../constants' +import { Empty } from './Empty' +import { Box } from '@mui/material' +import { useI18N } from '../../locales' interface MainProps { persona?: PersonaInformation openImageSetting: (status: CURRENT_STATUS, accountId: string) => void @@ -10,6 +13,7 @@ interface MainProps { accountList?: AccountType[] } export function Main(props: MainProps) { + const t = useI18N() const { openImageSetting, currentVisitingProfile, accountList } = props return (
@@ -23,7 +27,11 @@ export function Main(props: MainProps) { currentPersona={currentVisitingProfile} isCurrent={account?.identity === currentVisitingProfile?.identifier?.userId?.toLowerCase()} /> - ))} + )) ?? ( + + + + )}
) } diff --git a/packages/plugins/Web3Profile/src/SNSAdaptor/components/PersonaAction.tsx b/packages/plugins/Web3Profile/src/SNSAdaptor/components/PersonaAction.tsx index 556bb8f52be3..972e3ef5751b 100644 --- a/packages/plugins/Web3Profile/src/SNSAdaptor/components/PersonaAction.tsx +++ b/packages/plugins/Web3Profile/src/SNSAdaptor/components/PersonaAction.tsx @@ -1,14 +1,14 @@ -import { useStylesExtends, makeStyles } from '@masknet/theme' -import { Box, Link, Typography } from '@mui/material' -import { Copy } from 'react-feather' +import { useStylesExtends, makeStyles, ShadowRootTooltip } from '@masknet/theme' +import { Box, Typography } from '@mui/material' import { useAsyncRetry, useCopyToClipboard } from 'react-use' import { useI18N } from '../../locales' import { PlatformAvatar } from './PlatformAvatar' -import { useSnackbarCallback } from '@masknet/shared' import { formatPublicKey } from '../utils' import type { PersonaInformation } from '@masknet/shared-base' import type { IdentityResolved } from '@masknet/plugin-infra' import { context } from '../context' +import { CopyIcon } from '@masknet/icons' +import { useCallback, useState } from 'react' const useStyles = makeStyles()((theme) => ({ bottomFixed: { @@ -27,6 +27,7 @@ const useStyles = makeStyles()((theme) => ({ linkIcon: { marginRight: theme.spacing(1), color: theme.palette.maskColor.second, + cursor: 'pointer', }, personaKey: { fontSize: '12px', @@ -45,22 +46,27 @@ export function PersonaAction(props: PersonaActionProps) { const { currentPersona, currentVisitingProfile } = props const t = useI18N() + const [open, setOpen] = useState(false) + const { value: avatar } = useAsyncRetry(async () => { const avatar = await context.getPersonaAvatar(currentPersona?.identifier) if (!avatar) return undefined return avatar }) const [, copyToClipboard] = useCopyToClipboard() - const onCopy = useSnackbarCallback( - async (ev: React.MouseEvent) => { - ev.stopPropagation() + + const onCopy = useCallback( + (e: React.MouseEvent) => { + e.preventDefault() + e.stopPropagation() copyToClipboard(currentPersona?.identifier?.rawPublicKey ?? '') + setOpen(true) + // Close tooltip after five seconds of copying + setTimeout(() => { + setOpen(false) + }, 5000) }, - [], - undefined, - undefined, - undefined, - t.copied(), + [currentPersona?.identifier?.rawPublicKey, copyToClipboard], ) return ( @@ -74,14 +80,15 @@ export function PersonaAction(props: PersonaActionProps) { {currentPersona?.identifier ? formatPublicKey(currentPersona?.identifier?.rawPublicKey) : '--'} - - - + setOpen(false)} + disableFocusListener + disableTouchListener> + +
diff --git a/packages/plugins/Web3Profile/src/SNSAdaptor/components/WalletAssets.tsx b/packages/plugins/Web3Profile/src/SNSAdaptor/components/WalletAssets.tsx index 04a41965fa87..38f839a74630 100644 --- a/packages/plugins/Web3Profile/src/SNSAdaptor/components/WalletAssets.tsx +++ b/packages/plugins/Web3Profile/src/SNSAdaptor/components/WalletAssets.tsx @@ -45,7 +45,7 @@ const useStyles = makeStyles()((theme) => { }, }, linkIcon: { - fill: theme.palette.maskColor.second, + color: theme.palette.maskColor.second, height: 20, width: 20, marginLeft: theme.spacing(0.5), diff --git a/packages/plugins/Web3Profile/src/SNSAdaptor/components/WalletSetting.tsx b/packages/plugins/Web3Profile/src/SNSAdaptor/components/WalletSetting.tsx index fc28d84146cc..7a30bda13bbc 100644 --- a/packages/plugins/Web3Profile/src/SNSAdaptor/components/WalletSetting.tsx +++ b/packages/plugins/Web3Profile/src/SNSAdaptor/components/WalletSetting.tsx @@ -156,7 +156,7 @@ const WalletSetting = memo( -1, ), ) - }, [open]) + }, [open, wallets]) useEffect(() => { if (confirmButtonDisabled) setConfirmButtonDisabled(false) diff --git a/packages/plugins/Web3Profile/src/SNSAdaptor/components/WalletSwitch.tsx b/packages/plugins/Web3Profile/src/SNSAdaptor/components/WalletSwitch.tsx index b1cf6d81d2a1..6443bd11b57e 100644 --- a/packages/plugins/Web3Profile/src/SNSAdaptor/components/WalletSwitch.tsx +++ b/packages/plugins/Web3Profile/src/SNSAdaptor/components/WalletSwitch.tsx @@ -45,7 +45,7 @@ const useStyles = makeStyles()((theme) => ({ alignItems: 'center', }, linkIcon: { - fill: theme.palette.maskColor.second, + color: theme.palette.maskColor.second, height: 15, width: 15, marginTop: '1px', diff --git a/packages/plugins/Web3Profile/src/base.ts b/packages/plugins/Web3Profile/src/base.ts index 20da9ef9a52f..fe2134e1b89f 100644 --- a/packages/plugins/Web3Profile/src/base.ts +++ b/packages/plugins/Web3Profile/src/base.ts @@ -1,4 +1,4 @@ -import type { Plugin } from '@masknet/plugin-infra' +import { CurrentSNSNetwork, Plugin } from '@masknet/plugin-infra' import { PLUGIN_DESCRIPTION, PLUGIN_ID, PLUGIN_NAME } from './constants' import { languages } from './locales/languages' @@ -9,7 +9,14 @@ export const base: Plugin.Shared.Definition = { publisher: { name: { fallback: '' }, link: '' }, enableRequirement: { architecture: { app: false, web: true }, - networks: { type: 'opt-out', networks: {} }, + networks: { + type: 'opt-in', + networks: { + [CurrentSNSNetwork.Twitter]: true, + [CurrentSNSNetwork.Facebook]: false, + [CurrentSNSNetwork.Instagram]: false, + }, + }, target: 'stable', }, i18n: languages, diff --git a/packages/plugins/Web3Profile/src/locales/en-US.json b/packages/plugins/Web3Profile/src/locales/en-US.json index b99a368308a5..f2adcfa8b843 100644 --- a/packages/plugins/Web3Profile/src/locales/en-US.json +++ b/packages/plugins/Web3Profile/src/locales/en-US.json @@ -26,7 +26,8 @@ "copied": "Copied", "listed": "Listed", "unlisted": "Unlisted", - "add_wallet": "No connected wallet, pleae add wallet.", + "add_wallet": "Add Wallet", + "add_wallet_to_connected": "No connected wallet, pleae add wallet.", "open_wallet": "You’ve switched off all wallets. Please go to settings to active.", "tip_persona_sign_success": "Persona signed successfully.", "tip_wallet_sign_error": "Wallet connection failed.", @@ -51,5 +52,6 @@ "no_unlisted_collection": " Click to show your {{collection}} on Web3 profile.", "wallet_setting_hint": "Toggle the button to manage wallet display settings.", "no_authenticated_wallet": "That hasn't been authenticated yet.", - "no_items_found": "No Items found." + "no_items_found": "No Items found.", + "account_empty": "Please verify this persona to set your Web3 profile." } diff --git a/packages/plugins/Web3Profile/src/locales/languages.ts b/packages/plugins/Web3Profile/src/locales/languages.ts index cff1155ab4cb..48c9a26a18f7 100644 --- a/packages/plugins/Web3Profile/src/locales/languages.ts +++ b/packages/plugins/Web3Profile/src/locales/languages.ts @@ -16,8 +16,6 @@ export const languages = { 'zh-CN': zh_CN, zh: zh_TW, } -import { createI18NBundle } from '@masknet/shared-base' -export const add__template__I18N = createI18NBundle('__template__', languages) // @ts-ignore if (import.meta.webpackHot) { // @ts-ignore @@ -27,7 +25,7 @@ if (import.meta.webpackHot) { globalThis.dispatchEvent?.( new CustomEvent('MASK_I18N_HMR', { detail: [ - '__template__', + 'io.mask.web3-profile', { en: en_US, ja: ja_JP, ko: ko_KR, qy: qya_AA, 'zh-CN': zh_CN, zh: zh_TW }, ], }), diff --git a/packages/shared-base/src/Messages/Shared.ts b/packages/shared-base/src/Messages/Shared.ts index 6913ee9a2be4..c4fb159b62b6 100644 --- a/packages/shared-base/src/Messages/Shared.ts +++ b/packages/shared-base/src/Messages/Shared.ts @@ -17,6 +17,5 @@ export const CrossIsolationMessages = new WebExtensionMessage()( color: theme.palette.mode === 'light' ? theme.palette.common.white : theme.palette.common.black, }, arrow: { + marginLeft: '-12px', color: theme.palette.mode === 'light' ? theme.palette.common.black : theme.palette.common.white, }, + firstAreaArrow: { + marginLeft: '12px !important', + }, recommendFeatureApplicationBox: { width: 220, minWidth: 220, @@ -139,7 +143,10 @@ export function ApplicationEntry(props: ApplicationEntryProps) { disablePortal: true, placement: recommendFeature ? 'bottom' : 'top', }} - classes={{ tooltip: classes.tooltip, arrow: classes.arrow }} + classes={{ + tooltip: classes.tooltip, + arrow: classNames(classes.arrow, recommendFeature?.isFirst ? classes.firstAreaArrow : ''), + }} placement={recommendFeature ? 'bottom' : 'top'} arrow disableHoverListener={!tooltipHint} diff --git a/packages/shared/src/UI/components/NFTCard/index.tsx b/packages/shared/src/UI/components/NFTCard/index.tsx index b9303adc712a..241ea284d505 100644 --- a/packages/shared/src/UI/components/NFTCard/index.tsx +++ b/packages/shared/src/UI/components/NFTCard/index.tsx @@ -67,6 +67,7 @@ const useStyles = makeStyles<{ networkPluginID: NetworkPluginID }>()((theme, pro display: 'flex', justifyContent: 'center', alignItems: 'center', + borderRadius: 12, }, image: { width: 126, diff --git a/packages/shared/src/UI/components/NFTList/index.tsx b/packages/shared/src/UI/components/NFTList/index.tsx index 37606c65d08b..9143372abde6 100644 --- a/packages/shared/src/UI/components/NFTList/index.tsx +++ b/packages/shared/src/UI/components/NFTList/index.tsx @@ -2,21 +2,21 @@ import { useChainId, useCurrentWeb3NetworkPluginID, useWeb3State, Web3Helper } f import { ElementAnchor, Linking, NFTCardStyledAssetPlayer, RetryHint } from '@masknet/shared' import { LoadingBase, makeStyles } from '@masknet/theme' import { isSameAddress, NetworkPluginID, NonFungibleToken } from '@masknet/web3-shared-base' -import { formatTokenId } from '@masknet/web3-shared-evm' +import type { ChainId, SchemaType } from '@masknet/web3-shared-evm' import { Checkbox, List, ListItem, Radio, Stack, Typography } from '@mui/material' import classnames from 'classnames' import { noop } from 'lodash-unified' import { FC, useCallback } from 'react' interface NFTItemProps { - token: NonFungibleToken + token: NonFungibleToken } export type NFTKeyPair = [address: string, tokenId: string] interface Props { selectable?: boolean - tokens: Array> + tokens: Array> selectedPairs?: NFTKeyPair[] onChange?: (id: string | null, contractAddress?: string) => void limit?: number @@ -28,7 +28,7 @@ interface Props { hasError?: boolean } -const useStyles = makeStyles<{ columns?: number; gap?: number }>()((theme, { columns, gap }) => { +const useStyles = makeStyles<{ columns?: number; gap?: number }>()((theme, { columns = 4, gap = 12 }) => { const isLight = theme.palette.mode === 'light' return { checkbox: { @@ -37,16 +37,17 @@ const useStyles = makeStyles<{ columns?: number; gap?: number }>()((theme, { col top: 0, }, list: { - gridGap: gap ?? 12, + gridGap: gap, padding: 0, display: 'grid', - gridTemplateColumns: `repeat(${columns ?? 4}, 1fr)`, + gridTemplateColumns: `repeat(${columns}, 1fr)`, }, nftContainer: { background: isLight ? '#EDEFEF' : '#2F3336', borderRadius: 8, width: '100%', transition: 'all 0.2s ease', + overflow: 'auto', '&:hover': { backgroundColor: isLight ? theme.palette.background.paper : undefined, boxShadow: isLight ? '0px 4px 30px rgba(0, 0, 0, 0.1)' : undefined, @@ -122,7 +123,10 @@ const useStyles = makeStyles<{ columns?: number; gap?: number }>()((theme, { col export const NFTItem: FC = ({ token }) => { const { classes } = useStyles({}) - const chainId = useChainId() + const chainId = useChainId(NetworkPluginID.PLUGIN_EVM) + const { Others } = useWeb3State(NetworkPluginID.PLUGIN_EVM) + const fullCaption = token.metadata?.name || token.tokenId + const caption = token.metadata?.name?.match(/#\d+$/) ? token.metadata.name : Others?.formatTokenId(token.tokenId) return (
= ({ token }) => { wrapper: classes.wrapper, }} /> - {formatTokenId(token.tokenId)} + + {caption} +
) } @@ -177,7 +183,7 @@ export const NFTList: FC = ({ } const SelectComponent = isRadio ? Radio : Checkbox - const { Others } = useWeb3State() + const { Others } = useWeb3State(NetworkPluginID.PLUGIN_EVM) return ( <> diff --git a/packages/shared/src/UI/components/TokenIcon/index.tsx b/packages/shared/src/UI/components/TokenIcon/index.tsx index a200c0e49d15..ba40155c6b93 100644 --- a/packages/shared/src/UI/components/TokenIcon/index.tsx +++ b/packages/shared/src/UI/components/TokenIcon/index.tsx @@ -1,17 +1,16 @@ import { memo } from 'react' import { useAsyncRetry } from 'react-use' import { first } from 'lodash-unified' -import { Avatar, AvatarProps } from '@mui/material' +import { Avatar, AvatarProps, useTheme } from '@mui/material' import { makeStyles, useStylesExtends } from '@masknet/theme' import NO_IMAGE_COLOR from './constants' import { useChainId, useWeb3Hub, Web3Helper } from '@masknet/plugin-infra/web3' -import type { NetworkPluginID } from '@masknet/web3-shared-base' +import { NetworkPluginID, TokenType } from '@masknet/web3-shared-base' import { EMPTY_LIST } from '@masknet/shared-base' -import { useImageBase64 } from '../../../hooks/useImageBase64' +import { useAccessibleUrl } from '../../../hooks/useImageBase64' const useStyles = makeStyles()((theme) => ({ icon: { - backgroundColor: theme.palette.common.white, margin: 0, }, })) @@ -23,26 +22,40 @@ export interface TokenIconProps extends withClasses<'icon'> { name?: string logoURL?: string isERC721?: boolean + tokenType?: TokenType + disableDefaultIcon?: boolean AvatarProps?: Partial } export function TokenIcon(props: TokenIconProps) { - const { address, logoURL, name, AvatarProps, classes, isERC721 } = props + const { address, logoURL, name, AvatarProps, classes, tokenType = TokenType.Fungible, disableDefaultIcon } = props const chainId = useChainId(props.pluginID, props.chainId) const hub = useWeb3Hub(props.pluginID) - + const isNFT = tokenType === TokenType.NonFungible const { value } = useAsyncRetry(async () => { - const logoURLs = await hub?.getFungibleTokenIconURLs?.(chainId, address) + const logoURLs = isNFT + ? await hub?.getNonFungibleTokenIconURLs?.(chainId, address) + : await hub?.getFungibleTokenIconURLs?.(chainId, address).catch(() => []) + const key = address ? [chainId, address].join('/') : logoURL return { - key: [chainId, address, logoURL].join('/'), + key, urls: [logoURL, ...(logoURLs ?? [])].filter(Boolean) as string[], } - }, [chainId, address, logoURL, hub]) + }, [chainId, address, isNFT, logoURL, hub]) const { urls = EMPTY_LIST, key } = value ?? {} - const base64 = useImageBase64(key, first(urls)) + const accessibleUrl = useAccessibleUrl(key, first(urls)) + + if (!accessibleUrl && disableDefaultIcon) return null - return + return ( + + ) } export interface TokenIconUIProps extends withClasses<'icon'> { @@ -61,13 +74,17 @@ export const TokenIconUI = memo((props) => { : undefined const classes = useStylesExtends(useStyles(), props) + const theme = useTheme() return ( + {...AvatarProps} + sx={{ + ...AvatarProps?.sx, + backgroundColor: logoURL ? theme.palette.common.white : defaultBackgroundColor, + }}> {name?.slice(0, 1).toUpperCase()} ) diff --git a/packages/shared/src/UI/components/TokenSecurity/CheckSecurityDialog.tsx b/packages/shared/src/UI/components/TokenSecurity/CheckSecurityDialog.tsx index 868420c0fc9d..a47eb982e03d 100644 --- a/packages/shared/src/UI/components/TokenSecurity/CheckSecurityDialog.tsx +++ b/packages/shared/src/UI/components/TokenSecurity/CheckSecurityDialog.tsx @@ -12,13 +12,12 @@ import { useFungibleToken, useFungibleTokenPrice } from '@masknet/plugin-infra/w import { NetworkPluginID } from '@masknet/web3-shared-base' const useStyles = makeStyles()((theme) => ({ - root: { - width: 600, - }, content: { - height: 510, - maxHeight: 510, - padding: 16, + margin: 0, + padding: '0px !important', + '::-webkit-scrollbar': { + display: 'none', + }, }, footer: { boxShadow: @@ -30,7 +29,7 @@ const useStyles = makeStyles()((theme) => ({ }, })) -export interface BuyTokenDialogProps extends withClasses { +export interface BuyTokenDialogProps { open: boolean onClose(): void tokenSecurity: TokenSecurity @@ -53,13 +52,9 @@ export function CheckSecurityDialog(props: BuyTokenDialogProps) { }, [tokenSecurity]) return ( - + - + {loadingToken && (
diff --git a/packages/shared/src/UI/components/TokenSecurity/components/RiskCard.tsx b/packages/shared/src/UI/components/TokenSecurity/components/RiskCard.tsx index 7c3e61369a15..62071b3981d1 100644 --- a/packages/shared/src/UI/components/TokenSecurity/components/RiskCard.tsx +++ b/packages/shared/src/UI/components/TokenSecurity/components/RiskCard.tsx @@ -22,11 +22,14 @@ const useStyles = makeStyles()((theme) => ({ alignItems: 'center', }, header: { - fontSize: 14, + fontSize: 16, + fontWeight: 700, lineHeight: '22px', }, description: { - fontSize: 12, + fontSize: 16, + fontWeight: 400, + color: theme.palette.maskColor.second, }, })) diff --git a/packages/shared/src/UI/components/TokenSecurity/components/SecurityPanel.tsx b/packages/shared/src/UI/components/TokenSecurity/components/SecurityPanel.tsx index 250ea2402b6e..b2f55f3a569b 100644 --- a/packages/shared/src/UI/components/TokenSecurity/components/SecurityPanel.tsx +++ b/packages/shared/src/UI/components/TokenSecurity/components/SecurityPanel.tsx @@ -1,6 +1,5 @@ import { Collapse, Link, Stack, Typography } from '@mui/material' import { useSharedI18N } from '../../../../locales' -import { ExternalLink } from 'react-feather' import { makeStyles } from '@masknet/theme' import { memo, useMemo, useState } from 'react' import { DefineMapping, SecurityMessageLevel, TokenSecurity } from './Common' @@ -14,6 +13,7 @@ import type { ChainId, SchemaType } from '@masknet/web3-shared-evm' import urlcat from 'urlcat' import type { TokenAPI } from '@masknet/web3-providers' import { formatCurrency, FungibleToken } from '@masknet/web3-shared-base' +import { LinkOutIcon } from '@masknet/icons' interface TokenCardProps { tokenSecurity: TokenSecurity @@ -24,33 +24,38 @@ interface TokenCardProps { const useStyles = makeStyles()((theme) => ({ header: { - fontWeight: 500, + fontWeight: 700, fontSize: 18, }, - root: { - width: '600px', - }, detectionCard: { backgroundColor: theme.palette.background.default, }, detectionCollection: { overflowY: 'auto', - '&::-webkit-scrollbar': { - display: 'none', - }, }, icon: { width: '48px', height: '48px', }, tokenName: { - fontSize: '16px', + fontSize: '18px', fontWeight: 700, + color: theme.palette.maskColor.main, }, tokenPrice: { - fontSize: '16px', + fontSize: '18px', fontWeight: 700, - color: theme.palette.text.secondary, + color: theme.palette.maskColor.main, + }, + arrowIcon: { + fontSize: 15, + cursor: 'pointer', + fill: theme.palette.maskColor.second, + }, + linkIcon: { + fill: theme.palette.maskColor.main, + width: 18, + height: 18, }, })) @@ -90,7 +95,7 @@ export const SecurityPanel = memo(({ tokenSecurity, tokenInfo, t }, [riskyFactors, attentionFactors]) return ( - + (({ tokenSecurity, tokenInfo, t - - {t.token_info()} - - setCollapse(!isCollapse)} - sx={{ fontSize: 15, cursor: 'pointer' }} - /> + {t.token_info()} + setCollapse(!isCollapse)} className={classes.arrowIcon} /> @@ -167,7 +167,7 @@ export const SecurityPanel = memo(({ tokenSecurity, tokenInfo, t href={resolveGoLabLink(tokenSecurity.chainId, tokenSecurity.contract)} target="_blank" rel="noopener noreferrer"> - + diff --git a/packages/shared/src/UI/components/TokenSecurity/components/TokenPanel.tsx b/packages/shared/src/UI/components/TokenSecurity/components/TokenPanel.tsx index d5817705696d..b9aef2cc6deb 100644 --- a/packages/shared/src/UI/components/TokenSecurity/components/TokenPanel.tsx +++ b/packages/shared/src/UI/components/TokenSecurity/components/TokenPanel.tsx @@ -6,7 +6,8 @@ import { useTheme } from '@mui/system' import { ExternalLink } from 'react-feather' import { makeStyles, usePortalShadowRoot } from '@masknet/theme' import { explorerResolver, formatEthereumAddress } from '@masknet/web3-shared-evm' -import { formatCurrency } from '@masknet/web3-shared-base' +import { formatCurrency, formatSupply } from '@masknet/web3-shared-base' +import { LinkOutIcon } from '@masknet/icons' const useStyles = makeStyles()((theme) => ({ card: { @@ -18,18 +19,23 @@ const useStyles = makeStyles()((theme) => ({ : '0px 0px 20px rgba(255, 255, 255, 0.12)', }, subtitle: { - color: theme.palette.text.secondary, + color: theme.palette.maskColor.second, fontWeight: 400, fontSize: 16, }, cardValue: { - color: theme.palette.text.primary, + color: theme.palette.maskColor.main, fontSize: 16, fontWeight: 700, }, tooltip: { fontSize: 12, }, + linkIcon: { + fill: theme.palette.maskColor.main, + width: 18, + height: 18, + }, })) const DEFAULT_PLACEHOLDER = '--' @@ -50,16 +56,17 @@ export const TokenPanel = React.forwardRef(({ tokenSecurity, tokenMarketCap }: T const theme = useTheme() const totalSupply = usePortalShadowRoot((container) => { + const supply = tokenSecurity.total_supply ? formatSupply(tokenSecurity.total_supply) : DEFAULT_PLACEHOLDER return ( theme.palette.info.contrastText} className={classes.tooltip}> - {tokenSecurity.total_supply} + {supply} }> - {formatTotalSupply(tokenSecurity.total_supply)} + {supply} ) }) @@ -87,7 +94,7 @@ export const TokenPanel = React.forwardRef(({ tokenSecurity, tokenMarketCap }: T href={explorerResolver.fungibleTokenLink(tokenSecurity.chainId, tokenSecurity.contract)} target="_blank" rel="noopener noreferrer"> - + @@ -108,7 +115,7 @@ export const TokenPanel = React.forwardRef(({ tokenSecurity, tokenMarketCap }: T )} target="_blank" rel="noopener noreferrer"> - + )} @@ -139,8 +146,7 @@ export const TokenPanel = React.forwardRef(({ tokenSecurity, tokenMarketCap }: T {t.token_info_market_cap()} - {' '} - {tokenMarketCap ? formatCurrency(tokenMarketCap) : DEFAULT_PLACEHOLDER}{' '} + {tokenMarketCap ? `$${formatSupply(tokenMarketCap)}` : DEFAULT_PLACEHOLDER} diff --git a/packages/shared/src/contexts/components/SelectFungibleTokenDialog.tsx b/packages/shared/src/contexts/components/SelectFungibleTokenDialog.tsx index 3c12d01f9382..b580e4363ba3 100644 --- a/packages/shared/src/contexts/components/SelectFungibleTokenDialog.tsx +++ b/packages/shared/src/contexts/components/SelectFungibleTokenDialog.tsx @@ -19,6 +19,9 @@ const useStyles = makeStyles()((theme, { compact, disablePaddingTop ...(compact ? { minWidth: 552 } : {}), padding: theme.spacing(3), paddingTop: disablePaddingTop ? 0 : theme.spacing(2.8), + display: 'flex', + flexDirection: 'column', + overflow: 'auto', }, list: { scrollbarWidth: 'none', diff --git a/packages/shared/src/hooks/useImageBase64.ts b/packages/shared/src/hooks/useImageBase64.ts index 609dbe819606..20db584af4bc 100644 --- a/packages/shared/src/hooks/useImageBase64.ts +++ b/packages/shared/src/hooks/useImageBase64.ts @@ -1,4 +1,5 @@ import { useState } from 'react' +import LRUCache from 'lru-cache' import { useAsyncRetry } from 'react-use' function readAsDataURL(blob: Blob) { @@ -10,35 +11,53 @@ function readAsDataURL(blob: Blob) { }) } -const cache = new Map() +const cache = new LRUCache>({ + max: 500, + ttl: 300_000, +}) +const responseToBase64 = async (response: Response) => { + const blob = await response.blob() + const dataURL = await readAsDataURL(blob) + return dataURL +} -export function useImageBase64( - key?: string, +export function useAccessibleUrl( + key = '', url?: string, options?: { fetch: typeof globalThis.fetch }, ) { const fetch = options?.fetch ?? globalThis.fetch - const [base64, setBase64] = useState(cache.get(key ?? '') ?? '') + const [availableUrl, setAvailableUrl] = useState(() => { + const hit = cache.get(key) + return typeof hit === 'string' ? hit : '' + }) useAsyncRetry(async () => { if (!key) return const hit = cache.get(key) - if (hit) { - setBase64(hit) + if (typeof hit === 'string') { + setAvailableUrl(hit) + return + } else if (hit instanceof Promise) { + setAvailableUrl(await responseToBase64((await hit).clone())) return } if (!url || !fetch) return - const response = await fetch(`https://cors.r2d2.to/?${url}`) - if (!response) return + const fetchingTask = fetch(`https://cors.r2d2.to/?${url}`) + cache.set(key, fetchingTask) + const response = await fetchingTask + if (!response.ok) { + cache.delete(key) + return + } - const blob = await response.blob() - const dataURL = await readAsDataURL(blob) + const dataURL = await responseToBase64(response) cache.set(key, dataURL) - setBase64(dataURL) + setAvailableUrl(dataURL) }, [key, url]) - return base64 + return availableUrl } diff --git a/packages/theme/src/Components/SearchableList/SearchableList.tsx b/packages/theme/src/Components/SearchableList/SearchableList.tsx index 28f642d090dd..9108373db0d9 100644 --- a/packages/theme/src/Components/SearchableList/SearchableList.tsx +++ b/packages/theme/src/Components/SearchableList/SearchableList.tsx @@ -136,7 +136,9 @@ export function SearchableList({ ) } const useStyles = makeStyles()((theme) => ({ - container: {}, + container: { + overflow: 'hidden', + }, list: { marginTop: theme.spacing(1.5), '& > div::-webkit-scrollbar': { diff --git a/packages/theme/src/Components/Snackbar/index.tsx b/packages/theme/src/Components/Snackbar/index.tsx index df9fcdedf173..2ec07f0ba924 100644 --- a/packages/theme/src/Components/Snackbar/index.tsx +++ b/packages/theme/src/Components/Snackbar/index.tsx @@ -11,7 +11,7 @@ import { SnackbarAction, OptionsObject, } from 'notistack' -import { Typography, IconButton } from '@mui/material' +import { Typography, IconButton, alpha } from '@mui/material' import classnames from 'classnames' import { Close as CloseIcon } from '@mui/icons-material' import WarningIcon from '@mui/icons-material/Warning' @@ -64,15 +64,15 @@ to { }, } const success = { - backgroundColor: MaskColorVar.greenMain, + backgroundColor: theme.palette.maskColor.success, color: '#ffffff', [`& .${refs.title}`]: { color: 'inherit', }, [`& .${refs.message}`]: { - color: MaskColorVar.normalTextLight, + color: alpha(theme.palette.maskColor.white, 0.8), '& svg': { - color: MaskColorVar.white, + color: theme.palette.maskColor.white, }, }, } as const diff --git a/packages/theme/src/Components/Tabs/index.tsx b/packages/theme/src/Components/Tabs/index.tsx index 07bcbdf88252..7568459c66ae 100644 --- a/packages/theme/src/Components/Tabs/index.tsx +++ b/packages/theme/src/Components/Tabs/index.tsx @@ -2,7 +2,6 @@ import { Box, Button, ButtonGroup, ButtonGroupProps, styled, Tab } from '@mui/ma import { useTabContext, getPanelId, getTabId } from '@mui/lab/TabContext' import { forwardRef, - cloneElement, Children, isValidElement, useState, @@ -63,6 +62,7 @@ const FlexibleButtonGroupPanel = styled(Box, { : 'none', backdropFilter: 'blur(20px)', background: theme.palette.mode === 'dark' ? 'rgba(0, 0, 0, 0.8)' : 'rgba(255, 255, 255, 0.8)', + boxSizing: 'content-box', })) const ButtonGroupWrap = styled(ButtonGroup, { @@ -176,11 +176,6 @@ export const MaskTabList = forwardRef((props, 'aria-controls': getPanelId(context, child.props.value), id: getTabId(context, child.props.value), selected: child.props.value === context.value, - // if move tab to first in flexible tabs - isVisitable: (top: number, right: number) => { - const anchor = anchorRef.current?.getBoundingClientRect() - return right <= (anchor?.right ?? 0) - defaultTabSize && top - (anchor?.top ?? 0) < defaultTabSize - }, onChange: (event: object, value: string, visitable?: boolean) => { handleToggle(false) props.onChange(event, value) @@ -190,16 +185,23 @@ export const MaskTabList = forwardRef((props, }, } - if (child.type === Tab) { - const C = tabMapping[variant] - return ( - - {child.props.label} - - ) - } + if (child.type !== Tab) return child - return cloneElement(child, extra) + if (variant === 'flexible') { + Object.assign(extra, { + // if move tab to first in flexible tabs + isVisitable: (top: number, right: number) => { + const anchor = anchorRef.current?.getBoundingClientRect() + return right <= (anchor?.right ?? 0) - defaultTabSize && top - (anchor?.top ?? 0) < defaultTabSize + }, + }) + } + const C = tabMapping[variant] + return ( + + {child.props.label} + + ) }) // #region hide tab should up to first when chick diff --git a/packages/theme/src/Components/TextField/index.tsx b/packages/theme/src/Components/TextField/index.tsx index 83bbd4cf47f5..907c22c31b80 100644 --- a/packages/theme/src/Components/TextField/index.tsx +++ b/packages/theme/src/Components/TextField/index.tsx @@ -2,6 +2,9 @@ import { ForwardedRef, forwardRef } from 'react' import { Box, formHelperTextClasses, TextField, StandardTextFieldProps, InputProps, Typography } from '@mui/material' import { makeStyles } from '../../UIHelper/makeStyles' import { getMaskColor, MaskColorVar } from '../../CSSVariables/vars' +import { isDashboardPage } from '@masknet/shared-base' + +const isDashboard = isDashboardPage() const useStyles = makeStyles()((theme) => ({ label: { @@ -27,12 +30,16 @@ const useStyles = makeStyles()((theme) => ({ lineHeight: '16px', }, '& input::-webkit-input-placeholder': { - color: theme.palette.maskColor.second, + color: !isDashboard ? theme.palette.maskColor.second : undefined, }, }, input: { padding: theme.spacing(1), - background: theme.palette.maskColor.input, + background: !isDashboard + ? theme.palette.maskColor.input + : theme.palette.mode === 'dark' + ? '#2B2E4C' + : '#F6F6F8', fontSize: 13, lineHeight: '16px', borderRadius: 6, @@ -46,7 +53,7 @@ const useStyles = makeStyles()((theme) => ({ color: 'rgba(255, 255, 255, 0.4)', }, inputFocused: { - backgroundColor: theme.palette.maskColor.input, + backgroundColor: !isDashboard ? theme.palette.maskColor.input : MaskColorVar.primaryBackground, boxShadow: `0 0 0 2px ${theme.palette.mode === 'dark' ? '#4F5378' : 'rgba(28, 104, 243, 0.2)'}`, }, })) diff --git a/packages/web3-constants/evm/trader.json b/packages/web3-constants/evm/trader.json index b50faa4f5d1e..440bbe3e7d09 100644 --- a/packages/web3-constants/evm/trader.json +++ b/packages/web3-constants/evm/trader.json @@ -561,8 +561,8 @@ "BSCT": "", "Matic": "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", "Mumbai": "", - "Arbitrum": "", - "Arbitrum_Rinkeby": "", + "Arbitrum": "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", + "Arbitrum_Rinkeby": "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", "xDai": "", "Avalanche": "", "Avalanche_Fuji": "", diff --git a/packages/web3-providers/src/CoinMarketCap/index.ts b/packages/web3-providers/src/CoinMarketCap/index.ts index a28232ea527f..ab014be513c7 100644 --- a/packages/web3-providers/src/CoinMarketCap/index.ts +++ b/packages/web3-providers/src/CoinMarketCap/index.ts @@ -1,5 +1,6 @@ import getUnixTime from 'date-fns/getUnixTime' -import { TrendingAPI, TrendingCoinType } from '../types' +import type { TrendingAPI } from '../types' +import { TokenType } from '@masknet/web3-shared-base' import type { ChainId } from '@masknet/web3-shared-evm' import { BTC_FIRST_LEGER_DATE, CMC_STATIC_BASE_URL, CMC_V1_BASE_URL, THIRD_PARTY_V1_BASE_URL } from './constants' import { getCommunityLink, isMirroredKeyword, resolveChainIdByName } from './helper' @@ -222,17 +223,16 @@ export class CoinMarketCapAPI implements TrendingAPI.Provider { `${CMC_V1_BASE_URL}/cryptocurrency/map?aux=status,platform&listing_status=active,untracked&sort=cmc_rank`, { cache: 'force-cache' }, ) - return (response.data ?? []) + if (!response.data) return [] + return response.data .filter((x) => x.status === 'active') - .map( - (x) => - ({ - id: String(x.id), - name: x.name, - symbol: x.symbol, - contract_address: x.platform?.name === 'Ethereum' ? x.platform.token_address : undefined, - } as TrendingAPI.Coin), - ) + .map((x) => ({ + id: String(x.id), + name: x.name, + symbol: x.symbol, + type: TokenType.Fungible, + contract_address: x.platform?.name === 'Ethereum' ? x.platform.token_address : undefined, + })) } async getHistorical( id: string, @@ -284,7 +284,7 @@ export class CoinMarketCapAPI implements TrendingAPI.Provider { id, name: coinInfo.name, symbol: coinInfo.symbol, - type: TrendingCoinType.Fungible, + type: TokenType.Fungible, is_mirrored: isMirroredKeyword(coinInfo.symbol), announcement_urls: coinInfo.urls.announcement?.filter(Boolean), tech_docs_urls: coinInfo.urls.technical_doc?.filter(Boolean), diff --git a/packages/web3-providers/src/NFTScan/index.ts b/packages/web3-providers/src/NFTScan/index.ts index 2e0d375905e7..96c68def6c2a 100644 --- a/packages/web3-providers/src/NFTScan/index.ts +++ b/packages/web3-providers/src/NFTScan/index.ts @@ -21,7 +21,7 @@ import type { AbiItem } from 'web3-utils' import { courier } from '../helpers' import { LooksRare, OpenSea } from '../index' import { LooksRareLogo, OpenSeaLogo } from '../resources' -import { NonFungibleMarketplace, NonFungibleTokenAPI, TrendingAPI, TrendingCoinType } from '../types' +import { NonFungibleMarketplace, NonFungibleTokenAPI, TrendingAPI } from '../types' import { NFTSCAN_ACCESS_TOKEN_URL, NFTSCAN_BASE, NFTSCAN_BASE_API, NFTSCAN_LOGO_BASE } from './constants' import type { NFTPlatformInfo, @@ -218,7 +218,7 @@ export class NFTScanAPI implements NonFungibleTokenAPI.Provider { const contractAddress = asset.contract?.address - const tokenId = Number.parseInt(asset.id?.tokenId, 16).toString() - + let tokenId = asset.id.tokenId + if (isHex(asset.id.tokenId)) { + tokenId = hexToNumberString(asset.id.tokenId) + } return { id: `${contractAddress}_${tokenId}`, chainId, @@ -198,6 +201,10 @@ function createNFTAsset_EVM( contractMetadataResponse?: AlchemyResponse_EVM_Contact_Metadata, ownersResponse?: AlchemyResponse_EVM_Owners, ): NonFungibleAsset { + let tokenId = metaDataResponse.id.tokenId + if (isHex(metaDataResponse.id.tokenId)) { + tokenId = hexToNumberString(metaDataResponse.id.tokenId) + } return { id: metaDataResponse.contract?.address, chainId, @@ -206,7 +213,7 @@ function createNFTAsset_EVM( metaDataResponse?.id?.tokenMetadata?.tokenType === 'ERC721' ? SchemaType_EVM.ERC721 : SchemaType_EVM.ERC1155, - tokenId: metaDataResponse.id?.tokenId, + tokenId, address: metaDataResponse.contract?.address, metadata: { chainId, @@ -252,12 +259,16 @@ function createNftToken_FLOW( chainId: ChainId_FLOW, asset: AlchemyNFT_FLOW, ): NonFungibleAsset { + let tokenId = asset.id.tokenId + if (isHex(asset.id.tokenId)) { + tokenId = hexToNumberString(asset.id.tokenId) + } return { id: asset.contract?.address, chainId, type: TokenType.NonFungible, schema: SchemaType_FLOW.NonFungible, - tokenId: Number.parseInt(asset.id?.tokenId, 16).toString(), + tokenId, address: asset.contract?.address, metadata: { chainId, @@ -295,12 +306,16 @@ function createNFTAsset_FLOW( ownerAddress: string, metaDataResponse: AlchemyResponse_FLOW_Metadata, ): NonFungibleAsset { + let tokenId = metaDataResponse.id.tokenId + if (isHex(metaDataResponse.id.tokenId)) { + tokenId = hexToNumberString(metaDataResponse.id.tokenId) + } return { id: metaDataResponse.contract?.address, chainId, type: TokenType.NonFungible, schema: SchemaType_FLOW.NonFungible, - tokenId: Number.parseInt(metaDataResponse.id?.tokenId, 16).toString(), + tokenId, address: metaDataResponse.contract?.address, metadata: { chainId, diff --git a/packages/web3-providers/src/coingecko/base-api.ts b/packages/web3-providers/src/coingecko/base-api.ts index bcf3cbf54fd5..3ecaecc79d8a 100644 --- a/packages/web3-providers/src/coingecko/base-api.ts +++ b/packages/web3-providers/src/coingecko/base-api.ts @@ -103,7 +103,7 @@ export interface CoinInfo { } export async function getCoinInfo(coinId: string) { - return fetchJSON( + return fetchJSON( `${COINGECKO_URL_BASE}/coins/${coinId}?developer_data=false&community_data=false&tickers=true`, { cache: getTraderAllAPICachedFlag() }, ) diff --git a/packages/web3-providers/src/coingecko/index.ts b/packages/web3-providers/src/coingecko/index.ts index cc99f155024c..3b0c66b9a69c 100644 --- a/packages/web3-providers/src/coingecko/index.ts +++ b/packages/web3-providers/src/coingecko/index.ts @@ -1,11 +1,11 @@ import { DataProvider } from '@masknet/public-api' -import { CurrencyType, Price } from '@masknet/web3-shared-base' +import { CurrencyType, Price, TokenType } from '@masknet/web3-shared-base' import type { ChainId } from '@masknet/web3-shared-evm' import urlcat from 'urlcat' -import { uniq } from 'lodash-unified' +import { uniq, uniqBy } from 'lodash-unified' import { getCommunityLink, isMirroredKeyword, resolveChainId, resolveCoinAddress } from '../CoinMarketCap/helper' import { fetchJSON } from '../helpers' -import { PriceAPI, TrendingAPI, TrendingCoinType } from '../types' +import type { PriceAPI, TrendingAPI } from '../types' import { getAllCoins, getAllCurrencies, getCoinInfo, getPriceStats as getStats } from './base-api' import { COINGECKO_URL_BASE } from './constants' import { resolveChain } from './helper' @@ -53,11 +53,13 @@ export class CoinGeckoAPI implements PriceAPI.Provider, TrendingAPI.Provider { const info = await getCoinInfo(id) + if ('error' in info) throw new Error(info.error) + const platform_url = `https://www.coingecko.com/en/coins/${info.id}` const twitter_url = info.links.twitter_screen_name ? `https://twitter.com/${info.links.twitter_screen_name}` : '' - const facebook_url = info.links.facebook_username ? `https://t.me/${info.links.facebook_username}` : '' + const facebook_url = info.links.facebook_username ? `https://facebook.com/${info.links.facebook_username}` : '' const telegram_url = info.links.telegram_channel_identifier ? `https://t.me/${info.links.telegram_channel_identifier}` : '' @@ -76,7 +78,7 @@ export class CoinGeckoAPI implements PriceAPI.Provider, TrendingAPI.Provider x.toLowerCase(), + ), ), source_code_urls: Object.values(info.links.repos_url).flatMap((x) => x), home_urls: info.links.homepage.filter(Boolean), @@ -136,8 +141,9 @@ export class CoinGeckoAPI implements PriceAPI.Provider, TrendingAPI.Provider { - return getAllCoins() + async getCoins(): Promise { + const coins = await getAllCoins() + return coins.map((coin) => ({ ...coin, type: TokenType.Fungible })) } async getCurrencies(): Promise { diff --git a/packages/web3-providers/src/debank/index.ts b/packages/web3-providers/src/debank/index.ts index 33f5f2e85d31..c0e63f2ab71b 100644 --- a/packages/web3-providers/src/debank/index.ts +++ b/packages/web3-providers/src/debank/index.ts @@ -85,11 +85,17 @@ export class DeBankAPI // rename bsc to bnb id: x.id === 'bsc' ? 'bnb' : x.id, chain: x.chain === 'bsc' ? 'bnb' : x.chain, + // prefix ARETH + symbol: x.chain === 'arb' && x.symbol === 'ETH' ? 'ARETH' : x.symbol, + logo_url: + x.chain === 'arb' && x.symbol === 'ETH' + ? 'https://assets.debank.com/static/media/arbitrum.8e326f58.svg' + : x.logo_url, })), options?.chainId, ), getAllEVMNativeAssets(), - (a, z) => a.symbol === z.symbol, + (a, z) => a.symbol === z.symbol && a.chainId === z.chainId, ), createIndicator(options?.indicator), ) diff --git a/packages/web3-providers/src/opensea/index.ts b/packages/web3-providers/src/opensea/index.ts index 5794f4d74f5d..6d89e08d5ba2 100644 --- a/packages/web3-providers/src/opensea/index.ts +++ b/packages/web3-providers/src/opensea/index.ts @@ -19,6 +19,7 @@ import { createIndicator, createNextIndicator, NonFungibleAsset, + formatBalance, } from '@masknet/web3-shared-base' import { ChainId, SchemaType, createNativeToken, createERC20Token } from '@masknet/web3-shared-evm' import type { NonFungibleTokenAPI, TrendingAPI } from '../types' @@ -169,6 +170,18 @@ function createNFTAsset(chainId: ChainId, asset: OpenSeaResponse): NonFungibleAs asset.last_sale?.payment_token.decimals, )?.toString(), }, + priceInToken: { + token: createTokenDetailed(chainId, { + address: asset.last_sale?.payment_token.address ?? '', + decimals: Number(asset.last_sale?.payment_token.decimals ?? '0'), + name: '', + symbol: asset.last_sale?.payment_token.symbol ?? '', + }), + amount: formatBalance( + new BigNumber(asset.last_sale?.total_price ?? '0'), + asset.last_sale?.payment_token.decimals, + ), + }, orders: asset.orders ?.sort((a, z) => new BigNumber(getOrderUSDPrice(z.current_price, z.payment_token_contract?.usd_price) ?? 0) diff --git a/packages/web3-providers/src/opensea/types.ts b/packages/web3-providers/src/opensea/types.ts index 5a32c0d2484a..b42f69ddc804 100644 --- a/packages/web3-providers/src/opensea/types.ts +++ b/packages/web3-providers/src/opensea/types.ts @@ -61,6 +61,7 @@ export interface AssetEvent { decimals: number symbol: string usd_price: string + address: string } quantity: string } diff --git a/packages/web3-providers/src/twitter/index.ts b/packages/web3-providers/src/twitter/index.ts index f589576f5a7a..c0b8e02ec7c5 100644 --- a/packages/web3-providers/src/twitter/index.ts +++ b/packages/web3-providers/src/twitter/index.ts @@ -1,5 +1,6 @@ import { escapeRegExp } from 'lodash-unified' import urlcat from 'urlcat' +import LRUCache from 'lru-cache' import type { TwitterBaseAPI } from '../types' const UPLOAD_AVATAR_URL = 'https://upload.twitter.com/i/media/upload.json' @@ -117,6 +118,11 @@ async function getSettings(bearerToken: string, csrfToken: string): Promise({ + max: 20, + ttl: 300_000, +}) + export class TwitterAPI implements TwitterBaseAPI.Provider { async getSettings() { const { bearerToken, queryToken, csrfToken } = await getTokens() @@ -197,7 +203,7 @@ export class TwitterAPI implements TwitterBaseAPI.Provider { } } - async getUserByScreenName(screenName: string): Promise { + async getUserByScreenName(screenName: string): Promise { const { bearerToken, csrfToken, queryId } = await getTokens('UserByScreenName') const url = urlcat('https://twitter.com/i/api/graphql/:queryId/UserByScreenName', { queryId, @@ -207,17 +213,26 @@ export class TwitterAPI implements TwitterBaseAPI.Provider { withSuperFollowsUserFields: true, }), }) + const cacheKey = `${bearerToken}/${csrfToken}/${url}` + const fetchingTask: Promise = + cache.get(cacheKey) ?? + fetch(url, { + headers: { + authorization: `Bearer ${bearerToken}`, + 'x-csrf-token': csrfToken, + 'content-type': 'application/json', + 'x-twitter-auth-type': 'OAuth2Session', + 'x-twitter-active-user': 'yes', + referer: `https://twitter.com/${screenName}`, + }, + }) - const response = await fetch(url, { - headers: { - authorization: `Bearer ${bearerToken}`, - 'x-csrf-token': csrfToken, - 'content-type': 'application/json', - 'x-twitter-auth-type': 'OAuth2Session', - 'x-twitter-active-user': 'yes', - referer: `https://twitter.com/${screenName}`, - }, - }) + cache.set(cacheKey, fetchingTask) + const response = (await fetchingTask).clone() + if (!response.ok) { + cache.delete(cacheKey) + return null + } const userResponse: TwitterBaseAPI.UserByScreenNameResponse = await response.json() return userResponse.data.user.result } diff --git a/packages/web3-providers/src/types.ts b/packages/web3-providers/src/types.ts index a8150e067e8b..0228bc21d020 100644 --- a/packages/web3-providers/src/types.ts +++ b/packages/web3-providers/src/types.ts @@ -25,6 +25,7 @@ import type { GasOptionType, HubOptions, HubIndicator, + TokenType, } from '@masknet/web3-shared-base' import type { DataProvider } from '@masknet/public-api' import type { ChainId } from '@masknet/web3-shared-evm' @@ -531,7 +532,7 @@ export namespace TwitterBaseAPI { > uploadUserAvatar: (screenName: string, image: Blob | File) => Promise updateProfileImage: (screenName: string, media_id_str: string) => Promise - getUserByScreenName: (screenName: string) => Promise + getUserByScreenName: (screenName: string) => Promise } } @@ -608,10 +609,6 @@ export namespace TokenAPI { } } -export enum TrendingCoinType { - Fungible = 1, - NonFungible = 2, -} export enum NonFungibleMarketplace { OpenSea = 'OpenSea', LooksRare = 'LooksRare', @@ -658,7 +655,7 @@ export namespace TrendingAPI { chainId?: ChainId name: string symbol: string - type: TrendingCoinType + type: TokenType decimals?: number is_mirrored?: boolean platform_url?: string diff --git a/packages/web3-providers/src/uniswap/base-api.ts b/packages/web3-providers/src/uniswap/base-api.ts index 011441db0169..5d5d41af9a86 100644 --- a/packages/web3-providers/src/uniswap/base-api.ts +++ b/packages/web3-providers/src/uniswap/base-api.ts @@ -16,9 +16,9 @@ import { fetchBlockNumbersObjectByTimestamps, } from './blocks' import { fetchLatestBlocks } from './uniswap-health' -import { isGreaterThan, isLessThanOrEqualTo } from '@masknet/web3-shared-base' +import { isGreaterThan, isLessThanOrEqualTo, TokenType } from '@masknet/web3-shared-base' import type { ChainId } from '@masknet/web3-shared-evm' -import { TrendingAPI, TrendingCoinType } from '../types' +import type { TrendingAPI } from '../types' type Value = string | number | BigNumber | undefined @@ -118,7 +118,7 @@ export async function getAllCoinsByKeyword(chainId: ChainId, keyword: string): P id: '0x69af81e73a73b40adf4f3d4223cd9b1ece623074', name: 'Mask Network', symbol: 'MASK', - type: TrendingCoinType.Fungible, + type: TokenType.Fungible, contract_address: '0x69af81e73a73b40adf4f3d4223cd9b1ece623074', }, ] @@ -128,7 +128,7 @@ export async function getAllCoinsByKeyword(chainId: ChainId, keyword: string): P const coins: TrendingAPI.Coin[] = tokens.map((x) => ({ ...x, - type: TrendingCoinType.Fungible, + type: TokenType.Fungible, address: x.id, contract_address: x.id, })) @@ -140,7 +140,7 @@ export async function getAllCoinsByKeyword(chainId: ChainId, keyword: string): P name: 'ETHer (Wrapped)', contract_address: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', symbol: 'eth', - type: TrendingCoinType.Fungible, + type: TokenType.Fungible, decimals: 18, }) } else if (keyword === 'nrge') { @@ -150,7 +150,7 @@ export async function getAllCoinsByKeyword(chainId: ChainId, keyword: string): P name: 'Energi', contract_address: '0x1416946162b1c2c871a73b07e932d2fb6c932069', symbol: 'NRGT', - type: TrendingCoinType.Fungible, + type: TokenType.Fungible, decimals: 18, }) } diff --git a/packages/web3-providers/src/uniswap/index.ts b/packages/web3-providers/src/uniswap/index.ts index 246bd6de1ef1..5ce1e7769f2b 100644 --- a/packages/web3-providers/src/uniswap/index.ts +++ b/packages/web3-providers/src/uniswap/index.ts @@ -1,7 +1,8 @@ import { DataProvider } from '@masknet/public-api' +import { TokenType } from '@masknet/web3-shared-base' import type { ChainId } from '@masknet/web3-shared-evm' import { isMirroredKeyword } from '../CoinMarketCap/helper' -import { TrendingAPI, TrendingCoinType } from '../types' +import type { TrendingAPI } from '../types' import * as BaseAPI from './base-api' import { BTC_FIRST_LEGER_DATE, getAllCoinsByKeyword, getPriceStats as getStats } from './base-api' @@ -46,9 +47,10 @@ export class UniSwapAPI implements TrendingAPI.Provider { market: marketInfo, coin: { id, + chainId, name: token?.name || '', symbol: token?.symbol || '', - type: TrendingCoinType.Fungible, + type: TokenType.Fungible, decimals: Number(token?.decimals || '0'), is_mirrored: isMirroredKeyword(token?.symbol || ''), blockchain_urls: [`https://info.uniswap.org/token/${id}`, `https://etherscan.io/address/${id}`], diff --git a/packages/web3-shared/base/src/specs/index.ts b/packages/web3-shared/base/src/specs/index.ts index 2181e27b3da8..cb7ac40e8a5d 100644 --- a/packages/web3-shared/base/src/specs/index.ts +++ b/packages/web3-shared/base/src/specs/index.ts @@ -66,7 +66,7 @@ export enum SourceType { LooksRare = 'looksrare', NFTScan = 'NFTScan', Alchemy_EVM = 'Alchemy_EVM', - Alchemy_FLOW = 'Alchemy_FLOW' + Alchemy_FLOW = 'Alchemy_FLOW', } export enum TransactionStatusType { @@ -232,6 +232,7 @@ export interface NonFungibleTokenContract { export interface NonFungibleTokenMetadata { chainId: ChainId + /** Might be the format `TheName #42` */ name: string symbol: string description?: string @@ -354,6 +355,11 @@ export interface FungibleAsset extends FungibleToken { + amount: string + token: FungibleToken +} + /** * A non-fungible token but with more metadata */ @@ -373,6 +379,7 @@ export interface NonFungibleAsset extends NonFungibleToken< orders?: Array> events?: Array> paymentTokens?: Array> + priceInToken?:PriceInToken } /** @@ -387,7 +394,7 @@ export interface FungibleTokenAuthorization { /** * Authorization about a non-fungible token. */ - export interface NonFungibleTokenAuthorization { +export interface NonFungibleTokenAuthorization { all: boolean recipient: string tokens: NonFungibleToken @@ -552,7 +559,6 @@ export interface Account { chainId: ChainId } - export interface BalanceEvent { /** Emit if the balance of the account updated. */ update: [Account] @@ -682,7 +688,12 @@ export interface Connection< /** Get fungible token balance */ getFungibleTokenBalance(address: string, initial?: Web3ConnectionOptions): Promise /** Get non-fungible token balance */ - getNonFungibleTokenBalance(address: string, tokenId?: string, schema?: SchemaType, initial?: Web3ConnectionOptions): Promise + getNonFungibleTokenBalance( + address: string, + tokenId?: string, + schema?: SchemaType, + initial?: Web3ConnectionOptions, + ): Promise /** Get fungible token balance */ getFungibleTokensBalance(listOfAddress: string[], initial?: Web3ConnectionOptions): Promise> /** Get non-fungible token balance */ @@ -728,7 +739,7 @@ export interface Connection< address: string, recipient: string, amount: string, - initial?: Web3ConnectionOptions + initial?: Web3ConnectionOptions, ): Promise /** Approve a recipient for using a non-fungible token. */ approveNonFungibleToken( @@ -736,7 +747,7 @@ export interface Connection< recipient: string, tokenId: string, schema?: SchemaType, - initial?: Web3ConnectionOptions + initial?: Web3ConnectionOptions, ): Promise /** Approve a recipient for using all non-fungible tokens. */ approveAllNonFungibleTokens( @@ -744,7 +755,7 @@ export interface Connection< recipient: string, approved: boolean, schema?: SchemaType, - initial?: Web3ConnectionOptions + initial?: Web3ConnectionOptions, ): Promise /** Transfer fungible token to */ transferFungibleToken( @@ -771,6 +782,12 @@ export interface Connection< callTransaction(transaction: Transaction, initial?: Web3ConnectionOptions): Promise /** Send a transaction and wait for mining */ sendTransaction(transaction: Transaction, initial?: Web3ConnectionOptions): Promise + /** Estimate a transaction */ + estimateTransaction?: ( + transaction: Transaction, + fallback?: number, + initial?: Web3ConnectionOptions, + ) => Promise /** Send a signed transaction */ sendSignedTransaction(signature: TransactionSignature, initial?: Web3ConnectionOptions): Promise /** Build connection */ @@ -817,9 +834,17 @@ export interface Hub Promise>> /** Get security diagnosis about a fungible token */ - getFungibleTokenSecurity?: (chainId: ChainId, address: string, initial?: Web3HubOptions) => Promise + getFungibleTokenSecurity?: ( + chainId: ChainId, + address: string, + initial?: Web3HubOptions, + ) => Promise /** Get security diagnosis about a non-fungible token */ - getNonFungibleTokenSecurity?: (chainId: ChainId, address: string, initial?: Web3HubOptions) => Promise + getNonFungibleTokenSecurity?: ( + chainId: ChainId, + address: string, + initial?: Web3HubOptions, + ) => Promise /** Get the fungible from built-in token list */ getFungibleTokensFromTokenList?: ( chainId: ChainId, @@ -897,7 +922,7 @@ export interface Hub Promise>> } @@ -1025,7 +1050,12 @@ export interface TransactionWatcherState { /** Notify error */ notifyError: (error: Error) => Promise /** Notify transaction status */ - notifyTransaction: (chainId: ChainId, id: string, transaction: Transaction, status: TransactionStatusType) => Promise + notifyTransaction: ( + chainId: ChainId, + id: string, + transaction: Transaction, + status: TransactionStatusType, + ) => Promise } export interface ProviderState { /** The account of the currently visiting site. */ diff --git a/packages/web3-shared/evm/constants/chains.json b/packages/web3-shared/evm/constants/chains.json index 70abf0d8c5cb..725cccd2906c 100644 --- a/packages/web3-shared/evm/constants/chains.json +++ b/packages/web3-shared/evm/constants/chains.json @@ -2782,7 +2782,7 @@ "nativeCurrency": { "chainId": 1313161554, "name": "Ether", - "symbol": "aETH", + "symbol": "AETH", "decimals": 18 }, "infoURL": "https://aurora.dev", @@ -2805,7 +2805,7 @@ "nativeCurrency": { "chainId": 1313161555, "name": "Ether", - "symbol": "aETH", + "symbol": "AETH", "decimals": 18 }, "infoURL": "https://aurora.dev" @@ -2821,7 +2821,7 @@ "nativeCurrency": { "chainId": 1313161556, "name": "Ether", - "symbol": "aETH", + "symbol": "AETH", "decimals": 18 }, "infoURL": "https://aurora.dev" @@ -2839,7 +2839,14 @@ "symbol": "ONE", "decimals": 18 }, - "infoURL": "https://www.harmony.one/" + "infoURL": "https://www.harmony.one/", + "explorers": [ + { + "name": "harmony", + "url": "https://explorer.harmony.one", + "standard": "EIP3091" + } + ] }, { "chainId": 1666600001, @@ -2854,7 +2861,14 @@ "symbol": "ONE", "decimals": 18 }, - "infoURL": "https://www.harmony.one/" + "infoURL": "https://www.harmony.one/", + "explorers": [ + { + "name": "harmony", + "url": "https://explorer.harmony.one", + "standard": "EIP3091" + } + ] }, { "chainId": 1666600002, @@ -2869,7 +2883,14 @@ "symbol": "ONE", "decimals": 18 }, - "infoURL": "https://www.harmony.one/" + "infoURL": "https://www.harmony.one/", + "explorers": [ + { + "name": "harmony", + "url": "https://explorer.harmony.one", + "standard": "EIP3091" + } + ] }, { "chainId": 1666600003, @@ -2884,7 +2905,14 @@ "symbol": "ONE", "decimals": 18 }, - "infoURL": "https://www.harmony.one/" + "infoURL": "https://www.harmony.one/", + "explorers": [ + { + "name": "harmony", + "url": "https://explorer.harmony.one", + "standard": "EIP3091" + } + ] }, { "chainId": 1666700000, diff --git a/packages/web3-shared/evm/constants/descriptors.ts b/packages/web3-shared/evm/constants/descriptors.ts index 0fd7557101ec..e0f468c7ac04 100644 --- a/packages/web3-shared/evm/constants/descriptors.ts +++ b/packages/web3-shared/evm/constants/descriptors.ts @@ -93,7 +93,6 @@ export const NETWORK_DESCRIPTORS: Array> chainId: ChainId.BSC, type: NetworkType.Binance, name: 'BNB Chain', - shortName: 'BNB', icon: new URL('../assets/binance.png', import.meta.url), iconColor: 'rgb(240, 185, 10)', backgroundGradient: 'linear-gradient(180deg, rgba(243, 186, 47, 0.15) 0%, rgba(243, 186, 47, 0.05) 100%)', diff --git a/packages/web3-shared/evm/utils/contract.ts b/packages/web3-shared/evm/utils/contract.ts index db5966518bf0..46d2c0e53caf 100644 --- a/packages/web3-shared/evm/utils/contract.ts +++ b/packages/web3-shared/evm/utils/contract.ts @@ -57,6 +57,8 @@ export async function encodeContractTransaction( if (!tx.gas) { tx.gas = await transaction.estimateGas({ from: tx.from as string | undefined, + to: tx.to as string | undefined, + data: tx.data as string | undefined, value: tx.value, }) } diff --git a/packages/web3-shared/evm/utils/formatter.ts b/packages/web3-shared/evm/utils/formatter.ts index e8bff3b48c65..f394f9dba98a 100644 --- a/packages/web3-shared/evm/utils/formatter.ts +++ b/packages/web3-shared/evm/utils/formatter.ts @@ -20,9 +20,14 @@ export function formatEthereumAddress(address: string, size = 0) { return `${address_.slice(0, Math.max(0, 2 + size))}...${address_.slice(-size)}` } -export function formatTokenId(tokenId: string, size = 0) { - if (tokenId.length < 9) return `#${tokenId}` - return `#${tokenId.slice(0, Math.max(0, 2 + size))}...${tokenId.slice(-size)}` +export function formatTokenId(tokenId: string, size = 4) { + size = Math.max(2, size) + const isHex = tokenId.toLowerCase().startsWith('0x') + const prefix = isHex ? '0x' : '#' + if (tokenId.length < size * 2 + prefix.length) return `#${tokenId}` + const head = tokenId.slice(0, isHex ? 2 + size : size) + const tail = tokenId.slice(-size) + return `${prefix}${head}...${tail}` } export function formatDomainName(domain: string, size = 4) { diff --git a/packages/web3-shared/evm/utils/payload.ts b/packages/web3-shared/evm/utils/payload.ts index 58c2e282ac65..a8b5fce28157 100644 --- a/packages/web3-shared/evm/utils/payload.ts +++ b/packages/web3-shared/evm/utils/payload.ts @@ -1,6 +1,7 @@ import BigNumber from 'bignumber.js' -import { first } from 'lodash-unified' +import { first, isUndefined, omitBy } from 'lodash-unified' import type { JsonRpcPayload } from 'web3-core-helpers' +import { hexToNumberString } from 'web3-utils' import { EthereumMethodType, Transaction } from '../types' export function addGasMargin(value: BigNumber.Value, scale = 3000) { @@ -56,3 +57,25 @@ export function getPayloadConfig(payload: JsonRpcPayload) { return } } + +export function getSignablePayloadConfig(payload: JsonRpcPayload) { + const raw = getPayloadConfig(payload) + if (!raw) return + + const parseHexNumberString = (hex: string | number | undefined) => + typeof hex !== 'undefined' ? hexToNumberString(hex ?? '0x0') : undefined + + return omitBy( + { + ...raw, + value: parseHexNumberString(raw.value as string | undefined), + gas: parseHexNumberString(raw.gas), + gasPrice: parseHexNumberString(raw.gasPrice as string | undefined), + maxFeePerGas: parseHexNumberString(raw.maxFeePerGas as string | undefined), + maxPriorityFeePerGas: parseHexNumberString(raw.maxPriorityFeePerGas as string | undefined), + chainId: parseHexNumberString(raw.chainId), + nonce: parseHexNumberString(raw.nonce), + }, + isUndefined, + ) as Transaction +} diff --git a/packages/web3-shared/evm/utils/provider.ts b/packages/web3-shared/evm/utils/provider.ts index 4ec56364cdc1..2367e25a3568 100644 --- a/packages/web3-shared/evm/utils/provider.ts +++ b/packages/web3-shared/evm/utils/provider.ts @@ -12,6 +12,15 @@ export function createWeb3(provider: Provider) { return web3 } +export function createSignableWeb3(provider: Provider, keys: string[]) { + const web3 = createWeb3(provider) + if (keys.length) { + web3.eth.accounts.wallet.clear() + keys.forEach((k) => k && ['0x', '0x0'].includes(k) && web3.eth.accounts.wallet.add(k)) + } + return web3 +} + export function createWeb3Provider(request: (requestArguments: RequestArguments) => Promise): Web3Provider { const provider: Web3Provider = { on() { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d9551915fa10..144ef7e10c0b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -421,6 +421,7 @@ importers: '@types/elliptic': ^6.4.14 '@types/json-stable-stringify': ^1.0.34 '@types/json2csv': ^5.0.3 + '@types/lru-cache': ^7.10.10 '@types/node': ^17.0.34 '@types/react-avatar-editor': ^12.0.0 '@types/react-highlight-words': ^0.16.4 @@ -461,6 +462,7 @@ importers: jsbi: 3.1.4 json-stable-stringify: ^1.0.1 json2csv: ^5.0.6 + lru-cache: ^7.10.1 millify: ^4.0.0 next-tick: ^1.0.0 opensea-js: ^1.2.7 @@ -605,6 +607,7 @@ importers: jsbi: 3.1.4 json-stable-stringify: 1.0.1 json2csv: 5.0.6 + lru-cache: 7.10.1 millify: 4.0.0 next-tick: 1.1.0 opensea-js: 1.2.7_webpack-cli@4.10.0 @@ -650,6 +653,7 @@ importers: '@nice-labs/emit-file-webpack-plugin': 1.1.2_webpack@5.73.0 '@pmmmwh/react-refresh-webpack-plugin': 0.5.7_g5otyhka3xgu4m6o2r25nvx6tm '@types/color': 3.0.3 + '@types/lru-cache': 7.10.10 binaryen: 107.0.0 copy-webpack-plugin: 11.0.0_webpack@5.73.0 html-webpack-plugin: 5.5.0_webpack@5.73.0 @@ -1312,6 +1316,7 @@ importers: '@masknet/web3-shared-solana': workspace:* '@react-hookz/web': ^14.2.2 '@solana/web3.js': ^1.30.2 + '@types/lru-cache': ^7.10.10 '@types/qrcode': ^1.4.2 anchorme: ^2.1.2 bignumber.js: ^9.0.2 @@ -1319,6 +1324,7 @@ importers: date-fns: 2.28.0 iframe-resizer-react: ^1.1.0 lodash-es: ^4.17.21 + lru-cache: ^7.10.1 qrcode: ^1.5.0 react-feather: ^2.0.9 react-hook-form: ^7.31.1 @@ -1342,6 +1348,7 @@ importers: '@masknet/web3-shared-solana': link:../web3-shared/solana '@react-hookz/web': 14.2.2 '@solana/web3.js': 1.44.2 + '@types/lru-cache': 7.10.10 '@types/qrcode': 1.4.2 anchorme: 2.1.2 bignumber.js: 9.0.2 @@ -1349,6 +1356,7 @@ importers: date-fns: 2.28.0 iframe-resizer-react: 1.1.0 lodash-es: 4.17.21 + lru-cache: 7.10.1 qrcode: 1.5.0 react-feather: 2.0.9 react-hook-form: 7.31.1 @@ -1838,11 +1846,11 @@ packages: dev: false /@0xproject/json-schemas/0.7.24: - resolution: {integrity: sha1-IaErQ6sKtKowLQLEiRZozaNrbGQ=} + resolution: {integrity: sha512-20MeFHMptuv0e58mjSLmW84m2HbYdmuxFeeXqGZjv047zBawONe2aXKqLX266XYjgNSif/cDVzdgh7/46J9MVQ==} engines: {node: '>=6.12'} dependencies: '@0xproject/typescript-typings': 0.3.2 - '@types/node': 17.0.34 + '@types/node': 17.0.45 jsonschema: 1.4.0 lodash.values: 4.3.0 dev: false @@ -1855,15 +1863,15 @@ packages: dev: false /@0xproject/types/0.7.0: - resolution: {integrity: sha1-+tE5Je6SrU7hmAZopcsr7U3Kq48=} + resolution: {integrity: sha512-LlTnq7raPnCioCokNy5CLDxZJeg3KtcHT4PBJD6BDiXYtNJxBWvL7/jr6JiwdjReNg7ihGi1265VKfFX7qukRw==} engines: {node: '>=6.12'} dependencies: - '@types/node': 17.0.34 + '@types/node': 17.0.45 bignumber.js: 4.1.0 dev: false /@0xproject/typescript-typings/0.3.2: - resolution: {integrity: sha1-q8NgtEGv2pkxAEUMqDad+r91k2s=} + resolution: {integrity: sha512-sHwGTxerREyQhyXFHhZRHvy7PlbBUjsoVbxhf5f+9JasC+IhQ2Nv4uJFjOo16uUPPp7l+lsRUvK/ZwrdGRbFBA==} engines: {node: '>=6.12'} dependencies: '@0xproject/types': 0.7.0 @@ -9953,7 +9961,6 @@ packages: deprecated: This is a stub types definition. lru-cache provides its own type definitions, so you do not need this installed. dependencies: lru-cache: 7.10.1 - dev: true /@types/mdast/3.0.10: resolution: {integrity: sha512-W864tg/Osz1+9f4lrGTZpCSO5/z4608eUp19tbozkq2HJK6i3z1kT0H9tlADXuYIb1YYOBByU4Jsqkk75q48qA==} @@ -11703,7 +11710,7 @@ packages: dev: false /axios/0.17.1: - resolution: {integrity: sha1-LY4+XQvb1zJ/kbyBT1xXZg+Bgk0=} + resolution: {integrity: sha512-mZzWRyJeJ0rtK7e1/6iYBUzmeXjzei+1h1IvbedyU0sB52++tU5AU6r6TLXpwNVR0ebXIpvTVW+9CpWNyc1n8w==} deprecated: Critical security vulnerability fixed in v0.21.1. For more information, see https://github.com/axios/axios/pull/3410 dependencies: follow-redirects: 1.14.9 @@ -11737,7 +11744,7 @@ packages: dev: false /babel-code-frame/6.26.0: - resolution: {integrity: sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=} + resolution: {integrity: sha512-XqYMR2dfdGMW+hd0IUZ2PwK+fGeFkOxZJ0wY+JaQAHzt1Zx8LcvpiZD2NiGkEG8qx0CfkAOr5xt76d1e8vG90g==} dependencies: chalk: 1.1.3 esutils: 2.0.3 @@ -11784,7 +11791,7 @@ packages: dev: false /babel-helper-builder-binary-assignment-operator-visitor/6.24.1: - resolution: {integrity: sha1-zORReto1b0IgvK6KAsKzRvmlZmQ=} + resolution: {integrity: sha512-gCtfYORSG1fUMX4kKraymq607FWgMWg+j42IFPc18kFQEsmtaibP4UrqsXt8FlEJle25HUd4tsoDR7H2wDhe9Q==} dependencies: babel-helper-explode-assignable-expression: 6.24.1 babel-runtime: 6.26.0 @@ -11794,7 +11801,7 @@ packages: dev: false /babel-helper-call-delegate/6.24.1: - resolution: {integrity: sha1-7Oaqzdx25Bw0YfiL/Fdb0Nqi340=} + resolution: {integrity: sha512-RL8n2NiEj+kKztlrVJM9JT1cXzzAdvWFh76xh/H1I4nKwunzE4INBXn8ieCZ+wh4zWszZk7NBS1s/8HR5jDkzQ==} dependencies: babel-helper-hoist-variables: 6.24.1 babel-runtime: 6.26.0 @@ -11805,7 +11812,7 @@ packages: dev: false /babel-helper-define-map/6.26.0: - resolution: {integrity: sha1-pfVtq0GiX5fstJjH66ypgZ+Vvl8=} + resolution: {integrity: sha512-bHkmjcC9lM1kmZcVpA5t2om2nzT/xiZpo6TJq7UlZ3wqKfzia4veeXbIhKvJXAMzhhEBd3cR1IElL5AenWEUpA==} dependencies: babel-helper-function-name: 6.24.1 babel-runtime: 6.26.0 @@ -11816,7 +11823,7 @@ packages: dev: false /babel-helper-explode-assignable-expression/6.24.1: - resolution: {integrity: sha1-8luCz33BBDPFX3BZLVdGQArCLKo=} + resolution: {integrity: sha512-qe5csbhbvq6ccry9G7tkXbzNtcDiH4r51rrPUbwwoTzZ18AqxWYRZT6AOmxrpxKnQBW0pYlBI/8vh73Z//78nQ==} dependencies: babel-runtime: 6.26.0 babel-traverse: 6.26.0 @@ -11826,7 +11833,7 @@ packages: dev: false /babel-helper-function-name/6.24.1: - resolution: {integrity: sha1-00dbjAPtmCQqJbSDUasYOZ01gKk=} + resolution: {integrity: sha512-Oo6+e2iX+o9eVvJ9Y5eKL5iryeRdsIkwRYheCuhYdVHsdEQysbc2z2QkqCLIYnNxkT5Ss3ggrHdXiDI7Dhrn4Q==} dependencies: babel-helper-get-function-arity: 6.24.1 babel-runtime: 6.26.0 @@ -11838,28 +11845,28 @@ packages: dev: false /babel-helper-get-function-arity/6.24.1: - resolution: {integrity: sha1-j3eCqpNAfEHTqlCQj4mwMbG2hT0=} + resolution: {integrity: sha512-WfgKFX6swFB1jS2vo+DwivRN4NB8XUdM3ij0Y1gnC21y1tdBoe6xjVnd7NSI6alv+gZXCtJqvrTeMW3fR/c0ng==} dependencies: babel-runtime: 6.26.0 babel-types: 6.26.0 dev: false /babel-helper-hoist-variables/6.24.1: - resolution: {integrity: sha1-HssnaJydJVE+rbyZFKc/VAi+enY=} + resolution: {integrity: sha512-zAYl3tqerLItvG5cKYw7f1SpvIxS9zi7ohyGHaI9cgDUjAT6YcY9jIEH5CstetP5wHIVSceXwNS7Z5BpJg+rOw==} dependencies: babel-runtime: 6.26.0 babel-types: 6.26.0 dev: false /babel-helper-optimise-call-expression/6.24.1: - resolution: {integrity: sha1-96E0J7qfc/j0+pk8VKl4gtEkQlc=} + resolution: {integrity: sha512-Op9IhEaxhbRT8MDXx2iNuMgciu2V8lDvYCNQbDGjdBNCjaMvyLf4wl4A3b8IgndCyQF8TwfgsQ8T3VD8aX1/pA==} dependencies: babel-runtime: 6.26.0 babel-types: 6.26.0 dev: false /babel-helper-regex/6.26.0: - resolution: {integrity: sha1-MlxZ+QL4LyS3T6zu0DY5VPZJXnI=} + resolution: {integrity: sha512-VlPiWmqmGJp0x0oK27Out1D+71nVVCTSdlbhIVoaBAj2lUgrNjBCRR9+llO4lTSb2O4r7PJg+RobRkhBrf6ofg==} dependencies: babel-runtime: 6.26.0 babel-types: 6.26.0 @@ -11867,7 +11874,7 @@ packages: dev: false /babel-helper-remap-async-to-generator/6.24.1: - resolution: {integrity: sha1-XsWBgnrXI/7N04HxySg5BnbkVRs=} + resolution: {integrity: sha512-RYqaPD0mQyQIFRu7Ho5wE2yvA/5jxqCIj/Lv4BXNq23mHYu/vxikOy2JueLiBxQknwapwrJeNCesvY0ZcfnlHg==} dependencies: babel-helper-function-name: 6.24.1 babel-runtime: 6.26.0 @@ -11879,7 +11886,7 @@ packages: dev: false /babel-helper-replace-supers/6.24.1: - resolution: {integrity: sha1-v22/5Dk40XNpohPKiov3S2qQqxo=} + resolution: {integrity: sha512-sLI+u7sXJh6+ToqDr57Bv973kCepItDhMou0xCP2YPVmR1jkHSCY+p1no8xErbV1Siz5QE8qKT1WIwybSWlqjw==} dependencies: babel-helper-optimise-call-expression: 6.24.1 babel-messages: 6.23.0 @@ -11892,7 +11899,7 @@ packages: dev: false /babel-helpers/6.24.1: - resolution: {integrity: sha1-NHHenK7DiOXIUOWX5Yom3fN2ArI=} + resolution: {integrity: sha512-n7pFrqQm44TCYvrCDb0MqabAF+JUBq+ijBvNMUxpkLjJaAu32faIexewMumrH5KLLJ1HDyT0PTEqRyAe/GwwuQ==} dependencies: babel-runtime: 6.26.0 babel-template: 6.26.0 @@ -11975,7 +11982,7 @@ packages: dev: true /babel-messages/6.23.0: - resolution: {integrity: sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=} + resolution: {integrity: sha512-Bl3ZiA+LjqaMtNYopA9TYE9HP1tQ+E5dLxE0XrAzcIJeK2UqF0/EaqXwBn9esd4UmTfEab+P+UYQ1GnioFIb/w==} dependencies: babel-runtime: 6.26.0 dev: false @@ -11998,7 +12005,7 @@ packages: dev: true /babel-plugin-check-es2015-constants/6.22.0: - resolution: {integrity: sha1-NRV7EBQm/S/9PaP3XH0ekYNbv4o=} + resolution: {integrity: sha512-B1M5KBP29248dViEo1owyY32lk1ZSH2DaNNrXLGt8lyjjHm7pBqAdQ7VKUPR6EEDO323+OvT3MQXbCin8ooWdA==} dependencies: babel-runtime: 6.26.0 dev: false @@ -12169,19 +12176,19 @@ packages: dev: true /babel-plugin-syntax-async-functions/6.13.0: - resolution: {integrity: sha1-ytnK0RkbWtY0vzCuCHI5HgZHvpU=} + resolution: {integrity: sha512-4Zp4unmHgw30A1eWI5EpACji2qMocisdXhAftfhXoSV9j0Tvj6nRFE3tOmRY912E0FMRm/L5xWE7MGVT2FoLnw==} dev: false /babel-plugin-syntax-exponentiation-operator/6.13.0: - resolution: {integrity: sha1-nufoM3KQ2pUoggGmpX9BcDF4MN4=} + resolution: {integrity: sha512-Z/flU+T9ta0aIEKl1tGEmN/pZiI1uXmCiGFRegKacQfEJzp7iNsKloZmyJlQr+75FCJtiFfGIK03SiCvCt9cPQ==} dev: false /babel-plugin-syntax-trailing-function-commas/6.22.0: - resolution: {integrity: sha1-ugNgk3+NBuQBgKQ/4NVhb/9TLPM=} + resolution: {integrity: sha512-Gx9CH3Q/3GKbhs07Bszw5fPTlU+ygrOGfAhEt7W2JICwufpC4SuO0mG0+4NykPBSYPMJhqvVlDBU17qB1D+hMQ==} dev: false /babel-plugin-transform-async-to-generator/6.24.1: - resolution: {integrity: sha1-ZTbjeK/2yx1VF6wOQOs+n8jQh2E=} + resolution: {integrity: sha512-7BgYJujNCg0Ti3x0c/DL3tStvnKS6ktIYOmo9wginv/dfZOrbSZ+qG4IRRHMBOzZ5Awb1skTiAsQXg/+IWkZYw==} dependencies: babel-helper-remap-async-to-generator: 6.24.1 babel-plugin-syntax-async-functions: 6.13.0 @@ -12191,19 +12198,19 @@ packages: dev: false /babel-plugin-transform-es2015-arrow-functions/6.22.0: - resolution: {integrity: sha1-RSaSy3EdX3ncf4XkQM5BufJE0iE=} + resolution: {integrity: sha512-PCqwwzODXW7JMrzu+yZIaYbPQSKjDTAsNNlK2l5Gg9g4rz2VzLnZsStvp/3c46GfXpwkyufb3NCyG9+50FF1Vg==} dependencies: babel-runtime: 6.26.0 dev: false /babel-plugin-transform-es2015-block-scoped-functions/6.22.0: - resolution: {integrity: sha1-u8UbSflk1wy42OC5ToICRs46YUE=} + resolution: {integrity: sha512-2+ujAT2UMBzYFm7tidUsYh+ZoIutxJ3pN9IYrF1/H6dCKtECfhmB8UkHVpyxDwkj0CYbQG35ykoz925TUnBc3A==} dependencies: babel-runtime: 6.26.0 dev: false /babel-plugin-transform-es2015-block-scoping/6.26.0: - resolution: {integrity: sha1-1w9SmcEwjQXBL0Y4E7CgnnOxiV8=} + resolution: {integrity: sha512-YiN6sFAQ5lML8JjCmr7uerS5Yc/EMbgg9G8ZNmk2E3nYX4ckHR01wrkeeMijEf5WHNK5TW0Sl0Uu3pv3EdOJWw==} dependencies: babel-runtime: 6.26.0 babel-template: 6.26.0 @@ -12215,7 +12222,7 @@ packages: dev: false /babel-plugin-transform-es2015-classes/6.24.1: - resolution: {integrity: sha1-WkxYpQyclGHlZLSyo7+ryXolhNs=} + resolution: {integrity: sha512-5Dy7ZbRinGrNtmWpquZKZ3EGY8sDgIVB4CU8Om8q8tnMLrD/m94cKglVcHps0BCTdZ0TJeeAWOq2TK9MIY6cag==} dependencies: babel-helper-define-map: 6.26.0 babel-helper-function-name: 6.24.1 @@ -12231,7 +12238,7 @@ packages: dev: false /babel-plugin-transform-es2015-computed-properties/6.24.1: - resolution: {integrity: sha1-b+Ko0WiV1WNPTNmZttNICjCBWbM=} + resolution: {integrity: sha512-C/uAv4ktFP/Hmh01gMTvYvICrKze0XVX9f2PdIXuriCSvUmV9j+u+BB9f5fJK3+878yMK6dkdcq+Ymr9mrcLzw==} dependencies: babel-runtime: 6.26.0 babel-template: 6.26.0 @@ -12240,26 +12247,26 @@ packages: dev: false /babel-plugin-transform-es2015-destructuring/6.23.0: - resolution: {integrity: sha1-mXux8auWf2gtKwh2/jWNYOdlxW0=} + resolution: {integrity: sha512-aNv/GDAW0j/f4Uy1OEPZn1mqD+Nfy9viFGBfQ5bZyT35YqOiqx7/tXdyfZkJ1sC21NyEsBdfDY6PYmLHF4r5iA==} dependencies: babel-runtime: 6.26.0 dev: false /babel-plugin-transform-es2015-duplicate-keys/6.24.1: - resolution: {integrity: sha1-c+s9MQypaePvnskcU3QabxV2Qj4=} + resolution: {integrity: sha512-ossocTuPOssfxO2h+Z3/Ea1Vo1wWx31Uqy9vIiJusOP4TbF7tPs9U0sJ9pX9OJPf4lXRGj5+6Gkl/HHKiAP5ug==} dependencies: babel-runtime: 6.26.0 babel-types: 6.26.0 dev: false /babel-plugin-transform-es2015-for-of/6.23.0: - resolution: {integrity: sha1-9HyVsrYT3x0+zC/bdXNiPHUkhpE=} + resolution: {integrity: sha512-DLuRwoygCoXx+YfxHLkVx5/NpeSbVwfoTeBykpJK7JhYWlL/O8hgAK/reforUnZDlxasOrVPPJVI/guE3dCwkw==} dependencies: babel-runtime: 6.26.0 dev: false /babel-plugin-transform-es2015-function-name/6.24.1: - resolution: {integrity: sha1-g0yJhTvDaxrw86TF26qU/Y6sqos=} + resolution: {integrity: sha512-iFp5KIcorf11iBqu/y/a7DK3MN5di3pNCzto61FqCNnUX4qeBwcV1SLqe10oXNnCaxBUImX3SckX2/o1nsrTcg==} dependencies: babel-helper-function-name: 6.24.1 babel-runtime: 6.26.0 @@ -12269,13 +12276,13 @@ packages: dev: false /babel-plugin-transform-es2015-literals/6.22.0: - resolution: {integrity: sha1-T1SgLWzWbPkVKAAZox0xklN3yi4=} + resolution: {integrity: sha512-tjFl0cwMPpDYyoqYA9li1/7mGFit39XiNX5DKC/uCNjBctMxyL1/PT/l4rSlbvBG1pOKI88STRdUsWXB3/Q9hQ==} dependencies: babel-runtime: 6.26.0 dev: false /babel-plugin-transform-es2015-modules-amd/6.24.1: - resolution: {integrity: sha1-Oz5UAXI5hC1tGcMBHEvS8AoA0VQ=} + resolution: {integrity: sha512-LnIIdGWIKdw7zwckqx+eGjcS8/cl8D74A3BpJbGjKTFFNJSMrjN4bIh22HY1AlkUbeLG6X6OZj56BDvWD+OeFA==} dependencies: babel-plugin-transform-es2015-modules-commonjs: 6.26.2 babel-runtime: 6.26.0 @@ -12296,7 +12303,7 @@ packages: dev: false /babel-plugin-transform-es2015-modules-systemjs/6.24.1: - resolution: {integrity: sha1-/4mhQrkRmpBhlfXxBuzzBdlAfSM=} + resolution: {integrity: sha512-ONFIPsq8y4bls5PPsAWYXH/21Hqv64TBxdje0FvU3MhIV6QM2j5YS7KvAzg/nTIVLot2D2fmFQrFWCbgHlFEjg==} dependencies: babel-helper-hoist-variables: 6.24.1 babel-runtime: 6.26.0 @@ -12306,7 +12313,7 @@ packages: dev: false /babel-plugin-transform-es2015-modules-umd/6.24.1: - resolution: {integrity: sha1-rJl+YoXNGO1hdq22B9YCNErThGg=} + resolution: {integrity: sha512-LpVbiT9CLsuAIp3IG0tfbVo81QIhn6pE8xBJ7XSeCtFlMltuar5VuBV6y6Q45tpui9QWcy5i0vLQfCfrnF7Kiw==} dependencies: babel-plugin-transform-es2015-modules-amd: 6.24.1 babel-runtime: 6.26.0 @@ -12316,7 +12323,7 @@ packages: dev: false /babel-plugin-transform-es2015-object-super/6.24.1: - resolution: {integrity: sha1-JM72muIcuDp/hgPa0CH1cusnj40=} + resolution: {integrity: sha512-8G5hpZMecb53vpD3mjs64NhI1au24TAmokQ4B+TBFBjN9cVoGoOvotdrMMRmHvVZUEvqGUPWL514woru1ChZMA==} dependencies: babel-helper-replace-supers: 6.24.1 babel-runtime: 6.26.0 @@ -12325,7 +12332,7 @@ packages: dev: false /babel-plugin-transform-es2015-parameters/6.24.1: - resolution: {integrity: sha1-V6w1GrScrxSpfNE7CfZv3wpiXys=} + resolution: {integrity: sha512-8HxlW+BB5HqniD+nLkQ4xSAVq3bR/pcYW9IigY+2y0dI+Y7INFeTbfAQr+63T3E4UDsZGjyb+l9txUnABWxlOQ==} dependencies: babel-helper-call-delegate: 6.24.1 babel-helper-get-function-arity: 6.24.1 @@ -12338,20 +12345,20 @@ packages: dev: false /babel-plugin-transform-es2015-shorthand-properties/6.24.1: - resolution: {integrity: sha1-JPh11nIch2YbvZmkYi5R8U3jiqA=} + resolution: {integrity: sha512-mDdocSfUVm1/7Jw/FIRNw9vPrBQNePy6wZJlR8HAUBLybNp1w/6lr6zZ2pjMShee65t/ybR5pT8ulkLzD1xwiw==} dependencies: babel-runtime: 6.26.0 babel-types: 6.26.0 dev: false /babel-plugin-transform-es2015-spread/6.22.0: - resolution: {integrity: sha1-1taKmfia7cRTbIGlQujdnxdG+NE=} + resolution: {integrity: sha512-3Ghhi26r4l3d0Js933E5+IhHwk0A1yiutj9gwvzmFbVV0sPMYk2lekhOufHBswX7NCoSeF4Xrl3sCIuSIa+zOg==} dependencies: babel-runtime: 6.26.0 dev: false /babel-plugin-transform-es2015-sticky-regex/6.24.1: - resolution: {integrity: sha1-AMHNsaynERLN8M9hJsLta0V8zbw=} + resolution: {integrity: sha512-CYP359ADryTo3pCsH0oxRo/0yn6UsEZLqYohHmvLQdfS9xkf+MbCzE3/Kolw9OYIY4ZMilH25z/5CbQbwDD+lQ==} dependencies: babel-helper-regex: 6.26.0 babel-runtime: 6.26.0 @@ -12359,19 +12366,19 @@ packages: dev: false /babel-plugin-transform-es2015-template-literals/6.22.0: - resolution: {integrity: sha1-qEs0UPfp+PH2g51taH2oS7EjbY0=} + resolution: {integrity: sha512-x8b9W0ngnKzDMHimVtTfn5ryimars1ByTqsfBDwAqLibmuuQY6pgBQi5z1ErIsUOWBdw1bW9FSz5RZUojM4apg==} dependencies: babel-runtime: 6.26.0 dev: false /babel-plugin-transform-es2015-typeof-symbol/6.23.0: - resolution: {integrity: sha1-3sCfHN3/lLUqxz1QXITfWdzOs3I=} + resolution: {integrity: sha512-fz6J2Sf4gYN6gWgRZaoFXmq93X+Li/8vf+fb0sGDVtdeWvxC9y5/bTD7bvfWMEq6zetGEHpWjtzRGSugt5kNqw==} dependencies: babel-runtime: 6.26.0 dev: false /babel-plugin-transform-es2015-unicode-regex/6.24.1: - resolution: {integrity: sha1-04sS9C6nMj9yk4fxinxa4frrNek=} + resolution: {integrity: sha512-v61Dbbihf5XxnYjtBN04B/JBvsScY37R1cZT5r9permN1cp+b70DY3Ib3fIkgn1DI9U3tGgBJZVD8p/mE/4JbQ==} dependencies: babel-helper-regex: 6.26.0 babel-runtime: 6.26.0 @@ -12379,7 +12386,7 @@ packages: dev: false /babel-plugin-transform-exponentiation-operator/6.24.1: - resolution: {integrity: sha1-KrDJx/MJj6SJB3cruBP+QejeOg4=} + resolution: {integrity: sha512-LzXDmbMkklvNhprr20//RStKVcT8Cu+SQtX18eMHLhjHf2yFzwtQ0S2f0jQ+89rokoNdmwoSqYzAhq86FxlLSQ==} dependencies: babel-helper-builder-binary-assignment-operator-visitor: 6.24.1 babel-plugin-syntax-exponentiation-operator: 6.13.0 @@ -12389,13 +12396,13 @@ packages: dev: false /babel-plugin-transform-regenerator/6.26.0: - resolution: {integrity: sha1-4HA2lvveJ/Cj78rPi03KL3s6jy8=} + resolution: {integrity: sha512-LS+dBkUGlNR15/5WHKe/8Neawx663qttS6AGqoOUhICc9d1KciBvtrQSuc0PI+CxQ2Q/S1aKuJ+u64GtLdcEZg==} dependencies: regenerator-transform: 0.10.1 dev: false /babel-plugin-transform-strict-mode/6.24.1: - resolution: {integrity: sha1-1fr3qleKZbvlkc9e2uBKDGcCB1g=} + resolution: {integrity: sha512-j3KtSpjyLSJxNoCDrhwiJad8kw0gJ9REGj8/CqL0HeRyLnvUNYV9zcqluL6QJSXh3nfsLEmSLvwRfGzrgR96Pw==} dependencies: babel-runtime: 6.26.0 babel-types: 6.26.0 @@ -12476,7 +12483,7 @@ packages: dev: true /babel-register/6.26.0: - resolution: {integrity: sha1-btAhFz4vy0htestFxgCahW9kcHE=} + resolution: {integrity: sha512-veliHlHX06wjaeY8xNITbveXSiI+ASFnOqvne/LaIJIqOWi2Ogmj91KOugEz/hoh/fwMhXNBJPCv8Xaz5CyM4A==} dependencies: babel-core: 6.26.3 babel-runtime: 6.26.0 @@ -12490,14 +12497,14 @@ packages: dev: false /babel-runtime/6.26.0: - resolution: {integrity: sha1-llxwWGaOgrVde/4E/yM3vItWR/4=} + resolution: {integrity: sha512-ITKNuq2wKlW1fJg9sSW52eepoYgZBggvOAHC0u/CYu/qxQ9EVzThCgR69BnSXLHjy2f7SY5zaQ4yt7H9ZVxY2g==} dependencies: core-js: 2.6.12 regenerator-runtime: 0.11.1 dev: false /babel-template/6.26.0: - resolution: {integrity: sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI=} + resolution: {integrity: sha512-PCOcLFW7/eazGUKIoqH97sO9A2UYMahsn/yRQ7uOk37iutwjq7ODtcTNF+iFDSHNfkctqsLRjLP7URnOx0T1fg==} dependencies: babel-runtime: 6.26.0 babel-traverse: 6.26.0 @@ -12509,7 +12516,7 @@ packages: dev: false /babel-traverse/6.26.0: - resolution: {integrity: sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=} + resolution: {integrity: sha512-iSxeXx7apsjCHe9c7n8VtRXGzI2Bk1rBSOJgCCjfyXb6v1aCqE1KSEpq/8SXuVN8Ka/Rh1WDTF0MDzkvTA4MIA==} dependencies: babel-code-frame: 6.26.0 babel-messages: 6.23.0 @@ -12525,7 +12532,7 @@ packages: dev: false /babel-types/6.26.0: - resolution: {integrity: sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=} + resolution: {integrity: sha512-zhe3V/26rCWsEZK8kZN+HaQj5yQ1CilTObixFzKW1UWjqG7618Twz6YEsCnjfg5gBcJh02DrpCkS9h98ZqDY+g==} dependencies: babel-runtime: 6.26.0 esutils: 2.0.3 @@ -12534,7 +12541,7 @@ packages: dev: false /babelify/7.3.0: - resolution: {integrity: sha1-qlau3nBn/XvVSWZu4W3ChQh+iOU=} + resolution: {integrity: sha512-vID8Fz6pPN5pJMdlUnNFSfrlcx5MUule4k9aKs/zbZPyXxMTcRrB0M4Tarw22L8afr8eYSWxDPYCob3TdrqtlA==} dependencies: babel-core: 6.26.3 object-assign: 4.1.1 @@ -12698,7 +12705,7 @@ packages: file-uri-to-path: 1.0.0 /bintrees/1.0.2: - resolution: {integrity: sha1-SfiW1uhYpKSZ34XDj7OZua/4QPg=} + resolution: {integrity: sha512-VOMgTMwjAaUG580SXn3LacVgjurrbMme7ZZNYGSSV7mmtY6QQRh0Eg3pwIcntQ77DErK1L0NxkbetjcoXzVwKw==} dev: false /bip32/2.0.6: @@ -13039,8 +13046,8 @@ packages: resolution: {integrity: sha512-WHVocJYavUwVgVViC0ORikPHQquXwVh939TaelZ4WDqpWgTX/FsGhl/+P4qBUAGcRvtOgDgC+xftNWWp2RUTAQ==} hasBin: true dependencies: - caniuse-lite: 1.0.30001341 - electron-to-chromium: 1.4.137 + caniuse-lite: 1.0.30001359 + electron-to-chromium: 1.4.168 dev: false /browserslist/4.20.3: @@ -13181,7 +13188,7 @@ packages: node-gyp-build: 4.3.0 /builtin-modules/1.1.1: - resolution: {integrity: sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=} + resolution: {integrity: sha512-wxXCdllwGhI2kCC0MnvTGYTMvnVZTvqgypkiTI8Pa5tcz2i6VqsqwYGgqwXji+4RgCzms6EajE4IxiUH6HH8nQ==} engines: {node: '>=0.10.0'} dev: false @@ -13380,7 +13387,6 @@ packages: /caniuse-lite/1.0.30001359: resolution: {integrity: sha512-Xln/BAsPzEuiVLgJ2/45IaqD9jShtk3Y33anKb4+yLwQzws3+v6odKfpgES/cDEaZMLzSChpIGdbOYtH9MyuHw==} - dev: true /canonicalize/1.0.8: resolution: {integrity: sha512-0CNTVCLZggSh7bc5VkX5WWPWO+cyZbNd07IHIsSXLia/eAq+r836hgk+8BKoEh7949Mda87VUOitx5OddVj64A==} @@ -13477,7 +13483,7 @@ packages: dev: false /checkpoint-store/1.1.0: - resolution: {integrity: sha1-BOTLUWuRQziTWB5tRgGnjpVS6gY=} + resolution: {integrity: sha512-J/NdY2WvIx654cc6LWSq/IYFFCUf75fFTgwzFnmbqyORH4MwgiQCgswLLKBGzmsyTI5V7i5bp/So6sMbDWhedg==} dependencies: functional-red-black-tree: 1.0.1 dev: false @@ -14215,7 +14221,7 @@ packages: /core-js/2.6.12: resolution: {integrity: sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==} - deprecated: core-js@<3.4 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Please, upgrade your dependencies to the actual version of core-js. + deprecated: core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js. dev: false /core-js/3.23.1: @@ -15201,7 +15207,7 @@ packages: isobject: 3.0.1 /defined/1.0.0: - resolution: {integrity: sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=} + resolution: {integrity: sha512-Y2caI5+ZwS5c3RiNDJ6u53VhQHv+hHKwhkI1iHvceKUHw9Df6EK2zRLfjejRgMuCuxK7PfSWIMwWecceVvThjQ==} dev: false /del/4.1.1: @@ -15272,7 +15278,7 @@ packages: dev: true /detect-indent/4.0.0: - resolution: {integrity: sha1-920GQ1LN9Docts5hnE7jqUdd4gg=} + resolution: {integrity: sha512-BDKtmHlOzwI7iRuEkhzsnPoi5ypEhWAJB5RvHWe1kMr06js3uK5B3734i3ui5Yd+wOJV1cpE4JnivPD283GU/A==} engines: {node: '>=0.10.0'} dependencies: repeating: 2.0.1 @@ -15424,7 +15430,7 @@ packages: dev: false /doctrine/0.7.2: - resolution: {integrity: sha1-fLhgNZujvpDgQLJrcpzkv6ZUxSM=} + resolution: {integrity: sha512-qiB/Rir6Un6Ad/TIgTRzsremsTGWzs8j7woXvp14jgq00676uBiBT5eUOi+FgRywZFVy5Us/c04ISRpZhRbS6w==} engines: {node: '>=0.10.0'} dependencies: esutils: 1.1.6 @@ -15625,7 +15631,6 @@ packages: /electron-to-chromium/1.4.168: resolution: {integrity: sha512-yz247hclRBaP8ABB1hf9kL7AMfa+yC2hB9F3XF8Y87VWMnYgq4QYvV6acRACcDkTDxfGQ4GYK/aZPQiuFMGbaA==} - dev: true /elliptic/6.5.4: resolution: {integrity: sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==} @@ -15653,7 +15658,7 @@ packages: optional: true /emailjs/2.2.0: - resolution: {integrity: sha1-ulsj5KSwpFEPZS6HOxVOlAe2ygM=} + resolution: {integrity: sha512-J9HNx13GA5DnJma10YxsSqYCErTyB0KoVflTddPTyKlEVHM0MckZXn/zDqovdacwWkHCxqC9AKVY8GMPaGvaGQ==} dependencies: addressparser: 0.3.2 emailjs-mime-codec: 2.0.9 @@ -15899,35 +15904,6 @@ packages: unbox-primitive: 1.0.1 dev: true - /es-abstract/1.20.0: - resolution: {integrity: sha512-URbD8tgRthKD3YcC39vbvSDrX23upXnPcnGAjQfgxXF5ID75YcENawc9ZX/9iTP9ptUyfCLIxTTuMYoRfiOVKA==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.2 - es-to-primitive: 1.2.1 - function-bind: 1.1.1 - function.prototype.name: 1.1.5 - get-intrinsic: 1.1.1 - get-symbol-description: 1.0.0 - has: 1.0.3 - has-property-descriptors: 1.0.0 - has-symbols: 1.0.3 - internal-slot: 1.0.3 - is-callable: 1.2.4 - is-negative-zero: 2.0.2 - is-regex: 1.1.4 - is-shared-array-buffer: 1.0.2 - is-string: 1.0.7 - is-weakref: 1.0.2 - object-inspect: 1.12.0 - object-keys: 1.1.1 - object.assign: 4.1.2 - regexp.prototype.flags: 1.4.3 - string.prototype.trimend: 1.0.5 - string.prototype.trimstart: 1.0.5 - unbox-primitive: 1.0.2 - dev: false - /es-abstract/1.20.1: resolution: {integrity: sha512-WEm2oBhfoI2sImeM4OF2zE2V3BYdSF+KnSi9Sidz51fQHd7+JuF8Xgcj9/0o+OWeIeIS/MiuNnlruQrJf16GQA==} engines: {node: '>= 0.4'} @@ -16401,7 +16377,7 @@ packages: resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} /esutils/1.1.6: - resolution: {integrity: sha1-wBzKqa5LiXxtDD4hCuUvPHqEQ3U=} + resolution: {integrity: sha512-RG1ZkUT7iFJG9LSHr7KDuuMSlujfeTtMNIcInURxKAxhMtwQhI3NrQhz26gZQYlsYZQKzsnwtpKrFKj9K9Qu1A==} engines: {node: '>=0.10.0'} dev: false @@ -16466,7 +16442,7 @@ packages: dev: false /eth-query/2.1.2: - resolution: {integrity: sha1-1nQdkAAQa1FRDHLbktY2VFam2l4=} + resolution: {integrity: sha512-srES0ZcvwkR/wd5OQBRA1bIJMww1skfGS0s8wlwK3/oNP4+wnds60krvu5R1QbpRQjMmpG5OMIWro5s7gvDPsA==} dependencies: json-rpc-random-id: 1.0.1 xtend: 4.0.2 @@ -16479,7 +16455,7 @@ packages: dev: false /eth-sig-util/1.4.2: - resolution: {integrity: sha1-jZWCAsftuq6Dlwf7pvCf8ydgYhA=} + resolution: {integrity: sha512-iNZ576iTOGcfllftB73cPB5AN+XUQAT/T8xzsILsghXC1o8gJUqe3RHlcDqagu+biFpYQ61KQrZZJza8eRSYqw==} deprecated: Deprecated in favor of '@metamask/eth-sig-util' dependencies: ethereumjs-abi: github.com/ethereumjs/ethereumjs-abi/ee3994657fa7a427238e6ba92a84d0b529bbcde0 @@ -16496,7 +16472,7 @@ packages: js-sha3: 0.8.0 /ethereum-common/0.0.18: - resolution: {integrity: sha1-L9w1dvIykDNYl26znaeDIT/5Uj8=} + resolution: {integrity: sha512-EoltVQTRNg2Uy4o84qpa2aXymXDJhxm7eos/ACOg0DG4baAbMjhbdAEsx9GeE8sC3XCxnYvrrzZDH8D8MtA2iQ==} dev: false /ethereum-common/0.2.0: @@ -17141,7 +17117,7 @@ packages: dev: false /fake-merkle-patricia-tree/1.0.1: - resolution: {integrity: sha1-S4w6z7Ugr635hgsfFM2M40As3dM=} + resolution: {integrity: sha512-Tgq37lkc9pUIgIKw5uitNUKcgcYL3R6JvXtKQbOf/ZSavXbidsksgp/pAY6p//uhw0I4yoMsvTSovvVIsk/qxA==} dependencies: checkpoint-store: 1.1.0 dev: false @@ -17294,7 +17270,7 @@ packages: dev: false /fetch-ponyfill/4.1.0: - resolution: {integrity: sha1-rjzl9zLGReq4fkroeTQUcJsjmJM=} + resolution: {integrity: sha512-knK9sGskIg2T7OnYLdZ2hZXn0CtDrAIBxYQLpmEf0BqfdWnwmM1weccUl5+4EdA44tzNSFAuxITPbXtPehUB3g==} dependencies: node-fetch: 1.7.3 dev: false @@ -17448,7 +17424,7 @@ packages: path-exists: 4.0.0 /find-versions/2.0.0: - resolution: {integrity: sha1-KtkNSQ9oKMGqQCks9wmsMxghDDw=} + resolution: {integrity: sha512-nzvoTkgyiTcXOT9PNHYWP2wlAoNbXanK/FnlHnB6v2yA1HoyDNTxN08+NobUIXL0qnBZPtegjXgohsQa8YiT+Q==} engines: {node: '>=0.10.0'} dependencies: array-uniq: 1.0.3 @@ -17757,7 +17733,7 @@ packages: dev: true /fs-extra/0.30.0: - resolution: {integrity: sha1-8jP/zAjU2n1DLapEl3aYnbHfk/A=} + resolution: {integrity: sha512-UvSPKyhMn6LEd/WpUaV9C9t3zATuqoqfWc3QdPhPLb58prN9tqYPlPWi8Krxi44loBoUzlobqZ3+8tGpxxSzwA==} dependencies: graceful-fs: 4.2.10 jsonfile: 2.4.0 @@ -17916,6 +17892,7 @@ packages: function-bind: 1.1.1 has: 1.0.3 has-symbols: 1.0.3 + dev: true /get-intrinsic/1.1.2: resolution: {integrity: sha512-Jfm3OyCxHh9DJyc28qGk+JmfkpO41A4XkneDSujN9MDXrm4oDKdHvndhZ2dN94+ERNfkYJWDclW6k2L/ZGHjXA==} @@ -18424,7 +18401,7 @@ packages: engines: {node: '>=6'} /has-ansi/2.0.0: - resolution: {integrity: sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=} + resolution: {integrity: sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==} engines: {node: '>=0.10.0'} dependencies: ansi-regex: 2.1.1 @@ -18647,7 +18624,7 @@ packages: dev: false /home-or-tmp/2.0.0: - resolution: {integrity: sha1-42w/LSyufXRqhX440Y1fMqeILbg=} + resolution: {integrity: sha512-ycURW7oUxE2sNiPVw1HVEFsW+ecOpJ5zaj7eC0RlwhibhRBod20muUN8qu/gzx956YrLolVvs1MTXwKgC2rVEg==} engines: {node: '>=0.10.0'} dependencies: os-homedir: 1.0.2 @@ -19477,7 +19454,7 @@ packages: engines: {node: '>=0.10.0'} /is-fn/1.0.0: - resolution: {integrity: sha1-lUPV3nvPWwiiLsiiC65uKG1RDYw=} + resolution: {integrity: sha512-XoFPJQmsAShb3jEQRfzf2rqXavq7fIqF/jOekp308JlThqrODnMpweVSGilKTCXELfLhltGP2AGgbQGVP8F1dg==} engines: {node: '>=0.10.0'} dev: false @@ -20554,7 +20531,7 @@ packages: dev: false /js-sha3/0.6.1: - resolution: {integrity: sha1-W4n3enR3Z5h39YxKB1JAk0sflcA=} + resolution: {integrity: sha512-2OHj7sAZ9gnJS4lQsgIsTslmqVrNQdDC99bvwYGQKU1w6k/gwsTLeGBfWt8yHCuTOGqk7DXzuVlK8J+dDXnG7A==} dev: false /js-sha3/0.7.0: @@ -20653,7 +20630,7 @@ packages: dev: false /json-rpc-error/2.0.0: - resolution: {integrity: sha1-p6+cICg4tekFxyUOVH8a/3cligI=} + resolution: {integrity: sha512-EwUeWP+KgAZ/xqFpaP6YDAXMtCJi+o/QQpCQFIYyxr01AdADi2y413eM8hSqJcoQym9WMePAJWoaODEJufC4Ug==} dependencies: inherits: 2.0.4 dev: false @@ -20730,7 +20707,7 @@ packages: hasBin: true /jsonfile/2.4.0: - resolution: {integrity: sha1-NzaitCi4e72gzIO1P6PWM6NcKug=} + resolution: {integrity: sha512-PKllAqbgLgxHaj8TElYymKCAgrASebJrWpTnEkOaTowt23VKXXN0sUeriJ+eh7y6ufb/CC5ap11pz71/cM0hUw==} optionalDependencies: graceful-fs: 4.2.10 dev: false @@ -20850,7 +20827,7 @@ packages: engines: {node: '>=0.10.0'} /klaw/1.3.1: - resolution: {integrity: sha1-QIhDO0azsbolnXh4XY6W9zugJDk=} + resolution: {integrity: sha512-TED5xi9gGQjGpNnvRWknrwAB1eL5GciPfVFOt3Vk1OJCVDQbzuSfrF3hkUQKlsgKrG1F+0t5W0m+Fje1jIt8rw==} optionalDependencies: graceful-fs: 4.2.10 dev: false @@ -20948,7 +20925,7 @@ packages: dev: false /level-iterator-stream/1.3.1: - resolution: {integrity: sha1-5Dt4sagUPm+pek9IXrjqUwNS8u0=} + resolution: {integrity: sha512-1qua0RHNtr4nrZBgYlpV0qHHeHpcRRWTxEZJ8xsemoHAXNL5tbooh4tPEEqIqsbWCAJBmUmkwYK/sW5OrFjWWw==} dependencies: inherits: 2.0.4 level-errors: 1.0.5 @@ -21158,7 +21135,7 @@ packages: dev: false /lodash.assign/4.2.0: - resolution: {integrity: sha1-DZnzzNem0mHRm9rrkkUAXShYCOc=} + resolution: {integrity: sha512-hFuH8TY+Yji7Eja3mGiuAxBqLagejScbG8GbG0j6o9vzn0YL14My+ktnqtZgFTosKymC9/44wP6s7xyuLfnClw==} dev: false /lodash.clonedeep/4.5.0: @@ -21195,7 +21172,7 @@ packages: dev: true /lodash.values/4.3.0: - resolution: {integrity: sha1-o6bCsOvsxcLLocF+bmIP6BtT00c=} + resolution: {integrity: sha512-r0RwvdCv8id9TUblb/O7rYPwVy6lerCbcawrfdo9iC/1t1wsNMJknO79WNBgwkH0hIeJ08jmvvESbFpNb4jH0Q==} dev: false /lodash/4.17.21: @@ -21304,7 +21281,7 @@ packages: dev: false /ltgt/2.2.1: - resolution: {integrity: sha1-81ypHEk/e3PaDgdJUwTxezH4fuU=} + resolution: {integrity: sha512-AI2r85+4MquTw9ZYqabu4nMwy9Oftlfa/e/52t9IjtfG+mGBbTNdAoZ3RQKLHR6r0wQnwZnPIEh/Ya6XTWAKNA==} dev: false /magic-string/0.25.9: @@ -21462,7 +21439,7 @@ packages: dev: true /memdown/1.4.1: - resolution: {integrity: sha1-tOThkhdGZP+65BNhqlAPMRnv4hU=} + resolution: {integrity: sha512-iVrGHZB8i4OQfM155xx8akvG9FIj+ht14DX5CQkCTG4EHzZ3d3sgckIf/Lm9ivZalEsFuEVnWv2B2WZvbrro2w==} dependencies: abstract-leveldown: 2.7.2 functional-red-black-tree: 1.0.1 @@ -21507,7 +21484,7 @@ packages: dev: true /memorystream/0.3.1: - resolution: {integrity: sha1-htcJCzDORV1j+64S3aUaR93K+bI=} + resolution: {integrity: sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==} engines: {node: '>= 0.10.0'} dev: false @@ -22516,6 +22493,7 @@ packages: /object-inspect/1.12.0: resolution: {integrity: sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g==} + dev: true /object-inspect/1.12.2: resolution: {integrity: sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==} @@ -23538,7 +23516,7 @@ packages: dev: true /promise-to-callback/1.0.0: - resolution: {integrity: sha1-XSp0kBC/tn2WNZj805YHRqaP7vc=} + resolution: {integrity: sha512-uhMIZmKM5ZteDMfLgJnoSq9GCwsNKrYau73Awf1jIy6/eUcuuZ3P+CD9zUv0kJsIUbU+x6uLNIhXhLHDs1pNPA==} engines: {node: '>=0.10.0'} dependencies: is-fn: 1.0.0 @@ -24575,7 +24553,7 @@ packages: dev: true /regexpu-core/2.0.0: - resolution: {integrity: sha1-SdA4g3uNz4v6W5pCE5k45uoq4kA=} + resolution: {integrity: sha512-tJ9+S4oKjxY8IZ9jmjnp/mtytu1u3iyIQAfmI51IKWH6bFf7XR1ybtaO6j7INhZKXOTYADk7V5qxaqLkmNxiZQ==} dependencies: regenerate: 1.4.2 regjsgen: 0.2.0 @@ -24595,7 +24573,7 @@ packages: dev: true /regjsgen/0.2.0: - resolution: {integrity: sha1-bAFq3qxVT3WCP+N6wFuS1aTtsfc=} + resolution: {integrity: sha512-x+Y3yA24uF68m5GA+tBjbGYo64xXVJpbToBaWCoSNSc1hdk6dfctaRWrNFTVJZIIhL5GxW8zwjoixbnifnK59g==} dev: false /regjsgen/0.6.0: @@ -24603,7 +24581,7 @@ packages: dev: true /regjsparser/0.1.5: - resolution: {integrity: sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw=} + resolution: {integrity: sha512-jlQ9gYLfk2p3V5Ag5fYhA7fv7OHzd1KUH0PRP46xc3TgwjwgROIW572AfYg/X9kaNq/LJnu6oJcFRXlIrGoTRw==} hasBin: true dependencies: jsesc: 0.5.0 @@ -24793,7 +24771,7 @@ packages: engines: {node: '>=0.10.0'} /require-from-string/1.2.1: - resolution: {integrity: sha1-UpyczvJzgK3+yaL5ZbZJu+5jZBg=} + resolution: {integrity: sha512-H7AkJWMobeskkttHyhTVtS0fxpFLjxhbfMa6Bk3wimP7sdPRGL3EyCg3sAQenFfAe+xQ+oAc85Nmtvq0ROM83Q==} engines: {node: '>=0.10.0'} dev: false @@ -24911,7 +24889,7 @@ packages: dev: true /resumer/0.0.0: - resolution: {integrity: sha1-8ej0YeQGS6Oegq883CqMiT0HZ1k=} + resolution: {integrity: sha512-Fn9X8rX8yYF4m81rZCK/5VmrmsSbqS/i3rDLl6ZZHAXgC2nTAx3dhwG8q8odP/RmdLa2YrybDJaAMg+X1ajY3w==} dependencies: through: 2.3.8 dev: false @@ -25281,7 +25259,7 @@ packages: dev: true /semver-regex/1.0.0: - resolution: {integrity: sha1-kqSWkGX5xwxpR1PVUkj8aPj2Usk=} + resolution: {integrity: sha512-1vZcoRC+LPtHFkLUPyrabsATDSHerxW+hJBN8h04HZOZBuewbXaNROtUVdEPrTdZsWNq6sfsXDhd48GB2xTG4g==} engines: {node: '>=0.10.0'} dev: false @@ -25477,7 +25455,7 @@ packages: dev: false /set-immediate-shim/1.0.1: - resolution: {integrity: sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=} + resolution: {integrity: sha512-Li5AOqrZWCVA2n5kryzEmqai6bKSIvpz5oUJHPVj6+dsbD3X1ixtsY5tEnsaNpH3pFAHmG8eIHUrtEtohrg+UQ==} engines: {node: '>=0.10.0'} dev: false @@ -25585,7 +25563,7 @@ packages: dev: true /slash/1.0.0: - resolution: {integrity: sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=} + resolution: {integrity: sha512-3TYDR7xWt4dIqV2JauJr+EJeW356RXijHeUlO+8djJ+uBXPn8/2dpzBc8yQhh583sVvc9CvFAeQVgijsH+PNNg==} engines: {node: '>=0.10.0'} dev: false @@ -26240,7 +26218,7 @@ packages: dependencies: call-bind: 1.0.2 define-properties: 1.1.4 - es-abstract: 1.20.0 + es-abstract: 1.20.1 dev: false /string.prototype.trimend/1.0.4: @@ -26566,7 +26544,7 @@ packages: inherits: 2.0.4 is-regex: 1.1.4 minimist: 1.2.6 - object-inspect: 1.12.0 + object-inspect: 1.12.2 resolve: 1.22.0 resumer: 0.0.0 string.prototype.trim: 1.2.6 @@ -26923,7 +26901,7 @@ packages: engines: {node: '>=4'} /to-no-case/1.0.2: - resolution: {integrity: sha1-xyKQcWTvaxeBMsjmmTAhLRtKoWo=} + resolution: {integrity: sha512-Z3g735FxuZY8rodxV4gH7LxClE4H0hTIyHNIHdk+vpQxjLm0cwnKXq/OFVZ76SOQmto7txVcwSCwkU5kqp+FKg==} dev: false /to-object-path/0.3.0: @@ -26960,13 +26938,13 @@ packages: safe-regex: 1.1.0 /to-snake-case/1.0.0: - resolution: {integrity: sha1-znRpE4l5RgGah+Yu366upMYIq4w=} + resolution: {integrity: sha512-joRpzBAk1Bhi2eGEYBjukEWHOe/IvclOkiJl3DtA91jV6NwQ3MwXA4FHYeqk8BNp/D8bmi9tcNbRu/SozP0jbQ==} dependencies: to-space-case: 1.0.0 dev: false /to-space-case/1.0.0: - resolution: {integrity: sha1-sFLar7Gysp3HcM6gFj5ewOvJ/Bc=} + resolution: {integrity: sha512-rLdvwXZ39VOn1IxGL3V6ZstoTbwLRckQmn/U8ZDLuWwIXNpuZDhQ3AiRUlhTbOXFVE9C+dR51wM0CBDhk31VcA==} dependencies: to-no-case: 1.0.2 dev: false @@ -27019,7 +26997,7 @@ packages: engines: {node: '>=8'} /trim-right/1.0.1: - resolution: {integrity: sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=} + resolution: {integrity: sha512-WZGXGstmCWgeevgTL54hrCuw1dyMQIzWy7ZfqRJfSmJZBwklI15egmQytFP6bPidmw3M8d5yEowl1niq4vmqZw==} engines: {node: '>=0.10.0'} dev: false @@ -27306,7 +27284,7 @@ packages: resolution: {integrity: sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==} /tslint-eslint-rules/4.1.1_ew7ikuw7vzbxz2yx5mufkmltai: - resolution: {integrity: sha1-fDDniC8mvCdr/5HSOEl1xp2viLo=} + resolution: {integrity: sha512-QS9o6vNZ2XwWxW+DE5uXde1dhQ2ebNuvebjfF/P4b9uACPdzxQCkaHjNU5GO+0UqPuOmZNR7mwsBaSlWQfCgVg==} peerDependencies: tslint: ^5.0.0 dependencies: @@ -27362,9 +27340,9 @@ packages: dev: false /tsutils/1.9.1_typescript@2.9.2: - resolution: {integrity: sha1-ufmrROVa+WgYMdXyjQrur1x1DLA=} + resolution: {integrity: sha512-Z4MMpdLvxER0Wz+l9TM71URBKGoHKBzArEraOFmTp44jxzdqiG8oTCtpjiZ9YtFXNwWQfMv+g8VAxTlBEVS6yw==} peerDependencies: - typescript: '>=2.0.0 || >=2.0.0-dev || >=2.1.0-dev || >=2.2.0-dev || >=2.3.0-dev || >= 2.4.0-dev || 4' + typescript: '>=2.0.0 || >=2.0.0-dev || >=2.1.0-dev || >=2.2.0-dev || >=2.3.0-dev || >= 2.4.0-dev' peerDependenciesMeta: typescript: optional: true @@ -27375,7 +27353,7 @@ packages: /tsutils/2.29.0_typescript@2.9.2: resolution: {integrity: sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==} peerDependencies: - typescript: '>=2.1.0 || >=2.1.0-dev || >=2.2.0-dev || >=2.3.0-dev || >=2.4.0-dev || >=2.5.0-dev || >=2.6.0-dev || >=2.7.0-dev || >=2.8.0-dev || >=2.9.0-dev || >= 3.0.0-dev || >= 3.1.0-dev || 4' + typescript: '>=2.1.0 || >=2.1.0-dev || >=2.2.0-dev || >=2.3.0-dev || >=2.4.0-dev || >=2.5.0-dev || >=2.6.0-dev || >=2.7.0-dev || >=2.8.0-dev || >=2.9.0-dev || >= 3.0.0-dev || >= 3.1.0-dev' peerDependenciesMeta: typescript: optional: true @@ -27510,7 +27488,7 @@ packages: dev: false /typed-promisify/0.4.0: - resolution: {integrity: sha1-reHT0yEwdnuk71OFFixyBpgQXQ8=} + resolution: {integrity: sha512-Nhj1AwKD59L/vW+gqobaeyrxERI9p1FWCjGif1cPvZwYXOXkbyQehe7Rt92++a/WMw6BebxdplhZYaLkUH8NQQ==} dev: false /typedarray-to-buffer/3.1.5: @@ -27635,7 +27613,7 @@ packages: optional: true /uglify-to-browserify/1.0.2: - resolution: {integrity: sha1-bgkk1r2mta/jSeOabWMoUKD4grc=} + resolution: {integrity: sha512-vb2s1lYx2xBtUgy+ta+b2J/GLVUR+wmpINwHePmPRhOsIVCG2wDzKJ0n14GslH1BifsqVzSOwQhRaCAsZ/nI4Q==} dev: false optional: true @@ -28063,7 +28041,7 @@ packages: dev: true /uuid/3.0.1: - resolution: {integrity: sha1-ZUS7ot/ajBzxfmKaOjBeK7H+5sE=} + resolution: {integrity: sha512-tyhM7iisckwwmyHVFcjTzISz/R1ss/bRudNgHFYsgeu7j4JbhRvjE+Hbcpr9y5xh+b+HxeFjuToDT4i9kQNrtA==} deprecated: Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details. hasBin: true dev: false @@ -28107,7 +28085,7 @@ packages: dev: true /valid-url/1.0.9: - resolution: {integrity: sha1-HBRHm0DxOXp1eC8RXkCGRHQzogA=} + resolution: {integrity: sha512-QQDsV8OnSf5Uc30CKSwG9lnhMPe6exHtTXLRYX8uMwKENy640pU+2BgBL0LRbDh/eYRahNCS7aewCx0wf3NYVA==} dev: false /validate-npm-package-license/3.0.4: @@ -29218,7 +29196,7 @@ packages: dev: false /window-size/0.2.0: - resolution: {integrity: sha1-tDFbtCFKPXBY6+7okuE/ok2YsHU=} + resolution: {integrity: sha512-UD7d8HFA2+PZsbKyaOCEy8gMh1oDtHgJh1LfgjQ4zVXmYjAT/kvz3PueITKuqDiIXQe7yzpPnxX3lNc+AhQMyw==} engines: {node: '>= 0.10.0'} hasBin: true dev: false @@ -29555,7 +29533,7 @@ packages: dev: false /yargs-parser/2.4.1: - resolution: {integrity: sha1-hVaN488VD/SfpRgl8DqMiA3cxcQ=} + resolution: {integrity: sha512-9pIKIJhnI5tonzG6OnCFlz/yln8xHYcGl+pn3xR0Vzff0vzN1PbNRaelgfgRUwZ3s4i3jvxT9WhmUGL4whnasA==} dependencies: camelcase: 3.0.0 lodash.assign: 4.2.0 @@ -29685,7 +29663,7 @@ packages: dev: false /yargs/4.8.1: - resolution: {integrity: sha1-wMQpJMpKqmsObaFznfshZDn53cA=} + resolution: {integrity: sha512-LqodLrnIDM3IFT+Hf/5sxBnEGECrfdC1uIbgZeJmESCSo4HoCAaKEus8MylXHAkdacGc0ye+Qa+dpkuom8uVYA==} dependencies: cliui: 3.2.0 decamelize: 1.2.0 @@ -29750,7 +29728,7 @@ packages: dev: false /yn/2.0.0: - resolution: {integrity: sha1-5a2ryKz0CPY4X8dklWhMiOavaJo=} + resolution: {integrity: sha512-uTv8J/wiWTgUTg+9vLTi//leUl5vDQS6uii/emeTb2ssY7vl6QWf2fFbIIGjnhjvbdKlU0ed7QPgY1htTC86jQ==} engines: {node: '>=4'} dev: false @@ -29815,7 +29793,7 @@ packages: '@0xproject/utils': 0.1.3 '@0xproject/web3-wrapper': 0.1.14 '@types/lodash': 4.14.182 - '@types/node': 17.0.34 + '@types/node': 17.0.45 0x.js: 0.29.2 awesome-typescript-loader: 3.5.0_typescript@2.9.2 bn.js: 4.12.0