@frontiercompute/zcash-ika 0.5.0 → 0.6.0

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/dist/index.d.ts CHANGED
@@ -216,6 +216,64 @@ export declare function setPolicy(config: ZcashIkaConfig, walletId: string, poli
216
216
  export declare function checkPolicy(config: ZcashIkaConfig, policyId: string, amount?: number, recipient?: string): Promise<PolicyState & {
217
217
  allowed: boolean;
218
218
  }>;
219
+ export interface VaultResult {
220
+ /** CustodyVault shared object ID on Sui */
221
+ vaultId: string;
222
+ /** AdminCap object ID (vault creator holds this) */
223
+ adminCapId: string;
224
+ /** Sui transaction digest */
225
+ txDigest: string;
226
+ }
227
+ export interface AgentResult {
228
+ /** AgentCap object ID (issued to the registered agent) */
229
+ agentCapId: string;
230
+ /** Sui transaction digest */
231
+ txDigest: string;
232
+ }
233
+ export interface VaultState {
234
+ vaultId: string;
235
+ dwalletId: string;
236
+ maxPerTx: number;
237
+ maxDaily: number;
238
+ dailySpent: number;
239
+ totalSpent: number;
240
+ totalTxCount: number;
241
+ agentCount: number;
242
+ frozen: boolean;
243
+ }
244
+ /**
245
+ * Create a CustodyVault for a dWallet.
246
+ * The vault enforces spend policy on-chain and tracks agent access.
247
+ * Returns the vault ID and AdminCap (transferred to caller).
248
+ */
249
+ export declare function createVault(config: ZcashIkaConfig, dwalletId: string, maxPerTx: number, maxDaily: number): Promise<VaultResult>;
250
+ /**
251
+ * Register an agent on a CustodyVault.
252
+ * Only the admin (holder of AdminCap) can do this.
253
+ * Returns the AgentCap which should be transferred to the agent.
254
+ */
255
+ export declare function registerAgent(config: ZcashIkaConfig, vaultId: string, adminCapId: string, agentAddress: string, agentName: string): Promise<AgentResult>;
256
+ /**
257
+ * Request a spend through the CustodyVault.
258
+ * The Move contract checks all policy constraints on-chain.
259
+ * Aborts if the spend violates any limit.
260
+ */
261
+ export declare function requestSpend(config: ZcashIkaConfig, vaultId: string, agentCapId: string, amount: number, recipient: string, chain: string): Promise<{
262
+ approved: boolean;
263
+ txDigest: string;
264
+ }>;
265
+ /**
266
+ * Read the on-chain state of a CustodyVault.
267
+ */
268
+ export declare function getVaultState(config: ZcashIkaConfig, vaultId: string): Promise<VaultState>;
269
+ /**
270
+ * Freeze a CustodyVault. All spend requests will be rejected until unfrozen.
271
+ */
272
+ export declare function freezeVault(config: ZcashIkaConfig, vaultId: string, adminCapId: string): Promise<string>;
273
+ /**
274
+ * Unfreeze a CustodyVault. Resumes normal spend processing.
275
+ */
276
+ export declare function unfreezeVault(config: ZcashIkaConfig, vaultId: string, adminCapId: string): Promise<string>;
219
277
  /**
220
278
  * Spend from a Zcash transparent wallet.
221
279
  *
package/dist/index.js CHANGED
@@ -517,9 +517,9 @@ export async function sign(config, request) {
517
517
  signTxDigest: signResult.digest,
518
518
  };
519
519
  }
520
- // Published package ID - set after sui client publish
521
- // Override via POLICY_PACKAGE_ID env var or pass directly
522
- const DEFAULT_POLICY_PACKAGE_ID = "0x0";
520
+ // Sui testnet deployment (zap1_policy package)
521
+ // Override via POLICY_PACKAGE_ID env var for mainnet or redeployments
522
+ const DEFAULT_POLICY_PACKAGE_ID = "0xb0468033d854e95ad89de4b6fec8f6d8e8187778c9d8337a6aa30a5c24775a77";
523
523
  function getPolicyPackageId() {
524
524
  return process.env.POLICY_PACKAGE_ID || DEFAULT_POLICY_PACKAGE_ID;
525
525
  }
@@ -530,10 +530,6 @@ function getPolicyPackageId() {
530
530
  */
531
531
  export async function setPolicy(config, walletId, policy) {
532
532
  const packageId = getPolicyPackageId();
533
- if (packageId === "0x0") {
534
- throw new Error("Policy Move module not deployed. Set POLICY_PACKAGE_ID env var " +
535
- "after running: sui client publish --path move/");
536
- }
537
533
  const { suiClient, keypair } = await initClients(config);
538
534
  const tx = new Transaction();
539
535
  // 0x6 is the shared Clock object on Sui
@@ -655,6 +651,211 @@ export async function checkPolicy(config, policyId, amount, recipient) {
655
651
  }
656
652
  return { ...state, allowed };
657
653
  }
654
+ /**
655
+ * Create a CustodyVault for a dWallet.
656
+ * The vault enforces spend policy on-chain and tracks agent access.
657
+ * Returns the vault ID and AdminCap (transferred to caller).
658
+ */
659
+ export async function createVault(config, dwalletId, maxPerTx, maxDaily) {
660
+ const packageId = getPolicyPackageId();
661
+ const { suiClient, keypair } = await initClients(config);
662
+ const sender = keypair.getPublicKey().toSuiAddress();
663
+ const tx = new Transaction();
664
+ const adminCap = tx.moveCall({
665
+ target: `${packageId}::custody::create_vault`,
666
+ arguments: [
667
+ tx.pure.address(dwalletId),
668
+ tx.pure.u64(maxPerTx),
669
+ tx.pure.u64(maxDaily),
670
+ tx.object("0x6"), // Clock
671
+ ],
672
+ });
673
+ tx.transferObjects([adminCap], sender);
674
+ const result = await suiClient.signAndExecuteTransaction({
675
+ transaction: tx,
676
+ signer: keypair,
677
+ options: { showEffects: true, showObjectChanges: true },
678
+ });
679
+ if (result.effects?.status?.status !== "success") {
680
+ throw new Error(`createVault TX failed: ${result.effects?.status?.error}`);
681
+ }
682
+ let vaultId = "";
683
+ let adminCapId = "";
684
+ const changes = result.objectChanges || [];
685
+ for (const change of changes) {
686
+ if (change.type !== "created")
687
+ continue;
688
+ const objType = change.objectType || "";
689
+ if (objType.includes("::custody::CustodyVault")) {
690
+ vaultId = change.objectId;
691
+ }
692
+ else if (objType.includes("::custody::AdminCap")) {
693
+ adminCapId = change.objectId;
694
+ }
695
+ }
696
+ if (!vaultId || !adminCapId) {
697
+ const created = result.effects?.created || [];
698
+ for (const obj of created) {
699
+ const id = obj.reference?.objectId || obj.objectId;
700
+ if (id && !vaultId)
701
+ vaultId = id;
702
+ else if (id && !adminCapId)
703
+ adminCapId = id;
704
+ }
705
+ }
706
+ return { vaultId, adminCapId, txDigest: result.digest };
707
+ }
708
+ /**
709
+ * Register an agent on a CustodyVault.
710
+ * Only the admin (holder of AdminCap) can do this.
711
+ * Returns the AgentCap which should be transferred to the agent.
712
+ */
713
+ export async function registerAgent(config, vaultId, adminCapId, agentAddress, agentName) {
714
+ const packageId = getPolicyPackageId();
715
+ const { suiClient, keypair } = await initClients(config);
716
+ const sender = keypair.getPublicKey().toSuiAddress();
717
+ const tx = new Transaction();
718
+ const nameBytes = Array.from(new TextEncoder().encode(agentName));
719
+ const agentCap = tx.moveCall({
720
+ target: `${packageId}::custody::register_agent`,
721
+ arguments: [
722
+ tx.object(vaultId),
723
+ tx.object(adminCapId),
724
+ tx.pure.address(agentAddress),
725
+ tx.pure.vector("u8", nameBytes),
726
+ tx.object("0x6"), // Clock
727
+ ],
728
+ });
729
+ // Transfer AgentCap to the agent address
730
+ tx.transferObjects([agentCap], agentAddress);
731
+ const result = await suiClient.signAndExecuteTransaction({
732
+ transaction: tx,
733
+ signer: keypair,
734
+ options: { showEffects: true, showObjectChanges: true },
735
+ });
736
+ if (result.effects?.status?.status !== "success") {
737
+ throw new Error(`registerAgent TX failed: ${result.effects?.status?.error}`);
738
+ }
739
+ let agentCapId = "";
740
+ const changes = result.objectChanges || [];
741
+ for (const change of changes) {
742
+ if (change.type !== "created")
743
+ continue;
744
+ const objType = change.objectType || "";
745
+ if (objType.includes("::custody::AgentCap")) {
746
+ agentCapId = change.objectId;
747
+ }
748
+ }
749
+ return { agentCapId, txDigest: result.digest };
750
+ }
751
+ /**
752
+ * Request a spend through the CustodyVault.
753
+ * The Move contract checks all policy constraints on-chain.
754
+ * Aborts if the spend violates any limit.
755
+ */
756
+ export async function requestSpend(config, vaultId, agentCapId, amount, recipient, chain) {
757
+ const packageId = getPolicyPackageId();
758
+ const { suiClient, keypair } = await initClients(config);
759
+ const tx = new Transaction();
760
+ const recipientBytes = Array.from(new TextEncoder().encode(recipient));
761
+ const chainBytes = Array.from(new TextEncoder().encode(chain));
762
+ tx.moveCall({
763
+ target: `${packageId}::custody::request_spend_entry`,
764
+ arguments: [
765
+ tx.object(vaultId),
766
+ tx.object(agentCapId),
767
+ tx.pure.u64(amount),
768
+ tx.pure.vector("u8", recipientBytes),
769
+ tx.pure.vector("u8", chainBytes),
770
+ tx.object("0x6"), // Clock
771
+ ],
772
+ });
773
+ const result = await suiClient.signAndExecuteTransaction({
774
+ transaction: tx,
775
+ signer: keypair,
776
+ options: { showEffects: true },
777
+ });
778
+ if (result.effects?.status?.status !== "success") {
779
+ return { approved: false, txDigest: result.digest };
780
+ }
781
+ return { approved: true, txDigest: result.digest };
782
+ }
783
+ /**
784
+ * Read the on-chain state of a CustodyVault.
785
+ */
786
+ export async function getVaultState(config, vaultId) {
787
+ const { suiClient } = await initClients(config);
788
+ const obj = await suiClient.getObject({
789
+ id: vaultId,
790
+ options: { showContent: true },
791
+ });
792
+ const content = obj.data?.content;
793
+ if (!content || content.dataType !== "moveObject") {
794
+ throw new Error(`Vault object ${vaultId} not found or not a Move object`);
795
+ }
796
+ const fields = content.fields;
797
+ return {
798
+ vaultId,
799
+ dwalletId: fields.dwallet_id,
800
+ maxPerTx: Number(fields.max_per_tx),
801
+ maxDaily: Number(fields.max_daily),
802
+ dailySpent: Number(fields.daily_spent),
803
+ totalSpent: Number(fields.total_spent),
804
+ totalTxCount: Number(fields.total_tx_count),
805
+ agentCount: Number(fields.agent_count),
806
+ frozen: fields.frozen,
807
+ };
808
+ }
809
+ /**
810
+ * Freeze a CustodyVault. All spend requests will be rejected until unfrozen.
811
+ */
812
+ export async function freezeVault(config, vaultId, adminCapId) {
813
+ const packageId = getPolicyPackageId();
814
+ const { suiClient, keypair } = await initClients(config);
815
+ const tx = new Transaction();
816
+ tx.moveCall({
817
+ target: `${packageId}::custody::freeze_vault`,
818
+ arguments: [
819
+ tx.object(vaultId),
820
+ tx.object(adminCapId),
821
+ tx.object("0x6"), // Clock
822
+ ],
823
+ });
824
+ const result = await suiClient.signAndExecuteTransaction({
825
+ transaction: tx,
826
+ signer: keypair,
827
+ options: { showEffects: true },
828
+ });
829
+ if (result.effects?.status?.status !== "success") {
830
+ throw new Error(`freezeVault TX failed: ${result.effects?.status?.error}`);
831
+ }
832
+ return result.digest;
833
+ }
834
+ /**
835
+ * Unfreeze a CustodyVault. Resumes normal spend processing.
836
+ */
837
+ export async function unfreezeVault(config, vaultId, adminCapId) {
838
+ const packageId = getPolicyPackageId();
839
+ const { suiClient, keypair } = await initClients(config);
840
+ const tx = new Transaction();
841
+ tx.moveCall({
842
+ target: `${packageId}::custody::unfreeze_vault`,
843
+ arguments: [
844
+ tx.object(vaultId),
845
+ tx.object(adminCapId),
846
+ tx.object("0x6"), // Clock
847
+ ],
848
+ });
849
+ const result = await suiClient.signAndExecuteTransaction({
850
+ transaction: tx,
851
+ signer: keypair,
852
+ options: { showEffects: true },
853
+ });
854
+ if (result.effects?.status?.status !== "success") {
855
+ throw new Error(`unfreezeVault TX failed: ${result.effects?.status?.error}`);
856
+ }
857
+ return result.digest;
858
+ }
658
859
  /**
659
860
  * Spend from a Zcash transparent wallet.
660
861
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@frontiercompute/zcash-ika",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "description": "Split-key custody for Zcash, Bitcoin, and EVM. 2PC-MPC signing, on-chain spend policy, transparent TX builder, ZAP1 attestation.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",