Skip to content

Commit b88d766

Browse files
authored
Merge pull request #402 from WatchItDev/app/refactor/quick-trasnsfer-amount
refactor: metamask handling
2 parents ab4a74d + e42ec9f commit b88d766

17 files changed

+248
-218
lines changed

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/App.tsx

Lines changed: 48 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ import { ResponsiveOverlay } from '@src/components/responsive-overlay';
4646

4747
import { Buffer } from 'buffer';
4848
import { Provider } from 'react-redux';
49+
import { MetaMaskProvider } from '@metamask/sdk-react';
4950

5051
window.Buffer = Buffer;
5152

@@ -67,32 +68,52 @@ export default function App() {
6768
useScrollToTop();
6869

6970
return (
70-
<LocalizationProvider dateAdapter={AdapterDateFns}>
71-
<SettingsProvider
72-
defaultSettings={{
73-
themeMode: 'dark', // 'light' | 'dark'
74-
themeDirection: 'ltr', // 'rtl' | 'ltr'
75-
themeContrast: 'default', // 'default' | 'bold'
76-
themeLayout: 'vertical', // 'vertical' | 'horizontal' | 'mini'
77-
themeColorPresets: 'default', // 'default' | 'cyan' | 'purple' | 'blue' | 'orange' | 'red'
78-
themeStretch: false,
79-
}}
80-
>
81-
<AuthProvider>
82-
<Provider store={store}>
83-
<ThemeProvider>
84-
<MotionLazy>
85-
<SnackbarProvider>
86-
<SettingsDrawer />
87-
<ProgressBar />
88-
<Router />
89-
<ResponsiveOverlay />
90-
</SnackbarProvider>
91-
</MotionLazy>
92-
</ThemeProvider>
93-
</Provider>
94-
</AuthProvider>
95-
</SettingsProvider>
96-
</LocalizationProvider>
71+
<MetaMaskProvider
72+
sdkOptions={{
73+
dappMetadata: {
74+
name: 'Watchit Dapp',
75+
url: window.location.href,
76+
},
77+
openDeeplink: (url) => {
78+
const isMM = (window as any).ethereum?.isMetaMask;
79+
if (typeof (window as any).ethereum === 'undefined' || !isMM) {
80+
// Mobile version / no extension
81+
window.location.href = 'https://metamask.app.link';
82+
} else {
83+
// Desktop with MetaMask extension
84+
window.location.href = url;
85+
}
86+
},
87+
// headless: true, // If we wanted to personalize our own modals
88+
}}
89+
>
90+
<LocalizationProvider dateAdapter={AdapterDateFns}>
91+
<SettingsProvider
92+
defaultSettings={{
93+
themeMode: 'dark', // 'light' | 'dark'
94+
themeDirection: 'ltr', // 'rtl' | 'ltr'
95+
themeContrast: 'default', // 'default' | 'bold'
96+
themeLayout: 'vertical', // 'vertical' | 'horizontal' | 'mini'
97+
themeColorPresets: 'default', // 'default' | 'cyan' | 'purple' | 'blue' | 'orange' | 'red'
98+
themeStretch: false,
99+
}}
100+
>
101+
<AuthProvider>
102+
<Provider store={store}>
103+
<ThemeProvider>
104+
<MotionLazy>
105+
<SnackbarProvider>
106+
<SettingsDrawer />
107+
<ProgressBar />
108+
<Router />
109+
<ResponsiveOverlay />
110+
</SnackbarProvider>
111+
</MotionLazy>
112+
</ThemeProvider>
113+
</Provider>
114+
</AuthProvider>
115+
</SettingsProvider>
116+
</LocalizationProvider>
117+
</MetaMaskProvider>
97118
);
98119
}

src/clients/viem/walletClient.ts

Lines changed: 0 additions & 27 deletions
This file was deleted.

src/hooks/use-authorize-policy.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ export const useAuthorizePolicy = (): useAuthorizePolicyHook => {
100100
setData(receipt);
101101
setLoading(false);
102102
} catch (err: any) {
103+
console.log('AUTHORIZE: ',err);
103104
setError(ERRORS.UNKNOWN_ERROR);
104105
setLoading(false);
105106
}

src/hooks/use-deposit-metamask.ts

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
import { useState } from 'react';
2-
import { parseUnits } from 'viem';
3-
import { ConnectWalletClient } from '@src/clients/viem/walletClient';
2+
import { Address, parseUnits } from 'viem';
43
import LedgerVaultAbi from '@src/config/abi/LedgerVault.json';
54
import MMCAbi from '@src/config/abi/MMC.json';
65
import { GLOBAL_CONSTANTS } from '@src/config-global';
76
import { publicClient } from '@src/clients/viem/publicClient';
87
import { ERRORS } from '@notifications/errors.ts';
98
import { notifyInfo } from '@notifications/internal-notifications.ts';
109
import { INFO } from '@notifications/info.ts';
10+
import { useMetaMask } from '@src/hooks/use-metamask.ts';
11+
// import { enqueueSnackbar } from 'notistack';
1112

