PT20 — Principal Token (monthly cohort)
PT20 is the fungible, ERC-5095-compatible principal claim for monthly maturities. One PT20 contract per maturity, deployed deterministically by PT20Factory.
- Standard: ERC-20 + ERC-5095, 18 decimals
- Naming:
kDIEM-JUN2026/kDIEM-JUN2026 - Exchange rate: Fixed 1:1 — 1 PT20 always redeems for 1 DIEM at maturity.
- Maturity: Always 1st of a month at 00:00 UTC.
How you get PT20
By calling Locker.lockPT20(amount, maturity, recipient). The Locker:
- Stakes your DIEM into the vault.
- Gets or deploys the PT20 contract for that maturity.
- Mints
amountPT20 torecipient.
Result: you hold amount PT20, fungible with everyone else who locked into the same maturity.
How you redeem
After maturity, call Redeemer.redeemAndRequestUnstake(pt, ptShares, beneficiary).
This burns your PT20 and queues your DIEM unstake. After Venice's 1-day cooldown plus one Queue.sync() call, you can Queue.claim() the DIEM.
You do not have to be the original locker to redeem — any holder of PT20 can. This is the whole point of PT20 being fungible and tradeable.
Why monthly only
Earlier designs considered per-day PT20 contracts. Per-day means 365 PT20 contracts per year, each with too little TVL to support meaningful DEX liquidity. Monthly means at most 13 active PT20 contracts at any time (one per month over the 400-day max horizon), each accumulating enough TVL to sustain a real Uniswap-style pool.
If you need a non-monthly maturity (e.g. 45 days from now), use PT721 instead. See PT20 vs PT721 for guidance.
Tradeable on DEXes
kDIEM-JUN2026 is a standard ERC-20. Liquidity is expected to concentrate on:
- Pendle-style pools if Pendle integrates (PT20 is structurally compatible).
- Uniswap V3 pools paired against DIEM or USDC.
- Lending markets (Aave, Morpho) once integrations land — PT20 is good collateral because its maturity behaviour is fully on-chain and redemption is permissionless.
ERC-5095 surface
The standard view functions are implemented at 1:1:
maturity() → uint256 (timestamp)
convertToUnderlying(amount) → amount (1:1)
convertToPrincipal(amount) → amount (1:1)
maxRedeem(holder) → balanceOf(holder) post-maturity, else 0
maxWithdraw(holder) → balanceOf(holder) post-maturity, else 0
previewRedeem(amount) → amount
previewWithdraw(amount) → amount
redeem(amount, to, from) → underlying out
withdraw(amount, to, from) → underlying out (alias)redeem() and withdraw() are open to anyone (or any approved spender) post-maturity. They unwrap to the underlying claim at 1:1; the typical caller is the Redeemer contract which atomically chains redemption into an unstake request, so users don't need to know about the internal claim representation.
Locker binding
Each PT20 records its Locker as an immutable address at deploy time (supplied by PT20Factory.locker at the moment getOrCreate was first called). PT20.deposit is onlyLocker — only the recorded Locker can create new PT20 shares. This means a PT20 deployed under Locker v1 will continue to be mintable only by Locker v1, even if PT20Factory is later rotated to a v2 Locker.
Post-maturity redemption is open and does not care about the Locker binding — anyone can unwrap.
Pre-maturity vs post-maturity
| Phase | deposit() | redeem() / withdraw() |
|---|---|---|
| Pre-maturity | Allowed (onlyLocker) | Reverts (NotMatured) |
| At maturity (exact second) | Reverts (PreMaturityOnly) | Allowed |
| Post-maturity | Reverts | Allowed |
The boundary is strict at the maturity second — no overlap.