diff --git a/docs/api/CONTRACT_ABI.md b/docs/api/CONTRACT_ABI.md
new file mode 100644
index 0000000..f529249
--- /dev/null
+++ b/docs/api/CONTRACT_ABI.md
@@ -0,0 +1,613 @@
+# PrivacyLayer Contract ABI Documentation
+
+> **Contract**: `privacy_pool`
+> **Network**: Stellar Soroban
+> **Language**: Rust (Soroban SDK)
+> **Version**: 1.0.0
+
+---
+
+## Overview
+
+The PrivacyPool contract implements a ZK-proof shielded pool on Stellar Soroban. It allows users to deposit tokens (XLM/USDC) and withdraw them privately using zero-knowledge proofs.
+
+### Contract Address
+
+| Network | Contract ID |
+|---------|-------------|
+| Testnet | `TBD` |
+| Mainnet | `TBD` |
+
+---
+
+## Data Types
+
+### Primitive Types
+
+| Type | Description | Size |
+|------|-------------|------|
+| `Address` | Stellar account or contract address | 32 bytes |
+| `BytesN<32>` | 32-byte array (commitments, roots, hashes) | 32 bytes |
+| `u32` | Unsigned 32-bit integer | 4 bytes |
+| `bool` | Boolean value | 1 byte |
+
+### Composite Types
+
+#### `Denomination`
+
+```rust
+pub enum Denomination {
+ XLM(i128), // Amount in stroops (1 XLM = 10,000,000 stroops)
+ USDC(i128), // Amount in micro-units
+}
+```
+
+#### `VerifyingKey`
+
+Groth16 verifying key for BN254 curve.
+
+```rust
+pub struct VerifyingKey {
+ pub alpha_g1: (i128, i128),
+ pub beta_g2: ((i128, i128), (i128, i128)),
+ pub gamma_g2: ((i128, i128), (i128, i128)),
+ pub delta_g2: ((i128, i128), (i128, i128)),
+ pub ic: Vec<(i128, i128)>,
+}
+```
+
+#### `Proof`
+
+Groth16 zero-knowledge proof.
+
+```rust
+pub struct Proof {
+ pub a: (i128, i128), // G1 point
+ pub b: ((i128, i128), (i128, i128)), // G2 point
+ pub c: (i128, i128), // G1 point
+}
+```
+
+#### `PublicInputs`
+
+Public inputs for withdrawal verification.
+
+```rust
+pub struct PublicInputs {
+ pub root: BytesN<32>, // Merkle root
+ pub nullifier_hash: BytesN<32>, // Nullifier hash
+ pub recipient: Address, // Withdrawal recipient
+ pub relayer: Option
, // Optional relayer
+ pub fee: Option, // Optional relayer fee
+}
+```
+
+#### `PoolConfig`
+
+Contract configuration state.
+
+```rust
+pub struct PoolConfig {
+ pub admin: Address,
+ pub token: Address,
+ pub denomination: Denomination,
+ pub paused: bool,
+ pub initialized: bool,
+}
+```
+
+---
+
+## Contract Functions
+
+### Initialization
+
+#### `initialize`
+
+Initialize the privacy pool. Must be called once after deployment.
+
+```rust
+fn initialize(
+ env: Env,
+ admin: Address,
+ token: Address,
+ denomination: Denomination,
+ vk: VerifyingKey,
+) -> Result<(), Error>
+```
+
+**Parameters**:
+
+| Name | Type | Description |
+|------|------|-------------|
+| `admin` | `Address` | Admin address for privileged operations |
+| `token` | `Address` | Token contract address (XLM or USDC) |
+| `denomination` | `Denomination` | Fixed deposit amount |
+| `vk` | `VerifyingKey` | Groth16 verifying key |
+
+**Returns**: `Result<(), Error>`
+
+**Errors**:
+- `AlreadyInitialized`: Pool already initialized
+- `InvalidToken`: Invalid token address
+- `InvalidVerifyingKey`: Malformed verifying key
+
+**Authorization**: Requires signature from deployer.
+
+**Example**:
+
+```javascript
+await contract.initialize({
+ admin: "GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWHF",
+ token: "CCHEGGH7VWDPOHCQFDKH2TJ5TTKYQ4FW8VBA5EOFWYPG5CLIBVH2GLI5",
+ denomination: { tag: "USDC", value: 1000000000n },
+ vk: { /* verifying key */ }
+});
+```
+
+---
+
+### Core Operations
+
+#### `deposit`
+
+Deposit tokens into the shielded pool.
+
+```rust
+fn deposit(
+ env: Env,
+ from: Address,
+ commitment: BytesN<32>,
+) -> Result<(u32, BytesN<32>), Error>
+```
+
+**Parameters**:
+
+| Name | Type | Description |
+|------|------|-------------|
+| `from` | `Address` | Depositor address |
+| `commitment` | `BytesN<32>` | Poseidon(nullifier ∥ secret) hash |
+
+**Returns**: `Result<(u32, BytesN<32>), Error>`
+- `u32`: Leaf index in Merkle tree
+- `BytesN<32>`: New Merkle root
+
+**Errors**:
+- `NotInitialized`: Pool not initialized
+- `PoolPaused`: Pool is currently paused
+- `ZeroCommitment`: Cannot deposit zero commitment
+- `InsufficientBalance`: Depositor has insufficient tokens
+
+**Authorization**: Requires signature from `from` address.
+
+**Events Emitted**: `DepositEvent`
+
+**Example**:
+
+```javascript
+// Generate note
+const nullifier = randomBytes(31);
+const secret = randomBytes(31);
+const commitment = poseidon2Hash(nullifier, secret);
+
+// Deposit
+const [leafIndex, root] = await contract.deposit({
+ from: wallet.address,
+ commitment: commitment,
+});
+
+// Store note securely
+const note = { nullifier, secret, leafIndex };
+saveNote(note);
+```
+
+---
+
+#### `withdraw`
+
+Withdraw tokens from the shielded pool using a ZK proof.
+
+```rust
+fn withdraw(
+ env: Env,
+ proof: Proof,
+ pub_inputs: PublicInputs,
+) -> Result
+```
+
+**Parameters**:
+
+| Name | Type | Description |
+|------|------|-------------|
+| `proof` | `Proof` | Groth16 ZK proof |
+| `pub_inputs` | `PublicInputs` | Public inputs (root, nullifier, recipient) |
+
+**Returns**: `Result`
+
+**Errors**:
+- `NotInitialized`: Pool not initialized
+- `PoolPaused`: Pool is currently paused
+- `InvalidProof`: ZK proof verification failed
+- `NullifierSpent`: Nullifier already used
+- `UnknownRoot`: Merkle root not in history
+
+**Authorization**: None (proof-based authentication)
+
+**Events Emitted**: `WithdrawEvent`
+
+**Example**:
+
+```javascript
+// 1. Sync Merkle tree
+const leaves = await fetchLeaves();
+const tree = buildMerkleTree(leaves);
+
+// 2. Generate Merkle proof
+const merkleProof = tree.getProof(note.leafIndex);
+
+// 3. Generate ZK proof
+const proof = await generateZKProof({
+ nullifier: note.nullifier,
+ secret: note.secret,
+ merkleProof: merkleProof,
+ root: tree.root(),
+ recipient: recipientAddress,
+});
+
+// 4. Submit withdrawal
+const success = await contract.withdraw({
+ proof: proof.proof,
+ pub_inputs: {
+ root: proof.root,
+ nullifier_hash: proof.nullifierHash,
+ recipient: recipientAddress,
+ },
+});
+```
+
+---
+
+### View Functions
+
+#### `get_root`
+
+Get the current Merkle root.
+
+```rust
+fn get_root(env: Env) -> Result, Error>
+```
+
+**Returns**: `Result, Error>` - Current Merkle root
+
+**Errors**:
+- `NotInitialized`: Pool not initialized
+
+---
+
+#### `deposit_count`
+
+Get the total number of deposits.
+
+```rust
+fn deposit_count(env: Env) -> u32
+```
+
+**Returns**: `u32` - Number of deposits
+
+---
+
+#### `is_known_root`
+
+Check if a root exists in the historical buffer.
+
+```rust
+fn is_known_root(env: Env, root: BytesN<32>) -> bool
+```
+
+**Parameters**:
+
+| Name | Type | Description |
+|------|------|-------------|
+| `root` | `BytesN<32>` | Merkle root to check |
+
+**Returns**: `bool` - True if root is in history
+
+---
+
+#### `is_spent`
+
+Check if a nullifier has been spent.
+
+```rust
+fn is_spent(env: Env, nullifier_hash: BytesN<32>) -> bool
+```
+
+**Parameters**:
+
+| Name | Type | Description |
+|------|------|-------------|
+| `nullifier_hash` | `BytesN<32>` | Nullifier hash to check |
+
+**Returns**: `bool` - True if spent
+
+---
+
+#### `get_config`
+
+Get pool configuration.
+
+```rust
+fn get_config(env: Env) -> Result
+```
+
+**Returns**: `Result`
+
+---
+
+### Admin Functions
+
+#### `pause`
+
+Pause all pool operations.
+
+```rust
+fn pause(env: Env, admin: Address) -> Result<(), Error>
+```
+
+**Authorization**: Requires admin signature
+
+**Errors**:
+- `NotInitialized`: Pool not initialized
+- `Unauthorized`: Caller is not admin
+- `AlreadyPaused`: Pool already paused
+
+---
+
+#### `unpause`
+
+Resume pool operations.
+
+```rust
+fn unpause(env: Env, admin: Address) -> Result<(), Error>
+```
+
+**Authorization**: Requires admin signature
+
+**Errors**:
+- `NotInitialized`: Pool not initialized
+- `Unauthorized`: Caller is not admin
+- `NotPaused`: Pool not paused
+
+---
+
+#### `set_verifying_key`
+
+Update the Groth16 verifying key.
+
+```rust
+fn set_verifying_key(
+ env: Env,
+ admin: Address,
+ new_vk: VerifyingKey,
+) -> Result<(), Error>
+```
+
+**Authorization**: Requires admin signature
+
+**Errors**:
+- `NotInitialized`: Pool not initialized
+- `Unauthorized`: Caller is not admin
+- `InvalidVerifyingKey`: Malformed verifying key
+
+---
+
+## Events
+
+### `DepositEvent`
+
+Emitted on successful deposit.
+
+```rust
+pub struct DepositEvent {
+ pub from: Address,
+ pub commitment: BytesN<32>,
+ pub leaf_index: u32,
+ pub root: BytesN<32>,
+ pub timestamp: u64,
+}
+```
+
+### `WithdrawEvent`
+
+Emitted on successful withdrawal.
+
+```rust
+pub struct WithdrawEvent {
+ pub nullifier_hash: BytesN<32>,
+ pub recipient: Address,
+ pub relayer: Option,
+ pub fee: Option,
+ pub timestamp: u64,
+}
+```
+
+### `PauseEvent`
+
+Emitted when pool is paused/unpaused.
+
+```rust
+pub struct PauseEvent {
+ pub paused: bool,
+ pub admin: Address,
+ pub timestamp: u64,
+}
+```
+
+### `VerifyingKeyUpdatedEvent`
+
+Emitted when verifying key is updated.
+
+```rust
+pub struct VerifyingKeyUpdatedEvent {
+ pub admin: Address,
+ pub timestamp: u64,
+}
+```
+
+---
+
+## Error Codes
+
+| Code | Name | Description |
+|------|------|-------------|
+| 1 | `NotInitialized` | Pool not initialized |
+| 2 | `AlreadyInitialized` | Pool already initialized |
+| 3 | `PoolPaused` | Pool is paused |
+| 4 | `ZeroCommitment` | Cannot deposit zero commitment |
+| 5 | `InsufficientBalance` | Insufficient token balance |
+| 6 | `InvalidProof` | ZK proof verification failed |
+| 7 | `NullifierSpent` | Nullifier already used |
+| 8 | `UnknownRoot` | Merkle root not in history |
+| 9 | `Unauthorized` | Caller not authorized |
+| 10 | `InvalidToken` | Invalid token address |
+| 11 | `InvalidVerifyingKey` | Malformed verifying key |
+| 12 | `AlreadyPaused` | Pool already paused |
+| 13 | `NotPaused` | Pool not paused |
+
+---
+
+## Gas Estimates
+
+| Function | Estimated Gas |
+|----------|---------------|
+| `initialize` | ~50,000 |
+| `deposit` | ~80,000 |
+| `withdraw` | ~150,000 |
+| `get_root` | ~5,000 |
+| `deposit_count` | ~3,000 |
+| `is_known_root` | ~5,000 |
+| `is_spent` | ~5,000 |
+| `get_config` | ~5,000 |
+| `pause/unpause` | ~10,000 |
+| `set_verifying_key` | ~20,000 |
+
+---
+
+## Security Considerations
+
+### Zero-Knowledge Proofs
+
+- All withdrawals require valid Groth16 proofs
+- Proof verification uses native BN254 host functions
+- Circuit must be audited before mainnet deployment
+
+### Nullifier Protection
+
+- Each note can only be withdrawn once
+- Nullifiers are stored on-chain to prevent double-spending
+- Use strong randomness for nullifier generation
+
+### Merkle Tree
+
+- Tree depth: 20 levels (~1M leaves)
+- Historical roots: 100 most recent roots
+- Root eviction after overflow (warn users to sync)
+
+### Admin Controls
+
+- Admin can pause/unpause the pool
+- Admin can update the verifying key
+- Consider multi-sig for admin role
+
+---
+
+## Integration Guide
+
+### TypeScript/JavaScript
+
+```javascript
+import { PrivacyPool } from '@privacylayer/sdk';
+
+// Initialize client
+const pool = new PrivacyPool({
+ network: 'testnet',
+ contractId: 'CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP3B',
+});
+
+// Connect wallet
+await pool.connect(wallet);
+
+// Deposit
+const note = await pool.deposit({
+ denomination: 'USDC',
+ amount: 100n,
+});
+
+// Withdraw
+await pool.withdraw({
+ note: note,
+ recipient: 'GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWHF',
+});
+```
+
+### Python
+
+```python
+from privacylayer import PrivacyPool
+
+# Initialize client
+pool = PrivacyPool(
+ network='testnet',
+ contract_id='CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP3B'
+)
+
+# Deposit
+note = pool.deposit(
+ denomination='USDC',
+ amount=100
+)
+
+# Withdraw
+pool.withdraw(
+ note=note,
+ recipient='GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWHF'
+)
+```
+
+### Rust
+
+```rust
+use privacy_pool::PrivacyPool;
+
+// Initialize client
+let pool = PrivacyPool::new(
+ Network::Testnet,
+ "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP3B"
+);
+
+// Deposit
+let note = pool.deposit(
+ Denomination::USDC(100),
+ &wallet
+).await?;
+
+// Withdraw
+pool.withdraw(
+ ¬e,
+ &recipient_address
+).await?;
+```
+
+---
+
+## References
+
+- [Soroban SDK Documentation](https://docs.rs/soroban-sdk)
+- [Stellar Protocol 25 - BN254](https://github.com/stellar/stellar-protocol/blob/master/core/cap-0074.md)
+- [Stellar Protocol 25 - Poseidon](https://github.com/stellar/stellar-protocol/blob/master/core/cap-0075.md)
+- [Noir Language](https://noir-lang.org)
+- [Groth16 Paper](https://eprint.iacr.org/2016/260.pdf)
+
+---
+
+*Last Updated: 2026-03-25*
\ No newline at end of file
diff --git a/docs/api/examples/python.py b/docs/api/examples/python.py
new file mode 100644
index 0000000..de7fc08
--- /dev/null
+++ b/docs/api/examples/python.py
@@ -0,0 +1,452 @@
+"""
+PrivacyLayer Python SDK Examples
+
+Install: pip install privacylayer-sdk stellar-sdk pycryptodome
+"""
+
+import asyncio
+import json
+from typing import Optional
+from dataclasses import dataclass
+
+from stellar_sdk import (
+ SorobanServer,
+ Keypair,
+ TransactionBuilder,
+ Networks,
+ xdr,
+ Address,
+)
+from Crypto.Random import get_random_bytes
+
+# ─────────────────────────────────────────────────────────────────
+# Configuration
+# ─────────────────────────────────────────────────────────────────
+
+CONFIG = {
+ "testnet": {
+ "rpc_url": "https://soroban-testnet.stellar.org:443",
+ "network_passphrase": Networks.TESTNET,
+ "contract_id": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP3B",
+ },
+ "mainnet": {
+ "rpc_url": "https://soroban-mainnet.stellar.org:443",
+ "network_passphrase": Networks.PUBLIC,
+ "contract_id": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP3B",
+ },
+}
+
+ENV = "testnet"
+config = CONFIG[ENV]
+
+
+# ─────────────────────────────────────────────────────────────────
+# Data Classes
+# ─────────────────────────────────────────────────────────────────
+
+@dataclass
+class Note:
+ """Deposit note - KEEP SECRET!"""
+ nullifier: str
+ secret: str
+ leaf_index: int
+ commitment: str
+ denomination: str
+ amount: int
+ network: str
+ created_at: int
+ spent: bool = False
+
+
+@dataclass
+class PoolConfig:
+ """Pool configuration"""
+ admin: str
+ token: str
+ denomination_tag: str
+ denomination_value: int
+ paused: bool
+ initialized: bool
+
+
+# ─────────────────────────────────────────────────────────────────
+# Client
+# ─────────────────────────────────────────────────────────────────
+
+class PrivacyPoolClient:
+ """PrivacyLayer Python Client"""
+
+ def __init__(self, secret_key: Optional[str] = None):
+ self.server = SorobanServer(config["rpc_url"])
+ self.contract_id = config["contract_id"]
+ self.keypair = Keypair.from_secret(secret_key) if secret_key else None
+
+ # ──────────────────────────────────────────────────────────────
+ # Initialization
+ # ──────────────────────────────────────────────────────────────
+
+ async def initialize_pool(
+ self,
+ admin: str,
+ token: str,
+ denomination_tag: str,
+ denomination_value: int,
+ verifying_key: dict,
+ ) -> dict:
+ """Initialize the privacy pool (admin only)"""
+
+ account = await self.server.load_account(self.keypair.public_key)
+
+ # Build denomination SCVal
+ denom_scval = xdr.ScVal(
+ type=xdr.ScValType.SCV_MAP,
+ map=xdr.ScMap(
+ [
+ xdr.ScMapEntry(
+ key=xdr.ScVal(type=xdr.ScValType.SCV_SYMBOL, sym="tag"),
+ val=xdr.ScVal(type=xdr.ScValType.SCV_SYMBOL, sym=denomination_tag),
+ ),
+ xdr.ScMapEntry(
+ key=xdr.ScVal(type=xdr.ScValType.SCV_SYMBOL, sym="value"),
+ val=xdr.ScVal(
+ type=xdr.ScValType.SCV_INT128,
+ i128=xdr.Int128Parts(lo=denomination_value, hi=0),
+ ),
+ ),
+ ]
+ ),
+ )
+
+ tx = (
+ TransactionBuilder(
+ account,
+ config["network_passphrase"],
+ base_fee=100,
+ )
+ .add_operation(
+ xdr.Operation(
+ body=xdr.OperationBody.INVOKE_HOST_FUNCTION,
+ invoke_host_function=xdr.InvokeHostFunctionOp(
+ host_function=xdr.HostFunction(
+ type=xdr.HostFunctionType.HOST_FUNCTION_TYPE_INVOKE_CONTRACT,
+ invoke_contract=xdr.InvokeContractArgs(
+ contract_address=Address(self.contract_id).to_xdr_sc_address(),
+ function_name="initialize",
+ args=[
+ Address(admin).to_scval(),
+ Address(token).to_scval(),
+ denom_scval,
+ self._verifying_key_to_scval(verifying_key),
+ ],
+ ),
+ ),
+ ),
+ )
+ )
+ .set_timeout(30)
+ .build()
+ )
+
+ tx.sign(self.keypair)
+ result = await self.server.send_transaction(tx)
+ return result
+
+ # ──────────────────────────────────────────────────────────────
+ # Deposit
+ # ──────────────────────────────────────────────────────────────
+
+ async def deposit(self) -> Note:
+ """Deposit into the shielded pool"""
+
+ # 1. Generate note
+ nullifier = get_random_bytes(31)
+ secret = get_random_bytes(31)
+
+ # 2. Compute commitment
+ commitment = await self._poseidon2_hash(nullifier, secret)
+
+ # 3. Build transaction
+ account = await self.server.load_account(self.keypair.public_key)
+
+ tx = (
+ TransactionBuilder(
+ account,
+ config["network_passphrase"],
+ base_fee=100,
+ )
+ .add_operation(
+ xdr.Operation(
+ body=xdr.OperationBody.INVOKE_HOST_FUNCTION,
+ invoke_host_function=xdr.InvokeHostFunctionOp(
+ host_function=xdr.HostFunction(
+ type=xdr.HostFunctionType.HOST_FUNCTION_TYPE_INVOKE_CONTRACT,
+ invoke_contract=xdr.InvokeContractArgs(
+ contract_address=Address(self.contract_id).to_xdr_sc_address(),
+ function_name="deposit",
+ args=[
+ Address(self.keypair.public_key).to_scval(),
+ self._bytes32_to_scval(commitment),
+ ],
+ ),
+ ),
+ ),
+ )
+ )
+ .set_timeout(30)
+ .build()
+ )
+
+ tx.sign(self.keypair)
+ result = await self.server.send_transaction(tx)
+
+ # 4. Extract leaf index
+ leaf_index = self._parse_leaf_index(result)
+
+ # 5. Create and store note
+ note = Note(
+ nullifier=nullifier.hex(),
+ secret=secret.hex(),
+ leaf_index=leaf_index,
+ commitment=commitment.hex(),
+ denomination="USDC",
+ amount=100,
+ network=ENV,
+ created_at=int(asyncio.get_event_loop().time()),
+ )
+
+ self._save_note(note)
+
+ print(f"Deposit successful! Leaf index: {leaf_index}")
+ print("Note (KEEP SECRET):", note)
+
+ return note
+
+ # ──────────────────────────────────────────────────────────────
+ # Withdraw
+ # ──────────────────────────────────────────────────────────────
+
+ async def withdraw(self, note: Note, recipient: str) -> dict:
+ """Withdraw from the shielded pool"""
+
+ # 1. Sync Merkle tree
+ leaves = await self._fetch_all_leaves()
+ tree = self._build_merkle_tree(leaves)
+
+ # 2. Get Merkle proof
+ merkle_proof = tree.get_proof(note.leaf_index)
+ root = tree.root()
+
+ # 3. Generate ZK proof
+ zk_proof = await self._generate_zk_proof(
+ nullifier=bytes.fromhex(note.nullifier),
+ secret=bytes.fromhex(note.secret),
+ merkle_proof=merkle_proof,
+ root=root,
+ recipient=recipient,
+ )
+
+ # 4. Build transaction
+ account = await self.server.load_account(self.keypair.public_key)
+
+ tx = (
+ TransactionBuilder(
+ account,
+ config["network_passphrase"],
+ base_fee=200,
+ )
+ .add_operation(
+ xdr.Operation(
+ body=xdr.OperationBody.INVOKE_HOST_FUNCTION,
+ invoke_host_function=xdr.InvokeHostFunctionOp(
+ host_function=xdr.HostFunction(
+ type=xdr.HostFunctionType.HOST_FUNCTION_TYPE_INVOKE_CONTRACT,
+ invoke_contract=xdr.InvokeContractArgs(
+ contract_address=Address(self.contract_id).to_xdr_sc_address(),
+ function_name="withdraw",
+ args=[
+ self._proof_to_scval(zk_proof.proof),
+ self._public_inputs_to_scval({
+ "root": root,
+ "nullifier_hash": zk_proof.nullifier_hash,
+ "recipient": recipient,
+ }),
+ ],
+ ),
+ ),
+ ),
+ )
+ )
+ .set_timeout(30)
+ .build()
+ )
+
+ tx.sign(self.keypair)
+ result = await self.server.send_transaction(tx)
+
+ # 5. Mark note as spent
+ note.spent = True
+ self._update_note(note)
+
+ print(f"Withdrawal successful! Recipient: {recipient}")
+ return result
+
+ # ──────────────────────────────────────────────────────────────
+ # View Functions
+ # ──────────────────────────────────────────────────────────────
+
+ async def get_root(self) -> str:
+ """Get current Merkle root"""
+ result = await self.server.simulate_transaction(
+ self._build_view_call("get_root", [])
+ )
+ return self._parse_bytes32(result)
+
+ async def get_deposit_count(self) -> int:
+ """Get total deposit count"""
+ result = await self.server.simulate_transaction(
+ self._build_view_call("deposit_count", [])
+ )
+ return self._parse_u32(result)
+
+ async def get_config(self) -> PoolConfig:
+ """Get pool configuration"""
+ result = await self.server.simulate_transaction(
+ self._build_view_call("get_config", [])
+ )
+ return self._parse_pool_config(result)
+
+ async def is_known_root(self, root: str) -> bool:
+ """Check if root is in history"""
+ result = await self.server.simulate_transaction(
+ self._build_view_call("is_known_root", [self._bytes32_to_scval(root)])
+ )
+ return self._parse_bool(result)
+
+ async def is_spent(self, nullifier_hash: str) -> bool:
+ """Check if nullifier is spent"""
+ result = await self.server.simulate_transaction(
+ self._build_view_call("is_spent", [self._bytes32_to_scval(nullifier_hash)])
+ )
+ return self._parse_bool(result)
+
+ async def check_note_status(self, note: Note) -> dict:
+ """Check if note can be withdrawn"""
+ nullifier_hash = await self._compute_nullifier_hash(note.nullifier)
+ spent = await self.is_spent(nullifier_hash)
+
+ leaves = await self._fetch_all_leaves()
+ in_tree = note.commitment in leaves
+
+ return {
+ "is_spent": spent,
+ "in_tree": in_tree,
+ "can_withdraw": not spent and in_tree,
+ }
+
+ # ──────────────────────────────────────────────────────────────
+ # Helper Methods
+ # ──────────────────────────────────────────────────────────────
+
+ async def _poseidon2_hash(self, a: bytes, b: bytes) -> bytes:
+ """Compute Poseidon2 hash"""
+ # Placeholder - use actual implementation
+ from privacylayer.crypto import poseidon2
+ return poseidon2(a, b)
+
+ async def _generate_zk_proof(self, **inputs) -> dict:
+ """Generate Groth16 proof using Noir"""
+ # Placeholder - use actual prover
+ from privacylayer.prover import generate_proof
+ return generate_proof(inputs)
+
+ def _build_merkle_tree(self, leaves: list) -> object:
+ """Build Merkle tree"""
+ from privacylayer.merkle import MerkleTree
+ return MerkleTree(20, leaves)
+
+ async def _fetch_all_leaves(self) -> list:
+ """Fetch all deposit commitments"""
+ events = await self.server.get_events({
+ "filters": [{
+ "type": "contract",
+ "contractIds": [self.contract_id],
+ "topics": [["deposit"]],
+ }],
+ "startLedger": 1,
+ })
+ return [e["value"]["commitment"] for e in events]
+
+ def _save_note(self, note: Note):
+ """Save note to storage"""
+ notes = json.loads(localStorage.get("privacylayer_notes", "[]"))
+ notes.append(note.__dict__)
+ localStorage.set("privacylayer_notes", json.dumps(notes))
+
+ def _update_note(self, note: Note):
+ """Update note in storage"""
+ notes = json.loads(localStorage.get("privacylayer_notes", "[]"))
+ for i, n in enumerate(notes):
+ if n["commitment"] == note.commitment:
+ notes[i] = note.__dict__
+ break
+ localStorage.set("privacylayer_notes", json.dumps(notes))
+
+ # SCVal helpers...
+ def _bytes32_to_scval(self, b: str) -> xdr.ScVal:
+ return xdr.ScVal(
+ type=xdr.ScValType.SCV_BYTES,
+ bytes=bytes.fromhex(b.replace("0x", "")),
+ )
+
+ def _build_view_call(self, fn: str, args: list) -> xdr.Transaction:
+ # Build simulation transaction
+ pass
+
+ # Parse helpers...
+ def _parse_bytes32(self, result) -> str:
+ pass
+
+ def _parse_u32(self, result) -> int:
+ pass
+
+ def _parse_bool(self, result) -> bool:
+ pass
+
+ def _parse_pool_config(self, result) -> PoolConfig:
+ pass
+
+
+# ─────────────────────────────────────────────────────────────────
+# Usage Example
+# ─────────────────────────────────────────────────────────────────
+
+async def main():
+ # Initialize client with secret key
+ client = PrivacyPoolClient(secret_key="S...")
+
+ # Query pool state
+ root = await client.get_root()
+ count = await client.get_deposit_count()
+ config = await client.get_config()
+
+ print(f"Pool State:")
+ print(f" Root: {root}")
+ print(f" Deposits: {count}")
+ print(f" Token: {config.token}")
+ print(f" Paused: {config.paused}")
+
+ # Deposit
+ note = await client.deposit()
+
+ # Check note status
+ status = await client.check_note_status(note)
+ print(f"Note Status: {status}")
+
+ # Withdraw
+ if status["can_withdraw"]:
+ recipient = "GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWHF"
+ await client.withdraw(note, recipient)
+
+
+if __name__ == "__main__":
+ asyncio.run(main())
\ No newline at end of file
diff --git a/docs/api/examples/typescript.ts b/docs/api/examples/typescript.ts
new file mode 100644
index 0000000..2a19e8c
--- /dev/null
+++ b/docs/api/examples/typescript.ts
@@ -0,0 +1,389 @@
+/**
+ * PrivacyLayer TypeScript SDK Examples
+ *
+ * Install: npm install @privacylayer/sdk
+ * Or use the client directly with stellar-sdk
+ */
+
+import { SorobanRpc, TransactionBuilder, Networks, xdr, Address } from '@stellar/stellar-sdk';
+
+// ─────────────────────────────────────────────────────────────────
+// Configuration
+// ─────────────────────────────────────────────────────────────────
+
+const CONFIG = {
+ testnet: {
+ rpcUrl: 'https://soroban-testnet.stellar.org:443',
+ networkPassphrase: Networks.TESTNET,
+ contractId: 'CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP3B',
+ },
+ mainnet: {
+ rpcUrl: 'https://soroban-mainnet.stellar.org:443',
+ networkPassphrase: Networks.PUBLIC,
+ contractId: 'CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP3B',
+ },
+};
+
+const env = 'testnet';
+const config = CONFIG[env];
+
+// ─────────────────────────────────────────────────────────────────
+// Client Setup
+// ─────────────────────────────────────────────────────────────────
+
+const server = new SorobanRpc.Server(config.rpcUrl);
+
+async function getClient() {
+ // Use Freighter or other wallet
+ // This is a placeholder - integrate with your wallet
+ const wallet = await connectWallet();
+ return wallet;
+}
+
+// ─────────────────────────────────────────────────────────────────
+// Example 1: Initialize Pool (Admin)
+// ─────────────────────────────────────────────────────────────────
+
+async function initializePool() {
+ const wallet = await getClient();
+
+ const admin = wallet.publicKey;
+ const token = 'CCHEGGH7VWDPOHCQFDKH2TJ5TTKYQ4FW8VBA5EOFWYPG5CLIBVH2GLI5'; // USDC
+
+ // Denomination: 100 USDC (in micro-units)
+ const denomination = {
+ tag: 'USDC',
+ value: BigInt(100_000_000), // 100 USDC with 6 decimals
+ };
+
+ // Verifying key (placeholder - get from circuit setup)
+ const verifyingKey = {
+ alpha_g1: ['0x...', '0x...'],
+ beta_g2: [['0x...', '0x...'], ['0x...', '0x...']],
+ gamma_g2: [['0x...', '0x...'], ['0x...', '0x...']],
+ delta_g2: [['0x...', '0x...'], ['0x...', '0x...']],
+ ic: [],
+ };
+
+ // Build transaction
+ const account = await server.getAccount(wallet.publicKey);
+
+ const tx = new TransactionBuilder(account, {
+ fee: '100',
+ networkPassphrase: config.networkPassphrase,
+ })
+ .addOperation(
+ xdr.Operation.contractCall({
+ contract: config.contractId,
+ function_name: 'initialize',
+ args: [
+ Address.fromString(admin).toScVal(),
+ Address.fromString(token).toScVal(),
+ xdr.ScVal.scvMap([
+ new xdr.ScMapEntry({
+ key: xdr.ScVal.scvSymbol('tag'),
+ val: xdr.ScVal.scvSymbol('USDC'),
+ }),
+ new xdr.ScMapEntry({
+ key: xdr.ScVal.scvSymbol('value'),
+ val: xdr.ScVal.scvInt128Parts({ lo: BigInt(100_000_000), hi: BigInt(0) }),
+ }),
+ ]),
+ // Verifying key SCVal...
+ ],
+ })
+ )
+ .setTimeout(30)
+ .build();
+
+ // Sign and submit
+ const signedTx = await wallet.signTransaction(tx);
+ const result = await server.sendTransaction(signedTx);
+
+ console.log('Pool initialized:', result);
+ return result;
+}
+
+// ─────────────────────────────────────────────────────────────────
+// Example 2: Deposit
+// ─────────────────────────────────────────────────────────────────
+
+async function deposit() {
+ const wallet = await getClient();
+
+ // 1. Generate note (nullifier + secret)
+ const nullifier = generateRandomBytes(31);
+ const secret = generateRandomBytes(31);
+
+ // 2. Compute commitment using Poseidon hash
+ const commitment = await poseidon2Hash(nullifier, secret);
+
+ // 3. Build deposit transaction
+ const account = await server.getAccount(wallet.publicKey);
+
+ const tx = new TransactionBuilder(account, {
+ fee: '100',
+ networkPassphrase: config.networkPassphrase,
+ })
+ .addOperation(
+ xdr.Operation.contractCall({
+ contract: config.contractId,
+ function_name: 'deposit',
+ args: [
+ Address.fromString(wallet.publicKey).toScVal(),
+ commitmentToScVal(commitment),
+ ],
+ })
+ )
+ .setTimeout(30)
+ .build();
+
+ // 4. Sign and submit
+ const signedTx = await wallet.signTransaction(tx);
+ const result = await server.sendTransaction(signedTx);
+
+ // 5. Extract leaf index from result
+ const leafIndex = parseLeafIndexFromResult(result);
+
+ // 6. Store note securely
+ const note = {
+ nullifier: bufferToHex(nullifier),
+ secret: bufferToHex(secret),
+ leafIndex,
+ commitment: bufferToHex(commitment),
+ denomination: 'USDC',
+ amount: 100,
+ network: env,
+ createdAt: Date.now(),
+ };
+
+ // Save to secure storage
+ saveNoteToStorage(note);
+
+ console.log('Deposit successful!');
+ console.log('Leaf index:', leafIndex);
+ console.log('Note (KEEP SECRET):', note);
+
+ return note;
+}
+
+// ─────────────────────────────────────────────────────────────────
+// Example 3: Withdraw
+// ─────────────────────────────────────────────────────────────────
+
+async function withdraw(note, recipientAddress) {
+ const wallet = await getClient();
+
+ // 1. Sync Merkle tree
+ const leaves = await fetchAllLeaves();
+ const tree = buildMerkleTree(leaves);
+
+ // 2. Get Merkle proof for your commitment
+ const merkleProof = tree.getProof(note.leafIndex);
+ const root = tree.root();
+
+ // 3. Generate ZK proof using Noir prover
+ const zkProof = await generateZKProof({
+ nullifier: hexToBuffer(note.nullifier),
+ secret: hexToBuffer(note.secret),
+ merkleProof,
+ root,
+ recipient: recipientAddress,
+ });
+
+ // 4. Build withdraw transaction
+ const account = await server.getAccount(wallet.publicKey);
+
+ const tx = new TransactionBuilder(account, {
+ fee: '200', // Higher fee for withdraw
+ networkPassphrase: config.networkPassphrase,
+ })
+ .addOperation(
+ xdr.Operation.contractCall({
+ contract: config.contractId,
+ function_name: 'withdraw',
+ args: [
+ proofToScVal(zkProof.proof),
+ publicInputsToScVal({
+ root,
+ nullifierHash: zkProof.nullifierHash,
+ recipient: recipientAddress,
+ }),
+ ],
+ })
+ )
+ .setTimeout(30)
+ .build();
+
+ // 5. Sign and submit
+ const signedTx = await wallet.signTransaction(tx);
+ const result = await server.sendTransaction(signedTx);
+
+ // 6. Mark note as spent
+ markNoteAsSpent(note);
+
+ console.log('Withdrawal successful!');
+ console.log('Recipient:', recipientAddress);
+
+ return result;
+}
+
+// ─────────────────────────────────────────────────────────────────
+// Example 4: Query Pool State
+// ─────────────────────────────────────────────────────────────────
+
+async function queryPoolState() {
+ // Get current root
+ const rootResult = await server.simulateTransaction(
+ buildViewCall('get_root', [])
+ );
+ const root = parseBytes32(rootResult);
+
+ // Get deposit count
+ const countResult = await server.simulateTransaction(
+ buildViewCall('deposit_count', [])
+ );
+ const count = parseU32(countResult);
+
+ // Get config
+ const configResult = await server.simulateTransaction(
+ buildViewCall('get_config', [])
+ );
+ const poolConfig = parsePoolConfig(configResult);
+
+ console.log('Pool State:');
+ console.log('- Root:', root);
+ console.log('- Deposits:', count);
+ console.log('- Token:', poolConfig.token);
+ console.log('- Denomination:', poolConfig.denomination);
+ console.log('- Paused:', poolConfig.paused);
+
+ return { root, count, config: poolConfig };
+}
+
+// ─────────────────────────────────────────────────────────────────
+// Example 5: Check Note Status
+// ─────────────────────────────────────────────────────────────────
+
+async function checkNoteStatus(note) {
+ // 1. Get current root and check if it's known
+ const rootResult = await server.simulateTransaction(
+ buildViewCall('get_root', [])
+ );
+ const currentRoot = parseBytes32(rootResult);
+
+ // 2. Check if note's nullifier is spent
+ const nullifierHash = await computeNullifierHash(note.nullifier);
+ const spentResult = await server.simulateTransaction(
+ buildViewCall('is_spent', [bytes32ToScVal(nullifierHash)])
+ );
+ const isSpent = parseBool(spentResult);
+
+ // 3. Get leaves and check if commitment is in tree
+ const leaves = await fetchAllLeaves();
+ const isInTree = leaves.includes(note.commitment);
+
+ console.log('Note Status:');
+ console.log('- Spent:', isSpent);
+ console.log('- In Tree:', isInTree);
+ console.log('- Can Withdraw:', !isSpent && isInTree);
+
+ return {
+ isSpent,
+ isInTree,
+ canWithdraw: !isSpent && isInTree,
+ };
+}
+
+// ─────────────────────────────────────────────────────────────────
+// Helper Functions
+// ─────────────────────────────────────────────────────────────────
+
+function generateRandomBytes(length: number): Buffer {
+ return require('crypto').randomBytes(length);
+}
+
+function bufferToHex(buffer: Buffer): string {
+ return '0x' + buffer.toString('hex');
+}
+
+function hexToBuffer(hex: string): Buffer {
+ return Buffer.from(hex.replace('0x', ''), 'hex');
+}
+
+async function poseidon2Hash(a: Buffer, b: Buffer): Promise {
+ // Use noir.js or native implementation
+ // This is a placeholder
+ const { poseidon2 } = await import('@privacylayer/crypto');
+ return poseidon2(a, b);
+}
+
+async function generateZKProof(inputs: any): Promise {
+ // Use noir.js to generate proof
+ // This requires the compiled circuit
+ const { generateProof } = await import('@privacylayer/prover');
+ return generateProof(inputs);
+}
+
+function buildMerkleTree(leaves: string[]): any {
+ // Build incremental Merkle tree
+ // Depth = 20
+ const { MerkleTree } = require('@privacylayer/merkle');
+ return new MerkleTree(20, leaves);
+}
+
+async function fetchAllLeaves(): Promise {
+ // Fetch all deposit events from the contract
+ // Use Stellar RPC event streaming
+ const events = await server.getEvents({
+ filters: [{
+ type: 'contract',
+ contractIds: [config.contractId],
+ topics: [['deposit']],
+ }],
+ startLedger: 1,
+ });
+
+ return events.map(e => e.value.commitment);
+}
+
+function saveNoteToStorage(note: any) {
+ // Save to secure storage (localStorage, IndexedDB, or encrypted)
+ const notes = JSON.parse(localStorage.getItem('privacylayer_notes') || '[]');
+ notes.push(note);
+ localStorage.setItem('privacylayer_notes', JSON.stringify(notes));
+}
+
+function markNoteAsSpent(note: any) {
+ const notes = JSON.parse(localStorage.getItem('privacylayer_notes') || '[]');
+ const updated = notes.map(n =>
+ n.commitment === note.commitment ? { ...n, spent: true } : n
+ );
+ localStorage.setItem('privacylayer_notes', JSON.stringify(updated));
+}
+
+// ─────────────────────────────────────────────────────────────────
+// Usage
+// ─────────────────────────────────────────────────────────────────
+
+async function main() {
+ try {
+ // Query pool state
+ await queryPoolState();
+
+ // Deposit
+ const note = await deposit();
+
+ // Check note status
+ await checkNoteStatus(note);
+
+ // Withdraw
+ const recipient = 'GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWHF';
+ await withdraw(note, recipient);
+
+ } catch (error) {
+ console.error('Error:', error);
+ }
+}
+
+main();
\ No newline at end of file
diff --git a/docs/api/openapi.yaml b/docs/api/openapi.yaml
new file mode 100644
index 0000000..359a639
--- /dev/null
+++ b/docs/api/openapi.yaml
@@ -0,0 +1,689 @@
+openapi: 3.0.3
+info:
+ title: PrivacyLayer API
+ description: |
+ # PrivacyLayer API Documentation
+
+ PrivacyLayer is the first ZK-proof shielded pool on Stellar Soroban, enabling compliance-forward private transactions.
+
+ ## Overview
+
+ - **Deposit**: Users deposit fixed-denomination XLM or USDC into a shielded pool
+ - **Withdraw**: Users withdraw to any address using a zero-knowledge proof
+ - **Privacy**: No on-chain link between deposit and withdrawal
+
+ ## Authentication
+
+ All contract interactions require Stellar wallet signatures. Use Freighter or other compatible wallets.
+
+ ## Rate Limits
+
+ - Maximum 100 requests per minute per IP
+ - Maximum 10 concurrent transactions per address
+
+ ## Base URL
+
+ - **Mainnet**: `https://mainnet.stellar.org`
+ - **Testnet**: `https://testnet.stellar.org`
+
+ version: 1.0.0
+ contact:
+ name: PrivacyLayer Team
+ url: https://github.com/ANAVHEOBA/PrivacyLayer
+ license:
+ name: MIT
+ url: https://opensource.org/licenses/MIT
+
+servers:
+ - url: https://testnet.stellar.org
+ description: Stellar Testnet
+ - url: https://mainnet.stellar.org
+ description: Stellar Mainnet
+
+tags:
+ - name: Core Operations
+ description: Deposit and withdraw operations
+ - name: View Functions
+ description: Query contract state
+ - name: Admin Functions
+ description: Administrative operations (admin only)
+ - name: Events
+ description: Contract event definitions
+
+paths:
+ # ─────────────────────────────────────────────────────────────────
+ # Initialization
+ # ─────────────────────────────────────────────────────────────────
+ /contracts/privacy-pool/initialize:
+ post:
+ tags:
+ - Admin Functions
+ summary: Initialize the privacy pool
+ description: |
+ Initialize the privacy pool contract. Must be called once before any deposits or withdrawals.
+
+ **Required Authorization**: Deployer address
+
+ **Prerequisites**:
+ - Contract must not be already initialized
+ - Valid token address (USDC or XLM)
+ - Valid Groth16 verifying key
+ operationId: initialize
+ requestBody:
+ required: true
+ content:
+ application/json:
+ schema:
+ type: object
+ required:
+ - admin
+ - token
+ - denomination
+ - vk
+ properties:
+ admin:
+ $ref: '#/components/schemas/Address'
+ token:
+ $ref: '#/components/schemas/Address'
+ denomination:
+ $ref: '#/components/schemas/Denomination'
+ vk:
+ $ref: '#/components/schemas/VerifyingKey'
+ examples:
+ initialize_usdc_100:
+ summary: Initialize with 100 USDC denomination
+ value:
+ admin: "GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWHF"
+ token: "CCHEGGH7VWDPOHCQFDKH2TJ5TTKYQ4FW8VBA5EOFWYPG5CLIBVH2GLI5"
+ denomination:
+ tag: "USDC"
+ value: "1000000000"
+ vk:
+ alpha_g1: ["0x1234...", "0x5678..."]
+ beta_g2: [["0xabcd...", "0xef01..."], ["0x2345...", "0x6789..."]]
+ gamma_g2: [["0x...", "0x..."], ["0x...", "0x..."]]
+ delta_g2: [["0x...", "0x..."], ["0x...", "0x..."]]
+ ic: []
+ responses:
+ '200':
+ description: Pool initialized successfully
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ success:
+ type: boolean
+ example: true
+ '400':
+ $ref: '#/components/responses/BadRequest'
+ '409':
+ description: Pool already initialized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/Error'
+ example:
+ code: "ALREADY_INITIALIZED"
+ message: "Pool has already been initialized"
+
+ # ─────────────────────────────────────────────────────────────────
+ # Deposit
+ # ─────────────────────────────────────────────────────────────────
+ /contracts/privacy-pool/deposit:
+ post:
+ tags:
+ - Core Operations
+ summary: Deposit into the shielded pool
+ description: |
+ Deposit tokens into the privacy pool.
+
+ **Flow**:
+ 1. Generate a note: `(nullifier, secret)` using client SDK
+ 2. Compute commitment: `commitment = Poseidon(nullifier || secret)`
+ 3. Call this endpoint with the commitment
+ 4. Store the note securely for future withdrawal
+
+ **Note Security**:
+ - The note is the only proof of your deposit
+ - If lost, funds are unrecoverable
+ - Never share your note with anyone
+ operationId: deposit
+ requestBody:
+ required: true
+ content:
+ application/json:
+ schema:
+ type: object
+ required:
+ - from
+ - commitment
+ properties:
+ from:
+ $ref: '#/components/schemas/Address'
+ commitment:
+ $ref: '#/components/schemas/Bytes32'
+ examples:
+ deposit_example:
+ summary: Standard deposit
+ value:
+ from: "GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWHF"
+ commitment: "0xa1b2c3d4e5f67890123456789012345678901234567890123456789012345678"
+ responses:
+ '200':
+ description: Deposit successful
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ leaf_index:
+ type: integer
+ description: Position in Merkle tree
+ example: 42
+ root:
+ $ref: '#/components/schemas/Bytes32'
+ example:
+ leaf_index: 42
+ root: "0x1234...abcd"
+ '400':
+ $ref: '#/components/responses/BadRequest'
+ '403':
+ description: Pool is paused
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/Error'
+ example:
+ code: "POOL_PAUSED"
+ message: "Deposits are currently paused"
+
+ # ─────────────────────────────────────────────────────────────────
+ # Withdraw
+ # ─────────────────────────────────────────────────────────────────
+ /contracts/privacy-pool/withdraw:
+ post:
+ tags:
+ - Core Operations
+ summary: Withdraw from the shielded pool
+ description: |
+ Withdraw tokens from the privacy pool using a ZK proof.
+
+ **Flow**:
+ 1. Sync Merkle tree to get all leaves
+ 2. Generate Merkle proof for your commitment
+ 3. Generate ZK proof using Noir prover
+ 4. Submit proof and public inputs
+
+ **Proof Components**:
+ - `proof_a`, `proof_b`, `proof_c`: Groth16 proof (BN254)
+ - `public_inputs`: Root, nullifier hash, recipient address
+ operationId: withdraw
+ requestBody:
+ required: true
+ content:
+ application/json:
+ schema:
+ type: object
+ required:
+ - proof
+ - pub_inputs
+ properties:
+ proof:
+ $ref: '#/components/schemas/Proof'
+ pub_inputs:
+ $ref: '#/components/schemas/PublicInputs'
+ examples:
+ withdraw_example:
+ summary: Standard withdrawal
+ value:
+ proof:
+ a: ["0x1234...", "0x5678..."]
+ b: [["0xabcd...", "0xef01..."], ["0x2345...", "0x6789..."]]
+ c: ["0x9876...", "0x5432..."]
+ pub_inputs:
+ root: "0x1234...abcd"
+ nullifier_hash: "0x5678...ef01"
+ recipient: "GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWHF"
+ relayer: "GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWHF"
+ fee: "1000000"
+ responses:
+ '200':
+ description: Withdrawal successful
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ success:
+ type: boolean
+ example: true
+ '400':
+ $ref: '#/components/responses/BadRequest'
+ '403':
+ description: Invalid proof or spent nullifier
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/Error'
+ examples:
+ invalid_proof:
+ summary: Invalid ZK proof
+ value:
+ code: "INVALID_PROOF"
+ message: "Groth16 proof verification failed"
+ spent_nullifier:
+ summary: Nullifier already spent
+ value:
+ code: "NULLIFIER_SPENT"
+ message: "This note has already been spent"
+
+ # ─────────────────────────────────────────────────────────────────
+ # View Functions
+ # ─────────────────────────────────────────────────────────────────
+ /contracts/privacy-pool/root:
+ get:
+ tags:
+ - View Functions
+ summary: Get current Merkle root
+ description: Returns the most recent Merkle root of the commitment tree.
+ operationId: getRoot
+ responses:
+ '200':
+ description: Current root
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/Bytes32'
+ example: "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef"
+
+ /contracts/privacy-pool/deposit-count:
+ get:
+ tags:
+ - View Functions
+ summary: Get total deposit count
+ description: Returns the total number of deposits made to the pool.
+ operationId: depositCount
+ responses:
+ '200':
+ description: Deposit count
+ content:
+ application/json:
+ schema:
+ type: integer
+ example: 1337
+
+ /contracts/privacy-pool/is-known-root:
+ post:
+ tags:
+ - View Functions
+ summary: Check if root is in history
+ description: |
+ Check if a Merkle root exists in the historical root buffer.
+
+ **Use Case**: Verify that a root used in a withdrawal proof is still valid.
+ operationId: isKnownRoot
+ requestBody:
+ required: true
+ content:
+ application/json:
+ schema:
+ type: object
+ required:
+ - root
+ properties:
+ root:
+ $ref: '#/components/schemas/Bytes32'
+ responses:
+ '200':
+ description: Root validity
+ content:
+ application/json:
+ schema:
+ type: boolean
+ example: true
+
+ /contracts/privacy-pool/is-spent:
+ post:
+ tags:
+ - View Functions
+ summary: Check if nullifier is spent
+ description: |
+ Check if a nullifier hash has been spent.
+
+ **Use Case**: Verify if a note can still be withdrawn.
+ operationId: isSpent
+ requestBody:
+ required: true
+ content:
+ application/json:
+ schema:
+ type: object
+ required:
+ - nullifier_hash
+ properties:
+ nullifier_hash:
+ $ref: '#/components/schemas/Bytes32'
+ responses:
+ '200':
+ description: Spent status
+ content:
+ application/json:
+ schema:
+ type: boolean
+ example: false
+
+ /contracts/privacy-pool/config:
+ get:
+ tags:
+ - View Functions
+ summary: Get pool configuration
+ description: Returns the current pool configuration including admin, token, and denomination.
+ operationId: getConfig
+ responses:
+ '200':
+ description: Pool configuration
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PoolConfig'
+
+ # ─────────────────────────────────────────────────────────────────
+ # Admin Functions
+ # ─────────────────────────────────────────────────────────────────
+ /contracts/privacy-pool/pause:
+ post:
+ tags:
+ - Admin Functions
+ summary: Pause the pool
+ description: |
+ Pause all deposit and withdraw operations.
+
+ **Required Authorization**: Admin address
+ operationId: pause
+ requestBody:
+ required: true
+ content:
+ application/json:
+ schema:
+ type: object
+ required:
+ - admin
+ properties:
+ admin:
+ $ref: '#/components/schemas/Address'
+ responses:
+ '200':
+ description: Pool paused
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ success:
+ type: boolean
+ '403':
+ description: Not authorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/Error'
+
+ /contracts/privacy-pool/unpause:
+ post:
+ tags:
+ - Admin Functions
+ summary: Unpause the pool
+ description: |
+ Resume deposit and withdraw operations.
+
+ **Required Authorization**: Admin address
+ operationId: unpause
+ requestBody:
+ required: true
+ content:
+ application/json:
+ schema:
+ type: object
+ required:
+ - admin
+ properties:
+ admin:
+ $ref: '#/components/schemas/Address'
+ responses:
+ '200':
+ description: Pool unpaused
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ success:
+ type: boolean
+
+ /contracts/privacy-pool/verifying-key:
+ put:
+ tags:
+ - Admin Functions
+ summary: Update verifying key
+ description: |
+ Update the Groth16 verifying key for the circuit.
+
+ **Use Case**: Circuit upgrade or bug fix.
+
+ **Required Authorization**: Admin address
+ operationId: setVerifyingKey
+ requestBody:
+ required: true
+ content:
+ application/json:
+ schema:
+ type: object
+ required:
+ - admin
+ - new_vk
+ properties:
+ admin:
+ $ref: '#/components/schemas/Address'
+ new_vk:
+ $ref: '#/components/schemas/VerifyingKey'
+ responses:
+ '200':
+ description: Verifying key updated
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ success:
+ type: boolean
+
+# ─────────────────────────────────────────────────────────────────
+# Components
+# ─────────────────────────────────────────────────────────────────
+components:
+ schemas:
+ Address:
+ type: string
+ description: Stellar address (G... for accounts, C... for contracts)
+ pattern: '^G[A-Z2-7]{55}$|^C[A-Z2-7]{55}$'
+ example: "GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWHF"
+
+ Bytes32:
+ type: string
+ description: 32-byte hex string with 0x prefix
+ pattern: '^0x[a-fA-F0-9]{64}$'
+ example: "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef"
+
+ Denomination:
+ type: object
+ description: Token denomination for deposits
+ required:
+ - tag
+ - value
+ properties:
+ tag:
+ type: string
+ enum: [XLM, USDC]
+ description: Token type
+ value:
+ type: string
+ description: Amount in smallest unit (stroops for XLM, micro-units for USDC)
+ example:
+ tag: "USDC"
+ value: "1000000000"
+
+ VerifyingKey:
+ type: object
+ description: Groth16 verifying key for BN254 curve
+ properties:
+ alpha_g1:
+ type: array
+ items:
+ type: string
+ minItems: 2
+ maxItems: 2
+ beta_g2:
+ type: array
+ items:
+ type: array
+ items:
+ type: string
+ minItems: 2
+ maxItems: 2
+ minItems: 2
+ maxItems: 2
+ gamma_g2:
+ type: array
+ items:
+ type: array
+ items:
+ type: string
+ minItems: 2
+ maxItems: 2
+ minItems: 2
+ maxItems: 2
+ delta_g2:
+ type: array
+ items:
+ type: array
+ items:
+ type: string
+ minItems: 2
+ maxItems: 2
+ minItems: 2
+ maxItems: 2
+ ic:
+ type: array
+ items:
+ type: array
+ items:
+ type: string
+ minItems: 2
+ maxItems: 2
+
+ Proof:
+ type: object
+ description: Groth16 zero-knowledge proof
+ required:
+ - a
+ - b
+ - c
+ properties:
+ a:
+ type: array
+ items:
+ type: string
+ minItems: 2
+ maxItems: 2
+ description: Proof component A (G1 point)
+ b:
+ type: array
+ items:
+ type: array
+ items:
+ type: string
+ minItems: 2
+ maxItems: 2
+ minItems: 2
+ maxItems: 2
+ description: Proof component B (G2 point)
+ c:
+ type: array
+ items:
+ type: string
+ minItems: 2
+ maxItems: 2
+ description: Proof component C (G1 point)
+
+ PublicInputs:
+ type: object
+ description: Public inputs for withdrawal proof
+ required:
+ - root
+ - nullifier_hash
+ - recipient
+ properties:
+ root:
+ $ref: '#/components/schemas/Bytes32'
+ nullifier_hash:
+ $ref: '#/components/schemas/Bytes32'
+ recipient:
+ $ref: '#/components/schemas/Address'
+ relayer:
+ $ref: '#/components/schemas/Address'
+ fee:
+ type: string
+ description: Relayer fee in stroops
+
+ PoolConfig:
+ type: object
+ description: Pool configuration
+ properties:
+ admin:
+ $ref: '#/components/schemas/Address'
+ token:
+ $ref: '#/components/schemas/Address'
+ denomination:
+ $ref: '#/components/schemas/Denomination'
+ paused:
+ type: boolean
+ initialized:
+ type: boolean
+
+ Error:
+ type: object
+ properties:
+ code:
+ type: string
+ description: Error code
+ message:
+ type: string
+ description: Human-readable error message
+ example:
+ code: "INVALID_PROOF"
+ message: "Groth16 proof verification failed"
+
+ responses:
+ BadRequest:
+ description: Invalid request parameters
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/Error'
+ example:
+ code: "INVALID_INPUT"
+ message: "Invalid commitment format"
+
+# ─────────────────────────────────────────────────────────────────
+# Security
+# ─────────────────────────────────────────────────────────────────
+security:
+ - stellar_signature: []
+
+securitySchemes:
+ stellar_signature:
+ type: apiKey
+ in: header
+ name: X-Stellar-Signature
+ description: Stellar transaction signature for authentication
\ No newline at end of file
diff --git a/docs/api/postman_collection.json b/docs/api/postman_collection.json
new file mode 100644
index 0000000..9a9fc60
--- /dev/null
+++ b/docs/api/postman_collection.json
@@ -0,0 +1,474 @@
+{
+ "info": {
+ "name": "PrivacyLayer API",
+ "description": "PrivacyLayer is the first ZK-proof shielded pool on Stellar Soroban. This collection provides all endpoints for interacting with the privacy pool contract.",
+ "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json",
+ "version": "1.0.0"
+ },
+ "variable": [
+ {
+ "key": "base_url",
+ "value": "https://testnet.stellar.org",
+ "type": "string"
+ },
+ {
+ "key": "contract_id",
+ "value": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP3B",
+ "type": "string"
+ },
+ {
+ "key": "admin_address",
+ "value": "GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWHF",
+ "type": "string"
+ },
+ {
+ "key": "token_address",
+ "value": "CCHEGGH7VWDPOHCQFDKH2TJ5TTKYQ4FW8VBA5EOFWYPG5CLIBVH2GLI5",
+ "type": "string"
+ }
+ ],
+ "item": [
+ {
+ "name": "Core Operations",
+ "item": [
+ {
+ "name": "Deposit",
+ "request": {
+ "method": "POST",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json"
+ }
+ ],
+ "body": {
+ "mode": "raw",
+ "raw": "{\n \"from\": \"{{admin_address}}\",\n \"commitment\": \"0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef\"\n}"
+ },
+ "url": {
+ "raw": "{{base_url}}/contracts/privacy-pool/deposit",
+ "host": ["{{base_url}}"],
+ "path": ["contracts", "privacy-pool", "deposit"]
+ },
+ "description": "Deposit tokens into the shielded pool.\n\n**Prerequisites**:\n1. Generate note (nullifier + secret)\n2. Compute commitment: `Poseidon(nullifier || secret)`\n3. Approve token transfer\n\n**Note Security**:\n- Store the note securely - it's your only proof of deposit\n- If lost, funds are unrecoverable\n- Never share your note"
+ },
+ "response": [
+ {
+ "name": "Success",
+ "originalRequest": {
+ "method": "POST",
+ "header": [],
+ "body": {
+ "mode": "raw",
+ "raw": "{\n \"from\": \"GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWHF\",\n \"commitment\": \"0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef\"\n}"
+ },
+ "url": {
+ "raw": "{{base_url}}/contracts/privacy-pool/deposit",
+ "host": ["{{base_url}}"],
+ "path": ["contracts", "privacy-pool", "deposit"]
+ }
+ },
+ "status": "OK",
+ "code": 200,
+ "_postman_previewlanguage": "json",
+ "header": [],
+ "body": "{\n \"leaf_index\": 42,\n \"root\": \"0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef\"\n}"
+ }
+ ]
+ },
+ {
+ "name": "Withdraw",
+ "request": {
+ "method": "POST",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json"
+ }
+ ],
+ "body": {
+ "mode": "raw",
+ "raw": "{\n \"proof\": {\n \"a\": [\"0x1234567890abcdef\", \"0x1234567890abcdef\"],\n \"b\": [[\"0x1234567890abcdef\", \"0x1234567890abcdef\"], [\"0x1234567890abcdef\", \"0x1234567890abcdef\"]],\n \"c\": [\"0x1234567890abcdef\", \"0x1234567890abcdef\"]\n },\n \"pub_inputs\": {\n \"root\": \"0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef\",\n \"nullifier_hash\": \"0x567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef12\",\n \"recipient\": \"GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWHF\",\n \"relayer\": \"GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWHF\",\n \"fee\": \"1000000\"\n }\n}"
+ },
+ "url": {
+ "raw": "{{base_url}}/contracts/privacy-pool/withdraw",
+ "host": ["{{base_url}}"],
+ "path": ["contracts", "privacy-pool", "withdraw"]
+ },
+ "description": "Withdraw tokens from the shielded pool using a ZK proof.\n\n**Prerequisites**:\n1. Sync Merkle tree to get all leaves\n2. Generate Merkle proof for your commitment\n3. Generate ZK proof using Noir prover\n\n**Proof Components**:\n- `a`, `b`, `c`: Groth16 proof points (BN254)\n- Public inputs: root, nullifier hash, recipient"
+ },
+ "response": [
+ {
+ "name": "Success",
+ "originalRequest": {
+ "method": "POST",
+ "header": [],
+ "body": {
+ "mode": "raw",
+ "raw": "{\n \"proof\": {\n \"a\": [\"0x1234567890abcdef\", \"0x1234567890abcdef\"],\n \"b\": [[\"0x1234567890abcdef\", \"0x1234567890abcdef\"], [\"0x1234567890abcdef\", \"0x1234567890abcdef\"]],\n \"c\": [\"0x1234567890abcdef\", \"0x1234567890abcdef\"]\n },\n \"pub_inputs\": {\n \"root\": \"0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef\",\n \"nullifier_hash\": \"0x567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef12\",\n \"recipient\": \"GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWHF\"\n }\n}"
+ },
+ "url": {
+ "raw": "{{base_url}}/contracts/privacy-pool/withdraw",
+ "host": ["{{base_url}}"],
+ "path": ["contracts", "privacy-pool", "withdraw"]
+ }
+ },
+ "status": "OK",
+ "code": 200,
+ "_postman_previewlanguage": "json",
+ "header": [],
+ "body": "{\n \"success\": true\n}"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "name": "View Functions",
+ "item": [
+ {
+ "name": "Get Root",
+ "request": {
+ "method": "GET",
+ "header": [],
+ "url": {
+ "raw": "{{base_url}}/contracts/privacy-pool/root",
+ "host": ["{{base_url}}"],
+ "path": ["contracts", "privacy-pool", "root"]
+ },
+ "description": "Get the current Merkle root of the commitment tree."
+ },
+ "response": [
+ {
+ "name": "Success",
+ "originalRequest": {
+ "method": "GET",
+ "header": [],
+ "url": {
+ "raw": "{{base_url}}/contracts/privacy-pool/root",
+ "host": ["{{base_url}}"],
+ "path": ["contracts", "privacy-pool", "root"]
+ }
+ },
+ "status": "OK",
+ "code": 200,
+ "_postman_previewlanguage": "json",
+ "header": [],
+ "body": "\"0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef\""
+ }
+ ]
+ },
+ {
+ "name": "Get Deposit Count",
+ "request": {
+ "method": "GET",
+ "header": [],
+ "url": {
+ "raw": "{{base_url}}/contracts/privacy-pool/deposit-count",
+ "host": ["{{base_url}}"],
+ "path": ["contracts", "privacy-pool", "deposit-count"]
+ },
+ "description": "Get the total number of deposits made to the pool."
+ },
+ "response": [
+ {
+ "name": "Success",
+ "originalRequest": {
+ "method": "GET",
+ "header": [],
+ "url": {
+ "raw": "{{base_url}}/contracts/privacy-pool/deposit-count",
+ "host": ["{{base_url}}"],
+ "path": ["contracts", "privacy-pool", "deposit-count"]
+ }
+ },
+ "status": "OK",
+ "code": 200,
+ "_postman_previewlanguage": "json",
+ "header": [],
+ "body": "1337"
+ }
+ ]
+ },
+ {
+ "name": "Is Known Root",
+ "request": {
+ "method": "POST",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json"
+ }
+ ],
+ "body": {
+ "mode": "raw",
+ "raw": "{\n \"root\": \"0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef\"\n}"
+ },
+ "url": {
+ "raw": "{{base_url}}/contracts/privacy-pool/is-known-root",
+ "host": ["{{base_url}}"],
+ "path": ["contracts", "privacy-pool", "is-known-root"]
+ },
+ "description": "Check if a Merkle root exists in the historical root buffer."
+ },
+ "response": [
+ {
+ "name": "Success",
+ "originalRequest": {
+ "method": "POST",
+ "header": [],
+ "body": {
+ "mode": "raw",
+ "raw": "{\n \"root\": \"0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef\"\n}"
+ },
+ "url": {
+ "raw": "{{base_url}}/contracts/privacy-pool/is-known-root",
+ "host": ["{{base_url}}"],
+ "path": ["contracts", "privacy-pool", "is-known-root"]
+ }
+ },
+ "status": "OK",
+ "code": 200,
+ "_postman_previewlanguage": "json",
+ "header": [],
+ "body": "true"
+ }
+ ]
+ },
+ {
+ "name": "Is Spent",
+ "request": {
+ "method": "POST",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json"
+ }
+ ],
+ "body": {
+ "mode": "raw",
+ "raw": "{\n \"nullifier_hash\": \"0x567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef12\"\n}"
+ },
+ "url": {
+ "raw": "{{base_url}}/contracts/privacy-pool/is-spent",
+ "host": ["{{base_url}}"],
+ "path": ["contracts", "privacy-pool", "is-spent"]
+ },
+ "description": "Check if a nullifier hash has been spent."
+ },
+ "response": [
+ {
+ "name": "Success",
+ "originalRequest": {
+ "method": "POST",
+ "header": [],
+ "body": {
+ "mode": "raw",
+ "raw": "{\n \"nullifier_hash\": \"0x567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef12\"\n}"
+ },
+ "url": {
+ "raw": "{{base_url}}/contracts/privacy-pool/is-spent",
+ "host": ["{{base_url}}"],
+ "path": ["contracts", "privacy-pool", "is-spent"]
+ }
+ },
+ "status": "OK",
+ "code": 200,
+ "_postman_previewlanguage": "json",
+ "header": [],
+ "body": "false"
+ }
+ ]
+ },
+ {
+ "name": "Get Config",
+ "request": {
+ "method": "GET",
+ "header": [],
+ "url": {
+ "raw": "{{base_url}}/contracts/privacy-pool/config",
+ "host": ["{{base_url}}"],
+ "path": ["contracts", "privacy-pool", "config"]
+ },
+ "description": "Get the current pool configuration."
+ },
+ "response": [
+ {
+ "name": "Success",
+ "originalRequest": {
+ "method": "GET",
+ "header": [],
+ "url": {
+ "raw": "{{base_url}}/contracts/privacy-pool/config",
+ "host": ["{{base_url}}"],
+ "path": ["contracts", "privacy-pool", "config"]
+ }
+ },
+ "status": "OK",
+ "code": 200,
+ "_postman_previewlanguage": "json",
+ "header": [],
+ "body": "{\n \"admin\": \"GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWHF\",\n \"token\": \"CCHEGGH7VWDPOHCQFDKH2TJ5TTKYQ4FW8VBA5EOFWYPG5CLIBVH2GLI5\",\n \"denomination\": {\n \"tag\": \"USDC\",\n \"value\": \"100000000\"\n },\n \"paused\": false,\n \"initialized\": true\n}"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "name": "Admin Functions",
+ "item": [
+ {
+ "name": "Initialize Pool",
+ "request": {
+ "method": "POST",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json"
+ }
+ ],
+ "body": {
+ "mode": "raw",
+ "raw": "{\n \"admin\": \"{{admin_address}}\",\n \"token\": \"{{token_address}}\",\n \"denomination\": {\n \"tag\": \"USDC\",\n \"value\": \"100000000\"\n },\n \"vk\": {\n \"alpha_g1\": [\"0x1234...\", \"0x5678...\"],\n \"beta_g2\": [[\"0xabcd...\", \"0xef01...\"], [\"0x2345...\", \"0x6789...\"]],\n \"gamma_g2\": [[\"0x...\", \"0x...\"], [\"0x...\", \"0x...\"]],\n \"delta_g2\": [[\"0x...\", \"0x...\"], [\"0x...\", \"0x...\"]],\n \"ic\": []\n }\n}"
+ },
+ "url": {
+ "raw": "{{base_url}}/contracts/privacy-pool/initialize",
+ "host": ["{{base_url}}"],
+ "path": ["contracts", "privacy-pool", "initialize"]
+ },
+ "description": "Initialize the privacy pool. Must be called once after deployment."
+ },
+ "response": [
+ {
+ "name": "Success",
+ "originalRequest": {
+ "method": "POST",
+ "header": [],
+ "body": {
+ "mode": "raw",
+ "raw": "{\n \"admin\": \"GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWHF\",\n \"token\": \"CCHEGGH7VWDPOHCQFDKH2TJ5TTKYQ4FW8VBA5EOFWYPG5CLIBVH2GLI5\",\n \"denomination\": {\n \"tag\": \"USDC\",\n \"value\": \"100000000\"\n },\n \"vk\": {}\n}"
+ },
+ "url": {
+ "raw": "{{base_url}}/contracts/privacy-pool/initialize",
+ "host": ["{{base_url}}"],
+ "path": ["contracts", "privacy-pool", "initialize"]
+ }
+ },
+ "status": "OK",
+ "code": 200,
+ "_postman_previewlanguage": "json",
+ "header": [],
+ "body": "{\n \"success\": true\n}"
+ }
+ ]
+ },
+ {
+ "name": "Pause Pool",
+ "request": {
+ "method": "POST",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json"
+ }
+ ],
+ "body": {
+ "mode": "raw",
+ "raw": "{\n \"admin\": \"{{admin_address}}\"\n}"
+ },
+ "url": {
+ "raw": "{{base_url}}/contracts/privacy-pool/pause",
+ "host": ["{{base_url}}"],
+ "path": ["contracts", "privacy-pool", "pause"]
+ },
+ "description": "Pause all pool operations (admin only)."
+ },
+ "response": [
+ {
+ "name": "Success",
+ "originalRequest": {
+ "method": "POST",
+ "header": [],
+ "body": {
+ "mode": "raw",
+ "raw": "{\n \"admin\": \"GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWHF\"\n}"
+ },
+ "url": {
+ "raw": "{{base_url}}/contracts/privacy-pool/pause",
+ "host": ["{{base_url}}"],
+ "path": ["contracts", "privacy-pool", "pause"]
+ }
+ },
+ "status": "OK",
+ "code": 200,
+ "_postman_previewlanguage": "json",
+ "header": [],
+ "body": "{\n \"success\": true\n}"
+ }
+ ]
+ },
+ {
+ "name": "Unpause Pool",
+ "request": {
+ "method": "POST",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json"
+ }
+ ],
+ "body": {
+ "mode": "raw",
+ "raw": "{\n \"admin\": \"{{admin_address}}\"\n}"
+ },
+ "url": {
+ "raw": "{{base_url}}/contracts/privacy-pool/unpause",
+ "host": ["{{base_url}}"],
+ "path": ["contracts", "privacy-pool", "unpause"]
+ },
+ "description": "Resume pool operations (admin only)."
+ },
+ "response": []
+ },
+ {
+ "name": "Update Verifying Key",
+ "request": {
+ "method": "PUT",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json"
+ }
+ ],
+ "body": {
+ "mode": "raw",
+ "raw": "{\n \"admin\": \"{{admin_address}}\",\n \"new_vk\": {\n \"alpha_g1\": [\"0x1234...\", \"0x5678...\"],\n \"beta_g2\": [[\"0xabcd...\", \"0xef01...\"], [\"0x2345...\", \"0x6789...\"]],\n \"gamma_g2\": [[\"0x...\", \"0x...\"], [\"0x...\", \"0x...\"]],\n \"delta_g2\": [[\"0x...\", \"0x...\"], [\"0x...\", \"0x...\"]],\n \"ic\": []\n }\n}"
+ },
+ "url": {
+ "raw": "{{base_url}}/contracts/privacy-pool/verifying-key",
+ "host": ["{{base_url}}"],
+ "path": ["contracts", "privacy-pool", "verifying-key"]
+ },
+ "description": "Update the Groth16 verifying key (admin only)."
+ },
+ "response": []
+ }
+ ]
+ }
+ ],
+ "event": [
+ {
+ "listen": "prerequest",
+ "script": {
+ "type": "text/javascript",
+ "exec": [
+ "// Add timestamp to requests",
+ "pm.request.addHeader({",
+ " key: 'X-Timestamp',",
+ " value: Date.now().toString()",
+ "});"
+ ]
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/docs/api/swagger-ui.html b/docs/api/swagger-ui.html
new file mode 100644
index 0000000..23e8cd4
--- /dev/null
+++ b/docs/api/swagger-ui.html
@@ -0,0 +1,106 @@
+
+
+
+
+
+ PrivacyLayer API Documentation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file