| name | 8004-solana-sdk |
| description | TypeScript SDK for the 8004 Trustless Agent Registry on Solana. Covers agent registration, feedback/SEAL v1, ATOM reputation engine, signing, indexer queries, x402 payment feedback, and skipSend server-mode patterns. |
| version | 0.6.3 |
| homepage | https://github.com/QuantuLabs/8004-solana-ts |
| metadata | {"openclaw":{"emoji":"🔗","requires":{"bins":["node"],"env":["SOLANA_PRIVATE_KEY"]},"primaryEnv":"SOLANA_PRIVATE_KEY","os":["darwin","linux","windows"]}} |
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.6.x):
- Single-collection architecture is active.
createCollection() and updateCollectionUri() are deprecated and return { success: false, error }.
- Feedback reads (
readAllFeedback, getClients, getLastIndex, readFeedback, etc.) rely on the indexer.
Install
npm install 8004-solana @solana/web3.js
Imports
import {
SolanaSDK,
IPFSClient,
buildRegistrationFileJson,
ServiceType,
TrustTier,
Tag,
AtomStats,
trustTierToString,
computeSealHash,
computeFeedbackLeafV1,
verifySealHash,
createSealParams,
validateSealInputs,
MAX_TAG_LEN,
MAX_ENDPOINT_LEN,
MAX_URI_LEN,
getAllSkills,
getAllDomains,
isKnownTag,
getTagDescription,
buildSignedPayload,
verifySignedPayload,
parseSignedPayload,
normalizeSignData,
createNonce,
canonicalizeJson,
encodeReputationValue,
decodeToDecimalString,
decodeToNumber,
keccak256,
sha256,
sha256Sync,
replayFeedbackChain,
replayResponseChain,
replayRevokeChain,
IndexerClient,
EndpointCrawler,
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,
});
Full config
const sdk = new SolanaSDK({
cluster: 'devnet',
rpcUrl: 'https://...',
signer: keypair,
indexerUrl: 'https://xxx.supabase.co/rest/v1',
indexerApiKey: process.env.INDEXER_API_KEY,
useIndexer: true,
indexerFallback: true,
forceOnChain: false,
});
IPFS client
const ipfsPinata = new IPFSClient({
pinataEnabled: true,
pinataJwt: process.env.PINATA_JWT!,
});
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' },
],
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}`);
Step 4: Set operational wallet
const opWallet = Keypair.generate();
await sdk.setAgentWallet(result.asset, opWallet);
Collection (v0.6.x: single-collection)
All agents register into the base collection automatically. createCollection() and updateCollectionUri() are deprecated and return { success: false }.
const baseCollection = await sdk.getBaseCollection();
3. Read Agent Data
const agent = await sdk.loadAgent(assetPubkey);
const exists = await sdk.agentExists(assetPubkey);
const owner = await sdk.getAgentOwner(assetPubkey);
const isMine = await sdk.isAgentOwner(assetPubkey, myPubkey);
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
await sdk.setAgentUri(assetPubkey, collectionPubkey, `ipfs://${newCid}`);
await sdk.setMetadata(assetPubkey, 'version', '2.0.0');
await sdk.setMetadata(assetPubkey, 'certification', 'audited-2026', true);
await sdk.deleteMetadata(assetPubkey, 'version');
await sdk.transferAgent(assetPubkey, collectionPubkey, newOwnerPubkey);
await sdk.syncOwner(assetPubkey);
5. Feedback System
Give feedback
await sdk.giveFeedback(assetPubkey, {
value: '99.77',
tag1: Tag.uptime,
tag2: Tag.day,
score: 95,
endpoint: '/api/v1/generate',
feedbackUri: `ipfs://${feedbackCid}`,
feedbackFileHash,
});
GiveFeedbackParams reference
| Field | Type | Required | Description |
|---|
value | string | number | bigint | Yes | Metric value. Strings auto-encode decimals ("99.77" -> 9977n, 2) |
valueDecimals | number (0-6) | 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 | Yes | 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
const feedbackExamples = [
{ value: '99.75', tag1: Tag.uptime },
{ value: 250, tag1: Tag.responseTime, valueDecimals: 0 },
{ value: '150.25', tag1: Tag.revenues, tag2: Tag.week },
{ value: '-15.5', tag1: Tag.tradingYield, tag2: Tag.month },
{ value: 1, tag1: Tag.reachable, valueDecimals: 0, score: 100 },
{ value: '85', tag1: Tag.starred, score: 85 },
];
Score behavior
score: 95 -> explicit quality score, used directly by ATOM
score: undefined/null -> ATOM infers from tag1 if it's a known tag (uptime, successRate, starred)
- Tags without auto-score (responseTime, revenues, etc.) require explicit
score for 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
const fb = await sdk.readFeedback(assetPubkey, clientPubkey, 0);
const all = await sdk.readAllFeedback(assetPubkey);
const withRevoked = await sdk.readAllFeedback(assetPubkey, true);
const lastIndex = await sdk.getLastIndex(assetPubkey, clientPubkey);
const nextIndex = lastIndex + 1n;
const clients = await sdk.getClients(assetPubkey);
const feedbacks = await sdk.getFeedbacksFromIndexer(assetPubkey, { limit: 50 });
const byEndpoint = await sdk.getFeedbacksByEndpoint('/api/generate');
const byTag = await sdk.getFeedbacksByTag('uptime');
Revoke 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,
fb.sealHash!,
`ipfs://${responseCid}`,
);
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);
const filtered = await sdk.getSummary(assetPubkey, 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;
atom.confidence;
atom.ema_score_fast;
atom.ema_score_slow;
atom.ema_volatility;
atom.diversity_ratio;
atom.risk_score;
atom.trust_tier;
atom.getQualityPercent();
atom.getConfidencePercent();
atom.getAverageScore();
atom.estimateUniqueClients();
atom.getTrustTier();
}
Trust tier
const tier = await sdk.getTrustTier(assetPubkey);
const name = trustTierToString(tier);
Enriched summary (ATOM + raw feedback combined)
const enriched = await sdk.getEnrichedSummary(assetPubkey);
if (enriched) {
enriched.trustTier;
enriched.qualityScore;
enriched.confidence;
enriched.riskScore;
enriched.diversityRatio;
enriched.uniqueCallers;
enriched.emaScoreFast;
enriched.emaScoreSlow;
enriched.volatility;
enriched.totalFeedbacks;
enriched.averageScore;
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(),
});
Verify signature
const isValidFromJson = await sdk.verify(signedJson, assetPubkey);
const isValidFromIpfs = await sdk.verify('ipfs://QmPayload...', assetPubkey);
const isValidFromHttps = await sdk.verify('https://example.com/signed.json', assetPubkey);
const isValidFromFile = await sdk.verify('./signed-payload.json', assetPubkey);
const isValidWithPubkey = await sdk.verify(signedJson, assetPubkey, walletPubkey);
SignedPayloadV1 format
const exampleSignedPayload = {
v: 1,
alg: 'ed25519',
asset: 'base58...',
nonce: 'random-base58',
issuedAt: 1234567890,
data: { action: 'authorize', target: 'task-123' },
sig: 'base58...',
};
8. Liveness Check
const defaultReport = await sdk.isItAlive(assetPubkey);
const tunedReport = await sdk.isItAlive(assetPubkey, {
timeoutMs: 10000,
concurrency: 2,
treatAuthAsAlive: true,
includeTypes: [ServiceType.MCP],
});
9. SEAL v1 (Feedback Authenticity)
SEAL provides client-side hash computation matching on-chain Keccak256. Required for revokeFeedback() and appendResponse().
const fileHash = await SolanaSDK.computeHash(JSON.stringify(feedbackFile));
const params = createSealParams(
9977n,
2,
85,
'uptime',
'day',
'https://api.example.com',
'ipfs://QmFeedback...',
fileHash,
);
validateSealInputs(params);
const sealHash = computeSealHash(params);
const valid = verifySealHash({ ...params, sealHash });
const leaf = computeFeedbackLeafV1(
assetPubkey.toBuffer(),
clientPubkey.toBuffer(),
0n,
sealHash,
12345n,
);
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);
Deep verification (spot checks)
const deep = await sdk.verifyIntegrityDeep(assetPubkey, {
spotChecks: 10,
checkBoundaries: true,
verifyContent: false,
});
Full 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...',
collection: 'base58...',
wallet: 'base58...',
limit: 20,
offset: 0,
});
Leaderboard
const top = await sdk.getLeaderboard({
minTier: 2,
limit: 50,
collection: 'base58...',
});
Global stats
const global = await sdk.getGlobalStats();
Find 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');
const a2a = await crawler.fetchA2aCapabilities('https://agent.com');
12. SDK Introspection
const chain = await sdk.chainId();
const cluster = sdk.getCluster();
const programs = sdk.getProgramIds();
const regs = sdk.registries();
const rpcUrl = sdk.getRpcUrl();
const isDefaultRpc = sdk.isUsingDefaultDevnetRpc();
const canBulkQuery = sdk.supportsAdvancedQueries();
const readOnly = sdk.isReadOnly;
const base = await sdk.getBaseCollection();
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');
isKnownTag('custom-metric');
getTagDescription('successRate');
Custom tags are fully supported - any string up to 32 UTF-8 bytes.
14. OASF Taxonomy
const skills = getAllSkills();
const domains = getAllDomains();
15. Hash Utilities
const hash = await SolanaSDK.computeHash('My feedback content');
const bufHash = await SolanaSDK.computeHash(Buffer.from(jsonData));
const uriHash = await SolanaSDK.computeUriHash('https://example.com/data.json');
const ipfsHash = await SolanaSDK.computeUriHash('ipfs://Qm...');
import { keccak256 } from '8004-solana';
const k = keccak256(Buffer.from('data'));
import { sha256Sync } from '8004-solana';
const s = sha256Sync('data');
16. Value Encoding
import {
encodeReputationValue,
decodeToDecimalString,
decodeToNumber,
} from '8004-solana';
const encoded = encodeReputationValue('99.77');
const neg = encodeReputationValue('-15.5');
const raw = encodeReputationValue(9977n, 2);
decodeToDecimalString(9977n, 2);
decodeToDecimalString(-155n, 1);
decodeToNumber(9977n, 2);
17. Canonical JSON & Signing Utilities
import {
canonicalizeJson,
normalizeSignData,
createNonce,
} from '8004-solana';
canonicalizeJson({ b: 2, a: 1 });
const normalized = normalizeSignData({
amount: 100n,
key: somePubkey,
when: new Date(),
data: Buffer.from([1]),
});
const nonce = createNonce();
const nonce32 = createNonce(32);
18. IPFS Operations
const jsonCid = await ipfs.addJson({ key: 'value' });
const rawCid = await ipfs.add('raw string data');
const fileCid = await ipfs.addFile('./image.png');
const registrationCid = await ipfs.addRegistrationFile(registrationFile);
const data = await ipfs.get(jsonCid);
const json = await ipfs.getJson(jsonCid);
const reg = await ipfs.getRegistrationFile(registrationCid);
const ipfsPrefixedData = await ipfs.get('ipfs://QmAbc...');
await ipfs.pin(fileCid);
await ipfs.unpin(fileCid);
await ipfs.close();
19. Server Mode (skipSend)
For browser wallets or external signing:
const assetKeypair = Keypair.generate();
const prepared = await sdk.registerAgent(uri, undefined, {
skipSend: true,
signer: ownerPubkey,
assetPubkey: assetKeypair.publicKey,
});
const { message, complete } = await sdk.prepareSetAgentWallet(
assetPubkey,
walletPubkey,
{ signer: ownerPubkey }
);
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();
const available = await sdk.isIndexerAvailable();
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);
const synced = await sdk.waitForIndexerSync(
async () => {
const fbs = await sdk.getFeedbacksFromIndexer(assetPubkey);
return fbs.length >= expectedCount;
},
{ timeout: 30000 }
);
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) {
}
if (e instanceof IndexerUnavailableError) {
}
if (e instanceof IndexerRateLimitError) {
}
}
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 | Includes ATOM auto-init |
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();
await sdk.giveFeedback(assetPubkey, {
value: uptimePercent.toFixed(2),
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)
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(),
},
settlement: {
success: true,
network: 'solana',
settledAt: new Date().toISOString(),
},
result: {
delivered: true,
latencyMs: 230,
quality: 'good',
},
};
const feedbackCid = await ipfs.addJson(feedbackFile);
const feedbackFileHash = await SolanaSDK.computeHash(
JSON.stringify(feedbackFile)
);
await sdk.giveFeedback(assetPubkey, {
value: '100.00',
tag1: Tag.x402ResourceDelivered,
tag2: Tag.x402Svm,
score: 95,
endpoint: '/api/generate',
feedbackUri: `ipfs://${feedbackCid}`,
feedbackFileHash,
});
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}`);
}
24. Program IDs
import {
PROGRAM_ID,
MPL_CORE_PROGRAM_ID,
ATOM_ENGINE_PROGRAM_ID,
} from '8004-solana';