From f53e2b006b809c99b5731696b7e244b0c6ee8f7b Mon Sep 17 00:00:00 2001 From: antongotech <105588893+antongotech@users.noreply.github.com> Date: Thu, 26 Jan 2023 11:18:31 +0200 Subject: [PATCH 01/28] tonconnect 2.0 added --- package-lock.json | 64 +++++++++ package.json | 1 + src/App.tsx | 23 +++- src/components/Navbar/Menu/WalletAddress.tsx | 2 + src/components/SelectWallet/AdaptersList.tsx | 14 +- .../SelectWallet/DesktopFlow/index.tsx | 64 ++++++--- .../wallets/adapters/TonConnectAdapter.ts | 122 ++++++++++++++++++ src/services/wallets/config.ts | 33 ++--- src/services/wallets/types.ts | 2 + 9 files changed, 284 insertions(+), 41 deletions(-) create mode 100644 src/services/wallets/adapters/TonConnectAdapter.ts diff --git a/package-lock.json b/package-lock.json index c874eccaf..f753ea1f8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,6 +21,7 @@ "@testing-library/jest-dom": "^5.16.2", "@testing-library/react": "^12.1.4", "@testing-library/user-event": "^13.5.0", + "@tonconnect/sdk": "^2.0.7", "@types/jest": "^27.4.1", "@types/node": "^16.11.26", "@types/react": "^17.0.41", @@ -8540,6 +8541,26 @@ "@testing-library/dom": ">=7.21.4" } }, + "node_modules/@tonconnect/protocol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@tonconnect/protocol/-/protocol-2.0.1.tgz", + "integrity": "sha512-jkSj6EKjIlHnJxrtxdlO7KqVJe41yrIgqamGZiqziKH6iwx0m9YyKvuIREd6CmWY2jbsev3BvBWqPp9KH6HrRw==", + "dependencies": { + "tweetnacl-util": "^0.15.1" + } + }, + "node_modules/@tonconnect/sdk": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@tonconnect/sdk/-/sdk-2.0.7.tgz", + "integrity": "sha512-MDbA5RhkVbSQQYXsLuVXLATROSQgDJSIx9Y2HIPohfU44PH6vaJ1cBrv9nogIoAQUM2tNuBXC0w+RcFT+eRTCg==", + "dependencies": { + "@tonconnect/protocol": "^2.0.1", + "deepmerge": "^4.2.2", + "eventsource": "^2.0.2", + "node-fetch": "^2.6.7", + "tweetnacl": "^1.0.3" + } + }, "node_modules/@tootallnate/once": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", @@ -13936,6 +13957,14 @@ "node": ">=0.8.x" } }, + "node_modules/eventsource": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-2.0.2.tgz", + "integrity": "sha512-IzUmBGPR3+oUG9dUeXynyNmf91/3zUSJg1lCktzKw47OXuhco54U3r9B7O4XX+Rb1Itm9OZ2b0RkTs10bICOxA==", + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/exec-async": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/exec-async/-/exec-async-2.2.0.tgz", @@ -28453,6 +28482,11 @@ "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==" }, + "node_modules/tweetnacl-util": { + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/tweetnacl-util/-/tweetnacl-util-0.15.1.tgz", + "integrity": "sha512-RKJBIj8lySrShN4w6i/BonWp2Z/uxwC3h4y7xsRrpP59ZboCd0GpEVsOnMDYLMmKBpYhb5TgHzZXy7wTfYFBRw==" + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -36162,6 +36196,26 @@ "@babel/runtime": "^7.12.5" } }, + "@tonconnect/protocol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@tonconnect/protocol/-/protocol-2.0.1.tgz", + "integrity": "sha512-jkSj6EKjIlHnJxrtxdlO7KqVJe41yrIgqamGZiqziKH6iwx0m9YyKvuIREd6CmWY2jbsev3BvBWqPp9KH6HrRw==", + "requires": { + "tweetnacl-util": "^0.15.1" + } + }, + "@tonconnect/sdk": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@tonconnect/sdk/-/sdk-2.0.7.tgz", + "integrity": "sha512-MDbA5RhkVbSQQYXsLuVXLATROSQgDJSIx9Y2HIPohfU44PH6vaJ1cBrv9nogIoAQUM2tNuBXC0w+RcFT+eRTCg==", + "requires": { + "@tonconnect/protocol": "^2.0.1", + "deepmerge": "^4.2.2", + "eventsource": "^2.0.2", + "node-fetch": "^2.6.7", + "tweetnacl": "^1.0.3" + } + }, "@tootallnate/once": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", @@ -40312,6 +40366,11 @@ "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==" }, + "eventsource": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-2.0.2.tgz", + "integrity": "sha512-IzUmBGPR3+oUG9dUeXynyNmf91/3zUSJg1lCktzKw47OXuhco54U3r9B7O4XX+Rb1Itm9OZ2b0RkTs10bICOxA==" + }, "exec-async": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/exec-async/-/exec-async-2.2.0.tgz", @@ -51276,6 +51335,11 @@ "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==" }, + "tweetnacl-util": { + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/tweetnacl-util/-/tweetnacl-util-0.15.1.tgz", + "integrity": "sha512-RKJBIj8lySrShN4w6i/BonWp2Z/uxwC3h4y7xsRrpP59ZboCd0GpEVsOnMDYLMmKBpYhb5TgHzZXy7wTfYFBRw==" + }, "type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", diff --git a/package.json b/package.json index 49cce8799..8d78c9716 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "@testing-library/jest-dom": "^5.16.2", "@testing-library/react": "^12.1.4", "@testing-library/user-event": "^13.5.0", + "@tonconnect/sdk": "^2.0.7", "@types/jest": "^27.4.1", "@types/node": "^16.11.26", "@types/react": "^17.0.41", diff --git a/src/App.tsx b/src/App.tsx index 6cd908c66..8d84d5884 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -10,9 +10,11 @@ import useEffectOnce from "hooks/useEffectOnce"; import { useWebAppResize } from "store/application/hooks"; import './services/i18next/i18n'; import { useEffect } from 'react' -import { getHttpEndpoint } from '@orbs-network/ton-access' import { TonClient } from 'ton' import { setClienT } from 'services/api' +import { getSupportedWallets } from 'services/wallets/adapters/TonConnectAdapter' +import { adapters } from 'services/wallets/config' +import { Adapters } from 'services/wallets/types' const StyledAppContainer = styled(Box)({ display: "flex", @@ -48,6 +50,25 @@ const App = () => { })(); }, []) + useEffect(() => { + const fetchTonConnectDevices = async () => { + const supportedWallets = await getSupportedWallets() + supportedWallets.map((w) => { + adapters.push({ + name: w.name, + type: w.name === 'OpenMask' ? Adapters.OPENMASK : Adapters.TON_KEEPER, + icon: w.imageUrl, + mobileCompatible: w.name !== 'OpenMask', + description: w.name === 'OpenMask' + ? 'TON Wallet Plugin for Google Chrome' + : 'A mobile wallet in your pocket', + tonConnect: true, + }) + }) + } + fetchTonConnectDevices() + },[]) + return ( <> diff --git a/src/components/Navbar/Menu/WalletAddress.tsx b/src/components/Navbar/Menu/WalletAddress.tsx index dba5907cf..6541761ef 100644 --- a/src/components/Navbar/Menu/WalletAddress.tsx +++ b/src/components/Navbar/Menu/WalletAddress.tsx @@ -16,6 +16,7 @@ import { useTranslation } from "react-i18next"; import { isMobile } from "react-device-detect"; import SelectLanguage from "./SelectLanguage"; import { isTelegramWebApp } from "utils"; +import { disconnectTC } from 'services/wallets/adapters/TonConnectAdapter' const StyledIconButton = styled("button")({ cursor: "pointer", @@ -51,6 +52,7 @@ const WalletAddress = observer(() => { const onDisconnect = () => { resetWallet(); + disconnectTC(); setShowDisconnect(false); gaAnalytics.disconnect(); }; diff --git a/src/components/SelectWallet/AdaptersList.tsx b/src/components/SelectWallet/AdaptersList.tsx index a2a467af9..f88f32b3b 100644 --- a/src/components/SelectWallet/AdaptersList.tsx +++ b/src/components/SelectWallet/AdaptersList.tsx @@ -13,6 +13,7 @@ import { Adapter, Adapters } from "services/wallets/types"; import CircularProgress from "@mui/material/CircularProgress"; import gaAnalytics from "services/analytics/ga/ga"; import { useTranslation } from "react-i18next"; +import {isMobile} from 'react-device-detect' const StyledListItem = styled(ListItem)( ({ disabled }: { disabled?: boolean }) => ({ @@ -78,7 +79,7 @@ const StyledIcon = styled(Box)({ }); interface Props { - select: (adapter: Adapters) => void; + select: (adapter: Adapters, supportsTonConnect?: boolean) => void; open: boolean; onClose: () => void; adapters: Adapter[]; @@ -97,9 +98,10 @@ function AdaptersList({ title = "Select Wallet" }: Props) { const { t } = useTranslation() + const adaptersToShow = isMobile ? adapters.filter((adapter) => adapter.mobileCompatible) : adapters - const onSelect = (adapter: Adapters) => { - select(adapter) + const onSelect = (adapter: Adapters, supportsTonConnect?: boolean) => { + select(adapter, supportsTonConnect) gaAnalytics.selectWallet(adapter) } @@ -116,8 +118,8 @@ function AdaptersList({ - {adapters.map((adapter) => { - const { type, icon, name, description, disabled } = adapter; + {adaptersToShow.map((adapter) => { + const { type, icon, name, description, disabled, tonConnect } = adapter; return ( { } : () => onSelect(type)} + onClick={disabled ? () => { } : () => onSelect(type, tonConnect)} > diff --git a/src/components/SelectWallet/DesktopFlow/index.tsx b/src/components/SelectWallet/DesktopFlow/index.tsx index db1d9eca8..0fecd421c 100644 --- a/src/components/SelectWallet/DesktopFlow/index.tsx +++ b/src/components/SelectWallet/DesktopFlow/index.tsx @@ -1,11 +1,14 @@ -import { styled } from '@mui/styles'; -import { Box } from "@mui/material"; -import QR from "./QR"; -import AdaptersList from "../AdaptersList"; -import { useState } from "react"; -import { Adapters } from "services/wallets/types"; -import { adapters } from "services/wallets/config"; -import { useWalletActions, useWalletStore } from 'store/wallet/hooks'; +import { styled } from '@mui/styles' +import { Box } from '@mui/material' +import QR from './QR' +import AdaptersList from '../AdaptersList' +import { useState } from 'react' +import { Adapters } from 'services/wallets/types' +import { adapters } from 'services/wallets/config' +import { useWalletActions, useWalletStore } from 'store/wallet/hooks' +import { connect } from 'services/wallets/adapters/TonConnectAdapter' +import { updateWallet } from 'store/wallet/actions' +import { useDispatch } from 'react-redux' const StyledContainer = styled(Box)({ display: "flex", @@ -25,16 +28,41 @@ const DesktopFlow = ({ closeModal }: Props) => { const [ adapter, setAdapter ] = useState(Adapters.TON_HUB); const { sessionLink } = useWalletStore() const [showQr, setShowQr] = useState(false); + const [link ,setLink] = useState(''); + const dispatch = useDispatch() - const onSelect = (adapter: Adapters) => { - resetWallet(); - setAdapter(adapter); - createWalletSession(adapter); - if (adapter === Adapters.TON_HUB) { - setShowQr(true); - } - if (adapter === Adapters.TON_KEEPER) { - setShowQr(true); + const onSelect = async (adapter: Adapters, supportsTonConnect?: boolean) => { + if(adapter === Adapters.OPENMASK) { + const provider = window.ton + //@ts-ignore + if(provider?.isOpenMask) { + const accounts = await provider.send("ton_requestAccounts"); + const wallet = {address: accounts[0] } + dispatch(updateWallet({ wallet, adapterId: Adapters.TON_KEEPER })); + } else { + console.log('OpenMask is not installed!') + } + + } else if(supportsTonConnect) { + try { + const wallet = await connect(null, { + manifestUrl: "https://tonverifier.live/tonconnect-manifest.json", + onSessionLinkReady: (val: string) => { + setLink(val) + setShowQr(true) + }, + }) + dispatch(updateWallet({ wallet, adapterId: Adapters.TON_KEEPER })); + } catch (error) { + console.log(error) + } + } else { + resetWallet(); + setAdapter(adapter); + createWalletSession(adapter); + if (adapter === Adapters.TON_HUB) { + setShowQr(true); + } } }; @@ -52,7 +80,7 @@ const DesktopFlow = ({ closeModal }: Props) => { select={onSelect} /> - + ); } diff --git a/src/services/wallets/adapters/TonConnectAdapter.ts b/src/services/wallets/adapters/TonConnectAdapter.ts new file mode 100644 index 000000000..268c0d5f4 --- /dev/null +++ b/src/services/wallets/adapters/TonConnectAdapter.ts @@ -0,0 +1,122 @@ +import TonConnect, { + IStorage, + WalletInfo, + WalletInfoInjected, + WalletInfoRemote, +} from "@tonconnect/sdk"; +import { Address, Cell, StateInit } from 'ton' +import BN from 'bn.js' + +export const getWallets = () => { + return connector.getWallets() +} + +export interface TonWalletProvider { + connect(): Promise; + requestTransaction(request: TransactionDetails, onSuccess?: () => void): Promise; + disconnect(): Promise; +} + +export type TonkeeperProviderConfig = { + manifestUrl: string; + onSessionLinkReady: (link: string) => void; + storage?: IStorage; +}; + +export function stateInitToBuffer(s: StateInit): Buffer { + const INIT_CELL = new Cell(); + s.writeTo(INIT_CELL); + return INIT_CELL.toBoc(); +} + +export interface Wallet { + address: string; +} + +export interface TransactionDetails { + to: Address; + value: BN; + stateInit?: StateInit; + message?: Cell; +} + +export const connector = new TonConnect({ manifestUrl: "https://tonverifier.live/tonconnect-manifest.json"}); // add storage if needed + + export async function disconnectTC(): Promise { + await connector.disconnect(); +} + +export const getSupportedWallets = async () => await connector.getWallets(); + +function isInjected(walletInfo: WalletInfo): walletInfo is WalletInfoInjected { + return "jsBridgeKey" in walletInfo && "injected" in walletInfo && walletInfo.injected; +} + +function isRemote(walletInfo: WalletInfo): walletInfo is WalletInfoRemote { + return "universalLink" in walletInfo && "bridgeUrl" in walletInfo; +} + +export async function connect(walletInfo: any, config: any): Promise { + if (!walletInfo) { + const wallets = await connector.getWallets(); + walletInfo = wallets.find((w) => w.name === "Tonkeeper"); + if (!walletInfo) { + throw new Error("Tonkeeper wallet not found"); + } +} +const getWalletP = new Promise((resolve, reject) => { + connector.onStatusChange((wallet) => { + try { + if (wallet) { + resolve({ + address: Address.parse(wallet.account.address).toFriendly(), + }); + } else { + reject("No wallet received"); + } + } catch (e) { + reject(e); + } + }, reject); +}); + +await connector.restoreConnection(); + +if (!connector.connected) { + if (isInjected(walletInfo)) { + connector.connect({ jsBridgeKey: walletInfo.jsBridgeKey }); + } else if (isRemote(walletInfo)) { + const sessionLink = connector.connect({ + universalLink: walletInfo.universalLink, + bridgeUrl: walletInfo.bridgeUrl, + }); + config.onSessionLinkReady(sessionLink); + } else { + throw new Error("Unknown wallet type"); + } +} + +return getWalletP; +} + + +async function requestTransaction( + request: TransactionDetails, + onSuccess?: (() => void) | undefined +): Promise { + await connector.sendTransaction({ + validUntil: Date.now() + 5 * 60 * 1000, + messages: [ + { + address: request.to.toFriendly(), + amount: request.value.toString(), + stateInit: request.stateInit + ? stateInitToBuffer(request.stateInit).toString("base64") + : undefined, + payload: request.message ? request.message.toBoc().toString("base64") : undefined, + }, + ], + }); + + onSuccess?.(); +} \ No newline at end of file diff --git a/src/services/wallets/config.ts b/src/services/wallets/config.ts index 47513d928..6bd41b305 100644 --- a/src/services/wallets/config.ts +++ b/src/services/wallets/config.ts @@ -3,29 +3,30 @@ import TonhubImg from "assets/images/shared/tonhub.png"; import ChromeExtImg from "assets/images/shared/chrome.svg"; import TonKeeper from "assets/images/shared/tonkeeper.svg"; -const adapters: Adapter[] = [ - { - name: "Tonkeeper", - type: Adapters.TON_KEEPER, - icon: TonKeeper, - mobileCompatible: true, - description: "A mobile wallet in your pocket", - disabled: false, - }, +let adapters: Adapter[] = [ + // { + // name: "Tonkeeper", + // type: Adapters.TON_KEEPER, + // icon: TonKeeper, + // mobileCompatible: true, + // description: "A mobile wallet in your pocket", + // disabled: false, + // }, { name: "Tonhub", type: Adapters.TON_HUB, icon: TonhubImg, mobileCompatible: true, description: "Crypto wallet for Toncoin", + tonConnect: false, }, - { - name: "Google Chrome Plugin", - type: Adapters.TON_WALLET, - icon: ChromeExtImg, - mobileCompatible: false, - description: "TON Wallet Plugin for Google Chrome", - }, + // { + // name: "Google Chrome Plugin", + // type: Adapters.TON_WALLET, + // icon: ChromeExtImg, + // mobileCompatible: false, + // description: "TON Wallet Plugin for Google Chrome", + // }, ]; export { adapters }; diff --git a/src/services/wallets/types.ts b/src/services/wallets/types.ts index 0562b671b..e5aa999ae 100644 --- a/src/services/wallets/types.ts +++ b/src/services/wallets/types.ts @@ -41,6 +41,7 @@ export enum Adapters { TON_HUB = "tonhub", TON_WALLET = "ton-wallet", TON_KEEPER = "ton-keeper", + OPENMASK = 'openmask' } export interface Adapter { @@ -50,6 +51,7 @@ export interface Adapter { mobileCompatible: boolean; description: string; disabled?: boolean; + tonConnect?: boolean } export enum ActionCategory { From 0208c5a2cbdded4d07d8a90a4060a0cfe5dcb8a6 Mon Sep 17 00:00:00 2001 From: antongotech <105588893+antongotech@users.noreply.github.com> Date: Thu, 26 Jan 2023 11:21:07 +0200 Subject: [PATCH 02/28] cleanup --- src/services/wallets/config.ts | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/src/services/wallets/config.ts b/src/services/wallets/config.ts index 6bd41b305..d072599da 100644 --- a/src/services/wallets/config.ts +++ b/src/services/wallets/config.ts @@ -1,17 +1,7 @@ import { Adapter, Adapters } from "./types"; import TonhubImg from "assets/images/shared/tonhub.png"; -import ChromeExtImg from "assets/images/shared/chrome.svg"; -import TonKeeper from "assets/images/shared/tonkeeper.svg"; let adapters: Adapter[] = [ - // { - // name: "Tonkeeper", - // type: Adapters.TON_KEEPER, - // icon: TonKeeper, - // mobileCompatible: true, - // description: "A mobile wallet in your pocket", - // disabled: false, - // }, { name: "Tonhub", type: Adapters.TON_HUB, @@ -20,13 +10,6 @@ let adapters: Adapter[] = [ description: "Crypto wallet for Toncoin", tonConnect: false, }, - // { - // name: "Google Chrome Plugin", - // type: Adapters.TON_WALLET, - // icon: ChromeExtImg, - // mobileCompatible: false, - // description: "TON Wallet Plugin for Google Chrome", - // }, ]; export { adapters }; From 06bb4533c9895a39cc2718351f48b817c1f5e1e2 Mon Sep 17 00:00:00 2001 From: antongotech <105588893+antongotech@users.noreply.github.com> Date: Sun, 29 Jan 2023 16:16:16 +0200 Subject: [PATCH 03/28] fixes --- package-lock.json | 38 ----- package.json | 1 - src/App.tsx | 26 +--- .../SelectWallet/DesktopFlow/QR.tsx | 20 +-- .../SelectWallet/DesktopFlow/index.tsx | 68 ++------- .../SelectWallet/MobileFlow/index.tsx | 51 ++----- .../wallets/adapters/TonConnectAdapter.ts | 15 +- src/services/wallets/types.ts | 3 + src/store/wallet/actions.ts | 25 +++- src/store/wallet/hooks.ts | 131 ++++++++++-------- src/store/wallet/reducer.ts | 25 +++- 11 files changed, 157 insertions(+), 246 deletions(-) diff --git a/package-lock.json b/package-lock.json index f753ea1f8..a23b00ad9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -46,7 +46,6 @@ "react-ga4": "^1.4.1", "react-i18next": "^11.18.3", "react-number-format": "^4.9.1", - "react-qr-code": "^2.0.7", "react-qrcode-logo": "^2.8.0", "react-redux": "^8.0.2", "react-router-dom": "^6.2.2", @@ -24507,11 +24506,6 @@ "teleport": ">=0.2.0" } }, - "node_modules/qr.js": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/qr.js/-/qr.js-0.0.0.tgz", - "integrity": "sha1-ys6GOG9ZoNuAUPqQ2baw6IoeNk8=" - }, "node_modules/qrcode-generator": { "version": "1.4.4", "resolved": "https://registry.npmjs.org/qrcode-generator/-/qrcode-generator-1.4.4.tgz", @@ -25243,24 +25237,6 @@ "react-dom": "^0.14 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0" } }, - "node_modules/react-qr-code": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/react-qr-code/-/react-qr-code-2.0.7.tgz", - "integrity": "sha512-NpknL80p7dWbLdHfBSIxQIqLCu3+kBlyzYD692rO0UnVwfCSziIxc1xn/p3JhPEv1RV1lRD8j0w+hR3L7tawTQ==", - "dependencies": { - "prop-types": "^15.7.2", - "qr.js": "0.0.0" - }, - "peerDependencies": { - "react": "^16.x || ^17.x || ^18.x", - "react-native-svg": "*" - }, - "peerDependenciesMeta": { - "react-native-svg": { - "optional": true - } - } - }, "node_modules/react-qrcode-logo": { "version": "2.8.0", "resolved": "https://registry.npmjs.org/react-qrcode-logo/-/react-qrcode-logo-2.8.0.tgz", @@ -48236,11 +48212,6 @@ "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=" }, - "qr.js": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/qr.js/-/qr.js-0.0.0.tgz", - "integrity": "sha1-ys6GOG9ZoNuAUPqQ2baw6IoeNk8=" - }, "qrcode-generator": { "version": "1.4.4", "resolved": "https://registry.npmjs.org/qrcode-generator/-/qrcode-generator-1.4.4.tgz", @@ -48791,15 +48762,6 @@ "prop-types": "^15.7.2" } }, - "react-qr-code": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/react-qr-code/-/react-qr-code-2.0.7.tgz", - "integrity": "sha512-NpknL80p7dWbLdHfBSIxQIqLCu3+kBlyzYD692rO0UnVwfCSziIxc1xn/p3JhPEv1RV1lRD8j0w+hR3L7tawTQ==", - "requires": { - "prop-types": "^15.7.2", - "qr.js": "0.0.0" - } - }, "react-qrcode-logo": { "version": "2.8.0", "resolved": "https://registry.npmjs.org/react-qrcode-logo/-/react-qrcode-logo-2.8.0.tgz", diff --git a/package.json b/package.json index 8d78c9716..e09005138 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,6 @@ "react-ga4": "^1.4.1", "react-i18next": "^11.18.3", "react-number-format": "^4.9.1", - "react-qr-code": "^2.0.7", "react-qrcode-logo": "^2.8.0", "react-redux": "^8.0.2", "react-router-dom": "^6.2.2", diff --git a/src/App.tsx b/src/App.tsx index 8d84d5884..4f8a92886 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -12,9 +12,8 @@ import './services/i18next/i18n'; import { useEffect } from 'react' import { TonClient } from 'ton' import { setClienT } from 'services/api' -import { getSupportedWallets } from 'services/wallets/adapters/TonConnectAdapter' -import { adapters } from 'services/wallets/config' -import { Adapters } from 'services/wallets/types' +import { useDispatch } from 'react-redux' +import { fetchTonConnectWallets } from 'store/wallet/actions' const StyledAppContainer = styled(Box)({ display: "flex", @@ -35,6 +34,7 @@ const StyledRoutesContainer = styled(AppGrid)({ const App = () => { const { restoreSession } = useWalletActions(); + const dispatch = useDispatch() useWebAppResize(); useEffectOnce(() => { @@ -48,27 +48,9 @@ const App = () => { }); setClienT(_client) })(); + dispatch(fetchTonConnectWallets()) }, []) - useEffect(() => { - const fetchTonConnectDevices = async () => { - const supportedWallets = await getSupportedWallets() - supportedWallets.map((w) => { - adapters.push({ - name: w.name, - type: w.name === 'OpenMask' ? Adapters.OPENMASK : Adapters.TON_KEEPER, - icon: w.imageUrl, - mobileCompatible: w.name !== 'OpenMask', - description: w.name === 'OpenMask' - ? 'TON Wallet Plugin for Google Chrome' - : 'A mobile wallet in your pocket', - tonConnect: true, - }) - }) - } - fetchTonConnectDevices() - },[]) - return ( <> diff --git a/src/components/SelectWallet/DesktopFlow/QR.tsx b/src/components/SelectWallet/DesktopFlow/QR.tsx index 6ab8da8fb..13208999b 100644 --- a/src/components/SelectWallet/DesktopFlow/QR.tsx +++ b/src/components/SelectWallet/DesktopFlow/QR.tsx @@ -1,12 +1,10 @@ import {useTheme } from '@mui/styles'; import { Box } from "@mui/material"; -import QRCode from "react-qr-code"; import { QRCode as QrCodeLogo } from "react-qrcode-logo"; import Fade from "@mui/material/Fade"; import CircularProgress from "@mui/material/CircularProgress"; import Title from "../Title"; import { styled } from '@mui/system'; -import { Adapter, Adapters } from 'services/wallets/types'; const StyledContainer = styled(Box)({ display: "flex", @@ -27,34 +25,26 @@ const StyledQrBox = styled(Box)({ }); interface Props { - onClose: () => void; link?: string; open: boolean; - adapter: Adapters + title?: string + image?: string } -function QR({ onClose, link, open, adapter }: Props) { +function QR({ link, open, title, image }: Props) { const theme = useTheme() if (!open) { return null; } - let title = ''; - let qrCode = ; - if (adapter == Adapters.TON_KEEPER) { - title = 'Connect Ton Keeper'; - qrCode = < QrCodeLogo size={250} logoImage={"https://tonkeeper.com/assets/logo.svg"} value={link} /> - } else { - title = 'Connect Tonhub'; - } return ( - + <Title text={`Connect ${title}`} /> <StyledQrBox> {link ? ( <Fade in={true}> <Box> - {qrCode} + <QrCodeLogo size={250} logoImage={image} logoWidth={50} logoHeight={50} value={link} removeQrCodeBehindLogo /> </Box> </Fade> ) : ( diff --git a/src/components/SelectWallet/DesktopFlow/index.tsx b/src/components/SelectWallet/DesktopFlow/index.tsx index 0fecd421c..5b7c91b40 100644 --- a/src/components/SelectWallet/DesktopFlow/index.tsx +++ b/src/components/SelectWallet/DesktopFlow/index.tsx @@ -2,13 +2,9 @@ import { styled } from '@mui/styles' import { Box } from '@mui/material' import QR from './QR' import AdaptersList from '../AdaptersList' -import { useState } from 'react' -import { Adapters } from 'services/wallets/types' -import { adapters } from 'services/wallets/config' -import { useWalletActions, useWalletStore } from 'store/wallet/hooks' -import { connect } from 'services/wallets/adapters/TonConnectAdapter' -import { updateWallet } from 'store/wallet/actions' -import { useDispatch } from 'react-redux' +import { useWalletSelect } from 'store/wallet/hooks' +import { useSelector } from 'react-redux' +import { RootState } from 'store/store' const StyledContainer = styled(Box)({ display: "flex", @@ -24,63 +20,19 @@ interface Props { } const DesktopFlow = ({ closeModal }: Props) => { - const { resetWallet, createWalletSession } = useWalletActions(); - const [ adapter, setAdapter ] = useState<Adapters>(Adapters.TON_HUB); - const { sessionLink } = useWalletStore() - const [showQr, setShowQr] = useState(false); - const [link ,setLink] = useState<string>(''); - const dispatch = useDispatch() - - const onSelect = async (adapter: Adapters, supportsTonConnect?: boolean) => { - if(adapter === Adapters.OPENMASK) { - const provider = window.ton - //@ts-ignore - if(provider?.isOpenMask) { - const accounts = await provider.send("ton_requestAccounts"); - const wallet = {address: accounts[0] } - dispatch(updateWallet({ wallet, adapterId: Adapters.TON_KEEPER })); - } else { - console.log('OpenMask is not installed!') - } - - } else if(supportsTonConnect) { - try { - const wallet = await connect(null, { - manifestUrl: "https://tonverifier.live/tonconnect-manifest.json", - onSessionLinkReady: (val: string) => { - setLink(val) - setShowQr(true) - }, - }) - dispatch(updateWallet({ wallet, adapterId: Adapters.TON_KEEPER })); - } catch (error) { - console.log(error) - } - } else { - resetWallet(); - setAdapter(adapter); - createWalletSession(adapter); - if (adapter === Adapters.TON_HUB) { - setShowQr(true); - } - } - }; - - const cancel = () => { - setShowQr(false); - resetWallet(); - }; + const {selectWallet, session, adapter} = useWalletSelect() + const wallets = useSelector((state: RootState) => state.wallet.allWallets) return ( <StyledContainer> <AdaptersList - adapters={adapters} + adapters={wallets || []} onClose={closeModal} - open={!showQr} - select={onSelect} + open={!session} + select={selectWallet} /> - - <QR open={showQr} adapter={adapter} link={link || sessionLink} onClose={cancel} /> + + {adapter && session && <QR open={!!session} link={session} title={adapter.name} image={adapter.icon} />} </StyledContainer> ); } diff --git a/src/components/SelectWallet/MobileFlow/index.tsx b/src/components/SelectWallet/MobileFlow/index.tsx index 845f66cd9..8693a330a 100644 --- a/src/components/SelectWallet/MobileFlow/index.tsx +++ b/src/components/SelectWallet/MobileFlow/index.tsx @@ -1,11 +1,8 @@ import { styled } from "@mui/styles"; -import { Box, Link } from "@mui/material"; +import { Box } from "@mui/material"; import { observer } from "mobx-react-lite"; -import { useEffect, useMemo, useRef, useState } from "react"; -import { adapters } from "services/wallets/config"; -import { Adapters } from "services/wallets/types"; import AdaptersList from "../AdaptersList"; -import { useWalletActions, useWalletStore } from "store/wallet/hooks"; +import { useWalletSelect } from 'store/wallet/hooks' const StyledContainer = styled(Box)({ display: "flex", @@ -21,46 +18,20 @@ interface Props { } const MobileFlow = observer(({ closeModal }: Props) => { - - const { sessionLink } = useWalletStore(); - const { createWalletSession } = useWalletActions(); - const [selectedAdapter, setSelectedAdapter] = useState< - Adapters | undefined - >(); - - const filteredAdapters = useMemo( - () => adapters.filter((m) => m.mobileCompatible), - [] - ); - - const onSelect = async (adapter: Adapters) => { - - await createWalletSession(adapter); - setSelectedAdapter(adapters.find((m) => m.type === adapter)?.type); - }; + const {selectWallet, session, adapter: selectedAdapter} = useWalletSelect() const openDeepLink = () => { - if (sessionLink) { - window.location.href = sessionLink; + if (session) { + window.location.href = session; } } - const createSessions = async () => { - // const tonhub = await createWalletSession(Adapters.TON_HUB) - // const tonkeeper = await createWalletSession(Adapters.TON_KEEPER) - } - - useEffect(() => { - - // createSessions() - }, []); - if (sessionLink && selectedAdapter) { + if (session) { - const currentAdapter = filteredAdapters.filter((a) => a.type == selectedAdapter ) return (<StyledContainer style={{ width: "100%", display:"flex" }}> <AdaptersList - adapterLoading={selectedAdapter} - adapters={currentAdapter} + adapterLoading={selectedAdapter ? selectedAdapter.type : undefined} + adapters={selectedAdapter ? [selectedAdapter] : []} onClose={closeModal} open={true} select={openDeepLink} @@ -73,11 +44,11 @@ const MobileFlow = observer(({ closeModal }: Props) => { return ( <StyledContainer style={{ width: "100%" }}> <AdaptersList - adapterLoading={selectedAdapter} - adapters={filteredAdapters} + adapterLoading={selectedAdapter ? selectedAdapter.type : undefined} + adapters={selectedAdapter ? [selectedAdapter] : []} onClose={closeModal} open={true} - select={onSelect} + select={selectWallet} isLoading={false} /> </StyledContainer> diff --git a/src/services/wallets/adapters/TonConnectAdapter.ts b/src/services/wallets/adapters/TonConnectAdapter.ts index 268c0d5f4..6dc116d20 100644 --- a/src/services/wallets/adapters/TonConnectAdapter.ts +++ b/src/services/wallets/adapters/TonConnectAdapter.ts @@ -40,13 +40,13 @@ export interface TransactionDetails { message?: Cell; } -export const connector = new TonConnect({ manifestUrl: "https://tonverifier.live/tonconnect-manifest.json"}); // add storage if needed +export const connector = new TonConnect({ manifestUrl: "https://tonverifier.live/tonconnect-manifest.json"}); export async function disconnectTC(): Promise<void> { await connector.disconnect(); } -export const getSupportedWallets = async () => await connector.getWallets(); +export const getTonConnectWallets = async () => await connector.getWallets(); function isInjected(walletInfo: WalletInfo): walletInfo is WalletInfoInjected { return "jsBridgeKey" in walletInfo && "injected" in walletInfo && walletInfo.injected; @@ -57,14 +57,7 @@ function isRemote(walletInfo: WalletInfo): walletInfo is WalletInfoRemote { } export async function connect(walletInfo: any, config: any): Promise<Wallet> { - if (!walletInfo) { - const wallets = await connector.getWallets(); - walletInfo = wallets.find((w) => w.name === "Tonkeeper"); - if (!walletInfo) { - throw new Error("Tonkeeper wallet not found"); - } -} -const getWalletP = new Promise<Wallet>((resolve, reject) => { + const getWalletP = new Promise<Wallet>((resolve, reject) => { connector.onStatusChange((wallet) => { try { if (wallet) { @@ -100,7 +93,7 @@ return getWalletP; } -async function requestTransaction( +export async function requestTonConnectTransaction( request: TransactionDetails, onSuccess?: (() => void) | undefined ): Promise<void> { diff --git a/src/services/wallets/types.ts b/src/services/wallets/types.ts index e5aa999ae..ac31e46a2 100644 --- a/src/services/wallets/types.ts +++ b/src/services/wallets/types.ts @@ -1,4 +1,5 @@ import { StateInit } from "ton"; +import { WalletInfo } from '@tonconnect/sdk' export interface TransactionRequest { /** Destination */ @@ -52,6 +53,8 @@ export interface Adapter { description: string; disabled?: boolean; tonConnect?: boolean + //Only for TonConnect + walletInfo?: WalletInfo } export enum ActionCategory { diff --git a/src/store/wallet/actions.ts b/src/store/wallet/actions.ts index b2d2e4001..629624b97 100644 --- a/src/store/wallet/actions.ts +++ b/src/store/wallet/actions.ts @@ -1,6 +1,7 @@ import { createAction, createAsyncThunk } from "@reduxjs/toolkit"; -import { Adapters, Wallet } from "services/wallets/types"; +import { Adapter, Adapters, Wallet } from 'services/wallets/types' import { walletService } from "services/wallets/WalletService"; +import { getTonConnectWallets } from 'services/wallets/adapters/TonConnectAdapter' export const onWalletConnect = createAction<{ wallet: Wallet; @@ -17,6 +18,7 @@ export const awaitWalletReadiness = createAsyncThunk< { wallet: Wallet; adapterId: Adapters }, { adapterId: Adapters; session: string | {} } >("wallet/createWalletSession", async ({ adapterId, session }, thunkApi) => { + const wallet = await walletService.awaitReadiness(adapterId, session); if (!wallet) { thunkApi.dispatch(resetWallet); @@ -27,3 +29,24 @@ export const awaitWalletReadiness = createAsyncThunk< adapterId, }; }); + +export const fetchTonConnectWallets = createAsyncThunk<Adapter[]>( + "wallet/fetchTonConnectWallets", async () => { + const supportedWallets = await getTonConnectWallets() + const result = supportedWallets.map((w) => { + return { + name: w.name, + type: w.name === 'OpenMask' ? Adapters.OPENMASK : Adapters.TON_KEEPER, + icon: w.imageUrl, + mobileCompatible: w.name !== 'OpenMask', + description: w.name === 'OpenMask' + ? 'TON Wallet Plugin for Google Chrome' + : 'A mobile wallet in your pocket', + tonConnect: true, + walletInfo: w, + } + }) + + return result + } +) \ No newline at end of file diff --git a/src/store/wallet/hooks.ts b/src/store/wallet/hooks.ts index 1dd0d7e8d..99e46c407 100644 --- a/src/store/wallet/hooks.ts +++ b/src/store/wallet/hooks.ts @@ -1,61 +1,80 @@ -import { useCallback } from "react"; -import { useDispatch, useSelector } from "react-redux"; -import { Adapters } from "services/wallets/types"; -import { walletService } from "services/wallets/WalletService"; -import { RootState } from "store/store"; -import { awaitWalletReadiness, resetWallet, setConnecting, setSession, updateWallet } from "./actions"; +import { useCallback, useState } from 'react' +import { useDispatch, useSelector } from 'react-redux' +import { Adapter, Adapters } from 'services/wallets/types' +import { walletService } from 'services/wallets/WalletService' +import { RootState } from 'store/store' +import { awaitWalletReadiness, resetWallet, setConnecting, updateWallet } from './actions' +import { connect } from 'services/wallets/adapters/TonConnectAdapter' export const useWalletStore = () => { - return useSelector((state: RootState) => state.wallet); -}; + return useSelector((state: RootState) => state.wallet) +} + +export const useWalletSelect = () => { + const dispatch = useDispatch<any>() + const [adapter, setAdapter] = useState<Adapter | null>(null) + const [session, setSession] = useState<null | string>(null) + const { resetWallet } = useWalletActions() + const wallets = useSelector((state:RootState) => state.wallet.allWallets) + const _resetWallet = () => { + setSession(null) + resetWallet() + } + + const selectWallet = async (adapterId: Adapters, supportsTonConnect?: boolean) => { + resetWallet() + const adapter:Adapter | null = wallets?.find((wallet) => wallet.type === adapterId) || null + if(!adapter) return + setAdapter(adapter) + + if (!supportsTonConnect) { + const _session: string | {} = await walletService.createSession(adapterId) + dispatch(awaitWalletReadiness({ adapterId, session: _session })) + const parsedSession = typeof _session === 'string' ? JSON.parse(_session) : _session + parsedSession.link.replace('ton-test://', 'https://test.tonhub.com/').replace('ton://', 'https://tonhub.com/') + setSession(parsedSession.link) + return + } + if (adapterId === Adapters.OPENMASK && (window.ton as any).isOpenMask) { + const accounts = await window.ton?.send('ton_requestAccounts') + dispatch(updateWallet({ wallet: { address: accounts[0] }, adapterId: Adapters.TON_KEEPER })) + return + } + const wallet = await connect(adapter.walletInfo, { + onSessionLinkReady: (val: string) => { + setSession(val) + }, + }) + dispatch(updateWallet({ wallet, adapterId: Adapters.TON_KEEPER })) + } + return { selectWallet, session, resetWallet: _resetWallet, adapter } +} export const useWalletActions = (): { - createWalletSession: (adapterId: Adapters) => void; - restoreSession: () => void; - resetWallet: () => void; + restoreSession: () => void; + resetWallet: () => void; } => { - const dispatch = useDispatch<any>(); - - const createWalletSession = useCallback( - async (adapterId: Adapters) => { - const session: string | {} = await walletService.createSession(adapterId); - dispatch(setSession(session)); - dispatch(awaitWalletReadiness({ adapterId, session })); - return session; - }, - [dispatch] - ); - - const reset = useCallback(async () => { - dispatch(resetWallet()); - }, [dispatch]); - - const restoreSession = useCallback(() => { - const adapterId = localStorage.getItem("wallet:adapter-id"); - const session = localStorage.getItem("wallet:session"); - - if (!adapterId || !session) { - dispatch(setConnecting(false)); - return; - } - if (adapterId == Adapters.TON_KEEPER) { - const wallet = { - address: JSON.parse(session).address, - publicKey: "", - walletVersion: "4", - }; - dispatch(updateWallet({ wallet, adapterId: Adapters.TON_KEEPER })); - return; - } - - dispatch(setSession(session)); - dispatch( - awaitWalletReadiness({ - adapterId: adapterId as Adapters, - session: JSON.parse(session), - }) - ); - }, [dispatch]); - - return { createWalletSession, restoreSession, resetWallet: reset }; -}; + const dispatch = useDispatch<any>() + + const reset = useCallback(async () => { + dispatch(resetWallet()) + }, [dispatch]) + + const restoreSession = useCallback(() => { + const adapterId = localStorage.getItem('wallet:adapter-id') + const session = localStorage.getItem('wallet:session') + + if (!adapterId || !session) { + dispatch(setConnecting(false)) + return + } + dispatch( + awaitWalletReadiness({ + adapterId: adapterId as Adapters, + session: JSON.parse(session), + }), + ) + }, [dispatch]) + + return { restoreSession, resetWallet: reset } +} diff --git a/src/store/wallet/reducer.ts b/src/store/wallet/reducer.ts index 5b5f99554..05734b44e 100644 --- a/src/store/wallet/reducer.ts +++ b/src/store/wallet/reducer.ts @@ -1,7 +1,14 @@ import { createReducer } from "@reduxjs/toolkit"; -import { LOCAL_STORAGE_ADDRESS } from "consts"; -import { Wallet } from "services/wallets/types"; -import { awaitWalletReadiness, resetWallet, setConnecting, setSession, updateWallet } from "./actions"; +import { Adapter, Wallet } from 'services/wallets/types' +import { + awaitWalletReadiness, + fetchTonConnectWallets, + resetWallet, + setConnecting, + setSession, + updateWallet, +} from './actions' +import { adapters } from 'services/wallets/config' interface State { address?: string; @@ -11,6 +18,9 @@ interface State { adapterId?: string; sessionLink?: string; connecting: boolean; + tonConnectWallets?: Adapter[] + allWallets?: Adapter[] + mobileWallets?: Adapter[] } const initialState: State = { @@ -61,8 +71,15 @@ const reducer = createReducer(initialState, (builder) => { localStorage.setItem("wallet:adapter-id", adapterId); localStorage.setItem("wallet:session", JSON.stringify({ ...state.session, address: wallet.address })); state.connecting = false; - }); + }) + + .addCase(fetchTonConnectWallets.fulfilled, (state, action) => { + state.tonConnectWallets = action.payload + const _allWallets = [...adapters, ...action.payload] + state.allWallets = _allWallets + state.mobileWallets = _allWallets.filter((wallet) => wallet.mobileCompatible) + }) // Or, you can reference the .type field: // if using TypeScript, the action type cannot be inferred that way }); From 0f844b379a9329a94334c66f834d3557f2aaf826 Mon Sep 17 00:00:00 2001 From: antongotech <105588893+antongotech@users.noreply.github.com> Date: Sun, 29 Jan 2023 18:17:23 +0200 Subject: [PATCH 04/28] wip --- .../SelectWallet/DesktopFlow/index.tsx | 5 +- .../SelectWallet/MobileFlow/index.tsx | 7 +- .../components/TokenOperations/index.tsx | 91 +++++++++++++------ .../wallets/adapters/TonConnectAdapter.ts | 21 ++--- src/store/wallet/actions.ts | 1 + src/store/wallet/hooks.ts | 12 ++- src/store/wallet/reducer.ts | 5 + 7 files changed, 94 insertions(+), 48 deletions(-) diff --git a/src/components/SelectWallet/DesktopFlow/index.tsx b/src/components/SelectWallet/DesktopFlow/index.tsx index 5b7c91b40..7c565d33d 100644 --- a/src/components/SelectWallet/DesktopFlow/index.tsx +++ b/src/components/SelectWallet/DesktopFlow/index.tsx @@ -2,7 +2,7 @@ import { styled } from '@mui/styles' import { Box } from '@mui/material' import QR from './QR' import AdaptersList from '../AdaptersList' -import { useWalletSelect } from 'store/wallet/hooks' +import { useSelectedAdapter, useWalletSelect } from 'store/wallet/hooks' import { useSelector } from 'react-redux' import { RootState } from 'store/store' @@ -20,7 +20,8 @@ interface Props { } const DesktopFlow = ({ closeModal }: Props) => { - const {selectWallet, session, adapter} = useWalletSelect() + const {selectWallet, session} = useWalletSelect() + const adapter = useSelectedAdapter() const wallets = useSelector((state: RootState) => state.wallet.allWallets) return ( diff --git a/src/components/SelectWallet/MobileFlow/index.tsx b/src/components/SelectWallet/MobileFlow/index.tsx index 8693a330a..241e840da 100644 --- a/src/components/SelectWallet/MobileFlow/index.tsx +++ b/src/components/SelectWallet/MobileFlow/index.tsx @@ -2,7 +2,9 @@ import { styled } from "@mui/styles"; import { Box } from "@mui/material"; import { observer } from "mobx-react-lite"; import AdaptersList from "../AdaptersList"; -import { useWalletSelect } from 'store/wallet/hooks' +import { useSelectedAdapter, useWalletSelect } from 'store/wallet/hooks' +import { useSelector } from 'react-redux' +import { RootState } from 'store/store' const StyledContainer = styled(Box)({ display: "flex", @@ -18,7 +20,8 @@ interface Props { } const MobileFlow = observer(({ closeModal }: Props) => { - const {selectWallet, session, adapter: selectedAdapter} = useWalletSelect() + const {selectWallet, session} = useWalletSelect() + const selectedAdapter = useSelectedAdapter() const openDeepLink = () => { if (session) { diff --git a/src/screens/components/TokenOperations/index.tsx b/src/screens/components/TokenOperations/index.tsx index dcb814414..223569e87 100644 --- a/src/screens/components/TokenOperations/index.tsx +++ b/src/screens/components/TokenOperations/index.tsx @@ -1,7 +1,7 @@ import { Box, styled } from "@mui/material"; import { useEffect, useState } from "react"; import { ActionButton, Popup } from "components"; -import { getToken, PoolInfo } from "services/api/addresses"; +import { PoolInfo } from "services/api/addresses"; import WarningAmberRoundedIcon from "@mui/icons-material/WarningAmberRounded"; import { useStyles } from "./styles"; import DestToken from "./DestToken"; @@ -11,14 +11,14 @@ import { useTokenOperationsActions, useTokenOperationsStore, } from "store/token-operations/hooks"; -import { useWalletStore } from "store/wallet/hooks"; +import { useSelectedAdapter, useWalletStore } from 'store/wallet/hooks' import { useIsExpandedView, useWalletModalToggle, } from "store/application/hooks"; import { StyledTokenOperationActions } from "styles/styles"; import Icon from "./Icon"; -import { ActionCategory, ActionType, Adapters } from "services/wallets/types"; +import { ActionCategory, ActionType, Adapters } from 'services/wallets/types' import { client, GAS_FEE, waitForSeqno } from "services/api"; import { Address } from "ton"; import SuccessModal from "./SuccessModal"; @@ -27,10 +27,10 @@ import TxError from "./TxError"; import useTxAnalytics from "./useTxAnalytics"; import gaAnalytics from "services/analytics/ga/ga"; import { useTranslation } from "react-i18next"; -import TradeInfo from "./TradeInfo"; import TxLoader from "./TxLoader"; import { isMobile } from "react-device-detect"; import { QRCode } from "react-qrcode-logo"; +import { requestTonConnectTransaction } from 'services/wallets/adapters/TonConnectAdapter' interface Props { srcToken: PoolInfo; @@ -99,35 +99,68 @@ const TokenOperations = ({ } }; - const submitTransaction = async () => { - const tx = async () => { - const txRequest = await getTxRequest(); - const waiter = await waitForSeqno( - client.openWalletFromAddress({ - source: Address.parse(address!!), - }) - ); - let deepLinkUrl = await walletService.requestTransaction(adapterId!!, session, txRequest); - if (typeof deepLinkUrl == "string") { - if (isMobile) { - window.location.href = deepLinkUrl; - } else { - setKeeperTransactionLink(deepLinkUrl); + //create hook for submitting transactions} + //check adapter type + //according to adapter type use exact requestTransaction + //hook returns status (pending, error, success) + + const useSubmitTransaction = () => { + const selectedAdapter = useSelectedAdapter() + + const submitTransaction = async () => { + debugger + const tx = async () => { + const txRequest = await getTxRequest(); + + const waiter = await waitForSeqno( + client.openWalletFromAddress({ + source: Address.parse(address!!), + }) + ) + if(!selectedAdapter?.tonConnect) { + let deepLinkUrl = await walletService.requestTransaction(adapterId!!, session, txRequest); + console.log(deepLinkUrl) + } else { + const result = await requestTonConnectTransaction(txRequest) + console.log(result) + } + await waiter(); } - } - await waiter(); - setTimeout(() => { - onSuccess?.() - },7000) - setKeeperTransactionLink(""); - sendAnalyticsEvent() - onResetAmounts(); - getTokensBalance(getBalances); - }; + sendTransaction(tx) + } - sendTransaction(tx); + return submitTransaction } + const submitTransaction = useSubmitTransaction() + + // const submitTransaction = async () => { + // const tx = async () => { + // const txRequest = await getTxRequest(); + // const waiter = await waitForSeqno( + // client.openWalletFromAddress({ + // source: Address.parse(address!!), + // }) + // ); + // let deepLinkUrl = await walletService.requestTransaction(adapterId!!, session, txRequest); + // if (typeof deepLinkUrl == "string") { + // if (isMobile) { + // window.location.href = deepLinkUrl; + // } else { + // setKeeperTransactionLink(deepLinkUrl); + // } + // } + // await waiter(); + // setTimeout(() => { + // onSuccess?.() + // },7000) + // setKeeperTransactionLink(""); + // sendAnalyticsEvent() + // onResetAmounts(); + // getTokensBalance(getBalances); + // }; + // } + useEffect(() => { if (address && refreshAmountsOnActionChange) { getTokensBalance(getBalances); diff --git a/src/services/wallets/adapters/TonConnectAdapter.ts b/src/services/wallets/adapters/TonConnectAdapter.ts index 6dc116d20..10ec8c188 100644 --- a/src/services/wallets/adapters/TonConnectAdapter.ts +++ b/src/services/wallets/adapters/TonConnectAdapter.ts @@ -1,9 +1,9 @@ import TonConnect, { - IStorage, + IStorage, SendTransactionResponse, WalletInfo, WalletInfoInjected, WalletInfoRemote, -} from "@tonconnect/sdk"; +} from '@tonconnect/sdk' import { Address, Cell, StateInit } from 'ton' import BN from 'bn.js' @@ -34,8 +34,8 @@ export interface Wallet { } export interface TransactionDetails { - to: Address; - value: BN; + to: string; + value: string; stateInit?: StateInit; message?: Cell; } @@ -88,21 +88,20 @@ if (!connector.connected) { throw new Error("Unknown wallet type"); } } - return getWalletP; } export async function requestTonConnectTransaction( request: TransactionDetails, - onSuccess?: (() => void) | undefined -): Promise<void> { - await connector.sendTransaction({ + // onSuccess?: (() => void) | undefined +): Promise<SendTransactionResponse> { + return connector.sendTransaction({ validUntil: Date.now() + 5 * 60 * 1000, messages: [ { - address: request.to.toFriendly(), - amount: request.value.toString(), + address: request.to, + amount: request.value, stateInit: request.stateInit ? stateInitToBuffer(request.stateInit).toString("base64") : undefined, @@ -111,5 +110,5 @@ export async function requestTonConnectTransaction( ], }); - onSuccess?.(); + // onSuccess?.(); } \ No newline at end of file diff --git a/src/store/wallet/actions.ts b/src/store/wallet/actions.ts index 629624b97..926b0a90a 100644 --- a/src/store/wallet/actions.ts +++ b/src/store/wallet/actions.ts @@ -9,6 +9,7 @@ export const onWalletConnect = createAction<{ }>("wallet/onWalletConnect"); export const resetWallet = createAction("wallet/resetWallet"); export const setConnecting = createAction<boolean>("wallet/setConnecting"); +export const setAdapter = createAction<Adapter>("wallet/setAdapter") export const setSession = createAction<string | {}>("wallet/setSession"); export const updateWallet = createAction<any>("wallet/updateWallet"); diff --git a/src/store/wallet/hooks.ts b/src/store/wallet/hooks.ts index 99e46c407..7b07df16a 100644 --- a/src/store/wallet/hooks.ts +++ b/src/store/wallet/hooks.ts @@ -3,16 +3,20 @@ import { useDispatch, useSelector } from 'react-redux' import { Adapter, Adapters } from 'services/wallets/types' import { walletService } from 'services/wallets/WalletService' import { RootState } from 'store/store' -import { awaitWalletReadiness, resetWallet, setConnecting, updateWallet } from './actions' +import { awaitWalletReadiness, resetWallet, setAdapter, setConnecting, updateWallet } from './actions' import { connect } from 'services/wallets/adapters/TonConnectAdapter' export const useWalletStore = () => { return useSelector((state: RootState) => state.wallet) } +export const useSelectedAdapter = () => { + return useSelector((state: RootState) => state.wallet.adapter) +} + + export const useWalletSelect = () => { const dispatch = useDispatch<any>() - const [adapter, setAdapter] = useState<Adapter | null>(null) const [session, setSession] = useState<null | string>(null) const { resetWallet } = useWalletActions() const wallets = useSelector((state:RootState) => state.wallet.allWallets) @@ -25,8 +29,8 @@ export const useWalletSelect = () => { resetWallet() const adapter:Adapter | null = wallets?.find((wallet) => wallet.type === adapterId) || null if(!adapter) return - setAdapter(adapter) + dispatch(setAdapter(adapter)) if (!supportsTonConnect) { const _session: string | {} = await walletService.createSession(adapterId) dispatch(awaitWalletReadiness({ adapterId, session: _session })) @@ -47,7 +51,7 @@ export const useWalletSelect = () => { }) dispatch(updateWallet({ wallet, adapterId: Adapters.TON_KEEPER })) } - return { selectWallet, session, resetWallet: _resetWallet, adapter } + return { selectWallet, session, resetWallet: _resetWallet } } export const useWalletActions = (): { diff --git a/src/store/wallet/reducer.ts b/src/store/wallet/reducer.ts index 05734b44e..f11cdf59a 100644 --- a/src/store/wallet/reducer.ts +++ b/src/store/wallet/reducer.ts @@ -6,6 +6,7 @@ import { resetWallet, setConnecting, setSession, + setAdapter, updateWallet, } from './actions' import { adapters } from 'services/wallets/config' @@ -21,6 +22,7 @@ interface State { tonConnectWallets?: Adapter[] allWallets?: Adapter[] mobileWallets?: Adapter[] + adapter?: Adapter } const initialState: State = { @@ -80,6 +82,9 @@ const reducer = createReducer(initialState, (builder) => { state.mobileWallets = _allWallets.filter((wallet) => wallet.mobileCompatible) }) + .addCase(setAdapter, (state, action) => { + state.adapter = action.payload + }) // Or, you can reference the .type field: // if using TypeScript, the action type cannot be inferred that way }); From 6450c1740a040270482ada9504b4c99b4bc1a7fb Mon Sep 17 00:00:00 2001 From: antongotech <105588893+antongotech@users.noreply.github.com> Date: Sun, 29 Jan 2023 18:28:14 +0200 Subject: [PATCH 05/28] wip --- src/components/SelectWallet/MobileFlow/index.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/SelectWallet/MobileFlow/index.tsx b/src/components/SelectWallet/MobileFlow/index.tsx index 241e840da..a0b4c678f 100644 --- a/src/components/SelectWallet/MobileFlow/index.tsx +++ b/src/components/SelectWallet/MobileFlow/index.tsx @@ -22,6 +22,7 @@ interface Props { const MobileFlow = observer(({ closeModal }: Props) => { const {selectWallet, session} = useWalletSelect() const selectedAdapter = useSelectedAdapter() + const wallets = useSelector((state: RootState) => state.wallet.allWallets) const openDeepLink = () => { if (session) { @@ -48,7 +49,7 @@ const MobileFlow = observer(({ closeModal }: Props) => { <StyledContainer style={{ width: "100%" }}> <AdaptersList adapterLoading={selectedAdapter ? selectedAdapter.type : undefined} - adapters={selectedAdapter ? [selectedAdapter] : []} + adapters={wallets || []} onClose={closeModal} open={true} select={selectWallet} From 42727fc9647c0ac1f0c720416db0de31844a122e Mon Sep 17 00:00:00 2001 From: antongotech <105588893+antongotech@users.noreply.github.com> Date: Sun, 29 Jan 2023 18:31:05 +0200 Subject: [PATCH 06/28] wip --- src/components/SelectWallet/MobileFlow/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/SelectWallet/MobileFlow/index.tsx b/src/components/SelectWallet/MobileFlow/index.tsx index a0b4c678f..3466e134c 100644 --- a/src/components/SelectWallet/MobileFlow/index.tsx +++ b/src/components/SelectWallet/MobileFlow/index.tsx @@ -52,7 +52,7 @@ const MobileFlow = observer(({ closeModal }: Props) => { adapters={wallets || []} onClose={closeModal} open={true} - select={selectWallet} + select={openDeepLink} isLoading={false} /> </StyledContainer> From 2536c63162ff3715b70eebac13f66c54e4088e31 Mon Sep 17 00:00:00 2001 From: antongotech <105588893+antongotech@users.noreply.github.com> Date: Sun, 29 Jan 2023 18:32:35 +0200 Subject: [PATCH 07/28] wip --- src/components/SelectWallet/MobileFlow/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/SelectWallet/MobileFlow/index.tsx b/src/components/SelectWallet/MobileFlow/index.tsx index 3466e134c..14824f6bf 100644 --- a/src/components/SelectWallet/MobileFlow/index.tsx +++ b/src/components/SelectWallet/MobileFlow/index.tsx @@ -49,10 +49,10 @@ const MobileFlow = observer(({ closeModal }: Props) => { <StyledContainer style={{ width: "100%" }}> <AdaptersList adapterLoading={selectedAdapter ? selectedAdapter.type : undefined} - adapters={wallets || []} + adapters={wallets} onClose={closeModal} open={true} - select={openDeepLink} + select={selectWallet} isLoading={false} /> </StyledContainer> From 84645ebe411cd1b015e50f8e3e7bf8afb7bef746 Mon Sep 17 00:00:00 2001 From: antongotech <105588893+antongotech@users.noreply.github.com> Date: Sun, 29 Jan 2023 18:56:19 +0200 Subject: [PATCH 08/28] wip --- src/components/SelectWallet/MobileFlow/index.tsx | 2 +- src/screens/components/TokenOperations/index.tsx | 13 ++++++++++--- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/components/SelectWallet/MobileFlow/index.tsx b/src/components/SelectWallet/MobileFlow/index.tsx index 14824f6bf..a0b4c678f 100644 --- a/src/components/SelectWallet/MobileFlow/index.tsx +++ b/src/components/SelectWallet/MobileFlow/index.tsx @@ -49,7 +49,7 @@ const MobileFlow = observer(({ closeModal }: Props) => { <StyledContainer style={{ width: "100%" }}> <AdaptersList adapterLoading={selectedAdapter ? selectedAdapter.type : undefined} - adapters={wallets} + adapters={wallets || []} onClose={closeModal} open={true} select={selectWallet} diff --git a/src/screens/components/TokenOperations/index.tsx b/src/screens/components/TokenOperations/index.tsx index 223569e87..d740e7c37 100644 --- a/src/screens/components/TokenOperations/index.tsx +++ b/src/screens/components/TokenOperations/index.tsx @@ -108,7 +108,6 @@ const TokenOperations = ({ const selectedAdapter = useSelectedAdapter() const submitTransaction = async () => { - debugger const tx = async () => { const txRequest = await getTxRequest(); @@ -119,13 +118,21 @@ const TokenOperations = ({ ) if(!selectedAdapter?.tonConnect) { let deepLinkUrl = await walletService.requestTransaction(adapterId!!, session, txRequest); - console.log(deepLinkUrl) + if(typeof deepLinkUrl === 'string') { + if(isMobile) { + window.location.href = deepLinkUrl + } else { + setKeeperTransactionLink(deepLinkUrl) + } + } } else { const result = await requestTonConnectTransaction(txRequest) - console.log(result) } await waiter(); } + setKeeperTransactionLink('') + sendAnalyticsEvent() + getTokensBalance(getBalances) sendTransaction(tx) } From e202f4f5c32dfdf2c4aac3b219d45dbb08b2b471 Mon Sep 17 00:00:00 2001 From: antongotech <105588893+antongotech@users.noreply.github.com> Date: Tue, 31 Jan 2023 16:42:26 +0200 Subject: [PATCH 09/28] wip --- src/App.tsx | 21 ++--- .../components/TokenOperations/index.tsx | 76 ++++++------------ src/services/api/deploy-pool.ts | 2 +- .../wallets/adapters/TonConnectAdapter.ts | 14 ++-- src/store/token-operations/actions.ts | 14 ++-- src/store/token-operations/hooks.ts | 16 +++- src/store/token-operations/reducer.ts | 20 ++++- src/store/wallet/actions.ts | 3 +- src/store/wallet/hooks.ts | 79 +++++++++++++------ src/store/wallet/reducer.ts | 11 ++- 10 files changed, 150 insertions(+), 106 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 4f8a92886..a75eccdc1 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -4,16 +4,16 @@ import { Navbar } from "components"; import { LAYOUT_MAX_WIDTH } from "consts"; import { styled } from "@mui/system"; import SelectWallet from "components/SelectWallet"; -import { useWalletActions } from "store/wallet/hooks"; +import { useWalletActions, useWalletStore } from 'store/wallet/hooks' import { AppGrid } from "styles/styles"; -import useEffectOnce from "hooks/useEffectOnce"; import { useWebAppResize } from "store/application/hooks"; import './services/i18next/i18n'; import { useEffect } from 'react' import { TonClient } from 'ton' import { setClienT } from 'services/api' -import { useDispatch } from 'react-redux' +import { useDispatch, useSelector } from 'react-redux' import { fetchTonConnectWallets } from 'store/wallet/actions' +import { RootState } from 'store/store' const StyledAppContainer = styled(Box)({ display: "flex", @@ -33,14 +33,12 @@ const StyledRoutesContainer = styled(AppGrid)({ }); const App = () => { - const { restoreSession } = useWalletActions(); + const { restoreSession, restoreAdapter } = useWalletActions(); + const { adapterId } = useWalletStore(); + const wallets = useSelector((state: RootState) => state.wallet.allWallets) const dispatch = useDispatch<any>() useWebAppResize(); - useEffectOnce(() => { - restoreSession(); - }); - useEffect(() => { (async () => { const _client = new TonClient({ @@ -49,8 +47,13 @@ const App = () => { setClienT(_client) })(); dispatch(fetchTonConnectWallets()) + restoreSession(); }, []) + useEffect(() => { + !!wallets?.length && restoreAdapter(adapterId!) + }, [wallets]) + return ( <> <StyledAppContainer> @@ -64,4 +67,4 @@ const App = () => { ); }; -export default App; +export default App; \ No newline at end of file diff --git a/src/screens/components/TokenOperations/index.tsx b/src/screens/components/TokenOperations/index.tsx index d740e7c37..9498f4c3d 100644 --- a/src/screens/components/TokenOperations/index.tsx +++ b/src/screens/components/TokenOperations/index.tsx @@ -85,6 +85,7 @@ const TokenOperations = ({ getTokensBalance, resetTokensBalance, sendTransaction, + sendTonConnectTransaction, } = useTokenOperationsActions(); const { t } = useTranslation(); @@ -99,41 +100,39 @@ const TokenOperations = ({ } }; - //create hook for submitting transactions} - //check adapter type - //according to adapter type use exact requestTransaction - //hook returns status (pending, error, success) - const useSubmitTransaction = () => { const selectedAdapter = useSelectedAdapter() const submitTransaction = async () => { - const tx = async () => { - const txRequest = await getTxRequest(); + const txRequest = await getTxRequest(); + + const waiter = await waitForSeqno( + client.openWalletFromAddress({ + source: Address.parse(address!!), + }) + ) - const waiter = await waitForSeqno( - client.openWalletFromAddress({ - source: Address.parse(address!!), - }) - ) - if(!selectedAdapter?.tonConnect) { - let deepLinkUrl = await walletService.requestTransaction(adapterId!!, session, txRequest); - if(typeof deepLinkUrl === 'string') { - if(isMobile) { - window.location.href = deepLinkUrl - } else { - setKeeperTransactionLink(deepLinkUrl) - } + if(!selectedAdapter?.tonConnect) { + const tx = async () => { + let deepLinkUrl = await walletService.requestTransaction(adapterId!!, session, txRequest); + if (typeof deepLinkUrl === 'string') { + if (isMobile) { + window.location.href = deepLinkUrl + } else { + setKeeperTransactionLink(deepLinkUrl) } - } else { - const result = await requestTonConnectTransaction(txRequest) } - await waiter(); } - setKeeperTransactionLink('') + sendTransaction(tx) + await waiter(); + } else { + sendTonConnectTransaction(async () => await requestTonConnectTransaction(txRequest)) + await waiter(); + } + + setKeeperTransactionLink('') sendAnalyticsEvent() getTokensBalance(getBalances) - sendTransaction(tx) } return submitTransaction @@ -141,33 +140,6 @@ const TokenOperations = ({ const submitTransaction = useSubmitTransaction() - // const submitTransaction = async () => { - // const tx = async () => { - // const txRequest = await getTxRequest(); - // const waiter = await waitForSeqno( - // client.openWalletFromAddress({ - // source: Address.parse(address!!), - // }) - // ); - // let deepLinkUrl = await walletService.requestTransaction(adapterId!!, session, txRequest); - // if (typeof deepLinkUrl == "string") { - // if (isMobile) { - // window.location.href = deepLinkUrl; - // } else { - // setKeeperTransactionLink(deepLinkUrl); - // } - // } - // await waiter(); - // setTimeout(() => { - // onSuccess?.() - // },7000) - // setKeeperTransactionLink(""); - // sendAnalyticsEvent() - // onResetAmounts(); - // getTokensBalance(getBalances); - // }; - // } - useEffect(() => { if (address && refreshAmountsOnActionChange) { getTokensBalance(getBalances); diff --git a/src/services/api/deploy-pool.ts b/src/services/api/deploy-pool.ts index 58000cc90..a4a412f61 100644 --- a/src/services/api/deploy-pool.ts +++ b/src/services/api/deploy-pool.ts @@ -1,6 +1,6 @@ import BN from "bn.js"; import { Address, Cell, contractAddress, toNano, TonClient, beginDict, beginCell, StateInit, Slice } from "ton"; -import { getTokenData, _getJettonBalance } from "."; +import { getTokenData } from "."; import { Sha256 } from "@aws-crypto/sha256-js"; import { walletService } from "services/wallets/WalletService"; import { TransactionRequest } from "services/wallets/types"; diff --git a/src/services/wallets/adapters/TonConnectAdapter.ts b/src/services/wallets/adapters/TonConnectAdapter.ts index 10ec8c188..7e8dd3ed2 100644 --- a/src/services/wallets/adapters/TonConnectAdapter.ts +++ b/src/services/wallets/adapters/TonConnectAdapter.ts @@ -5,7 +5,6 @@ import TonConnect, { WalletInfoRemote, } from '@tonconnect/sdk' import { Address, Cell, StateInit } from 'ton' -import BN from 'bn.js' export const getWallets = () => { return connector.getWallets() @@ -43,7 +42,14 @@ export interface TransactionDetails { export const connector = new TonConnect({ manifestUrl: "https://tonverifier.live/tonconnect-manifest.json"}); export async function disconnectTC(): Promise<void> { - await connector.disconnect(); + localStorage.removeItem('ton-connect-storage_bridge-connection') + localStorage.removeItem('ton-connect-storage_http-bridge-gateway') + + try { + await connector.disconnect(); + } catch (e) { + console.log(e) + } } export const getTonConnectWallets = async () => await connector.getWallets(); @@ -94,8 +100,8 @@ return getWalletP; export async function requestTonConnectTransaction( request: TransactionDetails, - // onSuccess?: (() => void) | undefined ): Promise<SendTransactionResponse> { + await connector.restoreConnection() return connector.sendTransaction({ validUntil: Date.now() + 5 * 60 * 1000, messages: [ @@ -109,6 +115,4 @@ export async function requestTonConnectTransaction( }, ], }); - - // onSuccess?.(); } \ No newline at end of file diff --git a/src/store/token-operations/actions.ts b/src/store/token-operations/actions.ts index f1812a521..532cbb940 100644 --- a/src/store/token-operations/actions.ts +++ b/src/store/token-operations/actions.ts @@ -1,10 +1,6 @@ -import { createAsyncThunk } from "@reduxjs/toolkit"; -import gaAnalytics from "services/analytics/ga/ga"; -import { client, waitForSeqno } from "services/api"; -import { TransactionRequest } from "services/wallets/types"; -import { walletService } from "services/wallets/WalletService"; -import { Address, fromNano } from "ton"; +import { createAsyncThunk } from '@reduxjs/toolkit' import { fromDecimals } from "utils"; +import { SendTransactionResponse } from '@tonconnect/sdk' export const getAmounts = createAsyncThunk< // Return type of the payload creator @@ -28,3 +24,9 @@ export const onSendTransaction = createAsyncThunk< >("token-operations/sendTransaction", async (txMethod) => { await txMethod(); }); + +export const onSendTonConnectTransaction = createAsyncThunk< + any, () => Promise<SendTransactionResponse> + >("token-operations/sendTonConnectTransaction", async (res) => { + await res() +}) \ No newline at end of file diff --git a/src/store/token-operations/hooks.ts b/src/store/token-operations/hooks.ts index d2ad4bb4d..4a04f5c4f 100644 --- a/src/store/token-operations/hooks.ts +++ b/src/store/token-operations/hooks.ts @@ -1,4 +1,4 @@ -import { useCallback, useRef } from "react"; +import { useCallback } from "react"; import { useDispatch, useSelector } from "react-redux"; import { ROUTES } from "router/routes"; import { RootState } from "store/store"; @@ -18,9 +18,10 @@ import { toggleAction, } from "./reducer"; -import { getAmounts, onSendTransaction } from "./actions"; +import { getAmounts, onSendTransaction, onSendTonConnectTransaction } from "./actions"; import useNavigateWithParams from "hooks/useNavigateWithParams"; import { PoolInfo } from "services/api/addresses"; +import { SendTransactionResponse } from '@tonconnect/sdk' export const useTokenOperationsStore = () => { return useSelector((state: RootState) => state.tokenOperations); @@ -41,6 +42,7 @@ export const useTokenOperationsActions = (): { clearStore: () => void; selectToken: (token?: PoolInfo) => void; sendTransaction: (txMethod: () => Promise<void>) => void; + sendTonConnectTransaction: (res: () => Promise<SendTransactionResponse>) => void; hideTxError: () => void; closeSuccessModal: () => void; onInputChange: (inInput :InInput) => void; @@ -161,6 +163,15 @@ export const useTokenOperationsActions = (): { [dispatch] ); + const sendTonConnectTransaction = useCallback( + ( + res: () => Promise<SendTransactionResponse> + ) => { + dispatch(onSendTonConnectTransaction(res)); + }, + [dispatch] + ); + const onInputChange = useCallback( (inInput: InInput) => { @@ -192,6 +203,7 @@ export const useTokenOperationsActions = (): { clearStore, selectToken, sendTransaction, + sendTonConnectTransaction, hideTxError, closeSuccessModal, onInputChange diff --git a/src/store/token-operations/reducer.ts b/src/store/token-operations/reducer.ts index 49a561d97..4f3a68ffb 100644 --- a/src/store/token-operations/reducer.ts +++ b/src/store/token-operations/reducer.ts @@ -1,7 +1,7 @@ import { createSlice, PayloadAction } from "@reduxjs/toolkit"; import { PoolInfo } from "services/api/addresses"; -import { getAmounts, onSendTransaction } from "./actions"; +import { getAmounts, onSendTonConnectTransaction, onSendTransaction } from './actions' interface TxConfirmation { destTokenAmount: string; @@ -134,7 +134,23 @@ const WalletOperationSlice = createSlice({ .addCase(onSendTransaction.fulfilled, (state, action) => { state.txPending = false; state.txSuccess = true; - }); + }) + .addCase(onSendTonConnectTransaction.pending, (state, action) => { + state.txPending = true; + state.txConfirmation = { + destTokenAmount: state.destTokenAmount, + srcTokenAmount: state.srcTokenAmount, + tokenName: state.selectedToken?.displayName, + }; + }) + .addCase(onSendTonConnectTransaction.rejected, (state, action) => { + state.txError = action.error.message; + state.txPending = false; + }) + .addCase(onSendTonConnectTransaction.fulfilled, (state, action) => { + state.txPending = false; + state.txSuccess = true; + }) }, }); diff --git a/src/store/wallet/actions.ts b/src/store/wallet/actions.ts index 926b0a90a..b2f6bfa1a 100644 --- a/src/store/wallet/actions.ts +++ b/src/store/wallet/actions.ts @@ -11,11 +11,10 @@ export const resetWallet = createAction("wallet/resetWallet"); export const setConnecting = createAction<boolean>("wallet/setConnecting"); export const setAdapter = createAction<Adapter>("wallet/setAdapter") -export const setSession = createAction<string | {}>("wallet/setSession"); +export const setTonHubSession = createAction<string | {}>("wallet/setTonHubSession"); export const updateWallet = createAction<any>("wallet/updateWallet"); export const awaitWalletReadiness = createAsyncThunk< - // Return type of the payload creator { wallet: Wallet; adapterId: Adapters }, { adapterId: Adapters; session: string | {} } >("wallet/createWalletSession", async ({ adapterId, session }, thunkApi) => { diff --git a/src/store/wallet/hooks.ts b/src/store/wallet/hooks.ts index 7b07df16a..c13da5a4b 100644 --- a/src/store/wallet/hooks.ts +++ b/src/store/wallet/hooks.ts @@ -3,8 +3,9 @@ import { useDispatch, useSelector } from 'react-redux' import { Adapter, Adapters } from 'services/wallets/types' import { walletService } from 'services/wallets/WalletService' import { RootState } from 'store/store' -import { awaitWalletReadiness, resetWallet, setAdapter, setConnecting, updateWallet } from './actions' -import { connect } from 'services/wallets/adapters/TonConnectAdapter' +import { awaitWalletReadiness, resetWallet, setAdapter, setConnecting, setTonHubSession, updateWallet } from './actions' +import { connect, disconnectTC, Wallet } from 'services/wallets/adapters/TonConnectAdapter' +import { Address } from 'ton' export const useWalletStore = () => { return useSelector((state: RootState) => state.wallet) @@ -14,71 +15,103 @@ export const useSelectedAdapter = () => { return useSelector((state: RootState) => state.wallet.adapter) } - export const useWalletSelect = () => { const dispatch = useDispatch<any>() - const [session, setSession] = useState<null | string>(null) + const [localSession, setLocalSession] = useState<null | string>(null) const { resetWallet } = useWalletActions() - const wallets = useSelector((state:RootState) => state.wallet.allWallets) + const wallets = useSelector((state: RootState) => state.wallet.allWallets) const _resetWallet = () => { - setSession(null) + setLocalSession(null) resetWallet() } const selectWallet = async (adapterId: Adapters, supportsTonConnect?: boolean) => { resetWallet() - const adapter:Adapter | null = wallets?.find((wallet) => wallet.type === adapterId) || null - if(!adapter) return + disconnectTC() + + const adapter: Adapter | null = wallets?.find((wallet) => wallet.type === adapterId) || null + if (!adapter) { + return + } dispatch(setAdapter(adapter)) if (!supportsTonConnect) { const _session: string | {} = await walletService.createSession(adapterId) + dispatch(setTonHubSession(_session)) dispatch(awaitWalletReadiness({ adapterId, session: _session })) const parsedSession = typeof _session === 'string' ? JSON.parse(_session) : _session - parsedSession.link.replace('ton-test://', 'https://test.tonhub.com/').replace('ton://', 'https://tonhub.com/') - setSession(parsedSession.link) + const deepLink = parsedSession.link.replace('ton-test://', 'https://test.tonhub.com/').replace('ton://', 'https://tonhub.com/') + setLocalSession(deepLink) return } if (adapterId === Adapters.OPENMASK && (window.ton as any).isOpenMask) { const accounts = await window.ton?.send('ton_requestAccounts') - dispatch(updateWallet({ wallet: { address: accounts[0] }, adapterId: Adapters.TON_KEEPER })) + dispatch(updateWallet({ wallet: { address: accounts[0] }, adapterId: Adapters.OPENMASK })) + localStorage.setItem('ton-connect-storage_bridge-connection', '{"type":"injected","jsBridgeKey":"openmask"}') return } const wallet = await connect(adapter.walletInfo, { onSessionLinkReady: (val: string) => { - setSession(val) + setLocalSession(val) }, }) dispatch(updateWallet({ wallet, adapterId: Adapters.TON_KEEPER })) } - return { selectWallet, session, resetWallet: _resetWallet } + return { selectWallet, session: localSession, resetWallet: _resetWallet } } export const useWalletActions = (): { restoreSession: () => void; resetWallet: () => void; + restoreAdapter: (adapterId: string) => void; } => { const dispatch = useDispatch<any>() + const wallets = useSelector((state: RootState) => state.wallet.allWallets) const reset = useCallback(async () => { dispatch(resetWallet()) }, [dispatch]) - const restoreSession = useCallback(() => { + const restoreAdapter = (adapterId: string) => { + const adapter: Adapter | null = wallets?.find((wallet) => wallet.type === adapterId) || null + if (!adapter) { + return + } + dispatch(setAdapter(adapter)) + } + + const restoreSession = useCallback(async () => { const adapterId = localStorage.getItem('wallet:adapter-id') const session = localStorage.getItem('wallet:session') - if (!adapterId || !session) { - dispatch(setConnecting(false)) + const tcBridgeConnection = localStorage.getItem('ton-connect-storage_bridge-connection') + const tcBridgeGateway = localStorage.getItem('ton-connect-storage_http-bridge-gateway') + + if (adapterId && session) { + dispatch(setTonHubSession(session)) + dispatch( + awaitWalletReadiness({ + adapterId: adapterId as Adapters, + session: JSON.parse(session), + }), + ) + return + } + + if (tcBridgeConnection && !tcBridgeGateway) { + const accounts = await window.ton?.send('ton_requestAccounts') + dispatch(updateWallet({ wallet: { address: accounts[0] }, adapterId: Adapters.OPENMASK })) + return + } + if (tcBridgeConnection && tcBridgeGateway) { + const session = JSON.parse(tcBridgeConnection) + const wallet: Wallet = { address: Address.parse(session.connectEvent.payload.items[0].address).toFriendly() } + dispatch(updateWallet({ wallet, adapterId: Adapters.TON_KEEPER })) return } - dispatch( - awaitWalletReadiness({ - adapterId: adapterId as Adapters, - session: JSON.parse(session), - }), - ) + dispatch(setConnecting(false)) + return }, [dispatch]) - return { restoreSession, resetWallet: reset } + return { restoreSession, resetWallet: reset, restoreAdapter } } diff --git a/src/store/wallet/reducer.ts b/src/store/wallet/reducer.ts index f11cdf59a..6af5c8b73 100644 --- a/src/store/wallet/reducer.ts +++ b/src/store/wallet/reducer.ts @@ -5,7 +5,7 @@ import { fetchTonConnectWallets, resetWallet, setConnecting, - setSession, + setTonHubSession, setAdapter, updateWallet, } from './actions' @@ -55,8 +55,9 @@ const reducer = createReducer(initialState, (builder) => { state.wallet = payload.wallet; state.adapterId = payload.adapterId; state.address = payload.wallet.address; + state.connecting = false }) - .addCase(setSession, (state, action) => { + .addCase(setTonHubSession, (state, action) => { const { payload } = action; const session = typeof payload === "string" ? JSON.parse(payload) : payload; state.session = session; @@ -70,8 +71,10 @@ const reducer = createReducer(initialState, (builder) => { state.wallet = wallet; state.adapterId = adapterId; state.address = wallet.address; - localStorage.setItem("wallet:adapter-id", adapterId); - localStorage.setItem("wallet:session", JSON.stringify({ ...state.session, address: wallet.address })); + if(!!state.session && adapterId) { + localStorage.setItem("wallet:adapter-id", adapterId); + localStorage.setItem("wallet:session", JSON.stringify({ ...state.session, address: wallet.address })); + } state.connecting = false; }) From de671b5582a6d100852eff2bcbe2467c162d8553 Mon Sep 17 00:00:00 2001 From: antongotech <105588893+antongotech@users.noreply.github.com> Date: Tue, 31 Jan 2023 16:44:31 +0200 Subject: [PATCH 10/28] wip --- src/store/token-operations/reducer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/store/token-operations/reducer.ts b/src/store/token-operations/reducer.ts index 4f3a68ffb..057b17734 100644 --- a/src/store/token-operations/reducer.ts +++ b/src/store/token-operations/reducer.ts @@ -152,7 +152,7 @@ const WalletOperationSlice = createSlice({ state.txSuccess = true; }) }, -}); +}) export const { resetState, From b33a9262bb350735c88aa507208efcfbe87e6349 Mon Sep 17 00:00:00 2001 From: antongotech <105588893+antongotech@users.noreply.github.com> Date: Tue, 31 Jan 2023 18:48:18 +0200 Subject: [PATCH 11/28] bugfixes --- src/components/SelectWallet/AdaptersList.tsx | 33 +++++------ src/hooks/useSubmitTransaction.ts | 51 +++++++++++++++++ .../components/TokenOperations/TxError.tsx | 8 ++- .../components/TokenOperations/index.tsx | 56 +++---------------- 4 files changed, 79 insertions(+), 69 deletions(-) create mode 100644 src/hooks/useSubmitTransaction.ts diff --git a/src/components/SelectWallet/AdaptersList.tsx b/src/components/SelectWallet/AdaptersList.tsx index f88f32b3b..369451923 100644 --- a/src/components/SelectWallet/AdaptersList.tsx +++ b/src/components/SelectWallet/AdaptersList.tsx @@ -1,19 +1,12 @@ -import { styled } from "@mui/styles"; -import { - ListItem, - List, - ListItemButton, - Box, - Typography, - Fade, -} from "@mui/material"; -import Title from "./Title"; -import { Theme } from "@mui/material/styles"; -import { Adapter, Adapters } from "services/wallets/types"; -import CircularProgress from "@mui/material/CircularProgress"; -import gaAnalytics from "services/analytics/ga/ga"; -import { useTranslation } from "react-i18next"; -import {isMobile} from 'react-device-detect' +import { styled } from '@mui/styles' +import { Box, Fade, List, ListItem, ListItemButton, Typography } from '@mui/material' +import Title from './Title' +import { Theme } from '@mui/material/styles' +import { Adapter, Adapters } from 'services/wallets/types' +import CircularProgress from '@mui/material/CircularProgress' +import gaAnalytics from 'services/analytics/ga/ga' +import { useTranslation } from 'react-i18next' +import { isMobile } from 'react-device-detect' const StyledListItem = styled(ListItem)( ({ disabled }: { disabled?: boolean }) => ({ @@ -124,11 +117,15 @@ function AdaptersList({ <StyledListItem disablePadding key={type} - disabled={disabled} style={{ pointerEvents: isLoading ? "none" : "all" }} > <StyledListItemButton - onClick={disabled ? () => { } : () => onSelect(type, tonConnect)} + onClick={() => { + //@ts-ignore + type === Adapters.OPENMASK && window.ton && !window.ton.isOpenMask + ? window.open('https://www.openmask.app/', "_blank") + : onSelect(type, tonConnect) + }} > <StyledIcon> <img style={{ "borderRadius": "9px" }} src={icon} /> diff --git a/src/hooks/useSubmitTransaction.ts b/src/hooks/useSubmitTransaction.ts new file mode 100644 index 000000000..a0af9fcd0 --- /dev/null +++ b/src/hooks/useSubmitTransaction.ts @@ -0,0 +1,51 @@ +import { useSelectedAdapter, useWalletStore } from 'store/wallet/hooks' +import { client, waitForSeqno } from 'services/api' +import { Address } from 'ton' +import { walletService } from 'services/wallets/WalletService' +import { isMobile } from 'react-device-detect' +import { requestTonConnectTransaction } from 'services/wallets/adapters/TonConnectAdapter' +import { useTokenOperationsActions } from 'store/token-operations/hooks' +import { Dispatch, SetStateAction } from 'react' + +export const useSubmitTransaction = () => { + const selectedAdapter = useSelectedAdapter() + const { + getTokensBalance, + sendTransaction, + sendTonConnectTransaction, + } = useTokenOperationsActions(); + const { address, adapterId, session } = useWalletStore(); + + const submitTransaction = async (getTxRequest: () => any, sendAnalyticsEvent: () => (undefined | void), getBalances: () => Promise<any>, setKeeperTransactionLink: Dispatch<SetStateAction<string>>) => { + const txRequest = await getTxRequest(); + + const waiter = await waitForSeqno( + client.openWalletFromAddress({ + source: Address.parse(address!!), + }) + ) + if(!selectedAdapter?.tonConnect) { + const tx = async () => { + let deepLinkUrl = await walletService.requestTransaction(adapterId!!, session, txRequest); + if (typeof deepLinkUrl === 'string') { + if (isMobile) { + window.location.href = deepLinkUrl + } else { + setKeeperTransactionLink(deepLinkUrl) + } + } + } + sendTransaction(tx) + await waiter(); + } else { + sendTonConnectTransaction(async () => await requestTonConnectTransaction(txRequest)) + await waiter(); + } + + setKeeperTransactionLink('') + sendAnalyticsEvent() + getTokensBalance(getBalances) + } + + return submitTransaction +} \ No newline at end of file diff --git a/src/screens/components/TokenOperations/TxError.tsx b/src/screens/components/TokenOperations/TxError.tsx index 07c37fa6b..ca59f0e50 100644 --- a/src/screens/components/TokenOperations/TxError.tsx +++ b/src/screens/components/TokenOperations/TxError.tsx @@ -9,6 +9,10 @@ import ErrorIcon from "assets/images/shared/error.png"; import { useEffect, useState } from "react"; import { delay } from "utils"; +const errorCleaner = (error: string) => { + return error.replace('[TON_CONNECT_SDK_ERROR]', '') +} + function TxError() { const { txError } = useTokenOperationsStore(); const { hideTxError } = useTokenOperationsActions(); @@ -30,9 +34,9 @@ function TxError() { return ( <Popup open={open} onClose={onClose} maxWidth={400}> - <StyledContent> + <StyledContent sx={{minWidth: 240}}> <img src={ErrorIcon} className="icon" /> - <Typography>{txError}</Typography> + <Typography>{txError && errorCleaner(txError)}</Typography> </StyledContent> </Popup> ); diff --git a/src/screens/components/TokenOperations/index.tsx b/src/screens/components/TokenOperations/index.tsx index 9498f4c3d..f6f5c2d3d 100644 --- a/src/screens/components/TokenOperations/index.tsx +++ b/src/screens/components/TokenOperations/index.tsx @@ -6,12 +6,11 @@ import WarningAmberRoundedIcon from "@mui/icons-material/WarningAmberRounded"; import { useStyles } from "./styles"; import DestToken from "./DestToken"; import SrcToken from "./SrcToken"; -import { walletService } from "services/wallets/WalletService"; import { useTokenOperationsActions, useTokenOperationsStore, } from "store/token-operations/hooks"; -import { useSelectedAdapter, useWalletStore } from 'store/wallet/hooks' +import { useWalletStore } from 'store/wallet/hooks' import { useIsExpandedView, useWalletModalToggle, @@ -19,8 +18,7 @@ import { import { StyledTokenOperationActions } from "styles/styles"; import Icon from "./Icon"; import { ActionCategory, ActionType, Adapters } from 'services/wallets/types' -import { client, GAS_FEE, waitForSeqno } from "services/api"; -import { Address } from "ton"; +import { GAS_FEE } from "services/api"; import SuccessModal from "./SuccessModal"; import useValidation from "./useValidation"; import TxError from "./TxError"; @@ -30,7 +28,7 @@ import { useTranslation } from "react-i18next"; import TxLoader from "./TxLoader"; import { isMobile } from "react-device-detect"; import { QRCode } from "react-qrcode-logo"; -import { requestTonConnectTransaction } from 'services/wallets/adapters/TonConnectAdapter' +import { useSubmitTransaction } from 'hooks/useSubmitTransaction' interface Props { srcToken: PoolInfo; @@ -88,58 +86,18 @@ const TokenOperations = ({ sendTonConnectTransaction, } = useTokenOperationsActions(); const { t } = useTranslation(); + const submitTransaction = useSubmitTransaction() - - + const submitInternalTransaction = () => submitTransaction(getTxRequest, sendAnalyticsEvent, getBalances, setKeeperTransactionLink) const onSubmit = () => { if ( adapterId === Adapters.TON_HUB && isMobile) { setShowTxLoader(true) } else { - submitTransaction() + submitInternalTransaction() } }; - const useSubmitTransaction = () => { - const selectedAdapter = useSelectedAdapter() - - const submitTransaction = async () => { - const txRequest = await getTxRequest(); - - const waiter = await waitForSeqno( - client.openWalletFromAddress({ - source: Address.parse(address!!), - }) - ) - - if(!selectedAdapter?.tonConnect) { - const tx = async () => { - let deepLinkUrl = await walletService.requestTransaction(adapterId!!, session, txRequest); - if (typeof deepLinkUrl === 'string') { - if (isMobile) { - window.location.href = deepLinkUrl - } else { - setKeeperTransactionLink(deepLinkUrl) - } - } - } - sendTransaction(tx) - await waiter(); - } else { - sendTonConnectTransaction(async () => await requestTonConnectTransaction(txRequest)) - await waiter(); - } - - setKeeperTransactionLink('') - sendAnalyticsEvent() - getTokensBalance(getBalances) - } - - return submitTransaction - } - - const submitTransaction = useSubmitTransaction() - useEffect(() => { if (address && refreshAmountsOnActionChange) { getTokensBalance(getBalances); @@ -184,7 +142,7 @@ const TokenOperations = ({ open={showTxLoader} adapterId={adapterId} close={closeTransactionLoader} - confirm={submitTransaction} + confirm={submitInternalTransaction} /> {qrCodeComponent()} From cc833611e5c68e7d78dc71e00a33ac8a40cb48c0 Mon Sep 17 00:00:00 2001 From: antongotech <105588893+antongotech@users.noreply.github.com> Date: Wed, 1 Feb 2023 12:49:58 +0200 Subject: [PATCH 12/28] bugfixes & translations --- src/App.tsx | 14 ++++++++++++-- src/components/Navbar/LogoWithText.tsx | 5 ++++- src/consts/index.ts | 10 ++-------- src/hooks/useSubmitTransaction.ts | 3 +++ .../components/TokenOperations/SrcToken.tsx | 13 +++++++++---- .../components/TokenOperations/SuccessModal.tsx | 4 ++-- src/services/i18next/locales/en.json | 5 ++++- src/services/i18next/locales/es.json | 5 ++++- src/services/i18next/locales/pt.json | 5 ++++- src/services/i18next/locales/ru.json | 5 ++++- src/services/i18next/locales/zh.json | 5 ++++- 11 files changed, 52 insertions(+), 22 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index a75eccdc1..06942b9d1 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,10 +1,10 @@ -import { Box } from "@mui/material"; +import { Box, Typography } from '@mui/material' import AppRoutes from "router/Router"; import { Navbar } from "components"; import { LAYOUT_MAX_WIDTH } from "consts"; import { styled } from "@mui/system"; import SelectWallet from "components/SelectWallet"; -import { useWalletActions, useWalletStore } from 'store/wallet/hooks' +import { useSelectedAdapter, useWalletActions, useWalletStore } from 'store/wallet/hooks' import { AppGrid } from "styles/styles"; import { useWebAppResize } from "store/application/hooks"; import './services/i18next/i18n'; @@ -14,6 +14,10 @@ import { setClienT } from 'services/api' import { useDispatch, useSelector } from 'react-redux' import { fetchTonConnectWallets } from 'store/wallet/actions' import { RootState } from 'store/store' +import FullPageLoader from 'components/FullPageLoader' +import { useTokenOperationsStore } from 'store/token-operations/hooks' +import { isMobile } from 'react-device-detect' +import { useTranslation } from 'react-i18next' const StyledAppContainer = styled(Box)({ display: "flex", @@ -37,6 +41,9 @@ const App = () => { const { adapterId } = useWalletStore(); const wallets = useSelector((state: RootState) => state.wallet.allWallets) const dispatch = useDispatch<any>() + const { txPending } = useTokenOperationsStore(); + const adapter = useSelectedAdapter() + const {t} = useTranslation() useWebAppResize(); useEffect(() => { @@ -56,6 +63,9 @@ const App = () => { return ( <> + <FullPageLoader open={txPending && !isMobile}> + <Typography>{t('pending-transaction', {adapter: adapter?.name || ''})}</Typography> + </FullPageLoader> <StyledAppContainer> <Navbar /> <SelectWallet /> diff --git a/src/components/Navbar/LogoWithText.tsx b/src/components/Navbar/LogoWithText.tsx index 8d351e4b0..31590f0a3 100644 --- a/src/components/Navbar/LogoWithText.tsx +++ b/src/components/Navbar/LogoWithText.tsx @@ -8,6 +8,7 @@ import { ROUTES } from "router/routes"; import useNavigateWithParams from "hooks/useNavigateWithParams"; import { useApplicationStore } from "store/application/hooks"; import { OperationType } from "store/application/reducer"; +import { APP_VERSION } from 'consts' const StyledText = styled(Typography)(({ theme }) => ({ fontSize: 18, @@ -45,7 +46,9 @@ const LogoWithText = () => { > <img className={classes.logo} src={TonLogo} alt="" /> <StyledText> - <strong>Ton</strong>Swap + <strong>Ton</strong>Swap{' '} + {(window.location.hostname === "localhost" || window.location.hostname === "127.0.0.1" || window.location.hostname.includes('netlify.app') || window.location.hostname.includes('ngrok.io')) && + <span style={{fontSize: 11}}>{APP_VERSION}</span>} </StyledText> </Box> ); diff --git a/src/consts/index.ts b/src/consts/index.ts index dbae2984b..54d562615 100644 --- a/src/consts/index.ts +++ b/src/consts/index.ts @@ -1,10 +1,9 @@ import { getParamsFromUrl } from "utils"; -const LOCAL_STORAGE_ADDRESS = "ton_address"; const LAYOUT_MAX_WIDTH = "1200px"; const TELEGRAM_WEBAPP_PARAM = "tg"; -const DESTINATION_PATH = "destination_path"; const APP_NAME = "TonSwap"; +const APP_VERSION = '1.0.3' const TEST_MODE = "test-mode"; const TON_WALLET_EXTENSION_URL = "https://chrome.google.com/webstore/detail/ton-wallet/nphplpgoakhhjchkkhmiggakijnkhfnd"; @@ -22,15 +21,14 @@ const colors = [ const TOKENS_IN_LOCAL_STORAGE = "user_tokens"; export { - LOCAL_STORAGE_ADDRESS, LAYOUT_MAX_WIDTH, TELEGRAM_WEBAPP_PARAM, TON_WALLET_EXTENSION_URL, - DESTINATION_PATH, APP_NAME, TEST_MODE, TOKENS_IN_LOCAL_STORAGE, colors, + APP_VERSION, }; export const COMING_SOON = "(coming soon)"; @@ -40,10 +38,6 @@ export const TELEGRAM = "https://t.me/mint_xyz"; export const SUPPORT = "https://t.me/Mint_xyz_chat"; -export const ZERO_ADDRESS = "EQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAM9c"; - -export const BETA_TEXT = "TonSwap is in Beta, use at your own risk"; - export const isDebug = () => getParamsFromUrl("beta") || localStorage["debug"]; export const DECIMALS_LIMIT = 9; diff --git a/src/hooks/useSubmitTransaction.ts b/src/hooks/useSubmitTransaction.ts index a0af9fcd0..56cac88e0 100644 --- a/src/hooks/useSubmitTransaction.ts +++ b/src/hooks/useSubmitTransaction.ts @@ -38,6 +38,9 @@ export const useSubmitTransaction = () => { sendTransaction(tx) await waiter(); } else { + if(isMobile) { + window.location.href = `https://app.tonkeeper.com` + } sendTonConnectTransaction(async () => await requestTonConnectTransaction(txRequest)) await waiter(); } diff --git a/src/screens/components/TokenOperations/SrcToken.tsx b/src/screens/components/TokenOperations/SrcToken.tsx index 0f47a60ca..b409a0648 100644 --- a/src/screens/components/TokenOperations/SrcToken.tsx +++ b/src/screens/components/TokenOperations/SrcToken.tsx @@ -7,10 +7,10 @@ import { } from "store/token-operations/hooks"; import { InInput } from "store/token-operations/reducer"; import { useWalletStore } from "store/wallet/hooks"; -import { fromNano, toNano } from "ton"; import { useDebouncedCallback } from "use-debounce"; import { fromDecimals, toDecimals } from "utils"; import { calculateTokens } from "./util"; + interface Props { token: PoolInfo; destTokenName: string; @@ -35,7 +35,8 @@ const SrcToken = ({ totalBalances, srcLoading, srcAvailableAmountLoading, - inInput + inInput, + txSuccess } = useTokenOperationsStore(); const { address } = useWalletStore(); @@ -107,8 +108,12 @@ const SrcToken = ({ } }, []); - - + useEffect(() => { + if(txSuccess) { + updateSrcTokenAmount(''); + updateDestTokenAmount(''); + } + }, [txSuccess]) return ( <SwapCard diff --git a/src/screens/components/TokenOperations/SuccessModal.tsx b/src/screens/components/TokenOperations/SuccessModal.tsx index 4c33ea570..5c697b009 100644 --- a/src/screens/components/TokenOperations/SuccessModal.tsx +++ b/src/screens/components/TokenOperations/SuccessModal.tsx @@ -40,9 +40,9 @@ function SuccessModal({ actionType }: Props) { ); case ActionType.SELL: return ( - <Container title={t('purchase-confirmation')}> + <Container title={t('sell-confirmation')}> <Box className="row"> - <Typography>{t('token-received', { token: txConfirmation.tokenName })}</Typography> + <Typography>{t('token-sold', { token: txConfirmation.tokenName })}</Typography> <Typography> <BigNumberDisplay value={txConfirmation.srcTokenAmount} />{" "} </Typography> diff --git a/src/services/i18next/locales/en.json b/src/services/i18next/locales/en.json index 9a039de50..e71f93cff 100644 --- a/src/services/i18next/locales/en.json +++ b/src/services/i18next/locales/en.json @@ -16,10 +16,12 @@ "token-added-pool": "{{token}} added to pool", "token-removed-pool": "{{token}} removed from pool", "ton-paid": "TON Paid", + "token-sold": "{{token}} Sold", "ton-received": "TON Received", "ton-removed-pool": "Ton removed from pool", "ton-added-pool": "Ton added to pool", "purchase-confirmation": "Purchase Confirmation", + "sell-confirmation": "Sale Confirmation", "liquidity-removed": "Liquidity Removed", "liquidity-added": "Liquidity Added", "address-copy": "Address copied", @@ -58,6 +60,7 @@ "pool-info": "Pool info", "liquidity": "Liquidity", "warning": "WARNING", - "impact-warning": "Price Impact warning!" + "impact-warning": "Price Impact warning!", + "pending-transaction": "Please check your {{adapter}} wallet for pending transaction" } } diff --git a/src/services/i18next/locales/es.json b/src/services/i18next/locales/es.json index 933b4b9e4..336d422e9 100644 --- a/src/services/i18next/locales/es.json +++ b/src/services/i18next/locales/es.json @@ -16,10 +16,12 @@ "token-added-pool": "{{token}} agregado a la piscina", "token-removed-pool": "{{token}} eliminado de la piscina", "ton-paid": "Toncoin pagado", + "token-sold": "{{token}} Vendido", "ton-received": "Toncoin recibido", "ton-removed-pool": "Toncoin eliminado de la piscina", "ton-added-pool": "Toncoin agregado a la piscina", "purchase-confirmation": "Confirmación de compra", + "sell-confirmation": "Confirmación de venta", "liquidity-removed": "Liquidez eliminada", "liquidity-added": "Liquidez agregada", "address-copy": "Dirección copiada", @@ -58,6 +60,7 @@ "pool-info": "Liquidez de la información del pool", "liquidity": "Liquidez", "warning": "ADVERTENCIA", - "impact-warning": "Advertencia de impacto de precio!" + "impact-warning": "Advertencia de impacto de precio!", + "pending-transaction": "Verifique su billetera {{adapter}} para transacciones pendientes" } } \ No newline at end of file diff --git a/src/services/i18next/locales/pt.json b/src/services/i18next/locales/pt.json index 332d334f7..afff4fb6f 100644 --- a/src/services/i18next/locales/pt.json +++ b/src/services/i18next/locales/pt.json @@ -16,10 +16,12 @@ "token-added-pool": "{{token}} adicionado a pool", "token-removed-pool": "{{token}} removido da pool", "ton-paid": "Toncoin Pago", + "token-sold": "{{token}} Vendido", "ton-received": "Toncoin Recebido", "ton-removed-pool": "Toncoin removido da pool", "ton-added-pool": "Toncoin adicionado a pool", "purchase-confirmation": "Confirmação de compra", + "sell-confirmation": "Confirmação de venda", "liquidity-removed": "Liquidez removida", "liquidity-added": "Liquidez adicionada", "address-copy": "Endereço copiado", @@ -58,6 +60,7 @@ "pool-info": "Liquidez das informações do pool", "liquidity": "Liquidez", "warning": "AVISO", - "impact-warning": "Aviso de impacto de preço!" + "impact-warning": "Aviso de impacto de preço!", + "pending-transaction": "Por favor, verifique sua carteira {{adapter}} para transações pendentes" } } \ No newline at end of file diff --git a/src/services/i18next/locales/ru.json b/src/services/i18next/locales/ru.json index 310975e5a..170c06f0d 100644 --- a/src/services/i18next/locales/ru.json +++ b/src/services/i18next/locales/ru.json @@ -16,10 +16,12 @@ "token-added-pool": "{{token}} добавлен в пул", "token-removed-pool": "{{token}} удален из пула", "ton-paid": "TON оплачено", + "token-sold": "{{token}} продано", "ton-received": "TON получено", "ton-removed-pool": "TON удален из пула", "ton-added-pool": "TON добавлен в пул", "purchase-confirmation": "Подтверждение покупки", + "sell-confirmation": "Подтверждение продажи", "liquidity-removed": "Ликвидность удалена", "liquidity-added": "Добавлена ​​ликвидность", "address-copy": "Адрес скопирован", @@ -58,6 +60,7 @@ "pool-info": "Информация о пуле", "liquidity": "Ликвидность", "warning": "ПРЕДУПРЕЖДЕНИЕ", - "impact-warning": "Предупреждение о влиянии цены!" + "impact-warning": "Предупреждение о влиянии цены!", + "pending-transaction": "Пожалуйста, проверьте ваш {{adapter}} кошелек на наличие ожидающих транзакции" } } diff --git a/src/services/i18next/locales/zh.json b/src/services/i18next/locales/zh.json index 2a762727e..937fee8d8 100644 --- a/src/services/i18next/locales/zh.json +++ b/src/services/i18next/locales/zh.json @@ -16,10 +16,12 @@ "token-added-pool": "{{token}}添加進池 ", "token-removed-pool": "{{token}} 從池中移除", "ton-paid": "Toncoin 支付", + "token-sold": "{{token}} 賣", "ton-received": "Toncoin 收到", "ton-removed-pool": "Toncoin 從池中移除", "ton-added-pool": "Toncoin 添加進池", "purchase-confirmation": "購買確認", + "sell-confirmation": "銷售確認", "liquidity-removed": "流動性移除", "liquidity-added": "增加流動性", "address-copy": "複製地址", @@ -58,6 +60,7 @@ "pool-info": "池信息流動性", "liquidity": "流動性", "warning": "警告", - "impact-warning": "價格影響警告!" + "impact-warning": "價格影響警告!", + "pending-transaction": "請檢查您的 {{adapter}} 錢包是否有待處理的交易" } } From b9aa2d6d5a3d97e50d6dd9becd4ffc74583449d9 Mon Sep 17 00:00:00 2001 From: antongotech <105588893+antongotech@users.noreply.github.com> Date: Wed, 1 Feb 2023 15:16:20 +0200 Subject: [PATCH 13/28] button state bugfix --- src/consts/index.ts | 2 +- src/screens/components/TokenOperations/index.tsx | 11 +++++------ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/consts/index.ts b/src/consts/index.ts index 54d562615..7828602dd 100644 --- a/src/consts/index.ts +++ b/src/consts/index.ts @@ -3,7 +3,7 @@ import { getParamsFromUrl } from "utils"; const LAYOUT_MAX_WIDTH = "1200px"; const TELEGRAM_WEBAPP_PARAM = "tg"; const APP_NAME = "TonSwap"; -const APP_VERSION = '1.0.3' +const APP_VERSION = '1.0.4' const TEST_MODE = "test-mode"; const TON_WALLET_EXTENSION_URL = "https://chrome.google.com/webstore/detail/ton-wallet/nphplpgoakhhjchkkhmiggakijnkhfnd"; diff --git a/src/screens/components/TokenOperations/index.tsx b/src/screens/components/TokenOperations/index.tsx index f6f5c2d3d..519fd13ee 100644 --- a/src/screens/components/TokenOperations/index.tsx +++ b/src/screens/components/TokenOperations/index.tsx @@ -29,6 +29,7 @@ import TxLoader from "./TxLoader"; import { isMobile } from "react-device-detect"; import { QRCode } from "react-qrcode-logo"; import { useSubmitTransaction } from 'hooks/useSubmitTransaction' +import { getWalletAddress } from 'store/wallet/utils' interface Props { srcToken: PoolInfo; @@ -79,11 +80,8 @@ const TokenOperations = ({ destToken ); const { - onResetAmounts, getTokensBalance, resetTokensBalance, - sendTransaction, - sendTonConnectTransaction, } = useTokenOperationsActions(); const { t } = useTranslation(); const submitTransaction = useSubmitTransaction() @@ -122,7 +120,8 @@ const TokenOperations = ({ } function qrCodeComponent() { - + const address = getWalletAddress(); + const el = ( <Popup open={true} onClose={onClose}> <StyledContainer> @@ -178,7 +177,7 @@ const TokenOperations = ({ <Box className={classes.button}> {!address ? ( <ActionButton onClick={onConnect}>{t('connect-wallet')}</ActionButton> - ) : insufficientFunds ? ( + ) : insufficientFunds && !address.length ? ( <ActionButton isDisabled={disabled || insufficientFunds} onClick={() => { }} @@ -195,7 +194,7 @@ const TokenOperations = ({ ) : ( <ActionButton isLoading={showTxLoader || txPending} - isDisabled={disabled || insufficientFunds} + isDisabled={disabled || insufficientFunds || !address?.length} onClick={onSubmit} > {submitButtonText} From a1cb46cd18a25e430cbbda00f6904be7f7e5755a Mon Sep 17 00:00:00 2001 From: antongotech <105588893+antongotech@users.noreply.github.com> Date: Wed, 1 Feb 2023 15:47:03 +0200 Subject: [PATCH 14/28] transaction status bugfix --- src/hooks/useSubmitTransaction.ts | 2 ++ src/store/token-operations/actions.ts | 4 +++- src/store/token-operations/hooks.ts | 13 +++++++++++-- src/store/token-operations/reducer.ts | 5 ++++- 4 files changed, 20 insertions(+), 4 deletions(-) diff --git a/src/hooks/useSubmitTransaction.ts b/src/hooks/useSubmitTransaction.ts index 56cac88e0..e02edca6e 100644 --- a/src/hooks/useSubmitTransaction.ts +++ b/src/hooks/useSubmitTransaction.ts @@ -13,10 +13,12 @@ export const useSubmitTransaction = () => { getTokensBalance, sendTransaction, sendTonConnectTransaction, + setTransactionStatus, } = useTokenOperationsActions(); const { address, adapterId, session } = useWalletStore(); const submitTransaction = async (getTxRequest: () => any, sendAnalyticsEvent: () => (undefined | void), getBalances: () => Promise<any>, setKeeperTransactionLink: Dispatch<SetStateAction<string>>) => { + setTransactionStatus(true) const txRequest = await getTxRequest(); const waiter = await waitForSeqno( diff --git a/src/store/token-operations/actions.ts b/src/store/token-operations/actions.ts index 532cbb940..7e616e3c1 100644 --- a/src/store/token-operations/actions.ts +++ b/src/store/token-operations/actions.ts @@ -1,4 +1,4 @@ -import { createAsyncThunk } from '@reduxjs/toolkit' +import { createAsyncThunk, createAction } from '@reduxjs/toolkit' import { fromDecimals } from "utils"; import { SendTransactionResponse } from '@tonconnect/sdk' @@ -17,6 +17,8 @@ export const getAmounts = createAsyncThunk< }; }); +export const onSetTransactionStatusManually = createAction<boolean>('token-operations/setTransactionStatusManually') + export const onSendTransaction = createAsyncThunk< // Return type of the payload creator any, diff --git a/src/store/token-operations/hooks.ts b/src/store/token-operations/hooks.ts index 4a04f5c4f..e2886ca5c 100644 --- a/src/store/token-operations/hooks.ts +++ b/src/store/token-operations/hooks.ts @@ -18,7 +18,7 @@ import { toggleAction, } from "./reducer"; -import { getAmounts, onSendTransaction, onSendTonConnectTransaction } from "./actions"; +import { getAmounts, onSendTransaction, onSendTonConnectTransaction, onSetTransactionStatusManually } from './actions' import useNavigateWithParams from "hooks/useNavigateWithParams"; import { PoolInfo } from "services/api/addresses"; import { SendTransactionResponse } from '@tonconnect/sdk' @@ -43,6 +43,7 @@ export const useTokenOperationsActions = (): { selectToken: (token?: PoolInfo) => void; sendTransaction: (txMethod: () => Promise<void>) => void; sendTonConnectTransaction: (res: () => Promise<SendTransactionResponse>) => void; + setTransactionStatus: (status: boolean) => void; hideTxError: () => void; closeSuccessModal: () => void; onInputChange: (inInput :InInput) => void; @@ -188,6 +189,13 @@ export const useTokenOperationsActions = (): { [dispatch] ); + const setTransactionStatus = useCallback( + (status: boolean) => { + dispatch(onSetTransactionStatusManually(status)) + }, + [dispatch] + ) + return { onResetAmounts, updateSrcTokenAmount, @@ -206,6 +214,7 @@ export const useTokenOperationsActions = (): { sendTonConnectTransaction, hideTxError, closeSuccessModal, - onInputChange + onInputChange, + setTransactionStatus }; }; diff --git a/src/store/token-operations/reducer.ts b/src/store/token-operations/reducer.ts index 057b17734..1fcb7c962 100644 --- a/src/store/token-operations/reducer.ts +++ b/src/store/token-operations/reducer.ts @@ -1,7 +1,7 @@ import { createSlice, PayloadAction } from "@reduxjs/toolkit"; import { PoolInfo } from "services/api/addresses"; -import { getAmounts, onSendTonConnectTransaction, onSendTransaction } from './actions' +import { getAmounts, onSendTonConnectTransaction, onSendTransaction, onSetTransactionStatusManually } from './actions' interface TxConfirmation { destTokenAmount: string; @@ -151,6 +151,9 @@ const WalletOperationSlice = createSlice({ state.txPending = false; state.txSuccess = true; }) + .addCase(onSetTransactionStatusManually, (state, action) => { + state.txPending = action.payload + }) }, }) From 795564b7ee336824ce4a1a3206716fd1ccd5bc10 Mon Sep 17 00:00:00 2001 From: antongotech <105588893+antongotech@users.noreply.github.com> Date: Wed, 1 Feb 2023 15:49:27 +0200 Subject: [PATCH 15/28] version raise --- src/consts/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/consts/index.ts b/src/consts/index.ts index 7828602dd..d68f0cc57 100644 --- a/src/consts/index.ts +++ b/src/consts/index.ts @@ -3,7 +3,7 @@ import { getParamsFromUrl } from "utils"; const LAYOUT_MAX_WIDTH = "1200px"; const TELEGRAM_WEBAPP_PARAM = "tg"; const APP_NAME = "TonSwap"; -const APP_VERSION = '1.0.4' +const APP_VERSION = '1.0.5' const TEST_MODE = "test-mode"; const TON_WALLET_EXTENSION_URL = "https://chrome.google.com/webstore/detail/ton-wallet/nphplpgoakhhjchkkhmiggakijnkhfnd"; From 069148575361d71496b444053d4264cee9405f3a Mon Sep 17 00:00:00 2001 From: antongotech <105588893+antongotech@users.noreply.github.com> Date: Wed, 1 Feb 2023 17:40:16 +0200 Subject: [PATCH 16/28] fixing comments --- public/manifest.json | 2 ++ src/App.tsx | 5 ++-- src/components/Navbar/LogoWithText.tsx | 5 ++-- src/hooks/useSubmitTransaction.ts | 5 +--- .../components/TokenOperations/index.tsx | 28 ++----------------- .../wallets/adapters/TonConnectAdapter.ts | 2 +- 6 files changed, 12 insertions(+), 35 deletions(-) diff --git a/public/manifest.json b/public/manifest.json index 6e12f89fc..d84238bce 100644 --- a/public/manifest.json +++ b/public/manifest.json @@ -1,6 +1,8 @@ { "short_name": "Tonswap AMM", "name": "The first decentralized AMM on The Open Network", + "url": "https://tonswap.org", + "iconUrl": "https://tonswap.org/logo192.png", "icons": [ { "src": "favicon.png", diff --git a/src/App.tsx b/src/App.tsx index 06942b9d1..7bfdc03b6 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -40,6 +40,7 @@ const App = () => { const { restoreSession, restoreAdapter } = useWalletActions(); const { adapterId } = useWalletStore(); const wallets = useSelector((state: RootState) => state.wallet.allWallets) + const walletsLength = wallets?.length const dispatch = useDispatch<any>() const { txPending } = useTokenOperationsStore(); const adapter = useSelectedAdapter() @@ -58,8 +59,8 @@ const App = () => { }, []) useEffect(() => { - !!wallets?.length && restoreAdapter(adapterId!) - }, [wallets]) + walletsLength && restoreAdapter(adapterId!) + }, [walletsLength]) return ( <> diff --git a/src/components/Navbar/LogoWithText.tsx b/src/components/Navbar/LogoWithText.tsx index 31590f0a3..9712f1e7d 100644 --- a/src/components/Navbar/LogoWithText.tsx +++ b/src/components/Navbar/LogoWithText.tsx @@ -18,6 +18,8 @@ const StyledText = styled(Typography)(({ theme }) => ({ }, })); +const showVersionPlug = () => (window.location.hostname === "localhost" || window.location.hostname === "127.0.0.1" || window.location.hostname.includes('netlify.app') || window.location.hostname.includes('ngrok.io')) ? APP_VERSION : '' + const LogoWithText = () => { const classes = useStyles(); const { selectedToken } = useTokenOperationsStore(); @@ -47,8 +49,7 @@ const LogoWithText = () => { <img className={classes.logo} src={TonLogo} alt="" /> <StyledText> <strong>Ton</strong>Swap{' '} - {(window.location.hostname === "localhost" || window.location.hostname === "127.0.0.1" || window.location.hostname.includes('netlify.app') || window.location.hostname.includes('ngrok.io')) && - <span style={{fontSize: 11}}>{APP_VERSION}</span>} + <span style={{fontSize: 11}}>{showVersionPlug()}</span> </StyledText> </Box> ); diff --git a/src/hooks/useSubmitTransaction.ts b/src/hooks/useSubmitTransaction.ts index e02edca6e..2884345b9 100644 --- a/src/hooks/useSubmitTransaction.ts +++ b/src/hooks/useSubmitTransaction.ts @@ -17,7 +17,7 @@ export const useSubmitTransaction = () => { } = useTokenOperationsActions(); const { address, adapterId, session } = useWalletStore(); - const submitTransaction = async (getTxRequest: () => any, sendAnalyticsEvent: () => (undefined | void), getBalances: () => Promise<any>, setKeeperTransactionLink: Dispatch<SetStateAction<string>>) => { + const submitTransaction = async (getTxRequest: () => any, sendAnalyticsEvent: () => (undefined | void), getBalances: () => Promise<any>) => { setTransactionStatus(true) const txRequest = await getTxRequest(); @@ -32,8 +32,6 @@ export const useSubmitTransaction = () => { if (typeof deepLinkUrl === 'string') { if (isMobile) { window.location.href = deepLinkUrl - } else { - setKeeperTransactionLink(deepLinkUrl) } } } @@ -47,7 +45,6 @@ export const useSubmitTransaction = () => { await waiter(); } - setKeeperTransactionLink('') sendAnalyticsEvent() getTokensBalance(getBalances) } diff --git a/src/screens/components/TokenOperations/index.tsx b/src/screens/components/TokenOperations/index.tsx index 519fd13ee..a452a76fe 100644 --- a/src/screens/components/TokenOperations/index.tsx +++ b/src/screens/components/TokenOperations/index.tsx @@ -1,6 +1,6 @@ import { Box, styled } from "@mui/material"; import { useEffect, useState } from "react"; -import { ActionButton, Popup } from "components"; +import { ActionButton } from "components"; import { PoolInfo } from "services/api/addresses"; import WarningAmberRoundedIcon from "@mui/icons-material/WarningAmberRounded"; import { useStyles } from "./styles"; @@ -27,9 +27,7 @@ import gaAnalytics from "services/analytics/ga/ga"; import { useTranslation } from "react-i18next"; import TxLoader from "./TxLoader"; import { isMobile } from "react-device-detect"; -import { QRCode } from "react-qrcode-logo"; import { useSubmitTransaction } from 'hooks/useSubmitTransaction' -import { getWalletAddress } from 'store/wallet/utils' interface Props { srcToken: PoolInfo; @@ -65,7 +63,6 @@ const TokenOperations = ({ const expanded = useIsExpandedView(); const classes = useStyles({ color: srcToken?.color || "", expanded }); const [showTxLoader, setShowTxLoader] = useState<boolean>(false); - const [keeperTransactionLink, setKeeperTransactionLink] = useState(""); const { txPending, srcTokenAmount } = useTokenOperationsStore(); const toggleModal = useWalletModalToggle(); @@ -86,7 +83,7 @@ const TokenOperations = ({ const { t } = useTranslation(); const submitTransaction = useSubmitTransaction() - const submitInternalTransaction = () => submitTransaction(getTxRequest, sendAnalyticsEvent, getBalances, setKeeperTransactionLink) + const submitInternalTransaction = () => submitTransaction(getTxRequest, sendAnalyticsEvent, getBalances) const onSubmit = () => { if ( adapterId === Adapters.TON_HUB && isMobile) { @@ -113,26 +110,7 @@ const TokenOperations = ({ const closeTransactionLoader = () => { setShowTxLoader(false); - setKeeperTransactionLink(""); } - function onClose() { - setKeeperTransactionLink("") - } - - function qrCodeComponent() { - const address = getWalletAddress(); - - const el = ( - <Popup open={true} onClose={onClose}> - <StyledContainer> - <p>Please Scan using the QR code using TonKeeper</p> - < QRCode logoOpacity={0.5} ecLevel={"H"} size={250} value={keeperTransactionLink} /> - </StyledContainer> - </Popup> - ); - return keeperTransactionLink && !isMobile ? el : null; - } - return ( <StyledTokenOperationActions> @@ -143,8 +121,6 @@ const TokenOperations = ({ close={closeTransactionLoader} confirm={submitInternalTransaction} /> - {qrCodeComponent()} - <SuccessModal actionType={actionType} /> <Box className={classes.content}> <Box diff --git a/src/services/wallets/adapters/TonConnectAdapter.ts b/src/services/wallets/adapters/TonConnectAdapter.ts index 7e8dd3ed2..26f64e14b 100644 --- a/src/services/wallets/adapters/TonConnectAdapter.ts +++ b/src/services/wallets/adapters/TonConnectAdapter.ts @@ -39,7 +39,7 @@ export interface TransactionDetails { message?: Cell; } -export const connector = new TonConnect({ manifestUrl: "https://tonverifier.live/tonconnect-manifest.json"}); +export const connector = new TonConnect({ manifestUrl: "https://tonswap.org/manifest.json"}); export async function disconnectTC(): Promise<void> { localStorage.removeItem('ton-connect-storage_bridge-connection') From dae9edac086bc98284b4c66b548022b71bc8d39f Mon Sep 17 00:00:00 2001 From: antongotech <105588893+antongotech@users.noreply.github.com> Date: Wed, 1 Feb 2023 17:42:07 +0200 Subject: [PATCH 17/28] fixing comments --- public/manifest.json | 2 -- public/tonswap-manifest.json | 5 +++++ src/services/wallets/adapters/TonConnectAdapter.ts | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) create mode 100644 public/tonswap-manifest.json diff --git a/public/manifest.json b/public/manifest.json index d84238bce..6e12f89fc 100644 --- a/public/manifest.json +++ b/public/manifest.json @@ -1,8 +1,6 @@ { "short_name": "Tonswap AMM", "name": "The first decentralized AMM on The Open Network", - "url": "https://tonswap.org", - "iconUrl": "https://tonswap.org/logo192.png", "icons": [ { "src": "favicon.png", diff --git a/public/tonswap-manifest.json b/public/tonswap-manifest.json new file mode 100644 index 000000000..6e5c28dfd --- /dev/null +++ b/public/tonswap-manifest.json @@ -0,0 +1,5 @@ +{ + "name": "The first decentralized AMM on The Open Network", + "url": "https://tonswap.org", + "iconUrl": "https://tonswap.org/logo192.png" +} \ No newline at end of file diff --git a/src/services/wallets/adapters/TonConnectAdapter.ts b/src/services/wallets/adapters/TonConnectAdapter.ts index 26f64e14b..780c0cc0a 100644 --- a/src/services/wallets/adapters/TonConnectAdapter.ts +++ b/src/services/wallets/adapters/TonConnectAdapter.ts @@ -39,7 +39,7 @@ export interface TransactionDetails { message?: Cell; } -export const connector = new TonConnect({ manifestUrl: "https://tonswap.org/manifest.json"}); +export const connector = new TonConnect({ manifestUrl: "https://tonswap.org/tonswap-manifest.json"}); export async function disconnectTC(): Promise<void> { localStorage.removeItem('ton-connect-storage_bridge-connection') From 87b01d79f4dc402a5b688dfd0bb6b0a562779c38 Mon Sep 17 00:00:00 2001 From: antongotech <105588893+antongotech@users.noreply.github.com> Date: Thu, 2 Feb 2023 16:27:59 +0200 Subject: [PATCH 18/28] fixed transactions bug --- src/consts/index.ts | 2 +- src/hooks/useSubmitTransaction.ts | 30 +++++++++---------- .../wallets/adapters/TonConnectAdapter.ts | 23 +++++++------- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/consts/index.ts b/src/consts/index.ts index d68f0cc57..5e6898f27 100644 --- a/src/consts/index.ts +++ b/src/consts/index.ts @@ -3,7 +3,7 @@ import { getParamsFromUrl } from "utils"; const LAYOUT_MAX_WIDTH = "1200px"; const TELEGRAM_WEBAPP_PARAM = "tg"; const APP_NAME = "TonSwap"; -const APP_VERSION = '1.0.5' +const APP_VERSION = '1.0.8' const TEST_MODE = "test-mode"; const TON_WALLET_EXTENSION_URL = "https://chrome.google.com/webstore/detail/ton-wallet/nphplpgoakhhjchkkhmiggakijnkhfnd"; diff --git a/src/hooks/useSubmitTransaction.ts b/src/hooks/useSubmitTransaction.ts index 2884345b9..c8feab4e9 100644 --- a/src/hooks/useSubmitTransaction.ts +++ b/src/hooks/useSubmitTransaction.ts @@ -1,11 +1,11 @@ import { useSelectedAdapter, useWalletStore } from 'store/wallet/hooks' import { client, waitForSeqno } from 'services/api' -import { Address } from 'ton' +import { Address, Cell } from 'ton' import { walletService } from 'services/wallets/WalletService' import { isMobile } from 'react-device-detect' import { requestTonConnectTransaction } from 'services/wallets/adapters/TonConnectAdapter' import { useTokenOperationsActions } from 'store/token-operations/hooks' -import { Dispatch, SetStateAction } from 'react' +import { onSetTransactionStatusManually } from 'store/token-operations/actions' export const useSubmitTransaction = () => { const selectedAdapter = useSelectedAdapter() @@ -14,35 +14,35 @@ export const useSubmitTransaction = () => { sendTransaction, sendTonConnectTransaction, setTransactionStatus, - } = useTokenOperationsActions(); - const { address, adapterId, session } = useWalletStore(); + } = useTokenOperationsActions() + const { address, adapterId, session } = useWalletStore() const submitTransaction = async (getTxRequest: () => any, sendAnalyticsEvent: () => (undefined | void), getBalances: () => Promise<any>) => { setTransactionStatus(true) - const txRequest = await getTxRequest(); + const txRequest = await getTxRequest() const waiter = await waitForSeqno( client.openWalletFromAddress({ source: Address.parse(address!!), - }) + }), ) - if(!selectedAdapter?.tonConnect) { + if (!selectedAdapter?.tonConnect) { const tx = async () => { - let deepLinkUrl = await walletService.requestTransaction(adapterId!!, session, txRequest); - if (typeof deepLinkUrl === 'string') { - if (isMobile) { - window.location.href = deepLinkUrl - } + let deepLinkUrl = await walletService.requestTransaction(adapterId!!, session, txRequest) + if (typeof deepLinkUrl === 'string' && isMobile) { + window.location.href = deepLinkUrl } } sendTransaction(tx) - await waiter(); + await waiter() } else { - if(isMobile) { + if (isMobile) { + onSetTransactionStatusManually(true) window.location.href = `https://app.tonkeeper.com` } sendTonConnectTransaction(async () => await requestTonConnectTransaction(txRequest)) - await waiter(); + await waiter() + onSetTransactionStatusManually(false) } sendAnalyticsEvent() diff --git a/src/services/wallets/adapters/TonConnectAdapter.ts b/src/services/wallets/adapters/TonConnectAdapter.ts index 780c0cc0a..2eba76647 100644 --- a/src/services/wallets/adapters/TonConnectAdapter.ts +++ b/src/services/wallets/adapters/TonConnectAdapter.ts @@ -39,7 +39,9 @@ export interface TransactionDetails { message?: Cell; } -export const connector = new TonConnect({ manifestUrl: "https://tonswap.org/tonswap-manifest.json"}); +// TODO revert to the link below after pr is approved +//"https://tonswap.org/tonswap-manifest.json" +export const connector = new TonConnect({ manifestUrl: "https://tonverifier.live/tonconnect-manifest.json"}); export async function disconnectTC(): Promise<void> { localStorage.removeItem('ton-connect-storage_bridge-connection') @@ -102,17 +104,16 @@ export async function requestTonConnectTransaction( request: TransactionDetails, ): Promise<SendTransactionResponse> { await connector.restoreConnection() + + const message = { + address: request.to, + amount: request.value, + //@ts-ignore + payload: request.payload, + } + return connector.sendTransaction({ validUntil: Date.now() + 5 * 60 * 1000, - messages: [ - { - address: request.to, - amount: request.value, - stateInit: request.stateInit - ? stateInitToBuffer(request.stateInit).toString("base64") - : undefined, - payload: request.message ? request.message.toBoc().toString("base64") : undefined, - }, - ], + messages: [message], }); } \ No newline at end of file From d7000cfd0bf71426ea5611f3d2c53124c1cc2117 Mon Sep 17 00:00:00 2001 From: antongotech <105588893+antongotech@users.noreply.github.com> Date: Sun, 12 Feb 2023 16:12:19 +0200 Subject: [PATCH 19/28] mytonwallet connector added --- src/consts/index.ts | 2 +- src/services/wallets/config.ts | 8 +++ src/services/wallets/types.ts | 3 +- src/store/wallet/actions.ts | 116 +++++++++++++++++++++------------ src/store/wallet/hooks.ts | 6 +- 5 files changed, 85 insertions(+), 50 deletions(-) diff --git a/src/consts/index.ts b/src/consts/index.ts index 5e6898f27..e7a438149 100644 --- a/src/consts/index.ts +++ b/src/consts/index.ts @@ -3,7 +3,7 @@ import { getParamsFromUrl } from "utils"; const LAYOUT_MAX_WIDTH = "1200px"; const TELEGRAM_WEBAPP_PARAM = "tg"; const APP_NAME = "TonSwap"; -const APP_VERSION = '1.0.8' +const APP_VERSION = '1.0.9' const TEST_MODE = "test-mode"; const TON_WALLET_EXTENSION_URL = "https://chrome.google.com/webstore/detail/ton-wallet/nphplpgoakhhjchkkhmiggakijnkhfnd"; diff --git a/src/services/wallets/config.ts b/src/services/wallets/config.ts index d072599da..a411d7b86 100644 --- a/src/services/wallets/config.ts +++ b/src/services/wallets/config.ts @@ -1,5 +1,6 @@ import { Adapter, Adapters } from "./types"; import TonhubImg from "assets/images/shared/tonhub.png"; +import ChromeExtImg from "assets/images/shared/chrome.svg"; let adapters: Adapter[] = [ { @@ -10,6 +11,13 @@ let adapters: Adapter[] = [ description: "Crypto wallet for Toncoin", tonConnect: false, }, + // { + // name: "Google Chrome Plugin", + // type: Adapters.TON_WALLET, + // icon: ChromeExtImg, + // mobileCompatible: false, + // description: "TON Wallet Plugin for Google Chrome", + // }, ]; export { adapters }; diff --git a/src/services/wallets/types.ts b/src/services/wallets/types.ts index ac31e46a2..3674faf01 100644 --- a/src/services/wallets/types.ts +++ b/src/services/wallets/types.ts @@ -42,7 +42,8 @@ export enum Adapters { TON_HUB = "tonhub", TON_WALLET = "ton-wallet", TON_KEEPER = "ton-keeper", - OPENMASK = 'openmask' + OPENMASK = 'openmask', + MYTONWALLET = 'mytonwallet' } export interface Adapter { diff --git a/src/store/wallet/actions.ts b/src/store/wallet/actions.ts index b2f6bfa1a..e43fa85ed 100644 --- a/src/store/wallet/actions.ts +++ b/src/store/wallet/actions.ts @@ -1,52 +1,82 @@ -import { createAction, createAsyncThunk } from "@reduxjs/toolkit"; +import { createAction, createAsyncThunk } from '@reduxjs/toolkit' import { Adapter, Adapters, Wallet } from 'services/wallets/types' -import { walletService } from "services/wallets/WalletService"; +import { walletService } from 'services/wallets/WalletService' import { getTonConnectWallets } from 'services/wallets/adapters/TonConnectAdapter' export const onWalletConnect = createAction<{ - wallet: Wallet; - _adapterId?: string; -}>("wallet/onWalletConnect"); -export const resetWallet = createAction("wallet/resetWallet"); -export const setConnecting = createAction<boolean>("wallet/setConnecting"); -export const setAdapter = createAction<Adapter>("wallet/setAdapter") - -export const setTonHubSession = createAction<string | {}>("wallet/setTonHubSession"); -export const updateWallet = createAction<any>("wallet/updateWallet"); - -export const awaitWalletReadiness = createAsyncThunk< - { wallet: Wallet; adapterId: Adapters }, - { adapterId: Adapters; session: string | {} } ->("wallet/createWalletSession", async ({ adapterId, session }, thunkApi) => { - - const wallet = await walletService.awaitReadiness(adapterId, session); - if (!wallet) { - thunkApi.dispatch(resetWallet); - throw new Error(""); - } - return { - wallet, - adapterId, - }; -}); + wallet: Wallet; + _adapterId?: string; +}>('wallet/onWalletConnect') +export const resetWallet = createAction('wallet/resetWallet') +export const setConnecting = createAction<boolean>('wallet/setConnecting') +export const setAdapter = createAction<Adapter>('wallet/setAdapter') + +export const setTonHubSession = createAction<string | {}>('wallet/setTonHubSession') +export const updateWallet = createAction<any>('wallet/updateWallet') + +export const awaitWalletReadiness = createAsyncThunk<{ wallet: Wallet; adapterId: Adapters }, + { adapterId: Adapters; session: string | {} }>('wallet/createWalletSession', async ({ + adapterId, + session, +}, thunkApi) => { + + const wallet = await walletService.awaitReadiness(adapterId, session) + if (!wallet) { + thunkApi.dispatch(resetWallet) + throw new Error('') + } + return { + wallet, + adapterId, + } +}) + +const defineWalletType: any = (name: string) => { + if (name === 'OpenMask') { + return Adapters.OPENMASK + } + if (name === 'TonKeeper') { + return Adapters.TON_KEEPER + } + if (name === 'MyTonWallet') { + return Adapters.MYTONWALLET + } +} + +const defineWalletDescription: any = (name: string) => { + if (name === 'OpenMask') { + return 'TON Wallet Plugin for Google Chrome' + } + return 'A mobile wallet in your pocket' +} + +const defineIsMobileCompatible: any = (name: string) => { + if (name === 'OpenMask') { + return false + } + if (name === 'Tonkeeper') { + return true + } + if (name === 'MyTonWallet') { + return false + } +} export const fetchTonConnectWallets = createAsyncThunk<Adapter[]>( - "wallet/fetchTonConnectWallets", async () => { - const supportedWallets = await getTonConnectWallets() - const result = supportedWallets.map((w) => { - return { - name: w.name, - type: w.name === 'OpenMask' ? Adapters.OPENMASK : Adapters.TON_KEEPER, - icon: w.imageUrl, - mobileCompatible: w.name !== 'OpenMask', - description: w.name === 'OpenMask' - ? 'TON Wallet Plugin for Google Chrome' - : 'A mobile wallet in your pocket', - tonConnect: true, - walletInfo: w, - } - }) + 'wallet/fetchTonConnectWallets', async () => { + const supportedWallets = await getTonConnectWallets() + const result = supportedWallets.map((w) => { + return { + name: w.name, + type: defineWalletType(w.name), + icon: w.imageUrl, + mobileCompatible: defineIsMobileCompatible(w.name), + description: defineWalletDescription(w.name), + tonConnect: true, + walletInfo: w, + } + }) return result - } + }, ) \ No newline at end of file diff --git a/src/store/wallet/hooks.ts b/src/store/wallet/hooks.ts index c13da5a4b..9aca2bec2 100644 --- a/src/store/wallet/hooks.ts +++ b/src/store/wallet/hooks.ts @@ -20,10 +20,6 @@ export const useWalletSelect = () => { const [localSession, setLocalSession] = useState<null | string>(null) const { resetWallet } = useWalletActions() const wallets = useSelector((state: RootState) => state.wallet.allWallets) - const _resetWallet = () => { - setLocalSession(null) - resetWallet() - } const selectWallet = async (adapterId: Adapters, supportsTonConnect?: boolean) => { resetWallet() @@ -57,7 +53,7 @@ export const useWalletSelect = () => { }) dispatch(updateWallet({ wallet, adapterId: Adapters.TON_KEEPER })) } - return { selectWallet, session: localSession, resetWallet: _resetWallet } + return { selectWallet, session: localSession } } export const useWalletActions = (): { From 329504ff3126d073fb7baf710f7e641b396e2c32 Mon Sep 17 00:00:00 2001 From: antongotech <105588893+antongotech@users.noreply.github.com> Date: Sun, 12 Feb 2023 16:14:32 +0200 Subject: [PATCH 20/28] bugfix --- src/components/SelectWallet/AdaptersList.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/SelectWallet/AdaptersList.tsx b/src/components/SelectWallet/AdaptersList.tsx index 369451923..6539cf68a 100644 --- a/src/components/SelectWallet/AdaptersList.tsx +++ b/src/components/SelectWallet/AdaptersList.tsx @@ -116,7 +116,7 @@ function AdaptersList({ return ( <StyledListItem disablePadding - key={type} + key={name} style={{ pointerEvents: isLoading ? "none" : "all" }} > <StyledListItemButton From d6b82efde60b65adab0b1df6c7e0157bead71f75 Mon Sep 17 00:00:00 2001 From: antongotech <105588893+antongotech@users.noreply.github.com> Date: Wed, 15 Feb 2023 12:45:43 +0200 Subject: [PATCH 21/28] bugfixes --- src/App.tsx | 4 ++-- src/components/FullPageLoader.tsx | 12 ++++++------ src/consts/index.ts | 2 +- src/services/wallets/types.ts | 2 +- src/store/wallet/actions.ts | 26 +++++++++++++------------- src/store/wallet/hooks.ts | 4 +++- src/store/wallet/reducer.ts | 3 +++ 7 files changed, 29 insertions(+), 24 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 7bfdc03b6..4b99f3328 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -59,8 +59,8 @@ const App = () => { }, []) useEffect(() => { - walletsLength && restoreAdapter(adapterId!) - }, [walletsLength]) + adapterId && walletsLength && restoreAdapter(adapterId) + }, [walletsLength, adapterId]) return ( <> diff --git a/src/components/FullPageLoader.tsx b/src/components/FullPageLoader.tsx index aee9036b4..34c503fc2 100644 --- a/src/components/FullPageLoader.tsx +++ b/src/components/FullPageLoader.tsx @@ -23,7 +23,7 @@ const StyledContainer = styled(Box)({ function FullPageLoader({ open, children }: Props) { const { adapterId } = useWalletStore(); const { t } = useTranslation(); - const showReminderInLoader = !isMobile && adapterId === Adapters.TON_HUB; + // const showReminderInLoader = !isMobile && adapterId === Adapters.TON_HUB; return ( <Backdrop @@ -37,11 +37,11 @@ function FullPageLoader({ open, children }: Props) { <StyledContainer> <CircularProgress color="inherit" /> {children} - {showReminderInLoader && ( - <Typography> - {t('check-tonhub')} - </Typography> - )} + {/*{showReminderInLoader && (*/} + {/* <Typography>*/} + {/* {t('check-tonhub')}*/} + {/* </Typography>*/} + {/*)}*/} </StyledContainer> </Backdrop> ); diff --git a/src/consts/index.ts b/src/consts/index.ts index 2ec752ea8..a48fca8ed 100644 --- a/src/consts/index.ts +++ b/src/consts/index.ts @@ -3,7 +3,7 @@ import { getParamsFromUrl } from "utils"; const LAYOUT_MAX_WIDTH = "1200px"; const TELEGRAM_WEBAPP_PARAM = "tg"; const APP_NAME = "TonSwap"; -const APP_VERSION = '1.0.9' +const APP_VERSION = '1.0.11' const TEST_MODE = "test-mode"; const TON_WALLET_EXTENSION_URL = "https://chrome.google.com/webstore/detail/ton-wallet/nphplpgoakhhjchkkhmiggakijnkhfnd"; diff --git a/src/services/wallets/types.ts b/src/services/wallets/types.ts index 3674faf01..f1174642e 100644 --- a/src/services/wallets/types.ts +++ b/src/services/wallets/types.ts @@ -41,7 +41,7 @@ export interface TonWalletProvider { export enum Adapters { TON_HUB = "tonhub", TON_WALLET = "ton-wallet", - TON_KEEPER = "ton-keeper", + TON_KEEPER = "tonkeeper", OPENMASK = 'openmask', MYTONWALLET = 'mytonwallet' } diff --git a/src/store/wallet/actions.ts b/src/store/wallet/actions.ts index e43fa85ed..4ce1ba795 100644 --- a/src/store/wallet/actions.ts +++ b/src/store/wallet/actions.ts @@ -31,17 +31,17 @@ export const awaitWalletReadiness = createAsyncThunk<{ wallet: Wallet; adapterId } }) -const defineWalletType: any = (name: string) => { - if (name === 'OpenMask') { - return Adapters.OPENMASK - } - if (name === 'TonKeeper') { - return Adapters.TON_KEEPER - } - if (name === 'MyTonWallet') { - return Adapters.MYTONWALLET - } -} +// const defineWalletType: any = (name: string) => { +// if (name === 'OpenMask') { +// return Adapters.OPENMASK +// } +// if (name === 'TonKeeper') { +// return Adapters.TON_KEEPER +// } +// if (name === 'MyTonWallet') { +// return Adapters.MYTONWALLET +// } +// } const defineWalletDescription: any = (name: string) => { if (name === 'OpenMask') { @@ -68,7 +68,8 @@ export const fetchTonConnectWallets = createAsyncThunk<Adapter[]>( const result = supportedWallets.map((w) => { return { name: w.name, - type: defineWalletType(w.name), + //@ts-ignore + type: w.jsBridgeKey, icon: w.imageUrl, mobileCompatible: defineIsMobileCompatible(w.name), description: defineWalletDescription(w.name), @@ -76,7 +77,6 @@ export const fetchTonConnectWallets = createAsyncThunk<Adapter[]>( walletInfo: w, } }) - return result }, ) \ No newline at end of file diff --git a/src/store/wallet/hooks.ts b/src/store/wallet/hooks.ts index 9aca2bec2..29c312be0 100644 --- a/src/store/wallet/hooks.ts +++ b/src/store/wallet/hooks.ts @@ -29,7 +29,7 @@ export const useWalletSelect = () => { if (!adapter) { return } - + // debugger dispatch(setAdapter(adapter)) if (!supportsTonConnect) { const _session: string | {} = await walletService.createSession(adapterId) @@ -69,6 +69,7 @@ export const useWalletActions = (): { }, [dispatch]) const restoreAdapter = (adapterId: string) => { + // debugger const adapter: Adapter | null = wallets?.find((wallet) => wallet.type === adapterId) || null if (!adapter) { return @@ -77,6 +78,7 @@ export const useWalletActions = (): { } const restoreSession = useCallback(async () => { + // debugger const adapterId = localStorage.getItem('wallet:adapter-id') const session = localStorage.getItem('wallet:session') diff --git a/src/store/wallet/reducer.ts b/src/store/wallet/reducer.ts index 6af5c8b73..9e39eefa6 100644 --- a/src/store/wallet/reducer.ts +++ b/src/store/wallet/reducer.ts @@ -51,6 +51,7 @@ const reducer = createReducer(initialState, (builder) => { localStorage.removeItem("wallet:session"); }) .addCase(updateWallet, (state, action) => { + // debugger const { payload } = action; state.wallet = payload.wallet; state.adapterId = payload.adapterId; @@ -58,6 +59,7 @@ const reducer = createReducer(initialState, (builder) => { state.connecting = false }) .addCase(setTonHubSession, (state, action) => { + // debugger const { payload } = action; const session = typeof payload === "string" ? JSON.parse(payload) : payload; state.session = session; @@ -86,6 +88,7 @@ const reducer = createReducer(initialState, (builder) => { state.mobileWallets = _allWallets.filter((wallet) => wallet.mobileCompatible) }) .addCase(setAdapter, (state, action) => { + // debugger state.adapter = action.payload }) // Or, you can reference the .type field: From 539c27a194f8c5501de6b9fed24bda0a67c690d3 Mon Sep 17 00:00:00 2001 From: antongotech <105588893+antongotech@users.noreply.github.com> Date: Thu, 16 Feb 2023 10:48:43 +0200 Subject: [PATCH 22/28] mytonwallet added --- src/components/SelectWallet/AdaptersList.tsx | 2 +- src/consts/index.ts | 2 +- src/store/wallet/hooks.ts | 23 +++++++++++++++----- src/store/wallet/reducer.ts | 3 --- 4 files changed, 20 insertions(+), 10 deletions(-) diff --git a/src/components/SelectWallet/AdaptersList.tsx b/src/components/SelectWallet/AdaptersList.tsx index 09c8c4844..6539cf68a 100644 --- a/src/components/SelectWallet/AdaptersList.tsx +++ b/src/components/SelectWallet/AdaptersList.tsx @@ -111,7 +111,7 @@ function AdaptersList({ </Fade> <Fade in={!isLoading}> <StyledList> - {adaptersToShow.filter((adapter) => adapter.type !== Adapters.MYTONWALLET).map((adapter) => { + {adaptersToShow.map((adapter) => { const { type, icon, name, description, disabled, tonConnect } = adapter; return ( <StyledListItem diff --git a/src/consts/index.ts b/src/consts/index.ts index a48fca8ed..7245338bd 100644 --- a/src/consts/index.ts +++ b/src/consts/index.ts @@ -3,7 +3,7 @@ import { getParamsFromUrl } from "utils"; const LAYOUT_MAX_WIDTH = "1200px"; const TELEGRAM_WEBAPP_PARAM = "tg"; const APP_NAME = "TonSwap"; -const APP_VERSION = '1.0.11' +const APP_VERSION = '1.0.13' const TEST_MODE = "test-mode"; const TON_WALLET_EXTENSION_URL = "https://chrome.google.com/webstore/detail/ton-wallet/nphplpgoakhhjchkkhmiggakijnkhfnd"; diff --git a/src/store/wallet/hooks.ts b/src/store/wallet/hooks.ts index 29c312be0..cc626a40c 100644 --- a/src/store/wallet/hooks.ts +++ b/src/store/wallet/hooks.ts @@ -29,7 +29,6 @@ export const useWalletSelect = () => { if (!adapter) { return } - // debugger dispatch(setAdapter(adapter)) if (!supportsTonConnect) { const _session: string | {} = await walletService.createSession(adapterId) @@ -46,6 +45,16 @@ export const useWalletSelect = () => { localStorage.setItem('ton-connect-storage_bridge-connection', '{"type":"injected","jsBridgeKey":"openmask"}') return } + if (adapterId === Adapters.MYTONWALLET && (window as any).myTonWallet.isMyTonWallet) { + debugger + const wallet = await connect(adapter.walletInfo, { + onSessionLinkReady: (val: string) => { + setLocalSession(val) + }, + }) + dispatch(updateWallet({ wallet, adapterId: Adapters.MYTONWALLET })) + return + } const wallet = await connect(adapter.walletInfo, { onSessionLinkReady: (val: string) => { setLocalSession(val) @@ -69,7 +78,6 @@ export const useWalletActions = (): { }, [dispatch]) const restoreAdapter = (adapterId: string) => { - // debugger const adapter: Adapter | null = wallets?.find((wallet) => wallet.type === adapterId) || null if (!adapter) { return @@ -78,11 +86,10 @@ export const useWalletActions = (): { } const restoreSession = useCallback(async () => { - // debugger const adapterId = localStorage.getItem('wallet:adapter-id') const session = localStorage.getItem('wallet:session') - const tcBridgeConnection = localStorage.getItem('ton-connect-storage_bridge-connection') + const tcBridgeConnection = JSON.parse(localStorage.getItem('ton-connect-storage_bridge-connection') || '{}') const tcBridgeGateway = localStorage.getItem('ton-connect-storage_http-bridge-gateway') if (adapterId && session) { @@ -96,11 +103,17 @@ export const useWalletActions = (): { return } - if (tcBridgeConnection && !tcBridgeGateway) { + if (tcBridgeConnection.jsBridgeKey === Adapters.OPENMASK) { const accounts = await window.ton?.send('ton_requestAccounts') dispatch(updateWallet({ wallet: { address: accounts[0] }, adapterId: Adapters.OPENMASK })) return } + if (tcBridgeConnection.jsBridgeKey === Adapters.MYTONWALLET) { + //@ts-ignore + const accounts = await window.myTonWallet.send('requestAccounts') + dispatch(updateWallet({ wallet: { address: accounts[0] }, adapterId: Adapters.MYTONWALLET })) + return + } if (tcBridgeConnection && tcBridgeGateway) { const session = JSON.parse(tcBridgeConnection) const wallet: Wallet = { address: Address.parse(session.connectEvent.payload.items[0].address).toFriendly() } diff --git a/src/store/wallet/reducer.ts b/src/store/wallet/reducer.ts index 9e39eefa6..6af5c8b73 100644 --- a/src/store/wallet/reducer.ts +++ b/src/store/wallet/reducer.ts @@ -51,7 +51,6 @@ const reducer = createReducer(initialState, (builder) => { localStorage.removeItem("wallet:session"); }) .addCase(updateWallet, (state, action) => { - // debugger const { payload } = action; state.wallet = payload.wallet; state.adapterId = payload.adapterId; @@ -59,7 +58,6 @@ const reducer = createReducer(initialState, (builder) => { state.connecting = false }) .addCase(setTonHubSession, (state, action) => { - // debugger const { payload } = action; const session = typeof payload === "string" ? JSON.parse(payload) : payload; state.session = session; @@ -88,7 +86,6 @@ const reducer = createReducer(initialState, (builder) => { state.mobileWallets = _allWallets.filter((wallet) => wallet.mobileCompatible) }) .addCase(setAdapter, (state, action) => { - // debugger state.adapter = action.payload }) // Or, you can reference the .type field: From 01929f11567d20c74f61a2a473ff8ee7a6ac12cb Mon Sep 17 00:00:00 2001 From: antongotech <105588893+antongotech@users.noreply.github.com> Date: Thu, 16 Feb 2023 12:00:14 +0200 Subject: [PATCH 23/28] tonkeeper relaod bugfix --- src/components/FullPageLoader.tsx | 22 +++++----------------- src/services/wallets/config.ts | 7 ------- src/store/wallet/actions.ts | 15 +-------------- src/store/wallet/hooks.ts | 7 +++---- 4 files changed, 9 insertions(+), 42 deletions(-) diff --git a/src/components/FullPageLoader.tsx b/src/components/FullPageLoader.tsx index 34c503fc2..04b1b9e45 100644 --- a/src/components/FullPageLoader.tsx +++ b/src/components/FullPageLoader.tsx @@ -1,12 +1,8 @@ -import Backdrop from "@mui/material/Backdrop"; -import CircularProgress from "@mui/material/CircularProgress"; -import { styled, Typography } from "@mui/material"; -import { Box } from "@mui/system"; -import { ReactNode } from "react"; -import { isMobile } from "react-device-detect"; -import { Adapters } from "services/wallets/types"; -import { useWalletStore } from "store/wallet/hooks"; -import { useTranslation } from "react-i18next"; +import Backdrop from '@mui/material/Backdrop' +import CircularProgress from '@mui/material/CircularProgress' +import { styled } from '@mui/material' +import { Box } from '@mui/system' +import { ReactNode } from 'react' interface Props { open: boolean; @@ -21,9 +17,6 @@ const StyledContainer = styled(Box)({ }); function FullPageLoader({ open, children }: Props) { - const { adapterId } = useWalletStore(); - const { t } = useTranslation(); - // const showReminderInLoader = !isMobile && adapterId === Adapters.TON_HUB; return ( <Backdrop @@ -37,11 +30,6 @@ function FullPageLoader({ open, children }: Props) { <StyledContainer> <CircularProgress color="inherit" /> {children} - {/*{showReminderInLoader && (*/} - {/* <Typography>*/} - {/* {t('check-tonhub')}*/} - {/* </Typography>*/} - {/*)}*/} </StyledContainer> </Backdrop> ); diff --git a/src/services/wallets/config.ts b/src/services/wallets/config.ts index a411d7b86..8f7b5770a 100644 --- a/src/services/wallets/config.ts +++ b/src/services/wallets/config.ts @@ -11,13 +11,6 @@ let adapters: Adapter[] = [ description: "Crypto wallet for Toncoin", tonConnect: false, }, - // { - // name: "Google Chrome Plugin", - // type: Adapters.TON_WALLET, - // icon: ChromeExtImg, - // mobileCompatible: false, - // description: "TON Wallet Plugin for Google Chrome", - // }, ]; export { adapters }; diff --git a/src/store/wallet/actions.ts b/src/store/wallet/actions.ts index 4ce1ba795..688abe686 100644 --- a/src/store/wallet/actions.ts +++ b/src/store/wallet/actions.ts @@ -31,18 +31,6 @@ export const awaitWalletReadiness = createAsyncThunk<{ wallet: Wallet; adapterId } }) -// const defineWalletType: any = (name: string) => { -// if (name === 'OpenMask') { -// return Adapters.OPENMASK -// } -// if (name === 'TonKeeper') { -// return Adapters.TON_KEEPER -// } -// if (name === 'MyTonWallet') { -// return Adapters.MYTONWALLET -// } -// } - const defineWalletDescription: any = (name: string) => { if (name === 'OpenMask') { return 'TON Wallet Plugin for Google Chrome' @@ -65,7 +53,7 @@ const defineIsMobileCompatible: any = (name: string) => { export const fetchTonConnectWallets = createAsyncThunk<Adapter[]>( 'wallet/fetchTonConnectWallets', async () => { const supportedWallets = await getTonConnectWallets() - const result = supportedWallets.map((w) => { + return supportedWallets.map((w) => { return { name: w.name, //@ts-ignore @@ -77,6 +65,5 @@ export const fetchTonConnectWallets = createAsyncThunk<Adapter[]>( walletInfo: w, } }) - return result }, ) \ No newline at end of file diff --git a/src/store/wallet/hooks.ts b/src/store/wallet/hooks.ts index cc626a40c..79adad602 100644 --- a/src/store/wallet/hooks.ts +++ b/src/store/wallet/hooks.ts @@ -102,7 +102,7 @@ export const useWalletActions = (): { ) return } - +debugger if (tcBridgeConnection.jsBridgeKey === Adapters.OPENMASK) { const accounts = await window.ton?.send('ton_requestAccounts') dispatch(updateWallet({ wallet: { address: accounts[0] }, adapterId: Adapters.OPENMASK })) @@ -114,9 +114,8 @@ export const useWalletActions = (): { dispatch(updateWallet({ wallet: { address: accounts[0] }, adapterId: Adapters.MYTONWALLET })) return } - if (tcBridgeConnection && tcBridgeGateway) { - const session = JSON.parse(tcBridgeConnection) - const wallet: Wallet = { address: Address.parse(session.connectEvent.payload.items[0].address).toFriendly() } + if (tcBridgeConnection?.session && tcBridgeGateway?.length) { + const wallet: Wallet = { address: Address.parse(tcBridgeConnection.connectEvent.payload.items[0].address).toFriendly() } dispatch(updateWallet({ wallet, adapterId: Adapters.TON_KEEPER })) return } From 9af375295e883665984d67253bc723810fe420d3 Mon Sep 17 00:00:00 2001 From: antongotech <105588893+antongotech@users.noreply.github.com> Date: Sun, 19 Feb 2023 11:21:32 +0200 Subject: [PATCH 24/28] tonsafe addded, mobile select wallet path styled --- src/components/SelectWallet/AdaptersList.tsx | 110 +++++++++--------- .../SelectWallet/MobileFlow/index.tsx | 2 +- src/services/api/index.ts | 2 - src/services/wallets/types.ts | 3 +- src/store/wallet/actions.ts | 9 +- src/store/wallet/hooks.ts | 16 +-- src/store/wallet/reducer.ts | 4 +- 7 files changed, 76 insertions(+), 70 deletions(-) diff --git a/src/components/SelectWallet/AdaptersList.tsx b/src/components/SelectWallet/AdaptersList.tsx index 6539cf68a..74c4f9f74 100644 --- a/src/components/SelectWallet/AdaptersList.tsx +++ b/src/components/SelectWallet/AdaptersList.tsx @@ -1,5 +1,5 @@ import { styled } from '@mui/styles' -import { Box, Fade, List, ListItem, ListItemButton, Typography } from '@mui/material' +import { Box, Button, Fade, List, ListItem, ListItemButton, Typography } from '@mui/material' import Title from './Title' import { Theme } from '@mui/material/styles' import { Adapter, Adapters } from 'services/wallets/types' @@ -7,69 +7,63 @@ import CircularProgress from '@mui/material/CircularProgress' import gaAnalytics from 'services/analytics/ga/ga' import { useTranslation } from 'react-i18next' import { isMobile } from 'react-device-detect' - -const StyledListItem = styled(ListItem)( - ({ disabled }: { disabled?: boolean }) => ({ - background: "white", - width: "100%", - }) -); +import { useWalletSelect } from 'store/wallet/hooks' const StyledList = styled(List)({ - width: "100%", - gap: "5px", - display: "flex", - flexDirection: "column", -}); + width: '100%', + gap: '5px', + display: 'flex', + flexDirection: 'column', +}) const StyledListItemButton = styled(ListItemButton)({ paddingLeft: 10, -}); +}) const StyledContainer = styled(Box)(({ theme }: { theme: Theme }) => ({ - width: "100%", + width: '100%', position: 'relative', - "& .MuiCircularProgress-root": { + '& .MuiCircularProgress-root': { position: 'absolute', left: '40%', top: '50%', - transform: 'translate(-50%, -50%)' + transform: 'translate(-50%, -50%)', }, -})); +})) const StyledConnectModalTitle = styled(Box)({ - paddingLeft: "10px", -}); + paddingLeft: '10px', +}) const StyledListItemRight = styled(Box)(({ theme }: { theme: Theme }) => ({ - "& h5": { + '& h5': { color: theme.palette.secondary.main, - fontSize: "18px", - fontWeight: "500", - marginBottom: "5px", + fontSize: '18px', + fontWeight: '500', + marginBottom: '5px', }, - "& p": { + '& p': { color: theme.palette.secondary.main, - fontSize: "14px", - opacity: "0.7", + fontSize: '14px', + opacity: '0.7', }, -})); +})) const StyledIcon = styled(Box)({ - width: "40px", - height: "40px", - marginRight: "24px", - display: "flex", - alignItems: "center", - justifyContent: "center", - "& img": { - width: "100%", - height: "100%", - objectFit: "cover", + width: '40px', + height: '40px', + marginRight: '24px', + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + '& img': { + width: '100%', + height: '100%', + objectFit: 'cover', }, - "& .MuiCircularProgress-root": { + '& .MuiCircularProgress-root': { zoom: 0.85, }, -}); +}) interface Props { select: (adapter: Adapters, supportsTonConnect?: boolean) => void; @@ -88,10 +82,10 @@ function AdaptersList({ adapters, adapterLoading, isLoading, - title = "Select Wallet" + title = 'Select Wallet', }: Props) { const { t } = useTranslation() - const adaptersToShow = isMobile ? adapters.filter((adapter) => adapter.mobileCompatible) : adapters + const adaptersToShow = isMobile ? adapters.filter((adapter) => adapter.mobileCompatible || adapter.name.toLowerCase() === Adapters.TONSAFE) : adapters const onSelect = (adapter: Adapters, supportsTonConnect?: boolean) => { select(adapter, supportsTonConnect) @@ -99,7 +93,7 @@ function AdaptersList({ } if (!open) { - return null; + return null } return ( @@ -112,39 +106,45 @@ function AdaptersList({ <Fade in={!isLoading}> <StyledList> {adaptersToShow.map((adapter) => { - const { type, icon, name, description, disabled, tonConnect } = adapter; + const { type, icon, name, description, disabled, tonConnect } = adapter return ( - <StyledListItem + <ListItem disablePadding key={name} - style={{ pointerEvents: isLoading ? "none" : "all" }} + style={{ pointerEvents: isLoading ? 'none' : 'all' }} + sx={{ + width: '100%', + background: 'white', + borderRadius: '12px', + border: title === 'Select Wallet' ? '': '1px solid #007AFE' + }} > <StyledListItemButton onClick={() => { //@ts-ignore type === Adapters.OPENMASK && window.ton && !window.ton.isOpenMask - ? window.open('https://www.openmask.app/', "_blank") + ? window.open('https://www.openmask.app/', '_blank') : onSelect(type, tonConnect) }} > <StyledIcon> - <img style={{ "borderRadius": "9px" }} src={icon} /> + <img style={{ 'borderRadius': '9px' }} src={icon} /> </StyledIcon> <StyledListItemRight> - <Typography variant="h5"> - {name} <small>{disabled ? t('coming-soon') : ""}</small> + <Typography variant="h5" sx={{color: title === 'Select Wallet' ? '':'#007AFE !important'}}> + {title === 'Tap to connect' && 'Connect'} {name} <small>{disabled ? t('coming-soon') : ''}</small> </Typography> - <Typography>{description}</Typography> + {title !== 'Tap to connect' && <Typography>{description}</Typography>} </StyledListItemRight> </StyledListItemButton> - </StyledListItem> - ); + </ListItem> + ) })} </StyledList> </Fade> {isLoading && <CircularProgress />} </StyledContainer> - ); + ) } -export default AdaptersList; +export default AdaptersList diff --git a/src/components/SelectWallet/MobileFlow/index.tsx b/src/components/SelectWallet/MobileFlow/index.tsx index a0b4c678f..2659f9ac1 100644 --- a/src/components/SelectWallet/MobileFlow/index.tsx +++ b/src/components/SelectWallet/MobileFlow/index.tsx @@ -40,7 +40,7 @@ const MobileFlow = observer(({ closeModal }: Props) => { open={true} select={openDeepLink} isLoading={false} - title={"Click to connect"} + title={"Tap to connect"} /> </StyledContainer>) } diff --git a/src/services/api/index.ts b/src/services/api/index.ts index c7c9e2fef..7223a7fbd 100644 --- a/src/services/api/index.ts +++ b/src/services/api/index.ts @@ -86,7 +86,6 @@ function getOwner() { export async function _getJettonBalance(jettonWallet: Address, minterAddress?: Address) { try { console.log(`_getJettonBalance:: jettonWallet at ${jettonWallet.toFriendly()}`); - let res = await client.callGetMethod(jettonWallet, "get_wallet_data", []); const balance = hexToBn(res.stack[0][1]); const walletOwner = bytesToAddress(res.stack[1][1].bytes); @@ -100,7 +99,6 @@ export async function _getJettonBalance(jettonWallet: Address, minterAddress?: A }; } catch (e) { console.log(e); - return { balance: new BN(0), walletOwner: getOwner(), diff --git a/src/services/wallets/types.ts b/src/services/wallets/types.ts index f1174642e..43b39d0fc 100644 --- a/src/services/wallets/types.ts +++ b/src/services/wallets/types.ts @@ -43,7 +43,8 @@ export enum Adapters { TON_WALLET = "ton-wallet", TON_KEEPER = "tonkeeper", OPENMASK = 'openmask', - MYTONWALLET = 'mytonwallet' + MYTONWALLET = 'mytonwallet', + TONSAFE = 'tonsafe' } export interface Adapter { diff --git a/src/store/wallet/actions.ts b/src/store/wallet/actions.ts index 688abe686..dfd89fa22 100644 --- a/src/store/wallet/actions.ts +++ b/src/store/wallet/actions.ts @@ -54,10 +54,15 @@ export const fetchTonConnectWallets = createAsyncThunk<Adapter[]>( 'wallet/fetchTonConnectWallets', async () => { const supportedWallets = await getTonConnectWallets() return supportedWallets.map((w) => { + //@ts-ignore + let _type = w.jsBridgeKey + if(w.name.toLowerCase() === Adapters.TONSAFE) { + _type = Adapters.TONSAFE + } + return { name: w.name, - //@ts-ignore - type: w.jsBridgeKey, + type: _type, icon: w.imageUrl, mobileCompatible: defineIsMobileCompatible(w.name), description: defineWalletDescription(w.name), diff --git a/src/store/wallet/hooks.ts b/src/store/wallet/hooks.ts index 79adad602..9467f8a76 100644 --- a/src/store/wallet/hooks.ts +++ b/src/store/wallet/hooks.ts @@ -7,6 +7,8 @@ import { awaitWalletReadiness, resetWallet, setAdapter, setConnecting, setTonHub import { connect, disconnectTC, Wallet } from 'services/wallets/adapters/TonConnectAdapter' import { Address } from 'ton' +const findAdapter = (wallets: any, adapterId: Adapters): Adapter | null => wallets?.find((wallet: Adapter) => wallet.type === adapterId) || null + export const useWalletStore = () => { return useSelector((state: RootState) => state.wallet) } @@ -22,10 +24,10 @@ export const useWalletSelect = () => { const wallets = useSelector((state: RootState) => state.wallet.allWallets) const selectWallet = async (adapterId: Adapters, supportsTonConnect?: boolean) => { + debugger resetWallet() disconnectTC() - - const adapter: Adapter | null = wallets?.find((wallet) => wallet.type === adapterId) || null + const adapter: Adapter | null = findAdapter(wallets, adapterId) if (!adapter) { return } @@ -46,7 +48,6 @@ export const useWalletSelect = () => { return } if (adapterId === Adapters.MYTONWALLET && (window as any).myTonWallet.isMyTonWallet) { - debugger const wallet = await connect(adapter.walletInfo, { onSessionLinkReady: (val: string) => { setLocalSession(val) @@ -60,7 +61,7 @@ export const useWalletSelect = () => { setLocalSession(val) }, }) - dispatch(updateWallet({ wallet, adapterId: Adapters.TON_KEEPER })) + dispatch(updateWallet({ wallet, adapterId: adapterId })) } return { selectWallet, session: localSession } } @@ -78,7 +79,7 @@ export const useWalletActions = (): { }, [dispatch]) const restoreAdapter = (adapterId: string) => { - const adapter: Adapter | null = wallets?.find((wallet) => wallet.type === adapterId) || null + const adapter: Adapter | null = findAdapter(wallets, adapterId as Adapters) if (!adapter) { return } @@ -102,7 +103,7 @@ export const useWalletActions = (): { ) return } -debugger + if (tcBridgeConnection.jsBridgeKey === Adapters.OPENMASK) { const accounts = await window.ton?.send('ton_requestAccounts') dispatch(updateWallet({ wallet: { address: accounts[0] }, adapterId: Adapters.OPENMASK })) @@ -116,7 +117,8 @@ debugger } if (tcBridgeConnection?.session && tcBridgeGateway?.length) { const wallet: Wallet = { address: Address.parse(tcBridgeConnection.connectEvent.payload.items[0].address).toFriendly() } - dispatch(updateWallet({ wallet, adapterId: Adapters.TON_KEEPER })) + const _adapter = tcBridgeConnection.connectEvent.payload.device.appName.toLowerCase() + dispatch(updateWallet({ wallet, adapterId: _adapter })) return } dispatch(setConnecting(false)) diff --git a/src/store/wallet/reducer.ts b/src/store/wallet/reducer.ts index 6af5c8b73..1514dc017 100644 --- a/src/store/wallet/reducer.ts +++ b/src/store/wallet/reducer.ts @@ -1,5 +1,5 @@ import { createReducer } from "@reduxjs/toolkit"; -import { Adapter, Wallet } from 'services/wallets/types' +import { Adapter, Adapters, Wallet } from 'services/wallets/types' import { awaitWalletReadiness, fetchTonConnectWallets, @@ -83,7 +83,7 @@ const reducer = createReducer(initialState, (builder) => { const _allWallets = [...adapters, ...action.payload] state.allWallets = _allWallets - state.mobileWallets = _allWallets.filter((wallet) => wallet.mobileCompatible) + state.mobileWallets = _allWallets.filter((wallet) => wallet.mobileCompatible || wallet.name.toLowerCase() === Adapters.TONSAFE) }) .addCase(setAdapter, (state, action) => { state.adapter = action.payload From 37511e6f687fa277633ec96704ef370338ff1523 Mon Sep 17 00:00:00 2001 From: antongotech <105588893+antongotech@users.noreply.github.com> Date: Sun, 19 Feb 2023 11:41:26 +0200 Subject: [PATCH 25/28] bugfix --- src/consts/index.ts | 2 +- src/store/wallet/actions.ts | 2 +- src/store/wallet/hooks.ts | 3 +-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/consts/index.ts b/src/consts/index.ts index 7245338bd..a4500ab81 100644 --- a/src/consts/index.ts +++ b/src/consts/index.ts @@ -3,7 +3,7 @@ import { getParamsFromUrl } from "utils"; const LAYOUT_MAX_WIDTH = "1200px"; const TELEGRAM_WEBAPP_PARAM = "tg"; const APP_NAME = "TonSwap"; -const APP_VERSION = '1.0.13' +const APP_VERSION = '1.0.14' const TEST_MODE = "test-mode"; const TON_WALLET_EXTENSION_URL = "https://chrome.google.com/webstore/detail/ton-wallet/nphplpgoakhhjchkkhmiggakijnkhfnd"; diff --git a/src/store/wallet/actions.ts b/src/store/wallet/actions.ts index dfd89fa22..3dca31958 100644 --- a/src/store/wallet/actions.ts +++ b/src/store/wallet/actions.ts @@ -32,7 +32,7 @@ export const awaitWalletReadiness = createAsyncThunk<{ wallet: Wallet; adapterId }) const defineWalletDescription: any = (name: string) => { - if (name === 'OpenMask') { + if (name === 'OpenMask' || name === 'MyTonWallet') { return 'TON Wallet Plugin for Google Chrome' } return 'A mobile wallet in your pocket' diff --git a/src/store/wallet/hooks.ts b/src/store/wallet/hooks.ts index 9467f8a76..e9c6645b5 100644 --- a/src/store/wallet/hooks.ts +++ b/src/store/wallet/hooks.ts @@ -24,7 +24,6 @@ export const useWalletSelect = () => { const wallets = useSelector((state: RootState) => state.wallet.allWallets) const selectWallet = async (adapterId: Adapters, supportsTonConnect?: boolean) => { - debugger resetWallet() disconnectTC() const adapter: Adapter | null = findAdapter(wallets, adapterId) @@ -111,7 +110,7 @@ export const useWalletActions = (): { } if (tcBridgeConnection.jsBridgeKey === Adapters.MYTONWALLET) { //@ts-ignore - const accounts = await window.myTonWallet.send('requestAccounts') + const accounts = await window.myTonWallet.send('ton_requestAccounts') dispatch(updateWallet({ wallet: { address: accounts[0] }, adapterId: Adapters.MYTONWALLET })) return } From 50278e93f201a901f45280f0f6285fe9cc7ef4c1 Mon Sep 17 00:00:00 2001 From: antongotech <105588893+antongotech@users.noreply.github.com> Date: Sun, 19 Feb 2023 13:38:06 +0200 Subject: [PATCH 26/28] tonsafe mobile disabled --- src/components/SelectWallet/AdaptersList.tsx | 35 ++++++++++++++------ 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/src/components/SelectWallet/AdaptersList.tsx b/src/components/SelectWallet/AdaptersList.tsx index 74c4f9f74..ba1003701 100644 --- a/src/components/SelectWallet/AdaptersList.tsx +++ b/src/components/SelectWallet/AdaptersList.tsx @@ -1,5 +1,5 @@ import { styled } from '@mui/styles' -import { Box, Button, Fade, List, ListItem, ListItemButton, Typography } from '@mui/material' +import { Box, Fade, List, ListItem, ListItemButton, Typography } from '@mui/material' import Title from './Title' import { Theme } from '@mui/material/styles' import { Adapter, Adapters } from 'services/wallets/types' @@ -7,7 +7,6 @@ import CircularProgress from '@mui/material/CircularProgress' import gaAnalytics from 'services/analytics/ga/ga' import { useTranslation } from 'react-i18next' import { isMobile } from 'react-device-detect' -import { useWalletSelect } from 'store/wallet/hooks' const StyledList = styled(List)({ width: '100%', @@ -96,6 +95,23 @@ function AdaptersList({ return null } + const onAdapterSelect = (type: Adapters, tonConnect?: boolean) => { + //@ts-ignore + if (type === Adapters.OPENMASK && !window.ton.isOpenMask) { + window.open('https://www.openmask.app/', '_blank') + return + } + //@ts-ignore + if(type === Adapters.MYTONWALLET && !window.myTonWallet.isMyTonWallet) { + window.open('https://mytonwallet.io/', '_blank') + return + } + if (type === Adapters.TONSAFE && isMobile) { + return + } + onSelect(type, tonConnect) + } + return ( <StyledContainer> <Fade in={!isLoading}> @@ -111,28 +127,25 @@ function AdaptersList({ <ListItem disablePadding key={name} + disabled={adapter.name.toLowerCase() === Adapters.TONSAFE && isMobile} style={{ pointerEvents: isLoading ? 'none' : 'all' }} sx={{ width: '100%', background: 'white', borderRadius: '12px', - border: title === 'Select Wallet' ? '': '1px solid #007AFE' + border: title === 'Select Wallet' ? '' : '1px solid #007AFE', }} > <StyledListItemButton - onClick={() => { - //@ts-ignore - type === Adapters.OPENMASK && window.ton && !window.ton.isOpenMask - ? window.open('https://www.openmask.app/', '_blank') - : onSelect(type, tonConnect) - }} + onClick={() => onAdapterSelect(type, tonConnect)} > <StyledIcon> <img style={{ 'borderRadius': '9px' }} src={icon} /> </StyledIcon> <StyledListItemRight> - <Typography variant="h5" sx={{color: title === 'Select Wallet' ? '':'#007AFE !important'}}> - {title === 'Tap to connect' && 'Connect'} {name} <small>{disabled ? t('coming-soon') : ''}</small> + <Typography variant="h5" sx={{ color: title === 'Select Wallet' ? '' : '#007AFE !important' }}> + {title === 'Tap to connect' && 'Connect'} {name} {' '} + <small>{disabled || adapter.name.toLowerCase() === Adapters.TONSAFE && isMobile ? t('coming-soon') : ''}</small> </Typography> {title !== 'Tap to connect' && <Typography>{description}</Typography>} </StyledListItemRight> From 32511f80864d13984a3ae762b563a86da8316c85 Mon Sep 17 00:00:00 2001 From: antongotech <105588893+antongotech@users.noreply.github.com> Date: Mon, 20 Feb 2023 12:12:12 +0200 Subject: [PATCH 27/28] tonsafe disabled, pending transaction popup added --- src/components/Popup.tsx | 9 +- src/components/SelectWallet/AdaptersList.tsx | 12 +- src/consts/index.ts | 2 +- .../TokenOperations/SuccessModal.tsx | 143 +++++++++++------- src/store/wallet/actions.ts | 1 + src/store/wallet/reducer.ts | 3 +- 6 files changed, 107 insertions(+), 63 deletions(-) diff --git a/src/components/Popup.tsx b/src/components/Popup.tsx index 609828a4a..b7129d306 100644 --- a/src/components/Popup.tsx +++ b/src/components/Popup.tsx @@ -34,7 +34,6 @@ export function Popup({ minWidth }: Props) { const expanded = useIsExpandedView(); - return isMobile ? ( <SwipeableDrawer anchor="bottom" @@ -52,9 +51,9 @@ export function Popup({ }} > <StyledDrawer expanded={expanded}> - <StyledClose onClick={onClose} style={{ top: 14, right: 14 }}> + {typeof onClose !== 'undefined' && <StyledClose onClick={onClose} style={{ top: 14, right: 14 }}> <img src={CloseImg} /> - </StyledClose> + </StyledClose>} {children} </StyledDrawer> </SwipeableDrawer> @@ -82,9 +81,9 @@ export function Popup({ }} > <StyledDialogContent maxWidth={maxWidth} minWidth={minWidth}> - <StyledClose onClick={onClose} style={{ top: -50, right: -40 }}> + {typeof onClose !== 'undefined' &&<StyledClose onClick={onClose} style={{ top: -50, right: -40 }}> <img src={CloseImg} /> - </StyledClose> + </StyledClose>} <StyledChildren>{children}</StyledChildren> </StyledDialogContent> </Dialog> diff --git a/src/components/SelectWallet/AdaptersList.tsx b/src/components/SelectWallet/AdaptersList.tsx index ba1003701..ddfd009ee 100644 --- a/src/components/SelectWallet/AdaptersList.tsx +++ b/src/components/SelectWallet/AdaptersList.tsx @@ -84,7 +84,8 @@ function AdaptersList({ title = 'Select Wallet', }: Props) { const { t } = useTranslation() - const adaptersToShow = isMobile ? adapters.filter((adapter) => adapter.mobileCompatible || adapter.name.toLowerCase() === Adapters.TONSAFE) : adapters + // const adaptersToShow = isMobile ? adapters.filter((adapter) => adapter.mobileCompatible || adapter.name.toLowerCase() === Adapters.TONSAFE) : adapters + const adaptersToShow = isMobile ? adapters.filter((adapter) => adapter.mobileCompatible) : adapters const onSelect = (adapter: Adapters, supportsTonConnect?: boolean) => { select(adapter, supportsTonConnect) @@ -106,7 +107,8 @@ function AdaptersList({ window.open('https://mytonwallet.io/', '_blank') return } - if (type === Adapters.TONSAFE && isMobile) { + // if (type === Adapters.TONSAFE && isMobile) { + if (type === Adapters.TONSAFE) { return } onSelect(type, tonConnect) @@ -123,11 +125,12 @@ function AdaptersList({ <StyledList> {adaptersToShow.map((adapter) => { const { type, icon, name, description, disabled, tonConnect } = adapter + {/* disabled={adapter.name.toLowerCase() === Adapters.TONSAFE && isMobile} */} return ( <ListItem disablePadding key={name} - disabled={adapter.name.toLowerCase() === Adapters.TONSAFE && isMobile} + disabled={adapter.name.toLowerCase() === Adapters.TONSAFE} style={{ pointerEvents: isLoading ? 'none' : 'all' }} sx={{ width: '100%', @@ -145,7 +148,8 @@ function AdaptersList({ <StyledListItemRight> <Typography variant="h5" sx={{ color: title === 'Select Wallet' ? '' : '#007AFE !important' }}> {title === 'Tap to connect' && 'Connect'} {name} {' '} - <small>{disabled || adapter.name.toLowerCase() === Adapters.TONSAFE && isMobile ? t('coming-soon') : ''}</small> + {/* <small>{disabled || adapter.name.toLowerCase() === Adapters.TONSAFE && isMobile ? t('coming-soon') : ''}</small>*/} + <small>{disabled ? t('coming-soon') : ''}</small> </Typography> {title !== 'Tap to connect' && <Typography>{description}</Typography>} </StyledListItemRight> diff --git a/src/consts/index.ts b/src/consts/index.ts index a4500ab81..214758c92 100644 --- a/src/consts/index.ts +++ b/src/consts/index.ts @@ -3,7 +3,7 @@ import { getParamsFromUrl } from "utils"; const LAYOUT_MAX_WIDTH = "1200px"; const TELEGRAM_WEBAPP_PARAM = "tg"; const APP_NAME = "TonSwap"; -const APP_VERSION = '1.0.14' +const APP_VERSION = '1.0.15' const TEST_MODE = "test-mode"; const TON_WALLET_EXTENSION_URL = "https://chrome.google.com/webstore/detail/ton-wallet/nphplpgoakhhjchkkhmiggakijnkhfnd"; diff --git a/src/screens/components/TokenOperations/SuccessModal.tsx b/src/screens/components/TokenOperations/SuccessModal.tsx index 5c697b009..649938069 100644 --- a/src/screens/components/TokenOperations/SuccessModal.tsx +++ b/src/screens/components/TokenOperations/SuccessModal.tsx @@ -1,23 +1,26 @@ -import { Typography } from "@mui/material"; -import { Box, styled } from "@mui/system"; -import { Popup } from "components"; -import { ReactNode, useCallback } from "react"; -import { ActionType } from "services/wallets/types"; +import { Typography } from '@mui/material' +import { Box, styled } from '@mui/system' +import { Popup } from 'components' +import { ReactNode, useCallback, useEffect, useState } from 'react' +import { ActionType } from 'services/wallets/types' import { useTokenOperationsActions, useTokenOperationsStore, -} from "store/token-operations/hooks"; -import SuccessIcon from "assets/images/shared/success.svg"; -import BigNumberDisplay from "components/BigNumberDisplay"; -import { useTranslation } from "react-i18next"; +} from 'store/token-operations/hooks' +import SuccessIcon from 'assets/images/shared/success.svg' +import BigNumberDisplay from 'components/BigNumberDisplay' +import { useTranslation } from 'react-i18next' +import CircularProgress from '@mui/material/CircularProgress' + interface Props { actionType: ActionType; } function SuccessModal({ actionType }: Props) { - const { txConfirmation, txSuccess } = useTokenOperationsStore(); - const { closeSuccessModal } = useTokenOperationsActions(); + const { txConfirmation, txSuccess } = useTokenOperationsStore() + const { closeSuccessModal } = useTokenOperationsActions() const { t } = useTranslation() + const [allowActions, setAllowActions] = useState(false) const createComponent = useCallback(() => { switch (actionType) { @@ -37,14 +40,14 @@ function SuccessModal({ actionType }: Props) { </Typography> </Box> </Container> - ); + ) case ActionType.SELL: return ( <Container title={t('sell-confirmation')}> <Box className="row"> <Typography>{t('token-sold', { token: txConfirmation.tokenName })}</Typography> <Typography> - <BigNumberDisplay value={txConfirmation.srcTokenAmount} />{" "} + <BigNumberDisplay value={txConfirmation.srcTokenAmount} />{' '} </Typography> </Box> <Box className="row"> @@ -54,7 +57,7 @@ function SuccessModal({ actionType }: Props) { </Typography> </Box> </Container> - ); + ) case ActionType.REMOVE_LIQUIDITY: return ( <Container title={t('liquidity-removed')}> @@ -74,7 +77,7 @@ function SuccessModal({ actionType }: Props) { </Typography> </Box> </Container> - ); + ) case ActionType.ADD_LIQUIDITY: return ( <Container title={t('liquidity-added')}> @@ -94,86 +97,122 @@ function SuccessModal({ actionType }: Props) { </Typography> </Box> </Container> - ); + ) default: - return <></>; + return <></> } - }, [actionType, txConfirmation]); + }, [actionType, txConfirmation]) return ( - <Popup open={txSuccess} onClose={closeSuccessModal} maxWidth={400}> - {createComponent()} + <Popup open={txSuccess} onClose={allowActions ? closeSuccessModal : undefined} maxWidth={400}> + <DelayedRender setAllowActions={setAllowActions} waitBeforeShow={45000}> + {createComponent()} + </DelayedRender> </Popup> - ); + ) } -export default SuccessModal; +export default SuccessModal interface ContainerProps { children: ReactNode; title: string; + showIcon?: boolean } -function Container({ children, title }: ContainerProps) { +function Container({ children, title, showIcon = true }: ContainerProps) { return ( <StyledContainer> <StyledHeader> - <img src={SuccessIcon} className="icon" /> + {showIcon && <img src={SuccessIcon} className="icon" />} <Typography>{title}</Typography> </StyledHeader> <StyledChildren>{children}</StyledChildren> </StyledContainer> - ); + ) } const StyledHeader = styled(Box)({ - display: "flex", - alignItems: "center", + display: 'flex', + alignItems: 'center', gap: 22, - flexDirection: "column", - "& p": { + flexDirection: 'column', + '& p': { fontSize: 19, fontWeight: 500, }, - "& .icon": { + '& .icon': { width: 45, height: 45, - objectFit: "contain", + objectFit: 'contain', }, -}); +}) const StyledChildren = styled(Box)({ - display: "flex", - flexDirection: "column", + display: 'flex', + flexDirection: 'column', gap: 13, - width: "100%", -}); + width: '100%', +}) const StyledContainer = styled(Box)({ - display: "flex", - flexDirection: "column", + display: 'flex', + flexDirection: 'column', gap: 19, - alignItems: "center", - width: "100%", + alignItems: 'center', + width: '100%', - "& *": { - color: "black", + '& *': { + color: 'black', }, - - "& .row": { - display: "flex", + '& .row': { + display: 'flex', gap: 40, - background: "#EEEEEE", + background: '#EEEEEE', borderRadius: 12, minHeight: 49, - width: "100%", - alignItems: "center", - justifyContent: "space-between", - padding: "10px 20px", - "& p": { + width: '100%', + alignItems: 'center', + justifyContent: 'space-between', + padding: '10px 20px', + '& p': { fontSize: 14, }, }, -}); +}) + +interface IDelayedRender { + children: ReactNode; + waitBeforeShow?: number; + setAllowActions: any; +} + +const DelayedContainer = styled(Container)({ + minWidth: 260, minHeight: 285, display: 'flex', flexDirection: 'column' +}) + +export const DelayedRender = ({ children, waitBeforeShow = 7000, setAllowActions }: IDelayedRender) => { + const [isShown, setIsShown] = useState(false) + + useEffect(() => { + const timer = setTimeout(() => { + setIsShown(true) + setAllowActions(true) + }, waitBeforeShow) + return () => clearTimeout(timer) + }, [waitBeforeShow]) + + return <Box> + {isShown ? children + : <DelayedContainer showIcon={false} title='Please wait'> + <Typography sx={{width: '100%', marginBottom: 3, textAlign: 'center'}}> + Transaction is being processed + </Typography> + <Box sx={{ height: '100%', display: 'flex', alignItems: 'center', justifyContent: 'center' }}> + <CircularProgress /> + </Box> + </DelayedContainer>} + </Box> +} diff --git a/src/store/wallet/actions.ts b/src/store/wallet/actions.ts index 3dca31958..a14b1188a 100644 --- a/src/store/wallet/actions.ts +++ b/src/store/wallet/actions.ts @@ -68,6 +68,7 @@ export const fetchTonConnectWallets = createAsyncThunk<Adapter[]>( description: defineWalletDescription(w.name), tonConnect: true, walletInfo: w, + disabled: _type === Adapters.TONSAFE } }) }, diff --git a/src/store/wallet/reducer.ts b/src/store/wallet/reducer.ts index 1514dc017..5cba21085 100644 --- a/src/store/wallet/reducer.ts +++ b/src/store/wallet/reducer.ts @@ -83,7 +83,8 @@ const reducer = createReducer(initialState, (builder) => { const _allWallets = [...adapters, ...action.payload] state.allWallets = _allWallets - state.mobileWallets = _allWallets.filter((wallet) => wallet.mobileCompatible || wallet.name.toLowerCase() === Adapters.TONSAFE) + // state.mobileWallets = _allWallets.filter((wallet) => wallet.mobileCompatible || wallet.name.toLowerCase() === Adapters.TONSAFE) + state.mobileWallets = _allWallets.filter((wallet) => wallet.mobileCompatible) }) .addCase(setAdapter, (state, action) => { state.adapter = action.payload From b7d1cf102a17910f5a28bdee439d74bc883f8b01 Mon Sep 17 00:00:00 2001 From: antongotech <105588893+antongotech@users.noreply.github.com> Date: Mon, 20 Feb 2023 12:15:56 +0200 Subject: [PATCH 28/28] delay fixed --- src/screens/components/TokenOperations/SuccessModal.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/screens/components/TokenOperations/SuccessModal.tsx b/src/screens/components/TokenOperations/SuccessModal.tsx index 649938069..b570da930 100644 --- a/src/screens/components/TokenOperations/SuccessModal.tsx +++ b/src/screens/components/TokenOperations/SuccessModal.tsx @@ -106,7 +106,7 @@ function SuccessModal({ actionType }: Props) { return ( <Popup open={txSuccess} onClose={allowActions ? closeSuccessModal : undefined} maxWidth={400}> - <DelayedRender setAllowActions={setAllowActions} waitBeforeShow={45000}> + <DelayedRender setAllowActions={setAllowActions} waitBeforeShow={13000}> {createComponent()} </DelayedRender> </Popup>