Smart contract
Credit Vaults Integrator Guide
Overview
Credit Vaults are epoch-based lending vaults built on top of IdleCDOEpochVariant (the vault) and IdleCreditVault (the strategy). Lenders deposit into AA or BB tranches and receive tranche ERC20 tokens. At the start of each epoch, funds are lent directly to a single borrower. At the end of the epoch, the borrower repays interest and funds needed to satisfy pending withdrawals. Tranche prices are updated at epoch stop, and lenders can request withdrawals only between epochs (except for the optional mid-epoch deposit flow).
Key properties:
Epochs gate deposits and withdrawals. During an epoch, the vault is paused and normal deposits/withdraw requests are blocked.
Withdrawals are two-step: request between epochs, claim after at least one full epoch has passed.
If the APR drops enough between epochs and instant withdrawals are enabled, a request becomes an instant withdraw.
The strategy token (
IdleCreditVault) has a fixed price of 1 underlying unit and is used as a receipt token for withdrawal requests (because tranche tokens are burned on request).KYC gating is enforced via Keyring (
isWalletAllowed).
Roles (for context only):
Lenders/integrators: deposit into tranches and manage withdrawals.
Borrower: receives funds at epoch start, repays at epoch end, may fulfill write-off escrow requests.
Manager/owner: starts/stops epochs and configures parameters (not covered here).
Epoch lifecycle (high level)
Buffer period (between epochs)
isEpochRunning == falseDeposits and withdrawal requests are allowed.
Epoch start
Manager calls
startEpoch()(not covered here).isEpochRunning == true, deposits/withdraw requests paused.Underlyings are sent to the borrower; instant-withdraw funding is handled.
Epoch running
Optional
depositDuringEpoch()may be enabled.Optional Write-off escrow is available for lenders and borrower to request a withdraw (borrower needs to accept the request).
Epoch stop
Manager calls
stopEpoch()(not covered here).Interest and pending withdrawal funds are collected.
Tranche prices update; new APR set.
isEpochRunning == falseand buffer period starts again.
Contracts and data model
IdleCDOEpochVariant: main vault contract. Inherits
IdleCDOand adds epoch logic plus request/claim flows.IdleCreditVault: strategy/receipt-token contract. Holds pending withdrawal funds and mints receipt tokens for requests.
IdleCDOTranche (AA/BB tokens): ERC20 tranche tokens with 18 decimals (
ONE_TRANCHE_TOKEN = 1e18) used as receipt for deposits.IdleCreditVaultWriteOffEscrow: escrow for in-epoch write-off exits; borrower can buy out lender positions and optionally write off debt.
Integrator flows
1) Deposit between epochs (standard)
Use this flow when isEpochRunning == false (vault not paused).
Confirm
IdleCDOEpochVariant.isWalletAllowed(user)istrue.approvethe vault (IdleCDOEpochVariant) to spend the underlying token.Call
depositAA(amount)ordepositBB(amount).Receive AA or BB tranche tokens (ERC20, 18 decimals).
Notes:
Deposits are blocked while the epoch is running because the vault is paused.
Tranche token price for valuation should be read via
virtualPrice(tranche).Deposits always require
isWalletAllowed(user) == true(no bypass).
2) Deposit during an epoch (optional)
Use only if mid-epoch deposits are enabled.
Ensure
isEpochRunning == true,block.timestamp < epochEndDate, andisDepositDuringEpochDisabled == false.Ensure AYS is not active (
isAYSActive == false).Approve underlying to the vault.
Call
depositDuringEpoch(amount, tranche).
Notes:
The first deposit into a tranche cannot be mid-epoch (tranche supply must be > 0).
The minted amount is prorated based on remaining epoch time plus the buffer period.
Funds are immediately transferred to the borrower; strategy tokens are minted to the vault.
3) Normal withdraw (request + claim)
Use this flow between epochs (isEpochRunning == false).
Call
requestWithdraw(trancheAmount, tranche).If
trancheAmount == 0, the vault treats it as "withdraw all."The function burns tranche tokens and mints receipt tokens in
IdleCreditVaultfor the user.The return value is the underlyings requested, including next-epoch net interest (no buffer-period interest).
Wait at least one epoch. Claim is allowed when:
IdleCreditVault.epochNumber > IdleCreditVault.lastWithdrawRequest(user)or the vault is closed (
epochEndDate == 0).
Call
claimWithdrawRequest().This burns receipt tokens and transfers underlyings from
IdleCreditVault.
Notes:
Requests require
isWalletAllowed(user) == trueunlesskeyringAllowWithdraw == true.
4) Instant withdraw (APR drop)
Instant withdrawals are triggered automatically by requestWithdraw when:
disableInstantWithdraw == false, andlastEpochApr > IdleCreditVault.unscaledApr() + instantWithdrawAprDelta.
Flow:
Call
requestWithdraw(trancheAmount, tranche)between epochs.If conditions are met, the request becomes instant:
Tranche tokens are burned.
Receipt tokens are minted in
IdleCreditVault.The requested amount equals current underlyings (no next-epoch interest).
Claim when
allowInstantWithdraw == true:This may be true immediately after epoch start if new deposits cover the requests.
Otherwise it becomes true after
instantWithdrawDeadlineand manager callsgetInstantWithdrawFunds().
Call
claimInstantWithdrawRequest().
5) Write-off escrow flow (during epoch)
Used to let lenders exit during an epoch by selling tranche tokens to the borrower. This flow is only available if a IdleCreditVaultWriteOffEscrow is deployed for the specific credit vault, and the exit is only completed once the borrower accepts it by fulfilling the request.
Lender flow:
Ensure
IdleCDOEpochVariant.isEpochRunning() == trueand wallet is allowed.approvethe escrow to transfer tranche tokens.Call
createWriteOffRequest(trancheAmount, underlyingsRequested).Requests accumulate per user; amounts are not auto-priced.
Optional: call
deleteWriteOffRequest()to cancel and reclaim tranche tokens.
Borrower flow:
Call
fullfillWriteOffRequest(user, trancheAmount, underlyingsRequested).Borrower transfers underlyings to escrow.
Exit fee (if set) is taken, remainder is sent to the lender.
Tranche tokens are transferred to the borrower.
Borrower may then call
writeOffDeposit()onIdleCDOEpochVariantto burn tranche + strategy tokens and reduce expected interest (borrower-only).
6) Default considerations
If the borrower defaults, the vault sets defaulted == true, pauses deposits/withdraw requests, and enables transfers of receipt tokens via IdleCreditVault.allowTransfers(). Integrators should monitor the BorrowerDefault event and treat receipt tokens as claims on whatever recovery process is used.
Integrator-facing methods (by contract)
IdleCDOEpochVariant (vault)
User/integrator calls:
depositAA(uint256 amount) returns (uint256 minted)- Deposit underlying into AA; mints AA tranche tokens (not strategy tokens).amountis underlying.depositBB(uint256 amount) returns (uint256 minted)- Deposit underlying into BB; mints BB tranche tokens (not strategy tokens).amountis underlying.depositDuringEpoch(uint256 amount, address tranche) returns (uint256 minted)- Mid-epoch deposit;trancheisAATrancheorBBTranche.requestWithdraw(uint256 trancheAmount, address tranche) returns (uint256 underlyingsRequested)- Create a withdrawal request;trancheAmountis tranche tokens,trancheis AA/BB.claimWithdrawRequest()- Claim a normal withdraw request once eligible (>= 1 epoch after last request).claimInstantWithdrawRequest()- Claim an instant withdraw request whenallowInstantWithdrawis true.
Views (from IdleCDO / IdleCDOEpochVariant):
virtualPrice(address tranche) view returns (uint256)- Tranche price, in underlyings, including accrued interest.getApr(address tranche) view returns (uint256)- Current APR for the tranche.getContractValue() view returns (uint256)- Vault NAV in underlying terms.maxWithdrawable(address user, address tranche) view returns (uint256)- Max underlyings for a normal request.isWalletAllowed(address user) view returns (bool)- Keyring/KYC allowlist check.
Key public state to read:
AATranche,BBTranche,token,strategyisEpochRunning,epochEndDate,epochDuration,bufferPeriodinstantWithdrawDelay,instantWithdrawDeadline,instantWithdrawAprDeltaallowInstantWithdraw,disableInstantWithdrawallowAAWithdrawRequest,allowBBWithdrawRequestkeyringAllowWithdraw,defaulted,lastEpochApr,lastEpochInterest,expectedEpochInterestfee,feeReceiver,trancheAPRSplitRatio,isAYSActive,isDepositDuringEpochDisabled
Not supported (no-op in Credit Vaults):
withdrawAA,withdrawBB,depositAARef,depositBBRef
IdleCreditVault (strategy / receipt token)
Read-only for integrators (calls are restricted to IdleCDOEpochVariant for state-changing methods).
User-visible reads:
Receipt token ERC20:
balanceOf(user),totalSupply(),decimals()- Receipt tokens for pending requests.Request tracking:
withdrawsRequests(user),instantWithdrawsRequests(user),lastWithdrawRequest(user)- Per-user request state.Totals:
pendingWithdraws,pendingInstantWithdraws,totEpochDeposits- Aggregate counters.Epoch tracking:
epochNumber- Strategy epoch counter for claim eligibility.APR data:
getApr()(scaled),unscaledApr()- APR used for epoch interest math.Metadata:
token,tokenDecimals,borrower,manager,price()(alwaysoneToken) - Vault metadata.Transferability:
canTransfer(false unless defaulted) - Receipt token transfer flag.
IdleCreditVaultWriteOffEscrow
User/borrower calls:
createWriteOffRequest(uint256 trancheAmount, uint256 underlyingsRequested)- Lender proposes exit terms (requires escrow deployed).deleteWriteOffRequest()- Lender cancels and retrieves tranche tokens.fullfillWriteOffRequest(address user, uint256 trancheAmount, uint256 underlyingsRequested)- Borrower accepts and settles the request (borrower-only).
Views:
userRequests(user) -> (tranches, underlyings)idleCDOEpoch,strategy,underlying,tranche,borrowerexitFee,feeReceiver
Monitoring positions and claimability
Active tranche position
Tranche balance:
IERC20(AATranche|BBTranche).balanceOf(user)Valuation:
balance * virtualPrice(tranche) / 1e18virtualPriceincludes accrued interest since last accounting update.
Pending withdrawal position
Normal:
IdleCreditVault.withdrawsRequests(user)Instant:
IdleCreditVault.instantWithdrawsRequests(user)Receipt balance:
IdleCreditVault.balanceOf(user)In steady state, this equals normal + instant requests.
Escrowed position
IdleCreditVaultWriteOffEscrow.userRequests(user)tranchesandunderlyingsrequested are both tracked.
Claim eligibility
Normal withdraw claim:
IdleCreditVault.epochNumber > IdleCreditVault.lastWithdrawRequest(user)or
IdleCDOEpochVariant.epochEndDate == 0(vault closed)
Instant withdraw claim:
IdleCDOEpochVariant.allowInstantWithdraw == true
Timing checks
Epoch status:
isEpochRunning,epochEndDateInstant deadline:
instantWithdrawDeadline
Practical integration tips
Tranche tokens are 18 decimals; underlying decimals vary. Convert carefully.
requestWithdraw(0, tranche)withdraws the full tranche balance.maxWithdrawable(user, tranche)is useful for estimating a normal request amount (net of fees).Mid-epoch deposits are disallowed when
isDepositDuringEpochDisabled == trueorisAYSActive == true.Receipt tokens are non-transferable until
IdleCreditVault.canTransfer == true(typically on default).
Last updated