@frontiercompute/zcash-ika 0.1.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 ADDED
@@ -0,0 +1,154 @@
1
+ # zcash-ika
2
+
3
+ Split-key custody for Zcash and Bitcoin. The private key never exists whole.
4
+
5
+ [![npm](https://img.shields.io/npm/v/@frontiercompute/zcash-ika)](https://www.npmjs.com/package/@frontiercompute/zcash-ika)
6
+
7
+ ## What this is
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).
10
+
11
+ **Ed25519 dWallet live on Ika testnet.** First Zcash-capable dWallet ever created on the network.
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.
30
+
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.
33
+
34
+ ## Signing parameters
35
+
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 |
42
+
43
+ One secp256k1 dWallet signs for Bitcoin, Zcash transparent, and every EVM chain.
44
+
45
+ ## Install
46
+
47
+ ```bash
48
+ npm install @frontiercompute/zcash-ika
49
+ ```
50
+
51
+ ## Quick start
52
+
53
+ ```typescript
54
+ import {
55
+ createDualCustody,
56
+ spendShielded,
57
+ spendBitcoin,
58
+ setPolicy,
59
+ getHistory,
60
+ checkCompliance,
61
+ CHAIN_PARAMS,
62
+ } from "@frontiercompute/zcash-ika";
63
+
64
+ const config = {
65
+ network: "mainnet",
66
+ zebraRpcUrl: "http://127.0.0.1:8232",
67
+ zap1ApiUrl: "https://pay.frontiercompute.io",
68
+ zap1ApiKey: "your-key",
69
+ };
70
+
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
80
+ });
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",
87
+ });
88
+
89
+ // Compliance check (works now against live ZAP1 API)
90
+ const compliance = await checkCompliance(config, custody.shielded.id);
91
+ // { compliant: true, violations: 0, bondDeposits: 1 }
92
+ ```
93
+
94
+ ## How it works
95
+
96
+ ```
97
+ Operator (phone / hardware wallet / DAO multisig)
98
+ |
99
+ | user key share
100
+ |
101
+ Ika MPC Network (2PC-MPC on Sui, mainnet live)
102
+ |
103
+ | network key share (distributed across nodes)
104
+ |
105
+ +-- Spending Policy (Sui Move contract)
106
+ | max per tx, daily cap, approved recipients
107
+ | the agent CANNOT modify its own limits
108
+ |
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
112
+ |
113
+ 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
118
+ ```
119
+
120
+ ## What's live
121
+
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
127
+
128
+ ## What's next
129
+
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
135
+
136
+ ## The competition
137
+
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** |
144
+
145
+ ## Stack
146
+
147
+ - [Ika](https://ika.xyz) - 2PC-MPC threshold signing on Sui
148
+ - [ZAP1](https://pay.frontiercompute.io) - on-chain attestation protocol
149
+ - [Zebra](https://github.com/ZcashFoundation/zebra) - Zcash node
150
+ - [zcash-mcp](https://www.npmjs.com/package/@frontiercompute/zcash-mcp) - 17-tool MCP server
151
+
152
+ ## License
153
+
154
+ MIT
@@ -0,0 +1,186 @@
1
+ /**
2
+ * @frontiercompute/zcash-ika
3
+ *
4
+ * Zero-trust custody for Zcash and Bitcoin. Born shielded, stay shielded.
5
+ *
6
+ * Two dWallets, one operator:
7
+ * - Ed25519 dWallet -> Zcash Orchard (shielded ZEC)
8
+ * - secp256k1 dWallet -> Bitcoin (BTC) + Zcash transparent (t-addr)
9
+ *
10
+ * Neither key ever exists whole. Both chains signed through Ika 2PC-MPC.
11
+ * Every operation attested to Zcash via ZAP1.
12
+ *
13
+ * Built on Ika's 2PC-MPC network (Sui).
14
+ */
15
+ export { Curve, Hash, SignatureAlgorithm, IkaClient, IkaTransaction, UserShareEncryptionKeys, getNetworkConfig, createClassGroupsKeypair, createRandomSessionIdentifier, prepareDKG, prepareDKGAsync, prepareDKGSecondRound, prepareDKGSecondRoundAsync, createDKGUserOutput, publicKeyFromDWalletOutput, parseSignatureFromSignOutput, } from "@ika.xyz/sdk";
16
+ export type Chain = "zcash-shielded" | "zcash-transparent" | "bitcoin";
17
+ export interface ZcashIkaConfig {
18
+ /** Ika network: mainnet or testnet */
19
+ network: "mainnet" | "testnet";
20
+ /** Sui RPC URL (defaults to Ika's network config) */
21
+ suiRpcUrl?: string;
22
+ /** Zebra node RPC for broadcasting Zcash txs */
23
+ zebraRpcUrl: string;
24
+ /** ZAP1 API for attestation */
25
+ zap1ApiUrl: string;
26
+ /** ZAP1 API key for write operations */
27
+ zap1ApiKey?: string;
28
+ }
29
+ /** Parameters for dWallet creation per chain */
30
+ export declare const CHAIN_PARAMS: {
31
+ readonly "zcash-shielded": {
32
+ readonly curve: "ED25519";
33
+ readonly algorithm: "EdDSA";
34
+ readonly hash: "SHA512";
35
+ readonly description: "Zcash Orchard shielded pool (Ed25519/EdDSA)";
36
+ };
37
+ readonly "zcash-transparent": {
38
+ readonly curve: "SECP256K1";
39
+ readonly algorithm: "ECDSASecp256k1";
40
+ readonly hash: "DoubleSHA256";
41
+ readonly description: "Zcash transparent t-address (secp256k1/ECDSA)";
42
+ };
43
+ readonly bitcoin: {
44
+ readonly curve: "SECP256K1";
45
+ readonly algorithm: "ECDSASecp256k1";
46
+ readonly hash: "DoubleSHA256";
47
+ readonly description: "Bitcoin (secp256k1/ECDSA, DoubleSHA256)";
48
+ };
49
+ };
50
+ export interface DWalletHandle {
51
+ /** dWallet object ID on Sui */
52
+ id: string;
53
+ /** Raw public key bytes */
54
+ publicKey: Uint8Array;
55
+ /** Which chain this wallet targets */
56
+ chain: Chain;
57
+ /** Derived address for the target chain */
58
+ address: string;
59
+ /** Ika network (mainnet/testnet) */
60
+ network: string;
61
+ }
62
+ export interface DualCustody {
63
+ /** Shielded ZEC wallet (Ed25519 dWallet -> Orchard address) */
64
+ shielded: DWalletHandle;
65
+ /** Bitcoin wallet (secp256k1 dWallet -> BTC address) */
66
+ bitcoin: DWalletHandle;
67
+ /** Operator ID (shared across both wallets) */
68
+ operatorId: string;
69
+ }
70
+ export interface SpendPolicy {
71
+ /** Max zatoshis (or satoshis) per single transaction */
72
+ maxPerTx: number;
73
+ /** Max per 24h window */
74
+ maxDaily: number;
75
+ /** Allowed recipient addresses (empty = any) */
76
+ allowedRecipients: string[];
77
+ /** Require operator approval above this amount */
78
+ approvalThreshold: number;
79
+ }
80
+ export interface SpendRequest {
81
+ /** Recipient address (Orchard UA, t-addr, or BTC address) */
82
+ to: string;
83
+ /** Amount in smallest unit (zatoshis or satoshis) */
84
+ amount: number;
85
+ /** Memo (ZAP1 structured or plain text, Zcash only) */
86
+ memo?: string;
87
+ }
88
+ export interface SpendResult {
89
+ /** Transaction ID on target chain */
90
+ txid: string;
91
+ /** ZAP1 attestation leaf hash */
92
+ leafHash: string;
93
+ /** Chain the spend was on */
94
+ chain: Chain;
95
+ /** Whether policy was checked */
96
+ policyChecked: boolean;
97
+ }
98
+ export interface SignRequest {
99
+ /** Raw message hash to sign (sighash) */
100
+ messageHash: Uint8Array;
101
+ /** Which dWallet to sign with */
102
+ walletId: string;
103
+ /** Chain determines signing params */
104
+ chain: Chain;
105
+ }
106
+ export interface SignResult {
107
+ /** DER-encoded signature (ECDSA) or raw Ed25519 signature */
108
+ signature: Uint8Array;
109
+ /** Public key used */
110
+ publicKey: Uint8Array;
111
+ }
112
+ /**
113
+ * Create a dual-custody setup: one shielded ZEC wallet + one BTC wallet.
114
+ * Same operator controls both via Ika split-key.
115
+ *
116
+ * Flow per wallet:
117
+ * 1. Generate UserShareEncryptionKeys from operator seed
118
+ * 2. Run DKG on Ika (2PC-MPC key generation)
119
+ * 3. Extract public key, derive chain-specific address
120
+ * 4. Attest wallet creation via ZAP1
121
+ */
122
+ export declare function createDualCustody(config: ZcashIkaConfig, operatorSeed: Uint8Array): Promise<DualCustody>;
123
+ /**
124
+ * Create a single dWallet for a specific chain.
125
+ */
126
+ export declare function createWallet(config: ZcashIkaConfig, chain: Chain, operatorSeed: Uint8Array): Promise<DWalletHandle>;
127
+ /**
128
+ * Sign a message hash through Ika 2PC-MPC.
129
+ *
130
+ * The operator provides their seed, Ika provides the network share.
131
+ * Neither party ever sees the full private key.
132
+ *
133
+ * Flow:
134
+ * 1. Create presign session on Ika
135
+ * 2. Compute partial user signature locally
136
+ * 3. Submit to Ika coordinator
137
+ * 4. Poll for completion
138
+ * 5. Extract full signature from sign output
139
+ */
140
+ export declare function sign(config: ZcashIkaConfig, operatorSeed: Uint8Array, request: SignRequest): Promise<SignResult>;
141
+ /**
142
+ * Set spending policy on the dWallet.
143
+ * Policy enforced at Sui Move contract level.
144
+ * The agent cannot bypass it - the contract holds the DWalletCap.
145
+ */
146
+ export declare function setPolicy(config: ZcashIkaConfig, walletId: string, policy: SpendPolicy): Promise<string>;
147
+ /**
148
+ * Spend from a shielded ZEC wallet.
149
+ *
150
+ * 1. Build Zcash Orchard transaction (zcash_primitives)
151
+ * 2. Extract sighash
152
+ * 3. Sign via Ika 2PC-MPC (Ed25519/EdDSA)
153
+ * 4. Attach signature to transaction
154
+ * 5. Broadcast via Zebra sendrawtransaction
155
+ * 6. Attest via ZAP1 as AGENT_ACTION
156
+ */
157
+ export declare function spendShielded(config: ZcashIkaConfig, walletId: string, operatorSeed: Uint8Array, request: SpendRequest): Promise<SpendResult>;
158
+ /**
159
+ * Spend from a Bitcoin wallet.
160
+ *
161
+ * 1. Build Bitcoin transaction
162
+ * 2. Compute sighash (DoubleSHA256)
163
+ * 3. Sign via Ika 2PC-MPC (secp256k1/ECDSA)
164
+ * 4. Attach signature
165
+ * 5. Broadcast to Bitcoin network
166
+ * 6. Attest via ZAP1 as AGENT_ACTION
167
+ */
168
+ export declare function spendBitcoin(config: ZcashIkaConfig, walletId: string, operatorSeed: Uint8Array, request: SpendRequest): Promise<SpendResult>;
169
+ /**
170
+ * Verify the wallet's attestation history via ZAP1.
171
+ * Works today against the live API.
172
+ */
173
+ export declare function getHistory(config: ZcashIkaConfig, walletId: string): Promise<{
174
+ leafHash: string;
175
+ eventType: string;
176
+ timestamp: string;
177
+ }[]>;
178
+ /**
179
+ * Check wallet's policy compliance status via ZAP1.
180
+ * Works today against the live API.
181
+ */
182
+ export declare function checkCompliance(config: ZcashIkaConfig, walletId: string): Promise<{
183
+ compliant: boolean;
184
+ violations: number;
185
+ bondDeposits: number;
186
+ }>;
package/dist/index.js ADDED
@@ -0,0 +1,168 @@
1
+ /**
2
+ * @frontiercompute/zcash-ika
3
+ *
4
+ * Zero-trust custody for Zcash and Bitcoin. Born shielded, stay shielded.
5
+ *
6
+ * Two dWallets, one operator:
7
+ * - Ed25519 dWallet -> Zcash Orchard (shielded ZEC)
8
+ * - secp256k1 dWallet -> Bitcoin (BTC) + Zcash transparent (t-addr)
9
+ *
10
+ * Neither key ever exists whole. Both chains signed through Ika 2PC-MPC.
11
+ * Every operation attested to Zcash via ZAP1.
12
+ *
13
+ * Built on Ika's 2PC-MPC network (Sui).
14
+ */
15
+ export { Curve, Hash, SignatureAlgorithm, IkaClient, IkaTransaction, UserShareEncryptionKeys, getNetworkConfig, createClassGroupsKeypair, createRandomSessionIdentifier, prepareDKG, prepareDKGAsync, prepareDKGSecondRound, prepareDKGSecondRoundAsync, createDKGUserOutput, publicKeyFromDWalletOutput, parseSignatureFromSignOutput, } from "@ika.xyz/sdk";
16
+ /** Parameters for dWallet creation per chain */
17
+ export const CHAIN_PARAMS = {
18
+ "zcash-shielded": {
19
+ curve: "ED25519",
20
+ algorithm: "EdDSA",
21
+ hash: "SHA512",
22
+ description: "Zcash Orchard shielded pool (Ed25519/EdDSA)",
23
+ },
24
+ "zcash-transparent": {
25
+ curve: "SECP256K1",
26
+ algorithm: "ECDSASecp256k1",
27
+ hash: "DoubleSHA256",
28
+ description: "Zcash transparent t-address (secp256k1/ECDSA)",
29
+ },
30
+ bitcoin: {
31
+ curve: "SECP256K1",
32
+ algorithm: "ECDSASecp256k1",
33
+ hash: "DoubleSHA256",
34
+ description: "Bitcoin (secp256k1/ECDSA, DoubleSHA256)",
35
+ },
36
+ };
37
+ /**
38
+ * Create a dual-custody setup: one shielded ZEC wallet + one BTC wallet.
39
+ * Same operator controls both via Ika split-key.
40
+ *
41
+ * Flow per wallet:
42
+ * 1. Generate UserShareEncryptionKeys from operator seed
43
+ * 2. Run DKG on Ika (2PC-MPC key generation)
44
+ * 3. Extract public key, derive chain-specific address
45
+ * 4. Attest wallet creation via ZAP1
46
+ */
47
+ export async function createDualCustody(config, operatorSeed) {
48
+ // Both wallets share one operator seed.
49
+ // Each gets its own dWallet with different curve params.
50
+ //
51
+ // Phase 1 (current): throw with setup instructions
52
+ // Phase 2: actual DKG on Ika testnet
53
+ throw new Error("createDualCustody requires Ika network access. " +
54
+ "Shielded: Ed25519/EdDSA/SHA512 dWallet -> Orchard address. " +
55
+ "Bitcoin: secp256k1/ECDSA/DoubleSHA256 dWallet -> BTC address. " +
56
+ "npm install @ika.xyz/sdk @mysten/sui && configure Sui wallet. " +
57
+ "See https://docs.ika.xyz for DKG walkthrough.");
58
+ }
59
+ /**
60
+ * Create a single dWallet for a specific chain.
61
+ */
62
+ export async function createWallet(config, chain, operatorSeed) {
63
+ const params = CHAIN_PARAMS[chain];
64
+ // The DKG flow:
65
+ // 1. IkaClient.init({ network: config.network })
66
+ // 2. UserShareEncryptionKeys from operatorSeed
67
+ // 3. prepareDKG(curve, signatureAlgorithm, hash)
68
+ // 4. Submit DKG round 1 to Ika via IkaTransaction
69
+ // 5. prepareDKGSecondRound with network output
70
+ // 6. Submit round 2 -> get dWallet object ID + public key
71
+ // 7. Derive chain address from public key
72
+ throw new Error(`createWallet(${chain}) requires Ika ${config.network} access. ` +
73
+ `Params: ${params.curve}/${params.algorithm}/${params.hash}. ` +
74
+ `${params.description}.`);
75
+ }
76
+ /**
77
+ * Sign a message hash through Ika 2PC-MPC.
78
+ *
79
+ * The operator provides their seed, Ika provides the network share.
80
+ * Neither party ever sees the full private key.
81
+ *
82
+ * Flow:
83
+ * 1. Create presign session on Ika
84
+ * 2. Compute partial user signature locally
85
+ * 3. Submit to Ika coordinator
86
+ * 4. Poll for completion
87
+ * 5. Extract full signature from sign output
88
+ */
89
+ export async function sign(config, operatorSeed, request) {
90
+ const params = CHAIN_PARAMS[request.chain];
91
+ // The sign flow:
92
+ // 1. IkaClient.init({ network: config.network })
93
+ // 2. RequestGlobalPresign for the dWallet
94
+ // 3. createUserSignMessageWithCentralizedOutput(messageHash, userShare, ...)
95
+ // 4. ApproveMessage on Sui (this is where Move policy gates)
96
+ // 5. RequestSign -> poll SessionsManager for Completed status
97
+ // 6. parseSignatureFromSignOutput(signOutput)
98
+ throw new Error(`sign requires active dWallet + Ika ${config.network}. ` +
99
+ `Chain: ${request.chain}, params: ${params.curve}/${params.algorithm}/${params.hash}.`);
100
+ }
101
+ /**
102
+ * Set spending policy on the dWallet.
103
+ * Policy enforced at Sui Move contract level.
104
+ * The agent cannot bypass it - the contract holds the DWalletCap.
105
+ */
106
+ export async function setPolicy(config, walletId, policy) {
107
+ throw new Error("setPolicy requires a deployed Move module on Sui. " +
108
+ "The module gates approve_message() with spending constraints. " +
109
+ "See docs/move-policy-template.move for the template.");
110
+ }
111
+ /**
112
+ * Spend from a shielded ZEC wallet.
113
+ *
114
+ * 1. Build Zcash Orchard transaction (zcash_primitives)
115
+ * 2. Extract sighash
116
+ * 3. Sign via Ika 2PC-MPC (Ed25519/EdDSA)
117
+ * 4. Attach signature to transaction
118
+ * 5. Broadcast via Zebra sendrawtransaction
119
+ * 6. Attest via ZAP1 as AGENT_ACTION
120
+ */
121
+ export async function spendShielded(config, walletId, operatorSeed, request) {
122
+ throw new Error("spendShielded requires active Ed25519 dWallet + Zebra node. " +
123
+ "Integration in progress - need Ed25519 -> Orchard spending key derivation bridge.");
124
+ }
125
+ /**
126
+ * Spend from a Bitcoin wallet.
127
+ *
128
+ * 1. Build Bitcoin transaction
129
+ * 2. Compute sighash (DoubleSHA256)
130
+ * 3. Sign via Ika 2PC-MPC (secp256k1/ECDSA)
131
+ * 4. Attach signature
132
+ * 5. Broadcast to Bitcoin network
133
+ * 6. Attest via ZAP1 as AGENT_ACTION
134
+ */
135
+ export async function spendBitcoin(config, walletId, operatorSeed, request) {
136
+ throw new Error("spendBitcoin requires active secp256k1 dWallet + Bitcoin node. " +
137
+ "Same MPC flow as Zcash transparent - DoubleSHA256 sighash, ECDSA signature.");
138
+ }
139
+ /**
140
+ * Verify the wallet's attestation history via ZAP1.
141
+ * Works today against the live API.
142
+ */
143
+ export async function getHistory(config, walletId) {
144
+ const resp = await fetch(`${config.zap1ApiUrl}/lifecycle/${walletId}`);
145
+ if (!resp.ok)
146
+ return [];
147
+ const data = (await resp.json());
148
+ return (data.leaves || []).map((l) => ({
149
+ leafHash: l.leaf_hash,
150
+ eventType: l.event_type,
151
+ timestamp: l.created_at,
152
+ }));
153
+ }
154
+ /**
155
+ * Check wallet's policy compliance status via ZAP1.
156
+ * Works today against the live API.
157
+ */
158
+ export async function checkCompliance(config, walletId) {
159
+ const resp = await fetch(`${config.zap1ApiUrl}/agent/${walletId}/policy/verify`);
160
+ if (!resp.ok)
161
+ return { compliant: false, violations: -1, bondDeposits: 0 };
162
+ const data = (await resp.json());
163
+ return {
164
+ compliant: data.compliant,
165
+ violations: data.violations,
166
+ bondDeposits: data.bond_deposits || 0,
167
+ };
168
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Ika DKG test - create dWallets on testnet.
3
+ *
4
+ * Creates:
5
+ * 1. Ed25519 dWallet (Zcash Orchard shielded)
6
+ * 2. secp256k1 dWallet (Bitcoin + USDC + USDT + any EVM)
7
+ *
8
+ * One operator, split-key custody across all chains.
9
+ * Swiss bank in your pocket. Jailbroken but legal tender.
10
+ *
11
+ * Requires: SUI_PRIVATE_KEY env var (base64 Sui keypair)
12
+ * Get testnet SUI: https://faucet.sui.io
13
+ *
14
+ * Usage:
15
+ * SUI_PRIVATE_KEY=... node dist/test-dkg.js
16
+ */
17
+ export {};
@@ -0,0 +1,150 @@
1
+ // @ts-nocheck
2
+ /**
3
+ * Ika DKG test - create dWallets on testnet.
4
+ *
5
+ * Creates:
6
+ * 1. Ed25519 dWallet (Zcash Orchard shielded)
7
+ * 2. secp256k1 dWallet (Bitcoin + USDC + USDT + any EVM)
8
+ *
9
+ * One operator, split-key custody across all chains.
10
+ * Swiss bank in your pocket. Jailbroken but legal tender.
11
+ *
12
+ * Requires: SUI_PRIVATE_KEY env var (base64 Sui keypair)
13
+ * Get testnet SUI: https://faucet.sui.io
14
+ *
15
+ * Usage:
16
+ * SUI_PRIVATE_KEY=... node dist/test-dkg.js
17
+ */
18
+ import { Ed25519Keypair } from "@mysten/sui/keypairs/ed25519";
19
+ import { Transaction } from "@mysten/sui/transactions";
20
+ import { decodeSuiPrivateKey } from "@mysten/sui/cryptography";
21
+ import { IkaClient, IkaTransaction, UserShareEncryptionKeys, getNetworkConfig, Curve, prepareDKGAsync, createRandomSessionIdentifier, CHAIN_PARAMS, } from "./index.js";
22
+ const NETWORK = "testnet";
23
+ async function createDWallet(ikaClient, suiClient, keypair, encKeys, chain, address) {
24
+ const params = CHAIN_PARAMS[chain];
25
+ console.log(`\n--- ${params.description} ---`);
26
+ console.log(`Params: ${params.curve}/${params.algorithm}/${params.hash}`);
27
+ // Prepare DKG (async fetches protocol public params from network)
28
+ const bytesToHash = createRandomSessionIdentifier();
29
+ const dkgInput = await prepareDKGAsync(ikaClient, Curve[params.curve], encKeys, bytesToHash, address);
30
+ console.log("DKG prepared locally");
31
+ // Build transaction
32
+ const tx = new Transaction();
33
+ const ikaTx = new IkaTransaction({
34
+ ikaClient,
35
+ transaction: tx,
36
+ userShareEncryptionKeys: encKeys,
37
+ });
38
+ const sessionId = ikaTx.registerSessionIdentifier(bytesToHash);
39
+ // Get network encryption key
40
+ const networkEncKey = await ikaClient.getLatestNetworkEncryptionKey?.()
41
+ || await ikaClient.getConfiguredNetworkEncryptionKey?.();
42
+ if (!networkEncKey) {
43
+ // Try without - some SDK versions auto-detect
44
+ console.log("No explicit encryption key method found, trying auto-detect...");
45
+ }
46
+ // Submit DKG request
47
+ const dkgResult = await ikaTx.requestDWalletDKG({
48
+ dkgRequestInput: dkgInput,
49
+ sessionIdentifier: sessionId,
50
+ dwalletNetworkEncryptionKeyId: networkEncKey?.id,
51
+ curve: Curve[params.curve],
52
+ ikaCoin: tx.splitCoins(tx.gas, [50_000_000]),
53
+ suiCoin: tx.splitCoins(tx.gas, [50_000_000]),
54
+ });
55
+ console.log("Submitting DKG to Ika network...");
56
+ const result = await suiClient.signAndExecuteTransaction({
57
+ transaction: tx,
58
+ signer: keypair,
59
+ options: { showEffects: true, showEvents: true },
60
+ });
61
+ console.log("TX digest:", result.digest);
62
+ if (result.effects?.status?.status !== "success") {
63
+ console.error("TX failed:", result.effects?.status?.error);
64
+ return null;
65
+ }
66
+ console.log("DKG submitted. Poll for completion...");
67
+ // Extract dWallet ID from events/created objects
68
+ const created = result.effects?.created || [];
69
+ console.log(`Created ${created.length} objects`);
70
+ for (const obj of created) {
71
+ console.log(` ${obj.reference?.objectId || obj}`);
72
+ }
73
+ return result;
74
+ }
75
+ async function main() {
76
+ const privKeyRaw = process.env.SUI_PRIVATE_KEY;
77
+ if (!privKeyRaw) {
78
+ console.log("zcash-ika DKG test");
79
+ console.log("==================");
80
+ console.log("");
81
+ console.log("Chain params (what Ika signs for each chain):");
82
+ for (const [chain, params] of Object.entries(CHAIN_PARAMS)) {
83
+ console.log(` ${chain}: ${params.curve}/${params.algorithm}/${params.hash}`);
84
+ }
85
+ console.log("");
86
+ console.log("secp256k1/ECDSA/DoubleSHA256 signs for:");
87
+ console.log(" - Bitcoin (BTC)");
88
+ console.log(" - Zcash transparent (t-addr)");
89
+ console.log(" - Ethereum/Base (USDC, USDT) via KECCAK256 variant");
90
+ console.log("");
91
+ console.log("Ed25519/EdDSA/SHA512 signs for:");
92
+ console.log(" - Zcash Orchard (shielded ZEC)");
93
+ console.log("");
94
+ console.log("One operator. Split-key custody. Every chain.");
95
+ console.log("");
96
+ console.log("To run DKG:");
97
+ console.log(" SUI_PRIVATE_KEY=suiprivkey1... node dist/test-dkg.js");
98
+ console.log(" Get testnet SUI: https://faucet.sui.io");
99
+ return;
100
+ }
101
+ // Decode Sui keypair
102
+ const decoded = decodeSuiPrivateKey(privKeyRaw);
103
+ const keypair = Ed25519Keypair.fromSecretKey(decoded.secretKey);
104
+ const address = keypair.getPublicKey().toSuiAddress();
105
+ console.log(`Sui address: ${address}`);
106
+ // Init clients
107
+ // PublicNode doesn't rate limit like Mysten's public RPC
108
+ const { SuiJsonRpcClient } = await import("@mysten/sui/jsonRpc");
109
+ const suiClient = new SuiJsonRpcClient({ url: "https://sui-testnet-rpc.publicnode.com" });
110
+ const ikaConfig = getNetworkConfig(NETWORK);
111
+ if (!ikaConfig)
112
+ throw new Error("No Ika testnet config");
113
+ const ikaClient = new IkaClient({ suiClient, config: ikaConfig });
114
+ await ikaClient.initialize();
115
+ console.log("Ika client initialized on testnet");
116
+ // Check balance
117
+ const balance = await suiClient.getBalance({ owner: address });
118
+ const suiBalance = Number(balance.totalBalance) / 1e9;
119
+ console.log(`SUI balance: ${suiBalance} SUI`);
120
+ if (suiBalance < 0.2) {
121
+ console.log("Need at least 0.2 SUI for gas (two DKG operations).");
122
+ console.log("Get testnet SUI: https://faucet.sui.io");
123
+ return;
124
+ }
125
+ // Generate encryption keys - one per curve
126
+ const seed = new Uint8Array(32);
127
+ crypto.getRandomValues(seed);
128
+ // Ed25519 encryption keys for shielded ZEC wallet
129
+ const edEncKeys = await UserShareEncryptionKeys.fromRootSeedKey(seed, Curve.ED25519);
130
+ console.log("Ed25519 encryption keys generated");
131
+ // secp256k1 encryption keys for BTC/stablecoin wallet
132
+ const secpEncKeys = await UserShareEncryptionKeys.fromRootSeedKey(seed, Curve.SECP256K1);
133
+ console.log("secp256k1 encryption keys generated");
134
+ // Create Ed25519 dWallet (Zcash Orchard)
135
+ try {
136
+ await createDWallet(ikaClient, suiClient, keypair, edEncKeys, "zcash-shielded", address);
137
+ }
138
+ catch (err) {
139
+ console.error("Ed25519 DKG error:", err.message);
140
+ }
141
+ // Create secp256k1 dWallet (Bitcoin + stablecoins)
142
+ try {
143
+ await createDWallet(ikaClient, suiClient, keypair, secpEncKeys, "bitcoin", address);
144
+ }
145
+ catch (err) {
146
+ console.error("secp256k1 DKG error:", err.message);
147
+ }
148
+ console.log("\nDone.");
149
+ }
150
+ main().catch(console.error);
package/package.json ADDED
@@ -0,0 +1,20 @@
1
+ {
2
+ "name": "@frontiercompute/zcash-ika",
3
+ "version": "0.1.0",
4
+ "description": "Zero-trust Zcash agent custody via Ika dWallet. Born shielded, stay shielded.",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "scripts": {
8
+ "build": "tsc",
9
+ "test": "node --experimental-vm-modules dist/test.js"
10
+ },
11
+ "dependencies": {
12
+ "@ika.xyz/sdk": "^0.3.1",
13
+ "@mysten/sui": "^2.5.0"
14
+ },
15
+ "devDependencies": {
16
+ "@types/node": "^25.5.2",
17
+ "typescript": "^5.4.0"
18
+ },
19
+ "license": "MIT"
20
+ }
package/src/index.ts ADDED
@@ -0,0 +1,338 @@
1
+ /**
2
+ * @frontiercompute/zcash-ika
3
+ *
4
+ * Zero-trust custody for Zcash and Bitcoin. Born shielded, stay shielded.
5
+ *
6
+ * Two dWallets, one operator:
7
+ * - Ed25519 dWallet -> Zcash Orchard (shielded ZEC)
8
+ * - secp256k1 dWallet -> Bitcoin (BTC) + Zcash transparent (t-addr)
9
+ *
10
+ * Neither key ever exists whole. Both chains signed through Ika 2PC-MPC.
11
+ * Every operation attested to Zcash via ZAP1.
12
+ *
13
+ * Built on Ika's 2PC-MPC network (Sui).
14
+ */
15
+
16
+ export {
17
+ Curve,
18
+ Hash,
19
+ SignatureAlgorithm,
20
+ IkaClient,
21
+ IkaTransaction,
22
+ UserShareEncryptionKeys,
23
+ getNetworkConfig,
24
+ createClassGroupsKeypair,
25
+ createRandomSessionIdentifier,
26
+ prepareDKG,
27
+ prepareDKGAsync,
28
+ prepareDKGSecondRound,
29
+ prepareDKGSecondRoundAsync,
30
+ createDKGUserOutput,
31
+ publicKeyFromDWalletOutput,
32
+ parseSignatureFromSignOutput,
33
+ } from "@ika.xyz/sdk";
34
+
35
+ // Chain identifiers for wallet creation
36
+ export type Chain = "zcash-shielded" | "zcash-transparent" | "bitcoin";
37
+
38
+ export interface ZcashIkaConfig {
39
+ /** Ika network: mainnet or testnet */
40
+ network: "mainnet" | "testnet";
41
+ /** Sui RPC URL (defaults to Ika's network config) */
42
+ suiRpcUrl?: string;
43
+ /** Zebra node RPC for broadcasting Zcash txs */
44
+ zebraRpcUrl: string;
45
+ /** ZAP1 API for attestation */
46
+ zap1ApiUrl: string;
47
+ /** ZAP1 API key for write operations */
48
+ zap1ApiKey?: string;
49
+ }
50
+
51
+ /** Parameters for dWallet creation per chain */
52
+ export const CHAIN_PARAMS = {
53
+ "zcash-shielded": {
54
+ curve: "ED25519" as const,
55
+ algorithm: "EdDSA" as const,
56
+ hash: "SHA512" as const,
57
+ description: "Zcash Orchard shielded pool (Ed25519/EdDSA)",
58
+ },
59
+ "zcash-transparent": {
60
+ curve: "SECP256K1" as const,
61
+ algorithm: "ECDSASecp256k1" as const,
62
+ hash: "DoubleSHA256" as const,
63
+ description: "Zcash transparent t-address (secp256k1/ECDSA)",
64
+ },
65
+ bitcoin: {
66
+ curve: "SECP256K1" as const,
67
+ algorithm: "ECDSASecp256k1" as const,
68
+ hash: "DoubleSHA256" as const,
69
+ description: "Bitcoin (secp256k1/ECDSA, DoubleSHA256)",
70
+ },
71
+ } as const;
72
+
73
+ export interface DWalletHandle {
74
+ /** dWallet object ID on Sui */
75
+ id: string;
76
+ /** Raw public key bytes */
77
+ publicKey: Uint8Array;
78
+ /** Which chain this wallet targets */
79
+ chain: Chain;
80
+ /** Derived address for the target chain */
81
+ address: string;
82
+ /** Ika network (mainnet/testnet) */
83
+ network: string;
84
+ }
85
+
86
+ export interface DualCustody {
87
+ /** Shielded ZEC wallet (Ed25519 dWallet -> Orchard address) */
88
+ shielded: DWalletHandle;
89
+ /** Bitcoin wallet (secp256k1 dWallet -> BTC address) */
90
+ bitcoin: DWalletHandle;
91
+ /** Operator ID (shared across both wallets) */
92
+ operatorId: string;
93
+ }
94
+
95
+ export interface SpendPolicy {
96
+ /** Max zatoshis (or satoshis) per single transaction */
97
+ maxPerTx: number;
98
+ /** Max per 24h window */
99
+ maxDaily: number;
100
+ /** Allowed recipient addresses (empty = any) */
101
+ allowedRecipients: string[];
102
+ /** Require operator approval above this amount */
103
+ approvalThreshold: number;
104
+ }
105
+
106
+ export interface SpendRequest {
107
+ /** Recipient address (Orchard UA, t-addr, or BTC address) */
108
+ to: string;
109
+ /** Amount in smallest unit (zatoshis or satoshis) */
110
+ amount: number;
111
+ /** Memo (ZAP1 structured or plain text, Zcash only) */
112
+ memo?: string;
113
+ }
114
+
115
+ export interface SpendResult {
116
+ /** Transaction ID on target chain */
117
+ txid: string;
118
+ /** ZAP1 attestation leaf hash */
119
+ leafHash: string;
120
+ /** Chain the spend was on */
121
+ chain: Chain;
122
+ /** Whether policy was checked */
123
+ policyChecked: boolean;
124
+ }
125
+
126
+ export interface SignRequest {
127
+ /** Raw message hash to sign (sighash) */
128
+ messageHash: Uint8Array;
129
+ /** Which dWallet to sign with */
130
+ walletId: string;
131
+ /** Chain determines signing params */
132
+ chain: Chain;
133
+ }
134
+
135
+ export interface SignResult {
136
+ /** DER-encoded signature (ECDSA) or raw Ed25519 signature */
137
+ signature: Uint8Array;
138
+ /** Public key used */
139
+ publicKey: Uint8Array;
140
+ }
141
+
142
+ /**
143
+ * Create a dual-custody setup: one shielded ZEC wallet + one BTC wallet.
144
+ * Same operator controls both via Ika split-key.
145
+ *
146
+ * Flow per wallet:
147
+ * 1. Generate UserShareEncryptionKeys from operator seed
148
+ * 2. Run DKG on Ika (2PC-MPC key generation)
149
+ * 3. Extract public key, derive chain-specific address
150
+ * 4. Attest wallet creation via ZAP1
151
+ */
152
+ export async function createDualCustody(
153
+ config: ZcashIkaConfig,
154
+ operatorSeed: Uint8Array
155
+ ): Promise<DualCustody> {
156
+ // Both wallets share one operator seed.
157
+ // Each gets its own dWallet with different curve params.
158
+ //
159
+ // Phase 1 (current): throw with setup instructions
160
+ // Phase 2: actual DKG on Ika testnet
161
+
162
+ throw new Error(
163
+ "createDualCustody requires Ika network access. " +
164
+ "Shielded: Ed25519/EdDSA/SHA512 dWallet -> Orchard address. " +
165
+ "Bitcoin: secp256k1/ECDSA/DoubleSHA256 dWallet -> BTC address. " +
166
+ "npm install @ika.xyz/sdk @mysten/sui && configure Sui wallet. " +
167
+ "See https://docs.ika.xyz for DKG walkthrough."
168
+ );
169
+ }
170
+
171
+ /**
172
+ * Create a single dWallet for a specific chain.
173
+ */
174
+ export async function createWallet(
175
+ config: ZcashIkaConfig,
176
+ chain: Chain,
177
+ operatorSeed: Uint8Array
178
+ ): Promise<DWalletHandle> {
179
+ const params = CHAIN_PARAMS[chain];
180
+
181
+ // The DKG flow:
182
+ // 1. IkaClient.init({ network: config.network })
183
+ // 2. UserShareEncryptionKeys from operatorSeed
184
+ // 3. prepareDKG(curve, signatureAlgorithm, hash)
185
+ // 4. Submit DKG round 1 to Ika via IkaTransaction
186
+ // 5. prepareDKGSecondRound with network output
187
+ // 6. Submit round 2 -> get dWallet object ID + public key
188
+ // 7. Derive chain address from public key
189
+
190
+ throw new Error(
191
+ `createWallet(${chain}) requires Ika ${config.network} access. ` +
192
+ `Params: ${params.curve}/${params.algorithm}/${params.hash}. ` +
193
+ `${params.description}.`
194
+ );
195
+ }
196
+
197
+ /**
198
+ * Sign a message hash through Ika 2PC-MPC.
199
+ *
200
+ * The operator provides their seed, Ika provides the network share.
201
+ * Neither party ever sees the full private key.
202
+ *
203
+ * Flow:
204
+ * 1. Create presign session on Ika
205
+ * 2. Compute partial user signature locally
206
+ * 3. Submit to Ika coordinator
207
+ * 4. Poll for completion
208
+ * 5. Extract full signature from sign output
209
+ */
210
+ export async function sign(
211
+ config: ZcashIkaConfig,
212
+ operatorSeed: Uint8Array,
213
+ request: SignRequest
214
+ ): Promise<SignResult> {
215
+ const params = CHAIN_PARAMS[request.chain];
216
+
217
+ // The sign flow:
218
+ // 1. IkaClient.init({ network: config.network })
219
+ // 2. RequestGlobalPresign for the dWallet
220
+ // 3. createUserSignMessageWithCentralizedOutput(messageHash, userShare, ...)
221
+ // 4. ApproveMessage on Sui (this is where Move policy gates)
222
+ // 5. RequestSign -> poll SessionsManager for Completed status
223
+ // 6. parseSignatureFromSignOutput(signOutput)
224
+
225
+ throw new Error(
226
+ `sign requires active dWallet + Ika ${config.network}. ` +
227
+ `Chain: ${request.chain}, params: ${params.curve}/${params.algorithm}/${params.hash}.`
228
+ );
229
+ }
230
+
231
+ /**
232
+ * Set spending policy on the dWallet.
233
+ * Policy enforced at Sui Move contract level.
234
+ * The agent cannot bypass it - the contract holds the DWalletCap.
235
+ */
236
+ export async function setPolicy(
237
+ config: ZcashIkaConfig,
238
+ walletId: string,
239
+ policy: SpendPolicy
240
+ ): Promise<string> {
241
+ throw new Error(
242
+ "setPolicy requires a deployed Move module on Sui. " +
243
+ "The module gates approve_message() with spending constraints. " +
244
+ "See docs/move-policy-template.move for the template."
245
+ );
246
+ }
247
+
248
+ /**
249
+ * Spend from a shielded ZEC wallet.
250
+ *
251
+ * 1. Build Zcash Orchard transaction (zcash_primitives)
252
+ * 2. Extract sighash
253
+ * 3. Sign via Ika 2PC-MPC (Ed25519/EdDSA)
254
+ * 4. Attach signature to transaction
255
+ * 5. Broadcast via Zebra sendrawtransaction
256
+ * 6. Attest via ZAP1 as AGENT_ACTION
257
+ */
258
+ export async function spendShielded(
259
+ config: ZcashIkaConfig,
260
+ walletId: string,
261
+ operatorSeed: Uint8Array,
262
+ request: SpendRequest
263
+ ): Promise<SpendResult> {
264
+ throw new Error(
265
+ "spendShielded requires active Ed25519 dWallet + Zebra node. " +
266
+ "Integration in progress - need Ed25519 -> Orchard spending key derivation bridge."
267
+ );
268
+ }
269
+
270
+ /**
271
+ * Spend from a Bitcoin wallet.
272
+ *
273
+ * 1. Build Bitcoin transaction
274
+ * 2. Compute sighash (DoubleSHA256)
275
+ * 3. Sign via Ika 2PC-MPC (secp256k1/ECDSA)
276
+ * 4. Attach signature
277
+ * 5. Broadcast to Bitcoin network
278
+ * 6. Attest via ZAP1 as AGENT_ACTION
279
+ */
280
+ export async function spendBitcoin(
281
+ config: ZcashIkaConfig,
282
+ walletId: string,
283
+ operatorSeed: Uint8Array,
284
+ request: SpendRequest
285
+ ): Promise<SpendResult> {
286
+ throw new Error(
287
+ "spendBitcoin requires active secp256k1 dWallet + Bitcoin node. " +
288
+ "Same MPC flow as Zcash transparent - DoubleSHA256 sighash, ECDSA signature."
289
+ );
290
+ }
291
+
292
+ /**
293
+ * Verify the wallet's attestation history via ZAP1.
294
+ * Works today against the live API.
295
+ */
296
+ export async function getHistory(
297
+ config: ZcashIkaConfig,
298
+ walletId: string
299
+ ): Promise<{ leafHash: string; eventType: string; timestamp: string }[]> {
300
+ const resp = await fetch(`${config.zap1ApiUrl}/lifecycle/${walletId}`);
301
+ if (!resp.ok) return [];
302
+ const data = (await resp.json()) as {
303
+ leaves?: { leaf_hash: string; event_type: string; created_at: string }[];
304
+ };
305
+ return (data.leaves || []).map((l) => ({
306
+ leafHash: l.leaf_hash,
307
+ eventType: l.event_type,
308
+ timestamp: l.created_at,
309
+ }));
310
+ }
311
+
312
+ /**
313
+ * Check wallet's policy compliance status via ZAP1.
314
+ * Works today against the live API.
315
+ */
316
+ export async function checkCompliance(
317
+ config: ZcashIkaConfig,
318
+ walletId: string
319
+ ): Promise<{
320
+ compliant: boolean;
321
+ violations: number;
322
+ bondDeposits: number;
323
+ }> {
324
+ const resp = await fetch(
325
+ `${config.zap1ApiUrl}/agent/${walletId}/policy/verify`
326
+ );
327
+ if (!resp.ok) return { compliant: false, violations: -1, bondDeposits: 0 };
328
+ const data = (await resp.json()) as {
329
+ compliant: boolean;
330
+ violations: number;
331
+ bond_deposits?: number;
332
+ };
333
+ return {
334
+ compliant: data.compliant,
335
+ violations: data.violations,
336
+ bondDeposits: data.bond_deposits || 0,
337
+ };
338
+ }
@@ -0,0 +1,199 @@
1
+ // @ts-nocheck
2
+ /**
3
+ * Ika DKG test - create dWallets on testnet.
4
+ *
5
+ * Creates:
6
+ * 1. Ed25519 dWallet (Zcash Orchard shielded)
7
+ * 2. secp256k1 dWallet (Bitcoin + USDC + USDT + any EVM)
8
+ *
9
+ * One operator, split-key custody across all chains.
10
+ * Swiss bank in your pocket. Jailbroken but legal tender.
11
+ *
12
+ * Requires: SUI_PRIVATE_KEY env var (base64 Sui keypair)
13
+ * Get testnet SUI: https://faucet.sui.io
14
+ *
15
+ * Usage:
16
+ * SUI_PRIVATE_KEY=... node dist/test-dkg.js
17
+ */
18
+
19
+ import { CoreClient } from "@mysten/sui/client";
20
+ import { Ed25519Keypair } from "@mysten/sui/keypairs/ed25519";
21
+ import { Transaction } from "@mysten/sui/transactions";
22
+ import { decodeSuiPrivateKey } from "@mysten/sui/cryptography";
23
+ import {
24
+ IkaClient,
25
+ IkaTransaction,
26
+ UserShareEncryptionKeys,
27
+ getNetworkConfig,
28
+ Curve,
29
+ prepareDKGAsync,
30
+ publicKeyFromDWalletOutput,
31
+ createRandomSessionIdentifier,
32
+ CHAIN_PARAMS,
33
+ type Chain,
34
+ } from "./index.js";
35
+
36
+ const NETWORK = "testnet";
37
+
38
+ async function createDWallet(
39
+ ikaClient: IkaClient,
40
+ suiClient: CoreClient,
41
+ keypair: Ed25519Keypair,
42
+ encKeys: UserShareEncryptionKeys,
43
+ chain: Chain,
44
+ address: string,
45
+ ) {
46
+ const params = CHAIN_PARAMS[chain];
47
+ console.log(`\n--- ${params.description} ---`);
48
+ console.log(`Params: ${params.curve}/${params.algorithm}/${params.hash}`);
49
+
50
+ // Prepare DKG (async fetches protocol public params from network)
51
+ const bytesToHash = createRandomSessionIdentifier();
52
+ const dkgInput = await prepareDKGAsync(
53
+ ikaClient,
54
+ Curve[params.curve],
55
+ encKeys,
56
+ bytesToHash,
57
+ address,
58
+ );
59
+ console.log("DKG prepared locally");
60
+
61
+ // Build transaction
62
+ const tx = new Transaction();
63
+ const ikaTx = new IkaTransaction({
64
+ ikaClient,
65
+ transaction: tx,
66
+ userShareEncryptionKeys: encKeys,
67
+ });
68
+
69
+ const sessionId = ikaTx.registerSessionIdentifier(bytesToHash);
70
+
71
+ // Get network encryption key
72
+ const networkEncKey = await (ikaClient as any).getLatestNetworkEncryptionKey?.()
73
+ || await (ikaClient as any).getConfiguredNetworkEncryptionKey?.();
74
+
75
+ if (!networkEncKey) {
76
+ // Try without - some SDK versions auto-detect
77
+ console.log("No explicit encryption key method found, trying auto-detect...");
78
+ }
79
+
80
+ // Submit DKG request
81
+ const dkgResult = await (ikaTx as any).requestDWalletDKG({
82
+ dkgRequestInput: dkgInput,
83
+ sessionIdentifier: sessionId,
84
+ dwalletNetworkEncryptionKeyId: networkEncKey?.id,
85
+ curve: Curve[params.curve],
86
+ ikaCoin: tx.splitCoins(tx.gas, [50_000_000]),
87
+ suiCoin: tx.splitCoins(tx.gas, [50_000_000]),
88
+ });
89
+
90
+ console.log("Submitting DKG to Ika network...");
91
+ const result = await suiClient.signAndExecuteTransaction({
92
+ transaction: tx,
93
+ signer: keypair,
94
+ options: { showEffects: true, showEvents: true },
95
+ });
96
+ console.log("TX digest:", result.digest);
97
+
98
+ if (result.effects?.status?.status !== "success") {
99
+ console.error("TX failed:", result.effects?.status?.error);
100
+ return null;
101
+ }
102
+
103
+ console.log("DKG submitted. Poll for completion...");
104
+
105
+ // Extract dWallet ID from events/created objects
106
+ const created = result.effects?.created || [];
107
+ console.log(`Created ${created.length} objects`);
108
+ for (const obj of created) {
109
+ console.log(` ${(obj.reference as any)?.objectId || obj}`);
110
+ }
111
+
112
+ return result;
113
+ }
114
+
115
+ async function main() {
116
+ const privKeyRaw = process.env.SUI_PRIVATE_KEY;
117
+ if (!privKeyRaw) {
118
+ console.log("zcash-ika DKG test");
119
+ console.log("==================");
120
+ console.log("");
121
+ console.log("Chain params (what Ika signs for each chain):");
122
+ for (const [chain, params] of Object.entries(CHAIN_PARAMS)) {
123
+ console.log(` ${chain}: ${params.curve}/${params.algorithm}/${params.hash}`);
124
+ }
125
+ console.log("");
126
+ console.log("secp256k1/ECDSA/DoubleSHA256 signs for:");
127
+ console.log(" - Bitcoin (BTC)");
128
+ console.log(" - Zcash transparent (t-addr)");
129
+ console.log(" - Ethereum/Base (USDC, USDT) via KECCAK256 variant");
130
+ console.log("");
131
+ console.log("Ed25519/EdDSA/SHA512 signs for:");
132
+ console.log(" - Zcash Orchard (shielded ZEC)");
133
+ console.log("");
134
+ console.log("One operator. Split-key custody. Every chain.");
135
+ console.log("");
136
+ console.log("To run DKG:");
137
+ console.log(" SUI_PRIVATE_KEY=suiprivkey1... node dist/test-dkg.js");
138
+ console.log(" Get testnet SUI: https://faucet.sui.io");
139
+ return;
140
+ }
141
+
142
+ // Decode Sui keypair
143
+ const decoded = decodeSuiPrivateKey(privKeyRaw);
144
+ const keypair = Ed25519Keypair.fromSecretKey(decoded.secretKey);
145
+ const address = keypair.getPublicKey().toSuiAddress();
146
+ console.log(`Sui address: ${address}`);
147
+
148
+ // Init clients
149
+ // PublicNode doesn't rate limit like Mysten's public RPC
150
+ const { SuiJsonRpcClient } = await import("@mysten/sui/jsonRpc");
151
+ const suiClient = new SuiJsonRpcClient({ url: "https://sui-testnet-rpc.publicnode.com" });
152
+ const ikaConfig = getNetworkConfig(NETWORK);
153
+ if (!ikaConfig) throw new Error("No Ika testnet config");
154
+
155
+ const ikaClient = new IkaClient({ suiClient, config: ikaConfig });
156
+ await ikaClient.initialize();
157
+ console.log("Ika client initialized on testnet");
158
+
159
+ // Check balance
160
+ const balance = await suiClient.getBalance({ owner: address });
161
+ const suiBalance = Number(balance.totalBalance) / 1e9;
162
+ console.log(`SUI balance: ${suiBalance} SUI`);
163
+
164
+ if (suiBalance < 0.2) {
165
+ console.log("Need at least 0.2 SUI for gas (two DKG operations).");
166
+ console.log("Get testnet SUI: https://faucet.sui.io");
167
+ return;
168
+ }
169
+
170
+ // Generate encryption keys - one per curve
171
+ const seed = new Uint8Array(32);
172
+ crypto.getRandomValues(seed);
173
+
174
+ // Ed25519 encryption keys for shielded ZEC wallet
175
+ const edEncKeys = await UserShareEncryptionKeys.fromRootSeedKey(seed, Curve.ED25519);
176
+ console.log("Ed25519 encryption keys generated");
177
+
178
+ // secp256k1 encryption keys for BTC/stablecoin wallet
179
+ const secpEncKeys = await UserShareEncryptionKeys.fromRootSeedKey(seed, Curve.SECP256K1);
180
+ console.log("secp256k1 encryption keys generated");
181
+
182
+ // Create Ed25519 dWallet (Zcash Orchard)
183
+ try {
184
+ await createDWallet(ikaClient, suiClient, keypair, edEncKeys, "zcash-shielded", address);
185
+ } catch (err) {
186
+ console.error("Ed25519 DKG error:", (err as Error).message);
187
+ }
188
+
189
+ // Create secp256k1 dWallet (Bitcoin + stablecoins)
190
+ try {
191
+ await createDWallet(ikaClient, suiClient, keypair, secpEncKeys, "bitcoin", address);
192
+ } catch (err) {
193
+ console.error("secp256k1 DKG error:", (err as Error).message);
194
+ }
195
+
196
+ console.log("\nDone.");
197
+ }
198
+
199
+ main().catch(console.error);
package/tsconfig.json ADDED
@@ -0,0 +1,13 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "NodeNext",
5
+ "moduleResolution": "NodeNext",
6
+ "outDir": "dist",
7
+ "declaration": true,
8
+ "strict": true,
9
+ "esModuleInterop": true,
10
+ "skipLibCheck": true
11
+ },
12
+ "include": ["src"]
13
+ }