Transfer Assignment

Great solution @RyanYeap :muscle:

You have added both of the essential lines of code needed to solve the problem with the withdraw function, and your statements are also in the correct order within the function body to reduce security risk:

  • Check inputs (require statements)
  • Effects (update the contract state)
  • External Interactions (e.g. transfer method)

Just as you have done, it’s important 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, the share of the total contract balance they are entitled to withdraw) 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. But it’s great you’re already getting into good habits in terms of smart contract security :+1:

You are also right that it’s not necessary to return the updated balance, and so returns(uint) can be removed from the function header. Besides, we do already have the getBalance() function which can be called separately to consult the individual account holder’s updated balance after the withdrawal.

Yes … well done for resolving this yourself :+1:

msg.sender is payable by default in versions of Solidity prior to v0.8, where it doesn’t need to be explicitly converted (which is what you were expecting). However, from v0.8 msg.sender is non-payable by default, and so when it needs to refer to a payable address — such as here, when the transfer method is called on it — it needs to be explicitly converted using payable()

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

2 Likes
    function withdrawl(uint amount) public returns (uint) {
        require(balance[msg.sender] >= amount);
        balance[msg.sender] -= amount;
        msg.sender.transfer(amount);
        return amount;
    }

I found out that the
balance[msg.sender] -= amount;
was needed by messing around and to my shock being able to take out more. You can have the require all you want but if you never actually update their balance in the balance mapping :stuck_out_tongue:

I see I am not the only one to have found that out and that luckily I put it in the correct spot to stop a re-entrancy attack as mentioned by jon_m above. Makes sense and really enjoyed the extra reasoning.

1 Like
function withdraw(uint amount) public returns (uint){
    require(balance[msg.sender] >= amount);
        balance[msg.sender] -= amount;
        msg.sender.transfer(amount);
    return balance[msg.sender];
}
1 Like

Great solution, testing, reasoning, research and analysis @ekr990011 :muscle: :slight_smile:

