OwnableOracle_USDTUSDC

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: MIT
pragma solidity ^0.8.0 ^0.8.19;

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

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

// src/oracles/OracleUSDTUSDC.sol

/**
 * @title USDT/USDC Oracle
 * @dev Custom oracle that composes Chainlink USDT/USD and USDC/USD feeds to provide USDT/USDC rate
 * @notice This contract follows the Chainlink AggregatorV3Interface
 */
contract OracleUSDTUSDC is IAggregatorV3Interface {
    // Errors
    error InvalidPriceData();
    error StalePrice();
    error InvalidOraclePrice();
    error InvalidFeedAddress();
    error InvalidThreshold();
    
    uint256 public constant override version = 1;
    uint8 public constant override decimals = 6;
    string public constant override description = "USDT / USDC";

    uint256 public stalenessThreshold;
    IAggregatorV3Interface private immutable usdtUsdPriceFeed;
    IAggregatorV3Interface private immutable usdcUsdPriceFeed;
    
    // Events
    event PriceUpdated(int256 price, uint256 timestamp);
    
    
    /**
     * @dev Constructor sets the Chainlink price feed addresses
     * @param _usdtUsdPriceFeed Address of the USDT/USD Chainlink price feed
     * @param _usdcUsdPriceFeed Address of the USDC/USD Chainlink price feed
     * @param newStalenessThreshold The staleness threshold expressed in seconds
     */
    constructor(address _usdtUsdPriceFeed, address _usdcUsdPriceFeed, uint256 newStalenessThreshold) {
        if ((_usdtUsdPriceFeed == address(0)) || (_usdcUsdPriceFeed == address(0))) revert InvalidFeedAddress();
        if (newStalenessThreshold < 1) revert InvalidThreshold();
        
        stalenessThreshold = newStalenessThreshold;
        usdtUsdPriceFeed = IAggregatorV3Interface(_usdtUsdPriceFeed);
        usdcUsdPriceFeed = IAggregatorV3Interface(_usdcUsdPriceFeed);
    }
    
    /**
     * @dev Returns the latest round data for USDT/USDC
     * @return roundId The round ID (based on USDT feed)
     * @return answer The USDT/USDC price with 18 decimals
     * @return startedAt Timestamp when the round started (min of both feeds)
     * @return updatedAt Timestamp when the round was updated (min of both feeds)
     * @return answeredInRound The round ID in which the answer was computed
     */
    function latestRoundData()
        external
        view
        override
        returns (
            uint80 roundId,
            int256 answer,
            uint256 startedAt,
            uint256 updatedAt,
            uint80 answeredInRound
        )
    {
        // Get data from both price feeds
        (
            uint80 usdtRoundId,
            int256 usdtPrice,
            uint256 usdtStartedAt,
            uint256 usdtUpdatedAt,
            uint80 usdtAnsweredInRound
        ) = usdtUsdPriceFeed.latestRoundData();
        
        (
            ,
            int256 usdcPrice,
            uint256 usdcStartedAt,
            uint256 usdcUpdatedAt,
            
        ) = usdcUsdPriceFeed.latestRoundData();
        
        // Validate prices
        if (usdtPrice < 1 || usdcPrice < 1) revert InvalidOraclePrice();
        
        // Check for stale prices
        if (
            block.timestamp - usdtUpdatedAt > stalenessThreshold ||
            block.timestamp - usdcUpdatedAt > stalenessThreshold
        ) {
            revert StalePrice();
        }
                
        // Calculate USDT/USDC = (USDT/USD) / (USDC/USD)
        int256 usdtUsdcPrice = _normalizePrice(usdtPrice, usdcPrice);

        // Return data using USDT feed's metadata with calculated price
        return (
            usdtRoundId,
            usdtUsdcPrice,
            usdtStartedAt < usdcStartedAt ? usdtStartedAt : usdcStartedAt, // min startedAt
            usdtUpdatedAt < usdcUpdatedAt ? usdtUpdatedAt : usdcUpdatedAt, // min updatedAt
            usdtAnsweredInRound
        );
    }

    function _normalizePrice(int256 usdtPrice, int256 usdcPrice) private view returns (int256) {
        // Calculate USDT/USDC price with proper decimal handling
        uint8 usdtDecimals = usdtUsdPriceFeed.decimals();
        uint8 usdcDecimals = usdcUsdPriceFeed.decimals();
        
        // Normalize prices to 18 decimals for calculation
        uint256 normalizedUsdtPrice = uint256(usdtPrice) * (10 ** (18 - usdtDecimals));
        uint256 normalizedUsdcPrice = uint256(usdcPrice) * (10 ** (18 - usdcDecimals));
        
        // Calculate USDT/USDC = (USDT/USD) / (USDC/USD)
        int256 usdtUsdcPrice = int256((normalizedUsdtPrice * 1e18) / normalizedUsdcPrice);

        return usdtUsdcPrice / 1e12;
    }
    
    /**
     * @dev Returns historical round data
     * @notice This implementation returns the latest data regardless of roundId
     * @param _roundId The round ID to query (ignored in this implementation)
     */
    function getRoundData(uint80 _roundId)
        external
        view
        override
        returns (
            uint80 roundId,
            int256 answer,
            uint256 startedAt,
            uint256 updatedAt,
            uint80 answeredInRound
        )
    {
        // For simplicity, return latest data for any round query
        return this.latestRoundData();
    }
    
    /**
     * @dev Get the current USDT/USDC price
     * @return price The current price with 18 decimals
     */
    function getPrice() external view returns (int256 price) {
        (, price, , , ) = this.latestRoundData();
    }
    
    /**
     * @dev Get the addresses of the underlying price feeds
     * @return usdtFeed Address of USDT/USD price feed
     * @return usdcFeed Address of USDC/USD price feed
     */
    function getUnderlyingFeeds() external view returns (address usdtFeed, address usdcFeed) {
        return (address(usdtUsdPriceFeed), address(usdcUsdPriceFeed));
    }
    
    /**
     * @dev Check if the price data is fresh
     * @return true if both underlying feeds have fresh data
     */
    function isFresh() external view returns (bool) {
        (, , , uint256 usdtUpdatedAt, ) = usdtUsdPriceFeed.latestRoundData();
        (, , , uint256 usdcUpdatedAt, ) = usdcUsdPriceFeed.latestRoundData();
        
        return (
            block.timestamp - usdtUpdatedAt <= stalenessThreshold &&
            block.timestamp - usdcUpdatedAt <= stalenessThreshold
        );
    }
}

// src/oracles/OwnableOracle_USDTUSDC.sol

contract OwnableOracle_USDTUSDC is OwnableGuarded, OracleUSDTUSDC {
    constructor(
        address _usdtUsdPriceFeed, 
        address _usdcUsdPriceFeed, 
        uint256 newStalenessThreshold
    ) OracleUSDTUSDC(_usdtUsdPriceFeed, _usdcUsdPriceFeed, newStalenessThreshold) {
        _owner = msg.sender;
    }

    function updateStalenessThreshold(uint256 newStalenessThreshold) external nonReentrant onlyOwner {
        if (newStalenessThreshold < 1) revert InvalidThreshold();
        stalenessThreshold = newStalenessThreshold;
    }
}

Tags:
Multisig, Upgradeable, Multi-Signature, Factory, Oracle|addr:0xa54a7f3fdaa0abbe0d9bf946752cdd3e3539c876|verified:true|block:23470816|tx:0xe352644c490a28eb16cfddaa7a15c11966dcc1a73a5e67332aeacc7437b84117|first_check:1759219203

Submitted on: 2025-09-30 10:00:03

Comments

Log in to comment.

No comments yet.