diff --git a/packages/extension-base/src/koni/background/handlers/Extension.ts b/packages/extension-base/src/koni/background/handlers/Extension.ts index ad8c22d398..273ce331bd 100644 --- a/packages/extension-base/src/koni/background/handlers/Extension.ts +++ b/packages/extension-base/src/koni/background/handlers/Extension.ts @@ -1782,7 +1782,6 @@ export default class KoniExtension { const substrateApi = this.#koniState.chainService.getSubstrateApi(originTokenInfo.originChain); const chainInfoMap = this.#koniState.chainService.getChainInfoMap(); const destinationTokenInfo = this.#koniState.getXcmEqualAssetByChain(destChain, originTokenInfo.slug); - const existentialDeposit = originTokenInfo.minAmount || '0'; // todo: improve this case. Currently set 1 AVAIL for covering fee as default. const isSpecialBridgeFromAvail = originTokenInfo.slug === 'avail_mainnet-NATIVE-AVAIL' && destChain === COMMON_CHAIN_SLUGS.ETHEREUM; @@ -1795,9 +1794,9 @@ export default class KoniExtension { ]); const bnMaxTransferable = new BigN(value); - const estimatedFee = isSpecialBridgeFromAvail ? specialBridgeFromAvailFee : bnMockExecutionFee.multipliedBy(XCM_FEE_RATIO).plus(new BigN(existentialDeposit)); + const txFee = isSpecialBridgeFromAvail ? specialBridgeFromAvailFee : bnMockExecutionFee.multipliedBy(XCM_FEE_RATIO); - return bnMaxTransferable.minus(estimatedFee); + return bnMaxTransferable.minus(txFee); } return new BigN(0); diff --git a/packages/extension-base/src/services/earning-service/handlers/special.ts b/packages/extension-base/src/services/earning-service/handlers/special.ts index 9d95dda327..cfbb4d994e 100644 --- a/packages/extension-base/src/services/earning-service/handlers/special.ts +++ b/packages/extension-base/src/services/earning-service/handlers/special.ts @@ -3,11 +3,11 @@ import { TransactionError } from '@subwallet/extension-base/background/errors/TransactionError'; import { AmountData, ChainType, ExtrinsicType } from '@subwallet/extension-base/background/KoniTypes'; -import { ALL_ACCOUNT_KEY } from '@subwallet/extension-base/constants'; +import { ALL_ACCOUNT_KEY, XCM_FEE_RATIO } from '@subwallet/extension-base/constants'; import { YIELD_POOL_STAT_REFRESH_INTERVAL } from '@subwallet/extension-base/koni/api/yield/helper/utils'; import KoniState from '@subwallet/extension-base/koni/background/handlers/State'; import { createXcmExtrinsic } from '@subwallet/extension-base/services/balance-service/transfer/xcm'; -import { _getChainNativeTokenSlug } from '@subwallet/extension-base/services/chain-service/utils'; +import { _getAssetDecimals, _getAssetExistentialDeposit, _getAssetName, _getAssetSymbol, _getChainNativeTokenSlug } from '@subwallet/extension-base/services/chain-service/utils'; import { BaseYieldStepDetail, BasicTxErrorType, HandleYieldStepData, OptimalYieldPath, OptimalYieldPathParams, RequestCrossChainTransfer, RequestEarlyValidateYield, ResponseEarlyValidateYield, RuntimeDispatchInfo, SpecialYieldPoolInfo, SpecialYieldPoolMetadata, SubmitYieldJoinData, SubmitYieldStepData, TransactionData, UnstakingInfo, YieldPoolInfo, YieldPoolTarget, YieldPoolType, YieldProcessValidation, YieldStepBaseInfo, YieldStepType, YieldTokenBaseInfo, YieldValidationStatus } from '@subwallet/extension-base/types'; import { createPromiseHandler, formatNumber, PromiseHandler } from '@subwallet/extension-base/utils'; import { t } from 'i18next'; @@ -96,20 +96,35 @@ export default abstract class BaseSpecialStakingPoolHandler extends BasePoolHand ]); const bnInputAssetBalance = new BN(inputAssetBalance.value); - const bnAltInputAssetBalance = new BN(altInputAssetBalance.value); + const bnMinJoinPool = new BN(poolInfo.statistic.earningThreshold.join); const inputTokenInfo = this.state.chainService.getAssetBySlug(this.inputAsset); const altInputTokenInfo = this.state.chainService.getAssetBySlug(this.altInputAsset); + const existentialDeposit = new BN(_getAssetExistentialDeposit(altInputTokenInfo)); + const bnAltInputAssetBalance = new BN(altInputAssetBalance.value); + if (bnInputAssetBalance.add(bnAltInputAssetBalance).lt(bnMinJoinPool)) { + const missingAmount = bnMinJoinPool.sub(bnInputAssetBalance).sub(bnAltInputAssetBalance); + const isTheSame = missingAmount.toString() === bnMinJoinPool.toString(); const originChain = this.state.getChainInfo(inputTokenInfo.originChain); const altChain = this.state.getChainInfo(altInputTokenInfo.originChain); - const parsedMinJoinPool = formatNumber(bnMinJoinPool.toString(), inputAssetInfo.decimals || 0); + + const originSymbol = _getAssetSymbol(inputTokenInfo); + const altSymbol = _getAssetSymbol(altInputTokenInfo); + + const originName = originChain.name; + const altName = altChain.name; + + const parsedMinJoinPool = formatNumber(missingAmount.toString(), inputAssetInfo.decimals || 0); + const formatparsedMinJoinPool = isTheSame ? parsedMinJoinPool : Number(parsedMinJoinPool) + 0.01; + const parsedMinAltJoinPool = formatNumber((missingAmount.add(existentialDeposit)).toString(), inputAssetInfo.decimals || 0); + const formatParsedMinAltJoinPool = isTheSame ? parsedMinAltJoinPool : Number(parsedMinAltJoinPool) + 0.01; return { passed: false, - errorMessage: `You need at least ${parsedMinJoinPool} ${inputTokenInfo.symbol} (${originChain.name}) or ${altInputTokenInfo.symbol} (${altChain.name}) to start earning` + errorMessage: `You need to deposit an additional ${formatparsedMinJoinPool} ${originSymbol} (${originName}) or ${formatParsedMinAltJoinPool} ${altSymbol} (${altName}) to start earning` }; } @@ -345,6 +360,7 @@ export default abstract class BaseSpecialStakingPoolHandler extends BasePoolHand const missingAmount = bnAmount.sub(bnInputTokenBalance); // TODO: what if input token is not LOCAL ?? const xcmFee = new BN(path.totalFee[1].amount || '0'); + const xcmAmount = missingAmount.add(xcmFee); const bnAltInputTokenBalance = new BN(altInputTokenBalance.value || '0'); @@ -354,17 +370,17 @@ export default abstract class BaseSpecialStakingPoolHandler extends BasePoolHand processValidation.ok = false; processValidation.status = YieldValidationStatus.NOT_ENOUGH_BALANCE; + const bnMaxXCM = new BN(altInputTokenBalance.value).sub(xcmFee.mul(new BN(XCM_FEE_RATIO))); + const inputTokenDecimal = _getAssetDecimals(inputTokenInfo); const maxBn = bnInputTokenBalance.add(new BN(altInputTokenBalance.value)).sub(xcmFee).sub(xcmFee); const maxValue = formatNumber(maxBn.toString(), inputTokenInfo.decimals || 0); + const maxXCMValue = formatNumber(bnMaxXCM.toString(), inputTokenDecimal); + + const symbol = _getAssetSymbol(altInputTokenInfo); - const altInputTokenInfo = this.state.getAssetBySlug(altInputTokenSlug); - const symbol = altInputTokenInfo.symbol; - const altNetwork = this.state.getChainInfo(altInputTokenInfo.originChain); const inputNetworkName = this.chainInfo.name; - const altNetworkName = altNetwork.name; - const currentValue = formatNumber(bnInputTokenBalance.toString(), inputTokenInfo.decimals || 0); - const bnMaxXCM = new BN(altInputTokenBalance.value).sub(xcmFee).sub(xcmFee); - const maxXCMValue = formatNumber(bnMaxXCM.toString(), inputTokenInfo.decimals || 0); + const altNetworkName = _getAssetName(altInputTokenInfo); + const currentValue = formatNumber(bnInputTokenBalance.toString(), inputTokenDecimal); processValidation.message = t( 'You can only enter a maximum of {{maxValue}} {{symbol}}, which is {{currentValue}} {{symbol}} ({{inputNetworkName}}) and {{maxXCMValue}} {{symbol}} ({{altNetworkName}}). Lower your amount and try again.',