@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 +154 -0
- package/dist/index.d.ts +186 -0
- package/dist/index.js +168 -0
- package/dist/test-dkg.d.ts +17 -0
- package/dist/test-dkg.js +150 -0
- package/package.json +20 -0
- package/src/index.ts +338 -0
- package/src/test-dkg.ts +199 -0
- package/tsconfig.json +13 -0
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
|
+
[](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
|
package/dist/index.d.ts
ADDED
|
@@ -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 {};
|
package/dist/test-dkg.js
ADDED
|
@@ -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
|
+
}
|
package/src/test-dkg.ts
ADDED
|
@@ -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