From e7a64be58b6f3ab5c4057ebd50e95fc6fcf7bfbc Mon Sep 17 00:00:00 2001 From: dominhquang Date: Thu, 31 Oct 2024 11:45:50 +0700 Subject: [PATCH] [Issue-1826] Implement Export Account --- .../common/SelectExportType/index.tsx | 50 +- src/messaging/accounts/export.ts | 36 + src/messaging/accounts/index.ts | 1 + src/messaging/index.ts | 642 +----------------- src/screens/Account/AccountDetail/index.tsx | 7 +- src/screens/Account/AccountExport/index.tsx | 49 +- .../Account/ExportAllAccount/index.tsx | 100 +-- 7 files changed, 120 insertions(+), 765 deletions(-) create mode 100644 src/messaging/accounts/export.ts diff --git a/src/components/common/SelectExportType/index.tsx b/src/components/common/SelectExportType/index.tsx index 5969deeca..117a1f44d 100644 --- a/src/components/common/SelectExportType/index.tsx +++ b/src/components/common/SelectExportType/index.tsx @@ -1,5 +1,3 @@ -import { isEthereumAddress } from '@polkadot/util-crypto'; -import { AccountJson } from '@subwallet/extension-base/background/types'; import { BackgroundIcon } from 'components/design-system-ui'; import React, { useCallback, useMemo } from 'react'; import { Text, View, ViewStyle } from 'react-native'; @@ -8,7 +6,7 @@ import { useSubWalletTheme } from 'hooks/useSubWalletTheme'; import SelectExportTypeStyles from './style'; import { FileJs, IconProps, Leaf, QrCode, Wallet } from 'phosphor-react-native'; import i18n from 'utils/i18n/i18n'; -import AlertBox from 'components/design-system-ui/alert-box/simple'; +import { AccountActions, AccountProxy } from '@subwallet/extension-base/types'; export enum ExportType { JSON_FILE = 'json-file', @@ -30,14 +28,14 @@ interface ExportTypeItem { interface SelectAccountTypeProps { selectedItems: ExportType[]; setSelectedItems: React.Dispatch>; - account: AccountJson | null; + accountProxy: AccountProxy | null; title?: string; styles?: ViewStyle; loading: boolean; } export const SelectExportType = (props: SelectAccountTypeProps) => { - const { title, selectedItems, setSelectedItems, styles, account, loading } = props; + const { title, selectedItems, setSelectedItems, styles, accountProxy, loading } = props; const theme = useSubWalletTheme().swThemes; const _style = SelectExportTypeStyles(theme); @@ -67,7 +65,7 @@ export const SelectExportType = (props: SelectAccountTypeProps) => { label: i18n.exportAccount.exportSeedPhrase, onClick: onClickItem(ExportType.SEED_PHRASE), bgColor: theme['orange-7'], - disable: !account || account.isExternal || !account.isMasterAccount, + disable: !accountProxy || !accountProxy.accountActions.includes(AccountActions.EXPORT_MNEMONIC), hidden: false, }, { @@ -76,7 +74,7 @@ export const SelectExportType = (props: SelectAccountTypeProps) => { label: i18n.exportAccount.exportJsonFile, onClick: onClickItem(ExportType.JSON_FILE), bgColor: theme['geekblue-7'], - disable: !account || !!account.isExternal, + disable: !accountProxy || !accountProxy.accountActions.includes(AccountActions.EXPORT_JSON), hidden: false, }, { @@ -85,8 +83,8 @@ export const SelectExportType = (props: SelectAccountTypeProps) => { label: i18n.exportAccount.exportPrivateKey, onClick: onClickItem(ExportType.PRIVATE_KEY), bgColor: theme['magenta-7'], - disable: !account || account.isExternal || !isEthereumAddress(account.address), - hidden: !isEthereumAddress(account?.address || ''), + disable: !accountProxy || !accountProxy.accountActions.includes(AccountActions.EXPORT_PRIVATE_KEY), + hidden: false, }, { icon: QrCode, @@ -94,36 +92,24 @@ export const SelectExportType = (props: SelectAccountTypeProps) => { label: i18n.exportAccount.exportQRCode, onClick: onClickItem(ExportType.QR_CODE), bgColor: theme['green-7'], - disable: !account || !!account?.isExternal, + disable: !accountProxy || !accountProxy.accountActions.includes(AccountActions.EXPORT_QR), hidden: false, }, ], - [onClickItem, theme, account], + [accountProxy, onClickItem, theme], ); - const accountType = useMemo(() => { - if (account?.isExternal) { - if (account?.isReadOnly) { - return 'watch-only'; - } else if (account?.isHardware && account?.hardwareType === 'ledger') { - return 'ledger'; - } else { - return 'QR-signer'; - } - } - }, [account]); - return ( - {!!account?.isExternal && ( - - - - )} + {/*{!!account?.isExternal && (*/} + {/* */} + {/* */} + {/* */} + {/*)}*/} {title && ( {title} diff --git a/src/messaging/accounts/export.ts b/src/messaging/accounts/export.ts new file mode 100644 index 000000000..62938233c --- /dev/null +++ b/src/messaging/accounts/export.ts @@ -0,0 +1,36 @@ +// Copyright 2019-2022 @subwallet/extension-koni-ui authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +import { ResponseAccountExportPrivateKey } from '@subwallet/extension-base/background/KoniTypes'; +import { + RequestAccountBatchExportV2, + RequestExportAccountProxyMnemonic, + ResponseExportAccountProxyMnemonic, +} from '@subwallet/extension-base/types'; +import { KeyringPair$Json } from '@subwallet/keyring/types'; +import { KeyringPairs$Json } from '@subwallet/ui-keyring/types'; +import { sendMessage } from 'messaging/index'; + +// JSON +export async function exportAccount(address: string, password: string): Promise<{ exportedJson: KeyringPair$Json }> { + return sendMessage('pri(accounts.export.json)', { address, password }); +} + +export async function exportAccountPrivateKey( + address: string, + password: string, +): Promise { + return sendMessage('pri(accounts.export.privateKey)', { address, password }); +} + +export async function exportAccountBatch( + request: RequestAccountBatchExportV2, +): Promise<{ exportedJson: KeyringPairs$Json }> { + return sendMessage('pri(accounts.export.json.batch)', request); +} + +export async function exportAccountMnemonic( + request: RequestExportAccountProxyMnemonic, +): Promise { + return sendMessage('pri(accounts.export.mnemonic)', request); +} diff --git a/src/messaging/accounts/index.ts b/src/messaging/accounts/index.ts index c67476a2f..42e10e512 100644 --- a/src/messaging/accounts/index.ts +++ b/src/messaging/accounts/index.ts @@ -1,3 +1,4 @@ export * from './validate'; export * from './create'; export * from './json'; +export * from './export'; diff --git a/src/messaging/index.ts b/src/messaging/index.ts index ac1f01d99..07a83ceb6 100644 --- a/src/messaging/index.ts +++ b/src/messaging/index.ts @@ -8,8 +8,6 @@ import { WebRunnerStatus } from 'providers/contexts'; import { WebviewError, WebviewNotReadyError, WebviewResponseError } from '../errors/WebViewErrors'; import EventEmitter from 'eventemitter3'; import type { - AccountJson, - AuthorizeRequest, MessageTypes, MessageTypesWithNoSubscriptions, MessageTypesWithNullRequest, @@ -18,93 +16,45 @@ import type { RequestSignatures, RequestTypes, ResponseAuthorizeList, - ResponseDeriveValidate, - ResponseJsonGetAccountInfo, - ResponseSigningIsLocked, ResponseTypes, - SeedLengths, SubscriptionMessageTypes, } from '@subwallet/extension-base/background/types'; import { - AccountsWithCurrentAddress, - ActiveCronAndSubscriptionMap, AmountData, AmountDataWithId, AssetSettingUpdateReq, - BrowserConfirmationType, - ChainStakingMetadata, ConfirmationDefinitions, - ConfirmationsQueue, ConfirmationType, - CronReloadRequest, - CronServiceType, - CrowdloanJson, CurrencyType, CurrentAccountInfo, - KeyringState, LanguageType, MobileData, - NftCollection, - NftJson, NftTransactionRequest, - NominatorMetadata, - OptionInputAddress, - PriceJson, - RequestAccountBatchExportV2, - RequestAccountMeta, RequestApproveConnectWalletSession, RequestAuthorizationBlock, RequestAuthorizationPerSite, - RequestBondingSubmit, RequestCampaignBannerComplete, RequestChangeMasterPassword, RequestConnectWalletConnect, - RequestCronAndSubscriptionAction, RequestCrossChainTransfer, - RequestDeriveCreateMultiple, RequestDeriveCreateV3, - RequestDeriveValidateV2, RequestFreeBalance, - RequestGetDeriveAccounts, - RequestGetTransaction, - RequestInitCronAndSubscription, RequestKeyringExportMnemonic, RequestMaxTransferable, RequestMigratePassword, - RequestParseEvmContractInput, RequestParseTransactionSubstrate, RequestQrSignEvm, RequestQrSignSubstrate, RequestRejectConnectWalletSession, RequestResetWallet, - RequestSettingsType, RequestSigningApprovePasswordV2, - RequestStakePoolingBonding, - RequestStakePoolingUnbonding, - RequestSubscribeBalance, - RequestSubscribeBalancesVisibility, - RequestSubscribeCrowdloan, - RequestSubscribeNft, - RequestSubscribePrice, - RequestSubscribeStaking, - RequestSubscribeStakingReward, RequestTransfer, - RequestTuringCancelStakeCompound, - RequestTuringStakeCompound, - RequestUnbondingSubmit, RequestUnlockKeyring, - RequestYieldFastWithdrawal, ResolveAddressToDomainRequest, ResolveDomainRequest, - ResponseAccountExportPrivateKey, - ResponseAccountIsLocked, - ResponseAccountMeta, ResponseChangeMasterPassword, - ResponseDeriveValidateV2, - ResponseGetDeriveAccounts, ResponseKeyringExportMnemonic, ResponseMigratePassword, - ResponseParseEvmContractInput, ResponseParseTransactionSubstrate, ResponseQrParseRLP, ResponseQrSignEvm, @@ -112,18 +62,12 @@ import { ResponseResetWallet, ResponseSubscribeHistory, ResponseUnlockKeyring, - StakingJson, - StakingRewardJson, StakingType, - SubscriptionServiceType, - ThemeNames, TransactionHistoryItem, - UiSettings, ValidateNetworkResponse, ValidatorInfo, } from '@subwallet/extension-base/background/KoniTypes'; import { - BalanceJson, Message, NominationPoolInfo, OptimalYieldPathParams, @@ -137,20 +81,12 @@ import { RequestYieldWithdrawal, TokenSpendingApprovalParams, ValidateYieldProcessParams, - YieldPoolInfo, } from '@subwallet/extension-base/types'; -import type { KeyringPair$Json } from '@subwallet/keyring/types'; -import type { KeyringAddress, KeyringPairs$Json } from '@subwallet/ui-keyring/types'; +import type { KeyringAddress } from '@subwallet/ui-keyring/types'; import type { HexString } from '@polkadot/util/types'; -import type { KeypairType } from '@polkadot/util-crypto/types'; import { MetadataDef } from '@subwallet/extension-inject/types'; +import { SWTransactionResponse } from '@subwallet/extension-base/services/transaction-service/types'; import { - SWTransactionResponse, - SWTransactionResult, -} from '@subwallet/extension-base/services/transaction-service/types'; -import { - _ChainApiStatus, - _ChainState, _NetworkUpsertParams, _ValidateCustomAssetRequest, _ValidateCustomAssetResponse, @@ -159,7 +95,6 @@ import { _ChainAsset, _ChainInfo } from '@subwallet/chain-list/types'; import { AuthUrls } from '@subwallet/extension-base/services/request-service/types'; import { _getKnownHashes } from 'utils/defaultChains'; import { needBackup, triggerBackup } from 'utils/storage'; -import { WindowOpenParams } from '@subwallet/extension-base/background/types'; import { RequestOptimalTransferProcess } from '@subwallet/extension-base/services/balance-service/helpers'; import { CommonOptimalPath } from '@subwallet/extension-base/types/service-base'; import { createRegistry } from '@subwallet/extension-base/utils'; @@ -224,10 +159,6 @@ export function getMessageType(message: string): MessageType { return 'UNKNOWN'; } -export function getHandlerId(message: string, id?: string): string { - return `${getMessageType(message)}|${id ? id : getId()}`; -} - function isDappHandle(id: string): boolean { if (!handlerTypeMap[id]) { return false; @@ -478,65 +409,8 @@ export function subscribeMessage { - // @ts-ignore - return sendMessage('mobile(ping)', null); -} - -export async function initCronAndSubscription( - request: RequestInitCronAndSubscription, -): Promise { - return sendMessage('mobile(cronAndSubscription.init)', request); -} - -export async function subscribeActiveCronAndSubscriptionServiceMap( - callback: (data: ActiveCronAndSubscriptionMap) => void, - handlerId?: string, -): Promise { - return sendMessage('mobile(cronAndSubscription.activeService.subscribe)', null, callback, handlerId); -} - export * from './accounts'; -export async function startCronAndSubscriptionServices(request: RequestCronAndSubscriptionAction): Promise { - return sendMessage('mobile(cronAndSubscription.start)', request); -} - -export async function stopCronAndSubscriptionServices(request: RequestCronAndSubscriptionAction): Promise { - return sendMessage('mobile(cronAndSubscription.stop)', request); -} - -export async function restartCronAndSubscriptionServices(request: RequestCronAndSubscriptionAction): Promise { - return sendMessage('mobile(cronAndSubscription.restart)', request); -} - -export async function reloadCron(request: CronReloadRequest): Promise { - return sendMessage('pri(cron.reload)', request); -} - -export async function startCronServices(request: CronServiceType[]): Promise { - return sendMessage('mobile(cron.start)', request); -} - -export async function stopCronServices(request: CronServiceType[]): Promise { - return sendMessage('mobile(cron.stop)', request); -} - -export async function restartCronServices(request: CronServiceType[]): Promise { - return sendMessage('mobile(cron.restart)', request); -} - -export async function startSubscriptionServices(request: SubscriptionServiceType[]): Promise { - return sendMessage('mobile(subscription.start)', request); -} - -export async function stopSubscriptionServices(request: SubscriptionServiceType[]): Promise { - return sendMessage('mobile(subscription.stop)', request); -} - -export async function restartSubscriptionServices(request: SubscriptionServiceType[]): Promise { - return sendMessage('mobile(subscription.restart)', request); -} export async function mobileBackup(): Promise { return sendMessage('mobile(storage.backup)'); } @@ -550,10 +424,6 @@ export async function editAccount(address: string, name: string): Promise { - return sendMessage('pri(accounts.show)', { address, isShowing }); -} - export async function saveCurrentAccountAddress(data: RequestCurrentAccountAddress): Promise { return sendMessage('pri(accounts.saveCurrentProxy)', data); } @@ -562,76 +432,18 @@ export async function toggleBalancesVisibility(): Promise { return sendMessage('pri(settings.changeBalancesVisibility)', null); } -export async function saveAccountAllLogo( - accountAllLogo: string, - callback: (data: RequestSettingsType) => void, -): Promise { - return sendMessage('pri(settings.saveAccountAllLogo)', accountAllLogo, callback); -} - -export async function saveBrowserConfirmationType(type: BrowserConfirmationType): Promise { - return sendMessage('pri(settings.saveBrowserConfirmationType)', type); -} - -export async function saveTheme(theme: ThemeNames): Promise { - return sendMessage('pri(settings.saveTheme)', theme); -} - export async function saveLanguage(lang: LanguageType): Promise { return sendMessage('pri(settings.saveLanguage)', { language: lang }); } -export async function subscribeSettings( - data: RequestSubscribeBalancesVisibility, - callback: (data: UiSettings) => void, -): Promise { - return sendMessage('pri(settings.subscribe)', data, callback); -} - export async function savePriceCurrency(currency: CurrencyType): Promise { return sendMessage('pri(settings.savePriceCurrency)', { currency }); } -export async function tieAccount(address: string, genesisHash: string | null): Promise { - return sendMessage('pri(accounts.tie)', { address, genesisHash }); -} - -export async function exportAccount(address: string, password: string): Promise<{ exportedJson: KeyringPair$Json }> { - return sendMessage('pri(accounts.export)', { address, password }); -} - -export async function exportAccountPrivateKey( - address: string, - password: string, -): Promise { - return sendMessage('pri(accounts.exportPrivateKey)', { address, password }); -} - -export async function exportAccounts( - addresses: string[], - password: string, -): Promise<{ exportedJson: KeyringPairs$Json }> { - return sendMessage('pri(accounts.batchExport)', { addresses, password }); -} - -export async function exportAccountsV2( - request: RequestAccountBatchExportV2, -): Promise<{ exportedJson: KeyringPairs$Json }> { - return sendMessage('pri(accounts.batchExportV2)', request); -} - -export async function validateAccount(address: string, password: string): Promise { - return sendMessage('pri(accounts.validate)', { address, password }); -} - export async function forgetAccount(address: string, lockAfter = false): Promise { return sendMessage('pri(accounts.forget)', { address, lockAfter }); } -export async function approveAuthRequest(id: string): Promise { - return sendMessage('pri(authorize.approve)', { id }); -} - export async function approveAuthRequestV2(id: string, accounts: string[]): Promise { return sendMessage('pri(authorize.approveV2)', { id, accounts }); } @@ -644,10 +456,6 @@ export async function cancelSignRequest(id: string): Promise { return sendMessage('pri(signing.cancel)', { id }); } -export async function isSignLocked(id: string): Promise { - return sendMessage('pri(signing.isLocked)', { id }); -} - export async function approveSignPassword(id: string, savePass: boolean, password?: string): Promise { return sendMessage('pri(signing.approve.password)', { id, password, savePass }); } @@ -664,132 +472,6 @@ export async function approveSignSignature(id: string, signature: HexString): Pr return sendMessage('pri(signing.approve.signature)', { id, signature }); } -export async function createAccountExternal(name: string, address: string, genesisHash: string): Promise { - return sendMessage('pri(accounts.create.external)', { address, genesisHash, name }); -} - -export async function createAccountHardware( - address: string, - hardwareType: string, - accountIndex: number, - addressOffset: number, - name: string, - genesisHash: string, -): Promise { - return sendMessage('pri(accounts.create.hardware)', { - accountIndex, - address, - addressOffset, - genesisHash, - hardwareType, - name, - }); -} - -export async function createAccountSuri( - name: string, - password: string, - suri: string, - type?: KeypairType, - genesisHash?: string, -): Promise { - return sendMessage('pri(accounts.create.suri)', { genesisHash, name, password, suri, type }); -} - -export async function createSeed( - length?: SeedLengths, - seed?: string, - type?: KeypairType, -): Promise<{ address: string; seed: string }> { - return sendMessage('pri(seed.create)', { length, seed, type }); -} - -export async function getAllMetadata(): Promise { - return sendMessage('pri(metadata.list)'); -} - -// export async function getMetadata(genesisHash?: string | null, isPartial = false): Promise { -// if (!genesisHash) { -// return null; -// } -// -// // const chains = await getNetworkMap(); -// const parsedChains = _getKnownHashes({}); -// -// let request = getSavedMeta(genesisHash); -// -// if (!request) { -// request = sendMessage('pri(metadata.get)', genesisHash || null); -// setSavedMeta(genesisHash, request); -// } -// -// const def = await request; -// -// if (def) { -// return metadataExpand(def, isPartial); -// } else if (isPartial) { -// const chain = parsedChains.find(chain => chain.genesisHash === genesisHash); -// -// if (chain) { -// return metadataExpand( -// { -// ...chain, -// specVersion: 0, -// tokenDecimals: 15, -// tokenSymbol: 'Unit', -// types: {}, -// }, -// isPartial, -// ); -// } -// } -// -// return null; -// } -// -// export async function getChainMetadata(genesisHash?: string | null): Promise { -// if (!genesisHash) { -// return null; -// } -// -// // const chains = await getNetworkMap(); -// const parsedChains = _getKnownNetworks({}); -// -// let request = getSavedMeta(genesisHash); -// -// if (!request) { -// request = sendMessage('pri(metadata.get)', genesisHash || null); -// setSavedMeta(genesisHash, request); -// } -// -// const def = await request; -// -// if (def) { -// return metadataExpand(def, false); -// } else { -// const chain = parsedChains.find(chain => chain.genesisHash === genesisHash); -// -// if (chain) { -// return metadataExpand( -// { -// specVersion: 0, -// tokenDecimals: 15, -// tokenSymbol: 'Unit', -// types: {}, -// ...chain, -// }, -// false, -// ); -// } -// } -// -// return null; -// } - -export async function rejectAuthRequest(id: string): Promise { - return sendMessage('pri(authorize.reject)', { id }); -} - export async function rejectAuthRequestV2(id: string): Promise { return sendMessage('pri(authorize.rejectV2)', { id }); } @@ -802,20 +484,6 @@ export async function rejectMetaRequest(id: string): Promise { return sendMessage('pri(metadata.reject)', { id }); } -export async function subscribeAccounts(cb: (accounts: AccountJson[]) => void): Promise { - return sendMessage('pri(accounts.subscribe)', {}, cb); -} - -export async function subscribeAccountsWithCurrentAddress( - cb: (data: AccountsWithCurrentAddress) => void, -): Promise { - return sendMessage('pri(accounts.subscribeWithCurrentAddress)', {}, cb); -} - -export async function subscribeAccountsInputAddress(cb: (data: OptionInputAddress) => void): Promise { - return sendMessage('pri(accounts.subscribeAccountsInputAddress)', {}, cb); -} - export async function saveRecentAccountId(accountId: string, chain?: string): Promise { return sendMessage('pri(accounts.saveRecent)', { accountId, chain }); } @@ -828,24 +496,6 @@ export async function removeContactAddress(address: string): Promise { return sendMessage('pri(accounts.deleteContact)', { address: address }); } -export async function subscribeAuthorizeRequests(cb: (accounts: AuthorizeRequest[]) => void): Promise { - return sendMessage('pri(authorize.requests)', null, cb); -} - -export async function subscribeAuthorizeRequestsV2( - cb: (accounts: AuthorizeRequest[]) => void, -): Promise { - return sendMessage('pri(authorize.requestsV2)', null, cb); -} - -export async function getAuthList(): Promise { - return sendMessage('pri(authorize.list)'); -} - -export async function getAuthListV2(): Promise { - return sendMessage('pri(authorize.listV2)'); -} - export async function toggleAuthorization(url: string): Promise { return sendMessage('pri(authorize.toggle)', url); } @@ -890,141 +540,7 @@ export async function forgetAllSite(callback: (data: AuthUrls) => void): Promise return sendMessage('pri(authorize.forgetAllSite)', null, callback); } -export async function validateDerivationPath( - parentAddress: string, - suri: string, - parentPassword: string, -): Promise { - return sendMessage('pri(derivation.validate)', { parentAddress, parentPassword, suri }); -} - -export async function deriveAccount( - parentAddress: string, - suri: string, - parentPassword: string, - name: string, - password: string, - genesisHash: string | null, -): Promise { - return sendMessage('pri(derivation.create)', { genesisHash, name, parentAddress, parentPassword, password, suri }); -} - -export async function deriveAccountV2( - parentAddress: string, - suri: string, - parentPassword: string, - name: string, - password: string, - genesisHash: string | null, - isAllowed: boolean, -): Promise { - return sendMessage('pri(derivation.createV2)', { genesisHash, name, parentAddress, suri, isAllowed }); -} - -export async function windowOpen(params: WindowOpenParams): Promise { - return sendMessage('pri(window.open)', params); -} - -export async function jsonGetAccountInfo(json: KeyringPair$Json): Promise { - return sendMessage('pri(json.account.info)', json); -} - -export async function jsonRestore(file: KeyringPair$Json, password: string, address: string): Promise { - return sendMessage('pri(json.restore)', { file, password, address }); -} - -export async function batchRestore(file: KeyringPairs$Json, password: string, address: string): Promise { - return sendMessage('pri(json.batchRestore)', { file, password, address }); -} - -export async function setNotification(notification: string): Promise { - return sendMessage('pri(settings.notification)', notification); -} - -export async function getPrice(): Promise { - return sendMessage('pri(price.getPrice)', null); -} - -export async function subscribePrice( - request: RequestSubscribePrice, - callback: (priceData: PriceJson) => void, -): Promise { - return sendMessage('pri(price.getSubscription)', request, callback); -} - -export async function getBalance(): Promise { - return sendMessage('pri(balance.getBalance)', null); -} - -export async function subscribeBalance( - request: RequestSubscribeBalance, - callback: (balanceData: BalanceJson) => void, -): Promise { - return sendMessage('pri(balance.getSubscription)', request, callback); -} - -export async function getCrowdloan(): Promise { - return sendMessage('pri(crowdloan.getCrowdloan)', null); -} - -export async function subscribeCrowdloan( - request: RequestSubscribeCrowdloan, - callback: (crowdloanData: CrowdloanJson) => void, -): Promise { - return sendMessage('pri(crowdloan.getSubscription)', request, callback); -} - // TODO: remove, deprecated -export async function subscribeAssetRegistry( - callback: (map: Record) => void, -): Promise> { - return sendMessage('pri(chainService.subscribeAssetRegistry)', null, callback); -} - -export async function subscribeHistory( - callback: (historyMap: TransactionHistoryItem[]) => void, -): Promise { - return sendMessage('pri(transaction.history.getSubscription)', null, callback); -} - -export async function getNft(account: string): Promise { - // @ts-ignore - return sendMessage('pri(nft.getNft)', account); -} - -export async function subscribeNft( - request: RequestSubscribeNft, - callback: (nftData: NftJson) => void, -): Promise { - return sendMessage('pri(nft.getSubscription)', request, callback); -} - -export async function subscribeNftCollection(callback: (data: NftCollection[]) => void): Promise { - return sendMessage('pri(nftCollection.getSubscription)', null, callback); -} - -export async function getStaking(account: string): Promise { - // @ts-ignore - return sendMessage('pri(staking.getStaking)', account); -} - -export async function subscribeStaking( - request: RequestSubscribeStaking, - callback: (stakingData: StakingJson) => void, -): Promise { - return sendMessage('pri(staking.getSubscription)', request, callback); -} - -export async function getStakingReward(): Promise { - return sendMessage('pri(stakingReward.getStakingReward)'); -} - -export async function subscribeStakingReward( - request: RequestSubscribeStakingReward, - callback: (stakingRewardData: StakingRewardJson) => void, -): Promise { - return sendMessage('pri(stakingReward.getSubscription)', request, callback); -} export async function makeTransfer(request: RequestTransfer): Promise { return sendMessage('pri(accounts.transfer)', request); @@ -1039,25 +555,6 @@ export async function evmNftSubmitTransaction(request: NftTransactionRequest): P } // ChainService ------------------------------------------------------------------------------------- - -export async function subscribeChainInfoMap( - callback: (data: Record) => void, -): Promise> { - return sendMessage('pri(chainService.subscribeChainInfoMap)', null, callback); -} - -export async function subscribeChainStateMap( - callback: (data: Record) => void, -): Promise> { - return sendMessage('pri(chainService.subscribeChainStateMap)', null, callback); -} - -export async function subscribeChainStatusMap( - callback: (data: Record) => void, -): Promise> { - return sendMessage('pri(chainService.subscribeChainStatusMap)', null, callback); -} - export async function removeChain(networkKey: string): Promise { return sendMessage('pri(chainService.removeChain)', networkKey); } @@ -1078,22 +575,10 @@ export async function enableChain(chainSlug: string, enableTokens = true): Promi return sendMessage('pri(chainService.enableChain)', { chainSlug, enableTokens }); } -export async function enableChains(chainSlugs: string[], enableTokens = true): Promise { - return sendMessage('pri(chainService.enableChains)', { chainSlugs, enableTokens }); -} - -export async function disableChains(targetKeys: string[]): Promise { - return sendMessage('pri(chainService.disableChains)', targetKeys); -} - export async function upsertChain(data: _NetworkUpsertParams): Promise { return sendMessage('pri(chainService.upsertChain)', data); } -export async function getSupportedContractTypes(): Promise { - return sendMessage('pri(chainService.getSupportedContractTypes)', null); -} - export async function upsertCustomToken(data: _ChainAsset): Promise { return sendMessage('pri(chainService.upsertCustomAsset)', data); } @@ -1106,10 +591,6 @@ export async function validateCustomToken(data: _ValidateCustomAssetRequest): Pr return sendMessage('pri(chainService.validateCustomAsset)', data); } -export async function resetDefaultNetwork(): Promise { - return sendMessage('pri(chainService.resetDefaultChains)', null); -} - export async function updateAssetSetting(data: AssetSettingUpdateReq): Promise { return sendMessage('pri(assetSetting.update)', data); } @@ -1123,10 +604,6 @@ export async function validateCustomChain( return sendMessage('pri(chainService.validateCustomChain)', { provider, existedChainSlug }); } -export async function disableAllNetwork(): Promise { - return sendMessage('pri(chainService.disableAllChains)', null); -} - export async function cancelSubscription(request: string): Promise { return sendMessage('pri(subscription.cancel)', request); } @@ -1158,16 +635,7 @@ export async function substrateNftSubmitTransaction(request: NftTransactionReque return sendMessage('pri(substrateNft.submitTransaction)', request); } -export async function recoverDotSamaApi(request: string): Promise { - return sendMessage('pri(chainService.recoverSubstrateApi)', request); -} - // Sign Qr - -export async function accountIsLocked(address: string): Promise { - return sendMessage('pri(account.isLocked)', { address }); -} - export async function qrSignSubstrate(request: RequestQrSignSubstrate): Promise { return sendMessage('pri(qr.sign.substrate)', request); } @@ -1186,16 +654,6 @@ export async function parseEVMTransaction(data: string): Promise { - return sendMessage('pri(accounts.get.meta)', request); -} - -export async function subscribeConfirmations( - callback: (data: ConfirmationsQueue) => void, -): Promise { - return sendMessage('pri(confirmations.subscribe)', null, callback); -} - export async function completeConfirmation( type: CT, payload: ConfirmationDefinitions[CT][1], @@ -1211,61 +669,7 @@ export async function getBondingOptions(networkKey: string, type: StakingType): return sendMessage('pri(bonding.getBondingOptions)', { chain: networkKey, type }); } -export async function subscribeChainStakingMetadata( - callback: (data: ChainStakingMetadata[]) => void, -): Promise { - return sendMessage('pri(bonding.subscribeChainStakingMetadata)', null, callback); -} - -export async function subscribeStakingNominatorMetadata( - callback: (data: NominatorMetadata[]) => void, -): Promise { - return sendMessage('pri(bonding.subscribeNominatorMetadata)', null, callback); -} - -export async function submitPoolUnbonding(request: RequestStakePoolingUnbonding): Promise { - return sendMessage('pri(bonding.nominationPool.submitUnbonding)', request); -} - -export async function submitBonding(request: RequestBondingSubmit): Promise { - return sendMessage('pri(bonding.submitBondingTransaction)', request); -} - -export async function submitPoolBonding(request: RequestStakePoolingBonding): Promise { - return sendMessage('pri(bonding.nominationPool.submitBonding)', request); -} - -export async function submitUnbonding(request: RequestUnbondingSubmit): Promise { - return sendMessage('pri(unbonding.submitTransaction)', request); -} - -export async function parseEVMTransactionInput( - request: RequestParseEvmContractInput, -): Promise { - return sendMessage('pri(evm.transaction.parse.input)', request); -} - -export async function subscribeAuthUrl(callback: (data: AuthUrls) => void): Promise { - return sendMessage('pri(authorize.subscribe)', null, callback); -} - -export async function submitTuringStakeCompounding( - request: RequestTuringStakeCompound, -): Promise { - return sendMessage('pri(staking.submitTuringCompound)', request); -} - -export async function submitTuringCancelStakeCompounding( - request: RequestTuringCancelStakeCompound, -): Promise { - return sendMessage('pri(staking.submitTuringCancelCompound)', request); -} - // Keyring state -export async function keyringStateSubscribe(cb: (value: KeyringState) => void): Promise { - return sendMessage('pri(keyring.subscribe)', null, cb); -} - export async function keyringChangeMasterPassword( request: RequestChangeMasterPassword, ): Promise { @@ -1295,26 +699,10 @@ export async function resetWallet(request: RequestResetWallet): Promise { - return sendMessage('pri(derivation.validateV2)', request); -} - -export async function getListDeriveAccounts(request: RequestGetDeriveAccounts): Promise { - return sendMessage('pri(derivation.getList)', request); -} - -export async function deriveMultiple(request: RequestDeriveCreateMultiple): Promise { - return sendMessage('pri(derivation.create.multiple)', request); -} - export async function deriveAccountV3(request: RequestDeriveCreateV3): Promise { return sendMessage('pri(derivation.createV3)', request); } -export async function getTransaction(request: RequestGetTransaction): Promise { - return sendMessage('pri(transactions.getOne)', request); -} - // Wallet Connect export async function addConnection(request: RequestConnectWalletConnect): Promise { @@ -1341,12 +729,6 @@ export async function resolveAddressToDomain(request: ResolveAddressToDomainRequ return sendMessage('pri(accounts.resolveAddressToDomain)', request); } -export async function subscribeTransactions( - callback: (rs: Record) => void, -): Promise> { - return sendMessage('pri(transactions.subscribe)', null, callback); -} - export async function getMetadata(genesisHash?: string | null, isPartial = false): Promise { if (!genesisHash) { return null; @@ -1422,10 +804,6 @@ export const getMetadataHash = async (chain: string) => { return sendMessage('pri(metadata.hash)', { chain }); }; -export const shortenMetadata = async (chain: string, txBlob: string) => { - return sendMessage('pri(metadata.transaction.shorten)', { chain, txBlob }); -}; - export async function completeBannerCampaign(request: RequestCampaignBannerComplete): Promise { return sendMessage('pri(campaign.banner.complete)', request); } @@ -1456,14 +834,6 @@ export async function submitJoinYieldPool(data: RequestYieldStepSubmit): Promise return sendMessage('pri(yield.join.handleStep)', data); } -export async function getYieldNativeStakingValidators(poolInfo: YieldPoolInfo): Promise { - return sendMessage('pri(yield.getNativeStakingValidators)', poolInfo); -} - -export async function getYieldNominationPools(poolInfo: YieldPoolInfo): Promise { - return sendMessage('pri(yield.getStakingNominationPools)', poolInfo); -} - export async function validateYieldProcess(data: ValidateYieldProcessParams): Promise { return sendMessage('pri(yield.join.validateProcess)', data); } @@ -1484,14 +854,6 @@ export async function yieldSubmitStakingClaimReward(data: RequestStakeClaimRewar return sendMessage('pri(yield.claimReward.submit)', data); } -export async function yieldSubmitNominationPoolUnstaking(data: RequestStakePoolingUnbonding) { - return sendMessage('pri(yield.nominationPool.submitUnstaking)', data); -} - -export async function yieldSubmitRedeem(data: RequestYieldFastWithdrawal) { - return sendMessage('pri(yield.submitRedeem)', data); -} - /* Earning */ /* Mint campaign */ diff --git a/src/screens/Account/AccountDetail/index.tsx b/src/screens/Account/AccountDetail/index.tsx index db2533723..0e3ae2bcc 100644 --- a/src/screens/Account/AccountDetail/index.tsx +++ b/src/screens/Account/AccountDetail/index.tsx @@ -101,6 +101,10 @@ const Component = ({ accountProxy }: Props) => { return result; }, [showDerivationInfoTab, showDerivedAccounts]); + const onExportAccount = useCallback(() => { + navigation.navigate('AccountExport', { address: accountProxy.id }); + }, [accountProxy.id, navigation]); + const onSave = useCallback( (editName: string) => { clearTimeout(saveTimeOutRef.current); @@ -165,13 +169,14 @@ const Component = ({ accountProxy }: Props) => { ); - }, [accountProxy.accountType, styles.noPaddingHorizontal]); + }, [accountProxy.accountType, onExportAccount, styles.noPaddingHorizontal]); return ( (''); const [publicKey, setPublicKey] = useState(''); const [seedPhrase, setSeedPhrase] = useState(''); - const [jsonData, setJsonData] = useState(null); + const [jsonData, setJsonData] = useState(null); const titleMap: Record = useMemo( () => ({ [ExportType.JSON_FILE]: i18n.header.successful, @@ -61,20 +62,20 @@ export const AccountExport = ({ const exportSingle = selectedTypes.length <= 1; useHandlerHardwareBackPress(isBusy); - const account = useGetAccountByAddress(address); + const accountProxy = useGetAccountProxyById(address); const qrData = useMemo((): string => { const prefix = 'secret'; const result: string[] = [prefix, privateKey || '', publicKey]; - if (account?.name) { - result.push(account.name); + if (accountProxy?.name) { + result.push(accountProxy.name); } return result.join(':'); - }, [account?.name, publicKey, privateKey]); + }, [accountProxy?.name, publicKey, privateKey]); - const onExportJson = useCallback((_jsonData: KeyringPair$Json, _address: string): (() => void) => { + const onExportJson = useCallback((_jsonData: KeyringPairs$Json, _address: string): (() => void) => { return () => { if (_jsonData) { Share.share({ title: 'Account Json', message: JSON.stringify(_jsonData) }); @@ -84,11 +85,11 @@ export const AccountExport = ({ const onPressSubmit = useCallback( (password: string) => { - if (!selectedTypes.length || !account) { + if (!selectedTypes.length || !accountProxy) { return; } - const _address = account.address; + const _address = accountProxy.id; if (!_address) { return; @@ -110,7 +111,12 @@ export const AccountExport = ({ } }; - if (selectedTypes.includes(ExportType.PRIVATE_KEY) || selectedTypes.includes(ExportType.QR_CODE)) { + if ( + (selectedTypes.includes(ExportType.PRIVATE_KEY) && + accountProxy.accountActions.includes(AccountActions.EXPORT_PRIVATE_KEY)) || + (selectedTypes.includes(ExportType.QR_CODE) && + accountProxy.accountActions.includes(AccountActions.EXPORT_QR)) + ) { exportAccountPrivateKey(_address, password) .then(res => { setPrivateKey(res.privateKey); @@ -125,8 +131,11 @@ export const AccountExport = ({ result.privateKey = true; } - if (selectedTypes.includes(ExportType.SEED_PHRASE) && account?.isMasterAccount) { - keyringExportMnemonic({ address, password: password }) + if ( + selectedTypes.includes(ExportType.SEED_PHRASE) && + accountProxy.accountActions.includes(AccountActions.EXPORT_MNEMONIC) + ) { + exportAccountMnemonic({ proxyId: address, password: password }) .then(res => { setSeedPhrase(res.result); result.seedPhrase = true; @@ -140,11 +149,15 @@ export const AccountExport = ({ } if (selectedTypes.includes(ExportType.JSON_FILE)) { - exportAccount(_address, password) + exportAccountBatch({ proxyIds: [accountProxy.id], password: password }) .then(res => { setJsonData(res.exportedJson); result.jsonFile = true; checkDone(); + + if (exportSingle) { + onExportJson(res.exportedJson, accountProxy.name)(); + } }) .catch((e: Error) => { reject(new Error(e.message)); @@ -165,7 +178,7 @@ export const AccountExport = ({ }); }, 500); }, - [account, address, selectedTypes], + [accountProxy, address, exportSingle, onExportJson, selectedTypes], ); const copyPrivateKey = useCopyClipboard(privateKey); @@ -186,7 +199,7 @@ export const AccountExport = ({ } }, [currentViewStep, exportSingle, selectedTypes, titleMap]); - if (!account) { + if (!accountProxy || accountProxy.accounts.length === 0) { return null; } @@ -206,7 +219,7 @@ export const AccountExport = ({ )} diff --git a/src/screens/Account/ExportAllAccount/index.tsx b/src/screens/Account/ExportAllAccount/index.tsx index ced42f3eb..79f94872d 100644 --- a/src/screens/Account/ExportAllAccount/index.tsx +++ b/src/screens/Account/ExportAllAccount/index.tsx @@ -1,13 +1,9 @@ -import { AccountJson } from '@subwallet/extension-base/background/types'; import i18n from 'utils/i18n/i18n'; import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { isAccountAll } from 'utils/accountAll'; import { ALL_ACCOUNT_KEY } from '@subwallet/extension-base/constants'; import { AddressBook, CheckCircle, DownloadSimple, Export, MagnifyingGlass } from 'phosphor-react-native'; import PasswordModal from 'components/Modal/PasswordModal'; -import { exportAccountsV2 } from 'messaging/index'; -import { AccountSignMode } from 'types/signer'; -import { getSignMode } from 'utils/account'; import { Button, Icon, SelectItem, SwFullSizeModal, Typography } from 'components/design-system-ui'; import AlertBox from 'components/design-system-ui/alert-box/simple'; import { KeyringPairs$Json } from '@subwallet/ui-keyring/types'; @@ -25,25 +21,8 @@ import { RootState } from 'stores/index'; import { SelectAccountItem } from 'components/common/SelectAccountItem'; import { FontSemiBold } from 'styles/sharedStyles'; import { ListRenderItemInfo } from '@shopify/flash-list'; - -const filterOptions = [ - { - label: 'Normal account', - value: AccountSignMode.PASSWORD, - }, - { - label: 'QR signer account', - value: AccountSignMode.QR, - }, - { - label: 'Ledger account', - value: 'ledger', - }, - { - label: 'Watch-only account', - value: AccountSignMode.READ_ONLY, - }, -]; +import { AccountProxy } from '@subwallet/extension-base/types'; +import { exportAccountBatch } from 'messaging/accounts'; const renderListEmptyComponent = () => { return ( @@ -55,39 +34,16 @@ const renderListEmptyComponent = () => { ); }; -const filterFunction = (items: AccountJson[], filters: string[]) => { - if (!filters.length) { - return items; - } - - return items.filter(item => { - const signMode = getSignMode(item); - for (const filter of filters) { - switch (filter) { - case AccountSignMode.PASSWORD: - return signMode === AccountSignMode.PASSWORD; - case AccountSignMode.QR: - return signMode === AccountSignMode.QR; - case 'ledger': - return signMode === AccountSignMode.LEGACY_LEDGER || signMode === AccountSignMode.GENERIC_LEDGER; - case AccountSignMode.READ_ONLY: - return signMode === AccountSignMode.READ_ONLY; - } - } - return false; - }); -}; - -const searchFunc = (items: AccountJson[], searchText: string) => { +const searchFunc = (items: AccountProxy[], searchText: string) => { return items.filter( acc => (acc.name && acc.name.toLowerCase().includes(searchText.toLowerCase())) || - acc.address.toLowerCase().includes(searchText.toLowerCase()), + acc.id.toLowerCase().includes(searchText.toLowerCase()), ); }; export const ExportAllAccount = () => { - const fullAccounts = useSelector((state: RootState) => state.accountState.accounts); + const accountProxies = useSelector((state: RootState) => state.accountState.accountProxies); const theme = useSubWalletTheme().swThemes; const navigation = useNavigation(); const successModalRef = useRef(null); @@ -101,11 +57,11 @@ export const ExportAllAccount = () => { const currentAccountAddress = useSelector((state: RootState) => state.accountState.currentAccount?.address); const [isReady, setIsReady] = useState(false); const items = useMemo(() => { - if (fullAccounts.length > 2) { - const foundAccountAll = fullAccounts.find(a => isAccountAll(a.address)); - const foundCurrentAccount = fullAccounts.find(a => a.address === currentAccountAddress); + if (accountProxies.length > 2) { + const foundAccountAll = accountProxies.find(a => isAccountAll(a.id)); + const foundCurrentAccount = accountProxies.find(a => a.id === currentAccountAddress); - const result = fullAccounts.filter(a => !(isAccountAll(a.address) || a.address === currentAccountAddress)); + const result = accountProxies.filter(a => !(isAccountAll(a.id) || a.id === currentAccountAddress)); if (foundCurrentAccount && !isAccountAll(currentAccountAddress || '')) { result.unshift(foundCurrentAccount); @@ -118,8 +74,8 @@ export const ExportAllAccount = () => { return result; } - return fullAccounts.filter(a => !isAccountAll(a.address)); - }, [fullAccounts, currentAccountAddress]); + return accountProxies.filter(a => !isAccountAll(a.id)); + }, [accountProxies, currentAccountAddress]); useEffect(() => { InteractionManager.runAfterInteractions(() => { @@ -131,7 +87,7 @@ export const ExportAllAccount = () => { const addresses: string[] = []; items.forEach(obj => { - addresses.push(obj.address); + addresses.push(obj.id); }); return addresses; @@ -148,17 +104,17 @@ export const ExportAllAccount = () => { }, [selectedValueMap]); const onSelectAccount = useCallback( - (value: string, isCheck?: boolean) => { + (ap: AccountProxy, isCheck?: boolean) => { setSelectedValueMap(prev => { const newMap = { ...prev }; - if (isAccountAll(value)) { + if (isAccountAll(ap.id)) { allAddress.forEach(key => { newMap[key] = !!isCheck; }); newMap[ALL_ACCOUNT_KEY] = !!isCheck; } else { - newMap[value] = !!isCheck; + newMap[ap.id] = !!isCheck; newMap[ALL_ACCOUNT_KEY] = allAddress.filter(i => !isAccountAll(i)).every(item => newMap[item]); } @@ -178,9 +134,9 @@ export const ExportAllAccount = () => { setJsonFileName(`${toShort(selectedAccounts[0])}.json`); } - exportAccountsV2({ + exportAccountBatch({ password: password, - addresses: selectedAccounts, + proxyIds: selectedAccounts, }) .then(data => { setJsonData(data.exportedJson); @@ -205,25 +161,23 @@ export const ExportAllAccount = () => { }, [navigation]); const renderItem = useCallback( - ({ item }: ListRenderItemInfo) => { - const isAllAccount = isAccountAll(item.address); + ({ item }: ListRenderItemInfo) => { + const isAllAccount = isAccountAll(item.id); return ( { - onSelectAccount(_item, !selectedValueMap[_item]); + onSelectAccount={() => { + onSelectAccount(item, !selectedValueMap[item.id]); }} onPressDetailBtn={() => { - navigation.navigate('EditAccount', { address: item.address, name: item.name || '' }); + navigation.navigate('EditAccount', { address: item.id, name: item.name || '' }); }} - avatarGroupStyle={{ width: 40 }} /> ); }, @@ -258,14 +212,12 @@ export const ExportAllAccount = () => { onPressBack={() => navigation.goBack()} renderItem={renderItem} loading={!isReady} - filterOptions={filterOptions} - filterFunction={filterFunction} isShowFilterBtn={true} afterListItem={renderFooter()} searchFunction={searchFunc} autoFocus={false} extraData={JSON.stringify(selectedValueMap)} - keyExtractor={item => item.address} + keyExtractor={item => item.id} estimatedItemSize={80} />