1import { useCallback, useEffect, useState } from 'react';
2import { Just } from 'folktale/maybe';
3import { fromWei, toWei } from 'web3-utils';
5import { useNetwork } from 'store/network';
6import { useWallet } from 'store/wallet';
7import { usePointCursor } from 'store/pointCursor';
9import { GAS_LIMITS } from './constants';
12 sendSignedTransaction,
13 waitForTransactionConfirm,
16import * as need from './need';
17import { ensureFundsFor } from './tank';
18import useDeepEqualReference from './useDeepEqualReference';
23 BROADCASTED: 'BROADCASTED',
24 CONFIRMED: 'CONFIRMED',
25 COMPLETED: 'COMPLETED',
29 * manage the state around sending and confirming an ethereum transaction
30 * @param {Transaction Function()} transactionBuilder
31 * @param {Promise<any> Function()} refetch async function called after completion
32 * @param {number} initialGasLimit gas limit
33 * @param {number} initialGasPrice gas price in gwei
35export default function useEthereumTransaction(
38 initialGasLimit = GAS_LIMITS.DEFAULT,
41 const { wallet, walletType, walletHdPath } = useWallet();
42 const { web3, networkType } = useNetwork();
43 const { pointCursor } = usePointCursor();
45 const _web3 = need.web3(web3);
46 const _wallet = need.wallet(wallet);
48 const [error, _setError] = useState();
49 const setError = useCallback(
58 const [state, setState] = useState(STATE.NONE);
59 const [chainId, setChainId] = useState();
60 const [nonce, setNonce] = useState();
61 const [gasPrice, setGasPrice] = useState(initialGasPrice); // gwei
62 const [suggestedGasPrice, setSuggestedGasPrice] = useState(gasPrice); // gwei
63 const [gasLimit] = useState(initialGasLimit);
64 const [unsignedTransaction, setUnsignedTransaction] = useState();
65 const [signedTransaction, setSignedTransaction] = useState();
66 const [txHash, setTxHash] = useState();
67 const [needFunds, setNeedFunds] = useState();
69 const initializing = nonce === undefined || chainId === undefined;
70 const constructed = !!unsignedTransaction;
71 const isDefaultState = state === STATE.NONE;
72 const signed = state === STATE.SIGNED;
73 const broadcasted = state === STATE.BROADCASTED;
74 const confirmed = state === STATE.CONFIRMED;
75 const completed = state === STATE.COMPLETED;
77 // lock inputs once we're out of the default state
78 const inputsLocked = !isDefaultState;
81 const canSign = !initializing && constructed && isDefaultState;
83 const construct = useCallback(
85 setUnsignedTransaction(await transactionBuilder(...args)),
89 const unconstruct = useCallback(() => setUnsignedTransaction(undefined), [
90 setUnsignedTransaction,
93 const generateAndSign = useCallback(async () => {
97 const txn = await signTransaction({
102 txn: unsignedTransaction,
105 gasPrice: gasPrice.toFixed(0),
109 setSignedTransaction(txn);
110 setState(STATE.SIGNED);
127 const broadcast = useCallback(async () => {
131 const rawTx = hexify(signedTransaction.serialize());
132 const cost = toWei(gasPrice.toFixed(0), 'gwei') * gasLimit;
134 let usedTank = false;
135 // if this ethereum transaction is being executed by a specific point
136 // see if we can use the tank
137 if (Just.hasInstance(pointCursor)) {
138 usedTank = await ensureFundsFor(
144 (address, minBalance, balance) =>
145 setNeedFunds({ address, minBalance, balance }),
146 () => setNeedFunds(undefined)
150 const txHash = await sendSignedTransaction(
153 /* doubtNonceError */ usedTank
156 setState(STATE.BROADCASTED);
159 await waitForTransactionConfirm(_web3, txHash);
161 setState(STATE.CONFIRMED);
175 const reset = useCallback(() => {
176 setTxHash(undefined);
177 setSignedTransaction(undefined);
179 setChainId(undefined);
180 setGasPrice(suggestedGasPrice);
181 setState(STATE.NONE);
183 setNeedFunds(undefined);
184 }, [suggestedGasPrice, setError]);
186 const resetGasPrice = useCallback(() => setGasPrice(suggestedGasPrice), [
192 // if nonce or chainId is undefined, re-fetch on-chain info
193 if (!(nonce === undefined || chainId === undefined)) {
200 const [nonce, chainId, estimatedGasPrice] = await Promise.all([
201 _web3.eth.getTransactionCount(_wallet.address),
202 _web3.eth.net.getId(),
203 _web3.eth.getGasPrice(),
208 const gasPrice = parseInt(fromWei(estimatedGasPrice, 'gwei'), 10);
209 setSuggestedGasPrice(gasPrice);
210 setGasPrice(gasPrice);
215 }, [_wallet, _web3, setError, nonce, chainId]);
218 let cancelled = false;
227 console.error(error);
228 // track a user-friendly error
231 'The transaction succeeded but we were unable to refresh chain state. Refresh to continue.'
241 setState(STATE.COMPLETED);
245 return () => (cancelled = true);
246 }, [confirmed, refetch, completed, setError]);
248 const values = useDeepEqualReference({
275 return { ...values, bind: values };
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list