nara-sdk 1.0.40 → 1.0.41

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 +1 -0
  2. package/package.json +1 -1
  3. package/src/zkid.ts +93 -1
package/index.ts CHANGED
@@ -66,6 +66,7 @@ export {
66
66
  withdraw,
67
67
  transferZkId,
68
68
  transferZkIdByCommitment,
69
+ makeWithdrawIx,
69
70
  deriveIdSecret,
70
71
  computeIdCommitment,
71
72
  isValidRecipient,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nara-sdk",
3
- "version": "1.0.40",
3
+ "version": "1.0.41",
4
4
  "description": "SDK for the Nara chain (Solana-compatible)",
5
5
  "module": "index.ts",
6
6
  "main": "index.ts",
package/src/zkid.ts CHANGED
@@ -7,7 +7,7 @@
7
7
  * - Ownership can be transferred via ZK proof without revealing the owner's wallet
8
8
  */
9
9
 
10
- import { Connection, Keypair, PublicKey } from "@solana/web3.js";
10
+ import { Connection, Keypair, PublicKey, TransactionInstruction } from "@solana/web3.js";
11
11
  import * as anchor from "@coral-xyz/anchor";
12
12
  import { Program, AnchorProvider, Wallet } from "@coral-xyz/anchor";
13
13
  import { buildPoseidon as _buildPoseidon } from "circomlibjs";
@@ -542,6 +542,98 @@ export async function withdraw(
542
542
  .rpc();
543
543
  }
544
544
 
545
+ /**
546
+ * Build a withdraw instruction without executing it.
547
+ * Useful for composing into an existing transaction.
548
+ *
549
+ * Same ZK proof generation as withdraw(), but returns a TransactionInstruction
550
+ * instead of sending the transaction.
551
+ *
552
+ * @param authority - The payer/signer public key (does not need Keypair since we don't sign)
553
+ * @param depositInfo - From scanClaimableDeposits()
554
+ * @param recipient - Destination address. Must satisfy isValidRecipient().
555
+ */
556
+ export async function makeWithdrawIx(
557
+ connection: Connection,
558
+ authority: PublicKey,
559
+ name: string,
560
+ idSecret: bigint,
561
+ depositInfo: ClaimableDeposit,
562
+ recipient: PublicKey,
563
+ options?: ZkIdOptions
564
+ ): Promise<TransactionInstruction> {
565
+ if (!isValidRecipient(recipient)) {
566
+ throw new Error(
567
+ "Recipient pubkey is >= BN254 field prime. Use generateValidRecipient() to get a compatible address."
568
+ );
569
+ }
570
+
571
+ const program = createReadProgram(connection, options?.programId);
572
+ const programId = new PublicKey(options?.programId ?? DEFAULT_ZKID_PROGRAM_ID);
573
+ const denominationBN = new BN(depositInfo.denomination.toString());
574
+
575
+ // Fetch Merkle tree state
576
+ const [treePda] = PublicKey.findProgramAddressSync(
577
+ [new TextEncoder().encode("tree"), denomBuf(denominationBN)],
578
+ programId
579
+ );
580
+ const treeData = await program.account.merkleTreeAccount.fetch(treePda);
581
+
582
+ const rootIdx: number = treeData.currentRootIndex;
583
+ const root = new Uint8Array((treeData.roots as number[][])[rootIdx]!);
584
+ const filledSubtrees = (treeData.filledSubtrees as number[][]).map(s =>
585
+ new Uint8Array(s)
586
+ );
587
+ const zeros = (treeData.zeros as number[][]).map(z =>
588
+ bytes32ToBigInt(new Uint8Array(z))
589
+ );
590
+
591
+ const { pathElements, pathIndices } = await buildMerklePath(
592
+ depositInfo.leafIndex,
593
+ filledSubtrees,
594
+ zeros
595
+ );
596
+
597
+ const nullifier = await poseidonHash([idSecret, BigInt(depositInfo.depositIndex)]);
598
+ const nullifierHashBuf = bigIntToBytes32BE(nullifier);
599
+ const recipientField = bytes32ToBigInt(recipient.toBytes());
600
+
601
+ const input = {
602
+ idSecret: idSecret.toString(),
603
+ depositIndex: depositInfo.depositIndex.toString(),
604
+ pathElements: pathElements.map(e => e.toString()),
605
+ pathIndices: pathIndices.map(i => i.toString()),
606
+ root: bytes32ToBigInt(root).toString(),
607
+ nullifierHash: nullifier.toString(),
608
+ recipient: recipientField.toString(),
609
+ };
610
+
611
+ let wasmSource = options?.withdrawWasm;
612
+ let zkeySource = options?.withdrawZkey;
613
+ if (!wasmSource || !zkeySource) {
614
+ const defaults = await resolveDefaultZkPaths();
615
+ wasmSource ??= defaults.withdrawWasm;
616
+ zkeySource ??= defaults.withdrawZkey;
617
+ }
618
+
619
+ const { proof } = await silentProve(input, wasmSource, zkeySource);
620
+ const packedProof = packProof(proof);
621
+
622
+ return program.methods
623
+ .withdraw(
624
+ Buffer.from(packedProof) as any,
625
+ toBytes32(root),
626
+ toBytes32(nullifierHashBuf),
627
+ recipient,
628
+ denominationBN
629
+ )
630
+ .accounts({
631
+ payer: authority,
632
+ recipient,
633
+ } as any)
634
+ .instruction();
635
+ }
636
+
545
637
  /**
546
638
  * Transfer ZK ID ownership to a new identity.
547
639
  *