Events Assignment

pragma solidity 0.7.5; 

contract Bank {   
    mapping (address => uint) balance; 
    address owner; 
    
    event balanceAdded(uint amount, address depositedTo) ;
    event amountTransferred(uint amount, address toAddress, address fromAddress);
    
    modifier onlyOwner {
        require(msg.sender == owner);
        _; 
    }
    
    constructor() {
        owner = msg.sender;
    }
    
    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, "Don't transfer money to yourself");
        
        uint previousSenderBalance = balance[msg.sender];    

        _transfer(msg.sender, recipient, amount);
        
        assert(balance[msg.sender] == previousSenderBalance - amount);   
        emit amountTransferred(amount, recipient, msg.sender);      
    }
    
    function _transfer(address from, address to, uint amount) private {      
        balance[from] -= amount; 
        balance[to] += amount;     
    }
    
}

I noticed others above indexing the events. While the video it talked about it to be able to look up addresses later, Filip said that amounts were not worth indexing.

I was curious why because there might be scenarios where you want to index an amount transferred, or if this can always be looked up on the blockchain itself afterwards?

Can’t addresses be looked up by having interacted with a smart contract? A block explorer shows things like the most used contracts and some transaction details with them from what I’ve seen.

My eth experience almost anything about a transaction of tokens can be seen from an address with a block explorer.

Thanks for anyone’s time!

edit: Question from the Event Quiz right after this assignment
It appears the first question gets the syntax incorrect:

Both how the video shows events and how jon_m above has the first word in an event as lower case not upper case.

from jon_m:

(2) A generally-accepted convention is to start the names of …

  • Contracts, Interfaces, Events and Structs with a capital letter
  • functions, variables, mappings, arrays, arguments, parameters and modifiers with a lower-case letter
2 Likes

Nice solution, and a very well coded contract @ekr990011 :muscle:

… apologies for the delayed response!

Your transfer event and corresponding emit statement 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.


Regarding your question about whether or not to index specific event parameters, we need to be clear that this specifically relates to the extent to which the frontend, using a library such as web3.js, is able to filter historic event data when searching for specific events. What it doesn’t relate to is our ability to look up standard transaction data using a block explorer.

Every ethereum transaction will contain data such as the transaction hash, transaction status, calling address (msg.sender), address of the smart contract interacted with, input values (arguments) and return values, any Ether value sent, and the gas/transaction fee (amongst other things). You are right that we can look up this data on a block explorer, either for specific transactions using the transaction hash, or for transactions associated with a specific ethereum address. However, events allow specific sets of data to be defined and logged for specific transactions, and are also able to capture values which would not otherwise be included in the standard transaction data, such as a value calculated within the function body and not returned.

Marking event parameters as indexed allows their names to be used as search parameters to filter logged event data. However, each event parameter that is indexed (up to a maximum of 3 per event declaration) increases the gas cost, so they should only be indexed when this is actually necessary. Therefore, deciding whether or not to index event parameters will depend on whether we will need to retrieve historical event data and, if so, whether we require the returned dataset to have been filtered according to specific event parameters other than the event name.

For example …

event balanceAdded(uint amount, address indexed depositedTo);
  • If we need to retrieve the historical event data just for the deposits made by a specific address, then we need to index the address parameter corresponding to the depositor’s address.
  • If we don’t need to retrieve the historical event data just for deposits of a specific amount, then indexing the amount parameter won’t be necessary.
  • If we need to retrieve the historical event data just for the deposits of a specific amount made by a specific address, then we need to index both of these parameters.

The following question-and-answer thread illustrates this nicely …
https://ethereum.stackexchange.com/q/50501

And here’s a link to a helpful discussion about indexing, which I think explains quite well how this works in practice: https://ethereum.stackexchange.com/q/8658


So, by this I mean the convention is to start the event name with a capital letter, not the event keyword itself e.g.   event BalanceAdded( )

