@frontiercompute/zcash-ika 0.1.0 → 0.2.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/README.md CHANGED
@@ -1,46 +1,28 @@
1
1
  # zcash-ika
2
2
 
3
- Split-key custody for Zcash and Bitcoin. The private key never exists whole.
3
+ Split-key custody for Zcash, Bitcoin, and EVM chains. The private key never exists whole.
4
4
 
5
5
  [![npm](https://img.shields.io/npm/v/@frontiercompute/zcash-ika)](https://www.npmjs.com/package/@frontiercompute/zcash-ika)
6
6
 
7
7
  ## What this is
8
8
 
9
- Your agent/DAO/treasury holds ZEC and BTC through [Ika's 2PC-MPC network](https://ika.xyz). One key share on your device, one distributed across Ika's nodes on Sui. Both must cooperate to sign. Spending policy enforced by smart contract. Every action attested on-chain via [ZAP1](https://pay.frontiercompute.io).
9
+ One secp256k1 dWallet on [Ika's 2PC-MPC network](https://ika.xyz) signs for Zcash transparent, Bitcoin, and Ethereum. Your device holds half the key. Ika's nodes hold the other half. Spending policy enforced by Sui Move contract. Every action attested on Zcash via [ZAP1](https://pay.frontiercompute.io).
10
10
 
11
- **Ed25519 dWallet live on Ika testnet.** First Zcash-capable dWallet ever created on the network.
11
+ ## What works today
12
12
 
13
- ## Use cases
14
-
15
- ### AI Agent Custody
16
- Your agent needs to spend money. Give it a split-key wallet instead of full access. The agent requests transactions, but can't override spending limits, daily caps, or approved recipient lists. If the agent gets compromised, the attacker gets half a key. Worthless.
17
-
18
- ```
19
- npx @frontiercompute/zcash-mcp # 17 tools, any MCP client
20
- ```
21
-
22
- ### DAO Treasury
23
- Multi-sig is 2003 technology. Split-key custody means the treasury key literally doesn't exist in one place. Policy lives in a Sui Move contract that no single party controls. Every spend attested to Zcash for the full audit trail, but balances stay shielded.
24
-
25
- ### Privacy Payroll
26
- Pay contributors without publishing amounts on a block explorer. Shielded ZEC from an Orchard address, every payment attested via ZAP1 for compliance. The auditor sees proof of payment. Nobody else sees anything.
27
-
28
- ### Cross-Border Commerce
29
- Hold shielded ZEC + stablecoins (USDC/USDT via secp256k1 on EVM chains) in one wallet. Same operator, same policy. Swap between them via NEAR Intents. Settlement is private. Compliance is provable.
13
+ | Chain | Curve | Algorithm | Hash | Status |
14
+ |-------|-------|-----------|------|--------|
15
+ | Zcash transparent | secp256k1 | ECDSA | DoubleSHA256 | dWallet on testnet, signing wired |
16
+ | Bitcoin | secp256k1 | ECDSA | DoubleSHA256 | Same dWallet, same key |
17
+ | Ethereum/EVM | secp256k1 | ECDSA | KECCAK256 | Same dWallet, different hash |
30
18
 
31
- ### Compliance Without Exposure
32
- ZAP1 attestations prove what happened without revealing what you hold. Bond deposits prove skin in the game. Policy verification proves you followed the rules. All on Zcash mainnet, all verifiable, nothing visible beyond what you choose to share.
19
+ One dWallet. Three chain families. Split custody on all of them.
33
20
 
34
- ## Signing parameters
21
+ ## What does NOT work
35
22
 
36
- | Chain | Curve | Algorithm | Hash | Status |
37
- |-------|-------|-----------|------|--------|
38
- | Zcash Orchard | ED25519 | EdDSA | SHA512 | dWallet live on testnet |
39
- | Bitcoin | SECP256K1 | ECDSASecp256k1 | DoubleSHA256 | SDK ready |
40
- | Zcash transparent | SECP256K1 | ECDSASecp256k1 | DoubleSHA256 | SDK ready |
41
- | Ethereum/Base | SECP256K1 | ECDSASecp256k1 | KECCAK256 | SDK ready |
23
+ **Zcash shielded (Orchard)** requires RedPallas signatures on the Pallas curve. Ika's MPC supports secp256k1 and Ed25519, but not Pallas. There is no path from Ika to Orchard signing today. Same for Sapling (RedJubjub on Jubjub).
42
24
 
43
- One secp256k1 dWallet signs for Bitcoin, Zcash transparent, and every EVM chain.
25
+ For shielded operations, use the [embedded Orchard wallet](https://github.com/Frontier-Compute/zap1) which holds keys directly. The hybrid architecture: MPC custody for transparent + cross-chain, local wallet for shielded.
44
26
 
45
27
  ## Install
46
28
 
@@ -48,106 +30,106 @@ One secp256k1 dWallet signs for Bitcoin, Zcash transparent, and every EVM chain.
48
30
  npm install @frontiercompute/zcash-ika
49
31
  ```
50
32
 
51
- ## Quick start
33
+ ## Usage
52
34
 
53
35
  ```typescript
54
36
  import {
37
+ createWallet,
38
+ sign,
55
39
  createDualCustody,
56
- spendShielded,
57
- spendBitcoin,
58
- setPolicy,
59
40
  getHistory,
60
41
  checkCompliance,
61
42
  CHAIN_PARAMS,
62
43
  } from "@frontiercompute/zcash-ika";
63
44
 
64
45
  const config = {
65
- network: "mainnet",
66
- zebraRpcUrl: "http://127.0.0.1:8232",
46
+ network: "testnet",
47
+ suiPrivateKey: "suiprivkey1...",
67
48
  zap1ApiUrl: "https://pay.frontiercompute.io",
68
- zap1ApiKey: "your-key",
69
49
  };
70
50
 
71
- // Create dual custody - shielded ZEC + BTC/stablecoins
72
- const custody = await createDualCustody(config, operatorSeed);
73
-
74
- // Set spending policy (enforced by Sui contract, not by trust)
75
- await setPolicy(config, custody.shielded.id, {
76
- maxPerTx: 100_000, // 0.001 ZEC per tx
77
- maxDaily: 1_000_000, // 0.01 ZEC daily cap
78
- allowedRecipients: [], // any recipient (or lock to specific addresses)
79
- approvalThreshold: 500_000, // operator approval above 0.005 ZEC
51
+ // Create split-key wallet (secp256k1 - signs for ZEC + BTC + ETH)
52
+ const custody = await createDualCustody(config);
53
+ console.log("dWallet:", custody.primary.id);
54
+ console.log("Save this seed:", custody.primary.encryptionSeed);
55
+
56
+ // Sign a Zcash transparent sighash through MPC
57
+ const result = await sign(config, {
58
+ messageHash: sighashBytes, // DoubleSHA256 of the tx
59
+ walletId: custody.primary.id,
60
+ chain: "zcash-transparent",
61
+ encryptionSeed: custody.primary.encryptionSeed,
80
62
  });
81
-
82
- // Shielded spend - agent requests, both key shares cooperate
83
- const result = await spendShielded(config, custody.shielded.id, operatorSeed, {
84
- to: "u1abc...",
85
- amount: 50_000,
86
- memo: "payment for API access",
63
+ console.log("Signature:", Buffer.from(result.signature).toString("hex"));
64
+
65
+ // Sign Bitcoin (same dWallet, same MPC, same seed)
66
+ const btcSig = await sign(config, {
67
+ messageHash: btcSighash,
68
+ walletId: custody.primary.id,
69
+ chain: "bitcoin",
70
+ encryptionSeed: custody.primary.encryptionSeed,
87
71
  });
88
72
 
89
73
  // Compliance check (works now against live ZAP1 API)
90
- const compliance = await checkCompliance(config, custody.shielded.id);
91
- // { compliant: true, violations: 0, bondDeposits: 1 }
74
+ const compliance = await checkCompliance(config, custody.primary.id);
92
75
  ```
93
76
 
94
77
  ## How it works
95
78
 
96
79
  ```
97
- Operator (phone / hardware wallet / DAO multisig)
80
+ Operator (phone / hardware wallet)
98
81
  |
99
- | user key share
82
+ | user key share (encryption seed)
100
83
  |
101
- Ika MPC Network (2PC-MPC on Sui, mainnet live)
84
+ Ika MPC Network (2PC-MPC on Sui)
102
85
  |
103
86
  | network key share (distributed across nodes)
104
87
  |
105
88
  +-- Spending Policy (Sui Move contract)
106
89
  | max per tx, daily cap, approved recipients
107
- | the agent CANNOT modify its own limits
108
90
  |
109
- +-- Sign Zcash tx (Ed25519/EdDSA) -> shielded spend
110
- +-- Sign Bitcoin tx (secp256k1/ECDSA) -> BTC spend
111
- +-- Sign EVM tx (secp256k1/ECDSA) -> USDC/USDT spend
91
+ +-- Sign ZEC transparent tx (secp256k1/ECDSA/DoubleSHA256)
92
+ +-- Sign BTC tx (secp256k1/ECDSA/DoubleSHA256)
93
+ +-- Sign ETH tx (secp256k1/ECDSA/KECCAK256)
112
94
  |
113
95
  ZAP1 Attestation (Zcash mainnet)
114
- +-- every spend recorded as AGENT_ACTION
115
- +-- policy violations on-chain as POLICY_VIOLATION
116
- +-- bond deposits prove skin in the game
117
- +-- full audit trail, verifiable by anyone
96
+ +-- every spend recorded
97
+ +-- policy violations on-chain
98
+ +-- full audit trail
118
99
  ```
119
100
 
120
- ## What's live
101
+ ## Sign flow (two transactions)
102
+
103
+ 1. **Presign** - pre-compute MPC ephemeral key share (TX 1, poll for completion)
104
+ 2. **Sign** - approve message + request signature (TX 2, poll for completion)
121
105
 
122
- - Ed25519 dWallet on Ika testnet (TX: `FYcuaxBCAfuZqfBW7JEtEJME3KLBSBKLvhjLpZGSyaXb`)
123
- - `getHistory()` and `checkCompliance()` against live ZAP1 API
124
- - All Ika SDK primitives re-exported and typed
125
- - Chain parameter configs for all signing modes
126
- - DKG test script proving the full round-trip
106
+ Both transactions on Sui. The user partial signature is computed locally via WASM. Neither party ever sees the full private key.
127
107
 
128
- ## What's next
108
+ ## On-chain proof
129
109
 
130
- - secp256k1 dWallet for BTC/stablecoins (same flow, different curve)
131
- - Sign a real Zcash sighash through the MPC
132
- - Ed25519 -> Orchard spending key derivation bridge
133
- - Move policy template for spending limits
134
- - Mainnet deployment
110
+ secp256k1 dWallet created on Ika testnet:
135
111
 
136
- ## The competition
112
+ - dWalletId: `0xd9055400c88aeae675413b78143aa54e25eca7061ab659f54a42167cbfdd7aec`
113
+ - TX: [`CYrS5X1S3itHUtux4qS35AJz5AAyUaJYeWZuqm1CcX2L`](https://testnet.suivision.xyz/txblock/CYrS5X1S3itHUtux4qS35AJz5AAyUaJYeWZuqm1CcX2L)
114
+ - Public key: `03ba9e85a85674df494520c2e80b804656fac54fe68668266f33fee9b03ad4b069`
115
+ - Derived BTC: `moV3JAzgNa6NkxVfdaNqUjLoDxKEwNAnkX`
116
+ - Derived ZEC t-addr: `t1Rqh1TKqXsSiaV4wrSDandEPccucpHEudn`
137
117
 
138
- | Project | Custody | Privacy | Attestation |
139
- |---------|---------|---------|-------------|
140
- | Coinbase AgentKit | Full key in agent | None | None |
141
- | GOAT SDK | Full key in agent | None | None |
142
- | Solana Agent Kit | Full key in agent | None | None |
143
- | **zcash-ika** | **Split-key MPC** | **Zcash Orchard** | **ZAP1 on-chain** |
118
+ ## Test scripts
119
+
120
+ ```bash
121
+ # Create a new dWallet (saves encryption seed)
122
+ SUI_PRIVATE_KEY=suiprivkey1... node dist/test-dkg.js
123
+
124
+ # Sign a test message through MPC
125
+ SUI_PRIVATE_KEY=... DWALLET_ID=0x... ENC_SEED=... node dist/test-sign.js
126
+ ```
144
127
 
145
128
  ## Stack
146
129
 
147
130
  - [Ika](https://ika.xyz) - 2PC-MPC threshold signing on Sui
148
131
  - [ZAP1](https://pay.frontiercompute.io) - on-chain attestation protocol
149
132
  - [Zebra](https://github.com/ZcashFoundation/zebra) - Zcash node
150
- - [zcash-mcp](https://www.npmjs.com/package/@frontiercompute/zcash-mcp) - 17-tool MCP server
151
133
 
152
134
  ## License
153
135
 
@@ -0,0 +1,119 @@
1
+ /**
2
+ * Hybrid custody model for Zcash agents.
3
+ *
4
+ * Two custody paths, one interface:
5
+ *
6
+ * TRANSPARENT (secp256k1 via Ika MPC):
7
+ * - t-addr transactions signed through 2PC-MPC
8
+ * - Neither party holds the full key
9
+ * - Policy enforced by Sui Move contract
10
+ * - Same key signs BTC and ETH
11
+ *
12
+ * SHIELDED (RedPallas via local Orchard wallet):
13
+ * - Orchard transactions signed locally (direct key)
14
+ * - Ika cannot sign RedPallas (Pallas curve not supported)
15
+ * - Privacy from Zcash protocol, not from MPC
16
+ * - Every shielded spend attested to ZAP1
17
+ *
18
+ * The gap:
19
+ * Orchard uses RedPallas on the Pallas curve. Ika supports secp256k1,
20
+ * Ed25519, secp256r1, and Ristretto. None of these are Pallas.
21
+ * Until an MPC network adds Pallas curve support, shielded custody
22
+ * requires holding keys directly.
23
+ *
24
+ * The bridge:
25
+ * shield() - move ZEC from t-addr (MPC) to shielded pool (local wallet)
26
+ * unshield() - move ZEC from shielded back to t-addr (for MPC custody)
27
+ * Both operations attested on-chain via ZAP1.
28
+ */
29
+ import type { ZcashIkaConfig, DWalletHandle, SignResult } from "./index.js";
30
+ export type CustodyMode = "mpc" | "local";
31
+ export interface HybridWallet {
32
+ /** MPC-custody transparent wallet (secp256k1 dWallet on Ika) */
33
+ transparent: {
34
+ mode: "mpc";
35
+ walletId: string;
36
+ publicKey: Uint8Array;
37
+ tAddress: string;
38
+ encryptionSeed: string;
39
+ };
40
+ /** Local-custody shielded wallet (Orchard keys held directly) */
41
+ shielded: {
42
+ mode: "local";
43
+ /** Unified address (starts with u1) */
44
+ uAddress: string;
45
+ /** Whether the local wallet is initialized */
46
+ initialized: boolean;
47
+ };
48
+ /** ZAP1 attestation endpoint */
49
+ zap1ApiUrl: string;
50
+ }
51
+ export interface ShieldRequest {
52
+ /** Amount in zatoshis to move from t-addr to shielded pool */
53
+ amount: number;
54
+ /** Optional memo for the shielding transaction */
55
+ memo?: string;
56
+ }
57
+ export interface UnshieldRequest {
58
+ /** Amount in zatoshis to move from shielded back to t-addr */
59
+ amount: number;
60
+ /** Optional memo */
61
+ memo?: string;
62
+ }
63
+ export interface ShieldResult {
64
+ /** Zcash transaction ID */
65
+ txid: string;
66
+ /** Direction */
67
+ direction: "shield" | "unshield";
68
+ /** Amount moved */
69
+ amount: number;
70
+ /** ZAP1 attestation leaf hash */
71
+ leafHash?: string;
72
+ }
73
+ /**
74
+ * Create a hybrid wallet - MPC for transparent, local for shielded.
75
+ *
76
+ * The transparent side is an Ika dWallet (already created via createWallet).
77
+ * The shielded side connects to the local Zebra/Zashi wallet.
78
+ */
79
+ export declare function createHybridWallet(transparentWallet: DWalletHandle, shieldedAddress: string, zap1ApiUrl: string): HybridWallet;
80
+ /**
81
+ * Sign a transparent transaction through MPC.
82
+ * Delegates to the Ika sign() function.
83
+ */
84
+ export declare function signTransparent(config: ZcashIkaConfig, wallet: HybridWallet, sighash: Uint8Array): Promise<SignResult>;
85
+ /**
86
+ * Shield ZEC - move from MPC-custody t-addr to local shielded pool.
87
+ *
88
+ * Flow:
89
+ * 1. Build transparent tx: t-addr -> shielded address
90
+ * 2. Compute sighash (DoubleSHA256)
91
+ * 3. Sign via Ika MPC (secp256k1)
92
+ * 4. Broadcast via Zebra
93
+ * 5. Attest via ZAP1 as AGENT_ACTION
94
+ *
95
+ * After shielding, the ZEC is in the local Orchard wallet.
96
+ * Private from that point forward.
97
+ */
98
+ export declare function shield(config: ZcashIkaConfig, wallet: HybridWallet, request: ShieldRequest): Promise<ShieldResult>;
99
+ /**
100
+ * Unshield ZEC - move from local shielded pool back to MPC-custody t-addr.
101
+ *
102
+ * Flow:
103
+ * 1. Build Orchard tx: shielded -> t-addr (signed locally, RedPallas)
104
+ * 2. Broadcast via Zebra
105
+ * 3. Attest via ZAP1 as AGENT_ACTION
106
+ *
107
+ * After unshielding, the ZEC is back under MPC custody.
108
+ * The MPC can then send it to BTC, ETH, or another t-addr.
109
+ */
110
+ export declare function unshield(config: ZcashIkaConfig, wallet: HybridWallet, request: UnshieldRequest): Promise<ShieldResult>;
111
+ /**
112
+ * Attest a custody operation to ZAP1.
113
+ */
114
+ export declare function attestCustodyOp(zap1ApiUrl: string, zap1ApiKey: string, op: {
115
+ direction: "shield" | "unshield";
116
+ txid: string;
117
+ amount: number;
118
+ walletId: string;
119
+ }): Promise<string | null>;
package/dist/hybrid.js ADDED
@@ -0,0 +1,148 @@
1
+ /**
2
+ * Hybrid custody model for Zcash agents.
3
+ *
4
+ * Two custody paths, one interface:
5
+ *
6
+ * TRANSPARENT (secp256k1 via Ika MPC):
7
+ * - t-addr transactions signed through 2PC-MPC
8
+ * - Neither party holds the full key
9
+ * - Policy enforced by Sui Move contract
10
+ * - Same key signs BTC and ETH
11
+ *
12
+ * SHIELDED (RedPallas via local Orchard wallet):
13
+ * - Orchard transactions signed locally (direct key)
14
+ * - Ika cannot sign RedPallas (Pallas curve not supported)
15
+ * - Privacy from Zcash protocol, not from MPC
16
+ * - Every shielded spend attested to ZAP1
17
+ *
18
+ * The gap:
19
+ * Orchard uses RedPallas on the Pallas curve. Ika supports secp256k1,
20
+ * Ed25519, secp256r1, and Ristretto. None of these are Pallas.
21
+ * Until an MPC network adds Pallas curve support, shielded custody
22
+ * requires holding keys directly.
23
+ *
24
+ * The bridge:
25
+ * shield() - move ZEC from t-addr (MPC) to shielded pool (local wallet)
26
+ * unshield() - move ZEC from shielded back to t-addr (for MPC custody)
27
+ * Both operations attested on-chain via ZAP1.
28
+ */
29
+ import { sign } from "./index.js";
30
+ /**
31
+ * Create a hybrid wallet - MPC for transparent, local for shielded.
32
+ *
33
+ * The transparent side is an Ika dWallet (already created via createWallet).
34
+ * The shielded side connects to the local Zebra/Zashi wallet.
35
+ */
36
+ export function createHybridWallet(transparentWallet, shieldedAddress, zap1ApiUrl) {
37
+ return {
38
+ transparent: {
39
+ mode: "mpc",
40
+ walletId: transparentWallet.id,
41
+ publicKey: transparentWallet.publicKey,
42
+ tAddress: transparentWallet.address,
43
+ encryptionSeed: transparentWallet.encryptionSeed,
44
+ },
45
+ shielded: {
46
+ mode: "local",
47
+ uAddress: shieldedAddress,
48
+ initialized: shieldedAddress.startsWith("u1"),
49
+ },
50
+ zap1ApiUrl,
51
+ };
52
+ }
53
+ /**
54
+ * Sign a transparent transaction through MPC.
55
+ * Delegates to the Ika sign() function.
56
+ */
57
+ export async function signTransparent(config, wallet, sighash) {
58
+ return sign(config, {
59
+ messageHash: sighash,
60
+ walletId: wallet.transparent.walletId,
61
+ chain: "zcash-transparent",
62
+ encryptionSeed: wallet.transparent.encryptionSeed,
63
+ });
64
+ }
65
+ /**
66
+ * Shield ZEC - move from MPC-custody t-addr to local shielded pool.
67
+ *
68
+ * Flow:
69
+ * 1. Build transparent tx: t-addr -> shielded address
70
+ * 2. Compute sighash (DoubleSHA256)
71
+ * 3. Sign via Ika MPC (secp256k1)
72
+ * 4. Broadcast via Zebra
73
+ * 5. Attest via ZAP1 as AGENT_ACTION
74
+ *
75
+ * After shielding, the ZEC is in the local Orchard wallet.
76
+ * Private from that point forward.
77
+ */
78
+ export async function shield(config, wallet, request) {
79
+ if (!wallet.shielded.initialized) {
80
+ throw new Error("Shielded wallet not initialized. Provide a valid u1 address.");
81
+ }
82
+ if (!config.zebraRpcUrl) {
83
+ throw new Error("shield() requires zebraRpcUrl for building and broadcasting the tx.");
84
+ }
85
+ // The full pipeline:
86
+ // 1. z_listunspent on the t-addr to find UTXOs
87
+ // 2. Build raw tx spending to the shielded address
88
+ // 3. Extract sighash
89
+ // 4. sign() via Ika MPC
90
+ // 5. Attach signature
91
+ // 6. sendrawtransaction
92
+ // 7. Attest to ZAP1
93
+ throw new Error("shield() requires Zcash transparent tx builder (zcash-primitives or librustzcash). " +
94
+ "Use sign() with a pre-built sighash for now. " +
95
+ "The shielding tx is a standard t-addr -> Orchard spend.");
96
+ }
97
+ /**
98
+ * Unshield ZEC - move from local shielded pool back to MPC-custody t-addr.
99
+ *
100
+ * Flow:
101
+ * 1. Build Orchard tx: shielded -> t-addr (signed locally, RedPallas)
102
+ * 2. Broadcast via Zebra
103
+ * 3. Attest via ZAP1 as AGENT_ACTION
104
+ *
105
+ * After unshielding, the ZEC is back under MPC custody.
106
+ * The MPC can then send it to BTC, ETH, or another t-addr.
107
+ */
108
+ export async function unshield(config, wallet, request) {
109
+ if (!config.zebraRpcUrl) {
110
+ throw new Error("unshield() requires zebraRpcUrl.");
111
+ }
112
+ // Unshielding is done entirely locally - no MPC involved.
113
+ // The Orchard wallet holds keys directly and signs with RedPallas.
114
+ // z_sendmany from the shielded address to the t-addr.
115
+ throw new Error("unshield() requires connection to local Zebra wallet with Orchard spending keys. " +
116
+ "Use z_sendmany via Zebra RPC directly for now.");
117
+ }
118
+ /**
119
+ * Attest a custody operation to ZAP1.
120
+ */
121
+ export async function attestCustodyOp(zap1ApiUrl, zap1ApiKey, op) {
122
+ try {
123
+ const resp = await fetch(`${zap1ApiUrl}/attest`, {
124
+ method: "POST",
125
+ headers: {
126
+ "Content-Type": "application/json",
127
+ Authorization: `Bearer ${zap1ApiKey}`,
128
+ },
129
+ body: JSON.stringify({
130
+ event_type: "AGENT_ACTION",
131
+ wallet_hash: op.walletId,
132
+ input_hash: op.txid,
133
+ memo: JSON.stringify({
134
+ action: op.direction,
135
+ amount: op.amount,
136
+ custody: op.direction === "shield" ? "mpc->local" : "local->mpc",
137
+ }),
138
+ }),
139
+ });
140
+ if (!resp.ok)
141
+ return null;
142
+ const data = (await resp.json());
143
+ return data.leaf_hash || null;
144
+ }
145
+ catch {
146
+ return null;
147
+ }
148
+ }