utilities.adoc 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  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#ECDSA-toEthSignedMessageHash-bytes32-[`toEthSignedMessageHash`].
  8. [source,solidity]
  9. ----
  10. using ECDSA for bytes32;
  11. function _verify(bytes32 data, bytes memory signature, address account) internal pure returns (bool) {
  12. return data
  13. .toEthSignedMessageHash()
  14. .recover(signature) == account;
  15. }
  16. ----
  17. WARNING: Getting signature verification right is not trivial: make sure you fully read and understand xref:api:utils.adoc#ECDSA[`ECDSA`]'s documentation.
  18. === Verifying Merkle Proofs
  19. xref:api:utils.adoc#MerkleProof[`MerkleProof`] provides:
  20. * 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].
  21. * xref:api:utils.adoc#MerkleProof-multiProofVerify-bytes32-bytes32---bytes32---bool---[`multiProofVerify`] - can prove multiple values are part of a Merkle tree.
  22. [[introspection]]
  23. == Introspection
  24. 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:
  25. * xref:api:utils.adoc#IERC165[`IERC165`] — this is the ERC165 interface that defines xref:api:utils.adoc#IERC165-supportsInterface-bytes4-[`supportsInterface`]. When implementing ERC165, you'll conform to this interface.
  26. * 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 ERC721 implementation.
  27. * xref:api:utils.adoc#ERC165Checker[`ERC165Checker`] — ERC165Checker simplifies the process of checking whether or not a contract supports an interface you care about.
  28. * include with `using ERC165Checker for address;`
  29. * xref:api:utils.adoc#ERC165Checker-_supportsInterface-address-bytes4-[`myAddress._supportsInterface(bytes4)`]
  30. * xref:api:utils.adoc#ERC165Checker-_supportsAllInterfaces-address-bytes4---[`myAddress._supportsAllInterfaces(bytes4[\])`]
  31. [source,solidity]
  32. ----
  33. contract MyContract {
  34. using ERC165Checker for address;
  35. bytes4 private InterfaceId_ERC721 = 0x80ac58cd;
  36. /**
  37. * @dev transfer an ERC721 token from this contract to someone else
  38. */
  39. function transferERC721(
  40. address token,
  41. address to,
  42. uint256 tokenId
  43. )
  44. public
  45. {
  46. require(token.supportsInterface(InterfaceId_ERC721), "IS_NOT_721_TOKEN");
  47. IERC721(token).transferFrom(address(this), to, tokenId);
  48. }
  49. }
  50. ----
  51. [[math]]
  52. == Math
  53. The most popular math related library OpenZeppelin Contracts provides is xref:api:utils.adoc#SafeMath[`SafeMath`], which provides mathematical functions that protect your contract from overflows and underflows.
  54. Include the contract with `using SafeMath for uint256;` and then call the functions:
  55. * `myNumber.add(otherNumber)`
  56. * `myNumber.sub(otherNumber)`
  57. * `myNumber.div(otherNumber)`
  58. * `myNumber.mul(otherNumber)`
  59. * `myNumber.mod(otherNumber)`
  60. Easy!
  61. [[payment]]
  62. == Payment
  63. 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 xref:api:finance.adoc#PaymentSplitter[`PaymentSplitter`]!
  64. 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 https://consensys.github.io/smart-contract-best-practices/[Ethereum Smart Contract Best Practices] website.
  65. [[collections]]
  66. == Collections
  67. If you need support for more powerful collections than Solidity's native arrays and mappings, take a look at xref:api:utils.adoc#EnumerableSet[`EnumerableSet`] and xref:api:utils.adoc#EnumerableMap[`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.
  68. [[misc]]
  69. == Misc
  70. === Base64
  71. xref:api:utils.adoc#Base64[`Base64`] util allows you to transform `bytes32` data into its Base64 `string` representation.
  72. This is especially useful for building URL-safe tokenURIs for both xref:api:token/ERC721.adoc#IERC721Metadata-tokenURI-uint256-[`ERC721`] or xref:api:token/ERC1155.adoc#IERC1155MetadataURI-uri-uint256-[`ERC1155`]. 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.
  73. Here is an example to send JSON Metadata through a Base64 Data URI using an ERC721:
  74. [source, solidity]
  75. ----
  76. // contracts/My721Token.sol
  77. // SPDX-License-Identifier: MIT
  78. import {ERC721} from "@openzeppelin/contracts/token/ERC721/ERC721.sol";
  79. import {Strings} from "@openzeppelin/contracts/utils/Strings.sol";
  80. import {Base64} from "@openzeppelin/contracts/utils/Base64.sol";
  81. contract My721Token is ERC721 {
  82. using Strings for uint256;
  83. constructor() ERC721("My721Token", "MTK") {}
  84. ...
  85. function tokenURI(uint256 tokenId)
  86. public
  87. pure
  88. override
  89. returns (string memory)
  90. {
  91. bytes memory dataURI = abi.encodePacked(
  92. '{',
  93. '"name": "My721Token #', tokenId.toString(), '"',
  94. // Replace with extra ERC721 Metadata properties
  95. '}'
  96. );
  97. return string(
  98. abi.encodePacked(
  99. "data:application/json;base64,",
  100. Base64.encode(dataURI)
  101. )
  102. );
  103. }
  104. }
  105. ----
  106. === Multicall
  107. 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.
  108. Consider this dummy contract:
  109. [source,solidity]
  110. ----
  111. // contracts/Box.sol
  112. // SPDX-License-Identifier: MIT
  113. pragma solidity ^0.8.19;
  114. import "@openzeppelin/contracts/utils/Multicall.sol";
  115. contract Box is Multicall {
  116. function foo() public {
  117. ...
  118. }
  119. function bar() public {
  120. ...
  121. }
  122. }
  123. ----
  124. This is how to call the `multicall` function using Truffle, allowing `foo` and `bar` to be called in a single transaction:
  125. [source,javascript]
  126. ----
  127. // scripts/foobar.js
  128. const Box = artifacts.require('Box');
  129. const instance = await Box.new();
  130. await instance.multicall([
  131. instance.contract.methods.foo().encodeABI(),
  132. instance.contract.methods.bar().encodeABI()
  133. ]);
  134. ----