# Vaultfire V3 — P0 Findings (PR1 revised)

**Prepared:** 2026-08-20
**Repo HEAD (V2 canonical):** `a80144d1acbbbbf24b9eca7cd91e61daad0c0ad6` on `main` at `gitlawb://z6MkryiNsYdFEMHv95wxzSQ1vtFXPyVQQrxXdPw3tE5HpfxV/vaultfire`
**Live registry snapshot:** 134 contract instances (36 unique names × 4 EVM chains) + Solana x402/read surface
**Prior audit rounds incorporated:** V3.2 (2026-01-16), V3.3-R2 (2026-03-04), V3.3-R3 Coinbase-grade (2026-03-27), V3.3-R3 Final (2026-04-16)

This document supersedes the initial `P0_FINDINGS.md` written before the canonical repo was accessible. It reconciles the reality on chain today against the audited state and against the assignment invariants. It records what is P0 for a partner pilot as of now.

> Nothing in this document has been executed. No keys are rotated, no funds moved, no contracts deployed. Every remediation below requires the deployer's explicit owner-approved action.

---

## Severity legend

- **P0-CRITICAL** — funds-at-risk or admin-takeover-at-risk right now. Must be resolved (or an accepted mitigation documented) before any partner pilot.
- **P0-BLOCKER** — prevents V3 deployment or a required feature from working at all.
- **P0-DOC** — deployed reality contradicts a public claim; must be corrected in public copy before partners integrate.

---

## P0-1 · STALE `owner()` METADATA ON 16 CONTRACTS (P0-DOC — safe by construction)

**Status:** OPEN as a documentation and public-metadata cleanup item. **NOT a live admin-takeover risk.**

**Correction:** An earlier draft of this document classified this finding as P0-CRITICAL. That classification was wrong. On-chain bytecode analysis confirms the operator's prior finding: these 16 contracts expose `owner()` as a read-only view but have no owner-gated state-changing functions in their deployed bytecode. The compromised key cannot use its `owner()` label to do anything on chain.

**What is true (verified 2026-08-20 via `eth_getCode` on all 4 chains):**

Wallet `0xA054f831B562e729F8D268291EBde1B2EDcFb84F` (`bpow20.cb.id`) is the recorded `owner()` value for 16 deployed contracts. This wallet is recorded in the operator's own notes as compromised and was superseded by `ghostkey.base.eth` (`0xfA15Ee28939B222B0448261A22156070f0A7813C`) as the canonical deployer wallet. Because these 16 contracts are **effectively immutable by bytecode** (no admin selectors present), `transferOwnership` was not necessary — there is nothing owner-gated to protect.

**Selector scan (all 16 SAFE):** See `baseline/compromised_owner_selector_scan.json`. Every contract was checked for these dangerous selectors — **none present on any of them:**

- `transferOwnership(address)` `0xf2fde38b` — absent everywhere
- `renounceOwnership()` `0x715018a6` — absent
- `grantRole(bytes32,address)` `0x2f2ff15d` — absent
- `revokeRole(bytes32,address)` `0xd547741f` — absent
- `pause()` `0x8456cb59` — absent
- `unpause()` `0x3f4ba83a` — absent
- `upgradeTo(address)` `0x3659cfe6` — absent
- `upgradeToAndCall(address,bytes)` `0x4f1ef286` — absent

The only `owner`-related selector present is `owner()` `0x8da5cb5b` as a pure getter. This is common in older OZ patterns where a contract stored `owner` at construction but exposed no privileged functions — the field is metadata, not authority.

**The 16 contracts (all confirmed SAFE by bytecode):**

