Created
October 8, 2024 05:37
-
-
Save yousuf-hossain-shanto/769bff44f6d0c3f0a10bc767737b9f26 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=true&runs=200&gist=
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // SPDX-License-Identifier: UNLICENSED | |
| pragma solidity ^0.8.24; | |
| import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol"; | |
| import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; | |
| import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; | |
| import "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; | |
| import "@openzeppelin/contracts/access/Ownable.sol"; | |
| /// @custom:security-contact yousuf.hossain.shanto@gmail.com | |
| contract BlocICO is ReentrancyGuard, Ownable { | |
| using SafeERC20 for IERC20Metadata; | |
| uint256 public constant totalPresaleAllocation = 5_000_000 * 10**18; // 5 million tokens (assuming 18 decimals on BLOC) | |
| uint256 public soldTokens = 0; // 18 decimals points | |
| AggregatorV3Interface public constant EthToUsdFeed = AggregatorV3Interface(0x694AA1769357215DE4FAC081bf1f309aDC325306); // Sepolia Testnet | |
| IERC20Metadata public constant BLOC = IERC20Metadata(0x8835958755971c8febd3E367dC4A5CB6A92a3dd8); // Sepolia Testnet | |
| IERC20Metadata public constant USDC = IERC20Metadata(0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238); // Sepolia Testnet USDC | |
| uint256 public constant blocPriceInUSDC = 0.02 * 10**18; // 0.02 USDC per BLOC. converting to 18 decimals | |
| event TokensPurchased(address indexed buyer, uint256 amount, uint256 priceInUSDC, uint256 priceInETH, string paymentMethod); | |
| constructor() Ownable(msg.sender) {} | |
| // Buy tokens using ETH (ETH is sent as msg.value). msg.value must be in 18 decimals | |
| function buyWithETH() external payable nonReentrant { | |
| uint256 ethAmount = msg.value; | |
| // Calculate how many BLOC tokens the user will get based on current ETH price | |
| (uint256 tokensToReceive, uint256 needToReturn) = getTokensAmount(ethAmount, true); | |
| require(tokensToReceive > 0, "Invalid ETH amount"); | |
| if (needToReturn > 0) { | |
| (bool success, ) = msg.sender.call{value: needToReturn}(""); | |
| require(success, "ETH refund failed"); | |
| } | |
| _processPurchase(msg.sender, tokensToReceive, (ethAmount-needToReturn), true); | |
| } | |
| // Buy tokens using USDC. usdcAmount must be in 18 decimals | |
| function buyWithUSDC(uint256 usdcAmount) external nonReentrant { | |
| (uint256 tokensToReceive, uint256 needToReturn) = getTokensAmount(usdcAmount, false); | |
| require(tokensToReceive > 0, "Invalid USDC amount"); | |
| USDC.safeTransferFrom(msg.sender, address(this), ((usdcAmount-needToReturn) / 10**18) * 10**6); // as USDC maintains 6 decimals | |
| _processPurchase(msg.sender, tokensToReceive, (usdcAmount-needToReturn), false); | |
| } | |
| // Calculate token amount based on ETH/USDC sent. providedAmount must be in 18 decimals | |
| function getTokensAmount(uint256 providedAmount, bool isETH) public view returns (uint256, uint256) { | |
| uint256 remaining = totalPresaleAllocation - soldTokens; | |
| uint256 blocUnitPrice; // 18 decimals points | |
| if (isETH) { | |
| blocUnitPrice = getBlocPriceInEth(blocPriceInUSDC); // Calculate ETH price dynamically | |
| } else { | |
| blocUnitPrice = blocPriceInUSDC; | |
| } | |
| uint256 tokensToReceive = (providedAmount / blocUnitPrice) * 10**18; // convert 18 decimals to tokens | |
| tokensToReceive = tokensToReceive > remaining ? remaining : tokensToReceive; | |
| uint256 needToReturn = providedAmount - ((tokensToReceive / (10**18)) * blocUnitPrice); | |
| return (tokensToReceive, needToReturn); | |
| } | |
| // Fetch the latest price of ETH in USD from Chainlink | |
| function getEthPrice() public view returns (uint256) { | |
| (, int256 price, , , ) = EthToUsdFeed.latestRoundData(); // ETH/USD price from Chainlink | |
| return uint256(price * 10**10); // Convert price to 18 decimals (Chainlink provides 8 decimals) | |
| } | |
| // Calculate ETH equivalent price for BLOC tokens | |
| function getBlocPriceInEth(uint256 usdcPrice) public view returns (uint256) { | |
| uint256 ethPriceInUsd = getEthPrice(); // Fetch the current ETH price in USD | |
| // return ((usdcPrice/(10**18))/(ethPriceInUsd/(10**18))) * 10**18; // Convert USDC price to ETH wei | |
| return (usdcPrice * 10**18) / ethPriceInUsd; | |
| } | |
| // Process the purchase and transfer tokens | |
| function _processPurchase(address buyer, uint256 tokenAmount, uint256 paidAmount, bool isETH) internal { | |
| require(tokenAmount > 0, "Invalid token amount"); | |
| require(soldTokens + tokenAmount <= totalPresaleAllocation, "Exceeds presale allocation"); | |
| // Transfer BLOC tokens to the buyer | |
| BLOC.safeTransfer(buyer, tokenAmount); | |
| soldTokens += tokenAmount; | |
| // Emit event | |
| emit TokensPurchased(buyer, tokenAmount, isETH ? 0 : paidAmount, isETH ? paidAmount : 0, isETH ? "ETH" : "USDC"); | |
| } | |
| // Withdraw funds (ETH and USDC). amount must be 18 decimals | |
| function withdrawFunds(uint256 amount, bool isEth) external onlyOwner nonReentrant { | |
| if (isEth) { | |
| payable(owner()).transfer(amount); // Withdraw ETH | |
| } else { | |
| USDC.safeTransfer(owner(), (amount / 10**18) * 10**6); | |
| } | |
| } | |
| function withdrawBloc(uint256 amount) external onlyOwner nonReentrant { | |
| BLOC.safeTransfer(owner(), amount); | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment