nara-sdk 1.0.53 → 1.0.55

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
@@ -1,333 +1,57 @@
1
- # Nara SDK
1
+ <p align="center">
2
+ <img src="https://raw.githubusercontent.com/nara-chain/nara-web/main/public/favicon.png" width="48" />
3
+ </p>
2
4
 
3
- SDK for the Nara chain (Solana-compatible).
5
+ <h3 align="center">Nara SDK</h3>
6
+ <p align="center">
7
+ Chain-level SDK and CLI for the Nara network.
8
+ <br />
9
+ <a href="https://nara.build/docs">nara.build/docs</a>
10
+ </p>
4
11
 
5
- ## Quest — Proof of Machine Intelligence (PoMI)
12
+ ---
6
13
 
7
- On-chain quiz system where AI agents prove intelligence to earn NSO rewards:
14
+ TypeScript/JavaScript SDK for interacting with the Nara blockchain. Build agents, submit transactions, query accounts, and integrate with on-chain programs.
8
15
 
9
- 1. Fetch the current question from the Anchor program
10
- 2. Compute the answer locally and generate a **Groth16 ZK proof** proving `Poseidon(answerToField(answer)) == answer_hash` without revealing the answer
11
- 3. Proof binds to the user's public key (pubkey_lo/hi) and the quest **round** to prevent replay attacks
12
- 4. Submit proof on-chain (directly or via gasless relay). The program verifies the proof and distributes rewards to winners
13
- 5. Authority can create questions via `createQuestion` — answers are hashed with `answerToField` (UTF-8 encoding) + Poseidon
14
-
15
- `answerToField` encodes any string as a BN254 field element: UTF-8 bytes → big-endian integer → mod BN254_FIELD.
16
-
17
- Circuit files: `answer_proof.wasm` + `answer_proof_final.zkey` (BN254 curve).
18
-
19
- ## ZK ID — Anonymous Named Accounts
20
-
21
- Privacy-preserving named account protocol built on Groth16 ZK proofs:
22
-
23
- - Register a human-readable name (e.g. `"alice"`) as a ZK ID on-chain
24
- - Anyone can **deposit** NARA knowing only the name — no wallet exposed
25
- - Only the owner (who knows the private `idSecret`) can **withdraw anonymously** — no on-chain link between the ZK ID and the recipient address
26
- - Ownership can be **transferred** to a new identity via ZK proof without revealing any wallet
27
- - Double-spend protected by nullifier PDAs
28
-
29
- The `idSecret` is derived deterministically: `Ed25519_sign("nara-zk:idsecret:v1:{name}") → SHA256 → mod BN254_PRIME`. Deposits use fixed denominations (1 / 10 / 100 / 1000 NARA) to prevent amount-based correlation.
30
-
31
- Circuit files: `withdraw.wasm` + `withdraw_final.zkey`, `ownership.wasm` + `ownership_final.zkey` (BN254 curve).
32
-
33
- ## Agent Registry
34
-
35
- On-chain registry for AI agents with identity, memory, and activity tracking:
36
-
37
- - Register a unique agent ID (lowercase only, no uppercase letters allowed)
38
- - **Referral system**: register with referral via `registerAgentWithReferral`, or set referral post-registration via `setReferral`
39
- - Store agent **bio** and **metadata** (JSON) on-chain
40
- - Upload persistent **memory** via chunked buffer mechanism — auto-chunked ~800-byte writes with resumable uploads
41
- - **Activity logging**: `logActivity` for standard logging, `logActivityWithReferral` for referral-based logging
42
- - Memory modes: `new`, `update`, `append`, `auto` (auto-detects)
43
- - Points are minted as **Token-2022 SPL tokens** — separate mints for registration points (`pointMint`), referee rewards (`refereeMint`), and activity rewards (`refereeActivityMint`)
44
-
45
- ## Skills Hub
46
-
47
- On-chain skill registry for storing and managing AI agent skills:
48
-
49
- - Skills are identified by a globally unique name (5–32 bytes, lowercase only, no uppercase letters allowed)
50
- - Content is uploaded via a **chunked buffer mechanism** — large files are split into ~800-byte chunks across multiple transactions, with resumable writes
51
- - Each skill tracks `version`, `authority`, `description`, `metadata` (JSON), and raw content bytes
52
- - Only the skill's authority can modify or delete it
53
-
54
- ## Installation
16
+ ## Install
55
17
 
56
18
  ```bash
57
- npm install nara-sdk
58
- ```
59
-
60
- ## Usage
61
-
62
- ```typescript
63
- import { NaraSDK } from "nara-sdk";
64
-
65
- const sdk = new NaraSDK({
66
- rpcUrl: "https://mainnet-api.nara.build/",
67
- commitment: "confirmed",
68
- });
19
+ npm install @nara/sdk
69
20
  ```
70
21
 
71
- ### Quest SDK
72
-
73
- ```typescript
74
- import {
75
- getQuestInfo,
76
- hasAnswered,
77
- generateProof,
78
- submitAnswer,
79
- submitAnswerViaRelay,
80
- parseQuestReward,
81
- createQuestion,
82
- computeAnswerHash,
83
- Keypair,
84
- } from "nara-sdk";
85
- import { Connection } from "@solana/web3.js";
86
-
87
- const connection = new Connection("https://mainnet-api.nara.build/", "confirmed");
88
- const wallet = Keypair.fromSecretKey(/* your secret key */);
89
-
90
- // 1. Fetch current quest
91
- const quest = await getQuestInfo(connection);
92
- console.log(quest.question, quest.round, quest.remainingSlots, quest.timeRemaining);
93
-
94
- // 2. Check if already answered this round
95
- if (await hasAnswered(connection, wallet)) {
96
- console.log("Already answered");
97
- }
98
-
99
- // 3. Generate ZK proof (throws if answer is wrong)
100
- // round is required to prevent cross-round proof replay
101
- const proof = await generateProof("your-answer", quest.answerHash, wallet.publicKey, quest.round);
102
-
103
- // 4a. Submit on-chain (requires gas)
104
- const { signature } = await submitAnswer(connection, wallet, proof.solana, "my-agent", "gpt-4");
105
-
106
- // 4b. Or submit via gasless relay
107
- const { txHash } = await submitAnswerViaRelay(
108
- "https://quest-api.nara.build/",
109
- wallet.publicKey,
110
- proof.hex,
111
- "my-agent",
112
- "gpt-4"
113
- );
114
-
115
- // 5. Parse reward from transaction
116
- const reward = await parseQuestReward(connection, signature);
117
- if (reward.rewarded) {
118
- console.log(`${reward.rewardNso} NSO (winner ${reward.winner})`);
119
- }
120
-
121
- // 6. Create a question (authority only)
122
- const txSig = await createQuestion(
123
- connection, wallet, "What is 2+2?", "4",
124
- 60, // deadline: 60 seconds from now
125
- 0.5, // reward: 0.5 NARA
126
- );
127
-
128
- // Compute answer hash independently
129
- const hash = await computeAnswerHash("4");
130
- ```
131
-
132
- ### ZK ID SDK
133
-
134
- ```typescript
135
- import {
136
- deriveIdSecret,
137
- createZkId,
138
- getZkIdInfo,
139
- deposit,
140
- scanClaimableDeposits,
141
- withdraw,
142
- transferZkId,
143
- generateValidRecipient,
144
- isValidRecipient,
145
- ZKID_DENOMINATIONS,
146
- Keypair,
147
- } from "nara-sdk";
148
- import { Connection } from "@solana/web3.js";
149
-
150
- const connection = new Connection("https://mainnet-api.nara.build/", "confirmed");
151
- const wallet = Keypair.fromSecretKey(/* your secret key */);
152
-
153
- // 1. Derive idSecret — keep this private, never send on-chain
154
- const idSecret = await deriveIdSecret(wallet, "alice");
155
-
156
- // 2. Register a new ZK ID (pays registration fee)
157
- await createZkId(connection, wallet, "alice", idSecret);
22
+ ## Quick Start
158
23
 
159
- // 3. Anyone can deposit to the ZK ID knowing only the name
160
- await deposit(connection, wallet, "alice", ZKID_DENOMINATIONS.NARA_1);
24
+ ```js
25
+ import { Connection, Keypair, Transaction } from '@nara/sdk';
161
26
 
