// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.6.0) (token/ERC1155/extensions/ERC1155Supply.sol) pragma solidity ^0.8.24; import {ERC1155Upgradeable} from "../ERC1155Upgradeable.sol"; import {Arrays} from "@openzeppelin/contracts/utils/Arrays.sol"; import {Initializable} from "@openzeppelin/contracts/proxy/utils/Initializable.sol"; /** * @dev Extension of ERC-1155 that adds tracking of total supply per id. * * Useful for scenarios where Fungible and Non-fungible tokens have to be * clearly identified. Note: While a `totalSupply` of 1 may mean the * corresponding token is an NFT, there are no inherent guarantees that * no more tokens with the same id will be minted in future. * * NOTE: This contract implies a global limit of 2**256 - 1 to the number of tokens * that can be minted. * * CAUTION: This extension should not be added in an upgrade to an already deployed contract. */ abstract contract ERC1155SupplyUpgradeable is Initializable, ERC1155Upgradeable { using Arrays for uint256[]; /// @custom:storage-location erc7201:openzeppelin.storage.ERC1155Supply struct ERC1155SupplyStorage { mapping(uint256 id => uint256) _totalSupply; uint256 _totalSupplyAll; } // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.ERC1155Supply")) - 1)) & ~bytes32(uint256(0xff)) bytes32 private constant ERC1155SupplyStorageLocation = 0x4a593662ee04d27b6a00ebb31be7fe0c102c2ade82a7c5d764f2df05dc4e2800; function _getERC1155SupplyStorage() private pure returns (ERC1155SupplyStorage storage $) { assembly { $.slot := ERC1155SupplyStorageLocation } } function __ERC1155Supply_init() internal onlyInitializing { } function __ERC1155Supply_init_unchained() internal onlyInitializing { } /** * @dev Total value of tokens with a given id. */ function totalSupply(uint256 id) public view virtual returns (uint256) { ERC1155SupplyStorage storage $ = _getERC1155SupplyStorage(); return $._totalSupply[id]; } /** * @dev Total value of tokens. */ function totalSupply() public view virtual returns (uint256) { ERC1155SupplyStorage storage $ = _getERC1155SupplyStorage(); return $._totalSupplyAll; } /** * @dev Indicates whether any tokens exist with a given id, or not. */ function exists(uint256 id) public view virtual returns (bool) { return totalSupply(id) > 0; } /// @inheritdoc ERC1155Upgradeable function _update( address from, address to, uint256[] memory ids, uint256[] memory values ) internal virtual override { ERC1155SupplyStorage storage $ = _getERC1155SupplyStorage(); super._update(from, to, ids, values); if (from == address(0)) { uint256 totalMintValue = 0; for (uint256 i = 0; i < ids.length; ++i) { uint256 value = values.unsafeMemoryAccess(i); // Overflow check required: The rest of the code assumes that totalSupply never overflows $._totalSupply[ids.unsafeMemoryAccess(i)] += value; totalMintValue += value; } // Overflow check required: The rest of the code assumes that totalSupplyAll never overflows $._totalSupplyAll += totalMintValue; } if (to == address(0)) { uint256 totalBurnValue = 0; for (uint256 i = 0; i < ids.length; ++i) { uint256 value = values.unsafeMemoryAccess(i); unchecked { // Overflow not possible: values[i] <= balanceOf(from, ids[i]) <= totalSupply(ids[i]) $._totalSupply[ids.unsafeMemoryAccess(i)] -= value; // Overflow not possible: sum_i(values[i]) <= sum_i(totalSupply(ids[i])) <= totalSupplyAll totalBurnValue += value; } } unchecked { // Overflow not possible: totalBurnValue = sum_i(values[i]) <= sum_i(totalSupply(ids[i])) <= totalSupplyAll $._totalSupplyAll -= totalBurnValue; } } } }