diff --git a/docs/api/CONTRACT_ABI.md b/docs/api/CONTRACT_ABI.md new file mode 100644 index 0000000..357d5df --- /dev/null +++ b/docs/api/CONTRACT_ABI.md @@ -0,0 +1,430 @@ +# PrivacyLayer Contract ABI Documentation + +This document describes the Soroban smart contract interface for PrivacyLayer. + +## Contract Overview + +**Contract ID**: `C...` (deployed address) +**Network**: Stellar Testnet/Mainnet +**Language**: Rust (Soroban SDK) + +## Data Types + +### Bytes32 +``` +BytesN<32> - 32-byte cryptographic value (commitment, nullifier, root, etc.) +``` + +### Address +``` +Address - Stellar account address (ed25519 public key) +``` + +### Note +``` +struct Note { + nullifier: BytesN<31>, // Random 31-byte nullifier + secret: BytesN<31>, // Random 31-byte secret + amount: i128, // Deposit amount + asset: Address, // Asset contract address (XLM or USDC) +} +``` + +## Contract Functions + +### Initialize + +```rust +fn initialize( + env: Env, + admin: Address, + verifier: BytesN<32>, // Verifying key hash + denomination: i128, // Fixed deposit amount + asset: Address, // Asset to deposit (XLM or USDC) +) -> Result<(), Error> +``` + +Initializes the privacy pool contract. Must be called once after deployment. + +**Authorization**: None (initial setup) + +**Parameters**: +- `admin`: Admin address with pause/update capabilities +- `verifier`: Hash of the Groth16 verifying key +- `denomination`: Fixed deposit amount in stroops +- `asset`: Asset contract address (native XLM or USDC token) + +**Errors**: +- `AlreadyInitialized`: Contract already initialized + +--- + +### Deposit + +```rust +fn deposit( + env: Env, + commitment: BytesN<32>, // Poseidon(nullifier, secret) +) -> Result +``` + +Deposits funds into the shielded pool. + +**Authorization**: Caller must have sufficient balance + +**Parameters**: +- `commitment`: Poseidon hash of nullifier and secret + +**Returns**: Leaf index in Merkle tree + +**Events Emitted**: +- `DepositEvent { commitment, leaf_index, root }` + +**Errors**: +- `PoolPaused`: Pool is currently paused +- `InvalidCommitment`: Invalid commitment format +- `InsufficientBalance`: Caller lacks funds + +--- + +### Withdraw + +```rust +fn withdraw( + env: Env, + proof: Proof, // Groth16 proof + nullifier_hash: BytesN<32>, + recipient: Address, + relayer: Option
, + fee: i128, +) -> Result<(), Error> +``` + +Withdraws funds from the shielded pool to a new address. + +**Authorization**: None (proof-based authentication) + +**Parameters**: +- `proof`: Groth16 ZK proof components + ```rust + struct Proof { + a: G1, // pi_a: [x, y] + b: G2, // pi_b: [[x1, y1], [x2, y2]] + c: G1, // pi_c: [x, y] + } + ``` +- `nullifier_hash`: Hash of nullifier (prevents double-spend) +- `recipient`: Address to receive withdrawn funds +- `relayer`: Optional relayer address (for gasless withdrawals) +- `fee`: Fee to pay relayer (0 if no relayer) + +**Events Emitted**: +- `WithdrawEvent { nullifier_hash, recipient, relayer, fee, amount }` + +**Errors**: +- `PoolPaused`: Pool is currently paused +- `InvalidProof`: ZK proof verification failed +- `NullifierAlreadyUsed`: Double-spend attempt detected +- `InvalidNullifierHash`: Nullifier hash mismatch + +--- + +### Pause + +```rust +fn pause(env: Env) -> Result<(), Error> +``` + +Pauses the pool, preventing new deposits and withdrawals. + +**Authorization**: Admin only + +**Events Emitted**: +- `PoolPausedEvent { admin }` + +**Errors**: +- `Unauthorized`: Caller is not admin +- `AlreadyPaused`: Pool already paused + +--- + +### Unpause + +```rust +fn unpause(env: Env) -> Result<(), Error> +``` + +Unpauses the pool, allowing deposits and withdrawals. + +**Authorization**: Admin only + +**Events Emitted**: +- `PoolUnpausedEvent { admin }` + +**Errors**: +- `Unauthorized`: Caller is not admin +- `NotPaused`: Pool is not paused + +--- + +### Update Verifying Key + +```rust +fn update_verifying_key( + env: Env, + new_verifier: BytesN<32>, +) -> Result<(), Error> +``` + +Updates the Groth16 verifying key hash. + +**Authorization**: Admin only + +**⚠ WARNING**: This is a critical operation that affects all future withdrawals. + +**Events Emitted**: +- `VkUpdatedEvent { admin }` + +**Errors**: +- `Unauthorized`: Caller is not admin + +--- + +## View Functions + +### Get Merkle Root + +```rust +fn get_root(env: Env) -> BytesN<32> +``` + +Returns the current Merkle tree root. + +--- + +### Get Leaf Count + +```rust +fn get_leaf_count(env: Env) -> u32 +``` + +Returns the number of leaves in the Merkle tree. + +--- + +### Get Denomination + +```rust +fn get_denomination(env: Env) -> i128 +``` + +Returns the fixed deposit amount. + +--- + +### Get Asset + +```rust +fn get_asset(env: Env) -> Address +``` + +Returns the asset contract address. + +--- + +### Is Paused + +```rust +fn is_paused(env: Env) -> bool +``` + +Returns whether the pool is currently paused. + +--- + +### Is Nullifier Used + +```rust +fn is_nullifier_used(env: Env, nullifier_hash: BytesN<32>) -> bool +``` + +Checks if a nullifier has been used (for double-spend detection). + +--- + +### Is Commitment Valid + +```rust +fn is_commitment_valid(env: Env, commitment: BytesN<32>) -> bool +``` + +Checks if a commitment exists in the Merkle tree. + +--- + +## Events + +### DepositEvent + +```rust +#[contractevent] +struct DepositEvent { + commitment: BytesN<32>, // The deposited commitment + leaf_index: u32, // Position in Merkle tree + root: BytesN<32>, // New Merkle root after insertion +} +``` + +### WithdrawEvent + +```rust +#[contractevent] +struct WithdrawEvent { + nullifier_hash: BytesN<32>, // Nullifier hash (prevents double-spend) + recipient: Address, // Funds recipient + relayer: Option
, // Optional relayer + fee: i128, // Fee paid to relayer + amount: i128, // Amount withdrawn +} +``` + +### PoolPausedEvent + +```rust +#[contractevent] +struct PoolPausedEvent { + admin: Address, // Admin who paused +} +``` + +### PoolUnpausedEvent + +```rust +#[contractevent] +struct PoolUnpausedEvent { + admin: Address, // Admin who unpaused +} +``` + +### VkUpdatedEvent + +```rust +#[contractevent] +struct VkUpdatedEvent { + admin: Address, // Admin who updated VK +} +``` + +--- + +## Error Codes + +| Code | Name | Description | +|------|------|-------------| +| 1 | `AlreadyInitialized` | Contract already initialized | +| 2 | `NotInitialized` | Contract not initialized | +| 3 | `Unauthorized` | Caller lacks authorization | +| 4 | `PoolPaused` | Pool is currently paused | +| 5 | `NotPaused` | Pool is not paused | +| 6 | `InvalidCommitment` | Invalid commitment format | +| 7 | `InvalidProof` | ZK proof verification failed | +| 8 | `NullifierAlreadyUsed` | Nullifier already spent | +| 9 | `InvalidNullifierHash` | Nullifier hash mismatch | +| 10 | `InsufficientBalance` | Insufficient balance | +| 11 | `AlreadyPaused` | Pool already paused | +| 12 | `InvalidDenomination` | Invalid deposit amount | + +--- + +## Cryptographic Primitives + +The contract uses Stellar Protocol 25 native cryptographic functions: + +### Poseidon Hash +```rust +// Host function: poseidon2_hash +// Used for: commitment = Poseidon(nullifier, secret) +``` + +### BN254 Operations +```rust +// Host functions: bn254_mul, bn254_add, bn254_pairing +// Used for: Groth16 proof verification +``` + +--- + +## Integration Example + +### Deposit Flow + +```typescript +// 1. Generate note +const nullifier = randomBytes(31); +const secret = randomBytes(31); + +// 2. Compute commitment +const commitment = poseidon2Hash(nullifier, secret); + +// 3. Call deposit +const leafIndex = await contract.deposit({ commitment }); + +// 4. Store note securely +const note = { nullifier, secret, amount, asset }; +``` + +### Withdraw Flow + +```typescript +// 1. Sync Merkle tree +const leaves = await fetchAllDepositEvents(); +const tree = buildMerkleTree(leaves); + +// 2. Get Merkle proof +const proof = tree.getProof(leafIndex); + +// 3. Generate ZK proof +const zkProof = await generateGroth16Proof({ + nullifier, + secret, + merkleProof: proof, + recipient, + fee +}); + +// 4. Call withdraw +await contract.withdraw({ + proof: zkProof, + nullifierHash: poseidon2Hash(nullifier), + recipient, + relayer: null, + fee: 0 +}); +``` + +--- + +## Gas Costs (Estimated) + +| Operation | Estimated Gas | +|-----------|--------------| +| Deposit | ~50,000 | +| Withdraw | ~200,000 | +| Pause/Unpause | ~10,000 | +| View Functions | ~5,000 | + +*Actual costs may vary based on network conditions.* + +--- + +## Security Considerations + +1. **Privacy**: Never reveal your note (nullifier + secret) to anyone +2. **Double-Spend**: Nullifiers prevent spending the same note twice +3. **Front-running**: Transactions are processed atomically on-chain +4. **Admin Powers**: Admin can pause the pool and update verifying key +5. **Audits**: Contract should be audited before mainnet use + +--- + +*For the full contract source code, see `contracts/privacy_pool/src/contract.rs`* \ No newline at end of file diff --git a/docs/api/examples/README.md b/docs/api/examples/README.md new file mode 100644 index 0000000..88649af --- /dev/null +++ b/docs/api/examples/README.md @@ -0,0 +1,61 @@ +# PrivacyLayer Code Examples + +This directory contains code examples for integrating with PrivacyLayer. + +## Contents + +### JavaScript/TypeScript +- [deposit.ts](./typescript/deposit.ts) - Deposit flow example +- [withdraw.ts](./typescript/withdraw.ts) - Withdrawal flow example +- [relayer.ts](./typescript/relayer.ts) - Using the relayer service + +### Python +- [deposit.py](./python/deposit.py) - Deposit flow example +- [withdraw.py](./python/withdraw.py) - Withdrawal flow example + +### cURL +- [api_calls.sh](./curl/api_calls.sh) - Basic API calls + +## Quick Start + +### TypeScript + +```bash +cd typescript +npm install +npm run deposit +``` + +### Python + +```bash +cd python +pip install -r requirements.txt +python deposit.py +``` + +## Prerequisites + +1. Node.js 18+ or Python 3.9+ +2. Stellar account with testnet XLM +3. Freighter wallet (for browser) or secret key (for scripts) + +## Environment Setup + +Create a `.env` file: + +```env +# Network +SOROBAN_RPC_URL=https://soroban-testnet.stellar.org:443 +NETWORK_PASSPHRASE=Test SDF Network ; September 2015 + +# Contract +CONTRACT_ID=C... + +# Your account +SECRET_KEY=S... + +# API (optional) +API_URL=https://api-testnet.privacylayer.io +API_KEY=your_api_key +``` \ No newline at end of file diff --git a/docs/api/examples/curl/api_calls.sh b/docs/api/examples/curl/api_calls.sh new file mode 100644 index 0000000..f5ba719 --- /dev/null +++ b/docs/api/examples/curl/api_calls.sh @@ -0,0 +1,168 @@ +#!/bin/bash + +# PrivacyLayer API Examples using cURL +# +# This script demonstrates basic API calls to the PrivacyLayer API. +# +# Usage: +# chmod +x api_calls.sh +# ./api_calls.sh + +# Configuration +API_URL="${API_URL:-https://api-testnet.privacylayer.io}" +API_KEY="${API_KEY:-your_api_key_here}" + +echo "=== PrivacyLayer API Examples ===" +echo "API URL: $API_URL" +echo "" + +# 1. Health Check +echo "1. Health Check" +echo "GET /v1/health" +curl -s "$API_URL/v1/health" | jq . +echo "" +echo "---" +echo "" + +# 2. Get Pool Info +echo "2. Get Pool Info" +echo "GET /v1/pool/info" +curl -s "$API_URL/v1/pool/info" | jq . +echo "" +echo "---" +echo "" + +# 3. Get Denominations +echo "3. Get Supported Denominations" +echo "GET /v1/pool/denominations" +curl -s "$API_URL/v1/pool/denominations" | jq . +echo "" +echo "---" +echo "" + +# 4. Get Merkle Root +echo "4. Get Current Merkle Root" +echo "GET /v1/merkle/root" +curl -s "$API_URL/v1/merkle/root" | jq . +echo "" +echo "---" +echo "" + +# 5. Prepare Deposit +echo "5. Prepare Deposit" +echo "POST /v1/deposit/prepare" +curl -s -X POST "$API_URL/v1/deposit/prepare" \ + -H "Content-Type: application/json" \ + -d '{ + "nullifier": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890ab", + "secret": "0xfedcba0987654321fedcba0987654321fedcba0987654321fedcba09876543" + }' | jq . +echo "" +echo "---" +echo "" + +# 6. Estimate Deposit Cost +echo "6. Estimate Deposit Cost" +echo "POST /v1/deposit/estimate" +curl -s -X POST "$API_URL/v1/deposit/estimate" \ + -H "Content-Type: application/json" \ + -d '{"asset": "XLM"}' | jq . +echo "" +echo "---" +echo "" + +# 7. Get Deposit Events +echo "7. Get Recent Deposit Events" +echo "GET /v1/events/deposits" +curl -s "$API_URL/v1/events/deposits?skip=0&take=5" | jq . +echo "" +echo "---" +echo "" + +# 8. Get Withdrawal Events +echo "8. Get Recent Withdrawal Events" +echo "GET /v1/events/withdrawals" +curl -s "$API_URL/v1/events/withdrawals?skip=0&take=5" | jq . +echo "" +echo "---" +echo "" + +# 9. Get Merkle Proof +echo "9. Get Merkle Proof for Commitment" +echo "GET /v1/merkle/proof/:commitment" +COMMITMENT="0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef" +curl -s "$API_URL/v1/merkle/proof/$COMMITMENT" | jq . +echo "" +echo "---" +echo "" + +# 10. Generate ZK Proof +echo "10. Generate ZK Proof" +echo "POST /v1/proof/generate" +curl -s -X POST "$API_URL/v1/proof/generate" \ + -H "Content-Type: application/json" \ + -d '{ + "nullifier": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890ab", + "secret": "0xfedcba0987654321fedcba0987654321fedcba0987654321fedcba09876543", + "merkleProof": { + "root": "0x...", + "leaf": "0x...", + "pathElements": ["0x...", "0x..."], + "pathIndices": [0, 1, 0] + }, + "recipient": "GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWHF", + "fee": "10000000" + }' | jq . +echo "" +echo "---" +echo "" + +# 11. Prepare Withdrawal +echo "11. Prepare Withdrawal" +echo "POST /v1/withdraw/prepare" +curl -s -X POST "$API_URL/v1/withdraw/prepare" \ + -H "Content-Type: application/json" \ + -d '{ + "note": "0x...", + "recipient": "GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWHF", + "fee": "10000000" + }' | jq . +echo "" +echo "---" +echo "" + +# 12. Relayer Submit (requires API key) +echo "12. Submit via Relayer (requires API key)" +echo "POST /v1/relayer/submit" +curl -s -X POST "$API_URL/v1/relayer/submit" \ + -H "Content-Type: application/json" \ + -H "X-API-Key: $API_KEY" \ + -d '{ + "proof": { + "pi_a": ["0x...", "0x..."], + "pi_b": [["0x...", "0x..."], ["0x...", "0x..."]], + "pi_c": ["0x...", "0x..."] + }, + "nullifierHash": "0x...", + "recipient": "GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWHF", + "fee": "10000000" + }' | jq . +echo "" +echo "---" +echo "" + +# 13. Check Relayer Status +echo "13. Check Relayed Transaction Status" +echo "GET /v1/relayer/status/:txHash" +TX_HASH="abc123..." +curl -s "$API_URL/v1/relayer/status/$TX_HASH" | jq . +echo "" +echo "---" +echo "" + +echo "=== Examples Complete ===" +echo "" +echo "For more information, see:" +echo "- OpenAPI spec: docs/api/openapi.yaml" +echo "- Contract ABI: docs/api/CONTRACT_ABI.md" +echo "- Code examples: docs/api/examples/" \ No newline at end of file diff --git a/docs/api/examples/typescript/deposit.ts b/docs/api/examples/typescript/deposit.ts new file mode 100644 index 0000000..99fc30b --- /dev/null +++ b/docs/api/examples/typescript/deposit.ts @@ -0,0 +1,180 @@ +/** + * PrivacyLayer Deposit Example + * + * This example demonstrates how to deposit funds into the PrivacyLayer shielded pool. + */ + +import { Keypair, Server, TransactionBuilder, Operation, Asset } from 'stellar-sdk'; +import { randomBytes } from 'crypto'; + +// Configuration +const SOROBAN_RPC_URL = process.env.SOROBAN_RPC_URL || 'https://soroban-testnet.stellar.org:443'; +const CONTRACT_ID = process.env.CONTRACT_ID || ''; +const SECRET_KEY = process.env.SECRET_KEY || ''; + +// Denomination (in stroops, 1 XLM = 10,000,000 stroops) +const DENOMINATION = BigInt(1000000000); // 100 XLM + +interface Note { + nullifier: string; // 31 bytes hex + secret: string; // 31 bytes hex + amount: bigint; + asset: string; + leafIndex?: number; +} + +/** + * Generate a random 31-byte value for nullifier or secret + */ +function generateRandom31Bytes(): string { + return '0x' + randomBytes(31).toString('hex'); +} + +/** + * Compute commitment using Poseidon hash + * In production, use the actual Poseidon implementation + */ +async function computeCommitment(nullifier: string, secret: string): Promise { + // Note: In production, use @privacylayer/sdk for actual Poseidon hash + // This is a placeholder + console.log('Computing commitment from:', { nullifier, secret }); + + // Simulated commitment (replace with actual Poseidon hash) + // commitment = Poseidon(nullifier, secret) + const commitment = '0x' + randomBytes(32).toString('hex'); + return commitment; +} + +/** + * Store note securely + */ +function storeNote(note: Note): void { + // In production, encrypt and store securely + // NEVER store unencrypted notes! + console.log('\n=== STORE THIS NOTE SECURELY ==='); + console.log(JSON.stringify(note, null, 2)); + console.log('================================\n'); + + // Example: Write to encrypted file + // fs.writeFileSync('note.enc', encrypt(JSON.stringify(note))); +} + +/** + * Main deposit flow + */ +async function deposit(): Promise { + console.log('Starting PrivacyLayer deposit...\n'); + + // 1. Initialize server and keypair + const server = new Server(SOROBAN_RPC_URL); + const keypair = Keypair.fromSecret(SECRET_KEY); + const publicKey = keypair.publicKey(); + + console.log('Account:', publicKey); + + // 2. Check balance + const account = await server.loadAccount(publicKey); + const xlmBalance = account.balances.find(b => b.asset_type === 'native'); + console.log('XLM Balance:', xlmBalance?.balance || '0'); + + // 3. Generate note (nullifier + secret) + const nullifier = generateRandom31Bytes(); + const secret = generateRandom31Bytes(); + + console.log('\nGenerated note components:'); + console.log(' Nullifier:', nullifier); + console.log(' Secret:', secret); + + // 4. Compute commitment + const commitment = await computeCommitment(nullifier, secret); + console.log('\nCommitment:', commitment); + + // 5. Build deposit transaction + // Note: In production, use the actual Soroban contract invocation + console.log('\nBuilding deposit transaction...'); + + const txBuilder = new TransactionBuilder(account, { + fee: '100000', + networkPassphrase: 'Test SDF Network ; September 2015', + }); + + // Add deposit operation to the privacy pool contract + // This is a simplified example - actual implementation uses Soroban SDK + // txBuilder.addOperation( + // Operation.invokeHostFunction({ + // func: xdr.HostFunction.hostFnTypeContractFn( + // xdr.ScVal.scvBytes(Buffer.from(CONTRACT_ID, 'hex')), + // 'deposit', + // [xdr.ScVal.scvBytes(Buffer.from(commitment.slice(2), 'hex'))] + // ), + // }) + // ); + + const tx = txBuilder.setTimeout(30).build(); + + // 6. Sign and submit + console.log('\nSigning transaction...'); + tx.sign(keypair); + + console.log('Submitting to network...'); + try { + const result = await server.submitTransaction(tx); + console.log('\n✅ Deposit successful!'); + console.log('Transaction hash:', result.hash); + + // 7. Store the note + const note: Note = { + nullifier, + secret, + amount: DENOMINATION, + asset: 'XLM', + }; + + storeNote(note); + + console.log('\nDeposit complete. Keep your note safe!'); + } catch (error) { + console.error('\n❌ Deposit failed:', error); + throw error; + } +} + +// Run the deposit +deposit().catch(console.error); + +/** + * Example usage: + * + * $ ts-node deposit.ts + * + * Output: + * Starting PrivacyLayer deposit... + * + * Account: GXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + * XLM Balance: 1000.0000000 + * + * Generated note components: + * Nullifier: 0x1234567890abcdef... + * Secret: 0xfedcba0987654321... + * + * Commitment: 0xabcd1234... + * + * Building deposit transaction... + * + * Signing transaction... + * Submitting to network... + * + * ✅ Deposit successful! + * Transaction hash: abc123... + * + * === STORE THIS NOTE SECURELY === + * { + * "nullifier": "0x...", + * "secret": "0x...", + * "amount": "1000000000", + * "asset": "XLM" + * } + * ================================ + * + * Deposit complete. Keep your note safe! + */ \ No newline at end of file diff --git a/docs/api/examples/typescript/withdraw.ts b/docs/api/examples/typescript/withdraw.ts new file mode 100644 index 0000000..1e296e9 --- /dev/null +++ b/docs/api/examples/typescript/withdraw.ts @@ -0,0 +1,245 @@ +/** + * PrivacyLayer Withdrawal Example + * + * This example demonstrates how to withdraw funds from the PrivacyLayer shielded pool. + */ + +import { Keypair, Server, TransactionBuilder } from 'stellar-sdk'; + +// Configuration +const SOROBAN_RPC_URL = process.env.SOROBAN_RPC_URL || 'https://soroban-testnet.stellar.org:443'; +const CONTRACT_ID = process.env.CONTRACT_ID || ''; +const SECRET_KEY = process.env.SECRET_KEY || ''; + +interface Note { + nullifier: string; + secret: string; + amount: bigint; + asset: string; + leafIndex?: number; +} + +interface MerkleProof { + root: string; + leaf: string; + pathElements: string[]; + pathIndices: number[]; +} + +interface ZKProof { + pi_a: string[]; + pi_b: string[][]; + pi_c: string[]; +} + +/** + * Load a stored note + */ +function loadNote(): Note { + // In production, load from encrypted storage + // This is an example placeholder + return { + nullifier: '0x...', + secret: '0x...', + amount: BigInt(1000000000), + asset: 'XLM', + leafIndex: 42, + }; +} + +/** + * Fetch Merkle tree state from the indexer + */ +async function getMerkleTreeState(): Promise<{ root: string; leafCount: number }> { + const response = await fetch('https://api-testnet.privacylayer.io/v1/merkle/root'); + return response.json(); +} + +/** + * Get Merkle proof for a leaf + */ +async function getMerkleProof(commitment: string): Promise { + const response = await fetch( + `https://api-testnet.privacylayer.io/v1/merkle/proof/${commitment}` + ); + return response.json(); +} + +/** + * Generate ZK proof using the prover service + */ +async function generateZKProof(params: { + nullifier: string; + secret: string; + merkleProof: MerkleProof; + recipient: string; + relayer?: string; + fee: bigint; +}): Promise { + const response = await fetch('https://api-testnet.privacylayer.io/v1/proof/generate', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(params), + }); + return response.json(); +} + +/** + * Compute nullifier hash + */ +function computeNullifierHash(nullifier: string): string { + // In production, use Poseidon hash + // nullifierHash = Poseidon(nullifier) + return '0x' + nullifier.slice(2).padEnd(66, '0').slice(0, 66); +} + +/** + * Compute commitment from nullifier and secret + */ +function computeCommitment(nullifier: string, secret: string): string { + // In production, use Poseidon hash + // commitment = Poseidon(nullifier, secret) + return '0x' + (nullifier + secret).slice(2, 66); +} + +/** + * Main withdrawal flow + */ +async function withdraw(recipientAddress: string): Promise { + console.log('Starting PrivacyLayer withdrawal...\n'); + + // 1. Initialize + const server = new Server(SOROBAN_RPC_URL); + const keypair = Keypair.fromSecret(SECRET_KEY); + const publicKey = keypair.publicKey(); + + console.log('Account:', publicKey); + console.log('Recipient:', recipientAddress); + + // 2. Load the stored note + const note = loadNote(); + console.log('\nLoaded note:'); + console.log(' Amount:', note.amount.toString(), 'stroops'); + console.log(' Asset:', note.asset); + + // 3. Compute commitment + const commitment = computeCommitment(note.nullifier, note.secret); + console.log('\nCommitment:', commitment); + + // 4. Get Merkle tree state + console.log('\nFetching Merkle tree state...'); + const treeState = await getMerkleTreeState(); + console.log('Current root:', treeState.root); + console.log('Leaf count:', treeState.leafCount); + + // 5. Get Merkle proof + console.log('\nFetching Merkle proof...'); + const merkleProof = await getMerkleProof(commitment); + console.log('Merkle proof root:', merkleProof.root); + + // 6. Generate ZK proof + console.log('\nGenerating ZK proof...'); + const fee = BigInt(10000000); // 1 XLM fee (optional) + const zkProof = await generateZKProof({ + nullifier: note.nullifier, + secret: note.secret, + merkleProof, + recipient: recipientAddress, + fee, + }); + console.log('ZK proof generated'); + + // 7. Build withdrawal transaction + console.log('\nBuilding withdrawal transaction...'); + + const nullifierHash = computeNullifierHash(note.nullifier); + + const account = await server.loadAccount(publicKey); + const txBuilder = new TransactionBuilder(account, { + fee: '200000', + networkPassphrase: 'Test SDF Network ; September 2015', + }); + + // Add withdraw operation to the privacy pool contract + // This is a simplified example - actual implementation uses Soroban SDK + // txBuilder.addOperation( + // Operation.invokeHostFunction({ + // func: xdr.HostFunction.hostFnTypeContractFn( + // xdr.ScVal.scvBytes(Buffer.from(CONTRACT_ID, 'hex')), + // 'withdraw', + // [ + // proofToScVal(zkProof), + // xdr.ScVal.scvBytes(Buffer.from(nullifierHash.slice(2), 'hex')), + // xdr.ScVal.scvAddress(recipientAddress), + // xdr.ScVal.scvOption(null), + // xdr.ScVal.scvI128(new xdr.Int128Parts({ lo: fee })), + // ] + // ), + // }) + // ); + + const tx = txBuilder.setTimeout(30).build(); + + // 8. Sign and submit + console.log('\nSigning transaction...'); + tx.sign(keypair); + + console.log('Submitting to network...'); + try { + const result = await server.submitTransaction(tx); + console.log('\n✅ Withdrawal successful!'); + console.log('Transaction hash:', result.hash); + console.log('Amount withdrawn:', note.amount.toString(), 'stroops'); + console.log('Recipient:', recipientAddress); + + // Delete the used note + console.log('\n⚠️ Note has been spent. Remove from storage.'); + } catch (error) { + console.error('\n❌ Withdrawal failed:', error); + throw error; + } +} + +// Run the withdrawal +const recipient = process.argv[2] || 'GXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'; +withdraw(recipient).catch(console.error); + +/** + * Example usage: + * + * $ ts-node withdraw.ts GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWHF + * + * Output: + * Starting PrivacyLayer withdrawal... + * + * Account: G... + * Recipient: GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWHF + * + * Loaded note: + * Amount: 1000000000 stroops + * Asset: XLM + * + * Commitment: 0x... + * + * Fetching Merkle tree state... + * Current root: 0x... + * Leaf count: 1234 + * + * Fetching Merkle proof... + * Merkle proof root: 0x... + * + * Generating ZK proof... + * ZK proof generated + * + * Building withdrawal transaction... + * + * Signing transaction... + * Submitting to network... + * + * ✅ Withdrawal successful! + * Transaction hash: abc123... + * Amount withdrawn: 1000000000 stroops + * Recipient: GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWHF + * + * ⚠️ Note has been spent. Remove from storage. + */ \ No newline at end of file diff --git a/docs/api/openapi.yaml b/docs/api/openapi.yaml new file mode 100644 index 0000000..991fe89 --- /dev/null +++ b/docs/api/openapi.yaml @@ -0,0 +1,698 @@ +openapi: 3.1.0 +info: + title: PrivacyLayer API + description: | + PrivacyLayer is a compliance-forward private transaction protocol on Stellar Soroban. + + This API provides endpoints for: + - Managing shielded pool deposits and withdrawals + - Generating zero-knowledge proofs + - Querying contract state and events + - Relayer services for gasless transactions + + ## Authentication + Most endpoints are public. Relayer endpoints require API key authentication. + + ## Rate Limits + - Public endpoints: 100 requests/minute + - Relayer endpoints: 1000 requests/minute + + ## Base URLs + - Testnet: `https://api-testnet.privacylayer.io` + - Mainnet: `https://api.privacylayer.io` + 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://api-testnet.privacylayer.io + description: Testnet + - url: https://api.privacylayer.io + description: Mainnet + +tags: + - name: Pool + description: Shielded pool operations + - name: Deposit + description: Deposit operations + - name: Withdraw + description: Withdrawal operations + - name: Proof + description: ZK proof generation + - name: Relayer + description: Relayer services + - name: Events + description: Event queries + - name: Health + description: Service health checks + +paths: + /v1/pool/info: + get: + tags: [Pool] + summary: Get pool information + description: Returns current pool state including Merkle root, total deposits, and pause status + operationId: getPoolInfo + responses: + '200': + description: Pool information + content: + application/json: + schema: + $ref: '#/components/schemas/PoolInfo' + example: + merkleRoot: "0x1234567890abcdef..." + leafCount: 1234 + denomination: "1000000000" + asset: "XLM" + isPaused: false + totalDeposits: 1234 + totalWithdrawals: 567 + + /v1/pool/denominations: + get: + tags: [Pool] + summary: Get supported denominations + description: Returns list of supported deposit denominations + operationId: getDenominations + responses: + '200': + description: List of denominations + content: + application/json: + schema: + type: object + properties: + denominations: + type: array + items: + $ref: '#/components/schemas/Denomination' + + /v1/deposit/prepare: + post: + tags: [Deposit] + summary: Prepare deposit + description: | + Generates commitment and prepares deposit transaction. + + The client should: + 1. Call this endpoint to get commitment + 2. Sign the transaction with their wallet + 3. Submit the transaction to Stellar network + operationId: prepareDeposit + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/PrepareDepositRequest' + responses: + '200': + description: Deposit preparation data + content: + application/json: + schema: + $ref: '#/components/schemas/PrepareDepositResponse' + + /v1/deposit/estimate: + post: + tags: [Deposit] + summary: Estimate deposit cost + description: Estimates gas cost for deposit transaction + operationId: estimateDeposit + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/EstimateDepositRequest' + responses: + '200': + description: Cost estimation + content: + application/json: + schema: + $ref: '#/components/schemas/CostEstimation' + + /v1/withdraw/prepare: + post: + tags: [Withdraw] + summary: Prepare withdrawal + description: | + Generates ZK proof and prepares withdrawal transaction. + + The client should: + 1. Provide note and recipient address + 2. System generates Merkle proof and ZK proof + 3. Sign and submit the transaction + operationId: prepareWithdraw + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/PrepareWithdrawRequest' + responses: + '200': + description: Withdrawal preparation data + content: + application/json: + schema: + $ref: '#/components/schemas/PrepareWithdrawResponse' + + /v1/withdraw/estimate: + post: + tags: [Withdraw] + summary: Estimate withdrawal cost + description: Estimates gas cost for withdrawal transaction + operationId: estimateWithdraw + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/EstimateWithdrawRequest' + responses: + '200': + description: Cost estimation + content: + application/json: + schema: + $ref: '#/components/schemas/CostEstimation' + + /v1/proof/generate: + post: + tags: [Proof] + summary: Generate ZK proof + description: Generates a Groth16 proof for withdrawal + operationId: generateProof + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/GenerateProofRequest' + responses: + '200': + description: Generated proof + content: + application/json: + schema: + $ref: '#/components/schemas/ZKProof' + + /v1/merkle/root: + get: + tags: [Proof] + summary: Get current Merkle root + description: Returns the current Merkle tree root + operationId: getMerkleRoot + responses: + '200': + description: Merkle root + content: + application/json: + schema: + type: object + properties: + root: + type: string + description: 32-byte hex string + example: "0x1234567890abcdef..." + leafCount: + type: integer + example: 1234 + + /v1/merkle/proof/{commitment}: + get: + tags: [Proof] + summary: Get Merkle proof + description: Generates Merkle inclusion proof for a commitment + operationId: getMerkleProof + parameters: + - name: commitment + in: path + required: true + schema: + type: string + description: 32-byte commitment hex string + responses: + '200': + description: Merkle proof + content: + application/json: + schema: + $ref: '#/components/schemas/MerkleProof' + '404': + description: Commitment not found + + /v1/relayer/submit: + post: + tags: [Relayer] + summary: Submit transaction via relayer + description: | + Submits a withdrawal transaction through the relayer service. + The relayer pays gas fees and takes a fee from the withdrawal. + + Requires API key authentication. + operationId: relayerSubmit + security: + - ApiKeyAuth: [] + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/RelayerSubmitRequest' + responses: + '200': + description: Transaction submitted + content: + application/json: + schema: + $ref: '#/components/schemas/RelayerSubmitResponse' + '400': + description: Invalid request + '401': + description: Unauthorized + + /v1/relayer/status/{txHash}: + get: + tags: [Relayer] + summary: Get relayed transaction status + description: Returns status of a relayed transaction + operationId: relayerStatus + parameters: + - name: txHash + in: path + required: true + schema: + type: string + responses: + '200': + description: Transaction status + content: + application/json: + schema: + $ref: '#/components/schemas/RelayerStatus' + + /v1/events/deposits: + get: + tags: [Events] + summary: Query deposit events + description: Returns paginated list of deposit events + operationId: getDepositEvents + parameters: + - name: skip + in: query + schema: + type: integer + default: 0 + - name: take + in: query + schema: + type: integer + default: 20 + maximum: 100 + - name: fromLedger + in: query + schema: + type: integer + description: Start from this ledger + - name: toLedger + in: query + schema: + type: integer + description: End at this ledger + responses: + '200': + description: List of deposit events + content: + application/json: + schema: + type: object + properties: + events: + type: array + items: + $ref: '#/components/schemas/DepositEvent' + total: + type: integer + + /v1/events/withdrawals: + get: + tags: [Events] + summary: Query withdrawal events + description: Returns paginated list of withdrawal events + operationId: getWithdrawalEvents + parameters: + - name: skip + in: query + schema: + type: integer + default: 0 + - name: take + in: query + schema: + type: integer + default: 20 + maximum: 100 + - name: recipient + in: query + schema: + type: string + description: Filter by recipient address + responses: + '200': + description: List of withdrawal events + content: + application/json: + schema: + type: object + properties: + events: + type: array + items: + $ref: '#/components/schemas/WithdrawalEvent' + total: + type: integer + + /v1/health: + get: + tags: [Health] + summary: Health check + description: Returns service health status + operationId: healthCheck + responses: + '200': + description: Service is healthy + content: + application/json: + schema: + $ref: '#/components/schemas/HealthStatus' + +components: + securitySchemes: + ApiKeyAuth: + type: apiKey + in: header + name: X-API-Key + + schemas: + PoolInfo: + type: object + properties: + merkleRoot: + type: string + description: Current Merkle tree root (32-byte hex) + leafCount: + type: integer + description: Number of leaves in Merkle tree + denomination: + type: string + description: Fixed deposit amount in stroops + asset: + type: string + enum: [XLM, USDC] + isPaused: + type: boolean + totalDeposits: + type: integer + totalWithdrawals: + type: integer + verifyingKeyHash: + type: string + description: Hash of current verifying key + + Denomination: + type: object + properties: + amount: + type: string + description: Amount in stroops + asset: + type: string + minDeposit: + type: string + maxDeposit: + type: string + + PrepareDepositRequest: + type: object + required: + - nullifier + - secret + properties: + nullifier: + type: string + description: Random 31-byte nullifier (hex) + secret: + type: string + description: Random 31-byte secret (hex) + + PrepareDepositResponse: + type: object + properties: + commitment: + type: string + description: Poseidon(nullifier, secret) - 32-byte hex + expectedLeafIndex: + type: integer + transactionXDR: + type: string + description: Unsigned transaction XDR + + EstimateDepositRequest: + type: object + properties: + asset: + type: string + enum: [XLM, USDC] + + EstimateWithdrawRequest: + type: object + properties: + asset: + type: string + enum: [XLM, USDC] + useRelayer: + type: boolean + + CostEstimation: + type: object + properties: + gasEstimate: + type: integer + feeEstimate: + type: string + description: Fee in stroops + + PrepareWithdrawRequest: + type: object + required: + - note + - recipient + properties: + note: + type: string + description: Encrypted note (hex) + recipient: + type: string + description: Stellar address to receive funds + relayer: + type: string + description: Optional relayer address + fee: + type: string + description: Fee for relayer (stroops) + + PrepareWithdrawResponse: + type: object + properties: + nullifierHash: + type: string + merkleProof: + $ref: '#/components/schemas/MerkleProof' + zkProof: + $ref: '#/components/schemas/ZKProof' + transactionXDR: + type: string + + GenerateProofRequest: + type: object + required: + - nullifier + - secret + - merkleProof + - recipient + properties: + nullifier: + type: string + secret: + type: string + merkleProof: + $ref: '#/components/schemas/MerkleProof' + recipient: + type: string + relayer: + type: string + fee: + type: string + + MerkleProof: + type: object + properties: + root: + type: string + leaf: + type: string + pathElements: + type: array + items: + type: string + pathIndices: + type: array + items: + type: integer + + ZKProof: + type: object + properties: + pi_a: + type: array + items: + type: string + pi_b: + type: array + items: + type: array + items: + type: string + pi_c: + type: array + items: + type: string + protocol: + type: string + enum: [groth16] + curve: + type: string + enum: [bn128] + + DepositEvent: + type: object + properties: + id: + type: string + commitment: + type: string + leafIndex: + type: integer + root: + type: string + txHash: + type: string + timestamp: + type: string + format: date-time + ledger: + type: integer + + WithdrawalEvent: + type: object + properties: + id: + type: string + nullifierHash: + type: string + recipient: + type: string + relayer: + type: string + nullable: true + fee: + type: string + amount: + type: string + txHash: + type: string + timestamp: + type: string + format: date-time + ledger: + type: integer + + RelayerSubmitRequest: + type: object + required: + - proof + - nullifierHash + - recipient + properties: + proof: + $ref: '#/components/schemas/ZKProof' + nullifierHash: + type: string + recipient: + type: string + relayer: + type: string + fee: + type: string + + RelayerSubmitResponse: + type: object + properties: + txHash: + type: string + status: + type: string + enum: [pending, submitted, confirmed, failed] + fee: + type: string + + RelayerStatus: + type: object + properties: + txHash: + type: string + status: + type: string + enum: [pending, submitted, confirmed, failed] + confirmedAt: + type: string + format: date-time + nullable: true + ledger: + type: integer + nullable: true + + HealthStatus: + type: object + properties: + status: + type: string + enum: [healthy, degraded, unhealthy] + version: + type: string + uptime: + type: integer + description: Uptime in seconds + components: + type: object + properties: + database: + type: string + enum: [connected, disconnected] + sorobanRpc: + type: string + enum: [connected, disconnected] + prover: + type: string + enum: [available, unavailable] + timestamp: + type: string + format: date-time \ 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..0947935 --- /dev/null +++ b/docs/api/postman_collection.json @@ -0,0 +1,360 @@ +{ + "info": { + "name": "PrivacyLayer API", + "description": "PrivacyLayer is a compliance-forward private transaction protocol on Stellar Soroban.\n\nBase URLs:\n- Testnet: https://api-testnet.privacylayer.io\n- Mainnet: https://api.privacylayer.io", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" + }, + "auth": { + "type": "apikey", + "apikey": [ + { + "key": "key", + "value": "X-API-Key", + "type": "string" + }, + { + "key": "value", + "value": "{{api_key}}", + "type": "string" + }, + { + "key": "in", + "value": "header", + "type": "string" + } + ] + }, + "event": [ + { + "listen": "prerequest", + "script": { + "type": "text/javascript", + "exec": [ + "" + ] + } + } + ], + "variable": [ + { + "key": "base_url", + "value": "https://api-testnet.privacylayer.io" + }, + { + "key": "api_key", + "value": "your_api_key_here" + } + ], + "item": [ + { + "name": "Pool", + "item": [ + { + "name": "Get Pool Info", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{base_url}}/v1/pool/info", + "host": ["{{base_url}}"], + "path": ["v1", "pool", "info"] + }, + "description": "Returns current pool state including Merkle root, total deposits, and pause status" + }, + "response": [ + { + "name": "Success Response", + "originalRequest": { + "method": "GET", + "url": { + "raw": "{{base_url}}/v1/pool/info" + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "body": "{\n \"merkleRoot\": \"0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef\",\n \"leafCount\": 1234,\n \"denomination\": \"1000000000\",\n \"asset\": \"XLM\",\n \"isPaused\": false,\n \"totalDeposits\": 1234,\n \"totalWithdrawals\": 567\n}" + } + ] + }, + { + "name": "Get Denominations", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{base_url}}/v1/pool/denominations", + "host": ["{{base_url}}"], + "path": ["v1", "pool", "denominations"] + } + } + } + ] + }, + { + "name": "Deposit", + "item": [ + { + "name": "Prepare Deposit", + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"nullifier\": \"0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890ab\",\n \"secret\": \"0xfedcba0987654321fedcba0987654321fedcba0987654321fedcba09876543\"\n}" + }, + "url": { + "raw": "{{base_url}}/v1/deposit/prepare", + "host": ["{{base_url}}"], + "path": ["v1", "deposit", "prepare"] + } + } + }, + { + "name": "Estimate Deposit Cost", + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"asset\": \"XLM\"\n}" + }, + "url": { + "raw": "{{base_url}}/v1/deposit/estimate", + "host": ["{{base_url}}"], + "path": ["v1", "deposit", "estimate"] + } + } + } + ] + }, + { + "name": "Withdraw", + "item": [ + { + "name": "Prepare Withdrawal", + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"note\": \"0x...encrypted_note...\",\n \"recipient\": \"GXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\",\n \"fee\": \"10000000\"\n}" + }, + "url": { + "raw": "{{base_url}}/v1/withdraw/prepare", + "host": ["{{base_url}}"], + "path": ["v1", "withdraw", "prepare"] + } + } + }, + { + "name": "Estimate Withdrawal Cost", + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"asset\": \"XLM\",\n \"useRelayer\": false\n}" + }, + "url": { + "raw": "{{base_url}}/v1/withdraw/estimate", + "host": ["{{base_url}}"], + "path": ["v1", "withdraw", "estimate"] + } + } + } + ] + }, + { + "name": "Proof", + "item": [ + { + "name": "Generate ZK Proof", + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"nullifier\": \"0x...\",\n \"secret\": \"0x...\",\n \"merkleProof\": {\n \"root\": \"0x...\",\n \"leaf\": \"0x...\",\n \"pathElements\": [\"0x...\", \"0x...\"],\n \"pathIndices\": [0, 1, 0]\n },\n \"recipient\": \"G...\",\n \"fee\": \"10000000\"\n}" + }, + "url": { + "raw": "{{base_url}}/v1/proof/generate", + "host": ["{{base_url}}"], + "path": ["v1", "proof", "generate"] + } + } + }, + { + "name": "Get Merkle Root", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{base_url}}/v1/merkle/root", + "host": ["{{base_url}}"], + "path": ["v1", "merkle", "root"] + } + } + }, + { + "name": "Get Merkle Proof", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{base_url}}/v1/merkle/proof/:commitment", + "host": ["{{base_url}}"], + "path": ["v1", "merkle", "proof", ":commitment"], + "variable": [ + { + "key": "commitment", + "value": "0x1234567890abcdef..." + } + ] + } + } + } + ] + }, + { + "name": "Relayer", + "item": [ + { + "name": "Submit via Relayer", + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "X-API-Key", + "value": "{{api_key}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"proof\": {\n \"pi_a\": [\"0x...\", \"0x...\"],\n \"pi_b\": [[\"0x...\", \"0x...\"], [\"0x...\", \"0x...\"]],\n \"pi_c\": [\"0x...\", \"0x...\"]\n },\n \"nullifierHash\": \"0x...\",\n \"recipient\": \"G...\",\n \"fee\": \"10000000\"\n}" + }, + "url": { + "raw": "{{base_url}}/v1/relayer/submit", + "host": ["{{base_url}}"], + "path": ["v1", "relayer", "submit"] + } + } + }, + { + "name": "Get Relayer Status", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{base_url}}/v1/relayer/status/:txHash", + "host": ["{{base_url}}"], + "path": ["v1", "relayer", "status", ":txHash"], + "variable": [ + { + "key": "txHash", + "value": "abc123..." + } + ] + } + } + } + ] + }, + { + "name": "Events", + "item": [ + { + "name": "Get Deposit Events", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{base_url}}/v1/events/deposits?skip=0&take=20", + "host": ["{{base_url}}"], + "path": ["v1", "events", "deposits"], + "query": [ + { + "key": "skip", + "value": "0" + }, + { + "key": "take", + "value": "20" + } + ] + } + } + }, + { + "name": "Get Withdrawal Events", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{base_url}}/v1/events/withdrawals?skip=0&take=20", + "host": ["{{base_url}}"], + "path": ["v1", "events", "withdrawals"], + "query": [ + { + "key": "skip", + "value": "0" + }, + { + "key": "take", + "value": "20" + } + ] + } + } + } + ] + }, + { + "name": "Health", + "item": [ + { + "name": "Health Check", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{base_url}}/v1/health", + "host": ["{{base_url}}"], + "path": ["v1", "health"] + } + } + } + ] + } + ] +} \ No newline at end of file