Contract Name:
BasketsPriceFeed
Contract Source Code:
pragma solidity ^0.8.0;
import {Ownable} from "@openzeppelin/access/Ownable.sol";
import {IBasketFacet} from "./Interfaces/IBasketFacet.sol";
import {ILendingRegistry} from "./Interfaces/ILendingRegistry.sol";
import {IChainLinkOracle} from "./Interfaces/IChainLinkOracle.sol";
import {IERC20} from "@openzeppelin/token/ERC20/IERC20.sol";
import {ILendingLogic} from "./Interfaces/ILendingLogic.sol";
import {IERC20Metadata} from "@openzeppelin/token/ERC20/extensions/IERC20Metadata.sol";
contract BasketsPriceFeed is Ownable {
IBasketFacet immutable basket;
ILendingRegistry immutable lendingRegistry;
uint8 public constant decimals = 18;
mapping(address => IChainLinkOracle) public linkFeeds;
constructor (address _basket, address _lendingRegistry) {
basket = IBasketFacet(_basket);
lendingRegistry = ILendingRegistry(_lendingRegistry);
}
/**
* Function to retrieve the price of 1 basket token in USD, scaled by 1e18
*
* @return usdPrice Price of 1 basket token in USD
*/
function latestAnswer() external view returns (uint256 usdPrice) {
address[] memory components = basket.getTokens();
uint256 marketCapUSD = 0;
// Gather link prices, component balances, and basket market cap
for (uint8 i = 0; i < components.length; i++) {
address component = components[i];
address underlying = lendingRegistry.wrappedToUnderlying(component);
IERC20 componentToken = IERC20(component);
IChainLinkOracle linkFeed;
if (underlying != address(0)) { // Wrapped tokens
ILendingLogic lendingLogic = ILendingLogic(lendingRegistry.protocolToLogic(lendingRegistry.wrappedToProtocol(component)));
linkFeed = linkFeeds[underlying];
marketCapUSD += (
fmul(fmul(componentToken.balanceOf(address(basket)), lendingLogic.exchangeRateView(component), 1 ether),
10 ** (36 - IERC20Metadata(address(componentToken)).decimals() - linkFeed.decimals()) * uint256(linkFeed.latestAnswer()),1 ether)
);
} else { // Non-wrapped tokens
linkFeed = linkFeeds[component];
marketCapUSD += (
fmul(componentToken.balanceOf(address(basket)),10 ** (36 - IERC20Metadata(address(componentToken)).decimals() - linkFeed.decimals()) * uint256(linkFeed.latestAnswer()),1 ether)
);
}
}
usdPrice = fdiv(marketCapUSD, IERC20(address(basket)).totalSupply(), 1 ether);
return usdPrice;
}
function setTokenFeed(address _token, address _oracle) external onlyOwner {
linkFeeds[_token] = IChainLinkOracle(_oracle);
}
function removeTokenFeed(address _token) external onlyOwner {
delete linkFeeds[_token];
}
function fmul(
uint256 x,
uint256 y,
uint256 baseUnit
) internal pure returns (uint256 z) {
assembly {
if iszero(eq(div(mul(x,y),x),y)) {revert(0,0)}
z := div(mul(x,y),baseUnit)
}
}
function fdiv(
uint256 x,
uint256 y,
uint256 baseUnit
) internal pure returns (uint256 z) {
assembly {
if iszero(eq(div(mul(x,baseUnit),x),baseUnit)) {revert(0,0)}
z := div(mul(x,baseUnit),y)
}
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/Ownable.sol)
pragma solidity ^0.8.0;
import "../utils/Context.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* By default, the owner account will be the one that deploys the contract. This
* can later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract Ownable is Context {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/
constructor() {
_transferOwnership(_msgSender());
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
require(owner() == _msgSender(), "Ownable: caller is not the owner");
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions anymore. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby removing any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IBasketFacet {
event TokenAdded(address indexed _token);
event TokenRemoved(address indexed _token);
event EntryFeeSet(uint256 fee);
event ExitFeeSet(uint256 fee);
event AnnualizedFeeSet(uint256 fee);
event FeeBeneficiarySet(address indexed beneficiary);
event EntryFeeBeneficiaryShareSet(uint256 share);
event ExitFeeBeneficiaryShareSet(uint256 share);
event PoolJoined(address indexed who, uint256 amount);
event PoolExited(address indexed who, uint256 amount);
event FeeCharged(uint256 amount);
event LockSet(uint256 lockBlock);
event CapSet(uint256 cap);
/**
@notice Sets entry fee paid when minting
@param _fee Amount of fee. 1e18 == 100%, capped at 10%
*/
function setEntryFee(uint256 _fee) external;
/**
@notice Get the entry fee
@return Current entry fee
*/
function getEntryFee() external view returns(uint256);
/**
@notice Set the exit fee paid when exiting
@param _fee Amount of fee. 1e18 == 100%, capped at 10%
*/
function setExitFee(uint256 _fee) external;
/**
@notice Get the exit fee
@return Current exit fee
*/
function getExitFee() external view returns(uint256);
/**
@notice Set the annualized fee. Often referred to as streaming fee
@param _fee Amount of fee. 1e18 == 100%, capped at 10%
*/
function setAnnualizedFee(uint256 _fee) external;
/**
@notice Get the annualized fee.
@return Current annualized fee.
*/
function getAnnualizedFee() external view returns(uint256);
/**
@notice Set the address receiving the fees.
*/
function setFeeBeneficiary(address _beneficiary) external;
/**
@notice Get the fee benificiary
@return The current fee beneficiary
*/
function getFeeBeneficiary() external view returns(address);
/**
@notice Set the fee beneficiaries share of the entry fee
@notice _share Share of the fee. 1e18 == 100%. Capped at 100%
*/
function setEntryFeeBeneficiaryShare(uint256 _share) external;
/**
@notice Get the entry fee beneficiary share
@return Feeshare amount
*/
function getEntryFeeBeneficiaryShare() external view returns(uint256);
/**
@notice Set the fee beneficiaries share of the exit fee
@notice _share Share of the fee. 1e18 == 100%. Capped at 100%
*/
function setExitFeeBeneficiaryShare(uint256 _share) external;
/**
@notice Get the exit fee beneficiary share
@return Feeshare amount
*/
function getExitFeeBeneficiaryShare() external view returns(uint256);
/**
@notice Calculate the oustanding annualized fee
@return Amount of pool tokens to be minted to charge the annualized fee
*/
function calcOutStandingAnnualizedFee() external view returns(uint256);
/**
@notice Charges the annualized fee
*/
function chargeOutstandingAnnualizedFee() external;
/**
@notice Pulls underlying from caller and mints the pool token
@param _amount Amount of pool tokens to mint
*/
function joinPool(uint256 _amount) external;
/**
@notice Burns pool tokens from the caller and returns underlying assets
*/
function exitPool(uint256 _amount) external;
/**
@notice Get if the pool is locked or not. (not accepting exit and entry)
@return Boolean indicating if the pool is locked
*/
function getLock() external view returns (bool);
/**
@notice Get the block until which the pool is locked
@return The lock block
*/
function getLockBlock() external view returns (uint256);
/**
@notice Set the lock block
@param _lock Block height of the lock
*/
function setLock(uint256 _lock) external;
/**
@notice Get the maximum of pool tokens that can be minted
@return Cap
*/
function getCap() external view returns (uint256);
/**
@notice Set the maximum of pool tokens that can be minted
@param _maxCap Max cap
*/
function setCap(uint256 _maxCap) external;
/**
@notice Get the amount of tokens owned by the pool
@param _token Addres of the token
@return Amount owned by the contract
*/
function balance(address _token) external view returns (uint256);
/**
@notice Get the tokens in the pool
@return Array of tokens in the pool
*/
function getTokens() external view returns (address[] memory);
/**
@notice Add a token to the pool. Should have at least a balance of 10**6
@param _token Address of the token to add
*/
function addToken(address _token) external;
/**
@notice Removes a token from the pool
@param _token Address of the token to remove
*/
function removeToken(address _token) external;
/**
@notice Checks if a token was added to the pool
@param _token address of the token
@return If token is in the pool or not
*/
function getTokenInPool(address _token) external view returns (bool);
/**
@notice Calculate the amounts of underlying needed to mint that pool amount.
@param _amount Amount of pool tokens to mint
@return tokens Tokens needed
@return amounts Amounts of underlying needed
*/
function calcTokensForAmount(uint256 _amount)
external
view
returns (address[] memory tokens, uint256[] memory amounts);
/**
@notice Calculate the amounts of underlying to receive when burning that pool amount
@param _amount Amount of pool tokens to burn
@return tokens Tokens returned
@return amounts Amounts of underlying returned
*/
function calcTokensForAmountExit(uint256 _amount)
external
view
returns (address[] memory tokens, uint256[] memory amounts);
}
//SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.0;
interface ILendingRegistry {
// Maps wrapped token to protocol
function wrappedToProtocol(address _wrapped) external view returns(bytes32);
// Maps wrapped token to underlying
function wrappedToUnderlying(address _wrapped) external view returns(address);
function underlyingToProtocolWrapped(address _underlying, bytes32 protocol) external view returns (address);
function protocolToLogic(bytes32 _protocol) external view returns (address);
/**
@notice Set which protocl a wrapped token belongs to
@param _wrapped Address of the wrapped token
@param _protocol Bytes32 key of the protocol
*/
function setWrappedToProtocol(address _wrapped, bytes32 _protocol) external;
/**
@notice Set what is the underlying for a wrapped token
@param _wrapped Address of the wrapped token
@param _underlying Address of the underlying token
*/
function setWrappedToUnderlying(address _wrapped, address _underlying) external;
/**
@notice Set the logic contract for the protocol
@param _protocol Bytes32 key of the procol
@param _logic Address of the lending logic contract for that protocol
*/
function setProtocolToLogic(bytes32 _protocol, address _logic) external;
/**
@notice Set the wrapped token for the underlying deposited in this protocol
@param _underlying Address of the unerlying token
@param _protocol Bytes32 key of the protocol
@param _wrapped Address of the wrapped token
*/
function setUnderlyingToProtocolWrapped(address _underlying, bytes32 _protocol, address _wrapped) external;
/**
@notice Get tx data to lend the underlying amount in a specific protocol
@param _underlying Address of the underlying token
@param _amount Amount to lend
@param _protocol Bytes32 key of the protocol
@return targets Addresses of the src to call
@return data Calldata for the calls
*/
function getLendTXData(address _underlying, uint256 _amount, bytes32 _protocol) external view returns(address[] memory targets, bytes[] memory data);
/**
@notice Get the tx data to unlend the wrapped amount
@param _wrapped Address of the wrapped token
@param _amount Amount of wrapped token to unlend
@return targets Addresses of the src to call
@return data Calldata for the calls
*/
function getUnlendTXData(address _wrapped, uint256 _amount) external view returns(address[] memory targets, bytes[] memory data);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IChainLinkOracle {
function decimals() external view returns (uint8);
function description() external view returns (string memory);
function latestAnswer() external view returns (int256);
function latestTimestamp() external view returns (uint256);
function latestRound() external view returns (uint256);
function getAnswer(uint256 roundId) external view returns (int256);
function getTimestamp(uint256 roundId) external view returns (uint256);
event AnswerUpdated(int256 indexed current, uint256 indexed roundId, uint256 updatedAt);
event NewRound(uint256 indexed roundId, address indexed startedBy, uint256 startedAt);
function version() external view returns (uint256);
// getRoundData and latestRoundData should both raise "No data present"
// if they do not have data to report, instead of returning unset values
// which could be misinterpreted as actual reported values.
function getRoundData(uint80 _roundId)
external
view
returns (
uint80 roundId,
int256 answer,
uint256 startedAt,
uint256 updatedAt,
uint80 answeredInRound
);
function latestRoundData()
external
view
returns (
uint80 roundId,
int256 answer,
uint256 startedAt,
uint256 updatedAt,
uint80 answeredInRound
);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `from` to `to` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(
address from,
address to,
uint256 amount
) external returns (bool);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface ILendingLogic {
/**
@notice Get the APR based on underlying token.
@param _token Address of the underlying token
@return Interest with 18 decimals
*/
function getAPRFromUnderlying(address _token) external view returns(uint256);
/**
@notice Get the APR based on wrapped token.
@param _token Address of the wrapped token
@return Interest with 18 decimals
*/
function getAPRFromWrapped(address _token) external view returns(uint256);
/**
@notice Get the calls needed to lend.
@param _underlying Address of the underlying token
@param _amount Amount of the underlying token
@return targets Addresses of the src to call
@return data Calldata of the calls
*/
function lend(address _underlying, uint256 _amount, address _tokenHolder) external view returns(address[] memory targets, bytes[] memory data);
/**
@notice Get the calls needed to unlend
@param _wrapped Address of the wrapped token
@param _amount Amount of the underlying tokens
@return targets Addresses of the src to call
@return data Calldata of the calls
*/
function unlend(address _wrapped, uint256 _amount, address _tokenHolder) external view returns(address[] memory targets, bytes[] memory data);
/**
@notice Get the underlying wrapped exchange rate
@param _wrapped Address of the wrapped token
@return The exchange rate
*/
function exchangeRate(address _wrapped) external returns(uint256);
/**
@notice Get the underlying wrapped exchange rate in a view (non state changing) way
@param _wrapped Address of the wrapped token
@return The exchange rate
*/
function exchangeRateView(address _wrapped) external view returns(uint256);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)
pragma solidity ^0.8.0;
import "../IERC20.sol";
/**
* @dev Interface for the optional metadata functions from the ERC20 standard.
*
* _Available since v4.1._
*/
interface IERC20Metadata is IERC20 {
/**
* @dev Returns the name of the token.
*/
function name() external view returns (string memory);
/**
* @dev Returns the symbol of the token.
*/
function symbol() external view returns (string memory);
/**
* @dev Returns the decimals places of the token.
*/
function decimals() external view returns (uint8);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
pragma solidity ^0.8.0;
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
}