Continuing on the previous article concerning address member types and type information, we’ll learn about
- members of bytes and string,
- error handling,
- math and crypto functions, and
- contract-related properties and functions.
It’s part of our long-standing tradition to make this (and other) articles a faithful companion, or a supplement to the official Solidity documentation.
Members of bytes and string
Two practical member functions are available for concatenating a variable number of the underlying type arguments to a resulting, same-type array: bytes.concat(...)
and string.concat(...)
.
We can use them as:
bytes.concat(...) returns (bytes memory)
and
string.concat(...) returns (string memory).
π Recommended Tutorial: Solidity String and Byte Concatenation
Error Handling
We will discuss error handling in more detail in one of the future articles, but this time, we’ll get familiar with the functions available to implement error handling in our smart contract projects.
assert(bool condition)
This function will cause a Panic error and revert the contract state if the condition is not fulfilled. It is intended to signal internal errors.
require(bool condition)
This function will revert the contract state if the condition is not fulfilled. It is intended to signal errors in internal or external components.
require(bool condition, string memory message)
This function will revert the contract state if the condition is not fulfilled. It is intended to signal errors in internal or external components. In addition to its shorter variant, this function provides a defined error message.
revert()
This function unconditionally aborts execution and reverts the state changes.
revert(string memory reason)
This function unconditionally aborts execution and reverts the state changes, while providing a reason for reverting the contract state.
Mathematical and Cryptographic Functions
In this subsection, we’ll show a few commonly used mathematical and cryptographic functions.
addmod(uint x, uint y, uint k) returns (uint)
This function computes (x + y) % k
, i.e. the remainder of a sum divided by k
. The addition is computed with arbitrary precision, avoiding the wrap-around at 2**256
. Since Solidity v0.5.0 the function makes the assertion that k != 0
.
mulmod(uint x, uint y, uint k) returns (uint)
This function computes (x * y) % k
, i.e., the remainder of a product divided by k
. The product is computed with arbitrary precision, avoiding the wrap-around at 2**256
. Since Solidity v0.5.0 the function makes the assertion that k != 0
.
keccak256(bytes memory) returns (bytes32)
This function computes the Keccak-256 hash of the input parameter placed in memory.
Note: a previous alias for keccak256
called sha3
was removed in Solidity v0.5.0.
sha256(bytes memory) returns (bytes32)
This function computes the SHA-256 hash of the input parameter placed in memory.
ripemd160(bytes memory) returns (bytes20)
This function computes the RIPEMD-160 hash of the input parameter placed in memory.
ecrecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) returns (address)
This function recovers the address associated with the public key from the elliptic curve signature (hence the “ec
” prefix) and returns a zero on error.
The function parameters represent the ECDSA values (docs) of the signature algorithm:
- r = first 32 bytes of signature
- s = second 32 bytes of signature
- v = final 1 byte of signature
We should also pay attention to the fact that the ecrecover(...)
function returns an address as the address type, not as an address payable type.
However, we already know (from previous articles) how to convert an address
to the address payable
type, e.g. in a case we need to transfer the funds to a recovered address, which is returned from ecrecover(...)
.
β‘ Warning: We should be aware that by using ecrecover(...)
, a valid signature can be turned into another valid signature without requiring knowledge of the corresponding private key. In the Homestead hard fork (the second major version of the Ethereum platform), the described issue was fixed for _transaction_
signatures (more details are available in EIP-2 here), but the ecrecover(...)
function wasn’t modified. This behavior shouldn’t pose a problem, with the exclusion of cases where we require uniqueness of signatures or plan to use them for item identification. However, we can find a workaround to this problem in OpenZeppelin, the standard for secure blockchain applications. There’s an ECDSA helper library available for use as a wrapper for ecrecover(...)
, enabling us to circumvent the problematic behavior.
When we use functions sha256(...)
, ripemd160(...)
, and ecrecover(...)
in smart contracts run on a private blockchain (but not on mainnet or testnet), we should expect to see the Out-of-Gas error.
The cause of this warning lies in the fact that these functions are implemented as precompiled contracts and start existing after they receive the first message. Since messages to non-existing contracts are more expensive (because they first have to be instantiated), their first execution might trigger an Out-of-Gas error.
A common workaround is to first instantiate these contracts by sending 1 Wei to each of them, and then start using the contracts in a regular way.
Contract-related
In this subsection, we’re listing the contract-related members.
this (current contractβs type)
The keyword this
refers to the current contract, explicitly convertible to the address type.
selfdestruct(address payable recipient)
This function destroys the current contract, sends its funds to the recipient address, and ends the execution. We should note several EVM-inherited peculiarities:
- The receiving contract’s receive function is not executed in the process.
- The initiating contract is destroyed only at the end of the transaction execution, so a revert may undo the destruction.
We can directly call functions of the current contract.
Note: “Prior to version 0.5.0, there was a function called suicide(...)
with the same semantics as selfdestruct(...)
.” (docs)
Conclusion
ποΈ In this article, we learned about members of bytes and string types. We also refreshed our memory on the topic of error handling. Then we got introduced to mathematical and cryptographic functions. We concluded the article with contract-related properties and functions.
First, we introduced and explained the very common member function of bytes and string types: the function for bytes and string concatenation.
Second, we explained and listed the ways how to handle errors in Solidity.
Third, we took a look at mathematical and cryptographic functions and explained what they do and how they work. Then we mentioned peculiar behaviors of functions and became aware of specific situations that we should recognize when using the functions.
Fourth, we made a simple introduction to contract-related members, showing what they are and how they work.
What’s Next?
This tutorial is part of our extended Solidity documentation with videos and more accessible examples and explanations. You can navigate the series here (all links open in a new tab):