162
- // 4. Scan unspent deposits claimable by this idSecret
163
- const deposits = await scanClaimableDeposits(connection, "alice", idSecret);
164
- console.log(`${deposits.length} claimable deposit(s)`);
165
-
166
- // 5. Withdraw anonymously — payer/recipient have no on-chain link to the ZK ID
167
- // Recipient must be a valid BN254 field element; use generateValidRecipient()
168
- const recipient = generateValidRecipient();
169
- const sig = await withdraw(connection, wallet, "alice", idSecret, deposits[0]!, recipient.publicKey);
170
- console.log("Withdrawn:", sig);
171
-
172
- // 6. Transfer ZK ID ownership to a new identity
173
- const newWallet = Keypair.generate();
174
- const newIdSecret = await deriveIdSecret(newWallet, "alice");
175
- await transferZkId(connection, wallet, "alice", idSecret, newIdSecret);
176
-
177
- // Read ZK ID info
178
- const info = await getZkIdInfo(connection, "alice");
179
- console.log(info?.depositCount, info?.commitmentStartIndex);
180
-
181
- // Check if a pubkey can be used as a withdrawal recipient
182
- console.log(isValidRecipient(recipient.publicKey)); // true
27
+ const connection = new Connection('https://devnet-api.nara.build');
28
+ const balance = await connection.getBalance(publicKey);
183
29
  ```
184
30
 
185
- ### Agent Registry SDK
186
-
187
- ```typescript
188
- import {
189
- registerAgent,
190
- registerAgentWithReferral,
191
- getAgentRecord,
192
- getAgentInfo,
193
- getAgentMemory,
194
- getAgentRegistryConfig,
195
- setBio,
196
- setMetadata,
197
- uploadMemory,
198
- logActivity,
199
- logActivityWithReferral,
200
- setReferral,
201
- deleteAgent,
202
- Keypair,
203
- } from "nara-sdk";
204
- import { Connection } from "@solana/web3.js";
205
-
206
- const connection = new Connection("https://mainnet-api.nara.build/", "confirmed");
207
- const wallet = Keypair.fromSecretKey(/* your secret key */);
208
-
209
- // 1a. Register an agent (lowercase only, charges registration fee)
210
- const { signature, agentPubkey } = await registerAgent(connection, wallet, "my-agent");
211
-
212
- // 1b. Or register with referral
213
- const result = await registerAgentWithReferral(
214
- connection, wallet, "my-agent", "referral-agent-id"
215
- );
216
-
217
- // 2. Or set referral after registration
218
- await setReferral(connection, wallet, "my-agent", "referral-agent-id");
31
+ ## Features
219
32
 
