# Create an RFQ

Submit a Request for Quotation to the OptionFactory and have market makers compete to fill your custom option. This page covers **cash-settled** RFQs (the default). For physically settled options, see [Physical Options](/sdk/rfq-factory/physical-options.md).

RFQ uses the same cash-settled implementation contracts as OptionBook (PUT, INVERSE\_CALL, spreads, etc.). The difference is that you choose the strike and expiry instead of filling an existing order.

## Prerequisites

* Install the SDK: `npm install @thetanuts-finance/thetanuts-client`
* A funded wallet on Base mainnet (chain ID 8453)
* For **SELL positions**: USDC (or WETH/cbBTC) approved for the OptionFactory

***

## Critical Rule: collateralAmount is ALWAYS 0

When creating any RFQ, the `collateralAmount` parameter must always be `0`. Collateral is **not** locked at RFQ creation — it is pulled from the seller at settlement time.

```typescript
// CORRECT — buildRFQParams enforces this automatically
const params = client.optionFactory.buildRFQParams({ ... });
// params.collateralAmount === BigInt(0)  always

// WRONG — never set manually
collateralAmount: BigInt(1000000)  // causes issues at settlement!
```

***

## Approach 1: buildRFQRequest (One-Liner)

`buildRFQRequest` combines `buildRFQParams` and the tracking/key fields into a single object ready for `encodeRequestForQuotation`.

### BUY Position (isLong: true)

```typescript
import { ethers } from 'ethers';
import { ThetanutsClient } from '@thetanuts-finance/thetanuts-client';

const provider = new ethers.JsonRpcProvider('https://mainnet.base.org');
const signer = new ethers.Wallet(process.env.PRIVATE_KEY!, provider);
const client = new ThetanutsClient({ chainId: 8453, provider, signer });
const userAddress = await signer.getAddress();

// 1. Generate or load your ECDH keypair (used to decrypt MM offers)
const keyPair = await client.rfqKeys.getOrCreateKeyPair();

// 2. Build the complete RFQ request
const rfqRequest = client.optionFactory.buildRFQRequest({
  requester: userAddress,
  underlying: 'ETH',              // 'ETH' | 'BTC'
  optionType: 'PUT',              // 'CALL' | 'PUT'
  strike: 2000,                   // human-readable strike price
  expiry: Math.floor(Date.now() / 1000) + 86400 * 7,  // 7 days from now
  numContracts: 1.5,              // human-readable contract count
  isLong: true,                   // true = BUY, false = SELL
  offerDeadlineMinutes: 60,       // how long MMs have to respond
  collateralToken: 'USDC',        // 'USDC' | 'WETH' | 'cbBTC'
  reservePrice: 0.015,            // optional: max price per contract (BUY)
  requesterPublicKey: keyPair.compressedPublicKey,
});

// 3. Encode and send
const { to, data } = client.optionFactory.encodeRequestForQuotation(rfqRequest);
const tx = await signer.sendTransaction({ to, data });
console.log('RFQ created:', tx.hash);
```

### SELL Position (isLong: false) — Approval Required

When selling (going short), **you are the collateral provider**. You must approve the OptionFactory to pull collateral at settlement before creating the RFQ.

