# ERC-8004 Mapping (Trustless Agents)

**Status:** Informational
**Version:** 0.1
**EIP Reference:** [EIP-8004](https://eips.ethereum.org/EIPS/eip-8004) (Draft)

> **Note:** EIP-8004 is currently in Draft status. This mapping aligns with the Draft specification as of January 2026. Monitor the EIP for updates before production deployment.

This document specifies how to use **PEAC receipts** as the off-chain evidence payload for ERC-8004 reputation events.

ERC-8004's pattern is:

- An on-chain event includes:
  - a locator (`feedbackURI`) to an off-chain object
  - a commitment (`feedbackHash`) to the exact bytes retrieved via that locator

PEAC fits as the off-chain object: a signed, portable interaction record that can be hosted anywhere.

---

## Scope

This is a **mapping and interoperability guide**. It does not change PEAC's core wire format and does not add Ethereum dependencies to PEAC core packages.

---

## Compatibility Contract

This section clarifies what is normative versus best-effort in this mapping.

**Normative for PEAC implementers:**

- Hash computation: `keccak256(utf8_bytes(JCS(payload)))`
- Canonicalization: RFC 8785 JCS (not `JSON.stringify`)
- Wrapper fields (Mode B): `peac.typ`, `peac.uri`, `peac.sha256`

**Best-effort (implementation guidance):**

- Receipt URI durability and hosting patterns
- Content-Type expectations (`application/json; charset=utf-8`)
- Cache headers and CDN configuration

**Explicitly out of scope:**

- Identity semantics (agent registration, key management)
- Economic settlement (payment finality, dispute resolution)
- On-chain storage or verification of PEAC receipts
- ERC-8004 contract deployment or registry addresses

---

## Preconditions

- You can produce a PEAC receipt for an interaction (API call, agent action, tool invocation, crawl, etc.).
- You can host that receipt at a stable URI.
- You can compute a deterministic hash over the receipt bytes.

---

## Mapping Summary

### On-Chain Fields (ERC-8004)

| ERC-8004 field | Source                    | Notes                                       |
| -------------- | ------------------------- | ------------------------------------------- |
| `feedbackURI`  | Receipt hosting URI       | MUST serve a stable, byte-exact payload     |
| `feedbackHash` | `keccak256(JCS(receipt))` | Commitment to deterministic canonical bytes |

All other `giveFeedback` parameters (`agentId`, `value`, `valueDecimals`, `tag1`, `tag2`, `endpoint`) are application-specific and not dictated by PEAC.

### Off-Chain Payload

- The off-chain payload referenced by `feedbackURI` SHOULD be the **PEAC receipt JSON** itself.
- The receipt MUST NOT embed `feedbackHash` (it would be a circular dependency).

---

## Receipt Canonicalization and Hashing

ERC-8004 expects `feedbackHash` to commit to bytes. JSON is only safe for this if the producer and verifier agree on a canonical serialization.

### Canonicalization

Use **RFC 8785 JSON Canonicalization Scheme (JCS)** to produce a deterministic JSON string for hashing.

Rules (high level):

- Object keys are sorted lexicographically.
- No insignificant whitespace.
- UTF-8 encoding.
- Numbers normalized per JCS.

### Hash Computation

```text
feedbackHash = keccak256( utf8_bytes( JCS(feedback_payload_json) ) )
```

The `feedback_payload_json` is the document served at `feedbackURI`:

- **Mode A (Direct):** The PEAC receipt itself
- **Mode B (Wrapper):** A wrapper file that references the PEAC receipt via `peac.sha256`

Notes:

- `keccak256` is the same hashing function used broadly in Ethereum tooling.
- The committed bytes are the **UTF-8 bytes of the canonical JSON string**.

```typescript
import { keccak256 } from 'viem';
import { canonicalize } from '@peac/crypto'; // RFC 8785 JCS

function computeFeedbackHash(feedbackPayload: object): `0x${string}` {
  // IMPORTANT: Use canonical serialization, not JSON.stringify
  // JSON.stringify is NOT deterministic (key order, spacing varies)
  const canonicalJson = canonicalize(feedbackPayload);
  const bytes = new TextEncoder().encode(canonicalJson);
  return keccak256(bytes);
}
```

**Warning:** `JSON.stringify()` is not canonical: key order and spacing vary across implementations. Always use JCS canonicalization when computing `feedbackHash`.

**Note:** PEAC's internal digest uses SHA-256 + JCS. When bridging to ERC-8004, use keccak256 (Ethereum's hash function) on the same canonical bytes.

