Transfer Assignment

Hey! This is my solution.
If the msg sender is trying to withdraw more than what he has in the balance, he will still be able to withdraw 100% of what the bank owes him. It raises just if he has an empty balance.

pragma solidity 0.7.5;

contract SimpleBank {

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

  constructor(){
    owner = msg.sender;
  }

  modifier onlyOwner {
    require(msg.sender == owner, "Owner only");
    _;
  }

  /***
  *  Events
  */

  event balanceChanged(
    address indexed caller, // can be queried later
    address indexed target, // can be queried later
    uint amount,
    bool increment // true == add, false == remove
  );
 
  event depositReceived(
    address indexed sender,
    uint amount
  );

  event withdrawalProcessed(
    address indexed recipient,
    uint amount
  );


  /**
  *  Getters
  */

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

  function getBalance(address addr) public view
    returns(uint){
    return balances[addr];
  }


  /**
  *  Single balance modifiers
  */

  function _removeFromBalance(address recipient, uint amount) private
    returns(uint){
    if  (amount > balances[recipient]){
      uint amt = balances[recipient];
      balances[recipient] = 0;
      emit balanceChanged(msg.sender, recipient, amt, false);
      return amt;
    } else {
      balances[recipient] -= amount;
      emit balanceChanged(msg.sender, recipient, amount, false);
      return amount;
    }
  }

  function _addToBalance(address recipient, uint amount) private
    returns(uint){
    balances[recipient] += amount;
    emit balanceChanged(msg.sender, recipient, amount, true);
    return amount;
  }

  function deposit() public payable
    returns(uint){
    emit depositReceived(msg.sender, msg.value);
    uint deposited = _addToBalance(msg.sender, msg.value);
    return balances[msg.sender];
  }

  function withdraw(uint amount) public
    returns(uint){
    require(amount > 0, "Amount too small");
    uint senderPreBalance = balances[msg.sender];
    require(senderPreBalance > 0, "Balance empty");
    amount = _removeFromBalance(msg.sender, amount);
    msg.sender.transfer(amount);
    emit withdrawalProcessed(msg.sender, amount);
    return balances[msg.sender];
  }


  /**
  * From - to
  */

  function _transfer(address from, address to, uint amount) private
  {
    balances[from] -= amount;
    emit balanceChanged(msg.sender, from, amount, false);
    balances[to] += amount;
    emit balanceChanged(msg.sender, to, amount, true);
  }

  function transfer(address recipient, uint amount) public
  {
    require(amount > 0, "No 0 amount transfer");
    require(msg.sender != recipient, "No self transfer");
    require(balances[msg.sender] >= amount, "Insufficient balance");

    uint preSenderBalance = balances[msg.sender];
    uint preRecipientBalance = balances[recipient];

    _transfer(msg.sender, recipient, amount);

    assert(balances[msg.sender] == preSenderBalance - amount);
    assert(balances[recipient] == preRecipientBalance + amount);
  }
}
1 Like
    event withdrawComplete(uint amount, address indexed withdrawnFrom);

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

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

        emit withdrawComplete(amount, msg.sender);

        return balance[msg.sender];
    }
1 Like

image Seems to work through testing, anyone have any suggestions on where to insert emit for event logging THANKS

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

Nice solution @JayM700 :ok_hand:

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

Your withdraw event and corresponding emit statement are also both well coded. 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.

The additional two lines you’ve included for the assert() statement are also well coded.

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

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

Hi @casadcode,

The require statement and the return statement you’ve added to your withdraw function are both correct, and the additional two lines you’ve included for the assert() statement are also well coded :ok_hand:

But you are missing one very important line of code …

If you deploy your contract, make a deposit, and then call your withdraw function, you will notice that the caller’s address receives the requested withdrawal amount, but their balance in the mapping is not reduced (call getBalance to check this). I’m sure you can see that this is a very serious bug because, even though there is a limit on each separate withdrawal, this limit is never reduced to reflect each withdrawal. This means that a user can withdraw more than their own individual balance (the share of the total contract balance to which they are entitled), and effectively steal other users’ funds.

msg.sender.transfer(amount)   transfers ether from the contract address balance to the caller’s external wallet 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 (effectively, each user’s entitlement to withdraw ether from the contract). 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.

Once you’ve had a go at correcting your code for this, have a look at this post which explains the importance of the order of the statements within your withdraw function body.

After completing this assignment, have a really good think about why your testing didn’t pick up on the missing line of important code discussed above. Think about how you can make your testing more thorough, so that it identifies this kind of issue :muscle:


An emit statement should be placed at the end of a function, after the operations associated with the event itself (which it is logging data for) 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.

Don’t forget to do the Events Assignment and post your solution here. This is the assignment from the Events video lecture, which is near the end of the Additional Solidity Concepts section of the course, and it will help you to practise coding event declarations, emit statements, and where to position them.


