Skip to content
Back to skills

Solidity Audit

ASecurity

Security audit and code review checklist. Covers 30+ vulnerability types with real-world exploit cases (2021-2026) and EVMbench Code4rena patterns. Use when conducting security audits, code reviews, or pre-deployment security assessments.

  • 65 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added October 9, 2026
developmentjavascriptrustgojavaawsfrontendsecurity

Works with

  • mcp

Security analysis

A100/100

Scanned October 9, 2026

npx -y skills add ComeOnOliver/skillshub --skill solidity-audit --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Solidity Audit?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Solidity Audit
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/comeonoliver-solidity-audit/badge)](https://www.skillsdirectory.com/skills/comeonoliver-solidity-audit)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

SKILL.md
---
name: solidity-audit
description: "Security audit and code review checklist. Covers 30+ vulnerability types with real-world exploit cases (2021-2026) and EVMbench Code4rena patterns. Use when conducting security audits, code reviews, or pre-deployment security assessments."
---

# Solidity Security Audit Checklist

## Language Rule

- **Always respond in the same language the user is using.** If the user asks in Chinese, respond in Chinese. If in English, respond in English.

> **Usage**: This skill is for security audits and code reviews. It is NOT auto-invoked — call `/solidity-audit` when reviewing contracts for vulnerabilities.

## Contract-Level Vulnerabilities

### 1. Reentrancy

| Variant | Description | Check |
|---------|-------------|-------|
| Same-function | Attacker re-enters the same function via fallback/receive | All external calls after state updates (CEI pattern)? |
| Cross-function | Attacker re-enters a different function sharing state | All functions touching shared state protected by `nonReentrant`? |
| Cross-contract | Attacker re-enters through a different contract that reads stale state | External contracts cannot read intermediate state? |
| Read-only | View function returns stale data during mid-execution state | No critical view functions used as oracle during state transitions? |

**Case**: [GMX v1 (Jul 2025, $42M)](https://www.halborn.com/blog/post/explained-the-gmx-hack-july-2025) — reentrancy in GLP pool on Arbitrum, attacker looped withdrawals to drain liquidity.

### 2. Access Control

| Check | Detail |
|-------|--------|
| Missing modifier | Every state-changing function has explicit access control? |
| Modifier logic | Modifier actually reverts on failure (not just empty check)? |
| State flag | Access-once patterns properly update storage after each user? |
| Admin privilege scope | Owner powers are minimal and time-limited? |

**Case**: [Bybit (Feb 2025, $1.4B)](https://www.halborn.com/blog/post/explained-the-bybit-hack-february-2025) — Safe{Wallet} UI injected with malicious JS, hijacked signing process. Not a contract flaw, but access control at the infrastructure layer.

### 3. Input Validation

| Check | Detail |
|-------|--------|
| Zero address | All address params reject `address(0)`? |
| Zero amount | Fund transfers reject zero amounts? |
| Array bounds | Paired arrays validated for matching length? |
| Arbitrary call | No unvalidated `address.call(data)` where attacker controls `data`? |
| Numeric bounds | Inputs bounded to prevent dust attacks or gas griefing? |

### 4. Flash Loan Attacks

| Variant | Mechanism | Defense |
|---------|-----------|---------|
| Price manipulation | Flash-borrow → swap to move price → exploit price-dependent logic → repay | TWAP oracle with min-liquidity check |
| Governance | Flash-borrow governance tokens → vote → repay in same block | Snapshot voting + minimum holding period + timelock ≥ 48h |
| Liquidation | Flash-borrow → manipulate collateral value → trigger liquidation | Multi-oracle price verification + circuit breaker |
| Combo (rounding) | Flash-borrow → manipulate pool → micro-withdrawals exploit rounding → repay | Minimum withdrawal amount + virtual shares |

**Cases**:
- [Cream Finance (Oct 2021, $130M)](https://rekt.news/cream-rekt-2/) — flash loan + yUSD oracle manipulation + missing reentrancy guard
- [Abracadabra (Mar 2025, $13M)](https://www.halborn.com/blog/post/explained-the-abracadabra-money-hack-march-2025) — state tracking error in cauldron, self-liquidation + bad loan
- [Bunni (Sep 2025, $8.4M)](https://www.theblock.co/post/369564/bunni-smart-contract-rounding-error) — flash loan + pool price manipulation + rounding error micro-withdrawals

### 5. Oracle & Price

| Check | Detail |
|-------|--------|
| Single oracle dependency | Using multiple independent price sources? |
| Stale price | Checking `updatedAt` timestamp and rejecting old data? |
| Spot price usage | Never using raw AMM reserves for pricing? |
| Minimum liquidity | Oracle reverts if pool reserves below threshold? |
| Price deviation | Circuit breaker if price moves beyond threshold vs last known? |
| Chainlink round completeness | Checking `answeredInRound >= roundId`? |

**Case**: [Cream Finance (Oct 2021, $130M)](https://rekt.news/cream-rekt-2/) — attacker manipulated yUSD vault price by reducing supply, then used inflated collateral to drain all lending pools.

### 6. Numerical Issues

| Type | Description | Defense |
|------|-------------|---------|
| Primitive overflow | `uint256 a = uint8(b) + 1` — reverts if b=255 on Solidity ≥0.8 | Use consistent types, avoid implicit narrowing |
| Truncation | `int8(int256Value)` — silently overflows even on ≥0.8 | Use `SafeCast` library for all type narrowing |
| Rounding / precision loss | `usdcAmount / 1e12` always rounds to 0 for small amounts | Multiply before divide; check for zero result |
| Division before multiplication | `(a / b) * c` loses precision | Always `(a * c) / b` |

**Case**: [Bunni (Sep 2025, $8.4M)](https://www.halborn.com/blog/post/explained-the-bunni-hack-september-2025) — rounding errors in micro-withdrawals exploited via flash loan.

### 7. Signature Issues

| Type | Description | Defense |
|------|-------------|---------|
| ecrecover returns address(0) | Invalid sig returns `address(0)`, not revert | Always check `recovered != address(0)` |
| Replay attack | Same signature reused across txs/chains | Include `chainId` + `nonce` + `deadline` in signed data |
| Signature malleability | ECDSA has two valid (s, v) pairs per signature | Use OpenZeppelin `ECDSA.recover` (enforces low-s) |
| Empty loop bypass | Signature verification in for-loop, attacker sends empty array | Check `signatures.length >= requiredCount` before loop |
| Missing msg.sender binding | Proof/signature not bound to caller | Always include `msg.sender` in signed/proven data |

### 8. ERC20 Compatibility

| Issue | Description | Defense |
|-------|-------------|---------|
| Fee-on-transfer | `transfer(100)` may deliver <100 tokens | Check balance before/after, use actual received amount |
| Rebase tokens | Token balances change without transfers | Never cache external balances; always read live |
| No bool return | Some tokens (USDT) don't return bool on transfer | Use `SafeERC20.safeTransfer` |
| ERC777 hooks | Transfer hooks can trigger reentrancy | Use `ReentrancyGuard` on all token-receiving functions |
| Zero-amount transfer | `transferFrom(A, B, 0)` — address poisoning | Reject zero-amount transfers |
| Approval race | Changing allowance from N to M allows spending N+M | Use `safeIncreaseAllowance` / `safeDecreaseAllowance` |

### 9. MEV / Front-Running

| Type | Description | Defense |
|------|-------------|---------|
| Sandwich attack | Attacker front-runs buy + back-runs sell around victim | Slippage protection + deadline parameter |
| ERC4626 inflation | First depositor donates to inflate share price, rounding out later depositors | Minimum first deposit or virtual shares (ERC4626 with offset) |
| Approval front-run | Attacker spends old allowance before new allowance tx confirms | Use `increaseAllowance` not `approve` |
| Unrestricted withdrawal | Attacker monitors mempool for withdraw tx, front-runs with own | Require commit-reveal or auth binding |

### 10. Storage & Low-Level

| Issue | Description |
|-------|-------------|
| Storage pointer | `Foo storage foo = arr[0]; foo = arr[1];` — does NOT update arr[0] |
| Nested delete | `delete structWithMapping` — inner mapping data persists |
| Private variables | All contract storage is publicly readable via `eth_getStorageAt` |
| Unsafe delegatecall | Delegatecall to untrusted contract can `selfdestruct` the caller |
| Proxy storage collision | Upgrade changes parent order → variables overwrite each other (use storage gaps) |
| msg.value in loop | msg.value doesn't decrease in loop — enables double-spend |

### 11. Contract Detection Bypass

| Method | How it works |
|--------|-------------|
| Constructor call | Attack from constructor — `extcodesize == 0` during deployment |
| CREATE2 pre-compute | Pre-calculate contract address, use as EOA before deploying |

### 12. Proxy & Upgrade Vulnerabilities

> Source: [EVMbench Paper §4.2, Appendix H](https://cdn.openai.com/evmbench/evmbench.pdf) / [Code4rena 2024-07-basin H-01](https://code4rena.com/reports/2024-07-basin)

| Check | Detail |
|-------|--------|
| `_authorizeUpgrade` access control | UUPS `_authorizeUpgrade` must have `onlyOwner` modifier? |
| Permissionless factory/registry | Can attacker use permissionless factory (e.g. Aquifer `boreWell`) to satisfy upgrade checks? |
| `upgradeTo` modifier | Overridden `upgradeTo`/`upgradeToAndCall` retains `onlyProxy` modifier? |
| Initializer protection | `initializer` modifier prevents re-initialization? Implementation calls `_disableInitializers()`? |
| Storage layout compatibility | Upgrade-safe storage layout (storage gaps or ERC-7201 namespace)? |

**Case**: [Code4rena 2024-07-basin H-01](https://code4rena.com/reports/2024-07-basin) (via [EVMbench Paper Fig.12, p.19](https://cdn.openai.com/evmbench/evmbench.pdf)) — `_authorizeUpgrade` only checked delegatecall and Aquifer registration but lacked `onlyOwner`, allowing anyone to upgrade a Well proxy to a malicious implementation and drain funds. Oracle patch: add a single `onlyOwner` modifier.

### 13. Trust Boundary & Protocol Composability

> Source: [EVMbench Paper §4.2.1, Fig.6](https://cdn.openai.com/evmbench/evmbench.pdf) / Code4rena [2024-04-noya H-08](https://code4rena.com/reports/2024-04-noya), [2024-07-benddao](https://code4rena.com/reports/2024-07-benddao)

| Check | Detail |
|-------|--------|
| Cross-vault trust isolation | Registry/Router relay calls verify vault-level authorization? |
| Trusted sender abuse | Functions like `sendTokensToTrustedAddress` verify source vault, not just router identity? |
| Flash loan + routing combo | Can attacker use flash loan callback to make router impersonate arbitrary vault? |
| Collateral ownership verification | Liquidation/staking operations verify actual NFT/collateral owner? |
| Cross-contract state dependency | Multi-contract interactions free from intermediate state dependencies? |

**Cases**:
- [Code4rena 2024-04-noya H-08](https://code4rena.com/reports/2024-04-noya) (via [EVMbench Paper §4.2.1, Fig.6, p.8-9](https://cdn.openai.com/evmbench/evmbench.pdf)) — PositionRegistry + BalancerFlashLoan pipeline lacked vault-level auth; keeper used flash loan to make router impersonate any vault, draining cross-vault funds via `sendTokensToTrustedAddress`
- [Code4rena 2024-07-benddao](https://code4rena.com/reports/2024-07-benddao) (via [EVMbench Paper Fig.13, p.19](https://cdn.openai.com/evmbench/evmbench.pdf)) — `isolateLiquidate` did not verify NFT ownership, allowing attacker to pass others' tokenIds for liquidation

### 14. State Ordering & Counter Manipulation

> Source: [EVMbench Paper Appendix H.1, Fig.19-21](https://cdn.openai.com/evmbench/evmbench.pdf) / [Code4rena 2024-08-phi H-06](https://code4rena.com/reports/2024-08-phi)

| Check | Detail |
|-------|--------|
| Counter/ID increment order | `credIdCounter++` or similar ID increments happen before external calls? |
| Auto-buy in create | `create()` functions with auto `buy()` calls execute only after ID/state fully initialized? |
| Refund timing | ETH refund (excess) happens after all state updates complete? |
| Bonding curve metadata overwrite | Can attacker reenter to modify bonding curve/pricing params — buy cheap, switch to expensive curve, sell high? |

**Case**: [Code4rena 2024-08-phi H-06](https://code4rena.com/reports/2024-08-phi) (via [EVMbench Paper Appendix H.1, p.25-28](https://cdn.openai.com/evmbench/evmbench.pdf)) — `_createCredInternal` called `buyShareCred` before incrementing `credIdCounter`; `_handleTrade` refunded excess ETH before updating `lastTradeTimestamp`. Attacker reentered to accumulate shares on cheap curve, overwrote metadata to expensive curve, sold to drain all contract ETH. Fix: add `nonReentrant` to `buyShareCred`/`sellShareCred`.

## Infrastructure-Level Vulnerabilities

### 15. Frontend / UI Injection

Attackers inject malicious code into the dApp frontend or signing interface.

**Defense**: Verify transaction calldata matches expected function selector and parameters before signing. Use hardware wallet with on-device transaction preview. Audit all frontend dependencies regularly.

**Case**: [Bybit (Feb 2025, $1.4B)](https://www.nccgroup.com/research-blog/in-depth-technical-analysis-of-the-bybit-hack/) — malicious JavaScript injected into Safe{Wallet} UI, tampered with transaction data during signing.

### 16. Private Key & Social Engineering

Compromised keys remain the #1 loss source in 2025-2026.

**Defense**: Store keys in HSM or hardware wallet. Use multisig (≥ 3/5) for all treasury and admin operations. Never share seed phrases with any "support" contact. Conduct regular social engineering awareness training.

**Case**: [Step Finance (Jan 2026, $30M)](https://www.halborn.com/blog/post/explained-the-step-finance-hack-january-2026) — treasury wallet private keys compromised via device breach.

### 17. Cross-Chain Bridge

| Check | Detail |
|-------|--------|
| Inherited code | Audit all bridge logic inherited from third-party frameworks |
| Message verification | Cross-chain messages validated with proper signatures and replay protection? |
| Liquidity isolation | Bridge funds separated from protocol treasury? |

**Case**: [SagaEVM (Jan 2026, $7M)](https://www.theblock.co/post/386638/sagaevm-suffers-exploit) — inherited vulnerable EVM precompile bridge logic from Ethermint.

### 18. Legacy / Deprecated Contracts

Old contracts with known bugs remain callable on-chain forever.

**Defense**: Permanently `pause` or migrate funds from deprecated contracts. Monitor old contract addresses for unexpected activity. Remove mint/admin functions before deprecation.

**Case**: [Truebit (Jan 2026, $26.4M)](https://www.coindesk.com/markets/2026/01/09/truebit-token-tru-crashes-99-9-after-usd26-6m-exploit-drains-8-535-eth) — Solidity 0.6.10 contract lacked overflow protection, attacker minted tokens at near-zero cost.

## Automated Analysis with Slither MCP (if available)

When `slither` MCP is configured, run automated analysis BEFORE the manual checklist below:

### Recommended Audit Flow

```
Step 1: slither MCP automated scan
        → get_detector_results(path, impact="High")
        → get_detector_results(path, impact="Medium")
Step 2: Review Slither findings — triage true positives vs false positives
Step 3: Manual checklist below — catch what Slither misses (business logic, economic attacks)
Step 4: Cross-reference — Slither + manual findings combined into final report
```

### Slither MCP Tools

| Tool | Usage | Complements |
|---|---|---|
| `get_contract_metadata` | Extract functions, inheritance, flags | Manual access control review |
| `get_function_source` | Get exact source code with line numbers | Faster than grep for locating code |
| `find_implementations` | Find all implementations of a function signature | Cross-contract reentrancy analysis |
| `get_detector_results` | Run 90+ security detectors, filter by impact/confidence | Automated version of manual checklist |
| `get_detector_metadata` | List available detectors with descriptions | Understanding what's being checked |

### What Slither Catches vs What It Misses

| Slither Catches Well | Manual Review Still Needed |
|---|---|
| Reentrancy patterns | Business logic flaws |
| Unprotected functions | Economic attack vectors (flash loan combos) |
| Unused state variables | Cross-protocol composability risks |
| Shadowing issues | Oracle manipulation scenarios |
| Incorrect ERC20 interface | Trust boundary architecture issues |
| Dead code | MEV/front-running specific to business logic |

**Key Principle**: Slither provides ground truth via static analysis — reduces false negatives on known vulnerability patterns. But it cannot reason about protocol-level economic attacks — that's where the manual checklist below is essential.

**Graceful degradation**: If slither MCP is not configured, skip this section and proceed directly to the manual checklist. All checklist items remain valid and self-contained.

## Audit Execution Checklist

When conducting a security audit, check each item:

**Reentrancy:**
- [ ] All functions with external calls use `nonReentrant`
- [ ] CEI pattern followed — no state reads after external calls
- [ ] View functions not used as oracle during state transitions

**Access Control:**
- [ ] Every state-changing function has explicit access modifier
- [ ] Modifiers actually revert (not silently pass)
- [ ] Admin privileges are minimal and documented

**Input & Logic:**
- [ ] No unvalidated arbitrary `call` / `delegatecall`
- [ ] No `tx.origin` for authentication
- [ ] Array lengths validated for paired inputs
- [ ] No division-before-multiplication precision loss

**Token Handling:**
- [ ] All ERC20 ops use `SafeERC20`
- [ ] Fee-on-transfer tokens handled (balance diff check)
- [ ] Rebase token balances not cached
- [ ] Zero-amount transfers rejected

**Price & Oracle:**
- [ ] No raw spot price usage
- [ ] Stale price check (`updatedAt` / `answeredInRound`)
- [ ] Minimum liquidity threshold enforced
- [ ] Price deviation circuit breaker

**Signature & Crypto:**
- [ ] `ecrecover` result checked against `address(0)`
- [ ] Signed data includes `chainId`, `nonce`, `msg.sender`, `deadline`
- [ ] Using OZ `ECDSA` (low-s enforced)
- [ ] MerkleProof leaves bound to `msg.sender`

**Flash Loan Defense:**
- [ ] Governance: snapshot voting + holding period + timelock
- [ ] Price: TWAP or multi-oracle, not single-block spot
- [ ] Vault: minimum first deposit or virtual shares (ERC4626)

**Proxy & Upgrade ([EVMbench](https://cdn.openai.com/evmbench/evmbench.pdf)):**
- [ ] UUPS `_authorizeUpgrade` has `onlyOwner` — [EVMbench/basin H-01]
- [ ] `upgradeTo`/`upgradeToAndCall` retains `onlyProxy` — [EVMbench/basin H-01]
- [ ] Implementation constructor calls `_disableInitializers()` — [EVMbench/basin H-01]
- [ ] Storage layout upgrade-compatible (storage gaps or ERC-7201) — [EVMbench/basin H-01]

**Trust Boundary & Composability ([EVMbench](https://cdn.openai.com/evmbench/evmbench.pdf)):**
- [ ] Router/Registry relay calls verify source vault/contract authorization — [EVMbench/noya H-08]
- [ ] Liquidation operations verify actual collateral ownership — [EVMbench/benddao]
- [ ] Flash loan callback paths cannot be abused to penetrate trust boundaries — [EVMbench/noya H-08]
- [ ] No intermediate state dependencies in multi-contract interactions — [EVMbench/noya H-08]

**State Ordering ([EVMbench](https://cdn.openai.com/evmbench/evmbench.pdf)):**
- [ ] Counter/ID increments complete before external calls — [EVMbench/phi H-06]
- [ ] ETH refunds execute after all state updates — [EVMbench/phi H-06]
- [ ] Auto-operations in create functions (auto-buy etc.) execute after full initialization — [EVMbench/phi H-06]

**Infrastructure:**
- [ ] Third-party dependencies audited (bridge code, inherited contracts)
- [ ] No deprecated contracts still callable with admin/mint functions
- [ ] Multisig on all treasury and admin wallets
- [ ] Frontend transaction verification (calldata matches expected)

## AI Agent Audit Methodology

> Source: [EVMbench (OpenAI/Paradigm, Feb 2026)](https://cdn.openai.com/evmbench/evmbench.pdf) — evaluated AI agents on 120 high-severity vulnerabilities from 40 Code4rena audit repositories across Detect/Patch/Exploit modes.

### Audit Strategy

1. **Coverage over depth**: scan ALL in-scope files; do not stop after finding the first vulnerability [EVMbench §5, p.10]
2. **Three-phase audit**: Detect (identify vulnerabilities) -> Patch (write fix) -> Exploit (build PoC) [EVMbench §3.2, p.5]
3. **Incremental output**: write findings continuously during audit to preserve progress [EVMbench Appendix G, Fig.18, p.24]
4. **Systematic category scan**: check by vulnerability class (reentrancy, access control, numerical, oracle...) rather than intuition [EVMbench §3.1, p.4]
5. **Verify fixes**: after patching, confirm original tests still pass AND exploit is no longer viable [EVMbench §3.2.2, p.5]

### High-Frequency Vulnerability Patterns (Code4rena Data)

> Source: EVMbench Table 4 (p.17) — 40 audit repositories

- Missing access control (upgradeability, liquidation, admin functions) — basin H-01, munchables, benddao
- Reentrancy + state ordering errors (refund before state update) — phi H-06, noya H-08
- Flash loan trust boundary penetration (exploiting router/registry trust propagation) — noya H-08
- Signature replay / front-running (checkpoint bypass, session signature replay) — sequence H-01, H-02
- Numerical precision / rounding (bonding curve, micro-withdrawals) — abracadabra H-02, size H-02

### Key Findings

> Source: EVMbench Paper §4.1 (p.7), Fig.7 (p.10), Fig.10 (p.18), Fig.11 (p.19)

- With mechanism hints, Patch success rate jumps from ~40% to ~94% [Fig.7] — agents know how to fix but struggle to find vulnerabilities
- Most vulnerabilities require ≤5 lines of code to fix [Fig.10, p.18]
- Most exploits require only 1-3 transactions [Fig.11, p.19]
- Agents whose finding count is closest to actual vulnerability count score highest (quality > quantity) [Fig.5, p.8]

## 2021-2026 Incident Quick Reference

| Date | Project | Loss | Attack Type | Root Cause | Source |
|------|---------|------|-------------|------------|--------|
| Oct 2021 | Cream Finance | $130M | Flash loan + oracle | yUSD vault price manipulation via supply reduction | [rekt.news](https://rekt.news/cream-rekt-2/) |
| Feb 2025 | Bybit | $1.4B | UI injection / supply chain | Safe{Wallet} JS tampered via compromised dev machine | [NCC Group](https://www.nccgroup.com/research-blog/in-depth-technical-analysis-of-the-bybit-hack/) |
| Mar 2025 | Abracadabra | $13M | Logic flaw | State tracking error in cauldron liquidation | [Halborn](https://www.halborn.com/blog/post/explained-the-abracadabra-money-hack-march-2025) |
| Jul 2025 | GMX v1 | $42M | Reentrancy | GLP pool cross-contract reentrancy on Arbitrum | [Halborn](https://www.halborn.com/blog/post/explained-the-gmx-hack-july-2025) |
| Sep 2025 | Bunni | $8.4M | Flash loan + rounding | Rounding direction error in withdraw, 44 micro-withdrawals | [The Block](https://www.theblock.co/post/369564/bunni-smart-contract-rounding-error) |
| Oct 2025 | Abracadabra #2 | $1.8M | Logic flaw | cook() validation flag reset, uncollateralized MIM borrow | [Halborn](https://www.halborn.com/blog/post/explained-the-abracadabra-hack-october-2025) |
| Jan 2026 | Step Finance | $30M | Key compromise | Treasury wallet private keys stolen via device breach | [Halborn](https://www.halborn.com/blog/post/explained-the-step-finance-hack-january-2026) |
| Jan 2026 | Truebit | $26.4M | Legacy contract | Solidity 0.6.10 integer overflow in mint pricing | [CoinDesk](https://www.coindesk.com/markets/2026/01/09/truebit-token-tru-crashes-99-9-after-usd26-6m-exploit-drains-8-535-eth) |
| Jan 2026 | SagaEVM | $7M | Supply chain / bridge | Inherited Ethermint precompile bridge vulnerability | [The Block](https://www.theblock.co/post/386638/sagaevm-suffers-exploit) |

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.