| Chain | Name | Address | Verified safe |
|---|---|---|---|
| base | ERC8004ReputationRegistry | `0x98afd1440B2238D73c1394720277a6d031fCbbD0` | ✓ |
| base | VaultfireDisputeResolution | `0x3a681b6eF98c6d35643Ca631eE50EF35edd6BcBD` | ✓ |
| avalanche | ERC8004ReputationRegistry | `0x5DB1B4b412b80d03819395794fCcF5A73BF30656` | ✓ |
| avalanche | MissionEnforcement | `0xA4F4EDd4ed280Bd784650c048190fBA16e0AFd11` | ✓ |
| avalanche | VaultfireBondInsurancePool | `0xf7B2dC73Ec69528F171F4a0ded62da557866BaA5` | ✓ |
| avalanche | VaultfireCapabilityCredentials | `0x6750D28865434344e04e1D0a6044394b726C3dfE` | ✓ |
| avalanche | VaultfireDisputeResolution | `0x98afd1440B2238D73c1394720277a6d031fCbbD0` | ✓ |
| avalanche | VaultfireReputationStaking | `0x32211773C8266774A704aeD471f8eF073f34bA79` | ✓ |
| arbitrum | ERC8004ReputationRegistry | `0x8B8Ba34F8AAB800F0Ba8391fb1388c6EFb911F92` | ✓ |
| arbitrum | VaultfireCapabilityCredentials | `0x472dF1dD6D8218D0BF748e910E32861dAb88EDA6` | ✓ |
| arbitrum | VaultfireReputationStaking | `0x9d4AbeAa8201e6f1649c296dd179Bbb3c401f96F` | ✓ |
| polygon | ERC8004ReputationRegistry | `0x54e00081978eE2C8d9Ada8e9975B0Bb543D06A55` | ✓ |
| polygon | VaultfireBondInsurancePool | `0x9CF1fcba0c657e9FD807a9c8c028E0ef561b6B56` | ✓ |
| polygon | VaultfireCapabilityCredentials | `0x1762e484A48558Ea5Cd056ac2CDf43FBbA52ba58` | ✓ |
| polygon | VaultfireDisputeResolution | `0x5F7680a9a720c03812532A735D1165D9f97c0Cd1` | ✓ |
| polygon | VaultfireReputationStaking | `0x3ceA2B0853B76Ac75603C1fa2A29D96A4467Bb79` | ✓ |

**One follow-up sanity check to keep on the list (P1, not P0):**

`VaultfireBondInsurancePool` on Avalanche and Polygon should still be verified for any deposit/withdraw/callable paths that route funds — the fact that they don't have admin selectors doesn't automatically mean they don't have callable, non-admin fund flows. `MissionEnforcement` on Avalanche similarly. This is a P1 audit item, not a P0 admin-key risk. Native balances on these are already tracked in `ASSET_AND_LIABILITY_LEDGER.md` (all report 0 native at snapshot).

**Remediation (documentation-only, no owner action needed):**

1. In `deployments.json`, add an `owner_note` field to these 16 entries: `"owner value is a legacy stored reference; contract has no admin-gated functions; verified via bytecode selector scan 2026-08-20"`.
2. On `/api/contracts`, `/api/hub/stats`, and any explorer view that surfaces `owner`, either hide the field for these 16 or annotate it with the same note. Currently the raw address is displayed with no context — a partner viewing this would reasonably (and incorrectly) treat it as an active admin.
3. Add a note to the public docs: "Legacy V2 contracts using a deprecated wallet address as an inert `owner()` value; see [audit note]."
4. **Do not** try to `transferOwnership` on these — there is no `transferOwnership` function. Any attempt will simply revert.

Evidence: `baseline/compromised_owner_selector_scan.json`.

---

## P0-2 · TWO STAR CONTRACTS EXCEED 24KB (P0-BLOCKER for V3 deploy)

**Status:** OPEN. Verified in R3 audit (2026-03-27) and unchanged in latest V3 draft.

- `AIAccountabilityBondsV3.sol`: 28,767 bytes (limit 24,576) → +4,191 bytes over
- `AIPartnershipBondsV3.sol`: 26,546 bytes (limit 24,576) → +1,970 bytes over

Both will fail to deploy on Base, Avalanche C-Chain, Arbitrum, and Polygon under EIP-170. Local tests pass only because `allowUnlimitedContractSize: true` is set in `hardhat.config.js`.

**Remediation options (from R3 audit, still valid):**
1. Enable `--via-ir` + optimizer `runs: 1` in Hardhat.
2. Extract `BondMathLib` (`globalFlourishingScore`, `calculateBondValue`, `shouldLockProfits`) as a library.
3. Extract paginated view helpers to `BondViewHelpersV3`.
4. Consider UUPS proxy only if immutability is not a stated requirement — the assignment prefers immutable, so lean on 1–3.

---

## P0-3 · ZK MODE PERMANENTLY BROKEN (P0-BLOCKER for ZK feature)

**Status:** OPEN. R3 CRIT-R3-01. `DilithiumAttestorV3.verifyZKProof` at line 166 uses `block.timestamp` as `publicInputs[5]`. STARK proofs are generated off-chain at time T₀ but verified at T₁ > T₀, so the journal digest never matches. Every real proof reverts.

Was introduced by the CRIT-07 fix (public inputs array size 5 → 6).

**Remediation:** Accept the timestamp as a prover-supplied parameter from the proof bundle. Add a `MAX_PROOF_AGE` window (e.g., 15 min) for replay protection.

**Acceptable interim:** Ship V3 with `zkEnabled = false` on all chains and label ZK mode "unavailable" in `/api/v3/status`. Do not claim ZK works.

---

## P0-4 · PARTNERSHIP BOND `remainingStake` DOUBLE-COUNT (P0-CRITICAL solvency)

