SRC-5115: Standardized Yield Token Standard
Abstract
This specification defines a standardized interface for yield-bearing tokens on the Fuel blockchain. Building upon the SRC-20 standard, it introduces functionality for depositing assets, redeeming shares, and tracking exchange rates for yield-generating mechanisms. The standard enables seamless integration between different yield protocols and provides a consistent interface for yield-bearing token operations.
Key Concepts
SCY Token (Standardized Composable Yield Token): A standardized yield-bearing token that represents ownership in a yield-generating pool. SCY tokens are minted when users deposit assets and can be redeemed for underlying assets plus accumulated yield. SRC-5115 contract mints only one asset (the SCY token itself) regardless of the variety of input tokens supported.
Shares: Units of ownership in the yield-generating pool. Each share represents a proportional claim on the underlying assets and accumulated yield. The number of shares a user receives is determined by the exchange rate at the time of deposit.
Exchange Rate: The ratio between the total value of underlying assets in the pool and the total number of shares. As the pool generates yield, the exchange rate increases, making each share worth more underlying assets over time.
Motivation
This standard is heavily inspired by Ethereum’s ERC-5115.
DeFi protocols implement yield generation through diverse mechanisms, requiring custom integration for each new protocol combination. This fragmentation creates barriers to interoperability and increases development overhead. The SRC-5115 standard addresses this by providing a unified interface for yield-bearing tokens across various DeFi mechanisms.
The standard accommodates multiple yield-generating scenarios:
- Yield-bearing assets that have different input tokens used for minting vs accounting for the pool value
- Assets with reward tokens by default (e.g., lending rewards)
- Liquid staking positions
- AMM liquidity provision tokens
- Rebasing tokens
Specification
Core Interface
The SRC-5115 standard defines the following ABI interface:
abi SRC5115 : SRC20 {
#[payable, storage(read, write)]
fn deposit(
receiver: Identity,
token_in: AssetId,
amount_token_to_deposit: u64,
min_shares_out: u64,
) -> u64;
#[payable, storage(read, write)]
fn redeem(
receiver: Identity,
token_out: AssetId,
amount_shares_to_redeem: u64,
min_token_out: u64,
) -> u64;
#[storage(read)]
fn exchange_rate() -> u256;
#[storage(read)]
fn get_tokens_in() -> Vec<AssetId>;
#[storage(read)]
fn get_tokens_out() -> Vec<AssetId>;
#[storage(read)]
fn yield_token() -> AssetId;
#[storage(read)]
fn preview_deposit(token_in: AssetId, amount_token_to_deposit: u64) -> u64;
#[storage(read)]
fn preview_redeem(token_out: AssetId, amount_shares_to_redeem: u64) -> u64;
}
struct DepositEvent {
caller: Identity,
receiver: Identity,
token_in: AssetId,
amount_deposited: u64,
amount_sy_out: u64,
}
struct RedeemEvent {
caller: Identity,
receiver: Identity,
token_out: AssetId,
amount_sy_to_redeem: u64,
amount_token_out: u64,
}
Required Functions
deposit
#[payable, storage(read, write)]
fn deposit(
receiver: Identity,
token_in: AssetId,
amount_token_to_deposit: u64,
min_shares_out: u64,
) -> u64;
Deposits amount_token_to_deposit of input token token_in and mints shares to receiver.
Parameters:
receiver: The Identity that will receive the minted sharestoken_in: The AssetId of the token being depositedamount_token_to_deposit: The amount of tokens to depositmin_shares_out: The minimum amount of shares to receive (slippage protection)
Returns:
u64: The actual amount of shares minted
Requirements:
- MUST emit a
Depositevent - MUST revert if amount of minted shares is less than
min_shares_out.
redeem
#[payable, storage(read, write)]
fn redeem(
receiver: Identity,
token_out: AssetId,
amount_shares_to_redeem: u64,
min_token_out: u64,
) -> u64;
Redeems amount_shares_to_redeem shares for token_out tokens and transfers them to receiver.
Parameters:
receiver: The Identity that will receive the redeemed tokenstoken_out: The AssetId of the token to receiveamount_shares_to_redeem: The amount of shares to redeemmin_token_out: The minimum amount of tokens to receive (slippage protection)
Returns:
u64: The actual amount of tokens redeemed
Requirements:
- MUST emit a
Redeemevent - MUST revert if redeemed amount of tokens is less than
min_token_out
exchange_rate
#[storage(read)]
fn exchange_rate() -> u256;
Returns the current exchange rate between shares and underlying assets, scaled by a fixed scaling factor (1e18).
Returns:
u256: The current exchange rate scaled by 1e18
Requirements:
- MUST return the current exchange rate such that
exchange_rate * shares / 1e18 = assets - MUST NOT include fees that are charged against the underlying yield token
Calculation:
The exchange rate is calculated as:
exchange_rate = (total_underlying_assets * EXCHANGE_RATE_SCALE) / total_shares
Where:
total_underlying_assetsis the total value of underlying assets in the pooltotal_sharesis the total supply of SCY tokens (shares)EXCHANGE_RATE_SCALEis typically 1e18 for precision
As the pool generates yield, the total underlying assets increase while the total shares remain constant, causing the exchange rate to increase over time.
get_tokens_in
#[storage(read)]
fn get_tokens_in() -> Vec<AssetId>;
Returns the list of all input tokens that can be used to deposit.
Returns:
Vec<AssetId>: Array of AssetIds that can be deposited
Requirements:
- MUST return at least one AssetId
get_tokens_out
#[storage(read)]
fn get_tokens_out() -> Vec<AssetId>;
Returns the list of all output tokens that can be converted into when redeeming.
Returns:
Vec<AssetId>: Array of AssetIds that can be redeemed
Requirements:
- MUST return at least one AssetId
yield_token
#[storage(read)]
fn yield_token() -> AssetId;
Returns the underlying yield-bearing token AssetId.
Returns:
AssetId: The underlying yield-bearing token AssetId
preview_deposit
#[storage(read)]
fn preview_deposit(token_in: AssetId, amount_token_to_deposit: u64) -> u64;
Returns the amount of shares that would be minted if amount_token_to_deposit of token_in were deposited.
Parameters:
token_in: The AssetId of the token to be depositedamount_token_to_deposit: The amount of tokens to deposit
Returns:
u64: The amount of shares that would be minted
Requirements:
- MUST return less than or equal to the actual return value of the
depositmethod - SHOULD NOT return greater than the actual return value of the
depositmethod
preview_redeem
#[storage(read)]
fn preview_redeem(token_out: AssetId, amount_shares_to_redeem: u64) -> u64;
Returns the amount of token_out that would be received if amount_shares_to_redeem shares were redeemed.
Parameters:
token_out: The AssetId of the token to receiveamount_shares_to_redeem: The amount of shares to redeem
Returns:
u64: The amount of tokens that would be received
Requirements:
- MUST return less than or equal to the actual return value of the
redeemmethod - SHOULD NOT return greater than the actual return value of the
redeemmethod
Events
The SRC-5115 standard defines the following events:
DepositEvent
pub struct DepositEvent {
caller: Identity,
receiver: Identity,
token_in: AssetId,
amount_deposited: u64,
amount_sy_out: u64,
}
Emitted when input tokens are deposited into the SRC-5115 contract via the deposit method.
Fields:
caller: The Identity that initiated the depositreceiver: The Identity that received the sharestoken_in: The AssetId of the deposited tokenamount_deposited: The amount of tokens depositedamount_sy_out: The amount of shares minted
RedeemEvent
pub struct RedeemEvent {
caller: Identity,
receiver: Identity,
token_out: AssetId,
amount_sy_to_redeem: u64,
amount_token_out: u64,
}
Emitted when shares are redeemed from the SRC-5115 contract via the redeem method.
Fields:
caller: The Identity that initiated the redemptionreceiver: The Identity that received the tokenstoken_out: The AssetId of the redeemed tokenamount_sy_to_redeem: The amount of shares redeemedamount_token_out: The amount of tokens received
Multiple Input/Output Tokens Support
SRC-5115 contracts may support multiple input and output tokens, providing flexibility for different yield-generating mechanisms. This allows users to deposit various assets and redeem into different tokens based on their needs.
Examples of Multi-Token Support
The following examples are taken from Ethereum’s Pendle protocol:
1. Liquid Staking Derivatives (Pendle wstETH SCY)
- Input tokens: ETH, wstETH
- Output tokens: ETH, wstETH
- Use case: Users can deposit ETH (which gets wrapped to wstETH) or directly deposit wstETH, and redeem into either ETH or wstETH
2. Money Market Supply (Pendle Compound SCY)
- Input tokens: USDC, cUSDC
- Output tokens: USDC, cUSDC, COMP (reward token)
- Use case: Users can deposit USDC (which gets supplied to Compound) or directly deposit cUSDC, and redeem into USDC, cUSDC, or claim COMP rewards
Implementation Guidelines
Exchange Rate Calculation
The exchange rate should be calculated as:
exchange_rate = (total_underlying_assets * exchange_rate_scale) / total_shares
Where:
total_underlying_assetsis the total value of underlying assets in the pooltotal_sharesis the total supply of SRC-5115 sharesexchange_rate_scaleis typically 1e18 for precision
Share Calculation
When depositing tokens:
shares_to_mint = (amount_deposited * exchange_rate_scale) / exchange_rate
When redeeming shares:
tokens_to_receive = (shares_to_redeem * exchange_rate) / exchange_rate_scale
Security Considerations
- Malicious implementations that conform to the interface can put users at risk
- All integrators should review implementations to avoid possible exploits
- The
yield_tokenfunction must accurately reflect the underlying yield-bearing token - Exchange rate calculations should be protected against manipulation
- Proper access controls should be implemented for administrative functions
Compatibility
All SRC-5115 tokens are also SRC-20 compliant and can be used in any SRC-20 context.