Blockchain / Global / Address

Contract

0xF450bcfd5aaCC81161Fe795434BA3100a4a22aa5

Balance
0 SOMI
Transactions sent
1
Type
Contract · 3,369 bytes of code

CLFeedRegistryAdapter Verified

Compiler
solc 0.8.28+commit.7893614a
Language
Solidity
Optimization
Yes, 200 runs
EVM version
paris
License
—
Verified
2026-10-12

contracts/cl-adapters/registries/CLFeedRegistryAdapter.sol

and 5 more files

Solidity
// SPDX-License-Identifier: MIT
// SPDX-FileCopyrightText: Copyright (c) 2024-2025 Schelling Point Labs Inc.
pragma solidity ^0.8.28;

import '../../interfaces/ICLFeedRegistryAdapter.sol';
import {ICLAggregatorAdapter} from '../../interfaces/ICLAggregatorAdapter.sol';
import {CLAdapterLib} from '../../libraries/CLAdapterLib.sol';

/// @title Registry aggregating information from CLAggregatorAdapters and the feed itself
/// @author Aneta Tsvetkova
/// @notice This contract is used to store and retrieve information about feeds
/// @dev To reduce gas costs, the contract calls the dataFeedStore directly instead of using the CLAggregatorAdapter
contract CLFeedRegistryAdapter is ICLFeedRegistryAdapter {
  /// @notice The data feed store contract
  address internal immutable DATA_FEED_STORE;
  /// @inheritdoc ICLFeedRegistryAdapter
  address public immutable override OWNER;

  /// @notice Feed data for a given pair
  mapping(address => mapping(address => FeedData)) internal feedData;

  /// @notice Constructor
  /// @param _owner The owner of the contract
  /// @param _dataFeedStore The address of the data feed store
  constructor(address _owner, address _dataFeedStore) {
    OWNER = _owner;
    DATA_FEED_STORE = _dataFeedStore;
  }

  /// @inheritdoc IChainlinkFeedRegistry
  function decimals(
    address base,
    address quote
  ) external view override returns (uint8) {
    return feedData[base][quote].decimals;
  }

  /// @inheritdoc IChainlinkFeedRegistry
  function description(
    address base,
    address quote
  ) external view override returns (string memory) {
    return feedData[base][quote].description;
  }

  /// @inheritdoc IChainlinkFeedRegistry
  function latestAnswer(
    address base,
    address quote
  ) external view override returns (int256) {
    return CLAdapterLib.latestAnswer(feedData[base][quote].id, DATA_FEED_STORE);
  }

  /// @inheritdoc IChainlinkFeedRegistry
  function getRoundData(
    address base,
    address quote,
    uint80 _roundId
  ) external view override returns (uint80, int256, uint256, uint256, uint80) {
    return
      CLAdapterLib.getRoundData(
        _roundId,
        feedData[base][quote].id,
        DATA_FEED_STORE
      );
  }

  /// @inheritdoc IChainlinkFeedRegistry
  function getFeed(
    address base,
    address quote
  ) external view override returns (IChainlinkAggregator) {
    return feedData[base][quote].aggregator;
  }

  /// @inheritdoc ICLFeedRegistryAdapter
  function setFeeds(Feed[] calldata feeds) external override {
    if (msg.sender != OWNER) {
      revert OnlyOwner();
    }

    for (uint256 i = 0; i < feeds.length; i++) {
      feedData[feeds[i].base][feeds[i].quote] = FeedData(
        IChainlinkAggregator(feeds[i].feed),
        ICLAggregatorAdapter(feeds[i].feed).decimals(),
        CLAdapterLib.shiftId(ICLAggregatorAdapter(feeds[i].feed).id()),
        ICLAggregatorAdapter(feeds[i].feed).description()
      );
    }
  }

  /// @inheritdoc IChainlinkFeedRegistry
  function latestRound(
    address base,
    address quote
  ) external view override returns (uint256) {
    return CLAdapterLib.latestRound(feedData[base][quote].id, DATA_FEED_STORE);
  }

  /// @inheritdoc IChainlinkFeedRegistry
  function latestRoundData(
    address base,
    address quote
  ) external view override returns (uint80, int256, uint256, uint256, uint80) {
    return
      CLAdapterLib.latestRoundData(feedData[base][quote].id, DATA_FEED_STORE);
  }
}

