Create a transfer
Open a transfer and get the deposit address plus the unsigned deposit transaction from your wallet.
One request: where from, where to, how much, which route and mode (see Wallet transfers for what each means). The answer is a one-time deposit address and an unsigned deposit transaction from from.wallet; sign it, send it, then track it.
Exchange routing reduces on-chain linkability between the two wallets; it is not anonymity, and the exchange route applies its own policies.
mode: "private" adds a privacy-coin leg (slower, ~1 %). Never describe this to users as untraceable.POSThttps://api.trenchytools.lol/api/transfer
Create a transfer
| Field | Type | Description |
|---|---|---|
| from | object | { chain, wallet } — the wallet that will sign the deposit. sol or rh. |
| to | object | { chain, address } — where the coins end up. Any supported chain; validated for that chain. |
| amount | number | In the source chain's native coin (SOL on sol, ETH on rh). |
| route | string | auto (default: exchange when enabled), direct (same chain, one signed transfer), exchange. |
| mode | string | float (default, market rate, ~0.1–0.4 %), fixed (locked output, 0.8 %, deposit within ~15 min), private (privacy-coin leg, 1 %, 5+ min). |
| venue | string | auto (best execution, default), random (an open venue rolled per transfer), or a named exchange (bybit, kucoin, htx, mexc, bitget, gate, binance) to force who pays the destination — the break-wallet-cluster option. Closed venues are refused (transfer_route_off); the answer says venueHonoured. |
| refundAddress | string | Where a failed order is refunded. Default: from.wallet. |
const t = await fetch(`${BASE}/api/transfer?api-key=${API_KEY}`, {
method: 'POST', headers: { 'content-type': 'application/json' },
body: JSON.stringify({ from: { chain: 'sol', wallet: wallet.publicKey.toBase58() }, to: { chain: 'sol', address: 'DEST…' }, amount: 0.5, mode: 'float', venue: 'auto' }),
}).then((r) => r.json())
// t.deposit.address is a one-time address for this transfer; t.transaction is the unsigned deposit from your wallet
const tx = VersionedTransaction.deserialize(Buffer.from(t.transaction.transaction, 'base64'))
tx.sign([wallet])
await fetch(`${BASE}/api/sol/send?api-key=${API_KEY}`, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ transaction: Buffer.from(tx.serialize()).toString('base64') }) })
// then poll t.poll until status is "completed" (payoutHash) or a final failure
Response (202)
{
"transferId": "k9f3…", "status": "awaiting_deposit", "route": "exchange", "mode": "float", "venue": "binance",
"from": { "chain": "sol", "wallet": "…" }, "to": { "chain": "rh", "address": "0x…" }, "amount": 0.5,
"deposit": { "chain": "sol", "address": "<one-time address>", "amount": 0.5, "deadline": "2026-10-22T23:40:00Z" },
"transaction": { "chain": "sol", "encoding": "base64", "transaction": "<unsigned v0 tx: your wallet → deposit address>" },
"poll": "/api/transfer/k9f3…"
}
On Robinhood chain the deposit is transaction.evm ({chainId, from, to, value, gas}): sign and send it with viem like any trade-local transaction. A direct transfer answers 200 with the same shape and no deposit deadline.