Solidity Payable Functions - Discussion

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

I don’t get the transfer in withdraw function.
My question is:
The amount is transferred from which address to which address?

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

@filip
why we are increasing the balance of the sender by one ether if the sender is depositing 1 ether.
The balance should be decreased by 1 ether right?

1 Like

Thanks for your help! This is more clear.

Two more questions. An event transferDone successfully printed to log in the console:

{
		"from": "0x7EF2e0048f5bAeDe046f6BF797943daF4ED8CB47",
		"topic": "0xb0ee4d593b8cd94a0820e9d3cc9719bd6fb3fb6c50b0f0e1d5e3fc7356fc7ee9",
		"event": "transferDone",
		"args": {
			"0": "2",
			"1": "0x5B38Da6a701c568545dCfcB03FcB875f56beddC4",
			"2": "0xd28374186360650595864e657B7fcA15AD2CBaFF",
			"amount": "2",
			"sentFrom": "0x5B38Da6a701c568545dCfcB03FcB875f56beddC4",
			"sentTo": "0xd28374186360650595864e657B7fcA15AD2CBaFF"
		}
	}
]

what is “topic”? the transaction id ?

And are these logs something only in context to Remix, or if choosing indexed in the event, is this same data schema also indexed on the blockchain?

1 Like

Hi @saishet,

If you are talking about the deposit function, then…

  • The ether amount deposited (the Value sent to the deposit function) will be deducted from the account of the calling address (you will see their account balance decreasing by this amount in the Account field at the top of the Deploy & Run Transactions panel). Think of this as the balance of the depositor’s external wallet address being reduced by the amount of ether they send and deposit within the Bank smart contract.

  • The ether value is automatically added to the contract address balance (it’s a payable function, so it can receive ether). If you want to check that this is actually happening, you can add the following function to your contract to get the Bank contract balance (the total pooled funds held in the smart contract from all account holders) …

    function getContractBalance() public view returns(uint) {
        return address(this).balance;
    }
  • The amount of ether deposited into the contract also needs to be added to the depositor’s individual Bank account balance (recorded in the mapping). This internal accounting adjustment is crucial, because the contract needs to keep track of each account holder’s individual share of the total contract balance in order to ensure that users cannot withdraw more ether than they are entitled to.

It’s important to understand that when a user deposits ether into the Bank contract, they are transferring ether from one address (e.g. their external wallet address) to the Bank smart contract address. They still “have” the same amount of ether, but the amount transferred is now part of a different Ethereum account balance — it’s included within the Bank contract’s ether balance.

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

1 Like

No… the "from" address is the Bank contract address. This is the address which is emitting the event.

The "to" address is called "to" because that’s the name you’ve given to the second event argument…

… and this event’s emit statement references msg.sender as the "to" address i.e. the caller of the deposit function — the depositor …

The emitted event data is in the “args” property value (an object) of the event log, and is repeated …

[ { 
    "from": "0x7EF...B47",     // Smart contract address
    "topic": "0x451...f7f", 
    "event": "Log",            // Event name
    "args": {  // Logged data, according to the defined event arguments
               // Same data given twice (i) by index (ii) by argument name 
        "0": "1000",          // Amount deposited (always in wei)
        "1": "0x5B3...dC4",   // Depositor's address
        "amount": "1000",     // Amount deposited (repeated)
        "to": "0x5B3...dC4"   // Depositor's address (repeated)
    } 
} ]

Hi @chen,

<address payable>.transfer(uint amount)

This is an predefined address member (or method) in Solidity. It automatically deducts the amount argument’s value (always in wei) from the contract address balance, and transfers it to 
<address payable> i.e. to the address the method is called on, which in our code is msg.sender

msg.sender.transfer(amount);

So, in other words, the wei amount is transferred from the contract address to the external address which calls the withdraw function.

I hope that makes things clearer, but just let me know if you have any further questions about any of these points :slight_smile:

In our withdraw function,
The amount is sent from msg.sender to msg.sender?
In other words, the amount is sent to itself?

Hi @chen,

This is explained in more detail in the reply I’ve just sent you in the Transfer Assignment discussion topic

To summarise…