```typescript
import { ethers } from 'ethers';
import { ThetanutsClient } from '@thetanuts-finance/thetanuts-client';

const provider = new ethers.JsonRpcProvider('https://mainnet.base.org');
const signer = new ethers.Wallet(process.env.PRIVATE_KEY!, provider);
const client = new ThetanutsClient({ chainId: 8453, provider, signer });
const userAddress = await signer.getAddress();

const keyPair = await client.rfqKeys.getOrCreateKeyPair();

// Collateral approval amounts:
//   PUT:           strike × numContracts  (in USDC decimals = 6)
//   CALL (inverse): numContracts          (in WETH decimals = 18)
const strike = 1850;
const numContracts = 1.5;
const approvalAmount = BigInt(Math.round(strike * numContracts * 1e6)); // USDC

const USDC = client.chainConfig.tokens.USDC.address;
await client.erc20.ensureAllowance(
  USDC,
  client.optionFactory.contractAddress,
  approvalAmount
);

// Build and submit SELL RFQ
const rfqRequest = client.optionFactory.buildRFQRequest({
  requester: userAddress,
  underlying: 'ETH',
  optionType: 'PUT',
  strike: 1850,
  expiry: Math.floor(Date.now() / 1000) + 86400 * 7,
  numContracts: 1.5,
  isLong: false,                  // SELL — you provide collateral
  offerDeadlineMinutes: 60,
  collateralToken: 'USDC',
  reservePrice: 0.010,            // optional: min acceptable price (SELL)
  requesterPublicKey: keyPair.compressedPublicKey,
});

const { to, data } = client.optionFactory.encodeRequestForQuotation(rfqRequest);
const tx = await signer.sendTransaction({ to, data });
console.log('SELL RFQ created:', tx.hash);
```

***

## Approach 2: buildRFQParams (Two-Step)

`buildRFQParams` builds just the on-chain `QuotationParameters` object. Use this when you want fine-grained control over the tracking fields or reserve price encoding.

```typescript
import { ethers } from 'ethers';
import { ThetanutsClient } from '@thetanuts-finance/thetanuts-client';

const provider = new ethers.JsonRpcProvider('https://mainnet.base.org');
const signer = new ethers.Wallet(process.env.PRIVATE_KEY!, provider);
const client = new ThetanutsClient({ chainId: 8453, provider, signer });
const userAddress = await signer.getAddress();

const keyPair = await client.rfqKeys.getOrCreateKeyPair();

// Step 1: Build the QuotationParameters object
const quotationParams = client.optionFactory.buildRFQParams({
  requester: userAddress,
  underlying: 'ETH',
  optionType: 'PUT',
  strike: 1850,           // human-readable; SDK converts to 8-decimal bigint
  expiry: 1741334400,     // unix timestamp
  numContracts: 1.5,      // human-readable; SDK converts to token-decimal bigint
  isLong: true,
  offerDeadlineMinutes: 60,
  collateralToken: 'USDC',
});
// quotationParams.collateralAmount === 0n  — enforced automatically

// Step 2: Encode the full request with reserve price and public key
const { to, data } = client.optionFactory.encodeRequestForQuotation({
  params: quotationParams,
  tracking: { referralId: BigInt(0), eventCode: BigInt(0) },
  reservePrice: BigInt(0),  // 0 = no price protection; or set in collateral decimals
  requesterPublicKey: keyPair.compressedPublicKey,
});

const tx = await signer.sendTransaction({ to, data });
console.log('RFQ created:', tx.hash);
```

***

## Reserve Price

The `reservePrice` parameter protects you from unfavorable fills:

| Position               | Meaning                                    | Behavior if violated        |
| ---------------------- | ------------------------------------------ | --------------------------- |
| BUY (`isLong: true`)   | Maximum price per contract you will pay    | RFQ fails; deposit returned |
| SELL (`isLong: false`) | Minimum price per contract you will accept | RFQ fails                   |
| `0`                    | No price protection                        | Accept any offer            |

**Example:** BUY with reserve price 0.015 USDC, 10 contracts → total escrow = 0.015 × 10 = **0.15 USDC** locked until settlement.

In `buildRFQRequest`, pass `reservePrice` as a human-readable number (e.g. `0.015`). When using `buildRFQParams` + `encodeRequestForQuotation` directly, pass it as a bigint in collateral token decimals.

***

## RFQ Parameters Reference

