Overview
ETH Balance
0 ETH
Eth Value
$0.00More Info
Private Name Tags
ContractCreator
View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
Similar Match Source Code This contract matches the deployed Bytecode of the Source Code for Contract 0x2EB56aA6...7281580bD The constructor portion of the code might be different and could alter the actual behaviour of the contract
Contract Name:
FxChainlinkTwapOracle
Compiler Version
v0.7.6+commit.7338295f
Optimization Enabled:
Yes with 200 runs
Other Settings:
istanbul EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT pragma solidity ^0.7.6; import "@openzeppelin/contracts/math/SafeMath.sol"; import { AggregatorV3Interface } from "../../price-oracle/interfaces/AggregatorV3Interface.sol"; import { ITwapOracle } from "../../price-oracle/interfaces/ITwapOracle.sol"; // solhint-disable not-rely-on-time // solhint-disable reason-string contract FxChainlinkTwapOracle is ITwapOracle { using SafeMath for uint256; uint256 private constant MAX_ITERATION = 500; /// @notice The twap duration. uint256 public immutable epoch; /// @notice Chainlink aggregator used as the data source. address public immutable chainlinkAggregator; /// @notice Minimum number of Chainlink rounds required in an epoch. uint256 public immutable chainlinkMinMessageCount; /// @notice Maximum gap between an epoch start and a previous Chainlink round for the round /// to be used in TWAP calculation. /// @dev This should at least be equal to Chainlink's heartbeat duration. uint256 public immutable chainlinkMessageExpiration; /// @dev A multiplier that normalizes price from the Chainlink aggregator to 18 decimal places. uint256 private immutable _chainlinkPriceMultiplier; string public symbol; constructor( uint256 epoch_, address chainlinkAggregator_, uint256 chainlinkMinMessageCount_, uint256 chainlinkMessageExpiration_, string memory symbol_ ) { require(chainlinkMinMessageCount_ > 0); epoch = epoch_; chainlinkAggregator = chainlinkAggregator_; chainlinkMinMessageCount = chainlinkMinMessageCount_; chainlinkMessageExpiration = chainlinkMessageExpiration_; uint256 decimal = AggregatorV3Interface(chainlinkAggregator_).decimals(); _chainlinkPriceMultiplier = 10**(uint256(18).sub(decimal)); symbol = symbol_; } /// @inheritdoc ITwapOracle function getLatest() external view override returns (uint256) { (, int256 answer, , uint256 updatedAt, ) = AggregatorV3Interface(chainlinkAggregator).latestRoundData(); require(updatedAt >= block.timestamp - chainlinkMessageExpiration, "Stale price oracle"); return uint256(answer).mul(_chainlinkPriceMultiplier); } /// @inheritdoc ITwapOracle function getTwap(uint256 timestamp) external view override returns (uint256) { return _getTwapFromChainlink(timestamp); } /// @notice Search for the last round before the given timestamp. Zeros are returned /// if the search fails. function findLastRoundBefore(uint256 timestamp) public view returns ( uint80 roundID, int256 answer, uint256 updatedAt ) { (roundID, answer, , updatedAt, ) = AggregatorV3Interface(chainlinkAggregator).latestRoundData(); if (updatedAt < timestamp + epoch) { // Fast path: sequentially check each round when the target epoch is in the near past. for (uint256 i = 0; i < MAX_ITERATION && updatedAt >= timestamp && answer != 0; i++) { roundID--; (, answer, , updatedAt, ) = _getChainlinkRoundData(roundID); } } else { // Slow path: binary search. During the search, the `roundID` round is always updated // at or after the given timestamp, and the `leftRoundID` round is either invalid or // updated before the given timestamp. uint80 step = 1; uint80 leftRoundID = 0; while (step <= roundID) { leftRoundID = roundID - step; (, answer, , updatedAt, ) = _getChainlinkRoundData(leftRoundID); if (updatedAt < timestamp || answer == 0) { break; } step <<= 1; roundID = leftRoundID; } while (leftRoundID + 1 < roundID) { uint80 midRoundID = (leftRoundID + roundID) / 2; (, answer, , updatedAt, ) = _getChainlinkRoundData(midRoundID); if (updatedAt < timestamp || answer == 0) { leftRoundID = midRoundID; } else { roundID = midRoundID; } } roundID = leftRoundID; (, answer, , updatedAt, ) = _getChainlinkRoundData(roundID); } if (updatedAt >= timestamp || answer == 0) { // The last round before the epoch end is not found, due to either discontinuous // round IDs caused by a phase change or abnormal `updatedAt` timestamps. return (0, 0, 0); } } /// @dev Calculate TWAP of the given epoch from the Chainlink oracle. /// @param timestamp End timestamp of the epoch to be updated /// @return TWAP of the epoch calculated from Chainlink, or zero if there's no sufficient data function _getTwapFromChainlink(uint256 timestamp) private view returns (uint256) { require(block.timestamp >= timestamp, "Too soon"); (uint80 roundID, int256 answer, uint256 updatedAt) = findLastRoundBefore(timestamp); if (answer == 0) { return 0; } uint256 sum = 0; uint256 sumTimestamp = timestamp; uint256 messageCount = 1; for (uint256 i = 0; i < MAX_ITERATION && updatedAt >= timestamp - epoch; i++) { sum = sum.add(uint256(answer).mul(sumTimestamp - updatedAt)); sumTimestamp = updatedAt; if (roundID == 0) { break; } roundID--; (, int256 newAnswer, , uint256 newUpdatedAt, ) = _getChainlinkRoundData(roundID); if (newAnswer == 0 || newUpdatedAt > updatedAt || newUpdatedAt < timestamp - epoch - chainlinkMessageExpiration) { break; // Stop if the previous round is invalid } answer = newAnswer; updatedAt = newUpdatedAt; messageCount++; } if (messageCount >= chainlinkMinMessageCount) { // the only update is expired. if (messageCount == 1 && updatedAt < timestamp - chainlinkMessageExpiration) return 0; sum = sum.add(uint256(answer).mul(sumTimestamp - (timestamp - epoch))); return sum.mul(_chainlinkPriceMultiplier) / epoch; } else { return 0; } } /// @dev Call `chainlinkAggregator.getRoundData(roundID)`. Return zero if the call reverts. function _getChainlinkRoundData(uint80 roundID) private view returns ( uint80, int256, uint256, uint256, uint80 ) { (bool success, bytes memory returnData) = chainlinkAggregator.staticcall( abi.encodePacked(AggregatorV3Interface.getRoundData.selector, abi.encode(roundID)) ); if (success) { return abi.decode(returnData, (uint80, int256, uint256, uint256, uint80)); } else { return (roundID, 0, 0, 0, roundID); } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.7.0; /** * @dev Wrappers over Solidity's arithmetic operations with added overflow * checks. * * Arithmetic operations in Solidity wrap on overflow. This can easily result * in bugs, because programmers usually assume that an overflow raises an * error, which is the standard behavior in high level programming languages. * `SafeMath` restores this intuition by reverting the transaction when an * operation overflows. * * Using this library instead of the unchecked operations eliminates an entire * class of bugs, so it's recommended to use it always. */ library SafeMath { /** * @dev Returns the addition of two unsigned integers, with an overflow flag. * * _Available since v3.4._ */ function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) { uint256 c = a + b; if (c < a) return (false, 0); return (true, c); } /** * @dev Returns the substraction of two unsigned integers, with an overflow flag. * * _Available since v3.4._ */ function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) { if (b > a) return (false, 0); return (true, a - b); } /** * @dev Returns the multiplication of two unsigned integers, with an overflow flag. * * _Available since v3.4._ */ function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) { // Gas optimization: this is cheaper than requiring 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 if (a == 0) return (true, 0); uint256 c = a * b; if (c / a != b) return (false, 0); return (true, c); } /** * @dev Returns the division of two unsigned integers, with a division by zero flag. * * _Available since v3.4._ */ function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) { if (b == 0) return (false, 0); return (true, a / b); } /** * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag. * * _Available since v3.4._ */ function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) { if (b == 0) return (false, 0); return (true, a % b); } /** * @dev Returns the addition of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `+` operator. * * Requirements: * * - Addition cannot overflow. */ function add(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a + b; require(c >= a, "SafeMath: addition overflow"); return c; } /** * @dev Returns the subtraction of two unsigned integers, reverting on * overflow (when the result is negative). * * Counterpart to Solidity's `-` operator. * * Requirements: * * - Subtraction cannot overflow. */ function sub(uint256 a, uint256 b) internal pure returns (uint256) { require(b <= a, "SafeMath: subtraction overflow"); return a - b; } /** * @dev Returns the multiplication of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `*` operator. * * Requirements: * * - Multiplication cannot overflow. */ function mul(uint256 a, uint256 b) internal pure returns (uint256) { if (a == 0) return 0; uint256 c = a * b; require(c / a == b, "SafeMath: multiplication overflow"); return c; } /** * @dev Returns the integer division of two unsigned integers, reverting on * division by zero. The result is rounded towards zero. * * Counterpart to Solidity's `/` operator. Note: this function uses a * `revert` opcode (which leaves remaining gas untouched) while Solidity * uses an invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function div(uint256 a, uint256 b) internal pure returns (uint256) { require(b > 0, "SafeMath: division by zero"); return a / b; } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * reverting when dividing by zero. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function mod(uint256 a, uint256 b) internal pure returns (uint256) { require(b > 0, "SafeMath: modulo by zero"); return a % b; } /** * @dev Returns the subtraction of two unsigned integers, reverting with custom message on * overflow (when the result is negative). * * CAUTION: This function is deprecated because it requires allocating memory for the error * message unnecessarily. For custom revert reasons use {trySub}. * * Counterpart to Solidity's `-` operator. * * Requirements: * * - Subtraction cannot overflow. */ function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b <= a, errorMessage); return a - b; } /** * @dev Returns the integer division of two unsigned integers, reverting with custom message on * division by zero. The result is rounded towards zero. * * CAUTION: This function is deprecated because it requires allocating memory for the error * message unnecessarily. For custom revert reasons use {tryDiv}. * * Counterpart to Solidity's `/` operator. Note: this function uses a * `revert` opcode (which leaves remaining gas untouched) while Solidity * uses an invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b > 0, errorMessage); return a / b; } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * reverting with custom message when dividing by zero. * * CAUTION: This function is deprecated because it requires allocating memory for the error * message unnecessarily. For custom revert reasons use {tryMod}. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b > 0, errorMessage); return a % b; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.7.0 || ^0.8.0; interface AggregatorV3Interface { function decimals() external view returns (uint8); function description() external view returns (string memory); function version() external view returns (uint256); function latestAnswer() 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 ); }
// SPDX-License-Identifier: MIT pragma solidity ^0.7.0 || ^0.8.0; interface ITwapOracle { /// @notice Return TWAP with 18 decimal places in the epoch ending at the specified timestamp. /// Zero is returned if TWAP in the epoch is not available. /// @param timestamp End Timestamp in seconds of the epoch /// @return TWAP (18 decimal places) in the epoch, or zero if not available function getTwap(uint256 timestamp) external view returns (uint256); /// @notice Return the latest price with 18 decimal places. function getLatest() external view returns (uint256); }
{ "optimizer": { "enabled": true, "runs": 200 }, "evmVersion": "istanbul", "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[{"internalType":"uint256","name":"epoch_","type":"uint256"},{"internalType":"address","name":"chainlinkAggregator_","type":"address"},{"internalType":"uint256","name":"chainlinkMinMessageCount_","type":"uint256"},{"internalType":"uint256","name":"chainlinkMessageExpiration_","type":"uint256"},{"internalType":"string","name":"symbol_","type":"string"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"chainlinkAggregator","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"chainlinkMessageExpiration","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"chainlinkMinMessageCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"epoch","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"findLastRoundBefore","outputs":[{"internalType":"uint80","name":"roundID","type":"uint80"},{"internalType":"int256","name":"answer","type":"int256"},{"internalType":"uint256","name":"updatedAt","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getLatest","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"getTwap","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"}]
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106100885760003560e01c806392ce710f1161005b57806392ce710f146100db57806395d89b41146101205780639f0571511461019d578063c36af460146101ba57610088565b80632ab4dd191461008d5780635004d36c146100a75780636a077536146100cb578063900cf0cf146100d3575b600080fd5b6100956101c2565b60408051918252519081900360200190f35b6100af6101e6565b604080516001600160a01b039092168252519081900360200190f35b61009561020a565b61009561022e565b6100f8600480360360208110156100f157600080fd5b5035610252565b604080516001600160501b039094168452602084019290925282820152519081900360600190f35b610128610463565b6040805160208082528351818301528351919283929083019185019080838360005b8381101561016257818101518382015260200161014a565b50505050905090810190601f16801561018f5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b610095600480360360208110156101b357600080fd5b50356104f1565b610095610504565b7f000000000000000000000000000000000000000000000000000000000001fa4081565b7f0000000000000000000000008751f736e94f6cd167e8c5b97e245680fbd9cc3681565b7f000000000000000000000000000000000000000000000000000000000000000181565b7f000000000000000000000000000000000000000000000000000000000000070881565b60008060007f0000000000000000000000008751f736e94f6cd167e8c5b97e245680fbd9cc366001600160a01b031663feaf968c6040518163ffffffff1660e01b815260040160a06040518083038186803b1580156102b057600080fd5b505afa1580156102c4573d6000803e3d6000fd5b505050506040513d60a08110156102da57600080fd5b508051602082015160609092015190945090925090507f000000000000000000000000000000000000000000000000000000000000070884018110156103655760005b6101f48110801561032e5750848210155b801561033957508215155b1561035f576000199093019261034e8461069c565b50919550909350505060010161031d565b50610440565b600160005b846001600160501b0316826001600160501b0316116103c757508084036103908161069c565b509196509094505050858310806103a5575083155b156103af576103c7565b935060011b6a01fffffffffffffffffffe168361036a565b846001600160501b0316816001016001600160501b031610156104285760028582016001600160501b0316046103fc8161069c565b50919750909550505086841080610411575084155b1561041e57809150610422565b8095505b506103c7565b8094506104348561069c565b50919650909450505050505b838110158061044d575081155b1561045c575060009150819050805b9193909250565b6000805460408051602060026001851615610100026000190190941693909304601f810184900484028201840190925281815292918301828280156104e95780601f106104be576101008083540402835291602001916104e9565b820191906000526020600020905b8154815290600101906020018083116104cc57829003601f168201915b505050505081565b60006104fc82610877565b90505b919050565b60008060007f0000000000000000000000008751f736e94f6cd167e8c5b97e245680fbd9cc366001600160a01b031663feaf968c6040518163ffffffff1660e01b815260040160a06040518083038186803b15801561056257600080fd5b505afa158015610576573d6000803e3d6000fd5b505050506040513d60a081101561058c57600080fd5b50602081015160609091015190925090507f000000000000000000000000000000000000000000000000000000000001fa404203811015610609576040805162461bcd60e51b81526020600482015260126024820152715374616c65207072696365206f7261636c6560701b604482015290519081900360640190fd5b610633827f00000000000000000000000000000000000000000000000000000002540be400610af8565b9250505090565b600082821115610691576040805162461bcd60e51b815260206004820152601e60248201527f536166654d6174683a207375627472616374696f6e206f766572666c6f770000604482015290519081900360640190fd5b508082035b92915050565b60008060008060008060007f0000000000000000000000008751f736e94f6cd167e8c5b97e245680fbd9cc366001600160a01b0316639a6fc8f560e01b8960405160200180826001600160501b0316815260200191505060405160208183030381529060405260405160200180836001600160e01b031916815260040182805190602001908083835b602083106107445780518252601f199092019160209182019101610725565b6001836020036101000a038019825116818451168082178552505050505050905001925050506040516020818303038152906040526040518082805190602001908083835b602083106107a85780518252601f199092019160209182019101610789565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d8060008114610808576040519150601f19603f3d011682016040523d82523d6000602084013e61080d565b606091505b5091509150811561085a578080602001905160a081101561082d57600080fd5b508051602082015160408301516060840151608090940151929a509098509650909450925061086e915050565b8760008060008b9650965096509650965050505b91939590929450565b6000814210156108b9576040805162461bcd60e51b81526020600482015260086024820152672a37b79039b7b7b760c11b604482015290519081900360640190fd5b60008060006108c785610252565b92509250925081600014156108e257600093505050506104ff565b6000856001825b6101f48110801561091c57507f000000000000000000000000000000000000000000000000000000000000070889038510155b156109e25761093761093087878603610af8565b8590610b58565b93508492506001600160501b03871661094f576109e2565b600019909601956000806109628961069c565b50935050925050816000148061097757508681115b806109c557507f000000000000000000000000000000000000000000000000000000000001fa407f00000000000000000000000000000000000000000000000000000000000007088c030381105b156109d15750506109e2565b9096509450600191820191016108e9565b507f00000000000000000000000000000000000000000000000000000000000000018110610ae957806001148015610a3b57507f000000000000000000000000000000000000000000000000000000000001fa40880384105b15610a4f57600096505050505050506104ff565b610a87610a80867f00000000000000000000000000000000000000000000000000000000000007088b038503610af8565b8490610b58565b92507f0000000000000000000000000000000000000000000000000000000000000708610ad4847f00000000000000000000000000000000000000000000000000000002540be400610af8565b81610adb57fe5b0496505050505050506104ff565b600096505050505050506104ff565b600082610b0757506000610696565b82820282848281610b1457fe5b0414610b515760405162461bcd60e51b8152600401808060200182810382526021815260200180610bb36021913960400191505060405180910390fd5b9392505050565b600082820183811015610b51576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fdfe536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f77a264697066735822122068889fb82b49525a79f2e89a8be1eff8c5efed3e32d94fa5bc27dbde3296f13c64736f6c63430007060033
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 31 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
Loading...
Loading
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.