Skip to content

Commit

Permalink
Refactored eip-1559 logic (#1610).
Browse files Browse the repository at this point in the history
  • Loading branch information
ricmoo committed Jun 15, 2021
1 parent 5456c35 commit c5bca77
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 66 deletions.
26 changes: 24 additions & 2 deletions packages/abstract-provider/src.ts/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import { BigNumber, BigNumberish } from "@ethersproject/bignumber";
import { BytesLike, isHexString } from "@ethersproject/bytes";
import { Network } from "@ethersproject/networks";
import { Deferrable, Description, defineReadOnly } from "@ethersproject/properties";
import { Deferrable, Description, defineReadOnly, resolveProperties } from "@ethersproject/properties";
import { AccessListish, Transaction } from "@ethersproject/transactions";
import { OnceBlockable } from "@ethersproject/web";

Expand Down Expand Up @@ -117,6 +117,12 @@ export interface TransactionReceipt {
status?: number
};

export interface FeeData {
maxFeePerGas: null | BigNumber;
maxPriorityFeePerGas: null | BigNumber;
gasPrice: null | BigNumber;
}

export interface EventFilter {
address?: string;
topics?: Array<string | Array<string> | null>;
Expand Down Expand Up @@ -211,7 +217,6 @@ export type Listener = (...args: Array<any>) => void;

///////////////////////////////
// Exported Abstracts

export abstract class Provider implements OnceBlockable {

// Network
Expand All @@ -220,6 +225,23 @@ export abstract class Provider implements OnceBlockable {
// Latest State
abstract getBlockNumber(): Promise<number>;
abstract getGasPrice(): Promise<BigNumber>;
async getFeeData(): Promise<FeeData> {
const { block, gasPrice } = await resolveProperties({
block: this.getBlock(-1),
gasPrice: this.getGasPrice()
});

let maxFeePerGas = null, maxPriorityFeePerGas = null;

if (block && block.baseFee) {
maxFeePerGas = block.baseFee.mul(2);
//maxPriorityFeePerGas = BigNumber.from("1000000000");
// @TODO: This needs to come from somewhere.
maxPriorityFeePerGas = BigNumber.from("1");
}

return { maxFeePerGas, maxPriorityFeePerGas, gasPrice };
}

// Account
abstract getBalance(addressOrName: string | Promise<string>, blockTag?: BlockTag | Promise<BlockTag>): Promise<BigNumber>;
Expand Down
91 changes: 32 additions & 59 deletions packages/abstract-signer/src.ts/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"use strict";

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

export interface FeeData {
maxFeePerGas: null | BigNumber;
maxPriorityFeePerGas: null | BigNumber;
gasPrice: null | BigNumber;
}

// EIP-712 Typed Data
// See: https://eips.ethereum.org/EIPS/eip-712

Expand Down Expand Up @@ -146,21 +140,8 @@ export abstract class Signer {
}

async getFeeData(): Promise<FeeData> {
this._checkProvider("getFeeStats");

const { block, gasPrice } = await resolveProperties({
block: this.provider.getBlock(-1),
gasPrice: this.provider.getGasPrice()
});

let maxFeePerGas = null, maxPriorityFeePerGas = null;

if (block && block.baseFee) {
maxFeePerGas = block.baseFee.mul(2);
maxPriorityFeePerGas = BigNumber.from("1000000000");
}

return { maxFeePerGas, maxPriorityFeePerGas, gasPrice };
this._checkProvider("getFeeData");
return await this.provider.getFeeData();
}


Expand Down Expand Up @@ -230,26 +211,23 @@ export abstract class Signer {
});
}

if ((tx.type === 2 || tx.type == null) && (tx.maxFeePerGas != null && tx.maxPriorityFeePerGas != null)) {
// Fully-formed EIP-1559 transaction

// Check the gasPrice == maxFeePerGas
if (tx.gasPrice != null && !BigNumber.from(tx.gasPrice).eq(<BigNumberish>(tx.maxFeePerGas))) {
logger.throwArgumentError("gasPrice/maxFeePerGas mismatch", "transaction", transaction);
}
// Do not allow mixing pre-eip-1559 and eip-1559 proerties
const hasEip1559 = (tx.maxFeePerGas != null || tx.maxPriorityFeePerGas != null);
if (tx.gasPrice != null && (tx.type === 2 || hasEip1559)) {
logger.throwArgumentError("eip-1559 transaction do not support gasPrice", "transaction", transaction);
} else if ((tx.type === -1 || tx.type === 1) && hasEip1559) {
logger.throwArgumentError("pre-eip-1559 transaction do not support maxFeePerGas/maxPriorityFeePerGas", "transaction", transaction);
}

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

} else if (tx.type === -1 || tx.type === 1) {
// Explicit EIP-2930 or Legacy transaction

// Do not allow EIP-1559 properties
if (tx.maxFeePerGas != null || tx.maxPriorityFeePerGas != null) {
logger.throwArgumentError(`transaction type ${ tx.type } does not support eip-1559 keys`, "transaction", transaction);
}
// Explicit Legacy or EIP-2930 transaction

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

} else {

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

if (tx.gasPrice != null && tx.maxFeePerGas == null && tx.maxPriorityFeePerGas == null) {
// Legacy or EIP-2930 transaction, but without its type set
tx.type = (tx.accessList ? 1: -1);
// Upgrade transaction from null to eip-1559
tx.type = 2;

if (tx.gasPrice != null) {
// Using legacy gasPrice property on an eip-1559 network,
// so use gasPrice as both fee properties
const gasPrice = tx.gasPrice;
delete tx.gasPrice;
tx.maxFeePerGas = gasPrice;
tx.maxPriorityFeePerGas = gasPrice;

} else {
// Use EIP-1559; no gas price or one EIP-1559 property was specified

// Check that gasPrice == maxFeePerGas
if (tx.gasPrice != null) {
// The first condition fails only if gasPrice and maxPriorityFeePerGas
// were specified, which is a weird thing to do
if (tx.maxFeePerGas == null || !BigNumber.from(tx.gasPrice).eq(<BigNumberish>(tx.maxFeePerGas))) {
logger.throwArgumentError("gasPrice/maxFeePerGas mismatch", "transaction", transaction);
}
}

tx.type = 2;
// Populate missing fee data
if (tx.maxFeePerGas == null) { tx.maxFeePerGas = feeData.maxFeePerGas; }
if (tx.maxPriorityFeePerGas == null) { tx.maxPriorityFeePerGas = feeData.maxPriorityFeePerGas; }
}
Expand All @@ -287,14 +261,17 @@ export abstract class Signer {
// Network doesn't support EIP-1559...

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

tx.gasPrice = feeData.gasPrice;
tx.type = (tx.accessList ? 1: -1);
// Populate missing fee data
if (tx.gasPrice == null) { tx.gasPrice = feeData.gasPrice; }

// Explicitly set untyped transaction to legacy
tx.type = -1;

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

// Check gasPrice == maxFeePerGas
if (tx.gasPrice != null && !BigNumber.from(tx.gasPrice).eq(<BigNumberish>(tx.maxFeePerGas))) {
logger.throwArgumentError("gasPrice/maxFeePerGas mismatch", "transaction", transaction);
}

// Populate missing fee data
if (tx.maxFeePerGas == null) { tx.maxFeePerGas = feeData.maxFeePerGas; }
if (tx.maxPriorityFeePerGas == null) { tx.maxPriorityFeePerGas = feeData.maxPriorityFeePerGas; }
}
Expand Down
2 changes: 2 additions & 0 deletions packages/providers/src.ts/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
Block,
BlockTag,
EventType,
FeeData,
Filter,
Log,
Listener,
Expand Down Expand Up @@ -150,6 +151,7 @@ export {
Block,
BlockTag,
EventType,
FeeData,
Filter,
Log,
Listener,
Expand Down
12 changes: 7 additions & 5 deletions packages/transactions/src.ts/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ const transactionFields = [
];

const allowedTransactionKeys: { [ key: string ]: boolean } = {
chainId: true, data: true, gasLimit: true, gasPrice:true, nonce: true, to: true, value: true
chainId: true, data: true, gasLimit: true, gasPrice:true, nonce: true, to: true, value: true, type: true
}

export function computeAddress(key: BytesLike | string): string {
Expand Down Expand Up @@ -368,15 +368,16 @@ function _parseEip1559(payload: Uint8Array): Transaction {
value: handleNumber(transaction[6]),
data: transaction[7],
accessList: accessListify(transaction[8]),
hash: keccak256(payload)
};

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

tx.hash = keccak256(payload);

_parseEipSignature(tx, transaction.slice(9), _serializeEip2930);

return null;
return tx;
}

function _parseEip2930(payload: Uint8Array): Transaction {
Expand All @@ -395,13 +396,14 @@ function _parseEip2930(payload: Uint8Array): Transaction {
to: handleAddress(transaction[4]),
value: handleNumber(transaction[5]),
data: transaction[6],
accessList: accessListify(transaction[7]),
hash: keccak256(payload)
accessList: accessListify(transaction[7])
};

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

tx.hash = keccak256(payload);

_parseEipSignature(tx, transaction.slice(8), _serializeEip2930);

return tx;
Expand Down

0 comments on commit c5bca77

Please sign in to comment.