// SPDX-License-Identifier: GPL-3.0 pragma solidity 0.6.12; import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol"; import "@openzeppelin/contracts-upgradeable/token/ERC20/SafeERC20Upgradeable.sol"; import "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol"; import "@pooltogether/yield-source-interface/contracts/IYieldSource.sol"; import "../PrizePool.sol"; contract YieldSourcePrizePool is PrizePool { using SafeERC20Upgradeable for IERC20Upgradeable; using AddressUpgradeable for address; IYieldSource public yieldSource; event YieldSourcePrizePoolInitialized(address indexed yieldSource); /// @notice Initializes the Prize Pool and Yield Service with the required contract connections /// @param _controlledTokens Array of addresses for the Ticket and Sponsorship Tokens controlled by the Prize Pool /// @param _maxExitFeeMantissa The maximum exit fee size, relative to the withdrawal amount /// @param _yieldSource Address of the yield source function initializeYieldSourcePrizePool ( RegistryInterface _reserveRegistry, ControlledTokenInterface[] memory _controlledTokens, uint256 _maxExitFeeMantissa, IYieldSource _yieldSource ) public initializer { require(address(_yieldSource).isContract(), "YieldSourcePrizePool/yield-source-not-contract-address"); PrizePool.initialize( _reserveRegistry, _controlledTokens, _maxExitFeeMantissa ); yieldSource = _yieldSource; // A hack to determine whether it's an actual yield source (bool succeeded,) = address(_yieldSource).staticcall(abi.encode(_yieldSource.depositToken.selector)); require(succeeded, "YieldSourcePrizePool/invalid-yield-source"); emit YieldSourcePrizePoolInitialized(address(_yieldSource)); } /// @notice Determines whether the passed token can be transferred out as an external award. /// @dev Different yield sources will hold the deposits as another kind of token: such a Compound's cToken. The /// prize strategy should not be allowed to move those tokens. /// @param _externalToken The address of the token to check /// @return True if the token may be awarded, false otherwise function _canAwardExternal(address _externalToken) internal override view returns (bool) { return _externalToken != address(yieldSource); } /// @notice Returns the total balance (in asset tokens). This includes the deposits and interest. /// @return The underlying balance of asset tokens function _balance() internal override returns (uint256) { return yieldSource.balanceOfToken(address(this)); } function _token() internal override view returns (IERC20Upgradeable) { return IERC20Upgradeable(yieldSource.depositToken()); } /// @notice Supplies asset tokens to the yield source. /// @param mintAmount The amount of asset tokens to be supplied function _supply(uint256 mintAmount) internal override { _token().safeApprove(address(yieldSource), mintAmount); yieldSource.supplyTokenTo(mintAmount, address(this)); } /// @notice Redeems asset tokens from the yield source. /// @param redeemAmount The amount of yield-bearing tokens to be redeemed /// @return The actual amount of tokens that were redeemed. function _redeem(uint256 redeemAmount) internal override returns (uint256) { return yieldSource.redeemToken(redeemAmount); } }