diff --git a/chains/SonicLib.sol b/chains/SonicLib.sol index dcb49d15..7fd85881 100644 --- a/chains/SonicLib.sol +++ b/chains/SonicLib.sol @@ -29,6 +29,7 @@ import {ISiloIncentivesController} from "../src/integrations/silo/ISiloIncentive import {IGaugeV3} from "../src/integrations/shadow/IGaugeV3.sol"; import {ALMShadowFarmStrategy} from "../src/strategies/ALMShadowFarmStrategy.sol"; import {SiloLeverageStrategy} from "../src/strategies/SiloLeverageStrategy.sol"; +import {SiloAdvancedLeverageStrategy} from "../src/strategies/SiloAdvancedLeverageStrategy.sol"; /// @dev Sonic network [chainId: 146] data library // _____ _ @@ -81,6 +82,7 @@ library SonicLib { address public constant TOKEN_wstkscUSD = 0x9fb76f7ce5FCeAA2C42887ff441D46095E494206; address public constant TOKEN_stkscETH = 0x455d5f11Fea33A8fa9D3e285930b478B6bF85265; address public constant TOKEN_wstkscETH = 0xE8a41c62BB4d5863C6eadC96792cFE90A1f37C47; + address public constant TOKEN_wOS = 0x9F0dF7799f6FDAd409300080cfF680f5A23df4b1; // AMMs address public constant POOL_BEETS_wS_stS = 0x374641076B68371e69D03C417DAc3E5F236c32FA; @@ -238,7 +240,11 @@ library SonicLib { address public constant SILO_VAULT_3_stS = 0x396922EF30Cf012973343f7174db850c7D265278; address public constant SILO_VAULT_3_wS = 0x47d8490Be37ADC7Af053322d6d779153689E13C1; address public constant SILO_VAULT_23_wstkscUSD = 0x4E09FF794D255a123b00efa30162667A8054a845; + address public constant SILO_VAULT_23_USDC = 0x5954ce6671d97D24B782920ddCdBB4b1E63aB2De; address public constant SILO_VAULT_26_wstkscETH = 0xE8e1A980a7fc8D47D337d704FA73FBb81eE55C25; + address public constant SILO_VAULT_26_wETH = 0x219656F33c58488D09d518BaDF50AA8CdCAcA2Aa; + address public constant SILO_VAULT_22_wOS = 0x1d7E3726aFEc5088e11438258193A199F9D5Ba93; + address public constant SILO_VAULT_22_wS = 0x112380065A2cb73A5A429d9Ba7368cc5e8434595; // Gamma address public constant GAMMA_UNISWAPV3_UNIPROXY = 0xcD5A60eb030300661cAf97244aE98e1D5A70f2c8; @@ -317,6 +323,7 @@ library SonicLib { .setupHelpers(BEETS_BALANCER_HELPERS); DeployAdapterLib.deployAmmAdapter(platform, AmmAdapterIdLib.SOLIDLY); DeployAdapterLib.deployAmmAdapter(platform, AmmAdapterIdLib.ALGEBRA_V4); + DeployAdapterLib.deployAmmAdapter(platform, AmmAdapterIdLib.ERC_4626); LogDeployLib.logDeployAmmAdapters(platform, showLog); //endregion @@ -359,6 +366,15 @@ library SonicLib { p.initNums[0] = 1; p.initTicks = new int24[](0); factory.setStrategyAvailableInitParams(StrategyIdLib.SILO_LEVERAGE, p); + p.initAddresses = new address[](4); + p.initAddresses[0] = SILO_VAULT_23_wstkscUSD; + p.initAddresses[1] = SILO_VAULT_23_USDC; + p.initAddresses[2] = BEETS_VAULT; + p.initAddresses[3] = SILO_LENS; + p.initNums = new uint[](1); + p.initNums[0] = 87_00; + p.initTicks = new int24[](0); + factory.setStrategyAvailableInitParams(StrategyIdLib.SILO_ADVANCED_LEVERAGE, p); //endregion -- Add strategy available init params ----- //region ----- Deploy strategy logics ----- @@ -371,6 +387,7 @@ library SonicLib { _addStrategyLogic(factory, StrategyIdLib.SILO_FARM, address(new SiloFarmStrategy()), true); _addStrategyLogic(factory, StrategyIdLib.ALM_SHADOW_FARM, address(new ALMShadowFarmStrategy()), true); _addStrategyLogic(factory, StrategyIdLib.SILO_LEVERAGE, address(new SiloLeverageStrategy()), false); + _addStrategyLogic(factory, StrategyIdLib.SILO_ADVANCED_LEVERAGE, address(new SiloAdvancedLeverageStrategy()), false); LogDeployLib.logDeployStrategies(platform, showLog); //endregion @@ -387,17 +404,23 @@ library SonicLib { returns (ISwapper.AddPoolData[] memory bcPools, ISwapper.AddPoolData[] memory pools) { //region ----- BC pools ---- - bcPools = new ISwapper.AddPoolData[](2); + bcPools = new ISwapper.AddPoolData[](4); //bcPools[0] = _makePoolData(POOL_BEETS_wS_stS, AmmAdapterIdLib.BALANCER_COMPOSABLE_STABLE, TOKEN_stS, TOKEN_wS); //bcPools[0] = _makePoolData(POOL_EQUALIZER_wS_stS, AmmAdapterIdLib.SOLIDLY, TOKEN_stS, TOKEN_wS); bcPools[0] = _makePoolData(POOL_SWAPX_CL_wS_stS, AmmAdapterIdLib.ALGEBRA_V4, TOKEN_stS, TOKEN_wS); // bcPools[1] = _makePoolData(POOL_BEETS_wS_USDC, AmmAdapterIdLib.BALANCER_WEIGHTED, TOKEN_USDC, TOKEN_wS); // bcPools[1] = _makePoolData(POOL_SUSHI_wS_USDC, AmmAdapterIdLib.UNISWAPV3, TOKEN_USDC, TOKEN_wS); bcPools[1] = _makePoolData(POOL_EQUALIZER_wS_USDC, AmmAdapterIdLib.SOLIDLY, TOKEN_USDC, TOKEN_wS); + bcPools[2] = _makePoolData( + POOL_SHADOW_CL_USDC_scUSD_100, AmmAdapterIdLib.UNISWAPV3, TOKEN_scUSD, TOKEN_USDC + ); + bcPools[3] = _makePoolData( + POOL_SHADOW_CL_scETH_WETH_100, AmmAdapterIdLib.UNISWAPV3, TOKEN_scETH, TOKEN_wETH + ); //endregion ----- BC pools ---- //region ----- Pools ---- - pools = new ISwapper.AddPoolData[](15); + pools = new ISwapper.AddPoolData[](21); uint i; //pools[i++] = _makePoolData(POOL_BEETS_wS_stS, AmmAdapterIdLib.BALANCER_COMPOSABLE_STABLE, TOKEN_wS, TOKEN_stS); //pools[i++] = _makePoolData(POOL_EQUALIZER_wS_stS, AmmAdapterIdLib.SOLIDLY, TOKEN_wS, TOKEN_stS); @@ -420,6 +443,32 @@ library SonicLib { pools[i++] = _makePoolData(POOL_SWAPX_AUR_auUSDC, AmmAdapterIdLib.SOLIDLY, TOKEN_auUSDC, TOKEN_AUR); pools[i++] = _makePoolData(POOL_SHADOW_wS_SHADOW, AmmAdapterIdLib.SOLIDLY, TOKEN_SHADOW, TOKEN_wS); pools[i++] = _makePoolData(POOL_SHADOW_CL_wS_BRUSH_5000, AmmAdapterIdLib.UNISWAPV3, TOKEN_BRUSH, TOKEN_wS); + pools[i++] = _makePoolData( + TOKEN_wstkscUSD, AmmAdapterIdLib.ERC_4626, TOKEN_wstkscUSD, TOKEN_stkscUSD + ); + pools[i++] = _makePoolData( + POOL_SHADOW_CL_stkscUSD_scUSD_3000, + AmmAdapterIdLib.UNISWAPV3, + TOKEN_stkscUSD, + TOKEN_scUSD + ); + pools[i++] = _makePoolData( + TOKEN_wstkscETH, AmmAdapterIdLib.ERC_4626, TOKEN_wstkscETH, TOKEN_stkscETH + ); + pools[i++] = _makePoolData( + POOL_SHADOW_CL_scETH_stkscETH_250, + AmmAdapterIdLib.UNISWAPV3, + TOKEN_stkscETH, + TOKEN_scETH + ); + + pools[i++] = _makePoolData( + POOL_SWAPX_CL_wS_OS, AmmAdapterIdLib.ALGEBRA_V4, TOKEN_OS, TOKEN_wS + ); + pools[i++] = _makePoolData( + TOKEN_wOS, AmmAdapterIdLib.ERC_4626, TOKEN_wOS, TOKEN_OS + ); + //endregion ----- Pools ---- } diff --git a/script/deploy-adapter/ERC4626.Sonic.s.sol b/script/deploy-adapter/ERC4626.Sonic.s.sol new file mode 100644 index 00000000..95c83751 --- /dev/null +++ b/script/deploy-adapter/ERC4626.Sonic.s.sol @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.23; + +import {Script} from "forge-std/Script.sol"; +import {Proxy} from "../../src/core/proxy/Proxy.sol"; +import {ERC4626Adapter} from "../../src/adapters/ERC4626Adapter.sol"; + +contract DeployERC4626AdapterSonic is Script { + address public constant PLATFORM = 0x4Aca671A420eEB58ecafE83700686a2AD06b20D8; + + function run() external { + uint deployerPrivateKey = vm.envUint("PRIVATE_KEY"); + vm.startBroadcast(deployerPrivateKey); + + Proxy proxy = new Proxy(); + proxy.initProxy(address(new ERC4626Adapter())); + ERC4626Adapter(address(proxy)).init(PLATFORM); + + vm.stopBroadcast(); + } + + function testDeployAdapter() external {} +} diff --git a/script/deploy-strategy/SiAL.s.sol b/script/deploy-strategy/SiAL.s.sol new file mode 100644 index 00000000..6e2969e8 --- /dev/null +++ b/script/deploy-strategy/SiAL.s.sol @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.23; + +import {Script} from "forge-std/Script.sol"; +import {SiloAdvancedLeverageStrategy} from "../../src/strategies/SiloAdvancedLeverageStrategy.sol"; + +contract DeploySiAL is Script { + function run() external { + uint deployerPrivateKey = vm.envUint("PRIVATE_KEY"); + vm.startBroadcast(deployerPrivateKey); + new SiloAdvancedLeverageStrategy(); + vm.stopBroadcast(); + } + + function testDeployStrategy() external {} +} diff --git a/script/libs/DeployAdapterLib.sol b/script/libs/DeployAdapterLib.sol index 97175504..a9b3bcc1 100644 --- a/script/libs/DeployAdapterLib.sol +++ b/script/libs/DeployAdapterLib.sol @@ -12,6 +12,7 @@ import {BalancerComposableStableAdapter} from "../../src/adapters/BalancerCompos import {BalancerWeightedAdapter} from "../../src/adapters/BalancerWeightedAdapter.sol"; import {SolidlyAdapter} from "../../src/adapters/SolidlyAdapter.sol"; import {AlgebraV4Adapter} from "../../src/adapters/AlgebraV4Adapter.sol"; +import {ERC4626Adapter} from "../../src/adapters/ERC4626Adapter.sol"; library DeployAdapterLib { function deployAmmAdapter(address platform, string memory id) internal returns (address) { @@ -54,6 +55,10 @@ library DeployAdapterLib { proxy.initProxy(address(new AlgebraV4Adapter())); } + if (eq(id, AmmAdapterIdLib.ERC_4626)) { + proxy.initProxy(address(new ERC4626Adapter())); + } + require(proxy.implementation() != address(0), string.concat("Unknown AmmAdapter:", id)); IAmmAdapter(address(proxy)).init(platform); IPlatform(platform).addAmmAdapter(id, address(proxy)); diff --git a/src/interfaces/ILeverageLendingStrategy.sol b/src/interfaces/ILeverageLendingStrategy.sol index f1389034..bc2dd124 100644 --- a/src/interfaces/ILeverageLendingStrategy.sol +++ b/src/interfaces/ILeverageLendingStrategy.sol @@ -11,6 +11,7 @@ interface ILeverageLendingStrategy { ); event LeverageLendingHealth(uint ltv, uint leverage); event TargetLeveragePercent(uint value); + event UniversalParams(uint[] params); /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* DATA TYPES */ @@ -32,6 +33,22 @@ interface ILeverageLendingStrategy { // configurable params /// @dev Percent of max leverage. 90_00 is 90%. uint targetLeveragePercent; + /// @dev Universal configurable param 0 for depositAssets + uint depositParam0; + /// @dev Universal configurable param 1 for depositAssets + uint depositParam1; + /// @dev Universal configurable param 0 for withdrawAssets + uint withdrawParam0; + /// @dev Universal configurable param 1 for withdrawAssets + uint withdrawParam1; + /// @dev Universal configurable param 0 for increase LTV + uint increaseLtvParam0; + /// @dev Universal configurable param 1 for increase LTV + uint increaseLtvParam1; + /// @dev Universal configurable param 0 for decrease LTV + uint decreaseLtvParam0; + /// @dev Universal configurable param 1 for decrease LTV + uint decreaseLtvParam1; } struct LeverageLendingStrategyBaseInitParams { @@ -44,6 +61,7 @@ interface ILeverageLendingStrategy { address borrowingVault; address flashLoanVault; address helper; + uint targetLeveragePercent; } struct LeverageLendingAddresses { @@ -74,6 +92,9 @@ interface ILeverageLendingStrategy { /// @param value Value with 4 decimals, 90_00 is 90%. function setTargetLeveragePercent(uint value) external; + /// @notice Change universal configurable params + function setUniversalParams(uint[] memory params) external; + /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* VIEW FUNCTIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ diff --git a/src/interfaces/ITeller.sol b/src/interfaces/ITeller.sol new file mode 100644 index 00000000..f5931514 --- /dev/null +++ b/src/interfaces/ITeller.sol @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.23; + +interface ITeller { + function deposit(address depositAsset, uint depositAmount, uint minimumMint) external payable returns (uint shares); + +} diff --git a/src/strategies/SiloAdvancedLeverageStrategy.sol b/src/strategies/SiloAdvancedLeverageStrategy.sol new file mode 100644 index 00000000..acd07427 --- /dev/null +++ b/src/strategies/SiloAdvancedLeverageStrategy.sol @@ -0,0 +1,436 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.23; + +import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; +import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; +import {IERC4626} from "@openzeppelin/contracts/interfaces/IERC4626.sol"; +import {Math} from "@openzeppelin/contracts/utils/math/Math.sol"; +import {CommonLib} from "../core/libs/CommonLib.sol"; +import {ConstantsLib} from "../core/libs/ConstantsLib.sol"; +import {StrategyBase} from "./base/StrategyBase.sol"; +import {LeverageLendingBase} from "./base/LeverageLendingBase.sol"; +import {StrategyIdLib} from "./libs/StrategyIdLib.sol"; +import {StrategyLib} from "./libs/StrategyLib.sol"; +import {SiloAdvancedLib} from "./libs/SiloAdvancedLib.sol"; +import {IStrategy} from "../interfaces/IStrategy.sol"; +import {IControllable} from "../interfaces/IControllable.sol"; +import {IFactory} from "../interfaces/IFactory.sol"; +import {IPlatform} from "../interfaces/IPlatform.sol"; +import {ILeverageLendingStrategy} from "../interfaces/ILeverageLendingStrategy.sol"; +import {ISilo} from "../integrations/silo/ISilo.sol"; +import {ISiloConfig} from "../integrations/silo/ISiloConfig.sol"; +import {ISiloLens} from "../integrations/silo/ISiloLens.sol"; +import {IFlashLoanRecipient} from "../integrations/balancer/IFlashLoanRecipient.sol"; +import {IBVault} from "../integrations/balancer/IBVault.sol"; + +/// @title Silo V2 advanced leverage strategy +/// @author Alien Deployer (https://github.com/a17) +contract SiloAdvancedLeverageStrategy is LeverageLendingBase, IFlashLoanRecipient { + using SafeERC20 for IERC20; + + /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ + /* CONSTANTS */ + /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ + + /// @inheritdoc IControllable + string public constant VERSION = "1.0.0"; + + /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ + /* INITIALIZATION */ + /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ + + /// @inheritdoc IStrategy + function initialize(address[] memory addresses, uint[] memory nums, int24[] memory ticks) public initializer { + if (addresses.length != 6 || nums.length != 1 || ticks.length != 0) { + revert IControllable.IncorrectInitParams(); + } + + LeverageLendingStrategyBaseInitParams memory params; + params.platform = addresses[0]; + params.vault = addresses[1]; + params.collateralAsset = IERC4626(addresses[2]).asset(); + params.borrowAsset = IERC4626(addresses[3]).asset(); + params.lendingVault = addresses[2]; + params.borrowingVault = addresses[3]; + params.flashLoanVault = addresses[4]; + params.helper = addresses[5]; + params.targetLeveragePercent = nums[0]; + __LeverageLendingBase_init(params); + + IERC20(params.collateralAsset).forceApprove(params.lendingVault, type(uint).max); + IERC20(params.borrowAsset).forceApprove(params.borrowingVault, type(uint).max); + address swapper = IPlatform(params.platform).swapper(); + IERC20(params.collateralAsset).forceApprove(swapper, type(uint).max); + IERC20(params.borrowAsset).forceApprove(swapper, type(uint).max); + + LeverageLendingBaseStorage storage $ = _getLeverageLendingBaseStorage(); + // Multiplier of flash amount for borrow on deposit. Default is 100_30 == 100.3%. + $.depositParam0 = 100_30; + // Multiplier of debt diff + $.increaseLtvParam0 = 100_80; + // Multiplier of swap borrow asset to collateral in flash loan callback + $.increaseLtvParam1 = 99_00; + // Multiplier of collateral diff + $.decreaseLtvParam0 = 101_00; + } + + /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ + /* CALLBACKS */ + /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ + + /// @inheritdoc IFlashLoanRecipient + function receiveFlashLoan( + address[] memory tokens, + uint[] memory amounts, + uint[] memory feeAmounts, + bytes memory /*userData*/ + ) external { + // Flash loan is performed upon deposit and withdrawal + LeverageLendingBaseStorage storage $ = _getLeverageLendingBaseStorage(); + SiloAdvancedLib.receiveFlashLoan(platform(), $, tokens[0], amounts[0], feeAmounts[0]); + } + + /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ + /* VIEW FUNCTIONS */ + /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ + + /// @inheritdoc IStrategy + function isHardWorkOnDepositAllowed() external pure returns (bool) { + return true; + } + + /// @inheritdoc IStrategy + function strategyLogicId() public pure override returns (string memory) { + return StrategyIdLib.SILO_ADVANCED_LEVERAGE; + } + + /// @inheritdoc IStrategy + function initVariants(address platform_) + public + view + returns (string[] memory variants, address[] memory addresses, uint[] memory nums, int24[] memory ticks) + { + IFactory.StrategyAvailableInitParams memory params = + IFactory(IPlatform(platform_).factory()).strategyAvailableInitParams(keccak256(bytes(strategyLogicId()))); + uint len = params.initAddresses.length / 4; + variants = new string[](len); + addresses = new address[](len * 4); + nums = new uint[](0); + ticks = new int24[](0); + for (uint i; i < len; ++i) { + address collateralAsset = IERC4626(params.initAddresses[i * 2]).asset(); + address borrowAsset = IERC4626(params.initAddresses[i * 2 + 1]).asset(); + variants[i] = _generateDescription(params.initAddresses[i * 2], collateralAsset, borrowAsset); + addresses[i * 2] = params.initAddresses[i * 2]; + addresses[i * 2 + 1] = params.initAddresses[i * 2 + 1]; + addresses[i * 2 + 2] = params.initAddresses[i * 2 + 2]; + addresses[i * 2 + 3] = params.initAddresses[i * 2 + 3]; + } + } + + /// @inheritdoc IStrategy + function description() external view returns (string memory) { + LeverageLendingBaseStorage storage $ = _getLeverageLendingBaseStorage(); + return _generateDescription($.lendingVault, $.collateralAsset, $.borrowAsset); + } + + /// @inheritdoc IStrategy + function getSpecificName() external view override returns (string memory, bool) { + LeverageLendingBaseStorage storage $ = _getLeverageLendingBaseStorage(); + address lendingVault = $.lendingVault; + uint siloId = ISiloConfig(ISilo(lendingVault).config()).SILO_ID(); + string memory borrowAssetSymbol = IERC20Metadata($.borrowAsset).symbol(); + (,, uint targetLeverage) = SiloAdvancedLib.getLtvData(lendingVault, $.targetLeveragePercent); + return ( + string.concat(CommonLib.u2s(siloId), " ", borrowAssetSymbol, " ", _formatLeverageShort(targetLeverage)), + false + ); + } + + /// @inheritdoc ILeverageLendingStrategy + function realTvl() public view returns (uint tvl, bool trusted) { + LeverageLendingBaseStorage storage $ = _getLeverageLendingBaseStorage(); + return SiloAdvancedLib.realTvl(platform(), $); + } + + /// @inheritdoc ILeverageLendingStrategy + function realSharePrice() public view returns (uint sharePrice, bool trusted) { + uint _realTvl; + (_realTvl, trusted) = realTvl(); + uint totalSupply = IERC20(vault()).totalSupply(); + if (totalSupply != 0) { + sharePrice = _realTvl * 1e18 / totalSupply; + } + } + + /// @inheritdoc ILeverageLendingStrategy + function health() + public + view + returns ( + uint ltv, + uint maxLtv, + uint leverage, + uint collateralAmount, + uint debtAmount, + uint targetLeveragePercent + ) + { + LeverageLendingBaseStorage storage $ = _getLeverageLendingBaseStorage(); + return SiloAdvancedLib.health(platform(), $); + } + + /// @inheritdoc ILeverageLendingStrategy + function getSupplyAndBorrowAprs() external view returns (uint supplyApr, uint borrowApr) { + LeverageLendingBaseStorage storage $ = _getLeverageLendingBaseStorage(); + return _getDepositAndBorrowAprs($.helper, $.lendingVault, $.borrowingVault); + } + + /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ + /* LEVERAGE LENDING BASE */ + /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ + + function _rebalanceDebt(uint newLtv) internal override returns (uint resultLtv) { + (uint ltv,,, uint collateralAmount, uint debtAmount,) = health(); + + LeverageLendingBaseStorage storage $ = _getLeverageLendingBaseStorage(); + LeverageLendingAddresses memory v = LeverageLendingAddresses({ + collateralAsset: $.collateralAsset, + borrowAsset: $.borrowAsset, + lendingVault: $.lendingVault, + borrowingVault: $.borrowingVault + }); + + uint tvlPricedInCollateralAsset = SiloAdvancedLib.calcTotal(v); + + // here is the math that works: + // collateral_value - debt_value = real_TVL + // debt_value * PRECISION / collateral_value = LTV + // --- + // collateral_value = real_TVL * PRECISION / (PRECISION - LTV) + + uint newCollateralValue = tvlPricedInCollateralAsset * INTERNAL_PRECISION / (INTERNAL_PRECISION - newLtv); + (uint priceCtoB,) = SiloAdvancedLib.getPrices(v.lendingVault, v.borrowingVault); + uint newDebtAmount = newCollateralValue * newLtv / INTERNAL_PRECISION * priceCtoB / 1e18; + address[] memory flashAssets = new address[](1); + flashAssets[0] = v.borrowAsset; + uint[] memory flashAmounts = new uint[](1); + + if (newLtv < ltv) { + // need decrease debt and collateral + $.tempAction = CurrentAction.DecreaseLtv; + + uint debtDiff = debtAmount - newDebtAmount; + flashAmounts[0] = debtDiff; + + $.tempCollateralAmount = (collateralAmount - newCollateralValue) * $.decreaseLtvParam0 / INTERNAL_PRECISION; + } else { + // need increase debt and collateral + $.tempAction = CurrentAction.IncreaseLtv; + + uint debtDiff = newDebtAmount - debtAmount; + + $.tempBorrowAmount = debtDiff; + flashAmounts[0] = debtDiff * $.increaseLtvParam0 / INTERNAL_PRECISION; + } + + IBVault($.flashLoanVault).flashLoan(address(this), flashAssets, flashAmounts, ""); + + $.tempAction = CurrentAction.None; + (resultLtv,,,,,) = health(); + } + + /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ + /* STRATEGY BASE */ + /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ + + /// @inheritdoc StrategyBase + function _assetsAmounts() internal view override returns (address[] memory assets_, uint[] memory amounts_) { + assets_ = assets(); + amounts_ = new uint[](1); + LeverageLendingBaseStorage storage $ = _getLeverageLendingBaseStorage(); + amounts_[0] = SiloAdvancedLib.totalCollateral($.lendingVault); + } + + /// @inheritdoc StrategyBase + function _claimRevenue() + internal + override + returns ( + address[] memory __assets, + uint[] memory __amounts, + address[] memory __rewardAssets, + uint[] memory __rewardAmounts + ) + { + __assets = assets(); + __rewardAssets = new address[](0); + __rewardAmounts = new uint[](0); + __amounts = new uint[](1); + + LeverageLendingBaseStorage storage $ = _getLeverageLendingBaseStorage(); + LeverageLendingAddresses memory v = LeverageLendingAddresses({ + collateralAsset: $.collateralAsset, + borrowAsset: $.borrowAsset, + lendingVault: $.lendingVault, + borrowingVault: $.borrowingVault + }); + StrategyBaseStorage storage $base = _getStrategyBaseStorage(); + + uint totalWas = $base.total; + + ISilo(v.lendingVault).accrueInterest(); + ISilo(v.borrowingVault).accrueInterest(); + + uint totalNow = StrategyLib.balance(v.collateralAsset) + SiloAdvancedLib.calcTotal(v); + if (totalNow > totalWas) { + __amounts[0] = totalNow - totalWas; + } + $base.total = totalNow; + + { + int earned = int(totalNow) - int(totalWas); + (uint _realTvl,) = realTvl(); + uint duration = block.timestamp - $base.lastHardWork; + int realApr = StrategyLib.computeAprInt(_realTvl, earned, duration); + (uint depositApr, uint borrowApr) = _getDepositAndBorrowAprs($.helper, v.lendingVault, v.borrowingVault); + (uint sharePrice,) = realSharePrice(); + emit LeverageLendingHardWork(realApr, earned, _realTvl, duration, sharePrice, depositApr, borrowApr); + } + + (uint ltv,, uint leverage,,,) = health(); + emit LeverageLendingHealth(ltv, leverage); + } + + /// @inheritdoc StrategyBase + function _previewDepositAssets(uint[] memory amountsMax) + internal + pure + override(StrategyBase) + returns (uint[] memory amountsConsumed, uint value) + { + amountsConsumed = new uint[](1); + amountsConsumed[0] = amountsMax[0]; + value = amountsConsumed[0]; + } + + /// @inheritdoc StrategyBase + function _depositAssets(uint[] memory amounts, bool /*claimRevenue*/ ) internal override returns (uint value) { + LeverageLendingBaseStorage storage $ = _getLeverageLendingBaseStorage(); + $.tempAction = CurrentAction.Deposit; + LeverageLendingAddresses memory v = LeverageLendingAddresses({ + collateralAsset: $.collateralAsset, + borrowAsset: $.borrowAsset, + lendingVault: $.lendingVault, + borrowingVault: $.borrowingVault + }); + address[] memory _assets = assets(); + uint valueWas = StrategyLib.balance(_assets[0]) + SiloAdvancedLib.calcTotal(v); + + (,, uint targetLeverage) = SiloAdvancedLib.getLtvData(v.lendingVault, $.targetLeveragePercent); + + address[] memory flashAssets = new address[](1); + flashAssets[0] = v.borrowAsset; + uint[] memory flashAmounts = new uint[](1); + flashAmounts[0] = amounts[0] * targetLeverage / INTERNAL_PRECISION; + + $.tempBorrowAmount = flashAmounts[0] * $.depositParam0 / INTERNAL_PRECISION; + + IBVault($.flashLoanVault).flashLoan(address(this), flashAssets, flashAmounts, ""); + + uint valueNow = StrategyLib.balance(_assets[0]) + SiloAdvancedLib.calcTotal(v); + + if (valueNow > valueWas) { + //console.log('deposit profit', valueNow - valueWas); + value = amounts[0] + (valueNow - valueWas); + } else { + //console.log('deposit loss', valueWas - valueNow); + value = amounts[0] - (valueWas - valueNow); + } + + StrategyBaseStorage storage $base = _getStrategyBaseStorage(); + $base.total += value; + } + + /// @inheritdoc StrategyBase + function _withdrawAssets(uint value, address receiver) internal override returns (uint[] memory amountsOut) { + LeverageLendingBaseStorage storage $ = _getLeverageLendingBaseStorage(); + $.tempAction = CurrentAction.Withdraw; + LeverageLendingAddresses memory v = LeverageLendingAddresses({ + collateralAsset: $.collateralAsset, + borrowAsset: $.borrowAsset, + lendingVault: $.lendingVault, + borrowingVault: $.borrowingVault + }); + amountsOut = new uint[](1); + + uint valueWas = StrategyLib.balance(v.collateralAsset) + SiloAdvancedLib.calcTotal(v); + + (uint maxLtv, uint maxLeverage,) = SiloAdvancedLib.getLtvData(v.lendingVault, $.targetLeveragePercent); + + (uint priceCtoB,) = SiloAdvancedLib.getPrices(v.lendingVault, v.borrowingVault); + + { + uint collateralAmountToWithdraw = value * maxLeverage / INTERNAL_PRECISION; + $.tempCollateralAmount = collateralAmountToWithdraw; + uint[] memory flashAmounts = new uint[](1); + flashAmounts[0] = (collateralAmountToWithdraw * maxLtv / 1e18) * priceCtoB / 1e18; + address[] memory flashAssets = new address[](1); + flashAssets[0] = $.borrowAsset; + IBVault($.flashLoanVault).flashLoan(address(this), flashAssets, flashAmounts, ""); + } + + uint valueNow = StrategyLib.balance(v.collateralAsset) + SiloAdvancedLib.calcTotal(v); + + uint bal = StrategyLib.balance(v.collateralAsset); + if (valueWas > valueNow) { + //console.log('withdraw loss', valueWas - valueNow); + amountsOut[0] = Math.min(value - (valueWas - valueNow), bal); + } else { + //console.log('withdraw profit', valueNow - valueWas); + amountsOut[0] = Math.min(value + (valueNow - valueWas), bal); + } + + IERC20(v.collateralAsset).safeTransfer(receiver, amountsOut[0]); + + StrategyBaseStorage storage $base = _getStrategyBaseStorage(); + $base.total -= value; + } + + /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ + /* INTERNAL LOGIC */ + /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ + + function _generateDescription( + address lendingVault, + address collateralAsset, + address borrowAsset + ) internal view returns (string memory) { + uint siloId = ISiloConfig(ISilo(lendingVault).config()).SILO_ID(); + return string.concat( + "Supply ", + IERC20Metadata(collateralAsset).symbol(), + " and borrow ", + IERC20Metadata(borrowAsset).symbol(), + " on Silo V2 market ", + CommonLib.u2s(siloId), + " with leverage looping" + ); + } + + function _formatLeverageShort(uint amount) internal pure returns (string memory) { + uint intAmount = amount / 100_00; + uint decimalAmount = (amount - intAmount * 100_00) / 10_00; + return string.concat("x", CommonLib.u2s(intAmount), ".", CommonLib.u2s(decimalAmount)); + } + + function _getDepositAndBorrowAprs( + address lens, + address lendingVault, + address debtVault + ) internal view returns (uint depositApr, uint borrowApr) { + depositApr = ISiloLens(lens).getDepositAPR(lendingVault) * ConstantsLib.DENOMINATOR / 1e18; + borrowApr = ISiloLens(lens).getBorrowAPR(debtVault) * ConstantsLib.DENOMINATOR / 1e18; + } +} diff --git a/src/strategies/SiloLeverageStrategy.sol b/src/strategies/SiloLeverageStrategy.sol index 081f2100..d4cb7dfe 100644 --- a/src/strategies/SiloLeverageStrategy.sol +++ b/src/strategies/SiloLeverageStrategy.sol @@ -25,6 +25,8 @@ import {IFlashLoanRecipient} from "../integrations/balancer/IFlashLoanRecipient. import {IBVault} from "../integrations/balancer/IBVault.sol"; /// @title Silo V2 leverage strategy +/// Changelog: +/// 1.1.0: use LeverageLendingBase 1.1.0 /// @author Alien Deployer (https://github.com/a17) contract SiloLeverageStrategy is LeverageLendingBase, IFlashLoanRecipient { using SafeERC20 for IERC20; @@ -34,7 +36,7 @@ contract SiloLeverageStrategy is LeverageLendingBase, IFlashLoanRecipient { /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @inheritdoc IControllable - string public constant VERSION = "1.0.0"; + string public constant VERSION = "1.1.0"; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* INITIALIZATION */ @@ -55,6 +57,7 @@ contract SiloLeverageStrategy is LeverageLendingBase, IFlashLoanRecipient { params.borrowingVault = addresses[3]; params.flashLoanVault = addresses[4]; params.helper = addresses[5]; + params.targetLeveragePercent = 87_00; __LeverageLendingBase_init(params); IERC20(params.collateralAsset).forceApprove(params.lendingVault, type(uint).max); diff --git a/src/strategies/base/LeverageLendingBase.sol b/src/strategies/base/LeverageLendingBase.sol index 21d65edc..1cb47399 100644 --- a/src/strategies/base/LeverageLendingBase.sol +++ b/src/strategies/base/LeverageLendingBase.sol @@ -11,13 +11,17 @@ import {IHardWorker} from "../../interfaces/IHardWorker.sol"; import {IPlatform} from "../../interfaces/IPlatform.sol"; import {IControllable} from "../../interfaces/IControllable.sol"; +/// @notice Base strategy for leverage lending +/// Changelog: +/// 1.1.0: targetLeveragePercent setup in strategy initializer; 8 universal configurable params +/// @author Alien Deployer (https://github.com/a17) abstract contract LeverageLendingBase is StrategyBase, ILeverageLendingStrategy { /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CONSTANTS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Version of FarmingStrategyBase implementation - string public constant VERSION_LEVERAGE_LENDING_STRATEGY_BASE = "1.0.0"; + string public constant VERSION_LEVERAGE_LENDING_STRATEGY_BASE = "1.1.0"; /// @dev 100_00 is 1.0 or 100% uint internal constant INTERNAL_PRECISION = 100_00; @@ -42,8 +46,8 @@ abstract contract LeverageLendingBase is StrategyBase, ILeverageLendingStrategy $.borrowingVault = params.borrowingVault; $.flashLoanVault = params.flashLoanVault; $.helper = params.helper; - $.targetLeveragePercent = 87_00; - emit TargetLeveragePercent(87_00); + $.targetLeveragePercent = params.targetLeveragePercent; + emit TargetLeveragePercent(params.targetLeveragePercent); address[] memory _assets = new address[](1); _assets[0] = params.collateralAsset; __StrategyBase_init(params.platform, params.strategyId, params.vault, _assets, address(0), type(uint).max); @@ -75,6 +79,20 @@ abstract contract LeverageLendingBase is StrategyBase, ILeverageLendingStrategy emit TargetLeveragePercent(value); } + /// @inheritdoc ILeverageLendingStrategy + function setUniversalParams(uint[] memory params) external onlyOperator { + LeverageLendingBaseStorage storage $ = _getLeverageLendingBaseStorage(); + $.depositParam0 = params[0]; + $.depositParam1 = params[1]; + $.withdrawParam0 = params[2]; + $.withdrawParam1 = params[3]; + $.increaseLtvParam0 = params[4]; + $.increaseLtvParam1 = params[5]; + $.decreaseLtvParam0 = params[6]; + $.decreaseLtvParam1 = params[7]; + emit UniversalParams(params); + } + /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* VIEW FUNCTIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ diff --git a/src/strategies/libs/SiloAdvancedLib.sol b/src/strategies/libs/SiloAdvancedLib.sol new file mode 100644 index 00000000..55a48709 --- /dev/null +++ b/src/strategies/libs/SiloAdvancedLib.sol @@ -0,0 +1,277 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.23; + +import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; +import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; +import {IERC4626} from "@openzeppelin/contracts/interfaces/IERC4626.sol"; +import {Math} from "@openzeppelin/contracts/utils/math/Math.sol"; +import {StrategyLib} from "./StrategyLib.sol"; +import {ISilo} from "../../integrations/silo/ISilo.sol"; +import {ISiloConfig} from "../../integrations/silo/ISiloConfig.sol"; +import {ISiloOracle} from "../../integrations/silo/ISiloOracle.sol"; +import {ISiloLens} from "../../integrations/silo/ISiloLens.sol"; +import {ILeverageLendingStrategy} from "../../interfaces/ILeverageLendingStrategy.sol"; +import {IPriceReader} from "../../interfaces/IPriceReader.sol"; +import {IPlatform} from "../../interfaces/IPlatform.sol"; +import {IControllable} from "../../interfaces/IControllable.sol"; +import {ITeller} from "../../interfaces/ITeller.sol"; + +library SiloAdvancedLib { + using SafeERC20 for IERC20; + + /// @dev 100_00 is 1.0 or 100% + uint public constant INTERNAL_PRECISION = 100_00; + + function receiveFlashLoan( + address platform, + ILeverageLendingStrategy.LeverageLendingBaseStorage storage $, + address token, + uint amount, + uint feeAmount + ) external { + // token is borrow asset (USDC/WETH/wS) + address collateralAsset = $.collateralAsset; + address flashLoanVault = $.flashLoanVault; + if (msg.sender != flashLoanVault) { + revert IControllable.IncorrectMsgSender(); + } + + if ($.tempAction == ILeverageLendingStrategy.CurrentAction.Deposit) { + uint tempBorrowAmount = $.tempBorrowAmount; + + // swap + StrategyLib.swap(platform, token, collateralAsset, amount); + + // supply + ISilo($.lendingVault).deposit( + IERC20(collateralAsset).balanceOf(address(this)), address(this), ISilo.CollateralType.Collateral + ); + + // borrow + ISilo($.borrowingVault).borrow(tempBorrowAmount, address(this), address(this)); + + // pay flash loan + IERC20(token).safeTransfer(flashLoanVault, amount + feeAmount); + + // repay remaining balance + ISilo($.borrowingVault).repay(StrategyLib.balance(token), address(this)); + + // reset temp vars + $.tempBorrowAmount = 0; + } + + if ($.tempAction == ILeverageLendingStrategy.CurrentAction.Withdraw) { + uint tempCollateralAmount = $.tempCollateralAmount; + + // repay debt + ISilo($.borrowingVault).repay(amount, address(this)); + + // withdraw + { + address lendingVault = $.lendingVault; + uint collateralAmountTotal = totalCollateral(lendingVault); + collateralAmountTotal -= collateralAmountTotal / 1000; + ISilo(lendingVault).withdraw( + Math.min(tempCollateralAmount, collateralAmountTotal), + address(this), + address(this), + ISilo.CollateralType.Collateral + ); + } + + // swap + StrategyLib.swap( + platform, collateralAsset, token, Math.min(tempCollateralAmount, StrategyLib.balance(collateralAsset)) + ); + + // pay flash loan + IERC20(token).safeTransfer(flashLoanVault, amount + feeAmount); + + // swap unnecessary borrow asset + StrategyLib.swap(platform, token, collateralAsset, StrategyLib.balance(token)); + + // reset temp vars + $.tempCollateralAmount = 0; + } + + if ($.tempAction == ILeverageLendingStrategy.CurrentAction.DecreaseLtv) { + address lendingVault = $.lendingVault; + + // repay + ISilo($.borrowingVault).repay(StrategyLib.balance(token), address(this)); + + // withdraw amount + ISilo(lendingVault).withdraw( + $.tempCollateralAmount, address(this), address(this), ISilo.CollateralType.Collateral + ); + + // swap + StrategyLib.swap(platform, collateralAsset, token, $.tempCollateralAmount); + + // pay flash loan + IERC20(token).safeTransfer(flashLoanVault, amount + feeAmount); + + // repay remaining balance + ISilo($.borrowingVault).repay(StrategyLib.balance(token), address(this)); + + $.tempCollateralAmount = 0; + } + + if ($.tempAction == ILeverageLendingStrategy.CurrentAction.IncreaseLtv) { + uint tempBorrowAmount = $.tempBorrowAmount; + + // swap + StrategyLib.swap( + platform, + token, + collateralAsset, + IERC20(token).balanceOf(address(this)) * $.increaseLtvParam1 / INTERNAL_PRECISION + ); + + // supply + ISilo($.lendingVault).deposit( + IERC20(collateralAsset).balanceOf(address(this)), address(this), ISilo.CollateralType.Collateral + ); + + // borrow + ISilo($.borrowingVault).borrow(tempBorrowAmount, address(this), address(this)); + + // pay flash loan + IERC20(token).safeTransfer(flashLoanVault, amount + feeAmount); + + // repay remaining balance + ISilo($.borrowingVault).repay(StrategyLib.balance(token), address(this)); + + // reset temp vars + $.tempBorrowAmount = 0; + } + + (uint ltv,, uint leverage,,,) = health(platform, $); + emit ILeverageLendingStrategy.LeverageLendingHealth(ltv, leverage); + + $.tempAction = ILeverageLendingStrategy.CurrentAction.None; + } + + function health( + address platform, + ILeverageLendingStrategy.LeverageLendingBaseStorage storage $ + ) + public + view + returns ( + uint ltv, + uint maxLtv, + uint leverage, + uint collateralAmount, + uint debtAmount, + uint targetLeveragePercent + ) + { + address lendingVault = $.lendingVault; + address collateralAsset = $.collateralAsset; + + ltv = ISiloLens($.helper).getLtv(lendingVault, address(this)); + ltv = ltv * INTERNAL_PRECISION / 1e18; + + collateralAmount = StrategyLib.balance(collateralAsset) + totalCollateral(lendingVault); + debtAmount = totalDebt($.borrowingVault); + + IPriceReader priceReader = IPriceReader(IPlatform(platform).priceReader()); + (uint _realTvl,) = realTvl(platform, $); + (uint collateralPrice,) = priceReader.getPrice(collateralAsset); + uint collateralUsd = collateralAmount * collateralPrice / 10 ** IERC20Metadata(collateralAsset).decimals(); + leverage = collateralUsd * INTERNAL_PRECISION / _realTvl; + + targetLeveragePercent = $.targetLeveragePercent; + + (maxLtv,,) = getLtvData(lendingVault, targetLeveragePercent); + } + + function realTvl( + address platform, + ILeverageLendingStrategy.LeverageLendingBaseStorage storage $ + ) public view returns (uint tvl, bool trusted) { + IPriceReader priceReader = IPriceReader(IPlatform(platform).priceReader()); + address lendingVault = $.lendingVault; + address collateralAsset = $.collateralAsset; + address borrowAsset = $.borrowAsset; + uint collateralAmount = StrategyLib.balance(collateralAsset) + totalCollateral(lendingVault); + (uint collateralPrice, bool CollateralPriceTrusted) = priceReader.getPrice(collateralAsset); + uint collateralUsd = collateralAmount * collateralPrice / 10 ** IERC20Metadata(collateralAsset).decimals(); + uint borrowedAmount = totalDebt($.borrowingVault); + (uint borrowAssetPrice, bool borrowAssetPriceTrusted) = priceReader.getPrice(borrowAsset); + uint borrowAssetUsd = borrowedAmount * borrowAssetPrice / 10 ** IERC20Metadata(borrowAsset).decimals(); + tvl = collateralUsd - borrowAssetUsd; + trusted = CollateralPriceTrusted && borrowAssetPriceTrusted; + } + + function getPrices(address lendVault, address debtVault) public view returns (uint priceCtoB, uint priceBtoC) { + ISiloConfig siloConfig = ISiloConfig(ISilo(lendVault).config()); + ISiloConfig.ConfigData memory collateralConfig = siloConfig.getConfig(lendVault); + address collateralOracle = collateralConfig.solvencyOracle; + ISiloConfig.ConfigData memory borrowConfig = siloConfig.getConfig(debtVault); + address borrowOracle = borrowConfig.solvencyOracle; + if (collateralOracle != address(0) && borrowOracle == address(0)) { + priceCtoB = ISiloOracle(collateralOracle).quote( + 10 ** IERC20Metadata(collateralConfig.token).decimals(), collateralConfig.token + ); + priceBtoC = 1e18 * 1e18 / priceCtoB; + } else if (collateralOracle == address(0) && borrowOracle != address(0)) { + priceBtoC = + ISiloOracle(borrowOracle).quote(10 ** IERC20Metadata(borrowConfig.token).decimals(), borrowConfig.token); + priceCtoB = 1e18 * 1e18 / priceBtoC; + } else { + priceCtoB = ISiloOracle(collateralOracle).quote( + 10 ** IERC20Metadata(collateralConfig.token).decimals(), collateralConfig.token + ); + priceBtoC = 1e18 * 1e18 / priceCtoB; + } + } + + /// @dev LTV data + /// @return maxLtv Max LTV with 18 decimals + /// @return maxLeverage Max leverage multiplier with 4 decimals + /// @return targetLeverage Target leverage multiplier with 4 decimals + function getLtvData( + address lendingVault, + uint targetLeveragePercent + ) public view returns (uint maxLtv, uint maxLeverage, uint targetLeverage) { + address configContract = ISilo(lendingVault).config(); + ISiloConfig.ConfigData memory config = ISiloConfig(configContract).getConfig(lendingVault); + maxLtv = config.maxLtv; + maxLeverage = 1e18 * INTERNAL_PRECISION / (1e18 - maxLtv); + targetLeverage = maxLeverage * targetLeveragePercent / INTERNAL_PRECISION; + } + + function calcTotal(ILeverageLendingStrategy.LeverageLendingAddresses memory v) external view returns (uint) { + (, uint priceBtoC) = getPrices(v.lendingVault, v.borrowingVault); + uint borrowedAmountPricedInCollateral = totalDebt(v.borrowingVault) * priceBtoC / 1e18; + return totalCollateral(v.lendingVault) - borrowedAmountPricedInCollateral; + } + + function totalCollateral(address lendingVault) public view returns (uint) { + return IERC4626(lendingVault).convertToAssets(StrategyLib.balance(lendingVault)); + } + + function totalDebt(address borrowingVault) public view returns (uint) { + return ISilo(borrowingVault).maxRepay(address(this)); + } + + /*function _mintCollateralAsset(address token, address */ + + /*tokenOut*/ + /*, uint amount) internal { + if (token == TOKEN_USDC) { + // mint scUSD + IERC20(TOKEN_USDC).forceApprove(TOKEN_scUSD, amount); + ITeller(TELLER_scUSD).deposit(TOKEN_USDC, amount, 0); + // mint stkscUSD + IERC20(TOKEN_scUSD).forceApprove(TOKEN_stkscUSD, amount); + ITeller(TELLER_stkscUSD).deposit(TOKEN_scUSD, amount, 0); + // mint wstkscUSD + IERC20(TOKEN_stkscUSD).forceApprove(TOKEN_wstkscUSD, amount); + IERC4626(TOKEN_wstkscUSD).deposit(amount, address(this)); + } + }*/ +} diff --git a/src/strategies/libs/StrategyDeveloperLib.sol b/src/strategies/libs/StrategyDeveloperLib.sol index 11c02d42..964cf8f5 100644 --- a/src/strategies/libs/StrategyDeveloperLib.sol +++ b/src/strategies/libs/StrategyDeveloperLib.sol @@ -68,6 +68,9 @@ library StrategyDeveloperLib { if (CommonLib.eq(strategyId, StrategyIdLib.SILO_LEVERAGE)) { return 0x88888887C3ebD4a33E34a15Db4254C74C75E5D4A; } + if (CommonLib.eq(strategyId, StrategyIdLib.SILO_ADVANCED_LEVERAGE)) { + return 0x88888887C3ebD4a33E34a15Db4254C74C75E5D4A; + } return address(0); } } diff --git a/src/strategies/libs/StrategyIdLib.sol b/src/strategies/libs/StrategyIdLib.sol index a891a685..27cd7ab3 100644 --- a/src/strategies/libs/StrategyIdLib.sol +++ b/src/strategies/libs/StrategyIdLib.sol @@ -24,4 +24,5 @@ library StrategyIdLib { string internal constant SILO_FARM = "Silo Farm"; string internal constant ALM_SHADOW_FARM = "ALM Shadow Farm"; string internal constant SILO_LEVERAGE = "Silo Leverage"; + string internal constant SILO_ADVANCED_LEVERAGE = "Silo Advanced Leverage"; } diff --git a/test/base/UniversalTest.sol b/test/base/UniversalTest.sol index 0f4c18b3..d51c7406 100644 --- a/test/base/UniversalTest.sol +++ b/test/base/UniversalTest.sol @@ -52,6 +52,7 @@ abstract contract UniversalTest is Test, ChainSetup, Utils { address pool; uint farmId; address[] strategyInitAddresses; + uint[] strategyInitNums; } struct TestStrategiesVars { @@ -248,7 +249,7 @@ abstract contract UniversalTest is Test, ChainSetup, Utils { ); initStrategyAddresses = strategies[i].strategyInitAddresses; - nums = new uint[](0); + nums = strategies[i].strategyInitNums; } factory.deployVaultAndStrategy( @@ -802,7 +803,7 @@ abstract contract UniversalTest is Test, ChainSetup, Utils { uint balNow = IERC20(assets[j]).balanceOf(address(this)); vars.withdrawnUsdValue += balNow * price / 10 ** IERC20Metadata(assets[j]).decimals(); } - assertGe(vars.withdrawnUsdValue, vars.depositUsdValue * 96_00 / 100_00, "E1"); + assertGe(vars.withdrawnUsdValue, vars.depositUsdValue * 93_00 / 100_00, "E1"); /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* COVERAGE */ diff --git a/test/strategies/ASF.Sonic.t.sol b/test/strategies/ASF.Sonic.t.sol index bfb02057..cd9d52b9 100644 --- a/test/strategies/ASF.Sonic.t.sol +++ b/test/strategies/ASF.Sonic.t.sol @@ -83,7 +83,8 @@ contract ALMShadowFarmStrategyTest is SonicSetup, UniversalTest { id: StrategyIdLib.ALM_SHADOW_FARM, pool: address(0), farmId: farmId, - strategyInitAddresses: new address[](0) + strategyInitAddresses: new address[](0), + strategyInitNums: new uint[](0) }) ); } diff --git a/test/strategies/BSF.Sonic.t.sol b/test/strategies/BSF.Sonic.t.sol index c70f49b2..20f55f07 100644 --- a/test/strategies/BSF.Sonic.t.sol +++ b/test/strategies/BSF.Sonic.t.sol @@ -30,7 +30,8 @@ contract BeetsStableFarmStrategyTest is SonicSetup, UniversalTest { id: StrategyIdLib.BEETS_STABLE_FARM, pool: address(0), farmId: farmId, - strategyInitAddresses: new address[](0) + strategyInitAddresses: new address[](0), + strategyInitNums: new uint[](0) }) ); } diff --git a/test/strategies/BWF.Sonic.t.sol b/test/strategies/BWF.Sonic.t.sol index 99ddad72..d49befb7 100644 --- a/test/strategies/BWF.Sonic.t.sol +++ b/test/strategies/BWF.Sonic.t.sol @@ -17,7 +17,8 @@ contract BeetsWeightedFarmStrategyTest is SonicSetup, UniversalTest { id: StrategyIdLib.BEETS_WEIGHTED_FARM, pool: address(0), farmId: farmId, - strategyInitAddresses: new address[](0) + strategyInitAddresses: new address[](0), + strategyInitNums: new uint[](0) }) ); } diff --git a/test/strategies/CCF.Polygon.t.sol b/test/strategies/CCF.Polygon.t.sol index dabc5098..2fe90eb3 100644 --- a/test/strategies/CCF.Polygon.t.sol +++ b/test/strategies/CCF.Polygon.t.sol @@ -25,7 +25,8 @@ contract CurveConvexFarmStrategyTest is PolygonSetup, UniversalTest { id: StrategyIdLib.CURVE_CONVEX_FARM, pool: address(0), farmId: farmId, - strategyInitAddresses: new address[](0) + strategyInitAddresses: new address[](0), + strategyInitNums: new uint[](0) }) ); } diff --git a/test/strategies/CF.Arbitrum.t.sol b/test/strategies/CF.Arbitrum.t.sol index 1c64cf67..24eb046f 100644 --- a/test/strategies/CF.Arbitrum.t.sol +++ b/test/strategies/CF.Arbitrum.t.sol @@ -15,7 +15,8 @@ contract CompoundFarmStrategyTest is ArbitrumSetup, UniversalTest { id: StrategyIdLib.COMPOUND_FARM, pool: address(0), farmId: farmId, // chains/ArbitrumLib.sol - strategyInitAddresses: new address[](0) + strategyInitAddresses: new address[](0), + strategyInitNums: new uint[](0) }) ); } diff --git a/test/strategies/CF.Base.t.sol b/test/strategies/CF.Base.t.sol index 1039c0f2..20e3cce9 100644 --- a/test/strategies/CF.Base.t.sol +++ b/test/strategies/CF.Base.t.sol @@ -18,7 +18,8 @@ contract CompoundFarmStrategyTest is BaseSetup, UniversalTest { id: StrategyIdLib.COMPOUND_FARM, pool: address(0), farmId: farmId, // chains/BaseLib.sol - strategyInitAddresses: new address[](0) + strategyInitAddresses: new address[](0), + strategyInitNums: new uint[](0) }) ); } diff --git a/test/strategies/CF.Ethereum.t.sol b/test/strategies/CF.Ethereum.t.sol deleted file mode 100644 index ae3411cb..00000000 --- a/test/strategies/CF.Ethereum.t.sol +++ /dev/null @@ -1,19 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.23; - -/* -import "../base/chains/EthereumSetup.sol"; -import "../base/UniversalTest.sol"; - -contract CompoundFarmStrategyTest is EthereumSetup, UniversalTest { - function testCFEthereum() public universalTest { - _addStrategy(0); - } - - function _addStrategy(uint farmId) internal { - strategies.push( - Strategy({id: StrategyIdLib.COMPOUND_FARM, pool: address(0), farmId: farmId, strategyInitAddresses: new address[](0)}) - ); - } -} -*/ diff --git a/test/strategies/CF.Polygon.t.sol b/test/strategies/CF.Polygon.t.sol index ab8864d8..18eccfc7 100644 --- a/test/strategies/CF.Polygon.t.sol +++ b/test/strategies/CF.Polygon.t.sol @@ -11,7 +11,8 @@ contract CompoundFarmStrategyTest is PolygonSetup, UniversalTest { id: StrategyIdLib.COMPOUND_FARM, pool: address(0), farmId: 17, // chains/PolygonLib.sol - strategyInitAddresses: new address[](0) + strategyInitAddresses: new address[](0), + strategyInitNums: new uint[](0) }) ); } diff --git a/test/strategies/DQMF.Polygon.t.sol b/test/strategies/DQMF.Polygon.t.sol index d2055e5b..6f700006 100644 --- a/test/strategies/DQMF.Polygon.t.sol +++ b/test/strategies/DQMF.Polygon.t.sol @@ -35,7 +35,8 @@ contract DefiEdgeQuickSwapMerklFarmStrategyTest is PolygonSetup, UniversalTest { id: StrategyIdLib.DEFIEDGE_QUICKSWAP_MERKL_FARM, pool: address(0), farmId: farmId, - strategyInitAddresses: new address[](0) + strategyInitAddresses: new address[](0), + strategyInitNums: new uint[](0) }) ); } diff --git a/test/strategies/EF.Sonic.t.sol b/test/strategies/EF.Sonic.t.sol index 5bd65888..bf53207a 100644 --- a/test/strategies/EF.Sonic.t.sol +++ b/test/strategies/EF.Sonic.t.sol @@ -26,7 +26,8 @@ contract EqualizerFarmStrategyTest is SonicSetup, UniversalTest { id: StrategyIdLib.EQUALIZER_FARM, pool: address(0), farmId: farmId, - strategyInitAddresses: new address[](0) + strategyInitAddresses: new address[](0), + strategyInitNums: new uint[](0) }) ); } diff --git a/test/strategies/GQMF.Polygon.t.sol b/test/strategies/GQMF.Polygon.t.sol index 95296b7f..b734aa30 100644 --- a/test/strategies/GQMF.Polygon.t.sol +++ b/test/strategies/GQMF.Polygon.t.sol @@ -31,7 +31,8 @@ contract GammaQuickSwapMerklFarmStrategyTest is PolygonSetup, UniversalTest { id: StrategyIdLib.GAMMA_QUICKSWAP_MERKL_FARM, pool: address(0), farmId: farmId, - strategyInitAddresses: new address[](0) + strategyInitAddresses: new address[](0), + strategyInitNums: new uint[](0) }) ); } diff --git a/test/strategies/GRMF.Polygon.t.sol b/test/strategies/GRMF.Polygon.t.sol index 0f5790d1..6f25179b 100644 --- a/test/strategies/GRMF.Polygon.t.sol +++ b/test/strategies/GRMF.Polygon.t.sol @@ -22,7 +22,8 @@ contract GammaRetroMerklFarmStrategyTest is PolygonSetup, UniversalTest { id: StrategyIdLib.GAMMA_RETRO_MERKL_FARM, pool: address(0), farmId: farmId, - strategyInitAddresses: new address[](0) + strategyInitAddresses: new address[](0), + strategyInitNums: new uint[](0) }) ); } diff --git a/test/strategies/GUMF.Arbitrum.t.sol b/test/strategies/GUMF.Arbitrum.t.sol index 2f92a2b1..88fb638c 100644 --- a/test/strategies/GUMF.Arbitrum.t.sol +++ b/test/strategies/GUMF.Arbitrum.t.sol @@ -16,7 +16,8 @@ contract GammaUniswapV3MerklFarmStrategyTest is ArbitrumSetup, UniversalTest { id: StrategyIdLib.GAMMA_UNISWAPV3_MERKL_FARM, pool: address(0), farmId: farmId, - strategyInitAddresses: new address[](0) + strategyInitAddresses: new address[](0), + strategyInitNums: new uint[](0) }) ); } diff --git a/test/strategies/GUMF.Base.t.sol b/test/strategies/GUMF.Base.t.sol index ea0d4beb..96f477ba 100644 --- a/test/strategies/GUMF.Base.t.sol +++ b/test/strategies/GUMF.Base.t.sol @@ -19,7 +19,8 @@ contract GammaUniswapV3MerklFarmStrategyTest is BaseSetup, UniversalTest { id: StrategyIdLib.GAMMA_UNISWAPV3_MERKL_FARM, pool: address(0), farmId: farmId, - strategyInitAddresses: new address[](0) + strategyInitAddresses: new address[](0), + strategyInitNums: new uint[](0) }) ); } diff --git a/test/strategies/GUMF.Sonic.t.sol b/test/strategies/GUMF.Sonic.t.sol index 6a832cef..0942c9cf 100644 --- a/test/strategies/GUMF.Sonic.t.sol +++ b/test/strategies/GUMF.Sonic.t.sol @@ -22,7 +22,8 @@ contract GammaUniswapV3MerklFarmStrategyTestSonic is SonicSetup, UniversalTest { id: StrategyIdLib.GAMMA_UNISWAPV3_MERKL_FARM, pool: address(0), farmId: farmId, - strategyInitAddresses: new address[](0) + strategyInitAddresses: new address[](0), + strategyInitNums: new uint[](0) }) ); } diff --git a/test/strategies/IQMF.Polygon.t.sol b/test/strategies/IQMF.Polygon.t.sol index fc8f0734..3e3b303d 100644 --- a/test/strategies/IQMF.Polygon.t.sol +++ b/test/strategies/IQMF.Polygon.t.sol @@ -22,7 +22,8 @@ contract IchiQuickSwapMerklFarmStrategyTest is PolygonSetup, UniversalTest { id: StrategyIdLib.ICHI_QUICKSWAP_MERKL_FARM, pool: address(0), farmId: farmId, - strategyInitAddresses: new address[](0) + strategyInitAddresses: new address[](0), + strategyInitNums: new uint[](0) }) ); } diff --git a/test/strategies/IRMF.Polygon.t.sol b/test/strategies/IRMF.Polygon.t.sol index af22947d..f6fe084b 100644 --- a/test/strategies/IRMF.Polygon.t.sol +++ b/test/strategies/IRMF.Polygon.t.sol @@ -24,7 +24,8 @@ contract IchiRetroMerklFarmStrategyTest is PolygonSetup, UniversalTest { id: StrategyIdLib.ICHI_RETRO_MERKL_FARM, pool: address(0), farmId: farmId, - strategyInitAddresses: new address[](0) + strategyInitAddresses: new address[](0), + strategyInitNums: new uint[](0) }) ); } diff --git a/test/strategies/ISF.Sonic.t.sol b/test/strategies/ISF.Sonic.t.sol index e0b6b5b0..30e626b5 100644 --- a/test/strategies/ISF.Sonic.t.sol +++ b/test/strategies/ISF.Sonic.t.sol @@ -57,7 +57,8 @@ contract IchiSwapXFarmStrategyTest is SonicSetup, UniversalTest { id: StrategyIdLib.ICHI_SWAPX_FARM, pool: address(0), farmId: farmId, - strategyInitAddresses: new address[](0) + strategyInitAddresses: new address[](0), + strategyInitNums: new uint[](0) }) ); } diff --git a/test/strategies/QSMF.Polygon.t.sol b/test/strategies/QSMF.Polygon.t.sol index 69a1e598..0e25dc7b 100644 --- a/test/strategies/QSMF.Polygon.t.sol +++ b/test/strategies/QSMF.Polygon.t.sol @@ -18,7 +18,8 @@ contract QuickswapStaticMerklFarmStrategyTest is PolygonSetup, UniversalTest { id: StrategyIdLib.QUICKSWAP_STATIC_MERKL_FARM, pool: address(0), farmId: farmId, - strategyInitAddresses: new address[](0) + strategyInitAddresses: new address[](0), + strategyInitNums: new uint[](0) }) ); } diff --git a/test/strategies/SF.Sonic.t.sol b/test/strategies/SF.Sonic.t.sol index ce6671fd..652bedce 100644 --- a/test/strategies/SF.Sonic.t.sol +++ b/test/strategies/SF.Sonic.t.sol @@ -25,7 +25,8 @@ contract SwapXFarmStrategyTest is SonicSetup, UniversalTest { id: StrategyIdLib.SWAPX_FARM, pool: address(0), farmId: farmId, - strategyInitAddresses: new address[](0) + strategyInitAddresses: new address[](0), + strategyInitNums: new uint[](0) }) ); } diff --git a/test/strategies/SQMF.Polygon.t.sol b/test/strategies/SQMF.Polygon.t.sol index b62b6179..c34d27f5 100644 --- a/test/strategies/SQMF.Polygon.t.sol +++ b/test/strategies/SQMF.Polygon.t.sol @@ -25,7 +25,8 @@ contract SteerQuickSwapMerklFarmStrategyTest is PolygonSetup, UniversalTest { id: StrategyIdLib.STEER_QUICKSWAP_MERKL_FARM, pool: address(0), farmId: farmId, - strategyInitAddresses: new address[](0) + strategyInitAddresses: new address[](0), + strategyInitNums: new uint[](0) }) ); } diff --git a/test/strategies/SiAL.Sonic.sol b/test/strategies/SiAL.Sonic.sol new file mode 100644 index 00000000..1c29a34b --- /dev/null +++ b/test/strategies/SiAL.Sonic.sol @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.23; + +import {SonicSetup} from "../base/chains/SonicSetup.sol"; +import {SonicLib} from "../../chains/SonicLib.sol"; +import {UniversalTest} from "../base/UniversalTest.sol"; +import {StrategyIdLib} from "../../src/strategies/libs/StrategyIdLib.sol"; + +contract SiloAdvancedLeverageStrategyTest is SonicSetup, UniversalTest { + constructor() { + vm.selectFork(vm.createFork(vm.envString("SONIC_RPC_URL"))); + vm.rollFork(11356000); // Mar-03-2025 08:19:49 AM +UTC + allowZeroApr = true; + duration1 = 0.1 hours; + duration2 = 0.1 hours; + duration3 = 0.1 hours; + } + + function testSiALSonic() public universalTest { + _addStrategy(SonicLib.SILO_VAULT_23_wstkscUSD, SonicLib.SILO_VAULT_23_USDC, 80_00); + // not work because Swapper need support longer route + //_addStrategy(SonicLib.SILO_VAULT_26_wstkscETH, SonicLib.SILO_VAULT_26_wETH, 80_00); + //_addStrategy(SonicLib.SILO_VAULT_22_wOS, SonicLib.SILO_VAULT_22_wS, 87_00); + } + + function _addStrategy( + address strategyInitAddress0, + address strategyInitAddress1, + uint targetLeveragePercent + ) internal { + address[] memory initStrategyAddresses = new address[](4); + initStrategyAddresses[0] = strategyInitAddress0; + initStrategyAddresses[1] = strategyInitAddress1; + initStrategyAddresses[2] = SonicLib.BEETS_VAULT; + initStrategyAddresses[3] = SonicLib.SILO_LENS; + uint[] memory strategyInitNums = new uint[](1); + strategyInitNums[0] = targetLeveragePercent; + strategies.push( + Strategy({ + id: StrategyIdLib.SILO_ADVANCED_LEVERAGE, + pool: address(0), + farmId: type(uint).max, + strategyInitAddresses: initStrategyAddresses, + strategyInitNums: strategyInitNums + }) + ); + } +} diff --git a/test/strategies/SiF.Sonic.sol b/test/strategies/SiF.Sonic.sol index d26dc0b4..7db3db76 100644 --- a/test/strategies/SiF.Sonic.sol +++ b/test/strategies/SiF.Sonic.sol @@ -21,7 +21,8 @@ contract SiloFarmStrategyTest is SonicSetup, UniversalTest { id: StrategyIdLib.SILO_FARM, pool: address(0), farmId: farmId, // chains/SonicLib.sol - strategyInitAddresses: new address[](0) + strategyInitAddresses: new address[](0), + strategyInitNums: new uint[](0) }) ); } diff --git a/test/strategies/SiL.LeverageDebug.Sonic.t.sol b/test/strategies/SiL.LeverageDebug.Sonic.t.sol index 39d572a9..8797934f 100644 --- a/test/strategies/SiL.LeverageDebug.Sonic.t.sol +++ b/test/strategies/SiL.LeverageDebug.Sonic.t.sol @@ -16,8 +16,8 @@ import {CommonLib} from "../../src/core/libs/CommonLib.sol"; contract SiloLeverageLendingStrategyDebugTest is Test { address public constant PLATFORM = 0x4Aca671A420eEB58ecafE83700686a2AD06b20D8; - address public constant STRATEGY = 0x811002015AC45D551A3D962d8375A7B16Dede6BE; // S-stS - //address public constant STRATEGY = 0xfF9C35acDA4b136F71B1736B2BDFB5479f111C4A; // stS-S + //address public constant STRATEGY = 0x811002015AC45D551A3D962d8375A7B16Dede6BE; // S-stS + address public constant STRATEGY = 0xfF9C35acDA4b136F71B1736B2BDFB5479f111C4A; // stS-S address public vault; address public multisig; IFactory public factory; diff --git a/test/strategies/SiL.Sonic.sol b/test/strategies/SiL.Sonic.sol index 43434321..550afdb1 100644 --- a/test/strategies/SiL.Sonic.sol +++ b/test/strategies/SiL.Sonic.sol @@ -33,7 +33,8 @@ contract SiloLeverageStrategyTest is SonicSetup, UniversalTest { id: StrategyIdLib.SILO_LEVERAGE, pool: address(0), farmId: type(uint).max, - strategyInitAddresses: initStrategyAddresses + strategyInitAddresses: initStrategyAddresses, + strategyInitNums: new uint[](0) }) ); } diff --git a/test/strategies/TPF.Real.t.sol b/test/strategies/TPF.Real.t.sol index 7d5868d3..28a8d430 100644 --- a/test/strategies/TPF.Real.t.sol +++ b/test/strategies/TPF.Real.t.sol @@ -23,7 +23,8 @@ contract TridentPearlFarmStrategyTest is RealSetup, UniversalTest { id: StrategyIdLib.TRIDENT_PEARL_FARM, pool: address(0), farmId: farmId, - strategyInitAddresses: new address[](0) + strategyInitAddresses: new address[](0), + strategyInitNums: new uint[](0) }) ); } diff --git a/test/strategies/Y.Polygon.t.sol b/test/strategies/Y.Polygon.t.sol index ffbb93bd..1b468f1c 100644 --- a/test/strategies/Y.Polygon.t.sol +++ b/test/strategies/Y.Polygon.t.sol @@ -25,7 +25,8 @@ contract YearnStrategyTest is PolygonSetup, UniversalTest { id: StrategyIdLib.YEARN, pool: address(0), farmId: type(uint).max, - strategyInitAddresses: initStrategyAddresses + strategyInitAddresses: initStrategyAddresses, + strategyInitNums: new uint[](0) }) ); }