EnableOnlyAssetsWhitelist

Description:

Multi-signature wallet contract requiring multiple confirmations for transaction execution.

Blockchain: Ethereum

Source Code: View Code On The Blockchain

Solidity Source Code:

// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0 ^0.8.19 ^0.8.3;

// lib/open-zeppelin/utils/Context.sol

// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)

/**
 * @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;
    }
}

// src/core/interfaces/IAggregatorV3Interface.sol

interface IAggregatorV3Interface {
  function decimals() external view returns (uint8);

  function description() external view returns (string memory);

  function version() external view returns (uint256);

  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);
}

// lib/open-zeppelin/token/ERC20/IERC20.sol

// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)

/**
 * @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);
}

// src/core/interfaces/IEnableOnlyAssetsWhitelist.sol

interface IEnableOnlyAssetsWhitelist {
    error ZeroAddressError();
    error WhitelistLimitReached();
    error InvalidOraclePrice();
    error InvalidAddress();
    error ReferenceAssetNotPermitted();
    error InvalidDecimalPlaces();
    error AssetAlreadyEnabled();
    error StalePrice();
    error RoundNotComplete();
    error InvalidTimePeriod();
    error InvalidOracleTimestamp();

    struct OracleInfo {
        address oracleAddress;
        address tokenAddress;
        uint8 oracleDecimals;
        uint8 tokenDecimals;
    }

    function enableAsset(
        address depositableAssetAddr, 
        address oracleAddr,
        uint256 newOracleDuration
    ) external;

    function getWhitelistedAssets() external view returns (address[] memory);
    function isWhitelisted(address addr) external view returns (bool);
    function getOracleAddress(address assetAddr) external view returns (address);
    function fromInputAssetToReferenceAsset(address assetAddr, uint256 amount) external view returns (uint256);
    function getTotalAssetsValuation(uint256 externalAssets) external view returns (uint256);

    function convertToShares(
        address lpTokenAddress, 
        address assetInAddr,
        address vaultAddr,
        uint256 assetInAmount,
        uint256 externalAssets
    ) external view returns (uint256 shares, uint256 amountInReferenceTokens);

    function REFERENCE_ASSET() external view returns (address);
    function REFERENCE_ASSET_DECIMALS() external view returns (uint8);
}

// lib/open-zeppelin/utils/math/MathUpgradeable.sol

// OpenZeppelin Contracts (last updated v4.7.0) (utils/math/Math.sol)

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library MathUpgradeable {
    enum Rounding {
        Down, // Toward negative infinity
        Up, // Toward infinity
        Zero // Toward zero
    }

    /**
     * @dev Returns the largest of two numbers.
     */
    function max(uint256 a, uint256 b) internal pure returns (uint256) {
        return a > b ? a : b;
    }

    /**
     * @dev Returns the smallest of two numbers.
     */
    function min(uint256 a, uint256 b) internal pure returns (uint256) {
        return a < b ? a : b;
    }

    /**
     * @dev Returns the average of two numbers. The result is rounded towards
     * zero.
     */
    function average(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b) / 2 can overflow.
        return (a & b) + (a ^ b) / 2;
    }

    /**
     * @dev Returns the ceiling of the division of two numbers.
     *
     * This differs from standard division with `/` in that it rounds up instead
     * of rounding down.
     */
    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b - 1) / b can overflow on addition, so we distribute.
        return a == 0 ? 0 : (a - 1) / b + 1;
    }

    /**
     * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
     * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
     * with further edits by Uniswap Labs also under MIT license.
     */
    function mulDiv(
        uint256 x,
        uint256 y,
        uint256 denominator
    ) internal pure returns (uint256 result) {
        unchecked {
            // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
            // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
            // variables such that product = prod1 * 2^256 + prod0.
            uint256 prod0; // Least significant 256 bits of the product
            uint256 prod1; // Most significant 256 bits of the product
            assembly {
                let mm := mulmod(x, y, not(0))
                prod0 := mul(x, y)
                prod1 := sub(sub(mm, prod0), lt(mm, prod0))
            }

            // Handle non-overflow cases, 256 by 256 division.
            if (prod1 == 0) {
                return prod0 / denominator;
            }

            // Make sure the result is less than 2^256. Also prevents denominator == 0.
            require(denominator > prod1);

            ///////////////////////////////////////////////
            // 512 by 256 division.
            ///////////////////////////////////////////////

            // Make division exact by subtracting the remainder from [prod1 prod0].
            uint256 remainder;
            assembly {
                // Compute remainder using mulmod.
                remainder := mulmod(x, y, denominator)

                // Subtract 256 bit number from 512 bit number.
                prod1 := sub(prod1, gt(remainder, prod0))
                prod0 := sub(prod0, remainder)
            }

            // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
            // See https://cs.stackexchange.com/q/138556/92363.

            // Does not overflow because the denominator cannot be zero at this stage in the function.
            uint256 twos = denominator & (~denominator + 1);
            assembly {
                // Divide denominator by twos.
                denominator := div(denominator, twos)

                // Divide [prod1 prod0] by twos.
                prod0 := div(prod0, twos)

                // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
                twos := add(div(sub(0, twos), twos), 1)
            }

            // Shift in bits from prod1 into prod0.
            prod0 |= prod1 * twos;

            // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
            // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
            // four bits. That is, denominator * inv = 1 mod 2^4.
            uint256 inverse = (3 * denominator) ^ 2;

            // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
            // in modular arithmetic, doubling the correct bits in each step.
            inverse *= 2 - denominator * inverse; // inverse mod 2^8
            inverse *= 2 - denominator * inverse; // inverse mod 2^16
            inverse *= 2 - denominator * inverse; // inverse mod 2^32
            inverse *= 2 - denominator * inverse; // inverse mod 2^64
            inverse *= 2 - denominator * inverse; // inverse mod 2^128
            inverse *= 2 - denominator * inverse; // inverse mod 2^256

            // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
            // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
            // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
            // is no longer required.
            result = prod0 * inverse;
            return result;
        }
    }

    /**
     * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
     */
    function mulDiv(
        uint256 x,
        uint256 y,
        uint256 denominator,
        Rounding rounding
    ) internal pure returns (uint256) {
        uint256 result = mulDiv(x, y, denominator);
        if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
            result += 1;
        }
        return result;
    }

    /**
     * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
     *
     * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
     */
    function sqrt(uint256 a) internal pure returns (uint256) {
        if (a == 0) {
            return 0;
        }

        // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
        //
        // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
        // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
        //
        // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
        // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
        // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
        //
        // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
        uint256 result = 1 << (log2(a) >> 1);

        // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
        // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
        // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
        // into the expected uint128 result.
        unchecked {
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            return min(result, a / result);
        }
    }

    /**
     * @notice Calculates sqrt(a), following the selected rounding direction.
     */
    function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = sqrt(a);
            return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 2, rounded down, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 128;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 64;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 32;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 16;
            }
            if (value >> 8 > 0) {
                value >>= 8;
                result += 8;
            }
            if (value >> 4 > 0) {
                value >>= 4;
                result += 4;
            }
            if (value >> 2 > 0) {
                value >>= 2;
                result += 2;
            }
            if (value >> 1 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log2(value);
            return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 10, rounded down, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >= 10**64) {
                value /= 10**64;
                result += 64;
            }
            if (value >= 10**32) {
                value /= 10**32;
                result += 32;
            }
            if (value >= 10**16) {
                value /= 10**16;
                result += 16;
            }
            if (value >= 10**8) {
                value /= 10**8;
                result += 8;
            }
            if (value >= 10**4) {
                value /= 10**4;
                result += 4;
            }
            if (value >= 10**2) {
                value /= 10**2;
                result += 2;
            }
            if (value >= 10**1) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log10(value);
            return result + (rounding == Rounding.Up && 10**result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 256, rounded down, of a positive value.
     * Returns 0 if given 0.
     *
     * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
     */
    function log256(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 16;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 8;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 4;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 2;
            }
            if (value >> 8 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log256(value);
            return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0);
        }
    }
}

