Skip to content
Back to skills

Indexedex Testing

ASecurity

Write or review IndexedEx production-first Foundry tests, TestBases, registry deployment and SE/DETF coverage.

  • 9 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added September 10, 2026
businesspythonrustgotesting

Security analysis

A100/100

Scanned September 22, 2026

npx -y skills add cyotee/indexedex --skill indexedex-testing --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Indexedex Testing?

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

Security grade badge for Indexedex Testing
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/cyotee-indexedex-testing/badge)](https://www.skillsdirectory.com/skills/cyotee-indexedex-testing)

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

Download with Pro
SKILL.md
---
name: indexedex-testing
description: "Write or review IndexedEx production-first Foundry tests, TestBases, registry deployment and SE/DETF coverage."
license: MIT
---

# IndexedEx Testing Patterns

IndexedEx layers a **vault registry + manager** on Crane. Tests must use the same deploy paths as production and **prefer production code over mocks**.

**Read first:**

1. `lib/crane/AGENTS.md` and **canonical** `lib/crane/.claude/skills/crane-testing/` (production-first + LR-7).
2. `lib/crane/.claude/skills/crane-deployment/` (CREATE3, DFPkg, factories).
3. This skill + root `CLAUDE.md` then [`docs/agent/INDEXEDEX_AGENT_LAW.md`](../../../docs/agent/INDEXEDEX_AGENT_LAW.md) (DETF roles, **token policy**). **No** root `AGENTS.md`.

Generic Foundry skills (`forge-testing` mock sections) are **subordinate** to Crane + this skill.

## `forge build` before `forge test` (LOCKED)

IndexedEx FactoryServices load creation bytecode directly from `out/` artifact JSON via `ArtifactCreationCode`. The factory-aware overload recursively deploys and links external libraries. Keep Facet/DFPkg implementation imports and artifact-seed inheritance out of deployment helpers; import standalone interfaces instead. Editing an implementation then does not invalidate helpers that reference it only by artifact ID.

After any production contract change, run **`forge build` then `forge test`** (same for `forge script`). `forge test` alone can CREATE3-deploy stale `out/` bytecode. Full text: root `CLAUDE.md` item 10 and agent law § FactoryService creation bytecode.

For incremental work, use `python3 scripts/forge-artifacts.py test contracts/path/EditedTarget.sol --test-root test/foundry/spec/path/RelevantTest.t.sol -- -vv`. It refreshes concrete descendants and runtime artifacts before selecting test roots with `--skip`. Repeat `--test-root` for multiple suites. Keep configured project paths stable: changing `FOUNDRY_TEST` invalidates the shared cache. See [artifact build workflow](../../../docs/testing/ARTIFACT_BUILDS.md).

## Production-first (IndexedEx)

### Ladder

1. Deploy **real** facets via `create3Factory` + `*FactoryService` / `*_Component_FactoryService`.
2. Deploy **core** packages (manager, fee collector) like `IndexedexTest` (Crane path).
3. Deploy **vault / Standard Exchange / DETF** DFPkgs via **`indexedexManager.deploy*DFPkg(...)`** (registry path), then `deployVault` / manager `deployVault`.
4. Inherit existing TestBases before inventing setup.
5. External protocols: Crane protocol ports (hermetic) or `test/foundry/fork/**` (live). Do not invent DEX/lending mocks when a TestBase exists.
6. Non-SUT doubles only: mintable ERC20, reentrancy token, etc.
7. `vm.mockCall` / `Mock*` only as last resort for **non-SUT** isolation — **never** for facets, DFPkgs, vaults, manager, fee oracle, or vault registry under test.

**Prefer real Standard Exchange vaults over `MockStandardExchange` for new work.**

### Forbidden

```solidity
// WRONG — mock SUT
MockStandardExchange se = new MockStandardExchange(...);
vm.mockCall(address(vault), ..., ...);

// WRONG — bypass factories / registry
SomeFacet f = new SomeFacet();
SomeDFPkg p = new SomeDFPkg(init);
diamondPackageFactory.deploy(IDiamondFactoryPackage(vaultPkg), args); // registered vault DFPkg
```

```solidity
// RIGHT — factory + registry path (see gold TestBases)
camelotV2InFacet = create3Factory.deployCamelotV2StandardExchangeInFacet();
vm.prank(owner);
pkg = indexedexManager.deployCamelotV2StandardExchangeDFPkg(pkgInit);
vault = pkg.deployVault(asset); // or indexedexManager.deployVault(...)
```

## Inheritance chain

```
CraneTest                         # create3Factory + diamondPackageFactory (InitDevService)
  └── IndexedexTest               # fee collector + indexedexManager + operator wiring
        └── TestBase_VaultComponents   # shared vault ERC20/ERC4626/multi-asset facets
              └── TestBase_*StandardExchange / protocol base
                    └── YourSpec.t.sol
```

Fork tests often combine `IndexedexTest` / vault components with `TestBase_*Fork` and network constants (e.g. Base main).

**Always call parent `setUp()` in the correct override order** (explicit parent names when multiple bases).

## Gold TestBases (copy these patterns)

| Base | Path | Notes |
|------|------|--------|
| Core stack | `contracts/test/IndexedexTest.sol` | Manager + fee collector via factories |
| Vault facets | `contracts/vaults/TestBase_VaultComponents.sol` | Shared vault components |
| Camelot SE | `contracts/protocols/dexes/camelot/v2/TestBase_CamelotV2StandardExchange.sol` | In/Out facets + `deployCamelotV2StandardExchangeDFPkg` |
| Aave Stata SE | `contracts/test/bases/TestBase_AaveV3StataStandardExchange.sol` | Registry path for lending SE |
| Aerodrome SE | `contracts/protocols/dexes/aerodrome/v1/TestBase_AerodromeStandardExchange.sol` | Same pattern as Camelot |
| Dual-liquidity (fork) | `test/foundry/fork/base_main/vaults/detf/protocols/dexes/balancer/v3/uniswap/v4/crossVersion/v2/TestBase_DualLiquidityLinkedCrossVersionUniswapVault.sol` | Full production deploy on Base fork |

## Two deploy paths (critical)

### 1. Pure Crane (facets + non-vault packages)

- Facets: `create3Factory.deploy*Facet()` via FactoryService.
- Generic DFPkgs: `create3Factory.deployPackageWithArgs(...)`.
- Instances: `diamondPackageFactory.deploy(pkg, args)` or package helper.

Used in `IndexedexTest` for fee collector + manager package construction.

### 2. IndexedEx vault package path

- Facets still via `create3Factory`.
- **DFPkg must** go through manager:

```solidity
vm.prank(owner);
myVaultDFPkg = indexedexManager.deployCamelotV2StandardExchangeDFPkg(pkgInit);
// deployAaveV3Stata..., deployUniswapV4..., DualLiquidity Component_FactoryService, etc.
```

- Instance:

```solidity
// Often:
vault = myVaultDFPkg.deployVault(...);
// Or:
vault = indexedexManager.deployVault(IStandardVaultPkg(address(pkg)), abi.encode(pkgArgs));
```

Why: registry discovery (`vaultsOfToken` / `vaultsOfType`), authorization, fee oracle + manager wiring.

## Directory layout

```
contracts/                 # Production + TestBase_* next to features
contracts/test/            # IndexedexTest, shared bases, rare harness stubs
test/foundry/spec/         # Hermetic / unit / integration / invariant / comparative
test/foundry/fork/         # Live-network forks (base_main, eth_main, ...)
lib/crane/                 # Crane (canonical crane-* skills under lib/crane/.claude/skills/)
```

## Before writing a mock

1. Search for `TestBase_*` and `*_FactoryService` for the feature.
2. Check gold bases in the table above.
3. If only need a token: use mintable ERC20 / `ERC20PermitMintableStub`, not a mock vault.
4. If only need a failure mode: reentrancy token or controlled caller — keep SUT real.
5. Only then consider `vm.mockCall` on a **non-SUT** dependency, and document why.

## Spec vs fork

| Mode | When | How |
|------|------|-----|
| **Hermetic / spec** | Fast unit + integration without RPC | Crane protocol ports + factories + IndexedexTest |
| **Fork** | Live pools, mainnet state, parity | `vm.createSelectFork` + network constants + same factory/registry path for *IndexedEx* deploys |

Do not mix live addresses with hermetic protocol ports in one base without an explicit mode.

## Assertions & Behaviors

- Prefer exact `assertEq` / deltas (Crane LR-7), not “something changed”.
- Facet/package declaration: use Crane `Behavior_*` + control virtuals where applicable.
- Preview/execute parity for vault exchange paths when the feature has preview functions.

## Anti-pattern checklist

- [ ] No `new` for IndexedEx/Crane facets or vault DFPkgs under test
- [ ] Vault DFPkg via `indexedexManager.deploy*DFPkg` (or Component_FactoryService registry helper), not raw diamond factory alone
- [ ] No mock of manager / registry / vault / SE package under test
- [ ] Parent `setUp()` called; factories non-zero before deploy
- [ ] PkgInit uses real facet addresses (never `address(0)`)
- [ ] `PkgInit` / `PkgArgs` defined on the **interface**, not the contract (Crane rule)
- [ ] **Facet surface:** `controlFacetFuncs` from Target/product interface; every product selector on live proxy after registry deploy
- [ ] **Trust flags:** negative tests for `pretransferred=true` without transfer (vault already funded) — not only happy path
- [ ] Inbound credit uses measured **delta**, not absolute balance + claimed amount
- [ ] Token policy (do not re-ask): FoT forbidden; rebasing **underlyings** forbidden (`rebasingClaimToken` is a protocol product); non-18 decimals allowed (scale to 18); pause/blacklist accepted; no `PkgArgs` allowlist
- [ ] `--match-test` prefixes unique enough (or `--match-contract` the suite); do not treat colliding extras as this change
- [ ] After production contract edits: `forge build` then `forge test` (FactoryService reads `out/`; tests can deploy stale bytecode)

## Token policy (LOCKED)

Universal. Full text: agent law § Token policy. Do not invent FoT support, a rebase underlying, or a `processArgs` allowlist.

## Mandatory negative paths (SE / vault / DETF)

When implementing or reviewing tests for any path that mints shares or credits deposits:

| Case | Assert |
|------|--------|
| `pretransferred=true`, no tokens sent, vault holds inventory | Revert **or** zero shares minted; attacker product balance unchanged |
| `pretransferred=true`, short delivery | Exact transfer-not-received / insufficient selector |
| Donation then deposit | No free mint from donation (or documented beneficiary + no victim loss) |
| Fat `max` + transfer only `used` + booked `R` | Attacker does not receive booked inventory (E6) |
| `setVaultAddressDisabled(true)` then mature close / redeem / `exchangeOut` | Still succeeds (CROPS; inbound may stay gated) |
| Product fn only on Target/Facet impl | Must also succeed on **deployed vault/DETF proxy** |

Happy-path `pretransferred=true` with a real prior `transfer` does **not** cover free-mint from reserves.

Reference: `docs/NEGATIVE_TEST_COVERAGE_REPORT.md`, Crane adversarial **I/J/K**, ship gate `lib/crane/.claude/skills/crane-adversarial-testing/references/implementation-test-dod.md`.

## Related skills

- `crane-testing`, `crane-deployment`, `crane-architecture`, `crane-adversarial-testing` — **canonical** under `lib/crane/.claude/skills/`
- `indexedex-adversarial-testing` — DETF/SE abuse suites + I/J/K extensions
- `indexedex-uniswap-v4-hook-packages` — V4 hook DFPkgs, `deployHookVault`, flag mining (not monomorph CREATE3 hooks)
- `forge-testing` — Foundry cheatcodes only; ignore its mock-first examples for IndexedEx
- `permit2-*` — signature flows; still use real router + Permit2, not mocks

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…