---
name: "gas_optimization"
description: "Estimate gas, set EIP-1559 fees, and batch transactions on EVM chains without overpaying. Triggers when an agent must send an onchain transaction, tune fees for urgency, or react to a stuck transaction."
metadata: { "includeInPrompt": true }
---

# Gas Optimization

## Purpose
Help an agent price and execute EVM transactions sensibly: estimate how much gas a transaction needs, read the live fee market, set a fee that matches the transaction's urgency, batch independent calls when possible, and handle stuck transactions. Spend-side skill; pairs with `wallet_safety` (defense) — this one is about fees, not key hygiene. No trading advice, no speculation.

## Workflow
1. Read the live fee market before quoting anything. Call `eth_gasPrice`, or better, `eth_feeHistory` for the EIP-1559 base fee and recent priority-fee percentiles:
   ```bash
   curl -s -X POST -H "Content-Type: application/json" \
     -d '{"jsonrpc":"2.0","id":1,"method":"eth_feeHistory","params":["0x4","latest",[25,50,75]]}' \
     https://mainnet.base.org
   ```
   Real response shape (Base, 2026-09-21): `baseFeePerGas: ["0x4c4b40", ...]` (= 5,000,000 wei = 0.005 gwei) and 25th-percentile reward `0xf4240` (= 1,000,000 wei = 0.001 gwei). Values are a snapshot — always re-read live; they move block to block.
2. Estimate the gas LIMIT with `eth_estimateGas` against the pending state, then add a small margin (default 10–20%) so edge-case execution doesn't run out of gas mid-call. The limit is units of gas, not money — do not confuse the two.
3. Set fees as a type-2 (EIP-1559) transaction: `maxFeePerGas` = recent base fee + 2x base fee headroom (default) + your chosen priority fee; `maxPriorityFeePerGas` = the 25th–50th percentile reward for non-urgent work, higher only when urgency demands. You pay the base fee actually in effect, never your cap — so a generous cap is safe, not expensive. The priority fee (tip) is the part you actually choose; that is where overpaying happens.
4. Match the fee to the urgency, not the habit. Non-urgent work (approvals, scheduled jobs, test sends) defaults to the cheap end of recent percentiles and can wait out a spike — read two or three `eth_feeHistory` samples over a few blocks; if the base fee is climbing, wait. Urgent work pays for the 75th+ percentile tip. Never copy the last transaction's fee blindly.
5. Batch independent calls when the chain supports it: multiple `approve`/`transfer`/contract calls in one transaction via a multicall-style aggregator contract saves the 21,000-gas base cost per extra transaction. VERIFY the aggregator's address on the chain's block explorer before use — I checked one address from memory against Base and it held no contract, so do not quote multicall addresses from memory. Only batch independent calls; a batch reverts as a unit, so a failing step kills the whole batch.
6. If a transaction is stuck pending: re-read the base fee. If the base fee moved above your `maxFeePerGas`, the transaction can never mine as priced — submit a replacement with the same nonce and strictly higher `maxFeePerGas` (relays typically require a bump on the order of ~10%; confirm the relay's policy before quoting a number). If the base fee is flat, just wait — resubmitting the same fee wastes effort and RPC quota.
7. Before broadcast, run the pre-flight from `wallet_safety` (checksum the destination, confirm the chain ID, test-send on a new destination where possible).

Save the fee snapshot helper below as `fee_check.py` and run it before quoting fees:

```python
"""Read live EIP-1559 fees. Usage: python3 fee_check.py https://mainnet.base.org
Uses curl under the hood: Base's public RPCs drop plain Python-urllib
requests but answer curl's User-Agent."""
import json, subprocess, sys

rpc = sys.argv[1] if len(sys.argv) > 1 else "https://mainnet.base.org"

def call(method, params):
    body = json.dumps({"jsonrpc": "2.0", "id": 1, "method": method,
                       "params": params})
    out = subprocess.run(
        ["curl", "-s", "-m", "30", "-A", "Mozilla/5.0",
         "-H", "Content-Type: application/json", "-d", body, rpc],
        capture_output=True, text=True, check=True).stdout
    return json.loads(out)["result"]

hist = call("eth_feeHistory", ["0x4", "latest", [25, 50, 75]])
base = int(hist["baseFeePerGas"][-1], 16)
p25 = int(hist["reward"][-1][0], 16)
p50 = int(hist["reward"][-1][1], 16)
p75 = int(hist["reward"][-1][2], 16)
tip = call("eth_maxPriorityFeePerGas", [])
print(f"base fee (gwei):            {base / 1e9:.6f}")
print(f"priority 25/50/75 (gwei):   {p25/1e9:.6f} / {p50/1e9:.6f} / {p75/1e9:.6f}")
print(f"node suggested tip (gwei):  {int(tip, 16) / 1e9:.6f}")
print(f"suggested maxFee non-urgent: {(base * 2 + p50) / 1e9:.6f} gwei")
```

Expected shape of the output (real run, Base, 2026-09-21 — yours will differ):
```
base fee (gwei):            0.005000
priority 25/50/75 (gwei):   0.000990 / 0.001000 / 0.001323
node suggested tip (gwei):  0.001000
suggested maxFee non-urgent: 0.011000 gwei
```
If any RPC method returns an error, that method is not supported by the endpoint — fall back to `eth_gasPrice` and quote legacy fees, or pick another RPC.

## Output Contract
The agent must return, for each transaction it prices:
- The live fee snapshot (base fee, chosen percentile, source block) it read.
- The urgency classification (urgent / non-urgent) and the reasoning in one line.
- The chosen `maxFeePerGas` and `maxPriorityFeePerGas`, or the legacy `gasPrice` if the chain/endpoint requires it.
- The gas limit from `eth_estimateGas` plus the margin applied.
- For stuck transactions: the re-read base fee vs the original cap, and what action was taken (wait / replace with new nonce-matching fee).

## Operating Rules
1. Always read the fee market live before quoting or setting a fee — never quote gwei figures from memory or from a past snapshot.
2. Default to type-2 (EIP-1559) transactions on chains that support them; fall back to legacy `gasPrice` only when the chain or RPC requires it.
3. Default non-urgent transactions to the cheap end of recent priority-fee percentiles; reserve high tips for genuinely urgent work.
4. Add a 10–20% margin on `eth_estimateGas` results by default, unless the call is a simple value transfer with a known cost.
5. Never invent contract addresses from memory — multicall aggregators, fee oracles, and permit contracts must be verified on the chain's block explorer first.
6. Replacement transactions keep the same nonce and raise `maxFeePerGas` strictly; never resubmit the identical fee and call it a fix.
7. Only batch independent calls; if one step can fail, keep it in its own transaction unless you have confirmed the whole batch can revert safely.
8. Treat every gwei number as time-sensitive: label snapshots with chain and read time, and mark anything older than a few minutes as stale.
9. Do not confuse units: gas limit is gas, fees are wei/gwei, cost is limit × fee. State which one every number refers to.
10. Public RPCs can serve stale state right after a block lands — if a fee read looks wrong, wait a few seconds and re-read on a second RPC before acting.
