npm package: @hula-privacy/mixer
Description: Hula Privacy Protocol SDK - Complete toolkit for private transactions on Solana
Latest version: 2.0.0

# Hula Privacy SDK

Complete toolkit for privacy transactions on Solana using ZK proofs and UTXOs.

## Installation

```bash
npm install @hula-privacy/mixer
# or
bun add @hula-privacy/mixer
```

## Quick Start

```typescript
import { HulaWallet, initHulaSDK, getKeyDerivationMessage } from '@hula-privacy/mixer';

// Initialize SDK (loads Poseidon hasher)
await initHulaSDK();

// Create wallet from Phantom signature (deterministic derivation)
const message = getKeyDerivationMessage();
const signature = await wallet.signMessage(message);
const hulaWallet = await HulaWallet.fromSignature(signature, {
  rpcUrl: 'https://api.devnet.solana.com',
  relayerUrl: 'http://localhost:3001',
});

// Or create a new wallet with random keys
const newWallet = await HulaWallet.create({
  rpcUrl: 'https://api.devnet.solana.com',
  relayerUrl: 'http://localhost:3001',
});

// Get wallet public identifier
console.log('Owner hash:', hulaWallet.ownerHex);
console.log('Encryption pubkey:', Buffer.from(hulaWallet.encryptionPublicKey).toString('hex'));
```

## Syncing UTXOs

The wallet syncs with the relayer to discover your UTXOs from encrypted notes:

```typescript
// Sync wallet to find your UTXOs
const result = await hulaWallet.sync((progress) => {
  console.log(`Syncing: ${progress.stage} ${progress.current}/${progress.total}`);
});

console.log(`Found ${result.newUtxos} new UTXOs`);
console.log(`Marked ${result.spentUtxos} UTXOs as spent`);

// Check balance
const balance = hulaWallet.getBalance(mintAddress);
console.log('Private balance:', balance);
```

## Transactions

### Deposit (Public → Private)

```typescript
const { transaction } = await hulaWallet.deposit(mintAddress, 1_000_000n);

// Submit transaction using your preferred method
// The transaction object contains proof, public inputs, etc.
```

### Transfer (Private → Private)

```typescript
// Get recipient's public info
const recipientOwner = BigInt('0x...');  // Recipient's owner hash
const recipientEncPubKey = new Uint8Array([...]); // Recipient's encryption pubkey

const { transaction } = await hulaWallet.transfer(
  mintAddress,
  500_000n,
  recipientOwner,
  recipientEncPubKey
);
```

### Withdraw (Private → Public)

```typescript
const recipientPubkey = new PublicKey('...');

const { transaction } = await hulaWallet.withdraw(
  mintAddress,
  200_000n,
  recipientPubkey
);
```

## Low-Level API

For more control, use the low-level functions directly:

### Key Derivation

```typescript
import { generateSpendingKey, deriveKeys, initPoseidon } from '@hula-privacy/mixer';

await initPoseidon();

const spendingKey = generateSpendingKey();
const keys = deriveKeys(spendingKey);

console.log('Owner:', keys.owner.toString(16));
console.log('Viewing key:', keys.viewingKey.toString(16));
console.log('Encryption pubkey:', Buffer.from(keys.encryptionKeyPair.publicKey).toString('hex'));
```

### UTXO Management

```typescript
import { createUTXO, computeCommitment, computeNullifier } from '@hula-privacy/mixer';

// Create a UTXO
const utxo = createUTXO(
  1_000_000n,        // value
  mintBigInt,        // mint address as bigint
  keys.owner,        // owner hash
  0,                 // leaf index
  0                  // tree index
);

// Compute commitment manually
const commitment = computeCommitment(
  utxo.value,
  utxo.mintTokenAddress,
  utxo.owner,
  utxo.secret
);

// Compute nullifier for spending
const nullifier = computeNullifier(
  keys.spendingKey,
  utxo.commitment,
  utxo.leafIndex
);
```

### Merkle Tree Operations

```typescript
import { 
  fetchMerklePath, 
  fetchMerkleRoot,
  computeMerklePathFromLeaves,
  getCurrentTreeIndex 
} from '@hula-privacy/mixer';

// Get current tree index
const treeIndex = await getCurrentTreeIndex('http://localhost:3001');

// Fetch merkle root
const root = await fetchMerkleRoot(treeIndex);

// Fetch merkle path for a specific leaf
const path = await fetchMerklePath(treeIndex, leafIndex);

// Or compute locally from leaves
const leaves = [commitment1, commitment2, ...];
const localPath = computeMerklePathFromLeaves(leafIndex, leaves);
```

### Relayer API

```typescript
import { RelayerClient, getRelayerClient } from '@hula-privacy/mixer';

const client = getRelayerClient('http://localhost:3001');

// Get pool state
const pool = await client.getPool();
console.log('Current tree:', pool.currentTreeIndex);
console.log('Total commitments:', pool.commitmentCount);

// Get leaves for a tree
const leaves = await client.getAllLeavesForTree(0);

// Check if nullifier is spent
const { spent } = await client.checkNullifier(nullifierHex);

// Get encrypted notes
const notes = await client.getAllNotes();
```

### Proof Generation

```typescript
import { generateProof, setCircuitPaths } from '@hula-privacy/mixer';

// Set circuit paths (if not in default locations)
setCircuitPaths(
  '/path/to/transaction.wasm',
  '/path/to/transaction_final.zkey'
);

// Generate proof
const { proof, publicSignals } = await generateProof(circuitInputs);
```

## Configuration

### Circuit Files

The SDK looks for circuit files in these locations:

1. `./circuits/build/transaction_js/transaction.wasm`
2. `./circuits/build/keys/transaction_final.zkey`
3. `./assets/transaction.wasm` and `./assets/transaction_final.zkey`

You can also set custom paths:

```typescript
import { setCircuitPaths } from '@hula-privacy/mixer';

setCircuitPaths('/custom/path/transaction.wasm', '/custom/path/transaction.zkey');
```

### Default Relayer URL

```typescript
import { setDefaultRelayerUrl } from '@hula-privacy/mixer';

setDefaultRelayerUrl('https://relayer.hulaprivacy.io');
```

## Types

All types are exported for TypeScript users:

```typescript
import type {
  UTXO,
  SerializableUTXO,
  WalletKeys,
  EncryptedNote,
  MerklePath,
  CircuitInputs,
  TransactionRequest,
  BuiltTransaction,
  HulaSDKConfig,
} from '@hula-privacy/mixer';
```

## Building

```bash
# Install dependencies
bun install

# Build
bun run build

# Type check
bun run typecheck
```

## Architecture

```
sdk/src/
├── index.ts        # Main exports
├── types.ts        # Type definitions
├── constants.ts    # Program IDs, seeds, circuit params
├── api.ts          # Relayer API client
├── crypto.ts       # Poseidon, key derivation, encryption
├── merkle.ts       # Merkle tree operations
├── utxo.ts         # UTXO management
├── proof.ts        # ZK proof generation
├── transaction.ts  # Transaction building
└── wallet.ts       # High-level wallet abstraction
```

## Security Considerations

1. **Spending Key**: The spending key is the master secret. Never share it or store it insecurely.
2. **Wallet Recovery**: Using `fromSignature()` allows deterministic recovery from a Solana wallet signature.
3. **Local Storage**: When storing UTXOs locally, use appropriate encryption.
4. **Note Encryption**: Encrypted notes allow recipients to discover UTXOs sent to them.

## License

MIT