@@ -1,20 +1,17 @@
1−import Common, { Chain, Hardfork } from '@ethereumjs/common';
1+import { Common, Chain, Hardfork } from '@ethereumjs/common';
2−import { JsonTx, Transaction } from '@ethereumjs/tx';
2+import { JsonTx, Transaction, FeeMarketEIP1559Transaction as EIP1559Transaction, TxOptions, TxData, FeeMarketEIP1559TxData as EIP1559TxData } from '@ethereumjs/tx';
3import { ITxData } from '@walletconnect/types';
3import { ITxData } from '@walletconnect/types';
4−import { toHex } from 'web3-utils';
4+import { toHex, toWei } from 'web3-utils';
5−import { safeFromWei, safeToWei } from './lib';
6import retry from 'async-retry';
5import retry from 'async-retry';
8import { NETWORK_TYPES } from './network';
6import { NETWORK_TYPES } from './network';
9−import { ledgerSignTransaction } from './ledger';
10−import { trezorSignTransaction } from './trezor';
11import { walletConnectSignTransaction } from './WalletConnect';
7import { walletConnectSignTransaction } from './WalletConnect';
12import { metamaskSignTransaction } from './metamask';
8import { metamaskSignTransaction } from './metamask';
13import { addHexPrefix } from './utils/address';
9import { addHexPrefix } from './utils/address';
14−import { CHECK_BLOCK_EVERY_MS, WALLET_TYPES } from './constants';
10+import { CHECK_BLOCK_EVERY_MS, EIP1559_TRANSACTION_TYPE, WALLET_TYPES } from './constants';
15import Web3 from 'web3';
11import Web3 from 'web3';
16−import { TransactionReceipt } from 'web3-core';
12+import { TransactionConfig, TransactionReceipt } from 'web3-core';
17import BridgeWallet from './types/BridgeWallet';
13import BridgeWallet from './types/BridgeWallet';
14+import { GasPriceData } from 'components/L2/Dropdowns/FeeDropdown';
19const RETRY_OPTIONS = {
16const RETRY_OPTIONS = {
20 retries: 99999,
17 retries: 99999,
@@ -23,12 +20,12 @@ const RETRY_OPTIONS = {
23 randomize: false,
20 randomize: false,
26−type SignableTx = Transaction & {
23+type SignableTx = (Transaction | EIP1559Transaction) & {
27 nonce: string;
24 nonce: string;
28 chainId: string;
25 chainId: string;
29− gasPrice: string;
26+ gasPrice?: string; // TODO: is this still needed?
30− gasLimit: string;
27+ gasLimit: string; // TODO: is this still needed?
31− from: string;
28+ from?: string;
34// catch-all type: Metamask passes an obj shaped like ITxData, WC passes JsonTx + from
31// catch-all type: Metamask passes an obj shaped like ITxData, WC passes JsonTx + from
@@ -39,13 +36,10 @@ export type FakeSignableTx =
40type TxSender = (txn: FakeSignableTx, web3: Web3) => Promise<string>; // Metamask requires Web3 (txhash)
37type TxSender = (txn: FakeSignableTx, web3: Web3) => Promise<string>; // Metamask requires Web3 (txhash)
43− serialize: () => string;
46−export type FakeSignedTx = SignedTx & {
39+export type FakeSignedTx = {
47 txn: FakeSignableTx;
40 txn: FakeSignableTx;
48 send: TxSender;
41 send: TxSender;
42+ serialize: () => string;
51const FakeSignResult = (txn: FakeSignableTx, send: TxSender): FakeSignedTx => {
45const FakeSignResult = (txn: FakeSignableTx, send: TxSender): FakeSignedTx => {
@@ -61,107 +55,81 @@ const FakeSignResult = (txn: FakeSignableTx, send: TxSender): FakeSignedTx => {
61interface signTransactionProps {
55interface signTransactionProps {
62 wallet: BridgeWallet;
56 wallet: BridgeWallet;
63 walletType: symbol;
57 walletType: symbol;
64− walletHdPath: string;
58+ walletHdPath: string; // TODO: is this still needed?
65 networkType: symbol;
59 networkType: symbol;
66− txn: Transaction;
60+ txn: Transaction | EIP1559Transaction;
67 nonce: number | string;
61 nonce: number | string;
68 chainId: number | string;
62 chainId: number | string;
69− gasPrice: string;
63+ gasPriceData: GasPriceData;
70− gasLimit: string;
64+ gasLimit: number;
71 txnSigner?: (args: ITxData) => Promise<string>;
65 txnSigner?: (args: ITxData) => Promise<string>;
72 txnSender?: (args: ITxData) => Promise<string>;
66 txnSender?: (args: ITxData) => Promise<string>;
70+ const ENDPOINT = `https://mainnet.infura.io/v3/${import.meta.env.VITE_INFURA_ENDPOINT}`;
71+ return new Web3(new Web3.providers.HttpProvider(ENDPOINT));
74+const estimateGasLimit = async (utx: TransactionConfig) => {
75+ const web3 = _web3();
76+ const estimate = await web3.eth.estimateGas(utx);
77+ console.log(estimate);
78+ return web3.utils.toBN(estimate).muln(120).divn(100); // 20% cushion
81+const getMaxFeePerGas = async () => {
82+ const web3 = _web3();
83+ const fee = await web3.eth.getGasPrice();
84+ return (Number(fee) * 1.20).toFixed(0); // 20% cushion
75const signTransaction = async ({
87const signTransaction = async ({
77 walletType,
89 walletType,
78− walletHdPath,
90+ walletHdPath, // TODO
79 networkType,
91 networkType,
81 nonce, // number
93 nonce, // number
82 chainId, // number
94 chainId, // number
83− gasPrice, // string, in gwei
95+ gasPriceData, // GasPriceData
84− gasLimit, // string | number
96+ gasLimit, // TODO: do we need the default values anymore? now that the estiamte is loaded dynamically
85 txnSigner, // optionally inject a transaction signing function,
97 txnSigner, // optionally inject a transaction signing function,
86 txnSender, // and a sending function, for wallets that need these passed in.
98 txnSender, // and a sending function, for wallets that need these passed in.
87}: signTransactionProps) => {
99}: signTransactionProps) => {
88− // TODO: require these in txn object
100+ console.log('txn', txn);
89− nonce = toHex(nonce);
90− chainId = toHex(chainId);
91− gasPrice = toHex(safeToWei(gasPrice, 'gwei'));
92− gasLimit = toHex(gasLimit);
93 const from = wallet.address;
101 const from = wallet.address;
102+ const estimate = await estimateGasLimit({...txn, from });
103+ const maxFeePerGas = await getMaxFeePerGas();
95− const txParams = { nonce, chainId, gasPrice, gasLimit, from };
105+ const txParams: EIP1559TxData = {
106+ data: toHex(txn.data),
108+ gasLimit: toHex(estimate),
109+ maxFeePerGas: toHex(maxFeePerGas),
110+ maxPriorityFeePerGas: toHex(toWei(Math.round(gasPriceData.maxPriorityFeePerGas).toFixed(0), 'gwei')),
111+ nonce: toHex(nonce),
112+ chainId: toHex(chainId),
113+ type: toHex(EIP1559_TRANSACTION_TYPE),
97− // NB (jtobin)
116+ const utx: SignableTx = Object.assign(txn, txParams);
99− // Ledger does not seem to handle EIP-155 automatically. When using a Ledger,
118+ // TODO: Ropsten will sunset Q4 2022, need to replace w/ support for Goerli
100− // if the block number is at least FORK_BLKNUM = 2675000, one needs to
119+ const chain =
101− // pre-set the ECDSA signature parameters with r = 0, s = 0, and v = chainId
120+ networkType === NETWORK_TYPES.ROPSTEN ? Chain.Ropsten : Chain.Mainnet;
102− // prior to signing.
104− // The easiest way to handle this is to just branch on the network, since
105− // mainnet and Ropsten have obviously passed FORK_BLKNUM. This is somewhat
106− // awkward when dealing with offline transactions, since we might want to
107− // test them on a local network as well.
109− // The best thing to do is probably to add an 'advanced' tab to offline
110− // transaction generation where one can disable the defaulted-on EIP-155
111− // settings in this case. This is pretty low-priority, but is a
112− // comprehensive solution.
116− // See https://github.com/LedgerHQ/ledgerjs/issues/43#issuecomment-366984725
118− // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-155.md
120− const eip155Params = {
122+ const txConfig: TxOptions = {
121− r: '0x00',
123+ freeze: false,
122− s: '0x00',
124+ common: new Common({
123− v: chainId,
125+ chain,
126+ hardfork: Hardfork.Merge,
126− const defaultEip155Networks = [
127− NETWORK_TYPES.MAINNET,
128− NETWORK_TYPES.ROPSTEN,
129− NETWORK_TYPES.OFFLINE,
132− const needEip155Params =
133− walletType === WALLET_TYPES.LEDGER &&
134− defaultEip155Networks.includes(networkType);
136− const signingParams = needEip155Params
137− ? Object.assign(txParams, eip155Params)
140− const utx: SignableTx = Object.assign(txn, signingParams);
142− const txConfig: any = { freeze: false };
143− // we must specify the chain *either* in the Common object,
144− // *or* eip155 style, but never both!
146− if (!needEip155Params) {
148− networkType === NETWORK_TYPES.ROPSTEN ? Chain.Ropsten : Chain.Mainnet;
149− txConfig.common = new Common({
151− hardfork: Hardfork.MuirGlacier,
155− let stx: Transaction | FakeSignedTx = Transaction.fromTxData(utx, txConfig);
130+ let stx = EIP1559Transaction.fromTxData(txParams, txConfig)
157− //TODO should try-catch and display error message to user,
158− // ie ledger's "pls enable contract data"
159− // needs to maybe happen at call-site though
160− if (walletType === WALLET_TYPES.LEDGER) {
161− await ledgerSignTransaction(stx, walletHdPath);
162− } else if (walletType === WALLET_TYPES.TREZOR) {
163− await trezorSignTransaction(stx, walletHdPath);
164− } else if (walletType === WALLET_TYPES.METAMASK) {
132+ if (walletType === WALLET_TYPES.METAMASK) {
165 return metamaskSignTransaction(utx);
133 return metamaskSignTransaction(utx);
166 } else if (walletType === WALLET_TYPES.WALLET_CONNECT) {
134 } else if (walletType === WALLET_TYPES.WALLET_CONNECT) {
167 if (!(txnSender && txnSigner)) {
135 if (!(txnSender && txnSigner)) {
@@ -184,11 +152,12 @@ const signTransaction = async ({
185const sendSignedTransaction = (
153const sendSignedTransaction = (
186 web3: Web3,
154 web3: Web3,
155+ // TODO: do we still need legacy TX type? elsewhere too
187− stx: Transaction | FakeSignedTx,
156+ stx: Transaction | EIP1559Transaction | FakeSignedTx,
188 doubtNonceError: boolean
157 doubtNonceError: boolean
189): Promise<string> => {
158): Promise<string> => {
190 // if we couldn't sign it, we depend on the given sender function
159 // if we couldn't sign it, we depend on the given sender function
191− if (!(stx instanceof Transaction)) {
160+ if (!(stx instanceof Transaction || stx instanceof EIP1559Transaction)) { // TODO
192 if (doubtNonceError) {
161 if (doubtNonceError) {
193 console.log('why doubting nonce error? tank unavailable without rawtx.');
162 console.log('why doubting nonce error? tank unavailable without rawtx.');
@@ -252,7 +221,7 @@ const waitForTransactionConfirm = (web3: Web3, txHash: string) => {
252// returns a Promise that resolves when all stxs have been sent & confirmed
221// returns a Promise that resolves when all stxs have been sent & confirmed
253const sendAndAwaitAll = (
222const sendAndAwaitAll = (
254 web3: Web3,
223 web3: Web3,
255− stxs: Transaction[] | FakeSignedTx[],
224+ stxs: Transaction[] | EIP1559Transaction[] | FakeSignedTx[],
256 doubtNonceError: boolean
225 doubtNonceError: boolean
258 return Promise.all(
227 return Promise.all(
@@ -265,7 +234,7 @@ const sendAndAwaitAll = (
266const sendAndAwaitAllSerial = (
235const sendAndAwaitAllSerial = (
267 web3: Web3,
236 web3: Web3,
268− stxs: Array<Transaction> | Array<FakeSignedTx>,
237+ stxs: Array<Transaction> | Array<EIP1559Transaction> | Array<FakeSignedTx>,
269 doubtNonceError: boolean
238 doubtNonceError: boolean
271 // tsc complains that stxs.reduce() is not callable
240 // tsc complains that stxs.reduce() is not callable
@@ -282,7 +251,7 @@ const sendAndAwaitAllSerial = (
283const sendTransactionsAndAwaitConfirm = async (
252const sendTransactionsAndAwaitConfirm = async (
284 web3: Web3,
253 web3: Web3,
285− signedTxs: Array<Transaction> | Array<FakeSignedTx>,
254+ signedTxs: Array<Transaction> | Array<EIP1559Transaction> | Array<FakeSignedTx>,
286 usedTank: boolean
255 usedTank: boolean
288 Promise.all(signedTxs.map(tx => sendSignedTransaction(web3, tx, usedTank)));
257 Promise.all(signedTxs.map(tx => sendSignedTransaction(web3, tx, usedTank)));