Skip to content

Instantly share code, notes, and snippets.

@theishu4
Created December 26, 2024 16:01
Show Gist options
  • Save theishu4/7a2c942e0896b273ecf7b9329775a718 to your computer and use it in GitHub Desktop.
Save theishu4/7a2c942e0896b273ecf7b9329775a718 to your computer and use it in GitHub Desktop.
Created using remix-ide: Realtime Ethereum Contract Compiler and Runtime. Load this file by pasting this gists URL or ID at https://remix.ethereum.org/#version=soljson-v0.8.24+commit.e11b9ed9.js&optimize=false&runs=200&gist=
// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;
import {PriceConverter} from "../PriceConverter.sol";
// import {AggregatorV3Interface} from "@chainlink/contracts/src/v0.8/shared/interfaces/AggregatorV3Interface.sol";
error NotOwner();
error IndexOutOfBound();
error InsufficientBalanceToWithdraw();
/**
* Prefer using custom error types with `revert` instead of `require()` for error handling
* to save gas. Custom errors are more efficient because they don't store the error message
* as a string in the contract's bytecode, reducing gas costs.
* Example: if (msg.sender != owner) { revert NotOwner(); }
*/
contract FundMe {
// using custom library
using PriceConverter for uint;
/**
* `constant`: A constant variable is a value that is fixed at compile-time and cannot be changed.
* It is embedded directly into the contract’s bytecode, making it gas-efficient.
*
* `immutable`: An immutable variable is set once during contract deployment (usually in the constructor)
* and cannot be changed afterward. It is stored in contract storage, making it slightly more expensive
* than constant but still more efficient than regular state variables.
*/
address public immutable i_owner;
uint public constant MINIMUM_USD = 5e18; // 5 USD in wei
address[] private fundersAddressList;
mapping(address funderAddress => uint totalFunded) private fundsList;
mapping(address => bool) private hasFunded;
constructor() {
i_owner = msg.sender;
}
modifier onlyOwner() {
if (msg.sender != i_owner) {revert NotOwner();}
/** `_` is a special placeholder represents the point where the function’s
* body is inserted within the modifier.
*/
_;
}
modifier hasBalance() {
// require(address(this).balance > 0, "Insufficient contract balance for withdrawal!");
if (address(this).balance <= 0) {revert InsufficientBalanceToWithdraw();}
_;
}
/*
* `payable`: Allows a function to receive Ether.
*
* `msg`: It is a global object in Solidity that contains information about the current transaction.
*
*
* Important `msg` properties include:
* - `msg.value` : It is the amount of Ether (in wei) sent with the transaction.
* - `msg.sender`: The address of the sender who initiated the transaction.
* - `msg.data`: The calldata of the transaction (input data for the function call).
*/
function fund() public payable {
/*
* If the transaction fails, it is reverted.
* -> Reverting cancels all previous operations performed in the transaction
* and refunds the remaining gas back to the sender.
*
* `value` is passed as first argument to `convertToUSD()`
*/
require(msg.value.convertToUSD() >= MINIMUM_USD, "Amount is less than minimum USD");
if (!hasFunded[msg.sender]){
fundersAddressList.push(msg.sender);
hasFunded[msg.sender] = true;
}
fundsList[msg.sender] += msg.value;
}
// Function to transfer Ether using `transfer`
// Both modifier execute serial wise.
function withdrawUsingTransfer() public onlyOwner hasBalance {
/**
* Transfer:
* - Sends Ether to the recipient with a fixed gas limit of 2300 gas.
* - If the gas limit is exceeded or the transfer fails, the transaction reverts.
* - Note: Not recommended for sending Ether in complex scenarios.
*/
payable(msg.sender).transfer(address(this).balance);
resetFunders();
}
// Function to transfer Ether using `send`
function withdrawUsingSend() public onlyOwner hasBalance {
/**
* Send:
* - Sends Ether to the recipient with a fixed gas limit of 2300 gas.
* - Unlike `transfer`, it returns `false` on failure instead of reverting.
* - You must check the return value to handle errors.
* - Note: Consider using `call` instead for better flexibility.
*/
bool isSuccess = payable(msg.sender).send(address(this).balance);
require(isSuccess, "Send failed!");
resetFunders();
}
// Function to transfer Ether using `call` (recommended way)
function withdrawUsingCall() public onlyOwner hasBalance {
/**
* Call: Recommended
* - Sends Ether without a fixed gas limit, making it more flexible.
* - Returns a success flag (`bool`) and optional returned data (`bytes`).
* - Always check the success flag to handle errors properly.
*/
(bool isCallSuccess, ) = payable(msg.sender).call{value: address(this).balance}("");
require(isCallSuccess, "Call failed!");
resetFunders();
}
function resetFunders() internal {
for (uint i = 0; i < fundersAddressList.length; i++) {
address funder = fundersAddressList[i];
fundsList[funder] = 0;
hasFunded[funder] = false;
}
// fundersAddressList = new address[](0);
delete fundersAddressList;
}
function getBalance() public view returns (uint) {
return address(this).balance;
}
function getFunderAddress(uint index) public view returns (address) {
if(index >= fundersAddressList.length) {revert IndexOutOfBound();}
return fundersAddressList[index];
}
function getFunderContribution(address funderAddress) public view returns (uint) {
return fundsList[funderAddress];
}
/**
* TODO: Write about receive and fallback and also draw that tree chart
* TODO: Test it on the metamask
*/
receive() external payable {
fund();
}
fallback() external payable {
fund();
}
}
// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;
import {AggregatorV3Interface} from "@chainlink/contracts/src/v0.8/shared/interfaces/AggregatorV3Interface.sol";
library PriceConverter {
/**
* Library doesn't have state variable.
* Library can't send ether.
* Library function can only have visibility `internal`
*/
function currentPrice() internal view returns (uint) {
// Sepolia testnet: 0x694AA1769357215DE4FAC081bf1f309aDC325306
// ZKsync Sepolia testnet: 0xfEefF7c3fB57d18C5C6Cdd71e45D2D0b4F9377bF
// network: sepolia testnet
// Aggregator: ETH/USD
AggregatorV3Interface dataFeed = AggregatorV3Interface(0x694AA1769357215DE4FAC081bf1f309aDC325306);
(, int answer,,,) = dataFeed.latestRoundData();
// Making 8bit precision to 18bit
return uint(answer) * 1e10;
}
function convertToUSD(uint256 amountInETH) internal view returns (uint){
uint amountInUSD = (amountInETH * currentPrice()) / 1e18;
return amountInUSD;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment