@agenticprimitives/contracts 0.1.0-alpha.3 → 1.0.0-alpha.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AUDIT.md +324 -47
- package/README.md +45 -10
- package/deployments-base-sepolia.json +36 -0
- package/deployments-registries-base-sepolia.json +1 -0
- package/dist/abi/AgentAccount.json +1670 -0
- package/dist/abi/AgentAccountFactory.json +477 -0
- package/dist/abi/AgentNameRegistry.json +51 -0
- package/dist/abi/AgenticGovernance.json +32 -0
- package/dist/abi/AgreementRegistry.json +427 -0
- package/dist/abi/AttestationRegistry.json +436 -0
- package/dist/abi/CallDataHashEnforcer.json +119 -0
- package/dist/abi/CustodyPolicy.json +1122 -0
- package/dist/abi/DelegationManager.json +645 -0
- package/dist/abi/GeoFeatureRegistry.json +625 -0
- package/dist/abi/IAgentAccount.json +262 -0
- package/dist/abi/IAgentAccountFactoryAccessor.json +15 -0
- package/dist/abi/IAgentAccountFactoryView.json +41 -0
- package/dist/abi/IAgentAccountPauseView.json +15 -0
- package/dist/abi/IAgenticPrimitivesAgentAccount.json +15 -0
- package/dist/abi/IApprovedHashRegistryView.json +26 -0
- package/dist/abi/ICustodyPolicyFactoryView.json +15 -0
- package/dist/abi/ICustodyPolicyPauseView.json +15 -0
- package/dist/abi/IDelegationManager.json +171 -0
- package/dist/abi/IERC7579HookLike.json +44 -0
- package/dist/abi/IERC7579ModuleLike.json +28 -0
- package/dist/abi/IPaymentReceiptRegistry.json +50 -0
- package/dist/abi/MockUSDC.json +621 -0
- package/dist/abi/PaymentEnforcer.json +407 -0
- package/dist/abi/PaymentEscrow.json +299 -0
- package/dist/abi/PaymentReceiptRegistry.json +182 -0
- package/dist/abi/PermissionlessSubregistry.json +5 -0
- package/dist/abi/QuorumEnforcer.json +311 -0
- package/dist/abi/SignatureSlotRecovery.json +88 -0
- package/dist/abi/SkillDefinitionRegistry.json +491 -0
- package/dist/abi/SmartAgentPaymaster.json +873 -0
- package/dist/abi/UniversalSignatureValidator.json +65 -0
- package/dist/abi/index.d.ts +28 -0
- package/dist/abi/index.js +28 -0
- package/dist/deployments/base-sepolia.d.ts +41 -0
- package/dist/deployments/base-sepolia.js +39 -0
- package/dist/deployments/index.d.ts +9 -0
- package/dist/deployments/index.js +16 -0
- package/dist/deployments/registries-base-sepolia.d.ts +9 -0
- package/dist/deployments/registries-base-sepolia.js +7 -0
- package/package.json +24 -3
- package/src/AgentAccount.sol +191 -11
- package/src/AgentAccountFactory.sol +66 -12
- package/src/SmartAgentPaymaster.sol +186 -20
- package/src/UniversalSignatureValidator.sol +3 -0
- package/src/agency/DelegationManager.sol +170 -0
- package/src/agency/IDelegationManager.sol +38 -0
- package/src/agreement/AgreementRegistry.sol +331 -0
- package/src/attestation/AttestationRegistry.sol +384 -0
- package/src/custody/CustodyPolicy.sol +138 -8
- package/src/enforcers/AllowedMethodsEnforcer.sol +6 -0
- package/src/enforcers/AllowedTargetsEnforcer.sol +6 -0
- package/src/enforcers/CallDataHashEnforcer.sol +61 -0
- package/src/enforcers/PaymentEnforcer.sol +194 -0
- package/src/enforcers/QuorumEnforcer.sol +20 -0
- package/src/enforcers/TimestampEnforcer.sol +7 -0
- package/src/enforcers/ValueEnforcer.sol +6 -0
- package/src/geo/GeoFeatureRegistry.sol +197 -0
- package/src/governance/AgenticGovernance.sol +26 -4
- package/src/libraries/SignatureSlotRecovery.sol +8 -3
- package/src/libraries/WebAuthnLib.sol +13 -0
- package/src/mocks/MockUSDC.sol +152 -0
- package/src/naming/AgentNameRegistry.sol +52 -15
- package/src/naming/AgentNameUniversalResolver.sol +1 -1
- package/src/naming/PermissionlessSubregistry.sol +19 -2
- package/src/payments/PaymentEscrow.sol +137 -0
- package/src/payments/PaymentReceiptRegistry.sol +67 -0
- package/src/relationships/AgentRelationship.sol +8 -0
- package/src/skills/SkillDefinitionRegistry.sol +187 -0
package/AUDIT.md
CHANGED
|
@@ -1,67 +1,344 @@
|
|
|
1
1
|
# `@agenticprimitives/contracts` — Security & Architecture Audit
|
|
2
2
|
|
|
3
|
-
**Status:** alpha (Base Sepolia testnet
|
|
4
|
-
**Last refreshed:** 2026-
|
|
3
|
+
**Status:** alpha (Base Sepolia testnet, deployed end-to-end)
|
|
4
|
+
**Last refreshed:** 2026-06-01 (R9 wave landed — Foundry invariant suites + Halmos symbolic proofs + Echidna nightly + Medusa weekend; H-6 `MAX_INITIAL_CUSTODIANS = 32` cap closed; supply-chain CVE allowlist; AEL spec 237 + Package Design v2 spec 238)
|
|
5
|
+
**Prior refresh:** 2026-06-01 (R6 contracts hardening wave landed)
|
|
5
6
|
**Owners:** contracts package CODEOWNERS
|
|
6
|
-
**System audit cross-reference:** [docs/
|
|
7
|
+
**System audit cross-reference:** [`docs/architecture/product-readiness-audit.md`](../../docs/architecture/product-readiness-audit.md)
|
|
8
|
+
**Prior dossier:** [`docs/audits/2026-05-packages-contracts-production-readiness.md`](../../docs/audits/2026-05-packages-contracts-production-readiness.md) (per-contract findings table)
|
|
9
|
+
**Recon doc:** [`docs/audits/r6-contracts-recon-2026-05-31.md`](../../docs/audits/r6-contracts-recon-2026-05-31.md) (R6 wave triage)
|
|
10
|
+
|
|
11
|
+
> **Reader's note for external auditors.** This dossier is the AUDIT-ready summary of the security posture of every contract under `src/`. It pairs with `specs/214-production-audit-dossier.md` (the target shape we're auditing toward), the [`docs/audits/`](../../docs/audits) folder (threat model, architecture diagram, evidence checklist), and the **R10 internal readiness assessment** at [`docs/audits/2026-06-01-r10-internal-readiness-assessment.md`](../../docs/audits/2026-06-01-r10-internal-readiness-assessment.md) (active P0/P1/P2/P3 backlog). For finding-level detail use the cross-references at the bottom of each section.
|
|
12
|
+
>
|
|
13
|
+
> **Coverage stack as of R9 (2026-06-01):** 680 Foundry tests across 37 .sol files + 7 Halmos symbolic proofs + 4 Echidna nightly properties + 4 Medusa weekend properties. R6 (2026-06-01), H1-H4 (2026-05-23), and earlier waves are all reflected. Static analysis (Slither + Aderyn + CodeQL) + Solhint security lint are PR-blocking; pnpm-audit + gitleaks + SBOM in the security workflow.
|
|
7
14
|
|
|
8
15
|
## 1. Charter
|
|
9
16
|
|
|
10
17
|
This package ships the **on-chain enforcement layer** for the agenticprimitives stack:
|
|
11
18
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
19
|
+
| File | Role |
|
|
20
|
+
|---|---|
|
|
21
|
+
| `AgentAccount.sol` | ERC-4337 v0.7 SA (UUPS-upgradeable, ERC-7579 module-host, ERC-1271 verifier, WebAuthn-supporting) |
|
|
22
|
+
| `AgentAccountFactory.sol` | CREATE2 deterministic deployer; salt derives from auth methods + scope (NEVER from a name, per ADR-0010) |
|
|
23
|
+
| `agency/DelegationManager.sol` + `enforcers/*` | Scoped ERC-7710 delegation with on-chain caveat enforcement (AllowedTargets, AllowedMethods, Timestamp, Value, Quorum) |
|
|
24
|
+
| `custody/CustodyPolicy.sol` | Multi-sig custodian / trustee quorum + scheduled-action machinery (spec 213 carve-out, ERC-7579 module) |
|
|
25
|
+
| `SmartAgentPaymaster.sol` | ERC-4337 paymaster — three validation modes (dev / allowlist / verifying-paymaster) |
|
|
26
|
+
| `naming/{AgentNameRegistry, PermissionlessSubregistry, AgentNameUniversalResolver}.sol` | `.agent` + `.impact` TLDs + hierarchical registry + resolver |
|
|
27
|
+
| `identity/AgentProfileResolver.sol` | ERC-1056-style profile / AgentCard facet resolver |
|
|
28
|
+
| `ontology/{OntologyTermRegistry, ShapeRegistry, AttributeStorage}.sol` | SHACL shape + JSON-LD predicate registries |
|
|
29
|
+
| `relationships/AgentRelationship.sol` | Public on-chain edge model (⚠ Privacy Fork — see EXT-019; remains *experimental* surface) |
|
|
30
|
+
| `governance/{AgenticGovernance, GovernanceManaged}.sol` | System-wide pause flag + governance-managed base (R6.8 added `whenNotPaused` modifier via inheritance) |
|
|
31
|
+
| `libraries/{WebAuthnLib, P256Verifier, SignatureSlotRecovery, MultiSendCallOnly}.sol` | Security-critical primitives |
|
|
32
|
+
| `UniversalSignatureValidator.sol` | Single-entry signature validator per spec 214 SB-4 (ERC-6492 + ERC-1271 + raw ECDSA fanout) |
|
|
33
|
+
| `ApprovedHashRegistry.sol` | v=1 pre-approved hash signature path |
|
|
23
34
|
|
|
24
|
-
|
|
35
|
+
**Public addresses per network:** [`deployments-base-sepolia.json`](./deployments-base-sepolia.json) — committed; the source of truth, surfaced to TypeScript consumers via the generated [`@agenticprimitives/contracts/deployments/base-sepolia`](./dist/deployments/) subpath (R7.3).
|
|
25
36
|
|
|
26
37
|
## 2. Security invariants (DO NOT BREAK)
|
|
27
38
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
39
|
+
| # | Invariant | Status | Evidence |
|
|
40
|
+
|---|---|---|---|
|
|
41
|
+
| 2.1 | **EIP-712 typehashes byte-match the off-chain TS constants.** | ✅ CLOSED R11.4 | `packages/delegation/test/integration/cross-stack-typehashes.test.ts` + `pnpm check:eip712-typehash-equality` assert TS/Solidity equality. Keep this command in the public self-audit validation ledger. |
|
|
42
|
+
| 2.2 | **`AgentAccount` storage uses ERC-7201 namespaced slots + 50-slot gap.** | ✅ CLOSED H7-C.6 | `pnpm check:storage-layouts` (snapshot under CI) |
|
|
43
|
+
| 2.3 | **WebAuthn assertion verification pins RP-ID + UP flag.** | ✅ CLOSED H7-C.1 / CON-WEBAUTHN-001 | `_setupInitialPasskey` rejects zero rpIdHash (`AgentAccount.sol:354-356`); R6.10 added `_validateUserOp` happy-path test pack |
|
|
44
|
+
| 2.4 | **P256Verifier dispatcher rejects silent Daimo fallback.** | ✅ CLOSED H7-C.2 / CON-P256-001 | `P256Verifier.sol` |
|
|
45
|
+
| 2.5 | **`SignatureSlotRecovery` bounds-checks `v=0` + `v=2` slots.** | ✅ CLOSED H7-C.3 / CON-SIG-SLOT-001/-002 | `libraries/SignatureSlotRecovery.sol` |
|
|
46
|
+
| 2.6 | **`AgentNameRegistry.initializeRoot` cannot be frontrun.** | ✅ CLOSED H7-C.4 / CON-NAMING-001 | `AgentNameRegistry.sol:144-147` — immutable `initializer` + same-tx deploy |
|
|
47
|
+
| 2.7 | **`DelegationManager.redeemDelegation` is `nonReentrant`.** | ✅ CLOSED SC5 §6.2 | OpenZeppelin `ReentrancyGuard` mixin on `redeemDelegation` |
|
|
48
|
+
| 2.8 | **Factory + Paymaster governance is timelock + multisig, not a deployer EOA.** | ⏳ OPEN — N1 (operator-deferred) | Testnet currently uses the disclosed demo deployer; production rotation runbook is § 4 below |
|
|
49
|
+
| 2.9 | **Pause surfaces wired on critical paths.** | ✅ CLOSED R6.5 / R6.6 / R6.8 — pause matrix in § 3.4 | See § 3.4 |
|
|
50
|
+
| 2.10 | **`PermissionlessSubregistry.register` is `nonReentrant`.** | ✅ CLOSED R6.2 / CON-SUBREGISTRY-003 | `PermissionlessSubregistry.sol:register` |
|
|
51
|
+
| 2.11 | **Stateless enforcers don't need a pause check.** | ✅ CLOSED R6.7 / CON-ENFORCER-PAUSE-001 | Documented + test-locked in `test/EnforcerPauseInvariantR67.t.sol` |
|
|
52
|
+
| 2.12 | **CustodyPolicy reinstall is permanently forbidden.** | ✅ CLOSED audit C-11 | `CustodyPolicy.sol:onUninstall` + `onInstall` reinstall gate; tested in `CustodyPolicyWave2C.t.sol` |
|
|
53
|
+
| 2.13 | **`SmartAgentPaymaster.getHash` binds `address(entryPoint)` + chainId.** | ✅ CLOSED H7-C.7 / CON-PAYMASTER-004 | `SmartAgentPaymaster.sol:206-222`; tested in `SmartAgentPaymasterValidateR610.t.sol` |
|
|
54
|
+
| 2.14 | **`SmartAgentPaymaster` constructor takes EXPLICIT `devMode` (no implicit fail-open).** | ✅ CLOSED R5.7 / PKG-PAYMASTER-002 / external audit P0-2 | `SmartAgentPaymaster.sol:126-141`; tested in `SmartAgentPaymaster.t.sol::test_R5_7_*` |
|
|
37
55
|
|
|
38
|
-
## 3.
|
|
56
|
+
## 3. Per-contract notes for the external review
|
|
39
57
|
|
|
40
|
-
|
|
58
|
+
### 3.1 `AgentAccount.sol` + `AgentAccountFactory.sol`
|
|
59
|
+
|
|
60
|
+
- **ERC-4337 v0.7 + ERC-7579.** AgentAccount is a thin core (custody / threshold / spend / sessions live in modules, NOT inlined) — see spec 209 + `feedback_erc7579_module_architecture`.
|
|
61
|
+
- **UUPS upgradeable** with `_authorizeUpgrade` gated by `onlySelf` (modifier requires `msg.sender == address(this)`). The ONLY path is a re-entrant call from `upgradeToWithAuthorization` — that function is **explicitly disabled (`LegacyUpgradePathDisabled()`)** per H7-A.5 / audit C-3 closure. Per-account upgrades route through `CustodyPolicy.ApplySystemUpdate` (T5 / 24h timelock by default).
|
|
62
|
+
- **Pause coverage (R6.5):** `whenNotPaused` on `execute`, `executeBatch`, `executeFromModule`, `installModule`, `executePendingUpgrade`, `addCustodian` (6 functions). Recovery paths INTENTIONALLY UNGUARDED: `uninstallModule`, `cancelPendingUpgrade`, `removeCustodian` — incident response must remain functional under pause. Test pack: `test/AgentAccountPauseR65.t.sol` (14 tests).
|
|
63
|
+
- **`isValidSignature` dispatch:** type byte 0x01 → WebAuthn, type byte 0x02 → ECDSA, raw fallback → custodian sig. WebAuthn path pins per-credential rpIdHash + requires UP flag.
|
|
64
|
+
- **CREATE2 salt invariant.** Salt MUST derive from auth methods + scope, NEVER from a name (ADR-0010 + spec 220). Credential rotation does NOT change the SA address (ADR-0011 + spec 221).
|
|
65
|
+
|
|
66
|
+
### 3.2 `agency/DelegationManager.sol` + `enforcers/*`
|
|
67
|
+
|
|
68
|
+
- **5 enforcers.** AllowedMethods, AllowedTargets, Timestamp, Value, Quorum. All five are **stateless validators**: `beforeHook` / `afterHook` are pure / view, no SSTORE, no events. Invariant locked in `test/EnforcerPauseInvariantR67.t.sol` (R6.7) — see § 3.3.
|
|
69
|
+
- **EIP-712 Caveat typehash** = `keccak256("Caveat(address enforcer,bytes terms)")` — EXCLUDES `args` (audit F-1 — the inclusion bug caused every caveat-carrying delegation to fail on-chain ERC-1271 redeem AND let a redeemer's chosen args ride inside the delegator's signature). `packages/delegation/src/hash.ts` mirrors this.
|
|
70
|
+
- **Test posture:** DelegationManager 95.8% lines / 88.2% branches (post-R6 aggregator).
|
|
71
|
+
- **QuorumEnforcer:** payload-hash binding closed in H7 (`QuorumEnforcer.sol` checks `keccak256(args) == decoded_payload_hash`). R6.9 aggregator confirms 95.5% lines / 100% branches.
|
|
72
|
+
|
|
73
|
+
### 3.3 Pause invariant for enforcers (R6.7)
|
|
74
|
+
|
|
75
|
+
All five enforcer `beforeHook` / `afterHook` functions are `external view` AND read no storage. Pause is not applicable because there is no state to mutate. The invariant is documented in source and **test-locked**: `test/EnforcerPauseInvariantR67.t.sol::SideEffectfulEnforcer` mock proves any future enforcer that writes state would fail the test.
|
|
76
|
+
|
|
77
|
+
### 3.4 Pause matrix (R6.5 / R6.6 / R6.8)
|
|
78
|
+
|
|
79
|
+
Pause is system-wide via `AgenticGovernance.setPaused(bool)`. Each consumer reads the flag via a fail-soft staticcall (governance == EOA / non-conforming contract → treated as "not paused" — legacy/test compat) per `_pausedSafe()` in `GovernanceManaged.sol:69-74`.
|
|
80
|
+
|
|
81
|
+
| Contract | Guarded paths | Unguarded (intentional) | Closure |
|
|
82
|
+
|---|---|---|---|
|
|
83
|
+
| `AgentAccount` | execute, executeBatch, executeFromModule, installModule, executePendingUpgrade, addCustodian | uninstallModule, cancelPendingUpgrade, removeCustodian (recovery primitives) | R6.5 |
|
|
84
|
+
| `CustodyPolicy` | scheduleCustodyChange, applyCustodyChange | cancelScheduledChange (allow recovery cancel during incident), onUninstall (operator escape) | R6.6 |
|
|
85
|
+
| `AgentNameRegistry` | register, backfillLabel, setOwner, setResolver, setSubregistry, renew, setPrimaryName (7 functions) | initializeRoot (deployer init, runs ONCE per TLD anyway) | R6.8 |
|
|
86
|
+
| `PermissionlessSubregistry` | (no own guard — inherits via inner `REGISTRY.register` call) | — | Transitive R6.8 coverage; proven by `test_R6_8_subregistryRegister_pausedRevertsTransitively` |
|
|
87
|
+
| `SmartAgentPaymaster` | `_validatePaymasterUserOp` (early reverts on governance.isPaused()) | — | H7-C.10 / EXT3-010 |
|
|
88
|
+
|
|
89
|
+
### 3.5 `custody/CustodyPolicy.sol`
|
|
90
|
+
|
|
91
|
+
- **One ERC-7579 module per AgentAccount,** factory-immutable per spec 213 § 2.4. Per-account install via `onInstall(initData)` with ABI: `(uint8 mode, uint8 recoveryApprovals, address[] trustees, uint8[7] thresholds, uint32[7] timelocks, uint256 t3Ceiling, address approvedHashReg)`.
|
|
92
|
+
- **16 CustodyAction enum cases** (per § `_applyCustodyChange` dispatcher). Coverage by branch family in R6.10b + R6.10c — final state: **81.4% lines / 53% branches** (R6.10b) → after R6.10c **92.4% lines / 68% branches**.
|
|
93
|
+
- **Audit C-6..C-11 closures:** zero `credentialIdDigest` rejected, RotateAllCustodians wire shape add+remove, CustodyPolicy reinstall permanently forbidden, etc. See `test/CustodyPolicyWave2C.t.sol`.
|
|
94
|
+
- **Pause coverage (R6.6):** `scheduleCustodyChange` + `applyCustodyChange` guarded with `whenAccountNotPaused(account)`; `cancelScheduledChange` UNGUARDED intentionally (recovery cancel during incident).
|
|
95
|
+
- **Threshold defaults (spec § 5.1):** T4 default = max(N − 2, 1); T5 default = max(N − 1, 1); T6 default = `recoveryApprovals`. Factory derives `recoveryApprovals = trustees/2 + 1` if not overridden.
|
|
96
|
+
|
|
97
|
+
### 3.6 `SmartAgentPaymaster.sol` — economics + paymaster modes
|
|
98
|
+
|
|
99
|
+
**Three validation modes:**
|
|
100
|
+
|
|
101
|
+
| Mode | Constructor wiring | Sponsor behavior | Use case |
|
|
102
|
+
|---|---|---|---|
|
|
103
|
+
| **Dev** | `devMode_=true` | Accept every userOp (no sig check) | Testnet only — Deploy.s.sol auto-flips devMode=true via `_isTestnetNetwork()` |
|
|
104
|
+
| **Allowlist** | `devMode_=false`, `verifyingSigner_=address(0)` | Only senders in `_acceptList` are sponsored | Allowlist mode — fail-closed (every userOp reverts `SenderNotAccepted` until `setAccepted` runs) |
|
|
105
|
+
| **Verifying** | `devMode_=false`, `verifyingSigner_=<EOA>` | EOA signs over `getHash(userOp, validUntil, validAfter)` off-chain | Production preferred — Pimlico / Stackup / Alchemy reference pattern |
|
|
106
|
+
|
|
107
|
+
**Hash binding (CON-PAYMASTER-004 / H7-C.7):**
|
|
108
|
+
```
|
|
109
|
+
keccak256(abi.encode(
|
|
110
|
+
userOp.sender, userOp.nonce, keccak256(userOp.initCode), keccak256(userOp.callData),
|
|
111
|
+
userOp.accountGasLimits, userOp.preVerificationGas, userOp.gasFees,
|
|
112
|
+
block.chainid, address(this), address(entryPoint()),
|
|
113
|
+
validUntil, validAfter
|
|
114
|
+
))
|
|
115
|
+
```
|
|
116
|
+
Binds `address(entryPoint())` so a long-lived signed envelope CANNOT survive an EntryPoint redeploy.
|
|
117
|
+
|
|
118
|
+
**Pause integration:** `_validatePaymasterUserOp` runs the governance.isPaused() staticcall FIRST. Pause flips paymaster to reject-all without operator intervention.
|
|
119
|
+
|
|
120
|
+
**Economics:**
|
|
121
|
+
- Stake: 0.0005 ETH at deploy (paid from deployer at constructor time via Deploy.s.sol).
|
|
122
|
+
- Deposit: 0.001 ETH at deploy.
|
|
123
|
+
- 1-day unstake delay (BasePaymaster default; cannot be reduced).
|
|
124
|
+
- Topup: operator endpoint `/paymaster/topup` (`apps/demo-a2a/src/index.ts:2329+`) — capped at 0.002 ETH per call (`TOPUP_MAX_WEI`) and gated below 0.005 ETH floor (`TOPUP_TARGET_FLOOR`). The signer is wrapped in `createSpendCappedAccount` so a `value > PAYMASTER_TOPUP_CAP_WEI` tx throws BEFORE the HSM round-trip even if the app-layer cap is bypassed (R5.12).
|
|
125
|
+
|
|
126
|
+
**Coverage (R6.10):** 98.2% lines / 100% branches; the full validation path is exercised via `vm.prank(address(ep))` calls in `test/SmartAgentPaymasterValidateR610.t.sol` (20 tests).
|
|
41
127
|
|
|
42
|
-
|
|
128
|
+
### 3.7 `UniversalSignatureValidator.sol` — single signature entrypoint
|
|
43
129
|
|
|
44
|
-
|
|
130
|
+
Per spec 214 SB-4. **One library function consumers call (`validateSig(signer, hash, sig)`)** that internally:
|
|
45
131
|
|
|
46
|
-
|
|
47
|
-
|
|
132
|
+
1. If `sig` looks like ERC-6492 (`0x6492...` magic suffix): execute the prefix calldata at the magic-named factory, then re-evaluate.
|
|
133
|
+
2. If `signer` has code: call `signer.isValidSignature(hash, sig)` (ERC-1271) and check magic value.
|
|
134
|
+
3. Otherwise: ECDSA `ecrecover` against `signer`.
|
|
135
|
+
|
|
136
|
+
**The signer-agnostic property** — caller passes one address + one hash + one signature, regardless of whether the SA is deployed, undeployed-but-counterfactual, or an EOA. Consumers (demo-a2a SIWE verify, off-chain delegation verify) never branch on signer kind.
|
|
137
|
+
|
|
138
|
+
**Coverage (R6.9 aggregator):** 94.4% lines / 83.3% branches.
|
|
139
|
+
|
|
140
|
+
### 3.8 `naming/*`
|
|
141
|
+
|
|
142
|
+
- **Multi-root registry.** `.agent` (deployed via Deploy.s.sol) + `.impact` (added 2026-06-01 via `script/AddImpactTld.s.sol`). Each TLD has an immutable initializer set at construction — front-run-proof.
|
|
143
|
+
- **PermissionlessSubregistry per TLD** — one-name-per-caller, no whitelist. Backing the `<handle>.impact-agent.{me,io}` subdomain mapping.
|
|
144
|
+
- **`reverseResolveString` (spec/222)** is the canonical reverse path — NO `eth_getLogs` walker fallback (ADR-0012, ADR-0013).
|
|
145
|
+
- **Pause coverage (R6.8):** all mutating functions guarded; `initializeRoot` exempt because it runs ONCE per TLD ever.
|
|
146
|
+
|
|
147
|
+
### 3.9 `relationships/AgentRelationship.sol` — ⚠ Privacy Fork
|
|
148
|
+
|
|
149
|
+
EXT-019 status: **experimental** — public on-chain edges leak the social graph. Production deployments where membership / participation is sensitive should NOT register relationships on this contract; the package's `AUDIT.md` makes this explicit. A future spec covers an encrypted-edge variant.
|
|
150
|
+
|
|
151
|
+
## 4. Upgrade & governance — production posture
|
|
152
|
+
|
|
153
|
+
### 4.1 The production gate (open audit item N1)
|
|
154
|
+
|
|
155
|
+
**Current testnet state.** Deployer EOA `0x31ed17fb99e82E02085Ab4B3cbdaB05489098b44` is publicly disclosed (intentional — keeps the demo reproducible from a clean clone) and currently holds: governance role on AgenticGovernance; bundler signer + session issuer on AgentAccountFactory; initial owner + governance pointer on SmartAgentPaymaster.
|
|
156
|
+
|
|
157
|
+
**Why this is testnet-only.** Any holder of the disclosed key could rotate factory roles to a hostile address (factory `setBundlerSigner` is `onlyGovernance`), pause the paymaster, or withdraw paymaster stake after the 1-day unstake delay.
|
|
158
|
+
|
|
159
|
+
**Production runbook.** Before any production network deploy:
|
|
160
|
+
|
|
161
|
+
1. Generate a clean deployer key inside KMS (gcp-kms / aws-kms recommended). Never touches a developer's filesystem.
|
|
162
|
+
2. Deploy AgenticGovernance under a multisig owner. Suggested: 3-of-5 Safe with a 24h TimelockController.
|
|
163
|
+
3. Deploy contracts with `Deploy.s.sol`, passing the clean deployer address + the multisig.
|
|
164
|
+
4. The deploy script's `setBundlerSigner` / `setSessionIssuer` runs in the same broadcast — atomic.
|
|
165
|
+
5. After successful deploy, REVOKE the deployer key entirely (KMS scheduled destroy).
|
|
166
|
+
6. The preflight gate `scripts/check-production-deploy.ts:gateOnLeakedDeployerKey` refuses deploys when `.env.deploy.local` references the disclosed deployer EOA (with explicit `AGENTICPRIMITIVES_DEMO_KEYS_ACCEPTED=true` override for testnet runs).
|
|
167
|
+
|
|
168
|
+
### 4.2 Upgrade pattern
|
|
169
|
+
|
|
170
|
+
- AgentAccount: UUPSUpgradeable. `_authorizeUpgrade` gated by `onlySelf`. Per-account upgrades routed through `CustodyPolicy.ApplySystemUpdate` (T5 / 24h timelock by default).
|
|
171
|
+
- AgenticGovernance: NOT upgradeable. Operator deploys a new governance instance to migrate.
|
|
172
|
+
- AgentAccountFactory + SmartAgentPaymaster + DelegationManager + CustodyPolicy + naming/* + enforcers: NOT upgradeable. Redeploy + migrate-by-naming-handle. The 2026-06-01 R6 redeploy + `.impact` re-init demonstrates the runbook.
|
|
173
|
+
|
|
174
|
+
### 4.3 TimelockController
|
|
175
|
+
|
|
176
|
+
`Deploy.s.sol` deploys an OpenZeppelin TimelockController with a 24h minimum delay. Owner = the multisig deployed in step 4.1. Production governance flows queue → wait 24h → execute.
|
|
48
177
|
|
|
49
178
|
## 5. Test posture
|
|
50
179
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
-
|
|
180
|
+
**Forge:** 635 tests across 28 contracts (all pass, 2026-06-01).
|
|
181
|
+
|
|
182
|
+
**Coverage (R6.9 per-contract aggregator):**
|
|
183
|
+
|
|
184
|
+
| Category | Lines | Branches | Functions |
|
|
185
|
+
|---|---:|---:|---:|
|
|
186
|
+
| security-critical (11 contracts) | **90.3%** | **76.6%** | 93.4% |
|
|
187
|
+
| core (1) | 100% | 100% | 100% |
|
|
188
|
+
| naming-ontology (7) | 71.2% | 61.0% | 69.3% |
|
|
189
|
+
| identity (3) | 79.6% | 50.0% | 70.4% |
|
|
190
|
+
| governance (2) | 97.8% | 66.7% | 100% |
|
|
191
|
+
| library (4) | 77.7% | 79.5% | 100% |
|
|
192
|
+
| **Overall (28)** | **83.1%** | **70.6%** | 81.8% |
|
|
193
|
+
|
|
194
|
+
All 11 security-critical contracts ≥ 75% lines (the audit-floor). Branches: 10/11 ≥ 80%; CustodyPolicy at 68% is the remaining outlier (the `_applyChangeApprovalsRequired` tier-matrix edges — diminishing returns).
|
|
195
|
+
|
|
196
|
+
**SAST:** Slither runs on every PR (fail-on `high`); Aderyn runs as a second-opinion AI scanner (artifact-uploaded, non-blocking) — R6.3.
|
|
197
|
+
|
|
198
|
+
**Storage layouts:** `pnpm check:storage-layouts` snapshot-locks AgentAccount + CustodyPolicy + DelegationManager + SmartAgentPaymaster slot layouts (R1.3 / C6). Any drift fails CI explicitly.
|
|
199
|
+
|
|
200
|
+
**Symbolic verification (R9.3 + R9.3.x):** Halmos symbolic-execution proofs LIVE in `test/halmos/`. PR-blocking via the `halmos` job in `.github/workflows/security.yml`. Current proof set (7 proofs, terminate in 0.13s):
|
|
201
|
+
|
|
202
|
+
| Proof | Target | File |
|
|
203
|
+
|---|---|---|
|
|
204
|
+
| R8.2 UV-required gate | `WebAuthnLib.verify(..., requireUv = true)` rejects when UV bit unset, for ALL inputs | `WebAuthnLibUvR82.halmos.t.sol::check_R82_uvNotSet_with_requireUvTrue_alwaysRejects` |
|
|
205
|
+
| H7-C.1 UP-required gate | `WebAuthnLib.verify` rejects when UP bit unset, regardless of `requireUv` | `WebAuthnLibUvR82.halmos.t.sol::check_H7C1_upNotSet_alwaysRejects_regardlessOfRequireUv` |
|
|
206
|
+
| `setDelegationManager` onlySelf | External caller cannot rotate the delegation root of trust | `AgentAccountOnlySelf.halmos.t.sol::check_onlySelf_setDelegationManager_revertsForExternalCaller` |
|
|
207
|
+
| `removeCustodian` onlySelf | External caller cannot kick custodians | same file |
|
|
208
|
+
| `setUpgradeTimelock` onlySelf | External caller cannot disable the upgrade timelock | same file |
|
|
209
|
+
| `upgradeToAndCall` onlySelf (UUPS hook) | **Catastrophic if bypassable — Halmos proves it isn't.** External caller cannot swap the implementation | same file |
|
|
210
|
+
| `removePasskey` onlySelf | External caller cannot un-register a credential | same file |
|
|
211
|
+
|
|
212
|
+
Each proof: caller address symbolic, all args symbolic, only constraint is `caller != address(acct)`. Halmos explores every other input dimension.
|
|
213
|
+
|
|
214
|
+
**Foundry stateful invariants (R9.1 + R9.2):** in `test/invariant/`. 15 invariants × 25,600 calls each per CI run, PR-blocking.
|
|
215
|
+
|
|
216
|
+
| Suite | Invariants |
|
|
217
|
+
|---|---|
|
|
218
|
+
| `CustodyPolicy.invariant.t.sol` | thresholds nonzero • recoveryApprovals ≤ trusteeCount • mode ∈ {0..3} • changeCount monotonic • uninstalled views zero |
|
|
219
|
+
| `DelegationManager.invariant.t.sol` | revocation irreversible • hash deterministic • domain separator immutable • root/open constants unchanged • revoked-set monotonic |
|
|
220
|
+
| `SmartAgentPaymaster.invariant.t.sol` | devMode locked • governance immutable • verifyingSigner locked • arbitrary-sender not accepted • governance gate holds for fresh caller |
|
|
221
|
+
|
|
222
|
+
**Echidna nightly fuzz (R9.4):** `test/echidna/CustodyPolicyEchidna.t.sol` — 4 properties × 50,000 sequences × 4 parallel workers, runs nightly at 02:17 UTC via `.github/workflows/contracts-echidna-nightly.yml`. Artifact-only (`continue-on-error: true`); corpus uploaded as a 30-day-retained artifact. Smoke run: 1.3M calls / 4524 unique instructions / all 4 properties PASS in 26 seconds.
|
|
223
|
+
|
|
224
|
+
**Medusa weekend fuzz (R9.5):** `test/medusa/CustodyPolicyMedusa.t.sol` — 4 properties × 4-hour budget × 4 parallel workers, runs Saturday 03:17 UTC via `.github/workflows/contracts-medusa-weekend.yml`. Different EVM engine (go-ethereum vs Echidna's HEVM) — independent coverage graphs. Artifact-only; 60-day corpus retention.
|
|
225
|
+
|
|
226
|
+
**Stack mix rationale.** Each tool has a different blind spot:
|
|
227
|
+
- Foundry invariants — stateful random + seeded shrinking; PR-blocking; catches per-PR regressions.
|
|
228
|
+
- Halmos — symbolic execution; covers ALL paths but bounded; PR-blocking on narrow proofs.
|
|
229
|
+
- Echidna — stateful ABI-aware random + coverage-guided; nightly; catches multi-step sequence bugs Foundry's seed strategy doesn't explore.
|
|
230
|
+
- Medusa — different EVM + different coverage-guidance strategy; weekly; deep-corpus regression coverage.
|
|
231
|
+
|
|
232
|
+
A regression that slips one tool's coverage graph can still be caught by another.
|
|
233
|
+
|
|
234
|
+
## 6. Public API surface (audit scope)
|
|
235
|
+
|
|
236
|
+
All `*.sol` files under `src/` + the JSON ABIs published under `dist/abi/`. Consumers MUST import ABIs via the npm-published `@agenticprimitives/contracts/abi` subpath, NOT by reading `out/` directly.
|
|
237
|
+
|
|
238
|
+
The `dist/deployments/` subpath (R7.3) exposes per-network contract addresses to TypeScript consumers — a single source of truth replacing per-app hardcoded tables.
|
|
239
|
+
|
|
240
|
+
## 7. Known findings (cross-reference)
|
|
241
|
+
|
|
242
|
+
Canonical cross-cutting status: [`docs/audits/findings.yaml`](../../docs/audits/findings.yaml) (CI-enforced).
|
|
243
|
+
Historical detail: [`docs/audits/archive/2026-05/2026-05-packages-contracts-production-readiness.md`](../../docs/audits/archive/2026-05/2026-05-packages-contracts-production-readiness.md) § 3–4.
|
|
244
|
+
|
|
245
|
+
**Closed (2026-06-09 independent contracts audit — SC-1..SC-5):**
|
|
246
|
+
|
|
247
|
+
- **SC-1** (critical) — agreement issuer signature now bound to contents: `AgreementRegistry.register()` recomputes the digest over `agreementCommitment, schemaHash, issuer, chainId, address(this)` (`AGREEMENT_ISSUER_TYPEHASH`).
|
|
248
|
+
- **SC-2** (high) — attestation subject-spoofing closed: `assertAssociation()` binds `subject, issuer, schemaId, credentialType, credentialHash, chainId, address(this)` (`ASSOCIATION_ATTESTATION_TYPEHASH`); joint path requires both party co-signatures.
|
|
249
|
+
- **SC-3** (medium) — `DelegationManager.verifyAuthorizationForCall()` returns `(false,"no-code-enforcer")` instead of failing open on a no-code enforcer.
|
|
250
|
+
- **SC-4** (medium) — `.impact` TLD root handed to `roles.namingRootOwner` (governance) at deploy, with a post-deploy assertion.
|
|
251
|
+
- **SC-5** (medium) — paymaster deploy `require`-rejects deployer-as-governance/owner in production mode.
|
|
252
|
+
|
|
253
|
+
**Open — contract deep audit 2026-06-10** (full detail: [`docs/audits/2026-06-10-contract-by-contract-audit.md`](../../docs/audits/2026-06-10-contract-by-contract-audit.md); status in `findings.yaml`):
|
|
254
|
+
|
|
255
|
+
| Finding | Severity | One-line |
|
|
256
|
+
|---|---|---|
|
|
257
|
+
| **CA-F1** | High | Factory counterfactual address omits mode/trustees/timelocks → pre-deploy custody hijack of the canonical identity. **Mainnet blocker.** |
|
|
258
|
+
| **CP-1** | High | Custody tier thresholds default to 1-of-n → one custodian key can rotate the set / upgrade impl. **Mainnet blocker.** |
|
|
259
|
+
| **AN-1-ONCHAIN** | High | On-chain name registry enforces no label normalization (the SDK fix is bypassable by direct callers). |
|
|
260
|
+
| **SUB-1 / SUB-2** | High | Subregistry name front-running (no commit-reveal) + sybil/homoglyph mass-squatting. |
|
|
261
|
+
| **ATT-1** | High | `assertJointAgreement` issuer signature over a bare `credentialHash` (the SC-2 bug class, issuer side). |
|
|
262
|
+
| **AGR-1, ATT-2, CA-1, CA-2, CP-2, DM-1/2, EN-11/13/22, PM-1/2, GOV-1, WA-1/2, AN-2, RES-1, ONT-4/7** | Medium | See the report + `findings.yaml`. |
|
|
263
|
+
|
|
264
|
+
**Open at 2026-06-01:**
|
|
265
|
+
|
|
266
|
+
| Finding | Severity | Notes |
|
|
267
|
+
|---|---|---|
|
|
268
|
+
| N1 (system audit) | P0 | Leaked deployer key — testnet-only acceptance; production runbook in § 4.1 above |
|
|
269
|
+
| External Solidity audit | High (gate-level) | Cyfrin / CodeHawks contest planned; refer to R10 readiness doc P1.10 |
|
|
270
|
+
| CON-WEBAUTHN-AUTHDATA-len | Medium | Authenticator-data length check (open across H7) |
|
|
271
|
+
| EIP-712 cross-stack typehash equality | Medium | Test file LIVE (`packages/delegation/test/integration/cross-stack-typehashes.test.ts`) — 6 tests pass; wrapper command `pnpm check:eip712-typehash-equality` is part of the public self-audit validation ledger. |
|
|
272
|
+
| Kontrol / Certora formal verification | Medium | Halmos covers narrow proofs; Kontrol/Certora are R10 P2.2/P2.3 deferred to post-audit |
|
|
273
|
+
| Encrypted-edge AgentRelationship | Low | EXT-019; product decision pending |
|
|
274
|
+
|
|
275
|
+
**Closed since prior refresh (R9 wave, 2026-06-01):**
|
|
276
|
+
|
|
277
|
+
- Halmos symbolic verification — landed in R9.3 + R9.3.x (7 proofs)
|
|
278
|
+
- Foundry stateful invariants — landed in R9.1 + R9.2 (15 invariants)
|
|
279
|
+
- Echidna nightly + Medusa weekend — landed in R9.4 + R9.5
|
|
280
|
+
- Aderyn H-6 (`MAX_INITIAL_CUSTODIANS = 32`) — R9.6 cap with 6 regression tests
|
|
281
|
+
- Slither M-1 (PermissionlessSubregistry cross-function reentrancy) — false positive triaged + `slither-disable-next-line` annotated; full triage in [`docs/audits/r9-static-analysis-triage.md`](../../docs/audits/r9-static-analysis-triage.md)
|
|
282
|
+
|
|
283
|
+
**Closed in R6 (2026-06-01):**
|
|
284
|
+
|
|
285
|
+
- CON-SUBREGISTRY-003 (subregistry reentrancy guard) — R6.2
|
|
286
|
+
- CON-AgentAccount-005 (system-pause coverage) — R6.5
|
|
287
|
+
- CON-CustodyPolicy-005 (system-pause coverage) — R6.6
|
|
288
|
+
- CON-NAMING-005 (naming layer pause coverage) — R6.8
|
|
289
|
+
- CON-ENFORCER-PAUSE-001 (stateless validator invariant) — R6.7
|
|
290
|
+
- CON-SmartAgentPaymaster-001 (validation-path coverage 50.9% → 98.2%) — R6.10
|
|
291
|
+
- CON-CustodyPolicy-001 (branch coverage 30% → 68%) — R6.10b/c
|
|
292
|
+
|
|
293
|
+
## 8. Pre-publication checklist
|
|
294
|
+
|
|
295
|
+
- [x] License (MIT) + AUDIT.md (this file) + LICENSE + `publishConfig.access=public` — H7-A.2
|
|
296
|
+
- [x] Extracted as `@agenticprimitives/contracts` — H7-A.2 / EXT3-001
|
|
297
|
+
- [x] WebAuthn / P-256 / SignatureSlot / Naming hardening — H7-C.1..C.4
|
|
298
|
+
- [x] Coverage ≥ 75% lines on every security-critical contract — R6.10 (final security-critical floor cleared)
|
|
299
|
+
- [x] Storage-layout snapshots committed — H7-C.6
|
|
300
|
+
- [x] System-wide pause wired on critical paths — R6.5 / R6.6 / R6.8
|
|
301
|
+
- [x] Subregistry reentrancy guarded — R6.2
|
|
302
|
+
- [x] Stateless-enforcer invariant test-locked — R6.7
|
|
303
|
+
- [x] Cross-stack typehash test green — H7-D.9 / R11.4 closure; lives at `packages/delegation/test/integration/cross-stack-typehashes.test.ts` (6 tests pass) and runs through `pnpm check:eip712-typehash-equality`.
|
|
304
|
+
- [x] Halmos symbolic harness on top-3 invariants — R9.3 + R9.3.x landed (7 proofs, PR-blocking).
|
|
305
|
+
- [x] Echidna nightly stateful fuzzing — R9.4 (nightly schedule, artifact-only).
|
|
306
|
+
- [x] Medusa weekend coverage-guided fuzzing — R9.5 (weekly schedule, artifact-only).
|
|
307
|
+
- [x] H-6 / ATL-SEC-05 — initial custodians cap closed at construction — R9.6.
|
|
308
|
+
- [ ] Governance pattern: Safe + Timelock(24h); deployer EOA renounces — H7-C.9 / EXT3-009 (production-deploy item; runbook in § 4.1; R10 P1.1)
|
|
309
|
+
- [ ] On-chain governance-shape verifier script — R10 P1.2 (post-deploy gate that fails CI if `factory.governance() != multisig` etc.)
|
|
310
|
+
- [ ] Kontrol / Certora formal verification on top-3 invariants — R10 P2.2 / P2.3 (post-audit)
|
|
311
|
+
- [ ] One external Solidity audit firm engagement — Cyfrin / CodeHawks (planned)
|
|
312
|
+
- [x] Per-network deployments JSON committed — R7.3 (`deployments-base-sepolia.json` in tree)
|
|
313
|
+
- [x] Generated TS deployments module — R7.3 (`@agenticprimitives/contracts/deployments/<network>`)
|
|
314
|
+
- [x] ABI sync doctrine — R7.2 (`pnpm check:abi-sync` against Foundry truth)
|
|
315
|
+
|
|
316
|
+
## 9. Threat model summary
|
|
317
|
+
|
|
318
|
+
For the full threat model see [`docs/audits/threat-model.md`](../../docs/audits/threat-model.md). Quick reference for external reviewers:
|
|
319
|
+
|
|
320
|
+
| Adversary | Capability | Mitigation |
|
|
321
|
+
|---|---|---|
|
|
322
|
+
| **Hostile bundler** | Submits crafted userOps to drain paymaster | Verifying-paymaster mode (preferred prod) — paymaster signs each envelope; OR allowlist mode — fail-closed |
|
|
323
|
+
| **Leaked credential** | Owns a passkey / EOA on a multi-custody SA | CustodyPolicy quorum + 24h timelock + trustee recovery; ADR-0011 — credential rotation preserves SA address |
|
|
324
|
+
| **Front-runner** | Watches mempool for the deployer's first `initializeRoot` tx | Immutable `initializer` set at construction; deploy script runs both in the SAME tx (R6.8 / H7-C.4) |
|
|
325
|
+
| **Cross-EntryPoint replay** | Reuses a paymaster envelope against a redeployed EntryPoint | `getHash` binds `address(entryPoint())` — H7-C.7 / CON-PAYMASTER-004 |
|
|
326
|
+
| **Caveat author** | Picks `args` to slip a redeem through a delegator's signature | Caveat typehash EXCLUDES `args` — audit F-1 closure |
|
|
327
|
+
| **Reentrant subregistry caller** | Mock registry re-enters during inner `register` call | `nonReentrant` on `PermissionlessSubregistry.register` — R6.2 |
|
|
328
|
+
| **Governance pause bypass** | Calls during incident | `whenNotPaused` modifier on all write surfaces; recovery primitives explicitly exempt |
|
|
329
|
+
| **WebAuthn rp-id swap** | Registers passkey for one origin, signs for another | `_setupInitialPasskey` rejects zero `rpIdHash`; per-credential rpIdHash stored + checked at verify time |
|
|
330
|
+
|
|
331
|
+
## 10. Audit engagement targets
|
|
332
|
+
|
|
333
|
+
External engagements considered:
|
|
334
|
+
|
|
335
|
+
- **Cyfrin** — full-engagement contract audit; AgentAccount + AgentAccountFactory + CustodyPolicy + DelegationManager + SmartAgentPaymaster + 5 enforcers.
|
|
336
|
+
- **CodeHawks contest** — broader-surface contest including the naming layer + UniversalSignatureValidator.
|
|
337
|
+
- **Spearbit** — alternative full-engagement (research-grade review).
|
|
338
|
+
- **OpenZeppelin** — alternative full-engagement (large-firm review).
|
|
339
|
+
|
|
340
|
+
Pre-engagement deliverables: this file + threat model + architecture diagram + evidence checklist + spec 214 (already in `docs/audits/` + `specs/`). Pre-engagement code freeze: master branch with R6 wave landed + the R7 audit-driven hardening (R7.2 / R7.3 / R7.4 / R7.5) closed.
|
|
341
|
+
|
|
342
|
+
---
|
|
343
|
+
|
|
344
|
+
**Reviewer feedback** welcome — please file findings at https://github.com/agentictrustlabs/agenticprimitives/issues with the `audit-finding` label, OR encrypt and email to security@agentictrustlabs.com (PGP key in [`docs/audits/evidence-checklist.md`](../../docs/audits/evidence-checklist.md) § PGP).
|
package/README.md
CHANGED
|
@@ -1,15 +1,35 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @agenticprimitives/contracts
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**Contracts and SDK are one artifact.**
|
|
4
4
|
|
|
5
|
-
- `
|
|
6
|
-
- `AgentAccountFactory.sol` — CREATE2 factory for deterministic addressing
|
|
7
|
-
- `DelegationManager.sol` — delegation registry + revocation
|
|
8
|
-
- `ICaveatEnforcer.sol` + `enforcers/*` — the four enforcers our demo uses (Timestamp, AllowedTargets, AllowedMethods, Value)
|
|
5
|
+
This is the on-chain enforcement layer of the agenticprimitives substrate — 42 Solidity sources, all present and audited under `src/`, none of them stubs. ERC-4337 v0.7 Smart Agent core, ERC-7710-style delegation manager with caveat enforcers, an ERC-7579 modular CustodyPolicy, a three-mode paymaster, `.agent` TLD naming with hierarchical subregistries, ontology and profile resolvers, identity facets, and an ERC-6492-aware UniversalSignatureValidator. The package publishes ABI subpaths, per-network deployment JSON, flattened sources, and the Solidity itself — so a TypeScript consumer pulls addresses and ABIs from the same artifact the auditors read.
|
|
9
6
|
|
|
10
|
-
|
|
7
|
+
"One artifact" is CI-enforced, not aspirational: TypeScript typehashes are locked to the Solidity constants (`pnpm check:eip712-typehash-equality`), ABIs are sync-gated, and storage layouts are snapshot-gated (`pnpm check:storage-layouts`). You cannot drift the client from the chain.
|
|
11
8
|
|
|
12
|
-
>
|
|
9
|
+
> Part of [agenticprimitives](../../README.md) — the trust substrate for the agent economy: one canonical Smart Agent identity with custody, delegation, naming, credentials, and audit evidence designed as one system.
|
|
10
|
+
|
|
11
|
+
## What's in `src/`
|
|
12
|
+
|
|
13
|
+
- `AgentAccount.sol` — UUPSUpgradeable + ERC-7579 + ERC-1271 + WebAuthn-supporting Smart Agent; a thin modular core where custody, threshold, and session machinery are modules, not inlined features
|
|
14
|
+
- `AgentAccountFactory.sol` — CREATE2 deterministic deploys; salt from auth methods + scope (NEVER from a name)
|
|
15
|
+
- `SmartAgentPaymaster.sol` — verifying-paymaster + allowlist + devMode (testnet only) modes
|
|
16
|
+
- `agency/DelegationManager.sol` + `enforcers/{AllowedMethods,AllowedTargets,Timestamp,Value,Quorum,CallDataHash}Enforcer.sol` — scoped delegation with on-chain caveat enforcement
|
|
17
|
+
- `custody/CustodyPolicy.sol` — 16-action ERC-7579 module, T4/T5/T6 quorum tiers + 24h default T5 timelock
|
|
18
|
+
- `naming/{AgentNameRegistry,AgentNameUniversalResolver,AgentNameAttributeResolver,PermissionlessSubregistry}.sol`
|
|
19
|
+
- `identity/AgentProfileResolver.sol` + `ontology/{OntologyTermRegistry,ShapeRegistry,AttributeStorage}.sol`
|
|
20
|
+
- `relationships/{AgentRelationship,RelationshipTypeRegistry}.sol` (⚠ Privacy Fork — experimental surface; see AUDIT.md § 3.9)
|
|
21
|
+
- `governance/{AgenticGovernance,GovernanceManaged}.sol` — system pause + governance base
|
|
22
|
+
- `libraries/{WebAuthnLib,P256Verifier,SignatureSlotRecovery,MultiSendCallOnly}.sol` — security-critical primitives
|
|
23
|
+
- `UniversalSignatureValidator.sol` — single signature entrypoint (ERC-6492 + ERC-1271 + raw ECDSA fanout per spec 214 SB-4)
|
|
24
|
+
- `ApprovedHashRegistry.sol` — v=1 pre-approved hash signature path
|
|
25
|
+
|
|
26
|
+
Behind it: 774 Foundry tests including invariant suites, plus Halmos symbolic proofs, Echidna and Medusa fuzzing campaigns, and PR-blocking static analysis (Slither, Aderyn, CodeQL, Solhint).
|
|
27
|
+
|
|
28
|
+
Deployed addresses (Base Sepolia testnet): [`deployments-base-sepolia.json`](./deployments-base-sepolia.json) — committed, public, and surfaced to TypeScript consumers via the generated `@agenticprimitives/contracts/deployments/base-sepolia` subpath.
|
|
29
|
+
|
|
30
|
+
## How it's different
|
|
31
|
+
|
|
32
|
+
Compared to Safe, ZeroDev, or Alchemy's contract stacks, the difference is scope and coupling. Those projects ship an account (and ship it well); the rest of the trust chain — who the account is, what it may delegate, how custody recovers it — is left to integrations with other vendors' contracts. Here the account, delegation manager, caveat enforcers, custody module, naming registry, and signature validator are designed and audited as one system, sharing one identity anchor. There is no third-party multi-sig dependency: custody quorums are our own ERC-7579 module, with Safe-style signature packing ported as a pattern rather than inherited as a runtime dependency. And the consumption model is different — most contract suites hand you addresses and a block explorer link; this package hands you versioned ABIs, deployments, and flattened sources as npm subpaths, CI-locked to the TypeScript SDK that consumes them.
|
|
13
33
|
|
|
14
34
|
## Setup
|
|
15
35
|
|
|
@@ -32,7 +52,7 @@ anvil
|
|
|
32
52
|
pnpm deploy:anvil
|
|
33
53
|
```
|
|
34
54
|
|
|
35
|
-
Writes deployed addresses to `deployments-anvil.json`. The
|
|
55
|
+
Writes deployed addresses to `deployments-anvil.json`. The demo apps (`apps/demo-web`, `apps/demo-a2a`, `apps/demo-mcp`) read this file on startup.
|
|
36
56
|
|
|
37
57
|
## Deploy to Base Sepolia
|
|
38
58
|
|
|
@@ -42,4 +62,19 @@ export PRIVATE_KEY=0x... # funded deployer
|
|
|
42
62
|
pnpm deploy:base-sepolia
|
|
43
63
|
```
|
|
44
64
|
|
|
45
|
-
Writes to `deployments-base-sepolia.json`.
|
|
65
|
+
Writes to `deployments-base-sepolia.json`. CI and hosted deploys read this. Demo idle cost: ~$0.
|
|
66
|
+
|
|
67
|
+
## Audit posture
|
|
68
|
+
|
|
69
|
+
Trust infrastructure should be the most transparent code you depend on, so the audit trail is public by default:
|
|
70
|
+
|
|
71
|
+
- [`AUDIT.md`](./AUDIT.md) — the security and invariant dossier for every contract under `src/`, including the production key-rotation runbook (§ 4.1).
|
|
72
|
+
- [`docs/audits/findings.yaml`](../../docs/audits/findings.yaml) — the live, CI-gated findings ledger; a "closed" finding must anchor to real source or the build fails.
|
|
73
|
+
- [`docs/audits/2026-05-packages-contracts-production-readiness.md`](../../docs/audits/2026-05-packages-contracts-production-readiness.md) — the per-contract findings dossier.
|
|
74
|
+
- [`CLAUDE.md`](./CLAUDE.md) — architecture rationale and per-file roles.
|
|
75
|
+
|
|
76
|
+
## Status
|
|
77
|
+
|
|
78
|
+
Testnet/pilot-ready. Production launch is gated on the public checklist in the root README — including third-party contract audit and governance key rotation. Track every security finding live in [`docs/audits/findings.yaml`](../../docs/audits/findings.yaml).
|
|
79
|
+
|
|
80
|
+
**Production deployment is deliberately deferred:** the testnet uses a publicly disclosed deployer EOA on purpose (it keeps the demo reproducible). The production rotation runbook is [`AUDIT.md` § 4.1](./AUDIT.md#41-the-production-gate-open-audit-item-n1).
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"agentAccountFactory": "0x3E68B72B45e7C9d35B210E4Ab06e5Cece85cEbE4",
|
|
3
|
+
"agentAccountImplementation": "0x338318ABf9C8b28E7e77ed96a91857cCE1Db1328",
|
|
4
|
+
"agentNameRegistry": "0x15F7ed064A230C011b0244A14fD9653f011d609B",
|
|
5
|
+
"agentNameResolver": "0xcC9855A23C7a4D4592E68Fe47E195C2f9D832473",
|
|
6
|
+
"agentNameUniversalResolver": "0x7d777d2d0bbc1806B9Cc779121C27fbaAaFDb60b",
|
|
7
|
+
"agentProfileResolver": "0xc448EB159B2a35F07f6E1814CC8d125244d7384B",
|
|
8
|
+
"agentRelationship": "0xE6B8F6C7F1c9857013dA356DF08D0384334A9607",
|
|
9
|
+
"agreementRegistry": "0x7f4d6A74eda1D6ce251D371566f1c6639F9B0DDa",
|
|
10
|
+
"allowedMethodsEnforcer": "0xf77cf06aCd8b9e2f3204fe4bE16449B3594c9633",
|
|
11
|
+
"allowedTargetsEnforcer": "0xe3909FdEfD8b74B7D006E045DCd678e97D469Bfd",
|
|
12
|
+
"approvedHashRegistry": "0x90735B5281434D1746926E2AA722Cc6DbF3EFEe6",
|
|
13
|
+
"attestationRegistry": "0xaE3861fd299c74D96F6FCA25f19c7ACf8b9020ed",
|
|
14
|
+
"callDataHashEnforcer": "0x9F12932F1b85A1F96c1Cb1c5A0645e3F0Ab478Dd",
|
|
15
|
+
"chainId": 84532,
|
|
16
|
+
"custodyPolicy": "0xDeD6542A55476745846370562d1c75D15DDf2572",
|
|
17
|
+
"delegationManager": "0x3a8E2cE74564f699b135db6f266ccDb563979C05",
|
|
18
|
+
"deployer": "0x31ed17fb99e82E02085Ab4B3cbdaB05489098b44",
|
|
19
|
+
"entryPoint": "0x9B33De9d9597de82Eb56b5B26C478Dc0a6955388",
|
|
20
|
+
"geoFeatureRegistry": "0x78FdCae0aC9B0CD9943996282b8F39D77bec5b1c",
|
|
21
|
+
"ontologyTermRegistry": "0xf486Ba6e9FD2d687e68a1a7A77D0FA27bb1E66c3",
|
|
22
|
+
"permissionlessSubregistry": "0x1B8ED8693738e1A9DD653FEE5430d49e00202Bb7",
|
|
23
|
+
"permissionlessSubregistryDemoAgent": "0xc76b1cdB4340398f59fD9C5d6Cb65fCdC606B33b",
|
|
24
|
+
"quorumEnforcer": "0x76fb494aE991d1fe1603a91384BA4c1F890442a8",
|
|
25
|
+
"relationshipTypeRegistry": "0x526fBF5900b18B1D06201493bcE470b416FB71C3",
|
|
26
|
+
"shapeRegistry": "0x03AbD67A910abB2c5026f1973A05502e0473e316",
|
|
27
|
+
"skillDefinitionRegistry": "0x5ae94aba46D7daA1Ca9eaE0051043Ad05bcc392B",
|
|
28
|
+
"smartAgentPaymaster": "0x8eF92B9D62826052D8F7e6dcaB630dC3890bF540",
|
|
29
|
+
"timestampEnforcer": "0x7c452434314A5bf2048f9263aeCd0cC20Fa07965",
|
|
30
|
+
"universalSignatureValidator": "0xe8F3e531429F44D77dC3A331C18032bfa1550D84",
|
|
31
|
+
"valueEnforcer": "0x7c4533e0A9fB0B1cb5aa70327b222a80d72d52db",
|
|
32
|
+
"paymentEnforcer": "0xAF48273e11435cC2D56f4AFBfD091Abb162458E9",
|
|
33
|
+
"paymentReceiptRegistry": "0x366616E265cd3cDE0F042A592C17838fe210D1d4",
|
|
34
|
+
"mockUsdc": "0x8fb56ff3C13347DFC4E1287aE83E88deE5a7211C",
|
|
35
|
+
"paymentEscrow": "0x954Ba6B3A02E02c5a3Fcd570943126633071cbdD"
|
|
36
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"agreementRegistry":"0x9f2A085f0bd2d8Afb69490C0C98D88592093906A","attestationRegistry":"0x4a156CAaE69E894E3C5e6F3064fE028F138b6847"}
|