### Byte Commitment Definition

To ensure cross-implementation compatibility, the committed bytes MUST be:

- **Encoding:** UTF-8 (no BOM)
- **Canonicalization:** RFC 8785 JCS
- **No trailing content:** No trailing whitespace or newlines beyond what JCS produces
- **Document:** The payload served at `feedbackURI` (receipt for Mode A, wrapper for Mode B)

This definition prevents "why doesn't my hash match" issues across different stacks.

---

## Integration Modes

ERC-8004 only commits to `feedbackURI` and `feedbackHash`. Implementations typically want additional context for indexing and discovery.

### Mode A: EIP-Valid Minimal

- `feedbackURI` points to the PEAC receipt.
- The receipt contains **no ERC-8004 registry metadata**.
- Indexers resolve `agentRegistry`, `reputationRegistry`, chain, etc., from:
  - the ERC-8004 event log (contract address + chain)
  - app-level configuration

This mode is maximally portable and keeps the receipt purely about the interaction.

### Mode B: Indexer-Friendly

- `feedbackURI` still points to the PEAC receipt.
- The receipt may include a non-normative `custom.erc8004` object containing:
  - `agentRegistry` address
  - `reputationRegistry` address
  - optional `chain` (CAIP-2 or EIP-155)

This mode reduces out-of-band configuration for indexers while keeping on-chain arguments unchanged.

In both modes, `feedbackHash` MUST be computed over the entire canonical receipt JSON as served at `feedbackURI`.

---

## Hosting and Byte-Exactness

If the bytes served at `feedbackURI` change, verifiers will compute a different `feedbackHash`.

### Server Requirements

Servers hosting feedback payloads:

- MUST NOT apply content-coding (no `Content-Encoding: gzip`, `br`, etc.)
- SHOULD set `Cache-Control: public, max-age=31536000, immutable, no-transform`
- SHOULD set `Content-Type: application/json; charset=utf-8`

### Verifier Requirements

Verifiers fetching feedback payloads:

- MUST send `Accept-Encoding: identity` to prevent server-side compression
- MUST hash the exact response body bytes received

### CDN/Proxy Guidance

Disable transformations that modify response body bytes:

- **Cloudflare:** Disable "Auto Minify" and "Rocket Loader"
- **AWS CloudFront:** Set `Compress: false` on the behavior
- **Fastly:** Disable automatic gzip/brotli in VCL

### Storage Backends

- Immutable URLs (content-addressed storage, versioned object keys, immutable CDN cache) are preferred.
- If using IPFS, ensure the **exact same canonical bytes** are pinned and fetched.
- For IPFS or other content-addressed URIs, `feedbackHash` can be `bytes32(0)` since the URI itself commits to the content.

---

## Retrieval and Indexing

ERC-8004 events store the URI and hash in logs, not in contract storage. Indexers SHOULD:

1. Read the `FeedbackGiven` event log.
2. Fetch `feedbackURI` (byte-exact, no transformation).
3. Canonicalize with RFC 8785 JCS.
4. Compute `keccak256`.
5. Compare to `feedbackHash`.

---

## Identifier Format

ERC-8004 defines two separate fields per EIP-8004:

### Agent Registry

```text
agentRegistry = "{namespace}:{chainId}:{identityRegistry}"
```

Examples:

- Ethereum Mainnet: `eip155:1:0x<identityRegistryAddress>`
- Sepolia Testnet: `eip155:11155111:0x<identityRegistryAddress>`

