Skip to content

Commit

Permalink
feat: add new wallet menu
Browse files Browse the repository at this point in the history
  • Loading branch information
chybisov committed Sep 25, 2024
1 parent 91777e5 commit 38a7a7d
Show file tree
Hide file tree
Showing 89 changed files with 1,247 additions and 671 deletions.
2 changes: 1 addition & 1 deletion .eslintrc.base.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"sourceType": "module"
},
"rules": {
"@typescript-eslint/consistent-type-imports": "warn",
"@typescript-eslint/consistent-type-imports": "error",
"@typescript-eslint/explicit-module-boundary-types": "off",
"@typescript-eslint/no-shadow": "off",
"@typescript-eslint/no-unused-vars": "warn",
Expand Down
7 changes: 4 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@
"pre-commit:validate": "yarn workspaces foreach -Apt run pre-commit:validate",
"pre-push": "yarn workspaces foreach -Apt run pre-push:validate",
"prepare": "husky",
"postinstall": "husky"
"postinstall": "husky",
"check:types": "yarn workspaces foreach -Apt run check:types"
},
"standard-version": {
"scripts": {
Expand All @@ -43,8 +44,8 @@
"@testing-library/user-event": "^14.5.2",
"@types/eslint": "^8.56.12",
"@types/events": "^3.0.3",
"@types/node": "^22.5.5",
"@types/react": "^18.3.8",
"@types/node": "^22.7.0",
"@types/react": "^18.3.9",
"@types/react-dom": "^18.3.0",
"@types/uuid": "^10.0.0",
"@typescript-eslint/eslint-plugin": "^7.18.0",
Expand Down
12 changes: 10 additions & 2 deletions packages/wallet-management/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,20 @@
"lifi"
],
"dependencies": {
"@emotion/react": "^11.13.3",
"@emotion/styled": "^11.13.0",
"@lifi/sdk": "^3.3.0-beta.0",
"@mui/icons-material": "^5.16.7",
"@mui/lab": "^5.0.0-alpha.173",
"@mui/material": "^5.16.7",
"@solana/wallet-adapter-base": "^0.9.23",
"i18next": "^23.15.1",
"react": "^18.3.1",
"react-i18next": "^15.0.2",
"use-sync-external-store": "^1.2.2",
"viem": "^2.21.12",
"wagmi": "^2.12.13"
"viem": "^2.21.14",
"wagmi": "^2.12.14",
"zustand": "^4.5.5"
},
"devDependencies": {
"@types/use-sync-external-store": "^0.0.6",
Expand Down
80 changes: 80 additions & 0 deletions packages/wallet-management/src/components/EVMListItemButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { ChainType } from '@lifi/sdk';
import { Avatar, ListItemAvatar } from '@mui/material';
import type { Connector } from 'wagmi';
import { useConfig } from 'wagmi';
import { connect, disconnect, getAccount } from 'wagmi/actions';
import { ListItemButton } from '../components/ListItemButton.js';
import { ListItemText } from '../components/ListItemText.js';
import type { CreateConnectorFnExtended } from '../connectors/types.js';
import { useLastConnectedAccount } from '../hooks/useAccount.js';
import { useWalletManagementEvents } from '../hooks/useWalletManagementEvents.js';
import { WalletManagementEvent } from '../types/events.js';
import { getConnectorIcon } from '../utils/getConnectorIcon.js';
import { isWalletInstalledAsync } from '../utils/isWalletInstalledAsync.js';
import type { WalletListItemButtonProps } from './types.js';

interface EVMListItemButtonProps extends WalletListItemButtonProps {
connector: CreateConnectorFnExtended | Connector;
}

export const EVMListItemButton = ({
ecosystemSelection,
connector,
onNotInstalled,
onConnected,
onConnecting,
onError,
}: EVMListItemButtonProps) => {
const emitter = useWalletManagementEvents();
const config = useConfig();
const { setLastConnectedAccount } = useLastConnectedAccount();

const handleEVMConnect = async () => {
try {
const identityCheckPassed = await isWalletInstalledAsync(
(connector as Connector).id,
);
if (!identityCheckPassed) {
onNotInstalled?.(connector as Connector);
return;
}
const connectedAccount = getAccount(config);
onConnecting?.();
const data = await connect(config, { connector });
if (connectedAccount.connector) {
await disconnect(config, { connector: connectedAccount.connector });
}
setLastConnectedAccount(connector);
emitter.emit(WalletManagementEvent.WalletConnected, {
address: data.accounts[0],
chainId: data.chainId,
chainType: ChainType.EVM,
});
onConnected?.();
} catch (error) {
onError?.(error);
}
};

const connectorName: string = ecosystemSelection
? 'Ethereum'
: (connector as CreateConnectorFnExtended).displayName || connector.name;

return (
<ListItemButton key={connector.id} onClick={handleEVMConnect}>
<ListItemAvatar>
<Avatar
src={
ecosystemSelection
? 'https://raw.githubusercontent.com/lifinance/types/main/src/assets/icons/chains/ethereum.svg'
: getConnectorIcon(connector as Connector)
}
alt={connectorName}
>
{connectorName?.[0]}
</Avatar>
</ListItemAvatar>
<ListItemText primary={connectorName} />
</ListItemButton>
);
};
24 changes: 24 additions & 0 deletions packages/wallet-management/src/components/ListItemButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import {
alpha,
ListItemButton as MuiListItemButton,
styled,
} from '@mui/material';

export const ListItemButton = styled(MuiListItemButton)(({
theme,
disabled,
}) => {
const backgroundHoverColor =
theme.palette.mode === 'light'
? alpha(theme.palette.common.black, 0.04)
: alpha(theme.palette.common.white, 0.04);
return {
borderRadius: theme.shape.borderRadius,
paddingLeft: theme.spacing(1.5),
height: 56,
'&:hover': {
backgroundColor: !disabled && backgroundHoverColor,
},
...(disabled ? { opacity: 0.5, cursor: 'auto' } : {}),
};
});
11 changes: 11 additions & 0 deletions packages/wallet-management/src/components/ListItemText.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import {
ListItemText as MuiListItemText,
listItemTextClasses,
styled,
} from '@mui/material';

export const ListItemText = styled(MuiListItemText)(({ theme }) => ({
[`.${listItemTextClasses.primary}`]: {
fontWeight: 500,
},
}));
71 changes: 71 additions & 0 deletions packages/wallet-management/src/components/SVMListItemButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { ChainId, ChainType } from '@lifi/sdk';
import { Avatar, ListItemAvatar } from '@mui/material';
import type { WalletAdapter } from '@solana/wallet-adapter-base';
import { useWallet } from '@solana/wallet-adapter-react';
import { ListItemButton } from '../components/ListItemButton.js';
import { ListItemText } from '../components/ListItemText.js';
import { useLastConnectedAccount } from '../hooks/useAccount.js';
import { useWalletManagementEvents } from '../hooks/useWalletManagementEvents.js';
import { WalletManagementEvent } from '../types/events.js';
import type { WalletListItemButtonProps } from './types.js';

interface SVMListItemButtonProps extends WalletListItemButtonProps {
walletAdapter: WalletAdapter;
}

export const SVMListItemButton = ({
ecosystemSelection,
walletAdapter,
onConnected,
onConnecting,
onError,
}: SVMListItemButtonProps) => {
const emitter = useWalletManagementEvents();
const { select, disconnect, connected } = useWallet();
const { setLastConnectedAccount } = useLastConnectedAccount();

const connect = async () => {
try {
onConnecting?.();
if (connected) {
await disconnect();
}
select(walletAdapter.name);
// We use autoConnect on wallet selection
// await solanaConnect();
walletAdapter.once('connect', (publicKey) => {
setLastConnectedAccount(walletAdapter);
emitter.emit(WalletManagementEvent.WalletConnected, {
address: publicKey?.toString(),
chainId: ChainId.SOL,
chainType: ChainType.SVM,
});
});
onConnected?.();
} catch (error) {
onError?.(error);
}
};

const connectorName: string = ecosystemSelection
? 'Solana'
: walletAdapter.name;

return (
<ListItemButton key={connectorName} onClick={connect}>
<ListItemAvatar>
<Avatar
src={
ecosystemSelection
? 'https://raw.githubusercontent.com/lifinance/types/main/src/assets/icons/chains/solana.svg'
: walletAdapter.icon
}
alt={connectorName}
>
{connectorName[0]}
</Avatar>
</ListItemAvatar>
<ListItemText primary={connectorName} />
</ListItemButton>
);
};
80 changes: 80 additions & 0 deletions packages/wallet-management/src/components/UTXOListItemButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { ChainType } from '@lifi/sdk';
import { Avatar, ListItemAvatar } from '@mui/material';
import type { Connector } from 'wagmi';
import { connect, disconnect, getAccount } from 'wagmi/actions';
import { ListItemButton } from '../components/ListItemButton.js';
import { ListItemText } from '../components/ListItemText.js';
import type { CreateConnectorFnExtended } from '../connectors/types.js';
import { useLastConnectedAccount } from '../hooks/useAccount.js';
import { useWalletManagementEvents } from '../hooks/useWalletManagementEvents.js';
import { WalletManagementEvent } from '../types/events.js';
import { getConnectorIcon } from '../utils/getConnectorIcon.js';
import { isWalletInstalledAsync } from '../utils/isWalletInstalledAsync.js';
import { useConfig } from '../utxo/hooks/useConfig.js';
import type { WalletListItemButtonProps } from './types.js';

interface UTXOListItemButtonProps extends WalletListItemButtonProps {
connector: CreateConnectorFnExtended | Connector;
}

export const UTXOListItemButton = ({
ecosystemSelection,
connector,
onNotInstalled,
onConnected,
onConnecting,
onError,
}: UTXOListItemButtonProps) => {
const emitter = useWalletManagementEvents();
const config = useConfig();
const { setLastConnectedAccount } = useLastConnectedAccount();

const handleUTXOConnect = async () => {
try {
const identityCheckPassed = await isWalletInstalledAsync(
(connector as Connector).id,
);
if (!identityCheckPassed) {
onNotInstalled?.(connector as Connector);
return;
}
const connectedAccount = getAccount(config);
onConnecting?.();
const data = await connect(config, { connector });
if (connectedAccount.connector) {
await disconnect(config, { connector: connectedAccount.connector });
}
setLastConnectedAccount(connector);
emitter.emit(WalletManagementEvent.WalletConnected, {
address: data.accounts[0],
chainId: data.chainId,
chainType: ChainType.UTXO,
});
onConnected?.();
} catch (error) {
onError?.(error);
}
};

const connectorName: string = ecosystemSelection
? 'Bitcoin'
: (connector as CreateConnectorFnExtended).displayName || connector.name;

return (
<ListItemButton key={connector.id} onClick={handleUTXOConnect}>
<ListItemAvatar>
<Avatar
src={
ecosystemSelection
? 'https://raw.githubusercontent.com/lifinance/types/main/src/assets/icons/chains/bitcoin.svg'
: getConnectorIcon(connector as Connector)
}
alt={connectorName}
>
{connectorName?.[0]}
</Avatar>
</ListItemAvatar>
<ListItemText primary={connectorName} />
</ListItemButton>
);
};
Loading

0 comments on commit 38a7a7d

Please sign in to comment.