Skip to main content
Ghost Mode keeps the recipient out of your transaction. With private: true, DZap routes the trade through a privacy exchange. Your transaction sends the tokens through the DZap router to the exchange’s deposit address, the recipient is paid separately, and the transaction itself is still public. Ghost Mode is EVM-only and works for swaps and bridges on the pairs the privacy exchanges support. Expect it to be slower and pay out less than the recommended public route.

Setup

This example bridges 100 USDC from Arbitrum to another wallet on Base, signing with walletClient, a viem wallet client built from the private key in PRIVATE_KEY. Set RECIPIENT to that wallet; sending to your own address defeats the purpose. Amounts are strings in the token’s smallest unit, and no API key is needed.

Steps

1

Quote

getTradeQuotes with private: true returns only privacy-exchange routes. The flag is top-level, so it applies to every pair in the request. pair.quoteRates lists the routes, recommendedSource names DZap’s pick, and each route’s duration is its estimated time in seconds.
2

Approve

The DZap router on Arbitrum needs permission to move your USDC, as in a regular bridge. getAllowance reads the current allowance, and if it’s too low, approve() approves exactly the bridge amount.
3

Build

buildTradeTxn with private: true opens an order with the exchange and returns the transaction to send, private: true as confirmation, and a txId. Keep the txId: it’s the only way to track a private trade. additionalInfo holds the order details, including the exchange’s deposit address. Private trades can’t be gasless, so gasless stays false.
4

Send

trade() signs and sends the built transaction; passing it as txnData skips a second build. The transaction moves your USDC through the router to the exchange’s deposit address, and the recipient doesn’t appear in it.
5

Status

The recipient is paid after your transaction confirms, which takes minutes. Poll getTradeTxnStatus with the build’s txId until the status leaves PENDING. The status comes back empty until DZap picks up the trade, which the loop treats as “not yet”. The catch skips only the 429s that the keyless limit of 5 requests a minute can cause and rethrows anything else. The loop gives up after 30 minutes, and the check after it throws unless the status is COMPLETED.

End-to-end

API usage

The same flow over REST: send "private": true on the quote and the build, and track the trade by the build’s txId.
Approve the DZap router for the USDC first, then sign and send the transaction from the build response with your own wallet. Full reference: Quote, Build Tx, Status.

Public vs Ghost Mode

What can go wrong

  • No route: the response has no entry for the pair, or its quoteRates is empty. Private routes cover fewer pairs than public ones, and quoting again sometimes returns routes.
  • The build fails: buildTradeTxn throws. The error’s action says what to fix; TRY_ANOTHER_ROUTE means building with another route from pair.quoteRates.
  • An old build: the build opens an order with the exchange, and some orders expire an hour later (additionalInfo.expiresAt). Build again instead of sending an old build.
  • gasless: true: private trades can’t be gasless; the API answers Private swaps cannot be gasless.
  • Not completed: no status: the status stayed empty until the loop gave up. Check that your transaction confirmed and that txId comes from this build.
  • 400 Private transaction status check is not supported: you polled with the transaction hash. Use the build’s txId.
  • HTTP 429: without an API key, the trade API allows 5 requests a minute in total, shared by every endpoint, and retryAfter says how many seconds to wait. The status loop skips 429s and polls again.
See Error codes for the full catalog.
Last modified on October 1, 2026