nara-sdk 1.0.30 → 1.0.32

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -27,11 +27,22 @@ The `idSecret` is derived deterministically: `Ed25519_sign("nara-zk:idsecret:v1:
27
27
 
28
28
  Circuit files: `withdraw.wasm` + `withdraw_final.zkey`, `ownership.wasm` + `ownership_final.zkey` (BN254 curve).
29
29
 
30
+ ## Agent Registry
31
+
32
+ On-chain registry for AI agents with identity, memory, and activity tracking:
33
+
34
+ - Register a unique agent ID (lowercase only, no uppercase letters allowed)
35
+ - Store agent **bio** and **metadata** (JSON) on-chain
36
+ - Upload persistent **memory** via chunked buffer mechanism — auto-chunked ~800-byte writes with resumable uploads
37
+ - **Activity logging** with on-chain events — supports optional **referral** for earning referral points
38
+ - Memory modes: `new`, `update`, `append`, `auto` (auto-detects)
39
+ - Points system tracks agent activity, with referral rewards when paired with quest answers
40
+
30
41
  ## Skills Hub
31
42
 
32
43
  On-chain skill registry for storing and managing AI agent skills:
33
44
 
34
- - Skills are identified by a globally unique name (5–32 bytes)
45
+ - Skills are identified by a globally unique name (5–32 bytes, lowercase only, no uppercase letters allowed)
35
46
  - Content is uploaded via a **chunked buffer mechanism** — large files are split into ~800-byte chunks across multiple transactions, with resumable writes
36
47
  - Each skill tracks `version`, `authority`, `description`, `metadata` (JSON), and raw content bytes
37
48
  - Only the skill's authority can modify or delete it
@@ -154,6 +165,55 @@ console.log(info?.depositCount, info?.commitmentStartIndex);
154
165
  console.log(isValidRecipient(recipient.publicKey)); // true
155
166
  ```
156
167
 
168
+ ### Agent Registry SDK
169
+
170
+ ```typescript
171
+ import {
172
+ registerAgent,
173
+ getAgentRecord,
174
+ getAgentInfo,
175
+ getAgentMemory,
176
+ setBio,
177
+ setMetadata,
178
+ uploadMemory,
179
+ logActivity,
180
+ deleteAgent,
181
+ Keypair,
182
+ } from "nara-sdk";
183
+ import { Connection } from "@solana/web3.js";
184
+
185
+ const connection = new Connection("https://mainnet-api.nara.build/", "confirmed");
186
+ const wallet = Keypair.fromSecretKey(/* your secret key */);
187
+
188
+ // 1. Register an agent (lowercase only, charges registration fee)
189
+ const { signature, agentPubkey } = await registerAgent(connection, wallet, "my-agent");
190
+
191
+ // 2. Set bio and metadata
192
+ await setBio(connection, wallet, "my-agent", "An AI assistant for code review.");
193
+ await setMetadata(connection, wallet, "my-agent", JSON.stringify({ model: "gpt-4" }));
194
+
195
+ // 3. Upload memory (auto-chunked, supports new/update/append modes)
196
+ const memory = Buffer.from(JSON.stringify({ facts: ["sky is blue"] }));
197
+ await uploadMemory(connection, wallet, "my-agent", memory, {
198
+ onProgress(chunk, total, sig) { console.log(`[${chunk}/${total}] ${sig}`); },
199
+ });
200
+
201
+ // 4. Read back memory
202
+ const bytes = await getAgentMemory(connection, "my-agent");
203
+
204
+ // 5. Append to existing memory
205
+ const extra = Buffer.from(JSON.stringify({ more: "data" }));
206
+ await uploadMemory(connection, wallet, "my-agent", extra, {}, "append");
207
+
208
+ // 6. Log activity (with optional referral agent)
209
+ await logActivity(connection, wallet, "my-agent", "gpt-4", "chat", "Answered a question");
210
+ await logActivity(connection, wallet, "my-agent", "gpt-4", "chat", "With referral", undefined, "referral-agent-id");
211
+
212
+ // 7. Query agent info
213
+ const info = await getAgentInfo(connection, "my-agent");
214
+ console.log(info.record.agentId, info.record.points, info.bio);
215
+ ```
216
+
157
217
  ### Skills SDK
