Modern Yield Protocols,
How They're Made: Pendle

Author(s): Sergey Boogerwooger, Pavel Morozov
Security researcher(s) at MixBytes
Intro
Pendle is a modular yield tokenization protocol that allows users to tokenize their yield and operate with it in secondary markets. The main idea behind Pendle is to split yield-bearing assets into two parts: "principal" and "yield", where the principal part is locked in the protocol until the maturity date, while the yield part is freely convertible to the underlying yield at any time.

Both parts, principal and yield, are tokenized and can be traded in AMM pools created by the protocol for each type of yield, such as staking rewards (example: stETH), supply rewards in lending protocols (example: cToken), incentivization rewards (CRV+CVX), and other forms of yield.

This approach enables a variety of interesting strategies, allowing users to trade not only yield-bearing assets but also "estimated yield", selling declining yield and buying increasing yield. An important feature of these markets is the low risk for the principal assets, where all risks are confined to the yield aspect.

Let’s explore how this works.
Higher-level overview
Each yield-bearing strategy (for example, supplying assets to a lending protocol) on Pendle has its own set of two tokens:

  • Principal Token (PT), which represents the principal amount of assets put into the strategy.
  • Yield Token (YT), which represents the yield generated by the selected strategy.

In all cases, PT_amount = YT_amount. Another significant invariant is SY_value = PT_value + YT_value, highlighting that PT and YT tokens represent complementary portions of the underlying asset. As the market approaches the maturity date (at this moment PT tokens become fully redeemable while YT tokens stop receiving yield), PT tokens increase in value, while YT tokens decrease in value. It's demonstrated in code here and here, showing that PT and YT rates depend on market.expiry - blockTime.

PT and YT tokens provide flexible ways to interact with the principal and yield portions, supporting multiple reward tokens for a single YT by leveraging tokens from nearly any modern DeFi project for yield tokenization. When users deposit yield-bearing assets (e.g., stETH, cUSDT, yvUSDC) into Pendle, they receive equal amounts of PT and YT tokens, which they can then trade independently. Since DeFi projects’ APYs are dynamic, this mechanic enables a distinct market for YT tokens that operates based on yield estimations rather than simple token prices.

Different types of yield in DeFi protocols require diverse approaches. Some protocols use rebaseable tokens like stETH (from Lido) or aTokens (from Aave), others rely on external rates like cTokens (from Compound) or wstETH (from Lido). Some have variable behavior driven by user activity, such as CRV/CVX (from Curve/Convex), while others derive tokens from external yield strategies, such as yvUSDC (from Yearn). Implementing PT and YT across these varied cases necessitates a unified standard for yield tokenization, which is outlined in Pendle's Standardized Yield whitepaper. This standard allows tokens like stETH, cDAI, and yvUSDC to be wrapped into SY-stETH, SY-cDAI, and SY-yvUSDC tokens with standardized operations in the Pendle protocol.

When a new SY (Standardized Yield) instance is created, Pendle launches an AMM where the PT and YT tokens of the respective market can be traded, enabling the yield to be traded right from the start. Let’s illustrate this with an example:

• Alice tokenizes 100 SY and receives 100 PT + 100 YT.
• Alice sells her 100 YT to Bob on the market.
• Bob now holds 100 YT, while Alice keeps 100 PT.
ᅠ◦ The system holds a total supply of 100 PT and 100 YT.
• Bob "redeems" his YT for SY via the market:
ᅠ◦ The market uses its available PT to match Bob’s YT.
ᅠ◦ The pair (PT + YT) is redeemed for SY.
ᅠ◦ Bob receives the SY (minus fees).
ᅠ◦ The total supply of PT + YT decreases by the same amount.

The AMM ensures that any YT redeemed is matched with an equivalent amount of PT from the system, maintaining the global supply invariant.
In summary, although users can trade PT and YT tokens separately, the protocol ensures that token burning always preserves the balance between the two.

