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