Skip to content

Commit

Permalink
Add constructor tests
Browse files Browse the repository at this point in the history
  • Loading branch information
acolytec3 committed Oct 13, 2022
1 parent cb66e3e commit c9024ad
Show file tree
Hide file tree
Showing 5 changed files with 110 additions and 19 deletions.
85 changes: 79 additions & 6 deletions packages/tx/src/eip4844Transaction.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,98 @@
import { toHexString } from '@chainsafe/ssz'
import { Address } from '@ethereumjs/util'
import { Address, MAX_INTEGER, bufferToBigInt, toBuffer } from '@ethereumjs/util'

import { FeeMarketEIP1559Transaction } from './eip1559Transaction'
import { BaseTransaction } from './baseTransaction'
import { BlobTransactionType } from './types'
import { AccessLists, checkMaxInitCodeSize } from './util'

import type {
AccessList,
AccessListBuffer,
AccessListBufferItem,
BlobEip4844TxData,
BlobEIP4844TxData,
JsonTx,
TxOptions,
TxValuesArray,
} from './types'
import type { Common } from '@ethereumjs/common'

const TRANSACTION_TYPE = 0x05
const TRANSACTION_TYPE_BUFFER = Buffer.from(TRANSACTION_TYPE.toString(16).padStart(2, '0'), 'hex')

