Disclaimer: Everything stated in this article does not constitute financial advice. Please consider the presented material solely as a demonstration of using Python in the field of DeFi (decentralized finance). We strongly encourage you to conduct your research, analyze the issue carefully, and account for your personal financial goals and risks. Setting personal parameters and coefficients is a crucial aspect of working with DeFi instruments. All decisions should be made deliberately and independently, with a clear mind and a full understanding of the potential consequences.
The cryptocurrency sphere is striking with its diversity and opportunities. In 2024, there are many different ways to interact with the blockchain. One popular direction is DeFi (decentralized finance), which main idea is to provide liquidity in exchange for rewards. Each protocol offers its own unique reward distribution system for active users, aiming to attract them through various incentive methods.
Nearly all projects provide rewards in their native tokens. Some offer fixed rates akin to bank deposits, while others provide floating rates, which fluctuate based on market conditions and user activity. Additionally, some reward retrospectively, revealing the criteria for earning rewards at the time of token distribution.
At the beginning of 2024, the concept of issuing rewards in the form of points became popular. These points can be exchanged for the project’s tokens at the end of the incentive program. This approach combines elements of all previous models. Users can calculate the number of points earned over the participation period, but the final value of these points remains undetermined until the program concludes. Many factors affect the final reward price: the protocol valuation (FDV — Fully Diluted Valuation), the liquidity volume in the protocol (TVL — Total Value Locked), the duration of the program, the percentage of tokens allocated for rewards, the rules for converting points, and much more. The more of these factors are known, the more accurately one can estimate the potential rewards and make a well-considered decision — whether to participate in the project or wait for more favorable opportunities.
Today, I would like to consider one method of automated yield calculation using the Python language. As an example, we will use the Renzo liquid restaking protocol and the Pendle platform, which allows separating the principal asset (PT — Principal Tokens) and the yield (YT — Yield Tokens), exchanging one for the other. In this case, the principal asset (ETH), purchased for USD, will be exchanged for YT tokens that generate yield from the Renzo protocol. At the end of the pool’s term, the YT tokens will lose their value (their price will become 0), but during that period, they will generate yield that we will calculate.
On July 27, 2024, the Renzo project announced the end of the second incentive season and the start of the third. According to the provided data, the third season will last four months, during which 400 million Rez tokens will be distributed as rewards to participants of the restaking program via the platform. As in previous seasons, Renzo rewards users linearly, based on accumulated points, without providing advantages to users with small or large deposits. This creates more equal conditions for all participants.
The project has already conducted its TGE (Token Generation Event), so the token price is known and available for analysis. The interaction model with the project assumes obtaining rewards both from staking in EigenLayer and from restaking in Renzo. The mechanism for distributing Eigen tokens from EigenLayer is also known: it is a linear weekly distribution of a fixed amount of tokens among all users who have deposited assets into the protocol.
Now, with the input data gathered, we can proceed with the calculations.
First, it is necessary to obtain the current price of one YT token in the ezETH pool with an expiration date of December 26, 2024. The pool in the ERC20 network is used for the search, and the contract address is stored in advance. Additional information about other pools on different networks can be found in the Pendle documentation (https://api-v2.pendle.finance/core/docs).
def get_pendle_pool_info() -> float:
chain_id = 1
renzo_yt_contract = "0x7749f5ed1e356edc63d469c2fcac9adeb56d1c2b"
url = f"https://api-v2.pendle.finance/core/v1/{chain_id}/assets/prices?addresses={renzo_yt_contract}"
return get(url).json()['prices'][renzo_yt_contract]Next, it is necessary to calculate the number of hours during which points will be accrued in the selected pool. For this, a specific season end date is used.
season_date_end = datetime.fromisoformat("2024-11-27T00:00:00.000")
hours_before_season_ends = int((season_date_end - datetime.utcnow()).total_seconds() / 3600)Then, it is necessary to compute the number of YT tokens that can be purchased with a nominal amount of $1000.
amount_to_invest = 1000
yt_amount = amount_to_invest / rez_yt_priceNext, the number of points that will be accrued until the end of the season is calculated. The standard number of points per 1 ETH is used for the calculation. The pool has a multiplier of x2, which should be taken into account in the calculations (the multiplier should be confirmed in the Pendle or Renzo Discord channels, as project documentation may indicate different values, but in such calculations it is better to use the lower bound).
def calculate_point_amount(yt_amount: float, hours_before_season_ends: int) -> float:
points_per_hour = 2
return (
yt_amount
* points_per_hour
* hours_before_season_ends
)For further calculations, it is necessary to know the current prices of REZ tokens (for Renzo rewards) and EIGEN tokens (for EigenLayer rewards). It is important to note that these prices can be volatile and may change by the time the rewards are received.
def get_token_prices() -> Tuple[float, float]:
response = get("https://coins.llama.fi/prices/current/coingecko:renzo,coingecko:eigenlayer")
return response.json()["coins"]["coingecko:renzo"]["price"],\
response.json()["coins"]["coingecko:eigenlayer"]["price"]To estimate the total number of points that will be distributed by the end of the season, several key factors must be considered: the current amount of ETH locked in the protocol, the expected duration of the season, and the average multiplier. The process begins by obtaining data on the current state of the protocol. Using the API provided by the Renzo project, one can get information on the amount of ETH that is staked. This value reflects the current state of the protocol's liquidity and is used to calculate the total number of points that will be distributed among participants.
Next, the duration of the season is determined, which, in our case, is 123 days. This value is taken based on public data on the duration of the current staking season in the protocol. The specific number of hours until the end of the season can be calculated using the season end date and the current time.
Another important parameter is the multiplier. The multiplier is a coefficient that affects the rate of point accumulation depending on the specific pool or activity. In this example, an average multiplier value of 2 is used, as it is the most common value for most active pools. However, it should be noted that different assets and pools may have different multipliers, and their values should be confirmed directly in the project or community (for example, via Discord).
It is important to note that this calculation is approximate and cannot claim absolute accuracy. Various factors may influence the total number of points accrued by the end of the season, such as:
Based on previous seasons, such a calculation gives an error margin of approximately 5-10%. For a more precise estimate, specialized analytical tools such as Dune Analytics can be used, allowing for an in-depth analysis of liquidity dynamics, asset distribution, and user participation. Such analysis can reduce the error margin and provide a more accurate picture of potential rewards by the end of the season.
This approach to estimating points allows for a forecast that will become more accurate with regular updates of data on liquidity, token prices, and protocol participants.
def get_eth_based_season_points() -> float:
response = get("https://app.renzoprotocol.com/api/stats")
tvl_in_eth = response.json()["data"]["restakedTVL"]["data"]["eth"]
season_hours_length = 123 * 24
points_per_hour = 1
average_multiplier = 2
return tvl_in_eth * season_hours_length * average_multiplier * points_per_hourAfter calculating the number of points earned based on the ETH locked in the protocol, it is necessary to account for an additional coefficient related to staking previously received REZ tokens. This step is important because REZ tokens received by participants in previous distributions can also be used to earn additional points. This adds another layer of rewards for participants who continue to hold or stake their REZ tokens in the system.
The Renzo project incentivizes participants not only for restaking ETH but also for holding and staking REZ tokens received as rewards. This creates a situation where rewards from previous seasons can be used to increase future income. To account for this factor, an additional coefficient is introduced.
At the time of writing, the ratio between the points earned from ETH staking and those from REZ staking is approximately 1 to 25. This means that for every 25 points earned from ETH, there is 1 point from REZ staking. This coefficient may vary depending on market dynamics and the protocol’s policies. In our case, for simplicity, a coefficient of 1.04 is used. This means that the final number of points earned from ETH is increased by 4% due to the points from staking REZ tokens.
For a more accurate coefficient, one can use more detailed data available through analytical services such as Dune Analytics or the protocol’s internal dashboards. These services allow tracking the daily dynamics of point distribution between ETH and REZ, analyzing how the share of REZ stakers changes, and calculating the impact of these factors on the overall coefficient. A deeper analysis will allow taking into account changes in the number of REZ tokens held by users and their impact on the total volume of rewards.
def get_total_season_points() -> float:
rez_coefficient = 1.04
return get_eth_based_season_points() * rez_coefficientTo obtain the final result, the calculated number of points based on ETH is multiplied by the REZ staking coefficient:
def calculate_point_price(rez_price: float) -> float:
airdrop_token_distribution = 400_000_000
total_season_points = get_total_season_points()
tokens_per_point = airdrop_token_distribution / total_season_points
point_price = rez_price * tokens_per_point
return point_priceIt is important to note that the REZ staking coefficient may change over time, depending on how many REZ tokens participants decide to keep staked. Over time, the number of participants using their REZ to earn additional points may increase or decrease, which will directly affect the coefficient. Thus, for more accurate calculations, it is important to regularly update the data and conduct further analysis based on the current situation in the protocol. Accounting for this coefficient allows for a more complete picture of how many points may be earned by the end of the season and how the reward distribution affects the overall yield for participants.
Once the price of points and their number are known, the potential profit can be calculated.
def calculate_renzo_profit(renzo_points: float, point_price: float) -> float:
return renzo_points * point_priceEigenLayer operates on a system of linear distribution of fixed rewards every week among all participants staking ETH. To calculate the potential yield, several parameters are used:
def calculate_eigen_profit(eigen_price: float, yt_amount: float, hours_before_season_ends: int) -> float:
eigen_rewards_per_year = 66_945_866
total_eth_locked = 4_220_000
one_week_rewards = eigen_rewards_per_year / 52
weekly_rewards_per_one_eth = one_week_rewards / total_eth_locked
weeks_until_pool_ends = hours_before_season_ends / 24 / 7
return weeks_until_pool_ends * yt_amount * weekly_rewards_per_one_eth * eigen_priceAll intermediate values are combined to obtain the final profit value.
def calculate_renzo_pool_profit():
amount_to_invest = 1000
season_date_end = datetime.fromisoformat("2024-11-27T00:00:00.000") # predicted time according to docs
hours_before_season_ends = int((season_date_end - datetime.utcnow()).total_seconds() / 3600)
if hours_before_season_ends < 0:
print("Pool is expired")
return
rez_yt_price = get_pendle_pool_info()
yt_amount = amount_to_invest / rez_yt_price
rez_price, eigen_price = get_token_prices()
renzo_points = calculate_point_amount(yt_amount, hours_before_season_ends)
point_price = calculate_point_price(rez_price)
renzo_profit = calculate_renzo_profit(renzo_points, point_price)
el_profit = calculate_eigen_profit(eigen_price, yt_amount, hours_before_season_ends)
value_delta = (
renzo_profit + el_profit - amount_to_invest
)
percent_delta = (value_delta / amount_to_invest) * 100
print(f"Amount invested {amount_to_invest}")
print(f"Price of 1 renzo YT {rez_yt_price}")
print(f"Renzo rewards {renzo_profit}")
print(f"Eigen rewards {el_profit}")
print(f"Delta {value_delta}")
print(f"Delta in percents {percent_delta}")
Based on the current calculations, purchasing this asset may lead to a loss of funds. However, indicators such as multipliers, project TVL, token prices, and other parameters may change, which will affect the situation in the future. It is also worth noting that the Renzo yield calculation was conducted for only the third season and did not take into account additional rewards from subsequent campaigns.
In conclusion, it is essential to regularly update all data and conduct analyses based on current information and sound judgment. All calculations depend on numerous dynamic factors, such as liquidity, token prices, and reward policies, which can change over time. Making investment decisions requires careful calculation and thorough research.
And, as the saying goes, in the times of the gold rush, the one who sells the pickaxes earns the most. Good luck!
from typing import Tuple
from requests import get
from datetime import datetime
def get_pendle_pool_info() -> float:
"""Getting data about required asset.
More info: https://api-v2.pendle.finance/core/docs
"""
# use only erc20 chain for example
chain_id = 1
# ezETH YT contract
renzo_yt_contract = "0x7749f5ed1e356edc63d469c2fcac9adeb56d1c2b"
url = f"https://api-v2.pendle.finance/core/v1/{chain_id}/assets/prices?addresses={renzo_yt_contract}"
return get(url).json()['prices'][renzo_yt_contract]
def calculate_point_amount(yt_amount: float, hours_before_season_ends: int) -> float:
# default amount is 1 but this pool has a 2x multiplier
points_per_hour = 2
predicted_protocol_points = (
yt_amount
* points_per_hour
* hours_before_season_ends
)
return predicted_protocol_points
def calculate_point_price(rez_price: float) -> float:
airdrop_token_distribution = 400_000_000 # according to season 3 rules
total_season_points = get_total_season_points()
tokens_per_point = airdrop_token_distribution / total_season_points
point_price = rez_price * tokens_per_point
return point_price
def get_token_prices() -> Tuple[float, float]:
"""Fetch current REZ and EIGEN price from coingecko."""
response = get("https://coins.llama.fi/prices/current/coingecko:renzo,coingecko:eigenlayer")
return response.json()["coins"]["coingecko:renzo"]["price"],\
response.json()["coins"]["coingecko:eigenlayer"]["price"]
def calculate_renzo_profit(renzo_points: float, point_price: float) -> float:
return renzo_points * point_price
def calculate_eigen_profit(eigen_price: float, yt_amount: float, hours_before_season_ends: int) -> float:
eigen_rewards_per_year = 66_945_866
# amount on the date of article creation
total_eth_locked = 4_220_000
# tokens distributed ones per week
one_week_rewards = eigen_rewards_per_year / 52
weekly_rewards_per_one_eth = one_week_rewards / total_eth_locked
weeks_until_pool_ends = hours_before_season_ends / 24 / 7
return weeks_until_pool_ends * yt_amount * weekly_rewards_per_one_eth * eigen_price
def calculate_renzo_pool_profit():
"""Check renzo pendle pool price.
Steps:
1. Get renzo yt price
2. Calculate renzo point that will be farmed until target date
3. Calculate renzo point price
4. Calculate renzo profit
5. Calculate eigen profit
"""
amount_to_invest = 1000 # could be changed to any value
season_date_end = datetime.fromisoformat("2024-11-27T00:00:00.000") # predicted time according to docs
hours_before_season_ends = int((season_date_end - datetime.utcnow()).total_seconds() / 3600)
if hours_before_season_ends < 0:
print("Pool is expired")
return
rez_yt_price = get_pendle_pool_info()
yt_amount = amount_to_invest / rez_yt_price
rez_price, eigen_price = get_token_prices()
renzo_points = calculate_point_amount(yt_amount, hours_before_season_ends)
point_price = calculate_point_price(rez_price)
renzo_profit = calculate_renzo_profit(renzo_points, point_price)
el_profit = calculate_eigen_profit(eigen_price, yt_amount, hours_before_season_ends)
value_delta = (
renzo_profit + el_profit - amount_to_invest
)
percent_delta = (value_delta / amount_to_invest) * 100
print(f"Amount invested {amount_to_invest}")
print(f"Price of 1 renzo YT {rez_yt_price}")
print(f"Renzo rewards {renzo_profit}")
print(f"Eigen rewards {el_profit}")
print(f"Delta {value_delta}")
print(f"Delta in percents {percent_delta}")
return value_delta, percent_delta
def get_total_season_points() -> float:
# approximate coefficient of farming points through rez token staking
# this proportion is not accurate cause proportion of farming changes
rez_coefficient = 1.04
return get_eth_based_season_points() * rez_coefficient
def get_eth_based_season_points() -> float:
response = get("https://app.renzoprotocol.com/api/stats")
tvl_in_eth = response.json()["data"]["restakedTVL"]["data"]["eth"]
season_hours_length = 123 * 24 # approximate season length
points_per_hour = 1 # amount of points farmed per hour per 1 eth
# different strategies gives from 1 to 4 multiplier for earned points.
# most people just store assets on wallets with minimal multiplier,
# but some use DEFi and earns 4x.
# for demo purposes i use 2 as an average
# but it's possible to calculate it more accurately using renzo DEFi dashboard or dune analytics
average_multiplier = 2
return tvl_in_eth * season_hours_length * average_multiplier * points_per_hour
if __name__ == '__main__':
calculate_renzo_pool_profit()