// src/core/OwnableGuarded.sol

abstract contract OwnableGuarded {
    // ----------------------------------------------------------------------------------------------------
    // Constants
    // ----------------------------------------------------------------------------------------------------
    // Booleans are more expensive than uint256 or any type that takes up a full
    // word because each write operation emits an extra SLOAD to first read the
    // slot's contents, replace the bits taken up by the boolean, and then write
    // back. This is the compiler's defense against contract upgrades and
    // pointer aliasing, and it cannot be disabled.

    // The values being non-zero value makes deployment a bit more expensive,
    // but in exchange the refund on every call to nonReentrant will be lower in
    // amount. Since refunds are capped to a percentage of the total
    // transaction's gas, it is best to keep them low in cases like this one, to
    // increase the likelihood of the full refund coming into effect.
    uint256 private constant NOT_ENTERED = 1;
    uint256 private constant ENTERED = 2;

    // ----------------------------------------------------------------------------------------------------
    // Errors
    // ----------------------------------------------------------------------------------------------------
    error OwnerOnly();
    error OwnerAddressRequired();
    error ReentrancyGuardReentrantCall();

    // ----------------------------------------------------------------------------------------------------
    // Storage layout
    // ----------------------------------------------------------------------------------------------------
    uint256 private _status;
    address internal _owner;

    // ----------------------------------------------------------------------------------------------------
    // Events
    // ----------------------------------------------------------------------------------------------------
    /**
     * @notice Triggers when contract ownership changes.
     * @param previousOwner The previous owner of the contract.
     * @param newOwner The new owner of the contract.
     */
    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    // ----------------------------------------------------------------------------------------------------
    // Modifiers
    // ----------------------------------------------------------------------------------------------------
    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and making it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        // On the first call to nonReentrant, _status will be NOT_ENTERED
        if (_status == ENTERED) {
            revert ReentrancyGuardReentrantCall();
        }

        // Any calls to nonReentrant after this point will fail
        _status = ENTERED;

        _;

        // By storing the original value once again, a refund is triggered (see https://eips.ethereum.org/EIPS/eip-2200)
        _status = NOT_ENTERED;
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        if (msg.sender != _owner) revert OwnerOnly();
        _;
    }

    // ----------------------------------------------------------------------------------------------------
    // Functions
    // ----------------------------------------------------------------------------------------------------
    /**
     * @notice Transfers ownership of the contract to the account specified.
     * @param newOwner The address of the new owner.
     */
    function transferOwnership(address newOwner) external virtual nonReentrant onlyOwner {
        _transferOwnership(newOwner);
    }

    function _transferOwnership(address newOwner) internal virtual {
        if (newOwner == address(0)) revert OwnerAddressRequired();

        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }

    /**
     * @notice Gets the owner of the contract.
     * @return address The address who owns the contract.
     */
    function owner() external view virtual returns (address) {
        return _owner;
    }
}

