Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Changelog

## 0.1.0-alpha.0

Initial alpha release.

### Identity

- `registerAgent` — register as an ERC-8004 agent (mints agent NFT)
- `parseRegisterReceipt` — extract agentId from register transaction receipt
- `isRegistered` — check if an address is a registered agent
- `verifyAgentId` — verify that an agentId belongs to a claimed address
- `resolveAgent` — resolve agent by ID (owner, wallet, URI, ownerMismatch flag)
- `getAgentWallet` / `setAgentWallet` / `unsetAgentWallet` — wallet management
- `signAgentWalletConsent` — sign EIP-712 typed data for `setAgentWallet`
- `getMetadata` / `setMetadata` — on-chain key-value metadata
- `parseMetadataSetReceipt` — extract fields from setMetadata transaction receipt
- `setAgentURI` — update agent URI
- `parseURIUpdatedReceipt` — extract fields from setAgentURI transaction receipt
- `getVersion` — read contract version string

### Reputation

- `giveFeedback` — submit feedback for an agent
- `parseGiveFeedbackReceipt` — extract fields from giveFeedback transaction receipt
- `revokeFeedback` — revoke previously given feedback
- `parseFeedbackRevokedReceipt` — extract fields from revokeFeedback transaction receipt
- `appendResponse` — append a response to existing feedback
- `parseResponseAppendedReceipt` — extract fields from appendResponse transaction receipt
- `readFeedback` / `readAllFeedback` — read feedback entries (optional `batchSize` for large reviewer sets)
- `getSummary` — aggregated reputation summary
- `getClients` — all reviewer addresses for an agent
- `getLastIndex` — latest feedback index for an agent-client pair
- `getResponseCount` — count responses to a feedback entry
- `getIdentityRegistry` — get linked Identity Registry address
- `getVersion` — read contract version string

### Infrastructure

- Registry addresses for 14 chains (Ethereum, Base, Polygon, Arbitrum, Optimism, Avalanche, BSC, Scroll, Linea, Mantle, Gnosis, Celo, Base Sepolia, Ethereum Sepolia)
- Auto-resolve registry address from `client.chain`
- Sub-path exports: `/identity`, `/reputation`, `/abis`
134 changes: 134 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
# @x402r/erc8004