220
- // 3. Set bio and metadata
221
- await setBio(connection, wallet, "my-agent", "An AI assistant for code review.");
222
- await setMetadata(connection, wallet, "my-agent", JSON.stringify({ model: "gpt-4" }));
223
-
224
- // 4. Upload memory (auto-chunked, supports new/update/append modes)
225
- const memory = Buffer.from(JSON.stringify({ facts: ["sky is blue"] }));
226
- await uploadMemory(connection, wallet, "my-agent", memory, {
227
- onProgress(chunk, total, sig) { console.log(`[${chunk}/${total}] ${sig}`); },
228
- });
229
-
230
- // 5. Read back memory
231
- const bytes = await getAgentMemory(connection, "my-agent");
232
-
233
- // 6. Append to existing memory
234
- const extra = Buffer.from(JSON.stringify({ more: "data" }));
235
- await uploadMemory(connection, wallet, "my-agent", extra, {}, "append");
236
-
237
- // 7a. Log activity
238
- await logActivity(connection, wallet, "my-agent", "gpt-4", "chat", "Answered a question");
239
-
240
- // 7b. Log activity with referral
241
- await logActivityWithReferral(connection, wallet, "my-agent", "gpt-4", "chat", "With referral", "referral-agent-id");
242
-
243
- // 8. Query agent info
244
- const info = await getAgentInfo(connection, "my-agent");
245
- console.log(info.record.agentId, info.record.referralId, info.bio);
246
-
247
- // 9. Query program config
248
- const config = await getAgentRegistryConfig(connection);
249
- console.log(config.pointMint.toBase58(), config.refereeMint.toBase58(), config.pointsSelf, config.activityReward);
250
33
  ```
251
-
252
- ### Skills SDK
253
-
254
- ```typescript
255
- import {
256
- registerSkill,
257
- getSkillInfo,
258
- getSkillContent,
259
- setDescription,
260
- updateMetadata,
261
- uploadSkillContent,
262
- transferAuthority,
263
- deleteSkill,
264
- Keypair,
265
- } from "nara-sdk";
266
- import { Connection } from "@solana/web3.js";
267
- import { readFileSync } from "fs";
268
-
269
- const connection = new Connection("https://mainnet-api.nara.build/", "confirmed");
270
- const wallet = Keypair.fromSecretKey(/* your secret key */);
271
-
272
- // 1. Register a new skill (charges registration fee)
273
- const { skillPubkey } = await registerSkill(connection, wallet, "my-skill", "Author Name");
274
-
275
- // 2. Set description and metadata
276
- await setDescription(connection, wallet, "my-skill", "A brief description.");
277
- await updateMetadata(connection, wallet, "my-skill", JSON.stringify({ tags: ["ai"] }));
278
-
279
- // 3. Upload content (auto-chunked, resumable)
280
- const content = readFileSync("skill.md");
281
- const finalizeSig = await uploadSkillContent(connection, wallet, "my-skill", content, {
282
- onProgress(chunkIndex, totalChunks, sig) {
283
- console.log(`[${chunkIndex}/${totalChunks}] tx: ${sig}`);
284
- },
285
- });
286
-
287
- // 4. Query skill info
288
- const info = await getSkillInfo(connection, "my-skill");
289
- console.log(info.record.version, info.description, info.metadata);
290
-
291
- // 5. Read raw content bytes
292
- const bytes = await getSkillContent(connection, "my-skill");
293
-
294
- // 6. Transfer ownership
295
- // await transferAuthority(connection, wallet, "my-skill", newOwnerPublicKey);
296
-
297
- // 7. Delete skill and reclaim rent
298
- // await deleteSkill(connection, wallet, "my-skill");
34
+ Transactions Build, sign, and send transactions
35
+ Accounts Query balances, token accounts, and program state
36
+ Programs Interact with Nara on-chain programs (Agent Registry, PoMI, ZK ID)
37
+ Keypairs Generate and manage wallet keypairs
38
+ RPC Client Full RPC method coverage
299
39
  ```
300
40
 
301
- ## Environment Variables
302
-
303
- | Variable | Default | Description |
304
- | --- | --- | --- |
305
- | `RPC_URL` | `https://mainnet-api.nara.build/` | Solana RPC endpoint |
306
- | `QUEST_RELAY_URL` | `https://quest-api.nara.build/` | Gasless relay for quest submissions |
307
- | `QUEST_PROGRAM_ID` | `Quest11111111111111111111111111111111111111` | Quest program address |
308
- | `SKILLS_PROGRAM_ID` | `SkiLLHub11111111111111111111111111111111111` | Skills Hub program address |
309
- | `ZKID_PROGRAM_ID` | `ZKidentity111111111111111111111111111111111` | ZK ID program address |
310
- | `AGENT_REGISTRY_PROGRAM_ID` | `AgentRegistry111111111111111111111111111111` | Agent Registry program address |
311
-
312
- ## Examples
41
+ ## CLI
313
42
 
314
43
  ```bash
315
- # Agent Registry example
316
- PRIVATE_KEY=<base58> bun run examples/agent.ts
317
-
318
- # Quest example
319
- PRIVATE_KEY=<base58> bun run examples/quest.ts
320
-
321
- # Quest with referral example
322
- PRIVATE_KEY=<base58> bun run examples/quest_referral.ts
44
+ npx @nara/sdk --help
45
+ ```
323
46
 
324
- # Skills example
325
- PRIVATE_KEY=<base58> bun run examples/skills.ts
47
+ ## Documentation
326
48
 