And finally, a friendly reminder not to post screen shots, because we can’t copy and paste your code in order to execute and test it, if we need to. Instead, follow the instructions in this FAQ: How to post code in the forum

Let me know if you have any questions about any of these points :slight_smile:

Hi @Cosmin_Fechet,

I think you need to slow down a bit, and make sure you test your code to see if it actually works and does what it should :sweat_smile:

There are lots of issues with this solution. I’ll point you in the right direction, and then see if you can correct it yourself :muscle:

(1) The condition in your require() statement does the opposite of what it should. We want to make sure a user can’t withdraw more than their individual balance, not force them to only withdraw more than their share!

(2) Once you’ve corrected the require() statement, you will still find that every time you call the withdraw() function, assert() fails and triggers revert. This is because the condition in your assert() statement can never be true. Can you see why?

Once, you can see the problem, comment out these last two lines, and continue with point 3. We’ll come back and correct these two lines of code associated with assert() at the end, because you need to make another important modification first.

(3) Your withdraw function is missing an essential line of code …

If you now deploy your contract (with just the two statements you should currently have in your withdraw function body), make a deposit, and then call your withdraw() function, you will notice that the caller’s address receives the requested withdrawal amount, but their balance in the mapping is not reduced (call getBalance to check this). I’m sure you can see that this is a very serious bug because, even though there is a limit on each separate withdrawal, this limit is never reduced to reflect each withdrawal. This means that a user can withdraw more than their own individual balance (the share of the total contract balance to which they are entitled), and effectively steal other users’ funds.

msg.sender.transfer(amount) transfers ether from the contract address balance to the caller’s external wallet 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 (effectively, each user’s entitlement to withdraw ether from the contract). 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.

(4) 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. The compiler will have given you an orange warning about this.

(5) Now let’s return to the two lines of code I asked you to comment out. Your local variable previousBalance was saving the user’s balance after the transfer, not before. This didn’t make any difference because you hadn’t included the essential line of code (mentioned above) needed to reduce this balance for the withdrawal amount. However, after adding this essential line of code (in point 3) you’ll now need to reposition this local variable declaration and assignment, so that the assert() statement checks an invariance that should always be true, instead of a condition that can never be true :wink:

(6) Once you’ve made all of the above changes and tested your withdraw() function to ensure it is working as it should, have a look at this post which explains the importance of the order of two of the statements that you should now have in your withdraw() function body. Make sure you have these statements in the right order to guard against re-entrancy attacks and keep your smart contract secure.

Let me know if anything is unclear, if you have any questions, or if you need any help in correcting your withdraw() function :slight_smile:

Hi Federico,

Apologies for the delay in giving you some feedback on this assignment.

Your solution is excellent, and I really like how you’ve adapted and been creative with the code, experimenting with your own variations :100:

You’ve solved the problem with the withdraw function (adding your own adaptation — see my comments below), and the following operations are performed in the correct order within the control flow, which reduces 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…

 // if there is sufficient balance to withdraw the whole amount requested
balances[recipient] -= amount;

// if the requested amount is greater than the user's individual balance 
balances[recipient] = 0;

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:


Some additional comments …

(1) Still allowing a withdrawal to go ahead if the amount requested by a user is greater than their individual balance, but reducing the amount actually withdrawn to the total balance the user has available, is an interesting variation. You’ve implemented this very well, and the require() statements that you’ve included instead are appropriate.

However, do you think that this would be appropriate in practice? When someone requests the withdrawal of a specific amount, don’t you think they would rather have the transaction stopped, and be notified that they don’t have sufficient funds, rather than finding out afterwards that the amount they’ve received is less than expected? The transaction can be reverted and the error handled in the front-end user interface in such a way that the user is fully aware of the circumstances, and is given the opportunity to repeat the withdrawal but for a lower amount.

(2) Adding the helper functions _addToBalance() and _removeFromBalance() is a nice implementation of modularity: breaking down a computation and separating out specific chunks of code into their own specialised functions, where they can be reused elsewhere. However, you’ve missed the opportunity to re-use both of these functions in the transfer() function to perform the necessary adjustments to the individual balances of the sender and recipient. Instead of adding another helper function (_transfer), you can call both of the helper functions you already have …

  //_transfer(msg.sender, recipient, amount);
  amount = _removeFromBalance(msg.sender, amount);
  _addToBalance(recipient, amount);

As the _removeFromBalance() function would no longer only be used for withdrawals, you would also need to think of a less specific name for the address parameter than recipient, so that it makes sense for all use cases. However, your if/else logic could still be applied to internal transfers initiated by the transfer() function, allowing them to still go ahead if the transfer amount requested is greater than the caller’s individual balance. Just like with withdrawals, in these cases the amount actually transferred could also be reduced to the total balance available to the caller.

