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

Feat: support multi wallet connector #201

Draft
wants to merge 4 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
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
7 changes: 7 additions & 0 deletions apps/godwoken-bridge/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,13 @@
"@types/react-dom": "^17.0.14",
"@types/react-router-dom": "^5.3.2",
"@types/styled-components": "^5.1.18",
"@walletconnect/ethereum-provider": "^1.7.5",
"@web3-react/core": "8.0.35-beta.0",
"@web3-react/eip1193": "8.0.26-beta.0",
"@web3-react/metamask": "8.0.28-beta.0",
"@web3-react/types": "8.0.20-beta.0",
"@web3-react/url": "8.0.25-beta.0",
"@web3-react/walletconnect": "8.0.35-beta.0",
"ahooks": "^3.7.0",
"antd": "^4.17.3",
"axios": "^0.27.2",
Expand Down
9 changes: 9 additions & 0 deletions apps/godwoken-bridge/src/assets/wallets/imtoken.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
19 changes: 19 additions & 0 deletions apps/godwoken-bridge/src/assets/wallets/metamask.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions apps/godwoken-bridge/src/assets/wallets/safepal.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions apps/godwoken-bridge/src/assets/wallets/wallet-connect.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
134 changes: 134 additions & 0 deletions apps/godwoken-bridge/src/components/WalletConnect/connector.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
import React, { useEffect } from "react";
import styled from "styled-components";
import { COLOR } from "../../style/variables";
import { ConfirmModal } from "../../style/common";
import { connectors } from "./connectors";
import { ReactComponent as MetaMaskIcon } from "../../assets/wallets/metamask.svg";
import { ReactComponent as WalletConnectIcon } from "../../assets/wallets/wallet-connect.svg";
import { ReactComponent as ImTokenIcon } from "../../assets/wallets/imtoken.svg";
import { ReactComponent as SafePalIcon } from "../../assets/wallets/safepal.svg";

const StyleWrapper = styled.div`
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: center;
`;

const WalletBoxWrapper = styled.div`
display: flex;
flex-direction: column;
width: 150px;
height: 100px;
color: ${COLOR.primary};
text-decoration: none;
cursor: pointer;
padding: 0 10px;
border-radius: 8px;
margin: 10px 10px;
text-align: center;
line-height: 33px;
font-size: 14px;
font-weight: bold;
&:hover {
background: rgba(0, 0, 0, 0.1);
color: ${COLOR.primary};
}
> svg {
width: 120px;
height: 80px;
}
.title {
color: ${COLOR.primary};
}
`;

type SelectMenuProps = {
handleClick: () => void;
};
export const SelectMenu: React.FC<SelectMenuProps> = ({ handleClick }) => {
const connectMetamask = () => {
connectors.injectedConnect.instance.activate().catch((err) => {
console.error(err);
});
};

const connectImToken = () => {
connectors.injectedConnect.instance.activate().catch((err) => {
console.error(err);
});
};

const connectSafePal = () => {
connectors.injectedConnect.instance.activate().catch((err) => {
console.error(err);
});
};

const connectWalletConnect = () => {
connectors.walletConnect.instance.activate().catch((err) => {
console.error(err);
});
};

return (
<StyleWrapper>
<WalletBoxWrapper onClick={connectMetamask}>
<MetaMaskIcon />
<div className="title">Metamask</div>
</WalletBoxWrapper>

<WalletBoxWrapper onClick={connectImToken}>
<ImTokenIcon />
<div className="title">ImToken</div>
</WalletBoxWrapper>

<WalletBoxWrapper onClick={connectSafePal}>
<SafePalIcon />
<div className="title">SafePal</div>
</WalletBoxWrapper>

<WalletBoxWrapper onClick={connectWalletConnect}>
<WalletConnectIcon />
<div className="title">WalletConnect</div>
</WalletBoxWrapper>
</StyleWrapper>
);
};

