Solidity Payable Functions - Discussion

Hey @thecil,

Thank you for that. I’m still not quite clear though… sorry…

For clarity I’m looking at the bank contract provided by Filip :

pragma solidity 0.8.0;
pragma abicoder v2;
import "./Ownable.sol";
import "./SafeMath.sol";

contract Bank is Ownable {
    
    using SafeMath for uint256;
    
    mapping(address => uint) balance;
    address[] customers;
    
    event depositDone(uint amount, address indexed depositedTo);
    
    function deposit() public payable returns (uint)  {
        balance[msg.sender] = balance[msg.sender].add(msg.value);
        emit depositDone(msg.value, msg.sender);
        return balance[msg.sender];
    }
    
    function withdraw(uint amount) public onlyOwner returns (uint){
        require(balance[msg.sender] >= amount);
        balance[msg.sender] = balance[msg.sender].sub(msg.value);
        payable(msg.sender).transfer(amount);
        return balance[msg.sender];
    }
    
    function getBalance() public view returns (uint){
        return balance[msg.sender];
    }
    
      function contractBalance() public view returns (uint){
        return address(this).balance;
    }
    
    
    function transfer(address recipient, uint amount) public {
        require(balance[msg.sender] >= amount, "Balance not sufficient");
        require(msg.sender != recipient, "Don't transfer money to yourself");
        
        uint previousSenderBalance = balance[msg.sender];
        
        _transfer(msg.sender, recipient, amount);
        
        assert(balance[msg.sender] == previousSenderBalance - amount);
    }
    
    function _transfer(address from, address to, uint amount) private {
        balance[from] -= amount;
        balance[to] += amount;
    }
    
}

I assume for the ETH to move from the contract to the msg.sender address this method

 payable(msg.sender).transfer(amount);

is responsible for moving the ETH and the internal balance is handled by the balance mapping

mapping(address => uint) balance;

Correct?

I have a problem though:

Here is the withdrawal function from the bank contract that Filips provided, however without making it payable I get an error :

  function withdraw(uint amount) public onlyOwner returns (uint){
        require(balance[msg.sender] >= amount);
        balance[msg.sender] = balance[msg.sender].sub(msg.value);
        payable(msg.sender).transfer(amount);
        return balance[msg.sender];
    }
    

This is the error.

TypeError: “msg.value” and “callvalue()” can only be used in payable public functions. Make the function “payable” or use an internal function to avoid this error.
–> Dawie_Laptop/Remix Exercises/BANK 2.0/Bank_Fillip.sol:24:55:
|
24 | balance[msg.sender] = balance[msg.sender].sub(msg.value);
| ^^^^^^^^^

If you have some insight for me I’d much appreciate it. Obviously, this is a fundamental part of Solidity that I want to fully understand.

Correct! :nerd_face:

That error comes from this change, since solidity version 0.8.0
https://docs.soliditylang.org/en/latest/080-breaking-changes.html#interface-changes

image

image

Carlos Z

Hi Guys,
I just was studying payable function from Etth101.
I did similar contract as Filip on remix and has different output after deploying it.
I send ss of both mine and his o output after deploying:
image

image

1)In my case I have to write the amount next to the red button “deposit” in the Deployed contract and his write the amount above in the Value section and he could choose the unit wei or ETH, I couldn’t.
2)When I deposited Eth the amount on my account next to the address was the same 99…, in his case the value of Eth decreased.

Could anyone explain me that? Or tell what did i do wrong?

1 Like

Good, Fine and Okay.
I got it.
That’s just because decelerate the deposit function with parametr.

But now I have another questions about payable function:
How i could call payable function with a value if the function does not take any arguments?
Yes, We can run transactions and pass there the value we would like to call the payable function with in the Remix IDE.
But how it would be if not Remix?
Do we always need a software e g MetaMask to call such a function?
If Yes, how the program, software, wallet like MetaMask call non parametric function with an argument - a value - in our case amount of ETH?
And how it was before first web3 software - blockchain handler software on Blockchain?

1 Like

Correct, it was an extra parameter that you dont need.

payable allows to use msg.value to receive ethers.

In Ethereum Smart Contract Programming 201, you will learn how to use truffle, which is another tool to compile solidity contracts.

To interact with dapps, yes.

through web3.js library.

Connection to nodes directly.

Carlos Z

Thank you very much for your answers.
I wonder how does calling payable function works when connecting nodes directly.
Could you explain or tell where I could find explanation?

Kacper Pajak

1 Like

I think this video explain pretty well what you are asking. :face_with_monocle:

https://www.youtube.com/watch?v=5HRmpeFbdnI

Carlos Z

Guys I have a question. What balance is the getBalance function getting in the tutorial? Is that the smart contract balance or the internal balance … It wasn’t very clear that. thanks

2 Likes

image

Why my function doesnt works?

1 Like

Hey @Artrex_G, hope you are ok.

The receiver must be a payable address, by default msg.sender is not, please share your code in the following way so i can review it properly:

Carlos Z

Did the following solution:

