Skip to content

Commit

Permalink
First Commit
Browse files Browse the repository at this point in the history
  • Loading branch information
ParsimonyMain committed Sep 3, 2024
1 parent 5520564 commit 172097f
Show file tree
Hide file tree
Showing 17 changed files with 558 additions and 58 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,6 @@ docs/

# Dotenv file
.env

broadcast/
lib/
6 changes: 6 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
[submodule "lib/forge-std"]
path = lib/forge-std
url = https://github.com/foundry-rs/forge-std
[submodule "lib/chainlink-brownie-contracts"]
path = lib/chainlink-brownie-contracts
url = https://github.com/smartcontractkit/chainlink-brownie-contracts
[submodule "lib/foundry-devops"]
path = lib/foundry-devops
url = https://github.com/Cyfrin/foundry-devops
6 changes: 6 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
-include .env

build:; forge build

deploy-sepolia:
forge script script/DeployFundMe.s.sol:DeployFundMe --rpc-url $(SEPOLIA_RPC_URL) --private-key $(PRIVATE_KEY) --broadcast --verify --etherscan-api-key $(ETHERSCAN_API_KEY) -vvvv
5 changes: 4 additions & 1 deletion foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,8 @@
src = "src"
out = "out"
libs = ["lib"]

remappings = [
"@chainlink/contracts/=lib/chainlink-brownie-contracts/contracts/",
]
ffi = true
# See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options
1 change: 1 addition & 0 deletions lib/chainlink-brownie-contracts
1 change: 1 addition & 0 deletions lib/foundry-devops
Submodule foundry-devops added at df9f90
19 changes: 0 additions & 19 deletions script/Counter.s.sol

This file was deleted.

18 changes: 18 additions & 0 deletions script/DeployFundMe.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {Script} from "forge-std/Script.sol";
import {FundMe} from "../src/FundMe.sol";
import {HelperConfig} from "./HelperConfig.s.sol";

contract DeployFundMe is Script {
function run() external returns (FundMe) {
HelperConfig helperConfig = new HelperConfig();
address ethUsdPriceFeed = helperConfig.activeNetworkConfig();

vm.startBroadcast();
FundMe fundMe = new FundMe(ethUsdPriceFeed);
vm.stopBroadcast();
return fundMe;
}
}
50 changes: 50 additions & 0 deletions script/HelperConfig.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.18;

import {Script} from "forge-std/Script.sol";
import {MockV3Aggregator} from "../test/mocks/MockV3Aggregator.sol";

contract HelperConfig is Script {
NetworkConfig public activeNetworkConfig;

uint8 public constant DECIMALS = 8;
int256 public constant INITIAL_PRICE = 2000e8;

struct NetworkConfig {
address priceFeed;
}

constructor() {
if (block.chainid == 11155111) {
activeNetworkConfig = getSepoliaEthConfig();
} else {
activeNetworkConfig = getOrCreateAnvilConfig();
}
}

function getSepoliaEthConfig() public pure returns (NetworkConfig memory) {
NetworkConfig memory sepoliaConfig = NetworkConfig({
priceFeed: 0x694AA1769357215DE4FAC081bf1f309aDC325306
});
return sepoliaConfig;
}

function getOrCreateAnvilConfig() public returns (NetworkConfig memory) {
if (activeNetworkConfig.priceFeed != address(0)) {
return activeNetworkConfig;
}

vm.startBroadcast();
MockV3Aggregator mockPriceFeed = new MockV3Aggregator(
DECIMALS,
INITIAL_PRICE
);
vm.stopBroadcast();

NetworkConfig memory anvilConfig = NetworkConfig({
priceFeed: address(mockPriceFeed)
});
return anvilConfig;
}
}
43 changes: 43 additions & 0 deletions script/Interactions.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.18;

import {Script, console} from "forge-std/Script.sol";
import {DevOpsTools} from "foundry-devops/src/DevOpsTools.sol";
import {FundMe} from "../src/FundMe.sol";

contract FundFundMe is Script {
uint256 constant SEND_VALUE = 0.01 ether;

function fundFundMe(address mostRecentlyDeployed) public {
vm.startBroadcast();
FundMe(payable(mostRecentlyDeployed)).fund{value: SEND_VALUE}();
vm.stopBroadcast();
console.log("Funded FundMe with %s", SEND_VALUE);
}

function run() external {
address mostRecentlyDeployed = DevOpsTools.get_most_recent_deployment(
"FundMe",
block.chainid
);
fundFundMe(mostRecentlyDeployed);
}
}

contract WithdrawFundMe is Script {
function withdrawFundMe(address mostRecentlyDeployed) public {
vm.startBroadcast();
FundMe(payable(mostRecentlyDeployed)).withdraw();
vm.stopBroadcast();
console.log("Withdrawed FundMe");
}

function run() external {
address mostRecentlyDeployed = DevOpsTools.get_most_recent_deployment(
"FundMe",
block.chainid
);
withdrawFundMe(mostRecentlyDeployed);
}
}
14 changes: 0 additions & 14 deletions src/Counter.sol