Let’s examine how this is implemented.
MarketFactory
Let’s begin with the creation of a new market in PendleMarketFactoryV3.sol. The core of each market is a PT token, which is utilized as the main "identifier" of the market (PT key in a mapping).

When creating a new SY instance, one key parameter encountered is scalarRoot. This parameter plays a major role in "shaping" the market's behavior, influencing factors like price impact and arbitrage efficiency. The underlying concept is that assets promised at a future date are worth less today, creating a discount that defines the PT price (relative to the YT price). scalarRoot determines whether the price curve is steeper or flatter, depending on time before the maturity date, and is used in PT token exchange rate calculations.

_getRateScalar() increases as the maturity date approaches, meaning that during the "early" phase of the market, price impacts are smaller, allowing larger PT transactions. As the maturity date draws nearer, price sensitivity increases, encouraging market makers to rebalance the market closer to fair value. This mechanism ensures that PT prices progressively align with their redemption value at expiry, all without relying on external oracles or manual adjustments.

Another important parameter, initialAnchor, represents the initial exchange rate. It determines the initial distribution of PT and underlying tokens in the market pool. Both initialAnchor and scalarRoot shape the price curve, which converges toward the underlying token’s actual value at maturity.
This curve underpins Pendle’s main use case: trading future yield today at market-determined rates.

Additionally, PendleMarketFactoryV3 includes two governance-related functions for setting the treasury fee and address, and defining a special overriddenFee that supersedes the standard market fee when required.
Pendle tokens
As previously mentioned, all Pendle logic revolves around SY tokens and their derivatives: PT and YT tokens. A useful information about wrapped yield-bearing can be found in EIP-5115, proposed by Pendle.

Let's start with PT and the PendlePrincipalToken contract, which is relatively simple. It is created by the token factory, includes an isExpired check (for the maturity date), and can be burned or minted only in sync with the corresponding YT token.

The PendleYieldToken (YT), on the other hand, is much more complex. It contains functions that mint and burn synced pairs(PY) of PT+YT tokens using SY tokens. These functions are integral to many Pendle operations, as any action where a user requires only YT or PT tokens involves minting or burning SY tokens, receiving "synced" amounts of PT and YT tokens, and then utilizing the required part of this pair.

Additionally, the YT token, which represents yield, contains functions for interest accrual. It features the _pyIndexCurrent() function, which returns the current exchange rate. This function is widely used in calculations involving the amount of PT and YT tokens based on the currently accrued yield. One key usage of this index is in determining the target SY amount from given PT and YT tokens(combined as "PY" in the code) and vice versa (examples can be found here or here). In these functions, the index acts as an exchange rate between SY and PT (or YT) tokens.

Let's consider an example scenario involving yield accrual in Pendle:

  • Alice deposits 1000 USDC into Aave, receiving 1000 aUSDC, and wraps them into 1000 SY-aUSDC in Pendle.
  • Alice mints PT+YT tokens from her SY-aUSDC using mintPY() and receives equal amounts of PT and YT tokens corresponding to the current index (for example, 1000 PT and 1000 YT).
  • Alice waits as her aUSDC generates yield; for instance, 1000 aUSDC becomes 1020 aUSDC.
  • The pyIndex increases, so redeeming PT and YT tokens now results in more SY-aUSDC tokens.
  • Alice redeems her 1000 PT + 1000 YT tokens for 1020 SY-aUSDC.

This example describes the simplest case. At any intermediate moment, Alice can trade PT and YT tokens independently in Pendle's AMM and later "combine" PT and YT tokens to redeem her aUSDC for SY tokens. She could focus on YT tokens, assuming yields will increase, or on PT tokens, assuming the yield rate may decline. Alternatively, she could simply speculate with PT and YT tokens.

