xanoutas/solidity-security-findings huggingface dataset:
State DB contract code status inconsistency allows contract addresses to bypass EOA-only transaction restrictions
Audit: 2024-11-nibiru-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-11-nibiru/blob/84054a4f00fdfefaa8e5849c53eb66851a762319/x/evm/keeper/msg_server.go#L324
https://github.com/code-423n4/2024-11-nibiru/blob/84054a4f00fdfefaa8e5849c53eb66851a762319/app/evmante/evmante_verify_eth_acc.go#L70


# Vulnerability details

## Finding description and impact

The Nibiru EVM implementation maintains a restriction that transaction senders must be Externally Owned Accounts (EOAs) and not contracts. This is enforced in `evmante_verify_eth_acc.go` via the `IsContract` check. However, there is an inconsistency in how contract code status is tracked in the state DB.

When a new contract is created through `msg_server.go`, the code hash is never properly set in the state DB via `SetCode`. This means that newly created contract accounts will appear as EOAs when checked via `IsContract`, allowing them to bypass the EOA-only transaction restriction.

The only place where contract code is properly set is during genesis initialization in `genesis.go`, making the `IsContract` check effectively useless for any contracts created after genesis.

## Proof of Concept

The key pieces of code that demonstrate this issue are:

1. Contract creation in msg_server.go ([line 324](https://github.com/code-423n4/2024-11-nibiru/blob/84054a4f00fdfefaa8e5849c53eb66851a762319/x/evm/keeper/msg_server.go#L324)):
```go
if contractCreation {
    // take over the nonce management from evm:
    // - reset sender's nonce to msg.Nonce() before calling evm.
    // - increase sender's nonce by one no matter the result.
    stateDB.SetNonce(sender.Address(), msg.Nonce())
    ret, _, leftoverGas, vmErr = evmObj.Create(
        sender,
        msg.Data(),
        leftoverGas,
        msgWei,
    )
    stateDB.SetNonce(sender.Address(), msg.Nonce()+1)
}
```
Note that there is no `SetCode` call after contract creation.

2. The EOA check in evmante_verify_eth_acc.go ([line 70](https://github.com/code-423n4/2024-11-nibiru/blob/84054a4f00fdfefaa8e5849c53eb66851a762319/app/evmante/evmante_verify_eth_acc.go#L70)):
```go
} else if acct.IsContract() {
    return ctx, errors.Wrapf(sdkerrors.ErrInvalidType,
        "the sender is not EOA: address %s, codeHash <%s>", fromAddr, acct.CodeHash)
}
```

3. Genesis code setting in genesis.go ([line 65](https://github.com/code-423n4/2024-11-nibiru/blob/84054a4f00fdfefaa8e5849c53eb66851a762319/x/evm/evmmodule/genesis.go#L65)):
```go
k.SetCode(ctx, codeHash.Bytes(), code)
```

This issue affects both regular CREATE operations and CREATE2, since neither will properly record the contract code status in the state DB. The fix is easier for the ones that are created via CREATE from an EOA because there is special code handling these.

## Recommended mitigation steps

The code needs to be properly set in the state DB when creating new contracts. As a first step, a `SetCode` call after successful contract creation in `msg_server.go` can be added:
```go
if contractCreation {
    stateDB.SetNonce(
Gas used mismatch in failed contract calls can lead to wrong gas deductions
Audit: 2024-11-nibiru-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-11-nibiru/blob/main/x/evm/keeper/call_contract.go#L118


# Vulnerability details

## Finding description and impact

In `call_contract.go`, when a contract call fails, the code only consumes gas for the failed transaction but does not account for previously accumulated block gas usage. This creates a mismatch between the actual gas used in the block and what gets consumed in the gas meter.

The issue occurs in the error handling path of `CallContractWithInput` where after a failed call, only the gas of the failed transaction is consumed:

```go
if evmResp.Failed() {
    k.ResetGasMeterAndConsumeGas(ctx, evmResp.GasUsed) // Only consumes gas for this tx
    // ... error handling
}
```

However, in the success path, the code correctly adds the gas used to the block total:

```go
blockGasUsed, err := k.AddToBlockGasUsed(ctx, evmResp.GasUsed)
// ...
k.ResetGasMeterAndConsumeGas(ctx, blockGasUsed)
```

This inconsistency means that failed transactions do not properly contribute to the block's gas tracking. This is especially bad when the failed transaction is the last one in a block, as it will decrease the gas counter heavily (only the last transaction vs. the sum of all). 


## Proof of Concept

The issue can be found in [call_contract.go#L118](https://github.com/code-423n4/2024-11-nibiru/blob/main/x/evm/keeper/call_contract.go#L118):

```go
if evmResp.Failed() {
    k.ResetGasMeterAndConsumeGas(ctx, evmResp.GasUsed) // Only consumes failed tx gas
    if strings.Contains(evmResp.VmError, vm.ErrOutOfGas.Error()) {
        err = fmt.Errorf("gas required exceeds allowance (%d)", gasLimit)
        return
    }
    if evmResp.VmError == vm.ErrExecutionReverted.Error() {
        err = fmt.Errorf("VMError: %w", evm.NewRevertError(evmResp.Ret))
        return
    }
    err = fmt.Errorf("VMError: %s", evmResp.VmError)
    return
}
```

Compared to the success path at [call_contract.go#L133](https://github.com/code-423n4/2024-11-nibiru/blob/main/x/evm/keeper/call_contract.go#L133):

```go
blockGasUsed, err := k.AddToBlockGasUsed(ctx, evmResp.GasUsed)
if err != nil {
    k.ResetGasMeterAndConsumeGas(ctx, ctx.GasMeter().Limit())
    return nil, nil, errors.Wrap(err, "error adding transient gas used to block")
}
k.ResetGasMeterAndConsumeGas(ctx, blockGasUsed)
```

## Recommended mitigation steps

To fix this issue, the gas accounting should be consistent between success and failure paths. Add the block gas tracking before handling the failure case:

```go
// Add to block gas used regardless of success/failure
blockGasUsed, err := k.AddToBlockGasUsed(ctx, evmResp.GasUsed)
if err != nil {
    k.ResetGasMeterAndConsumeGas(ctx, ctx.GasMeter().Limit())
    return nil, nil, errors.Wrap(err, "error adding transient gas used to block")
}
k.ResetGasMeterAndConsumeGas(ctx, blockGasUsed)

if evmResp.Failed() {
    if strings.Contains(evmResp.VmError, vm.ErrOutOfGas.Error()) {
        err = fmt.Errorf("gas required exceeds
Gas refunds use block gas instead of transaction gas, leading to incorrect refund amounts
Audit: 2024-11-nibiru-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-11-nibiru/blob/84054a4f00fdfefaa8e5849c53eb66851a762319/app/evmante/evmante_gas_consume.go#L100-L105


# Vulnerability details

## Finding description and impact

There is a mismatch between how gas fees are deducted and refunded in the EVM implementation:

1. In `evmante_gas_consume.go`, gas fees are deducted upfront based on each transaction's individual gas limit
2. However, the refund calculation in `msg_server.go` uses the cumulative block gas usage to determine refunds for individual transactions

This mismatch means users will receive incorrect (lower) refunds than they should. The gas refund should be based on the difference between a transaction's gas limit (what was charged) and its actual gas usage (what was consumed), not the block's total gas usage.

The impact is that users will lose funds as they receive smaller refunds than they should. This becomes especially problematic when multiple transactions are included in a block, as the cumulative block gas increases with each transaction, reducing refunds for subsequent transactions.

## Proof of Concept

The issue stems from two pieces of code:

1. Gas fees are deducted in `evmante_gas_consume.go` based on transaction gas limit:
```go
// https://github.com/code-423n4/2024-11-nibiru/blob/84054a4f00fdfefaa8e5849c53eb66851a762319/app/evmante/evmante_gas_consume.go#L100-L105
		fees, err := keeper.VerifyFee(
			txData,
			evm.EVMBankDenom,
			baseFeeMicronibiPerGas,
			ctx.IsCheckTx(),
		)
```

Where `VerifyFee` returns the fee based on the transaction gas limit:
```go
// https://github.com/code-423n4/2024-11-nibiru/blob/84054a4f00fdfefaa8e5849c53eb66851a762319/x/evm/keeper/gas_fees.go#L194-L200
	feeAmtMicronibi := evm.WeiToNative(txData.EffectiveFeeWei(baseFeeWei))
	if feeAmtMicronibi.Sign() == 0 {
		// zero fee, no need to deduct
		return sdk.Coins{{Denom: denom, Amount: sdkmath.ZeroInt()}}, nil
	}

	return sdk.Coins{{Denom: denom, Amount: sdkmath.NewIntFromBigInt(feeAmtMicronibi)}}, nil
```

2. But refunds in `msg_server.go` are calculated using block gas:
```go
// https://github.com/code-423n4/2024-11-nibiru/blob/84054a4f00fdfefaa8e5849c53eb66851a762319/x/evm/keeper/msg_server.go#L87-L93
blockGasUsed, err := k.AddToBlockGasUsed(ctx, evmResp.GasUsed)
if err != nil {
    return nil, errors.Wrap(err, "EthereumTx: error adding transient gas used to block")
}

refundGas := uint64(0)
if evmMsg.Gas() > blockGasUsed {
    refundGas = evmMsg.Gas() - blockGasUsed
}
```

To demonstrate the impact, consider this scenario:

1. Transaction A has gas limit 100,000 and uses 50,000 gas
2. Transaction B has gas limit 100,000 and uses 40,000 gas
3. When calculating the refund for transaction B:
   - It should receive: 100,000 - 40,000 = 60,000 gas refund
   - But actually receives: 100,000 - (50,000 + 40,000) = 10,000 gas refund
   - The user loses refund for 50,000 gas

## Recommended mitigation steps

The refund calculation should be based on each tran
Inconsistent Fee Denomination Handling in Transaction Validation and Building
Audit: 2024-11-nibiru-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-11-nibiru/blob/84054a4f00fdfefaa8e5849c53eb66851a762319/app/evmante/evmante_validate_basic.go#L128


# Vulnerability details

## Finding description and impact

The Nibiru EVM module incorrectly handles fee denominations during transaction validation and building, failing to convert wei amounts to the native unibi denomination. This can lead to significant discrepancies in fee calculations and potentially allow users to pay far fewer fees than intended.

The issue occurs in two locations:

1. Transaction validation in `evmante_validate_basic.go`
2. Transaction building in `msg.go`

In both cases, fees that are calculated in wei (1e18 units) are directly used with `evm.EVMBankDenom` (unibi), without converting from wei to unibi (1e6 units). This causes an undervaluation of fees by a factor of 10^12, as the system expects fees in unibi but receives them in wei.

## Proof of Concept

1. In `evmante_validate_basic.go`, the fee validation directly uses the wei amount:

```go
txFee = txFee.Add(
    sdk.Coin{
        Denom:  evm.EVMBankDenom,
        Amount: sdkmath.NewIntFromBigInt(txData.Fee()), // Fee() returns wei amount
    },
)
```
[Link to code](https://github.com/code-423n4/2024-11-nibiru/blob/84054a4f00fdfefaa8e5849c53eb66851a762319/app/evmante/evmante_validate_basic.go#L128)

2. Similarly in `msg.go`, when building a transaction:

```go
feeAmt := sdkmath.NewIntFromBigInt(txData.Fee())
if feeAmt.Sign() > 0 {
    fees = append(fees, sdk.NewCoin(evmDenom, feeAmt)) // Fee() returns wei amount
}
```
[Link to code](https://github.com/code-423n4/2024-11-nibiru/blob/84054a4f00fdfefaa8e5849c53eb66851a762319/x/evm/msg.go#L367)

The `Fee()` function in `tx_data_legacy.go` calculates fees in wei:
```go
func (tx LegacyTx) Fee() *big.Int {
    return priceTimesGas(tx.GetGasPrice(), tx.GetGas())
}
```

To demonstrate the impact:
1. A transaction with a gas price of 1 wei and gas limit of 21000 would result in a fee of 21000 wei
2. This should be converted to 0.000021 unibi (21000 / 10^12)
3. However, the current code would set it as 21000 unibi, which is incorrect by a factor of 10^12

## Recommended mitigation steps

1. In `evmante_validate_basic.go`, modify the fee validation to convert from wei to unibi:

```go
txFee = txFee.Add(
    sdk.Coin{
        Denom:  evm.EVMBankDenom,
        Amount: sdkmath.NewIntFromBigInt(evm.WeiToNative(txData.Fee())),
    },
)
```

2. In `msg.go`, update the fee conversion in `BuildTx`:

```go
feeAmt := sdkmath.NewIntFromBigInt(evm.WeiToNative(txData.Fee()))
if feeAmt.Sign() > 0 {
    fees = append(fees, sdk.NewCoin(evmDenom, feeAmt))
}
```
Access Control Vulnerability in `updateBlockBloom` Function
Audit: 2024-11-nibiru-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-11-nibiru/blob/main/x/evm/keeper/msg_server.go#L709


# Vulnerability details

#### **Summary**
The `updateBlockBloom` function in the smart contract module lacks proper access control checks, allowing unauthorized entities to manipulate critical blockchain state variables, including the `BlockBloom` filter and `BlockLogSize`. This vulnerability can lead to corruption of the blockchain state and potential denial of service (DoS) attacks.

##### **Code Snippet : msg_server.go#L709**
```go
func (k *Keeper) updateBlockBloom(
	ctx sdk.Context,
	evmResp *evm.MsgEthereumTxResponse,
	logIndex uint64,
) {
	logs := evm.LogsToEthereum(evmResp.Logs)
	if len(logs) > 0 {
		k.EvmState.BlockBloom.Set(ctx, k.EvmState.CalcBloomFromLogs(ctx, logs).Bytes())
		blockLogSize := logIndex + uint64(len(logs))
		k.EvmState.BlockLogSize.Set(ctx, blockLogSize)
	}
}
```

#### **Suggested Fix**
Implement access control checks to ensure that only authorized roles (e.g., validators, system administrators) can invoke the `updateBlockBloom` function. Suggested solutions:
1. **Caller Authorization**: Verify the caller’s identity using a role-based mechanism:
   ```go
   if !k.HasPermission(ctx, callerAddress) {
       return sdkerrors.Wrap(sdkerrors.ErrUnauthorized, "caller not authorized")
   }
   ```
Missing Contract Type Check in DeleteAccount Function
Audit: 2024-11-nibiru-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-11-nibiru/blob/main/x/evm/keeper/statedb.go#L159-L185


# Vulnerability details

## Finding description and impact

DeleteAccount function is designed to handles contract's suicide call, clearing the balance, contract bytecode, contract state, and its native account. However lack of check is current account is an contract address.

The potential problem arises when the function processes non-contract accounts, which may not have associated contract bytecode or state, leading to unnecessary operations or errors when interacting with contract-specific methods like clearing the contract's storage and bytecode.

## Proof of Concept
From the function comment we can see developer intends to clean the bytecode :
<https://github.com/code-423n4/2024-11-nibiru/blob/main/x/evm/keeper/statedb.go#L155-L158>
```golang
// DeleteAccount handles contract's suicide call, clearing the balance, contract
// bytecode, contract state, and its native account.
// Implements the `statedb.Keeper` interface.
// Only called by `StateDB.Commit()`.
```

```golang
func (k *Keeper) DeleteAccount(ctx sdk.Context, addr gethcommon.Address) error { //@audit not check if account is contract ? https://github.com/evmos/evmos/blob/main/x/evm/keeper/statedb.go#L244-L279
	nibiruAddr := sdk.AccAddress(addr.Bytes())
	acct := k.accountKeeper.GetAccount(ctx, nibiruAddr)
	if acct == nil {
		return nil
	}

	_, ok := acct.(eth.EthAccountI)
	if !ok {
		return evm.ErrInvalidAccount.Wrapf("type %T, address %s", acct, addr)
	}

	// clear balance
	if err := k.SetAccBalance(ctx, addr, new(big.Int)); err != nil {
		return err
	}

	// clear storage
	k.ForEachStorage(ctx, addr, func(key, _ gethcommon.Hash) bool {
		k.SetState(ctx, addr, key, nil)
		return true
	})

	k.accountKeeper.RemoveAccount(ctx, acct)

	return nil
}
```
Above code is lack of IsContract check.


## Recommended mitigation steps

Add an additional check to verify whether the account is a contract before attempting to clear the contract-specific data (bytecode, storage)

```diff
@@ -163,6 +164,12 @@ func (k *Keeper) DeleteAccount(ctx sdk.Context, addr gethcommon.Address) error {
                return nil
        }
 
+       //add IsContract check here
+       acctWithOutBal := k.GetAccountWithoutBalance(ctx, addr)
+       if !acctWithOutBal.IsContract() {
+               return errors.New("only smart contracts can be self-destructed")
+       }
+
        _, ok := acct.(eth.EthAccountI)
        if !ok {
```
The ApplyEvmMsg Function in x/evm/keeper/msg_server.go Allows Transfers with Negative Values
Audit: 2024-11-nibiru-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-11-nibiru/blob/main/x/evm/keeper/msg_server.go#L292
https://github.com/code-423n4/2024-11-nibiru/blob/main/x/evm/keeper/msg_server.go#L249
https://github.com/code-423n4/2024-11-nibiru/blob/main/x/evm/keeper/msg_server.go#L402


# Vulnerability details

# The `ApplyEvmMsg` Function in `x/evm/keeper/msg_server.go` Allows Transfers with Negative Values

The ApplyEvmMsg function is responsible for applying a message in the Ethereum Virtual Machine (EVM) and computing the updated blockchain state.

Within ApplyEvmMsg, the ParseWeiAsMultipleOfMicronibi function is called to convert the Wei value (the smallest unit of Ether) from the message into Micronibi.

[Source Code](https://github.com/code-423n4/2024-11-nibiru/blob/main/x/evm/keeper/msg_server.go#L249)

```go
func (k *Keeper) ApplyEvmMsg(ctx sdk.Context,
	msg core.Message,
	tracer vm.EVMLogger,
	commit bool,
	evmConfig *statedb.EVMConfig,
	txConfig statedb.TxConfig,
	fullRefundLeftoverGas bool,
) (resp *evm.MsgEthereumTxResponse, evmObj *vm.EVM, err error) {
	...

	msgWei, err := ParseWeiAsMultipleOfMicronibi(msg.Value()) // <--------
	if err != nil {
		return nil, evmObj, errors.Wrapf(err, "ApplyEvmMsg: invalid wei amount %s", msg.Value())
	}

	...
}
```

However, in the ParseWeiAsMultipleOfMicronibi function, when weiInt is negative, it is incorrectly returned as-is without conversion to Micronibi. One Micronibi equals 101210^{12}1012 Wei. This leads to subsequent misinterpretation of the negative Wei value as a valid Micronibi value.

[Source Code](https://github.com/code-423n4/2024-11-nibiru/blob/main/x/evm/keeper/msg_server.go#L402)

```go
func ParseWeiAsMultipleOfMicronibi(weiInt *big.Int) (newWeiInt *big.Int, err error) {
	// If "weiValue" is nil, 0, or negative, early return
	if weiInt == nil || !(weiInt.Cmp(big.NewInt(0)) > 0) { // <----------
		return weiInt, nil
	}

	// Error if weiInt is too small
	tenPow12 := new(big.Int).Exp(big.NewInt(10), big.NewInt(12), nil)
	if weiInt.Cmp(tenPow12) < 0 {
		return weiInt, fmt.Errorf(
			"wei amount is too small (%s), cannot transfer less than 1 micronibi. 1 NIBI == 10^6 micronibi == 10^18 wei", weiInt)
	}

	// Truncate to the highest Micronibi amount
	newWeiInt = evm.NativeToWei(evm.WeiToNative(weiInt))
	return newWeiInt, nil
}
```

This behavior allows subsequent operations, such as evmObj.Create or evmObj.Call, to process transactions with negative transfer values.

### Example Flow of Negative Transfers

In ApplyEvmMsg, if a negative msgWei value is passed to evmObj.Call, the following chain of events occurs:

```go
func (k *Keeper) ApplyEvmMsg(ctx sdk.Context,
	msg core.Message,
	tracer vm.EVMLogger,
	commit bool,
	evmConfig *statedb.EVMConfig,
	txConfig statedb.TxConfig,
	fullRefundLeftoverGas bool,
) (resp *evm.MsgEthereumTxResponse, evmObj *vm.EVM, err error) {
	...

	msgWei, err := ParseWeiAsMultipleOfMicronibi(msg.Value())
	if err != nil {
		return nil, evmObj, errors.Wrapf(err, "ApplyEv
Overly broad generic grants could cause privilege escalation and misuse of staking or governance actions
Audit: 2024-11-nibiru-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-11-nibiru/blob/8ed91a036f664b421182e183f19f6cef1a4e28ea/app/ante/auth_grard_test.go#L39-L49


# Vulnerability details

## Finding description and impact
Overly Broad Generic Grants
Root Cause: The code permits generic grants (authz.NewGenericAuthorization) for non-EVM messages without sufficient granularity or constraints. This creates potential for privilege escalation, where grants are misused for unintended purposes.

Impact: Users might exploit generic grants to perform unauthorized operations, including staking and governance actions.

## Proof of Concept
// Abusing generic grants for staking operations
someTime := time.Now()
expiryTime := someTime.Add(time.Hour)
genericGrant, err := authz.NewGrant(
    someTime,
    authz.NewGenericAuthorization(sdk.MsgTypeURL(&stakingtypes.MsgDelegate{})),
    &expiryTime,
)
if err != nil {
    log.Fatalf("Grant creation failed: %v", err)
}
msgGrant := &authz.MsgGrant{Grant: genericGrant}

// Executing staking actions using generic grant
msgExec := authz.NewMsgExec(
    sdk.AccAddress("malicioususer"),
    []sdk.Msg{
        msgGrant,
    },
)

## Recommended mitigation steps
1. Prohibit generic grants for critical modules (e.g., staking, governance).
2. Implement specific, restrictive grants for such operations to ensure explicit intent.
RPC DOS via TraceTx
Audit: 2024-11-nibiru-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-11-nibiru/blob/main/x/evm/keeper/grpc_query.go#L547
https://github.com/code-423n4/2024-11-nibiru/blob/main/x/evm/keeper/grpc_query.go#L511


# Vulnerability details

The TraceTx method in x/evm/keeper/grpc_query.go implements a gRPC query interface that allows simulation and tracing of specific transactions based on provided configurations. This method enables users to perform detailed execution simulations for transactions in a block.

However, a DOS issue arises during the simulation of predecessor transactions.

---

### Proof of Concept

Within the TraceTx function, predecessor transactions are simulated first, followed by transaction tracing:

https://github.com/code-423n4/2024-11-nibiru/blob/main/x/evm/keeper/grpc_query.go#L547

```go
result, _, err := k.TraceEthTxMsg(ctx, cfg, txConfig, msg, req.TraceConfig, false, tracerConfig)
if err != nil {
    // error will be returned with detailed status from traceTx
    return nil, err
}
```

During the simulation of predecessor transactions, an attacker can exploit the process by providing an excessively large number of transactions in the req.Predecessors parameter. This forces the chain to repeatedly compute transaction results, maliciously consuming resources and leading to a denial of service.

https://github.com/code-423n4/2024-11-nibiru/blob/main/x/evm/keeper/grpc_query.go#L511

```go
for i, tx := range req.Predecessors {
    ethTx := tx.AsTransaction()
    msg, err := ethTx.AsMessage(signer, cfg.BaseFeeWei)
    if err != nil {
        continue
    }
    txConfig.TxHash = ethTx.Hash()
    txConfig.TxIndex = uint(i)
    ctx = ctx.WithGasMeter(eth.NewInfiniteGasMeterWithLimit(msg.Gas())).
        WithKVGasConfig(storetypes.GasConfig{}).
        WithTransientKVGasConfig(storetypes.GasConfig{})
    rsp, _, err := k.ApplyEvmMsg(ctx, msg, evm.NewNoOpTracer(), true, cfg, txConfig, false)
    if err != nil {
        continue
    }
    txConfig.LogIndex += uint(len(rsp.Logs))
}
```

An attacker only needs to send an RPC query with an excessively large --predecessors parameter to trigger the DOS:

```bash
nibid query evm trace-tx \
  --block-number 100 \
  --block-time "2024-11-18T00:00:00Z" \
  --block-hash "0x123abc..." \
  --proposer-address "nibiru1xyz..." \
  --predecessors '[{"hash":"0x456def...","nonce":1,"from":"0xabc123...","to":"0xdef456..."}]' \
  --msg '{"hash":"0x789ghi...","nonce":2,"from":"0xabc123...","to":"0xdef456...","data":"0x..."}' \
  --trace-config '{"disableStorage":false,"disableMemory":false}'
```

---

### Suggested Fixes

- **Limit the number of predecessor transactions**: Set an upper bound on the number of transactions allowed in req.Predecessors to prevent resource abuse.
- **Enforce a total gas consumption limit**: Add a global gas consumption restriction for the simulation process to avoid infinite computation scenarios.
- Add Timeout Limitation

Similar to the timeout mechanism in TraceEthTxMsg, a timeout restriction