1import { Common, Chain, Hardfork } from '@ethereumjs/common';
3 FeeMarketEIP1559Transaction as EIP1559Transaction,
5 FeeMarketEIP1559TxData as EIP1559TxData,
6} from '@ethereumjs/tx';
7import { toHex, toWei } from 'web3-utils';
8import retry from 'async-retry';
9import { NETWORK_TYPES } from './network';
10import { walletConnectSignTransaction } from './WalletConnect';
11import { metamaskSignTransaction } from './metamask';
12import { addHexPrefix } from './utils/address';
15 EIP1559_TRANSACTION_TYPE,
18import Web3 from 'web3';
19import { TransactionConfig, TransactionReceipt } from 'web3-core';
20import BridgeWallet from './types/BridgeWallet';
21import { GasPriceData } from 'components/L2/Dropdowns/FeeDropdown';
22import { ITxData } from './types/ITxData';
24const RETRY_OPTIONS = {
27 minTimeout: CHECK_BLOCK_EVERY_MS,
31export type FakeSignableTx = EIP1559Transaction;
33type TxSender = (txn: FakeSignableTx, web3: Web3) => Promise<string>;
35export type FakeSignedTx = {
38 serialize: () => string;
41const FakeSignResult = (txn: FakeSignableTx, send: TxSender): FakeSignedTx => {
45 return '???'; //NOTE update tank.js when changing this!
51interface signTransactionProps {
54 walletHdPath: string; // TODO: is this still needed?
56 txn: { data: string; to: string; value: number };
57 nonce: number | string;
58 chainId: number | string;
59 gasPriceData: GasPriceData;
61 txnSigner?: (args: ITxData) => Promise<string>;
62 txnSender?: (args: ITxData) => Promise<string>;
66const estimateGasLimit = async (utx: TransactionConfig, web3: Web3) => {
67 const estimate = await web3.eth.estimateGas(utx);
71 .divn(100); // 20% cushion
74const getMaxFeePerGas = async (web3: Web3) => {
75 const fee = await web3.eth.getGasPrice();
76 return (Number(fee) * 1.2).toFixed(0); // 20% cushion
79const signTransaction = async ({
84 txn, // output of transactionBuilder in useEthereumTransaction
87 gasPriceData, // GasPriceData
88 gasLimit, // TODO: do we need the default values anymore? now that the estiamte is loaded dynamically
89 txnSigner, // optionally inject a transaction signing function,
90 txnSender, // and a sending function, for wallets that need these passed in.
92}: signTransactionProps) => {
93 const from = wallet.address;
94 const estimate = await estimateGasLimit({ ...txn, from }, web3);
95 const maxFeePerGas = await getMaxFeePerGas(web3);
97 const txParams: EIP1559TxData = {
98 data: toHex(txn.data),
100 gasLimit: toHex(estimate),
101 maxFeePerGas: toHex(maxFeePerGas),
102 maxPriorityFeePerGas: toHex(
103 toWei(Math.round(gasPriceData.maxPriorityFeePerGas).toFixed(0), 'gwei')
106 chainId: toHex(chainId),
107 type: toHex(EIP1559_TRANSACTION_TYPE),
111 (networkType === NETWORK_TYPES.GOERLI) ||
112 (networkType === NETWORK_TYPES.LOCAL) ? Chain.Goerli : Chain.Mainnet;
114 const common = new Common({
116 hardfork: Hardfork.Merge,
119 if (networkType === NETWORK_TYPES.LOCAL) {
120 common._chainParams.chainId = 1337;
121 common._chainParams.name = "ganache";
124 const txConfig: TxOptions = {
129 let stx = EIP1559Transaction.fromTxData(txParams, txConfig);
131 if (walletType === WALLET_TYPES.METAMASK) {
132 return metamaskSignTransaction(stx);
133 } else if (walletType === WALLET_TYPES.WALLET_CONNECT) {
134 if (!(txnSender && txnSigner)) {
135 throw Error('WalletConnect TX signer unavailable');
138 stx = await walletConnectSignTransaction({
146 stx = stx.sign(wallet.privateKey!);
152const sendSignedTransaction = (
154 stx: EIP1559Transaction | FakeSignedTx,
155 doubtNonceError: boolean
156): Promise<string> => {
157 // if we couldn't sign it, we depend on the given sender function
158 if (!(stx instanceof EIP1559Transaction)) {
160 if (doubtNonceError) {
161 console.log('why doubting nonce error? tank unavailable without rawtx.');
164 throw new Error('no sign+sending function available');
166 return stx.send(stx.txn, web3);
169 let rawTx: string = hexify(stx.serialize());
170 let eventEmitter = web3.eth.sendSignedTransaction(rawTx);
171 return new Promise(async (resolve, reject) => {
173 .on('transactionHash', (hash: string) => {
176 .on('error', err => {
177 // if there's a nonce error, but we used the gas tank, it's likely
178 // that it's because the tank already submitted our transaction.
179 // we just wait for first confirmation here.
180 const message = typeof err === 'string' ? err : err.message || '';
182 message.includes('known transaction') ||
183 message.includes('already known');
185 message.includes("the tx doesn't have the correct nonce.") ||
186 message.includes('nonce too low');
187 if (rawTx && (isKnownError || (doubtNonceError && isNonceError))) {
189 'tx send error likely from gas tank submission, ignoring:',
192 const txHash = web3.utils.keccak256(rawTx);
196 const wrappedErr = err instanceof Error ? err : new Error(message);
203// returns a Promise<void>, throwing on tx failure
204const waitForTransactionConfirm = (web3: Web3, txHash: string) => {
205 return retry(async (bail, n) => {
206 const receipt = await web3.eth.getTransactionReceipt(txHash);
207 const confirmed = receipt !== null;
209 throw new Error('Transaction not confirmed.');
212 const success = receipt.status === true;
214 return bail(new Error('Transaction failed.'));
220// returns a Promise that resolves when all stxs have been sent & confirmed
221const sendAndAwaitAll = (
223 stxs: EIP1559Transaction[] | FakeSignedTx[],
224 doubtNonceError: boolean
227 stxs.map(async tx => {
228 const txHash = await sendSignedTransaction(web3, tx, doubtNonceError);
229 return await waitForTransactionConfirm(web3, txHash);
234const sendAndAwaitAllSerial = (
236 stxs: EIP1559Transaction[] | FakeSignedTx[],
237 doubtNonceError: boolean
239 //@ts-expect-error tsc complains that stxs.reduce() is not callable
242 promise: Promise<TransactionReceipt[]>,
243 stx: EIP1559Transaction | FakeSignedTx
245 promise.then(async (receipts = []) => {
246 const txHash = await sendSignedTransaction(web3, stx, doubtNonceError);
247 return [...receipts, await waitForTransactionConfirm(web3, txHash)];
253const sendTransactionsAndAwaitConfirm = async (
255 signedTxs: EIP1559Transaction[] | FakeSignedTx[],
258 Promise.all(signedTxs.map(tx => sendSignedTransaction(web3, tx, usedTank)));
260const hexify = (val: string | Buffer) => addHexPrefix(val.toString('hex'));
266 sendSignedTransaction,
267 waitForTransactionConfirm,
268 sendTransactionsAndAwaitConfirm,
270 sendAndAwaitAllSerial,
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