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

# ETH Escrow and Deposits

> Managing gateway sender deposits and penalty escrow in the Livepeer TicketBroker contract on Arbitrum One.

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 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 Gateway must hold a sender deposit in TicketBroker before sending jobs. Orchestrators check deposit levels before accepting tickets. A deposit below the ticket face value causes job rejection.</Tip>
</CenteredContainer>

***

Gateways fund two accounts in the `TicketBroker` contract on Arbitrum One before sending jobs: a sender deposit and a penalty escrow (reserve). These ETH balances back the probabilistic micropayment tickets the Gateway sends to Orchestrators.

<CustomDivider />

## Deposit vs Reserve

<StyledTable variant="bordered">
  <thead>
    <TableRow header>
      <TableCell header>Account</TableCell>
      <TableCell header>Purpose</TableCell>
      <TableCell header>Decreases when</TableCell>
    </TableRow>
  </thead>

  <tbody>
    <TableRow>
      <TableCell>**Sender deposit**</TableCell>
      <TableCell>Covers winning ticket payouts. Each winning ticket reduces the deposit by the face value.</TableCell>
      <TableCell>Orchestrator redeems a winning ticket</TableCell>
    </TableRow>

    <TableRow>
      <TableCell>**Penalty escrow (reserve)**</TableCell>
      <TableCell>Slashed for misbehaviour: double-spending tickets or sending invalid signatures.</TableCell>
      <TableCell>Slashing event (protocol violation)</TableCell>
    </TableRow>
  </tbody>
</StyledTable>

Orchestrators query the Gateway's deposit before accepting a ticket. If `deposit < faceVal`, the ticket is invalid and the job is rejected. Most Orchestrators also apply a minimum deposit threshold above `faceVal` to account for concurrent sessions.

<CustomDivider />

## Funding Deposits via livepeer\_cli

```bash theme={"theme":{"light":"github-light","dark":"dark-plus"}}
# Fund sender deposit
livepeer_cli -sender-deposit <AMOUNT_IN_ETH>

# Fund penalty reserve
livepeer_cli -sender-reserve <AMOUNT_IN_ETH>
```

The `livepeer_cli` binary ships alongside go-livepeer. Both commands prompt for confirmation before submitting the on-chain transaction.

## Funding Deposits via Contract

Call `TicketBroker` directly on Arbitrum One using cast (Foundry) or ethers.js:

```bash theme={"theme":{"light":"github-light","dark":"dark-plus"}}
# Using cast (Foundry)
cast send <TICKET_BROKER_ADDRESS> \
  "fundDeposit()" \
  --value <AMOUNT_IN_WEI> \
  --rpc-url <ARBITRUM_RPC_URL> \
  --private-key <GATEWAY_KEY>

cast send <TICKET_BROKER_ADDRESS> \
  "fundReserve()" \
  --value <AMOUNT_IN_WEI> \
  --rpc-url <ARBITRUM_RPC_URL> \
  --private-key <GATEWAY_KEY>
```

Replace `<TICKET_BROKER_ADDRESS>` with the address from the <LinkArrow href="/v2/about/resources/reference/livepeer-contract-addresses" label="Contract Addresses" newline={false} /> reference.

<CustomDivider />

## Withdrawing Deposits

Withdrawals from `TicketBroker` require an unlock period. The Gateway initiates an unlock, waits for the unlock period to expire, then withdraws:

```bash theme={"theme":{"light":"github-light","dark":"dark-plus"}}
livepeer_cli -unlock-deposit
# Wait for unlock period (defined by protocol)
livepeer_cli -withdraw-deposit
```

The unlock period prevents Gateways from withdrawing funds that have already been committed to in-flight tickets. Orchestrators can still redeem outstanding winning tickets during the unlock period.

<CustomDivider />

## Checking Deposit Status

Query current deposit levels:

```bash theme={"theme":{"light":"github-light","dark":"dark-plus"}}
livepeer_cli -status
```

The output includes the sender deposit and reserve balances for the Gateway's Ethereum address.

For programmatic monitoring, call `getSenderInfo(address)` on `TicketBroker`:

```javascript theme={"theme":{"light":"github-light","dark":"dark-plus"}}
const ticketBroker = new ethers.Contract(
  TICKET_BROKER_ADDRESS,
  TICKET_BROKER_ABI,
  provider,
);

const info = await ticketBroker.getSenderInfo(gatewayAddress);
console.log('deposit:', ethers.formatEther(info.sender.deposit));
console.log('reserve:', ethers.formatEther(info.reserve.funds));
```

<Note>
  The Livepeer Network runs on Arbitrum One, not Ethereum mainnet. Ensure your RPC URL points to an Arbitrum One endpoint and that your Gateway holds ETH on Arbitrum One, not Ethereum L1.
</Note>

<CustomDivider />

## Related Pages

<CardGroup cols={2}>
  <Card title="Probabilistic Micropayments" icon="coin" href="/v2/developers/guides/payments/probabilistic-micropayments" arrow horizontal>
    How tickets draw on the sender deposit and what makes a ticket valid.
  </Card>

  <Card title="Payments Overview" icon="grid" href="/v2/developers/guides/payments/overview" arrow horizontal>
    Payment modes and Gateway deposit flow overview.
  </Card>

  <Card title="Contract Addresses" icon="file-code" href="/v2/about/resources/reference/livepeer-contract-addresses" arrow horizontal>
    TicketBroker address on Arbitrum One.
  </Card>
</CardGroup>
