on master help sign in
txns: refactor TX flow --> EIP1559
016cde3tomholford
src/components/InlineEthereumTransaction.jsx+3−3
@@ -42,8 +42,8 @@ export default function InlineEthereumTransaction({
42 completed,
42 completed,
43 reset,
43 reset,
44 error,
44 error,
45− gasPrice,
45+ gasPrice, // Type: GasPriceData
46− setGasPrice,
46+ setGasPrice, // Type: (g: GasPriceData) => void
47 resetGasPrice,
47 resetGasPrice,
48 txHashes,
48 txHashes,
49 nonce,
49 nonce,
@@ -109,7 +109,7 @@ export default function InlineEthereumTransaction({
109 safeFromWei(
109 safeFromWei(
110 safeToWei(
110 safeToWei(
111 toBN(gasLimit)
111 toBN(gasLimit)
112− .mul(toBN(gasPrice))
112+ .mul(toBN(gasPrice.price))
113 .mul(toBN(numTxs)),
113 .mul(toBN(numTxs)),
114 'gwei'
114 'gwei'
115 ),
115 ),
src/components/L2/Dropdowns/FeeDropdown.tsx+5−4
@@ -5,13 +5,14 @@ import useGasPrice from 'lib/useGasPrice';
5
5
6import Dropdown from './Dropdown';
6import Dropdown from './Dropdown';
7import './FeeDropdown.scss';
7import './FeeDropdown.scss';
8+import { defaultGasValues } from 'lib/getSuggestedGasPrice';
8
9
9const PRICE_LABELS = ['Fast', 'Normal', 'Slow'];
10const PRICE_LABELS = ['Fast', 'Normal', 'Slow'];
10
11
11export interface GasPriceData {
12export interface GasPriceData {
12 price: number;
13 price: number;
13 wait: string;
14 wait: string;
14− maxFeePerGas: number;
15+ maxFeePerGas: string; // hex
15 maxPriorityFeePerGas: number;
16 maxPriorityFeePerGas: number;
16 suggestedBaseFeePerGas: number;
17 suggestedBaseFeePerGas: number;
17}
18}
@@ -23,7 +24,7 @@ export const formatDisplay = ({ price, wait }: GasPriceData) =>
23export default function FeeDropdown({
24export default function FeeDropdown({
24 setGasPrice,
25 setGasPrice,
25}: {
26}: {
26− setGasPrice: (g: number) => void;
27+ setGasPrice: (g: GasPriceData) => void;
27}) {
28}) {
28 const { suggestedGasPrices }: { suggestedGasPrices: {
29 const { suggestedGasPrices }: { suggestedGasPrices: {
29 fast: GasPriceData,
30 fast: GasPriceData,
@@ -42,7 +43,7 @@ export default function FeeDropdown({
42 const cleanedValue = e.target.value.replace(/[^0-9]/g, '');
43 const cleanedValue = e.target.value.replace(/[^0-9]/g, '');
43 const cleanedNum = Number(cleanedValue);
44 const cleanedNum = Number(cleanedValue);
44 setCustom(cleanedValue);
45 setCustom(cleanedValue);
45− setGasPrice(cleanedNum);
46+ setGasPrice(defaultGasValues(cleanedNum).average);
46
47
47 let customWait = 'Unknown';
48 let customWait = 'Unknown';
48
49
@@ -74,7 +75,7 @@ export default function FeeDropdown({
74 const selectPrice = useCallback(
75 const selectPrice = useCallback(
75 (value: GasPriceData) => () => {
76 (value: GasPriceData) => () => {
76 setSelected(value);
77 setSelected(value);
77− setGasPrice(value.price);
78+ setGasPrice(value);
78 setOpen(false);
79 setOpen(false);
79 },
80 },
80 [setGasPrice, setSelected, setOpen]
81 [setGasPrice, setSelected, setOpen]
src/lib/WalletConnect.ts+5−4
@@ -1,12 +1,13 @@
1import { ITxData } from '@walletconnect/types';
1import { ITxData } from '@walletconnect/types';
2−import { JsonTx, Transaction } from '@ethereumjs/tx';
2+import { JsonTx, Transaction, FeeMarketEIP1559Transaction as EIP1559Transaction } from '@ethereumjs/tx';
3import { stripHexPrefix } from './utils/address';
3import { stripHexPrefix } from './utils/address';
4import { FakeSignableTx, FakeSignResult } from './txn';
4import { FakeSignableTx, FakeSignResult } from './txn';
5import Web3 from 'web3';
5import Web3 from 'web3';
6+import { EIP1559_TRANSACTION_TYPE } from './constants';
6
7
7type SignWalletConnectTransactionArgs = {
8type SignWalletConnectTransactionArgs = {
8 from: string;
9 from: string;
9− txn: Transaction;
10+ txn: Transaction | EIP1559Transaction;
10 txnSigner: (txn: ITxData) => Promise<string>;
11 txnSigner: (txn: ITxData) => Promise<string>;
11 txnSender: (txn: ITxData) => Promise<string>;
12 txnSender: (txn: ITxData) => Promise<string>;
12};
13};
@@ -24,7 +25,7 @@ const walletConnectSignTransaction = async ({
24 gas: string;
25 gas: string;
25 } = txn.toJSON();
26 } = txn.toJSON();
26 wcFormattedTx.from = from;
27 wcFormattedTx.from = from;
27− wcFormattedTx.gas = txn.gasLimit.toString();
28+ wcFormattedTx.type = `0x${EIP1559_TRANSACTION_TYPE}`;
28
29
29 let signature;
30 let signature;
30 try {
31 try {
@@ -42,7 +43,7 @@ const walletConnectSignTransaction = async ({
42 }
43 }
43 }
44 }
44 const serializedTx = Buffer.from(stripHexPrefix(signature), 'hex');
45 const serializedTx = Buffer.from(stripHexPrefix(signature), 'hex');
45− const signedTx = Transaction.fromSerializedTx(serializedTx);
46+ const signedTx = EIP1559Transaction.fromSerializedTx(serializedTx);
46
47
47 return signedTx;
48 return signedTx;
48};
49};
src/lib/constants.ts+3
@@ -224,10 +224,13 @@ const LOCKUP_TOOLTIP =
224// the initial network key revision is always 1
224// the initial network key revision is always 1
225const INITIAL_NETWORK_KEY_REVISION = 1;
225const INITIAL_NETWORK_KEY_REVISION = 1;
226
226
227+const EIP1559_TRANSACTION_TYPE = 2;
228+
227export {
229export {
228 CHECK_BLOCK_EVERY_MS,
230 CHECK_BLOCK_EVERY_MS,
229 DEFAULT_GAS_PRICE_GWEI,
231 DEFAULT_GAS_PRICE_GWEI,
230 DEFAULT_HD_PATH,
232 DEFAULT_HD_PATH,
233+ EIP1559_TRANSACTION_TYPE,
231 ETH_ZERO_ADDR,
234 ETH_ZERO_ADDR,
232 ETH_ZERO_ADDR_SHORT,
235 ETH_ZERO_ADDR_SHORT,
233 LINEAR_STAR_RELEASE,
236 LINEAR_STAR_RELEASE,
src/lib/getSuggestedGasPrice.js+24−8
@@ -1,24 +1,40 @@
1import { NETWORK_TYPES } from './network';
1import { NETWORK_TYPES } from './network';
2import { DEFAULT_GAS_PRICE_GWEI, MAX_GAS_PRICE_GWEI } from './constants';
2import { DEFAULT_GAS_PRICE_GWEI, MAX_GAS_PRICE_GWEI } from './constants';
3import { formatWait } from 'components/L2/Dropdowns/FeeDropdown';
3import { formatWait } from 'components/L2/Dropdowns/FeeDropdown';
4+import Web3 from 'web3';
4
5
5// ethgasstation returns values in floating point, one order of magitude
6// ethgasstation returns values in floating point, one order of magitude
6// more than gwei. see: https://docs.ethgasstation.info
7// more than gwei. see: https://docs.ethgasstation.info
7// we don't want to charge users more than the gas tank funds
8// we don't want to charge users more than the gas tank funds
8const minGas = gas => Math.min(Math.ceil(gas), MAX_GAS_PRICE_GWEI);
9const minGas = gas => Math.min(Math.ceil(gas), MAX_GAS_PRICE_GWEI);
9
10
11+const feeToInt = (f) => f < 1 ? 1 : Math.round(f);
12+
13+const feeToWei = (fee) => Web3.utils.toHex(Web3.utils.toWei(String(fee), 'gwei' ))
14+
15+const calculateMaxFee = (baseFee, maxPriorityFee) => feeToWei(Math.round((2 * baseFee) + maxPriorityFee))
16+
10export const defaultGasValues = value => ({
17export const defaultGasValues = value => ({
11 fast: {
18 fast: {
12 price: value,
19 price: value,
13 wait: '1',
20 wait: '1',
21+ maxFeePerGas: value,
22+ maxPriorityFeePerGas: 1,
23+ suggestedBaseFeePerGas: value > 2 ? value -1 : 1,
14 },
24 },
15 average: {
25 average: {
16 price: value,
26 price: value,
17 wait: '1',
27 wait: '1',
28+ maxFeePerGas: value,
29+ maxPriorityFeePerGas: 1,
30+ suggestedBaseFeePerGas: value > 2 ? value -1 : 1,
18 },
31 },
19 low: {
32 low: {
20 price: value,
33 price: value,
21 wait: '1',
34 wait: '1',
35+ maxFeePerGas: value,
36+ maxPriorityFeePerGas: 1,
37+ suggestedBaseFeePerGas: value > 2 ? value -1 : 1,
22 },
38 },
23});
39});
24
40
@@ -58,28 +74,28 @@ export default async function getSuggestedGasPrice(networkType) {
58
74
59 const suggestedBaseFeePerGas = Number(etherscanGasJson.result.suggestBaseFee);
75 const suggestedBaseFeePerGas = Number(etherscanGasJson.result.suggestBaseFee);
60
76
61− // Calculations inspired by:
77+ // Calculations derived from:
62− // https://github.com/liquality/chainify/blob/6e8243e8daf4dc219dcfd7df144ead3ff6183b26/packages/evm/lib/fee/EIP1559FeeApiProvider/ethereum.ts#L5
78+ // https://www.blocknative.com/blog/eip-1559-fees
63 return {
79 return {
64 fast: {
80 fast: {
65 price: minGas(etherscanGasJson.result.FastGasPrice),
81 price: minGas(etherscanGasJson.result.FastGasPrice),
66 wait: formatWait(ethgasstationGasJson.fastWait),
82 wait: formatWait(ethgasstationGasJson.fastWait),
67− maxFeePerGas: minGas(etherscanGasJson.result.FastGasPrice),
83+ maxFeePerGas: calculateMaxFee(suggestedBaseFeePerGas, etherscanGasJson.result.FastGasPrice - suggestedBaseFeePerGas),
68− maxPriorityFeePerGas: (etherscanGasJson.result.FastGasPrice - suggestedBaseFeePerGas),
84+ maxPriorityFeePerGas: feeToInt((etherscanGasJson.result.FastGasPrice - suggestedBaseFeePerGas)),
69 suggestedBaseFeePerGas
85 suggestedBaseFeePerGas
70 },
86 },
71 average: {
87 average: {
72 price: minGas(etherscanGasJson.result.ProposeGasPrice),
88 price: minGas(etherscanGasJson.result.ProposeGasPrice),
73 wait: formatWait(ethgasstationGasJson.avgWait),
89 wait: formatWait(ethgasstationGasJson.avgWait),
74− maxFeePerGas: minGas(etherscanGasJson.result.ProposeGasPrice),
90+ maxFeePerGas: calculateMaxFee(suggestedBaseFeePerGas, etherscanGasJson.result.ProposeGasPrice - suggestedBaseFeePerGas),
75− maxPriorityFeePerGas: (etherscanGasJson.result.ProposeGasPrice - suggestedBaseFeePerGas),
91+ maxPriorityFeePerGas: feeToInt((etherscanGasJson.result.ProposeGasPrice - suggestedBaseFeePerGas)),
76 suggestedBaseFeePerGas
92 suggestedBaseFeePerGas
77 },
93 },
78 low: {
94 low: {
79 price: minGas(etherscanGasJson.result.SafeGasPrice),
95 price: minGas(etherscanGasJson.result.SafeGasPrice),
80 wait: formatWait(ethgasstationGasJson.safeLowWait),
96 wait: formatWait(ethgasstationGasJson.safeLowWait),
81− maxFeePerGas: minGas(etherscanGasJson.result.SafeGasPrice),
97+ maxFeePerGas: calculateMaxFee(suggestedBaseFeePerGas, etherscanGasJson.result.SafeGasPrice - suggestedBaseFeePerGas),
82− maxPriorityFeePerGas: (etherscanGasJson.result.SafeGasPrice - suggestedBaseFeePerGas),
98+ maxPriorityFeePerGas: feeToInt((etherscanGasJson.result.SafeGasPrice - suggestedBaseFeePerGas)),
83 suggestedBaseFeePerGas
99 suggestedBaseFeePerGas
84 },
100 },
85 };
101 };
src/lib/metamask.ts+4−4
@@ -17,16 +17,16 @@ export const metamaskSignTransaction = async (txn: FakeSignableTx) => {
17};
17};
18
18
19const metamaskSendTransaction = async (txn: FakeSignableTx, web3: Web3) => {
19const metamaskSendTransaction = async (txn: FakeSignableTx, web3: Web3) => {
20− const { data, gasLimit, gasPrice, nonce, to, from } = txn;
20+ const { data, gasLimit, gasPrice, nonce, to, from } = txn; // TODO
21− if (!(data && gasLimit && gasPrice && nonce && to && from)) {
21+ if (!(data && gasLimit && gasPrice && nonce && to && from)) { // TODO
22 throw new Error('Unable to send Metamask TX, something is missing');
22 throw new Error('Unable to send Metamask TX, something is missing');
23 }
23 }
24
24
25− // web3-eth/TransactionConfig type
25+ // https://docs.metamask.io/guide/sending-transactions.html#transaction-parameters
26 const metamaskFormattedTxn = {
26 const metamaskFormattedTxn = {
27 data: data.toString(),
27 data: data.toString(),
28 gasLimit,
28 gasLimit,
29− gasPrice,
29+ gasPrice, // TODO
30 nonce: Number(nonce).toString(),
30 nonce: Number(nonce).toString(),
31 to: to.toString(),
31 to: to.toString(),
32 from: toHex(from),
32 from: toHex(from),
src/lib/reticket.js+6−5
@@ -146,9 +146,10 @@ export async function reticketPointBetweenWallets({
146
146
147 progress(TRANSACTION_PROGRESS.SIGNING);
147 progress(TRANSACTION_PROGRESS.SIGNING);
148
148
149− const gasPriceGwei = (
149+ const gasPriceData = (
150 await getSuggestedGasPrice(networkType)
150 await getSuggestedGasPrice(networkType)
151− ).average.price.toFixed();
151+ ).average;
152+ const gasPriceGwei = gasPriceData.price.toFixed();
152 const chainId = await web3.eth.net.getId();
153 const chainId = await web3.eth.net.getId();
153 const gasPriceWeiBN = toBN(safeToWei(gasPriceGwei, 'gwei'));
154 const gasPriceWeiBN = toBN(safeToWei(gasPriceGwei, 'gwei'));
154 let inviteNonce = await web3.eth.getTransactionCount(fromWallet.address);
155 let inviteNonce = await web3.eth.getTransactionCount(fromWallet.address);
@@ -169,7 +170,7 @@ export async function reticketPointBetweenWallets({
169 txn: txs[i],
170 txn: txs[i],
170 nonce: inviteNonce + i,
171 nonce: inviteNonce + i,
171 chainId,
172 chainId,
172− gasPrice: gasPriceGwei,
173+ gasPriceData,
173 gasLimit: txs[i].gas,
174 gasLimit: txs[i].gas,
174 txnSigner,
175 txnSigner,
175 txnSender,
176 txnSender,
@@ -240,7 +241,7 @@ export async function reticketPointBetweenWallets({
240 txn,
241 txn,
241 chainId,
242 chainId,
242 networkType,
243 networkType,
243− gasPrice: gasPriceGwei,
244+ gasPriceData,
244 gasLimit,
245 gasLimit,
245 nonce: inviteNonce,
246 nonce: inviteNonce,
246 });
247 });
@@ -270,7 +271,7 @@ export async function reticketPointBetweenWallets({
270 txn,
271 txn,
271 chainId,
272 chainId,
272 networkType,
273 networkType,
273− gasPrice: gasPriceGwei,
274+ gasPriceData,
274 gasLimit,
275 gasLimit,
275 nonce: inviteNonce,
276 nonce: inviteNonce,
276 });
277 });
src/lib/txn.ts+69−100
@@ -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';
7−
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';
18
15
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,
24};
21};
25
22
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;
32};
29};
33
30
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 =
39
36
40type TxSender = (txn: FakeSignableTx, web3: Web3) => Promise<string>; // Metamask requires Web3 (txhash)
37type TxSender = (txn: FakeSignableTx, web3: Web3) => Promise<string>; // Metamask requires Web3 (txhash)
41
38
42−type SignedTx = {
43− serialize: () => string;
44−};
45−
46−export type FakeSignedTx = SignedTx & {
39+export type FakeSignedTx = {
47 txn: FakeSignableTx;
40 txn: FakeSignableTx;
48 send: TxSender;
41 send: TxSender;
42+ serialize: () => string;
49};
43};
50
44
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>;
73}
67}
74
68
69+const _web3 = () => {
70+ const ENDPOINT = `https://mainnet.infura.io/v3/${import.meta.env.VITE_INFURA_ENDPOINT}`;
71+ return new Web3(new Web3.providers.HttpProvider(ENDPOINT));
72+}
73+
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
79+}
80+
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
85+}
86+
75const signTransaction = async ({
87const signTransaction = async ({
76 wallet,
88 wallet,
77 walletType,
89 walletType,
78− walletHdPath,
90+ walletHdPath, // TODO
79 networkType,
91 networkType,
80 txn,
92 txn,
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();
94
104
95− const txParams = { nonce, chainId, gasPrice, gasLimit, from };
105+ const txParams: EIP1559TxData = {
106+ data: toHex(txn.data),
107+ to: toHex(txn.to),
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),
114+ }
96
115
97− // NB (jtobin)
116+ const utx: SignableTx = Object.assign(txn, txParams);
98− //
117+
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.
103− //
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.
108− //
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.
113− //
114− // See:
115− //
116− // See https://github.com/LedgerHQ/ledgerjs/issues/43#issuecomment-366984725
117− //
118− // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-155.md
119
121
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,
127+ })
124 };
128 };
125
129
126− const defaultEip155Networks = [
127− NETWORK_TYPES.MAINNET,
128− NETWORK_TYPES.ROPSTEN,
129− NETWORK_TYPES.OFFLINE,
130− ];
131−
132− const needEip155Params =
133− walletType === WALLET_TYPES.LEDGER &&
134− defaultEip155Networks.includes(networkType);
135−
136− const signingParams = needEip155Params
137− ? Object.assign(txParams, eip155Params)
138− : txParams;
139−
140− const utx: SignableTx = Object.assign(txn, signingParams);
141−
142− const txConfig: any = { freeze: false };
143− // we must specify the chain *either* in the Common object,
144− // *or* eip155 style, but never both!
145− //
146− if (!needEip155Params) {
147− const chain =
148− networkType === NETWORK_TYPES.ROPSTEN ? Chain.Ropsten : Chain.Mainnet;
149− txConfig.common = new Common({
150− chain: chain,
151− hardfork: Hardfork.MuirGlacier,
152− });
153− }
154−
155− let stx: Transaction | FakeSignedTx = Transaction.fromTxData(utx, txConfig);
130+ let stx = EIP1559Transaction.fromTxData(txParams, txConfig)
156
131
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 ({
184
152
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.');
194 }
163 }
@@ -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
257) => {
226) => {
258 return Promise.all(
227 return Promise.all(
@@ -265,7 +234,7 @@ const sendAndAwaitAll = (
265
234
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
270) => {
239) => {
271 // tsc complains that stxs.reduce() is not callable
240 // tsc complains that stxs.reduce() is not callable
@@ -282,7 +251,7 @@ const sendAndAwaitAllSerial = (
282
251
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
287) =>
256) =>
288 Promise.all(signedTxs.map(tx => sendSignedTransaction(web3, tx, usedTank)));
257 Promise.all(signedTxs.map(tx => sendSignedTransaction(web3, tx, usedTank)));
src/lib/useEthereumTransaction.js+3−3
@@ -135,7 +135,7 @@ export default function useEthereumTransaction(
135 txn: utx,
135 txn: utx,
136 nonce: nonce + i,
136 nonce: nonce + i,
137 chainId,
137 chainId,
138− gasPrice: gasPrice.toFixed(0),
138+ gasPriceData: gasPrice,
139 gasLimit,
139 gasLimit,
140 txnSigner,
140 txnSigner,
141 txnSender,
141 txnSender,
@@ -181,7 +181,7 @@ export default function useEthereumTransaction(
181 const rawTxs = signedTransactions.map(stx => hexify(stx.serialize()));
181 const rawTxs = signedTransactions.map(stx => hexify(stx.serialize()));
182
182
183 const costGwei = toBN(gasLimit)
183 const costGwei = toBN(gasLimit)
184− .mul(toBN(gasPrice))
184+ .mul(toBN(gasPrice.price))
185 .mul(toBN(rawTxs.length));
185 .mul(toBN(rawTxs.length));
186 const cost = safeToWei(costGwei, 'gwei');
186 const cost = safeToWei(costGwei, 'gwei');
187 let usedTank = false;
187 let usedTank = false;
@@ -229,7 +229,7 @@ export default function useEthereumTransaction(
229 receipts.reduce(
229 receipts.reduce(
230 (acc, receipt) =>
230 (acc, receipt) =>
231 toBN(receipt.gasUsed)
231 toBN(receipt.gasUsed)
232− .mul(toBN(gasPrice))
232+ .mul(toBN(gasPrice.price))
233 .add(acc),
233 .add(acc),
234 toBN(0)
234 toBN(0)
235 ),
235 ),
src/lib/useGasPrice.js+4−4
@@ -20,7 +20,7 @@ export default function useGasPrice(initialGasPrice = DEFAULT_GAS_PRICE_GWEI) {
20 const [suggestedGasPrices, setSuggestedGasPrices] = useState(
20 const [suggestedGasPrices, setSuggestedGasPrices] = useState(
21 defaultGasValues(initialGasPrice)
21 defaultGasValues(initialGasPrice)
22 ); // fast, average, low. In gwei
22 ); // fast, average, low. In gwei
23− const [gasPrice, setGasPrice] = useState(initialGasPrice); // gwei
23+ const [gasPrice, setGasPrice] = useState(defaultGasValues(initialGasPrice).average); // GasPriceData
24
24
25 const fetchPrices = useCallback(async () => {
25 const fetchPrices = useCallback(async () => {
26 if (!mounted) {
26 if (!mounted) {
@@ -32,17 +32,17 @@ export default function useGasPrice(initialGasPrice = DEFAULT_GAS_PRICE_GWEI) {
32
32
33 // TODO: BN
33 // TODO: BN
34 setSuggestedGasPrices(suggestedGasPrices);
34 setSuggestedGasPrices(suggestedGasPrices);
35− setGasPrice(suggestedGasPrices.average.price);
35+ setGasPrice(suggestedGasPrices.average);
36 }, [mounted]);
36 }, [mounted]);
37
37
38 useEffect(() => {
38 useEffect(() => {
39 setMounted(true);
39 setMounted(true);
40− fetchPrices()
40+ fetchPrices();
41 return () => (setMounted(false));
41 return () => (setMounted(false));
42 }, [mounted, networkType]);
42 }, [mounted, networkType]);
43
43
44 const resetGasPrice = useCallback(
44 const resetGasPrice = useCallback(
45− () => setGasPrice(suggestedGasPrices.average.price),
45+ () => setGasPrice(suggestedGasPrices.average),
46 [suggestedGasPrices]
46 [suggestedGasPrices]
47 );
47 );
48
48