Skip to content

Tranche

ERC-4626 share token for one side of a TranchedVault waterfall — senior (skUSD) or junior (jkUSD). Same contract for both roles; is_senior immutable picks which vault hook runs on deposit/exit.

Source: khomdev-keep/src/Tranche.vy (Vyper 0.4.x)

Concept guide: Tranches protocol page

Pairs with: TranchedVault · underlying USDC via vault

Implementation overview

  • NAVtotalAssets() reads senior_value or junior_value from TranchedVault.
  • Share math — ERC-4626 with DECIMALS_OFFSET = 6 virtual shares (OpenZeppelin-style inflation guard).
  • Deposits — pull USDC → approve TV → deposit_senior / deposit_junior; mint tranche shares.
  • Exits — burn shares → process_senior_exit / process_junior_exit; penalty handled in TV.
  • Impairment guard — if totalSupply > 0 and tranche value < impaired_floor, new deposits blocked (prevents re-subscription dilution after wipe).
  • Penalty semanticswithdraw burns shares for gross assets; receiver gets net after TV penalty. redeem returns net paid. preview* exclude penalty (standard “if-no-fees”).
flowchart LR
    U[User USDC] --> T[Tranche ERC-4626]
    T --> TV[TranchedVault]
    TV --> MSV[MultiStrategyVault]

Immutables

Name Role
asset Underlying USDC
tranched_vault Parent TranchedVault
is_senior True → skUSD hooks; False → jkUSD
impaired_floor Min tranche value (1 whole token) before deposits reopen after impairment

ERC-20 surface

Standard ERC-20 + EIP-2612 permit re-exported from snekmate erc20 (transfer, approve, permit, etc.). Not duplicated here.

Events

Event When
Deposit Shares minted on deposit/mint
Withdraw Shares burned on withdraw/redeem (logs net assets paid)

ERC-4626 views

totalAssets

Tranche.totalAssets() -> uint256

Live tranche NAV in underlying USDC — not token balance held by this contract.

Returns: uint256senior_value() or junior_value() on parent TranchedVault, per is_senior.

Access: view, any caller.

Source (khomdev-keep/src/Tranche.vy:149-153, _total_assets 297-300):

@external
@view
def totalAssets() -> uint256:
    return self._total_assets()

# internal:
if is_senior:
    return ITranchedVault(tranched_vault).senior_value()
return ITranchedVault(tranched_vault).junior_value()

Example:

nav = tranche.totalAssets()

Cross-links: convertToShares · TranchedVault senior_value · TranchedVault junior_value

convertToShares

Tranche.convertToShares(assets_in) -> uint256

ERC-4626 quote: underlying USDC → tranche shares (floor rounding).

Param Type Description
assets_in uint256 USDC amount (6 decimals)

Returns: uint256 — share amount. Uses virtual shares (DECIMALS_OFFSET = 6) against live totalAssets.

Access: view, any caller.

Source (khomdev-keep/src/Tranche.vy:158-162, _convert_to_shares 304-310):

@external
@view
def convertToShares(assets_in: uint256) -> uint256:
    return self._convert_to_shares(assets_in, False)

# shares = assets * (totalSupply + 10**6) / (totalAssets + 1)

Example:

shares = tranche.convertToShares(10_000 * 10**6)

Cross-links: convertToAssets · totalAssets · previewDeposit

convertToAssets

Tranche.convertToAssets(shares) -> uint256

ERC-4626 quote: tranche shares → underlying USDC (floor rounding).

Param Type Description
shares uint256 Tranche share amount

Returns: uint256 — USDC value at current totalAssets / totalSupply. Excludes early-exit penalty (standard if-no-fees quote).

Access: view, any caller.

Source (khomdev-keep/src/Tranche.vy:167-171, _convert_to_assets 314-320):

@external
@view
def convertToAssets(shares: uint256) -> uint256:
    return self._convert_to_assets(shares, False)

# assets = shares * (totalAssets + 1) / (totalSupply + 10**6)

Example:

assets = tranche.convertToAssets(shares)

Cross-links: convertToShares · totalAssets · previewRedeem

maxDeposit

Tranche.maxDeposit(receiver) -> uint256

ERC-4626 deposit cap query.

Param Type Description
receiver address Share recipient (unused in limit math; ERC-4626 signature)

Returns: uint256 — always max_uint256. No on-chain deposit cap in this view.

Access: view, any caller.

