> For the complete documentation index, see [llms.txt](https://mute-sh.gitbook.io/mute.sh/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://mute-sh.gitbook.io/mute.sh/integration-guides/builder-guide.md).

# Builder Guide

A **builder** is any platform, application, or wallet that surfaces the Parlay UI to end users and earns a fee on every parlay submitted through their interface. This guide covers everything you need to integrate as a builder — fee mechanics, on-chain trust guarantees, and how builder revenue is realized.

{% hint style="warning" %}
**v2 builder identity is a single relay-wide setting, not a per-request registry.** A given v2 relay deployment is configured with exactly one builder address and one builder fee rate at boot time. There is no `builderId` parameter you pass per RFQ to select among multiple registered builders, no per-builder correlation/eligibility/payout-cap configuration, and no "managed vs open" book mode. If you want your own builder identity and fee rate, you need your own relay deployment (see [Custom Relay Deployment](#12-custom-relay-deployment) below) — you cannot register as one of several builders sharing a single relay instance.
{% endhint %}

***

## 1. Overview

**How builder fees work:**

* The relay operator configures a single builder address and a single `builderFeeBps` rate for the whole deployment.
* The builder fee is charged in basis points on the taker's gross deposit (the full collateral the user sends to the escrow) — the same deposit-time deduction that produces `netStake` for maker pricing.
* The builder address is included in the maker's EIP-712 `ParlayQuote` signature and re-verified on-chain against the caller-supplied `builder` argument, making the fee **trustless**: neither the relay nor the taker can redirect it to a different address.
* The fee is credited **unconditionally at parlay creation** (deposit time) — it does not depend on whether the parlay later wins, loses, or voids. See [Fee Regime](#4-fee-regime) for the exact split, and [How Builder Revenue Is Realized](#11-how-builder-revenue-is-realized) for why "credited" and "received" are not the same moment.

A builder does not take on book risk. The fee is a flat cut of the gross stake, independent of outcome.

***

## 2. Builder Identity Resolution

The relay resolves a single effective builder for every RFQ from its own server-side configuration — not from anything the client sends:

* If the relay operator has configured a non-zero builder address, every RFQ created through that relay carries that address as `builder` and that rate as `builderFeeBps`.
* If no builder address is configured (the zero address), `builder` and `builderFeeBps` are both forced to zero for every RFQ on that deployment — there is no builder fee at all.

This resolution happens once per RFQ, server-side, and is embedded in the `escrow_terms` the relay hands back with the RFQ (and, from there, in the `ParlayQuote` the maker signs). Some taker client libraries accept an optional builder-identifier field alongside an RFQ request for forward compatibility, but the current v2 relay does not read or act on it — the resolved builder is always the one configured on that relay deployment. Do not build product logic around being able to switch builder identity per request.

***

## 3. Deposit-Time Fee Split

Every parlay's gross taker deposit is split into three components at the moment the parlay is created on-chain:

| Component    | Party    | Notes                                                                                                                                       |
| ------------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| Protocol fee | Protocol | Always applied, in basis points on gross collateral.                                                                                        |
| Builder fee  | Builder  | Applied only when a non-zero builder address is attached to the quote; zero otherwise.                                                      |
| Net stake    | —        | What remains after both fees — this is what the maker actually prices against and what the escrow records as the parlay's taker collateral. |

Both the protocol fee rate and the builder fee rate are snapshotted into the signed `ParlayQuote` at quote time and re-derived independently on-chain from that snapshot — the relay cannot inflate either rate after the maker has signed, and their combined total is capped on-chain.

The win-only protocol spread is a separate, escrow-owner-configured value applied later, at settlement, only on a taker win — it is not part of the deposit-time split and is not signed by the maker. See the [Maker Protocol Spec](/mute.sh/reference/maker-protocol-spec.md) for the full spread mechanics.

***

## 4. Fee Regime

Fees are computed by the relay before broadcasting the RFQ to makers. Makers price their quotes against **netStake**, not gross collateral.

### Exact math

```
protocolFee = grossCollateral × protocolFeeBps / 10_000
builderFee  = grossCollateral × builderFeeBps  / 10_000     (0 if no builder configured)
netStake    = grossCollateral − protocolFee − builderFee
```

All fee math is done in the token's smallest unit (6 decimals for the testnet USDC) using integer division — there is no floating-point rounding.

### Example: $100 gross stake (illustrative rates)

```
grossCollateral = $100.00
protocolFee     = $100 × 100 / 10000 = $1.00   (1%)
builderFee      = $100 ×  25 / 10000 = $0.25   (0.25%, only if a builder is configured)
netStake        = $100 − $1.00 − $0.25 = $98.75
```

Makers receive the RFQ with `netStake = $98.75` and price their combined odds against that amount.

{% hint style="danger" %}
**Common mistake:** displaying odds or potential payout computed from the gross $100 stake instead of the $98.75 `netStake`. Always use `netStake` for payout math in your UI.
{% endhint %}

{% hint style="info" %}
The protocol fee rate is not a fixed constant — the relay may scale it with leg count and taper it for large stakes. Treat the rate as dynamic per RFQ and always read the actual `protocol_fee_bps`/`builder_fee_bps` values off the RFQ's `escrow_terms` rather than hardcoding a number in your UI.
{% endhint %}

***

## 5. Combo Conflict Checks

Before any RFQ is broadcast to makers, the relay runs a fixed set of leg-conflict checks on every combo — same-outcome-both-sides, dead/unknown legs, same-market, same-entity, and category homogeneity (a combo cannot mix asset classes, e.g. sports and crypto, in one parlay). These checks are protocol-level and apply uniformly to every RFQ passing through a given relay — they are not a per-builder configuration you can toggle on or off.

If any check fails, the RFQ is rejected before any market maker ever sees it. See [Maker Protocol Spec §6, Combo identity: condition\_id, position ids, and conflict checks](/mute.sh/reference/maker-protocol-spec.md#6-combo-identity-condition_id-position-ids-and-conflict-checks) for the full conflict-check taxonomy and error codes, and the [Taker Guide](/mute.sh/integration-guides/taker-guide.md) for the client-facing error shapes.

***

## 6. Escrow Binding

The builder address is included in the **`ParlayQuote` EIP-712 struct** that the market maker signs. The full 10-field struct (v2 — the win-only spread is owner-set escrow config, not a maker-signed field):

```typescript
// EIP-712 ParlayQuote — 10 fields (v2), exact order matters for the struct hash
{
  maker:           Address,  // the MM's signing address
  taker:           Address,  // the end user
  legsHash:        bytes32,  // canonical condition_id — keccak over the ordered, deduped leg hashes
  takerCollateral: uint256,  // GROSS taker deposit in USDC base units (6dp)
  makerCollateral: uint256,  // MM's collateral — determines odds; higher = better for taker
  builder:         Address,  // the builder fee recipient, baked in by the maker's signature
  protocolFeeBps:  uint16,   // relay-configured protocol fee rate for this RFQ
  builderFeeBps:   uint16,   // relay-configured builder fee rate for this RFQ (0 if no builder)
  nonce:           uint256,  // random 256-bit, per-quote
  deadline:        uint256,  // unix timestamp
}
```

The escrow contract verifies, on parlay creation, that the caller-supplied `builder` argument matches the `builder` field the maker actually signed into the quote — a mismatch reverts the transaction outright. This means the relay cannot silently redirect a builder fee to a different address than the one the maker priced and signed against.

**Trust properties:**

* The maker signs the specific `builder` address as part of the quote — the relay cannot substitute a different address at execution time without invalidating the signature.
* The taker countersigns a `ParlayOrder` containing the `quoteHash` (the EIP-712 hash of the full `ParlayQuote`), binding the taker to the exact terms including `builder`.
* The escrow itself checks the passed-in `builder` argument against the signed quote's `builder` field on every parlay creation and reverts on mismatch.

**Audit path:**

Every parlay creation emits a `ParlayCreated` event (taker, maker, collateral amounts, `legsHash`, leg count) and, whenever fees are non-zero, a `FeesPaid` event carrying the protocol fee recipient, the builder address, and the exact fee amounts credited to each. You can verify which builder address was actually credited on any given parlay by reading `FeesPaid` off-chain — no trust in the relay's own reporting is required.

***

## 7. Integrating as a Builder

### Step 1 — Talk to the relay operator

Builder identity in v2 is relay-wide configuration, not a self-service registration. Contact the relay operator to have your builder address and fee rate configured on a deployment — either the shared reference deployment or your own (see [Custom Relay Deployment](#12-custom-relay-deployment)).

### Step 2 — Create RFQs via `POST /v1/rfq`

Submit RFQs with the taker address and stake. The builder is resolved server-side — you do not pass it on the request:

```json
{
  "taker": "0xTakerEVMAddress",
  "stake_usd": 100,
  "legs": [
    { "outcomeId": 42, "sideIndex": 0 },
    { "outcomeId": 17, "sideIndex": 0 }
  ]
}
```

The response is an RFQ snapshot (status, the full request including `escrow_terms` with the resolved `protocol_fee_bps`/`builder_fee_bps`/`spread_bps`, and — once one arrives — the winning quote). Live quotes arrive over the taker WebSocket channel as they're submitted; there is no REST endpoint that returns a quote list. See the [Taker Guide](/mute.sh/integration-guides/taker-guide.md) for the full snapshot shape and the WebSocket message catalog.

### Step 3 — Display fees and odds correctly

Before showing odds or potential payout to the user, compute `netStake` from the gross amount they entered, using the actual `protocol_fee_bps`/`builder_fee_bps` values returned in the RFQ's `escrow_terms` — do not hardcode fee rates in your client:

```typescript
function computeNetStake(
  grossUsd: number,
  protocolFeeBps: number,
  builderFeeBps: number,
): { netStake: number; protocolFee: number; builderFee: number } {
  const protocolFee = (grossUsd * protocolFeeBps) / 10_000;
  const builderFee  = (grossUsd * builderFeeBps)  / 10_000;
  return { netStake: grossUsd - protocolFee - builderFee, protocolFee, builderFee };
}
```

**Show the user:**

* Gross stake
* Fees (protocol + builder, broken out)
* Effective (net) stake
* Potential payout computed from the **net** stake, never the gross

{% hint style="danger" %}
**Do not** compute payout as `grossStake × combinedOdds`. That overstates the return by the fee percentage.
{% endhint %}

### Step 4 — Handle rejected RFQs

The relay rejects malformed or out-of-bounds RFQs with a typed error code before any MM sees them — invalid stake amounts, leg-count violations, and combo conflict checks all surface this way. See the [Taker Guide](/mute.sh/integration-guides/taker-guide.md) for the full error-code table and response shape; do not assume specific error strings, as only the typed code is part of the stable contract.

***

## 8. Stake and Leg Limits

The relay enforces a minimum and maximum gross USD stake and a minimum/maximum leg count for every RFQ, regardless of builder. These are relay-wide configuration (operator-set), not per-builder settings. A custom relay deployment (see below) can be configured with different limits than the shared reference deployment. Always read the live limits off the relay's connection-time payload rather than hardcoding them — they are not part of the stable public contract and may differ between deployments.

***

## 9. How Builder Revenue Is Realized

Builder fee revenue scales linearly with volume through your interface:

```
daily_revenue = parlays_per_day × avg_gross_stake_usd × (builderFeeBps / 10_000)
```

Because the builder fee is charged on gross deposit rather than on outcome, revenue does **not** depend on parlay win rates — a builder earns the same fee whether the parlay ultimately wins, loses, or voids.

{% hint style="warning" %}
**"Credited" is not "sent."** The builder fee is not pushed to your wallet automatically. On parlay creation, the escrow credits your builder address's balance in an internal withdrawable ledger — the same pull-payment mechanism used for settlement payouts and refunds. Your builder address must call the escrow's own `withdraw()` (or `withdrawTo()`, to send to a different address) to actually receive the accumulated USDC. There is no push/auto-send step anywhere in the flow. Build a periodic withdrawal job (or a manual one, for low volume) rather than assuming fees arrive automatically as parlays are created.
{% endhint %}

Note also that the fee is credited **at parlay creation (deposit time)**, not at settlement — it is realized as soon as a parlay is filled, well before that parlay's legs resolve. This is what makes revenue purely a function of volume: the fee has already been credited to your withdrawable balance long before win/loss is known.

***

## 10. Reference: Fee Split Pseudocode

```typescript
function feeRegime(): {
  builder: Address;
  protocolFeeBps: number;
  builderFeeBps: number;
} {
  const builderPresent = configuredBuilderAddress !== ZERO_ADDR;
  return {
    builder:        builderPresent ? configuredBuilderAddress : ZERO_ADDR,
    protocolFeeBps: PROTOCOL_FEE_BPS,               // relay-configured, may vary per RFQ
    builderFeeBps:  builderPresent ? configuredBuilderFeeBps : 0,
  };
}

function splitFees(
  grossCollateral: bigint,
  protocolFeeBps: number,
  builderFeeBps: number,
): { protocolFee: bigint; builderFee: bigint; netStake: bigint } {
  const protocolFee = (grossCollateral * BigInt(protocolFeeBps)) / 10_000n;
  const builderFee  = (grossCollateral * BigInt(builderFeeBps))  / 10_000n;
  return {
    protocolFee,
    builderFee,
    netStake: grossCollateral - protocolFee - builderFee,
  };
}
```

Note: all fee math uses `bigint` with integer basis-point division (`/ 10_000n`). There is no floating-point rounding — amounts are in the token's smallest unit (6 decimals for the testnet USDC).

***

## 11. Reference: RFQ Create Request

`POST /v1/rfq`

```json
{
  "taker": "0xTakerEVMAddress",
  "stake_usd": 100,
  "legs": [
    { "outcomeId": 42, "sideIndex": 0 },
    { "outcomeId": 17, "sideIndex": 0 }
  ]
}
```

| Field              | Type      | Required | Description                                                                                                                                            |
| ------------------ | --------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `taker`            | `Address` | Yes      | EVM address of the end user.                                                                                                                           |
| `stake_usd`        | `number`  | Yes      | Gross stake in USD (e.g. `100`). Server converts to 6dp USDC base units.                                                                               |
| `legs`             | `Leg[]`   | Yes      | An array of outcome selections within the relay's configured leg-count bounds.                                                                         |
| `legs[].outcomeId` | `number`  | Yes      | HIP-4 numeric outcome id. Get valid ids from the combo-markets discovery endpoint (see the [Taker Guide](/mute.sh/integration-guides/taker-guide.md)). |
| `legs[].sideIndex` | `0\|1`    | Yes      | `0` = YES (long), `1` = NO (short).                                                                                                                    |

**Success response (200):** an RFQ snapshot — status, the full request (including `escrow_terms` with the resolved fee rates), and the winning quote once one is selected. See the [Taker Guide](/mute.sh/integration-guides/taker-guide.md) for the exact snapshot shape.

**Error response:** a 4xx with a typed error code (out-of-bounds stake, invalid leg count, combo conflict, etc.) rather than a free-form message — see the [Taker Guide](/mute.sh/integration-guides/taker-guide.md)'s error-code table for the stable contract.

***

## 12. Custom Relay Deployment

If you want your own builder identity and fee rate rather than sharing a deployment configured for a different builder — or you need different stake/leg limits or a dedicated MM pool — contact the mute.sh team to discuss a dedicated relay deployment.

What a dedicated deployment can include:

| Setting                    | What you control                                              |
| -------------------------- | ------------------------------------------------------------- |
| Builder address / fee rate | Your own builder identity and `builderFeeBps`                 |
| Stake and leg limits       | Minimum/maximum stake, minimum/maximum legs                   |
| MM pool                    | Which market makers are authenticated against your deployment |

The on-chain escrow and USDC contracts are shared across all deployments on a given chain — only the relay-layer configuration changes between deployments.

***

## Contract Addresses (HyperEVM Testnet, chainId=998)

| Contract             | Address                                      |
| -------------------- | -------------------------------------------- |
| Parlay Escrow v2     | `0x551D4A7EdB71583C7C12900128727D857e308925` |
| Mock USDC (EIP-2612) | `0x75b69d0E8fA3CdB644b0f069E4e876B85A76E137` |

{% hint style="info" %}
The escrow address above is current as of this deployment and has changed at least once as the contract gained new capabilities (novation/cashout support). Always confirm the live address from the relay's own connection-time payload (or ask the relay operator) rather than hardcoding it long-term in production code.
{% endhint %}


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## 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, and the optional `goal` query parameter:

```
GET https://mute-sh.gitbook.io/mute.sh/integration-guides/builder-guide.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

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.
