Utilities
The OpenZeppelin Contracts provide a ton of useful utilities that you can use in your project. Here are some of the more popular ones.
Cryptography
Checking Signatures On-Chain
ECDSA
provides functions for recovering and managing Ethereum account ECDSA signatures. These are often generated via web3.eth.sign
, and are a 65 byte array (of type bytes
in Solidity) arranged the following way: [[v (1)], [r (32)], [s (32)]]
.
The data signer can be recovered with ECDSA.recover
, and its address compared to verify the signature. Most wallets will hash the data to sign and add the prefix '\x19Ethereum Signed Message:\n', so when attempting to recover the signer of an Ethereum signed message hash, you’ll want to use toEthSignedMessageHash
.
using ECDSA for bytes32;
function _verify(bytes32 data, bytes memory signature, address account) internal pure returns (bool) {
return data
.toEthSignedMessageHash()
.recover(signature) == account;
}
Getting signature verification right is not trivial: make sure you fully read and understand ECDSA 's documentation.
|
Verifying Merkle Proofs
MerkleProof
provides:
-
verify
- can prove that some value is part of a Merkle tree. -
multiProofVerify
- can prove multiple values are part of a Merkle tree.
Introspection
In Solidity, it’s frequently helpful to know whether or not a contract supports an interface you’d like to use. ERC165 is a standard that helps do runtime interface detection. Contracts provide helpers both for implementing ERC165 in your contracts and querying other contracts:
-
IERC165
— this is the ERC165 interface that definessupportsInterface
. When implementing ERC165, you’ll conform to this interface. -
ERC165
— inherit this contract if you’d like to support interface detection using a lookup table in contract storage. You can register interfaces using_registerInterface(bytes4)
: check out example usage as part of the ERC721 implementation. -
ERC165Checker
— ERC165Checker simplifies the process of checking whether or not a contract supports an interface you care about. -
include with
using ERC165Checker for address;
contract MyContract {
using ERC165Checker for address;
bytes4 private InterfaceId_ERC721 = 0x80ac58cd;
/**
* @dev transfer an ERC721 token from this contract to someone else
*/
function transferERC721(
address token,
address to,
uint256 tokenId
)
public
{
require(token.supportsInterface(InterfaceId_ERC721), "IS_NOT_721_TOKEN");
IERC721(token).transferFrom(address(this), to, tokenId);
}
}
Math
The most popular math related library OpenZeppelin Contracts provides is SafeMath
, which provides mathematical functions that protect your contract from overflows and underflows.
Include the contract with using SafeMath for uint256;
and then call the functions:
-
myNumber.add(otherNumber)
-
myNumber.sub(otherNumber)
-
myNumber.div(otherNumber)
-
myNumber.mul(otherNumber)
-
myNumber.mod(otherNumber)
Easy!
Payment
Want to split some payments between multiple people? Maybe you have an app that sends 30% of art purchases to the original creator and 70% of the profits to the current owner; you can build that with PaymentSplitter
!
In Solidity, there are some security concerns with blindly sending money to accounts, since it allows them to execute arbitrary code. You can read up on these security concerns in the Ethereum Smart Contract Best Practices website. One of the ways to fix reentrancy and stalling problems is, instead of immediately sending Ether to accounts that need it, you can use PullPayment
, which offers an _asyncTransfer
function for sending money to something and requesting that they withdrawPayments()
it later.
If you want to Escrow some funds, check out Escrow
and ConditionalEscrow
for governing the release of some escrowed Ether.
Collections
If you need support for more powerful collections than Solidity’s native arrays and mappings, take a look at EnumerableSet
and EnumerableMap
. They are similar to mappings in that they store and remove elements in constant time and don’t allow for repeated entries, but they also support enumeration, which means you can easily query all stored entries both on and off-chain.
Misc
Want to check if an address is a contract? Use Address
and Address.isContract()
.
Want to keep track of some numbers that increment by 1 every time you want another one? Check out Counters
. This is useful for lots of things, like creating incremental identifiers, as shown on the ERC721 guide.
Base64
Base64
util allows you to transform bytes32
data into its Base64 string
representation.
This is especially useful for building URL-safe tokenURIs for both ERC721
or ERC1155
. This library provides a clever way to serve URL-safe Data URI compliant strings to serve on-chain data structures.
Here is an example to send JSON Metadata through a Base64 Data URI using an ERC721:
// contracts/My721Token.sol
// SPDX-License-Identifier: MIT
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/utils/Strings.sol";
import "@openzeppelin/contracts/utils/Base64.sol";
contract My721Token is ERC721 {
using Strings for uint256;
constructor() ERC721("My721Token", "MTK") {}
...
function tokenURI(uint256 tokenId)
public
pure
override
returns (string memory)
{
bytes memory dataURI = abi.encodePacked(
'{',
'"name": "My721Token #', tokenId.toString(), '"',
// Replace with extra ERC721 Metadata properties
'}'
);
return string(
abi.encodePacked(
"data:application/json;base64,",
Base64.encode(dataURI)
)
);
}
}
Multicall
The Multicall
abstract contract comes with a multicall
function that bundles together multiple calls in a single external call. With it, external accounts may perform atomic operations comprising several function calls. This is not only useful for EOAs to make multiple calls in a single transaction, it’s also a way to revert a previous call if a later one fails.
Consider this dummy contract:
// contracts/Box.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/utils/Multicall.sol";
contract Box is Multicall {
function foo() public {
...
}
function bar() public {
...
}
}
This is how to call the multicall
function using Truffle, allowing foo
and bar
to be called in a single transaction:
// scripts/foobar.js
const Box = artifacts.require('Box');
const instance = await Box.new();
await instance.multicall([
instance.contract.methods.foo().encodeABI(),
instance.contract.methods.bar().encodeABI()
]);