Transaction Hash:
Block:
11870289 at Feb-16-2021 09:30:55 PM +UTC
Transaction Fee:
0.367729973 ETH
$883.20
Gas Used:
2,175,917 Gas / 169 Gwei
Account State Difference:
Address | Before | After | State Difference | ||
---|---|---|---|---|---|
0x3E7d1eAB...65364e32D | (Chainlink: USDT/USD Price Feed) |
0 Eth
Nonce: 0
|
0 Eth
Nonce: 1
| ||
0x536Ee634...66E9B8eC7 |
9.779934971970548526 Eth
Nonce: 710
|
9.412204998970548526 Eth
Nonce: 711
| 0.367729973 | ||
0xEA674fdD...16B898ec8
Miner
| (Ethermine) | 1,436.70317119779573647 Eth | 1,437.07090117079573647 Eth | 0.367729973 |
Execution Trace
EACAggregatorProxy.60806040( )
pragma solidity 0.6.6; /** * @title The Owned contract * @notice A contract with helpers for basic contract ownership. */ contract Owned { address public owner; address private pendingOwner; event OwnershipTransferRequested( address indexed from, address indexed to ); event OwnershipTransferred( address indexed from, address indexed to ); constructor() public { owner = msg.sender; } /** * @dev Allows an owner to begin transferring ownership to a new address, * pending. */ function transferOwnership(address _to) external onlyOwner() { pendingOwner = _to; emit OwnershipTransferRequested(owner, _to); } /** * @dev Allows an ownership transfer to be completed by the recipient. */ function acceptOwnership() external { require(msg.sender == pendingOwner, "Must be proposed owner"); address oldOwner = owner; owner = msg.sender; pendingOwner = address(0); emit OwnershipTransferred(oldOwner, msg.sender); } /** * @dev Reverts if called by anyone other than the contract owner. */ modifier onlyOwner() { require(msg.sender == owner, "Only callable by owner"); _; } } interface AggregatorInterface { function latestAnswer() external view returns (int256); function latestTimestamp() external view returns (uint256); function latestRound() external view returns (uint256); function getAnswer(uint256 roundId) external view returns (int256); function getTimestamp(uint256 roundId) external view returns (uint256); event AnswerUpdated(int256 indexed current, uint256 indexed roundId, uint256 updatedAt); event NewRound(uint256 indexed roundId, address indexed startedBy, uint256 startedAt); } interface AggregatorV3Interface { function decimals() external view returns (uint8); function description() external view returns (string memory); function version() external view returns (uint256); // getRoundData and latestRoundData should both raise "No data present" // if they do not have data to report, instead of returning unset values // which could be misinterpreted as actual reported values. 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 ); } interface AggregatorV2V3Interface is AggregatorInterface, AggregatorV3Interface { } /** * @title A trusted proxy for updating where current answers are read from * @notice This contract provides a consistent address for the * CurrentAnwerInterface but delegates where it reads from to the owner, who is * trusted to update it. */ contract AggregatorProxy is AggregatorV2V3Interface, Owned { struct Phase { uint16 id; AggregatorV2V3Interface aggregator; } Phase private currentPhase; AggregatorV2V3Interface public proposedAggregator; mapping(uint16 => AggregatorV2V3Interface) public phaseAggregators; uint256 constant private PHASE_OFFSET = 64; uint256 constant private PHASE_SIZE = 16; uint256 constant private MAX_ID = 2**(PHASE_OFFSET+PHASE_SIZE) - 1; constructor(address _aggregator) public Owned() { setAggregator(_aggregator); } /** * @notice Reads the current answer from aggregator delegated to. * * @dev #[deprecated] Use latestRoundData instead. This does not error if no * answer has been reached, it will simply return 0. Either wait to point to * an already answered Aggregator or use the recommended latestRoundData * instead which includes better verification information. */ function latestAnswer() public view virtual override returns (int256 answer) { return currentPhase.aggregator.latestAnswer(); } /** * @notice Reads the last updated height from aggregator delegated to. * * @dev #[deprecated] Use latestRoundData instead. This does not error if no * answer has been reached, it will simply return 0. Either wait to point to * an already answered Aggregator or use the recommended latestRoundData * instead which includes better verification information. */ function latestTimestamp() public view virtual override returns (uint256 updatedAt) { return currentPhase.aggregator.latestTimestamp(); } /** * @notice get past rounds answers * @param _roundId the answer number to retrieve the answer for * * @dev #[deprecated] Use getRoundData instead. This does not error if no * answer has been reached, it will simply return 0. Either wait to point to * an already answered Aggregator or use the recommended getRoundData * instead which includes better verification information. */ function getAnswer(uint256 _roundId) public view virtual override returns (int256 answer) { if (_roundId > MAX_ID) return 0; (uint16 phaseId, uint64 aggregatorRoundId) = parseIds(_roundId); AggregatorV2V3Interface aggregator = phaseAggregators[phaseId]; if (address(aggregator) == address(0)) return 0; return aggregator.getAnswer(aggregatorRoundId); } /** * @notice get block timestamp when an answer was last updated * @param _roundId the answer number to retrieve the updated timestamp for * * @dev #[deprecated] Use getRoundData instead. This does not error if no * answer has been reached, it will simply return 0. Either wait to point to * an already answered Aggregator or use the recommended getRoundData * instead which includes better verification information. */ function getTimestamp(uint256 _roundId) public view virtual override returns (uint256 updatedAt) { if (_roundId > MAX_ID) return 0; (uint16 phaseId, uint64 aggregatorRoundId) = parseIds(_roundId); AggregatorV2V3Interface aggregator = phaseAggregators[phaseId]; if (address(aggregator) == address(0)) return 0; return aggregator.getTimestamp(aggregatorRoundId); } /** * @notice get the latest completed round where the answer was updated. This * ID includes the proxy's phase, to make sure round IDs increase even when * switching to a newly deployed aggregator. * * @dev #[deprecated] Use latestRoundData instead. This does not error if no * answer has been reached, it will simply return 0. Either wait to point to * an already answered Aggregator or use the recommended latestRoundData * instead which includes better verification information. */ function latestRound() public view virtual override returns (uint256 roundId) { Phase memory phase = currentPhase; // cache storage reads return addPhase(phase.id, uint64(phase.aggregator.latestRound())); } /** * @notice get data about a round. Consumers are encouraged to check * that they're receiving fresh data by inspecting the updatedAt and * answeredInRound return values. * Note that different underlying implementations of AggregatorV3Interface * have slightly different semantics for some of the return values. Consumers * should determine what implementations they expect to receive * data from and validate that they can properly handle return data from all * of them. * @param _roundId the requested round ID as presented through the proxy, this * is made up of the aggregator's round ID with the phase ID encoded in the * two highest order bytes * @return roundId is the round ID from the aggregator for which the data was * retrieved combined with an phase to ensure that round IDs get larger as * time moves forward. * @return answer is the answer for the given round * @return startedAt is the timestamp when the round was started. * (Only some AggregatorV3Interface implementations return meaningful values) * @return updatedAt is the timestamp when the round last was updated (i.e. * answer was last computed) * @return answeredInRound is the round ID of the round in which the answer * was computed. * (Only some AggregatorV3Interface implementations return meaningful values) * @dev Note that answer and updatedAt may change between queries. */ function getRoundData(uint80 _roundId) public view virtual override returns ( uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound ) { (uint16 phaseId, uint64 aggregatorRoundId) = parseIds(_roundId); ( uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 ansIn ) = phaseAggregators[phaseId].getRoundData(aggregatorRoundId); return addPhaseIds(roundId, answer, startedAt, updatedAt, ansIn, phaseId); } /** * @notice get data about the latest round. Consumers are encouraged to check * that they're receiving fresh data by inspecting the updatedAt and * answeredInRound return values. * Note that different underlying implementations of AggregatorV3Interface * have slightly different semantics for some of the return values. Consumers * should determine what implementations they expect to receive * data from and validate that they can properly handle return data from all * of them. * @return roundId is the round ID from the aggregator for which the data was * retrieved combined with an phase to ensure that round IDs get larger as * time moves forward. * @return answer is the answer for the given round * @return startedAt is the timestamp when the round was started. * (Only some AggregatorV3Interface implementations return meaningful values) * @return updatedAt is the timestamp when the round last was updated (i.e. * answer was last computed) * @return answeredInRound is the round ID of the round in which the answer * was computed. * (Only some AggregatorV3Interface implementations return meaningful values) * @dev Note that answer and updatedAt may change between queries. */ function latestRoundData() public view virtual override returns ( uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound ) { Phase memory current = currentPhase; // cache storage reads ( uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 ansIn ) = current.aggregator.latestRoundData(); return addPhaseIds(roundId, answer, startedAt, updatedAt, ansIn, current.id); } /** * @notice Used if an aggregator contract has been proposed. * @param _roundId the round ID to retrieve the round data for * @return roundId is the round ID for which data was retrieved * @return answer is the answer for the given round * @return startedAt is the timestamp when the round was started. * (Only some AggregatorV3Interface implementations return meaningful values) * @return updatedAt is the timestamp when the round last was updated (i.e. * answer was last computed) * @return answeredInRound is the round ID of the round in which the answer * was computed. */ function proposedGetRoundData(uint80 _roundId) public view virtual hasProposal() returns ( uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound ) { return proposedAggregator.getRoundData(_roundId); } /** * @notice Used if an aggregator contract has been proposed. * @return roundId is the round ID for which data was retrieved * @return answer is the answer for the given round * @return startedAt is the timestamp when the round was started. * (Only some AggregatorV3Interface implementations return meaningful values) * @return updatedAt is the timestamp when the round last was updated (i.e. * answer was last computed) * @return answeredInRound is the round ID of the round in which the answer * was computed. */ function proposedLatestRoundData() public view virtual hasProposal() returns ( uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound ) { return proposedAggregator.latestRoundData(); } /** * @notice returns the current phase's aggregator address. */ function aggregator() external view returns (address) { return address(currentPhase.aggregator); } /** * @notice returns the current phase's ID. */ function phaseId() external view returns (uint16) { return currentPhase.id; } /** * @notice represents the number of decimals the aggregator responses represent. */ function decimals() external view override returns (uint8) { return currentPhase.aggregator.decimals(); } /** * @notice the version number representing the type of aggregator the proxy * points to. */ function version() external view override returns (uint256) { return currentPhase.aggregator.version(); } /** * @notice returns the description of the aggregator the proxy points to. */ function description() external view override returns (string memory) { return currentPhase.aggregator.description(); } /** * @notice Allows the owner to propose a new address for the aggregator * @param _aggregator The new address for the aggregator contract */ function proposeAggregator(address _aggregator) external onlyOwner() { proposedAggregator = AggregatorV2V3Interface(_aggregator); } /** * @notice Allows the owner to confirm and change the address * to the proposed aggregator * @dev Reverts if the given address doesn't match what was previously * proposed * @param _aggregator The new address for the aggregator contract */ function confirmAggregator(address _aggregator) external onlyOwner() { require(_aggregator == address(proposedAggregator), "Invalid proposed aggregator"); delete proposedAggregator; setAggregator(_aggregator); } /* * Internal */ function setAggregator(address _aggregator) internal { uint16 id = currentPhase.id + 1; currentPhase = Phase(id, AggregatorV2V3Interface(_aggregator)); phaseAggregators[id] = AggregatorV2V3Interface(_aggregator); } function addPhase( uint16 _phase, uint64 _originalId ) internal view returns (uint80) { return uint80(uint256(_phase) << PHASE_OFFSET | _originalId); } function parseIds( uint256 _roundId ) internal view returns (uint16, uint64) { uint16 phaseId = uint16(_roundId >> PHASE_OFFSET); uint64 aggregatorRoundId = uint64(_roundId); return (phaseId, aggregatorRoundId); } function addPhaseIds( uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound, uint16 phaseId ) internal view returns (uint80, int256, uint256, uint256, uint80) { return ( addPhase(phaseId, uint64(roundId)), answer, startedAt, updatedAt, addPhase(phaseId, uint64(answeredInRound)) ); } /* * Modifiers */ modifier hasProposal() { require(address(proposedAggregator) != address(0), "No proposed aggregator present"); _; } } interface AccessControllerInterface { function hasAccess(address user, bytes calldata data) external view returns (bool); } /** * @title External Access Controlled Aggregator Proxy * @notice A trusted proxy for updating where current answers are read from * @notice This contract provides a consistent address for the * Aggregator and AggregatorV3Interface but delegates where it reads from to the owner, who is * trusted to update it. * @notice Only access enabled addresses are allowed to access getters for * aggregated answers and round information. */ contract EACAggregatorProxy is AggregatorProxy { AccessControllerInterface public accessController; constructor( address _aggregator, address _accessController ) public AggregatorProxy(_aggregator) { setController(_accessController); } /** * @notice Allows the owner to update the accessController contract address. * @param _accessController The new address for the accessController contract */ function setController(address _accessController) public onlyOwner() { accessController = AccessControllerInterface(_accessController); } /** * @notice Reads the current answer from aggregator delegated to. * @dev overridden function to add the checkAccess() modifier * * @dev #[deprecated] Use latestRoundData instead. This does not error if no * answer has been reached, it will simply return 0. Either wait to point to * an already answered Aggregator or use the recommended latestRoundData * instead which includes better verification information. */ function latestAnswer() public view override checkAccess() returns (int256) { return super.latestAnswer(); } /** * @notice get the latest completed round where the answer was updated. This * ID includes the proxy's phase, to make sure round IDs increase even when * switching to a newly deployed aggregator. * * @dev #[deprecated] Use latestRoundData instead. This does not error if no * answer has been reached, it will simply return 0. Either wait to point to * an already answered Aggregator or use the recommended latestRoundData * instead which includes better verification information. */ function latestTimestamp() public view override checkAccess() returns (uint256) { return super.latestTimestamp(); } /** * @notice get past rounds answers * @param _roundId the answer number to retrieve the answer for * @dev overridden function to add the checkAccess() modifier * * @dev #[deprecated] Use getRoundData instead. This does not error if no * answer has been reached, it will simply return 0. Either wait to point to * an already answered Aggregator or use the recommended getRoundData * instead which includes better verification information. */ function getAnswer(uint256 _roundId) public view override checkAccess() returns (int256) { return super.getAnswer(_roundId); } /** * @notice get block timestamp when an answer was last updated * @param _roundId the answer number to retrieve the updated timestamp for * @dev overridden function to add the checkAccess() modifier * * @dev #[deprecated] Use getRoundData instead. This does not error if no * answer has been reached, it will simply return 0. Either wait to point to * an already answered Aggregator or use the recommended getRoundData * instead which includes better verification information. */ function getTimestamp(uint256 _roundId) public view override checkAccess() returns (uint256) { return super.getTimestamp(_roundId); } /** * @notice get the latest completed round where the answer was updated * @dev overridden function to add the checkAccess() modifier * * @dev #[deprecated] Use latestRoundData instead. This does not error if no * answer has been reached, it will simply return 0. Either wait to point to * an already answered Aggregator or use the recommended latestRoundData * instead which includes better verification information. */ function latestRound() public view override checkAccess() returns (uint256) { return super.latestRound(); } /** * @notice get data about a round. Consumers are encouraged to check * that they're receiving fresh data by inspecting the updatedAt and * answeredInRound return values. * Note that different underlying implementations of AggregatorV3Interface * have slightly different semantics for some of the return values. Consumers * should determine what implementations they expect to receive * data from and validate that they can properly handle return data from all * of them. * @param _roundId the round ID to retrieve the round data for * @return roundId is the round ID from the aggregator for which the data was * retrieved combined with a phase to ensure that round IDs get larger as * time moves forward. * @return answer is the answer for the given round * @return startedAt is the timestamp when the round was started. * (Only some AggregatorV3Interface implementations return meaningful values) * @return updatedAt is the timestamp when the round last was updated (i.e. * answer was last computed) * @return answeredInRound is the round ID of the round in which the answer * was computed. * (Only some AggregatorV3Interface implementations return meaningful values) * @dev Note that answer and updatedAt may change between queries. */ function getRoundData(uint80 _roundId) public view checkAccess() override returns ( uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound ) { return super.getRoundData(_roundId); } /** * @notice get data about the latest round. Consumers are encouraged to check * that they're receiving fresh data by inspecting the updatedAt and * answeredInRound return values. * Note that different underlying implementations of AggregatorV3Interface * have slightly different semantics for some of the return values. Consumers * should determine what implementations they expect to receive * data from and validate that they can properly handle return data from all * of them. * @return roundId is the round ID from the aggregator for which the data was * retrieved combined with a phase to ensure that round IDs get larger as * time moves forward. * @return answer is the answer for the given round * @return startedAt is the timestamp when the round was started. * (Only some AggregatorV3Interface implementations return meaningful values) * @return updatedAt is the timestamp when the round last was updated (i.e. * answer was last computed) * @return answeredInRound is the round ID of the round in which the answer * was computed. * (Only some AggregatorV3Interface implementations return meaningful values) * @dev Note that answer and updatedAt may change between queries. */ function latestRoundData() public view checkAccess() override returns ( uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound ) { return super.latestRoundData(); } /** * @notice Used if an aggregator contract has been proposed. * @param _roundId the round ID to retrieve the round data for * @return roundId is the round ID for which data was retrieved * @return answer is the answer for the given round * @return startedAt is the timestamp when the round was started. * (Only some AggregatorV3Interface implementations return meaningful values) * @return updatedAt is the timestamp when the round last was updated (i.e. * answer was last computed) * @return answeredInRound is the round ID of the round in which the answer * was computed. */ function proposedGetRoundData(uint80 _roundId) public view checkAccess() hasProposal() override returns ( uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound ) { return super.proposedGetRoundData(_roundId); } /** * @notice Used if an aggregator contract has been proposed. * @return roundId is the round ID for which data was retrieved * @return answer is the answer for the given round * @return startedAt is the timestamp when the round was started. * (Only some AggregatorV3Interface implementations return meaningful values) * @return updatedAt is the timestamp when the round last was updated (i.e. * answer was last computed) * @return answeredInRound is the round ID of the round in which the answer * was computed. */ function proposedLatestRoundData() public view checkAccess() hasProposal() override returns ( uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound ) { return super.proposedLatestRoundData(); } /** * @dev reverts if the caller does not have access by the accessController * contract or is the contract itself. */ modifier checkAccess() { AccessControllerInterface ac = accessController; require(address(ac) == address(0) || ac.hasAccess(msg.sender, msg.data), "No access"); _; } }