Solidity Payable Functions - Discussion

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

i’m extremely confused when it comes to troubleshooting my interface code, which doesn’t want to stop reverting…

as part of this troubleshooting, i’m trying to go back to the payable and transfer video lessons in order to make sure i have the basics down.
However, at lines 27 and 36 of Filip’s code (e.g. at time point 3:06), seem not to be compatible (the former requests withdrawal to an address==msg.sender, which the latter explicitly forbids); is it that “transfer” is a method of addresses (“is a member of the address object”) independent of the “transfer” function starting on line 34? OR am i missing something in interpreting the code?

Hi @B_S,

Which video are you referring to? Can you give me the precise lesson? I’m not sure if you’re referring to the video where we code an interface in Bank for our Government contract, or an earlier one going through the theory of payable functions.

In the meantime, I think maybe this will help…

msg.sender.transfer(amount);

This line of code isn’t calling our smart contract’s transfer() function. To do that we wouldn’t call it on an address, and we would need to call it with two arguments (a recipient address as well as the uint amount) e.g.

transfer(recipient, amount);

Instead, what we have in the withdraw function is an address member, which is what you are referring to here …

This particular address member is a special type of function. It is pre-defined within Solidity’s syntax, and is similar to a method in JavaScript. This is the syntax…

<address payable>.transfer(uint amount)

…meaning that transfer must be called on a payable address with a single uint argument. The uint value will always be the amount of wei which is deducted from the smart contract’s ether balance, and transferred to the external address which transfer is called on.

The caller of the withdraw function is msg.sender , so because the transfer method (address member) is called on msg.sender using dot notation, that’s why it will transfer the withdrawal amount to the caller’s external address.

ah! sorry about that; i said payable and transfer, but i should’ve specified (and probably quoted) “transfer” video/lesson.

i think you’ve answered my question (“transfer” is akin to a solidity method), and your points mostly make sense, but i do have a couple remaining questions:
if i understand your second and third code snippets correctly, calling just “transfer(addy, amt)” from anywhere within the contract sends the contract’s own funds, i.e. it seems to serve as a withdraw function from the point of view of ‘recipient’; however, in the first snippet, --“msg.sender.transfer(amount)”-- is ‘transfer’ from msg.sender to the contract in which it’s called? if not, then from whom (to msg.sender) if not contract?

on another note: is an official list of object members kept somewhere? or is that cumbersome enough that it’s not catalogued? i haven’t googled it, but i’m more wondering how complex / evolving it is if you have an opinion on it; i worked in matlab for a while, and they have function help as a call to “help [function]”, which gives its usage, inputs/outputs the code itself, etc, but javascript seemed not to have it, and i wonder about solidity now too…

1 Like

Hi @B_S,

Yes

No …

If this line of code were to call the transfer() function (not the transfer method) from within another function in the Bank contract, neither the contract balance nor any external address balances would be affected. The contract balance only changes when ether either enters or leaves the contract, for example, when the deposit function or the withdraw function is called. The transfer() function performs an internal transfer (i.e. within the contract) between two individual users. All that needs to change is the share of the total contract balance each user is entitled to, which is done by what is effectively an internal accounting adjustment…

  • The sender’s individual balance is looked up in the mapping (using msg.sender as the key) and reduced by the value of the amount parameter;
  • The recipient’s individual balance is looked up in the mapping (using the value of the recipient parameter as the key) and increased by the value of the amount parameter.

The recipient address would now be entitled to withdraw more ether from the contract, but it would still have to call the withdraw() function to transfer any of this additional ether out of the contract and add it to its external address balance

FROM the contract address balance (built in to the pre-defined functionality of the transfer method)
TO the external address balance referenced by msg.sender

This line of code, which implements Solidity’s pre-defined transfer method, is called when the withdraw() function is executing. It deducts the value of the amount parameter (input into the withdraw function in wei) from the contract balance, and increases the external address balance of msg.sender (whichever address has called the withdraw function) by the same amount. This functionality is pre-defined for us by the syntax…

<address payable>
// external address receiving wei transferred out of contract

uint amount 
// wei amount deducted from contract balance / added to external address balance

All of this information is in the Solidity Documentation
When you first start learning, the documentation can seem extremely dense. As the terms used for different elements of the syntax can often be different to what you are used to from other languages, it can be hard at first to know where to look and what to search for. Here are some helpful lists which I think is the sort of thing you’re looking for. They come under a general heading of Global Variables and Functions, which I don’t think is the most helpful, especially as some are what we would consider methods… but anyway, here you go…

