Solidity Payable Functions - Discussion

pragma solidity 0.5.12;

contract Payable{

struct Person{
    uint id;
    string name;
    uint age;
    uint height;
    bool senior;
}

event personCreated(string name, bool senior);
event personDeleted(string name, bool senior, address deletedBy);

address public owner;
uint public balance;


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

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

constructor() public{
    owner = msg.sender;
}

mapping (address => Person) private people;
address[] private creators;

function createPerson(string memory name, uint age, uint height)
    public payable costs(1 ether){
        require(age < 150, "Age needs to be below 150");
        balance += msg.value;
        
        //This creates a Person
        Person memory newPerson;
        newPerson.name = name;
        newPerson.age = age;
        newPerson.height = height;
         if(age >= 65){
             newPerson.senior = true;
         }
         else{
             newPerson.senior = false;
             
         }
         insertPerson(newPerson);
         creators.push(msg.sender);
         
         assert(
             keccak256(
                 abi.encodePacked(
                     people[msg.sender].name,
                     people[msg.sender].age,
                     people[msg.sender].height,
                     people[msg.sender].senior
                )
            )
            ==
            keccak256(
                abi.encodePacked(
                    newPerson.name,
                    newPerson.age,
                    newPerson.height,
                    newPerson.senior
                )
            )
        );
        emit personCreated(newPerson.name, newPerson.senior);
    }
    function insertPerson(Person memory newPerson) private {
        address creator = msg.sender;
        people[creator] = newPerson;
    }
    function getPerson() public view returns(string memory name, uint age, uint height, bool senior){
        address creator = msg.sender;
        return (people[creator].name, people[creator].age, people[creator].height, people[creator].senior);
    }
    function deletePerson(address creator) public onlyOwner {
        string memory name = people[creator].name;
        bool senior = people[creator].senior;
        
        delete people[creator];
        assert(people[creator].age == 0);
        emit personDeleted(name, senior, owner);
    }
    function getCreator(uint index) public view onlyOwner returns(address) {
        return creators[index];
    }
    }

I deployed and tested the contract. It works well for its limited function.

1 Like

Hi @jdbarrancs,

The idea here was to store and retrieve the contract balance, rather than individual user balances. This can simply be done by creating a state variable…

uint public balance;
/* as this state variable has public visibility,
   a getter will be created automatically */

… and then assigning to this balance variable, the transaction costs paid to the smart contract address (in our contract, 1 ETH for creating each new person):

// createPerson() function
balance += msg.value;

Your getBalance() function works, but will only enable an individual user to retrieve their own balance, and not the contract balance.

2 Likes

Dear @jon_m

Please help me understand this - where in the code does it explain the logic that the withdrawn amount will credit Owner’s address? “msg.sender.transfer(toTransfer)”, doesn’t the logic here state that the owner / msg.sender is sending his balance to an internal uint variable in the contract. Why am I reading the logic as reversed. Please help clarify this confusion.

Hi @Li_Sun
Thanks for asking :slight_smile:
If you see the modifier onlyOwner it means that this function can only be called by the owner of the contract (or address), whoever was defined as the owner.
msg.sender.transfer(toTransfer) transfer the amount (toTransfer) to the owner of the contract
I hope it clarifies

1 Like

By creating a variable for contract balance and making it public we can get the balance.

uint public contractBalance;

So whe a function receive eth correctly we just plus that amount to the current balance variable.

  contractBalance = ContractBalance + msg.value;         
2 Likes

The same for me with an onlyOwner option!

My solution to the “Receiving money” lesson:
Create an event and notify listeners, so define the event:

    event showreceivedAmount(string name, uint256 amount);

And use it at end of createPerson function:

emit showreceivedAmount(newPerson.name, msg.value);
1 Like

@CasNWK
Yes, you are right!

1 Like
pragma solidity 0.5.12;

