diff --git a/.vscode/settings.json b/.vscode/settings.json index 16e06291..cd384a8c 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -20,5 +20,6 @@ "javascript.updateImportsOnFileMove.enabled": "always", "typescript.updateImportsOnFileMove.enabled": "always", "eslint.useFlatConfig": true, - "githubPullRequests.overrideDefaultBranch": "dev" + "githubPullRequests.overrideDefaultBranch": "dev", + "githubIssues.issueBranchTitle": "${issueNumber}-${sanitizedIssueTitle}" } diff --git a/_raw/manifest/manifest.dev.json b/_raw/manifest/manifest.dev.json index 0eed9cb0..7351d005 100644 --- a/_raw/manifest/manifest.dev.json +++ b/_raw/manifest/manifest.dev.json @@ -2,7 +2,7 @@ "manifest_version": 3, "name": "FlowWallet-dev", "short_name": "__MSG_appName__", - "version": "2.6.2", + "version": "2.6.3", "default_locale": "en", "description": "__MSG_appDescription__", "icons": { diff --git a/_raw/manifest/manifest.pro.json b/_raw/manifest/manifest.pro.json index bfb19a48..548c3006 100644 --- a/_raw/manifest/manifest.pro.json +++ b/_raw/manifest/manifest.pro.json @@ -2,7 +2,7 @@ "manifest_version": 3, "name": "__MSG_appName__", "short_name": "__MSG_appName__", - "version": "2.6.2", + "version": "2.6.3", "default_locale": "en", "description": "__MSG_appDescription__", "icons": { diff --git a/build/analyze-dependencies.js b/build/analyze-dependencies.js new file mode 100644 index 00000000..81acec7d --- /dev/null +++ b/build/analyze-dependencies.js @@ -0,0 +1,129 @@ +const fs = require('fs'); +const path = require('path'); +const webpack = require('webpack'); +const webpackConfig = require('../webpack.config.js'); + +class DependencyAnalyzer { + constructor() { + this.dependencies = { + background: new Set(), + ui: new Set(), + content: new Set(), + shared: new Set(), + }; + } + + analyzeDependencies(stats) { + const modules = new Map(); + + // Use compilation.modules instead of chunk.getModules() + stats.compilation.modules.forEach((module) => { + if (!module.resource || !module.resource.includes('node_modules')) { + return; + } + + const pkg = module.resource.split('node_modules/')[1].split('/')[0]; + const chunks = Array.from(module.chunksIterable || []); + + chunks.forEach((chunk) => { + const target = chunk.name?.includes('background') + ? 'background' + : chunk.name?.includes('ui') + ? 'ui' + : chunk.name?.includes('content') + ? 'content' + : null; + + if (target) { + if (!modules.has(pkg)) { + modules.set(pkg, new Set()); + } + modules.get(pkg).add(target); + } + }); + }); + + // Categorize dependencies + modules.forEach((targets, pkg) => { + if (targets.size > 1) { + this.dependencies.shared.add(pkg); + } else { + const [target] = targets; + this.dependencies[target].add(pkg); + } + }); + + return this; + } + + generateReport() { + let report = '# Extension Dependencies Analysis\n\n'; + report += `> Generated on ${new Date().toLocaleString()}\n\n`; + + // Add summary + report += '## Summary\n\n'; + Object.entries(this.dependencies).forEach(([target, deps]) => { + report += `- **${target}**: ${deps.size} packages\n`; + }); + report += '\n'; + + // Add detailed lists + Object.entries(this.dependencies).forEach(([target, deps]) => { + report += `## ${target.charAt(0).toUpperCase() + target.slice(1)} Dependencies\n\n`; + if (deps.size === 0) { + report += '_No dependencies_\n\n'; + } else { + Array.from(deps) + .sort() + .forEach((dep) => { + report += `- \`${dep}\`\n`; + }); + report += '\n'; + } + }); + + return report; + } +} + +// Prepare build environment +console.log('Preparing build environment...'); + +// Copy manifest +fs.copyFileSync( + path.join(__dirname, '../_raw/manifest/manifest.dev.json'), + path.join(__dirname, '../_raw/manifest.json') +); + +// Clean dist directory +const distPath = path.join(__dirname, '../dist'); +if (!fs.existsSync(distPath)) { + fs.mkdirSync(distPath); +} else { + fs.rmSync(distPath, { recursive: true }); + fs.mkdirSync(distPath); +} + +// Copy _raw contents to dist +fs.cpSync(path.join(__dirname, '../_raw'), distPath, { recursive: true }); + +// Get webpack config using the same configuration as build:dev +const config = webpackConfig({ config: 'dev' }); + +// Run the analysis +console.log('Starting webpack build and analysis...'); +const analyzer = new DependencyAnalyzer(); + +webpack(config, (err, stats) => { + if (err || stats.hasErrors()) { + console.error('Build failed:', err || stats.toString()); + process.exit(1); + } + + console.log('Build complete, analyzing dependencies...'); + const report = analyzer.analyzeDependencies(stats).generateReport(); + + const reportPath = path.join(__dirname, '../extension-dependencies.md'); + fs.writeFileSync(reportPath, report); + console.log(`Analysis complete! Check ${reportPath}`); +}); diff --git a/build/analyze-imports.js b/build/analyze-imports.js new file mode 100644 index 00000000..05810eef --- /dev/null +++ b/build/analyze-imports.js @@ -0,0 +1,136 @@ +const fs = require('fs'); +const path = require('path'); +const glob = require('glob'); +const parser = require('@babel/parser'); +const traverse = require('@babel/traverse').default; + +class ImportAnalyzer { + constructor() { + this.imports = new Map(); // package -> Set of files using it + this.packageLocations = { + background: new Set(), + ui: new Set(), + content: new Set(), + }; + } + + analyzeFile(filePath) { + const content = fs.readFileSync(filePath, 'utf-8'); + const relativePath = path.relative(process.cwd(), filePath); + + try { + const ast = parser.parse(content, { + sourceType: 'module', + plugins: ['typescript', 'jsx'], + }); + + traverse(ast, { + ImportDeclaration: (path) => { + const importPath = path.node.source.value; + + // Only analyze external packages (not relative imports) + if (!importPath.startsWith('.') && !importPath.startsWith('@/')) { + const packageName = importPath.startsWith('@') + ? importPath.split('/').slice(0, 2).join('/') + : importPath.split('/')[0]; + + if (!this.imports.has(packageName)) { + this.imports.set(packageName, new Set()); + } + this.imports.get(packageName).add(relativePath); + + // Categorize by location + if (relativePath.includes('background/')) { + this.packageLocations.background.add(packageName); + } else if (relativePath.includes('ui/')) { + this.packageLocations.ui.add(packageName); + } else if (relativePath.includes('content/')) { + this.packageLocations.content.add(packageName); + } + } + }, + CallExpression(path) { + // Check for require() calls + if (path.node.callee.name === 'require') { + const arg = path.node.arguments[0]; + if (arg && arg.type === 'StringLiteral') { + const importPath = arg.value; + if (!importPath.startsWith('.') && !importPath.startsWith('@/')) { + const packageName = importPath.startsWith('@') + ? importPath.split('/').slice(0, 2).join('/') + : importPath.split('/')[0]; + + if (!this.imports.has(packageName)) { + this.imports.set(packageName, new Set()); + } + this.imports.get(packageName).add(relativePath); + + // Categorize by location + if (relativePath.includes('background/')) { + this.packageLocations.background.add(packageName); + } else if (relativePath.includes('ui/')) { + this.packageLocations.ui.add(packageName); + } else if (relativePath.includes('content/')) { + this.packageLocations.content.add(packageName); + } + } + } + } + }, + }); + } catch (error) { + console.warn(`Failed to parse ${relativePath}:`, error.message); + } + } + + generateReport() { + let report = '# Dependency Usage Analysis\n\n'; + report += `> Generated on ${new Date().toLocaleString()}\n\n`; + + // Summary by location + report += '## Package Usage by Location\n\n'; + Object.entries(this.packageLocations).forEach(([location, packages]) => { + report += `### ${location.charAt(0).toUpperCase() + location.slice(1)}\n\n`; + Array.from(packages) + .sort() + .forEach((pkg) => { + const usageCount = this.imports.get(pkg).size; + report += `- \`${pkg}\` (${usageCount} ${usageCount === 1 ? 'file' : 'files'})\n`; + }); + report += '\n'; + }); + + // Detailed usage + report += '## Detailed Package Usage\n\n'; + Array.from(this.imports.entries()) + .sort(([a], [b]) => a.localeCompare(b)) + .forEach(([pkg, files]) => { + report += `### \`${pkg}\`\n\n`; + Array.from(files) + .sort() + .forEach((file) => { + report += `- ${file}\n`; + }); + report += '\n'; + }); + + return report; + } +} + +// Run the analysis +console.log('Starting import analysis...'); +const analyzer = new ImportAnalyzer(); + +// Find all TypeScript and JavaScript files +const files = glob.sync('src/**/*.{ts,tsx,js,jsx}', { + ignore: ['**/node_modules/**', '**/dist/**'], +}); + +files.forEach((file) => { + analyzer.analyzeFile(file); +}); + +const report = analyzer.generateReport(); +fs.writeFileSync('dependency-usage.md', report); +console.log('Analysis complete! Check dependency-usage.md'); diff --git a/src/background/controller/wallet.ts b/src/background/controller/wallet.ts index 1eec4920..f2633672 100644 --- a/src/background/controller/wallet.ts +++ b/src/background/controller/wallet.ts @@ -11,9 +11,10 @@ import { getAuth } from 'firebase/auth'; import web3, { TransactionError } from 'web3'; import eventBus from '@/eventBus'; +import { type FeatureFlags } from '@/shared/types/feature-types'; import { isValidEthereumAddress, withPrefix } from '@/shared/utils/address'; import { getHashAlgo, getSignAlgo } from '@/shared/utils/algo'; -// eslint-disable-next-line import/order,no-restricted-imports +// eslint-disable-next-line no-restricted-imports import { findAddressWithNetwork } from '@/ui/utils/modules/findAddressWithPK'; import { keyringService, @@ -54,7 +55,7 @@ import DisplayKeyring from '../service/keyring/display'; import type { NFTData, NFTModel, StorageInfo, WalletResponse } from '../service/networkModel'; import type { ConnectedSite } from '../service/permission'; import type { Account } from '../service/preference'; -import { StorageEvaluator } from '../service/storage-evaluator'; +import { type EvaluateStorageResult, StorageEvaluator } from '../service/storage-evaluator'; import type { UserInfoStore } from '../service/user'; import defaultConfig from '../utils/defaultConfig.json'; import { getStoragedAccount } from '../utils/getStoragedAccount'; @@ -3875,6 +3876,10 @@ export class WalletController extends BaseController { } }; + getFeatureFlags = async (): Promise => { + return openapiService.getFeatureFlags(); + }; + allowLilicoPay = async (): Promise => { const isFreeGasFeeKillSwitch = await storage.get('freeGas'); const isFreeGasFeeEnabled = await storage.get('lilicoPayer'); @@ -4072,24 +4077,17 @@ export class WalletController extends BaseController { transferAmount?: number; // amount in coins coin?: string; // coin name movingBetweenEVMAndFlow?: boolean; // are we moving between EVM and Flow? - } = {}): Promise<{ - isStorageSufficient: boolean; - isStorageSufficientAfterAction: boolean; - storageInfo: StorageInfo; - }> => { + } = {}): Promise => { const address = await this.getCurrentAddress(); - const { isStorageSufficient, isStorageSufficientAfterAction, storageInfo } = - await this.storageEvaluator.evaluateStorage( - address!, - transferAmount, - coin, - movingBetweenEVMAndFlow - ); - return { - isStorageSufficient, - isStorageSufficientAfterAction, - storageInfo, - }; + const isFreeGasFeeEnabled = await this.allowLilicoPay(); + const result = await this.storageEvaluator.evaluateStorage( + address!, + transferAmount, + coin, + movingBetweenEVMAndFlow, + isFreeGasFeeEnabled + ); + return result; }; // Tracking stuff diff --git a/src/background/service/conditions-evaluator.ts b/src/background/service/conditions-evaluator.ts index da4fdc74..c609dfe0 100644 --- a/src/background/service/conditions-evaluator.ts +++ b/src/background/service/conditions-evaluator.ts @@ -1,7 +1,8 @@ +import type { NewsConditionType } from '@/shared/types/news-types'; + import { userWalletService } from '../service'; import { StorageEvaluator } from '../service/storage-evaluator'; -import type { NewsConditionType } from './networkModel'; import openapi from './openapi'; const CURRENT_VERSION = chrome.runtime.getManifest().version; @@ -25,6 +26,11 @@ class ConditionsEvaluator { if (!currentAddress) return false; return this.evaluateStorageCondition(currentAddress); } + case 'insufficientBalance': { + const currentAddress = userWalletService.getCurrentAddress(); + if (!currentAddress) return false; + return this.evaluateBalanceCondition(currentAddress); + } case 'unknown': default: @@ -66,6 +72,12 @@ class ConditionsEvaluator { const { isStorageSufficient } = await storageEvaluator.evaluateStorage(address); return !isStorageSufficient; } + + async evaluateBalanceCondition(address: string): Promise { + const storageEvaluator = new StorageEvaluator(); + const { isBalanceSufficient } = await storageEvaluator.evaluateStorage(address); + return !isBalanceSufficient; + } } export default new ConditionsEvaluator(); diff --git a/src/background/service/networkModel.ts b/src/background/service/networkModel.ts index 51ed4674..4ce5e514 100644 --- a/src/background/service/networkModel.ts +++ b/src/background/service/networkModel.ts @@ -357,36 +357,10 @@ export interface DeviceInfo { device_info: DeviceInfoRequest; } -/* - * News items - */ -export type NewsType = 'message' | 'image'; - -export type NewsPriority = 'urgent' | 'high' | 'medium' | 'low'; - -export type NewsDisplayType = - | 'once' // show once - | 'click' // close it when user click on it - | 'expiry'; // it will display until it expired - -export type NewsConditionType = - | 'unknown' - | 'canUpgrade' - | 'isIOS' - | 'isAndroid' - | 'isWeb' - | 'insufficientStorage'; - -export interface NewsItem { - id: string; - priority: NewsPriority; - type: NewsType; - title: string; - body?: string; - icon?: string; - image?: string; - url?: string; - expiryTime: Date; - displayType: NewsDisplayType; - conditions?: NewsConditionType[]; -} +export { + type NewsItem, + type NewsPriority, + type NewsType, + type NewsDisplayType, + type NewsConditionType, +} from '@/shared/types/news-types'; diff --git a/src/background/service/openapi.ts b/src/background/service/openapi.ts index 54297c71..a9298677 100644 --- a/src/background/service/openapi.ts +++ b/src/background/service/openapi.ts @@ -16,6 +16,7 @@ import type { TokenInfo } from 'flow-native-token-registry'; import log from 'loglevel'; import { storage } from '@/background/webapi'; +import { type FeatureFlagKey, type FeatureFlags } from '@/shared/types/feature-types'; import { isValidFlowAddress, isValidEthereumAddress } from '@/shared/utils/address'; import { getStringFromHashAlgo, getStringFromSignAlgo } from '@/shared/utils/algo'; import { getPeriodFrequency } from '@/shared/utils/getPeriodFrequency'; @@ -1332,16 +1333,23 @@ class OpenApiService { return tokenList.find((item) => item.id === contract_name); }; + getFeatureFlags = async (): Promise => { + try { + const config = await remoteFetch.remoteConfig(); + return config.features; + } catch (err) { + console.error(err); + } + // By default, all feature flags are disabled + return {}; + }; + getFeatureFlag = async (featureFlag: FeatureFlagKey): Promise => { + const flags = await this.getFeatureFlags(); + return !!flags[featureFlag]; + }; + getSwapInfo = async (): Promise => { - remoteFetch - .remoteConfig() - .then((res) => { - return res.features.swap; - }) - .catch((err) => { - console.log('getNFTCollectionInfo -->', err); - }); - return false; + return (await this.getFeatureFlags()).swap; }; // @ts-ignore diff --git a/src/background/service/storage-evaluator.ts b/src/background/service/storage-evaluator.ts index 8aa2a17d..a1bcb691 100644 --- a/src/background/service/storage-evaluator.ts +++ b/src/background/service/storage-evaluator.ts @@ -1,53 +1,72 @@ -import * as fcl from '@onflow/fcl'; - import { openapiService } from '../service'; import type { StorageInfo } from './networkModel'; -interface EvaluateStorageResult { + +export type EvaluateStorageResult = { isStorageSufficient: boolean; + isBalanceSufficient: boolean; isStorageSufficientAfterAction: boolean; storageInfo: StorageInfo; -} +}; + export class StorageEvaluator { + private static MINIMUM_FLOW_BALANCE = 0.001; private static MINIMUM_STORAGE_BUFFER = 10000; // minimum required storage buffer (10,000 bytes) private static FIXED_MOVE_FEE = 0.001; - private static AVERAGE_TX_FEE = 0.001; + private static AVERAGE_TX_FEE = 0.0005; private static BYTES_PER_FLOW = 100 * 1024 * 1024; // 100 MB async evaluateStorage( address: string, sendAmount?: number, coin?: string, - movingBetweenEVMAndFlow?: boolean + movingBetweenEVMAndFlow?: boolean, + freeGas?: boolean ): Promise { // Get storage info from openapi service const storageInfo = await openapiService.getStorageInfo(address); - const remainingStorage = storageInfo.capacity - storageInfo.used; const isStorageSufficient = remainingStorage >= StorageEvaluator.MINIMUM_STORAGE_BUFFER; - let noStorageAfterAction = false; + // Calculate the flow balance that is used by the storage calculation + // I don't "love" this approach as it involves a division, but it + // avoids having to figure out which flow balance is used by the storage calculation + const flowBalanceAffectingStorage = storageInfo.capacity / StorageEvaluator.BYTES_PER_FLOW; + + // Check if the flow balance is sufficient + const isBalanceSufficient = + flowBalanceAffectingStorage >= StorageEvaluator.MINIMUM_FLOW_BALANCE; + + let isStorageSufficientAfterAction = true; + + // Check feature flag + const FEATURE_FLAG_TX_WARNING_PREDICTION = + await openapiService.getFeatureFlag('tx_warning_prediction'); - if (isStorageSufficient) { - // Check if there is enough storage after the action - if (sendAmount !== undefined) { - // This is the amount of flow that will be used by the transaction - const flowUsed = - (coin === 'flow' ? sendAmount : 0) + - (movingBetweenEVMAndFlow ? StorageEvaluator.FIXED_MOVE_FEE : 0) + - StorageEvaluator.AVERAGE_TX_FEE; + if (FEATURE_FLAG_TX_WARNING_PREDICTION) { + // The feature is enabled, so we need to check if there is enough storage after the action + if (isStorageSufficient) { + // Check if there is enough storage after the action + if (sendAmount !== undefined) { + // This is the amount of flow that will be used by the transaction + const flowUsed = + (coin === 'flow' ? sendAmount : 0) + + (movingBetweenEVMAndFlow ? StorageEvaluator.FIXED_MOVE_FEE : 0) + + (freeGas ? 0 : StorageEvaluator.AVERAGE_TX_FEE); - const storageAffected = flowUsed * StorageEvaluator.BYTES_PER_FLOW; - const remainingStorageAfterAction = storageInfo.available - storageAffected; + const storageAffected = flowUsed * StorageEvaluator.BYTES_PER_FLOW; + const remainingStorageAfterAction = storageInfo.available - storageAffected; - noStorageAfterAction = - remainingStorageAfterAction < StorageEvaluator.MINIMUM_STORAGE_BUFFER; + isStorageSufficientAfterAction = + remainingStorageAfterAction >= StorageEvaluator.MINIMUM_STORAGE_BUFFER; + } } } return { isStorageSufficient, - isStorageSufficientAfterAction: !noStorageAfterAction, + isBalanceSufficient, + isStorageSufficientAfterAction, storageInfo, }; } diff --git a/src/background/service/userWallet.ts b/src/background/service/userWallet.ts index 031cd4ee..7eecb693 100644 --- a/src/background/service/userWallet.ts +++ b/src/background/service/userWallet.ts @@ -3,6 +3,7 @@ import * as fcl from '@onflow/fcl'; import { getApp } from 'firebase/app'; import { getAuth, signInAnonymously } from 'firebase/auth'; +import { type ActiveChildType } from '@/shared/types/wallet-types'; import { withPrefix } from '@/shared/utils/address'; import { getHashAlgo, getSignAlgo } from '@/shared/utils/algo'; // eslint-disable-next-line no-restricted-imports @@ -30,7 +31,7 @@ interface UserWalletStore { childAccount: ChildAccount; network: string; monitor: string; - activeChild: any; + activeChild: ActiveChildType; evmEnabled: boolean; } @@ -126,11 +127,11 @@ class UserWallet { this.store.childAccount = wallet; }; - setActiveWallet = (key: any) => { + setActiveWallet = (key: ActiveChildType) => { this.store.activeChild = key; }; - getActiveWallet = () => { + getActiveWallet = (): ActiveChildType => { return this.store.activeChild; }; diff --git a/src/content-script/script.js b/src/content-script/script.js index 055c9116..2a93471a 100644 --- a/src/content-script/script.js +++ b/src/content-script/script.js @@ -1,5 +1,5 @@ // @ts-nocheck -import { WalletUtils } from '@onflow/fcl'; +//import { WalletUtils } from '@onflow/fcl'; // import { nanoid } from 'nanoid'; // const channelName = nanoid(); @@ -32,4 +32,15 @@ const service = { }, }; -WalletUtils.injectExtService(service); +function injectExtService(service) { + if (service.type === 'authn' && service.endpoint !== null) { + if (!Array.isArray(window.fcl_extensions)) { + window.fcl_extensions = []; + } + window.fcl_extensions.push(service); + } else { + console.warn('Authn service is required'); + } +} + +injectExtService(service); diff --git a/src/shared/types/feature-types.ts b/src/shared/types/feature-types.ts new file mode 100644 index 00000000..9c1a5a1f --- /dev/null +++ b/src/shared/types/feature-types.ts @@ -0,0 +1,8 @@ +// Example of features we use, but could be any string key as we add more +export type FeatureFlagKey = 'free_gas' | 'swap' | 'tx_warning_prediction' | string; + +// Feature flags +export type FeatureFlags = { + // Other feature flags + [key: FeatureFlagKey]: boolean; +}; diff --git a/src/shared/types/news-types.ts b/src/shared/types/news-types.ts new file mode 100644 index 00000000..16d2b59e --- /dev/null +++ b/src/shared/types/news-types.ts @@ -0,0 +1,34 @@ +/* + * News items + */ +export type NewsType = 'message' | 'image'; + +export type NewsPriority = 'urgent' | 'high' | 'medium' | 'low'; + +export type NewsDisplayType = + | 'once' // show once + | 'click' // close it when user click on it + | 'expiry'; // it will display until it expired + +export type NewsConditionType = + | 'unknown' + | 'canUpgrade' + | 'isIOS' + | 'isAndroid' + | 'isWeb' + | 'insufficientStorage' + | 'insufficientBalance'; + +export interface NewsItem { + id: string; + priority: NewsPriority; + type: NewsType; + title: string; + body?: string; + icon?: string; + image?: string; + url?: string; + expiryTime: Date; + displayType: NewsDisplayType; + conditions?: NewsConditionType[]; +} diff --git a/src/shared/types/wallet-types.ts b/src/shared/types/wallet-types.ts new file mode 100644 index 00000000..08287862 --- /dev/null +++ b/src/shared/types/wallet-types.ts @@ -0,0 +1,5 @@ +// Matches exactly 16 hex characters, with optional 0x prefix +export type FlowAddress = `0x${string & { length: 16 }}` | `${string & { length: 16 }}`; + +// ActiveChildType is the type of the active child in the wallet. It can be 'evm', a FlowAddress, or null. +export type ActiveChildType = 'evm' | FlowAddress | null; diff --git a/src/ui/FRWAssets/image/movenft.png b/src/ui/FRWAssets/image/movenft.png index 2c3f768c..7735fc57 100644 Binary files a/src/ui/FRWAssets/image/movenft.png and b/src/ui/FRWAssets/image/movenft.png differ diff --git a/src/ui/FRWComponent/WarningStorageLowSnackbar.tsx b/src/ui/FRWComponent/WarningStorageLowSnackbar.tsx index 8f1e9adc..eb7bc96c 100644 --- a/src/ui/FRWComponent/WarningStorageLowSnackbar.tsx +++ b/src/ui/FRWComponent/WarningStorageLowSnackbar.tsx @@ -22,7 +22,6 @@ export const WarningStorageLowSnackbar = ({ return null; } return ( - // {}} alertIcon={warningIcon} message={message} /> - <> + {}} alertIcon={warningIcon} message={message} /> ); }; diff --git a/src/ui/utils/useStorageCheck.ts b/src/ui/utils/useStorageCheck.ts index 9ce9f625..c9dc1dce 100644 --- a/src/ui/utils/useStorageCheck.ts +++ b/src/ui/utils/useStorageCheck.ts @@ -36,15 +36,19 @@ export const useStorageCheck = ({ storageInfo: StorageInfo; }> => { try { - const { isStorageSufficient, isStorageSufficientAfterAction, storageInfo } = - await wallet.checkStorageStatus({ - transferAmount: transferAmount, - coin, - movingBetweenEVMAndFlow, - }); + const { + isStorageSufficient, + isBalanceSufficient, + isStorageSufficientAfterAction, + storageInfo, + } = await wallet.checkStorageStatus({ + transferAmount: transferAmount, + coin, + movingBetweenEVMAndFlow, + }); return { - sufficient: isStorageSufficient, + sufficient: isStorageSufficient && isBalanceSufficient, sufficientAfterAction: isStorageSufficientAfterAction, storageInfo, }; diff --git a/src/ui/views/AddWelcome/Sync/SyncQr.tsx b/src/ui/views/AddWelcome/Sync/SyncQr.tsx index 19e15144..fa4b678d 100644 --- a/src/ui/views/AddWelcome/Sync/SyncQr.tsx +++ b/src/ui/views/AddWelcome/Sync/SyncQr.tsx @@ -76,7 +76,6 @@ const SyncQr = ({ const usewallet = useWallet(); const classes = useStyles(); const [Uri, setUri] = useState(''); - const [web3wallet, setWeb3Wallet] = useState(null); const [loading, setShowLoading] = useState(false); const [session, setSession] = useState(); const [mnemonic, setMnemonic] = useState(bip39.generateMnemonic()); @@ -260,31 +259,24 @@ const SyncQr = ({ }, }); - // Open QRCode modal if a URI was returned (i.e. we're not connecting an existing pairing). if (uri) { console.log('uri ', uri); await setUri(uri); - // Await session approval from the wallet. const session = await approval(); await onSessionConnected(session); - console.log('session ', session); sendRequest(wallet, session.topic); - - // onSessionConnect(session) - // Close the QRCode modal in case it was open. } } catch (e) { console.error(e); } - await setWeb3Wallet(wallet); - console.log('web3wallet', web3wallet); } catch (e) { console.error(e); } }; + createWeb3Wallet(); - }, [_subscribeToEvents, currentNetwork, onSessionConnected, sendRequest, web3wallet]); + }, [_subscribeToEvents, currentNetwork, onSessionConnected, sendRequest]); return ( <> diff --git a/src/ui/views/Dashboard/index.tsx b/src/ui/views/Dashboard/index.tsx index eb5a4ffa..8cbc413c 100644 --- a/src/ui/views/Dashboard/index.tsx +++ b/src/ui/views/Dashboard/index.tsx @@ -49,45 +49,53 @@ const Dashboard = ({ value, setValue }) => { setValue(index); }; - const fetchAll = useCallback(async () => { - setLoading(true); - //todo fix cadence loading - await wallet.getCadenceScripts(); - const [network, userDomain] = await Promise.all([ - wallet.getNetwork(), - wallet.fetchUserDomain(), - ]); - const isChild = await wallet.getActiveWallet(); - - if (isChild === 'evm') { - setIsEvm(true); - } - const env: string = process.env.NODE_ENV!; - const firebaseConfig = getFirbaseConfig(); - console.log(process.env.NODE_ENV); - // const firebaseProductionConfig = prodConig; - - const app = initializeApp(firebaseConfig, env); - const remoteConfig = getRemoteConfig(app); - console.log('remoteConfig ', app); - fetchAndActivate(remoteConfig) - .then((res) => { - console.log('res ', remoteConfig); - - console.log('Remote Config values fetched and activated'); - }) - .catch((error) => { - console.error('Error fetching remote config:', error); - }); - - setNetwork(network); - setDomain(userDomain); - setLoading(false); - }, [wallet]); - useEffect(() => { - fetchAll(); - }, [fetchAll, wallet]); + console.log('useEffect - fetchAll'); + let isMounted = true; + + const fetchAll = async () => { + //todo fix cadence loading + await wallet.getCadenceScripts(); + const [network, userDomain] = await Promise.all([ + wallet.getNetwork(), + wallet.fetchUserDomain(), + ]); + const isChild = await wallet.getActiveWallet(); + + if (isChild === 'evm') { + setIsEvm(true); + } + const env: string = process.env.NODE_ENV!; + const firebaseConfig = getFirbaseConfig(); + + const app = initializeApp(firebaseConfig, env); + const remoteConfig = getRemoteConfig(app); + console.log('remoteConfig ', app); + fetchAndActivate(remoteConfig) + .then((res) => { + console.log('res ', remoteConfig); + + console.log('Remote Config values fetched and activated'); + }) + .catch((error) => { + console.error('Error fetching remote config:', error); + }); + + return { network, userDomain }; + }; + + fetchAll().then(({ network, userDomain }) => { + if (isMounted) { + setNetwork(network); + setDomain(userDomain); + setLoading(false); + } + }); + + return () => { + isMounted = false; + }; + }, [wallet]); return (
diff --git a/src/ui/views/EvmMove/MoveFromChild/index.tsx b/src/ui/views/EvmMove/MoveFromChild/index.tsx index 04a9d720..d65104b6 100644 --- a/src/ui/views/EvmMove/MoveFromChild/index.tsx +++ b/src/ui/views/EvmMove/MoveFromChild/index.tsx @@ -4,7 +4,7 @@ import React, { useState, useEffect, useCallback } from 'react'; import { useHistory } from 'react-router-dom'; import wallet from '@/background/controller/wallet'; -import { withPrefix } from '@/shared/utils/address'; +import { isValidEthereumAddress, withPrefix } from '@/shared/utils/address'; import { WarningStorageLowSnackbar } from '@/ui/FRWComponent/WarningStorageLowSnackbar'; import { useStorageCheck } from '@/ui/utils/useStorageCheck'; import type { CoinItem } from 'background/service/coinList'; @@ -92,10 +92,13 @@ const MoveFromChild = (props: TransferConfirmationProps) => { const [errorType, setErrorType] = useState(null); const [exceed, setExceed] = useState(false); const [minAmount, setMinAmount] = useState(0.001); + const { sufficient: isSufficient, sufficientAfterAction } = useStorageCheck({ transferAmount: Number(amount) || 0, coin: currentCoin, - movingBetweenEVMAndFlow: true, + // Rendering this component means we are moving from a FLOW child account + // We are moving to userInfo.address. Check if it's an EVM address + movingBetweenEVMAndFlow: isValidEthereumAddress(userInfo.address), }); const isLowStorage = isSufficient !== undefined && !isSufficient; // isSufficient is undefined when the storage check is not yet completed @@ -105,6 +108,8 @@ const MoveFromChild = (props: TransferConfirmationProps) => { // const walletList = await storage.get('userWallet'); setLoading(true); const token = await usewallet.getCurrentCoin(); + + // This is the main wallet address const wallet = await usewallet.getMainWallet(); const network = await usewallet.getNetwork(); setNetwork(network); diff --git a/src/ui/views/EvmMove/MoveFromEvm/index.tsx b/src/ui/views/EvmMove/MoveFromEvm/index.tsx index af8a3b04..af1efe65 100644 --- a/src/ui/views/EvmMove/MoveFromEvm/index.tsx +++ b/src/ui/views/EvmMove/MoveFromEvm/index.tsx @@ -4,7 +4,7 @@ import React, { useState, useEffect, useCallback } from 'react'; import { useHistory } from 'react-router-dom'; import wallet from '@/background/controller/wallet'; -import { withPrefix } from '@/shared/utils/address'; +import { isValidEthereumAddress, withPrefix } from '@/shared/utils/address'; import { WarningStorageLowSnackbar } from '@/ui/FRWComponent/WarningStorageLowSnackbar'; import { useStorageCheck } from '@/ui/utils/useStorageCheck'; import type { CoinItem } from 'background/service/coinList'; @@ -80,7 +80,9 @@ const MoveFromEvm = (props: TransferConfirmationProps) => { const { sufficient: isSufficient, sufficientAfterAction } = useStorageCheck({ transferAmount: Number(amount) || 0, coin: currentCoin, - movingBetweenEVMAndFlow: true, + // Rendering this component means we are moving from an EVM account + // If we are not moving to an EVM account, we are moving to a FLOW account + movingBetweenEVMAndFlow: !isValidEthereumAddress(userInfo.address), }); const isLowStorage = isSufficient !== undefined && !isSufficient; // isSufficient is undefined when the storage check is not yet completed diff --git a/src/ui/views/EvmMove/MoveFromFlow/index.tsx b/src/ui/views/EvmMove/MoveFromFlow/index.tsx index 2a832969..219aff5a 100644 --- a/src/ui/views/EvmMove/MoveFromFlow/index.tsx +++ b/src/ui/views/EvmMove/MoveFromFlow/index.tsx @@ -96,6 +96,7 @@ const MoveFromFlow = (props: TransferConfirmationProps) => { const { sufficient: isSufficient, sufficientAfterAction } = useStorageCheck({ transferAmount: Number(amount) || 0, coin: currentCoin, + // We are moving from a Flow account to an EVM account movingBetweenEVMAndFlow: true, }); diff --git a/src/ui/views/EvmMove/MoveFromParent/index.tsx b/src/ui/views/EvmMove/MoveFromParent/index.tsx index da0eaacb..bd56fd99 100644 --- a/src/ui/views/EvmMove/MoveFromParent/index.tsx +++ b/src/ui/views/EvmMove/MoveFromParent/index.tsx @@ -5,7 +5,7 @@ import React, { useState, useEffect, useCallback } from 'react'; import { useHistory } from 'react-router-dom'; import wallet from '@/background/controller/wallet'; -import { withPrefix } from '@/shared/utils/address'; +import { isValidEthereumAddress, withPrefix } from '@/shared/utils/address'; import { WarningStorageLowSnackbar } from '@/ui/FRWComponent/WarningStorageLowSnackbar'; import { useStorageCheck } from '@/ui/utils/useStorageCheck'; import { type CoinItem } from 'background/service/coinList'; @@ -96,7 +96,9 @@ const MoveFromParent = (props: TransferConfirmationProps) => { const { sufficient: isSufficient, sufficientAfterAction } = useStorageCheck({ transferAmount: Number(amount) || 0, coin: currentCoin, - movingBetweenEVMAndFlow: true, + // Determine if the transfer is between EVM and Flow + movingBetweenEVMAndFlow: + isValidEthereumAddress(userInfo.address) !== isValidEthereumAddress(childUserInfo.address), }); const isLowStorage = isSufficient !== undefined && !isSufficient; // isSufficient is undefined when the storage check is not yet completed diff --git a/src/ui/views/MoveBoard/MoveEvm/index.tsx b/src/ui/views/MoveBoard/MoveEvm/index.tsx index 8e6ccfa5..ee0f1790 100644 --- a/src/ui/views/MoveBoard/MoveEvm/index.tsx +++ b/src/ui/views/MoveBoard/MoveEvm/index.tsx @@ -40,7 +40,8 @@ const MoveEvm = (props: MoveBoardProps) => { const [selectedAccount, setSelectedChildAccount] = useState(null); const { sufficient: isSufficient, sufficientAfterAction } = useStorageCheck({ transferAmount: 0, - movingBetweenEVMAndFlow: true, + // We are moving within the EVM network, the flag should be false + movingBetweenEVMAndFlow: false, }); const isLowStorage = isSufficient !== undefined && !isSufficient; // isSufficient is undefined when the storage check is not yet completed diff --git a/src/ui/views/MoveBoard/MoveFromChild/index.tsx b/src/ui/views/MoveBoard/MoveFromChild/index.tsx index 8c793f5a..81bd156d 100644 --- a/src/ui/views/MoveBoard/MoveFromChild/index.tsx +++ b/src/ui/views/MoveBoard/MoveFromChild/index.tsx @@ -61,8 +61,9 @@ const MoveFromChild = (props: MoveBoardProps) => { // console.log('props.loggedInAccounts', props.current) const { sufficient: isSufficient, sufficientAfterAction } = useStorageCheck({ transferAmount: 0, + // Check if the selected account is an EVM address movingBetweenEVMAndFlow: selectedAccount - ? isValidEthereumAddress(selectedAccount!['address']) + ? isValidEthereumAddress(selectedAccount['address']) : false, }); diff --git a/src/ui/views/MoveBoard/index.tsx b/src/ui/views/MoveBoard/index.tsx index 142f5a1b..db73bc58 100644 --- a/src/ui/views/MoveBoard/index.tsx +++ b/src/ui/views/MoveBoard/index.tsx @@ -1,33 +1,22 @@ -import React, { useState, useEffect } from 'react'; -import { - Box, - Button, - ListItemButton, - Typography, - Drawer, - IconButton, - ListItem, - ListItemIcon, - ListItemText, - Avatar, - CardMedia, -} from '@mui/material'; import CloseIcon from '@mui/icons-material/Close'; -import { useWallet } from 'ui/utils'; +import { Box, Button, Typography, Drawer, IconButton, CardMedia } from '@mui/material'; +import React, { useState, useEffect, useCallback } from 'react'; import { useHistory } from 'react-router-dom'; -import ErrorModel from '../../FRWComponent/PopupModal/errorModel'; -import moveftbg from 'ui/FRWAssets/svg/moveftbg.svg'; -import movenftbg from 'ui/FRWAssets/svg/movenftbg.svg'; + +import { type ActiveChildType } from '@/shared/types/wallet-types'; +import LLComingSoon from '@/ui/FRWComponent/LLComingSoonWarning'; import moveft from 'ui/FRWAssets/image/moveft.png'; import movenft from 'ui/FRWAssets/image/movenft.png'; +import moveftbg from 'ui/FRWAssets/svg/moveftbg.svg'; +import movenftbg from 'ui/FRWAssets/svg/movenftbg.svg'; +import { useWallet } from 'ui/utils'; + +import MoveFromEvm from '../EvmMove/MoveFromEvm'; +import MoveFromFlow from '../EvmMove/MoveFromFlow'; + import MoveEvm from './MoveEvm'; -import MoveToChild from './MoveToChild'; import MoveFromChild from './MoveFromChild'; -import MoveFromFlow from '../EvmMove/MoveFromFlow'; -import MoveFromEvm from '../EvmMove/MoveFromEvm'; -import MoveFromChildFT from '../EvmMove/MoveFromChild'; -import LLComingSoon from '@/ui/FRWComponent/LLComingSoonWarning'; -import { add } from 'lodash'; +import MoveToChild from './MoveToChild'; interface MoveBoardProps { showMoveBoard: boolean; @@ -38,21 +27,20 @@ interface MoveBoardProps { const MoveBoard = (props: MoveBoardProps) => { const usewallet = useWallet(); - const history = useHistory(); const [showSelectNft, setSelectBoard] = useState(false); const [moveFtOpen, setMoveFt] = useState(false); - const [childType, setChildType] = useState(''); + const [childType, setChildType] = useState(null); const [network, setNetwork] = useState(''); const [alertOpen, setAlertOpen] = useState(false); // console.log('props.loggedInAccounts', props.current) - const requestChildType = async () => { + const requestChildType = useCallback(async () => { const result = await usewallet.getActiveWallet(); const currentNetwork = await usewallet.getNetwork(); setNetwork(currentNetwork); setChildType(result); - }; + }, [usewallet]); const closeFullPage = () => { setMoveFt(false); @@ -61,10 +49,11 @@ const MoveBoard = (props: MoveBoardProps) => { useEffect(() => { requestChildType(); - }, []); + }, [requestChildType]); const renderMoveComponent = () => { if (childType === 'evm') { + // EVM child address return ( { ); } - if (childType && childType !== 'evm') { + if (childType !== null) { + // We are moving FROM a flow child address return ( { ); } + // There is no child active, so we are moving TO a flow child address return ( { const { sufficient: isSufficient, sufficientAfterAction } = useStorageCheck({ transferAmount: 0, - movingBetweenEVMAndFlow: true, // hard to tell if the user is sending to an evm address or not. True for now. + // Check if the recipient is an EVM address + movingBetweenEVMAndFlow: isValidEthereumAddress(props?.data?.contact?.address), }); const isLowStorage = isSufficient !== undefined && !isSufficient; // isSufficient is undefined when the storage check is not yet completed @@ -258,7 +260,7 @@ const SendNFTConfirmation = (props: SendNFTConfirmationProps) => { const checkChild = useCallback(async () => { const isChild = await wallet.getActiveWallet(); - setIsChild(isChild); + setIsChild(isChild !== null); }, [wallet]); const initializeContract = useCallback(async () => { diff --git a/src/ui/views/Send/SendAmount.tsx b/src/ui/views/Send/SendAmount.tsx index 87c82bbc..b76ee8a4 100644 --- a/src/ui/views/Send/SendAmount.tsx +++ b/src/ui/views/Send/SendAmount.tsx @@ -3,6 +3,7 @@ import { Box, Button, Typography, IconButton, CardMedia } from '@mui/material'; import React, { useState, useEffect, useCallback } from 'react'; import { useHistory, useLocation } from 'react-router-dom'; +import { type ActiveChildType } from '@/shared/types/wallet-types'; import { withPrefix } from '@/shared/utils/address'; import { LLHeader } from '@/ui/FRWComponent'; import SlideRelative from '@/ui/FRWComponent/SlideRelative'; @@ -56,7 +57,7 @@ const SendAmount = () => { const [network, setNetwork] = useState('mainnet'); const [coinInfo, setCoinInfo] = useState(EMPTY_COIN); const [isLoading, setLoading] = useState(false); - const [childType, setChildType] = useState(''); + const [childType, setChildType] = useState(null); const [minAmount, setMinAmount] = useState(0); const setUserMinAmount = useCallback( diff --git a/src/ui/views/Send/SendEth/EvmConfirmation.tsx b/src/ui/views/Send/SendEth/EvmConfirmation.tsx index 40d835ca..7e144a41 100644 --- a/src/ui/views/Send/SendEth/EvmConfirmation.tsx +++ b/src/ui/views/Send/SendEth/EvmConfirmation.tsx @@ -34,12 +34,12 @@ const ToEthConfirmation = (props: ToEthConfirmationProps) => { const [count, setCount] = useState(0); const transferAmount = props?.data?.amount ? parseFloat(props.data.amount) : undefined; - // TODO: check if this is correct - const movingBetweenEVMAndFlow = true; + const { sufficient: isSufficient, sufficientAfterAction } = useStorageCheck({ transferAmount, coin: props.data?.coinInfo?.coin, - movingBetweenEVMAndFlow, + // the transfer is within the EVM network, the flag should be false + movingBetweenEVMAndFlow: false, }); const isLowStorage = isSufficient !== undefined && !isSufficient; // isSufficient is undefined when the storage check is not yet completed diff --git a/src/ui/views/Send/SendEth/ToEthConfirmation.tsx b/src/ui/views/Send/SendEth/ToEthConfirmation.tsx index c04a33c1..6c0198bb 100644 --- a/src/ui/views/Send/SendEth/ToEthConfirmation.tsx +++ b/src/ui/views/Send/SendEth/ToEthConfirmation.tsx @@ -34,7 +34,8 @@ const ToEthConfirmation = (props: ToEthConfirmationProps) => { const { sufficient: isSufficient, sufficientAfterAction } = useStorageCheck({ transferAmount: 0, coin: props.data?.coinInfo?.coin, - movingBetweenEVMAndFlow: true, + // the transfer is within the EVM network, the flag should be false + movingBetweenEVMAndFlow: false, }); const isLowStorage = isSufficient !== undefined && !isSufficient; // isSufficient is undefined when the storage check is not yet completed diff --git a/src/ui/views/Send/SendEth/index.tsx b/src/ui/views/Send/SendEth/index.tsx index 29c331bb..87bb0f1e 100644 --- a/src/ui/views/Send/SendEth/index.tsx +++ b/src/ui/views/Send/SendEth/index.tsx @@ -3,6 +3,7 @@ import React, { useState, useEffect, useCallback } from 'react'; import { useHistory, useLocation } from 'react-router-dom'; import Web3 from 'web3'; +import { type ActiveChildType } from '@/shared/types/wallet-types'; import { withPrefix, isValidEthereumAddress } from '@/shared/utils/address'; import { LLHeader } from '@/ui/FRWComponent'; import SlideRelative from '@/ui/FRWComponent/SlideRelative'; @@ -56,7 +57,7 @@ const SendEth = () => { const [network, setNetwork] = useState('mainnet'); const [coinInfo, setCoinInfo] = useState(EMPTY_COIN); const [isLoading, setLoading] = useState(false); - const [childType, setChildType] = useState(''); + const [childType, setChildType] = useState(null); const [minAmount, setMinAmount] = useState(0); const [erc20Contract, setErc20Contract] = useState(null); const [web3, setWeb3] = useState(null); diff --git a/src/ui/views/Send/TransferConfirmation.tsx b/src/ui/views/Send/TransferConfirmation.tsx index db42d399..44293cca 100644 --- a/src/ui/views/Send/TransferConfirmation.tsx +++ b/src/ui/views/Send/TransferConfirmation.tsx @@ -33,8 +33,10 @@ const TransferConfirmation = (props: TransferConfirmationProps) => { const [count, setCount] = useState(0); const transferAmount = props?.data?.amount ? parseFloat(props.data.amount) : undefined; - // TODO: check if this is correct - const movingBetweenEVMAndFlow = true; + + // This component is only used for sending Flow on the Flow network + const movingBetweenEVMAndFlow = false; + const { sufficient: isSufficient, sufficientAfterAction: isSufficientAfterAction } = useStorageCheck({ transferAmount, diff --git a/src/ui/views/Setting/index.tsx b/src/ui/views/Setting/index.tsx index 5964e717..c5f03733 100644 --- a/src/ui/views/Setting/index.tsx +++ b/src/ui/views/Setting/index.tsx @@ -91,7 +91,7 @@ const SettingTab = () => { // setSending(true); const activeChild = await usewallet.getActiveWallet(); if (activeChild) { - setIsActive(activeChild); + setIsActive(activeChild !== null); } const keyrings = await usewallet.checkMnemonics(); await setIsKeyphrase(keyrings); diff --git a/src/ui/views/SwitchUnlock/index.tsx b/src/ui/views/SwitchUnlock/index.tsx index 80497e4d..a89e30c6 100644 --- a/src/ui/views/SwitchUnlock/index.tsx +++ b/src/ui/views/SwitchUnlock/index.tsx @@ -35,6 +35,9 @@ const useStyles = makeStyles(() => ({ }, })); +const DEFAULT_PASSWORD = + process.env.NODE_ENV === 'development' ? process.env.DEV_PASSWORD || '' : ''; + const SwitchUnlock = () => { const wallet = useWallet(); const classes = useStyles(); @@ -43,7 +46,7 @@ const SwitchUnlock = () => { // const { t } = useTranslation(); const [showError, setShowError] = useState(false); const [isLoading, setLoading] = useState(false); - const [password, setPassword] = useState(''); + const [password, setPassword] = useState(DEFAULT_PASSWORD); const [resetPop, setResetPop] = useState(false); useEffect(() => { diff --git a/src/ui/views/Sync/SyncQr.tsx b/src/ui/views/Sync/SyncQr.tsx index 2befa86e..12c98ef5 100644 --- a/src/ui/views/Sync/SyncQr.tsx +++ b/src/ui/views/Sync/SyncQr.tsx @@ -69,7 +69,6 @@ const SyncQr = ({ handleClick, savedUsername, confirmMnemonic, setUsername }) => const usewallet = useWallet(); const classes = useStyles(); const [Uri, setUri] = useState(''); - const [web3wallet, setWeb3Wallet] = useState(null); const [loading, setShowLoading] = useState(false); const [session, setSession] = useState(); const [mnemonic, setMnemonic] = useState(bip39.generateMnemonic()); @@ -249,31 +248,25 @@ const SyncQr = ({ handleClick, savedUsername, confirmMnemonic, setUsername }) => }, }); - // Open QRCode modal if a URI was returned (i.e. we're not connecting an existing pairing). if (uri) { console.log('uri ', uri); await setUri(uri); - // Await session approval from the wallet. const session = await approval(); await onSessionConnected(session); console.log('session ', session); sendRequest(wallet, session.topic); - - // onSessionConnect(session) - // Close the QRCode modal in case it was open. } } catch (e) { console.error(e); } - await setWeb3Wallet(wallet); - console.log('web3wallet', web3wallet); } catch (e) { console.error(e); } }; createWeb3Wallet(); - }, [_subscribeToEvents, currentNetwork, onSessionConnected, sendRequest, web3wallet]); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); return ( <> diff --git a/src/ui/views/TokenDetail/index.tsx b/src/ui/views/TokenDetail/index.tsx index c704c7ff..aabbb8a7 100644 --- a/src/ui/views/TokenDetail/index.tsx +++ b/src/ui/views/TokenDetail/index.tsx @@ -9,6 +9,7 @@ import { useHistory, useParams } from 'react-router-dom'; import type { PriceProvider } from '@/background/service/networkModel'; import { storage } from '@/background/webapi'; +import { type ActiveChildType } from '@/shared/types/wallet-types'; import LLComingSoon from '@/ui/FRWComponent/LLComingSoonWarning'; import StorageUsageCard from '@/ui/FRWComponent/StorageUsageCard'; import tips from 'ui/FRWAssets/svg/tips.svg'; @@ -54,7 +55,7 @@ const TokenDetail = () => { const [tokenInfo, setTokenInfo] = useState(undefined); const [providers, setProviders] = useState([]); const [childAccount, setChildAccount] = useState({}); - const [childType, setChildType] = useState(''); + const [childType, setChildType] = useState(null); const [alertOpen, setAlertOpen] = useState(false); const [menuOpen, setMenuOpen] = useState(false); @@ -62,10 +63,6 @@ const TokenDetail = () => { setMenuOpen(!menuOpen); }; - const handleMenuClose = () => { - setMenuOpen(false); - }; - const handleDeleteEFT = async () => { const network = await usewallet.getNetwork(); @@ -165,6 +162,7 @@ const TokenDetail = () => { /> ); } else if (childType) { + // We are moving from a FLOW child account return ( { const [coinData, setCoinData] = useState([]); const [accessible, setAccessible] = useState([]); const [balance, setBalance] = useState('$0.00'); - const [childType, setChildType] = useState(''); + const [childType, setChildType] = useState(null); const [alertOpen, setAlertOpen] = useState(false); const [, setChildAccount] = useState({}); const [txCount, setTxCount] = useState(''); @@ -127,7 +128,7 @@ const WalletTab = ({ network }) => { const pollTimer = setInterval(async () => { const nowTime = new Date().getTime(); const data = await func(); - if (data.length > 2 || nowTime - startTime >= endTime) { + if ((data && data.length > 2) || nowTime - startTime >= endTime) { if (pollTimer) { clearInterval(pollTimer); } @@ -236,15 +237,13 @@ const WalletTab = ({ network }) => { const fetchWallet = useCallback(async () => { // If childType is 'evm', handle it first - const isChild = await wallet.getActiveWallet(); - if (isChild === 'evm') { + const activeChild = await wallet.getActiveWallet(); + if (activeChild === 'evm') { const storageData = await wallet.refreshEvmList(expiry_time); sortWallet(storageData); return; - } - - // If not 'evm', check if it's active or not - if (!isActive && isChild !== 'evm') { + // If not 'evm', check if it's not active + } else if (!isActive) { const ftResult = await wallet.checkAccessibleFt(address); if (ftResult) { setAccessible(ftResult); @@ -414,7 +413,7 @@ const WalletTab = ({ network }) => { }} > - {(!childType || childType === '' || childType === 'evm') && ( + {(!childType || childType === null || childType === 'evm') && (