// lib/open-zeppelin/token/ERC20/extensions/IERC20Metadata.sol

// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.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);
}

// lib/open-zeppelin/token/ERC20/ERC20.sol

// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/ERC20.sol)

/**
 * @dev Implementation of the {IERC20} interface.
 *
 * This implementation is agnostic to the way tokens are created. This means
 * that a supply mechanism has to be added in a derived contract using {_mint}.
 * For a generic mechanism see {ERC20PresetMinterPauser}.
 *
 * TIP: For a detailed writeup see our guide
 * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
 * to implement supply mechanisms].
 *
 * The default value of {decimals} is 18. To change this, you should override
 * this function so it returns a different value.
 *
 * We have followed general OpenZeppelin Contracts guidelines: functions revert
 * instead returning `false` on failure. This behavior is nonetheless
 * conventional and does not conflict with the expectations of ERC20
 * applications.
 *
 * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
 * This allows applications to reconstruct the allowance for all accounts just
 * by listening to said events. Other implementations of the EIP may not emit
 * these events, as it isn't required by the specification.
 *
 * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
 * functions have been added to mitigate the well-known issues around setting
 * allowances. See {IERC20-approve}.
 */
contract ERC20 is Context, IERC20, IERC20Metadata {
    mapping(address => uint256) private _balances;

    mapping(address => mapping(address => uint256)) private _allowances;

    uint256 private _totalSupply;

    string private _name;
    string private _symbol;

    /**
     * @dev Sets the values for {name} and {symbol}.
     *
     * All two of these values are immutable: they can only be set once during
     * construction.
     */
    constructor(string memory name_, string memory symbol_) {
        _name = name_;
        _symbol = symbol_;
    }

    /**
     * @dev Returns the name of the token.
     */
    function name() public view virtual override returns (string memory) {
        return _name;
    }

    /**
     * @dev Returns the symbol of the token, usually a shorter version of the
     * name.
     */
    function symbol() public view virtual override returns (string memory) {
        return _symbol;
    }

    /**
     * @dev Returns the number of decimals used to get its user representation.
     * For example, if `decimals` equals `2`, a balance of `505` tokens should
     * be displayed to a user as `5.05` (`505 / 10 ** 2`).
     *
     * Tokens usually opt for a value of 18, imitating the relationship between
     * Ether and Wei. This is the default value returned by this function, unless
     * it's overridden.
     *
     * NOTE: This information is only used for _display_ purposes: it in
     * no way affects any of the arithmetic of the contract, including
     * {IERC20-balanceOf} and {IERC20-transfer}.
     */
    function decimals() public view virtual override returns (uint8) {
        return 18;
    }

    /**
     * @dev See {IERC20-totalSupply}.
     */
    function totalSupply() public view virtual override returns (uint256) {
        return _totalSupply;
    }

    /**
     * @dev See {IERC20-balanceOf}.
     */
    function balanceOf(address account) public view virtual override returns (uint256) {
        return _balances[account];
    }

    /**
     * @dev See {IERC20-transfer}.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - the caller must have a balance of at least `amount`.
     */
    function transfer(address to, uint256 amount) public virtual override returns (bool) {
        address owner = _msgSender();
        _transfer(owner, to, amount);
        return true;
    }

    /**
     * @dev See {IERC20-allowance}.
     */
    function allowance(address owner, address spender) public view virtual override returns (uint256) {
        return _allowances[owner][spender];
    }

    /**
     * @dev See {IERC20-approve}.
     *
     * NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on
     * `transferFrom`. This is semantically equivalent to an infinite approval.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function approve(address spender, uint256 amount) public virtual override returns (bool) {
        address owner = _msgSender();
        _approve(owner, spender, amount);
        return true;
    }

    /**
     * @dev See {IERC20-transferFrom}.
     *
     * Emits an {Approval} event indicating the updated allowance. This is not
     * required by the EIP. See the note at the beginning of {ERC20}.
     *
     * NOTE: Does not update the allowance if the current allowance
     * is the maximum `uint256`.
     *
     * Requirements:
     *
     * - `from` and `to` cannot be the zero address.
     * - `from` must have a balance of at least `amount`.
     * - the caller must have allowance for ``from``'s tokens of at least
     * `amount`.
     */
    function transferFrom(address from, address to, uint256 amount) public virtual override returns (bool) {
        address spender = _msgSender();
        _spendAllowance(from, spender, amount);
        _transfer(from, to, amount);
        return true;
    }

    /**
     * @dev Atomically increases the allowance granted to `spender` by the caller.
     *
     * This is an alternative to {approve} that can be used as a mitigation for
     * problems described in {IERC20-approve}.
     *
     * Emits an {Approval} event indicating the updated allowance.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
        address owner = _msgSender();
        _approve(owner, spender, allowance(owner, spender) + addedValue);
        return true;
    }

    /**
     * @dev Atomically decreases the allowance granted to `spender` by the caller.
     *
     * This is an alternative to {approve} that can be used as a mitigation for
     * problems described in {IERC20-approve}.
     *
     * Emits an {Approval} event indicating the updated allowance.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `spender` must have allowance for the caller of at least
     * `subtractedValue`.
     */
    function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
        address owner = _msgSender();
        uint256 currentAllowance = allowance(owner, spender);
        require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
        unchecked {
            _approve(owner, spender, currentAllowance - subtractedValue);
        }

        return true;
    }

    /**
     * @dev Moves `amount` of tokens from `from` to `to`.
     *
     * This internal function is equivalent to {transfer}, and can be used to
     * e.g. implement automatic token fees, slashing mechanisms, etc.
     *
     * Emits a {Transfer} event.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `from` must have a balance of at least `amount`.
     */
    function _transfer(address from, address to, uint256 amount) internal virtual {
        require(from != address(0), "ERC20: transfer from the zero address");
        require(to != address(0), "ERC20: transfer to the zero address");

        _beforeTokenTransfer(from, to, amount);

        uint256 fromBalance = _balances[from];
        require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
        unchecked {
            _balances[from] = fromBalance - amount;
            // Overflow not possible: the sum of all balances is capped by totalSupply, and the sum is preserved by
            // decrementing then incrementing.
            _balances[to] += amount;
        }

        emit Transfer(from, to, amount);

        _afterTokenTransfer(from, to, amount);
    }

    /** @dev Creates `amount` tokens and assigns them to `account`, increasing
     * the total supply.
     *
     * Emits a {Transfer} event with `from` set to the zero address.
     *
     * Requirements:
     *
     * - `account` cannot be the zero address.
     */
    function _mint(address account, uint256 amount) internal virtual {
        require(account != address(0), "ERC20: mint to the zero address");

        _beforeTokenTransfer(address(0), account, amount);

        _totalSupply += amount;
        unchecked {
            // Overflow not possible: balance + amount is at most totalSupply + amount, which is checked above.
            _balances[account] += amount;
        }
        emit Transfer(address(0), account, amount);

        _afterTokenTransfer(address(0), account, amount);
    }

    /**
     * @dev Destroys `amount` tokens from `account`, reducing the
     * total supply.
     *
     * Emits a {Transfer} event with `to` set to the zero address.
     *
     * Requirements:
     *
     * - `account` cannot be the zero address.
     * - `account` must have at least `amount` tokens.
     */
    function _burn(address account, uint256 amount) internal virtual {
        require(account != address(0), "ERC20: burn from the zero address");

        _beforeTokenTransfer(account, address(0), amount);

        uint256 accountBalance = _balances[account];
        require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
        unchecked {
            _balances[account] = accountBalance - amount;
            // Overflow not possible: amount <= accountBalance <= totalSupply.
            _totalSupply -= amount;
        }

        emit Transfer(account, address(0), amount);

        _afterTokenTransfer(account, address(0), amount);
    }

    /**
     * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
     *
     * This internal function is equivalent to `approve`, and can be used to
     * e.g. set automatic allowances for certain subsystems, etc.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `owner` cannot be the zero address.
     * - `spender` cannot be the zero address.
     */
    function _approve(address owner, address spender, uint256 amount) internal virtual {
        require(owner != address(0), "ERC20: approve from the zero address");
        require(spender != address(0), "ERC20: approve to the zero address");

        _allowances[owner][spender] = amount;
        emit Approval(owner, spender, amount);
    }

    /**
     * @dev Updates `owner` s allowance for `spender` based on spent `amount`.
     *
     * Does not update the allowance amount in case of infinite allowance.
     * Revert if not enough allowance is available.
     *
     * Might emit an {Approval} event.
     */
    function _spendAllowance(address owner, address spender, uint256 amount) internal virtual {
        uint256 currentAllowance = allowance(owner, spender);
        if (currentAllowance != type(uint256).max) {
            require(currentAllowance >= amount, "ERC20: insufficient allowance");
            unchecked {
                _approve(owner, spender, currentAllowance - amount);
            }
        }
    }

    /**
     * @dev Hook that is called before any transfer of tokens. This includes
     * minting and burning.
     *
     * Calling conditions:
     *
     * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
     * will be transferred to `to`.
     * - when `from` is zero, `amount` tokens will be minted for `to`.
     * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
     * - `from` and `to` are never both zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual {}

    /**
     * @dev Hook that is called after any transfer of tokens. This includes
     * minting and burning.
     *
     * Calling conditions:
     *
     * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
     * has been transferred to `to`.
     * - when `from` is zero, `amount` tokens have been minted for `to`.
     * - when `to` is zero, `amount` of ``from``'s tokens have been burned.
     * - `from` and `to` are never both zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _afterTokenTransfer(address from, address to, uint256 amount) internal virtual {}
}

// src/core/EnableOnlyAssetsWhitelist.sol

/**
 * @title Assets whitelist.
 * @dev Assets cannot be disabled once they're enabled. There is one whitelist per Reference Asset. 
 */