contract HelloWorld{
    struct Person{
        uint id;
        string name;
        uint age;
        uint height;
        bool senior;
        uint value;
    }
    
    event personCreated(string name, bool senior);
    event personDeleted(string name, bool senior, address deletedBy);
    
    address public owner;
    
    modifier onlyOwner(){
        require(msg.sender == owner);
        _; // Contine the exexution 
    }
    
    
    constructor() public{
        owner = msg.sender;
    }
    
    mapping(address => Person) private people ;
    address[] private creators;
    
    function createPerson(string memory name, uint age, uint height) public payable {
        require(age < 150, "Age needs to be below 150");
        // check if the payment is >= 1
        require(msg.value >= 1 ether );
        
        
        Person memory newPerson;
        newPerson.name = name;
        newPerson.age = age;
        newPerson.height = height;
        newPerson.value = msg.value;
        
        if(age > 65){
            newPerson.senior = true;
        }
        else{
            newPerson.senior = false;
        }
        
        insertPerson(newPerson);
        creators.push(msg.sender);
        assert(
            keccak256(
                abi.encodePacked(
                    people[msg.sender].name, 
                    people[msg.sender].age, 
                    people[msg.sender].height, 
                    people[msg.sender].senior
                )   
            ) 
            == 
            keccak256(
                abi.encodePacked(
                    newPerson.name, 
                    newPerson.age, 
                    newPerson.height, 
                    newPerson.senior
                )
            )
        );
        
        emit personCreated(newPerson.name, newPerson.senior);
    }
    
    function insertPerson(Person memory newPerson) private {
        address creator = msg.sender;
        people[creator] = newPerson;
    }
    
    function getPerson() public view returns(string memory name, uint age, uint height, bool senior, uint value){
        //address creator = msg.sender;
        return  (people[msg.sender].name, people[msg.sender].age, people[msg.sender].height, people[msg.sender].senior, people[msg.sender].value );
    }
    
    function deletePerson(address creator) public onlyOwner {
        string memory name = people[creator].name;
        bool senior = people[creator].senior;
        
        delete people[creator];
        assert(people[creator].age == 0);
        emit personDeleted(name, senior, msg.sender);
    }
    
    function getAddress(uint index) public view onlyOwner returns(address){
        return creators[index];
    }
    
}

I had modified struct Person

struct Person{
        uint id;
        string name;
        uint age;
        uint height;
        bool senior;
        uint value; // This will store the current amount of ether when new Person created
    }

And to show the current amount I had modified getPerson() function to show the amount of ether sent to a createPerson() function

function getPerson() public view returns(string memory name, uint age, uint height, bool senior, uint value){
        //address creator = msg.sender;
        return  (people[msg.sender].name, people[msg.sender].age, people[msg.sender].height, people[msg.sender].senior, people[msg.sender].value );
    }
1 Like

@rostyslavdzhohola
Amazing work!, I saw in your profile you are looking for ideas to startup, I suggest you to look into our bootcamp :slight_smile:

1 Like

Hi @filip,

What is the purpose of keeping uint public balance as a property when the address(this).balance keeps track of that for us?

Thanks

@_alarming
Thanks for reaching out!
There is quite a big difference between the two

address(this).balance is for getting the balance of the current contract
and
uint public balance is an unsigned integer public variable named as balance, this can store balance (uint) of any address wether contract address or private key account :slight_smile:
and can even just store a number (if not balances).

Hope this answers the question

2 Likes

Here is mine solution.

pragma solidity 0.5.12;

