diff --git a/script/HelperConfig.s.sol b/script/HelperConfig.s.sol index ed14c16..087a507 100644 --- a/script/HelperConfig.s.sol +++ b/script/HelperConfig.s.sol @@ -6,12 +6,15 @@ pragma solidity ^0.8.28; import {Script} from "forge-std/Script.sol"; +import {MockV3Aggregator} from "../test/mocks/MockV3Aggregator.sol"; -contract HelperConfig { +contract HelperConfig is Script { // If we are on a local anvil, we deploy mocks // Otherwise, grab the existing address from the live network NetworkConfig public activeNetworkConfig; + uint8 public constant DECIMALS = 8; + int256 public constant INITIAL_PRICE = 2000e8; struct NetworkConfig { address priceFeed; @@ -23,7 +26,7 @@ contract HelperConfig { } else if (block.chainid == 1) { activeNetworkConfig = getMainnetEthConfig(); } else { - activeNetworkConfig = getAnvilEthConfig(); + activeNetworkConfig = getOrCreateAnvilEthConfig(); } } @@ -42,7 +45,23 @@ contract HelperConfig { return mainnetConfig; } - function getAnvilEthConfig() public pure returns (NetworkConfig memory) { + function getOrCreateAnvilEthConfig() public returns (NetworkConfig memory) { + if (activeNetworkConfig.priceFeed != address(0)) { + return activeNetworkConfig; + } // price feed address + // 1. Deploy the mocks + // 2. Return the mock address + vm.startBroadcast(); + MockV3Aggregator mockPriceFeed = new MockV3Aggregator( + DECIMALS, + INITIAL_PRICE + ); + vm.stopBroadcast(); + + NetworkConfig memory anvilConfig = NetworkConfig({ + priceFeed: address(mockPriceFeed) + }); + return anvilConfig; } } diff --git a/test/mocks/MockV3Aggregator.sol b/test/mocks/MockV3Aggregator.sol new file mode 100644 index 0000000..97dad52 --- /dev/null +++ b/test/mocks/MockV3Aggregator.sol @@ -0,0 +1,99 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {AggregatorV3Interface} from "@chainlink/contracts/src/v0.8/shared/interfaces/AggregatorV3Interface.sol"; + +/** + * @title MockV3Aggregator + * @notice Based on the FluxAggregator contract + * @notice Use this contract when you need to test + * other contract's ability to read data from an + * aggregator contract, but how the aggregator got + * its answer is unimportant + */ +contract MockV3Aggregator is AggregatorV3Interface { + uint256 public constant version = 4; + + uint8 public decimals; + int256 public latestAnswer; + uint256 public latestTimestamp; + uint256 public latestRound; + + mapping(uint256 => int256) public getAnswer; + mapping(uint256 => uint256) public getTimestamp; + mapping(uint256 => uint256) private getStartedAt; + + constructor(uint8 _decimals, int256 _initialAnswer) { + decimals = _decimals; + updateAnswer(_initialAnswer); + } + + function updateAnswer(int256 _answer) public { + latestAnswer = _answer; + latestTimestamp = block.timestamp; + latestRound++; + getAnswer[latestRound] = _answer; + getTimestamp[latestRound] = block.timestamp; + getStartedAt[latestRound] = block.timestamp; + } + + function updateRoundData( + uint80 _roundId, + int256 _answer, + uint256 _timestamp, + uint256 _startedAt + ) public { + latestRound = _roundId; + latestAnswer = _answer; + latestTimestamp = _timestamp; + getAnswer[latestRound] = _answer; + getTimestamp[latestRound] = _timestamp; + getStartedAt[latestRound] = _startedAt; + } + + function getRoundData( + uint80 _roundId + ) + external + view + returns ( + uint80 roundId, + int256 answer, + uint256 startedAt, + uint256 updatedAt, + uint80 answeredInRound + ) + { + return ( + _roundId, + getAnswer[_roundId], + getStartedAt[_roundId], + getTimestamp[_roundId], + _roundId + ); + } + + function latestRoundData() + external + view + returns ( + uint80 roundId, + int256 answer, + uint256 startedAt, + uint256 updatedAt, + uint80 answeredInRound + ) + { + return ( + uint80(latestRound), + getAnswer[latestRound], + getStartedAt[latestRound], + getTimestamp[latestRound], + uint80(latestRound) + ); + } + + function description() external pure returns (string memory) { + return "v0.6/test/mock/MockV3Aggregator.sol"; + } +}