@@ -59,6 +59,9 @@ func NewTransaction(ctx context.Context, provider *ethrpc.Provider, txnRequest *
5959 return nil , fmt .Errorf ("ethtxn: provider is not set" )
6060 }
6161
62+ if txnRequest .Nonce == nil && txnRequest .From == (common.Address {}) {
63+ return nil , fmt .Errorf ("ethtxn: from address is required when nonce is not set on txnRequest" )
64+ }
6265 if txnRequest .Nonce == nil {
6366 nonce , err := provider .PendingNonceAt (ctx , txnRequest .From )
6467 if err != nil {
@@ -76,6 +79,40 @@ func NewTransaction(ctx context.Context, provider *ethrpc.Provider, txnRequest *
7679 txnRequest .GasPrice = gasPrice
7780 }
7881
82+ // If we're constructing a legacy (or access-list) transaction (ie. GasTip is nil) on a
83+ // post-London network, gasPrice MUST be >= baseFee. The node error you saw:
84+ // "max fee per gas less than block base fee" also applies to legacy txs internally
85+ // because the validation unifies the code paths. eth_gasPrice can occasionally return
86+ // a value slightly below the current baseFee if the baseFee just jumped. We defensively
87+ // bump it here. We also (optionally) add a small priority tip using SuggestGasTipCap so
88+ // the tx isn't sent with zero miner incentive (gasPrice == baseFee).
89+ if txnRequest .GasTip == nil { // staying in legacy/access-list mode
90+ if head , err := provider .HeaderByNumber (ctx , nil ); err == nil && head != nil && head .BaseFee != nil {
91+ baseFee := head .BaseFee
92+
93+ // Base fee can increase by up to 12.5% per block (EIP-1559). We add headroom so a
94+ // transaction constructed right before a block is sealed doesn't become invalid if
95+ // the next block's baseFee rises. headroomBaseFee = baseFee * 1050/1000 (~+5.0%).
96+ // headroomBaseFee := new(big.Int).Mul(baseFee, big.NewInt(1125)) // 12.5%
97+ headroomBaseFee := new (big.Int ).Mul (baseFee , big .NewInt (1050 )) // 5%
98+ headroomBaseFee .Div (headroomBaseFee , big .NewInt (1000 ))
99+
100+ // We treat legacy gasPrice as (baseFee + implicit priority). Ensure gasPrice >= headroomBaseFee.
101+ if txnRequest .GasPrice .Cmp (headroomBaseFee ) < 0 {
102+ // Optionally attempt to get a tip suggestion and add it on top of headroom base fee.
103+ if tip , err2 := provider .SuggestGasTipCap (ctx ); err2 == nil && tip != nil {
104+ candidate := new (big.Int ).Add (headroomBaseFee , tip )
105+ if txnRequest .GasPrice .Cmp (candidate ) < 0 {
106+ txnRequest .GasPrice = candidate
107+ }
108+ } else {
109+ // Fallback just headroom base fee
110+ txnRequest .GasPrice = headroomBaseFee
111+ }
112+ }
113+ }
114+ }
115+
79116 if txnRequest .GasLimit == 0 {
80117 callMsg := ethereum.CallMsg {
81118 From : txnRequest .From ,
@@ -103,7 +140,6 @@ func NewTransaction(ctx context.Context, provider *ethrpc.Provider, txnRequest *
103140 if err != nil {
104141 return nil , err
105142 }
106-
107143 rawTx = types .NewTx (& types.DynamicFeeTx {
108144 ChainID : chainId ,
109145 To : txnRequest .To ,
@@ -120,7 +156,6 @@ func NewTransaction(ctx context.Context, provider *ethrpc.Provider, txnRequest *
120156 if err != nil {
121157 return nil , err
122158 }
123-
124159 rawTx = types .NewTx (& types.AccessListTx {
125160 ChainID : chainId ,
126161 To : txnRequest .To ,
@@ -145,16 +180,16 @@ func NewTransaction(ctx context.Context, provider *ethrpc.Provider, txnRequest *
145180 return rawTx , nil
146181}
147182
148- func SendTransaction (ctx context.Context , provider * ethrpc.Provider , signedTx * types.Transaction ) (* types.Transaction , WaitReceipt , error ) {
183+ func SendTransaction (ctx context.Context , provider * ethrpc.Provider , signedTxn * types.Transaction ) (* types.Transaction , WaitReceipt , error ) {
149184 if provider == nil {
150185 return nil , nil , fmt .Errorf ("ethtxn (SendTransaction): provider is not set" )
151186 }
152187
153188 waitFn := func (ctx context.Context ) (* types.Receipt , error ) {
154- return ethrpc .WaitForTxnReceipt (ctx , provider , signedTx .Hash ())
189+ return ethrpc .WaitForTxnReceipt (ctx , provider , signedTxn .Hash ())
155190 }
156191
157- return signedTx , waitFn , provider .SendTransaction (ctx , signedTx )
192+ return signedTxn , waitFn , provider .SendTransaction (ctx , signedTxn )
158193}
159194
160195var zeroBigInt = big .NewInt (0 )
0 commit comments