type ConnectorModalProps = {
connectBtnQuerySelector: string;
popoverVisible: boolean;
setPopoverVisible: (v: boolean) => void;
};
export const ConnectorModal: React.FC<ConnectorModalProps> = ({
connectBtnQuerySelector,
popoverVisible,
setPopoverVisible,
}) => {
const closeSelectMenu = () => {
setPopoverVisible(false);
};

useEffect(() => {
document.addEventListener("click", (e) => {
const target = document.querySelector(connectBtnQuerySelector);
if (!(e.target && e.target instanceof Element && (e.target === target || target?.contains(e.target)))) {
closeSelectMenu();
}
});
});

return (
<ConfirmModal
title={"Choose Wallet"}
visible={popoverVisible}
onOk={closeSelectMenu}
onCancel={closeSelectMenu}
footer={null}
width={400}
>
<SelectMenu handleClick={closeSelectMenu}></SelectMenu>
</ConfirmModal>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { Web3ReactHooks } from "@web3-react/core";
import { MetaMask } from "@web3-react/metamask";
import { WalletConnect } from "@web3-react/walletconnect";
import { metaMask, hooks as metaMaskHooks } from "./metamask";
import { walletConnect, hooks as walletConnectHooks } from "./walletConnect";

export const connectors = {
walletConnect: {
instance: walletConnect,
hooks: walletConnectHooks,
},
injectedConnect: {
instance: metaMask,
hooks: metaMaskHooks,
},
};

// use for Web3ReactProvider in App.tsx
export const connectorArray: [MetaMask | WalletConnect, Web3ReactHooks][] = [
[metaMask, metaMaskHooks],
[walletConnect, walletConnectHooks],
];
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { initializeConnector } from "@web3-react/core";
import { MetaMask } from "@web3-react/metamask";

export const [metaMask, hooks] = initializeConnector<MetaMask>((actions) => new MetaMask({ actions }));
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { initializeConnector } from "@web3-react/core";
import { WalletConnect } from "@web3-react/walletconnect";
import { GodwokenNetwork, GodwokenVersion, initConfig } from "light-godwoken";
import { isMainnet } from "../../../utils/environment";

type Rpc = {
[key: string]: string;
};

// get rpc info
const rpc: Rpc = {};
const getRpcInfo = (version: GodwokenVersion) => {
const network = isMainnet ? GodwokenNetwork.Mainnet : GodwokenNetwork.Testnet;
const config = initConfig(network, version);
const chainId = config.layer2Config.GW_POLYJUICE_CHAIN_ID;
const url = config.layer2Config.GW_POLYJUICE_RPC_URL;
return { chainId, url };
};

const { chainId: chainIdV0, url: urlV0 } = getRpcInfo(GodwokenVersion.V0);
const { chainId: chainIdV1, url: urlV1 } = getRpcInfo(GodwokenVersion.V1);

rpc[chainIdV1.toString()] = urlV1;
rpc[chainIdV0.toString()] = urlV0;

export const [walletConnect, hooks] = initializeConnector<WalletConnect>(
(actions) =>
new WalletConnect({
actions,
options: {
rpc,
bridge: "https://bridge.walletconnect.org",
qrcode: true,
},
}),
);
37 changes: 32 additions & 5 deletions apps/godwoken-bridge/src/components/WalletConnect/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@ import styled from "styled-components";
import { SecondeButton } from "../../style/common";
import { useLightGodwoken } from "../../hooks/useLightGodwoken";
import React, { useEffect, useState } from "react";
import detectEthereumProvider from "@metamask/detect-provider";
import { useNavigate, useParams } from "react-router-dom";
import { availableVersions } from "../../utils/environment";
import { ConnectorModal } from "./connector";
import { connectors } from "./connectors";
import { URI_AVAILABLE } from "@web3-react/walletconnect";

const { Option } = Select;
const StyleWrapper = styled.div`
display: flex;
Expand All @@ -25,6 +28,7 @@ export const WalletConnect: React.FC = () => {
const params = useParams();
const navigate = useNavigate();

const [openWalletSelector, setOpenWalletSelector] = useState<boolean>(false);
const [version, setVersion] = useState<string>();
const lightGodwoken = useLightGodwoken();

Expand All @@ -34,6 +38,22 @@ export const WalletConnect: React.FC = () => {
}
}, [params.version]);

// log URI when available
useEffect(() => {
connectors.walletConnect.instance.events.on(URI_AVAILABLE, (uri: string) => {
console.log(`uri: ${uri}`);
});
}, []);
// attempt to connect eagerly on mount
useEffect(() => {
connectors.injectedConnect.instance.connectEagerly().catch(() => {
console.debug("Failed to connect eagerly to injectedConnect");
});
connectors.walletConnect.instance.connectEagerly().catch(() => {
console.debug("Failed to connect eagerly to walletconnect");
});
}, []);

const handleChange = (value: string) => {
setVersion(value);
navigate(`/${value}`);
Expand All @@ -42,11 +62,11 @@ export const WalletConnect: React.FC = () => {
const connect = () => {
if (lightGodwoken) return;

detectEthereumProvider().then((ethereum: any) => {
return ethereum.request({ method: "eth_requestAccounts" });
});
setOpenWalletSelector(!openWalletSelector);
};

if (lightGodwoken) return null;

return (
<StyleWrapper>
<Select className="network-select" value={version} onChange={handleChange}>
Expand All @@ -56,7 +76,14 @@ export const WalletConnect: React.FC = () => {
</Option>
))}
</Select>
<SecondeButton onClick={connect}>Connect</SecondeButton>
<SecondeButton className="connect-wallet" onClick={connect}>
Connect
</SecondeButton>
<ConnectorModal
connectBtnQuerySelector=".connect-wallet"
popoverVisible={openWalletSelector}
setPopoverVisible={setOpenWalletSelector}
></ConnectorModal>
</StyleWrapper>
);
};
Loading