Note: deposit can still revert if tranche is impaired (totalAssets < impaired_floor with totalSupply > 0) or TranchedVault is paused.

Source (khomdev-keep/src/Tranche.vy:176-180):

@external
@view
def maxDeposit(receiver: address) -> uint256:
    return max_value(uint256)

Example:

cap = tranche.maxDeposit(user)

Cross-links: maxMint · deposit · previewDeposit

maxMint

Tranche.maxMint(receiver) -> uint256

ERC-4626 mint cap query.

Param Type Description
receiver address Share recipient (ERC-4626 signature)

Returns: uint256 — always max_uint256. Same practical limits as maxDeposit.

Access: view, any caller.

Source (khomdev-keep/src/Tranche.vy:185-189):

@external
@view
def maxMint(receiver: address) -> uint256:
    return max_value(uint256)

Example:

cap = tranche.maxMint(user)

Cross-links: maxDeposit · mint · previewMint

maxWithdraw

Tranche.maxWithdraw(owner) -> uint256

Largest gross USDC withdraw amount for owner at current NAV.

Param Type Description
owner address Share holder

Returns: uint256convertToAssets(balanceOf(owner)). Full position in underlying terms (gross; penalty applied on execution).

Access: view, any caller.

Note: Does not cap for MSV liquidity — exit may revert at TranchedVault if MSV cannot fund process_*_exit.

Source (khomdev-keep/src/Tranche.vy:194-198):

@external
@view
def maxWithdraw(owner: address) -> uint256:
    return self._convert_to_assets(erc20.balanceOf[owner], False)

Example:

gross = tranche.maxWithdraw(user)

Cross-links: maxRedeem · withdraw · convertToAssets

maxRedeem

Tranche.maxRedeem(owner) -> uint256

Largest share count redeemable for owner.

Param Type Description
owner address Share holder

Returns: uint256 — full balanceOf(owner). No liquidity haircut in this view.

Access: view, any caller.

Note: redeem returns net paid (post-penalty). MSV funding limits apply at TranchedVault exit, not reflected here.

Source (khomdev-keep/src/Tranche.vy:203-207):

@external
@view
def maxRedeem(owner: address) -> uint256:
    return erc20.balanceOf[owner]

Example:

shares = tranche.maxRedeem(user)

Cross-links: maxWithdraw · redeem · previewRedeem

previewDeposit

Tranche.previewDeposit(assets_in) -> uint256

Simulate deposit — shares minted for given USDC (floor rounding).

Param Type Description
assets_in uint256 USDC to deposit

Returns: uint256 — expected shares (= convertToShares at current rate).

Access: view, any caller. Does not check impairment or TV pause.

Source (khomdev-keep/src/Tranche.vy:212-216):

@external
@view
def previewDeposit(assets_in: uint256) -> uint256:
    return self._convert_to_shares(assets_in, False)

Example:

shares = tranche.previewDeposit(10_000 * 10**6)

Cross-links: deposit · convertToShares · maxDeposit

previewMint

Tranche.previewMint(shares) -> uint256

Simulate mint — USDC required for exact share amount (ceil rounding).

Param Type Description
shares uint256 Target share amount

Returns: uint256 — USDC cost (= _convert_to_assets(shares, roundup=True)).

Access: view, any caller. Does not check impairment or TV pause.

Source (khomdev-keep/src/Tranche.vy:221-225):

@external
@view
def previewMint(shares: uint256) -> uint256:
    return self._convert_to_assets(shares, True)

Example:

cost = tranche.previewMint(shares)

Cross-links: mint · convertToAssets · maxMint

previewWithdraw

Tranche.previewWithdraw(assets_in) -> uint256

Simulate withdraw — shares burned for exact gross USDC position (ceil rounding).

Param Type Description
assets_in uint256 Gross USDC position to exit

Returns: uint256 — shares required (= _convert_to_shares(assets_in, roundup=True)). Excludes early-exit penalty; receiver gets less pre-maturity on actual withdraw.

Access: view, any caller.

Source (khomdev-keep/src/Tranche.vy:230-234):

@external
@view
def previewWithdraw(assets_in: uint256) -> uint256:
    return self._convert_to_shares(assets_in, True)

Example:

shares = tranche.previewWithdraw(5_000 * 10**6)

Cross-links: withdraw · convertToShares · maxWithdraw

previewRedeem

Tranche.previewRedeem(shares) -> uint256

Simulate redeem — gross USDC for burning shares (floor rounding).