It’s not wrong to start the event name with a lower case letter (as you have, and as shown in the video); but to avoid confusion we should then be consistent about this across all our events (as you have also been with both of your events).

Example = New Event(uint number, address from);

Yes … but this is incorrect because an event declaration (i) starts with the event keyword, (ii) has no assignment operator (=), and (iii) has no new keyword (which starts with a lower case letter, anyway).

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

1 Like

Thanks a lot for the explanation, all very good to know.

I actually was talking about the third answer (maybe my picture was zoomed in to much?):

event Example(uint number, address from)

This is the correct answer but in the video the “Example” would be lower case or so I thought but you cleared that up with the well detailed reply!

I feel kinda silly I misread even the quote I took from you about Contracts, Interfaces, Events, and Structures start with a capital letter.

I just took what I saw from the video and assumed you had also made it lower case

1 Like

Hey @ekr990011,

Don’t worry — it’s so easy to mix up / misread these things, especially when the differences are so small.

Ah! :man_facepalming:I can see, now, where I misunderstood what you meant.

Anyway, the main thing is that the issue has been cleared up and you have a satisfactory answer to your question :slight_smile:

1 Like
pragma solidity 0.8.7;

contract Testing{

event transferredTo (uint amountTransferred, address indexed recipientAddress); // Here we define the event

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 transferredTo(amount,recipient);
    }

}

I would say we should emit the event right after the assert verification in the function so that if there was a problem with assert it won’t be emitted

1 Like

Hi @seybastiens,

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

If the emit statement is placed before assert() and assert() fails, the transaction will revert. This means that any operations performed up to that point are reversed, including emitting the event data. Having said that, however, we should always adopt an approach which is as risk-averse as possible. And in that sense you are correct. Whenever possible, 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. And generally speaking, it’s probably also better to place an emit statement after an assert statement if there is one.

Just one other observation …

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.

Let me know if you have any questions :slight_smile:

1 Like
pragma solidity 0.7.5;

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

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

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

    constructor() {
        owner = msg.sender;
    }

    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, "Don't transfer money to yourself");

        uint previousSenderBalance = balance[msg.sender];
        _transfer(msg.sender, recipient, amount);
        assert(balance[msg.sender] == previousSenderBalance - amount);

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

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

Hi @oioii1999,

Your transfer event and corresponding emit statement 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:

1 Like
pragma solidity 0.7.5;

contract Bank {

  mapping(address => uint) balance;

  address owner;

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

  constructor(){
    owner = msg.sender;
  }

  event BalanceAdded(uint amount, address depositedTo);

  event TransactionLog(address indexed sender, address indexed recipient, uint transactionValue);

  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 {
    //check balance of msg.sender
    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);

    //event logs and further checks
    emit TransactionLog (msg.sender, recipient, amount);
  }

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

I tried to test this code in Remix, but didn’t manage to see the Event logs. I got the error message “false Transaction mined but execution failed”. Pretty sure the balance was sufficient for transferring, so I’m still figuring out why Remix keeps throwing this message.

1 Like

Hi @JoriDev,

Sorry for the delay in replying!

Your transfer event and corresponding emit statement are both correct and well coded. The emit statement is in the correct position in the transfer() function body and logs appropriate data when the transfer() function is successfully executed :ok_hand: … at least it does when I deploy and test the contract code you’ve posted!

Have you managed to work out what the problem is yet? When exactly are you getting this error message: when you deploy the contract, when you call addBalance(), or when you call transfer()?

