Katkat

Build an Account Transfer Ledger

StripeSoftware Engineer · Onsite

Last updated

Design and implement an in-memory account ledger that supports deposits, transfers, and platform-backed lending with automatic debt repayment.

Problem Statement

You are tasked with building an internal account transfer ledger for a payments platform. The platform allows accounts to hold balances, transfer money to one another, and leverage a shared platform lending facility when an account does not have enough funds to cover an outgoing transfer.

Implement the TransferLedger class with the following methods:

  • __init__(credit_limit: int): Initializes the ledger with a maximum total credit limit that the platform can lend out across all accounts combined.
  • deposit(account_id: str, amount: int) -> None: Deposits amount into account_id. If the account has any outstanding debt, the deposit must pay down the debt first. Any remaining amount is credited to the account's cash balance.
  • transfer(sender: str, receiver: str, amount: int) -> bool: Attempts to transfer amount from sender to receiver.
    • Returns False if amount = amount`, the transfer proceeds normally.
    • If sender has a cash balance < amount, the platform attempts to lend the shortfall (amount - balance). If total_borrowed + shortfall <= credit_limit, the loan is granted: the sender's cash balance drops to 0, their personal debt increases by shortfall, and the platform's total_borrowed increases by shortfall.
    • If the shortfall exceeds available platform credit, the transfer fails completely, returning False with no balance or debt changes.
    • Transferred funds arriving at receiver must automatically pay down the receiver's outstanding debt first, with any remainder added to the receiver's cash balance.
    • Returns True if the transfer succeeds.
  • get_balance(account_id: str) -> int: Returns the current cash balance of account_id (always >= 0). Returns 0 if the account does not exist.
  • get_debt(account_id: str) -> int: Returns the current outstanding debt of account_id. Returns 0 if the account does not exist.
  • get_total_borrowed() -> int: Returns the total outstanding debt across all accounts currently financed by the platform.

Example

ledger = TransferLedger(credit_limit=100)

ledger.deposit("alice", 40)
assert ledger.get_balance("alice") == 40

# Alice transfers 60 to Bob. Shortfall is 20, which is covered by platform credit.
assert ledger.transfer("alice", "bob", 60) == True
assert ledger.get_balance("alice") == 0
assert ledger.get_debt("alice") == 20
assert ledger.get_balance("bob") == 60
assert ledger.get_total_borrowed() == 20

# Bob transfers 50 to Alice. Alice owes 20 debt, so 20 pays down debt, 30 goes to balance.
assert ledger.transfer("bob", "alice", 50) == True
assert ledger.get_debt("alice") == 0
assert ledger.get_balance("alice") == 30
assert ledger.get_balance("bob") == 10
assert ledger.get_total_borrowed() == 0

# Attempt transfer exceeding remaining platform credit (limit is 100)
assert ledger.transfer("bob", "alice", 200) == False

Constraints

  • 0 <= credit_limit <= 10^9
  • 1 <= amount <= 10^6 for all deposits and transfers
  • Total operations (calls to any method) <= 10^5
  • account_id, sender, and receiver consist of lowercase English letters

Hints

Hint 1

Track balance and debt separately for each account, along with a single global variable for total platform debt.

Hint 2

Write a helper function to credit an account, as both deposits and receiving transfers share the exact same repayment-then-balance logic.

Hint 3

Ensure transfers are atomic: validate both input arguments and platform credit capacity before mutating any sender or platform state.

Follow-up questions

  • How would you support per-account credit limits alongside the platform-wide credit limit?
  • How would you make this ledger thread-safe for high-concurrency environments without acquiring a single global lock for every operation?
  • How would you implement an audit log to reconstruct account states at any historical timestamp `t`?