Param Type Description
shares uint256 Shares to redeem

Returns: uint256 — gross position value (= convertToAssets). Excludes early-exit penalty; actual redeem returns net paid.

Access: view, any caller.

Source (khomdev-keep/src/Tranche.vy:239-243):

@external
@view
def previewRedeem(shares: uint256) -> uint256:
    return self._convert_to_assets(shares, False)

Example:

gross = tranche.previewRedeem(shares)

Cross-links: redeem · convertToAssets · maxRedeem

ERC-4626 writes

deposit

Tranche.deposit(assets_in, receiver) -> uint256

Deposit USDC, mint tranche shares to receiver. Routes into TranchedVault via deposit_senior / deposit_junior.

Param Type Description
assets_in uint256 USDC amount
receiver address Share recipient

Returns: uint256 — shares minted.

Returns / state: Pulls USDC from msg.sender → approves TV → vault deposit → mints shares. Reverts if impaired or TV paused.

Access: Any caller. nonreentrant.

Events: Deposit(sender, owner, assets, shares).

Source (khomdev-keep/src/Tranche.vy:251-260):

@external
@nonreentrant
def deposit(assets_in: uint256, receiver: address) -> uint256:
    shares = self._convert_to_shares(assets_in, False)
    assert shares != 0, "tranche: zero shares"
    self._do_deposit(msg.sender, receiver, assets_in, shares)
    return shares

Example:

shares = tranche.deposit(10_000 * 10**6, user)

Cross-links: previewDeposit · TranchedVault deposit_senior · TranchedVault deposit_junior

mint

Tranche.mint(shares, receiver) -> uint256

Mint exact shares to receiver; pulls ceil-rounded USDC from msg.sender.

Param Type Description
shares uint256 Target share amount
receiver address Share recipient

Returns: uint256 — USDC pulled from msg.sender.

Returns / state: Same deposit path as deposit via _do_deposit.

Access: Any caller. nonreentrant.

Events: Deposit(sender, owner, assets, shares).

Source (khomdev-keep/src/Tranche.vy:264-268):

@external
@nonreentrant
def mint(shares: uint256, receiver: address) -> uint256:
    assets_in = self._convert_to_assets(shares, True)
    assert assets_in != 0, "tranche: zero assets"
    self._do_deposit(msg.sender, receiver, assets_in, shares)
    return assets_in

Example:

assets = tranche.mint(shares, user)

Cross-links: previewMint · deposit

withdraw

Tranche.withdraw(assets_in, receiver, owner) -> uint256

Burn shares for gross USDC position; pay net to receiver after TV early-exit penalty.

Param Type Description
assets_in uint256 Gross USDC position to exit
receiver address USDC payout recipient
owner address Share holder (caller or approved)

Returns: uint256 — shares burned (ceil vs gross assets_in).

Returns / state: Burns shares → TranchedVault process_senior_exit / process_junior_exit. receiver gets net paid; penalty stays in MSV.

Access: Any caller with share allowance on owner. Not blocked by TV pause.

Events: Withdraw(sender, receiver, owner, assets, shares)assets is net paid.

Source (khomdev-keep/src/Tranche.vy:272-280):

@external
@nonreentrant
def withdraw(assets_in: uint256, receiver: address, owner: address) -> uint256:
    shares = self._convert_to_shares(assets_in, True)
    self._do_exit(msg.sender, receiver, owner, shares)
    return shares

Example:

shares_burned = tranche.withdraw(5_000 * 10**6, user, user)

Cross-links: previewWithdraw · TranchedVault process_senior_exit · redeem

redeem

Tranche.redeem(shares, receiver, owner) -> uint256

Burn exact shares; pay net USDC to receiver (post-penalty).

Param Type Description
shares uint256 Shares to burn
receiver address USDC recipient
owner address Share holder

Returns: uint256 — net USDC paid to receiver.

Returns / state: Same exit path as withdraw via _do_exit.

Access: Any caller with share allowance. Not blocked by TV pause.

Events: Withdraw(sender, receiver, owner, assets, shares).

Source (khomdev-keep/src/Tranche.vy:284-289):

@external
@nonreentrant
def redeem(shares: uint256, receiver: address, owner: address) -> uint256:
    return self._do_exit(msg.sender, receiver, owner, shares)

Example:

paid = tranche.redeem(shares, user, user)

Cross-links: previewRedeem · withdraw · TranchedVault process_junior_exit