# Some Tx Refactoring Conceptual Ideas for Jochem
Author: Holger, 2024-11-07
So this is how I could imagine a tx type data class
```ts
// I would keep calling this with the "core" name, no `data` attached
// or something, since this IS the tx
class FeeMarket1559Tx implements 1559CompatibleTx, 2718CompatibleTx,... {
public readonly type: TransactionType
public readonly nonce: bigint
public readonly gasLimit: bigint
// ...
public readonly v?: bigint
public readonly r?: bigint
public readonly s?: bigint
public readonly chainId: bigint
public readonly accessList: AccessListBytes
// ...
// This would be something for (another) step 4 or so, but the more I think
// about this the more I get convinced that we just do not want Common
// attached to a tx *at all*. A tx can just exist for itself as a fee market
// tx and be perfectly happy with it (and what makes an Ethereum dev happier
// than happy txs? 😂). The Common info just always comes from the context
// e.g. a block the tx is included within. The VM which executes the tx. I
// would assume that if we remove common here, this will somewhat naturally
// solve structural problems. Beside common *also* make txs really heavy,
// this is a performance burner on its own (have to copy in client over and
// over)
// Again:
// For step 4 though. 🙂 Makes a lot of sense to not deal with this now.
public readonly common: Common
}
```
Some notes/additional thoughts:
- ~~I would first-round think this does not need additional TypeScript interfaces or anything (basically `FeeMarket1559Tx` is already somewhat as simple as a type definition)~~Â Update: this *does* need interfaces I realized further down the line to be able to tell consuming methods what a certain type of tx is composed of (added these, e.g. `1559CompatibleTx`, notes on this further down)
- I would also tend to not do anything with inheritance here and in doubt take some mild redundancy, e.g. all v, r, s containing txs just have these three properties. Tx types are just made/setup to be independent by design, and this would reflect this
Going further: the data interfaces we have now like `EIP1559CompatibleTx` can still be used (renamed to `1559CompatibleTx`) and are now a lot better compatible with `FeeMarket1559Tx`, but they should fully abandon inheritance to allow for fully modular composition (so no `extends EIP2930CompatibleTx` or the like).
And then we might have something like type-specific worker classes, one-time instantiated:
```ts
class FeeMarket1559TxWorker {
// So, the respective tx is always passed in here
sign(tx: FeeMarket1559Tx, privateKey: Uint8Array): FeeMarket1559Tx {
// Do not have the full picture here yet how to decompose the base
// functionality into capabilities, maybe non-EIP-named but still atomic
// capabilities like `ECDSASignatureCompatibleTx` (so that's the related
// data interface)
}
getUpfrontCost(tx: FeeMarket1559Tx, baseFee: bigint = BIGINT_0): bigint {
// This will call into the respective capability methods as before
return EIP1559.getUpfrontCost(tx, baseFee)
}
}
```
Then, if I got everything right, the capabilities itself could be easily typed with the data interfaces from above:
```ts
export function getEffectivePriorityFee(
// Guess this might be necessary (not here but in general), just tested
// that typing like `1559CompatibleTx & 2718CompatibleTx` seems possible,
// so a type where compatibility with 1559 *and* 2718 is needed
tx: 1559CompatibleTx,
baseFee: bigint | undefined,
): bigint
```
I think the capabilities also need interfaces for the methods supported by
each capability:
```ts
interface 1559CompatibleWorker {
getEffectivePriorityFee(tx: 1559CompatibleTx, baseFee: bigint | undefined): bigint
// ...
}
interface 2718CompatibleWorker {
getHashedMessageToSign(tx: 2718CompatibleTx): Uint8Array
// ...
}
```
And these interfaces can then be used to type the tx type specific worker from above (had not yet added this above to not overload the example):
```ts
class FeeMarket1559TxWorker implements 1559CompatibleWorker, 2718CompatibleWorker {
}
```
I would then think we then also needs a generic `TxWorker`, to make it as comfortable as we have to work with txs when the type can be diverse.
I will give it a try to sketch this out:
```ts
// Guess this will just need to implement all the worker classes
class TxWorker implements 1559CompatibleWorker, 2718CompatibleWorker,... {
// This whole class basically holds "forwards" (to capability methods) for
// all existing methods from capabilities
// The only thing different here is the typing
// We will - respectively should - see this in code upstream if we apply
// a method in a context where this could be called with a not-compatible
// tx type. This will the directly break (aka: "notify" us) on the
// TypeScript level
getUpfrontCost(tx: 1559CompatibleTx, baseFee: bigint = BIGINT_0): bigint {
// This will call into the respective capability methods as before
return EIP1559.getUpfrontCost(this, baseFee)
}
// ...
}
```
Not fully sure if this `TxWorker` setup fully "holds" (but it might).
Guess I will leave it there/here nevertheless. Enough food for thought/discussion/inspiration. 🙂