> ## 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.

# On-Chain Metrics and Monitoring

> Monitor on-chain Gateway activity - TicketBroker contract events, Arbiscan watchlists, deposit and reserve verification, gas price impact, and deposit depletion tracking on Arbitrum.

export const StyledStep = ({title, icon, titleSize = 'h3', iconColor = null, titleColor = null, children, className = '', style = {}, ...rest}) => {
  const styledTitle = titleColor ? <span style={{
    color: titleColor
  }}>{title}</span> : title;
  return <Step title={styledTitle} icon={icon} iconColor={iconColor || undefined} titleSize={titleSize} className={className} style={style} {...rest}>
      {children}
    </Step>;
};

export const StyledSteps = ({children, iconColor, titleColor, lineColor, iconSize = '24px', className = '', style = {}, ...rest}) => {
  const resolvedIconColor = iconColor || 'var(--accent-dark, #18794E)';
  const resolvedTitleColor = titleColor || 'var(--lp-color-accent)';
  const resolvedLineColor = lineColor || 'var(--lp-color-accent)';
  return <div className={['docs-styled-steps', className].filter(Boolean).join(' ')} style={style} {...rest}>
      <style>{`
        .docs-styled-steps .steps > div > div.absolute > div {
          background-color: ${resolvedIconColor};
        }
        .docs-styled-steps .steps > div > div.w-full > p {
          color: ${resolvedTitleColor};
        }
        .docs-styled-steps .steps > div > div.absolute.w-px {
          background-color: ${resolvedLineColor};
        }
        .docs-styled-steps .steps > div:last-child > div.absolute.w-px::after {
          content: '';
          position: absolute;
          bottom: 0;
          left: 50%;
          transform: translateX(-50%);
          width: 6px;
          height: 6px;
          background-color: ${resolvedLineColor};
          transform: translateX(-50%) rotate(45deg);
        }
      `}</style>
      <div>
        <Steps>{children}</Steps>
      </div>
    </div>;
};

export const TableCell = ({children, align = "left", header = false, style = {}, className = "", ...rest}) => {
  const Component = header ? "th" : "td";
  return <Component className={className} style={{
    padding: "0.75rem 1rem",
    textAlign: align,
    border: header ? "none" : "1px solid var(--lp-color-border-default)",
    ...style
  }} {...rest}>
      {children}
    </Component>;
};

export const TableRow = ({children, header = false, hover = false, style = {}, className = "", ...rest}) => {
  const rowId = `table-row-${Math.random().toString(36).substr(2, 9)}`;
  return <>
      {hover && <style>{`
          #${rowId}:hover {
            background-color: var(--lp-color-bg-card);
          }
        `}</style>}
      <tr id={rowId} className={className} style={{
    ...header && ({
      backgroundColor: "var(--lp-color-accent-strong)",
      color: "var(--lp-color-on-accent)",
      fontWeight: "bold"
    }),
    ...style
  }} {...rest}>
        {children}
      </tr>
    </>;
};

export const StyledTable = ({children, variant = "default", style = {}, className = "", ...rest}) => {
  const wrapperVariants = {
    default: {
      border: "1px solid var(--lp-color-border-default)",
      backgroundColor: "var(--lp-color-bg-card)",
      overflow: "hidden"
    },
    bordered: {
      border: "2px solid var(--lp-color-accent)",
      backgroundColor: "var(--lp-color-bg-page)",
      overflow: "hidden"
    },
    minimal: {
      border: "none",
      backgroundColor: "transparent",
      overflow: "visible"
    }
  };
  return <div data-docs-styled-table-shell className={className} style={{
    width: "100%",
    padding: 0,
    margin: 0,
    ...wrapperVariants[variant],
    ...style
  }} {...rest}>
      <table data-docs-styled-table style={{
    width: "100%",
    borderCollapse: "collapse",
    borderSpacing: 0,
    margin: 0,
    backgroundColor: "transparent"
  }}>
        {children}
      </table>
    </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>;
};

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>;
};