After deploying your contract, I performed the following steps, and it worked fine …

  1. (i) Address 0x5B3…dC4 calls addBalance() with a _toAdd argument of 5
    Address 0x5B3…dC4 is the msg.sender (showing in the Account field near the top of the Deploy & Run Transactions panel in Remix).
    (ii) You will see the logged BalanceAdded event data in the transaction receipt (with the green tick) in the Remix terminal (at the bottom of the screen, below the code editor). Click on the transaction receipt to open it and scroll down to logs and you will see the amount and the depositedTo address (the depositor, msg.sender) x2 in the "args" property.

  2. Address 0x5B3…dC4 calls getBalance() to check that their balance in the mapping is 5
    Again, address 0x5B3…dC4 is the msg.sender (showing in the Account field near the top of the Deploy & Run Transactions panel in Remix).

  3. (i) Use the Account field dropdown to copy the 2nd address from list (0xAb8…cb2), and then paste it into the transfer() function call’s recipient argument field (unhide the 2 argument fields by clicking the down-arrow).
    (ii) Don’t forget to reset the address in the Account field to 0x5B3…dC4, because this is the address that needs to call transfer() with an amount argument of less than or equal to 5
    (iii) Address 0x5B3…dC4 calls transfer() with a recipient argument of 0xAb8...cb2, and an amount argument of 3
    Address 0x5B3…dC4 is the msg.sender (showing in the Account field).
    (iv) You will see the logged TransactionLog event data in the transaction receipt (with the green tick) in the Remix terminal. Click on the transaction receipt to open it and scroll down to logs and you will see the sender and recipient addresses, and the transactionValue (the amount transferred from the sender to the recipient) x2 in the "args" property.

  4. (i) Address 0x5B3…dC4 calls getBalance() to check that their balance in the mapping is now 2
    (ii) Change the address showing in the Account field to 0xAb8…cb2
    Address 0xAb8…cb2 calls getBalance() to check that their balance in the mapping is 3


A couple of additional comments about your code …

(1) Calling both the deposit() and the transfer() functions result in transactions, so I think TransferLog and transferValue would be better/clearer names than TransactionLog and transactionValue for your transfer event and its 3rd parameter.

(2)

An assert() statement is a “further check”, so I would put this below the comment as well.
But you are right to emit the event after assert() :ok_hand:

These are just minor points though.

Let me know if you have any questions, or if you’re still having issues testing your code and seeing the logged event data.

Hi @jon_m,

Just tested it again in Remix and this time everything worked perfect. I guess I messed up with the addresses the first time. Of course your reply and taking a break helped too :smiley:

Also as I struggled a bit with the naming of the transfer event, I liked that you commented on it. Giving attention to little things like order, naming and naming conventions are a great way to make a difference on a larger scale to avoid confusion. Thank you for the fine tuning.

1 Like

You’re very welcome @JoriDev!

Glad you found the comments so helpful :slightly_smiling_face:

1 Like

pragma solidity 0.7.5;

contract Bank{

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

event balanceAdded(uint amount, address indexed depositedTo);
event transferAdded(uint amount, address indexed transferTo, address indexed transferFrom); //ANSWER

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

}

constructor(){
    owner = msg.sender;
}

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, "Can't send to self");

    uint previousSenderBalance = balance[msg.sender];
    _transfer (msg.sender, recipient, amount);
    assert(balance[msg.sender] == previousSenderBalance - amount);

    emit transferAdded(amount, recipient, msg.sender); //ANSWER
}


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

}

1 Like

pragma solidity 0.7.5;

contract Bank {

mapping (address => uint) balance;

address owner;

event balanceAdded(uint amount, address depositedTo);

event transferAdded(uint amount, address depositedTo, address depositedFrom); //THIS IS MY ANSWER FOR EVENTS ASSIGNMENT

modifier onlyOwner {

  require(msg.sender == owner);

  _;

}

constructor(){

 owner == msg.sender;

}

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, "Don't transfer money");

 uint previousSenderBalance = balance[msg.sender];

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

 balance[msg.sender] -= amount;

 balance[recipient] += amount;

  }

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

 **emit transferAdded(amount, to, msg.sender);** // THIS IS MY ANSWER FOR EVENTS ASSIGNMENT

 balance[from] -= amount;

 balance[to] += amount;

}

}Preformatted text

1 Like

Hi @martina,

