diff --git a/apps/sample-next-app/src/app/pages/homepage.tsx b/apps/sample-next-app/src/app/pages/homepage.tsx index 31f9b803..744494c3 100644 --- a/apps/sample-next-app/src/app/pages/homepage.tsx +++ b/apps/sample-next-app/src/app/pages/homepage.tsx @@ -1,5 +1,5 @@ import { - ConnectWalletButtonWithModal, + ConnectButtonWithModal, DAppKitProvider, } from '@vechain/dapp-kit-react'; import type { WalletConnectOptions } from '@vechain/dapp-kit'; @@ -27,7 +27,7 @@ function HomePage() { usePersistence walletConnectOptions={walletConnectOptions} > - <ConnectWalletButtonWithModal />; + <ConnectButtonWithModal /> </DAppKitProvider> ); } diff --git a/apps/sample-react-app/src/App.tsx b/apps/sample-react-app/src/App.tsx index 10508b6f..e2f03ad0 100644 --- a/apps/sample-react-app/src/App.tsx +++ b/apps/sample-react-app/src/App.tsx @@ -1,7 +1,7 @@ import reactLogo from './assets/react.svg'; import viteLogo from '/vite.svg'; import './App.css'; -import { VechainDappConnectKit } from '@vechain/dapp-kit-react'; +import { ConnectButtonWithModal } from '@vechain/dapp-kit-react'; function App() { return ( @@ -20,7 +20,7 @@ function App() { </div> <h1>Vite + React</h1> <div className="card"> - <VechainDappConnectKit /> + <ConnectButtonWithModal /> </div> <p className="read-the-docs"> Click on the Vite and React logos to learn more diff --git a/packages/dapp-kit-react/.eslintrc.cjs b/packages/dapp-kit-react/.eslintrc.cjs index 7b4ac4c0..28e680d9 100644 --- a/packages/dapp-kit-react/.eslintrc.cjs +++ b/packages/dapp-kit-react/.eslintrc.cjs @@ -1,3 +1,6 @@ const Config = require('@vechain/repo-config'); -module.exports = Config.EslintReact; +module.exports = { + ...Config.EslintReact, + ignorePatterns: [...Config.EslintReact.ignorePatterns, '*.test.ts', 'test/**'] +}; diff --git a/packages/dapp-kit-react/src/Components/VechainDappConnectKit/VechainDappConnectKit.tsx b/packages/dapp-kit-react/src/Components/ConnectButtonWithModal/ConnectButtonWithModal.tsx similarity index 83% rename from packages/dapp-kit-react/src/Components/VechainDappConnectKit/VechainDappConnectKit.tsx rename to packages/dapp-kit-react/src/Components/ConnectButtonWithModal/ConnectButtonWithModal.tsx index d98eeae1..9bb2f446 100644 --- a/packages/dapp-kit-react/src/Components/VechainDappConnectKit/VechainDappConnectKit.tsx +++ b/packages/dapp-kit-react/src/Components/ConnectButtonWithModal/ConnectButtonWithModal.tsx @@ -3,7 +3,7 @@ import GlobalFonts from '../../../assets/fonts/fonts'; import { ThemeProvider } from '../../provider/ThemeProvider'; import { VechainDappConnectKitWithTheme } from './Wrapped/VechainDappConnectKitWrapped'; -export const VechainDappConnectKit: React.FC = (): React.ReactElement => { +export const ConnectButtonWithModal: React.FC = (): React.ReactElement => { return ( <ThemeProvider> <GlobalFonts /> diff --git a/packages/dapp-kit-react/src/Components/VechainDappConnectKit/Wrapped/VechainDappConnectKitWrapped.tsx b/packages/dapp-kit-react/src/Components/ConnectButtonWithModal/Wrapped/VechainDappConnectKitWrapped.tsx similarity index 100% rename from packages/dapp-kit-react/src/Components/VechainDappConnectKit/Wrapped/VechainDappConnectKitWrapped.tsx rename to packages/dapp-kit-react/src/Components/ConnectButtonWithModal/Wrapped/VechainDappConnectKitWrapped.tsx diff --git a/packages/dapp-kit-react/src/Components/ConnectButtonWithModal/index.ts b/packages/dapp-kit-react/src/Components/ConnectButtonWithModal/index.ts new file mode 100644 index 00000000..9f3ee594 --- /dev/null +++ b/packages/dapp-kit-react/src/Components/ConnectButtonWithModal/index.ts @@ -0,0 +1 @@ +export * from './ConnectButtonWithModal'; diff --git a/packages/dapp-kit-react/src/Components/ConnectWalletButtonWithModal/Components/ConnectButtonWithModal.tsx b/packages/dapp-kit-react/src/Components/ConnectWalletButtonWithModal/Components/ConnectButtonWithModal.tsx deleted file mode 100644 index 6492b4ac..00000000 --- a/packages/dapp-kit-react/src/Components/ConnectWalletButtonWithModal/Components/ConnectButtonWithModal.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import { useMemo } from 'react'; -import { useThemeSelector } from '../../../provider/ThemeProvider'; -import { createButtonWithModal } from './Wrapped/ConnectModalWithButtonWrapped'; - -export const ConnectButtonWithModal = () => { - const { theme } = useThemeSelector(); - - const ModalWithButton = useMemo(() => createButtonWithModal(), []); - - return <ModalWithButton mode={theme.mode} />; -}; diff --git a/packages/dapp-kit-react/src/Components/ConnectWalletButtonWithModal/Components/Wrapped/ConnectModalWithButtonWrapped.tsx b/packages/dapp-kit-react/src/Components/ConnectWalletButtonWithModal/Components/Wrapped/ConnectModalWithButtonWrapped.tsx deleted file mode 100644 index 4ecab9d9..00000000 --- a/packages/dapp-kit-react/src/Components/ConnectWalletButtonWithModal/Components/Wrapped/ConnectModalWithButtonWrapped.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import React from 'react'; -import { createComponent } from '@lit/react'; -import { ConnectButtonWithModal } from '@vechain/dapp-kit-ui'; - -export const createButtonWithModal = () => - createComponent({ - tagName: 'vwk-connect-button-with-modal', - elementClass: ConnectButtonWithModal, - react: React, - }); diff --git a/packages/dapp-kit-react/src/Components/ConnectWalletButtonWithModal/ConnectWalletButton.tsx b/packages/dapp-kit-react/src/Components/ConnectWalletButtonWithModal/ConnectWalletButton.tsx deleted file mode 100644 index 7165779b..00000000 --- a/packages/dapp-kit-react/src/Components/ConnectWalletButtonWithModal/ConnectWalletButton.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import React from 'react'; -import GlobalFonts from '../../../assets/fonts/fonts'; -import { ThemeProvider } from '../../provider/ThemeProvider'; -import { ConnectButtonWithModal } from './Components/ConnectButtonWithModal'; - -export const ConnectWalletButtonWithModal: React.FC = - (): React.ReactElement => { - return ( - <ThemeProvider> - <GlobalFonts /> - <ConnectButtonWithModal /> - </ThemeProvider> - ); - }; diff --git a/packages/dapp-kit-react/src/Components/ConnectWalletButtonWithModal/index.ts b/packages/dapp-kit-react/src/Components/ConnectWalletButtonWithModal/index.ts deleted file mode 100644 index f0725a9c..00000000 --- a/packages/dapp-kit-react/src/Components/ConnectWalletButtonWithModal/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './ConnectWalletButton'; diff --git a/packages/dapp-kit-react/src/Components/ConnectWalletModal/ConnectWalletModal.tsx b/packages/dapp-kit-react/src/Components/ConnectWalletModal/ConnectWalletModal.tsx deleted file mode 100644 index 07d39fd7..00000000 --- a/packages/dapp-kit-react/src/Components/ConnectWalletModal/ConnectWalletModal.tsx +++ /dev/null @@ -1,47 +0,0 @@ -import React, { useCallback, useMemo } from 'react'; -import { createComponent } from '@lit/react'; -import type { SourceInfo } from '@vechain/dapp-kit-ui'; -import { ConnectModal } from '@vechain/dapp-kit-ui'; -import type { ConnectResponse } from '@vechain/dapp-kit'; -import { useWallet } from '../../DAppKitProvider'; - -const createButtonWithModal = () => - createComponent({ - tagName: 'vwk-connect-modal', - elementClass: ConnectModal, - react: React, - }); - -interface ConnectWalletProps { - onConnectError?: (err: unknown) => void; - onConnected?: (res: ConnectResponse) => void; - isOpen: boolean; -} - -/** - * ConnectWalletModal - * - * This component allows the user to select a wallet and then connect. The account address should be available after the connection is successful. - */ - -export const ConnectWalletModal: React.FC<ConnectWalletProps> = ({ - onConnectError, - onConnected, - isOpen, -}) => { - const Modal = useMemo(() => createButtonWithModal(), []); - - const { setSource, connect } = useWallet(); - - const onSourceClick = useCallback( - (source?: SourceInfo) => { - if (source) { - setSource(source.id); - connect().then(onConnected).catch(onConnectError); - } - }, - [onConnectError, onConnected, connect, setSource], - ); - - return <Modal onSourceClick={onSourceClick} open={isOpen} />; -}; diff --git a/packages/dapp-kit-react/src/Components/ConnectWalletModal/index.ts b/packages/dapp-kit-react/src/Components/ConnectWalletModal/index.ts deleted file mode 100644 index 423a5eae..00000000 --- a/packages/dapp-kit-react/src/Components/ConnectWalletModal/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './ConnectWalletModal'; diff --git a/packages/dapp-kit-react/src/Components/SelectWalletModal/SelectWalletModal.tsx b/packages/dapp-kit-react/src/Components/SelectWalletModal/SelectWalletModal.tsx deleted file mode 100644 index 14331b39..00000000 --- a/packages/dapp-kit-react/src/Components/SelectWalletModal/SelectWalletModal.tsx +++ /dev/null @@ -1,44 +0,0 @@ -import React, { useCallback, useMemo } from 'react'; -import { createComponent } from '@lit/react'; -import type { SourceInfo } from '@vechain/dapp-kit-ui'; -import { ConnectModal } from '@vechain/dapp-kit-ui'; -import type { WalletSource } from '@vechain/dapp-kit'; -import { useWallet } from '../../DAppKitProvider'; - -const createButtonWithModal = () => - createComponent({ - tagName: 'vwk-connect-modal', - elementClass: ConnectModal, - react: React, - }); - -interface SelectWalletProps { - onSelected?: (source: WalletSource) => void; - isOpen: boolean; -} - -/** - * SelectWalletModal - * - * This component is used to select the wallet source. It will not attempt to connect to the wallet - */ -export const SelectWalletModal: React.FC<SelectWalletProps> = ({ - onSelected, - isOpen, -}) => { - const Modal = useMemo(() => createButtonWithModal(), []); - - const { setSource } = useWallet(); - - const onSourceClick = useCallback( - (source?: SourceInfo) => { - if (source) { - setSource(source.id); - onSelected?.(source.id); - } - }, - [onSelected, setSource], - ); - - return <Modal onSourceClick={onSourceClick} open={isOpen} />; -}; diff --git a/packages/dapp-kit-react/src/Components/SelectWalletModal/index.ts b/packages/dapp-kit-react/src/Components/SelectWalletModal/index.ts deleted file mode 100644 index 48f33157..00000000 --- a/packages/dapp-kit-react/src/Components/SelectWalletModal/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './SelectWalletModal'; diff --git a/packages/dapp-kit-react/src/Components/VechainDappConnectKit/index.ts b/packages/dapp-kit-react/src/Components/VechainDappConnectKit/index.ts deleted file mode 100644 index 45ab9aa9..00000000 --- a/packages/dapp-kit-react/src/Components/VechainDappConnectKit/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './VechainDappConnectKit'; diff --git a/packages/dapp-kit-react/src/Components/index.ts b/packages/dapp-kit-react/src/Components/index.ts index 757462f7..9f3ee594 100644 --- a/packages/dapp-kit-react/src/Components/index.ts +++ b/packages/dapp-kit-react/src/Components/index.ts @@ -1,4 +1 @@ -export * from './ConnectWalletButtonWithModal'; -export * from './ConnectWalletModal'; -export * from './SelectWalletModal'; -export * from './VechainDappConnectKit'; +export * from './ConnectButtonWithModal'; diff --git a/packages/dapp-kit-react/src/DAppKitProvider.tsx b/packages/dapp-kit-react/src/DAppKitProvider.tsx index a9fab983..5f165222 100644 --- a/packages/dapp-kit-react/src/DAppKitProvider.tsx +++ b/packages/dapp-kit-react/src/DAppKitProvider.tsx @@ -9,13 +9,12 @@ import React, { import type { WalletSource } from '@vechain/dapp-kit'; import { DAppKitUI } from '@vechain/dapp-kit-ui'; import { subscribeKey } from 'valtio/utils'; -import type { DAppKitProviderOptions } from './types'; -import { DAppKitContext } from './types'; +import type { DAppKitProviderOptions, DAppKitContext } from './types'; /** * Context */ -const DAppKitContext = createContext<DAppKitContext | undefined>(undefined); +const Context = createContext<DAppKitContext | undefined>(undefined); export const DAppKitProvider: React.FC<DAppKitProviderOptions> = ({ children, @@ -87,15 +86,11 @@ export const DAppKitProvider: React.FC<DAppKitProviderOptions> = ({ }; }, [connex, account, source, closeModal, openModal]); - return ( - <DAppKitContext.Provider value={context}> - {children} - </DAppKitContext.Provider> - ); + return <Context.Provider value={context}>{children}</Context.Provider>; }; export const useConnex = (): DAppKitContext['connex'] => { - const context = useContext(DAppKitContext); + const context = useContext(Context); if (!context) { throw new Error('"useConnex" must be used within a ConnexProvider'); @@ -105,7 +100,7 @@ export const useConnex = (): DAppKitContext['connex'] => { }; export const useWallet = (): DAppKitContext['wallet'] => { - const context = useContext(DAppKitContext); + const context = useContext(Context); if (!context) { throw new Error('"useWallet" must be used within a ConnexProvider'); @@ -115,7 +110,7 @@ export const useWallet = (): DAppKitContext['wallet'] => { }; export const useWalletModal = (): DAppKitContext['modal'] => { - const context = useContext(DAppKitContext); + const context = useContext(Context); if (!context) { throw new Error( diff --git a/packages/dapp-kit-react/test/helpers/mocked-signer.ts b/packages/dapp-kit-react/test/helpers/mocked-signer.ts new file mode 100644 index 00000000..63666c76 --- /dev/null +++ b/packages/dapp-kit-react/test/helpers/mocked-signer.ts @@ -0,0 +1,44 @@ +/// <reference types="@vechain/connex" /> +import { blake2b256, Certificate, HDNode, secp256k1 } from 'thor-devkit'; + +const mnemonicWords = + 'denial kitchen pet squirrel other broom bar gas better priority spoil cross'; + +const hdNode = HDNode.fromMnemonic(mnemonicWords.split(' ')); + +const firstAccount = hdNode.derive(0); + +const privateKey: Buffer = firstAccount.privateKey!; +const address = firstAccount.address; + +const mockedConnexSigner: Connex.Signer = { + signTx() { + return Promise.resolve({ txid: '0x1234', signer: address }); + }, + + signCert(msg) { + const certificate: Certificate = { + domain: ' localhost:3000', + timestamp: 12341234, + signer: address, + payload: msg.payload, + purpose: msg.purpose, + }; + + const signature = secp256k1.sign( + blake2b256(Certificate.encode(certificate)), + privateKey, + ); + + return Promise.resolve({ + annex: { + domain: certificate.domain, + timestamp: certificate.timestamp, + signer: certificate.signer, + }, + signature: `0x${signature.toString('hex')}`, + }); + }, +}; + +export { mockedConnexSigner, hdNode, mnemonicWords, privateKey, address }; diff --git a/packages/dapp-kit-react/test/helpers/react-test-helpers.tsx b/packages/dapp-kit-react/test/helpers/react-test-helpers.tsx index f9c7bae9..3c809d27 100644 --- a/packages/dapp-kit-react/test/helpers/react-test-helpers.tsx +++ b/packages/dapp-kit-react/test/helpers/react-test-helpers.tsx @@ -1,20 +1,11 @@ -import { - ConnectWalletModal, - DAppKitProvider, - SelectWalletModal, - VechainDappConnectKit, - ConnectWalletButtonWithModal, -} from '../../src'; +import { ConnectButtonWithModal, DAppKitProvider } from '../../src'; import { ThemeProvider } from '../../src/provider/ThemeProvider'; export const wrapper = ({ children }: { children?: React.ReactNode }) => ( <DAppKitProvider nodeUrl="https://testnet.vechain.org"> <ThemeProvider> {children} - <VechainDappConnectKit /> - <ConnectWalletButtonWithModal /> - <SelectWalletModal isOpen={false} /> - <ConnectWalletModal isOpen={false} /> + <ConnectButtonWithModal /> </ThemeProvider> </DAppKitProvider> ); diff --git a/packages/dapp-kit-react/test/useWallet.test.tsx b/packages/dapp-kit-react/test/useWallet.test.tsx index 372910de..725ed51b 100644 --- a/packages/dapp-kit-react/test/useWallet.test.tsx +++ b/packages/dapp-kit-react/test/useWallet.test.tsx @@ -1,8 +1,34 @@ -import { describe, it } from 'vitest'; +import { describe, it, vi } from 'vitest'; import { renderHook, waitFor } from '@testing-library/react'; +import { mockedConnexSigner } from '@vechain/dapp-kit/test/helpers/mocked-signer'; +import { Connex } from '@vechain/connex'; import { useWallet } from '../src'; import { wrapper } from './helpers/react-test-helpers'; +vi.mock('@vechain/connex'); + +vi.mocked(Connex.Vendor).mockImplementation((): Connex.Vendor => { + return { + // @ts-ignore + sign: (type, msg) => { + if (type === 'tx') { + return { + request: () => { + // @ts-ignore + return mockedConnexSigner.signTx(msg, {}); + }, + }; + } + return { + request: () => { + // @ts-ignore + return mockedConnexSigner.signCert(msg, {}); + }, + }; + }, + }; +}); + describe('useWallet', () => { it('should be able to set the source', async () => { const { result } = renderHook(() => useWallet(), { wrapper }); @@ -15,4 +41,29 @@ describe('useWallet', () => { expect(result.current.source).toBe('sync2'); }); }); + + it('should be able to connect & disconnect', async () => { + const { result } = renderHook(() => useWallet(), { wrapper }); + + expect(result.current).toBeDefined(); + + result.current.setSource('sync2'); + result.current.connect().catch(() => { + // ignore + }); + + await waitFor(() => { + expect(result.current.source).toBe('sync2'); + expect(result.current.account).toBe( + '0xf077b491b355e64048ce21e3a6fc4751eeea77fa', + ); + }); + + result.current.disconnect(); + + await waitFor(() => { + expect(result.current.source).toBe(null); + expect(result.current.account).toBeNull(); + }); + }); }); diff --git a/packages/dapp-kit-react/test/useWalletModal.test.tsx b/packages/dapp-kit-react/test/useWalletModal.test.tsx index 39b29808..96e29432 100644 --- a/packages/dapp-kit-react/test/useWalletModal.test.tsx +++ b/packages/dapp-kit-react/test/useWalletModal.test.tsx @@ -15,6 +15,16 @@ describe('useWalletModal', () => { const modal = window.document.querySelector('vwk-connect-modal'); expect(modal).toBeDefined(); + + expect(modal?.open).toBe(true); + }); + + result.current.close(); + + await waitFor(() => { + const modal = window.document.querySelector('vwk-connect-modal'); + + expect(modal?.open).toBe(false); }); }); }); diff --git a/packages/dapp-kit-ui/src/modal.ts b/packages/dapp-kit-ui/src/modal.ts index f39f454f..25593667 100644 --- a/packages/dapp-kit-ui/src/modal.ts +++ b/packages/dapp-kit-ui/src/modal.ts @@ -85,9 +85,8 @@ export class DAppKitModal { open(): void { DAppKitLogger.debug('DAppKitModal', 'opening the modal'); - const existingElement = window.document.querySelector( - 'vwk-vechain-dapp-connect-kit', - ); + const existingElement = + window.document.querySelector('vwk-connect-modal'); if (!existingElement) { DAppKitLogger.debug( @@ -96,9 +95,9 @@ export class DAppKitModal { 'creating a new element', ); - const element = window.document.createElement( - 'vwk-vechain-dapp-connect-kit', - ); + const element = window.document.createElement('vwk-connect-modal'); + + element.open = true; window.document.body.appendChild(element); }