Skip to content

Commit

Permalink
Merge pull request #402 from WatchItDev/app/refactor/quick-trasnsfer-…
Browse files Browse the repository at this point in the history
…amount

refactor: metamask handling
  • Loading branch information
geolffreym authored Jan 10, 2025
2 parents ab4a74d + e42ec9f commit b88d766
Show file tree
Hide file tree
Showing 17 changed files with 248 additions and 218 deletions.
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

75 changes: 48 additions & 27 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import { ResponsiveOverlay } from '@src/components/responsive-overlay';

import { Buffer } from 'buffer';
import { Provider } from 'react-redux';
import { MetaMaskProvider } from '@metamask/sdk-react';

window.Buffer = Buffer;

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

return (
<LocalizationProvider dateAdapter={AdapterDateFns}>
<SettingsProvider
defaultSettings={{
themeMode: 'dark', // 'light' | 'dark'
themeDirection: 'ltr', // 'rtl' | 'ltr'
themeContrast: 'default', // 'default' | 'bold'
themeLayout: 'vertical', // 'vertical' | 'horizontal' | 'mini'
themeColorPresets: 'default', // 'default' | 'cyan' | 'purple' | 'blue' | 'orange' | 'red'
themeStretch: false,
}}
>
<AuthProvider>
<Provider store={store}>
<ThemeProvider>
<MotionLazy>
<SnackbarProvider>
<SettingsDrawer />
<ProgressBar />
<Router />
<ResponsiveOverlay />
</SnackbarProvider>
</MotionLazy>
</ThemeProvider>
</Provider>
</AuthProvider>
</SettingsProvider>
</LocalizationProvider>
<MetaMaskProvider
sdkOptions={{
dappMetadata: {
name: 'Watchit Dapp',
url: window.location.href,
},
openDeeplink: (url) => {
const isMM = (window as any).ethereum?.isMetaMask;
if (typeof (window as any).ethereum === 'undefined' || !isMM) {
// Mobile version / no extension
window.location.href = 'https://metamask.app.link';
} else {
// Desktop with MetaMask extension
window.location.href = url;
}
},
// headless: true, // If we wanted to personalize our own modals
}}
>
<LocalizationProvider dateAdapter={AdapterDateFns}>
<SettingsProvider
defaultSettings={{
themeMode: 'dark', // 'light' | 'dark'
themeDirection: 'ltr', // 'rtl' | 'ltr'
themeContrast: 'default', // 'default' | 'bold'
themeLayout: 'vertical', // 'vertical' | 'horizontal' | 'mini'
themeColorPresets: 'default', // 'default' | 'cyan' | 'purple' | 'blue' | 'orange' | 'red'
themeStretch: false,
}}
>
<AuthProvider>
<Provider store={store}>
<ThemeProvider>
<MotionLazy>
<SnackbarProvider>
<SettingsDrawer />
<ProgressBar />
<Router />
<ResponsiveOverlay />
</SnackbarProvider>
</MotionLazy>
</ThemeProvider>
</Provider>
</AuthProvider>
</SettingsProvider>
</LocalizationProvider>
</MetaMaskProvider>
);
}
27 changes: 0 additions & 27 deletions src/clients/viem/walletClient.ts

This file was deleted.

1 change: 1 addition & 0 deletions src/hooks/use-authorize-policy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ export const useAuthorizePolicy = (): useAuthorizePolicyHook => {
setData(receipt);
setLoading(false);
} catch (err: any) {
console.log('AUTHORIZE: ',err);
setError(ERRORS.UNKNOWN_ERROR);
setLoading(false);
}
Expand Down
39 changes: 21 additions & 18 deletions src/hooks/use-deposit-metamask.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import { useState } from 'react';
import { parseUnits } from 'viem';
import { ConnectWalletClient } from '@src/clients/viem/walletClient';
import { Address, parseUnits } from 'viem';
import LedgerVaultAbi from '@src/config/abi/LedgerVault.json';
import MMCAbi from '@src/config/abi/MMC.json';
import { GLOBAL_CONSTANTS } from '@src/config-global';
import { publicClient } from '@src/clients/viem/publicClient';
import { ERRORS } from '@notifications/errors.ts';
import { notifyInfo } from '@notifications/internal-notifications.ts';
import { INFO } from '@notifications/info.ts';
import { useMetaMask } from '@src/hooks/use-metamask.ts';
// import { enqueueSnackbar } from 'notistack';

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

