nara-sdk 1.0.34 → 1.0.36

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.
Files changed (3) hide show
  1. package/index.ts +2 -0
  2. package/package.json +1 -1
  3. package/src/quest.ts +57 -0
package/index.ts CHANGED
@@ -24,6 +24,8 @@ export {
24
24
  submitAnswer,
25
25
  submitAnswerViaRelay,
26
26
  parseQuestReward,
27
+ computeAnswerHash,
28
+ createQuestion,
27
29
  type QuestInfo,
28
30
  type ZkProof,
29
31
  type ZkProofHex,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nara-sdk",
3
- "version": "1.0.34",
3
+ "version": "1.0.36",
4
4
  "description": "SDK for the Nara chain (Solana-compatible)",
5
5
  "module": "index.ts",
6
6
  "main": "index.ts",
package/src/quest.ts CHANGED
@@ -264,11 +264,18 @@ export async function hasAnswered(
264
264
  /**
265
265
  * Generate a ZK proof for a quest answer.
266
266
  * Throws if the answer is wrong (circuit assertion fails).
267
+ *
268
+ * @param answer - The answer string (passed directly as circuit input)
269
+ * @param answerHash - The on-chain answer hash bytes
270
+ * @param userPubkey - The user's public key (binds proof to user)
271
+ * @param round - The quest round number (binds proof to round, prevents replay)
272
+ * @param options - Optional circuit paths
267
273
  */
268
274
  export async function generateProof(
269
275
  answer: string,
270
276
  answerHash: number[],
271
277
  userPubkey: PublicKey,
278
+ round: string,
272
279
  options?: QuestOptions
273
280
  ): Promise<{ solana: ZkProof; hex: ZkProofHex }> {
274
281
  const wasmPath = options?.circuitWasmPath ?? process.env.QUEST_CIRCUIT_WASM ?? DEFAULT_CIRCUIT_WASM;
@@ -285,6 +292,7 @@ export async function generateProof(
285
292
  answer_hash: answerHashFieldStr,
286
293
  pubkey_lo: lo,
287
294
  pubkey_hi: hi,
295
+ round: round,
288
296
  },
289
297
  wasmPath,
290
298
  zkeyPath
@@ -427,3 +435,52 @@ export async function parseQuestReward(
427
435
  winner,
428
436
  };
429
437
  }
438
+
439
+ /**
440
+ * Compute the Poseidon answer hash for a given answer string.
441
+ * Uses answerToField (UTF-8 encoding) consistent with on-chain question creation.
442
+ */
443
+ export async function computeAnswerHash(answer: string): Promise<number[]> {
444
+ const circomlibjs = await import("circomlibjs");
445
+ const poseidon = await circomlibjs.buildPoseidon();
446
+ const fieldVal = answerToField(answer);
447
+ const hashRaw = poseidon([fieldVal]);
448
+ const hashStr: string = poseidon.F.toString(hashRaw);
449
+ return Array.from(toBigEndian32(BigInt(hashStr)));
450
+ }
451
+
452
+ /**
453
+ * Create a new quest question on-chain (authority only).
454
+ *
455
+ * @param connection - Solana connection
456
+ * @param wallet - Authority keypair (must be the program authority)
457
+ * @param question - The question text
458
+ * @param answer - The answer string (will be hashed with Poseidon + answerToField)
459
+ * @param deadlineSeconds - Duration in seconds from now until the deadline
460
+ * @param rewardSol - Total reward amount in SOL/NARA
461
+ * @param difficulty - Difficulty level (default: 1)
462
+ * @param options - Optional program ID override
463
+ */
464
+ export async function createQuestion(
465
+ connection: Connection,
466
+ wallet: Keypair,
467
+ question: string,
468
+ answer: string,
469
+ deadlineSeconds: number,
470
+ rewardSol: number,
471
+ difficulty: number = 1,
472
+ options?: QuestOptions
473
+ ): Promise<string> {
474
+ const program = createProgram(connection, wallet, options?.programId);
475
+ const answerHash = await computeAnswerHash(answer);
476
+ const deadline = new anchor.BN(Math.floor(Date.now() / 1000) + deadlineSeconds);
477
+ const rewardAmount = new anchor.BN(Math.round(rewardSol * LAMPORTS_PER_SOL));
478
+
479
+ const signature = await program.methods
480
+ .createQuestion(question, answerHash as any, deadline, rewardAmount, difficulty)
481
+ .accounts({ authority: wallet.publicKey } as any)
482
+ .signers([wallet])
483
+ .rpc();
484
+
485
+ return signature;
486
+ }