export class BlobEIP4844Transaction extends FeeMarketEIP1559Transaction {
export class BlobEIP4844Transaction extends BaseTransaction<BlobEIP4844Transaction> {
public readonly chainId: bigint
public readonly accessList: AccessListBuffer
public readonly AccessListJSON: AccessList
public readonly maxPriorityFeePerGas: bigint
public readonly maxFeePerGas: bigint

public readonly common: Common
private versionedHashes: Buffer[]

constructor(txData: BlobEip4844TxData, opts?: TxOptions) {
constructor(txData: BlobEIP4844TxData, opts: TxOptions = {}) {
super({ ...txData, type: TRANSACTION_TYPE }, opts)
const { chainId, accessList, maxFeePerGas, maxPriorityFeePerGas } = txData

this.common = this._getCommon(opts.common, chainId)
this.chainId = this.common.chainId()

if (this.common.isActivatedEIP(1559) === false) {
throw new Error('EIP-1559 not enabled on Common')
}
this.activeCapabilities = this.activeCapabilities.concat([1559, 2718, 2930])

// Populate the access list fields
const accessListData = AccessLists.getAccessListData(accessList ?? [])
this.accessList = accessListData.accessList
this.AccessListJSON = accessListData.AccessListJSON
// Verify the access list format.
AccessLists.verifyAccessList(this.accessList)

this.maxFeePerGas = bufferToBigInt(toBuffer(maxFeePerGas === '' ? '0x' : maxFeePerGas))
this.maxPriorityFeePerGas = bufferToBigInt(
toBuffer(maxPriorityFeePerGas === '' ? '0x' : maxPriorityFeePerGas)
)

this._validateCannotExceedMaxInteger({
maxFeePerGas: this.maxFeePerGas,
maxPriorityFeePerGas: this.maxPriorityFeePerGas,
})

BaseTransaction._validateNotArray(txData)

if (this.gasLimit * this.maxFeePerGas > MAX_INTEGER) {
const msg = this._errorMsg('gasLimit * maxFeePerGas cannot exceed MAX_INTEGER (2^256-1)')
throw new Error(msg)
}

if (this.maxFeePerGas < this.maxPriorityFeePerGas) {
const msg = this._errorMsg(
'maxFeePerGas cannot be less than maxPriorityFeePerGas (The total must be the larger of the two)'
)
throw new Error(msg)
}

this._validateYParity()
this._validateHighS()

if (this.common.isActivatedEIP(3860)) {
checkMaxInitCodeSize(this.common, this.data.length)
}

this.versionedHashes = txData.versionedHashes

const freeze = opts?.freeze ?? true
if (freeze) {
Object.freeze(this)
}
}

public static fromTxData(txData: BlobEIP4844TxData, opts?: TxOptions) {
return new BlobEIP4844Transaction(txData, opts)
}

public static fromSerializedTx(serialized: Buffer, opts?: TxOptions): BlobEIP4844Transaction {
const decodedTx = BlobTransactionType.deserialize(serialized)
const decodedTx = BlobTransactionType.deserialize(serialized.slice(1))
const versionedHashes = decodedTx.blobVersionedHash.map((el) => Buffer.from(el))
const accessList: AccessListBuffer = []
for (const listItem of decodedTx.accessList) {
Expand All @@ -42,10 +110,15 @@ export class BlobEIP4844Transaction extends FeeMarketEIP1559Transaction {
}
return new BlobEIP4844Transaction(txData, opts)
}

getUpfrontCost(): bigint {
throw new Error('Method not implemented.')
}

raw(): TxValuesArray {
throw new Error('Method not implemented.')
}

serialize(): Buffer {
const to = {
selector: this.to !== undefined ? 1 : 0,
Expand Down
1 change: 1 addition & 0 deletions packages/tx/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export { FeeMarketEIP1559Transaction } from './eip1559Transaction'
export { AccessListEIP2930Transaction } from './eip2930Transaction'
export { BlobEIP4844Transaction } from './eip4844Transaction'
export { Transaction } from './legacyTransaction'
export { TransactionFactory } from './transactionFactory'
export * from './types'
21 changes: 9 additions & 12 deletions packages/tx/src/transactionFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ import { ethers } from 'ethers'

import { FeeMarketEIP1559Transaction } from './eip1559Transaction'
import { AccessListEIP2930Transaction } from './eip2930Transaction'
import { BlobEIP4844Transaction } from './eip4844Transaction'
import { normalizeTxParams } from './fromRpc'
import { Transaction } from './legacyTransaction'

import type {
AccessListEIP2930TxData,
BlobEIP4844TxData,
FeeMarketEIP1559TxData,
TxData,
TxOptions,
Expand All @@ -25,7 +27,7 @@ export class TransactionFactory {
* @param txOptions - Options to pass on to the constructor of the transaction
*/
public static fromTxData(
txData: TxData | AccessListEIP2930TxData | FeeMarketEIP1559TxData,
txData: TxData | AccessListEIP2930TxData | FeeMarketEIP1559TxData | BlobEIP4844TxData,
txOptions: TxOptions = {}
): TypedTransaction {
if (!('type' in txData) || txData.type === undefined) {
Expand All @@ -39,6 +41,8 @@ export class TransactionFactory {
return AccessListEIP2930Transaction.fromTxData(<AccessListEIP2930TxData>txData, txOptions)
} else if (txType === 2) {
return FeeMarketEIP1559Transaction.fromTxData(<FeeMarketEIP1559TxData>txData, txOptions)
} else if (txType === 5) {
return BlobEIP4844Transaction.fromTxData(<BlobEIP4844TxData>txData, txOptions)
} else {
throw new Error(`Tx instantiation with type ${txType} not supported`)
}
Expand All @@ -54,23 +58,16 @@ export class TransactionFactory {
public static fromSerializedData(data: Buffer, txOptions: TxOptions = {}): TypedTransaction {
if (data[0] <= 0x7f) {
// Determine the type.
let EIP: number
switch (data[0]) {
case 1:
EIP = 2930
break
return AccessListEIP2930Transaction.fromSerializedTx(data, txOptions)
case 2:
EIP = 1559
break
return FeeMarketEIP1559Transaction.fromSerializedTx(data, txOptions)
case 5:
return BlobEIP4844Transaction.fromSerializedTx(data, txOptions)
default:
throw new Error(`TypedTransaction with ID ${data[0]} unknown`)
}
if (EIP === 1559) {
return FeeMarketEIP1559Transaction.fromSerializedTx(data, txOptions)
} else {
// EIP === 2930
return AccessListEIP2930Transaction.fromSerializedTx(data, txOptions)
}
} else {
return Transaction.fromSerializedTx(data, txOptions)
}
Expand Down
2 changes: 1 addition & 1 deletion packages/tx/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ export interface FeeMarketEIP1559TxData extends AccessListEIP2930TxData {
/**
* {@link BlobEip4844Transaction} data.
*/
export interface BlobEip4844TxData extends FeeMarketEIP1559TxData {
export interface BlobEIP4844TxData extends FeeMarketEIP1559TxData {
/**
* The versioned hashes used to validate the blobs attached to a transaction
*/
Expand Down
20 changes: 20 additions & 0 deletions packages/tx/test/eip4844.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import * as tape from 'tape'

import { BlobEIP4844Transaction, TransactionFactory } from '../src'

tape('EIP4844 constructor tests', (t) => {
const txData = {
type: 0x05,
versionedHashes: [Buffer.from([])],
}
const tx = BlobEIP4844Transaction.fromTxData(txData)
t.equal(tx.type, 5, 'successfully instantiated a blob transaction from txData')
const factoryTx = TransactionFactory.fromTxData(txData)
t.equal(factoryTx.type, 5, 'instantiated a blob transaction from the tx factory')

const serializedTx = tx.serialize()
t.equal(serializedTx[0], 5, 'successfully serialized a blob tx')
const deserializedTx = BlobEIP4844Transaction.fromSerializedTx(serializedTx)
t.equal(deserializedTx.type, 5, 'deserialized a blob tx')
t.end()
})

0 comments on commit c9024ad

Please sign in to comment.