Katkat

Banking System Simulation

AnthropicSoftware Engineer · Onsite

Last updated

This question evaluates a candidate's ability to design a stateful, multi-level simulation system that processes a time-ordered stream of operations. It tests object-oriented design, incremental complexity management, and handling of deferred effects like scheduled cashback credits. Such object-modeling questions are commonly used in coding interviews to assess practical application of data structures under evolving requirements.

Banking System Simulation

You are building an in-memory banking system that is driven by a time-ordered stream of operations. Implement the system so that it processes a list of queries and returns one result per query.

Each query is a list of strings. The first element is the operation name; the remaining elements are its string arguments. The second element of every query is a timestamp (an integer number of milliseconds, given in strictly increasing order across the whole input). Amounts are non-negative integers. Account identifiers are arbitrary non-empty strings.

Your function receives queries: List[List[str]] and must return List[str] — the result of each query as a string (use an empty string "" when an operation is invalid, as defined below). Numbers in the output are rendered without a currency symbol or thousands separators (e.g. "1500").

The system is leveled: implement the levels in order. Each higher level adds operations; earlier operations keep working unchanged.

Level 1 — Accounts and balances

  • ["CREATE_ACCOUNT", timestamp, account_id] → "true" if a new account is created; "false" if an account with that id already exists.
  • ["DEPOSIT", timestamp, account_id, amount] → the account's new balance as a string. If the account does not exist, return "".
  • ["GET_BALANCE", timestamp, account_id] → the account's current balance. If the account does not exist (or no longer exists), return "".

Level 2 — Transfers and spending ranks

  • ["TRANSFER", timestamp, source_id, target_id, amount] → the source account's resulting balance. Return "" if either account is missing, if source_id == target_id, or if the source has insufficient funds (no transfer happens in that case).
  • ["TOP_SPENDERS", timestamp, n] → the top n accounts ranked by total outgoing amount (the sum of all TRANSFER amounts they sent plus all PAY amounts, see Level 3), as a comma-and-space separated string: "id1(total1), id2(total2)". Rank highest total first; break ties by account id in ascending lexicographic order. Include every existing account; if fewer than n accounts exist, return all of them.

Level 3 — Payments with deferred cashback

  • ["PAY", timestamp, account_id, amount] → debits amount immediately and returns a new payment id of the form "payment1", "payment2", … (a single global counter, incremented on every successful PAY). Return "" if the account is missing or has insufficient funds. Each payment earns 2% cashback (floor(amount * 2 / 100)) that is credited back to the same account exactly 86400000 ms (24 hours) after the payment timestamp.
  • ["GET_PAYMENT_STATUS", timestamp, account_id, payment] → "IN_PROGRESS" if the cashback has not yet been credited at timestamp, "CASHBACK_RECEIVED" if it has. Return "" if the account does not exist or the payment id does not belong to that account.

Cashback that is due must be settled (added to the balance) before evaluating any operation whose timestamp is at or after the cashback time. In other words, every operation should first apply all pending cashbacks whose credit time is <= timestamp.

Level 4 — Account merge

  • ["MERGE_ACCOUNTS", timestamp, account_id_1, account_id_2] → "true" on success, "false" if either account is missing or account_id_1 == account_id_2. Merging folds account_id_2 into account_id_1: balances are summed, outgoing-total histories are summed, and any of account 2's pending cashbacks are redirected to account 1. After the merge, account_id_2 no longer exists, but a GET_PAYMENT_STATUS query for a payment originally made by account 2 must still succeed when issued against account_id_1.

Constraints

  • 1 <= len(queries) <= 10^4.
  • Timestamps are strictly increasing integers in [1, 10^15].
  • Amounts are integers in [0, 10^9].
  • All outputs are strings.

Example

Input:

[
 ["CREATE_ACCOUNT", "1", "alice"],
 ["CREATE_ACCOUNT", "2", "bob"],
 ["DEPOSIT", "3", "alice", "2000"],
 ["TRANSFER", "4", "alice", "bob", "500"],
 ["TOP_SPENDERS", "5", "2"],
 ["PAY", "6", "alice", "1000"],
 ["GET_PAYMENT_STATUS", "7", "alice", "payment1"],
 ["GET_PAYMENT_STATUS", "86400007", "alice", "payment1"],
 ["GET_BALANCE", "86400008", "alice"]
]

Output:

["true", "true", "2000", "1500", "alice(500), bob(0)", "payment1", "IN_PROGRESS", "CASHBACK_RECEIVED", "520"]

Explanation of the last two lines: alice paid 1000 at t=6, leaving 500. At t=86400006 (24h later) the 2% cashback of 20 is credited, so by t=86400008 her balance is 500 + 20 = 520.