diff --git a/CHANGELOG.md b/CHANGELOG.md index 1c224d2992..89037646da 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ ### Features +- Implemented error message when trying to leave wallet without enough ada to support tokens ([PR 2783](https://github.com/input-output-hk/daedalus/pull/2783)) - Added display of current/unspent rewards ([PR 2803](https://github.com/input-output-hk/daedalus/pull/2803)) - Improve the syncing screen by showing syncing progress split into three stages ([PR 2877](https://github.com/input-output-hk/daedalus/pull/2877)) - Improved stake pool searchbar ([PR 2847](https://github.com/input-output-hk/daedalus/pull/2847)) @@ -19,6 +20,7 @@ - Fixed Daedalus menu in Storybook used for theme and language selection ([PR 2886](https://github.com/input-output-hk/daedalus/pull/2886)) ### Chores + - Removed SASS ts-lint ignore comments ([PR 2870](https://github.com/input-output-hk/daedalus/pull/2870)) ## 4.9.0-FC1 @@ -85,10 +87,12 @@ ### Fixes +- Fixed immediate language updates of application top menu bar ([PR 2813](https://github.com/input-output-hk/daedalus/pull/2813)) - Fixed receiver address validation by disallowing rewards addresses ([PR 2781](https://github.com/input-output-hk/daedalus/pull/2781)) ### Chores +- Integrated Chromatic for visual regression testing ([PR 2776](https://github.com/input-output-hk/daedalus/pull/2776)) - Updated `trezor-connect` dependency to version `8.2.4` ([PR 2726](https://github.com/input-output-hk/daedalus/pull/2726)) - Updated vulnerable dependencies ([PR 2769](https://github.com/input-output-hk/daedalus/pull/2769)) - Updated CWB and Cardano Node ([PR 2799](https://github.com/input-output-hk/daedalus/pull/2799)) diff --git a/source/common/types/logging.types.ts b/source/common/types/logging.types.ts index 9aa2f77368..e8d93af11e 100644 --- a/source/common/types/logging.types.ts +++ b/source/common/types/logging.types.ts @@ -19,10 +19,10 @@ import type { AdaApiStakePool } from '../../renderer/app/api/staking/types'; export type LoggingLevel = 'debug' | 'info' | 'error' | 'warn'; export type Logger = { - debug: (arg0: string, arg1: Record | null | undefined) => void; - info: (arg0: string, arg1: Record | null | undefined) => void; - error: (arg0: string, arg1: Record | null | undefined) => void; - warn: (arg0: string, arg1: Record | null | undefined) => void; + debug: (arg0: string, arg1?: Record | null | undefined) => void; + info: (arg0: string, arg1?: Record | null | undefined) => void; + error: (arg0: string, arg1?: Record | null | undefined) => void; + warn: (arg0: string, arg1?: Record | null | undefined) => void; }; export type FormatMessageContextParams = { appName: string; diff --git a/source/main/ipc/downloadManagerChannel.ts b/source/main/ipc/downloadManagerChannel.ts index dac186fd71..fd9e8afbad 100644 --- a/source/main/ipc/downloadManagerChannel.ts +++ b/source/main/ipc/downloadManagerChannel.ts @@ -211,8 +211,9 @@ const getDownloadLocalData = async ({ const getDownloadsLocalData = async (): Promise< DownloadsLocalDataMainResponse - // @ts-ignore ts-migrate(2322) FIXME: Type 'unknown' is not assignable to type 'Download... Remove this comment to see the full error message -> => localStorage.getAll(); +> => { + return localStorage.getAll() as Promise; +}; const clearDownloadLocalData = async ({ fileName, diff --git a/source/renderer/app/actions/lib/Action.ts b/source/renderer/app/actions/lib/Action.ts index 34e1fbef6f..79b7aac220 100644 --- a/source/renderer/app/actions/lib/Action.ts +++ b/source/renderer/app/actions/lib/Action.ts @@ -30,7 +30,7 @@ export default class Action { this.listeners.push(listener); } - trigger(params: Params) { + trigger(params?: Params) { this.listeners.forEach((listener) => listener(params)); } diff --git a/source/renderer/app/actions/wallets-actions.ts b/source/renderer/app/actions/wallets-actions.ts index d05dc7795c..ed4149038b 100644 --- a/source/renderer/app/actions/wallets-actions.ts +++ b/source/renderer/app/actions/wallets-actions.ts @@ -60,6 +60,7 @@ export default class WalletsActions { passphrase: string; assets?: Array; assetsAmounts?: Array; + hasAssetsRemainingAfterTransaction?: boolean; }> = new Action(); chooseWalletExportType: Action<{ walletExportType: WalletExportTypeChoices; diff --git a/source/renderer/app/api/api.ts b/source/renderer/app/api/api.ts index 51e3d3a561..2fda2709b6 100644 --- a/source/renderer/app/api/api.ts +++ b/source/renderer/app/api/api.ts @@ -128,7 +128,7 @@ import type { GetNetworkParametersApiResponse, } from './network/types'; // Transactions Types -import type { +import { Transaction, TransactionFee, TransactionWithdrawals, @@ -150,7 +150,7 @@ import type { ICOPublicKeyParams, } from './transactions/types'; // Wallets Types -import type { +import { AdaWallet, AdaWallets, CreateHardwareWalletRequest, @@ -215,7 +215,7 @@ import { getSHA256HexForString } from './utils/hashing'; import { getNewsHash } from './news/requests/getNewsHash'; import { deleteTransaction } from './transactions/requests/deleteTransaction'; import { WALLET_BYRON_KINDS } from '../config/walletRestoreConfig'; -import ApiError from '../domains/ApiError'; +import ApiError, { ErrorType } from '../domains/ApiError'; import { formattedAmountToLovelace } from '../utils/formatters'; import type { GetAssetsRequest, @@ -227,6 +227,7 @@ import type { AssetLocalData } from './utils/localStorage'; import Asset from '../domains/Asset'; import { getAssets } from './assets/requests/getAssets'; import { getAccountPublicKey } from './wallets/requests/getAccountPublicKey'; +import { doesWalletRequireAdaToRemainToSupportTokens } from './utils/apiHelpers'; export default class AdaApi { config: RequestConfig; @@ -244,7 +245,6 @@ export default class AdaApi { } getWallets = async (): Promise> => { - // @ts-ignore ts-migrate(2554) FIXME: Expected 2 arguments, but got 1. logger.debug('AdaApi::getWallets called'); const { getHardwareWalletLocalData, @@ -252,7 +252,9 @@ export default class AdaApi { } = global.daedalus.api.localStorage; try { - const wallets: AdaWallets = await getWallets(this.config); + const wallets: Array = await getWallets( + this.config + ); const legacyWallets: LegacyAdaWallets = await getLegacyWallets( this.config ); @@ -273,12 +275,11 @@ export default class AdaApi { }, isLegacy: true, }; - // @ts-ignore ts-migrate(2345) FIXME: Argument of type '{ address_pool_gap: number; dele... Remove this comment to see the full error message wallets.push({ ...legacyAdaWallet, ...extraLegacyWalletProps }); }); // @TODO - Remove this once we get hardware wallet flag from WBE return await Promise.all( - wallets.map(async (wallet) => { + wallets.map(async (wallet: AdaWallet) => { const { id } = wallet; const walletData = await getHardwareWalletLocalData(id); return _createWalletFromServerData({ @@ -463,13 +464,13 @@ export default class AdaApi { {}, { order: order || 'descending', + start: undefined, + end: undefined, } ); if (fromDate) - // @ts-ignore ts-migrate(2339) FIXME: Property 'start' does not exist on type '{ order: ... Remove this comment to see the full error message params.start = `${moment.utc(fromDate).format('YYYY-MM-DDTHH:mm:ss')}Z`; if (toDate) - // @ts-ignore ts-migrate(2339) FIXME: Property 'end' does not exist on type '{ order: "a... Remove this comment to see the full error message params.end = `${moment.utc(toDate).format('YYYY-MM-DDTHH:mm:ss')}Z`; try { @@ -837,7 +838,9 @@ export default class AdaApi { } }; createTransaction = async ( - request: CreateTransactionRequest + request: CreateTransactionRequest & { + hasAssetsRemainingAfterTransaction: boolean; + } ): Promise => { logger.debug('AdaApi::createTransaction called', { parameters: filterLogData(request), @@ -850,6 +853,7 @@ export default class AdaApi { isLegacy, assets, withdrawal = TransactionWithdrawal, + hasAssetsRemainingAfterTransaction, } = request; try { @@ -889,7 +893,8 @@ export default class AdaApi { logger.error('AdaApi::createTransaction error', { error, }); - throw new ApiError(error) + + const apiError = new ApiError(error) .set('wrongEncryptionPassphrase') .where('code', 'bad_request') .inc('message', 'passphrase is too short') @@ -897,8 +902,20 @@ export default class AdaApi { linkLabel: 'tooBigTransactionErrorLinkLabel', linkURL: 'tooBigTransactionErrorLinkURL', }) - .where('code', 'transaction_is_too_big') - .result(); + .where('code', 'transaction_is_too_big'); + + const { + requiresAdaToRemainToSupportNativeTokens, + adaToRemain, + } = doesWalletRequireAdaToRemainToSupportTokens( + error, + hasAssetsRemainingAfterTransaction + ); + if (requiresAdaToRemainToSupportNativeTokens) { + apiError.set('cannotLeaveWalletEmpty', true, { adaToRemain }); + } + + throw apiError.result(); } }; // For testing purpose ONLY @@ -1025,6 +1042,7 @@ export default class AdaApi { const { fee, minimumAda } = _createTransactionFeeFromServerData(response); const amountWithFee = formattedTxAmount.plus(fee); + // @ts-ignore ts-migrate(2304) FIXME: Cannot find name 'Array'. const isRewardsRedemptionRequest = Array.isArray(withdrawal); if (!isRewardsRedemptionRequest && amountWithFee.gt(walletBalance)) { @@ -1072,6 +1090,7 @@ export default class AdaApi { .set(notEnoughMoneyError, true) .where('code', 'not_enough_money') .set('utxoTooSmall', true, { + // @ts-ignore ts-migrate(2339) FIXME: Property 'exec' does not exist on type '{}'. minimumAda: get( /(Expected min coin value: +)([0-9]+.[0-9]+)/.exec(error.message), 2, @@ -1150,7 +1169,7 @@ export default class AdaApi { let totalOutputs = new BigNumber(0); map(response.inputs, (input) => { const inputAmount = new BigNumber(input.amount.quantity.toString()); - // @ts-ignore ts-migrate(2339) FIXME: Property 'assets' does not exist on type '{ addres... Remove this comment to see the full error message + // @ts-ignore ts-migrate(2339) FIXME: Property 'assets' does not exist on type 'unknown'... Remove this comment to see the full error message const inputAssets = map(input.assets, (asset) => ({ policyId: asset.policy_id, assetName: asset.asset_name, @@ -1165,11 +1184,12 @@ export default class AdaApi { derivationPath: input.derivation_path, assets: inputAssets, }; + // @ts-ignore ts-migrate(2339) FIXME: Property 'push' does not exist on type '{}'. inputsData.push(inputData); }); map(outputs, (output) => { const outputAmount = new BigNumber(output.amount.quantity.toString()); - // @ts-ignore ts-migrate(2339) FIXME: Property 'assets' does not exist on type '{ addres... Remove this comment to see the full error message + // @ts-ignore ts-migrate(2339) FIXME: Property 'assets' does not exist on type 'unknown'... Remove this comment to see the full error message const outputAssets = map(output.assets, (asset) => ({ policyId: asset.policy_id, assetName: asset.asset_name, @@ -1182,6 +1202,7 @@ export default class AdaApi { derivationPath: output.derivation_path || null, assets: outputAssets, }; + // @ts-ignore ts-migrate(2339) FIXME: Property 'push' does not exist on type '{}'. outputsData.push(outputData); }); @@ -1192,6 +1213,7 @@ export default class AdaApi { rewardAccountPath: certificate.reward_account_path, pool: certificate.pool || null, }; + // @ts-ignore ts-migrate(2339) FIXME: Property 'push' does not exist on type '{}'. certificatesData.push(certificateData); }); } @@ -1459,14 +1481,12 @@ export default class AdaApi { mnemonic.split(' ').length === ADA_CERTIFICATE_MNEMONIC_LENGTH; getWalletRecoveryPhrase(): Promise> { - // @ts-ignore ts-migrate(2554) FIXME: Expected 2 arguments, but got 1. logger.debug('AdaApi::getWalletRecoveryPhrase called'); try { const response: Promise> = new Promise((resolve) => resolve(generateAccountMnemonics(WALLET_RECOVERY_PHRASE_WORD_COUNT)) ); - // @ts-ignore ts-migrate(2554) FIXME: Expected 2 arguments, but got 1. logger.debug('AdaApi::getWalletRecoveryPhrase success'); return response; } catch (error) { @@ -1478,14 +1498,12 @@ export default class AdaApi { } getWalletCertificateAdditionalMnemonics(): Promise> { - // @ts-ignore ts-migrate(2554) FIXME: Expected 2 arguments, but got 1. logger.debug('AdaApi::getWalletCertificateAdditionalMnemonics called'); try { const response: Promise> = new Promise((resolve) => resolve(generateAdditionalMnemonics()) ); - // @ts-ignore ts-migrate(2554) FIXME: Expected 2 arguments, but got 1. logger.debug('AdaApi::getWalletCertificateAdditionalMnemonics success'); return response; } catch (error) { @@ -1499,7 +1517,6 @@ export default class AdaApi { getWalletCertificateRecoveryPhrase( request: GetWalletCertificateRecoveryPhraseRequest ): Promise> { - // @ts-ignore ts-migrate(2554) FIXME: Expected 2 arguments, but got 1. logger.debug('AdaApi::getWalletCertificateRecoveryPhrase called'); const { passphrase, input: scrambledInput } = request; @@ -1512,7 +1529,6 @@ export default class AdaApi { }) ) ); - // @ts-ignore ts-migrate(2554) FIXME: Expected 2 arguments, but got 1. logger.debug('AdaApi::getWalletCertificateRecoveryPhrase success'); return response; } catch (error) { @@ -1526,7 +1542,6 @@ export default class AdaApi { getWalletRecoveryPhraseFromCertificate( request: GetWalletRecoveryPhraseFromCertificateRequest ): Promise> { - // @ts-ignore ts-migrate(2554) FIXME: Expected 2 arguments, but got 1. logger.debug('AdaApi::getWalletRecoveryPhraseFromCertificate called'); const { passphrase, scrambledInput } = request; @@ -1535,7 +1550,6 @@ export default class AdaApi { passphrase, scrambledInput, }); - // @ts-ignore ts-migrate(2554) FIXME: Expected 2 arguments, but got 1. logger.debug('AdaApi::getWalletRecoveryPhraseFromCertificate success'); return Promise.resolve(response); } catch (error) { @@ -2100,7 +2114,6 @@ export default class AdaApi { }); } - // @ts-ignore ts-migrate(2554) FIXME: Expected 2 arguments, but got 1. logger.debug('AdaApi::updateSpendingPassword success'); return true; } catch (error) { @@ -2143,7 +2156,6 @@ export default class AdaApi { } }; getSmashSettings = async (): Promise => { - // @ts-ignore ts-migrate(2554) FIXME: Expected 2 arguments, but got 1. logger.debug('AdaApi::getSmashSettings called'); try { @@ -2206,10 +2218,9 @@ export default class AdaApi { ); if (!isSmashServerValid) { - const error = { + const error: ErrorType = { code: 'invalid_smash_server', }; - // @ts-ignore ts-migrate(2345) FIXME: Argument of type '{ code: string; }' is not assign... Remove this comment to see the full error message throw new ApiError(error); } @@ -2334,8 +2345,7 @@ export default class AdaApi { }); try { - // @ts-ignore ts-migrate(2322) FIXME: Type '[]' is not assignable to type 'Promise<[]>'. - const response: Promise<[]> = await exportWalletAsJSON(this.config, { + const response: [] = await exportWalletAsJSON(this.config, { walletId, filePath, }); @@ -2483,7 +2493,6 @@ export default class AdaApi { } }; testReset = async (): Promise => { - // @ts-ignore ts-migrate(2554) FIXME: Expected 2 arguments, but got 1. logger.debug('AdaApi::testReset called'); try { @@ -2497,7 +2506,6 @@ export default class AdaApi { }) ) ); - // @ts-ignore ts-migrate(2554) FIXME: Expected 2 arguments, but got 1. logger.debug('AdaApi::testReset success'); } catch (error) { logger.error('AdaApi::testReset error', { @@ -2507,7 +2515,6 @@ export default class AdaApi { } }; getNetworkInfo = async (): Promise => { - // @ts-ignore ts-migrate(2554) FIXME: Expected 2 arguments, but got 1. logger.debug('AdaApi::getNetworkInfo called'); try { @@ -2568,6 +2575,7 @@ export default class AdaApi { throw new ApiError(error); } }; + // @ts-ignore ts-migrate(2583) FIXME: Cannot find name 'Promise'. Do you need to change ... Remove this comment to see the full error message getNetworkClock = async ( isForceCheck: boolean ): Promise => { @@ -2596,6 +2604,7 @@ export default class AdaApi { throw new ApiError(error); } }; + // @ts-ignore ts-migrate(2583) FIXME: Cannot find name 'Promise'. Do you need to change ... Remove this comment to see the full error message getNetworkParameters = async (): Promise => { // @ts-ignore ts-migrate(2554) FIXME: Expected 2 arguments, but got 1. logger.debug('AdaApi::getNetworkParameters called'); @@ -2641,7 +2650,6 @@ export default class AdaApi { } }; getNews = async (): Promise => { - // @ts-ignore ts-migrate(2554) FIXME: Expected 2 arguments, but got 1. logger.debug('AdaApi::getNews called'); // Fetch news json let rawNews: string; diff --git a/source/renderer/app/api/errors.ts b/source/renderer/app/api/errors.ts index 3e69f10af8..9f0eee4578 100644 --- a/source/renderer/app/api/errors.ts +++ b/source/renderer/app/api/errors.ts @@ -111,4 +111,11 @@ export const messages = defineMessages({ defaultMessage: '!!!This URL is not a valid SMASH server', description: '"This URL is not a valid SMASH server" error message', }, + cannotLeaveWalletEmpty: { + id: 'api.errors.NotEnoughFundsForTransactionFeesErrorWithTokens', + defaultMessage: + '!!!Insufficient funds to support tokens. A minimum of {adaToRemain} ADA must remain in the wallet after this transaction.', + description: + '"Balance after transaction would not leave enough ada in the wallet to support tokens remaining in wallet', + }, }); diff --git a/source/renderer/app/api/utils/apiHelpers.spec.ts b/source/renderer/app/api/utils/apiHelpers.spec.ts new file mode 100644 index 0000000000..bfc6088ab3 --- /dev/null +++ b/source/renderer/app/api/utils/apiHelpers.spec.ts @@ -0,0 +1,76 @@ +import { ErrorType } from '../../domains/ApiError'; +import { doesWalletRequireAdaToRemainToSupportTokens } from './apiHelpers'; + +describe('throw error if not enough Ada to support tokens', () => { + it('should not throw if error.code is not "cannot_cover_fee"', () => { + const error: ErrorType = { code: 'bad_request' }; + + expect(doesWalletRequireAdaToRemainToSupportTokens(error, true)).toEqual({ + requiresAdaToRemainToSupportNativeTokens: false, + }); + }); + it('should not throw error if error code is "cannot_cover_fee" but hasAssetsRemainingAfterTransaction is undefined', () => { + const error: ErrorType = { + message: + 'I cannot proceed with transaction, I need approximately 1.6 ada to proceed', + code: 'cannot_cover_fee', + }; + + expect(doesWalletRequireAdaToRemainToSupportTokens(error)).toEqual({ + requiresAdaToRemainToSupportNativeTokens: false, + }); + }); + it('should not throw error if error code is "cannot_cover_fee" but message does not match reegex', () => { + const error: ErrorType = { + message: 'other message', + code: 'cannot_cover_fee', + }; + + expect(doesWalletRequireAdaToRemainToSupportTokens(error, true)).toEqual({ + requiresAdaToRemainToSupportNativeTokens: false, + }); + }); + it('should not throw error if error code is not "cannot_cover_fee" and message matches regex', () => { + const error: ErrorType = { + message: + 'I cannot proceed with transaction, I need approximately 1.6 ada to proceed', + code: 'bad_request', + }; + + expect(doesWalletRequireAdaToRemainToSupportTokens(error, true)).toEqual({ + requiresAdaToRemainToSupportNativeTokens: false, + }); + }); + it('should not throw if there are no tokens remaining in wallet after transaction', () => { + const error: ErrorType = { code: 'cannot_cover_fee' }; + + expect(doesWalletRequireAdaToRemainToSupportTokens(error, true)).toEqual({ + requiresAdaToRemainToSupportNativeTokens: false, + }); + }); + it('should throw if there are tokens remaining in wallet after transaction and error is "cannot_cover_fee" and round to 2 minimum ada', () => { + const error: ErrorType = { + message: + 'I am unable to finalize the transaction, as there is not enough ada available to pay for the fee and also pay for the minimum ada quantities of all change outputs. I need approximately 0.629344 ada to proceed. Try increasing your wallet balance or sending a smaller amount.', + code: 'cannot_cover_fee', + }; + + expect(doesWalletRequireAdaToRemainToSupportTokens(error, true)).toEqual({ + requiresAdaToRemainToSupportNativeTokens: true, + adaToRemain: 2, + }); + }); + + it('should throw if there are tokens remaining in wallet after transaction and error is "cannot_cover_fee" and round to 2 nearest whole value provided by error', () => { + const error: ErrorType = { + message: + 'I am unable to finalize the transaction, as there is not enough ada available to pay for the fee and also pay for the minimum ada quantities of all change outputs. I need approximately 2.629344 ada to proceed. Try increasing your wallet balance or sending a smaller amount.', + code: 'cannot_cover_fee', + }; + + expect(doesWalletRequireAdaToRemainToSupportTokens(error, true)).toEqual({ + requiresAdaToRemainToSupportNativeTokens: true, + adaToRemain: 3, + }); + }); +}); diff --git a/source/renderer/app/api/utils/apiHelpers.ts b/source/renderer/app/api/utils/apiHelpers.ts index 8e20197929..85f51f2927 100644 --- a/source/renderer/app/api/utils/apiHelpers.ts +++ b/source/renderer/app/api/utils/apiHelpers.ts @@ -1,4 +1,5 @@ import { ApiMethodNotYetImplementedError } from '../common/errors'; +import { ErrorType } from '../../domains/ApiError'; export const notYetImplemented = async () => new Promise((resolve, reject) => { @@ -21,3 +22,27 @@ export const testSync = (apiMethod: (...args: Array) => any) => { // helper code for deferring API call execution export const wait = (ms: number): Promise => new Promise((resolve) => setTimeout(resolve, ms)); +export const doesWalletRequireAdaToRemainToSupportTokens = ( + error: ErrorType, + hasAssetsRemainingAfterTransaction?: boolean +): { + requiresAdaToRemainToSupportNativeTokens: boolean; + adaToRemain?: number; +} => { + const adaToProceedRegex = new RegExp( + /.*I need approximately([\s\d.,]+)ada to proceed.*/ + ); + + if ( + error.code === 'cannot_cover_fee' && + hasAssetsRemainingAfterTransaction && + adaToProceedRegex.test(error.message) + ) { + const roundedAda = Math.ceil( + Number(error.message.replace(adaToProceedRegex, '$1')) + ); + const adaToRemain = roundedAda > 2 ? roundedAda : 2; + return { requiresAdaToRemainToSupportNativeTokens: true, adaToRemain }; + } + return { requiresAdaToRemainToSupportNativeTokens: false }; +}; diff --git a/source/renderer/app/components/status/DaedalusDiagnostics.tsx b/source/renderer/app/components/status/DaedalusDiagnostics.tsx index 0418f7da13..d99618a9f6 100644 --- a/source/renderer/app/components/status/DaedalusDiagnostics.tsx +++ b/source/renderer/app/components/status/DaedalusDiagnostics.tsx @@ -24,6 +24,7 @@ import type { CardanoNodeState } from '../../../../common/types/cardano-node.typ import type { SystemInfo } from '../../types/systemInfoTypes'; import type { CoreSystemInfo } from '../../types/coreSystemInfoTypes'; import type { TipInfo } from '../../api/network/types'; +import { ErrorType } from '../../domains/ApiError'; const messages = defineMessages({ systemInfo: { @@ -552,7 +553,7 @@ class DaedalusDiagnostics extends Component { const { isNodeRestarting } = this.state; const isNTPServiceReachable = localTimeDifference != null; const connectionError = get(nodeConnectionError, 'values', '{}'); - const { message, code } = connectionError; + const { message, code } = connectionError as ErrorType; const unknownDiskSpaceSupportUrl = intl.formatMessage( messages.unknownDiskSpaceSupportUrl ); diff --git a/source/renderer/app/components/wallet/send-form/WalletSendAssetsConfirmationDialog.tsx b/source/renderer/app/components/wallet/send-form/WalletSendAssetsConfirmationDialog.tsx index 243ae6c00c..221d6cfb24 100644 --- a/source/renderer/app/components/wallet/send-form/WalletSendAssetsConfirmationDialog.tsx +++ b/source/renderer/app/components/wallet/send-form/WalletSendAssetsConfirmationDialog.tsx @@ -15,7 +15,6 @@ import Dialog from '../../widgets/Dialog'; import DialogCloseButton from '../../widgets/DialogCloseButton'; import LocalizableError from '../../../i18n/LocalizableError'; import styles from './WalletSendAssetsConfirmationDialog.scss'; -// @ts-ignore ts-migrate(2307) FIXME: Cannot find module '../../../assets/images/questio... Remove this comment to see the full error message import questionMarkIcon from '../../../assets/images/question-mark.inline.svg'; import { FORM_VALIDATION_DEBOUNCE_WAIT } from '../../../config/timingConfig'; import { submitOnEnter } from '../../../utils/form'; @@ -146,6 +145,9 @@ class WalletSendAssetsConfirmationDialog extends Component { isHardwareWallet, } = this.props; const { passphrase } = form.values(); + const hasAssetsRemainingAfterTransaction = + this.props.selectedAssets.length < + this.props.allAvailableTokens?.length; const transactionData = { receiver, amount: amountToNaturalUnits(amount), @@ -153,6 +155,7 @@ class WalletSendAssetsConfirmationDialog extends Component { isHardwareWallet, assets: selectedAssets, assetsAmounts, + hasAssetsRemainingAfterTransaction, }; this.props.onSubmit(transactionData); }, @@ -161,6 +164,8 @@ class WalletSendAssetsConfirmationDialog extends Component { }; handleSubmitOnEnter = (event: KeyboardEvent) => (this.props.isHardwareWallet || this.form.$('passphrase').isValid) && + this.props.error?.id !== + 'api.errors.NotEnoughFundsForTransactionFeesErrorWithTokens' && submitOnEnter(this.submit, event); renderConfirmationElement = ( isHardwareWallet: boolean @@ -267,6 +272,8 @@ class WalletSendAssetsConfirmationDialog extends Component { primary: true, className: 'confirmButton', disabled: + error?.id === + 'api.errors.NotEnoughFundsForTransactionFeesErrorWithTokens' || (!isHardwareWallet && !passphraseField.isValid) || (isHardwareWallet && hwDeviceStatus !== @@ -281,7 +288,6 @@ class WalletSendAssetsConfirmationDialog extends Component { let errorElement = null; if (error) { - // @ts-ignore ts-migrate(2339) FIXME: Property 'values' does not exist on type 'Localiza... Remove this comment to see the full error message const errorHasLink = !!error.values.linkLabel; errorElement = errorHasLink ? ( { onExternalLinkClick={onExternalLinkClick} /> ) : ( - intl.formatMessage(error) + ); } diff --git a/source/renderer/app/components/wallet/send-form/WalletSendConfirmationDialog.tsx b/source/renderer/app/components/wallet/send-form/WalletSendConfirmationDialog.tsx index beb63bc039..bacf2974f4 100644 --- a/source/renderer/app/components/wallet/send-form/WalletSendConfirmationDialog.tsx +++ b/source/renderer/app/components/wallet/send-form/WalletSendConfirmationDialog.tsx @@ -116,11 +116,14 @@ class WalletSendConfirmationDialog extends Component { isHardwareWallet, } = this.props; const { passphrase } = form.values(); + const hasAssetsRemainingAfterTransaction = + this.props.allAvailableTokens?.length > 0; const transactionData = { receiver, amount: amountToNaturalUnits(amount), passphrase, isHardwareWallet, + hasAssetsRemainingAfterTransaction, }; this.props.onSubmit(transactionData); }, @@ -129,6 +132,8 @@ class WalletSendConfirmationDialog extends Component { }; handleSubmitOnEnter = (event: KeyboardEvent) => (this.props.isHardwareWallet || this.form.$('passphrase').isValid) && + this.props.error?.id !== + 'api.errors.NotEnoughFundsForTransactionFeesErrorWithTokens' && submitOnEnter(this.submit, event); renderConfirmationElement = ( isHardwareWallet: boolean @@ -218,6 +223,8 @@ class WalletSendConfirmationDialog extends Component { primary: true, className: 'confirmButton', disabled: + error?.id === + 'api.errors.NotEnoughFundsForTransactionFeesErrorWithTokens' || (!isHardwareWallet && !passphraseField.isValid) || (isHardwareWallet && hwDeviceStatus !== @@ -228,7 +235,6 @@ class WalletSendConfirmationDialog extends Component { let errorElement = null; if (error) { - // @ts-ignore ts-migrate(2339) FIXME: Property 'values' does not exist on type 'Localiza... Remove this comment to see the full error message const errorHasLink = !!error.values.linkLabel; errorElement = errorHasLink ? ( { onExternalLinkClick={onExternalLinkClick} /> ) : ( - intl.formatMessage(error) + ); } diff --git a/source/renderer/app/domains/ApiError.ts b/source/renderer/app/domains/ApiError.ts index 1f393d7204..13ee1deadc 100644 --- a/source/renderer/app/domains/ApiError.ts +++ b/source/renderer/app/domains/ApiError.ts @@ -46,12 +46,14 @@ type KnownErrorType = | 'wallet_not_responding' | 'address_already_exists' | 'utxo_too_small' - | 'invalid_smash_server'; + | 'invalid_smash_server' + | 'cannot_cover_fee'; + type LoggingType = { msg?: string; logError?: Record; }; -type ErrorType = { +export type ErrorType = { code?: KnownErrorType; message?: string; }; diff --git a/source/renderer/app/i18n/LocalizableError.ts b/source/renderer/app/i18n/LocalizableError.ts index 9b809da060..e63f61e772 100644 --- a/source/renderer/app/i18n/LocalizableError.ts +++ b/source/renderer/app/i18n/LocalizableError.ts @@ -1,16 +1,18 @@ import ExtendableError from 'es6-error'; import type { ReactIntlMessageShape } from './types'; +type KnownErrorType = 'api.errors.NotEnoughFundsForTransactionFeesErrorWithTokens'; + export default class LocalizableError extends ExtendableError { + id: KnownErrorType | string; + defaultMessage: string; + values?: Record; constructor({ id, defaultMessage, values = {} }: ReactIntlMessageShape) { if (!id) throw new Error('id:string is required.'); if (!defaultMessage) throw new Error('defaultMessage:string is required.'); super(`${id}: ${JSON.stringify(values)}`); - // @ts-ignore ts-migrate(2339) FIXME: Property 'id' does not exist on type 'LocalizableE... Remove this comment to see the full error message this.id = id; - // @ts-ignore ts-migrate(2339) FIXME: Property 'defaultMessage' does not exist on type '... Remove this comment to see the full error message this.defaultMessage = defaultMessage; - // @ts-ignore ts-migrate(2339) FIXME: Property 'values' does not exist on type 'Localiza... Remove this comment to see the full error message this.values = values; } } diff --git a/source/renderer/app/i18n/locales/defaultMessages.json b/source/renderer/app/i18n/locales/defaultMessages.json index c4a8b61ad5..55f11cc6c4 100644 --- a/source/renderer/app/i18n/locales/defaultMessages.json +++ b/source/renderer/app/i18n/locales/defaultMessages.json @@ -285,6 +285,20 @@ "column": 22, "line": 109 } + }, + { + "defaultMessage": "!!!Insufficient funds to support tokens. A minimum of {adaToRemain} ADA must remain in the wallet after this transaction.", + "description": "\"Balance after transaction would not leave enough ada in the wallet to support tokens remaining in wallet", + "end": { + "column": 3, + "line": 120 + }, + "file": "source/renderer/app/api/errors.ts", + "id": "api.errors.NotEnoughFundsForTransactionFeesErrorWithTokens", + "start": { + "column": 26, + "line": 114 + } } ], "path": "source/renderer/app/api/errors.json" @@ -7457,13 +7471,13 @@ "description": "System info", "end": { "column": 3, - "line": 33 + "line": 34 }, "file": "source/renderer/app/components/status/DaedalusDiagnostics.tsx", "id": "daedalus.diagnostics.dialog.system.info", "start": { "column": 14, - "line": 29 + "line": 30 } }, { @@ -7471,13 +7485,13 @@ "description": "Platform", "end": { "column": 3, - "line": 38 + "line": 39 }, "file": "source/renderer/app/components/status/DaedalusDiagnostics.tsx", "id": "daedalus.diagnostics.dialog.platform", "start": { "column": 12, - "line": 34 + "line": 35 } }, { @@ -7485,13 +7499,13 @@ "description": "Platform version", "end": { "column": 3, - "line": 43 + "line": 44 }, "file": "source/renderer/app/components/status/DaedalusDiagnostics.tsx", "id": "daedalus.diagnostics.dialog.platform.version", "start": { "column": 19, - "line": 39 + "line": 40 } }, { @@ -7499,13 +7513,13 @@ "description": "CPU", "end": { "column": 3, - "line": 48 + "line": 49 }, "file": "source/renderer/app/components/status/DaedalusDiagnostics.tsx", "id": "daedalus.diagnostics.dialog.cpu", "start": { "column": 7, - "line": 44 + "line": 45 } }, { @@ -7513,13 +7527,13 @@ "description": "RAM", "end": { "column": 3, - "line": 53 + "line": 54 }, "file": "source/renderer/app/components/status/DaedalusDiagnostics.tsx", "id": "daedalus.diagnostics.dialog.ram", "start": { "column": 7, - "line": 49 + "line": 50 } }, { @@ -7527,13 +7541,13 @@ "description": "Available disk space", "end": { "column": 3, - "line": 58 + "line": 59 }, "file": "source/renderer/app/components/status/DaedalusDiagnostics.tsx", "id": "daedalus.diagnostics.dialog.availableDiskSpace", "start": { "column": 22, - "line": 54 + "line": 55 } }, { @@ -7541,13 +7555,13 @@ "description": "Unknown amount of disk space", "end": { "column": 3, - "line": 63 + "line": 64 }, "file": "source/renderer/app/components/status/DaedalusDiagnostics.tsx", "id": "daedalus.diagnostics.dialog.unknownDiskSpace", "start": { "column": 20, - "line": 59 + "line": 60 } }, { @@ -7555,13 +7569,13 @@ "description": "\"Support\" link URL while disk space is unknown", "end": { "column": 3, - "line": 68 + "line": 69 }, "file": "source/renderer/app/components/status/DaedalusDiagnostics.tsx", "id": "daedalus.diagnostics.dialog.unknownDiskSpaceSupportUrl", "start": { "column": 30, - "line": 64 + "line": 65 } }, { @@ -7569,13 +7583,13 @@ "description": "Displayed on the left of the Recommended system requirements status row", "end": { "column": 3, - "line": 74 + "line": 75 }, "file": "source/renderer/app/components/status/DaedalusDiagnostics.tsx", "id": "daedalus.diagnostics.dialog.hasMetHardwareRequirementsStatus", "start": { "column": 35, - "line": 69 + "line": 70 } }, { @@ -7583,13 +7597,13 @@ "description": "Displayed on the right of the Recommended system requirements status row when hardware requirements are insufficient", "end": { "column": 3, - "line": 80 + "line": 81 }, "file": "source/renderer/app/components/status/DaedalusDiagnostics.tsx", "id": "daedalus.diagnostics.dialog.hasMetHardwareRequirementsStatusLowValue", "start": { "column": 44, - "line": 75 + "line": 76 } }, { @@ -7597,13 +7611,13 @@ "description": "Displayed on the right of the Recommended system requirements status row when hardware requirements are ok", "end": { "column": 3, - "line": 86 + "line": 87 }, "file": "source/renderer/app/components/status/DaedalusDiagnostics.tsx", "id": "daedalus.diagnostics.dialog.hasMetHardwareRequirementsStatusGoodValue", "start": { "column": 45, - "line": 81 + "line": 82 } }, { @@ -7611,13 +7625,13 @@ "description": "Visible on hovering over Recommended system requirement status when status is Low", "end": { "column": 3, - "line": 94 + "line": 95 }, "file": "source/renderer/app/components/status/DaedalusDiagnostics.tsx", "id": "daedalus.diagnostics.dialog.hasMetHardwareRequirementsStatusLowTooltip", "start": { "column": 46, - "line": 87 + "line": 88 } }, { @@ -7625,13 +7639,13 @@ "description": "Visible on hovering over Recommended system requirement status when status is Good", "end": { "column": 3, - "line": 102 + "line": 103 }, "file": "source/renderer/app/components/status/DaedalusDiagnostics.tsx", "id": "daedalus.diagnostics.dialog.hasMetHardwareRequirementsStatusGoodTooltip", "start": { "column": 47, - "line": 95 + "line": 96 } }, { @@ -7639,13 +7653,13 @@ "description": "Indicates whether RTS Flags Mode is enabled or not", "end": { "column": 3, - "line": 107 + "line": 108 }, "file": "source/renderer/app/components/status/DaedalusDiagnostics.tsx", "id": "daedalus.diagnostics.dialog.isRTSFlagsModeEnabled", "start": { "column": 25, - "line": 103 + "line": 104 } }, { @@ -7653,13 +7667,13 @@ "description": "CORE INFO", "end": { "column": 3, - "line": 112 + "line": 113 }, "file": "source/renderer/app/components/status/DaedalusDiagnostics.tsx", "id": "daedalus.diagnostics.dialog.coreInfo", "start": { "column": 12, - "line": 108 + "line": 109 } }, { @@ -7667,13 +7681,13 @@ "description": "Daedalus version", "end": { "column": 3, - "line": 117 + "line": 118 }, "file": "source/renderer/app/components/status/DaedalusDiagnostics.tsx", "id": "daedalus.diagnostics.dialog.daedalusVersion", "start": { "column": 19, - "line": 113 + "line": 114 } }, { @@ -7681,13 +7695,13 @@ "description": "Daedalus build number", "end": { "column": 3, - "line": 122 + "line": 123 }, "file": "source/renderer/app/components/status/DaedalusDiagnostics.tsx", "id": "daedalus.diagnostics.dialog.daedalusBuildNumber", "start": { "column": 23, - "line": 118 + "line": 119 } }, { @@ -7695,13 +7709,13 @@ "description": "Daedalus main process ID", "end": { "column": 3, - "line": 127 + "line": 128 }, "file": "source/renderer/app/components/status/DaedalusDiagnostics.tsx", "id": "daedalus.diagnostics.dialog.daedalusMainProcessID", "start": { "column": 25, - "line": 123 + "line": 124 } }, { @@ -7709,13 +7723,13 @@ "description": "Daedalus renderer process ID", "end": { "column": 3, - "line": 132 + "line": 133 }, "file": "source/renderer/app/components/status/DaedalusDiagnostics.tsx", "id": "daedalus.diagnostics.dialog.daedalusProcessID", "start": { "column": 21, - "line": 128 + "line": 129 } }, { @@ -7723,13 +7737,13 @@ "description": "Daedalus 'Blank Screen Fix' active", "end": { "column": 3, - "line": 137 + "line": 138 }, "file": "source/renderer/app/components/status/DaedalusDiagnostics.tsx", "id": "daedalus.diagnostics.dialog.blankScreenFix", "start": { "column": 18, - "line": 133 + "line": 134 } }, { @@ -7737,13 +7751,13 @@ "description": "Cardano node version", "end": { "column": 3, - "line": 142 + "line": 143 }, "file": "source/renderer/app/components/status/DaedalusDiagnostics.tsx", "id": "daedalus.diagnostics.dialog.cardanoNodeVersion", "start": { "column": 22, - "line": 138 + "line": 139 } }, { @@ -7751,13 +7765,13 @@ "description": "Cardano node process ID", "end": { "column": 3, - "line": 147 + "line": 148 }, "file": "source/renderer/app/components/status/DaedalusDiagnostics.tsx", "id": "daedalus.diagnostics.dialog.cardanoNodePID", "start": { "column": 18, - "line": 143 + "line": 144 } }, { @@ -7765,13 +7779,13 @@ "description": "Cardano node port", "end": { "column": 3, - "line": 152 + "line": 153 }, "file": "source/renderer/app/components/status/DaedalusDiagnostics.tsx", "id": "daedalus.diagnostics.dialog.cardanoNodeApiPort", "start": { "column": 22, - "line": 148 + "line": 149 } }, { @@ -7779,13 +7793,13 @@ "description": "Cardano wallet process ID", "end": { "column": 3, - "line": 157 + "line": 158 }, "file": "source/renderer/app/components/status/DaedalusDiagnostics.tsx", "id": "daedalus.diagnostics.dialog.cardanoWalletPID", "start": { "column": 20, - "line": 153 + "line": 154 } }, { @@ -7793,13 +7807,13 @@ "description": "Cardano wallet version", "end": { "column": 3, - "line": 162 + "line": 163 }, "file": "source/renderer/app/components/status/DaedalusDiagnostics.tsx", "id": "daedalus.diagnostics.dialog.cardanoWalletVersion", "start": { "column": 24, - "line": 158 + "line": 159 } }, { @@ -7807,13 +7821,13 @@ "description": "Cardano wallet port", "end": { "column": 3, - "line": 167 + "line": 168 }, "file": "source/renderer/app/components/status/DaedalusDiagnostics.tsx", "id": "daedalus.diagnostics.dialog.cardanoWalletApiPort", "start": { "column": 24, - "line": 163 + "line": 164 } }, { @@ -7821,13 +7835,13 @@ "description": "Cardano network", "end": { "column": 3, - "line": 172 + "line": 173 }, "file": "source/renderer/app/components/status/DaedalusDiagnostics.tsx", "id": "daedalus.diagnostics.dialog.cardanoNetwork", "start": { "column": 18, - "line": 168 + "line": 169 } }, { @@ -7835,13 +7849,13 @@ "description": "Daedalus state directory", "end": { "column": 3, - "line": 177 + "line": 178 }, "file": "source/renderer/app/components/status/DaedalusDiagnostics.tsx", "id": "daedalus.diagnostics.dialog.stateDirectory", "start": { "column": 22, - "line": 173 + "line": 174 } }, { @@ -7849,13 +7863,13 @@ "description": "Open", "end": { "column": 3, - "line": 182 + "line": 183 }, "file": "source/renderer/app/components/status/DaedalusDiagnostics.tsx", "id": "daedalus.diagnostics.dialog.stateDirectoryPathOpenBtn", "start": { "column": 29, - "line": 178 + "line": 179 } }, { @@ -7863,13 +7877,13 @@ "description": "CONNECTION ERROR", "end": { "column": 3, - "line": 187 + "line": 188 }, "file": "source/renderer/app/components/status/DaedalusDiagnostics.tsx", "id": "daedalus.diagnostics.dialog.connectionError", "start": { "column": 19, - "line": 183 + "line": 184 } }, { @@ -7877,13 +7891,13 @@ "description": "DAEDALUS STATUS", "end": { "column": 3, - "line": 192 + "line": 193 }, "file": "source/renderer/app/components/status/DaedalusDiagnostics.tsx", "id": "daedalus.diagnostics.dialog.daedalusStatus", "start": { "column": 18, - "line": 188 + "line": 189 } }, { @@ -7891,13 +7905,13 @@ "description": "Connected", "end": { "column": 3, - "line": 197 + "line": 198 }, "file": "source/renderer/app/components/status/DaedalusDiagnostics.tsx", "id": "daedalus.diagnostics.dialog.connected", "start": { "column": 13, - "line": 193 + "line": 194 } }, { @@ -7905,13 +7919,13 @@ "description": "Synced", "end": { "column": 3, - "line": 202 + "line": 203 }, "file": "source/renderer/app/components/status/DaedalusDiagnostics.tsx", "id": "daedalus.diagnostics.dialog.synced", "start": { "column": 10, - "line": 198 + "line": 199 } }, { @@ -7919,13 +7933,13 @@ "description": "Sync percentage", "end": { "column": 3, - "line": 207 + "line": 208 }, "file": "source/renderer/app/components/status/DaedalusDiagnostics.tsx", "id": "daedalus.diagnostics.dialog.syncPercentage", "start": { "column": 18, - "line": 203 + "line": 204 } }, { @@ -7933,13 +7947,13 @@ "description": "Local time difference", "end": { "column": 3, - "line": 212 + "line": 213 }, "file": "source/renderer/app/components/status/DaedalusDiagnostics.tsx", "id": "daedalus.diagnostics.dialog.localTimeDifference", "start": { "column": 23, - "line": 208 + "line": 209 } }, { @@ -7947,13 +7961,13 @@ "description": "System time correct", "end": { "column": 3, - "line": 217 + "line": 218 }, "file": "source/renderer/app/components/status/DaedalusDiagnostics.tsx", "id": "daedalus.diagnostics.dialog.systemTimeCorrect", "start": { "column": 21, - "line": 213 + "line": 214 } }, { @@ -7961,13 +7975,13 @@ "description": "System time ignored", "end": { "column": 3, - "line": 222 + "line": 223 }, "file": "source/renderer/app/components/status/DaedalusDiagnostics.tsx", "id": "daedalus.diagnostics.dialog.systemTimeIgnored", "start": { "column": 21, - "line": 218 + "line": 219 } }, { @@ -7975,13 +7989,13 @@ "description": "Checking system time", "end": { "column": 3, - "line": 227 + "line": 228 }, "file": "source/renderer/app/components/status/DaedalusDiagnostics.tsx", "id": "daedalus.diagnostics.dialog.checkingNodeTime", "start": { "column": 20, - "line": 223 + "line": 224 } }, { @@ -7989,13 +8003,13 @@ "description": "CARDANO NODE STATUS", "end": { "column": 3, - "line": 232 + "line": 233 }, "file": "source/renderer/app/components/status/DaedalusDiagnostics.tsx", "id": "daedalus.diagnostics.dialog.cardanoNodeStatus", "start": { "column": 21, - "line": 228 + "line": 229 } }, { @@ -8003,13 +8017,13 @@ "description": "Restarting Cardano node...", "end": { "column": 3, - "line": 237 + "line": 238 }, "file": "source/renderer/app/components/status/DaedalusDiagnostics.tsx", "id": "daedalus.diagnostics.dialog.cardanoNodeStatusRestarting", "start": { "column": 31, - "line": 233 + "line": 234 } }, { @@ -8017,13 +8031,13 @@ "description": "Restart Cardano node", "end": { "column": 3, - "line": 242 + "line": 243 }, "file": "source/renderer/app/components/status/DaedalusDiagnostics.tsx", "id": "daedalus.diagnostics.dialog.cardanoNodeStatusRestart", "start": { "column": 28, - "line": 238 + "line": 239 } }, { @@ -8031,13 +8045,13 @@ "description": "Cardano node state", "end": { "column": 3, - "line": 247 + "line": 248 }, "file": "source/renderer/app/components/status/DaedalusDiagnostics.tsx", "id": "daedalus.diagnostics.dialog.cardanoNodeState", "start": { "column": 20, - "line": 243 + "line": 244 } }, { @@ -8045,13 +8059,13 @@ "description": "Updated", "end": { "column": 3, - "line": 252 + "line": 253 }, "file": "source/renderer/app/components/status/DaedalusDiagnostics.tsx", "id": "daedalus.diagnostics.dialog.nodeHasBeenUpdated", "start": { "column": 22, - "line": 248 + "line": 249 } }, { @@ -8059,13 +8073,13 @@ "description": "Crashed", "end": { "column": 3, - "line": 257 + "line": 258 }, "file": "source/renderer/app/components/status/DaedalusDiagnostics.tsx", "id": "daedalus.diagnostics.dialog.nodeHasCrashed", "start": { "column": 18, - "line": 253 + "line": 254 } }, { @@ -8073,13 +8087,13 @@ "description": "Errored", "end": { "column": 3, - "line": 262 + "line": 263 }, "file": "source/renderer/app/components/status/DaedalusDiagnostics.tsx", "id": "daedalus.diagnostics.dialog.nodeHasErrored", "start": { "column": 18, - "line": 258 + "line": 259 } }, { @@ -8087,13 +8101,13 @@ "description": "Stopped", "end": { "column": 3, - "line": 267 + "line": 268 }, "file": "source/renderer/app/components/status/DaedalusDiagnostics.tsx", "id": "daedalus.diagnostics.dialog.nodeHasStopped", "start": { "column": 18, - "line": 263 + "line": 264 } }, { @@ -8101,13 +8115,13 @@ "description": "Exiting", "end": { "column": 3, - "line": 272 + "line": 273 }, "file": "source/renderer/app/components/status/DaedalusDiagnostics.tsx", "id": "daedalus.diagnostics.dialog.nodeIsExiting", "start": { "column": 17, - "line": 268 + "line": 269 } }, { @@ -8115,13 +8129,13 @@ "description": "Running", "end": { "column": 3, - "line": 277 + "line": 278 }, "file": "source/renderer/app/components/status/DaedalusDiagnostics.tsx", "id": "daedalus.diagnostics.dialog.nodeIsRunning", "start": { "column": 17, - "line": 273 + "line": 274 } }, { @@ -8129,13 +8143,13 @@ "description": "Starting", "end": { "column": 3, - "line": 282 + "line": 283 }, "file": "source/renderer/app/components/status/DaedalusDiagnostics.tsx", "id": "daedalus.diagnostics.dialog.nodeIsStarting", "start": { "column": 18, - "line": 278 + "line": 279 } }, { @@ -8143,13 +8157,13 @@ "description": "Stopping", "end": { "column": 3, - "line": 287 + "line": 288 }, "file": "source/renderer/app/components/status/DaedalusDiagnostics.tsx", "id": "daedalus.diagnostics.dialog.nodeIsStopping", "start": { "column": 18, - "line": 283 + "line": 284 } }, { @@ -8157,13 +8171,13 @@ "description": "Unrecoverable", "end": { "column": 3, - "line": 292 + "line": 293 }, "file": "source/renderer/app/components/status/DaedalusDiagnostics.tsx", "id": "daedalus.diagnostics.dialog.nodeIsUnrecoverable", "start": { "column": 23, - "line": 288 + "line": 289 } }, { @@ -8171,13 +8185,13 @@ "description": "Updating", "end": { "column": 3, - "line": 297 + "line": 298 }, "file": "source/renderer/app/components/status/DaedalusDiagnostics.tsx", "id": "daedalus.diagnostics.dialog.nodeIsUpdating", "start": { "column": 18, - "line": 293 + "line": 294 } }, { @@ -8185,13 +8199,13 @@ "description": "Cardano node responding", "end": { "column": 3, - "line": 302 + "line": 303 }, "file": "source/renderer/app/components/status/DaedalusDiagnostics.tsx", "id": "daedalus.diagnostics.dialog.cardanoNodeResponding", "start": { "column": 25, - "line": 298 + "line": 299 } }, { @@ -8199,13 +8213,13 @@ "description": "Cardano node subscribed", "end": { "column": 3, - "line": 307 + "line": 308 }, "file": "source/renderer/app/components/status/DaedalusDiagnostics.tsx", "id": "daedalus.diagnostics.dialog.cardanoNodeSubscribed", "start": { "column": 25, - "line": 303 + "line": 304 } }, { @@ -8213,13 +8227,13 @@ "description": "Cardano node time correct", "end": { "column": 3, - "line": 312 + "line": 313 }, "file": "source/renderer/app/components/status/DaedalusDiagnostics.tsx", "id": "daedalus.diagnostics.dialog.cardanoNodeTimeCorrect", "start": { "column": 26, - "line": 308 + "line": 309 } }, { @@ -8227,13 +8241,13 @@ "description": "Cardano node syncing", "end": { "column": 3, - "line": 317 + "line": 318 }, "file": "source/renderer/app/components/status/DaedalusDiagnostics.tsx", "id": "daedalus.diagnostics.dialog.cardanoNodeSyncing", "start": { "column": 22, - "line": 313 + "line": 314 } }, { @@ -8241,13 +8255,13 @@ "description": "Cardano node in sync", "end": { "column": 3, - "line": 322 + "line": 323 }, "file": "source/renderer/app/components/status/DaedalusDiagnostics.tsx", "id": "daedalus.diagnostics.dialog.cardanoNodeInSync", "start": { "column": 21, - "line": 318 + "line": 319 } }, { @@ -8255,13 +8269,13 @@ "description": "Checking...", "end": { "column": 3, - "line": 327 + "line": 328 }, "file": "source/renderer/app/components/status/DaedalusDiagnostics.tsx", "id": "daedalus.diagnostics.dialog.localTimeDifferenceChecking", "start": { "column": 31, - "line": 323 + "line": 324 } }, { @@ -8269,13 +8283,13 @@ "description": "Check time", "end": { "column": 3, - "line": 332 + "line": 333 }, "file": "source/renderer/app/components/status/DaedalusDiagnostics.tsx", "id": "daedalus.diagnostics.dialog.localTimeDifferenceCheckTime", "start": { "column": 32, - "line": 328 + "line": 329 } }, { @@ -8283,13 +8297,13 @@ "description": "Yes", "end": { "column": 3, - "line": 337 + "line": 338 }, "file": "source/renderer/app/components/status/DaedalusDiagnostics.tsx", "id": "daedalus.diagnostics.dialog.statusOn", "start": { "column": 12, - "line": 333 + "line": 334 } }, { @@ -8297,13 +8311,13 @@ "description": "No", "end": { "column": 3, - "line": 342 + "line": 343 }, "file": "source/renderer/app/components/status/DaedalusDiagnostics.tsx", "id": "daedalus.diagnostics.dialog.statusOff", "start": { "column": 13, - "line": 338 + "line": 339 } }, { @@ -8311,13 +8325,13 @@ "description": "On", "end": { "column": 3, - "line": 347 + "line": 348 }, "file": "source/renderer/app/components/status/DaedalusDiagnostics.tsx", "id": "daedalus.diagnostics.dialog.statusOnForUserSettings", "start": { "column": 27, - "line": 343 + "line": 344 } }, { @@ -8325,13 +8339,13 @@ "description": "Off", "end": { "column": 3, - "line": 352 + "line": 353 }, "file": "source/renderer/app/components/status/DaedalusDiagnostics.tsx", "id": "daedalus.diagnostics.dialog.statusOffForUserSettings", "start": { "column": 28, - "line": 348 + "line": 349 } }, { @@ -8339,13 +8353,13 @@ "description": "NTP service unreachable", "end": { "column": 3, - "line": 357 + "line": 358 }, "file": "source/renderer/app/components/status/DaedalusDiagnostics.tsx", "id": "daedalus.diagnostics.dialog.serviceUnreachable", "start": { "column": 22, - "line": 353 + "line": 354 } }, { @@ -8353,13 +8367,13 @@ "description": "message", "end": { "column": 3, - "line": 362 + "line": 363 }, "file": "source/renderer/app/components/status/DaedalusDiagnostics.tsx", "id": "daedalus.diagnostics.dialog.message", "start": { "column": 11, - "line": 358 + "line": 359 } }, { @@ -8367,13 +8381,13 @@ "description": "code", "end": { "column": 3, - "line": 367 + "line": 368 }, "file": "source/renderer/app/components/status/DaedalusDiagnostics.tsx", "id": "daedalus.diagnostics.dialog.code", "start": { "column": 8, - "line": 363 + "line": 364 } }, { @@ -8381,13 +8395,13 @@ "description": "Last network block", "end": { "column": 3, - "line": 372 + "line": 373 }, "file": "source/renderer/app/components/status/DaedalusDiagnostics.tsx", "id": "daedalus.diagnostics.dialog.lastNetworkBlock", "start": { "column": 20, - "line": 368 + "line": 369 } }, { @@ -8395,13 +8409,13 @@ "description": "Last synchronized block", "end": { "column": 3, - "line": 377 + "line": 378 }, "file": "source/renderer/app/components/status/DaedalusDiagnostics.tsx", "id": "daedalus.diagnostics.dialog.lastSynchronizedBlock", "start": { "column": 25, - "line": 373 + "line": 374 } }, { @@ -8409,13 +8423,13 @@ "description": "epoch", "end": { "column": 3, - "line": 382 + "line": 383 }, "file": "source/renderer/app/components/status/DaedalusDiagnostics.tsx", "id": "daedalus.diagnostics.dialog.epoch", "start": { "column": 9, - "line": 378 + "line": 379 } }, { @@ -8423,13 +8437,13 @@ "description": "slot", "end": { "column": 3, - "line": 387 + "line": 388 }, "file": "source/renderer/app/components/status/DaedalusDiagnostics.tsx", "id": "daedalus.diagnostics.dialog.slot", "start": { "column": 8, - "line": 383 + "line": 384 } } ], diff --git a/source/renderer/app/i18n/locales/en-US.json b/source/renderer/app/i18n/locales/en-US.json index 7c1110e97c..f2e73a4400 100755 --- a/source/renderer/app/i18n/locales/en-US.json +++ b/source/renderer/app/i18n/locales/en-US.json @@ -6,6 +6,7 @@ "api.errors.IncorrectPasswordError": "Incorrect wallet password.", "api.errors.NotEnoughFundsForTransactionError": "Not enough ada. Try sending a smaller amount.", "api.errors.NotEnoughFundsForTransactionFeesError": "Not enough ada for fees. Try sending a smaller amount.", + "api.errors.NotEnoughFundsForTransactionFeesErrorWithTokens": "Insufficient funds to support tokens. A minimum of {adaToRemain} ADA must remain in the wallet after this transaction.", "api.errors.NotEnoughMoneyToSendError": "Not enough money to make this transaction.", "api.errors.TooBigTransactionError": "Transaction too big due to too many inputs.", "api.errors.TooBigTransactionErrorLinkLabel": "Learn more.", diff --git a/source/renderer/app/i18n/locales/ja-JP.json b/source/renderer/app/i18n/locales/ja-JP.json index 16d18e8c47..1a571a1310 100755 --- a/source/renderer/app/i18n/locales/ja-JP.json +++ b/source/renderer/app/i18n/locales/ja-JP.json @@ -6,6 +6,7 @@ "api.errors.IncorrectPasswordError": "ウォレットパスワードが間違っています", "api.errors.NotEnoughFundsForTransactionError": "ADAが不足しています。送金額を減らしてください。", "api.errors.NotEnoughFundsForTransactionFeesError": "手数料分のADAが不足しています。送金額を減らしてください。", + "api.errors.NotEnoughFundsForTransactionFeesErrorWithTokens": "トークンをサポートする資金が不足しています。このトランザクションの後に、ウォレットに{adaToRemain} ADA以上残るようにしてください。", "api.errors.NotEnoughMoneyToSendError": "このトランザクションに必要な資金が不足しています。", "api.errors.TooBigTransactionError": "入力過多によりトランザクションサイズが超過しています。", "api.errors.TooBigTransactionErrorLinkLabel": "もっと知る。", diff --git a/source/renderer/app/stores/StakingStore.ts b/source/renderer/app/stores/StakingStore.ts index 56a95c24d9..127d5b2fc7 100644 --- a/source/renderer/app/stores/StakingStore.ts +++ b/source/renderer/app/stores/StakingStore.ts @@ -527,7 +527,9 @@ export default class StakingStore extends Store { try { const delegationFee: DelegationCalculateFeeResponse = await this.calculateDelegationFeeRequest.execute( - { ...delegationFeeRequest } + { + ...delegationFeeRequest, + } ).promise; if (this._delegationFeeCalculationWalletId !== walletId) { diff --git a/source/renderer/app/stores/WalletsStore.ts b/source/renderer/app/stores/WalletsStore.ts index daf97704c8..7827d2ffcc 100644 --- a/source/renderer/app/stores/WalletsStore.ts +++ b/source/renderer/app/stores/WalletsStore.ts @@ -503,7 +503,6 @@ export default class WalletsStore extends Store { this._restoreWalletResetData(); - // @ts-ignore ts-migrate(2554) FIXME: Expected 1 arguments, but got 0. this.actions.dialogs.closeActiveDialog.trigger(); } }; @@ -619,7 +618,6 @@ export default class WalletsStore extends Store { await this._patchWalletRequestWithNewWallet(wallet); this.goToWalletRoute(wallet.id); this.refreshWalletsData(); - // @ts-ignore ts-migrate(2554) FIXME: Expected 1 arguments, but got 0. this.actions.dialogs.closeActiveDialog.trigger(); } } catch (error) { @@ -638,7 +636,6 @@ export default class WalletsStore extends Store { if (wallet) { await this._patchWalletRequestWithNewWallet(wallet); - // @ts-ignore ts-migrate(2554) FIXME: Expected 1 arguments, but got 0. this.actions.dialogs.closeActiveDialog.trigger(); this.goToWalletRoute(wallet.id); this.refreshWalletsData(); @@ -684,7 +681,6 @@ export default class WalletsStore extends Store { }); } }); - // @ts-ignore ts-migrate(2554) FIXME: Expected 1 arguments, but got 0. this.actions.dialogs.closeActiveDialog.trigger(); this.actions.walletsLocal.unsetWalletLocalData.trigger({ walletId: params.walletId, @@ -738,7 +734,6 @@ export default class WalletsStore extends Store { } ).promise; this.getWalletRecoveryPhraseFromCertificateRequest.reset(); - // @ts-ignore return unscrambledRecoveryPhrase; }; @action @@ -787,12 +782,14 @@ export default class WalletsStore extends Store { passphrase, assets, assetsAmounts: assetsAmountsStr, + hasAssetsRemainingAfterTransaction, }: { receiver: string; amount: string; passphrase: string; assets?: Array; assetsAmounts?: Array; + hasAssetsRemainingAfterTransaction?: boolean; }) => { const assetsAmounts = assetsAmountsStr ? assetsAmountsStr.map((assetAmount) => new BigNumber(assetAmount)) @@ -818,9 +815,9 @@ export default class WalletsStore extends Store { walletId: wallet.id, isLegacy: wallet.isLegacy, assets: formattedAssets, + hasAssetsRemainingAfterTransaction, }); this.refreshWalletsData(); - // @ts-ignore ts-migrate(2554) FIXME: Expected 1 arguments, but got 0. this.actions.dialogs.closeActiveDialog.trigger(); this.sendMoneyRequest.reset(); this.goToWalletRoute(wallet.id); @@ -1198,7 +1195,6 @@ export default class WalletsStore extends Store { syncState.status !== WalletSyncStateStatuses.NOT_RESPONDING ) .map((wallet: Wallet) => wallet.id); - // @ts-ignore ts-migrate(2554) FIXME: Expected 1 arguments, but got 0. await this.actions.walletsLocal.refreshWalletsLocalData.trigger(); runInAction('refresh active wallet', () => { if (this.active) { @@ -1208,7 +1204,7 @@ export default class WalletsStore extends Store { } }); runInAction('refresh address data', () => { - // @ts-ignore ts-migrate(2322) FIXME: Type '{ walletId: string; allRequest: LocalizedCac... Remove this comment to see the full error message + // @ts-ignore ts-migrate(2339) FIXME: Property 'stores' does not exist on type 'WalletsS... Remove this comment to see the full error message this.stores.addresses.addressesRequests = walletIds.map((walletId) => ({ walletId, allRequest: this.stores.addresses._getAddressesAllRequest(walletId), @@ -1217,7 +1213,7 @@ export default class WalletsStore extends Store { this.stores.addresses._refreshAddresses(); }); runInAction('refresh transaction data', () => { - // @ts-ignore ts-migrate(2322) FIXME: Type '{ walletId: string; recentRequest: Localized... Remove this comment to see the full error message + // @ts-ignore ts-migrate(2339) FIXME: Property 'stores' does not exist on type 'WalletsS... Remove this comment to see the full error message this.stores.transactions.transactionsRequests = walletIds.map( (walletId) => ({ walletId, @@ -1235,7 +1231,6 @@ export default class WalletsStore extends Store { this.stores.transactions._refreshTransactionData(); }); - // @ts-ignore ts-migrate(2554) FIXME: Expected 1 arguments, but got 0. this.actions.wallets.refreshWalletsDataSuccess.trigger(); } }; @@ -1257,7 +1252,6 @@ export default class WalletsStore extends Store { if (!importedWallet) throw new Error('Imported wallet was not received correctly'); await this._patchWalletRequestWithNewWallet(importedWallet); - // @ts-ignore ts-migrate(2554) FIXME: Expected 1 arguments, but got 0. this.actions.dialogs.closeActiveDialog.trigger(); this.importFromFileRequest.reset(); this.goToWalletRoute(importedWallet.id); @@ -1384,9 +1378,11 @@ export default class WalletsStore extends Store { this._updateCertificateCreationState(true); // Generate wallet recovery phrase + const recoveryPhrase: Array = yield this.getWalletRecoveryPhraseRequest.execute() .promise; // Generate 9-words (additional) mnemonic + const additionalMnemonicWords: Array = yield this.getWalletCertificateAdditionalMnemonicsRequest.execute() .promise; this.additionalMnemonicWords = additionalMnemonicWords.join(' '); @@ -1395,6 +1391,7 @@ export default class WalletsStore extends Store { const spendingPassword = mnemonicToSeedHex(this.additionalMnemonicWords); this.walletCertificatePassword = spendingPassword; // Generate paper wallet scrambled mnemonic + const walletCertificateRecoveryPhrase: Array = yield this.getWalletCertificateRecoveryPhraseRequest.execute( { passphrase: spendingPassword, @@ -1449,8 +1446,10 @@ export default class WalletsStore extends Store { const locale = this.stores.profile.currentLocale; const intl = i18nContext(locale); const { isMainnet } = this.environment; - // @ts-ignore ts-migrate(2339) FIXME: Property 'buildLabel' does not exist on type 'type... Remove this comment to see the full error message - const { buildLabel } = global; + const { + // @ts-ignore ts-migrate(2339) FIXME: Property 'buildLabel' does not exist on type 'type... Remove this comment to see the full error message + buildLabel, + } = global; try { await paperWalletPdfGenerator({ @@ -1642,14 +1641,12 @@ export default class WalletsStore extends Store { }; @action _closeCertificateGeneration = () => { - // @ts-ignore ts-migrate(2554) FIXME: Expected 1 arguments, but got 0. this.actions.dialogs.closeActiveDialog.trigger(); this._resetCertificateData(); }; @action _closeRewardsCsvGeneration = () => { - // @ts-ignore ts-migrate(2554) FIXME: Expected 1 arguments, but got 0. this.actions.dialogs.closeActiveDialog.trigger(); this._resetRewardsCsvData();