diff --git a/foundry.toml b/foundry.toml index ff12d144..e75ef310 100644 --- a/foundry.toml +++ b/foundry.toml @@ -11,11 +11,13 @@ runs = 256 [profile.ci] fuzz_runs = 100_000 verbosity = 4 +solc = "0.8.21" +evm_version = "cancun" [rpc_endpoints] goerli = "${RPC_URL_GOERLI}" mainnet = "${RPC_URL_MAINNET}" [etherscan] -goerli = {key = "${ETHERSCAN_KEY}", url = "https://api-goerli.etherscan.io/api"} -mainnet = {key = "${ETHERSCAN_KEY}"} +goerli = { key = "${ETHERSCAN_KEY}", url = "https://api-goerli.etherscan.io/api" } +mainnet = { key = "${ETHERSCAN_KEY}" } diff --git a/script/DeployLiquity.s.sol b/script/DeployLiquity.s.sol index 5d16da00..527398a4 100644 --- a/script/DeployLiquity.s.sol +++ b/script/DeployLiquity.s.sol @@ -4,11 +4,11 @@ pragma solidity ^0.8.13; import "forge-std/console2.sol"; import {ERC20} from "solmate/tokens/ERC20.sol"; -import {MainnetAddresses} from "./base/MainnetAddresses.sol"; -import {MainnetDeployBase} from "./base/MainnetDeployBase.sol"; -import {ISwapRouter} from "../src/interfaces/uniswap/ISwapRouter.sol"; -import {Constants as C} from "../src/lib/Constants.sol"; -import {scLiquity} from "../src/liquity/scLiquity.sol"; +import {SwapperLib} from "src/lib/SwapperLib.sol"; +import {MainnetAddresses} from "script/base/MainnetAddresses.sol"; +import {MainnetDeployBase} from "script/base/MainnetDeployBase.sol"; +import {Constants as C} from "src/lib/Constants.sol"; +import {scLiquity} from "src/liquity/scLiquity.sol"; /** * Mainnet deployment script for scLiquity vault. @@ -24,32 +24,17 @@ contract DeployLiquity is MainnetDeployBase { // get some LUSD and make the initial deposit (addressing share inflation) weth.deposit{value: 0.01 ether}(); - uint256 usdcAmount = _swapWethForUsdc(0.01 ether); - uint256 lusdAmount = _swapUsdcForLusd(usdcAmount); + uint256 usdcAmount = + SwapperLib._uniswapSwapExactInput(address(weth), address(usdc), deployerAddress, 0.01 ether, 0, 500); // 0.05% pool fee + uint256 lusdAmount = + SwapperLib._uniswapSwapExactInput(address(usdc), address(lusd), deployerAddress, usdcAmount, 0, 500); // 0.05% pool fee _deposit(vault, lusdAmount); _setTreasury(vault, MainnetAddresses.TREASURY); - _transferAdminRoleToMultisig(vault, deployerAddress); + _transferAdminRoleToMultisig(vault); vm.stopBroadcast(); } - - function _swapUsdcForLusd(uint256 _amountIn) internal returns (uint256 amountOut) { - usdc.approve(C.UNISWAP_V3_SWAP_ROUTER, _amountIn); - - ISwapRouter.ExactInputSingleParams memory params = ISwapRouter.ExactInputSingleParams({ - tokenIn: address(usdc), - tokenOut: address(lusd), - fee: 500, // 0.05% - recipient: deployerAddress, - deadline: block.timestamp + 1000, - amountIn: _amountIn, - amountOutMinimum: 0, - sqrtPriceLimitX96: 0 - }); - - amountOut = ISwapRouter(C.UNISWAP_V3_SWAP_ROUTER).exactInputSingle(params); - } } diff --git a/script/DeployStaking.s.sol b/script/DeployStaking.s.sol index 7168ee5e..f52d551e 100644 --- a/script/DeployStaking.s.sol +++ b/script/DeployStaking.s.sol @@ -2,10 +2,10 @@ pragma solidity ^0.8.13; import "forge-std/Script.sol"; -import {CREATE3Script} from "./base/CREATE3Script.sol"; -import {RewardTracker} from "../src/staking/RewardTracker.sol"; -import {MainnetAddresses as MA} from "./base/MainnetAddresses.sol"; -import {Constants as C} from "../src/lib/Constants.sol"; +import {CREATE3Script} from "script/base/CREATE3Script.sol"; +import {MainnetAddresses as MA} from "script/base/MainnetAddresses.sol"; +import {RewardTracker} from "src/staking/RewardTracker.sol"; +import {Constants as C} from "src/lib/Constants.sol"; contract DeployStaking is CREATE3Script { string public NAME = "Staked Quartz"; diff --git a/script/base/CREATE3Script.sol b/script/base/CREATE3Script.sol index afee3c03..f4753732 100644 --- a/script/base/CREATE3Script.sol +++ b/script/base/CREATE3Script.sol @@ -17,4 +17,12 @@ abstract contract CREATE3Script is Script { return keccak256(bytes(string.concat(name, "-v", version))); } + + // function predictDeploy(IERC4626 _vault) public view returns (YieldStreams predicted) { + // predicted = YieldStreams(CREATE3.getDeployed(getSalt(_vault), address(this))); + // } + + // function isDeployed(IERC4626 _vault) public view returns (bool) { + // return address(predictDeploy(_vault)).code.length > 0; + // } } diff --git a/script/base/DeployLeveragedEth.sol b/script/base/DeployScWethV2AndScUsdcV2.sol similarity index 84% rename from script/base/DeployLeveragedEth.sol rename to script/base/DeployScWethV2AndScUsdcV2.sol index 14e7706f..2cd8a813 100644 --- a/script/base/DeployLeveragedEth.sol +++ b/script/base/DeployScWethV2AndScUsdcV2.sol @@ -27,29 +27,22 @@ import {MainnetDeployBase} from "../base/MainnetDeployBase.sol"; * Base Deployment file that handles Forked & Mainnet deployment. * Forked node deployments are equivalent to mainnet deployments. */ -abstract contract DeployLeveragedEth is MainnetDeployBase { - IPool aavePool = IPool(C.AAVE_V3_POOL); - ICurvePool curveEthStEthPool = ICurvePool(C.CURVE_ETH_STETH_POOL); - ISwapRouter uniswapRouter = ISwapRouter(C.UNISWAP_V3_SWAP_ROUTER); - +abstract contract DeployScWethV2AndScUsdcV2 is MainnetDeployBase { scWETH scWeth; scUSDC scUsdc; - address alice = DC.ALICE; - address bob = DC.BOB; - function _deploy() internal { - vm.startBroadcast(deployerPrivateKey); + vm.startBroadcast(deployerAddress); scWETH.ConstructorParams memory scWethParams = scWETH.ConstructorParams({ admin: deployerAddress, keeper: keeper, targetLtv: 0.85e18, slippageTolerance: 0.99e18, - aavePool: aavePool, + aavePool: IPool(C.AAVE_V3_POOL), aaveAwstEth: IAToken(C.AAVE_V3_AWSTETH_TOKEN), aaveVarDWeth: ERC20(C.AAVE_V3_VAR_DEBT_WETH_TOKEN), - curveEthStEthPool: curveEthStEthPool, + curveEthStEthPool: ICurvePool(C.CURVE_ETH_STETH_POOL), stEth: ILido(C.STETH), wstEth: IwstETH(C.WSTETH), weth: weth, @@ -66,11 +59,11 @@ abstract contract DeployLeveragedEth is MainnetDeployBase { scWETH: scWeth, usdc: usdc, weth: WETH(payable(C.WETH)), - aavePool: aavePool, + aavePool: IPool(C.AAVE_V3_POOL), aavePoolDataProvider: IPoolDataProvider(C.AAVE_V3_POOL_DATA_PROVIDER), aaveAUsdc: IAToken(C.AAVE_V3_AUSDC_TOKEN), aaveVarDWeth: ERC20(C.AAVE_V3_VAR_DEBT_WETH_TOKEN), - uniswapSwapRouter: uniswapRouter, + uniswapSwapRouter: ISwapRouter(C.UNISWAP_V3_SWAP_ROUTER), chainlinkUsdcToEthPriceFeed: AggregatorV3Interface(C.CHAINLINK_USDC_ETH_PRICE_FEED), balancerVault: IVault(C.BALANCER_VAULT) }); diff --git a/script/base/MainnetAddresses.sol b/script/base/MainnetAddresses.sol index 48f0b9c5..cdc1e3c5 100644 --- a/script/base/MainnetAddresses.sol +++ b/script/base/MainnetAddresses.sol @@ -17,8 +17,17 @@ library MainnetAddresses { address public constant SCUSDCV2_AAVEV2_ADAPTER = 0xE0E9E98FD963C2e69718C76939924522A9646885; address public constant SCUSDCV2_AAVEV3_ADAPTER = 0xf59c324fF111D86894f175E22B70b0d54998ff3E; - address public constant SCDAI = address(0x00); //todo: update after deployment - address public constant SCDAI_SPARK_ADAPTER = address(0x00); //TODO: update after deployment + address public constant SCUSDT = 0x237eCDF745d2a0052AeaF6f027ce82F77431871E; + address public constant SCUSDT_AAVEV3_ADAPTER = 0x795FDAC1bB26D219991f1923190cA7eB0046e32E; + address public constant USDT_WETH_PRICE_CONVERTER = 0x8e045458385d51d047bCA5b2B3b74053e235f5F9; + address public constant USDT_WETH_SWAPPER = 0xcD0Ae497068980ce94fbe20A6F7431329F0e4D08; + + address public constant SCDAI = 0x16f3cdA06743a58bDdE123687F99E80DCbc28d14; + + address public constant SCSDAI = 0x0Fc97657B67C7E7bD4100c72851d0377DA14B470; + address public constant SCSDAI_SPARK_ADAPTER = 0xeD81a1D7cb2AdC2ee379B594e0dA6a988a865ae4; + address public constant SDAI_WETH_PRICE_CONVERTER = 0x0dD22Ebc502f328b29a19Bf0A74373B03DfD374c; + address public constant SDAI_WETH_SWAPPER = 0x3F76D80dfc54677262FeA7E94a73D96aC69F4A10; address public constant PRICE_CONVERTER = 0xD76B0Ff4A487CaFE4E19ed15B73f12f6A92095Ca; address public constant SWAPPER = 0x6649f12b5ef495a3861b21E3206B1AbfA33A6531; diff --git a/script/base/MainnetDeployBase.sol b/script/base/MainnetDeployBase.sol index 2f4f1559..c68070b5 100644 --- a/script/base/MainnetDeployBase.sol +++ b/script/base/MainnetDeployBase.sol @@ -20,7 +20,6 @@ import {sc4626} from "../../src/sc4626.sol"; abstract contract MainnetDeployBase is CREATE3Script { using SafeTransferLib for ERC20; - uint256 deployerPrivateKey; address deployerAddress; address keeper; address multisig; @@ -33,42 +32,38 @@ abstract contract MainnetDeployBase is CREATE3Script { } function _init() internal virtual { - deployerPrivateKey = uint256(vm.envBytes32("PRIVATE_KEY")); + uint256 deployerPrivateKey = uint256(vm.envBytes32("PRIVATE_KEY")); + + require(deployerPrivateKey != 0, "Deployer private key not set"); + deployerAddress = vm.rememberKey(deployerPrivateKey); - keeper = vm.envAddress("KEEPER"); - multisig = vm.envAddress("MULTISIG"); + + keeper = vm.envOr("KEEPER", MainnetAddresses.KEEPER); + multisig = vm.envOr("MULTISIG", MainnetAddresses.MULTISIG); + } + + function deployWithCreate3(string memory _name, bytes memory _creationCode) public returns (address deployed) { + deployed = getCreate3Contract(deployerAddress, _name); + + if (deployed.code.length != 0) { + console2.log("Existing", _name, ":", deployed); + } else { + create3.deploy(getCreate3ContractSalt(_name), _creationCode); + console2.log("Deployed", _name, ":", deployed); + } } - function _transferAdminRoleToMultisig(AccessControl _contract, address _currentAdmin) internal { + function _transferAdminRoleToMultisig(AccessControl _contract) internal { _contract.grantRole(_contract.DEFAULT_ADMIN_ROLE(), multisig); - _contract.revokeRole(_contract.DEFAULT_ADMIN_ROLE(), _currentAdmin); + _contract.revokeRole(_contract.DEFAULT_ADMIN_ROLE(), deployerAddress); } function _setTreasury(sc4626 _vault, address _treasury) internal { _vault.setTreasury(_treasury); } - function _deposit(sc4626 _vault, uint256 _amount) internal virtual { - _vault.asset().approve(address(_vault), _amount); + function _deposit(ERC4626 _vault, uint256 _amount) internal virtual { + ERC20(_vault.asset()).safeApprove(address(_vault), _amount); _vault.deposit(_amount, deployerAddress); } - - function _swapWethForUsdc(uint256 _amount) internal returns (uint256 amountOut) { - weth.deposit{value: _amount}(); - - weth.approve(C.UNISWAP_V3_SWAP_ROUTER, _amount); - - ISwapRouter.ExactInputSingleParams memory params = ISwapRouter.ExactInputSingleParams({ - tokenIn: address(weth), - tokenOut: address(usdc), - fee: 500, // 0.05% - recipient: deployerAddress, - deadline: block.timestamp + 1000, - amountIn: _amount, - amountOutMinimum: 0, - sqrtPriceLimitX96: 0 - }); - - amountOut = ISwapRouter(C.UNISWAP_V3_SWAP_ROUTER).exactInputSingle(params); - } } diff --git a/script/base/ScUsdcV2ScriptBase.sol b/script/base/ScUsdcV2ScriptBase.sol deleted file mode 100644 index 09a65a6b..00000000 --- a/script/base/ScUsdcV2ScriptBase.sol +++ /dev/null @@ -1,57 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0 -pragma solidity ^0.8.13; - -import "forge-std/console2.sol"; -import "forge-std/Test.sol"; -import "forge-std/Script.sol"; - -import {ERC20} from "solmate/tokens/ERC20.sol"; -import {WETH} from "solmate/tokens/WETH.sol"; -import {Address} from "openzeppelin-contracts/utils/Address.sol"; - -import {MainnetAddresses} from "./MainnetAddresses.sol"; -import {PriceConverter} from "../../src/steth/priceConverter/PriceConverter.sol"; -import {scUSDCv2} from "../../src/steth/scUSDCv2.sol"; -import {scWETHv2} from "../../src/steth/scWETHv2.sol"; -import {MorphoAaveV3ScUsdcAdapter} from "../../src/steth/scUsdcV2-adapters/MorphoAaveV3ScUsdcAdapter.sol"; -import {AaveV2ScUsdcAdapter} from "../../src/steth/scUsdcV2-adapters/AaveV2ScUsdcAdapter.sol"; -import {AaveV3ScUsdcAdapter} from "../../src/steth/scUsdcV2-adapters/AaveV3ScUsdcAdapter.sol"; - -/** - * A base script for executing keeper functions on scUsdcV2 vault. - */ -abstract contract ScUsdcV2ScriptBase is Script { - using Address for address; - - uint256 keeperPrivateKey = uint256(vm.envOr("KEEPER_PRIVATE_KEY", bytes32(0x0))); - // if keeper private key is not provided, use the default keeper address for running the script tests - address keeper = keeperPrivateKey != 0 ? vm.addr(keeperPrivateKey) : MainnetAddresses.KEEPER; - - scUSDCv2 public scUsdcV2 = scUSDCv2(vm.envOr("SC_USDC_V2", MainnetAddresses.SCUSDCV2)); - - PriceConverter priceConverter = PriceConverter(vm.envOr("PRICE_CONVERTER", MainnetAddresses.PRICE_CONVERTER)); - - MorphoAaveV3ScUsdcAdapter public morphoAdapter = MorphoAaveV3ScUsdcAdapter(MainnetAddresses.SCUSDCV2_MORPHO_ADAPTER); - AaveV2ScUsdcAdapter public aaveV2Adapter = AaveV2ScUsdcAdapter(MainnetAddresses.SCUSDCV2_AAVEV2_ADAPTER); - AaveV3ScUsdcAdapter public aaveV3Adapter = AaveV3ScUsdcAdapter(MainnetAddresses.SCUSDCV2_AAVEV3_ADAPTER); - - function _logScriptParams() internal view virtual { - console2.log("\t script params"); - console2.log("keeper\t\t", address(keeper)); - console2.log("scUsdcV2\t\t", address(scUsdcV2)); - } - - function wethInvested() public view returns (uint256) { - return scWETH().convertToAssets(scWETH().balanceOf(address(scUsdcV2))); - } - - function scWETH() public view returns (scWETHv2) { - bytes memory result = address(scUsdcV2).functionStaticCall(abi.encodeWithSignature("scWETH()")); - - return abi.decode(result, (scWETHv2)); - } - - function usdcBalance() public view returns (uint256) { - return scUsdcV2.asset().balanceOf(address(scUsdcV2)); - } -} diff --git a/script/base/scCrossAssetYieldVaultBaseScript.sol b/script/base/scCrossAssetYieldVaultBaseScript.sol new file mode 100644 index 00000000..d9ce1f40 --- /dev/null +++ b/script/base/scCrossAssetYieldVaultBaseScript.sol @@ -0,0 +1,138 @@ +// SPDX-License-Identifier: AGPL-3.0 +pragma solidity ^0.8.13; + +import "forge-std/console2.sol"; +import "forge-std/Test.sol"; +import "forge-std/Script.sol"; + +import {ERC20} from "solmate/tokens/ERC20.sol"; +import {ERC4626} from "solmate/mixins/ERC4626.sol"; +import {WETH} from "solmate/tokens/WETH.sol"; + +import {MainnetAddresses} from "./MainnetAddresses.sol"; +import {PriceConverter} from "../../src/steth/priceConverter/PriceConverter.sol"; +import {scCrossAssetYieldVault} from "../../src/steth/scCrossAssetYieldVault.sol"; +import {ISinglePairPriceConverter} from "../../src/steth/priceConverter/ISinglePairPriceConverter.sol"; + +/** + * A base script for executing keeper functions on `scCrossAssetYieldVault` contracts. + */ +abstract contract scCrossAssetYieldVaultBaseScript is Script { + uint256 keeperPrivateKey; + address public keeper; + + scCrossAssetYieldVault vault = _getVaultAddress(); + + function _initEnv() internal virtual { + keeperPrivateKey = uint256(vm.envOr("KEEPER_PRIVATE_KEY", bytes32(0x0))); + // if keeper private key is not provided, use the default keeper address for running the script tests + keeper = keeperPrivateKey != 0 ? vm.rememberKey(keeperPrivateKey) : MainnetAddresses.KEEPER; + + console2.log("keeper address\t", address(keeper)); + console2.log("keeper private key\t", keeperPrivateKey); + } + + function run() external { + _initEnv(); + + console2.log(_startMessage()); + + require(vault.hasRole(vault.KEEPER_ROLE(), address(keeper)), "invalid keeper"); + + _logScriptParams(); + + _execute(); + + console2.log(_endMessage()); + } + + function _execute() internal virtual; + + function _startMessage() internal view virtual returns (string memory); + function _endMessage() internal view virtual returns (string memory); + function _getVaultAddress() internal virtual returns (scCrossAssetYieldVault); + + function _logScriptParams() internal view virtual { + console2.log("\t -script params-"); + console2.log("vault\t\t\t", string.concat(vault.name(), " - ", vault.symbol())); + console2.log("vault address\t\t", address(vault)); + console2.log("caller\t\t", address(keeper)); + } + + function targetTokensInvested() public view returns (uint256) { + uint256 targetVaultShares = targetVault().balanceOf(address(vault)); + + return targetVaultShares != 0 ? targetVault().convertToAssets(targetVault().balanceOf(address(vault))) : 0; + } + + function targetVault() public view returns (ERC4626) { + // try to use the vault to get the target vault for older version of scUSDCv2 + (bool ok, bytes memory result) = address(vault).staticcall(abi.encodeWithSignature("scWETH()")); + + return ok ? ERC4626(abi.decode(result, (address))) : vault.targetVault(); + } + + function assetBalance() public view returns (uint256) { + return vault.asset().balanceOf(address(vault)); + } + + function totalAssets() public view returns (uint256) { + (bool ok, bytes memory result) = address(vault).staticcall(abi.encodeWithSelector(vault.totalAssets.selector)); + + return ok ? abi.decode(result, (uint256)) : assetBalance(); + } + + function getProfit() public view returns (uint256) { + (bool ok, bytes memory result) = address(vault).staticcall(abi.encodeWithSelector(vault.getProfit.selector)); + + return ok ? abi.decode(result, (uint256)) : 0; + } + + function totalCollateral() public view returns (uint256) { + (bool ok, bytes memory result) = + address(vault).staticcall(abi.encodeWithSelector(vault.totalCollateral.selector)); + + return ok ? abi.decode(result, (uint256)) : 0; + } + + function totalDebt() public view returns (uint256) { + (bool ok, bytes memory result) = address(vault).staticcall(abi.encodeWithSelector(vault.totalDebt.selector)); + + return ok ? abi.decode(result, (uint256)) : 0; + } + + function getCollateral(uint256 _adapterId) public view returns (uint256) { + (bool ok, bytes memory result) = + address(vault).staticcall(abi.encodeWithSelector(vault.getCollateral.selector, _adapterId)); + + return ok ? abi.decode(result, (uint256)) : 0; + } + + function getDebt(uint256 _adapterId) public view returns (uint256) { + (bool ok, bytes memory result) = + address(vault).staticcall(abi.encodeWithSelector(vault.getDebt.selector, _adapterId)); + + return ok ? abi.decode(result, (uint256)) : 0; + } + + function assetPriceInTargetTokens(uint256 _assetAmount) public view returns (uint256 targetTokenAmount) { + // try to use the price converter to get the price for older version of scUSDCv2 + (bool ok, bytes memory result) = + address(vault.priceConverter()).staticcall(abi.encodeWithSignature("usdcToEth(uint256)", _assetAmount)); + + targetTokenAmount = ok + ? abi.decode(result, (uint256)) + : ISinglePairPriceConverter(address(vault.priceConverter())).assetToTargetToken(_assetAmount); + } + + function targetTokensPriceInAssets(uint256 _targetTokenAmount) public view returns (uint256 assetAmount) { + // try to use the price converter to get the price for older version of scUSDCv2 + (bool ok, bytes memory result) = address(vault.priceConverter()).staticcall( + abi.encodeWithSignature("ethToUsdc(uint256)", _targetTokenAmount) + ); + + assetAmount = ok + ? abi.decode(result, (uint256)) + : ISinglePairPriceConverter(address(vault.priceConverter())).targetTokenToAsset(_targetTokenAmount); + } +} diff --git a/script/base/scCrossAssetYieldVaultExitAllPositionsScript.sol b/script/base/scCrossAssetYieldVaultExitAllPositionsScript.sol new file mode 100644 index 00000000..797e35d1 --- /dev/null +++ b/script/base/scCrossAssetYieldVaultExitAllPositionsScript.sol @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: AGPL-3.0 +pragma solidity ^0.8.13; + +import "forge-std/console2.sol"; + +import {AccessControl} from "openzeppelin-contracts/access/AccessControl.sol"; +import {FixedPointMathLib} from "solmate/utils/FixedPointMathLib.sol"; + +import {scCrossAssetYieldVaultBaseScript} from "./scCrossAssetYieldVaultBaseScript.sol"; +import {scCrossAssetYieldVault} from "src/steth/scCrossAssetYieldVault.sol"; + +/** + * A script for executing "exitAllPositions" function on the scCrossAssetYieldVault contracts. + * This results in withdrawing all staked assets from the target vault, repaying all debt (using a flashloan if necessary) and withdrawing all collateral. + */ +abstract contract scCrossAssetYieldVaultExitAllPositionsScript is scCrossAssetYieldVaultBaseScript { + using FixedPointMathLib for uint256; + + /*////////////////////////////////////////////////////////////// + SCRIPT PARAMETERS + //////////////////////////////////////////////////////////////*/ + + /// @dev maxAceeptableLossPercent - the maximum acceptable loss in percent of the current total assets amount + function maxAceeptableLossPercent() public view virtual returns (uint256) { + return 0.02e18; // 2% + } + + /*//////////////////////////////////////////////////////////////*/ + + function _startMessage() internal pure override returns (string memory) { + return "--Exit all positions script running--"; + } + + function _endMessage() internal pure override returns (string memory) { + return "--Exit all positions script done--"; + } + + function _logScriptParams() internal view override { + super._logScriptParams(); + console2.log("maxAceeptableLossPercent\t", maxAceeptableLossPercent()); + } + + function _execute() internal virtual override { + uint256 totalAssets = vault.totalAssets(); + uint256 minEndTotalAssets = totalAssets.mulWadDown(1e18 - maxAceeptableLossPercent()); + + _logVaultInfo("state before"); + + vm.startBroadcast(keeper); + vault.exitAllPositions(minEndTotalAssets); + vm.stopBroadcast(); + + _logVaultInfo("state after"); + } + + function _logVaultInfo(string memory message) internal view { + console2.log("\t", message); + console2.log("total assets\t\t", vault.totalAssets()); + console2.log("weth profit\t\t", vault.getProfit()); + console2.log("float\t\t\t", assetBalance()); + console2.log("total collateral\t", vault.totalCollateral()); + console2.log("total debt\t\t", vault.totalDebt()); + console2.log("weth invested\t\t", targetTokensInvested()); + } +} diff --git a/script/base/scCrossAssetYieldVaultReallocateScript.sol b/script/base/scCrossAssetYieldVaultReallocateScript.sol new file mode 100644 index 00000000..e5e163dd --- /dev/null +++ b/script/base/scCrossAssetYieldVaultReallocateScript.sol @@ -0,0 +1,122 @@ +// SPDX-License-Identifier: AGPL-3.0 +pragma solidity ^0.8.13; + +import "forge-std/console2.sol"; +import {FixedPointMathLib} from "solmate/utils/FixedPointMathLib.sol"; + +import {scCrossAssetYieldVaultBaseScript} from "./scCrossAssetYieldVaultBaseScript.sol"; +import {scCrossAssetYieldVault} from "src/steth/scCrossAssetYieldVault.sol"; + +abstract contract scCrossAssetYieldVaultReallocateScript is scCrossAssetYieldVaultBaseScript { + using FixedPointMathLib for uint256; + + struct ReallocateData { + uint256 adapterId; + uint256 allocationPercent; + uint256 supplyAmount; + uint256 borrowAmount; + uint256 withdrawAmount; + uint256 repayAmount; + } + + uint256 flashloanFeePercent = 0e18; // currently 0% on balancer + + // script state + ReallocateData[] public reallocateData; + bytes[] multicallData; + uint256 flashLoanAmount; + + function _startMessage() internal pure override returns (string memory) { + return "--reallocate script running--"; + } + + function _endMessage() internal pure override returns (string memory) { + return "--reallocate script done--"; + } + + function _execute() internal override { + _initReallocateData(); + _createMulticallData(); + _checkAllocationPercentages(); + + _logPositions("before reallocate"); + + vm.startBroadcast(keeper); + vault.reallocate(flashLoanAmount, multicallData); + vm.stopBroadcast(); + + _logPositions("after reallocate"); + } + + function _initReallocateData() internal virtual; + function _logPositions(string memory _message) internal virtual; + + function _checkAllocationPercentages() internal view { + uint256 totalAllocationPercent = 0; + for (uint256 i = 0; i < reallocateData.length; i++) { + totalAllocationPercent += reallocateData[i].allocationPercent; + } + + if (totalAllocationPercent != 1e18) { + revert("total allocation percent not 100%"); + } + } + + function _createMulticallData() internal { + for (uint256 i = 0; i < reallocateData.length; i++) { + ReallocateData memory data = reallocateData[i]; + + if (data.withdrawAmount > 0) { + flashLoanAmount += data.repayAmount; + + multicallData.push( + abi.encodeWithSelector(scCrossAssetYieldVault.repay.selector, data.adapterId, data.repayAmount) + ); + multicallData.push( + abi.encodeWithSelector( + scCrossAssetYieldVault.withdraw.selector, data.adapterId, data.withdrawAmount + ) + ); + } + } + + for (uint256 i = 0; i < reallocateData.length; i++) { + ReallocateData memory data = reallocateData[i]; + + if (data.supplyAmount > 0) { + uint256 borrowAmount = data.borrowAmount.mulWadDown(1e18 - flashloanFeePercent); + + multicallData.push( + abi.encodeWithSelector(scCrossAssetYieldVault.supply.selector, data.adapterId, data.supplyAmount) + ); + multicallData.push( + abi.encodeWithSelector(scCrossAssetYieldVault.borrow.selector, data.adapterId, borrowAmount) + ); + } + } + } + + function _createReallocateData(uint256 _adapterId, uint256 _allocationPercent) internal { + ReallocateData memory data; + + data.adapterId = _adapterId; + data.allocationPercent = _allocationPercent; + + uint256 currentAllocation = vault.getCollateral(_adapterId); + uint256 expectedAllocation = vault.totalCollateral().mulWadUp(_allocationPercent); + uint256 currentDebt = vault.getDebt(_adapterId); + uint256 expectedDebt = vault.totalDebt().mulWadUp(_allocationPercent); + + if (currentAllocation > expectedAllocation) { + // we need to withdraw some collateral + data.withdrawAmount = currentAllocation - expectedAllocation; + data.repayAmount = currentDebt - expectedDebt; + } else if (currentAllocation < expectedAllocation) { + // we need to supply some collateral + data.supplyAmount = expectedAllocation - currentAllocation; + data.borrowAmount = expectedDebt - currentDebt; + } + + reallocateData.push(data); + } +} diff --git a/script/base/scCrossAssetYieldVaultRebalanceScript.sol b/script/base/scCrossAssetYieldVaultRebalanceScript.sol new file mode 100644 index 00000000..7a1d89aa --- /dev/null +++ b/script/base/scCrossAssetYieldVaultRebalanceScript.sol @@ -0,0 +1,217 @@ +// SPDX-License-Identifier: AGPL-3.0 +pragma solidity ^0.8.13; + +import "forge-std/console2.sol"; +import "forge-std/Script.sol"; + +import {FixedPointMathLib} from "solmate/utils/FixedPointMathLib.sol"; + +import {scCrossAssetYieldVaultBaseScript} from "./scCrossAssetYieldVaultBaseScript.sol"; +import {scCrossAssetYieldVault} from "src/steth/scCrossAssetYieldVault.sol"; + +abstract contract scCrossAssetYieldVaultRebalanceScript is scCrossAssetYieldVaultBaseScript { + using FixedPointMathLib for uint256; + + error ScriptCannotUseUnsupportedAdapter(uint256 id); + + struct RebalanceData { + uint256 adapterId; + uint256 repayAmount; + uint256 borrowAmount; + uint256 supplyAmount; + uint256 withdrawAmount; + } + + struct AdapterSettings { + uint256 adapterId; + uint256 investableAmountPercent; + uint256 targetLtv; + } + + // script state + AdapterSettings[] adapterSettings; + RebalanceData[] rebalanceDatas; + uint256 disinvestAmount = 0; + bytes[] multicallData; + + uint256 public ltvDiffTolerance = 0.05e18; // 5% + uint256 public maxProfitSellSlippage = 0.01e18; // 1% + + uint256 minProfitToReinvest = _getMinProfitToReinvest(); + + // configuration functions + + function _getMinProfitToReinvest() internal view virtual returns (uint256); + + function _initializeAdapterSettings() internal virtual; + + function _startMessage() internal pure override returns (string memory) { + return "--Rebalance script running--"; + } + + function _endMessage() internal pure override returns (string memory) { + return "--Rebalance script done--"; + } + + function _execute() internal override { + _initializeAdapterSettings(); + _checkAllocationPercentages(); + + uint256 minReceivedFromProfitSelling = _sellProfitIfAboveDefinedMin(); + uint256 minAssetBalance = minReceivedFromProfitSelling + assetBalance(); + uint256 minFloatRequired = totalAssets().mulWadUp(vault.floatPercentage()); + uint256 missingFloat = minFloatRequired > minAssetBalance ? minFloatRequired - minAssetBalance : 0; + uint256 investableAmount = minFloatRequired < minAssetBalance ? minAssetBalance - minFloatRequired : 0; + + _createRebalanceMulticallDataForAllAdapters(investableAmount, missingFloat); + + _logVaultInfo("state before rebalance"); + + vm.startBroadcast(keeper); + vault.rebalance(multicallData); + vm.stopBroadcast(); + + _logVaultInfo("state after rebalance"); + } + + function _checkAllocationPercentages() internal view { + uint256 totalAllocationPercent = 0; + for (uint256 i = 0; i < adapterSettings.length; i++) { + AdapterSettings memory settings = adapterSettings[i]; + totalAllocationPercent += settings.investableAmountPercent; + } + + require(totalAllocationPercent == 1e18, "investable amount percent not 100%"); + } + + function _sellProfitIfAboveDefinedMin() internal returns (uint256) { + uint256 profit = getProfit(); + + if (profit == 0) return 0; + + // account for slippage when swapping target token profits to underlying assets + uint256 minExpectedProfit = targetTokensPriceInAssets(profit).mulWadDown(1e18 - maxProfitSellSlippage); + + // if profit is too small, don't sell & reinvest + if (minExpectedProfit < minProfitToReinvest) return 0; + + multicallData.push(abi.encodeWithSelector(scCrossAssetYieldVault.sellProfit.selector, minExpectedProfit)); + + return minExpectedProfit; + } + + function _createAdapterRebalanceData( + uint256 _adapterId, + uint256 _targetLtv, + uint256 _investableAmount, + uint256 _missingFloat + ) internal { + uint256 collateral = getCollateral(_adapterId); + uint256 debt = getDebt(_adapterId); + + uint256 targetCollateral = collateral + _investableAmount - _missingFloat; + uint256 targetDebt = assetPriceInTargetTokens(targetCollateral).mulWadDown(_targetLtv); + + RebalanceData memory rebalanceData; + rebalanceData.adapterId = _adapterId; + + if (targetCollateral > collateral) { + rebalanceData.supplyAmount = targetCollateral - collateral; + } else if (targetCollateral < collateral) { + rebalanceData.withdrawAmount = collateral - targetCollateral; + } + + uint256 debtUpperBound = targetDebt.mulWadUp(1e18 + ltvDiffTolerance); + uint256 debtLowerBound = targetDebt.mulWadDown(1e18 - ltvDiffTolerance); + + if (debt < debtLowerBound) { + rebalanceData.borrowAmount = targetDebt - debt; + } else if (debt > debtUpperBound) { + uint256 repayAmount = debt - targetDebt; + rebalanceData.repayAmount = repayAmount; + disinvestAmount += repayAmount; + } + + rebalanceDatas.push(rebalanceData); + } + + function _createRebalanceMulticallDataForAllAdapters(uint256 _investableAmount, uint256 _missingFloat) internal { + for (uint256 i = 0; i < adapterSettings.length; i++) { + AdapterSettings memory settings = adapterSettings[i]; + + if ( + !vault.isSupported(settings.adapterId) + && (settings.targetLtv > 0 || settings.investableAmountPercent > 0) + ) { + revert ScriptCannotUseUnsupportedAdapter(settings.adapterId); + } + + _createAdapterRebalanceData( + settings.adapterId, + settings.targetLtv, + _investableAmount.mulWadDown(settings.investableAmountPercent), + _missingFloat.mulWadDown(settings.investableAmountPercent) + ); + } + + _createRebalanceMulticallData(); + } + + function _createRebalanceMulticallData() internal { + if (disinvestAmount > 0) { + multicallData.push(abi.encodeWithSelector(scCrossAssetYieldVault.disinvest.selector, disinvestAmount)); + } + + for (uint256 i = 0; i < rebalanceDatas.length; i++) { + RebalanceData memory rebalanceData = rebalanceDatas[i]; + if (rebalanceData.supplyAmount > 0) { + multicallData.push( + abi.encodeWithSelector( + scCrossAssetYieldVault.supply.selector, rebalanceData.adapterId, rebalanceData.supplyAmount + ) + ); + } + if (rebalanceData.borrowAmount > 0) { + multicallData.push( + abi.encodeWithSelector( + scCrossAssetYieldVault.borrow.selector, rebalanceData.adapterId, rebalanceData.borrowAmount + ) + ); + } + if (rebalanceData.repayAmount > 0) { + multicallData.push( + abi.encodeWithSelector( + scCrossAssetYieldVault.repay.selector, rebalanceData.adapterId, rebalanceData.repayAmount + ) + ); + } + if (rebalanceData.withdrawAmount > 0) { + multicallData.push( + abi.encodeWithSelector( + scCrossAssetYieldVault.withdraw.selector, rebalanceData.adapterId, rebalanceData.withdrawAmount + ) + ); + } + } + } + + function _logVaultInfo(string memory message) internal view { + console2.log("\n\t----------------------------"); + console2.log("\t", message); + console2.log("\t----------------------------"); + console2.log("total assets\t\t", totalAssets()); + console2.log("profit\t\t", getProfit()); + console2.log("float\t\t\t", assetBalance()); + console2.log("total collateral\t", totalCollateral()); + console2.log("total debt\t\t", totalDebt()); + console2.log("invested\t\t", targetTokensInvested()); + console2.log("\t----------------------------"); + } + + function _logScriptParams() internal view virtual override { + super._logScriptParams(); + console2.log("ltv diff tolerance\t", ltvDiffTolerance); + console2.log("min profit\t\t", minProfitToReinvest); + console2.log("max slippage\t\t", maxProfitSellSlippage); + } +} diff --git a/script/v2/DeployScDaiEthMainnet.s.sol b/script/v2/DeployScDaiEthMainnet.s.sol deleted file mode 100644 index c4c07058..00000000 --- a/script/v2/DeployScDaiEthMainnet.s.sol +++ /dev/null @@ -1,80 +0,0 @@ -// // SPDX-License-Identifier: AGPL-3.0 -// pragma solidity ^0.8.13; - -// import "forge-std/console2.sol"; -// import {MainnetAddresses} from "../base/MainnetAddresses.sol"; -// import {MainnetDeployBase} from "../base/MainnetDeployBase.sol"; -// import {scWETHv2} from "../../src/steth/scWETHv2.sol"; -// import {scDAI} from "../../src/steth/scDAI.sol"; -// import {Swapper} from "../../src/steth/Swapper.sol"; -// import {PriceConverter} from "../../src/steth/PriceConverter.sol"; -// import {SparkScDaiAdapter} from "../../src/steth/scDai-adapters/SparkScDaiAdapter.sol"; -// import {IAdapter} from "../../src/steth/IAdapter.sol"; -// import {Constants as C} from "../../src/lib/Constants.sol"; -// import {ERC20} from "solmate/tokens/ERC20.sol"; -// import {ISwapRouter} from "../../src/interfaces/uniswap/ISwapRouter.sol"; - -// import {CREATE3Script} from "../base/CREATE3Script.sol"; - -// contract DeployScDaiMainnet is MainnetDeployBase { -// scWETHv2 scWethV2 = scWETHv2(payable(0x4c406C068106375724275Cbff028770C544a1333)); - -// function run() external { -// console2.log("--Deploy scDAI script running--"); - -// require(address(scWethV2) != address(0), "invalid address for ScWethV2 contract"); - -// _logScriptParams(); - -// vm.startBroadcast(deployerAddress); - -// Swapper swapper = new Swapper(); -// PriceConverter priceConverter = new PriceConverter(MainnetAddresses.MULTISIG); -// IAdapter sparkAdapter = new SparkScDaiAdapter(); - -// console2.log("swapper\t\t", address(swapper)); -// console2.log("priceConverter\t\t", address(priceConverter)); -// console2.log("sparkAdapter\t\t", address(sparkAdapter)); - -// // deploy vault -// scDAI vault = new scDAI(deployerAddress, keeper, scWethV2, priceConverter, swapper); - -// console2.log("scDAI\t\t", address(vault)); - -// vault.addAdapter(sparkAdapter); - -// // initial deposit -// uint256 daiAmount = _swapWethForDai(0.01 ether); -// ERC20(C.DAI).approve(address(vault), daiAmount); -// vault.depositDai(daiAmount, deployerAddress); // 0.01 ether worth of sDAI - -// _transferAdminRoleToMultisig(vault, deployerAddress); - -// vm.stopBroadcast(); - -// console2.log("--Deploy scDAI script done--"); -// } - -// function _logScriptParams() internal view { -// console2.log("\t script params"); -// console2.log("deployer\t\t", address(deployerAddress)); -// console2.log("keeper\t\t", address(keeper)); -// console2.log("scWethV2\t\t", address(scWethV2)); -// } - -// function _swapWethForDai(uint256 _amount) internal returns (uint256 amountOut) { -// weth.deposit{value: _amount}(); - -// weth.approve(C.UNISWAP_V3_SWAP_ROUTER, _amount); - -// ISwapRouter.ExactInputParams memory params = ISwapRouter.ExactInputParams({ -// path: abi.encodePacked(C.WETH, uint24(500), C.USDC, uint24(100), C.DAI), -// recipient: deployerAddress, -// deadline: block.timestamp + 1000, -// amountIn: _amount, -// amountOutMinimum: 0 -// }); - -// amountOut = ISwapRouter(C.UNISWAP_V3_SWAP_ROUTER).exactInput(params); -// } -// } diff --git a/script/v2/DeployAdapterEthMainnet.s.sol b/script/v2/deploy/DeployAdapterEthMainnet.s.sol similarity index 55% rename from script/v2/DeployAdapterEthMainnet.s.sol rename to script/v2/deploy/DeployAdapterEthMainnet.s.sol index 7437d738..62009677 100644 --- a/script/v2/DeployAdapterEthMainnet.s.sol +++ b/script/v2/deploy/DeployAdapterEthMainnet.s.sol @@ -1,12 +1,12 @@ // SPDX-License-Identifier: AGPL-3.0 pragma solidity ^0.8.13; -import {MainnetDeployBase} from "../base/MainnetDeployBase.sol"; -import {MorphoAaveV3ScWethAdapter} from "../../src/steth/scWethV2-adapters/MorphoAaveV3ScWethAdapter.sol"; -import {AaveV3ScWethAdapter} from "../../src/steth/scWethV2-adapters/AaveV3ScWethAdapter.sol"; -import {AaveV3ScUsdcAdapter} from "../../src/steth/scUsdcV2-adapters/AaveV3ScUsdcAdapter.sol"; +import {MainnetDeployBase} from "script/base/MainnetDeployBase.sol"; +import {MorphoAaveV3ScWethAdapter} from "src/steth/scWethV2-adapters/MorphoAaveV3ScWethAdapter.sol"; +import {AaveV3ScWethAdapter} from "src/steth/scWethV2-adapters/AaveV3ScWethAdapter.sol"; +import {AaveV3ScUsdcAdapter} from "src/steth/scUsdcV2-adapters/AaveV3ScUsdcAdapter.sol"; -contract DeployScript is MainnetDeployBase { +contract DeployAdapterEthMainnet is MainnetDeployBase { // note: to deploy any other adapter simply change the return type and the line which instantiates the adapter function run() external diff --git a/script/DeployLeveragedEthForked.s.sol b/script/v2/deploy/DeployLeveragedEthForked.s.sol similarity index 86% rename from script/DeployLeveragedEthForked.s.sol rename to script/v2/deploy/DeployLeveragedEthForked.s.sol index 475569d9..8fda63e9 100644 --- a/script/DeployLeveragedEthForked.s.sol +++ b/script/v2/deploy/DeployLeveragedEthForked.s.sol @@ -3,26 +3,21 @@ pragma solidity ^0.8.13; import "forge-std/console2.sol"; import "forge-std/Test.sol"; -import "forge-std/StdStorage.sol"; -import {DeployLeveragedEth} from "./base/DeployLeveragedEth.sol"; -import {FixedPointMathLib} from "solmate/utils/FixedPointMathLib.sol"; -import {MockWETH} from "../test/mocks/MockWETH.sol"; -import {MockUSDC} from "../test/mocks/MockUSDC.sol"; -import {Constants as C} from "../src/lib/Constants.sol"; +import {SwapperLib} from "src/lib/SwapperLib.sol"; +import {DeploymentConstants as DC} from "src/lib/DeploymentConstants.sol"; +import {DeployScWethV2AndScUsdcV2} from "script/base/DeployScWethV2AndScUsdcV2.sol"; import {ERC20} from "solmate/tokens/ERC20.sol"; -import {sc4626} from "../src/sc4626.sol"; -import {scWETH} from "../src/steth/scWETH.sol"; -import {scUSDC} from "../src/steth/scUSDC.sol"; +import {sc4626} from "src/sc4626.sol"; +import {scWETH} from "src/steth/scWETH.sol"; +import {scUSDC} from "src/steth/scUSDC.sol"; /** * Deployment & Fixture exection script to be run against staging forked node. * i.e.: ` forge script script/DeployLeveragedEthForked.s.sol * --rpc-url=http://forked-node` */ -contract DeployLeveragedEthForked is DeployLeveragedEth, Test { - using stdStorage for StdStorage; - +contract DeployLeveragedEthForked is DeployScWethV2AndScUsdcV2, Test { uint256 public constant INITIAL_WETH_DEPOSIT = 10e18; uint256 public constant INITIAL_WETH_WITHDRAW = 1e18; uint256 public constant INITIAL_USDC_DEPOSIT = 100e6; @@ -30,6 +25,9 @@ contract DeployLeveragedEthForked is DeployLeveragedEth, Test { uint256 public constant INITIAL_WETH_FUNDING = 10000e18; uint256 public constant INITIAL_USDC_FUNDING = 10000e6; + address alice = DC.ALICE; + address bob = DC.BOB; + function run() external { _deploy(); _fixtures(); @@ -142,7 +140,7 @@ contract DeployLeveragedEthForked is DeployLeveragedEth, Test { weth.deposit{value: 1000 ether}(); console2.log("swap 1000 eth for USDC"); - _swapWethForUsdc(1000 ether); + SwapperLib._uniswapSwapExactInput(address(weth), address(usdc), keeper, 1000 ether, 0, 500); // 0.05% fee vm.stopBroadcast(); } diff --git a/script/DeployLeveragedEthMainnet.s.sol b/script/v2/deploy/DeployLeveragedEthMainnet.s.sol similarity index 60% rename from script/DeployLeveragedEthMainnet.s.sol rename to script/v2/deploy/DeployLeveragedEthMainnet.s.sol index abb02941..b48cdf18 100644 --- a/script/DeployLeveragedEthMainnet.s.sol +++ b/script/v2/deploy/DeployLeveragedEthMainnet.s.sol @@ -3,14 +3,12 @@ pragma solidity ^0.8.13; import "forge-std/console2.sol"; -import {DeployLeveragedEth} from "./base/DeployLeveragedEth.sol"; -import {scWETH} from "../src/steth/scWETH.sol"; -import {scUSDC} from "../src/steth/scUSDC.sol"; -import {sc4626} from "../src/sc4626.sol"; -import {Constants as C} from "../src/lib/Constants.sol"; -import {ISwapRouter} from "../src/interfaces/uniswap/ISwapRouter.sol"; - -contract DeployLeveragedEthMainnet is DeployLeveragedEth { +import {SwapperLib} from "src/lib/SwapperLib.sol"; +import {DeployScWethV2AndScUsdcV2} from "script/base/DeployScWethV2AndScUsdcV2.sol"; +import {scWETH} from "src/steth/scWETH.sol"; +import {scUSDC} from "src/steth/scUSDC.sol"; + +contract DeployLeveragedEthMainnet is DeployScWethV2AndScUsdcV2 { function run() external { _deploy(); @@ -18,7 +16,7 @@ contract DeployLeveragedEthMainnet is DeployLeveragedEth { } function _postDeployment() internal { - vm.startBroadcast(deployerPrivateKey); + vm.startBroadcast(deployerAddress); // scWETH console2.log("eth deposited for weth"); @@ -27,7 +25,7 @@ contract DeployLeveragedEthMainnet is DeployLeveragedEth { // scUSDC console2.log("eth deposited for weth to swap for usdc"); - _swapWethForUsdc(0.01 ether); + SwapperLib._uniswapSwapExactInput(address(weth), address(usdc), deployerAddress, 0.01 ether, 0, 500); console2.log("eth swapped for USDC"); _deposit(scUsdc, usdc.balanceOf(address(deployerAddress))); // 0.01 ether worth of USDC console2.log("usdc deposited into usdcContract"); diff --git a/script/v2/DeployPriceConverterEthMainnet.s.sol b/script/v2/deploy/DeployPriceConverterEthMainnet.s.sol similarity index 51% rename from script/v2/DeployPriceConverterEthMainnet.s.sol rename to script/v2/deploy/DeployPriceConverterEthMainnet.s.sol index e6cb6a76..1ff598ce 100644 --- a/script/v2/DeployPriceConverterEthMainnet.s.sol +++ b/script/v2/deploy/DeployPriceConverterEthMainnet.s.sol @@ -1,11 +1,11 @@ // SPDX-License-Identifier: AGPL-3.0 pragma solidity ^0.8.13; -import {MainnetDeployBase} from "../base/MainnetDeployBase.sol"; -import {MainnetAddresses} from "../base/MainnetAddresses.sol"; -import {PriceConverter} from "../../src/steth/priceConverter/PriceConverter.sol"; +import {MainnetDeployBase} from "script/base/MainnetDeployBase.sol"; +import {MainnetAddresses} from "script/base/MainnetAddresses.sol"; +import {PriceConverter} from "src/steth/priceConverter/PriceConverter.sol"; -contract DeployScript is MainnetDeployBase { +contract DeployPriceConverterEthMainnet is MainnetDeployBase { function run() external returns (PriceConverter priceConverter) { vm.startBroadcast(deployerAddress); diff --git a/script/v2/DeployScUsdcV2EthMainnet.s.sol b/script/v2/deploy/DeployScUsdcV2EthMainnet.s.sol similarity index 52% rename from script/v2/DeployScUsdcV2EthMainnet.s.sol rename to script/v2/deploy/DeployScUsdcV2EthMainnet.s.sol index 4865be7a..3be295c5 100644 --- a/script/v2/DeployScUsdcV2EthMainnet.s.sol +++ b/script/v2/deploy/DeployScUsdcV2EthMainnet.s.sol @@ -2,24 +2,25 @@ pragma solidity ^0.8.13; import "forge-std/console2.sol"; -import {MainnetAddresses} from "../base/MainnetAddresses.sol"; -import {MainnetDeployBase} from "../base/MainnetDeployBase.sol"; -import {scWETHv2} from "../../src/steth/scWETHv2.sol"; -import {scUSDCv2} from "../../src/steth/scUSDCv2.sol"; -import {Swapper} from "../../src/steth/swapper/Swapper.sol"; -import {PriceConverter} from "../../src/steth/priceConverter/PriceConverter.sol"; -import {UsdcWethPriceConverter} from "../../src/steth/priceConverter/UsdcWethPriceConverter.sol"; -import {AaveV3ScUsdcAdapter} from "../../src/steth/scUsdcV2-adapters/AaveV3ScUsdcAdapter.sol"; -import {MorphoAaveV3ScUsdcAdapter} from "../../src/steth/scUsdcV2-adapters/MorphoAaveV3ScUsdcAdapter.sol"; -import {AaveV2ScUsdcAdapter} from "../../src/steth/scUsdcV2-adapters/AaveV2ScUsdcAdapter.sol"; -import {UsdcWethSwapper} from "../../src/steth/swapper/UsdcWethSwapper.sol"; - -contract DeployScript is MainnetDeployBase { - scWETHv2 scWethV2 = scWETHv2(payable(MainnetAddresses.SCWETHV2)); - UsdcWethSwapper swapper = new UsdcWethSwapper(); - UsdcWethPriceConverter priceConverter = new UsdcWethPriceConverter(); +import {SwapperLib} from "src/lib/SwapperLib.sol"; +import {MainnetAddresses} from "script/base/MainnetAddresses.sol"; +import {MainnetDeployBase} from "script/base/MainnetDeployBase.sol"; +import {scWETHv2} from "src/steth/scWETHv2.sol"; +import {scUSDCv2} from "src/steth/scUSDCv2.sol"; +import {Swapper} from "src/steth/swapper/Swapper.sol"; +import {UsdcWethPriceConverter} from "src/steth/priceConverter/UsdcWethPriceConverter.sol"; +import {AaveV3ScUsdcAdapter} from "src/steth/scUsdcV2-adapters/AaveV3ScUsdcAdapter.sol"; +import {MorphoAaveV3ScUsdcAdapter} from "src/steth/scUsdcV2-adapters/MorphoAaveV3ScUsdcAdapter.sol"; +import {AaveV2ScUsdcAdapter} from "src/steth/scUsdcV2-adapters/AaveV2ScUsdcAdapter.sol"; +import {UsdcWethSwapper} from "src/steth/swapper/UsdcWethSwapper.sol"; + +contract DeployScUsdcV2EthMainnet is MainnetDeployBase { function run() external returns (scUSDCv2 scUsdcV2) { + scWETHv2 scWethV2 = scWETHv2(payable(MainnetAddresses.SCWETHV2)); + UsdcWethSwapper swapper = new UsdcWethSwapper(); + UsdcWethPriceConverter priceConverter = new UsdcWethPriceConverter(); + require(address(swapper) != address(0), "invalid address for Swapper contract"); require(address(priceConverter) != address(0), "invalid address for PriceConverter contract"); require(address(scWethV2) != address(0), "invalid address for ScWethV2 contract"); @@ -43,10 +44,12 @@ contract DeployScript is MainnetDeployBase { console2.log("scUSDCv2 AaveV3Adapter:", address(aaveV3Adapter)); // initial deposit - uint256 usdcAmount = _swapWethForUsdc(0.01 ether); + uint256 usdcAmount = + SwapperLib._uniswapSwapExactInput(address(weth), address(usdc), deployerAddress, 0.01 ether, 0, 500); + _deposit(scUsdcV2, usdcAmount); // 0.01 ether worth of USDC - _transferAdminRoleToMultisig(scUsdcV2, deployerAddress); + _transferAdminRoleToMultisig(scUsdcV2); vm.stopBroadcast(); } diff --git a/script/v2/deploy/DeployScUsds.s.sol b/script/v2/deploy/DeployScUsds.s.sol new file mode 100644 index 00000000..098ae204 --- /dev/null +++ b/script/v2/deploy/DeployScUsds.s.sol @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: AGPL-3.0 +pragma solidity ^0.8.13; + +import "forge-std/console2.sol"; + +import {Constants as C} from "src/lib/Constants.sol"; +import {SwapperLib} from "src/lib/SwapperLib.sol"; +import {MainnetAddresses} from "script/base/MainnetAddresses.sol"; +import {MainnetDeployBase} from "script/base/MainnetDeployBase.sol"; +import {scUSDS} from "src/steth/scUSDS.sol"; + +contract DeployScUsds is MainnetDeployBase { + function run() external returns (scUSDS scUsds) { + vm.startBroadcast(deployerAddress); + + // step 1 - deploy scUSDS wtih scSDAI target + scUsds = scUSDS( + deployWithCreate3( + type(scUSDS).name, abi.encodePacked(type(scUSDS).creationCode, abi.encode(MainnetAddresses.SCSDAI)) + ) + ); + + // step 2 - deposit initial funds + require(deployerAddress.balance >= 0.01 ether, "insufficient balance"); + weth.deposit{value: 0.01 ether}(); // wrap 0.01 ETH into WETH + bytes memory swapPath = abi.encodePacked(C.WETH, uint24(500), C.USDC, uint24(100), C.DAI, uint24(3000), C.USDS); + uint256 initialDeposit = + SwapperLib._uniswapSwapExactInputMultihop(C.WETH, 0.01 ether, 0, swapPath, deployerAddress); + // deposit 0.01 ether worth of USDS + _deposit(scUsds, initialDeposit); + + vm.stopBroadcast(); + } +} diff --git a/script/v2/deploy/DeployScUsdt.s.sol b/script/v2/deploy/DeployScUsdt.s.sol new file mode 100644 index 00000000..5105facd --- /dev/null +++ b/script/v2/deploy/DeployScUsdt.s.sol @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: AGPL-3.0 +pragma solidity ^0.8.13; + +import "forge-std/console2.sol"; + +import {Constants as C} from "src/lib/Constants.sol"; +import {SwapperLib} from "src/lib/SwapperLib.sol"; +import {MainnetAddresses} from "script/base/MainnetAddresses.sol"; +import {MainnetDeployBase} from "script/base/MainnetDeployBase.sol"; +import {scUSDT} from "src/steth/scUSDT.sol"; +import {UsdtWethPriceConverter} from "src/steth/priceConverter/UsdtWethPriceConverter.sol"; +import {AaveV3ScUsdtAdapter} from "src/steth/scUsdt-adapters/AaveV3ScUsdtAdapter.sol"; +import {UsdtWethSwapper} from "src/steth/swapper/UsdtWethSwapper.sol"; + +contract DeployScUsdt is MainnetDeployBase { + function run() external returns (scUSDT scUsdt) { + vm.startBroadcast(deployerAddress); + + /// step1 - deploy adapter, swapper, and price converter + address aaveV3Adapter = + deployWithCreate3(type(AaveV3ScUsdtAdapter).name, abi.encodePacked(type(AaveV3ScUsdtAdapter).creationCode)); + address swapper = + deployWithCreate3(type(UsdtWethSwapper).name, abi.encodePacked(type(UsdtWethSwapper).creationCode)); + address priceConverter = deployWithCreate3( + type(UsdtWethPriceConverter).name, abi.encodePacked(type(UsdtWethPriceConverter).creationCode) + ); + + // step2 - deploy scUSDT & add adapter + scUsdt = scUSDT( + deployWithCreate3( + type(scUSDT).name, + abi.encodePacked( + type(scUSDT).creationCode, + abi.encode(deployerAddress, keeper, MainnetAddresses.SCWETHV2, priceConverter, swapper) + ) + ) + ); + + scUsdt.addAdapter(AaveV3ScUsdtAdapter(aaveV3Adapter)); + + // step3 - deposit initial funds & transfer admin role to multisig + require(deployerAddress.balance >= 0.01 ether, "insufficient balance"); + weth.deposit{value: 0.01 ether}(); // wrap 0.01 ETH into WETH + uint256 initialDeposit = + SwapperLib._uniswapSwapExactInput(address(weth), C.USDT, deployerAddress, 0.01 ether, 0, 500); + // deposit 0.01 ether worth of USDT + _deposit(scUsdt, initialDeposit); + + _transferAdminRoleToMultisig(scUsdt); + + vm.stopBroadcast(); + } +} diff --git a/script/v2/DeployScWethV2EthMainnet.s.sol b/script/v2/deploy/DeployScWethV2EthMainnet.s.sol similarity index 69% rename from script/v2/DeployScWethV2EthMainnet.s.sol rename to script/v2/deploy/DeployScWethV2EthMainnet.s.sol index 65a95230..466bf57d 100644 --- a/script/v2/DeployScWethV2EthMainnet.s.sol +++ b/script/v2/deploy/DeployScWethV2EthMainnet.s.sol @@ -2,14 +2,14 @@ pragma solidity ^0.8.13; import "forge-std/console2.sol"; -import {MainnetAddresses} from "../base/MainnetAddresses.sol"; -import {MainnetDeployBase} from "../base/MainnetDeployBase.sol"; -import {scWETHv2} from "../../src/steth/scWETHv2.sol"; -import {Swapper} from "../../src/steth/swapper/Swapper.sol"; -import {PriceConverter} from "../../src/steth/priceConverter/PriceConverter.sol"; -import {AaveV3ScWethAdapter} from "../../src/steth/scWethV2-adapters/AaveV3ScWethAdapter.sol"; -import {MorphoAaveV3ScWethAdapter} from "../../src/steth/scWethV2-adapters/MorphoAaveV3ScWethAdapter.sol"; -import {CompoundV3ScWethAdapter} from "../../src/steth/scWethV2-adapters/CompoundV3ScWethAdapter.sol"; +import {MainnetAddresses} from "script/base/MainnetAddresses.sol"; +import {MainnetDeployBase} from "script/base/MainnetDeployBase.sol"; +import {scWETHv2} from "src/steth/scWETHv2.sol"; +import {Swapper} from "src/steth/swapper/Swapper.sol"; +import {PriceConverter} from "src/steth/priceConverter/PriceConverter.sol"; +import {AaveV3ScWethAdapter} from "src/steth/scWethV2-adapters/AaveV3ScWethAdapter.sol"; +import {MorphoAaveV3ScWethAdapter} from "src/steth/scWethV2-adapters/MorphoAaveV3ScWethAdapter.sol"; +import {CompoundV3ScWethAdapter} from "src/steth/scWethV2-adapters/CompoundV3ScWethAdapter.sol"; contract DeployScript is MainnetDeployBase { Swapper swapper = Swapper(MainnetAddresses.SWAPPER); @@ -41,7 +41,7 @@ contract DeployScript is MainnetDeployBase { weth.deposit{value: 0.01 ether}(); // wrap 0.01 ETH into WETH _deposit(scWethV2, 0.01 ether); // 0.01 WETH - _transferAdminRoleToMultisig(scWethV2, deployerAddress); + _transferAdminRoleToMultisig(scWethV2); _setTreasury(scWethV2, MainnetAddresses.TREASURY); diff --git a/script/v2/DeployScWethV2KeeperMainnet.s.sol b/script/v2/deploy/DeployScWethV2KeeperMainnet.s.sol similarity index 59% rename from script/v2/DeployScWethV2KeeperMainnet.s.sol rename to script/v2/deploy/DeployScWethV2KeeperMainnet.s.sol index b53b591c..b12d0952 100644 --- a/script/v2/DeployScWethV2KeeperMainnet.s.sol +++ b/script/v2/deploy/DeployScWethV2KeeperMainnet.s.sol @@ -3,10 +3,10 @@ pragma solidity ^0.8.13; import "forge-std/console2.sol"; -import {MainnetAddresses} from "../base/MainnetAddresses.sol"; -import {MainnetDeployBase} from "../base/MainnetDeployBase.sol"; -import {scWETHv2} from "../../src/steth/scWETHv2.sol"; -import {scWETHv2Keeper} from "../../src/steth/scWETHv2Keeper.sol"; +import {MainnetAddresses} from "script/base/MainnetAddresses.sol"; +import {MainnetDeployBase} from "script/base/MainnetDeployBase.sol"; +import {scWETHv2} from "src/steth/scWETHv2.sol"; +import {scWETHv2Keeper} from "src/steth/scWETHv2Keeper.sol"; contract DeployScWethV2KeeperMainnet is MainnetDeployBase { function run() external returns (scWETHv2Keeper deployed) { @@ -20,9 +20,4 @@ contract DeployScWethV2KeeperMainnet is MainnetDeployBase { console2.log("---Deploy scWETHv2Keeper script done ---"); } - - function _init() internal override { - deployerPrivateKey = uint256(vm.envBytes32("PRIVATE_KEY")); - deployerAddress = vm.rememberKey(deployerPrivateKey); - } } diff --git a/script/v2/DeploySwapperEthMainnet.s.sol b/script/v2/deploy/DeploySwapperEthMainnet.s.sol similarity index 56% rename from script/v2/DeploySwapperEthMainnet.s.sol rename to script/v2/deploy/DeploySwapperEthMainnet.s.sol index 1e9dbcab..51ddd622 100644 --- a/script/v2/DeploySwapperEthMainnet.s.sol +++ b/script/v2/deploy/DeploySwapperEthMainnet.s.sol @@ -1,10 +1,10 @@ // SPDX-License-Identifier: AGPL-3.0 pragma solidity ^0.8.13; -import {MainnetDeployBase} from "../base/MainnetDeployBase.sol"; -import {Swapper} from "../../src/steth/swapper/Swapper.sol"; +import {MainnetDeployBase} from "script/base/MainnetDeployBase.sol"; +import {Swapper} from "src/steth/swapper/Swapper.sol"; -contract DeployScript is MainnetDeployBase { +contract DeploySwapperEthMainnet is MainnetDeployBase { function run() external returns (Swapper swapper) { vm.startBroadcast(deployerAddress); diff --git a/script/v2/DeployV2LeveragedEthMainnet.s.sol b/script/v2/deploy/DeployV2LeveragedEthMainnet.s.sol similarity index 63% rename from script/v2/DeployV2LeveragedEthMainnet.s.sol rename to script/v2/deploy/DeployV2LeveragedEthMainnet.s.sol index 637c6a5f..2f73f3a2 100644 --- a/script/v2/DeployV2LeveragedEthMainnet.s.sol +++ b/script/v2/deploy/DeployV2LeveragedEthMainnet.s.sol @@ -2,31 +2,31 @@ pragma solidity ^0.8.13; import "forge-std/console2.sol"; -import {CREATE3Script} from "../base/CREATE3Script.sol"; import {ERC20} from "solmate/tokens/ERC20.sol"; import {WETH} from "solmate/tokens/WETH.sol"; import {AccessControl} from "openzeppelin-contracts/access/AccessControl.sol"; -import {Constants as C} from "../../src/lib/Constants.sol"; -import {ISwapRouter} from "../../src/interfaces/uniswap/ISwapRouter.sol"; -import {sc4626} from "../../src/sc4626.sol"; -import {scWETHv2} from "../../src/steth/scWETHv2.sol"; -import {scUSDCv2} from "../../src/steth/scUSDCv2.sol"; -import {Swapper} from "../../src/steth/swapper/Swapper.sol"; -import {PriceConverter} from "../../src/steth/priceConverter/PriceConverter.sol"; -import {UsdcWethPriceConverter} from "../../src/steth/priceConverter/UsdcWethPriceConverter.sol"; -import {AaveV3ScWethAdapter} from "../../src/steth/scWethV2-adapters/AaveV3ScWethAdapter.sol"; -import {CompoundV3ScWethAdapter} from "../../src/steth/scWethV2-adapters/CompoundV3ScWethAdapter.sol"; -import {MorphoAaveV3ScWethAdapter} from "../../src/steth/scWethV2-adapters/MorphoAaveV3ScWethAdapter.sol"; -import {AaveV3ScUsdcAdapter} from "../../src/steth/scUsdcV2-adapters/AaveV3ScUsdcAdapter.sol"; -import {AaveV2ScUsdcAdapter} from "../../src/steth/scUsdcV2-adapters/AaveV2ScUsdcAdapter.sol"; -import {MorphoAaveV3ScUsdcAdapter} from "../../src/steth/scUsdcV2-adapters/MorphoAaveV3ScUsdcAdapter.sol"; -import {MainnetDeployBase} from "../base/MainnetDeployBase.sol"; -import {UsdcWethSwapper} from "../../src/steth/swapper/UsdcWethSwapper.sol"; - -contract DeployScript is MainnetDeployBase { +import {SwapperLib} from "src/lib/SwapperLib.sol"; +import {MainnetDeployBase} from "script/base/MainnetDeployBase.sol"; +import {Constants as C} from "src/lib/Constants.sol"; +import {ISwapRouter} from "src/interfaces/uniswap/ISwapRouter.sol"; +import {sc4626} from "src/sc4626.sol"; +import {scWETHv2} from "src/steth/scWETHv2.sol"; +import {scUSDCv2} from "src/steth/scUSDCv2.sol"; +import {Swapper} from "src/steth/swapper/Swapper.sol"; +import {PriceConverter} from "src/steth/priceConverter/PriceConverter.sol"; +import {UsdcWethPriceConverter} from "src/steth/priceConverter/UsdcWethPriceConverter.sol"; +import {AaveV3ScWethAdapter} from "src/steth/scWethV2-adapters/AaveV3ScWethAdapter.sol"; +import {CompoundV3ScWethAdapter} from "src/steth/scWethV2-adapters/CompoundV3ScWethAdapter.sol"; +import {MorphoAaveV3ScWethAdapter} from "src/steth/scWethV2-adapters/MorphoAaveV3ScWethAdapter.sol"; +import {AaveV3ScUsdcAdapter} from "src/steth/scUsdcV2-adapters/AaveV3ScUsdcAdapter.sol"; +import {AaveV2ScUsdcAdapter} from "src/steth/scUsdcV2-adapters/AaveV2ScUsdcAdapter.sol"; +import {MorphoAaveV3ScUsdcAdapter} from "src/steth/scUsdcV2-adapters/MorphoAaveV3ScUsdcAdapter.sol"; +import {UsdcWethSwapper} from "src/steth/swapper/UsdcWethSwapper.sol"; + +contract DeployV2LeveragedEthMainnet is MainnetDeployBase { function run() external returns (scWETHv2 scWethV2, scUSDCv2 scUsdcV2) { - vm.startBroadcast(deployerPrivateKey); + vm.startBroadcast(deployerAddress); Swapper swapper = new Swapper(); UsdcWethSwapper scUsdcSwapper = new UsdcWethSwapper(); @@ -35,7 +35,7 @@ contract DeployScript is MainnetDeployBase { UsdcWethPriceConverter usdcPriceConverter = new UsdcWethPriceConverter(); console2.log("PriceConverter:", address(priceConverter)); - _transferAdminRoleToMultisig(priceConverter, deployerAddress); + _transferAdminRoleToMultisig(priceConverter); scWethV2 = _deployScWethV2(priceConverter, swapper); @@ -60,7 +60,7 @@ contract DeployScript is MainnetDeployBase { weth.deposit{value: 0.01 ether}(); // wrap 0.01 ETH into WETH _deposit(vault, 0.01 ether); // 0.01 WETH - _transferAdminRoleToMultisig(vault, deployerAddress); + _transferAdminRoleToMultisig(vault); console2.log("scWethV2 vault:", address(vault)); console2.log("scWethV2 AaveV3Adapter:", address(aaveV3Adapter)); @@ -84,10 +84,12 @@ contract DeployScript is MainnetDeployBase { MorphoAaveV3ScUsdcAdapter morphoAdapter = new MorphoAaveV3ScUsdcAdapter(); vault.addAdapter(morphoAdapter); - uint256 usdcAmount = _swapWethForUsdc(0.01 ether); + uint256 usdcAmount = + SwapperLib._uniswapSwapExactInput(address(weth), address(usdc), deployerAddress, 0.01 ether, 0, 500); + _deposit(vault, usdcAmount); // 0.01 ether worth of USDC - _transferAdminRoleToMultisig(vault, deployerAddress); + _transferAdminRoleToMultisig(vault); console2.log("scUSDCv2 vault:", address(vault)); console2.log("scUSDCv2 AaveV3Adapter:", address(aaveV3Adapter)); diff --git a/script/v2/RedeployScUsdcV2EthMainnet.s.sol b/script/v2/deploy/RedeployScUsdcV2EthMainnet.s.sol similarity index 71% rename from script/v2/RedeployScUsdcV2EthMainnet.s.sol rename to script/v2/deploy/RedeployScUsdcV2EthMainnet.s.sol index 3e839a7e..0eb8f7c5 100644 --- a/script/v2/RedeployScUsdcV2EthMainnet.s.sol +++ b/script/v2/deploy/RedeployScUsdcV2EthMainnet.s.sol @@ -2,19 +2,19 @@ pragma solidity ^0.8.13; import "forge-std/console2.sol"; -import {MainnetAddresses} from "../base/MainnetAddresses.sol"; -import {MainnetDeployBase} from "../base/MainnetDeployBase.sol"; -import {scWETHv2} from "../../src/steth/scWETHv2.sol"; -import {scUSDCv2} from "../../src/steth/scUSDCv2.sol"; -import {Swapper} from "../../src/steth/swapper/Swapper.sol"; -import {PriceConverter} from "../../src/steth/priceConverter/PriceConverter.sol"; -import {UsdcWethPriceConverter} from "../../src/steth/priceConverter/UsdcWethPriceConverter.sol"; -import {AaveV3ScUsdcAdapter} from "../../src/steth/scUsdcV2-adapters/AaveV3ScUsdcAdapter.sol"; -import {MorphoAaveV3ScUsdcAdapter} from "../../src/steth/scUsdcV2-adapters/MorphoAaveV3ScUsdcAdapter.sol"; -import {AaveV2ScUsdcAdapter} from "../../src/steth/scUsdcV2-adapters/AaveV2ScUsdcAdapter.sol"; -import {UsdcWethSwapper} from "../../src/steth/swapper/UsdcWethSwapper.sol"; - -contract RedeployScript is MainnetDeployBase { + +import {SwapperLib} from "src/lib/SwapperLib.sol"; +import {MainnetAddresses} from "script/base/MainnetAddresses.sol"; +import {MainnetDeployBase} from "script/base/MainnetDeployBase.sol"; +import {scWETHv2} from "src/steth/scWETHv2.sol"; +import {scUSDCv2} from "src/steth/scUSDCv2.sol"; +import {UsdcWethPriceConverter} from "src/steth/priceConverter/UsdcWethPriceConverter.sol"; +import {AaveV3ScUsdcAdapter} from "src/steth/scUsdcV2-adapters/AaveV3ScUsdcAdapter.sol"; +import {MorphoAaveV3ScUsdcAdapter} from "src/steth/scUsdcV2-adapters/MorphoAaveV3ScUsdcAdapter.sol"; +import {AaveV2ScUsdcAdapter} from "src/steth/scUsdcV2-adapters/AaveV2ScUsdcAdapter.sol"; +import {UsdcWethSwapper} from "src/steth/swapper/UsdcWethSwapper.sol"; + +contract RedeployScUsdcV2EthMainnet is MainnetDeployBase { // @note: change scWethV2 to the address of the deployed scWethV2 contract scWETHv2 scWethV2 = scWETHv2(payable(vm.envOr("SC_WETH_V2", address(0)))); @@ -54,10 +54,12 @@ contract RedeployScript is MainnetDeployBase { } // initial deposit - uint256 usdcAmount = _swapWethForUsdc(0.01 ether); + uint256 usdcAmount = + SwapperLib._uniswapSwapExactInput(address(weth), address(usdc), deployerAddress, 0.01 ether, 0, 500); + _deposit(scUsdcV2, usdcAmount); // 0.01 ether worth of USDC - _transferAdminRoleToMultisig(scUsdcV2, deployerAddress); + _transferAdminRoleToMultisig(scUsdcV2); vm.stopBroadcast(); diff --git a/script/v2/RedeployScWethV2EthMainnet.s.sol b/script/v2/deploy/RedeployScWethV2EthMainnet.s.sol similarity index 71% rename from script/v2/RedeployScWethV2EthMainnet.s.sol rename to script/v2/deploy/RedeployScWethV2EthMainnet.s.sol index dc280b3f..c55e27eb 100644 --- a/script/v2/RedeployScWethV2EthMainnet.s.sol +++ b/script/v2/deploy/RedeployScWethV2EthMainnet.s.sol @@ -2,14 +2,14 @@ pragma solidity ^0.8.13; import "forge-std/console2.sol"; -import {MainnetAddresses} from "../base/MainnetAddresses.sol"; -import {MainnetDeployBase} from "../base/MainnetDeployBase.sol"; -import {scWETHv2} from "../../src/steth/scWETHv2.sol"; -import {Swapper} from "../../src/steth/swapper/Swapper.sol"; -import {PriceConverter} from "../../src/steth/priceConverter/PriceConverter.sol"; -import {AaveV3ScWethAdapter} from "../../src/steth/scWethV2-adapters/AaveV3ScWethAdapter.sol"; -import {CompoundV3ScWethAdapter} from "../../src/steth/scWethV2-adapters/CompoundV3ScWethAdapter.sol"; -import {MorphoAaveV3ScWethAdapter} from "../../src/steth/scWethV2-adapters/MorphoAaveV3ScWethAdapter.sol"; +import {MainnetAddresses} from "script/base/MainnetAddresses.sol"; +import {MainnetDeployBase} from "script/base/MainnetDeployBase.sol"; +import {scWETHv2} from "src/steth/scWETHv2.sol"; +import {Swapper} from "src/steth/swapper/Swapper.sol"; +import {PriceConverter} from "src/steth/priceConverter/PriceConverter.sol"; +import {AaveV3ScWethAdapter} from "src/steth/scWethV2-adapters/AaveV3ScWethAdapter.sol"; +import {CompoundV3ScWethAdapter} from "src/steth/scWethV2-adapters/CompoundV3ScWethAdapter.sol"; +import {MorphoAaveV3ScWethAdapter} from "src/steth/scWethV2-adapters/MorphoAaveV3ScWethAdapter.sol"; contract RedeployScript is MainnetDeployBase { Swapper swapper = Swapper(MainnetAddresses.SWAPPER); @@ -46,7 +46,7 @@ contract RedeployScript is MainnetDeployBase { _setTreasury(scWethV2, MainnetAddresses.TREASURY); - _transferAdminRoleToMultisig(scWethV2, deployerAddress); + _transferAdminRoleToMultisig(scWethV2); vm.stopBroadcast(); } diff --git a/script/v2/keeper-actions/ExitAllPositionsScSDai.s.sol b/script/v2/keeper-actions/ExitAllPositionsScSDai.s.sol new file mode 100644 index 00000000..627e5fb0 --- /dev/null +++ b/script/v2/keeper-actions/ExitAllPositionsScSDai.s.sol @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: AGPL-3.0 +pragma solidity ^0.8.13; + +import "forge-std/console2.sol"; + +import {scCrossAssetYieldVault} from "src/steth/scCrossAssetYieldVault.sol"; +import {scCrossAssetYieldVaultExitAllPositionsScript} from + "script/base/scCrossAssetYieldVaultExitAllPositionsScript.sol"; +import {MainnetAddresses} from "script/base/MainnetAddresses.sol"; + +/** + * A script for executing "exitAllPositions" function on the scSDAI vault. + * This results in withdrawing all WETH invested into leveraged staking (scWETH vault), repaying all WETH debt (using a flashloan if necessary) and withdrawing all sDAI collateral to the vault. + */ +contract ExitAllPositionsScSDai is scCrossAssetYieldVaultExitAllPositionsScript { + /*////////////////////////////////////////////////////////////// + SCRIPT PARAMETERS + //////////////////////////////////////////////////////////////*/ + + // maxAceeptableLossPercent - the maximum acceptable loss in percent of the current total assets amount + // NOTE: override to change the max acceptable loss percent + // function maxAceeptableLossPercent() public pure override returns (uint256) { + // return 0.02e18; // 2% + // } + + function _getVaultAddress() internal virtual override returns (scCrossAssetYieldVault) { + return scCrossAssetYieldVault(vm.envOr("SC_SDAI", MainnetAddresses.SCSDAI)); + } + + /*//////////////////////////////////////////////////////////////*/ +} diff --git a/script/v2/keeper-actions/ExitAllPositionsScUsdcV2.s.sol b/script/v2/keeper-actions/ExitAllPositionsScUsdcV2.s.sol index fc81d7c6..3091eae2 100644 --- a/script/v2/keeper-actions/ExitAllPositionsScUsdcV2.s.sol +++ b/script/v2/keeper-actions/ExitAllPositionsScUsdcV2.s.sol @@ -2,75 +2,30 @@ pragma solidity ^0.8.13; import "forge-std/console2.sol"; -import "forge-std/Test.sol"; -import "forge-std/Script.sol"; -import {ERC20} from "solmate/tokens/ERC20.sol"; -import {WETH} from "solmate/tokens/WETH.sol"; -import {AccessControl} from "openzeppelin-contracts/access/AccessControl.sol"; -import {FixedPointMathLib} from "solmate/utils/FixedPointMathLib.sol"; -import {Address} from "openzeppelin-contracts/utils/Address.sol"; - -import {ScUsdcV2ScriptBase} from "../../base/ScUsdcV2ScriptBase.sol"; -import {MainnetAddresses} from "../../base/MainnetAddresses.sol"; -import {PriceConverter} from "../../../src/steth/priceConverter/PriceConverter.sol"; -import {scUSDCv2} from "../../../src/steth/scUSDCv2.sol"; -import {scWETHv2} from "../../../src/steth/scWETHv2.sol"; -import {MorphoAaveV3ScUsdcAdapter} from "../../../src/steth/scUsdcV2-adapters/MorphoAaveV3ScUsdcAdapter.sol"; -import {AaveV2ScUsdcAdapter} from "../../../src/steth/scUsdcV2-adapters/AaveV2ScUsdcAdapter.sol"; -import {AaveV3ScUsdcAdapter} from "../../../src/steth/scUsdcV2-adapters/AaveV3ScUsdcAdapter.sol"; -import {IAdapter} from "../../../src/steth/IAdapter.sol"; +import {scCrossAssetYieldVault} from "src/steth/scCrossAssetYieldVault.sol"; +import {scCrossAssetYieldVaultExitAllPositionsScript} from + "script/base/scCrossAssetYieldVaultExitAllPositionsScript.sol"; +import {MainnetAddresses} from "script/base/MainnetAddresses.sol"; /** * A script for executing "exitAllPositions" function on the scUsdcV2 vault. * This results in withdrawing all WETH invested into leveraged staking (scWETH vault), repaying all WETH debt (using a flashloan if necessary) and withdrawing all USDC collateral to the vault. */ -contract ExitAllPositionsScUsdcV2 is ScUsdcV2ScriptBase { - using Address for address; - using FixedPointMathLib for uint256; - +contract ExitAllPositionsScUsdcV2 is scCrossAssetYieldVaultExitAllPositionsScript { /*////////////////////////////////////////////////////////////// SCRIPT PARAMETERS //////////////////////////////////////////////////////////////*/ - // @dev: maxAceeptableLossPercent - the maximum acceptable loss in percent of the current total assets amount - - uint256 public maxAceeptableLossPercent = 0.02e18; // 2% - - /*//////////////////////////////////////////////////////////////*/ - - function run() external { - console2.log("--Exit all positions ScUsdcV2 script running--"); - - require(scUsdcV2.hasRole(scUsdcV2.KEEPER_ROLE(), address(keeper)), "invalid keeper"); - - _logScriptParams(); + // maxAceeptableLossPercent - the maximum acceptable loss in percent of the current total assets amount + // NOTE: override to change the max acceptable loss percent + // function maxAceeptableLossPercent() public pure override returns (uint256) { + // return 0.02e18; // 2% + // } - uint256 totalAssets = scUsdcV2.totalAssets(); - uint256 minEndTotalAssets = totalAssets.mulWadDown(1e18 - maxAceeptableLossPercent); - - _logVaultInfo("state before"); - - vm.startBroadcast(keeper); - scUsdcV2.exitAllPositions(minEndTotalAssets); - vm.stopBroadcast(); - - _logVaultInfo("state after"); - console2.log("--Exit all positions ScUsdcV2 script done--"); + function _getVaultAddress() internal override returns (scCrossAssetYieldVault) { + return scCrossAssetYieldVault(vm.envOr("SC_USDC_V2", MainnetAddresses.SCUSDCV2)); } - function _logScriptParams() internal view override { - super._logScriptParams(); - console2.log("maxAceeptableLossPercent\t", maxAceeptableLossPercent); - } - - function _logVaultInfo(string memory message) internal view { - console2.log("\t", message); - console2.log("total assets\t\t", scUsdcV2.totalAssets()); - console2.log("weth profit\t\t", scUsdcV2.getProfit()); - console2.log("float\t\t\t", usdcBalance()); - console2.log("total collateral\t", scUsdcV2.totalCollateral()); - console2.log("total debt\t\t", scUsdcV2.totalDebt()); - console2.log("weth invested\t\t", wethInvested()); - } + /*//////////////////////////////////////////////////////////////*/ } diff --git a/script/v2/keeper-actions/ExitAllPositionsScUsdt.s.sol b/script/v2/keeper-actions/ExitAllPositionsScUsdt.s.sol new file mode 100644 index 00000000..f7d14847 --- /dev/null +++ b/script/v2/keeper-actions/ExitAllPositionsScUsdt.s.sol @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: AGPL-3.0 +pragma solidity ^0.8.13; + +import "forge-std/console2.sol"; + +import {scCrossAssetYieldVault} from "src/steth/scCrossAssetYieldVault.sol"; +import {scCrossAssetYieldVaultExitAllPositionsScript} from + "script/base/scCrossAssetYieldVaultExitAllPositionsScript.sol"; +import {MainnetAddresses} from "script/base/MainnetAddresses.sol"; + +/** + * A script for executing "exitAllPositions" function on the scUSDT vault. + * This results in withdrawing all WETH invested into leveraged staking (scWETH vault), repaying all WETH debt (using a flashloan if necessary) and withdrawing all USDT collateral to the vault. + */ +contract ExitAllPositionsScUsdt is scCrossAssetYieldVaultExitAllPositionsScript { + /*////////////////////////////////////////////////////////////// + SCRIPT PARAMETERS + //////////////////////////////////////////////////////////////*/ + + // maxAceeptableLossPercent - the maximum acceptable loss in percent of the current total assets amount + // NOTE: override to change the max acceptable loss percent + // function maxAceeptableLossPercent() public pure override returns (uint256) { + // return 0.02e18; // 2% + // } + + function _getVaultAddress() internal virtual override returns (scCrossAssetYieldVault) { + return scCrossAssetYieldVault(vm.envOr("SC_USDT", MainnetAddresses.SCUSDT)); + } + + /*//////////////////////////////////////////////////////////////*/ +} diff --git a/script/v2/keeper-actions/ReallocateScUsdcV2.s.sol b/script/v2/keeper-actions/ReallocateScUsdcV2.s.sol index 2f8f9e49..b43e258c 100644 --- a/script/v2/keeper-actions/ReallocateScUsdcV2.s.sol +++ b/script/v2/keeper-actions/ReallocateScUsdcV2.s.sol @@ -4,38 +4,31 @@ pragma solidity ^0.8.13; import "forge-std/console2.sol"; import "forge-std/Test.sol"; -import {ERC20} from "solmate/tokens/ERC20.sol"; -import {WETH} from "solmate/tokens/WETH.sol"; -import {AccessControl} from "openzeppelin-contracts/access/AccessControl.sol"; import {FixedPointMathLib} from "solmate/utils/FixedPointMathLib.sol"; -import {ScUsdcV2ScriptBase} from "../../base/ScUsdcV2ScriptBase.sol"; import {MainnetAddresses} from "../../base/MainnetAddresses.sol"; -import {PriceConverter} from "../../../src/steth/priceConverter/PriceConverter.sol"; import {scUSDCv2} from "../../../src/steth/scUSDCv2.sol"; import {MorphoAaveV3ScUsdcAdapter} from "../../../src/steth/scUsdcV2-adapters/MorphoAaveV3ScUsdcAdapter.sol"; import {AaveV2ScUsdcAdapter} from "../../../src/steth/scUsdcV2-adapters/AaveV2ScUsdcAdapter.sol"; import {AaveV3ScUsdcAdapter} from "../../../src/steth/scUsdcV2-adapters/AaveV3ScUsdcAdapter.sol"; -import {IAdapter} from "../../../src/steth/IAdapter.sol"; import {scCrossAssetYieldVault} from "../../../src/steth/scCrossAssetYieldVault.sol"; +import {scCrossAssetYieldVaultReallocateScript} from "../../base/scCrossAssetYieldVaultReallocateScript.sol"; /** * A script for executing reallocate functionality for scUsdcV2 vaults. */ -contract ReallocateScUsdcV2 is ScUsdcV2ScriptBase { +contract ReallocateScUsdcV2 is scCrossAssetYieldVaultReallocateScript { using FixedPointMathLib for uint256; /*////////////////////////////////////////////////////////////// SCRIPT PARAMETERS //////////////////////////////////////////////////////////////*/ - // @dev The following parameters are used to configure the reallocate script. The goal is to move funds from one lending protocol to another without touching the invested WETH. - // @note: supply and withdraw amounts have to sum up to 0, same for borrow and repay amounts or else the script will revert + /// @dev The following parameters are used to configure the reallocate script. The goal is to move funds from one lending protocol to another without touching the invested WETH. + // NOTE: supply and withdraw amounts have to sum up to 0, same for borrow and repay amounts or else the script will revert // use adapter - whether or not to use a specific adapter // allocationPercent - the percentage of the total assets used as collateral to be allocated to the protocol adapter - uint256 flashloanFeePercent = 0e18; // currently 0% on balancer - bool public useMorpho = true; uint256 public morphoAllocationPercent = 0; @@ -47,150 +40,58 @@ contract ReallocateScUsdcV2 is ScUsdcV2ScriptBase { /*//////////////////////////////////////////////////////////////*/ - struct ReallocateData { - uint256 adapterId; - uint256 supplyAmount; - uint256 borrowAmount; - uint256 withdrawAmount; - uint256 repayAmount; - } - - // script state - ReallocateData[] public reallocateData; - bytes[] multicallData; - uint256 flashLoanAmount; - uint256 totalAllocationPercent; - - function run() external { - console2.log("--ReallocateScUsdcV2 script running--"); - require(scUsdcV2.hasRole(scUsdcV2.KEEPER_ROLE(), address(keeper)), "invalid keeper"); - - _logScriptParams(); - - _logPositions("\tbefore reallocate"); - _initReallocateData(); - _createMulticallData(); - - vm.startBroadcast(keeper); - - console2.log("start execution"); - - scUsdcV2.reallocate(flashLoanAmount, multicallData); - - vm.stopBroadcast(); - - _logPositions("\tafter reallocate"); - console2.log("--ReallocateScUsdcV2 script done--"); - } - - function _logPositions(string memory message) internal view { - console2.log(message); - - console2.log("moprhoCollateral\t", morphoAdapter.getCollateral(address(scUsdcV2))); - console2.log("moprhoDebt\t\t", morphoAdapter.getDebt(address(scUsdcV2))); - - console2.log("aaveV2Collateral\t", aaveV2Adapter.getCollateral(address(scUsdcV2))); - console2.log("aaveV2Debt\t\t", aaveV2Adapter.getDebt(address(scUsdcV2))); + MorphoAaveV3ScUsdcAdapter public morphoAdapter = MorphoAaveV3ScUsdcAdapter(MainnetAddresses.SCUSDCV2_MORPHO_ADAPTER); + AaveV2ScUsdcAdapter public aaveV2Adapter = AaveV2ScUsdcAdapter(MainnetAddresses.SCUSDCV2_AAVEV2_ADAPTER); + AaveV3ScUsdcAdapter public aaveV3Adapter = AaveV3ScUsdcAdapter(MainnetAddresses.SCUSDCV2_AAVEV3_ADAPTER); - console2.log("aaveV3Collateral\t", aaveV3Adapter.getCollateral(address(scUsdcV2))); - console2.log("aaveV3Debt\t\t", aaveV3Adapter.getDebt(address(scUsdcV2))); + function _getVaultAddress() internal override returns (scCrossAssetYieldVault) { + return scCrossAssetYieldVault(vm.envOr("SC_USDC_V2", MainnetAddresses.SCUSDCV2)); } - function _initReallocateData() internal { + function _initReallocateData() internal override { if (useMorpho) { - if (!scUsdcV2.isSupported(morphoAdapter.id())) revert("morpho adapter not supported"); + if (!vault.isSupported(morphoAdapter.id())) revert("morpho adapter not supported"); - _createData(morphoAdapter.id(), morphoAllocationPercent); - - totalAllocationPercent += morphoAllocationPercent; + _createReallocateData(morphoAdapter.id(), morphoAllocationPercent); } if (useAaveV2) { - if (!scUsdcV2.isSupported(aaveV2Adapter.id())) revert("aave v2 adapter not supported"); - - _createData(aaveV2Adapter.id(), aaveV2AllocationPercent); + if (!vault.isSupported(aaveV2Adapter.id())) revert("aave v2 adapter not supported"); - totalAllocationPercent += aaveV2AllocationPercent; + _createReallocateData(aaveV2Adapter.id(), aaveV2AllocationPercent); } if (useAaveV3) { - if (!scUsdcV2.isSupported(aaveV3Adapter.id())) revert("aave v3 adapter not supported"); - - _createData(aaveV3Adapter.id(), aaveV3AllocationPercent); - - totalAllocationPercent += aaveV3AllocationPercent; - } - - if (totalAllocationPercent != 1e18) { - revert("total allocation percent not 100%"); - } - } - - function _createData(uint256 _adapterId, uint256 _allocationPercent) internal { - ReallocateData memory data; - - data.adapterId = _adapterId; - - uint256 currentAllocation = scUsdcV2.getCollateral(_adapterId); - uint256 expectedAllocation = scUsdcV2.totalCollateral().mulWadUp(_allocationPercent); - uint256 currentDebt = scUsdcV2.getDebt(_adapterId); - uint256 expectedDebt = scUsdcV2.totalDebt().mulWadUp(_allocationPercent); + if (!vault.isSupported(aaveV3Adapter.id())) revert("aave v3 adapter not supported"); - if (currentAllocation > expectedAllocation) { - // we need to withdraw some collateral - data.withdrawAmount = currentAllocation - expectedAllocation; - data.repayAmount = currentDebt - expectedDebt; - } else if (currentAllocation < expectedAllocation) { - // we need to supply some collateral - data.supplyAmount = expectedAllocation - currentAllocation; - data.borrowAmount = expectedDebt - currentDebt; + _createReallocateData(aaveV3Adapter.id(), aaveV3AllocationPercent); } - - reallocateData.push(data); } - function _createMulticallData() internal { - for (uint256 i = 0; i < reallocateData.length; i++) { - ReallocateData memory data = reallocateData[i]; - - if (data.withdrawAmount > 0) { - flashLoanAmount += data.repayAmount; - - multicallData.push( - abi.encodeWithSelector(scCrossAssetYieldVault.repay.selector, data.adapterId, data.repayAmount) - ); - multicallData.push( - abi.encodeWithSelector( - scCrossAssetYieldVault.withdraw.selector, data.adapterId, data.withdrawAmount - ) - ); - } - } + function _logPositions(string memory message) internal view override { + console2.log("\n\t----------------------------"); + console2.log(string.concat("\t\t", message)); + console2.log("\t----------------------------"); - for (uint256 i = 0; i < reallocateData.length; i++) { - ReallocateData memory data = reallocateData[i]; + console2.log("moprho collateral\t", morphoAdapter.getCollateral(address(vault))); + console2.log("moprho debt\t\t", morphoAdapter.getDebt(address(vault))); - if (data.supplyAmount > 0) { - uint256 borrowAmount = data.borrowAmount.mulWadDown(1e18 - flashloanFeePercent); + console2.log("aave v2 collateral\t", aaveV2Adapter.getCollateral(address(vault))); + console2.log("aave v2 debt\t\t", aaveV2Adapter.getDebt(address(vault))); - multicallData.push( - abi.encodeWithSelector(scCrossAssetYieldVault.supply.selector, data.adapterId, data.supplyAmount) - ); - multicallData.push( - abi.encodeWithSelector(scCrossAssetYieldVault.borrow.selector, data.adapterId, borrowAmount) - ); - } - } + console2.log("aave v3 collateral\t", aaveV3Adapter.getCollateral(address(vault))); + console2.log("aave v3 debt\t\t", aaveV3Adapter.getDebt(address(vault))); + console2.log("\t----------------------------"); } function _logScriptParams() internal view override { super._logScriptParams(); - console2.log("flashloanFeePercent\t", flashloanFeePercent); - console2.log("useMorpho\t\t", useMorpho); - console2.log("morphoAllocationPercent\t", morphoAllocationPercent); - console2.log("useAaveV2\t\t", useAaveV2); - console2.log("aaveV2AllocationPercent\t", aaveV2AllocationPercent); - console2.log("useAaveV3\t\t", useAaveV3); - console2.log("aaveV3AllocationPercent\t", aaveV3AllocationPercent); + console2.log("flash loan fee pct\t", flashloanFeePercent); + console2.log("use morpho\t\t", useMorpho); + console2.log("morpho allocation pct\t", morphoAllocationPercent); + console2.log("use aave v2\t\t", useAaveV2); + console2.log("aave v2 allocation pct", aaveV2AllocationPercent); + console2.log("use aave v3\t\t", useAaveV3); + console2.log("aave v3 allocation pct", aaveV3AllocationPercent); } } diff --git a/script/v2/keeper-actions/RebalanceScDai.s.sol b/script/v2/keeper-actions/RebalanceScDai.s.sol deleted file mode 100644 index 0187ed28..00000000 --- a/script/v2/keeper-actions/RebalanceScDai.s.sol +++ /dev/null @@ -1,244 +0,0 @@ -// // SPDX-License-Identifier: AGPL-3.0 -// pragma solidity ^0.8.13; - -// import "forge-std/console2.sol"; -// import "forge-std/Test.sol"; -// import "forge-std/Script.sol"; - -// import {Surl} from "surl/Surl.sol"; -// import {Strings} from "openzeppelin-contracts/utils/Strings.sol"; -// import {stdJson} from "forge-std/StdJson.sol"; -// import {ERC20} from "solmate/tokens/ERC20.sol"; -// import {WETH} from "solmate/tokens/WETH.sol"; -// import {AccessControl} from "openzeppelin-contracts/access/AccessControl.sol"; -// import {FixedPointMathLib} from "solmate/utils/FixedPointMathLib.sol"; - -// import {Constants as C} from "../../../src/lib/Constants.sol"; -// import {MainnetAddresses} from "../../base/MainnetAddresses.sol"; -// import {PriceConverter} from "../../../src/steth/PriceConverter.sol"; -// import {scDAI} from "../../../src/steth/scDAI.sol"; -// import {SparkScDaiAdapter} from "../../../src/steth/scDai-adapters/SparkScDaiAdapter.sol"; -// import {IAdapter} from "../../../src/steth/IAdapter.sol"; - -// /** -// * A script for executing rebalance functionality for scDAI vaults. -// */ -// contract RebalanceScDAI is Script { -// using FixedPointMathLib for uint256; -// using Surl for *; -// using Strings for *; -// using stdJson for string; - -// uint256 keeperPrivateKey = uint256(vm.envOr("KEEPER_PRIVATE_KEY", bytes32(0x0))); -// // if keeper private key is not provided, use the default keeper address for running the script tests -// address keeper = keeperPrivateKey != 0 ? vm.addr(keeperPrivateKey) : MainnetAddresses.KEEPER; - -// scDAI public vault = scDAI(vm.envOr("SC_DAI", MainnetAddresses.SCDAI)); - -// PriceConverter priceConverter = PriceConverter(vm.envOr("PRICE_CONVERTER", MainnetAddresses.PRICE_CONVERTER)); - -// SparkScDaiAdapter public sparkAdapter = SparkScDaiAdapter(MainnetAddresses.SCDAI_SPARK_ADAPTER); - -// /*////////////////////////////////////////////////////////////// -// SCRIPT PARAMETERS -// //////////////////////////////////////////////////////////////*/ - -// // @dev The following parameters are used to configure the rebalance script. -// // ltvDiffTolerance - the maximum difference between the target ltv and the actual ltv that is allowed for any adapter -// // minSDaiProfitToReinvest - the minimum amount of weth profit (converted to USDC) that needs to be made for reinvesting to make sense (ie gas costs < profit made) -// // maxProfitSellSlippage - the maximum amount of slippage allowed when selling weth profit for dai -// // investable amount percent - the percentage of the available funds that can be invested for a specific adapter (all have to sum up to 100% or 1e18) -// // target ltv - the target loan to value ratio for a specific adapter. Set to 0 for unused or unsupported adapters! - -// uint256 public ltvDiffTolerance = 0.05e18; // 5% -// uint256 public minSDaiProfitToReinvest = 100e18; // 100 SDAI -// uint256 public maxProfitSellSlippage = 0.01e18; // 1% - -// uint256 public sparkInvestableAmountPercent = 1e18; // 100% -// uint256 public sparkTargetLtv = 0.65e18; - -// /*//////////////////////////////////////////////////////////////*/ - -// struct RebalanceData { -// uint256 adapterId; -// uint256 repayAmount; -// uint256 borrowAmount; -// uint256 supplyAmount; -// uint256 withdrawAmount; -// } - -// struct AdapterSettings { -// uint256 adapterId; -// uint256 investableAmountPercent; -// uint256 targetLtv; -// } - -// error ScriptCannotUseUnsupportedAdapter(uint256 id); - -// // script state -// AdapterSettings[] adapterSettings; -// RebalanceData[] rebalanceDatas; -// uint256 disinvestAmount = 0; -// bytes[] multicallData; - -// function run() external { -// console2.log("--RebalancescDAI script running--"); - -// require(vault.hasRole(vault.KEEPER_ROLE(), address(keeper)), "invalid keeper"); - -// _logScriptParams(); - -// _initializeAdapterSettings(); - -// uint256 minUsdcFromProfitSelling = _sellWethProfitIfAboveDefinedMin(); -// uint256 minUsdcBalance = minUsdcFromProfitSelling + vault.sDaiBalance(); -// uint256 minFloatRequired = vault.totalAssets().mulWadUp(vault.floatPercentage()); -// uint256 missingFloat = minFloatRequired > minUsdcBalance ? minFloatRequired - minUsdcBalance : 0; -// uint256 investableAmount = minFloatRequired < minUsdcBalance ? minUsdcBalance - minFloatRequired : 0; - -// _createRebalanceMulticallDataForAllAdapters(investableAmount, missingFloat); - -// _logVaultInfo("state before rebalance"); - -// vm.startBroadcast(keeper); -// vault.rebalance(multicallData); -// vm.stopBroadcast(); - -// _logVaultInfo("state after rebalance"); -// console2.log("--RebalancescDAI script done--"); -// } - -// function _initializeAdapterSettings() internal { -// adapterSettings.push( -// AdapterSettings({ -// adapterId: sparkAdapter.id(), -// investableAmountPercent: sparkInvestableAmountPercent, -// targetLtv: sparkTargetLtv -// }) -// ); -// } - -// function _sellWethProfitIfAboveDefinedMin() internal returns (uint256) { -// uint256 wethProfit = vault.getProfit(); -// // account for slippage when selling weth profit for sDai -// uint256 minExpectedsDaiProfit = -// vault.priceConverter().ethTosDai(wethProfit).mulWadDown(1e18 - maxProfitSellSlippage); - -// // if profit is too small, don't sell & reinvest -// if (minExpectedsDaiProfit < minSDaiProfitToReinvest) return 0; - -// multicallData.push(abi.encodeWithSelector(scDAI.sellProfit.selector, minExpectedsDaiProfit)); - -// return minExpectedsDaiProfit; -// } - -// function _createRebalanceMulticallDataForAllAdapters(uint256 _investableAmount, uint256 _missingFloat) internal { -// for (uint256 i = 0; i < adapterSettings.length; i++) { -// AdapterSettings memory settings = adapterSettings[i]; - -// if ( -// !vault.isSupported(settings.adapterId) -// && (settings.targetLtv > 0 || settings.investableAmountPercent > 0) -// ) { -// revert ScriptCannotUseUnsupportedAdapter(settings.adapterId); -// } - -// _createAdapterRebalanceData( -// settings.adapterId, -// settings.targetLtv, -// _investableAmount.mulWadDown(settings.investableAmountPercent), -// _missingFloat.mulWadDown(settings.investableAmountPercent) -// ); -// } - -// _createRebalanceMulticallData(); -// } - -// function _createAdapterRebalanceData( -// uint256 _adapterId, -// uint256 _targetLtv, -// uint256 _investableAmount, -// uint256 _missingFloat -// ) internal { -// uint256 collateral = vault.getCollateral(_adapterId); -// uint256 debt = vault.getDebt(_adapterId); - -// uint256 targetCollateral = collateral + _investableAmount - _missingFloat; -// uint256 targetDebt = vault.priceConverter().sDaiToEth(targetCollateral).mulWadDown(_targetLtv); - -// RebalanceData memory rebalanceData; -// rebalanceData.adapterId = _adapterId; - -// if (targetCollateral > collateral) { -// rebalanceData.supplyAmount = targetCollateral - collateral; -// } else if (targetCollateral < collateral) { -// rebalanceData.withdrawAmount = collateral - targetCollateral; -// } - -// uint256 debtUpperBound = targetDebt.mulWadUp(1e18 + ltvDiffTolerance); -// uint256 debtLowerBound = targetDebt.mulWadDown(1e18 - ltvDiffTolerance); - -// if (debt < debtLowerBound) { -// rebalanceData.borrowAmount = targetDebt - debt; -// } else if (debt > debtUpperBound) { -// uint256 repayAmount = debt - targetDebt; -// rebalanceData.repayAmount = repayAmount; -// disinvestAmount += repayAmount; -// } - -// rebalanceDatas.push(rebalanceData); -// } - -// function _createRebalanceMulticallData() internal { -// if (disinvestAmount > 0) { -// multicallData.push(abi.encodeWithSelector(scDAI.disinvest.selector, disinvestAmount)); -// } - -// for (uint256 i = 0; i < rebalanceDatas.length; i++) { -// RebalanceData memory rebalanceData = rebalanceDatas[i]; -// if (rebalanceData.supplyAmount > 0) { -// multicallData.push( -// abi.encodeWithSelector(scDAI.supply.selector, rebalanceData.adapterId, rebalanceData.supplyAmount) -// ); -// } -// if (rebalanceData.borrowAmount > 0) { -// multicallData.push( -// abi.encodeWithSelector(scDAI.borrow.selector, rebalanceData.adapterId, rebalanceData.borrowAmount) -// ); -// } -// if (rebalanceData.repayAmount > 0) { -// multicallData.push( -// abi.encodeWithSelector(scDAI.repay.selector, rebalanceData.adapterId, rebalanceData.repayAmount) -// ); -// } -// if (rebalanceData.withdrawAmount > 0) { -// multicallData.push( -// abi.encodeWithSelector( -// scDAI.withdraw.selector, rebalanceData.adapterId, rebalanceData.withdrawAmount -// ) -// ); -// } -// } -// } - -// function _logScriptParams() internal view { -// console2.log("\t script params"); -// console2.log("keeper\t\t", address(keeper)); -// console2.log("scDAI\t\t", address(vault)); -// console2.log("ltv diff tolerance\t", ltvDiffTolerance); -// console2.log("min sDai profit\t\t", minSDaiProfitToReinvest); -// console2.log("max profit sell slippage\t", maxProfitSellSlippage); -// console2.log("spark investable amount percent\t", sparkInvestableAmountPercent); -// console2.log("spark target ltv\t", sparkTargetLtv); -// } - -// function _logVaultInfo(string memory message) internal view { -// console2.log("\t", message); -// console2.log("total assets\t\t", vault.totalAssets()); -// console2.log("weth profit\t\t", vault.getProfit()); -// console2.log("float\t\t\t", vault.sDaiBalance()); -// console2.log("total collateral\t", vault.totalCollateral()); -// console2.log("total debt\t\t", vault.totalDebt()); -// console2.log("weth invested\t\t", vault.wethInvested()); -// } -// } diff --git a/script/v2/keeper-actions/RebalanceScSDai.s.sol b/script/v2/keeper-actions/RebalanceScSDai.s.sol new file mode 100644 index 00000000..3bf393c1 --- /dev/null +++ b/script/v2/keeper-actions/RebalanceScSDai.s.sol @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: AGPL-3.0 +pragma solidity ^0.8.13; + +import "forge-std/console2.sol"; + +import {IAdapter} from "src/steth/IAdapter.sol"; +import {scCrossAssetYieldVault} from "src/steth/scCrossAssetYieldVault.sol"; +import {MainnetAddresses} from "script/base/MainnetAddresses.sol"; +import {scCrossAssetYieldVaultRebalanceScript} from "script/base/scCrossAssetYieldVaultRebalanceScript.sol"; + +/** + * A script for executing rebalance functionality for... + */ +contract RebalanceScSDai is scCrossAssetYieldVaultRebalanceScript { + /*////////////////////////////////////////////////////////////// + SCRIPT PARAMETERS + //////////////////////////////////////////////////////////////*/ + + // @dev The following parameters are used to configure the rebalance script. + // ltvDiffTolerance (scCrossAssetYieldVaultRebalanceScript) - the maximum difference between the target ltv and the actual ltv that is allowed for any adapter + // minProfitToReinvest - the minimum amount of WETH profit (converted to USDT) that needs to be made for reinvesting to make sense (ie gas costs < profit made) + // maxProfitSellSlippage (scCrossAssetYieldVaultRebalanceScript) - the maximum amount of slippage allowed when selling WETH profit for USDT + // aave v3 investable amount percent - the percentage of the available funds that can be invested for a specific adapter (all have to sum up to 100% or 1e18) + // aave v3 target ltv - the target loan to value ratio for a specific adapter. Set to 0 for unused or unsupported adapters! + + function _getMinProfitToReinvest() internal pure override returns (uint256) { + return 100e18; // 100 sDAI + } + + function _getVaultAddress() internal virtual override returns (scCrossAssetYieldVault) { + return scCrossAssetYieldVault(vm.envOr("SC_SDAI", MainnetAddresses.SCSDAI)); + } + + uint256 public sparkInvestableAmountPercent = 1e18; // 100% + uint256 public sparkTargetLtv = 0.65e18; // 65% + + /*//////////////////////////////////////////////////////////////*/ + + function _initializeAdapterSettings() internal override { + adapterSettings.push( + AdapterSettings({ + adapterId: IAdapter(MainnetAddresses.SCSDAI_SPARK_ADAPTER).id(), + investableAmountPercent: sparkInvestableAmountPercent, + targetLtv: sparkTargetLtv + }) + ); + } + + function _logScriptParams() internal view override { + super._logScriptParams(); + console2.log("spark invest pct\t", sparkInvestableAmountPercent); + console2.log("spark target ltv\t", sparkTargetLtv); + } +} diff --git a/script/v2/keeper-actions/RebalanceScUsdcV2.s.sol b/script/v2/keeper-actions/RebalanceScUsdcV2.s.sol index c3c3c64a..482b005c 100644 --- a/script/v2/keeper-actions/RebalanceScUsdcV2.s.sol +++ b/script/v2/keeper-actions/RebalanceScUsdcV2.s.sol @@ -2,28 +2,24 @@ pragma solidity ^0.8.13; import "forge-std/console2.sol"; -import "forge-std/Test.sol"; -import "forge-std/Script.sol"; import {ERC20} from "solmate/tokens/ERC20.sol"; import {WETH} from "solmate/tokens/WETH.sol"; -import {AccessControl} from "openzeppelin-contracts/access/AccessControl.sol"; import {FixedPointMathLib} from "solmate/utils/FixedPointMathLib.sol"; -import {ScUsdcV2ScriptBase} from "../../base/ScUsdcV2ScriptBase.sol"; import {MainnetAddresses} from "../../base/MainnetAddresses.sol"; -import {PriceConverter} from "../../../src/steth/priceConverter/PriceConverter.sol"; import {scUSDCv2} from "../../../src/steth/scUSDCv2.sol"; +import {IAdapter} from "../../../src/steth/IAdapter.sol"; import {MorphoAaveV3ScUsdcAdapter} from "../../../src/steth/scUsdcV2-adapters/MorphoAaveV3ScUsdcAdapter.sol"; import {AaveV2ScUsdcAdapter} from "../../../src/steth/scUsdcV2-adapters/AaveV2ScUsdcAdapter.sol"; import {AaveV3ScUsdcAdapter} from "../../../src/steth/scUsdcV2-adapters/AaveV3ScUsdcAdapter.sol"; -import {IAdapter} from "../../../src/steth/IAdapter.sol"; import {scCrossAssetYieldVault} from "../../../src/steth/scCrossAssetYieldVault.sol"; +import {scCrossAssetYieldVaultRebalanceScript} from "../../base/scCrossAssetYieldVaultRebalanceScript.sol"; /** - * A script for executing rebalance functionality for scUsdcV2 vaults. + * A script for executing rebalance functionality for... */ -contract RebalanceScUsdcV2 is ScUsdcV2ScriptBase { +contract RebalanceScUsdcV2 is scCrossAssetYieldVaultRebalanceScript { using FixedPointMathLib for uint256; /*////////////////////////////////////////////////////////////// @@ -37,9 +33,13 @@ contract RebalanceScUsdcV2 is ScUsdcV2ScriptBase { // investable amount percent - the percentage of the available funds that can be invested for a specific adapter (all have to sum up to 100% or 1e18) // target ltv - the target loan to value ratio for a specific adapter. Set to 0 for unused or unsupported adapters! - uint256 public ltvDiffTolerance = 0.05e18; // 5% - uint256 public minUsdcProfitToReinvest = 100e6; // 100 USDC - uint256 public maxProfitSellSlippage = 0.01e18; // 1% + function _getMinProfitToReinvest() internal pure override returns (uint256) { + return 100e6; // 100 USDC + } + + function _getVaultAddress() internal override returns (scCrossAssetYieldVault) { + return scCrossAssetYieldVault(vm.envOr("SC_USDC_V2", MainnetAddresses.SCUSDCV2)); + } uint256 public morphoInvestableAmountPercent = 1e18; // 100% uint256 public morphoTargetLtv = 0.65e18; // 65% @@ -52,59 +52,14 @@ contract RebalanceScUsdcV2 is ScUsdcV2ScriptBase { /*//////////////////////////////////////////////////////////////*/ - struct RebalanceData { - uint256 adapterId; - uint256 repayAmount; - uint256 borrowAmount; - uint256 supplyAmount; - uint256 withdrawAmount; - } - - struct AdapterSettings { - uint256 adapterId; - uint256 investableAmountPercent; - uint256 targetLtv; - } - - error ScriptCannotUseUnsupportedAdapter(uint256 id); - - // script state - AdapterSettings[] adapterSettings; - RebalanceData[] rebalanceDatas; - uint256 disinvestAmount = 0; - bytes[] multicallData; - - function run() external { - console2.log("--RebalanceScUsdcV2 script running--"); - - require(scUsdcV2.hasRole(scUsdcV2.KEEPER_ROLE(), address(keeper)), "invalid keeper"); + MorphoAaveV3ScUsdcAdapter public morphoAdapter = MorphoAaveV3ScUsdcAdapter(MainnetAddresses.SCUSDCV2_MORPHO_ADAPTER); + AaveV2ScUsdcAdapter public aaveV2Adapter = AaveV2ScUsdcAdapter(MainnetAddresses.SCUSDCV2_AAVEV2_ADAPTER); + AaveV3ScUsdcAdapter public aaveV3Adapter = AaveV3ScUsdcAdapter(MainnetAddresses.SCUSDCV2_AAVEV3_ADAPTER); - _logScriptParams(); - - _initializeAdapterSettings(); - - uint256 minUsdcFromProfitSelling = _sellWethProfitIfAboveDefinedMin(); - uint256 minUsdcBalance = minUsdcFromProfitSelling + usdcBalance(); - uint256 minFloatRequired = scUsdcV2.totalAssets().mulWadUp(scUsdcV2.floatPercentage()); - uint256 missingFloat = minFloatRequired > minUsdcBalance ? minFloatRequired - minUsdcBalance : 0; - uint256 investableAmount = minFloatRequired < minUsdcBalance ? minUsdcBalance - minFloatRequired : 0; - - _createRebalanceMulticallDataForAllAdapters(investableAmount, missingFloat); - - _logVaultInfo("state before rebalance"); - - vm.startBroadcast(keeper); - scUsdcV2.rebalance(multicallData); - vm.stopBroadcast(); - - _logVaultInfo("state after rebalance"); - console2.log("--RebalanceScUsdcV2 script done--"); - } - - function _initializeAdapterSettings() internal { + function _initializeAdapterSettings() internal override { adapterSettings.push( AdapterSettings({ - adapterId: morphoAdapter.id(), + adapterId: IAdapter(MainnetAddresses.SCUSDCV2_MORPHO_ADAPTER).id(), investableAmountPercent: morphoInvestableAmountPercent, targetLtv: morphoTargetLtv }) @@ -112,7 +67,7 @@ contract RebalanceScUsdcV2 is ScUsdcV2ScriptBase { adapterSettings.push( AdapterSettings({ - adapterId: aaveV2Adapter.id(), + adapterId: IAdapter(MainnetAddresses.SCUSDCV2_AAVEV2_ADAPTER).id(), investableAmountPercent: aaveV2InvestableAmountPercent, targetLtv: aaveV2TargetLtv }) @@ -120,146 +75,20 @@ contract RebalanceScUsdcV2 is ScUsdcV2ScriptBase { adapterSettings.push( AdapterSettings({ - adapterId: aaveV3Adapter.id(), + adapterId: IAdapter(MainnetAddresses.SCUSDCV2_AAVEV3_ADAPTER).id(), investableAmountPercent: aaveV3InvestableAmountPercent, targetLtv: aaveV3TargetLtv }) ); - - require( - morphoInvestableAmountPercent + aaveV2InvestableAmountPercent + aaveV3InvestableAmountPercent == 1e18, - "investable amount percent not 100%" - ); - } - - function _sellWethProfitIfAboveDefinedMin() internal returns (uint256) { - uint256 wethProfit = scUsdcV2.getProfit(); - // account for slippage when selling weth profit for usdc - uint256 minExpectedUsdcProfit = priceConverter.ethToUsdc(wethProfit).mulWadDown(1e18 - maxProfitSellSlippage); - - // if profit is too small, don't sell & reinvest - if (minExpectedUsdcProfit < minUsdcProfitToReinvest) return 0; - - multicallData.push(abi.encodeWithSelector(scCrossAssetYieldVault.sellProfit.selector, minExpectedUsdcProfit)); - - return minExpectedUsdcProfit; - } - - function _createRebalanceMulticallDataForAllAdapters(uint256 _investableAmount, uint256 _missingFloat) internal { - for (uint256 i = 0; i < adapterSettings.length; i++) { - AdapterSettings memory settings = adapterSettings[i]; - - if ( - !scUsdcV2.isSupported(settings.adapterId) - && (settings.targetLtv > 0 || settings.investableAmountPercent > 0) - ) { - revert ScriptCannotUseUnsupportedAdapter(settings.adapterId); - } - - _createAdapterRebalanceData( - settings.adapterId, - settings.targetLtv, - _investableAmount.mulWadDown(settings.investableAmountPercent), - _missingFloat.mulWadDown(settings.investableAmountPercent) - ); - } - - _createRebalanceMulticallData(); - } - - function _createAdapterRebalanceData( - uint256 _adapterId, - uint256 _targetLtv, - uint256 _investableAmount, - uint256 _missingFloat - ) internal { - uint256 collateral = scUsdcV2.getCollateral(_adapterId); - uint256 debt = scUsdcV2.getDebt(_adapterId); - - uint256 targetCollateral = collateral + _investableAmount - _missingFloat; - uint256 targetDebt = priceConverter.usdcToEth(targetCollateral).mulWadDown(_targetLtv); - - RebalanceData memory rebalanceData; - rebalanceData.adapterId = _adapterId; - - if (targetCollateral > collateral) { - rebalanceData.supplyAmount = targetCollateral - collateral; - } else if (targetCollateral < collateral) { - rebalanceData.withdrawAmount = collateral - targetCollateral; - } - - uint256 debtUpperBound = targetDebt.mulWadUp(1e18 + ltvDiffTolerance); - uint256 debtLowerBound = targetDebt.mulWadDown(1e18 - ltvDiffTolerance); - - if (debt < debtLowerBound) { - rebalanceData.borrowAmount = targetDebt - debt; - } else if (debt > debtUpperBound) { - uint256 repayAmount = debt - targetDebt; - rebalanceData.repayAmount = repayAmount; - disinvestAmount += repayAmount; - } - - rebalanceDatas.push(rebalanceData); - } - - function _createRebalanceMulticallData() internal { - if (disinvestAmount > 0) { - multicallData.push(abi.encodeWithSelector(scCrossAssetYieldVault.disinvest.selector, disinvestAmount)); - } - - for (uint256 i = 0; i < rebalanceDatas.length; i++) { - RebalanceData memory rebalanceData = rebalanceDatas[i]; - if (rebalanceData.supplyAmount > 0) { - multicallData.push( - abi.encodeWithSelector( - scCrossAssetYieldVault.supply.selector, rebalanceData.adapterId, rebalanceData.supplyAmount - ) - ); - } - if (rebalanceData.borrowAmount > 0) { - multicallData.push( - abi.encodeWithSelector( - scCrossAssetYieldVault.borrow.selector, rebalanceData.adapterId, rebalanceData.borrowAmount - ) - ); - } - if (rebalanceData.repayAmount > 0) { - multicallData.push( - abi.encodeWithSelector( - scCrossAssetYieldVault.repay.selector, rebalanceData.adapterId, rebalanceData.repayAmount - ) - ); - } - if (rebalanceData.withdrawAmount > 0) { - multicallData.push( - abi.encodeWithSelector( - scCrossAssetYieldVault.withdraw.selector, rebalanceData.adapterId, rebalanceData.withdrawAmount - ) - ); - } - } } function _logScriptParams() internal view override { super._logScriptParams(); - console2.log("ltv diff tolerance\t", ltvDiffTolerance); - console2.log("min usdc profit\t\t", minUsdcProfitToReinvest); - console2.log("max profit sell slippage\t", maxProfitSellSlippage); - console2.log("morpho investable amount percent\t", morphoInvestableAmountPercent); + console2.log("morpho invest pct\t", morphoInvestableAmountPercent); console2.log("morpho target ltv\t", morphoTargetLtv); - console2.log("aave v2 investable amount percent\t", aaveV2InvestableAmountPercent); + console2.log("aave v2 invest pct\t", aaveV2InvestableAmountPercent); console2.log("aave v2 target ltv\t", aaveV2TargetLtv); - console2.log("aave v3 investable amount percent\t", aaveV3InvestableAmountPercent); + console2.log("aave v3 invest pct\t", aaveV3InvestableAmountPercent); console2.log("aave v3 target ltv\t", aaveV3TargetLtv); } - - function _logVaultInfo(string memory message) internal view { - console2.log("\t", message); - console2.log("total assets\t\t", scUsdcV2.totalAssets()); - console2.log("weth profit\t\t", scUsdcV2.getProfit()); - console2.log("float\t\t\t", usdcBalance()); - console2.log("total collateral\t", scUsdcV2.totalCollateral()); - console2.log("total debt\t\t", scUsdcV2.totalDebt()); - console2.log("weth invested\t\t", wethInvested()); - } } diff --git a/script/v2/keeper-actions/RebalanceScUsdt.s.sol b/script/v2/keeper-actions/RebalanceScUsdt.s.sol new file mode 100644 index 00000000..a6ebc373 --- /dev/null +++ b/script/v2/keeper-actions/RebalanceScUsdt.s.sol @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: AGPL-3.0 +pragma solidity ^0.8.13; + +import "forge-std/console2.sol"; + +import {MainnetAddresses} from "../../base/MainnetAddresses.sol"; +import {IAdapter} from "../../../src/steth/IAdapter.sol"; +import {scCrossAssetYieldVault} from "../../../src/steth/scCrossAssetYieldVault.sol"; +import {scCrossAssetYieldVaultRebalanceScript} from "../../base/scCrossAssetYieldVaultRebalanceScript.sol"; + +/** + * A script for executing rebalance functionality for... + */ +contract RebalanceScUsdt is scCrossAssetYieldVaultRebalanceScript { + /*////////////////////////////////////////////////////////////// + SCRIPT PARAMETERS + //////////////////////////////////////////////////////////////*/ + + // @dev The following parameters are used to configure the rebalance script. + // ltvDiffTolerance (scCrossAssetYieldVaultRebalanceScript) - the maximum difference between the target ltv and the actual ltv that is allowed for any adapter + // minProfitToReinvest - the minimum amount of WETH profit (converted to USDT) that needs to be made for reinvesting to make sense (ie gas costs < profit made) + // maxProfitSellSlippage (scCrossAssetYieldVaultRebalanceScript) - the maximum amount of slippage allowed when selling WETH profit for USDT + // aave v3 investable amount percent - the percentage of the available funds that can be invested for a specific adapter (all have to sum up to 100% or 1e18) + // aave v3 target ltv - the target loan to value ratio for a specific adapter. Set to 0 for unused or unsupported adapters! + + function _getMinProfitToReinvest() internal pure override returns (uint256) { + return 100e6; // 100 USDT + } + + function _getVaultAddress() internal virtual override returns (scCrossAssetYieldVault) { + return scCrossAssetYieldVault(vm.envOr("SC_USDT", MainnetAddresses.SCUSDT)); + } + + uint256 public aaveV3InvestableAmountPercent = 1e18; // 100% + uint256 public aaveV3TargetLtv = 0.65e18; // 65% + + /*//////////////////////////////////////////////////////////////*/ + + function _initializeAdapterSettings() internal override { + adapterSettings.push( + AdapterSettings({ + adapterId: IAdapter(MainnetAddresses.SCUSDT_AAVEV3_ADAPTER).id(), + investableAmountPercent: aaveV3InvestableAmountPercent, + targetLtv: aaveV3TargetLtv + }) + ); + } + + function _logScriptParams() internal view override { + super._logScriptParams(); + console2.log("aave v3 invest pct\t", aaveV3InvestableAmountPercent); + console2.log("aave v3 target ltv\t", aaveV3TargetLtv); + } +} diff --git a/src/steth/swapper/SwapperLib.sol b/src/lib/SwapperLib.sol similarity index 75% rename from src/steth/swapper/SwapperLib.sol rename to src/lib/SwapperLib.sol index 94af9ff3..2b9279f5 100644 --- a/src/steth/swapper/SwapperLib.sol +++ b/src/lib/SwapperLib.sol @@ -6,12 +6,12 @@ import {WETH} from "solmate/tokens/WETH.sol"; import {SafeTransferLib} from "solmate/utils/SafeTransferLib.sol"; import {Address} from "openzeppelin-contracts/utils/Address.sol"; -import {Constants as C} from "../../lib/Constants.sol"; -import {AmountReceivedBelowMin} from "../../errors/scErrors.sol"; -import {ISwapRouter} from "../../interfaces/uniswap/ISwapRouter.sol"; -import {ILido} from "../../interfaces/lido/ILido.sol"; -import {IwstETH} from "../../interfaces/lido/IwstETH.sol"; -import {ICurvePool} from "../../interfaces/curve/ICurvePool.sol"; +import {Constants as C} from "./Constants.sol"; +import {AmountReceivedBelowMin} from "src/errors/scErrors.sol"; +import {ISwapRouter} from "src/interfaces/uniswap/ISwapRouter.sol"; +import {ILido} from "src/interfaces/lido/ILido.sol"; +import {IwstETH} from "src/interfaces/lido/IwstETH.sol"; +import {ICurvePool} from "src/interfaces/curve/ICurvePool.sol"; /** * @title SwapperLib @@ -52,6 +52,27 @@ library SwapperLib { uint256 _amountIn, uint256 _amountOutMin, uint24 _poolFee + ) internal returns (uint256 amountOut) { + amountOut = _uniswapSwapExactInput(_tokenIn, _tokenOut, address(this), _amountIn, _amountOutMin, _poolFee); + } + + /** + * @notice Swap tokens on Uniswap V3 using the exact input single function. + * @param _tokenIn The address of the token to swap from. + * @param _tokenOut The address of the token to swap to. + * @param _recipient The address to receive the output tokens. + * @param _amountIn The amount of `_tokenIn` to swap. + * @param _amountOutMin The minimum amount of `_tokenOut` to receive. + * @param _poolFee The fee tier of the Uniswap V3 pool. + * @return amountOut The amount of `_tokenOut` received from the swap. + */ + function _uniswapSwapExactInput( + address _tokenIn, + address _tokenOut, + address _recipient, + uint256 _amountIn, + uint256 _amountOutMin, + uint24 _poolFee ) internal returns (uint256 amountOut) { ERC20(_tokenIn).safeApprove(address(swapRouter), _amountIn); @@ -59,8 +80,8 @@ library SwapperLib { tokenIn: _tokenIn, tokenOut: _tokenOut, fee: _poolFee, - recipient: address(this), - deadline: block.timestamp, + recipient: _recipient, + deadline: block.timestamp + 24 hours, amountIn: _amountIn, amountOutMinimum: _amountOutMin, sqrtPriceLimitX96: 0 @@ -82,13 +103,32 @@ library SwapperLib { uint256 _amountIn, uint256 _amountOutMin, bytes memory _path + ) internal returns (uint256 amountOut) { + amountOut = _uniswapSwapExactInputMultihop(_tokenIn, _amountIn, _amountOutMin, _path, address(this)); + } + + /** + * @notice Swap tokens on Uniswap V3 using exact input multi-hop. + * @param _tokenIn The address of the token to swap from. + * @param _amountIn The amount of `_tokenIn` to swap. + * @param _amountOutMin The minimum amount of the output token to receive. + * @param _path The encoded path for the swap, including tokens and fees. + * @param _recipient The address to receive the output tokens. + * @return amountOut The amount of output tokens received from the swap. + */ + function _uniswapSwapExactInputMultihop( + address _tokenIn, + uint256 _amountIn, + uint256 _amountOutMin, + bytes memory _path, + address _recipient ) internal returns (uint256 amountOut) { ERC20(_tokenIn).safeApprove(address(swapRouter), _amountIn); ISwapRouter.ExactInputParams memory params = ISwapRouter.ExactInputParams({ path: _path, - recipient: address(this), - deadline: block.timestamp, + recipient: _recipient, + deadline: block.timestamp + 24 hours, amountIn: _amountIn, amountOutMinimum: _amountOutMin }); diff --git a/src/steth/swapper/SDaiWethSwapper.sol b/src/steth/swapper/SDaiWethSwapper.sol index f97154b6..7624acd5 100644 --- a/src/steth/swapper/SDaiWethSwapper.sol +++ b/src/steth/swapper/SDaiWethSwapper.sol @@ -6,7 +6,7 @@ import {ERC4626} from "solmate/mixins/ERC4626.sol"; import {Constants as C} from "../../lib/Constants.sol"; import {ISinglePairSwapper} from "../swapper/ISinglePairSwapper.sol"; -import {SwapperLib} from "./SwapperLib.sol"; +import {SwapperLib} from "src/lib/SwapperLib.sol"; import {UniversalSwapper} from "./UniversalSwapper.sol"; /** diff --git a/src/steth/swapper/Swapper.sol b/src/steth/swapper/Swapper.sol index e0ce1d5b..69b7d7a2 100644 --- a/src/steth/swapper/Swapper.sol +++ b/src/steth/swapper/Swapper.sol @@ -3,16 +3,16 @@ pragma solidity ^0.8.19; import {ERC20} from "solmate/tokens/ERC20.sol"; import {WETH} from "solmate/tokens/WETH.sol"; -import {ILido} from "../../interfaces/lido/ILido.sol"; -import {IwstETH} from "../../interfaces/lido/IwstETH.sol"; -import {ICurvePool} from "../../interfaces/curve/ICurvePool.sol"; import {SafeTransferLib} from "solmate/utils/SafeTransferLib.sol"; import {Address} from "openzeppelin-contracts/utils/Address.sol"; -import {AmountReceivedBelowMin} from "../../errors/scErrors.sol"; -import {Constants as C} from "../../lib/Constants.sol"; +import {SwapperLib} from "src/lib/SwapperLib.sol"; +import {ILido} from "src/interfaces/lido/ILido.sol"; +import {IwstETH} from "src/interfaces/lido/IwstETH.sol"; +import {ICurvePool} from "src/interfaces/curve/ICurvePool.sol"; +import {AmountReceivedBelowMin} from "src/errors/scErrors.sol"; +import {Constants as C} from "src/lib/Constants.sol"; import {IScWETHSwapper} from "./IScWETHSwapper.sol"; -import {SwapperLib} from "./SwapperLib.sol"; import {UniversalSwapper} from "./UniversalSwapper.sol"; /** diff --git a/src/steth/swapper/UniversalSwapper.sol b/src/steth/swapper/UniversalSwapper.sol index 14a40cd2..73f5b7d8 100644 --- a/src/steth/swapper/UniversalSwapper.sol +++ b/src/steth/swapper/UniversalSwapper.sol @@ -4,9 +4,9 @@ pragma solidity ^0.8.19; import {ERC20} from "solmate/tokens/ERC20.sol"; import {SafeTransferLib} from "solmate/utils/SafeTransferLib.sol"; -import {Constants as C} from "../../lib/Constants.sol"; +import {Constants as C} from "src/lib/Constants.sol"; +import {SwapperLib} from "src/lib/SwapperLib.sol"; import {ISwapper} from "./ISwapper.sol"; -import {SwapperLib} from "./SwapperLib.sol"; /** * @title UniversalSwapper diff --git a/src/steth/swapper/UsdcWethSwapper.sol b/src/steth/swapper/UsdcWethSwapper.sol index 7651ac74..6586daf2 100644 --- a/src/steth/swapper/UsdcWethSwapper.sol +++ b/src/steth/swapper/UsdcWethSwapper.sol @@ -5,7 +5,7 @@ import {ERC20} from "solmate/tokens/ERC20.sol"; import {Constants as C} from "../../lib/Constants.sol"; import {ISinglePairSwapper} from "./../swapper/ISinglePairSwapper.sol"; -import {SwapperLib} from "./SwapperLib.sol"; +import {SwapperLib} from "src/lib/SwapperLib.sol"; import {UniversalSwapper} from "./UniversalSwapper.sol"; /** diff --git a/src/steth/swapper/UsdsWethSwapper.sol b/src/steth/swapper/UsdsWethSwapper.sol index a714fae9..0c0d27fc 100644 --- a/src/steth/swapper/UsdsWethSwapper.sol +++ b/src/steth/swapper/UsdsWethSwapper.sol @@ -4,11 +4,11 @@ pragma solidity ^0.8.19; import {ERC20} from "solmate/tokens/ERC20.sol"; import {ERC4626} from "solmate/mixins/ERC4626.sol"; -import {Constants as C} from "../../lib/Constants.sol"; -import {ISinglePairSwapper} from "../swapper/ISinglePairSwapper.sol"; -import {SwapperLib} from "./SwapperLib.sol"; +import {Constants as C} from "src/lib/Constants.sol"; +import {ISinglePairSwapper} from "src/steth/swapper/ISinglePairSwapper.sol"; +import {SwapperLib} from "src/lib/SwapperLib.sol"; +import {IDaiUsds} from "src/interfaces/sky/IDaiUsds.sol"; import {UniversalSwapper} from "./UniversalSwapper.sol"; -import {IDaiUsds} from "../../interfaces/sky/IDaiUsds.sol"; contract UsdsWethSwapper is ISinglePairSwapper, UniversalSwapper { /// @notice The address of the asset token (USDS). diff --git a/src/steth/swapper/UsdtWethSwapper.sol b/src/steth/swapper/UsdtWethSwapper.sol index 41b38195..f3d94854 100644 --- a/src/steth/swapper/UsdtWethSwapper.sol +++ b/src/steth/swapper/UsdtWethSwapper.sol @@ -3,10 +3,10 @@ pragma solidity ^0.8.19; import {ERC20} from "solmate/tokens/ERC20.sol"; -import {Constants as C} from "../../lib/Constants.sol"; -import {ISinglePairSwapper} from "./../swapper/ISinglePairSwapper.sol"; -import {SwapperLib} from "./SwapperLib.sol"; +import {Constants as C} from "src/lib/Constants.sol"; +import {SwapperLib} from "src/lib/SwapperLib.sol"; import {UniversalSwapper} from "./UniversalSwapper.sol"; +import {ISinglePairSwapper} from "./../swapper/ISinglePairSwapper.sol"; /** * @title UsdtWethSwapper diff --git a/test/BonusTracker.t.sol b/test/BonusTracker.t.sol index c3d4e53b..d995503d 100644 --- a/test/BonusTracker.t.sol +++ b/test/BonusTracker.t.sol @@ -195,10 +195,10 @@ contract BonusTrackerTest is DSTestPlus { } function testCorrectness_bonus(uint256 timeLapsed) public { - timeLapsed = bound(timeLapsed, 1, 36500 days); + timeLapsed = bound(timeLapsed, 2, 36500 days); hevm.warp(timeLapsed); stakingPool.boost(); - timeLapsed = bound(timeLapsed, 1, 36500 days); + timeLapsed = bound(timeLapsed, 2, 36500 days); hevm.warp(timeLapsed * 2); uint256 currentBonus = stakingPool.bonusOf(address(this)); uint256 accountBalance = stakingPool.balanceOf(address(this)); diff --git a/test/RewardTracker.t.sol b/test/RewardTracker.t.sol index 9920b1fe..e8da9137 100644 --- a/test/RewardTracker.t.sol +++ b/test/RewardTracker.t.sol @@ -280,8 +280,8 @@ contract RewardTrackerTest is Test { function testCorrectness_claimReward(uint128 amount0_, uint128 amount1_, uint8 stakeTimeAsDurationPercentage) public { - vm.assume(amount0_ > 0); - vm.assume(amount1_ > 0); + vm.assume(amount0_ > 500); + vm.assume(amount1_ > 500); vm.assume(stakeTimeAsDurationPercentage > 0); uint256 amount0 = amount0_; uint256 amount1 = amount1_; @@ -339,7 +339,7 @@ contract RewardTrackerTest is Test { (((REWARD_AMOUNT * stakeTimeAsDurationPercentage) / 100) * amount1) / (amount0 + amount1); } - assertApproxEqRel(rewardAmount, expectedRewardAmount, 0.001e18); + assertApproxEqAbs(rewardAmount, expectedRewardAmount, 0.001e18); } function testCorrectness_claimReward_withBonus( diff --git a/test/scDai-adapters/SparkScDaiAdapter.t.sol b/test/scDai-adapters/SparkScSDaiAdapter.t.sol similarity index 98% rename from test/scDai-adapters/SparkScDaiAdapter.t.sol rename to test/scDai-adapters/SparkScSDaiAdapter.t.sol index ee13d63b..6662880a 100644 --- a/test/scDai-adapters/SparkScDaiAdapter.t.sol +++ b/test/scDai-adapters/SparkScSDaiAdapter.t.sol @@ -10,7 +10,7 @@ import {Constants as C} from "../../src/lib/Constants.sol"; import {IAdapter} from "../../src/steth/IAdapter.sol"; import {SparkScSDaiAdapter} from "../../src/steth/scSDai-adapters/SparkScSDaiAdapter.sol"; -contract SparkScDaiAdapterTest is Test { +contract SparkScSDaiAdapterTest is Test { SparkScSDaiAdapter adapter; ERC20 sDai; WETH weth; diff --git a/test/scUSDCv2.t.sol b/test/scUSDCv2.t.sol index 6596c423..b0d88729 100644 --- a/test/scUSDCv2.t.sol +++ b/test/scUSDCv2.t.sol @@ -1593,7 +1593,7 @@ contract scUSDCv2Test is Test { } function testFuzz_withdraw(uint256 _amount, uint256 _withdrawAmount) public { - _amount = bound(_amount, 1e6, 4_500_000e6); // upper limit constrained by weth available on aave v3 + _amount = bound(_amount, 1e6, 8_000_000e6); // upper limit constrained by weth available on aave v3 at the fork block number _setBalance(alice, _amount); vm.startPrank(alice); @@ -1601,7 +1601,7 @@ contract scUSDCv2Test is Test { vault.deposit(_amount, alice); vm.stopPrank(); - uint256 borrowAmount = priceConverter.assetToTargetToken(_amount.mulWadDown(0.7e18)); + uint256 borrowAmount = priceConverter.assetToTargetToken(_amount.mulWadDown(0.6e18)); bytes[] memory callData; callData = _getSupplyAndBorrowCallData(callData, aaveV3.id(), _amount, borrowAmount); diff --git a/test/script/ExitAllPositionsScSDai.s.t.sol b/test/script/ExitAllPositionsScSDai.s.t.sol new file mode 100644 index 00000000..0bd6967f --- /dev/null +++ b/test/script/ExitAllPositionsScSDai.s.t.sol @@ -0,0 +1,82 @@ +// SPDX-License-Identifier: AGPL-3.0-only +pragma solidity ^0.8.19; + +import "forge-std/console2.sol"; +import "forge-std/Test.sol"; +import {FixedPointMathLib} from "solmate/utils/FixedPointMathLib.sol"; +import {SafeTransferLib} from "solmate/utils/SafeTransferLib.sol"; +import {ERC20} from "solmate/tokens/ERC20.sol"; +import {ERC4626} from "solmate/mixins/ERC4626.sol"; + +import {Constants as C} from "src/lib/Constants.sol"; +import {scCrossAssetYieldVault} from "src/steth/scCrossAssetYieldVault.sol"; +import {scSDAI} from "src/steth/scSDAI.sol"; +import {PriceConverter} from "src/steth/priceConverter/PriceConverter.sol"; +import {MainnetAddresses} from "script/base/MainnetAddresses.sol"; +import {ExitAllPositionsScSDai} from "script/v2/keeper-actions/ExitAllPositionsScSDai.s.sol"; +import {SparkScSDaiAdapter} from "src/steth/scSDai-adapters/SparkScSDaiAdapter.sol"; +import {SDaiWethPriceConverter} from "src/steth/priceConverter/SDaiWethPriceConverter.sol"; + +contract ExitAllPositionsScSDaiTest is Test { + using FixedPointMathLib for uint256; + using SafeTransferLib for ERC20; + + scSDAI vault; + SparkScSDaiAdapter spark; + SDaiWethPriceConverter priceConverter; + ExitAllPositionsScSDai script; + + constructor() { + uint256 mainnetFork = vm.createFork(vm.envString("RPC_URL_MAINNET")); + vm.selectFork(mainnetFork); + vm.rollFork(21031368); + + vault = scSDAI(MainnetAddresses.SCSDAI); + spark = SparkScSDaiAdapter(vault.getAdapter(1)); + priceConverter = SDaiWethPriceConverter(address(vault.priceConverter())); + + script = new ExitAllPositionsScSDaiTestHarness(vault); + } + + function test_run_exitsAllPositions() public { + assertTrue(vault.asset().balanceOf(address(vault)) > 0, "sDai balance"); + assertTrue(vault.totalCollateral() > 0, "total collateral"); + assertTrue(vault.totalDebt() > 0, "total debt"); + + // add 20% profit + ERC20 targetAsset = vault.targetVault().asset(); + uint256 targetVaultBalance = targetAsset.balanceOf(address(vault.targetVault())); + uint256 targetVaultTotalAssets = vault.targetVault().totalAssets(); + deal( + address(targetAsset), + address(vault.targetVault()), + targetVaultBalance + targetVaultTotalAssets.mulWadDown(0.2e18) + ); + + uint256 totalAssetsBefore = vault.totalAssets(); + uint256 maxLossPercent = script.maxAceeptableLossPercent(); + + // exit + script.run(); + + assertEq(script.targetTokensInvested(), 0, "weth invested"); + assertEq(vault.totalDebt(), 0, "total debt"); + assertEq(vault.totalCollateral(), 0, "total collateral"); + assertApproxEqRel(vault.totalAssets(), totalAssetsBefore, maxLossPercent, "total assets"); + } +} + +contract ExitAllPositionsScSDaiTestHarness is ExitAllPositionsScSDai { + constructor(scCrossAssetYieldVault _vault) { + vault = _vault; + } + + function _initEnv() internal override { + // TODO: WRONG KEEPER ADDRESS??? + keeper = 0x3Ab6EBDBf08e1954e69F6859AdB2DA5236D2e838; + } + + function _getVaultAddress() internal view override returns (scCrossAssetYieldVault) { + return vault; + } +} diff --git a/test/script/ExitAllPositionsScUsdcV2.s.t.sol b/test/script/ExitAllPositionsScUsdcV2.s.t.sol index b51de2df..27fce1a9 100644 --- a/test/script/ExitAllPositionsScUsdcV2.s.t.sol +++ b/test/script/ExitAllPositionsScUsdcV2.s.t.sol @@ -4,36 +4,22 @@ pragma solidity ^0.8.19; import "forge-std/console2.sol"; import "forge-std/Test.sol"; import {FixedPointMathLib} from "solmate/utils/FixedPointMathLib.sol"; -import {WETH} from "solmate/tokens/WETH.sol"; - -import {scUSDCv2} from "../../src/steth/scUSDCv2.sol"; -import {scWETHv2} from "../../src/steth/scWETHv2.sol"; -import {PriceConverter} from "../../src/steth/priceConverter/PriceConverter.sol"; -import {AaveV2ScUsdcAdapter} from "../../src/steth/scUsdcV2-adapters/AaveV2ScUsdcAdapter.sol"; -import {AaveV3ScUsdcAdapter} from "../../src/steth/scUsdcV2-adapters/AaveV3ScUsdcAdapter.sol"; -import {MorphoAaveV3ScUsdcAdapter} from "../../src/steth/scUsdcV2-adapters/MorphoAaveV3ScUsdcAdapter.sol"; -import {RebalanceScUsdcV2} from "../../script/v2/keeper-actions/RebalanceScUsdcV2.s.sol"; -import {MainnetAddresses} from "../../script/base/MainnetAddresses.sol"; -import {Constants} from "../../src/lib/Constants.sol"; - -import {RedeployScript} from "../../script/v2/RedeployScUsdcV2EthMainnet.s.sol"; -import {ExitAllPositionsScUsdcV2} from "../../script/v2/keeper-actions/ExitAllPositionsScUsdcV2.s.sol"; -import {scCrossAssetYieldVault} from "../../src/steth/scCrossAssetYieldVault.sol"; + +import {scCrossAssetYieldVault} from "src/steth/scCrossAssetYieldVault.sol"; +import {scUSDCv2} from "src/steth/scUSDCv2.sol"; +import {PriceConverter} from "src/steth/priceConverter/PriceConverter.sol"; +import {MainnetAddresses} from "script/base/MainnetAddresses.sol"; +import {ExitAllPositionsScUsdcV2} from "script/v2/keeper-actions/ExitAllPositionsScUsdcV2.s.sol"; contract ExitAllPositionsScUsdcV2Test is Test { using FixedPointMathLib for uint256; - uint256 mainnetFork; - scUSDCv2 vault; - AaveV3ScUsdcAdapter aaveV3; - AaveV2ScUsdcAdapter aaveV2; - MorphoAaveV3ScUsdcAdapter morpho; PriceConverter priceConverter; ExitAllPositionsScUsdcV2 script; function setUp() public { - mainnetFork = vm.createFork(vm.envString("RPC_URL_MAINNET")); + uint256 mainnetFork = vm.createFork(vm.envString("RPC_URL_MAINNET")); vm.selectFork(mainnetFork); vm.rollFork(18488739); @@ -44,7 +30,7 @@ contract ExitAllPositionsScUsdcV2Test is Test { } function test_run_exitsAllPositions() public { - assertEq(script.wethInvested(), 0, "weth invested"); + assertEq(script.targetTokensInvested(), 0, "weth invested"); assertEq(vault.totalDebt(), 0, "total debt"); assertEq(vault.totalCollateral(), 0, "total collateral"); assertTrue(vault.asset().balanceOf(address(vault)) > 0, "usdc balance"); @@ -63,14 +49,14 @@ contract ExitAllPositionsScUsdcV2Test is Test { uint256 totalAssetsBefore = vault.totalAssets(); uint256 maxLossPercent = script.maxAceeptableLossPercent(); - assertApproxEqAbs(script.wethInvested(), debtAmount, 1, "weth invested"); + assertApproxEqAbs(script.targetTokensInvested(), debtAmount, 1, "weth invested"); assertApproxEqAbs(vault.totalDebt(), debtAmount, 1, "total debt"); assertApproxEqAbs(vault.totalCollateral(), investAmount, 1, "total collateral"); // exit script.run(); - assertEq(script.wethInvested(), 0, "weth invested"); + assertEq(script.targetTokensInvested(), 0, "weth invested"); assertEq(vault.totalDebt(), 0, "total debt"); assertEq(vault.totalCollateral(), 0, "total collateral"); assertApproxEqRel(vault.totalAssets(), totalAssetsBefore, maxLossPercent, "total assets"); diff --git a/test/script/ExitAllPositionsScUsdt.s.t.sol b/test/script/ExitAllPositionsScUsdt.s.t.sol new file mode 100644 index 00000000..5f30ecb8 --- /dev/null +++ b/test/script/ExitAllPositionsScUsdt.s.t.sol @@ -0,0 +1,91 @@ +// SPDX-License-Identifier: AGPL-3.0-only +pragma solidity ^0.8.19; + +import "forge-std/console2.sol"; +import "forge-std/Test.sol"; +import {FixedPointMathLib} from "solmate/utils/FixedPointMathLib.sol"; +import {SafeTransferLib} from "solmate/utils/SafeTransferLib.sol"; +import {ERC20} from "solmate/tokens/ERC20.sol"; +import {ERC4626} from "solmate/mixins/ERC4626.sol"; + +import {Constants as C} from "src/lib/Constants.sol"; +import {scCrossAssetYieldVault} from "src/steth/scCrossAssetYieldVault.sol"; +import {scUSDT} from "src/steth/scUSDT.sol"; +import {PriceConverter} from "src/steth/priceConverter/PriceConverter.sol"; +import {MainnetAddresses} from "script/base/MainnetAddresses.sol"; +import {ExitAllPositionsScUsdt} from "script/v2/keeper-actions/ExitAllPositionsScUsdt.s.sol"; +import {AaveV3ScUsdtAdapter} from "src/steth/scUsdt-adapters/AaveV3ScUsdtAdapter.sol"; +import {UsdtWethPriceConverter} from "src/steth/priceConverter/UsdtWethPriceConverter.sol"; +import {UsdtWethSwapper} from "src/steth/swapper/UsdtWethSwapper.sol"; + +contract ExitAllPositionsScUsdtTest is Test { + using FixedPointMathLib for uint256; + using SafeTransferLib for ERC20; + + scUSDT vault; + UsdtWethPriceConverter priceConverter; + ExitAllPositionsScUsdt script; + + constructor() { + uint256 mainnetFork = vm.createFork(vm.envString("RPC_URL_MAINNET")); + vm.selectFork(mainnetFork); + vm.rollFork(21031368); + + priceConverter = UsdtWethPriceConverter(MainnetAddresses.USDT_WETH_PRICE_CONVERTER); + vault = scUSDT(MainnetAddresses.SCUSDT); + + script = new ExitAllPositionsScUsdtTestHarness(vault); + } + + function test_run_exitsAllPositions() public { + assertTrue(vault.asset().balanceOf(address(vault)) > 0, "usdt balance"); + assertTrue(vault.totalCollateral() > 0, "total collateral"); + assertTrue(vault.totalDebt() > 0, "total debt"); + + // add 20% profit + ERC20 targetAsset = vault.targetVault().asset(); + uint256 targetVaultBalance = targetAsset.balanceOf(address(vault.targetVault())); + uint256 targetVaultTotalAssets = vault.targetVault().totalAssets(); + deal( + address(targetAsset), + address(vault.targetVault()), + targetVaultBalance + targetVaultTotalAssets.mulWadDown(0.2e18) + ); + + uint256 totalAssetsBefore = vault.totalAssets(); + uint256 maxLossPercent = script.maxAceeptableLossPercent(); + + // exit + script.run(); + + assertEq(script.targetTokensInvested(), 0, "weth invested"); + assertEq(vault.totalDebt(), 0, "total debt"); + assertEq(vault.totalCollateral(), 0, "total collateral"); + assertApproxEqRel(vault.totalAssets(), totalAssetsBefore, maxLossPercent, "total assets"); + } + + function _rebalance(uint256 investAmount, uint256 debtAmount) internal { + bytes[] memory callData = new bytes[](2); + + callData[0] = abi.encodeWithSelector(scCrossAssetYieldVault.supply.selector, 1, investAmount); + callData[1] = abi.encodeWithSelector(scCrossAssetYieldVault.borrow.selector, 1, debtAmount); + + vm.prank(0x3Ab6EBDBf08e1954e69F6859AdB2DA5236D2e838); + vault.rebalance(callData); + } +} + +contract ExitAllPositionsScUsdtTestHarness is ExitAllPositionsScUsdt { + constructor(scCrossAssetYieldVault _vault) { + vault = _vault; + } + + function _initEnv() internal override { + // TODO: WRONG KEEPER ADDRESS??? + keeper = 0x3Ab6EBDBf08e1954e69F6859AdB2DA5236D2e838; + } + + function _getVaultAddress() internal view override returns (scCrossAssetYieldVault) { + return vault; + } +} diff --git a/test/script/ExitAllPositionsScWETHv2.t.sol b/test/script/ExitAllPositionsScWETHv2.s.t.sol similarity index 85% rename from test/script/ExitAllPositionsScWETHv2.t.sol rename to test/script/ExitAllPositionsScWETHv2.s.t.sol index 87b86153..6d05a90e 100644 --- a/test/script/ExitAllPositionsScWETHv2.t.sol +++ b/test/script/ExitAllPositionsScWETHv2.s.t.sol @@ -14,13 +14,13 @@ import {ERC20} from "solmate/tokens/ERC20.sol"; import {Address} from "openzeppelin-contracts/utils/Address.sol"; import {WETH} from "solmate/tokens/WETH.sol"; -import {Constants as C} from "../../src/lib/Constants.sol"; -import {MainnetAddresses} from "../../script/base/MainnetAddresses.sol"; -import {RebalanceScWethV2} from "../../script/v2/keeper-actions/RebalanceScWethV2.s.sol"; -import {ExitAllPositionsScWethV2} from "../../script/v2/keeper-actions/ExitAllPositionsScWethV2.s.sol"; +import {Constants as C} from "src/lib/Constants.sol"; +import {MainnetAddresses} from "script/base/MainnetAddresses.sol"; +import {RebalanceScWethV2} from "script/v2/keeper-actions/RebalanceScWethV2.s.sol"; +import {ExitAllPositionsScWethV2} from "script/v2/keeper-actions/ExitAllPositionsScWethV2.s.sol"; -import {scWETHv2} from "../../src/steth/scWETHv2.sol"; -import {IAdapter} from "../../src/steth/IAdapter.sol"; +import {scWETHv2} from "src/steth/scWETHv2.sol"; +import {IAdapter} from "src/steth/IAdapter.sol"; contract ExitAllPositionsScWETHv2Test is Test { using FixedPointMathLib for uint256; diff --git a/test/script/ReallocateScUsdcV2.s.t.sol b/test/script/ReallocateScUsdcV2.s.t.sol index ebea7dcf..3478b962 100644 --- a/test/script/ReallocateScUsdcV2.s.t.sol +++ b/test/script/ReallocateScUsdcV2.s.t.sol @@ -5,17 +5,17 @@ import "forge-std/console2.sol"; import "forge-std/Test.sol"; import {FixedPointMathLib} from "solmate/utils/FixedPointMathLib.sol"; -import {Constants as C} from "../../src/lib/Constants.sol"; -import {AggregatorV3Interface} from "../../src/interfaces/chainlink/AggregatorV3Interface.sol"; -import {scUSDCv2} from "../../src/steth/scUSDCv2.sol"; -import {PriceConverter} from "../../src/steth/priceConverter/PriceConverter.sol"; -import {IAdapter} from "../../src/steth/IAdapter.sol"; -import {AaveV2ScUsdcAdapter} from "../../src/steth/scUsdcV2-adapters/AaveV2ScUsdcAdapter.sol"; -import {AaveV3ScUsdcAdapter} from "../../src/steth/scUsdcV2-adapters/AaveV3ScUsdcAdapter.sol"; -import {MorphoAaveV3ScUsdcAdapter} from "../../src/steth/scUsdcV2-adapters/MorphoAaveV3ScUsdcAdapter.sol"; -import {ReallocateScUsdcV2} from "../../script/v2/keeper-actions/ReallocateScUsdcV2.s.sol"; -import {MainnetAddresses} from "../../script/base/MainnetAddresses.sol"; -import {ISinglePairPriceConverter} from "../../src/steth/priceConverter/ISinglePairPriceConverter.sol"; +import {Constants as C} from "src/lib/Constants.sol"; +import {AggregatorV3Interface} from "src/interfaces/chainlink/AggregatorV3Interface.sol"; +import {scUSDCv2} from "src/steth/scUSDCv2.sol"; +import {PriceConverter} from "src/steth/priceConverter/PriceConverter.sol"; +import {IAdapter} from "src/steth/IAdapter.sol"; +import {AaveV2ScUsdcAdapter} from "src/steth/scUsdcV2-adapters/AaveV2ScUsdcAdapter.sol"; +import {AaveV3ScUsdcAdapter} from "src/steth/scUsdcV2-adapters/AaveV3ScUsdcAdapter.sol"; +import {MorphoAaveV3ScUsdcAdapter} from "src/steth/scUsdcV2-adapters/MorphoAaveV3ScUsdcAdapter.sol"; +import {ReallocateScUsdcV2} from "script/v2/keeper-actions/ReallocateScUsdcV2.s.sol"; +import {MainnetAddresses} from "script/base/MainnetAddresses.sol"; +import {ISinglePairPriceConverter} from "src/steth/priceConverter/ISinglePairPriceConverter.sol"; contract ReallocateScUsdcV2Test is Test { using FixedPointMathLib for uint256; @@ -70,7 +70,7 @@ contract ReallocateScUsdcV2Test is Test { script.setAaveV2AllocationPercent(aaveV2AllocationPercent); uint256 totalAssetsBefore = vault.totalAssets(); - uint256 wethInvestedBefore = script.wethInvested(); + uint256 wethInvestedBefore = script.targetTokensInvested(); script.run(); @@ -95,7 +95,7 @@ contract ReallocateScUsdcV2Test is Test { _getAllocationPercent(aaveV2), aaveV2AllocationPercent, 0.0001e18, "aave v2 allocation percent" ); assertApproxEqRel(vault.totalAssets(), totalAssetsBefore, 0.0001e18, "total assets"); - assertApproxEqRel(script.wethInvested(), wethInvestedBefore, 0.0001e18, "weth invested"); + assertApproxEqRel(script.targetTokensInvested(), wethInvestedBefore, 0.0001e18, "weth invested"); } function test_run_moveWholePositionFromAaveV2ToMorpho() public { @@ -114,7 +114,7 @@ contract ReallocateScUsdcV2Test is Test { assertTrue(script.useMorpho(), "morpho not used"); uint256 totalAssetsBefore = vault.totalAssets(); - uint256 wethInvestedBefore = script.wethInvested(); + uint256 wethInvestedBefore = script.targetTokensInvested(); uint256 morphoAllocationPercent = 1e18; uint256 aaveV2AllocationPercent = 0; @@ -138,7 +138,7 @@ contract ReallocateScUsdcV2Test is Test { assertEq(_getAllocationPercent(morpho), morphoAllocationPercent, "morpho allocation percent"); assertEq(_getAllocationPercent(aaveV2), aaveV2AllocationPercent, "aave v2 allocation percent"); assertApproxEqAbs(vault.totalAssets(), totalAssetsBefore, 1, "total assets"); - assertEq(script.wethInvested(), wethInvestedBefore, "weth invested"); + assertEq(script.targetTokensInvested(), wethInvestedBefore, "weth invested"); } function test_run_moveHalfFromAaveV2AndMorphoToAaveV3() public { @@ -159,7 +159,7 @@ contract ReallocateScUsdcV2Test is Test { uint256 expectedMorphoRepayAmount = morphoInitialDebt / 2; uint256 totalAssetsBefore = vault.totalAssets(); - uint256 wethInvestedBefore = script.wethInvested(); + uint256 wethInvestedBefore = script.targetTokensInvested(); script.setUseAaveV3(true); assertTrue(script.useAaveV2(), "aave v2 not used"); @@ -216,7 +216,7 @@ contract ReallocateScUsdcV2Test is Test { ); assertApproxEqAbs(vault.totalAssets(), totalAssetsBefore, 1, "total assets"); - assertApproxEqAbs(script.wethInvested(), wethInvestedBefore, 1, "weth invested"); + assertApproxEqAbs(script.targetTokensInvested(), wethInvestedBefore, 1, "weth invested"); } function test_run_failsIfAllocationPercentSumIsNot100() public { diff --git a/test/script/ReallocateScWethV2.s.t.sol b/test/script/ReallocateScWethV2.s.t.sol index a522fa98..d6dea0fc 100644 --- a/test/script/ReallocateScWethV2.s.t.sol +++ b/test/script/ReallocateScWethV2.s.t.sol @@ -14,12 +14,12 @@ import {ERC20} from "solmate/tokens/ERC20.sol"; import {Address} from "openzeppelin-contracts/utils/Address.sol"; import {WETH} from "solmate/tokens/WETH.sol"; -import {Constants as C} from "../../src/lib/Constants.sol"; -import {MainnetAddresses} from "../../script/base/MainnetAddresses.sol"; -import {RebalanceScWethV2} from "../../script/v2/keeper-actions/RebalanceScWethV2.s.sol"; -import {ReallocateScWethV2} from "../../script/v2/keeper-actions/ReallocateScWethV2.s.sol"; -import {scWETHv2} from "../../src/steth/scWETHv2.sol"; -import {IAdapter} from "../../src/steth/IAdapter.sol"; +import {Constants as C} from "src/lib/Constants.sol"; +import {MainnetAddresses} from "script/base/MainnetAddresses.sol"; +import {RebalanceScWethV2} from "script/v2/keeper-actions/RebalanceScWethV2.s.sol"; +import {ReallocateScWethV2} from "script/v2/keeper-actions/ReallocateScWethV2.s.sol"; +import {scWETHv2} from "src/steth/scWETHv2.sol"; +import {IAdapter} from "src/steth/IAdapter.sol"; contract ReallocateScWethV2Test is Test { using FixedPointMathLib for uint256; diff --git a/test/script/RebalanceScDai.s.t.sol b/test/script/RebalanceScDai.s.t.sol deleted file mode 100644 index fcbd0b8b..00000000 --- a/test/script/RebalanceScDai.s.t.sol +++ /dev/null @@ -1,32 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-only -pragma solidity ^0.8.19; - -import "forge-std/console2.sol"; -import "forge-std/Test.sol"; -import {FixedPointMathLib} from "solmate/utils/FixedPointMathLib.sol"; -import {WETH} from "solmate/tokens/WETH.sol"; - -import {scUSDCv2} from "../../src/steth/scUSDCv2.sol"; -import {PriceConverter} from "../../src/steth/priceConverter/PriceConverter.sol"; -import {AaveV2ScUsdcAdapter} from "../../src/steth/scUsdcV2-adapters/AaveV2ScUsdcAdapter.sol"; -import {AaveV3ScUsdcAdapter} from "../../src/steth/scUsdcV2-adapters/AaveV3ScUsdcAdapter.sol"; -import {MorphoAaveV3ScUsdcAdapter} from "../../src/steth/scUsdcV2-adapters/MorphoAaveV3ScUsdcAdapter.sol"; -import {RebalanceScUsdcV2} from "../../script/v2/keeper-actions/RebalanceScUsdcV2.s.sol"; -import {MainnetAddresses} from "../../script/base/MainnetAddresses.sol"; -import {Constants} from "../../src/lib/Constants.sol"; - -contract RebalanceScDaiTest is Test { - uint256 mainnetFork; - - constructor() { - mainnetFork = vm.createFork(vm.envString("RPC_URL_MAINNET")); - // vm.selectFork(mainnetFork); - // vm.rollFork(18488739); - - // script = new RebalanceScUsdcV2TestHarness(); - - // vault = scUSDCv2(MainnetAddresses.SCDAI); - // priceConverter = vault.priceConverter(); - // spark = script.sparkAdapter(); - } -} diff --git a/test/script/RebalanceScSDai.s.t.sol b/test/script/RebalanceScSDai.s.t.sol new file mode 100644 index 00000000..f91911cf --- /dev/null +++ b/test/script/RebalanceScSDai.s.t.sol @@ -0,0 +1,146 @@ +// SPDX-License-Identifier: AGPL-3.0-only +pragma solidity ^0.8.19; + +import "forge-std/console2.sol"; +import "forge-std/Test.sol"; +import {FixedPointMathLib} from "solmate/utils/FixedPointMathLib.sol"; +import {SafeTransferLib} from "solmate/utils/SafeTransferLib.sol"; +import {WETH} from "solmate/tokens/WETH.sol"; +import {ERC20} from "solmate/tokens/ERC20.sol"; +import {ERC4626} from "solmate/mixins/ERC4626.sol"; + +import {Constants as C} from "src/lib/Constants.sol"; +import {scSDAI} from "src/steth/scSDAI.sol"; +import {scCrossAssetYieldVault} from "src/steth/scCrossAssetYieldVault.sol"; +import {SparkScSDaiAdapter} from "src/steth/scSDai-adapters/SparkScSDaiAdapter.sol"; +import {SDaiWethPriceConverter} from "src/steth/priceConverter/SDaiWethPriceConverter.sol"; + +import {RebalanceScSDai} from "script/v2/keeper-actions/RebalanceScSDai.s.sol"; +import {scCrossAssetYieldVaultRebalanceScript} from "script/base/scCrossAssetYieldVaultRebalanceScript.sol"; +import {MainnetAddresses} from "script/base/MainnetAddresses.sol"; + +contract RebalanceScSDaiTest is Test { + using FixedPointMathLib for uint256; + using SafeTransferLib for ERC20; + + uint256 mainnetFork; + + scSDAI vault; + SparkScSDaiAdapter spark; + SDaiWethPriceConverter priceConverter; + RebalanceScSDaiTestHarness script; + + constructor() { + mainnetFork = vm.createFork(vm.envString("RPC_URL_MAINNET")); + vm.selectFork(mainnetFork); + vm.rollFork(21031368); + + vault = scSDAI(MainnetAddresses.SCSDAI); + spark = SparkScSDaiAdapter(MainnetAddresses.SCSDAI_SPARK_ADAPTER); + priceConverter = SDaiWethPriceConverter(MainnetAddresses.SDAI_WETH_PRICE_CONVERTER); + + script = new RebalanceScSDaiTestHarness(vault); + } + + function test_run_initialRebalance() public { + deal(C.SDAI, address(vault), 10000e18); + + uint256 expectedFloat = script.totalAssets().mulWadDown(vault.floatPercentage()); + uint256 expectedCollateral = script.totalCollateral() + script.assetBalance() - expectedFloat; + uint256 expectedDebt = priceConverter.assetToTargetToken(expectedCollateral).mulWadDown(script.sparkTargetLtv()); + + script.run(); + + assertApproxEqRel(script.targetTokensInvested(), expectedDebt, 0.001e18, "weth invested"); + assertApproxEqRel(script.totalDebt(), expectedDebt, 0.001e18, "total debt"); + assertApproxEqRel(script.totalCollateral(), expectedCollateral, 0.001e18, "total collateral"); + assertApproxEqRel(script.assetBalance(), expectedFloat, 0.001e18, "sDai balance"); + } + + function test_run_rebalanceWithProfit() public { + script.run(); + + _assertInitialState(); + + _simulate100PctProfit(); + + uint256 expectedFloat = script.totalAssets().mulWadDown(vault.floatPercentage()); + uint256 expectedCollateral = script.totalCollateral().mulWadDown(1e18 + script.sparkTargetLtv()); + uint256 expectedDebt = priceConverter.assetToTargetToken(expectedCollateral).mulWadDown(script.sparkTargetLtv()); + + script = new RebalanceScSDaiTestHarness(vault); + script.run(); + + assertApproxEqRel(script.targetTokensInvested(), expectedDebt, 0.01e18, "weth invested"); + assertApproxEqRel(script.totalDebt(), expectedDebt, 0.01e18, "total debt"); + assertApproxEqRel(script.totalCollateral(), expectedCollateral, 0.01e18, "total collateral"); + assertApproxEqRel(script.assetBalance(), expectedFloat, 0.8e18, "sDai balance"); + } + + function test_run_canDeleverage() public { + script.run(); + + _assertInitialState(); + + script = new RebalanceScSDaiTestHarness(vault); + script.setSparkTargetLtv(0.2e18); + + uint256 expectedFloat = script.totalAssets().mulWadDown(vault.floatPercentage()); + uint256 expectedCollateral = script.totalCollateral(); + uint256 expectedDebt = priceConverter.assetToTargetToken(expectedCollateral).mulWadDown(script.sparkTargetLtv()); + + script.run(); + + assertApproxEqRel(script.targetTokensInvested(), expectedDebt, 0.01e18, "weth invested"); + assertApproxEqRel(script.totalDebt(), expectedDebt, 0.01e18, "total debt"); + assertApproxEqRel(script.totalCollateral(), expectedCollateral, 0.01e18, "total collateral"); + assertApproxEqRel(script.assetBalance(), expectedFloat, 0.8e18, "sDai balance"); + } + + function _assertInitialState() internal { + assertTrue(script.targetTokensInvested() > 0, "initial weth invested 0"); + assertTrue(vault.totalDebt() > 0, "initial total debt 0"); + assertTrue(vault.totalCollateral() > 0, "initial total collateral 0"); + assertTrue(script.assetBalance() > 0, "initial sDai balance 0"); + } + + function _simulate100PctProfit() internal { + WETH weth = WETH(payable(C.WETH)); + + console2.log("weth balance before", weth.balanceOf(address(script.targetVault()))); + console2.log("total assets before", script.targetVault().totalAssets()); + + // simulate 100% profit by dealing more WETH to scWETH vault + deal( + address(weth), + address(script.targetVault()), + script.targetVault().totalAssets() + weth.balanceOf(address(script.targetVault())) + ); + + console2.log("weth balance after", weth.balanceOf(address(script.targetVault()))); + console2.log("total assets after", script.targetVault().totalAssets()); + } +} + +contract RebalanceScSDaiTestHarness is RebalanceScSDai { + constructor(scSDAI _vault) { + vault = _vault; + } + + function _initEnv() internal override { + // TODO: WRONG KEEPER ADDRESS??? + keeper = 0x3Ab6EBDBf08e1954e69F6859AdB2DA5236D2e838; + } + + function _getVaultAddress() internal view override returns (scCrossAssetYieldVault) { + return scCrossAssetYieldVault(vault); + } + + function setSparkTargetLtv(uint256 _newTargetLtv) public { + sparkTargetLtv = _newTargetLtv; + } + + function setSparkInvestableAmountPercent(uint256 _newInvestableAmountPercent) public { + sparkInvestableAmountPercent = _newInvestableAmountPercent; + } +} diff --git a/test/script/RebalanceScUsdcV2.s.t.sol b/test/script/RebalanceScUsdcV2.s.t.sol index 0baf1e27..6535ca28 100644 --- a/test/script/RebalanceScUsdcV2.s.t.sol +++ b/test/script/RebalanceScUsdcV2.s.t.sol @@ -7,15 +7,16 @@ import {FixedPointMathLib} from "solmate/utils/FixedPointMathLib.sol"; import {WETH} from "solmate/tokens/WETH.sol"; import {ERC20} from "solmate/tokens/ERC20.sol"; -import {Constants as C} from "../../src/lib/Constants.sol"; -import {scUSDCv2} from "../../src/steth/scUSDCv2.sol"; -import {PriceConverter} from "../../src/steth/priceConverter/PriceConverter.sol"; -import {AaveV2ScUsdcAdapter} from "../../src/steth/scUsdcV2-adapters/AaveV2ScUsdcAdapter.sol"; -import {AaveV3ScUsdcAdapter} from "../../src/steth/scUsdcV2-adapters/AaveV3ScUsdcAdapter.sol"; -import {MorphoAaveV3ScUsdcAdapter} from "../../src/steth/scUsdcV2-adapters/MorphoAaveV3ScUsdcAdapter.sol"; -import {RebalanceScUsdcV2} from "../../script/v2/keeper-actions/RebalanceScUsdcV2.s.sol"; -import {MainnetAddresses} from "../../script/base/MainnetAddresses.sol"; -import {Constants} from "../../src/lib/Constants.sol"; +import {Constants as C} from "src/lib/Constants.sol"; +import {scUSDCv2} from "src/steth/scUSDCv2.sol"; +import {PriceConverter} from "src/steth/priceConverter/PriceConverter.sol"; +import {AaveV2ScUsdcAdapter} from "src/steth/scUsdcV2-adapters/AaveV2ScUsdcAdapter.sol"; +import {AaveV3ScUsdcAdapter} from "src/steth/scUsdcV2-adapters/AaveV3ScUsdcAdapter.sol"; +import {MorphoAaveV3ScUsdcAdapter} from "src/steth/scUsdcV2-adapters/MorphoAaveV3ScUsdcAdapter.sol"; +import {RebalanceScUsdcV2} from "script/v2/keeper-actions/RebalanceScUsdcV2.s.sol"; +import {scCrossAssetYieldVaultRebalanceScript} from "script/base/scCrossAssetYieldVaultRebalanceScript.sol"; +import {MainnetAddresses} from "script/base/MainnetAddresses.sol"; +import {Constants} from "src/lib/Constants.sol"; contract RebalanceScUsdcV2Test is Test { using FixedPointMathLib for uint256; @@ -44,21 +45,21 @@ contract RebalanceScUsdcV2Test is Test { } function test_run_initialRebalance() public { - assertEq(script.wethInvested(), 0, "weth invested"); + assertEq(script.targetTokensInvested(), 0, "weth invested"); assertEq(vault.totalDebt(), 0, "total debt"); assertEq(vault.totalCollateral(), 0, "total collateral"); - assertTrue(script.usdcBalance() > 0, "usdc balance"); + assertTrue(script.assetBalance() > 0, "usdc balance"); uint256 expectedFloat = vault.totalAssets().mulWadDown(vault.floatPercentage()); - uint256 expectedCollateral = script.usdcBalance() - expectedFloat; + uint256 expectedCollateral = script.assetBalance() - expectedFloat; uint256 expectedDebt = priceConverter.usdcToEth(expectedCollateral).mulWadDown(script.morphoTargetLtv()); script.run(); - assertApproxEqRel(script.wethInvested(), expectedDebt, 0.001e18, "weth invested"); + assertApproxEqRel(script.targetTokensInvested(), expectedDebt, 0.001e18, "weth invested"); assertApproxEqRel(vault.totalDebt(), expectedDebt, 0.001e18, "total debt"); assertApproxEqRel(vault.totalCollateral(), expectedCollateral, 0.001e18, "total collateral"); - assertApproxEqRel(script.usdcBalance(), expectedFloat, 0.001e18, "usdc balance"); + assertApproxEqRel(script.assetBalance(), expectedFloat, 0.001e18, "usdc balance"); } function test_run_failsIfTotalInvestableAmountPercentNot100() public { @@ -77,15 +78,15 @@ contract RebalanceScUsdcV2Test is Test { script.setAaveV2InvestableAmountPercent(aaveV2InvestableAmountPercent); uint256 expectedFloat = vault.totalAssets().mulWadDown(vault.floatPercentage()); - uint256 expectedCollateral = script.usdcBalance() - expectedFloat; + uint256 expectedCollateral = script.assetBalance() - expectedFloat; uint256 expectedDebt = priceConverter.usdcToEth(expectedCollateral).mulWadDown(script.morphoTargetLtv()); script.run(); - assertApproxEqRel(script.wethInvested(), expectedDebt, 0.001e18, "weth invested"); + assertApproxEqRel(script.targetTokensInvested(), expectedDebt, 0.001e18, "weth invested"); assertApproxEqRel(vault.totalDebt(), expectedDebt, 0.001e18, "total debt"); assertApproxEqRel(vault.totalCollateral(), expectedCollateral, 0.001e18, "total collateral"); - assertApproxEqRel(script.usdcBalance(), expectedFloat, 0.001e18, "usdc balance"); + assertApproxEqRel(script.assetBalance(), expectedFloat, 0.001e18, "usdc balance"); uint256 morphoExpectedCollateral = expectedCollateral.mulWadDown(morphoInvestableAmountPercent); uint256 aaveV2ExpectedCollateral = expectedCollateral.mulWadDown(aaveV2InvestableAmountPercent); @@ -115,10 +116,10 @@ contract RebalanceScUsdcV2Test is Test { script.run(); - assertApproxEqRel(script.wethInvested(), expectedDebt, 0.001e18, "weth invested"); + assertApproxEqRel(script.targetTokensInvested(), expectedDebt, 0.001e18, "weth invested"); assertApproxEqRel(vault.totalDebt(), expectedDebt, 0.001e18, "total debt"); assertApproxEqRel(vault.totalCollateral(), expectedCollateral, 0.001e18, "total collateral"); - assertApproxEqRel(script.usdcBalance(), expectedFloat, 0.001e18, "usdc balance"); + assertApproxEqRel(script.assetBalance(), expectedFloat, 0.001e18, "usdc balance"); } function test_run_twoAdaptersLeverageDown() public { @@ -199,8 +200,8 @@ contract RebalanceScUsdcV2Test is Test { script.run(); - assertApproxEqRel(script.usdcBalance(), expectedFloat, 0.001e18, "usdc balance"); - assertApproxEqRel(script.wethInvested(), expectedDebt, 0.001e18, "weth invested"); + assertApproxEqRel(script.assetBalance(), expectedFloat, 0.001e18, "usdc balance"); + assertApproxEqRel(script.targetTokensInvested(), expectedDebt, 0.001e18, "weth invested"); assertApproxEqRel(vault.totalDebt(), expectedDebt, 0.001e18, "total debt"); assertApproxEqRel(vault.totalCollateral(), expectedCollateral, 0.001e18, "total collateral"); assertTrue(vault.totalDebt() >= debtBefore, "total debt decreased"); @@ -227,8 +228,8 @@ contract RebalanceScUsdcV2Test is Test { script.run(); - assertApproxEqRel(script.usdcBalance(), expectedFloat, 0.001e18, "usdc balance"); - assertApproxEqRel(script.wethInvested(), expectedDebt, 0.001e18, "weth invested"); + assertApproxEqRel(script.assetBalance(), expectedFloat, 0.001e18, "usdc balance"); + assertApproxEqRel(script.targetTokensInvested(), expectedDebt, 0.001e18, "weth invested"); assertApproxEqRel(vault.totalDebt(), expectedDebt, 0.001e18, "total debt"); assertApproxEqRel(vault.totalCollateral(), expectedCollateral, 0.001e18, "total collateral"); assertTrue(vault.totalDebt() <= debtBefore, "total debt increased"); @@ -250,8 +251,8 @@ contract RebalanceScUsdcV2Test is Test { script.run(); - assertApproxEqRel(script.usdcBalance(), expectedFloat, 0.001e18, "usdc balance"); - assertApproxEqRel(script.wethInvested(), expectedDebt, 0.001e18, "weth invested"); + assertApproxEqRel(script.assetBalance(), expectedFloat, 0.001e18, "usdc balance"); + assertApproxEqRel(script.targetTokensInvested(), expectedDebt, 0.001e18, "weth invested"); assertApproxEqRel(vault.totalDebt(), expectedDebt, 0.001e18, "total debt"); assertApproxEqRel(vault.totalCollateral(), expectedCollateral, 0.001e18, "total collateral"); } @@ -266,17 +267,17 @@ contract RebalanceScUsdcV2Test is Test { _assertInitialState(); - vault.withdraw(script.usdcBalance(), address(this), address(this)); - assertEq(script.usdcBalance(), 0, "usdc balance"); + vault.withdraw(script.assetBalance(), address(this), address(this)); + assertEq(script.assetBalance(), 0, "usdc balance"); uint256 expectedFloat = vault.totalAssets().mulWadDown(vault.floatPercentage()); uint256 expectedCollateral = vault.totalAssets() - expectedFloat; uint256 expectedDebt = vault.totalDebt(); - uint256 wethInvested = script.wethInvested(); + uint256 wethInvested = script.targetTokensInvested(); script.run(); - assertApproxEqRel(script.wethInvested(), wethInvested, 0.001e18, "weth invested"); + assertApproxEqRel(script.targetTokensInvested(), wethInvested, 0.001e18, "weth invested"); assertApproxEqRel(vault.totalDebt(), expectedDebt, 0.001e18, "total debt"); assertApproxEqRel(vault.totalCollateral(), expectedCollateral, 0.001e18, "total collateral"); } @@ -291,8 +292,8 @@ contract RebalanceScUsdcV2Test is Test { _assertInitialState(); - vault.withdraw(script.usdcBalance(), address(this), address(this)); - assertEq(script.usdcBalance(), 0, "usdc balance"); + vault.withdraw(script.assetBalance(), address(this), address(this)); + assertEq(script.assetBalance(), 0, "usdc balance"); uint256 newMorphoTargetLtv = script.morphoTargetLtv() + 0.05e18; script.setMorphoTargetLtv(newMorphoTargetLtv); @@ -303,7 +304,7 @@ contract RebalanceScUsdcV2Test is Test { script.run(); - assertApproxEqRel(script.wethInvested(), expectedDebt, 0.001e18, "weth invested"); + assertApproxEqRel(script.targetTokensInvested(), expectedDebt, 0.001e18, "weth invested"); assertApproxEqRel(vault.totalDebt(), expectedDebt, 0.001e18, "total debt"); assertApproxEqRel(vault.totalCollateral(), expectedCollateral, 0.001e18, "total collateral"); } @@ -318,8 +319,8 @@ contract RebalanceScUsdcV2Test is Test { _assertInitialState(); - vault.withdraw(script.usdcBalance(), address(this), address(this)); - assertEq(script.usdcBalance(), 0, "usdc balance"); + vault.withdraw(script.assetBalance(), address(this), address(this)); + assertEq(script.assetBalance(), 0, "usdc balance"); uint256 newMorphoTargetLtv = script.morphoTargetLtv() - 0.05e18; script.setMorphoTargetLtv(newMorphoTargetLtv); @@ -330,7 +331,7 @@ contract RebalanceScUsdcV2Test is Test { script.run(); - assertApproxEqRel(script.wethInvested(), expectedDebt, 0.001e18, "weth invested"); + assertApproxEqRel(script.targetTokensInvested(), expectedDebt, 0.001e18, "weth invested"); assertApproxEqRel(vault.totalDebt(), expectedDebt, 0.001e18, "total debt"); assertApproxEqRel(vault.totalCollateral(), expectedCollateral, 0.001e18, "total collateral"); } @@ -347,7 +348,7 @@ contract RebalanceScUsdcV2Test is Test { script.setAaveV3TargetLtv(newAaveV3TargetLtv); // 50% uint256 expectedFloat = vault.totalAssets().mulWadDown(vault.floatPercentage()); - uint256 investableAmount = script.usdcBalance() - expectedFloat; + uint256 investableAmount = script.assetBalance() - expectedFloat; uint256 expectedAaveV3Debt = priceConverter.usdcToEth(investableAmount).mulWadDown(newAaveV3TargetLtv); script.run(); @@ -371,7 +372,11 @@ contract RebalanceScUsdcV2Test is Test { script.setAaveV3InvestableAmountPercent(0.2e18); // 100% script.setAaveV3TargetLtv(0); - vm.expectRevert(abi.encodePacked(RebalanceScUsdcV2.ScriptCannotUseUnsupportedAdapter.selector, aaveV3.id())); + vm.expectRevert( + abi.encodePacked( + scCrossAssetYieldVaultRebalanceScript.ScriptCannotUseUnsupportedAdapter.selector, aaveV3.id() + ) + ); script.run(); } @@ -390,7 +395,11 @@ contract RebalanceScUsdcV2Test is Test { script.setAaveV3InvestableAmountPercent(0); script.setAaveV3TargetLtv(0.5e18); // 50% - vm.expectRevert(abi.encodePacked(RebalanceScUsdcV2.ScriptCannotUseUnsupportedAdapter.selector, aaveV3.id())); + vm.expectRevert( + abi.encodePacked( + scCrossAssetYieldVaultRebalanceScript.ScriptCannotUseUnsupportedAdapter.selector, aaveV3.id() + ) + ); script.run(); } @@ -429,7 +438,7 @@ contract RebalanceScUsdcV2Test is Test { assertTrue(vault.getProfit() == 0, "profit != 0"); - uint256 wethInvested = script.wethInvested(); + uint256 wethInvested = script.targetTokensInvested(); _simulate100PctProfit(); @@ -448,12 +457,12 @@ contract RebalanceScUsdcV2Test is Test { script.run(); assertApproxEqAbs(vault.getProfit(), 0, 2, "profit not sold entirely"); - assertTrue(script.usdcBalance() >= expectedFloat, "float balance"); + assertTrue(script.assetBalance() >= expectedFloat, "float balance"); assertApproxEqRel( vault.totalCollateral(), initialCollateral + approxUsdcReinvested, 0.01e18, "total collateral" ); assertApproxEqRel(vault.totalDebt(), initialDebt + approxAdditionalDebt, 0.01e18, "total debt"); - assertApproxEqRel(script.wethInvested(), wethInvested + approxAdditionalDebt, 0.01e18, "weth invested"); + assertApproxEqRel(script.targetTokensInvested(), wethInvested + approxAdditionalDebt, 0.01e18, "weth invested"); } function test_run_doesntSellProfitIfBelowDefinedMin() public { @@ -469,21 +478,21 @@ contract RebalanceScUsdcV2Test is Test { // set min profit to reinvest to 2x the actual profit script.setMinUsdcProfitToReinvest(priceConverter.ethToUsdc(wethProfit * 2)); - uint256 wethInvested = script.wethInvested(); + uint256 wethInvested = script.targetTokensInvested(); uint256 expectedFloat = vault.totalAssets().mulWadDown(vault.floatPercentage()); uint256 initialCollateral = vault.totalCollateral(); uint256 initialDebt = vault.totalDebt(); - uint256 missingFloat = expectedFloat - script.usdcBalance(); + uint256 missingFloat = expectedFloat - script.assetBalance(); uint256 wethDisinvested = priceConverter.usdcToEth(missingFloat); script.run(); assertEq(vault.getProfit(), wethProfit, "profit"); - assertTrue(script.usdcBalance() >= expectedFloat, "float balance"); + assertTrue(script.assetBalance() >= expectedFloat, "float balance"); assertApproxEqRel(vault.totalCollateral(), initialCollateral - missingFloat, 0.005e18, "total collateral"); assertApproxEqRel(vault.totalDebt(), initialDebt, 0.005e18, "total debt"); - assertApproxEqRel(script.wethInvested(), wethInvested - wethDisinvested, 0.005e18, "weth invested"); + assertApproxEqRel(script.targetTokensInvested(), wethInvested - wethDisinvested, 0.005e18, "weth invested"); } function test_run_failsIfRealizedSlippageOnSellingProfitsIsTooHigh() public { @@ -512,7 +521,7 @@ contract RebalanceScUsdcV2Test is Test { _assertInitialState(); - uint256 wethInvested = script.wethInvested(); + uint256 wethInvested = script.targetTokensInvested(); _simulate100PctProfit(); assertApproxEqAbs(vault.getProfit(), wethInvested, 1, "profit != wethInvested"); @@ -530,7 +539,7 @@ contract RebalanceScUsdcV2Test is Test { script.run(); assertApproxEqAbs(vault.getProfit(), 0, 2, "profit not sold entirely"); - assertApproxEqRel(script.wethInvested(), expectedDebt, 0.01e18, "weth invested"); + assertApproxEqRel(script.targetTokensInvested(), expectedDebt, 0.01e18, "weth invested"); assertApproxEqRel(vault.totalDebt(), expectedDebt, 0.01e18, "total debt"); assertApproxEqRel(vault.totalCollateral(), expectedCollateral, 0.001e18, "total collateral"); assertTrue(vault.totalCollateral() > initialCollateral, "total collateral not increased"); @@ -541,10 +550,10 @@ contract RebalanceScUsdcV2Test is Test { } function _assertInitialState() internal { - assertTrue(script.wethInvested() > 0, "weth invested"); + assertTrue(script.targetTokensInvested() > 0, "weth invested"); assertTrue(vault.totalDebt() > 0, "total debt"); assertTrue(vault.totalCollateral() > 0, "total collateral"); - assertTrue(script.usdcBalance() > 0, "usdc balance"); + assertTrue(script.assetBalance() > 0, "usdc balance"); } function _simulate100PctProfit() internal { @@ -553,15 +562,15 @@ contract RebalanceScUsdcV2Test is Test { // simulate 100% profit by dealing more WETH to scWETH vault deal( address(weth), - address(script.scWETH()), - script.scWETH().totalAssets() + weth.balanceOf(address(script.scWETH())) + address(script.targetVault()), + script.targetVault().totalAssets() + weth.balanceOf(address(script.targetVault())) ); } } contract RebalanceScUsdcV2TestHarness is RebalanceScUsdcV2 { function setMinUsdcProfitToReinvest(uint256 _minUsdcProfitToReinvest) public { - minUsdcProfitToReinvest = _minUsdcProfitToReinvest; + minProfitToReinvest = _minUsdcProfitToReinvest; } function setMaxProfitSellSlippage(uint256 _maxProfitSellSlippage) public { diff --git a/test/script/RebalanceScUsdt.s.t.sol b/test/script/RebalanceScUsdt.s.t.sol new file mode 100644 index 00000000..d0e805a8 --- /dev/null +++ b/test/script/RebalanceScUsdt.s.t.sol @@ -0,0 +1,154 @@ +// SPDX-License-Identifier: AGPL-3.0-only +pragma solidity ^0.8.19; + +import "forge-std/console2.sol"; +import "forge-std/Test.sol"; +import {FixedPointMathLib} from "solmate/utils/FixedPointMathLib.sol"; +import {SafeTransferLib} from "solmate/utils/SafeTransferLib.sol"; +import {WETH} from "solmate/tokens/WETH.sol"; +import {ERC20} from "solmate/tokens/ERC20.sol"; +import {ERC4626} from "solmate/mixins/ERC4626.sol"; + +import {Constants as C} from "src/lib/Constants.sol"; +import {scUSDT} from "src/steth/scUSDT.sol"; +import {AaveV3ScUsdtAdapter} from "src/steth/scUsdt-adapters/AaveV3ScUsdtAdapter.sol"; +import {UsdtWethPriceConverter} from "src/steth/priceConverter/UsdtWethPriceConverter.sol"; +import {scCrossAssetYieldVault} from "src/steth/scCrossAssetYieldVault.sol"; + +import {RebalanceScUsdt} from "script/v2/keeper-actions/RebalanceScUsdt.s.sol"; +import {MainnetAddresses} from "script/base/MainnetAddresses.sol"; + +contract RebalanceScUsdtTest is Test { + using FixedPointMathLib for uint256; + using SafeTransferLib for ERC20; + + uint256 mainnetFork; + + scUSDT vault; + AaveV3ScUsdtAdapter aaveV3; + UsdtWethPriceConverter priceConverter; + RebalanceScUsdtTestHarness script; + + constructor() { + mainnetFork = vm.createFork(vm.envString("RPC_URL_MAINNET")); + vm.selectFork(mainnetFork); + vm.rollFork(21031368); + + aaveV3 = AaveV3ScUsdtAdapter(MainnetAddresses.SCUSDT_AAVEV3_ADAPTER); + priceConverter = UsdtWethPriceConverter(MainnetAddresses.USDT_WETH_PRICE_CONVERTER); + + vault = scUSDT(MainnetAddresses.SCUSDT); + + script = new RebalanceScUsdtTestHarness(vault); + } + + function test_run_initialRebalance() public { + uint256 newDepositAmount = vault.totalAssets(); + + // make an initial deposit + deal(C.USDT, address(this), newDepositAmount); + ERC20(C.USDT).safeApprove(address(vault), newDepositAmount); + vault.deposit(newDepositAmount, address(this)); + + uint256 expectedFloat = script.totalAssets().mulWadDown(vault.floatPercentage()); + uint256 expectedCollateral = script.totalAssets() - expectedFloat; + uint256 expectedDebt = + priceConverter.assetToTargetToken(expectedCollateral).mulWadDown(script.aaveV3TargetLtv()); + + script.run(); + + assertApproxEqRel(script.targetTokensInvested(), expectedDebt, 0.001e18, "weth invested"); + assertApproxEqRel(script.totalDebt(), expectedDebt, 0.001e18, "total debt"); + assertApproxEqRel(script.totalCollateral(), expectedCollateral, 0.001e18, "total collateral"); + assertApproxEqRel(script.assetBalance(), expectedFloat, 0.001e18, "usdt balance"); + } + + function test_run_rebalanceWithProfit() public { + script.run(); + + _assertInitialState(); + + _simulate100PctProfit(); + + uint256 expectedFloat = script.totalAssets().mulWadDown(vault.floatPercentage()); + uint256 expectedCollateral = script.totalCollateral().mulWadDown(1e18 + script.aaveV3TargetLtv()); + uint256 expectedDebt = + priceConverter.assetToTargetToken(expectedCollateral).mulWadDown(script.aaveV3TargetLtv()); + + script = new RebalanceScUsdtTestHarness(vault); + script.run(); + + assertApproxEqRel(script.targetTokensInvested(), expectedDebt, 0.01e18, "weth invested"); + assertApproxEqRel(script.totalDebt(), expectedDebt, 0.01e18, "total debt"); + assertApproxEqRel(script.totalCollateral(), expectedCollateral, 0.01e18, "total collateral"); + assertApproxEqRel(script.assetBalance(), expectedFloat, 0.4e18, "usdt balance"); + } + + function test_run_canDeleverage() public { + script.run(); + + _assertInitialState(); + + script = new RebalanceScUsdtTestHarness(vault); + script.setAaveV3TargetLtv(0.2e18); + + uint256 expectedFloat = script.totalAssets().mulWadDown(vault.floatPercentage()); + uint256 expectedCollateral = script.totalCollateral(); + uint256 expectedDebt = + priceConverter.assetToTargetToken(expectedCollateral).mulWadDown(script.aaveV3TargetLtv()); + + script.run(); + + assertApproxEqRel(script.targetTokensInvested(), expectedDebt, 0.01e18, "weth invested"); + assertApproxEqRel(script.totalDebt(), expectedDebt, 0.01e18, "total debt"); + assertApproxEqRel(script.totalCollateral(), expectedCollateral, 0.01e18, "total collateral"); + assertApproxEqRel(script.assetBalance(), expectedFloat, 0.4e18, "usdt balance"); + } + + function _assertInitialState() internal { + assertTrue(script.targetTokensInvested() > 0, "initial weth invested 0"); + assertTrue(vault.totalDebt() > 0, "initial total debt 0"); + assertTrue(vault.totalCollateral() > 0, "initial total collateral 0"); + assertTrue(script.assetBalance() > 0, "initial usdt balance 0"); + } + + function _simulate100PctProfit() internal { + WETH weth = WETH(payable(C.WETH)); + + console2.log("weth balance before", weth.balanceOf(address(script.targetVault()))); + console2.log("total assets before", script.targetVault().totalAssets()); + + // simulate 100% profit by dealing more WETH to scWETH vault + deal( + address(weth), + address(script.targetVault()), + script.targetVault().totalAssets() + weth.balanceOf(address(script.targetVault())) + ); + + console2.log("weth balance after", weth.balanceOf(address(script.targetVault()))); + console2.log("total assets after", script.targetVault().totalAssets()); + } +} + +contract RebalanceScUsdtTestHarness is RebalanceScUsdt { + constructor(scUSDT _vault) { + vault = _vault; + } + + function _initEnv() internal override { + // TODO: WRONG KEEPER ADDRESS??? + keeper = 0x3Ab6EBDBf08e1954e69F6859AdB2DA5236D2e838; + } + + function _getVaultAddress() internal view override returns (scCrossAssetYieldVault) { + return scCrossAssetYieldVault(vault); + } + + function setAaveV3TargetLtv(uint256 _newTargetLtv) public { + aaveV3TargetLtv = _newTargetLtv; + } + + function setAaveV3InvestableAmountPercent(uint256 _newInvestableAmountPercent) public { + aaveV3InvestableAmountPercent = _newInvestableAmountPercent; + } +} diff --git a/test/script/RebalanceScWethV2.t.sol b/test/script/RebalanceScWethV2.t.sol index 014da110..6a02d04b 100644 --- a/test/script/RebalanceScWethV2.t.sol +++ b/test/script/RebalanceScWethV2.t.sol @@ -14,12 +14,12 @@ import {ERC20} from "solmate/tokens/ERC20.sol"; import {Address} from "openzeppelin-contracts/utils/Address.sol"; import {WETH} from "solmate/tokens/WETH.sol"; -import {Constants as C} from "../../src/lib/Constants.sol"; -import {MainnetAddresses} from "../../script/base/MainnetAddresses.sol"; -import {RebalanceScWethV2} from "../../script/v2/keeper-actions/RebalanceScWethV2.s.sol"; -import {scWETHv2} from "../../src/steth/scWETHv2.sol"; -import {IAdapter} from "../../src/steth/IAdapter.sol"; -import {scWETHv2Keeper} from "../../src/steth/scWETHv2Keeper.sol"; +import {Constants as C} from "src/lib/Constants.sol"; +import {MainnetAddresses} from "script/base/MainnetAddresses.sol"; +import {RebalanceScWethV2} from "script/v2/keeper-actions/RebalanceScWethV2.s.sol"; +import {scWETHv2} from "src/steth/scWETHv2.sol"; +import {IAdapter} from "src/steth/IAdapter.sol"; +import {scWETHv2Keeper} from "src/steth/scWETHv2Keeper.sol"; contract RebalanceScWethV2Test is Test { using FixedPointMathLib for uint256; diff --git a/test/script/deploy/DeployScUsds.s.t.sol b/test/script/deploy/DeployScUsds.s.t.sol new file mode 100644 index 00000000..8108bb83 --- /dev/null +++ b/test/script/deploy/DeployScUsds.s.t.sol @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: AGPL-3.0-only +pragma solidity ^0.8.19; + +import "forge-std/console2.sol"; +import "forge-std/Test.sol"; +import {ICREATE3Factory} from "create3-factory/ICREATE3Factory.sol"; + +import {Constants as C} from "src/lib/Constants.sol"; +import {MainnetAddresses as MA} from "script/base/MainnetAddresses.sol"; +import {scUSDS} from "src/steth/scUSDS.sol"; +import {DeployScUsds} from "script/v2/deploy/DeployScUsds.s.sol"; + +contract DeployScUsdsTest is Test { + DeployScUsds script; + + address deployerAddress; + ICREATE3Factory create3 = ICREATE3Factory(0x9fBB3DF7C40Da2e5A0dE984fFE2CCB7C47cd0ABf); + + constructor() { + uint256 mainnetFork = vm.createFork(vm.envString("RPC_URL_MAINNET")); + vm.selectFork(mainnetFork); + vm.rollFork(21031368); + + uint256 deployerPrivateKey = uint256(keccak256("privateKey")); + + deployerAddress = vm.addr(deployerPrivateKey); + } + + function test_run_deploysScUsdsContractUsingCreate3() public { + // setup + + script = new DeployScUsdsTestHarness(deployerAddress); + address expected = script.getCreate3Contract(deployerAddress, type(scUSDS).name); + deal(deployerAddress, 10 ether); + + // deploy + scUSDS deployed = script.run(); + + // assert + assertEq(address(deployed), expected, "deployed address"); + assertTrue(address(deployed).code.length != 0, "contract code"); + assertTrue(deployed.totalAssets() > 0, "total assets = 0, no initial deposit made"); + assertEq(address(deployed.scsDai()), MA.SCSDAI, "target vault"); + } +} + +contract DeployScUsdsTestHarness is DeployScUsds { + constructor(address _deployerAddress) { + deployerAddress = _deployerAddress; + } + + function _init() internal override { + // override to prevent reading env variables + } +} diff --git a/test/script/deploy/DeployScUsdt.s.t.sol b/test/script/deploy/DeployScUsdt.s.t.sol new file mode 100644 index 00000000..19b9b7cb --- /dev/null +++ b/test/script/deploy/DeployScUsdt.s.t.sol @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: AGPL-3.0-only +pragma solidity ^0.8.19; + +import "forge-std/console2.sol"; +import "forge-std/Test.sol"; +import {FixedPointMathLib} from "solmate/utils/FixedPointMathLib.sol"; +import {ICREATE3Factory} from "create3-factory/ICREATE3Factory.sol"; + +import {Constants as C} from "src/lib/Constants.sol"; +import {MainnetAddresses as MA} from "script/base/MainnetAddresses.sol"; +import {scUSDT} from "src/steth/scUSDT.sol"; +import {UsdtWethSwapper} from "src/steth/swapper/UsdtWethSwapper.sol"; +import {UsdtWethPriceConverter} from "src/steth/priceConverter/UsdtWethPriceConverter.sol"; +import {DeployScUsdt} from "script/v2/deploy/DeployScUsdt.s.sol"; + +contract DeployScUsdtTest is Test { + using FixedPointMathLib for uint256; + + DeployScUsdt script; + + address deployerAddress; + ICREATE3Factory create3 = ICREATE3Factory(0x9fBB3DF7C40Da2e5A0dE984fFE2CCB7C47cd0ABf); + + constructor() { + uint256 mainnetFork = vm.createFork(vm.envString("RPC_URL_MAINNET")); + vm.selectFork(mainnetFork); + vm.rollFork(20068274); + + uint256 deployerPrivateKey = uint256(keccak256("privateKey")); + + deployerAddress = vm.addr(deployerPrivateKey); + } + + function test_run_deploysScUsdtContractUsingCreate3() public { + // setup + + script = new DeployScUsdtTestHarness(deployerAddress); + address expected = script.getCreate3Contract(deployerAddress, type(scUSDT).name); + address swapper = script.getCreate3Contract(deployerAddress, type(UsdtWethSwapper).name); + address priceConverter = script.getCreate3Contract(deployerAddress, type(UsdtWethPriceConverter).name); + deal(deployerAddress, 10 ether); + + // deploy + scUSDT deployed = script.run(); + + // assert + assertEq(address(deployed), expected, "deployed address"); + assertTrue(address(deployed).code.length != 0, "contract code"); + assertTrue(deployed.hasRole(deployed.DEFAULT_ADMIN_ROLE(), MA.MULTISIG), "admin role"); + assertTrue(deployed.hasRole(deployed.KEEPER_ROLE(), MA.KEEPER), "keeper role"); + assertTrue(deployed.totalAssets() > 0, "total assets = 0, no initial deposit made"); + assertTrue(deployed.isSupported(1), "adapter not added"); + assertEq(address(deployed.swapper()), swapper, "swapper"); + assertEq(address(deployed.priceConverter()), priceConverter, "price converter"); + assertEq(address(deployed.targetVault()), MA.SCWETHV2, "target vault"); + } +} + +contract DeployScUsdtTestHarness is DeployScUsdt { + constructor(address _deployerAddress) { + deployerAddress = _deployerAddress; + keeper = MA.KEEPER; + multisig = MA.MULTISIG; + } + + function _init() internal override { + // override to prevent reading env variables + } +} diff --git a/test/script/deploy/DeployStaking.s.t.sol b/test/script/deploy/DeployStaking.s.t.sol index 64072ad0..e66a6d01 100644 --- a/test/script/deploy/DeployStaking.s.t.sol +++ b/test/script/deploy/DeployStaking.s.t.sol @@ -5,11 +5,10 @@ import "forge-std/console2.sol"; import "forge-std/Test.sol"; import {FixedPointMathLib} from "solmate/utils/FixedPointMathLib.sol"; -import {Constants as C} from "../../../src/lib/Constants.sol"; -import {MainnetAddresses as MA} from "../../../script/base/MainnetAddresses.sol"; - -import {RewardTracker} from "../../../src/staking/RewardTracker.sol"; -import {DeployStaking} from "../../../script/DeployStaking.s.sol"; +import {Constants as C} from "src/lib/Constants.sol"; +import {RewardTracker} from "src/staking/RewardTracker.sol"; +import {MainnetAddresses as MA} from "script/base/MainnetAddresses.sol"; +import {DeployStaking} from "script/DeployStaking.s.sol"; contract DeployStakingTest is Test { using FixedPointMathLib for uint256;