Functions
7 minutes of reading
State Mutability
Functions can be classified as state-changing and non-state-changing. State-changing functions initiate transactions, thus they cost gas. On the other hand, non-state changing functions don’t initiate transactions therefore you don’t need to pay gas fee for calling them.
State-changing Functions
These are functions that upon invocations modify the state of the blockchain.
1
2
3
4
5
6
7
8
9
10
11
12
13
// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.7.0 <0.9.0;

contract Storage {

    uint256 number;

    // this is a state-changing function
    function store(uint256 num) public {
        number = num;
    }
}
Non-state-changing Functions
These are functions that upon invocations guarantee not to modify the state of the blockchain.
View Functions
Functions can be declared as view if they promise not to modify the state.
The following statements are considered modifying the state:
1. Writing to state variables.
2. Emitting events.
3. Creating other contracts.
4. Using selfdestruct.
5. Sending Ether via call.
6. Calling any function not marked as view or pure.
7. Using low-level calls.
8. Using inline assembly that contains certain opcodes.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// SPDX-License-Identifier: GPL-3.0
 
pragma solidity >=0.7.0 <0.9.0;

contract Demo {
 
    uint256 age;

    function getAgeTimesNumber(uint256 number) public view returns (uint256) {
        return age * number;
    }

    function getMsgSender() public view returns (address) {
        return msg.sender;
    }
}
Pure Functions
Functions can be declared pure in which case they promise not to read from or modify the state. In particular, it should be possible to evaluate a pure function at compile-time given only its inputs and msg.data (more on that later), but without any knowledge of the current blockchain state.
The following are considered reading from the state:
1. Reading from state variables.
2. Accessing address(this).balance or <address>.balance.
3. Accessing any of the members of block, tx, msg (with the exception of msg.sig and msg.data).
4. Calling any function not marked pure.
5. Using inline assembly that contains certain opcodes.
1
2
3
4
5
6
7
8
9
// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.7.0 <0.9.0;

contract Demo {
    function multiply(uint256 a, uint256 b) public pure returns (uint256) {
        return a * b;
    }
}
Appendix
Function call arguments can be given by name, in any order, if they are enclosed in { }.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// SPDX-License-Identifier: GPL-3.0
 
pragma solidity >=0.7.0 <0.9.0;

contract Demo {
    function twoTimesThree() public pure returns (uint256) {
        // same as multiple(2, 3) or multiple({b: 3, a: 2})
        return multiply({a: 2, b: 3});
    }

    function multiply(uint256 a, uint256 b) public pure returns (uint256) {
        return a * b;
    }
}
The names of unused parameters (especially return parameters) can be omitted. Those parameters will still be present on the stack, but they are inaccessible.
1
2
3
4
5
6
7
8
9
// SPDX-License-Identifier: GPL-3.0
 
pragma solidity >=0.7.0 <0.9.0;

contract Demo {
    function foo(uint k, uint) public pure returns(uint) {
        return k;
    }
}
You can either explicitly assign values to return variables.
1
2
3
4
5
6
7
8
9
10
// SPDX-License-Identifier: GPL-3.0
 
pragma solidity >=0.7.0 <0.9.0;

contract Demo {
    function foo(uint _a, uint _b) public pure returns(uint result_1, uint result_2) {
        result_1 = _a;
        result_2 = _b;
    }
}
Or you can provide return values directly with the return statement. In that case, the names of return variables can be omitted.
1
2
3
4
5
6
7
8
9
// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.7.0 <0.9.0;

contract Demo {
    function foo(uint _a, uint _b) public pure returns(uint result_1, uint result_2) {
        return (_a, _b);
    }
}