> ## Documentation Index
> Fetch the complete documentation index at: https://docs.cosmos.network/llms.txt
> Use this file to discover all available pages before exploring further.

# EVM Mempool Integration

> Integrate the EVM mempool with your Cosmos EVM chain

## Overview

This guide provides step-by-step instructions for integrating the EVM mempool into your Cosmos EVM chain. For conceptual information about mempool design and architecture, see the [Mempool Concepts](/evm/v0.5.0/documentation/concepts/mempool) page.

### Step 1: Add EVM Mempool to App Struct

```go expandable theme={"theme":{"light":"github-light-high-contrast","dark":"github-dark-high-contrast"}}
// Update your app/app.go to include the EVM mempool
type App struct {
    *baseapp.BaseApp
    // ... other keepers

    // Cosmos EVM keepers
    FeeMarketKeeper   feemarketkeeper.Keeper
    EVMKeeper         *evmkeeper.Keeper
    EVMMempool        *evmmempool.ExperimentalEVMMempool
}
```

### Step 2: Configure Mempool in NewApp Constructor

<Note>
  The mempool must be initialized **after** the antehandler has been set in the app.
</Note>

Add the following configuration in your `NewApp` constructor:

```go expandable theme={"theme":{"light":"github-light-high-contrast","dark":"github-dark-high-contrast"}}
// Set the EVM priority nonce mempool
if evmtypes.GetChainConfig() != nil {
    mempoolConfig := &evmmempool.EVMMempoolConfig{
        AnteHandler:   app.GetAnteHandler(),
        BlockGasLimit: 100_000_000, // or 0 to use default
    }

    evmMempool := evmmempool.NewExperimentalEVMMempool(
        app.CreateQueryContext,
        logger,
        app.EVMKeeper,
        app.FeeMarketKeeper,
        app.txConfig,
        app.clientCtx,
        mempoolConfig,
    )
    app.EVMMempool = evmMempool

    // Replace BaseApp mempool
    app.SetMempool(evmMempool)

    // Set custom CheckTx handler for nonce gap support
    checkTxHandler := evmmempool.NewCheckTxHandler(evmMempool)
    app.SetCheckTxHandler(checkTxHandler)

    // Set custom PrepareProposal handler
    abciProposalHandler := baseapp.NewDefaultProposalHandler(evmMempool, app)
    abciProposalHandler.SetSignerExtractionAdapter(
        evmmempool.NewEthSignerExtractionAdapter(
            sdkmempool.NewDefaultSignerExtractionAdapter(),
        ),
    )
    app.SetPrepareProposal(abciProposalHandler.PrepareProposalHandler())
}
```

<Warning>
  **Breaking Change from v0.4.x:** The global mempool registry (`SetGlobalEVMMempool`) has been removed. Mempool is now passed directly to the JSON-RPC server during initialization.
</Warning>

## Configuration Options

The `EVMMempoolConfig` struct provides several configuration options for customizing the mempool behavior:

<Tabs>
  <Tab title="Minimal Configuration">
    For most use cases, the minimal configuration is sufficient:

    ```go expandable theme={"theme":{"light":"github-light-high-contrast","dark":"github-dark-high-contrast"}}
    mempoolConfig := &evmmempool.EVMMempoolConfig{
        AnteHandler:   app.GetAnteHandler(),
        BlockGasLimit: 100_000_000, // or 0 to use default
    }
    ```
  </Tab>

  <Tab title="Full Configuration Options">
    ```go expandable theme={"theme":{"light":"github-light-high-contrast","dark":"github-dark-high-contrast"}}
    type EVMMempoolConfig struct {
        // Required: AnteHandler for transaction validation
        AnteHandler   sdk.AnteHandler

        // Required: Block gas limit for transaction selection
        BlockGasLimit uint64

        // Optional: Custom legacy pool configuration (replaces TxPool)
        LegacyPoolConfig *legacypool.Config

        // Optional: Custom Cosmos pool configuration (replaces CosmosPool)
        CosmosPoolConfig *sdkmempool.PriorityNonceMempoolConfig[math.Int]

        // Optional: Custom broadcast function for promoted transactions
        BroadcastTxFn func(txs []*ethtypes.Transaction) error

        // Optional: Minimum tip required for EVM transactions
        MinTip *uint256.Int
    }
    ```
  </Tab>
