我一直在BSC测试网上开发一个预售dApp,使用solidity部署合约并获取ABI,并使用Preact/Ethers.js构建接口。
我的构建中没有真实的的错误消息。buyButton按预期工作,MetaMask打开。控制台日志显示了与presaleContract的连接,并帮助显示了许多其他事情按预期工作。然而,我的问题是MetaMask交易只显示了gas费。交易中没有任何其他与支出货币或购买货币相关的内容。MM还确认presaleContract地址以及被调用的函数。我哪里做错了?
P.S.我一般都是编程新手,更不用说基于区块链的东西了,所以如果我的代码有点精神,我道歉!
metamask txn screengrab
document.getElementById("buyButton").addEventListener("click", async () => {
const usdtEther = document.getElementById("usdtAmount");
console.log(usdtAmount);
try {
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');
const data = presaleContract.interface.encodeFunctionData("buyTokens", [usdtAmount]); // Encoded 'buyTokens' function data
console.log('userAdd', userAddress);
console.log(buyProv);
console.log(buySign);
console.log(buyContract);
console.log(usdtAmount);
// Transaction options (gasPrice, gasLimit, etc.)
const txOptions = {
from: userAddress,
to: presaleContractAddress,
gasLimit: 200000,
gasPrice: ethers.parseUnits("20", "gwei"),
chainId: 97,
value: 0,
nonce: nonce,
data: data,
};
console.log('txOptions:', txOptions);
txOptions.sendTransaction = true;
await buySign.sendTransaction({
to: presaleContractAddress,
...txOptions,
});
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);
}
function buyTokens(uint256 usdtAmount) external whenNotPaused nonReentrant {
require(usdtAmount > 0, "Amount must be greater than 0");
uint256 tokensToBuy = usdtAmount * 10**7 / presalePrice; // Calculate tokens to buy using the 7 decimal precision of the price
uint256 remainingTokens = tokensToSell - totalTokensSold;
require(tokensToBuy <= remainingTokens, "Not enough tokens left for sale");
// Transfer USDT from buyer to this contract
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 {}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/security/Pausable.sol";
import "@openzeppelin/contracts/utils/math/SafeMath.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
contract kooToken is ERC20Burnable, Ownable, Pausable, ReentrancyGuard {
using SafeMath for uint256;
address public presaleContract;
address public liquidityWallet;
address public rewardsWallet;
uint256 public buyFeePercent = 1;
uint256 public sellFeePercent = 1;
constructor() ERC20("HATO", "KOO") {
_mint(msg.sender, 21 * 10**9 * 10**18); // 21 billion initial supply
}
function setPresaleContract(address _presaleContract) external onlyOwner {
presaleContract = _presaleContract;
}
function setLiquidityWallet(address _liquidityWallet) external onlyOwner {
liquidityWallet = _liquidityWallet;
}
function setRewardsWallet(address _rewardsWallet) external onlyOwner {
rewardsWallet = _rewardsWallet;
}
function setBuyFeePercent(uint256 _percent) external onlyOwner {
require(_percent <= 1, "Buy fee cannot exceed 1%");
buyFeePercent = _percent;
}
function setSellFeePercent(uint256 _percent) external onlyOwner {
require(_percent <= 1, "Sell fee cannot exceed 1%");
sellFeePercent = _percent;
}
function _transfer(
address sender,
address recipient,
uint256 amount
) internal override nonReentrant {
require(sender != address(0), "ERC20: transfer from the zero address");
require(recipient != address(0), "ERC20: transfer to the zero address");
uint256 fee = 0;
if (
msg.sender == presaleContract ||
msg.sender == owner() ||
msg.sender == address(this) || // Allow the contract itself to use tokens during buy/sell
paused() // Allow both owner and presale contract to use tokens when paused
) {
fee = 0; // No fees for owner, presale contract, and the contract itself
} else if (recipient == address(this)) {
fee = amount.mul(buyFeePercent).div(100); // Buy fee
} else if (sender == address(this)) {
fee = amount.mul(sellFeePercent).div(100); // Sell fee
}
uint256 transferAmount = amount.sub(fee);
// Check if the sender has a sufficient balance to cover the transfer and fees
require(balanceOf(sender) >= amount, "Insufficient balance");
require(balanceOf(sender) >= fee, "Insufficient balance for fees");
super._transfer(sender, recipient, transferAmount);
if (fee > 0) {
super._transfer(sender, liquidityWallet, fee.div(2));
super._transfer(sender, rewardsWallet, fee.div(2));
}
}
function pause() external onlyOwner {
_pause();
}
function unpause() external onlyOwner {
_unpause();
}
}
我对这个有点不知所措,我花了一段时间才达到这一点,进行了大量的故障排除和错误处理-感觉就像我几乎在那里,但我无法找出问题是什么。在网上搜索已经存在的帖子并没有帮助,ChatGPT也没有帮助(但是什么时候有过!)正如前面提到的,我是新手,所以很难准确地确定问题可能是什么。
1条答案
按热度按时间h7wcgrx31#
要找出问题所在,您需要了解
usdtToken.transferFrom()
是如何工作的。ERC20合约或任何智能合约都需要与所有者钱包直接交互才能转移任何代币。但是由于
usdtToken.transferFrom()
是从其他间接源调用的,因此在调用usdtToken.transferFrom()
之前需要调用另一个附带的函数usdtToken.approve()
。usdtToken.approve()
将在调用任何将在内部调用usdtToken.transferFrom()
的函数之前单独调用。在调用
PreSaleContract.buyTokens(uint256 _value)
之前,需要在JS中调用USDTContract.approve(address _spender, uint256 _value)
https://ethereum.stackexchange.com/questions/145356/understanding-approve-and-transferfrom-confused
下一次在ethereum.stackexchange.com中发布您的问题