In traditional accounting, all cash is fungible—any dollar can be used for any purpose. But ESOP trust cash is different. The source of cash determines how it can be legally used.
Using restricted cash for the wrong purpose can result in ERISA violations, DOL audits, and plan disqualification.
What It Is: The sum of all cash held in individual employee accounts.How It Gets There:
Cash dividends on ESOP shares
Proceeds from diversification elections
Forfeitures reallocated as cash
Usage Restrictions:
✅ Can be used for distributions to that participant
✅ Can fund repurchases per plan document
❌ Cannot be used for plan expenses
❌ Cannot be used for other participants
Copy
Ask AI
# Example: Participant has $5,000 cash in account# Can be distributed to participant on termination# Or used to repurchase their shares (per plan rules)
Unallocated Company Contributions
What It Is: Company contributions that have been received but not yet credited to individual participants.How It Gets There:
Annual company cash contributions
Loan proceeds (for leveraged ESOPs)
Usage Restrictions:
✅ Can be used for share purchases
✅ Can fund repurchases (per plan document)
✅ Can be allocated to participants
✅ Flexible use per cash_usage_policy
Copy
Ask AI
# Most flexible cash source# Temporary holding account before allocation
Timing Gap: There’s often a delay between when a contribution is made and when shares are allocated. This account bridges that gap.
Unallocated Forfeiture Cash
What It Is: Cash from the non-vested accounts of terminated participants.How It Gets There:
Terminated participant had non-vested shares
Shares sold, proceeds held here
Usage Restrictions:
✅ Can reduce company contributions (most common)
✅ Can be reallocated to remaining participants
✅ Can fund administrative expenses (if plan allows)
⚠️ Usage strictly governed by plan document
Copy
Ask AI
# Example: Employee terminates with 40% vesting# Non-vested 60% becomes forfeiture# Shares sold → cash held here
Highly Restricted: Plan document specifies exactly how forfeitures can be used. Deviation can cause plan disqualification.
When the trust needs cash (e.g., for repurchases), it draws from sources in a specific order defined by PlanRules.cash_usage_policy:
Copy
Ask AI
# Example cash_usage_policycash_usage_policy = [ "unallocated_company_contributions", # Draw from here first "unallocated_forfeiture_cash", # Then here "participant_cash_accounts" # Last resort]# Process a $500,000 repurchaseledger = TrustCashLedger( participant_cash_accounts=150_000, unallocated_contributions=200_000, unallocated_forfeitures=100_000)result = ledger.draw_cash( amount=500_000, sources=cash_usage_policy)# Execution:# 1. Draw $200K from unallocated_contributions → $300K remaining# 2. Draw $100K from unallocated_forfeitures → $200K remaining# 3. Draw $200K from participant_cash → $0 remaining ✓
def draw_cash( self, amount: Decimal, sources: List[str]) -> CashDrawResult: """ Draw cash following the waterfall sequence. """ remaining_need = amount transactions = [] for source in sources: if remaining_need <= 0: break available = getattr(self, source) amount_to_draw = min(available, remaining_need) if amount_to_draw > 0: # Deduct from source setattr(self, source, available - amount_to_draw) remaining_need -= amount_to_draw transactions.append( CashTransaction( source=source, amount=amount_to_draw ) ) return CashDrawResult( requested=amount, drawn=amount - remaining_need, shortfall=remaining_need, transactions=transactions )
Copy
Ask AI
def deposit_cash( self, amount: Decimal, source: str): """ Add cash to a specific source. """ current = getattr(self, source) setattr(self, source, current + amount)# Example usageledger.deposit_cash( amount=500_000, source="unallocated_company_contributions")
Copy
Ask AI
def transfer( self, amount: Decimal, from_source: str, to_source: str): """ Move cash between sources (e.g., allocation). """ # Validate sufficient funds available = getattr(self, from_source) if available < amount: raise InsufficientFundsError() # Execute transfer setattr(self, from_source, available - amount) current_dest = getattr(self, to_source) setattr(self, to_source, current_dest + amount)# Example: Allocate forfeituresledger.transfer( amount=25_000, from_source="unallocated_forfeiture_cash", to_source="participant_cash_accounts")
Copy
Ask AI
def validate(self) -> List[ValidationError]: """ Ensure all cash balances are non-negative. """ errors = [] if self.participant_cash_accounts < 0: errors.append(ValidationError( "Negative participant cash", self.participant_cash_accounts )) if self.unallocated_company_contributions < 0: errors.append(ValidationError( "Negative unallocated contributions", self.unallocated_company_contributions )) if self.unallocated_forfeiture_cash < 0: errors.append(ValidationError( "Negative forfeiture cash", self.unallocated_forfeiture_cash )) return errors