(3) In your deposit() function, you don’t need to assign the value returned from the _addToBalance function call to a local variable, because this value isn’t used and will just be lost anyway when the function finishes executing.

You should have got an orange compiler warning for this, notifying you of an unused local variable.

The _addToBalance() helper function doesn’t need to return any value, and the function call can be made as follows…

function _addToBalance(address recipient, uint amount) private {
    balances[recipient] += amount;
}

function deposit() public payable returns(uint) {
    _addToBalance(msg.sender, msg.value);
    emit depositReceived(msg.sender, msg.value);
    return balances[msg.sender];
}

On the other hand, your _removeFromBalance() helper function does need to return a value, because the amount may need to be changed to the caller’s maximum balance available in the if-statement. Therefore, it’s also correct to assign the return value to the amount parameter in the main function that calls this helper …

Notice in point 2, above, that this line of code could be used in both the withdraw() and the transfer() function.

(4) All 3 event declarations and their corresponding emit statements are well coded, and the emit statements log appropriate data when their respective functions are successfully executed.

Where possible, an emit statement should be placed at the end of a function, after all of the operations associated with the event itself have 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.

So, bearing this in mind, your emit statement in the withdraw() function is in the correct position, but you should reconsider where you place the one in your deposit() function.

Do you think it’s necessary to emit 2 events for each deposit and each withdrawal? Personally, I think it’s an unnecessary duplication of data, and that you could safely remove the balanceChanged event and its corresponding emit statements in the helper functions. You already have sufficient data logged by the depositReceived and the withdrawalProcessed events. We know that the amount associated with a depositReceived event will be an increase to the sender’s individual balance, and that associated with a withdrawalProcessed event will be a reduction in the recipient’s individual balance, without needing the Boolean values to be logged in a separate balanceChanged event.

Removing the balanceChanged event and adding a separate event only for the transfer() function, means that you’ll only need one event emitted at the end of this function as well (which is the most appropriate place for an event, anyway). At the same time, the transfer() function will still be able to make use of the two helper functions to adjust the individual user balances accordingly.

(5) If you need a getter to return a user’s individual balance to anyone, then you no longer need the getter which only returns the caller’s own balance, because the other getter can now be used for both. Remember that the more code a contract contains, the higher the gas cost will be to deploy it. So, we can’t afford to be wasteful by including redundant code.

I hope you find these comments helpful. Let me know if you have any questions, and please do let me know if you disagree with any of my suggestions.

You’re making great progress with Solidity, Federico :muscle: Keep it up!

Hi @jon_m what a in-depth review, thank you so much, I really appreciate!
Regarding your comments:

Yes you’re right, it might be confusing and has definitely its drawbacks (smart contracts which depends upon on that must absolutely take this behaviour in consideration). But this can protect greedy and lazy users (as I am) from bad frontends. Have you ever had to replay (and sadly… rePAY) a transaction just because the UI of the dapp wasn’t rounding properly? That sucks and does really hurt when fees are as high as they’re now on ethereum :joy:

No excuses, you’re 100% right. The private helpers should have been used in transfer too. :ok_hand:

Yeah I saw it, but as I said I’m kind of lazy and I just let that… “just in case I need it for some reason…”
Obviously not for-prod behaviour :grimacing:

You’re right, I had better understood this (and coded accordingly) in the MultiSig final project in which I’ve always put assertions in the helpers and emitted signals at the end of the public function.
I still have to get used to signals, as I still think them more as log functions, when I should threat them as costly signals for other processes. And this require clear vision/planning of what I would expect to integrate/need from the SC itself. It’s a good exercise.

Yes I definetly have to get used to think about gas!

Your comments are really helpful and I would be super happy if you could review the Multi Sig final project with the same spirit: I’m trying to learn as much as I can!

Have a nice day

1 Like

Hey Federico,

Glad you found the comments helpful. What’s good is that, I can see from your reply, it got you thinking and reflecting further on things that you’d already come across yourself. Personally, I’ve always found this reflection, re-working and re-factoring stage really profitable, especially if you’ve already invested so much effort into the original solution, as you obviously do.

I will gladly take a look at your Multisig project and provide you with some feedback. I will try to do that sometime over the next few days :slight_smile:


Point 1

Good point :+1: However, when you build a dapp, I do think the best solution for this would be to handle any rounding issues in the front-end before the actual withdraw() or transfer() function is called. The user’s current balance could be retrieved first by calling the getter (no gas cost for calling a getter), and then performing or alerting the user to any roundings or necessary adjustments to the actual amount they have requested to withdraw or transfer. Once these issues have been addressed (based on current information retrieved from the smart contract), the user can then have an option to accept or reject the adjusted/rounded amount (generated in the front-end). If they accept, then only at this point would the function in the smart contract, which sends the transaction to the network, automatically be called. The user experience would be that of performing just one single operation/transaction, but with a few additional steps in the UI if the original amount they request needs rounding or adjusting. The smart contract function would still contain the necessary require statements as further validation checks, but overall this approach should address the important issue you’ve raised, whilst also keeping gas costs down.