contract EnableOnlyAssetsWhitelist is IEnableOnlyAssetsWhitelist, OwnableGuarded {
    using MathUpgradeable for uint256;

    // --------------------------------------------------------------------------
    // Storage layout
    // --------------------------------------------------------------------------
    /// @dev The list of whitelisted assets
    address[] internal _whitelistedAssets;

    /// @notice The address of the reference asset
    address public immutable override REFERENCE_ASSET;

    /// @notice The decimal places of the reference asset
    uint8 public immutable override REFERENCE_ASSET_DECIMALS;

    /// @dev Tracks the oracle assigned to a synthetic pair (input asset => OracleInfo)
    mapping (address => OracleInfo) internal _oracleOfInputAsset;

    /// @notice The difference between the current timestamp and the quote from the external oracle, expressed in seconds.
    mapping (address => uint256) public maxOracleUpdatesDuration;

    // --------------------------------------------------------------------------
    // Constructor
    // --------------------------------------------------------------------------
    constructor(address ownerAddr, address referenceAssetAddr) {
        if (ownerAddr == address(0)) revert OwnerAddressRequired();
        if (referenceAssetAddr == address(0)) revert InvalidAddress();

        _owner = ownerAddr;
        REFERENCE_ASSET = referenceAssetAddr;
        REFERENCE_ASSET_DECIMALS = ERC20(referenceAssetAddr).decimals();
    }

    // --------------------------------------------------------------------------
    // Functions
    // --------------------------------------------------------------------------
    /**
     * @notice Enables the asset specified.
     * @dev Enabling an asset requires assigning an oracle to the synthetic pair.
     * @param assetAddr The address of the asset to enable.
     * @param oracleAddr The oracle assigned to the asset specified.
     * @param newOracleDuration The lag accepted on the oracle of this asset
     */
    function enableAsset(
        address assetAddr, 
        address oracleAddr,
        uint256 newOracleDuration
    ) external override nonReentrant onlyOwner {
        if ((oracleAddr == address(0)) || (assetAddr == address(0))) revert ZeroAddressError();
        if (_whitelistedAssets.length > 30) revert WhitelistLimitReached();
        if (assetAddr == REFERENCE_ASSET) revert ReferenceAssetNotPermitted();

        // Make sure the asset is not duplicated
        if (_oracleOfInputAsset[assetAddr].tokenAddress != address(0)) revert AssetAlreadyEnabled();
        _whitelistedAssets.push(assetAddr);

        uint8 tokenDecimals = ERC20(assetAddr).decimals();
        
        // Make sure the token has 6 decimal positions at least
        if (tokenDecimals < 6) revert InvalidDecimalPlaces();

        uint8 oracleDecimals = IAggregatorV3Interface(oracleAddr).decimals();

        _oracleOfInputAsset[assetAddr] = OracleInfo({
            oracleAddress: oracleAddr,
            tokenAddress: assetAddr,
            oracleDecimals: oracleDecimals,
            tokenDecimals: tokenDecimals
        });

        maxOracleUpdatesDuration[assetAddr] = newOracleDuration;
    }

    /**
     * @notice Updates the maximum lag of oracles.
     * @param newMaxOracleUpdatesDuration The lag we are willing to accept.
     * @param assetAddr The asset address
     */
    function updateOracleLagDuration(uint256 newMaxOracleUpdatesDuration, address assetAddr) external nonReentrant onlyOwner {
        maxOracleUpdatesDuration[assetAddr] = newMaxOracleUpdatesDuration;
    }

    /**
     * @notice Shares conversion function.
     * @param lpTokenAddress The address of the LP token.
     * @param assetInAddr The address of the deposit token.
     * @param vaultAddr The address of the vault.
     * @param assetInAmount The deposit amount.
     * @param externalAssets The external assets reported by the vault.
     * @return shares The number of shares
     * @return amountInReferenceTokens The amount expressed in reference tokens
     */
    function convertToShares(
        address lpTokenAddress, 
        address assetInAddr,
        address vaultAddr,
        uint256 assetInAmount,
        uint256 externalAssets
    ) external view override returns (
        uint256 shares, 
        uint256 amountInReferenceTokens
    ) {
        if (assetInAmount < 1) return (0, 0);

        uint256 tSupply = IERC20(lpTokenAddress).totalSupply();
        amountInReferenceTokens = (assetInAddr == REFERENCE_ASSET) ? assetInAmount : _fromInputAssetToReferenceAsset(assetInAddr, assetInAmount);
        uint256 totalAssetsInReferenceTokens = _getTotalAssetsValuation(vaultAddr, externalAssets);

        shares = (tSupply < 1) ? amountInReferenceTokens : amountInReferenceTokens.mulDiv(tSupply, totalAssetsInReferenceTokens, MathUpgradeable.Rounding.Down);
    }

    /**
     * @notice Gets the oracle assigned to the asset specified.
     * @return address Returns the address of the oracle.
     */
    function getOracleAddress(address assetAddr) external view override returns (address) {
        return _oracleOfInputAsset[assetAddr].oracleAddress;
    }

    /**
     * @notice Indicates if a given asset is whitelisted.
     * @return bool Returns true if the asset is whitelisted.
     */
    function isWhitelisted(address assetAddr) external view override returns (bool) {
        return _oracleOfInputAsset[assetAddr].oracleAddress != address(0);
    }

    /**
     * @notice Gets the list of whitelisted assets.
     * @return address[] Returns an array of asset addresses.
     */
    function getWhitelistedAssets() external view override returns (address[] memory) {
        return _whitelistedAssets;
    }

    /**
     * @notice Converts the input amount into the respective amount of reference tokens.
     * @param assetAddr The asset address.
     * @param amount The asset amount.
     */
    function fromInputAssetToReferenceAsset(
        address assetAddr, 
        uint256 amount
    ) external view override returns (uint256) {
        return _fromInputAssetToReferenceAsset(assetAddr, amount);
    }

    /**
     * @notice Gets the valuation (total assets) of the sender specified. The sender is a vault.
     * @param externalAssets The external assets reported by the vault.
     * @return uint256 The total valuation of the vault.
     */
    function getTotalAssetsValuation(uint256 externalAssets) external view override returns (uint256) {
        return _getTotalAssetsValuation(msg.sender, externalAssets);
    }

    function _convertToAssets(
        address vaultAddr,
        address lpTokenAddress, 
        uint256 externalAssets, 
        uint256 shares, 
        MathUpgradeable.Rounding rounding
    ) internal view returns (uint256) {
        uint256 tSupply = IERC20(lpTokenAddress).totalSupply();
        return (tSupply < 1) ? shares : shares.mulDiv(_getTotalAssetsValuation(vaultAddr, externalAssets), tSupply, rounding);
    }

    /// @dev Calculates the valuation of the vault specified. The result is expressed in reference tokens.
    function _getTotalAssetsValuation(
        address vaultAddr,
        uint256 externalAssets
    ) internal view returns (uint256) {
        address assetAddr;
        uint256 assetBalance;
        uint256 balanceInReferenceTokens;
        uint256 t = _whitelistedAssets.length;

        uint256 acum = externalAssets + IERC20(REFERENCE_ASSET).balanceOf(vaultAddr);

        for (uint256 i; i < t; i++) {
            assetAddr = _whitelistedAssets[i];
            assetBalance = IERC20(assetAddr).balanceOf(vaultAddr);

            if (assetBalance > 0) {
                balanceInReferenceTokens = _fromInputAssetToReferenceAsset(assetAddr, assetBalance);
                acum += balanceInReferenceTokens;
            }
        }

        // External assets + multi assets liquidity + liquidity of the reference token
        return acum;
    }

    /// @dev Converts the input amount into the respective amount of reference tokens
    function _fromInputAssetToReferenceAsset(
        address assetAddr, 
        uint256 amount
    ) internal view returns (uint256) {
        address oracleAddr = _oracleOfInputAsset[assetAddr].oracleAddress;
        uint8 tokenDecimals = _oracleOfInputAsset[assetAddr].tokenDecimals;
        uint8 oracleDecimals = _oracleOfInputAsset[assetAddr].oracleDecimals;

        // Query the external Oracle
        (
            uint80 quoteRoundId, 
            int256 quoteAnswer,
            , 
            uint256 quoteTimestamp, 
            uint80 quoteAnsweredInRound
        ) = IAggregatorV3Interface(oracleAddr).latestRoundData();

        // Validate the Oracle's response
        if (quoteAnswer < 1) revert InvalidOraclePrice();
        if (quoteAnsweredInRound < quoteRoundId) revert StalePrice();
        if (quoteTimestamp < 1) revert RoundNotComplete();
        if (quoteTimestamp > block.timestamp) revert InvalidOracleTimestamp(); // Cannot trust in the external contract/oracle, thus the check.
        if (block.timestamp - quoteTimestamp > maxOracleUpdatesDuration[assetAddr]) revert InvalidTimePeriod();

        // Distance between the decimals of the oracle and the decimals of the token
        uint256 d1 = (oracleDecimals > tokenDecimals) ? 10 ** (oracleDecimals - tokenDecimals) : 1;
        uint256 d2 = (REFERENCE_ASSET_DECIMALS > oracleDecimals) ? 10 ** (REFERENCE_ASSET_DECIMALS - oracleDecimals) : 1;
        uint256 amountInNormalized = amount * d1;

        // Distance between the maximum scale and the scale provided by the oracle
        uint256 d3 = 10 ** (18 - oracleDecimals);
        uint256 ratio = (10 ** oracleDecimals) * d3 / uint256(quoteAnswer); // Scaled to d2
        uint256 result = ratio * amountInNormalized * d2 / d3;

        if (tokenDecimals == REFERENCE_ASSET_DECIMALS) {
            if (REFERENCE_ASSET_DECIMALS == 6) {
                result /= d1;
            }
            else if (REFERENCE_ASSET_DECIMALS > 17) {
                ratio = uint256(quoteAnswer);
                result = ratio * amountInNormalized * d2 / d3 / 1e18;                
            }
        }

        if (REFERENCE_ASSET_DECIMALS == 6) {
            if (tokenDecimals == 18) result /= 1e7;
            else if (tokenDecimals == 8) result /= 1e2;
        }

        return result;
    }
}

Tags:
ERC20, Multisig, Liquidity, Upgradeable, Multi-Signature, Factory, Oracle|addr:0x72f3cf0d87e9688fad77711d02d86c68334fb275|verified:true|block:23447879|tx:0xb9c91d2aedc1d9b0c21e2b796d0bb1c910e50202fa36e59d3db842242dbfce0f|first_check:1758898137

Submitted on: 2025-09-26 16:49:00

Comments

Log in to comment.

No comments yet.