# How To Integrate

### Overview

Solvers on the network receive information via continuous GET requests on Mystic's API to know when an instant redemption request or liquidation happens. They then sign and send a quote with the price they offer to settle the trade at. Mystic will then choose the one with the best price, give it the trade and settle the transaction. Here we go over how a solver can integrate with Mystic to receive our trade requests and send us their bids. There are 4 steps to follow:

* Register as a Solver;
* Query Requests;
* (Optional) Approve the Mystic contract;
* Sign and send quote.

Let us now look at how to do each one.

### Register as a Solver

Anyone can see open RFQs, but only registered Solvers can bid on them. To participate in the Mystic RFQ process, you first need to register as a Solver with our team. It's a straightforward process, all we need is your firm's name, the address of the wallet you'll be bidding with (and on what chain) and a general awareness of what RFQ's interest you. To register as a Solver, [contact us via email](mailto:joao.moreira@mysticfinance.xyz).

### Query Redemption Requests

To see all requests you can bid on at any moment, all you have to do is query this API endpoint according to our [API Reference](https://curator-api.mysticfinance.xyz/docs/#/rfq):

```
https://api.mysticfinance.xyz/redemption/requests
```

This is the response you would receive, assuming you see only one redemption request for WPLUME:

```
{
      "account": "0x8480b3aa300dd7fad979d31270732040011a6ca7",
      "apy": "0",
      "riskRating": "BBB",
      "chainId": 98867,
      "requestId": "6caa1f2f-0743-4470-9fd2-b76c7d05626b",
      "user": "0x8480b3aa300dd7fad979d31270732040011a6ca7",
      "sellToken": "0x1e0e030abcb4f07de629dccea458a271e0e82624",
      "buyToken": "0xc1fd14775c8665b31c7154074f537338774351eb",
      "sellAmount": "10000",
      "underlyingAsset": "0x1e0e030abcb4f07de629dccea458a271e0e82624",
      "expiryTime": 1760089073,
      "componentAssets": [],
      "redemptionTime": 1760088473,
      "status": "pending",
      "slippageTolerance": 3,
      "createdAt": "2025-10-06T22:07:53.048Z",
      "updatedAt": "2025-10-06T22:07:53.048Z",
      "timestamp": 1759788473048,
      "metadata": {
        "redeemAsset": "WPLUME",
        "assetAddress": "0x1e0e030abcb4f07de629dccea458a271e0e82624",
        "collateralType": "Digital asset",
        "accessType": "Permissionless",
        "yield": "0",
        "decimals": "18",
        "riskRating": "BBB",
        "exchangeProxy": "0x21722911A85C6F6F1Bb84273B1Aa882C59121179"
        "rfqOrder": {
          "makerToken": "0xc1fd14775c8665b31c7154074f537338774351eb",
          "takerToken": "0x1e0e030abcb4f07de629dccea458a271e0e82624",
          "makerAmount": "",
          "takerAmount": "10000",
          "maker": "",
          "taker": "0x8480b3aa300dd7fad979d31270732040011a6ca7",
          "pool": "0x0000000000000000000000000000000000000000000000000000000000000000",
          "sender": "0x0000000000000000000000000000000000000000",
          "feeRecipient": "0x46892c3265BCe02a17408D86a5120b4D62D7606b",
          "takerTokenFeeAmount": 100,
          "expiry": 1760089073,
          "salt": "6caa1f2f-0743-4470-9fd2-b76c7d05626b"
        }
      }
    },
```

Once you see all the available opportunities, you can start defining a thesis around which ones you want to bid on and which ones you do not. After you know on which RFQ's you want to start participating in, you can now start automatically sending us quotes when you receive relevant RFQ's.

### (Optional) Approve Contract

You will need to approve Mystic's contract so it can take assets from your wallet when you win RFQs. You can either choose to do an unlimited approval of the relevant contract address once before you start bidding, or do it for smaller amounts every time you bid. We advise you to approve it once, as it will make bidding easier for you moving forward.

Here is a code sample of how you can approve contract via code by referencing variables available in [Query Redemption Requests](#query-redemption-requests):

```
import { ethers } from 'ethers';

// Unlimited approval to Exchange Proxy (spender) from rfqRequest.metadata
export async function approveUnlimitedToExchangeProxy(
  rfqRequest: { metadata?: { exchangeProxy?: string; } },
  token: string,
  signer: ethers.Signer,
) {
  const spender =
    rfqRequest?.metadata?.exchangeProxy;
  if (!spender) throw new Error('metadata.exchangeProxy (or allowanceTarget) missing');

  const erc20 = new ethers.Contract(
    token,
    [
      'function approve(address spender, uint256 amount) external returns (bool)',
      'function allowance(address owner, address spender) view returns (uint256)',
      'function decimals() view returns (uint8)',
    ],
    signer,
  );

  const owner = await signer.getAddress();
  const current = await erc20.allowance(owner, spender);
  if (current.gt(0)) return { txHash: null, alreadyApproved: true };

  const tx = await erc20.approve(spender, ethers.constants.MaxUint256);
  const receipt = await tx.wait();
  return { txHash: receipt.transactionHash, alreadyApproved: false };
}
```

### Sign Order

For your quote to be able to fill an RFQ, you need first sign the respective order. You will be signing and sending a quote every time you wish to participate in an RFQ. Here is a code sample of how you sign one:

```
// Use rfqOrder from RFQRequest.metadata to build/sign the final order.
// Only provide makerAmount and maker (everything else comes from metadata.rfqOrder).

import { LimitOrder, SignatureType } from '@0x/protocol-utils';
import { BigNumber } from '@0x/utils';
import { Wallet } from 'ethers';

type RFQOrderFromMetadata = {
  chainId: number,
  verifyingContract: string,
  makerToken: string,
  takerToken: string,
  takerAmount: string,
  taker: string, 
  pool: string,
  sender: string,
  feeRecipient: string,
  takerTokenFeeAmount: string,
  expiry: number,
  salt: number,
};

export async function signFromMetadata(
  rfqRequest: any, 
  makerAmount: string,
  maker: string,
) {
  const base: RFQOrderFromMetadata = rfqRequest?.metadata?.rfqOrder;
  if (!base) throw new Error('metadata.rfqOrder missing');

  const order = new LimitOrder({
    chainId: base.chainId,
    verifyingContract: base.,verifyingContract,
    makerToken: base.makerToken,
    takerToken: base.takerToken,
    makerAmount: new BigNumber(makerAmount),
    takerAmount: new BigNumber(base.takerAmount),
    maker,
    taker: base.taker,
    pool: base.pool,
    sender: base.sender,
    feeRecipient: base.feeRecipient,
    takerTokenFeeAmount: new BigNumber(String(base.takerTokenFeeAmount)),
    expiry: new BigNumber(base.expiry),
    salt: new BigNumber(base.salt),
  });

  const signature = await order.getSignatureWithKey(
    new Wallet(process.env.PRIV_KEY).privateKey,
    SignatureType.EIP712,
  );

  return {
    order: {
      chainId: order.chainId,
      verifyingContract: order.verifyingContract,
      makerToken: order.makerToken,
      takerToken: order.takerToken,
      makerAmount: order.makerAmount.toString(),
      takerAmount: order.takerAmount.toString(),
      maker: order.maker,
      taker: order.taker,
      pool: order.pool,
      sender: order.sender,
      feeRecipient: order.feeRecipient,
      takerTokenFeeAmount: order.takerTokenFeeAmount.toString(),
      expiry: order.expiry.toString(),
      salt: order.salt.toString(),
    },
    signature,
  };
}
```

### Send Quotes

To send us a quote, you need to both sign and send us a response via POST request to the following endpoint:

```
https://api.mysticfinance.xyz/redemption/quote
```

Assuming you have already signed as per the code sample in the previous section, this is the format that the POST request you're sending should be in, which includes the signature above:

```
{
  "requestId": "6387df25-d64b-485b-8ed7-5422459bcf35",
  "maker": "0x8480b3aa300dd7fad979d31270732040011a6ca7",
  "makerAmount": "10000",
  "signature": {
        "signatureType": 2,
        "v": 27,
        "r": "0xd988ae764344e7a116eb1924f491d81736558025ed63ce4f28759a7b68ff58a6",
        "s": "0x1d0d1a92f7087d39e29ecb9416a8105209f19c85d199e2179d6d268395157329"
      },
  "expiry": 1000
}
```

Do note:

* The price you are quoting is calculated as `takerAmount/makerAmount`;
* `maker` is your own address, with which you first registered as a Solver;
* Responses must be sent before the  `expiry`  date in the request for quote (`1760089073` in the example above).

### Winner Selection

When the request for quote hits its expiry date, Mystic selects the bid with the best price and awards it the trade. We then use the winning bid's details to create an RFQ object which will be used to fill the order on-chain. As such, once the RFQ object is created, Mystic fills the trade with the winning bid by bundling settlement and fee distribution. At once, two things happen:

* RFQ order fill - effectively taking the redemption asset from the Solver and giving it to the user and taking the redeem asset from the user and giving it to the Solver.
* Fee settlement - taking the protocol fee and sending the remaining assets to the Solver.

Once this is done, Mystic updates the losing bids to `Rejected` , the winning bid to `Accepted` and the redemption request to `Completed`.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.mysticfinance.xyz/overview/instant-liquidity-api/how-to-integrate.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
