Skip to content

feat: add invoice tranching (senior/junior) with waterfall repayment and loss allocation (#862) - #1023

Merged
sanmipaul merged 4 commits into
astera-hq:mainfrom
DIFoundation:issue#862
Jul 29, 2026
Merged

feat: add invoice tranching (senior/junior) with waterfall repayment and loss allocation (#862)#1023
sanmipaul merged 4 commits into
astera-hq:mainfrom
DIFoundation:issue#862

Conversation

@DIFoundation

Copy link
Copy Markdown
Contributor

Closes #862

Summary

Adds senior/junior invoice tranching with waterfall repayment and first-loss
allocation, spanning a new Soroban contract, SDK bindings, indexer aggregation,
and investor/admin frontend surfaces. Junior capital absorbs losses first;
senior capital receives priority repayment up to a time-proportional yield cap.

What's included

Contract (contracts/tranche/)

  • calculate_waterfall_split and calculate_loss_allocation as pure functions
  • Deposit/withdraw with senior advance-rate enforcement, invoice funding,
    waterfall repayment distribution, and loss allocation
  • Reentrancy guard on state-mutating entrypoints
  • Config admin (set_tranche_config, open_tranche_for_token) and read views
    (get_pool, get_position, get_effective_apy, simulate_waterfall)

SDK (packages/sdk, sdk/)

  • client.tranche namespace on AsteraClient: deposit, withdraw,
    getPool, getPosition, getTotals, getEffectiveApy, getInvoiceExposure,
    simulateWaterfall, setConfig
  • TrancheClient with real Soroban invocations (replaces the prior stubs)
  • trancheContractId added to AsteraConfig

Indexer (indexer/)

  • Routes tranche deposit/withdraw/fund/repay/default/config events
  • Derived tranche_apy table: realized return per tranche per token, recomputed
    from fund/repay/default events after each ingest batch
  • GET /tranches/apy and GET /tranches/:token/apy endpoints

Frontend (frontend/)

  • app/invest/tranches: side-by-side senior/junior cards (target vs.
    trailing-realized APY from the indexer), risk explainer, live deposit flow
  • app/portfolio: per-investor tranche-position section, hidden when the
    investor has no tranche exposure
  • app/admin/tranches: on-chain config editor
  • app/admin/waterfall-simulation: hypothetical-default simulation tool

Testing

  • 33 contract tests passing (unit, property-based, dust/rounding boundary,
    end-to-end scenarios)
  • Property tests cover: split sum invariant, senior-cap ceiling, monotonicity,
    loss sum invariant, junior-first ordering
  • E2E scenarios: junior absorbs a full default with senior kept whole; senior
    takes loss once junior is exhausted
  • Legacy (non-tranched) pool tests unchanged and still passing

Acceptance criteria

  • calculate_waterfall_split / calculate_loss_allocation pure + unit +
    property tested (zero junior balance, exact-threshold, dust/rounding)
  • E2E: junior absorbs full default, senior whole; senior takes loss after
    junior exhausted
  • Legacy pool deposit/withdraw/repay unchanged; existing tests pass
  • Tranche page shows trailing-realized junior APY sourced from indexer data

Notes

Merged latest main (picks up the #864 access-control work); resolved
astera-client.ts and types.ts to keep both the tranche and
accessControl namespaces.

@sanmipaul

Copy link
Copy Markdown
Contributor

Thanks for the scope of this, this is solid.

@sanmipaul
sanmipaul merged commit 92e93a0 into astera-hq:main Jul 29, 2026
2 of 5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add invoice tranching (senior/junior) with waterfall repayment and loss allocation

2 participants