Like almost every programming language, Solidity has variables: local and state. Additionally, Solidity functions accept parameters the same as JavaScript. This tutorial discusses both variables and parameters which are defined similarly.
It also talks about the Solidity ABI which is the Contract Application Binary Interface designed for managing smart contracts. Additionally, we answer the question: how many parameters Solidity event can have?
Contents
- 1. Solidity Variables: Main Tips
- 2. Local Variables
- 3. State Variables
- 3.1. Constant State Variables
- 3.2. State Variables in Storage: Layout
- 4. Control Variable Visibility
- 5. Units and Globally Available Variables
- 5.1. Ether Units
- 5.2. Time Units
- 6. Special Functions and Variables
- 6.1. Block and Transaction Properties
- 6.2. Solidity ABI Encoding and Decoding Functions
- 6.3. Functions for Error Handling
- 7. Introduction to Parameters
- 7.1. Function Parameters
- 7.2. Return Variables
- 7.3. Event Parameters
- 8. Removing Unnecessary Variable Bits
- 9. Solidity Variables: Summary
Solidity Variables: Main Tips
- There are two types of Solidity variables: local and state. State variables can be set as
constant
. - You can adjust variable visibility to control who can use its values.
- Solidity handles parameters similarly to JavaScript.
- Events accept a limited number of arguments.
Local Variables
Local variables are defined inside functions. They are kept in the stack
location.
Between different function calls, the local variables do not save their value. Therefore, blockchain does not hold such values and they disappear after functions conclude.
State Variables
You declare state variables in the contract
part. State variables are stored in storage
. The Solidity storage
keyword indicates one of the possible storing locations.
pragma solidity >=0.4.0 <0.7.0;
contract SimpleStorage {
uint storedData; // State variable
// ...
}
Note: the word state specifies that variables indicate the state of smart contracts.
Constant State Variables
It is possible to declare state variables with Solidity constant
. This assignment takes place during the compiling process since it must be set from a constant expression.
Solidity does not permit expressions that reach storage, execution or blockchain data, or makes calls to external contracts.
Note: currently, constants are used for strings and value types.
pragma solidity >=0.4.0 <0.7.0;
contract C {
uint constant x = 32**22 + 8;
string constant text = "abc";
bytes32 constant myHash = keccak256("abc");
}
State Variables in Storage: Layout
Solidity places variables that are statically-sized in storage from position 0 (except mapping and dynamically-sized array). It puts items that require less than 32 bytes into a single storage slot (if achievable).
Control Variable Visibility
Visibility modifiers restrict who can use values of Solidity variables. Here is a list of modifiers that change these permissions:
public
: anyone can get the value of a variable.external
: only external functions can get the value of a local variable. It is not used on state variables.internal
: only functions in this contract and related contracts can get values.private
: access limited to functions from this contract.
Units and Globally Available Variables
Ether Units
Ether units indicate currency and can be used on Solidity variables or on any literal number. Literal numbers contain the following suffixes to indicate a subdenomination of Ether:
wei
finney
szabo
ether
assert(1 wei == 1);
assert(1 szabo == 1e12);
assert(1 finney == 1e15);
assert(1 ether == 1e18);
Remember: specify the selected unit at the end of any literal number and the denominations will take place automatically.
Time Units
Apply the Solidity time units to any literal number to specify the selected unit of time. The following suffixes can be attached:
seconds
minutes
hours
days
weeks
Note: Solidity deprecated the year unit due to the issue with leap years. A similar problem exists with leap seconds (not all days have 24 hours). Be careful when using these units for calendar management.
This example shows the way days
suffix affects functions:
function f(uint start, uint daysAfter) public {
if (now >= start + daysAfter * 1 days) {
// ...
}
}
Note: do not apply these Solidity time units to variables.
Special Functions and Variables
Variables and methods that remain permanently in the global namespace are special. They give details about the blockchain or the overall use of utility functions.
Block and Transaction Properties
Property | Description |
---|---|
blockhash(uint blockNumber) returns (bytes32) | Hash of the provided block. It functions for 256 most recent blocks (not including the current ones). |
block.coinbase (address payable) | Address of the current block miner. |
block.difficulty (uint) | Difficulty of the current block. |
block.gaslimit (uint) | Gas limit of the current block. |
block.number (uint) | Number of the current block. |
block.timestamp (uint) | Timestamp of the current block (in seconds since Unix epoch). |
gasleft() returns (uint256) | Remaining gas. |
msg.data (bytes calldata) | Full calldata. |
msg.sender (address payable) | Sender of the message. |
msg.sig (bytes4) | First four bytes of the calldata. |
msg.value (uint) | Number of wei transferred with the message. |
now (uint) | Timestamp of the current block. |
tx.gasprice (uint) | Gas price of the transaction. |
tx.origin (address payable) | Sender of the transaction. |
External function calls can change the values of msg
members (also msg.sender
and msg.value
).
Note: prior to Solidity version 0.4.22, blockhash was known as block.blockhash. The latter one was removed in 0.5.0 version. Additionally, gasleft was called msg.gas.
Solidity ABI Encoding and Decoding Functions
Function | Description |
---|---|
abi.decode(bytes memory encodedData, (...)) returns (...) | ABI-decodes the provided data. The second argument indicates the types. |
abi.encode(...) returns (bytes memory) | ABI-encodes the provided arguments. |
abi.encodePacked(...) returns (bytes memory) | Starts packed encoding for the provided arguments. |
abi.encodeWithSelector(bytes4 selector, ...) returns (bytes memory) | ABI-encodes the provided arguments (starting from the second) and adds the indicated four-byte selector. |
abi.encodeWithSignature(string memory signature, ...) returns (bytes memory) | Same as abi.encodeWithSelector(bytes4(keccak256(bytes(signature))), ...)` |
Note: these methods create data for external function calls. By using them, it is possible to avoid invoking the external function.
Functions for Error Handling
Function | Description |
---|---|
assert(bool condition) | If the condition is not met, it will cause an invalid opcode and thus state change reversion (for internal errors). |
require(bool condition) | If the condition is not met, it will revert (for errors in inputs or external components). |
require(bool condition, string memory message) | If the condition is not met, it will revert (for errors in inputs or external components). Shows an error message as well. |
revert() | Cancels execution and reverts state changes. |
revert(string memory reason) | Cancels execution and reverts state changes. Gives an explanatory string. |
Introduction to Parameters
Currently, Solidity default parameters do not exist. This section reviews function parameters, return variables and event parameters.
Function Parameters
Just like in JavaScript, functions accept parameters as input. As output, Solidity functions can deliver an arbitrary number of values.
Note: Solidity defines parameters and variables in the same manner. It is possible to omit unapplied parameters as well.
To make Solidity accept one type of external call with two integers, use this code:
pragma solidity >=0.4.16 <0.7.0;
contract Simple {
uint sum;
function taker(uint _a, uint _b) public {
sum = _a + _b;
}
}
Return Variables
Solidity defines the return variables by following the same syntax after the keyword returns
. This example shows that two results will be returned (the sum and the product of two integers included as function parameters:
pragma solidity >=0.4.16 <0.7.0;
contract Simple {
function arithmetic(uint _a, uint _b)
public
pure
returns (uint o_sum, uint o_product)
{
o_sum = _a + _b;
o_product = _a * _b;
}
}
You can omit names of return variables. Use return variables as regular local variables. They have their default value (if not specified otherwise).
It is possible to explicitly assign to return variables and then leave the function using return
. Also, use return
statement to specify return values (single or multiple):
pragma solidity >=0.4.16 <0.7.0;
contract Simple {
function arithmetic(uint _a, uint _b)
public
pure
returns (uint o_sum, uint o_product)
{
return (_a + _b, _a * _b);
}
}
Event Parameters
A common question is how many parameters Solidity event can have. Event declaration is the same as function declaration. The usage of non-indexed parameters is less strict: the limit for such event arguments is 17.
Remember: arrays as arguments count as 2.
Warning: events can have up to 3 indexed arguments.
Removing Unnecessary Variable Bits
Solidity allows omitting the remaining bits of values that do not reach the length of 256-bit. The Solidity compiler removes such unnecessary bits automatically to prevent a negative effect on operations.
Note: there are some exceptions. It is not necessary to remove bits that won’t affect the next operation. For instance, boolean values should not be omitted before they are applied as a condition for JUMPI.
Solidity Variables: Summary
- Solidity variables can either be local or state (they can also be constant).
- You can control the visibility of variable values with modifiers.
- Functions accept parameters, just like in JavaScript.
- There are limitations for event parameters: up to 3 indexed parameters and 17 non-indexed ones.