**Status:** OPEN. R3 HIGH-R3-02.

`AIPartnershipBondsV3.distributeBond` (lines 495–527) credits `remainingStake[bondId] += absAmount` (which includes `fundShare`) AND `partnershipFundNative += fundShare`. Both claim the same physical ETH. If the owner withdraws the partnership fund via its intended path, `remainingStake` claims can no longer be fully honored → protocol insolvency at the bond level.

**Assignment invariant hit:** #1 (assets cover recorded liabilities), #6 (total payout never exceeds collateral plus reserved rewards).

**Remediation:** Credit only actually transferred shares: `remainingStake[bondId] += (humanShare + aiShare)` instead of `+= absAmount`. This is a one-line accounting fix; must be paired with fuzz invariant `sum(remainingStake) + partnershipFundNative == contract balance - protocolFundsNative - challenge stakes - pending payouts`.

---

## P0-5 · ORACLE REENTRANCY IN `requestDistribution` (P0-HIGH)

**Status:** OPEN. R3 HIGH-R3-01.

`AIAccountabilityBondsV3.requestDistribution` (line 678) is missing `nonReentrant`. A malicious oracle can reenter during external `flourishingOracle.isMetricFresh` / `getMetricTWAP` calls (the `distributionPending` guard is still `false` at that point) and double-credit `totalPendingDistributions` — a permanent griefable DoS on all bonds in the native yield pool.

**Assignment invariant hit:** #1 (accounting), #7 (governance cannot withdraw pending liabilities — pending liabilities themselves cannot be inflated).

**Remediation:** Add `nonReentrant` modifier. Trivial, zero logic impact.

---

## P0-6 · SINGLE-EOA BRIDGE OWNERSHIP (P0-CRITICAL admin key concentration)

**Status:** OPEN. Assignment requires threshold governance for admin decisions.

`VaultfireTeleporterBridgeV3.owner` and `TrustPortabilityExtensionV3.owner` are single EOAs. `MultisigGovernanceV3` exists in the V3 draft (~350 lines) but the bridge is not yet transferred to it. The R3 partner-readiness checklist explicitly says "Transfer bridge ownership to `MultisigGovernanceV3` (single-EOA is unacceptable for Coinbase-grade)."

**Assignment invariant hit:** "A production dispute must not be controlled by one EOA." "Use threshold governance and timelocks for administrative decisions."

**Remediation:** Deploy `MultisigGovernanceV3` first, verify threshold + guardians config, then `bridge.transferOwnership(multisig)` from ghostkey.base.eth. Pair with a timelock for critical admin functions.

---

## P0-7 · SDK / API / WEBSITE CONTRACT COUNT LIES (P0-DOC)

**Status:** OPEN. Public claims contradict on-chain reality.

- April R3 final audit says **67 contracts**. Live registry today: **134 instances** (36 unique names × 4 chains). Contracts roughly doubled between April and August 2026 with no updated audit.
- 15+ unique contract names in production have no documented audit coverage: `AgentInsurancePool`, `AgentSLAEnforcer`, `AgentSafetyManager`, `AntiSurveillance`, `ComplianceRegistry`, `ReputationDecay`, `ScopedDelegation`, `VKPDelegationRegistry`, `VKPFactory`, `VKPSessionKeyManager`, `VaultfireBIPVault`, `VaultfireBatchOnboarder`, `VaultfireBondInsurancePool`, `VaultfireForumRegistry`, `VaultfireNameService`, `VaultfireStreetCred`, `VaultfireTaskEscrow`, `VaultfireTrustAttestation`, `VaultfireTrustOracle`, `BeliefAttestationVerifier`.
- Public "Solana" claims: registry lists Solana as x402/`payToSolana` only — no Solana contracts deployed. This is fine to say honestly ("four EVM contract deployments plus Solana x402 and read support") but incorrect to say as "five-chain protocol deployment."
- `VaultfireBIPVault` (Base only) claims ERC-4626 compatibility — no property tests found in `contracts-root/`. If shipped as such, must pass the ERC-4626 property battery (see assignment).

**Remediation:** Regenerate `/api/contracts`, `/.well-known/x402.json`, SDK constants, docs and `SKILL.md` from the canonical `deployments.json` in `baseline/`. Rewrite chain claims to "4 EVM + Solana x402/read." Do not publish a partner claim until all 15+ un-audited names either have audit coverage or are explicitly labeled `unaudited` in `deployments.json`.

---

## P0-8 · SOURCIFY VERIFICATION 0/134 (P0-DOC and P0-INTEG)

**Status:** OPEN. Confirmed in initial baseline.

