Ethereum lottery smart contract reverts due to insufficient funds. Where does my code consumes that much gas? - solidity

I am experimenting with solidity and I faced an issue for what I could not find a solution.
The program should let addresses buy ticket at a preset price, and the owner can start the "roll the dice" function which randomly selects the winner and transfer the funds to that address.
I beleive that this program would be easier with mapping instead of array, but getting experience with array was the main purpose of this program.
The error happens when the user calls buyTicket function. Based on the response I beleive the contract comsumes too much gas. Can someone tell me why it doesnt work? I appreciate any other helping comment regarding the rest of the code:)
Thanks in advance!
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.0;
contract Lottery {
constructor () {
owner = msg.sender;
}
address[] public Players;
address private owner;
uint public ticketPrice;
uint public price;
uint public nonce;
uint public variations;
address payable winner;
bool hasTicketAnswer;
event Winner(address _winner);
event PriceSet(uint _setPrice);
event TicketBought();
function setTicketPrice(uint _ticketPrice) public {
require(msg.sender == owner, "Only Owner...");
ticketPrice = _ticketPrice;
emit PriceSet(_ticketPrice);
}
function hasTicket(address _sender) private returns(bool) {
hasTicketAnswer = false;
for (uint i = 0; i < Players.length; i++) {
if (Players[i] == _sender) hasTicketAnswer = true;
}
return hasTicketAnswer;
}
function buyTicket() external payable {
require(ticketPrice > 0, "Price did not set, be patient...");
require(hasTicket(msg.sender) == false, "You cannot have two tickets...");
require(msg.sender.balance <= ticketPrice, "Insufficient funds...");
payable(address(this)).transfer(ticketPrice);
Players.push(address(msg.sender));
price += msg.value;
emit TicketBought();
}
function checkBalance() public view returns(uint) {
return address(this).balance;
}
function rollTheDice() public payable {
variations = Players.length;
winner = payable(Players[uint(keccak256(abi.encodePacked(msg.sender, nonce, block.timestamp))) % variations]);
winner.transfer(price);
emit Winner(winner);
}
receive () external payable {
}
}

Besides probably finding the problem, I've read some things that I'd like to comment on.
Your problem
The reason why you're getting the "Insufficient funds" error is because the condition is returning false. You're asking the msg.sender balance to be less than or equal (<=) to ticketPrice, when it should be more than or equal (>=).
Let's say Alice has a balance of 0.05 ETH and interacts with the contract whose ticket price is 0.001 ETH...
require(0.05 ETH <= 0.001 ETH) // Reverting...
Observations
I'm curious if you're intentionally coding the buyTicket function in that way. What it actually does is checking if the msg.sender has enough ETH to buy a ticket in its wallet, which doesn't mean that the user is effectively sending ETH in the transaction (the amount of wei sent in the transaction can be checked with msg.value, you can read more about it here).
My last observation is the payable(address(this)).transfer(ticketPrice) line of code, because it's not necessary to do so, once a payable function receives ETH, it is saved into the contract... In that line you're just making the Bob's contract to send ETH to the Bob's contract, which just wastes gas without reason
I hope I've helped with you and if I wasn't completely clear in any thing I've said don't doubt in asking me

Related

I need help to customize my smart contract lottery project

Things that I need to add as functions:
I want to integrate a management fee of 1% of the total pot. Execution: The management fee (going to contract owner) will be deducted to the total prize before the remaining 99% amount will be sent to the lottery winner. I need to add it to the selectWinner function.
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;
contract Lottery{
address public manager;
//global dynamic array for participants.
address payable[] public participants;
constructor()
{
//msg.sender is a global variable used to store contract address to manager.
manager=msg.sender;
}
//receive function only creates once in a smart comtract.
//this function help to transfer the ether.
//always use with external keyword and payable.
receive() external payable{
//require is used as a if statement. it check if ether value is 2 then only run below code.
require(msg.value==0.02 ether);
participants.push(payable(msg.sender));
}
function getBalance() public view returns(uint){
//only manager check the total balance.
require(msg.sender==manager);
return address(this).balance;
}
//this random function will genrate random value and from participant array and then return to the winnerFunction.
function random() public view returns(uint)
{
return uint(keccak256(abi.encodePacked(block.difficulty, block.timestamp, participants.length)));
}
//this function decide the winner randomly.
function selectWinner() public{
require(msg.sender==manager);
require(participants.length>=3);
uint r=random(); //call random function.
uint index=r % participants.length; //for making random function value in array length range.
address payable winner;
winner=participants[index];
winner.transfer(getBalance());
participants=new address payable[](0);
}
}
I need help to do it. Thank you.
I think something like this should work. I don't have time to test the function right now, but will edit this answer later today. Hope it helps you at the moment.
function selectWinner() public {
require(msg.sender==manager);
require(participants.length>=3);
uint r=random(); //call random function.
uint index=r % participants.length; //for making random function value in array length range.
address payable winner;
winner=participants[index];
uint balance = getBalance(); // balance is in wei
uint fee = balance * 0.01 ether; // 1% in wei
uint amountToTransfer = balance - fee;
winner.transfer(amountToTransfer);
_withdrawFee(fee); // transfer fee to manager
participants=new address payable[](0);
}
function _withdrawFee(uint feeAmount) payable private {
msg.sender.transfer(feeAmount);
}

