Skip to main content
There is no official SDK for the Message Bus API yet. However, the intents-sdk can be used to create and sign intents.
This page shows how to respond to quote requests as a market maker using TypeScript.

Overview

When you receive a quote request from the Message Bus, you need to:
  1. Calculate your proposed amount
  2. Generate a valid nonce
  3. Build and sign a token_diff intent
  4. Send the response back to the Message Bus

Generating nonces

Nonces must include the current contract salt and be unique. Learn more about nonce structure.
import { VersionedNonceBuilder } from "@defuse-protocol/intents-sdk";

const generateNonce = async (deadline: Date): Promise<string> => {
  // Get your NEAR account instance
  const account = await getAccount();

  // Fetch the current salt from the contract
  const salt_hex = await account.viewFunction({
    contractId: "intents.near",
    methodName: "current_salt",
  });

  const salt_bytes = Uint8Array.from(Buffer.from(salt_hex, "hex"));
  const versionedNonce = VersionedNonceBuilder.encodeNonce(salt_bytes, deadline);

  // Optional: verify nonce hasn't been used
  if (await isNonceUsed(versionedNonce)) {
    return generateNonce(deadline);
  }

  return versionedNonce;
};

const isNonceUsed = async (nonce: string): Promise<boolean> => {
  const account = await getAccount();

  return await account.viewFunction({
    contractId: "intents.near",
    methodName: "is_nonce_used",
    args: {
      account_id: account.accountId,
      nonce,
    },
  });
};

Building the intent payload

The params object matches what you receive from the relay in the "quote" event:
interface QuoteParams {
  defuse_asset_identifier_in: string;
  defuse_asset_identifier_out: string;
  exact_amount_in: string | undefined;
  exact_amount_out: string | undefined;
  min_deadline_ms: number;
}
Build the intent payload:
const getIntentPayload = async (
  sdk: IntentsSDK,
  deadline: Date,
  params: QuoteParams,
  amount: string
) => {
  const nonce = await generateNonce(deadline);

  return await sdk
    .intentBuilder()
    .setDeadline(deadline)
    .setNonce(nonce)
    .addIntent({
      intent: "token_diff",
      diff: {
        // Token you're receiving (positive = incoming)
        [params.defuse_asset_identifier_in]: params.exact_amount_in
          ? params.exact_amount_in
          : amount,
        // Token you're giving (negative = outgoing)
        [params.defuse_asset_identifier_out]: `-${
          params.exact_amount_out ? params.exact_amount_out : amount
        }`,
      },
    })
    .build();
};

Complete example

Putting it all together to respond to a quote request:
import { IntentsSDK, createIntentSignerNearKeyPair } from "@defuse-protocol/intents-sdk";
import bs58 from "bs58";

// Initialize the SDK with your signer
const intentSigner = createIntentSignerNearKeyPair({
  signer,      // Your NEAR signer
  accountId,   // Your NEAR account ID
});

const sdk = new IntentsSDK({
  env: "production",
  referral: "your_referral",
  intentSigner: intentSigner,
});

// When you receive a quote request...
const handleQuoteRequest = async (
  quote_id: string,
  params: QuoteParams
) => {
  // 1. Calculate your proposed amount based on your pricing logic
  const amount = calculateAmount(params);

  // 2. Set deadline (e.g., 5 minutes from now)
  const deadline = new Date(Date.now() + 5 * 60 * 1000);

  // 3. Build the intent payload
  const payload = await getIntentPayload(sdk, deadline, params, amount);

  // 4. Sign the payload
  const signedPayload = await intentSigner.signIntent(payload);

  // 5. Build the response
  const response = {
    quote_id,
    quote_output: params.exact_amount_in
      ? { amount_out: amount }
      : { amount_in: amount },
    signed_data: signedPayload,
  };

  // 6. Send via WebSocket
  ws.send(JSON.stringify({
    jsonrpc: "2.0",
    id: Date.now(),
    method: "quote_response",
    params: [response],
  }));
};

WebSocket connection example

const ws = new WebSocket("wss://solver-relay-v2.chaindefuser.com/ws");

ws.onopen = () => {
  // Subscribe to quote requests
  ws.send(JSON.stringify({
    jsonrpc: "2.0",
    id: 1,
    method: "subscribe",
    params: ["quote"],
  }));

  // Subscribe to quote status updates
  ws.send(JSON.stringify({
    jsonrpc: "2.0",
    id: 2,
    method: "subscribe",
    params: ["quote_status"],
  }));
};

ws.onmessage = (event) => {
  const data = JSON.parse(event.data);

  if (data.params?.quote_id) {
    // Handle quote request
    handleQuoteRequest(data.params.quote_id, data.params);
  } else if (data.params?.tx_hash) {
    // Handle settlement notification
    console.log(`Quote ${data.params.quote_hash} settled: ${data.params.tx_hash}`);
  }
};

TokenDiff intent structure

The token_diff intent expresses what tokens you’re willing to give and receive:
{
  intent: "token_diff",
  diff: {
    // Positive values = tokens you receive
    "nep141:usdc.near": "1000",
    // Negative values = tokens you give
    "nep141:usdt.near": "-1000",
  }
}
Ensure your account has sufficient balance in the Verifier contract for the tokens you’re offering. The intent will fail if you don’t have enough tokens.