There's a specific kind of frustration that only crypto people know. You submit a transaction. It disappears into the void. No confirmation. No failure. No error message. Just a spinning indicator and a transaction hash pointing at something that, as far as the network is concerned, might as well not exist yet.
Welcome to mempool purgatory. Population: your 0.003 ETH.
The culprit is almost always gas. Specifically, the gap between what you told the network you'd pay and what the network actually needs to include your transaction in the next block. That gap sounds like a simple thing to manage. In practice it's the most friction-generating mechanic in crypto UX, and it trips up everyone from first-time MetaMask users to people who have been in the space for years.
Here's how it actually works.
What Gas Actually Is
Gas is not a fee in the traditional sense. It's a unit of computational work. Every operation the Ethereum Virtual Machine performs has a cost measured in gas ā a simple ETH transfer costs 21,000 gas, a token swap costs more, a complex DeFi interaction with multiple contract calls can cost hundreds of thousands.
You don't pay in gas directly. You pay in ETH, at a rate of (gas units) Ć (gas price). The gas price is what you're bidding ā what you're willing to pay per unit of work. That's denominated in Gwei (one billionth of an ETH).
Post-EIP-1559, the structure has two components:
Base fee ā Set by the protocol, not by you. It adjusts automatically based on how full the previous block was. If blocks are filling up, the base fee rises. If blocks are emptying out, it drops. The base fee is burned ā it doesn't go to miners/validators.
Priority fee (tip) ā What you pay to validators on top of the base fee to incentivize them to include your transaction. This is the part that actually reaches the block producer.
Your total gas cost is: (base fee + priority fee) Ć gas units
When MetaMask shows you gas options ā slow, market, aggressive ā it's estimating where these numbers need to be for your transaction to land in the next block, the next few blocks, or eventually. The estimations are imperfect because the mempool is a live auction and conditions change.
Transaction cost breakdown:
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
Gas limit: 21,000 units (ETH transfer)
Base fee: 85 Gwei (protocol-set, burned)
Priority fee: 2 Gwei (your tip to validator)
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
Total gas price: 87 Gwei
Total cost: 21,000 Ć 87 Gwei = 0.001827 ETH
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
At $3,200/ETH that's ~$5.85
The number that matters when your transaction is stuck: your max fee vs. the current base fee. If the base fee climbed above your max fee after you submitted, your transaction literally cannot be included ā validators would lose money processing it. It sits in the mempool and waits.
Why Transactions Get Stuck
The mempool is a waiting room. Every unconfirmed transaction on Ethereum lives there, visible to anyone, waiting to be selected by a validator building the next block. Validators are rational actors. They pick transactions that maximize their fee income. If yours is priced below the current base fee, or your tip is too low relative to competing transactions, you wait.
A few ways this happens in practice:
You submitted during a quiet period and gas spiked. You set 120 Gwei when the base fee was 115. Seemed fine. Then a popular NFT mint or token launch hit and the base fee jumped to 180. Your transaction is now unprocessable at its current price. It'll sit until activity dies back down ā which might be minutes or hours.
You used the default "slow" option without checking. MetaMask's slow setting is an estimate for eventual inclusion, not a guarantee. In volatile conditions, "slow" can mean very slow. Or stuck.
You sent a second transaction with a higher nonce while the first is pending. This is the compounding mistake. Ethereum processes transactions from each address in nonce order. If nonce 47 is stuck, nonce 48, 49, and every subsequent transaction from your wallet is also stuck behind it. The queue doesn't skip.
# Checking your pending transactions via web3.py
# Useful for understanding what's actually in the mempool
from web3 import Web3
w3 = Web3(Web3.HTTPProvider('https://mainnet.infura.io/v3/YOUR_KEY'))
address = '0xYourWalletAddress'
checksum = w3.to_checksum_address(address)
# Confirmed nonce (last included in a block)
confirmed_nonce = w3.eth.get_transaction_count(checksum, 'latest')
# Pending nonce (including mempool)
pending_nonce = w3.eth.get_transaction_count(checksum, 'pending')
pending_count = pending_nonce - confirmed_nonce
print(f"Confirmed nonce: {confirmed_nonce}")
print(f"Pending nonce: {pending_nonce}")
print(f"Stuck txs: {pending_count}")
# If pending > confirmed, you have transactions sitting in mempool
# The first stuck one is at nonce: confirmed_nonce
If pending_count is greater than zero, you have something waiting. The fix always starts with the lowest stuck nonce.