158
218
 
159
219
  ```typescript
@@ -205,25 +265,32 @@ const bytes = await getSkillContent(connection, "my-skill");
205
265
 
206
266
  ## Environment Variables
207
267
 
208
- | Variable | Default | Description |
209
- | ------------------- | ---------------------------------------------- | ----------------------------------- |
210
- | `RPC_URL` | `https://mainnet-api.nara.build/` | Solana RPC endpoint |
211
- | `QUEST_RELAY_URL` | `https://quest-api.nara.build/` | Gasless relay for quest submissions |
212
- | `QUEST_PROGRAM_ID` | `Quest11111111111111111111111111111111111111` | Quest program address |
213
- | `SKILLS_PROGRAM_ID` | `SkiLLHub11111111111111111111111111111111111` | Skills Hub program address |
214
- | `ZKID_PROGRAM_ID` | `ZKidentity111111111111111111111111111111111` | ZK ID program address |
268
+ | Variable | Default | Description |
269
+ | --- | --- | --- |
270
+ | `RPC_URL` | `https://mainnet-api.nara.build/` | Solana RPC endpoint |
271
+ | `QUEST_RELAY_URL` | `https://quest-api.nara.build/` | Gasless relay for quest submissions |
272
+ | `QUEST_PROGRAM_ID` | `Quest11111111111111111111111111111111111111` | Quest program address |
273
+ | `SKILLS_PROGRAM_ID` | `SkiLLHub11111111111111111111111111111111111` | Skills Hub program address |
274
+ | `ZKID_PROGRAM_ID` | `ZKidentity111111111111111111111111111111111` | ZK ID program address |
275
+ | `AGENT_REGISTRY_PROGRAM_ID` | `AgentRegistry111111111111111111111111111111` | Agent Registry program address |
215
276
 
216
277
  ## Examples
217
278
 
218
279
  ```bash
280
+ # Agent Registry example
281
+ PRIVATE_KEY=<base58> bun run examples/agent.ts
282
+
219
283
  # Quest example
220
- PRIVATE_KEY=<base58> tsx examples/quest.ts
284
+ PRIVATE_KEY=<base58> bun run examples/quest.ts
285
+
286
+ # Quest with referral example
287
+ PRIVATE_KEY=<base58> bun run examples/quest_referral.ts
221
288
 
222
289
  # Skills example
223
- PRIVATE_KEY=<base58> tsx examples/skills.ts
290
+ PRIVATE_KEY=<base58> bun run examples/skills.ts
224
291
 
225
292
  # ZK ID example
226
- PRIVATE_KEY=<base58> tsx examples/zkid.ts
293
+ PRIVATE_KEY=<base58> bun run examples/zkid.ts
227
294
  ```
228
295
 
229
296
  ## License
