> ## Documentation Index
> Fetch the complete documentation index at: https://docs.livepeer.org/llms.txt
> Use this file to discover all available pages before exploring further.

# Probabilistic Micropayments

> The PM mechanism: how Livepeer gateways pay orchestrators via lottery-based tickets, win probability, face value, and TicketBroker escrow.

export const CenteredContainer = ({children, maxWidth = "800px", padding = "0", preset = "default", width = "", minWidth = "", marginRight = "", marginBottom = "", textAlign = "", style = {}, className = "", ...rest}) => {
  const presets = {
    default: {},
    fitContent: {
      width: "fit-content",
      minWidth: "fit-content"
    },
    readable70: {
      width: "70%",
      minWidth: "fit-content"
    },
    readable80: {
      width: "80%",
      minWidth: "fit-content"
    },
    readable90: {
      width: "90%"
    },
    wide900: {
      maxWidth: "900px"
    }
  };
  const presetStyle = presets[preset] || presets.default;
  return <div className={className} style={{
    maxWidth: presetStyle.maxWidth || maxWidth,
    margin: "0 auto",
    padding: padding,
    ...presetStyle.width ? {
      width: presetStyle.width
    } : {},
    ...presetStyle.minWidth ? {
      minWidth: presetStyle.minWidth
    } : {},
    ...width ? {
      width
    } : {},
    ...minWidth ? {
      minWidth
    } : {},
    ...marginRight ? {
      marginRight
    } : {},
    ...marginBottom ? {
      marginBottom
    } : {},
    ...textAlign ? {
      textAlign
    } : {},
    ...style
  }} {...rest}>
      {children}
    </div>;
};

export const CustomDivider = ({color = "var(--lp-color-border-default)", middleText = "", spacing = "default", style = {}, className = "", ...rest}) => {
  const spacingPresets = {
    default: {
      margin: "24px 0"
    },
    overlap: {
      margin: "-1rem 0 -1rem 0"
    },
    tight: {
      margin: "0 0 -1rem 0"
    },
    section: {
      margin: "0 0 -2rem 0"
    },
    sectionOverlap: {
      margin: "-1rem 0 -2rem 0"
    },
    deepOverlap: {
      margin: "-1rem 0 -1.5rem 0"
    }
  };
  const spacingStyle = spacingPresets[spacing] || spacingPresets.default;
  return <div role="separator" aria-orientation="horizontal" className={className} style={{
    display: "flex",
    alignItems: "center",
    ...spacingStyle,
    fontSize: style?.fontSize || "16px",
    height: "fit-content",
    ...style
  }} {...rest}>
      <span style={{
    marginRight: "var(--lp-spacing-px-8)",
    opacity: 0.2
  }}>
        <Icon icon="/snippets/assets/logos/Livepeer-Logo-Symbol-Theme.svg" />
      </span>
      <div style={{
    flex: 1,
    height: "1px",
    background: "var(--lp-color-border-default)",
    opacity: 0.4
  }}></div>
      {middleText && <>
          <Icon icon="circle" size={2} />
          <span style={{
    margin: "0 8px",
    fontWeight: "bold",
    color: color,
    opacity: 0.7
  }}>
            {middleText}
          </span>
          <Icon icon="circle" size={2} />
        </>}
      <div style={{
    flex: 1,
    height: "1px",
    background: "var(--lp-color-border-default)",
    opacity: 0.4
  }}></div>
      <span style={{
    marginLeft: "var(--lp-spacing-px-8)",
    opacity: 0.2
  }}>
        <span style={{
    display: "inline-block",
    transform: "scaleX(-1)"
  }}>
          <Icon icon="/snippets/assets/logos/Livepeer-Logo-Symbol-Theme.svg" />
        </span>
      </span>
    </div>;
};

export const LinkArrow = ({href, label, description, newline = true, borderColor, className = '', style = {}, ...rest}) => {
  const linkArrowStyle = {
    display: 'inline-flex',
    alignItems: 'center',
    justifyContent: 'center',
    gap: "var(--lp-spacing-1)",
    width: 'fit-content',
    ...borderColor && ({
      borderColor
    })
  };
  return <span className={className} style={style} {...rest}>
      {newline && <br />}
      <span style={linkArrowStyle}>
        <a href={href} target="_blank" rel="noopener noreferrer">
          {label}
        </a>
        <Icon icon="arrow-up-right" size={14} color="var(--lp-color-accent)" />
      </span>
      {description && description}
      {description && <div style={{
    height: "var(--lp-spacing-3)"
  }} />}
    </span>;
};

<CenteredContainer preset="readable90">
  <Tip>A PM ticket is a lottery ticket with a face value. Only winning tickets are submitted on-chain. The expected payout per ticket (face value multiplied by win probability) equals the agreed per-unit job price.</Tip>
</CenteredContainer>

***

