Transfer Assignment

Hi @Giupi & @mcgrane5,

Not sure if you guys have already seen, but the issue has now been fixed, and we can now set values to zero and use the delete operator in Remix without running out of gas :sweat_smile:

I’ve tested it, and it now works fine. The issue that was opened in the Remix Project’s repository in GitHub also now confirms that it’s been fixed and the issue is now closed…

https://github.com/ethereum/remix-project/issues/2320#issue-1207367110

So this definitely seems to be a good place to report Remix issues!

1 Like

nicee brillant thanks for sharing jon

1 Like

Hi @Giupi,

Just following up, as promised, after doing some testing …

This is correct… And it’s also worth emphasising that, with a fixed-size array, you will obviously only retrieve one of the zero values I listed, if you reference an index position which is less than the pre-defined fixed size e.g. If the fixed-size array  uint[3] balances  has not yet had any values assigned to it, then referencing: balances[0] , balances[1]  and  balances[2]  will all return zero; but  balances[3] will result in an error.

This is correct

Yes you can … this works in exactly the same way as with a fixed-size array.

This is correct.

Yes, you are right, they are related…
Removing a struct instance from an array using the delete operator is equivalent to assigning a zero value to each of its properties (according to their data types).
You can also use the delete operator on individual properties of a specific struct instance (whether stored in an array or mapping, or not). This will set that property’s value to a zero value, but it will not affect any of the values stored in the other properties of that struct instance.

I hope that clarifies things, and ties up any loose ends. Just let me know if you have any other questions :slight_smile:

pragma solidity 0.7.5;
//"SPDX-License"

contract Bank{

        
        mapping(address => uint) balance;

        address owner ;

        event balanceAdded(uint amount, address indexed depositedTo);
        //indexed permet de rechercher un event grâce au paramètre indexed
        //on peut avoir 3 indexed paramaters dans un event

          event depositDone(uint amount, address indexed depositedTo);
        //indexed permet de rechercher un event grâce au paramètre indexed
        //on peut avoir 3 indexed paramaters dans un event

        event withdrawDone(uint amount, address indexed sendFrom, address indexed sendTo);
 
        constructor(){
                owner = msg.sender;
        }

        

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


        modifier costs(uint _price){
                require(msg.value >= _price);
                _;
        }

         function deposit() public payable returns(uint)  {
                 
                // payable ===> msg.sender pays the contract
                //msg.sender deposits wei in the contract
                //1 eth = 1 * 10 ^18 wei = 1 000 000 000 000 000 000 wei
                uint previousbalance = balance[msg.sender];
                 balance[msg.sender] += msg.value; //just to keep tract of what address the contract must pay
               assert(balance[msg.sender] == previousbalance + msg.value);
                 //when msg.sender deposits money, he has less money in his blockchain wallet
                 //and the balance of the smart contract on the blockchain is added with the deposited money
                 //by msg.sender 
                 
                 emit depositDone(msg.value, msg.sender);
                 return balance[msg.sender];
         
        }

        function withdraw(uint _amount, address payable _toAddress) public payable returns(uint){
                require(msg.sender.balance >= _amount, "insufficient Balance");

                uint fromaddressbalanceBeforeWithdraw = msg.sender.balance;
                uint toAddressBalanceBeforeWithdraw = _toAddress.balance;
                

                _toAddress.transfer(_amount);
                msg.sender.transfer(_amount);

                assert(_toAddress.balance == toAddressBalanceBeforeWithdraw + _amount);
                assert(msg.sender.balance == fromaddressbalanceBeforeWithdraw - _amount);

                emit withdrawDone(_amount, msg.sender, _toAddress);


                return msg.value;

        }

 


        function addBalance(uint _toAdd) public onlyOwner returns(uint) {
               
                balance[msg.sender] += _toAdd;
                emit balanceAdded(_toAdd, msg.sender);
                return balance[msg.sender];

        }


        function getBalance() public view  returns(uint){

                return balance[msg.sender];
        }


        function getContractBalance() public view returns (uint) {

        return address(this).balance;//total of money deposited into the contract

        }

        function transfer(address _recipient, uint _amount) public   {
            //check balance of msg.sender
            require(balance[msg.sender] >= _amount , "Insufficient Balance");
            require(msg.sender != _recipient , "Don't Transfer Money to Yourself") ;


             uint previousSenderBalance = balance[msg.sender];

            _transfer(msg.sender, _recipient, _amount);

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

        }

        function _transfer(address _from, address _to, uint _amount) private{
            balance[_from] -= _amount;
            balance[_to] += _amount;

        }
 
 
}
1 Like

function withdraw() public returns (unit) {

        require(balance[msg.sender] >= amount, “Withdraw amount must be equal to or less than balance amount”);
        balance[msg.sender] -= amount;

        msg.sender.transfer(amount);

    }
1 Like
// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.13;

