Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove common logic from react #24

Merged
merged 2 commits into from
Oct 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/sample-react-app/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ const nodeOptions: Omit<Options, 'signer'> = {
};

const walletConnectOptions: WalletConnectOptions = {
projectId: '8dfc5cac972ee656e6edeb8309ab30ec',
projectId: 'a0b855ceaf109dbc8426479a4c3d38d8',
metadata: {
name: 'Sample VeChain dApp',
description: 'A sample VeChain dApp',
Expand Down
2 changes: 2 additions & 0 deletions apps/sample-react-app/src/Components/ConnectWalletModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ const ConnectedWalletBody: React.FC<ConnectedWalletBodyProps> = ({
},
};

if (!vendor) throw new Error('Vendor not available');

const certResponse = await vendor.sign('cert', message).request();

const cert: Certificate = {
Expand Down
11 changes: 5 additions & 6 deletions apps/sample-react-app/src/Hooks/useCounter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ interface UseCounter {
}

export const useCounter = (): UseCounter => {
const { vendor, thor } = useConnex();
const { thor } = useConnex();

const [count, setCount] = useState<number>(0);
const [status, setStatus] = useState<IncrementStatus>('idle');
Expand All @@ -55,12 +55,11 @@ export const useCounter = (): UseCounter => {
}, [setValue]);

const increment = useCallback(async (): Promise<void> => {
const clause = contract.method(_increment).asClause();

setStatus('in-wallet');

await vendor
.sign('tx', [clause])
await contract
.method(_increment)
.transact()
.delegate('https://sponsor-testnet.vechain.energy/by/90')
.request();

Expand All @@ -71,7 +70,7 @@ export const useCounter = (): UseCounter => {
await setValue()
.then(() => setStatus('idle'))
.catch(() => setStatus('error'));
}, [thor, vendor, contract, setValue]);
}, [thor, contract, setValue]);

return { count, increment, status, address: contract.address };
};
199 changes: 44 additions & 155 deletions packages/react-wallet-kit/src/ConnexProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,11 @@ import React, {
createContext,
useCallback,
useContext,
useEffect,
useMemo,
useReducer,
useRef,
} from 'react';
import { Connex } from '@vechain/connex';
import { newVendor } from '@vechain/connex-framework';
import type { WalletConnectOptions, WCSigner } from '@vechain/wallet-connect';
import {
newWcClient,
newWcSigner,
newWeb3Modal,
} from '@vechain/wallet-connect';
import { WalletSource } from '@vechain/wallet-kit';
import type { ConnexInstance } from '@vechain/wallet-kit';
import { createConnexInstance, WalletSource } from '@vechain/wallet-kit';
import { accountReducer, defaultAccountState } from './AccountReducer';
import type {
ConnexContext,
Expand All @@ -40,18 +31,35 @@ export const ConnexProvider: React.FC<ConnexProviderOptions> = ({
defaultAccountState,
);

const wcSignerRef = useRef<WCSigner | undefined>();
const onDisconnected = useCallback((): void => {
dispatch({ type: 'clear' });
}, []);

const disconnectMobile = useCallback(() => {
const client = wcSignerRef.current;
const connexInstance: ConnexInstance = useMemo(
() =>
createConnexInstance({
nodeUrl: nodeOptions.node,
genesis: nodeOptions.network,
source: accountState.source ?? undefined,
walletConnectOptions,
onDisconnected,
}),
//eslint-disable-next-line react-hooks/exhaustive-deps
[
nodeOptions.node,
nodeOptions.network,
walletConnectOptions,
onDisconnected,
],
);

if (client) {
client.disconnect().catch((err) => {
throw err;
});
wcSignerRef.current = undefined;
}
}, []);
const disconnect = useCallback((): void => {
dispatch({ type: 'clear' });

connexInstance.disconnect().catch(() => {
// do nothing
});
}, [connexInstance]);

const availableWallets = useMemo(() => {
const wallets: WalletSource[] = [WalletSource.Sync2];
Expand All @@ -71,132 +79,19 @@ export const ConnexProvider: React.FC<ConnexProviderOptions> = ({
return wallets;
}, [walletConnectOptions]);

const onDisconnected = useCallback((): void => {
if (accountState.source === WalletSource.WalletConnect) {
disconnectMobile();
}

dispatch({ type: 'clear' });
}, [disconnectMobile, accountState]);

useEffect(() => {
if (
accountState.source === WalletSource.WalletConnect &&
!walletConnectOptions
) {
onDisconnected();
}

if (
accountState.source === WalletSource.VeWorldExtension &&
!window.vechain
) {
onDisconnected();
}
}, [accountState, walletConnectOptions, onDisconnected]);

const updateSource: SetSource = useCallback(
const setSource: SetSource = useCallback(
(wallet: WalletSource): void => {
// We can't set VeWorld Mobile if there is no Wallet Connect config
if (
wallet === WalletSource.WalletConnect &&
!walletConnectOptions
) {
throw new Error('Wallet Connect config not found');
}

// We can't set VeWorld Extension if there is no VeChain extension
if (wallet === WalletSource.VeWorldExtension && !window.vechain) {
throw new Error('VeWorld extension not found');
}
connexInstance.setSource(wallet);

dispatch({
type: 'set-wallet-source',
payload: { source: wallet, persist: persistState },
});
},
[walletConnectOptions, persistState],
);

const thor: Connex.Thor = useMemo(
() => new Connex.Thor(nodeOptions),
[nodeOptions],
[connexInstance, persistState],
);

const createWalletConnectVendor = useCallback(
(options: WalletConnectOptions) => {
const { projectId, metadata } = options;

const wcClient = newWcClient({
projectId,
metadata,
});

const web3Modal = newWeb3Modal(projectId);

const wcSigner: WCSigner = newWcSigner({
genesisId: thor.genesis.id,
wcClient,
web3Modal,
onDisconnected,
});

wcSignerRef.current = wcSigner;

return newVendor(wcSigner);
},
[onDisconnected, thor.genesis.id],
);

/**
* Create the vendor
* Create a vendor for the provided options. If that vendor is not available, the priority is:
* 1. Wallet Connect - If the options are provided
* 2. VeWorld Extension - If the extension is available
* 3. Sync2 - As a fallback, as it is always available
*/
const vendor: Connex.Vendor = useMemo(() => {
const { source } = accountState;

if (source === WalletSource.WalletConnect && walletConnectOptions) {
return createWalletConnectVendor(walletConnectOptions);
}

if (source === WalletSource.Sync2 || source === WalletSource.Sync) {
return new Connex.Vendor(thor.genesis.id, source);
}

if (source === WalletSource.VeWorldExtension && window.vechain) {
const extensionSigner = window.vechain.newConnexSigner(
thor.genesis.id,
);

return newVendor(extensionSigner);
}

// We've exhausted all options, so default to Wallet Connect if the options are provided
if (walletConnectOptions) {
return createWalletConnectVendor(walletConnectOptions);
}

// No wallet connect options, so use the extension if it's available
if (window.vechain) {
const extensionSigner: Connex.Signer =
window.vechain.newConnexSigner(thor.genesis.id);

return newVendor(extensionSigner);
}

// Default to Sync2
return new Connex.Vendor(thor.genesis.id, WalletSource.Sync2);
}, [
createWalletConnectVendor,
thor.genesis.id,
walletConnectOptions,
accountState,
]);

const updateAccount: SetAccount = useCallback(
const setAccount: SetAccount = useCallback(
(address: string) => {
dispatch({
type: 'set-address',
Expand All @@ -206,40 +101,34 @@ export const ConnexProvider: React.FC<ConnexProviderOptions> = ({
[persistState],
);

const wallets: WalletSource[] = useMemo(() => {
return Object.values(WalletSource);
}, []);

const context: ConnexContext = useMemo(() => {
return {
connex: {
thor,
vendor,
thor: connexInstance.thor,
vendor: connexInstance.vendor,
},
wallet: {
setSource: updateSource,
setAccount: updateAccount,
setSource,
setAccount,
availableWallets,
wallets,
wallets: Object.values(WalletSource),
accountState,
disconnect: onDisconnected,
disconnect,
},
};
}, [
onDisconnected,
updateAccount,
accountState,
thor,
vendor,
updateSource,
wallets,
connexInstance,
setAccount,
setSource,
availableWallets,
disconnect,
]);

return <Context.Provider value={context}>{children}</Context.Provider>;
};

export const useConnex = (): Connex => {
export const useConnex = (): ConnexContext['connex'] => {
const context = useContext(Context);
return context.connex;
};
Expand Down
12 changes: 4 additions & 8 deletions packages/react-wallet-kit/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,17 +38,13 @@ export interface ConnexProviderOptions {
* Connex Context
* This context is used to provide the Connex instance and the Connex Vendor instance
* to the application.
*
* @param thor - {@link Connex.Thor}: used to interact with the blockchain
* @param vendor - {@link Connex.Vendor}: used to interact with the wallet
* @param setWallet - used to set the wallet source
* @param availableWallets - list of available wallets
* @param accountState - current account state
* @param dispatch - used to dispatch account actions
*/

export interface ConnexContext {
connex: Connex;
connex: {
thor: Connex.Thor;
vendor?: Connex.Vendor;
};
wallet: {
setSource: SetSource;
setAccount: SetAccount;
Expand Down
4 changes: 2 additions & 2 deletions packages/wallet-connect/src/signer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import type { EngineTypes } from '@walletconnect/types/dist/types/sign-client/en
import { getSdkError } from '@walletconnect/utils';
import type { SignClient } from '@walletconnect/sign-client/dist/types/client';
import type { WCSigner, WCSignerOptions } from './types';
import { DefaultEvents, DefaultMethods } from './constants';
import { DefaultMethods } from './constants';

interface SessionAccount {
networkIdentifier: string;
Expand Down Expand Up @@ -136,7 +136,7 @@ export const newWcSigner = ({
const namespace: ProposalTypes.RequiredNamespace = {
methods: Object.values(DefaultMethods),
chains: [chainId],
events: Object.values(DefaultEvents),
events: [],
};

try {
Expand Down
Loading