function withdraw(uint amount) public returns(uint) {

    uint assertTransfer = balance[msg.sender];
    
    msg.sender.transfer(amount);
    
    assert(balance[msg.sender] == assertTransfer - amount);
1 Like

Thanks, it helped me a lot.

I’m still learning, and here in Brazil the material is still very old.

I’m learning a lot here.

1 Like

Hi,

I am having problems with the transfer function. I wrote this simple code in attempts to understand how to use transfer correctly. I was thinking everything I deposit into the contract would be able to be withdrawn to the current address (msg.sender) however whenever I withdraw an amount it is never added to the current address (msg.sender) and when I display the balance using the showBalance function the balance only changes when I deposit but nothing happens when I withdraw… somebody please help. Thanks.

contract bank {

mapping (address => uint) balance;

function deposit () public payable returns (uint){
balance[msg.sender] += msg.value;
return balance[msg.sender];
}

function showBalance () public view returns (uint){
return balance[msg.sender];
}

function withdraw (uint amount) public returns(uint){
msg.sender.transfer(amount);
}
}

Hey @Rob1, hope you are ok.

The only issue that I see with your withdraw function is that you never update the balance of your mapping, also the msg.sender must be casted as payable.

// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.7;

contract bank {

    mapping (address => uint) balance;

    function deposit () public payable returns (uint){
    balance[msg.sender] += msg.value;
    return balance[msg.sender];
    }
    
    function showBalance () public view returns (uint){
    return balance[msg.sender];
    }
    
    function withdraw (uint amount) public {
        balance[msg.sender] -= amount;
        payable(msg.sender).transfer(amount);
    }
}

Carlos Z

I’m trying to understand how this deposit() function really works.
How is msg.value being passed to this function?

In remix, it only shows a “Deposit” button without a field. So no value is actually being deposited (???).

For comparison, withdraw(uint amount) is being passed an amount value.

Can someone explain how this deposit function is supposed to work?

function deposit() public payable returns(uint) {
        balance[msg.sender] += msg.value;
        emit depositDone(msg.value, msg.sender);
        return balance[msg.sender];
    }
1 Like

and, when i run this with the full contract in Remix, how can i see the event being emitted? i do not see events being printed in the Remix console … so where are events emitted to?

Hey @bcripy,

I’m glad the information helped, and I hope you’re still learning lots :muscle:

Hi @gaio,

In Remix, when you send ETH to a contract, and call the function which RECEIVES that ether value, you need to input the amount of ether/wei you want to send in the VALUE field (below the Account and Gas Limit fields) and set the dropdown to the relevant unit of currency (wei, gwei, finney or ether). You need to do this before you call the deposit function (by clicking the “Deposit” button), otherwise, as you have discovered, no value will be deposited.

The input fields which appear next to the function-call buttons are for arguments/parameters that are explicity defined in the function header, such as   address recipient   or   uint amount , for example. However, when a smart contract function RECEIVES ETH from an external address, Solidity does not require this to be explicity defined as a parameter, and the ether value will be automatically passed to the function. It is still available as an implicit parameter, though, and can be referenced and handled with msg.value.

Another implicit parameter which Solidity automatically passes to a function (i.e.without needing to be explicity defined as a parameter in the function header) is msg.sender . In Remix, this address value will always be the address which is set in the Account field — the calling address. When the deposit function is called, both the calling address (Account field) and the ether/wei value sent/deposited (Value field) are automatically passed to the function. The ether/wei value is automatically added to the contract address balance, and this does not require any additional code. However, we can reference and handle this value with msg.value and the depositor’s address with msg.sender . We need to reference and handle these values in order to keep track of each account holder’s share of the total contract balance, which we do with the line…

balance[msg.sender] += msg.value;

This internal accounting (recorded by the mapping) is crucial, because the contract balance figure represents the total pooled funds (i.e. the sum of all account holder balances).


The withdraw function works differently. It uses the syntax…

<address payable>.transfer(uint amount)

… to deduct the withdrawal amount from the contract balance and transfer it to the caller’s address. As with the deposit function, the caller’s address can be referenced with msg.sender , but the withdrawal amount needs to be input as a uint value, which can then be referenced by its parameter name (amount)…

msg.sender.transfer(amount);

I hope that makes things clearer; but just let me know if you have any further questions :slight_smile:

2 Likes

Once you’ve called the function with an ether/wei value, as I’ve described, then your emit statement should successfully log your deposit event data: the deposit amount (always in wei), and the depositor’s address. You should see this event log if you expand the transaction confirmation (with the green tick) in the Remix console, and scroll down to logs. You should see something like…

[{
	"from": "0xd914...138",    // Smart contract address
	"topic": "0x4bc...a53",
	"event": "depositDone",    // Event name
	"args": { // Logged data, according to the defined event arguments
              // Same data given twice (i) by index (ii) by argument name      
		"0": "4000000000000000000",       // Amount deposited (always in wei)
		"1": "0x5B3...dC4",               // Depositor's address
		"amount": "4000000000000000000",  // Amount deposited (repeated)
		"depositedTo": "0x5B3...dC4"      // Depositor's address (repeated)
	}
}]

Obviously, this is assumes you’ve defined your event at the top of the contract, as well as emitting it :wink:

Just let me know if anything is still unclear.

1 Like
pragma solidity 0.7.5;
contract Bank {
    
    event Log(uint amount,address to );
    function Deposit() public payable {
        emit Log(msg.value,msg.sender);
    }
}

The log shows a “from” address and a “to” address.
I know the “to” is the contract address.
My question is what is the from address?

[ { "from": "0x7EF2e0048f5bAeDe046f6BF797943daF4ED8CB47", "topic": "0x451fd692d761946da28778ba4fec74700fa4197e441dad0b36dd8284f65f9f7f", "event": "Log", "args": { "0": "1000", "1": "0x5B38Da6a701c568545dCfcB03FcB875f56beddC4", "amount": "1000", "to": "0x5B38Da6a701c568545dCfcB03FcB875f56beddC4" } } ]

1 Like