</Tabs>

#### Defaults and Fallbacks

* If `BlockGasLimit` is `0`, the mempool uses a fallback of `100_000_000` gas.
* If `LegacyPoolConfig` is not provided, defaults from `legacypool.DefaultConfig` are used.
* If `CosmosPoolConfig` is not provided, a default `PriorityNonceMempool` is created with:
  * Priority = `(fee_amount / gas_limit)` in the chain bond denom
  * Comparator = big-int comparison (higher is selected first)
  * `MinValue = 0`
* If `BroadcastTxFn` is not provided, a default is created that uses the app `clientCtx`/`txConfig` to broadcast EVM transactions when they are promoted from queued → pending.
* `MinTip` is optional. If unset, selection uses the effective tip from each tx (`min(gas_tip_cap, gas_fee_cap - base_fee)`).

### v0.4.x to v0.5.0 Migration

**Breaking Change:** Pre-built pools replaced with configuration objects

PR [#496](https://github.com/cosmos/evm/pull/496) replaced pre-built pools with configs in `EVMMempoolConfig`:

* **Removed:** `TxPool *txpool.TxPool`, `CosmosPool sdkmempool.ExtMempool`
* **Added:** `LegacyPoolConfig *legacypool.Config`, `CosmosPoolConfig *sdkmempool.PriorityNonceMempoolConfig[math.Int]`

#### Minimal Setups: Nothing to Change

If you use the default mempool wiring (no custom pools), your existing code continues to work:

```go expandable theme={"theme":{"light":"github-light-high-contrast","dark":"github-dark-high-contrast"}}
mempoolConfig := &evmmempool.EVMMempoolConfig{
    AnteHandler:   app.GetAnteHandler(),
    BlockGasLimit: 100_000_000, // or 0 to use default
}
evmMempool := evmmempool.NewExperimentalEVMMempool(
    app.CreateQueryContext, logger, app.EVMKeeper, app.FeeMarketKeeper,
    app.txConfig, app.clientCtx, mempoolConfig
)
```

#### Advanced Setups: Migrate Your Customizations

If you built custom pools yourself, replace them with configuration objects:

**Before (v0.4.x):**

```go expandable theme={"theme":{"light":"github-light-high-contrast","dark":"github-dark-high-contrast"}}
mempoolConfig := &evmmempool.EVMMempoolConfig{
    TxPool:     customTxPool,      // ← REMOVED
    CosmosPool: customCosmosPool,  // ← REMOVED
    AnteHandler:   app.GetAnteHandler(),
    BlockGasLimit: 100_000_000,
}
```

**After (v0.5.0):**

```go expandable theme={"theme":{"light":"github-light-high-contrast","dark":"github-dark-high-contrast"}}
mempoolConfig := &evmmempool.EVMMempoolConfig{
    LegacyPoolConfig: &legacyCfg,  // ← NEW (or nil for defaults)
    CosmosPoolConfig: &cosmosCfg,  // ← NEW (or nil for defaults)
    AnteHandler:      app.GetAnteHandler(),
    BlockGasLimit:    100_000_000,
}
```

### Custom Legacy Pool Configuration

Customize EVM transaction pool parameters:

```go expandable theme={"theme":{"light":"github-light-high-contrast","dark":"github-dark-high-contrast"}}
// EVM legacy txpool tuning
legacyCfg := legacypool.DefaultConfig
legacyCfg.PriceLimit = 2              // Minimum gas price (wei)
legacyCfg.PriceBump = 15              // 15% price bump to replace
legacyCfg.AccountSlots = 32           // Slots per account
legacyCfg.GlobalSlots = 10240         // Total executable slots
legacyCfg.AccountQueue = 128          // Non-executable per account
legacyCfg.GlobalQueue = 2048          // Total non-executable
legacyCfg.Lifetime = 6 * time.Hour    // Max queue time

mempoolConfig.LegacyPoolConfig = &legacyCfg
```

### Custom Cosmos Mempool Configuration

The mempool uses a `PriorityNonceMempool` for Cosmos transactions by default. You can customize the priority calculation:

```go expandable theme={"theme":{"light":"github-light-high-contrast","dark":"github-dark-high-contrast"}}
// Define custom priority calculation for Cosmos transactions
cosmosCfg := sdkmempool.PriorityNonceMempoolConfig[math.Int]{}
cosmosCfg.TxPriority = sdkmempool.TxPriority[math.Int]{
    GetTxPriority: func(goCtx context.Context, tx sdk.Tx) math.Int {
        feeTx, ok := tx.(sdk.FeeTx)
        if !ok {
            return math.ZeroInt()
        }

        // Get fee in bond denomination
        bondDenom := "uatom" // or your chain's bond denom
        fee := feeTx.GetFee()
        found, coin := fee.Find(bondDenom)
        if !found {
            return math.ZeroInt()
        }

        // Calculate gas price: fee_amount / gas_limit
        gasPrice := coin.Amount.Quo(math.NewIntFromUint64(feeTx.GetGas()))
        return gasPrice
    },
    Compare: func(a, b math.Int) int {
        return a.BigInt().Cmp(b.BigInt()) // Higher values have priority
    },
    MinValue: math.ZeroInt(),
}

mempoolConfig.CosmosPoolConfig = &cosmosCfg
```

### Custom Broadcast Function

Override the default broadcast behavior for promoted EVM transactions:

```go expandable theme={"theme":{"light":"github-light-high-contrast","dark":"github-dark-high-contrast"}}
// Custom EVM broadcast (optional)
mempoolConfig.BroadcastTxFn = func(txs []*ethtypes.Transaction) error {
    // Custom logic for broadcasting promoted transactions
    return nil
}
```

### Custom Block Gas Limit

Different chains may require different gas limits based on their capacity:

```go expandable theme={"theme":{"light":"github-light-high-contrast","dark":"github-dark-high-contrast"}}
// Example: 50M gas limit for lower capacity chains
mempoolConfig := &evmmempool.EVMMempoolConfig{
    AnteHandler:   app.GetAnteHandler(),
    BlockGasLimit: 50_000_000,
}
```

### Event Bus Integration

For best results, connect the mempool to CometBFT's EventBus so it can react to finalized blocks:

```go expandable theme={"theme":{"light":"github-light-high-contrast","dark":"github-dark-high-contrast"}}
// After starting the CometBFT node
if m, ok := app.GetMempool().(*evmmempool.ExperimentalEVMMempool); ok {
    m.SetEventBus(bftNode.EventBus())
}
```

This enables chain-head notifications so the mempool can promptly promote/evict transactions when blocks are committed.

## Architecture Components

The EVM mempool consists of several key components:

### ExperimentalEVMMempool

The main coordinator implementing Cosmos SDK's `ExtMempool` interface (`mempool/mempool.go`).

**Key Methods**:

* `Insert(ctx, tx)`: Routes transactions to appropriate pools
* `Select(ctx, filter)`: Returns unified iterator over all transactions
* `Remove(tx)`: Handles transaction removal with EVM-specific logic
* `InsertInvalidNonce(txBytes)`: Queues nonce-gapped EVM transactions locally

### CheckTx Handler

Custom transaction validation that handles nonce gaps specially (`mempool/check_tx.go`).

**Special Handling**: On `ErrNonceGap` for EVM transactions:

```go expandable theme={"theme":{"light":"github-light-high-contrast","dark":"github-dark-high-contrast"}}
if errors.Is(err, ErrNonceGap) {
    // Route to local queue instead of rejecting
    err := mempool.InsertInvalidNonce(request.Tx)
    // Must intercept error and return success to EVM client
    return interceptedSuccess
}
```

### TxPool

Direct port of Ethereum's transaction pool managing both pending and queued transactions (`mempool/txpool/`).

**Key Features**:

* Uses `vm.StateDB` interface for Cosmos state compatibility
* Implements `BroadcastTxFn` callback for transaction promotion
* Cosmos-specific reset logic for instant finality

### PriorityNonceMempool

Standard Cosmos SDK mempool for non-EVM transactions with fee-based prioritization.

**Default Priority Calculation**:

```go expandable theme={"theme":{"light":"github-light-high-contrast","dark":"github-dark-high-contrast"}}
// Calculate effective gas price
priority = (fee_amount / gas_limit) - base_fee
```

## Transaction Type Routing

The mempool handles different transaction types appropriately:

### Ethereum Transactions (MsgEthereumTx)

* **Tier 1 (Local)**: EVM TxPool handles nonce gaps and promotion
* **Tier 2 (Network)**: CometBFT broadcasts executable transactions

### Cosmos Transactions (Bank, Staking, Gov, etc.)

* **Direct to Tier 2**: Always go directly to CometBFT mempool
* **Standard Flow**: Follow normal Cosmos SDK validation and broadcasting
* **Priority-Based**: Use PriorityNonceMempool for fee-based ordering

## Unified Transaction Selection

During block building, both transaction types compete fairly based on their effective tips:

```go expandable theme={"theme":{"light":"github-light-high-contrast","dark":"github-dark-high-contrast"}}
// Simplified selection logic
func SelectTransactions() Iterator {
    evmTxs := GetPendingEVMTransactions()      // From local TxPool
    cosmosTxs := GetPendingCosmosTransactions() // From Cosmos mempool

    return NewUnifiedIterator(evmTxs, cosmosTxs) // Fee-based priority
}
```

**Fee Comparison**:

* **EVM**: `gas_tip_cap` or `min(gas_tip_cap, gas_fee_cap - base_fee)`
* **Cosmos**: `(fee_amount / gas_limit) - base_fee`
* **Selection**: Higher effective tip gets selected first

## Testing Your Integration

### Verify Nonce Gap Handling

Test that transactions with nonce gaps are properly queued:

```javascript expandable theme={"theme":{"light":"github-light-high-contrast","dark":"github-dark-high-contrast"}}
// Send transactions out of order
await wallet.sendTransaction({nonce: 100, ...}); // OK: Immediate execution
await wallet.sendTransaction({nonce: 102, ...}); // OK: Queued locally (gap)
await wallet.sendTransaction({nonce: 101, ...}); // OK: Fills gap, both execute
```

### Test Transaction Replacement

Verify that higher-fee transactions replace lower-fee ones:

```javascript expandable theme={"theme":{"light":"github-light-high-contrast","dark":"github-dark-high-contrast"}}
// Send initial transaction
const tx1 = await wallet.sendTransaction({
  nonce: 100,
  gasPrice: parseUnits("20", "gwei")
});

// Replace with higher fee
const tx2 = await wallet.sendTransaction({
  nonce: 100, // Same nonce
  gasPrice: parseUnits("30", "gwei") // Higher fee
});
// tx1 is replaced by tx2
```

### Verify Batch Deployments

Test typical deployment scripts (like Uniswap) that send many transactions at once:

```javascript expandable theme={"theme":{"light":"github-light-high-contrast","dark":"github-dark-high-contrast"}}
// Deploy multiple contracts in quick succession
const factory = await Factory.deploy();
const router = await Router.deploy(factory.address);
const multicall = await Multicall.deploy();
// All transactions should queue and execute properly
```

## Monitoring and Debugging

Use the [txpool RPC methods](/evm/v0.5.0/api-reference/ethereum-json-rpc/methods#txpool-methods) to monitor mempool state:

* `txpool_status`: Get pending and queued transaction counts
* `txpool_content`: View all transactions in the pool
* `txpool_inspect`: Get human-readable transaction summaries
* `txpool_contentFrom`: View transactions from specific addresses

## Related Documentation

* [Mempool Concepts](/evm/v0.5.0/documentation/concepts/mempool) - Understanding mempool behavior and design
* [EVM Module Integration](/evm/v0.5.0/documentation/getting-started/build-a-chain/overview) - Prerequisites for mempool integration
* [JSON-RPC Methods](/evm/v0.5.0/api-reference/ethereum-json-rpc/methods#txpool-methods) - Mempool query methods
