on 2b8b988 help sign in
bridge/src/lib/useEthereumTransaction.js
195 lines 4.8 kB history raw
1import { useCallback, useState } from 'react';
2import { fromWei } from 'web3-utils';
4import { useNetwork } from 'store/network';
5import { useWallet } from 'store/wallet';
7import {
8 signTransaction,
9 sendSignedTransaction,
10 waitForTransactionConfirm,
11} from './txn';
12import * as need from './need';
13import { GAS_LIMITS } from './constants';
14import useLifecycle from './useLifecycle';
16const STATE = {
17 NONE: 'NONE',
18 SIGNED: 'SIGNED',
19 BROADCASTED: 'BROADCASTED',
20 CONFIRMED: 'CONFIRMED',
21};
23/**
24 * manage the state around sending and confirming an ethereum transaction
25 * @param {number} initialGasLimit
26 */
27export default function useEthereumTransaction(
28 initialGasLimit = GAS_LIMITS.DEFAULT,
29 initialGasPrice = 20
30) {
31 const { wallet, walletType, walletHdPath } = useWallet();
32 const { web3, networkType } = useNetwork();
33 const _web3 = need.web3(web3);
34 const _wallet = need.wallet(wallet);
36 const [error, _setError] = useState();
37 const setError = useCallback(
38 error => {
39 _setError(error);
40 if (error) {
41 console.error(error);
42 }
43 },
44 [_setError]
45 );
46 const [state, setState] = useState(STATE.NONE);
47 const [chainId, setChainId] = useState();
48 const [nonce, setNonce] = useState();
49 const [gasPrice, setGasPrice] = useState(initialGasPrice); // gwei
50 const [suggestedGasPrice, setSuggestedGasPrice] = useState(gasPrice); // gwei
51 const [gasLimit] = useState(initialGasLimit);
52 const [unsignedTransaction, setUnsignedTransaction] = useState();
53 const [signedTransaction, setSignedTransaction] = useState();
54 const [txHash, setTxHash] = useState();
56 const initializing = nonce === undefined || chainId === undefined;
57 const constructed = !!unsignedTransaction;
58 const isDefaultState = state === STATE.NONE;
59 const signed = state === STATE.SIGNED;
60 const broadcasted = state === STATE.BROADCASTED;
61 const confirmed = state === STATE.CONFIRMED;
63 // disable the inputs when:
64 const inputsLocked = signed || broadcasted || confirmed;
66 // we can sign when:
67 const canSign = !initializing && constructed;
69 const construct = useCallback(txn => setUnsignedTransaction(txn), [
70 setUnsignedTransaction,
71 ]);
73 const unconstruct = useCallback(() => setUnsignedTransaction(undefined), [
74 setUnsignedTransaction,
75 ]);
77 const generateAndSign = useCallback(async () => {
78 try {
79 setError(undefined);
81 const txn = await signTransaction({
82 wallet: _wallet,
83 walletType,
84 walletHdPath,
85 networkType,
86 txn: unsignedTransaction,
87 nonce,
88 chainId,
89 gasPrice: gasPrice.toFixed(0),
90 gasLimit,
91 });
93 setSignedTransaction(txn);
94 setState(STATE.SIGNED);
95 } catch (error) {
96 setError(error);
97 }
98 }, [
99 _wallet,
100 chainId,
101 gasLimit,
102 gasPrice,
103 networkType,
104 nonce,
105 setError,
106 unsignedTransaction,
107 walletHdPath,
108 walletType,
109 ]);
111 const broadcast = useCallback(async () => {
112 try {
113 setError(undefined);
114 const txHash = await sendSignedTransaction(
115 _web3,
116 signedTransaction,
117 /* doubtNonceError */ false
118 );
120 setState(STATE.BROADCASTED);
121 setTxHash(txHash);
123 await waitForTransactionConfirm(_web3, txHash);
125 setState(STATE.CONFIRMED);
126 } catch (error) {
127 setError(error);
128 }
129 }, [_web3, setError, signedTransaction]);
131 const reset = useCallback(() => {
132 setUnsignedTransaction(undefined);
133 setTxHash(undefined);
134 setSignedTransaction(undefined);
135 setGasPrice(suggestedGasPrice);
136 setState(STATE.NONE);
137 setError(undefined);
138 }, [suggestedGasPrice, setError]);
140 const resetGasPrice = useCallback(() => setGasPrice(suggestedGasPrice), [
141 setGasPrice,
142 suggestedGasPrice,
143 ]);
145 useLifecycle(() => {
146 (async () => {
147 try {
148 setError(undefined);
149 const [nonce, chainId, estimatedGasPrice] = await Promise.all([
150 _web3.eth.getTransactionCount(_wallet.address),
151 _web3.eth.net.getId(),
152 _web3.eth.getGasPrice(),
153 ]);
155 setNonce(nonce);
156 setChainId(chainId);
157 const gasPrice = parseInt(fromWei(estimatedGasPrice, 'gwei'), 10);
158 setSuggestedGasPrice(gasPrice);
159 setGasPrice(gasPrice);
160 } catch (error) {
161 setError(error);
162 }
163 })();
164 });
166 const values = {
167 isDefaultState,
168 initializing,
169 construct,
170 unconstruct,
171 constructed,
172 canSign,
173 generateAndSign,
174 signed,
175 broadcast,
176 broadcasted,
177 confirmed,
178 reset,
179 error,
180 inputsLocked,
181 //
182 txHash,
183 signedTransaction,
184 gasPrice,
185 setGasPrice,
186 resetGasPrice,
187 nonce,
188 chainId,
189 };
191 return {
192 ...values,
193 bind: values,
194 };