From 3926880c257dc7e9d5674452883f8f2e573b9e4d Mon Sep 17 00:00:00 2001 From: Kacper Szarkiewicz Date: Mon, 10 Jun 2024 15:12:05 +0200 Subject: [PATCH 01/50] rename 'Connect to Wallet' to 'Connect Wallet' --- src/components/Wallet/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Wallet/index.tsx b/src/components/Wallet/index.tsx index aa0180ee..69e38258 100644 --- a/src/components/Wallet/index.tsx +++ b/src/components/Wallet/index.tsx @@ -74,7 +74,7 @@ const OpenWallet = (props: Props): JSX.Element => { color="primary" type="button" > - Connect to Wallet + Connect Wallet } onAccountSelected={setWalletAccount} From 46fe5a4fca19d30461b83fc05ef2a6390feea2f6 Mon Sep 17 00:00:00 2001 From: Kacper Szarkiewicz Date: Mon, 10 Jun 2024 21:14:11 +0200 Subject: [PATCH 02/50] replace deprecated hero icon --- src/components/Wallet/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/Wallet/index.tsx b/src/components/Wallet/index.tsx index 69e38258..aab96015 100644 --- a/src/components/Wallet/index.tsx +++ b/src/components/Wallet/index.tsx @@ -1,4 +1,4 @@ -import { ArrowLeftOnRectangleIcon } from '@heroicons/react/20/solid'; +import { ArrowLeftEndOnRectangleIcon } from '@heroicons/react/20/solid'; import { WalletSelect } from '@talismn/connect-components'; import { Button, Divider, Dropdown } from 'react-daisyui'; import { isMobile } from 'react-device-detect'; @@ -56,7 +56,7 @@ const OpenWallet = (props: Props): JSX.Element => { {balance} {tokenSymbol}

From 29612bd77112a20abbd795346f51923aaf0f4f17 Mon Sep 17 00:00:00 2001 From: Kacper Szarkiewicz Date: Mon, 10 Jun 2024 22:00:29 +0200 Subject: [PATCH 03/50] Divide WalletConnect button to separate components --- src/components/Wallet/Connect/index.tsx | 49 ++++++++++ src/components/Wallet/Disconnect/index.tsx | 105 +++++++++++++++++++++ src/components/Wallet/index.tsx | 101 ++------------------ 3 files changed, 160 insertions(+), 95 deletions(-) create mode 100644 src/components/Wallet/Connect/index.tsx create mode 100644 src/components/Wallet/Disconnect/index.tsx diff --git a/src/components/Wallet/Connect/index.tsx b/src/components/Wallet/Connect/index.tsx new file mode 100644 index 00000000..16005a88 --- /dev/null +++ b/src/components/Wallet/Connect/index.tsx @@ -0,0 +1,49 @@ +import { WalletSelect } from '@talismn/connect-components'; +import { Button, Divider } from 'react-daisyui'; +import { isMobile } from 'react-device-detect'; +import { useGlobalState } from '../../../GlobalStateProvider'; +import MetamaskWallet from '../MetamaskWallet'; +import NovaWallet from '../NovaWallet'; +import WalletConnect from '../WalletConnect'; + +export interface ConnectProps { + isHeader?: boolean; +} + +export const Connect = ({ isHeader }: ConnectProps) => { + const { dAppName, setWalletAccount } = useGlobalState(); + + return ( + <> + + Connect Wallet + + } + onAccountSelected={setWalletAccount} + footer={ + <> + {isMobile && ( + <> + + + + )} + + + + + } + /> + + ); +}; diff --git a/src/components/Wallet/Disconnect/index.tsx b/src/components/Wallet/Disconnect/index.tsx new file mode 100644 index 00000000..2dc4fcd2 --- /dev/null +++ b/src/components/Wallet/Disconnect/index.tsx @@ -0,0 +1,105 @@ +import { Wallet, WalletAccount } from '@talismn/connect-wallets'; +import { UseQueryResult } from '@tanstack/react-query'; +import { FrameSystemAccountInfo } from '@polkadot/types/lookup'; + +import { ArrowLeftEndOnRectangleIcon } from '@heroicons/react/20/solid'; +import { Button, Dropdown } from 'react-daisyui'; +import { getAddressForFormat } from '../../../helpers/addressFormatter'; +import { useNodeInfoState } from '../../../NodeInfoProvider'; +import { useAccountBalance } from '../../../shared/useAccountBalance'; +import { useGlobalState } from '../../../GlobalStateProvider'; +import { CopyableAddress } from '../../PublicKey'; +import { Skeleton } from '../../Skeleton'; + +interface WalletButtonProps { + wallet?: Wallet; + query: UseQueryResult; + balance?: string; + tokenSymbol?: string; + walletAccount?: WalletAccount; +} + +const WalletButton = ({ wallet, query, balance, tokenSymbol, walletAccount }: WalletButtonProps) => ( + +); + +interface WalletDropdownMenuProps { + address: string; + balance?: string; + tokenSymbol?: string; + walletAccount?: WalletAccount; + ss58Format?: number; + removeWalletAccount: () => void; +} + +const WalletDropdownMenu = ({ + walletAccount, + ss58Format, + address, + balance, + tokenSymbol, + removeWalletAccount, +}: WalletDropdownMenuProps) => ( + +
{walletAccount?.name}
+
+ +
+

+ {balance} {tokenSymbol} +

+ +
+); + +export const Disconnect = () => { + const { walletAccount, removeWalletAccount } = useGlobalState(); + const { query, balance } = useAccountBalance(); + const { ss58Format, tokenSymbol } = useNodeInfoState().state; + const { wallet, address } = walletAccount || {}; + + if (!address) return <>; + + return ( + + + + + ); +}; diff --git a/src/components/Wallet/index.tsx b/src/components/Wallet/index.tsx index aab96015..0070a1a0 100644 --- a/src/components/Wallet/index.tsx +++ b/src/components/Wallet/index.tsx @@ -1,101 +1,12 @@ -import { ArrowLeftEndOnRectangleIcon } from '@heroicons/react/20/solid'; -import { WalletSelect } from '@talismn/connect-components'; -import { Button, Divider, Dropdown } from 'react-daisyui'; -import { isMobile } from 'react-device-detect'; +import { Disconnect } from './Disconnect'; import { useGlobalState } from '../../GlobalStateProvider'; -import { useNodeInfoState } from '../../NodeInfoProvider'; -import { getAddressForFormat } from '../../helpers/addressFormatter'; -import { useAccountBalance } from '../../shared/useAccountBalance'; -import { CopyableAddress } from '../PublicKey'; -import { Skeleton } from '../Skeleton'; -import MetamaskWallet from './MetamaskWallet'; -import NovaWallet from './NovaWallet'; -import WalletConnect from './WalletConnect'; +import { Connect, ConnectProps } from './Connect'; -interface Props { - isHeader?: boolean; -} +const OpenWallet = (props: ConnectProps): JSX.Element => { + const { walletAccount } = useGlobalState(); + const { address } = walletAccount || {}; -const OpenWallet = (props: Props): JSX.Element => { - const { walletAccount, dAppName, setWalletAccount, removeWalletAccount } = useGlobalState(); - const { wallet, address } = walletAccount || {}; - const { query, balance } = useAccountBalance(); - const { ss58Format, tokenSymbol } = useNodeInfoState().state; - - return ( - <> - {address ? ( - - - -
{walletAccount?.name}
-
- -
-

- {balance} {tokenSymbol} -

- -
-
- ) : ( - <> - - Connect Wallet - - } - onAccountSelected={setWalletAccount} - footer={ - <> - {isMobile && ( - <> - - - - )} - - - - - } - /> - - )} - - ); + return address ? : ; }; export default OpenWallet; From 9f1ff79a0fc9dde34dcbf80d892e22e519d036e9 Mon Sep 17 00:00:00 2001 From: Kacper Szarkiewicz Date: Fri, 14 Jun 2024 14:29:53 +0200 Subject: [PATCH 04/50] change prettier and tailwindcss config filenames --- .prettierrc | 13 +++++++++++++ prettier.config.cjs | 11 ----------- tailwind.config.cjs => tailwind.config.js | 0 3 files changed, 13 insertions(+), 11 deletions(-) create mode 100644 .prettierrc delete mode 100644 prettier.config.cjs rename tailwind.config.cjs => tailwind.config.js (100%) diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 00000000..cf6d73d7 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,13 @@ +{ + "arrowParens": "always", + "printWidth": 120, + "tabWidth": 2, + "useTabs": false, + "semi": true, + "singleQuote": true, + "trailingComma": "all", + "bracketSpacing": true, + "proseWrap": "always", + "plugins": ["prettier-plugin-tailwindcss"], + "tailwindConfig": "./tailwind.config.js" +} diff --git a/prettier.config.cjs b/prettier.config.cjs deleted file mode 100644 index 4d1a33ff..00000000 --- a/prettier.config.cjs +++ /dev/null @@ -1,11 +0,0 @@ -module.exports = { - arrowParens: 'always', - printWidth: 120, - tabWidth: 2, - useTabs: false, - semi: true, - singleQuote: true, - trailingComma: 'all', - bracketSpacing: true, - proseWrap: 'always', -}; diff --git a/tailwind.config.cjs b/tailwind.config.js similarity index 100% rename from tailwind.config.cjs rename to tailwind.config.js From e713e5c5046a8e458e4372d4f41232ddf190539d Mon Sep 17 00:00:00 2001 From: Kacper Szarkiewicz Date: Fri, 14 Jun 2024 14:30:13 +0200 Subject: [PATCH 05/50] add prettier plugin for tailwindcss --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 2d641a48..f1d25613 100644 --- a/package.json +++ b/package.json @@ -114,6 +114,7 @@ "postcss": "^8.4.21", "postcss-import": "^15.1.0", "prettier": "^3.1.1", + "prettier-plugin-tailwindcss": "^0.6.4", "react-error-overlay": "6.0.9", "react-table": "^7.8.0", "sass": "^1.58.3", From b8387a814a23f4b7b8e62523aa1d48cd02439cad Mon Sep 17 00:00:00 2001 From: Kacper Szarkiewicz Date: Fri, 14 Jun 2024 14:31:37 +0200 Subject: [PATCH 06/50] restructure ConnectWallet file structure --- src/components/Wallet/Connect/index.tsx | 49 -------- src/components/Wallet/Disconnect/index.tsx | 105 ---------------- .../Wallet/MetamaskWallet/index.tsx | 79 ------------ src/components/Wallet/NovaWallet/index.tsx | 114 ------------------ src/components/Wallet/WalletConnect/index.tsx | 93 -------------- src/components/Wallet/index.tsx | 6 +- yarn.lock | 56 +++++++++ 7 files changed, 59 insertions(+), 443 deletions(-) delete mode 100644 src/components/Wallet/Connect/index.tsx delete mode 100644 src/components/Wallet/Disconnect/index.tsx delete mode 100644 src/components/Wallet/MetamaskWallet/index.tsx delete mode 100644 src/components/Wallet/NovaWallet/index.tsx delete mode 100644 src/components/Wallet/WalletConnect/index.tsx diff --git a/src/components/Wallet/Connect/index.tsx b/src/components/Wallet/Connect/index.tsx deleted file mode 100644 index 16005a88..00000000 --- a/src/components/Wallet/Connect/index.tsx +++ /dev/null @@ -1,49 +0,0 @@ -import { WalletSelect } from '@talismn/connect-components'; -import { Button, Divider } from 'react-daisyui'; -import { isMobile } from 'react-device-detect'; -import { useGlobalState } from '../../../GlobalStateProvider'; -import MetamaskWallet from '../MetamaskWallet'; -import NovaWallet from '../NovaWallet'; -import WalletConnect from '../WalletConnect'; - -export interface ConnectProps { - isHeader?: boolean; -} - -export const Connect = ({ isHeader }: ConnectProps) => { - const { dAppName, setWalletAccount } = useGlobalState(); - - return ( - <> - - Connect Wallet - - } - onAccountSelected={setWalletAccount} - footer={ - <> - {isMobile && ( - <> - - - - )} - - - - - } - /> - - ); -}; diff --git a/src/components/Wallet/Disconnect/index.tsx b/src/components/Wallet/Disconnect/index.tsx deleted file mode 100644 index 2dc4fcd2..00000000 --- a/src/components/Wallet/Disconnect/index.tsx +++ /dev/null @@ -1,105 +0,0 @@ -import { Wallet, WalletAccount } from '@talismn/connect-wallets'; -import { UseQueryResult } from '@tanstack/react-query'; -import { FrameSystemAccountInfo } from '@polkadot/types/lookup'; - -import { ArrowLeftEndOnRectangleIcon } from '@heroicons/react/20/solid'; -import { Button, Dropdown } from 'react-daisyui'; -import { getAddressForFormat } from '../../../helpers/addressFormatter'; -import { useNodeInfoState } from '../../../NodeInfoProvider'; -import { useAccountBalance } from '../../../shared/useAccountBalance'; -import { useGlobalState } from '../../../GlobalStateProvider'; -import { CopyableAddress } from '../../PublicKey'; -import { Skeleton } from '../../Skeleton'; - -interface WalletButtonProps { - wallet?: Wallet; - query: UseQueryResult; - balance?: string; - tokenSymbol?: string; - walletAccount?: WalletAccount; -} - -const WalletButton = ({ wallet, query, balance, tokenSymbol, walletAccount }: WalletButtonProps) => ( - -); - -interface WalletDropdownMenuProps { - address: string; - balance?: string; - tokenSymbol?: string; - walletAccount?: WalletAccount; - ss58Format?: number; - removeWalletAccount: () => void; -} - -const WalletDropdownMenu = ({ - walletAccount, - ss58Format, - address, - balance, - tokenSymbol, - removeWalletAccount, -}: WalletDropdownMenuProps) => ( - -
{walletAccount?.name}
-
- -
-

- {balance} {tokenSymbol} -

- -
-); - -export const Disconnect = () => { - const { walletAccount, removeWalletAccount } = useGlobalState(); - const { query, balance } = useAccountBalance(); - const { ss58Format, tokenSymbol } = useNodeInfoState().state; - const { wallet, address } = walletAccount || {}; - - if (!address) return <>; - - return ( - - - - - ); -}; diff --git a/src/components/Wallet/MetamaskWallet/index.tsx b/src/components/Wallet/MetamaskWallet/index.tsx deleted file mode 100644 index 0edbe865..00000000 --- a/src/components/Wallet/MetamaskWallet/index.tsx +++ /dev/null @@ -1,79 +0,0 @@ -import { useCallback, useEffect, useState } from 'preact/compat'; -import { Modal } from 'react-daisyui'; -import { GlobalState, useGlobalState } from '../../../GlobalStateProvider'; -import logo from '../../../assets/metamask-wallet.png'; -import { - ExtensionAccount, - buildWalletAccount, - initiateMetamaskInjectedAccount, -} from '../../../services/metamask/metamask'; -import { CloseButton } from '../../CloseButton'; -import { PublicKey } from '../../PublicKey'; - -export type MetamaskWalletProps = { - setWalletAccount: GlobalState['setWalletAccount']; -}; - -const MetamaskWallet = ({ setWalletAccount }: MetamaskWalletProps) => { - const [openModal, setOpenModal] = useState(false); - const [accounts, setAccounts] = useState([]); - const [selectedAccount, setSelectedAccount] = useState(); - const { tenantName } = useGlobalState(); - - const onClick = useCallback(async () => { - const injectedMetamaskAccount = (await initiateMetamaskInjectedAccount(tenantName)) as ExtensionAccount; - if (injectedMetamaskAccount) { - setAccounts([injectedMetamaskAccount]); - setOpenModal(true); - } else { - console.error('Something went wrong, snap not found.'); - setOpenModal(false); - } - }, [setOpenModal]); - - useEffect(() => { - if (selectedAccount) { - buildWalletAccount(selectedAccount) - .then((account) => setWalletAccount(account)) - .then(() => { - setOpenModal(false); - }) - .catch((error) => console.error(error)); - } - }, [selectedAccount, setWalletAccount]); - - return ( -
- - setOpenModal(false)} /> -

Metamask Snap Polkadot account:

-
- {accounts.map((a, i) => ( - - ))} -
-
- -
- ); -}; - -export default MetamaskWallet; diff --git a/src/components/Wallet/NovaWallet/index.tsx b/src/components/Wallet/NovaWallet/index.tsx deleted file mode 100644 index 8482aa24..00000000 --- a/src/components/Wallet/NovaWallet/index.tsx +++ /dev/null @@ -1,114 +0,0 @@ -import { web3Accounts, web3Enable, web3FromAddress } from '@polkadot/extension-dapp'; -import { WalletAccount } from '@talismn/connect-wallets'; -import { useCallback, useEffect, useState } from 'preact/compat'; -import { Modal } from 'react-daisyui'; -import logo from '../../../assets/nova-wallet.png'; -import { GlobalState } from '../../../GlobalStateProvider'; -import { PublicKey } from '../../PublicKey'; - -export type NovaWalletProps = { - setWalletAccount: GlobalState['setWalletAccount']; -}; - -interface ExtensionAccount { - address: string; - name: string; - source: string; -} - -const NovaWallet = ({ setWalletAccount }: NovaWalletProps) => { - const [openModal, setOpenModal] = useState(false); - const [accounts, setAccounts] = useState([]); - const [selectedAccount, setSelectedAccount] = useState(); - - const onClick = useCallback(async () => { - const _ = await web3Enable('Pendulum Chain Portal'); - const allAccounts = await web3Accounts(); - setAccounts( - allAccounts - .filter(({ meta: { source } }) => source === 'polkadot-js') - .map( - ({ address, meta }): ExtensionAccount => ({ - address: address, - name: meta.name || 'My Account', - source: meta.source, - }), - ), - ); - setOpenModal(true); - }, [setOpenModal]); - - useEffect(() => { - async function buildWalletAccount(extAcc: ExtensionAccount) { - const signer = await web3FromAddress(extAcc.address); - return { - address: extAcc.address, - source: extAcc.source, - name: extAcc.name, - signer: signer as WalletAccount['signer'], - wallet: { - enable: () => undefined, - extensionName: 'polkadot-js', - title: 'Nova Wallet', - installUrl: 'https://novawallet.io/', - logo: { - src: logo, - alt: 'Nova Wallet', - }, - installed: true, - extension: undefined, - signer, - /** - * The following methods are tagged as 'Unused' since they are only required by the @talisman package, - * which we are not using to handle this wallet connection. - */ - getAccounts: () => Promise.resolve([]), // Unused - subscribeAccounts: () => undefined, // Unused - transformError: (err: Error) => err, // Unused - }, - }; - } - if (selectedAccount) { - buildWalletAccount(selectedAccount) - .then((account) => setWalletAccount(account)) - .then(() => { - setOpenModal(false); - }) - .catch((error) => console.error(error)); - } - }, [selectedAccount, setWalletAccount]); - - return ( -
- -

Select your Nova account

-
- {accounts.map((a, i) => ( - - ))} -
-
- -
- ); -}; - -export default NovaWallet; diff --git a/src/components/Wallet/WalletConnect/index.tsx b/src/components/Wallet/WalletConnect/index.tsx deleted file mode 100644 index b2d6c2cc..00000000 --- a/src/components/Wallet/WalletConnect/index.tsx +++ /dev/null @@ -1,93 +0,0 @@ -import { WalletConnectModal } from '@walletconnect/modal'; -import UniversalProvider from '@walletconnect/universal-provider'; -import { SessionTypes } from '@walletconnect/types'; -import { useCallback, useEffect, useState } from 'preact/compat'; -import logo from '../../../assets/wallet-connect.svg'; -import { config } from '../../../config'; -import { chainIds, walletConnectConfig } from '../../../config/walletConnect'; -import { useGlobalState } from '../../../GlobalStateProvider'; -import { walletConnectService } from '../../../services/walletConnect'; -import { ToastMessage, showToast } from '../../../shared/showToast'; - -const WalletConnect = () => { - const [loading, setLoading] = useState(false); - const [provider, setProvider] = useState | undefined>(); - const [modal, setModal] = useState(); - const { tenantName, setWalletAccount, removeWalletAccount } = useGlobalState(); - - const setupClientDisconnectListener = useCallback( - async (provider: Promise) => { - (await provider).client.on('session_delete', () => { - removeWalletAccount(); - }); - }, - [removeWalletAccount], - ); - - const handleModal = useCallback( - (uri?: string) => { - if (uri) { - modal?.openModal({ uri, onclose: () => setLoading(false) }); - } - }, - [modal], - ); - - const handleSession = useCallback( - async (approval: () => Promise, chainId: string) => { - const session = await approval(); - setWalletAccount(await walletConnectService.init(session, chainId)); - modal?.closeModal(); - }, - [setWalletAccount, modal], - ); - - const handleConnect = useCallback(async () => { - const chainId = chainIds[tenantName]; - if (!provider || !chainId) return; - - const wcProvider = await provider; - const { uri, approval } = await wcProvider.client.connect(walletConnectConfig); - - handleModal(uri); - handleSession(approval, chainId); - await setupClientDisconnectListener(provider); - }, [provider, tenantName, setupClientDisconnectListener, handleModal, handleSession]); - - const walletConnectClick = useCallback(async () => { - setLoading(true); - try { - await handleConnect(); - - //@eslint-disable-next-line no-explicit-any - } catch (error: any) { - showToast(ToastMessage.ERROR, error); - } finally { - setLoading(false); - } - }, [handleConnect]); - - useEffect(() => { - if (provider) return; - setProvider(walletConnectService.getProvider()); - setModal( - new WalletConnectModal({ - projectId: config.walletConnect.projectId, - }), - ); - }, [provider]); - - return ( -
- -
- ); -}; -export default WalletConnect; diff --git a/src/components/Wallet/index.tsx b/src/components/Wallet/index.tsx index 0070a1a0..2c5b07b5 100644 --- a/src/components/Wallet/index.tsx +++ b/src/components/Wallet/index.tsx @@ -1,12 +1,12 @@ -import { Disconnect } from './Disconnect'; +import { DisconnectModal } from './modals/DisconnectModal'; import { useGlobalState } from '../../GlobalStateProvider'; -import { Connect, ConnectProps } from './Connect'; +import { ConnectModal, ConnectProps } from './modals/ConnectModal'; const OpenWallet = (props: ConnectProps): JSX.Element => { const { walletAccount } = useGlobalState(); const { address } = walletAccount || {}; - return address ? : ; + return address ? : ; }; export default OpenWallet; diff --git a/yarn.lock b/yarn.lock index 1bce2fe5..d2999123 100644 --- a/yarn.lock +++ b/yarn.lock @@ -13475,6 +13475,7 @@ __metadata: postcss-import: "npm:^15.1.0" preact: "npm:^10.21.0" prettier: "npm:^3.1.1" + prettier-plugin-tailwindcss: "npm:^0.6.4" qrcode.react: "npm:^3.1.0" react-daisyui: "npm:^5.0.0" react-device-detect: "npm:^2.2.3" @@ -14117,6 +14118,61 @@ __metadata: languageName: node linkType: hard +"prettier-plugin-tailwindcss@npm:^0.6.4": + version: 0.6.4 + resolution: "prettier-plugin-tailwindcss@npm:0.6.4" + peerDependencies: + "@ianvs/prettier-plugin-sort-imports": "*" + "@prettier/plugin-pug": "*" + "@shopify/prettier-plugin-liquid": "*" + "@trivago/prettier-plugin-sort-imports": "*" + "@zackad/prettier-plugin-twig-melody": "*" + prettier: ^3.0 + prettier-plugin-astro: "*" + prettier-plugin-css-order: "*" + prettier-plugin-import-sort: "*" + prettier-plugin-jsdoc: "*" + prettier-plugin-marko: "*" + prettier-plugin-organize-attributes: "*" + prettier-plugin-organize-imports: "*" + prettier-plugin-sort-imports: "*" + prettier-plugin-style-order: "*" + prettier-plugin-svelte: "*" + peerDependenciesMeta: + "@ianvs/prettier-plugin-sort-imports": + optional: true + "@prettier/plugin-pug": + optional: true + "@shopify/prettier-plugin-liquid": + optional: true + "@trivago/prettier-plugin-sort-imports": + optional: true + "@zackad/prettier-plugin-twig-melody": + optional: true + prettier-plugin-astro: + optional: true + prettier-plugin-css-order: + optional: true + prettier-plugin-import-sort: + optional: true + prettier-plugin-jsdoc: + optional: true + prettier-plugin-marko: + optional: true + prettier-plugin-organize-attributes: + optional: true + prettier-plugin-organize-imports: + optional: true + prettier-plugin-sort-imports: + optional: true + prettier-plugin-style-order: + optional: true + prettier-plugin-svelte: + optional: true + checksum: 4c486b1119d80b3d0eab951f4e70c1ea7250c8f43d5caa646b60648a20187fb21f5d43507525d7850a9b6b1cfa0d5a602a14d6e347a0d737dfb7740d5f559e18 + languageName: node + linkType: hard + "prettier@npm:^3.1.1": version: 3.2.5 resolution: "prettier@npm:3.2.5" From 4f23be39e307925f66aacecd2ea2c64e176ce77c Mon Sep 17 00:00:00 2001 From: Kacper Szarkiewicz Date: Fri, 14 Jun 2024 14:32:40 +0200 Subject: [PATCH 07/50] implement ConnectWalletModal --- .../ConnectModal/ConnectModalContent.tsx | 67 +++++++++++ .../ConnectModalAccountsList/index.tsx | 28 +++++ .../ConnectModalWalletsListItem.tsx | 25 +++++ .../ConnectModalWalletsList/index.tsx | 27 +++++ .../Wallet/modals/ConnectModal/index.tsx | 33 ++++++ .../Wallet/modals/DisconnectModal/index.tsx | 105 ++++++++++++++++++ 6 files changed, 285 insertions(+) create mode 100644 src/components/Wallet/modals/ConnectModal/ConnectModalContent.tsx create mode 100644 src/components/Wallet/modals/ConnectModal/ConnectModalList/ConnectModalAccountsList/index.tsx create mode 100644 src/components/Wallet/modals/ConnectModal/ConnectModalList/ConnectModalWalletsList/ConnectModalWalletsListItem.tsx create mode 100644 src/components/Wallet/modals/ConnectModal/ConnectModalList/ConnectModalWalletsList/index.tsx create mode 100644 src/components/Wallet/modals/ConnectModal/index.tsx create mode 100644 src/components/Wallet/modals/DisconnectModal/index.tsx diff --git a/src/components/Wallet/modals/ConnectModal/ConnectModalContent.tsx b/src/components/Wallet/modals/ConnectModal/ConnectModalContent.tsx new file mode 100644 index 00000000..0243a7ee --- /dev/null +++ b/src/components/Wallet/modals/ConnectModal/ConnectModalContent.tsx @@ -0,0 +1,67 @@ +import { StateUpdater, useCallback, useEffect, useState } from 'preact/hooks'; +import { Wallet, WalletAccount, getWallets } from '@talismn/connect-wallets'; +import { Collapse } from 'react-daisyui'; +import { ConnectModalWalletsList } from './ConnectModalList/ConnectModalWalletsList'; +import { useGlobalState } from '../../../../GlobalStateProvider'; +import { ConnectModalAccountsList } from './ConnectModalList/ConnectModalAccountsList'; + +export const ConnectModalContent = () => { + const [wallets, setWallets] = useState(); + const [selectedWallet, setSelectedWallet] = useState(); + const [accounts, setAccounts] = useState([]); + + const { dAppName, walletAccount } = useGlobalState(); + const [unsubscribe, setUnsubscribe] = useState unknown>>>(); + + useEffect(() => { + const wallets = getWallets(); + const installedWallets = wallets.filter((wallet) => wallet.installed); + setWallets(installedWallets || wallets); + + return () => { + setSelectedWallet(undefined); + }; + }, []); + + const onWalletListSelected = useCallback( + async (wallet: Wallet) => { + setSelectedWallet(wallet); + + try { + await wallet.enable(dAppName); + + await wallet.subscribeAccounts((walletAccounts) => { + if (walletAccounts) { + setAccounts(walletAccounts); + } + }); + } catch (err) { + console.warn(err); + } + }, + [dAppName], + ); + + return ( + + ); +}; diff --git a/src/components/Wallet/modals/ConnectModal/ConnectModalList/ConnectModalAccountsList/index.tsx b/src/components/Wallet/modals/ConnectModal/ConnectModalList/ConnectModalAccountsList/index.tsx new file mode 100644 index 00000000..e03f84fd --- /dev/null +++ b/src/components/Wallet/modals/ConnectModal/ConnectModalList/ConnectModalAccountsList/index.tsx @@ -0,0 +1,28 @@ +import { WalletAccount } from '@talismn/connect-wallets'; +import { useDeferredValue, useState } from 'preact/compat'; +import { SearchInput } from '../../../../../SearchInput'; +import { SimpleAccountCard } from '../../../../../AccountCard/SimpleAccountCard'; + +interface ConnectModalAccountsListProps { + accounts: WalletAccount[]; +} + +export const ConnectModalAccountsList = ({ accounts }: ConnectModalAccountsListProps) => { + const [inputSearchValue, setInputSearchValue] = useState(''); + const deferredInputSearchValue = useDeferredValue(inputSearchValue); + + const filteredAccounts = deferredInputSearchValue.length + ? accounts.filter((account) => account.address.toLowerCase().includes(deferredInputSearchValue.toLowerCase())) + : accounts; + + return ( +
+ +
    + {filteredAccounts.map((account: WalletAccount) => ( + + ))} +
+
+ ); +}; diff --git a/src/components/Wallet/modals/ConnectModal/ConnectModalList/ConnectModalWalletsList/ConnectModalWalletsListItem.tsx b/src/components/Wallet/modals/ConnectModal/ConnectModalList/ConnectModalWalletsList/ConnectModalWalletsListItem.tsx new file mode 100644 index 00000000..d4b9c862 --- /dev/null +++ b/src/components/Wallet/modals/ConnectModal/ConnectModalList/ConnectModalWalletsList/ConnectModalWalletsListItem.tsx @@ -0,0 +1,25 @@ +import { Wallet } from '@talismn/connect-wallets'; +import { Button } from 'react-daisyui'; + +interface WalletButtonProps { + wallet: Wallet; + onClick: (wallet: Wallet) => void; + makeInstallable?: boolean; +} + +function buttonOnClick(props: WalletButtonProps) { + const { wallet, onClick, makeInstallable } = props; + + return wallet.installed + ? onClick?.(wallet) + : (!wallet.installed && wallet.extensionName === 'talisman') || makeInstallable + ? window.open(wallet.installUrl, '_blank', 'noopener,noreferrer') + : null; +} + +export const ConnectModalListWalletsItem = (props: WalletButtonProps) => ( + +); diff --git a/src/components/Wallet/modals/ConnectModal/ConnectModalList/ConnectModalWalletsList/index.tsx b/src/components/Wallet/modals/ConnectModal/ConnectModalList/ConnectModalWalletsList/index.tsx new file mode 100644 index 00000000..094aef01 --- /dev/null +++ b/src/components/Wallet/modals/ConnectModal/ConnectModalList/ConnectModalWalletsList/index.tsx @@ -0,0 +1,27 @@ +import { Wallet } from '@talismn/connect-wallets'; +import { ConnectModalListWalletsItem } from './ConnectModalWalletsListItem'; + +interface ConnectWalletListProps { + items?: Wallet[]; + onClick: (wallet: Wallet) => void; + makeInstallable?: boolean; +} + +export function ConnectModalWalletsList({ items, onClick, makeInstallable }: ConnectWalletListProps) { + if (!items) { + return null; + } + + return ( +
+ {items.map((wallet: Wallet) => ( + + ))} +
+ ); +} diff --git a/src/components/Wallet/modals/ConnectModal/index.tsx b/src/components/Wallet/modals/ConnectModal/index.tsx new file mode 100644 index 00000000..01fa79a4 --- /dev/null +++ b/src/components/Wallet/modals/ConnectModal/index.tsx @@ -0,0 +1,33 @@ +import { Button } from 'react-daisyui'; +import { useState } from 'preact/hooks'; +import { Dialog } from '../../../../pages/collators/dialogs/Dialog'; +import { ConnectModalContent } from './ConnectModalContent'; + +export interface ConnectProps { + isHeader?: boolean; +} + +export const ConnectModal = ({ isHeader }: ConnectProps) => { + const [visible, setVisible] = useState(false); + + return ( + <> + + setVisible((state) => !state)} + content={} + actions={<>} + /> + + ); +}; diff --git a/src/components/Wallet/modals/DisconnectModal/index.tsx b/src/components/Wallet/modals/DisconnectModal/index.tsx new file mode 100644 index 00000000..cc668ead --- /dev/null +++ b/src/components/Wallet/modals/DisconnectModal/index.tsx @@ -0,0 +1,105 @@ +import { Wallet, WalletAccount } from '@talismn/connect-wallets'; +import { UseQueryResult } from '@tanstack/react-query'; +import { FrameSystemAccountInfo } from '@polkadot/types/lookup'; + +import { ArrowLeftEndOnRectangleIcon } from '@heroicons/react/20/solid'; +import { Button, Dropdown } from 'react-daisyui'; +import { getAddressForFormat } from '../../../../helpers/addressFormatter'; +import { useNodeInfoState } from '../../../../NodeInfoProvider'; +import { useAccountBalance } from '../../../../shared/useAccountBalance'; +import { useGlobalState } from '../../../../GlobalStateProvider'; +import { CopyableAddress } from '../../../PublicKey'; +import { Skeleton } from '../../../Skeleton'; + +interface WalletButtonProps { + wallet?: Wallet; + query: UseQueryResult; + balance?: string; + tokenSymbol?: string; + walletAccount?: WalletAccount; +} + +const WalletButton = ({ wallet, query, balance, tokenSymbol, walletAccount }: WalletButtonProps) => ( + +); + +interface WalletDropdownMenuProps { + address: string; + balance?: string; + tokenSymbol?: string; + walletAccount?: WalletAccount; + ss58Format?: number; + removeWalletAccount: () => void; +} + +const WalletDropdownMenu = ({ + walletAccount, + ss58Format, + address, + balance, + tokenSymbol, + removeWalletAccount, +}: WalletDropdownMenuProps) => ( + +
{walletAccount?.name}
+
+ +
+

+ {balance} {tokenSymbol} +

+ +
+); + +export const DisconnectModal = () => { + const { walletAccount, removeWalletAccount } = useGlobalState(); + const { query, balance } = useAccountBalance(); + const { ss58Format, tokenSymbol } = useNodeInfoState().state; + const { wallet, address } = walletAccount || {}; + + if (!address) return <>; + + return ( + + + + + ); +}; From 1eea1a864c55344bbfd23bae538aa42505e1c373 Mon Sep 17 00:00:00 2001 From: Kacper Szarkiewicz Date: Fri, 14 Jun 2024 14:33:01 +0200 Subject: [PATCH 08/50] implement SearchInput --- src/components/SearchInput/index.tsx | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 src/components/SearchInput/index.tsx diff --git a/src/components/SearchInput/index.tsx b/src/components/SearchInput/index.tsx new file mode 100644 index 00000000..0c8e65f9 --- /dev/null +++ b/src/components/SearchInput/index.tsx @@ -0,0 +1,21 @@ +import { MagnifyingGlassIcon } from '@heroicons/react/20/solid'; +import { StateUpdater } from 'preact/hooks'; + +interface SearchInputProps { + set: Dispatch>; +} + +export const SearchInput = ({ set, ...p }: SearchInputProps) => ( + +); From b0c2cf37634109f8802695f2e20bf745866a1ca0 Mon Sep 17 00:00:00 2001 From: Kacper Szarkiewicz Date: Fri, 14 Jun 2024 14:33:36 +0200 Subject: [PATCH 09/50] implement AccountCard component --- .../AccountCard/SimpleAccountCard/index.tsx | 24 +++++++++++++++++++ src/components/AccountCard/index.tsx | 24 +++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 src/components/AccountCard/SimpleAccountCard/index.tsx create mode 100644 src/components/AccountCard/index.tsx diff --git a/src/components/AccountCard/SimpleAccountCard/index.tsx b/src/components/AccountCard/SimpleAccountCard/index.tsx new file mode 100644 index 00000000..1ca1472b --- /dev/null +++ b/src/components/AccountCard/SimpleAccountCard/index.tsx @@ -0,0 +1,24 @@ +import { WalletAccount } from '@talismn/connect-wallets'; +import pendulumIcon from '../../../assets/pendulum-icon.svg'; +import { trimAddress } from '../../../helpers/addressFormatter'; +import { useGlobalState } from '../../../GlobalStateProvider'; + +interface AccountProps { + account: WalletAccount; +} + +export const SimpleAccountCard = ({ account }: AccountProps) => { + const { setWalletAccount } = useGlobalState(); + return ( +
  • + +
  • + ); +}; diff --git a/src/components/AccountCard/index.tsx b/src/components/AccountCard/index.tsx new file mode 100644 index 00000000..d5c775c4 --- /dev/null +++ b/src/components/AccountCard/index.tsx @@ -0,0 +1,24 @@ +import { WalletAccount } from '@talismn/connect-wallets'; +import pendulumIcon from '../../assets/pendulum-icon.svg'; +import { trimAddress } from '../../helpers/addressFormatter'; +import { useGlobalState } from '../../GlobalStateProvider'; + +interface AccountProps { + account: WalletAccount; +} + +export const Account = ({ account }: AccountProps) => { + const { setWalletAccount } = useGlobalState(); + return ( +
  • + +
  • + ); +}; From 6853c55671282b96cb2cf97a767de8cf500c1105 Mon Sep 17 00:00:00 2001 From: Kacper Szarkiewicz Date: Fri, 14 Jun 2024 14:34:14 +0200 Subject: [PATCH 10/50] remove unnecessary style overrides --- src/index.css | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/index.css b/src/index.css index dca288c6..0bfa7bc6 100644 --- a/src/index.css +++ b/src/index.css @@ -341,14 +341,6 @@ w3m-modal { color: currentColor; } -.collapse-content { - max-height: 0; -} - -.collapse-open .collapse-content { - max-height: none; -} - .collapse-arrow > .collapse-title:after { top: 50%; } From 26cb523f4c71a50b9e5c91c6e2ce0d8ea3549e4d Mon Sep 17 00:00:00 2001 From: Kacper Szarkiewicz Date: Fri, 14 Jun 2024 14:35:19 +0200 Subject: [PATCH 11/50] move wallets (not included in talismn) to wallets directory --- .../Wallet/wallets/MetamaskWallet/index.tsx | 79 ++++++++++++ .../Wallet/wallets/NovaWallet/index.tsx | 114 ++++++++++++++++++ .../Wallet/wallets/WalletConnect/index.tsx | 93 ++++++++++++++ 3 files changed, 286 insertions(+) create mode 100644 src/components/Wallet/wallets/MetamaskWallet/index.tsx create mode 100644 src/components/Wallet/wallets/NovaWallet/index.tsx create mode 100644 src/components/Wallet/wallets/WalletConnect/index.tsx diff --git a/src/components/Wallet/wallets/MetamaskWallet/index.tsx b/src/components/Wallet/wallets/MetamaskWallet/index.tsx new file mode 100644 index 00000000..1d85ab15 --- /dev/null +++ b/src/components/Wallet/wallets/MetamaskWallet/index.tsx @@ -0,0 +1,79 @@ +import { useCallback, useEffect, useState } from 'preact/compat'; +import { Modal } from 'react-daisyui'; +import { GlobalState, useGlobalState } from '../../../../GlobalStateProvider'; +import logo from '../../../assets/metamask-wallet.png'; +import { + ExtensionAccount, + buildWalletAccount, + initiateMetamaskInjectedAccount, +} from '../../../../services/metamask/metamask'; +import { CloseButton } from '../../../CloseButton'; +import { PublicKey } from '../../../PublicKey'; + +export type MetamaskWalletProps = { + setWalletAccount: GlobalState['setWalletAccount']; +}; + +const MetamaskWallet = ({ setWalletAccount }: MetamaskWalletProps) => { + const [openModal, setOpenModal] = useState(false); + const [accounts, setAccounts] = useState([]); + const [selectedAccount, setSelectedAccount] = useState(); + const { tenantName } = useGlobalState(); + + const onClick = useCallback(async () => { + const injectedMetamaskAccount = (await initiateMetamaskInjectedAccount(tenantName)) as ExtensionAccount; + if (injectedMetamaskAccount) { + setAccounts([injectedMetamaskAccount]); + setOpenModal(true); + } else { + console.error('Something went wrong, snap not found.'); + setOpenModal(false); + } + }, [setOpenModal]); + + useEffect(() => { + if (selectedAccount) { + buildWalletAccount(selectedAccount) + .then((account) => setWalletAccount(account)) + .then(() => { + setOpenModal(false); + }) + .catch((error) => console.error(error)); + } + }, [selectedAccount, setWalletAccount]); + + return ( +
    + + setOpenModal(false)} /> +

    Metamask Snap Polkadot account:

    +
    + {accounts.map((a, i) => ( + + ))} +
    +
    + +
    + ); +}; + +export default MetamaskWallet; diff --git a/src/components/Wallet/wallets/NovaWallet/index.tsx b/src/components/Wallet/wallets/NovaWallet/index.tsx new file mode 100644 index 00000000..82ac1368 --- /dev/null +++ b/src/components/Wallet/wallets/NovaWallet/index.tsx @@ -0,0 +1,114 @@ +import { web3Accounts, web3Enable, web3FromAddress } from '@polkadot/extension-dapp'; +import { WalletAccount } from '@talismn/connect-wallets'; +import { useCallback, useEffect, useState } from 'preact/compat'; +import { Modal } from 'react-daisyui'; +import logo from '../../../assets/nova-wallet.png'; +import { GlobalState } from '../../../../GlobalStateProvider'; +import { PublicKey } from '../../../PublicKey'; + +export type NovaWalletProps = { + setWalletAccount: GlobalState['setWalletAccount']; +}; + +interface ExtensionAccount { + address: string; + name: string; + source: string; +} + +const NovaWallet = ({ setWalletAccount }: NovaWalletProps) => { + const [openModal, setOpenModal] = useState(false); + const [accounts, setAccounts] = useState([]); + const [selectedAccount, setSelectedAccount] = useState(); + + const onClick = useCallback(async () => { + const _ = await web3Enable('Pendulum Chain Portal'); + const allAccounts = await web3Accounts(); + setAccounts( + allAccounts + .filter(({ meta: { source } }) => source === 'polkadot-js') + .map( + ({ address, meta }): ExtensionAccount => ({ + address: address, + name: meta.name || 'My Account', + source: meta.source, + }), + ), + ); + setOpenModal(true); + }, [setOpenModal]); + + useEffect(() => { + async function buildWalletAccount(extAcc: ExtensionAccount) { + const signer = await web3FromAddress(extAcc.address); + return { + address: extAcc.address, + source: extAcc.source, + name: extAcc.name, + signer: signer as WalletAccount['signer'], + wallet: { + enable: () => undefined, + extensionName: 'polkadot-js', + title: 'Nova Wallet', + installUrl: 'https://novawallet.io/', + logo: { + src: logo, + alt: 'Nova Wallet', + }, + installed: true, + extension: undefined, + signer, + /** + * The following methods are tagged as 'Unused' since they are only required by the @talisman package, + * which we are not using to handle this wallet connection. + */ + getAccounts: () => Promise.resolve([]), // Unused + subscribeAccounts: () => undefined, // Unused + transformError: (err: Error) => err, // Unused + }, + }; + } + if (selectedAccount) { + buildWalletAccount(selectedAccount) + .then((account) => setWalletAccount(account)) + .then(() => { + setOpenModal(false); + }) + .catch((error) => console.error(error)); + } + }, [selectedAccount, setWalletAccount]); + + return ( +
    + +

    Select your Nova account

    +
    + {accounts.map((a, i) => ( + + ))} +
    +
    + +
    + ); +}; + +export default NovaWallet; diff --git a/src/components/Wallet/wallets/WalletConnect/index.tsx b/src/components/Wallet/wallets/WalletConnect/index.tsx new file mode 100644 index 00000000..5f5bd976 --- /dev/null +++ b/src/components/Wallet/wallets/WalletConnect/index.tsx @@ -0,0 +1,93 @@ +import { WalletConnectModal } from '@walletconnect/modal'; +import UniversalProvider from '@walletconnect/universal-provider'; +import { SessionTypes } from '@walletconnect/types'; +import { useCallback, useEffect, useState } from 'preact/compat'; +import logo from '../../../assets/wallet-connect.svg'; +import { config } from '../../../../config'; +import { chainIds, walletConnectConfig } from '../../../../config/walletConnect'; +import { useGlobalState } from '../../../../GlobalStateProvider'; +import { walletConnectService } from '../../../../services/walletConnect'; +import { ToastMessage, showToast } from '../../../../shared/showToast'; + +const WalletConnect = () => { + const [loading, setLoading] = useState(false); + const [provider, setProvider] = useState | undefined>(); + const [modal, setModal] = useState(); + const { tenantName, setWalletAccount, removeWalletAccount } = useGlobalState(); + + const setupClientDisconnectListener = useCallback( + async (provider: Promise) => { + (await provider).client.on('session_delete', () => { + removeWalletAccount(); + }); + }, + [removeWalletAccount], + ); + + const handleModal = useCallback( + (uri?: string) => { + if (uri) { + modal?.openModal({ uri, onclose: () => setLoading(false) }); + } + }, + [modal], + ); + + const handleSession = useCallback( + async (approval: () => Promise, chainId: string) => { + const session = await approval(); + setWalletAccount(await walletConnectService.init(session, chainId)); + modal?.closeModal(); + }, + [setWalletAccount, modal], + ); + + const handleConnect = useCallback(async () => { + const chainId = chainIds[tenantName]; + if (!provider || !chainId) return; + + const wcProvider = await provider; + const { uri, approval } = await wcProvider.client.connect(walletConnectConfig); + + handleModal(uri); + handleSession(approval, chainId); + await setupClientDisconnectListener(provider); + }, [provider, tenantName, setupClientDisconnectListener, handleModal, handleSession]); + + const walletConnectClick = useCallback(async () => { + setLoading(true); + try { + await handleConnect(); + + //@eslint-disable-next-line no-explicit-any + } catch (error: any) { + showToast(ToastMessage.ERROR, error); + } finally { + setLoading(false); + } + }, [handleConnect]); + + useEffect(() => { + if (provider) return; + setProvider(walletConnectService.getProvider()); + setModal( + new WalletConnectModal({ + projectId: config.walletConnect.projectId, + }), + ); + }, [provider]); + + return ( +
    + +
    + ); +}; +export default WalletConnect; From 705cd5697255c2760c65015b34342c696b261ee3 Mon Sep 17 00:00:00 2001 From: Kacper Szarkiewicz Date: Fri, 14 Jun 2024 14:35:28 +0200 Subject: [PATCH 12/50] remove unused code --- src/GlobalStateProvider.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/GlobalStateProvider.tsx b/src/GlobalStateProvider.tsx index 2c43026d..58685727 100644 --- a/src/GlobalStateProvider.tsx +++ b/src/GlobalStateProvider.tsx @@ -41,7 +41,6 @@ const initTalisman = async (dAppName: string, selected?: string) => { }; const initWalletConnect = async (chainId: string) => { const provider = await walletConnectService.getProvider(); - //const pairings = provider.client.pairing.getAll({ active: true }); if (!provider?.session) return; return await walletConnectService.init(provider?.session, chainId); }; From 59eeb6a1cec95919894959ad187eb67d3fd81ce2 Mon Sep 17 00:00:00 2001 From: Kacper Szarkiewicz Date: Fri, 14 Jun 2024 14:49:14 +0200 Subject: [PATCH 13/50] show proper chain icon for wallet account --- src/components/AccountCard/SimpleAccountCard/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/AccountCard/SimpleAccountCard/index.tsx b/src/components/AccountCard/SimpleAccountCard/index.tsx index 1ca1472b..f4efcf2d 100644 --- a/src/components/AccountCard/SimpleAccountCard/index.tsx +++ b/src/components/AccountCard/SimpleAccountCard/index.tsx @@ -1,7 +1,7 @@ import { WalletAccount } from '@talismn/connect-wallets'; -import pendulumIcon from '../../../assets/pendulum-icon.svg'; import { trimAddress } from '../../../helpers/addressFormatter'; import { useGlobalState } from '../../../GlobalStateProvider'; +import ChainLogo from '../../../assets/ChainLogo'; interface AccountProps { account: WalletAccount; @@ -16,7 +16,7 @@ export const SimpleAccountCard = ({ account }: AccountProps) => { className="flex w-full cursor-pointer items-center rounded border-l-2 border-transparent p-1.5 hover:border-primary hover:bg-base-100" onClick={() => setWalletAccount(account)} > - +

    {trimAddress(account.address)}

    From 81e17a30f24db9e141a5a817bc8344499eb3f47e Mon Sep 17 00:00:00 2001 From: Kacper Szarkiewicz Date: Mon, 17 Jun 2024 08:38:01 +0200 Subject: [PATCH 14/50] create useConnectWallet hook --- src/hooks/useConnectWallet/index.tsx | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 src/hooks/useConnectWallet/index.tsx diff --git a/src/hooks/useConnectWallet/index.tsx b/src/hooks/useConnectWallet/index.tsx new file mode 100644 index 00000000..e132a4d0 --- /dev/null +++ b/src/hooks/useConnectWallet/index.tsx @@ -0,0 +1,26 @@ +import { StateUpdater, useCallback, useEffect, useState } from 'preact/hooks'; +import { Wallet, WalletAccount, getWallets } from '@talismn/connect-wallets'; +import { useMutation, useQuery } from '@tanstack/react-query'; +import { useGlobalState } from '../../GlobalStateProvider'; + +export const useConnectWallet = () => { + const [wallets, setWallets] = useState(); + const [selectedWallet, setSelectedWallet] = useState(); + + const { dAppName } = useGlobalState(); + + useEffect(() => { + const installedWallets = getWallets().filter((wallet) => wallet.installed); + setWallets(installedWallets); + }, []); + + const { mutate: selectWallet, data: accounts } = useMutation(async (wallet: Wallet) => { + setSelectedWallet(wallet); + + await wallet.enable(dAppName); + + return wallet.getAccounts(); + }); + + return { accounts, wallets, selectWallet }; +}; From bbe2d314db23b189a178b0501cabb4eedf1f1236 Mon Sep 17 00:00:00 2001 From: Kacper Szarkiewicz Date: Mon, 17 Jun 2024 08:38:23 +0200 Subject: [PATCH 15/50] Add ConnectModalContentLoading --- .../ConnectModal/ConnectModalContent.tsx | 58 +++++-------------- 1 file changed, 16 insertions(+), 42 deletions(-) diff --git a/src/components/Wallet/modals/ConnectModal/ConnectModalContent.tsx b/src/components/Wallet/modals/ConnectModal/ConnectModalContent.tsx index 0243a7ee..6191346b 100644 --- a/src/components/Wallet/modals/ConnectModal/ConnectModalContent.tsx +++ b/src/components/Wallet/modals/ConnectModal/ConnectModalContent.tsx @@ -1,56 +1,30 @@ -import { StateUpdater, useCallback, useEffect, useState } from 'preact/hooks'; -import { Wallet, WalletAccount, getWallets } from '@talismn/connect-wallets'; -import { Collapse } from 'react-daisyui'; +import { Collapse, Loading } from 'react-daisyui'; import { ConnectModalWalletsList } from './ConnectModalList/ConnectModalWalletsList'; -import { useGlobalState } from '../../../../GlobalStateProvider'; import { ConnectModalAccountsList } from './ConnectModalList/ConnectModalAccountsList'; +import { useConnectWallet } from '../../../../hooks/useConnectWallet'; -export const ConnectModalContent = () => { - const [wallets, setWallets] = useState(); - const [selectedWallet, setSelectedWallet] = useState(); - const [accounts, setAccounts] = useState([]); - - const { dAppName, walletAccount } = useGlobalState(); - const [unsubscribe, setUnsubscribe] = useState unknown>>>(); - - useEffect(() => { - const wallets = getWallets(); - const installedWallets = wallets.filter((wallet) => wallet.installed); - setWallets(installedWallets || wallets); - - return () => { - setSelectedWallet(undefined); - }; - }, []); +const ConnectModalLoading = () => ( +
    + +

    Connecting wallet

    +

    Please approve walletName and approve transaction.

    +
    +); - const onWalletListSelected = useCallback( - async (wallet: Wallet) => { - setSelectedWallet(wallet); - - try { - await wallet.enable(dAppName); +export const ConnectModalContent = () => { + const { wallets, accounts, selectWallet } = useConnectWallet(); - await wallet.subscribeAccounts((walletAccounts) => { - if (walletAccounts) { - setAccounts(walletAccounts); - } - }); - } catch (err) { - console.warn(err); - } - }, - [dAppName], - ); + // if (loading) return ; return (
    Select Wallet - onWalletListSelected(wallet)} /> -

    + selectWallet(wallet)} /> +

    Want to know more? - + Learn more about wallets

    @@ -59,7 +33,7 @@ export const ConnectModalContent = () => { Choose Account - +
    From 2355865b3ad511e714c6f014b9214e1c406a01e8 Mon Sep 17 00:00:00 2001 From: Kacper Szarkiewicz Date: Mon, 17 Jun 2024 12:12:36 +0200 Subject: [PATCH 16/50] add toast when user tries to connect a wallet that is already pending --- src/hooks/useConnectWallet/index.tsx | 29 +++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/src/hooks/useConnectWallet/index.tsx b/src/hooks/useConnectWallet/index.tsx index e132a4d0..f151f687 100644 --- a/src/hooks/useConnectWallet/index.tsx +++ b/src/hooks/useConnectWallet/index.tsx @@ -1,11 +1,12 @@ -import { StateUpdater, useCallback, useEffect, useState } from 'preact/hooks'; +import { useEffect, useState } from 'preact/hooks'; import { Wallet, WalletAccount, getWallets } from '@talismn/connect-wallets'; -import { useMutation, useQuery } from '@tanstack/react-query'; +import { useMutation } from '@tanstack/react-query'; import { useGlobalState } from '../../GlobalStateProvider'; +import { ToastMessage, showToast } from '../../shared/showToast'; export const useConnectWallet = () => { const [wallets, setWallets] = useState(); - const [selectedWallet, setSelectedWallet] = useState(); + const [selectedWallet, setSelectedWallet] = useState(); const { dAppName } = useGlobalState(); @@ -14,13 +15,27 @@ export const useConnectWallet = () => { setWallets(installedWallets); }, []); - const { mutate: selectWallet, data: accounts } = useMutation(async (wallet: Wallet) => { + const { + mutate: selectWallet, + data: accounts, + isLoading: loading, + } = useMutation(async (wallet) => { setSelectedWallet(wallet); - await wallet.enable(dAppName); + if (!wallet) return []; - return wallet.getAccounts(); + try { + await wallet.enable(dAppName); + + return wallet.getAccounts(); + } catch (e) { + showToast( + ToastMessage.WARNING, + 'There is already opened pending connection for this wallet. Please close it and try again.', + ); + return []; + } }); - return { accounts, wallets, selectWallet }; + return { accounts, wallets, selectWallet, loading, selectedWallet }; }; From 9f4a554fa7fb1e4c1e5ae3f385a0d618bfd9b773 Mon Sep 17 00:00:00 2001 From: Kacper Szarkiewicz Date: Mon, 17 Jun 2024 12:12:48 +0200 Subject: [PATCH 17/50] Add warning to the showToast --- src/shared/showToast.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/shared/showToast.ts b/src/shared/showToast.ts index 47795f82..0d778ab6 100644 --- a/src/shared/showToast.ts +++ b/src/shared/showToast.ts @@ -9,6 +9,7 @@ export enum ToastMessage { BUYOUT_ERROR = 'BUYOUT_ERROR', ERROR = 'ERROR', INFO = 'INFO', + WARNING = 'WARNING', } type ToastSettings = { @@ -64,6 +65,12 @@ const ToastProperties: Record = { type: toast.TYPE.INFO, }, }, + [ToastMessage.WARNING]: { + message: 'Warning', + options: { + type: toast.TYPE.WARNING, + }, + }, [ToastMessage.BUYOUT_ERROR]: { message: 'A buyout error occurred', options: { From 332b005b93d21fbe84a788010233621deeb96a5e Mon Sep 17 00:00:00 2001 From: Kacper Szarkiewicz Date: Mon, 17 Jun 2024 12:13:03 +0200 Subject: [PATCH 18/50] restructure ConnectModal --- ...odalContent.tsx => ConnectModalDialog.tsx} | 44 +++++++++++++------ .../ConnectModalDialogLoading.tsx | 13 ++++++ .../Wallet/modals/ConnectModal/index.tsx | 12 +++-- 3 files changed, 48 insertions(+), 21 deletions(-) rename src/components/Wallet/modals/ConnectModal/{ConnectModalContent.tsx => ConnectModalDialog.tsx} (55%) create mode 100644 src/components/Wallet/modals/ConnectModal/ConnectModalDialogLoading.tsx diff --git a/src/components/Wallet/modals/ConnectModal/ConnectModalContent.tsx b/src/components/Wallet/modals/ConnectModal/ConnectModalDialog.tsx similarity index 55% rename from src/components/Wallet/modals/ConnectModal/ConnectModalContent.tsx rename to src/components/Wallet/modals/ConnectModal/ConnectModalDialog.tsx index 6191346b..3cbf394e 100644 --- a/src/components/Wallet/modals/ConnectModal/ConnectModalContent.tsx +++ b/src/components/Wallet/modals/ConnectModal/ConnectModalDialog.tsx @@ -1,22 +1,19 @@ -import { Collapse, Loading } from 'react-daisyui'; +import { Collapse } from 'react-daisyui'; +import { useConnectWallet } from '../../../../hooks/useConnectWallet'; +import { Dialog } from '../../../../pages/collators/dialogs/Dialog'; import { ConnectModalWalletsList } from './ConnectModalList/ConnectModalWalletsList'; import { ConnectModalAccountsList } from './ConnectModalList/ConnectModalAccountsList'; -import { useConnectWallet } from '../../../../hooks/useConnectWallet'; +import { ConnectModalDialogLoading } from './ConnectModalDialogLoading'; -const ConnectModalLoading = () => ( -
    - -

    Connecting wallet

    -

    Please approve walletName and approve transaction.

    -
    -); +interface ConnectModalDialogProps { + visible: boolean; + onClose: () => void; +} -export const ConnectModalContent = () => { - const { wallets, accounts, selectWallet } = useConnectWallet(); +export const ConnectModalDialog = ({ visible, onClose }: ConnectModalDialogProps) => { + const { wallets, accounts, selectWallet, loading, selectedWallet } = useConnectWallet(); - // if (loading) return ; - - return ( + const content = (
    Select Wallet @@ -38,4 +35,23 @@ export const ConnectModalContent = () => {
    ); + + return ( + { + selectWallet(undefined); + onClose(); + }} + content={ + loading ? ( + + ) : ( + content + ) + } + actions={<>} + /> + ); }; diff --git a/src/components/Wallet/modals/ConnectModal/ConnectModalDialogLoading.tsx b/src/components/Wallet/modals/ConnectModal/ConnectModalDialogLoading.tsx new file mode 100644 index 00000000..71ff73cf --- /dev/null +++ b/src/components/Wallet/modals/ConnectModal/ConnectModalDialogLoading.tsx @@ -0,0 +1,13 @@ +import { Loading } from 'react-daisyui'; + +interface ConnectModalDialogLoadingProps { + selectedWallet: string; +} + +export const ConnectModalDialogLoading = ({ selectedWallet }: ConnectModalDialogLoadingProps) => ( +
    + +

    Connecting wallet

    +

    Please approve {selectedWallet} and approve transaction.

    +
    +); diff --git a/src/components/Wallet/modals/ConnectModal/index.tsx b/src/components/Wallet/modals/ConnectModal/index.tsx index 01fa79a4..d5d73fe9 100644 --- a/src/components/Wallet/modals/ConnectModal/index.tsx +++ b/src/components/Wallet/modals/ConnectModal/index.tsx @@ -1,7 +1,6 @@ import { Button } from 'react-daisyui'; import { useState } from 'preact/hooks'; -import { Dialog } from '../../../../pages/collators/dialogs/Dialog'; -import { ConnectModalContent } from './ConnectModalContent'; +import { ConnectModalDialog } from './ConnectModalDialog'; export interface ConnectProps { isHeader?: boolean; @@ -21,12 +20,11 @@ export const ConnectModal = ({ isHeader }: ConnectProps) => { > Connect Wallet - setVisible((state) => !state)} - content={} - actions={<>} + onClose={() => { + setVisible(false); + }} /> ); From ce10bc604c5d44c6ddd6412944eb3434d02b8c89 Mon Sep 17 00:00:00 2001 From: Kacper Szarkiewicz Date: Mon, 17 Jun 2024 12:17:16 +0200 Subject: [PATCH 19/50] extract toast communicate to another ToastMessage --- src/hooks/useConnectWallet/index.tsx | 7 ++----- src/shared/showToast.ts | 8 ++++++++ 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/hooks/useConnectWallet/index.tsx b/src/hooks/useConnectWallet/index.tsx index f151f687..1cef6294 100644 --- a/src/hooks/useConnectWallet/index.tsx +++ b/src/hooks/useConnectWallet/index.tsx @@ -28,11 +28,8 @@ export const useConnectWallet = () => { await wallet.enable(dAppName); return wallet.getAccounts(); - } catch (e) { - showToast( - ToastMessage.WARNING, - 'There is already opened pending connection for this wallet. Please close it and try again.', - ); + } catch { + showToast(ToastMessage.WALLET_ALREADY_OPEN_PENDING_CONNECTION); return []; } }); diff --git a/src/shared/showToast.ts b/src/shared/showToast.ts index 0d778ab6..2e72a08b 100644 --- a/src/shared/showToast.ts +++ b/src/shared/showToast.ts @@ -7,6 +7,7 @@ export enum ToastMessage { UPDATED_DELEGATOR_REWARDS = 'UPDATED_DELEGATOR_REWARDS', NO_WALLET_SELECTED = 'NO_WALLET_SELECTED', BUYOUT_ERROR = 'BUYOUT_ERROR', + WALLET_ALREADY_OPEN_PENDING_CONNECTION = 'WALLET_ALREADY_OPEN_PENDING_CONNECTION', ERROR = 'ERROR', INFO = 'INFO', WARNING = 'WARNING', @@ -78,6 +79,13 @@ const ToastProperties: Record = { toastId: ToastMessage.BUYOUT_ERROR, }, }, + [ToastMessage.WALLET_ALREADY_OPEN_PENDING_CONNECTION]: { + message: 'There is already an open pending connection for this wallet. Please close it and try again.', + options: { + toastId: ToastMessage.WALLET_ALREADY_OPEN_PENDING_CONNECTION, + type: toast.TYPE.WARNING, + }, + }, }; export function showToast(message: ToastMessage, customMessage?: string) { From 39173a7b15f3147628fb2a85198dd7e14d54b6aa Mon Sep 17 00:00:00 2001 From: Kacper Szarkiewicz Date: Tue, 18 Jun 2024 12:13:04 +0200 Subject: [PATCH 20/50] add WalletConnect and error handling --- .../AccountCard/SimpleAccountCard/index.tsx | 1 + .../ConnectModal/ConnectModalDialog.tsx | 2 +- .../ConnectModalWalletsList/index.tsx | 14 +++++--- .../Wallet/wallets/NovaWallet/index.tsx | 6 ++-- .../Wallet/wallets/WalletConnect/index.tsx | 32 +++++++++---------- src/hooks/useConnectWallet/index.tsx | 1 - src/services/walletConnect/index.ts | 10 ++++-- 7 files changed, 37 insertions(+), 29 deletions(-) diff --git a/src/components/AccountCard/SimpleAccountCard/index.tsx b/src/components/AccountCard/SimpleAccountCard/index.tsx index f4efcf2d..d98f3def 100644 --- a/src/components/AccountCard/SimpleAccountCard/index.tsx +++ b/src/components/AccountCard/SimpleAccountCard/index.tsx @@ -9,6 +9,7 @@ interface AccountProps { export const SimpleAccountCard = ({ account }: AccountProps) => { const { setWalletAccount } = useGlobalState(); + return (
  • - + ); }; export default WalletConnect; diff --git a/src/hooks/useConnectWallet/index.tsx b/src/hooks/useConnectWallet/index.tsx index 1cef6294..ba84f8e7 100644 --- a/src/hooks/useConnectWallet/index.tsx +++ b/src/hooks/useConnectWallet/index.tsx @@ -7,7 +7,6 @@ import { ToastMessage, showToast } from '../../shared/showToast'; export const useConnectWallet = () => { const [wallets, setWallets] = useState(); const [selectedWallet, setSelectedWallet] = useState(); - const { dAppName } = useGlobalState(); useEffect(() => { diff --git a/src/services/walletConnect/index.ts b/src/services/walletConnect/index.ts index 0838aa26..17d1fb38 100644 --- a/src/services/walletConnect/index.ts +++ b/src/services/walletConnect/index.ts @@ -1,7 +1,7 @@ import { Signer } from '@polkadot/types/types'; import { WalletAccount } from '@talismn/connect-wallets'; import type { SessionTypes } from '@walletconnect/types/dist/types/sign-client/session'; -import UniversalProvider, { UniversalProviderOpts } from '@walletconnect/universal-provider'; +import UniversalProvider from '@walletconnect/universal-provider'; import logo from '../../assets/wallet-connect.svg'; import { config } from '../../config'; @@ -14,7 +14,13 @@ export const walletConnectService = { (await UniversalProvider.init({ projectId: config.walletConnect.projectId, relayUrl: config.walletConnect.url, - } as UniversalProviderOpts)); + metadata: { + name: 'Pendulum Chain Portal', + description: 'Pendulum Chain Portal', + url: 'https://portal.pendulumchain.org/', + icons: ['https://walletconnect.com/walletconnect-logo.png'], + }, + })); return this.provider; }, init: async function init(session: SessionTypes.Struct, chainId: string): Promise { From 731036ae899d5e82f30ae5620645862140c4df85 Mon Sep 17 00:00:00 2001 From: Kacper Szarkiewicz Date: Tue, 18 Jun 2024 12:26:29 +0200 Subject: [PATCH 21/50] add metamask wallet connect --- .../ConnectModalWalletsList/index.tsx | 2 + .../Wallet/wallets/MetamaskWallet/index.tsx | 80 +++++++++---------- 2 files changed, 42 insertions(+), 40 deletions(-) diff --git a/src/components/Wallet/modals/ConnectModal/ConnectModalList/ConnectModalWalletsList/index.tsx b/src/components/Wallet/modals/ConnectModal/ConnectModalList/ConnectModalWalletsList/index.tsx index b07892ec..b636cd45 100644 --- a/src/components/Wallet/modals/ConnectModal/ConnectModalList/ConnectModalWalletsList/index.tsx +++ b/src/components/Wallet/modals/ConnectModal/ConnectModalList/ConnectModalWalletsList/index.tsx @@ -1,6 +1,7 @@ import { Wallet } from '@talismn/connect-wallets'; import { ConnectModalListWalletsItem } from './ConnectModalWalletsListItem'; import WalletConnect from '../../../../wallets/WalletConnect'; +import MetamaskWallet from '../../../../wallets/MetamaskWallet'; interface ConnectWalletListProps { wallets?: Wallet[]; @@ -26,6 +27,7 @@ export function ConnectModalWalletsList({ wallets, onClick, makeInstallable, onC ))} + ); } diff --git a/src/components/Wallet/wallets/MetamaskWallet/index.tsx b/src/components/Wallet/wallets/MetamaskWallet/index.tsx index 1d85ab15..ac2f3dd6 100644 --- a/src/components/Wallet/wallets/MetamaskWallet/index.tsx +++ b/src/components/Wallet/wallets/MetamaskWallet/index.tsx @@ -1,7 +1,7 @@ import { useCallback, useEffect, useState } from 'preact/compat'; -import { Modal } from 'react-daisyui'; -import { GlobalState, useGlobalState } from '../../../../GlobalStateProvider'; -import logo from '../../../assets/metamask-wallet.png'; +import { Button, Modal } from 'react-daisyui'; +import { useGlobalState } from '../../../../GlobalStateProvider'; +import logo from '../../../../assets/metamask-wallet.png'; import { ExtensionAccount, buildWalletAccount, @@ -9,16 +9,13 @@ import { } from '../../../../services/metamask/metamask'; import { CloseButton } from '../../../CloseButton'; import { PublicKey } from '../../../PublicKey'; +import { Dialog } from '../../../../pages/collators/dialogs/Dialog'; -export type MetamaskWalletProps = { - setWalletAccount: GlobalState['setWalletAccount']; -}; - -const MetamaskWallet = ({ setWalletAccount }: MetamaskWalletProps) => { +const MetamaskWallet = () => { const [openModal, setOpenModal] = useState(false); const [accounts, setAccounts] = useState([]); const [selectedAccount, setSelectedAccount] = useState(); - const { tenantName } = useGlobalState(); + const { tenantName, setWalletAccount } = useGlobalState(); const onClick = useCallback(async () => { const injectedMetamaskAccount = (await initiateMetamaskInjectedAccount(tenantName)) as ExtensionAccount; @@ -29,7 +26,7 @@ const MetamaskWallet = ({ setWalletAccount }: MetamaskWalletProps) => { console.error('Something went wrong, snap not found.'); setOpenModal(false); } - }, [setOpenModal]); + }, [setOpenModal, tenantName]); useEffect(() => { if (selectedAccount) { @@ -42,37 +39,40 @@ const MetamaskWallet = ({ setWalletAccount }: MetamaskWalletProps) => { } }, [selectedAccount, setWalletAccount]); + const content = ( + <> + {accounts.map((a, i) => ( + + ))} + + ); + return ( -
    - - setOpenModal(false)} /> -

    Metamask Snap Polkadot account:

    -
    - {accounts.map((a, i) => ( - - ))} -
    -
    - -
    + <> + setOpenModal(false)} + actions={<>} + visible={openModal} + /> + + ); }; From 6c47f56bb30abc507d2352ce2bbfa9ce5d8d5c8d Mon Sep 17 00:00:00 2001 From: Kacper Szarkiewicz Date: Tue, 25 Jun 2024 18:45:54 +0200 Subject: [PATCH 22/50] adjust metamask snap connection --- .../ConnectModal/ConnectModalDialog.tsx | 11 +- .../ConnectModalWalletsList/index.tsx | 3 - .../Wallet/wallets/MetamaskWallet/index.tsx | 36 ++--- src/hooks/useConnectWallet/index.tsx | 2 - src/hooks/useMetamask.ts | 45 +++++++ src/services/metamask/metamask.ts | 123 ++++++++---------- 6 files changed, 115 insertions(+), 105 deletions(-) create mode 100644 src/hooks/useMetamask.ts diff --git a/src/components/Wallet/modals/ConnectModal/ConnectModalDialog.tsx b/src/components/Wallet/modals/ConnectModal/ConnectModalDialog.tsx index 5fbe1f66..ba2ec84c 100644 --- a/src/components/Wallet/modals/ConnectModal/ConnectModalDialog.tsx +++ b/src/components/Wallet/modals/ConnectModal/ConnectModalDialog.tsx @@ -4,6 +4,7 @@ import { Dialog } from '../../../../pages/collators/dialogs/Dialog'; import { ConnectModalWalletsList } from './ConnectModalList/ConnectModalWalletsList'; import { ConnectModalAccountsList } from './ConnectModalList/ConnectModalAccountsList'; import { ConnectModalDialogLoading } from './ConnectModalDialogLoading'; +import { useMetamask } from '../../../../hooks/useMetamask'; interface ConnectModalDialogProps { visible: boolean; @@ -13,12 +14,18 @@ interface ConnectModalDialogProps { export const ConnectModalDialog = ({ visible, onClose }: ConnectModalDialogProps) => { const { wallets, accounts, selectWallet, loading, selectedWallet } = useConnectWallet(); + const { accounts: metamaskAccounts, selectedWallet: metamaskSelectedWallet } = useMetamask(); + const allWallets = [...(wallets || []), metamaskSelectedWallet]; + const allAccounts = [...(accounts || []), ...metamaskAccounts]; + + console.log('allWallets:', allWallets); + const content = (
    diff --git a/src/components/Wallet/modals/ConnectModal/ConnectModalList/ConnectModalWalletsList/index.tsx b/src/components/Wallet/modals/ConnectModal/ConnectModalList/ConnectModalWalletsList/index.tsx index b636cd45..a9852ecf 100644 --- a/src/components/Wallet/modals/ConnectModal/ConnectModalList/ConnectModalWalletsList/index.tsx +++ b/src/components/Wallet/modals/ConnectModal/ConnectModalList/ConnectModalWalletsList/index.tsx @@ -1,7 +1,6 @@ import { Wallet } from '@talismn/connect-wallets'; import { ConnectModalListWalletsItem } from './ConnectModalWalletsListItem'; import WalletConnect from '../../../../wallets/WalletConnect'; -import MetamaskWallet from '../../../../wallets/MetamaskWallet'; interface ConnectWalletListProps { wallets?: Wallet[]; @@ -25,9 +24,7 @@ export function ConnectModalWalletsList({ wallets, onClick, makeInstallable, onC makeInstallable={makeInstallable} /> ))} - - ); } diff --git a/src/components/Wallet/wallets/MetamaskWallet/index.tsx b/src/components/Wallet/wallets/MetamaskWallet/index.tsx index ac2f3dd6..508fed69 100644 --- a/src/components/Wallet/wallets/MetamaskWallet/index.tsx +++ b/src/components/Wallet/wallets/MetamaskWallet/index.tsx @@ -1,41 +1,21 @@ -import { useCallback, useEffect, useState } from 'preact/compat'; -import { Button, Modal } from 'react-daisyui'; +import { useEffect, useState } from 'preact/compat'; +import { Button } from 'react-daisyui'; import { useGlobalState } from '../../../../GlobalStateProvider'; import logo from '../../../../assets/metamask-wallet.png'; -import { - ExtensionAccount, - buildWalletAccount, - initiateMetamaskInjectedAccount, -} from '../../../../services/metamask/metamask'; -import { CloseButton } from '../../../CloseButton'; +import { ExtensionAccount, buildWalletAccount } from '../../../../services/metamask/metamask'; import { PublicKey } from '../../../PublicKey'; import { Dialog } from '../../../../pages/collators/dialogs/Dialog'; const MetamaskWallet = () => { const [openModal, setOpenModal] = useState(false); - const [accounts, setAccounts] = useState([]); const [selectedAccount, setSelectedAccount] = useState(); - const { tenantName, setWalletAccount } = useGlobalState(); - - const onClick = useCallback(async () => { - const injectedMetamaskAccount = (await initiateMetamaskInjectedAccount(tenantName)) as ExtensionAccount; - if (injectedMetamaskAccount) { - setAccounts([injectedMetamaskAccount]); - setOpenModal(true); - } else { - console.error('Something went wrong, snap not found.'); - setOpenModal(false); - } - }, [setOpenModal, tenantName]); + const { setWalletAccount } = useGlobalState(); useEffect(() => { if (selectedAccount) { - buildWalletAccount(selectedAccount) - .then((account) => setWalletAccount(account)) - .then(() => { - setOpenModal(false); - }) - .catch((error) => console.error(error)); + const account = buildWalletAccount(selectedAccount); + setWalletAccount(account); + setOpenModal(false); } }, [selectedAccount, setWalletAccount]); @@ -51,7 +31,7 @@ const MetamaskWallet = () => {
    {a.name}
    - {' '} +
    diff --git a/src/hooks/useConnectWallet/index.tsx b/src/hooks/useConnectWallet/index.tsx index ba84f8e7..7649e09e 100644 --- a/src/hooks/useConnectWallet/index.tsx +++ b/src/hooks/useConnectWallet/index.tsx @@ -20,12 +20,10 @@ export const useConnectWallet = () => { isLoading: loading, } = useMutation(async (wallet) => { setSelectedWallet(wallet); - if (!wallet) return []; try { await wallet.enable(dAppName); - return wallet.getAccounts(); } catch { showToast(ToastMessage.WALLET_ALREADY_OPEN_PENDING_CONNECTION); diff --git a/src/hooks/useMetamask.ts b/src/hooks/useMetamask.ts new file mode 100644 index 00000000..0e04ac56 --- /dev/null +++ b/src/hooks/useMetamask.ts @@ -0,0 +1,45 @@ +import { useGlobalState } from '../GlobalStateProvider'; +import { Wallet } from '@talismn/connect-wallets'; +import { useEffect, useMemo, useState } from 'preact/hooks'; +import { ExtensionAccount, initiateMetamaskInjectedAccount } from '../services/metamask/metamask'; +import logo from '../assets/metamask-wallet.png'; + +export const useMetamask = () => { + const { tenantName } = useGlobalState(); + const [accounts, setAccounts] = useState([]); + + useEffect(() => { + async function fetchAccounts() { + const injectedMetamaskAccount = (await initiateMetamaskInjectedAccount(tenantName)) as ExtensionAccount; + if (injectedMetamaskAccount) { + setAccounts([injectedMetamaskAccount]); + } + } + + fetchAccounts(); + }, [tenantName]); + + const selectedWallet: Wallet = useMemo( + () => ({ + enable: () => undefined, + extensionName: 'polkadot-js', + title: 'Metamask', + installUrl: 'https://metamask.io/', + logo: { + src: logo, + alt: 'Metamask Wallet', + }, + installed: Boolean(window.ethereum), + extension: window.ethereum, + + signer: undefined, + getAccounts: () => initiateMetamaskInjectedAccount(tenantName), + subscribeAccounts: () => undefined, + transformError: (err: Error) => new Error(err.message), + }), + + [tenantName], + ); + + return { selectedWallet, accounts }; +}; diff --git a/src/services/metamask/metamask.ts b/src/services/metamask/metamask.ts index a4dcf35d..0ed6cfcb 100644 --- a/src/services/metamask/metamask.ts +++ b/src/services/metamask/metamask.ts @@ -1,35 +1,34 @@ +import { WalletAccount } from '@talismn/connect-wallets'; import { enablePolkadotSnap } from '@chainsafe/metamask-polkadot-adapter'; import type { MetamaskPolkadotSnap } from '@chainsafe/metamask-polkadot-adapter/build/snap'; import type { InjectedMetamaskExtension } from '@chainsafe/metamask-polkadot-adapter/src/types'; +import { MetamaskSnapApi } from '@chainsafe/metamask-polkadot-adapter/build/types'; import type { SnapNetworks } from '@chainsafe/metamask-polkadot-types'; import { Signer } from '@polkadot/api/types'; import { web3EnablePromise } from '@polkadot/extension-dapp'; import type { InjectedExtension } from '@polkadot/extension-inject/types'; import { SignerPayloadJSON, SignerPayloadRaw, SignerResult } from '@polkadot/types/types'; -import { WalletAccount } from '@talismn/connect-wallets'; -import logo from '../../assets/metamask-wallet.png'; + import { TenantName } from '../../models/Tenant'; +import { trimAddress } from '../../helpers/addressFormatter'; export const WALLET_SOURCE_METAMASK = 'metamask'; export function tenantToSnapNetwork(tenantName: TenantName): SnapNetworks { - switch (tenantName) { - case TenantName.Pendulum: - return 'polkadot'; - case TenantName.Amplitude: - return 'kusama'; - default: - return 'kusama'; - } + const tenantNetworkMap: { [key in TenantName]?: SnapNetworks } = { + [TenantName.Pendulum]: 'polkadot', + [TenantName.Amplitude]: 'kusama', + }; + + return tenantNetworkMap[tenantName] || 'kusama'; } export async function installPolkadotSnap(relayChain: SnapNetworks): Promise { try { await enablePolkadotSnap({ networkName: relayChain }); - console.info('Snap installed!!'); return true; } catch (err) { - console.error(err); + console.error('Failed to install PolkadotSnap, ', err); return false; } } @@ -56,9 +55,7 @@ export interface SnapInitializationResponse { export async function initiatePolkadotSnap(relayChain: SnapNetworks): Promise { try { - console.info('Attempting to connect to snap...'); const metamaskPolkadotSnap = await enablePolkadotSnap({ networkName: relayChain }); - console.info('Snap installed!'); return { isSnapInstalled: true, snap: metamaskPolkadotSnap }; } catch (e) { console.error(e); @@ -72,37 +69,17 @@ export interface ExtensionAccount { signer: Signer; } -export async function buildWalletAccount(extAcc: ExtensionAccount) { - return { - address: extAcc.address, - source: extAcc.source, - name: extAcc.name, - signer: extAcc.signer as WalletAccount['signer'], - wallet: { - enable: () => undefined, - extensionName: 'polkadot-js', - title: 'Metamask Wallet', - installUrl: 'https://metamask.io/', - logo: { - src: logo, - alt: 'Metamask Wallet', - }, - installed: true, - extension: undefined, - signer: extAcc.signer, - /** - * The following methods are tagged as 'Unused' since they are only required by the @talisman package, - * which we are not using to handle this wallet connection. - */ - getAccounts: () => Promise.resolve([]), // Unused - subscribeAccounts: () => undefined, // Unused - transformError: (err: Error) => err, // Unused - }, - }; -} +export const buildWalletAccount = (extAcc: ExtensionAccount): WalletAccount => ({ + address: extAcc.address, + source: extAcc.source, + name: extAcc.name, + signer: extAcc.signer as WalletAccount['signer'], + wallet: undefined, +}); -export async function initiateMetamaskInjectedAccount(tenantName: TenantName) { +const getProvider = async (tenantName: TenantName) => { const provider = await initiatePolkadotSnap(tenantToSnapNetwork(tenantName)); + if (!provider.isSnapInstalled) { const installResult = await installPolkadotSnap(tenantToSnapNetwork(tenantName)); if (!installResult) { @@ -110,31 +87,37 @@ export async function initiateMetamaskInjectedAccount(tenantName: TenantName) { return; } } - if (provider.snap) { - const api = provider.snap.getMetamaskSnapApi(); - const injectedMetamaskAccount: ExtensionAccount = { - address: await api.getAddress(), - name: 'Metamask Snap', - source: WALLET_SOURCE_METAMASK, - signer: { - signPayload: async (payload: SignerPayloadJSON) => { - const stringResult = await api.signPayloadJSON(payload); - // Metamask snap doesn't provide a request Id, but just the hex string, so - // adding id: 1 to be compliant with SignerResult - return { id: 1, signature: stringResult } as SignerResult; - }, - signRaw: async (raw: SignerPayloadRaw) => { - const stringResult = await api.signPayloadRaw(raw); - // Metamask snap doesn't provide a request Id, but just the hex string, so - // adding id: 1 to be compliant with SignerResult - return { id: 1, signature: stringResult } as SignerResult; - }, - update: (id, status) => { - console.log('Status update for Id %d: %s', id, status.toHuman()); - }, - }, - }; - return await buildWalletAccount(injectedMetamaskAccount); - } - return undefined; + + return provider; +}; + +const getSigner = (api: MetamaskSnapApi) => ({ + signPayload: async (payload: SignerPayloadJSON) => { + const stringResult = await api.signPayloadJSON(payload); + // Metamask snap doesn't provide a request Id, but just the hex string, so + // adding id: 1 to be compliant with SignerResult + return { id: 1, signature: stringResult } as SignerResult; + }, + signRaw: async (raw: SignerPayloadRaw) => { + const stringResult = await api.signPayloadRaw(raw); + // Metamask snap doesn't provide a request Id, but just the hex string, so + // adding id: 1 to be compliant with SignerResult + return { id: 1, signature: stringResult } as SignerResult; + }, +}); + +export async function initiateMetamaskInjectedAccount(tenantName: TenantName) { + const provider = await getProvider(tenantName); + + if (!provider?.snap) return undefined; + const api = provider.snap.getMetamaskSnapApi(); + const address = await api.getAddress(); + + const injectedMetamaskAccount: ExtensionAccount = { + address, + name: trimAddress(address), + source: WALLET_SOURCE_METAMASK, + signer: getSigner(api), + }; + return await buildWalletAccount(injectedMetamaskAccount); } From b64d653c72add2b4aa4ef89ea34b77957db6fccf Mon Sep 17 00:00:00 2001 From: Kacper Szarkiewicz Date: Thu, 27 Jun 2024 13:09:36 +0200 Subject: [PATCH 23/50] unify wallet connection logic --- .../ConnectModal/ConnectModalDialog.tsx | 16 +++--------- src/hooks/useWalletConnection/index.tsx | 26 +++++++++++++++++++ .../useConnectWallet/index.tsx | 4 +-- .../useMetamask/index.tsx} | 26 ++++++++----------- 4 files changed, 43 insertions(+), 29 deletions(-) create mode 100644 src/hooks/useWalletConnection/index.tsx rename src/hooks/{ => useWalletConnection}/useConnectWallet/index.tsx (88%) rename src/hooks/{useMetamask.ts => useWalletConnection/useMetamask/index.tsx} (58%) diff --git a/src/components/Wallet/modals/ConnectModal/ConnectModalDialog.tsx b/src/components/Wallet/modals/ConnectModal/ConnectModalDialog.tsx index ba2ec84c..8f68c788 100644 --- a/src/components/Wallet/modals/ConnectModal/ConnectModalDialog.tsx +++ b/src/components/Wallet/modals/ConnectModal/ConnectModalDialog.tsx @@ -1,10 +1,9 @@ import { Collapse } from 'react-daisyui'; -import { useConnectWallet } from '../../../../hooks/useConnectWallet'; import { Dialog } from '../../../../pages/collators/dialogs/Dialog'; import { ConnectModalWalletsList } from './ConnectModalList/ConnectModalWalletsList'; import { ConnectModalAccountsList } from './ConnectModalList/ConnectModalAccountsList'; import { ConnectModalDialogLoading } from './ConnectModalDialogLoading'; -import { useMetamask } from '../../../../hooks/useMetamask'; +import { useWalletConnection } from '../../../../hooks/useWalletConnection'; interface ConnectModalDialogProps { visible: boolean; @@ -12,20 +11,13 @@ interface ConnectModalDialogProps { } export const ConnectModalDialog = ({ visible, onClose }: ConnectModalDialogProps) => { - const { wallets, accounts, selectWallet, loading, selectedWallet } = useConnectWallet(); - - const { accounts: metamaskAccounts, selectedWallet: metamaskSelectedWallet } = useMetamask(); - const allWallets = [...(wallets || []), metamaskSelectedWallet]; - const allAccounts = [...(accounts || []), ...metamaskAccounts]; - - console.log('allWallets:', allWallets); - + const { allAccounts, allWallets, handleSelectWallet, loading, selectedWallet } = useWalletConnection(); const content = (
    Select Wallet - selectWallet(wallet)} onClose={onClose} /> +

    Want to know more? @@ -48,7 +40,7 @@ export const ConnectModalDialog = ({ visible, onClose }: ConnectModalDialogProps visible={visible} headerText={loading ? '' : 'Connect wallet'} onClose={() => { - selectWallet(undefined); + handleSelectWallet(undefined); onClose(); }} content={ diff --git a/src/hooks/useWalletConnection/index.tsx b/src/hooks/useWalletConnection/index.tsx new file mode 100644 index 00000000..775ac915 --- /dev/null +++ b/src/hooks/useWalletConnection/index.tsx @@ -0,0 +1,26 @@ +import { Wallet } from '@talismn/connect-wallets'; +import { useMetamask } from './useMetamask'; +import { useConnectWallet } from './useConnectWallet'; + +export function useWalletConnection() { + const { wallets = [], accounts = [], selectWallet, loading, selectedWallet } = useConnectWallet(); + const { + accounts: metamaskAccounts, + selectedWallet: metamaskSelectedWallet, + selectWallet: metamaskSelectWallet, + } = useMetamask(); + + const selectWalletHandlers = [ + { condition: (wallet?: Wallet) => wallet && wallet.extensionName === 'metamask', caller: metamaskSelectWallet }, + ]; + + const allWallets = [...wallets, metamaskSelectedWallet]; + const allAccounts = [...accounts, ...metamaskAccounts]; + + function handleSelectWallet(wallet?: Wallet) { + const handler = selectWalletHandlers.find((handler) => handler.condition(wallet)); + handler ? handler.caller() : selectWallet(wallet); + } + + return { allWallets, allAccounts, handleSelectWallet, loading, selectedWallet }; +} diff --git a/src/hooks/useConnectWallet/index.tsx b/src/hooks/useWalletConnection/useConnectWallet/index.tsx similarity index 88% rename from src/hooks/useConnectWallet/index.tsx rename to src/hooks/useWalletConnection/useConnectWallet/index.tsx index 7649e09e..35ae6e80 100644 --- a/src/hooks/useConnectWallet/index.tsx +++ b/src/hooks/useWalletConnection/useConnectWallet/index.tsx @@ -1,8 +1,8 @@ import { useEffect, useState } from 'preact/hooks'; import { Wallet, WalletAccount, getWallets } from '@talismn/connect-wallets'; import { useMutation } from '@tanstack/react-query'; -import { useGlobalState } from '../../GlobalStateProvider'; -import { ToastMessage, showToast } from '../../shared/showToast'; +import { useGlobalState } from '../../../GlobalStateProvider'; +import { ToastMessage, showToast } from '../../../shared/showToast'; export const useConnectWallet = () => { const [wallets, setWallets] = useState(); diff --git a/src/hooks/useMetamask.ts b/src/hooks/useWalletConnection/useMetamask/index.tsx similarity index 58% rename from src/hooks/useMetamask.ts rename to src/hooks/useWalletConnection/useMetamask/index.tsx index 0e04ac56..50fc68c5 100644 --- a/src/hooks/useMetamask.ts +++ b/src/hooks/useWalletConnection/useMetamask/index.tsx @@ -1,28 +1,24 @@ -import { useGlobalState } from '../GlobalStateProvider'; +import { useGlobalState } from '../../../GlobalStateProvider'; import { Wallet } from '@talismn/connect-wallets'; -import { useEffect, useMemo, useState } from 'preact/hooks'; -import { ExtensionAccount, initiateMetamaskInjectedAccount } from '../services/metamask/metamask'; -import logo from '../assets/metamask-wallet.png'; +import { useMemo, useState } from 'preact/hooks'; +import { ExtensionAccount, initiateMetamaskInjectedAccount } from '../../../services/metamask/metamask'; +import logo from '../../../assets/metamask-wallet.png'; export const useMetamask = () => { const { tenantName } = useGlobalState(); const [accounts, setAccounts] = useState([]); - useEffect(() => { - async function fetchAccounts() { - const injectedMetamaskAccount = (await initiateMetamaskInjectedAccount(tenantName)) as ExtensionAccount; - if (injectedMetamaskAccount) { - setAccounts([injectedMetamaskAccount]); - } + async function selectWallet() { + const injectedMetamaskAccount = (await initiateMetamaskInjectedAccount(tenantName)) as ExtensionAccount; + if (injectedMetamaskAccount) { + setAccounts([injectedMetamaskAccount]); } - - fetchAccounts(); - }, [tenantName]); + } const selectedWallet: Wallet = useMemo( () => ({ enable: () => undefined, - extensionName: 'polkadot-js', + extensionName: 'metamask', title: 'Metamask', installUrl: 'https://metamask.io/', logo: { @@ -41,5 +37,5 @@ export const useMetamask = () => { [tenantName], ); - return { selectedWallet, accounts }; + return { selectedWallet, accounts, selectWallet }; }; From 5604930964c95420734ce6a8829b173ca0255c9b Mon Sep 17 00:00:00 2001 From: Kacper Szarkiewicz Date: Thu, 27 Jun 2024 13:12:56 +0200 Subject: [PATCH 24/50] fix connect metamask logic --- src/hooks/useWalletConnection/useMetamask/index.tsx | 4 ++-- src/services/metamask/metamask.ts | 7 ++++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/hooks/useWalletConnection/useMetamask/index.tsx b/src/hooks/useWalletConnection/useMetamask/index.tsx index 50fc68c5..53d1e36b 100644 --- a/src/hooks/useWalletConnection/useMetamask/index.tsx +++ b/src/hooks/useWalletConnection/useMetamask/index.tsx @@ -9,9 +9,9 @@ export const useMetamask = () => { const [accounts, setAccounts] = useState([]); async function selectWallet() { - const injectedMetamaskAccount = (await initiateMetamaskInjectedAccount(tenantName)) as ExtensionAccount; + const injectedMetamaskAccount = (await initiateMetamaskInjectedAccount(tenantName)) as ExtensionAccount[]; if (injectedMetamaskAccount) { - setAccounts([injectedMetamaskAccount]); + setAccounts(injectedMetamaskAccount); } } diff --git a/src/services/metamask/metamask.ts b/src/services/metamask/metamask.ts index 0ed6cfcb..4e3a0507 100644 --- a/src/services/metamask/metamask.ts +++ b/src/services/metamask/metamask.ts @@ -106,10 +106,10 @@ const getSigner = (api: MetamaskSnapApi) => ({ }, }); -export async function initiateMetamaskInjectedAccount(tenantName: TenantName) { +export async function initiateMetamaskInjectedAccount(tenantName: TenantName): Promise { const provider = await getProvider(tenantName); - if (!provider?.snap) return undefined; + if (!provider?.snap) return []; const api = provider.snap.getMetamaskSnapApi(); const address = await api.getAddress(); @@ -119,5 +119,6 @@ export async function initiateMetamaskInjectedAccount(tenantName: TenantName) { source: WALLET_SOURCE_METAMASK, signer: getSigner(api), }; - return await buildWalletAccount(injectedMetamaskAccount); + + return await [buildWalletAccount(injectedMetamaskAccount)]; } From 53b5d3cf797a0dc7568ef7843ed7de59d63fc065 Mon Sep 17 00:00:00 2001 From: Kacper Szarkiewicz Date: Thu, 27 Jun 2024 13:23:50 +0200 Subject: [PATCH 25/50] unify selectWallet logic --- .../modals/ConnectModal/ConnectModalDialog.tsx | 6 +++--- src/hooks/useWalletConnection/index.tsx | 18 ++---------------- 2 files changed, 5 insertions(+), 19 deletions(-) diff --git a/src/components/Wallet/modals/ConnectModal/ConnectModalDialog.tsx b/src/components/Wallet/modals/ConnectModal/ConnectModalDialog.tsx index 8f68c788..1add969f 100644 --- a/src/components/Wallet/modals/ConnectModal/ConnectModalDialog.tsx +++ b/src/components/Wallet/modals/ConnectModal/ConnectModalDialog.tsx @@ -11,13 +11,13 @@ interface ConnectModalDialogProps { } export const ConnectModalDialog = ({ visible, onClose }: ConnectModalDialogProps) => { - const { allAccounts, allWallets, handleSelectWallet, loading, selectedWallet } = useWalletConnection(); + const { allAccounts, allWallets, selectWallet, loading, selectedWallet } = useWalletConnection(); const content = (

    Select Wallet - +

    Want to know more? @@ -40,7 +40,7 @@ export const ConnectModalDialog = ({ visible, onClose }: ConnectModalDialogProps visible={visible} headerText={loading ? '' : 'Connect wallet'} onClose={() => { - handleSelectWallet(undefined); + selectWallet(undefined); onClose(); }} content={ diff --git a/src/hooks/useWalletConnection/index.tsx b/src/hooks/useWalletConnection/index.tsx index 775ac915..b43ed33f 100644 --- a/src/hooks/useWalletConnection/index.tsx +++ b/src/hooks/useWalletConnection/index.tsx @@ -1,26 +1,12 @@ -import { Wallet } from '@talismn/connect-wallets'; import { useMetamask } from './useMetamask'; import { useConnectWallet } from './useConnectWallet'; export function useWalletConnection() { const { wallets = [], accounts = [], selectWallet, loading, selectedWallet } = useConnectWallet(); - const { - accounts: metamaskAccounts, - selectedWallet: metamaskSelectedWallet, - selectWallet: metamaskSelectWallet, - } = useMetamask(); - - const selectWalletHandlers = [ - { condition: (wallet?: Wallet) => wallet && wallet.extensionName === 'metamask', caller: metamaskSelectWallet }, - ]; + const { accounts: metamaskAccounts, selectedWallet: metamaskSelectedWallet } = useMetamask(); const allWallets = [...wallets, metamaskSelectedWallet]; const allAccounts = [...accounts, ...metamaskAccounts]; - function handleSelectWallet(wallet?: Wallet) { - const handler = selectWalletHandlers.find((handler) => handler.condition(wallet)); - handler ? handler.caller() : selectWallet(wallet); - } - - return { allWallets, allAccounts, handleSelectWallet, loading, selectedWallet }; + return { allWallets, allAccounts, selectWallet, loading, selectedWallet }; } From b1e8fb3652571cc7b0c8c9c513e274808376fb3e Mon Sep 17 00:00:00 2001 From: Kacper Szarkiewicz Date: Thu, 27 Jun 2024 13:28:26 +0200 Subject: [PATCH 26/50] improve metamask account creation --- src/services/metamask/metamask.ts | 20 +++----------------- 1 file changed, 3 insertions(+), 17 deletions(-) diff --git a/src/services/metamask/metamask.ts b/src/services/metamask/metamask.ts index 4e3a0507..29f43b44 100644 --- a/src/services/metamask/metamask.ts +++ b/src/services/metamask/metamask.ts @@ -4,7 +4,6 @@ import type { MetamaskPolkadotSnap } from '@chainsafe/metamask-polkadot-adapter/ import type { InjectedMetamaskExtension } from '@chainsafe/metamask-polkadot-adapter/src/types'; import { MetamaskSnapApi } from '@chainsafe/metamask-polkadot-adapter/build/types'; import type { SnapNetworks } from '@chainsafe/metamask-polkadot-types'; -import { Signer } from '@polkadot/api/types'; import { web3EnablePromise } from '@polkadot/extension-dapp'; import type { InjectedExtension } from '@polkadot/extension-inject/types'; import { SignerPayloadJSON, SignerPayloadRaw, SignerResult } from '@polkadot/types/types'; @@ -62,20 +61,6 @@ export async function initiatePolkadotSnap(relayChain: SnapNetworks): Promise ({ - address: extAcc.address, - source: extAcc.source, - name: extAcc.name, - signer: extAcc.signer as WalletAccount['signer'], - wallet: undefined, -}); const getProvider = async (tenantName: TenantName) => { const provider = await initiatePolkadotSnap(tenantToSnapNetwork(tenantName)); @@ -113,12 +98,13 @@ export async function initiateMetamaskInjectedAccount(tenantName: TenantName): P const api = provider.snap.getMetamaskSnapApi(); const address = await api.getAddress(); - const injectedMetamaskAccount: ExtensionAccount = { + const injectedMetamaskAccount: WalletAccount = { address, name: trimAddress(address), source: WALLET_SOURCE_METAMASK, signer: getSigner(api), + wallet: undefined, }; - return await [buildWalletAccount(injectedMetamaskAccount)]; + return await [injectedMetamaskAccount]; } From ec494b23c61ff39ffc021d122abffffe2315468c Mon Sep 17 00:00:00 2001 From: Kacper Szarkiewicz Date: Thu, 27 Jun 2024 13:32:25 +0200 Subject: [PATCH 27/50] refactor metamask snap connection logic --- src/GlobalStateProvider.tsx | 2 +- .../Wallet/wallets/MetamaskWallet/index.tsx | 2 +- .../useWalletConnection/useMetamask/index.tsx | 8 +++---- .../metamask/{metamask.ts => index.ts} | 22 ++----------------- src/services/metamask/isInstalled.ts | 18 +++++++++++++++ 5 files changed, 26 insertions(+), 26 deletions(-) rename src/services/metamask/{metamask.ts => index.ts} (77%) create mode 100644 src/services/metamask/isInstalled.ts diff --git a/src/GlobalStateProvider.tsx b/src/GlobalStateProvider.tsx index 58685727..92bd4c4c 100644 --- a/src/GlobalStateProvider.tsx +++ b/src/GlobalStateProvider.tsx @@ -9,7 +9,7 @@ import { storageKeys } from './constants/localStorage'; import { useLocalStorage } from './hooks/useLocalStorage'; import { TenantName } from './models/Tenant'; import { ThemeName } from './models/Theme'; -import { initiateMetamaskInjectedAccount, WALLET_SOURCE_METAMASK } from './services/metamask/metamask'; +import { initiateMetamaskInjectedAccount, WALLET_SOURCE_METAMASK } from './services/metamask'; import { storageService } from './services/storage/local'; import { walletConnectService } from './services/walletConnect'; diff --git a/src/components/Wallet/wallets/MetamaskWallet/index.tsx b/src/components/Wallet/wallets/MetamaskWallet/index.tsx index 508fed69..fda771a5 100644 --- a/src/components/Wallet/wallets/MetamaskWallet/index.tsx +++ b/src/components/Wallet/wallets/MetamaskWallet/index.tsx @@ -2,7 +2,7 @@ import { useEffect, useState } from 'preact/compat'; import { Button } from 'react-daisyui'; import { useGlobalState } from '../../../../GlobalStateProvider'; import logo from '../../../../assets/metamask-wallet.png'; -import { ExtensionAccount, buildWalletAccount } from '../../../../services/metamask/metamask'; +import { ExtensionAccount, buildWalletAccount } from '../../../../services/metamask'; import { PublicKey } from '../../../PublicKey'; import { Dialog } from '../../../../pages/collators/dialogs/Dialog'; diff --git a/src/hooks/useWalletConnection/useMetamask/index.tsx b/src/hooks/useWalletConnection/useMetamask/index.tsx index 53d1e36b..ab2aae07 100644 --- a/src/hooks/useWalletConnection/useMetamask/index.tsx +++ b/src/hooks/useWalletConnection/useMetamask/index.tsx @@ -1,15 +1,15 @@ import { useGlobalState } from '../../../GlobalStateProvider'; -import { Wallet } from '@talismn/connect-wallets'; +import { Wallet, WalletAccount } from '@talismn/connect-wallets'; import { useMemo, useState } from 'preact/hooks'; -import { ExtensionAccount, initiateMetamaskInjectedAccount } from '../../../services/metamask/metamask'; +import { initiateMetamaskInjectedAccount } from '../../../services/metamask'; import logo from '../../../assets/metamask-wallet.png'; export const useMetamask = () => { const { tenantName } = useGlobalState(); - const [accounts, setAccounts] = useState([]); + const [accounts, setAccounts] = useState([]); async function selectWallet() { - const injectedMetamaskAccount = (await initiateMetamaskInjectedAccount(tenantName)) as ExtensionAccount[]; + const injectedMetamaskAccount = await initiateMetamaskInjectedAccount(tenantName); if (injectedMetamaskAccount) { setAccounts(injectedMetamaskAccount); } diff --git a/src/services/metamask/metamask.ts b/src/services/metamask/index.ts similarity index 77% rename from src/services/metamask/metamask.ts rename to src/services/metamask/index.ts index 29f43b44..6b471f45 100644 --- a/src/services/metamask/metamask.ts +++ b/src/services/metamask/index.ts @@ -1,11 +1,8 @@ import { WalletAccount } from '@talismn/connect-wallets'; import { enablePolkadotSnap } from '@chainsafe/metamask-polkadot-adapter'; import type { MetamaskPolkadotSnap } from '@chainsafe/metamask-polkadot-adapter/build/snap'; -import type { InjectedMetamaskExtension } from '@chainsafe/metamask-polkadot-adapter/src/types'; import { MetamaskSnapApi } from '@chainsafe/metamask-polkadot-adapter/build/types'; import type { SnapNetworks } from '@chainsafe/metamask-polkadot-types'; -import { web3EnablePromise } from '@polkadot/extension-dapp'; -import type { InjectedExtension } from '@polkadot/extension-inject/types'; import { SignerPayloadJSON, SignerPayloadRaw, SignerResult } from '@polkadot/types/types'; import { TenantName } from '../../models/Tenant'; @@ -32,27 +29,12 @@ export async function installPolkadotSnap(relayChain: SnapNetworks): Promise { - return !!(await getInjectedMetamaskExtension()); -} - -export async function getInjectedMetamaskExtension(): Promise { - const extensions = await web3EnablePromise; - return getMetamaskExtension(extensions || []) || null; -} - -function getMetamaskExtension(extensions: InjectedExtension[]): InjectedMetamaskExtension | undefined { - return extensions.find((item) => item.name === 'metamask-polkadot-snap') as unknown as - | InjectedMetamaskExtension - | undefined; -} - -export interface SnapInitializationResponse { +export interface InitiatePolkadotSnapResponse { isSnapInstalled: boolean; snap?: MetamaskPolkadotSnap; } -export async function initiatePolkadotSnap(relayChain: SnapNetworks): Promise { +export async function initiatePolkadotSnap(relayChain: SnapNetworks): Promise { try { const metamaskPolkadotSnap = await enablePolkadotSnap({ networkName: relayChain }); return { isSnapInstalled: true, snap: metamaskPolkadotSnap }; diff --git a/src/services/metamask/isInstalled.ts b/src/services/metamask/isInstalled.ts new file mode 100644 index 00000000..d44470e1 --- /dev/null +++ b/src/services/metamask/isInstalled.ts @@ -0,0 +1,18 @@ +import { web3EnablePromise } from '@polkadot/extension-dapp'; +import type { InjectedExtension } from '@polkadot/extension-inject/types'; +import type { InjectedMetamaskExtension } from '@chainsafe/metamask-polkadot-adapter/src/types'; + +export async function isPolkadotSnapInstalled(): Promise { + return !!(await getInjectedMetamaskExtension()); +} + +export async function getInjectedMetamaskExtension(): Promise { + const extensions = await web3EnablePromise; + return getMetamaskExtension(extensions || []) || null; +} + +function getMetamaskExtension(extensions: InjectedExtension[]): InjectedMetamaskExtension | undefined { + return extensions.find((item) => item.name === 'metamask-polkadot-snap') as unknown as + | InjectedMetamaskExtension + | undefined; +} From 03d1585d99dfbbf4052d5c147750135d8fecb0ba Mon Sep 17 00:00:00 2001 From: Kacper Szarkiewicz Date: Thu, 27 Jun 2024 14:36:23 +0200 Subject: [PATCH 28/50] fix globalstate metamask connection, add metamask account disclaimer --- src/GlobalStateProvider.tsx | 8 +++---- src/assets/spacewalk.tsx | 2 +- .../ConnectModal/ConnectModalDialog.tsx | 15 +++++++++--- src/hooks/useWalletConnection/index.tsx | 5 ++-- .../useWalletConnection/useMetamask/index.tsx | 24 +++++++------------ src/services/metamask/index.ts | 6 ++--- 6 files changed, 30 insertions(+), 30 deletions(-) diff --git a/src/GlobalStateProvider.tsx b/src/GlobalStateProvider.tsx index 92bd4c4c..cb6da540 100644 --- a/src/GlobalStateProvider.tsx +++ b/src/GlobalStateProvider.tsx @@ -9,7 +9,7 @@ import { storageKeys } from './constants/localStorage'; import { useLocalStorage } from './hooks/useLocalStorage'; import { TenantName } from './models/Tenant'; import { ThemeName } from './models/Theme'; -import { initiateMetamaskInjectedAccount, WALLET_SOURCE_METAMASK } from './services/metamask'; +import { initiateMetamaskInjectedAccount, METAMASK_EXTENSION_NAME } from './services/metamask'; import { storageService } from './services/storage/local'; import { walletConnectService } from './services/walletConnect'; @@ -48,9 +48,9 @@ const initWalletConnect = async (chainId: string) => { const initMetamaskWallet = async (tenantName: TenantName) => { const metamaskWalletAddress = storageService.get(`metamask-snap-account`); if (metamaskWalletAddress) { - return await initiateMetamaskInjectedAccount(tenantName); + const injectedAccounts = await initiateMetamaskInjectedAccount(tenantName); + return injectedAccounts[0]; } - return; }; const GlobalStateProvider = ({ children }: { children: ComponentChildren }) => { @@ -104,7 +104,7 @@ const GlobalStateProvider = ({ children }: { children: ComponentChildren }) => { (wallet: WalletAccount | undefined) => { set(wallet?.address); setWallet(wallet); - if (wallet?.source === WALLET_SOURCE_METAMASK) { + if (wallet?.source === METAMASK_EXTENSION_NAME) { storageService.set(`metamask-snap-account`, wallet.address); } }, diff --git a/src/assets/spacewalk.tsx b/src/assets/spacewalk.tsx index c47991a8..c4c9b848 100644 --- a/src/assets/spacewalk.tsx +++ b/src/assets/spacewalk.tsx @@ -3,7 +3,7 @@ import { HTMLAttributes } from 'preact/compat'; const SpacewalkIcon = (props: HTMLAttributes) => ( - {' '} + ); diff --git a/src/components/Wallet/modals/ConnectModal/ConnectModalDialog.tsx b/src/components/Wallet/modals/ConnectModal/ConnectModalDialog.tsx index 1add969f..ad6585f1 100644 --- a/src/components/Wallet/modals/ConnectModal/ConnectModalDialog.tsx +++ b/src/components/Wallet/modals/ConnectModal/ConnectModalDialog.tsx @@ -4,6 +4,7 @@ import { ConnectModalWalletsList } from './ConnectModalList/ConnectModalWalletsL import { ConnectModalAccountsList } from './ConnectModalList/ConnectModalAccountsList'; import { ConnectModalDialogLoading } from './ConnectModalDialogLoading'; import { useWalletConnection } from '../../../../hooks/useWalletConnection'; +import { METAMASK_EXTENSION_NAME } from '../../../../services/metamask'; interface ConnectModalDialogProps { visible: boolean; @@ -11,13 +12,13 @@ interface ConnectModalDialogProps { } export const ConnectModalDialog = ({ visible, onClose }: ConnectModalDialogProps) => { - const { allAccounts, allWallets, selectWallet, loading, selectedWallet } = useWalletConnection(); + const { accounts, wallets, selectWallet, loading, selectedWallet } = useWalletConnection(); const content = (

    diff --git a/src/hooks/useWalletConnection/index.tsx b/src/hooks/useWalletConnection/index.tsx index b43ed33f..7680292f 100644 --- a/src/hooks/useWalletConnection/index.tsx +++ b/src/hooks/useWalletConnection/index.tsx @@ -3,10 +3,9 @@ import { useConnectWallet } from './useConnectWallet'; export function useWalletConnection() { const { wallets = [], accounts = [], selectWallet, loading, selectedWallet } = useConnectWallet(); - const { accounts: metamaskAccounts, selectedWallet: metamaskSelectedWallet } = useMetamask(); + const { selectedWallet: metamaskSelectedWallet } = useMetamask(); const allWallets = [...wallets, metamaskSelectedWallet]; - const allAccounts = [...accounts, ...metamaskAccounts]; - return { allWallets, allAccounts, selectWallet, loading, selectedWallet }; + return { wallets: allWallets, accounts, selectWallet, loading, selectedWallet }; } diff --git a/src/hooks/useWalletConnection/useMetamask/index.tsx b/src/hooks/useWalletConnection/useMetamask/index.tsx index ab2aae07..400d516e 100644 --- a/src/hooks/useWalletConnection/useMetamask/index.tsx +++ b/src/hooks/useWalletConnection/useMetamask/index.tsx @@ -1,24 +1,15 @@ import { useGlobalState } from '../../../GlobalStateProvider'; -import { Wallet, WalletAccount } from '@talismn/connect-wallets'; -import { useMemo, useState } from 'preact/hooks'; -import { initiateMetamaskInjectedAccount } from '../../../services/metamask'; +import { Wallet } from '@talismn/connect-wallets'; +import { useMemo } from 'preact/hooks'; +import { METAMASK_EXTENSION_NAME, initiateMetamaskInjectedAccount } from '../../../services/metamask'; import logo from '../../../assets/metamask-wallet.png'; export const useMetamask = () => { const { tenantName } = useGlobalState(); - const [accounts, setAccounts] = useState([]); - - async function selectWallet() { - const injectedMetamaskAccount = await initiateMetamaskInjectedAccount(tenantName); - if (injectedMetamaskAccount) { - setAccounts(injectedMetamaskAccount); - } - } const selectedWallet: Wallet = useMemo( () => ({ - enable: () => undefined, - extensionName: 'metamask', + extensionName: METAMASK_EXTENSION_NAME, title: 'Metamask', installUrl: 'https://metamask.io/', logo: { @@ -29,13 +20,14 @@ export const useMetamask = () => { extension: window.ethereum, signer: undefined, - getAccounts: () => initiateMetamaskInjectedAccount(tenantName), - subscribeAccounts: () => undefined, + getAccounts: () => initiateMetamaskInjectedAccount(tenantName), // we use polkadot-snap, getAccounts gets a polkadot-snap account linked to the user's metamask wallet + subscribeAccounts: () => undefined, // we use polkadot-snap, subscribeAccounts is not needed + enable: () => undefined, // we use polkadot-snap, enable is not needed transformError: (err: Error) => new Error(err.message), }), [tenantName], ); - return { selectedWallet, accounts, selectWallet }; + return { selectedWallet }; }; diff --git a/src/services/metamask/index.ts b/src/services/metamask/index.ts index 6b471f45..011cc620 100644 --- a/src/services/metamask/index.ts +++ b/src/services/metamask/index.ts @@ -1,14 +1,14 @@ import { WalletAccount } from '@talismn/connect-wallets'; +import { SignerPayloadJSON, SignerPayloadRaw, SignerResult } from '@polkadot/types/types'; import { enablePolkadotSnap } from '@chainsafe/metamask-polkadot-adapter'; import type { MetamaskPolkadotSnap } from '@chainsafe/metamask-polkadot-adapter/build/snap'; import { MetamaskSnapApi } from '@chainsafe/metamask-polkadot-adapter/build/types'; import type { SnapNetworks } from '@chainsafe/metamask-polkadot-types'; -import { SignerPayloadJSON, SignerPayloadRaw, SignerResult } from '@polkadot/types/types'; import { TenantName } from '../../models/Tenant'; import { trimAddress } from '../../helpers/addressFormatter'; -export const WALLET_SOURCE_METAMASK = 'metamask'; +export const METAMASK_EXTENSION_NAME = 'metamask'; export function tenantToSnapNetwork(tenantName: TenantName): SnapNetworks { const tenantNetworkMap: { [key in TenantName]?: SnapNetworks } = { @@ -83,7 +83,7 @@ export async function initiateMetamaskInjectedAccount(tenantName: TenantName): P const injectedMetamaskAccount: WalletAccount = { address, name: trimAddress(address), - source: WALLET_SOURCE_METAMASK, + source: METAMASK_EXTENSION_NAME, signer: getSigner(api), wallet: undefined, }; From b22456cde1258cfeea35dec17a3c016cd110d262 Mon Sep 17 00:00:00 2001 From: Kacper Szarkiewicz Date: Thu, 27 Jun 2024 18:01:12 +0200 Subject: [PATCH 29/50] remove unnecessary text --- .../Wallet/modals/ConnectModal/ConnectModalDialog.tsx | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/components/Wallet/modals/ConnectModal/ConnectModalDialog.tsx b/src/components/Wallet/modals/ConnectModal/ConnectModalDialog.tsx index ad6585f1..3b7d7208 100644 --- a/src/components/Wallet/modals/ConnectModal/ConnectModalDialog.tsx +++ b/src/components/Wallet/modals/ConnectModal/ConnectModalDialog.tsx @@ -19,12 +19,6 @@ export const ConnectModalDialog = ({ visible, onClose }: ConnectModalDialogProps Select Wallet -

    - Want to know more? - - Learn more about wallets - -

    From b2a57d4b06b19dd5758dadb3d0f6eb1a2dc92325 Mon Sep 17 00:00:00 2001 From: Kacper Szarkiewicz Date: Thu, 27 Jun 2024 18:27:07 +0200 Subject: [PATCH 30/50] fix GET TOKEN on mobile --- src/components/GetToken/index.tsx | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/components/GetToken/index.tsx b/src/components/GetToken/index.tsx index f40f75c6..b0de027f 100644 --- a/src/components/GetToken/index.tsx +++ b/src/components/GetToken/index.tsx @@ -78,6 +78,7 @@ export const GetToken = () => { const isBalanceZero = Number(balance) === 0; + const showCurrentToken = getTokenIcon(currentTenant) return (
    {isBalanceZero && ( @@ -90,8 +91,14 @@ export const GetToken = () => { {tokenSymbol ? ( ) : ( From feb2309f1b70c83c9b82af8818046a20a7ed406b Mon Sep 17 00:00:00 2001 From: Kacper Szarkiewicz Date: Mon, 1 Jul 2024 13:28:01 +0200 Subject: [PATCH 31/50] improve responsive design of buttons --- .../ConnectModalWalletsListItem.tsx | 8 ++++++-- .../ConnectModalList/ConnectModalWalletsList/index.tsx | 2 +- src/components/Wallet/modals/ConnectModal/index.tsx | 2 +- src/components/Wallet/wallets/WalletConnect/index.tsx | 4 ++-- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/components/Wallet/modals/ConnectModal/ConnectModalList/ConnectModalWalletsList/ConnectModalWalletsListItem.tsx b/src/components/Wallet/modals/ConnectModal/ConnectModalList/ConnectModalWalletsList/ConnectModalWalletsListItem.tsx index d4b9c862..1fed00d3 100644 --- a/src/components/Wallet/modals/ConnectModal/ConnectModalList/ConnectModalWalletsList/ConnectModalWalletsListItem.tsx +++ b/src/components/Wallet/modals/ConnectModal/ConnectModalList/ConnectModalWalletsList/ConnectModalWalletsListItem.tsx @@ -18,8 +18,12 @@ function buttonOnClick(props: WalletButtonProps) { } export const ConnectModalListWalletsItem = (props: WalletButtonProps) => ( - -); +); \ No newline at end of file diff --git a/src/components/Wallet/modals/ConnectModal/ConnectModalList/ConnectModalWalletsList/index.tsx b/src/components/Wallet/modals/ConnectModal/ConnectModalList/ConnectModalWalletsList/index.tsx index a9852ecf..5de8f94b 100644 --- a/src/components/Wallet/modals/ConnectModal/ConnectModalList/ConnectModalWalletsList/index.tsx +++ b/src/components/Wallet/modals/ConnectModal/ConnectModalList/ConnectModalWalletsList/index.tsx @@ -15,7 +15,7 @@ export function ConnectModalWalletsList({ wallets, onClick, makeInstallable, onC } return ( -
    +
    {wallets.map((wallet: Wallet) => ( { type="button" onClick={() => setVisible(true)} > - Connect Wallet + Connect Wallet { }, [provider]); return ( - ); }; From 242a57e7506cf86846f6ee1315cae498278ee1bf Mon Sep 17 00:00:00 2001 From: Kacper Szarkiewicz Date: Mon, 1 Jul 2024 15:18:57 +0200 Subject: [PATCH 32/50] hide accounts if wallet is not selected, remove required from Dialog actions prop, remove Metamask component --- .../ConnectModal/ConnectModalDialog.tsx | 76 +++++++++++-------- .../Wallet/wallets/MetamaskWallet/index.tsx | 59 -------------- .../Pools/Backstop/BackstopPoolModals.tsx | 1 - .../nabla/Pools/Swap/SwapPoolModals.tsx | 1 - .../useConnectWallet/index.tsx | 1 - src/pages/collators/dialogs/Dialog.tsx | 2 +- 6 files changed, 46 insertions(+), 94 deletions(-) delete mode 100644 src/components/Wallet/wallets/MetamaskWallet/index.tsx diff --git a/src/components/Wallet/modals/ConnectModal/ConnectModalDialog.tsx b/src/components/Wallet/modals/ConnectModal/ConnectModalDialog.tsx index 3b7d7208..e9898eaa 100644 --- a/src/components/Wallet/modals/ConnectModal/ConnectModalDialog.tsx +++ b/src/components/Wallet/modals/ConnectModal/ConnectModalDialog.tsx @@ -13,47 +13,61 @@ interface ConnectModalDialogProps { export const ConnectModalDialog = ({ visible, onClose }: ConnectModalDialogProps) => { const { accounts, wallets, selectWallet, loading, selectedWallet } = useWalletConnection(); + + const accountsContent = ( + + Choose Account + + + {selectedWallet?.extensionName === METAMASK_EXTENSION_NAME ? ( +

    + For Metamask connection we use Polkadot-Snap which creates only one Polkadot address for your Metamask + Wallet. +

    + ) : ( + <> + )} +
    +
    + ); + + const walletsContent = ( + + Select Wallet + + + + + ); + const content = (
    - - Select Wallet - - - - - - Choose Account - - - {selectedWallet?.extensionName === METAMASK_EXTENSION_NAME ? ( -

    - For Metamask connection we use Polkadot-Snap which creates only one Polkadot account for your Metamask - Wallet. -

    - ) : ( - <> - )} -
    -
    + {walletsContent} + {(accounts.length) ? accountsContent : <>}
    ); return ( - } onClose={() => { selectWallet(undefined); onClose(); }} - content={ - loading ? ( - - ) : ( - content - ) - } - actions={<>} /> - ); + ) + : ( + { + selectWallet(undefined); + onClose(); + }} + content={content} + /> + ) + ) }; diff --git a/src/components/Wallet/wallets/MetamaskWallet/index.tsx b/src/components/Wallet/wallets/MetamaskWallet/index.tsx deleted file mode 100644 index fda771a5..00000000 --- a/src/components/Wallet/wallets/MetamaskWallet/index.tsx +++ /dev/null @@ -1,59 +0,0 @@ -import { useEffect, useState } from 'preact/compat'; -import { Button } from 'react-daisyui'; -import { useGlobalState } from '../../../../GlobalStateProvider'; -import logo from '../../../../assets/metamask-wallet.png'; -import { ExtensionAccount, buildWalletAccount } from '../../../../services/metamask'; -import { PublicKey } from '../../../PublicKey'; -import { Dialog } from '../../../../pages/collators/dialogs/Dialog'; - -const MetamaskWallet = () => { - const [openModal, setOpenModal] = useState(false); - const [selectedAccount, setSelectedAccount] = useState(); - const { setWalletAccount } = useGlobalState(); - - useEffect(() => { - if (selectedAccount) { - const account = buildWalletAccount(selectedAccount); - setWalletAccount(account); - setOpenModal(false); - } - }, [selectedAccount, setWalletAccount]); - - const content = ( - <> - {accounts.map((a, i) => ( - - ))} - - ); - - return ( - <> - setOpenModal(false)} - actions={<>} - visible={openModal} - /> - - - ); -}; - -export default MetamaskWallet; diff --git a/src/components/nabla/Pools/Backstop/BackstopPoolModals.tsx b/src/components/nabla/Pools/Backstop/BackstopPoolModals.tsx index f0f456de..5f337e8e 100644 --- a/src/components/nabla/Pools/Backstop/BackstopPoolModals.tsx +++ b/src/components/nabla/Pools/Backstop/BackstopPoolModals.tsx @@ -24,7 +24,6 @@ export function BackstopPoolModals() { } content={Component ? : <>} /> ); diff --git a/src/components/nabla/Pools/Swap/SwapPoolModals.tsx b/src/components/nabla/Pools/Swap/SwapPoolModals.tsx index 1bec25b7..07798c16 100644 --- a/src/components/nabla/Pools/Swap/SwapPoolModals.tsx +++ b/src/components/nabla/Pools/Swap/SwapPoolModals.tsx @@ -28,7 +28,6 @@ export function SwapPoolModals() { } content={Component ? : <>} /> ); diff --git a/src/hooks/useWalletConnection/useConnectWallet/index.tsx b/src/hooks/useWalletConnection/useConnectWallet/index.tsx index 35ae6e80..a62f1eda 100644 --- a/src/hooks/useWalletConnection/useConnectWallet/index.tsx +++ b/src/hooks/useWalletConnection/useConnectWallet/index.tsx @@ -21,7 +21,6 @@ export const useConnectWallet = () => { } = useMutation(async (wallet) => { setSelectedWallet(wallet); if (!wallet) return []; - try { await wallet.enable(dAppName); return wallet.getAccounts(); diff --git a/src/pages/collators/dialogs/Dialog.tsx b/src/pages/collators/dialogs/Dialog.tsx index a42a2dd9..459669b5 100644 --- a/src/pages/collators/dialogs/Dialog.tsx +++ b/src/pages/collators/dialogs/Dialog.tsx @@ -8,7 +8,7 @@ interface DialogProps { onClose: () => void; headerText?: string; content: JSX.Element; - actions: JSX.Element; + actions?: JSX.Element; form?: { onSubmit: (event?: Event) => void | Promise; className?: string; From 8b75a7fd7d735da1401860c91e8cb46634f2c114 Mon Sep 17 00:00:00 2001 From: Kacper Szarkiewicz Date: Tue, 2 Jul 2024 19:51:17 +0200 Subject: [PATCH 33/50] refactor GlobalStateProvider, implement talisman wallets localstorage --- src/GlobalStateProvider/helpers.ts | 52 ++++++++++++ .../index.tsx} | 82 +++++-------------- src/TermsAndConditions.tsx | 4 +- .../Wallet/wallets/WalletConnect/index.tsx | 4 +- src/hooks/useLocalStorage.ts | 6 ++ .../useConnectWallet/index.tsx | 8 ++ 6 files changed, 90 insertions(+), 66 deletions(-) create mode 100644 src/GlobalStateProvider/helpers.ts rename src/{GlobalStateProvider.tsx => GlobalStateProvider/index.tsx} (51%) diff --git a/src/GlobalStateProvider/helpers.ts b/src/GlobalStateProvider/helpers.ts new file mode 100644 index 00000000..b1ed3f04 --- /dev/null +++ b/src/GlobalStateProvider/helpers.ts @@ -0,0 +1,52 @@ +import { getWalletBySource, WalletAccount } from '@talismn/connect-wallets'; +import { getSdkError } from '@walletconnect/utils'; +import { storageService } from '../services/storage/local'; +import { walletConnectService } from '../services/walletConnect'; +import { initiateMetamaskInjectedAccount } from '../services/metamask'; +import { chainIds } from '../config/walletConnect'; +import { TenantName } from '../models/Tenant'; +import { LocalStorageKeys } from '../hooks/useLocalStorage'; + +const initTalisman = async (dAppName: string, selected?: string) => { + const name = storageService.get(LocalStorageKeys.SELECTED_WALLET_NAME); + if (!name?.length) return; + const wallet = getWalletBySource(name); + if (!wallet) return; + await wallet.enable(dAppName); + const accounts = await wallet.getAccounts(); + const selectedWallet = accounts.find((a) => a.address === selected) || accounts[0]; + return selectedWallet; +}; + +const initWalletConnect = async (chainId: string) => { + const provider = await walletConnectService.getProvider(); + if (!provider?.session) return; + return await walletConnectService.init(provider?.session, chainId); +}; + +const initMetamask = async (tenantName: TenantName) => { + const metamaskWalletAddress = storageService.get(LocalStorageKeys.SELECTED_WALLET_NAME); + if (metamaskWalletAddress) { + const injectedAccounts = await initiateMetamaskInjectedAccount(tenantName); + return injectedAccounts[0]; + } +}; + +export const initSelectedWallet = async (dAppName: TenantName, tenantName: TenantName, storageAddress: string) => { + const appName = dAppName || TenantName.Amplitude; + return (await initTalisman(appName, storageAddress)) || + (await initWalletConnect(chainIds[tenantName])) || + (await initMetamask(tenantName)); +} + +export const handleWalletConnectDisconnect = async (walletAccount: WalletAccount | undefined) => { + if (walletAccount?.wallet?.extensionName === 'WalletConnect') { + const topic = walletConnectService.session?.topic; + if (topic) { + await walletConnectService.provider?.client.disconnect({ + topic, + reason: getSdkError('USER_DISCONNECTED'), + }); + } + } +} \ No newline at end of file diff --git a/src/GlobalStateProvider.tsx b/src/GlobalStateProvider/index.tsx similarity index 51% rename from src/GlobalStateProvider.tsx rename to src/GlobalStateProvider/index.tsx index cb6da540..0c9a0faf 100644 --- a/src/GlobalStateProvider.tsx +++ b/src/GlobalStateProvider/index.tsx @@ -1,17 +1,14 @@ -import { getWalletBySource, WalletAccount } from '@talismn/connect-wallets'; -import { getSdkError } from '@walletconnect/utils'; import { ComponentChildren, createContext } from 'preact'; import { useCallback, useContext, useEffect, useMemo, useRef, useState } from 'preact/compat'; import { useLocation } from 'react-router-dom'; -import { config } from './config'; -import { chainIds } from './config/walletConnect'; -import { storageKeys } from './constants/localStorage'; -import { useLocalStorage } from './hooks/useLocalStorage'; -import { TenantName } from './models/Tenant'; -import { ThemeName } from './models/Theme'; -import { initiateMetamaskInjectedAccount, METAMASK_EXTENSION_NAME } from './services/metamask'; -import { storageService } from './services/storage/local'; -import { walletConnectService } from './services/walletConnect'; +import { WalletAccount } from '@talismn/connect-wallets'; +import { config } from '../config'; +import { storageKeys } from '../constants/localStorage'; +import { LocalStorageKeys, useLocalStorage } from '../hooks/useLocalStorage'; +import { TenantName } from '../models/Tenant'; +import { ThemeName } from '../models/Theme'; +import { storageService } from '../services/storage/local'; +import { handleWalletConnectDisconnect, initSelectedWallet } from './helpers'; const SECONDS_IN_A_DAY = 86400; const EXPIRATION_PERIOD = 2 * SECONDS_IN_A_DAY; // 2 days @@ -29,30 +26,6 @@ export interface GlobalState { export const defaultTenant = TenantName.Pendulum; const GlobalStateContext = createContext(undefined); -const initTalisman = async (dAppName: string, selected?: string) => { - const name = storageService.get('@talisman-connect/selected-wallet-name'); - if (!name?.length) return; - const wallet = getWalletBySource(name); - if (!wallet) return; - await wallet.enable(dAppName); - const accounts = await wallet.getAccounts(); - const selectedWallet = accounts.find((a) => a.address === selected) || accounts[0]; - return selectedWallet; -}; -const initWalletConnect = async (chainId: string) => { - const provider = await walletConnectService.getProvider(); - if (!provider?.session) return; - return await walletConnectService.init(provider?.session, chainId); -}; - -const initMetamaskWallet = async (tenantName: TenantName) => { - const metamaskWalletAddress = storageService.get(`metamask-snap-account`); - if (metamaskWalletAddress) { - const injectedAccounts = await initiateMetamaskInjectedAccount(tenantName); - return injectedAccounts[0]; - } -}; - const GlobalStateProvider = ({ children }: { children: ComponentChildren }) => { const tenantRef = useRef(); const [walletAccount, setWallet] = useState(undefined); @@ -70,6 +43,7 @@ const GlobalStateProvider = ({ children }: { children: ComponentChildren }) => { [tenantName], ); + // Get currently selected wallet account from local storage const { state: storageAddress, set, @@ -79,34 +53,22 @@ const GlobalStateProvider = ({ children }: { children: ComponentChildren }) => { expire: EXPIRATION_PERIOD, }); - const handleWalletConnectDisconnect = useCallback(async () => { - if (walletAccount?.wallet?.extensionName === 'WalletConnect') { - const topic = walletConnectService.session?.topic; - if (topic) { - await walletConnectService.provider?.client.disconnect({ - topic, - reason: getSdkError('USER_DISCONNECTED'), - }); - } - } - }, [walletAccount]); + + const clearLocalStorageWallets = () => { + storageService.remove(LocalStorageKeys.SELECTED_WALLET_NAME); + } const removeWalletAccount = useCallback(async () => { - await handleWalletConnectDisconnect(); + await handleWalletConnectDisconnect(walletAccount); clear(); - // remove talisman - storageService.remove('@talisman-connect/selected-wallet-name'); - storageService.remove(`metamask-snap-account`); + clearLocalStorageWallets() setWallet(undefined); - }, [clear, handleWalletConnectDisconnect]); + }, [clear, walletAccount]); const setWalletAccount = useCallback( - (wallet: WalletAccount | undefined) => { - set(wallet?.address); - setWallet(wallet); - if (wallet?.source === METAMASK_EXTENSION_NAME) { - storageService.set(`metamask-snap-account`, wallet.address); - } + (newWalletAccount: WalletAccount | undefined) => { + set(newWalletAccount?.address); + setWallet(newWalletAccount); }, [set], ); @@ -121,11 +83,7 @@ const GlobalStateProvider = ({ children }: { children: ComponentChildren }) => { // skip if tenant already initialized if (tenantRef.current === tenantName || accountAddress) return; tenantRef.current = tenantName; - const appName = dAppName || TenantName.Amplitude; - const selectedWallet = - (await initTalisman(appName, storageAddress)) || - (await initWalletConnect(chainIds[tenantName])) || - (await initMetamaskWallet(tenantName)); + const selectedWallet = await initSelectedWallet(dAppName, tenantName, storageAddress) if (selectedWallet) setWallet(selectedWallet); }; run(); diff --git a/src/TermsAndConditions.tsx b/src/TermsAndConditions.tsx index 1e724911..b66a24d2 100644 --- a/src/TermsAndConditions.tsx +++ b/src/TermsAndConditions.tsx @@ -1,9 +1,9 @@ import { PropsWithChildren, useState } from 'preact/compat'; import { Button, Checkbox, Link, Modal } from 'react-daisyui'; -import { useLocalStorage } from './hooks/useLocalStorage'; +import { useLocalStorage, LocalStorageKeys } from './hooks/useLocalStorage'; const TermsAndConditions = (_props: PropsWithChildren) => { - const { state, set } = useLocalStorage({ key: 'termsAndConditions' }); + const { state, set } = useLocalStorage({ key: LocalStorageKeys.TERMS_AND_CONDITIONS }); const [checked, setChecked] = useState(false); const acceptTerms = () => { diff --git a/src/components/Wallet/wallets/WalletConnect/index.tsx b/src/components/Wallet/wallets/WalletConnect/index.tsx index 5892ea92..bb9a12de 100644 --- a/src/components/Wallet/wallets/WalletConnect/index.tsx +++ b/src/components/Wallet/wallets/WalletConnect/index.tsx @@ -1,6 +1,7 @@ import { WalletConnectModal } from '@walletconnect/modal'; import UniversalProvider from '@walletconnect/universal-provider'; import { SessionTypes } from '@walletconnect/types'; +import { Button } from 'react-daisyui'; import { useCallback, useEffect, useState } from 'preact/compat'; import logo from '../../../../assets/wallet-connect.svg'; import { config } from '../../../../config'; @@ -8,7 +9,6 @@ import { chainIds, walletConnectConfig } from '../../../../config/walletConnect' import { useGlobalState } from '../../../../GlobalStateProvider'; import { walletConnectService } from '../../../../services/walletConnect'; import { ToastMessage, showToast } from '../../../../shared/showToast'; -import { Button } from 'react-daisyui'; interface WalletConnectProps { onClick: () => void; @@ -82,7 +82,7 @@ const WalletConnect = ({ onClick }: WalletConnectProps) => { }, [provider]); return ( - diff --git a/src/hooks/useLocalStorage.ts b/src/hooks/useLocalStorage.ts index 2690831b..37d11d59 100644 --- a/src/hooks/useLocalStorage.ts +++ b/src/hooks/useLocalStorage.ts @@ -103,3 +103,9 @@ export const useLocalStorage = ({ return { state, set, merge, clear }; }; + + +export enum LocalStorageKeys { + TERMS_AND_CONDITIONS = "TERMS_AND_CONDITIONS", + SELECTED_WALLET_NAME = "SELECTED_WALLET_NAME", +} \ No newline at end of file diff --git a/src/hooks/useWalletConnection/useConnectWallet/index.tsx b/src/hooks/useWalletConnection/useConnectWallet/index.tsx index a62f1eda..9b8dfcad 100644 --- a/src/hooks/useWalletConnection/useConnectWallet/index.tsx +++ b/src/hooks/useWalletConnection/useConnectWallet/index.tsx @@ -3,6 +3,8 @@ import { Wallet, WalletAccount, getWallets } from '@talismn/connect-wallets'; import { useMutation } from '@tanstack/react-query'; import { useGlobalState } from '../../../GlobalStateProvider'; import { ToastMessage, showToast } from '../../../shared/showToast'; +import { storageService } from '../../../services/storage/local'; +import { LocalStorageKeys } from '../../useLocalStorage'; export const useConnectWallet = () => { const [wallets, setWallets] = useState(); @@ -23,6 +25,12 @@ export const useConnectWallet = () => { if (!wallet) return []; try { await wallet.enable(dAppName); + + // Save selected wallet name to local storage + if(wallet.installed){ + storageService.set(LocalStorageKeys.SELECTED_WALLET_NAME, wallet.extensionName); + } + return wallet.getAccounts(); } catch { showToast(ToastMessage.WALLET_ALREADY_OPEN_PENDING_CONNECTION); From ee2fa7dd4860ac959e8b84da540df23a44377dd6 Mon Sep 17 00:00:00 2001 From: Kacper Szarkiewicz Date: Tue, 2 Jul 2024 20:01:53 +0200 Subject: [PATCH 34/50] always show talisman, subwallet, polkadotjs wallets --- src/hooks/useWalletConnection/useConnectWallet/index.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/hooks/useWalletConnection/useConnectWallet/index.tsx b/src/hooks/useWalletConnection/useConnectWallet/index.tsx index 9b8dfcad..6c033929 100644 --- a/src/hooks/useWalletConnection/useConnectWallet/index.tsx +++ b/src/hooks/useWalletConnection/useConnectWallet/index.tsx @@ -6,13 +6,16 @@ import { ToastMessage, showToast } from '../../../shared/showToast'; import { storageService } from '../../../services/storage/local'; import { LocalStorageKeys } from '../../useLocalStorage'; + +const alwaysShowWallets = ['talisman', 'subwallet-js', 'polkadot-js'] + export const useConnectWallet = () => { const [wallets, setWallets] = useState(); const [selectedWallet, setSelectedWallet] = useState(); const { dAppName } = useGlobalState(); useEffect(() => { - const installedWallets = getWallets().filter((wallet) => wallet.installed); + const installedWallets = getWallets().filter((wallet) => alwaysShowWallets.includes(wallet.extensionName) || wallet.installed); setWallets(installedWallets); }, []); From 38a184e87de9b9667408f1f5895e7e68967a8504 Mon Sep 17 00:00:00 2001 From: Kacper Szarkiewicz Date: Tue, 2 Jul 2024 20:05:06 +0200 Subject: [PATCH 35/50] redirect to wallet install page if it not installed --- .../ConnectModalWalletsListItem.tsx | 11 ++++------- .../ConnectModalWalletsList/index.tsx | 6 ++---- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/src/components/Wallet/modals/ConnectModal/ConnectModalList/ConnectModalWalletsList/ConnectModalWalletsListItem.tsx b/src/components/Wallet/modals/ConnectModal/ConnectModalList/ConnectModalWalletsList/ConnectModalWalletsListItem.tsx index 1fed00d3..f2ba1850 100644 --- a/src/components/Wallet/modals/ConnectModal/ConnectModalList/ConnectModalWalletsList/ConnectModalWalletsListItem.tsx +++ b/src/components/Wallet/modals/ConnectModal/ConnectModalList/ConnectModalWalletsList/ConnectModalWalletsListItem.tsx @@ -4,23 +4,20 @@ import { Button } from 'react-daisyui'; interface WalletButtonProps { wallet: Wallet; onClick: (wallet: Wallet) => void; - makeInstallable?: boolean; } function buttonOnClick(props: WalletButtonProps) { - const { wallet, onClick, makeInstallable } = props; + const { wallet, onClick } = props; return wallet.installed - ? onClick?.(wallet) - : (!wallet.installed && wallet.extensionName === 'talisman') || makeInstallable - ? window.open(wallet.installUrl, '_blank', 'noopener,noreferrer') - : null; + ? onClick?.(wallet) : + window.open(wallet.installUrl, '_blank', 'noopener,noreferrer') } export const ConnectModalListWalletsItem = (props: WalletButtonProps) => ( -
  • - ); -}; diff --git a/src/components/AccountCard/index.tsx b/src/components/AccountCard/index.tsx index d5c775c4..36ac10cf 100644 --- a/src/components/AccountCard/index.tsx +++ b/src/components/AccountCard/index.tsx @@ -1,22 +1,23 @@ import { WalletAccount } from '@talismn/connect-wallets'; -import pendulumIcon from '../../assets/pendulum-icon.svg'; import { trimAddress } from '../../helpers/addressFormatter'; import { useGlobalState } from '../../GlobalStateProvider'; +import ChainLogo from '../../assets/ChainLogo'; interface AccountProps { account: WalletAccount; } -export const Account = ({ account }: AccountProps) => { +export const AccountCard = ({ account }: AccountProps) => { const { setWalletAccount } = useGlobalState(); + return (
  • diff --git a/src/components/Wallet/modals/ConnectModal/ConnectModalList/ConnectModalAccountsList/index.tsx b/src/components/Wallet/modals/ConnectModal/ConnectModalList/ConnectModalAccountsList/index.tsx index e03f84fd..14f4e369 100644 --- a/src/components/Wallet/modals/ConnectModal/ConnectModalList/ConnectModalAccountsList/index.tsx +++ b/src/components/Wallet/modals/ConnectModal/ConnectModalList/ConnectModalAccountsList/index.tsx @@ -1,7 +1,7 @@ import { WalletAccount } from '@talismn/connect-wallets'; import { useDeferredValue, useState } from 'preact/compat'; import { SearchInput } from '../../../../../SearchInput'; -import { SimpleAccountCard } from '../../../../../AccountCard/SimpleAccountCard'; +import { AccountCard } from '../../../../../AccountCard'; interface ConnectModalAccountsListProps { accounts: WalletAccount[]; @@ -20,7 +20,7 @@ export const ConnectModalAccountsList = ({ accounts }: ConnectModalAccountsListP
      {filteredAccounts.map((account: WalletAccount) => ( - + ))}
    From 8b48b6a34ef747bacfe01e3bc90c0cc9b0807a61 Mon Sep 17 00:00:00 2001 From: Kacper Szarkiewicz Date: Tue, 2 Jul 2024 21:41:31 +0200 Subject: [PATCH 37/50] refactor Nova Wallet connection --- .../Wallet/wallets/NovaWallet/index.tsx | 114 ------------------ src/hooks/useWalletConnection/index.tsx | 4 +- .../useWalletConnection/useNova/index.ts | 78 ++++++++++++ 3 files changed, 81 insertions(+), 115 deletions(-) delete mode 100644 src/components/Wallet/wallets/NovaWallet/index.tsx create mode 100644 src/hooks/useWalletConnection/useNova/index.ts diff --git a/src/components/Wallet/wallets/NovaWallet/index.tsx b/src/components/Wallet/wallets/NovaWallet/index.tsx deleted file mode 100644 index b7a7bf8e..00000000 --- a/src/components/Wallet/wallets/NovaWallet/index.tsx +++ /dev/null @@ -1,114 +0,0 @@ -import { web3Accounts, web3Enable, web3FromAddress } from '@polkadot/extension-dapp'; -import { WalletAccount } from '@talismn/connect-wallets'; -import { useCallback, useEffect, useState } from 'preact/compat'; -import { Modal } from 'react-daisyui'; -import logo from '../../../../assets/nova-wallet.png'; -import { GlobalState } from '../../../../GlobalStateProvider'; -import { PublicKey } from '../../../PublicKey'; - -export type NovaWalletProps = { - setWalletAccount: GlobalState['setWalletAccount']; -}; - -interface ExtensionAccount { - address: string; - name: string; - source: string; -} - -const NovaWallet = ({ setWalletAccount }: NovaWalletProps) => { - const [openModal, setOpenModal] = useState(false); - const [accounts, setAccounts] = useState([]); - const [selectedAccount, setSelectedAccount] = useState(); - - const onClick = useCallback(async () => { - const _ = await web3Enable('Pendulum Chain Portal'); - const allAccounts = await web3Accounts(); - setAccounts( - allAccounts - .filter(({ meta: { source } }) => source === 'polkadot-js') - .map( - ({ address, meta }): ExtensionAccount => ({ - address: address, - name: meta.name || 'My Account', - source: meta.source, - }), - ), - ); - setOpenModal(true); - }, [setOpenModal]); - - useEffect(() => { - async function buildWalletAccount(extAcc: ExtensionAccount) { - const signer = await web3FromAddress(extAcc.address); - return { - address: extAcc.address, - source: extAcc.source, - name: extAcc.name, - signer: signer as WalletAccount['signer'], - wallet: { - enable: () => undefined, - extensionName: 'polkadot-js', - title: 'Nova Wallet', - installUrl: 'https://novawallet.io/', - logo: { - src: logo, - alt: 'Nova Wallet', - }, - installed: true, - extension: undefined, - signer, - /** - * The following methods are tagged as 'Unused' since they are only required by the @talisman package, - * which we are not using to handle this wallet connection. - */ - getAccounts: () => Promise.resolve([]), // Unused - subscribeAccounts: () => undefined, // Unused - transformError: (err: Error) => err, // Unused - }, - }; - } - if (selectedAccount) { - buildWalletAccount(selectedAccount) - .then((account) => setWalletAccount(account)) - .then(() => { - setOpenModal(false); - }) - .catch((error) => console.error(error)); - } - }, [selectedAccount, setWalletAccount]); - - return ( -
    - -

    Select your Nova account

    -
    - {accounts.map((a, i) => ( - - ))} -
    -
    - -
    - ); -}; - -export default NovaWallet; diff --git a/src/hooks/useWalletConnection/index.tsx b/src/hooks/useWalletConnection/index.tsx index 7680292f..7329b43c 100644 --- a/src/hooks/useWalletConnection/index.tsx +++ b/src/hooks/useWalletConnection/index.tsx @@ -1,11 +1,13 @@ import { useMetamask } from './useMetamask'; import { useConnectWallet } from './useConnectWallet'; +import { useNova } from './useNova'; export function useWalletConnection() { const { wallets = [], accounts = [], selectWallet, loading, selectedWallet } = useConnectWallet(); const { selectedWallet: metamaskSelectedWallet } = useMetamask(); + const { selectedWallet: novaSelectedWallet } = useNova() - const allWallets = [...wallets, metamaskSelectedWallet]; + const allWallets = [...wallets, metamaskSelectedWallet, novaSelectedWallet]; return { wallets: allWallets, accounts, selectWallet, loading, selectedWallet }; } diff --git a/src/hooks/useWalletConnection/useNova/index.ts b/src/hooks/useWalletConnection/useNova/index.ts new file mode 100644 index 00000000..c5334312 --- /dev/null +++ b/src/hooks/useWalletConnection/useNova/index.ts @@ -0,0 +1,78 @@ +import { web3Accounts, web3Enable, web3FromAddress } from '@polkadot/extension-dapp'; +import { Wallet, WalletAccount } from '@talismn/connect-wallets'; +import { useMemo } from 'preact/hooks'; +import logo from '../../../assets/nova-wallet.png'; + +declare global { + interface Window { + injectedWeb3?: { + 'polkadot-js'?: boolean + [key: string]: unknown; + }; + walletExtension?: { + isNovaWallet?: boolean; + [key: string]: unknown; + }; + } + } + + +function isNovaInstalled() { + + const injectedExtension = window?.injectedWeb3?.['polkadot-js'] + const isNovaWallet = window?.walletExtension?.isNovaWallet + + return !!(injectedExtension && isNovaWallet) +} + +async function getAccounts(): Promise{ + const _ = await web3Enable('Pendulum Chain Portal'); + const accounts = await web3Accounts(); + + const walletAccounts = Promise.all(accounts + .filter(({ meta: { source } }) => source === 'polkadot-js') + .map( + async ({ address, meta }) => { + const signer = await web3FromAddress(address); + + const account: WalletAccount = { + address, + name: meta.name, + source: meta.source, + signer, + wallet: undefined + } + + return account; + })); + + return await walletAccounts +} + + +export const useNova = () => { + + const selectedWallet: Wallet = useMemo( + () => ({ + extensionName: 'polkadot-js', + title: 'Nova Wallet', + installUrl: 'https://novawallet.io/', + logo: { + src: logo, + alt: 'Nova Wallet', + }, + installed: isNovaInstalled(), + + extension: undefined, + signer: undefined, + getAccounts, + subscribeAccounts: () => undefined, // Unused + enable: () => undefined, + transformError: (err: Error) => new Error(err.message), + }), + + [], + ); + + return { selectedWallet }; +}; From a7a2e9cef39e1dc57c1a01013da142165a5ba0ce Mon Sep 17 00:00:00 2001 From: Kacper Szarkiewicz Date: Tue, 2 Jul 2024 21:49:45 +0200 Subject: [PATCH 38/50] use preact/compat instead of React --- src/assets/CloseIcon.tsx | 4 +++- src/components/Form/From/Badges/index.tsx | 3 ++- src/components/Layout/NavCollapseButtonContent.tsx | 3 ++- src/components/PublicKey/index.tsx | 10 +++++----- src/pages/bridge/Issue/SettingsDialog.tsx | 11 ++++++----- src/pages/bridge/Transactions.tsx | 6 +++--- src/pages/bridge/index.tsx | 14 +++++++------- .../StakingContent/StakingContent.tsx | 5 +++-- .../StakingRewardsContent/ClaimButton.tsx | 3 ++- .../StakingRewardsContent.tsx | 3 ++- .../StakingRewardsContent/UpdateButton.tsx | 3 ++- src/pages/collators/dialogs/Dialog.tsx | 4 ++-- src/pages/gas/GasForm.tsx | 4 ++-- src/pages/gas/GasSuccessDialog.tsx | 3 ++- 14 files changed, 43 insertions(+), 33 deletions(-) diff --git a/src/assets/CloseIcon.tsx b/src/assets/CloseIcon.tsx index d220e46e..3387345b 100644 --- a/src/assets/CloseIcon.tsx +++ b/src/assets/CloseIcon.tsx @@ -1,8 +1,10 @@ +import { FC } from "preact/compat"; + interface Props { className?: string; } -export const CloseIcon: React.FC = ({ className = 'dark:fill-white fill-black' }) => ( +export const CloseIcon: FC = ({ className = 'dark:fill-white fill-black' }) => ( diff --git a/src/components/Form/From/Badges/index.tsx b/src/components/Form/From/Badges/index.tsx index be300dda..98777f13 100644 --- a/src/components/Form/From/Badges/index.tsx +++ b/src/components/Form/From/Badges/index.tsx @@ -1,3 +1,4 @@ +import { FC } from 'preact/compat'; import useSwitchChain from '../../../../hooks/useSwitchChain'; import { TenantName } from '../../../../models/Tenant'; @@ -31,7 +32,7 @@ const getTenantColors = (tenantName: TenantName) => { return tenantColors[tenantName]; }; -export const Badges: React.FC = ({ minBadge, maxBadge, disabled }) => { +export const Badges: FC = ({ minBadge, maxBadge, disabled }) => { const { currentTenant } = useSwitchChain(); const colors = getTenantColors(currentTenant); diff --git a/src/components/Layout/NavCollapseButtonContent.tsx b/src/components/Layout/NavCollapseButtonContent.tsx index e850f546..0355a855 100644 --- a/src/components/Layout/NavCollapseButtonContent.tsx +++ b/src/components/Layout/NavCollapseButtonContent.tsx @@ -1,13 +1,14 @@ import Lottie from 'react-lottie'; import { LinkItem, isLottieOptions } from './links'; +import { FC } from 'preact/compat'; interface NavButtonContentProps { item: LinkItem; isPlaying: boolean; } -export const NavCollapseButtonContent: React.FC = ({ item, isPlaying }) => ( +export const NavCollapseButtonContent: FC = ({ item, isPlaying }) => ( <> {isLottieOptions(item.prefix) ? ( void; @@ -83,7 +83,7 @@ interface AddressProps { export const ClickableAddress = memo(function ClickableAddress(props: AddressProps) { return (