Reported November 2023
Rampdesign

Banking System with Accepted Transfers, Part 3: Pending Transfer Acceptance

Reported by candidates from Ramp's online assessment. Pattern, common pitfall, and the honest play if you blank under the timer.

Get StealthCoderRuns invisibly during the live Ramp OA. Under 2s to a working solution.
Founder's read

Ramp reported this banking system OA in November 2023, and Part 3 is where the clean CRUD-style problem turns into a state machine. Timestamps go up to 10^9 and expiry is 86400000 ms, so you can't scan every transfer on every query. It's a design problem: hash maps for accounts, a map for pending transfers, and lazy expiry. Each level stacks on the last, so a sloppy Part 1 will bite you later. If you blank mid-assessment, StealthCoder runs invisibly as a safety net, but the pattern below is enough to walk in with.

The problem

Implement a simplified banking system. The assessment is cumulative: after unlocking a new level, every operation from the current and previous levels remains available.
Query and Output Rules
Each query calls exactly one operation and includes a stringified millisecond timestamp.
Timestamps are unique, lie between 1 and 10^9, and appear in strictly increasing order.
Return one string result for every query, in input order.
Return the empty string when an operation has no successful scalar result, exactly as specified below.
Multipart Series
Part 1: Accounts and Payments
Part 2: Activity Ranking
Part 3: Pending Transfer Acceptance
Part 4: Account Merging and Balance History
Level 1: Accounts and Payments
The banking system should support creating new accounts, depositing money, and withdrawing or paying money from accounts.
CREATE_ACCOUNT <timestamp> <accountId> creates a new account with the given accountId if it does not already exist. Return "true" if the account is created and "false" if it already exists.
DEPOSIT <timestamp> <accountId> <amount> deposits amount into the account. Return the account balance after processing the query, or the empty string if the account does not exist.
PAY <timestamp> <accountId> <amount> withdraws amount from the account. Return the account balance after processing the query. Return the empty string if the account does not exist or has insufficient funds.
Level 2: Activity Ranking
The banking system should support ranking accounts by the total value of their transactions.
TOP_ACTIVITY <timestamp> <n> returns the top n accounts with the highest total transaction value, sorted by total value descending and then by accountId alphabetically ascending.
Return one string in the format "<accountId1>(<transactionsValue1>),..., <accountIdN>(<transactionsValueN>)".
Total transaction value is the sum of every processed amount for an account, regardless of how it changes the balance: deposits, payments, and each side of a successfully accepted transfer count.
If fewer than n accounts exist, return all active accounts in the same format.
Level 3: Pending Transfer Acceptance
The banking system should allow transfers to be scheduled and their status to be resolved by the target account.
TRANSFER <timestamp> <sourceAccountId> <targetAccountId> <amount> initiates a transfer. Withdraw amount from the source immediately and hold it until the target accepts the transfer or it expires. Refund the held money to the source when it expires.
Return the empty string when the source equals the target, either account is absent, or the source has insufficient funds.
A transfer expires after 24 * 60 * 60 * 1000 = 86400000 milliseconds. It expires at the beginning of the next millisecond after that period, so acceptance at the exact expiration timestamp is still valid.
A valid transfer returns the next global ID: "transfer1", "transfer2", and so on. Failed transfers do not consume an ID.
The source and target transaction histories are updated only after acceptance. A successfully accepted transfer contributes its amount to the total transaction value of both accounts.
ACCEPT_TRANSFER <timestamp> <accountId> <transferId> accepts a pending transfer. Return "true" on success. Return "false" if the transfer does not exist, was already accepted, expired, or accountId is not its target.

Function
bankingAcceptedTransfersLevel3(queries: String[][]) → String[]

