Optimize array access in ERC1155 (#4300)

Co-authored-by: Hadrien Croubois <hadrien.croubois@gmail.com>
Co-authored-by: Francisco Giordano <fg@frang.io>
This commit is contained in:
Claudia Barcelo
2023-06-15 04:39:34 +02:00
committed by GitHub
parent 2477534260
commit 05ef6924ac
4 changed files with 41 additions and 5 deletions

View File

@ -0,0 +1,5 @@
---
'openzeppelin-solidity': minor
---
`Arrays`: Add `unsafeMemoryAccess` helpers to read from a memory array without checking the length.

View File

@ -0,0 +1,5 @@
---
'openzeppelin-solidity': patch
---
`ERC1155`: Optimize array accesses by skipping bounds checking when unnecessary.

View File

@ -8,6 +8,7 @@ import "./IERC1155Receiver.sol";
import "./extensions/IERC1155MetadataURI.sol"; import "./extensions/IERC1155MetadataURI.sol";
import "../../utils/Context.sol"; import "../../utils/Context.sol";
import "../../utils/introspection/ERC165.sol"; import "../../utils/introspection/ERC165.sol";
import "../../utils/Arrays.sol";
import "../../interfaces/draft-IERC6093.sol"; import "../../interfaces/draft-IERC6093.sol";
/** /**
@ -18,6 +19,9 @@ import "../../interfaces/draft-IERC6093.sol";
* _Available since v3.1._ * _Available since v3.1._
*/ */
abstract contract ERC1155 is Context, ERC165, IERC1155, IERC1155MetadataURI, IERC1155Errors { abstract contract ERC1155 is Context, ERC165, IERC1155, IERC1155MetadataURI, IERC1155Errors {
using Arrays for uint256[];
using Arrays for address[];
// Mapping from token ID to account balances // Mapping from token ID to account balances
mapping(uint256 => mapping(address => uint256)) private _balances; mapping(uint256 => mapping(address => uint256)) private _balances;
@ -87,7 +91,7 @@ abstract contract ERC1155 is Context, ERC165, IERC1155, IERC1155MetadataURI, IER
uint256[] memory batchBalances = new uint256[](accounts.length); uint256[] memory batchBalances = new uint256[](accounts.length);
for (uint256 i = 0; i < accounts.length; ++i) { for (uint256 i = 0; i < accounts.length; ++i) {
batchBalances[i] = balanceOf(accounts[i], ids[i]); batchBalances[i] = balanceOf(accounts.unsafeMemoryAccess(i), ids.unsafeMemoryAccess(i));
} }
return batchBalances; return batchBalances;
@ -157,8 +161,8 @@ abstract contract ERC1155 is Context, ERC165, IERC1155, IERC1155MetadataURI, IER
address operator = _msgSender(); address operator = _msgSender();
for (uint256 i = 0; i < ids.length; ++i) { for (uint256 i = 0; i < ids.length; ++i) {
uint256 id = ids[i]; uint256 id = ids.unsafeMemoryAccess(i);
uint256 amount = amounts[i]; uint256 amount = amounts.unsafeMemoryAccess(i);
if (from != address(0)) { if (from != address(0)) {
uint256 fromBalance = _balances[id][from]; uint256 fromBalance = _balances[id][from];
@ -176,8 +180,8 @@ abstract contract ERC1155 is Context, ERC165, IERC1155, IERC1155MetadataURI, IER
} }
if (ids.length == 1) { if (ids.length == 1) {
uint256 id = ids[0]; uint256 id = ids.unsafeMemoryAccess(0);
uint256 amount = amounts[0]; uint256 amount = amounts.unsafeMemoryAccess(0);
emit TransferSingle(operator, from, to, id, amount); emit TransferSingle(operator, from, to, id, amount);
if (to != address(0)) { if (to != address(0)) {
_doSafeTransferAcceptanceCheck(operator, from, to, id, amount, data); _doSafeTransferAcceptanceCheck(operator, from, to, id, amount, data);

View File

@ -102,4 +102,26 @@ library Arrays {
} }
return slot.getUint256Slot(); return slot.getUint256Slot();
} }
/**
* @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
*
* WARNING: Only use if you are certain `pos` is lower than the array length.
*/
function unsafeMemoryAccess(uint256[] memory arr, uint256 pos) internal pure returns (uint256 res) {
assembly {
res := mload(add(add(arr, 0x20), mul(pos, 0x20)))
}
}
/**
* @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
*
* WARNING: Only use if you are certain `pos` is lower than the array length.
*/
function unsafeMemoryAccess(address[] memory arr, uint256 pos) internal pure returns (address res) {
assembly {
res := mload(add(add(arr, 0x20), mul(pos, 0x20)))
}
}
} }