feat: finish up challenges
Some checks failed
Lint / ci (lts/*, ubuntu-latest) (push) Has been cancelled
Some checks failed
Lint / ci (lts/*, ubuntu-latest) (push) Has been cancelled
This commit is contained in:
1
packages/hardhat/deployments-backup/localhost/.chainId
Normal file
1
packages/hardhat/deployments-backup/localhost/.chainId
Normal file
@@ -0,0 +1 @@
|
||||
31337
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,42 @@
|
||||
{
|
||||
"language": "Solidity",
|
||||
"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"
|
||||
},
|
||||
"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"
|
||||
},
|
||||
"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"
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
"optimizer": {
|
||||
"enabled": true,
|
||||
"runs": 200
|
||||
},
|
||||
"evmVersion": "paris",
|
||||
"outputSelection": {
|
||||
"*": {
|
||||
"*": [
|
||||
"abi",
|
||||
"evm.bytecode",
|
||||
"evm.deployedBytecode",
|
||||
"evm.methodIdentifiers",
|
||||
"metadata",
|
||||
"devdoc",
|
||||
"userdoc",
|
||||
"storageLayout",
|
||||
"evm.gasEstimates"
|
||||
],
|
||||
"": [
|
||||
"ast"
|
||||
]
|
||||
}
|
||||
},
|
||||
"metadata": {
|
||||
"useLiteralContent": true
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user