on a13b9eb help sign in
bridge/src/lib/useEthereumTransaction.js
165 lines 4.1 kB history raw
1import { useCallback, useEffect, useState } from 'react';
2import { Just } from 'folktale/maybe';
4import { useNetwork } from 'store/network';
5import { useWallet } from 'store/wallet';
7import {
8 signTransaction,
9 sendSignedTransaction,
10 waitForTransactionConfirm,
11 hexify,
12} from './txn';
13import * as need from './need';
14import { toHex, toWei, fromWei } from 'web3-utils';
16const STATE = {
17 NONE: 'NONE',
18 GENERATED: 'GENERATED',
19 SIGNED: 'SIGNED',
20 BROADCASTED: 'BROADCASTED',
21 CONFIRMED: 'CONFIRMED',
22};
24/**
25 * manage the state around sending and confirming an ethereum transaction
26 * @param {Maybe<EthereumTx>} unsignedTx
27 * @param {number} initialGasLimit
28 */
29export default function useEthereumTransaction(
30 unsignedTx,
31 initialGasLimit = 0
32) {
33 const { wallet, walletType, walletHdPath } = useWallet();
34 const { web3, networkType } = useNetwork();
35 const _web3 = need.web3(web3);
36 const _wallet = need.wallet(wallet);
38 const [error, setError] = useState();
39 const [state, setState] = useState(STATE.NONE);
40 const [chainId, setChainId] = useState();
41 const [nonce, setNonce] = useState();
42 const [gasPrice, setGasPrice] = useState(20); // gwei
43 const [gasLimit] = useState(initialGasLimit);
44 const [unsignedTransaction, setUnsignedTransaction] = useState();
45 const [signedTransaction, setSignedTransaction] = useState();
47 const initializing = nonce === undefined || chainId === undefined;
48 const constructed = Just.hasInstance(unsignedTx);
49 const generated = state === STATE.GENERATED;
50 const signed = state === STATE.SIGNED;
51 const broadcasted = state === STATE.BROADCASTED;
52 const confirmed = state === STATE.CONFIRMED;
54 const generate = useCallback(() => {
55 const txn = unsignedTx.value;
56 try {
57 txn.gas = toHex(gasLimit);
58 txn.gasPrice = toHex(toWei(gasPrice, 'gwei'));
60 setUnsignedTransaction(txn);
61 setState(STATE.GENERATED);
62 } catch (error) {
63 setError(error);
64 }
65 }, [gasLimit, gasPrice, unsignedTx.value]);
67 const sign = useCallback(async () => {
68 try {
69 // TODO: sign the transaction
70 const txn = await signTransaction(
71 _wallet,
72 walletType,
73 walletHdPath.getOrElse(''),
74 networkType,
75 chainId,
76 unsignedTransaction
77 );
78 const stx = hexify(txn.serialize());
80 setSignedTransaction(stx);
81 setState(STATE.SIGNED);
82 } catch (error) {
83 setError(error);
84 }
85 }, [
86 _wallet,
87 chainId,
88 networkType,
89 unsignedTransaction,
90 walletHdPath,
91 walletType,
92 ]);
94 const waitForConfirmation = useCallback(
95 async txHash => {
96 try {
97 await waitForTransactionConfirm(_web3, txHash);
98 setState(STATE.CONFIRMED);
99 } catch (error) {
100 setError(error);
101 }
102 },
103 [_web3]
104 );
106 const broadcast = useCallback(async () => {
107 try {
108 const txHash = await sendSignedTransaction(_web3, signedTransaction);
110 setState(STATE.BROADCASTED);
111 waitForConfirmation(txHash);
112 } catch (error) {
113 setError(error);
114 }
115 }, [_web3, signedTransaction, waitForConfirmation]);
117 const reset = useCallback(() => {
118 setState(STATE.NONE);
119 setError(undefined);
120 setGasPrice(0);
121 setSignedTransaction(undefined);
122 }, [setState]);
124 useEffect(() => {
125 (async () => {
126 try {
127 const [nonce, chainId, estimatedGasPrice] = await Promise.all([
128 _web3.eth.getTransactionCount(_wallet.address),
129 _web3.eth.net.getId(),
130 _web3.eth.getGasPrice(),
131 ]);
133 setNonce(nonce);
134 setChainId(chainId);
135 setGasPrice(fromWei(estimatedGasPrice, 'gwei'));
136 } catch (error) {
137 setError(error);
138 }
139 })();
140 }, [_wallet.address, _web3.eth]);
142 return {
143 initializing,
144 constructed,
145 generate,
146 generated,
147 sign,
148 signed,
149 broadcast,
150 broadcasted,
151 confirmed,
152 reset,
153 error,
154 bind: {
155 error,
156 initializing,
157 constructed,
158 generated,
159 signed,
160 broadcasted,
161 confirmed,
162 setGasPrice,
163 },
164 };