> **Note:** Always verify current registry addresses from the [erc-8004-contracts](https://github.com/erc-8004/erc-8004-contracts) repository before production use.

### Agent ID

The `agentId` is the ERC-721 tokenId assigned by the registry (uint256).

These are **two separate fields**, not a combined string:

```json
{
  "agentRegistry": "eip155:1:0x<identityRegistryAddress>",
  "agentId": 1234
}
```

**Important:** EIP-8004 does not define a combined single-string identifier format. Always use the structured two-field representation.

---

## Example Implementation

A working example is provided at:

- `examples/erc8004-feedback/`

It demonstrates:

- Computing `feedbackHash` from a receipt via JCS + `keccak256`
- Generating `giveFeedback` call arguments
- Keeping `feedbackHash` out of the receipt

### Test Vectors

#### Mode A: Direct PEAC Receipt

| Component               | Value                                                                |
| ----------------------- | -------------------------------------------------------------------- |
| Input                   | `specs/conformance/erc8004-mapping/golden-receipt.json`              |
| Canonicalization        | RFC 8785 JCS (via `@peac/crypto`)                                    |
| Hash Algorithm          | keccak256                                                            |
| Expected `feedbackHash` | `0xf6194f761fdecef4eaf577f04465229551185e5e8e716d30d955f20eae12fafc` |

#### Mode B: Wrapper File

| Component               | Value                                                                |
| ----------------------- | -------------------------------------------------------------------- |
| Input                   | `specs/conformance/erc8004-mapping/golden-wrapper.json`              |
| Canonicalization        | RFC 8785 JCS (via `@peac/crypto`)                                    |
| Hash Algorithm          | keccak256                                                            |
| Expected `feedbackHash` | `0x58a0056caf5fb01a324b023bfac341b5fdef8fb8b21e7f7a4d0f279de3ec1083` |

**Note:** In Mode B, the wrapper's `peac.sha256` field provides independent verification of the referenced PEAC receipt.

### Verification

```bash
cd examples/erc8004-feedback
pnpm verify
```

---

## Security Considerations

- TLS is REQUIRED for HTTPS URIs.
- Receipts SHOULD be signed and verifiable independently of the URI transport.
- Treat `feedbackURI` as an untrusted input: enforce size limits, timeouts, and content-type checks.
- Ensure verifiers use the exact same canonicalization algorithm (RFC 8785), not generic `JSON.stringify`.

### URI Mutability (TOCTOU)

ERC-8004 commits to bytes at a URI; URIs can change over time.

**Threat:** Mutable `feedbackURI` allows attestation to point to different bytes later.

**Mitigations:**

- Use content-addressed URIs (IPFS, Arweave) where the URI itself commits to the content
- Use immutable hosting with versioned URLs
- PEAC-signed receipts provide independent verification regardless of URI stability

---

## Non-Goals

This mapping does NOT:

- Add Ethereum dependencies to PEAC core packages
- Require on-chain verification for PEAC receipt validity
- Make ERC-8004 registration mandatory for PEAC agents
- Change PEAC's internal digest algorithm (SHA-256 + JCS)
- Define a combined single-string identifier format (use structured fields)

ERC-8004 is an **optional identity anchor** and **reputation distribution channel**. PEAC verification remains fully functional without any blockchain interaction.

---

## Related Documentation

- [Agent Identity](../specs/AGENT-IDENTITY.md) - PEAC agent identity attestations
- [EIP-8004](https://eips.ethereum.org/EIPS/eip-8004) - Trustless Agents specification
- [erc-8004-contracts](https://github.com/erc-8004/erc-8004-contracts) - Reference implementation
- [RFC 8785](https://www.rfc-editor.org/rfc/rfc8785) - JSON Canonicalization Scheme

---

## Version History

| Version | Date       | Changes                  |
| ------- | ---------- | ------------------------ |
| 0.1     | 2026-01-31 | Initial mapping document |
