// SPDX-License-Identifier: Apache-2.0 // https://docs.soliditylang.org/en/v0.8.10/style-guide.html pragma solidity ^0.8.10; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import {IERC20Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol"; import {ERC4626Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC4626Upgradeable.sol"; import {IERC20MetadataUpgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/IERC20MetadataUpgradeable.sol"; import {SafeERC20Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol"; import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol"; import {MathUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/math/MathUpgradeable.sol"; import "src/interfaces/IMoolaLendingPool.sol"; import {IERC20Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol"; import "src/interfaces/IStableToken.sol"; import "src/staking/StakedERC4626Upgradeable.sol"; import "src/interfaces/IRegistry.sol"; /// @title SpiralsCUSDVault /// @author @douglasqian @no40 /// @notice This is a modification of the EIP-4626 tokenized vault standard /// for yield-bearing tokens where the yield accrued on cUSD via a lending protocol /// is held by the contract, giving the user a derivative token to redeem them at a /// later date. /// /// DEPRECATED: see "SpiralsCUSDImpactVault" contract SpiralsCUSDVault is StakedERC4626Upgradeable { event Receive(address indexed sender, uint256 indexed amount); event DependenciesUpdated( address indexed moolaLendingPool, address celoRegistry ); IRegistry internal c_celoRegistry; IMoolaLendingPool internal c_moolaLendingPool; function initialize( address _moolaLendingPoolAddress, address _celoRegistryAddress ) external initializer { __Ownable_init(); __Pausable_init(); __ReentrancyGuard_init(); // Ensures that `_owner` is set. setDependencies(_moolaLendingPoolAddress, _celoRegistryAddress); // Ensures that `_moolaLendingPoolAddress` has been sanitized. address cUSDAddress = address(getStableToken()); __ERC20_init("Spirals cUSD Vault Token", "spcUSD"); __ERC4626_init(IERC20MetadataUpgradeable(cUSDAddress)); __StakedERC4626_init( IERC20MetadataUpgradeable( c_moolaLendingPool.getReserveData(cUSDAddress).aTokenAddress ) ); } receive() external payable { emit Receive(msg.sender, msg.value); } /** * @notice Sets dependencies on contract (Moola contract addresses). */ // lendingPoolAddrProvider 0xD1088091A174d33412a968Fa34Cb67131188B332 // cUSD 0x765DE816845861e75A25fCA122bb6898B8B1282a function setDependencies( address _moolaLendingPoolAddress, address _celoRegistryAddress ) public onlyOwner { require( IRegistry(_celoRegistryAddress).getAddressForStringOrDie( "Validators" ) != address(0), "INVALID_REGISTRY_ADDRESS" ); c_moolaLendingPool = IMoolaLendingPool(_moolaLendingPoolAddress); c_celoRegistry = IRegistry(_celoRegistryAddress); // Keep this here becaue "getStableToken" has a dependency on // "c_celoRegistry" being set. require( IMoolaLendingPool(_moolaLendingPoolAddress) .getReserveData(address(getStableToken())) .aTokenAddress != address(0), "INVALID_LENDING_POOL" ); emit DependenciesUpdated( _moolaLendingPoolAddress, _celoRegistryAddress ); } /** * @dev Deposit cUSD into Moola cUSD lending pool. */ function stake(uint256 assets) internal virtual override { // Approve cUSD from contract -> Moola LP SafeERC20Upgradeable.safeApprove( IERC20Upgradeable(asset()), address(c_moolaLendingPool), assets ); c_moolaLendingPool.deposit(asset(), assets, address(this), 0); } /** * @dev Withdraw cUSD from Moola lending pool. */ function _withdrawFromStakedAsset(uint256 stakedAssets) internal virtual override returns (uint256 assets) { // Withdraw from Moola lending pool directly into "receiver" in // "_sendAssetToReceiver" return convertStakedToAssets(stakedAssets); } /** * @dev Send cUSD to receiver. */ function _sendAssetToReceiver( address, address receiver, uint256 assets ) internal virtual override { c_moolaLendingPool.withdraw( asset(), convertAssetsToStaked(assets), receiver ); } /** * @dev cUSD -> spcUSD */ function _convertToShares(uint256 assets, MathUpgradeable.Rounding) internal pure override returns (uint256 shares) { return assets; // # assets = # shares for rebasing token } /** * @dev spcUSD -> cUSD */ function _convertToAssets(uint256 shares, MathUpgradeable.Rounding) internal pure override returns (uint256 assets) { return shares; // # assets = # shares for rebasing token } /** * @dev mcUSD -> cUSD */ function convertStakedToAssets(uint256 stakedAssets) public view virtual override returns (uint256) { return stakedAssets; } /** * @dev cUSD -> mcUSD */ function convertAssetsToStaked(uint256 assets) public view virtual override returns (uint256) { return assets; } /// @dev Returns a StableToken.sol interface for interacting with the smart contract. function getStableToken() internal view returns (IStableToken) { address stableTokenAddr = c_celoRegistry.getAddressForStringOrDie( "StableToken" ); return IStableToken(stableTokenAddr); } }