https://docs.soliditylang.org/en/latest/units-and-global-variables.html#members-of-address-types

https://docs.soliditylang.org/en/latest/units-and-global-variables.html#contract-related

https://docs.soliditylang.org/en/latest/units-and-global-variables.html#block-and-transaction-properties

There is also a Cheat Sheet which contains pretty much the same information, but organised differently. Amongst other useful lists, it includes a Global Variables section — but which in reality is an assortment of syntax, including methods / address members …

https://docs.soliditylang.org/en/latest/cheatsheet.html#global-variables

1 Like

ah! got it!

totally makes sense: double boom!

to your last point: yeah, i followed up on those links in your other comments that i hadn’t seen, which were exactly for what i was looking.

again thanks for your thorough explanations; i’ll definitely be back for the 201 course at some point

1 Like

My Solution:

when i use the below code, I get this message:

transact to Bank.withdraw errored: VM error: revert.

revert
The transaction has been reverted to the initial state.
Reason provided by the contract: “Balance not sufficient”.
Debug the transaction to get more information.

    function withdraw (uint amount) public returns (uint) {
        require(balance[msg.sender]>=amount, "Balance not sufficient");
        payable(msg.sender).transfer(amount);
    }

I think this is the purpose. We don’t want to allow withdraw any amount of ether we don’t have in our balance. Require function will throw error.

1 Like

Hi @skawal22,

Am I right to assume that this solution is an earlier version of the final one you posted for the Transfer Assignment, which I’ve already given you feedback for?

Correct … If the address calling the withdraw function has an individual balance in the contract (recorded in the mapping) which is less than the amount requested for withdrawal, then the require() statement will fail and revert the transaction. We want this to happen in order to prevent a user withdrawing more than their share of the total contract balance (i.e. from effectively stealing other users’ funds). We know which specific require() statement has triggered revert, because the error message included as its 2nd argument is included within the error message displayed in the console.

Calling this function with a withdrawal amount, before having deposited any ether by calling the deposit() function with the same address, will always generate this error message, because the address doesn’t yet have any deposited ether to withdraw.

And as I’m sure you’ve already realised, for the require() statement to perform its check correctly, you need to add an additional line of code to reduce the user’s individual balance in the mapping by the same amount as the ether transferred out of the contract and added to the user’s external address balance. You included this in the other solution you posted (as shown above).

Just let me know if you still have any questions about this.

Hi @jon_m, I mistakenly posted this one here. Yes you’re right, its an earlier version of the final one for the Transfer assignment.

Thanks,
Sk

1 Like

Good day Filip and all,

I’m struggling to figure out the connection between the different accounts in solidity. for example, when I invoke the getAddress function, I’m getting the address of the account in box ‘1’. i.e. the msg.sender is the account in Box1. Does this mean that the account in Box 1 is actually ‘calling’ the getAddress function ?

Next question…

If I write the following function to transfer some Eth:

    function getMoney() external payable {

    }

I see that the amount in box 1 diminishes by the value in enter in the ‘VALUE’ box. but where does this Eth go to ?

If its going to the contract how can I check this value and transfer it back to the account shown in Box 1 ?

thank you in advance!

1 Like

Hi @Azmil_K,

… and welcome to the forum! :slight_smile:

Yes … you can change the address showing in the Account field (your “Box 1”) using the dropdown, to simulate different external addresses calling specific functions in your smart contract.

Ether/wei values sent to a function marked payable are automatically added to the contract address balance.

address(this).balance  will give you the contract balance. You can add a getter so that any external address can check this balance e.g.

function getContractBalance() public view returns(uint) {
    return address(this).balance;
}

You also need to keep a record of each individual user address’s share of the total contract balance in a mapping. Effectively, this records and tracks each user’s entitlement to withdraw funds from the contract. We show you how to start doing this, with ether transferred into the contract, in one of the lectures in this section of the course, using the deposit() function.

This is the withdraw() function task we give you in the Transfer Assignment near the end of this section of the course. Continue with the video lectures, complete the Transfer Assignment, and then, if you still have questions about how an external address can withdraw the ether they have deposited in the contract, let me know and I’ll be glad to help you further.

Also, don’t forget to post your solutions to all of the assignmnets, here in the forum. A link to where you need to post each of your solutions is provided in the relevant lesson of the course. This is important, because we will review your solutions and, where necessary, provide you with useful feedback. The assignments you should have already completed by the beginning of the Payable Functions section of the course are the Data Location Assignment and the Events Assignment.