contract HelloWorld{

    struct Person {
      uint id;
      string name;
      uint age;
      uint height;
      bool senior;
    }

    event personCreated(string name, bool senior);
    event personDeleted(string name, bool senior, address deletedBy);

    address public owner;

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

    constructor() public{
        owner = msg.sender;
    }

    mapping (address => Person) private people;
    address[] private creators;

    function createPerson(string memory name, uint age, uint height) public payable {
      require(age < 150, "Age needs to be below 150");
      require(msg.value >= 1 ether);
        //This creates a person
        Person memory newPerson;
        newPerson.name = name;
        newPerson.age = age;
        newPerson.height = height;

        if(age >= 65){
           newPerson.senior = true;
       }
       else{
           newPerson.senior = false;
       }

        insertPerson(newPerson);
        creators.push(msg.sender);

        assert(
            keccak256(
                abi.encodePacked(
                    people[msg.sender].name,
                    people[msg.sender].age,
                    people[msg.sender].height,
                    people[msg.sender].senior
                )
            )
            ==
            keccak256(
                abi.encodePacked(
                    newPerson.name,
                    newPerson.age,
                    newPerson.height,
                    newPerson.senior
                )
            )
        );
        emit personCreated(newPerson.name, newPerson.senior);
    }
    function insertPerson(Person memory newPerson) private {
        address creator = msg.sender;
        people[creator] = newPerson;
    }
    function getPerson() public view returns(string memory name, uint age, uint height, bool senior){
        address creator = msg.sender;
        return (people[creator].name, people[creator].age, people[creator].height, people[creator].senior);
    }
    function deletePerson(address creator) public onlyOwner {
      string memory name = people[creator].name;
      bool senior = people[creator].senior;

       delete people[creator];
       assert(people[creator].age == 0);
       emit personDeleted(name, senior, owner);
   }
   function getCreator(uint index) public view onlyOwner returns(address){
       return creators[index];
   }
    function getBalance (uint index) view public returns (uint){
        return creators[index].balance;
        
    }
}
1 Like

Question on reverting on failed transactions. -
I understand that the function would reverse setting such as balance, but how does this work when you use emit for messaging events. Would you need to guard those more to prevent messages going out incorrectly, or does the system hold onto sending messages until the function completes?

When you create a contract with a payable address, can you not simply use a wallet address?
I understand this would not be best practice - just curious.

You can only have one constructor per contract, and so if that is inherited there is no way to add another constructor in the child contract, right? This seems limiting, yes?

@iamchaoticcoder
Yes, the system hold onto sending message until the function completes.
This is because Events are inheritable members of contracts. When you call them, they cause the arguments to be stored in the transaction’s log - a special data structure in the blockchain.
This means Events are not stored unless transaction on the EVM are stored i.e. successful.

// Alternative way to do it:
        require(
            amount <= msg.value / 2 ether,
            "Not enough Ether provided."
        );

You can add a message to require(), in case the function reverts, the message is displayed.

Hope this answers the question :slight_smile:

2 Likes

@iamchaoticcoder

  1. First, payable is a modifier that can be added to a function.

It’s impossible to have payable() as a function name as it is a reserved keyword. You may use payable only in addition to existing functions like:

function deposit() payable {}
function register(address sender) payable {}
  1. To make an address collect / receive funds in ethers we have to explicitly declare the address as payable

source: https://ethereum.stackexchange.com/questions/64108/whats-the-difference-between-address-and-address-payable

2 Likes

This is what I would use for getting the balance

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

@iamchaoticcoder
We can add another constructor to child contract

For example

pragma solidity >0.6.99 <0.8.0;

contract Base1 {
    constructor() {}
}

contract Base2 {
    constructor() {}
}

// Constructors are executed in the following order:
//  1 - Base1
//  2 - Base2
//  3 - Derived1
contract Derived1 is Base1, Base2 {
    constructor() Base1() Base2() {}
}

// Constructors are executed in the following order:
//  1 - Base2
//  2 - Base1
//  3 - Derived2
contract Derived2 is Base2, Base1 {
    constructor() Base2() Base1() {}
}

// Constructors are still executed in the following order:
//  1 - Base2
//  2 - Base1
//  3 - Derived3
contract Derived3 is Base2, Base1 {
    constructor() Base1() Base2() {}
}

source: https://solidity.readthedocs.io/en/develop/contracts.html#arguments-for-base-constructors

2 Likes