Solidity Error Handling

Hey @Leonard_Gohlke, hope you are well.

Please share your contract in the following way so we can give it a look to solve your issue :face_with_monocle:

Carlos Z

Hey thecil! Here is the code:

pragma solidity 0.7.5;

contract Firstcontract {

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

event balanceAdded( uint amount, address depositTo);
event coinsTransfered (address sentTo, uint amount, address sentFrom);

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

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 transfer(address recipient, uint amount) public {
    require (balance[msg.sender]>=amount, "balance not sufficient");
    require (msg.sender != recipient, "dont send money to yourself!");
    
    
    uint previousSenderBalance = balance [msg.sender];
    
    _transfer (msg.sender, recipient, amount);
    
    assert (balance[msg.sender]==previousSenderBalance - amount );
    
    emit coinsTransfered(recipient, amount, msg.sender);
    
    
}

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

Hey guys i just realized i forgot to add the constructor() function into my code… Now it is working.

Thank you very much for your help :smiley:

1 Like

@thecil I’m getting a bit confused right now here…I want to create a p2p lending smart contract but really I don’t know what I need to write in the code…it’s going to be a DeX where they can just connect to wallet…do deposit and all of that needed to be on th e code…and can you help with maybe any sketch or information I can use…?

I have a question about modifiers. I’m working on the Inheritance assignment, and in reworking my Bank contract, I am adding some new modifiers. I get errors on enoughBalance and senderNotRec modifiers, probably because the amount and toRecipient values in the modifiers are not declared. Can I pass those vars to the modifiers? Or how to go about this?

pragma solidity 0.7.5;
//SPDX-License-Identifier: UNLICENSED

//\\ -- BANK -- //\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\
contract Bank {

    address owner;
    mapping (address => uint) balance;
    
    event depositDone(uint amount, address indexed depositedTo);
    event transferDone(uint amount, address indexed sentTo, address indexed sentFrom);
    
    modifier onlyOwner() {
        require(msg.sender == owner);
        _; // run the function
    }

    modifier enoughBalance() {
        // check if balance of sender is sufficient
        require(balance[msg.sender] >= amount,"Balance not sufficient");
        _; // run the function
    }

    modifier senderNotRec() {
        // check for redundancy
        require(msg.sender != toRecipient, "Don't send money to yourself");
        _; // run the function
    }
        
    // INIT //////////////////////////////////
    constructor() {
        owner = msg.sender;
    }
    
    // DEPOSIT - payable //////////////////////////////////
    function deposit() public payable returns(uint) {
        balance[msg.sender] += msg.value;
        emit depositDone(msg.value, msg.sender);
        return balance[msg.sender];
    }
    
    // GET BALANCE - read only //////////////////////////////////
    function getBalance() public view returns(uint) {
        return balance[msg.sender];
    }
    
    // WITHDRAW //////////////////////////////////
    function withdraw(uint amount) public enoughBalance returns (uint)  {
        // msg.sender is an address, and address has a method to trasfer 
        msg.sender.transfer(amount);
        // adjust balance
        balance[msg.sender] -= amount;
        return balance[msg.sender];
    }
    
    // TRANSFER TO //////////////////////////////////
    function tranferTo(address recipient, uint amount) public enoughBalance senderNotRec {
        uint previousSenderBalance = balance[msg.sender];
        _transfer(msg.sender, recipient, amount);

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

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

}

Both modifiers needs to know the value of the parameter you want to compare. So you might have to do something like:

    modifier enoughBalance(uint amount) {
        // check if balance of sender is sufficient
        require(balance[msg.sender] >= amount,"Balance not sufficient");
        _; // run the function
    }

Then, you just need to send the argument of the modifier on the function you want to trigger it:

    function withdraw(uint amount) public enoughBalance(amount) returns (uint)  {
        // msg.sender is an address, and address has a method to trasfer 
        msg.sender.transfer(amount);
        // adjust balance
        balance[msg.sender] -= amount;
        return balance[msg.sender];
    }

Carlos Z

1 Like

Got it! Thank you very much

1 Like

I am confused about ‘msg.sender == owner’.
Who is the owner?
Does creating a transaction amount to deploying a contract?

Hi @Rounak_Jain,

Welcome to the forum :slight_smile:

owner references the owner state variable, so its value is the value stored in that state variable, which in our case is the address that deploys the contract.

When the contract is deployed, the address that deploys it is the address displayed in the Account field in the Deploy & Run Transactions panel. You can change this address by selecting another one from the dropdown.

On deployment, the constructor assigns the deployer’s address to the owner state variable. This will be the address referenced by owner in the modifier’s require() statement…

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

The condition in the require() statement is comparing the owner address with whichever address calls a function (msg.sender) containing the onlyOwner modifier in its header.

If these two addresses are the same, the condition will evaluate to true and the function will continue executing. In other words, a function that includes the onlyOwner modifier in it’s header will only execute if it is called by the owner address. The address that calls a function is the address displayed in the Account field when the button with the function name is clicked in the Deploy & Run Transactions panel.

No… the contract needs to be deployed before any transactions can be executed. Contract deployment is also itself a transaction — the first transaction associated with that contract. Once the contract is deployed it remains deployed. To create a transaction, a function needs to be called which will change the contract state (orange and red buttons in Remix).

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

1 Like

Thanks for the reply Jon.
I am an experienced javascript programmer, so the code part is clear.

I will try to rephrase my question:

I am not able to relate to the “owner” in real world scenario. If deployer is the owner, what is the practical use case of writing a contract which only interacts with his account?

Basically, in my mind, I am thinking of a contract enabling transactions between several parties. I am not able to relate the Bank contract example with such real world usage.

Thanks again.

1 Like

Hi @Rounak_Jain,

At this early stage, we are only experimenting with an example, in order to introduce different Solidity concepts and syntax. The important thing about the code you’ve highlighted at this point in the course is that you understand how we can code a condition within a require statement within a modifier, which only allows a particular address to call and execute a function, and not so much which particular function, and which specific address, this modifier would be best applied to — that comes later.

You are right that it would make no practical sense, in a real-world scenario, to restrict access to the deposit function of a smart contract designed to provide a “banking service”. However, later on in the course, after introducing more concepts and syntax, you will see how we can then use this same onlyOwner modifer to restrict access to a function which destroys the contract. For obvious reasons we don’t want to allow any address to destroy the contract!

You are absolutely right to apply this type of critical thinking to the example code we are using, because, ultimately, we obviously want to write smart contracts which add value to real world scenarios. Just bear in mind, though, that, especially during this introductory course, even though we do try to make the examples as realistic as possible, some elements have a more theoretical and instructional focus in terms of introducing the syntax as efficiently as possible.

As you are already an experienced programmer, and are naturally relating the theory you are learning to potential real-world use cases, whenever you notice potential flaws or disadvantages with certain parts of our Bank contract — and there will be more, I promise you :wink: — I would encourage you to experiment with the code yourself and try to come up with your own improvements/modifications which make more practical sense to you. When you come on to the assignments, there are always alternative solutions, and as long as you don’t completely rewrite the whole contract, we do encourage you to introduce your own adaptations and extensions.

Just one final point about the onlyOwner modifier applied to the deposit function. As we also have a transfer function which allows deposited funds to be transferred to other addresses within the contract, we could interpret this particular version of the contract as enabling a single owner to deposit funds and then distribute these funds to other addresses of their choosing. Perhaps this version would be better named contract Philanthropy, or contract Funding. A little later in the course we will also be adding a withdraw function, which won’t be restricted to the owner address, so that addresses with funds can make withdrawals.

What I’m trying to say is that, even though we’ve called the contract Bank, you sometimes need to be a bit more flexible and imaginative in how you think about potential real-world use cases than the contract name at first suggests.

I hope that makes sense. Just let me know if you have any other concerns or questions :slight_smile:

1 Like

Thanks for the clarification.

1 Like

If anyone wanna try a “Assert” error, can try with this function that i do :

Note : The “assert” is run when some conditions are not expected that could be,
in this case if i have 1 and subtract 1 the expected ( in assert ) is 0, but if i put 1, the
error go be executed because that is not true, this is only for test, in real cases
i think this is a good practice for the most functions, because if somebody wants to hack the function and has the assert … the transaction fails, is not the best for security but solve some cases for unexpected things :slight_smile:

sorry for my bad english, i hope anyone can understand :slight_smile: :heart:

function test() public {
        
        uint256 have = 1;
        uint256 sub = 1;

        assert(have - sub == 1);

}
2 Likes

Asserts are very useful to validate an expected behavior of an operation, but in this case, the validation is not true.

1 - 1 = 0, so the assert will fail because that condition is not true. The expected behavior is 0.

Carlos Z

2 Likes

Hey @ismael_zamora,

Yes… because the condition in assert() should always be true, we can’t actually demonstrate an example of assert() failing unless we “force” the condition to do the opposite… which is what your function does, and you’ve explained its purpose nicely :ok_hand:

I would say that assert() is more definite than that, and that it checks for conditions that will never occur, unless our code fundamentally fails to do what it’s been designed to do. This is what’s called an invariance.

Rather than being used to prevent attacks aimed at manipulating weaknesses, ambiguities and/or bugs in how the smart contract code has been written, the job of assert() is more to prevent a catastrophic failure in the code’s own environment, in its “fundamental laws” so to speak.

In terms of when to use it, we need to consider what the consequences of such a fundamental failure would actually be, and weigh that up against the additional cost of gas to execute it.

It’s important to realise that certain attacks could still succeed whether or not there is an assert() statement. So, it’s also important to consider other smart-contract security measures, and not just to rely on assert(). You’ll learn about some of these other security measures later on :slight_smile:

2 Likes

I am getting type error - Operator not compatible.

Hi Filip, In require function I am getting the error " operator not compatible" please take a look at it.

pragma solidity 0.8.13;

contract HelloBank{

    mapping(address => uint)balance;
    address owner;
    constructor()
    {
         owner = msg.sender;
    }
    modifier onlyOwner(){

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

    function addBalance(uint _amt) public onlyOwner {

        balance[msg.sender]+=_amt;
    }

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

    function transfer(address recpt, uint _amount) public {
        **require(balance[msg.sender >= _amount], "Insufficient Balance");**
        require(msg.sender != recpt);
        uint prevBalance = balance[msg.sender];
        _transfer(msg.sender,recpt,_amount);
        assert(prevBalance == balance[msg.sender]- _amount);
    }

    function _transfer(address from, address to, uint _amount) private {

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

    }
}

Hi @Gaurav_Sahoo,

Your condition in the require statement needs to compare the caller’s balance in the mapping…

balance[msg.sender]

with the transfer amount …

_amount

So, the error is caused by the incorrect position of the closing square barackets …

require(balance[msg.sender] >= _amount, "Insufficient Balance");

Once you’ve corrected the require() statement and deployed and tested your contract, you will find that when the transfer() function is called, the assert() statement is failing when it shouldn’t.

Your code is saying that the SENDER’S previous balance should be equal to their updated balance (after the transfer) LESS the amount transferred. So this will never evaluate to true. Can you see why, and then correct it?

Just let me know if you have any questions.

Hey there,

I am currently watching the “require” lecture, and I have a small question: In the lecture, you set owner address by using a constructor and setting the owner variable to msg.sender.
why is it that we have to create a constructor to set the owner address to msg.sender?
and why can’t we just set it on the same line as you set the owner variable?

Thank you,
-Bogdan Manzar

1 Like

You can set the owner in both ways, either in constructor, or with address owner = msg.sender;
Constructor is optional, it is just a way to set state variable that you can´t change later. It depends on your needs, whether you want modifiable or immutable values.

1 Like