contract Bank {
    
    mapping(address => uint) balance;
    
    event depositDone(uint amount, address indexed depositedTo);
    
    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);
        uint previousDepositorBalance = balance[msg.sender];
        balance[msg.sender] -= amount;
        payable(msg.sender).transfer(amount);
        assert(balance[msg.sender] == previousDepositorBalance - amount);
        return balance[msg.sender];
    }
    
    function getBalance() public view returns (uint){
        return balance[msg.sender];
    }
    
}
1 Like

1) Make sure that the user can not withdraw more than their balance

require(balance[msg.sender] >= amount);

2) Correctly adjust the balance of the user after a withdrawal

balance[msg.sender] -= amount;
msg.sender.transfer(amount);
1 Like
pragma solidity 0.7.5;

contract Bank {
    
    mapping(address => uint) balance;
    
    event depositDone(uint amount, address indexed depositedTo);
    
    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[msg.sender] -= amount;
        msg.sender.transfer(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 transfer money to yourself");
        
        uint previousSenderBalance = balance[msg.sender];
        
        _transfer(msg.sender, recipient, amount);
                
        assert(balance[msg.sender] == previousSenderBalance - amount);
    }
    
    function _transfer(address from, address to, uint amount) private {
        balance[from] -= amount;
        balance[to] += amount;
    }
    
}
1 Like
  1. Check the amount the sender is requesting to withdraw is less or equal to the balance within the contract; then if so
  2. after the withdrawal reduce the senders balance by the withdrawal amount, then
  3. assert that the previous balance is now the same as the previous balance less the amount withdrawn
function withdraw(uint amount) public returns (uint){
        // check balance of address in contract in less or equal to the withdraw amount
        require(balance[msg.sender] >= amount);

        // execute the withdral
        msg.sender.transfer(amount);

        // reduce the balance of the address in the contract by the withdraw amount
        balance[msg.sender] -= amount;

        // check that the balance of the address has been reduced by the withdraw amount
        uint previousSenderBalance = balance[msg.sender];
        assert(balance[msg.sender]== previousSenderBalance - amount);
    }

Hey @sph73, hope you are well.

Your function is OK, but the order of the variables is not entirely correct, you are transfering before chaing the value and fetching the previous value after sending the funds.

It should be like this

function withdraw(uint amount) public returns (uint){
        // check balance of address in contract in less or equal to the withdraw amount
        require(balance[msg.sender] >= amount);
        // check that the balance of the address has been reduced by the withdraw amount
        uint previousSenderBalance = balance[msg.sender];
        // reduce the balance of the address in the contract by the withdraw amount
        balance[msg.sender] -= amount;

        // execute the withdral
        msg.sender.transfer(amount);

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

Carlos Z

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

    require(balance[msg.sender]>= amount,"ERROR 1_Insufficient balance to withdraw funds.");

    msg.sender.transfer(amount);

    balance[msg.sender] -= amount;

    return balance[msg.sender];

}

Hi, Carlos!
Thanks for your feedback. I also used an assert rather than returning, which I also noticed wasn’t what Filip did.

So the correct way would be to reduce the amount before the transfer?

Best,
Sue

indeed, also the assertion is a nice feature to validate the entire process made before finishing the function execution.

Also its nice to decrease the balance of the user before transferring in case of reentrancy attacks, not an entire solution for it, but a nice first step. (check the ethereum security course for more info on reentrancy attack) :nerd_face:

Carlos Z

Absolutely; I am beginning to see the order of events now, thanks to your feedback, Carlos.
:thinking:

I will definitely be checking out the security course next…

Cheers,
Sue

pragma solidity 0.7.5;

contract Bank {
    
    //Please see function withdraw

    mapping(address => uint) balance;

    address owner;

    event depositDone(uint amount, address indexed depositedTo); 

    event transferMade(address recipient, uint amount);

    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){
        //msg.sender is an address
        require(balance[msg.sender] >= amount, "Balance not sufficient");
        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");
        emit transferMade(recipient, amount);

        uint previousSenderBalance = balance[msg.sender];

        _transfer(msg.sender, recipient, amount);

        assert(balance[msg.sender] == previousSenderBalance - amount);
        //event logs and further checks
    }

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

}


2 Likes
pragma solidity 0.8.7;

contract Bank {

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

    event balanceUpdated(uint prevBalance,uint newBalance, address indexed updatedAccount);

    modifier onlyOwner {
        require(msg.sender == owner,"Only SC Owner can Call this");
        //run the real functions, Practically EVM will replace all of function statement here
        _; 
    }

    modifier sufficientBalance(uint _amount){
        require(balances[msg.sender] >= _amount,"Balance Not Sufficient");
        _;
    }
    
    constructor(){
        //here owner becomes the creator of SC
        //mostly useful for restricting function access to admins
        owner = msg.sender;
    }
  
    // admin function can only be perfromed by the owner of SC
    //payable allows receiving money from the caller of the function
    //the msg.value will get added to the balance of the SC
    //the internal balance data structure allows keeping track of who this money is owed to
    function deposit() public payable returns(uint){
        
        uint prevBalance = balances[msg.sender];
        balances[msg.sender] += msg.value;
        //emit log 
        emit balanceUpdated(prevBalance, balances[msg.sender], msg.sender);

        return balances[msg.sender];
    }
    function withdraw(uint amount) public sufficientBalance(amount) returns(uint){
        //we can have other addressess
        //address payable _toAddress = 0x617F2E2fD72FD9D5503197092aC168c91465E7f2;
        //_toAddress.transfer(amount);

        //this will send amount(in ether) from SC to msg.sender
        //since balances are internally Manager within SC,
        //Condition should be checked if msg.sender balance is enoough
        //else it will transfer from SC which is cumulative of all users
        //sufficient balance, commented as using modifier
        //require(balances[msg.sender] >= amount,"Balance Not Sufficient");
        balances[msg.sender] -= amount;
        payable(msg.sender).transfer(amount);
        //emit log 
        emit balanceUpdated(balances[msg.sender]+amount, balances[msg.sender], msg.sender);
        return amount;
    }

    function getBalance() public view returns(uint){

        return balances[msg.sender];
    }
   
    
}
2 Likes
pragma solidity 0.7.5;

contract Bank{

    //storage - permanent storage of data (state variables)
    //memory - temporary storage used in funtion execution
    //calldate - save arguments/inputs to our functions
    //strings, arrays, mapping, scructs

    mapping(address => uint) balance;
    
    address owner;
    
    event balanceAdded(uint amount, address indexed depositedTo);
    event depositDone(uint amount, address indexed depositedTo);
    event transfered(uint amount, address recipient);
    

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

    function deposit() public payable returns (uint){
        balance[msg.sender] += msg.value; //Mapping only internal to track funds from user   
        emit depositDone(msg.value, msg.sender);
        return balance[msg.sender];
    }
    
    function withdraw(uint amount) public returns (uint) {
        // msg.sender is an address
      // address payable toSend = 0x4B20993Bc481177ec7E8f571ceCaE8A9e22C02db
      // toSend.transfer(amount); 
       require(balance[msg.sender]>=amount, "Balance not sufficient");      //cant withdraw more than your balance
        balance[msg.sender] -= amount;         //important to change the balance after withdrawing
        msg.sender.transfer(amount);

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

    function getAllBalance() public view returns (uint) {

        return address(this).balance;

    }

    function transfer(address recipient, uint amount) public{
        require(balance[msg.sender] >= amount, "Balance not sufficient");
        require(msg.sender != recipient, "Dont transfer money to yyourself");

        uint previousSenderBalance = balance[msg.sender];

        _transfer(msg.sender, recipient, amount);

         emit transfered(amount, recipient);
        assert(balance[msg.sender] == previousSenderBalance - amount);
       
    }

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

contract Bank {
    
    mapping(address => uint) balance;
    address owner;

    event balanceAdded(uint amount, address indexed sender);
    event transferCompleted(uint amount, address indexed sender, address indexed receiver);

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

    modifier enoughBalance (uint withdraw){
       require(balance[msg.sender] >= withdraw, "Not enough balance");
       _; 
    }


    constructor (){
        owner = msg.sender;
    }

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

    function withdraw(uint amount) public enoughBalance(amount) returns (uint) {
        uint previous = balance[msg.sender];

        balance[msg.sender] -= amount;
        payable(msg.sender).transfer(amount);
        
        assert(balance[msg.sender] == previous - amount);
        return balance[msg.sender];
    }

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

    function transfer(address recepient, uint amount) enoughBalance(amount) public {    
        require(msg.sender != recepient, "Sender cannot be recepient");

        uint previous = balance[msg.sender];
        _transfer(msg.sender, recepient, amount);
        
        assert(balance[msg.sender] == previous - amount);
        emit transferCompleted(amount, msg.sender, recepient);

    }

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


2 Likes

Hey guys! Here’s my solution to the transfer assignment.
Cheers

pragma solidity 0.7.5;


contract Bank {


    mapping(address => uint) balance;


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

    function withdraw(uint _amount) public returns(uint) {
        require(_amount <= balance[msg.sender], "Not enough balance to withdraw");
        msg.sender.transfer(_amount);
        balance[msg.sender] -= _amount;
        return balance[msg.sender];
    }

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

}
2 Likes

Here’s my solution for the withdraw function. :slightly_smiling_face:

function withdraw(uint amount) public returns (uint) {
    uint userBalance = balance[msg.sender];
    require(userBalance >= amount, "Cannot withdraw more than you have!");
    userBalance -= amount;
    msg.sender.transfer(amount);
    return userBalance;
}

1 Like