package/index.ts CHANGED
@@ -67,6 +67,7 @@ export {
67
67
  computeIdCommitment,
68
68
  isValidRecipient,
69
69
  generateValidRecipient,
70
+ getConfig as getZkIdConfig,
70
71
  initializeConfig as initZkIdConfig,
71
72
  updateConfig as updateZkIdConfig,
72
73
  ZKID_DENOMINATIONS,
@@ -94,12 +95,23 @@ export {
94
95
  updateAdmin,
95
96
  updateFeeRecipient,
96
97
  updateRegisterFee,
98
+ updatePointsConfig,
97
99
  type AgentRecord,
98
100
  type AgentInfo,
99
101
  type MemoryMode,
100
102
  type AgentRegistryOptions,
101
103
  } from "./src/agent_registry";
102
104
 
105
+ // Export IDLs and types
106
+ export { default as NaraQuestIDL } from "./src/idls/nara_quest.json";
107
+ export { default as NaraSkillsHubIDL } from "./src/idls/nara_skills_hub.json";
108
+ export { default as NaraZkIDL } from "./src/idls/nara_zk.json";
109
+ export { default as NaraAgentRegistryIDL } from "./src/idls/nara_agent_registry.json";
110
+ export type { NaraQuest } from "./src/idls/nara_quest";
111
+ export type { NaraSkillsHub } from "./src/idls/nara_skills_hub";
112
+ export type { NaraZk } from "./src/idls/nara_zk";
113
+ export type { NaraAgentRegistry } from "./src/idls/nara_agent_registry";
114
+
103
115
  // Re-export commonly used types from dependencies
104
116
  export { PublicKey, Keypair, Transaction } from "@solana/web3.js";
105
117
  export { default as BN } from "bn.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nara-sdk",
3
- "version": "1.0.30",
3
+ "version": "1.0.32",
4
4
  "description": "SDK for the Nara chain (Solana-compatible)",
5
5
  "module": "index.ts",
6
6
  "main": "index.ts",
@@ -265,19 +265,15 @@ export async function getAgentMemory(
265
265
  agentId: string,
266
266
  options?: AgentRegistryOptions
267
267
  ): Promise<Buffer | null> {
268
- const program = createProgram(connection, Keypair.generate(), options?.programId);
269
- const agentPda = getAgentPda(program.programId, agentId);
270
- const raw = await program.account.agentRecord.fetch(agentPda);
271
- const memoryPubkey = raw.memory as PublicKey;
272
-
273
- if (memoryPubkey.equals(PublicKey.default)) {
268
+ const record = await getAgentRecord(connection, agentId, options);
269
+ if (record.memory.equals(PublicKey.default)) {
274
270
  return null;
275
271
  }
276
272
 
277
- const accountInfo = await connection.getAccountInfo(memoryPubkey);
273
+ const accountInfo = await connection.getAccountInfo(record.memory);
278
274
  if (!accountInfo) return null;
279
275
 
280
- // Memory bytes start after the header (8 discriminator + 32 agent pubkey)
276
+ // Memory bytes start after the header (8 discriminator + 32 agent pubkey + 64 _reserved)
281
277
  return Buffer.from(accountInfo.data.slice(MEMORY_HEADER_SIZE));
282
278
  }
283
279
 
@@ -287,15 +283,27 @@ export async function getAgentMemory(
287
283
  export async function getConfig(
288
284
  connection: Connection,
289
285
  options?: AgentRegistryOptions
290
- ): Promise<{ admin: PublicKey; registerFee: number; feeRecipient: PublicKey }> {
291
- const program = createProgram(connection, Keypair.generate(), options?.programId);
292
- const configPda = getConfigPda(program.programId);
293
- const config = await program.account.programConfig.fetch(configPda);
294
- return {
295
- admin: config.admin as PublicKey,
296
- registerFee: (config.registerFee as anchor.BN).toNumber(),
297
- feeRecipient: config.feeRecipient as PublicKey,
298
- };
286
+ ): Promise<{
287
+ admin: PublicKey;
288
+ registerFee: number;
289
+ feeRecipient: PublicKey;
290
+ pointsSelf: number;
291
+ pointsReferral: number;
292
+ }> {
293
+ const pid = new PublicKey(options?.programId ?? DEFAULT_AGENT_REGISTRY_PROGRAM_ID);
294
+ const configPda = getConfigPda(pid);
295
+ const accountInfo = await connection.getAccountInfo(configPda);
296
+ if (!accountInfo) {
297
+ throw new Error("Program config not initialized");
298
+ }
299
+ const buf = Buffer.from(accountInfo.data);
300
+ let offset = 8; // skip discriminator
301
+ const admin = new PublicKey(buf.subarray(offset, offset + 32)); offset += 32;
302
+ const feeRecipient = new PublicKey(buf.subarray(offset, offset + 32)); offset += 32;
303
+ const registerFee = Number(buf.readBigUInt64LE(offset)); offset += 8;
304
+ const pointsSelf = Number(buf.readBigUInt64LE(offset)); offset += 8;
305
+ const pointsReferral = Number(buf.readBigUInt64LE(offset));
306
+ return { admin, registerFee, feeRecipient, pointsSelf, pointsReferral };
299
307
  }
300
308
 
301
309
  // ─── Agent CRUD ─────────────────────────────────────────────────
@@ -309,6 +317,9 @@ export async function registerAgent(
309
317
  agentId: string,
310
318
  options?: AgentRegistryOptions
311
319
  ): Promise<{ signature: string; agentPubkey: PublicKey }> {
320
+ if (/[A-Z]/.test(agentId)) {
321
+ throw new Error(`Agent ID must not contain uppercase letters: "${agentId}"`);
322
+ }
312
323
  const program = createProgram(connection, wallet, options?.programId);
313
324
  const configPda = getConfigPda(program.programId);
314
325
  const config = await program.account.programConfig.fetch(configPda);
@@ -355,14 +366,12 @@ export async function deleteAgent(
355
366
  options?: AgentRegistryOptions
356
367
  ): Promise<string> {
357
368
  const program = createProgram(connection, wallet, options?.programId);
358
- const agentPda = getAgentPda(program.programId, agentId);
359
- const raw = await program.account.agentRecord.fetch(agentPda);
360
- const memoryPubkey = raw.memory as PublicKey;
369
+ const record = await getAgentRecord(connection, agentId, options);
361
370
 
362
371
  // When no memory exists, pass authority pubkey as placeholder
363
- const memoryAccount = memoryPubkey.equals(PublicKey.default)
372
+ const memoryAccount = record.memory.equals(PublicKey.default)
364
373
  ? wallet.publicKey
365
- : memoryPubkey;
374
+ : record.memory;
366
375
 
367
376
  return program.methods
368
377
  .deleteAgent(agentId)
@@ -438,9 +447,8 @@ export async function uploadMemory(
438
447
  const program = createProgram(connection, wallet, options?.programId);
439
448
  const chunkSize = options?.chunkSize ?? DEFAULT_CHUNK_SIZE;
440
449
  const totalLen = data.length;
441
- const agentPda = getAgentPda(program.programId, agentId);
442
- const raw = await program.account.agentRecord.fetch(agentPda);
443
- const existingMemory = raw.memory as PublicKey;
450
+ const record = await getAgentRecord(connection, agentId, options);
451
+ const existingMemory = record.memory;
444
452
  const hasMemory = !existingMemory.equals(PublicKey.default);
445
453
 
446
454
  // Resolve mode
@@ -561,15 +569,13 @@ export async function closeBuffer(
561
569
  options?: AgentRegistryOptions
562
570
  ): Promise<string> {
563
571
  const program = createProgram(connection, wallet, options?.programId);
564
- const agentPda = getAgentPda(program.programId, agentId);
565
- const raw = await program.account.agentRecord.fetch(agentPda);
566
- const bufferPubkey = raw.pendingBuffer as PublicKey;
567
- if (bufferPubkey.equals(PublicKey.default)) {
572
+ const record = await getAgentRecord(connection, agentId, options);
573
+ if (!record.pendingBuffer) {
568
574
  throw new Error(`Agent "${agentId}" has no pending buffer`);
569
575
  }
570
576
  return program.methods
571
577
  .closeBuffer(agentId)
572
- .accounts({ authority: wallet.publicKey, buffer: bufferPubkey } as any)
578
+ .accounts({ authority: wallet.publicKey, buffer: record.pendingBuffer } as any)
573
579
  .signers([wallet])
574
580
  .rpc();
575
581
  }
@@ -705,3 +711,24 @@ export async function updateRegisterFee(
705
711
  .signers([wallet])
706
712
  .rpc();
707
713
  }
714
+
715
+ /**
716
+ * Update the points configuration (admin-only).
717
+ * Sets how many points are awarded per activity and per referral.
718
+ */
719
+ export async function updatePointsConfig(
720
+ connection: Connection,
721
+ wallet: Keypair,
722
+ pointsSelf: number | anchor.BN,
723
+ pointsReferral: number | anchor.BN,
724
+ options?: AgentRegistryOptions
725
+ ): Promise<string> {
726
+ const program = createProgram(connection, wallet, options?.programId);
727
+ const ps = typeof pointsSelf === "number" ? new anchor.BN(pointsSelf) : pointsSelf;
728
+ const pr = typeof pointsReferral === "number" ? new anchor.BN(pointsReferral) : pointsReferral;
729
+ return program.methods
730
+ .updatePointsConfig(ps, pr)
731
+ .accounts({ admin: wallet.publicKey } as any)
732
+ .signers([wallet])
733
+ .rpc();
734
+ }
@@ -504,6 +504,24 @@
504
504
  ]
505
505
  }
506
506
  },
507
+ {
508
+ "name": "config",
509
+ "pda": {
510
+ "seeds": [
511
+ {
512
+ "kind": "const",
513
+ "value": [
514
+ 99,
515
+ 111,
516
+ 110,
517
+ 102,
518
+ 105,
519
+ 103
520
+ ]
521
+ }
522
+ ]
523
+ }
524
+ },
507
525
  {
508
526
  "name": "referral_agent",
509
527
  "docs": [
@@ -915,6 +933,57 @@
915
933
  }
916
934
  ]
917
935
  },
936
+ {
937
+ "name": "update_points_config",
938
+ "discriminator": [
939
+ 15,
940
+ 89,
941
+ 27,
942
+ 201,
943
+ 127,
944
+ 239,
945
+ 187,
946
+ 80
947
+ ],
948
+ "accounts": [
949
+ {
950
+ "name": "admin",
951
+ "signer": true,
952
+ "relations": [
953
+ "config"
954
+ ]
955
+ },
956
+ {
957
+ "name": "config",
958
+ "writable": true,
959
+ "pda": {
960
+ "seeds": [
961
+ {
962
+ "kind": "const",
963
+ "value": [
964
+ 99,
965
+ 111,
966
+ 110,
967
+ 102,
968
+ 105,
969
+ 103
970
+ ]
971
+ }
972
+ ]
973
+ }
974
+ }
975
+ ],
976
+ "args": [
977
+ {
978
+ "name": "points_self",
979
+ "type": "u64"
980
+ },
981
+ {
982
+ "name": "points_referral",
983
+ "type": "u64"
984
+ }
985
+ ]
986
+ },
918
987
  {
919
988
  "name": "update_register_fee",
920
989
  "discriminator": [
@@ -1168,11 +1237,16 @@
1168
1237
  },
1169
1238
  {
1170
1239
  "code": 6017,
1240
+ "name": "AgentIdNotLowercase",
1241
+ "msg": "Agent ID must be lowercase"
1242
+ },
1243
+ {
1244
+ "code": 6018,
1171
1245
  "name": "QuestIxNotFound",
1172
1246
  "msg": "No valid submit_answer instruction found in transaction"
1173
1247
  },
1174
1248
  {
1175
- "code": 6018,
1249
+ "code": 6019,
1176
1250
  "name": "ReferralNotFound",
1177
1251
  "msg": "Referral agent not found"
1178
1252
  }
@@ -1353,6 +1427,14 @@
1353
1427
  "name": "register_fee",
1354
1428
  "type": "u64"
1355
1429
  },
1430
+ {
1431
+ "name": "points_self",
1432
+ "type": "u64"
1433
+ },
1434
+ {
1435
+ "name": "points_referral",
1436
+ "type": "u64"
1437
+ },
1356
1438
  {
1357
1439
  "name": "_reserved",
1358
1440
  "type": {
@@ -510,6 +510,24 @@ export type NaraAgentRegistry = {
510
510
  ]
511
511
  }
512
512
  },
513
+ {
514
+ "name": "config",
515
+ "pda": {
516
+ "seeds": [
517
+ {
518
+ "kind": "const",
519
+ "value": [
520
+ 99,
521
+ 111,
522
+ 110,
523
+ 102,
524
+ 105,
525
+ 103
526
+ ]
527
+ }
528
+ ]
529
+ }
530
+ },
513
531
  {
514
532
  "name": "referralAgent",
515
533
  "docs": [
@@ -921,6 +939,57 @@ export type NaraAgentRegistry = {
921
939
  }
922
940
  ]
923
941
  },
942
+ {
943
+ "name": "updatePointsConfig",
944
+ "discriminator": [
945
+ 15,
946
+ 89,
947
+ 27,
948
+ 201,
949
+ 127,
950
+ 239,
951
+ 187,
952
+ 80
953
+ ],
954
+ "accounts": [
955
+ {
956
+ "name": "admin",
957
+ "signer": true,
958
+ "relations": [
959
+ "config"
960
+ ]
961
+ },
962
+ {
963
+ "name": "config",
964
+ "writable": true,
965
+ "pda": {
966
+ "seeds": [
967
+ {
968
+ "kind": "const",
969
+ "value": [
970
+ 99,
971
+ 111,
972
+ 110,
973
+ 102,
974
+ 105,
975
+ 103
976
+ ]
977
+ }
978
+ ]
979
+ }
980
+ }
981
+ ],
982
+ "args": [
983
+ {
984
+ "name": "pointsSelf",
985
+ "type": "u64"
986
+ },
987
+ {
988
+ "name": "pointsReferral",
989
+ "type": "u64"
990
+ }
991
+ ]
992
+ },
924
993
  {
925
994
  "name": "updateRegisterFee",
926
995
  "discriminator": [
@@ -1174,11 +1243,16 @@ export type NaraAgentRegistry = {
1174
1243
  },
1175
1244
  {
1176
1245
  "code": 6017,
1246
+ "name": "agentIdNotLowercase",
1247
+ "msg": "Agent ID must be lowercase"
1248
+ },
1249
+ {
1250
+ "code": 6018,
1177
1251
  "name": "questIxNotFound",
1178
1252
  "msg": "No valid submit_answer instruction found in transaction"
1179
1253
  },
1180
1254
  {
1181
- "code": 6018,
1255
+ "code": 6019,
1182
1256
  "name": "referralNotFound",
1183
1257
  "msg": "Referral agent not found"
1184
1258
  }
@@ -1359,6 +1433,14 @@ export type NaraAgentRegistry = {
1359
1433
  "name": "registerFee",
1360
1434
  "type": "u64"
1361
1435
  },
1436
+ {
1437
+ "name": "pointsSelf",
1438
+ "type": "u64"
1439
+ },
1440
+ {
1441
+ "name": "pointsReferral",
1442
+ "type": "u64"
1443
+ },
1362
1444
  {
1363
1445
  "name": "reserved",
1364
1446
  "type": {
package/src/skills.ts CHANGED
@@ -183,6 +183,9 @@ export async function registerSkill(
183
183
  author: string,
184
184
  options?: SkillOptions
185
185
  ): Promise<{ signature: string; skillPubkey: PublicKey }> {
186
+ if (/[A-Z]/.test(name)) {
187
+ throw new Error(`Skill name must not contain uppercase letters: "${name}"`);
188
+ }
186
189
  const program = createProgram(connection, wallet, options?.programId);
187
190
  const configPda = getConfigPda(program.programId);
188
191
  const config = await program.account.programConfig.fetch(configPda);
package/src/zkid.ts CHANGED
@@ -621,6 +621,30 @@ export async function transferZkIdByCommitment(
621
621
  * Initialize the program configuration (one-time setup).
622
622
  * The caller becomes the admin.
623
623
  */
624
+ /**
625
+ * Query the ZK ID program config (admin, fee recipient, fee amount).
626
+ */
627
+ export async function getConfig(
628
+ connection: Connection,
629
+ options?: ZkIdOptions
630
+ ): Promise<{ admin: PublicKey; feeRecipient: PublicKey; feeAmount: number }> {
631
+ const programId = new PublicKey(options?.programId ?? DEFAULT_ZKID_PROGRAM_ID);
632
+ const [configPda] = PublicKey.findProgramAddressSync(
633
+ [Buffer.from("config")],
634
+ programId
635
+ );
636
+ const accountInfo = await connection.getAccountInfo(configPda);
637
+ if (!accountInfo) {
638
+ throw new Error("ZK ID config account not found");
639
+ }
640
+ const buf = Buffer.from(accountInfo.data);
641
+ let offset = 8; // skip discriminator
642
+ const admin = new PublicKey(buf.subarray(offset, offset + 32)); offset += 32;
643
+ const feeRecipient = new PublicKey(buf.subarray(offset, offset + 32)); offset += 32;
644
+ const feeAmount = Number(buf.readBigUInt64LE(offset));
645
+ return { admin, feeRecipient, feeAmount };
646
+ }
647
+
624
648
  export async function initializeConfig(
625
649
  connection: Connection,
626
650
  wallet: Keypair,