Refactored eip-1559 logic (#1610).
This commit is contained in:
parent
5456c35924
commit
c5bca7767e
@ -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";
|
||||
|
||||
@ -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>;
|
||||
@ -211,7 +217,6 @@ export type Listener = (...args: Array<any>) => void;
|
||||
|
||||
///////////////////////////////
|
||||
// Exported Abstracts
|
||||
|
||||
export abstract class Provider implements OnceBlockable {
|
||||
|
||||
// Network
|
||||
@ -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>;
|
||||
|
@ -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";
|
||||
@ -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
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
|
||||
@ -230,26 +211,23 @@ export abstract class Signer {
|
||||
});
|
||||
}
|
||||
|
||||
// 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
|
||||
|
||||
// Check the gasPrice == maxFeePerGas
|
||||
if (tx.gasPrice != null && !BigNumber.from(tx.gasPrice).eq(<BigNumberish>(tx.maxFeePerGas))) {
|
||||
logger.throwArgumentError("gasPrice/maxFeePerGas mismatch", "transaction", transaction);
|
||||
}
|
||||
|
||||
// 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 {
|
||||
|
||||
@ -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; }
|
||||
}
|
||||
@ -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.
|
||||
@ -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; }
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import {
|
||||
Block,
|
||||
BlockTag,
|
||||
EventType,
|
||||
FeeData,
|
||||
Filter,
|
||||
Log,
|
||||
Listener,
|
||||
@ -150,6 +151,7 @@ export {
|
||||
Block,
|
||||
BlockTag,
|
||||
EventType,
|
||||
FeeData,
|
||||
Filter,
|
||||
Log,
|
||||
Listener,
|
||||
|
@ -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 {
|
||||
@ -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 {
|
||||
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user