diff --git a/packages/@core-js/src/managers/BatteryManager.ts b/packages/@core-js/src/managers/BatteryManager.ts index 96672738a..4f98e8d8c 100644 --- a/packages/@core-js/src/managers/BatteryManager.ts +++ b/packages/@core-js/src/managers/BatteryManager.ts @@ -1,32 +1,42 @@ import { WalletContext, WalletIdentity } from '../Wallet'; import { MessageConsequences } from '../TonAPI'; +import { Storage } from '../declarations/Storage'; +import { State } from '../utils/State'; -export class BatteryManager { - constructor(private ctx: WalletContext, private identity: WalletIdentity) {} +export interface BatteryState { + isLoading: boolean; + balance?: string; +} - public get cacheKey() { - return ['battery', this.ctx.address.ton.raw]; +export const batteryState = new State({ + isLoading: false, + balance: undefined, +}); + +export class BatteryManager { + public state = batteryState; + + constructor( + private ctx: WalletContext, + private identity: WalletIdentity, + private storage: Storage, + ) { + this.state.persist({ + partialize: ({ balance }) => ({ balance }), + storage: this.storage, + key: 'battery', + }); } - public async getBalance() { + public async fetchBalance() { try { + this.state.set({ isLoading: true }); const data = await this.ctx.batteryapi.getBalance({ headers: { 'X-TonConnect-Auth': this.identity.tonProof, }, }); - return data.balance; - } catch (err) { - return null; - } - } - - public async refetchBalance() { - try { - await this.ctx.queryClient.refetchQueries({ - refetchPage: (_, index) => index === 0, - queryKey: this.cacheKey, - }); + this.state.set({ isLoading: false, balance: data.balance }); } catch (err) { return null; } @@ -58,7 +68,7 @@ export class BatteryManager { ); if (data.success) { - this.refetchBalance(); + this.fetchBalance(); } return data; @@ -78,7 +88,7 @@ export class BatteryManager { }, ); - await this.refetchBalance(); + await this.fetchBalance(); return data.transactions; } catch (err) { @@ -97,7 +107,7 @@ export class BatteryManager { }, ); - await this.refetchBalance(); + await this.fetchBalance(); return data.purchases; } catch (err) { @@ -117,7 +127,7 @@ export class BatteryManager { }, ); - await this.refetchBalance(); + await this.fetchBalance(); } catch (err) { console.log('[battery sendMessage]', err); throw new Error(err); @@ -139,4 +149,12 @@ export class BatteryManager { throw new Error(err); } } + + public async rehydrate() { + return this.state.rehydrate(); + } + + public async clear() { + return this.state.clear(); + } } diff --git a/packages/mobile/src/blockchain/wallet.ts b/packages/mobile/src/blockchain/wallet.ts index a8e3788e1..d8d7bf518 100644 --- a/packages/mobile/src/blockchain/wallet.ts +++ b/packages/mobile/src/blockchain/wallet.ts @@ -7,6 +7,7 @@ import { getServerConfig } from '$shared/constants'; import { UnlockedVault, Vault } from './vault'; import { Address as AddressFormatter, + BASE_FORWARD_AMOUNT, ContractService, contractVersionsMap, isActiveAccount, @@ -498,7 +499,7 @@ export class TonWallet { throw new Error(t('send_fee_estimation_error')); } - const transferAmount = feeNano.plus(toNano('0.05').toString()); + const transferAmount = feeNano.plus(BASE_FORWARD_AMOUNT.toString()).toString(); if (this.isLockup()) { const lockupBalances = await this.getLockupBalances(sender); diff --git a/packages/mobile/src/core/NFTSend/NFTSend.tsx b/packages/mobile/src/core/NFTSend/NFTSend.tsx index e7a1e4558..e3a520e0e 100644 --- a/packages/mobile/src/core/NFTSend/NFTSend.tsx +++ b/packages/mobile/src/core/NFTSend/NFTSend.tsx @@ -106,7 +106,7 @@ export const NFTSend: FC = (props) => { const excessesAccount = !config.get('disable_battery_send') ? await tk.wallet.battery.getExcessesAccount() - : null; + : undefined; const nftTransferMessages = [ internal({ @@ -133,9 +133,10 @@ export const NFTSend: FC = (props) => { secretKey: Buffer.alloc(64), }); - const response = await emulateWithBattery(boc, { - params: [setBalanceForEmulation(toNano('2'))], // Emulate with higher balance to calculate fair amount to send - }); + const response = await emulateWithBattery( + boc, + [setBalanceForEmulation(toNano('2'))], // Emulate with higher balance to calculate fair amount to send + ); setConsequences(response.emulateResult); setIsBattery(response.battery); @@ -175,7 +176,7 @@ export const NFTSend: FC = (props) => { : BigInt(Math.abs(consequences?.event.extra!)) + BASE_FORWARD_AMOUNT; const checkResult = await checkIsInsufficient(totalAmount.toString()); - if (checkResult.insufficient) { + if (!isBattery && checkResult.insufficient) { openInsufficientFundsModal({ totalAmount: totalAmount.toString(), balance: checkResult.balance, @@ -220,6 +221,7 @@ export const NFTSend: FC = (props) => { }, [ comment, consequences?.event.extra, + isBattery, nftAddress, recipient, total.isRefund, diff --git a/packages/mobile/src/store/wallet/sagas.ts b/packages/mobile/src/store/wallet/sagas.ts index 731500994..c221fd162 100644 --- a/packages/mobile/src/store/wallet/sagas.ts +++ b/packages/mobile/src/store/wallet/sagas.ts @@ -84,7 +84,7 @@ import { clearSubscribeStatus } from '$utils/messaging'; import { useRatesStore } from '$store/zustand/rates'; import { Cell } from '@ton/core'; import nacl from 'tweetnacl'; -import { encryptMessageComment } from '@tonkeeper/core'; +import { BASE_FORWARD_AMOUNT, encryptMessageComment } from '@tonkeeper/core'; import { goBack } from '$navigation/imperative'; import { trackEvent } from '$utils/stats'; import { tk } from '@tonkeeper/shared/tonkeeper'; @@ -448,10 +448,10 @@ function* confirmSendCoinsWorker(action: ConfirmSendCoinsAction) { yield delay(100); if (onNext) { - if (!isSendAll && onInsufficientFunds) { + if ((tokenType !== TokenType.TON || !isSendAll) && onInsufficientFunds) { const amountNano = tokenType === TokenType.Jetton - ? new BigNumber(toNano(fee)).plus(toNano('0.05')).toString() + ? new BigNumber(toNano(fee)).plus(BASE_FORWARD_AMOUNT.toString()).toString() : new BigNumber(toNano(fee)).plus(toNano(amount)).toString(); const address = yield call([wallet.ton, 'getAddress']); const { balance } = yield call(Tonapi.getWalletInfo, address); @@ -541,7 +541,7 @@ function* sendCoinsWorker(action: SendCoinsAction) { unlockedVault, commentValue, sendWithBattery, - BigNumber(toNano(fee)).plus(toNano('0.05')).toString(), + BigNumber(toNano(fee)).plus(BASE_FORWARD_AMOUNT.toString()).toString(), ); } else if (tokenType === TokenType.TON && currency === CryptoCurrencies.Ton) { yield call( diff --git a/packages/shared/modals/ActivityActionModal/RechargeByPromoModal.tsx b/packages/shared/modals/ActivityActionModal/RechargeByPromoModal.tsx index 3efca7d75..ca4946881 100644 --- a/packages/shared/modals/ActivityActionModal/RechargeByPromoModal.tsx +++ b/packages/shared/modals/ActivityActionModal/RechargeByPromoModal.tsx @@ -1,5 +1,14 @@ import { memo, useCallback, useState } from 'react'; -import { Button, Input, Modal, Spacer, Steezy, Toast, View } from '@tonkeeper/uikit'; +import { + Button, + Input, + isIOS, + Modal, + Spacer, + Steezy, + Toast, + View, +} from '@tonkeeper/uikit'; import { navigation, SheetActions, useNavigation } from '@tonkeeper/router'; import { t } from '@tonkeeper/shared/i18n'; import { tk } from '../../tonkeeper'; @@ -36,10 +45,10 @@ export const RechargeByPromoModal = memo(() => { withClearButton value={code} onChangeText={onChangeText} - placeholder={t('battery.promocode.placeholder')} />