Now, let's move on to the Pendle Market.
Market
The Pendle market resides in the PendleMarketV3.sol contract. This contract functions as an ERC20 PENDLE-LPT token, representing user shares in the market. The market operates using three tokens, configured as immutables during deployment: SY, PT, and YT tokens. The total supply of tokens is tracked in the MarketStorage struct. As mentioned earlier, the yield curve parameters and the PT token maturity date are initially set.

First, we need to highlight the mint() and burn() functions, which act as liquidity-providing mechanisms for swaps between market tokens. Liquidity providers in this case earn fees from these swaps.

Now, let's look at the most important functions: swapExactPtForSy and swapSyForExactPt. These functions allow users to swap SY tokens for PT tokens and vice versa. Recall that SY tokens consist of both PT and YT tokens, meaning that holders of SY tokens own both the "fixed-rate" part (PT), with a guaranteed amount of underlying tokens at maturity, and the "yield" part (YT). These SY↔PT conversions let users shift from "fixed" guaranteed positions to more profitable but riskier "yield" positions.

For example: Alice has 1,000 USDC Principal Tokens (PT) from a 6-month market that expires in 3 months. She believes interest rates will rise significantly, making her fixed-rate position less attractive compared to variable yields available in the market. To adapt, she swaps her PT tokens for SY tokens (owning both PT and YT) and begins accruing yield at the prevailing market rate.

Another example: If Alice is concerned about a potential rate decrease with her stETH holdings and holds SY-stETH tokens, she can sell her YT tokens and buy PT tokens. This action gives her a more predictable outcome at expiry, locking in a fixed rate of return instead of being exposed to variable yields.

There are also interesting cases involving multiple markets with the same underlying yield-bearing assets. Refer to this screenshot:

In the image above, three markets with different maturity dates have their own YT tokens, which receive the same underlying yield. Users concerned about yield rates can exploit this by buying YT tokens in one market and selling YT tokens in another. For example, they might purchase YT tokens in a "short-term" market and sell YT tokens in a "long-term" market (using conversion to underlying tokens) if they believe short-term interest rates will fall faster than long-term rates. YT tokens in the "long-term" market will be less influenced by these rate changes. This functionality offers sophisticated fixed-income trading strategies previously only available in traditional finance.

We will explore the swapExactPtForSy() and swapSyForExactPt() functions further in later sections when discussing user scenarios. This combination of four tokens (underlying, SY, PT, YT) enables a wide variety of potential applications, which we cannot cover fully in this article.
Now, let's move on to the router, which combines markets and all participating tokens, and describe some example scenarios.
Router
The Router serves as the entry point for users interacting with the protocol. It is organized as multiple Action[...] contracts that contain various types of logic. While reviewing all the functions in these contracts would be excessive for this article, it’s useful to examine a specific user scenario and trace the involved functions. So…

Imagine a scenario where Alice expects a significant increase in stETH yield due to upcoming protocol changes. She decides to acquire YT tokens to capitalize on this potential upside.

She starts by using the swapExactTokenForYt() function, approving a certain amount of WETH and providing the market address, token input details (WETH, amount, etc.), the minimum YT output (to account for slippage), and other parameters (some of which will be described later). First, the function checks if an approximation can be used — a kind of optimization which we will discuss later. Alice mints SY tokens "wrapping" the underlying tokens into their "SY" version.

