Skip to content

Commit

Permalink
add Home page; Metamask connect
Browse files Browse the repository at this point in the history
  • Loading branch information
Lykhoyda committed Dec 3, 2024
1 parent 2be4b1b commit 80ed0ca
Show file tree
Hide file tree
Showing 41 changed files with 1,344 additions and 206 deletions.
1 change: 0 additions & 1 deletion packages/demo-wallet/src/App/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import { ReceiveFunds } from './components/ReceiveFunds';
import { Summary } from './components/Summary';
import { Settings } from './components/Settings';
import { RESCAN_INTERVAL } from './Constants';
import { ConnectMetamaskButton } from './components/ConnectMetamaskButton';
import { MetaMaskProvider } from '../hooks';

export type State = {
Expand Down
1 change: 1 addition & 0 deletions packages/web-wallet/.env
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
SNAP_ORIGIN=""
2 changes: 1 addition & 1 deletion packages/web-wallet/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + React + TS</title>
<title>WebZjs Web Wallet</title>
</head>
<body>
<div id="root"></div>
Expand Down
3 changes: 3 additions & 0 deletions packages/web-wallet/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,15 @@
"preview": "vite preview"
},
"dependencies": {
"@metamask/providers": "^18.2.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-router-dom": "^6.27.0"
},
"devDependencies": {
"@eslint/js": "^9.13.0",
"@fontsource/inter": "^5.1.0",
"@fontsource/roboto": "^5.1.0",
"@types/react": "^18.3.12",
"@types/react-dom": "^18.3.1",
"@vitejs/plugin-react": "^4.3.3",
Expand Down
10 changes: 0 additions & 10 deletions packages/web-wallet/src/App.tsx

This file was deleted.

9 changes: 9 additions & 0 deletions packages/web-wallet/src/assets/chainsafe.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
12 changes: 12 additions & 0 deletions packages/web-wallet/src/assets/ellipse.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
43 changes: 43 additions & 0 deletions packages/web-wallet/src/assets/form-transfer.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
15 changes: 15 additions & 0 deletions packages/web-wallet/src/assets/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import ChainsafeSvg from './chainsafe.svg';
import FormTransferSvg from './form-transfer.svg';
import MetaMaskLogoSvg from './metaMaskLogo.svg';
import MetaMaskSnapsLogoSvg from './metamask-snaps-logo.svg';
import ZcashSvg from './zcash.svg';
import ZcashYellowSvg from './zcash-yellow.svg';

export {
ChainsafeSvg,
MetaMaskLogoSvg,
MetaMaskSnapsLogoSvg,
ZcashSvg,
ZcashYellowSvg,
FormTransferSvg,
};
9 changes: 9 additions & 0 deletions packages/web-wallet/src/assets/metaMaskLogo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
58 changes: 58 additions & 0 deletions packages/web-wallet/src/assets/metamask-snaps-logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added packages/web-wallet/src/assets/noise.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 0 additions & 1 deletion packages/web-wallet/src/assets/react.svg

This file was deleted.

9 changes: 9 additions & 0 deletions packages/web-wallet/src/assets/zcash-yellow.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 9 additions & 0 deletions packages/web-wallet/src/assets/zcash.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
20 changes: 15 additions & 5 deletions packages/web-wallet/src/components/Footer/Footer.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,24 @@
import React from 'react';
import { ChainsafeSvg } from '../../assets';

const Footer = (): React.JSX.Element => {
return (
<footer className="w-full py-4 flex items-center justify-center border-t border-gray-300 bg-white">
<p className="text-gray-500">
Made and Maintained by{' '}
<a href="#" className="text-blue-600 hover:underline">
<footer className="w-full py-6 flex items-center justify-center font-medium">
<div className="flex items-center text-xs">
<span className="block mr-1 text-xs">Made and Maintained by</span>
<a
className="inline-flex items-center justify-center"
href="https://chainsafe.io/"
target="_blank"
>
<img
src={ChainsafeSvg}
alt="Chainsafe Systems Logo"
className="inline-block w-6 h-6 mr-1"
/>
ChainSafe
</a>
</p>
</div>
</footer>
);
};
Expand Down
43 changes: 30 additions & 13 deletions packages/web-wallet/src/components/Header/Header.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,41 @@
import React from 'react';
import { Link } from 'react-router-dom';
import ZcashSvg from '../../assets/zcash.svg';
import MetemaskSnap from '../../assets/metamask-snaps-logo.svg';

const Header = (): React.JSX.Element => {
return (
<header className="w-full px-6 py-4 flex items-center justify-between border-b border-gray-300 bg-white">
<div className="flex items-center">
<img
src="../../assets/react.svg"
alt="MetaMask Logo"
className="h-6 w-6 mr-2"
/>
<span className="text-lg font-medium">MetaMask | Snaps</span>
</div>
<nav className="flex space-x-4">
<a href="#" className="text-gray-600 hover:underline">
<header className="font-inter w-full px-16 flex items-center justify-between bg-transparent max-h-[3.125rem]">
<Link to={'/'}>
<div className="flex items-center">
<img src={ZcashSvg} alt="MetaMask Logo" className="h-6 w-6 mr-3" />
<img
src={MetemaskSnap}
alt="MetaMask Logo"
className="h-12 w-full max-w-[200px]"
/>
</div>
</Link>
<nav className="flex">
<a
target="_blank"
href="https://chainsafe.github.io/WebZjs/"
className="text-gray-600 px-6 relative after:content-['|'] after:absolute after:right-0 after:top-0 after:bottom-0 after:w-px after:bg-[divide-dividerColor] hover:underline"
>
ZCash Docs
</a>
<a href="#" className="text-gray-600 hover:underline">
<a
target="_blank"
href="https://docs.metamask.io/snaps/"
className="text-gray-600 px-6 hover:underline"
>
Snap Docs
</a>
<a href="#" className="text-gray-600 hover:underline">
<a
target="_blank"
href="https://snaps.metamask.io/"
className="text-gray-600 hover:underline"
>
Snap Registry
</a>
</nav>
Expand Down
2 changes: 1 addition & 1 deletion packages/web-wallet/src/components/Layout/Layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ const Layout = (): React.JSX.Element => {
return (
<div className="flex flex-col min-h-screen">
<Header />
<main className="flex-grow flex items-center justify-center p-6 bg-gray-100">
<main className="flex-grow flex items-center justify-center p-6">
<Outlet />
</main>
<Footer />
Expand Down
1 change: 1 addition & 0 deletions packages/web-wallet/src/config/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { defaultSnapOrigin } from './snap';
2 changes: 2 additions & 0 deletions packages/web-wallet/src/config/snap.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export const defaultSnapOrigin =
import.meta.env.SNAP_ORIGIN ?? `local:http://localhost:8080`;
74 changes: 74 additions & 0 deletions packages/web-wallet/src/hooks/MetamaskContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import type { MetaMaskInpageProvider } from '@metamask/providers';
import type { ReactNode } from 'react';
import { createContext, useContext, useEffect, useState } from 'react';

import type { Snap } from '../types';
import { getSnapsProvider } from '../utils';

type MetaMaskContextType = {
provider: MetaMaskInpageProvider | null;
installedSnap: Snap | null;
error: Error | null;
setInstalledSnap: (snap: Snap | null) => void;
setError: (error: Error) => void;
};

export const MetaMaskContext = createContext<MetaMaskContextType>({
provider: null,
installedSnap: null,
error: null,
setInstalledSnap: () => {
/* no-op */
},
setError: () => {
/* no-op */
},
});

/**
* MetaMask context provider to handle MetaMask and snap status.
*
* @param props - React Props.
* @param props.children - React component to be wrapped by the Provider.
* @returns JSX.
*/
export const MetaMaskProvider = ({ children }: { children: ReactNode }) => {
const [provider, setProvider] = useState<MetaMaskInpageProvider | null>(null);
const [installedSnap, setInstalledSnap] = useState<Snap | null>(null);
const [error, setError] = useState<Error | null>(null);

useEffect(() => {
getSnapsProvider().then(setProvider).catch(console.error);
}, []);

useEffect(() => {
if (error) {
const timeout = setTimeout(() => {
setError(null);
}, 10000);

return () => {
clearTimeout(timeout);
};
}

return undefined;
}, [error]);

return (
<MetaMaskContext.Provider
value={{ provider, error, setError, installedSnap, setInstalledSnap }}
>
{children}
</MetaMaskContext.Provider>
);
};

/**
* Utility hook to consume the MetaMask context.
*
* @returns The MetaMask context.
*/
export function useMetaMaskContext() {
return useContext(MetaMaskContext);
}
5 changes: 5 additions & 0 deletions packages/web-wallet/src/hooks/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export * from './MetamaskContext';
export * from './useMetaMask';
export * from './useRequest';
export * from './useRequestSnap';
export * from './useInvokeSnap';
37 changes: 37 additions & 0 deletions packages/web-wallet/src/hooks/useInvokeSnap.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { defaultSnapOrigin } from '../config';
import { useRequest } from './useRequest';

export type InvokeSnapParams = {
method: string;
params?: Record<string, unknown>;
};

/**
* Utility hook to wrap the `wallet_invokeSnap` method.
*
* @param snapId - The Snap ID to invoke. Defaults to the snap ID specified in the
* config.
* @returns The invokeSnap wrapper method.
*/
export const useInvokeSnap = (snapId = defaultSnapOrigin) => {
const request = useRequest();

/**
* Invoke the requested Snap method.
*
* @param params - The invoke params.
* @param params.method - The method name.
* @param params.params - The method params.
* @returns The Snap response.
*/
const invokeSnap = async ({ method, params }: InvokeSnapParams) =>
request({
method: 'wallet_invokeSnap',
params: {
snapId,
request: params ? { method, params } : { method },
},
});

return invokeSnap;
};
55 changes: 55 additions & 0 deletions packages/web-wallet/src/hooks/useMetaMask.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { useEffect, useState } from 'react';

import { useMetaMaskContext } from './MetamaskContext';
import { useRequest } from './useRequest';
import { GetSnapsResponse } from '../types';
import { defaultSnapOrigin } from '../config';

/**
* A Hook to retrieve useful data from MetaMask.
* @returns The informations.
*/
export const useMetaMask = () => {
const { provider, setInstalledSnap, installedSnap } = useMetaMaskContext();
const request = useRequest();
const [isFlask, setIsFlask] = useState(false);

const snapsDetected = provider !== null;

/**
* Detect if the version of MetaMask is Flask.
*/
const detectFlask = async () => {
const clientVersion = await request({
method: 'web3_clientVersion',
});

const isFlaskDetected = (clientVersion as string[])?.includes('flask');

setIsFlask(isFlaskDetected);
};

/**
* Get the Snap informations from MetaMask.
*/
const getSnap = async () => {
const snaps = (await request({
method: 'wallet_getSnaps',
})) as GetSnapsResponse;

setInstalledSnap(snaps[defaultSnapOrigin] ?? null);
};

useEffect(() => {
const detect = async () => {
if (provider) {
await detectFlask();
await getSnap();
}
};

detect().catch(console.error);
}, [detectFlask, getSnap, provider]);

return { isFlask, snapsDetected, installedSnap, getSnap };
};
40 changes: 40 additions & 0 deletions packages/web-wallet/src/hooks/useRequest.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import type { RequestArguments } from '@metamask/providers';

import { useMetaMaskContext } from './MetamaskContext';

export type Request = (params: RequestArguments) => Promise<unknown | null>;

/**
* Utility hook to consume the provider `request` method with the available provider.
*
* @returns The `request` function.
*/
export const useRequest = () => {
const { provider, setError } = useMetaMaskContext();

/**
* `provider.request` wrapper.
*
* @param params - The request params.
* @param params.method - The method to call.
* @param params.params - The method params.
* @returns The result of the request.
*/
const request: Request = async ({ method, params }) => {
try {
const data =
(await provider?.request({
method,
params,
} as RequestArguments)) ?? null;

return data;
} catch (requestError: any) {
setError(requestError);

return null;
}
};

return request;
};
Loading

0 comments on commit 80ed0ca

Please sign in to comment.