1213
// SAME HERE
1314
interface DepositParams {
@@ -33,62 +34,61 @@ export const useDepositMetamask = (): UseDepositHook => {
3334
const [data, setData] = useState<any>(null);
3435
const [loading, setLoading] = useState<boolean>(false);
3536
const [error, setError] = useState<keyof typeof ERRORS | null>(null);
37+
const { walletClient, account: address } = useMetaMask();
3638

3739
const deposit = async ({ recipient, amount }: DepositParams) => {
40+
if (!address) return;
41+
3842
setLoading(true);
3943
setError(null);
4044

4145
try {
42-
// 1) Connect to the wallet (MetaMask)
43-
const walletClient = await ConnectWalletClient();
44-
45-
// 2) Retrieve the user's address from the wallet
46-
const [senderAddress] = await walletClient.requestAddresses();
47-
48-
// 3) Convert the amount to Wei (18 decimals for MMC)
46+
// 1) Convert the amount to Wei (18 decimals for MMC)
4947
const weiAmount = parseUnits(amount.toString(), 18);
5048

5149
// Notify the user that we are sending approve transaction to the network
5250
notifyInfo(INFO.APPROVE_SENDING_CONFIRMATION, { options: { autoHideDuration: 3000 } });
5351

54-
// 4) First transaction: approve
55-
const approveTxHash = await walletClient.writeContract({
52+
// 2) First transaction: approve
53+
const approveTxHash = await walletClient?.writeContract({
5654
address: GLOBAL_CONSTANTS.MMC_ADDRESS,
5755
abi: MMCAbi.abi,
5856
functionName: 'approve',
5957
args: [GLOBAL_CONSTANTS.LEDGER_VAULT_ADDRESS, weiAmount],
60-
account: senderAddress,
58+
chain: undefined,
59+
account: address,
6160
});
6261

6362
// Notify the user that we are now waiting for the approve transaction to be confirmed
6463
notifyInfo(INFO.APPROVE_WAITING_CONFIRMATION, { options: { autoHideDuration: 7000 } });
6564

6665
// Wait for the approve transaction to be mined
6766
const approveReceipt = await publicClient.waitForTransactionReceipt({
68-
hash: approveTxHash,
67+
hash: approveTxHash as Address,
6968
});
7069

7170
// Notify the user that we are now sending the deposit transaction
7271
notifyInfo(INFO.DEPOSIT_SENDING_CONFIRMATION, { options: { autoHideDuration: 3000 } });
7372

74-
// 5) Second transaction: deposit
75-
const depositTxHash = await walletClient.writeContract({
73+
// 3) Second transaction: deposit
74+
const depositTxHash = await walletClient?.writeContract({
7675
address: GLOBAL_CONSTANTS.LEDGER_VAULT_ADDRESS,
7776
abi: LedgerVaultAbi.abi,
7877
functionName: 'deposit',
7978
args: [recipient, weiAmount, GLOBAL_CONSTANTS.MMC_ADDRESS],
80-
account: senderAddress,
79+
chain: undefined,
80+
account: address,
8181
});
8282

8383
// Notify the user that we are now waiting for the deposit transaction to be confirmed
8484
notifyInfo(INFO.DEPOSIT_WAITING_CONFIRMATION, { options: { autoHideDuration: 7000 } });
8585

8686
// Wait for the deposit transaction to be mined
8787
const depositReceipt = await publicClient.waitForTransactionReceipt({
88-
hash: depositTxHash,
88+
hash: depositTxHash as Address,
8989
});
9090

91-
// 6) Store data about both transactions
91+
// 4) Store data about both transactions
9292
setData({
9393
approveTxHash,
9494
depositTxHash,
@@ -97,6 +97,9 @@ export const useDepositMetamask = (): UseDepositHook => {
9797
});
9898
} catch (err: any) {
9999
// If something fails (either approve or deposit), set an error
100+
console.log('DEPOSIT FAILING ERROR: ', err);
101+
// const errorMessage = typeof err === 'object' ? JSON.stringify(err) : String(err);
102+
// enqueueSnackbar(`DEPOSIT FAILING ERROR: ${errorMessage}`);
100103
setError(ERRORS.UNKNOWN_ERROR);
101104
throw err;
102105
} finally {

src/hooks/use-deposit.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,11 @@ export const useDeposit = (): UseDepositHook => {
102102
setData(receipt);
103103
setLoading(false);
104104
} catch (err: any) {
105+
105106
setError(ERRORS.UNKNOWN_ERROR);
107+
108+
console.error('USE DEPOSIT:', err);
109+
106110
setLoading(false);
107111
}
108112
};

src/hooks/use-metamask.ts

Lines changed: 106 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,108 @@
1-
import { useEffect, useState } from 'react';
2-
import { Address } from 'viem';
3-
import { connectToMetaMask } from '@src/utils/metamask';
4-
import { notifyError } from '@notifications/internal-notifications.ts';
5-
import { ERRORS } from '@notifications/errors.ts';
6-
7-
export const useMetaMask = () => {
8-
const [address, setAddress] = useState<Address | undefined>();
9-
const [connecting, setConnecting] = useState(false);
10-
11-
useEffect(() => {
12-
const walletConnected = localStorage.getItem('walletConnected');
13-
if (walletConnected === 'true') {
14-
connect();
15-
}
16-
}, []);
17-
18-
const connect = async () => {
19-
setConnecting(true);
20-
try {
21-
const walletAddress = await connectToMetaMask();
22-
setAddress(walletAddress);
23-
localStorage.setItem('walletConnected', 'true');
24-
} catch (err) {
25-
notifyError(ERRORS.METAMASK_CONNECTING_ERROR);
26-
} finally {
27-
setConnecting(false);
1+
// REACT IMPORTS
2+
import { useCallback } from 'react';
3+
4+
// VIEM IMPORTS
5+
import { createWalletClient, custom, WalletClient } from 'viem';
6+
import type { Address } from 'viem';
7+
import { polygonAmoy } from 'viem/chains';
8+
9+
// METAMASK IMPORTS
10+
import { useSDK } from '@metamask/sdk-react';
11+
// import { enqueueSnackbar } from 'notistack';
12+
13+
/**
14+
* Represents the shape of the object returned by the useMetaMask hook.
15+
*/
16+
interface UseMetaMaskReturn {
17+
/**
18+
* Initiates the MetaMask connection flow via the MetaMask SDK.
19+
* Returns a list of connected accounts if successful.
20+
*
21+
* Throws an error if the MetaMask SDK isn't yet initialized.
22+
*/
23+
connect: () => Promise<string[] | undefined>;
24+
25+
/**
26+
* Indicates whether the user is currently connected to MetaMask.
27+
*/
28+
connected: boolean;
29+
30+
/**
31+
* The address of the connected account, if any.
32+
*/
33+
account?: Address;
34+
35+
/**
36+
* The chain ID of the currently connected network.
37+
*/
38+
chainId?: string;
39+
40+
/**
41+
* A viem WalletClient instance that uses the MetaMask provider as transport.
42+
*/
43+
walletClient?: WalletClient;
44+
45+
/**
46+
* Indicates if the connection flow is still in progress.
47+
*/
48+
loading: boolean;
49+
50+
/**
51+
* Contains any error that occurred during connection (if applicable).
52+
*/
53+
error?: Error;
54+
}
55+
56+
/**
57+
* Custom React hook that leverages the MetaMask React SDK
58+
* along with viem to create a WalletClient for the Polygon Amoy chain.
59+
*
60+
* @returns An object with methods and states for handling the MetaMask connection.
61+
*/
62+
export function useMetaMask(): UseMetaMaskReturn {
63+
const {
64+
sdk,
65+
connected,
66+
chainId: sdkChainId,
67+
account,
68+
connecting,
69+
error,
70+
provider,
71+
} = useSDK();
72+
73+
// useEffect(() => {
74+
// if (error) {
75+
// const errorMessage = typeof error === 'object' ? JSON.stringify(error) : String(error);
76+
// enqueueSnackbar(`METAMASK ERROR: ${errorMessage}`);
77+
// }
78+
// }, [error]);
79+
80+
/**
81+
* We define 'connect' as a guaranteed function (non-optional).
82+
* If the sdk is not ready, this method will throw an error.
83+
*/
84+
const connect = useCallback(async (): Promise<string[] | undefined> => {
85+
if (!sdk) {
86+
throw new Error('MetaMask SDK is not initialized.');
2887
}
29-
};
88+
return sdk.connect();
89+
}, [sdk]);
3090

31-
return { address, connecting, connect, setAddress };
32-
};
91+
// Generate a viem wallet client using the MetaMask provider, if available.
92+
const walletClient = provider
93+
? createWalletClient({
94+
chain: polygonAmoy,
95+
transport: custom(provider),
96+
})
97+
: undefined;
98+
99+
return {
100+
connect,
101+
connected,
102+
account: account as Address,
103+
chainId: sdkChainId,
104+
walletClient,
105+
loading: connecting,
106+
error,
107+
};
108+
}

0 commit comments

Comments
 (0)