1import { useCallback, useEffect, useState } from 'react';
2import { Just } from 'folktale/maybe';
3import { toWei } from 'web3-utils';
5import { useNetwork } from 'store/network';
6import { useWallet } from 'store/wallet';
7import { usePointCursor } from 'store/pointCursor';
9import { GAS_LIMITS, DEFAULT_GAS_PRICE_GWEI } from './constants';
12 sendSignedTransaction,
13 waitForTransactionConfirm,
16import * as need from 'lib/need';
17import { ensureFundsFor } from 'lib/tank';
18import useDeepEqualReference from './useDeepEqualReference';
19import useGasPrice from './useGasPrice';
20import timeout from './timeout';
25 BROADCASTED: 'BROADCASTED',
26 CONFIRMED: 'CONFIRMED',
27 COMPLETED: 'COMPLETED',
31 * manage the state around sending and confirming an ethereum transaction
32 * @param {Transaction Function()} transactionBuilder
33 * @param {Promise<any> Function()} refetch async function called after completion
34 * @param {number} initialGasLimit gas limit
35 * @param {number} initialGasPrice gas price in gwei
37export default function useEthereumTransaction(
40 initialGasLimit = GAS_LIMITS.DEFAULT,
41 initialGasPrice = DEFAULT_GAS_PRICE_GWEI
43 const { wallet, walletType, walletHdPath } = useWallet();
44 const { web3, networkType } = useNetwork();
45 const { pointCursor } = usePointCursor();
47 const _web3 = need.web3(web3);
48 const _wallet = need.wallet(wallet);
50 const [error, _setError] = useState();
51 const setError = useCallback(
60 const [state, setState] = useState(STATE.NONE);
61 const [chainId, setChainId] = useState();
62 const [nonce, setNonce] = useState();
63 const { gasPrice, setGasPrice, resetGasPrice } = useGasPrice(initialGasPrice);
64 const [gasLimit] = useState(initialGasLimit);
65 const [unsignedTransaction, setUnsignedTransaction] = useState();
66 const [signedTransaction, setSignedTransaction] = useState();
67 const [txHash, setTxHash] = useState();
68 const [needFunds, setNeedFunds] = useState();
69 const [confirmationProgress, setConfirmationProgress] = useState(0.0);
71 const initializing = nonce === undefined || chainId === undefined;
72 const constructed = !!unsignedTransaction;
73 const isDefaultState = state === STATE.NONE;
74 const signed = state === STATE.SIGNED;
75 const broadcasted = state === STATE.BROADCASTED;
76 const confirmed = state === STATE.CONFIRMED;
77 const completed = state === STATE.COMPLETED;
79 // lock inputs once we're out of the default state
80 const inputsLocked = !isDefaultState;
83 const canSign = !initializing && constructed && isDefaultState;
85 const construct = useCallback(
87 setUnsignedTransaction(await transactionBuilder(...args)),
91 const unconstruct = useCallback(() => setUnsignedTransaction(undefined), [
92 setUnsignedTransaction,
95 const generateAndSign = useCallback(async () => {
99 const txn = await signTransaction({
104 txn: unsignedTransaction,
107 gasPrice: gasPrice.toFixed(0),
111 setSignedTransaction(txn);
112 setState(STATE.SIGNED);
129 const broadcast = useCallback(async () => {
131 setConfirmationProgress(0.0);
134 const rawTx = hexify(signedTransaction.serialize());
135 const cost = toWei(gasPrice.toFixed(0), 'gwei') * gasLimit;
137 let usedTank = false;
138 // if this ethereum transaction is being executed by a specific point
139 // see if we can use the tank
140 if (Just.hasInstance(pointCursor)) {
141 usedTank = await ensureFundsFor(
147 (address, minBalance, balance) =>
148 setNeedFunds({ address, minBalance, balance }),
149 () => setNeedFunds(undefined)
153 const txHash = await sendSignedTransaction(
156 /* doubtNonceError */ usedTank
159 setState(STATE.BROADCASTED);
162 await timeout(500); // wait for .animated-width to complete
164 setConfirmationProgress(0.2);
166 await waitForTransactionConfirm(_web3, txHash);
168 setConfirmationProgress(0.9);
170 setState(STATE.CONFIRMED);
184 const reset = useCallback(() => {
185 setTxHash(undefined);
186 setSignedTransaction(undefined);
188 setChainId(undefined);
190 setState(STATE.NONE);
192 setNeedFunds(undefined);
193 setConfirmationProgress(0.0);
194 }, [resetGasPrice, setError]);
198 // if nonce or chainId is undefined, re-fetch on-chain info
199 if (!(nonce === undefined || chainId === undefined)) {
206 const [nonce, chainId] = await Promise.all([
207 _web3.eth.getTransactionCount(_wallet.address),
208 _web3.eth.net.getId(),
222 return () => (mounted = false);
223 }, [_wallet, _web3, setError, nonce, chainId, networkType, initialGasPrice]);
235 console.error(error);
236 // track a user-friendly error
239 'The transaction succeeded but we were unable to refresh chain state. Refresh to continue.'
249 setConfirmationProgress(1.0);
251 await timeout(500); // wait for .animated-width to complete
257 setState(STATE.COMPLETED);
261 return () => (mounted = false);
262 }, [confirmed, refetch, completed, setError]);
264 const values = useDeepEqualReference({
280 confirmationProgress,
291 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