diff --git a/CHANGELOG.md b/CHANGELOG.md index 876591712c..ac43d60e18 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## vNext + +### Fixes + +- Fixed phrasing of insufficient funds for tokens message ([PR 2966](https://github.com/input-output-hk/daedalus/pull/2966)) + ## 4.10.0 ### Features diff --git a/source/renderer/app/api/api.ts b/source/renderer/app/api/api.ts index 080a10d245..340dabc1fa 100644 --- a/source/renderer/app/api/api.ts +++ b/source/renderer/app/api/api.ts @@ -906,13 +906,15 @@ export default class AdaApi { const { requiresAdaToRemainToSupportNativeTokens, - adaToRemain, + adaToProceed, } = doesWalletRequireAdaToRemainToSupportTokens( error, hasAssetsRemainingAfterTransaction ); if (requiresAdaToRemainToSupportNativeTokens) { - apiError.set('cannotLeaveWalletEmpty', true, { adaToRemain }); + apiError.set('cannotLeaveWalletEmpty', true, { + adaAmount: adaToProceed, + }); } throw apiError.result(); diff --git a/source/renderer/app/api/errors.ts b/source/renderer/app/api/errors.ts index 9f0eee4578..0f0e84b501 100644 --- a/source/renderer/app/api/errors.ts +++ b/source/renderer/app/api/errors.ts @@ -114,7 +114,7 @@ export const messages = defineMessages({ cannotLeaveWalletEmpty: { id: 'api.errors.NotEnoughFundsForTransactionFeesErrorWithTokens', defaultMessage: - '!!!Insufficient funds to support tokens. A minimum of {adaToRemain} ADA must remain in the wallet after this transaction.', + '!!!Insufficient funds to support tokens. You need at least an additional {adaAmount} ADA in your wallet to process 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 index 07fedfa470..77b65515b8 100644 --- a/source/renderer/app/api/utils/apiHelpers.spec.ts +++ b/source/renderer/app/api/utils/apiHelpers.spec.ts @@ -48,20 +48,8 @@ describe('throw error if not enough Ada to support tokens', () => { 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', () => { + it('should throw if there are tokens remaining in wallet after transaction and error is "cannot_cover_fee"', () => { 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.', @@ -70,7 +58,7 @@ describe('throw error if not enough Ada to support tokens', () => { expect(doesWalletRequireAdaToRemainToSupportTokens(error, true)).toEqual({ requiresAdaToRemainToSupportNativeTokens: true, - adaToRemain: 3, + adaToProceed: 3, }); }); }); diff --git a/source/renderer/app/api/utils/apiHelpers.ts b/source/renderer/app/api/utils/apiHelpers.ts index 85f51f2927..08ed2ec5a1 100644 --- a/source/renderer/app/api/utils/apiHelpers.ts +++ b/source/renderer/app/api/utils/apiHelpers.ts @@ -27,22 +27,21 @@ export const doesWalletRequireAdaToRemainToSupportTokens = ( hasAssetsRemainingAfterTransaction?: boolean ): { requiresAdaToRemainToSupportNativeTokens: boolean; - adaToRemain?: number; + adaToProceed?: number; } => { - const adaToProceedRegex = new RegExp( - /.*I need approximately([\s\d.,]+)ada to proceed.*/ - ); + const adaToProceedRegex = /I need approximately\s+([\d.,]+)\s+ada to proceed/; + + const [, adaToProceed] = adaToProceedRegex.exec(error.message) ?? []; if ( error.code === 'cannot_cover_fee' && hasAssetsRemainingAfterTransaction && - adaToProceedRegex.test(error.message) + adaToProceed ) { - const roundedAda = Math.ceil( - Number(error.message.replace(adaToProceedRegex, '$1')) - ); - const adaToRemain = roundedAda > 2 ? roundedAda : 2; - return { requiresAdaToRemainToSupportNativeTokens: true, adaToRemain }; + return { + requiresAdaToRemainToSupportNativeTokens: true, + adaToProceed: Math.ceil(Number(adaToProceed)), + }; } return { requiresAdaToRemainToSupportNativeTokens: false }; }; diff --git a/source/renderer/app/containers/wallet/dialogs/send-confirmation/helpers.spec.ts b/source/renderer/app/containers/wallet/dialogs/send-confirmation/helpers.spec.ts new file mode 100644 index 0000000000..6b99ca8613 --- /dev/null +++ b/source/renderer/app/containers/wallet/dialogs/send-confirmation/helpers.spec.ts @@ -0,0 +1,88 @@ +import BigNumber from 'bignumber.js'; +import { hasAssetsAfterTransaction } from './helpers'; + +const assetWithOneQuantity = { + policyId: 'policyId1', + assetName: '54657374636f696e', + quantity: new BigNumber(1), + fingerprint: 'policyId154657374636f696e', + metadata: { + name: 'Testcoin', + description: 'Test coin', + }, + uniqueId: 'uniqueId1', + decimals: 1, + recommendedDecimals: null, +}; + +const assetWithTenQuantity = { + policyId: 'policyId2', + assetName: '436f696e74657374', + quantity: new BigNumber(10), + fingerprint: 'policyId2436f696e74657374', + uniqueId: 'uniqueId2', + decimals: 1, + recommendedDecimals: null, +}; + +describe('hasAssetsAfterTransaction', () => { + test('Should be true when wallet has assets and tx sending no assets', () => { + expect( + hasAssetsAfterTransaction({ + assetTokens: [assetWithOneQuantity, assetWithTenQuantity], + selectedAssets: [], + assetsAmounts: [], + }) + ).toEqual(true); + }); + + test('Should be true when wallet has assets and tx is not sending all asset types', () => { + expect( + hasAssetsAfterTransaction({ + assetTokens: [assetWithOneQuantity, assetWithTenQuantity], + selectedAssets: [assetWithTenQuantity], + assetsAmounts: ['5'], + }) + ).toEqual(true); + }); + + test('Should be true when wallet has assets and tx is not sending all assets', () => { + expect( + hasAssetsAfterTransaction({ + assetTokens: [assetWithOneQuantity, assetWithTenQuantity], + selectedAssets: [assetWithOneQuantity, assetWithTenQuantity], + assetsAmounts: ['1', '5'], + }) + ).toEqual(true); + }); + + test('Should be false when wallet has no assets', () => { + expect( + hasAssetsAfterTransaction({ + assetTokens: [], + selectedAssets: [], + assetsAmounts: [], + }) + ).toEqual(false); + }); + + test('Should be false when tx is sending all assets (1 asset)', () => { + expect( + hasAssetsAfterTransaction({ + assetTokens: [assetWithOneQuantity], + selectedAssets: [assetWithOneQuantity], + assetsAmounts: ['1'], + }) + ).toEqual(false); + }); + + test('Should be false when tx is sending all assets (2 assets)', () => { + expect( + hasAssetsAfterTransaction({ + assetTokens: [assetWithOneQuantity, assetWithTenQuantity], + selectedAssets: [assetWithOneQuantity, assetWithTenQuantity], + assetsAmounts: ['1', '10'], + }) + ).toEqual(false); + }); +}); diff --git a/source/renderer/app/containers/wallet/dialogs/send-confirmation/helpers.ts b/source/renderer/app/containers/wallet/dialogs/send-confirmation/helpers.ts index 59dfe902c5..540ce70ec3 100644 --- a/source/renderer/app/containers/wallet/dialogs/send-confirmation/helpers.ts +++ b/source/renderer/app/containers/wallet/dialogs/send-confirmation/helpers.ts @@ -7,7 +7,12 @@ import { AssetToken } from '../../../../api/assets/types'; import { HwDeviceStatuses } from '../../../../domains/Wallet'; import { FORM_VALIDATION_DEBOUNCE_WAIT } from '../../../../config/timingConfig'; import { formattedTokenWalletAmount } from '../../../../utils/formatters'; -import { CreateForm, FormFields, HasAssetsAfterTransaction } from './types'; +import { + CreateForm, + FormFields, + HasAssetsAfterTransaction, + IsSendingAllFromSelected, +} from './types'; export const doTermsNeedAcceptance = ({ isFlight, areTermsAccepted }) => !areTermsAccepted && isFlight; @@ -25,13 +30,32 @@ export const isNotEnoughFundsForTokenError = (errorId) => export const isPasswordValid = ({ isHardwareWallet, isValid }) => isHardwareWallet || isValid; +export const isSendingAllFromSelected = ({ + selectedAssets = [], + assetsAmounts = [], +}: IsSendingAllFromSelected) => + Boolean(selectedAssets.length) && + selectedAssets.every(({ quantity }, i) => + quantity.isEqualTo(assetsAmounts?.[i]) + ); + export const hasAssetsAfterTransaction = ({ selectedAssets = [], assetTokens = [], -}: HasAssetsAfterTransaction) => - selectedAssets.length - ? selectedAssets.length < assetTokens.length - : assetTokens.length > 0; + assetsAmounts = [], +}: HasAssetsAfterTransaction) => { + const sendingTokens = Boolean(selectedAssets.length); + const hasTokens = Boolean(assetTokens.length); + const sendingAllTokenTypes = selectedAssets.length === assetTokens.length; + const sendingAllFromSelected = isSendingAllFromSelected({ + assetsAmounts, + selectedAssets, + }); + + return sendingTokens + ? !(sendingAllTokenTypes && sendingAllFromSelected) + : hasTokens; +}; export const getFormattedAssetAmount = ( { metadata, decimals }: AssetToken, diff --git a/source/renderer/app/containers/wallet/dialogs/send-confirmation/hooks.ts b/source/renderer/app/containers/wallet/dialogs/send-confirmation/hooks.ts index 4e168fb557..d5cdd82351 100644 --- a/source/renderer/app/containers/wallet/dialogs/send-confirmation/hooks.ts +++ b/source/renderer/app/containers/wallet/dialogs/send-confirmation/hooks.ts @@ -39,6 +39,7 @@ export const useForm = ({ hasAssetsRemainingAfterTransaction: hasAssetsAfterTransaction({ assetTokens, selectedAssets, + assetsAmounts, }), ...(selectedAssets.length ? { assets: selectedAssets, assetsAmounts } diff --git a/source/renderer/app/containers/wallet/dialogs/send-confirmation/types.ts b/source/renderer/app/containers/wallet/dialogs/send-confirmation/types.ts index 5df99942b0..caeebbc1da 100644 --- a/source/renderer/app/containers/wallet/dialogs/send-confirmation/types.ts +++ b/source/renderer/app/containers/wallet/dialogs/send-confirmation/types.ts @@ -94,5 +94,10 @@ export type CreateForm = Pick; export type HasAssetsAfterTransaction = Pick< ViewProps, - 'assetTokens' | 'selectedAssets' + 'assetTokens' | 'selectedAssets' | 'assetsAmounts' +>; + +export type IsSendingAllFromSelected = Pick< + ViewProps, + 'selectedAssets' | 'assetsAmounts' >; diff --git a/source/renderer/app/i18n/locales/defaultMessages.json b/source/renderer/app/i18n/locales/defaultMessages.json index c7148727eb..76f9aa4e3b 100644 --- a/source/renderer/app/i18n/locales/defaultMessages.json +++ b/source/renderer/app/i18n/locales/defaultMessages.json @@ -287,7 +287,7 @@ } }, { - "defaultMessage": "!!!Insufficient funds to support tokens. A minimum of {adaToRemain} ADA must remain in the wallet after this transaction.", + "defaultMessage": "!!!Insufficient funds to support tokens. You need at least an additional {adaAmount} ADA in your wallet to process this transaction.", "description": "\"Balance after transaction would not leave enough ada in the wallet to support tokens remaining in wallet", "end": { "column": 3, diff --git a/source/renderer/app/i18n/locales/en-US.json b/source/renderer/app/i18n/locales/en-US.json index eb9b1b8ac8..24ab166d43 100755 --- a/source/renderer/app/i18n/locales/en-US.json +++ b/source/renderer/app/i18n/locales/en-US.json @@ -6,7 +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.NotEnoughFundsForTransactionFeesErrorWithTokens": "Insufficient funds to support tokens. You need at least an additional {adaAmount} ADA in your wallet to process 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 46662c6d08..01c6a43352 100755 --- a/source/renderer/app/i18n/locales/ja-JP.json +++ b/source/renderer/app/i18n/locales/ja-JP.json @@ -6,7 +6,7 @@ "api.errors.IncorrectPasswordError": "ウォレットパスワードが間違っています", "api.errors.NotEnoughFundsForTransactionError": "ADAが不足しています。送金額を減らしてください。", "api.errors.NotEnoughFundsForTransactionFeesError": "手数料分のADAが不足しています。送金額を減らしてください。", - "api.errors.NotEnoughFundsForTransactionFeesErrorWithTokens": "トークンをサポートする資金が不足しています。このトランザクションの後に、ウォレットに{adaToRemain} ADA以上残るようにしてください。", + "api.errors.NotEnoughFundsForTransactionFeesErrorWithTokens": "トークンをサポートする資金が不足しています。このトランザクションを処理するためには、ウォレットに少なくともあと{adaAmount} ADAが必要です。", "api.errors.NotEnoughMoneyToSendError": "このトランザクションに必要な資金が不足しています。", "api.errors.TooBigTransactionError": "入力過多によりトランザクションサイズが超過しています。", "api.errors.TooBigTransactionErrorLinkLabel": "もっと知る。", @@ -232,7 +232,7 @@ "loading.screen.updatingCardanoMessage": "Cardanoノードを更新しています", "loading.screen.validatingChunk": "ディスクのブロックチェーンステータスの検証", "loading.screen.validatingChunkDescription": "ブロックチェーンのハッシュ計算の整合性を検証します", - "news.newsfeed.empty": "Newsfeed is empty", + "news.newsfeed.empty": "ニュースフィードは空です", "news.newsfeed.iconTooltip": "ニュースフィード", "news.newsfeed.noFetch": "ニュースフィードを読み込んでいます...", "news.newsfeed.title": "ニュースフィード",