Examples
Example 1
queries = [["CREATE_ACCOUNT","1","account1"],["CREATE_ACCOUNT","2","account1"],["CREATE_ACCOUNT","3","account2"],["DEPOSIT","4","non-existing","2700"],["DEPOSIT","5","account1","2700"],["PAY","6","non-existing","2700"],["PAY","7","account1","2701"],["PAY","8","account1","200"]]
return = ["true","false","true","","2700","","","2500"]
The duplicate account creation fails. Missing-account operations and an overdraw return empty strings. The final payment succeeds and leaves account1 with 2500.
Example 2
queries = [["CREATE_ACCOUNT","1","account1"],["CREATE_ACCOUNT","2","account2"],["CREATE_ACCOUNT","3","account3"],["DEPOSIT","4","account1","2000"],["DEPOSIT","5","account2","3000"],["DEPOSIT","6","account3","4000"],["TOP_ACTIVITY","7","3"],["PAY","8","account1","1500"],["PAY","9","account2","250"],["DEPOSIT","10","account3","250"],["TOP_ACTIVITY","11","3"]]
return = ["true","true","true","2000","3000","4000","account3(4000), account2(3000), account1(2000)","500","2750","4250","account3(4250), account1(3500), account2(3250)"]
The first ranking follows the three deposits. Payments count toward total activity even though they reduce balances, so the second ranking uses totals 4250, 3500, and 3250.
Example 3
queries = [["CREATE_ACCOUNT","1","account1"],["CREATE_ACCOUNT","2","account2"],["DEPOSIT","3","account1","2000"],["DEPOSIT","4","account2","3000"],["TRANSFER","5","account1","account2","5000"],["TRANSFER","16","account1","account2","1000"],["ACCEPT_TRANSFER","20","account1","transfer1"],["ACCEPT_TRANSFER","21","non-existing","transfer1"],["ACCEPT_TRANSFER","22","account1","transfer2"],["ACCEPT_TRANSFER","25","account2","transfer1"],["ACCEPT_TRANSFER","30","account2","transfer1"],["TRANSFER","40","account1","account2","1000"],["ACCEPT_TRANSFER","86400045","account2","transfer2"],["TRANSFER","86400050","account1","account1","1000"]]
return = ["true","true","2000","3000","","transfer1","false","false","false","true","false","transfer2","false",""]
The first transfer fails for insufficient funds. Only account2 can accept transfer1, and it can be accepted only once. transfer2 expires before timestamp 86400045, so its held funds are refunded and acceptance fails.

Constraints
1 <= queries.length <= 500.
Every timestamp is a unique integer from 1 through 10^9, and queries are supplied in strictly increasing timestamp order.
Every query row is well formed and uses an operation available at this part.
Amounts and n are positive base-10 integers. Every balance and transaction total fits in a signed 64-bit integer.
Account identifiers are non-empty strings that do not contain commas or parentheses.

Reported by candidates. Source: FastPrep

Pattern and pitfall

The trick is lazy expiry. Don't run a timer or sweep all transfers every query. Store each transfer with its source, target, amount, creation timestamp and a status. When any operation touches a transfer, or when you need a balance, check whether timestamp > created + 86400000 and refund then. Since timestamps strictly increase, you can also keep pending transfers in a queue ordered by creation and pop expired ones from the front before each query. That's amortized O(1). Pitfalls: acceptance at exactly created + 86400000 is still valid, expiry starts the next millisecond. Failed transfers must not consume an ID. Transaction totals for TOP_ACTIVITY update only on acceptance, for both accounts. Money is held off the source balance while pending, so a refund must happen before the next PAY or DEPOSIT check. StealthCoder is the hedge if you freeze on the expiry ordering during the live OA.

StealthCoder is the hedge for the one pattern you didn't drill. It runs invisibly during the screen share.

If this hits your live OA

You can drill Banking System with Accepted Transfers, Part 3: Pending Transfer Acceptance cold, or you can hedge it. StealthCoder runs invisibly during screen share and surfaces a working solution in under 2 seconds. The proctor sees the IDE. They don't see what's behind it. If you're reading this with an OA window open, you're who this was built for.

Get StealthCoder

Related leaked OAs

⏵ The honest play

You've seen the question. Make sure you actually pass Ramp's OA.

Ramp reuses patterns across OAs. If you're reading this with an OA window open, you're who this was built for. Works on HackerRank, CodeSignal, CoderPad, and Karat.

Banking System with Accepted Transfers, Part 3: Pending Transfer Acceptance FAQ

What's the trick to the Ramp banking system Part 3?+

Lazy expiry. Keep pending transfers in a map keyed by ID with a creation timestamp. Before processing each query, refund any transfer whose creation time plus 86400000 is less than the current timestamp. No timers, no full scans per query.

Is acceptance at the exact expiration timestamp valid?+

Yes. The transfer expires at the start of the next millisecond. So if the current timestamp equals created + 86400000, ACCEPT_TRANSFER returns true. Use a strict greater-than check when deciding a transfer has expired.

Do failed transfers use up a transfer ID?+

No. Only valid transfers get the next global ID. Validate first: source equals target, missing account, or insufficient funds all return an empty string, and your counter stays put. Increment it only after the withdrawal succeeds.

When does a transfer count toward TOP_ACTIVITY?+

Only after acceptance. A pending or expired transfer adds nothing. On a successful accept, add the amount to both the source and target totals. Deposits and payments still count immediately, same as Level 2.

How do I prepare for this in 48 hours?+

Write the Level 1 and 2 operations cleanly first, since later levels reuse them. Then add the transfer map, the ID counter and the expiry check. Test the boundary timestamps by hand: exactly 86400000 after, and 86400001 after. Practice the whole class, not isolated functions.

Problem reported by candidates from a real Online Assessment. Sourced from a publicly-available candidate-aggregated repository. Not affiliated with Ramp.

OA at Ramp?
Invisible during screen share
Get it