This file was deleted.

129 changes: 129 additions & 0 deletions src/FundMe.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;

// Note: The AggregatorV3Interface might be at a different location than what was in the video!
import {AggregatorV3Interface} from "@chainlink/contracts/src/v0.8/shared/interfaces/AggregatorV3Interface.sol";
import {PriceConverter} from "./PriceConverter.sol";

error FundMe__NotOwner();

contract FundMe {
using PriceConverter for uint256;

mapping(address => uint256) private s_addressToAmountFunded;
address[] private s_funders;

// Could we make this constant? /* hint: no! We should make it immutable! */
address private immutable i_owner;
uint256 public constant MINIMUM_USD = 5 * 10 ** 18;
AggregatorV3Interface private s_priceFeed;

constructor(address priceFeed) {
i_owner = msg.sender;
s_priceFeed = AggregatorV3Interface(priceFeed);
}

function fund() public payable {
require(
msg.value.getConversionRate(s_priceFeed) >= MINIMUM_USD,
"You need to spend more ETH!"
);
// require(PriceConverter.getConversionRate(msg.value) >= MINIMUM_USD, "You need to spend more ETH!");
s_addressToAmountFunded[msg.sender] += msg.value;
s_funders.push(msg.sender);
}

function getVersion() public view returns (uint256) {
return s_priceFeed.version();
}

modifier onlyOwner() {
// require(msg.sender == owner);
if (msg.sender != i_owner) revert FundMe__NotOwner();
_;
}

function cheaperWithdraw() public onlyOwner {
uint256 fundersLength = s_funders.length;
for (
uint256 funderIndex = 0;
funderIndex < fundersLength;
funderIndex++
) {
address funder = s_funders[funderIndex];
s_addressToAmountFunded[funder] = 0;
}
s_funders = new address[](0);
(bool callSuccess, ) = payable(msg.sender).call{
value: address(this).balance
}("");
require(callSuccess, "Call failed");
}

function withdraw() public onlyOwner {
for (
uint256 funderIndex = 0;
funderIndex < s_funders.length;
funderIndex++
) {
address funder = s_funders[funderIndex];
s_addressToAmountFunded[funder] = 0;
}
s_funders = new address[](0);
// // transfer
// payable(msg.sender).transfer(address(this).balance);

// // send
// bool sendSuccess = payable(msg.sender).send(address(this).balance);
// require(sendSuccess, "Send failed");

// call
(bool callSuccess, ) = payable(msg.sender).call{
value: address(this).balance
}("");
require(callSuccess, "Call failed");
}

// Explainer from: https://solidity-by-example.org/fallback/
// Ether is sent to contract
// is msg.data empty?
// / \
// yes no
// / \
// receive()? fallback()
// / \
// yes no
// / \
//receive() fallback()

fallback() external payable {
fund();
}

receive() external payable {
fund();
}

function getAddressToAmountFunded(
address fundingAddress
) external view returns (uint256) {
return s_addressToAmountFunded[fundingAddress];
}

function getFunder(uint256 index) external view returns (address) {
return s_funders[index];
}

function getOwner() external view returns (address) {
return i_owner;
}
}

// Concepts we didn't cover yet (will cover in later sections)
// 1. Enum
// 2. Events
// 3. Try / Catch
// 4. Function Selector
// 5. abi.encode / decode
// 6. Hash with keccak256
// 7. Yul / Assembly
30 changes: 30 additions & 0 deletions src/PriceConverter.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;

import {AggregatorV3Interface} from "@chainlink/contracts/src/v0.8/shared/interfaces/AggregatorV3Interface.sol";

// Why is this a library and not abstract?
// Why not an interface?
library PriceConverter {
// We could make this public, but then we'd have to deploy it
function getPrice(
AggregatorV3Interface priceFeed
) internal view returns (uint256) {
// Sepolia ETH / USD Address
// https://docs.chain.link/data-feeds/price-feeds/addresses
(, int256 answer, , , ) = priceFeed.latestRoundData();
// ETH/USD rate in 18 digit
return uint256(answer * 10000000000);
}

// 1000000000
function getConversionRate(
uint256 ethAmount,
AggregatorV3Interface priceFeed
) internal view returns (uint256) {
uint256 ethPrice = getPrice(priceFeed);
uint256 ethAmountInUsd = (ethPrice * ethAmount) / 1000000000000000000;
// the actual ETH/USD conversion rate, after adjusting the extra 0s.
return ethAmountInUsd;
}
}
24 changes: 0 additions & 24 deletions test/Counter.t.sol

This file was deleted.

Loading

0 comments on commit 172097f

Please sign in to comment.