const deposit = async ({ recipient, amount }: DepositParams) => {
if (!address) return;

setLoading(true);
setError(null);

try {
// 1) Connect to the wallet (MetaMask)
const walletClient = await ConnectWalletClient();

// 2) Retrieve the user's address from the wallet
const [senderAddress] = await walletClient.requestAddresses();

// 3) Convert the amount to Wei (18 decimals for MMC)
// 1) Convert the amount to Wei (18 decimals for MMC)
const weiAmount = parseUnits(amount.toString(), 18);

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

// 4) First transaction: approve
const approveTxHash = await walletClient.writeContract({
// 2) First transaction: approve
const approveTxHash = await walletClient?.writeContract({
address: GLOBAL_CONSTANTS.MMC_ADDRESS,
abi: MMCAbi.abi,
functionName: 'approve',
args: [GLOBAL_CONSTANTS.LEDGER_VAULT_ADDRESS, weiAmount],
account: senderAddress,
chain: undefined,
account: address,
});

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

// Wait for the approve transaction to be mined
const approveReceipt = await publicClient.waitForTransactionReceipt({
hash: approveTxHash,
hash: approveTxHash as Address,
});

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

// 5) Second transaction: deposit
const depositTxHash = await walletClient.writeContract({
// 3) Second transaction: deposit
const depositTxHash = await walletClient?.writeContract({
address: GLOBAL_CONSTANTS.LEDGER_VAULT_ADDRESS,
abi: LedgerVaultAbi.abi,
functionName: 'deposit',
args: [recipient, weiAmount, GLOBAL_CONSTANTS.MMC_ADDRESS],
account: senderAddress,
chain: undefined,
account: address,
});

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

// Wait for the deposit transaction to be mined
const depositReceipt = await publicClient.waitForTransactionReceipt({
hash: depositTxHash,
hash: depositTxHash as Address,
});

// 6) Store data about both transactions
// 4) Store data about both transactions
setData({
approveTxHash,
depositTxHash,
Expand All @@ -97,6 +97,9 @@ export const useDepositMetamask = (): UseDepositHook => {
});
} catch (err: any) {
// If something fails (either approve or deposit), set an error
console.log('DEPOSIT FAILING ERROR: ', err);
// const errorMessage = typeof err === 'object' ? JSON.stringify(err) : String(err);
// enqueueSnackbar(`DEPOSIT FAILING ERROR: ${errorMessage}`);
setError(ERRORS.UNKNOWN_ERROR);
throw err;
} finally {
Expand Down
4 changes: 4 additions & 0 deletions src/hooks/use-deposit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,11 @@ export const useDeposit = (): UseDepositHook => {
setData(receipt);
setLoading(false);
} catch (err: any) {

setError(ERRORS.UNKNOWN_ERROR);

console.error('USE DEPOSIT:', err);

setLoading(false);
}
};
Expand Down
136 changes: 106 additions & 30 deletions src/hooks/use-metamask.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,108 @@
import { useEffect, useState } from 'react';
import { Address } from 'viem';
import { connectToMetaMask } from '@src/utils/metamask';
import { notifyError } from '@notifications/internal-notifications.ts';
import { ERRORS } from '@notifications/errors.ts';

export const useMetaMask = () => {
const [address, setAddress] = useState<Address | undefined>();
const [connecting, setConnecting] = useState(false);

useEffect(() => {
const walletConnected = localStorage.getItem('walletConnected');
if (walletConnected === 'true') {
connect();
}
}, []);

const connect = async () => {
setConnecting(true);
try {
const walletAddress = await connectToMetaMask();
setAddress(walletAddress);
localStorage.setItem('walletConnected', 'true');
} catch (err) {
notifyError(ERRORS.METAMASK_CONNECTING_ERROR);
} finally {
setConnecting(false);
// REACT IMPORTS
import { useCallback } from 'react';

// VIEM IMPORTS
import { createWalletClient, custom, WalletClient } from 'viem';
import type { Address } from 'viem';
import { polygonAmoy } from 'viem/chains';

// METAMASK IMPORTS
import { useSDK } from '@metamask/sdk-react';
// import { enqueueSnackbar } from 'notistack';

/**
* Represents the shape of the object returned by the useMetaMask hook.
*/
interface UseMetaMaskReturn {
/**
* Initiates the MetaMask connection flow via the MetaMask SDK.
* Returns a list of connected accounts if successful.
*
* Throws an error if the MetaMask SDK isn't yet initialized.
*/
connect: () => Promise<string[] | undefined>;

/**
* Indicates whether the user is currently connected to MetaMask.
*/
connected: boolean;

/**
* The address of the connected account, if any.
*/
account?: Address;

/**
* The chain ID of the currently connected network.
*/
chainId?: string;

/**
* A viem WalletClient instance that uses the MetaMask provider as transport.
*/
walletClient?: WalletClient;

/**
* Indicates if the connection flow is still in progress.
*/
loading: boolean;

/**
* Contains any error that occurred during connection (if applicable).
*/
error?: Error;
}

/**
* Custom React hook that leverages the MetaMask React SDK
* along with viem to create a WalletClient for the Polygon Amoy chain.
*
* @returns An object with methods and states for handling the MetaMask connection.
*/
export function useMetaMask(): UseMetaMaskReturn {
const {
sdk,
connected,
chainId: sdkChainId,
account,
connecting,
error,
provider,
} = useSDK();

// useEffect(() => {
// if (error) {
// const errorMessage = typeof error === 'object' ? JSON.stringify(error) : String(error);
// enqueueSnackbar(`METAMASK ERROR: ${errorMessage}`);
// }
// }, [error]);

/**
* We define 'connect' as a guaranteed function (non-optional).
* If the sdk is not ready, this method will throw an error.
*/
const connect = useCallback(async (): Promise<string[] | undefined> => {
if (!sdk) {
throw new Error('MetaMask SDK is not initialized.');
}
};
return sdk.connect();
}, [sdk]);

return { address, connecting, connect, setAddress };
};
// Generate a viem wallet client using the MetaMask provider, if available.
const walletClient = provider
? createWalletClient({
chain: polygonAmoy,
transport: custom(provider),
})
: undefined;

return {
connect,
connected,
account: account as Address,
chainId: sdkChainId,
walletClient,
loading: connecting,
error,
};
}
Loading

0 comments on commit b88d766

Please sign in to comment.