How do I create a smart contract that let buyer buy token by seding BNB directly to Presale Contract

I'm trying to create a token where buyTokens function will automatically receive the ETH and send back equivalent to the sender wallet address. for example:

  1. Token Price: 1 eth = 1000 Token
  2. Buyer will send 2 eth and receive 2000 Tokens
  3. User cannot buy less than 100 and more than 1000 ETH

I have tried this, but is not even deploying to the Blockchain:

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

interface IERC20 {
    function transfer(address recipient, uint256 amount) external returns (bool);
    function balanceOf(address account) external view returns (uint256);
}

contract Presale {
    address payable public owner;
    IERC20 public token;
    uint256 public exchangeRate;
    uint256 public presaleCap;
    uint256 public totalTokensSold;
    mapping(address => uint256) public purchasedTokens;
    
    event TokenPurchased(address indexed buyer, uint256 value, uint256 tokens);
    
    constructor(address _tokenAddress, uint256 _exchangeRate, uint256 _presaleCap) {
        owner = payable(msg.sender);
        token = IERC20(_tokenAddress);
        exchangeRate = _exchangeRate;
        presaleCap = _presaleCap;
    }
    
    function buyTokens() external payable {

        require(msg.value >= 100 && msg.value <= 1000, "Amount sent is not within range");
        
        uint256 tokensToPurchase = msg.value * exchangeRate;
        require(totalTokensSold + tokensToPurchase <= presaleCap, "Presale cap reached");
        require(token.balanceOf(address(this)) >= tokensToPurchase, "Not enough tokens in contract");
        
        purchasedTokens[msg.sender] += tokensToPurchase;
        totalTokensSold += tokensToPurchase;
        
        token.transfer(msg.sender, tokensToPurchase);
        emit TokenPurchased(msg.sender, msg.value, tokensToPurchase);
    }
    
    function withdrawETH() external {
        require(msg.sender == owner, "Only owner can withdraw ETH");
        
        uint256 balance = address(this).balance;
        require(balance > 0, "No ETH to withdraw");
        
        owner.transfer(balance);
    }
    
    function withdrawTokens() external {
        require(msg.sender == owner, "Only owner can withdraw tokens");
        
        uint256 balance = token.balanceOf(address(this));
        require(balance > 0, "No tokens to withdraw");
        
        token.transfer(owner, balance);
    }
}

To begin with, change this:

require(msg.value >= 100 && msg.value <= 1000, "Amount sent is not within range");

To this:

require(msg.value >= 100 ether && msg.value <= 1000 ether, "Amount sent is not within range");

I have changed that. it deployed (I was using the wrong chain before).

But once I try to send eth to the Presale Smart Contract, it will be failed

That's because you did not implement a payable fallback function or a payable receive function.

You can send ETH to the contract only through the buyTokens function.

It’s difficult to help you if you are not also providing the exact error message and preferably also the transaction.

If can fail for a lot of reasons. You’re performing a token.transfer.. does your contract have the rights to transfer tokens?

Thank you

Here's the updated version:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface IERC20 {
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
function transfer(address recipient, uint256 amount) external returns (bool);
function balanceOf(address account) external view returns (uint256);
function approve(address spender, uint256 amount) external returns (bool);
}

contract IDO {
address public owner;
uint256 public price;
uint256 public totalTokens;
uint256 public soldTokens;
uint256 public minPurchase;
uint256 public maxPurchase;
uint256 public startTime;
uint256 public endTime;
IERC20 public token;

mapping(address => uint256) public balances;

event Buy(address indexed buyer, uint256 amount);

constructor(
    uint256 _price,
    uint256 _totalTokens,
    uint256 _minPurchase,
    uint256 _maxPurchase,
    address _token,
    uint256 _startTime,
    uint256 _duration
) {
    owner = msg.sender;
    price = _price;
    totalTokens = _totalTokens;
    minPurchase = _minPurchase;
    maxPurchase = _maxPurchase;
    token = IERC20(_token);
    startTime = _startTime;
    endTime = _startTime + _duration;
}

function buyTokens(uint256 _amount) external {
    require(block.timestamp >= startTime, "Sale has not started yet");
    require(block.timestamp < endTime, "Sale has ended");
    require(_amount >= minPurchase, "Amount is less than minimum purchase");
    require(_amount <= maxPurchase, "Amount is greater than maximum purchase");
    require(soldTokens + _amount <= totalTokens, "Not enough tokens left for sale");
    uint256 cost = _amount * price;
    require(token.balanceOf(msg.sender) >= cost, "Not enough balance to buy tokens");
    token.transferFrom(msg.sender, address(this), cost);
    balances[msg.sender] += _amount;
    soldTokens += _amount;
    emit Buy(msg.sender, _amount);
}

function withdraw() external {
    require(msg.sender == owner, "Only owner can withdraw");
    require(block.timestamp >= endTime, "Cannot withdraw until sale has ended");
    uint256 balance = token.balanceOf(address(this));
    require(balance > 0, "No tokens to withdraw");
    token.transfer(owner, balance);
}

function setPrice(uint256 _price) external {
    require(msg.sender == owner, "Only owner can set price");
    require(block.timestamp < startTime, "Cannot set price after sale has started");
    price = _price;
}

function setMinPurchase(uint256 _minPurchase) external {
    require(msg.sender == owner, "Only owner can set minimum purchase");
    require(block.timestamp < startTime, "Cannot set minimum purchase after sale has started");
    minPurchase = _minPurchase;
}

function setMaxPurchase(uint256 _maxPurchase) external {
    require(msg.sender == owner, "Only owner can set maximum purchase");
    require(block.timestamp < startTime, "Cannot set maximum purchase after sale has started");
    maxPurchase = _maxPurchase;
}

function getBalance() external view returns (uint256) {
    return token.balanceOf(address(this));
}

}

The Error I got: Contract Interaction - Fail

I assume the transaction itself contains more information then just fail, however this this contract is totally different then the previous one and the buyTokens function doesn't make sense.

In the previous version the contract would transfer the tokens to the sender. So i was guessing the contract was not approved to send the tokens.

Now you are executing:

require(token.balanceOf(msg.sender) >= cost, "Not enough balance to buy tokens");

you are checking if the sender has XXXX amount of your tokens.. which is rather strange as the sender is trying to buy the tokens

token.transferFrom(msg.sender, address(this), cost);

now you trying to are transfer those tokens from the buyer to this contract

In the old contract you assumed the contract was approved to transfer the tokens to the user. In the new contract you assume that sender has approved your contract to transfer the tokens from the sender to the contract.