function recoverContribution() public payable{
require(hasDeadlinePassed(), "deadline has not passed, contributions cannot be recovered rightnow");
require(!(address(this).balance >= minimumTarget), "target has been met, cannot recover contributions now");
require(contributors[msg.sender] != 0, "you have not contributed anything");
payable(msg.sender).transfer(contributors[msg.sender]);
contributors[msg.sender] = 0;
}
The above function is called by a contributor to recover his/her funds incase the target has not been met and the dead line has passed.
this function gives a reentrancy error and a gas cost infinite error.
this function is extremely simple why would this function exhibit such potential errors?
Simple theory on reentrancy attacks.
When you use any transfer function on your contract, to transfer ether from your contract to someone else, if that someone else is another contract, the contract can execute logic before the transfer is finished ( check out the receive fallback function ), on this receive function, the attacker contract can call again your recoverContribution function, and keep on transferring and reentring.
Now, if you keep track of the ether AFTER the transfer, this will cause a reentrancy vulnerability because you'll update their ether balance only after the transfer has been executed, which is the case of your function, so they can empty your balance by calling recoverContribution, because your requires will pass the checks since you update their balance only after the transfer has been done.
To avoid this kind of attack, just update their balance BEFORE the transfer, this way their balance will be updated on each call, even if they are reentering from their receive function.
So basically to avoid reentrancy attacks on your function, just do:
function recoverContribution() public payable{
require(hasDeadlinePassed(), "deadline has not passed, contributions cannot be recovered rightnow");
require(!(address(this).balance >= minimumTarget), "target has been met, cannot recover contributions now");
require(contributors[msg.sender] != 0, "you have not contributed anything");
contributors[msg.sender] = 0;
payable(msg.sender).transfer(contributors[msg.sender]);
}
Related
I have 2 Contracts. One is an ERC721 Token (NFTCollectables). The other one is a market including an auction system (NFTMarket).
An auction can be claimed after it ended and only by the highest bidder.
When claiming an auction the transfer method of the NFTCollectables contract is called to transfer the NFT from the markets address to the address of the highest bidder.
I do not exactly understand why the exception comes, but it occurs at/inside the transfer method of the NFTCollectables contract. The strange thing is that even the last line of code inside the transfer method is getting executed (tested by putting a require(false, 'test') after _transfer(msg.sender, to, nftId)). But nothing after ctr.transfer(auction.highestBid.bidder, auction.nftId) is getting executed (tested by putting a require(false, 'test') after it).
Could it have to do with the gas limit?
Any idea is appreciated, thanks!
NFTMarket
function claimAuction(uint auctionIndex) external {
require(auctionIndex < auctions.length, "no auction");
Auction memory auction = auctions[auctionIndex];
require(block.timestamp <= auction.end, "auction still active");
NFTCollectables ctr = NFTCollectables(nftCollectablesAddress);
ctr.transfer(auction.highestBid.bidder, auction.nftId);
// deleting auction from active auctions list
for (uint i; i < activeAuctionIndexes.length; i++) {
if (activeAuctionIndexes[i] == auctionIndex) {
delete activeAuctionIndexes[i];
break;
}
}
emit AuctionEnd(auction.highestBid.bidder, auction.highestBid.price, auction.nftId);
}
NFTCollectables
function transfer(address payable to, uint nftId) external payable {
require(_exists(nftId), "transfer of non existing token");
require(_isApprovedOrOwner(msg.sender, nftId), "Sender not approved nor owner");
_transfer(msg.sender, to, nftId);
}
If you have deployed the contracts on the public network, like mainnet or testnet, use https://tenderly.co or EtherScan to debug the revert reason.
If you are running the contracts using unit tests then keep modifying the contracts e.g. by removing lines to see when it starts failing. Or alternatively use a better smart contract development framework like Brownie which will give you the cause of revert right away.
We had implemented a function on our codebase that i can share with you. This function is used to transfer ownership of nft to who wins the auction.
function transferNFTtoNewOwner(NFTItem memory t,address oldOwner, address newOwner) internal {
require(newOwner != address(0), "New owner can't be address zero.");
XXXX storage r = creatureList[t.tokenAddress][t.tokenId];
IERC721 nft = IERC721(t.tokenAddress);
nft.safeTransferFrom(oldOwner, newOwner, t.tokenId);
address currOwner = nft.ownerOf(t.tokenId);
require(newOwner == currOwner, "Problem on nft transfer");
r.owner = newOwner;
}
The major differences are we used safetransferfrom here. If your contract owns the NFT then you can call safeTransfer. and after that we checked the nft owner with require statement. If you put this statement and change your transfer function to safetransfer and the transaction still revert without giving any error on etherscan then you can investigate about your nft contract.
I've just deployed a Smart Contract using the ethereum wallet.
I got the Smart Contract address , I copied its ABI from remix and verified it on ethereum wallet,it was active and I could see all its methods.
Then I tried to call it from my nodejs server.I did it,I didn't get any error...
But instead of a classic response like the returned values of the methods below I got mined blocks....and this is very weird I think...
How am I supposed to get the methods output(the returns) ?
After that I tried to deploy another contract,this time a very simple one with the same methods name,smart contract name,parameters but without code inside the methods only a basic hard-coded return.When I deployed this contract as well I got the same mined smart contract address...which is weird,in my opinion...
I've been using 1.0.0-beta.46 , nodejs , expressjs
When I said eth wallet I meant...that website generated with puppeth , on /#wallet page
Here is the basic smart contract I tried to deploy second time.The result was the same as the first smart contract.
pragma solidity >= 0.4.22 < 0.6.0;
contract BasicContract {
function function1(uint16 a,uint16 b,uint16 c,uint16 d) external payable returns(uint256){
//a,b,c,d doesn't matter
return 68;
}
//buy a ticket
function function2(uint128 a,uint16 b) external payable returns(uint128){
//a,b doesn't matter
return 94;
}
function function3(uint128 a) external payable returns(bool){
//a doesn't matter
return false;
}
}
There are two ways to invoke a function in a smart contract: through a transaction sent to the network or via a local call.
Transactions don't have return values. What you get back from the library you use to make the transaction is typically the transaction hash. Any return value from the function you invoked is discarded.
A local call doesn't involve a transaction to the network and thus can't change any state. But it does give you a return value.
Which method is chosen by default for most libraries is based on whether the function is state-changing or not. If you mark your functions as view or pure, it tells the library that those functions don't change state and can then safely be just called locally to get a return value. So a simple "fix" for the above code is to make those functions pure. For functions that do change state, you'll want to switch from using return values to emitting events instead, which can be read after the transaction is mined.
I want to create a viewable function (needs to return a string to the user) that searches a mapping for msg.sender and if the senders value is x, I want the contract to proceed accordingly. It all does work inside remix but if I upload it to ropsten, it doesn't anymore. Is this a known issue? I have tried tx.origin as well, same result.
That's the problematic code I tried:
function getLink() public view returns(string){
if(tokenBalances[msg.sender]>0){
return link;
}else{
return "You need to purchase a token at first...";
}
}
EDIT: I think the problem is, that when using a viewable function there is no msg.sender because there is no actual transaction? Is there a way to return a value to the user without using the "view" functions?
Short answer
msg.sender does work in a view function, although it is useless as an authorization scheme. The lookup tool you use should have a mechanism to set the sender.
Call vs Transaction
First, it's important to understand the difference between a call and a transaction.
It appears you're running a call, which runs quickly and does not alter the state of the blockchain. msg.sender is set in both a transaction and a call. In a transaction, it cannot be faked: you must have the private key associated with the given account. But in a call, you are free to set the sender to any value you like.
Setting the Sender
How you set the sender depends on what tool you are using to call. That tool might be web3.js, web3.py, Mist, MyEtherWallet, MyCrypto, etc. They all have (or might not have!) a mechanism to set the sender in a call.
MyEtherWallet
In the comments, you mention MyEtherWallet specifically. In a quick search, I didn't find anything about how to set the sender. There is this unanswered question on ethereum.stackexchange that seems worth following, since it is asking roughly the same question: How to check msg.sender balance with MyEtherWallet contract
Contract Workarounds
is it possible to specify such settings for the contract?
There is no way to help someone set the sender from inside the contract. But you can supply a different method that takes an address as an argument. Then tools like MyEtherWallet will allow you to set the address of interest. For example:
function getLink(address account) public view returns(string){
if(tokenBalances[account] > 0){
return link;
}else{
return "You need to purchase a token at first...";
}
}
Hiding Data
It's worth noting that hiding data by checking msg.sender is useless. Anyone can set a fake sender in a call (or directly inspect blockchain state). So, it's trivial to bypass this "protection."
I have created a private blockchain using geth. I also have a contract in which there is a function which call another function it third contract to set an address. I can call this function on Local blockchains like testRPC and Ganache blockchain, Even it works in TestNet. But Once I setup the private blockchain using (geth). I get this error:
Gas estimation errored with the following message (see below).
The transaction execution will likely fail. Do you want to force sending?
gas required exceeds allowance or always failing transaction
I have enough ETH in the caller account and also
I have enough ETH in the caller account and also
GasLimit is high enough
The functions does not have any loop or fancy operation, setting an address and some condition checks
The node is an Ubuntu 16.04
geth -v --> 1.8.12-stable
// in contract 1
function func(address addr) public returns (bool result) {
Cantract2 c = Cantract2(addr);
if (!c.setAddress(..)) {
return false;
}
.....
return true;
}
You might want to check the genesis file/chain specification of your private test chain.
There is a variable called block gas limit that affects how much gas you can spend on each block. I recall that the default value used to be very low back in the days and maybe you are using such a config. What you could do:
check the block gas limit in the dev chain configuration and increase it as to your needs
or let the local test chain run for some time as the client will vote up the default block gas limit slowly, allowing you for larger transactions per block
I have geth 1.5.2 and work on testnet with Mist-linux64-0-8-7. I stuck at a simple issue when I try to send some ether (value) to my contract by calling any function.
For example, even this dummy function does not work with non-zero value but works normally with zero value:
function t() returns (uint){
return 1;
}
What is surprising here that I can see a warning in Mist with the message "It seems this transaction will fail. If you submit it, it may consume all the gas you send."
This does not depend on the gas amount that I provide.
Even if I put 500,000 of gas the transaction seem not to complete.
You can see its result here:
https://testnet.etherscan.io/tx/0x3206118530079d1b416dc649c6f0a89283f9457e9189f259b3429cf0c6a998d0
There is a message
Warning! Error encountered during contract execution [Bad jump
destination]
I tried to run different functions in different contracts. I was even using sendTransaction functionality to do the same from geth console directly but I still can't make it work.
I didn't find if anybody had similar problem. Please help me if you have any idea.
From Solidity 0.4 onwards:
Functions that want to receive Ether have to specify the new payable
modifier (otherwise they throw).
A throw consumes all gas, so use function t() payable returns (uint).