<Note>
  This page applies to **Gateways running in on-chain operational mode** (<Badge color="blue">Video</Badge> <Badge color="purple">AI</Badge> <Badge color="green">Dual</Badge>). Off-chain Gateways do not interact with Arbitrum contracts and can skip this page.
</Note>

<CustomDivider style={{margin: "-1rem 0 -1rem 0"}} />

On-chain metrics show what is actually happening at the contract layer on Arbitrum

* where ETH is moving,
* which tickets Orchestrators are redeeming, and
* whether gas costs are affecting the payment system.

The Gateway's deposit and reserve on the `TicketBroker` contract fund all PM ticket redemptions.

When the deposit is exhausted:

* Orchestrators reject tickets,
* job routing stops **silently**, and
* Prometheus shows `livepeer_gateway_deposit` at zero.

Arbiscan can help debug the cause (which Orchestrators redeemed, how much, when).

For full context on PM mechanics, see <LinkArrow href="/v2/gateways/guides/payments-and-pricing/payment-guide" label="Payments Guide" newline={false} />.

<CustomDivider style={{margin: "-1rem 0 -2rem 0"}} />

## Arbiscan

[Arbiscan](https://arbiscan.io) is the block explorer for Arbitrum One. For on-chain Gateway operators, it is the primary tool for viewing transactions, watching TicketBroker redemption events, monitoring deposit changes, and checking gas price history.

### Find the Gateway

The Gateway's ETH address is shown in `livepeer_cli` \*\*Option 1 (Get node status) \*\*under **ETH Account**.

Navigate to:

```icon="terminal" theme={"theme":{"light":"github-light","dark":"dark-plus"}}
https://arbiscan.io/address/<GATEWAY_ETH_ADDRESS>
```

Key Arbiscan tabs:

<StyledTable>
  <TableRow header>
    <TableCell header>Tab</TableCell>
    <TableCell header>What it shows</TableCell>
  </TableRow>

  <TableRow>
    <TableCell>**Transactions**</TableCell>
    <TableCell>All outgoing transactions (bridging, deposits)</TableCell>
  </TableRow>

  <TableRow>
    <TableCell>**Internal Txns**</TableCell>
    <TableCell>Contract-initiated transfers - ticket redemptions appear here</TableCell>
  </TableRow>

  <TableRow>
    <TableCell>**Token Transfers**</TableCell>
    <TableCell>ERC-20 token movements (LPT bridging)</TableCell>
  </TableRow>
</StyledTable>

### Contract Events

The `TicketBroker` contract emits events for every deposit, reserve, and redemption action:

<StyledTable>
  <TableRow header>
    <TableCell header>Event</TableCell>
    <TableCell header>Meaning</TableCell>
    <TableCell header>When it fires</TableCell>
  </TableRow>

  <TableRow>
    <TableCell>`FundDeposit`</TableCell>
    <TableCell>ETH added to Gateway deposit</TableCell>
    <TableCell>livepeer\_cli Option 11 (Deposit broadcasting funds)</TableCell>
  </TableRow>

  <TableRow>
    <TableCell>`FundReserve`</TableCell>
    <TableCell>ETH added to reserve</TableCell>
    <TableCell>Reserve set during deposit flow</TableCell>
  </TableRow>

  <TableRow>
    <TableCell>`RedeemWinningTicket`</TableCell>
    <TableCell>Orchestrator redeemed a winning ticket</TableCell>
    <TableCell>Each winning ticket redemption</TableCell>
  </TableRow>

  <TableRow>
    <TableCell>`Unlock`</TableCell>
    <TableCell>Withdrawal unlock period started</TableCell>
    <TableCell>Unlock called on deposited funds</TableCell>
  </TableRow>

  <TableRow>
    <TableCell>`Withdraw`</TableCell>
    <TableCell>Funds withdrawn after unlock period</TableCell>
    <TableCell>Withdrawal after unlock expires</TableCell>
  </TableRow>
</StyledTable>

To find redemptions against a Gateway: filter Arbiscan events by the TicketBroker contract address, then filter by `RedeemWinningTicket` events where the `sender` field matches the Gateway address.
For the current TicketBroker address, see <LinkArrow href="/v2/about/resources/reference/livepeer-contract-addresses" label="Contract Addresses" newline={false} />.

### Watchlist Alerts

Arbiscan allows email alerts when a watched address is involved in a transaction. This is the fastest way to detect unexpected deposit activity.

1. Create an Arbiscan account at [Arbiscan.io](https://arbiscan.io)
2. Navigate to **My Account > Watch List > Add Address**
3. Enter the Gateway ETH address
4. Select notification triggers: "Transaction sent to" and "Transaction sent from"
5. Enable email notifications

An email arrives each time a ticket redemption occurs against the deposit, allowing early detection of unexpected drain rates.

<CustomDivider style={{margin: "-1rem 0 -2rem 0"}} />

## Deposit Verification

Prometheus shows the deposit via `livepeer_gateway_deposit`, but the [TicketBroker]("https://arbiscan.io/address/0xea1b0F6c8D158328a6e3D3F924B86A759F41465c") contract can also be queried directly to verify node-reported values against chain state.

### Status: livepeer\_cli

```bash icon="terminal" CLI: Get Node Status theme={"theme":{"light":"github-light","dark":"dark-plus"}}
livepeer_cli -host 127.0.0.1 -http 5935
# Select Option 1: Get node status
# Look for: BROADCASTER STATS > Deposit, Reserve
```

The CLI uses the legacy term "Broadcaster" for Gateway.

### Status: Arbitrum RPC

Query the [TicketBroker]("https://arbiscan.io/address/0xea1b0F6c8D158328a6e3D3F924B86A759F41465c") contract directly via **JSON-RPC** to get deposit and reserve independently of the running node:

```bash icon="terminal" CLI: Get Node Status theme={"theme":{"light":"github-light","dark":"dark-plus"}}
# Encode call to getSenderInfo(<gateway_address>)
# Function selector: first 4 bytes of keccak256("getSenderInfo(address)")
# Advanced technique - see Arbitrum/Solidity docs for ABI encoding

curl -X POST https://arb1.arbitrum.io/rpc \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_call",
    "params": [{
      "to": "<TICKET_BROKER_CONTRACT_ADDRESS>",
      "data": "<ENCODED_CALL>"
    }, "latest"],
    "id": 1
  }'
```

For most operators, `livepeer_cli` is sufficient. Direct contract queries are useful when the node is offline or when verifying that node-reported state matches chain state.

<CustomDivider style={{margin: "-1rem 0 -2rem 0"}} />

## Gas Monitoring

<Tip>
  Arbitrum gas fees are generally 90% to over 99% cheaper than Ethereum mainnet, with typical transactions costing only a few cents (approx. $0.01–$0.30).
  <br /><br /> As a Layer 2 rollup, Arbitrum bundles transactions, significantly reducing costs compared to the high fees often seen on Ethereum, especially during congestion

  Gas Prices are not generally a cause of error since the mirgration to Arbitrum from Ethereum Mainnet.
</Tip>

Arbitrum One gas prices can fluctuate.
When gas is expensive, Orchestrators pay more to redeem winning tickets - this cost is reflected in their pricing to Gateways. High gas periods can cause Orchestrators to increase prices above the `-maxPricePerUnit` cap, leading to routing failures even when the deposit is healthy.

### Current Gas Price

```bash icon="terminal" Query Gas Price theme={"theme":{"light":"github-light","dark":"dark-plus"}}
curl -X POST https://arb1.arbitrum.io/rpc \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","method":"eth_gasPrice","params":[],"id":1}'
```

The response is in wei. Convert to Gwei (divide by 1e9) for a human-readable figure.

### Prometheus gas metric

```promql icon="gauge-high" Query Gas Price theme={"theme":{"light":"github-light","dark":"dark-plus"}}
# Current suggested gas price from node's oracle (in wei)
livepeer_suggested_gas_price

# Convert to Gwei
livepeer_suggested_gas_price / 1e9
```

Alert when `livepeer_suggested_gas_price` spikes significantly above its baseline - this is a leading indicator that Orchestrators may raise prices above the cap, causing job routing to slow.

### Gas spike response

During sustained high-gas periods:

1. **Raise `-maxPricePerUnit` temporarily** to keep jobs routing to Orchestrators absorbing higher redemption costs
2. **Monitor `livepeer_payment_create_errors`** - increases during gas spikes indicate ticket-related failures
3. **Use USD-mode pricing** (`-priceFeedAddr`) for stable USD-denominated caps that auto-adjust with ETH price. See <LinkArrow href="/v2/gateways/guides/payments-and-pricing/pricing-strategy" label="Pricing Strategy" newline={false} />

<CustomDivider style={{margin: "-1rem 0 -2rem 0"}} />

## Depletion Tracking

Understanding how fast the deposit depletes helps plan top-ups before service interruptions.

### From Prometheus

```promql icon="terminal" theme={"theme":{"light":"github-light","dark":"dark-plus"}}
# Deposit depletion rate (wei per second, negative = spending)
deriv(livepeer_gateway_deposit[1h])

# Estimated time to deposit exhaustion at current rate (seconds)
livepeer_gateway_deposit /
  abs(deriv(livepeer_gateway_deposit[1h]))
```

### From Arbiscan

Sort `RedeemWinningTicket` events by timestamp to see redemption frequency and value patterns. Unusually large or frequent redemptions from a single Orchestrator may indicate that Orchestrator is experiencing high gas costs, or in a worst case, receiving a disproportionate share of winning tickets.

<CustomDivider style={{margin: "-1rem 0 -2rem 0"}} />

## Diagnostic Checklist

If the Gateway stops routing jobs without clear error messages, work through this checklist in order:

<StyledSteps iconColor="var(--lp-color-accent)" titleColor="var(--accent)">
  <StyledStep title="Check Prometheus deposit" icon="gauge-high">
    `livepeer_gateway_deposit` - if zero, top up immediately via <LinkArrow href="/v2/gateways/guides/payments-and-pricing/funding-guide" label="Funding Guide" newline={false} />. If non-zero, continue.
  </StyledStep>

  <StyledStep title="Check livepeer_cli" icon="terminal">
    Option 1: Get node status. Under BROADCASTER STATS (legacy term for Gateway), both Deposit and Reserve must be non-zero.
  </StyledStep>

  <StyledStep title="Check Arbiscan redemptions" icon="magnifying-glass">
    Look for a burst of `RedeemWinningTicket` events in the last 30-60 minutes. A sudden burst may have drained the deposit faster than expected.
  </StyledStep>

  <StyledStep title="Test Arbitrum RPC" icon="plug">
    `curl -X POST <ethUrl> -d '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}'` - if this fails, the RPC is the problem, not the deposit.
  </StyledStep>

  <StyledStep title="Check gas price" icon="gas-pump">
    `livepeer_suggested_gas_price` - if gas spiked, Orchestrators may have raised prices above `-maxPricePerUnit`.
  </StyledStep>

  <StyledStep title="Verify Orchestrators" icon="server">
    Visit [Livepeer Explorer](https://explorer.livepeer.org) to confirm the configured Orchestrators are active.
  </StyledStep>
</StyledSteps>

<CustomDivider style={{margin: "0 0 -2rem 0"}} />

## Related Pages

<CardGroup cols={3}>
  <Card title="Contract Addresses" icon="file-contract" href="/v2/about/resources/reference/livepeer-contract-addresses">
    TicketBroker, BondingManager, and all Livepeer contracts on Arbitrum.
  </Card>

  <Card title="Funding Guide" icon="coins" href="/v2/gateways/guides/payments-and-pricing/funding-guide">
    Top up deposit and reserve via livepeer\_cli.
  </Card>

  <Card title="Monitoring Setup" icon="chart-line" href="/v2/gateways/guides/monitoring-and-tooling/monitoring-setup">
    Prometheus + Grafana for node-level metrics.
  </Card>
</CardGroup>
