// SPDX-License-Identifier: GPL-3.0 pragma solidity 0.8.6; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import "@openzeppelin/contracts/utils/Address.sol"; import "@pooltogether/yield-source-interface/contracts/IYieldSource.sol"; import "./PrizePool.sol"; /** * @title PoolTogether V4 YieldSourcePrizePool * @author PoolTogether Inc Team * @notice The Yield Source Prize Pool uses a yield source contract to generate prizes. * Funds that are deposited into the prize pool are then deposited into a yield source. (i.e. Aave, Compound, etc...) */ contract YieldSourcePrizePool is PrizePool { using SafeERC20 for IERC20; using Address for address; /// @notice Address of the yield source. IYieldSource public immutable yieldSource; /// @dev Emitted when yield source prize pool is deployed. /// @param yieldSource Address of the yield source. event Deployed(address indexed yieldSource); /// @notice Emitted when stray deposit token balance in this contract is swept /// @param amount The amount that was swept event Swept(uint256 amount); /// @notice Deploy the Prize Pool and Yield Service with the required contract connections /// @param _owner Address of the Yield Source Prize Pool owner /// @param _yieldSource Address of the yield source constructor(address _owner, IYieldSource _yieldSource) PrizePool(_owner) { require( address(_yieldSource) != address(0), "YieldSourcePrizePool/yield-source-not-zero-address" ); yieldSource = _yieldSource; // A hack to determine whether it's an actual yield source (bool succeeded, bytes memory data) = address(_yieldSource).staticcall( abi.encodePacked(_yieldSource.depositToken.selector) ); address resultingAddress; if (data.length > 0) { resultingAddress = abi.decode(data, (address)); } require(succeeded && resultingAddress != address(0), "YieldSourcePrizePool/invalid-yield-source"); emit Deployed(address(_yieldSource)); } /// @notice Sweeps any stray balance of deposit tokens into the yield source. /// @dev This becomes prize money function sweep() external nonReentrant onlyOwner { uint256 balance = _token().balanceOf(address(this)); _supply(balance); emit Swept(balance); } /// @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 view override returns (bool) { IYieldSource _yieldSource = yieldSource; return ( _externalToken != address(_yieldSource) && _externalToken != _yieldSource.depositToken() ); } /// @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)); } /// @notice Returns the address of the ERC20 asset token used for deposits. /// @return Address of the ERC20 asset token. function _token() internal view override returns (IERC20) { return IERC20(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().safeIncreaseAllowance(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); } }