Agent registration
npx skills add https://github.com/Hubra-labs/sol-skills --skill agent-registrationPurpose
Register your agent with the 8004 registry for agent registration, SEAL feedback, ATOM reputation, signing, IPFS, and indexer-backed registry workflows.
Trusted sources
Execution flow
1. SDK Setup
Read-only (no wallet needed)
const sdk = new SolanaSDK({ cluster: 'devnet' });With signer (for write operations)
const signer = Keypair.fromSecretKey(
Uint8Array.from(JSON.parse(process.env.SOLANA_PRIVATE_KEY!))
);
const sdk = new SolanaSDK({ signer });With custom RPC (required for bulk queries)
const sdk = new SolanaSDK({
rpcUrl: 'https://your-helius-rpc.helius.dev',
signer,
});Network defaults
// Mainnet ready defaults
const mainnetSdk = new SolanaSDK({ cluster: 'mainnet-beta', signer });
// Devnet defaults
const devnetSdk = new SolanaSDK({ cluster: 'devnet', signer });
// Localnet (override program IDs to your deployed addresses)
const localnetSdk = new SolanaSDK({
cluster: 'localnet',
rpcUrl: 'http://127.0.0.1:8899',
signer,
programIds: {
agentRegistry: '...',
atomEngine: '...',
},
});Full config
...
2. Register an Agent
Step 1: Build metadata
const metadata = buildRegistrationFileJson({
name: 'My Agent',
description: 'Autonomous trading agent',
image: 'ipfs://QmImageCid...',
services: [
{ type: ServiceType.MCP, value: 'https://my-agent.com/mcp' },
{ type: ServiceType.A2A, value: 'https://my-agent.com/a2a' },
{ type: ServiceType.SNS, value: 'my-agent.sol' },
],
skills: ['advanced_reasoning_planning/strategic_planning'],
domains: ['finance_and_business/finance'],
x402Support: true,
});Step 2: Upload to IPFS
const cid = await ipfs.addJson(metadata);Step 3: Register on-chain
const result = await sdk.registerAgent(`ipfs://${cid}`);
// result.asset -> PublicKey (agent NFT address)
// result.signature -> transaction signature
// Optional: enable ATOM at creation (irreversible)
const withAtom = await sdk.registerAgent(`ipfs://${cid}`, {
atomEnabled: true,
});Step 4: Set operational wallet
const opWallet = Keypair.generate();
await sdk.setAgentWallet(result.asset, opWallet);Collection + Parent Association (CID-first)
All agents register into the base collection automatically
...
Landing guidance
22. Operation Costs (Solana devnet)
| Operation | Cost | Notes | |-----------|------|-------| | registerAgent() | ~0.00651 SOL | Base registration (without ATOM init) | | registerAgent(..., { atomEnabled: true }) | higher than base | Includes ATOM stats initialization in the same transaction | | giveFeedback() (1st for agent) | ~0.00332 SOL | Creates reputation PDA | | giveFeedback() (subsequent) | ~0.00209 SOL | Feedback PDA only | | setMetadata() (1st key) | ~0.00319 SOL | PDA rent | | setMetadata() (update) | ~0.000005 SOL | TX fee only | | appendResponse() (1st) | ~0.00275 SOL | Response + index PDAs | | appendResponse() (subsequent) | ~0.00163 SOL | Response PDA only | | revokeFeedback() | ~0.000005 SOL | TX fee only | | deleteMetadata() | recovers rent | Returns lamports to owner |
5. Feedback System
Give feedback
// Decimal string auto-encodes: "99.77" -> { value: 9977n, valueDecimals: 2 }
await sdk.giveFeedback(assetPubkey, {
value: '99.77',
tag1: Tag.uptime,
tag2: Tag.day,
score: 95, // 0-100, optional
endpoint: '/api/v1/generate', // optional, max 250 bytes
feedbackUri: `ipfs://${feedbackCid}`,
feedbackFileHash, // optional, 32 bytes Buffer (links file to SEAL)
});GiveFeedbackParams reference
| Field | Type | Required | Description | |-------|------|----------|-------------| | value | string \| number \| bigint | Yes | Metric value. Strings auto-encode decimals ("99.77" -> 9977n, 2) | | valueDecimals | number (0-18) | No | Only needed for raw int/bigint. Auto-detected for strings | | score | number (0-100) | No | Explicit ATOM score. If omitted, inferred from tag1 | | tag1 | string | No | Category tag (max 32 UTF-8 bytes) | | tag2 | string | No | Period/network tag (max 32 UTF-8 bytes) | | endpoint | string | No | Endpoint used (max 250 UTF-8 bytes) | | feedbackUri | string | No | URI to detailed feedback file (IPFS/HTTPS, max 250 bytes) | | feedbackFileHash | Buffer | No | SHA-256 of feedback file content (32 bytes). Binds file to on-chain SEAL |
Value encoding patterns
...
Failure handling
21. Error Handling
import {
IndexerError,
IndexerUnavailableError,
IndexerTimeoutError,
IndexerRateLimitError,
UnsupportedRpcError,
RpcNetworkError,
} from '8004-solana';
try {
const agents = await sdk.getAllAgents();
} catch (e) {
if (e instanceof UnsupportedRpcError) {
// Default RPC doesn't support getProgramAccounts
// Use Helius, QuickNode, or Alchemy
}
if (e instanceof IndexerUnavailableError) {
// Indexer is down, SDK falls back to RPC if indexerFallback=true
}
if (e instanceof IndexerRateLimitError) {
// Back off and retry
}
}Methods requiring premium RPC
getAllAgents(), getAgentsByOwner(), getCollectionAgents(), getCollections()
Indexer-backed reads (readAllFeedback(), getClients(), getLastIndex(), readResponses()) do not require premium RPC, but they do require a reachable indexer.
Full guide
8004-solana SDK Skill
You are an AI agent with access to the 8004-solana TypeScript SDK. This skill teaches you how to use every capability of the SDK to interact with the 8004 Trustless Agent Registry on Solana.
Version note (SDK 0.8.0):
mainnet-betais first-class in SDK defaults (program IDs + indexer endpoints).- Cluster-aware indexer defaults are built in:
devnet/testnet:https://8004-indexer-dev.qnt.sh/rest/v1and/v2/graphqlmainnet-beta:https://8004-indexer-main.qnt.sh/rest/v1and/v2/graphqllocalnet:http://127.0.0.1:3005/rest/v1and/v2/graphqlregisterAgent(tokenUri?, options?)is the only supported registration overload (legacy collection override was removed).- Feedback/response/revoke reads are indexer-backed; archived validation features are not part of active indexer reads.
Install
npm install 8004-solana @solana/web3.jsImports
import {
// Core SDK
SolanaSDK,
IPFSClient,
// Builders
buildRegistrationFileJson,
// Enums & Types
ServiceType, // MCP, A2A, ENS, SNS, DID, WALLET, OASF
TrustTier, // Unrated=0, Bronze=1, Silver=2, Gold=3, Platinum=4
Tag, // Standardized tag constants
// ATOM Engine
AtomStats,
trustTierToString,
// SEAL v1
computeSealHash,
computeFeedbackLeafV1,
verifySealHash,
createSealParams,
validateSealInputs,
MAX_TAG_LEN, // 32 bytes
MAX_ENDPOINT_LEN, // 250 bytes
MAX_URI_LEN, // 250 bytes
// OASF Taxonomy
getAllSkills,
getAllDomains,
// Tag helpers
isKnownTag,
getTagDescription,
// Signing
buildSignedPayload,
verifySignedPayload,
parseSignedPayload,
normalizeSignData,
createNonce,
canonicalizeJson,
// Value encoding
encodeReputationValue,
decodeToDecimalString,
decodeToNumber,
// Crypto utilities
keccak256,
sha256,
sha256Sync, // Node.js only
// Hash-chain replay
replayFeedbackChain,
replayResponseChain,
replayRevokeChain,
// Indexer
IndexerClient,
// Endpoint crawler
EndpointCrawler,
// Error classes
IndexerError,
IndexerUnavailableError,
IndexerTimeoutError,
IndexerRateLimitError,
UnsupportedRpcError,
RpcNetworkError,
} from '8004-solana';
import { Keypair, PublicKey } from '@solana/web3.js';1. SDK Setup
Read-only (no wallet needed)
const sdk = new SolanaSDK({ cluster: 'devnet' });With signer (for write operations)
const signer = Keypair.fromSecretKey(
Uint8Array.from(JSON.parse(process.env.SOLANA_PRIVATE_KEY!))
);
const sdk = new SolanaSDK({ signer });With custom RPC (required for bulk queries)
const sdk = new SolanaSDK({
rpcUrl: 'https://your-helius-rpc.helius.dev',
signer,
});Network defaults
// Mainnet ready defaults
const mainnetSdk = new SolanaSDK({ cluster: 'mainnet-beta', signer });
// Devnet defaults
const devnetSdk = new SolanaSDK({ cluster: 'devnet', signer });
// Localnet (override program IDs to your deployed addresses)
const localnetSdk = new SolanaSDK({
cluster: 'localnet',
rpcUrl: 'http://127.0.0.1:8899',
signer,
programIds: {
agentRegistry: '...',
atomEngine: '...',
},
});Full config
const sdk = new SolanaSDK({
cluster: 'mainnet-beta',
rpcUrl: 'https://...',
signer: keypair,
// Optional: explicit overrides (otherwise cluster defaults are used)
programIds: {
agentRegistry: '...',
atomEngine: '...',
},
// Optional: force REST mode (omit indexerGraphqlUrl)
indexerUrl: 'https://8004-indexer-main.qnt.sh/rest/v1',
indexerApiKey: process.env.INDEXER_API_KEY, // if your indexer requires an API key, keep it in env
// Or force GraphQL mode instead:
// indexerGraphqlUrl: 'https://8004-indexer-main.qnt.sh/v2/graphql',
useIndexer: true,
indexerFallback: true,
forceOnChain: false,
});IPFS client
// Pinata (recommended)
const ipfsPinata = new IPFSClient({
pinataEnabled: true,
pinataJwt: process.env.PINATA_JWT!,
});
// Local node
const ipfsLocal = new IPFSClient({ url: 'http://localhost:5001' });2. Register an Agent
Step 1: Build metadata
const metadata = buildRegistrationFileJson({
name: 'My Agent',
description: 'Autonomous trading agent',
image: 'ipfs://QmImageCid...',
services: [
{ type: ServiceType.MCP, value: 'https://my-agent.com/mcp' },
{ type: ServiceType.A2A, value: 'https://my-agent.com/a2a' },
{ type: ServiceType.SNS, value: 'my-agent.sol' },
],
skills: ['advanced_reasoning_planning/strategic_planning'],
domains: ['finance_and_business/finance'],
x402Support: true,
});Step 2: Upload to IPFS
const cid = await ipfs.addJson(metadata);Step 3: Register on-chain
const result = await sdk.registerAgent(`ipfs://${cid}`);
// result.asset -> PublicKey (agent NFT address)
// result.signature -> transaction signature
// Optional: enable ATOM at creation (irreversible)
const withAtom = await sdk.registerAgent(`ipfs://${cid}`, {
atomEnabled: true,
});Step 4: Set operational wallet
const opWallet = Keypair.generate();
await sdk.setAgentWallet(result.asset, opWallet);Collection + Parent Association (CID-first)
All agents register into the base collection automatically. Collection metadata now follows an off-chain CID-first flow: A collection is unique only when the minting creator is the same and the collection pointer is the same. For indexer reads, you can also use sequential collection_id helpers (getCollectionPointerById, getCollectionAssetCountById, getCollectionAssetsById).
const { asset } = result; // from registerAgent(...)
// Build JSON payload (off-chain)
const collectionData = sdk.createCollectionData({
name: 'CasterCorp Agents',
symbol: 'CAST',
description: 'Production agent collection metadata',
});
// Upload and derive canonical pointer
const { pointer, uri } = await sdk.createCollection(collectionData);
// pointer -> canonical c1:b... string
// uri -> ipfs://...
// Attach pointer to the agent (string pointer, not pubkey)
await sdk.setCollectionPointer(asset, pointer!); // lock=true by default
// Parent hierarchy (advanced)
await sdk.setParentAsset(childAsset, parentAsset, { lock: false });Rules enforced on-chain:
- Collection pointer must be
c1:<payload>, lowercase alphanumeric payload, max 128 bytes. setCollectionPointer: signer must match immutableAgentAccount.creator.setParentAsset: signer must own child asset and match parent creator snapshot.- Parent must be a live asset and self-parent is forbidden.
lockdefaults totruefor both methods (col_locked/parent_locked).
3. Read Agent Data
// Load agent
const agent = await sdk.loadAgent(assetPubkey);
// agent.getOwnerPublicKey(), agent.getAgentWalletPublicKey(), agent.agent_uri, etc.
// Check existence
const exists = await sdk.agentExists(assetPubkey);
// Get owner
const owner = await sdk.getAgentOwner(assetPubkey);
// Check ownership
const isMine = await sdk.isAgentOwner(assetPubkey, myPubkey);
// On-chain metadata
const version = await sdk.getMetadata(assetPubkey, 'version');Bulk queries (requires premium RPC: Helius, QuickNode, Alchemy)
const allAgents = await sdk.getAllAgents();
const withFeedbacks = await sdk.getAllAgents({ includeFeedbacks: true });
const myAgents = await sdk.getAgentsByOwner(ownerPubkey);4. Update Agent
// Update metadata URI
await sdk.setAgentUri(assetPubkey, `ipfs://${newCid}`); // base registry account auto-resolved
// Set on-chain key-value metadata
await sdk.setMetadata(assetPubkey, 'version', '2.0.0');
// First call: ~0.00319 SOL (PDA rent). Updates: ~0.000005 SOL (tx fee only)
// Immutable metadata (permanent, cannot change or delete)
await sdk.setMetadata(assetPubkey, 'certification', 'audited-2026', true);
// Delete metadata (recovers rent)
await sdk.deleteMetadata(assetPubkey, 'version');
// Transfer ownership
await sdk.transferAgent(assetPubkey, newOwnerPubkey); // base registry account auto-resolved
// Sync owner after external NFT transfer
await sdk.syncOwner(assetPubkey);
// Burn agent Core asset (irreversible)
await sdk.burnAgent(assetPubkey);
// Note: burnAgent burns the Core asset; AgentAccount PDA remains until an on-chain unregister exists.5. Feedback System
Give feedback
// Decimal string auto-encodes: "99.77" -> { value: 9977n, valueDecimals: 2 }
await sdk.giveFeedback(assetPubkey, {
value: '99.77',
tag1: Tag.uptime,
tag2: Tag.day,
score: 95, // 0-100, optional
endpoint: '/api/v1/generate', // optional, max 250 bytes
feedbackUri: `ipfs://${feedbackCid}`,
feedbackFileHash, // optional, 32 bytes Buffer (links file to SEAL)
});GiveFeedbackParams reference
| Field | Type | Required | Description | |-------|------|----------|-------------| | value | string \| number \| bigint | Yes | Metric value. Strings auto-encode decimals ("99.77" -> 9977n, 2) | | valueDecimals | number (0-18) | No | Only needed for raw int/bigint. Auto-detected for strings | | score | number (0-100) | No | Explicit ATOM score. If omitted, inferred from tag1 | | tag1 | string | No | Category tag (max 32 UTF-8 bytes) | | tag2 | string | No | Period/network tag (max 32 UTF-8 bytes) | | endpoint | string | No | Endpoint used (max 250 UTF-8 bytes) | | feedbackUri | string | No | URI to detailed feedback file (IPFS/HTTPS, max 250 bytes) | | feedbackFileHash | Buffer | No | SHA-256 of feedback file content (32 bytes). Binds file to on-chain SEAL |
Value encoding patterns
// Percentage with 2 decimals: 99.75%
const feedbackExamples = [
{ value: '99.75', tag1: Tag.uptime },
// -> encoded: value=9975n, valueDecimals=2
// Milliseconds: 250ms
{ value: 250, tag1: Tag.responseTime, valueDecimals: 0 },
// Currency: $150.25
{ value: '150.25', tag1: Tag.revenues, tag2: Tag.week },
// Negative PnL: -$15.5
{ value: '-15.5', tag1: Tag.tradingYield, tag2: Tag.month },
// Binary check: reachable
{ value: 1, tag1: Tag.reachable, valueDecimals: 0, score: 100 },
// Quality rating (score only)
{ value: '85', tag1: Tag.starred, score: 85 },
];Score behavior
score: 95-> explicit quality score, used directly by ATOMscore: undefined/null-> ATOM infers from tag1 if it's a known tag (uptime, successRate, starred)- Tags without auto-score (responseTime, revenues, etc.) require explicit
scorefor ATOM impact
ATOM-enabled tags (auto-score from value): starred, uptime, successRate Context-dependent tags (require explicit score): reachable, ownerVerified, responseTime, blocktimeFreshness, revenues, tradingYield
Read feedback
// Single feedback
const fb = await sdk.readFeedback(assetPubkey, clientPubkey, 0);
// fb.value, fb.valueDecimals, fb.score, fb.tag1, fb.tag2, fb.sealHash
// All feedbacks for agent (indexer-backed)
const all = await sdk.readAllFeedback(assetPubkey);
const withRevoked = await sdk.readAllFeedback(assetPubkey, true);
// Last feedback index for a specific client (NOT a count)
const lastIndex = await sdk.getLastIndex(assetPubkey, clientPubkey);
const nextIndex = lastIndex + 1n; // if no feedback yet: lastIndex = -1n, nextIndex = 0n
// All clients who gave feedback (indexer-backed)
const clients = await sdk.getClients(assetPubkey);
// Via indexer (faster, no premium RPC needed)
const feedbacks = await sdk.getFeedbacksFromIndexer(assetPubkey, { limit: 50 });
const byEndpoint = await sdk.getFeedbacksByEndpoint('/api/generate');
const byTag = await sdk.getFeedbacksByTag('uptime');Revoke feedback
// Requires the sealHash from the original feedback
const fb = await sdk.readFeedback(assetPubkey, clientPubkey, 0);
await sdk.revokeFeedback(assetPubkey, 0, fb.sealHash!);Respond to feedback (as agent owner)
await sdk.appendResponse(
assetPubkey,
clientPubkey,
0, // feedbackIndex
fb.sealHash!, // sealHash from the feedback
`ipfs://${responseCid}`, // response URI
);
// Read responses
const responses = await sdk.readResponses(assetPubkey, clientPubkey, 0);
const count = await sdk.getResponseCount(assetPubkey, clientPubkey, 0);6. Reputation & ATOM Engine
Quick reputation summary
const summary = await sdk.getSummary(assetPubkey);
// summary.averageScore -> 0-100
// summary.totalFeedbacks
// summary.positiveCount -> score >= 50
// summary.negativeCount -> score < 50
// With filters
const filtered = await sdk.getSummary(assetPubkey, 70); // minScore=70
const byClient = await sdk.getSummary(assetPubkey, undefined, clientPubkey);ATOM stats (on-chain reputation engine)
const atom = await sdk.getAtomStats(assetPubkey);
if (atom) {
atom.quality_score; // 0-10000 (divide by 100 for percentage)
atom.confidence; // 0-10000
atom.ema_score_fast; // Fast EMA (reacts quickly)
atom.ema_score_slow; // Slow EMA (stable baseline)
atom.ema_volatility; // Score instability
atom.diversity_ratio; // 0-255 (unique caller diversity)
atom.risk_score; // 0-100
atom.trust_tier; // 0-4
// Helper methods
atom.getQualityPercent(); // quality_score / 100
atom.getConfidencePercent(); // confidence / 100
atom.getAverageScore(); // ema_score_slow / 100
atom.estimateUniqueClients(); // HyperLogLog estimation
atom.getTrustTier(); // TrustTier enum
}Trust tier
const tier = await sdk.getTrustTier(assetPubkey);
// TrustTier.Unrated = 0
// TrustTier.Bronze = 1
// TrustTier.Silver = 2
// TrustTier.Gold = 3
// TrustTier.Platinum = 4
const name = trustTierToString(tier); // "Gold"Enriched summary (ATOM + raw feedback combined)
const enriched = await sdk.getEnrichedSummary(assetPubkey);
if (enriched) {
enriched.trustTier;
enriched.qualityScore; // 0-10000
enriched.confidence; // 0-10000
enriched.riskScore; // 0-100
enriched.diversityRatio; // 0-255
enriched.uniqueCallers; // HLL estimate
enriched.emaScoreFast;
enriched.emaScoreSlow;
enriched.volatility;
enriched.totalFeedbacks;
enriched.averageScore; // 0-100
enriched.positiveCount;
enriched.negativeCount;
}Reputation from indexer
const rep = await sdk.getAgentReputationFromIndexer(assetPubkey);7. Signing & Verification
Sign data with agent wallet
const signedJson = sdk.sign(assetPubkey, {
action: 'authorize',
target: 'task-123',
timestamp: Date.now(),
});
// Returns: JSON string of SignedPayloadV1Verify signature
// From JSON string
const isValidFromJson = await sdk.verify(signedJson, assetPubkey);
// From IPFS URI
const isValidFromIpfs = await sdk.verify('ipfs://QmPayload...', assetPubkey);
// From HTTPS URL
const isValidFromHttps = await sdk.verify('https://example.com/signed.json', assetPubkey);
// From file path
const isValidFromFile = await sdk.verify('./signed-payload.json', assetPubkey);
// With explicit public key
const isValidWithPubkey = await sdk.verify(signedJson, assetPubkey, walletPubkey);SignedPayloadV1 format
const exampleSignedPayload = {
v: 1,
alg: 'ed25519',
asset: 'base58...', // agent asset pubkey
nonce: 'random-base58',
issuedAt: 1234567890, // unix seconds
data: { action: 'authorize', target: 'task-123' }, // your payload
sig: 'base58...', // Ed25519 signature
};8. Liveness Check
const defaultReport = await sdk.isItAlive(assetPubkey);
// report.status: 'live' | 'partially' | 'not_live'
// report.okCount, report.totalPinged, report.skippedCount
// report.liveServices[], report.deadServices[], report.skippedServices[]
// With options
const tunedReport = await sdk.isItAlive(assetPubkey, {
timeoutMs: 10000,
concurrency: 2,
treatAuthAsAlive: true, // 401/403 = alive
includeTypes: [ServiceType.MCP], // only check MCP endpoints
});9. SEAL v1 (Feedback Authenticity)
SEAL provides client-side hash computation matching on-chain Keccak256. Required for revokeFeedback() and appendResponse().
// Build params (with optional feedbackFileHash)
const fileHash = await SolanaSDK.computeHash(JSON.stringify(feedbackFile));
const params = createSealParams(
9977n, // value (i128)
2, // decimals
85, // score (or null)
'uptime', // tag1
'day', // tag2
'https://api.example.com', // endpoint (or null)
'ipfs://QmFeedback...', // feedbackUri
fileHash, // feedbackFileHash (or null)
);
// Validate inputs before hashing (throws on invalid params)
validateSealInputs(params);
// Compute hash
const sealHash = computeSealHash(params); // Buffer, 32 bytes (Keccak256)
// Verify
const valid = verifySealHash({ ...params, sealHash }); // true
// Compute feedback leaf (for hash-chain verification)
const leaf = computeFeedbackLeafV1(
assetPubkey.toBuffer(),
clientPubkey.toBuffer(),
0n, // feedbackIndex
sealHash,
12345n, // slot
);Field size limits
| Field | Max bytes | Constant | |-------|-----------|----------| | tag1, tag2 | 32 UTF-8 | MAX_TAG_LEN | | endpoint | 250 UTF-8 | MAX_ENDPOINT_LEN | | feedbackUri | 250 UTF-8 | MAX_URI_LEN | | feedbackFileHash | 32 exact | - |
10. Integrity Verification
Quick check (O(1))
const integrity = await sdk.verifyIntegrity(assetPubkey);
// integrity.valid -> boolean
// integrity.status -> 'valid' | 'syncing' | 'corrupted' | 'error'
// integrity.trustworthy -> boolean
// integrity.totalLag -> bigint (blocks behind)
// integrity.chains.feedback, .response, .revokeDeep verification (spot checks)
const deep = await sdk.verifyIntegrityDeep(assetPubkey, {
spotChecks: 10,
checkBoundaries: true,
verifyContent: false, // true = also verify IPFS content hashes (slow)
});
// deep.spotChecksPassed, deep.missingItems, deep.modifiedItemsFull hash-chain replay
const full = await sdk.verifyIntegrityFull(assetPubkey, {
onProgress: (chain, count, total) => {
console.log(`${chain}: ${count}/${total}`);
},
});11. Search & Discovery
Search agents (via indexer)
const results = await sdk.searchAgents({
owner: 'base58...',
creator: 'base58...',
collection: 'base58...', // base registry collection pubkey
collectionPointer: 'c1:bafybeigdyr...',
parentAsset: 'base58...',
wallet: 'base58...',
colLocked: true,
limit: 20,
offset: 0,
});Leaderboard
const top = await sdk.getLeaderboard({
minTier: 2, // Silver+
limit: 50,
collection: 'base58...',
});Global stats
const global = await sdk.getGlobalStats();
// global.total_agents, total_feedbacks, platinum_agents, gold_agents, avg_qualityFind agent by wallet
const agent = await sdk.getAgentByWallet(walletPubkey.toBase58());Endpoint crawler
const crawler = new EndpointCrawler(5000);
const mcp = await crawler.fetchMcpCapabilities('https://agent.com/mcp');
// mcp.mcpTools, mcp.mcpPrompts, mcp.mcpResources
const a2a = await crawler.fetchA2aCapabilities('https://agent.com');
// a2a.a2aSkills12. SDK Introspection
// Chain identity (CAIP-2 format)
const chain = await sdk.chainId(); // 'solana-devnet'
const cluster = sdk.getCluster(); // 'devnet' | 'testnet' | 'mainnet-beta' | 'localnet'
// Program IDs
const programs = sdk.getProgramIds();
// programs.identityRegistry -> PublicKey
// programs.reputationRegistry -> PublicKey
// programs.agentRegistry -> PublicKey
// programs.atomEngine -> PublicKey
// Registry addresses as strings (parity with agent0-ts)
const regs = sdk.registries();
// { IDENTITY: 'base58...', REPUTATION: 'base58...', ...legacy aliases }
// RPC info
const rpcUrl = sdk.getRpcUrl();
const isDefaultRpc = sdk.isUsingDefaultDevnetRpc();
const canBulkQuery = sdk.supportsAdvancedQueries();
const readOnly = sdk.isReadOnly;
// Base collection (single-collection architecture)
const base = await sdk.getBaseCollection();
// Advanced: access underlying clients
const solanaClient = sdk.getSolanaClient();
const feedbackMgr = sdk.getFeedbackManager();13. Tags Reference
Category tags (tag1)
| Constant | String | Value Type | ATOM Auto-Score | |----------|--------|-----------|-----------------| | Tag.starred | 'starred' | 0-100 | Yes | | Tag.uptime | 'uptime' | percentage | Yes | | Tag.successRate | 'successRate' | percentage | Yes | | Tag.reachable | 'reachable' | 0 or 1 | No | | Tag.ownerVerified | 'ownerVerified' | 0 or 1 | No | | Tag.responseTime | 'responseTime' | ms | No | | Tag.blocktimeFreshness | 'blocktimeFreshness' | blocks | No | | Tag.revenues | 'revenues' | currency | No | | Tag.tradingYield | 'tradingYield' | percentage | No |
Period tags (tag2)
| Constant | String | |----------|--------| | Tag.day | 'day' | | Tag.week | 'week' | | Tag.month | 'month' | | Tag.year | 'year' |
x402 tags (tag1) - Client -> Agent
| Constant | String | |----------|--------| | Tag.x402ResourceDelivered | 'x402-resource-delivered' | | Tag.x402DeliveryFailed | 'x402-delivery-failed' | | Tag.x402DeliveryTimeout | 'x402-delivery-timeout' | | Tag.x402QualityIssue | 'x402-quality-issue' |
x402 tags (tag1) - Agent -> Client
| Constant | String | |----------|--------| | Tag.x402GoodPayer | 'x402-good-payer' | | Tag.x402PaymentFailed | 'x402-payment-failed' | | Tag.x402InsufficientFunds | 'x402-insufficient-funds' | | Tag.x402InvalidSignature | 'x402-invalid-signature' |
x402 network tags (tag2)
| Constant | String | |----------|--------| | Tag.x402Evm | 'exact-evm' | | Tag.x402Svm | 'exact-svm' |
Tag utilities
isKnownTag('uptime'); // true
isKnownTag('custom-metric'); // false
getTagDescription('successRate'); // 'Task completion success percentage'Custom tags are fully supported - any string up to 32 UTF-8 bytes.
14. OASF Taxonomy
// List taxonomy slugs
const skills = getAllSkills(); // 136 skills
const domains = getAllDomains(); // 204 domains15. Hash Utilities
// SHA-256 (async, browser-compatible via WebCrypto)
const hash = await SolanaSDK.computeHash('My feedback content');
const bufHash = await SolanaSDK.computeHash(Buffer.from(jsonData));
// Returns: Buffer (32 bytes)
// URI hash (zeros for IPFS/Arweave since CID is already content-addressable)
const uriHash = await SolanaSDK.computeUriHash('https://example.com/data.json');
// -> SHA-256 of the URI string
const ipfsHash = await SolanaSDK.computeUriHash('ipfs://Qm...');
// -> Buffer.alloc(32) (zeros)
// Keccak-256 (synchronous, used by SEAL v1)
import { keccak256 } from '8004-solana';
const k = keccak256(Buffer.from('data'));
// SHA-256 sync (CJS context only — uses require('crypto'), throws in pure ESM or browser)
import { sha256Sync } from '8004-solana';
const s = sha256Sync('data'); // Uint8Array (32 bytes)16. Value Encoding
import {
encodeReputationValue,
decodeToDecimalString,
decodeToNumber,
} from '8004-solana';
// Encode: decimal string -> { value: bigint, valueDecimals: number }
const encoded = encodeReputationValue('99.77');
// { value: 9977n, valueDecimals: 2, normalized: '99.77' }
const neg = encodeReputationValue('-15.5');
// { value: -155n, valueDecimals: 1, normalized: '-15.5' }
const raw = encodeReputationValue(9977n, 2);
// { value: 9977n, valueDecimals: 2, normalized: '99.77' }
// Decode: bigint + decimals -> string or number
decodeToDecimalString(9977n, 2); // '99.77'
decodeToDecimalString(-155n, 1); // '-15.5'
decodeToNumber(9977n, 2); // 99.77
// Limits: max 18 decimal places, clamped to i128 range17. Canonical JSON & Signing Utilities
import {
canonicalizeJson,
normalizeSignData,
createNonce,
} from '8004-solana';
// RFC 8785 canonical JSON (deterministic key ordering, no whitespace)
canonicalizeJson({ b: 2, a: 1 }); // '{"a":1,"b":2}'
// Normalize data for signing (handles BigInt, PublicKey, Date, Buffer)
const normalized = normalizeSignData({
amount: 100n, // -> { $bigint: '100' }
key: somePubkey, // -> { $pubkey: 'base58...' }
when: new Date(), // -> { $date: 'ISO...' }
data: Buffer.from([1]), // -> { $bytes: 'AQ==', encoding: 'base64' }
});
// Cryptographic nonce generation (base58-encoded random bytes)
const nonce = createNonce(); // 16 bytes default
const nonce32 = createNonce(32); // 32 bytes18. IPFS Operations
// Add data
const jsonCid = await ipfs.addJson({ key: 'value' });
const rawCid = await ipfs.add('raw string data');
const fileCid = await ipfs.addFile('./image.png');
// Add registration file (normalizes and formats)
const registrationCid = await ipfs.addRegistrationFile(registrationFile);
// Retrieve
const data = await ipfs.get(jsonCid); // raw string
const json = await ipfs.getJson(jsonCid); // parsed JSON
const reg = await ipfs.getRegistrationFile(registrationCid); // RegistrationFile
// Supports ipfs:// prefix
const ipfsPrefixedData = await ipfs.get('ipfs://QmAbc...');
// Pin/unpin
await ipfs.pin(fileCid);
await ipfs.unpin(fileCid);
// Cleanup
await ipfs.close();19. Server Mode (skipSend)
For browser wallets or external signing:
// Get unsigned transaction
const assetKeypair = Keypair.generate();
const prepared = await sdk.registerAgent(uri, {
skipSend: true,
signer: ownerPubkey, // required when SDK has no signer
assetPubkey: assetKeypair.publicKey, // required in skipSend mode
});
// prepared.transaction -> base64 serialized unsigned transaction
// prepared.signer -> base58 of the required signer
// For agent wallet (browser wallet flow)
const { message, complete } = await sdk.prepareSetAgentWallet(
assetPubkey,
walletPubkey,
{ signer: ownerPubkey } // optional if SDK was initialized with signer
);
const signature = await phantomWallet.signMessage(message);
await complete(signature);All write methods accept { skipSend: true } in their options.
20. Indexer Client (Direct Access)
const indexer = sdk.getIndexerClient();
// Check availability
const available = await sdk.isIndexerAvailable();
// Direct queries
const agents = await indexer.getAgentsByOwner('base58...');
const feedbacks = await indexer.getFeedbacks('base58...', { limit: 100 });
const leaderboard = await indexer.getLeaderboard({ minTier: 3 });Wait for indexer sync after write
await sdk.giveFeedback(assetPubkey, feedbackParams);
// Poll until indexer catches up (returns true if synced, false on timeout)
const synced = await sdk.waitForIndexerSync(
async () => {
const fbs = await sdk.getFeedbacksFromIndexer(assetPubkey);
return fbs.length >= expectedCount;
},
{ timeout: 30000 } // ms, default 30s
);21. Error Handling
import {
IndexerError,
IndexerUnavailableError,
IndexerTimeoutError,
IndexerRateLimitError,
UnsupportedRpcError,
RpcNetworkError,
} from '8004-solana';
try {
const agents = await sdk.getAllAgents();
} catch (e) {
if (e instanceof UnsupportedRpcError) {
// Default RPC doesn't support getProgramAccounts
// Use Helius, QuickNode, or Alchemy
}
if (e instanceof IndexerUnavailableError) {
// Indexer is down, SDK falls back to RPC if indexerFallback=true
}
if (e instanceof IndexerRateLimitError) {
// Back off and retry
}
}Methods requiring premium RPC
getAllAgents(), getAgentsByOwner(), getCollectionAgents(), getCollections()
Indexer-backed reads (readAllFeedback(), getClients(), getLastIndex(), readResponses()) do not require premium RPC, but they do require a reachable indexer.
22. Operation Costs (Solana devnet)
| Operation | Cost | Notes | |-----------|------|-------| | registerAgent() | ~0.00651 SOL | Base registration (without ATOM init) | | registerAgent(..., { atomEnabled: true }) | higher than base | Includes ATOM stats initialization in the same transaction | | giveFeedback() (1st for agent) | ~0.00332 SOL | Creates reputation PDA | | giveFeedback() (subsequent) | ~0.00209 SOL | Feedback PDA only | | setMetadata() (1st key) | ~0.00319 SOL | PDA rent | | setMetadata() (update) | ~0.000005 SOL | TX fee only | | appendResponse() (1st) | ~0.00275 SOL | Response + index PDAs | | appendResponse() (subsequent) | ~0.00163 SOL | Response PDA only | | revokeFeedback() | ~0.000005 SOL | TX fee only | | deleteMetadata() | recovers rent | Returns lamports to owner |
23. Common Patterns
Monitor agent health
const report = await sdk.isItAlive(assetPubkey);
if (report.status !== 'live') {
await sdk.giveFeedback(assetPubkey, {
value: 0,
valueDecimals: 0,
tag1: Tag.reachable,
score: 0,
feedbackUri: `ipfs://${alertCid}`,
});
}Periodic uptime reporting
const uptimePercent = calculateUptime(); // your logic
await sdk.giveFeedback(assetPubkey, {
value: uptimePercent.toFixed(2), // "99.75" auto-encodes
tag1: Tag.uptime,
tag2: Tag.day,
feedbackUri: `ipfs://${reportCid}`,
});Trust-gated interaction
const tier = await sdk.getTrustTier(assetPubkey);
if (tier < TrustTier.Silver) {
throw new Error('Agent trust too low for this operation');
}x402 payment feedback (full flow)
// 1. Build the feedback file JSON (off-chain proof of payment)
const feedbackFile = {
version: '1.0',
type: 'x402-feedback',
agent: assetPubkey.toBase58(),
client: clientPubkey.toBase58(),
endpoint: '/api/generate',
timestamp: new Date().toISOString(),
proofOfPayment: {
txHash: 'base58-tx-signature...',
fromAddress: clientPubkey.toBase58(),
toAddress: agentWalletPubkey.toBase58(),
amount: '0.001',
token: 'SOL',
chainId: await sdk.chainId(), // 'solana-devnet'
},
settlement: {
success: true,
network: 'solana',
settledAt: new Date().toISOString(),
},
result: {
delivered: true,
latencyMs: 230,
quality: 'good',
},
};
// 2. Upload to IPFS
const feedbackCid = await ipfs.addJson(feedbackFile);
// 3. Optionally compute content hash for SEAL integrity
const feedbackFileHash = await SolanaSDK.computeHash(
JSON.stringify(feedbackFile)
);
// 4. Submit on-chain feedback with proof link
await sdk.giveFeedback(assetPubkey, {
value: '100.00',
tag1: Tag.x402ResourceDelivered,
tag2: Tag.x402Svm,
score: 95,
endpoint: '/api/generate',
feedbackUri: `ipfs://${feedbackCid}`,
feedbackFileHash, // links file content to on-chain SEAL
});
// Agent-side: report good payer
await sdk.giveFeedback(clientAgentPubkey, {
value: '1',
valueDecimals: 0,
tag1: Tag.x402GoodPayer,
tag2: Tag.x402Svm,
score: 100,
feedbackUri: `ipfs://${payerProofCid}`,
});Verify before trusting indexer data
const integrity = await sdk.verifyIntegrity(assetPubkey);
if (!integrity.trustworthy) {
console.warn(`Indexer data not trustworthy: ${integrity.status}`);
// Fall back to on-chain queries
}24. Program IDs
import {
PROGRAM_ID, // devnet default Agent Registry
ATOM_ENGINE_PROGRAM_ID, // devnet default ATOM
DEVNET_AGENT_REGISTRY_PROGRAM_ID,
DEVNET_ATOM_ENGINE_PROGRAM_ID,
MAINNET_AGENT_REGISTRY_PROGRAM_ID,
MAINNET_ATOM_ENGINE_PROGRAM_ID,
MPL_CORE_PROGRAM_ID,
} from '8004-solana';Cluster mapping in SDK defaults:
devnet/testnet/localnet: devnet program defaults unless overridden.mainnet-beta: mainnet program defaults.