I'm building a presale dApp as an experiment, and when I hit the buyButton, MetaMask successfully opens for me to approve spending my USDT (labelled a contract interaction). However the transaction is empty aside from showing gas fees. I'm able to approve this without any errors even though it's essentially empty. Shortly after, the buyTokens interaction opens in MM, same again - empty aside from gas fees. This one fails, as expected.
I know the contract is being detected to a certain point because my IDE, console log and MM all recognise the functions I'm trying to call. I'm also managing to pull a number of datapoints from the contract to display in my dApp.
Thanks in adv!
document.getElementById("buyButton").addEventListener("click", async () => {
try {
const usdtEther = document.getElementById("usdtAmount");
console.log(usdtEther);
// Convert the input amount to Wei (assuming USDT has 18 decimal places)
const usdtX = usdtEther.value;
const usdtAmount = ethers.parseUnits(usdtX, 6);
const usdtString = usdtAmount.toString();
console.log('USDT STRING:', usdtString);
const buyProv = new ethers.BrowserProvider(window.ethereum);
const buySign = await buyProv.getSigner();
const buyContract = new ethers.Contract(presaleContractAddress, presaleContractAbi, buySign);
const userAddress = await buySign.getAddress();
const nonce = await provider.getTransactionCount(userAddress, 'latest');
console.log('userAdd', userAddress);
console.log(buyProv);
console.log(buySign);
console.log(buyContract);
console.log(usdtAmount);
// Transaction options (gasPrice, gasLimit, etc.)
const txOptions = {
gasLimit: 200000,
gasPrice: ethers.parseUnits("20", "gwei"), // Set the gas price in Gwei
chainId: 97,
value: 0,
nonce: nonce,
sendTransaction: true,
};
console.log('txOptions:', txOptions);
const approveUsdt = await buyContract.approveUSDT(usdtAmount, { ...txOptions, });
await approveUsdt.wait();
const buyTokens = await buyContract.buyTokens(usdtAmount, {
...txOptions,
});
await buyTokens.wait();
// Send the transaction to the 'buyTokens' function
alert("Tokens purchased successfully!");
} catch (error) {
console.error("Error purchasing tokens:", error);
alert("Error purchasing tokens. See console for details.");
}
});
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/security/Pausable.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
contract Presale is Ownable, Pausable, ReentrancyGuard {
IERC20 public kooToken;
IERC20 public usdtToken;
uint256 public constant presalePrice = 793700000000000; // This represents 0.0007937 USDT with 7 decimal precision
uint256 public constant tokensToSell = 6300000000000000000000000000; // 6.3 billion KOO with 18 decimals
uint256 public totalTokensSold;
event TokensPurchased(address indexed buyer, uint256 amount);
event TokensWithdrawn(address indexed owner, uint256 amount);
event FundsWithdrawn(address indexed owner, uint256 amount);
constructor(address _kooToken, address _usdtToken) {
kooToken = IERC20(_kooToken);
usdtToken = IERC20(_usdtToken);
}
// Approval function to allow the contract to spend the buyer's USDT
function approveUSDT(uint256 usdtAmount) external whenNotPaused {
require(usdtAmount > 0, "Amount must be greater than 0");
// Check the current allowance
uint256 currentAllowance = usdtToken.allowance(msg.sender, address(this));
// Calculate the new allowance needed
uint256 newAllowance = usdtAmount;
// If the current allowance is already sufficient, no need to change it
if (newAllowance > currentAllowance) {
// Approve the contract to spend the buyer's USDT
require(usdtToken.approve(address(this), newAllowance - currentAllowance), "USDT approval failed");
}
}
function buyTokens(uint256 usdtAmount) external whenNotPaused nonReentrant {
require(usdtAmount > 0, "Amount must be greater than 0");
uint256 tokensToBuy = usdtAmount * 10**6 / presalePrice;
uint256 remainingTokens = tokensToSell - totalTokensSold;
require(tokensToBuy <= remainingTokens, "Not enough tokens left for sale");
// Transfer USDT from buyer to this contract using transferFrom
require(usdtToken.transferFrom(msg.sender, address(this), usdtAmount), "USDT transfer failed");
// Transfer KOO tokens from this contract to the buyer
kooToken.transfer(msg.sender, tokensToBuy);
totalTokensSold += tokensToBuy;
emit TokensPurchased(msg.sender, tokensToBuy);
}
function withdrawUnsoldTokens() external onlyOwner {
uint256 unsoldTokens = tokensToSell - totalTokensSold;
require(unsoldTokens > 0, "No unsold tokens left");
kooToken.transfer(owner(), unsoldTokens);
emit TokensWithdrawn(owner(), unsoldTokens);
}
function withdrawUSDT() external onlyOwner {
uint256 contractBalance = usdtToken.balanceOf(address(this));
require(contractBalance > 0, "No USDT to withdraw");
usdtToken.transfer(owner(), contractBalance);
emit FundsWithdrawn(owner(), contractBalance);
}
function withdrawAccidentallySentTokens(address _tokenAddress) external onlyOwner {
IERC20 token = IERC20(_tokenAddress);
uint256 contractBalance = token.balanceOf(address(this));
require(contractBalance > 0, "No tokens to withdraw");
token.transfer(owner(), contractBalance);
emit TokensWithdrawn(owner(), contractBalance);
}
function pause() external onlyOwner {
_pause();
}
function unpause() external onlyOwner {
_unpause();
}
// Fallback function to receive Ether (if needed)
receive() external payable {}
}