diff --git a/packages/dapp-kit-react/src/DAppKitProvider.tsx b/packages/dapp-kit-react/src/DAppKitProvider.tsx index 5d7c7ce9..530bf3d5 100644 --- a/packages/dapp-kit-react/src/DAppKitProvider.tsx +++ b/packages/dapp-kit-react/src/DAppKitProvider.tsx @@ -10,6 +10,7 @@ import type { WalletSource } from '@vechain/dapp-kit'; import { DAppKitUI } from '@vechain/dapp-kit-ui'; import { subscribeKey } from 'valtio/vanilla/utils'; import type { DAppKitProviderOptions, DAppKitContext } from './types'; +import { Certificate } from 'thor-devkit'; /** * Context @@ -69,6 +70,8 @@ export const DAppKitProvider: React.FC = ({ const [source, setSource] = useState( connex.wallet.state.source, ); + const [connectionCertificate, setConnectionCertificate] = + useState(connex.wallet.state.connectionCertificate); useEffect(() => { const addressSub = subscribeKey(connex.wallet.state, 'address', (v) => { @@ -77,10 +80,18 @@ export const DAppKitProvider: React.FC = ({ const sourceSub = subscribeKey(connex.wallet.state, 'source', (v) => { setSource(v); }); + const certificateSub = subscribeKey( + connex.wallet.state, + 'connectionCertificate', + (v) => { + setConnectionCertificate(v); + }, + ); return () => { addressSub(); sourceSub(); + certificateSub(); }; }, [connex.wallet.state]); @@ -110,6 +121,7 @@ export const DAppKitProvider: React.FC = ({ availableWallets: connex.wallet.state.availableSources, account, source, + connectionCertificate, }, modal: { open: openModal, @@ -117,7 +129,15 @@ export const DAppKitProvider: React.FC = ({ onConnectionStatusChange: onModalConnected, }, }; - }, [connex, account, source, closeModal, openModal, onModalConnected]); + }, [ + connex, + account, + source, + closeModal, + openModal, + onModalConnected, + connectionCertificate, + ]); return {children}; }; diff --git a/packages/dapp-kit-react/src/types.ts b/packages/dapp-kit-react/src/types.ts index 8a653d38..e44a89e1 100644 --- a/packages/dapp-kit-react/src/types.ts +++ b/packages/dapp-kit-react/src/types.ts @@ -1,5 +1,6 @@ /// import type React from 'react'; +import type { Certificate } from 'thor-devkit'; import type { ConnectResponse, WalletSource } from '@vechain/dapp-kit'; import { type DAppKitUIOptions } from '@vechain/dapp-kit-ui'; @@ -34,6 +35,7 @@ export interface DAppKitContext { connect: () => Promise; account: string | null; source: WalletSource | null; + connectionCertificate: Certificate | null; }; modal: { open: () => void; diff --git a/packages/dapp-kit/src/classes/certificate-wallet.ts b/packages/dapp-kit/src/classes/certificate-wallet.ts index 1a0350a9..e23b9d4f 100644 --- a/packages/dapp-kit/src/classes/certificate-wallet.ts +++ b/packages/dapp-kit/src/classes/certificate-wallet.ts @@ -6,7 +6,6 @@ import { DEFAULT_CONNECT_CERT_MESSAGE } from '../constants'; * A `ConnexWallet` for wallet's that use a certificate connection */ class CertificateBasedWallet implements ConnexWallet { - connectionCertificate?: Certificate; constructor(private readonly wallet: BaseWallet) {} connect = async (): Promise => { @@ -16,7 +15,7 @@ class CertificateBasedWallet implements ConnexWallet { signature, } = await this.signCert(cert, {}); - this.connectionCertificate = { + const connectionCertificate = { ...cert, signature, signer, @@ -25,12 +24,12 @@ class CertificateBasedWallet implements ConnexWallet { }; try { - Certificate.verify(this.connectionCertificate); + Certificate.verify(connectionCertificate); return { account: signer, verified: true, - connectionCertificate: this.connectionCertificate, + connectionCertificate, }; } catch (e) { return { diff --git a/packages/dapp-kit/src/classes/wallet-manager.ts b/packages/dapp-kit/src/classes/wallet-manager.ts index 3c1660ae..0b7b2132 100644 --- a/packages/dapp-kit/src/classes/wallet-manager.ts +++ b/packages/dapp-kit/src/classes/wallet-manager.ts @@ -66,10 +66,6 @@ class WalletManager { return wallet; } - get connectionCertificate(): Certificate | undefined { - return this.wallet.connectionCertificate; - } - // this is needed for wallet connect connections when a connection certificate is required signConnectionCertificate = async (): Promise => { const cert = DEFAULT_CONNECT_CERT_MESSAGE; @@ -78,7 +74,7 @@ class WalletManager { signature, } = await this.wallet.signCert(cert, {}); - this.wallet.connectionCertificate = { + const connectionCertificate = { ...cert, signature, signer, @@ -87,12 +83,13 @@ class WalletManager { }; try { - Certificate.verify(this.wallet.connectionCertificate); + Certificate.verify(connectionCertificate); this.state.address = signer; + this.state.connectionCertificate = connectionCertificate; return { account: signer, verified: true, - connectionCertificate: this.connectionCertificate, + connectionCertificate, }; } catch (e) { return { @@ -117,6 +114,8 @@ class WalletManager { this.options.walletConnectOptions.modal.askForConnectionCertificate(); } else { this.state.address = res.account; + this.state.connectionCertificate = + res.connectionCertificate ?? null; } return res; }) @@ -129,6 +128,7 @@ class WalletManager { if (!this.state.source) { this.state.source = null; this.state.address = null; + this.state.connectionCertificate = null; return; } @@ -153,6 +153,7 @@ class WalletManager { this.state.source = null; this.state.address = null; + this.state.connectionCertificate = null; }; signTx = ( @@ -162,6 +163,7 @@ class WalletManager { this.wallet .signTx(msg, options) .then((res) => { + // TODO: we should probably remove these assignment, because the user should be already logged in, and the address should be already defined, test it after e2e with transactions this.state.address = res.signer; return res; }) @@ -177,6 +179,7 @@ class WalletManager { this.wallet .signCert(msg, options) .then((res) => { + // TODO: we should probably remove these assignment, because the user should be already logged in, and the address should be already defined, test it after e2e with transactions this.state.address = res.annex.signer; return res; }) @@ -237,16 +240,19 @@ class WalletManager { source: null, address: null, availableSources, + connectionCertificate: null, }); } const address = Storage.getAccount(); const source = Storage.getSource(); + const connectionCertificate = Storage.getConnectionCertificate(); return proxy({ source, address, availableSources, + connectionCertificate, }); }; @@ -256,6 +262,10 @@ class WalletManager { } this.subscribeToKey('address', Storage.setAccount); this.subscribeToKey('source', Storage.setSource); + this.subscribeToKey( + 'connectionCertificate', + Storage.setConnectionCertificate, + ); }; private getAvailableSources = (): WalletSource[] => { diff --git a/packages/dapp-kit/src/types/types.d.ts b/packages/dapp-kit/src/types/types.d.ts index 3b1c4b25..1c341a96 100644 --- a/packages/dapp-kit/src/types/types.d.ts +++ b/packages/dapp-kit/src/types/types.d.ts @@ -48,7 +48,6 @@ type BaseWallet = Connex.Signer & { */ type ConnexWallet = BaseWallet & { connect: () => Promise; - connectionCertificate?: Certificate; }; interface ConnectResponse { @@ -61,6 +60,7 @@ interface WalletManagerState { source: WalletSource | null; address: string | null; availableSources: WalletSource[]; + connectionCertificate: Certificate | null; } export type { diff --git a/packages/dapp-kit/src/utils/local-storage.ts b/packages/dapp-kit/src/utils/local-storage.ts index 113e9738..038fbdd1 100644 --- a/packages/dapp-kit/src/utils/local-storage.ts +++ b/packages/dapp-kit/src/utils/local-storage.ts @@ -1,9 +1,11 @@ +import type { Certificate } from 'thor-devkit'; import type { WalletSource } from '../types'; import { DAppKitLogger } from './logger'; const STORAGE_PREFIX = 'dappkit@vechain'; const WALLET_SOURCE_KEY = `${STORAGE_PREFIX}/source`; const ACCOUNT_KEY = `${STORAGE_PREFIX}/account`; +const CERTIFICATE_KEY = `${STORAGE_PREFIX}/connectionCertificate`; const setSource = (source: WalletSource | null): void => { DAppKitLogger.debug('LocalStorage', 'setSource', source); @@ -24,6 +26,19 @@ const setAccount = (account: string | null): void => { } }; +const setConnectionCertificate = (certificate: Certificate | null): void => { + DAppKitLogger.debug( + 'LocalStorage', + 'setConnectionCertificate', + certificate, + ); + if (!certificate) { + localStorage.removeItem(CERTIFICATE_KEY); + } else { + localStorage.setItem(CERTIFICATE_KEY, JSON.stringify(certificate)); + } +}; + const getSource = (): WalletSource | null => { const source = localStorage.getItem(WALLET_SOURCE_KEY); @@ -44,9 +59,21 @@ const getAccount = (): string | null => { return account; }; +const getConnectionCertificate = (): Certificate | null => { + const connectionCertificate = localStorage.getItem(CERTIFICATE_KEY); + + if (!connectionCertificate) { + return null; + } + + return JSON.parse(connectionCertificate) as Certificate; +}; + export const Storage = { - getAccount, - getSource, setAccount, setSource, + setConnectionCertificate, + getAccount, + getSource, + getConnectionCertificate, };