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

Add Metamask wallet support #498

Merged
merged 4 commits into from
Dec 19, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
398 changes: 385 additions & 13 deletions package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"style": "src/global.css",
"scripts": {
"build": "tsc",
"build:watch": "tsc --watch",
"storybook": "storybook dev -p 6006",
"storybook:build": "storybook build",
"lint": "eslint .",
Expand Down
26 changes: 13 additions & 13 deletions src/lib/ConnectMassaWallets/components/BearbyWallet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,24 +10,24 @@ import { useAccountStore } from '../store';
import { BEARBY_INSTALL } from '../../massa-react/const';

export default function BearbyWallet() {
const { connectedAccount } = useAccountStore();
const { connectedAccount, isFetching } = useAccountStore();

if (connectedAccount) {
if (!connectedAccount && !isFetching) {
return (
<div className="flex flex-col gap-4 mas-body">
<ConnectedAccount />
<MASBalance />
</div>
<WalletError
description={Intl.t(
'connect-wallet.card-destination.bearby-not-installed',
)}
link={BEARBY_INSTALL}
linkLabel={Intl.t('connect-wallet.card-destination.get-bearby')}
/>
);
}

return (
<WalletError
description={Intl.t(
'connect-wallet.card-destination.bearby-not-installed',
)}
link={BEARBY_INSTALL}
linkLabel={Intl.t('connect-wallet.card-destination.get-bearby')}
/>
<div className="flex flex-col gap-4 mas-body">
<ConnectedAccount />
<MASBalance />
</div>
);
}
37 changes: 30 additions & 7 deletions src/lib/ConnectMassaWallets/components/ConnectMassaWallet.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { MetaMaskSvg } from './MetaMaskSvg';
import { BearbySvg } from './BearbySvg';
import BearbyWallet from './BearbyWallet';
import SelectMassaWallet from './SelectMassaWallet';
Expand All @@ -8,25 +9,37 @@ import Intl from '../i18n';
import { useAccountStore } from '../store';
import { MassaWallet, Tooltip } from '../../../components';
import { WalletName } from '@massalabs/wallet-provider';
import MetamaskWallet from './MetamaskWallet';
import { Network } from './Network';
import { useEffect, useState } from 'react';

export const ConnectMassaWallet = () => {
const { currentWallet, wallets, setCurrentWallet, isFetching } =
useAccountStore();
const [selectedWallet, setSelectedWallet] = useState<WalletName | null>(null);

useEffect(() => {
if (currentWallet) {
setSelectedWallet(currentWallet.name());
}
}, [currentWallet]);

function renderWallet() {
switch (currentWallet?.name()) {
switch (selectedWallet) {
case WalletName.MassaStation:
return <StationWallet />;
case WalletName.Bearby:
return <BearbyWallet />;
case WalletName.Metamask:
return <MetamaskWallet />;
default:
// Should not happen
return <>Error: no wallet selected</>;
}
}

function renderSelectedWallet() {
switch (currentWallet?.name()) {
switch (selectedWallet) {
case WalletName.MassaStation:
return (
<>
Expand All @@ -41,18 +54,26 @@ export const ConnectMassaWallet = () => {
{Intl.t(`connect-wallet.${WalletName.Bearby}`)}
</>
);
case WalletName.Metamask:
return (
<>
<MetaMaskSvg />
{Intl.t(`connect-wallet.${WalletName.Metamask}`)}
</>
);
}
}

if (!currentWallet) {
if (!selectedWallet && !isFetching) {
return (
<div className="text-f-primary">
<SelectMassaWallet
onClick={async (providerName) => {
const provider = wallets.find((p) => p.name() === providerName);
if (provider) {
await setCurrentWallet(provider);
const wallet = wallets.find((p) => p.name() === providerName);
if (wallet) {
await setCurrentWallet(wallet);
}
setSelectedWallet(providerName);
}}
/>
</div>
Expand All @@ -68,6 +89,7 @@ export const ConnectMassaWallet = () => {
<div className="flex gap-2 items-center">
{renderSelectedWallet()}
<ChainStatus />
<Network />
{currentWallet?.name() === WalletName.Bearby && (
<Tooltip
customClass="mas-caption w-fit whitespace-nowrap"
Expand All @@ -78,11 +100,12 @@ export const ConnectMassaWallet = () => {
<SwitchWalletButton
onClick={() => {
setCurrentWallet();
setSelectedWallet(null);
}}
/>
</div>

{!isFetching && renderWallet()}
{renderWallet()}
</div>
);
};
11 changes: 6 additions & 5 deletions src/lib/ConnectMassaWallets/components/MASBalance.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,22 @@ import { useEffect, useState } from 'react';
import Intl from '../i18n';
import { useAccountStore } from '../store';
import { FetchingLine } from '../../../components';
import { fetchMASBalance } from '../../massa-react/utils';
import { massaToken } from '../../massa-react/const';
import { formatAmount } from '../../util/parseAmount';

export function MASBalance() {
const [balance, setBalance] = useState<bigint>();

const { connectedAccount } = useAccountStore();
const { connectedAccount, currentWallet, network } = useAccountStore();

useEffect(() => {
if (!connectedAccount) return;
fetchMASBalance(connectedAccount).then((balance) => {
const fetchBalance = async () => {
const balance = await connectedAccount.balance(false);
setBalance(balance);
});
}, [connectedAccount, setBalance]);
};
fetchBalance();
}, [connectedAccount, setBalance, currentWallet, network]);

const formattedBalance = formatAmount(balance?.toString() || '0', 9).full;

Expand Down
57 changes: 57 additions & 0 deletions src/lib/ConnectMassaWallets/components/MetaMaskSvg.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/* eslint-disable max-len */
export function MetaMaskSvg() {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 256 240"
>
<path fill="#E17726" d="M250.066 0L140.219 81.279l20.427-47.9z" />
<path
fill="#E27625"
d="m6.191.096l89.181 33.289l19.396 48.528zM205.86 172.858l48.551.924l-16.968 57.642l-59.243-16.311zm-155.721 0l27.557 42.255l-59.143 16.312l-16.865-57.643z"
/>
<path
fill="#E27625"
d="m112.131 69.552l1.984 64.083l-59.371-2.701l16.888-25.478l.214-.245zm31.123-.715l40.9 36.376l.212.244l16.888 25.478l-59.358 2.7zM79.435 173.044l32.418 25.259l-37.658 18.181zm97.136-.004l5.131 43.445l-37.553-18.184z"
/>
<path
fill="#D5BFB2"
d="m144.978 195.922l38.107 18.452l-35.447 16.846l.368-11.134zm-33.967.008l-2.909 23.974l.239 11.303l-35.53-16.833z"
/>
<path
fill="#233447"
d="m100.007 141.999l9.958 20.928l-33.903-9.932zm55.985.002l24.058 10.994l-34.014 9.929z"
/>
<path
fill="#CC6228"
d="m82.026 172.83l-5.48 45.04l-29.373-44.055zm91.95.001l34.854.984l-29.483 44.057zm28.136-44.444l-25.365 25.851l-19.557-8.937l-9.363 19.684l-6.138-33.849zm-148.237 0l60.435 2.749l-6.139 33.849l-9.365-19.681l-19.453 8.935z"
/>
<path
fill="#E27525"
d="m52.166 123.082l28.698 29.121l.994 28.749zm151.697-.052l-29.746 57.973l1.12-28.8zm-90.956 1.826l1.155 7.27l2.854 18.111l-1.835 55.625l-8.675-44.685l-.003-.462zm30.171-.101l6.521 35.96l-.003.462l-8.697 44.797l-.344-11.205l-1.357-44.862z"
/>
<path
fill="#F5841F"
d="m177.788 151.046l-.971 24.978l-30.274 23.587l-6.12-4.324l6.86-35.335zm-99.471 0l30.399 8.906l6.86 35.335l-6.12 4.324l-30.275-23.589z"
/>
<path
fill="#C0AC9D"
d="m67.018 208.858l38.732 18.352l-.164-7.837l3.241-2.845h38.334l3.358 2.835l-.248 7.831l38.487-18.29l-18.728 15.476l-22.645 15.553h-38.869l-22.63-15.617z"
/>
<path
fill="#161616"
d="m142.204 193.479l5.476 3.869l3.209 25.604l-4.644-3.921h-36.476l-4.556 4l3.104-25.681l5.478-3.871z"
/>
<path
fill="#763E1A"
d="M242.814 2.25L256 41.807l-8.235 39.997l5.864 4.523l-7.935 6.054l5.964 4.606l-7.897 7.191l4.848 3.511l-12.866 15.026l-52.77-15.365l-.457-.245l-38.027-32.078zm-229.628 0l98.326 72.777l-38.028 32.078l-.457.245l-52.77 15.365l-12.866-15.026l4.844-3.508l-7.892-7.194l5.952-4.601l-8.054-6.071l6.085-4.526L0 41.809z"
/>
<path
fill="#F5841F"
d="m180.392 103.99l55.913 16.279l18.165 55.986h-47.924l-33.02.416l24.014-46.808zm-104.784 0l-17.151 25.873l24.017 46.808l-33.005-.416H1.631l18.063-55.985zm87.776-70.878l-15.639 42.239l-3.319 57.06l-1.27 17.885l-.101 45.688h-30.111l-.098-45.602l-1.274-17.986l-3.32-57.045l-15.637-42.239z"
/>
</svg>
);
}
63 changes: 63 additions & 0 deletions src/lib/ConnectMassaWallets/components/MetamaskWallet.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
import React from 'react';

import { ConnectedAccount } from './ConnectedAccount';
import { MASBalance } from './MASBalance';
import { WalletError } from './WalletError';
import Intl from '../i18n';
import { useAccountStore } from '../store';
import { METAMASK_INSTALL } from '../../massa-react/const';
import { Button } from '../../../components';
import { CHAIN_ID, Network } from '@massalabs/massa-web3';

export default function MetamaskWallet() {
const { connectedAccount, currentWallet, network, isFetching } =
useAccountStore();

function handleSwitchNetwork(network: Network): void {
if (!currentWallet) return;
if (network.chainId === CHAIN_ID.Mainnet) {
currentWallet.setRpcUrl('https://buildnet.massa.net/api/v2');
} else {
currentWallet.setRpcUrl('https://mainnet.massa.net/api/v2');
}
}

if (!connectedAccount && !isFetching) {
return (
<WalletError
description={Intl.t(
'connect-wallet.card-destination.meta-mask-not-connected',
)}
link={METAMASK_INSTALL}
linkLabel={Intl.t('connect-wallet.card-destination.get-metamask')}
/>
);
}

return (
<div className="flex flex-col gap-4 mas-body">
<ConnectedAccount />
<MASBalance />
{network && currentWallet && (
<div className="flex gap-4">
<SwitchNetwork
networkName={network.name === 'mainnet' ? 'buildnet' : 'mainnet'}
onClick={() => handleSwitchNetwork(network)}
/>
</div>
)}
</div>
);
}

function SwitchNetwork({
networkName,
onClick,
}: {
networkName: string;
onClick: () => void;
}) {
return <Button onClick={onClick}>Switch To {networkName}</Button>;
}
18 changes: 18 additions & 0 deletions src/lib/ConnectMassaWallets/components/Network.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
import React from 'react';

import { Tag, tagTypes } from '../../../components';
import { useAccountStore } from '../store';

export function Network() {
const { network } = useAccountStore();

if (!network) return;

return <NetworkTag networkName={network.name} />;
}

export function NetworkTag({ networkName }: { networkName: string }) {
return <Tag type={tagTypes.info}>{networkName}</Tag>;
}
5 changes: 5 additions & 0 deletions src/lib/ConnectMassaWallets/components/SelectMassaWallet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { Disconnected } from './Status/Disconnected';
import Intl from '../i18n';
import { Dropdown, MassaWallet } from '../../../components';
import { WalletName } from '@massalabs/wallet-provider';
import { MetaMaskSvg } from './MetaMaskSvg';

const walletList = [
{
Expand All @@ -17,6 +18,10 @@ const walletList = [
name: WalletName.Bearby,
icon: <BearbySvg />,
},
{
name: WalletName.Metamask,
icon: <MetaMaskSvg />,
},
];

interface SelectMassaWalletProps {
Expand Down
10 changes: 5 additions & 5 deletions src/lib/ConnectMassaWallets/components/StationWallet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import Intl from '../i18n';
import { useAccountStore } from '../store';

export default function StationWallet() {
const { accounts } = useAccountStore();
const { accounts, isFetching } = useAccountStore();

const [stationIsOn, setStationIsOn] = useState<boolean | undefined>(
undefined,
Expand All @@ -40,7 +40,7 @@ export default function StationWallet() {
});
});

if (stationIsOn === false) {
if (stationIsOn === false && !isFetching) {
return (
<WalletError
description={Intl.t(
Expand All @@ -52,7 +52,7 @@ export default function StationWallet() {
);
}

if (massaWalletIsOn === false) {
if (massaWalletIsOn === false && !isFetching) {
return (
<WalletError
description={Intl.t(
Expand All @@ -64,7 +64,7 @@ export default function StationWallet() {
);
}

if (accounts !== undefined && !accounts.length) {
if (accounts !== undefined && !accounts.length && !isFetching) {
return (
<WalletError
description={Intl.t(
Expand All @@ -78,7 +78,7 @@ export default function StationWallet() {
);
}

if (accounts === undefined) {
if (accounts === undefined && !isFetching) {
return <div className="h-14 bg-secondary rounded-lg animate-pulse"></div>;
}

Expand Down
3 changes: 3 additions & 0 deletions src/lib/ConnectMassaWallets/i18n/en_US.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
"your-wallet": "Your wallet",
"bearby-not-installed": "Bearby extension is either turned off or not installed. Make sure the extension is on and refresh the page.",
"get-bearby": "Get Bearby here",
"meta-mask-not-connected": "Metamask is not connected. Make sure the extension is on and connected to the right network.",
"get-metamask": "Get Metamask here",
"massa-station-not-detected": "Massa Station desktop app is not detected. Make sure the app is opened, or click below to install it.",
"get-massa-station": "Get Massa Station",
"massa-wallet-not-detected": "Massa Wallet is not detected. Make sure the plugin is installed, or click below to install it.",
Expand All @@ -26,6 +28,7 @@
},
"MASSASTATION": "MassaWallet",
"BEARBY": "Bearby",
"METAMASK": "Metamask",
"connected-cards": {
"wallet-balance": "Balance: "
},
Expand Down
Loading
Loading