// SPDX-License-Identifier: AGPL-3.0-only pragma solidity 0.8.11; // External references import { ERC20 } from "@rari-capital/solmate/src/tokens/ERC20.sol"; import { SafeTransferLib } from "@rari-capital/solmate/src/utils/SafeTransferLib.sol"; // Internal references import { Divider } from "../Divider.sol"; import { Errors } from "@sense-finance/v1-utils/src/libs/Errors.sol"; interface IPeriphery { function onFlashLoan( bytes calldata data, address initiator, address adapter, uint256 maturity, uint256 cBalIn, uint256 amount ) external returns (bytes32, uint256); } /// @title Assign value to Target tokens abstract contract BaseAdapter { using SafeTransferLib for ERC20; /* ========== CONSTANTS ========== */ bytes32 public constant CALLBACK_SUCCESS = keccak256("ERC3156FlashBorrower.onFlashLoan"); /* ========== PUBLIC IMMUTABLES ========== */ /// @notice Sense core Divider address address public immutable divider; /// @notice Target token to divide address public immutable target; /// @notice Underlying for the Target address public immutable underlying; /// @notice Oracle address address public immutable oracle; /// @notice Token to stake at issuance address public immutable stake; /// @notice Amount to stake at issuance uint256 public immutable stakeSize; /// @notice Min maturity (seconds after block.timstamp) uint256 public immutable minm; /// @notice Max maturity (seconds after block.timstamp) uint256 public immutable maxm; /// @notice 0 for monthly, 1 for weekly uint256 public immutable mode; /// @notice Issuance fee uint256 public immutable ifee; /// @notice WAD number representing the percentage of the total /// principal that's set aside for Yield Tokens (e.g. 0.1e18 means that 10% of the principal is reserved). /// @notice If `0`, it means no principal is set aside for Yield Tokens uint256 public immutable tilt; /// @notice The number this function returns will be used to determine its access by checking for binary /// digits using the following scheme:  /// (e.g. 0101 enables `collect` and `issue`, but not `combine`) uint256 public immutable level; /* ========== METADATA STORAGE ========== */ string public name; string public symbol; constructor( address _divider, address _target, address _underlying, address _oracle, uint256 _ifee, address _stake, uint256 _stakeSize, uint256 _minm, uint256 _maxm, uint256 _mode, uint256 _tilt, uint256 _level ) { // Sanity check if (_minm >= _maxm) revert Errors.InvalidMaturityOffsets(); divider = _divider; target = _target; underlying = _underlying; oracle = _oracle; ifee = _ifee; stake = _stake; stakeSize = _stakeSize; minm = _minm; maxm = _maxm; mode = _mode; tilt = _tilt; name = string(abi.encodePacked(ERC20(_target).name(), " Adapter")); symbol = string(abi.encodePacked(ERC20(_target).symbol(), "-adapter")); level = _level; ERC20(_target).approve(_divider, type(uint256).max); ERC20(_stake).approve(_divider, type(uint256).max); } /// @notice Loan `amount` target to `receiver`, and takes it back after the callback. /// @param receiver The contract receiving target, needs to implement the /// `onFlashLoan(address user, address adapter, uint256 maturity, uint256 amount)` interface. /// @param adapter adapter address /// @param maturity maturity /// @param cBalIn YT amount the user has sent in /// @param amount The amount of target lent. function flashLoan( bytes calldata data, address receiver, address adapter, uint256 maturity, uint256 cBalIn, uint256 amount ) external returns (bool, uint256) { if (Divider(divider).periphery() != msg.sender) revert Errors.OnlyPeriphery(); ERC20(target).safeTransfer(address(receiver), amount); (bytes32 keccak, uint256 value) = IPeriphery(receiver).onFlashLoan( data, msg.sender, adapter, maturity, cBalIn, amount ); if (keccak != CALLBACK_SUCCESS) revert Errors.FlashCallbackFailed(); ERC20(target).safeTransferFrom(address(receiver), address(this), amount); return (true, value); } /* ========== REQUIRED VALUE GETTERS ========== */ /// @notice Calculate and return this adapter's Scale value for the current timestamp. To be overriden by child contracts /// @dev For some Targets, such as cTokens, this is simply the exchange rate, or `supply cToken / supply underlying` /// @dev For other Targets, such as AMM LP shares, specialized logic will be required /// @dev This function _must_ return a WAD number representing the current exchange rate /// between the Target and the Underlying. /// @return value WAD Scale value function scale() external virtual returns (uint256); /// @notice Cached scale value getter /// @dev For situations where you need scale from a view function function scaleStored() external view virtual returns (uint256); /// @notice Returns the current price of the underlying in ETH terms function getUnderlyingPrice() external view virtual returns (uint256); /* ========== REQUIRED UTILITIES ========== */ /// @notice Deposits underlying `amount`in return for target. Must be overriden by child contracts /// @param amount Underlying amount /// @return amount of target returned function wrapUnderlying(uint256 amount) external virtual returns (uint256); /// @notice Deposits target `amount`in return for underlying. Must be overriden by child contracts /// @param amount Target amount /// @return amount of underlying returned function unwrapTarget(uint256 amount) external virtual returns (uint256); /* ========== OPTIONAL HOOKS ========== */ /// @notice Notification whenever the Divider adds or removes Target function notify( address, /* usr */ uint256, /* amt */ bool /* join */ ) public virtual { return; } /// @notice Hook called whenever a user redeems PT function onRedeem( uint256, /* uBal */ uint256, /* mscale */ uint256, /* maxscale */ uint256 /* tBal */ ) public virtual { return; } /* ========== PUBLIC STORAGE ACCESSORS ========== */ function getMaturityBounds() external view returns (uint256, uint256) { return (minm, maxm); } function getStakeAndTarget() external view returns ( address, address, uint256 ) { return (target, stake, stakeSize); } }