1 Like

Hey! You’re definitely right. Also while attending the 201 course and coming across the EIP20 proposal I’ve found that both txs with amount > balance should throw AND txs with amount == 0 should not throw and emit standard txs.
I will obviously stick to the standards even if the second clause looks a bit strange/waste of gas but maybe there was a reason at that time to ask complaiance to not throw on transfer 0 amounts.

1 Like

This problem can be easily fixed by setting a requirement that the balance is larger than the amount we are withdrawing. It returns the balance after the transfer.

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

An excellent solution @diehlca17 :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…

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:

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:

Hello! This is my solution:
` function withdraw(uint amount) public returns(uint){

require(balance[msg.sender] >= amount, "Not enough balance");
balance[msg.sender] -= amount;
msg.sender.transfer(amount);
emit withdrawedBalance(amount, msg.sender);
return balance[msg.sender];

}`
1 Like

Hello! This is what I came up with for my solution.

function withdraw(uint amount) public returns (uint){
        //Check balance
        require(balance[msg.sender] >= amount, "Insufficient funds");
        //Withdraw amount from balance
        msg.sender.transfer(amount);
        //Deduct amount from balance
        balance[msg.sender] -= amount;
        
        return balance[msg.sender];
    }```
1 Like

An excellent solution @Oprea :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:

Your emit statement also looks good, and it’s in the correct position within the withdraw() function body. However, to be sure the code is correct, we need to see the corresponding event declaration you’ve added. Can you post that as well so we can see the complete picture?

Also, 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:

Nice solution @0xsundown :ok_hand:

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

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.

Once you’ve read the linked post, my following comments (about two of your inline comments) should also make sense …

This line of code withdraws the amount from the contract address balance and transfers it to the external address which the transfer method is called on (in this case, msg.sender, the address calling the withdraw function). The contract’s Ether balance is reduced by the withdrawal amount, and the external address’s ether balance is increased by the same amount.

This line of code reduces the individual user’s balance in the mapping to account for the fact that it’s this particular user who is withdrawing this amount from the contract.
msg.sender.transfer(amount)   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, effectively, the amount each is entitled to withdraw from the contract.

Just let me know if anything is unclear, or if you have any questions :slight_smile:

1 Like
pragma solidity 0.7.5;

contract Bank {

    mapping(address => uint) balance;

    address owner;

    event depositDone(uint amount, address indexed depositedTo);

    event transferDone(uint amount, address toAddress, address fromAddress);

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

    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(amount <= balance[msg.sender]);
        msg.sender.transfer(amount);
    }

    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 transfer money to yourself");

        uint previousSenderBalance = balance[msg.sender];

        _transfer(msg.sender, recipient, amount);

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

        emit transferDone(amount, recipient, msg.sender);
    }

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


}

Hi @simfin94,

The require statement you’ve added is correct, but your withdraw function is 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.

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. 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.

Your transfer event and corresponding emit statement (from the previous Events Assignment) are both correct and well coded. The emit statement is in the correct position in the transfer() function body and will log appropriate data when the transfer() function is successfully executed :ok_hand:

Let me know if anything is unclear, or if you have any questions about any of these points :slight_smile:

This is my code.
I use the modifier to control the balance and stop function during execution to avoid re-entry attacks and I updated the receipt before sending Eth from the contract to the user.

// SPDX-License-Identifier: Leluk911
pragma solidity 0.8.7;
contract Bank_Eth {
    // deposit receipt Eth
    mapping(address=>uint) public receipt;
    // variable modifier 
    bool paused=false;
    // balance eth bank
    uint public balance_Bank = address(this).balance;
    // event register transaction
    event Deposit_bank (address indexed,uint indexed);
    //event register withdrawal
    event withdrawal_bank(address indexed,uint indexed);
    
    // stop contract during execution
    modifier stay_paused{
        require (!paused, "contract in exet");
        paused = true;
        _;
        paused = false;
    }
    // control receipt user after withdrawal
    modifier control_receipt(uint _amount){
        require (receipt[msg.sender]>=_amount, "you deposit is less you require");
        _;
    }

    // function deposit in contract
    function depositEth()payable public stay_paused {
        receipt[msg.sender] += msg.value;
        emit Deposit_bank(msg.sender,msg.value);
    }

    // function withdrawal
    function withdrawalEth(uint _amount)public control_receipt(_amount) stay_paused {
        receipt[msg.sender] -= _amount;
        (bool success,) = msg.sender.call{value:_amount}("");
        require(success,"transaction faill");
        emit withdrawal_bank(msg.sender,_amount);
    }



}
1 Like