From 3656e1cae8bb31f933de4001c89f39aabf229c56 Mon Sep 17 00:00:00 2001 From: Max Voloshinskii Date: Tue, 25 Jun 2024 13:53:23 +0300 Subject: [PATCH] release: 4.7.1 (#890) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(mobile): add signer & ledger support (#831) * feat(mobile): signer support * feat(mobile): ledger support (#835) * bump(mobile): 4.5.0 (#836) * fix(mobile): hidden amount background (#829) * fix(mobile): fix ledger on android (#837) * debug(mobile): add init error toast (#841) * fix(mobile): reduce storage size (#842) * feat(mobile): notcoin support (#843) * feat(mobile): notcoin transfer action * feat(mobile): burn notcoin vouchers * NotcoinVerifyModal & fixes * notcoin button in browser * Update BurnVouchersModal.tsx * fix ledger rename * fix(mobile): signer & ledger flags (#844) * feature(mobile): battery top-up (#833) * wip * fix ShowBalance button color * wip * feature(mobile): Battery top-up * minor logic fix * remove unused import * persist keyboard * fix(mobile): fix loadBatteryConfig * fix condition * fix keyboardAvoiding * fix(mobile): remove replaceModal call * Battery top-up improvements * Misc battery updates * add missing return * fix(mobile): Battery topup QA fixes * fix(mobile): Force relayer use for battery top-up (#845) * fix(mobile): customize ledger wallet name (#846) * fix(mobile): fix useShouldShowTokensButton (#847) * fix(mobile): Update flag for recharge module (#848) * fix(mobile): Update flag for recharge module * fix: flag for promo list item * fix(mobile): Topup QA fixes (#849) * fix(mobile): Topup QA fixes * fix merge bug * fix(mobile): release fixes (#850) * fix(mobile): show less charges when gift battery for friend (#851) * fix(mobile): show less charges when gift battery for friend * bump codeVersion * fix(mobile): fix burn_not_available toast (#852) * fix(mobile): fix TON position (#854) * fix(mobile): stop support tonkeeper domain in manifest * fix(mobile): Move TON to bottom * fix(mobile): 4.5.0 qa fixes (#853) * fix(mobile): burn voucher tx amount (#855) * fix(mobile): Fix recharge asset logic (#856) remove unnecessary logic * feature(mobile): WELCOME TO FAMILY 🦫 (#857) * fix(mobile): add signer_about_url to config (#858) * fix(mobile): fix crash (#859) * fix(mobile): fix crash * cleanup * fix(mobile): Remove useAnimatedKeyboard hook (#860) * fix(mobile): Fix some texts (#861) * fix(mobile): Fix some texts * fix reserved amount * fix(mobile): Remove subtitle * fix (#862) * bump 4.5.1 (#863) * fix(mobile): Join TG step (#864) * fix(mobile): Join TG step * fix amount * fix amount * fix(mobile): bunch of fixes for hotfix (#865) * fix(mobile): Adaptive wallet buttons (#868) * fix(mobile): NFT trust field support (#869) * fix(mobile): Small refactor createWalletInstance (#870) * fix(mobile): Refactor createWalletInstance * Some refactor * bump(mobile): 4.5.3 (#871) * fix(mobile): Invalidate unknown watch-only contracts properly (#872) * fix(mobile): Fix breaking change (#873) * fix(mobile): scroll on switch wallet (#875) * fix(mobile): diamonds accent color (#876) * feat(mobile): slider text animation (#867) * feat(mobile): bump firebase (#877) * feat(mobile): update ios icon (#878) * fix(mobile): tonconnect fixes (#880) * feature(mobile): Mark scam NFTs (#874) * wip * Add scam endpoint * fix endpoint * bump(mobile): 4.6.0 * Hide spam NFTs from history * feature(mobile): Battery refunds dApp (#879) * feature(mobile): Battery refunds dApp * bump(mobile): 4.6.0 * fix(mobile): disable Navbar * fix(mobile): refund && scam fixes (#883) * fix(mobile): Button colors * fix(mobile): Mark transactions with spam NFTs, update naming * fix(mobile): fix for NFTs with nullable address (#885) * bump(mobile): 4.6.1 * add testflight build details * fix test details * fix(mobile): Bunch of fixes for release * feat(mobile): wallet W5 (#2) * fix(mobile): Battery min amount * feature(mobile): Gasless transactions Bring back inscriptions support wip feature(mobile): Gasless payments feature(mobile): Gasless payments fix gasless payload fundReceiver -> excessesAddress fix excessesAccount fix type fix gasless fix forward amount get excessesAccount safely, fix NFT send fix gasless without balance fix(mobile): Gasless support from deeplink Small fixes * small fixes * fix(mobile): w5 fixes (#7) * fix(mobile): fix loadRate method (#8) * bump(mobile): 3.6.1 * fix(mobile): Add TK_RELAYER_FEE opcode * fix deeplinks and use recharge methods from cache * fix sendAll * bump to 4.6.1... * fix sendMode for w5 * fix(mobile): optimize recursive fee estimation * fix(mobile): memo * fix(mobile): fix estimate fee when transfer by deeplink * fix(mobile): Send fixes * fix(mobile): fix jetton swap params * fix fail * feature(mobile): Buy USDT * feature(mobile): Migrate from jUSDT * fix * remove console.log * fix(mobile): Validate dApp URL * fix(mobile): safe pending_transactions * bump(mobile): 4.6.2 * fix double toNano call * Bump(mobile): 4.6.2 * feature(mobile): support gasless BOLT * fix param * bump(mobile): 3.7.0 * bump(mobile): 4.7.0 * fix(mobile): design review fixes (#22) * fix(mobile): update font type * fix(mobile): Design review fixes * Add "TON" tag for tether * fix suggest for gasless * fix(mobile): report not scam too * fix(mobile): Top spacing on Android * feature(mobile): More locales (#23) * feature(mobile): More locales * Android support * fix(mobile): bump react-native-sse (#24) * fix(mobile): Last-Event-ID header (#25) * fix(mobile): fix exchange layout (#26) * fix(mobile): signer fixes (#28) * hotfix(mobile): 4.6.3 (#27) * fix(mobile): Optimize requests * bump(mobile): 4.6.3 * fix(mobile): Minor improvements (#29) * fix(mobile): fix multiple pending events (#30) * fix(mobile): fix multiple pending events * fix iteratee * fix(mobile): bunch of improvements (#31) * fix(mobile): Bring back ErrorBoundary (#33) * fix(mobile): Fix lockups (#32) * fix(mobile): Fix lockups * catch exceptions on emulation * fix(mobile): Fix ErrorScreen (#34) * fix(mobile): Fix condition (#35) * fix(mobile): fix logout text position (#36) * fix(mobile): release fixes (#37) * fix(mobile): report as scam only if needed * move backup status from prev wallet to w5 * fix gasless for signer * fix(mobile): fix condition * fix(mobile): bring back config load * fix(mobile): force ltr in native code * fix(mobile): fix android deeplinking handling * fix(mobile): fix tr locale * fix(mobile) show all button for sell (#38) * fix(mobile): signer fixes (#39) * fix(mobile): fix invalid encrypted comments (#40) * fix(mobile): fix invalid encrypted comments * feature(mobile): Refunds List.Item * fix(mobile): bring back isActiveAccount check (#41) * fix(mobile): Drop animation (#42) * fix(mobile): Drop subscriptions and fix staking * fix(mobile): Fix lockup send (#43) * bump(mobile): 4.7.1 --------- Co-authored-by: Andrey Sorokin --- package.json | 2 +- packages/@core-js/package.json | 6 +- .../src/BatteryAPI/BatteryGenerated.ts | 287 ++- packages/@core-js/src/SwapAPI/SwapAPI.ts | 8 + .../@core-js/src/SwapAPI/SwapGenerated.ts | 495 ++++ packages/@core-js/src/SwapAPI/index.ts | 2 + .../@core-js/src/TonAPI/TonAPIGenerated.ts | 88 +- packages/@core-js/src/formatters/Address.ts | 64 +- .../src/legacy/contracts/LockupContractV1.ts | 13 + .../legacy/contracts/WalletContractV4R1.ts | 19 + .../@core-js/src/service/contractService.ts | 68 +- .../src/service/transactionService.ts | 18 +- packages/@core-js/src/utils/State.ts | 22 +- packages/@core-js/src/utils/constants.ts | 10 + .../@core-js/src/utils/network/network.ts | 34 +- packages/mobile/android/app/build.gradle | 4 +- .../android/app/src/main/AndroidManifest.xml | 24 + .../java/com/ton_keeper/MainApplication.java | 8 +- .../app/src/main/res/xml/locales_config.xml | 3 + packages/mobile/android/build.gradle | 2 +- packages/mobile/ios/fastlane/Fastfile | 13 +- .../ios/ton_keeper.xcodeproj/project.pbxproj | 29 +- .../ios/ton_keeper/Application/AppDelegate.mm | 4 + .../AppIcon.appiconset/100.png | Bin 0 -> 2909 bytes .../AppIcon.appiconset/1024.png | Bin 0 -> 34525 bytes .../AppIcon.appiconset/114.png | Bin 0 -> 3258 bytes .../AppIcon.appiconset/120.png | Bin 0 -> 3414 bytes .../AppIcon.appiconset/144.png | Bin 0 -> 4226 bytes .../AppIcon.appiconset/152.png | Bin 0 -> 4413 bytes .../AppIcon.appiconset/167.png | Bin 0 -> 4864 bytes .../AppIcon.appiconset/180.png | Bin 0 -> 5266 bytes .../Images.xcassets/AppIcon.appiconset/20.png | Bin 0 -> 670 bytes .../Images.xcassets/AppIcon.appiconset/29.png | Bin 0 -> 992 bytes .../Images.xcassets/AppIcon.appiconset/40.png | Bin 0 -> 1358 bytes .../Images.xcassets/AppIcon.appiconset/50.png | Bin 0 -> 1655 bytes .../Images.xcassets/AppIcon.appiconset/57.png | Bin 0 -> 1919 bytes .../Images.xcassets/AppIcon.appiconset/58.png | Bin 0 -> 1881 bytes .../Images.xcassets/AppIcon.appiconset/60.png | Bin 0 -> 1879 bytes .../Images.xcassets/AppIcon.appiconset/72.png | Bin 0 -> 2184 bytes .../Images.xcassets/AppIcon.appiconset/76.png | Bin 0 -> 2307 bytes .../Images.xcassets/AppIcon.appiconset/80.png | Bin 0 -> 2424 bytes .../Images.xcassets/AppIcon.appiconset/87.png | Bin 0 -> 2591 bytes .../AppIcon.appiconset/Contents.json | 159 +- .../AppIcon.appiconset/icon-1024@1x.png | Bin 66093 -> 0 bytes .../AppIcon.appiconset/icon-20@1x.png | Bin 609 -> 0 bytes .../AppIcon.appiconset/icon-20@2x.png | Bin 1211 -> 0 bytes .../AppIcon.appiconset/icon-29@1x.png | Bin 876 -> 0 bytes .../AppIcon.appiconset/icon-29@2x.png | Bin 1815 -> 0 bytes .../AppIcon.appiconset/icon-40@1x.png | Bin 1211 -> 0 bytes .../AppIcon.appiconset/icon-40@2x.png | Bin 2282 -> 0 bytes .../AppIcon.appiconset/icon-50@1x.png | Bin 1520 -> 0 bytes .../AppIcon.appiconset/icon-50@2x.png | Bin 2967 -> 0 bytes .../AppIcon.appiconset/icon-57@1x.png | Bin 1737 -> 0 bytes .../AppIcon.appiconset/icon-57@2x.png | Bin 3337 -> 0 bytes .../AppIcon.appiconset/icon-72@1x.png | Bin 2105 -> 0 bytes .../AppIcon.appiconset/icon-72@2x.png | Bin 4327 -> 0 bytes .../AppIcon.appiconset/icon-76@1x.png | Bin 2177 -> 0 bytes .../AppIcon.appiconset/icon-76@2x.png | Bin 4506 -> 0 bytes .../AppIcon.appiconset/icon-83.5@2x.png | Bin 4939 -> 0 bytes .../AppIcon.appiconset/icon-app@2x.png | Bin 10050 -> 0 bytes .../AppIcon.appiconset/icon-app@3x.png | Bin 19781 -> 0 bytes .../icon-notification@2x.png | Bin 1947 -> 0 bytes .../icon-notification@3x.png | Bin 3436 -> 0 bytes .../AppIcon.appiconset/icon-settings@2x.png | Bin 3265 -> 0 bytes .../AppIcon.appiconset/icon-settings@3x.png | Bin 6005 -> 0 bytes .../AppIcon.appiconset/icon-spotlight@2x.png | Bin 5281 -> 0 bytes .../AppIcon.appiconset/icon-spotlight@3x.png | Bin 10050 -> 0 bytes .../Localizable/es.lproj/PassCode.strings | 5 + .../Localizable/es.lproj/PassCode.stringsdict | 22 + .../Localizable/uk.lproj/PassCode.strings | 5 + .../Localizable/uk.lproj/PassCode.stringsdict | 22 + .../Localizable/uz.lproj/PassCode.strings | 5 + .../Localizable/uz.lproj/PassCode.stringsdict | 22 + .../ios/ton_keeper/SupportingFiles/Info.plist | 2 + packages/mobile/package.json | 23 +- packages/mobile/src/blockchain/legacy.ts | 2 +- packages/mobile/src/blockchain/types.ts | 12 - packages/mobile/src/blockchain/vault.ts | 83 +- packages/mobile/src/blockchain/wallet.ts | 346 ++- .../LedgerConnectionSteps.tsx | 144 ++ .../LedgerConnectionSteps/LedgerView.tsx | 164 ++ .../components/LedgerConnectionSteps/index.ts | 1 + .../components/LedgerConnectionSteps/types.ts | 5 + packages/mobile/src/components/index.ts | 1 + packages/mobile/src/config/AppConfig.ts | 2 +- packages/mobile/src/config/index.ts | 72 +- .../AccessConfirmation/AccessConfirmation.tsx | 7 +- .../src/core/BatterySend/BatterySend.tsx | 536 +++++ .../components/BatteryPackItem.tsx | 97 + .../src/core/BatterySend/components/index.ts | 1 + .../BatterySend/hooks/useRechargeMethod.tsx | 102 + packages/mobile/src/core/BatterySend/index.ts | 1 + packages/mobile/src/core/BatterySend/types.ts | 6 + packages/mobile/src/core/BuyFiat/BuyFiat.tsx | 32 + .../src/core/Colectibles/Collectibles.tsx | 33 +- .../src/core/Colectibles/NFTCardItem.tsx | 49 +- .../core/CustomizeWallet/CustomizeWallet.tsx | 7 +- .../CustomizeWallet/EmojiPicker/emojis.json | 2 +- .../src/core/DAppBrowser/DAppBrowser.tsx | 55 +- .../BrowserNavBar/BrowserNavBar.tsx | 3 +- .../core/DAppBrowser/hooks/useDAppBridge.ts | 27 +- .../src/core/DAppsExplore/DAppsExplore.tsx | 68 +- .../NotcoinBotIcon/NotcoinBotIcon.tsx | 14 + .../NotcoinBotIcon/notcoin-bot@4x.png | Bin 0 -> 63640 bytes .../src/core/DAppsExplore/components/index.ts | 1 + .../src/core/DevMenu/DevConfigScreen.tsx | 11 + packages/mobile/src/core/DevMenu/DevMenu.tsx | 4 +- .../src/core/Exchange/Exchange.style.ts | 34 + .../ExchangeItem/ExchangeItem.style.ts | 59 +- .../Exchange/ExchangeItem/ExchangeItem.tsx | 36 +- .../src/core/HideableAmount/ShowBalance.tsx | 2 +- .../src/core/ImportWallet/ImportWallet.tsx | 2 +- packages/mobile/src/core/Jetton/Jetton.tsx | 375 ++- .../core/ManageTokens/hooks/useNftData.tsx | 58 +- .../ApproveToken/ApproveToken.tsx | 24 +- .../CreateSubscription/CreateSubscription.tsx | 1 - .../ModalContainer/LinkingDomainModal.tsx | 47 +- .../NFTOperations/Modals/SignRawModal.tsx | 130 +- .../NFTOperations/NFTOperationFooter.tsx | 9 +- .../NFTOperations/NFTOperations.ts | 320 --- .../NFTOperations/TxRequest.types.ts | 16 +- .../src/core/NFT/LinkingDomainButton.tsx | 4 +- packages/mobile/src/core/NFT/NFT.tsx | 280 ++- .../ProgrammableButtons.tsx | 24 +- .../mobile/src/core/NFT/RenewDomainButton.tsx | 16 +- packages/mobile/src/core/NFTSend/NFTSend.tsx | 291 ++- .../NFTSend/steps/ConfirmStep/ConfirmStep.tsx | 2 +- .../NotCoinVouchers/NotCoinVouchers.style.ts | 125 + .../core/NotCoinVouchers/NotCoinVouchers.tsx | 223 ++ .../mobile/src/core/ScanQR/ScanQR.style.ts | 6 +- packages/mobile/src/core/ScanQR/ScanQR.tsx | 2 +- .../mobile/src/core/ScanQR/ScannerMask.tsx | 34 + packages/mobile/src/core/Send/Send.tsx | 50 +- .../core/Send/hooks/useSuggestedAddresses.ts | 8 + packages/mobile/src/core/Send/new/Send.tsx | 276 +++ .../new/core/components/GaslessToggle.tsx | 46 + .../new/core/transactionBuilder/common.ts | 4 + .../core/transactionBuilder/inscription.ts | 73 + .../new/core/transactionBuilder/jetton.ts | 370 +++ .../Send/new/core/transactionBuilder/ton.ts | 188 ++ .../src/core/Send/new/core/useSendCore.ts | 369 +++ .../core/Send/new/hooks/useCurrencyToSend.tsx | 107 + .../Send/new/hooks/useSendInputHandlers.ts | 96 + .../core/Send/new/hooks/useSendNavigation.ts | 39 + .../Send/new/hooks/useTokenTypeFeatures.ts | 24 + .../AddressStep/AddressStep.interface.ts | 11 +- .../Send/steps/AddressStep/AddressStep.tsx | 23 +- .../components/CommentInput/CommentInput.tsx | 16 +- .../ConfirmStep/ConfirmStep.interface.ts | 9 +- .../Send/steps/ConfirmStep/ConfirmStep.tsx | 41 +- .../mobile/src/core/Settings/Settings.tsx | 161 +- .../src/core/StakingSend/StakingSend.tsx | 58 +- .../src/core/TonConnect/TonConnect.style.ts | 8 +- .../src/core/TonConnect/TonConnectModal.tsx | 230 +- packages/mobile/src/core/TonConnect/models.ts | 29 +- packages/mobile/src/hooks/useApprovedNfts.ts | 10 + .../mobile/src/hooks/useCurrencyToSend.tsx | 2 + .../mobile/src/hooks/useDiamondsChecker.ts | 13 +- packages/mobile/src/hooks/useMigration.ts | 2 +- .../src/hooks/useShouldShowTokensButton.ts | 15 +- packages/mobile/src/ledger/index.ts | 4 + .../src/ledger/useBluetoothAvailable.ts | 53 + .../mobile/src/ledger/useConnectLedger.ts | 103 + .../mobile/src/ledger/useLedgerAccounts.ts | 37 + packages/mobile/src/ledger/usePairLedger.ts | 43 + .../mobile/src/modals/BurnVouchersModal.tsx | 232 ++ packages/mobile/src/modals/ExchangeModal.tsx | 103 +- .../mobile/src/modals/LedgerConfirmModal.tsx | 119 + .../mobile/src/modals/LogoutWarningModal.tsx | 29 +- .../mobile/src/modals/PairLedgerModal.tsx | 121 + packages/mobile/src/modals/index.ts | 3 + .../navigation/AppStack/AppStack.interface.ts | 9 + .../ImportWalletStack/ImportWalletStack.tsx | 10 +- .../src/navigation/ImportWalletStack/types.ts | 10 +- .../MainStack/MainStack.interface.ts | 1 + .../src/navigation/MainStack/MainStack.tsx | 23 +- .../MainStack/TabStack/TabStack.tsx | 4 +- packages/mobile/src/navigation/ModalStack.tsx | 27 +- packages/mobile/src/navigation/Providers.tsx | 4 +- packages/mobile/src/navigation/helper.ts | 15 +- .../hooks/useDeeplinkingResolvers.ts | 327 ++- .../mobile/src/navigation/navigationNames.ts | 3 + .../ChooseLedgerWallets.tsx | 127 + .../src/screens/ChooseLedgerWallets/index.ts | 1 + .../screens/ChooseWallets/ChooseWallets.tsx | 64 +- .../PairSignerScreen/PairSignerScreen.tsx | 263 +++ .../src/screens/PairSignerScreen/index.ts | 1 + .../SignerConfirmScreen/QrCodeView.tsx | 46 + .../SignerConfirmScreen.tsx | 198 ++ .../src/screens/SignerConfirmScreen/index.ts | 1 + .../src/screens/StartScreen/StartScreen.tsx | 14 +- .../W5StoriesScreen/W5StoriesScreen.tsx | 62 + .../W5StoriesScreen/images/beta@4x.png | Bin 0 -> 672404 bytes .../W5StoriesScreen/images/fees@4x.png | Bin 0 -> 158630 bytes .../W5StoriesScreen/images/messages@4x.png | Bin 0 -> 463282 bytes .../W5StoriesScreen/images/phrase@4x.png | Bin 0 -> 154382 bytes .../W5StoriesScreen/images/usdt@4x.png | Bin 0 -> 270420 bytes .../src/screens/W5StoriesScreen/index.ts | 1 + packages/mobile/src/screens/index.ts | 4 + .../AmountInput/AmountInput.style.ts | 3 + .../components/AmountInput/AmountInput.tsx | 117 +- .../CellSection/CellSection.interface.ts | 1 + .../components/CellSection/CellSection.tsx | 5 +- .../src/shared/components/ErrorBoundary.tsx | 15 +- .../mobile/src/shared/constants/config.ts | 88 - .../src/shared/constants/serverConfig.ts | 2 - .../mobile/src/shared/screens/ErrorScreen.tsx | 31 +- packages/mobile/src/store/models.ts | 4 +- .../mobile/src/store/subscriptions/sagas.ts | 5 - packages/mobile/src/store/wallet/interface.ts | 2 + packages/mobile/src/store/wallet/sagas.ts | 30 +- .../store/zustand/connectedApps/selectors.ts | 6 +- .../store/zustand/devFeaturesToggle/types.ts | 1 + .../devFeaturesToggle/useDevFeaturesToggle.ts | 3 +- .../src/store/zustand/methodsToBuy/types.ts | 1 + .../mobile/src/tabs/Wallet/WalletScreen.tsx | 33 +- .../Wallet/components/FinishSetupList.tsx | 55 +- .../tabs/Wallet/components/ListItemRate.tsx | 13 +- .../WalletActionButtons.tsx | 27 +- .../Wallet/components/WalletContentList.tsx | 11 +- .../\320\241onfirmRenewAllDomains.tsx" | 84 +- .../content-providers/dependencies/jettons.ts | 6 +- .../dependencies/notcoinVouchers.ts | 22 + .../dependencies/tonPrice.ts | 8 +- .../dependencies/utils/trendByDiff.ts | 11 + .../Wallet/content-providers/providers.ts | 1 + .../tabs/Wallet/content-providers/tokens.ts | 11 +- .../src/tabs/Wallet/content-providers/ton.ts | 9 +- .../content-providers/utils/receiver.ts | 5 + .../Wallet/content-providers/utils/types.ts | 8 +- .../tabs/Wallet/content-providers/vouchers.ts | 70 + .../src/tonconnect/ConnectReplyBuilder.ts | 10 +- packages/mobile/src/tonconnect/TonConnect.ts | 75 +- .../src/tonconnect/TonConnectRemoteBridge.ts | 10 +- packages/mobile/src/uikit/NavBar/NavBar.tsx | 14 +- .../uikit/PopupMenu/PopupMenu.interface.ts | 1 + .../src/uikit/PopupMenu/PopupMenu.style.ts | 4 +- .../mobile/src/uikit/PopupMenu/PopupMenu.tsx | 19 +- .../src/uikit/PopupSelect/PopupSelect.tsx | 13 + packages/mobile/src/uikit/Tag/Tag.tsx | 13 +- .../mobile/src/utils/bluetoothPermissions.ts | 130 + packages/mobile/src/utils/currency.ts | 12 +- packages/mobile/src/utils/ledger.ts | 11 + .../mobile/src/utils/mapNewNftToOldNftData.ts | 2 +- packages/mobile/src/utils/notcoin.ts | 49 + .../src/wallet/Activity/ActivityList.ts | 40 +- .../src/wallet/Activity/ActivityLoader.ts | 9 +- packages/mobile/src/wallet/Tonkeeper.ts | 277 ++- packages/mobile/src/wallet/Wallet/Wallet.ts | 18 +- .../mobile/src/wallet/Wallet/WalletBase.ts | 70 +- .../mobile/src/wallet/Wallet/WalletContent.ts | 43 +- packages/mobile/src/wallet/WalletTypes.ts | 54 + packages/mobile/src/wallet/constants.ts | 22 + .../src/wallet/managers/BalancesManager.ts | 64 +- .../src/wallet/managers/BatteryManager.ts | 132 +- .../src/wallet/managers/JettonsManager.ts | 58 +- .../src/wallet/managers/NftsManager.tsx | 16 + .../src/wallet/managers/SignerManager.ts | 217 ++ .../src/wallet/managers/StakingManager.ts | 9 +- .../wallet/managers/SubscriptionsManager.ts | 12 +- .../wallet/managers/TokenApprovalManager.ts | 1 + .../src/wallet/managers/TonProofManager.ts | 8 +- .../JettonBalanceModel/JettonBalanceModel.ts | 5 +- packages/mobile/src/wallet/utils.ts | 15 + .../router/src/createModalStackNavigator.tsx | 8 +- packages/router/src/withModalStack.tsx | 9 +- .../ActivityList/ActionListItem.tsx | 13 +- .../ActivityList/ActionListItemByType.tsx | 26 +- .../ActivityList/ActionListItemWithNFT.tsx | 71 + .../components/ActivityList/ActivityList.tsx | 12 +- .../ActivityList/NftPreviewContent.tsx | 125 +- .../ActivityList/PureActionListItem.tsx | 2 +- .../items/JettonSwapActionListItem.tsx | 27 +- .../BatterySupportedTransactions.tsx | 89 +- .../EncryptedComment/EncryptedComment.tsx | 11 +- .../RefillBattery/RechargeByPromoButton.tsx | 16 - .../RefillBattery/RechargeMethods.tsx | 134 ++ .../RefillBattery/RefillBattery.tsx | 21 +- .../RefillBattery/RefillBatteryRefunds.tsx | 42 + .../RefillBatterySettingsWidget.tsx | 2 +- .../RefillBattery/RestorePurchases.tsx | 2 +- .../WalletListItem/WalletListItem.tsx | 5 +- packages/shared/hooks/useDangerLevel.ts | 2 +- packages/shared/i18n/i18n.ts | 4 - .../locales/tonkeeper/__snapshots__/es.json | 1424 +++++++++++ .../locales/tonkeeper/__snapshots__/id.json | 411 +++- .../tonkeeper/__snapshots__/tr-TR.json | 1424 +++++++++++ .../locales/tonkeeper/__snapshots__/uk.json | 1424 +++++++++++ .../locales/tonkeeper/__snapshots__/uz.json | 1424 +++++++++++ .../tonkeeper/__snapshots__/zh-Hans-CN.json | 1424 +++++++++++ .../shared/i18n/locales/tonkeeper/en.json | 220 +- .../shared/i18n/locales/tonkeeper/es.json | 1536 ++++++++++++ .../shared/i18n/locales/tonkeeper/id.json | 1200 ++++++---- .../shared/i18n/locales/tonkeeper/it.json | 3 +- .../shared/i18n/locales/tonkeeper/ru-RU.json | 2087 +++++++++-------- .../shared/i18n/locales/tonkeeper/tr-TR.json | 1400 +++++++---- .../i18n/locales/tonkeeper/translate.js | 10 +- .../shared/i18n/locales/tonkeeper/uk.json | 1542 ++++++++++++ .../shared/i18n/locales/tonkeeper/uz.json | 1536 ++++++++++++ .../i18n/locales/tonkeeper/zh-Hans-CN.json | 1396 +++++++---- packages/shared/i18n/translations.ts | 8 +- .../ActionModalContent.tsx | 32 +- .../ActivityActionModal.tsx | 32 +- .../components/NftItemPayload.tsx | 23 +- .../components/NotcoinConfetti.tsx | 26 + .../content/JettonSwapActionContent.tsx | 9 +- .../content/JettonTransferActionContent.tsx | 4 +- .../content/NftItemTransferActionContent.tsx | 4 +- .../content/NotcoinTransferActionContent.tsx | 86 + .../content/TonTransferActionContent.tsx | 4 +- packages/shared/modals/AddWalletModal.tsx | 209 +- packages/shared/modals/RefillBatteryModal.tsx | 1 + .../modals/SelectRechargeMethodModal.tsx | 98 + .../modals/SuspiciousNFTDetailsModal.tsx | 83 + packages/shared/modals/SwitchWalletModal.tsx | 92 +- packages/shared/query/hooks/index.ts | 3 +- .../query/hooks/useBatteryRechargeMethods.tsx | 14 + packages/shared/utils/battery.ts | 2 +- packages/shared/utils/blockchain.ts | 39 +- packages/shared/utils/date.ts | 4 +- packages/shared/utils/getNewsUrl.ts | 15 + packages/uikit/assets/battery/gift.png | Bin 0 -> 6994 bytes packages/uikit/assets/battery/promo.png | Bin 0 -> 6041 bytes packages/uikit/assets/battery/recharge.png | Bin 0 -> 6697 bytes packages/uikit/assets/battery/refunds.png | Bin 0 -> 5354 bytes packages/uikit/assets/cryptoAssets/NOT.png | Bin 0 -> 1759 bytes packages/uikit/assets/cryptoAssets/USDT.png | Bin 1290 -> 4921 bytes packages/uikit/assets/cryptoAssets/index.ts | 3 + .../icons/png/ic-battery-flash-44@4x.png | Bin 0 -> 8509 bytes .../uikit/assets/icons/png/ic-block-16@4x.png | Bin 0 -> 1367 bytes .../icons/png/ic-donemark-outline-28@4x.png | Bin 0 -> 488 bytes .../uikit/assets/icons/png/ic-dot-16@4x.png | Bin 0 -> 658 bytes .../assets/icons/png/ic-eye-disable-16@4x.png | Bin 0 -> 1490 bytes .../uikit/assets/icons/png/ic-flash-16@4x.png | Bin 0 -> 938 bytes .../assets/icons/png/ic-flash-large-16@4x.png | Bin 0 -> 1553 bytes .../icons/png/ic-globe-outline-28@4x.png | Bin 0 -> 4445 bytes .../png/ic-import-wallet-outline-28@4x.png | Bin 0 -> 2381 bytes .../assets/icons/png/ic-ledger-28@4x.png | Bin 0 -> 394 bytes .../png/ic-magnifying-glass-outline-28@4x.png | Bin 0 -> 3010 bytes .../uikit/assets/icons/png/ic-minus-16@4x.png | Bin 0 -> 213 bytes .../uikit/assets/icons/png/ic-plus-16@4x.png | Bin 0 -> 358 bytes .../assets/icons/png/ic-question-28@4x.png | Bin 0 -> 2427 bytes .../assets/icons/png/ic-signer-28@4x.png | Bin 0 -> 4350 bytes .../icons/png/ic-testnet-outline-28@4x.png | Bin 0 -> 3853 bytes .../uikit/assets/icons/svg/16/ic-block-16.svg | 3 + .../uikit/assets/icons/svg/16/ic-dot-16.svg | 3 + .../assets/icons/svg/16/ic-eye-disable-16.svg | 4 + .../uikit/assets/icons/svg/16/ic-flash-16.svg | 3 + .../assets/icons/svg/16/ic-flash-large-16.svg | 3 + .../uikit/assets/icons/svg/16/ic-minus-16.svg | 3 + .../uikit/assets/icons/svg/16/ic-plus-16.svg | 3 + .../icons/svg/28/ic-donemark-outline-28.svg | 3 + .../icons/svg/28/ic-globe-outline-28.svg | 3 + .../svg/28/ic-import-wallet-outline-28.svg | 3 + .../assets/icons/svg/28/ic-ledger-28.svg | 3 + .../svg/28/ic-magnifying-glass-outline-28.svg | 3 + .../assets/icons/svg/28/ic-question-28.svg | 4 + .../assets/icons/svg/28/ic-signer-28.svg | 3 + .../icons/svg/28/ic-testnet-outline-28.svg | 3 + .../icons/svg/44/ic-battery-flash-44.svg | 4 + packages/uikit/package.json | 2 +- .../components/ActionButton/ActionButton.tsx | 6 +- .../components/ActionButton/Separators.tsx | 15 +- .../src/components/AnimatedBatteryIcon.tsx | 2 +- packages/uikit/src/components/Button.tsx | 102 +- packages/uikit/src/components/Flash/Flash.tsx | 5 +- .../uikit/src/components/HeaderSwitch.tsx | 45 + packages/uikit/src/components/Icon/Icon.tsx | 1 + .../uikit/src/components/Icon/Icon.types.ts | 48 + .../src/components/Icon/IconList.native.ts | 16 + .../uikit/src/components/KeyboardSpacer.tsx | 2 +- .../uikit/src/components/List/ListItem.tsx | 2 +- .../uikit/src/components/Lottie/Lottie.tsx | 6 +- packages/uikit/src/components/Radio.tsx | 73 + .../components/SlideButton/SlideButton.tsx | 107 +- .../components/SlideButton/gradient@4x.png | Bin 0 -> 99622 bytes packages/uikit/src/components/Spacer.tsx | 4 +- .../components/StoriesView/StoriesView.tsx | 424 ++++ .../uikit/src/components/StoriesView/index.ts | 1 + packages/uikit/src/components/Text/Text.tsx | 4 +- .../uikit/src/components/TouchableOpacity.tsx | 2 +- .../Modal/ScreenModal/ScreenModalHeader.tsx | 14 +- .../ScreenModal/ScreenModalScrollView.tsx | 8 +- .../Modal/SheetModal/SheetModal.tsx | 25 +- .../src/containers/Screen/ScreenFlashList.tsx | 4 +- .../src/containers/Screen/ScreenHeader.tsx | 31 +- .../containers/Screen/ScreenScrollView.tsx | 16 +- packages/uikit/src/index.ts | 3 + packages/uikit/src/styles/themes/blue.ts | 9 + packages/uikit/src/styles/themes/dark.ts | 9 + packages/uikit/src/styles/themes/light.ts | 9 + patches/react-native-ble-plx+2.0.3.patch | 21 + patches/react-native-sse+1.2.1.patch | 25 + yarn.lock | 188 +- 394 files changed, 28032 insertions(+), 5078 deletions(-) create mode 100644 packages/@core-js/src/SwapAPI/SwapAPI.ts create mode 100644 packages/@core-js/src/SwapAPI/SwapGenerated.ts create mode 100644 packages/@core-js/src/SwapAPI/index.ts create mode 100644 packages/mobile/ios/ton_keeper/Resources/Images.xcassets/AppIcon.appiconset/100.png create mode 100644 packages/mobile/ios/ton_keeper/Resources/Images.xcassets/AppIcon.appiconset/1024.png create mode 100644 packages/mobile/ios/ton_keeper/Resources/Images.xcassets/AppIcon.appiconset/114.png create mode 100644 packages/mobile/ios/ton_keeper/Resources/Images.xcassets/AppIcon.appiconset/120.png create mode 100644 packages/mobile/ios/ton_keeper/Resources/Images.xcassets/AppIcon.appiconset/144.png create mode 100644 packages/mobile/ios/ton_keeper/Resources/Images.xcassets/AppIcon.appiconset/152.png create mode 100644 packages/mobile/ios/ton_keeper/Resources/Images.xcassets/AppIcon.appiconset/167.png create mode 100644 packages/mobile/ios/ton_keeper/Resources/Images.xcassets/AppIcon.appiconset/180.png create mode 100644 packages/mobile/ios/ton_keeper/Resources/Images.xcassets/AppIcon.appiconset/20.png create mode 100644 packages/mobile/ios/ton_keeper/Resources/Images.xcassets/AppIcon.appiconset/29.png create mode 100644 packages/mobile/ios/ton_keeper/Resources/Images.xcassets/AppIcon.appiconset/40.png create mode 100644 packages/mobile/ios/ton_keeper/Resources/Images.xcassets/AppIcon.appiconset/50.png create mode 100644 packages/mobile/ios/ton_keeper/Resources/Images.xcassets/AppIcon.appiconset/57.png create mode 100644 packages/mobile/ios/ton_keeper/Resources/Images.xcassets/AppIcon.appiconset/58.png create mode 100644 packages/mobile/ios/ton_keeper/Resources/Images.xcassets/AppIcon.appiconset/60.png create mode 100644 packages/mobile/ios/ton_keeper/Resources/Images.xcassets/AppIcon.appiconset/72.png create mode 100644 packages/mobile/ios/ton_keeper/Resources/Images.xcassets/AppIcon.appiconset/76.png create mode 100644 packages/mobile/ios/ton_keeper/Resources/Images.xcassets/AppIcon.appiconset/80.png create mode 100644 packages/mobile/ios/ton_keeper/Resources/Images.xcassets/AppIcon.appiconset/87.png delete mode 100644 packages/mobile/ios/ton_keeper/Resources/Images.xcassets/AppIcon.appiconset/icon-1024@1x.png delete mode 100644 packages/mobile/ios/ton_keeper/Resources/Images.xcassets/AppIcon.appiconset/icon-20@1x.png delete mode 100644 packages/mobile/ios/ton_keeper/Resources/Images.xcassets/AppIcon.appiconset/icon-20@2x.png delete mode 100644 packages/mobile/ios/ton_keeper/Resources/Images.xcassets/AppIcon.appiconset/icon-29@1x.png delete mode 100644 packages/mobile/ios/ton_keeper/Resources/Images.xcassets/AppIcon.appiconset/icon-29@2x.png delete mode 100644 packages/mobile/ios/ton_keeper/Resources/Images.xcassets/AppIcon.appiconset/icon-40@1x.png delete mode 100644 packages/mobile/ios/ton_keeper/Resources/Images.xcassets/AppIcon.appiconset/icon-40@2x.png delete mode 100644 packages/mobile/ios/ton_keeper/Resources/Images.xcassets/AppIcon.appiconset/icon-50@1x.png delete mode 100644 packages/mobile/ios/ton_keeper/Resources/Images.xcassets/AppIcon.appiconset/icon-50@2x.png delete mode 100644 packages/mobile/ios/ton_keeper/Resources/Images.xcassets/AppIcon.appiconset/icon-57@1x.png delete mode 100644 packages/mobile/ios/ton_keeper/Resources/Images.xcassets/AppIcon.appiconset/icon-57@2x.png delete mode 100644 packages/mobile/ios/ton_keeper/Resources/Images.xcassets/AppIcon.appiconset/icon-72@1x.png delete mode 100644 packages/mobile/ios/ton_keeper/Resources/Images.xcassets/AppIcon.appiconset/icon-72@2x.png delete mode 100644 packages/mobile/ios/ton_keeper/Resources/Images.xcassets/AppIcon.appiconset/icon-76@1x.png delete mode 100644 packages/mobile/ios/ton_keeper/Resources/Images.xcassets/AppIcon.appiconset/icon-76@2x.png delete mode 100644 packages/mobile/ios/ton_keeper/Resources/Images.xcassets/AppIcon.appiconset/icon-83.5@2x.png delete mode 100644 packages/mobile/ios/ton_keeper/Resources/Images.xcassets/AppIcon.appiconset/icon-app@2x.png delete mode 100644 packages/mobile/ios/ton_keeper/Resources/Images.xcassets/AppIcon.appiconset/icon-app@3x.png delete mode 100644 packages/mobile/ios/ton_keeper/Resources/Images.xcassets/AppIcon.appiconset/icon-notification@2x.png delete mode 100644 packages/mobile/ios/ton_keeper/Resources/Images.xcassets/AppIcon.appiconset/icon-notification@3x.png delete mode 100644 packages/mobile/ios/ton_keeper/Resources/Images.xcassets/AppIcon.appiconset/icon-settings@2x.png delete mode 100644 packages/mobile/ios/ton_keeper/Resources/Images.xcassets/AppIcon.appiconset/icon-settings@3x.png delete mode 100644 packages/mobile/ios/ton_keeper/Resources/Images.xcassets/AppIcon.appiconset/icon-spotlight@2x.png delete mode 100644 packages/mobile/ios/ton_keeper/Resources/Images.xcassets/AppIcon.appiconset/icon-spotlight@3x.png create mode 100644 packages/mobile/ios/ton_keeper/Resources/Localizable/es.lproj/PassCode.strings create mode 100644 packages/mobile/ios/ton_keeper/Resources/Localizable/es.lproj/PassCode.stringsdict create mode 100644 packages/mobile/ios/ton_keeper/Resources/Localizable/uk.lproj/PassCode.strings create mode 100644 packages/mobile/ios/ton_keeper/Resources/Localizable/uk.lproj/PassCode.stringsdict create mode 100644 packages/mobile/ios/ton_keeper/Resources/Localizable/uz.lproj/PassCode.strings create mode 100644 packages/mobile/ios/ton_keeper/Resources/Localizable/uz.lproj/PassCode.stringsdict delete mode 100644 packages/mobile/src/blockchain/types.ts create mode 100644 packages/mobile/src/components/LedgerConnectionSteps/LedgerConnectionSteps.tsx create mode 100644 packages/mobile/src/components/LedgerConnectionSteps/LedgerView.tsx create mode 100644 packages/mobile/src/components/LedgerConnectionSteps/index.ts create mode 100644 packages/mobile/src/components/LedgerConnectionSteps/types.ts create mode 100644 packages/mobile/src/core/BatterySend/BatterySend.tsx create mode 100644 packages/mobile/src/core/BatterySend/components/BatteryPackItem.tsx create mode 100644 packages/mobile/src/core/BatterySend/components/index.ts create mode 100644 packages/mobile/src/core/BatterySend/hooks/useRechargeMethod.tsx create mode 100644 packages/mobile/src/core/BatterySend/index.ts create mode 100644 packages/mobile/src/core/BatterySend/types.ts create mode 100644 packages/mobile/src/core/DAppsExplore/components/NotcoinBotIcon/NotcoinBotIcon.tsx create mode 100644 packages/mobile/src/core/DAppsExplore/components/NotcoinBotIcon/notcoin-bot@4x.png delete mode 100644 packages/mobile/src/core/ModalContainer/NFTOperations/NFTOperations.ts create mode 100644 packages/mobile/src/core/NotCoinVouchers/NotCoinVouchers.style.ts create mode 100644 packages/mobile/src/core/NotCoinVouchers/NotCoinVouchers.tsx create mode 100644 packages/mobile/src/core/ScanQR/ScannerMask.tsx create mode 100644 packages/mobile/src/core/Send/new/Send.tsx create mode 100644 packages/mobile/src/core/Send/new/core/components/GaslessToggle.tsx create mode 100644 packages/mobile/src/core/Send/new/core/transactionBuilder/common.ts create mode 100644 packages/mobile/src/core/Send/new/core/transactionBuilder/inscription.ts create mode 100644 packages/mobile/src/core/Send/new/core/transactionBuilder/jetton.ts create mode 100644 packages/mobile/src/core/Send/new/core/transactionBuilder/ton.ts create mode 100644 packages/mobile/src/core/Send/new/core/useSendCore.ts create mode 100644 packages/mobile/src/core/Send/new/hooks/useCurrencyToSend.tsx create mode 100644 packages/mobile/src/core/Send/new/hooks/useSendInputHandlers.ts create mode 100644 packages/mobile/src/core/Send/new/hooks/useSendNavigation.ts create mode 100644 packages/mobile/src/core/Send/new/hooks/useTokenTypeFeatures.ts create mode 100644 packages/mobile/src/ledger/index.ts create mode 100644 packages/mobile/src/ledger/useBluetoothAvailable.ts create mode 100644 packages/mobile/src/ledger/useConnectLedger.ts create mode 100644 packages/mobile/src/ledger/useLedgerAccounts.ts create mode 100644 packages/mobile/src/ledger/usePairLedger.ts create mode 100644 packages/mobile/src/modals/BurnVouchersModal.tsx create mode 100644 packages/mobile/src/modals/LedgerConfirmModal.tsx create mode 100644 packages/mobile/src/modals/PairLedgerModal.tsx create mode 100644 packages/mobile/src/screens/ChooseLedgerWallets/ChooseLedgerWallets.tsx create mode 100644 packages/mobile/src/screens/ChooseLedgerWallets/index.ts create mode 100644 packages/mobile/src/screens/PairSignerScreen/PairSignerScreen.tsx create mode 100644 packages/mobile/src/screens/PairSignerScreen/index.ts create mode 100644 packages/mobile/src/screens/SignerConfirmScreen/QrCodeView.tsx create mode 100644 packages/mobile/src/screens/SignerConfirmScreen/SignerConfirmScreen.tsx create mode 100644 packages/mobile/src/screens/SignerConfirmScreen/index.ts create mode 100644 packages/mobile/src/screens/W5StoriesScreen/W5StoriesScreen.tsx create mode 100644 packages/mobile/src/screens/W5StoriesScreen/images/beta@4x.png create mode 100644 packages/mobile/src/screens/W5StoriesScreen/images/fees@4x.png create mode 100644 packages/mobile/src/screens/W5StoriesScreen/images/messages@4x.png create mode 100644 packages/mobile/src/screens/W5StoriesScreen/images/phrase@4x.png create mode 100644 packages/mobile/src/screens/W5StoriesScreen/images/usdt@4x.png create mode 100644 packages/mobile/src/screens/W5StoriesScreen/index.ts create mode 100644 packages/mobile/src/tabs/Wallet/content-providers/dependencies/notcoinVouchers.ts create mode 100644 packages/mobile/src/tabs/Wallet/content-providers/dependencies/utils/trendByDiff.ts create mode 100644 packages/mobile/src/tabs/Wallet/content-providers/vouchers.ts create mode 100644 packages/mobile/src/utils/bluetoothPermissions.ts create mode 100644 packages/mobile/src/utils/ledger.ts create mode 100644 packages/mobile/src/utils/notcoin.ts create mode 100644 packages/mobile/src/wallet/managers/SignerManager.ts create mode 100644 packages/shared/components/ActivityList/ActionListItemWithNFT.tsx delete mode 100644 packages/shared/components/RefillBattery/RechargeByPromoButton.tsx create mode 100644 packages/shared/components/RefillBattery/RechargeMethods.tsx create mode 100644 packages/shared/components/RefillBattery/RefillBatteryRefunds.tsx create mode 100644 packages/shared/i18n/locales/tonkeeper/__snapshots__/es.json create mode 100644 packages/shared/i18n/locales/tonkeeper/__snapshots__/tr-TR.json create mode 100644 packages/shared/i18n/locales/tonkeeper/__snapshots__/uk.json create mode 100644 packages/shared/i18n/locales/tonkeeper/__snapshots__/uz.json create mode 100644 packages/shared/i18n/locales/tonkeeper/__snapshots__/zh-Hans-CN.json create mode 100644 packages/shared/i18n/locales/tonkeeper/es.json create mode 100644 packages/shared/i18n/locales/tonkeeper/uk.json create mode 100644 packages/shared/i18n/locales/tonkeeper/uz.json create mode 100644 packages/shared/modals/ActivityActionModal/components/NotcoinConfetti.tsx create mode 100644 packages/shared/modals/ActivityActionModal/content/NotcoinTransferActionContent.tsx create mode 100644 packages/shared/modals/SelectRechargeMethodModal.tsx create mode 100644 packages/shared/modals/SuspiciousNFTDetailsModal.tsx create mode 100644 packages/shared/query/hooks/useBatteryRechargeMethods.tsx create mode 100644 packages/shared/utils/getNewsUrl.ts create mode 100644 packages/uikit/assets/battery/gift.png create mode 100644 packages/uikit/assets/battery/promo.png create mode 100644 packages/uikit/assets/battery/recharge.png create mode 100644 packages/uikit/assets/battery/refunds.png create mode 100644 packages/uikit/assets/cryptoAssets/NOT.png create mode 100644 packages/uikit/assets/icons/png/ic-battery-flash-44@4x.png create mode 100644 packages/uikit/assets/icons/png/ic-block-16@4x.png create mode 100644 packages/uikit/assets/icons/png/ic-donemark-outline-28@4x.png create mode 100644 packages/uikit/assets/icons/png/ic-dot-16@4x.png create mode 100644 packages/uikit/assets/icons/png/ic-eye-disable-16@4x.png create mode 100644 packages/uikit/assets/icons/png/ic-flash-16@4x.png create mode 100644 packages/uikit/assets/icons/png/ic-flash-large-16@4x.png create mode 100644 packages/uikit/assets/icons/png/ic-globe-outline-28@4x.png create mode 100644 packages/uikit/assets/icons/png/ic-import-wallet-outline-28@4x.png create mode 100644 packages/uikit/assets/icons/png/ic-ledger-28@4x.png create mode 100644 packages/uikit/assets/icons/png/ic-magnifying-glass-outline-28@4x.png create mode 100644 packages/uikit/assets/icons/png/ic-minus-16@4x.png create mode 100644 packages/uikit/assets/icons/png/ic-plus-16@4x.png create mode 100644 packages/uikit/assets/icons/png/ic-question-28@4x.png create mode 100644 packages/uikit/assets/icons/png/ic-signer-28@4x.png create mode 100644 packages/uikit/assets/icons/png/ic-testnet-outline-28@4x.png create mode 100644 packages/uikit/assets/icons/svg/16/ic-block-16.svg create mode 100644 packages/uikit/assets/icons/svg/16/ic-dot-16.svg create mode 100644 packages/uikit/assets/icons/svg/16/ic-eye-disable-16.svg create mode 100644 packages/uikit/assets/icons/svg/16/ic-flash-16.svg create mode 100644 packages/uikit/assets/icons/svg/16/ic-flash-large-16.svg create mode 100644 packages/uikit/assets/icons/svg/16/ic-minus-16.svg create mode 100644 packages/uikit/assets/icons/svg/16/ic-plus-16.svg create mode 100644 packages/uikit/assets/icons/svg/28/ic-donemark-outline-28.svg create mode 100644 packages/uikit/assets/icons/svg/28/ic-globe-outline-28.svg create mode 100644 packages/uikit/assets/icons/svg/28/ic-import-wallet-outline-28.svg create mode 100644 packages/uikit/assets/icons/svg/28/ic-ledger-28.svg create mode 100644 packages/uikit/assets/icons/svg/28/ic-magnifying-glass-outline-28.svg create mode 100644 packages/uikit/assets/icons/svg/28/ic-question-28.svg create mode 100644 packages/uikit/assets/icons/svg/28/ic-signer-28.svg create mode 100644 packages/uikit/assets/icons/svg/28/ic-testnet-outline-28.svg create mode 100644 packages/uikit/assets/icons/svg/44/ic-battery-flash-44.svg create mode 100644 packages/uikit/src/components/HeaderSwitch.tsx create mode 100644 packages/uikit/src/components/Radio.tsx create mode 100644 packages/uikit/src/components/SlideButton/gradient@4x.png create mode 100644 packages/uikit/src/components/StoriesView/StoriesView.tsx create mode 100644 packages/uikit/src/components/StoriesView/index.ts create mode 100644 patches/react-native-ble-plx+2.0.3.patch create mode 100644 patches/react-native-sse+1.2.1.patch diff --git a/package.json b/package.json index 738b52f27..f3fc2a17d 100755 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "pull_locales": "cd packages && cd shared && yarn pull_locales", "postinstall": "patch-package", "pods": "cd packages && cd mobile && yarn pods", - "icons": "cd packages && cd mobile && yarn icons", + "icons": "cd packages && cd uikit && yarn icons", "codegen": "cd packages/@core-js && yarn codegen", "start:mobile": "cd packages && cd mobile && yarn start", "start:web": "cd packages && cd web && yarn start", diff --git a/packages/@core-js/package.json b/packages/@core-js/package.json index d21b70ab8..1f9d49dd7 100644 --- a/packages/@core-js/package.json +++ b/packages/@core-js/package.json @@ -15,9 +15,9 @@ "@aws-crypto/sha256-js": "^3.0.0", "@ethersproject/shims": "^5.7.0", "@noble/ed25519": "1.7.3", - "@ton/core": "^0.53.0", - "@ton/crypto": "^3.2.0", - "@ton/ton": "^13.9.0", + "@ton/core": "0.54.0", + "@ton/crypto": "3.2.0", + "@ton/ton": "https://github.com/tonkeeper/tonkeeper-ton#build9", "aes-js": "3.1.2", "bignumber.js": "^9.1.1", "ethers": "^6.7.1", diff --git a/packages/@core-js/src/BatteryAPI/BatteryGenerated.ts b/packages/@core-js/src/BatteryAPI/BatteryGenerated.ts index 23d039035..0855fb84f 100644 --- a/packages/@core-js/src/BatteryAPI/BatteryGenerated.ts +++ b/packages/@core-js/src/BatteryAPI/BatteryGenerated.ts @@ -33,6 +33,10 @@ export interface Config { excess_account: string; } +export interface GaslessEstimation { + commission: string; +} + export interface Balance { /** @example "10.250" */ balance: string; @@ -45,23 +49,66 @@ export interface Balance { units: BalanceUnitsEnum; } +export interface RechargeMethods { + methods: { + /** @example "https://example.com/image.png" */ + image?: string; + jetton_master?: string; + /** @example "10.0" */ + min_bootstrap_value?: string; + type: RechargeMethodsTypeEnum; + /** @example "10.250" */ + rate: string; + /** @example "usdt" */ + symbol: string; + /** @example 6 */ + decimals: number; + /** @example true */ + support_gasless: boolean; + /** @example true */ + support_recharge: boolean; + }[]; +} + export interface Purchases { /** @example 1 */ total_purchases: number; - /** - * if set, then there are more purchases to be loaded. Use this value as offset parameter in the next request. - * @example 10 - */ - next_offset?: number; purchases: { /** @example 2 */ - id: number; + user_purchase_id: number; /** @example "android" */ type: PurchasesTypeEnum; - /** @example "10.250" */ - value: string; + /** + * Amount describes the amount paid by the user for this purchase when we know it. For crypto purchases it is always set. + * @example "10.250" + */ + amount?: string; + /** @example 1200 */ + charges: number; + /** + * Currency is set when we know it. For crypto purchases it is always set. + * @example "USDT" + */ + currency?: string; /** @example "2006-01-02T15:04:05Z07:00" */ datetime: string; + refund_information?: { + fully_refunded: boolean; + partially_refunded: boolean; + pending_refund: boolean; + refunded?: { + /** @example "10.250" */ + amount: string; + /** @example 1200 */ + charges: number; + }; + refundable?: { + /** @example "10.250" */ + amount: string; + /** @example 1200 */ + charges: number; + }; + }; }[]; } @@ -82,6 +129,8 @@ export interface AndroidBatteryPurchaseStatus { }[]; } +export type AppStoreResponse = object; + export interface IOSBatteryPurchaseStatus { transactions: { /** @example "1000000790000000" */ @@ -138,10 +187,18 @@ export enum BalanceUnitsEnum { Ton = 'ton', } +export enum RechargeMethodsTypeEnum { + Jetton = 'jetton', + Ton = 'ton', +} + /** @example "android" */ export enum PurchasesTypeEnum { - RegularPurchase = 'regular-purchase', + Android = 'android', + Ios = 'ios', PromoCode = 'promo-code', + Crypto = 'crypto', + Gift = 'gift', } /** @example "invalid-product-id" */ @@ -194,6 +251,11 @@ export enum GetBalanceParams1UnitsEnum { Ton = 'ton', } +export interface GetRechargeMethodsParams { + /** @default true */ + include_recharge_only?: boolean; +} + export interface GetPurchasesParams { /** * @max 1000 @@ -214,6 +276,19 @@ export interface GetTransactionsParams { offset?: number; } +export interface ConfirmLargeRefundParams { + token: string; +} + +export enum RemoveRefundStatusEnum { + Failed = 'failed', + Pending = 'pending', +} + +export interface RemoveRefundParams { + token: string; +} + export type QueryParamsType = Record; export type ResponseFormat = keyof Omit; @@ -524,6 +599,41 @@ export class BatteryGenerated { ...params, }); + /** + * @description This method returns on-chain recharge methods. + * + * @name GetRechargeMethods + * @request GET:/recharge-methods + */ + getRechargeMethods = (query: GetRechargeMethodsParams, params: RequestParams = {}) => + this.http.request({ + path: `/recharge-methods`, + method: 'GET', + query: query, + format: 'json', + ...params, + }); + + /** + * No description + * + * @name RequestRefund + * @request POST:/request-refund + */ + requestRefund = ( + data: { + /** @example 43 */ + user_purchase_id: number; + }, + params: RequestParams = {}, + ) => + this.http.request({ + path: `/request-refund`, + method: 'POST', + body: data, + ...params, + }); + /** * @description This method returns a list of purchases made by a specific user. * @@ -554,6 +664,73 @@ export class BatteryGenerated { ...params, }); + payload = { + /** + * @description Get a payload for further token receipt + * + * @tags Connect + * @name GetTonConnectPayload + * @request GET:/tonconnect/payload + */ + getTonConnectPayload: (params: RequestParams = {}) => + this.http.request< + { + /** @example "84jHVNLQmZsAAAAAZB0Zryi2wqVJI-KaKNXOvCijEi46YyYzkaSHyJrMPBMOkVZa" */ + payload: string; + }, + Error + >({ + path: `/tonconnect/payload`, + method: 'GET', + format: 'json', + ...params, + }), + }; + proof = { + /** + * @description Account verification and token issuance + * + * @tags Wallet + * @name TonConnectProof + * @request POST:/tonconnect/proof + */ + tonConnectProof: ( + data: { + /** @example "0:97146a46acc2654y27947f14c4a4b14273e954f78bc017790b41208b0043200b" */ + address: string; + proof: { + /** + * @format int64 + * @example "1678275313" + */ + timestamp: number; + domain: { + /** @format int32 */ + length_bytes?: number; + value: string; + }; + signature: string; + /** @example "84jHVNLQmZsAAAAAZB0Zryi2wqVJI-KaKNXOvCijEi46YyYzkaSHyJrMPBMOkVZa" */ + payload: string; + state_init?: string; + }; + }, + params: RequestParams = {}, + ) => + this.http.request< + { + /** @example "NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2ODQ3..." */ + token: string; + }, + Error + >({ + path: `/tonconnect/proof`, + method: 'POST', + body: data, + format: 'json', + ...params, + }), + }; emulate = { /** * @description Emulate sending message to blockchain @@ -604,6 +781,26 @@ export class BatteryGenerated { }), }; ios = { + /** + * No description + * + * @name AppStoreNotification + * @request POST:/purchase-battery/ios/app-store-notification + */ + appStoreNotification: ( + data: { + signedPayload: string; + }, + params: RequestParams = {}, + ) => + this.http.request({ + path: `/purchase-battery/ios/app-store-notification`, + method: 'POST', + body: data, + format: 'json', + ...params, + }), + /** * @description verify an in-app purchase * @@ -648,4 +845,76 @@ export class BatteryGenerated { ...params, }), }; + estimateCost = { + /** + * No description + * + * @name EstimateGaslessCost + * @request POST:/gasless/estimate-cost/{jetton_master} + */ + estimateGaslessCost: ( + jettonMaster: string, + data: { + /** @default true */ + battery?: boolean; + payload: string; + }, + params: RequestParams = {}, + ) => + this.http.request({ + path: `/gasless/estimate-cost/${jettonMaster}`, + method: 'POST', + body: data, + format: 'json', + ...params, + }), + }; + confirmLargeRefund = { + /** + * No description + * + * @name ConfirmLargeRefund + * @request POST:/restricted/confirm-large-refund + */ + confirmLargeRefund: ( + query: ConfirmLargeRefundParams, + data: { + purchase_id: number; + }, + params: RequestParams = {}, + ) => + this.http.request({ + path: `/restricted/confirm-large-refund`, + method: 'POST', + query: query, + body: data, + format: 'json', + ...params, + }), + }; + removeRefund = { + /** + * No description + * + * @name RemoveRefund + * @request POST:/restricted/remove-refund + */ + removeRefund: ( + query: RemoveRefundParams, + data: { + purchase_id: number; + refund_id: number; + status: RemoveRefundStatusEnum; + }, + params: RequestParams = {}, + ) => + this.http.request({ + path: `/restricted/remove-refund`, + method: 'POST', + query: query, + body: data, + format: 'json', + ...params, + }), + }; } diff --git a/packages/@core-js/src/SwapAPI/SwapAPI.ts b/packages/@core-js/src/SwapAPI/SwapAPI.ts new file mode 100644 index 000000000..e0c376b9f --- /dev/null +++ b/packages/@core-js/src/SwapAPI/SwapAPI.ts @@ -0,0 +1,8 @@ +import { HttpClient, HttpClientOptions } from '../TonAPI/HttpClient'; +import { SwapGenerated } from './SwapGenerated'; + +export class SwapAPI extends SwapGenerated { + constructor(opts: HttpClientOptions) { + super(new (HttpClient as any)(opts)); + } +} diff --git a/packages/@core-js/src/SwapAPI/SwapGenerated.ts b/packages/@core-js/src/SwapAPI/SwapGenerated.ts new file mode 100644 index 000000000..e6e8693b9 --- /dev/null +++ b/packages/@core-js/src/SwapAPI/SwapGenerated.ts @@ -0,0 +1,495 @@ +/* eslint-disable */ +/* tslint:disable */ +/* + * --------------------------------------------------------------- + * ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API ## + * ## ## + * ## AUTHOR: acacode ## + * ## SOURCE: https://github.com/acacode/swagger-typescript-api ## + * --------------------------------------------------------------- + */ + +export enum CalculateSwapProviderEnum { + Stonfi = 'stonfi', +} + +export enum CalculateSwapProviderEnum1 { + Dedust = 'dedust', +} + +export interface CalculateSwapParams { + /** @pattern ^(ton|(-1|0):[0-9a-fA-F]{64})$ */ + fromAsset: string; + /** @pattern ^(ton|(-1|0):[0-9a-fA-F]{64})$ */ + toAsset: string; + /** @pattern ^\d+$ */ + fromAmount: string; + /** @pattern ^(-1|0):[0-9a-fA-F]{64}$ */ + referral?: string; + provider: ProviderEnum | ProviderEnum1; +} + +export enum ProviderEnum { + Dedust = 'dedust', +} + +export enum ProviderEnum1 { + Stonfi = 'stonfi', +} + +export enum CalculateSwapParams1ProviderEnum { + Dedust = 'dedust', +} + +export enum CalculateSwapParams1ProviderEnum1 { + Stonfi = 'stonfi', +} + +export enum EncodeSwapProviderEnum { + Dedust = 'dedust', +} + +export enum EncodeSwapProviderEnum1 { + Stonfi = 'stonfi', +} + +export type QueryParamsType = Record; +export type ResponseFormat = keyof Omit; + +export interface FullRequestParams extends Omit { + /** set parameter to `true` for call `securityWorker` for this request */ + secure?: boolean; + /** request path */ + path: string; + /** content type of request body */ + type?: ContentType; + /** query params */ + query?: QueryParamsType; + /** format of response (i.e. response.json() -> format: "json") */ + format?: ResponseFormat; + /** request body */ + body?: unknown; + /** base url */ + baseUrl?: string; + /** request cancellation token */ + cancelToken?: CancelToken; +} + +export type RequestParams = Omit; + +export interface ApiConfig { + baseUrl?: string; + baseApiParams?: Omit; + securityWorker?: ( + securityData: SecurityDataType | null, + ) => Promise | RequestParams | void; + customFetch?: typeof fetch; +} + +export interface HttpResponse + extends Response { + data: D; + error: E; +} + +type CancelToken = Symbol | string | number; + +export enum ContentType { + Json = 'application/json', + FormData = 'multipart/form-data', + UrlEncoded = 'application/x-www-form-urlencoded', + Text = 'text/plain', +} + +export class HttpClient { + public baseUrl: string = ''; + private securityData: SecurityDataType | null = null; + private securityWorker?: ApiConfig['securityWorker']; + private abortControllers = new Map(); + private customFetch = (...fetchParams: Parameters) => + fetch(...fetchParams); + + private baseApiParams: RequestParams = { + credentials: 'same-origin', + headers: {}, + redirect: 'follow', + referrerPolicy: 'no-referrer', + }; + + constructor(apiConfig: ApiConfig = {}) { + Object.assign(this, apiConfig); + } + + public setSecurityData = (data: SecurityDataType | null) => { + this.securityData = data; + }; + + protected encodeQueryParam(key: string, value: any) { + const encodedKey = encodeURIComponent(key); + return `${encodedKey}=${encodeURIComponent( + typeof value === 'number' ? value : `${value}`, + )}`; + } + + protected addQueryParam(query: QueryParamsType, key: string) { + return this.encodeQueryParam(key, query[key]); + } + + protected addArrayQueryParam(query: QueryParamsType, key: string) { + const value = query[key]; + return value.map((v: any) => this.encodeQueryParam(key, v)).join('&'); + } + + protected toQueryString(rawQuery?: QueryParamsType): string { + const query = rawQuery || {}; + const keys = Object.keys(query).filter((key) => 'undefined' !== typeof query[key]); + return keys + .map((key) => + Array.isArray(query[key]) + ? this.addArrayQueryParam(query, key) + : this.addQueryParam(query, key), + ) + .join('&'); + } + + protected addQueryParams(rawQuery?: QueryParamsType): string { + const queryString = this.toQueryString(rawQuery); + return queryString ? `?${queryString}` : ''; + } + + private contentFormatters: Record any> = { + [ContentType.Json]: (input: any) => + input !== null && (typeof input === 'object' || typeof input === 'string') + ? JSON.stringify(input) + : input, + [ContentType.Text]: (input: any) => + input !== null && typeof input !== 'string' ? JSON.stringify(input) : input, + [ContentType.FormData]: (input: any) => + Object.keys(input || {}).reduce((formData, key) => { + const property = input[key]; + formData.append( + key, + property instanceof Blob + ? property + : typeof property === 'object' && property !== null + ? JSON.stringify(property) + : `${property}`, + ); + return formData; + }, new FormData()), + [ContentType.UrlEncoded]: (input: any) => this.toQueryString(input), + }; + + protected mergeRequestParams( + params1: RequestParams, + params2?: RequestParams, + ): RequestParams { + return { + ...this.baseApiParams, + ...params1, + ...(params2 || {}), + headers: { + ...(this.baseApiParams.headers || {}), + ...(params1.headers || {}), + ...((params2 && params2.headers) || {}), + }, + }; + } + + protected createAbortSignal = (cancelToken: CancelToken): AbortSignal | undefined => { + if (this.abortControllers.has(cancelToken)) { + const abortController = this.abortControllers.get(cancelToken); + if (abortController) { + return abortController.signal; + } + return void 0; + } + + const abortController = new AbortController(); + this.abortControllers.set(cancelToken, abortController); + return abortController.signal; + }; + + public abortRequest = (cancelToken: CancelToken) => { + const abortController = this.abortControllers.get(cancelToken); + + if (abortController) { + abortController.abort(); + this.abortControllers.delete(cancelToken); + } + }; + + public request = async ({ + body, + secure, + path, + type, + query, + format, + baseUrl, + cancelToken, + ...params + }: FullRequestParams): Promise => { + const secureParams = + ((typeof secure === 'boolean' ? secure : this.baseApiParams.secure) && + this.securityWorker && + (await this.securityWorker(this.securityData))) || + {}; + const requestParams = this.mergeRequestParams(params, secureParams); + const queryString = query && this.toQueryString(query); + const payloadFormatter = this.contentFormatters[type || ContentType.Json]; + const responseFormat = format || requestParams.format; + + return this.customFetch( + `${baseUrl || this.baseUrl || ''}${path}${queryString ? `?${queryString}` : ''}`, + { + ...requestParams, + headers: { + ...(requestParams.headers || {}), + ...(type && type !== ContentType.FormData ? { 'Content-Type': type } : {}), + }, + signal: + (cancelToken ? this.createAbortSignal(cancelToken) : requestParams.signal) || + null, + body: + typeof body === 'undefined' || body === null ? null : payloadFormatter(body), + }, + ).then(async (response) => { + const r = response as HttpResponse; + r.data = null as unknown as T; + r.error = null as unknown as E; + + const data = !responseFormat + ? r + : await response[responseFormat]() + .then((data) => { + if (r.ok) { + r.data = data; + } else { + r.error = data; + } + return r; + }) + .catch((e) => { + r.error = e; + return r; + }); + + if (cancelToken) { + this.abortControllers.delete(cancelToken); + } + + if (!response.ok) throw data; + return data.data; + }); + }; +} + +/** + * @title Tonkeeper DEX service + * @version 1.0.0 + * + * Tonkeeper DEX service backend + */ +export class SwapGenerated { + http: HttpClient; + + constructor(http: HttpClient) { + this.http = http; + } + + swap = { + /** + * No description + * + * @tags swap + * @name CalculateSwap + * @request GET:/v2/swap/calculate + */ + calculateSwap: (query: CalculateSwapParams, params: RequestParams = {}) => + this.http.request< + | { + provider: CalculateSwapProviderEnum; + trades: ({ + /** @pattern ^(ton|(-1|0):[0-9a-fA-F]{64})$ */ + fromAsset: string; + /** @pattern ^(ton|(-1|0):[0-9a-fA-F]{64})$ */ + toAsset: string; + /** @pattern ^\d+$ */ + fromAmount: string; + /** @pattern ^\d+$ */ + toAmount: string; + /** @pattern ^\d+$ */ + blockchainFee: string; + path: string[]; + } & { + /** Value that should be passed to stonfiTrade property in encode api method */ + stonfiRawTrade: { + /** @pattern ^(ton|(-1|0):[0-9a-fA-F]{64})$ */ + fromAsset: string; + /** @pattern ^(ton|(-1|0):[0-9a-fA-F]{64})$ */ + toAsset: string; + /** @pattern ^\d+$ */ + fromAmount: string; + /** @pattern ^\d+$ */ + toAmount: string; + }; + })[]; + } + | { + provider: CalculateSwapProviderEnum1; + trades: ({ + /** @pattern ^(ton|(-1|0):[0-9a-fA-F]{64})$ */ + fromAsset: string; + /** @pattern ^(ton|(-1|0):[0-9a-fA-F]{64})$ */ + toAsset: string; + /** @pattern ^\d+$ */ + fromAmount: string; + /** @pattern ^\d+$ */ + toAmount: string; + /** @pattern ^\d+$ */ + blockchainFee: string; + path: string[]; + } & { + /** Value that should be passed to dedustTrade property in encode api method */ + dedustRawTrade: { + /** @pattern ^(ton|(-1|0):[0-9a-fA-F]{64})$ */ + fromAsset: string; + /** @pattern ^(ton|(-1|0):[0-9a-fA-F]{64})$ */ + toAsset: string; + /** @pattern ^\d+$ */ + fromAmount: string; + /** @pattern ^\d+$ */ + toAmount: string; + /** @pattern ^(-1|0):[0-9a-fA-F]{64}$ */ + poolAddress: string; + }[]; + })[]; + }, + string + >({ + path: `/v2/swap/calculate`, + method: 'GET', + query: query, + format: 'json', + ...params, + }), + + /** + * No description + * + * @tags swap + * @name EncodeSwap + * @request POST:/v2/swap/encode + */ + encodeSwap: ( + data: { + swap: + | { + provider: EncodeSwapProviderEnum; + dedustTrade: { + /** @pattern ^(ton|(-1|0):[0-9a-fA-F]{64})$ */ + fromAsset: string; + /** @pattern ^(ton|(-1|0):[0-9a-fA-F]{64})$ */ + toAsset: string; + /** @pattern ^\d+$ */ + fromAmount: string; + /** @pattern ^\d+$ */ + toAmount: string; + /** @pattern ^(-1|0):[0-9a-fA-F]{64}$ */ + poolAddress: string; + }[]; + } + | { + provider: EncodeSwapProviderEnum1; + stonfiTrade: { + /** @pattern ^(ton|(-1|0):[0-9a-fA-F]{64})$ */ + fromAsset: string; + /** @pattern ^(ton|(-1|0):[0-9a-fA-F]{64})$ */ + toAsset: string; + /** @pattern ^\d+$ */ + fromAmount: string; + /** @pattern ^\d+$ */ + toAmount: string; + }; + }; + options: { + senderAddress: string; + referralAddress?: string; + /** @pattern ^(\d+\.)?\d+$ */ + slippage: string; + }; + }, + params: RequestParams = {}, + ) => + this.http.request< + { + value: string; + to: string; + body: string; + }, + string + >({ + path: `/v2/swap/encode`, + method: 'POST', + body: data, + type: ContentType.Json, + format: 'json', + ...params, + }), + + /** + * No description + * + * @tags swap + * @name SwapGas + * @request GET:/v2/swap/gas + */ + swapGas: (params: RequestParams = {}) => + this.http.request< + { + dedust: { + tonToJetton: string; + jettonToTon: string; + jettonToJetton: string; + }; + stonfi: { + tonToJetton: string; + jettonToTon: string; + jettonToJetton: string; + }; + }, + string + >({ + path: `/v2/swap/gas`, + method: 'GET', + format: 'json', + ...params, + }), + + /** + * No description + * + * @tags swap + * @name SwapAssets + * @request GET:/v2/swap/assets + */ + swapAssets: (params: RequestParams = {}) => + this.http.request< + { + symbol: string; + name: string; + decimals: number; + /** @pattern ^(ton|(-1|0):[0-9a-fA-F]{64})$ */ + address: string; + image: string; + }[], + string + >({ + path: `/v2/swap/assets`, + method: 'GET', + format: 'json', + ...params, + }), + }; +} diff --git a/packages/@core-js/src/SwapAPI/index.ts b/packages/@core-js/src/SwapAPI/index.ts new file mode 100644 index 000000000..b041a02bb --- /dev/null +++ b/packages/@core-js/src/SwapAPI/index.ts @@ -0,0 +1,2 @@ +export { SwapAPI } from './SwapAPI'; +export * from './SwapGenerated'; diff --git a/packages/@core-js/src/TonAPI/TonAPIGenerated.ts b/packages/@core-js/src/TonAPI/TonAPIGenerated.ts index e9a622567..73931a9a4 100644 --- a/packages/@core-js/src/TonAPI/TonAPIGenerated.ts +++ b/packages/@core-js/src/TonAPI/TonAPIGenerated.ts @@ -184,6 +184,7 @@ export interface BlockchainBlockShards { shards: { /** @example "(0,8000000000000000,4234234)" */ last_known_block_id: string; + last_known_block: BlockchainBlock; }[]; } @@ -312,6 +313,7 @@ export interface ComputePhase { * @example 0 */ exit_code?: number; + exit_code_description?: string; } export interface StoragePhase { @@ -369,6 +371,7 @@ export interface ActionPhase { * @example 1000 */ total_fees: number; + result_code_description?: string; } export interface Transaction { @@ -394,6 +397,11 @@ export interface Transaction { * @example 25713146000001 */ total_fees: number; + /** + * @format int64 + * @example 25713146000001 + */ + end_balance: number; transaction_type: TransactionType; /** @example "55e8809519cd3c49098c9ee45afdafcea7a894a74d0f628d94a115a50e045122" */ state_update_old: string; @@ -1289,6 +1297,14 @@ export interface ImagePreview { export type NftApprovedBy = NftApprovedByEnum[]; +/** @example "whitelist" */ +export enum TrustType { + Whitelist = 'whitelist', + Graylist = 'graylist', + Blacklist = 'blacklist', + None = 'none', +} + export interface Sale { /** @example "0:10C1073837B93FDAAD594284CE8B8EFF7B9CF25427440EB2FC682762E1471365" */ address: string; @@ -1323,6 +1339,9 @@ export interface NftItem { /** @example "crypto.ton" */ dns?: string; approved_by: NftApprovedBy; + /** @example false */ + include_cnft?: boolean; + trust: TrustType; } export interface NftItems { @@ -1389,6 +1408,7 @@ export interface Action { InscriptionMint?: InscriptionMintAction; /** shortly describes what this action is about. */ simple_preview: ActionSimplePreview; + base_transactions: string[]; } export interface TonTransferAction { @@ -1817,6 +1837,7 @@ export interface Auctions { export interface WalletDNS { /** @example "0:da6b1b6663a0e4d18cc8574ccd9db5296e367dd9324706f3bbd9eb1cd2caf0bf" */ address: string; + account: AccountAddress; /** @example true */ is_wallet: boolean; /** @example true */ @@ -2051,6 +2072,7 @@ export interface JettonInfo { mintable: boolean; /** @example 311500000000000 */ total_supply: string; + admin?: AccountAddress; metadata: JettonMetadata; verification: JettonVerificationType; /** @@ -2068,6 +2090,11 @@ export interface JettonHolders { /** @example 1000000000 */ balance: string; }[]; + /** + * @format int64 + * @example 2000 + */ + total: number; } export interface AccountStaking { @@ -2441,6 +2468,11 @@ export interface GetBlockchainAccountTransactionsParams { * @example 100 */ limit?: number; + /** + * used to sort the result-set in ascending or descending order by lt. + * @default "desc" + */ + sort_order?: SortOrderEnum; /** * account ID * @example "0:97264395BD65A255A429B11326C84128B7D70FFED7949ABAE3036D506BA38621" @@ -2448,6 +2480,24 @@ export interface GetBlockchainAccountTransactionsParams { accountId: string; } +/** + * used to sort the result-set in ascending or descending order by lt. + * @default "desc" + */ +export enum SortOrderEnum { + Desc = 'desc', + Asc = 'asc', +} + +/** + * used to sort the result-set in ascending or descending order by lt. + * @default "desc" + */ +export enum GetBlockchainAccountTransactionsParams1SortOrderEnum { + Desc = 'desc', + Asc = 'asc', +} + export interface ExecGetMethodForBlockchainAccountParams { /** * Supported values: @@ -2686,6 +2736,12 @@ export interface GetAccountEventParams { } export interface GetAccountTracesParams { + /** + * omit this parameter to get last events + * @format int64 + * @example 25758317000002 + */ + before_lt?: number; /** * @min 1 * @max 1000 @@ -3403,13 +3459,13 @@ export class TonAPIGenerated { status = { /** - * @description Reduce indexing latency + * @description Status * * @tags Blockchain - * @name ReduceIndexingLatency + * @name Status * @request GET:/v2/status */ - reduceIndexingLatency: (params: RequestParams = {}) => + status: (params: RequestParams = {}) => this.http.request({ path: `/v2/status`, method: 'GET', @@ -5346,5 +5402,31 @@ export class TonAPIGenerated { format: 'json', ...params, }), + + /** + * @description Get out msg queue sizes + * + * @tags Lite Server + * @name GetOutMsgQueueSizes + * @request GET:/v2/liteserver/get_out_msg_queue_sizes + */ + getOutMsgQueueSizes: (params: RequestParams = {}) => + this.http.request< + { + /** @format uint32 */ + ext_msg_queue_size_limit: number; + shards: { + id: BlockRaw; + /** @format uint32 */ + size: number; + }[]; + }, + Error + >({ + path: `/v2/liteserver/get_out_msg_queue_sizes`, + method: 'GET', + format: 'json', + ...params, + }), }; } diff --git a/packages/@core-js/src/formatters/Address.ts b/packages/@core-js/src/formatters/Address.ts index 9ebb4bd62..9d32e727e 100644 --- a/packages/@core-js/src/formatters/Address.ts +++ b/packages/@core-js/src/formatters/Address.ts @@ -1,6 +1,8 @@ import TonWeb, { AddressType } from 'tonweb'; +import { ContractService, mappedFromLegacyWalletVersion } from '../service'; +import { WalletNetwork } from '@tonkeeper/mobile/src/wallet/WalletTypes'; -const ContractVersions = ['lockup-0.1', 'v3R1', 'v3R2', 'v4R1', 'v4R2'] as const; +const ContractVersions = ['lockup-0.1', 'v3R1', 'v3R2', 'v4R1', 'v4R2', 'v5R1'] as const; export type AddressFormats = { friendly: string; @@ -89,33 +91,42 @@ export class Address { } static async fromPubkey( - pubkey: string | null, + pubkey: string, isTestnet: boolean, lockupConfig?: { workchain: number; configPubKey?: string; allowedDestinations?: string; }, - ): Promise { - if (!pubkey) return null; - - const tonweb = new TonWeb(); + ): Promise { const addresses = {} as AddressesByVersion; - const publicKey = Uint8Array.from(Buffer.from(pubkey, 'hex')); for (let contractVersion of ContractVersions) { - if (contractVersion === 'lockup-0.1') { + if (!lockupConfig && contractVersion === 'lockup-0.1') { continue; } - const wallet = new tonweb.wallet.all[contractVersion](tonweb.provider, { - publicKey, - wc: 0, + const isLockup = contractVersion === 'lockup-0.1'; + + const contract = ContractService.getWalletContract( + mappedFromLegacyWalletVersion[contractVersion], + Buffer.from(pubkey, 'hex'), + isLockup ? lockupConfig?.workchain ?? 0 : 0, + isTestnet ? WalletNetwork.testnet : WalletNetwork.mainnet, + isLockup + ? { + lockupPubKey: lockupConfig?.configPubKey, + allowedDestinations: lockupConfig?.allowedDestinations, + } + : undefined, + ); + + const raw = contract.address.toRawString(); + const friendly = contract.address.toString({ + urlSafe: true, + testOnly: isTestnet, + bounceable: false, }); - - const address = await wallet.getAddress(); - const raw = address.toString(false, false); - const friendly = address.toString(true, true, false, isTestnet); const short = Address.toShort(friendly); addresses[contractVersion] = { @@ -125,29 +136,6 @@ export class Address { }; } - if (lockupConfig) { - const wallet = new tonweb.lockupWallet.all['lockup-0.1'](tonweb.provider, { - publicKey, - wc: lockupConfig.workchain, - config: { - wallet_type: 'lockup-0.1', - config_public_key: lockupConfig.configPubKey, - allowed_destinations: lockupConfig.allowedDestinations, - }, - }); - - const address = await wallet.getAddress(); - const raw = address.toString(false, false); - const friendly = address.toString(true, true, false, isTestnet); - const short = Address.toShort(friendly); - - addresses['lockup-0.1'] = { - friendly, - raw, - short, - }; - } - return addresses; } diff --git a/packages/@core-js/src/legacy/contracts/LockupContractV1.ts b/packages/@core-js/src/legacy/contracts/LockupContractV1.ts index 7d259ce2a..2c397a185 100644 --- a/packages/@core-js/src/legacy/contracts/LockupContractV1.ts +++ b/packages/@core-js/src/legacy/contracts/LockupContractV1.ts @@ -11,6 +11,7 @@ import { SendMode, } from '@ton/core'; import { Maybe } from '@ton/ton/dist/utils/maybe'; +import { ExternallySingedAuthWallet3SendArgs } from '@ton/ton/dist/wallets/WalletContractV3'; import { createWalletTransferV3 } from '@ton/ton/dist/wallets/signing/createWalletTransfer'; export interface LockupContractV1AdditionalParams { @@ -150,6 +151,18 @@ export class LockupContractV1 implements Contract { }); } + createTransferAndSignRequestAsync(args: ExternallySingedAuthWallet3SendArgs) { + let sendMode = SendMode.PAY_GAS_SEPARATELY; + if (args.sendMode !== null && args.sendMode !== undefined) { + sendMode = args.sendMode; + } + return createWalletTransferV3({ + ...args, + sendMode, + walletId: this.walletId, + }); + } + /** * Create sender */ diff --git a/packages/@core-js/src/legacy/contracts/WalletContractV4R1.ts b/packages/@core-js/src/legacy/contracts/WalletContractV4R1.ts index d0a4d648b..e24bb0ebb 100644 --- a/packages/@core-js/src/legacy/contracts/WalletContractV4R1.ts +++ b/packages/@core-js/src/legacy/contracts/WalletContractV4R1.ts @@ -11,6 +11,10 @@ import { SendMode, } from '@ton/core'; import { Maybe } from '@ton/ton/dist/utils/maybe'; +import { + ExternallySingedAuthWallet4SendArgs, + SingedAuthWallet4SendArgs, +} from '@ton/ton/dist/wallets/WalletContractV4'; import { createWalletTransferV4 } from '@ton/ton/dist/wallets/signing/createWalletTransfer'; export class WalletContractV4R1 implements Contract { @@ -123,6 +127,21 @@ export class WalletContractV4R1 implements Contract { }); } + /** + * Create signed transfer + */ + createTransferAndSignRequestAsync(args: ExternallySingedAuthWallet4SendArgs) { + let sendMode = SendMode.PAY_GAS_SEPARATELY; + if (args.sendMode !== null && args.sendMode !== undefined) { + sendMode = args.sendMode; + } + return createWalletTransferV4({ + ...args, + walletId: this.walletId, + sendMode, + }); + } + /** * Create sender */ diff --git a/packages/@core-js/src/service/contractService.ts b/packages/@core-js/src/service/contractService.ts index b9522cdf5..e1419ed72 100644 --- a/packages/@core-js/src/service/contractService.ts +++ b/packages/@core-js/src/service/contractService.ts @@ -1,5 +1,5 @@ import { AnyAddress, tonAddress } from './transactionService'; -import { beginCell, Cell, comment } from '@ton/core'; +import { beginCell, Cell, comment, Contract, storeStateInit } from '@ton/core'; import { WalletContractV4R1, LockupContractV1, @@ -7,11 +7,16 @@ import { } from '../legacy'; import { WalletContractV3R1, WalletContractV3R2, WalletContractV4 } from '@ton/ton'; import nacl from 'tweetnacl'; +import { WalletContractV5 } from '@ton/ton/dist/wallets/WalletContractV5'; +import { WalletNetwork } from '@tonkeeper/mobile/src/wallet/WalletTypes'; + +export type Signer = (message: Cell) => Promise; export enum OpCodes { JETTON_TRANSFER = 0xf8a7ea5, NFT_TRANSFER = 0x5fcc3d14, STONFI_SWAP = 0x25938561, + TK_RELAYER_FEE = 0x878da6e3, } export enum WalletVersion { @@ -20,9 +25,20 @@ export enum WalletVersion { v4R1 = 2, v4R2 = 3, LockupV1 = 4, + v5R1 = 5, } +export const mappedFromLegacyWalletVersion = { + 'lockup-0.1': WalletVersion.LockupV1, + v3R1: WalletVersion.v3R1, + v3R2: WalletVersion.v3R2, + v4R1: WalletVersion.v4R1, + v4R2: WalletVersion.v4R2, + v5R1: WalletVersion.v5R1, +}; + export const contractVersionsMap = { + v5R1: WalletVersion.v5R1, v4R2: WalletVersion.v4R2, v4R1: WalletVersion.v4R1, v3R2: WalletVersion.v3R2, @@ -35,7 +51,8 @@ export type WalletContract = | WalletContractV3R1 | WalletContractV3R2 | WalletContractV4R1 - | WalletContractV4; + | WalletContractV4 + | WalletContractV5; export interface CreateNftTransferBodyParams { forwardAmount?: number | bigint; @@ -45,7 +62,7 @@ export interface CreateNftTransferBodyParams { newOwnerAddress: AnyAddress; forwardBody?: Cell | string; /* Query id. Defaults to Tonkeeper signature query id with 32 random bits */ - queryId?: number; + queryId?: number | bigint; } export interface CreateJettonTransferBodyParams { @@ -56,7 +73,15 @@ export interface CreateJettonTransferBodyParams { jettonAmount: number | bigint; forwardBody?: Cell | string; /* Query id. Defaults to Tonkeeper signature query id with 32 random bits */ - queryId?: number; + queryId?: number | bigint; +} + +export interface CreateStonfiSwapBodyParams { + assetToSwap: AnyAddress; + // TODO: should be nullable + referralAddress: AnyAddress; + userWalletAddress: AnyAddress; + minAskAmount: number | bigint; } export class ContractService { @@ -64,6 +89,7 @@ export class ContractService { version: WalletVersion, publicKey: Buffer, workchain: number, + network: WalletNetwork, additionalParams?: LockupContractV1AdditionalParams, ) { switch (version) { @@ -75,6 +101,14 @@ export class ContractService { return WalletContractV4R1.create({ workchain, publicKey }); case WalletVersion.v4R2: return WalletContractV4.create({ workchain, publicKey }); + case WalletVersion.v5R1: + return WalletContractV5.create({ + walletId: { + workChain: workchain, + networkGlobalId: network, + }, + publicKey, + }); case WalletVersion.LockupV1: return LockupContractV1.create({ workchain, publicKey, additionalParams }); } @@ -89,6 +123,13 @@ export class ContractService { return BigInt('0x' + value.toString('hex')); } + public static getMigrationQueryId() { + // crc32('jusdt_migration') + const signature = (0x93b5fcdf).toString(16); + const value = Buffer.concat([Buffer.from(signature, 'hex'), nacl.randomBytes(4)]); + return BigInt('0x' + value.toString('hex')); + } + static prepareForwardBody(body?: Cell | string) { return typeof body === 'string' ? comment(body) : body; } @@ -125,4 +166,23 @@ export class ContractService { .storeMaybeRef(this.prepareForwardBody(createJettonTransferBodyParams.forwardBody)) .endCell(); } + + static createStonfiSwapBody(createStonfiSwapBodyParams: CreateStonfiSwapBodyParams) { + return beginCell() + .storeUint(OpCodes.STONFI_SWAP, 32) + .storeAddress(tonAddress(createStonfiSwapBodyParams.assetToSwap)) + .storeCoins(createStonfiSwapBodyParams.minAskAmount) + .storeAddress(tonAddress(createStonfiSwapBodyParams.userWalletAddress)) + .storeBit(true) + .storeAddress(tonAddress(createStonfiSwapBodyParams.referralAddress)) + .endCell(); + } + + static getStateInit(contract: Contract) { + return beginCell() + .store(storeStateInit(contract.init!)) + .endCell() + .toBoc({ idx: false }) + .toString('base64'); + } } diff --git a/packages/@core-js/src/service/transactionService.ts b/packages/@core-js/src/service/transactionService.ts index cb4ee7d1a..3e6dead60 100644 --- a/packages/@core-js/src/service/transactionService.ts +++ b/packages/@core-js/src/service/transactionService.ts @@ -10,9 +10,8 @@ import { loadStateInit, } from '@ton/core'; import { Address as AddressFormatter } from '../formatters/Address'; -import { OpCodes, WalletContract } from './contractService'; +import { OpCodes, Signer, WalletContract } from './contractService'; import { SignRawMessage } from '@tonkeeper/mobile/src/core/ModalContainer/NFTOperations/TxRequest.types'; -import { tk } from '@tonkeeper/mobile/src/wallet'; export type AnyAddress = string | Address | AddressFormatter; @@ -20,7 +19,6 @@ export interface TransferParams { seqno: number; timeout?: number; sendMode?: number; - secretKey: Buffer; messages: MessageRelaxed[]; } @@ -41,7 +39,7 @@ export class TransactionService { return Math.floor(Date.now() / 1e3) + TransactionService.TTL; } - private static externalMessage(contract: WalletContract, seqno: number, body: Cell) { + public static externalMessage(contract: WalletContract, seqno: number, body: Cell) { return beginCell() .storeWritable( storeMessage( @@ -163,11 +161,17 @@ export class TransactionService { } } - static createTransfer(contract, transferParams: TransferParams) { - const transfer = contract.createTransfer({ + static async createTransfer( + contract: WalletContract, + signer: Signer, + transferParams: TransferParams, + authType?: 'external' | 'internal', + ) { + const transfer = await contract.createTransferAndSignRequestAsync({ + authType, timeout: transferParams.timeout ?? TransactionService.getTimeout(), seqno: transferParams.seqno, - secretKey: transferParams.secretKey, + signer, sendMode: transferParams.sendMode ?? SendMode.PAY_GAS_SEPARATELY + SendMode.IGNORE_ERRORS, messages: transferParams.messages, diff --git a/packages/@core-js/src/utils/State.ts b/packages/@core-js/src/utils/State.ts index a1a40c589..06bd6a11f 100644 --- a/packages/@core-js/src/utils/State.ts +++ b/packages/@core-js/src/utils/State.ts @@ -9,6 +9,8 @@ export interface StatePersistOptions { storage: Storage; partialize?: (data: TData) => Partial; rehydrated?: (data: TData) => void; + version?: number; + onUpdate?: (prevVersion: number | undefined, prevData: any) => Partial; } export class State { @@ -29,7 +31,13 @@ export class State { const { key, storage, partialize } = this.persistOptions; try { - const data = partialize ? partialize(this.data) : this.data; + const data: Partial & { __version?: number } = partialize + ? partialize(this.data) + : this.data; + + // We should keep last version in storage + data.__version = this.persistOptions.version; + await storage.setItem(key, JSON.stringify(data)); } catch (err) { console.log('[State]: error persist for key', key, err); @@ -58,6 +66,18 @@ export class State { this.data = { ...this.data, ...parsedData }; + if ( + this.persistOptions.onUpdate && + this.persistOptions.version && + (!parsedData.__version || this.persistOptions.version > parsedData.__version) + ) { + this.data = { + ...this.data, + ...this.persistOptions.onUpdate(parsedData.__version, this.data), + }; + this.storeIfNeeded(); + } + if (rehydrated) { rehydrated(this.data); } diff --git a/packages/@core-js/src/utils/constants.ts b/packages/@core-js/src/utils/constants.ts index 461ac0482..bb871d2c4 100644 --- a/packages/@core-js/src/utils/constants.ts +++ b/packages/@core-js/src/utils/constants.ts @@ -1,4 +1,14 @@ import { toNano } from '@ton/core'; export const ONE_TON = toNano('1'); +export const POINT_ONE_TON = toNano('0.1'); export const BASE_FORWARD_AMOUNT = toNano('0.05'); + +export const STONFI_CONSTANTS = { + routerAddress: '0:779dcc815138d9500e449c5291e7f12738c23d575b5310000f6a253bd607384e', + TONProxyAddress: '0:8cdc1d7640ad5ee326527fc1ad0514f468b30dc84b0173f0e155f451b4e11f7c', + JettonToJettonSwapFees: { + gasAmount: BigInt('265000000'), + forwardGasAmount: BigInt('205000000'), + }, +}; diff --git a/packages/@core-js/src/utils/network/network.ts b/packages/@core-js/src/utils/network/network.ts index 2b68ef604..b7a3005dd 100644 --- a/packages/@core-js/src/utils/network/network.ts +++ b/packages/@core-js/src/utils/network/network.ts @@ -1,5 +1,5 @@ -import { BackoffOptions } from "./backoff"; -import { jsonToUrl } from "./network.utils"; +import { BackoffOptions } from './backoff'; +import { jsonToUrl } from './network.utils'; type NetworkMethods = 'GET' | 'POST' | 'PUT' | 'DELETE'; @@ -7,7 +7,7 @@ export type NetworkOptions = { params?: Record; backoff?: BackoffOptions | boolean; headers?: any; -} +}; export type NetworkResponse = { statusText: string; @@ -15,37 +15,43 @@ export type NetworkResponse = { status: number; ok: boolean; data: TData; -} +}; export function createNetworkMethod(method: NetworkMethods) { - return async (url: string, options: NetworkOptions): Promise> => { + return async ( + url: string, + options: NetworkOptions, + ): Promise> => { const fetchOptions: RequestInit = { method, headers: options.headers ?? {} }; let query: string | undefined; if (options.params) { if (method === 'GET') { query = jsonToUrl(options.params); - } else { + } else { fetchOptions.body = JSON.stringify(options.params); - (fetchOptions.headers as Record)['Content-Type'] = 'application/json'; + (fetchOptions.headers as Record)['Content-Type'] = + 'application/json'; } } const endpoint = url + (query ? `?${query}` : ''); const response = await fetch(endpoint, fetchOptions); - let data = {} as T; - try { - data = await response.json(); - } catch (err) {} - return { + if (response.status >= 400) { + throw new Error('Bat response'); + } + + let data = (await response.json()) as T; + + return { statusText: response.statusText, headers: response.headers, status: response.status, ok: response.ok, - data, + data, }; - } + }; } export const network = { diff --git a/packages/mobile/android/app/build.gradle b/packages/mobile/android/app/build.gradle index 0cc75381b..af4117071 100644 --- a/packages/mobile/android/app/build.gradle +++ b/packages/mobile/android/app/build.gradle @@ -91,8 +91,8 @@ android { applicationId "com.ton_keeper" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 433 - versionName "4.4.1" + versionCode 600 + versionName "4.7.1" missingDimensionStrategy 'react-native-camera', 'general' missingDimensionStrategy 'store', 'play' } diff --git a/packages/mobile/android/app/src/main/AndroidManifest.xml b/packages/mobile/android/app/src/main/AndroidManifest.xml index 074ae426c..e3ab56a1d 100644 --- a/packages/mobile/android/app/src/main/AndroidManifest.xml +++ b/packages/mobile/android/app/src/main/AndroidManifest.xml @@ -19,6 +19,30 @@ + + + + + + + + + + + + + + diff --git a/packages/mobile/android/app/src/main/java/com/ton_keeper/MainApplication.java b/packages/mobile/android/app/src/main/java/com/ton_keeper/MainApplication.java index dac4f818d..430b0f390 100644 --- a/packages/mobile/android/app/src/main/java/com/ton_keeper/MainApplication.java +++ b/packages/mobile/android/app/src/main/java/com/ton_keeper/MainApplication.java @@ -11,6 +11,7 @@ import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint; import com.facebook.react.defaults.DefaultReactNativeHost; import com.facebook.soloader.SoLoader; +import com.facebook.react.modules.i18nmanager.I18nUtil; import expo.modules.ApplicationLifecycleDispatcher; import expo.modules.ReactNativeHostWrapper; @@ -65,6 +66,11 @@ public ReactNativeHost getReactNativeHost() { @Override public void onCreate() { super.onCreate(); + + I18nUtil sharedI18nUtilInstance = I18nUtil.getInstance(); + sharedI18nUtilInstance.forceRTL(this, false); + sharedI18nUtilInstance.allowRTL(this, false); + SoLoader.init(this, /* native exopackage */ false); if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) { // If you opted-in for the New Architecture, we load the native entry point for this app. @@ -77,7 +83,7 @@ public void onCreate() { try { Field field = CursorWindow.class.getDeclaredField("sCursorWindowSize"); field.setAccessible(true); - field.set(null, 300 * 1024 * 1024); + field.set(null, 50 * 1024 * 1024); } catch (Exception e) { if (BuildConfig.DEBUG) { e.printStackTrace(); diff --git a/packages/mobile/android/app/src/main/res/xml/locales_config.xml b/packages/mobile/android/app/src/main/res/xml/locales_config.xml index 54caa1fa5..e89b01730 100644 --- a/packages/mobile/android/app/src/main/res/xml/locales_config.xml +++ b/packages/mobile/android/app/src/main/res/xml/locales_config.xml @@ -4,4 +4,7 @@ + + + diff --git a/packages/mobile/android/build.gradle b/packages/mobile/android/build.gradle index 776f800c7..8705dd359 100644 --- a/packages/mobile/android/build.gradle +++ b/packages/mobile/android/build.gradle @@ -20,7 +20,7 @@ buildscript { classpath("com.android.tools.build:gradle:7.4.2") classpath('com.facebook.react:react-native-gradle-plugin') classpath('de.undercouch:gradle-download-task:5.0.1') - classpath('com.google.gms:google-services:4.3.14') + classpath('com.google.gms:google-services:4.4.1') classpath('com.google.firebase:firebase-crashlytics-gradle:2.9.2') } } diff --git a/packages/mobile/ios/fastlane/Fastfile b/packages/mobile/ios/fastlane/Fastfile index 63345d50b..d8b75c938 100644 --- a/packages/mobile/ios/fastlane/Fastfile +++ b/packages/mobile/ios/fastlane/Fastfile @@ -135,14 +135,15 @@ platform :ios do desc "Upload to TestFlight / ASC" lane :upload_release do api_key = lane_context[SharedValues::APP_STORE_CONNECT_API_KEY] + + branch = sh("git rev-parse --abbrev-ref HEAD").strip + hash = sh("git rev-parse --short HEAD").strip + message = sh("git log -1 --pretty=%B").strip + changelog = "#{branch}/#{hash}\n\n#{message}" - deliver( + upload_to_testflight( api_key: api_key, - skip_screenshots: true, - skip_metadata: true, - skip_app_version_update: true, - force: true, # skips verification of HTML preview file (since this will be run from a CI machine) - run_precheck_before_submit: false # not supported through ASC API yet + changelog: changelog, ) end diff --git a/packages/mobile/ios/ton_keeper.xcodeproj/project.pbxproj b/packages/mobile/ios/ton_keeper.xcodeproj/project.pbxproj index 110e82934..d3e6e1171 100644 --- a/packages/mobile/ios/ton_keeper.xcodeproj/project.pbxproj +++ b/packages/mobile/ios/ton_keeper.xcodeproj/project.pbxproj @@ -131,6 +131,12 @@ 39C1C20FA83478354251FB2A /* libPods-ton_keeper.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ton_keeper.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 5008E610299F9D1400D4850B /* SFMono-Bold.otf */ = {isa = PBXFileReference; lastKnownFileType = file; name = "SFMono-Bold.otf"; path = "../assets/fonts/SFMono-Bold.otf"; sourceTree = ""; }; 5008E611299F9D1400D4850B /* SFMono-Medium.otf */ = {isa = PBXFileReference; lastKnownFileType = file; name = "SFMono-Medium.otf"; path = "../assets/fonts/SFMono-Medium.otf"; sourceTree = ""; }; + 5034B0C52C10AE4E0002EFBC /* uz */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uz; path = uz.lproj/PassCode.strings; sourceTree = ""; }; + 5034B0C62C10AE4F0002EFBC /* uz */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = uz; path = uz.lproj/PassCode.stringsdict; sourceTree = ""; }; + 5034B0C72C10AE570002EFBC /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/PassCode.strings; sourceTree = ""; }; + 5034B0C82C10AE570002EFBC /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = uk; path = uk.lproj/PassCode.stringsdict; sourceTree = ""; }; + 5034B0C92C10AE740002EFBC /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/PassCode.strings; sourceTree = ""; }; + 5034B0CA2C10AE740002EFBC /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = es; path = es.lproj/PassCode.stringsdict; sourceTree = ""; }; 506008712B7F9EF10061C2CD /* StoreKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = StoreKit.framework; path = System/Library/Frameworks/StoreKit.framework; sourceTree = SDKROOT; }; 50AD04972AD96A5C00E0648C /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/PassCode.strings; sourceTree = ""; }; 50AD04982AD96A5D00E0648C /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = tr; path = tr.lproj/PassCode.stringsdict; sourceTree = ""; }; @@ -829,6 +835,9 @@ ru, tr, "zh-Hans", + uz, + uk, + es, ); mainGroup = 83CBB9F61A601CBA00E9B192; productRefGroup = 83CBBA001A601CBA00E9B192 /* Products */; @@ -892,7 +901,7 @@ ); inputPaths = ( "${DWARF_DSYM_FOLDER_PATH}/${DWARF_DSYM_FILE_NAME}/Contents/Resources/DWARF/${TARGET_NAME}", - "$(SRCROOT)/$(BUILT_PRODUCTS_DIR)/$(INFOPLIST_PATH)", + "$(BUILT_PRODUCTS_DIR)/$(INFOPLIST_PATH)", ); name = "[CP-User] [RNFB] Crashlytics Configuration"; runOnlyForDeploymentPostprocessing = 0; @@ -947,7 +956,7 @@ name = "[CP-User] [RNFB] Core Configuration"; runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "#!/usr/bin/env bash\n#\n# Copyright (c) 2016-present Invertase Limited & Contributors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this library except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\nset -e\n\n_MAX_LOOKUPS=2;\n_SEARCH_RESULT=''\n_RN_ROOT_EXISTS=''\n_CURRENT_LOOKUPS=1\n_JSON_ROOT=\"'react-native'\"\n_JSON_FILE_NAME='firebase.json'\n_JSON_OUTPUT_BASE64='e30=' # { }\n_CURRENT_SEARCH_DIR=${PROJECT_DIR}\n_PLIST_BUDDY=/usr/libexec/PlistBuddy\n_TARGET_PLIST=\"${BUILT_PRODUCTS_DIR}/${INFOPLIST_PATH}\"\n_DSYM_PLIST=\"${DWARF_DSYM_FOLDER_PATH}/${DWARF_DSYM_FILE_NAME}/Contents/Info.plist\"\n\n# plist arrays\n_PLIST_ENTRY_KEYS=()\n_PLIST_ENTRY_TYPES=()\n_PLIST_ENTRY_VALUES=()\n\nfunction setPlistValue {\n echo \"info: setting plist entry '$1' of type '$2' in file '$4'\"\n ${_PLIST_BUDDY} -c \"Add :$1 $2 '$3'\" $4 || echo \"info: '$1' already exists\"\n}\n\nfunction getFirebaseJsonKeyValue () {\n if [[ ${_RN_ROOT_EXISTS} ]]; then\n ruby -Ku -e \"require 'rubygems';require 'json'; output=JSON.parse('$1'); puts output[$_JSON_ROOT]['$2']\"\n else\n echo \"\"\n fi;\n}\n\nfunction jsonBoolToYesNo () {\n if [[ $1 == \"false\" ]]; then\n echo \"NO\"\n elif [[ $1 == \"true\" ]]; then\n echo \"YES\"\n else echo \"NO\"\n fi\n}\n\necho \"info: -> RNFB build script started\"\necho \"info: 1) Locating ${_JSON_FILE_NAME} file:\"\n\nif [[ -z ${_CURRENT_SEARCH_DIR} ]]; then\n _CURRENT_SEARCH_DIR=$(pwd)\nfi;\n\nwhile true; do\n _CURRENT_SEARCH_DIR=$(dirname \"$_CURRENT_SEARCH_DIR\")\n if [[ \"$_CURRENT_SEARCH_DIR\" == \"/\" ]] || [[ ${_CURRENT_LOOKUPS} -gt ${_MAX_LOOKUPS} ]]; then break; fi;\n echo \"info: ($_CURRENT_LOOKUPS of $_MAX_LOOKUPS) Searching in '$_CURRENT_SEARCH_DIR' for a ${_JSON_FILE_NAME} file.\"\n _SEARCH_RESULT=$(find \"$_CURRENT_SEARCH_DIR\" -maxdepth 2 -name ${_JSON_FILE_NAME} -print | /usr/bin/head -n 1)\n if [[ ${_SEARCH_RESULT} ]]; then\n echo \"info: ${_JSON_FILE_NAME} found at $_SEARCH_RESULT\"\n break;\n fi;\n _CURRENT_LOOKUPS=$((_CURRENT_LOOKUPS+1))\ndone\n\nif [[ ${_SEARCH_RESULT} ]]; then\n _JSON_OUTPUT_RAW=$(cat \"${_SEARCH_RESULT}\")\n _RN_ROOT_EXISTS=$(ruby -Ku -e \"require 'rubygems';require 'json'; output=JSON.parse('$_JSON_OUTPUT_RAW'); puts output[$_JSON_ROOT]\" || echo '')\n\n if [[ ${_RN_ROOT_EXISTS} ]]; then\n if ! python3 --version >/dev/null 2>&1; then echo \"python3 not found, firebase.json file processing error.\" && exit 1; fi\n _JSON_OUTPUT_BASE64=$(python3 -c 'import json,sys,base64;print(base64.b64encode(bytes(json.dumps(json.loads(open('\"'${_SEARCH_RESULT}'\"', '\"'rb'\"').read())['${_JSON_ROOT}']), '\"'utf-8'\"')).decode())' || echo \"e30=\")\n fi\n\n _PLIST_ENTRY_KEYS+=(\"firebase_json_raw\")\n _PLIST_ENTRY_TYPES+=(\"string\")\n _PLIST_ENTRY_VALUES+=(\"$_JSON_OUTPUT_BASE64\")\n\n # config.app_data_collection_default_enabled\n _APP_DATA_COLLECTION_ENABLED=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"app_data_collection_default_enabled\")\n if [[ $_APP_DATA_COLLECTION_ENABLED ]]; then\n _PLIST_ENTRY_KEYS+=(\"FirebaseDataCollectionDefaultEnabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_APP_DATA_COLLECTION_ENABLED\")\")\n fi\n\n # config.analytics_auto_collection_enabled\n _ANALYTICS_AUTO_COLLECTION=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"analytics_auto_collection_enabled\")\n if [[ $_ANALYTICS_AUTO_COLLECTION ]]; then\n _PLIST_ENTRY_KEYS+=(\"FIREBASE_ANALYTICS_COLLECTION_ENABLED\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_AUTO_COLLECTION\")\")\n fi\n\n # config.analytics_collection_deactivated\n _ANALYTICS_DEACTIVATED=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"analytics_collection_deactivated\")\n if [[ $_ANALYTICS_DEACTIVATED ]]; then\n _PLIST_ENTRY_KEYS+=(\"FIREBASE_ANALYTICS_COLLECTION_DEACTIVATED\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_DEACTIVATED\")\")\n fi\n\n # config.analytics_idfv_collection_enabled\n _ANALYTICS_IDFV_COLLECTION=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"analytics_idfv_collection_enabled\")\n if [[ $_ANALYTICS_IDFV_COLLECTION ]]; then\n _PLIST_ENTRY_KEYS+=(\"GOOGLE_ANALYTICS_IDFV_COLLECTION_ENABLED\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_IDFV_COLLECTION\")\")\n fi\n\n # config.analytics_default_allow_ad_personalization_signals\n _ANALYTICS_PERSONALIZATION=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"analytics_default_allow_ad_personalization_signals\")\n if [[ $_ANALYTICS_PERSONALIZATION ]]; then\n _PLIST_ENTRY_KEYS+=(\"GOOGLE_ANALYTICS_DEFAULT_ALLOW_AD_PERSONALIZATION_SIGNALS\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_PERSONALIZATION\")\")\n fi\n\n # config.analytics_registration_with_ad_network_enabled\n _ANALYTICS_REGISTRATION_WITH_AD_NETWORK=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"google_analytics_registration_with_ad_network_enabled\")\n if [[ $_ANALYTICS_REGISTRATION_WITH_AD_NETWORK ]]; then\n _PLIST_ENTRY_KEYS+=(\"GOOGLE_ANALYTICS_REGISTRATION_WITH_AD_NETWORK_ENABLED\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_REGISTRATION_WITH_AD_NETWORK\")\")\n fi\n\n # config.google_analytics_automatic_screen_reporting_enabled\n _ANALYTICS_AUTO_SCREEN_REPORTING=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"google_analytics_automatic_screen_reporting_enabled\")\n if [[ $_ANALYTICS_AUTO_SCREEN_REPORTING ]]; then\n _PLIST_ENTRY_KEYS+=(\"FirebaseAutomaticScreenReportingEnabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_AUTO_SCREEN_REPORTING\")\")\n fi\n\n # config.perf_auto_collection_enabled\n _PERF_AUTO_COLLECTION=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"perf_auto_collection_enabled\")\n if [[ $_PERF_AUTO_COLLECTION ]]; then\n _PLIST_ENTRY_KEYS+=(\"firebase_performance_collection_enabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_PERF_AUTO_COLLECTION\")\")\n fi\n\n # config.perf_collection_deactivated\n _PERF_DEACTIVATED=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"perf_collection_deactivated\")\n if [[ $_PERF_DEACTIVATED ]]; then\n _PLIST_ENTRY_KEYS+=(\"firebase_performance_collection_deactivated\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_PERF_DEACTIVATED\")\")\n fi\n\n # config.messaging_auto_init_enabled\n _MESSAGING_AUTO_INIT=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"messaging_auto_init_enabled\")\n if [[ $_MESSAGING_AUTO_INIT ]]; then\n _PLIST_ENTRY_KEYS+=(\"FirebaseMessagingAutoInitEnabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_MESSAGING_AUTO_INIT\")\")\n fi\n\n # config.in_app_messaging_auto_colllection_enabled\n _FIAM_AUTO_INIT=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"in_app_messaging_auto_collection_enabled\")\n if [[ $_FIAM_AUTO_INIT ]]; then\n _PLIST_ENTRY_KEYS+=(\"FirebaseInAppMessagingAutomaticDataCollectionEnabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_FIAM_AUTO_INIT\")\")\n fi\n\n # config.app_check_token_auto_refresh\n _APP_CHECK_TOKEN_AUTO_REFRESH=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"app_check_token_auto_refresh\")\n if [[ $_APP_CHECK_TOKEN_AUTO_REFRESH ]]; then\n _PLIST_ENTRY_KEYS+=(\"FirebaseAppCheckTokenAutoRefreshEnabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_APP_CHECK_TOKEN_AUTO_REFRESH\")\")\n fi\n\n # config.crashlytics_disable_auto_disabler - undocumented for now - mainly for debugging, document if becomes useful\n _CRASHLYTICS_AUTO_DISABLE_ENABLED=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"crashlytics_disable_auto_disabler\")\n if [[ $_CRASHLYTICS_AUTO_DISABLE_ENABLED == \"true\" ]]; then\n echo \"Disabled Crashlytics auto disabler.\" # do nothing\n else\n _PLIST_ENTRY_KEYS+=(\"FirebaseCrashlyticsCollectionEnabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"NO\")\n fi\nelse\n _PLIST_ENTRY_KEYS+=(\"firebase_json_raw\")\n _PLIST_ENTRY_TYPES+=(\"string\")\n _PLIST_ENTRY_VALUES+=(\"$_JSON_OUTPUT_BASE64\")\n echo \"warning: A firebase.json file was not found, whilst this file is optional it is recommended to include it to configure firebase services in React Native Firebase.\"\nfi;\n\necho \"info: 2) Injecting Info.plist entries: \"\n\n# Log out the keys we're adding\nfor i in \"${!_PLIST_ENTRY_KEYS[@]}\"; do\n echo \" -> $i) ${_PLIST_ENTRY_KEYS[$i]}\" \"${_PLIST_ENTRY_TYPES[$i]}\" \"${_PLIST_ENTRY_VALUES[$i]}\"\ndone\n\nfor plist in \"${_TARGET_PLIST}\" \"${_DSYM_PLIST}\" ; do\n if [[ -f \"${plist}\" ]]; then\n\n # paths with spaces break the call to setPlistValue. temporarily modify\n # the shell internal field separator variable (IFS), which normally\n # includes spaces, to consist only of line breaks\n oldifs=$IFS\n IFS=\"\n\"\n\n for i in \"${!_PLIST_ENTRY_KEYS[@]}\"; do\n setPlistValue \"${_PLIST_ENTRY_KEYS[$i]}\" \"${_PLIST_ENTRY_TYPES[$i]}\" \"${_PLIST_ENTRY_VALUES[$i]}\" \"${plist}\"\n done\n\n # restore the original internal field separator value\n IFS=$oldifs\n else\n echo \"warning: A Info.plist build output file was not found (${plist})\"\n fi\ndone\n\necho \"info: <- RNFB build script finished\"\n"; + shellScript = "#!/usr/bin/env bash\n#\n# Copyright (c) 2016-present Invertase Limited & Contributors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this library except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n##########################################################################\n##########################################################################\n#\n# NOTE THAT IF YOU CHANGE THIS FILE YOU MUST RUN pod install AFTERWARDS\n#\n# This file is installed as an Xcode build script in the project file\n# by cocoapods, and you will not see your changes until you pod install\n#\n##########################################################################\n##########################################################################\n\nset -e\n\n_MAX_LOOKUPS=2;\n_SEARCH_RESULT=''\n_RN_ROOT_EXISTS=''\n_CURRENT_LOOKUPS=1\n_JSON_ROOT=\"'react-native'\"\n_JSON_FILE_NAME='firebase.json'\n_JSON_OUTPUT_BASE64='e30=' # { }\n_CURRENT_SEARCH_DIR=${PROJECT_DIR}\n_PLIST_BUDDY=/usr/libexec/PlistBuddy\n_TARGET_PLIST=\"${BUILT_PRODUCTS_DIR}/${INFOPLIST_PATH}\"\n_DSYM_PLIST=\"${DWARF_DSYM_FOLDER_PATH}/${DWARF_DSYM_FILE_NAME}/Contents/Info.plist\"\n\n# plist arrays\n_PLIST_ENTRY_KEYS=()\n_PLIST_ENTRY_TYPES=()\n_PLIST_ENTRY_VALUES=()\n\nfunction setPlistValue {\n echo \"info: setting plist entry '$1' of type '$2' in file '$4'\"\n ${_PLIST_BUDDY} -c \"Add :$1 $2 '$3'\" $4 || echo \"info: '$1' already exists\"\n}\n\nfunction getFirebaseJsonKeyValue () {\n if [[ ${_RN_ROOT_EXISTS} ]]; then\n ruby -Ku -e \"require 'rubygems';require 'json'; output=JSON.parse('$1'); puts output[$_JSON_ROOT]['$2']\"\n else\n echo \"\"\n fi;\n}\n\nfunction jsonBoolToYesNo () {\n if [[ $1 == \"false\" ]]; then\n echo \"NO\"\n elif [[ $1 == \"true\" ]]; then\n echo \"YES\"\n else echo \"NO\"\n fi\n}\n\necho \"info: -> RNFB build script started\"\necho \"info: 1) Locating ${_JSON_FILE_NAME} file:\"\n\nif [[ -z ${_CURRENT_SEARCH_DIR} ]]; then\n _CURRENT_SEARCH_DIR=$(pwd)\nfi;\n\nwhile true; do\n _CURRENT_SEARCH_DIR=$(dirname \"$_CURRENT_SEARCH_DIR\")\n if [[ \"$_CURRENT_SEARCH_DIR\" == \"/\" ]] || [[ ${_CURRENT_LOOKUPS} -gt ${_MAX_LOOKUPS} ]]; then break; fi;\n echo \"info: ($_CURRENT_LOOKUPS of $_MAX_LOOKUPS) Searching in '$_CURRENT_SEARCH_DIR' for a ${_JSON_FILE_NAME} file.\"\n _SEARCH_RESULT=$(find \"$_CURRENT_SEARCH_DIR\" -maxdepth 2 -name ${_JSON_FILE_NAME} -print | /usr/bin/head -n 1)\n if [[ ${_SEARCH_RESULT} ]]; then\n echo \"info: ${_JSON_FILE_NAME} found at $_SEARCH_RESULT\"\n break;\n fi;\n _CURRENT_LOOKUPS=$((_CURRENT_LOOKUPS+1))\ndone\n\nif [[ ${_SEARCH_RESULT} ]]; then\n _JSON_OUTPUT_RAW=$(cat \"${_SEARCH_RESULT}\")\n _RN_ROOT_EXISTS=$(ruby -Ku -e \"require 'rubygems';require 'json'; output=JSON.parse('$_JSON_OUTPUT_RAW'); puts output[$_JSON_ROOT]\" || echo '')\n\n if [[ ${_RN_ROOT_EXISTS} ]]; then\n if ! python3 --version >/dev/null 2>&1; then echo \"python3 not found, firebase.json file processing error.\" && exit 1; fi\n _JSON_OUTPUT_BASE64=$(python3 -c 'import json,sys,base64;print(base64.b64encode(bytes(json.dumps(json.loads(open('\"'${_SEARCH_RESULT}'\"', '\"'rb'\"').read())['${_JSON_ROOT}']), '\"'utf-8'\"')).decode())' || echo \"e30=\")\n fi\n\n _PLIST_ENTRY_KEYS+=(\"firebase_json_raw\")\n _PLIST_ENTRY_TYPES+=(\"string\")\n _PLIST_ENTRY_VALUES+=(\"$_JSON_OUTPUT_BASE64\")\n\n # config.app_data_collection_default_enabled\n _APP_DATA_COLLECTION_ENABLED=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"app_data_collection_default_enabled\")\n if [[ $_APP_DATA_COLLECTION_ENABLED ]]; then\n _PLIST_ENTRY_KEYS+=(\"FirebaseDataCollectionDefaultEnabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_APP_DATA_COLLECTION_ENABLED\")\")\n fi\n\n # config.analytics_auto_collection_enabled\n _ANALYTICS_AUTO_COLLECTION=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"analytics_auto_collection_enabled\")\n if [[ $_ANALYTICS_AUTO_COLLECTION ]]; then\n _PLIST_ENTRY_KEYS+=(\"FIREBASE_ANALYTICS_COLLECTION_ENABLED\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_AUTO_COLLECTION\")\")\n fi\n\n # config.analytics_collection_deactivated\n _ANALYTICS_DEACTIVATED=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"analytics_collection_deactivated\")\n if [[ $_ANALYTICS_DEACTIVATED ]]; then\n _PLIST_ENTRY_KEYS+=(\"FIREBASE_ANALYTICS_COLLECTION_DEACTIVATED\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_DEACTIVATED\")\")\n fi\n\n # config.analytics_idfv_collection_enabled\n _ANALYTICS_IDFV_COLLECTION=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"analytics_idfv_collection_enabled\")\n if [[ $_ANALYTICS_IDFV_COLLECTION ]]; then\n _PLIST_ENTRY_KEYS+=(\"GOOGLE_ANALYTICS_IDFV_COLLECTION_ENABLED\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_IDFV_COLLECTION\")\")\n fi\n\n # config.analytics_default_allow_analytics_storage\n _ANALYTICS_STORAGE=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"analytics_default_allow_analytics_storage\")\n if [[ $_ANALYTICS_STORAGE ]]; then\n _PLIST_ENTRY_KEYS+=(\"GOOGLE_ANALYTICS_DEFAULT_ALLOW_ANALYTICS_STORAGE\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_STORAGE\")\")\n fi\n\n # config.analytics_default_allow_ad_storage\n _ANALYTICS_AD_STORAGE=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"analytics_default_allow_ad_storage\")\n if [[ $_ANALYTICS_AD_STORAGE ]]; then\n _PLIST_ENTRY_KEYS+=(\"GOOGLE_ANALYTICS_DEFAULT_ALLOW_AD_STORAGE\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_AD_STORAGE\")\")\n fi\n\n # config.analytics_default_allow_ad_user_data\n _ANALYTICS_AD_USER_DATA=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"analytics_default_allow_ad_user_data\")\n if [[ $_ANALYTICS_AD_USER_DATA ]]; then\n _PLIST_ENTRY_KEYS+=(\"GOOGLE_ANALYTICS_DEFAULT_ALLOW_AD_USER_DATA\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_AD_USER_DATA\")\")\n fi\n\n # config.analytics_default_allow_ad_personalization_signals\n _ANALYTICS_PERSONALIZATION=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"analytics_default_allow_ad_personalization_signals\")\n if [[ $_ANALYTICS_PERSONALIZATION ]]; then\n _PLIST_ENTRY_KEYS+=(\"GOOGLE_ANALYTICS_DEFAULT_ALLOW_AD_PERSONALIZATION_SIGNALS\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_PERSONALIZATION\")\")\n fi\n\n # config.analytics_registration_with_ad_network_enabled\n _ANALYTICS_REGISTRATION_WITH_AD_NETWORK=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"google_analytics_registration_with_ad_network_enabled\")\n if [[ $_ANALYTICS_REGISTRATION_WITH_AD_NETWORK ]]; then\n _PLIST_ENTRY_KEYS+=(\"GOOGLE_ANALYTICS_REGISTRATION_WITH_AD_NETWORK_ENABLED\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_REGISTRATION_WITH_AD_NETWORK\")\")\n fi\n\n # config.google_analytics_automatic_screen_reporting_enabled\n _ANALYTICS_AUTO_SCREEN_REPORTING=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"google_analytics_automatic_screen_reporting_enabled\")\n if [[ $_ANALYTICS_AUTO_SCREEN_REPORTING ]]; then\n _PLIST_ENTRY_KEYS+=(\"FirebaseAutomaticScreenReportingEnabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_AUTO_SCREEN_REPORTING\")\")\n fi\n\n # config.perf_auto_collection_enabled\n _PERF_AUTO_COLLECTION=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"perf_auto_collection_enabled\")\n if [[ $_PERF_AUTO_COLLECTION ]]; then\n _PLIST_ENTRY_KEYS+=(\"firebase_performance_collection_enabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_PERF_AUTO_COLLECTION\")\")\n fi\n\n # config.perf_collection_deactivated\n _PERF_DEACTIVATED=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"perf_collection_deactivated\")\n if [[ $_PERF_DEACTIVATED ]]; then\n _PLIST_ENTRY_KEYS+=(\"firebase_performance_collection_deactivated\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_PERF_DEACTIVATED\")\")\n fi\n\n # config.messaging_auto_init_enabled\n _MESSAGING_AUTO_INIT=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"messaging_auto_init_enabled\")\n if [[ $_MESSAGING_AUTO_INIT ]]; then\n _PLIST_ENTRY_KEYS+=(\"FirebaseMessagingAutoInitEnabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_MESSAGING_AUTO_INIT\")\")\n fi\n\n # config.in_app_messaging_auto_colllection_enabled\n _FIAM_AUTO_INIT=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"in_app_messaging_auto_collection_enabled\")\n if [[ $_FIAM_AUTO_INIT ]]; then\n _PLIST_ENTRY_KEYS+=(\"FirebaseInAppMessagingAutomaticDataCollectionEnabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_FIAM_AUTO_INIT\")\")\n fi\n\n # config.app_check_token_auto_refresh\n _APP_CHECK_TOKEN_AUTO_REFRESH=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"app_check_token_auto_refresh\")\n if [[ $_APP_CHECK_TOKEN_AUTO_REFRESH ]]; then\n _PLIST_ENTRY_KEYS+=(\"FirebaseAppCheckTokenAutoRefreshEnabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_APP_CHECK_TOKEN_AUTO_REFRESH\")\")\n fi\n\n # config.crashlytics_disable_auto_disabler - undocumented for now - mainly for debugging, document if becomes useful\n _CRASHLYTICS_AUTO_DISABLE_ENABLED=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"crashlytics_disable_auto_disabler\")\n if [[ $_CRASHLYTICS_AUTO_DISABLE_ENABLED == \"true\" ]]; then\n echo \"Disabled Crashlytics auto disabler.\" # do nothing\n else\n _PLIST_ENTRY_KEYS+=(\"FirebaseCrashlyticsCollectionEnabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"NO\")\n fi\nelse\n _PLIST_ENTRY_KEYS+=(\"firebase_json_raw\")\n _PLIST_ENTRY_TYPES+=(\"string\")\n _PLIST_ENTRY_VALUES+=(\"$_JSON_OUTPUT_BASE64\")\n echo \"warning: A firebase.json file was not found, whilst this file is optional it is recommended to include it to configure firebase services in React Native Firebase.\"\nfi;\n\necho \"info: 2) Injecting Info.plist entries: \"\n\n# Log out the keys we're adding\nfor i in \"${!_PLIST_ENTRY_KEYS[@]}\"; do\n echo \" -> $i) ${_PLIST_ENTRY_KEYS[$i]}\" \"${_PLIST_ENTRY_TYPES[$i]}\" \"${_PLIST_ENTRY_VALUES[$i]}\"\ndone\n\nfor plist in \"${_TARGET_PLIST}\" \"${_DSYM_PLIST}\" ; do\n if [[ -f \"${plist}\" ]]; then\n\n # paths with spaces break the call to setPlistValue. temporarily modify\n # the shell internal field separator variable (IFS), which normally\n # includes spaces, to consist only of line breaks\n oldifs=$IFS\n IFS=\"\n\"\n\n for i in \"${!_PLIST_ENTRY_KEYS[@]}\"; do\n setPlistValue \"${_PLIST_ENTRY_KEYS[$i]}\" \"${_PLIST_ENTRY_TYPES[$i]}\" \"${_PLIST_ENTRY_VALUES[$i]}\" \"${plist}\"\n done\n\n # restore the original internal field separator value\n IFS=$oldifs\n else\n echo \"warning: A Info.plist build output file was not found (${plist})\"\n fi\ndone\n\necho \"info: <- RNFB build script finished\"\n"; }; 8C4EC056529EFADE0053216C /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; @@ -1056,7 +1065,7 @@ outputFileListPaths = ( ); outputPaths = ( - $SRCROOT/$PROJECT_NAME/Resources/Generated/R.generated.swift, + "$SRCROOT/$PROJECT_NAME/Resources/Generated/R.generated.swift", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; @@ -1208,6 +1217,9 @@ C438087A292BD26C00FF5669 /* ru */, 50AD04982AD96A5D00E0648C /* tr */, 50AD049A2AD96AFA00E0648C /* zh-Hans */, + 5034B0C62C10AE4F0002EFBC /* uz */, + 5034B0C82C10AE570002EFBC /* uk */, + 5034B0CA2C10AE740002EFBC /* es */, ); name = PassCode.stringsdict; sourceTree = ""; @@ -1219,6 +1231,9 @@ C4F5F2082927C88200D08D79 /* ru */, 50AD04972AD96A5C00E0648C /* tr */, 50AD04992AD96AFA00E0648C /* zh-Hans */, + 5034B0C52C10AE4E0002EFBC /* uz */, + 5034B0C72C10AE570002EFBC /* uk */, + 5034B0C92C10AE740002EFBC /* es */, ); name = PassCode.strings; sourceTree = ""; @@ -1288,7 +1303,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = ton_keeper/ton_keeper.entitlements; - CURRENT_PROJECT_VERSION = 433; + CURRENT_PROJECT_VERSION = 600; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = CT523DK2KC; ENABLE_BITCODE = NO; @@ -1298,7 +1313,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 4.4.1; + MARKETING_VERSION = 4.7.1; OTHER_LDFLAGS = ( "$(inherited)", "-ObjC", @@ -1323,7 +1338,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = ton_keeper/ton_keeper.entitlements; - CURRENT_PROJECT_VERSION = 433; + CURRENT_PROJECT_VERSION = 600; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = CT523DK2KC; INFOPLIST_FILE = ton_keeper/SupportingFiles/Info.plist; @@ -1332,7 +1347,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 4.4.1; + MARKETING_VERSION = 4.7.1; OTHER_LDFLAGS = ( "$(inherited)", "-ObjC", diff --git a/packages/mobile/ios/ton_keeper/Application/AppDelegate.mm b/packages/mobile/ios/ton_keeper/Application/AppDelegate.mm index 00f08c4a3..7a6e5b293 100644 --- a/packages/mobile/ios/ton_keeper/Application/AppDelegate.mm +++ b/packages/mobile/ios/ton_keeper/Application/AppDelegate.mm @@ -1,5 +1,6 @@ #import "AppDelegate.h" +#import #import #import #import @@ -14,6 +15,9 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:( self.moduleName = @"ton_keeper"; + [[RCTI18nUtil sharedInstance] allowRTL:NO]; + [[RCTI18nUtil sharedInstance] forceRTL:NO]; + // UIView *rootView = [self.reactDelegate createRootViewWithBridge:bridge moduleName:@"ton_keeper" initialProperties:nil]; // rootView.backgroundColor = [UIColor whiteColor]; // UIViewController *reactViewController = [self.reactDelegate createRootViewController]; diff --git a/packages/mobile/ios/ton_keeper/Resources/Images.xcassets/AppIcon.appiconset/100.png b/packages/mobile/ios/ton_keeper/Resources/Images.xcassets/AppIcon.appiconset/100.png new file mode 100644 index 0000000000000000000000000000000000000000..34c66babfec723f3b2b01e651f47e01ef84b333b GIT binary patch literal 2909 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 ZGYTthoLiYGKBxaOz{1SRw942u{y#FkZ5sdp literal 0 HcmV?d00001 diff --git a/packages/mobile/ios/ton_keeper/Resources/Images.xcassets/AppIcon.appiconset/1024.png b/packages/mobile/ios/ton_keeper/Resources/Images.xcassets/AppIcon.appiconset/1024.png new file mode 100644 index 0000000000000000000000000000000000000000..2ed2166819aae6c53cbbbc5d5c0f49b607199390 GIT binary patch literal 34525 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 literal 0 HcmV?d00001 diff --git a/packages/mobile/ios/ton_keeper/Resources/Images.xcassets/AppIcon.appiconset/144.png b/packages/mobile/ios/ton_keeper/Resources/Images.xcassets/AppIcon.appiconset/144.png new file mode 100644 index 0000000000000000000000000000000000000000..b61d1a0c6722ab787062d250f19fc90dfc6f3013 GIT binary patch literal 4226 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 QPX&OnzL{RF&i%yy0lv~1zyJUM literal 0 HcmV?d00001 diff --git a/packages/mobile/ios/ton_keeper/Resources/Images.xcassets/AppIcon.appiconset/152.png b/packages/mobile/ios/ton_keeper/Resources/Images.xcassets/AppIcon.appiconset/152.png new file mode 100644 index 0000000000000000000000000000000000000000..5198cd1751224001e970426724eb3987eeefd913 GIT binary patch literal 4413 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 literal 0 HcmV?d00001 diff --git a/packages/mobile/ios/ton_keeper/Resources/Images.xcassets/AppIcon.appiconset/20.png b/packages/mobile/ios/ton_keeper/Resources/Images.xcassets/AppIcon.appiconset/20.png new file mode 100644 index 0000000000000000000000000000000000000000..ed03cbfe0818f77c7633056cfb6a8c8a8415e23c GIT binary patch literal 670 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 B4;cUe literal 0 HcmV?d00001 diff --git a/packages/mobile/ios/ton_keeper/Resources/Images.xcassets/AppIcon.appiconset/57.png b/packages/mobile/ios/ton_keeper/Resources/Images.xcassets/AppIcon.appiconset/57.png new file mode 100644 index 0000000000000000000000000000000000000000..edef81fed8cb4d985839376e099d96457f2b2598 GIT binary patch literal 1919 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 literal 0 HcmV?d00001 diff --git a/packages/mobile/ios/ton_keeper/Resources/Images.xcassets/AppIcon.appiconset/60.png b/packages/mobile/ios/ton_keeper/Resources/Images.xcassets/AppIcon.appiconset/60.png new file mode 100644 index 0000000000000000000000000000000000000000..a657b374c0f1bb86ea8a07bc02e906089537fc09 GIT binary patch literal 1879 zcmV-d2dMaoP)Px+5lKWrRA@u(np< zqL2C}KJY;F)hAyxCKyT7V0b|VEf6UZ(`v0)j20{<(6YU5cX#$SGv=I`9a_4V+1=S~ zQ+E;)2-}(Yo%5Y@zVqJ-4Xy14{zsQk1zzRZu4Z_ZCm7WXS2MiIB2bm|Dof}RoN9*4 zonb+gVCXtOMi509x>oK-F1mkZGAsySP%Vj6Gz7^T;Fr+i5y<{}2$Eug8|An4>H*6%5jwim>>ZGT0F|I zzLqvL?>>l@?js1aZ?ky>5r~cs)jBU7 zI9;EqB!dcy7xi0rVciR_qN(cu|x)9D*n?$fC>8Vdw@#j~9~9gH$+->4EPt z(R&)9;cGCGaT8p2r_PH9&QzaW`x&%;>uv~&1TCs^qoT}1NM|*RKn;?x#tlsJ*T6_> z2;J<%#E+*jGjIV(m+HJ=;B58T+Idf=p_|t&B{_q33``Ou zqtN;KDXiOj99lfaGuCXjM*bSj2bJsp*^n53C^4L#N&)4}*=Y$76c0DA z#Own^M~4s{yJ@;Ie?4Y?`vLR+^cR#}PDR1Tv;tX06&=S;pl-`fq}XH_+(6{ICU_dw zLH4gv|Oy+4BBPhUciJq7#kqJWKL4BqB8 zbiQ$l>nd&f4J`$TA_FscT@wd_!KveBlDl1>GDO+KU<62@1WZ3m0kG$|u4CfT=SYU8 z*o+jqa1;)lmUd(gt=)&Q{y-1cmTfL913~hkt%#&flD8Lwbe&sGm0p^HLibR|h7lS4 zgC$H-CP{OYNaBVL(d);|U@t<$ef)Q)jcQ@#6a}2V0Y!iFYbW8~v=w?Hy1+J)g#^!Q znx&K>YHknf-0HYieG(+WrlJ%$*_0}E<@CBpC8Kzdn7W6FD`z+u#a=}N^O#Dg@Nawy zn~$H&Zt$sb>ExHmHd=MwxPg6lBgKacRp05`2tyYi#?ajk|I-B=KKJars>54Br%^4PNLIU6v+ zDn1@R!K+^twFx<~n`HtfbMfd#WtoPo88(-MQZ}LG1AcU)+TZhVHfI8MX%kvj;7l78 zed{1d5;r#6X;E-o+Ju%JcNTG!{ngo$i}Tr zXhi_qCl=O56N$9xMLq5vC^OE{_kA%%K4znGl#RBc+jaP9qiFYrQZDu_s)L|KI!B$s?s;1 ze6X%B5BVNQ(oU&iG;Hr?CpQ20FA(YXJ<9ki4wbQ%4i*fmU$+;7d;za`hV2*P13mgz zWn{cC5S6Q8C*iNaXR5#z2=cN%qMG4l4Sxk5Ud?a?g1oGcsAhOs!(V}i{|6m6C28oH RlMVm?002ovPDHLkV1jxffTjQd literal 0 HcmV?d00001 diff --git a/packages/mobile/ios/ton_keeper/Resources/Images.xcassets/AppIcon.appiconset/72.png b/packages/mobile/ios/ton_keeper/Resources/Images.xcassets/AppIcon.appiconset/72.png new file mode 100644 index 0000000000000000000000000000000000000000..b5c6af0f608338d66aa7357a3901a38a98273e47 GIT binary patch literal 2184 zcmV;32zU31P)Px-LPAD8!i!+s3ct8Y3lA)DL42B?d zU%U~$8}CEcMS~1z;@~c%hW>+zZ+D?$o+Bw?Bcc`<)Peyr^hH1`<)IdH2zM{TlFK)t zZ*Vi(SFfW#LCs~26pOb}#v$|T*GTSq9i#i+MQQvbIgzqD~KWfy6WfE(RUMUi56Z3E@OH)M6Q0sQ@7a>yPcBYtVHaQaB1ZqM(!> z$Jm$eBRTXMrVj51G}5VdUvB}|uq3gnbLXJdG$rqqkN=CF094CiP zDWyV$!U)BpP>Ur@e7_s1o&M{TCxGVF7XWHL%hkv_*9n)?&oQ)zs-KZ!{_B)0K+Wh& zb6{*xEs#z%4Tz#aD`WvlM#q}<=-ap%JsWOzt}ot^veBvckQ#a&lZW?1S1XXCq*HZt z&KEC6!WoI7^V};KOu~)6cm`^gP8AAB4}XHx$8RA$ycb$A=Unq@*3@tW(SM?n&!A`B z^*HD8e?urS=;b0b65hEgl2;(+x(|st>6{SUF9}&japX7-zp@pRhdzT8iF1eE$k8w$ zdIoGl*203$cpXG8- z$hrY7aNxn`5j%4Ycg7SgR5?hiMGgd8#~1f^gz5oM+kRZS@rfmSY{@YfLr zQy4kM&m>?RQ6UATv6D#cezUn}xe@8wEFcKvqv9*h!>aqAgI*~c3!)#h`=#^cXd6eA z(s@SXI%TIXfBb~(ZwFDDNV3X_P{e#Dr5t&>5Ld6_sXsXVnn^9sB8XEhm&URi9%Tb+ z<-R56^afVsP-RgPXE@U-bF6W?z8pd&JI)YTS6J*!@zpg>r^z2b$M`oNxk@e10CAFE|1<+iQKf07r&6G__dx0HM=0JgQ>UoXu)a`5R8bl|fsCau#T@%F zdKOot+S!n4BFH}7Ju#B4!um3)Yt^@;_wcT z)f?p|dB(i__9u9k!m&v-+XJdj4A$wv(G$a|eeWRq`>?0c!Li;-0>t5 z7hJ_d_c>FoS}p{GsRD8$uqGU`6eFL%&BE@cr2DEt)tgf!RLb1*3_ScYyMCw+DvEhs zLx9X{l0(}K&7X)L+y7M!)|vH1m?FKRG?Okp1T>O)NZvY$oTD`B3| z411kowUI>S`s~fmpnYH+i|Cws)CeHzyObyj$=@)tXGhaU)QQab5<&c6a{T22qDxm` zV9Sf-_W>C0yI$FERD+VcUPC#3>i-l_t>gdK-RQsWp;(amI36*@y``OJ~{Ok0%SS<3_8|suy_3P z0`lIew-li1267V1Z{3QX^|Ks*JwQG>^_BxP!|^}74WW)2$8QVB7@w+z43ayyb6@49 zQ*S*$b2)xHKt4P5mIP$jE*r;ByB@V}9`s#Mj5q)Ma_TJ$$TFrhz#rJMjX#hgqe)vg zRX0x`Z&ph1)2X*EplYg3JARsJQ^!q!$mG&i(bxfhoq9_HG7PO?80`30Ujj8df$Y(P z{7FqAMc*av;Qe;5#-~nz%syv9I_bI#o8kCh;%%Pn(QlzwDoE~rg9rG{?x{Be!|s~J z1yptX6X+eh7kz{GVCwJzOwgYCp}oEh!5V?L=zy%n8jk-36p}~r=LfHvn}dzKia+PJ z1|aiun)vc_p=%ZXO2epZ-fZ2tLDw39Ou@Fcdlp-FYJs*6AQKWVt_32;g49}o+~k5! z*9L&*0UH1c0J+P(AcBGj3L?lIfgpn1@e4F4h@c>X+!2`P>c0V&Mh~>REY)QI0000< KMNUMnLSTYa0v$pC literal 0 HcmV?d00001 diff --git a/packages/mobile/ios/ton_keeper/Resources/Images.xcassets/AppIcon.appiconset/76.png b/packages/mobile/ios/ton_keeper/Resources/Images.xcassets/AppIcon.appiconset/76.png new file mode 100644 index 0000000000000000000000000000000000000000..c16be09c6f2281a2f975576e5fe28f8f2c33fcc9 GIT binary patch literal 2307 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 literal 0 HcmV?d00001 diff --git a/packages/mobile/ios/ton_keeper/Resources/Images.xcassets/AppIcon.appiconset/Contents.json b/packages/mobile/ios/ton_keeper/Resources/Images.xcassets/AppIcon.appiconset/Contents.json index 178ca5d1a..65b74d7ef 100644 --- a/packages/mobile/ios/ton_keeper/Resources/Images.xcassets/AppIcon.appiconset/Contents.json +++ b/packages/mobile/ios/ton_keeper/Resources/Images.xcassets/AppIcon.appiconset/Contents.json @@ -1,158 +1 @@ -{ - "images" : [ - { - "filename" : "icon-notification@2x.png", - "idiom" : "iphone", - "scale" : "2x", - "size" : "20x20" - }, - { - "filename" : "icon-notification@3x.png", - "idiom" : "iphone", - "scale" : "3x", - "size" : "20x20" - }, - { - "filename" : "icon-29@1x.png", - "idiom" : "iphone", - "scale" : "1x", - "size" : "29x29" - }, - { - "filename" : "icon-settings@2x.png", - "idiom" : "iphone", - "scale" : "2x", - "size" : "29x29" - }, - { - "filename" : "icon-settings@3x.png", - "idiom" : "iphone", - "scale" : "3x", - "size" : "29x29" - }, - { - "filename" : "icon-spotlight@2x.png", - "idiom" : "iphone", - "scale" : "2x", - "size" : "40x40" - }, - { - "filename" : "icon-spotlight@3x.png", - "idiom" : "iphone", - "scale" : "3x", - "size" : "40x40" - }, - { - "filename" : "icon-57@1x.png", - "idiom" : "iphone", - "scale" : "1x", - "size" : "57x57" - }, - { - "filename" : "icon-57@2x.png", - "idiom" : "iphone", - "scale" : "2x", - "size" : "57x57" - }, - { - "filename" : "icon-app@2x.png", - "idiom" : "iphone", - "scale" : "2x", - "size" : "60x60" - }, - { - "filename" : "icon-app@3x.png", - "idiom" : "iphone", - "scale" : "3x", - "size" : "60x60" - }, - { - "filename" : "icon-20@1x.png", - "idiom" : "ipad", - "scale" : "1x", - "size" : "20x20" - }, - { - "filename" : "icon-20@2x.png", - "idiom" : "ipad", - "scale" : "2x", - "size" : "20x20" - }, - { - "filename" : "icon-29@1x.png", - "idiom" : "ipad", - "scale" : "1x", - "size" : "29x29" - }, - { - "filename" : "icon-29@2x.png", - "idiom" : "ipad", - "scale" : "2x", - "size" : "29x29" - }, - { - "filename" : "icon-40@1x.png", - "idiom" : "ipad", - "scale" : "1x", - "size" : "40x40" - }, - { - "filename" : "icon-40@2x.png", - "idiom" : "ipad", - "scale" : "2x", - "size" : "40x40" - }, - { - "filename" : "icon-50@1x.png", - "idiom" : "ipad", - "scale" : "1x", - "size" : "50x50" - }, - { - "filename" : "icon-50@2x.png", - "idiom" : "ipad", - "scale" : "2x", - "size" : "50x50" - }, - { - "filename" : "icon-72@1x.png", - "idiom" : "ipad", - "scale" : "1x", - "size" : "72x72" - }, - { - "filename" : "icon-72@2x.png", - "idiom" : "ipad", - "scale" : "2x", - "size" : "72x72" - }, - { - "filename" : "icon-76@1x.png", - "idiom" : "ipad", - "scale" : "1x", - "size" : "76x76" - }, - { - "filename" : "icon-76@2x.png", - "idiom" : "ipad", - "scale" : "2x", - "size" : "76x76" - }, - { - "filename" : "icon-83.5@2x.png", - "idiom" : "ipad", - "scale" : "2x", - "size" : "83.5x83.5" - }, - { - "filename" : "icon-1024@1x.png", - "idiom" : "ios-marketing", - "scale" : "1x", - "size" : "1024x1024" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} +{"images":[{"size":"60x60","expected-size":"180","filename":"180.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"40x40","expected-size":"80","filename":"80.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"40x40","expected-size":"120","filename":"120.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"60x60","expected-size":"120","filename":"120.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"57x57","expected-size":"57","filename":"57.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"1x"},{"size":"29x29","expected-size":"58","filename":"58.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"29x29","expected-size":"29","filename":"29.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"1x"},{"size":"29x29","expected-size":"87","filename":"87.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"57x57","expected-size":"114","filename":"114.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"20x20","expected-size":"40","filename":"40.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"20x20","expected-size":"60","filename":"60.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"1024x1024","filename":"1024.png","expected-size":"1024","idiom":"ios-marketing","folder":"Assets.xcassets/AppIcon.appiconset/","scale":"1x"},{"size":"40x40","expected-size":"80","filename":"80.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"72x72","expected-size":"72","filename":"72.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"1x"},{"size":"76x76","expected-size":"152","filename":"152.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"50x50","expected-size":"100","filename":"100.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"29x29","expected-size":"58","filename":"58.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"76x76","expected-size":"76","filename":"76.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"1x"},{"size":"29x29","expected-size":"29","filename":"29.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"1x"},{"size":"50x50","expected-size":"50","filename":"50.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"1x"},{"size":"72x72","expected-size":"144","filename":"144.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"40x40","expected-size":"40","filename":"40.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"1x"},{"size":"83.5x83.5","expected-size":"167","filename":"167.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"20x20","expected-size":"20","filename":"20.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"1x"},{"size":"20x20","expected-size":"40","filename":"40.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"}]} \ No newline at end of file diff --git a/packages/mobile/ios/ton_keeper/Resources/Images.xcassets/AppIcon.appiconset/icon-1024@1x.png b/packages/mobile/ios/ton_keeper/Resources/Images.xcassets/AppIcon.appiconset/icon-1024@1x.png deleted file mode 100644 index cc22d554fc5cd55ae16b2a1e7865d5fb1c04e7aa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 66093 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 diff --git a/packages/mobile/ios/ton_keeper/Resources/Images.xcassets/AppIcon.appiconset/icon-20@1x.png b/packages/mobile/ios/ton_keeper/Resources/Images.xcassets/AppIcon.appiconset/icon-20@1x.png deleted file mode 100644 index 3b0ba2cca0f5f47a6b4a133cacfbf472fd9867ef..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 609 zcmV-n0-pVeP)D&dX~PK^(yG`5T(;>}z&5+3X{aePpxUWZi77nuG=q z(p&MM(340JBtqlsW+)!4k5Y^XioK*I)E5*=Mern5u%b|pp1g^m2vskovT4#LX>wWk zFbp%_-!On+mB7F-8w(5!qxfKnrhx%y7hND$Vc783_NH$JAeZb!@;c;Hf)YbR2=W@l zYG?=%lnAlfyy}WU>-jw>5h5@_3dkrV&(3=HHodza5@)JN&l4~bA~58dpK1dn)>=4~ z7HwtXl2ctTG=7!VcE?wD##eVrYrBQUPpA4ynJ80&MRMx?2q1ag7d9Z5WRqi~wXNdI zf5m6pzNg#8=YNVX{uWk#pQwM&-dJa1F62})Z0t<|Dr~d}CZwcjVJbJjnVbJO_V{al z>BsTKZ|<$P)|G}?e!^!@K_*5;%>5BSM^XobG{vdL^aJnymV5i1d*{7XSxQ}eWL;j4 zPhO{ZjTX$#9?+5W0l}hq10|1TXJ2~@pWV5Qbfuo2sTtVTYWKu|jgIsZGc?vx7an>GpGK~(Bqpyhyuph0;S%_# zJ){g1F|nR^ZoVa_J{mD-AZ?-=< vpaGoh45YZe!2#gpBY}a|^ML@YDTx@_KfY=@z{A_kCRqHzy_%j zNNEuvVNr!RrD@nyg`jkS?1ZGPPz!cawRU5-ZX!`ZVbcxDqmUAUgkVvGgv3LHCN6oj z66iWXSS3zuZ$|nSqpN#=-#KRv(2*8R06NO52|zP3LRmEdAe2oL07Ba|0ca?;YXU$S zN8PY4jCW(4h;c5A_h78Efe=v7ZD&~~BDC$0jzb6mrL2c^EJ9g#&e3`*0F}A_qoPI5 zjWSLhX4D}(Aq0eHPXylnr1{)k>x)CAJ)%PmI@EyDc6f~wE8bRqI4i#aEVC-*k@9YY zw8C80rEfVZow(fc!FTcER>#ca_{?^EX1gu@qxa3MQ9X_uWF2ZiNXyPEjC1W14lveL z1rep~I#iDk0wZ@iyGE4JMfK)J=k34aGuvumJ(gcr3+rlrJzm`E{1_g#Tv&1UjB~*k zP|Bu5c$BhYtXM4{RI_5VgR+G!&nE(BZ%42G5}$pd7B^$p|BU5UW77|z^2Q@bp%j~b z2oH0s>dn96vrnSgKbp_owI4l0yP_E5sOAPcd+{{92O$JQ`^(Yn@A2YhytttjN@ZDT z*Y`A~@`Vy~-`a}h*V@t#%!hjsLV#<(mjd9NxHr5TBQ361laUMGgwHRxU->zfU)wFB zQdxkd#BXiIXCBA$Yps{Q4-S9o9n3jiI*YN+s^OdnaNfN)dJoPzDSIUN?&r$bm!afb zc>Jr@OFukw^;fk}iWfIwbHxq#zi&Kh&-@g6?+f2s1*tzR^{3@ThLJij#sNdBwUz7OR@Movun zhNev&-6(0r_3}R9fZ#p*p_eeyD!w)rxwxVv=akWV%IKn!nhz!CmDHkdC@=M0mflFq z15@(JD{>;^9n3nrMsUu3KrHV8gkIVYgN$<`4F~>Nczg-=J-S%to|2sR4rX_i15@(A zB>d>lFdh|WorFQ&KN%b#)=PxZi;-57`bv1>euWtJ?H$a?iOI5c3J&>n&fM9JQFg-M zJunu)(hh<&`f$eK?iq_*{8mZM?;(am&N+{b5C%A;S^%U$J{Y5y;61c092~y$-@q%y zC$C8T8P=l`oJbmdwPXU);H?Q$6HeRVz~5gArOHhw{}q>a%G}Y7GfvXvuQ3q70&B$N z$7#C=2fhLaUhY`xPs2?L57} zW%Ada2H@?Qp@ksHxrM_$;fecDeCnF_R8~0hI>}3v5U48()O8zT4iKE^?wtrveC->` zJG(}9hEXqoHZ|3UIY1lztQZZxbH{gjnv*&yqYoa|nFDBZQ{4zbk`*mSdacj(k(@{i z!3IJAEVcoJV7YLAgU`)@M#a$G3~5-*2tiE%8j71W0Wg+kO#qA~qzQnvgfszYC$=h@ Z@DBo4pbu6~QYioc002ovPDHLkV1l|$GAaN7 diff --git a/packages/mobile/ios/ton_keeper/Resources/Images.xcassets/AppIcon.appiconset/icon-29@1x.png b/packages/mobile/ios/ton_keeper/Resources/Images.xcassets/AppIcon.appiconset/icon-29@1x.png deleted file mode 100644 index 9814291175148ec2a3c198994eee011b39a2e7b0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 876 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 FV1h$FbGZNj diff --git a/packages/mobile/ios/ton_keeper/Resources/Images.xcassets/AppIcon.appiconset/icon-40@1x.png b/packages/mobile/ios/ton_keeper/Resources/Images.xcassets/AppIcon.appiconset/icon-40@1x.png deleted file mode 100644 index 01a0192e8dcd5d7b8c7ff238706e6b9dc50d37cb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1211 zcmV;s1VsCZP)DTx@_KfY=@z{A_kCRqHzy_%j zNNEuvVNr!RrD@nyg`jkS?1ZGPPz!cawRU5-ZX!`ZVbcxDqmUAUgkVvGgv3LHCN6oj z66iWXSS3zuZ$|nSqpN#=-#KRv(2*8R06NO52|zP3LRmEdAe2oL07Ba|0ca?;YXU$S zN8PY4jCW(4h;c5A_h78Efe=v7ZD&~~BDC$0jzb6mrL2c^EJ9g#&e3`*0F}A_qoPI5 zjWSLhX4D}(Aq0eHPXylnr1{)k>x)CAJ)%PmI@EyDc6f~wE8bRqI4i#aEVC-*k@9YY zw8C80rEfVZow(fc!FTcER>#ca_{?^EX1gu@qxa3MQ9X_uWF2ZiNXyPEjC1W14lveL z1rep~I#iDk0wZ@iyGE4JMfK)J=k34aGuvumJ(gcr3+rlrJzm`E{1_g#Tv&1UjB~*k zP|Bu5c$BhYtXM4{RI_5VgR+G!&nE(BZ%42G5}$pd7B^$p|BU5UW77|z^2Q@bp%j~b z2oH0s>dn96vrnSgKbp_owI4l0yP_E5sOAPcd+{{92O$JQ`^(Yn@A2YhytttjN@ZDT z*Y`A~@`Vy~-`a}h*V@t#%!hjsLV#<(mjd9NxHr5TBQ361laUMGgwHRxU->zfU)wFB zQdxkd#BXiIXCBA$Yps{Q4-S9o9n3jiI*YN+s^OdnaNfN)dJoPzDSIUN?&r$bm!afb zc>Jr@OFukw^;fk}iWfIwbHxq#zi&Kh&-@g6?+f2s1*tzR^{3@ThLJij#sNdBwUz7OR@Movun zhNev&-6(0r_3}R9fZ#p*p_eeyD!w)rxwxVv=akWV%IKn!nhz!CmDHkdC@=M0mflFq z15@(JD{>;^9n3nrMsUu3KrHV8gkIVYgN$<`4F~>Nczg-=J-S%to|2sR4rX_i15@(A zB>d>lFdh|WorFQ&KN%b#)=PxZi;-57`bv1>euWtJ?H$a?iOI5c3J&>n&fM9JQFg-M zJunu)(hh<&`f$eK?iq_*{8mZM?;(am&N+{b5C%A;S^%U$J{Y5y;61c092~y$-@q%y zC$C8T8P=l`oJbmdwPXU);H?Q$6HeRVz~5gArOHhw{}q>a%G}Y7GfvXvuQ3q70&B$N z$7#C=2fhLaUhY`xPs2?L57} zW%Ada2H@?Qp@ksHxrM_$;fecDeCnF_R8~0hI>}3v5U48()O8zT4iKE^?wtrveC->` zJG(}9hEXqoHZ|3UIY1lztQZZxbH{gjnv*&yqYoa|nFDBZQ{4zbk`*mSdacj(k(@{i z!3IJAEVcoJV7YLAgU`)@M#a$G3~5-*2tiE%8j71W0Wg+kO#qA~qzQnvgfszYC$=h@ Z@DBo4pbu6~QYioc002ovPDHLkV1l|$GAaN7 diff --git a/packages/mobile/ios/ton_keeper/Resources/Images.xcassets/AppIcon.appiconset/icon-40@2x.png b/packages/mobile/ios/ton_keeper/Resources/Images.xcassets/AppIcon.appiconset/icon-40@2x.png deleted file mode 100644 index 95d1f594fbc2cbfda8835da5c097a881c80b0dec..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2282 zcmVc3#>Ve6JQA#()WE=1^+ewF97xQv+BRY7$|6q}_m+5URu!{*I^i^|Ckqnml*6`mM=C z-vspR&Zy2r{rhNQ;0}CS|DEXmk6mkaGrpBZ!fHS`gep!SS(`eUoP{ygjhRG*DmEaT z5jE59O2>D97HWJy-hH!ra56D)C*D0e-O~B!9*uX6K=U`hsvexI>ii=3%U|2Sl_HD7 zMtEc%r5%__#8`Lk!mg}vVQ1y_29ka;G`P$4s+}|jFSuqZzOCeD`K$W*_G|eN|<#ca2icV_t3IKe@mXb z3#U=3D3z(n-5kKHiptBkl{Fq%->Stwx^+7iVP$M=2_ zX&y@Sj#XcnObmP#>%N&~OBXQD95(sp-RcXI@$Q?USKhUMdmCopuA4K+F4ez~N*7`|h$BlT;Xrk{nEDIKzyLNi{ZoxD5z@1pvNO|ig!R^0Mwzf+f z50mangs{WWorN&Qy0gI~B81ITdnkHfIMnz-c+XI@Ww`vCcw6 z3Ehptv8+=rs zu*!0jELPi_W!V7sDqC<~Mj4j~gMAHpLgCBdy(-Wgu0r&{a0QI|=kcEDsLzbf7bisf=yiac{#J>#ACmcHq2}OU)pc4`ZAMrJba+Jh=0n$i6EN z^f@iBHaygHDZKacve*Ayp+-H?H=cl=6N$cws*ca1Er0O;d{EwUQrdI`s;1iRw=_-N z31#bP_nO@pFQ>;xj>4Xt;AI523d^659{kfo!qeK&Jk$iuBm1tD9sWB!yBz*U>5ktj zTRWtUN2E>1GSQu-I)zeyit(;MDF?>MITj3XlP6a=8Ka$^^{vr^*TPL7&gHC?oxGphIjKAD{mB_?%?6Zm;GCR$wE*Wlxxz~r?WCRM!Ci9(dk*tZ z`sYgma5atf|+9uqr;3iRnQaLCrX@FTl39GPtU9{zoQ`2i9;nXR3vRTl1OB?H3 zfl&^emo+IBXd}!?I4@(g!&CbP+{{gvG!TZ9`K+yGJw}z_yrNZ|Kucl9MY$@1JKkO( z*k4Cj4YO;_ZZ*sv&5GnS6o$d}qJ$kz>;n&8ie%T}R2$=6fzeJ4+l#BhvX+v%+iaM@;MmJDh4`y~}ahNtnHwmj~1()EcuFr=97W$2lhU zJi_WVGn~w{L$%euHjpo2PGUTXxhM9cgwvN9Ynn)Fkl^!TOF&_>k{1LYMipCE{CM`w z5 zJdC?)_RZtN!gVKeFpRujpNU$O)1oB-EfGe6;1V6z^j5}7k>vx%8-xr>~ z0c=)Py?HOjQUGC#kF*AG+F@JyLL_}DGA&$}iL}XILzqNT@INWS7PZeN5Bya`-ON+M z>J8xP4RGrUfx=V*NQ=J^z*CgWi1FagwjsqHP4FlF%<4k1XGL}`6%c<*JU;f%dz1Idb%HCV_R z044Yfhc!U*GAqV|+b@di_7(K2g$#fa0tLldO0aGrxrUEClWA{Z3jl={h6G7oA zIHW58V+-jDu;>*m35S+C99oKWD3?7P))j!UhjqpO0ru2{j5#$WRR91007*qoM6N<$ Ef(Scv*Z=?k diff --git a/packages/mobile/ios/ton_keeper/Resources/Images.xcassets/AppIcon.appiconset/icon-50@1x.png b/packages/mobile/ios/ton_keeper/Resources/Images.xcassets/AppIcon.appiconset/icon-50@1x.png deleted file mode 100644 index 4030275283fab218df8bcf091e29cb83788ae990..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1520 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 N002ovPDHLkV1n6TrHudp diff --git a/packages/mobile/ios/ton_keeper/Resources/Images.xcassets/AppIcon.appiconset/icon-57@1x.png b/packages/mobile/ios/ton_keeper/Resources/Images.xcassets/AppIcon.appiconset/icon-57@1x.png deleted file mode 100644 index 9fa8503f2bf93c6e596be0ad686b44a1f32ac89d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1737 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 diff --git a/packages/mobile/ios/ton_keeper/Resources/Images.xcassets/AppIcon.appiconset/icon-72@2x.png b/packages/mobile/ios/ton_keeper/Resources/Images.xcassets/AppIcon.appiconset/icon-72@2x.png deleted file mode 100644 index 2d45318d1f80d8fe7f17b40a0a993712d78ffe4f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4327 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 Dc!v{@ diff --git a/packages/mobile/ios/ton_keeper/Resources/Images.xcassets/AppIcon.appiconset/icon-76@2x.png b/packages/mobile/ios/ton_keeper/Resources/Images.xcassets/AppIcon.appiconset/icon-76@2x.png deleted file mode 100644 index 3d097c8b64aad230129a8b03549972973f3352da..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4506 zcmYLNcQ~6--!@{VYEzq9wW(Dxqej#yv3F{e*ri5e3oSJoyHsl>R9eIyrJABvYZq05 zqV_DU?MuJD*ZW@AInVjyJm)v=bN|kD?vrF{qEAQ5NlQXPLT3QcffC2(e>*i9@ta%J z6ha(m0wA_QBqTuQznzpMzu+1P38RsL4%i~BV5{IXh-)&qXHPT0*=EyaPeeEFXoLLRf@JJ$_w28QV?xl7^lQN> zE93ZmWKpLE8H)4|iH)#BVE65ZyNx2qAIPFuA4@t&xrp{LcvuTmBR{P5gPu=-3(m3D zHK;|Nq!|(KgLX$VtCG_RUF0v#xF79P>Jb#3(30W;-JQ*-KT@L3s+dlZ1&@qot!~fZiq4ZBhl|eEF~J~D|KdT zI#LY`wLh_Hxt%kMw>*5O%O~Z_-VXIE&99L<9T7&F)|(x_4(z<~XL~fc5&+FKTqB0s ziu)I;7}pwP5R+sP#h`$a-n^oKI_lK#*R#|6-#jrDC|$3TF`9@#tWW2^tkA@)+_*w0 zIU#2Yg6@Jm-3{;(D^37ZX~dfr!h0DEeQKTx9>bI7hjmkd_4oXYv;MAr=;~1v+r_7G zM+q9N$EjXFUz)=EG_;gX=0ZTxXdPexbmI32XkbvQXv%zBhe;6qW@WdF-%u;E%p0c| zT`i8ud{OjSN=nVuyGiq8wwHcxGvZ=p(BId?agR;gv)D*zMcfVRg=Rw4&tp<}Gin;@ z1JHQ*`bA^&*3`7?<%?IXg?afyMzce08Xen9{+-|TrILEYN}GtDJb~PA*gC!n6q=Q# zIN9xv#qkwn$}-lc0?{)Yp`mn+#>RxcHo3z0yOx0wB63QZ+=T{dU(kbd18o|Gtw&$; zJ3ID$Ic|oi>8Hv4R0N~Taz32mf&_xJ#=YVg_ zJVsnaI?|E*u*lKFQS8l)tOV7LH{k10bye)bl%cTfL*CVlDHA*xj2hZ zmNJ?^(7q-{@ZCO6yCqxud&*<2;*{9>lI)X4Hj?U;Gnwm>8KkXTHV3*^50{R7PDUs5 zDvk-(QS1b{`^LUy#uscKA+kyq+b4U@K2lI>2;ML+0*(KsO$+c{=*n8zTUn$cP&7rj zYNSGOw=S_GM?NkQB9Ww$%8`3_z0(|isjK*;sOjNCa1Ils80P%QWf_$?G>}V#&zBL7 z*T1P0cXN%$tVj7&Q1c8vm$&g@XLJ&Z5blv!c2>NVxhk}l9NkYu@>=0m#KN4zTvSeQ zwZIMFxK3Pd4FI0m(Uxiolc90?wB!Nc!-O)#r&9vY2Vj?b4f>a&!FwAA-b@`Y z`f8@l14?d=a46J~jT~T3O_#TCc^mq=d?t;P%}6okye0{)dE%MmMhV>=5#e(WU_xt+ zS~(d#Qrb+q%?iqW6se)|NK_6X_~7@aI}Q&^*gDk1nMFo|f{0j+Cxn|W`_1}qhNZah z0K`lwJtbvD3TLzs%lt=%E9ye-0ub;{VtrH)f9pnN*HrGaHkHCh=TYltvrAo-<4Z4z zZK>ru9r{+f`F5R%U%AK*x_n!6epi#=K-ZfW}9H^gny-tlxUb?eu8J~C5=Ww{e^A$iO=ueWqDarPYMJmnJev?r5 zxg?@R#fR=+k|!r#o@vTrf`V{Uxnx52C5x|B>8V$sD?1{3{=d)0qIE+%cup?f{AkSW zj$OpZrthYid3y;~gbqWuuu3RYE-7?}1gXCNX6pS~Rq-3g+tx0pWv-{vJ@jpJzedX= zVJ%JjTU@pa1&+_-`hsA^HGJnc&o-N582>1x0qDgC#N+*SZ7W zM7|SQ`oJ2Yf!Sz9kCtJs7oFzJsYqe1vBR_WE?c+aaKhEd%dF*RS1+; zre!ltcx-ZhRM{Zdk#@ey4l5?_VYgXziPw4{bnqEIR#nW6kDOQYCeAF8RNEV z`dl@w&Da5S7XEY};dG+Vx>9(1h#$z)Ywu|r-}3Ovj+(cj|6^4FHbz5;T&%+J!Zz#1 zt}oBJ#y$-eR&60_!Myb%jW4Wv1#`KrdZ+?>uCtM2Cu-JLc#rjgjLHM=*`&FysI%nV zI#<&KzuRoBvkekCL%Wu*y!uoi$;U;wtj@o_mqH z6{Von``h0IG<_Tj+{t0T<|E<7IPQis^ePd6Gre>gwm~6UJSnr;_ItG_;}R}v+dTz3 z$l@QFANouob9%EoZQNzw0PT`^#AAJ)e!lK|P+K`*g;TWMc6V#x1G_GKW|sw;5Y{3O ze{CaoN)yzu+El0nD;1zsl|^J^uZ3<~dKcX&ix%tK&it9F$kkF%47Z(*!u!kh{OGfx zLH4Mt?ZX$P!$8Npgq$i1ecv@HmW_DPX8}*lDES3skSrSqzUnbo8hw&s?wOvOJ(t9y zy30>t?Nf}o@wl-t#DB_u#Nq`I>Q6hLYwMQcbeo4R#M8{{aY#zZvW_~#owAby375+U zgOdZhyzRDKd|D*%tMj>HMNI%Wn}g1>G^T{xTDf1LiDH% z$?LuWC$H3o&wq$IO1y6$IORb+q4R`&@#H~><94f1EiFsCH6Yo@^ z3F)&Kd*NASOq_B>sUU;###No8u68!yt9Qy;^<@mrrDCp3*t?`w=OQMVv?;l&k@)-8 zG*GOCepobFp4V&M2MmT=IG+gdtzl|ZFp~vN+CI=cB3pfGbDiii82F{-Fh}+Rmqh`m z>9-~((Qy2VTW<*O^ob;p0f9BGC=oXr9IeSIig2e}GmaW$b+)8edgp>tF;_fcM&Ntw zZXc0Do%Si=G_{Sgt^wbe-gq*)S<~KhuDI#I&VL8o%D$n8E84qRWqY zwf?yMtxAk08J4s{nWC9WeQNC!QcXbWGBq~dX?I}80?2=x7FCIqtN)h4MLg@hD0{)O zmf;Bb&Ud!nsuu&*+O(u_8V;W=1YyG&Os`}w*c)+|H4l94G~o@MkT;zm(vdmVKkzFQ zf8U!d?^G{a4y9d&jr9Q-+MmYJhO`RsQ(;ytl;sLsj4U&*-Vdbcn;6{nTNAOi&Zc}( z$?5jKyx&ge%-XJ$g^Fn+sGwZbE;5cw;_1G<1;5l3pFA8RJ*L`*jH|;9f-{`I!v#8J1;*4!Z}ZO zQZNCe$eJ$u7Pi?Xe#0b?pViA-Ct+-R&Lg*h8H&jf_M!Fr=74OipZ*rH6(_7-SPIk@ zZH{`$iu&(^pLZzjN!{=ksTmcG64sz2d5hXx`tgcZoA?}ZF-z>T9o^KKG)|52>7xzo zxgZPuC~OBN1tzpp&Mt|cDOWCQUDa5v(8hRGQ=la=5vaxxq+rR)Y)Ero!iD2KOMU^K zt-LP?yf;pN*Ex>zCOcWUj?B^7UAo*=yflTatfj;ZkNFde^mHXz8>DboIO0w{^;7Uq ziLbEy27o0DyG{lt&p{mMB7e{}QvWSL(V6qfU)$4O+>Z>ONb5<}>K{TW!o%<#5)RZ* zgvlWogXOKb;uhZ!9~OF-c){2se}r_*O0P~l?@4jVCqkS$R?JxeL|1msC)UOdy|Np= zej`R_?!3vP5vLtfr@u$vP&N9X@oGaK@4W>%sfxIlIa*K2dx*?XF{Hkv=Hd^IQ;5M= z>E2~*WB(qiQh%=Cw)7#9zl`oDzsIwAKDTzv;wF!{g*S^|AjUKqHn%)1U#>k=Q0vin z%_wS+v0Jl^5`y91;^8*RF~xtPk7OaJ{Iu=pfdLozEIZNUg=QFTGdJAsbE~iwTYRCc zjrI*5L2MQRJS|vInyHB3$*Zh<7L}`ba!X(Iuj<26!u21)y;pp;j`T-i6+CDjo4}B- zp!H6`=!GeSoCE()Zx;BVZ~xL?WP$G#8dNHOX?U-c-{Ybi<1eL##@V}PpB2#(Uk@r* z7sXS)w1ll>lvYN-vwN(TEBbXC`hQu^*TU`+MZftT_$az7#rcsaZ=eA2v4zAy*F>jE H8y5F}7;K%* diff --git a/packages/mobile/ios/ton_keeper/Resources/Images.xcassets/AppIcon.appiconset/icon-83.5@2x.png b/packages/mobile/ios/ton_keeper/Resources/Images.xcassets/AppIcon.appiconset/icon-83.5@2x.png deleted file mode 100644 index 70439d31eca28ff9e4ab33cfc57b913462e4693a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4939 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 OV=yrnCnV15 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 z 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

Xc*S-2&1G zkG*|4+jb5Hv7qL+yzT)0aW%@3n?w#e@Ps6lL%yo&6AkSqdngP+kvGfFG4Z)+lXw1W z_v`s_1TUTlxsxGK_54>pK`)*C0cnUqWF95(MqLqlSSSW{+O?e!inpo?IfORC4ZAt9 zrHw*Q3ov7ZglN`(g8)#WE1UP6yo|IE9|^`vk57?v(=($MzuCi*UVa$-&L#ACjgu&M%9*lSPo>4#Pb`3*fqnZNXNygCf7pOArP>~tzx>l- zy2)*H0kmil3r9}KUB|z>Vt!RqG|{rbd*REUUV$tgFm?`Y6<}d8$zUV_&x> zq#V-nzPD|s&_{T|F!i~^;o$Yu3e+w6>_nH-{Xp4B5U2If$xL4l6?z zzVpZ5x%?Vvm+kE0+7*X5mxphYb-i;4a>rkJ`vPz~_FEbBpzm4$D$_vOP>0pqtrULG zoP<{GB5?tuj2trgIX-^qz9qDfC9Dt<1XVK`$qJo!>x%;CzWu=D9FLHm7Nye)X`zFJ zhFU%mRbdE0z7NTKnM)xg9KGa!g2PiTPna=GtE8VWU&-l0WvNb``eWORk3KZ*xW7(k z-uBnlF$xa05*yp#TR^%tr$(|4@celF;=^BG9@L!t=xe6)>}eBu=8R&FoI|ryyZ(oj zQI{9*=JEq{Rm|DlYSK1zObj4o?>lmnIXomwv}7;jGz58=J)~eJ5Q+yTZQG8cINqc7QE$DODxo3>+^_ zJ$~u-h&`=(_Ve$jH&2^He$^dKHt13iif8L^6ty1BS(7s`t~@>edtwPgZhym@*V7U9 zk+mM>wPnvA-C0L4e;HpEI9bt-y1MvePVjFJw(`qQOku^BJ~c7t!63+elCLz5#FyQIo ziQAHdVlFbAeNh##9j+eFPbVICy!EH2@T@!hD|n zy+_tK`$u29oM*2S?DrlRAB$PNzO3Kv=@N%SY0vr=-wHpuvYsE$G4gVL96}00ow$gK ze;96dq)KVr)g@ z0Jai&%c@*oam2bR!(MO8U*5=j?|*0jgQe6CIBf+1kP@TKqM*mGO5tO3 zEW7QFJC`uz%#XceZ5LwVk>hZ=@yo2-b;(Ot=-gMoFr6RYM=vk$R~(w94I+YbLODf{ z6YWusrui=eyu3W#ZndO7d+zrV?n!Oz-~O&Phm!h^!H4UUV4019?>B@TR3*vS#rC#G z<~XKauX{swCcCB8M>4W}8DRd_#A~L|qf1GcZ3MW60DaIHz}xi_*{}RFn!P&Q)#)sI z{-<&F6pEbu=R&eWj{PFL{=+V7N8U$rPrO*{>`^w>9+ z7m%u?-I1KX9`{|%`CYjL&)Q63;ELO<#)M9MJIDHQHo|idkvsALm5%X;snrDp`>(88 zarDC?Y7T5G(aZDW?J0Kl;1jZk z)?*~%Y8DF6kIg&w~6G5fs! zg3j&tyqVtk5B@z-fW7Z9`+1L`BgBVokUBFfK7uf|bGYLUI`h_dOaOn~6poy+6`)nr z>$%?i{k$oxIQP&O+W9dk-r$w)2Ul4*dMBxJpn&cbIv&HsUIbYw(F&J#cQzi&i)*hl>6K@m zn9gHAOLzbDFOv%?K-SD^ajKY!o=xpjgn=o+0x>=481p9X!neQH&a+=jcTV7@JI)y9 zrvI(y$M@0s3CQ#Lck^c7HxBpO2MKmU9y9bups&tyay@D7%*?EzhW4icn?n7ezMwnl zY8mi!4~auNRc#9UBGvjC-98Bd{~fnZ4$EpwT*lh{+36VX z%#Z!_v{8HwiON_AVOnsq#qbmw7P5A%JAQL%4=q&d>SpuzAfNvJ6pnm<2}5qb_bqhw z;tSJJ<0qH!0(6l{;2}pmN@l-Zrc+EqrvnzzXFd==yJdDDupw{!p1Jz7&aV z;^vCm|7<0)PWF78qWIGZX$Bw$>M4NF@|zV zUkvsGkOYaNv!ks%7ryf#QRqXTC+ibsX6A4Y`UOfN?kTk9ZwtVS*|q$)<%#yg*GYhO z6R-ap7~rzDX?yGo4888wAK|(J5lWN4{KR*sP2y+Cchq9<2sjF$H`O+2-0Nf644y-$ z@3m(*QXN=vu$g1Cz0Yi61_wbljeV@H;zoceF#IZ~PS|AP#(3TkUVi#2(W-RuR7v2vp^|E$@BHf97vjoc;WV{HQ4qNS9iNsh*G+Ip-xa8ap~d?ara{ zton>BncP>uxb(U0Pl9?JmSh+c_ogJw-3;cJ`&X)lxDh`mC6UFf4S-hSo6fN&=VCkJ z2Vc?S-BxqZ!utlxF$d|M%5`#Ng!U~pcr|^@@9@rVef?HSaf=Lt7N9gz0u}wXB zfPeZ_vlvSr0AiS*RvK5yoTin}>BU46HQTYZswtDiu!Cv5s;8UdiP932UwHEb zAIeI)WES|XP$DC5Ev{pc#LOISxN^a`s%Hh6>EhYXeQIX~ic>YYx_8+TOGkVF%YTl8PiWDkJSdOO|4HFsDn8e``5&9x=ZE z6{P;3jc}t{jT1|a(7ZD`TvQ>9zS!N*GMmSCsK9I(7$HGk;@TBDr+^u$iUZXc#AQku zU3azTzV;cqG@X2`8EotfpIX-X7Qf@y^YJJx+bxdP$M2+g@U5y>xAsgM&$|Jn#OjnP z&33$)^s#M1UwYwLy7JUx3plLjl=!(w_O`A5ITCF1h{;6M)Q}1#>)f0a{9Cn+ShYvc2q<26A`x|ZL4X|>u1jFDay05ZOb1HvT2eN?|()?|@YH zL>LH9D@BKJ;#p?Yj_5D=M}1t`ku!K;RM!((7|*5Qzhm0;th;^`J93J4NdNzGf9|2r zEPwbWr2xpzly0?&OLyfFY~{`Y4tRYCaUc)93*Wzvo7$y~!$F&kG9APW`B&l0*>j8F zvIn1d6OoN|swFheY4~){5Q@a1TA9o)4s{Q%ZCAnHW)Z1Ysqw9NW4Tlz4!EVZs(jHW z>9hkE)@T0w4?WF6*x-(~)cH&9oUNfY?sj0an;a;mx4~ZLbl1ufb4TMoVo$w%XYx6%ry3XH@VjfRCCv_sKsPJx*3XOJCpIQtR#bPP$JMpO!yf0`gEMF& z1xC zFqhall%;yxfGxoaL#7P9o0!NCTQg{WYUfYYn$9Wshv#5E3_Z3^m{1mc^!+2~F@i*e&Lq=(k*iw+iQh{t$5F3It7#*Wk zY=CyfLHDZ3JJEX4Lyx|p>i~@pw&T;b^w^o~yN67LY;%?#AL})F2f}*plh`>NmPymp zbvq|}aSAma`zG|+L&SqwbX+QzavzQ;B4C>iEVD&sh=igw>nnKxV~8g~uBXGM^96hR zZ#U#-X}HX2VFDNqvgFt;D%C*2BFXJ?7|m_0YQgCLlliqhQDqsh6lTVfgD+Jg$hZuIKHo%9y1}Yy$bhFWq5H58Uhxg8^UFkK@awh$U>Ix zAj?bp5NhBn0+}cby!x`14!O8-ba`_zKQIcKVG zF#oJ$utRc8K}rJn%9z3KTk*!R9WJNYO#~IYjfZQdxZcU@fQwC5tU=;Fe3!+8Iz0$j zWnTE|r|pfro*39ud;ur#T}K@UwR(7x>(C1LXgibJfZCcXc26CcrhWcRx+T;o92jSi zq3<{resgWtV;F(@txOpJxu@>U60q8D1%W^P+Q{G+-0W4azBCya32MR#l=%IC-m^8P-^dVe^y8Xn0ZiC02-Ze|+}?d7LAZk~VjD4i{rY)!@B zRzQULEn{Afe>2y{U8=oLMQB{KiF(~caeNo}Y{shvRc z%Fr)gn*Ze5*U68QjYV~;A#OY=102TB^;E(q#d=^YfRu_$l>rdpv~UOsHpyE1(0?4R zv5`1WqudD8*Ta0LxPW&Nb}YJNT&LOQ96r`hw*dI{cECUa|5|>Redf>ppcywXpL`pO zC^MB$`2wlrm!Pa5-%}dC(51`xOts8|Is2 z_uMi9bRA@NliVEv+ssV!{QVzY{tE4K8&JtD9N^SG=~3ERj}-LwkNhO-!YI~j>?V8j zO!1oLzk@P|8u@q*m{rH^>S}MZd3|@BU>vYM*ok^m7$N~HLS;6^c1V|Ax6Pq&7EU`* z#4$E@g%}!D@K}fHB3~^ecWTU&Y3>fO1t|0AnI(*loq#RGo+lr3Ll0zX1aM67pvAwz2fGgiZDjwj*7i?bIIX|)AJ|;} zRI%F(w^N%2=J>5{@BH2Y)cMh%tuc3w;O8O3Mv6>vS2?V}@~jamdh#Fzv}6N^j!^{Y zvR2^KpcG!WkwWN^x)VUl&0a~YFak4q5a=XBS*v&UzK_z&=boccN~d%Ryyb3ne#Yiy zqdwQ0L87$boWCEq{2}=OF8ekOrEsEtEfm{}V-xczjc_@aWoSsbTi_6VyXzn9u!@%Y zHkk8}*`~FaDw@8mhA4G4#U;H^$~sjtfs%-4#<)oLH05w=7)f(Y_4ussHPZ zfom5Ma{UgpYz+MFw=gV)TTLp-ZA%^HmVq%A6*Pl7mxbozq;JD&IjUXNM zu>_pdVAVh=*6niy^)|tW3H?Ur*cbAkPB04ctj6G%lia#MHB|U1xA$F#!a$)Fs4$ii2WMOD|$Le0{;!6QIJd7>{NtIujsJV zcvTm(1VGZ+G-0tfSVcCWMaR^=UN59Dz=?5$kSX#*12O<;eeV$cx<6H%v*(}BI{Sr> zQjd!{C8A;&bd%n0x;}g3I*(hq^Lv& zHm3sy8TGo!wsQAWDgr!!WNz#E2mhQdPbVLK>io|RXtU`Z4Pac6;aED#65!S8dj4+U zr3t;x&U<%`ob;1%Yn7x&vFA>42CS?^kw!=zH zF-GA*dzH8KW!XZ0BFBO%SfHIl3vRp&_#CR?wNyaIi5DR}#*)f$n_Lejp8nYXTD}Zm zsej28h?gmWTsMcvxJ2Y$4^@?Q-u&m0yS+6llddS;3B9BGJ_+?cU-j`Tii!tsmDnjR zt_21{I)R_hun-a_5UnOL*g(Y8jhMxQUhn0ZwK}Xk!OCtiEgB;qQkMnG_=Bta}eQ5nt#l0WB zuYrNN>T4nl$wL*!0v%kOK0nU>C9%*9c0i|2Tqumslj1s`@S^?Rqx9XM)3vyetC*MS zCVKoub(f2bSUh;ki)^sn<}S&SPPleT@^dzPVY z&4Y(AF@HTVS+D0m8F67cX|a%kJdnw*{RvGWB-?fryAn0k_VS)>n$%A?OJi_- zol^l$%NJcQJp2`NvThSN_3+5fI!K9m#Ws#8rZ&4w$#R^gKkM1RMsE5D^k}6c07FOq zNs1+f*YtSb3ai@k5~w5VN&sZK6hA6%ClQzmD~7mHDj5p(Mf?&CyVdz2$I~DAecDSa zBfP zgkF05+tc~_Y|eLyW5^@i$KNuaH=r?NOcu; zm7!w^)d+Iyus_MzW8Bs4SK;H>c}}MUG&G-V3OG~$IKVL(?gAJO^@YqLr4W^tPkC} z{YeniRWcxw=*+$E>}u8X9LE>z#@asW>*F}s3~GHm^V`QrPe%fpDX~EhBxG2F7`7n6 z#O)LYW}UwP&_p(H6Xk678dIzwVH6b!N(-F;@-^1INY8xY|B!n(gX|adT|6nk$jA%O z$_(Zi)KWMV3Hvw8Y={@Y)gfcRx{78hZZ zy_V4)Pj~RQ{L9~ac==)hspEMk%!FbziRbDKNeO5T{=!dr;U>s)4T4B!K}6n z#lrlLQn|YBr)vl~q|QQI6jv@Q43aL5=~!Xwo{ZwKMUmS3#TlTooyi=-NdX9w!b#T8 zAAI}|)<0(1*;jGc!wvYwH70m+xaxlS;`!AnDj-K2fVl5HtHw zGJDJ>D_u!WLfhAp_F4LrfbPRmv=d@3tKrek?p$%Fb27+tL7Dix)}|goMG#6*j+5_z z4f0X|8$dV5(a(P717z=0_=uy!iQA@7Wm-sfVPg50FP?LD)Ig5*TEE>NO$uPfzL~QZ zV*z@-F0iW%PkMiXe9*89M|s5p*Ko=~aj@w~&+csKwQSY!gWiGBZvwk8)E8=e5Wv}W zI0mK+TFLRK$xG_fEdTudA7B1Jk3L)>qhj~-x9%Z}&`ZxgwVZ7D7(M7Gv(Gk(;@X$w zsN%rvoU3$fKlZ>;<0O=bq)qKH6fJ8)oY0f_jL3fv(OH>SdN-@{Eh6Q&=lzD=5ZUqDxDtCt}*Dw_9=Rj z*SRA2$~_028?{&L;iO2G29Qh0msBT$TF8HOD_AcPaI%ITpMF2-tD@OXpCern_GjmU`JmgrCc;+sT8Jb)$S|l9xb&+!OA!)$GeS#deDN!Es1vWE-CJn^^ zJja^Uz`m9}^?~2j6AuG+KcZWI+vn_OKSU*SA;9$qU&L&|8YQ^Hh>XC@xlN%ol6fAW zsh&pDt?#Uaa@PMur6UJBK3yk3lMRw>;YWg08$S9bLrB4I`e6E9KG=a1j=&^a4joX! z66|bn^*hKqxYx4z^YW9A({uNIbhWEJ{zlWzf9s3OQC)Wo1r)X$Lz>WT1~Oh&U=B(A zI7u}xRehEajhG8?YNBuk0@Fa7K5 zkkC#7b^wp7<7J=yG}d1S@D!kH|!G2xE-x)v8NQ}^|DK3aVwNnb={Z; za3iZD+g`pvPrd)Y!ZF=C+w&K04TaJ^P*-e+K&II%}kAqQ9lo^7HlCj&cCez*UU2S0CYNJeu_GCmK$Hr#hFuWve`>zZtf zR^9d_Dh7=dCfcA>#p(riDj&0*cmKVV~EJ%hb~9EM6+Zh^}6`NKb$G ze=I)|^YVprl*}72S~qPi$1hH;R>;7SeILsbDvYRhbYKkjf2?ir#=8%l7iK3q!m4XZ zeD7P|d4n{w$BOu@qJpnmm(b|fhomfHSI!aGsGY<8Mw?Dws3x?(0`;WB6Dvs-eM))h zG76xqz8+3;N@FVh-YM38%TP`Jt1vy^IK0J^8Z&~5OgCN3(E=iBU{|8rWZxNh44)8yf=5YjmIMyH_MC!8FZK>-Fw%fd($ zI`e#2GIlG&viMtdC9{rLS^FZ%<_iB*=vKL5hna2|n(2lDdQPxZW9LFgx91QyO~#aq ziVD;z6sxm4C|Lg4d}7UYkPF$oQl8PSfVA2RXAB63H!v}_WrA)m8tkh zf2eEN&*VMVHQiu9x73-ib#i-in9=URyOvu-AWfX>cBwAIFhocK3)%PnEPl3C1{#ON zv4Qmi=B@aG4QG}hDx~x`qu^jX9mA*E`)rKzG0IYnx|>o~nh6Lyighw+h5n zljB%nQ_fGIxc}MrexKX)XhmZIphGAhyg{xXnT{)#Tr?t@$7XpUXEdS zdnc}%>ganyW4`B9W2;#iV?>5u&OPQHwIk^Baf9BZJ0{%(h8}H#=1Ji_=~PBg zF2JU^^S8ItVC$Gp5{@~Q2=#U+s^+h~cOcd)*-?8W*dxu#SS7yeSe~i{`BIP2U{Q9;!($`)aT4q zOJ`elCq?~hVsXjDh-H`<8@TxZj_D@G&~=hi?10_TS+7rU;Zzt0@h-5BWNWP=wPD8q z9qn}V$fPe_xeeY9NHkDqXO{r|{68o0V=Zptm|X*uYnNJ^U@A zC@(mL_^bXv!wKa2;=*ZA?b}^HBhZ20@LXDox{9u2Kp>lKC*LMcXUk(CA2|k5G@P>h zu-N+M0(z^HRjfJ;J>X1uV*>ngDNrLjLTy81Z&Y^8Onw6<*wb(-5TIb&ZdBiPiFI>w z&7^VI4NPuY`k{v$mNW=1yq_vER#2B;;u7;J zMy@W8UjfQyyv~H~TLaX$5TNJ8l+KMeSkVFsNhSC_ya;4tYb>QCzh+`BDUPbSHjGCK zk4=GEDGr5Ko?jIh1Qk-Z75~~oD9(M*{z2S)3#3~F&;#%8nGE{98j%7&*1V-4)Ci|_mq`6Iyw#`pjQixDpI(Mfz+$~D4bx(Tk?Yjl6)r9CWh zZG={BMG0ry-Jm1<(|}A_&wj;r&AH(JGjx^0BoU|VBWjYk3ELE0rMT5;WMhfp%7-w> zYQs(D5pBn0j#z~N0y5^4iL8fd^Sdlt)dn}W28c1;A`Bf;{Mr*!VxMDz1qPnok}nS< zI_tHUkMI$9JUf?A_1cpE85s{f36ey0C=*vItdVtGyS*?WKg7q}Rl3C(x~1xIVThxT zc^*@-c4Skjg)LS3;im`io8ug+Z%?%(IdzY!{gF))wVH@j0;frR%yrXO96jA4p+--) zCPOa?I7Bce&FOR;A%wbj>q`E|Y7+`Ydpew|z+u$3paNT{DlC;o+O@nw4puw*N~OA) z%BA6yvv8x*t;x_X#X)O>3FCR1Y4Z$LfA1xxICXd=mj3b-FskSi$Q-#w!Yki~8~Y4Z z>j2X$hH#Blmr)APb!hpzq+5rfkNQo5_PIwsM~tmak7w*i$H+(BioKIVA3_j@PPUWk6&DGb-J@kpMOCWR8(qA$_&q<0WR+Lv`nE41`8Ef8a+3Ao_%B7UA z&8bW_+v9M3&Cm*_lpD_SfmY1BZZ360(qBSA6AjPo2D2?2Wv6bsZn>3>VhkU$WNrKH z9VK1F=xQf|lc|1cn)Dr+cMGLk{FK9SQZ>3D3bQ1@XQLOLM)Y?i0o@YQ8oeuik{^on kTV(x<4f2Zl|CUMr54`^MFl9a~l>h($07*qoM6N<$f)p{1k^lez diff --git a/packages/mobile/ios/ton_keeper/Resources/Images.xcassets/AppIcon.appiconset/icon-notification@2x.png b/packages/mobile/ios/ton_keeper/Resources/Images.xcassets/AppIcon.appiconset/icon-notification@2x.png deleted file mode 100644 index 3f02af27096f5e6c0b08457cd7adbb478809ed8a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1947 zcmV;M2W0q(P)P&{z#Lh(HcH&v_-?NyvioD)$xw{S%ACb)x)k)8rr3_?iP zDiWirKT{VvZxa8cfD}|!l(!@*01L$;nBra`z!i6-vg`UmX?m=>ABdcPSjCg7lutN7 zHpA0Gu`qaqE5KB&lS-WOS-I{tTAVZsGCYt~W&L!&nj1hiH!izps zVuB^4xDZqTBJEXELKz2IK)O?905c1fD)j@a(!!AUi^Fq~*EFc-lu```>C}woj&tY( znJ*G0;2=~|m4_4OeBH+l`~po-4kkc4I5SCt_9C&uN`##R+050GgQ0*kWfjU2IKWkd z!)WXn!e;A1QjEcDsh!!#luE}%WK3?YV(-OxT3f{ek(H2Ub#Rqo@oztCfjXc{ zW=t?v!G1wR^d=pC<#p_P_frhN{2I1%NIcO5%{!I6VbN&ZD&tdw8b?pnDY8lm1yvT8P59mRW9@N3h4=%q0X z?mLR)ULnLqxFCNs+vkGcNIk6YG@spz=G+Vh4<1JIx!q6$JHlNGeg=q5GW3IBrYiNm z*)fV;#oFXGY_2R}dE%T{u|em!rIHUrO`yIDBs^eiwT0#DXR+ta_pv@biORrP$iWv5 zo4B{r$GA$aUe+P(%qG%xH?aE0Z)i`Bqi4=7!}{zk=uSH~zqp9IOw&hmdhB*(2U)&( z2|EuT!{Gj-&_;#s;=KC}j2ytg=zcT|aKDLm=4P=vaS@&Q8N`IuPG+;UfbRS>kC6>X z$r4V%q%P&9RMONfUibi#uYku?-(EZ z)tMAB@C%&v*-4NwE!T5MK@Hk_aKD2J$z2B;E5_6|mc?N113E$owZ7JIE`ZWq0<7+? zdqD2j52Au<8DcoCN+y@U+KUuy2SyLiv6$jbr9|3b{?K(B(LvfQ|P8+xM+nR+2^Z9uA{l;pCBolA-p6emGK zIo>l(*}C=dkIvL_FLg@t zVODL&tlH_Dp?ypQYzEU~NMyc8@^QygF$~vHa$Fgwh?1GKPd0nT)Jqq>LCS(;X%p6) z=(SgTTIGH^fC?xPYf+rBiIe>U7E#^*VsdF`&*tixU10+4zi!kZH||fPb8jlZ#caA) z^i`-BA;f|eftrxV$vhr1IIl#_nXz3k+9d%Vu(!U3_S6K!k<6a67%1bQRF3*6nB5RF ziLkm*G7f6WEwlMAUHZBtB;Pz+>WVSa4 z=1dvh#>_k9@|80t>mEaQX%6c%w><#A1lb8Ci^O-2xRY>}mHzlp_3wi!D|`kb<6Q1* zg!BU6+`ZTD$Hv5LMmMGaLcTy0ZwRc=QxpWHm=*+e>WQ}^r>v{@ZT;s8`%<(Z#t@3d zYc?%$6UqW#{pkMz({-f6m#E;#N`OFj0#947z(002ovPDHLkV1in?sR;l8 diff --git a/packages/mobile/ios/ton_keeper/Resources/Images.xcassets/AppIcon.appiconset/icon-notification@3x.png b/packages/mobile/ios/ton_keeper/Resources/Images.xcassets/AppIcon.appiconset/icon-notification@3x.png deleted file mode 100644 index 9e745eea0eb6dd92881613ab8f35ed32683e4f5c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3436 zcmV-y4U_VTP)UO#^XP>#AnN##RUDZ|9-FKF>bbZh1)2F+>`o5}O<{I}se}rJwg8WV9I$U9_ zkrq_6H|%0oPve#O9->*Q8FGqfm*}mn*&S+XBEi#MxqNFzaa*rt1t_?bSLpmzdpW zH3s=Ag~)cwb?G1OH}Kp$(e~#9iENw+}-S!LWt~1gZmg z0CWzqFtCJ%?C=6O^?;JPSm!}cfI+kIJVf}9vFj%c#V?4 zY+|+3lz6XKfDL*wfFT6Y=w2K0S{%%p%-EAt8evIPF_3?Qx}hE9CL8SuBLFsxZHHYydJT5J@)|T-w#EL~ zu*Jzo@Z`i_VeZy{!u7+Lpop_l02B>2_x$bJb#2;~$xa!~JdaPhe;nW>Ih%Pw!CBDC zY*Vl;z?-pcu=~fa_dqdR>t@1c**MnH#3Pu!btQm*8x|)lZ~@nrpj$EyX*J#jWQuvp z0b`OtA;VnCjb&9qW7D;X-&+rV5BC1zE!cYKr6i@!m?pHNl(rCxEgzT33m?Gb`C~Bm z`Q?D4%@~dMu|5Kk>j=0_U~|Ce7%mcwjn@d0gvCbKY?SHXh;82pK!5qW0Qe=HWf0vw z8)MI8Ae&4R=`rGGZ(a_---n5F|B8|&2G@aVTf;i}>%=ST=@+a)`dhL7V^!xASOKEd*Hkv@5SWT^$CqSzpEo2ck4I53uF5ar#|d% ztnjcYgG8u%h_;e1FV`a!!MHR#0h5=G!LFBo z1M|0UKoh`j2%yIf9)f1`W;eg;+BwaFeSggcgmTr#NelEPS<1K1hA_iGDS+XNtz%rW7uvJJBu1)akuQ_Rn+v=Q?TXhM|w$hUT?=Ah9vS3HtpFD8~1!Q zu)fm-v`$UL^6Qt6jXoE)$xUAX!-sca`pPMI_V7-s?^-NPJq~sM11NpAYF(DJWHgzR zH6G*lhP2=q?AQ$lU;iWE#xd`tPzGPw8NeTajeGYqV96aKrEJO~&~BX-EbCm{N|MrriSC|=GKK+QRP1MExs&$A6R`8;U#Wz+qowJ| z{?mMM3&x&59OC^sz##XS@ihsu@cZjdDbrWa!d#GUCt)!CwVcU2u=97WL%T4S>~KqE z2`3vb8N()qJ=%E3|EB4HYz~2$Yp26B^#j<#pUuJi-Roe{=<iF_#lf#%uPU$bKIG$r&*y!s_sRq zAqCYE!GN;v?Vzb~Iu7L#`qPIPn2S&E!}PU}Vf%MpG0RGDOH5f(cAsaDAM~G@PtR)X zrvBAPU2ev^;hj%mc^IYa&f$r?6RgeBA1Um{;&Yu85-}a8=kYUj@xu_)Q|d2l{59$z z{Y20(cZexNr&&oGPm;yO$l(wh5NUHw9i0ov6dh6LSybxkAhL!%8IQEfhS+U;?xsxn*s}7EzJzCKI!*>=3$Lp z`SOEbC)3 zmMu<>ht=0@2HUcl^b8B~{9)^QHBn}}c?@=1%2Iqj4N<|UmAOAg6+@qV__wfsT}Uja z?ix(J#-t?_gymy6vZ2<3tn7fq*(_^Mh}?q~gyfJ$LHcy9`q!7;JXi&-hvl7-i&Nt; zarPL$fi6z<@5gLYxR}3vElD{Z!*c9)S-CZ#LzLN*69Z7lTzqXCL-{H@PGys4WoMjW zR~#^P>15b6+|yV#^KDvv1;)GSXYSTj4~oyAQHAyH8Ft z^0Cq;;6&9*l2HcaGkkP6Y%orie_Q99xzDfmCm8x?6^_rgO@DES38>X^ZO%y&&oCeq zr73Etq6xC`;H9msmq3N>?)&NKqp&dfw}))F5aWo%9p|N`1v2rD3Rc} zawLrgZ0y$?|E~w#CaFp6%2XnAX!mIsX5rD%zvwgtaax+baykQACc)w_hX$-CD3@|s z>VT^+W_46!l}sWd1<%*r>S^ly@f6Ps_dW}!o%c2FD}7PkZ@wllNh=jBj9(3K#Q|hv z*FN#a6fxX`UFP^+4o5=&49BeFy_EZN=1OK4@cA@IHD>d;O}1ba$zVm0F`pVa^Y*Rl zP|jXC)7qa8%5f{`8*Fqqy_lbc#~-`{v@jn|Nv6$MYOvdlK^e2&n!Jd`b%t@(ABZEC z9b)-s5HLF(ck*G{s|&&B1U6pheDjRbiojG+J$AYM3S|vH5H}?nO+r>=RY3P_v*{#1 zJyL^o?M49ZnYKuv$lOcc6+p=z0D#ddc4c+IB40O%fsqtzq-0MbA>XE9MQvo}dT=B7 zB&n_=Fko}TRw!#@n2pz;^D@P5Amif1Hk(K|()nK6wS<(lA}GSag}^Fh9g_BpNn*0c zGH9-y5vY=0(Xb-^Y?7jE?0UP^%DQ4W5n!1%*|nj6TuO_4^p`2qX@UU}dpLLeXyEJ<*1 zl#oOuvGE$)co`F~W6z9d=FZ}Kir%W~>+W;MV;}j>+4}UUufDIUtNYj;_{`^+?sao6 z?fkENSgyF@UTDsoZ<98uABLM-*qBRuc+=;2M0ed>YapCz8+a+gbaD^6(E}&_Fg)YZ zlWcEs@fx>wi9rR(VGoW5fnh`FueB8Mx%vV*9so&0Z&bvot>33dGjPtGq%*Qpmz$tu zhM64=ycIgYYi1G)koLjBXEsGCTQJmBj>$HIzkobW=lVW)@c_du(1tzpIOmRCCDp-W zICs@c({)bRs01^#CD1t(T;`Os#A^n52vEUj#mK<3&SASDfZ&1xbQPdkZrX$yNLI(ErO<%=u8`8~oii%9B(m9NV- zD& zGRc$;k(?7NyNr%e)HOv0cto`%kO2Vc3MPjD+clK7!^Gr3KZTGOVlV(nfLM%ofC-=& zvrIa!Gf`7TMd>!mb>+U1D}YphT??9Od8hsEr=F*Nb%j>1o+qmaNWm$ik!eoDNIdV2 zr#b*j)HGE4j%u0WX))k$3bHdsr#NNT)6df*-}(_f;DLrqD;JK@!jXftc;rn57LDT) zpvxeMI)AmVh;kWt#ULSwoU{%+`Mi#_RuDJ^YLe|LkaFG)+)u~d&fT={>o3uRUwo0a zJ#=8ul#TgoG71+UAlU5A!bkt0 zoBw*1Rxh8;ppex7vA-h>Lc5;%LWwSp zla6$l%qr2CvJjHWPjgAwmd_ofg%97L`43+6SP)=eFdiA~ja}(z0H`FQfvkyaDw7?} z?Al9@eDh^`=&RqQ(Y{AvaJK~EYOeAS(6ddZ9g^x?@mzRkXnW_@HM;)BZ)x%9+ca6Z zok31v6`0K!OQ5xEoA}Hi1Hk>?{-MvHFH^T;w;IpYI0aCEqi)xpc)ja7MUkA76I;m09aKy>JYhS1r)BPNsmpCbNlBA+TKVrWy7lf~>E=7H z(%L7dEvU15it$uZZ1&TrNc@of<-n$4X%`0gJ)ivwjW3>|?#e99?0t}SKJgT}?K`uv zF(OJf6XwhsCZmpss&{I&lN`Y7>$LRoN3?wUF!k%J{x_!arBgJtm3yBdFi z;T`)Qr)~aMjw|J{tI|~$N<42>0C@Z8yR`22(zhh@wdvrsn3~xX1ICz(hW|Cy(&DdD;@ppojPJKv| z$kTm*;8WKZod{uYJ#o zYnY7$S+ATwMQfKYra1oeGZ8!nZ|?yvpZFjG496_0fkGp69qNL$E9a?STcKg4A}Dcc ziDOiN%N?s~ab?Our+HiQh4=$cJ(nb$Jpf@AlhR~uH71db8&_$YXEy-eShzteXOG1! zisEx<1iZ5!FVO0hb5)jw<9r=uTxJ?d(iOQ%z?Z{{)V6XwQ^VrGBBoBpQG7RPf?WOI8J}gxYNgmvn5bKhtIBuL zG{mLK;I?=Z({y=*Z7Q^QOU zI@7Hylb8%}1E4c-H!#^1u!~3j8DrM!SIwt6cIA2=rwb3{m=5ppSvm|SSCmu-z_;s^DcZ~s{(i$JWNwd zDAp2C%(Anmtw2oMrq^^{yHnm1g8t*H)$jV%=5(^8|H{bdrNzvh| z%xYQ5S=s0RjW3?|RZw2??4gDwW0lC5ZRJa-;39($O$f70r~+u6s$9Y2X51tmube-g z5{kHXK(0>auN0l-ImuFPuz$^~q7VmMh1OF-%G@7-%$b+_fGU04J;#K`*k(Lr-B?x_4)B z&M#HIUS(KgQ}%Jp47DsIAYqaEzDaykNR~87)~4guO}|@p!UDEj9cM`YwvwKX1FMUN z-||znb44+L)xu`CT1jb-DPrD69G`*c;JwKCW&&XuY)O>RhJ91~Pw%@re0EI0U5nO`A;fC=uL2uUxes>n; zXyNc7NJ?qH?B}DC@$Etp`fPYdpF!4Hwund|LT_;+^cAL zUwk3(i6Uh`1g-eR=%PJ3NIo$TESI2sx%lLuIe^+kGX*FOd4wBQYi5lVwn|2apfnE7 z9sGkT>ehRIkJnVTAWi%AEtAv3uptSzP5#rPOe+W`$_%T65^LPXNc?IopZK!@p&*$3 z*nc!S==W#RHx#O4jSEMb!x5m(oU}G4>B`YGM%~^28>=g)0eKZ&{}fB5vSy?On6~G9 zuiNoJTn6D#w5tzLF?F&*iITA^v8cOEVQk^QvDWD<{0sQfAM4*{25=xg&wGC<%y6Ot z94R=4zAa2DmdV<;An(529Vl~?suKo9oB(BcvOFW~GJ=+4QzTki0>pb39z`3X&D462 zKp|F3Ljeea#mCqlbVCMV#+;JfC8N81+;qcRqjU>E1q77^IhmC7qpl%WhUMI3*Jyl- z!AzKgc(8DxEpJ-@iak2GB44ek$b1=A)t=#$ryE#Hxrsxb+V*bST`Qx~5MdAvVHta9 zkR^;MsR582c~0wDGUw&{Ry1x}6KuL|kx?p)=VHhK;6==lB{2q;hZG2m;jt@6%IdLu zLwoN)2R24;gK$*=ir4AiB5teRnvf`N-v^y^-#{hKq$tp?L(0noGILuT_R0jLpBlIPbhG^*^mMtxW;itM00000NkvXXu0mjfu&PE? diff --git a/packages/mobile/ios/ton_keeper/Resources/Images.xcassets/AppIcon.appiconset/icon-settings@3x.png b/packages/mobile/ios/ton_keeper/Resources/Images.xcassets/AppIcon.appiconset/icon-settings@3x.png deleted file mode 100644 index 4d8666d1407101e3a89c9f0bc81fa255a6158541..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6005 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 diff --git a/packages/mobile/ios/ton_keeper/Resources/Images.xcassets/AppIcon.appiconset/icon-spotlight@2x.png b/packages/mobile/ios/ton_keeper/Resources/Images.xcassets/AppIcon.appiconset/icon-spotlight@2x.png deleted file mode 100644 index a346574c43e422eeea1b79f3dfbb45c0b84391db..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5281 zcmV;S6kh9zP)MxiIa64+ev&K&v-oJnX&t9cXib{&pA~+9$!kDx2vnGtIv7rIhU$_=jG>r z^>v|#EzCsY-JH)$9ot-!)gJz#4KdQFqNC3ifg95Kqf4*_S(JCt!;LxA7<+ z8!OYZ3b+iDCGK{rKtgvU_jLwZwqe5CQ8J6f<*asu3Leqc<8kAn?5?9DU+$IY8e`Y@K`Dl^sous}-iK&6rTxazqQ!4&UVS+rGPBotNf zepA+2!9r9>53>P6TtCKps0PTIyg4UO+^xktxgCGTYhCVx6Cq}*ZhSWb5`ZvzX|t5c zY{5x}Nw}~O#H^Ud6jKP4zjWFraA;Z$polv+gcgBk0He|n>i5D}0u@84nrO*@ntLr4 zAoCzXS#VEm_$BA-m}&##nK~7A1~6MppKKbSKBybxJn*VvQ@VEB5;{0qBgfP?;%tlTi?C`;!+Go>@@cv=TLjR-J6 zzX=3sTdC+U%4_j~#7!3$0B68cQ#aZ`M^9BVT?3C>rmRO`pc{*;M`;829n6jmB$L?L zA7`-hXYs2Oh|u5p+`NgAU4TT|L2-E?MqJ^bNlU5mvn#-{&3ZItUF`dF>qCU>Xu#}JPiy( za?D`5o^cH`&xakmpP*-d|1W6s=2cq#;xuh8T>*hg6gCw6&LIQE!>>^81Hr_55in%t|;&VVPt$S-o77a31@K z6mKX5OOo=T*QxYr0a(mUnn2coaj1)H>vB8!b23(sfQ`V709LL$M_v@VFl+>H1aj%* zVO3tuG}ZfzQ||#253GUJY9j?sCsbwuH2_8FHxfYWE7d+1vLE;Hvrw0pI| z0QAw9z<1Fs`y4oGrX84Y7fiZKz&-cfzocgd|2Ebcb1x?xg=Yh(&QFp0{4vO^etwFs z9saig)XSb_TJQ6DA3&3ur7BNgBes(=FtG1_zAx21!=`%Y8P`qcPkigQ>A;`-E$w^l z4R5B1N1ixAu-J?j1TqLm@OqeltjF?uKct%{en_{^eE=Z!0BY}n>idkzWct*@uAutD z$iV5k1F-UW+hSiAd|?K5051Xer+=4@YI=lk<7`%AdSFmxMRJQbVX?&nISf--J$H(h zj*nm-${dapP-cp}>)y>4Lum0EsM8 zlK_C_RUlDZ9TaBfMLkB*&YNjB0{2IMO$P?xIQh5fAq*hdn5ZCtCgZS>6h~l4B_~Ht z@S)C2I#7x34nVF9AYOUrpJ{UdQhJNf-cOSGNd(XVIUO4eWSn|3|A)TL%=d|3|6O|V zd;dWDe(iUr0m)t^8Z>Yg?YGf}5!Vvq_I`k@jiMu>lmaGl3GZUA|L%L$ERNZF74X?Qc`5{eFidTe>mZv&-3+>xoY~QO(zBz z1mjW&TH_d_<)oDbv+M8f)n`odjQ#h0>$hn43$IX^587fLpkwjp!&1)Hkj^G5+!Q>W z_36s>!~aSo~hqlr`u=WPk(n;7HRp^kvuMb0!}Ct`Ua5Jzf|%fm?k<(t>xX@H`8k1+3)=M zkj$Q?9lLhZ!qd;t?t`xo5A|ugOWZ@%FXk=#Ze&vBz%l^YkEw4i5pn$JSViX`+vL}C z-;H%z`Q*I;xRWFUh~u-?E__V=omBvd$p;@VbBmheI19MebaTnIEpzjP>DHN}wD*UPt8fB&#j3);S7lAQ-8BkbwdOyAN z=06b+=8H;&a*^v`5 z(mb%w5w|Z9IRuqr9UAE+ZygHyK=NA6m1rxO757#qQ-0?6zE|CH_tx_8w?yk-eL=f_ zaP( z;5-(Q7m%h7H{K3(| zOfOn42<*jrx$)+(M(zjGP4XnO?V&E-oa4%5dH_6UsxK5DF+s&agJ{d3LYRue%8kXZ z1~wd{Cx7!hs>lS~#3HfqngrS$g3ZRYuT$h61J2IpUi2&sWdv>|14mhEB_~_G%d4ld zi+tx&!*o$6As;cQ4kFN7j|DS(57~0iuy9W_*z^22CQaY&J6_D_eUIu(`Ia}lkyX?l}=q58z~h!WgTfD+6~eJK54x@(ut8lC|RLha%nV69-&q`SI_=X+;% zF`SC85W&XeXv#}JKLkpl(zfVNjO3{gLmJ|h1Tf1O+>+1Aoi9HfmTPNdj6#6G?KE{o zeY|@3*z(0OLL9_3me~tF0|gSYDRzPmZ_kD`=be!Ss&q+%U^tXFJ6gg1rDtn-8Fyf& z#$>wdS1*wd4se9MJ@4yKhw>2oezljmBSHg)i3Pv}qen1ly)MCTs0>1jKl|^L@D}Aj zdpn}p5`~1?z>$0|xleR2$$zfrsH<{K`b>xFj-2Y;kBX?f>DkS7!Iu0KGA<+i0}~0Z zX9e!2W!{l&qGT*A^{E2vEbmSHTamK2h+wvYzzE*GVW*{lR>>${{`y<21Ufslt5W+w zY1aj%l+X%n!`ZadMrh@uW77@@`mUxQ^L|d86BoB}!f4_d^t-nfhvfsRRIFKsdL{0!+k*2BDd`A_{H`EN0llt_11N*#`? zzc@+deTI9LNdTtmLKPW=qV7tE8WWB|BtYq@+%j$xE`DXg&-`>8*|9vDa>AoCaoCRK z4^tR@EueBUSVHlGOm{AxRnxV}>#NCRFlna)d^*pS&s#wsf=J_PqfIKt5gFVjq`jQ+ zuMcmXewQ{EFS}F*j}T2VIAZ#99b8Ho1IOCMvkDgaIB^q7A=m-15j7DPP%8>02Er1@ zMlA;+Pk#!yiDRBf*uiN0jb?S@$N#AU6pvZzb*_gg3RfbOmsBgKk2cuAw$BpY>B_^gPxyfPa-3x4W3z+gh2Gs^_p~9s$I$J$OC&i57C0NH%L5k?vdAu;}8zfL{KrK@{>2 zG}9#mF)|TB9h4gP=B^$2k1`!Dzw;}Ox!Ik{?fhcge|gBW3SEm(l*e=>@#hSeJT%tm z|GTgDk&e~=`gTmYcOXs-F?Otfb#8e1b5sL{`9FqeOm20;wi_|zl^1C9<~1tcZJEaI zY1S7L*?X15xG4+b%*dxhJ0=P@)yH~?dKE2`pN&ffDz4@F#j`B zL^2dTovt2*s-+7kRSLwx-9Qm+C$NFWQk1msCMVXAvGK-_-%7jC?3WxSa%N_~wmJZQ zx@K4ak18M72X$t2fKC#8W2bsKqnWmy(PJ?gST)vUEyi|)aLCupJ)^1s|Z8C4skEKIYto9&;KuiGQ_OLN2Dr3-WaX3)dmbiVxF zkzp749N~5st52l#UrXb4!TYF0-g?bT=1M8wy5d;pwJ`ch`L~HgEunvU~c(Q^R z6$`5bG7zsG`j_bq9Gg1nb3ibqJYhoWNiil``y`WXwUM)!NQj~WZ=HgsvNeDlb3<`B ziE9fpiL5Ki!WX=4g8>4-aJ;*GofhAHo9u1ggyG7kr;_PX@pIXPrDAvwyDdeYlH0Xp z#3r~eFB7*U(L8|cL2$)DbZ_u-|FY(dzQ__=A_}ISzCkbM)0U1OruAV{RNoa3>B{P7 z?@xJLk7-a0BxRS&KSv77i%t>DIgGYJ!5ILzMjd-u3PUEKR>d(>ms8Q~2+S{kH|6Tv zKcJEhEFOD@Y?aZ&J>)Z@_3Vxv{Xi7Qbtuh&f3=C8NKs*vUwddMeEib)1^XDZY5 zzo7 zypDMshkn?{_sc6>t&gSKy*F)?Y5}%b)dOeE^Wc@Cm}H)T+qA+5_yNYqFfWt6zLkdW z$l^-;N1OaHG9uT@KSPhF{hif@6y}4q0t(lTANvFES}bgVd7tutzhK!mQ&Rq5@L_Vc zA53}_pW}Vh^j2*-0!yYZI)yu>0i@nLGcL`!m3DdXp8yRQ76>NH@F!9tpyYs5hgjMW zkgcMh#6SQb&VnR*ot@l~eN0;eT$cw2u({=B(Ig33q793`srVlT@dbxw&A;X#Ecsl` zS)x`jie@Ql(NgR60Kk><5DaXy#J0N#d|kO1XsdS2gg?7Br9_n>hG$6Q`V4CXdN&y9 z`#uDE^w7WJ%ath$1-5C+)K}9SHnuv&7^k3JWWHkO(vS)IyYCjx4f6oy5q<`F5?24^ zfT5jg_St9YLsYR63kREOU{!6o2{udfKAEHHLzagGQ0iI$5!sh1n5$g-uU635j!8*0 zWT|CpO_uBQXr-gaRk+m@7?c$%`*Er;u4h|8LCszKQAeq0W^+THk+qnQhUBCDl6iW5 zFj)$V5eohhF=2MsEO5_(Xh+=QXFPY$to@zbFPJ=Fd2|3V87|q1yN-%sCPviSle3?= zVsCx9Z_IJw2Ytgx{@Hq}uJ!TQ@M8cFs3Gj~8CBK+i$4Y5@J~X~lQjwHL(F!;kjI6a zmd5}fmJ$X5$i}i*e{~HTUEvvkU`aT0XMC1cs&6|uIzDuHJSGe#mWp_;Vb|>ybNF-a nAP(DfZ75fn*tz||hb+GY=r?BuF8b#a00000NkvXXu0mjfZuc;k diff --git a/packages/mobile/ios/ton_keeper/Resources/Images.xcassets/AppIcon.appiconset/icon-spotlight@3x.png b/packages/mobile/ios/ton_keeper/Resources/Images.xcassets/AppIcon.appiconset/icon-spotlight@3x.png deleted file mode 100644 index f0854f679a68afe858a90f3d59d1190f75a88349..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10050 zcmV-IC%xE-P)rnCnV15 zJ>HXeiDS=oco4P-un@u8(Mh! zOEOHuY}I1T8;&w{;nG3M9kv~XrR5IB9GAHm7?sk#(Y%t)d4oj`Ws7cD3CJ<8CFzfZ z!VR5y)}1Oq9_A6C?1IpnTYO_3MS7&`3MbdX8;){h)osdk1Xv|pB)$Rph7D>2rKt`- zO@$3CjRFXa!UZS}{Xb+x=OH7G|U<4(V3kfrLNnR=c39`p?JIvSLzr{2xJ-71IJzJ4iigjStBeC^mr??pAUu%Yf2#`?f@i_ zH3~_`+bDg2rFE*lo*EfPYIaLu^`K_0JzM3DA5Q4hlL#$q8Xv8`mL=grayQdPZwpZz z#3?Si7gF-FlWd&Giwd&O;~Ez&evmc7@Y5uEIt|eS6>1l!;WlQap9XlA0~pmh3x>(M z!US(tG?+zzi^{Xu4oTf^&P5%{GtDkf*u+z>kHKnkOzX>rv8`$6{&?%a4wh^3fIOSI zfnS?s_OZp=`jD%i5MHPQINMzMDLz>Tc{JT8HwM@6d4pYrLQJZ@c%} z7QMkj8wbz8ZHm0sqqoFtxYYUyoGjyk3Ix0t<8|JcLNeq|(f4(x#WkR88_!}3-D;S2 z8oJ&sH}i9hEE#i~T5@8UOD;aT(?%H7M<3T3*3Il?B80AAlDT_eqt*H*!w3&!I@Xzq z$@zZkx5h`96^A1O%KJ9kJ z4T%OJjf<|52@*j!0AmTpMviVKG;pZ%M0s0q+0D2~D;H#hyJ}DnuE0w>2vvdGy%2@n zN$`y>5fgLa&6#o?UmCXTWR74p(QX^FvSpF?0!X&yD13(G5gE$4(QqL$;P2-%aTsxb@zRvO0yDvu!0e%!@FixQWOKUqdk$AxBh zA;p3hPNy7m9`Q_{DDl@6>`X1*QBDiwv>#J=5`?2QY$x-(AZ!-r9)2YbjG_OyZ{=*! z6-CLcx=uoY2_hGpwX3x|Y8blK=1}9B=MlCjW>mb?3wR?hyGWDvE1g?lfi3}d7J8T>Td^VU8XK5;M$=Yu>_O2Tn~>Sb(!fnXP65q8Y- zHIuOTCypmkE1cV!OiqBlxMgv)V(z!*6NXj?PrC4WD_UCDa(kxHq^&I& z+{=&)cr_;1?G1B>Tc}sH>1rA%5y~Rsp2r$e^$}{%Eix-51J@_23CL8G8WbwsFLT|d z;WU_E&*L>v!_8J^xxj{>PbVmgj^HNS8^*(T9J7GyTyT%ZW$V7p%@-AoARAs`EnL*Z z8UfG71Y`4L-a6-+nhn<+B3OO$;sC|ujEqrsiI-4Bv@y!sF5se5w;XP3g1|N#v~xQ` z7hJp7LkYA*0M=?bfExNFe4qp#67gq^`5=s z`ovDaE6B2etKP~;5^@!mS}(QM>Zr3Mi>QgW6oG5NWokS_oIf6ZIFQ}2-37MliKGPp zOr$D!oVmvt00!4QsRM(miH*+jD%&eNkm4~&2V|OJa8N8VFtsm%1!R&4mgK<+p9J8h zbQWKUJ?Iqc%Mgvi^cjTPTp+HfTXu(s)>f6VBw-EEj22!`rwZrFj!rtG+LON@slA8pZk|hLlH@1T`nv z9~=MYU<=As3o#Z344wOJpTzh?l5 zgIi?-KX=LOl*L}l;c)Nc?}__)o7xW)>w>9}{#>j7S}u$T=O`VsI3+ydUD3@wlxEg$B*bpZ6P z-~7ud&Ju8CUQIw}=aVVjTMKS$=KI}OzD4IBALI0cATzK^xSw%Bw;kJ~C5zGbEWFeS z^)1(QDca|P1!viFnw`SEos}|CvbBlz4%|w&&axQ*F88ZrgB$+U!43cFAJI*N8%_>^ zO75b$Yg3ZLD9v}qn$cySdH7#x=eti2uKZN%LIIe$3hKda+czB!U45O}ry3JS@0AAdFa00H@d+RcQy#+_hok4BE=AZy0uy_922<#_H;#8JX5hXI7G^-Vg z`*N)lBKQ4YCVfIwhdvm9-hKI7bmpOdX@Le%r+GDj0$^NSY@0du@z^{At+8SW0IMgV z24If(K^;Rg2kYk0$ph*SH2UBgm?Q1VE5q=4;{L%6|JGkkpIkt5468XtmqiI(y=6KS zux{L%P*rep*B2gpkS+}Fd*{1Pk-2QMP_;(24ep61KtuFo%WtZ3nv#bi=$ZvErT! z4xda8{MGT>=mQ`76T0(be+=6kT@~qJKv{=7(=CcAF?rG}ZiL`fgsFwUU#2+y`G1}) z`rzD~!6lOiT0s^AC4$=k3QD1MD2}=kv|_Me+Xvgi9l$rjdYk&0_ND>YJAeI8=>x;h z>i7v2vwjM)5f1x-M)@!Cw61ra%O0FN&#n?6!a;q4W^=k6Whf)KBgkhy_fHD6Wy^=_ zU9}MKhL%rjW3hP!a`h9(vjvk|hGwzl8l*~26dACLiC66tVsCWAKlbx<@^}7*-ZOmS z8c?@Piy4HA?V>I?f6yU*Tp<$h2KY6VdXLI7kL&rV>S7*bZoBZ!&(irvKSP(l{UkN< z7_D*yo($aiZ<1**S+NT_5@Z!DxS*7P8}FI=nKLF!iS??>UJZc$0-gNbzo8R9`txp1 z2NqGo17^VFgf5mn6IiQu4Wj{=1~{x!z|5h}9Em9oL71rXi~yhi#skC8X9Bo~#j>Su ztpVh9v6$*=1r|q?WYOyW$^zI6SC`gt&rJ*tv3GUr5MMw3XLQ$Z{Mj5|K}HTQsCPvH zY>Pm%gEEeVo*35*iWNPCO&>CNH@`~Fl`DhGKK+@0q)RV8K3u;+1hF^*TorB6O?NGr ze4<7X;Ob7PQhT>-4b%ac%ZJsm1NP5{6Q@6);;W7`{EVB6EQ93|LoEpun7=Lt(Q{7Q z(bijO;*>|5sh3Inb^{uD2tItpgz^92>>G6Xr6=grr~iSjoc;dXba-g=6i>OlCCh>Z zcLDz3hkw?{*mWJ?B$ zF5vt!zp0eEqU4Ur@GJdY5pH|l_K}a#Eq8p#K!=|iMulN;aQBSyyT(dXpwf;%R#zMl z*W*!K%2>CWEUePlxW+rlu=~H={=!bbv{OHfo@w@Ny@EfiP>UHLeXiskLAiye=w;udwy7@hK(*B#T(6M`dgtp%Mep=mf zyzE(hyprP9V%_|bI`GB7Yw)rRG@0V6dnKXf;6oLQl)vgV$T>5%*R)oo9%ac;#`AYM z*rQ7?JWe|=K0#M6ouln1zYut@^BJ!&DNz%WV3H2VTam2_I8(oHykJh|n8&$#`2t;d z@_omz4N5_BYM~IFcBMgjcNdn`d03c*&hP}eAk+}8ZOX)6W5Cg3y zfE?pm;bpRPn9o>)I}J(FdbRPNEBh0$mxrIj;d(4PuRcqM2Rq0u)mMBl2rPl;C&??f z=F2hxnn$N}Ew@n#yTAC%BXsMBKSIavxqn#T9vz4bn)r0 z(Ejj=vH0rtTXb;hmDIGPoMZt~>>aibx~B1!7UNY~SMIlNyMyle z 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 + + + + tryAfter + + NSStringLocalizedFormatKey + %#@number@ + number + + NSStringFormatValueTypeKey + ld + NSStringFormatSpecTypeKey + NSStringPluralRuleType + other + Try after %ld minutes + one + Try after %ld minute + + + + diff --git a/packages/mobile/ios/ton_keeper/Resources/Localizable/uk.lproj/PassCode.strings b/packages/mobile/ios/ton_keeper/Resources/Localizable/uk.lproj/PassCode.strings new file mode 100644 index 000000000..a42a4775e --- /dev/null +++ b/packages/mobile/ios/ton_keeper/Resources/Localizable/uk.lproj/PassCode.strings @@ -0,0 +1,5 @@ +"enter" = "Enter passcode"; +"logout" = "Log out"; +"attempts.remaining" = "Attempts remaining: %@"; +"createNew" = "Create new passcode"; +"reenter" = "Re-enter passcode"; diff --git a/packages/mobile/ios/ton_keeper/Resources/Localizable/uk.lproj/PassCode.stringsdict b/packages/mobile/ios/ton_keeper/Resources/Localizable/uk.lproj/PassCode.stringsdict new file mode 100644 index 000000000..912057480 --- /dev/null +++ b/packages/mobile/ios/ton_keeper/Resources/Localizable/uk.lproj/PassCode.stringsdict @@ -0,0 +1,22 @@ + + + + + tryAfter + + NSStringLocalizedFormatKey + %#@number@ + number + + NSStringFormatValueTypeKey + ld + NSStringFormatSpecTypeKey + NSStringPluralRuleType + other + Try after %ld minutes + one + Try after %ld minute + + + + diff --git a/packages/mobile/ios/ton_keeper/Resources/Localizable/uz.lproj/PassCode.strings b/packages/mobile/ios/ton_keeper/Resources/Localizable/uz.lproj/PassCode.strings new file mode 100644 index 000000000..a42a4775e --- /dev/null +++ b/packages/mobile/ios/ton_keeper/Resources/Localizable/uz.lproj/PassCode.strings @@ -0,0 +1,5 @@ +"enter" = "Enter passcode"; +"logout" = "Log out"; +"attempts.remaining" = "Attempts remaining: %@"; +"createNew" = "Create new passcode"; +"reenter" = "Re-enter passcode"; diff --git a/packages/mobile/ios/ton_keeper/Resources/Localizable/uz.lproj/PassCode.stringsdict b/packages/mobile/ios/ton_keeper/Resources/Localizable/uz.lproj/PassCode.stringsdict new file mode 100644 index 000000000..912057480 --- /dev/null +++ b/packages/mobile/ios/ton_keeper/Resources/Localizable/uz.lproj/PassCode.stringsdict @@ -0,0 +1,22 @@ + + + + + tryAfter + + NSStringLocalizedFormatKey + %#@number@ + number + + NSStringFormatValueTypeKey + ld + NSStringFormatSpecTypeKey + NSStringPluralRuleType + other + Try after %ld minutes + one + Try after %ld minute + + + + diff --git a/packages/mobile/ios/ton_keeper/SupportingFiles/Info.plist b/packages/mobile/ios/ton_keeper/SupportingFiles/Info.plist index 47c0122ee..00e402a62 100644 --- a/packages/mobile/ios/ton_keeper/SupportingFiles/Info.plist +++ b/packages/mobile/ios/ton_keeper/SupportingFiles/Info.plist @@ -97,6 +97,8 @@ + NSBluetoothAlwaysUsageDescription + Tonkeeper uses bluetooth to connect your hardware Ledger Nano X NSCameraUsageDescription Tonkeeper uses camera to scan QR codes NSFaceIDUsageDescription diff --git a/packages/mobile/package.json b/packages/mobile/package.json index 00531a408..8e3be1b98 100644 --- a/packages/mobile/package.json +++ b/packages/mobile/package.json @@ -32,20 +32,23 @@ "@craftzdog/react-native-buffer": "^6.0.5", "@expo/react-native-action-sheet": "^4.0.1", "@gorhom/bottom-sheet": "^4.6.0", + "@ledgerhq/hw-transport": "^6.30.6", + "@ledgerhq/react-native-hw-transport-ble": "^6.32.5", "@rainbow-me/animated-charts": "https://github.com/tonkeeper/react-native-animated-charts#737b1633c41e13da437c8e111c4aedd15bd10558", "@react-native-async-storage/async-storage": "^1.23.1", "@react-native-community/clipboard": "^1.5.1", "@react-native-community/netinfo": "^9.3.2", - "@react-native-firebase/analytics": "18.5.0", - "@react-native-firebase/app": "18.5.0", - "@react-native-firebase/crashlytics": "18.5.0", - "@react-native-firebase/messaging": "18.5.0", + "@react-native-firebase/analytics": "^20.0.0", + "@react-native-firebase/app": "^20.0.0", + "@react-native-firebase/crashlytics": "^20.0.0", + "@react-native-firebase/messaging": "^20.0.0", + "@react-native-masked-view/masked-view": "^0.3.1", "@reduxjs/toolkit": "^1.6.1", "@shopify/flash-list": "^1.5.0", - "@ton/core": "^0.53.0", - "@ton/ton": "^13.9.0", - "@tonapps/tonlogin-client": "0.2.5", - "@tonconnect/protocol": "^2.2.5", + "@ton-community/ton-ledger": "^7.0.1", + "@ton/core": "0.54.0", + "@ton/ton": "https://github.com/tonkeeper/tonkeeper-ton#build9", + "@tonconnect/protocol": "^2.2.6", "@tonkeeper/core": "0.1.0", "@tonkeeper/router": "*", "@tonkeeper/shared": "*", @@ -88,6 +91,7 @@ "react": "18.2.0", "react-native": "0.72.6", "react-native-apk-install": "0.1.0", + "react-native-ble-plx": "2.0.3", "react-native-camera": "^4.2.1", "react-native-config": "^1.5.1", "react-native-console-time-polyfill": "^1.2.3", @@ -122,7 +126,7 @@ "react-native-scrypt": "^1.2.1", "react-native-securerandom": "^1.0.1", "react-native-share": "^9.4.1", - "react-native-sse": "^1.1.0", + "react-native-sse": "^1.2.1", "react-native-svg": "^13.10.0", "react-native-svg-transformer": "^0.14.3", "react-native-system-navigation-bar": "^2.6.4", @@ -133,6 +137,7 @@ "react-query": "^3.39.3", "react-redux": "^7.2.4", "redux-saga": "^1.1.3", + "rxjs": "^7.8.1", "stream-browserify": "^3.0.0", "styled-components": "^5.3.0", "text-encoding-polyfill": "^0.6.7", diff --git a/packages/mobile/src/blockchain/legacy.ts b/packages/mobile/src/blockchain/legacy.ts index af6c63295..d877083fa 100644 --- a/packages/mobile/src/blockchain/legacy.ts +++ b/packages/mobile/src/blockchain/legacy.ts @@ -6,7 +6,7 @@ export const createLegacyWallet = (wallet: Wallet) => { const vault = Vault.fromJSON({ name: wallet.identifier, tonPubkey: wallet.pubkey, - version: wallet.config.version, + version: wallet.version, workchain: wallet.config.workchain, configPubKey: wallet.config.configPubKey, allowedDestinations: wallet.config.allowedDestinations, diff --git a/packages/mobile/src/blockchain/types.ts b/packages/mobile/src/blockchain/types.ts deleted file mode 100644 index 159825351..000000000 --- a/packages/mobile/src/blockchain/types.ts +++ /dev/null @@ -1,12 +0,0 @@ -export enum WalletVersion { - v3R1 = 0, - v3R2 = 1, - v4R1 = 2, - v4R2 = 3, -} - -export const WalletVersions = [ - WalletVersion.v3R1, - WalletVersion.v3R2, - WalletVersion.v4R2, -]; diff --git a/packages/mobile/src/blockchain/vault.ts b/packages/mobile/src/blockchain/vault.ts index 7d160d0df..bb0f5719a 100644 --- a/packages/mobile/src/blockchain/vault.ts +++ b/packages/mobile/src/blockchain/vault.ts @@ -2,10 +2,11 @@ import { Buffer } from 'buffer'; import { Ton } from '$libs/Ton'; import { wordlist } from '$libs/Ton/mnemonic/wordlist'; import { config } from '$config'; +import { WalletContractVersion } from '$wallet/WalletTypes'; -const TonWeb = require('tonweb'); +const DEFAULT_WALLET_VERSION = WalletContractVersion.v4R2; -const DEFAULT_VERSION = 'v4R2'; +const TonWeb = require('tonweb'); // Non-secret metadata of the vault necessary to generate addresses and check balances. export type VaultJSON = { @@ -57,7 +58,7 @@ export class Vault { // whether the password is required or not. We use password only for on-device storage, // and keep *this* password empty. const phrase = (await Ton.mnemonic.generateMnemonic(24)).join(' '); - return await Vault.restore(name, phrase, [DEFAULT_VERSION]); + return await Vault.restore(name, phrase, [DEFAULT_WALLET_VERSION]); } // Restores the wallet from the mnemonic phrase. @@ -117,28 +118,10 @@ export class Vault { return this.info.version; } - getWalletId() { - if (this.info.version) { - const wallet = this.tonWalletByVersion(this.info.version); - return wallet?.options?.walletId ?? null; - } - - return null; - } - public get workchain() { return this.info.workchain ?? 0; } - getLockupConfig() { - return { - wallet_type: this.info.version, - workchain: this.info.workchain, - config_pubkey: this.info.configPubKey, - allowed_destinations: this.info.allowedDestinations, - }; - } - // Encodes non-secret metadata into a JSON object for storage on disk. toJSON(): VaultJSON { return { @@ -151,13 +134,12 @@ export class Vault { }; } - // TON public key - get tonPublicKey(): Uint8Array { - return this.info.tonPubkey; - } - // TON wallet instance. get tonWallet(): any { + if (this.info.version === WalletContractVersion.v5R1) { + return null; + } + const tonweb = new TonWeb( new TonWeb.HttpProvider(config.get('tonEndpoint'), { apiKey: config.get('tonEndpointAPIKey'), @@ -166,7 +148,7 @@ export class Vault { if (this.info.version && this.info.version.substr(0, 6) === 'lockup') { return new tonweb.lockupWallet.all[this.info.version](tonweb.provider, { - publicKey: this.tonPublicKey, + publicKey: this.info.tonPubkey, wc: this.info.workchain ?? 0, config: { wallet_type: this.info.version, @@ -176,48 +158,13 @@ export class Vault { }); } - return new tonweb.wallet.all[this.info.version ?? DEFAULT_VERSION](tonweb.provider, { - publicKey: this.tonPublicKey, - wc: 0, - }); - } - - tonWalletByVersion(version: string) { - const tonweb = new TonWeb( - new TonWeb.HttpProvider(config.get('tonEndpoint'), { - apiKey: config.get('tonEndpointAPIKey'), - }), + return new tonweb.wallet.all[this.info.version ?? DEFAULT_WALLET_VERSION]( + tonweb.provider, + { + publicKey: this.info.tonPubkey, + wc: 0, + }, ); - - return new tonweb.wallet.all[version](tonweb.provider, { - publicKey: this.tonPublicKey, - wc: 0, - }); - } - - // TON address corresponding to the public key - // To convert into a human-readable form use - // Address.toString(isUserFriendly,isUrlSafe,isBounceable,isTestOnly). - async getRawTonAddress(): Promise { - return await this.tonWallet.getAddress(); - } - - // Returns an encoded address with userfriendly + urlsafe + bounceable + isTestOnly attributes. - async getTonAddress(isTestnet: boolean = false): Promise { - return (await this.tonWallet.getAddress()).toString(true, true, true, !!isTestnet); - } - - async getTonAddressByWalletVersion( - tonweb: any, - version: string, - isTestnet: boolean = false, - ) { - const wallet = new tonweb.wallet.all[version](tonweb.provider, { - publicKey: this.tonPublicKey, - wc: 0, - }); - const address = await wallet.getAddress(); - return address.toString(true, true, true, !!isTestnet); } } diff --git a/packages/mobile/src/blockchain/wallet.ts b/packages/mobile/src/blockchain/wallet.ts index 0eb668ae2..ac21136fa 100644 --- a/packages/mobile/src/blockchain/wallet.ts +++ b/packages/mobile/src/blockchain/wallet.ts @@ -1,13 +1,11 @@ import BigNumber from 'bignumber.js'; import { getUnixTime } from 'date-fns'; -import { store } from '$store'; import { UnlockedVault, Vault } from './vault'; import { Address as AddressFormatter, BASE_FORWARD_AMOUNT, ContractService, - contractVersionsMap, isActiveAccount, ONE_TON, TransactionService, @@ -25,19 +23,28 @@ import { } from '@tonkeeper/core/src/legacy'; import { tk } from '$wallet'; -import { Address, Cell, internal } from '@ton/core'; import { - NetworkOverloadedError, + Address, + Cell, + beginCell, + toNano as tonCoreToNano, + internal, + SendMode, + comment, + storeStateInit, +} from '@ton/core'; +import { + IndexerLatencyError, emulateBoc, sendBoc, getTimeoutFromLiteserverSafely, } from '@tonkeeper/shared/utils/blockchain'; import { OperationEnum, TonAPI, TypeEnum } from '@tonkeeper/core/src/TonAPI'; -import { setBalanceForEmulation } from '@tonkeeper/shared/utils/wallet'; +import { getWalletSeqno, setBalanceForEmulation } from '@tonkeeper/shared/utils/wallet'; import { WalletNetwork } from '$wallet/WalletTypes'; import { createTonApiInstance } from '$wallet/utils'; import { config } from '$config'; -import { toNano } from '$utils'; +import { Base64, toNano } from '$utils'; import { BatterySupportedTransaction } from '$wallet/managers/BatteryManager'; import { compareAddresses } from '$utils/address'; @@ -52,10 +59,9 @@ interface JettonTransferParams { recipient: Account; amountNano: string; payload: Cell | string; - vault: Vault; - secretKey?: Buffer; excessesAccount?: string | null; jettonTransferAmount?: bigint; + estimateFee?: boolean; } interface TonTransferParams { @@ -64,10 +70,8 @@ interface TonTransferParams { amount: string; payload?: Cell | string; sendMode?: number; - vault: Vault; - walletVersion?: string | null; - secretKey?: Buffer; bounce: boolean; + estimateFee?: boolean; } export class Wallet { @@ -84,30 +88,11 @@ export class Wallet { this.vault = vault; this.ton = ton; - this.getReadableAddress(); - this.tonapi = createTonApiInstance( tk.wallet.config.network === WalletNetwork.testnet, ); } - public async getReadableAddress() { - if (this.vault) { - const rawAddress = await this.vault.getRawTonAddress(); - const tkWallet = tk.wallets.get(this.name)!; - const friendlyAddress = await this.vault.getTonAddress( - tkWallet.config.network === WalletNetwork.testnet, - ); - const version = this.vault.getVersion()!; - - this.address = { - rawAddress: rawAddress.toString(false), - friendlyAddress, - version, - }; - } - } - async clean() { await tk.removeWallet(this.vault.name); } @@ -154,10 +139,6 @@ export class TonWallet { return new TonWallet(vault); } - getTonWeb() { - return this.tonweb; - } - get isTestnet(): boolean { return tk.wallet.isTestnet; } @@ -170,55 +151,12 @@ export class TonWallet { return this.vault.workchain; } - isV4() { - return this.vault.getVersion() === 'v4R2'; - } - isLockup() { const version = this.vault.getVersion(); return version && version.substr(0, 6) === 'lockup'; } - async getAddress() { - return this.vault.getTonAddress(this.isTestnet); - } - - async createStateInitBase64() { - const { stateInit } = await this.vault.tonWallet.createStateInit(); - return TonWeb.utils.bytesToBase64(await stateInit.toBoc(false)); - } - - async getAddressByWalletVersion(version: string) { - return this.vault.getTonAddressByWalletVersion(this.tonweb, version, this.isTestnet); - } - - /* - All addresses, from newest to oldest - */ - async getAllAddresses() { - const versions = ['v4R2', 'v4R1', 'v3R2', 'v3R1']; - const addresses = {}; - for (let version of versions) { - addresses[version] = await this.getAddressByWalletVersion(version); - } - return addresses; - } - - async getSeqno(address: string): Promise { - try { - const seqno = (await this.tonapi.wallet.getAccountSeqno(address)).seqno; - - return seqno; - } catch (err) { - if (err.response.status === 400) { - return 0; - } - throw err; - } - } - async createSubscription( - unlockedVault: UnlockedVault | Vault, beneficiaryAddress: string, amountNano: string, interval: number, @@ -227,12 +165,12 @@ export class TonWallet { ) { let myinfo: Account; try { - myinfo = await this.getWalletInfo(await this.getAddress()); + myinfo = await this.getWalletInfo(tk.wallet.address.ton.raw); } catch (e) { throw new Error(t('send_get_wallet_info_error')); } - const walletAddress = await unlockedVault.tonWallet.getAddress(); + const walletAddress = new TonWeb.Address(tk.wallet.address.ton.raw); const startAt = getUnixTime(new Date()); const subscription = new TonWeb.SubscriptionContract(this.tonweb.provider, { @@ -248,21 +186,6 @@ export class TonWallet { const subscriptionAddress = await subscription.getAddress(); - const addr = AddressFormatter.parse(walletAddress).toRaw(); - const seqno = await this.getSeqno(addr); - - const params: any = { - seqno: seqno || 0, - pluginWc: 0, - amount: amountNano, - stateInit: (await subscription.createStateInit()).stateInit, - body: subscription.createBody(), - }; - if (!testOnly) { - params.secretKey = await (unlockedVault as UnlockedVault).getTonPrivateKey(); - } - - const tx = unlockedVault.tonWallet.methods.deployAndInstallPlugin(params); let feeNano: BigNumber; if (['empty', 'uninit'].includes(myinfo.status)) { feeNano = new BigNumber(Ton.toNano('0.1').toString()); @@ -276,8 +199,32 @@ export class TonWallet { }; } - const queryMsg = await tx.getQuery(); - const boc = TonWeb.utils.bytesToBase64(await queryMsg.toBoc(false)); + const signer = await tk.wallet.signer.getSigner(); + + const stateInit = beginCell() + .store(storeStateInit(tk.wallet.contract.init!)) + .endCell(); + + const bodyBoc = Base64.encodeBytes(subscription.createBody().toBoc(false)); + const body = Cell.fromBase64(bodyBoc); + + const boc = await TransactionService.createTransfer(tk.wallet.contract, signer, { + seqno: await getWalletSeqno(), + messages: [ + internal({ + to: Address.parse(subscriptionAddress.toString(false)), + bounce: true, + value: amountNano, + body: beginCell() + .storeUint(1, 8) + .storeInt(0, 8) + .storeCoins(Number(amountNano)) + .storeRef(stateInit) + .storeRef(body) + .endCell(), + }), + ], + }); return { subscriptionAddress: subscriptionAddress.toString(false), @@ -288,33 +235,41 @@ export class TonWallet { }; } - async getCancelSubscriptionBoc( - unlockedVault: UnlockedVault, - subscriptionAddress: string, - ) { + async getCancelSubscriptionBoc(subscriptionAddress: string) { const isInstalled = this.isSubscriptionActive(subscriptionAddress); + if (!isInstalled) { return; } - let walletAddress = await this.getAddress(); let myinfo: Account; try { - myinfo = await this.getWalletInfo(walletAddress); + myinfo = await this.getWalletInfo(tk.wallet.address.ton.raw); } catch (e) { throw new Error(t('send_get_wallet_info_error')); } - const seqno = await this.getSeqno(walletAddress); - const tx = await unlockedVault.tonWallet.methods.removePlugin({ - secretKey: await unlockedVault.getTonPrivateKey(), - amount: Ton.toNano('0.007'), - seqno, - pluginAddress: subscriptionAddress, - }); + const pluginAddress = Address.parse(subscriptionAddress); - const query = await tx.getQuery(); - const boc = TonWeb.utils.bytesToBase64(await query.toBoc(false)); + const signer = await tk.wallet.signer.getSigner(); + + const boc = await TransactionService.createTransfer(tk.wallet.contract, signer, { + seqno: await getWalletSeqno(), + messages: [ + internal({ + to: pluginAddress, + bounce: true, + value: Ton.toNano('0.007'), + body: beginCell() + .storeUint(3, 8) + .storeInt(pluginAddress.workChain, 8) + .storeBuffer(pluginAddress.hash) + .storeCoins(Ton.toNano('0.007')) + .storeUint(0, 64) + .endCell(), + }), + ], + }); const [fee] = await this.calcFee(boc); if (fee.isGreaterThan(myinfo.balance)) { @@ -359,35 +314,51 @@ export class TonWallet { return ['empty', 'uninit', 'nonexist'].includes(info?.status ?? ''); } - createJettonTransfer({ + async createJettonTransfer({ timeout, seqno, jettonWalletAddress, recipient, amountNano, payload = '', - vault, - secretKey = Buffer.alloc(64), excessesAccount = null, jettonTransferAmount = ONE_TON, + estimateFee, }: JettonTransferParams) { - const version = vault.getVersion(); - const lockupConfig = vault.getLockupConfig(); - const contract = ContractService.getWalletContract( - contractVersionsMap[version ?? 'v4R2'], - Buffer.from(vault.tonPublicKey), - vault.workchain, - { - allowedDestinations: lockupConfig?.allowed_destinations, - }, - ); - const jettonAmount = BigInt(amountNano); - return TransactionService.createTransfer(contract, { + if (tk.wallet.isLedger && !estimateFee) { + const transfer = await tk.wallet.signer.signLedgerTransaction({ + to: Address.parse(jettonWalletAddress), + bounce: true, + amount: jettonTransferAmount, + sendMode: SendMode.PAY_GAS_SEPARATELY + SendMode.IGNORE_ERRORS, + seqno, + timeout: timeout ?? TransactionService.getTimeout(), + payload: { + type: 'jetton-transfer', + queryId: ContractService.getWalletQueryId(), + amount: jettonAmount, + destination: Address.parse(recipient.address), + responseDestination: Address.parse( + excessesAccount ?? tk.wallet.address.ton.raw, + ), + forwardAmount: BigInt(1), + forwardPayload: typeof payload === 'string' ? comment(payload) : payload, + customPayload: null, + }, + }); + + return TransactionService.externalMessage(tk.wallet.contract, seqno, transfer) + .toBoc() + .toString('base64'); + } + + const signer = await tk.wallet.signer.getSigner(estimateFee); + + return TransactionService.createTransfer(tk.wallet.contract, signer, { timeout, seqno, - secretKey, messages: [ internal({ to: Address.parse(jettonWalletAddress), @@ -405,34 +376,33 @@ export class TonWallet { } async estimateJettonFee( + jettonMaster: string, jettonWalletAddress: string, address: string, amountNano: string, - vault: Vault, payload: Cell | string = '', ) { let recipient: Account; let seqno: number; try { - const fromAddress = await this.getAddress(); recipient = await this.getWalletInfo(address); - seqno = await this.getSeqno(fromAddress); + seqno = await getWalletSeqno(); } catch (e) { throw new Error(t('send_get_wallet_info_error')); } const timeout = await getTimeoutFromLiteserverSafely(); - const boc = this.createJettonTransfer({ + const boc = await this.createJettonTransfer({ timeout, seqno, jettonWalletAddress, recipient, amountNano, payload, - vault, excessesAccount: null, jettonTransferAmount: ONE_TON, + estimateFee: true, }); let [feeNano, isBattery] = await this.calcFee( @@ -451,7 +421,6 @@ export class TonWallet { jettonWalletAddress: string, address: string, amountNano: string, - unlockedVault: UnlockedVault, payload: Cell | string = '', sendWithBattery: boolean, forwardAmount: string, @@ -460,16 +429,13 @@ export class TonWallet { let recipient: Account; let seqno: number; try { - const fromAddress = await this.getAddress(); - sender = await this.getWalletInfo(fromAddress); + sender = await this.getWalletInfo(tk.wallet.address.ton.raw); recipient = await this.getWalletInfo(address); - seqno = await this.getSeqno(fromAddress); + seqno = await getWalletSeqno(); } catch (e) { throw new Error(t('send_get_wallet_info_error')); } - const secretKey = await unlockedVault.getTonPrivateKey(); - await tk.wallet.jettons.load(); const balances = tk.wallet.jettons.state.data.jettonBalances; @@ -488,20 +454,18 @@ export class TonWallet { } const excessesAccount = sendWithBattery - ? tk.wallet.battery.excessesAccount + ? await tk.wallet.battery.getExcessesAccount() : tk.wallet.address.ton.raw; const timeout = await getTimeoutFromLiteserverSafely(); - const boc = this.createJettonTransfer({ + const boc = await this.createJettonTransfer({ timeout, seqno, jettonWalletAddress, recipient, amountNano, payload, - vault: unlockedVault, - secretKey: Buffer.from(secretKey), excessesAccount, jettonTransferAmount: BigInt(forwardAmount), }); @@ -541,7 +505,7 @@ export class TonWallet { try { await sendBoc(boc, isBattery); } catch (e) { - if (e instanceof NetworkOverloadedError) { + if (e instanceof IndexerLatencyError) { throw e; } throw new Error(t('send_publish_tx_error')); @@ -559,29 +523,38 @@ export class TonWallet { amount, payload = '', sendMode = 3, - vault, bounce, - walletVersion = null, - secretKey = Buffer.alloc(64), + estimateFee, }: TonTransferParams) { - const version = vault.getVersion(); - const lockupConfig = vault.getLockupConfig(); - const contract = ContractService.getWalletContract( - contractVersionsMap[walletVersion ?? version ?? 'v4R2'], - Buffer.from(vault.tonPublicKey), - vault.workchain, - { - lockupPubKey: lockupConfig?.config_pubkey, - allowedDestinations: lockupConfig?.allowed_destinations, - }, - ); - const timeout = await getTimeoutFromLiteserverSafely(); - return TransactionService.createTransfer(contract, { + if (tk.wallet.isLedger && !estimateFee) { + const transfer = await tk.wallet.signer.signLedgerTransaction({ + to: Address.parse(recipient.address), + bounce, + amount: tonCoreToNano(amount), + sendMode, + seqno, + timeout, + payload: + typeof payload === 'string' && payload !== '' + ? { + type: 'comment', + text: payload, + } + : undefined, + }); + + return TransactionService.externalMessage(tk.wallet.contract, seqno, transfer) + .toBoc() + .toString('base64'); + } + + const signer = await tk.wallet.signer.getSigner(estimateFee); + + return TransactionService.createTransfer(tk.wallet.contract, signer, { timeout, seqno, - secretKey, sendMode, messages: [ internal({ @@ -599,7 +572,6 @@ export class TonWallet { type: TypeEnum, address: string, amount: string, - vault: Vault, payload: string = '', ) { const opTemplate = await this.tonapi.experimental.getInscriptionOpTemplate({ @@ -614,7 +586,6 @@ export class TonWallet { return this.estimateFee( opTemplate.destination, inscriptionTransferAmount, - vault, opTemplate.comment, 3, ); @@ -623,19 +594,14 @@ export class TonWallet { async estimateFee( address: string, amount: string, - vault: Vault, payload: Cell | string = '', sendMode = 3, - walletVersion: string | null = null, ) { let recipientInfo: Account; let seqno: number; try { - const fromAddress = walletVersion - ? await this.getAddressByWalletVersion(walletVersion) - : await this.getAddress(); recipientInfo = await this.getWalletInfo(address); - seqno = await this.getSeqno(fromAddress); + seqno = await getWalletSeqno(); } catch (e) { throw new Error(t('send_get_wallet_info_error')); } @@ -646,17 +612,20 @@ export class TonWallet { amount, payload, sendMode, - vault, - walletVersion, bounce: isActiveAccount(recipientInfo.status) ? AddressFormatter.isBounceable(address) : false, + estimateFee: true, }); const [feeNano, isBattery] = await this.calcFee(boc); return [Ton.fromNano(feeNano.toString()), isBattery]; } async deploy(unlockedVault: UnlockedVault) { + if (!this.isLockup) { + throw new Error('Wallet is not lockup'); + } + const wallet = unlockedVault.tonWallet; const secretKey = await unlockedVault.getTonPrivateKey(); @@ -668,7 +637,6 @@ export class TonWallet { type: TypeEnum, address: string, amount: string, - vault: UnlockedVault, payload: string = '', ) { const opTemplate = await this.tonapi.experimental.getInscriptionOpTemplate({ @@ -683,7 +651,6 @@ export class TonWallet { return this.transfer( opTemplate.destination, inscriptionTransferAmount, - vault, opTemplate.comment, 3, ); @@ -692,25 +659,18 @@ export class TonWallet { async transfer( address: string, amount: string, - unlockedVault: UnlockedVault, payload: Cell | string = '', sendMode = 3, - walletVersion: string | null = null, ) { - const secretKey = await unlockedVault.getTonPrivateKey(); - // We need to check our seqno which is null if uninitialized. // Do not use wallet.methods.seqno().call() - it returns some garbage (85143). let fromInfo: Account; let recipientInfo: Account; let seqno: number; try { - const fromAddress = walletVersion - ? await this.getAddressByWalletVersion(walletVersion) - : await this.getAddress(); - fromInfo = await this.getWalletInfo(fromAddress); + fromInfo = await this.getWalletInfo(tk.wallet.address.ton.raw); recipientInfo = await this.getWalletInfo(address); - seqno = await this.getSeqno(fromAddress); + seqno = await getWalletSeqno(); } catch (e) { throw new Error(t('send_get_wallet_info_error')); } @@ -723,9 +683,6 @@ export class TonWallet { amount, payload, sendMode, - vault: unlockedVault, - walletVersion, - secretKey: Buffer.from(secretKey), // We should keep bounce flag from user input. We should check contract status till Jan 1, 2024 according to internal Address reform roadmap bounce: isActiveAccount(recipientInfo.status) ? AddressFormatter.isBounceable(address) @@ -755,7 +712,7 @@ export class TonWallet { try { await sendBoc(boc, false); } catch (e) { - if (e instanceof NetworkOverloadedError) { + if (e instanceof IndexerLatencyError) { throw e; } throw new Error(t('send_publish_tx_error')); @@ -771,10 +728,6 @@ export class TonWallet { return await this.accountsApi.getAccount({ accountId: address }); } - async getTonPublicKey() { - return this.vault.tonPublicKey; - } - async getPublicKeyByAddress(address: string): Promise { const { publicKey } = await this.accountsApi.getPublicKeyByAccountID({ accountId: address, @@ -804,19 +757,4 @@ export class TonWallet { throw e; } } - - async getBalance(): Promise { - try { - const account = await this.vault.getTonAddress(this.isTestnet); - const balance = (await this.blockchainApi.getRawAccount({ accountId: account })) - .balance; - return Ton.fromNano(balance); - } catch (e) { - if (e?.response?.status === 404) { - return Ton.fromNano(0); - } - - throw e; - } - } } diff --git a/packages/mobile/src/components/LedgerConnectionSteps/LedgerConnectionSteps.tsx b/packages/mobile/src/components/LedgerConnectionSteps/LedgerConnectionSteps.tsx new file mode 100644 index 000000000..735cca486 --- /dev/null +++ b/packages/mobile/src/components/LedgerConnectionSteps/LedgerConnectionSteps.tsx @@ -0,0 +1,144 @@ +import { FC, useCallback } from 'react'; +import { Steezy } from '@tonkeeper/uikit/src/styles'; +import { Text, View, Icon, Loader, TouchableOpacity } from '@tonkeeper/uikit'; +import { t } from '@tonkeeper/shared/i18n'; +import { LedgerConnectionCurrentStep } from './types'; +import { LegderView } from './LedgerView'; +import { Linking, Platform } from 'react-native'; + +const LEDGER_LIVE_STORE_URL = Platform.select({ + ios: 'https://apps.apple.com/app/ledger-live/id1361671700', + android: 'https://play.google.com/store/apps/details?id=com.ledger.live', +}); + +const BUTTON_HIT_SLOP = { + top: 12, + bottom: 12, + left: 12, + right: 12, +}; + +const StepIcon: FC<{ state: 'future' | 'active' | 'completed' }> = ({ state }) => { + let content = ; + if (state === 'future') { + content = ; + } + + if (state === 'active') { + content = ; + } + + return {content}; +}; + +interface Props { + showConfirmTxStep?: boolean; + currentStep: LedgerConnectionCurrentStep; +} + +export const LedgerConnectionSteps: FC = (props) => { + const { currentStep, showConfirmTxStep } = props; + + const openLegderLive = useCallback(async () => { + try { + await Linking.openURL('ledgerlive://myledger?installApp=TON'); + } catch { + Linking.openURL(LEDGER_LIVE_STORE_URL!); + } + }, []); + + return ( + + + + + + + + {t('ledger.connect')} + + + + + + + + {t('ledger.open_ton_app')} + + {!showConfirmTxStep ? ( + + + {t('ledger.install_ton_app')} + + + ) : null} + + + {showConfirmTxStep ? ( + + + + + {t('ledger.confirm_tx')} + + + + ) : null} + + + ); +}; + +const styles = Steezy.create(({ colors, corners }) => ({ + container: { + backgroundColor: colors.backgroundContent, + overflow: 'hidden', + borderRadius: corners.medium, + marginBottom: 16, + }, + stepsContainer: { + paddingHorizontal: 16, + paddingVertical: 20, + }, + step: { + flexDirection: 'row', + marginBottom: 8, + alignItems: 'flex-start', + }, + stepText: { + flex: 1, + paddingLeft: 8, + }, + iconContainer: { + marginTop: 2, + }, +})); diff --git a/packages/mobile/src/components/LedgerConnectionSteps/LedgerView.tsx b/packages/mobile/src/components/LedgerConnectionSteps/LedgerView.tsx new file mode 100644 index 000000000..cc10f3d0e --- /dev/null +++ b/packages/mobile/src/components/LedgerConnectionSteps/LedgerView.tsx @@ -0,0 +1,164 @@ +import { Icon, Text, View, ns, useTheme } from '@tonkeeper/uikit'; +import { FC } from 'react'; +import Svg, { Path, Circle } from 'react-native-svg'; +import { LedgerConnectionCurrentStep } from './types'; +import { Steezy } from '$styles'; +import Animated, { + useAnimatedStyle, + withDelay, + withTiming, +} from 'react-native-reanimated'; +import { Platform } from 'react-native'; + +const BluetoothIcon = () => { + return ( + + + + + ); +}; + +const LedgerPicture = () => { + const theme = useTheme(); + return ( + + + + + + + + + + ); +}; + +const LEDGER_POS = ns(24); +const LEDGER_CONNECTED_POS = ns(-42); + +const fontFamily = Platform.select({ + ios: 'SFMono-Medium', + android: 'RobotoMono-Medium', +}); + +interface Props { + currentStep: LedgerConnectionCurrentStep; + showConfirmTxStep?: boolean; +} + +export const LegderView: FC = (props) => { + const { currentStep, showConfirmTxStep } = props; + + const bluetoothStyle = useAnimatedStyle( + () => ({ + opacity: currentStep === 'connect' ? withDelay(200, withTiming(1)) : withTiming(0), + }), + [currentStep], + ); + + const ledgerStyle = useAnimatedStyle( + () => ({ + transform: [ + { + translateX: + currentStep === 'connect' + ? withDelay(200, withTiming(LEDGER_POS)) + : withTiming(LEDGER_CONNECTED_POS, { duration: 350 }), + }, + ], + }), + [currentStep], + ); + + const textStyle = useAnimatedStyle( + () => ({ + opacity: currentStep === 'connect' ? withTiming(0) : withDelay(150, withTiming(1)), + }), + [currentStep], + ); + + return ( + + + + + + + + + {currentStep === 'all-completed' && showConfirmTxStep ? ( + + ) : ( + + {currentStep === 'confirm-tx' ? 'Review' : 'TON ready'} + + )} + + + + + ); +}; + +const styles = Steezy.create(({ colors }) => ({ + container: { + width: '100%', + paddingTop: 40, + paddingLeft: 40, + paddingBottom: 16, + flexDirection: 'row', + alignItems: 'center', + }, + ledger: { + position: 'relative', + transform: [{ translateX: LEDGER_POS }], + }, + textWrapper: { + position: 'absolute', + top: 8, + left: 52, + }, + textContainer: { + width: 89, + height: 40, + backgroundColor: colors.backgroundContent, + justifyContent: 'center', + alignItems: 'center', + borderRadius: 2, + }, +})); diff --git a/packages/mobile/src/components/LedgerConnectionSteps/index.ts b/packages/mobile/src/components/LedgerConnectionSteps/index.ts new file mode 100644 index 000000000..e9ce411be --- /dev/null +++ b/packages/mobile/src/components/LedgerConnectionSteps/index.ts @@ -0,0 +1 @@ +export * from './LedgerConnectionSteps'; diff --git a/packages/mobile/src/components/LedgerConnectionSteps/types.ts b/packages/mobile/src/components/LedgerConnectionSteps/types.ts new file mode 100644 index 000000000..bb1e95115 --- /dev/null +++ b/packages/mobile/src/components/LedgerConnectionSteps/types.ts @@ -0,0 +1,5 @@ +export type LedgerConnectionCurrentStep = + | 'connect' + | 'open-ton' + | 'confirm-tx' + | 'all-completed'; diff --git a/packages/mobile/src/components/index.ts b/packages/mobile/src/components/index.ts index d63c51d3d..9c1184d9f 100644 --- a/packages/mobile/src/components/index.ts +++ b/packages/mobile/src/components/index.ts @@ -1,2 +1,3 @@ export * from './CardsWidget'; export * from './SendScreenBatteryWidget'; +export * from './LedgerConnectionSteps'; diff --git a/packages/mobile/src/config/AppConfig.ts b/packages/mobile/src/config/AppConfig.ts index 6c8685bfb..d2a5acb8f 100644 --- a/packages/mobile/src/config/AppConfig.ts +++ b/packages/mobile/src/config/AppConfig.ts @@ -14,7 +14,7 @@ type AppConfigOptions = { }; export class AppConfig { - static readonly CONFIG_VERSION = 3; // change it, if config needs to be force updated + static readonly CONFIG_VERSION = 4; // change it, if config needs to be force updated private clientConfigStorageKey = `__client-config__v${AppConfig.CONFIG_VERSION}`; private serverConfigStorageKey = `__server-config__v${AppConfig.CONFIG_VERSION}`; diff --git a/packages/mobile/src/config/index.ts b/packages/mobile/src/config/index.ts index d826cfaee..13999e81d 100644 --- a/packages/mobile/src/config/index.ts +++ b/packages/mobile/src/config/index.ts @@ -18,6 +18,9 @@ export type AppConfigVars = { tonNFTsMarketplaceEndpoint: string; tonapiMainnetHost: string; accountExplorer: string; + telegram_ru: string; + telegram_global: string; + telegram_farsi: string; subscriptionsHost: string; cachedMediaEndpoint: string; cachedMediaKey: string; @@ -25,7 +28,8 @@ export type AppConfigVars = { NFTOnExplorerUrl: string; flags: Record; directSupportUrl: string; - tonkeeperNewsUrl: string; + faqUrlRu: string; + faqUrl: string; stakingInfoUrl: string; tonCommunityUrl: string; tonCommunityChatUrl: string; @@ -46,12 +50,16 @@ export type AppConfigVars = { batteryHost: string; batteryTestnetHost: string; + batteryRefundEndpoint: string; batteryMeanFees: string; batteryReservedAmount: string; + batteryMaxInputAmount: string; batteryMeanPrice_swap: string; batteryMeanPrice_jetton: string; batteryMeanPrice_nft: string; + scamEndpoint: string; + holdersAppEndpoint: string; holdersService: string; aptabaseEndpoint: string; @@ -62,10 +70,34 @@ export type AppConfigVars = { disable_battery_send: boolean; disable_show_unverified_token: boolean; disable_battery_promo_module: boolean; + disable_battery_crypto_recharge_module: boolean; + disable_signer: boolean; disable_tonstakers: boolean; disable_holders_cards: boolean; exclude_jetton_chart_periods: boolean; devmode_enabled: boolean; + + signer_store_url: string; + signer_about_url: string; + + tonkeeper_pro_url: string; + notcoin_jetton_master: string; + notcoin_nft_collection: string; + notcoin_bot_url: string; + notcoin_burn_addresses: string[]; + notcoin_burn_date: number; + notcoin_burn: boolean; + + web_swaps_url: string; + + swaps_referral_address: string; + + enable_jusdt_migration: boolean; + usdt_jetton_master: string; + jusdt_jetton_master: string; + + gasless_enabled: boolean; + v5_beta: boolean; }; const defaultConfig: Partial = { @@ -89,10 +121,15 @@ const defaultConfig: Partial = { tronapiHost: 'https://tron.tonkeeper.com', tronapiTestnetHost: 'https://testnet-tron.tonkeeper.com', + faqUrl: 'https://tonkeeper.helpscoutdocs.com', + faqUrlRu: 'https://tonkeeperru.helpscoutdocs.com', + batteryHost: 'https://battery.tonkeeper.com', batteryTestnetHost: 'https://testnet-battery.tonkeeper.com', + batteryRefundEndpoint: 'https://battery-refund-app.vercel.app', batteryMeanFees: '0.0055', batteryReservedAmount: '0.3', + batteryMaxInputAmount: '3', batteryMeanPrice_swap: '0.22', batteryMeanPrice_jetton: '0.06', batteryMeanPrice_nft: '0.03', @@ -101,12 +138,45 @@ const defaultConfig: Partial = { disable_battery_send: false, disable_battery_iap_module: Platform.OS === 'android', // Enable for iOS, disable for Android disable_battery_promo_module: true, + disable_battery_crypto_recharge_module: false, + disable_signer: true, + + scamEndpoint: 'https://scam.tonkeeper.com', + + telegram_global: 'https://t.me/tonkeeper_news', + telegram_ru: 'https://t.me/tonkeeper_ru', + telegram_farsi: 'https://t.me/tonkeeper_farsi', disable_show_unverified_token: false, disable_tonstakers: false, disable_holders_cards: true, exclude_jetton_chart_periods: true, devmode_enabled: false, + + signer_store_url: 'https://play.google.com/store/apps/details?id=com.tonapps.signer', + + tonkeeper_pro_url: 'https://tonkeeper.com/pro', + notcoin_jetton_master: + '0:2f956143c461769579baef2e32cc2d7bc18283f40d20bb03e432cd603ac33ffc', + notcoin_nft_collection: + '0:e6923eb901bfe6d1a65a5bc2292b0e2462a220213c3f1d1b2d60491543a34860', + notcoin_bot_url: 'https://t.me/notcoin_bot', + + web_swaps_url: 'https://swap.tonkeeper.com', + + swaps_referral_address: + '0:6c8eef549f003f4f421d009aede507e3f7535667b639e98fd09b55c8fc0e23cb', + + usdt_jetton_master: + '0:b113a994b5024a16719f69139328eb759596c38a25f59028b146fecdc3621dfe', + + jusdt_jetton_master: + '0:729c13b6df2c07cbf0a06ab63d34af454f3d320ec1bcd8fb5c6d24d0806a17c2', + + enable_jusdt_migration: true, + + gasless_enabled: true, + v5_beta: true, }; export const config = new AppConfig({ diff --git a/packages/mobile/src/core/AccessConfirmation/AccessConfirmation.tsx b/packages/mobile/src/core/AccessConfirmation/AccessConfirmation.tsx index 316db2832..0ce977d0e 100644 --- a/packages/mobile/src/core/AccessConfirmation/AccessConfirmation.tsx +++ b/packages/mobile/src/core/AccessConfirmation/AccessConfirmation.tsx @@ -75,7 +75,9 @@ export const AccessConfirmation: FC = () => { try { const promise = getCurrentConfirmationVaultPromise(); const walletForUnlock = - isUnlock || wallet.isWatchOnly ? tk.walletForUnlock : wallet; + isUnlock || wallet.isWatchOnly || wallet.isExternal + ? tk.walletForUnlock + : wallet; const mnemonic = await vault.exportWithPasscode( walletForUnlock.identifier, pin, @@ -123,7 +125,8 @@ export const AccessConfirmation: FC = () => { try { const promise = getCurrentConfirmationVaultPromise(); - const walletForUnlock = isUnlock ? tk.walletForUnlock : wallet; + const walletForUnlock = + isUnlock || wallet.isWatchOnly || wallet.isExternal ? tk.walletForUnlock : wallet; const passcode = await vault.exportPasscodeWithBiometry(); const mnemonic = await vault.exportWithPasscode( walletForUnlock.identifier, diff --git a/packages/mobile/src/core/BatterySend/BatterySend.tsx b/packages/mobile/src/core/BatterySend/BatterySend.tsx new file mode 100644 index 000000000..8f64302c6 --- /dev/null +++ b/packages/mobile/src/core/BatterySend/BatterySend.tsx @@ -0,0 +1,536 @@ +import React, { useCallback, useMemo, useRef, useState } from 'react'; +import { + Button, + HeaderSwitch, + Icon, + IconNames, + KeyboardSpacer, + List, + Radio, + Screen, + Spacer, + Steezy, + Toast, + TouchableOpacity, + View, +} from '@tonkeeper/uikit'; +import { AddressInput } from '$core/Send/steps/AddressStep/components'; +import { SendAmount, SendRecipient } from '$core/Send/Send.interface'; +import { AmountInput } from '$shared/components'; +import { TextInput } from 'react-native-gesture-handler'; +import { asyncDebounce, isTransferOp, parseLocaleNumber, parseTonLink } from '$utils'; +import { Address, AmountFormatter, ContractService, delay } from '@tonkeeper/core'; +import TonWeb from 'tonweb'; +import { formatter } from '$utils/formatter'; +import { Tonapi } from '$libs/Tonapi'; +import BigNumber from 'bignumber.js'; +import { config } from '$config'; +import { tk } from '$wallet'; +import Animated, { FadeIn, FadeOut } from 'react-native-reanimated'; +import { openSignRawModal } from '$core/ModalContainer/NFTOperations/Modals/SignRawModal'; +import { RouteProp } from '@react-navigation/native'; +import { AppStackParamList } from '$navigation/AppStack'; +import { AppStackRouteNames } from '$navigation'; +import { useBatteryRechargeMethods } from '@tonkeeper/shared/query/hooks'; +import { useRechargeMethod } from '$core/BatterySend/hooks/useRechargeMethod'; +import { beginCell } from '@ton/core'; +import { useExternalState } from '@tonkeeper/shared/hooks/useExternalState'; +import { useNavigation } from '@tonkeeper/router'; +import { openSelectRechargeMethodModal } from '@tonkeeper/shared/modals/SelectRechargeMethodModal'; +import { RechargeMethodsTypeEnum } from '@tonkeeper/core/src/BatteryAPI'; +import { compareAddresses } from '$utils/address'; +import { AnimatedScrollView } from 'react-native-reanimated/lib/typescript/reanimated2/component/ScrollView'; +import { BatteryPackItem } from '$core/BatterySend/components'; +import { t } from '@tonkeeper/shared/i18n'; +import { Keyboard, Platform } from 'react-native'; +import { useBalancesState } from '@tonkeeper/shared/hooks'; + +let dnsAbortController: null | AbortController = null; + +export interface BatterySendProps { + route: RouteProp; +} + +export const BatterySend: React.FC = ({ route }) => { + const navigation = useNavigation(); + + const packs: { icon: IconNames; key: string; tonAmount: string }[] = useMemo( + () => [ + { + icon: 'ic-battery-100-44', + key: 'large', + tonAmount: new BigNumber(config.get('batteryMeanFees')) + .multipliedBy(400) + .toString(), + }, + { + icon: 'ic-battery-50-44', + key: 'medium', + tonAmount: new BigNumber(config.get('batteryMeanFees')) + .multipliedBy(250) + .toString(), + }, + { + icon: 'ic-battery-25-44', + key: 'small', + tonAmount: new BigNumber(config.get('batteryMeanFees')) + .multipliedBy(150) + .toString(), + }, + ], + [], + ); + + const { recipient: initialRecipientAddress, jettonMaster: initialJettonMaster } = + route.params; + const [recipient, setRecipient] = useState( + initialRecipientAddress + ? { address: initialRecipientAddress, blockchain: 'ton' } + : null, + ); + const { methods } = useBatteryRechargeMethods(); + const textInputRef = useRef(null); + const [dnsLoading, setDnsLoading] = useState(false); + const [isManualAmountInput, setIsManualAmountInput] = useState(false); + + const batteryState = useExternalState(tk.wallet.battery.state, (state) => state); + const shouldMinusReservedAmount = + batteryState.reservedBalance === '0' || !initialRecipientAddress; + + const hasBatteryBalance = !!(batteryState.balance && batteryState.balance !== '0'); + + const tonBalance = useBalancesState().ton; + const hasEnoughTonBalance = new BigNumber(tonBalance).isGreaterThanOrEqualTo('0.1'); + + const [selectedJettonMaster, setSelectedJettonMaster] = useState( + initialJettonMaster, + ); + + const selectedMethod = useMemo(() => { + if (selectedJettonMaster === undefined) { + return methods.find((method) => method.type === RechargeMethodsTypeEnum.Ton)!; + } + return methods.find((method) => method.jetton_master === selectedJettonMaster)!; + }, [methods, selectedJettonMaster]); + const rechargeMethod = useRechargeMethod(selectedMethod); + + const [amount, setAmount] = useState({ + value: formatter.format('0', { + decimals: rechargeMethod.decimals, + }), + all: false, + }); + + const handleContinue = useCallback(async () => { + Keyboard.dismiss(); + const parsedAmount = parseLocaleNumber(amount.value); + + if (BigNumber(parsedAmount).isGreaterThan(rechargeMethod.maxInputAmount)) { + return Toast.fail( + t('battery.max_input_amount', { + amount: formatter.format(rechargeMethod.maxInputAmount, { + decimals: 0, + currency: rechargeMethod.symbol, + forceRespectDecimalPlaces: true, + }), + }), + ); + } + + const commentCell = initialRecipientAddress + ? undefined + : beginCell() + .storeUint(0, 32) + .storeStringTail(recipient?.address ?? '') + .endCell(); + + if (rechargeMethod.isTon) { + return await openSignRawModal( + { + messages: [ + { + amount: AmountFormatter.toNano(parsedAmount, rechargeMethod.decimals), + address: await tk.wallet.battery.getFundReceiver(), + payload: commentCell && commentCell.toBoc().toString('base64'), + }, + ], + }, + {}, + () => Toast.success(t('battery.please_wait')), + ); + } + + const jettonBalances = tk.wallet.jettons.state.data.jettonBalances; + + const relatedJettonBalance = jettonBalances.find((jettonBalance) => + compareAddresses(jettonBalance.jettonAddress, selectedJettonMaster), + )!; + + const jettonTransferPayload = ContractService.createJettonTransferBody({ + jettonAmount: Number(AmountFormatter.toNano(parsedAmount, rechargeMethod.decimals)), + receiverAddress: await tk.wallet.battery.getFundReceiver(), + excessesAddress: tk.wallet.address.ton.raw, + forwardBody: commentCell, + }); + + const sendWithBattery = + hasBatteryBalance || + !!( + rechargeMethod.min_bootstrap_value && + new BigNumber(parsedAmount).isGreaterThanOrEqualTo( + rechargeMethod.min_bootstrap_value, + ) + ); + + await openSignRawModal( + { + messages: [ + { + amount: AmountFormatter.toNano('0.1', 9), + address: relatedJettonBalance.walletAddress, + payload: jettonTransferPayload.toBoc().toString('base64'), + }, + ], + }, + { experimentalWithBattery: sendWithBattery, forceRelayerUse: true }, + () => Toast.success(t('battery.please_wait')), + ); + }, [ + amount.value, + hasBatteryBalance, + initialRecipientAddress, + rechargeMethod.decimals, + rechargeMethod.isTon, + rechargeMethod.maxInputAmount, + rechargeMethod.min_bootstrap_value, + rechargeMethod.symbol, + recipient?.address, + selectedJettonMaster, + ]); + + const scrollRef = useRef(); + + const handleAmountInputFocus = async () => { + await delay(250); + scrollRef.current?.scrollToEnd(); + }; + + const handleAmountSelect = useCallback( + (selectedAmount: undefined | number) => () => { + if (!selectedAmount) { + setAmount({ value: '0', all: false }); + setIsManualAmountInput(true); + } else { + setAmount({ + value: formatter.format(selectedAmount, { decimals: rechargeMethod.decimals }), + all: false, + }); + setIsManualAmountInput(false); + } + }, + [rechargeMethod.decimals], + ); + + const getAddressByDomain = useMemo( + () => + asyncDebounce(async (value: string, signal: AbortSignal) => { + try { + const domain = value.toLowerCase(); + const resolvedDomain = await Tonapi.resolveDns(domain, signal); + + if (resolvedDomain === 'aborted') { + return 'aborted'; + } else if (resolvedDomain?.wallet?.address) { + const isWallet = !!resolvedDomain?.wallet?.account?.is_wallet; + return new TonWeb.Address(resolvedDomain.wallet.address).toString( + true, + true, + !isWallet, + ) as string; + } + + return null; + } catch (e) { + console.log('err', e); + + return null; + } + }, 1000), + [], + ); + + const updateRecipient = useCallback( + async (value: string) => { + setRecipient(null); + + if (value.length === 0) { + setRecipient(null); + + return false; + } + + try { + const link = parseTonLink(value); + + if (link.match && isTransferOp(link.operation) && Address.isValid(link.address)) { + if (link.query.bin) { + return false; + } + + value = link.address; + } + + if (dnsAbortController) { + dnsAbortController.abort(); + dnsAbortController = null; + setDnsLoading(false); + } + + if (Address.isValid(value)) { + setRecipient({ blockchain: 'ton', address: value }); + return true; + } + + const domain = value.toLowerCase(); + + if (!TonWeb.Address.isValid(domain) && domain.indexOf('.') !== -1) { + setDnsLoading(true); + const abortController = new AbortController(); + dnsAbortController = abortController; + + const resolvedDomain = await getAddressByDomain(domain, abortController.signal); + + if (resolvedDomain === 'aborted') { + setDnsLoading(false); + dnsAbortController = null; + return true; + } else if (resolvedDomain) { + setRecipient({ address: resolvedDomain, domain, blockchain: 'ton' }); + setDnsLoading(false); + dnsAbortController = null; + return true; + } else { + setDnsLoading(false); + dnsAbortController = null; + } + } + + setRecipient(null); + + return false; + } catch (e) { + return false; + } + }, + [getAddressByDomain], + ); + + const renderFlashIcon = useCallback( + (size: 'small' | 'large') => ( + + + + ), + [], + ); + + const handleOpenSelectRechargeMethod = useCallback(() => { + Keyboard.dismiss(); + openSelectRechargeMethodModal( + selectedJettonMaster, + (selected: string | undefined) => { + setAmount({ value: '0', all: false }); + setIsManualAmountInput(false); + setSelectedJettonMaster(selected); + }, + ); + }, [selectedJettonMaster]); + + const localeAmount = parseLocaleNumber(amount.value); + const isZero = new BigNumber(localeAmount).isZero(); + const isLessThanMinimum = shouldMinusReservedAmount + ? new BigNumber(localeAmount).isLessThan(rechargeMethod.minInputAmount) + : false; + const isGreaterThanBalance = new BigNumber(localeAmount).isGreaterThan( + rechargeMethod.balance, + ); + + return ( + <> + + + + + + + + + + } + isModal={Platform.OS === 'ios'} + title={ + initialRecipientAddress + ? t('battery.recharge_by_crypto.title') + : t('battery.recharge_by_crypto.title_gift') + } + /> + + + + {!initialRecipientAddress ? ( + null} + /> + ) : null} + + {packs.map((pack) => ( + + ))} + + } + rightContent={ + null} isSelected={isManualAmountInput} /> + } + title={t('battery.recharge_by_crypto.other.title')} + subtitle={t('battery.recharge_by_crypto.other.subtitle')} + /> + + {isManualAmountInput && ( + + + + + + )} + ); } - }, [handleUpdateStatus, modalState, translateWithPrefix]); + }, [currentStatus, handleUpdateStatus, modalState, translateWithPrefix]); return ( diff --git a/packages/mobile/src/core/ModalContainer/CreateSubscription/CreateSubscription.tsx b/packages/mobile/src/core/ModalContainer/CreateSubscription/CreateSubscription.tsx index 9fa748ae4..d446f0c4b 100644 --- a/packages/mobile/src/core/ModalContainer/CreateSubscription/CreateSubscription.tsx +++ b/packages/mobile/src/core/ModalContainer/CreateSubscription/CreateSubscription.tsx @@ -83,7 +83,6 @@ export const CreateSubscription: FC = ({ if (info && !subscription) { wallet!.ton .createSubscription( - wallet!.vault, info.address, info.amountNano, info.intervalSec, diff --git a/packages/mobile/src/core/ModalContainer/LinkingDomainModal.tsx b/packages/mobile/src/core/ModalContainer/LinkingDomainModal.tsx index 2f6dce143..5c758e5ed 100644 --- a/packages/mobile/src/core/ModalContainer/LinkingDomainModal.tsx +++ b/packages/mobile/src/core/ModalContainer/LinkingDomainModal.tsx @@ -6,20 +6,19 @@ import { Base64, truncateDecimal } from '$utils'; import { debugLog } from '$utils/debugLog'; import React, { useEffect } from 'react'; import { ActionFooter, useActionFooter } from './NFTOperations/NFTOperationFooter'; -import { useUnlockVault } from './NFTOperations/useUnlockVault'; import * as S from './NFTOperations/NFTOperations.styles'; import BigNumber from 'bignumber.js'; import { Ton } from '$libs/Ton'; import { TouchableOpacity } from 'react-native'; -import { store, Toast } from '$store'; -import { Wallet } from 'blockchain'; +import { Toast } from '$store'; import { Modal } from '@tonkeeper/uikit'; import { push } from '$navigation/imperative'; import { SheetActions } from '@tonkeeper/router'; import { openReplaceDomainAddress } from './NFTOperations/ReplaceDomainAddressModal'; -import { Address } from '@tonkeeper/core'; +import { Address, TransactionService } from '@tonkeeper/core'; import { tk } from '$wallet'; +import { getWalletSeqno } from '@tonkeeper/shared/utils/wallet'; const TonWeb = require('tonweb'); @@ -32,10 +31,6 @@ interface LinkingDomainModalProps { } export class LinkingDomainActions { - /** - * Wallet instance - */ - private wallet: Wallet; /** * Transfer amount in nanocoins. Will be attached to transfer */ @@ -50,7 +45,6 @@ export class LinkingDomainActions { public walletAddress: string | undefined; constructor(domainAddress: string, walletAddress?: string) { - this.wallet = store.getState().wallet.wallet; this.domainAddress = domainAddress; this.walletAddress = walletAddress; } @@ -60,7 +54,7 @@ export class LinkingDomainActions { */ public async calculateFee() { try { - const boc = await this.createBoc(); + const boc = await this.createBoc(true); const feeInfo = await tk.wallet.tonapi.wallet.emulateMessageToWallet({ boc }); const feeNano = new BigNumber(feeInfo.event.extra).multipliedBy(-1); @@ -78,10 +72,7 @@ export class LinkingDomainActions { /** * Creates boc with DNS-record */ - public async createBoc(secretKey?: Uint8Array) { - const curWallet = this.wallet.vault.tonWallet; - const seqno = await this.wallet.ton.getSeqno(await this.wallet.ton.getAddress()); - + public async createBoc(isEstimate?: boolean) { const address = this.walletAddress && new TonWeb.Address(this.walletAddress); const payload = await TonWeb.dns.DnsItem.createChangeContentEntryBody({ @@ -89,18 +80,22 @@ export class LinkingDomainActions { value: address ? TonWeb.dns.createSmartContractAddressRecord(address) : null, }); - const tx = curWallet.methods.transfer({ - toAddress: this.domainAddress, - amount: this.transferAmount, - seqno: seqno, - payload, + const payloadBoc = Base64.encodeBytes(await payload.toBoc(false)); + + const signer = await tk.wallet.signer.getSigner(isEstimate); + + const boc = await TransactionService.createTransfer(tk.wallet.contract, signer, { + messages: TransactionService.parseSignRawMessages([ + { + address: this.domainAddress, + amount: this.transferAmount, + payload: payloadBoc, + }, + ]), sendMode: 3, - secretKey, + seqno: await getWalletSeqno(tk.wallet), }); - const queryMsg = await tx.getQuery(); - const boc = Base64.encodeBytes(await queryMsg.toBoc(false)); - return boc; } } @@ -127,15 +122,11 @@ export const LinkingDomainModal: React.FC = ({ linkingActions.updateWalletAddress(walletAddress); }, [walletAddress]); - const unlockVault = useUnlockVault(); const handleConfirm = onConfirm(async ({ startLoading }) => { - const vault = await unlockVault(); - const privateKey = await vault.getTonPrivateKey(); - startLoading(); setIsDisabled(true); - const boc = await linkingActions.createBoc(privateKey); + const boc = await linkingActions.createBoc(); await tk.wallet.tonapi.blockchain.sendBlockchainMessage({ boc }, { format: 'text' }); }); diff --git a/packages/mobile/src/core/ModalContainer/NFTOperations/Modals/SignRawModal.tsx b/packages/mobile/src/core/ModalContainer/NFTOperations/Modals/SignRawModal.tsx index 1db31d942..780bc9d61 100644 --- a/packages/mobile/src/core/ModalContainer/NFTOperations/Modals/SignRawModal.tsx +++ b/packages/mobile/src/core/ModalContainer/NFTOperations/Modals/SignRawModal.tsx @@ -1,23 +1,22 @@ import React, { memo, useEffect, useMemo } from 'react'; import { NFTOperationFooter, useNFTOperationState } from '../NFTOperationFooter'; import { SignRawMessage, SignRawParams, TxBodyOptions } from '../TXRequest.types'; -import { useUnlockVault } from '../useUnlockVault'; import { calculateMessageTransferAmount, delay } from '$utils'; import { debugLog } from '$utils/debugLog'; import { t } from '@tonkeeper/shared/i18n'; import { Toast } from '$store'; import { + Icon, + isAndroid, List, + ListItemContent, Modal, Spacer, Steezy, Text, + TouchableOpacity, View, WalletIcon, - isAndroid, - Icon, - ListItemContent, - TouchableOpacity, } from '@tonkeeper/uikit'; import { push } from '$navigation/imperative'; import { SheetActions, useNavigation } from '@tonkeeper/router'; @@ -28,17 +27,11 @@ import { import { TonConnectRemoteBridge } from '$tonconnect/TonConnectRemoteBridge'; import { formatter } from '$utils/formatter'; import { tk } from '$wallet'; -import { MessageConsequences } from '@tonkeeper/core/src/TonAPI'; -import { - Address, - ContractService, - contractVersionsMap, - TransactionService, -} from '@tonkeeper/core'; +import { ActionStatusEnum, MessageConsequences } from '@tonkeeper/core/src/TonAPI'; +import { Address, TransactionService } from '@tonkeeper/core'; import { ActionListItemByType } from '@tonkeeper/shared/components/ActivityList/ActionListItemByType'; import { useGetTokenPrice } from '$hooks/useTokenPrice'; import { formatValue, getActionTitle } from '@tonkeeper/shared/utils/signRaw'; -import { Buffer } from 'buffer'; import { trackEvent } from '$utils/stats'; import { Events, SendAnalyticsFrom } from '$store/models'; import { getWalletSeqno, setBalanceForEmulation } from '@tonkeeper/shared/utils/wallet'; @@ -56,12 +49,14 @@ import { ModalStackRouteNames } from '$navigation'; import { CanceledActionError } from '$core/Send/steps/ConfirmStep/ActionErrors'; import { emulateBoc, + emulateBocWithRelayer, getTimeoutFromLiteserverSafely, sendBoc, } from '@tonkeeper/shared/utils/blockchain'; import { openAboutRiskAmountModal } from '@tonkeeper/shared/modals/AboutRiskAmountModal'; -import { toNano } from '@ton/core'; +import { MessageRelaxed, toNano } from '@ton/core'; import BigNumber from 'bignumber.js'; +import { Wallet } from '$wallet/Wallet'; interface SignRawModalProps { consequences?: MessageConsequences; @@ -93,7 +88,6 @@ export const SignRawModal = memo((props) => { } const { footerRef, onConfirm } = useNFTOperationState(options, wallet); - const unlockVault = useUnlockVault(); const fiatCurrency = useWalletCurrency(); const getTokenPrice = useGetTokenPrice(); @@ -108,29 +102,27 @@ export const SignRawModal = memo((props) => { await delay(200); throw new CanceledActionError(); } - const vault = await unlockVault(wallet.identifier); - const privateKey = await vault.getTonPrivateKey(); startLoading(); - const contract = ContractService.getWalletContract( - contractVersionsMap[vault.getVersion() ?? 'v4R2'], - Buffer.from(vault.tonPublicKey), - vault.workchain, - ); - const timeout = await getTimeoutFromLiteserverSafely(); - const boc = TransactionService.createTransfer(contract, { - timeout, - messages: TransactionService.parseSignRawMessages( - params.messages, - isBattery ? tk.wallet.battery.excessesAccount : undefined, - ), - seqno: await getWalletSeqno(wallet), - sendMode: 3, - secretKey: Buffer.from(privateKey), - }); + const signer = await wallet.signer.getSigner(); + + const boc = await TransactionService.createTransfer( + wallet.contract, + signer, + { + timeout, + messages: TransactionService.parseSignRawMessages( + params.messages, + isBattery ? await tk.wallet.battery.getExcessesAccount() : undefined, + ), + seqno: await getWalletSeqno(wallet), + sendMode: 3, + }, + isBattery ? 'internal' : 'external', + ); await sendBoc(boc, isBattery); @@ -250,6 +242,11 @@ export const SignRawModal = memo((props) => { } }, [consequences?.risk.nfts.length, fiatCurrency, totalRiskedAmount]); + const hasFailedSwap = actions.some( + (action) => + action.status === ActionStatusEnum.Failed && action.type === ActionType.JettonSwap, + ); + return ( ((props) => { { (wallet ?? tk.wallet).activityList.reload(); }); } catch (error) { - if (error instanceof NetworkOverloadedError) { + if (error instanceof IndexerLatencyError) { ref.current?.setError(error.message); await delay(3500); ref.current?.setState(States.INITIAL); @@ -113,6 +114,10 @@ export const useActionFooter = (wallet?: Wallet) => { if (error?.message) { ref.current?.setError(error.message); } + } else if (error instanceof SignerError) { + if (error?.message) { + ref.current?.setError(error.message); + } } else { ref.current?.setError(t('error_occurred')); debugLog(error); diff --git a/packages/mobile/src/core/ModalContainer/NFTOperations/NFTOperations.ts b/packages/mobile/src/core/ModalContainer/NFTOperations/NFTOperations.ts deleted file mode 100644 index 8c9e4d655..000000000 --- a/packages/mobile/src/core/ModalContainer/NFTOperations/NFTOperations.ts +++ /dev/null @@ -1,320 +0,0 @@ -import { - TransferMethodParams, - WalletContract, -} from 'tonweb/dist/types/contract/wallet/wallet-contract'; -import { DeployParams, NftTransferParams, SignRawParams } from './TXRequest.types'; -import TonWeb, { Method } from 'tonweb'; -import BigNumber from 'bignumber.js'; -import { Base64, truncateDecimal } from '$utils'; -import { Wallet } from 'blockchain'; -import { NFTOperationError } from './NFTOperationError'; -import { Address as AddressType } from 'tonweb/dist/types/utils/address'; -import { Address } from '@ton/core'; -import { t } from '@tonkeeper/shared/i18n'; -import { Ton } from '$libs/Ton'; -import { Configuration, NFTApi } from '@tonkeeper/core/src/legacy'; -import { tk } from '$wallet'; -import { config } from '$config'; -import { sendBoc } from '@tonkeeper/shared/utils/blockchain'; - -const { NftItem } = TonWeb.token.nft; - -type EstimateFeeTransferMethod = ( - params: Omit, -) => Method; -export class NFTOperations { - private tonwebWallet: WalletContract; - private wallet: Wallet; - private nftApi = new NFTApi( - new Configuration({ - basePath: config.get('tonapiV2Endpoint', tk.wallet.isTestnet), - headers: { - Authorization: `Bearer ${config.get('tonApiV2Key', tk.wallet.isTestnet)}`, - }, - }), - ); - - private myAddresses: { [key: string]: string } = {}; - - constructor(wallet: Wallet) { - this.tonwebWallet = wallet.vault.tonWallet; - this.wallet = wallet; - this.getMyAddresses(); - } - - public async transfer( - params: Omit, - options?: { useCurrentWallet?: boolean }, - ) { - let wallet: WalletContract; - if (options?.useCurrentWallet) { - wallet = this.getCurrentWallet(); - } else { - const ownerAddress = await this.getOwnerAddressByItem(params.nftItemAddress); - wallet = await this.getWalletByAddress(ownerAddress); - } - - const seqno = await this.getSeqno((await wallet.getAddress()).toString(false)); - const responseAddress = await wallet.getAddress(); - - const forwardPayload = new TextEncoder().encode(params.text ?? ''); - const forwardAmount = this.toNano(params.forwardAmount); - const amount = this.toNano(params.amount); - - const newOwnerAddress = new TonWeb.utils.Address(params.newOwnerAddress); - const nftItemAddress = new TonWeb.utils.Address(params.nftItemAddress); - const nftItem = new NftItem(wallet.provider, { address: nftItemAddress }); - - const payload = await nftItem.createTransferBody({ - responseAddress, - newOwnerAddress, - forwardPayload, - forwardAmount, - }); - - return this.methods(wallet, { - toAddress: nftItemAddress, - amount: amount, - sendMode: 3, - payload, - seqno, - }); - } - - public async deploy(params: DeployParams) { - const wallet = this.getCurrentWallet(); - const seqno = await this.getSeqno((await wallet.getAddress()).toString(false)); - - const stateInitCell = TonWeb.boc.Cell.oneFromBoc(params.stateInitHex); - const hashBytes = await stateInitCell.hash(); - const hashHex = TonWeb.utils.bytesToHex(hashBytes); - const address = new TonWeb.utils.Address(params.address); - const addressHashHex = TonWeb.utils.bytesToHex(address.hashPart); - - if (hashHex !== addressHashHex) { - throw new NFTOperationError('Hash part from StateInit does not match address'); - } - - return this.methods(wallet, { - amount: this.toNano(params.amount), - stateInit: stateInitCell, - toAddress: address, - sendMode: 3, - seqno, - }); - } - - private seeIfBounceable(address: string) { - try { - return Address.isFriendly(address) - ? Address.parseFriendly(address).isBounceable - : true; - } catch { - return true; - } - } - - public async signRaw(params: SignRawParams, sendMode = 3) { - const wallet = this.getCurrentWallet(); - - const signRawMethods = async (secretKey?: Uint8Array) => { - const seqno = await this.getSeqno((await wallet.getAddress()).toString(false)); - const signingMessage = (wallet as any).createSigningMessage(seqno); - - const messages = [...params.messages].splice(0, 4); - for (let message of messages) { - const isBounceable = this.seeIfBounceable(message.address); - const order = TonWeb.Contract.createCommonMsgInfo( - TonWeb.Contract.createInternalMessageHeader( - new TonWeb.Address(message.address).toString(true, true, isBounceable), - new TonWeb.utils.BN(this.toNano(message.amount)), - ), - Ton.base64ToCell(message.stateInit), - Ton.base64ToCell(message.payload), - ); - - signingMessage.bits.writeUint8(sendMode); - signingMessage.refs.push(order); - } - - return TonWeb.Contract.createMethod( - wallet.provider, - (wallet as any).createExternalMessage( - signingMessage, - secretKey, - seqno, - !secretKey, - ), - ); - }; - - return { - getBoc: async (): Promise => { - const methods = await signRawMethods(); - - const queryMsg = await methods.getQuery(); - const boc = Base64.encodeBytes(await queryMsg.toBoc(false)); - - return boc; - }, - estimateFee: async () => { - const methods = await signRawMethods(); - const queryMsg = await methods.getQuery(); - const boc = Base64.encodeBytes(await queryMsg.toBoc(false)); - const feeInfo = await tk.wallet.tonapi.wallet.emulateMessageToWallet({ boc }); - const fee = new BigNumber(feeInfo.event.extra).multipliedBy(-1).toNumber(); - - return truncateDecimal(Ton.fromNano(fee.toString()), 2, true); - }, - send: async (secretKey: Uint8Array, onDone?: (boc: string) => void) => { - const methods = await signRawMethods(secretKey); - - const queryMsg = await methods.getQuery(); - const boc = Base64.encodeBytes(await queryMsg.toBoc(false)); - - await sendBoc(boc, false); - - onDone?.(boc); - }, - }; - } - - private async getOwnerAddressByItem(nftItemAddress: string) { - const nftItemData = await this.nftApi.getNftItemsByAddresses({ - getAccountsRequest: { accountIds: [nftItemAddress] }, - }); - const ownerAddress = nftItemData.nftItems[0].owner?.address; - - if (!ownerAddress) { - throw new NFTOperationError('No ownerAddress'); - } - - const isTestnet = this.wallet.ton.isTestnet; - return new TonWeb.Address(ownerAddress).toString(true, true, true, isTestnet); - } - - private async getOwnerAddressByCollection(nftCollectionAddress: string) { - const nftCollection = await this.nftApi.getNftCollection({ - accountId: nftCollectionAddress, - }); - - const isTestnet = this.wallet.ton.isTestnet; - return new TonWeb.Address(nftCollection.owner?.address as string).toString( - true, - true, - true, - isTestnet, - ); - } - - // - // Utils - // - - private async getSeqno(address: string) { - const seqno = await this.wallet.ton.getSeqno(address); - return seqno ?? 0; - } - - public toNano(amount: string) { - return Ton.toNano(Ton.fromNano(amount)); - } - - private methods( - wallet: WalletContract, - params: Omit, - data?: T, - ) { - return { - getData: () => data!, - estimateFee: async () => { - const transfer = wallet.methods.transfer as EstimateFeeTransferMethod; - const methods = transfer(params); - const queryMsg = await methods.getQuery(); - const boc = Base64.encodeBytes(await queryMsg.toBoc(false)); - const feeInfo = await tk.wallet.tonapi.wallet.emulateMessageToWallet({ boc }); - const fee = new BigNumber(feeInfo.event.extra).multipliedBy(-1).toNumber(); - - return truncateDecimal(Ton.fromNano(fee.toString()), 2, true); - }, - send: async (secretKey: Uint8Array) => { - const myInfo = await this.wallet.ton.getWalletInfo( - (wallet.address as AddressType).toString(true, true, false), - ); - - let amountBN: BigNumber; - if (typeof params.amount === 'number') { - amountBN = new BigNumber(params.amount); - } else { - amountBN = new BigNumber(params.amount.toNumber()); - } - - const transfer = wallet.methods.transfer({ - ...params, - secretKey, - }); - - let feeNano: BigNumber; - try { - const query = await transfer.getQuery(); - const boc = Base64.encodeBytes(await query.toBoc(false)); - const feeInfo = await tk.wallet.tonapi.wallet.emulateMessageToWallet({ boc }); - feeNano = new BigNumber(feeInfo.event.extra).multipliedBy(-1); - } catch (e) { - throw new NFTOperationError(t('send_fee_estimation_error')); - } - - if ( - amountBN - .plus(params.sendMode === 128 ? 0 : feeNano) - .isGreaterThan(myInfo?.balance ?? '0') - ) { - throw new NFTOperationError(t('send_insufficient_funds')); - } - - const queryMsg = await transfer.getQuery(); - const boc = Base64.encodeBytes(await queryMsg.toBoc(false)); - - await sendBoc(boc, false); - }, - }; - } - - private async getMyAddresses() { - if (Object.keys(this.myAddresses).length > 0) { - return this.myAddresses; - } - - const addresses = await this.wallet.ton.getAllAddresses(); - - const reverse = Object.fromEntries( - Object.entries(addresses).map(([key, value]) => { - const address = new TonWeb.utils.Address(value as string).toString(false); - return [address, key]; - }), - ); - - this.myAddresses = reverse; - - return reverse; - } - - private async getWalletByAddress(unknownAddress: string): Promise { - const addresses = await this.getMyAddresses(); - - const address = new TonWeb.utils.Address(unknownAddress).toString(false); - const version = addresses[address]; - - if (!version) { - throw new NFTOperationError('Wrong owner address'); - } - - const wallet = this.wallet.vault.tonWalletByVersion(version); - await wallet.getAddress(); - return wallet; - } - - private getCurrentWallet(): WalletContract { - return this.wallet.vault.tonWallet; - } -} diff --git a/packages/mobile/src/core/ModalContainer/NFTOperations/TxRequest.types.ts b/packages/mobile/src/core/ModalContainer/NFTOperations/TxRequest.types.ts index 200a82282..cd56349e1 100644 --- a/packages/mobile/src/core/ModalContainer/NFTOperations/TxRequest.types.ts +++ b/packages/mobile/src/core/ModalContainer/NFTOperations/TxRequest.types.ts @@ -88,9 +88,18 @@ export type NftSingleDeployParams = { export interface SignRawMessage { address: string; - amount: string; // (decimal string): number of nanocoins to send. - payload?: string; // (string base64, optional): raw one-cell BoC encoded in Base64. - stateInit?: string; // (string base64, optional): raw once-cell BoC encoded in Base64. + /* + number of nanocoins to send. decimal string + */ + amount: string; + /* + raw one-cell BoC encoded in Base64. String base64, optional + */ + payload?: string; + /* + raw once-cell BoC encoded in Base64. string base64, optional + */ + stateInit?: string; } export type SignRawParams = { @@ -136,6 +145,7 @@ export type TxResponseOptions = { export type TxRequestBody = { experimentalWithBattery?: boolean; + forceRelayerUse?: boolean; type: TxTypes; expires_sec?: number; response_options?: TxResponseOptions; diff --git a/packages/mobile/src/core/NFT/LinkingDomainButton.tsx b/packages/mobile/src/core/NFT/LinkingDomainButton.tsx index 7be04e683..e01117c72 100644 --- a/packages/mobile/src/core/NFT/LinkingDomainButton.tsx +++ b/packages/mobile/src/core/NFT/LinkingDomainButton.tsx @@ -152,7 +152,7 @@ export const LinkingDomainButton = React.memo((props) const handlePressButton = React.useCallback(async () => { Toast.loading(); - const currentWalletAddress = wallet.tonAllAddresses[wallet.config.version].friendly; + const currentWalletAddress = wallet.tonAllAddresses[wallet.version].friendly; const linkingActions = new LinkingDomainActions( props.domainAddress, isLinked ? undefined : currentWalletAddress, @@ -201,7 +201,7 @@ export const LinkingDomainButton = React.memo((props) }); }, }); - }, [wallet.tonAllAddresses, wallet.config.version, props, isLinked, dispatch]); + }, [wallet.tonAllAddresses, wallet.version, props, isLinked, dispatch]); const buttonTitle = React.useMemo(() => { if (record.walletAddress) { diff --git a/packages/mobile/src/core/NFT/NFT.tsx b/packages/mobile/src/core/NFT/NFT.tsx index e9e09b30b..2c2f1ab66 100644 --- a/packages/mobile/src/core/NFT/NFT.tsx +++ b/packages/mobile/src/core/NFT/NFT.tsx @@ -1,7 +1,8 @@ import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import * as S from './NFT.style'; -import { Button, Icon, NavBar, Text } from '$uikit'; +import { NavBar, PopupMenu, PopupMenuItem, Text } from '$uikit'; import Animated, { + FadeOut, useAnimatedScrollHandler, useSharedValue, } from 'react-native-reanimated'; @@ -17,7 +18,6 @@ import { t } from '@tonkeeper/shared/i18n'; import { Properties } from '$core/NFT/Properties/Properties'; import { Details } from '$core/NFT/Details/Details'; import { NFTProps } from '$core/NFT/NFT.interface'; -import { Platform, Share, TouchableOpacity, View } from 'react-native'; import { TonDiamondFeature } from './TonDiamondFeature/TonDiamondFeature'; import { NFTModel, TonDiamondMetadata } from '$store/models'; import { useFlags } from '$utils/flags'; @@ -30,11 +30,21 @@ import { useExpiringDomains } from '$store/zustand/domains/useExpiringDomains'; import { usePrivacyStore } from '$store/zustand/privacy/usePrivacyStore'; import { ProgrammableButtons } from '$core/NFT/ProgrammableButtons/ProgrammableButtons'; import { Address, DNS, KnownTLDs } from '@tonkeeper/core'; -import { NftItem } from '@tonkeeper/core/src/TonAPI'; +import { NftItem, TrustType } from '@tonkeeper/core/src/TonAPI'; import { tk } from '$wallet'; import { CustomNftItem } from '@tonkeeper/core/src/TonAPI/CustomNftItems'; import { mapNewNftToOldNftData } from '$utils/mapNewNftToOldNftData'; -import { useNftsState, useWallet } from '@tonkeeper/shared/hooks'; +import { useNftsState, useTokenApproval, useWallet } from '@tonkeeper/shared/hooks'; +import { config } from '$config'; +import { checkBurnDate } from '$utils/notcoin'; +import { Button, Icon, Spacer, Steezy, TouchableOpacity, View } from '@tonkeeper/uikit'; +import { openSuspiciousNFTDetails } from '@tonkeeper/shared/modals/SuspiciousNFTDetailsModal'; +import { + TokenApprovalStatus, + TokenApprovalType, +} from '$wallet/managers/TokenApprovalManager'; + +const unverifiedTokenHitSlop = { top: 4, left: 4, bottom: 4, right: 4 }; export const NFT: React.FC = ({ oldNftItem, route }) => { const { address: nftAddress } = route?.params?.keyPair || {}; @@ -76,6 +86,12 @@ export const NFT: React.FC = ({ oldNftItem, route }) => { [nft], ); + const approvalStatuses = useTokenApproval((state) => state.tokens); + const approvalIdentifier = Address.parse( + nft?.collection?.address ?? nftAddress ?? nft?.address, + ).toRaw(); + const nftApprovalStatus = approvalStatuses[approvalIdentifier]; + const isTG = DNS.getTLD(nft.dns || nft.name) === KnownTLDs.TELEGRAM; const isDNS = !!nft.dns && !isTG; const isTonDiamondsNft = checkIsTonDiamondsNFT(nft); @@ -90,6 +106,16 @@ export const NFT: React.FC = ({ oldNftItem, route }) => { return data.decoded.last_fill_up_time; }, [nft.address]); + useEffect(() => { + tk.wallet.nfts + .fetchByAddress(nft.address) + .then((data) => { + setNft(mapNewNftToOldNftData(data, wallet.address.ton.friendly)); + }) + .catch(null); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + useEffect(() => { if (isDNS) { getDNSLastFillTime().then((utime) => { @@ -154,17 +180,6 @@ export const NFT: React.FC = ({ oldNftItem, route }) => { }); }, [nav, nft.address]); - const handleShare = useCallback(() => { - if (!nft.marketplaceURL) { - return; - } - Share.share({ - url: nft.marketplaceURL, - title: nft.name, - message: Platform.OS === 'android' ? nft.marketplaceURL : undefined, - }); - }, [nft.marketplaceURL, nft.name]); - const lottieUri = isTonDiamondsNft ? nft.metadata?.lottie : undefined; const videoUri = isTonDiamondsNft ? nft.metadata?.animation_url : undefined; @@ -179,21 +194,144 @@ export const NFT: React.FC = ({ oldNftItem, route }) => { const isWatchOnly = wallet && wallet.isWatchOnly; + const handleBurn = useCallback(async () => { + try { + const isAvailable = await checkBurnDate(); + + if (!isAvailable) { + return; + } + + nav.navigate('/burn-vouchers'); + } catch (e) { + if (e.message) { + Toast.fail(e.message); + } + } + }, [nav]); + + const handleNewApproveStatus = useCallback( + (approvalStatus: TokenApprovalStatus) => () => { + const address = Address.parse(nft.collection?.address ?? nft.address).toRaw(); + + tk.wallet.tokenApproval.updateTokenStatus( + address, + approvalStatus, + nft.collection ? TokenApprovalType.Collection : TokenApprovalType.Token, + ); + + if ( + (!tk.wallet.isTestnet && + approvalStatus === TokenApprovalStatus.Spam && + nft.trust !== TrustType.Blacklist) || + (approvalStatus === TokenApprovalStatus.Approved && + nft.trust !== TrustType.Whitelist) + ) { + fetch(`${config.get('scamEndpoint')}/v1/report/${nft.address}`, { + method: 'POST', + body: JSON.stringify({ + is_scam: approvalStatus === TokenApprovalStatus.Spam, + }), + headers: { + 'Content-Type': 'application/json', + }, + }).catch((e) => console.warn(e)); + } + + if (approvalStatus === TokenApprovalStatus.Spam) { + Toast.success( + t(`suspicious.status_update.spam.${nft.collection ? 'collection' : 'nft'}`), + ); + nav.goBack(); + } + + if (approvalStatus === TokenApprovalStatus.Declined) { + Toast.success( + t(`suspicious.status_update.hidden.${nft.collection ? 'collection' : 'nft'}`), + ); + nav.goBack(); + } + }, + [nav, nft.address, nft.collection], + ); + + const handleOpenExplorer = useCallback(async () => { + openDAppBrowser( + config.get('accountExplorer', wallet.isTestnet).replace('%s', nft.address), + ); + }, [nft.address, wallet.isTestnet]); + + const isTrusted = nft.trust === TrustType.Whitelist; + const isManuallyApproved = nftApprovalStatus?.current === TokenApprovalStatus.Approved; + return ( } - /> - ) + } + />, + !isTrusted && ( + } + /> + ), + } + />, + ]} + > + + + + } - isModal scrollTop={scrollTop} + subtitle={ + !isTrusted && ( + + openSuspiciousNFTDetails({ + handleNewApproveStatus, + isApprovedNow: isManuallyApproved, + }) + } + hitSlop={unverifiedTokenHitSlop} + style={styles.subtitleContainer} + > + + {t('suspiciousNFTDetails.title')} + + + + + + + ) + } + subtitleProps={{ color: 'accentOrange' }} titleProps={{ numberOfLines: 1 }} > {hiddenAmounts ? '* * * *' : title} @@ -212,6 +350,29 @@ export const NFT: React.FC = ({ oldNftItem, route }) => { onScroll={scrollHandler} scrollEventThrottle={16} > + {!isTrusted && !isManuallyApproved && ( + + + title={isDNS ? t('nft_transfer_dns') : t('nft_transfer_nft')} + /> )} {isOnSale ? ( @@ -289,19 +449,32 @@ export const NFT: React.FC = ({ oldNftItem, route }) => { {nft.marketplaceURL && !flags.disable_nft_markets ? ( + color="secondary" + title={t('nft_open_in_marketplace')} + /> ) : null} - + {isCurrentAddressOwner && + nft.collection && + Address.compare( + nft.collection.address, + config.get('notcoin_nft_collection'), + ) && + config.get('notcoin_burn') ? ( + diff --git a/packages/mobile/src/core/Send/steps/AddressStep/components/CommentInput/CommentInput.tsx b/packages/mobile/src/core/Send/steps/AddressStep/components/CommentInput/CommentInput.tsx index 63d8a9521..8e60e2011 100644 --- a/packages/mobile/src/core/Send/steps/AddressStep/components/CommentInput/CommentInput.tsx +++ b/packages/mobile/src/core/Send/steps/AddressStep/components/CommentInput/CommentInput.tsx @@ -15,6 +15,7 @@ interface Props { innerRef?: RefObject; comment: string; isAbleToEncryptComment: boolean; + isCommentValid: boolean; isCommentRequired: boolean; isCommentEncrypted: boolean; setComment: React.Dispatch>; @@ -28,6 +29,7 @@ const CommentInputComponent: FC = (props) => { comment, isAbleToEncryptComment, isCommentRequired, + isCommentValid, isCommentEncrypted, setComment, setCommentEncrypted, @@ -91,10 +93,20 @@ const CommentInputComponent: FC = (props) => { }, [isCommentRequired]); return ( - + 0 && !isCommentValid ? ( + + {t('send_screen_steps.comfirm.comment_ascii_text')} + + ) : ( + commentDescription + ) + } + > = (props) => { const { @@ -40,6 +40,10 @@ const ConfirmStepComponent: FC = (props) => { active, isInactive, isBattery, + isGasless, + isForcedGasless, + supportsGasless, + customFeeCurrency, amount, comment, isCommentEncrypted, @@ -55,7 +59,6 @@ const ConfirmStepComponent: FC = (props) => { const balances = useBalancesState(); const wallet = useWallet(); - const batteryState = useBatteryState(); const { Logo, liquidJettonPool } = useCurrencyToSend(currency, tokenType); @@ -136,17 +139,32 @@ const ConfirmStepComponent: FC = (props) => { const feeCurrency = CryptoCurrencies.Ton; const calculatedValue = useMemo(() => { - if (amount.all && tokenType === TokenType.TON) { + if ( + amount.all && + (tokenType === TokenType.TON || + compareAddresses(currency, customFeeCurrency?.jetton_master)) + ) { return new BigNumber(parseLocaleNumber(amount.value)).minus(fee).toString(); } return parseLocaleNumber(amount.value); - }, [amount.all, amount.value, fee, tokenType]); + }, [ + amount.all, + amount.value, + currency, + customFeeCurrency?.jetton_master, + fee, + tokenType, + ]); const fiatValue = useFiatValue(currency as CryptoCurrency, calculatedValue, decimals); const fiatFee = useFiatValue( - currency === 'usdt' ? 'USDT' : CryptoCurrencies.Ton, + customFeeCurrency + ? customFeeCurrency.jetton_master + : currency === 'usdt' + ? 'USDT' + : CryptoCurrencies.Ton, fee || '0', 6, currency === 'usdt' ? 6 : Decimals[CryptoCurrencies.Ton], @@ -158,11 +176,12 @@ const ConfirmStepComponent: FC = (props) => { } return `≈ ${formatter.format(fee, { - decimals: currency === 'usdt' ? 6 : Decimals[feeCurrency], - currency: feeCurrency.toUpperCase(), + decimals: 3, + currency: customFeeCurrency ? customFeeCurrency.symbol : feeCurrency.toUpperCase(), currencySeparator: 'wide', + forceRespectDecimalPlaces: true, })}`; - }, [currency, fee, feeCurrency]); + }, [customFeeCurrency, fee, feeCurrency]); const amountValue = useMemo(() => { const value = formatter.format(calculatedValue, { @@ -293,10 +312,12 @@ const ConfirmStepComponent: FC = (props) => { )} - {batteryState !== BatteryState.Empty && isBattery ? ( + {isBattery ? ( {t('send_screen_steps.comfirm.will_be_paid_with_battery')} + ) : supportsGasless && !isForcedGasless ? ( + ) : ( )} diff --git a/packages/mobile/src/core/Settings/Settings.tsx b/packages/mobile/src/core/Settings/Settings.tsx index 7aea35bb9..f370f2399 100644 --- a/packages/mobile/src/core/Settings/Settings.tsx +++ b/packages/mobile/src/core/Settings/Settings.tsx @@ -1,16 +1,15 @@ import React, { FC, useCallback, useMemo, useRef } from 'react'; import { useDispatch } from 'react-redux'; import Rate, { AndroidMarket } from 'react-native-rate'; -import { Alert, Linking, Platform, View } from 'react-native'; +import { Alert, InteractionManager, Linking, Platform, View } from 'react-native'; import DeviceInfo from 'react-native-device-info'; import { TapGestureHandler } from 'react-native-gesture-handler'; import * as S from './Settings.style'; -import { Icon, PopupSelect, Spacer, Text } from '$uikit'; -import { Icon as NewIcon, Screen } from '@tonkeeper/uikit'; +import { Icon, PopupSelect, Spacer, Tag, Text } from '$uikit'; +import { Icon as NewIcon, Screen, List, WalletIcon } from '@tonkeeper/uikit'; import { useShouldShowTokensButton } from '$hooks/useShouldShowTokensButton'; import { useNavigation } from '@tonkeeper/router'; -import { List } from '@tonkeeper/uikit'; import { AppStackRouteNames, MainStackRouteNames, @@ -21,7 +20,6 @@ import { openNotifications, openSecurity, openSelectLanguage, - openSubscriptions, openRefillBatteryModal, } from '$navigation'; import { walletActions } from '$store/wallet'; @@ -47,6 +45,8 @@ import { WalletListItem } from '@tonkeeper/shared/components'; import { useSubscriptions } from '@tonkeeper/shared/hooks/useSubscriptions'; import { nativeLocaleNames } from '@tonkeeper/shared/i18n/translations'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; +import { reset } from '$navigation/imperative'; +import { getNewsUrl } from '@tonkeeper/shared/utils/getNewsUrl'; export const Settings: FC = () => { const animationRef = useRef(null); @@ -65,16 +65,16 @@ export const Settings: FC = () => { const fiatCurrency = useWalletCurrency(); const dispatch = useDispatch(); - const hasSubscriptions = useSubscriptions( - (state) => Object.values(state.subscriptions).length > 0, - ); const wallet = useWallet(); const shouldShowTokensButton = useShouldShowTokensButton(); const { lastBackupAt } = useWalletSetup(); const isBatteryVisible = - !!wallet && !wallet.isWatchOnly && !config.get('disable_battery'); + !!wallet && + !wallet.isWatchOnly && + !wallet.isExternal && + !config.get('disable_battery'); const searchEngine = useBrowserStore((state) => state.searchEngine); const setSearchEngine = useBrowserStore((state) => state.actions.setSearchEngine); @@ -112,16 +112,38 @@ export const Settings: FC = () => { }, []); const handleNews = useCallback(() => { - Linking.openURL(config.get('tonkeeperNewsUrl')).catch((err) => console.log(err)); + Linking.openURL(getNewsUrl()).catch((err) => console.log(err)); }, []); const handleSupport = useCallback(() => { Linking.openURL(config.get('directSupportUrl')).catch((err) => console.log(err)); }, []); + const handleFAQ = useCallback(() => { + Linking.openURL( + i18n.locale === 'ru' ? config.get('faqUrlRu') : config.get('faqUrl'), + ).catch((err) => console.log(err)); + }, []); + const handleResetWallet = useCallback(() => { - nav.navigate('/logout-warning'); - }, [nav]); + if (wallet.isExternal) { + Alert.alert(t('settings_delete_signer_account'), undefined, [ + { + text: t('cancel'), + style: 'cancel', + }, + { + text: t('settings_delete_watch_account_button'), + style: 'destructive', + onPress: () => { + dispatch(walletActions.cleanWallet()); + }, + }, + ]); + } else { + nav.navigate('/logout-warning'); + } + }, [dispatch, nav, wallet.isExternal]); const handleStopWatchWallet = useCallback(() => { Alert.alert(t('settings_delete_watch_account'), undefined, [ @@ -139,10 +161,6 @@ export const Settings: FC = () => { ]); }, [dispatch]); - const handleSubscriptions = useCallback(() => { - openSubscriptions(); - }, []); - const handleNotifications = useCallback(() => { openNotifications(); }, []); @@ -231,6 +249,28 @@ export const Settings: FC = () => { return hasDiamods && !flags.disable_apperance; }, [hasDiamods, flags.disable_apperance]); + const openW5Stories = useCallback(() => { + nav.navigate(AppStackRouteNames.W5StoriesScreen, { + onPressButton: wallet.isW5 + ? undefined + : () => { + InteractionManager.runAfterInteractions(async () => { + try { + await tk.addWalletV5(); + + reset(MainStackRouteNames.Tabs); + + setTimeout(() => { + nav.openModal('/switch-wallet', { + withW5Flash: true, + }); + }, 300); + } catch {} + }); + }, + }); + }, [nav]); + return ( @@ -256,7 +296,7 @@ export const Settings: FC = () => { ) : null} - {!!wallet && !wallet.isWatchOnly && ( + {!!wallet && !wallet.isWatchOnly && !wallet.isExternal && ( { onPress={handleManageTokens} /> )} - {!!wallet && !wallet.isWatchOnly && hasSubscriptions && ( - - } - title={t('settings_subscriptions')} - onPress={handleSubscriptions} - /> - )} {!!wallet && wallet.notifications.isAvailable && !wallet.isTestnet && ( } @@ -338,12 +365,33 @@ export const Settings: FC = () => { name={'ic-battery-28'} /> } - title={t('battery.settings', { - betaLabel: config.get('battery_beta') ? '(Beta)' : '', - })} + title={ + + {t('battery.settings')} + {config.get('battery_beta') ? Beta : null} + + } onPress={handleBattery} /> )} + {wallet.isMnemonic && (!tk.hasW5WithCurrentPubkey || wallet.isW5) && ( + + } + title={ + + {t('upgrade_to_w5.settings')} + {config.get('v5_beta') ? Beta : null} + + } + onPress={openW5Stories} + /> + )} {!config.get('disable_holders_cards') && !!wallet && !wallet.isWatchOnly && ( { + + } + title={t('settings_faq')} + /> {!flags.disable_support_button ? ( { } title={t('settings_rate')} /> - {!!wallet && !wallet.isWatchOnly && ( + {!!wallet && !wallet.isWatchOnly && !wallet.isExternal && ( { {t('stop_watch')} ) : ( - - {t('settings_reset')} + + + {t('access_confirmation_logout')} + + + {tk.wallet.config.name} + + )} @@ -575,4 +648,20 @@ const styles = Steezy.create({ paddingBottom: 6.5, marginLeft: 8, }, + row: { + flexDirection: 'row', + alignItems: 'center', + }, + logoutTitleContainer: { + gap: 4, + alignItems: 'center', + flexDirection: 'row', + paddingRight: 8, + }, + flex: { + flex: 1, + }, + emoji: { + fontSize: 20, + }, }); diff --git a/packages/mobile/src/core/StakingSend/StakingSend.tsx b/packages/mobile/src/core/StakingSend/StakingSend.tsx index c9efff22e..c73c3949f 100644 --- a/packages/mobile/src/core/StakingSend/StakingSend.tsx +++ b/packages/mobile/src/core/StakingSend/StakingSend.tsx @@ -1,8 +1,5 @@ -import { NFTOperations } from '$core/ModalContainer/NFTOperations/NFTOperations'; -import { useUnlockVault } from '$core/ModalContainer/NFTOperations/useUnlockVault'; import { SendAmount, TokenType } from '$core/Send/Send.interface'; import { useFiatValue } from '$hooks/useFiatValue'; -import { useInstance } from '$hooks/useInstance'; import { usePoolInfo } from '$hooks/usePoolInfo'; import { Ton } from '$libs/Ton'; import { AppStackRouteNames } from '$navigation'; @@ -11,16 +8,13 @@ import { StepView, StepViewItem, StepViewRef } from '$shared/components'; import { CryptoCurrencies, Decimals } from '$shared/constants'; import { Toast, useNotificationsStore } from '$store'; import { getStakingPoolByAddress } from '@tonkeeper/shared/utils/staking'; -import { walletWalletSelector } from '$store/wallet'; import { NavBar } from '$uikit'; import { calculateMessageTransferAmount, delay, parseLocaleNumber } from '$utils'; -import { getTimeSec } from '$utils/getTimeSec'; import { RouteProp } from '@react-navigation/native'; import axios from 'axios'; import BigNumber from 'bignumber.js'; import React, { FC, useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { useDerivedValue, useSharedValue } from 'react-native-reanimated'; -import { useSelector } from 'react-redux'; import { AmountStep, ConfirmStep } from './steps'; import { StakingSendSteps, StakingTransactionType } from './types'; import { getStakeSignRawMessage, getWithdrawalAlertFee, getWithdrawalFee } from './utils'; @@ -39,6 +33,12 @@ import { useStakingState, useWallet } from '@tonkeeper/shared/hooks'; import { tk } from '$wallet'; import { Address } from '@tonkeeper/shared/Address'; import { shallow } from 'zustand/shallow'; +import { TransactionService } from '@tonkeeper/core'; +import { getWalletSeqno } from '@tonkeeper/shared/utils/wallet'; +import { + getTimeoutFromLiteserverSafely, + sendBoc, +} from '@tonkeeper/shared/utils/blockchain'; interface Props { route: RouteProp; @@ -68,8 +68,6 @@ export const StakingSend: FC = (props) => { const isWithdrawalConfrim = transactionType === StakingTransactionType.WITHDRAWAL_CONFIRM; - const unlockVault = useUnlockVault(); - const pool = useStakingState( (s) => getStakingPoolByAddress(s, poolAddress), [poolAddress], @@ -121,8 +119,6 @@ export const StakingSend: FC = (props) => { const wallet = useWallet(); const walletAddress = wallet.address.ton.raw; - const walletLegacy = useSelector(walletWalletSelector)!; - const operations = useInstance(() => new NFTOperations(walletLegacy)); const [amount, setAmount] = useState({ value: isWithdrawalConfrim @@ -173,8 +169,6 @@ export const StakingSend: FC = (props) => { const hideTitle = currentStep.id === StakingSendSteps.CONFIRM; - const actionRef = useRef> | null>(null); - const messages = useRef([]); const rawAddress = wallet.address.ton.raw ?? ''; @@ -220,19 +214,18 @@ export const StakingSend: FC = (props) => { poolInfo.stakingJetton, ); - const action = await operations.signRaw( - { - source: walletAddress, - valid_until: getTimeSec() + 10 * 60, - messages: [message], - }, - transactionType === StakingTransactionType.DEPOSIT && amount.all ? 128 : 3, - ); - messages.current = [message]; - actionRef.current = action; - const boc = await action.getBoc(); + const signer = await wallet.signer.getSigner(true); + + const timeout = await getTimeoutFromLiteserverSafely(); + + const boc = await TransactionService.createTransfer(wallet.contract, signer, { + timeout, + messages: TransactionService.parseSignRawMessages(messages.current), + seqno: await getWalletSeqno(wallet), + }); + const response = await tk.wallet.tonapi.wallet.emulateMessageToWallet({ boc }); setConsequences(response); @@ -253,11 +246,11 @@ export const StakingSend: FC = (props) => { } }, [ amount.all, - operations, parsedAmount, pool, poolInfo.stakingJetton, transactionType, + wallet, walletAddress, ]); @@ -280,7 +273,7 @@ export const StakingSend: FC = (props) => { }, [consequences?.event.extra, pool]); const sendTx = useCallback(async () => { - if (!actionRef.current) { + if (!messages.current) { return Promise.reject(); } try { @@ -347,10 +340,17 @@ export const StakingSend: FC = (props) => { } } - const vault = await unlockVault(); - const privateKey = await vault.getTonPrivateKey(); + const signer = await wallet.signer.getSigner(); + + const timeout = await getTimeoutFromLiteserverSafely(); + + const boc = await TransactionService.createTransfer(wallet.contract, signer, { + timeout, + messages: TransactionService.parseSignRawMessages(messages.current), + seqno: await getWalletSeqno(wallet), + }); - await actionRef.current.send(privateKey); + await sendBoc(boc, false); const endTimestamp = pool.cycle_end * 1000; const isCooldown = Date.now() > endTimestamp; @@ -375,7 +375,7 @@ export const StakingSend: FC = (props) => { rawAddress, stakingAddressToMigrateFrom, totalFee, - unlockVault, + wallet, ]); useEffect(() => { diff --git a/packages/mobile/src/core/TonConnect/TonConnect.style.ts b/packages/mobile/src/core/TonConnect/TonConnect.style.ts index 09671d2a5..381d03ee8 100644 --- a/packages/mobile/src/core/TonConnect/TonConnect.style.ts +++ b/packages/mobile/src/core/TonConnect/TonConnect.style.ts @@ -88,8 +88,8 @@ export const TitleWrapper = styled.View` margin-bottom: ${ns(4)}px; `; -export const Center = styled.View<{ isTonConnectV2: boolean }>` - height: ${({ isTonConnectV2 }) => ns(isTonConnectV2 ? 56 + 16 + 40 : 56)}px; +export const Center = styled.View` + height: ${ns(56 + 16 + 40)}px; justify-content: center; align-items: center; `; @@ -103,8 +103,8 @@ export const LottieIcon = styled(LottieView)` margin-bottom: ${ns(-1.8)}px; `; -export const Footer = styled.View<{ isTonConnectV2: boolean }>` - height: ${({ isTonConnectV2 }) => ns(isTonConnectV2 ? 56 + 16 + 40 : 56)}px; +export const Footer = styled.View` + height: ${ns(56 + 16 + 40)}px; position: relative; `; diff --git a/packages/mobile/src/core/TonConnect/TonConnectModal.tsx b/packages/mobile/src/core/TonConnect/TonConnectModal.tsx index e3d371c29..27d39dd15 100644 --- a/packages/mobile/src/core/TonConnect/TonConnectModal.tsx +++ b/packages/mobile/src/core/TonConnect/TonConnectModal.tsx @@ -1,13 +1,10 @@ import React, { useCallback, useMemo } from 'react'; import axios from 'axios'; -import queryString from 'query-string'; -import TonWeb from 'tonweb'; -import { Linking, StyleSheet } from 'react-native'; +import { StyleSheet } from 'react-native'; import { useTheme } from '$hooks/useTheme'; import { Button, List, Loader, Spacer, Text, TransitionOpacity } from '$uikit'; import { convertHexToRGBA, - delay, getDomainFromURL, triggerNotificationSuccess, triggerSelection, @@ -30,23 +27,27 @@ import { Toast } from '$store'; import { push } from '$navigation/imperative'; import { openRequireWalletModal } from '$core/ModalContainer/RequireWallet/RequireWallet'; import { SheetActions, useNavigation } from '@tonkeeper/router'; -import { Address } from '@tonkeeper/core'; +import { Address, ContractService } from '@tonkeeper/core'; import { replaceString } from '@tonkeeper/shared/utils/replaceString'; import { tk } from '$wallet'; import { WalletListItem } from '@tonkeeper/shared/components'; import { useWallets } from '@tonkeeper/shared/hooks'; export const TonConnectModal = (props: TonConnectModalProps) => { + const { isInternalBrowser, replyBuilder, requestPromise, manifest } = props; + const animation = useTonConnectAnimation(); const unlockVault = useUnlockVault(); const theme = useTheme(); const nav = useNavigation(); const [selectedWalletIdentifier, setSelectedWalletIdentifier] = React.useState( - tk.wallet.isWatchOnly ? tk.walletForUnlock.identifier : tk.wallet.identifier, + tk.wallet.isWatchOnly || tk.wallet.isExternal + ? tk.walletForUnlock.identifier + : tk.wallet.identifier, ); const allWallets = useWallets(); const selectableWallets = useMemo( - () => allWallets.filter((wallet) => !wallet.isWatchOnly), + () => allWallets.filter((wallet) => !wallet.isWatchOnly && !wallet.isExternal), [allWallets], ); const wallet = useMemo( @@ -58,8 +59,6 @@ export const TonConnectModal = (props: TonConnectModalProps) => { const friendlyAddress = wallet.address.ton.friendly; const maskedAddress = Address.toShort(friendlyAddress); - const isInternalBrowser = props.protocolVersion !== 1 && props.isInternalBrowser; - const showWalletSelector = selectableWallets.length > 1 && !isInternalBrowser; const handleSwitchNotifications = useCallback(() => { @@ -69,135 +68,45 @@ export const TonConnectModal = (props: TonConnectModalProps) => { const closeModal = useCallback(() => nav.goBack(), [nav]); - const isTonapi = props.protocolVersion === 1 ? props?.hostname === 'tonapi.io' : false; - - let appIconUri: string; - let appName: string; - if (props.protocolVersion === 1) { - appIconUri = props.request.image_url; - if (isTonapi && props.request.app_name) { - appName = props.request.app_name; - } else { - appName = props.hostname; - } - } else { - appIconUri = props.manifest.iconUrl; - appName = props.manifest.name; - } - - const domain = - props.protocolVersion === 1 ? appName : getDomainFromURL(props.manifest.url); + const appIconUri = manifest.iconUrl; + const appName = manifest.name; - const isTonConnectV2 = props.protocolVersion !== 1; - - const sendToCallbackUrl = React.useCallback( - async (response: string) => { - if (props.protocolVersion !== 1) { - return; - } - - const { request } = props; - - if (request.callback_url) { - const callbackUrl = createCallbackLink({ - toHash: request.return_serverless, - url: request.callback_url, - response, - }); - - const resp = await axios.get(callbackUrl); - if (resp.status !== 200) { - throw new Error('Failed to send response'); - } - } - }, - [props], - ); + const domain = getDomainFromURL(manifest.url); const createResponse = React.useCallback(async () => { try { animation.startLoading(); const vault = await unlockVault(wallet.identifier); - - const address = await vault.getTonAddress(wallet.isTestnet); const privateKey = await vault.getTonPrivateKey(); - const walletSeed = TonWeb.utils.bytesToBase64(privateKey); - - if (props.protocolVersion === 1) { - const { tonconnect, request, hostname } = props; - - const response = await tonconnect.createResponse({ - service: hostname, - seed: walletSeed, - realm: 'web', - payload: { - tonAddress: () => ({ address }), - tonOwnership: ({ clientId }) => { - const pubkey = TonWeb.utils.bytesToBase64(vault.tonPublicKey); - const walletVersion = vault.getVersion() ?? ''; - const walletId = vault.getWalletId(); + const publicKey = Buffer.from(wallet.pubkey, 'hex'); - const signature = tonconnect.createTonOwnershipSignature({ - secretKey: privateKey, - walletVersion, - address, - clientId, - }); - - return { - wallet_version: walletVersion, - wallet_id: walletId, - signature, - address, - pubkey, - }; - }, - }, - }); - - if (request.callback_url) { - await sendToCallbackUrl(response); - } - } - - const withDelay = props.protocolVersion === 1 || !props.isInternalBrowser; + const address = wallet.address.ton.friendly; await animation.showSuccess(() => { triggerNotificationSuccess(); - }, withDelay); - - if (props.protocolVersion !== 1) { - const { stateInit } = await vault.tonWallet.createStateInit(); - const walletStateInit = TonWeb.utils.bytesToBase64(await stateInit.toBoc(false)); - const publicKey = vault.tonPublicKey; - - const { replyBuilder, requestPromise } = props; + }, !isInternalBrowser); - const replyItems = await replyBuilder.createReplyItems( - address, - privateKey, - publicKey, - walletStateInit, - wallet.isTestnet, - ); + const stateInit = ContractService.getStateInit(wallet.contract); - if (withNotifications && !wallet.tonProof.tonProofToken) { - await wallet.tonProof.obtainProof(await vault.getKeyPair()); - } + const replyItems = await replyBuilder.createReplyItems( + address, + privateKey, + publicKey, + stateInit, + wallet.isTestnet, + ); - requestPromise.resolve({ - address, - replyItems, - notificationsEnabled: withNotifications, - walletIdentifier: wallet.identifier, - }); + if (withNotifications && !wallet.tonProof.tonProofToken) { + await wallet.tonProof.obtainProof(await vault.getKeyPair()); } - if (props.protocolVersion === 1 && props.request.return_url) { - animation.showReturnButton(); - return; - } + requestPromise.resolve({ + address, + replyItems, + notificationsEnabled: withNotifications, + walletIdentifier: wallet.identifier, + }); closeModal(); } catch (error) { @@ -215,46 +124,14 @@ export const TonConnectModal = (props: TonConnectModalProps) => { }, [ animation, closeModal, - props, - sendToCallbackUrl, + isInternalBrowser, + replyBuilder, + requestPromise, unlockVault, wallet, withNotifications, ]); - const handleBackToService = React.useCallback(async () => { - if (props.protocolVersion !== 1) { - return; - } - - const { tonconnect, request, openUrl } = props; - - const response = tonconnect.getResponse(); - if (request.return_url && response) { - const returnUrl = createCallbackLink({ - toHash: request.return_serverless, - url: request.return_url, - response, - }); - const url = returnUrl.startsWith('http') ? returnUrl : `https://${returnUrl}`; - - if (openUrl) { - openUrl(url); - closeModal(); - return; - } - - try { - await Linking.openURL(url); - - await delay(2000); - closeModal(); - } catch (err) { - debugLog(err); - } - } - }, [closeModal, props]); - const handleWalletPress = useCallback(() => { Haptics.selection(); nav.openModal('/switch-wallet', { @@ -265,9 +142,7 @@ export const TonConnectModal = (props: TonConnectModalProps) => { useEffect( () => () => { - if (props.protocolVersion !== 1) { - props.requestPromise.reject(); - } + requestPromise.reject(); }, // eslint-disable-next-line react-hooks/exhaustive-deps [], @@ -343,7 +218,7 @@ export const TonConnectModal = (props: TonConnectModalProps) => { {' '} {maskedAddress}{' '} - {tk.wallet.config.version} + {tk.wallet.version} ) : ( ':' @@ -366,7 +241,7 @@ export const TonConnectModal = (props: TonConnectModalProps) => { /> ) : null} - {isTonConnectV2 && showNotifications ? ( + {showNotifications ? ( <> { ) : null} - + - {isTonConnectV2 ? ( - {t('ton_login_notice')} - ) : null} + {t('ton_login_notice')} - + - - - - - + {t('ton_login_success')} @@ -445,20 +309,6 @@ const styles = StyleSheet.create({ }, }); -type CreateAuthResponseLinkOptions = { - url: string; - response: string; - toHash?: boolean; -}; - -function createCallbackLink(options: CreateAuthResponseLinkOptions) { - return queryString.stringifyUrl({ - ...(options.toHash && { fragmentIdentifier: 'tonlogin' }), - query: { tonlogin: options.response }, - url: options.url, - }); -} - export function openTonConnect(props: TonConnectModalProps) { if (tk.walletForUnlock) { push('SheetsProvider', { diff --git a/packages/mobile/src/core/TonConnect/models.ts b/packages/mobile/src/core/TonConnect/models.ts index 6908420d7..bbbc12874 100644 --- a/packages/mobile/src/core/TonConnect/models.ts +++ b/packages/mobile/src/core/TonConnect/models.ts @@ -1,4 +1,3 @@ -import { AuthRequestBody, TonLoginClient } from '@tonapps/tonlogin-client'; import { ConnectItemReply } from '@tonconnect/protocol'; import { ConnectReplyBuilder, DAppManifest } from '$tonconnect'; @@ -9,21 +8,13 @@ export interface TonConnectModalResponse { walletIdentifier: string; } -export type TonConnectModalProps = - | { - protocolVersion: 1; - tonconnect: TonLoginClient; - request: AuthRequestBody; - hostname: string; - openUrl?: (url: string) => void; - } - | { - protocolVersion: 2; - manifest: DAppManifest; - replyBuilder: ConnectReplyBuilder; - requestPromise: { - resolve: (response: TonConnectModalResponse) => void; - reject: () => void; - }; - isInternalBrowser: boolean; - }; +export interface TonConnectModalProps { + protocolVersion: 2; + manifest: DAppManifest; + replyBuilder: ConnectReplyBuilder; + requestPromise: { + resolve: (response: TonConnectModalResponse) => void; + reject: () => void; + }; + isInternalBrowser: boolean; +} diff --git a/packages/mobile/src/hooks/useApprovedNfts.ts b/packages/mobile/src/hooks/useApprovedNfts.ts index 31ac93b8d..d22ebbac2 100644 --- a/packages/mobile/src/hooks/useApprovedNfts.ts +++ b/packages/mobile/src/hooks/useApprovedNfts.ts @@ -4,10 +4,12 @@ import { Address } from '@tonkeeper/shared/Address'; import { useNftsState, useTokenApproval, useWallet } from '@tonkeeper/shared/hooks'; import { mapNewNftToOldNftData } from '$utils/mapNewNftToOldNftData'; import { TokenApprovalStatus } from '$wallet/managers/TokenApprovalManager'; +import { TrustType } from '@tonkeeper/core/src/TonAPI'; export interface IBalances { enabled: NFTModel[]; disabled: NFTModel[]; + spam: NFTModel[]; } export function useApprovedNfts() { const accountNfts = useNftsState((s) => s.accountNfts); @@ -17,18 +19,26 @@ export function useApprovedNfts() { const nftBalances: IBalances = { enabled: [], disabled: [], + spam: [], }; Object.values(accountNfts).forEach((item) => { const nft = mapNewNftToOldNftData(item, wallet?.address.ton.friendly); const collectionAddress = nft?.collection?.address; const nftAddress = Address.parse(nft.address).toRaw(); + const isBlacklisted = nft.trust === TrustType.Blacklist; // get approval status using collection address if it exists, otherwise use nft address const approvalStatus = (collectionAddress && approvalStatuses[collectionAddress]) || approvalStatuses[nftAddress]; + if (approvalStatus?.current === TokenApprovalStatus.Declined) { nftBalances.disabled.push(nft); + } else if ( + (isBlacklisted && !approvalStatus) || + approvalStatus?.current === TokenApprovalStatus.Spam + ) { + nftBalances.spam.push(nft); } else { nftBalances.enabled.push(nft); } diff --git a/packages/mobile/src/hooks/useCurrencyToSend.tsx b/packages/mobile/src/hooks/useCurrencyToSend.tsx index b18462530..44be96ca5 100644 --- a/packages/mobile/src/hooks/useCurrencyToSend.tsx +++ b/packages/mobile/src/hooks/useCurrencyToSend.tsx @@ -74,6 +74,7 @@ export function useCurrencyToSend( jetton?.metadata?.symbol || Address.toShort(jetton?.jettonAddress), Logo, jettonWalletAddress: jetton?.walletAddress, + jettonMaster: jetton?.jettonAddress, isLiquidJetton: !!liquidJettonPool, }; // case TokenType.USDT: @@ -96,6 +97,7 @@ export function useCurrencyToSend( currencyTitle: currency.toUpperCase(), Logo, jettonWalletAddress: undefined, + jettonMaster: undefined, isLiquidJetton: false, liquidJettonPool: null, }; diff --git a/packages/mobile/src/hooks/useDiamondsChecker.ts b/packages/mobile/src/hooks/useDiamondsChecker.ts index 67468bf96..574f7954d 100644 --- a/packages/mobile/src/hooks/useDiamondsChecker.ts +++ b/packages/mobile/src/hooks/useDiamondsChecker.ts @@ -1,5 +1,6 @@ import { mainActions } from '$store/main'; -import { AccentKey, AppearanceAccents } from '$styled'; +import { NFTModel, TonDiamondMetadata } from '$store/models'; +import { AccentKey, getAccentIdByDiamondsNFT } from '$styled'; import { useNftsState } from '@tonkeeper/shared/hooks'; import { useEffect } from 'react'; import { useDispatch } from 'react-redux'; @@ -10,10 +11,12 @@ export const useDiamondsChecker = () => { const dispatch = useDispatch(); useEffect(() => { - const accent = - Object.values(AppearanceAccents).find( - (item) => item.colors.accentPrimary === selectedDiamond?.metadata.theme.main, - )?.id ?? AccentKey.default; + const accent = selectedDiamond + ? getAccentIdByDiamondsNFT( + selectedDiamond as unknown as NFTModel, + ) + : AccentKey.default; + console.log('accent', accent); dispatch(mainActions.setAccent(accent)); dispatch( mainActions.setTonCustomIcon( diff --git a/packages/mobile/src/hooks/useMigration.ts b/packages/mobile/src/hooks/useMigration.ts index e91e2f4a8..15911eefd 100644 --- a/packages/mobile/src/hooks/useMigration.ts +++ b/packages/mobile/src/hooks/useMigration.ts @@ -69,7 +69,7 @@ export const useMigration = () => { setLastEnteredPasscode(passcode); - const walletsInfo = await tk.getWalletsInfo(mnemonic, false); + const walletsInfo = await tk.getWalletsInfoByMnemonic(mnemonic, false); const shouldChooseWallets = !lockupConfig && walletsInfo.length > 1; diff --git a/packages/mobile/src/hooks/useShouldShowTokensButton.ts b/packages/mobile/src/hooks/useShouldShowTokensButton.ts index 1107da9af..65adb40f4 100644 --- a/packages/mobile/src/hooks/useShouldShowTokensButton.ts +++ b/packages/mobile/src/hooks/useShouldShowTokensButton.ts @@ -1,14 +1,13 @@ -import { useJettons, useNftsState } from '@tonkeeper/shared/hooks'; -import { useTonInscriptions } from '@tonkeeper/shared/query/hooks/useTonInscriptions'; +import { useInscriptionData } from '$core/ManageTokens/hooks/useInscriptionData'; +import { useJettonData } from '$core/ManageTokens/hooks/useJettonData'; +import { useNftData } from '$core/ManageTokens/hooks/useNftData'; export const useShouldShowTokensButton = () => { - const { jettonBalances } = useJettons(); - const inscriptions = useTonInscriptions(); - const { accountNfts } = useNftsState(); + const jettonData = useJettonData(); + const inscriptionData = useInscriptionData(); + const nftData = useNftData(); return Boolean( - inscriptions.items?.length || - jettonBalances.length > 0 || - Object.keys(accountNfts).length > 0, + jettonData.length > 0 || nftData.length > 0 || inscriptionData.length > 0, ); }; diff --git a/packages/mobile/src/ledger/index.ts b/packages/mobile/src/ledger/index.ts new file mode 100644 index 000000000..464b96dfb --- /dev/null +++ b/packages/mobile/src/ledger/index.ts @@ -0,0 +1,4 @@ +export * from './useBluetoothAvailable'; +export * from './useConnectLedger'; +export * from './usePairLedger'; +export * from './useLedgerAccounts'; diff --git a/packages/mobile/src/ledger/useBluetoothAvailable.ts b/packages/mobile/src/ledger/useBluetoothAvailable.ts new file mode 100644 index 000000000..cbf0a5390 --- /dev/null +++ b/packages/mobile/src/ledger/useBluetoothAvailable.ts @@ -0,0 +1,53 @@ +import { + checkAndRequestAndroidBluetooth, + showBluetoothPermissionsAlert, + showBluetoothPoweredOffAlert, +} from '$utils/bluetoothPermissions'; +import TransportBLE from '@ledgerhq/react-native-hw-transport-ble'; +import { delay } from '@tonkeeper/core'; +import { isIOS } from '@tonkeeper/uikit'; +import { useEffect, useState } from 'react'; +import { Observable } from 'rxjs'; + +export const useBluetoothAvailable = () => { + const [available, setAvailable] = useState(false); + + useEffect(() => { + const subscription = new Observable(TransportBLE.observeState).subscribe({ + next: async (event) => { + if (event.type === 'Unauthorized') { + console.log('Bluetooth Unauthorized'); + if (isIOS) { + showBluetoothPermissionsAlert(); + } + } + if (event.type === 'PoweredOff') { + console.log('Bluetooth Powered Off'); + showBluetoothPoweredOffAlert(); + } + + if (event.type === 'PoweredOn') { + if (isIOS) { + setAvailable(event.available); + } else { + try { + await delay(1000); + const granted = await checkAndRequestAndroidBluetooth(); + setAvailable(granted); + } catch { + setAvailable(false); + } + } + } + }, + complete: () => {}, + error: () => {}, + }); + + return () => { + subscription.unsubscribe(); + }; + }, []); + + return available; +}; diff --git a/packages/mobile/src/ledger/useConnectLedger.ts b/packages/mobile/src/ledger/useConnectLedger.ts new file mode 100644 index 000000000..174488399 --- /dev/null +++ b/packages/mobile/src/ledger/useConnectLedger.ts @@ -0,0 +1,103 @@ +import TransportBLE from '@ledgerhq/react-native-hw-transport-ble'; +import Transport from '@ledgerhq/hw-transport'; +import { useCallback, useEffect, useState } from 'react'; +import { TonTransport } from '@ton-community/ton-ledger'; + +export function useConnectLedger(deviceId: string | null) { + const [transport, setTransport] = useState(null); + const [tonTransport, setTonTransport] = useState(null); + const [isAppOpenRetry, setIsAppOpenRetry] = useState(0); + + const [connectError, setConnectError] = useState(false); + + const connect = useCallback(async () => { + if (!deviceId) { + return; + } + + try { + const bleTransport = await TransportBLE.open(deviceId); + setTransport(bleTransport); + console.log('bleTransport', bleTransport.device.id); + } catch (error) { + console.log('connect error', error.message); + setConnectError(true); + } + }, [deviceId]); + + useEffect(() => { + connect(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [deviceId]); + + useEffect(() => { + if (!connectError) { + return; + } + + const timeout = setTimeout(() => { + setConnectError(false); + connect(); + }, 3000); + + return () => { + clearTimeout(timeout); + }; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [connectError]); + + useEffect(() => { + let timeout: any; + + if (!transport) { + return; + } + + const handler = () => { + console.log('disconnect, retry...'); + connect(); + timeout = setTimeout(() => { + setTransport(null); + setTonTransport(null); + }, 2000); + }; + + transport.on('disconnect', handler); + + return () => { + transport.off('disconnect', handler); + transport.close(); + if (timeout) { + clearTimeout(timeout); + } + }; + }, [transport, connect]); + + useEffect(() => { + if (!transport) { + return; + } + + const timeout = setTimeout(async () => { + const newTonTransport = new TonTransport(transport); + try { + const isAppOpen = await newTonTransport.isAppOpen(); + if (isAppOpen) { + console.log('setTonTransport '); + setTonTransport(newTonTransport); + } else { + setIsAppOpenRetry((s) => s + 1); + } + } catch (e) { + console.log('isAppOpen error', e); + } + }, 1000); + + return () => { + clearTimeout(timeout); + setTonTransport(null); + }; + }, [transport, isAppOpenRetry]); + + return { transport, tonTransport }; +} diff --git a/packages/mobile/src/ledger/useLedgerAccounts.ts b/packages/mobile/src/ledger/useLedgerAccounts.ts new file mode 100644 index 000000000..00bcd96e9 --- /dev/null +++ b/packages/mobile/src/ledger/useLedgerAccounts.ts @@ -0,0 +1,37 @@ +import { getLedgerAccountPathByIndex } from '$utils/ledger'; +import { TonTransport } from '@ton-community/ton-ledger'; +import { Address } from '@ton/core'; +import { useCallback } from 'react'; + +export const useLedgerAccounts = ( + tonTransport: TonTransport | null, + deviceId: string | null, +) => { + const getAccounts = useCallback(async () => { + if (!tonTransport) { + throw new Error('No transport'); + } + + if (!deviceId) { + throw new Error('No device id'); + } + + const run = Array.from({ length: 10 }).map((_, i) => i); + const res: { address: string; pubkey: string; index: number }[] = []; + + for (const index of run) { + const path = getLedgerAccountPathByIndex(index); + const addr = await tonTransport.getAddress(path); + + res.push({ + address: Address.parse(addr.address).toRawString(), + pubkey: addr.publicKey.toString('hex'), + index, + }); + } + + return res; + }, [deviceId, tonTransport]); + + return getAccounts; +}; diff --git a/packages/mobile/src/ledger/usePairLedger.ts b/packages/mobile/src/ledger/usePairLedger.ts new file mode 100644 index 000000000..398b799e8 --- /dev/null +++ b/packages/mobile/src/ledger/usePairLedger.ts @@ -0,0 +1,43 @@ +import TransportBLE from '@ledgerhq/react-native-hw-transport-ble'; +import { useEffect, useState } from 'react'; + +export const usePairLedger = (available: boolean) => { + const [deviceId, setDeviceId] = useState(null); + + useEffect(() => { + if (!available) { + return; + } + + let currentDeviceId = ''; + + const subscription = TransportBLE.listen({ + complete: () => { + // this.setState({ refreshing: false }); + }, + next: async (event) => { + if (event.type === 'add') { + const device = event.descriptor; + // prevent duplicate alerts + if (currentDeviceId === device.id) { + return; + } + // set the current device id to prevent duplicate alerts + currentDeviceId = device.id; + + setDeviceId(device.id); + } + }, + error: (error) => { + console.log('Error Pairing'); + // this.setState({ error, refreshing: false }); + }, + }); + + return () => { + subscription.unsubscribe(); + }; + }, [available]); + + return deviceId; +}; diff --git a/packages/mobile/src/modals/BurnVouchersModal.tsx b/packages/mobile/src/modals/BurnVouchersModal.tsx new file mode 100644 index 000000000..f8b005441 --- /dev/null +++ b/packages/mobile/src/modals/BurnVouchersModal.tsx @@ -0,0 +1,232 @@ +import { + Button, + Icon, + List, + Modal, + Spacer, + Steezy, + Text, + Toast, + TouchableOpacity, + View, +} from '@tonkeeper/uikit'; +import { memo, useCallback, useMemo, useState } from 'react'; +import { t } from '@tonkeeper/shared/i18n'; +import { HideableImage } from '$core/HideableAmount/HideableImage'; +import { formatter } from '@tonkeeper/shared/formatter'; +import { useNftsState } from '@tonkeeper/shared/hooks'; +import { ContractService, OpCodes } from '@tonkeeper/core'; +import { config } from '$config'; +import { Linking } from 'react-native'; +import { openSignRawModal } from '$core/ModalContainer/NFTOperations/Modals/SignRawModal'; +import { getTimeSec } from '$utils/getTimeSec'; +import { tk } from '$wallet'; +import { Ton } from '$libs/Ton'; +import { BatterySupportedTransaction } from '$wallet/managers/BatteryManager'; +import { Address, beginCell, toNano } from '@ton/core'; +import { checkBurnDate, getNotcoinBurnAddress } from '$utils/notcoin'; + +interface BurnVouchersModalProps { + max?: boolean; +} + +export const BurnVouchersModal = memo((props) => { + const { max = false } = props; + + const nfts = useNftsState((s) => + Object.values(s.accountNfts).filter( + (nft) => + !nft.sale && + nft.collection && + Address.parse(nft.collection.address).equals( + Address.parse(config.get('notcoin_nft_collection')), + ), + ), + ); + + const maxCount = nfts.length > 4 ? 4 : nfts.length; + + const [count, setCount] = useState(max ? maxCount : 1); + + const selectedNfts = useMemo(() => nfts.slice(0, count), [count, nfts]); + + const totalValue = selectedNfts + .reduce( + (acc, nft) => + acc + parseInt(nft.metadata?.attributes?.[0]?.value?.replace(',', '') ?? '0', 10), + 0, + ) + .toString(); + + const handleContinue = useCallback(async () => { + try { + const valid_until = getTimeSec() + 10 * 60; + + const isAvailable = await checkBurnDate(); + + if (!isAvailable) { + return; + } + + openSignRawModal( + { + source: tk.wallet.address.ton.raw, + valid_until, + messages: selectedNfts.map((nft) => ({ + address: nft.address, + amount: Ton.toNano('0.1'), + payload: beginCell() + .storeUint(OpCodes.NFT_TRANSFER, 32) + .storeUint(ContractService.getWalletQueryId(), 64) + .storeAddress(getNotcoinBurnAddress(nft.address)) + .storeAddress(Address.parse(tk.wallet.address.ton.raw)) + .storeBit(false) + .storeCoins(toNano('0.05')) + .storeBit(false) + .storeUint(0x5fec6642, 32) + .storeUint(nft.index, 64) + .endCell() + .toBoc() + .toString('base64'), + })), + }, + { + experimentalWithBattery: + tk.wallet.battery.state.data.supportedTransactions[ + BatterySupportedTransaction.NFT + ], + expires_sec: valid_until, + response_options: { + broadcast: false, + }, + }, + ); + } catch (e) { + if (e.message) { + Toast.fail(e.message); + } + } + }, [selectedNfts]); + + const openTonkeeperPro = useCallback(() => { + Linking.openURL(config.get('tonkeeper_pro_url')).catch(null); + }, []); + + const imageUrl = (nfts[0].previews && + nfts[0].previews.find((preview) => preview.resolution === '500x500')!.url)!; + + return ( + + + + + + + + + + {t('notcoin.exchange_title')} + + + + {t('notcoin.exchange_subtitle')} + + + + + setCount((s) => (s > 1 ? s - 1 : s))} + > + + + + {count} + + setCount((s) => (s < 4 ? s + 1 : s))} + > + + + + } + /> + + + + + {withMaxButton && ( + + )} {isInsufficientBalance || isLessThanMin ? ( {isInsufficientBalance ? t('send_screen_steps.amount.insufficient_balance') - : t('send_screen_steps.amount.less_than_min', { minAmount })} + : t('send_screen_steps.amount.less_than_min_with_symbol', { + minAmount, + symbol: currencyTitle, + })} ) : ( diff --git a/packages/mobile/src/shared/components/CellSection/CellSection.interface.ts b/packages/mobile/src/shared/components/CellSection/CellSection.interface.ts index 093902933..970400d31 100644 --- a/packages/mobile/src/shared/components/CellSection/CellSection.interface.ts +++ b/packages/mobile/src/shared/components/CellSection/CellSection.interface.ts @@ -8,4 +8,5 @@ export interface CellProps { indicator?: ReactElement; inlineContent?: ReactElement | JSX.Element | null; content?: React.ReactNode; + unwrapChildren?: boolean; } diff --git a/packages/mobile/src/shared/components/CellSection/CellSection.tsx b/packages/mobile/src/shared/components/CellSection/CellSection.tsx index 9c9791312..c5e288bcd 100644 --- a/packages/mobile/src/shared/components/CellSection/CellSection.tsx +++ b/packages/mobile/src/shared/components/CellSection/CellSection.tsx @@ -36,6 +36,7 @@ export const CellSectionItem = forwardRef>( indicator, inlineContent, content, + unwrapChildren = false, } = props; return ( @@ -52,10 +53,12 @@ export const CellSectionItem = forwardRef>( {content ? ( content - ) : ( + ) : !unwrapChildren ? ( {children} + ) : ( + children )} {inlineContent} diff --git a/packages/mobile/src/shared/components/ErrorBoundary.tsx b/packages/mobile/src/shared/components/ErrorBoundary.tsx index c9b5be623..d3777b417 100644 --- a/packages/mobile/src/shared/components/ErrorBoundary.tsx +++ b/packages/mobile/src/shared/components/ErrorBoundary.tsx @@ -6,7 +6,7 @@ type ErrorBoundaryState = { isError: boolean; retry: number; message: string; -} +}; export class ErrorBoundary extends React.Component<{}, ErrorBoundaryState> { constructor(props: {}) { @@ -21,21 +21,16 @@ export class ErrorBoundary extends React.Component<{}, ErrorBoundaryState> { } private refresh = () => { - this.setState({ + this.setState({ retry: this.state.retry + 1, - isError: false, + isError: false, message: '', }); - } + }; render() { if (this.state.isError) { - return ( - - ); + return ; } return ( diff --git a/packages/mobile/src/shared/constants/config.ts b/packages/mobile/src/shared/constants/config.ts index c995831c8..421de5cbb 100644 --- a/packages/mobile/src/shared/constants/config.ts +++ b/packages/mobile/src/shared/constants/config.ts @@ -48,67 +48,6 @@ export enum WalletCurrency { export type FiatCurrency = (typeof WalletCurrency)[keyof typeof WalletCurrency]; -export enum SelectableVersions { - V4R2 = 'v4R2', - V4R1 = 'v4R1', - V3R2 = 'v3R2', - V3R1 = 'v3R1', -} - -export type SelectableVersion = - (typeof SelectableVersions)[keyof typeof SelectableVersions]; - -export const PrimaryCryptoCurrencies = [ - CryptoCurrencies.Ton, - CryptoCurrencies.Eth, - CryptoCurrencies.Btc, -]; - -export const SecondaryCryptoCurrencies = [ - CryptoCurrencies.Eth, - CryptoCurrencies.Usdt, - CryptoCurrencies.Btc, - CryptoCurrencies.Usdc, - CryptoCurrencies.Dai, - // CryptoCurrencies.Wbtc, -]; - -export const SwapCurrencies = [ - CryptoCurrencies.Ton, - CryptoCurrencies.Btc, - CryptoCurrencies.Eth, - CryptoCurrencies.Usdt, -]; - -export const CurrenciesIcons = { - [CryptoCurrencies.Ton]: require('$assets/currency/ic-ton-48.png'), - [CryptoCurrencies.TonLocked]: require('$assets/currency/ic-ton-48.png'), - [CryptoCurrencies.TonRestricted]: require('$assets/currency/ic-ton-48.png'), - [CryptoCurrencies.Eth]: require('$assets/currency/ic-eth-48.png'), - [CryptoCurrencies.Btc]: require('$assets/currency/ic-btc-48.png'), - [CryptoCurrencies.Usdt]: require('$assets/currency/ic-usdt-48.png'), - [CryptoCurrencies.Wbtc]: require('$assets/currency/ic-wbtc-48.png'), - [CryptoCurrencies.Usdc]: require('$assets/currency/ic-usdc-48.png'), - [CryptoCurrencies.Dai]: require('$assets/currency/ic-dai-48.png'), -}; - -export const CurrencyLongName = { - [CryptoCurrencies.Ton]: 'TON', - [CryptoCurrencies.TonLocked]: 'TON, locked', - [CryptoCurrencies.TonRestricted]: 'TON, restricted', - [CryptoCurrencies.Eth]: 'Ethereum', - [CryptoCurrencies.Btc]: 'Bitcoin', - [CryptoCurrencies.Usdt]: 'Tether USDT', - [CryptoCurrencies.Wbtc]: 'Wrapped Bitcoin', - [CryptoCurrencies.Usdc]: 'USD Coin', - [CryptoCurrencies.Dai]: 'Maker DAI', -}; - -export const LockupNames = { - [CryptoCurrencies.TonLocked]: 'Locked Toncoin', - [CryptoCurrencies.TonRestricted]: 'Restricted Toncoin', -}; - export const Decimals = { [CryptoCurrencies.Ton]: 9, [CryptoCurrencies.TonLocked]: 9, @@ -228,21 +167,6 @@ export const FiatCurrencySymbolsConfig = { }, }; -export const SelectableVersionsConfig = { - [SelectableVersions.V3R1]: { - label: 'v3R1', - }, - [SelectableVersions.V3R2]: { - label: 'v3R2', - }, - [SelectableVersions.V4R1]: { - label: 'v4R1', - }, - [SelectableVersions.V4R2]: { - label: 'v4R2', - }, -}; - export const TokenConfig: { [index: string]: any } = { [CryptoCurrencies.Usdt]: { address: '0xdac17f958d2ee523a2206206994597c13d831ec7', @@ -289,18 +213,6 @@ export const TokenConfigTestnet: { [index: string]: any } = { }, }; -export function getTonBridgeAddress() { - return 'Ef_dJMSh8riPi3BTUTtcxsWjG8RLKnLctNjAM4rw8NN-xWdr'; -} - -export function getTonCollectorAddress() { - return 'EQCuzvIOXLjH2tv35gY4tzhIvXCqZWDuK9kUhFGXKLImgxT5'; -} - -export function getWTonAddress() { - return '0x582d872a1b094fc48f5de31d3b73f2d9be47def1'; -} - export const tonDiamondCollectionAddress = { mainnet: 'EQAG2BH0JlmFkbMrLEnyn2bIITaOSssd4WdisE4BdFMkZbir', testnet: 'EQDR1lqTwhPJKjkEbICwXBbarhxCKXqNOlRTDMMbxbqambV0', diff --git a/packages/mobile/src/shared/constants/serverConfig.ts b/packages/mobile/src/shared/constants/serverConfig.ts index fe3362097..14cddfa65 100644 --- a/packages/mobile/src/shared/constants/serverConfig.ts +++ b/packages/mobile/src/shared/constants/serverConfig.ts @@ -26,7 +26,6 @@ export interface ServerConfig { transactionExplorer: string; flags: Record; directSupportUrl: string; - tonkeeperNewsUrl: string; stakingInfoUrl: string; tonCommunityUrl: string; tonCommunityChatUrl: string; @@ -66,7 +65,6 @@ export function setServerConfig(data: any, isTestnet: boolean) { cachedMediaSalt: data.cachedMediaSalt, NFTOnExplorerUrl: data.NFTOnExplorerUrl || 'https://tonscan.org/nft/%s', directSupportUrl: data.directSupportUrl, - tonkeeperNewsUrl: data.tonkeeperNewsUrl, stakingInfoUrl: data.stakingInfoUrl, tonCommunityUrl: data.tonCommunityUrl, tonCommunityChatUrl: data.tonCommunityChatUrl, diff --git a/packages/mobile/src/shared/screens/ErrorScreen.tsx b/packages/mobile/src/shared/screens/ErrorScreen.tsx index 5e89dc3df..b3de71f1a 100644 --- a/packages/mobile/src/shared/screens/ErrorScreen.tsx +++ b/packages/mobile/src/shared/screens/ErrorScreen.tsx @@ -2,7 +2,7 @@ import React from 'react'; import Clipboard from '@react-native-community/clipboard'; import { StyleSheet, View } from 'react-native'; import { t } from '@tonkeeper/shared/i18n'; -import { Button, DevSeparator, Screen, Text } from '$uikit'; +import { Button, Screen, Spacer, Text } from '@tonkeeper/uikit'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; import { ns } from '$utils'; import { Toast } from '$store'; @@ -15,43 +15,38 @@ interface ErrorScreenProps { export const ErrorScreen: React.FC = (props) => { const safeArea = useSafeAreaInsets(); - const handleCopyLog = React.useCallback((value?: string) => () => { + const handleCopyLog = React.useCallback( + (value?: string) => () => { if (value) { Clipboard.setString(value); Toast.success(t('copied')); } }, - [t] + [], ); return ( - - {t('error_occurred')} - + {t('error_occurred')} - - - + - + {footerContent ?? ( + + + + )} ); }); diff --git a/packages/shared/modals/ActivityActionModal/ActivityActionModal.tsx b/packages/shared/modals/ActivityActionModal/ActivityActionModal.tsx index db6fa9a1e..7da72afe5 100644 --- a/packages/shared/modals/ActivityActionModal/ActivityActionModal.tsx +++ b/packages/shared/modals/ActivityActionModal/ActivityActionModal.tsx @@ -6,11 +6,15 @@ import { memo } from 'react'; import { ActionItem, ActionSource, + ActionType, AnyActionItem, } from '@tonkeeper/mobile/src/wallet/models/ActivityModel'; +import { NotcoinTransferActionContent } from './content/NotcoinTransferActionContent'; +import { NotcoinConfetti } from './components/NotcoinConfetti'; type ActivityActionModalProps = { action: AnyActionItem; + isNotCoin?: boolean; }; /** Payload with possibly big content to render. @@ -21,7 +25,7 @@ type ActivityActionModalProps = { const possiblyLargePayloadFields = ['payload', 'comment', 'encrypted_comment']; export const ActivityActionModal = memo((props) => { - const { action } = props; + const { action, isNotCoin } = props; const shouldWrapIntoScrollView = possiblyLargePayloadFields.findIndex((field) => (action as any)?.payload?.[field]) !== @@ -29,20 +33,34 @@ export const ActivityActionModal = memo((props) => { const Content = shouldWrapIntoScrollView ? Modal.ScrollView : Modal.Content; + const actionContent = + isNotCoin && action.type === ActionType.JettonTransfer ? ( + + ) : ( + renderActionModalContent(action) + ); + return ( - - - {renderActionModalContent(action)} - + <> + + + {actionContent} + + {isNotCoin ? : null} + ); }); -export async function openActivityActionModal(actionId: string, source: ActionSource) { +export async function openActivityActionModal( + actionId: string, + source: ActionSource, + isNotCoin?: boolean, +) { const openModal = (action: ActionItem) => { navigation.push('SheetsProvider', { $$action: SheetActions.ADD, component: ActivityActionModal, - params: { action }, + params: { action, isNotCoin }, path: 'TRANSACTION_DETAILS', }); }; diff --git a/packages/shared/modals/ActivityActionModal/components/NftItemPayload.tsx b/packages/shared/modals/ActivityActionModal/components/NftItemPayload.tsx index 7bb1b3631..6c743fa7b 100644 --- a/packages/shared/modals/ActivityActionModal/components/NftItemPayload.tsx +++ b/packages/shared/modals/ActivityActionModal/components/NftItemPayload.tsx @@ -1,6 +1,6 @@ import { useNftItemByAddress } from '../../../query/hooks/useNftItemByAddress'; -import { Icon, TouchableOpacity, View, useTheme } from '@tonkeeper/uikit'; -import { NftItem } from '@tonkeeper/core/src/TonAPI'; +import { Icon, TouchableOpacity, useTheme, View } from '@tonkeeper/uikit'; +import { NftItem, TrustType } from '@tonkeeper/core/src/TonAPI'; import { memo, useCallback } from 'react'; import { StyleSheet } from 'react-native'; import { t } from '../../../i18n'; @@ -9,6 +9,9 @@ import { HideableImage } from '@tonkeeper/mobile/src/core/HideableAmount/Hideabl import { corners } from '@tonkeeper/uikit/src/styles/constants'; import { openNftModal } from '@tonkeeper/mobile/src/core/NFT/NFT'; import { HideableAmount } from '@tonkeeper/mobile/src/core/HideableAmount/HideableAmount'; +import { TokenApprovalStatus } from '@tonkeeper/mobile/src/wallet/managers/TokenApprovalManager'; +import { Address } from '../../../Address'; +import { useTokenApproval } from '../../../hooks'; interface NftItemPayloadProps { nftAddress?: string; @@ -18,6 +21,7 @@ interface NftItemPayloadProps { export const NftItemPayload = memo((props) => { const { data: nft } = useNftItemByAddress(props.nftAddress, { existingNft: props.nft }); const theme = useTheme(); + const approvalStatuses = useTokenApproval((state) => state.tokens); const handleOpenNftItem = useCallback(() => { if (nft) { @@ -29,6 +33,21 @@ export const NftItemPayload = memo((props) => { return null; } + const approvalIdentifier = Address.parse( + nft.collection?.address ?? nft.address, + ).toRaw(); + const nftApprovalStatus = approvalStatuses[approvalIdentifier]; + + if ( + [TokenApprovalStatus.Spam, TokenApprovalStatus.Declined].includes( + nftApprovalStatus?.current, + ) || + (nft?.trust === TrustType.Blacklist && + nftApprovalStatus?.current !== TokenApprovalStatus.Approved) + ) { + return null; + } + return ( {nft.image.small && ( diff --git a/packages/shared/modals/ActivityActionModal/components/NotcoinConfetti.tsx b/packages/shared/modals/ActivityActionModal/components/NotcoinConfetti.tsx new file mode 100644 index 000000000..b704e1db2 --- /dev/null +++ b/packages/shared/modals/ActivityActionModal/components/NotcoinConfetti.tsx @@ -0,0 +1,26 @@ +import { Lottie, Steezy, View, deviceHeight, deviceWidth } from '@tonkeeper/uikit'; +import { memo } from 'react'; + +const confettiWidth = deviceHeight * 0.5625; + +export const NotcoinConfetti = memo(() => { + return ( + + + + ); +}); + +const styles = Steezy.create(() => ({ + container: { + position: 'absolute', + left: (deviceWidth - confettiWidth) / 2, + bottom: 0, + width: confettiWidth, + height: deviceHeight, + }, + lottie: { + width: confettiWidth, + height: deviceHeight, + }, +})); diff --git a/packages/shared/modals/ActivityActionModal/content/JettonSwapActionContent.tsx b/packages/shared/modals/ActivityActionModal/content/JettonSwapActionContent.tsx index 4dd21d54a..56cc82334 100644 --- a/packages/shared/modals/ActivityActionModal/content/JettonSwapActionContent.tsx +++ b/packages/shared/modals/ActivityActionModal/content/JettonSwapActionContent.tsx @@ -16,6 +16,7 @@ import { ActionItem, ActionType, } from '@tonkeeper/mobile/src/wallet/models/ActivityModel'; +import { ActionStatusEnum } from '@tonkeeper/core/src/TonAPI'; interface JettonSwapActionContentProps { action: ActionItem; @@ -44,7 +45,9 @@ export const JettonSwapActionContent = memo((props Address.parse(payload.jetton_master_in.address).toFriendly(), ); if (tokenPrice.fiat) { - const parsedAmount = parseFloat(formatter.fromNano(payload.amount_in, 9)); + const parsedAmount = parseFloat( + formatter.fromNano(payload.amount_in, payload.jetton_master_in.decimals ?? 9), + ); return format(tokenPrice.fiat * parsedAmount, { currency: fiatCurrency, decimals: 9, @@ -115,6 +118,10 @@ export const JettonSwapActionContent = memo((props return ( ((pro hideName={isScam} /> - {!!action.payload.comment && ( + {!!action.payload.comment && !action.event.is_scam && ( ((pro valueMultiline /> )} - {action.payload?.encrypted_comment && ( + {action.payload?.encrypted_comment && !action.event.is_scam && ( - {action.payload?.encrypted_comment && ( + {action.payload?.encrypted_comment && !action.event.is_scam && ( )} - {!!action.payload.comment && ( + {!!action.payload.comment && !action.event.is_scam && ( ; +} + +const unverifiedTokenHitSlop = { top: 4, left: 4, bottom: 4, right: 4 }; + +export const NotcoinTransferActionContent = memo((props) => { + const { action } = props; + + const source = { uri: action.payload.jetton?.image }; + + const nav = useNavigation(); + + return ( + } + subtitle={ + !config.get('disable_show_unverified_token') && + action.payload.jetton.verification === JettonVerificationType.None && ( + + + {t('approval.unverified_token')} + + + ) + } + action={action} + footerContent={ + +