Other files

  • contracts/interfaces/ICLAggregatorAdapter.sol
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.28;
    
    import {IChainlinkAggregator} from './chainlink/IChainlinkAggregator.sol';
    
    interface ICLAggregatorAdapter is IChainlinkAggregator {
      /// @notice The feed data this contract is responsible for
      /// @dev This is the feed ID for the mapping in the dataFeedStore
      /// @return _id The ID for the feed
      function id() external view returns (uint256);
    
      /// @notice The dataFeedStore this contract is responsible for
      /// @dev The address of the underlying contract that stores the data
      /// @return dataFeedStore The address of the dataFeedStore
      function dataFeedStore() external view returns (address);
    }
    
  • contracts/interfaces/ICLFeedRegistryAdapter.sol
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.28;
    
    import {IChainlinkFeedRegistry, IChainlinkAggregator} from './chainlink/IChainlinkFeedRegistry.sol';
    
    interface ICLFeedRegistryAdapter is IChainlinkFeedRegistry {
      struct FeedData {
        IChainlinkAggregator aggregator;
        uint8 decimals;
        uint256 id;
        string description;
      }
    
      struct Feed {
        address base;
        address quote;
        address feed;
      }
    
      error OnlyOwner();
    
      /// @notice Contract owner
      /// @return owner The address of the owner
      function OWNER() external view returns (address);
    
      /// @notice Set the feed for a given pair
      /// @dev Stores immutable values (decimals, id, description) and contract address from CLAggregatorAdapter
      /// @param feeds Array of base, quote and feed address data
      function setFeeds(Feed[] calldata feeds) external;
    }
    
  • contracts/interfaces/chainlink/IChainlinkAggregator.sol
    /**
     * SPDX-FileCopyrightText: Copyright (c) 2021 SmartContract ChainLink Limited SEZC
     *
     * SPDX-License-Identifier: MIT
     */
    pragma solidity ^0.8.28;
    
    interface IChainlinkAggregator {
      /// @notice Decimals for the feed data
      /// @return decimals The decimals of the feed
      function decimals() external view returns (uint8);
    
      /// @notice Description text for the feed data
      /// @return description The description of the feed
      function description() external view returns (string memory);
    
      /// @notice Get the latest answer for the feed
      /// @return answer The latest value stored
      function latestAnswer() external view returns (int256);
    
      /// @notice Get the latest round ID for the feed
      /// @return roundId The latest round ID
      function latestRound() external view returns (uint256);
    
      /// @notice Get the data for a round at a given round ID
      /// @param _roundId The round ID to retrieve the data for
      /// @return roundId The round ID
      /// @return answer The value stored for the round
      /// @return startedAt Timestamp of when the value was stored
      /// @return updatedAt Same as startedAt
      /// @return answeredInRound Same as roundId
      function getRoundData(
        uint80 _roundId
      )
        external
        view
        returns (
          uint80 roundId,
          int256 answer,
          uint256 startedAt,
          uint256 updatedAt,
          uint80 answeredInRound
        );
    
      /// @notice Get the latest round data available
      /// @return roundId The latest round ID for the feed
      /// @return answer The value stored for the round
      /// @return startedAt Timestamp of when the value was stored
      /// @return updatedAt Same as startedAt
      /// @return answeredInRound Same as roundId
      function latestRoundData()
        external
        view
        returns (
          uint80 roundId,
          int256 answer,
          uint256 startedAt,
          uint256 updatedAt,
          uint80 answeredInRound
        );
    }
    
  • contracts/interfaces/chainlink/IChainlinkFeedRegistry.sol
    /**
     * SPDX-FileCopyrightText: Copyright (c) 2021 SmartContract ChainLink Limited SEZC
     *
     * SPDX-License-Identifier: MIT
     */
    pragma solidity ^0.8.28;
    
    import {IChainlinkAggregator} from './IChainlinkAggregator.sol';
    
    interface IChainlinkFeedRegistry {
      /// @notice Get decimals for a feed pair
      /// @param base The base asset of the feed
      /// @param quote The quote asset of the feed
      /// @return decimals The decimals of the feed pair
      function decimals(address base, address quote) external view returns (uint8);
    
      /// @notice Get description for a feed pair
      /// @param base The base asset of the feed
      /// @param quote The quote asset of the feed
      /// @return description The description of the feed pair
      function description(
        address base,
        address quote
      ) external view returns (string memory);
    
      /// @notice Get the latest answer for a feed pair
      /// @param base The base asset of the feed
      /// @param quote The quote asset of the feed
      /// @return answer The value sotred for the feed pair
      function latestAnswer(
        address base,
        address quote
      ) external view returns (int256 answer);
    
      /// @notice Get the latest round ID for a feed pair
      /// @param base The base asset of the feed
      /// @param quote The quote asset of the feed
      /// @return roundId The latest round ID
      function latestRound(
        address base,
        address quote
      ) external view returns (uint256 roundId);
    
      /// @notice Get the round data for a feed pair at a given round ID
      /// @param base The base asset of the feed
      /// @param quote The quote asset of the feed
      /// @param _roundId The round ID to retrieve data for
      /// @return roundId The round ID
      /// @return answer The value stored for the feed at the given round ID
      /// @return startedAt The timestamp when the value was stored
      /// @return updatedAt Same as startedAt
      /// @return answeredInRound Same as roundId
      function getRoundData(
        address base,
        address quote,
        uint80 _roundId
      )
        external
        view
        returns (
          uint80 roundId,
          int256 answer,
          uint256 startedAt,
          uint256 updatedAt,
          uint80 answeredInRound
        );
    
      /// @notice Get the latest round data for a feed pair
      /// @param base The base asset of the feed
      /// @param quote The quote asset of the feed
      /// @return roundId The latest round ID stored for the feed pair
      /// @return answer The latest value stored for the feed pair
      /// @return startedAt The timestamp when the value was stored
      /// @return updatedAt Same as startedAt
      /// @return answeredInRound Same as roundId
      function latestRoundData(
        address base,
        address quote
      )
        external
        view
        returns (
          uint80 roundId,
          int256 answer,
          uint256 startedAt,
          uint256 updatedAt,
          uint80 answeredInRound
        );
    
      /// @notice Get the CLAggregatorAdapter contract for a feed pair
      /// @param base The base asset of the feed
      /// @param quote The quote asset of the feed
      /// @return aggregator The CLAggregatorAdapter contract given pair
      function getFeed(
        address base,
        address quote
      ) external view returns (IChainlinkAggregator aggregator);
    }
    
  • contracts/libraries/CLAdapterLib.sol
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.28;
    
    //    ___  __         __                           _  __    __                  __
    //   / _ )/ /__  ____/ /__ ___ ___ ___  ___ ___   / |/ /__ / /__    _____  ____/ /__
    //  / _  / / _ \/ __/  '_/(_-</ -_) _ \(_-</ -_) /    / -_) __/ |/|/ / _ \/ __/  '_/
    // /____/_/\___/\__/_/\_\/___/\__/_//_/___/\__/ /_/|_/\__/\__/|__,__/\___/_/ /_/\_\
    //    _____   ____  ___    ___   ___  ________
    //   / __/ | / /  |/  /   / _ | / _ \/ __/ __/
    //  / _/ | |/ / /|_/ /   / __ |/ // / _/_\ \
    // /___/ |___/_/  /_/   /_/ |_/____/_/ /___/
    //
    // Website:         https://blocksense.network/
    // Git Repository:  https://github.com/blocksense-network/blocksense
    
    /// @title CLAdapterLib
    /// @author Aneta Tsvetkova
    /// @notice Library for calling dataFeedStore functions for Chainlink adapters
    /// @dev Contains utility functions for calling gas efficiently dataFeedStore functions and decoding return data
    library CLAdapterLib {
      /// @notice Gets the latest answer from the dataFeedStore
      /// @param dataFeedStore The address of the dataFeedStore contract
      /// @param id The ID of the feed
      /// @return answer The latest stored value after being decoded
      function latestAnswer(
        uint256 id,
        address dataFeedStore
      ) internal view returns (int256) {
        return
          int256(
            uint256(
              uint192(
                bytes24(
                  // 1st byte is function selector
                  // after that are 16 bytes of the feed id
                  _callDataFeed(dataFeedStore, (uint256(0x82) << 248) | id)
                )
              )
            )
          );
      }
    
      /// @notice Gets the round data from the dataFeedStore
      /// @param _roundId The round ID to retrieve data for
      /// @param id The ID of the feed
      /// @param dataFeedStore The address of the dataFeedStore contract
      /// @return roundId The round ID
      /// @return answer The value stored for the feed at the given round ID
      /// @return startedAt The timestamp when the value was stored
      /// @return updatedAt Same as startedAt
      /// @return answeredInRound Same as roundId
      function getRoundData(
        uint80 _roundId,
        uint256 id,
        address dataFeedStore
      )
        internal
        view
        returns (uint80, int256 answer, uint256 startedAt, uint256, uint80)
      {
        (answer, startedAt) = _decodeData(
          _callDataFeed(
            dataFeedStore,
            // 1st byte is function selector
            // after that are 16 bytes of the feed id
            // after the feed id are 2 bytes of the round id
            (uint256(0x86) << 248) | id | (uint256(_roundId) << 104)
          )
        );
        return (_roundId, answer, startedAt, startedAt, _roundId);
      }
    
      /// @notice Gets the latest round ID for a given feed from the dataFeedStore
      /// @param id The ID of the feed
      /// @param dataFeedStore The address of the dataFeedStore contract
      /// @return roundId The latest round ID
      function latestRound(
        uint256 id,
        address dataFeedStore
      ) internal view returns (uint256) {
        return
          uint256(
            // 1st byte is function selector
            // after that are 16 bytes of the feed id
            _callDataFeed(dataFeedStore, (uint256(0x81) << 248) | id)
          );
      }
    
      /// @notice Gets the latest round data for a given feed from the dataFeedStore
      /// @dev Using assembly achieves lower gas costs
      /// @param id The ID of the feed
      /// @param dataFeedStore The address of the dataFeedStore contract
      /// @return roundId The latest round ID
      /// @return answer The latest stored value after being decoded
      /// @return startedAt The timestamp when the value was stored
      /// @return updatedAt Same as startedAt
      /// @return answeredInRound Same as roundId
      function latestRoundData(
        uint256 id,
        address dataFeedStore
      )
        internal
        view
        returns (uint80 roundId, int256 answer, uint256 startedAt, uint256, uint80)
      {
        bytes32 returnData;
    
        // using assembly staticcall costs less gas than using a view function
        assembly {
          // get free memory pointer
          let ptr := mload(0x40)
    
          // store selector in memory at location 0
          // 1st byte is function selector
          // after that are 16 bytes of the feed id
          mstore(
            0x00,
            or(
              0x8300000000000000000000000000000000000000000000000000000000000000,
              id
            )
          )
    
          // call dataFeedStore with selector and store return value (64 bytes) at memory location ptr
          let success := staticcall(gas(), dataFeedStore, 0x00, 17, ptr, 64)
    
          // revert if call failed
          if iszero(success) {
            revert(0, 0)
          }
    
          // load return value from memory at location ptr
          // roundId is stored in the first 32 bytes of the returned 64 bytes
          roundId := mload(ptr)
    
          // value is stored in the second 32 bytes of the returned 64 bytes
          returnData := mload(add(ptr, 32))
        }
    
        (answer, startedAt) = _decodeData(returnData);
    
        return (roundId, answer, startedAt, startedAt, roundId);
      }
    
      /// @notice Calls the dataFeedStore with the given data
      /// @dev Using assembly achieves lower gas costs
      /// Used as a call() function to dataFeedStore
      /// @param dataFeedStore The address of the dataFeedStore contract
      /// @param selector The data to call the dataFeedStore with
      /// @return returnData The return value from the dataFeedStore
      function _callDataFeed(
        address dataFeedStore,
        uint256 selector
      ) internal view returns (bytes32 returnData) {
        // using assembly staticcall costs less gas than using a view function
        assembly {
          // store selector in memory at location 0
          mstore(0x00, selector)
    
          // call dataFeedStore with data and store return value (32 bytes) at memory location ptr
          let success := staticcall(
            gas(), // gas remaining
            dataFeedStore, // address to call
            0x00, // location of data to call
            19, // size of data to call - usually it is 17b but for _getRoundData it is 19b because of the 2 bytes of the roundId
            returnData, // where to store the return data
            32 // how much data to store
          )
    
          // revert if call failed
          if iszero(success) {
            revert(0, 0)
          }
    
          // assign loaded return value to returnData
          returnData := mload(returnData)
        }
      }
    
      /// @notice Decodes the return data from the dataFeedStore
      /// @param data The data to decode
      /// @return answer The value stored for the feed at the given round ID
      /// @return timestamp The timestamp when the value was stored
      function _decodeData(bytes32 data) internal pure returns (int256, uint256) {
        return (
          int256(uint256(uint192(bytes24(data)))),
          uint64(uint256(data)) / 1000 // timestamp is stored in milliseconds
        );
      }
    
      /// @notice Shifts the feed id to the left by 120 bits
      /// @dev This is used in the constructor to save gas when calling the dataFeedStore
      /// The dataFeedStore expects the feed id to be positioned in the calldata in a 16 bytes value starting from the 2nd byte
      /// @param id The feed id to shift
      function shiftId(uint256 id) internal pure returns (uint256) {
        return id << 120;
      }
    }
    
