Overview
S Balance
S Value
$0.00More Info
Private Name Tags
ContractCreator
Loading...
Loading
Similar Match Source Code This contract matches the deployed Bytecode of the Source Code for Contract 0x1daB6560...df3a51828 The constructor portion of the code might be different and could alter the actual behaviour of the contract
Contract Name:
ChainlinkEMA
Compiler Version
v0.8.25+commit.b61c2a91
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT pragma solidity 0.8.25; interface IChainlinkAggregator { function latestRoundData() external view returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound); function getRoundData( uint80 _roundId ) external view returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound); function decimals() external view returns (uint256); } interface IPriceOracle { /** @notice Returns the current oracle price, normalized to 1e18 precision @dev Called by all state-changing market / amm operations with the exception of `MainController.close_loan` */ function price_w() external returns (uint256); /** @notice Returns the current oracle price, normalized to 1e18 precision @dev Read-only version used within view methods. Should always return the same value as `price_w` */ function price() external view returns (uint256); } /** @title Chainlink EMA Oracle @author defidotmoney @notice Calculates an exponential moving average from a Chainlink feed @dev This contract is designed for use in L2/sidechain environments where gas costs are negligible. It is not recommended for use on Ethereum mainnet. */ contract ChainlinkEMA { IChainlinkAggregator public immutable chainlinkFeed; /// @notice The number of observations used in calculating the EMA. uint256 public immutable OBSERVATIONS; /// @notice The number of seconds between price observations when calculating the EMA. uint256 public immutable INTERVAL; /// @dev `2 / (OBSERVATIONS - 1)` stored with 1e18 precision uint256 public immutable SMOOTHING_FACTOR; uint256 private immutable MAX_LOOKBACK; uint256 private immutable PRECISION_MUL; uint256 public storedPrice; uint256 public storedObservationTimestamp; ChainlinkResponse public storedResponse; struct ChainlinkResponse { uint80 roundId; uint128 updatedAt; uint256 answer; // normalized to 1e18 } constructor(IChainlinkAggregator _chainlink, uint256 _observations, uint256 _interval) { chainlinkFeed = _chainlink; OBSERVATIONS = _observations; INTERVAL = _interval; SMOOTHING_FACTOR = 2e18 / (_observations + 1); MAX_LOOKBACK = _observations * 2; PRECISION_MUL = 10 ** (18 - _chainlink.decimals()); uint256 currentObservation = _getCurrentObservationTimestamp(); (storedPrice, storedResponse) = _calculateNewEMA(currentObservation); storedObservationTimestamp = currentObservation; } /** @notice Returns the current oracle price, normalized to 1e18 precision. @dev Read-only version used in view methods. Returns the same value as `price_w`. */ function price() external view returns (uint256 currentPrice) { uint256 currentObservation = _getCurrentObservationTimestamp(); uint256 storedObservation = storedObservationTimestamp; if (currentObservation == storedObservation) return storedPrice; if (storedObservation + MAX_LOOKBACK * INTERVAL > currentObservation) { (currentPrice, , ) = _calculateLatestEMA(currentObservation, storedObservation); } else { (currentPrice, ) = _calculateNewEMA(currentObservation); } return currentPrice; } /** @notice Returns the current oracle price, normalized to 1e18 precision. @dev It is preferred to call this method during on-chain interactions if possible. */ function price_w() external returns (uint256 currentPrice) { uint256 currentObservation = _getCurrentObservationTimestamp(); uint256 storedObservation = storedObservationTimestamp; if (currentObservation == storedObservation) return storedPrice; if (storedObservation + MAX_LOOKBACK * INTERVAL > currentObservation) { bool isNewResponse; ChainlinkResponse memory response; (currentPrice, response, isNewResponse) = _calculateLatestEMA(currentObservation, storedObservation); if (isNewResponse) storedResponse = response; } else { (currentPrice, storedResponse) = _calculateNewEMA(currentObservation); } storedObservationTimestamp = currentObservation; storedPrice = currentPrice; return currentPrice; } /** @dev Calculates the latest EMA price by performing observations at all observation intervals since the last stored one. Used when the number of new observations required is less than `2 * OBSERVATIONS`. */ function _calculateLatestEMA( uint256 currentObservation, uint256 storedObservation ) internal view returns (uint256 currentPrice, ChainlinkResponse memory latestResponse, bool isNewResponse) { currentPrice = storedPrice; latestResponse = _getLatestRoundData(); ChainlinkResponse memory response = storedResponse; // special case, latest round is the same as stored round if (latestResponse.roundId == response.roundId) { uint256 answer = response.answer; while (storedObservation < currentObservation) { storedObservation += INTERVAL; currentPrice = _getNextEMA(answer, currentPrice); } return (currentPrice, latestResponse, false); } bool isLatestResponse; ChainlinkResponse memory nextResponse; if (latestResponse.roundId > response.roundId + 1) { nextResponse = _getNextRoundData(response.roundId); } else { nextResponse = latestResponse; } while (storedObservation < currentObservation) { storedObservation += INTERVAL; while (!isLatestResponse && nextResponse.updatedAt < storedObservation) { response = nextResponse; if (nextResponse.roundId == latestResponse.roundId) { isLatestResponse = true; } else { nextResponse = _getNextRoundData(nextResponse.roundId); } } currentPrice = _getNextEMA(response.answer, currentPrice); } return (currentPrice, latestResponse, true); } /** @dev Calculates an EMA price without relying on the last stored observation. Used when the number of new observations required is at least `2 * OBSERVATIONS`. */ function _calculateNewEMA( uint256 observationTimestamp ) internal view returns (uint256 currentPrice, ChainlinkResponse memory latestResponse) { latestResponse = _getLatestRoundData(); ChainlinkResponse memory response = latestResponse; uint256[] memory oracleResponses = new uint256[](MAX_LOOKBACK); // in the following while loops, we manually decrement and then increment // idx so we know where the first non-zero value is within oracleResponses uint256 idx = MAX_LOOKBACK; // iterate backward to get oracle responses for each observation time while (true) { while (response.updatedAt >= observationTimestamp) { if (response.roundId & type(uint64).max == 1) { // first roundId for this aggregator, cannot look back further break; } response = _getRoundData(response.roundId - 1); } if (response.updatedAt >= observationTimestamp) { if (idx == MAX_LOOKBACK) { // edge case, if the first round is more recent than our latest // observation time we can only return the first round's response return (response.answer, latestResponse); } break; } idx--; oracleResponses[idx] = response.answer; if (idx == 0) break; observationTimestamp -= INTERVAL; } // now iterate forward to calculate EMA based on the observed oracle responses currentPrice = oracleResponses[idx]; idx++; while (idx < MAX_LOOKBACK) { currentPrice = _getNextEMA(oracleResponses[idx], currentPrice); idx++; } return (currentPrice, latestResponse); } /** @dev Given the latest price and the last EMA, returns the new EMA */ function _getNextEMA(uint256 newPrice, uint256 lastEMA) internal view returns (uint256) { return ((newPrice * SMOOTHING_FACTOR) + (lastEMA * (1e18 - SMOOTHING_FACTOR))) / 1e18; } /** @dev The timestamp of the latest oracle observation */ function _getCurrentObservationTimestamp() internal view returns (uint256) { return (block.timestamp / INTERVAL) * INTERVAL; } function _getLatestRoundData() internal view returns (ChainlinkResponse memory) { (uint80 roundId, int256 answer, , uint256 updatedAt, ) = chainlinkFeed.latestRoundData(); return _validateAndFormatResponse(roundId, answer, updatedAt); } function _getRoundData(uint80 roundId) internal view returns (ChainlinkResponse memory) { (uint80 roundId, int256 answer, , uint256 updatedAt, ) = chainlinkFeed.getRoundData(roundId); return _validateAndFormatResponse(roundId, answer, updatedAt); } /** @dev Given a `roundId`, gets the response data for the next round. This method is preferred over calling `_getRoundData(roundId + 1)` because it handles a case where the oracle phase has increased: https://docs.chain.link/data-feeds/historical-data#roundid-in-proxy */ function _getNextRoundData(uint80 roundId) internal view returns (ChainlinkResponse memory) { try chainlinkFeed.getRoundData(roundId + 1) returns (uint80 round, int answer, uint, uint updatedAt, uint80) { // depending on the direction the wind blows, an invalid roundId can revert or return zeros if (updatedAt > 0) return _validateAndFormatResponse(round, answer, updatedAt); } catch {} uint80 nextRoundId = (((roundId >> 64) + 1) << 64) + 1; return _getRoundData(nextRoundId); } function _validateAndFormatResponse( uint80 roundId, int256 answer, uint256 updatedAt ) internal view returns (ChainlinkResponse memory) { require(answer > 0, "DFM: Chainlink answer too low"); return ChainlinkResponse({ roundId: roundId, updatedAt: uint128(updatedAt), answer: uint256(answer) * PRECISION_MUL }); } }
{ "evmVersion": "istanbul", "optimizer": { "enabled": true, "runs": 200 }, "libraries": { "ChainlinkEMA.sol": {} }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } } }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[{"internalType":"contract IChainlinkAggregator","name":"_chainlink","type":"address"},{"internalType":"uint256","name":"_observations","type":"uint256"},{"internalType":"uint256","name":"_interval","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"INTERVAL","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"OBSERVATIONS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SMOOTHING_FACTOR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"chainlinkFeed","outputs":[{"internalType":"contract IChainlinkAggregator","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"price","outputs":[{"internalType":"uint256","name":"currentPrice","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"price_w","outputs":[{"internalType":"uint256","name":"currentPrice","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"storedObservationTimestamp","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"storedPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"storedResponse","outputs":[{"internalType":"uint80","name":"roundId","type":"uint80"},{"internalType":"uint128","name":"updatedAt","type":"uint128"},{"internalType":"uint256","name":"answer","type":"uint256"}],"stateMutability":"view","type":"function"}]
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106100935760003560e01c80639ed30128116100665780639ed3012814610171578063a035b1fe1461017a578063ae06785414610182578063b6e7aac4146101a9578063ceb7f759146101d057600080fd5b80637dbdf1f51461009857806380e63922146100dc57806389facb20146100f3578063901e9bd11461011a575b600080fd5b6100bf7f000000000000000000000000c76dfb89ff298145b417d221b2c747d84952e01d81565b6040516001600160a01b0390911681526020015b60405180910390f35b6100e560015481565b6040519081526020016100d3565b6100e57f000000000000000000000000000000000000000000000000000000000000001e81565b600254600354610144916001600160501b03811691600160501b9091046001600160801b03169083565b604080516001600160501b0390941684526001600160801b039092166020840152908201526060016100d3565b6100e560005481565b6100e56101d8565b6100e57f00000000000000000000000000000000000000000000000001525a8b03c0618681565b6100e57f000000000000000000000000000000000000000000000000000000000000001481565b6100e561027c565b6000806101e36103d2565b6001549091508082036101fa576000549250505090565b816102457f000000000000000000000000000000000000000000000000000000000000001e7f0000000000000000000000000000000000000000000000000000000000000028610c36565b61024f9083610c4d565b111561026a5761025f828261040e565b509093506102779050565b610273826105e8565b5092505b505090565b6000806102876103d2565b60015490915080820361029e576000549250505090565b816102e97f000000000000000000000000000000000000000000000000000000000000001e7f0000000000000000000000000000000000000000000000000000000000000028610c36565b6102f39083610c4d565b1115610378576040805160608101825260008082526020820181905291810182905261031f848461040e565b919650909250905081156103715780516002805460208401516001600160801b0316600160501b026001600160d01b03199091166001600160501b039093169290921791909117905560408101516003555b50506103c6565b610381826105e8565b80516002805460208401516001600160801b0316600160501b026001600160d01b03199091166001600160501b03909316929092179190911790556040015160035592505b50600155600081905590565b60007f000000000000000000000000000000000000000000000000000000000000001e6103ff8142610c60565b6104099190610c36565b905090565b60408051606081018252600080825260208201819052918101829052815491610435610823565b604080516060810182526002546001600160501b03808216808452600160501b9092046001600160801b031660208401526003549383019390935283519395509092909116036104d65760408101515b868610156104ca576104b77f000000000000000000000000000000000000000000000000000000000000001e87610c4d565b95506104c381866108e2565b9450610485565b50600091506105e19050565b6040805160608101825260008082526020820181905291810182905282516104ff906001610c82565b6001600160501b031685600001516001600160501b0316111561052e57825161052790610968565b9050610531565b50835b878710156105d9576105637f000000000000000000000000000000000000000000000000000000000000001e88610c4d565b96505b8115801561058057508681602001516001600160801b0316105b156105c45780925084600001516001600160501b031681600001516001600160501b0316036105b25760019150610566565b80516105bd90610968565b9050610566565b6105d28360400151876108e2565b9550610531565b506001925050505b9250925092565b6040805160608101825260008082526020820181905291810182905261060c610823565b90508060007f000000000000000000000000000000000000000000000000000000000000002867ffffffffffffffff81111561064a5761064a610ca9565b604051908082528060200260200182016040528015610673578160200160208202803683370190505b5090507f00000000000000000000000000000000000000000000000000000000000000285b8583602001516001600160801b0316106106dd57825167ffffffffffffffff166001146106dd5782516106d6906106d190600190610cbf565b610a84565b9250610698565b8583602001516001600160801b03161061072b577f0000000000000000000000000000000000000000000000000000000000000028810361072657505060400151939092509050565b610791565b8061073581610cdf565b915050826040015182828151811061074f5761074f610cf6565b602090810291909101015280156107915761078a7f000000000000000000000000000000000000000000000000000000000000001e87610d0c565b9550610698565b8181815181106107a3576107a3610cf6565b6020026020010151945080806107b890610d1f565b9150505b7f000000000000000000000000000000000000000000000000000000000000002881101561081b576108078282815181106107f9576107f9610cf6565b6020026020010151866108e2565b94508061081381610d1f565b9150506107bc565b505050915091565b604080516060810182526000808252602082018190529181019190915260008060007f000000000000000000000000c76dfb89ff298145b417d221b2c747d84952e01d6001600160a01b031663feaf968c6040518163ffffffff1660e01b815260040160a060405180830381865afa1580156108a3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108c79190610d54565b50935050925092506108da838383610b55565b935050505090565b6000670de0b6b3a76400006109177f00000000000000000000000000000000000000000000000001525a8b03c0618682610d0c565b6109219084610c36565b61094b7f00000000000000000000000000000000000000000000000001525a8b03c0618686610c36565b6109559190610c4d565b61095f9190610c60565b90505b92915050565b60408051606081018252600080825260208201819052918101919091526001600160a01b037f000000000000000000000000c76dfb89ff298145b417d221b2c747d84952e01d16639a6fc8f56109bf846001610c82565b6040516001600160e01b031960e084901b1681526001600160501b03909116600482015260240160a060405180830381865afa925050508015610a1f575060408051601f3d908101601f19168201909252610a1c91810190610d54565b60015b15610a46578115610a4057610a35858584610b55565b979650505050505050565b50505050505b60006040610a5b61ffff85831c166001610c82565b6001600160501b0316901b6001610a729190610c82565b9050610a7d81610a84565b9392505050565b6040805160608101825260008082526020820181905291810191909152604051639a6fc8f560e01b81526001600160501b0383166004820152600090819081906001600160a01b037f000000000000000000000000c76dfb89ff298145b417d221b2c747d84952e01d1690639a6fc8f59060240160a060405180830381865afa158015610b15573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b399190610d54565b5093505092509250610b4c838383610b55565b95945050505050565b60408051606081018252600080825260208201819052918101829052908313610bc45760405162461bcd60e51b815260206004820152601d60248201527f44464d3a20436861696e6c696e6b20616e7377657220746f6f206c6f77000000604482015260640160405180910390fd5b604080516060810182526001600160501b03861681526001600160801b0384166020820152908101610c167f00000000000000000000000000000000000000000000000000000002540be40086610c36565b9052949350505050565b634e487b7160e01b600052601160045260246000fd5b808202811582820484141761096257610962610c20565b8082018082111561096257610962610c20565b600082610c7d57634e487b7160e01b600052601260045260246000fd5b500490565b6001600160501b03818116838216019080821115610ca257610ca2610c20565b5092915050565b634e487b7160e01b600052604160045260246000fd5b6001600160501b03828116828216039080821115610ca257610ca2610c20565b600081610cee57610cee610c20565b506000190190565b634e487b7160e01b600052603260045260246000fd5b8181038181111561096257610962610c20565b600060018201610d3157610d31610c20565b5060010190565b80516001600160501b0381168114610d4f57600080fd5b919050565b600080600080600060a08688031215610d6c57600080fd5b610d7586610d38565b9450602086015193506040860151925060608601519150610d9860808701610d38565b9050929550929590935056fea26469706673582212203ec01c2730d64229b1b54a9d14f41d71f960ba9dccc6dd04ceab050008ed3e1964736f6c63430008190033
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 31 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.