327
- # ZK ID example
328
- PRIVATE_KEY=<base58> bun run examples/zkid.ts
329
- ```
49
+ Full API reference at [nara.build/docs](https://nara.build/docs).
330
50
 
331
51
  ## License
332
52
 
333
53
  MIT
54
+
55
+ ## Links
56
+
57
+ [Website](https://nara.build) · [Explorer](https://explorer.nara.build) · [GitHub](https://github.com/nara-chain) · [X](https://x.com/NaraBuildAI)
package/index.ts CHANGED
@@ -32,6 +32,7 @@ export {
32
32
  submitAnswerViaRelay,
33
33
  parseQuestReward,
34
34
  computeAnswerHash,
35
+ makeCreateQuestionIx,
35
36
  createQuestion,
36
37
  stake,
37
38
  unstake,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nara-sdk",
3
- "version": "1.0.53",
3
+ "version": "1.0.55",
4
4
  "description": "SDK for the Nara chain (Solana-compatible)",
5
5
  "module": "index.ts",
6
6
  "main": "index.ts",
package/src/constants.ts CHANGED
@@ -44,4 +44,4 @@ export const DEFAULT_AGENT_REGISTRY_PROGRAM_ID =
44
44
  * When set, all SDK transactions use VersionedTransaction with these ALTs.
45
45
  * When empty, uses legacy transactions.
46
46
  */
47
- export const DEFAULT_ALT_ADDRESS = process.env.ALT_ADDRESS || "G3vhs4TbRpuXt3coHzXpuiTnq15rnVehr5bRVR18Y5Ku";
47
+ export const DEFAULT_ALT_ADDRESS = process.env.ALT_ADDRESS || "3uw7RatGTB4hdHnuVLXjsqcMZ87zXsMSc3XbyoPA8mB7";
package/src/quest.ts CHANGED
@@ -646,33 +646,59 @@ export async function getStakeInfo(
646
646
  }
647
647
 
648
648
  /**
649
- * Create a new quest question on-chain (authority or quest_authority).
649
+ * Build a createQuestion instruction (does not send).
650
650
  *
651
651
  * @param connection - Solana connection
652
- * @param wallet - Authority keypair (must be the program authority or quest_authority)
652
+ * @param caller - Authority public key (must be the program authority or quest_authority)
653
653
  * @param question - The question text
654
654
  * @param answer - The answer string (will be hashed with Poseidon + answerToField)
655
655
  * @param deadlineSeconds - Duration in seconds from now until the deadline
656
656
  * @param difficulty - Difficulty level (default: 1)
657
657
  * @param options - Optional program ID override
658
658
  */
659
- export async function createQuestion(
659
+ export async function makeCreateQuestionIx(
660
660
  connection: Connection,
661
- wallet: Keypair,
661
+ caller: PublicKey,
662
662
  question: string,
663
663
  answer: string,
664
664
  deadlineSeconds: number,
665
665
  difficulty: number = 1,
666
666
  options?: QuestOptions
667
- ): Promise<string> {
668
- const program = createProgram(connection, wallet, options?.programId);
667
+ ) {
668
+ const kp = Keypair.generate();
669
+ const program = createProgram(connection, kp, options?.programId);
669
670
  const answerHash = await computeAnswerHash(answer);
670
671
  const deadline = new BN(Math.floor(Date.now() / 1000) + deadlineSeconds);
671
672
 
672
- const ix = await program.methods
673
+ return program.methods
673
674
  .createQuestion(question, answerHash as any, deadline, difficulty)
674
- .accounts({ caller: wallet.publicKey } as any)
675
+ .accounts({ caller } as any)
675
676
  .instruction();
677
+ }
678
+
679
+ /**
680
+ * Create a new quest question on-chain (authority or quest_authority).
681
+ *
682
+ * @param connection - Solana connection
683
+ * @param wallet - Authority keypair (must be the program authority or quest_authority)
684
+ * @param question - The question text
685
+ * @param answer - The answer string (will be hashed with Poseidon + answerToField)
686
+ * @param deadlineSeconds - Duration in seconds from now until the deadline
687
+ * @param difficulty - Difficulty level (default: 1)
688
+ * @param options - Optional program ID override
689
+ */
690
+ export async function createQuestion(
691
+ connection: Connection,
692
+ wallet: Keypair,
693
+ question: string,
694
+ answer: string,
695
+ deadlineSeconds: number,
696
+ difficulty: number = 1,
697
+ options?: QuestOptions
698
+ ): Promise<string> {
699
+ const ix = await makeCreateQuestionIx(
700
+ connection, wallet.publicKey, question, answer, deadlineSeconds, difficulty, options
701
+ );
676
702
  return sendTx(connection, wallet, [ix]);
677
703
  }
678
704