1 Like
function withdraw(uint amount) public returns (uint){
    require(balance[msg.sender] >= amount, "not enough to to withdraw");
    balance[msg.sender] -= amount;
    msg.sender.transfer(amount);
    return balance[msg.sender];
}```
1 Like

An excellent solution @Scott815 :muscle:

You have added all of the lines of code needed to solve the problem with the withdraw function, and your statements are also in the correct order within the function body to reduce security risk:

  • Check inputs (require statements)
  • Effects (update the contract state)
  • External Interactions (e.g. transfer method)

Just as you have done, it’s important to modify the contract state for the reduction in the individual user’s balance…

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

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, the share of the total contract balance they are entitled to withdraw) 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. But it’s great you’re already getting into good habits in terms of smart contract security :+1:

Don’t forget to post your solution to the Events Assignment here. This is the assignment from the Events video lecture, which is near the end of the Additional Solidity Concepts section of the course.

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

I used the require to make sure the balance of the msg.sender was greater than or equal to the amount he wanted to send

function withdraw(uint amount) public returns (uint) {
    require(balance[msg.sender] >= amount,'');
    msg.sender.transfer(amount);
}

Hi @OzzieAl ,

The require statement you’ve added is correct, although it would be better to add an error message to the second argument, rather than leave it as an empty string. You will see in the Remix terminal that having an empty string results in a confusing error message …

revert
        The transaction has been reverted to the initial state.
Reason provided by the contract: "".

You should include a reason, or not add a second argument to the require statement at all.


However, your withdraw function is also missing an essential line of code …

If you deploy your contract, make a deposit, and then call your withdraw function as it is, you will notice that the caller’s external address receives the requested withdrawal amount, but their balance in the mapping is not reduced! This means that they can make repeat withdrawals — each one no greater than their initial balance, which isn’t being reduced — up to the total amount of ether held in the contract (the sum of all of the individual account holders’ balances). So, I’m sure you can see why this is a very serious bug!

msg.sender.transfer(amount)  transfers ether from the contract address balance to the caller’s external address, but it doesn’t adjust the individual user balances in the mapping. These balances perform an internal accounting role, and record each user’s share of the contract’s total ether balance. So, just as we increase a user’s individual balance when they deposit ether in the contract, we need to do the opposite whenever they make a withdrawal.


Also, notice that the withdraw function header includes returns(uint) . This is not mandatory to include, and the function can still operate effectively without returning a value. But as it’s been included in the function header, you should also include a return statement in the function body. The compiler will have given you an orange warning about this.


Once you’ve made the above modifications to your code, have a look at this post which explains the importance of the order of the statements within your withdraw function body.

And don’t forget to post your solution to the Events Assignment here. This is the assignment from the Events video lecture, which is near the end of the Additional Solidity Concepts section of the course.

Let me know if anything is unclear, if you have any questions about any of these points, or if you need any further help correcting your solution :slight_smile:

//SPDX-License-Identifier:

pragma solidity 0.8.7;

contract myFirstContract {

mapping (address => uint) balance;
address owner;

event depositDone (uint amount, address depositedTo);
event balanceTransfered (uint amountTransfered, address transferedTo);

modifier onlyOwner {
require(msg.sender == owner);
_; // run the function
}

constructor(){
owner = msg.sender;
}

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

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

require (balance[msg.sender] >= amount, "Balance not sufficient!");

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

balance[msg.sender] -= amount;
return balance[msg.sender];

}

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

function transfer(address recipient, uint amount) public {

require (balance[msg.sender] >= amount, "Balance not sufficient!");
require (msg.sender != recipient, "Don't send money to yourself!");

uint previousSenderBalance = balance[msg.sender];

_transfer(msg.sender, recipient, amount);
emit balanceTransfered (amount, recipient);

assert(balance[msg.sender] == previousSenderBalance - amount);

}
function _transfer(address from, address to, uint amount) private {

balance[from] -= amount;
balance[to] += amount;

}

}

1 Like

why is it that when you deposit Ether into a payable function, it sometimes the funds dont get subtracted from your address and added to the contract, but decides to work when you try it again a few times at random. Also, when depositing say 20 ether into a contract, sometimes less than 20 ether ends up being deducted from my address and added to the smart contract. Is this a feature within remix?

Hi @Marta_Tofan,

(1) Withdraw function

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

Now have a look at this post which explains an important security modification that should be made to the order of the statements within your withdraw function body.

(2) Transfer Event (from the Events Assignment)

Your transfer event and corresponding emit statement are both accurately coded, and the emit statement will log the data when the transfer() function is successfully executed.

A couple of observations…

  • An emit statement should be placed at the end of a function, after the event itself has occurred, but before a return statement if there is one. Generally speaking, it’s probably also better to place an emit statement after an assert statement if there is one.

  • Where the deposit() function executes a transaction that only involves 1 user address (the depositor), the transfer() function executes a transaction that involves 2 user addresses (the sender and the recipient). So, it would be useful to include the sender’s address, as well as the recipient’s address, within the data emitted for the transfer event.


Please, format all your code when posting it, instead of just parts of it. This will make it clearer and easier to read. It will also make it easier to copy and paste if we need to deploy and test it. You just need to make sure that all your code is placed between the two sets of 3 back ticks i.e.

```
all your code
```
Follow the instructions in this FAQ: How to post code in the forum

Let me know if you have any questions :slight_smile:

1 Like

Hi @Adrian1,

The same amount of Ether, Finney, Gwei or Wei that you enter into the Value field will always be deducted from the sender’s address balance that is showing in the Account field, near the top of the Deploy & Run Transactions panel in Remix. Make sure you set the dropdown next to the Value input box to the appropriate currency unit for the amount you want to send e.g.

20 Ether … will deduct 20.0 Ether from the sender’s address balance
2 Finney … will deduct 0.002 Ether from the sender’s address balance
25 Gwei … will deduct 0.000000025 Ether from the sender’s address balance
275 Wei … will deduct 0.000000000000000275 Ether from the sender’s address balance

If you are sending units of Wei instead of Ether, this could be why you aren’t noticing the change in the address balance, because it’s only affecting the last decimal places.

When you call the deposit() function, the amount of Ether that is sent to the contract will be added to the contract address balance. If you add the following function to your contract, you can call it to retrieve the current contract address balance (instead of the individual user balances stored in the mapping).

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

The value returned from this function, and also from the getBalance() function, will always be in units of Wei e.g.

Return value of …

20000000000000000000  =  20 Ether
   2000000000000000  =   2 Finney
        25000000000  =  25 Gwei
               275  =  275 Wei

Let me know if anything is still unclear, or if you have any further questions.

function withdraw(uint amount) public returns (unit)
{
    require(balance[msg.sender] >= amount);
    msg.sender.transfer(amount);
    balance[msg.sender] -= amount;

    return balance[msg.sender];
}
1 Like
pragma solidity 0.8.7;

contract Bank {

    mapping(address => uint)balance;

    address owner;

    event depositDone (uint _amount, address _depositedTo);
    event amountWithdrawn (uint _amount, address _withdrawnFrom);

    constructor(){
        owner = msg.sender;
    }

    modifier onlyOwner {
        require(msg.sender == owner);
        _;
    }

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

    function withdraw (uint amount) public returns (uint) {
        require(balance[msg.sender] >= amount, "You cannot withdraw more than what you have!");
        
        balance[msg.sender] -= amount;
        payable(msg.sender).transfer(amount);

        emit amountWithdrawn(amount, msg.sender);
        return balance[msg.sender];
    }

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

    function transfer(address recipient, uint amount) public {
        
        require(msg.sender != recipient, "You cannot transfer to yourself!");
        require(balance[msg.sender] >= amount, "Balance insufficient.");

        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] = balance[from] - amount;
        balance[to] = balance[to] + amount;
    }

}
1 Like

Hi @oioii1999,

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

Now have a look at this post which explains an important security modification that should be made to the order of the statements within your withdraw function body.

And don’t forget to post your solution to the Events Assignment here. This is the assignment from the Events video lecture, which is near the end of the Additional Solidity Concepts section of the course.

Let me know if you have any questions :slight_smile:

Great solution @karendj and, overall, a very well coded contract :muscle:

You have added all of the lines of code needed to solve the problem with the withdraw function, and your statements are also in the correct order within the function body to reduce security risk.

Your withdraw event and corresponding emit statement are also both well coded, and a nice addition. The emit statement is in the correct position in the withdraw() function body and will log appropriate data when the withdraw() function is successfully executed.

Keep up the great coding! :smiley:

1 Like

Thanks for your advice! @jon_m

I have updated the code for the withdraw function as below, and submitted the event assignment in this post. Please check it at your convenience. :slightly_smiling_face:

function withdraw(uint amount) public returns (unit)
{
    require(balance[msg.sender] >= amount);
    balance[msg.sender] -= amount;
    msg.sender.transfer(amount);
    
    return balance[msg.sender];
}
1 Like
// SPDX-License-Identifier: GPL-3.0

pragma solidity >= 0.8.0;

contract bank{

    mapping(address => uint) balance;
    address owner;

    event depositDone(uint amount, address indexed to);

    event transferencedone(uint amoun, address indexed to, address indexed from);



    constructor(){
        owner = msg.sender;
    }

    modifier onlyOwner{
        require(msg.sender == owner);
        _;
    }

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

    function withdraw(uint amount) public returns(uint){
        require(balance[msg.sender] >= amount, "Saldo insuficiente");
        msg.sender.transfer(amount);
    }

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

    function transfer(address recipient, uint amount) public {
        //check balance del msg.sender
        require(balance[msg.sender] >= amount, "Saldo insuficiente");
        require(msg.sender != recipient, "No envies dinero a ti mismo");

        //creando variable que permite revisar el estado de la transaccion al finalizar la misma
        uint previousSenderBalance = balance[msg.sender];


        _transfer(msg.sender, recipient, amount);
        
        //emitiendo events logs cuando alguien haga una transferencia
        emit transferencedone(amount, msg.sender, recipient);

        //usando assert para verificar si el codigo esta correcto
        assert (balance[msg.sender] == previousSenderBalance - amount);
    }

    //implementando visibilidad, funcion privada

    function _transfer(address from, address to, uint amount) private {
        balance[from] -= amount;
        balance[to] += amount;
    }
}

To make sure that the user can not withdraw more then their balance
you check the balance for message sender but withdraw from owner.

function withdraw(uint withdrawAmount) public returns (uint) {
    require(balances[msg.sender] >= withdrawAmount);
    balances[msg.sender] -= withdrawAmount;
    emit LogWithdrawal(msg.sender, withdrawAmount, balances[msg.sender]);
    return balances[msg.sender];
}

Hi @valleria,

Sorry for the delay in giving you some feedback!

withdraw() function

(1) You should have found that your contract code didn’t compile, and so couldn’t be deployed…

Your pragma statement is declaring that the code should be compiled based on Solidity v0.8 syntax. That’s absolutely fine, but then you also need to bear in mind that this course material is based on v0.7 syntax. The compiler will always generate an error when your syntax needs to be changed for v0.8, and the error message will tell you why, and often what you should change it to. In this case, you will have got a red compiler error for this line of code…

In order to receive Ether, an address needs to be payable. Prior to Solidity v0.8, msg.sender is payable by default and so doesn’t need to be explicitly converted. However, from v0.8, msg.sender is non-payable by default, and so when it needs to refer to a payable address, it has to be explicitly converted. One of the ways to do this is using  payable() e.g.

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

If you make this modification, you will notice that the compiler error disappears.


(2) The require statement you’ve added is correct, but your withdraw function is also missing an essential line of code …

If you deploy your contract, make a deposit, and then call your withdraw function as it is, you will notice that the caller’s external address receives the requested withdrawal amount, but their balance in the mapping is not reduced! This means that they can make repeat withdrawals — each one no greater than their initial balance, which isn’t being reduced — up to the total amount of Ether held in the contract (the sum of all of the individual account holders’ balances). So, I’m sure you can see why this is a very serious bug!

payable(msg.sender).transfer(amount)   transfers Ether from the contract address balance to the caller’s external address, but it doesn’t adjust the individual user balances in the mapping. These balances perform an internal accounting role, and record each user’s share of the contract’s total Ether balance. So, just as we increase a user’s individual balance when they deposit Ether in the contract, we need to do the opposite whenever they make a withdrawal.


(3) 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 it’s been included in the function header, you should also include a return statement in the function body. Once the red compiler error (described above) has been corrected, the compiler will still give you an orange warning about this issue.

(4) Once you’ve made the above modifications to your code, have a look at this post which explains the importance of the order of the statements within your withdraw function body.


Transfer Event (from the Events Assignment)

Your transfer event declaration is well coded, and the corresponding emit statement, which you’ve added to the transfer() function body, will log data when the transfer() function is successfully executed.

However, you are emitting the msg.sender address as the event’s to address parameter, and the recipient address as the event’s from address parameter. Can you see that you’ve got these the wrong way round? The position of each value in the emit statement must be the same as that of its corresponding parameter in the event declaration, in terms of both data type and name. If the data types don’t correspond, then the contract won’t compile, but if the names are mixed up, then the affected data will be logged against potentially misleading identifiers (which is what will happen with your solution).

But you have referenced amount in the correct position in your emit statement :ok_hand: :sweat_smile:

Just one other observation …
In general, an emit statement is probably better placed after an assert() statement.


Let me know if anything is unclear, if you have any questions about any of these points, or if you need any further help to correct your solution code :slight_smile:

1 Like