Skip to content

Commit c5bca77

Browse files
committed
Refactored eip-1559 logic (#1610).
1 parent 5456c35 commit c5bca77

File tree

4 files changed

+65
-66
lines changed

4 files changed

+65
-66
lines changed

packages/abstract-provider/src.ts/index.ts

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import { BigNumber, BigNumberish } from "@ethersproject/bignumber";
44
import { BytesLike, isHexString } from "@ethersproject/bytes";
55
import { Network } from "@ethersproject/networks";
6-
import { Deferrable, Description, defineReadOnly } from "@ethersproject/properties";
6+
import { Deferrable, Description, defineReadOnly, resolveProperties } from "@ethersproject/properties";
77
import { AccessListish, Transaction } from "@ethersproject/transactions";
88
import { OnceBlockable } from "@ethersproject/web";
99

@@ -117,6 +117,12 @@ export interface TransactionReceipt {
117117
status?: number
118118
};
119119

120+
export interface FeeData {
121+
maxFeePerGas: null | BigNumber;
122+
maxPriorityFeePerGas: null | BigNumber;
123+
gasPrice: null | BigNumber;
124+
}
125+
120126
export interface EventFilter {
121127
address?: string;
122128
topics?: Array<string | Array<string> | null>;
@@ -211,7 +217,6 @@ export type Listener = (...args: Array<any>) => void;
211217

212218
///////////////////////////////
213219
// Exported Abstracts
214-
215220
export abstract class Provider implements OnceBlockable {
216221

217222
// Network
@@ -220,6 +225,23 @@ export abstract class Provider implements OnceBlockable {
220225
// Latest State
221226
abstract getBlockNumber(): Promise<number>;
222227
abstract getGasPrice(): Promise<BigNumber>;
228+
async getFeeData(): Promise<FeeData> {
229+
const { block, gasPrice } = await resolveProperties({
230+
block: this.getBlock(-1),
231+
gasPrice: this.getGasPrice()
232+
});
233+
234+
let maxFeePerGas = null, maxPriorityFeePerGas = null;
235+
236+
if (block && block.baseFee) {
237+
maxFeePerGas = block.baseFee.mul(2);
238+
//maxPriorityFeePerGas = BigNumber.from("1000000000");
239+
// @TODO: This needs to come from somewhere.
240+
maxPriorityFeePerGas = BigNumber.from("1");
241+
}
242+
243+
return { maxFeePerGas, maxPriorityFeePerGas, gasPrice };
244+
}
223245

224246
// Account
225247
abstract getBalance(addressOrName: string | Promise<string>, blockTag?: BlockTag | Promise<BlockTag>): Promise<BigNumber>;

packages/abstract-signer/src.ts/index.ts

Lines changed: 32 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"use strict";
22

3-
import { BlockTag, Provider, TransactionRequest, TransactionResponse } from "@ethersproject/abstract-provider";
3+
import { BlockTag, FeeData, Provider, TransactionRequest, TransactionResponse } from "@ethersproject/abstract-provider";
44
import { BigNumber, BigNumberish } from "@ethersproject/bignumber";
55
import { Bytes, BytesLike } from "@ethersproject/bytes";
66
import { Deferrable, defineReadOnly, resolveProperties, shallowCopy } from "@ethersproject/properties";
@@ -19,12 +19,6 @@ const forwardErrors = [
1919
Logger.errors.REPLACEMENT_UNDERPRICED,
2020
];
2121

22-
export interface FeeData {
23-
maxFeePerGas: null | BigNumber;
24-
maxPriorityFeePerGas: null | BigNumber;
25-
gasPrice: null | BigNumber;
26-
}
27-
2822
// EIP-712 Typed Data
2923
// See: https://eips.ethereum.org/EIPS/eip-712
3024

@@ -146,21 +140,8 @@ export abstract class Signer {
146140
}
147141

148142
async getFeeData(): Promise<FeeData> {
149-
this._checkProvider("getFeeStats");
150-
151-
const { block, gasPrice } = await resolveProperties({
152-
block: this.provider.getBlock(-1),
153-
gasPrice: this.provider.getGasPrice()
154-
});
155-
156-
let maxFeePerGas = null, maxPriorityFeePerGas = null;
157-
158-
if (block && block.baseFee) {
159-
maxFeePerGas = block.baseFee.mul(2);
160-
maxPriorityFeePerGas = BigNumber.from("1000000000");
161-
}
162-
163-
return { maxFeePerGas, maxPriorityFeePerGas, gasPrice };
143+
this._checkProvider("getFeeData");
144+
return await this.provider.getFeeData();
164145
}
165146

166147

@@ -230,26 +211,23 @@ export abstract class Signer {
230211
});
231212
}
232213

233-
if ((tx.type === 2 || tx.type == null) && (tx.maxFeePerGas != null && tx.maxPriorityFeePerGas != null)) {
234-
// Fully-formed EIP-1559 transaction
235-
236-
// Check the gasPrice == maxFeePerGas
237-
if (tx.gasPrice != null && !BigNumber.from(tx.gasPrice).eq(<BigNumberish>(tx.maxFeePerGas))) {
238-
logger.throwArgumentError("gasPrice/maxFeePerGas mismatch", "transaction", transaction);
239-
}
214+
// Do not allow mixing pre-eip-1559 and eip-1559 proerties
215+
const hasEip1559 = (tx.maxFeePerGas != null || tx.maxPriorityFeePerGas != null);
216+
if (tx.gasPrice != null && (tx.type === 2 || hasEip1559)) {
217+
logger.throwArgumentError("eip-1559 transaction do not support gasPrice", "transaction", transaction);
218+
} else if ((tx.type === -1 || tx.type === 1) && hasEip1559) {
219+
logger.throwArgumentError("pre-eip-1559 transaction do not support maxFeePerGas/maxPriorityFeePerGas", "transaction", transaction);
220+
}
240221

222+
if ((tx.type === 2 || tx.type == null) && (tx.maxFeePerGas != null && tx.maxPriorityFeePerGas != null)) {
223+
// Fully-formed EIP-1559 transaction (skip getFeeData)
241224
tx.type = 2;
242225

243226
} else if (tx.type === -1 || tx.type === 1) {
244-
// Explicit EIP-2930 or Legacy transaction
245-
246-
// Do not allow EIP-1559 properties
247-
if (tx.maxFeePerGas != null || tx.maxPriorityFeePerGas != null) {
248-
logger.throwArgumentError(`transaction type ${ tx.type } does not support eip-1559 keys`, "transaction", transaction);
249-
}
227+
// Explicit Legacy or EIP-2930 transaction
250228

229+
// Populate missing gasPrice
251230
if (tx.gasPrice == null) { tx.gasPrice = this.getGasPrice(); }
252-
tx.type = (tx.accessList ? 1: -1);
253231

254232
} else {
255233

@@ -262,23 +240,19 @@ export abstract class Signer {
262240
if (feeData.maxFeePerGas != null && feeData.maxPriorityFeePerGas != null) {
263241
// The network supports EIP-1559!
264242

265-
if (tx.gasPrice != null && tx.maxFeePerGas == null && tx.maxPriorityFeePerGas == null) {
266-
// Legacy or EIP-2930 transaction, but without its type set
267-
tx.type = (tx.accessList ? 1: -1);
243+
// Upgrade transaction from null to eip-1559
244+
tx.type = 2;
245+
246+
if (tx.gasPrice != null) {
247+
// Using legacy gasPrice property on an eip-1559 network,
248+
// so use gasPrice as both fee properties
249+
const gasPrice = tx.gasPrice;
250+
delete tx.gasPrice;
251+
tx.maxFeePerGas = gasPrice;
252+
tx.maxPriorityFeePerGas = gasPrice;
268253

269254
} else {
270-
// Use EIP-1559; no gas price or one EIP-1559 property was specified
271-
272-
// Check that gasPrice == maxFeePerGas
273-
if (tx.gasPrice != null) {
274-
// The first condition fails only if gasPrice and maxPriorityFeePerGas
275-
// were specified, which is a weird thing to do
276-
if (tx.maxFeePerGas == null || !BigNumber.from(tx.gasPrice).eq(<BigNumberish>(tx.maxFeePerGas))) {
277-
logger.throwArgumentError("gasPrice/maxFeePerGas mismatch", "transaction", transaction);
278-
}
279-
}
280-
281-
tx.type = 2;
255+
// Populate missing fee data
282256
if (tx.maxFeePerGas == null) { tx.maxFeePerGas = feeData.maxFeePerGas; }
283257
if (tx.maxPriorityFeePerGas == null) { tx.maxPriorityFeePerGas = feeData.maxPriorityFeePerGas; }
284258
}
@@ -287,14 +261,17 @@ export abstract class Signer {
287261
// Network doesn't support EIP-1559...
288262

289263
// ...but they are trying to use EIP-1559 properties
290-
if (tx.maxFeePerGas != null || tx.maxPriorityFeePerGas != null) {
264+
if (hasEip1559) {
291265
logger.throwError("network does not support EIP-1559", Logger.errors.UNSUPPORTED_OPERATION, {
292266
operation: "populateTransaction"
293267
});
294268
}
295269

296-
tx.gasPrice = feeData.gasPrice;
297-
tx.type = (tx.accessList ? 1: -1);
270+
// Populate missing fee data
271+
if (tx.gasPrice == null) { tx.gasPrice = feeData.gasPrice; }
272+
273+
// Explicitly set untyped transaction to legacy
274+
tx.type = -1;
298275

299276
} else {
300277
// getFeeData has failed us.
@@ -306,11 +283,7 @@ export abstract class Signer {
306283
} else if (tx.type === 2) {
307284
// Explicitly using EIP-1559
308285

309-
// Check gasPrice == maxFeePerGas
310-
if (tx.gasPrice != null && !BigNumber.from(tx.gasPrice).eq(<BigNumberish>(tx.maxFeePerGas))) {
311-
logger.throwArgumentError("gasPrice/maxFeePerGas mismatch", "transaction", transaction);
312-
}
313-
286+
// Populate missing fee data
314287
if (tx.maxFeePerGas == null) { tx.maxFeePerGas = feeData.maxFeePerGas; }
315288
if (tx.maxPriorityFeePerGas == null) { tx.maxPriorityFeePerGas = feeData.maxPriorityFeePerGas; }
316289
}

packages/providers/src.ts/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
Block,
55
BlockTag,
66
EventType,
7+
FeeData,
78
Filter,
89
Log,
910
Listener,
@@ -150,6 +151,7 @@ export {
150151
Block,
151152
BlockTag,
152153
EventType,
154+
FeeData,
153155
Filter,
154156
Log,
155157
Listener,

packages/transactions/src.ts/index.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ const transactionFields = [
103103
];
104104

105105
const allowedTransactionKeys: { [ key: string ]: boolean } = {
106-
chainId: true, data: true, gasLimit: true, gasPrice:true, nonce: true, to: true, value: true
106+
chainId: true, data: true, gasLimit: true, gasPrice:true, nonce: true, to: true, value: true, type: true
107107
}
108108

109109
export function computeAddress(key: BytesLike | string): string {
@@ -368,15 +368,16 @@ function _parseEip1559(payload: Uint8Array): Transaction {
368368
value: handleNumber(transaction[6]),
369369
data: transaction[7],
370370
accessList: accessListify(transaction[8]),
371-
hash: keccak256(payload)
372371
};
373372

374373
// Unsigned EIP-1559 Transaction
375374
if (transaction.length === 9) { return tx; }
376375

376+
tx.hash = keccak256(payload);
377+
377378
_parseEipSignature(tx, transaction.slice(9), _serializeEip2930);
378379

379-
return null;
380+
return tx;
380381
}
381382

382383
function _parseEip2930(payload: Uint8Array): Transaction {
@@ -395,13 +396,14 @@ function _parseEip2930(payload: Uint8Array): Transaction {
395396
to: handleAddress(transaction[4]),
396397
value: handleNumber(transaction[5]),
397398
data: transaction[6],
398-
accessList: accessListify(transaction[7]),
399-
hash: keccak256(payload)
399+
accessList: accessListify(transaction[7])
400400
};
401401

402402
// Unsigned EIP-2930 Transaction
403403
if (transaction.length === 8) { return tx; }
404404

405+
tx.hash = keccak256(payload);
406+
405407
_parseEipSignature(tx, transaction.slice(8), _serializeEip2930);
406408

407409
return tx;

0 commit comments

Comments
 (0)