home / skills / openclaw / skills / 8004-skill-monad

8004-skill-monad skill

/skills/fabriziogianni7/8004-skill-monad

This skill registers and manages ERC-8004 on-chain identities on Monad, enabling agent onboarding for CEO Protocol and other ERC-8004 integrations.

npx playbooks add skill openclaw/skills --skill 8004-skill-monad

Review the files below or copy the command above to add this skill to your agents.

Files (12)
SKILL.md
11.1 KB
---
name: 8004-skill
description: Register and manage ERC-8004 Identity NFTs on Monad. Use when the agent needs to mint an on-chain identity for CEO Protocol registration or other ERC-8004–integrated protocols.
---

# ERC-8004 Identity Skill

Use this skill when the agent must register on the ERC-8004 Identity Registry to obtain an on-chain identity NFT. This identity is **required** to register as an agent in The CEO Protocol (CEOVault).

Reference: [EIP-8004 Trustless Agents](https://eips.ethereum.org/EIPS/eip-8004)

## Contract Address (Monad Mainnet)

| Contract | Address |
|----------|---------|
| ERC-8004 Identity | `0x8004A169FB4a3325136EB29fA0ceB6D2e539a432` |

## Interface Summary

The Identity Registry is ERC-721 based. Registering mints an NFT to `msg.sender`; the token ID is the agent ID.

### Write Functions

| Function | Purpose |
|----------|---------|
| `register(string agentURI)` | Register with a URI; mints NFT, returns `agentId` |
| `register(string agentURI, MetadataEntry[] metadata)` | Register with URI and on-chain metadata |
| `register()` | Register with no URI (set later via `setAgentURI`) |
| `setAgentURI(uint256 agentId, string newURI)` | Update the agent's URI |
| `setMetadata(uint256 agentId, string metadataKey, bytes metadataValue)` | Set on-chain metadata |

### Read Functions (view)

| Function | Returns | Use |
|----------|---------|-----|
| `ownerOf(uint256 tokenId)` | `address` | Check who owns an agent NFT |
| `tokenURI(uint256 tokenId)` | `string` | Get agent URI (same as agentURI) |
| `getAgentWallet(uint256 agentId)` | `address` | Get wallet linked to agent |
| `getMetadata(uint256 agentId, string metadataKey)` | `bytes` | Get on-chain metadata |

### Events

| Event | Use |
|-------|-----|
| `Registered(uint256 indexed agentId, string agentURI, address indexed owner)` | Emitted on mint |
| `URIUpdated(uint256 indexed agentId, string newURI, address indexed updatedBy)` | Emitted on URI change |
| `MetadataSet(uint256 indexed agentId, string indexed metadataKey, string metadataKey, bytes metadataValue)` | Emitted on metadata set |

## Registration Data Template

The `agentURI` must resolve to a JSON document conforming to EIP-8004 registration. Use this template and replace placeholders before hosting (IPFS or data URI):

```json
{
  "type": "https://eips.ethereum.org/EIPS/eip-8004#registration-v1",
  "name": "AGENT_NAME",
  "description": "AGENT_DESCRIPTION",
  "image": "https://example.com/agent-image.png",
  "services": [
    {
      "name": "A2A",
      "endpoint": "https://YOUR_DOMAIN/.well-known/agent-card.json",
      "version": "0.3.0"
    },
    {
      "name": "MCP",
      "endpoint": "https://YOUR_DOMAIN/mcp",
      "version": "2025-06-18"
    }
  ],
  "x402Support": false,
  "active": true,
  "registrations": [],
  "supportedTrust": [
    "reputation"
  ]
}
```

| Field | Replace with |
|-------|--------------|
| `AGENT_NAME` | Agent display name |
| `AGENT_DESCRIPTION` | Short description of capabilities |
| `image` | URL to agent avatar/image |
| `YOUR_DOMAIN` | Your domain for A2A/MCP endpoints (or omit services if not applicable) |
| `supportedTrust` | Trust models (e.g. `["reputation"]` for CEO Protocol) |

For a minimal CEO Protocol–only registration, you can omit `services` or set them to empty; `supportedTrust: ["reputation"]` is typical.

## Automated Scripts (preferred)

The Docker image includes production-ready scripts at:

`/opt/erc8004-scripts`

Source in workspace:

`/root/.openclaw/workspace/skills/8004-skill/scripts`

### Required env vars for script flow

- `MONAD_RPC_URL`
- `MONAD_CHAIN_ID=143` (or pass `--chainId`)
- `AGENT_PRIVATE_KEY`
- `PINATA_JWT`
- `PINATA_GATEWAY` (recommended for verification fetch)

### Script commands

```bash
# 1) Register on-chain with empty URI -> returns agentId
node /opt/erc8004-scripts/register.mjs --network monad-mainnet

# 2) Build card JSON with registrations[] embedded
node /opt/erc8004-scripts/build-card.mjs \
  --network monad-mainnet \
  --agentId 42 \
  --template /root/.openclaw/workspace/skills/8004-skill/assets/registration-template.json \
  --name "CEO-1" \
  --description "Autonomous strategist for The CEO Protocol" \
  --out /tmp/agent-42.json

# 3) Upload to Pinata -> returns ipfs://CID
node /opt/erc8004-scripts/upload-pinata.mjs --file /tmp/agent-42.json

# 4) Set token URI on-chain
node /opt/erc8004-scripts/set-agent-uri.mjs \
  --network monad-mainnet \
  --agentId 42 \
  --uri ipfs://CID

# 5) Verify owner, tokenURI, wallet, and registrations[] match
node /opt/erc8004-scripts/verify.mjs --network monad-mainnet --agentId 42
```

### One-shot command

```bash
node /opt/erc8004-scripts/full-register.mjs \
  --network monad-mainnet \
  --name "CEO-1" \
  --description "Autonomous strategist for The CEO Protocol" \
  --template /root/.openclaw/workspace/skills/8004-skill/assets/registration-template.json \
  --outCard /tmp/agent-card.json \
  --identityFile /root/.openclaw/workspace/AGENT_IDENTITY.md
```

This executes all 4 registration steps (register -> build card -> upload -> set URI) and writes identity state for later CEO Protocol onboarding.

## Registration Flow

1. **Prerequisites**
   - Wallet with MON for gas (use `viem-local-signer address` to confirm signer).
   - `agentURI`: a URI pointing to your registration JSON (use the template above). Use IPFS (`ipfs://...`) or a data URI (`data:application/json;base64,...`).

2. **Call `register(agentURI)`**
   - Encode calldata with `encodeFunctionData`.
   - Send via `viem-local-signer send-contract`.
   - Parse `Registered` event or return value for `agentId`.

3. **Store `agentId`**
   - The returned `agentId` (token ID) is required for CEO Protocol `registerAgent(metadataURI, ceoAmount, erc8004Id)`.
   - Persist it in an identity file (see below).

## Identity File Template

After registration, persist the on-chain identity so the agent can reference it for CEO Protocol and other flows. Use this template:

```markdown
# Agent Identity
- **Address**: `<NOT SET>`
- **Agent ID**: `<NOT SET>`
- **Agent Registry**: `<NOT SET>`
- **Chain ID**: `<NOT SET>`
```

### How to fill it

| Field | Source | Example |
|-------|--------|---------|
| **Address** | `viem-local-signer address` (signer wallet) | `0xB4AF3708DA37a485E84b4F09c146eD0A8B7Df5c4` |
| **Agent ID** | Return value from `register(agentURI)` | `42` |
| **Agent Registry** | ERC-8004 Identity contract (Monad: `eip155:143:0x8004A169FB4a3325136EB29fA0ceB6D2e539a432`) | `eip155:143:0x8004A169FB4a3325136EB29fA0ceB6D2e539a432` |
| **Chain ID** | Monad mainnet | `143` |

### How to use it

1. **After registration**: Write the identity file to `workspace/IDENTITY.md` or `workspace/AGENT_IDENTITY.md` so it is in the agent's context.
2. **Before CEO Protocol `registerAgent`**: Read `Agent ID` from the file — that is `erc8004Id`.
3. **Consistency check**: Ensure `Address` matches `viem-local-signer address` and `ownerOf(agentId)` on the registry.

Example filled identity:

```markdown
# Agent Identity
- **Address**: `0xB4AF3708DA37a485E84b4F09c146eD0A8B7Df5c4`
- **Agent ID**: `42`
- **Agent Registry**: `eip155:143:0x8004A169FB4a3325136EB29fA0ceB6D2e539a432`
- **Chain ID**: `143`
```

## ABI (minimal)

```json
[
  {
    "type": "function",
    "name": "register",
    "stateMutability": "nonpayable",
    "inputs": [{ "name": "agentURI", "type": "string" }],
    "outputs": [{ "name": "agentId", "type": "uint256" }]
  },
  {
    "type": "function",
    "name": "register",
    "stateMutability": "nonpayable",
    "inputs": [
      { "name": "agentURI", "type": "string" },
      {
        "name": "metadata",
        "type": "tuple[]",
        "components": [
          { "name": "metadataKey", "type": "string" },
          { "name": "metadataValue", "type": "bytes" }
        ]
      }
    ],
    "outputs": [{ "name": "agentId", "type": "uint256" }]
  },
  {
    "type": "function",
    "name": "register",
    "stateMutability": "nonpayable",
    "inputs": [],
    "outputs": [{ "name": "agentId", "type": "uint256" }]
  },
  {
    "type": "function",
    "name": "setAgentURI",
    "stateMutability": "nonpayable",
    "inputs": [
      { "name": "agentId", "type": "uint256" },
      { "name": "newURI", "type": "string" }
    ],
    "outputs": []
  },
  {
    "type": "function",
    "name": "setMetadata",
    "stateMutability": "nonpayable",
    "inputs": [
      { "name": "agentId", "type": "uint256" },
      { "name": "metadataKey", "type": "string" },
      { "name": "metadataValue", "type": "bytes" }
    ],
    "outputs": []
  },
  {
    "type": "function",
    "name": "ownerOf",
    "stateMutability": "view",
    "inputs": [{ "name": "tokenId", "type": "uint256" }],
    "outputs": [{ "name": "", "type": "address" }]
  },
  {
    "type": "function",
    "name": "tokenURI",
    "stateMutability": "view",
    "inputs": [{ "name": "tokenId", "type": "uint256" }],
    "outputs": [{ "name": "", "type": "string" }]
  },
  {
    "type": "function",
    "name": "getAgentWallet",
    "stateMutability": "view",
    "inputs": [{ "name": "agentId", "type": "uint256" }],
    "outputs": [{ "name": "", "type": "address" }]
  },
  {
    "type": "function",
    "name": "getMetadata",
    "stateMutability": "view",
    "inputs": [
      { "name": "agentId", "type": "uint256" },
      { "name": "metadataKey", "type": "string" }
    ],
    "outputs": [{ "name": "", "type": "bytes" }]
  }
]
```

## Encoding and Sending

Use `viem` to encode, then `viem-local-signer send-contract` to broadcast. Example (Node/script):

```typescript
import { encodeFunctionData } from "viem";

const ERC8004_IDENTITY = "0x8004A169FB4a3325136EB29fA0ceB6D2e539a432";

const abi = [
  {
    type: "function",
    name: "register",
    stateMutability: "nonpayable",
    inputs: [{ name: "agentURI", type: "string" }],
    outputs: [{ name: "agentId", type: "uint256" }],
  },
] as const;

const agentURI = "ipfs://Qm..."; // or data:application/json;base64,...
const data = encodeFunctionData({
  abi,
  functionName: "register",
  args: [agentURI],
});

// Then run:
// viem-local-signer send-contract --to 0x8004A169FB4a3325136EB29fA0ceB6D2e539a432 --data <hex> --value-wei 0 --wait
```

## Agent Runbook

1. Confirm signer: `viem-local-signer address`
2. Ensure wallet has MON for gas.
3. Prepare `agentURI` (IPFS or data URI with registration JSON).
4. Encode `register(agentURI)` with viem.
5. Present tx summary and ask for user confirmation.
6. Run `viem-local-signer send-contract --to 0x8004A169FB4a3325136EB29fA0ceB6D2e539a432 --data <calldata> --value-wei 0 --wait`
7. Parse receipt/logs for `agentId` (or read from `Registered` event).
8. Write identity file (`workspace/AGENT_IDENTITY.md` or `workspace/IDENTITY.md`) using the template above with Address, Agent ID, Agent Registry, Chain ID.

## CEO Protocol Integration

CEOVault requires an ERC-8004 identity before `registerAgent`:

```
CEOVault.registerAgent(metadataURI, ceoAmount, erc8004Id)
```

- `erc8004Id` = the token ID from `ERC8004Identity.register(...)`.
- Caller must own that NFT (`ownerOf(erc8004Id) == msg.sender`).

## Block Explorer

- Monad: `https://monadscan.com/`
- Tx link: `https://monadscan.com/tx/<hash>`
- Contract: `https://monadscan.com/address/0x8004A169FB4a3325136EB29fA0ceB6D2e539a432`

Overview

This skill registers and manages ERC-8004 Identity NFTs on Monad so an agent can obtain an on-chain identity required by CEO Protocol and other ERC-8004 integrated systems. It provides registration flows, a registration JSON template, automated scripts, and guidance for persisting agent identity information. Use it to mint, update, verify, and link an ERC-8004 token ID to downstream protocol onboarding.

How this skill works

The skill interacts with the ERC-8004 Identity contract on Monad (0x8004A169FB4a3325136EB29fA0ceB6D2e539a432) using standard ERC-721 semantics. It supports register, setAgentURI, setMetadata, and read calls (ownerOf, tokenURI, getAgentWallet, getMetadata), and parses Registered, URIUpdated, and MetadataSet events to extract the agentId. Automated scripts perform a full flow: mint an empty identity, build and upload an EIP-8004 registration JSON (IPFS or data URI), set the token URI on-chain, then verify ownership and metadata.

When to use it

  • You need an ERC-8004 identity NFT to register an agent with CEOVault or other ERC-8004 consumers.
  • You want a reproducible, scriptable flow to mint an agent NFT and host the registration JSON on IPFS.
  • You must update the agentURI or set on-chain metadata after minting.
  • You need to verify ownership, tokenURI, and on-chain metadata before protocol onboarding.

Best practices

  • Prepare the registration JSON using the provided template and host it on IPFS or as a data URI before calling register(agentURI).
  • Persist the returned agentId and signer address in an identity file (workspace/AGENT_IDENTITY.md) for later CEOVault registerAgent calls.
  • Confirm signer address and sufficient MON balance before broadcasting transactions; parse Registered event from the receipt to obtain agentId reliably.
  • Use the included Docker/scripts flow for production: register -> build card -> upload -> set-agent-uri -> verify.
  • Keep supportedTrust set appropriately (e.g., ["reputation"]) for CEO Protocol compatibility.

Example use cases

  • Mint an ERC-8004 identity for an autonomous agent and register it with CEOVault.registerAgent(…) using the returned token ID.
  • Update the agentURI after changing agent capabilities or endpoints and emit URIUpdated for verifiers.
  • Attach on-chain metadata (setMetadata) to include service endpoints or key-value attributes required by integrators.
  • Automate full provisioning in CI: create agent identity, upload JSON to Pinata, set token URI, and write identity file for downstream tooling.

FAQ

What contract address does this skill target?

Monad mainnet ERC-8004 Identity: 0x8004A169FB4a3325136EB29fA0ceB6D2e539a432.

How do I get the agentId after registering?

Parse the Registered event from the transaction receipt or read the function return value; persist agentId in the identity file.

Can I register without an agentURI and set it later?

Yes. You can call register() with no URI and later call setAgentURI(agentId, newURI).