core: arena allocator improvements and GC safety fixes

Multi-slab BumpAllocator (8 MiB per slab, 1 GiB cap) with per-block
usage logging (used, peak, slab count, total capacity).

Fix GC corruption: only flat types (uint256.Int) are safe in the
[]byte arena slab. Pointer-containing types (Contract, Memory,
stateTransition, ExecutionResult) use standard heap allocation.

Convert Message fields from *big.Int to uint256.Int value types,
allowing Message and Stack to remain arena-allocated without GC
issues. The buyGas() function still uses big.Int internally for
balance overflow detection.

Arena hardening: clear() for optimized zeroing, Stack pre-alloc
1024 capacity, prefetcher isolation (Allocator=nil).
This commit is contained in:
tellabg 2026-02-19 00:33:02 +01:00 committed by Guillaume Ballet
parent 3f5b5dc3a3
commit 0eb659b003
No known key found for this signature in database
11 changed files with 127 additions and 89 deletions

View file

@ -101,7 +101,7 @@ func NewEVMTxContext(msg *Message) vm.TxContext {
func NewEVMTxContextWithAlloc(msg *Message, alloc arena.Allocator) vm.TxContext {
ctx := vm.TxContext{
Origin: msg.From,
GasPrice: uint256.MustFromBig(msg.GasPrice),
GasPrice: new(uint256.Int).Set(&msg.GasPrice),
BlobHashes: msg.BlobHashes,
}
return ctx

View file

@ -32,6 +32,7 @@ import (
"github.com/ethereum/go-ethereum/internal/telemetry"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params"
"github.com/holiman/uint256"
)
// StateProcessor is a basic Processor, which takes care of transitioning
@ -256,9 +257,9 @@ func ProcessBeaconBlockRoot(beaconRoot common.Hash, evm *vm.EVM) {
msg := &Message{
From: params.SystemAddress,
GasLimit: 30_000_000,
GasPrice: common.Big0,
GasFeeCap: common.Big0,
GasTipCap: common.Big0,
GasPrice: uint256.Int{},
GasFeeCap: uint256.Int{},
GasTipCap: uint256.Int{},
To: &params.BeaconRootsAddress,
Data: beaconRoot[:],
}
@ -280,9 +281,9 @@ func ProcessParentBlockHash(prevHash common.Hash, evm *vm.EVM) {
msg := &Message{
From: params.SystemAddress,
GasLimit: 30_000_000,
GasPrice: common.Big0,
GasFeeCap: common.Big0,
GasTipCap: common.Big0,
GasPrice: uint256.Int{},
GasFeeCap: uint256.Int{},
GasTipCap: uint256.Int{},
To: &params.HistoryStorageAddress,
Data: prevHash.Bytes(),
}
@ -320,9 +321,9 @@ func processRequestsSystemCall(requests *[][]byte, evm *vm.EVM, requestType byte
msg := &Message{
From: params.SystemAddress,
GasLimit: 30_000_000,
GasPrice: common.Big0,
GasFeeCap: common.Big0,
GasTipCap: common.Big0,
GasPrice: uint256.Int{},
GasFeeCap: uint256.Int{},
GasTipCap: uint256.Int{},
To: &addr,
}
evm.SetTxContext(NewEVMTxContext(msg))

View file

@ -147,14 +147,14 @@ type Message struct {
To *common.Address
From common.Address
Nonce uint64
Value *big.Int
Value uint256.Int
GasLimit uint64
GasPrice *big.Int
GasFeeCap *big.Int
GasTipCap *big.Int
GasPrice uint256.Int
GasFeeCap uint256.Int
GasTipCap uint256.Int
Data []byte
AccessList types.AccessList
BlobGasFeeCap *big.Int
BlobGasFeeCap uint256.Int
BlobHashes []common.Hash
SetCodeAuthorizations []types.SetCodeAuthorization
@ -181,31 +181,62 @@ func TransactionToMessage(tx *types.Transaction, s types.Signer, baseFee *big.In
// TransactionToMessageWithAlloc converts a transaction into a Message, using
// the provided allocator for the Message struct and its transient big.Int fields.
func TransactionToMessageWithAlloc(tx *types.Transaction, s types.Signer, baseFee *big.Int, alloc arena.Allocator) (*Message, error) {
msg := new(Message)
msg := arena.New[Message](alloc)
var err error
msg.From, err = types.Sender(s, tx)
if err != nil {
return nil, err
}
msg.Nonce = tx.Nonce()
msg.GasLimit = tx.Gas()
msg.GasPrice = tx.GasPrice()
msg.GasFeeCap = tx.GasFeeCap()
msg.GasTipCap = tx.GasTipCap()
var (
v *uint256.Int
overflow bool
)
if v, overflow = uint256.FromBig(tx.GasPrice()); overflow {
return nil, fmt.Errorf("%w: address %v, maxFeePerGas bit length: %d", ErrFeeCapVeryHigh,
msg.From.Hex(), tx.GasPrice().BitLen())
}
msg.GasPrice = *v
if v, overflow = uint256.FromBig(tx.GasFeeCap()); overflow {
return nil, fmt.Errorf("%w: address %v, maxFeePerGas bit length: %d", ErrFeeCapVeryHigh,
msg.From.Hex(), tx.GasFeeCap().BitLen())
}
msg.GasFeeCap = *v
if v, overflow = uint256.FromBig(tx.GasTipCap()); overflow {
return nil, fmt.Errorf("%w: address %v, maxPriorityFeePerGas bit length: %d", ErrTipVeryHigh,
msg.From.Hex(), tx.GasTipCap().BitLen())
}
msg.GasTipCap = *v
msg.To = tx.To()
msg.Value = tx.Value()
if v, overflow = uint256.FromBig(tx.Value()); overflow {
return nil, fmt.Errorf("%w: address %v", ErrInsufficientFundsForTransfer, msg.From.Hex())
}
msg.Value = *v
msg.Data = tx.Data()
msg.AccessList = tx.AccessList()
msg.SetCodeAuthorizations = tx.SetCodeAuthorizations()
msg.BlobHashes = tx.BlobHashes()
msg.BlobGasFeeCap = tx.BlobGasFeeCap()
if tx.BlobGasFeeCap() != nil {
if v, overflow = uint256.FromBig(tx.BlobGasFeeCap()); overflow {
return nil, fmt.Errorf("%w: blobGasFeeCap exceeds 256 bits", ErrBlobFeeCapTooLow)
}
msg.BlobGasFeeCap = *v
}
// If baseFee provided, set gasPrice to effectiveGasPrice.
if baseFee != nil {
msg.GasPrice = msg.GasPrice.Add(msg.GasTipCap, baseFee)
if msg.GasPrice.Cmp(msg.GasFeeCap) > 0 {
msg.GasPrice = msg.GasFeeCap
baseFeeU256 := uint256.MustFromBig(baseFee)
msg.GasPrice.Add(&msg.GasTipCap, baseFeeU256)
if msg.GasPrice.Cmp(&msg.GasFeeCap) > 0 {
msg.GasPrice.Set(&msg.GasFeeCap)
}
}
var err error
msg.From, err = types.Sender(s, tx)
return msg, err
return msg, nil
}
// ApplyMessage computes the new state by applying the given message
@ -274,22 +305,21 @@ func (st *stateTransition) to() common.Address {
}
func (st *stateTransition) buyGas() error {
// Compute mgval = gasLimit * gasPrice (the gas cost to deduct from sender).
// Use big.Int for balanceCheck to detect >256-bit overflow correctly.
// Use big.Int for balance check to detect >256-bit overflow correctly.
mgval := new(big.Int).SetUint64(st.msg.GasLimit)
mgval.Mul(mgval, st.msg.GasPrice)
mgval.Mul(mgval, st.msg.GasPrice.ToBig())
balanceCheck := new(big.Int).Set(mgval)
if st.msg.GasFeeCap != nil {
if !st.msg.GasFeeCap.IsZero() {
balanceCheck.SetUint64(st.msg.GasLimit)
balanceCheck = balanceCheck.Mul(balanceCheck, st.msg.GasFeeCap)
balanceCheck.Mul(balanceCheck, st.msg.GasFeeCap.ToBig())
}
balanceCheck.Add(balanceCheck, st.msg.Value)
balanceCheck.Add(balanceCheck, st.msg.Value.ToBig())
if st.evm.ChainConfig().IsCancun(st.evm.Context.BlockNumber, st.evm.Context.Time) {
if blobGas := st.blobGasUsed(); blobGas > 0 {
// Check that the user has enough funds to cover blobGasUsed * tx.BlobGasFeeCap
blobBalanceCheck := new(big.Int).SetUint64(blobGas)
blobBalanceCheck.Mul(blobBalanceCheck, st.msg.BlobGasFeeCap)
blobBalanceCheck.Mul(blobBalanceCheck, st.msg.BlobGasFeeCap.ToBig())
balanceCheck.Add(balanceCheck, blobBalanceCheck)
// Pay for blobGasUsed * actual blob fee
blobFee := new(big.Int).SetUint64(blobGas)
@ -352,25 +382,18 @@ func (st *stateTransition) preCheck() error {
// Make sure that transaction gasFeeCap is greater than the baseFee (post london)
if st.evm.ChainConfig().IsLondon(st.evm.Context.BlockNumber) {
// Skip the checks if gas fields are zero and baseFee was explicitly disabled (eth_call)
skipCheck := st.evm.Config.NoBaseFee && msg.GasFeeCap.BitLen() == 0 && msg.GasTipCap.BitLen() == 0
skipCheck := st.evm.Config.NoBaseFee && msg.GasFeeCap.IsZero() && msg.GasTipCap.IsZero()
if !skipCheck {
if l := msg.GasFeeCap.BitLen(); l > 256 {
return fmt.Errorf("%w: address %v, maxFeePerGas bit length: %d", ErrFeeCapVeryHigh,
msg.From.Hex(), l)
}
if l := msg.GasTipCap.BitLen(); l > 256 {
return fmt.Errorf("%w: address %v, maxPriorityFeePerGas bit length: %d", ErrTipVeryHigh,
msg.From.Hex(), l)
}
if msg.GasFeeCap.Cmp(msg.GasTipCap) < 0 {
if msg.GasFeeCap.Cmp(&msg.GasTipCap) < 0 {
return fmt.Errorf("%w: address %v, maxPriorityFeePerGas: %s, maxFeePerGas: %s", ErrTipAboveFeeCap,
msg.From.Hex(), msg.GasTipCap, msg.GasFeeCap)
msg.From.Hex(), &msg.GasTipCap, &msg.GasFeeCap)
}
// This will panic if baseFee is nil, but basefee presence is verified
// as part of header validation.
if msg.GasFeeCap.Cmp(st.evm.Context.BaseFee) < 0 {
baseFeeU256 := uint256.MustFromBig(st.evm.Context.BaseFee)
if msg.GasFeeCap.Cmp(baseFeeU256) < 0 {
return fmt.Errorf("%w: address %v, maxFeePerGas: %s, baseFee: %s", ErrFeeCapTooLow,
msg.From.Hex(), msg.GasFeeCap, st.evm.Context.BaseFee)
msg.From.Hex(), &msg.GasFeeCap, st.evm.Context.BaseFee)
}
}
}
@ -398,11 +421,12 @@ func (st *stateTransition) preCheck() error {
if st.evm.ChainConfig().IsCancun(st.evm.Context.BlockNumber, st.evm.Context.Time) {
if st.blobGasUsed() > 0 {
// Skip the checks if gas fields are zero and blobBaseFee was explicitly disabled (eth_call)
skipCheck := st.evm.Config.NoBaseFee && msg.BlobGasFeeCap.BitLen() == 0
skipCheck := st.evm.Config.NoBaseFee && msg.BlobGasFeeCap.IsZero()
if !skipCheck {
// This will panic if blobBaseFee is nil, but blobBaseFee presence
// is verified as part of header validation.
if msg.BlobGasFeeCap.Cmp(st.evm.Context.BlobBaseFee) < 0 {
blobBaseFeeU256 := uint256.MustFromBig(st.evm.Context.BlobBaseFee)
if msg.BlobGasFeeCap.Cmp(blobBaseFeeU256) < 0 {
return fmt.Errorf("%w: address %v blobGasFeeCap: %v, blobBaseFee: %v", ErrBlobFeeCapTooLow,
msg.From.Hex(), msg.BlobGasFeeCap, st.evm.Context.BlobBaseFee)
}
@ -486,11 +510,7 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
}
// Check clause 6
value, overflow := uint256.FromBig(msg.Value)
if overflow {
return nil, fmt.Errorf("%w: address %v", ErrInsufficientFundsForTransfer, msg.From.Hex())
}
if !value.IsZero() && !st.evm.Context.CanTransfer(st.state, msg.From, value) {
if !msg.Value.IsZero() && !st.evm.Context.CanTransfer(st.state, msg.From, &msg.Value) {
return nil, fmt.Errorf("%w: address %v", ErrInsufficientFundsForTransfer, msg.From.Hex())
}
@ -509,7 +529,7 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
vmerr error // vm errors do not effect consensus and are therefore not assigned to err
)
if contractCreation {
ret, _, st.gasRemaining, vmerr = st.evm.Create(msg.From, msg.Data, st.gasRemaining, value)
ret, _, st.gasRemaining, vmerr = st.evm.Create(msg.From, msg.Data, st.gasRemaining, &msg.Value)
} else {
// Increment the nonce for the next transaction.
st.state.SetNonce(msg.From, st.state.GetNonce(msg.From)+1, tracing.NonceChangeEoACall)
@ -532,7 +552,7 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
}
// Execute the transaction's call.
ret, st.gasRemaining, vmerr = st.evm.Call(msg.From, st.to(), msg.Data, st.gasRemaining, value)
ret, st.gasRemaining, vmerr = st.evm.Call(msg.From, st.to(), msg.Data, st.gasRemaining, &msg.Value)
}
// Record the gas used excluding gas refunds. This value represents the actual
@ -556,13 +576,13 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
}
st.returnGas()
effectiveTipU256, _ := uint256.FromBig(msg.GasPrice)
effectiveTipU256 := new(uint256.Int).Set(&msg.GasPrice)
if rules.IsLondon {
baseFee, _ := uint256.FromBig(st.evm.Context.BaseFee)
effectiveTipU256 = new(uint256.Int).Sub(effectiveTipU256, baseFee)
baseFee := uint256.MustFromBig(st.evm.Context.BaseFee)
effectiveTipU256.Sub(effectiveTipU256, baseFee)
}
if st.evm.Config.NoBaseFee && msg.GasFeeCap.Sign() == 0 && msg.GasTipCap.Sign() == 0 {
if st.evm.Config.NoBaseFee && msg.GasFeeCap.IsZero() && msg.GasTipCap.IsZero() {
// Skip fee payment when NoBaseFee is set and the fee fields
// are 0. This avoids a negative effectiveTip being applied to
// the coinbase when simulating calls.
@ -666,7 +686,7 @@ func (st *stateTransition) calcRefund() uint64 {
// exchanged at the original rate.
func (st *stateTransition) returnGas() {
remaining := uint256.NewInt(st.gasRemaining)
remaining.Mul(remaining, uint256.MustFromBig(st.msg.GasPrice))
remaining.Mul(remaining, &st.msg.GasPrice)
st.state.AddBalance(st.msg.From, remaining, tracing.BalanceIncreaseGasReturn)
if st.evm.Config.Tracer != nil && st.evm.Config.Tracer.OnGasChange != nil && st.gasRemaining > 0 {

View file

@ -47,7 +47,7 @@ func newstack() *Stack {
// falls back to the pool.
func newstackWithAlloc(alloc arena.Allocator) *Stack {
if _, ok := alloc.(*arena.BumpAllocator); ok {
s := new(Stack)
s := arena.New[Stack](alloc)
s.data = arena.MakeSlice[uint256.Int](alloc, 0, 1024)
s.arenaAlloc = true
return s

View file

@ -55,6 +55,7 @@ func (c Config) MarshalTOML() (interface{}, error) {
EnablePreimageRecording bool
EnableWitnessStats bool
StatelessSelfValidation bool
EnableArenaAlloc bool
EnableStateSizeTracking bool
VMTrace string
VMTraceJsonConfig string
@ -108,6 +109,7 @@ func (c Config) MarshalTOML() (interface{}, error) {
enc.EnablePreimageRecording = c.EnablePreimageRecording
enc.EnableWitnessStats = c.EnableWitnessStats
enc.StatelessSelfValidation = c.StatelessSelfValidation
enc.EnableArenaAlloc = c.EnableArenaAlloc
enc.EnableStateSizeTracking = c.EnableStateSizeTracking
enc.VMTrace = c.VMTrace
enc.VMTraceJsonConfig = c.VMTraceJsonConfig
@ -165,6 +167,7 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error {
EnablePreimageRecording *bool
EnableWitnessStats *bool
StatelessSelfValidation *bool
EnableArenaAlloc *bool
EnableStateSizeTracking *bool
VMTrace *string
VMTraceJsonConfig *string
@ -297,6 +300,9 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error {
if dec.StatelessSelfValidation != nil {
c.StatelessSelfValidation = *dec.StatelessSelfValidation
}
if dec.EnableArenaAlloc != nil {
c.EnableArenaAlloc = *dec.EnableArenaAlloc
}
if dec.EnableStateSizeTracking != nil {
c.EnableStateSizeTracking = *dec.EnableStateSizeTracking
}

View file

@ -81,10 +81,10 @@ func Estimate(ctx context.Context, call *core.Message, opts *Options, gasCap uin
// Normalize the max fee per gas the call is willing to spend.
var feeCap *big.Int
if call.GasFeeCap != nil {
feeCap = call.GasFeeCap
} else if call.GasPrice != nil {
feeCap = call.GasPrice
if !call.GasFeeCap.IsZero() {
feeCap = call.GasFeeCap.ToBig()
} else if !call.GasPrice.IsZero() {
feeCap = call.GasPrice.ToBig()
} else {
feeCap = common.Big0
}
@ -93,17 +93,18 @@ func Estimate(ctx context.Context, call *core.Message, opts *Options, gasCap uin
balance := opts.State.GetBalance(call.From).ToBig()
available := balance
if call.Value != nil {
if call.Value.Cmp(available) >= 0 {
if !call.Value.IsZero() {
valueBig := call.Value.ToBig()
if valueBig.Cmp(available) >= 0 {
return 0, nil, core.ErrInsufficientFundsForTransfer
}
available.Sub(available, call.Value)
available.Sub(available, valueBig)
}
if opts.Config.IsCancun(opts.Header.Number, opts.Header.Time) && len(call.BlobHashes) > 0 {
blobGasPerBlob := new(big.Int).SetInt64(params.BlobTxBlobGasPerBlob)
blobBalanceUsage := new(big.Int).SetInt64(int64(len(call.BlobHashes)))
blobBalanceUsage.Mul(blobBalanceUsage, blobGasPerBlob)
blobBalanceUsage.Mul(blobBalanceUsage, call.BlobGasFeeCap)
blobBalanceUsage.Mul(blobBalanceUsage, call.BlobGasFeeCap.ToBig())
if blobBalanceUsage.Cmp(available) >= 0 {
return 0, nil, core.ErrInsufficientFunds
}
@ -113,9 +114,9 @@ func Estimate(ctx context.Context, call *core.Message, opts *Options, gasCap uin
// If the allowance is larger than maximum uint64, skip checking
if allowance.IsUint64() && hi > allowance.Uint64() {
transfer := call.Value
if transfer == nil {
transfer = new(big.Int)
transfer := new(big.Int)
if !call.Value.IsZero() {
transfer = call.Value.ToBig()
}
log.Debug("Gas estimation capped by limited funds", "original", hi, "balance", balance,
"sent", transfer, "maxFeePerGas", feeCap, "fundable", allowance)
@ -252,7 +253,7 @@ func run(ctx context.Context, call *core.Message, opts *Options) (*core.Executio
if call.GasPrice.Sign() == 0 {
evmContext.BaseFee = new(big.Int)
}
if call.BlobGasFeeCap != nil && call.BlobGasFeeCap.BitLen() == 0 {
if call.BlobGasFeeCap.IsZero() {
evmContext.BlobBaseFee = new(big.Int)
}
evm := vm.NewEVM(evmContext, dirtyState, opts.Config, vm.Config{NoBaseFee: true})

View file

@ -994,7 +994,7 @@ func (api *API) TraceCall(ctx context.Context, args ethapi.TransactionArgs, bloc
if msg.GasPrice.Sign() == 0 {
blockContext.BaseFee = new(big.Int)
}
if msg.BlobGasFeeCap != nil && msg.BlobGasFeeCap.BitLen() == 0 {
if !msg.BlobGasFeeCap.IsZero() && msg.BlobGasFeeCap.BitLen() == 0 {
blockContext.BlobBaseFee = new(big.Int)
}
if config != nil {

View file

@ -761,7 +761,7 @@ func applyMessage(ctx context.Context, b Backend, args TransactionArgs, state *s
if msg.GasPrice.Sign() == 0 {
blockContext.BaseFee = new(big.Int)
}
if msg.BlobGasFeeCap != nil && msg.BlobGasFeeCap.BitLen() == 0 {
if !msg.BlobGasFeeCap.IsZero() && msg.BlobGasFeeCap.BitLen() == 0 {
blockContext.BlobBaseFee = new(big.Int)
}
evm := b.GetEVM(ctx, state, header, vmConfig, blockContext)
@ -1366,7 +1366,7 @@ func AccessList(ctx context.Context, b Backend, blockNrOrHash rpc.BlockNumberOrH
if msg.GasPrice.Sign() == 0 {
evm.Context.BaseFee = new(big.Int)
}
if msg.BlobGasFeeCap != nil && msg.BlobGasFeeCap.BitLen() == 0 {
if !msg.BlobGasFeeCap.IsZero() && msg.BlobGasFeeCap.BitLen() == 0 {
evm.Context.BlobBaseFee = new(big.Int)
}
res, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(msg.GasLimit))

View file

@ -477,18 +477,25 @@ func (args *TransactionArgs) ToMessage(baseFee *big.Int, skipNonceCheck bool) *c
if args.AccessList != nil {
accessList = *args.AccessList
}
bigToU256 := func(b *big.Int) uint256.Int {
if b == nil {
return uint256.Int{}
}
v, _ := uint256.FromBig(b)
return *v
}
return &core.Message{
From: args.from(),
To: args.To,
Value: (*big.Int)(args.Value),
Value: bigToU256((*big.Int)(args.Value)),
Nonce: uint64(*args.Nonce),
GasLimit: uint64(*args.Gas),
GasPrice: gasPrice,
GasFeeCap: gasFeeCap,
GasTipCap: gasTipCap,
GasPrice: bigToU256(gasPrice),
GasFeeCap: bigToU256(gasFeeCap),
GasTipCap: bigToU256(gasTipCap),
Data: args.data(),
AccessList: accessList,
BlobGasFeeCap: (*big.Int)(args.BlobFeeCap),
BlobGasFeeCap: bigToU256((*big.Int)(args.BlobFeeCap)),
BlobHashes: args.BlobHashes,
SetCodeAuthorizations: args.AuthorizationList,
SkipNonceChecks: skipNonceCheck,

View file

@ -35,7 +35,6 @@ import (
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/eth/tracers/logger"
"github.com/holiman/uint256"
)
func initMatcher(st *testMatcher) {
@ -316,7 +315,7 @@ func runBenchmark(b *testing.B, t *StateTest) {
start := time.Now()
// Execute the message.
_, leftOverGas, err := evm.Call(sender.Address(), *msg.To, msg.Data, msg.GasLimit, uint256.MustFromBig(msg.Value))
_, leftOverGas, err := evm.Call(sender.Address(), *msg.To, msg.Data, msg.GasLimit, &msg.Value)
if err != nil {
b.Error(err)
return

View file

@ -477,19 +477,23 @@ func (tx *stTransaction) toMessage(ps stPostState, baseFee *big.Int) (*core.Mess
}
}
var blobGasFeeCap uint256.Int
if tx.BlobGasFeeCap != nil {
blobGasFeeCap = *uint256.MustFromBig(tx.BlobGasFeeCap)
}
msg := &core.Message{
From: from,
To: to,
Nonce: tx.Nonce,
Value: value,
Value: *uint256.MustFromBig(value),
GasLimit: gasLimit,
GasPrice: gasPrice,
GasFeeCap: tx.MaxFeePerGas,
GasTipCap: tx.MaxPriorityFeePerGas,
GasPrice: *uint256.MustFromBig(gasPrice),
GasFeeCap: *uint256.MustFromBig(tx.MaxFeePerGas),
GasTipCap: *uint256.MustFromBig(tx.MaxPriorityFeePerGas),
Data: data,
AccessList: accessList,
BlobHashes: tx.BlobVersionedHashes,
BlobGasFeeCap: tx.BlobGasFeeCap,
BlobGasFeeCap: blobGasFeeCap,
SetCodeAuthorizations: authList,
}
return msg, nil