TypeScript SDK for [ERC-8004](https://eips.ethereum.org/EIPS/eip-8004) Identity and Reputation registries. Built on [viem](https://viem.sh).

## Install

```bash
npm install @x402r/erc8004
```

## Usage

### Register an agent

```ts
import { registerAgent, parseRegisterReceipt } from '@x402r/erc8004/identity'

const hash = await registerAgent(walletClient, {
agentURI: 'https://example.com/agent.json',
})

const receipt = await publicClient.waitForTransactionReceipt({ hash })
const { agentId } = parseRegisterReceipt(receipt)
```

### Verify identity

```ts
import { verifyAgentId, resolveAgent } from '@x402r/erc8004/identity'

const valid = await verifyAgentId(publicClient, {
agentId: 42n,
claimedAddress: '0x...',
})

const agent = await resolveAgent(publicClient, { agentId: 42n })
// { agentId, owner, agentWallet, agentURI, ownerMismatch }
```

### Give feedback

```ts
import { giveFeedback, getSummary } from '@x402r/erc8004/reputation'

await giveFeedback(walletClient, {
agentId: 42n,
value: 85n,
valueDecimals: 0,
tag1: 'service',
tag2: 'quality',
})

const summary = await getSummary(publicClient, {
agentId: 42n,
clientAddresses: ['0x...'],
tag1: 'service',
tag2: 'quality',
})
```

## API

### Identity

| Function | Description |
|---|---|
| `registerAgent` | Register as an ERC-8004 agent (mints NFT) |
| `parseRegisterReceipt` | Extract `agentId` from register tx receipt |
| `isRegistered` | Check if an address is registered |
| `verifyAgentId` | Verify agentId belongs to a claimed address |
| `resolveAgent` | Resolve agent by ID (owner, wallet, URI) |
| `getAgentWallet` | Get wallet address for an agent |
| `setAgentWallet` | Set verified payment wallet (EIP-712 sig) |
| `signAgentWalletConsent` | Sign EIP-712 consent for `setAgentWallet` |
| `unsetAgentWallet` | Clear agent wallet |
| `getMetadata` | Read on-chain metadata by key |
| `setMetadata` | Write on-chain metadata |
| `parseMetadataSetReceipt` | Extract fields from `setMetadata` tx receipt |
| `setAgentURI` | Update agent URI |
| `parseURIUpdatedReceipt` | Extract fields from `setAgentURI` tx receipt |
| `getVersion` | Read contract version string |

### Reputation

| Function | Description |
|---|---|
| `giveFeedback` | Submit feedback for an agent |
| `parseGiveFeedbackReceipt` | Extract fields from `giveFeedback` tx receipt |
| `revokeFeedback` | Revoke previously given feedback |
| `parseFeedbackRevokedReceipt` | Extract fields from `revokeFeedback` tx receipt |
| `appendResponse` | Append a response to feedback |
| `parseResponseAppendedReceipt` | Extract fields from `appendResponse` tx receipt |
| `readFeedback` | Read a single feedback entry |
| `readAllFeedback` | Read all feedback (filtered by reviewers and tags, optional `batchSize`) |
| `getSummary` | Aggregated reputation summary |
| `getClients` | All addresses that have given feedback |
| `getLastIndex` | Latest feedback index for an agent-client pair |
| `getResponseCount` | Count responses to a feedback entry |
| `getIdentityRegistry` | Get linked Identity Registry address |
| `getVersion` | Read contract version string |

## Chains

| Chain | ID |
|---|---|
| Ethereum | 1 |
| Base | 8453 |
| Polygon | 137 |
| Arbitrum | 42161 |
| Optimism | 10 |
| Avalanche | 43114 |
| BSC | 56 |
| Scroll | 534352 |
| Linea | 59144 |
| Mantle | 5000 |
| Gnosis | 100 |
| Celo | 42220 |
| Base Sepolia | 84532 |
| Ethereum Sepolia | 11155111 |

Registry addresses auto-resolve from `client.chain`. Pass `registryAddress` to override.

## Exports

| Path | Contents |
|---|---|
| `@x402r/erc8004` | Everything |
| `@x402r/erc8004/identity` | Identity registry functions and types |
| `@x402r/erc8004/reputation` | Reputation registry functions and types |
| `@x402r/erc8004/abis` | Raw contract ABIs |

## License

[Apache-2.0](./LICENSE)
12 changes: 10 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,12 +1,20 @@
{
"name": "@x402r/erc8004",
"version": "0.0.0",
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe release alpha version first for testing with SDK, than can release actual stable one ?

"version": "0.1.0-alpha.0",
"description": "Lightweight TypeScript SDK for ERC-8004 (Trustless Agents) — Identity and Reputation registries",
"type": "module",
"license": "Apache-2.0",
"sideEffects": false,
"repository": {
"type": "git",
"url": "git+https://github.com/BackTrackCo/erc8004.git"
},
"homepage": "https://github.com/BackTrackCo/erc8004#readme",
"bugs": {
"url": "https://github.com/BackTrackCo/erc8004/issues"
},
"engines": {
"node": ">=22"
"node": ">=18"
},
"packageManager": "pnpm@10.23.0",
"files": [
Expand Down
7 changes: 7 additions & 0 deletions src/addresses.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,13 @@ const ERC8004_REGISTRIES: Record<number, Erc8004Addresses> = {
137: MAINNET_ADDRESSES, // Polygon (Polygonscan verified)
42161: MAINNET_ADDRESSES, // Arbitrum (RPC verified, 8004scan indexes)
10: MAINNET_ADDRESSES, // Optimism (RPC verified, erc-8004 repo)
43114: MAINNET_ADDRESSES, // Avalanche (CREATE2, erc-8004-contracts repo)
56: MAINNET_ADDRESSES, // BSC (CREATE2, erc-8004-contracts repo)
534352: MAINNET_ADDRESSES, // Scroll (CREATE2, erc-8004-contracts repo)
59144: MAINNET_ADDRESSES, // Linea (CREATE2, erc-8004-contracts repo)
5000: MAINNET_ADDRESSES, // Mantle (CREATE2, erc-8004-contracts repo)
100: MAINNET_ADDRESSES, // Gnosis (CREATE2, erc-8004-contracts repo)
42220: MAINNET_ADDRESSES, // Celo (CREATE2, erc-8004-contracts repo)
// Testnets — verified via agent0-sdk + RPC
84532: TESTNET_ADDRESSES, // Base Sepolia
11155111: TESTNET_ADDRESSES, // Ethereum Sepolia
Expand Down
21 changes: 21 additions & 0 deletions src/identity/getVersion.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import type { PublicClient } from 'viem'
import { identityRegistryAbi } from '../abis/index.js'
import { resolveIdentityRegistry } from '../internal/resolveRegistryAddress.js'
import type { GetVersionParameters } from './types.js'

/** Read the contract version string from the Identity Registry. */
export async function getVersion(
publicClient: PublicClient,
parameters: GetVersionParameters = {},
): Promise<string> {
const registry = resolveIdentityRegistry(
publicClient,
parameters.registryAddress,
)

return publicClient.readContract({
address: registry,
abi: identityRegistryAbi,
functionName: 'getVersion',
})
}
14 changes: 14 additions & 0 deletions src/identity/index.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,33 @@
export { getAgentWallet } from './getAgentWallet.js'
export { getMetadata } from './getMetadata.js'
export { getVersion } from './getVersion.js'
export { isRegistered } from './isRegistered.js'
export type { MetadataSetResult } from './parseMetadataSetReceipt.js'
export { parseMetadataSetReceipt } from './parseMetadataSetReceipt.js'
export type { RegisterResult } from './parseRegisterReceipt.js'
export { parseRegisterReceipt } from './parseRegisterReceipt.js'
export type { URIUpdatedResult } from './parseURIUpdatedReceipt.js'
export { parseURIUpdatedReceipt } from './parseURIUpdatedReceipt.js'
export { registerAgent } from './register.js'
export { resolveAgent } from './resolveAgent.js'
export { setAgentURI } from './setAgentURI.js'
export { setAgentWallet } from './setAgentWallet.js'
export { setMetadata } from './setMetadata.js'
export { signAgentWalletConsent } from './signAgentWalletConsent.js'
export type {
GetAgentWalletParameters,
GetMetadataParameters,
GetVersionParameters,
IsRegisteredParameters,
RegisterAgentParameters,
ResolveAgentParameters,
ResolvedAgent,
SetAgentURIParameters,
SetAgentWalletParameters,
SetMetadataParameters,
SignAgentWalletConsentParameters,
UnsetAgentWalletParameters,
VerifyAgentIdParameters,
} from './types.js'
export { unsetAgentWallet } from './unsetAgentWallet.js'
export { verifyAgentId } from './verifyAgentId.js'
24 changes: 24 additions & 0 deletions src/identity/parseMetadataSetReceipt.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { type Hex, parseEventLogs, type TransactionReceipt } from 'viem'
import { identityRegistryAbi } from '../abis/index.js'

export interface MetadataSetResult {
agentId: bigint
metadataKey: string
metadataValue: Hex
}

/** Extract agentId, metadataKey, and metadataValue from a `setMetadata` transaction receipt. */
export function parseMetadataSetReceipt(
receipt: TransactionReceipt,
): MetadataSetResult {
const logs = parseEventLogs({
abi: identityRegistryAbi,
logs: receipt.logs,
eventName: 'MetadataSet',
})
if (logs.length === 0) {
throw new Error('No MetadataSet event found in receipt')
}
const { agentId, metadataKey, metadataValue } = logs[0].args
return { agentId, metadataKey, metadataValue }
}
27 changes: 27 additions & 0 deletions src/identity/parseRegisterReceipt.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { type Address, parseEventLogs, type TransactionReceipt } from 'viem'
import { identityRegistryAbi } from '../abis/index.js'

export interface RegisterResult {
agentId: bigint
owner: Address
agentURI: string
}

/**
* Extract the agentId, owner, and agentURI from a `registerAgent` transaction receipt.
* Parses the `Registered` event emitted by the Identity Registry.
*/
export function parseRegisterReceipt(
receipt: TransactionReceipt,
): RegisterResult {
const logs = parseEventLogs({
abi: identityRegistryAbi,
logs: receipt.logs,
eventName: 'Registered',
})
if (logs.length === 0) {
throw new Error('No Registered event found in receipt')
}
const { agentId, owner, agentURI } = logs[0].args
return { agentId, owner, agentURI }
}
24 changes: 24 additions & 0 deletions src/identity/parseURIUpdatedReceipt.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { type Address, parseEventLogs, type TransactionReceipt } from 'viem'
import { identityRegistryAbi } from '../abis/index.js'

export interface URIUpdatedResult {
agentId: bigint
newURI: string
updatedBy: Address
}

/** Extract agentId, newURI, and updatedBy from a `setAgentURI` transaction receipt. */
export function parseURIUpdatedReceipt(
receipt: TransactionReceipt,
): URIUpdatedResult {
const logs = parseEventLogs({
abi: identityRegistryAbi,
logs: receipt.logs,
eventName: 'URIUpdated',
})
if (logs.length === 0) {
throw new Error('No URIUpdated event found in receipt')
}
const { agentId, newURI, updatedBy } = logs[0].args
return { agentId, newURI, updatedBy }
}
21 changes: 19 additions & 2 deletions src/identity/register.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,13 @@ export async function registerAgent(
parameters.registryAddress,
)

if (metadata && metadata.length > 0) {
if (agentURI === undefined && metadata && metadata.length > 0) {
throw new Error(
'metadata requires agentURI: the contract has no register(metadata) overload without a URI',
)
}

if (agentURI !== undefined && metadata && metadata.length > 0) {
return walletClient.writeContract({
address: registry,
abi: identityRegistryAbi,
Expand All @@ -37,11 +43,22 @@ export async function registerAgent(
})
}

if (agentURI !== undefined) {
return walletClient.writeContract({
address: registry,
abi: identityRegistryAbi,
functionName: 'register',
args: [agentURI],
chain: walletClient.chain,
account,
})
}

return walletClient.writeContract({
address: registry,
abi: identityRegistryAbi,
functionName: 'register',
args: [agentURI],
args: [],
chain: walletClient.chain,
account,
})
Expand Down
5 changes: 2 additions & 3 deletions src/identity/resolveAgent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,8 @@ import type { ResolveAgentParameters, ResolvedAgent } from './types.js'
* Throws if the agentId does not exist (ERC-721 reverts on non-existent tokens).
* Use `verifyAgentId` first if you need a safe boolean check.
*
* If `owner !== agentWallet`, the agent NFT was transferred but the wallet
* wasn't updated. `ownerMismatch` is set to true as a warning — callers
* should decide which address to trust based on their use case.
* `agentWallet` is `address(0)` after NFT transfer or explicit `unsetAgentWallet()`.
* When `ownerMismatch` is true, use `owner` for the canonical address.
*/
export async function resolveAgent(
publicClient: PublicClient,
Expand Down
Loading
Loading