on 0cd0df9 help sign in
bridge/src/lib/useWalletConnect.ts
186 lines 4.0 kB history raw
1import WalletConnect from '@walletconnect/client';
2import QRCodeModal from '@walletconnect/qrcode-modal';
3import { useEffect, useState } from 'react';
4import { Just } from 'folktale/maybe';
6import { useWallet } from 'store/wallet';
7import { getAuthToken } from './authToken';
8import { DEFAULT_HD_PATH, WALLET_TYPES } from './constants';
9import WalletConnectWallet from './types/WalletConnectWallet';
11type PeerMeta = {
12 description: string;
13 icons: string[];
14 name: string;
15 url: string;
16};
18type ConnectEvent = {
19 event: 'connect';
20 params: [
21 {
22 accounts: string[];
23 chainId: number;
24 peerId: string;
25 peerMeta: PeerMeta;
26 }
27 ];
28};
30export const useWalletConnect = () => {
31 const { setWallet, setWalletHdPath, setAuthToken, resetWallet } = useWallet();
32 const [connector, setConnector] = useState<WalletConnect | null>(null);
33 const [address, setAddress] = useState<string | null>(null);
34 const [peerMeta, setPeerMeta] = useState<PeerMeta | null>(null);
36 // Init connector
37 useEffect(() => {
38 const newConnector = new WalletConnect({
39 bridge: 'https://bridge.walletconnect.org',
40 qrcodeModal: QRCodeModal,
41 });
43 setConnector(newConnector);
45 // restore state from cached connection
46 if (newConnector.accounts.length > 0) {
47 setAddress(newConnector.accounts[0]);
48 }
50 if (newConnector.peerMeta) {
51 setPeerMeta(newConnector.peerMeta);
52 }
53 // eslint-disable-next-line react-hooks/exhaustive-deps
54 }, []);
56 // Behavior
57 const connect = () => {
58 if (!connector || connector.connected) {
59 return;
60 }
62 connector.createSession();
63 };
65 const disconnect = () => {
66 if (!connector) {
67 console.warn('Nothing to disconnect');
68 return;
69 }
71 connector.killSession();
72 };
74 type AuthenticateArgs = {
75 hdPath?: string;
76 };
78 const authenticate = async ({
79 hdPath = DEFAULT_HD_PATH,
80 }: AuthenticateArgs) => {
81 if (!(address && connector)) {
82 return;
83 }
85 const authToken = await getAuthToken({
86 address,
87 connector,
88 walletType: WALLET_TYPES.WALLET_CONNECT,
89 });
91 // Inspired by the MetamaskWallet implementation :)
92 // TODO: We should refactor / unify how we handle wallets
93 const wallet: WalletConnectWallet = {
94 address,
95 };
97 setAuthToken(Just(authToken));
98 setWallet(Just(wallet));
99 setWalletHdPath(hdPath);
100 };
102 const isConnected = () => {
103 if (!connector) {
104 return false;
105 }
107 return connector.connected;
108 };
110 const initConnectHandler = () => {
111 if (!connector) {
112 return;
113 }
115 connector.on('connect', async (error, payload: ConnectEvent) => {
116 if (error) {
117 throw error;
118 }
120 const address = payload.params[0].accounts[0];
121 setAddress(address);
122 setPeerMeta(payload.params[0].peerMeta);
123 });
124 };
126 const initDisconnectHandler = () => {
127 if (!connector) {
128 return;
129 }
131 connector.on('disconnect', (error, _payload) => {
132 if (error) {
133 throw error;
134 }
136 setAddress(null);
137 setPeerMeta(null);
138 resetWallet();
139 });
140 };
142 const initSessionUpdateHandler = () => {
143 if (!connector) {
144 return;
145 }
147 connector.on('session_update', (error, payload) => {
148 if (error) {
149 throw error;
150 }
152 // TODO: Confirm what should happen on update?
153 // For now, disconnect and logout
154 disconnect();
155 });
156 };
158 // Init and clean up
159 useEffect(() => {
160 if (!connector) {
161 return;
162 }
164 initConnectHandler();
165 initDisconnectHandler();
166 initSessionUpdateHandler();
168 return () => {
169 // Clean up listeners
170 connector.off('connect');
171 connector.off('disconnect');
172 connector.off('session_update');
173 };
174 // eslint-disable-next-line react-hooks/exhaustive-deps
175 }, [connector]);
177 return {
178 address,
179 authenticate,
180 connect,
181 connector,
182 disconnect,
183 isConnected,
184 peerMeta,
185 };
186};