JavaScript SDK for ERC-8004 agent registration and feedback flows
npm install erc8004-sdk-jsTypeScript SDK that exposes the same Agent registration + feedback flows used in
the web dApp, but in a wallet-less/server context. Each step is exposed as an
individual method so you can decide how automated you want the workflow to be.
> Key changes vs the web app
>
> - Interactions are signed with your own private key (no RainbowKit/Wagmi UI)
> - Profile / feedback payloads are uploaded directly to IPFS (Pinata or local
> node). Supply your own API key via environment variables.
Install the SDK from npm with your preferred package manager:
``bashnpm
npm install erc8004-sdk-js
> Working inside this monorepo? You can still run
cd erc8004-sdk-js && npm install && npm run build to produce a local build, but external consumers only need to install from npm.$3
Create a
.env next to package.json (optional but convenient):`bash
RPC & contracts
ERC8004_RPC_URL=https://bsc-testnet.bnbchain.org
ERC8004_CHAIN_ID=97
ERC8004_IDENTITY_REGISTRY=0xIdentityRegistry
ERC8004_REPUTATION_REGISTRY=0xFeedbackContract
ERC8004_INDEX_REGISTRY=0xGetLastIndexContractsigner
ERC8004_PRIVATE_KEY=0xyourprivatekeyIPFS (example: Pinata JWT)
ERC8004_PINATA_JWT=eyJhbGciOiJI...
`Usage example
`ts
import { Erc8004Sdk } from 'erc8004-sdk-js';async function registerAndSetProfile() {
const sdk = new Erc8004Sdk({
rpcUrl: process.env.ERC8004_RPC_URL!,
chainId: Number(process.env.ERC8004_CHAIN_ID),
identityRegistryAddress: process.env
.ERC8004_IDENTITY_REGISTRY as
0x${string},
reputationRegistryAddress: process.env
.ERC8004_REPUTATION_REGISTRY as 0x${string},
feedbackIndexAddress: process.env.ERC8004_INDEX_REGISTRY as 0x${string},
privateKey: process.env.ERC8004_PRIVATE_KEY as 0x${string},
ipfs: {
provider: 'pinata',
pinataJwt: process.env.ERC8004_PINATA_JWT,
},
}); const registerTx = await sdk.registerAgent();
const { agentId } = await sdk.waitForRegisterReceipt(registerTx);
if (!agentId) throw new Error('agentId not found in receipt');
const profile = await sdk.uploadAgentProfile({
agentId,
name: 'Sample Agent',
description: 'Always-on helper',
a2aEndpoint: 'https://example.com/agent.json',
agentWallet: 'reputation',
supportedTrust: ['reputation'],
logoFilePath: './assets/logo.png',
});
await sdk.setAgentUri(agentId, profile.uri);
}
async function leaveFeedback(agentId: bigint) {
const sdk = new Erc8004Sdk({
rpcUrl: process.env.ERC8004_RPC_URL!,
chainId: Number(process.env.ERC8004_CHAIN_ID),
identityRegistryAddress: process.env
.ERC8004_IDENTITY_REGISTRY as
0x${string},
reputationRegistryAddress: process.env
.ERC8004_REPUTATION_REGISTRY as 0x${string},
feedbackIndexAddress: process.env.ERC8004_INDEX_REGISTRY as 0x${string},
privateKey: process.env.ERC8004_PRIVATE_KEY as 0x${string},
}); const nextIndex = await sdk.getNextFeedbackIndex(agentId, sdk.signerAddress);
const feedbackAuth = await sdk.buildFeedbackAuth({
agentId,
clientAddress: sdk.signerAddress,
indexLimit: nextIndex,
expiry: BigInt(Math.floor(Date.now() / 1000) + 60 * 60),
});
const feedbackDoc = await sdk.uploadFeedbackDocument(
{
agentId,
clientAddress: sdk.signerAddress,
score: 90,
tag1: 'support',
tag2: 'positive',
context: 'Resolved the issue within 2 minutes.',
},
feedbackAuth
);
const feedbackTx = await sdk.submitFeedback({
agentId,
score: 90,
tag1: 'support',
tag2: 'positive',
feedbackUri: feedbackDoc.uri,
feedbackHash: feedbackDoc.hash,
feedbackAuth: feedbackAuth.concatenated,
});
await sdk.waitForTransaction(feedbackTx);
}
`API surface
| Method | Description |
| ------------------------------------------------- | ----------------------------------------------------------------------------------------------- |
|
registerAgent() | Submit the on-chain register() transaction. Returns tx hash. |
| waitForRegisterReceipt(hash) | Wait for confirmation and decode the agentId. |
| uploadAgentProfile(input) | Push structured profile metadata (plus optional logo) to IPFS. |
| setAgentUri(agentId, uri) | Call setAgentUri with the IPFS URI returned above. |
| getFeedbackPermission({ tokenId, userAddress }) | Read ownerOf, getApproved, isApprovedForAll to decide whether a wallet may post feedback. |
| getNextFeedbackIndex(agentId, clientAddress) | Reads the index-tracking contract. |
| buildFeedbackAuth(params) | Produce the ABI-encoded struct + signature (feedbackAuth bytes). |
| uploadFeedbackDocument(data, auth) | Store the ERC-8004 feedback JSON on IPFS and return { uri, cid, hash }. |
| submitFeedback(params) | Call giveFeedback with the URI/hash/auth payloads. |
| waitForTransaction(hash) | Convenience helper for any tx. |All helpers are composable; you can insert manual approval steps (e.g. waiting for badge review) between calls.
IPFS strategies
Set
ipfs.provider to:-
pinata & supply pinataJwt or (pinataApiKey, pinataSecretApiKey)
- ipfs-http (default) to hit a local IPFS daemon via /api/v0/addAdditional knobs:
-
ipfs.apiUrl – override default gateway
- ipfs.pin – always/never pin content
- ipfs.timeoutMs – HTTP timeoutTesting ideas
- Run
npm run lint to type-check without emitting files.
- Mock IpfsClient` if you only want to test contract calls.MIT