Solidity: transferFrom can't reach allowance value

Here are the steps I follow on Remix:
Deploy my ERC20Basic token and my DEX contract.
DEX.buy > Buy ERC20Basic tokens in exchange of ETH (works fine).
ERC20Basic.approve(contractAddress, tokenAmount) (works fine).
ERC20Basic.allowance > Works fine, function returns the amount of the allowance.
The problem comes when I try to sell tokens with this function from my DEX contract:
function sell(uint256 amount) public {
uint256 allowance = token.allowance(msg.sender, address(this));
require(allowance >= amount, "Check the token allowance");
token.transferFrom(msg.sender, address(this), amount);
payable(msg.sender).transfer(amount);
emit Sold(amount);
}
I still get "Check the token allowance".
When I log the allowance I get 0.
When I log msg.sender and address(this), I get the same addresses I used on the Remix interface to get the value of the allowance manually.
It feels like the allowance is reset to 0 when I call the sell function or that the sell function can't reach the value of the allowance. Maybe this is something about memory and storage?
My allowance function inside of ERC20Basic contract is:
function allowance(address owner, address delegate)
public
view
override
returns (uint256)
{
return allowed[owner][delegate];
}
Approval function:
function approve(address delegate, uint256 numTokens)
public
override
returns (bool)
{
allowed[msg.sender][delegate] = numTokens;
emit Approval(msg.sender, delegate, numTokens);
return true;
}
Allowance structure:
mapping(address => mapping(address => uint256)) allowed;
How are you calling the approve function?
In your code you aren't passing the require statement because your approval wasn't successful. If it was your allowance wouldn't be 0.
You are probably calling the approve function from the contract which won't work because then the msg.sender would be address of the contract not your accounts address.
You have to approve the amount manually outside of the contract for it to work.

Token balance check is not working in Solidity smart contract

here is my code snippet and i have no idea why its not working
function Testdeposit(address _assetAddress) public returns (uint256 status)
{
//IERC20 erc20 = IERC20(_assetAddress);
//erc20.transferFrom(senderAddress, address(this), _amount));
//uint256 amount = erc20.allowance(senderAddress, address(this));
uint256 amount = IERC20(_assetAddress).balanceOf(address(this));
return amount;
}
i am using a standard IERC20 interface. just it dsnt have an event to emit. i assume it to return me 0 if there is no balance but its gvng me erro. Transcation not going through.
Any suggestions??
It is not working because you may have not overridden the IERC20 functions. At least as they should. I would highly recommend using the ERC20 standard instead, just for how easy it is to implement.

Burning Deployed ERC Tokens In an NFT Mint Function - Compiles, but Transaction Fails

