diff --git a/.solhint.json b/.solhint.json index a332cb0..8618c01 100644 --- a/.solhint.json +++ b/.solhint.json @@ -7,4 +7,4 @@ "no-empty-blocks": "off", "not-rely-on-time": "off" } -} \ No newline at end of file +} diff --git a/contracts/BaseStrategy.sol b/contracts/BaseStrategy.sol index 801b28e..a1ca399 100644 --- a/contracts/BaseStrategy.sol +++ b/contracts/BaseStrategy.sol @@ -17,12 +17,9 @@ abstract contract BaseStrategy { asset = IVault(vault).asset(); } - function maxDeposit(address receiver) - public - view - virtual - returns (uint256 maxAssets) - { + function maxDeposit( + address receiver + ) public view virtual returns (uint256 maxAssets) { maxAssets = type(uint256).max; } @@ -47,10 +44,10 @@ abstract contract BaseStrategy { return 0; } - function deposit(uint256 assets, address receiver) - public - returns (uint256) - { + function deposit( + uint256 assets, + address receiver + ) public returns (uint256) { require(msg.sender == vault && msg.sender == receiver, "not owner"); // transfer and invest @@ -63,12 +60,9 @@ abstract contract BaseStrategy { return _maxWithdraw(owner); } - function _maxWithdraw(address owner) - internal - view - virtual - returns (uint256 withdraw_amount) - {} + function _maxWithdraw( + address owner + ) internal view virtual returns (uint256 withdraw_amount) {} function withdraw( uint256 amount, diff --git a/contracts/Strategy.sol b/contracts/Strategy.sol index 5f67475..8a9d7cd 100644 --- a/contracts/Strategy.sol +++ b/contracts/Strategy.sol @@ -6,10 +6,12 @@ import "@openzeppelin/contracts/utils/math/Math.sol"; import {IERC20, BaseStrategy} from "BaseStrategy.sol"; import "interfaces/IVault.sol"; import {Comet, CometStructs, CometRewards} from "interfaces/CompoundV3.sol"; + contract Strategy is BaseStrategy { // TODO: add rewards Comet public immutable cToken; uint256 private SECONDS_PER_YEAR = 365 days; + constructor( address _vault, string memory _name, @@ -23,7 +25,8 @@ contract Strategy is BaseStrategy { address owner ) internal view override returns (uint256) { // TODO: may not be accurate due to unaccrued balance in cToken - return Math.min(IERC20(asset).balanceOf(address(cToken)), _totalAssets()); + return + Math.min(IERC20(asset).balanceOf(address(cToken)), _totalAssets()); } function _freeFunds( @@ -98,8 +101,10 @@ contract Strategy is BaseStrategy { uint256 borrows = cToken.totalBorrow(); uint256 supply = cToken.totalSupply(); - uint256 newUtilization = borrows * 1e18 / uint256(int256(supply) + delta); - uint256 newSupplyRate = cToken.getSupplyRate(newUtilization) * SECONDS_PER_YEAR; + uint256 newUtilization = (borrows * 1e18) / + uint256(int256(supply) + delta); + uint256 newSupplyRate = cToken.getSupplyRate(newUtilization) * + SECONDS_PER_YEAR; return newSupplyRate; } } diff --git a/contracts/interfaces/CompoundV3.sol b/contracts/interfaces/CompoundV3.sol index 7525e34..53c90c7 100644 --- a/contracts/interfaces/CompoundV3.sol +++ b/contracts/interfaces/CompoundV3.sol @@ -5,52 +5,52 @@ pragma experimental ABIEncoderV2; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; library CometStructs { - struct AssetInfo { - uint8 offset; - address asset; - address priceFeed; - uint64 scale; - uint64 borrowCollateralFactor; - uint64 liquidateCollateralFactor; - uint64 liquidationFactor; - uint128 supplyCap; - } - - struct UserBasic { - int104 principal; - uint64 baseTrackingIndex; - uint64 baseTrackingAccrued; - uint16 assetsIn; - uint8 _reserved; - } - - struct TotalsBasic { - uint64 baseSupplyIndex; - uint64 baseBorrowIndex; - uint64 trackingSupplyIndex; - uint64 trackingBorrowIndex; - uint104 totalSupplyBase; - uint104 totalBorrowBase; - uint40 lastAccrualTime; - uint8 pauseFlags; - } - - struct UserCollateral { - uint128 balance; - uint128 _reserved; - } - - struct RewardOwed { - address token; - uint owed; - } - - struct TotalsCollateral { - uint128 totalSupplyAsset; - uint128 _reserved; - } - - struct RewardConfig { + struct AssetInfo { + uint8 offset; + address asset; + address priceFeed; + uint64 scale; + uint64 borrowCollateralFactor; + uint64 liquidateCollateralFactor; + uint64 liquidationFactor; + uint128 supplyCap; + } + + struct UserBasic { + int104 principal; + uint64 baseTrackingIndex; + uint64 baseTrackingAccrued; + uint16 assetsIn; + uint8 _reserved; + } + + struct TotalsBasic { + uint64 baseSupplyIndex; + uint64 baseBorrowIndex; + uint64 trackingSupplyIndex; + uint64 trackingBorrowIndex; + uint104 totalSupplyBase; + uint104 totalBorrowBase; + uint40 lastAccrualTime; + uint8 pauseFlags; + } + + struct UserCollateral { + uint128 balance; + uint128 _reserved; + } + + struct RewardOwed { + address token; + uint owed; + } + + struct TotalsCollateral { + uint128 totalSupplyAsset; + uint128 _reserved; + } + + struct RewardConfig { address token; uint64 rescaleFactor; bool shouldUpscale; @@ -58,55 +58,93 @@ library CometStructs { } interface Comet is IERC20 { - function baseScale() external view returns (uint); - function supply(address asset, uint amount) external; - function supplyTo(address to, address asset, uint amount) external; - function withdraw(address asset, uint amount) external; + function baseScale() external view returns (uint); + + function supply(address asset, uint amount) external; - function getSupplyRate(uint utilization) external view returns (uint); - function getBorrowRate(uint utilization) external view returns (uint); + function supplyTo(address to, address asset, uint amount) external; - function getAssetInfoByAddress(address asset) external view returns (CometStructs.AssetInfo memory); - function getAssetInfo(uint8 i) external view returns (CometStructs.AssetInfo memory); + function withdraw(address asset, uint amount) external; - function borrowBalanceOf(address account) external view returns (uint256); + function getSupplyRate(uint utilization) external view returns (uint); - function getPrice(address priceFeed) external view returns (uint128); + function getBorrowRate(uint utilization) external view returns (uint); - function userBasic(address) external view returns (CometStructs.UserBasic memory); - function totalsBasic() external view returns (CometStructs.TotalsBasic memory); - function userCollateral(address, address) external view returns (CometStructs.UserCollateral memory); + function getAssetInfoByAddress( + address asset + ) external view returns (CometStructs.AssetInfo memory); - function baseTokenPriceFeed() external view returns (address); + function getAssetInfo( + uint8 i + ) external view returns (CometStructs.AssetInfo memory); - function numAssets() external view returns (uint8); + function borrowBalanceOf(address account) external view returns (uint256); - function getUtilization() external view returns (uint); + function getPrice(address priceFeed) external view returns (uint128); - function baseTrackingSupplySpeed() external view returns (uint); - function baseTrackingBorrowSpeed() external view returns (uint); + function userBasic( + address + ) external view returns (CometStructs.UserBasic memory); - function totalSupply() override external view returns (uint256); - function totalBorrow() external view returns (uint256); + function totalsBasic() + external + view + returns (CometStructs.TotalsBasic memory); - function baseIndexScale() external pure returns (uint64); - function baseTrackingAccrued(address account) external view returns (uint64); + function userCollateral( + address, + address + ) external view returns (CometStructs.UserCollateral memory); - function totalsCollateral(address asset) external view returns (CometStructs.TotalsCollateral memory); + function baseTokenPriceFeed() external view returns (address); - function baseMinForRewards() external view returns (uint256); - function baseToken() external view returns (address); + function numAssets() external view returns (uint8); - function accrueAccount(address account) external; - - function isLiquidatable(address _address) external view returns(bool); - function baseBorrowMin() external view returns(uint256); + function getUtilization() external view returns (uint); + + function baseTrackingSupplySpeed() external view returns (uint); + + function baseTrackingBorrowSpeed() external view returns (uint); + + function totalSupply() external view override returns (uint256); + + function totalBorrow() external view returns (uint256); + + function baseIndexScale() external pure returns (uint64); + + function baseTrackingAccrued( + address account + ) external view returns (uint64); + + function totalsCollateral( + address asset + ) external view returns (CometStructs.TotalsCollateral memory); + + function baseMinForRewards() external view returns (uint256); + + function baseToken() external view returns (address); + + function accrueAccount(address account) external; + + function isLiquidatable(address _address) external view returns (bool); + + function baseBorrowMin() external view returns (uint256); } interface CometRewards { - function getRewardOwed(address comet, address account) external returns (CometStructs.RewardOwed memory); - function claim(address comet, address src, bool shouldAccrue) external; + function getRewardOwed( + address comet, + address account + ) external returns (CometStructs.RewardOwed memory); + + function claim(address comet, address src, bool shouldAccrue) external; + + function rewardsClaimed( + address comet, + address account + ) external view returns (uint256); - function rewardsClaimed(address comet, address account) external view returns(uint256); - function rewardConfig(address comet) external view returns (CometStructs.RewardConfig memory); + function rewardConfig( + address comet + ) external view returns (CometStructs.RewardConfig memory); } diff --git a/contracts/interfaces/IVault.sol b/contracts/interfaces/IVault.sol index 55096ee..931fbf7 100644 --- a/contracts/interfaces/IVault.sol +++ b/contracts/interfaces/IVault.sol @@ -2,23 +2,27 @@ pragma solidity 0.8.14; interface IVault { - struct StrategyParams { uint256 activation; uint256 last_report; uint256 current_debt; uint256 max_debt; - } + } function asset() external view returns (address _asset); function decimals() external view returns (uint256); - + // HashMap that records all the strategies that are allowed to receive assets from the vault - function strategies(address _strategy) external view returns (StrategyParams memory _params); + function strategies( + address _strategy + ) external view returns (StrategyParams memory _params); // Current assets held in the vault contract. Replacing balanceOf(this) to avoid price_per_share manipulation function total_idle() external view returns (uint256); - function update_debt(address strategy, uint256 target_debt) external returns (uint256); + function update_debt( + address strategy, + uint256 target_debt + ) external returns (uint256); } diff --git a/tests/conftest.py b/tests/conftest.py index c9183a8..5a0b89e 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -50,7 +50,12 @@ def create_vault( max_profit_locking_time=WEEK, ): vault = gov.deploy( - project.dependencies['yearn-vaults']['master'].VaultV3, asset, "VaultV3", "AV", governance, max_profit_locking_time + project.dependencies["yearn-vaults"]["master"].VaultV3, + asset, + "VaultV3", + "AV", + governance, + max_profit_locking_time, ) # set vault deposit vault.set_deposit_limit(deposit_limit, sender=gov) @@ -76,7 +81,9 @@ def vault(gov, asset, create_vault): @pytest.fixture def create_strategy(project, strategist): def create_strategy(vault): - strategy = strategist.deploy(project.Strategy, vault.address, "strategy_name", CASSET_ADDRESS) + strategy = strategist.deploy( + project.Strategy, vault.address, "strategy_name", CASSET_ADDRESS + ) return strategy yield create_strategy @@ -123,4 +130,5 @@ def provide_strategy_with_debt(account, strategy, vault, target_debt: int): def user_interaction(strategy, vault, deposit_into_vault): def user_interaction(): return + yield user_interaction diff --git a/tests/test_strategy.py b/tests/test_strategy.py index cc6480d..eff2200 100644 --- a/tests/test_strategy.py +++ b/tests/test_strategy.py @@ -250,15 +250,22 @@ def test_withdraw_low_liquidity( assert pytest.approx(10 ** vault.decimals(), REL_ERROR) == asset.balanceOf(vault) assert pytest.approx( new_debt - 10 ** vault.decimals(), rel=1e-5 - ) == ctoken.balanceOf(strategy) + ) == ctoken.balanceOf(strategy) -def test_apr(asset, ctoken, user, create_vault_and_strategy, gov, amount, provide_strategy_with_debt): +def test_apr( + asset, + ctoken, + user, + create_vault_and_strategy, + gov, + amount, + provide_strategy_with_debt, +): vault, strategy = create_vault_and_strategy(gov, amount) new_debt = amount provide_strategy_with_debt(gov, strategy, vault, new_debt) - current_utilization = ctoken.getUtilization() current_real_apr = ctoken.getSupplyRate(current_utilization) * YEAR current_expected_apr = strategy.aprAfterDebtChange(0)