on 04ba898 help sign in
bridge/src/lib/useWalletConnect.ts
227 lines 4.7 kB history raw
1import WalletConnect from '@walletconnect/client';
2import QRCodeModal from '@walletconnect/qrcode-modal';
3import { ITxData } from '@walletconnect/types';
5import { useEffect, useState } from 'react';
6import { Just } from 'folktale/maybe';
8import { useWallet } from 'store/wallet';
9import { getAuthToken } from './authToken';
10import { DEFAULT_HD_PATH, WALLET_TYPES } from './constants';
11import WalletConnectWallet from './types/WalletConnectWallet';
13type PeerMeta = {
14 description: string;
15 icons: string[];
16 name: string;
17 url: string;
18};
20type ConnectEvent = {
21 event: 'connect';
22 params: [
23 {
24 accounts: string[];
25 chainId: number;
26 peerId: string;
27 peerMeta: PeerMeta;
28 }
29 ];
30};
32export const useWalletConnect = () => {
33 const { setWallet, setWalletHdPath, setAuthToken, resetWallet } = useWallet();
34 const [connector, setConnector] = useState<WalletConnect | null>(null);
35 const [address, setAddress] = useState<string | null>(null);
36 const [peerMeta, setPeerMeta] = useState<PeerMeta | null>(null);
38 const resetConnector = () => {
39 const newConnector = new WalletConnect({
40 bridge: 'https://bridge.walletconnect.org',
41 qrcodeModal: QRCodeModal,
42 });
44 setConnector(newConnector);
46 // restore state from cached connection
47 if (newConnector.accounts.length > 0) {
48 setAddress(newConnector.accounts[0]);
49 }
51 if (newConnector.peerMeta) {
52 setPeerMeta(newConnector.peerMeta);
53 }
54 };
56 // Init connector
57 useEffect(() => {
58 resetConnector();
59 // eslint-disable-next-line react-hooks/exhaustive-deps
60 }, []);
62 // Behavior
63 const connect = async () => {
64 if (!connector || connector.connected) {
65 return;
66 }
68 await connector.createSession();
69 };
71 const disconnect = async () => {
72 if (!connector) {
73 return;
74 }
76 await connector.killSession();
77 };
79 const authenticate = async ({ hdPath = DEFAULT_HD_PATH }) => {
80 if (!(address && connector)) {
81 return;
82 }
84 const authToken = await getAuthToken({
85 address,
86 connector,
87 walletType: WALLET_TYPES.WALLET_CONNECT,
88 });
90 const wallet: WalletConnectWallet = {
91 address,
92 };
94 setAuthToken(Just(authToken));
95 setWallet(Just(wallet));
96 setWalletHdPath(hdPath);
97 };
99 const isConnected = () => {
100 if (!connector) {
101 return false;
102 }
104 return connector.connected;
105 };
107 const signTransaction = async ({
108 from,
109 to,
110 gas,
111 gasPrice,
112 value,
113 data,
114 nonce,
115 }: ITxData): Promise<string> => {
116 return new Promise((resolve, reject) => {
117 if (!connector || !isConnected()) {
118 reject(new Error('No connected wallet available for signing'));
119 return;
120 }
122 return connector
123 .signTransaction({
124 from,
125 to,
126 gas,
127 gasPrice,
128 value,
129 data,
130 nonce,
131 })
132 .then((txHash: string) => resolve(txHash))
133 .catch((error: Error) => reject(error));
134 });
135 };
137 // Event Handlers
138 const initConnectHandler = () => {
139 if (!connector) {
140 return;
141 }
143 connector.on('connect', async (error, payload: ConnectEvent) => {
144 if (error) {
145 throw error;
146 }
148 const address = payload.params[0].accounts[0];
149 setAddress(address);
150 setPeerMeta(payload.params[0].peerMeta);
151 });
152 };
154 const initDisconnectHandler = () => {
155 if (!connector) {
156 return;
157 }
159 connector.on('disconnect', (error, _payload) => {
160 if (error) {
161 throw error;
162 }
164 setAddress(null);
165 setPeerMeta(null);
166 resetWallet();
167 });
168 };
170 const initSessionUpdateHandler = () => {
171 if (!connector) {
172 return;
173 }
175 connector.on('session_update', (error, payload) => {
176 if (error) {
177 throw error;
178 }
180 disconnect();
181 });
182 };
184 const initModalClosedHandler = () => {
185 if (!connector) {
186 return;
187 }
189 connector.on('modal_closed', () => {
190 resetWallet();
191 resetConnector();
192 });
193 };
195 // Init and clean up
196 useEffect(() => {
197 if (!connector) {
198 return;
199 }
201 initConnectHandler();
202 initDisconnectHandler();
203 initSessionUpdateHandler();
204 initModalClosedHandler();
206 return () => {
207 // Clean up listeners
208 connector.off('connect');
209 connector.off('disconnect');
210 connector.off('session_update');
211 connector.off('modal_closed');
212 };
213 // eslint-disable-next-line react-hooks/exhaustive-deps
214 }, [connector]);
216 return {
217 address,
218 authenticate,
219 connect,
220 connector,
221 disconnect,
222 isConnected,
223 peerMeta,
224 resetConnector,
225 signTransaction,
226 };
227};