1

Trying to write a quick function to withdraw ETH from my contract - it's not working.

Here's the code:

function withdrawEntireBalance() payable external {
    require(msg.sender == contractOwner, "ERROR! Only Contract's owner may call!");
    
    msg.sender.transfer(address(this).balance);
}

When I say it's not working I don't mean that it's throwing errors. On that contrary: it compiles perfectly, it runs - it's just plain not doing what it's supposed to be doing. It does nothing.

I also created a getContractBalance() function just so I can query the Contract and make sure it actually has some ETH in its balance:

function getContractBalance() external returns(uint256 theBalance) {
    return address(this).balance;
}

This function works perfectly well and I'm able to verify with 100% certainly that the Contract does indeed have ETH in its balance when I'm trying to withdraw that balance. Here's a sample output:

'getContractBalance' result is: 14700000000000000

I also tried some alternative code for the withdrawEntireBalance() function - after having read all sorts of posts re security issues and minimum gas costs increasing (potentially causing the traditional transfer() and send() methods normally used for this sort of operation to fail) - it didn't work either. I'm including it here anyway so you can see what I'v gone through:

    // Alternative code:
    (bool success, ) = msg.sender.call.value(address(this).balance)("");

    require(success, "ERROR!!!! 'withdrawEntireBalance()' - Transfer failed.");

Any ideas what's going on here?

=================

UPDATE:

As requested, here's how I'm calling my withdrawEntireBalance() method in my javascript code:

cashOut: function() {
    console.log("\n\n=================\n==> in 'cashOut()'!");

    var cashoutYo = jsApp.appContractInstance.methods.withdrawEntireBalance().call({from: jsApp.defaultAccount}, 
      function(error, result) {
        if (!error) {
          console.log("Back in 'withdrawEntireBalance'!!!  No ERRORS!!!");
          // jsApp.contractOwner = result;
          console.log("CashOut result is: " + result);
          console.log("CashOut result KEYS = " + Object.keys(result));
          console.log("CashOut result VALUES = " + Object.values(result));
        }
        else {
          console.log("ERROR!!! from 'withdrawEntireBalance()' -->\n" + error);
          console.log("Error KEYS = " + Object.keys(error));
          console.log("Error VALUES = " + Object.values(error));
        }
      });

    console.log("\n\n=================\n==>EXITING 'withdrawEntireBalance()'!");
  }
2
  • it runs - how? (please share your method of executing the withdrawEntireBalance function). Commented Nov 21, 2020 at 19:54
  • @goodvibration Updated my code as per your request -- see above
    – Mark55
    Commented Nov 21, 2020 at 20:44

1 Answer 1

1

var cashoutYo = jsApp.... is asynchronous, so try await.

Even so, it doesn't return what you expect. You will get a transaction hash for the transaction that was sent to the network - not the result after the network mines it because you have to wait for that.

Have a look at this for the stages you might be interested in and a convenient way to wait for them:

web3.eth.sendTransaction({from: '0x123...', data: '0x432...'})
.once('sending', function(payload){ ... })
.once('sent', function(payload){ ... })
.once('transactionHash', function(hash){ ... })
.once('receipt', function(receipt){ ... })
.on('confirmation', function(confNumber, receipt, latestBlockHash){ ... })
.on('error', function(error){ ... })
.then(function(receipt){
    // will be fired once the receipt is mined
});

https://web3js.readthedocs.io/en/v1.3.0/callbacks-promises-events.html

There are several different JavaScript libraries and contract abstractions with their own implementations but the underlying EVM is always the same.

Transactions get sent and that generates a hash (A UID for the txn) from the payload. No response from the network is even required so there is no assurance the network will even know about (e.g. connectivity problem). The first callback gives you the hash that your node is trying to send.

Transactions are mined when they are included in a block, later. Given a transaction hash, you can wait for it to appear in a block. Only then is it possible to say if it failed or succeeded and inspect the effect.

The asynchronous process can be somewhat masked by tools like ganache-cli because they mine blocks "on-demand" and immediately. Be careful to not be fooled by the instant mining. Use a public testnet to confirm you are handling the process correctly.

Hope it helps.

3
  • Hey Rob - thanks for the thorough answer. Yeah I'm totally familiar with all those .once and .on events - but for some reason it didn't cross my mind that I might need to enlist them for this Tx. Will give 'em a go and let you know how it goes!
    – Mark55
    Commented Nov 21, 2020 at 21:55
  • Ok, the await bit actually threw an error - so I took it out, but more importantly I realized I should never have been calling withdrawEntireBalance().call but rather withdrawEntireBalance().send, because the withdrawal of funds - which is really just a transfer from one account to another - is absolutely a Blockchain-state-changing type of event - meaning it should be a fee-incurring type of Tx. So I’m good to go now. And yes, I put in all those .on’s and .once’s and everything is firing correctly. Many thanks!
    – Mark55
    Commented Nov 21, 2020 at 23:20
  • Sweet. You are right. .call is indeed a read-only invocation. Commented Nov 21, 2020 at 23:49

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.