livepeer / livepeer/protocol

TicketBroker: allow unlocking deposit separately from reserve

Open
#659 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

blockchain enhancement pm
Dominant language
JavaScript
Stars
155
Forks
50
PR merge metrics
No merged PRs in 30d

Description

## Summary

Gateways (ticket senders) currently must unlock and withdraw **deposit and reserve together**. Gateways that want to reclaim idle deposit while keeping reserve funded for ticket coverage cannot do so without also unlocking (and eventually withdrawing) their reserve.

At minimum, `TicketBroker` should support unlocking / withdrawing **deposit funds separately from reserve**. Optionally, deposit withdrawal could skip (or use a shorter) unlock period, since reserve is what covers winning tickets claimed during the unlock / following-round window.

## Current behavior

In [`MixinTicketBrokerCore`](https://github.com/livepeer/protocol/blob/delta/contracts/pm/mixins/MixinTicketBrokerCore.sol):

- `Sender` has a single `withdrawRound` shared by deposit + reserve.
- `unlock()` starts one unlock that applies to both balances.
- `withdraw()` always clears **both** `sender.deposit` and the sender's reserve (`clearReserve`), then transfers the sum.
- Ticket redemption requires the sender to be locked (`isLocked`), and pays from deposit first, then reserve when face value exceeds deposit.

```solidity
// withdraw() — deposit and reserve are inseparable today
sender.deposit = 0;
clearReserve(msg.sender);
withdrawTransfer(payable(msg.sender), deposit.add(reserve));
```

Funding paths already treat deposit and reserve independently (`fundDeposit`, `fundReserve`, `fundDepositAndReserve`), but unlock/withdraw do not.

## Motivation

1. **Capital efficiency for gateways** — A gateway may hold more deposit than needed for day-to-day ticket face values while keeping reserve sized for worst-case / following-round claims. Today, reclaiming excess deposit forces unlocking reserve too, which either:
- blocks further ticket redemptions once the unlock completes (`sender is unlocked`), or
- requires cancelling unlock / re-funding to stay operational.
2. **Reserve already covers the lock-period risk** — The unlock period exists so tickets created before unlock can still be redeemed afterward. Reserve is the mechanism that covers claims when deposit is exhausted during that window (and for the following round). If reserve remains locked and funded, withdrawing deposit does not remove that safety net.
3. **Symmetric with funding API** — Deposit and reserve can already be funded independently; unlock/withdraw should match that model.

## Proposed requirements

### Minimum (required)

- Support unlocking / withdrawing **deposit only**, without clearing or unlocking reserve.
- Reserve-only unlock/withdraw can remain as today, or stay coupled only when the sender explicitly unlocks both.
- Preserve existing security invariants for reserve:
- Reserve still subject to unlock period before withdrawal.
- Sender remains locked for ticket redemption as long as reserve (and any remaining deposit policy) requires it — exact locking rules to be specified in design.
- Events / getter updates so indexers and UIs can distinguish deposit vs reserve unlock/withdraw.

### Stretch / discuss

- Allow **immediate** (or shorter-period) deposit withdrawal while reserve stays locked, on the rationale that reserve covers tickets claimed in the unlock / following round.
- If deposit can be withdrawn without a lock period, document how redemption behaves when deposit is zero but reserve is still locked and funded (already partially supported: redemption allows `deposit == 0` if `remainingReserve > 0`).

## Design questions

1. **API shape** — e.g. `unlockDeposit()` / `withdrawDeposit()` vs parameterized `unlock(UnlockTarget)` / `withdraw(WithdrawTarget)`?
2. **Locking semantics** — If deposit is withdrawn but reserve remains, should `isLocked` stay true solely based on reserve state (likely yes, so tickets remain redeemable)?
3. **Partial deposit withdraw** — Withdraw full deposit only, or allow withdrawing an amount up to `sender.deposit`?
4. **Self-funding cancel behavior** — Today, funding deposit/reserve as the sender cancels an in-progress unlock. How should that interact with separate deposit vs reserve unlocks?
5. **Upgrade path** — Proxy upgrade of `TicketBroker` target; any storage layout changes for separate withdraw rounds?

## Acceptance criteria

- [ ] Gateway can withdraw deposit without withdrawing or unlocking reserve.
- [ ] Reserve remains available for winning ticket claims under the existing reserve claim rules while still locked.
- [ ] Existing `unlock` / `withdraw` behavior is either preserved (both) or clearly migrated with tests.
- [ ] Unit + integration tests covering: deposit-only unlock/withdraw, reserve still claimable, redemption with zero deposit + non-zero reserve, and no regression on full unlock/withdraw.
- [ ] Docs / comments updated for gateway operators on when to use deposit-only vs full unlock.

## References

- `contracts/pm/mixins/MixinTicketBrokerCore.sol` — `unlock`, `withdraw`, `redeemWinningTicket`, `Sender`
- `contracts/pm/mixins/MixinReserve.sol` — reserve claim / clear semantics
- Repo: https://github.com/livepeer/protocol

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reading contracts/pm/mixins/MixinTicketBrokerCore.sol, especially Sender, unlock, withdraw, and redeemWinningTicket, then compare reserve claim and clearing behavior in contracts/pm/mixins/MixinReserve.sol. Resolve the proposed locking and API semantics, implement deposit-only handling without weakening reserve claims, and add unit and integration coverage for deposit-only withdrawal, zero-deposit redemption, and existing full unlock/withdraw behavior.

Written by the indexing model from the issue text.

Assessment

Tech stack
solidity
Domain
blockchain
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Needs clarification
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.