utilities.adoc 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. = Utilities
  2. The OpenZeppelin Contracts provide a ton of useful utilities that you can use in your project. Here are some of the more popular ones.
  3. [[cryptography]]
  4. == Cryptography
  5. === Checking Signatures On-Chain
  6. xref:api:utils.adoc#ECDSA[`ECDSA`] provides functions for recovering and managing Ethereum account ECDSA signatures. These are often generated via https://web3js.readthedocs.io/en/v1.7.3/web3-eth.html#sign[`web3.eth.sign`], and are a 65 byte array (of type `bytes` in Solidity) arranged the following way: `[[v (1)], [r (32)], [s (32)]]`.
  7. The data signer can be recovered with xref:api:utils.adoc#ECDSA-recover-bytes32-bytes-[`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 xref:api:utils.adoc#MessageHashUtils-toEthSignedMessageHash-bytes32-[`toEthSignedMessageHash`].
  8. [source,solidity]
  9. ----
  10. using ECDSA for bytes32;
  11. using MessageHashUtils for bytes32;
  12. function _verify(bytes32 data, bytes memory signature, address account) internal pure returns (bool) {
  13. return data
  14. .toEthSignedMessageHash()
  15. .recover(signature) == account;
  16. }
  17. ----
  18. WARNING: Getting signature verification right is not trivial: make sure you fully read and understand xref:api:utils.adoc#MessageHashUtils[`MessageHashUtils`]'s and xref:api:utils.adoc#ECDSA[`ECDSA`]'s documentation.
  19. === Verifying Merkle Proofs
  20. xref:api:utils.adoc#MerkleProof[`MerkleProof`] provides:
  21. * xref:api:utils.adoc#MerkleProof-verify-bytes32---bytes32-bytes32-[`verify`] - can prove that some value is part of a https://en.wikipedia.org/wiki/Merkle_tree[Merkle tree].
  22. * xref:api:utils.adoc#MerkleProof-multiProofVerify-bytes32-bytes32---bytes32---bool---[`multiProofVerify`] - can prove multiple values are part of a Merkle tree.
  23. [[introspection]]
  24. == Introspection
  25. In Solidity, it's frequently helpful to know whether or not a contract supports an interface you'd like to use. ERC-165 is a standard that helps do runtime interface detection. Contracts provide helpers both for implementing ERC-165 in your contracts and querying other contracts:
  26. * xref:api:utils.adoc#IERC165[`IERC165`] — this is the ERC-165 interface that defines xref:api:utils.adoc#IERC165-supportsInterface-bytes4-[`supportsInterface`]. When implementing ERC-165, you'll conform to this interface.
  27. * xref:api:utils.adoc#ERC165[`ERC165`] — inherit this contract if you'd like to support interface detection using a lookup table in contract storage. You can register interfaces using xref:api:utils.adoc#ERC165-_registerInterface-bytes4-[`_registerInterface(bytes4)`]: check out example usage as part of the ERC-721 implementation.
  28. * xref:api:utils.adoc#ERC165Checker[`ERC165Checker`] — ERC165Checker simplifies the process of checking whether or not a contract supports an interface you care about.
  29. * include with `using ERC165Checker for address;`
  30. * xref:api:utils.adoc#ERC165Checker-_supportsInterface-address-bytes4-[`myAddress._supportsInterface(bytes4)`]
  31. * xref:api:utils.adoc#ERC165Checker-_supportsAllInterfaces-address-bytes4---[`myAddress._supportsAllInterfaces(bytes4[\])`]
  32. [source,solidity]
  33. ----
  34. contract MyContract {
  35. using ERC165Checker for address;
  36. bytes4 private InterfaceId_ERC721 = 0x80ac58cd;
  37. /**
  38. * @dev transfer an ERC-721 token from this contract to someone else
  39. */
  40. function transferERC721(
  41. address token,
  42. address to,
  43. uint256 tokenId
  44. )
  45. public
  46. {
  47. require(token.supportsInterface(InterfaceId_ERC721), "IS_NOT_721_TOKEN");
  48. IERC721(token).transferFrom(address(this), to, tokenId);
  49. }
  50. }
  51. ----
  52. [[math]]
  53. == Math
  54. Although Solidity already provides math operators (i.e. `+`, `-`, etc.), Contracts includes xref:api:utils.adoc#Math[`Math`]; a set of utilities for dealing with mathematical operators, with support for extra operations (eg. xref:api:utils.adoc#Math-average-uint256-uint256-[`average`]) and xref:api:utils.adoc#SignedMath[`SignedMath`]; a library specialized in signed math operations.
  55. Include these contracts with `using Math for uint256` or `using SignedMath for int256` and then use their functions in your code:
  56. [source,solidity]
  57. ----
  58. contract MyContract {
  59. using Math for uint256;
  60. using SignedMath for int256;
  61. function tryOperations(uint256 a, uint256 b) internal pure {
  62. (bool overflowsAdd, uint256 resultAdd) = x.tryAdd(y);
  63. (bool overflowsSub, uint256 resultSub) = x.trySub(y);
  64. (bool overflowsMul, uint256 resultMul) = x.tryMul(y);
  65. (bool overflowsDiv, uint256 resultDiv) = x.tryDiv(y);
  66. // ...
  67. }
  68. function unsignedAverage(int256 a, int256 b) {
  69. int256 avg = a.average(b);
  70. // ...
  71. }
  72. }
  73. ----
  74. Easy!
  75. [[structures]]
  76. == Structures
  77. Some use cases require more powerful data structures than arrays and mappings offered natively in Solidity. Contracts provides these libraries for enhanced data structure management:
  78. - xref:api:utils.adoc#BitMaps[`BitMaps`]: Store packed booleans in storage.
  79. - xref:api:utils.adoc#Checkpoints[`Checkpoints`]: Checkpoint values with built-in lookups.
  80. - xref:api:utils.adoc#DoubleEndedQueue[`DoubleEndedQueue`]: Store items in a queue with `pop()` and `queue()` constant time operations.
  81. - xref:api:utils.adoc#EnumerableSet[`EnumerableSet`]: A https://en.wikipedia.org/wiki/Set_(abstract_data_type)[set] with enumeration capabilities.
  82. - xref:api:utils.adoc#EnumerableMap[`EnumerableMap`]: A `mapping` variant with enumeration capabilities.
  83. The `Enumerable*` structures 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.
  84. [[misc]]
  85. == Misc
  86. === Base64
  87. xref:api:utils.adoc#Base64[`Base64`] util allows you to transform `bytes32` data into its Base64 `string` representation.
  88. This is especially useful for building URL-safe tokenURIs for both xref:api:token/ERC721.adoc#IERC721Metadata-tokenURI-uint256-[`ERC-721`] or xref:api:token/ERC1155.adoc#IERC1155MetadataURI-uri-uint256-[`ERC-1155`]. This library provides a clever way to serve URL-safe https://developer.mozilla.org/docs/Web/HTTP/Basics_of_HTTP/Data_URIs/[Data URI] compliant strings to serve on-chain data structures.
  89. Here is an example to send JSON Metadata through a Base64 Data URI using an ERC-721:
  90. [source, solidity]
  91. ----
  92. // contracts/My721Token.sol
  93. // SPDX-License-Identifier: MIT
  94. import {ERC721} from "@openzeppelin/contracts/token/ERC721/ERC721.sol";
  95. import {Strings} from "@openzeppelin/contracts/utils/Strings.sol";
  96. import {Base64} from "@openzeppelin/contracts/utils/Base64.sol";
  97. contract My721Token is ERC721 {
  98. using Strings for uint256;
  99. constructor() ERC721("My721Token", "MTK") {}
  100. ...
  101. function tokenURI(uint256 tokenId)
  102. public
  103. pure
  104. override
  105. returns (string memory)
  106. {
  107. bytes memory dataURI = abi.encodePacked(
  108. '{',
  109. '"name": "My721Token #', tokenId.toString(), '"',
  110. // Replace with extra ERC-721 Metadata properties
  111. '}'
  112. );
  113. return string(
  114. abi.encodePacked(
  115. "data:application/json;base64,",
  116. Base64.encode(dataURI)
  117. )
  118. );
  119. }
  120. }
  121. ----
  122. === Multicall
  123. 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.
  124. Consider this dummy contract:
  125. [source,solidity]
  126. ----
  127. // contracts/Box.sol
  128. // SPDX-License-Identifier: MIT
  129. pragma solidity ^0.8.20;
  130. import "@openzeppelin/contracts/utils/Multicall.sol";
  131. contract Box is Multicall {
  132. function foo() public {
  133. ...
  134. }
  135. function bar() public {
  136. ...
  137. }
  138. }
  139. ----
  140. This is how to call the `multicall` function using Truffle, allowing `foo` and `bar` to be called in a single transaction:
  141. [source,javascript]
  142. ----
  143. // scripts/foobar.js
  144. const Box = artifacts.require('Box');
  145. const instance = await Box.new();
  146. await instance.multicall([
  147. instance.contract.methods.foo().encodeABI(),
  148. instance.contract.methods.bar().encodeABI()
  149. ]);
  150. ----