Paying for every transcoding segment or AI inference request on-chain would make each transaction uneconomic given gas costs. Probabilistic Micropayments (PM) solve this by replacing per-job on-chain transactions with a lottery scheme that provides the correct expected payment in aggregate, with only a small fraction of tickets settled on-chain.

<CustomDivider />

## Tickets

For each job, the Gateway creates a PM ticket with:

* **Face value** (`faceVal`): the ETH amount the Orchestrator receives if the ticket wins
* **Win probability** (`winProb`): the fraction of tickets expected to win, expressed as a fraction of 2^256
* **Sender nonce**: a monotonically increasing counter preventing ticket replay
* **Recipient digest**: a hash of the segment data (for transcoding) or job ID (for AI)
* **Signature**: signed by the Gateway's Ethereum key (or remote signer)

The expected payment per ticket is `faceVal × winProb`. Ticket parameters are chosen so this product equals the per-unit price the Gateway is paying for the job:

```
expected_payment = face_value × win_probability
                 = price_per_pixel × pixel_count  (for transcoding)
                 = price_per_second × elapsed_seconds  (for live AI)
```

## Win Determination

The Orchestrator determines whether a ticket wins by computing:

```
ticket_hash = keccak256(ticket_params || senderNonce)
is_winner = ticket_hash < winProb
```

This is verifiable by both parties and cannot be manipulated by either. The `TicketBroker` contract validates the computation when the Orchestrator submits a winning ticket.

<CustomDivider />

## Sender Deposit and Penalty Escrow

Before sending jobs, a Gateway must fund two escrow accounts in `TicketBroker`:

**Sender deposit** (`deposit`): covers expected winning ticket payouts. When a winning ticket is submitted, `deposit` decreases by `faceVal`. If the deposit falls below the ticket's `faceVal`, the ticket is invalid.

**Penalty escrow** (`reserve`): slashed if the Gateway double-spends (submits the same ticket twice) or sends tickets with invalid signatures. This creates a financial disincentive for misbehaviour.

Orchestrators check the Gateway's deposit level before accepting jobs. A Gateway whose deposit is below a threshold will have jobs rejected until the deposit is replenished.

Fund a Gateway deposit:

```bash theme={"theme":{"light":"github-light","dark":"dark-plus"}}
livepeer_cli -sender-deposit <AMOUNT_ETH>
livepeer_cli -sender-reserve <AMOUNT_ETH>
```

Or via the `TicketBroker` contract directly:

```solidity theme={"theme":{"light":"github-light","dark":"dark-plus"}}
ticketBroker.fundDeposit{value: depositAmount}();
ticketBroker.fundReserve{value: reserveAmount}();
```

<CustomDivider />

## Configuring Ticket Parameters

The Gateway configures ticket parameters via the `-maxTicketEV` flag, which sets the maximum expected value per ticket:

```bash theme={"theme":{"light":"github-light","dark":"dark-plus"}}
livepeer -broadcaster \
  -maxTicketEV 3000000000000000 \  # max EV = 0.003 ETH
  ...
```

Orchestrators set `-pricePerUnit` (for transcoding, in wei per pixel) or per-second pricing in `aiModels.json` (for AI). The Gateway selects Orchestrators whose price falls within its configured maximum.

For AI pipelines, Orchestrators advertise pricing per pipeline. The recommended baseline pricing for each pipeline is listed in the AI pipeline reference pages.

<CustomDivider />

## From Ticket to Settlement

1. Gateway creates a ticket and sends it alongside the job to the Orchestrator
2. Orchestrator receives the job, checks the ticket is valid (deposit sufficient, signature correct, nonce fresh)
3. Orchestrator processes the job and returns the result
4. Orchestrator evaluates win probability: `keccak256(ticket) < winProb`
5. If winner: Orchestrator submits ticket to `TicketBroker` and receives `faceVal`
6. If not winner: ticket is discarded; Orchestrator retains the expected value as credit toward future winning tickets

Over many tickets, the Orchestrator receives approximately `sum(faceVal x winProb)`, the expected total compensation for all work performed.

<CustomDivider />

## Related Pages

<CardGroup cols={2}>
  <Card title="Payments Overview" icon="grid" href="/v2/developers/guides/payments/overview" arrow horizontal>
    Payment modes, billing models, and the Gateway deposit flow.
  </Card>

  <Card title="ETH Escrow and Deposits" icon="lock" href="/v2/developers/guides/payments/eth-escrow-and-deposits" arrow horizontal>
    Managing sender deposits and topping up escrow in TicketBroker.
  </Card>

  <Card title="Per-Second Compute" icon="clock" href="/v2/developers/guides/payments/per-second-compute" arrow horizontal>
    How per-second billing works for the live-video-to-video pipeline.
  </Card>

  <Card title="Remote Signer" icon="key" href="/v2/developers/guides/payments/remote-signer" arrow horizontal>
    Delegating ticket signing to a separate service for off-chain Gateways.
  </Card>
</CardGroup>