| Parameter              | Type                          | Description                                         |
| ---------------------- | ----------------------------- | --------------------------------------------------- |
| `requester`            | `address`                     | Your wallet address                                 |
| `underlying`           | `'ETH' \| 'BTC'`              | Underlying asset                                    |
| `optionType`           | `'CALL' \| 'PUT'`             | Option type                                         |
| `strike`               | `number`                      | Strike price (human-readable)                       |
| `strikes`              | `number[]`                    | Strike array for multi-leg (auto-detects structure) |
| `expiry`               | `number`                      | Unix timestamp; must be after offer deadline        |
| `numContracts`         | `number \| bigint \| string`  | Contract count; use `bigint` when closing positions |
| `isLong`               | `boolean`                     | `true` = BUY, `false` = SELL                        |
| `offerDeadlineMinutes` | `number`                      | Minutes MMs have to respond                         |
| `collateralToken`      | `'USDC' \| 'WETH' \| 'cbBTC'` | Collateral denomination                             |
| `reservePrice`         | `number`                      | Optional max (BUY) or min (SELL) price per contract |
| `requesterPublicKey`   | `hex string`                  | Your ECDH compressed public key                     |

***

## Convenience Helpers: Multi-Leg Builders

The factory module exposes named helpers that make parameter intent explicit:

```typescript
// Spread: pass named lower/upper strikes
const spreadRFQ = client.optionFactory.buildSpreadRFQ({
  requester: userAddress,
  underlying: 'ETH',
  optionType: 'PUT',
  lowerStrike: 1800,
  upperStrike: 2000,
  expiry: 1741334400,
  numContracts: 1,
  isLong: true,
  offerDeadlineMinutes: 60,
  collateralToken: 'USDC',
});

// Butterfly: pass lower, middle, upper
const butterflyRFQ = client.optionFactory.buildButterflyRFQ({
  requester: userAddress,
  underlying: 'ETH',
  optionType: 'CALL',
  lowerStrike: 1800,
  middleStrike: 1900,
  upperStrike: 2000,
  expiry: 1741334400,
  numContracts: 1,
  isLong: true,
  offerDeadlineMinutes: 60,
  collateralToken: 'WETH',
});

// Condor: pass four ordered strikes
const condorRFQ = client.optionFactory.buildCondorRFQ({
  requester: userAddress,
  underlying: 'ETH',
  optionType: 'PUT',
  strike1: 1700,
  strike2: 1800,
  strike3: 1900,
  strike4: 2000,
  expiry: 1741334400,
  numContracts: 1,
  isLong: true,
  offerDeadlineMinutes: 60,
  collateralToken: 'USDC',
});
```

These return the same shape as `buildRFQRequest` and can be passed directly to `encodeRequestForQuotation` or `requestForQuotation`.

***

## Using requestForQuotation (Convenience)

Instead of encoding and sending manually, the module also provides a transaction-sending wrapper:

```typescript
const receipt = await client.optionFactory.requestForQuotation(rfqRequest);
console.log('TX hash:', receipt.hash);
console.log('Block:', receipt.blockNumber);

// Get the new RFQ ID
const rfqCount = await client.optionFactory.getQuotationCount();
const rfqId = rfqCount - 1n;
console.log('RFQ ID:', rfqId.toString());
```

***

## Common Pitfalls

| Mistake                                               | Fix                                               |
| ----------------------------------------------------- | ------------------------------------------------- |
| `collateralAmount != 0`                               | Always use `buildRFQParams`; never set manually   |
| SELL RFQ without token approval                       | Call `ensureAllowance` before submitting          |
| `expiry` before offer deadline                        | Ensure `expiry > now + offerDeadlineMinutes * 60` |
| Floating-point `numContracts` when closing a position | Pass the on-chain `bigint` value directly         |
| `reservePrice` in wrong decimals                      | Use `buildRFQRequest` with human-readable number  |

***

## See Also

* [RFQ Lifecycle](/sdk/rfq-factory/lifecycle.md) — What happens after you submit: MM offers, reveal phase, settlement
* [Multi-Leg Structures](/sdk/rfq-factory/multi-leg-structures.md) — Spreads, butterflies, condors with collateral math
* [Key Management](/sdk/rfq-factory/key-management.md) — How ECDH keys are stored and used for offer decryption


---

# 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.thetanuts.finance/sdk/rfq-factory/create-rfq.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.