The next step involves swapping these SY tokens for YT tokens using the _swapExactSyForYt() function, which is complex internally. During this swap (SY to YT tokens), Pendle first attempts to fill limit order, though this is not relevant in Alice’s case (we don't plan to discuss limit orders, as it will distract us from the main mechanics).

Alice then calculates the amount of YT tokens she will receive via the approxSwapExactSyForYtV2() function, which is particularly noteworthy. This function uses a numerical approximation to compute the optimal amount of YT tokens that should result from swapping a specified amount of SY tokens.

An optimization comes into play here. If a smart contract uses an iterative algorithm for approximation, optimization, or searching, the user can assist it externally (e.g., from an off-chain dApp) by providing suggested starting values for the algorithm. These hints can accelerate the calculation process. This technique is not new, as seen in Liquity V2 (article) in the getAppoxHint() function for a linked list. In this case, the ApproxParams provided by the user help to calculate the optimal YT token amount.

[NOTE] This "user-provided hints" approach is not fully foolproof and may become ineffective if other transactions change the contract state during calculations, leading to gas inefficiencies or even resulting in griefing attacks. In Pendle, this is mitigated by the imbalance protection in the pool, which prevents abrupt changes in token exchange rates.

If no external approx parameters are provided, the function uses min/max values from the current market.

Next, the function begins a loop, estimating the amount of PT that needs to be involved to efficiently convert the exact SY input into YT tokens. Because PT and YT tokens must be minted in equal amounts but have different prices, an approximation algorithm determines the optimal PT amount required to maximize the YT output for the given SY input, within an acceptable error range. This establishes how much SY tokens are necessary to achieve the target YT amount.

Finally, it’s time to redeem the SY tokens, extracting only the YT tokens since Alice only needs the YT tokens she has paid for. An interesting aspect arises here: the receiver is the YT token contract (not receiver). This is explained by the following sequence of actions during the swap:

  • Start with some initial SY (PT + YT).
  • "Flash-borrow" PT tokens needed to reach the target YT amount.
  • Buy additional SY tokens using the borrowed PT tokens from the market.
  • Combine the initial and "partially borrowed" SY tokens.
  • Mint PT + YT tokens from this combined SY amount.
  • Use the PT tokens to repay the flash loan.
  • Retain the YT tokens as the final output.

Although the process appears complex, the result is simple: Alice ends up with YT tokens (tradeable in the AMM) acquired using the underlying asset. For reverse swaps, YT tokens can be exchanged for other assets using the swapExactYtForToken() function.

Pendle routers also include similar functions for swaps involving PT tokens: swapExactTokenForPt(), swapExactSyForPt(), swapExactPtForToken(), and swapExactPtForSy(). All of these functions rely on SY tokens as intermediate assets.
AMM
Next, let’s explore Pendle's AMM, which facilitates trading with SY, PT and YT tokens. The AMM operates within a specific Market, while the mathematical logic defining the source and target states of the AMM after various operations resides in the MarketMathCore library contract.

As mentioned earlier, Pendle’s AMM supports liquidity provision. Let’s take a brief look at the addLiquidityCore() and removeLiquidityCore() functions, which manage the total SY and PT reserves and calculate the amounts of LP shares to mint or burn. These functions also add a MINIMUM_LIQUIDITY amount to the protocol reserves to protect against front-running the initial deposit. The initial price is set as the geometric mean of the initial SY and PT deposits. Both functions utilize the smaller of the SY and PT amounts for issued/burned shares calculations.

One of the most interesting components is the calcTrade() function, responsible for mathematical calculations related to swaps between PT, YT, and SY tokens. It factors in the exchange rate, which is determined by the market’s price curve. Examining the _getExchangeRate() function further:

  • we calculate the proportion, representing the ratio of PT tokens to the total pool value.
  • if the swap causes the proportion to exceed MAX_MARKET_PROPORTION (96%), we block the swap, preventing large imbalances in the pool.
  • we apply ln(proportion/1−proportion) to create more extreme exchange rates for greater pool disproportion. Essentially, the further the pool moves from equilibrium, the more expensive it becomes (in terms of price impact) to push it further in that direction.
  • we adjust the rate using the market-specific rateScalar and rateAnchor parameters, making the AMM increasingly sensitive to pool imbalances as expiry approaches (rateScalar increases).
  • if the final exchange rate < 1, block the swap because PT tokens cannot be valued less than SY tokens while maintaining their economic properties as principal tokens representing at least the base asset value.

This design uniquely combines time sensitivity and sensitivity to pool token balances, making Pendle’s AMM particularly effective for yield trading markets.

Swap fees are collected in SY tokens and distributed between the protocol reserves and the market. Fees for the reserves go to the market.treasury (as shown here and here), while the remaining fees stay in the market, creating yield for LPs.
Price oracles
The PendlePYOracleLib library provides price oracles for PT and YT tokens. These oracles utilize both the SY and PT indexes.

Interestingly, there is a "ratchet"-like mechanism in place. If the underlying asset experiences losses (caused by hacks, slashings, etc.), the SY index decreases. However, PT holders must be protected from these losses by receiving a fair amount of SY tokens for their PT tokens. To achieve this, the PY index is designed to never decrease, acting as a "ratchet" mechanism. When the SY index decreases, the PT index "freezes" and stays constant until the SY index recovers and "catches up." Once this happens, both indexes continue to increase together:

Because of this, the returned PY (PT and YT) index is selected as the maximum value of the SY and PY indexes. In all functions that return exchange rates for PT and YT tokens, there are conditions, like this, which adjust the rate calculation to account for differences between the SY index and PY index. Moreover, according to the Pendle invariant SY_value = PT_value + YT_value, any loss is "absorbed" by YT holders. During this time, YT holders do not receive any yield, and the YT token price may even become negative.

Pendle also has oracles for its LP tokens (PendleLpOracleLib), which also depend on the SY and PY indexes. These oracles use the same rate adjustments to determine the value of LP tokens.

This mechanism establishes a "floor" for PT redemption value in terms of the underlying token. PT tokens will always be redeemable for a predictable amount of the underlying SY token. However, it's important to note that the value of the SY token in the base asset may fluctuate if issues arise.
Implementation details
Pendle employs many interesting mechanics worth mentioning. One of them is "caching" essential market information in the MarketPreCompute struct. In the function, current market values required for calculations (such as rateScalar or rateAnchor) are computed once and then reused. This is crucial for functions performing iterations, like approxSwapExactSyForYtV2(), which require these values in every iteration.

Another notable feature is the skim() function in PendleMarketV3, which transfers any excess PT and SY tokens to the treasury. This allows the protocol to handle situations where "unaccounted" tokens appear due to rounding errors, incorrect direct transfers to the pool, hacks, etc.

Additionally, there is the overriddenFee, which can be set by a specific router in order to override the market fee. This functionality can encourage integration by partner protocols or be used by high-volume routers, which may qualify for reduced fees.

Finally, Pendle includes a significant component related to governance and incentivization gauges. This involves the vePENDLE token, which is outside the scope of this protocol review.
Conclusion
The Pendle protocol is an excellent example of a modern DeFi solution. Its ability to tokenize the principal and yield portions of multiple DeFi protocols into separate assets allows users to trade their expectations and concerns about protocol performance over different time periods. This opens up a wide variety of use cases for yield tokenization, including entirely new strategies.

Pendle incorporates several impressive features worth studying:

  • A unified standard (SY) that enables compatibility with various yield-bearing assets.
  • The ability to trade long- and short-term yields across multiple markets for the same underlying asset.
  • A sophisticated AMM that combines time-sensitivity and imbalance-sensitivity, tailored for yield trading.
  • Oracles and exchange rates that protect the protocol from manipulations, imbalances, and volatility in underlying assets.
  • A flexible fee system with overridden fees for specific routers.
  • A comprehensive gauge system for liquidity incentives.

All these innovations position Pendle as a "new wave" project, where on-chain assets rival, or even surpass, the complexity of traditional financial instruments.

Thank you for reading, see you in our next articles!
  • Who is MixBytes?
    MixBytes is a team of expert blockchain auditors and security researchers specializing in providing comprehensive smart contract audits and technical advisory services for EVM-compatible and Substrate-based projects. Join us on X to stay up-to-date with the latest industry trends and insights.
  • Disclaimer
    The information contained in this Website is for educational and informational purposes only and shall not be understood or construed as financial or investment advice.
Other posts