{
  "address": "0x7ac886207F1fea709117b73B8c1e97c40f46ad9c",
  "abi": [
    {
      "inputs": [],
      "stateMutability": "nonpayable",
      "type": "constructor"
    },
    {
      "inputs": [],
      "name": "EmptyArray",
      "type": "error"
    },
    {
      "inputs": [],
      "name": "IndexOutOfBounds",
      "type": "error"
    },
    {
      "inputs": [],
      "name": "NoOraclesAvailable",
      "type": "error"
    },
    {
      "inputs": [],
      "name": "OnlyOwner",
      "type": "error"
    },
    {
      "anonymous": false,
      "inputs": [
        {
          "indexed": false,
          "internalType": "address",
          "name": "oracleAddress",
          "type": "address"
        },
        {
          "indexed": false,
          "internalType": "address",
          "name": "oracleOwner",
          "type": "address"
        }
      ],
      "name": "OracleAdded",
      "type": "event"
    },
    {
      "anonymous": false,
      "inputs": [
        {
          "indexed": false,
          "internalType": "address",
          "name": "oracleAddress",
          "type": "address"
        }
      ],
      "name": "OracleRemoved",
      "type": "event"
    },
    {
      "inputs": [],
      "name": "STALE_DATA_WINDOW",
      "outputs": [
        {
          "internalType": "uint256",
          "name": "",
          "type": "uint256"
        }
      ],
      "stateMutability": "view",
      "type": "function"
    },
    {
      "inputs": [
        {
          "internalType": "address",
          "name": "_owner",
          "type": "address"
        }
      ],
      "name": "addOracle",
      "outputs": [],
      "stateMutability": "nonpayable",
      "type": "function"
    },
    {
      "inputs": [],
      "name": "getActiveOracleNodes",
      "outputs": [
        {
          "internalType": "address[]",
          "name": "",
          "type": "address[]"
        }
      ],
      "stateMutability": "view",
      "type": "function"
    },
    {
      "inputs": [],
      "name": "getPrice",
      "outputs": [
        {
          "internalType": "uint256",
          "name": "",
          "type": "uint256"
        }
      ],
      "stateMutability": "view",
      "type": "function"
    },
    {
      "inputs": [
        {
          "internalType": "uint256",
          "name": "",
          "type": "uint256"
        }
      ],
      "name": "oracles",
      "outputs": [
        {
          "internalType": "contract SimpleOracle",
          "name": "",
          "type": "address"
        }
      ],
      "stateMutability": "view",
      "type": "function"
    },
    {
      "inputs": [],
      "name": "owner",
      "outputs": [
        {
          "internalType": "address",
          "name": "",
          "type": "address"
        }
      ],
      "stateMutability": "view",
      "type": "function"
    },
    {
      "inputs": [
        {
          "internalType": "uint256",
          "name": "index",
          "type": "uint256"
        }
      ],
      "name": "removeOracle",
      "outputs": [],
      "stateMutability": "nonpayable",
      "type": "function"
    }
  ],
  "transactionHash": "0xe7bf1985a4ac083403429b91d04d1af6150ae59448ce8596d5d250d548a9be41",
  "receipt": {
    "to": null,
    "from": "0x23E53302800Ccd8a5b35cFA576B6eB73dab297B4",
    "contractAddress": "0x7ac886207F1fea709117b73B8c1e97c40f46ad9c",
    "transactionIndex": 48,
    "gasUsed": "814772",
    "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "blockHash": "0x89d50c2a3b549d5c8f917f8992f3a50b9462aca2cc76f37840aef9d6012b291c",
    "transactionHash": "0xe7bf1985a4ac083403429b91d04d1af6150ae59448ce8596d5d250d548a9be41",
    "logs": [],
    "blockNumber": 10119858,
    "cumulativeGasUsed": "18978613",
    "status": 1,
    "byzantium": true
  },
  "args": [],
  "numDeployments": 1,
  "solcInputHash": "5ddadefd5b01c44dd0bbf987b1598ce3",
  "metadata": "{\"compiler\":{\"version\":\"0.8.20+commit.a1b79de6\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"EmptyArray\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"IndexOutOfBounds\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NoOraclesAvailable\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyOwner\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"oracleAddress\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"oracleOwner\",\"type\":\"address\"}],\"name\":\"OracleAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"oracleAddress\",\"type\":\"address\"}],\"name\":\"OracleRemoved\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"STALE_DATA_WINDOW\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_owner\",\"type\":\"address\"}],\"name\":\"addOracle\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getActiveOracleNodes\",\"outputs\":[{\"internalType\":\"address[]\",\"name\":\"\",\"type\":\"address[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getPrice\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"oracles\",\"outputs\":[{\"internalType\":\"contract SimpleOracle\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"index\",\"type\":\"uint256\"}],\"name\":\"removeOracle\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}],\"devdoc\":{\"kind\":\"dev\",\"methods\":{\"addOracle(address)\":{\"details\":\"Creates a new SimpleOracle instance and adds it to the oracles array.\",\"params\":{\"_owner\":\"The address that will own the newly created oracle and can update its price\"}},\"getActiveOracleNodes()\":{\"details\":\"Iterates through all oracles and filters those with recent timestamps (within STALE_DATA_WINDOW).      Uses a temporary array to collect active nodes, then creates a right-sized return array      for gas optimization.\",\"returns\":{\"_0\":\"An array of addresses representing the currently active oracle contracts\"}},\"getPrice()\":{\"details\":\"Filters oracles with timestamps older than STALE_DATA_WINDOW, then calculates median      of remaining valid prices. Uses StatisticsUtils for sorting and median calculation.\",\"returns\":{\"_0\":\"The median price from all active oracles\"}},\"removeOracle(uint256)\":{\"details\":\"Uses swap-and-pop pattern for gas-efficient removal. Order is not preserved.      Reverts with IndexOutOfBounds, if the provided index is >= oracles.length.\",\"params\":{\"index\":\"The index of the oracle to remove from the oracles array\"}}},\"version\":1},\"userdoc\":{\"errors\":{\"EmptyArray()\":[{\"notice\":\"Errors //////\"}],\"OnlyOwner()\":[{\"notice\":\"Errors //////\"}]},\"events\":{\"OracleAdded(address,address)\":{\"notice\":\"Events /////\"}},\"kind\":\"user\",\"methods\":{\"addOracle(address)\":{\"notice\":\"Adds a new oracle to the whitelist by deploying a SimpleOracle contract (only contract owner)\"},\"constructor\":{\"notice\":\"Constructor ///\"},\"getActiveOracleNodes()\":{\"notice\":\"Returns the addresses of all oracles that have updated their price within the last STALE_DATA_WINDOW\"},\"getPrice()\":{\"notice\":\"Returns the aggregated price from all active oracles using median calculation\"},\"owner()\":{\"notice\":\"State Variables //\"},\"removeOracle(uint256)\":{\"notice\":\"Removes an oracle from the whitelist by its array index (only contract owner)\"}},\"version\":1}},\"settings\":{\"compilationTarget\":{\"contracts/00_Whitelist/WhitelistOracle.sol\":\"WhitelistOracle\"},\"evmVersion\":\"paris\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\",\"useLiteralContent\":true},\"optimizer\":{\"enabled\":true,\"runs\":200},\"remappings\":[]},\"sources\":{\"contracts/00_Whitelist/SimpleOracle.sol\":{\"content\":\"//SPDX-License-Identifier: MIT\\npragma solidity >=0.8.0 <0.9.0;\\n\\ncontract SimpleOracle {\\n    /////////////////\\n    /// Errors //////\\n    /////////////////\\n\\n    error OnlyOwner();\\n\\n    //////////////////////\\n    /// State Variables //\\n    //////////////////////\\n\\n    uint256 public price;\\n    uint256 public timestamp;\\n    address public owner;\\n\\n    ////////////////\\n    /// Events /////\\n    ////////////////\\n\\n    event PriceUpdated(uint256 newPrice);\\n\\n    ///////////////////\\n    /// Constructor ///\\n    ///////////////////\\n\\n    constructor(address _owner) {\\n        owner = _owner;\\n    }\\n\\n    ///////////////////\\n    /// Modifiers /////\\n    ///////////////////\\n\\n    /**\\n     * @notice Modifier to restrict function access to the contract owner\\n     * @dev Currently disabled to make it easy for you to impersonate the owner\\n     */\\n    modifier onlyOwner() {\\n        // Intentionally removing the owner requirement to make it easy for you to impersonate the owner\\n        // if (msg.sender != owner) revert OnlyOwner();\\n        _;\\n    }\\n\\n    ///////////////////\\n    /// Functions /////\\n    ///////////////////\\n\\n    /**\\n     * @notice Updates the oracle price with a new value (only contract owner)\\n     * @dev Sets the price and records the current block timestamp for freshness tracking.\\n     *      Emits PriceUpdated event upon successful update.\\n     * @param _newPrice The new price value to set for this oracle\\n     */\\n    function setPrice(uint256 _newPrice) public onlyOwner {\\n        price = _newPrice;\\n        timestamp = block.timestamp;\\n        emit PriceUpdated(_newPrice);\\n    }\\n\\n    /**\\n     * @notice Returns the current price and its timestamp\\n     * @dev Provides both the stored price value and when it was last updated.\\n     *      Used by aggregators to determine price freshness.\\n     * @return price The current price stored in this oracle\\n     * @return timestamp The block timestamp when the price was last updated\\n     */\\n    function getPrice() public view returns (uint256, uint256) {\\n        return (price, timestamp);\\n    }\\n}\\n\",\"keccak256\":\"0xf7824e681abc476a008bc4aa06b41e0c9d54e3a84db98ae142dae10eb022275b\",\"license\":\"MIT\"},\"contracts/00_Whitelist/WhitelistOracle.sol\":{\"content\":\"//SPDX-License-Identifier: MIT\\npragma solidity >=0.8.0 <0.9.0;\\n\\nimport \\\"./SimpleOracle.sol\\\";\\nimport { StatisticsUtils } from \\\"../utils/StatisticsUtils.sol\\\";\\n\\ncontract WhitelistOracle {\\n    using StatisticsUtils for uint256[];\\n\\n    /////////////////\\n    /// Errors //////\\n    /////////////////\\n\\n    error OnlyOwner();\\n    error IndexOutOfBounds();\\n    error NoOraclesAvailable();\\n\\n    //////////////////////\\n    /// State Variables //\\n    //////////////////////\\n\\n    address public owner;\\n    SimpleOracle[] public oracles;\\n    uint256 public constant STALE_DATA_WINDOW = 24 seconds;\\n\\n    ////////////////\\n    /// Events /////\\n    ////////////////\\n\\n    event OracleAdded(address oracleAddress, address oracleOwner);\\n    event OracleRemoved(address oracleAddress);\\n\\n    ///////////////////\\n    /// Modifiers /////\\n    ///////////////////\\n\\n    /**\\n     * @notice Modifier to restrict function access to the contract owner\\n     * @dev Currently disabled to make it easy for you to impersonate the owner\\n     */\\n    modifier onlyOwner() {\\n        // if (msg.sender != owner) revert OnlyOwner();\\n        _;\\n    }\\n\\n    ///////////////////\\n    /// Constructor ///\\n    ///////////////////\\n\\n    constructor() {\\n        owner = msg.sender;\\n    }\\n\\n    ///////////////////\\n    /// Functions /////\\n    ///////////////////\\n\\n    /**\\n     * @notice Adds a new oracle to the whitelist by deploying a SimpleOracle contract (only contract owner)\\n     * @dev Creates a new SimpleOracle instance and adds it to the oracles array.\\n     * @param _owner The address that will own the newly created oracle and can update its price\\n     */\\n    function addOracle(address _owner) public onlyOwner {\\n      SimpleOracle newOracle = new SimpleOracle(_owner);\\n      oracles.push(newOracle);\\n      emit OracleAdded(address(newOracle), _owner);\\n    }\\n\\n    /**\\n     * @notice Removes an oracle from the whitelist by its array index (only contract owner)\\n     * @dev Uses swap-and-pop pattern for gas-efficient removal. Order is not preserved.\\n     *      Reverts with IndexOutOfBounds, if the provided index is >= oracles.length.\\n     * @param index The index of the oracle to remove from the oracles array\\n     */\\n    function removeOracle(uint256 index) public onlyOwner {\\n      uint256 oraclesSize = oracles.length;\\n      if (index >= oraclesSize) revert IndexOutOfBounds();\\n\\n      address deletedOracle = address(oracles[index]);\\n      if (index != oraclesSize - 1) {\\n        oracles[index] = oracles[oraclesSize - 1];\\n      }\\n      \\n      oracles.pop();\\n      emit OracleRemoved(deletedOracle);\\n    }\\n\\n    /**\\n     * @notice Returns the aggregated price from all active oracles using median calculation\\n     * @dev Filters oracles with timestamps older than STALE_DATA_WINDOW, then calculates median\\n     *      of remaining valid prices. Uses StatisticsUtils for sorting and median calculation.\\n     * @return The median price from all active oracles\\n     */\\n    function getPrice() public view returns (uint256) {\\n      if (oracles.length == 0) revert NoOraclesAvailable();\\n      uint256[] memory prices = new uint256[](oracles.length);\\n      uint256 priceIndex = 0;\\n      uint256 currentTime = block.timestamp;\\n\\n      for (uint256 i = 0; i < oracles.length; i++) {\\n        (uint256 price, uint256 timestamp) = oracles[i].getPrice();\\n\\tif (currentTime - timestamp < STALE_DATA_WINDOW) {\\n\\t  prices[priceIndex] = price;\\n\\t  priceIndex++;\\n\\t}\\n      }\\n\\n      uint256[] memory fixedPrices = new uint256[](priceIndex);\\n      for (uint256 i = 0; i < priceIndex; i++) {\\n        fixedPrices[i] = prices[i];\\n      }\\n\\n      fixedPrices.sort();\\n      return fixedPrices.getMedian();\\n    }\\n\\n    /**\\n     * @notice Returns the addresses of all oracles that have updated their price within the last STALE_DATA_WINDOW\\n     * @dev Iterates through all oracles and filters those with recent timestamps (within STALE_DATA_WINDOW).\\n     *      Uses a temporary array to collect active nodes, then creates a right-sized return array\\n     *      for gas optimization.\\n     * @return An array of addresses representing the currently active oracle contracts\\n     */\\n    function getActiveOracleNodes() public view returns (address[] memory) {\\n      address[] memory activeOracles = new address[](oracles.length);\\n      uint256 oracleIndex = 0;\\n      uint256 currentTime = block.timestamp;\\n\\n      for (uint256 i = 0; i < oracles.length; i++) {\\n        (, uint256 timestamp) = oracles[i].getPrice();\\n\\tif (currentTime - timestamp < STALE_DATA_WINDOW) {\\n\\t  activeOracles[oracleIndex] = address(oracles[i]);\\n\\t  oracleIndex++;\\n\\t}\\n      }\\n\\n      address[] memory fixedActiveOracles = new address[](oracleIndex);\\n      for (uint256 i = 0; i < oracleIndex; i++) {\\n        fixedActiveOracles[i] = activeOracles[i];\\n      }\\n\\n      return fixedActiveOracles;\\n    }\\n}\\n\",\"keccak256\":\"0xf2436dd59b378d3b1a9ec8033beda6fe3cbe21f6d3f5c93b0d982bebd874630e\",\"license\":\"MIT\"},\"contracts/utils/StatisticsUtils.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\npragma solidity >=0.8.0 <0.9.0;\\n\\nlibrary StatisticsUtils {\\n    /////////////////\\n    /// Errors //////\\n    /////////////////\\n\\n    error EmptyArray();\\n\\n    ///////////////////\\n    /// Functions /////\\n    ///////////////////\\n\\n    /**\\n     * @notice Sorts an array of uint256 values in ascending order using selection sort\\n     * @dev Uses selection sort algorithm which is not gas-efficient but acceptable for small arrays.\\n     *      This implementation mimics the early MakerDAO Medianizer exactly.\\n     *      Modifies the input array in-place.\\n     * @param arr The array of uint256 values to sort in ascending order\\n     */\\n    function sort(uint256[] memory arr) internal pure {\\n        uint256 n = arr.length;\\n        for (uint256 i = 0; i < n; i++) {\\n            uint256 minIndex = i;\\n            for (uint256 j = i + 1; j < n; j++) {\\n                if (arr[j] < arr[minIndex]) {\\n                    minIndex = j;\\n                }\\n            }\\n            if (minIndex != i) {\\n                (arr[i], arr[minIndex]) = (arr[minIndex], arr[i]);\\n            }\\n        }\\n    }\\n\\n    /**\\n     * @notice Calculates the median value from a sorted array of uint256 values\\n     * @dev For arrays with even length, returns the average of the two middle elements.\\n     *      For arrays with odd length, returns the middle element.\\n     *      Assumes the input array is already sorted in ascending order.\\n     * @param arr The sorted array of uint256 values to calculate median from\\n     * @return The median value as a uint256\\n     */\\n    function getMedian(uint256[] memory arr) internal pure returns (uint256) {\\n        uint256 length = arr.length;\\n        if (length == 0) revert EmptyArray();\\n        if (length % 2 == 0) {\\n            return (arr[length / 2 - 1] + arr[length / 2]) / 2;\\n        } else {\\n            return arr[length / 2];\\n        }\\n    }\\n}\\n\",\"keccak256\":\"0xc0949844270a074ee4289211c32b5e33062343fb5ae282c314a944e344e609cc\",\"license\":\"MIT\"}},\"version\":1}",
  "bytecode": "[encoded data, 6950 characters]",
  "deployedBytecode": "[encoded data, 6850 characters]",
  "devdoc": {
    "kind": "dev",
    "methods": {
      "addOracle(address)": {
        "details": "Creates a new SimpleOracle instance and adds it to the oracles array.",
        "params": {
          "_owner": "The address that will own the newly created oracle and can update its price"
        }
      },
      "getActiveOracleNodes()": {
        "details": "Iterates through all oracles and filters those with recent timestamps (within STALE_DATA_WINDOW).      Uses a temporary array to collect active nodes, then creates a right-sized return array      for gas optimization.",
        "returns": {
          "_0": "An array of addresses representing the currently active oracle contracts"
        }
      },
      "getPrice()": {
        "details": "Filters oracles with timestamps older than STALE_DATA_WINDOW, then calculates median      of remaining valid prices. Uses StatisticsUtils for sorting and median calculation.",
        "returns": {
          "_0": "The median price from all active oracles"
        }
      },
      "removeOracle(uint256)": {
        "details": "Uses swap-and-pop pattern for gas-efficient removal. Order is not preserved.      Reverts with IndexOutOfBounds, if the provided index is >= oracles.length.",
        "params": {
          "index": "The index of the oracle to remove from the oracles array"
        }
      }
    },
    "version": 1
  },
  "userdoc": {
    "errors": {
      "EmptyArray()": [
        {
          "notice": "Errors //////"
        }
      ],
      "OnlyOwner()": [
        {
          "notice": "Errors //////"
        }
      ]
    },
    "events": {
      "OracleAdded(address,address)": {
        "notice": "Events /////"
      }
    },
    "kind": "user",
    "methods": {
      "addOracle(address)": {
        "notice": "Adds a new oracle to the whitelist by deploying a SimpleOracle contract (only contract owner)"
      },
      "constructor": {
        "notice": "Constructor ///"
      },
      "getActiveOracleNodes()": {
        "notice": "Returns the addresses of all oracles that have updated their price within the last STALE_DATA_WINDOW"
      },
      "getPrice()": {
        "notice": "Returns the aggregated price from all active oracles using median calculation"
      },
      "owner()": {
        "notice": "State Variables //"
      },
      "removeOracle(uint256)": {
        "notice": "Removes an oracle from the whitelist by its array index (only contract owner)"
      }
    },
    "version": 1
  },
  "storageLayout": {
    "storage": [
      {
        "astId": 87,
        "contract": "contracts/00_Whitelist/WhitelistOracle.sol:WhitelistOracle",
        "label": "owner",
        "offset": 0,
        "slot": "0",
        "type": "t_address"
      },
      {
        "astId": 91,
        "contract": "contracts/00_Whitelist/WhitelistOracle.sol:WhitelistOracle",
        "label": "oracles",
        "offset": 0,
        "slot": "1",
        "type": "t_array(t_contract(SimpleOracle)68)dyn_storage"
      }
    ],
    "types": {
      "t_address": {
        "encoding": "inplace",
        "label": "address",
        "numberOfBytes": "20"
      },
      "t_array(t_contract(SimpleOracle)68)dyn_storage": {
        "base": "t_contract(SimpleOracle)68",
        "encoding": "dynamic_array",
        "label": "contract SimpleOracle[]",
        "numberOfBytes": "32"
      },
      "t_contract(SimpleOracle)68": {
        "encoding": "inplace",
        "label": "contract SimpleOracle",
        "numberOfBytes": "20"
      }
    }
  }
}