Your transfer event declaration and corresponding emit statement are both well coded and, once you correct a couple of issues in your transfer() function, your emit statement will log appropriate data when the transfer() function is successfully executed.

You need to add a call to the _transfer() helper function in the body of your main transfer() function, otherwise your emit statement won’t be executed! You also need to remove the last two lines of code from your transfer() function …

… otherwise you will be adjusting the sender and recipient balances for the transfer amount twice!

The assert() statement needs to check the sender’s individual balance after it has been adjusted for the transfer amount, and so the _transfer() helper function needs to be called before the assert() statement.

If you don’t use a separate helper function, and put all of the code associated with the transfer transaction in the main transfer() function (which is also a possible solution), then you’ll need to place your assert() statement after the individual balance adjustments, otherwise it will fail when it shouldn’t and prevent valid transfers from completing.


Your error message is wrong here. Users can transfer tokens — that’s the whole point of the function — but just not to themselves!

"Don't transfer tokens to yourself"

One final observation …

Whenever possible, an emit statement should be placed at the end of a function after all of the operations associated with the event have been performed, but before a return statement if there is one. And generally speaking, it’s probably also better to place an emit statement after an assert statement if there is one.

In addition, it’s better to emit the event at the end of the function which completes the transaction i.e. transfer() not _transfer().
Another important factor here is that if you emit an event in a helper function, you are restricting your ability to reuse that helper function. It will be more reuseable if we can call this same helper function from another function when we may not want to emit the same event — either a different one, or no event at all. That way we can reuse the helper function whenever we just want to implement its functionality (i.e. the operations, or computation, that it performs).

See if you can correct your code for these points. Just let me know if anything is unclear, or if you have any questions :slight_smile:

Hi @jon_m. Thank you for your notes, I have tried to correct my code but I’m not sure to be honest:
I have just copied from the part: function transfer as I understood event transferAdded was correct. If you can have a look, thank you.

function transfer(address recipient, uint amount) public {

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

 require(msg.sender != recipient, "Don't transfer tokens to yourself");

 uint previousSenderBalance = balance[msg.sender];

 _transfer(msg.sender, recipient, amount);

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

 emit transferAdded(amount, recipient, msg.sender);
1 Like

Hi @martina,

Yes … your event declaration was correct.

Your transfer() function is looking good now, and the emit statement is in the correct place, at the very end of the function body :muscle:

The only thing to also confirm is that your _transfer() helper function should now look like this…

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

i.e. the same as you had before, but without the emit statement, which you’ve correctly moved to the main transfer() function.

1 Like

Hi @jon_m, is this a good solution or what am i missing.

pragma solidity 0.7.5;

contract Bank {

mapping (address => uint) balance;

address owner;



event balanceAdded(uint amount, address depositedTo);

event transferMade(address from, address to, uint amount); //answer

modifier onlyOwner {

   

    require(msg.sender == owner);

    _;

}



constructor() {

    owner = msg.sender;

}



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, "Can't transfer money to yourself");

    emit transferMade(msg.sender, recipient, amount); //answer

    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;

   

}

}Preformatted text

1 Like

Hi @Rebe

Your transfer event and corresponding emit statement are both correctly coded, and the emit statement will log appropriate data when the transfer() function is successfully executed :ok_hand:

However, whenever possible, an emit statement should be placed at the end of a function after all of the operations associated with the event have been performed, but before a return statement if there is one. And generally speaking, it’s probably also better to place an emit statement after an assert statement if there is one.

Just let me know if you have any questions.


event emits: sender, recipient and transferred amount

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

    event balanceAdded(uint amount, address indexed depositedTo); 
    event transferExecuted(address sender, address indexed recipient, uint amountTransferred);

    modifier onlyOwner {
        require(msg.sender == owner);
        _;
    }
        constructor(){
        owner = msg.sender;
    }
    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);
        require(msg.sender != recipient);

        uint previousSenderBalance = balance[msg.sender];

        _transfer(msg.sender, recipient, amount);
        emit transferExecuted(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