diff --git a/src/components/dapp-staking/stake-manage/StakeForm.vue b/src/components/dapp-staking/stake-manage/StakeForm.vue index dfce0944b..ef48a8b36 100644 --- a/src/components/dapp-staking/stake-manage/StakeForm.vue +++ b/src/components/dapp-staking/stake-manage/StakeForm.vue @@ -70,6 +70,12 @@ :set-selected-gas="setSelectedTip" /> +
+
+ + {{ $t(warningMsg) }} +
+
{{ $t(errMsg) }}
@@ -136,6 +142,9 @@ export default defineComponent({ getTokenImage({ isNativeToken: true, symbol: nativeTokenSymbol.value }) ); const { selectedTip, nativeTipPrice, setSelectedTip } = useGasPrice(); + const warningMsg = t('dappStaking.error.warningLeaveMinAmount', { + symbol: nativeTokenSymbol.value, + }); const inputHandler = (event: any): void => { amount.value = event.target.value; @@ -151,7 +160,12 @@ export default defineComponent({ }); const toMaxAmount = (): void => { - amount.value = maxAmount.value; + const maximumAmount = ethers.utils.parseEther(maxAmount.value); + // MEMO: it leave 10ASTR in the account so it will keep the balance for longer period. + const leaveAmount = ethers.utils.parseEther('10'); + amount.value = truncate( + ethers.utils.formatEther(maximumAmount.sub(leaveAmount).toString()) + ).toString(); }; const formattedMinStaking = computed(() => { @@ -166,11 +180,15 @@ export default defineComponent({ const stakingAmount = inputAmount + stakedAmount; const isNotEnoughMinAmount = formattedMinStaking.value > stakingAmount; + const formatInputAmount = ethers.utils.parseEther(inputAmount.toString()); + const maximumAmount = ethers.utils.parseEther(maxAmount.value); + const leaveAmount = ethers.utils.parseEther('10'); + if (!inputAmount) { return ''; } - if (isNotEnoughMinAmount) { + if (isNotEnoughMinAmount || maximumAmount.sub(formatInputAmount).lte(leaveAmount)) { return t('dappStaking.error.notEnoughMinAmount', { amount: formattedMinStaking.value, symbol: nativeTokenSymbol.value, @@ -209,6 +227,7 @@ export default defineComponent({ amount, errMsg, maxAmount, + warningMsg, setSelectedTip, toMaxAmount, getShortenAddress, diff --git a/src/components/dapp-staking/stake-manage/StakeManage.vue b/src/components/dapp-staking/stake-manage/StakeManage.vue index a9c0a09a9..5f61c6e19 100644 --- a/src/components/dapp-staking/stake-manage/StakeManage.vue +++ b/src/components/dapp-staking/stake-manage/StakeManage.vue @@ -59,7 +59,6 @@ import { Path } from 'src/router'; import { useStore } from 'src/store'; import { DappCombinedInfo } from 'src/v2/models'; import { computed, defineComponent, ref, watch } from 'vue'; -import { useI18n } from 'vue-i18n'; import { useRoute } from 'vue-router'; export type StakeRightUi = 'information' | 'select-funds-from'; @@ -76,8 +75,6 @@ export default defineComponent({ setup() { const isModalSelectFunds = ref(false); const rightUi = ref('information'); - - const { t } = useI18n(); const { screenSize, width } = useBreakpoints(); const route = useRoute(); useDappRedirect(); diff --git a/src/components/dapp-staking/stake-manage/styles/stake-form.scss b/src/components/dapp-staking/stake-manage/styles/stake-form.scss index 7313528be..1d6287630 100644 --- a/src/components/dapp-staking/stake-manage/styles/stake-form.scss +++ b/src/components/dapp-staking/stake-manage/styles/stake-form.scss @@ -39,17 +39,41 @@ } .row--box-error { - padding: 10px 20px; + display: flex; + flex-direction: column; + row-gap: 6px; + padding: 8px 16px; + margin-bottom: 5px; border-radius: 6px; width: 344px; - margin-bottom: 24px; border: 1px solid $warning-red; background: $box-red; - text-align: left; @media (min-width: $sm) { width: 412px; } } +.row--box-warning { + display: flex; + flex-direction: column; + row-gap: 6px; + border: 1px solid $border-yellow; + padding: 8px 16px; + width: 344px; + border-radius: 6px; + background: $transparent-yellow; + @media (min-width: $sm) { + width: 412px; + } +} +.text--dot { + font-size: 24px; + font-weight: 700; +} +.column--title { + display: flex; + align-items: center; + column-gap: 10px; +} .box__row { display: flex; diff --git a/src/hooks/dapps-staking/useStake.ts b/src/hooks/dapps-staking/useStake.ts index ce1f7db10..972333ea8 100644 --- a/src/hooks/dapps-staking/useStake.ts +++ b/src/hooks/dapps-staking/useStake.ts @@ -8,7 +8,10 @@ import { container } from 'src/v2/common'; import { IDappStakingService } from 'src/v2/services'; import { Symbols } from 'src/v2/symbols'; import { computed, ref, watch } from 'vue'; +import { useNetworkInfo } from 'src/hooks'; +import { useStore } from 'src/store'; import { useRouter, useRoute } from 'vue-router'; +import { useI18n } from 'vue-i18n'; export function useStake() { const router = useRouter(); @@ -17,6 +20,9 @@ export function useStake() { const { stakingList } = useStakingList(); const isStakePage = computed(() => route.fullPath.includes('stake')); const addressTransferFrom = ref(currentAccount.value); + const { t } = useI18n(); + const store = useStore(); + const { nativeTokenSymbol } = useNetworkInfo(); const setAddressTransferFrom = (address: string) => { addressTransferFrom.value = address; @@ -50,6 +56,17 @@ export function useStake() { }) => { const stakeAmount = new BN(ethers.utils.parseEther(amount).toString()); const dappStakingService = container.get(Symbols.DappStakingService); + const balance = new BN(formattedTransferFrom.value.item?.balance || '0'); + if (balance.lt(stakeAmount)) { + store.dispatch('general/showAlertMsg', { + msg: t('dappStaking.error.invalidBalance', { + symbol: nativeTokenSymbol.value, + }), + alertType: 'error', + }); + return; + } + if (formattedTransferFrom.value.isNominationTransfer) { if (!formattedTransferFrom.value.item) return; await dappStakingService.nominationTransfer({ diff --git a/src/hooks/useClaimAll.ts b/src/hooks/useClaimAll.ts index fe059ab9d..9b986d300 100644 --- a/src/hooks/useClaimAll.ts +++ b/src/hooks/useClaimAll.ts @@ -6,14 +6,16 @@ import { import { ISubmittableResult } from '@polkadot/types/types'; import { BN } from '@polkadot/util'; import { $api } from 'boot/api'; -import { useCurrentEra } from 'src/hooks'; +import { useCurrentEra, useBalance } from 'src/hooks'; import { displayCustomMessage, TxType } from 'src/hooks/custom-signature/message'; import { useStore } from 'src/store'; import { container } from 'src/v2/common'; import { DappCombinedInfo } from 'src/v2/models/DappsStaking'; import { IDappStakingService } from 'src/v2/services'; import { Symbols } from 'src/v2/symbols'; +import { ethers } from 'ethers'; import { computed, ref, watchEffect } from 'vue'; +import { useNetworkInfo } from 'src/hooks'; import { useI18n } from 'vue-i18n'; const MAX_BATCH_WEIGHT = new BN('50000000000'); @@ -31,6 +33,16 @@ export function useClaimAll() { const isSendingTx = computed(() => store.getters['general/isLoading']); const { t } = useI18n(); const { era } = useCurrentEra(); + const selectedAddress = computed(() => store.getters['general/selectedAddress']); + const { accountData } = useBalance(selectedAddress); + const { nativeTokenSymbol } = useNetworkInfo(); + + const transferableBalance = computed(() => { + const balance = accountData.value + ? ethers.utils.formatEther(accountData.value.getUsableTransactionBalance().toString()) + : '0'; + return Number(balance); + }); watchEffect(async () => { try { @@ -103,6 +115,22 @@ export function useClaimAll() { `Batch weight: ${totalWeight.toString()}, transactions no. ${txsToExecute.length}` ); const transaction = api.tx.utility.batch(txsToExecute); + const info = await api.tx.utility.batch(txsToExecute).paymentInfo(senderAddress.value); + const partialFee = info.partialFee.toBn(); + const balance = new BN( + ethers.utils.parseEther(transferableBalance.value.toString()).toString() + ); + + if (balance.sub(partialFee.muln(1.5)).isNeg()) { + store.dispatch('general/showAlertMsg', { + msg: t('dappStaking.error.invalidBalance', { + symbol: nativeTokenSymbol.value, + }), + alertType: 'error', + }); + return; + } + const finalizedCallback = (result: ISubmittableResult): void => { displayCustomMessage({ txType: TxType.dappsStaking, diff --git a/src/i18n/en-US/index.ts b/src/i18n/en-US/index.ts index 4923df1e9..1475636bd 100644 --- a/src/i18n/en-US/index.ts +++ b/src/i18n/en-US/index.ts @@ -315,6 +315,10 @@ export default { 'The amount of token to be staking must be greater than {amount} {symbol}', allFundsWillBeTransferred: 'All funds will be transferred because the min. staking amount is {minStakingAmount} {symbol}', + invalidBalance: + 'Invalid balance to make a claim. Please add {symbol} tokens in to the account', + warningLeaveMinAmount: + 'Account must hold greater than 10{symbol} in transferrable when you stake.', }, }, assets: {