Zero of the 134 deployed instances are verified on Sourcify (as pulled 2026-08-20). Etherscan family verification (BaseScan / SnowTrace / Arbiscan / Polygonscan) may be present but was not systematically checked and is not portable across explorers.

**Why it matters:** Partners integrating an "institutional-grade" protocol expect verified source at a canonical location. Sourcify is the multi-explorer-neutral option. Without verification, address-based trust is a bare bytecode hash.

**Remediation:** Use `sourcify.eth` upload flow for each of the 134 addresses from the exact V2 source pinned at HEAD `a80144d1acb...`. Batchable in one afternoon per chain. Requires: correct compiler version + settings per contract, matching source files, no key material required.

---

## P0-9 · UN-AUDITED PROD CONTRACTS WITH FUND CUSTODY (P0-CRITICAL until audited)

**Status:** OPEN.

Native-balance holders across the 134 (from `onchain_fetch_report.json`, non-zero native balance):
- Polygon `AIAccountabilityBondsV2` — 13.10 MATIC
- Base `AIAccountabilityBondsV2` — pending re-check
- Base `AIPartnershipBondsV2` — pending re-check
- Plus 6 smaller balances totaling ~4.3 units native across chains

For V2 balances: these are on immutable V2 code paths — the assignment forbids "editing" them. V3 migration must provide a documented voluntary exit (or document that no exit exists) per assignment §"V2-to-V3 migration."

For V3 fund-custody contracts (BIPVault, BondInsurancePool, TaskEscrow, DisputeResolution) all of which appear both in draft form AND deployed live: same rule — cannot ship a partner pilot until audit coverage exists.

---

## P0-10 · MISSING PROTOCOL PRIMITIVES REQUIRED BY THE ASSIGNMENT (P0-DESIGN)

**Status:** OPEN. Gap analysis of the existing V3 drafts against the assignment invariants.

The V3 drafts (see `V3_DRAFT_STATE.md`) implement bonds, bridges, oracles, and reputation to a high standard, but do **not** yet implement several assignment-mandated primitives:

| Assignment section | Missing in V3 draft | Priority |
|---|---|---|
| §Mandate Registry | No versioned `MandateRegistry` contract. Bonds carry terms but not a canonical `mandateHash` structure with the 16 required fields. | P0 |
| §Identity — EIP-712 / ERC-1271 | Existing V3 uses direct calls, not signed EIP-712 typed data with ERC-1271 fallback for smart-contract wallets. | P0 |
| §Consent — "no relationship without both signatures" | Partnership V3 requires proposal + acceptance, but the signature scheme is not EIP-712 with domain separation across (chainId, version, contract). | P0 |
| §Bond state machine | State machine exists implicitly, but no explicit deadline-with-safe-exit on every non-terminal state. `ForceExit` path is not present as a permissionless action. | P0 |
| §Reward programs separate from principal | Flourishing rewards are wired into bond math via `_capToOracle`; not clearly separated into a "sponsor-funded reward reserve" pool with fixed max liability distinct from principal. | P0 |
| §Evidence attestations | Present in reputation registry, but `Independence classification` field is not on the attestation struct. | P1 |

These are pure additions — they do not require touching V2 or touching the deployed V3 drafts' function bodies. They can be shipped as: (a) a new `MandateRegistryV3` contract, (b) an EIP-712 verifier library used by V3 bond variants that wrap the existing V3 draft with signed terms, and (c) an explicit `RewardProgramV3` contract that a bond references by ID. The wrapping happens at the "V3 pilot" bond variant — the existing drafts remain unmodified until the R3 audit findings above are resolved.

---

## P1 items intentionally not in this list

Deferred to `V3_DRAFT_STATE.md` remediation matrix:
- All R3 MEDIUM findings (19 items)
- R3 HIGH-R3-03 through -10 (bridge sender field, destination chainId, TPE stale-overwrite guards, slashed-oracle stake, queryBelief rate limit, 2-step ownership on Adapter and ProdVerifier) — these are P1 and mapped to PR4/PR5/PR6.
- Unbounded `sourceList` gas DoS on `FlourishingMetricsOracleV3` (MED-R3-14).
- Free ERC-8004 identity registration → Sybil concern (MED-R3-19).

## Verification & rebuild

The evidence in this document is reproducible from the workspace baseline:
- Ownership counts: `baseline/merged_onchain.json` (compromised-owner filter above)
- Contract inventory: `baseline/deployments.json`
- Audit findings: `v2-repo/reference-docs/VAULTFIRE-V3.3-R3-COINBASE-GRADE-AUDIT.md`
- Compromised key policy: memory note `vaultfire_deployer_wallet.md`

No live remediation action from this list will be executed without explicit owner approval and a per-item confirmation.
