TokenizedStrategy.sol
Author: yearn.finance
This TokenizedStrategy can be used by anyone wishing to easily build
and deploy their own custom ERC4626 compliant single strategy Vault.
The TokenizedStrategy contract is meant to be used as the proxy
implementation contract that will handle all logic, storage and
management for a custom strategy that inherits the BaseStrategy
.
Any function calls to the strategy that are not defined within that
strategy will be forwarded through a delegateCall to this contract.
A strategist only needs to override a few simple functions that are
focused entirely on the strategy specific needs to easily and cheaply
deploy their own permissionless 4626 compliant vault.
State Variables​
API_VERSION​
API version this TokenizedStrategy implements.
string internal constant API_VERSION = "3.0.2";
MAX_FEE​
Maximum in Basis Points the Performance Fee can be set to.
uint16 public constant MAX_FEE = 5_000;
BASE_STRATEGY_STORAGE​
Custom storage slot that will be used to store the
StrategyData
struct that holds each strategies
specific storage variables.
Any storage updates done by the TokenizedStrategy actually update
the storage of the calling contract. This variable points
to the specific location that will be used to store the
struct that holds all that data.
We use a custom string in order to get a random
storage slot that will allow for strategists to use any
amount of storage in their strategy without worrying
about collisions.
bytes32 internal constant BASE_STRATEGY_STORAGE = bytes32(uint256(keccak256("yearn.base.strategy.storage")) - 1);
FACTORY​
Address of the previously deployed Vault factory that the
address public immutable FACTORY;
Functions​
onlyManagement​
Require that the call is coming from the strategies management.
modifier onlyManagement();
onlyKeepers​
Require that the call is coming from either the strategies management or the keeper.
modifier onlyKeepers();
onlyEmergencyAuthorized​
Require that the call is coming from either the strategies management or the emergencyAdmin.
modifier onlyEmergencyAuthorized();
nonReentrant​
Prevents a contract from calling itself, directly or indirectly. Placed over all state changing functions for increased safety.
modifier nonReentrant();
requireManagement​
Require a caller is management
.
Is left public so that it can be used by the Strategy. When the Strategy calls this the msg.sender would be the address of the strategy so we need to specify the sender.
function requireManagement(address _sender) public view;
Parameters
Name | Type | Description |
---|---|---|
_sender | address | The original msg.sender. |
requireKeeperOrManagement​
Require a caller is the keeper
or management
.
Is left public so that it can be used by the Strategy. When the Strategy calls this the msg.sender would be the address of the strategy so we need to specify the sender.
function requireKeeperOrManagement(address _sender) public view;
Parameters
Name | Type | Description |
---|---|---|
_sender | address | The original msg.sender. |
requireEmergencyAuthorized​
Require a caller is the management
or emergencyAdmin
.
Is left public so that it can be used by the Strategy. When the Strategy calls this the msg.sender would be the address of the strategy so we need to specify the sender.
function requireEmergencyAuthorized(address _sender) public view;
Parameters
Name | Type | Description |
---|---|---|
_sender | address | The original msg.sender. |
_strategyStorage​
will return the actual storage slot where the strategy
specific StrategyData
struct is stored for both read
and write operations.
This loads just the slot location, not the full struct
so it can be used in a gas efficient manner.
function _strategyStorage() internal pure returns (StrategyData storage S);
initialize​
Used to initialize storage for a newly deployed strategy.
This should be called atomically whenever a new strategy is
deployed and can only be called once for each strategy.
This will set all the default storage that must be set for a
strategy to function. Any changes can be made post deployment
through external calls from management
.
The function will also emit an event that off chain indexers can
look for to track any new deployments using this TokenizedStrategy.
function initialize(
address _asset,
string memory _name,
address _management,
address _performanceFeeRecipient,
address _keeper
) external;
Parameters
Name | Type | Description |
---|---|---|
_asset | address | Address of the underlying asset. |
_name | string | Name the strategy will use. |
_management | address | Address to set as the strategies management . |
_performanceFeeRecipient | address | Address to receive performance fees. |
_keeper | address | Address to set as strategies keeper . |
deposit​
Mints shares
of strategy shares to receiver
by
depositing exactly assets
of underlying tokens.
function deposit(uint256 assets, address receiver) external nonReentrant returns (uint256 shares);
Parameters
Name | Type | Description |
---|---|---|
assets | uint256 | The amount of underlying to deposit in. |
receiver | address | The address to receive the shares . |
Returns
Name | Type | Description |
---|---|---|
shares | uint256 | The actual amount of shares issued. |
mint​
Mints exactly shares
of strategy shares to
receiver
by depositing assets
of underlying tokens.
function mint(uint256 shares, address receiver) external nonReentrant returns (uint256 assets);
Parameters
Name | Type | Description |
---|---|---|
shares | uint256 | The amount of strategy shares mint. |
receiver | address | The address to receive the shares . |
Returns
Name | Type | Description |
---|---|---|
assets | uint256 | The actual amount of asset deposited. |
withdraw​
Withdraws exactly assets
from owners
shares and sends
the underlying tokens to receiver
.
This will default to not allowing any loss to be taken.
function withdraw(uint256 assets, address receiver, address owner) external returns (uint256 shares);
Parameters
Name | Type | Description |
---|---|---|
assets | uint256 | The amount of underlying to withdraw. |
receiver | address | The address to receive assets . |
owner | address | The address whose shares are burnt. |
Returns
Name | Type | Description |
---|---|---|
shares | uint256 | The actual amount of shares burnt. |
withdraw​
Withdraws assets
from owners
shares and sends
the underlying tokens to receiver
.
This includes an added parameter to allow for losses.
function withdraw(uint256 assets, address receiver, address owner, uint256 maxLoss)
public
nonReentrant
returns (uint256 shares);
Parameters
Name | Type | Description |
---|---|---|
assets | uint256 | The amount of underlying to withdraw. |
receiver | address | The address to receive assets . |
owner | address | The address whose shares are burnt. |
maxLoss | uint256 | The amount of acceptable loss in Basis points. |
Returns
Name | Type | Description |
---|---|---|
shares | uint256 | The actual amount of shares burnt. |
redeem​
Redeems exactly shares
from owner
and
sends assets
of underlying tokens to receiver
.
This will default to allowing any loss passed to be realized.
function redeem(uint256 shares, address receiver, address owner) external returns (uint256);
Parameters
Name | Type | Description |
---|---|---|
shares | uint256 | The amount of shares burnt. |
receiver | address | The address to receive assets . |
owner | address | The address whose shares are burnt. |
Returns
Name | Type | Description |
---|---|---|
<none> | uint256 | assets The actual amount of underlying withdrawn. |
redeem​
Redeems exactly shares
from owner
and
sends assets
of underlying tokens to receiver
.
This includes an added parameter to allow for losses.
function redeem(uint256 shares, address receiver, address owner, uint256 maxLoss)
public
nonReentrant
returns (uint256);
Parameters
Name | Type | Description |
---|---|---|
shares | uint256 | The amount of shares burnt. |
receiver | address | The address to receive assets . |
owner | address | The address whose shares are burnt. |
maxLoss | uint256 | The amount of acceptable loss in Basis points. |
Returns
Name | Type | Description |
---|---|---|
<none> | uint256 | . The actual amount of underlying withdrawn. |
totalAssets​
Get the total amount of assets this strategy holds
as of the last report.
We manually track totalAssets
to avoid any PPS manipulation.
function totalAssets() external view returns (uint256);
Returns
Name | Type | Description |
---|---|---|
<none> | uint256 | . Total assets the strategy holds. |
totalSupply​
Get the current supply of the strategies shares. Locked shares issued to the strategy from profits are not counted towards the full supply until they are unlocked. As more shares slowly unlock the totalSupply will decrease causing the PPS of the strategy to increase.
function totalSupply() external view returns (uint256);
Returns
Name | Type | Description |
---|---|---|
<none> | uint256 | . Total amount of shares outstanding. |
convertToShares​
The amount of shares that the strategy would exchange for the amount of assets provided, in an ideal scenario where all the conditions are met.
function convertToShares(uint256 assets) external view returns (uint256);
Parameters
Name | Type | Description |
---|---|---|
assets | uint256 | The amount of underlying. |
Returns
Name | Type | Description |
---|---|---|
<none> | uint256 | . Expected shares that assets represents. |
convertToAssets​
The amount of assets that the strategy would exchange for the amount of shares provided, in an ideal scenario where all the conditions are met.
function convertToAssets(uint256 shares) external view returns (uint256);
Parameters
Name | Type | Description |
---|---|---|
shares | uint256 | The amount of the strategies shares. |
Returns
Name | Type | Description |
---|---|---|
<none> | uint256 | . Expected amount of asset the shares represents. |
previewDeposit​
Allows an on-chain or off-chain user to simulate the effects of their deposit at the current block, given current on-chain conditions.
This will round down.
function previewDeposit(uint256 assets) external view returns (uint256);
Parameters
Name | Type | Description |
---|---|---|
assets | uint256 | The amount of asset to deposits. |
Returns
Name | Type | Description |
---|---|---|
<none> | uint256 | . Expected shares that would be issued. |
previewMint​
Allows an on-chain or off-chain user to simulate the effects of their mint at the current block, given current on-chain conditions.
This is used instead of convertToAssets so that it can round up for safer mints.
function previewMint(uint256 shares) external view returns (uint256);
Parameters
Name | Type | Description |
---|---|---|
shares | uint256 | The amount of shares to mint. |
Returns
Name | Type | Description |
---|---|---|
<none> | uint256 | . The needed amount of asset for the mint. |
previewWithdraw​
Allows an on-chain or off-chain user to simulate the effects of their withdrawal at the current block, given current on-chain conditions.
This is used instead of convertToShares so that it can round up for safer withdraws.
function previewWithdraw(uint256 assets) external view returns (uint256);
Parameters
Name | Type | Description |
---|---|---|
assets | uint256 | The amount of asset that would be withdrawn. |
Returns
Name | Type | Description |
---|---|---|
<none> | uint256 | . The amount of shares that would be burnt. |
previewRedeem​
Allows an on-chain or off-chain user to simulate the effects of their redemption at the current block, given current on-chain conditions.
This will round down.
function previewRedeem(uint256 shares) external view returns (uint256);
Parameters
Name | Type | Description |
---|---|---|
shares | uint256 | The amount of shares that would be redeemed. |
Returns
Name | Type | Description |
---|---|---|
<none> | uint256 | . The amount of asset that would be returned. |
maxDeposit​
Total number of underlying assets that can
be deposited by _owner
into the strategy, where owner
corresponds to the receiver of a deposit call.
function maxDeposit(address owner) external view returns (uint256);
Parameters
Name | Type | Description |
---|---|---|
owner | address | The address depositing. |
Returns
Name | Type | Description |
---|---|---|
<none> | uint256 | . The max that owner can deposit in asset . |
maxMint​
Total number of shares that can be minted by owner
into the strategy, where _owner
corresponds to the receiver
of a mint call.
function maxMint(address owner) external view returns (uint256);
Parameters
Name | Type | Description |
---|---|---|
owner | address | The address minting. |
Returns
Name | Type | Description |
---|---|---|
<none> | uint256 | _maxMint The max that owner can mint in shares. |
maxWithdraw​
Total number of underlying assets that can be
withdrawn from the strategy by owner
, where owner
corresponds to the msg.sender of a redeem call.
function maxWithdraw(address owner) external view returns (uint256);
Parameters
Name | Type | Description |
---|---|---|
owner | address | The owner of the shares. |
Returns
Name | Type | Description |
---|---|---|
<none> | uint256 | _maxWithdraw Max amount of asset that can be withdrawn. |
maxRedeem​
Total number of strategy shares that can be
redeemed from the strategy by owner
, where owner
corresponds to the msg.sender of a redeem call.
function maxRedeem(address owner) external view returns (uint256);
Parameters
Name | Type | Description |
---|---|---|
owner | address | The owner of the shares. |
Returns
Name | Type | Description |
---|---|---|
<none> | uint256 | _maxRedeem Max amount of shares that can be redeemed. |
_totalAssets​
Internal implementation of totalAssets.
function _totalAssets(StrategyData storage S) internal view returns (uint256);
_totalSupply​
Internal implementation of totalSupply.
function _totalSupply(StrategyData storage S) internal view returns (uint256);
_convertToShares​
Internal implementation of convertToShares.
function _convertToShares(StrategyData storage S, uint256 assets, Math.Rounding _rounding)
internal
view
returns (uint256);
_convertToAssets​
Internal implementation of convertToAssets.
function _convertToAssets(StrategyData storage S, uint256 shares, Math.Rounding _rounding)
internal
view
returns (uint256);
_maxDeposit​
Internal implementation of maxDeposit.
function _maxDeposit(StrategyData storage S, address owner) internal view returns (uint256);
_maxMint​
Internal implementation of maxMint.
function _maxMint(StrategyData storage S, address owner) internal view returns (uint256 maxMint_);
_maxWithdraw​
Internal implementation of maxWithdraw.
function _maxWithdraw(StrategyData storage S, address owner) internal view returns (uint256 maxWithdraw_);
_maxRedeem​
Internal implementation of maxRedeem.
function _maxRedeem(StrategyData storage S, address owner) internal view returns (uint256 maxRedeem_);
_deposit​
Function to be called during deposit and mint. This function handles all logic including transfers, minting and accounting. We do all external calls before updating any internal values to prevent view reentrancy issues from the token transfers or the _deployFunds() calls.
function _deposit(StrategyData storage S, address receiver, uint256 assets, uint256 shares) internal;
_withdraw​
To be called during redeem and withdraw. This will handle all logic, transfers and accounting in order to service the withdraw request. If we are not able to withdraw the full amount needed, it will be counted as a loss and passed on to the user.
function _withdraw(
StrategyData storage S,
address receiver,
address owner,
uint256 assets,
uint256 shares,
uint256 maxLoss
) internal returns (uint256);
report​
Function for keepers to call to harvest and record all profits accrued.
This will account for any gains/losses since the last report
and charge fees accordingly.
Any profit over the fees charged will be immediately locked
so there is no change in PricePerShare. Then slowly unlocked
over the maxProfitUnlockTime
each second based on the
calculated profitUnlockingRate
.
In case of a loss it will first attempt to offset the loss
with any remaining locked shares from the last report in
order to reduce any negative impact to PPS.
Will then recalculate the new time to unlock profits over and the
rate based on a weighted average of any remaining time from the
last report and the new amount of shares to be locked.
function report() external nonReentrant onlyKeepers returns (uint256 profit, uint256 loss);
Returns
Name | Type | Description |
---|---|---|
profit | uint256 | The notional amount of gain if any since the last report in terms of asset . |
loss | uint256 | The notional amount of loss if any since the last report in terms of asset . |
unlockedShares​
Get how many shares have been unlocked since last report.
function unlockedShares() external view returns (uint256);
Returns
Name | Type | Description |
---|---|---|
<none> | uint256 | . The amount of shares that have unlocked. |
_unlockedShares​
To determine how many of the shares that were locked during the last
report have since unlocked.
If the fullProfitUnlockDate
has passed the full strategy's balance will
count as unlocked.
function _unlockedShares(StrategyData storage S) internal view returns (uint256 unlocked);
Returns
Name | Type | Description |
---|---|---|
unlocked | uint256 | The amount of shares that have unlocked. |
tend​
For a 'keeper' to 'tend' the strategy if a custom tendTrigger() is implemented.
Both 'tendTrigger' and '_tend' will need to be overridden for this to be used. This will callback the internal '_tend' call in the BaseStrategy with the total current amount available to the strategy to deploy. This is a permissioned function so if desired it could be used for illiquid or manipulatable strategies to compound rewards, perform maintenance or deposit/withdraw funds. This will not cause any change in PPS. Total assets will be the same before and after. A report() call will be needed to record any profits or losses.
function tend() external nonReentrant onlyKeepers;
shutdownStrategy​
Used to shutdown the strategy preventing any further deposits.
Can only be called by the current management
or emergencyAdmin
.
This will stop any new deposit or mint calls but will
not prevent withdraw or redeem. It will also still allow for
tend and report so that management can report any last losses
in an emergency as well as provide any maintenance to allow for full
withdraw.
This is a one way switch and can never be set back once shutdown.
function shutdownStrategy() external onlyEmergencyAuthorized;
emergencyWithdraw​
To manually withdraw funds from the yield source after a strategy has been shutdown.
This can only be called post shutdownStrategy.
This will never cause a change in PPS. Total assets will
be the same before and after.
A strategist will need to override the _emergencyWithdraw
function
in their strategy for this to work.
function emergencyWithdraw(uint256 amount) external nonReentrant onlyEmergencyAuthorized;
Parameters
Name | Type | Description |
---|---|---|
amount | uint256 | The amount of asset to attempt to free. |
asset​
Get the underlying asset for the strategy.
function asset() external view returns (address);
Returns
Name | Type | Description |
---|---|---|
<none> | address | . The underlying asset. |
apiVersion​
Get the API version for this TokenizedStrategy.
function apiVersion() external pure returns (string memory);
Returns
Name | Type | Description |
---|---|---|
<none> | string | . The API version for this TokenizedStrategy |
management​
Get the current address that controls the strategy.
function management() external view returns (address);
Returns
Name | Type | Description |
---|---|---|
<none> | address | . Address of management |
pendingManagement​
Get the current pending management address if any.
function pendingManagement() external view returns (address);
Returns
Name | Type | Description |
---|---|---|
<none> | address | . Address of pendingManagement |
keeper​
Get the current address that can call tend and report.
function keeper() external view returns (address);
Returns
Name | Type | Description |
---|---|---|
<none> | address | . Address of the keeper |