Common SY Contracts
Overview
Pendle provides a set of pre-built, audited Standardized Yield (SY) implementations that cover the most common yield-bearing asset patterns. Partners doing a Community Listing should use one of these contracts when their asset fits — this avoids the overhead of writing and auditing a custom SY from scratch.
These contracts can be deployed through PendleCommonSYFactory, described at the bottom of this page.
When in doubt about which common SY to use, reach out to the Pendle team. Choosing the wrong type can result in incorrect exchangeRate() behavior or broken deposit/redeem paths.
Choosing the Right Common SY
| Type | Yield token type | exchangeRate() | Deposit tokens | Redeem tokens |
|---|---|---|---|---|
| ERC-20 with Adapter | Plain ERC-20 + optional adapter tokens | 1e18 (constant) | yieldToken + adapter tokens | yieldToken + adapter tokens |
| ERC-20 with Oracle | Plain ERC-20, rate from oracle | From oracle | yieldToken | yieldToken |
| ERC-4626 with Adapter | ERC-4626 vault, full deposit/redeem + optional adapter | vault.convertToAssets(1e18) | asset, yieldToken + adapter tokens | asset, yieldToken + adapter tokens |
| ERC-4626 — No Redeem with Adapter | ERC-4626 vault, restricted withdraw + optional adapter | vault.convertToAssets(1e18) | asset, yieldToken + adapter tokens | yieldToken only |
| ERC-4626 — No Redeem, No Deposit | ERC-4626, both deposit and withdraw restricted | vault.convertToAssets(1e18) | yieldToken only | yieldToken only |
All WithAdapter variants accept adapter = address(0) if no adapter is needed at deploy time. An adapter can be set later via SY.setAdapter(adapterAddress).
Common SY Types
ERC-20 with Adapter — PendleERC20WithAdapterSY
A 1:1 wrap of a plain ERC-20 token, with optional adapter support for depositing and redeeming via additional tokens.
When to use: The yield token is a standard non-rebasing ERC-20 whose share price against itself is always 1 (1 SY = 1 yieldToken). Yield in Pendle comes from external incentives (points, Merkle distributions) rather than on-chain token appreciation. If additional deposit/redeem tokens are needed (e.g., deposit DAI to receive USDS), attach an adapter. Common examples: USDe, USDS, reward tokens with fixed exchange rate.
Key behaviors:
exchangeRate()=1e18(constant — no on-chain appreciation)PIVOT_TOKEN(adapter's pivot) =yieldTokenassetInfo()returnsyieldTokenitself as the asset- Without adapter:
getTokensIn()/getTokensOut()=[yieldToken] - With adapter: adds
adapter.getAdapterTokensDeposit()/getAdapterTokensRedeem()to the lists
Constructor params (for deployUpgradableSY):
bytes memory constructorParams = abi.encode(
address _erc20,
address _offchainRewardManager
);
bytes memory initData = abi.encodeWithSelector(
PendleERC20WithAdapterSY.initialize.selector,
string _name, // e.g. "SY USDS"
string _symbol, // e.g. "SY-USDS"
address _adapter // address(0) if no adapter needed at deploy time
);
ERC-20 with Oracle — PendleERC20WithOracleSY
A plain ERC-20 wrap where exchangeRate() is sourced from an external IPExchangeRateOracle contract and assetInfo() returns a separately specified underlying asset.
When to use: The exchange rate of the yield token against its underlying asset cannot be computed on-chain from the token itself — for example, a token bridged from another chain where the vault state lives. The rate is instead published on-chain via a dedicated oracle contract.
Example: sUSDai is an Arbitrum-native savings token. It is bridged to Plasma as a LayerZero OFT. On Plasma the vault's convertToAssets logic is unavailable, so a Chainlink feed is deployed to publish the sUSDai / USDai exchange rate. The SY on Plasma points exchangeRateOracle at that Chainlink adapter.
Key behaviors:
exchangeRate()=IPExchangeRateOracle(exchangeRateOracle).getExchangeRate()assetInfo()returnsunderlyingAsset(separate fromyieldToken)getTokensIn()/getTokensOut()=[yieldToken]
Constructor params (for deployUpgradableSY):
bytes memory constructorParams = abi.encode(
address _yieldToken, // the ERC-20 yield token (e.g. sUSDai OFT on Plasma)
address _underlyingAsset, // asset it appreciates against (e.g. USDai)
address _exchangeRateOracle, // IPExchangeRateOracle (e.g. Chainlink feed adapter)
address _offchainRewardManager
);
bytes memory initData = abi.encodeWithSelector(
PendleERC20WithOracleSY.initialize.selector,
string _name,
string _symbol,
address _owner
);
IPExchangeRateOracle implementations
Pendle ships two ready-to-use oracle adapters that implement IPExchangeRateOracle. Deploy one of these and pass its address as _exchangeRateOracle.
PendleChainlinkExchangeRateWrapper
Wraps any Chainlink-compatible aggregator (AggregatorV2V3Interface). Reads latestRoundData() and normalises the answer to the 1e18 scale expected by the SY.
constructor(
address _chainlinkFeed, // Chainlink aggregator address
int8 _tokenDecimalsOffset // decimal adjustment: see note below
)
tokenDecimalsOffset bridges the gap between the feed's native decimals and the SY's required scale:
offset < 0→ result is multiplied by10^|offset|(feed has fewer decimals than needed)offset > 0→ result is divided by10^offset(feed has more decimals than needed)offset = 0→ feed decimals already match; onlyoracleDecimalsnormalisation is applied
PendleRedStoneRateOracleAdapter
Wraps a RedStone push-model price feed (IRedstonePriceFeed). Reads latestAnswer() and rescales from the feed's rawDecimals to the target decimals.
constructor(
address _redStoneOracle, // RedStone push-feed address
uint8 _decimals // desired output decimals (typically 18)
)
// getExchangeRate() output:
// (latestAnswer * 10^decimals) / 10^rawDecimals
ERC-4626 with Adapter — PendleERC4626WithAdapterSY
A full ERC-4626 vault wrap with bidirectional deposit and redemption, plus optional adapter support for additional deposit/redeem tokens.
When to use: The yield token is a fully ERC-4626 compliant vault with unrestricted deposit() and redeem(). Users can enter and exit via the vault's underlying asset. If users should also be able to enter/exit with other tokens natively (e.g., deposit USDC → adapter converts USDC → vault asset → vault shares), attach an adapter. Common examples: sUSDe, sDAI, Morpho vaults.
Key behaviors:
exchangeRate()=IERC4626(yieldToken).convertToAssets(1e18)PIVOT_TOKEN(adapter's pivot) = vault'sassetassetInfo()returns the vault'sasset()- Without adapter:
getTokensIn()/getTokensOut()=[asset, yieldToken] - With adapter: adds
adapter.getAdapterTokens*()to both lists
Deposit flow with adapter token:
- Non-asset token
Tis transferred to the adapter adapter.convertToDeposit(T, amount)convertsT → assetIERC4626(yieldToken).deposit(amtAsset, address(this))mints vault shares
Constructor params (for deployUpgradableSY):
bytes memory constructorParams = abi.encode(
address _erc4626,
address _offchainRewardManager
);
bytes memory initData = abi.encodeWithSelector(
PendleERC4626WithAdapterSY.initialize.selector,
string _name,
string _symbol,
address _adapter // address(0) if no adapter needed at deploy time
);
ERC-4626 — No Redeem with Adapter — PendleERC4626NoRedeemWithAdapterSY
An ERC-4626 wrap where depositing via the underlying asset is supported, but redeeming back to the underlying asset is not. Optional adapter support for extended deposit tokens.
When to use: The vault supports deposit() but has restricted or locked redeem() (e.g., cooldown periods, liquidity limits, or protocol-level withdrawal queues). Users who want to exit to the underlying asset must do so through secondary markets or the vault's own withdrawal mechanism. Add an adapter if users should be able to deposit using tokens other than asset.
Key behaviors:
exchangeRate()=IERC4626(yieldToken).convertToAssets(1e18)PIVOT_TOKEN(adapter's pivot) = vault'sassetgetTokensIn()=[asset, yieldToken] + adapter.getAdapterTokensDeposit()(with adapter)getTokensOut()=[yieldToken]only — adapter is not used on the redeem side
Constructor params (for deployUpgradableSY): same as PendleERC4626WithAdapterSY above.
ERC-4626 — No Redeem, No Deposit — PendleERC4626NoRedeemNoDepositUpgSY
The most restrictive ERC-4626 variant. Neither deposit() nor redeem() is called on the vault — the SY only wraps existing vault shares.
When to use: The vault does not support standard ERC-4626 deposit(asset) or redeem(shares) flows at all. Users must already hold vault shares; the SY is purely a wrapper that reads the vault's exchange rate. Common for locked positions, receipt tokens from bespoke protocols, or vaults with fully non-standard deposit mechanisms.
Key behaviors:
exchangeRate()=IERC4626(yieldToken).convertToAssets(1e18)getTokensIn()=[yieldToken]onlygetTokensOut()=[yieldToken]only
Constructor params (for deployUpgradableSY):
bytes memory constructorParams = abi.encode(
address _erc4626,
address _offchainRewardManager
);
bytes memory initData = abi.encodeWithSelector(
PendleERC4626NoRedeemNoDepositUpgSY.initialize.selector,
string _name,
string _symbol
);