ABI · 11 entries
json
[
  {
    "type": "constructor",
    "inputs": [
      {
        "name": "_owner",
        "type": "address",
        "internalType": "address"
      },
      {
        "name": "_dataFeedStore",
        "type": "address",
        "internalType": "address"
      }
    ],
    "stateMutability": "nonpayable"
  },
  {
    "name": "OnlyOwner",
    "type": "error",
    "inputs": []
  },
  {
    "name": "OWNER",
    "type": "function",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "address",
        "internalType": "address"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "decimals",
    "type": "function",
    "inputs": [
      {
        "name": "base",
        "type": "address",
        "internalType": "address"
      },
      {
        "name": "quote",
        "type": "address",
        "internalType": "address"
      }
    ],
    "outputs": [
      {
        "name": "",
        "type": "uint8",
        "internalType": "uint8"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "description",
    "type": "function",
    "inputs": [
      {
        "name": "base",
        "type": "address",
        "internalType": "address"
      },
      {
        "name": "quote",
        "type": "address",
        "internalType": "address"
      }
    ],
    "outputs": [
      {
        "name": "",
        "type": "string",
        "internalType": "string"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "getFeed",
    "type": "function",
    "inputs": [
      {
        "name": "base",
        "type": "address",
        "internalType": "address"
      },
      {
        "name": "quote",
        "type": "address",
        "internalType": "address"
      }
    ],
    "outputs": [
      {
        "name": "",
        "type": "address",
        "internalType": "contract IChainlinkAggregator"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "getRoundData",
    "type": "function",
    "inputs": [
      {
        "name": "base",
        "type": "address",
        "internalType": "address"
      },
      {
        "name": "quote",
        "type": "address",
        "internalType": "address"
      },
      {
        "name": "_roundId",
        "type": "uint80",
        "internalType": "uint80"
      }
    ],
    "outputs": [
      {
        "name": "",
        "type": "uint80",
        "internalType": "uint80"
      },
      {
        "name": "",
        "type": "int256",
        "internalType": "int256"
      },
      {
        "name": "",
        "type": "uint256",
        "internalType": "uint256"
      },
      {
        "name": "",
        "type": "uint256",
        "internalType": "uint256"
      },
      {
        "name": "",
        "type": "uint80",
        "internalType": "uint80"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "latestAnswer",
    "type": "function",
    "inputs": [
      {
        "name": "base",
        "type": "address",
        "internalType": "address"
      },
      {
        "name": "quote",
        "type": "address",
        "internalType": "address"
      }
    ],
    "outputs": [
      {
        "name": "",
        "type": "int256",
        "internalType": "int256"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "latestRound",
    "type": "function",
    "inputs": [
      {
        "name": "base",
        "type": "address",
        "internalType": "address"
      },
      {
        "name": "quote",
        "type": "address",
        "internalType": "address"
      }
    ],
    "outputs": [
      {
        "name": "",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "latestRoundData",
    "type": "function",
    "inputs": [
      {
        "name": "base",
        "type": "address",
        "internalType": "address"
      },
      {
        "name": "quote",
        "type": "address",
        "internalType": "address"
      }
    ],
    "outputs": [
      {
        "name": "",
        "type": "uint80",
        "internalType": "uint80"
      },
      {
        "name": "",
        "type": "int256",
        "internalType": "int256"
      },
      {
        "name": "",
        "type": "uint256",
        "internalType": "uint256"
      },
      {
        "name": "",
        "type": "uint256",
        "internalType": "uint256"
      },
      {
        "name": "",
        "type": "uint80",
        "internalType": "uint80"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "setFeeds",
    "type": "function",
    "inputs": [
      {
        "name": "feeds",
        "type": "tuple[]",
        "components": [
          {
            "name": "base",
            "type": "address",
            "internalType": "address"
          },
          {
            "name": "quote",
            "type": "address",
            "internalType": "address"
          },
          {
            "name": "feed",
            "type": "address",
            "internalType": "address"
          }
        ],
        "internalType": "struct ICLFeedRegistryAdapter.Feed[]"
      }
    ],
    "outputs": [],
    "stateMutability": "nonpayable"
  }
]