Diffusal

On-Chain Guide

Current contract interaction patterns for app integrations

All state changes are executed on-chain by the caller wallet. Use API/helpers for discovery, then send contract transactions directly. The API also exposes authenticated /api/mm/*, /api/portfolio/*, and /api/strategies/* workflows, but direct contract interaction remains the canonical protocol path.

Core User Flows

FlowContractFunction
Deposit collateralDiffusalCollateralVaultdepositToPortfolio(uint256 portfolioId, uint256 amount)
Withdraw collateralDiffusalCollateralVaultwithdrawFromPortfolio(uint256 portfolioId, uint256 amount)
Place signed orderDiffusalOptionsOrderBookplaceOrderWithSignature(...)
Place signed lazy-seriesDiffusalOptionsOrderBookplaceOrderWithSeriesParamsWithSignature(...)
Cancel signed orderDiffusalOptionsOrderBookcancelOrderWithSignature(bytes32 orderId, uint256 nonce, uint256 deadline, bytes signature)
Fill RFQDiffusalOptionsRFQfillRfqQuoteInPortfolio(...)
Create portfolioDiffusalPortfolioManagercreatePortfolio()

Order Book Flow (Current)

  1. Build/find seriesId.
  2. Fetch pair tick precision (or derive from helper endpoint).
  3. Build PlaceOrder typed data or request it from the authenticated managed order placement endpoint.
  4. Maker signs the typed data.
  5. Submit the signature on-chain via placeOrderWithSignature(...).
  6. Crossed orders are identified for execution.
  7. Settlement is finalized on-chain via settleMatchByOrderId(makerOrderId, takerOrderId, fillAmount).

If the series may not exist yet, use placeOrderWithSeriesParamsWithSignature(...) instead so signature validation and lazy series creation happen in one transaction.

Direct registration via registerOrderInPortfolio(...) or registerOrderInPortfolioWithSeriesParams(...) still exists as a lower-level alternative, but it is not the recommended default flow for current integrations.

Key change vs legacy model:

  • Placement and cancellation are signature-first, while settlement is order-ID based (stored order data), not full-signature payload settlement.

Canonical Signed Placement Example

The authenticated API can return the exact typed-data payload used by the order book. Submit the unsigned request to /api/mm/orders/place, sign the returned typedData, then call placeOrderWithSignature(...).

{
  "seriesId": "0x8f1c9d7b6a4e2f00112233445566778899aabbccddeeff001122334455667788",
  "portfolioId": "0",
  "isBuy": true,
  "tick": "150",
  "size": "1000000000000000000",
  "expiry": "1762512000",
  "nonce": "7",
  "deadline": "1762512300"
}
import { diffusalOptionsOrderBookAbi } from "@diffusal/contracts/deployment/abis";

const orderRequest = {
  seriesId:
    "0x8f1c9d7b6a4e2f00112233445566778899aabbccddeeff001122334455667788",
  portfolioId: "0",
  isBuy: true,
  tick: "150",
  size: "1000000000000000000",
  expiry: "1762512000",
  nonce: "7",
  deadline: "1762512300",
};

const placementResponse = await fetch("/api/mm/orders/place", {
  method: "POST",
  headers: {
    "content-type": "application/json",
    authorization: `Bearer ${token}`,
  },
  body: JSON.stringify(orderRequest),
}).then((response) => response.json());

if (placementResponse.mode !== "typed_data") {
  throw new Error("Expected typed_data mode");
}

const signature = await walletClient.signTypedData(placementResponse.typedData);

await walletClient.writeContract({
  address: placementResponse.contractAddress,
  abi: diffusalOptionsOrderBookAbi,
  functionName: "placeOrderWithSignature",
  args: [
    placementResponse.typedData.message.seriesId,
    placementResponse.typedData.message.portfolioId,
    placementResponse.typedData.message.isBuy,
    placementResponse.typedData.message.tick,
    placementResponse.typedData.message.size,
    placementResponse.typedData.message.expiry,
    placementResponse.typedData.message.nonce,
    placementResponse.typedData.message.deadline,
    signature,
  ],
});

typedData.message matches the live PlaceOrder schema exactly: seriesId, portfolioId, isBuy, tick, size, expiry, nonce, and deadline.

If you already have a signature, the same endpoint can return mode: "relay" with relay-ready calldata instead of a typedData payload. If your order may create the series on first placement, use placeOrderWithSeriesParamsWithSignature(...) and include matching series parameters in the signed payload.

RFQ Flow (Current)

  1. Request a signed quote from /api/rfq/request (the public timed RFQ auction endpoint).
  2. The backend returns the winning signed EIP-712 quote, ready for contract submission.
  3. Optionally call authenticated /api/rfq/fill if you want API-built calldata for the returned quote.
  4. Submit the quote on-chain via fillRfqQuoteInPortfolio(...).
  5. Use getQuoteStatus(...) for pre-flight checks when needed.

Portfolio Notes

  • Portfolio 0 is the default route used by most examples.
  • Users can create additional portfolios with createPortfolio().
  • Collateral movement between portfolios is done via transferCollateralBetweenPortfolios(...).

Practical Integration Sequence

  1. Discover market/series via /api/markets.
  2. Prepare params via /api/helpers where useful.
  3. Build or request the signed order payload when using the order book.
  4. Execute on-chain transaction from wallet.
  5. Track status and state via REST/WebSocket.

On this page