internal/ethapi: reject gasPrice together with authorizationList (#35320)

Closes #35314.

### Problem

`TransactionArgs.ToTransaction` selects `SetCodeTxType` when an
`authorizationList` is present, but then unconditionally downgrades to
`LegacyTxType` when `gasPrice` is set:

```go
case args.AuthorizationList != nil || defaultType == types.SetCodeTxType:
    usedType = types.SetCodeTxType
...
if args.GasPrice != nil {
    usedType = types.LegacyTxType
}
```

A legacy transaction cannot carry an EIP-7702 authorization list, so the
list is silently dropped. `eth_sendTransaction`, `eth_signTransaction`
and `eth_fillTransaction` can then return a plain legacy
transaction/hash even though the requested delegation update or
revocation was never included.

The downgrade also masks a latent issue: `setFeeDefaults` returns early
once `gasPrice` is set (without `authorizationList`) and never fills
`MaxFeePerGas`/`MaxPriorityFeePerGas`. Building the `SetCodeTx` without
the downgrade would hit
`uint256.MustFromBig((*big.Int)(args.MaxFeePerGas))` with a nil fee cap
and panic. Erroring early is therefore the correct behavior.

### Fix

Reject the `gasPrice` + `authorizationList` combination in
`setFeeDefaults`, mirroring the existing `gasPrice` / EIP-1559 guard, so
the request fails explicitly instead of producing a transaction that
omits the authorization list.

### Test

Added a `setFeeDefaults` case asserting the new error. Verified
fail-on-main: with the guard reverted the test fails (`expected error:
both gasPrice and authorizationList specified`); with the guard it
passes. Full `internal/ethapi` package, `go vet` and `gofmt` are clean.

### Note

The same silent-drop applies to `gasPrice` + `blobVersionedHashes` (blob
transactions also cannot be legacy). I scoped this PR to the reported
`authorizationList` case; happy to extend the guard to blob hashes in
the same spot if preferred.
This commit is contained in:
ozpool 2026-07-08 23:27:31 +05:30 committed by GitHub
parent 0c88fee429
commit 2133e014ae
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 14 additions and 0 deletions

View file

@ -195,6 +195,12 @@ func (args *TransactionArgs) setFeeDefaults(ctx context.Context, b Backend, head
if args.GasPrice != nil && (args.MaxFeePerGas != nil || args.MaxPriorityFeePerGas != nil) {
return errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified")
}
// An EIP-7702 set-code transaction cannot be a legacy transaction, so gasPrice
// is incompatible with an authorization list. Reject the combination instead of
// silently dropping the authorization list in ToTransaction.
if args.GasPrice != nil && args.AuthorizationList != nil {
return errors.New("both gasPrice and authorizationList specified")
}
// If the tx has completely specified a fee mechanism, no default is needed.
// This allows users who are not yet synced past London to get defaults for
// other tx values. See https://github.com/ethereum/go-ethereum/pull/23274

View file

@ -58,6 +58,7 @@ func TestSetFeeDefaults(t *testing.T) {
fortytwo = (*hexutil.Big)(big.NewInt(42))
maxFee = (*hexutil.Big)(new(big.Int).Add(new(big.Int).Mul(b.current.BaseFee, big.NewInt(2)), fortytwo.ToInt()))
al = &types.AccessList{types.AccessTuple{Address: common.Address{0xaa}, StorageKeys: []common.Hash{{0x01}}}}
authList = []types.SetCodeAuthorization{{Address: common.Address{0xbb}}}
)
tests := []test{
@ -208,6 +209,13 @@ func TestSetFeeDefaults(t *testing.T) {
nil,
errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified"),
},
{
"set gas price and authorization list",
"london",
&TransactionArgs{GasPrice: fortytwo, AuthorizationList: authList},
nil,
errors.New("both gasPrice and authorizationList specified"),
},
// EIP-4844
{
"set gas price and maxFee for blob transaction",