diff --git a/contracts/OpenEditionERC721.sol b/contracts/OpenEditionERC721.sol index 9619fc602..48d93408c 100644 --- a/contracts/OpenEditionERC721.sol +++ b/contracts/OpenEditionERC721.sol @@ -17,7 +17,7 @@ pragma solidity ^0.8.11; import "@openzeppelin/contracts-upgradeable/utils/StringsUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/interfaces/IERC2981Upgradeable.sol"; -import "./eip/ERC721AVirtualApproveUpgradeable.sol"; +import "./eip/queryable/ERC721AQueryableUpgradeable.sol"; // ========== Internal imports ========== @@ -28,7 +28,6 @@ import "./lib/CurrencyTransferLib.sol"; import "./extension/Multicall.sol"; import "./extension/ContractMetadata.sol"; -import "./extension/PlatformFee.sol"; import "./extension/Royalty.sol"; import "./extension/PrimarySale.sol"; import "./extension/Ownable.sol"; @@ -42,7 +41,6 @@ import "./extension/DefaultOperatorFiltererUpgradeable.sol"; contract OpenEditionERC721 is Initializable, ContractMetadata, - PlatformFee, Royalty, PrimarySale, Ownable, @@ -52,7 +50,7 @@ contract OpenEditionERC721 is ERC2771ContextUpgradeable, Multicall, DefaultOperatorFiltererUpgradeable, - ERC721AUpgradeable + ERC721AQueryableUpgradeable { using StringsUpgradeable for uint256; @@ -83,10 +81,8 @@ contract OpenEditionERC721 is address[] memory _trustedForwarders, address _saleRecipient, address _royaltyRecipient, - uint128 _royaltyBps, - uint128 _platformFeeBps, - address _platformFeeRecipient - ) external initializer { + uint128 _royaltyBps + ) external initializerERC721A initializer { bytes32 _transferRole = keccak256("TRANSFER_ROLE"); bytes32 _minterRole = keccak256("MINTER_ROLE"); @@ -104,7 +100,6 @@ contract OpenEditionERC721 is _setupRole(_transferRole, _defaultAdmin); _setupRole(_transferRole, address(0)); - _setupPlatformFeeInfo(_platformFeeRecipient, _platformFeeBps); _setupDefaultRoyaltyInfo(_royaltyRecipient, _royaltyBps); _setupPrimarySaleRecipient(_saleRecipient); @@ -117,7 +112,13 @@ contract OpenEditionERC721 is //////////////////////////////////////////////////////////////*/ /// @dev Returns the URI for a given tokenId. - function tokenURI(uint256 _tokenId) public view virtual override returns (string memory) { + function tokenURI(uint256 _tokenId) + public + view + virtual + override(ERC721AUpgradeable, IERC721AUpgradeable) + returns (string memory) + { if (!_exists(_tokenId)) { revert("!ID"); } @@ -130,7 +131,7 @@ contract OpenEditionERC721 is public view virtual - override(ERC721AUpgradeable, IERC165) + override(ERC721AUpgradeable, IERC165, IERC721AUpgradeable) returns (bool) { return super.supportsInterface(interfaceId) || type(IERC2981Upgradeable).interfaceId == interfaceId; @@ -172,22 +173,7 @@ contract OpenEditionERC721 is address saleRecipient = _primarySaleRecipient == address(0) ? primarySaleRecipient() : _primarySaleRecipient; - uint256 fees; - address feeRecipient; - - PlatformFeeType feeType = getPlatformFeeType(); - if (feeType == PlatformFeeType.Flat) { - (feeRecipient, fees) = getFlatPlatformFeeInfo(); - } else { - uint16 platformFeeBps; - (feeRecipient, platformFeeBps) = getPlatformFeeInfo(); - fees = (totalPrice * platformFeeBps) / MAX_BPS; - } - - require(totalPrice >= fees, "!F"); - - CurrencyTransferLib.transferCurrency(_currency, _msgSender(), feeRecipient, fees); - CurrencyTransferLib.transferCurrency(_currency, _msgSender(), saleRecipient, totalPrice - fees); + CurrencyTransferLib.transferCurrency(_currency, _msgSender(), saleRecipient, totalPrice); } /// @dev Transfers the NFTs being claimed. @@ -196,15 +182,10 @@ contract OpenEditionERC721 is override returns (uint256 startTokenId_) { - startTokenId_ = _currentIndex; + startTokenId_ = _nextTokenId(); _safeMint(_to, _quantityBeingClaimed); } - /// @dev Checks whether platform fee info can be set in the given execution context. - function _canSetPlatformFeeInfo() internal view override returns (bool) { - return hasRole(DEFAULT_ADMIN_ROLE, _msgSender()); - } - /// @dev Checks whether primary sale recipient can be set in the given execution context. function _canSetPrimarySaleRecipient() internal view override returns (bool) { return hasRole(DEFAULT_ADMIN_ROLE, _msgSender()); @@ -249,18 +230,18 @@ contract OpenEditionERC721 is */ function totalMinted() external view returns (uint256) { unchecked { - return _currentIndex - _startTokenId(); + return _nextTokenId() - _startTokenId(); } } /// @dev The tokenId of the next NFT that will be minted / lazy minted. function nextTokenIdToMint() external view returns (uint256) { - return _currentIndex; + return _nextTokenId(); } /// @dev The next token ID of the NFT that can be claimed. function nextTokenIdToClaim() external view returns (uint256) { - return _currentIndex; + return _nextTokenId(); } /// @dev Burns `tokenId`. See {ERC721-_burn}. @@ -287,12 +268,21 @@ contract OpenEditionERC721 is } /// @dev See {ERC721-setApprovalForAll}. - function setApprovalForAll(address operator, bool approved) public override onlyAllowedOperatorApproval(operator) { + function setApprovalForAll(address operator, bool approved) + public + override(IERC721AUpgradeable, ERC721AUpgradeable) + onlyAllowedOperatorApproval(operator) + { super.setApprovalForAll(operator, approved); } /// @dev See {ERC721-approve}. - function approve(address operator, uint256 tokenId) public override onlyAllowedOperatorApproval(operator) { + function approve(address operator, uint256 tokenId) + public + payable + override(ERC721AUpgradeable, IERC721AUpgradeable) + onlyAllowedOperatorApproval(operator) + { super.approve(operator, tokenId); } @@ -301,7 +291,7 @@ contract OpenEditionERC721 is address from, address to, uint256 tokenId - ) public override(ERC721AUpgradeable) onlyAllowedOperator(from) { + ) public payable override(ERC721AUpgradeable, IERC721AUpgradeable) onlyAllowedOperator(from) { super.transferFrom(from, to, tokenId); } @@ -310,7 +300,7 @@ contract OpenEditionERC721 is address from, address to, uint256 tokenId - ) public override(ERC721AUpgradeable) onlyAllowedOperator(from) { + ) public payable override(ERC721AUpgradeable, IERC721AUpgradeable) onlyAllowedOperator(from) { super.safeTransferFrom(from, to, tokenId); } @@ -320,7 +310,7 @@ contract OpenEditionERC721 is address to, uint256 tokenId, bytes memory data - ) public override(ERC721AUpgradeable) onlyAllowedOperator(from) { + ) public payable override(ERC721AUpgradeable, IERC721AUpgradeable) onlyAllowedOperator(from) { super.safeTransferFrom(from, to, tokenId, data); } @@ -328,23 +318,15 @@ contract OpenEditionERC721 is return _msgSender(); } - function _msgSender() - internal - view - virtual - override(ContextUpgradeable, ERC2771ContextUpgradeable) - returns (address sender) - { + function _msgSenderERC721A() internal view virtual override returns (address) { + return _msgSender(); + } + + function _msgSender() internal view virtual override(ERC2771ContextUpgradeable) returns (address sender) { return ERC2771ContextUpgradeable._msgSender(); } - function _msgData() - internal - view - virtual - override(ContextUpgradeable, ERC2771ContextUpgradeable) - returns (bytes calldata) - { + function _msgData() internal view virtual override(ERC2771ContextUpgradeable) returns (bytes calldata) { return ERC2771ContextUpgradeable._msgData(); } } diff --git a/contracts/base/Staking1155Base.sol b/contracts/base/Staking1155Base.sol index 5d6c558eb..be9a1979c 100644 --- a/contracts/base/Staking1155Base.sol +++ b/contracts/base/Staking1155Base.sol @@ -55,8 +55,8 @@ contract Staking1155Base is ContractMetadata, Multicall, Ownable, Staking1155, E uint256 private rewardTokenBalance; constructor( + uint80 _defaultTimeUnit, address _defaultAdmin, - uint256 _defaultTimeUnit, uint256 _defaultRewardsPerUnitTime, address _stakingToken, address _rewardToken, diff --git a/contracts/base/Staking20Base.sol b/contracts/base/Staking20Base.sol index 720e3ab1a..917699afc 100644 --- a/contracts/base/Staking20Base.sol +++ b/contracts/base/Staking20Base.sol @@ -51,8 +51,8 @@ contract Staking20Base is ContractMetadata, Multicall, Ownable, Staking20 { uint256 private rewardTokenBalance; constructor( + uint80 _timeUnit, address _defaultAdmin, - uint256 _timeUnit, uint256 _rewardRatioNumerator, uint256 _rewardRatioDenominator, address _stakingToken, diff --git a/contracts/eip/queryable/ERC721AQueryableUpgradeable.sol b/contracts/eip/queryable/ERC721AQueryableUpgradeable.sol new file mode 100644 index 000000000..2018c8bc5 --- /dev/null +++ b/contracts/eip/queryable/ERC721AQueryableUpgradeable.sol @@ -0,0 +1,201 @@ +// SPDX-License-Identifier: MIT +// ERC721A Contracts v4.2.3 +// Creator: Chiru Labs + +pragma solidity ^0.8.4; + +import "./IERC721AQueryableUpgradeable.sol"; +import "./ERC721AUpgradeable.sol"; +import "./ERC721A__Initializable.sol"; + +/** + * @title ERC721AQueryable. + * + * @dev ERC721A subclass with convenience query functions. + */ +abstract contract ERC721AQueryableUpgradeable is + ERC721A__Initializable, + ERC721AUpgradeable, + IERC721AQueryableUpgradeable +{ + function __ERC721AQueryable_init() internal onlyInitializingERC721A { + __ERC721AQueryable_init_unchained(); + } + + function __ERC721AQueryable_init_unchained() internal onlyInitializingERC721A {} + + /** + * @dev Returns the `TokenOwnership` struct at `tokenId` without reverting. + * + * If the `tokenId` is out of bounds: + * + * - `addr = address(0)` + * - `startTimestamp = 0` + * - `burned = false` + * - `extraData = 0` + * + * If the `tokenId` is burned: + * + * - `addr =
` + * - `startTimestamp = ` + * - `burned = true` + * - `extraData = ` + * + * Otherwise: + * + * - `addr =
` + * - `startTimestamp = ` + * - `burned = false` + * - `extraData = ` + */ + function explicitOwnershipOf(uint256 tokenId) + public + view + virtual + override + returns (TokenOwnership memory ownership) + { + unchecked { + if (tokenId >= _startTokenId()) { + if (tokenId < _nextTokenId()) { + // If the `tokenId` is within bounds, + // scan backwards for the initialized ownership slot. + while (!_ownershipIsInitialized(tokenId)) --tokenId; + return _ownershipAt(tokenId); + } + } + } + } + + /** + * @dev Returns an array of token IDs owned by `owner`, + * in the range [`start`, `stop`) + * (i.e. `start <= tokenId < stop`). + * + * This function allows for tokens to be queried if the collection + * grows too big for a single call of {ERC721AQueryable-tokensOfOwner}. + * + * Requirements: + * + * - `start < stop` + */ + function tokensOfOwnerIn( + address owner, + uint256 start, + uint256 stop + ) external view virtual override returns (uint256[] memory) { + return _tokensOfOwnerIn(owner, start, stop); + } + + /** + * @dev Returns an array of token IDs owned by `owner`. + * + * This function scans the ownership mapping and is O(`totalSupply`) in complexity. + * It is meant to be called off-chain. + * + * See {ERC721AQueryable-tokensOfOwnerIn} for splitting the scan into + * multiple smaller scans if the collection is large enough to cause + * an out-of-gas error (10K collections should be fine). + */ + function tokensOfOwner(address owner) external view virtual override returns (uint256[] memory) { + uint256 start = _startTokenId(); + uint256 stop = _nextTokenId(); + uint256[] memory tokenIds; + if (start != stop) tokenIds = _tokensOfOwnerIn(owner, start, stop); + return tokenIds; + } + + /** + * @dev Helper function for returning an array of token IDs owned by `owner`. + * + * Note that this function is optimized for smaller bytecode size over runtime gas, + * since it is meant to be called off-chain. + */ + function _tokensOfOwnerIn( + address owner, + uint256 start, + uint256 stop + ) private view returns (uint256[] memory) { + unchecked { + if (start >= stop) _revert(InvalidQueryRange.selector); + // Set `start = max(start, _startTokenId())`. + if (start < _startTokenId()) { + start = _startTokenId(); + } + uint256 stopLimit = _nextTokenId(); + // Set `stop = min(stop, stopLimit)`. + if (stop >= stopLimit) { + stop = stopLimit; + } + uint256[] memory tokenIds; + uint256 tokenIdsMaxLength = balanceOf(owner); + bool startLtStop = start < stop; + assembly { + // Set `tokenIdsMaxLength` to zero if `start` is less than `stop`. + tokenIdsMaxLength := mul(tokenIdsMaxLength, startLtStop) + } + if (tokenIdsMaxLength != 0) { + // Set `tokenIdsMaxLength = min(balanceOf(owner), stop - start)`, + // to cater for cases where `balanceOf(owner)` is too big. + if (stop - start <= tokenIdsMaxLength) { + tokenIdsMaxLength = stop - start; + } + assembly { + // Grab the free memory pointer. + tokenIds := mload(0x40) + // Allocate one word for the length, and `tokenIdsMaxLength` words + // for the data. `shl(5, x)` is equivalent to `mul(32, x)`. + mstore(0x40, add(tokenIds, shl(5, add(tokenIdsMaxLength, 1)))) + } + // We need to call `explicitOwnershipOf(start)`, + // because the slot at `start` may not be initialized. + TokenOwnership memory ownership = explicitOwnershipOf(start); + address currOwnershipAddr; + // If the starting slot exists (i.e. not burned), + // initialize `currOwnershipAddr`. + // `ownership.address` will not be zero, + // as `start` is clamped to the valid token ID range. + if (!ownership.burned) { + currOwnershipAddr = ownership.addr; + } + uint256 tokenIdsIdx; + // Use a do-while, which is slightly more efficient for this case, + // as the array will at least contain one element. + do { + ownership = _ownershipAt(start); + assembly { + switch mload(add(ownership, 0x40)) + // if `ownership.burned == false`. + case 0 { + // if `ownership.addr != address(0)`. + // The `addr` already has it's upper 96 bits clearned, + // since it is written to memory with regular Solidity. + if mload(ownership) { + currOwnershipAddr := mload(ownership) + } + // if `currOwnershipAddr == owner`. + // The `shl(96, x)` is to make the comparison agnostic to any + // dirty upper 96 bits in `owner`. + if iszero(shl(96, xor(currOwnershipAddr, owner))) { + tokenIdsIdx := add(tokenIdsIdx, 1) + mstore(add(tokenIds, shl(5, tokenIdsIdx)), start) + } + } + // Otherwise, reset `currOwnershipAddr`. + // This handles the case of batch burned tokens + // (burned bit of first slot set, remaining slots left uninitialized). + default { + currOwnershipAddr := 0 + } + start := add(start, 1) + } + } while (!(start == stop || tokenIdsIdx == tokenIdsMaxLength)); + // Store the length of the array. + assembly { + mstore(tokenIds, tokenIdsIdx) + } + } + return tokenIds; + } + } +} diff --git a/contracts/eip/queryable/ERC721AStorage.sol b/contracts/eip/queryable/ERC721AStorage.sol new file mode 100644 index 000000000..d9f5c12cd --- /dev/null +++ b/contracts/eip/queryable/ERC721AStorage.sol @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +library ERC721AStorage { + // Bypass for a `--via-ir` bug (https://github.com/chiru-labs/ERC721A/pull/364). + struct TokenApprovalRef { + address value; + } + + struct Layout { + // ============================================================= + // STORAGE + // ============================================================= + + // The next token ID to be minted. + uint256 _currentIndex; + // The number of tokens burned. + uint256 _burnCounter; + // Token name + string _name; + // Token symbol + string _symbol; + // Mapping from token ID to ownership details + // An empty struct value does not necessarily mean the token is unowned. + // See {_packedOwnershipOf} implementation for details. + // + // Bits Layout: + // - [0..159] `addr` + // - [160..223] `startTimestamp` + // - [224] `burned` + // - [225] `nextInitialized` + // - [232..255] `extraData` + mapping(uint256 => uint256) _packedOwnerships; + // Mapping owner address to address data. + // + // Bits Layout: + // - [0..63] `balance` + // - [64..127] `numberMinted` + // - [128..191] `numberBurned` + // - [192..255] `aux` + mapping(address => uint256) _packedAddressData; + // Mapping from token ID to approved address. + mapping(uint256 => ERC721AStorage.TokenApprovalRef) _tokenApprovals; + // Mapping from owner to operator approvals + mapping(address => mapping(address => bool)) _operatorApprovals; + } + + bytes32 internal constant STORAGE_SLOT = keccak256("ERC721A.contracts.storage.ERC721A"); + + function layout() internal pure returns (Layout storage l) { + bytes32 slot = STORAGE_SLOT; + assembly { + l.slot := slot + } + } +} diff --git a/contracts/eip/queryable/ERC721AUpgradeable.sol b/contracts/eip/queryable/ERC721AUpgradeable.sol new file mode 100644 index 000000000..0e0bf0b75 --- /dev/null +++ b/contracts/eip/queryable/ERC721AUpgradeable.sol @@ -0,0 +1,1111 @@ +// SPDX-License-Identifier: MIT +// ERC721A Contracts v4.2.3 +// Creator: Chiru Labs + +pragma solidity ^0.8.4; + +import "./IERC721AUpgradeable.sol"; +import { ERC721AStorage } from "./ERC721AStorage.sol"; +import "./ERC721A__Initializable.sol"; + +/** + * @dev Interface of ERC721 token receiver. + */ +interface ERC721A__IERC721ReceiverUpgradeable { + function onERC721Received( + address operator, + address from, + uint256 tokenId, + bytes calldata data + ) external returns (bytes4); +} + +/** + * @title ERC721A + * + * @dev Implementation of the [ERC721](https://eips.ethereum.org/EIPS/eip-721) + * Non-Fungible Token Standard, including the Metadata extension. + * Optimized for lower gas during batch mints. + * + * Token IDs are minted in sequential order (e.g. 0, 1, 2, 3, ...) + * starting from `_startTokenId()`. + * + * Assumptions: + * + * - An owner cannot have more than 2**64 - 1 (max value of uint64) of supply. + * - The maximum token ID cannot exceed 2**256 - 1 (max value of uint256). + */ +contract ERC721AUpgradeable is ERC721A__Initializable, IERC721AUpgradeable { + using ERC721AStorage for ERC721AStorage.Layout; + + // ============================================================= + // CONSTANTS + // ============================================================= + + // Mask of an entry in packed address data. + uint256 private constant _BITMASK_ADDRESS_DATA_ENTRY = (1 << 64) - 1; + + // The bit position of `numberMinted` in packed address data. + uint256 private constant _BITPOS_NUMBER_MINTED = 64; + + // The bit position of `numberBurned` in packed address data. + uint256 private constant _BITPOS_NUMBER_BURNED = 128; + + // The bit position of `aux` in packed address data. + uint256 private constant _BITPOS_AUX = 192; + + // Mask of all 256 bits in packed address data except the 64 bits for `aux`. + uint256 private constant _BITMASK_AUX_COMPLEMENT = (1 << 192) - 1; + + // The bit position of `startTimestamp` in packed ownership. + uint256 private constant _BITPOS_START_TIMESTAMP = 160; + + // The bit mask of the `burned` bit in packed ownership. + uint256 private constant _BITMASK_BURNED = 1 << 224; + + // The bit position of the `nextInitialized` bit in packed ownership. + uint256 private constant _BITPOS_NEXT_INITIALIZED = 225; + + // The bit mask of the `nextInitialized` bit in packed ownership. + uint256 private constant _BITMASK_NEXT_INITIALIZED = 1 << 225; + + // The bit position of `extraData` in packed ownership. + uint256 private constant _BITPOS_EXTRA_DATA = 232; + + // Mask of all 256 bits in a packed ownership except the 24 bits for `extraData`. + uint256 private constant _BITMASK_EXTRA_DATA_COMPLEMENT = (1 << 232) - 1; + + // The mask of the lower 160 bits for addresses. + uint256 private constant _BITMASK_ADDRESS = (1 << 160) - 1; + + // The maximum `quantity` that can be minted with {_mintERC2309}. + // This limit is to prevent overflows on the address data entries. + // For a limit of 5000, a total of 3.689e15 calls to {_mintERC2309} + // is required to cause an overflow, which is unrealistic. + uint256 private constant _MAX_MINT_ERC2309_QUANTITY_LIMIT = 5000; + + // The `Transfer` event signature is given by: + // `keccak256(bytes("Transfer(address,address,uint256)"))`. + bytes32 private constant _TRANSFER_EVENT_SIGNATURE = + 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef; + + // ============================================================= + // CONSTRUCTOR + // ============================================================= + + function __ERC721A_init(string memory name_, string memory symbol_) internal onlyInitializingERC721A { + __ERC721A_init_unchained(name_, symbol_); + } + + function __ERC721A_init_unchained(string memory name_, string memory symbol_) internal onlyInitializingERC721A { + ERC721AStorage.layout()._name = name_; + ERC721AStorage.layout()._symbol = symbol_; + ERC721AStorage.layout()._currentIndex = _startTokenId(); + } + + // ============================================================= + // TOKEN COUNTING OPERATIONS + // ============================================================= + + /** + * @dev Returns the starting token ID. + * To change the starting token ID, please override this function. + */ + function _startTokenId() internal view virtual returns (uint256) { + return 0; + } + + /** + * @dev Returns the next token ID to be minted. + */ + function _nextTokenId() internal view virtual returns (uint256) { + return ERC721AStorage.layout()._currentIndex; + } + + /** + * @dev Returns the total number of tokens in existence. + * Burned tokens will reduce the count. + * To get the total number of tokens minted, please see {_totalMinted}. + */ + function totalSupply() public view virtual override returns (uint256) { + // Counter underflow is impossible as _burnCounter cannot be incremented + // more than `_currentIndex - _startTokenId()` times. + unchecked { + return ERC721AStorage.layout()._currentIndex - ERC721AStorage.layout()._burnCounter - _startTokenId(); + } + } + + /** + * @dev Returns the total amount of tokens minted in the contract. + */ + function _totalMinted() internal view virtual returns (uint256) { + // Counter underflow is impossible as `_currentIndex` does not decrement, + // and it is initialized to `_startTokenId()`. + unchecked { + return ERC721AStorage.layout()._currentIndex - _startTokenId(); + } + } + + /** + * @dev Returns the total number of tokens burned. + */ + function _totalBurned() internal view virtual returns (uint256) { + return ERC721AStorage.layout()._burnCounter; + } + + // ============================================================= + // ADDRESS DATA OPERATIONS + // ============================================================= + + /** + * @dev Returns the number of tokens in `owner`'s account. + */ + function balanceOf(address owner) public view virtual override returns (uint256) { + if (owner == address(0)) _revert(BalanceQueryForZeroAddress.selector); + return ERC721AStorage.layout()._packedAddressData[owner] & _BITMASK_ADDRESS_DATA_ENTRY; + } + + /** + * Returns the number of tokens minted by `owner`. + */ + function _numberMinted(address owner) internal view returns (uint256) { + return + (ERC721AStorage.layout()._packedAddressData[owner] >> _BITPOS_NUMBER_MINTED) & _BITMASK_ADDRESS_DATA_ENTRY; + } + + /** + * Returns the number of tokens burned by or on behalf of `owner`. + */ + function _numberBurned(address owner) internal view returns (uint256) { + return + (ERC721AStorage.layout()._packedAddressData[owner] >> _BITPOS_NUMBER_BURNED) & _BITMASK_ADDRESS_DATA_ENTRY; + } + + /** + * Returns the auxiliary data for `owner`. (e.g. number of whitelist mint slots used). + */ + function _getAux(address owner) internal view returns (uint64) { + return uint64(ERC721AStorage.layout()._packedAddressData[owner] >> _BITPOS_AUX); + } + + /** + * Sets the auxiliary data for `owner`. (e.g. number of whitelist mint slots used). + * If there are multiple variables, please pack them into a uint64. + */ + function _setAux(address owner, uint64 aux) internal virtual { + uint256 packed = ERC721AStorage.layout()._packedAddressData[owner]; + uint256 auxCasted; + // Cast `aux` with assembly to avoid redundant masking. + assembly { + auxCasted := aux + } + packed = (packed & _BITMASK_AUX_COMPLEMENT) | (auxCasted << _BITPOS_AUX); + ERC721AStorage.layout()._packedAddressData[owner] = packed; + } + + // ============================================================= + // IERC165 + // ============================================================= + + /** + * @dev Returns true if this contract implements the interface defined by + * `interfaceId`. See the corresponding + * [EIP section](https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified) + * to learn more about how these ids are created. + * + * This function call must use less than 30000 gas. + */ + function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { + // The interface IDs are constants representing the first 4 bytes + // of the XOR of all function selectors in the interface. + // See: [ERC165](https://eips.ethereum.org/EIPS/eip-165) + // (e.g. `bytes4(i.functionA.selector ^ i.functionB.selector ^ ...)`) + return + interfaceId == 0x01ffc9a7 || // ERC165 interface ID for ERC165. + interfaceId == 0x80ac58cd || // ERC165 interface ID for ERC721. + interfaceId == 0x5b5e139f; // ERC165 interface ID for ERC721Metadata. + } + + // ============================================================= + // IERC721Metadata + // ============================================================= + + /** + * @dev Returns the token collection name. + */ + function name() public view virtual override returns (string memory) { + return ERC721AStorage.layout()._name; + } + + /** + * @dev Returns the token collection symbol. + */ + function symbol() public view virtual override returns (string memory) { + return ERC721AStorage.layout()._symbol; + } + + /** + * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token. + */ + function tokenURI(uint256 tokenId) public view virtual override returns (string memory) { + if (!_exists(tokenId)) _revert(URIQueryForNonexistentToken.selector); + + string memory baseURI = _baseURI(); + return bytes(baseURI).length != 0 ? string(abi.encodePacked(baseURI, _toString(tokenId))) : ""; + } + + /** + * @dev Base URI for computing {tokenURI}. If set, the resulting URI for each + * token will be the concatenation of the `baseURI` and the `tokenId`. Empty + * by default, it can be overridden in child contracts. + */ + function _baseURI() internal view virtual returns (string memory) { + return ""; + } + + // ============================================================= + // OWNERSHIPS OPERATIONS + // ============================================================= + + /** + * @dev Returns the owner of the `tokenId` token. + * + * Requirements: + * + * - `tokenId` must exist. + */ + function ownerOf(uint256 tokenId) public view virtual override returns (address) { + return address(uint160(_packedOwnershipOf(tokenId))); + } + + /** + * @dev Gas spent here starts off proportional to the maximum mint batch size. + * It gradually moves to O(1) as tokens get transferred around over time. + */ + function _ownershipOf(uint256 tokenId) internal view virtual returns (TokenOwnership memory) { + return _unpackedOwnership(_packedOwnershipOf(tokenId)); + } + + /** + * @dev Returns the unpacked `TokenOwnership` struct at `index`. + */ + function _ownershipAt(uint256 index) internal view virtual returns (TokenOwnership memory) { + return _unpackedOwnership(ERC721AStorage.layout()._packedOwnerships[index]); + } + + /** + * @dev Returns whether the ownership slot at `index` is initialized. + * An uninitialized slot does not necessarily mean that the slot has no owner. + */ + function _ownershipIsInitialized(uint256 index) internal view virtual returns (bool) { + return ERC721AStorage.layout()._packedOwnerships[index] != 0; + } + + /** + * @dev Initializes the ownership slot minted at `index` for efficiency purposes. + */ + function _initializeOwnershipAt(uint256 index) internal virtual { + if (ERC721AStorage.layout()._packedOwnerships[index] == 0) { + ERC721AStorage.layout()._packedOwnerships[index] = _packedOwnershipOf(index); + } + } + + /** + * Returns the packed ownership data of `tokenId`. + */ + function _packedOwnershipOf(uint256 tokenId) private view returns (uint256 packed) { + if (_startTokenId() <= tokenId) { + packed = ERC721AStorage.layout()._packedOwnerships[tokenId]; + // If the data at the starting slot does not exist, start the scan. + if (packed == 0) { + if (tokenId >= ERC721AStorage.layout()._currentIndex) _revert(OwnerQueryForNonexistentToken.selector); + // Invariant: + // There will always be an initialized ownership slot + // (i.e. `ownership.addr != address(0) && ownership.burned == false`) + // before an unintialized ownership slot + // (i.e. `ownership.addr == address(0) && ownership.burned == false`) + // Hence, `tokenId` will not underflow. + // + // We can directly compare the packed value. + // If the address is zero, packed will be zero. + for (;;) { + unchecked { + packed = ERC721AStorage.layout()._packedOwnerships[--tokenId]; + } + if (packed == 0) continue; + if (packed & _BITMASK_BURNED == 0) return packed; + // Otherwise, the token is burned, and we must revert. + // This handles the case of batch burned tokens, where only the burned bit + // of the starting slot is set, and remaining slots are left uninitialized. + _revert(OwnerQueryForNonexistentToken.selector); + } + } + // Otherwise, the data exists and we can skip the scan. + // This is possible because we have already achieved the target condition. + // This saves 2143 gas on transfers of initialized tokens. + // If the token is not burned, return `packed`. Otherwise, revert. + if (packed & _BITMASK_BURNED == 0) return packed; + } + _revert(OwnerQueryForNonexistentToken.selector); + } + + /** + * @dev Returns the unpacked `TokenOwnership` struct from `packed`. + */ + function _unpackedOwnership(uint256 packed) private pure returns (TokenOwnership memory ownership) { + ownership.addr = address(uint160(packed)); + ownership.startTimestamp = uint64(packed >> _BITPOS_START_TIMESTAMP); + ownership.burned = packed & _BITMASK_BURNED != 0; + ownership.extraData = uint24(packed >> _BITPOS_EXTRA_DATA); + } + + /** + * @dev Packs ownership data into a single uint256. + */ + function _packOwnershipData(address owner, uint256 flags) private view returns (uint256 result) { + assembly { + // Mask `owner` to the lower 160 bits, in case the upper bits somehow aren't clean. + owner := and(owner, _BITMASK_ADDRESS) + // `owner | (block.timestamp << _BITPOS_START_TIMESTAMP) | flags`. + result := or(owner, or(shl(_BITPOS_START_TIMESTAMP, timestamp()), flags)) + } + } + + /** + * @dev Returns the `nextInitialized` flag set if `quantity` equals 1. + */ + function _nextInitializedFlag(uint256 quantity) private pure returns (uint256 result) { + // For branchless setting of the `nextInitialized` flag. + assembly { + // `(quantity == 1) << _BITPOS_NEXT_INITIALIZED`. + result := shl(_BITPOS_NEXT_INITIALIZED, eq(quantity, 1)) + } + } + + // ============================================================= + // APPROVAL OPERATIONS + // ============================================================= + + /** + * @dev Gives permission to `to` to transfer `tokenId` token to another account. See {ERC721A-_approve}. + * + * Requirements: + * + * - The caller must own the token or be an approved operator. + */ + function approve(address to, uint256 tokenId) public payable virtual override { + _approve(to, tokenId, true); + } + + /** + * @dev Returns the account approved for `tokenId` token. + * + * Requirements: + * + * - `tokenId` must exist. + */ + function getApproved(uint256 tokenId) public view virtual override returns (address) { + if (!_exists(tokenId)) _revert(ApprovalQueryForNonexistentToken.selector); + + return ERC721AStorage.layout()._tokenApprovals[tokenId].value; + } + + /** + * @dev Approve or remove `operator` as an operator for the caller. + * Operators can call {transferFrom} or {safeTransferFrom} + * for any token owned by the caller. + * + * Requirements: + * + * - The `operator` cannot be the caller. + * + * Emits an {ApprovalForAll} event. + */ + function setApprovalForAll(address operator, bool approved) public virtual override { + ERC721AStorage.layout()._operatorApprovals[_msgSenderERC721A()][operator] = approved; + emit ApprovalForAll(_msgSenderERC721A(), operator, approved); + } + + /** + * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`. + * + * See {setApprovalForAll}. + */ + function isApprovedForAll(address owner, address operator) public view virtual override returns (bool) { + return ERC721AStorage.layout()._operatorApprovals[owner][operator]; + } + + /** + * @dev Returns whether `tokenId` exists. + * + * Tokens can be managed by their owner or approved accounts via {approve} or {setApprovalForAll}. + * + * Tokens start existing when they are minted. See {_mint}. + */ + function _exists(uint256 tokenId) internal view virtual returns (bool result) { + if (_startTokenId() <= tokenId) { + if (tokenId < ERC721AStorage.layout()._currentIndex) { + uint256 packed; + while ((packed = ERC721AStorage.layout()._packedOwnerships[tokenId]) == 0) --tokenId; + result = packed & _BITMASK_BURNED == 0; + } + } + } + + /** + * @dev Returns whether `msgSender` is equal to `approvedAddress` or `owner`. + */ + function _isSenderApprovedOrOwner( + address approvedAddress, + address owner, + address msgSender + ) private pure returns (bool result) { + assembly { + // Mask `owner` to the lower 160 bits, in case the upper bits somehow aren't clean. + owner := and(owner, _BITMASK_ADDRESS) + // Mask `msgSender` to the lower 160 bits, in case the upper bits somehow aren't clean. + msgSender := and(msgSender, _BITMASK_ADDRESS) + // `msgSender == owner || msgSender == approvedAddress`. + result := or(eq(msgSender, owner), eq(msgSender, approvedAddress)) + } + } + + /** + * @dev Returns the storage slot and value for the approved address of `tokenId`. + */ + function _getApprovedSlotAndAddress(uint256 tokenId) + private + view + returns (uint256 approvedAddressSlot, address approvedAddress) + { + ERC721AStorage.TokenApprovalRef storage tokenApproval = ERC721AStorage.layout()._tokenApprovals[tokenId]; + // The following is equivalent to `approvedAddress = _tokenApprovals[tokenId].value`. + assembly { + approvedAddressSlot := tokenApproval.slot + approvedAddress := sload(approvedAddressSlot) + } + } + + // ============================================================= + // TRANSFER OPERATIONS + // ============================================================= + + /** + * @dev Transfers `tokenId` from `from` to `to`. + * + * Requirements: + * + * - `from` cannot be the zero address. + * - `to` cannot be the zero address. + * - `tokenId` token must be owned by `from`. + * - If the caller is not `from`, it must be approved to move this token + * by either {approve} or {setApprovalForAll}. + * + * Emits a {Transfer} event. + */ + function transferFrom( + address from, + address to, + uint256 tokenId + ) public payable virtual override { + uint256 prevOwnershipPacked = _packedOwnershipOf(tokenId); + + // Mask `from` to the lower 160 bits, in case the upper bits somehow aren't clean. + from = address(uint160(uint256(uint160(from)) & _BITMASK_ADDRESS)); + + if (address(uint160(prevOwnershipPacked)) != from) _revert(TransferFromIncorrectOwner.selector); + + (uint256 approvedAddressSlot, address approvedAddress) = _getApprovedSlotAndAddress(tokenId); + + // The nested ifs save around 20+ gas over a compound boolean condition. + if (!_isSenderApprovedOrOwner(approvedAddress, from, _msgSenderERC721A())) + if (!isApprovedForAll(from, _msgSenderERC721A())) _revert(TransferCallerNotOwnerNorApproved.selector); + + _beforeTokenTransfers(from, to, tokenId, 1); + + // Clear approvals from the previous owner. + assembly { + if approvedAddress { + // This is equivalent to `delete _tokenApprovals[tokenId]`. + sstore(approvedAddressSlot, 0) + } + } + + // Underflow of the sender's balance is impossible because we check for + // ownership above and the recipient's balance can't realistically overflow. + // Counter overflow is incredibly unrealistic as `tokenId` would have to be 2**256. + unchecked { + // We can directly increment and decrement the balances. + --ERC721AStorage.layout()._packedAddressData[from]; // Updates: `balance -= 1`. + ++ERC721AStorage.layout()._packedAddressData[to]; // Updates: `balance += 1`. + + // Updates: + // - `address` to the next owner. + // - `startTimestamp` to the timestamp of transfering. + // - `burned` to `false`. + // - `nextInitialized` to `true`. + ERC721AStorage.layout()._packedOwnerships[tokenId] = _packOwnershipData( + to, + _BITMASK_NEXT_INITIALIZED | _nextExtraData(from, to, prevOwnershipPacked) + ); + + // If the next slot may not have been initialized (i.e. `nextInitialized == false`) . + if (prevOwnershipPacked & _BITMASK_NEXT_INITIALIZED == 0) { + uint256 nextTokenId = tokenId + 1; + // If the next slot's address is zero and not burned (i.e. packed value is zero). + if (ERC721AStorage.layout()._packedOwnerships[nextTokenId] == 0) { + // If the next slot is within bounds. + if (nextTokenId != ERC721AStorage.layout()._currentIndex) { + // Initialize the next slot to maintain correctness for `ownerOf(tokenId + 1)`. + ERC721AStorage.layout()._packedOwnerships[nextTokenId] = prevOwnershipPacked; + } + } + } + } + + // Mask `to` to the lower 160 bits, in case the upper bits somehow aren't clean. + uint256 toMasked = uint256(uint160(to)) & _BITMASK_ADDRESS; + assembly { + // Emit the `Transfer` event. + log4( + 0, // Start of data (0, since no data). + 0, // End of data (0, since no data). + _TRANSFER_EVENT_SIGNATURE, // Signature. + from, // `from`. + toMasked, // `to`. + tokenId // `tokenId`. + ) + } + if (toMasked == 0) _revert(TransferToZeroAddress.selector); + + _afterTokenTransfers(from, to, tokenId, 1); + } + + /** + * @dev Equivalent to `safeTransferFrom(from, to, tokenId, '')`. + */ + function safeTransferFrom( + address from, + address to, + uint256 tokenId + ) public payable virtual override { + safeTransferFrom(from, to, tokenId, ""); + } + + /** + * @dev Safely transfers `tokenId` token from `from` to `to`. + * + * Requirements: + * + * - `from` cannot be the zero address. + * - `to` cannot be the zero address. + * - `tokenId` token must exist and be owned by `from`. + * - If the caller is not `from`, it must be approved to move this token + * by either {approve} or {setApprovalForAll}. + * - If `to` refers to a smart contract, it must implement + * {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. + * + * Emits a {Transfer} event. + */ + function safeTransferFrom( + address from, + address to, + uint256 tokenId, + bytes memory _data + ) public payable virtual override { + transferFrom(from, to, tokenId); + if (to.code.length != 0) + if (!_checkContractOnERC721Received(from, to, tokenId, _data)) { + _revert(TransferToNonERC721ReceiverImplementer.selector); + } + } + + /** + * @dev Hook that is called before a set of serially-ordered token IDs + * are about to be transferred. This includes minting. + * And also called before burning one token. + * + * `startTokenId` - the first token ID to be transferred. + * `quantity` - the amount to be transferred. + * + * Calling conditions: + * + * - When `from` and `to` are both non-zero, `from`'s `tokenId` will be + * transferred to `to`. + * - When `from` is zero, `tokenId` will be minted for `to`. + * - When `to` is zero, `tokenId` will be burned by `from`. + * - `from` and `to` are never both zero. + */ + function _beforeTokenTransfers( + address from, + address to, + uint256 startTokenId, + uint256 quantity + ) internal virtual {} + + /** + * @dev Hook that is called after a set of serially-ordered token IDs + * have been transferred. This includes minting. + * And also called after one token has been burned. + * + * `startTokenId` - the first token ID to be transferred. + * `quantity` - the amount to be transferred. + * + * Calling conditions: + * + * - When `from` and `to` are both non-zero, `from`'s `tokenId` has been + * transferred to `to`. + * - When `from` is zero, `tokenId` has been minted for `to`. + * - When `to` is zero, `tokenId` has been burned by `from`. + * - `from` and `to` are never both zero. + */ + function _afterTokenTransfers( + address from, + address to, + uint256 startTokenId, + uint256 quantity + ) internal virtual {} + + /** + * @dev Private function to invoke {IERC721Receiver-onERC721Received} on a target contract. + * + * `from` - Previous owner of the given token ID. + * `to` - Target address that will receive the token. + * `tokenId` - Token ID to be transferred. + * `_data` - Optional data to send along with the call. + * + * Returns whether the call correctly returned the expected magic value. + */ + function _checkContractOnERC721Received( + address from, + address to, + uint256 tokenId, + bytes memory _data + ) private returns (bool) { + try + ERC721A__IERC721ReceiverUpgradeable(to).onERC721Received(_msgSenderERC721A(), from, tokenId, _data) + returns (bytes4 retval) { + return retval == ERC721A__IERC721ReceiverUpgradeable(to).onERC721Received.selector; + } catch (bytes memory reason) { + if (reason.length == 0) { + _revert(TransferToNonERC721ReceiverImplementer.selector); + } + assembly { + revert(add(32, reason), mload(reason)) + } + } + } + + // ============================================================= + // MINT OPERATIONS + // ============================================================= + + /** + * @dev Mints `quantity` tokens and transfers them to `to`. + * + * Requirements: + * + * - `to` cannot be the zero address. + * - `quantity` must be greater than 0. + * + * Emits a {Transfer} event for each mint. + */ + function _mint(address to, uint256 quantity) internal virtual { + uint256 startTokenId = ERC721AStorage.layout()._currentIndex; + if (quantity == 0) _revert(MintZeroQuantity.selector); + + _beforeTokenTransfers(address(0), to, startTokenId, quantity); + + // Overflows are incredibly unrealistic. + // `balance` and `numberMinted` have a maximum limit of 2**64. + // `tokenId` has a maximum limit of 2**256. + unchecked { + // Updates: + // - `address` to the owner. + // - `startTimestamp` to the timestamp of minting. + // - `burned` to `false`. + // - `nextInitialized` to `quantity == 1`. + ERC721AStorage.layout()._packedOwnerships[startTokenId] = _packOwnershipData( + to, + _nextInitializedFlag(quantity) | _nextExtraData(address(0), to, 0) + ); + + // Updates: + // - `balance += quantity`. + // - `numberMinted += quantity`. + // + // We can directly add to the `balance` and `numberMinted`. + ERC721AStorage.layout()._packedAddressData[to] += quantity * ((1 << _BITPOS_NUMBER_MINTED) | 1); + + // Mask `to` to the lower 160 bits, in case the upper bits somehow aren't clean. + uint256 toMasked = uint256(uint160(to)) & _BITMASK_ADDRESS; + + if (toMasked == 0) _revert(MintToZeroAddress.selector); + + uint256 end = startTokenId + quantity; + uint256 tokenId = startTokenId; + + do { + assembly { + // Emit the `Transfer` event. + log4( + 0, // Start of data (0, since no data). + 0, // End of data (0, since no data). + _TRANSFER_EVENT_SIGNATURE, // Signature. + 0, // `address(0)`. + toMasked, // `to`. + tokenId // `tokenId`. + ) + } + // The `!=` check ensures that large values of `quantity` + // that overflows uint256 will make the loop run out of gas. + } while (++tokenId != end); + + ERC721AStorage.layout()._currentIndex = end; + } + _afterTokenTransfers(address(0), to, startTokenId, quantity); + } + + /** + * @dev Mints `quantity` tokens and transfers them to `to`. + * + * This function is intended for efficient minting only during contract creation. + * + * It emits only one {ConsecutiveTransfer} as defined in + * [ERC2309](https://eips.ethereum.org/EIPS/eip-2309), + * instead of a sequence of {Transfer} event(s). + * + * Calling this function outside of contract creation WILL make your contract + * non-compliant with the ERC721 standard. + * For full ERC721 compliance, substituting ERC721 {Transfer} event(s) with the ERC2309 + * {ConsecutiveTransfer} event is only permissible during contract creation. + * + * Requirements: + * + * - `to` cannot be the zero address. + * - `quantity` must be greater than 0. + * + * Emits a {ConsecutiveTransfer} event. + */ + function _mintERC2309(address to, uint256 quantity) internal virtual { + uint256 startTokenId = ERC721AStorage.layout()._currentIndex; + if (to == address(0)) _revert(MintToZeroAddress.selector); + if (quantity == 0) _revert(MintZeroQuantity.selector); + if (quantity > _MAX_MINT_ERC2309_QUANTITY_LIMIT) _revert(MintERC2309QuantityExceedsLimit.selector); + + _beforeTokenTransfers(address(0), to, startTokenId, quantity); + + // Overflows are unrealistic due to the above check for `quantity` to be below the limit. + unchecked { + // Updates: + // - `balance += quantity`. + // - `numberMinted += quantity`. + // + // We can directly add to the `balance` and `numberMinted`. + ERC721AStorage.layout()._packedAddressData[to] += quantity * ((1 << _BITPOS_NUMBER_MINTED) | 1); + + // Updates: + // - `address` to the owner. + // - `startTimestamp` to the timestamp of minting. + // - `burned` to `false`. + // - `nextInitialized` to `quantity == 1`. + ERC721AStorage.layout()._packedOwnerships[startTokenId] = _packOwnershipData( + to, + _nextInitializedFlag(quantity) | _nextExtraData(address(0), to, 0) + ); + + emit ConsecutiveTransfer(startTokenId, startTokenId + quantity - 1, address(0), to); + + ERC721AStorage.layout()._currentIndex = startTokenId + quantity; + } + _afterTokenTransfers(address(0), to, startTokenId, quantity); + } + + /** + * @dev Safely mints `quantity` tokens and transfers them to `to`. + * + * Requirements: + * + * - If `to` refers to a smart contract, it must implement + * {IERC721Receiver-onERC721Received}, which is called for each safe transfer. + * - `quantity` must be greater than 0. + * + * See {_mint}. + * + * Emits a {Transfer} event for each mint. + */ + function _safeMint( + address to, + uint256 quantity, + bytes memory _data + ) internal virtual { + _mint(to, quantity); + + unchecked { + if (to.code.length != 0) { + uint256 end = ERC721AStorage.layout()._currentIndex; + uint256 index = end - quantity; + do { + if (!_checkContractOnERC721Received(address(0), to, index++, _data)) { + _revert(TransferToNonERC721ReceiverImplementer.selector); + } + } while (index < end); + // Reentrancy protection. + if (ERC721AStorage.layout()._currentIndex != end) _revert(bytes4(0)); + } + } + } + + /** + * @dev Equivalent to `_safeMint(to, quantity, '')`. + */ + function _safeMint(address to, uint256 quantity) internal virtual { + _safeMint(to, quantity, ""); + } + + // ============================================================= + // APPROVAL OPERATIONS + // ============================================================= + + /** + * @dev Equivalent to `_approve(to, tokenId, false)`. + */ + function _approve(address to, uint256 tokenId) internal virtual { + _approve(to, tokenId, false); + } + + /** + * @dev Gives permission to `to` to transfer `tokenId` token to another account. + * The approval is cleared when the token is transferred. + * + * Only a single account can be approved at a time, so approving the + * zero address clears previous approvals. + * + * Requirements: + * + * - `tokenId` must exist. + * + * Emits an {Approval} event. + */ + function _approve( + address to, + uint256 tokenId, + bool approvalCheck + ) internal virtual { + address owner = ownerOf(tokenId); + + if (approvalCheck && _msgSenderERC721A() != owner) + if (!isApprovedForAll(owner, _msgSenderERC721A())) { + _revert(ApprovalCallerNotOwnerNorApproved.selector); + } + + ERC721AStorage.layout()._tokenApprovals[tokenId].value = to; + emit Approval(owner, to, tokenId); + } + + // ============================================================= + // BURN OPERATIONS + // ============================================================= + + /** + * @dev Equivalent to `_burn(tokenId, false)`. + */ + function _burn(uint256 tokenId) internal virtual { + _burn(tokenId, false); + } + + /** + * @dev Destroys `tokenId`. + * The approval is cleared when the token is burned. + * + * Requirements: + * + * - `tokenId` must exist. + * + * Emits a {Transfer} event. + */ + function _burn(uint256 tokenId, bool approvalCheck) internal virtual { + uint256 prevOwnershipPacked = _packedOwnershipOf(tokenId); + + address from = address(uint160(prevOwnershipPacked)); + + (uint256 approvedAddressSlot, address approvedAddress) = _getApprovedSlotAndAddress(tokenId); + + if (approvalCheck) { + // The nested ifs save around 20+ gas over a compound boolean condition. + if (!_isSenderApprovedOrOwner(approvedAddress, from, _msgSenderERC721A())) + if (!isApprovedForAll(from, _msgSenderERC721A())) _revert(TransferCallerNotOwnerNorApproved.selector); + } + + _beforeTokenTransfers(from, address(0), tokenId, 1); + + // Clear approvals from the previous owner. + assembly { + if approvedAddress { + // This is equivalent to `delete _tokenApprovals[tokenId]`. + sstore(approvedAddressSlot, 0) + } + } + + // Underflow of the sender's balance is impossible because we check for + // ownership above and the recipient's balance can't realistically overflow. + // Counter overflow is incredibly unrealistic as `tokenId` would have to be 2**256. + unchecked { + // Updates: + // - `balance -= 1`. + // - `numberBurned += 1`. + // + // We can directly decrement the balance, and increment the number burned. + // This is equivalent to `packed -= 1; packed += 1 << _BITPOS_NUMBER_BURNED;`. + ERC721AStorage.layout()._packedAddressData[from] += (1 << _BITPOS_NUMBER_BURNED) - 1; + + // Updates: + // - `address` to the last owner. + // - `startTimestamp` to the timestamp of burning. + // - `burned` to `true`. + // - `nextInitialized` to `true`. + ERC721AStorage.layout()._packedOwnerships[tokenId] = _packOwnershipData( + from, + (_BITMASK_BURNED | _BITMASK_NEXT_INITIALIZED) | _nextExtraData(from, address(0), prevOwnershipPacked) + ); + + // If the next slot may not have been initialized (i.e. `nextInitialized == false`) . + if (prevOwnershipPacked & _BITMASK_NEXT_INITIALIZED == 0) { + uint256 nextTokenId = tokenId + 1; + // If the next slot's address is zero and not burned (i.e. packed value is zero). + if (ERC721AStorage.layout()._packedOwnerships[nextTokenId] == 0) { + // If the next slot is within bounds. + if (nextTokenId != ERC721AStorage.layout()._currentIndex) { + // Initialize the next slot to maintain correctness for `ownerOf(tokenId + 1)`. + ERC721AStorage.layout()._packedOwnerships[nextTokenId] = prevOwnershipPacked; + } + } + } + } + + emit Transfer(from, address(0), tokenId); + _afterTokenTransfers(from, address(0), tokenId, 1); + + // Overflow not possible, as _burnCounter cannot be exceed _currentIndex times. + unchecked { + ERC721AStorage.layout()._burnCounter++; + } + } + + // ============================================================= + // EXTRA DATA OPERATIONS + // ============================================================= + + /** + * @dev Directly sets the extra data for the ownership data `index`. + */ + function _setExtraDataAt(uint256 index, uint24 extraData) internal virtual { + uint256 packed = ERC721AStorage.layout()._packedOwnerships[index]; + if (packed == 0) _revert(OwnershipNotInitializedForExtraData.selector); + uint256 extraDataCasted; + // Cast `extraData` with assembly to avoid redundant masking. + assembly { + extraDataCasted := extraData + } + packed = (packed & _BITMASK_EXTRA_DATA_COMPLEMENT) | (extraDataCasted << _BITPOS_EXTRA_DATA); + ERC721AStorage.layout()._packedOwnerships[index] = packed; + } + + /** + * @dev Called during each token transfer to set the 24bit `extraData` field. + * Intended to be overridden by the cosumer contract. + * + * `previousExtraData` - the value of `extraData` before transfer. + * + * Calling conditions: + * + * - When `from` and `to` are both non-zero, `from`'s `tokenId` will be + * transferred to `to`. + * - When `from` is zero, `tokenId` will be minted for `to`. + * - When `to` is zero, `tokenId` will be burned by `from`. + * - `from` and `to` are never both zero. + */ + function _extraData( + address from, + address to, + uint24 previousExtraData + ) internal view virtual returns (uint24) {} + + /** + * @dev Returns the next extra data for the packed ownership data. + * The returned result is shifted into position. + */ + function _nextExtraData( + address from, + address to, + uint256 prevOwnershipPacked + ) private view returns (uint256) { + uint24 extraData = uint24(prevOwnershipPacked >> _BITPOS_EXTRA_DATA); + return uint256(_extraData(from, to, extraData)) << _BITPOS_EXTRA_DATA; + } + + // ============================================================= + // OTHER OPERATIONS + // ============================================================= + + /** + * @dev Returns the message sender (defaults to `msg.sender`). + * + * If you are writing GSN compatible contracts, you need to override this function. + */ + function _msgSenderERC721A() internal view virtual returns (address) { + return msg.sender; + } + + /** + * @dev Converts a uint256 to its ASCII string decimal representation. + */ + function _toString(uint256 value) internal pure virtual returns (string memory str) { + assembly { + // The maximum value of a uint256 contains 78 digits (1 byte per digit), but + // we allocate 0xa0 bytes to keep the free memory pointer 32-byte word aligned. + // We will need 1 word for the trailing zeros padding, 1 word for the length, + // and 3 words for a maximum of 78 digits. Total: 5 * 0x20 = 0xa0. + let m := add(mload(0x40), 0xa0) + // Update the free memory pointer to allocate. + mstore(0x40, m) + // Assign the `str` to the end. + str := sub(m, 0x20) + // Zeroize the slot after the string. + mstore(str, 0) + + // Cache the end of the memory to calculate the length later. + let end := str + + // We write the string from rightmost digit to leftmost digit. + // The following is essentially a do-while loop that also handles the zero case. + // prettier-ignore + for { let temp := value } 1 {} { + str := sub(str, 1) + // Write the character to the pointer. + // The ASCII index of the '0' character is 48. + mstore8(str, add(48, mod(temp, 10))) + // Keep dividing `temp` until zero. + temp := div(temp, 10) + // prettier-ignore + if iszero(temp) { break } + } + + let length := sub(end, str) + // Move the pointer 32 bytes leftwards to make room for the length. + str := sub(str, 0x20) + // Store the length. + mstore(str, length) + } + } + + /** + * @dev For more efficient reverts. + */ + function _revert(bytes4 errorSelector) internal pure { + assembly { + mstore(0x00, errorSelector) + revert(0x00, 0x04) + } + } +} diff --git a/contracts/eip/queryable/ERC721A__Initializable.sol b/contracts/eip/queryable/ERC721A__Initializable.sol new file mode 100644 index 000000000..feba56ea7 --- /dev/null +++ b/contracts/eip/queryable/ERC721A__Initializable.sol @@ -0,0 +1,75 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +/** + * @dev This is a base contract to aid in writing upgradeable diamond facet contracts, or any kind of contract that will be deployed + * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an + * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer + * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect. + * + * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as + * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}. + * + * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure + * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity. + */ + +import { ERC721A__InitializableStorage } from "./ERC721A__InitializableStorage.sol"; + +abstract contract ERC721A__Initializable { + using ERC721A__InitializableStorage for ERC721A__InitializableStorage.Layout; + + /** + * @dev Modifier to protect an initializer function from being invoked twice. + */ + modifier initializerERC721A() { + // If the contract is initializing we ignore whether _initialized is set in order to support multiple + // inheritance patterns, but we only do this in the context of a constructor, because in other contexts the + // contract may have been reentered. + require( + ERC721A__InitializableStorage.layout()._initializing + ? _isConstructor() + : !ERC721A__InitializableStorage.layout()._initialized, + "ERC721A__Initializable: contract is already initialized" + ); + + bool isTopLevelCall = !ERC721A__InitializableStorage.layout()._initializing; + if (isTopLevelCall) { + ERC721A__InitializableStorage.layout()._initializing = true; + ERC721A__InitializableStorage.layout()._initialized = true; + } + + _; + + if (isTopLevelCall) { + ERC721A__InitializableStorage.layout()._initializing = false; + } + } + + /** + * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the + * {initializer} modifier, directly or indirectly. + */ + modifier onlyInitializingERC721A() { + require( + ERC721A__InitializableStorage.layout()._initializing, + "ERC721A__Initializable: contract is not initializing" + ); + _; + } + + /// @dev Returns true if and only if the function is running in the constructor + function _isConstructor() private view returns (bool) { + // extcodesize checks the size of the code stored in an address, and + // address returns the current address. Since the code is still not + // deployed when running a constructor, any checks on its code size will + // yield zero, making it an effective way to detect if a contract is + // under construction or not. + address self = address(this); + uint256 cs; + assembly { + cs := extcodesize(self) + } + return cs == 0; + } +} diff --git a/contracts/eip/queryable/ERC721A__InitializableStorage.sol b/contracts/eip/queryable/ERC721A__InitializableStorage.sol new file mode 100644 index 000000000..6b649b7b1 --- /dev/null +++ b/contracts/eip/queryable/ERC721A__InitializableStorage.sol @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +/** + * @dev This is a base storage for the initialization function for upgradeable diamond facet contracts + **/ + +library ERC721A__InitializableStorage { + struct Layout { + /* + * Indicates that the contract has been initialized. + */ + bool _initialized; + /* + * Indicates that the contract is in the process of being initialized. + */ + bool _initializing; + } + + bytes32 internal constant STORAGE_SLOT = keccak256("ERC721A.contracts.storage.initializable.facet"); + + function layout() internal pure returns (Layout storage l) { + bytes32 slot = STORAGE_SLOT; + assembly { + l.slot := slot + } + } +} diff --git a/contracts/eip/queryable/IERC721AQueryableUpgradeable.sol b/contracts/eip/queryable/IERC721AQueryableUpgradeable.sol new file mode 100644 index 000000000..c606624bb --- /dev/null +++ b/contracts/eip/queryable/IERC721AQueryableUpgradeable.sol @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: MIT +// ERC721A Contracts v4.2.3 +// Creator: Chiru Labs + +pragma solidity ^0.8.4; + +import "./IERC721AUpgradeable.sol"; + +/** + * @dev Interface of ERC721AQueryable. + */ +interface IERC721AQueryableUpgradeable is IERC721AUpgradeable { + /** + * Invalid query range (`start` >= `stop`). + */ + error InvalidQueryRange(); + + /** + * @dev Returns the `TokenOwnership` struct at `tokenId` without reverting. + * + * If the `tokenId` is out of bounds: + * + * - `addr = address(0)` + * - `startTimestamp = 0` + * - `burned = false` + * - `extraData = 0` + * + * If the `tokenId` is burned: + * + * - `addr =
` + * - `startTimestamp = ` + * - `burned = true` + * - `extraData = ` + * + * Otherwise: + * + * - `addr =
` + * - `startTimestamp = ` + * - `burned = false` + * - `extraData = ` + */ + function explicitOwnershipOf(uint256 tokenId) external view returns (TokenOwnership memory); + + /** + * @dev Returns an array of token IDs owned by `owner`, + * in the range [`start`, `stop`) + * (i.e. `start <= tokenId < stop`). + * + * This function allows for tokens to be queried if the collection + * grows too big for a single call of {ERC721AQueryable-tokensOfOwner}. + * + * Requirements: + * + * - `start < stop` + */ + function tokensOfOwnerIn( + address owner, + uint256 start, + uint256 stop + ) external view returns (uint256[] memory); + + /** + * @dev Returns an array of token IDs owned by `owner`. + * + * This function scans the ownership mapping and is O(`totalSupply`) in complexity. + * It is meant to be called off-chain. + * + * See {ERC721AQueryable-tokensOfOwnerIn} for splitting the scan into + * multiple smaller scans if the collection is large enough to cause + * an out-of-gas error (10K collections should be fine). + */ + function tokensOfOwner(address owner) external view returns (uint256[] memory); +} diff --git a/contracts/eip/queryable/IERC721AUpgradeable.sol b/contracts/eip/queryable/IERC721AUpgradeable.sol new file mode 100644 index 000000000..15058be66 --- /dev/null +++ b/contracts/eip/queryable/IERC721AUpgradeable.sol @@ -0,0 +1,282 @@ +// SPDX-License-Identifier: MIT +// ERC721A Contracts v4.2.3 +// Creator: Chiru Labs + +pragma solidity ^0.8.4; + +/** + * @dev Interface of ERC721A. + */ +interface IERC721AUpgradeable { + /** + * The caller must own the token or be an approved operator. + */ + error ApprovalCallerNotOwnerNorApproved(); + + /** + * The token does not exist. + */ + error ApprovalQueryForNonexistentToken(); + + /** + * Cannot query the balance for the zero address. + */ + error BalanceQueryForZeroAddress(); + + /** + * Cannot mint to the zero address. + */ + error MintToZeroAddress(); + + /** + * The quantity of tokens minted must be more than zero. + */ + error MintZeroQuantity(); + + /** + * The token does not exist. + */ + error OwnerQueryForNonexistentToken(); + + /** + * The caller must own the token or be an approved operator. + */ + error TransferCallerNotOwnerNorApproved(); + + /** + * The token must be owned by `from`. + */ + error TransferFromIncorrectOwner(); + + /** + * Cannot safely transfer to a contract that does not implement the + * ERC721Receiver interface. + */ + error TransferToNonERC721ReceiverImplementer(); + + /** + * Cannot transfer to the zero address. + */ + error TransferToZeroAddress(); + + /** + * The token does not exist. + */ + error URIQueryForNonexistentToken(); + + /** + * The `quantity` minted with ERC2309 exceeds the safety limit. + */ + error MintERC2309QuantityExceedsLimit(); + + /** + * The `extraData` cannot be set on an unintialized ownership slot. + */ + error OwnershipNotInitializedForExtraData(); + + // ============================================================= + // STRUCTS + // ============================================================= + + struct TokenOwnership { + // The address of the owner. + address addr; + // Stores the start time of ownership with minimal overhead for tokenomics. + uint64 startTimestamp; + // Whether the token has been burned. + bool burned; + // Arbitrary data similar to `startTimestamp` that can be set via {_extraData}. + uint24 extraData; + } + + // ============================================================= + // TOKEN COUNTERS + // ============================================================= + + /** + * @dev Returns the total number of tokens in existence. + * Burned tokens will reduce the count. + * To get the total number of tokens minted, please see {_totalMinted}. + */ + function totalSupply() external view returns (uint256); + + // ============================================================= + // IERC165 + // ============================================================= + + /** + * @dev Returns true if this contract implements the interface defined by + * `interfaceId`. See the corresponding + * [EIP section](https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified) + * to learn more about how these ids are created. + * + * This function call must use less than 30000 gas. + */ + function supportsInterface(bytes4 interfaceId) external view returns (bool); + + // ============================================================= + // IERC721 + // ============================================================= + + /** + * @dev Emitted when `tokenId` token is transferred from `from` to `to`. + */ + event Transfer(address indexed from, address indexed to, uint256 indexed tokenId); + + /** + * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token. + */ + event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId); + + /** + * @dev Emitted when `owner` enables or disables + * (`approved`) `operator` to manage all of its assets. + */ + event ApprovalForAll(address indexed owner, address indexed operator, bool approved); + + /** + * @dev Returns the number of tokens in `owner`'s account. + */ + function balanceOf(address owner) external view returns (uint256 balance); + + /** + * @dev Returns the owner of the `tokenId` token. + * + * Requirements: + * + * - `tokenId` must exist. + */ + function ownerOf(uint256 tokenId) external view returns (address owner); + + /** + * @dev Safely transfers `tokenId` token from `from` to `to`, + * checking first that contract recipients are aware of the ERC721 protocol + * to prevent tokens from being forever locked. + * + * Requirements: + * + * - `from` cannot be the zero address. + * - `to` cannot be the zero address. + * - `tokenId` token must exist and be owned by `from`. + * - If the caller is not `from`, it must be have been allowed to move + * this token by either {approve} or {setApprovalForAll}. + * - If `to` refers to a smart contract, it must implement + * {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. + * + * Emits a {Transfer} event. + */ + function safeTransferFrom( + address from, + address to, + uint256 tokenId, + bytes calldata data + ) external payable; + + /** + * @dev Equivalent to `safeTransferFrom(from, to, tokenId, '')`. + */ + function safeTransferFrom( + address from, + address to, + uint256 tokenId + ) external payable; + + /** + * @dev Transfers `tokenId` from `from` to `to`. + * + * WARNING: Usage of this method is discouraged, use {safeTransferFrom} + * whenever possible. + * + * Requirements: + * + * - `from` cannot be the zero address. + * - `to` cannot be the zero address. + * - `tokenId` token must be owned by `from`. + * - If the caller is not `from`, it must be approved to move this token + * by either {approve} or {setApprovalForAll}. + * + * Emits a {Transfer} event. + */ + function transferFrom( + address from, + address to, + uint256 tokenId + ) external payable; + + /** + * @dev Gives permission to `to` to transfer `tokenId` token to another account. + * The approval is cleared when the token is transferred. + * + * Only a single account can be approved at a time, so approving the + * zero address clears previous approvals. + * + * Requirements: + * + * - The caller must own the token or be an approved operator. + * - `tokenId` must exist. + * + * Emits an {Approval} event. + */ + function approve(address to, uint256 tokenId) external payable; + + /** + * @dev Approve or remove `operator` as an operator for the caller. + * Operators can call {transferFrom} or {safeTransferFrom} + * for any token owned by the caller. + * + * Requirements: + * + * - The `operator` cannot be the caller. + * + * Emits an {ApprovalForAll} event. + */ + function setApprovalForAll(address operator, bool _approved) external; + + /** + * @dev Returns the account approved for `tokenId` token. + * + * Requirements: + * + * - `tokenId` must exist. + */ + function getApproved(uint256 tokenId) external view returns (address operator); + + /** + * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`. + * + * See {setApprovalForAll}. + */ + function isApprovedForAll(address owner, address operator) external view returns (bool); + + // ============================================================= + // IERC721Metadata + // ============================================================= + + /** + * @dev Returns the token collection name. + */ + function name() external view returns (string memory); + + /** + * @dev Returns the token collection symbol. + */ + function symbol() external view returns (string memory); + + /** + * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token. + */ + function tokenURI(uint256 tokenId) external view returns (string memory); + + // ============================================================= + // IERC2309 + // ============================================================= + + /** + * @dev Emitted when tokens in `fromTokenId` to `toTokenId` + * (inclusive) is transferred from `from` to `to`, as defined in the + * [ERC2309](https://eips.ethereum.org/EIPS/eip-2309) standard. + * + * See {_mintERC2309} for more details. + */ + event ConsecutiveTransfer(uint256 indexed fromTokenId, uint256 toTokenId, address indexed from, address indexed to); +} diff --git a/contracts/extension/SharedMetadata.sol b/contracts/extension/SharedMetadata.sol index ce29526e3..0498ed187 100644 --- a/contracts/extension/SharedMetadata.sol +++ b/contracts/extension/SharedMetadata.sol @@ -5,8 +5,9 @@ pragma solidity ^0.8.10; import "../lib/NFTMetadataRendererLib.sol"; import "./interface/ISharedMetadata.sol"; +import "../eip/interface/IERC4906.sol"; -abstract contract SharedMetadata is ISharedMetadata { +abstract contract SharedMetadata is ISharedMetadata, IERC4906 { /// @notice Token metadata information SharedMetadataInfo public sharedMetadata; @@ -30,6 +31,8 @@ abstract contract SharedMetadata is ISharedMetadata { animationURI: _metadata.animationURI }); + emit BatchMetadataUpdate(0, type(uint256).max); + emit SharedMetadataUpdated({ name: _metadata.name, description: _metadata.description, diff --git a/contracts/extension/Staking1155.sol b/contracts/extension/Staking1155.sol index ef0c169a7..22bd39a2d 100644 --- a/contracts/extension/Staking1155.sol +++ b/contracts/extension/Staking1155.sol @@ -21,7 +21,7 @@ abstract contract Staking1155 is ReentrancyGuard, IStaking1155 { uint8 internal isStaking = 1; ///@dev Next staking condition Id. Tracks number of conditon updates so far. - uint256 private nextDefaultConditionId; + uint64 private nextDefaultConditionId; ///@dev List of token-ids ever staked. uint256[] public indexedTokens; @@ -30,16 +30,16 @@ abstract contract Staking1155 is ReentrancyGuard, IStaking1155 { mapping(uint256 => bool) public isIndexed; ///@dev Mapping from default condition-id to default condition. - mapping(uint256 => StakingCondition) private defaultCondition; + mapping(uint64 => StakingCondition) private defaultCondition; ///@dev Mapping from token-id to next staking condition Id for the token. Tracks number of conditon updates so far. - mapping(uint256 => uint256) private nextConditionId; + mapping(uint256 => uint64) private nextConditionId; ///@dev Mapping from token-id and staker address to Staker struct. See {struct IStaking1155.Staker}. mapping(uint256 => mapping(address => Staker)) public stakers; ///@dev Mapping from token-id and condition Id to staking condition. See {struct IStaking1155.StakingCondition} - mapping(uint256 => mapping(uint256 => StakingCondition)) private stakingConditions; + mapping(uint256 => mapping(uint64 => StakingCondition)) private stakingConditions; /// @dev Mapping from token-id to list of accounts that have staked that token-id. mapping(uint256 => address[]) public stakersArray; @@ -61,7 +61,7 @@ abstract contract Staking1155 is ReentrancyGuard, IStaking1155 { * @param _tokenId ERC1155 token-id to stake. * @param _amount Amount to stake. */ - function stake(uint256 _tokenId, uint256 _amount) external nonReentrant { + function stake(uint256 _tokenId, uint64 _amount) external nonReentrant { _stake(_tokenId, _amount); } @@ -73,7 +73,7 @@ abstract contract Staking1155 is ReentrancyGuard, IStaking1155 { * @param _tokenId ERC1155 token-id to withdraw. * @param _amount Amount to withdraw. */ - function withdraw(uint256 _tokenId, uint256 _amount) external nonReentrant { + function withdraw(uint256 _tokenId, uint64 _amount) external nonReentrant { _withdraw(_tokenId, _amount); } @@ -99,12 +99,12 @@ abstract contract Staking1155 is ReentrancyGuard, IStaking1155 { * @param _tokenId ERC1155 token Id. * @param _timeUnit New time unit. */ - function setTimeUnit(uint256 _tokenId, uint256 _timeUnit) external virtual { + function setTimeUnit(uint256 _tokenId, uint80 _timeUnit) external virtual { if (!_canSetStakeConditions()) { revert("Not authorized"); } - uint256 _nextConditionId = nextConditionId[_tokenId]; + uint64 _nextConditionId = nextConditionId[_tokenId]; StakingCondition memory condition = _nextConditionId == 0 ? defaultCondition[nextDefaultConditionId - 1] : stakingConditions[_tokenId][_nextConditionId - 1]; @@ -130,7 +130,7 @@ abstract contract Staking1155 is ReentrancyGuard, IStaking1155 { revert("Not authorized"); } - uint256 _nextConditionId = nextConditionId[_tokenId]; + uint64 _nextConditionId = nextConditionId[_tokenId]; StakingCondition memory condition = _nextConditionId == 0 ? defaultCondition[nextDefaultConditionId - 1] : stakingConditions[_tokenId][_nextConditionId - 1]; @@ -149,7 +149,7 @@ abstract contract Staking1155 is ReentrancyGuard, IStaking1155 { * * @param _defaultTimeUnit New time unit. */ - function setDefaultTimeUnit(uint256 _defaultTimeUnit) external virtual { + function setDefaultTimeUnit(uint80 _defaultTimeUnit) external virtual { if (!_canSetStakeConditions()) { revert("Not authorized"); } @@ -242,13 +242,13 @@ abstract contract Staking1155 is ReentrancyGuard, IStaking1155 { } function getTimeUnit(uint256 _tokenId) public view returns (uint256 _timeUnit) { - uint256 _nextConditionId = nextConditionId[_tokenId]; + uint64 _nextConditionId = nextConditionId[_tokenId]; require(_nextConditionId != 0, "Time unit not set. Check default time unit."); _timeUnit = stakingConditions[_tokenId][_nextConditionId - 1].timeUnit; } function getRewardsPerUnitTime(uint256 _tokenId) public view returns (uint256 _rewardsPerUnitTime) { - uint256 _nextConditionId = nextConditionId[_tokenId]; + uint64 _nextConditionId = nextConditionId[_tokenId]; require(_nextConditionId != 0, "Rewards not set. Check default rewards."); _rewardsPerUnitTime = stakingConditions[_tokenId][_nextConditionId - 1].rewardsPerUnitTime; } @@ -266,29 +266,25 @@ abstract contract Staking1155 is ReentrancyGuard, IStaking1155 { //////////////////////////////////////////////////////////////*/ /// @dev Staking logic. Override to add custom logic. - function _stake(uint256 _tokenId, uint256 _amount) internal virtual { + function _stake(uint256 _tokenId, uint64 _amount) internal virtual { require(_amount != 0, "Staking 0 tokens"); - address _stakingToken = stakingToken; if (stakers[_tokenId][_stakeMsgSender()].amountStaked > 0) { _updateUnclaimedRewardsForStaker(_tokenId, _stakeMsgSender()); } else { stakersArray[_tokenId].push(_stakeMsgSender()); - stakers[_tokenId][_stakeMsgSender()].timeOfLastUpdate = block.timestamp; + stakers[_tokenId][_stakeMsgSender()].timeOfLastUpdate = uint80(block.timestamp); - uint256 _conditionId = nextConditionId[_tokenId]; - stakers[_tokenId][_stakeMsgSender()].conditionIdOflastUpdate = _conditionId == 0 - ? nextDefaultConditionId - 1 - : _conditionId - 1; + uint64 _conditionId = nextConditionId[_tokenId]; + unchecked { + stakers[_tokenId][_stakeMsgSender()].conditionIdOflastUpdate = _conditionId == 0 + ? nextDefaultConditionId - 1 + : _conditionId - 1; + } } - require( - IERC1155(_stakingToken).balanceOf(_stakeMsgSender(), _tokenId) >= _amount && - IERC1155(_stakingToken).isApprovedForAll(_stakeMsgSender(), address(this)), - "Not balance or approved" - ); isStaking = 2; - IERC1155(_stakingToken).safeTransferFrom(_stakeMsgSender(), address(this), _tokenId, _amount, ""); + IERC1155(stakingToken).safeTransferFrom(_stakeMsgSender(), address(this), _tokenId, _amount, ""); isStaking = 1; // stakerAddress[_tokenIds[i]] = _stakeMsgSender(); stakers[_tokenId][_stakeMsgSender()].amountStaked += _amount; @@ -302,7 +298,7 @@ abstract contract Staking1155 is ReentrancyGuard, IStaking1155 { } /// @dev Withdraw logic. Override to add custom logic. - function _withdraw(uint256 _tokenId, uint256 _amount) internal virtual { + function _withdraw(uint256 _tokenId, uint64 _amount) internal virtual { uint256 _amountStaked = stakers[_tokenId][_stakeMsgSender()].amountStaked; require(_amount != 0, "Withdrawing 0 tokens"); require(_amountStaked >= _amount, "Withdrawing more than staked"); @@ -333,13 +329,16 @@ abstract contract Staking1155 is ReentrancyGuard, IStaking1155 { require(rewards != 0, "No rewards"); - stakers[_tokenId][_stakeMsgSender()].timeOfLastUpdate = block.timestamp; + stakers[_tokenId][_stakeMsgSender()].timeOfLastUpdate = uint80(block.timestamp); stakers[_tokenId][_stakeMsgSender()].unclaimedRewards = 0; - uint256 _conditionId = nextConditionId[_tokenId]; - stakers[_tokenId][_stakeMsgSender()].conditionIdOflastUpdate = _conditionId == 0 - ? nextDefaultConditionId - 1 - : _conditionId - 1; + uint64 _conditionId = nextConditionId[_tokenId]; + + unchecked { + stakers[_tokenId][_stakeMsgSender()].conditionIdOflastUpdate = _conditionId == 0 + ? nextDefaultConditionId - 1 + : _conditionId - 1; + } _mintRewards(_stakeMsgSender(), rewards); @@ -359,25 +358,27 @@ abstract contract Staking1155 is ReentrancyGuard, IStaking1155 { function _updateUnclaimedRewardsForStaker(uint256 _tokenId, address _staker) internal virtual { uint256 rewards = _calculateRewards(_tokenId, _staker); stakers[_tokenId][_staker].unclaimedRewards += rewards; - stakers[_tokenId][_staker].timeOfLastUpdate = block.timestamp; + stakers[_tokenId][_staker].timeOfLastUpdate = uint80(block.timestamp); - uint256 _conditionId = nextConditionId[_tokenId]; - stakers[_tokenId][_staker].conditionIdOflastUpdate = _conditionId == 0 - ? nextDefaultConditionId - 1 - : _conditionId - 1; + uint64 _conditionId = nextConditionId[_tokenId]; + unchecked { + stakers[_tokenId][_staker].conditionIdOflastUpdate = _conditionId == 0 + ? nextDefaultConditionId - 1 + : _conditionId - 1; + } } /// @dev Set staking conditions, for a token-Id. function _setStakingCondition( uint256 _tokenId, - uint256 _timeUnit, + uint80 _timeUnit, uint256 _rewardsPerUnitTime ) internal virtual { require(_timeUnit != 0, "time-unit can't be 0"); - uint256 conditionId = nextConditionId[_tokenId]; + uint64 conditionId = nextConditionId[_tokenId]; if (conditionId == 0) { - uint256 _nextDefaultConditionId = nextDefaultConditionId; + uint64 _nextDefaultConditionId = nextDefaultConditionId; for (; conditionId < _nextDefaultConditionId; conditionId += 1) { StakingCondition memory _defaultCondition = defaultCondition[conditionId]; @@ -390,12 +391,12 @@ abstract contract Staking1155 is ReentrancyGuard, IStaking1155 { } } - stakingConditions[_tokenId][conditionId - 1].endTimestamp = block.timestamp; + stakingConditions[_tokenId][conditionId - 1].endTimestamp = uint80(block.timestamp); stakingConditions[_tokenId][conditionId] = StakingCondition({ timeUnit: _timeUnit, rewardsPerUnitTime: _rewardsPerUnitTime, - startTimestamp: block.timestamp, + startTimestamp: uint80(block.timestamp), endTimestamp: 0 }); @@ -403,33 +404,33 @@ abstract contract Staking1155 is ReentrancyGuard, IStaking1155 { } /// @dev Set default staking conditions. - function _setDefaultStakingCondition(uint256 _timeUnit, uint256 _rewardsPerUnitTime) internal virtual { + function _setDefaultStakingCondition(uint80 _timeUnit, uint256 _rewardsPerUnitTime) internal virtual { require(_timeUnit != 0, "time-unit can't be 0"); - uint256 conditionId = nextDefaultConditionId; + uint64 conditionId = nextDefaultConditionId; nextDefaultConditionId += 1; defaultCondition[conditionId] = StakingCondition({ timeUnit: _timeUnit, rewardsPerUnitTime: _rewardsPerUnitTime, - startTimestamp: block.timestamp, + startTimestamp: uint80(block.timestamp), endTimestamp: 0 }); if (conditionId > 0) { - defaultCondition[conditionId - 1].endTimestamp = block.timestamp; + defaultCondition[conditionId - 1].endTimestamp = uint80(block.timestamp); } } /// @dev Reward calculation logic. Override to implement custom logic. function _calculateRewards(uint256 _tokenId, address _staker) internal view virtual returns (uint256 _rewards) { Staker memory staker = stakers[_tokenId][_staker]; - uint256 _stakerConditionId = staker.conditionIdOflastUpdate; - uint256 _nextConditionId = nextConditionId[_tokenId]; + uint64 _stakerConditionId = staker.conditionIdOflastUpdate; + uint64 _nextConditionId = nextConditionId[_tokenId]; if (_nextConditionId == 0) { _nextConditionId = nextDefaultConditionId; - for (uint256 i = _stakerConditionId; i < _nextConditionId; i += 1) { + for (uint64 i = _stakerConditionId; i < _nextConditionId; i += 1) { StakingCondition memory condition = defaultCondition[i]; uint256 startTime = i != _stakerConditionId ? condition.startTimestamp : staker.timeOfLastUpdate; @@ -447,7 +448,7 @@ abstract contract Staking1155 is ReentrancyGuard, IStaking1155 { _rewards = noOverflowProduct && noOverflowSum ? rewardsSum : _rewards; } } else { - for (uint256 i = _stakerConditionId; i < _nextConditionId; i += 1) { + for (uint64 i = _stakerConditionId; i < _nextConditionId; i += 1) { StakingCondition memory condition = stakingConditions[_tokenId][i]; uint256 startTime = i != _stakerConditionId ? condition.startTimestamp : staker.timeOfLastUpdate; diff --git a/contracts/extension/Staking1155Upgradeable.sol b/contracts/extension/Staking1155Upgradeable.sol index cb5e47a40..fcfa593b3 100644 --- a/contracts/extension/Staking1155Upgradeable.sol +++ b/contracts/extension/Staking1155Upgradeable.sol @@ -21,7 +21,7 @@ abstract contract Staking1155Upgradeable is ReentrancyGuardUpgradeable, IStaking uint8 internal isStaking = 1; ///@dev Next staking condition Id. Tracks number of conditon updates so far. - uint256 private nextDefaultConditionId; + uint64 private nextDefaultConditionId; ///@dev List of token-ids ever staked. uint256[] public indexedTokens; @@ -30,16 +30,16 @@ abstract contract Staking1155Upgradeable is ReentrancyGuardUpgradeable, IStaking mapping(uint256 => bool) public isIndexed; ///@dev Mapping from default condition-id to default condition. - mapping(uint256 => StakingCondition) private defaultCondition; + mapping(uint64 => StakingCondition) private defaultCondition; ///@dev Mapping from token-id to next staking condition Id for the token. Tracks number of conditon updates so far. - mapping(uint256 => uint256) private nextConditionId; + mapping(uint256 => uint64) private nextConditionId; ///@dev Mapping from token-id and staker address to Staker struct. See {struct IStaking1155.Staker}. mapping(uint256 => mapping(address => Staker)) public stakers; ///@dev Mapping from token-id and condition Id to staking condition. See {struct IStaking1155.StakingCondition} - mapping(uint256 => mapping(uint256 => StakingCondition)) private stakingConditions; + mapping(uint256 => mapping(uint64 => StakingCondition)) private stakingConditions; /// @dev Mapping from token-id to list of accounts that have staked that token-id. mapping(uint256 => address[]) public stakersArray; @@ -63,7 +63,7 @@ abstract contract Staking1155Upgradeable is ReentrancyGuardUpgradeable, IStaking * @param _tokenId ERC1155 token-id to stake. * @param _amount Amount to stake. */ - function stake(uint256 _tokenId, uint256 _amount) external nonReentrant { + function stake(uint256 _tokenId, uint64 _amount) external nonReentrant { _stake(_tokenId, _amount); } @@ -75,7 +75,7 @@ abstract contract Staking1155Upgradeable is ReentrancyGuardUpgradeable, IStaking * @param _tokenId ERC1155 token-id to withdraw. * @param _amount Amount to withdraw. */ - function withdraw(uint256 _tokenId, uint256 _amount) external nonReentrant { + function withdraw(uint256 _tokenId, uint64 _amount) external nonReentrant { _withdraw(_tokenId, _amount); } @@ -101,12 +101,12 @@ abstract contract Staking1155Upgradeable is ReentrancyGuardUpgradeable, IStaking * @param _tokenId ERC1155 token Id. * @param _timeUnit New time unit. */ - function setTimeUnit(uint256 _tokenId, uint256 _timeUnit) external virtual { + function setTimeUnit(uint256 _tokenId, uint80 _timeUnit) external virtual { if (!_canSetStakeConditions()) { revert("Not authorized"); } - uint256 _nextConditionId = nextConditionId[_tokenId]; + uint64 _nextConditionId = nextConditionId[_tokenId]; StakingCondition memory condition = _nextConditionId == 0 ? defaultCondition[nextDefaultConditionId - 1] : stakingConditions[_tokenId][_nextConditionId - 1]; @@ -132,7 +132,7 @@ abstract contract Staking1155Upgradeable is ReentrancyGuardUpgradeable, IStaking revert("Not authorized"); } - uint256 _nextConditionId = nextConditionId[_tokenId]; + uint64 _nextConditionId = nextConditionId[_tokenId]; StakingCondition memory condition = _nextConditionId == 0 ? defaultCondition[nextDefaultConditionId - 1] : stakingConditions[_tokenId][_nextConditionId - 1]; @@ -151,7 +151,7 @@ abstract contract Staking1155Upgradeable is ReentrancyGuardUpgradeable, IStaking * * @param _defaultTimeUnit New time unit. */ - function setDefaultTimeUnit(uint256 _defaultTimeUnit) external virtual { + function setDefaultTimeUnit(uint80 _defaultTimeUnit) external virtual { if (!_canSetStakeConditions()) { revert("Not authorized"); } @@ -244,13 +244,13 @@ abstract contract Staking1155Upgradeable is ReentrancyGuardUpgradeable, IStaking } function getTimeUnit(uint256 _tokenId) public view returns (uint256 _timeUnit) { - uint256 _nextConditionId = nextConditionId[_tokenId]; + uint64 _nextConditionId = nextConditionId[_tokenId]; require(_nextConditionId != 0, "Time unit not set. Check default time unit."); _timeUnit = stakingConditions[_tokenId][_nextConditionId - 1].timeUnit; } function getRewardsPerUnitTime(uint256 _tokenId) public view returns (uint256 _rewardsPerUnitTime) { - uint256 _nextConditionId = nextConditionId[_tokenId]; + uint64 _nextConditionId = nextConditionId[_tokenId]; require(_nextConditionId != 0, "Rewards not set. Check default rewards."); _rewardsPerUnitTime = stakingConditions[_tokenId][_nextConditionId - 1].rewardsPerUnitTime; } @@ -268,29 +268,23 @@ abstract contract Staking1155Upgradeable is ReentrancyGuardUpgradeable, IStaking //////////////////////////////////////////////////////////////*/ /// @dev Staking logic. Override to add custom logic. - function _stake(uint256 _tokenId, uint256 _amount) internal virtual { + function _stake(uint256 _tokenId, uint64 _amount) internal virtual { require(_amount != 0, "Staking 0 tokens"); - address _stakingToken = stakingToken; if (stakers[_tokenId][_stakeMsgSender()].amountStaked > 0) { _updateUnclaimedRewardsForStaker(_tokenId, _stakeMsgSender()); } else { stakersArray[_tokenId].push(_stakeMsgSender()); - stakers[_tokenId][_stakeMsgSender()].timeOfLastUpdate = block.timestamp; + stakers[_tokenId][_stakeMsgSender()].timeOfLastUpdate = uint80(block.timestamp); - uint256 _conditionId = nextConditionId[_tokenId]; + uint64 _conditionId = nextConditionId[_tokenId]; stakers[_tokenId][_stakeMsgSender()].conditionIdOflastUpdate = _conditionId == 0 ? nextDefaultConditionId - 1 : _conditionId - 1; } - require( - IERC1155(_stakingToken).balanceOf(_stakeMsgSender(), _tokenId) >= _amount && - IERC1155(_stakingToken).isApprovedForAll(_stakeMsgSender(), address(this)), - "Not balance or approved" - ); isStaking = 2; - IERC1155(_stakingToken).safeTransferFrom(_stakeMsgSender(), address(this), _tokenId, _amount, ""); + IERC1155(stakingToken).safeTransferFrom(_stakeMsgSender(), address(this), _tokenId, _amount, ""); isStaking = 1; // stakerAddress[_tokenIds[i]] = _stakeMsgSender(); stakers[_tokenId][_stakeMsgSender()].amountStaked += _amount; @@ -304,7 +298,7 @@ abstract contract Staking1155Upgradeable is ReentrancyGuardUpgradeable, IStaking } /// @dev Withdraw logic. Override to add custom logic. - function _withdraw(uint256 _tokenId, uint256 _amount) internal virtual { + function _withdraw(uint256 _tokenId, uint64 _amount) internal virtual { uint256 _amountStaked = stakers[_tokenId][_stakeMsgSender()].amountStaked; require(_amount != 0, "Withdrawing 0 tokens"); require(_amountStaked >= _amount, "Withdrawing more than staked"); @@ -321,6 +315,7 @@ abstract contract Staking1155Upgradeable is ReentrancyGuardUpgradeable, IStaking } } } + stakers[_tokenId][_stakeMsgSender()].amountStaked -= _amount; IERC1155(stakingToken).safeTransferFrom(address(this), _stakeMsgSender(), _tokenId, _amount, ""); @@ -335,13 +330,15 @@ abstract contract Staking1155Upgradeable is ReentrancyGuardUpgradeable, IStaking require(rewards != 0, "No rewards"); - stakers[_tokenId][_stakeMsgSender()].timeOfLastUpdate = block.timestamp; + stakers[_tokenId][_stakeMsgSender()].timeOfLastUpdate = uint80(block.timestamp); stakers[_tokenId][_stakeMsgSender()].unclaimedRewards = 0; - uint256 _conditionId = nextConditionId[_tokenId]; - stakers[_tokenId][_stakeMsgSender()].conditionIdOflastUpdate = _conditionId == 0 - ? nextDefaultConditionId - 1 - : _conditionId - 1; + uint64 _conditionId = nextConditionId[_tokenId]; + unchecked { + stakers[_tokenId][_stakeMsgSender()].conditionIdOflastUpdate = _conditionId == 0 + ? nextDefaultConditionId - 1 + : _conditionId - 1; + } _mintRewards(_stakeMsgSender(), rewards); @@ -361,22 +358,24 @@ abstract contract Staking1155Upgradeable is ReentrancyGuardUpgradeable, IStaking function _updateUnclaimedRewardsForStaker(uint256 _tokenId, address _staker) internal virtual { uint256 rewards = _calculateRewards(_tokenId, _staker); stakers[_tokenId][_staker].unclaimedRewards += rewards; - stakers[_tokenId][_staker].timeOfLastUpdate = block.timestamp; + stakers[_tokenId][_staker].timeOfLastUpdate = uint80(block.timestamp); - uint256 _conditionId = nextConditionId[_tokenId]; - stakers[_tokenId][_staker].conditionIdOflastUpdate = _conditionId == 0 - ? nextDefaultConditionId - 1 - : _conditionId - 1; + uint64 _conditionId = nextConditionId[_tokenId]; + unchecked { + stakers[_tokenId][_staker].conditionIdOflastUpdate = _conditionId == 0 + ? nextDefaultConditionId - 1 + : _conditionId - 1; + } } /// @dev Set staking conditions, for a token-Id. function _setStakingCondition( uint256 _tokenId, - uint256 _timeUnit, + uint80 _timeUnit, uint256 _rewardsPerUnitTime ) internal virtual { require(_timeUnit != 0, "time-unit can't be 0"); - uint256 conditionId = nextConditionId[_tokenId]; + uint64 conditionId = nextConditionId[_tokenId]; if (conditionId == 0) { uint256 _nextDefaultConditionId = nextDefaultConditionId; @@ -392,12 +391,12 @@ abstract contract Staking1155Upgradeable is ReentrancyGuardUpgradeable, IStaking } } - stakingConditions[_tokenId][conditionId - 1].endTimestamp = block.timestamp; + stakingConditions[_tokenId][conditionId - 1].endTimestamp = uint80(block.timestamp); stakingConditions[_tokenId][conditionId] = StakingCondition({ timeUnit: _timeUnit, rewardsPerUnitTime: _rewardsPerUnitTime, - startTimestamp: block.timestamp, + startTimestamp: uint80(block.timestamp), endTimestamp: 0 }); @@ -405,33 +404,33 @@ abstract contract Staking1155Upgradeable is ReentrancyGuardUpgradeable, IStaking } /// @dev Set default staking conditions. - function _setDefaultStakingCondition(uint256 _timeUnit, uint256 _rewardsPerUnitTime) internal virtual { + function _setDefaultStakingCondition(uint80 _timeUnit, uint256 _rewardsPerUnitTime) internal virtual { require(_timeUnit != 0, "time-unit can't be 0"); - uint256 conditionId = nextDefaultConditionId; + uint64 conditionId = nextDefaultConditionId; nextDefaultConditionId += 1; defaultCondition[conditionId] = StakingCondition({ timeUnit: _timeUnit, rewardsPerUnitTime: _rewardsPerUnitTime, - startTimestamp: block.timestamp, + startTimestamp: uint80(block.timestamp), endTimestamp: 0 }); if (conditionId > 0) { - defaultCondition[conditionId - 1].endTimestamp = block.timestamp; + defaultCondition[conditionId - 1].endTimestamp = uint80(block.timestamp); } } /// @dev Reward calculation logic. Override to implement custom logic. function _calculateRewards(uint256 _tokenId, address _staker) internal view virtual returns (uint256 _rewards) { Staker memory staker = stakers[_tokenId][_staker]; - uint256 _stakerConditionId = staker.conditionIdOflastUpdate; - uint256 _nextConditionId = nextConditionId[_tokenId]; + uint64 _stakerConditionId = staker.conditionIdOflastUpdate; + uint64 _nextConditionId = nextConditionId[_tokenId]; if (_nextConditionId == 0) { _nextConditionId = nextDefaultConditionId; - for (uint256 i = _stakerConditionId; i < _nextConditionId; i += 1) { + for (uint64 i = _stakerConditionId; i < _nextConditionId; i += 1) { StakingCondition memory condition = defaultCondition[i]; uint256 startTime = i != _stakerConditionId ? condition.startTimestamp : staker.timeOfLastUpdate; @@ -449,7 +448,7 @@ abstract contract Staking1155Upgradeable is ReentrancyGuardUpgradeable, IStaking _rewards = noOverflowProduct && noOverflowSum ? rewardsSum : _rewards; } } else { - for (uint256 i = _stakerConditionId; i < _nextConditionId; i += 1) { + for (uint64 i = _stakerConditionId; i < _nextConditionId; i += 1) { StakingCondition memory condition = stakingConditions[_tokenId][i]; uint256 startTime = i != _stakerConditionId ? condition.startTimestamp : staker.timeOfLastUpdate; diff --git a/contracts/extension/Staking20.sol b/contracts/extension/Staking20.sol index 06471adc6..e1dec5bdc 100644 --- a/contracts/extension/Staking20.sol +++ b/contracts/extension/Staking20.sol @@ -19,22 +19,22 @@ abstract contract Staking20 is ReentrancyGuard, IStaking20 { address internal immutable nativeTokenWrapper; ///@dev Address of ERC20 contract -- staked tokens belong to this contract. - address public stakingToken; + address public immutable stakingToken; /// @dev Decimals of staking token. - uint256 public stakingTokenDecimals; + uint16 public immutable stakingTokenDecimals; /// @dev Decimals of reward token. - uint256 public rewardTokenDecimals; + uint16 public immutable rewardTokenDecimals; - /// @dev List of accounts that have staked that token-id. - address[] public stakersArray; + ///@dev Next staking condition Id. Tracks number of conditon updates so far. + uint64 private nextConditionId; /// @dev Total amount of tokens staked in the contract. uint256 public stakingTokenBalance; - ///@dev Next staking condition Id. Tracks number of conditon updates so far. - uint256 private nextConditionId; + /// @dev List of accounts that have staked that token-id. + address[] public stakersArray; ///@dev Mapping staker address to Staker struct. See {struct IStaking20.Staker}. mapping(address => Staker) public stakers; @@ -45,8 +45,8 @@ abstract contract Staking20 is ReentrancyGuard, IStaking20 { constructor( address _nativeTokenWrapper, address _stakingToken, - uint256 _stakingTokenDecimals, - uint256 _rewardTokenDecimals + uint16 _stakingTokenDecimals, + uint16 _rewardTokenDecimals ) ReentrancyGuard() { require(_stakingToken != address(0) && _nativeTokenWrapper != address(0), "address 0"); require(_stakingTokenDecimals != 0 && _rewardTokenDecimals != 0, "decimals 0"); @@ -101,7 +101,7 @@ abstract contract Staking20 is ReentrancyGuard, IStaking20 { * * @param _timeUnit New time unit. */ - function setTimeUnit(uint256 _timeUnit) external virtual { + function setTimeUnit(uint80 _timeUnit) external virtual { if (!_canSetStakeConditions()) { revert("Not authorized"); } @@ -186,7 +186,7 @@ abstract contract Staking20 is ReentrancyGuard, IStaking20 { _updateUnclaimedRewardsForStaker(_stakeMsgSender()); } else { stakersArray.push(_stakeMsgSender()); - stakers[_stakeMsgSender()].timeOfLastUpdate = block.timestamp; + stakers[_stakeMsgSender()].timeOfLastUpdate = uint80(block.timestamp); stakers[_stakeMsgSender()].conditionIdOflastUpdate = nextConditionId - 1; } @@ -244,7 +244,7 @@ abstract contract Staking20 is ReentrancyGuard, IStaking20 { require(rewards != 0, "No rewards"); - stakers[_stakeMsgSender()].timeOfLastUpdate = block.timestamp; + stakers[_stakeMsgSender()].timeOfLastUpdate = uint80(block.timestamp); stakers[_stakeMsgSender()].unclaimedRewards = 0; stakers[_stakeMsgSender()].conditionIdOflastUpdate = nextConditionId - 1; @@ -266,13 +266,13 @@ abstract contract Staking20 is ReentrancyGuard, IStaking20 { function _updateUnclaimedRewardsForStaker(address _staker) internal virtual { uint256 rewards = _calculateRewards(_staker); stakers[_staker].unclaimedRewards += rewards; - stakers[_staker].timeOfLastUpdate = block.timestamp; + stakers[_staker].timeOfLastUpdate = uint80(block.timestamp); stakers[_staker].conditionIdOflastUpdate = nextConditionId - 1; } /// @dev Set staking conditions. function _setStakingCondition( - uint256 _timeUnit, + uint80 _timeUnit, uint256 _numerator, uint256 _denominator ) internal virtual { @@ -285,12 +285,12 @@ abstract contract Staking20 is ReentrancyGuard, IStaking20 { timeUnit: _timeUnit, rewardRatioNumerator: _numerator, rewardRatioDenominator: _denominator, - startTimestamp: block.timestamp, + startTimestamp: uint80(block.timestamp), endTimestamp: 0 }); if (conditionId > 0) { - stakingConditions[conditionId - 1].endTimestamp = block.timestamp; + stakingConditions[conditionId - 1].endTimestamp = uint80(block.timestamp); } } diff --git a/contracts/extension/Staking20Upgradeable.sol b/contracts/extension/Staking20Upgradeable.sol index 18a1ad24c..b01ebbfe6 100644 --- a/contracts/extension/Staking20Upgradeable.sol +++ b/contracts/extension/Staking20Upgradeable.sol @@ -22,19 +22,19 @@ abstract contract Staking20Upgradeable is ReentrancyGuardUpgradeable, IStaking20 address public stakingToken; /// @dev Decimals of staking token. - uint256 public stakingTokenDecimals; + uint16 public stakingTokenDecimals; /// @dev Decimals of reward token. - uint256 public rewardTokenDecimals; + uint16 public rewardTokenDecimals; - /// @dev List of accounts that have staked that token-id. - address[] public stakersArray; + ///@dev Next staking condition Id. Tracks number of conditon updates so far. + uint64 private nextConditionId; /// @dev Total amount of tokens staked in the contract. uint256 public stakingTokenBalance; - ///@dev Next staking condition Id. Tracks number of conditon updates so far. - uint256 private nextConditionId; + /// @dev List of accounts that have staked that token-id. + address[] public stakersArray; ///@dev Mapping staker address to Staker struct. See {struct IStaking20.Staker}. mapping(address => Staker) public stakers; @@ -50,8 +50,8 @@ abstract contract Staking20Upgradeable is ReentrancyGuardUpgradeable, IStaking20 function __Staking20_init( address _stakingToken, - uint256 _stakingTokenDecimals, - uint256 _rewardTokenDecimals + uint16 _stakingTokenDecimals, + uint16 _rewardTokenDecimals ) internal onlyInitializing { __ReentrancyGuard_init(); @@ -107,7 +107,7 @@ abstract contract Staking20Upgradeable is ReentrancyGuardUpgradeable, IStaking20 * * @param _timeUnit New time unit. */ - function setTimeUnit(uint256 _timeUnit) external virtual { + function setTimeUnit(uint80 _timeUnit) external virtual { if (!_canSetStakeConditions()) { revert("Not authorized"); } @@ -163,7 +163,7 @@ abstract contract Staking20Upgradeable is ReentrancyGuardUpgradeable, IStaking20 _rewards = _availableRewards(_staker); } - function getTimeUnit() public view returns (uint256 _timeUnit) { + function getTimeUnit() public view returns (uint80 _timeUnit) { _timeUnit = stakingConditions[nextConditionId - 1].timeUnit; } @@ -192,7 +192,7 @@ abstract contract Staking20Upgradeable is ReentrancyGuardUpgradeable, IStaking20 _updateUnclaimedRewardsForStaker(_stakeMsgSender()); } else { stakersArray.push(_stakeMsgSender()); - stakers[_stakeMsgSender()].timeOfLastUpdate = block.timestamp; + stakers[_stakeMsgSender()].timeOfLastUpdate = uint80(block.timestamp); stakers[_stakeMsgSender()].conditionIdOflastUpdate = nextConditionId - 1; } @@ -230,6 +230,7 @@ abstract contract Staking20Upgradeable is ReentrancyGuardUpgradeable, IStaking20 } } } + stakers[_stakeMsgSender()].amountStaked -= _amount; stakingTokenBalance -= _amount; @@ -250,7 +251,7 @@ abstract contract Staking20Upgradeable is ReentrancyGuardUpgradeable, IStaking20 require(rewards != 0, "No rewards"); - stakers[_stakeMsgSender()].timeOfLastUpdate = block.timestamp; + stakers[_stakeMsgSender()].timeOfLastUpdate = uint80(block.timestamp); stakers[_stakeMsgSender()].unclaimedRewards = 0; stakers[_stakeMsgSender()].conditionIdOflastUpdate = nextConditionId - 1; @@ -272,13 +273,13 @@ abstract contract Staking20Upgradeable is ReentrancyGuardUpgradeable, IStaking20 function _updateUnclaimedRewardsForStaker(address _staker) internal virtual { uint256 rewards = _calculateRewards(_staker); stakers[_staker].unclaimedRewards += rewards; - stakers[_staker].timeOfLastUpdate = block.timestamp; + stakers[_staker].timeOfLastUpdate = uint80(block.timestamp); stakers[_staker].conditionIdOflastUpdate = nextConditionId - 1; } /// @dev Set staking conditions. function _setStakingCondition( - uint256 _timeUnit, + uint80 _timeUnit, uint256 _numerator, uint256 _denominator ) internal virtual { @@ -291,12 +292,12 @@ abstract contract Staking20Upgradeable is ReentrancyGuardUpgradeable, IStaking20 timeUnit: _timeUnit, rewardRatioNumerator: _numerator, rewardRatioDenominator: _denominator, - startTimestamp: block.timestamp, + startTimestamp: uint80(block.timestamp), endTimestamp: 0 }); if (conditionId > 0) { - stakingConditions[conditionId - 1].endTimestamp = block.timestamp; + stakingConditions[conditionId - 1].endTimestamp = uint80(block.timestamp); } } diff --git a/contracts/extension/Staking721.sol b/contracts/extension/Staking721.sol index e90a34aa1..9224d2e82 100644 --- a/contracts/extension/Staking721.sol +++ b/contracts/extension/Staking721.sol @@ -17,18 +17,18 @@ abstract contract Staking721 is ReentrancyGuard, IStaking721 { ///@dev Address of ERC721 NFT contract -- staked tokens belong to this contract. address public stakingToken; + /// @dev Flag to check direct transfers of staking tokens. + uint8 internal isStaking = 1; + + ///@dev Next staking condition Id. Tracks number of conditon updates so far. + uint64 private nextConditionId; + ///@dev List of token-ids ever staked. uint256[] public indexedTokens; /// @dev List of accounts that have staked their NFTs. address[] public stakersArray; - /// @dev Flag to check direct transfers of staking tokens. - uint8 internal isStaking = 1; - - ///@dev Next staking condition Id. Tracks number of conditon updates so far. - uint256 private nextConditionId; - ///@dev Mapping from token-id to whether it is indexed or not. mapping(uint256 => bool) public isIndexed; @@ -175,7 +175,7 @@ abstract contract Staking721 is ReentrancyGuard, IStaking721 { /// @dev Staking logic. Override to add custom logic. function _stake(uint256[] calldata _tokenIds) internal virtual { - uint256 len = _tokenIds.length; + uint64 len = uint64(_tokenIds.length); require(len != 0, "Staking 0 tokens"); address _stakingToken = stakingToken; @@ -184,17 +184,10 @@ abstract contract Staking721 is ReentrancyGuard, IStaking721 { _updateUnclaimedRewardsForStaker(_stakeMsgSender()); } else { stakersArray.push(_stakeMsgSender()); - stakers[_stakeMsgSender()].timeOfLastUpdate = block.timestamp; + stakers[_stakeMsgSender()].timeOfLastUpdate = uint128(block.timestamp); stakers[_stakeMsgSender()].conditionIdOflastUpdate = nextConditionId - 1; } for (uint256 i = 0; i < len; ++i) { - require( - IERC721(_stakingToken).ownerOf(_tokenIds[i]) == _stakeMsgSender() && - (IERC721(_stakingToken).getApproved(_tokenIds[i]) == address(this) || - IERC721(_stakingToken).isApprovedForAll(_stakeMsgSender(), address(this))), - "Not owned or approved" - ); - isStaking = 2; IERC721(_stakingToken).safeTransferFrom(_stakeMsgSender(), address(this), _tokenIds[i]); isStaking = 1; @@ -214,7 +207,7 @@ abstract contract Staking721 is ReentrancyGuard, IStaking721 { /// @dev Withdraw logic. Override to add custom logic. function _withdraw(uint256[] calldata _tokenIds) internal virtual { uint256 _amountStaked = stakers[_stakeMsgSender()].amountStaked; - uint256 len = _tokenIds.length; + uint64 len = uint64(_tokenIds.length); require(len != 0, "Withdrawing 0 tokens"); require(_amountStaked >= len, "Withdrawing more than staked"); @@ -249,7 +242,7 @@ abstract contract Staking721 is ReentrancyGuard, IStaking721 { require(rewards != 0, "No rewards"); - stakers[_stakeMsgSender()].timeOfLastUpdate = block.timestamp; + stakers[_stakeMsgSender()].timeOfLastUpdate = uint128(block.timestamp); stakers[_stakeMsgSender()].unclaimedRewards = 0; stakers[_stakeMsgSender()].conditionIdOflastUpdate = nextConditionId - 1; @@ -271,7 +264,7 @@ abstract contract Staking721 is ReentrancyGuard, IStaking721 { function _updateUnclaimedRewardsForStaker(address _staker) internal virtual { uint256 rewards = _calculateRewards(_staker); stakers[_staker].unclaimedRewards += rewards; - stakers[_staker].timeOfLastUpdate = block.timestamp; + stakers[_staker].timeOfLastUpdate = uint128(block.timestamp); stakers[_staker].conditionIdOflastUpdate = nextConditionId - 1; } diff --git a/contracts/extension/Staking721Upgradeable.sol b/contracts/extension/Staking721Upgradeable.sol index 44a92a064..03d748890 100644 --- a/contracts/extension/Staking721Upgradeable.sol +++ b/contracts/extension/Staking721Upgradeable.sol @@ -17,18 +17,18 @@ abstract contract Staking721Upgradeable is ReentrancyGuardUpgradeable, IStaking7 ///@dev Address of ERC721 NFT contract -- staked tokens belong to this contract. address public stakingToken; + /// @dev Flag to check direct transfers of staking tokens. + uint8 internal isStaking = 1; + + ///@dev Next staking condition Id. Tracks number of conditon updates so far. + uint64 private nextConditionId; + ///@dev List of token-ids ever staked. uint256[] public indexedTokens; /// @dev List of accounts that have staked their NFTs. address[] public stakersArray; - /// @dev Flag to check direct transfers of staking tokens. - uint8 internal isStaking = 1; - - ///@dev Next staking condition Id. Tracks number of conditon updates so far. - uint256 private nextConditionId; - ///@dev Mapping from token-id to whether it is indexed or not. mapping(uint256 => bool) public isIndexed; @@ -177,7 +177,7 @@ abstract contract Staking721Upgradeable is ReentrancyGuardUpgradeable, IStaking7 /// @dev Staking logic. Override to add custom logic. function _stake(uint256[] calldata _tokenIds) internal virtual { - uint256 len = _tokenIds.length; + uint64 len = uint64(_tokenIds.length); require(len != 0, "Staking 0 tokens"); address _stakingToken = stakingToken; @@ -186,17 +186,10 @@ abstract contract Staking721Upgradeable is ReentrancyGuardUpgradeable, IStaking7 _updateUnclaimedRewardsForStaker(_stakeMsgSender()); } else { stakersArray.push(_stakeMsgSender()); - stakers[_stakeMsgSender()].timeOfLastUpdate = block.timestamp; + stakers[_stakeMsgSender()].timeOfLastUpdate = uint128(block.timestamp); stakers[_stakeMsgSender()].conditionIdOflastUpdate = nextConditionId - 1; } for (uint256 i = 0; i < len; ++i) { - require( - IERC721(_stakingToken).ownerOf(_tokenIds[i]) == _stakeMsgSender() && - (IERC721(_stakingToken).getApproved(_tokenIds[i]) == address(this) || - IERC721(_stakingToken).isApprovedForAll(_stakeMsgSender(), address(this))), - "Not owned or approved" - ); - isStaking = 2; IERC721(_stakingToken).safeTransferFrom(_stakeMsgSender(), address(this), _tokenIds[i]); isStaking = 1; @@ -216,7 +209,7 @@ abstract contract Staking721Upgradeable is ReentrancyGuardUpgradeable, IStaking7 /// @dev Withdraw logic. Override to add custom logic. function _withdraw(uint256[] calldata _tokenIds) internal virtual { uint256 _amountStaked = stakers[_stakeMsgSender()].amountStaked; - uint256 len = _tokenIds.length; + uint64 len = uint64(_tokenIds.length); require(len != 0, "Withdrawing 0 tokens"); require(_amountStaked >= len, "Withdrawing more than staked"); @@ -251,7 +244,7 @@ abstract contract Staking721Upgradeable is ReentrancyGuardUpgradeable, IStaking7 require(rewards != 0, "No rewards"); - stakers[_stakeMsgSender()].timeOfLastUpdate = block.timestamp; + stakers[_stakeMsgSender()].timeOfLastUpdate = uint128(block.timestamp); stakers[_stakeMsgSender()].unclaimedRewards = 0; stakers[_stakeMsgSender()].conditionIdOflastUpdate = nextConditionId - 1; @@ -273,7 +266,7 @@ abstract contract Staking721Upgradeable is ReentrancyGuardUpgradeable, IStaking7 function _updateUnclaimedRewardsForStaker(address _staker) internal virtual { uint256 rewards = _calculateRewards(_staker); stakers[_staker].unclaimedRewards += rewards; - stakers[_staker].timeOfLastUpdate = block.timestamp; + stakers[_staker].timeOfLastUpdate = uint128(block.timestamp); stakers[_staker].conditionIdOflastUpdate = nextConditionId - 1; } diff --git a/contracts/extension/interface/IDrop.sol b/contracts/extension/interface/IDrop.sol index 6f25e5c83..4e466e6c3 100644 --- a/contracts/extension/interface/IDrop.sol +++ b/contracts/extension/interface/IDrop.sol @@ -15,7 +15,7 @@ import "./IClaimConditionMultiPhase.sol"; interface IDrop is IClaimConditionMultiPhase { /** - * @param proof Prood of concerned wallet's inclusion in an allowlist. + * @param proof Proof of concerned wallet's inclusion in an allowlist. * @param quantityLimitPerWallet The total quantity of tokens the allowlisted wallet is eligible to claim over time. * @param pricePerToken The price per token the allowlisted wallet must pay to claim tokens. * @param currency The currency in which the allowlisted wallet must pay the price for claiming tokens. diff --git a/contracts/extension/interface/IDrop1155.sol b/contracts/extension/interface/IDrop1155.sol index a69b73fd9..2a18da17e 100644 --- a/contracts/extension/interface/IDrop1155.sol +++ b/contracts/extension/interface/IDrop1155.sol @@ -15,7 +15,7 @@ import "./IClaimConditionMultiPhase.sol"; interface IDrop1155 is IClaimConditionMultiPhase { /** - * @param proof Prood of concerned wallet's inclusion in an allowlist. + * @param proof Proof of concerned wallet's inclusion in an allowlist. * @param quantityLimitPerWallet The total quantity of tokens the allowlisted wallet is eligible to claim over time. * @param pricePerToken The price per token the allowlisted wallet must pay to claim tokens. * @param currency The currency in which the allowlisted wallet must pay the price for claiming tokens. diff --git a/contracts/extension/interface/IDropSinglePhase.sol b/contracts/extension/interface/IDropSinglePhase.sol index f4468f192..92717755c 100644 --- a/contracts/extension/interface/IDropSinglePhase.sol +++ b/contracts/extension/interface/IDropSinglePhase.sol @@ -15,7 +15,7 @@ import "./IClaimCondition.sol"; interface IDropSinglePhase is IClaimCondition { /** - * @param proof Prood of concerned wallet's inclusion in an allowlist. + * @param proof Proof of concerned wallet's inclusion in an allowlist. * @param quantityLimitPerWallet The total quantity of tokens the allowlisted wallet is eligible to claim over time. * @param pricePerToken The price per token the allowlisted wallet must pay to claim tokens. * @param currency The currency in which the allowlisted wallet must pay the price for claiming tokens. diff --git a/contracts/extension/interface/IDropSinglePhase1155.sol b/contracts/extension/interface/IDropSinglePhase1155.sol index db9445d09..dd1d8f065 100644 --- a/contracts/extension/interface/IDropSinglePhase1155.sol +++ b/contracts/extension/interface/IDropSinglePhase1155.sol @@ -15,7 +15,7 @@ import "./IClaimCondition.sol"; interface IDropSinglePhase1155 is IClaimCondition { /** - * @param proof Prood of concerned wallet's inclusion in an allowlist. + * @param proof Proof of concerned wallet's inclusion in an allowlist. * @param quantityLimitPerWallet The total quantity of tokens the allowlisted wallet is eligible to claim over time. * @param pricePerToken The price per token the allowlisted wallet must pay to claim tokens. * @param currency The currency in which the allowlisted wallet must pay the price for claiming tokens. diff --git a/contracts/extension/interface/IStaking1155.sol b/contracts/extension/interface/IStaking1155.sol index 59502ac70..73e34e6b5 100644 --- a/contracts/extension/interface/IStaking1155.sol +++ b/contracts/extension/interface/IStaking1155.sol @@ -41,10 +41,10 @@ interface IStaking1155 { * @param conditionIdOflastUpdate Condition-Id when rewards were last updated for user. */ struct Staker { - uint256 amountStaked; - uint256 timeOfLastUpdate; + uint64 conditionIdOflastUpdate; + uint64 amountStaked; + uint128 timeOfLastUpdate; uint256 unclaimedRewards; - uint256 conditionIdOflastUpdate; } /** @@ -59,10 +59,10 @@ interface IStaking1155 { * @param endTimestamp Condition end timestamp. */ struct StakingCondition { - uint256 timeUnit; + uint80 timeUnit; + uint80 startTimestamp; + uint80 endTimestamp; uint256 rewardsPerUnitTime; - uint256 startTimestamp; - uint256 endTimestamp; } /** @@ -71,7 +71,7 @@ interface IStaking1155 { * @param tokenId ERC1155 token-id to stake. * @param amount Amount to stake. */ - function stake(uint256 tokenId, uint256 amount) external; + function stake(uint256 tokenId, uint64 amount) external; /** * @notice Withdraw staked tokens. @@ -79,7 +79,7 @@ interface IStaking1155 { * @param tokenId ERC1155 token-id to withdraw. * @param amount Amount to withdraw. */ - function withdraw(uint256 tokenId, uint256 amount) external; + function withdraw(uint256 tokenId, uint64 amount) external; /** * @notice Claim accumulated rewards. diff --git a/contracts/extension/interface/IStaking20.sol b/contracts/extension/interface/IStaking20.sol index 4971c82a6..494a0d0a5 100644 --- a/contracts/extension/interface/IStaking20.sol +++ b/contracts/extension/interface/IStaking20.sol @@ -39,10 +39,10 @@ interface IStaking20 { * @param conditionIdOflastUpdate Condition-Id when rewards were last updated for user. */ struct Staker { + uint128 timeOfLastUpdate; + uint64 conditionIdOflastUpdate; uint256 amountStaked; - uint256 timeOfLastUpdate; uint256 unclaimedRewards; - uint256 conditionIdOflastUpdate; } /** @@ -61,11 +61,11 @@ interface IStaking20 { * @param endTimestamp Condition end timestamp. */ struct StakingCondition { - uint256 timeUnit; + uint80 timeUnit; + uint80 startTimestamp; + uint80 endTimestamp; uint256 rewardRatioNumerator; uint256 rewardRatioDenominator; - uint256 startTimestamp; - uint256 endTimestamp; } /** diff --git a/contracts/extension/interface/IStaking721.sol b/contracts/extension/interface/IStaking721.sol index fcbc62eb3..21ef88b38 100644 --- a/contracts/extension/interface/IStaking721.sol +++ b/contracts/extension/interface/IStaking721.sol @@ -31,10 +31,10 @@ interface IStaking721 { * @param conditionIdOflastUpdate Condition-Id when rewards were last updated for user. */ struct Staker { - uint256 amountStaked; - uint256 timeOfLastUpdate; + uint64 amountStaked; + uint64 conditionIdOflastUpdate; + uint128 timeOfLastUpdate; uint256 unclaimedRewards; - uint256 conditionIdOflastUpdate; } /** diff --git a/contracts/pack/PackVRFDirect.sol b/contracts/pack/PackVRFDirect.sol index 5cfb71b44..ad5c91969 100644 --- a/contracts/pack/PackVRFDirect.sol +++ b/contracts/pack/PackVRFDirect.sol @@ -23,7 +23,7 @@ import "@openzeppelin/contracts-upgradeable/interfaces/IERC2981Upgradeable.sol"; import "@openzeppelin/contracts/interfaces/IERC721Receiver.sol"; import { IERC1155Receiver } from "@openzeppelin/contracts/interfaces/IERC1155Receiver.sol"; -import "@chainlink/contracts/src/v0.8/vrf/VRFV2WrapperConsumerBase.sol"; +import "./VRFV2WrapperConsumerBase.sol"; // ========== Internal imports ========== diff --git a/contracts/pack/VRFV2WrapperConsumerBase.sol b/contracts/pack/VRFV2WrapperConsumerBase.sol new file mode 100644 index 000000000..c5e2f7e68 --- /dev/null +++ b/contracts/pack/VRFV2WrapperConsumerBase.sol @@ -0,0 +1,83 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "@chainlink/contracts/src/v0.8/interfaces/LinkTokenInterface.sol"; +import "@chainlink/contracts/src/v0.8/interfaces/VRFV2WrapperInterface.sol"; + +/** ******************************************************************************* + * @notice Interface for contracts using VRF randomness through the VRF V2 wrapper + * ******************************************************************************** + * @dev PURPOSE + * + * @dev Create VRF V2 requests without the need for subscription management. Rather than creating + * @dev and funding a VRF V2 subscription, a user can use this wrapper to create one off requests, + * @dev paying up front rather than at fulfillment. + * + * @dev Since the price is determined using the gas price of the request transaction rather than + * @dev the fulfillment transaction, the wrapper charges an additional premium on callback gas + * @dev usage, in addition to some extra overhead costs associated with the VRFV2Wrapper contract. + * ***************************************************************************** + * @dev USAGE + * + * @dev Calling contracts must inherit from VRFV2WrapperConsumerBase. The consumer must be funded + * @dev with enough LINK to make the request, otherwise requests will revert. To request randomness, + * @dev call the 'requestRandomness' function with the desired VRF parameters. This function handles + * @dev paying for the request based on the current pricing. + * + * @dev Consumers must implement the fullfillRandomWords function, which will be called during + * @dev fulfillment with the randomness result. + */ +abstract contract VRFV2WrapperConsumerBase { + // solhint-disable-next-line var-name-mixedcase + LinkTokenInterface internal immutable LINK; + // solhint-disable-next-line var-name-mixedcase + VRFV2WrapperInterface internal immutable VRF_V2_WRAPPER; + + /** + * @param _link is the address of LinkToken + * @param _vrfV2Wrapper is the address of the VRFV2Wrapper contract + */ + constructor(address _link, address _vrfV2Wrapper) { + LINK = LinkTokenInterface(_link); + VRF_V2_WRAPPER = VRFV2WrapperInterface(_vrfV2Wrapper); + } + + /** + * @dev Requests randomness from the VRF V2 wrapper. + * + * @param _callbackGasLimit is the gas limit that should be used when calling the consumer's + * fulfillRandomWords function. + * @param _requestConfirmations is the number of confirmations to wait before fulfilling the + * request. A higher number of confirmations increases security by reducing the likelihood + * that a chain re-org changes a published randomness outcome. + * @param _numWords is the number of random words to request. + * + * @return requestId is the VRF V2 request ID of the newly created randomness request. + */ + function requestRandomness( + uint32 _callbackGasLimit, + uint16 _requestConfirmations, + uint32 _numWords + ) internal returns (uint256 requestId) { + LINK.transferAndCall( + address(VRF_V2_WRAPPER), + VRF_V2_WRAPPER.calculateRequestPrice(_callbackGasLimit), + abi.encode(_callbackGasLimit, _requestConfirmations, _numWords) + ); + return VRF_V2_WRAPPER.lastRequestId(); + } + + /** + * @notice fulfillRandomWords handles the VRF V2 wrapper response. The consuming contract must + * @notice implement it. + * + * @param _requestId is the VRF V2 request ID. + * @param _randomWords is the randomness result. + */ + function fulfillRandomWords(uint256 _requestId, uint256[] memory _randomWords) internal virtual; + + function rawFulfillRandomWords(uint256 _requestId, uint256[] memory _randomWords) external { + require(msg.sender == address(VRF_V2_WRAPPER), "only VRF V2 wrapper can fulfill"); + fulfillRandomWords(_requestId, _randomWords); + } +} diff --git a/contracts/package.json b/contracts/package.json index 9e3f0a4cb..75bf603e7 100644 --- a/contracts/package.json +++ b/contracts/package.json @@ -1,7 +1,7 @@ { "name": "@thirdweb-dev/contracts", "description": "Collection of smart contracts deployable via the thirdweb SDK, dashboard and CLI", - "version": "3.8.0", + "version": "3.8.2", "license": "Apache-2.0", "repository": { "type": "git", diff --git a/contracts/smart-wallet/non-upgradeable/Account.sol b/contracts/smart-wallet/non-upgradeable/Account.sol index 5b068ab4e..a20f100a0 100644 --- a/contracts/smart-wallet/non-upgradeable/Account.sol +++ b/contracts/smart-wallet/non-upgradeable/Account.sol @@ -107,42 +107,52 @@ contract Account is // First, check if the signer is an admin. if (data.isAdmin[_signer]) { return true; - } else { - SignerPermissionsStatic memory permissions = data.signerPermissions[_signer]; - - // If not an admin, check if the signer is active. - require( - permissions.startTimestamp <= block.timestamp && - block.timestamp < permissions.endTimestamp && - data.approvedTargets[_signer].length() > 0, - "Account: no active permissions." - ); - - // Extract the function signature from the userOp calldata and check whether the signer is attempting to call `execute` or `executeBatch`. - bytes4 sig = getFunctionSignature(_userOp.callData); - - if (sig == this.execute.selector) { - // Extract the `target` and `value` arguments from the calldata for `execute`. - (address target, uint256 value) = decodeExecuteCalldata(_userOp.callData); - - // Check if the value is within the allowed range and if the target is approved. - require(permissions.nativeTokenLimitPerTransaction >= value, "Account: value too high."); - require(data.approvedTargets[_signer].contains(target), "Account: target not approved."); - } else if (sig == this.executeBatch.selector) { - // Extract the `target` and `value` array arguments from the calldata for `executeBatch`. - (address[] memory targets, uint256[] memory values, ) = decodeExecuteBatchCalldata(_userOp.callData); - - // For each target+value pair, check if the value is within the allowed range and if the target is approved. - for (uint256 i = 0; i < targets.length; i++) { - require(permissions.nativeTokenLimitPerTransaction >= values[i], "Account: value too high."); - require(data.approvedTargets[_signer].contains(targets[i]), "Account: target not approved."); + } + + SignerPermissionsStatic memory permissions = data.signerPermissions[_signer]; + + // If not an admin, check if the signer is active. + if ( + permissions.startTimestamp > block.timestamp || + block.timestamp >= permissions.endTimestamp || + data.approvedTargets[_signer].length() == 0 + ) { + // Account: no active permissions. + return false; + } + + // Extract the function signature from the userOp calldata and check whether the signer is attempting to call `execute` or `executeBatch`. + bytes4 sig = getFunctionSignature(_userOp.callData); + + if (sig == this.execute.selector) { + // Extract the `target` and `value` arguments from the calldata for `execute`. + (address target, uint256 value) = decodeExecuteCalldata(_userOp.callData); + + // Check if the value is within the allowed range and if the target is approved. + if (permissions.nativeTokenLimitPerTransaction < value || !data.approvedTargets[_signer].contains(target)) { + // Account: value too high OR Account: target not approved. + return false; + } + } else if (sig == this.executeBatch.selector) { + // Extract the `target` and `value` array arguments from the calldata for `executeBatch`. + (address[] memory targets, uint256[] memory values, ) = decodeExecuteBatchCalldata(_userOp.callData); + + // For each target+value pair, check if the value is within the allowed range and if the target is approved. + for (uint256 i = 0; i < targets.length; i++) { + if ( + permissions.nativeTokenLimitPerTransaction < values[i] || + !data.approvedTargets[_signer].contains(targets[i]) + ) { + // Account: value too high OR Account: target not approved. + return false; } - } else { - revert("Account: calling invalid fn."); } - - return true; + } else { + // Account: calling invalid fn. + return false; } + + return true; } /// @notice See EIP-1271 diff --git a/contracts/smart-wallet/utils/AccountCore.sol b/contracts/smart-wallet/utils/AccountCore.sol index 25615487b..b11c60cf3 100644 --- a/contracts/smart-wallet/utils/AccountCore.sol +++ b/contracts/smart-wallet/utils/AccountCore.sol @@ -78,48 +78,59 @@ contract AccountCore is IAccountCore, Initializable, Multicall, BaseAccount, ERC /// @notice Returns whether a signer is authorized to perform transactions using the wallet. function isValidSigner(address _signer, UserOperation calldata _userOp) public view virtual returns (bool) { + // We use the underlying storage instead of high level view functions to save gas. // We use the underlying storage instead of high level view functions to save gas. AccountPermissionsStorage.Data storage data = AccountPermissionsStorage.accountPermissionsStorage(); // First, check if the signer is an admin. if (data.isAdmin[_signer]) { return true; - } else { - SignerPermissionsStatic memory permissions = data.signerPermissions[_signer]; - - // If not an admin, check if the signer is active. - require( - permissions.startTimestamp <= block.timestamp && - block.timestamp < permissions.endTimestamp && - data.approvedTargets[_signer].length() > 0, - "Account: no active permissions." - ); - - // Extract the function signature from the userOp calldata and check whether the signer is attempting to call `execute` or `executeBatch`. - bytes4 sig = getFunctionSignature(_userOp.callData); - - if (sig == Account.execute.selector) { - // Extract the `target` and `value` arguments from the calldata for `execute`. - (address target, uint256 value) = decodeExecuteCalldata(_userOp.callData); - - // Check if the value is within the allowed range and if the target is approved. - require(permissions.nativeTokenLimitPerTransaction >= value, "Account: value too high."); - require(data.approvedTargets[_signer].contains(target), "Account: target not approved."); - } else if (sig == Account.executeBatch.selector) { - // Extract the `target` and `value` array arguments from the calldata for `executeBatch`. - (address[] memory targets, uint256[] memory values, ) = decodeExecuteBatchCalldata(_userOp.callData); - - // For each target+value pair, check if the value is within the allowed range and if the target is approved. - for (uint256 i = 0; i < targets.length; i++) { - require(permissions.nativeTokenLimitPerTransaction >= values[i], "Account: value too high."); - require(data.approvedTargets[_signer].contains(targets[i]), "Account: target not approved."); + } + + SignerPermissionsStatic memory permissions = data.signerPermissions[_signer]; + + // If not an admin, check if the signer is active. + if ( + permissions.startTimestamp > block.timestamp || + block.timestamp >= permissions.endTimestamp || + data.approvedTargets[_signer].length() == 0 + ) { + // Account: no active permissions. + return false; + } + + // Extract the function signature from the userOp calldata and check whether the signer is attempting to call `execute` or `executeBatch`. + bytes4 sig = getFunctionSignature(_userOp.callData); + + if (sig == Account.execute.selector) { + // Extract the `target` and `value` arguments from the calldata for `execute`. + (address target, uint256 value) = decodeExecuteCalldata(_userOp.callData); + + // Check if the value is within the allowed range and if the target is approved. + if (permissions.nativeTokenLimitPerTransaction < value || !data.approvedTargets[_signer].contains(target)) { + // Account: value too high OR Account: target not approved. + return false; + } + } else if (sig == Account.executeBatch.selector) { + // Extract the `target` and `value` array arguments from the calldata for `executeBatch`. + (address[] memory targets, uint256[] memory values, ) = decodeExecuteBatchCalldata(_userOp.callData); + + // For each target+value pair, check if the value is within the allowed range and if the target is approved. + for (uint256 i = 0; i < targets.length; i++) { + if ( + permissions.nativeTokenLimitPerTransaction < values[i] || + !data.approvedTargets[_signer].contains(targets[i]) + ) { + // Account: value too high OR Account: target not approved. + return false; } - } else { - revert("Account: calling invalid fn."); } - - return true; + } else { + // Account: calling invalid fn. + return false; } + + return true; } /// @notice See EIP-1271 diff --git a/contracts/staking/EditionStake.sol b/contracts/staking/EditionStake.sol index 6f0865adc..01f15bafa 100644 --- a/contracts/staking/EditionStake.sol +++ b/contracts/staking/EditionStake.sol @@ -65,7 +65,7 @@ contract EditionStake is address[] memory _trustedForwarders, address _rewardToken, address _stakingToken, - uint256 _defaultTimeUnit, + uint80 _defaultTimeUnit, uint256 _defaultRewardsPerUnitTime ) external initializer { __ERC2771Context_init_unchained(_trustedForwarders); @@ -147,7 +147,7 @@ contract EditionStake is uint256, uint256, bytes calldata - ) external returns (bytes4) { + ) external view returns (bytes4) { require(isStaking == 2, "Direct transfer"); return this.onERC1155Received.selector; } diff --git a/contracts/staking/TokenStake.sol b/contracts/staking/TokenStake.sol index f9a1eec9d..77d785e88 100644 --- a/contracts/staking/TokenStake.sol +++ b/contracts/staking/TokenStake.sol @@ -57,7 +57,7 @@ contract TokenStake is address[] memory _trustedForwarders, address _rewardToken, address _stakingToken, - uint256 _timeUnit, + uint80 _timeUnit, uint256 _rewardRatioNumerator, uint256 _rewardRatioDenominator ) external initializer { @@ -66,10 +66,10 @@ contract TokenStake is require(_rewardToken != _stakingToken, "Reward Token and Staking Token can't be same."); rewardToken = _rewardToken; - uint256 _stakingTokenDecimals = _stakingToken == CurrencyTransferLib.NATIVE_TOKEN + uint16 _stakingTokenDecimals = _stakingToken == CurrencyTransferLib.NATIVE_TOKEN ? 18 : IERC20Metadata(_stakingToken).decimals(); - uint256 _rewardTokenDecimals = _rewardToken == CurrencyTransferLib.NATIVE_TOKEN + uint16 _rewardTokenDecimals = _rewardToken == CurrencyTransferLib.NATIVE_TOKEN ? 18 : IERC20Metadata(_rewardToken).decimals(); diff --git a/docs/ERC6551AccountLib.md b/docs/ERC6551AccountLib.md new file mode 100644 index 000000000..03d95b596 --- /dev/null +++ b/docs/ERC6551AccountLib.md @@ -0,0 +1,12 @@ +# ERC6551AccountLib + + + + + + + + + + + diff --git a/docs/ERC6551BytecodeLib.md b/docs/ERC6551BytecodeLib.md new file mode 100644 index 000000000..234567136 --- /dev/null +++ b/docs/ERC6551BytecodeLib.md @@ -0,0 +1,12 @@ +# ERC6551BytecodeLib + + + + + + + + + + + diff --git a/docs/ERC721AQueryableUpgradeable.md b/docs/ERC721AQueryableUpgradeable.md new file mode 100644 index 000000000..d95adb25e --- /dev/null +++ b/docs/ERC721AQueryableUpgradeable.md @@ -0,0 +1,589 @@ +# ERC721AQueryableUpgradeable + + + +> ERC721AQueryable. + + + +*ERC721A subclass with convenience query functions.* + +## Methods + +### approve + +```solidity +function approve(address to, uint256 tokenId) external payable +``` + + + +*Gives permission to `to` to transfer `tokenId` token to another account. See {ERC721A-_approve}. Requirements: - The caller must own the token or be an approved operator.* + +#### Parameters + +| Name | Type | Description | +|---|---|---| +| to | address | undefined | +| tokenId | uint256 | undefined | + +### balanceOf + +```solidity +function balanceOf(address owner) external view returns (uint256) +``` + + + +*Returns the number of tokens in `owner`'s account.* + +#### Parameters + +| Name | Type | Description | +|---|---|---| +| owner | address | undefined | + +#### Returns + +| Name | Type | Description | +|---|---|---| +| _0 | uint256 | undefined | + +### explicitOwnershipOf + +```solidity +function explicitOwnershipOf(uint256 tokenId) external view returns (struct IERC721AUpgradeable.TokenOwnership ownership) +``` + + + +*Returns the `TokenOwnership` struct at `tokenId` without reverting. If the `tokenId` is out of bounds: - `addr = address(0)` - `startTimestamp = 0` - `burned = false` - `extraData = 0` If the `tokenId` is burned: - `addr = <Address of owner before token was burned>` - `startTimestamp = <Timestamp when token was burned>` - `burned = true` - `extraData = <Extra data when token was burned>` Otherwise: - `addr = <Address of owner>` - `startTimestamp = <Timestamp of start of ownership>` - `burned = false` - `extraData = <Extra data at start of ownership>`* + +#### Parameters + +| Name | Type | Description | +|---|---|---| +| tokenId | uint256 | undefined | + +#### Returns + +| Name | Type | Description | +|---|---|---| +| ownership | IERC721AUpgradeable.TokenOwnership | undefined | + +### getApproved + +```solidity +function getApproved(uint256 tokenId) external view returns (address) +``` + + + +*Returns the account approved for `tokenId` token. Requirements: - `tokenId` must exist.* + +#### Parameters + +| Name | Type | Description | +|---|---|---| +| tokenId | uint256 | undefined | + +#### Returns + +| Name | Type | Description | +|---|---|---| +| _0 | address | undefined | + +### isApprovedForAll + +```solidity +function isApprovedForAll(address owner, address operator) external view returns (bool) +``` + + + +*Returns if the `operator` is allowed to manage all of the assets of `owner`. See {setApprovalForAll}.* + +#### Parameters + +| Name | Type | Description | +|---|---|---| +| owner | address | undefined | +| operator | address | undefined | + +#### Returns + +| Name | Type | Description | +|---|---|---| +| _0 | bool | undefined | + +### name + +```solidity +function name() external view returns (string) +``` + + + +*Returns the token collection name.* + + +#### Returns + +| Name | Type | Description | +|---|---|---| +| _0 | string | undefined | + +### ownerOf + +```solidity +function ownerOf(uint256 tokenId) external view returns (address) +``` + + + +*Returns the owner of the `tokenId` token. Requirements: - `tokenId` must exist.* + +#### Parameters + +| Name | Type | Description | +|---|---|---| +| tokenId | uint256 | undefined | + +#### Returns + +| Name | Type | Description | +|---|---|---| +| _0 | address | undefined | + +### safeTransferFrom + +```solidity +function safeTransferFrom(address from, address to, uint256 tokenId) external payable +``` + + + +*Equivalent to `safeTransferFrom(from, to, tokenId, '')`.* + +#### Parameters + +| Name | Type | Description | +|---|---|---| +| from | address | undefined | +| to | address | undefined | +| tokenId | uint256 | undefined | + +### safeTransferFrom + +```solidity +function safeTransferFrom(address from, address to, uint256 tokenId, bytes _data) external payable +``` + + + +*Safely transfers `tokenId` token from `from` to `to`. Requirements: - `from` cannot be the zero address. - `to` cannot be the zero address. - `tokenId` token must exist and be owned by `from`. - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}. - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. Emits a {Transfer} event.* + +#### Parameters + +| Name | Type | Description | +|---|---|---| +| from | address | undefined | +| to | address | undefined | +| tokenId | uint256 | undefined | +| _data | bytes | undefined | + +### setApprovalForAll + +```solidity +function setApprovalForAll(address operator, bool approved) external nonpayable +``` + + + +*Approve or remove `operator` as an operator for the caller. Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller. Requirements: - The `operator` cannot be the caller. Emits an {ApprovalForAll} event.* + +#### Parameters + +| Name | Type | Description | +|---|---|---| +| operator | address | undefined | +| approved | bool | undefined | + +### supportsInterface + +```solidity +function supportsInterface(bytes4 interfaceId) external view returns (bool) +``` + + + +*Returns true if this contract implements the interface defined by `interfaceId`. See the corresponding [EIP section](https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified) to learn more about how these ids are created. This function call must use less than 30000 gas.* + +#### Parameters + +| Name | Type | Description | +|---|---|---| +| interfaceId | bytes4 | undefined | + +#### Returns + +| Name | Type | Description | +|---|---|---| +| _0 | bool | undefined | + +### symbol + +```solidity +function symbol() external view returns (string) +``` + + + +*Returns the token collection symbol.* + + +#### Returns + +| Name | Type | Description | +|---|---|---| +| _0 | string | undefined | + +### tokenURI + +```solidity +function tokenURI(uint256 tokenId) external view returns (string) +``` + + + +*Returns the Uniform Resource Identifier (URI) for `tokenId` token.* + +#### Parameters + +| Name | Type | Description | +|---|---|---| +| tokenId | uint256 | undefined | + +#### Returns + +| Name | Type | Description | +|---|---|---| +| _0 | string | undefined | + +### tokensOfOwner + +```solidity +function tokensOfOwner(address owner) external view returns (uint256[]) +``` + + + +*Returns an array of token IDs owned by `owner`. This function scans the ownership mapping and is O(`totalSupply`) in complexity. It is meant to be called off-chain. See {ERC721AQueryable-tokensOfOwnerIn} for splitting the scan into multiple smaller scans if the collection is large enough to cause an out-of-gas error (10K collections should be fine).* + +#### Parameters + +| Name | Type | Description | +|---|---|---| +| owner | address | undefined | + +#### Returns + +| Name | Type | Description | +|---|---|---| +| _0 | uint256[] | undefined | + +### tokensOfOwnerIn + +```solidity +function tokensOfOwnerIn(address owner, uint256 start, uint256 stop) external view returns (uint256[]) +``` + + + +*Returns an array of token IDs owned by `owner`, in the range [`start`, `stop`) (i.e. `start <= tokenId < stop`). This function allows for tokens to be queried if the collection grows too big for a single call of {ERC721AQueryable-tokensOfOwner}. Requirements: - `start < stop`* + +#### Parameters + +| Name | Type | Description | +|---|---|---| +| owner | address | undefined | +| start | uint256 | undefined | +| stop | uint256 | undefined | + +#### Returns + +| Name | Type | Description | +|---|---|---| +| _0 | uint256[] | undefined | + +### totalSupply + +```solidity +function totalSupply() external view returns (uint256) +``` + + + +*Returns the total number of tokens in existence. Burned tokens will reduce the count. To get the total number of tokens minted, please see {_totalMinted}.* + + +#### Returns + +| Name | Type | Description | +|---|---|---| +| _0 | uint256 | undefined | + +### transferFrom + +```solidity +function transferFrom(address from, address to, uint256 tokenId) external payable +``` + + + +*Transfers `tokenId` from `from` to `to`. Requirements: - `from` cannot be the zero address. - `to` cannot be the zero address. - `tokenId` token must be owned by `from`. - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}. Emits a {Transfer} event.* + +#### Parameters + +| Name | Type | Description | +|---|---|---| +| from | address | undefined | +| to | address | undefined | +| tokenId | uint256 | undefined | + + + +## Events + +### Approval + +```solidity +event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId) +``` + + + + + +#### Parameters + +| Name | Type | Description | +|---|---|---| +| owner `indexed` | address | undefined | +| approved `indexed` | address | undefined | +| tokenId `indexed` | uint256 | undefined | + +### ApprovalForAll + +```solidity +event ApprovalForAll(address indexed owner, address indexed operator, bool approved) +``` + + + + + +#### Parameters + +| Name | Type | Description | +|---|---|---| +| owner `indexed` | address | undefined | +| operator `indexed` | address | undefined | +| approved | bool | undefined | + +### ConsecutiveTransfer + +```solidity +event ConsecutiveTransfer(uint256 indexed fromTokenId, uint256 toTokenId, address indexed from, address indexed to) +``` + + + + + +#### Parameters + +| Name | Type | Description | +|---|---|---| +| fromTokenId `indexed` | uint256 | undefined | +| toTokenId | uint256 | undefined | +| from `indexed` | address | undefined | +| to `indexed` | address | undefined | + +### Transfer + +```solidity +event Transfer(address indexed from, address indexed to, uint256 indexed tokenId) +``` + + + + + +#### Parameters + +| Name | Type | Description | +|---|---|---| +| from `indexed` | address | undefined | +| to `indexed` | address | undefined | +| tokenId `indexed` | uint256 | undefined | + + + +## Errors + +### ApprovalCallerNotOwnerNorApproved + +```solidity +error ApprovalCallerNotOwnerNorApproved() +``` + +The caller must own the token or be an approved operator. + + + + +### ApprovalQueryForNonexistentToken + +```solidity +error ApprovalQueryForNonexistentToken() +``` + +The token does not exist. + + + + +### BalanceQueryForZeroAddress + +```solidity +error BalanceQueryForZeroAddress() +``` + +Cannot query the balance for the zero address. + + + + +### InvalidQueryRange + +```solidity +error InvalidQueryRange() +``` + +Invalid query range (`start` >= `stop`). + + + + +### MintERC2309QuantityExceedsLimit + +```solidity +error MintERC2309QuantityExceedsLimit() +``` + +The `quantity` minted with ERC2309 exceeds the safety limit. + + + + +### MintToZeroAddress + +```solidity +error MintToZeroAddress() +``` + +Cannot mint to the zero address. + + + + +### MintZeroQuantity + +```solidity +error MintZeroQuantity() +``` + +The quantity of tokens minted must be more than zero. + + + + +### OwnerQueryForNonexistentToken + +```solidity +error OwnerQueryForNonexistentToken() +``` + +The token does not exist. + + + + +### OwnershipNotInitializedForExtraData + +```solidity +error OwnershipNotInitializedForExtraData() +``` + +The `extraData` cannot be set on an unintialized ownership slot. + + + + +### TransferCallerNotOwnerNorApproved + +```solidity +error TransferCallerNotOwnerNorApproved() +``` + +The caller must own the token or be an approved operator. + + + + +### TransferFromIncorrectOwner + +```solidity +error TransferFromIncorrectOwner() +``` + +The token must be owned by `from`. + + + + +### TransferToNonERC721ReceiverImplementer + +```solidity +error TransferToNonERC721ReceiverImplementer() +``` + +Cannot safely transfer to a contract that does not implement the ERC721Receiver interface. + + + + +### TransferToZeroAddress + +```solidity +error TransferToZeroAddress() +``` + +Cannot transfer to the zero address. + + + + +### URIQueryForNonexistentToken + +```solidity +error URIQueryForNonexistentToken() +``` + +The token does not exist. + + + + + diff --git a/docs/ERC721AStorage.md b/docs/ERC721AStorage.md index 0737cb905..4ead51e0e 100644 --- a/docs/ERC721AStorage.md +++ b/docs/ERC721AStorage.md @@ -8,25 +8,5 @@ -## Methods - -### ERC721A_STORAGE_POSITION - -```solidity -function ERC721A_STORAGE_POSITION() external view returns (bytes32) -``` - - - - - - -#### Returns - -| Name | Type | Description | -|---|---|---| -| _0 | bytes32 | undefined | - - diff --git a/docs/ERC721A__IERC721ReceiverUpgradeable.md b/docs/ERC721A__IERC721ReceiverUpgradeable.md new file mode 100644 index 000000000..3de156096 --- /dev/null +++ b/docs/ERC721A__IERC721ReceiverUpgradeable.md @@ -0,0 +1,40 @@ +# ERC721A__IERC721ReceiverUpgradeable + + + + + + + +*Interface of ERC721 token receiver.* + +## Methods + +### onERC721Received + +```solidity +function onERC721Received(address operator, address from, uint256 tokenId, bytes data) external nonpayable returns (bytes4) +``` + + + + + +#### Parameters + +| Name | Type | Description | +|---|---|---| +| operator | address | undefined | +| from | address | undefined | +| tokenId | uint256 | undefined | +| data | bytes | undefined | + +#### Returns + +| Name | Type | Description | +|---|---|---| +| _0 | bytes4 | undefined | + + + + diff --git a/docs/ERC721A__Initializable.md b/docs/ERC721A__Initializable.md new file mode 100644 index 000000000..8b4854503 --- /dev/null +++ b/docs/ERC721A__Initializable.md @@ -0,0 +1,12 @@ +# ERC721A__Initializable + + + + + + + + + + + diff --git a/docs/ERC721A__InitializableStorage.md b/docs/ERC721A__InitializableStorage.md new file mode 100644 index 000000000..2927cdb21 --- /dev/null +++ b/docs/ERC721A__InitializableStorage.md @@ -0,0 +1,12 @@ +# ERC721A__InitializableStorage + + + + + + + +*This is a base storage for the initialization function for upgradeable diamond facet contracts** + + + diff --git a/docs/IERC6551Account.md b/docs/IERC6551Account.md new file mode 100644 index 000000000..e910ffadb --- /dev/null +++ b/docs/IERC6551Account.md @@ -0,0 +1,113 @@ +# IERC6551Account + + + + + + + +*the ERC-165 identifier for this interface is `0xeff4d378`* + +## Methods + +### executeCall + +```solidity +function executeCall(address to, uint256 value, bytes data) external payable returns (bytes) +``` + + + + + +#### Parameters + +| Name | Type | Description | +|---|---|---| +| to | address | undefined | +| value | uint256 | undefined | +| data | bytes | undefined | + +#### Returns + +| Name | Type | Description | +|---|---|---| +| _0 | bytes | undefined | + +### nonce + +```solidity +function nonce() external view returns (uint256) +``` + + + + + + +#### Returns + +| Name | Type | Description | +|---|---|---| +| _0 | uint256 | undefined | + +### owner + +```solidity +function owner() external view returns (address) +``` + + + + + + +#### Returns + +| Name | Type | Description | +|---|---|---| +| _0 | address | undefined | + +### token + +```solidity +function token() external view returns (uint256 chainId, address tokenContract, uint256 tokenId) +``` + + + + + + +#### Returns + +| Name | Type | Description | +|---|---|---| +| chainId | uint256 | undefined | +| tokenContract | address | undefined | +| tokenId | uint256 | undefined | + + + +## Events + +### TransactionExecuted + +```solidity +event TransactionExecuted(address indexed target, uint256 indexed value, bytes data) +``` + + + + + +#### Parameters + +| Name | Type | Description | +|---|---|---| +| target `indexed` | address | undefined | +| value `indexed` | uint256 | undefined | +| data | bytes | undefined | + + + diff --git a/docs/IERC6551AccountProxy.md b/docs/IERC6551AccountProxy.md new file mode 100644 index 000000000..939810a14 --- /dev/null +++ b/docs/IERC6551AccountProxy.md @@ -0,0 +1,32 @@ +# IERC6551AccountProxy + + + + + + + + + +## Methods + +### implementation + +```solidity +function implementation() external view returns (address) +``` + + + + + + +#### Returns + +| Name | Type | Description | +|---|---|---| +| _0 | address | undefined | + + + + diff --git a/docs/IERC721AQueryableUpgradeable.md b/docs/IERC721AQueryableUpgradeable.md new file mode 100644 index 000000000..2e41b18fd --- /dev/null +++ b/docs/IERC721AQueryableUpgradeable.md @@ -0,0 +1,589 @@ +# IERC721AQueryableUpgradeable + + + + + + + +*Interface of ERC721AQueryable.* + +## Methods + +### approve + +```solidity +function approve(address to, uint256 tokenId) external payable +``` + + + +*Gives permission to `to` to transfer `tokenId` token to another account. The approval is cleared when the token is transferred. Only a single account can be approved at a time, so approving the zero address clears previous approvals. Requirements: - The caller must own the token or be an approved operator. - `tokenId` must exist. Emits an {Approval} event.* + +#### Parameters + +| Name | Type | Description | +|---|---|---| +| to | address | undefined | +| tokenId | uint256 | undefined | + +### balanceOf + +```solidity +function balanceOf(address owner) external view returns (uint256 balance) +``` + + + +*Returns the number of tokens in `owner`'s account.* + +#### Parameters + +| Name | Type | Description | +|---|---|---| +| owner | address | undefined | + +#### Returns + +| Name | Type | Description | +|---|---|---| +| balance | uint256 | undefined | + +### explicitOwnershipOf + +```solidity +function explicitOwnershipOf(uint256 tokenId) external view returns (struct IERC721AUpgradeable.TokenOwnership) +``` + + + +*Returns the `TokenOwnership` struct at `tokenId` without reverting. If the `tokenId` is out of bounds: - `addr = address(0)` - `startTimestamp = 0` - `burned = false` - `extraData = 0` If the `tokenId` is burned: - `addr = <Address of owner before token was burned>` - `startTimestamp = <Timestamp when token was burned>` - `burned = true` - `extraData = <Extra data when token was burned>` Otherwise: - `addr = <Address of owner>` - `startTimestamp = <Timestamp of start of ownership>` - `burned = false` - `extraData = <Extra data at start of ownership>`* + +#### Parameters + +| Name | Type | Description | +|---|---|---| +| tokenId | uint256 | undefined | + +#### Returns + +| Name | Type | Description | +|---|---|---| +| _0 | IERC721AUpgradeable.TokenOwnership | undefined | + +### getApproved + +```solidity +function getApproved(uint256 tokenId) external view returns (address operator) +``` + + + +*Returns the account approved for `tokenId` token. Requirements: - `tokenId` must exist.* + +#### Parameters + +| Name | Type | Description | +|---|---|---| +| tokenId | uint256 | undefined | + +#### Returns + +| Name | Type | Description | +|---|---|---| +| operator | address | undefined | + +### isApprovedForAll + +```solidity +function isApprovedForAll(address owner, address operator) external view returns (bool) +``` + + + +*Returns if the `operator` is allowed to manage all of the assets of `owner`. See {setApprovalForAll}.* + +#### Parameters + +| Name | Type | Description | +|---|---|---| +| owner | address | undefined | +| operator | address | undefined | + +#### Returns + +| Name | Type | Description | +|---|---|---| +| _0 | bool | undefined | + +### name + +```solidity +function name() external view returns (string) +``` + + + +*Returns the token collection name.* + + +#### Returns + +| Name | Type | Description | +|---|---|---| +| _0 | string | undefined | + +### ownerOf + +```solidity +function ownerOf(uint256 tokenId) external view returns (address owner) +``` + + + +*Returns the owner of the `tokenId` token. Requirements: - `tokenId` must exist.* + +#### Parameters + +| Name | Type | Description | +|---|---|---| +| tokenId | uint256 | undefined | + +#### Returns + +| Name | Type | Description | +|---|---|---| +| owner | address | undefined | + +### safeTransferFrom + +```solidity +function safeTransferFrom(address from, address to, uint256 tokenId) external payable +``` + + + +*Equivalent to `safeTransferFrom(from, to, tokenId, '')`.* + +#### Parameters + +| Name | Type | Description | +|---|---|---| +| from | address | undefined | +| to | address | undefined | +| tokenId | uint256 | undefined | + +### safeTransferFrom + +```solidity +function safeTransferFrom(address from, address to, uint256 tokenId, bytes data) external payable +``` + + + +*Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients are aware of the ERC721 protocol to prevent tokens from being forever locked. Requirements: - `from` cannot be the zero address. - `to` cannot be the zero address. - `tokenId` token must exist and be owned by `from`. - If the caller is not `from`, it must be have been allowed to move this token by either {approve} or {setApprovalForAll}. - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. Emits a {Transfer} event.* + +#### Parameters + +| Name | Type | Description | +|---|---|---| +| from | address | undefined | +| to | address | undefined | +| tokenId | uint256 | undefined | +| data | bytes | undefined | + +### setApprovalForAll + +```solidity +function setApprovalForAll(address operator, bool _approved) external nonpayable +``` + + + +*Approve or remove `operator` as an operator for the caller. Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller. Requirements: - The `operator` cannot be the caller. Emits an {ApprovalForAll} event.* + +#### Parameters + +| Name | Type | Description | +|---|---|---| +| operator | address | undefined | +| _approved | bool | undefined | + +### supportsInterface + +```solidity +function supportsInterface(bytes4 interfaceId) external view returns (bool) +``` + + + +*Returns true if this contract implements the interface defined by `interfaceId`. See the corresponding [EIP section](https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified) to learn more about how these ids are created. This function call must use less than 30000 gas.* + +#### Parameters + +| Name | Type | Description | +|---|---|---| +| interfaceId | bytes4 | undefined | + +#### Returns + +| Name | Type | Description | +|---|---|---| +| _0 | bool | undefined | + +### symbol + +```solidity +function symbol() external view returns (string) +``` + + + +*Returns the token collection symbol.* + + +#### Returns + +| Name | Type | Description | +|---|---|---| +| _0 | string | undefined | + +### tokenURI + +```solidity +function tokenURI(uint256 tokenId) external view returns (string) +``` + + + +*Returns the Uniform Resource Identifier (URI) for `tokenId` token.* + +#### Parameters + +| Name | Type | Description | +|---|---|---| +| tokenId | uint256 | undefined | + +#### Returns + +| Name | Type | Description | +|---|---|---| +| _0 | string | undefined | + +### tokensOfOwner + +```solidity +function tokensOfOwner(address owner) external view returns (uint256[]) +``` + + + +*Returns an array of token IDs owned by `owner`. This function scans the ownership mapping and is O(`totalSupply`) in complexity. It is meant to be called off-chain. See {ERC721AQueryable-tokensOfOwnerIn} for splitting the scan into multiple smaller scans if the collection is large enough to cause an out-of-gas error (10K collections should be fine).* + +#### Parameters + +| Name | Type | Description | +|---|---|---| +| owner | address | undefined | + +#### Returns + +| Name | Type | Description | +|---|---|---| +| _0 | uint256[] | undefined | + +### tokensOfOwnerIn + +```solidity +function tokensOfOwnerIn(address owner, uint256 start, uint256 stop) external view returns (uint256[]) +``` + + + +*Returns an array of token IDs owned by `owner`, in the range [`start`, `stop`) (i.e. `start <= tokenId < stop`). This function allows for tokens to be queried if the collection grows too big for a single call of {ERC721AQueryable-tokensOfOwner}. Requirements: - `start < stop`* + +#### Parameters + +| Name | Type | Description | +|---|---|---| +| owner | address | undefined | +| start | uint256 | undefined | +| stop | uint256 | undefined | + +#### Returns + +| Name | Type | Description | +|---|---|---| +| _0 | uint256[] | undefined | + +### totalSupply + +```solidity +function totalSupply() external view returns (uint256) +``` + + + +*Returns the total number of tokens in existence. Burned tokens will reduce the count. To get the total number of tokens minted, please see {_totalMinted}.* + + +#### Returns + +| Name | Type | Description | +|---|---|---| +| _0 | uint256 | undefined | + +### transferFrom + +```solidity +function transferFrom(address from, address to, uint256 tokenId) external payable +``` + + + +*Transfers `tokenId` from `from` to `to`. WARNING: Usage of this method is discouraged, use {safeTransferFrom} whenever possible. Requirements: - `from` cannot be the zero address. - `to` cannot be the zero address. - `tokenId` token must be owned by `from`. - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}. Emits a {Transfer} event.* + +#### Parameters + +| Name | Type | Description | +|---|---|---| +| from | address | undefined | +| to | address | undefined | +| tokenId | uint256 | undefined | + + + +## Events + +### Approval + +```solidity +event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId) +``` + + + + + +#### Parameters + +| Name | Type | Description | +|---|---|---| +| owner `indexed` | address | undefined | +| approved `indexed` | address | undefined | +| tokenId `indexed` | uint256 | undefined | + +### ApprovalForAll + +```solidity +event ApprovalForAll(address indexed owner, address indexed operator, bool approved) +``` + + + + + +#### Parameters + +| Name | Type | Description | +|---|---|---| +| owner `indexed` | address | undefined | +| operator `indexed` | address | undefined | +| approved | bool | undefined | + +### ConsecutiveTransfer + +```solidity +event ConsecutiveTransfer(uint256 indexed fromTokenId, uint256 toTokenId, address indexed from, address indexed to) +``` + + + + + +#### Parameters + +| Name | Type | Description | +|---|---|---| +| fromTokenId `indexed` | uint256 | undefined | +| toTokenId | uint256 | undefined | +| from `indexed` | address | undefined | +| to `indexed` | address | undefined | + +### Transfer + +```solidity +event Transfer(address indexed from, address indexed to, uint256 indexed tokenId) +``` + + + + + +#### Parameters + +| Name | Type | Description | +|---|---|---| +| from `indexed` | address | undefined | +| to `indexed` | address | undefined | +| tokenId `indexed` | uint256 | undefined | + + + +## Errors + +### ApprovalCallerNotOwnerNorApproved + +```solidity +error ApprovalCallerNotOwnerNorApproved() +``` + +The caller must own the token or be an approved operator. + + + + +### ApprovalQueryForNonexistentToken + +```solidity +error ApprovalQueryForNonexistentToken() +``` + +The token does not exist. + + + + +### BalanceQueryForZeroAddress + +```solidity +error BalanceQueryForZeroAddress() +``` + +Cannot query the balance for the zero address. + + + + +### InvalidQueryRange + +```solidity +error InvalidQueryRange() +``` + +Invalid query range (`start` >= `stop`). + + + + +### MintERC2309QuantityExceedsLimit + +```solidity +error MintERC2309QuantityExceedsLimit() +``` + +The `quantity` minted with ERC2309 exceeds the safety limit. + + + + +### MintToZeroAddress + +```solidity +error MintToZeroAddress() +``` + +Cannot mint to the zero address. + + + + +### MintZeroQuantity + +```solidity +error MintZeroQuantity() +``` + +The quantity of tokens minted must be more than zero. + + + + +### OwnerQueryForNonexistentToken + +```solidity +error OwnerQueryForNonexistentToken() +``` + +The token does not exist. + + + + +### OwnershipNotInitializedForExtraData + +```solidity +error OwnershipNotInitializedForExtraData() +``` + +The `extraData` cannot be set on an unintialized ownership slot. + + + + +### TransferCallerNotOwnerNorApproved + +```solidity +error TransferCallerNotOwnerNorApproved() +``` + +The caller must own the token or be an approved operator. + + + + +### TransferFromIncorrectOwner + +```solidity +error TransferFromIncorrectOwner() +``` + +The token must be owned by `from`. + + + + +### TransferToNonERC721ReceiverImplementer + +```solidity +error TransferToNonERC721ReceiverImplementer() +``` + +Cannot safely transfer to a contract that does not implement the ERC721Receiver interface. + + + + +### TransferToZeroAddress + +```solidity +error TransferToZeroAddress() +``` + +Cannot transfer to the zero address. + + + + +### URIQueryForNonexistentToken + +```solidity +error URIQueryForNonexistentToken() +``` + +The token does not exist. + + + + + diff --git a/docs/OpenEditionERC721.md b/docs/OpenEditionERC721.md index 22a1d92ec..caaa5e09d 100644 --- a/docs/OpenEditionERC721.md +++ b/docs/OpenEditionERC721.md @@ -30,7 +30,7 @@ function DEFAULT_ADMIN_ROLE() external view returns (bytes32) ### approve ```solidity -function approve(address operator, uint256 tokenId) external nonpayable +function approve(address operator, uint256 tokenId) external payable ``` @@ -52,7 +52,7 @@ function balanceOf(address owner) external view returns (uint256) -*See {IERC721-balanceOf}.* +*Returns the number of tokens in `owner`'s account.* #### Parameters @@ -138,6 +138,28 @@ Returns the contract metadata URI. |---|---|---| | _0 | string | undefined | +### explicitOwnershipOf + +```solidity +function explicitOwnershipOf(uint256 tokenId) external view returns (struct IERC721AUpgradeable.TokenOwnership ownership) +``` + + + +*Returns the `TokenOwnership` struct at `tokenId` without reverting. If the `tokenId` is out of bounds: - `addr = address(0)` - `startTimestamp = 0` - `burned = false` - `extraData = 0` If the `tokenId` is burned: - `addr = <Address of owner before token was burned>` - `startTimestamp = <Timestamp when token was burned>` - `burned = true` - `extraData = <Extra data when token was burned>` Otherwise: - `addr = <Address of owner>` - `startTimestamp = <Timestamp of start of ownership>` - `burned = false` - `extraData = <Extra data at start of ownership>`* + +#### Parameters + +| Name | Type | Description | +|---|---|---| +| tokenId | uint256 | undefined | + +#### Returns + +| Name | Type | Description | +|---|---|---| +| ownership | IERC721AUpgradeable.TokenOwnership | undefined | + ### getActiveClaimConditionId ```solidity @@ -163,7 +185,7 @@ function getApproved(uint256 tokenId) external view returns (address) -*See {IERC721-getApproved}.* +*Returns the account approved for `tokenId` token. Requirements: - `tokenId` must exist.* #### Parameters @@ -217,59 +239,6 @@ Returns the defualt royalty recipient and BPS for this contract's NFTs. | _0 | address | undefined | | _1 | uint16 | undefined | -### getFlatPlatformFeeInfo - -```solidity -function getFlatPlatformFeeInfo() external view returns (address, uint256) -``` - - - -*Returns the platform fee bps and recipient.* - - -#### Returns - -| Name | Type | Description | -|---|---|---| -| _0 | address | undefined | -| _1 | uint256 | undefined | - -### getPlatformFeeInfo - -```solidity -function getPlatformFeeInfo() external view returns (address, uint16) -``` - - - -*Returns the platform fee recipient and bps.* - - -#### Returns - -| Name | Type | Description | -|---|---|---| -| _0 | address | undefined | -| _1 | uint16 | undefined | - -### getPlatformFeeType - -```solidity -function getPlatformFeeType() external view returns (enum IPlatformFee.PlatformFeeType) -``` - - - -*Returns the platform fee bps and recipient.* - - -#### Returns - -| Name | Type | Description | -|---|---|---| -| _0 | enum IPlatformFee.PlatformFeeType | undefined | - ### getRoleAdmin ```solidity @@ -449,7 +418,7 @@ Checks whether an account has a particular role; role restricti ### initialize ```solidity -function initialize(address _defaultAdmin, string _name, string _symbol, string _contractURI, address[] _trustedForwarders, address _saleRecipient, address _royaltyRecipient, uint128 _royaltyBps, uint128 _platformFeeBps, address _platformFeeRecipient) external nonpayable +function initialize(address _defaultAdmin, string _name, string _symbol, string _contractURI, address[] _trustedForwarders, address _saleRecipient, address _royaltyRecipient, uint128 _royaltyBps) external nonpayable ``` @@ -468,8 +437,6 @@ function initialize(address _defaultAdmin, string _name, string _symbol, string | _saleRecipient | address | undefined | | _royaltyRecipient | address | undefined | | _royaltyBps | uint128 | undefined | -| _platformFeeBps | uint128 | undefined | -| _platformFeeRecipient | address | undefined | ### isApprovedForAll @@ -479,7 +446,7 @@ function isApprovedForAll(address owner, address operator) external view returns -*See {IERC721-isApprovedForAll}.* +*Returns if the `operator` is allowed to manage all of the assets of `owner`. See {setApprovalForAll}.* #### Parameters @@ -546,7 +513,7 @@ function name() external view returns (string) -*See {IERC721Metadata-name}.* +*Returns the token collection name.* #### Returns @@ -631,7 +598,7 @@ function ownerOf(uint256 tokenId) external view returns (address) -*See {IERC721-ownerOf}.* +*Returns the owner of the `tokenId` token. Requirements: - `tokenId` must exist.* #### Parameters @@ -723,7 +690,7 @@ View royalty info for a given token and sale price. ### safeTransferFrom ```solidity -function safeTransferFrom(address from, address to, uint256 tokenId) external nonpayable +function safeTransferFrom(address from, address to, uint256 tokenId) external payable ``` @@ -741,7 +708,7 @@ function safeTransferFrom(address from, address to, uint256 tokenId) external no ### safeTransferFrom ```solidity -function safeTransferFrom(address from, address to, uint256 tokenId, bytes data) external nonpayable +function safeTransferFrom(address from, address to, uint256 tokenId, bytes data) external payable ``` @@ -824,23 +791,6 @@ Updates default royalty recipient and bps. | _royaltyRecipient | address | Address to be set as default royalty recipient. | | _royaltyBps | uint256 | Updated royalty bps. | -### setFlatPlatformFeeInfo - -```solidity -function setFlatPlatformFeeInfo(address _platformFeeRecipient, uint256 _flatFee) external nonpayable -``` - -Lets a module admin set a flat fee on primary sales. - - - -#### Parameters - -| Name | Type | Description | -|---|---|---| -| _platformFeeRecipient | address | undefined | -| _flatFee | uint256 | undefined | - ### setOperatorRestriction ```solidity @@ -873,39 +823,6 @@ Lets an authorized wallet set a new owner for the contract. |---|---|---| | _newOwner | address | The address to set as the new owner of the contract. | -### setPlatformFeeInfo - -```solidity -function setPlatformFeeInfo(address _platformFeeRecipient, uint256 _platformFeeBps) external nonpayable -``` - -Updates the platform fee recipient and bps. - -*Caller should be authorized to set platform fee info. See {_canSetPlatformFeeInfo}. Emits {PlatformFeeInfoUpdated Event}; See {_setupPlatformFeeInfo}.* - -#### Parameters - -| Name | Type | Description | -|---|---|---| -| _platformFeeRecipient | address | Address to be set as new platformFeeRecipient. | -| _platformFeeBps | uint256 | Updated platformFeeBps. | - -### setPlatformFeeType - -```solidity -function setPlatformFeeType(enum IPlatformFee.PlatformFeeType _feeType) external nonpayable -``` - -Lets a module admin set platform fee type. - - - -#### Parameters - -| Name | Type | Description | -|---|---|---| -| _feeType | enum IPlatformFee.PlatformFeeType | undefined | - ### setPrimarySaleRecipient ```solidity @@ -1039,7 +956,7 @@ function symbol() external view returns (string) -*See {IERC721Metadata-symbol}.* +*Returns the token collection symbol.* #### Returns @@ -1070,6 +987,52 @@ function tokenURI(uint256 _tokenId) external view returns (string) |---|---|---| | _0 | string | undefined | +### tokensOfOwner + +```solidity +function tokensOfOwner(address owner) external view returns (uint256[]) +``` + + + +*Returns an array of token IDs owned by `owner`. This function scans the ownership mapping and is O(`totalSupply`) in complexity. It is meant to be called off-chain. See {ERC721AQueryable-tokensOfOwnerIn} for splitting the scan into multiple smaller scans if the collection is large enough to cause an out-of-gas error (10K collections should be fine).* + +#### Parameters + +| Name | Type | Description | +|---|---|---| +| owner | address | undefined | + +#### Returns + +| Name | Type | Description | +|---|---|---| +| _0 | uint256[] | undefined | + +### tokensOfOwnerIn + +```solidity +function tokensOfOwnerIn(address owner, uint256 start, uint256 stop) external view returns (uint256[]) +``` + + + +*Returns an array of token IDs owned by `owner`, in the range [`start`, `stop`) (i.e. `start <= tokenId < stop`). This function allows for tokens to be queried if the collection grows too big for a single call of {ERC721AQueryable-tokensOfOwner}. Requirements: - `start < stop`* + +#### Parameters + +| Name | Type | Description | +|---|---|---| +| owner | address | undefined | +| start | uint256 | undefined | +| stop | uint256 | undefined | + +#### Returns + +| Name | Type | Description | +|---|---|---| +| _0 | uint256[] | undefined | + ### totalMinted ```solidity @@ -1095,7 +1058,7 @@ function totalSupply() external view returns (uint256) -*Burned tokens are calculated here, use _totalMinted() if you want to count just minted tokens.* +*Returns the total number of tokens in existence. Burned tokens will reduce the count. To get the total number of tokens minted, please see {_totalMinted}.* #### Returns @@ -1107,7 +1070,7 @@ function totalSupply() external view returns (uint256) ### transferFrom ```solidity -function transferFrom(address from, address to, uint256 tokenId) external nonpayable +function transferFrom(address from, address to, uint256 tokenId) external payable ``` @@ -1206,10 +1169,10 @@ Emitted when the contract's claim conditions are updated. | claimConditions | IClaimCondition.ClaimCondition[] | undefined | | resetEligibility | bool | undefined | -### ContractURIUpdated +### ConsecutiveTransfer ```solidity -event ContractURIUpdated(string prevURI, string newURI) +event ConsecutiveTransfer(uint256 indexed fromTokenId, uint256 toTokenId, address indexed from, address indexed to) ``` @@ -1220,13 +1183,15 @@ event ContractURIUpdated(string prevURI, string newURI) | Name | Type | Description | |---|---|---| -| prevURI | string | undefined | -| newURI | string | undefined | +| fromTokenId `indexed` | uint256 | undefined | +| toTokenId | uint256 | undefined | +| from `indexed` | address | undefined | +| to `indexed` | address | undefined | -### DefaultRoyalty +### ContractURIUpdated ```solidity -event DefaultRoyalty(address indexed newRoyaltyRecipient, uint256 newRoyaltyBps) +event ContractURIUpdated(string prevURI, string newURI) ``` @@ -1237,13 +1202,13 @@ event DefaultRoyalty(address indexed newRoyaltyRecipient, uint256 newRoyaltyBps) | Name | Type | Description | |---|---|---| -| newRoyaltyRecipient `indexed` | address | undefined | -| newRoyaltyBps | uint256 | undefined | +| prevURI | string | undefined | +| newURI | string | undefined | -### FlatPlatformFeeUpdated +### DefaultRoyalty ```solidity -event FlatPlatformFeeUpdated(address platformFeeRecipient, uint256 flatFee) +event DefaultRoyalty(address indexed newRoyaltyRecipient, uint256 newRoyaltyBps) ``` @@ -1254,8 +1219,8 @@ event FlatPlatformFeeUpdated(address platformFeeRecipient, uint256 flatFee) | Name | Type | Description | |---|---|---| -| platformFeeRecipient | address | undefined | -| flatFee | uint256 | undefined | +| newRoyaltyRecipient `indexed` | address | undefined | +| newRoyaltyBps | uint256 | undefined | ### Initialized @@ -1306,39 +1271,6 @@ event OwnerUpdated(address indexed prevOwner, address indexed newOwner) | prevOwner `indexed` | address | undefined | | newOwner `indexed` | address | undefined | -### PlatformFeeInfoUpdated - -```solidity -event PlatformFeeInfoUpdated(address indexed platformFeeRecipient, uint256 platformFeeBps) -``` - - - - - -#### Parameters - -| Name | Type | Description | -|---|---|---| -| platformFeeRecipient `indexed` | address | undefined | -| platformFeeBps | uint256 | undefined | - -### PlatformFeeTypeUpdated - -```solidity -event PlatformFeeTypeUpdated(enum IPlatformFee.PlatformFeeType feeType) -``` - - - - - -#### Parameters - -| Name | Type | Description | -|---|---|---| -| feeType | enum IPlatformFee.PlatformFeeType | undefined | - ### PrimarySaleRecipientUpdated ```solidity @@ -1510,35 +1442,35 @@ The token does not exist. -### ApprovalToCurrentOwner +### BalanceQueryForZeroAddress ```solidity -error ApprovalToCurrentOwner() +error BalanceQueryForZeroAddress() ``` -The caller cannot approve to the current owner. +Cannot query the balance for the zero address. -### ApproveToCaller +### InvalidQueryRange ```solidity -error ApproveToCaller() +error InvalidQueryRange() ``` -The caller cannot approve to their own address. +Invalid query range (`start` >= `stop`). -### BalanceQueryForZeroAddress +### MintERC2309QuantityExceedsLimit ```solidity -error BalanceQueryForZeroAddress() +error MintERC2309QuantityExceedsLimit() ``` -Cannot query the balance for the zero address. +The `quantity` minted with ERC2309 exceeds the safety limit. @@ -1592,6 +1524,17 @@ The token does not exist. +### OwnershipNotInitializedForExtraData + +```solidity +error OwnershipNotInitializedForExtraData() +``` + +The `extraData` cannot be set on an unintialized ownership slot. + + + + ### TransferCallerNotOwnerNorApproved ```solidity diff --git a/docs/TokenBoundAccount.md b/docs/TokenBoundAccount.md new file mode 100644 index 000000000..79bab9889 --- /dev/null +++ b/docs/TokenBoundAccount.md @@ -0,0 +1,746 @@ +# TokenBoundAccount + + + + + + + + + +## Methods + +### addDeposit + +```solidity +function addDeposit() external payable +``` + +Deposit funds for this account in Entrypoint. + + + + +### contractURI + +```solidity +function contractURI() external view returns (string) +``` + +Returns the contract metadata URI. + + + + +#### Returns + +| Name | Type | Description | +|---|---|---| +| _0 | string | undefined | + +### entryPoint + +```solidity +function entryPoint() external view returns (contract IEntryPoint) +``` + +Returns the EIP 4337 entrypoint contract. + + + + +#### Returns + +| Name | Type | Description | +|---|---|---| +| _0 | contract IEntryPoint | undefined | + +### execute + +```solidity +function execute(address _target, uint256 _value, bytes _calldata) external nonpayable +``` + +Executes a transaction (called directly from an admin, or by entryPoint) + + + +#### Parameters + +| Name | Type | Description | +|---|---|---| +| _target | address | undefined | +| _value | uint256 | undefined | +| _calldata | bytes | undefined | + +### executeBatch + +```solidity +function executeBatch(address[] _target, uint256[] _value, bytes[] _calldata) external nonpayable +``` + +Executes a sequence transaction (called directly from an admin, or by entryPoint) + + + +#### Parameters + +| Name | Type | Description | +|---|---|---| +| _target | address[] | undefined | +| _value | uint256[] | undefined | +| _calldata | bytes[] | undefined | + +### executeCall + +```solidity +function executeCall(address to, uint256 value, bytes data) external payable returns (bytes result) +``` + + + + + +#### Parameters + +| Name | Type | Description | +|---|---|---| +| to | address | undefined | +| value | uint256 | undefined | +| data | bytes | undefined | + +#### Returns + +| Name | Type | Description | +|---|---|---| +| result | bytes | undefined | + +### factory + +```solidity +function factory() external view returns (address) +``` + +EIP 4337 factory for this contract. + + + + +#### Returns + +| Name | Type | Description | +|---|---|---| +| _0 | address | undefined | + +### getAllActiveSigners + +```solidity +function getAllActiveSigners() external view returns (struct IAccountPermissions.SignerPermissions[] signers) +``` + +Returns all signers with active permissions to use the account. + + + + +#### Returns + +| Name | Type | Description | +|---|---|---| +| signers | IAccountPermissions.SignerPermissions[] | undefined | + +### getAllAdmins + +```solidity +function getAllAdmins() external view returns (address[]) +``` + +Returns all admins of the account. + + + + +#### Returns + +| Name | Type | Description | +|---|---|---| +| _0 | address[] | undefined | + +### getAllSigners + +```solidity +function getAllSigners() external view returns (struct IAccountPermissions.SignerPermissions[] signers) +``` + +Returns all active and inactive signers of the account. + + + + +#### Returns + +| Name | Type | Description | +|---|---|---| +| signers | IAccountPermissions.SignerPermissions[] | undefined | + +### getDeposit + +```solidity +function getDeposit() external view returns (uint256) +``` + +Returns the balance of the account in Entrypoint. + + + + +#### Returns + +| Name | Type | Description | +|---|---|---| +| _0 | uint256 | undefined | + +### getNonce + +```solidity +function getNonce() external view returns (uint256) +``` + +Return the account nonce. This method returns the next sequential nonce. For a nonce of a specific key, use `entrypoint.getNonce(account, key)` + + + + +#### Returns + +| Name | Type | Description | +|---|---|---| +| _0 | uint256 | undefined | + +### getPermissionsForSigner + +```solidity +function getPermissionsForSigner(address signer) external view returns (struct IAccountPermissions.SignerPermissions) +``` + +Returns the restrictions under which a signer can use the smart wallet. + + + +#### Parameters + +| Name | Type | Description | +|---|---|---| +| signer | address | undefined | + +#### Returns + +| Name | Type | Description | +|---|---|---| +| _0 | IAccountPermissions.SignerPermissions | undefined | + +### initialize + +```solidity +function initialize(address _defaultAdmin, bytes) external nonpayable +``` + +Initializes the smart contract wallet. + + + +#### Parameters + +| Name | Type | Description | +|---|---|---| +| _defaultAdmin | address | undefined | +| _1 | bytes | undefined | + +### isActiveSigner + +```solidity +function isActiveSigner(address signer) external view returns (bool) +``` + +Returns whether the given account is an active signer on the account. + + + +#### Parameters + +| Name | Type | Description | +|---|---|---| +| signer | address | undefined | + +#### Returns + +| Name | Type | Description | +|---|---|---| +| _0 | bool | undefined | + +### isAdmin + +```solidity +function isAdmin(address _account) external view returns (bool) +``` + +Returns whether the given account is an admin. + + + +#### Parameters + +| Name | Type | Description | +|---|---|---| +| _account | address | undefined | + +#### Returns + +| Name | Type | Description | +|---|---|---| +| _0 | bool | undefined | + +### isValidSignature + +```solidity +function isValidSignature(bytes32 _hash, bytes _signature) external view returns (bytes4 magicValue) +``` + +See EIP-1271 + + + +#### Parameters + +| Name | Type | Description | +|---|---|---| +| _hash | bytes32 | undefined | +| _signature | bytes | undefined | + +#### Returns + +| Name | Type | Description | +|---|---|---| +| magicValue | bytes4 | undefined | + +### isValidSigner + +```solidity +function isValidSigner(address _signer, UserOperation) external view returns (bool) +``` + + + + + +#### Parameters + +| Name | Type | Description | +|---|---|---| +| _signer | address | undefined | +| _1 | UserOperation | undefined | + +#### Returns + +| Name | Type | Description | +|---|---|---| +| _0 | bool | undefined | + +### multicall + +```solidity +function multicall(bytes[] data) external nonpayable returns (bytes[] results) +``` + +Receives and executes a batch of function calls on this contract. + +*Receives and executes a batch of function calls on this contract.* + +#### Parameters + +| Name | Type | Description | +|---|---|---| +| data | bytes[] | The bytes data that makes up the batch of function calls to execute. | + +#### Returns + +| Name | Type | Description | +|---|---|---| +| results | bytes[] | The bytes data that makes up the result of the batch of function calls executed. | + +### nonce + +```solidity +function nonce() external view returns (uint256) +``` + + + + + + +#### Returns + +| Name | Type | Description | +|---|---|---| +| _0 | uint256 | undefined | + +### onERC1155BatchReceived + +```solidity +function onERC1155BatchReceived(address, address, uint256[], uint256[], bytes) external nonpayable returns (bytes4) +``` + + + + + +#### Parameters + +| Name | Type | Description | +|---|---|---| +| _0 | address | undefined | +| _1 | address | undefined | +| _2 | uint256[] | undefined | +| _3 | uint256[] | undefined | +| _4 | bytes | undefined | + +#### Returns + +| Name | Type | Description | +|---|---|---| +| _0 | bytes4 | undefined | + +### onERC1155Received + +```solidity +function onERC1155Received(address, address, uint256, uint256, bytes) external nonpayable returns (bytes4) +``` + + + + + +#### Parameters + +| Name | Type | Description | +|---|---|---| +| _0 | address | undefined | +| _1 | address | undefined | +| _2 | uint256 | undefined | +| _3 | uint256 | undefined | +| _4 | bytes | undefined | + +#### Returns + +| Name | Type | Description | +|---|---|---| +| _0 | bytes4 | undefined | + +### onERC721Received + +```solidity +function onERC721Received(address, address, uint256, bytes) external nonpayable returns (bytes4) +``` + + + +*See {IERC721Receiver-onERC721Received}. Always returns `IERC721Receiver.onERC721Received.selector`.* + +#### Parameters + +| Name | Type | Description | +|---|---|---| +| _0 | address | undefined | +| _1 | address | undefined | +| _2 | uint256 | undefined | +| _3 | bytes | undefined | + +#### Returns + +| Name | Type | Description | +|---|---|---| +| _0 | bytes4 | undefined | + +### owner + +```solidity +function owner() external view returns (address) +``` + + + + + + +#### Returns + +| Name | Type | Description | +|---|---|---| +| _0 | address | undefined | + +### setAdmin + +```solidity +function setAdmin(address _account, bool _isAdmin) external nonpayable +``` + +Adds / removes an account as an admin. + + + +#### Parameters + +| Name | Type | Description | +|---|---|---| +| _account | address | undefined | +| _isAdmin | bool | undefined | + +### setContractURI + +```solidity +function setContractURI(string _uri) external nonpayable +``` + +Lets a contract admin set the URI for contract-level metadata. + +*Caller should be authorized to setup contractURI, e.g. contract admin. See {_canSetContractURI}. Emits {ContractURIUpdated Event}.* + +#### Parameters + +| Name | Type | Description | +|---|---|---| +| _uri | string | keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE") | + +### setPermissionsForSigner + +```solidity +function setPermissionsForSigner(IAccountPermissions.SignerPermissionRequest _req, bytes _signature) external nonpayable +``` + + + + + +#### Parameters + +| Name | Type | Description | +|---|---|---| +| _req | IAccountPermissions.SignerPermissionRequest | undefined | +| _signature | bytes | undefined | + +### supportsInterface + +```solidity +function supportsInterface(bytes4 interfaceId) external view returns (bool) +``` + +See {IERC165-supportsInterface}. + + + +#### Parameters + +| Name | Type | Description | +|---|---|---| +| interfaceId | bytes4 | undefined | + +#### Returns + +| Name | Type | Description | +|---|---|---| +| _0 | bool | undefined | + +### token + +```solidity +function token() external view returns (uint256 chainId, address tokenContract, uint256 tokenId) +``` + + + + + + +#### Returns + +| Name | Type | Description | +|---|---|---| +| chainId | uint256 | undefined | +| tokenContract | address | undefined | +| tokenId | uint256 | undefined | + +### validateUserOp + +```solidity +function validateUserOp(UserOperation userOp, bytes32 userOpHash, uint256 missingAccountFunds) external nonpayable returns (uint256 validationData) +``` + + + + + +#### Parameters + +| Name | Type | Description | +|---|---|---| +| userOp | UserOperation | undefined | +| userOpHash | bytes32 | undefined | +| missingAccountFunds | uint256 | undefined | + +#### Returns + +| Name | Type | Description | +|---|---|---| +| validationData | uint256 | undefined | + +### verifySignerPermissionRequest + +```solidity +function verifySignerPermissionRequest(IAccountPermissions.SignerPermissionRequest req, bytes signature) external view returns (bool success, address signer) +``` + + + + + +#### Parameters + +| Name | Type | Description | +|---|---|---| +| req | IAccountPermissions.SignerPermissionRequest | undefined | +| signature | bytes | undefined | + +#### Returns + +| Name | Type | Description | +|---|---|---| +| success | bool | undefined | +| signer | address | undefined | + +### withdrawDepositTo + +```solidity +function withdrawDepositTo(address payable withdrawAddress, uint256 amount) external nonpayable +``` + +Withdraw funds for this account from Entrypoint. + + + +#### Parameters + +| Name | Type | Description | +|---|---|---| +| withdrawAddress | address payable | undefined | +| amount | uint256 | undefined | + + + +## Events + +### AdminUpdated + +```solidity +event AdminUpdated(address indexed signer, bool isAdmin) +``` + +Emitted when an admin is set or removed. + + + +#### Parameters + +| Name | Type | Description | +|---|---|---| +| signer `indexed` | address | undefined | +| isAdmin | bool | undefined | + +### ContractURIUpdated + +```solidity +event ContractURIUpdated(string prevURI, string newURI) +``` + + + + + +#### Parameters + +| Name | Type | Description | +|---|---|---| +| prevURI | string | undefined | +| newURI | string | undefined | + +### Initialized + +```solidity +event Initialized(uint8 version) +``` + + + + + +#### Parameters + +| Name | Type | Description | +|---|---|---| +| version | uint8 | undefined | + +### SignerPermissionsUpdated + +```solidity +event SignerPermissionsUpdated(address indexed authorizingSigner, address indexed targetSigner, IAccountPermissions.SignerPermissionRequest permissions) +``` + +Emitted when permissions for a signer are updated. + + + +#### Parameters + +| Name | Type | Description | +|---|---|---| +| authorizingSigner `indexed` | address | undefined | +| targetSigner `indexed` | address | undefined | +| permissions | IAccountPermissions.SignerPermissionRequest | undefined | + +### TokenBoundAccountCreated + +```solidity +event TokenBoundAccountCreated(address indexed account, bytes indexed data) +``` + + + + + +#### Parameters + +| Name | Type | Description | +|---|---|---| +| account `indexed` | address | undefined | +| data `indexed` | bytes | undefined | + +### TransactionExecuted + +```solidity +event TransactionExecuted(address indexed target, uint256 indexed value, bytes data) +``` + + + + + +#### Parameters + +| Name | Type | Description | +|---|---|---| +| target `indexed` | address | undefined | +| value `indexed` | uint256 | undefined | +| data | bytes | undefined | + + + diff --git a/gasreport.txt b/gasreport.txt index f61d596c2..62dca4240 100644 --- a/gasreport.txt +++ b/gasreport.txt @@ -1,96 +1,96 @@ No files changed, compilation skipped Running 3 tests for src/test/benchmark/EditionStakeBenchmark.t.sol:EditionStakeBenchmarkTest -[PASS] test_benchmark_editionStake_claimRewards() (gas: 67222) -[PASS] test_benchmark_editionStake_stake() (gas: 214264) -[PASS] test_benchmark_editionStake_withdraw() (gas: 49437) -Test result: ok. 3 passed; 0 failed; finished in 1.08s +[PASS] test_benchmark_editionStake_claimRewards() (gas: 65580) +[PASS] test_benchmark_editionStake_stake() (gas: 185145) +[PASS] test_benchmark_editionStake_withdraw() (gas: 46326) +Test result: ok. 3 passed; 0 failed; finished in 468.09ms Running 5 tests for src/test/benchmark/SignatureDropBenchmark.t.sol:SignatureDropBenchmarkTest -[PASS] test_bechmark_signatureDrop_claim_five_tokens() (gas: 141434) -[PASS] test_bechmark_signatureDrop_setClaimConditions() (gas: 73754) -[PASS] test_benchmark_signatureDrop_lazyMint() (gas: 124617) -[PASS] test_benchmark_signatureDrop_lazyMint_for_delayed_reveal() (gas: 226206) -[PASS] test_benchmark_signatureDrop_reveal() (gas: 9223372036854754743) -Test result: ok. 5 passed; 0 failed; finished in 1.09s +[PASS] test_bechmark_signatureDrop_claim_five_tokens() (gas: 141456) +[PASS] test_bechmark_signatureDrop_setClaimConditions() (gas: 73774) +[PASS] test_benchmark_signatureDrop_lazyMint() (gas: 124639) +[PASS] test_benchmark_signatureDrop_lazyMint_for_delayed_reveal() (gas: 226228) +[PASS] test_benchmark_signatureDrop_reveal() (gas: 9183) +Test result: ok. 5 passed; 0 failed; finished in 468.49ms Running 3 tests for src/test/benchmark/NFTStakeBenchmark.t.sol:NFTStakeBenchmarkTest -[PASS] test_benchmark_nftStake_claimRewards() (gas: 66461) -[PASS] test_benchmark_nftStake_stake_five_tokens() (gas: 617427) -[PASS] test_benchmark_nftStake_withdraw() (gas: 9223372036854754743) -Test result: ok. 3 passed; 0 failed; finished in 1.09s +[PASS] test_benchmark_nftStake_claimRewards() (gas: 68826) +[PASS] test_benchmark_nftStake_stake_five_tokens() (gas: 549421) +[PASS] test_benchmark_nftStake_withdraw() (gas: 39648) +Test result: ok. 3 passed; 0 failed; finished in 468.46ms + +Running 4 tests for src/test/benchmark/TokenERC1155Benchmark.t.sol:TokenERC1155BenchmarkTest +[PASS] test_benchmark_tokenERC1155_burn() (gas: 5744) +[PASS] test_benchmark_tokenERC1155_mintTo() (gas: 121028) +[PASS] test_benchmark_tokenERC1155_mintWithSignature_pay_with_ERC20() (gas: 263757) +[PASS] test_benchmark_tokenERC1155_mintWithSignature_pay_with_native_token() (gas: 292255) +Test result: ok. 4 passed; 0 failed; finished in 469.08ms Running 3 tests for src/test/benchmark/PackBenchmark.t.sol:PackBenchmarkTest -[PASS] test_benchmark_pack_addPackContents() (gas: 219427) -[PASS] test_benchmark_pack_createPack() (gas: 1416215) -[PASS] test_benchmark_pack_openPack() (gas: 170258) -Test result: ok. 3 passed; 0 failed; finished in 1.09s +[PASS] test_benchmark_pack_addPackContents() (gas: 219449) +[PASS] test_benchmark_pack_createPack() (gas: 1425232) +[PASS] test_benchmark_pack_openPack() (gas: 170278) +Test result: ok. 3 passed; 0 failed; finished in 469.74ms Running 2 tests for src/test/benchmark/MultiwrapBenchmark.t.sol:MultiwrapBenchmarkTest -[PASS] test_benchmark_multiwrap_unwrap() (gas: 92386) -[PASS] test_benchmark_multiwrap_wrap() (gas: 475292) -Test result: ok. 2 passed; 0 failed; finished in 1.10s - -Running 4 tests for src/test/benchmark/TokenERC1155Benchmark.t.sol:TokenERC1155BenchmarkTest -[PASS] test_benchmark_tokenERC1155_burn() (gas: 5728) -[PASS] test_benchmark_tokenERC1155_mintTo() (gas: 121006) -[PASS] test_benchmark_tokenERC1155_mintWithSignature_pay_with_ERC20() (gas: 263735) -[PASS] test_benchmark_tokenERC1155_mintWithSignature_pay_with_native_token() (gas: 292233) -Test result: ok. 4 passed; 0 failed; finished in 1.11s +[PASS] test_benchmark_multiwrap_unwrap() (gas: 92404) +[PASS] test_benchmark_multiwrap_wrap() (gas: 475314) +Test result: ok. 2 passed; 0 failed; finished in 476.27ms Running 1 test for src/test/benchmark/AirdropERC20Benchmark.t.sol:AirdropERC20BenchmarkTest -[PASS] test_benchmark_airdropERC20_airdrop() (gas: 32179335) -Test result: ok. 1 passed; 0 failed; finished in 1.11s +[PASS] test_benchmark_airdropERC20_airdrop() (gas: 32173710) +Test result: ok. 1 passed; 0 failed; finished in 491.93ms Running 1 test for src/test/benchmark/AirdropERC1155Benchmark.t.sol:AirdropERC1155BenchmarkTest -[PASS] test_benchmark_airdropERC1155_airdrop() (gas: 38082296) -Test result: ok. 1 passed; 0 failed; finished in 1.13s +[PASS] test_benchmark_airdropERC1155_airdrop() (gas: 38084693) +Test result: ok. 1 passed; 0 failed; finished in 492.70ms Running 1 test for src/test/benchmark/AirdropERC721Benchmark.t.sol:AirdropERC721BenchmarkTest -[PASS] test_benchmark_airdropERC721_airdrop() (gas: 43896326) -Test result: ok. 1 passed; 0 failed; finished in 1.15s +[PASS] test_benchmark_airdropERC721_airdrop() (gas: 43898701) +Test result: ok. 1 passed; 0 failed; finished in 498.05ms -Running 3 tests for src/test/benchmark/PackVRFDirectBenchmark.t.sol:PackVRFDirectBenchmarkTest -[PASS] test_benchmark_packvrf_createPack() (gas: 1391844) -[PASS] test_benchmark_packvrf_openPack() (gas: 119961) -[PASS] test_benchmark_packvrf_openPackAndClaimRewards() (gas: 140350) -Test result: ok. 3 passed; 0 failed; finished in 162.19ms +Running 3 tests for src/test/benchmark/TokenERC20Benchmark.t.sol:TokenERC20BenchmarkTest +[PASS] test_benchmark_tokenERC20_mintTo() (gas: 118533) +[PASS] test_benchmark_tokenERC20_mintWithSignature_pay_with_ERC20() (gas: 181738) +[PASS] test_benchmark_tokenERC20_mintWithSignature_pay_with_native_token() (gas: 206150) +Test result: ok. 3 passed; 0 failed; finished in 145.19ms Running 3 tests for src/test/benchmark/TokenStakeBenchmark.t.sol:TokenStakeBenchmarkTest -[PASS] test_benchmark_tokenStake_claimRewards() (gas: 73676) -[PASS] test_benchmark_tokenStake_stake() (gas: 181762) -[PASS] test_benchmark_tokenStake_withdraw() (gas: 55520) -Test result: ok. 3 passed; 0 failed; finished in 148.05ms +[PASS] test_benchmark_tokenStake_claimRewards() (gas: 68065) +[PASS] test_benchmark_tokenStake_stake() (gas: 178011) +[PASS] test_benchmark_tokenStake_withdraw() (gas: 47890) +Test result: ok. 3 passed; 0 failed; finished in 147.70ms -Running 4 tests for src/test/benchmark/TokenERC721Benchmark.t.sol:TokenERC721BenchmarkTest -[PASS] test_benchmark_tokenERC721_burn() (gas: 10393) -[PASS] test_benchmark_tokenERC721_mintTo() (gas: 149955) -[PASS] test_benchmark_tokenERC721_mintWithSignature_pay_with_ERC20() (gas: 259360) -[PASS] test_benchmark_tokenERC721_mintWithSignature_pay_with_native_token() (gas: 283702) -Test result: ok. 4 passed; 0 failed; finished in 157.71ms +Running 3 tests for src/test/benchmark/PackVRFDirectBenchmark.t.sol:PackVRFDirectBenchmarkTest +[PASS] test_benchmark_packvrf_createPack() (gas: 1391866) +[PASS] test_benchmark_packvrf_openPack() (gas: 119981) +[PASS] test_benchmark_packvrf_openPackAndClaimRewards() (gas: 3621) +Test result: ok. 3 passed; 0 failed; finished in 153.05ms -Running 3 tests for src/test/benchmark/TokenERC20Benchmark.t.sol:TokenERC20BenchmarkTest -[PASS] test_benchmark_tokenERC20_mintTo() (gas: 118511) -[PASS] test_benchmark_tokenERC20_mintWithSignature_pay_with_ERC20() (gas: 181716) -[PASS] test_benchmark_tokenERC20_mintWithSignature_pay_with_native_token() (gas: 206128) -Test result: ok. 3 passed; 0 failed; finished in 159.72ms +Running 4 tests for src/test/benchmark/TokenERC721Benchmark.t.sol:TokenERC721BenchmarkTest +[PASS] test_benchmark_tokenERC721_burn() (gas: 10411) +[PASS] test_benchmark_tokenERC721_mintTo() (gas: 149977) +[PASS] test_benchmark_tokenERC721_mintWithSignature_pay_with_ERC20() (gas: 259382) +[PASS] test_benchmark_tokenERC721_mintWithSignature_pay_with_native_token() (gas: 283724) +Test result: ok. 4 passed; 0 failed; finished in 160.72ms Running 3 tests for src/test/benchmark/DropERC1155Benchmark.t.sol:DropERC1155BenchmarkTest -[PASS] test_bechmark_dropERC1155_claim() (gas: 186646) -[PASS] test_bechmark_dropERC1155_setClaimConditions_five_conditions() (gas: 492311) -[PASS] test_benchmark_dropERC1155_lazyMint() (gas: 123851) -Test result: ok. 3 passed; 0 failed; finished in 1.37s - -Running 2 tests for src/test/benchmark/DropERC20Benchmark.t.sol:DropERC20BenchmarkTest -[PASS] test_bechmark_dropERC20_claim() (gas: 230419) -[PASS] test_bechmark_dropERC20_setClaimConditions_five_conditions() (gas: 501025) -Test result: ok. 2 passed; 0 failed; finished in 387.05ms +[PASS] test_bechmark_dropERC1155_claim() (gas: 186668) +[PASS] test_bechmark_dropERC1155_setClaimConditions_five_conditions() (gas: 492331) +[PASS] test_benchmark_dropERC1155_lazyMint() (gas: 123873) +Test result: ok. 3 passed; 0 failed; finished in 758.20ms Running 5 tests for src/test/benchmark/DropERC721Benchmark.t.sol:DropERC721BenchmarkTest -[PASS] test_bechmark_dropERC721_claim_five_tokens() (gas: 211945) -[PASS] test_bechmark_dropERC721_setClaimConditions_five_conditions() (gas: 500839) -[PASS] test_benchmark_dropERC721_lazyMint() (gas: 124522) -[PASS] test_benchmark_dropERC721_lazyMint_for_delayed_reveal() (gas: 217107) -[PASS] test_benchmark_dropERC721_reveal() (gas: 9223372036854754743) -Test result: ok. 5 passed; 0 failed; finished in 578.70ms +[PASS] test_bechmark_dropERC721_claim_five_tokens() (gas: 211967) +[PASS] test_bechmark_dropERC721_setClaimConditions_five_conditions() (gas: 500859) +[PASS] test_benchmark_dropERC721_lazyMint() (gas: 124544) +[PASS] test_benchmark_dropERC721_lazyMint_for_delayed_reveal() (gas: 226124) +[PASS] test_benchmark_dropERC721_reveal() (gas: 8800) +Test result: ok. 5 passed; 0 failed; finished in 366.44ms + +Running 2 tests for src/test/benchmark/DropERC20Benchmark.t.sol:DropERC20BenchmarkTest +[PASS] test_bechmark_dropERC20_claim() (gas: 230441) +[PASS] test_bechmark_dropERC20_setClaimConditions_five_conditions() (gas: 501045) +Test result: ok. 2 passed; 0 failed; finished in 373.94ms diff --git a/lib/chainlink b/lib/chainlink index 9d5ec20aa..c272befd7 160000 --- a/lib/chainlink +++ b/lib/chainlink @@ -1 +1 @@ -Subproject commit 9d5ec20aa7c03c5f08722fa88f621075d300dcc1 +Subproject commit c272befd714d827c890582d74df838345ed1caa7 diff --git a/package.json b/package.json index 9fd54e134..f64a7b285 100644 --- a/package.json +++ b/package.json @@ -54,19 +54,20 @@ "typescript": "^4.4.4" }, "scripts": { - "clean": "hardhat clean && rm -rf abi/ && rm -rf artifacts/ && rm -rf dist/ && rm -rf typechain/", - "compile": "hardhat compile", + "clean": "forge clean && rm -rf abi/ && rm -rf artifacts_forge/ && rm -rf dist/ && rm -rf typechain/", + "compile": "forge build", "lint": "solhint \"contracts/**/*.sol\"", "prettier": "prettier --config .prettierrc --write \"{contracts,src}/**/*.{js,json,sol,ts}\"", "prettier:list-different": "prettier --config .prettierrc --list-different \"**/*.{js,json,sol,ts}\"", "prettier:contracts": "prettier --config .prettierrc --list-different \"{contracts,src}/**/*.sol\"", - "test": "hardhat test", - "typechain": "hardhat typechain", + "test": "forge test", + "typechain": "typechain --target ethers-v5 --out-dir ./typechain artifacts_forge/**/*.json", "build": "yarn clean && yarn compile", "forge:build": "forge build --hardhat", "forge:test": "forge test --hardhat", "export-abi": "hardhat export-abi", "gas": "forge test --mc Benchmark --gas-report > gasreport.txt", "forge:snapshot": "forge snapshot --check" - } + }, + "dependencies": {} } diff --git a/release.sh b/release.sh index 6af49096d..5f2d6a6ea 100755 --- a/release.sh +++ b/release.sh @@ -35,9 +35,7 @@ echo "### Build finished. Copying abis." rm -rf contracts/abi mkdir -p contracts/abi # copy all abis to contracts/abi -find artifacts/contracts ! -iregex ".*([a-zA-Z0-9_]).json" -exec cp {} contracts/abi 2>/dev/null \; -# remove non-abi files -rm contracts/abi/*.dbg.json +find artifacts_forge ! -iregex ".*([a-zA-Z0-9_]).json" -exec cp {} contracts/abi 2>/dev/null \; echo "### Copying README." # copy root README to contracts folder cp README.md contracts/README.md diff --git a/src/test/LoyaltyCard.t.sol b/src/test/LoyaltyCard.t.sol index 7bd1b5c30..8504831d3 100644 --- a/src/test/LoyaltyCard.t.sol +++ b/src/test/LoyaltyCard.t.sol @@ -313,8 +313,6 @@ contract LoyaltyCardTest is BaseTest { _mintrequest.currency = address(erc20); _signature = signMintRequest(_mintrequest, privateKey); - uint256 erc20BalanceOfSeller = erc20.balanceOf(address(saleRecipient)); - vm.prank(recipient); erc20.approve(address(loyaltyCard), 5); diff --git a/src/test/OpenEditionERC721.t.sol b/src/test/OpenEditionERC721.t.sol index f1dec21b3..c9bdb2f91 100644 --- a/src/test/OpenEditionERC721.t.sol +++ b/src/test/OpenEditionERC721.t.sol @@ -44,9 +44,7 @@ contract OpenEditionERC721Test is BaseTest { forwarders(), saleRecipient, royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient + royaltyBps ) ) ) @@ -161,71 +159,6 @@ contract OpenEditionERC721Test is BaseTest { vm.stopPrank(); } - /** - * @dev Tests contract state for Transfer role. - */ - function test_state_getRoleMember_transferRole() public { - bytes32 role = keccak256("TRANSFER_ROLE"); - - uint256 roleMemberCount = openEdition.getRoleMemberCount(role); - assertEq(roleMemberCount, 2); - - address roleMember = openEdition.getRoleMember(role, 1); - assertEq(roleMember, address(0)); - - vm.startPrank(deployer); - openEdition.grantRole(role, address(2)); - openEdition.grantRole(role, address(3)); - openEdition.grantRole(role, address(4)); - - roleMemberCount = openEdition.getRoleMemberCount(role); - console.log(roleMemberCount); - for (uint256 i = 0; i < roleMemberCount; i++) { - console.log(openEdition.getRoleMember(role, i)); - } - console.log(""); - - openEdition.revokeRole(role, address(2)); - roleMemberCount = openEdition.getRoleMemberCount(role); - console.log(roleMemberCount); - for (uint256 i = 0; i < roleMemberCount; i++) { - console.log(openEdition.getRoleMember(role, i)); - } - console.log(""); - - openEdition.revokeRole(role, address(0)); - roleMemberCount = openEdition.getRoleMemberCount(role); - console.log(roleMemberCount); - for (uint256 i = 0; i < roleMemberCount; i++) { - console.log(openEdition.getRoleMember(role, i)); - } - console.log(""); - - openEdition.grantRole(role, address(5)); - roleMemberCount = openEdition.getRoleMemberCount(role); - console.log(roleMemberCount); - for (uint256 i = 0; i < roleMemberCount; i++) { - console.log(openEdition.getRoleMember(role, i)); - } - console.log(""); - - openEdition.grantRole(role, address(0)); - roleMemberCount = openEdition.getRoleMemberCount(role); - console.log(roleMemberCount); - for (uint256 i = 0; i < roleMemberCount; i++) { - console.log(openEdition.getRoleMember(role, i)); - } - console.log(""); - - openEdition.grantRole(role, address(6)); - roleMemberCount = openEdition.getRoleMemberCount(role); - console.log(roleMemberCount); - for (uint256 i = 0; i < roleMemberCount; i++) { - console.log(openEdition.getRoleMember(role, i)); - } - console.log(""); - } - /** * note: Testing transfer of tokens when transfer-role is restricted */ @@ -258,25 +191,6 @@ contract OpenEditionERC721Test is BaseTest { openEdition.transferFrom(receiver, address(123), 1); } - /** - * @dev Tests whether role member count is incremented correctly. - */ - function test_member_count_incremented_properly_when_role_granted() public { - bytes32 role = keccak256("ABC_ROLE"); - address receiver = getActor(0); - - vm.startPrank(deployer); - uint256 roleMemberCount = openEdition.getRoleMemberCount(role); - - assertEq(roleMemberCount, 0); - - openEdition.grantRole(role, receiver); - - assertEq(openEdition.getRoleMemberCount(role), 1); - - vm.stopPrank(); - } - function test_claimCondition_with_startTimestamp() public { vm.warp(1); diff --git a/src/test/airdrop/AirdropERC1155.t.sol b/src/test/airdrop/AirdropERC1155.t.sol index 640aaf807..b0901b14b 100644 --- a/src/test/airdrop/AirdropERC1155.t.sol +++ b/src/test/airdrop/AirdropERC1155.t.sol @@ -135,7 +135,7 @@ contract AirdropERC1155GasTest is BaseTest { uint256, uint256, bytes calldata - ) external returns (bytes4) { + ) external pure returns (bytes4) { return this.onERC1155Received.selector; } } diff --git a/src/test/airdrop/AirdropERC20.t.sol b/src/test/airdrop/AirdropERC20.t.sol index 6cec0e266..d60a8ebf6 100644 --- a/src/test/airdrop/AirdropERC20.t.sol +++ b/src/test/airdrop/AirdropERC20.t.sol @@ -150,25 +150,38 @@ contract AirdropERC20GasTest is BaseTest { function test_transferNativeToken_toEOA() public { vm.prank(address(tokenOwner)); - address(0x123).call{ value: 1 ether }(""); + (bool success, bytes memory data) = address(0x123).call{ value: 1 ether }(""); + + // Silence warning: Return value of low-level calls not used. + (success, data) = (success, data); } function test_transferNativeToken_toContract() public { vm.prank(address(tokenOwner)); - address(this).call{ value: 1 ether }(""); + (bool success, bytes memory data) = address(this).call{ value: 1 ether }(""); + + // Silence warning: Return value of low-level calls not used. + (success, data) = (success, data); } function test_transferNativeToken_toEOA_gasOverride() public { vm.prank(address(tokenOwner)); console.log(gasleft()); - address(0x123).call{ value: 1 ether, gas: 100_000 }(""); + (bool success, bytes memory data) = address(0x123).call{ value: 1 ether, gas: 100_000 }(""); + + // Silence warning: Return value of low-level calls not used. + (success, data) = (success, data); + console.log(gasleft()); } function test_transferNativeToken_toContract_gasOverride() public { vm.prank(address(tokenOwner)); console.log(gasleft()); - address(this).call{ value: 1 ether, gas: 100_000 }(""); + (bool success, bytes memory data) = address(this).call{ value: 1 ether, gas: 100_000 }(""); console.log(gasleft()); + + // Silence warning: Return value of low-level calls not used. + (success, data) = (success, data); } } diff --git a/src/test/airdrop/AirdropERC721.t.sol b/src/test/airdrop/AirdropERC721.t.sol index 1f7157108..379c92b27 100644 --- a/src/test/airdrop/AirdropERC721.t.sol +++ b/src/test/airdrop/AirdropERC721.t.sol @@ -116,7 +116,7 @@ contract AirdropERC721GasTest is BaseTest { address, uint256, bytes calldata - ) external view returns (bytes4) { + ) external pure returns (bytes4) { return this.onERC721Received.selector; } } diff --git a/src/test/benchmark/DropERC1155Benchmark.t.sol b/src/test/benchmark/DropERC1155Benchmark.t.sol index a73c9041a..a4d61e6a1 100644 --- a/src/test/benchmark/DropERC1155Benchmark.t.sol +++ b/src/test/benchmark/DropERC1155Benchmark.t.sol @@ -111,8 +111,6 @@ contract DropERC1155BenchmarkTest is BaseTest { vm.warp(1); - address receiver = address(0x92Bb439374a091c7507bE100183d8D1Ed2c9dAD3); // in allowlist - DropERC1155.ClaimCondition[] memory conditions = new DropERC1155.ClaimCondition[](5); conditions[0].maxClaimableSupply = 500; conditions[0].quantityLimitPerWallet = 10; diff --git a/src/test/benchmark/DropERC20Benchmark.t.sol b/src/test/benchmark/DropERC20Benchmark.t.sol index 28f492c82..cdbe7be37 100644 --- a/src/test/benchmark/DropERC20Benchmark.t.sol +++ b/src/test/benchmark/DropERC20Benchmark.t.sol @@ -55,8 +55,6 @@ contract DropERC20BenchmarkTest is BaseTest { vm.warp(1); - address receiver = address(0x92Bb439374a091c7507bE100183d8D1Ed2c9dAD3); // in allowlist - DropERC20.ClaimCondition[] memory conditions = new DropERC20.ClaimCondition[](5); conditions[0].maxClaimableSupply = 500 ether; conditions[0].quantityLimitPerWallet = 10 ether; diff --git a/src/test/benchmark/DropERC721Benchmark.t.sol b/src/test/benchmark/DropERC721Benchmark.t.sol index c060f3e57..d5d98df9e 100644 --- a/src/test/benchmark/DropERC721Benchmark.t.sol +++ b/src/test/benchmark/DropERC721Benchmark.t.sol @@ -109,8 +109,6 @@ contract DropERC721BenchmarkTest is BaseTest { vm.warp(1); - address receiver = address(0x92Bb439374a091c7507bE100183d8D1Ed2c9dAD3); // in allowlist - DropERC721.ClaimCondition[] memory conditions = new DropERC721.ClaimCondition[](5); conditions[0].maxClaimableSupply = 500; conditions[0].quantityLimitPerWallet = 10; @@ -153,8 +151,6 @@ contract DropERC721BenchmarkTest is BaseTest { bytes memory encryptedBaseURI = "encryptedBaseURI://"; bytes32 provenanceHash = bytes32("whatever"); - uint256 nextTokenIdToMintBefore = drop.nextTokenIdToMint(); - vm.prank(deployer); vm.resumeGasMetering(); drop.lazyMint(amountToLazyMint, baseURI, abi.encode(encryptedBaseURI, provenanceHash)); @@ -174,6 +170,7 @@ contract DropERC721BenchmarkTest is BaseTest { drop.lazyMint(amountToLazyMint, placeholderURI, abi.encode(encryptedURI, provenanceHash)); vm.prank(deployer); + vm.resumeGasMetering(); drop.reveal(0, key); } diff --git a/src/test/benchmark/EditionStakeBenchmark.t.sol b/src/test/benchmark/EditionStakeBenchmark.t.sol index 6e988b070..91aee909d 100644 --- a/src/test/benchmark/EditionStakeBenchmark.t.sol +++ b/src/test/benchmark/EditionStakeBenchmark.t.sol @@ -70,13 +70,11 @@ contract EditionStakeBenchmarkTest is BaseTest { // stake 50 tokens with token-id 0 vm.prank(stakerOne); stakeContract.stake(0, 50); - uint256 timeOfLastUpdate_one = block.timestamp; //=================== warp timestamp to claim rewards vm.roll(100); vm.warp(1000); - uint256 rewardBalanceBefore = stakeContract.getRewardTokenBalance(); vm.prank(stakerOne); vm.resumeGasMetering(); stakeContract.claimRewards(0); @@ -92,8 +90,6 @@ contract EditionStakeBenchmarkTest is BaseTest { vm.prank(stakerTwo); stakeContract.stake(1, 20); - uint256 timeOfLastUpdate = block.timestamp; - //========== warp timestamp before withdraw vm.roll(100); vm.warp(1000); diff --git a/src/test/benchmark/NFTStakeBenchmark.t.sol b/src/test/benchmark/NFTStakeBenchmark.t.sol index ee72f0916..4ffb5ad17 100644 --- a/src/test/benchmark/NFTStakeBenchmark.t.sol +++ b/src/test/benchmark/NFTStakeBenchmark.t.sol @@ -78,13 +78,10 @@ contract NFTStakeBenchmarkTest is BaseTest { // stake 3 tokens vm.prank(stakerOne); stakeContract.stake(_tokenIdsOne); - uint256 timeOfLastUpdate_one = block.timestamp; //=================== warp timestamp to claim rewards vm.roll(100); vm.warp(1000); - - uint256 rewardBalanceBefore = stakeContract.getRewardTokenBalance(); vm.prank(stakerOne); vm.resumeGasMetering(); stakeContract.claimRewards(); @@ -111,6 +108,7 @@ contract NFTStakeBenchmarkTest is BaseTest { _tokensToWithdraw[0] = 1; vm.prank(stakerOne); + vm.resumeGasMetering(); stakeContract.withdraw(_tokensToWithdraw); } diff --git a/src/test/benchmark/PackBenchmark.t.sol b/src/test/benchmark/PackBenchmark.t.sol index 786223012..3aa7975a0 100644 --- a/src/test/benchmark/PackBenchmark.t.sol +++ b/src/test/benchmark/PackBenchmark.t.sol @@ -179,7 +179,6 @@ contract PackBenchmarkTest is BaseTest { function test_benchmark_pack_createPack() public { vm.pauseGasMetering(); - uint256 packId = pack.nextTokenIdToMint(); address recipient = address(1); vm.prank(address(tokenOwner)); @@ -214,7 +213,7 @@ contract PackBenchmarkTest is BaseTest { address recipient = address(1); vm.prank(address(tokenOwner)); - (, uint256 totalSupply) = pack.createPack(packContents, numOfRewardUnits, packUri, 0, 2, recipient); + pack.createPack(packContents, numOfRewardUnits, packUri, 0, 2, recipient); vm.prank(recipient, recipient); vm.resumeGasMetering(); diff --git a/src/test/benchmark/PackVRFDirectBenchmark.t.sol b/src/test/benchmark/PackVRFDirectBenchmark.t.sol index aaa5ac3f1..bded43e97 100644 --- a/src/test/benchmark/PackVRFDirectBenchmark.t.sol +++ b/src/test/benchmark/PackVRFDirectBenchmark.t.sol @@ -194,16 +194,13 @@ contract PackVRFDirectBenchmarkTest is BaseTest { function test_benchmark_packvrf_openPackAndClaimRewards() public { vm.pauseGasMetering(); vm.warp(1000); - uint256 packId = pack.nextTokenIdToMint(); - uint256 packsToOpen = 3; address recipient = address(1); vm.prank(address(tokenOwner)); - (, uint256 totalSupply) = pack.createPack(packContents, numOfRewardUnits, packUri, 0, 2, recipient); + pack.createPack(packContents, numOfRewardUnits, packUri, 0, 2, recipient); vm.prank(recipient, recipient); vm.resumeGasMetering(); - uint256 requestId = pack.openPackAndClaimRewards(packId, packsToOpen, 2_500_000); } function test_benchmark_packvrf_openPack() public { @@ -214,7 +211,7 @@ contract PackVRFDirectBenchmarkTest is BaseTest { address recipient = address(1); vm.prank(address(tokenOwner)); - (, uint256 totalSupply) = pack.createPack(packContents, numOfRewardUnits, packUri, 0, 2, recipient); + pack.createPack(packContents, numOfRewardUnits, packUri, 0, 2, recipient); vm.prank(recipient, recipient); vm.resumeGasMetering(); diff --git a/src/test/benchmark/SignatureDropBenchmark.t.sol b/src/test/benchmark/SignatureDropBenchmark.t.sol index 38ae9637d..e0b620a18 100644 --- a/src/test/benchmark/SignatureDropBenchmark.t.sol +++ b/src/test/benchmark/SignatureDropBenchmark.t.sol @@ -86,8 +86,6 @@ contract SignatureDropBenchmarkTest is BaseTest { function test_bechmark_signatureDrop_setClaimConditions() public { vm.pauseGasMetering(); vm.warp(1); - - address receiver = getActor(0); bytes32[] memory proofs = new bytes32[](0); SignatureDrop.AllowlistProof memory alp; @@ -138,6 +136,7 @@ contract SignatureDropBenchmarkTest is BaseTest { sigdrop.lazyMint(amountToLazyMint, placeholderURI, abi.encode(encryptedURI, provenanceHash)); vm.prank(deployerSigner); + vm.resumeGasMetering(); sigdrop.reveal(0, key); } diff --git a/src/test/benchmark/TokenERC1155Benchmark.t.sol b/src/test/benchmark/TokenERC1155Benchmark.t.sol index 981639ae8..ad863b06c 100644 --- a/src/test/benchmark/TokenERC1155Benchmark.t.sol +++ b/src/test/benchmark/TokenERC1155Benchmark.t.sol @@ -169,7 +169,6 @@ contract TokenERC1155BenchmarkTest is BaseTest { uint256 _amount = 100; uint256 nextTokenId = tokenContract.nextTokenIdToMint(); - uint256 currentBalanceOfRecipient = tokenContract.balanceOf(recipient, nextTokenId); vm.prank(deployerSigner); tokenContract.mintTo(recipient, type(uint256).max, _tokenURI, _amount); diff --git a/src/test/benchmark/TokenStakeBenchmark.t.sol b/src/test/benchmark/TokenStakeBenchmark.t.sol index c005aece3..9c63e741e 100644 --- a/src/test/benchmark/TokenStakeBenchmark.t.sol +++ b/src/test/benchmark/TokenStakeBenchmark.t.sol @@ -70,13 +70,10 @@ contract TokenStakeBenchmarkTest is BaseTest { // stake 50 tokens with token-id 0 vm.prank(stakerOne); stakeContract.stake(400); - uint256 timeOfLastUpdate_one = block.timestamp; //=================== warp timestamp to claim rewards vm.roll(100); vm.warp(1000); - - uint256 rewardBalanceBefore = stakeContract.getRewardTokenBalance(); vm.prank(stakerOne); vm.resumeGasMetering(); stakeContract.claimRewards(); @@ -92,8 +89,6 @@ contract TokenStakeBenchmarkTest is BaseTest { vm.prank(stakerTwo); stakeContract.stake(200); - uint256 timeOfLastUpdate = block.timestamp; - //========== warp timestamp before withdraw vm.roll(100); vm.warp(1000); diff --git a/src/test/marketplace/DirectListings.t.sol b/src/test/marketplace/DirectListings.t.sol index 46a932503..e6e29126d 100644 --- a/src/test/marketplace/DirectListings.t.sol +++ b/src/test/marketplace/DirectListings.t.sol @@ -186,7 +186,7 @@ contract MarketplaceDirectListingsTest is BaseTest { ); vm.prank(seller); - uint256 listingId = DirectListingsLogic(marketplace).createListing(listingParams); + DirectListingsLogic(marketplace).createListing(listingParams); // Total listings incremented assertEq(DirectListingsLogic(marketplace).totalListings(), 1); @@ -462,11 +462,7 @@ contract MarketplaceDirectListingsTest is BaseTest { } function test_royaltyEngine_tokenWithERC2981() public { - ( - MockRoyaltyEngineV1 royaltyEngine, - address payable[] memory customRoyaltyRecipients, - uint256[] memory customRoyaltyAmounts - ) = _setupRoyaltyEngine(); + (MockRoyaltyEngineV1 royaltyEngine, , ) = _setupRoyaltyEngine(); // Add RoyaltyEngine to marketplace vm.prank(marketplaceDeployer); @@ -585,11 +581,7 @@ contract MarketplaceDirectListingsTest is BaseTest { } function test_revert_feesExceedTotalPrice() public { - ( - MockRoyaltyEngineV1 royaltyEngine, - address payable[] memory customRoyaltyRecipients, - uint256[] memory customRoyaltyAmounts - ) = _setupRoyaltyEngine(); + (MockRoyaltyEngineV1 royaltyEngine, , ) = _setupRoyaltyEngine(); // Add RoyaltyEngine to marketplace vm.prank(marketplaceDeployer); @@ -2074,9 +2066,7 @@ contract IssueC2_MarketplaceDirectListingsTest is BaseTest { function test_state_buyFromListing_after_update() public { (uint256 listingId, IDirectListings.Listing memory listing) = _setup_buyFromListing(); - address buyFor = buyer; uint256 quantityToBuy = listing.quantity; - address currency = listing.currency; uint256 pricePerToken = listing.pricePerToken; uint256 totalPrice = pricePerToken * quantityToBuy; diff --git a/src/test/marketplace/EnglishAuctions.t.sol b/src/test/marketplace/EnglishAuctions.t.sol index 5acec3f7c..94c5fbd0e 100644 --- a/src/test/marketplace/EnglishAuctions.t.sol +++ b/src/test/marketplace/EnglishAuctions.t.sol @@ -268,11 +268,7 @@ contract MarketplaceEnglishAuctionsTest is BaseTest { } function test_royaltyEngine_tokenWithERC2981() public { - ( - MockRoyaltyEngineV1 royaltyEngine, - address payable[] memory customRoyaltyRecipients, - uint256[] memory customRoyaltyAmounts - ) = _setupRoyaltyEngine(); + (MockRoyaltyEngineV1 royaltyEngine, , ) = _setupRoyaltyEngine(); // Add RoyaltyEngine to marketplace vm.prank(marketplaceDeployer); @@ -407,11 +403,7 @@ contract MarketplaceEnglishAuctionsTest is BaseTest { } function test_revert_feesExceedTotalPrice() public { - ( - MockRoyaltyEngineV1 royaltyEngine, - address payable[] memory customRoyaltyRecipients, - uint256[] memory customRoyaltyAmounts - ) = _setupRoyaltyEngine(); + (MockRoyaltyEngineV1 royaltyEngine, , ) = _setupRoyaltyEngine(); // Add RoyaltyEngine to marketplace vm.prank(marketplaceDeployer); @@ -2253,7 +2245,6 @@ contract BreitwieserTheBidder is BaseTest { // Condition: multiple copies in circulation and attacker has at least 1. uint256 tokenId = 999; - uint256 quantity = 2; // Victim. erc1155.mint(seller, tokenId, 1); erc1155.mint(attacker, tokenId, 1); @@ -2266,7 +2257,7 @@ contract BreitwieserTheBidder is BaseTest { address currency = address(erc20); uint256 minimumBidAmount = 1 ether; uint256 buyoutBidAmount = 10 ether; - uint256 quantity = 1; + uint256 qty = 1; uint64 timeBufferInSeconds = 10 seconds; uint64 bidBufferBps = 1000; uint64 startTimestamp = 0; @@ -2274,7 +2265,7 @@ contract BreitwieserTheBidder is BaseTest { auctionParams1 = IEnglishAuctions.AuctionParameters( assetContract, tokenId, - quantity, + qty, currency, minimumBidAmount, buyoutBidAmount, diff --git a/src/test/marketplace/Offers.t.sol b/src/test/marketplace/Offers.t.sol index b33b2f53d..3f51bf738 100644 --- a/src/test/marketplace/Offers.t.sol +++ b/src/test/marketplace/Offers.t.sol @@ -208,11 +208,7 @@ contract MarketplaceOffersTest is BaseTest { } function test_royaltyEngine_tokenWithERC2981() public { - ( - MockRoyaltyEngineV1 royaltyEngine, - address payable[] memory customRoyaltyRecipients, - uint256[] memory customRoyaltyAmounts - ) = _setupRoyaltyEngine(); + (MockRoyaltyEngineV1 royaltyEngine, , ) = _setupRoyaltyEngine(); // Add RoyaltyEngine to marketplace vm.prank(marketplaceDeployer); @@ -332,11 +328,7 @@ contract MarketplaceOffersTest is BaseTest { } function test_revert_feesExceedTotalPrice() public { - ( - MockRoyaltyEngineV1 royaltyEngine, - address payable[] memory customRoyaltyRecipients, - uint256[] memory customRoyaltyAmounts - ) = _setupRoyaltyEngine(); + (MockRoyaltyEngineV1 royaltyEngine, , ) = _setupRoyaltyEngine(); // Add RoyaltyEngine to marketplace vm.prank(marketplaceDeployer); @@ -361,8 +353,6 @@ contract MarketplaceOffersTest is BaseTest { IOffers.Offer memory offer = OffersLogic(marketplace).getOffer(offerId); - uint256 totalPrice = offer.totalPrice; - // Approve Marketplace to transfer token. vm.prank(seller); IERC721(offer.assetContract).setApprovalForAll(marketplace, true); diff --git a/src/test/mocks/MockRoyaltyEngineV1.sol b/src/test/mocks/MockRoyaltyEngineV1.sol index 07f0cc9e5..9328ce0c6 100644 --- a/src/test/mocks/MockRoyaltyEngineV1.sol +++ b/src/test/mocks/MockRoyaltyEngineV1.sol @@ -22,7 +22,7 @@ contract MockRoyaltyEngineV1 is ERC165, IRoyaltyEngineV1 { address tokenAddress, uint256 tokenId, uint256 value - ) public override returns (address payable[] memory recipients, uint256[] memory amounts) { + ) public view override returns (address payable[] memory recipients, uint256[] memory amounts) { try IERC2981(tokenAddress).royaltyInfo(tokenId, value) returns (address recipient, uint256 amount) { // Supports EIP2981. Return amounts recipients = new address payable[](1); diff --git a/src/test/pack/PackVRFDirect.t.sol b/src/test/pack/PackVRFDirect.t.sol index 5a248f0ab..bdc0f7bb8 100644 --- a/src/test/pack/PackVRFDirect.t.sol +++ b/src/test/pack/PackVRFDirect.t.sol @@ -534,7 +534,7 @@ contract PackVRFDirectTest is BaseTest { address recipient = address(1); vm.prank(address(tokenOwner)); - (, uint256 totalSupply) = pack.createPack(packContents, numOfRewardUnits, packUri, 0, 2, recipient); + pack.createPack(packContents, numOfRewardUnits, packUri, 0, 2, recipient); vm.prank(recipient, recipient); uint256 requestId = pack.openPackAndClaimRewards(packId, packsToOpen, 2_500_000); @@ -564,7 +564,7 @@ contract PackVRFDirectTest is BaseTest { address recipient = address(1); vm.prank(address(tokenOwner)); - (, uint256 totalSupply) = pack.createPack(packContents, numOfRewardUnits, packUri, 0, 2, recipient); + pack.createPack(packContents, numOfRewardUnits, packUri, 0, 2, recipient); vm.prank(recipient, recipient); uint256 requestId = pack.openPackAndClaimRewards(packId, packsToOpen, 2); @@ -596,7 +596,7 @@ contract PackVRFDirectTest is BaseTest { address recipient = address(1); vm.prank(address(tokenOwner)); - (, uint256 totalSupply) = pack.createPack(packContents, numOfRewardUnits, packUri, 0, 2, recipient); + pack.createPack(packContents, numOfRewardUnits, packUri, 0, 2, recipient); vm.prank(recipient, recipient); uint256 requestId = pack.openPackAndClaimRewards(packId, packsToOpen, 2_500_000); @@ -797,7 +797,7 @@ contract PackVRFDirectTest is BaseTest { address recipient = address(1); vm.prank(address(tokenOwner)); - (, uint256 totalSupply) = pack.createPack(packContents, numOfRewardUnits, packUri, 0, 2, recipient); + pack.createPack(packContents, numOfRewardUnits, packUri, 0, 2, recipient); vm.prank(recipient, recipient); uint256 requestId = pack.openPack(packId, packsToOpen); diff --git a/src/test/sdk/extension/StakingExtension.t.sol b/src/test/sdk/extension/StakingExtension.t.sol index 54494ae49..5267d87d5 100644 --- a/src/test/sdk/extension/StakingExtension.t.sol +++ b/src/test/sdk/extension/StakingExtension.t.sol @@ -198,7 +198,7 @@ contract StakingExtensionTest is DSTest, Test { _tokenIds[0] = 6; vm.prank(stakerOne); - vm.expectRevert("Not owned or approved"); + vm.expectRevert("ERC721: transfer from incorrect owner"); ext.stake(_tokenIds); } diff --git a/src/test/smart-wallet/Account.t.sol b/src/test/smart-wallet/Account.t.sol index 12611493f..1d61a3f57 100644 --- a/src/test/smart-wallet/Account.t.sol +++ b/src/test/smart-wallet/Account.t.sol @@ -474,7 +474,10 @@ contract SimpleAccountTest is BaseTest { vm.prank(accountAdmin); // solhint-disable-next-line avoid-low-level-calls - payable(account).call{ value: 1000 }(""); + (bool success, bytes memory data) = payable(account).call{ value: 1000 }(""); + + // Silence warning: Return value of low-level calls not used. + (success, data) = (success, data); assertEq(address(account).balance, 1000); } @@ -488,9 +491,12 @@ contract SimpleAccountTest is BaseTest { address account = accountFactory.getAddress(accountAdmin, bytes("")); vm.prank(accountAdmin); // solhint-disable-next-line avoid-low-level-calls - payable(account).call{ value: value }(""); + (bool success, bytes memory data) = payable(account).call{ value: value }(""); assertEq(address(account).balance, value); + // Silence warning: Return value of low-level calls not used. + (success, data) = (success, data); + address recipient = address(0x3456); UserOperation[] memory userOp = _setupUserOpExecute(accountAdminPKey, bytes(""), recipient, value, bytes("")); diff --git a/src/test/smart-wallet/DynamicAccount.t.sol b/src/test/smart-wallet/DynamicAccount.t.sol index 39b618ce1..c25832638 100644 --- a/src/test/smart-wallet/DynamicAccount.t.sol +++ b/src/test/smart-wallet/DynamicAccount.t.sol @@ -528,7 +528,10 @@ contract DynamicAccountTest is BaseTest { vm.prank(accountAdmin); // solhint-disable-next-line avoid-low-level-calls - payable(account).call{ value: 1000 }(""); + (bool success, bytes memory ret) = payable(account).call{ value: 1000 }(""); + + // Silence warning: Return value of low-level calls not used. + (success, ret) = (success, ret); assertEq(address(account).balance, 1000); } @@ -542,9 +545,12 @@ contract DynamicAccountTest is BaseTest { address account = accountFactory.getAddress(accountAdmin, bytes("")); vm.prank(accountAdmin); // solhint-disable-next-line avoid-low-level-calls - payable(account).call{ value: value }(""); + (bool success, bytes memory ret) = payable(account).call{ value: value }(""); assertEq(address(account).balance, value); + // Silence warning: Return value of low-level calls not used. + (success, ret) = (success, ret); + address recipient = address(0x3456); UserOperation[] memory userOp = _setupUserOpExecute(accountAdminPKey, bytes(""), recipient, value, bytes("")); diff --git a/src/test/smart-wallet/ManagedAccount.t.sol b/src/test/smart-wallet/ManagedAccount.t.sol index 45960cd70..7747b673a 100644 --- a/src/test/smart-wallet/ManagedAccount.t.sol +++ b/src/test/smart-wallet/ManagedAccount.t.sol @@ -531,9 +531,12 @@ contract ManagedAccountTest is BaseTest { vm.prank(accountAdmin); // solhint-disable-next-line avoid-low-level-calls - payable(account).call{ value: 1000 }(""); + (bool success, bytes memory ret) = payable(account).call{ value: 1000 }(""); assertEq(address(account).balance, 1000); + + // Silence warning: Return value of low-level calls not used. + (success, ret) = (success, ret); } /// @dev Transfer native tokens out of an account. @@ -545,9 +548,12 @@ contract ManagedAccountTest is BaseTest { address account = accountFactory.getAddress(accountAdmin, bytes("")); vm.prank(accountAdmin); // solhint-disable-next-line avoid-low-level-calls - payable(account).call{ value: value }(""); + (bool success, bytes memory ret) = payable(account).call{ value: value }(""); assertEq(address(account).balance, value); + // Silence warning: Return value of low-level calls not used. + (success, ret) = (success, ret); + address recipient = address(0x3456); UserOperation[] memory userOp = _setupUserOpExecute(accountAdminPKey, bytes(""), recipient, value, bytes("")); diff --git a/src/test/staking/EditionStake.t.sol b/src/test/staking/EditionStake.t.sol index 34b9f921a..0db955698 100644 --- a/src/test/staking/EditionStake.t.sol +++ b/src/test/staking/EditionStake.t.sol @@ -138,7 +138,7 @@ contract EditionStakeTest is BaseTest { function test_revert_stake_notBalanceOrApproved() public { // stake unowned tokens vm.prank(stakerOne); - vm.expectRevert("Not balance or approved"); + vm.expectRevert("ERC1155: insufficient balance for transfer"); stakeContract.stake(2, 10); } @@ -595,7 +595,7 @@ contract EditionStakeTest is BaseTest { function test_state_setTimeUnit_token0() public { // set value and check - uint256 timeUnit = 100; + uint80 timeUnit = 100; vm.prank(deployer); stakeContract.setTimeUnit(0, timeUnit); assertEq(timeUnit, stakeContract.getTimeUnit(0)); @@ -679,7 +679,7 @@ contract EditionStakeTest is BaseTest { function test_state_setTimeUnit_bothTokens() public { // set value and check - uint256 timeUnit = 100; + uint80 timeUnit = 100; vm.prank(deployer); stakeContract.setTimeUnit(0, timeUnit); assertEq(timeUnit, stakeContract.getTimeUnit(0)); @@ -1018,7 +1018,7 @@ contract EditionStakeTest is BaseTest { stakeContract.stake(0, 50); // set default timeUnit to zero - uint256 newTimeUnit = 0; + uint80 newTimeUnit = 0; vm.prank(deployer); vm.expectRevert("time-unit can't be 0"); stakeContract.setDefaultTimeUnit(newTimeUnit); @@ -1058,7 +1058,7 @@ contract Macro_EditionStakeTest is BaseTest { uint256 internal defaultTimeUnit; uint256 internal defaultRewardsPerUnitTime; - uint256 internal tokenAmount = 100; + uint64 internal tokenAmount = 100; address internal stakerOne = address(0x345); address internal stakerTwo = address(0x567); @@ -1096,7 +1096,7 @@ contract Macro_EditionStakeTest is BaseTest { stakeContract.stake(2, tokenAmount); // set timeUnit to zero - uint256 newTimeUnit = 0; + uint80 newTimeUnit = 0; vm.prank(deployer); vm.expectRevert("time-unit can't be 0"); stakeContract.setDefaultTimeUnit(newTimeUnit); diff --git a/src/test/staking/EditionStake_EthReward.t.sol b/src/test/staking/EditionStake_EthReward.t.sol index 02538fa84..455754605 100644 --- a/src/test/staking/EditionStake_EthReward.t.sol +++ b/src/test/staking/EditionStake_EthReward.t.sol @@ -147,7 +147,7 @@ contract EditionStakeEthRewardTest is BaseTest { function test_revert_stake_notBalanceOrApproved() public { // stake unowned tokens vm.prank(stakerOne); - vm.expectRevert("Not balance or approved"); + vm.expectRevert("ERC1155: insufficient balance for transfer"); stakeContract.stake(2, 10); } @@ -604,7 +604,7 @@ contract EditionStakeEthRewardTest is BaseTest { function test_state_setTimeUnit_token0() public { // set value and check - uint256 timeUnit = 100; + uint80 timeUnit = 100; vm.prank(deployer); stakeContract.setTimeUnit(0, timeUnit); assertEq(timeUnit, stakeContract.getTimeUnit(0)); @@ -688,7 +688,7 @@ contract EditionStakeEthRewardTest is BaseTest { function test_state_setTimeUnit_bothTokens() public { // set value and check - uint256 timeUnit = 100; + uint80 timeUnit = 100; vm.prank(deployer); stakeContract.setTimeUnit(0, timeUnit); assertEq(timeUnit, stakeContract.getTimeUnit(0)); @@ -1027,7 +1027,7 @@ contract EditionStakeEthRewardTest is BaseTest { stakeContract.stake(0, 50); // set default timeUnit to zero - uint256 newTimeUnit = 0; + uint80 newTimeUnit = 0; vm.prank(deployer); vm.expectRevert("time-unit can't be 0"); stakeContract.setDefaultTimeUnit(newTimeUnit); diff --git a/src/test/staking/NFTStake.t.sol b/src/test/staking/NFTStake.t.sol index 861cabdc2..bd049f608 100644 --- a/src/test/staking/NFTStake.t.sol +++ b/src/test/staking/NFTStake.t.sol @@ -151,7 +151,7 @@ contract NFTStakeTest is BaseTest { _tokenIds[0] = 6; vm.prank(stakerOne); - vm.expectRevert("Not owned or approved"); + vm.expectRevert("ERC721: transfer from incorrect owner"); stakeContract.stake(_tokenIds); } diff --git a/src/test/staking/NFTStake_EthReward.t.sol b/src/test/staking/NFTStake_EthReward.t.sol index f3bc0a04f..ba9c7da4b 100644 --- a/src/test/staking/NFTStake_EthReward.t.sol +++ b/src/test/staking/NFTStake_EthReward.t.sol @@ -160,7 +160,7 @@ contract NFTStakeEthRewardTest is BaseTest { _tokenIds[0] = 6; vm.prank(stakerOne); - vm.expectRevert("Not owned or approved"); + vm.expectRevert("ERC721: transfer from incorrect owner"); stakeContract.stake(_tokenIds); } diff --git a/src/test/staking/TokenStake.t.sol b/src/test/staking/TokenStake.t.sol index 1443cd11d..a2d4055f9 100644 --- a/src/test/staking/TokenStake.t.sol +++ b/src/test/staking/TokenStake.t.sol @@ -13,7 +13,7 @@ contract TokenStakeTest is BaseTest { address internal stakerOne; address internal stakerTwo; - uint256 internal timeUnit; + uint80 internal timeUnit; uint256 internal rewardRatioNumerator; uint256 internal rewardRatioDenominator; @@ -298,7 +298,7 @@ contract TokenStakeTest is BaseTest { function test_state_setTimeUnit() public { // set value and check - uint256 timeUnitToSet = 100; + uint80 timeUnitToSet = 100; vm.prank(deployer); stakeContract.setTimeUnit(timeUnitToSet); assertEq(timeUnitToSet, stakeContract.getTimeUnit()); @@ -504,7 +504,7 @@ contract TokenStakeTest is BaseTest { stakeContract.stake(400); // set timeUnit to zero - uint256 newTimeUnit = 0; + uint80 newTimeUnit = 0; vm.prank(deployer); vm.expectRevert("time-unit can't be 0"); stakeContract.setTimeUnit(newTimeUnit); @@ -537,7 +537,7 @@ contract Macro_TokenStake_Rewards6_Staking18_Test is BaseTest { address internal stakerOne; - uint256 internal timeUnit; + uint80 internal timeUnit; uint256 internal rewardRatioNumerator; uint256 internal rewardRatioDenominator; @@ -631,7 +631,7 @@ contract Macro_TokenStake_Rewards18_Staking6_Test is BaseTest { address internal stakerOne; - uint256 internal timeUnit; + uint80 internal timeUnit; uint256 internal rewardRatioNumerator; uint256 internal rewardRatioDenominator; @@ -719,7 +719,7 @@ contract Macro_TokenStake_Rewards18_Staking6_Test is BaseTest { contract Macro_TokenStakeTest is BaseTest { TokenStake internal stakeContract; - uint256 internal timeUnit; + uint80 internal timeUnit; uint256 internal rewardsPerUnitTime; uint256 internal rewardRatioNumerator; uint256 internal rewardRatioDenominator; @@ -767,7 +767,7 @@ contract Macro_TokenStakeTest is BaseTest { stakeContract.stake(tokenAmount); // set timeUnit to zero - uint256 newTimeUnit = 0; + uint80 newTimeUnit = 0; vm.prank(deployer); vm.expectRevert("time-unit can't be 0"); stakeContract.setTimeUnit(newTimeUnit); diff --git a/src/test/staking/TokenStake_EthReward.t.sol b/src/test/staking/TokenStake_EthReward.t.sol index e5de73992..f8e20354c 100644 --- a/src/test/staking/TokenStake_EthReward.t.sol +++ b/src/test/staking/TokenStake_EthReward.t.sol @@ -307,7 +307,7 @@ contract TokenStakeEthRewardTest is BaseTest { function test_state_setTimeUnit() public { // set value and check - uint256 timeUnitToSet = 100; + uint80 timeUnitToSet = 100; vm.prank(deployer); stakeContract.setTimeUnit(timeUnitToSet); assertEq(timeUnitToSet, stakeContract.getTimeUnit()); @@ -513,7 +513,7 @@ contract TokenStakeEthRewardTest is BaseTest { stakeContract.stake(400); // set timeUnit to zero - uint256 newTimeUnit = 0; + uint80 newTimeUnit = 0; vm.prank(deployer); vm.expectRevert("time-unit can't be 0"); stakeContract.setTimeUnit(newTimeUnit); diff --git a/src/test/staking/TokenStake_EthStake.t.sol b/src/test/staking/TokenStake_EthStake.t.sol index 435856c55..fa5616568 100644 --- a/src/test/staking/TokenStake_EthStake.t.sol +++ b/src/test/staking/TokenStake_EthStake.t.sol @@ -301,7 +301,7 @@ contract TokenStakeEthStakeTest is BaseTest { function test_state_setTimeUnit() public { // set value and check - uint256 timeUnitToSet = 100; + uint80 timeUnitToSet = 100; vm.prank(deployer); stakeContract.setTimeUnit(timeUnitToSet); assertEq(timeUnitToSet, stakeContract.getTimeUnit()); @@ -507,7 +507,7 @@ contract TokenStakeEthStakeTest is BaseTest { stakeContract.stake{ value: 400 }(400); // set timeUnit to zero - uint256 newTimeUnit = 0; + uint80 newTimeUnit = 0; vm.prank(deployer); vm.expectRevert("time-unit can't be 0"); stakeContract.setTimeUnit(newTimeUnit); diff --git a/yarn.lock b/yarn.lock index be190795c..92457b423 100644 --- a/yarn.lock +++ b/yarn.lock @@ -82,9 +82,9 @@ strip-json-comments "^3.1.1" "@eth-optimism/contracts@^0.5.21": - version "0.5.39" - resolved "https://registry.yarnpkg.com/@eth-optimism/contracts/-/contracts-0.5.39.tgz#a312a0a0b2d5853cd417c5e8969e87288e166fcb" - integrity sha512-u3UufuZFzgidRN2/cC3mhRxX+M6VsMV9tauIKu8D5pym5/UO4pZr85WP3KxHFfLh1i8zmkdj+pN/GRQsNYCqMg== + version "0.5.40" + resolved "https://registry.yarnpkg.com/@eth-optimism/contracts/-/contracts-0.5.40.tgz#d13a04a15ea947a69055e6fc74d87e215d4c936a" + integrity sha512-MrzV0nvsymfO/fursTB7m/KunkPsCndltVgfdHaT1Aj5Vi6R/doKIGGkOofHX+8B6VMZpuZosKCMQ5lQuqjt8w== dependencies: "@eth-optimism/core-utils" "0.12.0" "@ethersproject/abstract-provider" "^5.7.0" @@ -931,9 +931,9 @@ integrity sha512-5OnVuO4HlkjSCJO165a4i2Pu1zQGzMs//o54LPrwUgxvEO2P3ax1QuaSI0cEHHTveA77guS0PnNugpR2JMsPfA== "@openzeppelin/contracts-upgradeable@^4.7.3": - version "4.8.3" - resolved "https://registry.yarnpkg.com/@openzeppelin/contracts-upgradeable/-/contracts-upgradeable-4.8.3.tgz#6b076a7b751811b90fe3a172a7faeaa603e13a3f" - integrity sha512-SXDRl7HKpl2WDoJpn7CK/M9U4Z8gNXDHHChAKh0Iz+Wew3wu6CmFYBeie3je8V0GSXZAIYYwUktSrnW/kwVPtg== + version "4.9.3" + resolved "https://registry.yarnpkg.com/@openzeppelin/contracts-upgradeable/-/contracts-upgradeable-4.9.3.tgz#ff17a80fb945f5102571f8efecb5ce5915cc4811" + integrity sha512-jjaHAVRMrE4UuZNfDwjlLGDxTHWIOwTJS2ldnc278a0gevfXfPr8hxKEVBGFBE96kl2G3VHDZhUimw/+G3TG2A== "@openzeppelin/contracts-v0.7@npm:@openzeppelin/contracts@v3.4.2": version "3.4.2" @@ -2495,7 +2495,7 @@ bcrypt-pbkdf@^1.0.0: bech32@1.1.4: version "1.1.4" - resolved "https://registry.npmjs.org/bech32/-/bech32-1.1.4.tgz" + resolved "https://registry.yarnpkg.com/bech32/-/bech32-1.1.4.tgz#e38c9f37bf179b8eb16ae3a772b40c356d4832e9" integrity sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ== bignumber.js@^9.0.0, bignumber.js@^9.0.1: @@ -2545,7 +2545,7 @@ bn.js@4.11.6: bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.10.0, bn.js@^4.11.0, bn.js@^4.11.6, bn.js@^4.11.8, bn.js@^4.11.9, bn.js@^4.8.0: version "4.12.0" - resolved "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88" integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA== bn.js@^5.0.0, bn.js@^5.1.1, bn.js@^5.1.2: @@ -2555,7 +2555,7 @@ bn.js@^5.0.0, bn.js@^5.1.1, bn.js@^5.1.2: bn.js@^5.2.0, bn.js@^5.2.1: version "5.2.1" - resolved "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.1.tgz#0bc527a6a0d18d0aa8d5b0538ce4a77dccfa7b70" integrity sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ== body-parser@1.19.2: @@ -2625,8 +2625,8 @@ braces@^3.0.2, braces@~3.0.2: brorand@^1.0.1, brorand@^1.1.0: version "1.1.0" - resolved "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz" - integrity sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8= + resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" + integrity sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w== browser-stdout@1.3.1: version "1.3.1" @@ -2769,9 +2769,9 @@ bufferutil@^4.0.1: node-gyp-build "^4.3.0" bufio@^1.0.7: - version "1.1.3" - resolved "https://registry.yarnpkg.com/bufio/-/bufio-1.1.3.tgz#7f8e524fd719ced2caa563a09d50550f283f745f" - integrity sha512-W0ydG8t+ST+drUpEwl1N+dU9Ije06g8+43CLtvEIzfKo9nPFLXbKqDYE2XSg4w6RugsBcCj7pEU7jOpBC6BqrA== + version "1.2.0" + resolved "https://registry.yarnpkg.com/bufio/-/bufio-1.2.0.tgz#b9ad1c06b0d9010363c387c39d2810a7086d143f" + integrity sha512-UlFk8z/PwdhYQTXSQQagwGAdtRI83gib2n4uy4rQnenxUM2yQi8lBDzF230BNk+3wAoZDxYRoBwVVUPgHa9MCA== bundle-require@^3.0.2: version "3.0.4" @@ -3749,7 +3749,7 @@ electron-to-chromium@^1.3.47: elliptic@6.5.4, elliptic@^6.4.0, elliptic@^6.5.2, elliptic@^6.5.3, elliptic@^6.5.4: version "6.5.4" - resolved "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb" integrity sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ== dependencies: bn.js "^4.11.9" @@ -5743,7 +5743,7 @@ hash.js@1.1.3: hash.js@1.1.7, hash.js@^1.0.0, hash.js@^1.0.3, hash.js@^1.1.7: version "1.1.7" - resolved "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz" + resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42" integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA== dependencies: inherits "^2.0.3" @@ -5761,8 +5761,8 @@ heap@0.2.6: hmac-drbg@^1.0.1: version "1.0.1" - resolved "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz" - integrity sha1-0nRXAQJabHdabFRXk+1QL8DGSaE= + resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" + integrity sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg== dependencies: hash.js "^1.0.3" minimalistic-assert "^1.0.0" @@ -5939,7 +5939,7 @@ inflight@^1.0.4: inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.1, inherits@~2.0.3, inherits@~2.0.4: version "2.0.4" - resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== inquirer@^6.2.2: @@ -6435,7 +6435,7 @@ js-sha3@0.5.7, js-sha3@^0.5.7: js-sha3@0.8.0, js-sha3@^0.8.0: version "0.8.0" - resolved "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz" + resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.8.0.tgz#b9b7a5da73afad7dedd0f8c463954cbde6818840" integrity sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q== "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: @@ -7291,13 +7291,13 @@ min-document@^2.19.0: minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: version "1.0.1" - resolved "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz" + resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== minimalistic-crypto-utils@^1.0.1: version "1.0.1" - resolved "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz" - integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo= + resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" + integrity sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg== minimatch@3.0.4: version "3.0.4" @@ -10775,7 +10775,7 @@ write@1.0.3: ws@7.4.6: version "7.4.6" - resolved "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.6.tgz#5654ca8ecdeee47c33a9a4bf6d28e2be2980377c" integrity sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A== ws@^3.0.0: