Contracts - cogeorg/teaching GitHub Wiki

A contract is what in other programming languages would be considered a class. They contain state variables that are altered by functions.

pragma solidity ^0.5.0;         // defines the compiler version

contract ExampleCoin {
    // State variables hold information
    // accessible to the contract
    address public minter;
    mapping (address => uint) public balances;

    // Events allow clients to react to specific
    // contract changes you declare
    event Sent(address from, address to, uint amount);

    // Constructor code is only run when the contract
    // is created
    constructor() public {
        minter = msg.sender;
    }

    // Sends an amount of newly created coins to an address
    // Can only be called by the contract creator
    function mint(address receiver, uint amount) public {
        require(msg.sender == minter, "Function can only be called by minter.");
        require(amount < 1e60, "Amount is too high.");
        balances[receiver] += amount;
    }

    // Sends an amount of existing coins
    // from any caller to an address
    function send(address receiver, uint amount) public {
        require(amount <= balances[msg.sender], "Insufficient balance.");
        balances[msg.sender] -= amount;
        balances[receiver] += amount;
        emit Sent(msg.sender, receiver, amount);
    }
}

Source: adapted from Solidity documentation

The line address public minter declares a state variable minter of type address that is publicly accessible. The keyword public automatically generates a getter-function that allows you to access the current value of the state variable. This function can either be called internally or via transactions from other contracts or users. By default, state variables are private, meaning that without the public keyword, other contracts have no possibility to access the variable.

The deployed contract will have its own contract address (different from the deployer’s address) on the blockchain. This address is accessible in a smart contract using the keyword this, e.g. address myAddress = this; stores the public address of the deployed contract under the variable myAddress.

The next line, mapping (address => uint) public balances, also creates a public state variable, balances, that allows addresses to be mapped to unsigned integers. Since the variable is public, there is no need to define a getter-function but it is defined automatically. The following snippet shows this function. balances returns a particular uint corresponding to an input address _account.

function balances(address _account) returns (uint) {
    return balances[_account];
}

Getter functions have external visibility. If a concept is accessed internally (i.e. without this.), it is evaluated as a state variable. If it is accessed externally (i.e. with this.), it is evaluated as a function.

contract C {
    uint public data;
    function x() {
        data = 3; // internal access
        uint val = this.data(); // external access
    }
}

The line event Sent(address from, address to, uint amount) declares the event Sent, which is executed in the last line of the function send. User interfaces are able to listen to events being fired on the blockchain without much cost. As soon as it is fired, the listener will also receive the arguments from, to and amount, which makes it easy to track transactions. Events allow light clients to track and react to transactions efficiently.

When a contract is created, the constructor function is executed only once and never again. While deploying the contract, the constructor is invoked and is used to initialize the state variables. It permanently stores the address of the person creating the contract. The information is retrieved from the global variable msg, that contains some properties which allow access to the blockchain. msg.sender always stores the address of the user or contract executing the current (external) function call.

The two functions mint and send are accessible only to users because they are external. mint, however, can only be successfully executed by the creator of the contract because it checks if the message sender is the minter (note that is functionality could have also been implemented as a modifier). If another user calls this function, it will return without executing the mining. The mint function controls the supply of the currency by issuing new coins to the creator of the contract.

On the other hand, send can be used by anyone who already has some of these coins to send coins to somebody else. Note that if you use this contract to send coins to an address, you will not see these coins when you look at that address on an Ethereum blockchain explorer because the balance is only stored in the data storage of that particular coin contract. By the use of events, however, it is relatively easy to create your own “blockchain explorer” that tracks transactions and balances of your new coin.

Inheritance

A contract can inherit code from one or multiple contracts. The inheritance system is very similar to the one of Python.

pragma solidity ^0.5.0;

contract Owned {

    address owner; 

    constructor () {
        owner = msg.sender;
    }

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

contract ExampleCoin is Owned{

    mapping (address => uint) public balances;

    event Sent(address from, address to, uint amount);

    function mint(address receiver, uint amount) external onlyOwner {
        require(amount < 1e60, "Amount is too high.");
        balances[receiver] += amount;
    }

    function send(address receiver, uint amount) public {
        require(amount <= balances[msg.sender], "Insufficient balance.");
        balances[msg.sender] -= amount;
        balances[receiver] += amount;
        emit Sent(msg.sender, receiver, amount);
    }
}  

The superior contract is Owned, that sets the state variable owner on construction. It also defines a modifier onlyOwner.

The contract ExampleCoin inherits from Owned. Therefore, it has access to the state variable owner and the modifier onlyOwner.