Vault Strategy
Strategy Definition
A vault strategy is defined by two lists of strateg blocks. A strategy setup in a StrategVault contract is defined by the setStrat
function. All these parameters determine the vault executions during its operations.
function setStrat(
address[] positionManagers,
address[] stratBlocks,
bytes[] stratBlocksParameters,
bool[] isFinalBlock,
address[] harvestBlocks,
bytes[] harvestBlocksParameters
) external;
positionManagers: List of position managers owned by the vault. These addresses have the ability to call a whitelisted function during a position manager rebalance operation.
stratBlocks: Ordered list of strategy block addresses executed during strategy execution.
stratBlocksParameters: Ordered list of bytes containing encoded parameters of the block in the same index as the one in the stratBlocks list.
isFinalBlock: Ordered list of boolean to set a block in the stratBlocks as the last block. This function is only used in the event of a strategy exit. When a block is a final block, it receives the current exit percentage, otherwise it receives 100% as exit percentage.
harvestBlocks: Ordered list of harvest block addresses executed during harvest execution.
harvestBlocksParameters: Ordered list of bytes containing encoded parameters of block in the same index in harvestBlocks list
Internal Oracle System
When a StrategVault contract needs to compute the TVL of its assets (e.g deposit/withdraw), it does so by calling the oracleExit functions of the blocks, from last to the first one, as follows.
/**
* @dev Internal function to get the native TVL (Total Value Locked) of the vault.
* @return The native TVL of the vault.
*/
function _getNativeTVL() internal view returns (uint256) {
address _asset = asset();
DataTypes.OracleState memory oracleState;
oracleState.vault = address(this);
uint256 _strategyBlocksLength = strategyBlocksLength;
if (_strategyBlocksLength == 0 || !isLive) {
return IERC20(_asset).balanceOf(address(this)) + IERC20(_asset).allowance(buffer, address(this));
} else if (_strategyBlocksLength == 1) {
oracleState =
IStrategStrategyBlock(strategyBlocks[0]).oracleExit(
oracleState,
LibBlock.getStrategyStorageByIndex(0),
10000
);
} else {
uint256 revertedIndex = _strategyBlocksLength - 1;
for (uint256 i = 0; i < _strategyBlocksLength; i++) {
uint256 index = revertedIndex - i;
oracleState = IStrategStrategyBlock(strategyBlocks[index]).oracleExit(
oracleState, LibBlock.getStrategyStorageByIndex(index), 10000
);
}
}
return oracleState.findTokenAmount(_asset) + IERC20(_asset).balanceOf(address(this))
+ IERC20(_asset).allowance(buffer, address(this));
}
By exemple:
Here the call to oracle exit of Aura block will provide the number of Balancer LP Token which will be returned by a withdraw. This state is passed to the Balancer Block which will determine the number of WETH which will be returned based on the previous block state
Dynamic Parameters
Vault strategy can be operated with dynamic parameters that adjust their behavior based on external inputs. These parameters can be passed during operations (e.g rebalancing/harvesting) to adapt the strategy to current market conditions or specific operational needs. There are different types of dynamic parameters.
Portal Swap
Dynamic swap parameters can be used in blocks to define a token swap requirement via the Strateg Portal Module. The block can provide a token swap operation dynamically during the execution of a block. These parameters can be adjusted based on the current state, enabling the block to perform optimized token swaps based on current market conditions or specific block requirements.
/// @notice Enum representing the different types of swap value
enum SwapValueType {
INPUT_STRICT_VALUE,
INPUT_PERCENT_VALUE,
OUTPUT_STRICT_VALUE
}
/// @notice Struct representing the dynamic swap parameters
/// @param fromToken The address of the token to swap from
/// @param toToken The address of the token to swap to
/// @param value The amount of tokens to swap
/// @param valueType The type of value to swap
struct DynamicSwapParams {
address fromToken;
address toToken;
uint256 value;
SwapValueType valueType;
}
/// @notice Struct representing the dynamic swap data
/// @param route The route to use for the swap
/// @param sourceAsset The address of the asset to swap from
/// @param approvalAddress The address to approve for the swap
/// @param targetAsset The address of the asset to swap to
/// @param amount The amount of tokens to swap
/// @param data The data to send to the swap
struct DynamicSwapData {
uint8 route;
address sourceAsset;
address approvalAddress;
address targetAsset;
uint256 amount;
bytes data;
}
SwapValueType
INPUT_STRICT_VALUE
INPUT_PERCENT_VALUE
OUTPUT_STRICT_VALUE
DynamicSwapParams
fromToken
toToken
value
SwapValueType
DynamicSwapData
route
sourceAsset
approvalAddress
targetAsset
amount
data
For exemple, if the block need to execute a swap, it must return the following data on the dynamicParamsInfo
is called:
return true, DataTypes.DynamicParamsType.PORTAL_SWAP, DynamicSwapParams({
fromToken: <tokenIn address>,
toToken: <tokenOut address>,
value: <amount>,
valueType: <Type of the value provided>
})
Static call
This dynamic parameters will ask to the operator to request an eth_call
on the EVM RPC on the provided address with the specified data. It can be useful if the block need a protection against market manipulation by comparing call data with the data during the execution.
/// @notice Struct representing the static call parameters
/// @param to The address of the contract to call
/// @param data The data to send to the contract
struct StaticCallParams {
address to;
bytes data;
}
Merkle
This dynamic parameters doesn't need to return data on dynamicParamsInfo
call. The operators will call the merkle API to check if the vault address have pending rewards and provided merkle proof to claim them to the block.
From dApp creation graph to vault strategy
As exemple, we will use the following strategy:
From this graph, there is logics applied to generate the strategy payload:
Edges loop removal: it remove every edges which is returning on a block already included is the strategy
Strategy splitting: split strategy and harvest block in two groups
Number formating: convert number inputs to big numbers
Percent formating: format percent and adapt them to be apply correctly in the strategy Exemple: a 33% / 33% / 33% fork will be result as 33% / 50% / 100%
Set each block without output edges as final.
So in this exemple, the list of strategy block will be composed by:
Strategy enter execution:
Aave v3 Deposit with 50% of WETH available
Balancer Deposit with 100% of remaining WETH
Deposit 100% of Balancer LP in Aura
Strategy exit execution of x percent:
Withdraw x% of Balancer LP deposited in Aura
Withdraw 100% of Balancer LP available
Withdraw x% of WETH availble in Aave v3
Harvest execution:
Claim Aura rewards (Aura + Bal)
Swap 100% of available Bal to WETH
Swap 100% of available Aura to WETH
Last updated