YieldSkimmingTokenizedStrategy
Inherits: TokenizedStrategy
Author: Golem Foundation
Specialized TokenizedStrategy for yield-bearing assets (appreciating exchange rate).
*Mechanism:
- Tracks value debt separately for users and dragon router (units: value-shares; 1 share = 1 asset value)
- On report(), compares total vault value (assets * rate in RAY) vs total value debt (users + dragon) • Profit: mints value-shares to dragon and increases dragon value debt • Loss: burns dragon shares (if enabled and available) and reduces dragon value debt
- Dual conversion modes: • Solvent: rate-based conversions using current exchange rate (RAY precision) • Insolvent: proportional distribution using base TokenizedStrategy logic; dragon operations blocked
- Dragon transfers trigger value-debt rebalancing; self-transfers by dragon are disallowed*
Note: security-contact: [email protected]
State Variables
YIELD_SKIMMING_STORAGE_SLOT
bytes32 private constant YIELD_SKIMMING_STORAGE_SLOT =
bytes32(uint256(keccak256("octant.yieldSkimming.exchangeRate")) - 1);
Functions
deposit
Deposit assets into the strategy with value debt tracking
*Requirements:
- Vault must be solvent (reverts otherwise)
- Receiver cannot be dragon router (dragon shares minted via report())
- Tracks asset value debt*
function deposit(uint256 assets, address receiver) external override nonReentrant returns (uint256 shares);
Parameters
| Name | Type | Description |
|---|---|---|
assets | uint256 | Amount of assets to deposit in asset base units |
receiver | address | Address to receive the shares (cannot be dragon router) |
Returns
| Name | Type | Description |
|---|---|---|
shares | uint256 | Amount of shares minted (1 share = 1 asset value) |
mint
Mint exact shares from the strategy with value debt tracking
Implements insolvency protection and tracks ETH value debt
function mint(uint256 shares, address receiver) external override nonReentrant returns (uint256 assets);
Parameters
| Name | Type | Description |
|---|---|---|
shares | uint256 | Amount of shares to mint |
receiver | address | Address to receive the shares |
Returns
| Name | Type | Description |
|---|---|---|
assets | uint256 | Amount of assets deposited in asset base units (1 share = 1 ETH value) |
redeem
Redeem shares from the strategy with default maxLoss
Wrapper that calls the full redeem function with MAX_BPS maxLoss
function redeem(uint256 shares, address receiver, address owner) external override returns (uint256 assets);
Parameters
| Name | Type | Description |
|---|---|---|
shares | uint256 | Amount of shares to redeem |
receiver | address | Address to receive the assets |
owner | address | Address whose shares are being redeemed |
Returns
| Name | Type | Description |
|---|---|---|
assets | uint256 | Amount of assets returned in asset base units |
redeem
Redeem shares from the strategy with value debt tracking
Shares represent ETH value (1 share = 1 ETH value)
function redeem(uint256 shares, address receiver, address owner, uint256 maxLoss)
public
override
nonReentrant
returns (uint256 assets);
Parameters
| Name | Type | Description |
|---|---|---|
shares | uint256 | Amount of shares to redeem |
receiver | address | Address to receive the assets |
owner | address | Address whose shares are being redeemed |
maxLoss | uint256 | Maximum acceptable loss in basis points |
Returns
| Name | Type | Description |
|---|---|---|
assets | uint256 | Amount of assets returned in asset base units |
withdraw
Withdraw assets from the strategy with value debt tracking
Calculates shares needed for the asset amount requested
function withdraw(uint256 assets, address receiver, address owner, uint256 maxLoss)
public
override
nonReentrant
returns (uint256 shares);
Parameters
| Name | Type | Description |
|---|---|---|
assets | uint256 | Amount of assets to withdraw in asset base units |
receiver | address | Address to receive the assets |
owner | address | Address whose shares are being redeemed |
maxLoss | uint256 | Maximum acceptable loss in basis points |
Returns
| Name | Type | Description |
|---|---|---|
shares | uint256 | Amount of shares burned in share base units |
withdraw
Withdraw assets from the strategy with default maxLoss
Wrapper that calls the full withdraw function with 0 maxLoss
function withdraw(uint256 assets, address receiver, address owner) external override returns (uint256 shares);
Parameters
| Name | Type | Description |
|---|---|---|
assets | uint256 | Amount of assets to withdraw in asset base units |
receiver | address | Address to receive withdrawn assets |
owner | address | Address whose shares are being redeemed |
Returns
| Name | Type | Description |
|---|---|---|
shares | uint256 | Amount of shares burned in share base units |
maxDeposit
Get the maximum amount of assets that can be deposited by a user
Returns 0 for dragon router as they cannot deposit
function maxDeposit(address receiver) public view override returns (uint256);
Parameters
| Name | Type | Description |
|---|---|---|
receiver | address | Address that would receive the shares |
Returns
| Name | Type | Description |
|---|---|---|
<none> | uint256 | Maximum deposit amount in asset base units |
maxMint
Get the maximum amount of shares that can be minted by a user
Returns 0 for dragon router as they cannot mint
function maxMint(address receiver) public view override returns (uint256);
Parameters
| Name | Type | Description |
|---|---|---|
receiver | address | Address that would receive the shares |
Returns
| Name | Type | Description |
|---|---|---|
<none> | uint256 | Maximum mint amount in shares |
maxWithdraw
Get the maximum amount of assets that can be withdrawn by a user
Returns 0 for dragon router during insolvency
function maxWithdraw(address owner) public view override returns (uint256);
Parameters
| Name | Type | Description |
|---|---|---|
owner | address | Address whose shares would be burned |
Returns
| Name | Type | Description |
|---|---|---|
<none> | uint256 | Maximum withdraw amount in asset base units |
maxRedeem
Get the maximum amount of shares that can be redeemed by a user
Returns 0 for dragon router during insolvency
function maxRedeem(address owner) public view override returns (uint256);
Parameters
| Name | Type | Description |
|---|---|---|
owner | address | Address whose shares would be burned |
Returns
| Name | Type | Description |
|---|---|---|
<none> | uint256 | Maximum redeem amount in shares |
getTotalUserDebtInAssetValue
Get the total ETH value debt owed to users
function getTotalUserDebtInAssetValue() external view returns (uint256);
Returns
| Name | Type | Description |
|---|---|---|
<none> | uint256 | Total user debt in asset value |
getDragonRouterDebtInAssetValue
Get the total ETH value debt owed to dragon router
function getDragonRouterDebtInAssetValue() external view returns (uint256);
Returns
| Name | Type | Description |
|---|---|---|
<none> | uint256 | Total dragon router debt in asset value |
getTotalValueDebtInAssetValue
Get the total ETH value debt owed to both users and dragon router combined
function getTotalValueDebtInAssetValue() external view returns (uint256);
Returns
| Name | Type | Description |
|---|---|---|
<none> | uint256 | Total debt in asset value combining users and dragon router |
transfer
Transfer shares with dragon solvency protection and debt rebalancing
*Special behaviors for dragon router:
- Dragon cannot transfer to itself (reverts)
- Dragon transfers trigger value debt rebalancing
- Dragon can only transfer when vault is solvent For non-dragon transfers, behaves like standard ERC20 transfer*
function transfer(address to, uint256 amount) external override returns (bool success);
Parameters
| Name | Type | Description |
|---|---|---|
to | address | Address receiving shares |
amount | uint256 | Amount of shares to transfer |
Returns
| Name | Type | Description |
|---|---|---|
success | bool | Whether transfer succeeded |
transferFrom
Transfer shares from one address to another with dragon solvency protection and debt rebalancing
*Special behaviors for dragon router:
- Dragon cannot transfer to itself (reverts)
- Dragon transfers trigger value debt rebalancing
- Dragon can only transfer when vault is solvent For non-dragon transfers, behaves like standard ERC20 transferFrom*
function transferFrom(address from, address to, uint256 amount) external override returns (bool success);
Parameters
| Name | Type | Description |
|---|---|---|
from | address | Address transferring shares |
to | address | Address receiving shares |
amount | uint256 | Amount of shares to transfer |
Returns
| Name | Type | Description |
|---|---|---|
success | bool | Whether transfer succeeded |
report
Reports yield skimming strategy performance and handles value debt adjustments
*Overrides report to handle yield appreciation and loss recovery using value debt approach. Health check effectiveness depends on report() frequency. Exchange rate checks become less effective over time if reports are infrequent, as profit limits may be exceeded. Management should ensure regular reporting or adjust profit/loss ratios based on expected frequency. Key behaviors:
- Value Debt Tracking: Compares current total value (assets * exchange rate) vs total debt (user debt + dragon router debt combined)
- Profit Capture: When current value exceeds total debt, mints shares to dragonRouter and increases dragon debt accordingly
- Loss Protection: When current value is less than total debt, burns dragon shares (up to available balance) and reduces dragon debt
- Insolvency Handling: If dragon buffer insufficient for losses, remaining shortfall is handled through proportional asset distribution during withdrawals, not by modifying debt balances*
function report() public override(TokenizedStrategy) nonReentrant onlyKeepers returns (uint256 profit, uint256 loss);
Returns
| Name | Type | Description |
|---|---|---|
profit | uint256 | Profit in assets from underlying value appreciation |
loss | uint256 | Loss in assets from underlying value depreciation |
getLastRateRay
Get the last reported exchange rate (RAY precision)
function getLastRateRay() external view returns (uint256);
Returns
| Name | Type | Description |
|---|---|---|
<none> | uint256 | Last reported exchange rate in RAY precision |
isVaultInsolvent
Check if the vault is currently insolvent
function isVaultInsolvent() external view returns (bool);
Returns
| Name | Type | Description |
|---|---|---|
<none> | bool | isInsolvent True if vault cannot cover user value debt and dragon router debt |
_deposit
Internal deposit function that handles asset transfers and share minting
function _deposit(StrategyData storage S, address receiver, uint256 assets, uint256 shares) internal override;
Parameters
| Name | Type | Description |
|---|---|---|
S | StrategyData | Strategy data storage reference |
receiver | address | Address receiving minted shares |
assets | uint256 | Amount of assets being deposited in asset base units |
shares | uint256 | Amount of shares to mint |
_convertToShares
Converts assets to shares using value debt approach with solvency awareness
function _convertToShares(StrategyData storage S, uint256 assets, Math.Rounding rounding)
internal
view
virtual
override
returns (uint256);
Parameters
| Name | Type | Description |
|---|---|---|
S | StrategyData | Strategy storage |
assets | uint256 | Amount of assets to convert |
rounding | Math.Rounding | Rounding mode for division |
Returns
| Name | Type | Description |
|---|---|---|
<none> | uint256 | Amount of shares equivalent in value (1 share = 1 ETH value) |
_convertToAssets
Converts shares to assets using value debt approach with solvency awareness
function _convertToAssets(StrategyData storage S, uint256 shares, Math.Rounding rounding)
internal
view
virtual
override
returns (uint256);
Parameters
| Name | Type | Description |
|---|---|---|
S | StrategyData | Strategy storage |
shares | uint256 | Amount of shares to convert |
rounding | Math.Rounding | Rounding mode for division |
Returns
| Name | Type | Description |
|---|---|---|
<none> | uint256 | Amount of assets user would receive in asset base units |
_isVaultInsolvent
Checks if the vault is currently insolvent
function _isVaultInsolvent() internal view returns (bool isInsolvent);
Returns
| Name | Type | Description |
|---|---|---|
isInsolvent | bool | True if vault cannot cover user value debt |
_rebalanceDebtOnDragonTransfer
Rebalances debt tracking when dragon transfers shares in or out
function _rebalanceDebtOnDragonTransfer(address from, address to, uint256 transferAmount) internal;
_requireDragonSolvency
Blocks dragon router from withdrawing during vault insolvency
function _requireDragonSolvency(address account) internal view;
Parameters
| Name | Type | Description |
|---|---|---|
account | address | Address to check (only blocks if it's dragon router) |
_requireVaultSolvency
Blocks all operations when vault is insolvent
function _requireVaultSolvency() internal view;
_currentRateRay
Get the current exchange rate scaled to RAY precision
function _currentRateRay() internal view virtual returns (uint256);
Returns
| Name | Type | Description |
|---|---|---|
<none> | uint256 | Current exchange rate in RAY format (1e27 = 1.0) |
_handleDragonLossProtection
Internal function to handle loss protection by burning dragon shares
function _handleDragonLossProtection(
StrategyData storage S,
YieldSkimmingStorage storage YS,
uint256 lossValue,
uint256 currentRate
) internal returns (uint256 loss);
Parameters
| Name | Type | Description |
|---|---|---|
S | StrategyData | Strategy storage pointer |
YS | YieldSkimmingStorage | Yield skimming storage pointer |
lossValue | uint256 | Loss amount in ETH value terms |
currentRate | uint256 | Current exchange rate in RAY format |
Returns
| Name | Type | Description |
|---|---|---|
loss | uint256 | Loss amount in asset terms for reporting |
finalizeDragonRouterChange
Finalizes the dragon router change with proper debt accounting migration
Migrates debt tracking when dragon router changes to maintain correct accounting
function finalizeDragonRouterChange() external override;
_strategyYieldSkimmingStorage
function _strategyYieldSkimmingStorage() internal pure returns (YieldSkimmingStorage storage S);
Events
Harvest
Event emitted when harvest is performed
event Harvest(address indexed caller, uint256 currentRate);
DonationMinted
Events for donation tracking
event DonationMinted(address indexed dragonRouter, uint256 amount, uint256 exchangeRate);
Parameters
| Name | Type | Description |
|---|---|---|
dragonRouter | address | Address receiving or burning donation shares |
amount | uint256 | Amount of value-shares minted or burned (1 share = 1 value unit) |
exchangeRate | uint256 | Current exchange rate (scaled to wad) at the time of the event |
DonationBurned
Emitted when dragon shares are burned to cover value losses
event DonationBurned(address indexed dragonRouter, uint256 amount, uint256 exchangeRate);
Structs
YieldSkimmingStorage
Storage for yield skimming strategy
struct YieldSkimmingStorage {
uint256 totalUserDebtInAssetValue;
uint256 lastReportedRate;
uint256 dragonRouterDebtInAssetValue;
}