I am very new to Solidity, and have recently been working on trying to learn the ropes. For reference, I have been using code from this video (https://www.youtube.com/watch?v=tBMk1iZa85Y) as a primer after having gone through the basic crypto zombies tutorial series.
I have been attempting to adapt the Solidity contract code presented in this video (which I had functioning just fine!) to require a Burn of a specified amount of an ERC-20 token before minting an NFT as an exercise for myself. I thought I had what should be a valid implementation which compiled in Remix, and then deployed to Rinkeby. I call the allowAccess function in Remix after deploying to Rinkeby, and that succeeds. But, when I call the mint function with the two parameters, I get: "gas estimation errored with the following message (see below). The transaction execution will likely fail. Do you want to force sending? execution reverted."
If I still send the transaction, metamask yields "Transaction xx failed! Transaction encountered an error.".
I'm positive it has to do with "require(paymentToken.transfer(burnwallet, amounttopay),"transfer Failed");", though I'm not sure what's wrong. Below is my entire contract code. I'm currently just interacting with the Chainlink contract on Rinkeby as my example, since they have a convenient token faucet.
pragma solidity ^0.8.0;
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/ERC721.sol";
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/IERC20.sol";
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/access/Ownable.sol";
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/Counters.sol";
contract myNFTwithBurn is ERC721, Ownable {
address externalTokenAddress = 0x01BE23585060835E02B77ef475b0Cc51aA1e0709; //Token Type to burn on minting
uint256 amounttopay = 5; //number of these tokens to burn
IERC20 paymentToken = IERC20(externalTokenAddress); //my code: create an interface of the external token
address burnwallet = 0x000000000000000000000000000000000000dEaD; //burn wallet
using Counters for Counters.Counter;
Counters.Counter private _tokenIds;
using Strings for uint256;
// Optional mapping for token URIs
mapping (uint256 => string) private _tokenURIs;
// Base URI
string private _baseURIextended;
constructor() ERC721("NFTsWithBurn","NWB") {
}
function setBaseURI(string memory baseURI_) external onlyOwner() {
_baseURIextended = baseURI_;
}
function _setTokenURI(uint256 tokenId, string memory _tokenURI) internal virtual {
require(_exists(tokenId), "ERC721Metadata: URI set of nonexistent token");
_tokenURIs[tokenId] = _tokenURI;
}
function _baseURI() internal view virtual override returns (string memory) {
return _baseURIextended;
}
function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token");
string memory _tokenURI = _tokenURIs[tokenId];
string memory base = _baseURI();
// If there is no base URI, return the token URI.
if (bytes(base).length == 0) {
return _tokenURI;
}
// If both are set, concatenate the baseURI and tokenURI (via abi.encodePacked).
if (bytes(_tokenURI).length > 0) {
return string(abi.encodePacked(base, _tokenURI));
}
// If there is a baseURI but no tokenURI, concatenate the tokenID to the baseURI.
return string(abi.encodePacked(base, tokenId.toString()));
}
function allowAccess() public
{
paymentToken.approve(address(this), 5000000); //This is my attempt to allow the contract access to the user's external tokens, in this case Chainlink (paymentToken)
}
function mintItem(address to, string memory tokenURI)
public
onlyOwner
returns (uint256)
{
require(paymentToken.transfer(burnwallet, amounttopay),"transfer Failed"); //Try to transfer 5 chainlink to the burn wallet
_tokenIds.increment();
uint256 id = _tokenIds.current();
_mint(to, id);
_setTokenURI(id, tokenURI);
return id;
}
}
If anybody can at least point me to what I'm doing completely wrong in the code that I've added, please do! TIA!
I'm not sure why are you trying to burn link in order to mint and nft but first check if the link code does not have a require that check if the destination address is the burn address if it has then burn the link is not possible and you should use any other erc20 maybe your own erc20, also your contract probably does not have any link and if you want to transfer the link from the user you should do this in the contract paymentToken.transferFrom(msg.sender,destinationAddress,amount) and if the user previously approve your contract you will able to send the tokens, and i suppose that the purpose of the allowAccess function is to make the user approve the contract to move the tokens that will never work, the approve function let's anyone that call it approve any address to move an amount of tokens, the thing is that to know who is approving to let other to move the tokens the function use msg.sender to explain how this work take a look at this example
let's say that your contract is the contract A and the link contract is the contract B
now a user call allowAccess in the contract A, so here the msg.sender is the user because they call the function
now internally this function call approve on contract B, here the contract A is the msg.sender, because the contract is who call the function
so what allowAccess is really doing is making the contract approving itself to move their own tokens that I assume it doesn't have

Sending ether in solidity smart contract

I'm writing a smart contract in solidity, and I need to send ether from my coinbase (eth.coinbase) to my friend coinbase (address = 0x0123).
If I try to use an address.send(value) the function doesn't decrease my account and doesn't increase my friend coin base.
I only can send ether in geth with "eth.sendTransaction(VALUE, {from:eth.coinbase, to:address})"
so I want to know if its possible to call an eth method in contract or a different way to send ether in smart contract
function withdraw() returns (bool) {
address x = 0x0123;
uint amount = 100 ether;
if (x.send(amount))
return true;
else
return false;
}
address.send does not propagate exception that's why you don't see any issue. Make sure you have enough Eth in your contract.
Have a look on this documentation that will explain how to set up your smart contract: https://developer.ibm.com/clouddataservices/2016/05/19/block-chain-technology-smart-contracts-and-ethereum/
You can use following function. just change variable name with yours.
function Transfer(uint amount,address reciever){
// check sender balance is less than of amount which he wants to send.
if(balance[msg.sender] < amount){
return;
}
// decrease sender's balance.
balance[msg.sender] = balance[msg.sender] - amount;
// increase reciever's balance.
balance[reciever] = balance[reciever] + amount;
// event
// transaction(msg.sender,reciever,amount);
}