The address calling the withdraw function (msg.sender) is requesting that the Bank contract transfers/sends the ether amount from the Bank contract address to their own external address (let’s say their wallet).

Hi @gaio,

No … it’s not the transaction ID … that appears separately at the top of the whole transaction receipt as the  transaction hash  (below status).

"topic" is the hash of the event name and its parameter types. This is known as the event signature e.g.

// For the event ....
event Transfer(address indexed from, address indexed to, uint256 amount);

// "topic" is the Keccak-256 hash of ...
Transfer(address, address, uint256)

Yes …

When a transaction emits an event, there can actually be from 1 to 4 topics stored in an Ethereum log record on the blockchain:

  • The first topic is the hash of the event name and its parameter types (as descibed above)

  • The other 3 topics will only be generated for any of the emitted event’s parameters which are indexed — one topic for each indexed parameter — which is why the maximum number of parameters that can be indexed in a single event is 3

It is the topics which enable event data (stored in log records on the blockchain) to be searched, or listened for, according to specified parameters. For example, if none of the parameters are indexed, I think you can only search or listen by event name (e.g. all Transfer events). However, with my Transfer event example with 2 indexed address parameters …

event Transfer(address indexed from, address indexed to, uint256 amount);

… you could also search and listen for only those transfers (i) from a specific sender’s address (ii) to a specific recipient’s address, or even (iii) only those with both a specific sender’s address and a specific recipient’s address; but as the amount parameter isn’t indexed, you couldn’t search or listen for transfers of a specific amount.

I’ve tested calling a function which emits an event with indexed parameters, in Remix, but still only one topic appears in the transaction receipt logs. This is probably because the transaction receipt generated in Remix is what has been selected from the encoded data for presentation in the frontend in a simplified, human-readable format. In the following article (which includes some nice diagrams) you can read more detail about “topics” and how the other non-indexed event data is stored on the Ethereum blockchain in log records, how the EVM generates these for emitted events, and also about the other cost and data-type/structure factors that need to be considered (as well as searchability) when deciding which parameters to declare as indexed.

https://medium.com/mycrypto/understanding-event-logs-on-the-ethereum-blockchain-f4ae7ba50378

The following article is also quite a nice introduction and overview of the contents of transaction receipts in Remix, and how they are generated:

https://medium.com/remix-ide/the-anatomy-of-a-transaction-receipt-d935aacc9fcd

And finally — despite having been written 5 years ago — the following article provides some good background information about events and logs in general:

https://media.consensys.net/technical-introduction-to-events-and-logs-in-ethereum-a074d65dd61e#.7w96id6rs

Happy reading :grin:

Two additional lines are needed:

  1. Add a require function so that the user cannot withdraw more than the balance that is recorded internally on the contract
  2. Make sure that after the withdrawal the amount has been deducted from their balance
    function withdraw(uint256 amount) public returns (uint256) {
        require(amount <= balance[msg.sender]);
        msg.sender.transfer(amount);
        balance[msg.sender] -= amount;
    }
1 Like

Hi @AU-Stephen ,

You have added both of the essential lines of code needed to solve the problem with the withdraw function :ok_hand:

However, notice that the withdraw function header also includes returns(uint) . This is not mandatory to include, and the function can still operate effectively without returning a value. But as you’ve included it in your function header, you should also include a return statement in your function body. You should have got a yellow/orange compiler warning about this.

In fact, best practice is to modify the contract state for the reduction in the individual user’s balance…

balance[msg.sender] -= amount;

before actually transferring the funds out of the contract to the external address…

msg.sender.transfer(amount);

This is to prevent what is known as a re-entrancy attack from occuring after the transfer, but before the user’s individual balance (effectively, their entitlement) is reduced to reflect this operation. You’ll learn more about this type of attack, and how the above order of operations helps to prevent it, in the courses which follow this one.

Just let me know if you have any questions :slight_smile:

1 Like

Thanks for the feedback, I’m revisiting the code I wrote. Yes you are right there is a yellow compiler warning about this, I didn’t know what to make of the message as I was coding but it makes sense after your explanation.

That’s interesting about the re-entrancy attack. I’m excited to learn more about it in the following courses. I’ll keep in mind to adjust the user’s balance before completing any sort of transaction for any future excercises. :slight_smile:

1 Like