Added EIP-4844 BLOb transactions (#4554).
This commit is contained in:
parent
a26ff77c21
commit
9c1e82e123
@ -31,6 +31,7 @@ link-eip-2098 [EIP-2098](https://eips.ethereum.org/EIPS/eip-2098)
|
||||
link-eip-2304 [EIP-2304](https://eips.ethereum.org/EIPS/eip-2304)
|
||||
link-eip-2718 [EIP-2718](https://eips.ethereum.org/EIPS/eip-2718)
|
||||
link-eip-2930 [EIP-2930](https://eips.ethereum.org/EIPS/eip-2930)
|
||||
link-eip-4844 [EIP-4844](https://eips.ethereum.org/EIPS/eip-4844)
|
||||
|
||||
# Open Standards
|
||||
link-base58 [Base58](https://en.bitcoinwiki.org/wiki/Base58)
|
||||
|
@ -64,6 +64,15 @@ describe("Tests Unsigned Transaction Serializing", function() {
|
||||
assert.equal(tx.unsignedSerialized, test.unsignedLondon, "unsignedLondon");
|
||||
});
|
||||
}
|
||||
|
||||
for (const test of tests) {
|
||||
if (!test.unsignedCancun) { continue; }
|
||||
it(`serialized unsigned cancun transaction: ${ test.name }`, function() {
|
||||
const txData = Object.assign({ }, test.transaction, { type: 3 });
|
||||
const tx = Transaction.from(txData);
|
||||
assert.equal(tx.unsignedSerialized, test.unsignedCancun, "unsignedCancun");
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
describe("Tests Signed Transaction Serializing", function() {
|
||||
@ -127,6 +136,20 @@ describe("Tests Signed Transaction Serializing", function() {
|
||||
assert.equal(tx.serialized, test.signedLondon, "signedLondon");
|
||||
});
|
||||
}
|
||||
|
||||
for (const test of tests) {
|
||||
if (!test.signedCancun) { continue; }
|
||||
|
||||
it(`serialized signed Cancun transaction: ${ test.name }`, function() {
|
||||
const txData = Object.assign({ }, test.transaction, {
|
||||
type: 3,
|
||||
signature: test.signatureCancun
|
||||
});
|
||||
|
||||
const tx = Transaction.from(txData);
|
||||
assert.equal(tx.serialized, test.signedCancun, "signedCancun");
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
function assertTxUint(actual: null | bigint, _expected: undefined | string, name: string): void {
|
||||
@ -227,6 +250,18 @@ describe("Tests Unsigned Transaction Parsing", function() {
|
||||
assertTxEqual(tx, expected);
|
||||
});
|
||||
}
|
||||
|
||||
for (const test of tests) {
|
||||
if (!test.unsignedCancun) { continue; }
|
||||
it(`parses unsigned Cancun transaction: ${ test.name }`, function() {
|
||||
const tx = Transaction.from(test.unsignedCancun);
|
||||
|
||||
const expected = addDefaults(test.transaction);
|
||||
expected.gasPrice = null;
|
||||
|
||||
assertTxEqual(tx, expected);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
describe("Tests Signed Transaction Parsing", function() {
|
||||
@ -277,6 +312,7 @@ describe("Tests Signed Transaction Parsing", function() {
|
||||
assert.equal(tx.isLegacy(), true, "isLegacy");
|
||||
assert.equal(tx.isBerlin(), false, "isBerlin");
|
||||
assert.equal(tx.isLondon(), false, "isLondon");
|
||||
assert.equal(tx.isCancun(), false, "isCancun");
|
||||
|
||||
assert.ok(!!tx.signature, "signature:!null")
|
||||
assert.equal(tx.signature.r, test.signatureEip155.r, "signature.r");
|
||||
@ -303,6 +339,7 @@ describe("Tests Signed Transaction Parsing", function() {
|
||||
assert.equal(tx.isLegacy(), false, "isLegacy");
|
||||
assert.equal(tx.isBerlin(), true, "isBerlin");
|
||||
assert.equal(tx.isLondon(), false, "isLondon");
|
||||
assert.equal(tx.isCancun(), false, "isCancun");
|
||||
|
||||
assert.ok(!!tx.signature, "signature:!null")
|
||||
assert.equal(tx.signature.r, test.signatureBerlin.r, "signature.r");
|
||||
@ -328,6 +365,7 @@ describe("Tests Signed Transaction Parsing", function() {
|
||||
assert.equal(tx.isLegacy(), false, "isLegacy");
|
||||
assert.equal(tx.isBerlin(), false, "isBerlin");
|
||||
assert.equal(tx.isLondon(), true, "isLondon");
|
||||
assert.equal(tx.isCancun(), false, "isCancun");
|
||||
|
||||
assert.ok(!!tx.signature, "signature:!null")
|
||||
assert.equal(tx.signature.r, test.signatureLondon.r, "signature.r");
|
||||
@ -339,6 +377,34 @@ describe("Tests Signed Transaction Parsing", function() {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
for (const test of tests) {
|
||||
if (!test.signedCancun) { continue; }
|
||||
it(`parses signed Cancun transaction: ${ test.name }`, function() {
|
||||
let tx = Transaction.from(test.signedCancun);
|
||||
|
||||
const expected = addDefaults(test.transaction);
|
||||
expected.gasPrice = null;
|
||||
|
||||
for (let i = 0; i < 2; i++) {
|
||||
assertTxEqual(tx, expected);
|
||||
|
||||
assert.equal(tx.typeName, "eip-4844", "typeName");
|
||||
assert.equal(tx.isLegacy(), false, "isLegacy");
|
||||
assert.equal(tx.isBerlin(), false, "isBerlin");
|
||||
assert.equal(tx.isLondon(), false, "isLondon");
|
||||
assert.equal(tx.isCancun(), true, "isCancun");
|
||||
|
||||
assert.ok(!!tx.signature, "signature:!null")
|
||||
assert.equal(tx.signature.r, test.signatureCancun.r, "signature.r");
|
||||
assert.equal(tx.signature.s, test.signatureCancun.s, "signature.s");
|
||||
assert.equal(tx.signature.yParity, parseInt(test.signatureCancun.v), "signature.v");
|
||||
|
||||
// Test cloning
|
||||
tx = tx.clone();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
describe("Tests Transaction Parameters", function() {
|
||||
|
@ -217,11 +217,14 @@ export interface TestCaseTransaction {
|
||||
signedBerlin: string;
|
||||
unsignedLondon: string;
|
||||
signedLondon: string;
|
||||
unsignedCancun: string;
|
||||
signedCancun: string;
|
||||
|
||||
signatureLegacy: TestCaseTransactionSig;
|
||||
signatureEip155: TestCaseTransactionSig;
|
||||
signatureBerlin: TestCaseTransactionSig;
|
||||
signatureLondon: TestCaseTransactionSig;
|
||||
signatureCancun: TestCaseTransactionSig;
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,9 +1,10 @@
|
||||
|
||||
import { getAddress } from "../address/index.js";
|
||||
import { ZeroAddress } from "../constants/addresses.js";
|
||||
import { keccak256, Signature, SigningKey } from "../crypto/index.js";
|
||||
import {
|
||||
concat, decodeRlp, encodeRlp, getBytes, getBigInt, getNumber, hexlify,
|
||||
assert, assertArgument, toBeArray, zeroPadValue
|
||||
assert, assertArgument, isHexString, toBeArray, zeroPadValue
|
||||
} from "../utils/index.js";
|
||||
|
||||
import { accessListify } from "./accesslist.js";
|
||||
@ -22,6 +23,7 @@ const BN_28 = BigInt(28)
|
||||
const BN_35 = BigInt(35);
|
||||
const BN_MAX_UINT = BigInt("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
|
||||
|
||||
|
||||
/**
|
||||
* A **TransactionLike** is an object which is appropriate as a loose
|
||||
* input for many operations which will populate missing properties of
|
||||
@ -97,6 +99,16 @@ export interface TransactionLike<A = string> {
|
||||
* The access list for berlin and london transactions.
|
||||
*/
|
||||
accessList?: null | AccessListish;
|
||||
|
||||
/**
|
||||
* The maximum fee per blob gas (see [[link-eip-4844]]).
|
||||
*/
|
||||
maxFeePerBlobGas?: null | BigNumberish;
|
||||
|
||||
/**
|
||||
* The versioned hashes (see [[link-eip-4844]]).
|
||||
*/
|
||||
blobVersionedHashes?: null | Array<string>;
|
||||
}
|
||||
|
||||
function handleAddress(value: string): null | string {
|
||||
@ -135,6 +147,14 @@ function formatAccessList(value: AccessListish): Array<[ string, Array<string> ]
|
||||
return accessListify(value).map((set) => [ set.address, set.storageKeys ]);
|
||||
}
|
||||
|
||||
function formatHashes(value: Array<string>, param: string): Array<string> {
|
||||
assertArgument(Array.isArray(value), `invalid ${ param }`, "value", value);
|
||||
for (let i = 0; i < value.length; i++) {
|
||||
assertArgument(isHexString(value[i], 32), "invalid ${ param } hash", `value[${ i }]`, value[i]);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
function _parseLegacy(data: Uint8Array): TransactionLike {
|
||||
const fields: any = decodeRlp(data);
|
||||
|
||||
@ -186,13 +206,15 @@ function _parseLegacy(data: Uint8Array): TransactionLike {
|
||||
}
|
||||
|
||||
function _serializeLegacy(tx: Transaction, sig?: Signature): string {
|
||||
assertArgument(tx.isLegacy(), "internal check failed; !legacy", "tx", tx);
|
||||
|
||||
const fields: Array<any> = [
|
||||
formatNumber(tx.nonce || 0, "nonce"),
|
||||
formatNumber(tx.gasPrice || 0, "gasPrice"),
|
||||
formatNumber(tx.gasLimit || 0, "gasLimit"),
|
||||
((tx.to != null) ? getAddress(tx.to): "0x"),
|
||||
formatNumber(tx.value || 0, "value"),
|
||||
(tx.data || "0x"),
|
||||
formatNumber(tx.nonce, "nonce"),
|
||||
formatNumber(tx.gasPrice, "gasPrice"),
|
||||
formatNumber(tx.gasLimit, "gasLimit"),
|
||||
(tx.to || "0x"),
|
||||
formatNumber(tx.value, "value"),
|
||||
tx.data,
|
||||
];
|
||||
|
||||
let chainId = BN_0;
|
||||
@ -265,14 +287,12 @@ function _parseEip1559(data: Uint8Array): TransactionLike {
|
||||
assertArgument(Array.isArray(fields) && (fields.length === 9 || fields.length === 12),
|
||||
"invalid field count for transaction type: 2", "data", hexlify(data));
|
||||
|
||||
const maxPriorityFeePerGas = handleUint(fields[2], "maxPriorityFeePerGas");
|
||||
const maxFeePerGas = handleUint(fields[3], "maxFeePerGas");
|
||||
const tx: TransactionLike = {
|
||||
type: 2,
|
||||
chainId: handleUint(fields[0], "chainId"),
|
||||
nonce: handleNumber(fields[1], "nonce"),
|
||||
maxPriorityFeePerGas: maxPriorityFeePerGas,
|
||||
maxFeePerGas: maxFeePerGas,
|
||||
maxPriorityFeePerGas: handleUint(fields[2], "maxPriorityFeePerGas"),
|
||||
maxFeePerGas: handleUint(fields[3], "maxFeePerGas"),
|
||||
gasPrice: null,
|
||||
gasLimit: handleUint(fields[4], "gasLimit"),
|
||||
to: handleAddress(fields[5]),
|
||||
@ -291,17 +311,19 @@ function _parseEip1559(data: Uint8Array): TransactionLike {
|
||||
return tx;
|
||||
}
|
||||
|
||||
function _serializeEip1559(tx: TransactionLike, sig?: Signature): string {
|
||||
function _serializeEip1559(tx: Transaction, sig?: Signature): string {
|
||||
assertArgument(tx.isLondon(), "internal check failed; !london", "tx", tx);
|
||||
|
||||
const fields: Array<any> = [
|
||||
formatNumber(tx.chainId || 0, "chainId"),
|
||||
formatNumber(tx.nonce || 0, "nonce"),
|
||||
formatNumber(tx.maxPriorityFeePerGas || 0, "maxPriorityFeePerGas"),
|
||||
formatNumber(tx.maxFeePerGas || 0, "maxFeePerGas"),
|
||||
formatNumber(tx.gasLimit || 0, "gasLimit"),
|
||||
((tx.to != null) ? getAddress(tx.to): "0x"),
|
||||
formatNumber(tx.value || 0, "value"),
|
||||
(tx.data || "0x"),
|
||||
(formatAccessList(tx.accessList || []))
|
||||
formatNumber(tx.chainId, "chainId"),
|
||||
formatNumber(tx.nonce, "nonce"),
|
||||
formatNumber(tx.maxPriorityFeePerGas, "maxPriorityFeePerGas"),
|
||||
formatNumber(tx.maxFeePerGas, "maxFeePerGas"),
|
||||
formatNumber(tx.gasLimit, "gasLimit"),
|
||||
(tx.to || "0x"),
|
||||
formatNumber(tx.value, "value"),
|
||||
tx.data,
|
||||
formatAccessList(tx.accessList)
|
||||
];
|
||||
|
||||
if (sig) {
|
||||
@ -341,16 +363,18 @@ function _parseEip2930(data: Uint8Array): TransactionLike {
|
||||
return tx;
|
||||
}
|
||||
|
||||
function _serializeEip2930(tx: TransactionLike, sig?: Signature): string {
|
||||
function _serializeEip2930(tx: Transaction, sig?: Signature): string {
|
||||
assertArgument(tx.isBerlin(), "internal check failed; !berlin", "tx", tx);
|
||||
|
||||
const fields: any = [
|
||||
formatNumber(tx.chainId || 0, "chainId"),
|
||||
formatNumber(tx.nonce || 0, "nonce"),
|
||||
formatNumber(tx.gasPrice || 0, "gasPrice"),
|
||||
formatNumber(tx.gasLimit || 0, "gasLimit"),
|
||||
((tx.to != null) ? getAddress(tx.to): "0x"),
|
||||
formatNumber(tx.value || 0, "value"),
|
||||
(tx.data || "0x"),
|
||||
(formatAccessList(tx.accessList || []))
|
||||
formatNumber(tx.chainId, "chainId"),
|
||||
formatNumber(tx.nonce, "nonce"),
|
||||
formatNumber(tx.gasPrice, "gasPrice"),
|
||||
formatNumber(tx.gasLimit, "gasLimit"),
|
||||
(tx.to || "0x"),
|
||||
formatNumber(tx.value, "value"),
|
||||
tx.data,
|
||||
formatAccessList(tx.accessList)
|
||||
];
|
||||
|
||||
if (sig) {
|
||||
@ -362,6 +386,71 @@ function _serializeEip2930(tx: TransactionLike, sig?: Signature): string {
|
||||
return concat([ "0x01", encodeRlp(fields)]);
|
||||
}
|
||||
|
||||
function _parseEip4844(data: Uint8Array): TransactionLike {
|
||||
const fields: any = decodeRlp(getBytes(data).slice(1));
|
||||
|
||||
assertArgument(Array.isArray(fields) && (fields.length === 11 || fields.length === 14),
|
||||
"invalid field count for transaction type: 3", "data", hexlify(data));
|
||||
|
||||
const tx: TransactionLike = {
|
||||
type: 3,
|
||||
chainId: handleUint(fields[0], "chainId"),
|
||||
nonce: handleNumber(fields[1], "nonce"),
|
||||
maxPriorityFeePerGas: handleUint(fields[2], "maxPriorityFeePerGas"),
|
||||
maxFeePerGas: handleUint(fields[3], "maxFeePerGas"),
|
||||
gasPrice: null,
|
||||
gasLimit: handleUint(fields[4], "gasLimit"),
|
||||
to: handleAddress(fields[5]),
|
||||
value: handleUint(fields[6], "value"),
|
||||
data: hexlify(fields[7]),
|
||||
accessList: handleAccessList(fields[8], "accessList"),
|
||||
maxFeePerBlobGas: handleUint(fields[9], "maxFeePerBlobGas"),
|
||||
blobVersionedHashes: fields[10]
|
||||
};
|
||||
|
||||
assertArgument(tx.to != null, "invalid address for transaction type: 3", "data", data);
|
||||
|
||||
assertArgument(Array.isArray(tx.blobVersionedHashes), "invalid blobVersionedHashes: must be an array", "data", data);
|
||||
for (let i = 0; i < tx.blobVersionedHashes.length; i++) {
|
||||
assertArgument(isHexString(tx.blobVersionedHashes[i], 32), `invalid blobVersionedHash at index ${ i }: must be length 32`, "data", data);
|
||||
}
|
||||
|
||||
// Unsigned EIP-4844 Transaction
|
||||
if (fields.length === 11) { return tx; }
|
||||
|
||||
tx.hash = keccak256(data);
|
||||
|
||||
_parseEipSignature(tx, fields.slice(11));
|
||||
|
||||
return tx;
|
||||
}
|
||||
|
||||
function _serializeEip4844(tx: Transaction, sig?: Signature): string {
|
||||
assertArgument(tx.isCancun(), "internal check failed; !cancun", "tx", tx);
|
||||
|
||||
const fields: Array<any> = [
|
||||
formatNumber(tx.chainId, "chainId"),
|
||||
formatNumber(tx.nonce, "nonce"),
|
||||
formatNumber(tx.maxPriorityFeePerGas, "maxPriorityFeePerGas"),
|
||||
formatNumber(tx.maxFeePerGas, "maxFeePerGas"),
|
||||
formatNumber(tx.gasLimit, "gasLimit"),
|
||||
tx.to,
|
||||
formatNumber(tx.value, "value"),
|
||||
tx.data,
|
||||
(formatAccessList(tx.accessList)),
|
||||
formatNumber(tx.maxFeePerBlobGas, "maxFeePerBlobGas"),
|
||||
formatHashes(tx.blobVersionedHashes, "blobVersionedHashes")
|
||||
];
|
||||
|
||||
if (sig) {
|
||||
fields.push(formatNumber(sig.yParity, "yParity"));
|
||||
fields.push(toBeArray(sig.r));
|
||||
fields.push(toBeArray(sig.s));
|
||||
}
|
||||
|
||||
return concat([ "0x03", encodeRlp(fields)]);
|
||||
}
|
||||
|
||||
/**
|
||||
* A **Transaction** describes an operation to be executed on
|
||||
* Ethereum by an Externally Owned Account (EOA). It includes
|
||||
@ -388,6 +477,8 @@ export class Transaction implements TransactionLike<string> {
|
||||
#chainId: bigint;
|
||||
#sig: null | Signature;
|
||||
#accessList: null | AccessList;
|
||||
#maxFeePerBlobGas: null | bigint;
|
||||
#blobVersionedHashes: null | Array<string>;
|
||||
|
||||
/**
|
||||
* The transaction type.
|
||||
@ -410,6 +501,9 @@ export class Transaction implements TransactionLike<string> {
|
||||
case 2: case "london": case "eip-1559":
|
||||
this.#type = 2;
|
||||
break;
|
||||
case 3: case "cancun": case "eip-4844":
|
||||
this.#type = 3;
|
||||
break;
|
||||
default:
|
||||
assertArgument(false, "unsupported transaction type", "type", value);
|
||||
}
|
||||
@ -423,6 +517,7 @@ export class Transaction implements TransactionLike<string> {
|
||||
case 0: return "legacy";
|
||||
case 1: return "eip-2930";
|
||||
case 2: return "eip-1559";
|
||||
case 3: return "eip-4844";
|
||||
}
|
||||
|
||||
return null;
|
||||
@ -432,7 +527,11 @@ export class Transaction implements TransactionLike<string> {
|
||||
* The ``to`` address for the transaction or ``null`` if the
|
||||
* transaction is an ``init`` transaction.
|
||||
*/
|
||||
get to(): null | string { return this.#to; }
|
||||
get to(): null | string {
|
||||
const value = this.#to;
|
||||
if (value == null && this.type === 3) { return ZeroAddress; }
|
||||
return value;
|
||||
}
|
||||
set to(value: null | string) {
|
||||
this.#to = (value == null) ? null: getAddress(value);
|
||||
}
|
||||
@ -471,7 +570,7 @@ export class Transaction implements TransactionLike<string> {
|
||||
get maxPriorityFeePerGas(): null | bigint {
|
||||
const value = this.#maxPriorityFeePerGas;
|
||||
if (value == null) {
|
||||
if (this.type === 2) { return BN_0; }
|
||||
if (this.type === 2 || this.type === 3) { return BN_0; }
|
||||
return null;
|
||||
}
|
||||
return value;
|
||||
@ -487,7 +586,7 @@ export class Transaction implements TransactionLike<string> {
|
||||
get maxFeePerGas(): null | bigint {
|
||||
const value = this.#maxFeePerGas;
|
||||
if (value == null) {
|
||||
if (this.type === 2) { return BN_0; }
|
||||
if (this.type === 2 || this.type === 3) { return BN_0; }
|
||||
return null;
|
||||
}
|
||||
return value;
|
||||
@ -534,7 +633,11 @@ export class Transaction implements TransactionLike<string> {
|
||||
get accessList(): null | AccessList {
|
||||
const value = this.#accessList || null;
|
||||
if (value == null) {
|
||||
if (this.type === 1 || this.type === 2) { return [ ]; }
|
||||
if (this.type === 1 || this.type === 2 || this.type === 3) {
|
||||
// @TODO: in v7, this should assign the value or become
|
||||
// a live object itself, otherwise mutation is inconsistent
|
||||
return [ ];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
return value;
|
||||
@ -543,6 +646,39 @@ export class Transaction implements TransactionLike<string> {
|
||||
this.#accessList = (value == null) ? null: accessListify(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* The max fee per blob gas for Cancun transactions.
|
||||
*/
|
||||
get maxFeePerBlobGas(): null | bigint {
|
||||
const value = this.#maxFeePerBlobGas;
|
||||
if (value == null && this.type === 3) { return BN_0; }
|
||||
return value;
|
||||
}
|
||||
set maxFeePerBlobGas(value: null | BigNumberish) {
|
||||
this.#maxFeePerBlobGas = (value == null) ? null: getBigInt(value, "maxFeePerBlobGas");
|
||||
}
|
||||
|
||||
/**
|
||||
* The BLOB versioned hashes for Cancun transactions.
|
||||
*/
|
||||
get blobVersionedHashes(): null | Array<string> {
|
||||
// @TODO: Mutation is inconsistent; if unset, the returned value
|
||||
// cannot mutate the object, if set it can
|
||||
let value = this.#blobVersionedHashes;
|
||||
if (value == null && this.type === 3) { return [ ]; }
|
||||
return value;
|
||||
}
|
||||
set blobVersionedHashes(value: null | Array<string>) {
|
||||
if (value != null) {
|
||||
assertArgument(Array.isArray(value), "blobVersionedHashes must be an Array", "value", value);
|
||||
value = value.slice();
|
||||
for (let i = 0; i < value.length; i++) {
|
||||
assertArgument(isHexString(value[i], 32), "invalid blobVersionedHash", `value[${ i }]`, value[i]);
|
||||
}
|
||||
}
|
||||
this.#blobVersionedHashes = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new Transaction with default values.
|
||||
*/
|
||||
@ -550,15 +686,17 @@ export class Transaction implements TransactionLike<string> {
|
||||
this.#type = null;
|
||||
this.#to = null;
|
||||
this.#nonce = 0;
|
||||
this.#gasLimit = BigInt(0);
|
||||
this.#gasLimit = BN_0;
|
||||
this.#gasPrice = null;
|
||||
this.#maxPriorityFeePerGas = null;
|
||||
this.#maxFeePerGas = null;
|
||||
this.#data = "0x";
|
||||
this.#value = BigInt(0);
|
||||
this.#chainId = BigInt(0);
|
||||
this.#value = BN_0;
|
||||
this.#chainId = BN_0;
|
||||
this.#sig = null;
|
||||
this.#accessList = null;
|
||||
this.#maxFeePerBlobGas = null;
|
||||
this.#blobVersionedHashes = null;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -602,7 +740,6 @@ export class Transaction implements TransactionLike<string> {
|
||||
* transaction are non-null.
|
||||
*/
|
||||
isSigned(): this is (Transaction & { type: number, typeName: string, from: string, signature: Signature }) {
|
||||
//isSigned(): this is SignedTransaction {
|
||||
return this.signature != null;
|
||||
}
|
||||
|
||||
@ -622,6 +759,8 @@ export class Transaction implements TransactionLike<string> {
|
||||
return _serializeEip2930(this, this.signature);
|
||||
case 2:
|
||||
return _serializeEip1559(this, this.signature);
|
||||
case 3:
|
||||
return _serializeEip4844(this, this.signature);
|
||||
}
|
||||
|
||||
assert(false, "unsupported transaction type", "UNSUPPORTED_OPERATION", { operation: ".serialized" });
|
||||
@ -641,6 +780,8 @@ export class Transaction implements TransactionLike<string> {
|
||||
return _serializeEip2930(this);
|
||||
case 2:
|
||||
return _serializeEip1559(this);
|
||||
case 3:
|
||||
return _serializeEip4844(this);
|
||||
}
|
||||
|
||||
assert(false, "unsupported transaction type", "UNSUPPORTED_OPERATION", { operation: ".unsignedSerialized" });
|
||||
@ -651,7 +792,13 @@ export class Transaction implements TransactionLike<string> {
|
||||
* supported transaction type.
|
||||
*/
|
||||
inferType(): number {
|
||||
return <number>(this.inferTypes().pop());
|
||||
const types = this.inferTypes();
|
||||
|
||||
// Prefer London (EIP-1559) over Cancun (BLOb)
|
||||
if (types.indexOf(2) >= 0) { return 2; }
|
||||
|
||||
// Return the highest inferred type
|
||||
return <number>(types.pop());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -664,6 +811,7 @@ export class Transaction implements TransactionLike<string> {
|
||||
const hasGasPrice = this.gasPrice != null;
|
||||
const hasFee = (this.maxFeePerGas != null || this.maxPriorityFeePerGas != null);
|
||||
const hasAccessList = (this.accessList != null);
|
||||
const hasBlob = (this.#maxFeePerBlobGas != null || this.#blobVersionedHashes);
|
||||
|
||||
//if (hasGasPrice && hasFee) {
|
||||
// throw new Error("transaction cannot have gasPrice and maxFeePerGas");
|
||||
@ -695,10 +843,13 @@ export class Transaction implements TransactionLike<string> {
|
||||
} else if (hasAccessList) {
|
||||
types.push(1);
|
||||
types.push(2);
|
||||
} else if (hasBlob && this.to) {
|
||||
types.push(3);
|
||||
} else {
|
||||
types.push(0);
|
||||
types.push(1);
|
||||
types.push(2);
|
||||
types.push(3);
|
||||
}
|
||||
}
|
||||
|
||||
@ -740,6 +891,17 @@ export class Transaction implements TransactionLike<string> {
|
||||
return (this.type === 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this transaction is an [[link-eip-4844]] BLOB
|
||||
* transaction.
|
||||
*
|
||||
* This provides a Type Guard that the related properties are
|
||||
* non-null.
|
||||
*/
|
||||
isCancun(): this is (Transaction & { type: 3, to: string, accessList: AccessList, maxFeePerGas: bigint, maxPriorityFeePerGas: bigint, maxFeePerBlobGas: bigint, blobVersionedHashes: Array<string> }) {
|
||||
return (this.type === 3);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a copy of this transaciton.
|
||||
*/
|
||||
@ -790,6 +952,7 @@ export class Transaction implements TransactionLike<string> {
|
||||
switch(payload[0]) {
|
||||
case 1: return Transaction.from(_parseEip2930(payload));
|
||||
case 2: return Transaction.from(_parseEip1559(payload));
|
||||
case 3: return Transaction.from(_parseEip4844(payload));
|
||||
}
|
||||
assert(false, "unsupported transaction type", "UNSUPPORTED_OPERATION", { operation: "from" });
|
||||
}
|
||||
@ -802,11 +965,13 @@ export class Transaction implements TransactionLike<string> {
|
||||
if (tx.gasPrice != null) { result.gasPrice = tx.gasPrice; }
|
||||
if (tx.maxPriorityFeePerGas != null) { result.maxPriorityFeePerGas = tx.maxPriorityFeePerGas; }
|
||||
if (tx.maxFeePerGas != null) { result.maxFeePerGas = tx.maxFeePerGas; }
|
||||
if (tx.maxFeePerBlobGas != null) { result.maxFeePerBlobGas = tx.maxFeePerBlobGas; }
|
||||
if (tx.data != null) { result.data = tx.data; }
|
||||
if (tx.value != null) { result.value = tx.value; }
|
||||
if (tx.chainId != null) { result.chainId = tx.chainId; }
|
||||
if (tx.signature != null) { result.signature = Signature.from(tx.signature); }
|
||||
if (tx.accessList != null) { result.accessList = tx.accessList; }
|
||||
if (tx.blobVersionedHashes != null) { result.blobVersionedHashes = tx.blobVersionedHashes; }
|
||||
|
||||
if (tx.hash != null) {
|
||||
assertArgument(result.isSigned(), "unsigned transaction cannot define hash", "tx", tx);
|
||||
|
Binary file not shown.
Loading…
Reference in New Issue
Block a user