Skip to content

Commit

Permalink
style: line wrap at 79 for contracts
Browse files Browse the repository at this point in the history
  • Loading branch information
AlbertoCentonze committed Sep 23, 2024
1 parent cfc5081 commit 3c21e42
Showing 1 changed file with 79 additions and 99 deletions.
178 changes: 79 additions & 99 deletions contracts/RewardsHandler.vy
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,19 @@

"""
@title Rewards Handler
@notice A contract that helps distributing rewards for
st-crvUSD, an ERC4626 vault for crvUSD (yearn's vault
v3 multi-vault implementaiton is used).
Any crvUSD token sent to this contract is considered donated
as rewards for staker and will not be recoverable.
This contract can receive funds to be distributed from
the FeeSplitter (crvUSD borrow rates revenues) and
potentially other sources as well.
The amount of funds that this contract should receive from
the fee splitter is determined by computing the
time-weighted average of the vault balance over crvUSD
circulating supply ratio.
The contract handles the rewards in a permissionless
manner, anyone can take snapshots of the TVL and
distribute rewards.
In case of manipulation of the time-weighted
average, the contract allows trusted contracts given
the role of `RATE_MANGER` to correct the distribution
@notice A contract that helps distributing rewards for st-crvUSD, an ERC4626
vault for crvUSD (yearn's vault v3 multi-vault implementaiton is used).
Any crvUSD token sent to this contract is considered donated as rewards for
staker and will not be recoverable.
This contract can receive funds to be distributed from the FeeSplitter (crvUSD
borrow rates revenues) and potentially other sources as well.
The amount of funds that this contract should receive from the fee splitter is
determined by computing the time-weighted average of the vault balance over
crvUSD circulating supply ratio.
The contract handles the rewards in a permissionless manner, anyone can take
snapshots of the TVL and distribute rewards.
In case of manipulation of the time-weighted average, the contract allows
trusted contracts given the role of `RATE_MANGER` to correct the distribution
rate of the rewards.
@license Copyright (c) Curve.Fi, 2020-2024 - all rights reserved
@author curve.fi
Expand All @@ -38,13 +33,10 @@ implements: IDynamicWeight
# yearn vault's interface
from interfaces import IVault

# we use access control because we want
# to have multiple addresses being able to
# adjust the rate while only the dao
# (which has the `DEFAULT_ADMIN_ROLE`)
# we use access control because we want to have multiple addresses being able
# to adjust the rate while only the dao (which has the `DEFAULT_ADMIN_ROLE`)
# can appoint `RATE_MANAGER`s
from snekmate.auth import access_control

initializes: access_control
exports: (
# we don't expose `supportsInterface` from access control
Expand All @@ -57,8 +49,7 @@ exports: (
access_control.getRoleAdmin,
)

# import custom modules that contain
# helper functions.
# import custom modules that contain helper functions.
import StablecoinLens as lens
initializes: lens

Expand All @@ -85,14 +76,11 @@ _SUPPORTED_INTERFACES: constant(bytes4[3]) = [
stablecoin: immutable(IERC20)
vault: immutable(IVault)

# the minimum amount of rewards requested
# to the FeeSplitter
# the minimum amount of rewards requested to the FeeSplitter.
minimum_weight: public(uint256)

# the time over which rewards will be
# distributed mirror of the private
# `profit_max_unlock_time` variable
# from yearn vaults.
# the time over which rewards will be distributed mirror of the private
# `profit_max_unlock_time` variable from yearn vaults.
distribution_time: public(uint256)


Expand All @@ -106,9 +94,13 @@ def __init__(
):
lens.__init__(controller_factory)

# initialize access control
access_control.__init__()
# admin (most likely the dao) controls who can be a rate manager
access_control._grant_role(access_control.DEFAULT_ADMIN_ROLE, admin)
# admin itself is a RATE_ADMIN
access_control._grant_role(RATE_MANAGER, admin)
# deployer does not control this contract
access_control._revoke_role(access_control.DEFAULT_ADMIN_ROLE, msg.sender)

twa.__init__(
Expand All @@ -127,27 +119,21 @@ def __init__(
@external
def take_snapshot():
"""
@notice Function that anyone can call to take a
snapshot of the current balance/supply ratio
in the vault. This is used to compute the
time-weighted average of the TVL to decide
on the amount of rewards to ask for (weight).
@dev There's no point in MEVing this snapshot as
the rewards distribution rate can always be
reduced (if a malicious actor inflates the
value of the snapshot) or the minimum amount
of rewards can always be increased (if a malicious
actor deflates the value of the snapshot).
@notice Function that anyone can call to take a snapshot of the current
staked supply ratio in the vault. This is used to compute the time-weighted
average of the TVL to decide on the amount of rewards to ask for (weight).
@dev There's no point in MEVing this snapshot as the rewards distribution
rate can always be reduced (if a malicious actor inflates the value of the
snapshot) or the minimum amount of rewards can always be increased (if a
malicious actor deflates the value of the snapshot).
"""

# get the circulating supply from a helper function
# (supply in circulation = controllers' debt + peg
# keppers' debt)
# get the circulating supply from a helper function.
# supply in circulation = controllers' debt + peg keppers' debt
circulating_supply: uint256 = lens._circulating_supply()

# obtain the supply of crvUSD contained in the
# vault by simply checking its balance since it's
# an ERC4626 vault. This will also take into account
# obtain the supply of crvUSD contained in the vault by simply checking its
# balance since it's an ERC4626 vault. This will also take into account
# rewards that are not yet distributed.
supply_in_vault: uint256 = staticcall stablecoin.balanceOf(vault.address)

Expand All @@ -159,19 +145,20 @@ def take_snapshot():
@external
def process_rewards():
"""
@notice Permissionless function that let anyone
distribute rewards (if any) to the crvUSD vault.
@notice Permissionless function that let anyone distribute rewards (if any)
to the crvUSD vault.
"""

# prevent the rewards from being distributed untill
# the distribution rate has been set
assert (self.distribution_time != 0), "rewards should be distributed over time"
# prevent the rewards from being distributed untill the distribution rate
# has been set
assert (
self.distribution_time != 0
), "rewards should be distributed over time"


# any crvUSD sent to this contract (usually
# through the fee splitter, but could also
# come from other sources) will be used as
# a reward for crvUSD stakers in the vault.
# any crvUSD sent to this contract (usually through the fee splitter, but
# could also come from other sources) will be used as a reward for crvUSD
# stakers in the vault.
available_balance: uint256 = staticcall stablecoin.balanceOf(self)

# we distribute funds in 2 steps:
Expand All @@ -190,11 +177,11 @@ def process_rewards():
@view
def supportsInterface(interface_id: bytes4) -> bool:
"""
@dev Returns `True` if this contract implements the
interface defined by `interface_id`.
@dev Returns `True` if this contract implements the interface defined by
`interface_id`.
@param interface_id The 4-byte interface identifier.
@return bool The verification whether the contract
implements the interface or not.
@return bool The verification whether the contract implements the
interface or not.
"""
return interface_id in _SUPPORTED_INTERFACES

Expand All @@ -203,17 +190,13 @@ def supportsInterface(interface_id: bytes4) -> bool:
@view
def weight() -> uint256:
"""
@notice this function is part of the dynamic weight
interface expected by the FeeSplitter to know
what percentage of funds should be sent for
rewards distribution to crvUSD stakerks.
@dev `minimum_weight` acts as a lower bound for
the percentage of rewards that should be
distributed to stakers. This is useful to
bootstrapping TVL by asking for more at the
beginning and can also be increased in the
future if someone tries to manipulate the
time-weighted average of the tvl ratio.
@notice this function is part of the dynamic weight interface expected by
the FeeSplitter to know what percentage of funds should be sent for
rewards distribution to crvUSD stakerks.
@dev `minimum_weight` acts as a lower bound for the percentage of rewards
that should be distributed to stakers. This is useful to bootstrapping TVL
by asking for more at the beginning and can also be increased in the future
if someone tries to manipulate the time-weighted average of the tvl ratio.
"""
return max(twa._compute(), self.minimum_weight)

Expand All @@ -226,10 +209,9 @@ def weight() -> uint256:
@external
def set_twa_snapshot_dt(_min_snapshot_dt_seconds: uint256):
"""
@notice Setter for the time-weighted average minimal
frequency.
@param _min_snapshot_dt_seconds The minimum amount of
time that should pass between two snapshots.
@notice Setter for the time-weighted average minimal frequency.
@param _min_snapshot_dt_seconds The minimum amount of time that should pass
between two snapshots.
"""
access_control._check_role(RATE_MANAGER, msg.sender)
twa._set_snapshot_dt(_min_snapshot_dt_seconds)
Expand All @@ -239,8 +221,8 @@ def set_twa_snapshot_dt(_min_snapshot_dt_seconds: uint256):
def set_twa_window(_twa_window: uint256):
"""
@notice Setter for the time-weighted average window
@param _twa_window The time window used to compute
the TWA value of the balance/supply ratio.
@param _twa_window The time window used to compute the TWA value of the
balance/supply ratio.
"""
access_control._check_role(RATE_MANAGER, msg.sender)
twa._set_twa_window(_twa_window)
Expand All @@ -249,20 +231,18 @@ def set_twa_window(_twa_window: uint256):
@external
def set_distribution_time(new_distribution_time: uint256):
"""
@notice Admin function to correct the distribution
rate of the rewards. Making this value lower will reduce
the time it takes to stream the rewards, making it longer
will do the opposite. Setting it to 0 will immediately
distribute all the rewards.
@dev This function can be used to prevent the rewards
distribution from being manipulated (i.e. MEV twa
snapshots to obtain higher APR for the vault). Setting
this value to zero can be used to pause `process_rewards`.
@notice Admin function to correct the distribution rate of the rewards.
Making this value lower will reduce the time it takes to stream the
rewards, making it longer will do the opposite. Setting it to 0 will
immediately distribute all the rewards.
@dev This function can be used to prevent the rewards distribution from
being manipulated (i.e. MEV twa snapshots to obtain higher APR for the
vault). Setting this value to zero can be used to pause `process_rewards`.
"""
access_control._check_role(RATE_MANAGER, msg.sender)

# we mirror the value of new_profit_max_unlock_time
# from the yearn vault since it's not exposed publicly.
# we mirror the value of new_profit_max_unlock_time from the yearn vault
# since it's not exposed publicly.
self.distribution_time = new_distribution_time

# change the distribution time of the rewards in the vault
Expand All @@ -271,6 +251,7 @@ def set_distribution_time(new_distribution_time: uint256):
# enact the changes
extcall vault.process_report(self)


@external
def set_minimum_weight(new_minimum_weight: uint256):
"""
Expand All @@ -288,21 +269,20 @@ def set_minimum_weight(new_minimum_weight: uint256):
@external
def recover_erc20(token: IERC20, receiver: address):
"""
@notice This is a helper function to let an admin
rescue funds sent by mistake to this contract.
crvUSD cannot be recovered as it's part of the core
logic of this contract.
@notice This is a helper function to let an admin rescue funds sent by
mistake to this contract. crvUSD cannot be recovered as it's part of the
core logic of this contract.
"""
access_control._check_role(RATE_MANAGER, msg.sender)

# if crvUSD was sent by accident to the contract
# the funds are lost and will be distributed as
# staking rewards on the next `process_rewards`
# if crvUSD was sent by accident to the contract the funds are lost and
# will be distributed as staking rewards on the next `process_rewards`
# call.
assert token != stablecoin, "can't recover crvusd"
# when funds are recovered the whole balanced is sent
# to a trusted address.
# when funds are recovered the whole balanced is sent to a trusted address.
balance_to_recover: uint256 = staticcall token.balanceOf(self)
assert extcall token.transfer(receiver, balance_to_recover, default_return_value=True)
assert extcall token.transfer(
receiver, balance_to_recover, default_return_value=True
)

0 comments on commit 3c21e42

Please sign in to comment.