Skip to content

feat: Support eth_fillTransaction#12204

Open
hudem1 wants to merge 7 commits into
masterfrom
hudem1/feat/eth-fill-tx
Open

feat: Support eth_fillTransaction#12204
hudem1 wants to merge 7 commits into
masterfrom
hudem1/feat/eth-fill-tx

Conversation

@hudem1

@hudem1 hudem1 commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Fixes Closes Resolves #11374

Summary

Implements the eth_fillTransaction JSON-RPC method (#11374) following ethereum/execution-apis#803.

Given a partial transaction, the node fills in the missing fields (nonce, gas, fees, chain id, and the blob sidecar) and returns the unsigned transaction, ready to be signed and submitted. Unlike eth_sendTransaction, it neither signs nor broadcasts — it is the "fill" half of eth_sendTransaction (fill + sign + submit). Per #803 the result is { tx } only; that PR removed the raw field from fill/sign results.

Transaction types that can be filled

All five current transaction types are supported. Each maps to its FilledTransaction output schema:

Type ID Output schema
Legacy 0x0 TransactionLegacyUnsigned
Access List (EIP-2930) 0x1 Transaction2930Unsigned
Dynamic Fee (EIP-1559) 0x2 Transaction1559Unsigned
Blob (EIP-4844) 0x3 Transaction4844Unsigned / Transaction4844UnsignedWithSidecar
Set Code (EIP-7702) 0x4 Transaction7702Unsigned

Which fields get filled

Fields fall into three groups: the node defaults/computes what it authoritatively knows, derives the blob sidecar from the caller's blobs, and treats a few fields as caller-mandatory — erroring rather than inventing them.

Field Source Applies to
type node — from discriminator, else defaults to 1559 all
nonce node — pending-pool nonce of from all
gas node — eth_estimateGas all
value node — default 0 all
input caller — default empty 0x all
gasPrice node — gas-price oracle (0x0/0x1); effective price (0x2) 0x0, 0x1, 0x2
maxFeePerGas / maxPriorityFeePerGas node — oracle tip, maxFee = 2·baseFee + tip 0x2, 0x3, 0x4
maxFeePerBlobGas node — current blob base fee 0x3
accessList node — default [] 0x1, 0x2, 0x3, 0x4
chainId node — chain id all typed
to caller-mandatory — blob/setcode can't be contract creation 0x3, 0x4
blobVersionedHashes derived from the caller's blobs 0x3
blobs / commitments / proofs caller supplies blobs; node derives commitments/proofs (proof shape per fork: 1 per blob pre-PeerDAS, 128 cell proofs per blob post-PeerDAS) 0x3 (with sidecar)
authorizationList caller-only — signed authorizations, cannot be synthesized 0x4

Error behavior

eth_fillTransaction fails hard (-32000 InvalidInput) instead of returning a partially-filled transaction when a required field cannot be filled or derived — e.g. contract creation without data, a blob/setcode tx missing to, or a blob tx with no blobs. This mirrors geth's setDefaults and is required to keep the result conformant with the FilledTransaction schema.

Types of changes

What types of changes does your code introduce?

  • Bugfix (a non-breaking change that fixes an issue)
  • New feature (a non-breaking change that adds functionality)
  • Breaking change (a change that causes existing functionality not to work as expected)
  • Optimization
  • Refactoring
  • Documentation update
  • Build-related changes
  • Other: Description

Testing

Requires testing

  • Yes
  • No

If yes, did you write tests?

  • Yes
  • No

Notes on testing

Added EthRpcModuleTests.FillTransaction.cs covering: EIP-1559 fill (nonce/gas/fees/chainId), legacy gasPrice fill, preservation of caller-supplied fields, contract-creation-without-data rejection, and blob sidecar derivation (commitments/proofs/versioned hashes) from caller-supplied blobs.

Documentation

Requires documentation update

  • Yes
  • No

If yes, link the PR to the docs update or the issue with the details labeled docs. Remove if not applicable.

Requires explanation in Release Notes

  • Yes
  • No

If yes, fill in the details here. Remove if not applicable.

Remarks

Optional. Remove if not applicable.

@claude

claude Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Claude finished @hudem1's task in 4m 38s —— View job


Code Review: eth_fillTransaction

  • Read changed files and related transaction-fill infrastructure
  • Analyze correctness, edge cases, security, performance
  • Post inline findings and summary

I reviewed the 4 changed files against the geth/reth eth_fillTransaction contract referenced in #11374 and the sibling eth_signTransaction/eth_sendTransaction paths. The implementation is clean, well-commented, and the ordering choices (nonce → gas estimate before fees → fee defaults → blob fields) are sound. A few points below.

Findings

Medium

  • Missing raw field in the result. FillTransactionResult returns only tx, but geth's SignTransactionResult and reth's FillTransaction (the API eth_fillTransaction #11374 points at) return { raw, tx }, where raw is the RLP of the unsigned transaction. Tooling expecting the standard shape won't find raw. Nethermind already models this exact shape in SignTransactionResult — see the inline comment for a suggested fix, or confirm the omission is intentional. (inline)

Low

  • from omitted → silently defaults to Address.Zero (EthRpcModule.cs:465). The nonce is then filled from the zero address's pending view and gas estimated from it, yielding a "successful" but meaningless fill. geth/reth require from. Consider erroring on a missing from. (inline)
  • RpcTxFeeCap not enforced. eth_signTransaction runs CheckTxFeeCap; eth_fillTransaction does not. This is actually consistent with geth (SetDefaults doesn't cap), so it's fine — noting it only for awareness, since a filled tx can carry fees the node would refuse at submission.
  • Legacy gasPrice requests aren't promoted to EIP-1559. eth_sendTransaction/eth_signTransaction call PromoteToEip1559IfTypeDefaulted(); the fill path keeps an explicit-gasPrice request as legacy. This is arguably more correct for a "fill" (respects the caller's chosen fee model), so I flag it only as a consistency note.

Things I verified as correct (not issues)

  • Untyped {from,to,value} produces an EIP-1559 result. I initially suspected a type-defaulting bug, but the deserializer defaults untyped requests to EIP1559TransactionForRpc (whose Type => EIP1559), so the switch fills 1559 fees and ToTransaction emits type 0x2 — matching the documented ExampleResponse.
  • Gas estimated before fees are defaulted so ShouldSetBaseFee() is false, base fee is zeroed during estimation, and an unfunded/low-balance sender still resolves (matching eth_estimateGas behavior).
  • EIP-1559 maxFeePerGas = 2·baseFee + tip matches geth's default.
  • Blob path: FillBlobFields derives commitments/proofs/hashes from caller blobs using the head-fork proof version, and TryAttachSidecar re-validates against the same version — consistent, and correctly guards incomplete sidecars.
  • Test coverage is good (missing-fields, legacy, caller-supplied-preserved, contract-creation-without-data error, blob derivation) and parameter-free duplication is avoided via the FillTransactionForResult helper.

Verdict: No Critical/High issues. The Medium raw-field gap should be resolved or explicitly acknowledged before merge, since #11374 references an API that includes it.
· branch hudem1/feat/eth-fill-tx

@LukaszRozmej LukaszRozmej requested a review from manusw7 July 2, 2026 11:43
@LukaszRozmej

Copy link
Copy Markdown
Member

duplicate of #11552 ?

@svlachakis svlachakis mentioned this pull request Jul 2, 2026
16 tasks
Comment thread src/Nethermind/Nethermind.JsonRpc/Data/FillTransactionResult.cs
Comment thread src/Nethermind/Nethermind.JsonRpc/Modules/Eth/EthRpcModule.cs Outdated
@svlachakis

Copy link
Copy Markdown
Contributor

@claude please check the following review

Findings

M1 — Expensive KZG proof computation runs on unbounded caller input before validation (DoS vector). FillBlobFields (EthRpcModule.cs:496 → 1298-1303) calls ComputeProofsAndCommitments over all caller-supplied blobTx.Blobs before ToTransaction(validateUserInput: true, …) at line 501 validates the blob count against the per-tx consensus limit. Post-PeerDAS that's 128 cell-proofs per blob; a caller can pack the request up to the JSON-RPC max body size (~hundreds of 128 KiB blobs) and force seconds of CPU per unauthenticated call before the tx is rejected as over-limit. eth_fillTransaction has no wallet/config gate (unlike eth_signTransaction's EnableEthSignTransaction), so it's reachable by any RPC client. Fix: validate the blob count (and cheap structural fields) before computing proofs — reject an over-limit blob set up front.

M2 — Missing from silently fills a meaningless nonce instead of erroring. Address from = (rpcTx as LegacyTransactionForRpc)?.From ?? Address.Zero; (EthRpcModule.cs:465), then nonceless.Nonce = _txPool.GetLatestPendingNonce(from) (:469). If the caller omits from, the nonce is filled from the zero address's pending nonce — a wrong value returned as if authoritative. geth's setDefaults requires from to fill the nonce. Since the whole point of fill is "node fills what it authoritatively knows," a nonce derived from 0x000…0 is not that. Fix: if from is null and nonce is missing, fail with InvalidInput rather than defaulting the sender.

L1 — Spec conformance: gasPrice not populated for typed (0x2/0x3/0x4) outputs. transaction.yaml lists gasPrice as required (DEPRECATED) in Transaction1559Unsigned (and Transaction2930Unsigned). The fill switch sets gasPrice only in the LegacyTransactionForRpc arm (0x0/0x1); for 1559+ it's never set. The output is produced by TransactionForRpc.FromTransaction(tx) without extraData (:516), and EIP1559TransactionForRpc.FromTransaction only sets GasPrice when extraData.BaseFee is not null (EIP1559TransactionForRpc.cs:35-36) — so the 1559 output will omit gasPrice. Also note the PR description claims "gasPrice — node — effective price (0x2)", which the implementation does not do. Verify against the actual FilledTransaction/fill.yaml schema in #803 (it may differ from the signed transaction.yaml); if that schema also marks gasPrice required for 1559, the result is non-conformant. Either way, reconcile the PR description with the code.

L2 — sidecarComplete path trusts caller-supplied commitments/proofs without recomputation. When the caller supplies Commitments+Proofs+BlobVersionedHashes, FillBlobFields returns early (:1294-1296) and TryAttachSidecar only checks lengths + (optionally) hash match. Caller-supplied-but-inconsistent proofs pass through into the returned tx. Low severity (unsigned, not broadcast → caller only hurts itself), but worth a note since the method's contract is "node derives the sidecar."

L3 — Result shape { tx } is correct for #803, which removed raw from fill results — matches FillTransactionResult. Note (out of scope): SignTransactionResult still carries Raw (:424); if #803 also drops raw from eth_signTransaction, that's a separate follow-up.

Tests

Good core coverage (1559 fill, legacy gasPrice, field preservation, contract-creation-without-data rejection, blob sidecar derivation). Gaps worth adding: 2930 and 7702 fills, blob tx missing to rejection, blob tx with no blobs rejection, and missing from (M2). The blob test manually calls KzgPolynomialCommitments.InitializeAsync() — confirm the production path guarantees KZG is initialized when eth_fillTransaction receives blobs on a blob-enabled chain, otherwise ComputeProofsAndCommitments can throw.

@claude

claude Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Claude finished @svlachakis's task in 2m 4s —— View job


Review check: eth_fillTransaction

I verified each finding against the code on hudem1/feat/eth-fill-tx. Summary of my assessment below.

ID Verdict Notes
M1 Confirmed Real ordering issue / DoS vector
M2 Confirmed from silently defaults to zero
L1 ⚠️ Mostly incorrect gasPrice is populated; only the PR description wording is off
L2 ✅ Valid (low) caller sidecar not recomputed
L3 ✅ Correct { tx } is right per #803
Tests ✅ Valid gaps KZG-init concern is addressed in prod

M1 — KZG computation before blob-count validation — Confirmed

The ordering is exactly as described. FillBlobFields (EthRpcModule.cs:496) runs ComputeProofsAndCommitments over all caller-supplied blobTx.Blobs (:1298-1303), and the per-tx blob-count limit is only enforced later inside ToTransaction at :501 (→ BlobTransactionForRpc.cs:72-77, BlobFieldsTxValidator.ValidateBlobGasLimits against spec.MaxBlobsPerTx). Nothing bounds the blob count before the proof math. eth_fillTransaction has no wallet/config gate (eth_signTransaction requires EnableEthSignTransaction), so it's reachable by any RPC client — a valid unauthenticated-CPU concern, more acute post-PeerDAS (128 cell proofs/blob). Fix as suggested: validate Blobs.Length (and cheap structural fields) up front before computing proofs.

M2 — Missing from fills a meaningless nonce — Confirmed

Address from = (rpcTx as LegacyTransactionForRpc)?.From ?? Address.Zero; (:465) then nonceless.Nonce = _txPool.GetLatestPendingNonce(from) (:469). With from omitted, the returned nonce is Address.Zero's pending nonce, presented as authoritative. Since fill's contract is "node fills what it authoritatively knows," rejecting a missing from (with InvalidInput) when the nonce is absent is the right behavior and matches geth's setDefaults.

L1 — gasPrice on typed outputs — Mostly incorrect

The claim that the 1559 output omits gasPrice doesn't hold. EIP1559TransactionForRpc's constructor (EIP1559TransactionForRpc.cs:35-37) is a ternary with an else branch:

GasPrice = extraData.BaseFee is not null
    ? transaction.CalculateEffectiveGasPrice(eip1559Enabled: true, extraData.BaseFee.Value)
    : transaction.MaxFeePerGas;   // <-- fallback still sets GasPrice

eth_fillTransaction calls TransactionForRpc.FromTransaction(tx) with no extraData, so BaseFee is null and GasPrice is set to MaxFeePerGas. So the typed output does carry gasPrice — it is not omitted, and the result is not non-conformant on that ground.

The residual valid point is only a doc/wording mismatch: the PR description says gasPrice — node — effective price (0x2), but the emitted value is maxFeePerGas, not the base-fee-adjusted effective price (no base fee is threaded into the output builder). Worth reconciling the PR text with the code, but no code change needed for conformance.

L2 — caller-supplied sidecar trusted — Valid (low)

Confirmed: when Commitments + Proofs + BlobVersionedHashes are all present, FillBlobFields returns early (:1294-1296) and TryAttachSidecar (BlobTransactionForRpc.cs:101-121) only checks lengths and (if hashes present) hash↔commitment consistency — proofs themselves aren't re-verified. Low severity as stated (unsigned, not broadcast), worth a comment given the "node derives the sidecar" contract.

L3 — { tx } shape — Correct

Agreed, and this supersedes the earlier review round's "Medium: missing raw" note: execution-apis#803 removed raw from fill/sign results, so returning only tx is correct and the prior Medium can be dismissed. SignTransactionResult.Raw remaining is a separate, out-of-scope follow-up.

Tests

Suggested gaps are reasonable (2930/7702 fills, blob-missing-to, blob-with-no-blobs, missing-from for M2).

On the KZG-init concern: the production path is safe on blob-enabled chains. InitializePrecompiles (Nethermind.Init/Steps/InitializePrecompiles.cs:21-31) calls KzgPolynomialCommitments.InitializeAsync at startup whenever GetFinalSpec().IsEip4844Enabled. The test's manual InitializeAsync() is only compensating for the lighter test harness; ComputeProofsAndCommitments won't throw for uninitialized KZG in a real blob-enabled node.


Bottom line

  • M1 and M2 are real and should be addressed (or explicitly acknowledged) before merge.
  • L1 is largely a false positive on the code (gasPrice is populated); only the PR description needs a wording fix.
  • L2, L3, and the test gaps are accurate. L3 usefully retires the earlier raw-field Medium.

Note: @LukaszRozmej flagged this may duplicate #11552 — worth resolving that before investing further in review fixes.
· branch hudem1/feat/eth-fill-tx

Comment thread src/Nethermind/Nethermind.JsonRpc/Modules/Eth/EthRpcModule.cs
Comment thread src/Nethermind/Nethermind.JsonRpc/Modules/Eth/EthRpcModule.cs
Comment thread src/Nethermind/Nethermind.JsonRpc/Modules/Eth/EthRpcModule.cs Outdated
@manusw7

manusw7 commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

@claude check the following review

1 — EthRpcModule.cs:501:

SetCode (0x4): to == null enforcement is inconsistent — it depends on whether gas was supplied. EIP-7702 forbids contract creation (our own SetCodeTxValidation.ValidateNoContractCreation, enforced in both TxValidator and TransactionProcessor.ValidateStatic), and #803's Transaction7702Unsigned requires to. When gas is omitted, estimation reaches ValidateStatic and the fill correctly errors; when the caller supplies gas, estimation is skipped and ToTransaction has no check, so we return a tx that can never be valid — despite the PR description listing to as caller-mandatory for 0x4. Suggest an explicit To is null check (mirroring MissingToInBlobTx) so behavior doesn't flip on an unrelated field, plus a test with gas supplied.

2 — EthRpcModule.cs:1294:

Partial sidecar silently overwrites caller-supplied fields, breaking the fill invariant. FillTransaction_WhenCallerSuppliesFields_LeavesThemUntouched establishes that fill only touches omitted fields, but when the sidecar is incomplete (e.g. commitments without proofs, or missing hashes) this recomputes and replaces the caller's commitments and even blobVersionedHashes — part of the payload that gets signed. Combined with the complete-sidecar path trusting values unverified (see L2), two adjacent paths have opposite trust models. Suggest either erroring on partial sidecars (geth: blob commitments provided while proofs were not) or deriving only the missing pieces and erroring when supplied values mismatch the derived ones.

3 — EthRpcModule.cs:1291 (suggestion):

Suggestion: consider 2 × blob base fee headroom. No spec mandates it and reth also uses 1 ×, so this is fine as-is — but blob base fee can rise ~8-12%/block under full blobs, making the filled tx immediately invalid if it rises before inclusion, and the cap costs the sender nothing (effective blob fee is unaffected). It would also be symmetric with the 2·baseFee + tip headroom this PR applies on the execution fee.

@claude

claude Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Claude finished @manusw7's task in 1m 23s —— View job


Review check: eth_fillTransaction (SetCode to, partial sidecar, blob fee headroom)

I verified each finding against the code on hudem1/feat/eth-fill-tx.

ID Verdict
1 — SetCode to == null enforcement depends on gas Confirmed
2 — Partial sidecar overwrites caller-supplied fields Confirmed
3 — blob base fee headroom 💡 Valid optional suggestion

1 — SetCode (0x4) to enforcement is inconsistent — Confirmed

The fill path has no explicit to != null check for SetCode, so validity flips on the unrelated gas field:

  • SetCodeTransactionForRpc.ToTransaction (SetCodeTransactionForRpc.cs:25-38) only adds the authorization list; it delegates to EIP1559TransactionForRpc.ToTransaction (EIP1559TransactionForRpc.cs:40-64), which validates only fee fields. Neither checks To.
  • The only To-null guard in the chain is LegacyTransactionForRpc.cs:99To is null && Input is null or empty → ContractCreationWithoutData. A 7702 request with to omitted but input present slips past it.
  • When gas is omitted, eth_estimateGas (EthRpcModule.cs:473-482) exercises TransactionProcessor.ValidateStatic → SetCodeTxValidation.ValidateNoContractCreation (TransactionProcessor.cs:830), so the fill correctly errors.
  • When the caller supplies gas, estimation is skipped and nothing rejects to == null — the method returns a Transaction7702Unsigned that can never validate (EIP-7702 forbids contract creation; Transaction7702Unsigned in Remove context 801 #803 requires to), contradicting the PR description's "caller-mandatory to for 0x4".

Fix as suggested: an explicit To is null check for SetCode mirroring BlobTransactionForRpc.cs:66-67's MissingToInBlobTx, so behavior is independent of gas. Add a regression test with gas supplied.

2 — Partial sidecar silently overwrites caller fields — Confirmed

FillBlobFields (EthRpcModule.cs:1294-1303):

bool sidecarComplete = blobTx.Commitments is not null && blobTx.Proofs is not null && blobTx.BlobVersionedHashes is { Length: > 0 };
if (blobTx.Blobs is not { Length: > 0 } || sidecarComplete)
    return;
// ...recomputes and REPLACES Commitments, Proofs, BlobVersionedHashes

sidecarComplete is all-or-nothing. If the caller supplies e.g. commitments but not proofs (or omits the hashes), the branch recomputes and overwrites the caller's commitments and blobVersionedHashes — fields that are part of the signed payload. This breaks the invariant that FillTransaction_WhenCallerSuppliesFields_LeavesThemUntouched asserts, and it sits directly next to the complete-sidecar path (:1294-1296 early-return + TryAttachSidecar) which trusts caller values unverified (the earlier L2 note) — two adjacent paths with opposite trust models.

Fix as suggested: either error on a partial sidecar (geth: blob commitments provided while proofs were not), or derive only the missing pieces and error when a supplied value mismatches the derived one. A partial-sidecar test would lock in whichever behavior is chosen.

3 — blob base fee headroom — Valid optional suggestion

FillBlobFields sets MaxFeePerBlobGas = feePerBlobGas (1×, EthRpcModule.cs:1291), while the execution fee uses 2·baseFee + tip (:488). The claim checks out: the cap is a ceiling, not the amount paid (effective blob fee = blob base fee, burned), so a higher cap costs the sender nothing but avoids the filled tx going invalid if the blob base fee rises before inclusion. No spec mandates it and reth uses 1×, so it's a reasonable-but-optional consistency improvement, not a correctness bug. If applied, keep the PR description in sync.


Bottom line

  • 1 and 2 are real and should be addressed (or explicitly acknowledged) before merge.
  • 3 is a valid optional suggestion; fine as-is.
  • Still open from prior rounds: M1 (KZG compute before blob-count validation), M2 (missing from fills a meaningless nonce), plus @wurdum's estimate-gas-before-FillBlobFields for blob txs and the chainId/maxFeePerBlobGas-min-1 comments. And @LukaszRozmej's #11552 duplicate question is still worth resolving.

I only checked the review — no code changes were made (not requested).
· branch hudem1/feat/eth-fill-tx

@manusw7 manusw7 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! agree with the reviews above. As a follow-up, it would be helpful to add eth_fillTransaction fixtures to rpc-tests

- Require an explicit `from`; reject the silent zero-address default.
- Reject a `chainId` that doesn't match the node (geth parity).
- Derive blob fields before gas estimation so blob txs estimate correctly.
- Default maxFeePerBlobGas to 2x the blob base fee and fail when it can't be
  computed, instead of emitting "0x0" (below the EIP-4844 minimum of 1).
- Add regression tests for the from and chainId guards.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@hudem1

hudem1 commented Jul 3, 2026

Copy link
Copy Markdown
Contributor Author

@manusw7 thanks! Agreed — eth_fillTransaction fixtures for rpc-tests will come as a separate follow-up PR.

Pushed 0b47753 addressing the review feedback:

  • reject missing from (was silently defaulting to the zero address)
  • reject a chainId that does not match the node (geth parity)
  • derive blob fields before gas estimation so blob txs estimate correctly
  • default maxFeePerBlobGas to 2× the blob base fee and fail when it cannot be computed, instead of emitting 0x0 (below the EIP-4844 minimum)

@LukaszRozmej LukaszRozmej left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Polimorphism > cascade of if checks

Comment thread src/Nethermind/Nethermind.JsonRpc/Data/FillTransactionResult.cs Outdated
Comment thread src/Nethermind/Nethermind.JsonRpc/Modules/Eth/EthRpcModule.cs Outdated
Comment thread src/Nethermind/Nethermind.JsonRpc/Modules/Eth/EthRpcModule.cs Outdated
Comment thread src/Nethermind/Nethermind.JsonRpc/Modules/Eth/EthRpcModule.cs Outdated
hudem1 and others added 3 commits July 6, 2026 12:17
Replace the per-type if/switch cascade in eth_fillTransaction with virtual
FillFeeDefaults / PrepareForGasEstimation methods on the TransactionForRpc
hierarchy, driven by a small TxFillContext of node-computed defaults. Each tx
type now populates the fee model it uses without the RPC layer branching on the
concrete type.

Also harden the blob path (moved into BlobTransactionForRpc): bound the blob
count before the KZG work and validate caller-supplied blobVersionedHashes
against the derived commitments instead of silently overwriting them.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The camelCase naming policy already serializes Tx as "tx".

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ation

Fold the success and error cases into TestCaseSource-driven tests, and add
coverage for the blob-count bound and the blob-hash mismatch rejection.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Comment on lines +466 to +470
// Every concrete tx type derives from LegacyTransactionForRpc, which carries the common
// from/nonce/chainId fields. geth/reth require an explicit sender: nonce and gas are
// meaningless without it, and defaulting to the zero address would fill the wrong account.
if (rpcTx is not LegacyTransactionForRpc baseTx || baseTx.From is null)
return ResultWrapper<FillTransactionResult>.Fail("from address not specified", ErrorCodes.InvalidInput);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe the parameter to this eth_fillTransaction should be of type LegacyTransactionForRpc, so we would eliminate other types just based on type system and eliminate this check? (Maybe worth applying to other methods with input too)

@hudem1 hudem1 Jul 7, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I looked into it, but it breaks polymorphic deserialization: TransactionJsonConverter is bound to the abstract TransactionForRpc base and intentionally does not apply when the declared target type is a derived class — that is exactly what lets the converter internally call JsonSerializer.Deserialize(ref reader, concreteTxType, ...) without recursing. If the parameter were declared as LegacyTransactionForRpc, the converter would be bypassed and a blob/1559 request would deserialize as plain legacy, silently dropping its fields. It also would not hold for plugin tx types like Optimism's DepositTransactionForRpc, which derives from TransactionForRpc directly — same constraint applies to the other TransactionForRpc-taking methods. (What I did in 6bfd1ac: split the guard into distinct errors (transaction type X is not supported for filling vs from address not specified) so the failure modes are explicit.)

Comment thread src/Nethermind/Nethermind.Facade/Eth/RpcTransaction/TransactionForRpc.cs Outdated
@LukaszRozmej

Copy link
Copy Markdown
Member

Skip on some comments

…ased method

Merge PrepareForGasEstimation and FillFeeDefaults into a single
FillDefaults(in TxFillContext) returning Result, called before gas estimation
to mirror geth's setDefaults order; estimation now runs with the effective
fees, failing like geth when the sender cannot cover them. Split the
unsupported-type and missing-from guards into distinct errors, and trim
comments to the non-obvious invariants.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

eth_fillTransaction

7 participants