# Vanish Core — LLM Reference Vanish Core is a private trading API for Solana. It allows any swap-based application to offer shielded swap execution by wrapping standard DEX aggregator transactions through a one-time wallet system. Full documentation: https://docs.vanish.trade --- ## Base URLs - **Production:** `https://core-api.vanish.trade` - **Development:** `https://core-api-dev.vanish.trade` --- ## Authentication All endpoints require: ``` x-api-key: Content-Type: application/json ``` API keys are provisioned during onboarding. Contact the Vanish team via Discord: https://discord.gg/vanishtrade --- ## Key Concepts ### Vanish Balance Users hold a balance inside Vanish's system. Funds must be deposited before trading and can be withdrawn afterwards. The balance is updated when `/commit` is called after each transaction. ### One-Time Wallet Each trade requires a fresh one-time wallet address fetched from Vanish via `GET /trade/one-time-wallet`. This is a single-use Solana wallet — **never reuse it across trades**. Set this address as the signer for all swap instructions in the unsigned transaction. ### Commit Pattern **Every transaction** (deposit, trade, or withdrawal) MUST be followed by a call to `POST /commit` with the on-chain transaction signature. This resolves the user's balance from its pending state. Without it, the balance remains frozen indefinitely. `/commit` is fully idempotent. ### Same Wallet In, Same Wallet Out Withdrawals are always routed back to the wallet that made the original deposit. This is enforced at the protocol level and cannot be bypassed. ### SOL Native Mint Address `11111111111111111111111111111111` ### Amounts All SOL amounts are in **lamports** (1 SOL = 1,000,000,000 lamports). SPL token amounts use the token's own decimal precision. --- ## Signing Several endpoints require a signed message proving ownership of `user_address`. Sign with the user's Ed25519 keypair and base64-encode the result. Timestamps must be **Unix time in milliseconds** — stale timestamps are rejected. ### Read Signing Format Used by: `/account/balances`, `/account/pending`, `/account/points`, and all account read endpoints. Pass result as `signature`. ``` By signing, I hereby agree to Vanish's Terms of Service and agree to be bound by them (docs.vanish.trade/legal/TOS) Details: read:{timestamp} ``` ### Trade Signing Format Used by: `POST /trade/create`. Pass result as `user_signature`. **All field values must exactly match what you send in the request body.** ``` By signing, I hereby agree to Vanish's Terms of Service and agree to be bound by them (docs.vanish.trade/legal/TOS) Details: trade:{source_token_address}:{target_token_address}:{amount}:{loan_additional_sol}:{timestamp}:{jito_tip_amount} ``` ### Withdraw Signing Format Used by: `POST /withdraw/create`. Pass result as `user_signature`. ``` By signing, I hereby agree to Vanish's Terms of Service and agree to be bound by them (docs.vanish.trade/legal/TOS) Details: withdraw:{token_address}:{amount}:{additional_sol}:{timestamp} ``` ### TypeScript Signing Helper ```typescript import * as nacl from 'tweetnacl'; import { Keypair } from '@solana/web3.js'; function signMessage(message: string, keypair: Keypair): string { const sig = nacl.sign.detached(new TextEncoder().encode(message), keypair.secretKey); return Buffer.from(sig).toString('base64'); } // timestamp = Date.now().toString() — milliseconds, not seconds function readSignature(timestamp: string, keypair: Keypair): string { return signMessage( `By signing, I hereby agree to Vanish's Terms of Service and agree to be bound by them (docs.vanish.trade/legal/TOS)\n\nDetails: read:${timestamp}`, keypair ); } function tradeSignature( source: string, target: string, amount: string, loanSol: string, timestamp: string, jitoTip: string, keypair: Keypair ): string { return signMessage( `By signing, I hereby agree to Vanish's Terms of Service and agree to be bound by them (docs.vanish.trade/legal/TOS)\n\nDetails: trade:${source}:${target}:${amount}:${loanSol}:${timestamp}:${jitoTip}`, keypair ); } function withdrawSignature( token: string, amount: string, additionalSol: string, timestamp: string, keypair: Keypair ): string { return signMessage( `By signing, I hereby agree to Vanish's Terms of Service and agree to be bound by them (docs.vanish.trade/legal/TOS)\n\nDetails: withdraw:${token}:${amount}:${additionalSol}:${timestamp}`, keypair ); } ``` ### Rust Signing Helper ```rust use solana_sdk::signature::{Keypair, Signer}; use base64::{engine::general_purpose, Engine as _}; // timestamp = chrono::Utc::now().timestamp_millis().to_string() — milliseconds, not seconds fn sign_message(message: &str, keypair: &Keypair) -> String { let sig = keypair.sign_message(message.as_bytes()); general_purpose::STANDARD.encode(sig.as_ref()) } fn read_signature(timestamp: &str, keypair: &Keypair) -> String { sign_message( &format!("By signing, I hereby agree to Vanish's Terms of Service and agree to be bound by them (docs.vanish.trade/legal/TOS)\n\nDetails: read:{timestamp}"), keypair, ) } fn trade_signature( source: &str, target: &str, amount: &str, loan_sol: &str, timestamp: &str, jito_tip: &str, keypair: &Keypair, ) -> String { sign_message( &format!("By signing, I hereby agree to Vanish's Terms of Service and agree to be bound by them (docs.vanish.trade/legal/TOS)\n\nDetails: trade:{source}:{target}:{amount}:{loan_sol}:{timestamp}:{jito_tip}"), keypair, ) } fn withdraw_signature( token: &str, amount: &str, additional_sol: &str, timestamp: &str, keypair: &Keypair, ) -> String { sign_message( &format!("By signing, I hereby agree to Vanish's Terms of Service and agree to be bound by them (docs.vanish.trade/legal/TOS)\n\nDetails: withdraw:{token}:{amount}:{additional_sol}:{timestamp}"), keypair, ) } ``` --- ## Endpoints ### GET /health Check API availability. No signed request required — just the API key header. **Response:** ```json { "status": "healthy" } ``` --- ### GET /deposit_address Get the deposit address for a given token. **Query parameters:** - `token_address` (required) — token mint address. Use `11111111111111111111111111111111` for native SOL. **Response:** ```json { "address": "7ozoNcVqgptbAUHjLR1vNHgEfKiE5aYufStEHzJhxKeG" } ``` Always fetch a fresh address before each deposit — addresses may rotate to ensure maximum privacy. --- ### GET /trade/one-time-wallet Get a one-time wallet address for a trade. **Response:** ```json { "address": "" } ``` **Critical:** Never reuse this address across trades. Fetch a fresh one for every single trade. --- ### POST /trade/create Submit a private swap transaction to Vanish. **Request body:** ```json { "user_address": "", "source_token_address": "", "target_token_address": "", "amount": "", "swap_transaction": "", "one_time_wallet": "
", "loan_additional_sol": "12000000", "jito_tip_amount": "1000000", "split_repay": 1, "timestamp": "", "user_signature": "", "prefer_non_jito": { "compute_unit_price": "", "compute_unit_limit": "", "custom_tip_address": "", "custom_tip_amount": "" } } ``` **Field notes:** - `swap_transaction`: base64-encoded **unsigned** transaction. The one-time wallet must be the signer for all swap instructions. Do NOT pre-sign. - `loan_additional_sol`: Recommended `12000000` (0.012 SOL). Covers ATA creation costs. Any unused amount is automatically refunded after settlement. - `jito_tip_amount`: Minimum recommended `1000000` (0.001 SOL). Only applied on the Jito route. - `split_repay`: Use `1` for most trades. Only increase if `amount_out` exceeds 0.5% of the token's total supply. Maximum `9`. On non-Jito route, always use `1`. - `prefer_non_jito`: **Omit this field entirely** to route via Jito (default). Include it to self-broadcast. **Response — Jito route (default, prefer_non_jito omitted):** ```json { "tx_id": "", "jito_bundle_id": "", "transaction": null } ``` **Response — Non-Jito route (prefer_non_jito included):** ```json { "tx_id": "", "jito_bundle_id": null, "transaction": "" } ``` **Non-Jito note:** Broadcast the `transaction` field through your own RPC. Then call `POST /commit` with the **on-chain signature** you receive from broadcasting — not the `tx_id` from this response. --- ### POST /commit Resolve a transaction's balance state. Must be called after **every** transaction — success, failure, or expiry. Fully idempotent: calling it twice with the same `tx_id` is safe and returns the same result. **Request body:** ```json { "tx_id": "" } ``` **Response:** ```json { "status": "completed", "action_type": "trade", "already_processed": false, "vanish_fee": "5000", "tx_fee": "5000", "user_address": "", "balance_changes": [ { "token_address": "", "change": "" } ] } ``` **Commit statuses:** | Status | Meaning | Action | |---|---|---| | `completed` | Settled — balance updated | Continue | | `pending` | On-chain confirmation or compliance check in progress | Poll again with the same `tx_id` | | `failed` | Transaction rejected on-chain — balance released | Inform user | | `expired` | Not confirmed within the required window — balance released | Inform user | | `rejected` | Deposit failed compliance screening — funds will be refunded to origin wallet | Do not re-attempt | If `already_processed: true` — this `tx_id` was already committed in a prior call. The response still contains the correct `status` and `balance_changes`. --- ### POST /withdraw/create Create a withdrawal transaction. Funds are always returned to the original deposit wallet. **Request body:** ```json { "user_address": "", "token_address": "", "amount": "", "additional_sol": "", "timestamp": "", "user_signature": "" } ``` **Field notes:** - `additional_sol`: Covers ATA creation at the destination wallet if it doesn't exist. Recommended `2000000` (0.002 SOL) as a safe default. - Withdrawal destination is always the user's original deposit wallet — cannot be changed. **Response:** ```json { "tx_id": "", "transaction_data": "" } ``` Broadcast `transaction_data` through your RPC, then call `POST /commit` with the on-chain signature. --- ### POST /account/points Get a user's point stats. **Request body:** ```json { "user_address": "", "timestamp": "", "signature": "" } ``` **Response:** ```json { "total": "", "by_ref": "", "last_7days": "", "multiplier": "" } ``` --- ### POST /account/balances Get a user's current Vanish balance across all tokens. **Request body:** ```json { "user_address": "", "timestamp": "", "signature": "" } ``` **Response:** ```json [ { "token_address": "", "balance": "", "program_id": "" } ] ``` --- ### GET /trade/debug Retrieve Jito bundle simulation logs for a submitted trade. Useful for debugging failed Jito submissions. **Query parameters:** - `tx_id` (required) — transaction ID of the trade to retrieve logs for. **Response:** ```json { "jito_simulation_log": "" } ``` --- ### POST /account/pending Get all transactions that were submitted but never committed. Call this on startup to detect and recover any interrupted flows. **Request body:** ```json { "user_address": "", "timestamp": "", "signature": "" } ``` **Response:** ```json [ { "tx_id": "", "action_type": "trade", "status": "pending", "created_at": "2025-01-01T00:00:00Z", "frozen_balance_changes": [{ "token_address": "", "change": "" }] } ] ``` Call `POST /commit` with each returned `tx_id` to resolve the balances. --- ## The Three Flows ### Deposit Flow ``` 1. GET /deposit_address?token_address={mint} → returns { address } 2. Transfer on-chain to returned address. Wait for confirmation. 3. POST /commit { tx_id: "" } → if status = "pending": poll /commit again with same tx_id → if status = "rejected": do not re-attempt — funds auto-refunded → if status = "completed": balance is ready for trading ``` ### Trade Flow ``` 1. GET /trade/one-time-wallet → returns { address: oneTimeWallet } 2. Build unsigned swap transaction with your DEX aggregator. Set oneTimeWallet as the signer for all swap instructions. Do NOT sign the transaction. 3. POST /trade/create { ..., swap_transaction: unsignedTx, one_time_wallet: oneTimeWallet, ... } 4a. Jito route (default): → call POST /commit { tx_id: trade.tx_id } 4b. Non-Jito route: → broadcast trade.transaction via your RPC → call POST /commit { tx_id: } ``` ### Withdraw Flow ``` 1. POST /account/balances — verify user has sufficient balance 2. POST /withdraw/create { user_address, token_address, amount, additional_sol, timestamp, user_signature } → returns { tx_id, transaction_data } 3. Broadcast transaction_data via your RPC. 4. POST /commit { tx_id: "" } ``` ### Interrupted Flow Recovery ``` On application startup: 1. POST /account/pending — fetch all uncommitted transactions 2. For each result: POST /commit { tx_id: action.tx_id } /commit is idempotent — safe to call even if already processed ``` --- ## HTTP Status Codes | Code | Meaning | Action | |---|---|---| | `200` | Success | Parse response normally | | `400` | Bad request — invalid body or parameters | Check request schema | | `401` | Unauthorized — invalid API key or signature verification failed | Check x-api-key and signing format | | `500` | Internal server error | Retry | --- ## Common Mistakes | Mistake | Consequence | Fix | |---|---|---| | Pre-signing the swap transaction | Trade fails | Submit unsigned only — Vanish signs it | | Reusing a one-time wallet | Trade fails | Fetch a fresh one-time wallet per trade | | Not calling /commit | Balance frozen indefinitely | Always call /commit after every transaction | | Polling /commit with a new tx_id | Creates a new pending state | Poll with the original tx_id | | Timestamp in seconds instead of milliseconds | 401 error | Use `Date.now()` in JS, `timestamp_millis()` in Rust | | Wrong tx_id on non-Jito commit | Balance not resolved | Use on-chain signature from broadcasting, not tx_id from /trade/create | | Mismatched signature fields | 401 error | Trade signature values must exactly match the request body fields | | Skipping /commit on failed/expired trades | Balance frozen | /commit must be called for every outcome, including failures | --- ## Recommended Parameter Values | Parameter | Recommended Value | Notes | |---|---|---| | `loan_additional_sol` | `12000000` | 0.012 SOL. Covers ATA creation. Unused amount automatically refunded. | | `jito_tip_amount` | `1000000` | Minimum recommended 0.001 SOL. Higher tips improve bundle inclusion. | | `split_repay` | `1` | Use `1` for most trades. Only increase if amount_out > 0.5% of token supply. Max `9`. | | `additional_sol` (withdraw) | `2000000` | 0.002 SOL. Covers ATA creation at destination. | --- ## Compliance Vanish enforces a **same wallet in, same wallet out** model. Every user's connected wallet is the fixed origin for all fund movements — deposits come from it, withdrawals go back to it. This cannot be bypassed. Every deposit and withdrawal is screened in real time against OFAC and global sanctions lists by Elliptic and Range. A `rejected` commit status means the transaction failed compliance screening — funds are automatically refunded to the originating wallet. Access is blocked from the U.S., U.K., OFAC-sanctioned, and other high-risk jurisdictions. --- ## Security Vanish's signing infrastructure is powered by **Turnkey** — wallet infrastructure built on hardware Trusted Execution Environments (TEEs). Private material is never stored unencrypted and is never exposed to Vanish, Turnkey, or any external process. All signing operations run inside a secure enclave, initiated only by the user's authenticated request. - Vanish never holds user private keys - No human approval in the loop — all processes are fully automated - Integrating Vanish introduces no additional custodial risk to your platform - Infrastructure audited by **Halborn** (independent blockchain security firm) - Operational security by **Groom Lake** (staffed by NSA/CIA veterans) --- ## Links - Introduction: https://docs.vanish.trade/start/introduction - Compliance: https://docs.vanish.trade/start/compliance - Security: https://docs.vanish.trade/start/security - Quickstart (TypeScript/Rust code examples): https://docs.vanish.trade/guide/quickstart - Integration Guide (full endpoint reference): https://docs.vanish.trade/guide/integration - Error Handling: https://docs.vanish.trade/guide/handling - FAQ: https://docs.vanish.trade/guide/faq - Health endpoint: https://docs.vanish.trade/api-reference/health - GET /deposit_address: https://docs.vanish.trade/api-reference/funds/deposit_address - POST /withdraw/create: https://docs.vanish.trade/api-reference/funds/withdraw - GET /trade/one-time-wallet: https://docs.vanish.trade/api-reference/trade/one-time-wallet - POST /trade/create: https://docs.vanish.trade/api-reference/trade/create - GET /trade/debug: https://docs.vanish.trade/api-reference/trade/debug - POST /commit: https://docs.vanish.trade/api-reference/commit - POST /account/balances: https://docs.vanish.trade/api-reference/account/get_balances - POST /account/points: https://docs.vanish.trade/api-reference/account/get_points - POST /account/pending: https://docs.vanish.trade/api-reference/account/get_pending_actions - Discord: https://discord.gg/vanishtrade