1import { useCallback, useEffect, useState } from 'react';
2import { Just } from 'folktale/maybe';
3import { toWei, toBN } from 'web3-utils';
5import { useNetwork } from 'store/network';
6import { useWallet } from 'store/wallet';
7import { usePointCursor } from 'store/pointCursor';
11 DEFAULT_GAS_PRICE_GWEI,
12 PROGRESS_ANIMATION_DELAY_MS,
16 sendSignedTransaction,
17 waitForTransactionConfirm,
20import * as need from 'lib/need';
21import { ensureFundsFor } from 'lib/tank';
22import useDeepEqualReference from 'lib/useDeepEqualReference';
23import useGasPrice from 'lib/useGasPrice';
24import timeout from 'lib/timeout';
29 BROADCASTED: 'BROADCASTED',
30 CONFIRMED: 'CONFIRMED',
31 COMPLETED: 'COMPLETED',
35 * manage the state around sending and confirming an ethereum transaction
36 * @param {Transaction Function()} transactionBuilder
37 * @param {Promise<any> Function()} refetch async function called after completion
38 * @param {number} initialGasLimit gas limit
39 * @param {number} initialGasPrice gas price in gwei
41export default function useEthereumTransaction(
44 initialGasLimit = GAS_LIMITS.DEFAULT,
45 initialGasPrice = DEFAULT_GAS_PRICE_GWEI
47 const { wallet, walletType, walletHdPath } = useWallet();
48 const { web3, networkType } = useNetwork();
49 const { pointCursor } = usePointCursor();
51 const _web3 = need.web3(web3);
52 const _wallet = need.wallet(wallet);
54 const [error, _setError] = useState();
55 const setError = useCallback(
64 const [state, setState] = useState(STATE.NONE);
65 const [chainId, setChainId] = useState();
66 const [nonce, setNonce] = useState();
67 const { gasPrice, setGasPrice, resetGasPrice } = useGasPrice(initialGasPrice);
68 const [gasLimit] = useState(initialGasLimit);
69 const [unsignedTransaction, setUnsignedTransaction] = useState();
70 const [signedTransaction, setSignedTransaction] = useState();
71 const [txHash, setTxHash] = useState();
72 const [needFunds, setNeedFunds] = useState();
73 const [confirmationProgress, setConfirmationProgress] = useState(0.0);
75 const initializing = nonce === undefined || chainId === undefined;
76 const constructed = !!unsignedTransaction;
77 const isDefaultState = state === STATE.NONE;
78 const signed = state === STATE.SIGNED;
79 const broadcasted = state === STATE.BROADCASTED;
80 const confirmed = state === STATE.CONFIRMED;
81 const completed = state === STATE.COMPLETED;
83 // lock inputs once we're out of the default state
84 const inputsLocked = !isDefaultState;
87 const canSign = !initializing && constructed && isDefaultState;
89 const construct = useCallback(
91 setUnsignedTransaction(await transactionBuilder(...args)),
95 const unconstruct = useCallback(() => setUnsignedTransaction(undefined), [
96 setUnsignedTransaction,
99 const generateAndSign = useCallback(async () => {
103 const txn = await signTransaction({
108 txn: unsignedTransaction,
111 gasPrice: gasPrice.toFixed(0),
115 setSignedTransaction(txn);
116 setState(STATE.SIGNED);
133 const broadcast = useCallback(async () => {
135 setConfirmationProgress(0.0);
138 const rawTx = hexify(signedTransaction.serialize());
139 const costGwei = toBN(gasLimit).mul(toBN(gasPrice));
140 const cost = toWei(costGwei.toString(), 'gwei');
142 let usedTank = false;
143 // if this ethereum transaction is being executed by a specific point
144 // see if we can use the tank
145 if (Just.hasInstance(pointCursor)) {
146 usedTank = await ensureFundsFor(
152 (address, minBalance, balance) =>
153 setNeedFunds({ address, minBalance, balance }),
154 () => setNeedFunds(undefined)
158 const txHash = await sendSignedTransaction(
161 /* doubtNonceError */ usedTank
164 setState(STATE.BROADCASTED);
167 await timeout(PROGRESS_ANIMATION_DELAY_MS);
169 setConfirmationProgress(0.2);
171 await waitForTransactionConfirm(_web3, txHash);
173 setConfirmationProgress(0.9);
175 setState(STATE.CONFIRMED);
189 const reset = useCallback(() => {
190 setTxHash(undefined);
191 setSignedTransaction(undefined);
193 setChainId(undefined);
195 setState(STATE.NONE);
197 setNeedFunds(undefined);
198 setConfirmationProgress(0.0);
199 }, [resetGasPrice, setError]);
203 // if nonce or chainId is undefined, re-fetch on-chain info
204 if (!(nonce === undefined || chainId === undefined)) {
211 const [nonce, chainId] = await Promise.all([
212 _web3.eth.getTransactionCount(_wallet.address),
213 _web3.eth.net.getId(),
227 return () => (mounted = false);
228 }, [_wallet, _web3, setError, nonce, chainId, networkType, initialGasPrice]);
240 console.error(error);
241 // track a user-friendly error
244 'The transaction succeeded but we were unable to refresh chain state. Refresh to continue.'
254 setConfirmationProgress(1.0);
256 await timeout(PROGRESS_ANIMATION_DELAY_MS);
262 setState(STATE.COMPLETED);
266 return () => (mounted = false);
267 }, [confirmed, refetch, completed, setError]);
269 const values = useDeepEqualReference({
285 confirmationProgress,
296 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