Diffusal

Quick Start

Minimal integration sequence for API + on-chain writes

For Traders

If you are a trader using the Diffusal trading app, you do not need to interact with the API directly. The app handles authentication, order submission, and portfolio management through its interface.

See the Authentication Guide for step-by-step instructions on wallet setup, placing trades, managing positions, and more.

The sections below are for API integrators — market makers, bot operators, and developers building on the Diffusal protocol.


1. Connect to API

  • Use the Monad testnet API base URL: https://api.testnet.diffusal.xyz.
  • Validate connectivity with GET /api/health.
  • Pull REST schemas from https://docs.diffusal.xyz/openapi.json.

2. Read Market Data

Start with:

  • GET /api/markets
  • GET /api/markets/orderbook/:symbol
  • GET /api/markets/ticker/:symbol
  • GET /api/markets/trades/:symbol
  • GET /api/markets/pairs

3. Prepare Transaction Params

Use helper endpoints for deterministic IDs and tick conversions:

  • GET /api/helpers/series-id
  • GET /api/helpers/pair-id
  • GET /api/helpers/tick-decimals/:pairId
  • GET /api/helpers/tick-to-price
  • GET /api/helpers/price-to-tick

4. Authenticate (If Needed)

Most trading and account endpoints require a session token. For programmatic integrations and market makers, use the Better Auth SIWE flow: call POST /api/auth/siwe/nonce with { walletAddress, chainId } to receive a nonce, sign a SIWE message with your wallet, then call POST /api/auth/siwe/verify with { message, signature, walletAddress, chainId } to receive a bearer token. Attach it as Authorization: Bearer <token> on subsequent requests. Nonces expire after 5 minutes.

SIWE is the only sign-in path — sessions use a 7-day TTL. For full details, curl examples, and common auth errors, see the Authentication page.

Protected endpoint groups available after authentication:

  • GET /api/mm/orders
  • POST /api/mm/orders/place
  • GET /api/mm/quotes
  • POST /api/mm/quotes/submit
  • GET /api/portfolio/list
  • POST /api/portfolio/create
  • POST /api/strategies/prepare
  • POST /api/strategies/place

5. Subscribe to Live Data

Connect to /ws/public for live market data and subscribe to channels such as <symbol>@depth, <symbol>@ticker, <symbol>@trade, or global@rfq. For RFQ taker quotes, authenticate on /ws/private and subscribe to rfq.quotes.<requestId>. Hydrate first with GET /api/rfq/active-auctions (public) or GET /api/rfq/quotes/:requestId (taker). Market makers connect to /ws/rfq/mm with Authorization: Bearer <session-token> in the handshake header. See WebSocket Streams for the multiplexed channel model, auth flow, and exact generated reference links.

6. Execute Writes On-Chain or via Managed Endpoints

Use wallet-signed transactions against protocol contracts (depositToPortfolio, placeOrderSigned, fillRfqQuoteInPortfolio, etc.). For order-book placement, the canonical MM order-maintenance flow is:

  1. POST /api/mm/orders/place without a signature → returns mode: "typed_data" with the EIP-712 PlaceOrder payload
  2. Sign the typed data with the maker wallet
  3. POST /api/mm/orders/place with the signature → returns mode: "relay" with encoded calldata
  4. Send the transaction on-chain — call the contract at relay.to with relay.calldata from your wallet

For MMM wallets, the unsigned request can omit portfolioId. The API will normalize the request to the MMM cash convention and return the correct typed data while the protocol auto-routes the internal trading portfolio by series.

Important: The API never submits transactions on-chain. It only returns the calldata. Your wallet must send the final transaction.

For signed order maintenance, placeOrderSigned(...), cancelOrderSigned(...), replaceOrderSigned(...), batchPlaceOrders(...), batchCancelOrders(...), and batchReplaceOrders(...) are the canonical MM path — each takes the SignedPlaceOrder / SignedCancelOrder / SignedReplaceOrder request struct. registerOrderInPortfolio(...), registerOrderInPortfolioWithSeriesParams(...), and cancelOrderById(...) remain available as low-level direct-write paths for maker-signed transactions. If the series may not exist yet, use registerOrderInPortfolioWithSeriesParams(...) — the only entry point that can lazily create a series on first placement.

See scripts/examples/mm-orderbook-example.ts for a complete working example including authentication, order placement, and on-chain submission.

For RFQ, request auction quotes via POST /api/rfq/request before on-chain fill. If your integration uses the authenticated API trading layer, manage MM orders and quotes through /api/mm/*, portfolio helpers through /api/portfolio/*, and multi-leg strategy helpers through /api/strategies/*.

TypeScript SDK Example

The following snippet demonstrates SIWE authentication, fetching market data, and subscribing to a live WebSocket channel. Copy-paste and run with npx tsx script.ts (requires viem and ws packages).

import { createWalletClient, http } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { monad } from "viem/chains";
import WebSocket from "ws";

const API = "https://api.testnet.diffusal.xyz";
const account = privateKeyToAccount("0xYOUR_PRIVATE_KEY");

async function main() {
  // --- 1. SIWE Authentication ---
  // Get a nonce
  const nonceRes = await fetch(`${API}/api/auth/siwe/nonce`, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ walletAddress: account.address, chainId: monad.id }),
  });
  const { nonce } = (await nonceRes.json()) as { nonce: string };

  // Build and sign the SIWE message
  const message = [
    `api.testnet.diffusal.xyz wants you to sign in with your Ethereum account:`,
    account.address,
    "",
    "Sign in to Diffusal",
    "",
    `URI: ${API}`,
    `Version: 1`,
    `Chain ID: ${monad.id}`,
    `Nonce: ${nonce}`,
    `Issued At: ${new Date().toISOString()}`,
  ].join("\n");

  const signature = await account.signMessage({ message });

  // Verify and receive a bearer token
  const verifyRes = await fetch(`${API}/api/auth/siwe/verify`, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      message,
      signature,
      walletAddress: account.address,
      chainId: monad.id,
    }),
  });
  const { token } = (await verifyRes.json()) as { token: string };
  console.log("Authenticated. Token:", token.slice(0, 12) + "...");

  // --- 2. First API Call: Get trading pairs ---
  const pairsRes = await fetch(`${API}/api/markets/pairs`);
  const pairs = await pairsRes.json();
  console.log("Trading pairs:", pairs);

  // --- 3. WebSocket Subscription ---
  const ws = new WebSocket(`wss://api.testnet.diffusal.xyz/ws/public`);

  ws.on("open", () => {
    // Subscribe to BTC index prices and global RFQ events
    ws.send(
      JSON.stringify({
        method: "SUBSCRIBE",
        params: ["btc-usdt@index", "global@rfq"],
        id: 1,
      }),
    );
    console.log("Subscribed to btc-usdt@index + global@rfq");
  });

  ws.on("message", (raw) => {
    const msg = JSON.parse(raw.toString());
    console.log("WS:", msg.stream, msg.data);
  });
}

main().catch(console.error);

Next

On this page