> For the complete documentation index, see [llms.txt](https://gotts.gitbook.io/docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://gotts.gitbook.io/docs/prd-shared/erc8004-integration.md).

# ERC-8004 Integration

> **Type**: SPEC (normative) | **Last Updated**: 2026-02-21
>
> Single normative reference for ERC-8004 identity registration patterns across Gotts. All other PRDs that reference ERC-8004 registration MUST cross-reference this document rather than duplicating SDK examples or registration flows.

***

## 1. EIP-1193 Provider Bridge

The `@gotts.ai/wallet` package exposes a `createEIP1193Provider(wallet)` function that returns a minimal EIP-1193-compliant object. This bridges any `GottsWallet` instance to the Agent0 SDK's `walletProvider` config option (v1.5.0+).

### Method Routing

| Method Category        | Examples                                                       | Routed To                                                       |
| ---------------------- | -------------------------------------------------------------- | --------------------------------------------------------------- |
| **Account queries**    | `eth_accounts`, `eth_requestAccounts`, `eth_chainId`           | Handled locally (returns wallet address / chain ID from config) |
| **Signing operations** | `eth_sendTransaction`, `personal_sign`, `eth_signTypedData_v4` | Wallet backend (Privy TEE enclave or local viem signer)         |
| **All other JSON-RPC** | `eth_call`, `eth_getBalance`, `eth_getLogs`, `eth_blockNumber` | Proxied to configured chain RPC URL                             |

### Two Modes

| Mode                    | Signing Path                                                                                  | Key Storage                                                         |
| ----------------------- | --------------------------------------------------------------------------------------------- | ------------------------------------------------------------------- |
| **Privy** (production)  | P-256 auth key authorizes request to Privy API; Privy TEE signs with secp256k1 inside enclave | Signing key never leaves TEE; auth key in `.env` or secrets manager |
| **Local** (dev/testing) | Signs locally with `viem` `privateKeyToAccount`                                               | Raw private key in memory; no TEE isolation, no policy enforcement  |

### Interface

```typescript
export interface GottsEIP1193Provider {
  request(args: {
    method: string;
    params?: unknown[] | Record<string, unknown>;
  }): Promise<unknown>;
}

export function createEIP1193Provider(
  wallet: GottsWallet,
): GottsEIP1193Provider;
```

The returned provider is consumed by Agent0 SDK:

```typescript
import { SDK } from "@agent0/sdk";
import { GottsWallet, createEIP1193Provider } from "@gotts.ai/wallet";

const wallet = new GottsWallet({
  mode: "privy",
  appId,
  appSecret,
  walletId,
  authPrivateKey,
});
const provider = createEIP1193Provider(wallet);
const sdk = new SDK({ chainId: 8453, rpcUrl, walletProvider: provider });
```

The same P-256 auth key and Privy credentials that sign transactions also power Agent0 SDK registration -- no additional credentials are needed. See [credential-architecture.md](/docs/prd-shared/credential-architecture.md) Section 6b.

***

## 2. Three-Tier Registration Fallback

`@gotts.ai/wallet` exposes a `registerAgent()` function that attempts registration using three tiers, falling back automatically:

### Tier 1: Agent0 SDK + Gotts IPFS Proxy (default)

Full discoverability. AgentCard JSON is uploaded to IPFS via the Gotts-hosted pinning proxy (`api.gotts.ai/v1/ipfs/pin`), then registered on-chain via Agent0 SDK. Zero IPFS config needed from the operator.

```typescript
import { GottsWallet, registerAgent } from "@gotts.ai/wallet";

const wallet = new GottsWallet({
  mode: "privy",
  appId,
  appSecret,
  walletId,
  authPrivateKey,
});
const result = await registerAgent(wallet, {
  name: "My Vault Manager",
  description: "Autonomous USDC yield optimizer on Base",
  role: "vault_manager",
  mcpEndpoint: "https://my-agent.fly.dev/mcp",
});
// result.agentId = "42"
// result.registrationMethod = "agent0-ipfs"
// result.agentURI = "ipfs://Qm..."
```

**Requirements**: `agent0-sdk` installed (>=1.5.0), network access to `api.gotts.ai`.

### Tier 2: Agent0 SDK + Inline Metadata URI

No IPFS needed. Metadata is encoded as a `data:` URI and stored on-chain directly. Reduced discoverability (no IPFS content hash for other agents to resolve), but zero external dependencies.

```typescript
const result = await registerAgent(wallet, {
  name: "My Agent",
  role: "vault_participant",
  ipfsMode: "inline",
});
// result.registrationMethod = "agent0-inline"
// result.agentURI = "data:application/json;base64,eyJuYW1lIjoi..."
```

**Requirements**: `agent0-sdk` installed (>=1.5.0). No network calls beyond the on-chain transaction.

### Tier 3: Direct Contract Call via viem

No Agent0 SDK dependency. Calls `identityRegistry.register(handle, metadataURI, interfaceType)` directly via viem. Minimal metadata. Useful for environments where `agent0-sdk` is not installed or when SDK initialization fails.

```typescript
// registerAgent() falls back to this automatically if agent0-sdk is not installed
const result = await registerAgent(wallet, {
  name: "my-agent",
  role: "vault_participant",
  ipfsMode: "inline",
});
// result.registrationMethod = "direct-contract"
```

**Requirements**: None beyond `@gotts.ai/wallet` itself.

### Fallback Logic

```
1. Is agent0-sdk installed? (dynamic import check)
   ├─ No → Tier 3 (direct contract call)
   └─ Yes → Is ipfsMode 'inline'?
        ├─ Yes → Tier 2 (SDK + inline URI)
        └─ No → Try Tier 1 (SDK + IPFS proxy)
             └─ IPFS proxy fails? → Fall back to Tier 2
```

***

## 3. IPFS Upload Strategy

Three modes for AgentCard metadata storage, controlled by the `GOTTS_IPFS_MODE` env var or the `ipfsMode` option in `registerAgent()`:

| Mode                   | Value             | Description                                                                                    | Operator Config Required    |
| ---------------------- | ----------------- | ---------------------------------------------------------------------------------------------- | --------------------------- |
| **Gotts proxy**        | `gotts` (default) | Upload to `api.gotts.ai/v1/ipfs/pin`. Gotts pins to IPFS via Pinata on behalf of the operator. | None (zero-config)          |
| **Self-hosted Pinata** | `pinata`          | Upload directly to Pinata using operator's own JWT. Full control over pinning.                 | `GOTTS_PINATA_JWT` required |
| **Inline**             | `inline`          | Encode metadata as a `data:` URI. No IPFS involved. Stored on-chain.                           | None                        |

### Gotts IPFS Proxy API

Part of the `api.gotts.ai` service (see [Section 8](#8-gotts-api-service-apigottsai) below).

**`POST /v1/ipfs/pin`**:

```json
// Request
{
  "agentCard": {
    "name": "My Agent",
    "description": "...",
    "domains": [...],
    "services": [...],
    "trust": {...}
  }
}

// Response (200)
{
  "cid": "QmXyz...",
  "uri": "ipfs://QmXyz...",
  "gatewayUrl": "https://gateway.pinata.cloud/ipfs/QmXyz..."
}
```

**Authentication**: Request includes the operator's `privyAppId` in a Bearer token. The proxy verifies the App ID is valid by calling Privy's public config API (`GET https://auth.privy.io/api/v1/apps/{appId}/config`). No separate API key needed.

**Rate limits**: 10 pins per Privy App per hour. Each pin is immutable (CID-addressed).

***

## 4. AgentCard Metadata Templates

The `registerAgent()` function generates AgentCard metadata based on the `role` parameter. Templates follow the ERC-8004 AgentCard schema.

### Participant Template

```json
{
  "name": "my-yield-agent",
  "description": "Gotts agent on Base",
  "domains": [
    { "domain": "finance_and_business/investment_services", "primary": true }
  ],
  "services": [{ "type": "mcp", "url": "https://my-agent.fly.dev/mcp" }],
  "trust": {
    "reputation": true,
    "cryptoEconomic": true,
    "zkml": false
  },
  "metadata": {
    "protocol:name": "Gotts",
    "role": "vault_participant",
    "vault:chain": "8453"
  }
}
```

### Manager Template

Extends Participant with additional domain and optional A2A endpoint:

```json
{
  "domains": [
    { "domain": "finance_and_business/investment_services", "primary": true },
    { "domain": "finance_and_business/asset_management", "primary": false }
  ],
  "services": [
    { "type": "mcp", "url": "https://my-agent.fly.dev/mcp" },
    { "type": "a2a", "url": "https://my-agent.fly.dev/.well-known/agent.json" }
  ],
  "metadata": {
    "role": "vault_manager"
  }
}
```

### Creator Template

Extends Manager with software development domain:

```json
{
  "domains": [
    { "domain": "finance_and_business/investment_services", "primary": true },
    { "domain": "finance_and_business/asset_management", "primary": false },
    { "domain": "technology/software_development", "primary": false }
  ],
  "metadata": {
    "role": "vault_creator"
  }
}
```

All templates are customizable via the `AgentRegistrationConfig` options. The templates provide sensible defaults; operators can override any field.

***

## 5. Chain Support Matrix

ERC-8004 Identity Registry deployments and their status:

| Chain            | Chain ID | Registry Deployed | Gotts Support      |
| ---------------- | -------- | ----------------- | ------------------ |
| Ethereum mainnet | 1        | Yes               | Yes (registration) |
| Sepolia          | 11155111 | Yes               | Yes (testing)      |
| Base             | 8453     | Yes               | Yes (primary)      |
| Polygon          | 137      | Yes               | Yes                |
| Base Sepolia     | 84532    | Yes               | Yes (testing)      |
| Optimism         | 10       | No                | Pending            |
| Arbitrum         | 42161    | No                | Pending            |
| BNB Chain        | 56       | No                | Pending            |
| Avalanche        | 43114    | No                | Pending            |
| Celo             | 42220    | No                | Pending            |
| Blast            | 81457    | No                | Pending            |
| Unichain         | 130      | No                | Pending            |
| zkSync Era       | 324      | No                | Pending            |

**Registry address** (same across all deployed chains): `0x8004A818BFB912233c491871b3d84c89A494BD9e`

The `registerAgent()` function validates that the target chain has an ERC-8004 registry deployment before attempting registration. If the chain is unsupported, it returns a `VALIDATION_CHAIN_NOT_SUPPORTED` error with a suggestion to use a supported chain.

***

## 6. Gas Strategy

Three-tier gas resolution for identity registration transactions. The install wizard and `registerAgent()` function both use this strategy:

### Tier 1: Paymaster-Sponsored (default on Base mainnet)

ERC-4337 UserOp with ERC-7677 paymaster. Zero ETH required from the operator.

```
wallet.sendUserOp({
  target: identityRegistryAddress,
  data: registerCalldata,
  paymaster: paymasterAddress,
})
```

**When**: Base mainnet (chain ID 8453). An ERC-7677-compliant paymaster sponsors identity registration as a qualifying operation.

### Tier 2: Testnet Faucet (testnet only)

The wizard automatically requests 0.01 ETH from the Gotts faucet API. One-time per wallet per chain, authenticated via Privy App ID.

```
POST api.gotts.ai/v1/faucet/request
{ "address": "0x...", "chainId": 84532, "privyAppId": "clx..." }
```

**When**: Sepolia (11155111) or Base Sepolia (84532), wallet balance is zero.

See [testnet-faucet.md](/docs/prd-shared/testnet-faucet.md) for the full faucet API spec.

### Tier 3: Manual Funding (fallback)

Show the wallet address and ask the operator to send ETH. Wait up to 60 seconds for the balance to appear, then offer to defer registration.

```
Send ~0.005 ETH to 0x1a2B...3c4D on Base
Waiting for funds... (60s timeout)
```

**When**: Mainnet without paymaster support, or when Tier 1/2 both fail.

### Resolution Flow

```
1. Is this a testnet (Sepolia, Base Sepolia)?
   → Check balance. If zero, call Gotts faucet API
   → If faucet succeeds: proceed with registration
   → If faucet fails (already claimed / rate limited): show address, ask operator to fund

2. Is this Base mainnet?
   → Try paymaster-sponsored ERC-4337 UserOp (ERC-7677 paymaster)
   → If paymaster succeeds: zero ETH needed, proceed
   → If paymaster fails: fall back to direct tx, check balance

3. Is wallet funded?
   → Yes: proceed with registration
   → No: show address + "Send ~0.005 ETH to 0x1a2B...3c4D", wait up to 60s, offer "Later"
```

***

## 7. Testing

### Local Devenv (Anvil)

The devenv package deploys a mock ERC-8004 Identity Registry to the local Anvil fork. Registration can be tested end-to-end:

```bash
cd packages/devenv
pnpm devenv          # Start Anvil + deploy contracts (includes mock registry)
```

```typescript
import { GottsWallet, registerAgent } from "@gotts.ai/wallet";

const wallet = new GottsWallet({
  mode: "local",
  privateKey:
    "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80",
  chainId: 31337,
});

const result = await registerAgent(wallet, {
  name: "test-agent",
  role: "vault_participant",
  ipfsMode: "inline",
});
// Registers on the local Anvil fork
```

### MSW Mocks (`@gotts.ai/test-utils`)

The test-utils package provides MSW handlers for:

* **Agent0 SDK mocks**: Intercepts SDK registration calls, returns deterministic `agentId` and `agentURI`
* **ERC-8004 Identity Registry contract mock**: Simulates `register()` call and event emission for direct contract fallback testing
* **IPFS proxy mock**: Intercepts `api.gotts.ai/v1/ipfs/pin`, returns deterministic CID
* **Faucet mock**: Intercepts `api.gotts.ai/v1/faucet/request`, returns success or 409 (already claimed)

```typescript
import {
  createTestServer,
  agent0Handlers,
  faucetHandlers,
} from "@gotts.ai/test-utils";

const server = createTestServer(agent0Handlers, faucetHandlers);
beforeAll(() => server.listen());
afterEach(() => server.resetHandlers());
afterAll(() => server.close());
```

***

## 8. Gotts API Service (`api.gotts.ai`)

The IPFS proxy and testnet faucet share a single minimal API service deployed on Fly.io. Two route groups, one deployment.

| Route Group          | Endpoints | Auth                      | Purpose                                   |
| -------------------- | --------- | ------------------------- | ----------------------------------------- |
| `/v1/ipfs/pin`       | `POST`    | Privy App ID verification | Pin AgentCard JSON to IPFS, return CID    |
| `/v1/faucet/request` | `POST`    | Privy App ID verification | Send testnet ETH to a wallet              |
| `/v1/faucet/status`  | `GET`     | None                      | Health check and remaining faucet balance |

**Authentication pattern** (shared): Requests include the operator's `privyAppId`. The service verifies the App ID by calling Privy's public API (`GET https://auth.privy.io/api/v1/apps/{appId}/config`). Valid response = legitimate operator. This prevents unauthenticated abuse while keeping the flow zero-config.

**Implementation**: Single Node.js service (\~200 LOC total). Dependencies: `express`, `viem`, `better-sqlite3`. Two SQLite tables: `pins` (CID, appId, createdAt) and `claims` (address, chainId, appId, txHash, createdAt). Deployed on Fly.io. Faucet wallet private key and Pinata JWT stored as Fly.io secrets.

***

## Cross-References

| Document                                                                                             | What It References                                                                                                                         |
| ---------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| [onboarding-workflow.md](/docs/prd-shared/onboarding-workflow.md)                                    | Path A Step 2 uses `registerAgent()` from `@gotts.ai/wallet`                                                                               |
| [credential-architecture.md](/docs/prd-shared/credential-architecture.md)                            | Section 6b references `createEIP1193Provider()` as the canonical bridge; Section 7a includes Identity Registry in policy preset allowlists |
| [mcp-server/11-config.md](/docs/gotts-safe-mcp-server/mcp-server/11-config.md)                       | ERC-8004 Agent Identity env vars (`GOTTS_IPFS_MODE`, `GOTTS_AGENT_NAME`, etc.)                                                             |
| [mcp-server/15-install-wizard.md](/docs/gotts-safe-mcp-server/mcp-server/15-install-wizard.md)       | Step 3/6 calls `registerAgent()` with gas resolution logic                                                                                 |
| [monorepo/11-primitive-packages.md](/docs/monorepo-infrastructure/monorepo/11-primitive-packages.md) | `@gotts.ai/wallet` API shape includes `createEIP1193Provider()` and `registerAgent()`                                                      |
| [testnet-faucet.md](/docs/prd-shared/testnet-faucet.md)                                              | Full API spec for the testnet faucet used in gas strategy Tier 2                                                                           |
