TrenchyToolsAPI

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

FieldTypeDescription
fromobject{ chain, wallet } — the wallet that will sign the deposit. sol or rh.
toobject{ chain, address } — where the coins end up. Any supported chain; validated for that chain.
amountnumberIn the source chain's native coin (SOL on sol, ETH on rh).
routestringauto (default: exchange when enabled), direct (same chain, one signed transfer), exchange.
modestringfloat (default, market rate, ~0.1–0.4 %), fixed (locked output, 0.8 %, deposit within ~15 min), private (privacy-coin leg, 1 %, 5+ min).
venuestringauto (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.
refundAddressstringWhere 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.