ZK Void Confidential Transfer API Documentation
Base URL: https://api.zkvoid.com
Overview
The ZK Void Confidential Transfer API enables developers to integrate confidential token transfers into their dApps using Solana's SPL Token 2022 confidential transfer extensions. This API provides a trusted backend model where the service manages encryption keys and generates zero-knowledge proofs, while users maintain control of their wallets and pay their own transaction fees.
Table of Contents
Authentication
The API uses a signature-based authentication system where users sign a deterministic message with their wallet to derive encryption keys. This ensures that only the wallet owner can access their confidential balances.
Key Derivation Message
Copy Confidential Transfer Key Derivation: {wallet_public_key}
All API responses follow a consistent format:
Copy {
"success": boolean,
"data": T | null,
"error": string | null
}
Endpoints
1. Health Check
GET /health
Check if the API service is running.
Response:
Copy {
"success": true,
"data": "Confidential Transfer Service is running",
"error": null
}
2. Setup Confidential Account
POST /setup-account
Configure a token account to enable confidential transfers. This is a one-time setup per token.
Request Body:
Copy {
"owner_pubkey": "string", // Wallet public key
"signature": "string", // Base64-encoded signature for key derivation
"mint_address": "string" // SPL Token 2022 mint address
}
Response:
Copy {
"success": true,
"data": {
"transaction": "string", // Base64-encoded transaction to sign
"description": "string" // Human-readable description
},
"error": null
}
3. Deposit Tokens
POST /deposit
Convert regular tokens to confidential state.
Request Body:
Copy {
"owner_pubkey": "string", // Wallet public key
"signature": "string", // Base64-encoded signature for key derivation
"amount": number, // Amount in smallest token units
"mint_address": "string" // SPL Token 2022 mint address
}
Response:
Copy {
"success": true,
"data": {
"transaction": "string", // Base64-encoded transaction to sign
"description": "string" // Human-readable description
},
"error": null
}
4. Apply Pending Balance
POST /apply-pending-balance
Apply pending balance to make it available for transfers.
Request Body:
Copy {
"owner_pubkey": "string", // Wallet public key
"signature": "string", // Base64-encoded signature for key derivation
"mint_address": "string" // SPL Token 2022 mint address
}
Response:
Copy {
"success": true,
"data": {
"transaction": "string", // Base64-encoded transaction to sign
"description": "string" // Human-readable description
},
"error": null
}
5. Get Confidential Balance
POST /balance
Decrypt and retrieve confidential balance information.
Request Body:
Copy {
"owner_pubkey": "string", // Wallet public key
"signature": "string", // Base64-encoded signature for key derivation
"mint_address": "string" // SPL Token 2022 mint address
}
Response:
Copy {
"success": true,
"data": {
"available_balance": number, // Available confidential balance
"pending_balance": number, // Pending balance (needs to be applied)
"public_balance": number // Regular (non-confidential) balance
},
"error": null
}
6. Confidential Transfer
POST /transfer
Execute a confidential token transfer between wallets.
Request Body:
Copy {
"sender_pubkey": "string", // Sender wallet public key
"sender_signature": "string", // Base64-encoded signature for key derivation
"recipient_pubkey": "string", // Recipient wallet public key
"amount": number, // Amount in smallest token units
"mint_address": "string" // SPL Token 2022 mint address
}
Response:
Copy {
"success": true,
"data": {
"transactions": ["string"], // Array of Base64-encoded transactions
"descriptions": ["string"], // Human-readable descriptions
"is_atomic": false, // Whether transactions must be executed atomically
"additional_signers": ["string"] // Base64-encoded additional keypairs (optional)
},
"error": null
}
7. Withdraw Tokens
POST /withdraw
Convert confidential tokens back to regular state.
Request Body:
Copy {
"owner_pubkey": "string", // Wallet public key
"signature": "string", // Base64-encoded signature for key derivation
"amount": number, // Amount in smallest token units
"mint_address": "string" // SPL Token 2022 mint address
}
Response:
Copy {
"success": true,
"data": {
"transactions": ["string"], // Array of Base64-encoded transactions
"descriptions": ["string"], // Human-readable descriptions
"is_atomic": false, // Whether transactions must be executed atomically
"additional_signers": ["string"] // Base64-encoded additional keypairs (optional)
},
"error": null
}
Error Handling
The API returns appropriate HTTP status codes and error messages:
400 Bad Request : Invalid input parameters
404 Not Found : Account or resource not found
500 Internal Server Error : Server-side errors
Error Response Example:
Copy {
"success": false,
"data": null,
"error": "Invalid signature: signature verification failed"
}
Integration Guide
Prerequisites
SPL Token 2022 Mint : Your token must be created with the confidential transfer extension
Wallet Integration : Support for message signing (Phantom, Solflare, etc.)
Solana Connection : RPC connection for transaction submission
Basic Integration Steps
Setup Account : Call /setup-account
to enable confidential transfers
Deposit Tokens : Use /deposit
to convert regular tokens to confidential
Apply Pending : Call /apply-pending-balance
to make deposited tokens available
Transfer : Use /transfer
for confidential transfers between wallets
Check Balance : Use /balance
to view confidential balances
Withdraw : Use /withdraw
to convert back to regular tokens
Code Examples
JavaScript/TypeScript Integration
Copy // Configuration
const API_BASE_URL = 'https://api.zkvoid.com';
const MINT_ADDRESS = 'your_token_mint_address';
// Helper function to call API
async function callAPI<T>(endpoint: string, data: any): Promise<T> {
const response = await fetch(`${API_BASE_URL}${endpoint}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
});
if (!response.ok) {
throw new Error(`API request failed: ${response.statusText}`);
}
const result = await response.json();
if (!result.success) {
throw new Error(result.error);
}
return result.data;
}
// Generate signature for key derivation
async function getKeyDerivationSignature(wallet: any): Promise<string> {
const message = `Confidential Transfer Key Derivation: ${wallet.publicKey.toString()}`;
const messageBytes = new TextEncoder().encode(message);
const signature = await wallet.signMessage(messageBytes);
return Buffer.from(signature).toString('base64');
}
// Setup confidential account
async function setupConfidentialAccount(wallet: any, mintAddress: string) {
const signature = await getKeyDerivationSignature(wallet);
const response = await callAPI('/setup-account', {
owner_pubkey: wallet.publicKey.toString(),
signature: signature,
mint_address: mintAddress,
});
// Sign and submit the transaction
const transaction = Transaction.from(Buffer.from(response.transaction, 'base64'));
const signedTx = await wallet.signTransaction(transaction);
const txSignature = await connection.sendRawTransaction(signedTx.serialize());
return txSignature;
}
// Get confidential balance
async function getConfidentialBalance(wallet: any, mintAddress: string) {
const signature = await getKeyDerivationSignature(wallet);
const balance = await callAPI('/balance', {
owner_pubkey: wallet.publicKey.toString(),
signature: signature,
mint_address: mintAddress,
});
return {
available: balance.available_balance,
pending: balance.pending_balance,
public: balance.public_balance,
};
}
// Deposit tokens to confidential balance
async function depositTokens(wallet: any, amount: number, mintAddress: string) {
const signature = await getKeyDerivationSignature(wallet);
const response = await callAPI('/deposit', {
owner_pubkey: wallet.publicKey.toString(),
signature: signature,
amount: amount,
mint_address: mintAddress,
});
// Sign and submit the transaction
const transaction = Transaction.from(Buffer.from(response.transaction, 'base64'));
const signedTx = await wallet.signTransaction(transaction);
const txSignature = await connection.sendRawTransaction(signedTx.serialize());
return txSignature;
}
// Execute confidential transfer
async function confidentialTransfer(
wallet: any,
recipientAddress: string,
amount: number,
mintAddress: string
) {
const signature = await getKeyDerivationSignature(wallet);
const response = await callAPI('/transfer', {
sender_pubkey: wallet.publicKey.toString(),
sender_signature: signature,
recipient_pubkey: recipientAddress,
amount: amount,
mint_address: mintAddress,
});
const signatures = [];
// Execute transactions sequentially
for (let i = 0; i < response.transactions.length; i++) {
const transaction = Transaction.from(Buffer.from(response.transactions[i], 'base64'));
// Handle additional signers if present
if (response.additional_signers?.[i]) {
const signerBytes = Buffer.from(response.additional_signers[i], 'base64');
// Add additional signers to transaction
// (Implementation depends on your specific needs)
}
const signedTx = await wallet.signTransaction(transaction);
const txSignature = await connection.sendRawTransaction(signedTx.serialize());
signatures.push(txSignature);
}
return signatures;
}
React Hook Example
Copy import { useWallet } from '@solana/wallet-adapter-react';
import { useState, useCallback } from 'react';
export function useConfidentialTransfer(mintAddress: string) {
const wallet = useWallet();
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const setupAccount = useCallback(async () => {
if (!wallet.publicKey || !wallet.signMessage) {
throw new Error('Wallet not connected');
}
setLoading(true);
setError(null);
try {
const signature = await setupConfidentialAccount(wallet, mintAddress);
return signature;
} catch (err) {
const errorMsg = err instanceof Error ? err.message : 'Unknown error';
setError(errorMsg);
throw err;
} finally {
setLoading(false);
}
}, [wallet, mintAddress]);
const getBalance = useCallback(async () => {
if (!wallet.publicKey || !wallet.signMessage) {
throw new Error('Wallet not connected');
}
setLoading(true);
setError(null);
try {
const balance = await getConfidentialBalance(wallet, mintAddress);
return balance;
} catch (err) {
const errorMsg = err instanceof Error ? err.message : 'Unknown error';
setError(errorMsg);
throw err;
} finally {
setLoading(false);
}
}, [wallet, mintAddress]);
const deposit = useCallback(async (amount: number) => {
if (!wallet.publicKey || !wallet.signMessage) {
throw new Error('Wallet not connected');
}
setLoading(true);
setError(null);
try {
const signature = await depositTokens(wallet, amount, mintAddress);
return signature;
} catch (err) {
const errorMsg = err instanceof Error ? err.message : 'Unknown error';
setError(errorMsg);
throw err;
} finally {
setLoading(false);
}
}, [wallet, mintAddress]);
const transfer = useCallback(async (recipientAddress: string, amount: number) => {
if (!wallet.publicKey || !wallet.signMessage) {
throw new Error('Wallet not connected');
}
setLoading(true);
setError(null);
try {
const signatures = await confidentialTransfer(wallet, recipientAddress, amount, mintAddress);
return signatures;
} catch (err) {
const errorMsg = err instanceof Error ? err.message : 'Unknown error';
setError(errorMsg);
throw err;
} finally {
setLoading(false);
}
}, [wallet, mintAddress]);
return {
setupAccount,
getBalance,
deposit,
transfer,
loading,
error,
};
}
Python Integration Example
Copy import requests
import base64
from solana.keypair import Keypair
from solana.publickey import PublicKey
class ConfidentialTransferAPI:
def __init__(self, base_url="https://api.zkvoid.com"):
self.base_url = base_url
def _call_api(self, endpoint, data):
response = requests.post(f"{self.base_url}{endpoint}", json=data)
response.raise_for_status()
result = response.json()
if not result["success"]:
raise Exception(result["error"])
return result["data"]
def get_key_derivation_signature(self, keypair: Keypair) -> str:
message = f"Confidential Transfer Key Derivation: {keypair.public_key}"
signature = keypair.sign(message.encode())
return base64.b64encode(signature).decode()
def setup_account(self, keypair: Keypair, mint_address: str):
signature = self.get_key_derivation_signature(keypair)
return self._call_api("/setup-account", {
"owner_pubkey": str(keypair.public_key),
"signature": signature,
"mint_address": mint_address
})
def get_balance(self, keypair: Keypair, mint_address: str):
signature = self.get_key_derivation_signature(keypair)
return self._call_api("/balance", {
"owner_pubkey": str(keypair.public_key),
"signature": signature,
"mint_address": mint_address
})
def deposit(self, keypair: Keypair, amount: int, mint_address: str):
signature = self.get_key_derivation_signature(keypair)
return self._call_api("/deposit", {
"owner_pubkey": str(keypair.public_key),
"signature": signature,
"amount": amount,
"mint_address": mint_address
})
# Usage example
api = ConfidentialTransferAPI()
keypair = Keypair.generate() # Or load your keypair
mint_address = "your_token_mint_address"
# Setup account
setup_response = api.setup_account(keypair, mint_address)
print(f"Setup transaction: {setup_response['transaction']}")
# Get balance
balance = api.get_balance(keypair, mint_address)
print(f"Available: {balance['available_balance']}")
print(f"Pending: {balance['pending_balance']}")
Security Considerations
1. Key Management
The API uses deterministic key derivation from wallet signatures
Private keys are never transmitted or stored
Each wallet generates unique encryption keys per token account
2. Transaction Security
All transactions are generated server-side but signed client-side
Users maintain full control of their wallets and funds
Zero-knowledge proofs ensure transaction privacy
3. Network Security
API uses HTTPS for all communications
CORS is properly configured for web applications
Rate limiting and DDoS protection are in place
4. Best Practices
Always verify transaction contents before signing
Use secure RPC endpoints for transaction submission
Implement proper error handling and user feedback
Store sensitive data securely on the client side
Note : This API is in active development. Breaking changes may occur in future versions. Please check this documentation regularly for updates.